using System; using System.Collections.Generic; using System.Linq; using BizHawk.Common; namespace BizHawk.Emulation.Cores.Computers.Commodore64.Media { public sealed class Disk { [SaveState.DoNotSave] public const int FluxBitsPerEntry = 32; [SaveState.DoNotSave] public const int FluxBitsPerTrack = 16000000 / 5; [SaveState.DoNotSave] public const int FluxEntriesPerTrack = FluxBitsPerTrack/FluxBitsPerEntry; [SaveState.DoNotSave] private int[][] _tracks; [SaveState.DoNotSave] private readonly int[] _originalMedia; [SaveState.DoNotSave] public bool Valid; [SaveState.SaveWithName("DiskIsWriteProtected")] public bool WriteProtected; /// /// Create a blank, unformatted disk. /// public Disk(int trackCapacity) { WriteProtected = false; _tracks = new int[trackCapacity][]; FillMissingTracks(); _originalMedia = SerializeTracks(_tracks); Valid = true; } /// /// Create an expanded representation of a magnetic disk. /// /// Raw bit data. /// Track numbers for the raw bit data. /// Density zones for the raw bit data. /// Length, in bits, of each raw bit data. /// Total number of tracks on the media. public Disk(IList trackData, IList trackNumbers, IList trackDensities, int trackCapacity) { WriteProtected = true; _tracks = new int[trackCapacity][]; for (var i = 0; i < trackData.Count; i++) { _tracks[trackNumbers[i]] = ConvertToFluxTransitions(trackDensities[i], trackData[i], 0); } FillMissingTracks(); Valid = true; _originalMedia = SerializeTracks(_tracks); } private int[] ConvertToFluxTransitions(int density, byte[] bytes, int fluxBitOffset) { var paddedLength = bytes.Length; switch (density) { case 3: paddedLength = Math.Max(bytes.Length, 7692); break; case 2: paddedLength = Math.Max(bytes.Length, 7142); break; case 1: paddedLength = Math.Max(bytes.Length, 6666); break; case 0: paddedLength = Math.Max(bytes.Length, 6250); break; } paddedLength++; var paddedBytes = new byte[paddedLength]; Array.Copy(bytes, paddedBytes, bytes.Length); for (var i = bytes.Length; i < paddedLength; i++) { paddedBytes[i] = 0xAA; } var result = new int[FluxEntriesPerTrack]; var lengthBits = (paddedLength * 8) - 7; var offsets = new List(); var remainingBits = lengthBits; const long bitsNum = FluxEntriesPerTrack * FluxBitsPerEntry; long bitsDen = lengthBits; for (var i = 0; i < paddedLength; i++) { var byteData = paddedBytes[i]; for (var j = 0; j < 8; j++) { var offset = fluxBitOffset + ((i * 8 + j) * bitsNum / bitsDen); var byteOffset = (int)(offset / FluxBitsPerEntry); var bitOffset = (int)(offset % FluxBitsPerEntry); offsets.Add(offset); result[byteOffset] |= ((byteData & 0x80) != 0 ? 1 : 0) << bitOffset; byteData <<= 1; remainingBits--; if (remainingBits <= 0) break; } if (remainingBits <= 0) break; } return result; } private void FillMissingTracks() { // Fill half tracks (should assist with EA "fat-track" protections) for (var i = 1; i < _tracks.Length; i += 2) { if (_tracks[i] == null && _tracks[i - 1] != null) { _tracks[i] = new int[FluxEntriesPerTrack]; Array.Copy(_tracks[i - 1], _tracks[i], FluxEntriesPerTrack); } } // Fill vacant tracks for (var i = 0; i < _tracks.Length; i++) { if (_tracks[i] == null) { _tracks[i] = new int[FluxEntriesPerTrack]; } } } public int[] GetDataForTrack(int halftrack) { return _tracks[halftrack]; } /// /// Combine the tracks into a single bitstream. /// private int[] SerializeTracks(int[][] tracks) { var trackCount = tracks.Length; var result = new int[trackCount * FluxEntriesPerTrack]; for (var i = 0; i < trackCount; i++) { Array.Copy(tracks[i], 0, result, i * FluxEntriesPerTrack, FluxEntriesPerTrack); } return result; } /// /// Split a bitstream into tracks. /// private int[][] DeserializeTracks(int[] data) { var trackCount = data.Length/FluxEntriesPerTrack; var result = new int[trackCount][]; for (var i = 0; i < trackCount; i++) { result[i] = new int[FluxEntriesPerTrack]; Array.Copy(data, i * FluxEntriesPerTrack, result[i], 0, FluxEntriesPerTrack); } return result; } public void SyncState(Serializer ser) { if (ser.IsReader) { var mediaState = new int[_originalMedia.Length]; SaveState.SyncDelta("MediaState", ser, _originalMedia, ref mediaState); _tracks = DeserializeTracks(mediaState); } else if (ser.IsWriter) { var mediaState = SerializeTracks(_tracks); SaveState.SyncDelta("MediaState", ser, _originalMedia, ref mediaState); } SaveState.SyncObject(ser, this); } } }