diff --git a/BizHawk.Emulation.Cores/Computers/Commodore64/Media/Disk.cs b/BizHawk.Emulation.Cores/Computers/Commodore64/Media/Disk.cs index dd0165bc73..814239b6a7 100644 --- a/BizHawk.Emulation.Cores/Computers/Commodore64/Media/Disk.cs +++ b/BizHawk.Emulation.Cores/Computers/Commodore64/Media/Disk.cs @@ -7,18 +7,11 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Media { public sealed class Disk { - public const int FLUX_BITS_PER_ENTRY = 32; - public const int FLUX_BITS_PER_TRACK = 16000000 / 5; - public const int FLUX_ENTRIES_PER_TRACK = FLUX_BITS_PER_TRACK/FLUX_BITS_PER_ENTRY; - - private class Track - { - public int Index; - public bool Present; - public int[] FluxData; - } - - [SaveState.DoNotSave] private readonly Track[] _tracks; + [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 readonly List _tracks; + [SaveState.DoNotSave] private readonly int[] _originalMedia; [SaveState.DoNotSave] public bool Valid; /// @@ -26,9 +19,10 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Media /// public Disk(int trackCapacity) { - _tracks = new Track[trackCapacity]; + _tracks = new List(trackCapacity); FillMissingTracks(); - Valid = true; + _originalMedia = SerializeTracks(_tracks); + Valid = true; } /// @@ -41,75 +35,101 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Media /// Total number of tracks on the media. public Disk(IList trackData, IList trackNumbers, IList trackDensities, IList trackLengths, int trackCapacity) { - _tracks = new Track[trackCapacity]; + _tracks = Enumerable.Repeat(null, trackCapacity).ToList(); for (var i = 0; i < trackData.Count; i++) { - var track = new Track - { - Index = trackNumbers[i], - Present = true, - FluxData = ConvertToFluxTransitions(trackDensities[i], trackData[i], 0) - }; - _tracks[trackNumbers[i]] = track; + _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 result = new int[FLUX_ENTRIES_PER_TRACK]; - var length = bytes.Length; - var lengthBits = length*8; + private int[] ConvertToFluxTransitions(int density, byte[] bytes, int fluxBitOffset) + { + var paddedBytes = new byte[bytes.Length + 1]; + Array.Copy(bytes, paddedBytes, bytes.Length); + paddedBytes[paddedBytes.Length - 1] = 0x00; + var result = new int[FluxEntriesPerTrack]; + var length = paddedBytes.Length; + var lengthBits = length*8+7; var offsets = new List(); + var remainingBits = lengthBits; - const long bitsNum = FLUX_ENTRIES_PER_TRACK * FLUX_BITS_PER_ENTRY; + const long bitsNum = FluxEntriesPerTrack * FluxBitsPerEntry; long bitsDen = lengthBits; for (var i = 0; i < length; i++) { - var byteData = bytes[i]; + var byteData = paddedBytes[i]; for (var j = 0; j < 8; j++) { var offset = fluxBitOffset + ((i * 8 + j) * bitsNum / bitsDen); - var byteOffset = (int)(offset / FLUX_BITS_PER_ENTRY); - var bitOffset = (int)(offset % FLUX_BITS_PER_ENTRY); + var byteOffset = (int)(offset / FluxBitsPerEntry); + var bitOffset = (int)(offset % FluxBitsPerEntry); offsets.Add(offset); result[byteOffset] |= ((byteData & 0x80) != 0 ? 1 : 0) << bitOffset; byteData <<= 1; } - } - return result; + remainingBits--; + if (remainingBits <= 0) + break; + } + + return result; } private void FillMissingTracks() { - for (var i = 0; i < _tracks.Length; i++) + for (var i = 0; i < _tracks.Count; i++) { if (_tracks[i] == null) { - _tracks[i] = new Track - { - Index = i, - FluxData = new int[FLUX_ENTRIES_PER_TRACK] - }; + _tracks[i] = new int[FluxEntriesPerTrack]; } } } public int[] GetDataForTrack(int halftrack) { - return _tracks[halftrack].FluxData; + return _tracks[halftrack]; } - public IEnumerable GetPresentTracks() + /// + /// Combine the tracks into a single bitstream. + /// + private int[] SerializeTracks(IEnumerable tracks) { - return _tracks.Where(t => t.Present).Select(t => t.Index); - } + return tracks.SelectMany(t => t).ToArray(); + } + + /// + /// Split a bitstream into tracks. + /// + private IEnumerable DeserializeTracks(int[] data) + { + var trackCount = data.Length/FluxEntriesPerTrack; + for (var i = 0; i < trackCount; i++) + { + yield return data.Skip(i*FluxEntriesPerTrack).Take(FluxEntriesPerTrack).ToArray(); + } + } public void SyncState(Serializer ser) { + if (ser.IsReader) + { + var mediaState = new int[_originalMedia.Length]; + SaveState.SyncDeltaInts("MediaState", ser, _originalMedia, ref mediaState); + _tracks.Clear(); + _tracks.AddRange(DeserializeTracks(mediaState)); + } + else if (ser.IsWriter) + { + var mediaState = SerializeTracks(_tracks); + SaveState.SyncDeltaInts("MediaState", ser, _originalMedia, ref mediaState); + } SaveState.SyncObject(ser, this); } } diff --git a/BizHawk.Emulation.Cores/Computers/Commodore64/SaveState.cs b/BizHawk.Emulation.Cores/Computers/Commodore64/SaveState.cs index b87509162d..464c33fdee 100644 --- a/BizHawk.Emulation.Cores/Computers/Commodore64/SaveState.cs +++ b/BizHawk.Emulation.Cores/Computers/Commodore64/SaveState.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Reflection; @@ -14,9 +15,62 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64 { } + public class SaveWithName : Attribute + { + public string Name { get; set; } + + public SaveWithName() + { + } + + public SaveWithName(string name) + { + Name = name; + } + } + private static readonly Encoding Encoding = Encoding.Unicode; - public static void SyncObject(Serializer ser, object obj) + private static int[] GetDelta(IList source, IList data) + { + var length = Math.Min(source.Count, data.Count); + var delta = new int[length]; + for (var i = 0; i < length; i++) + { + delta[i] = source[i] ^ data[i]; + } + return delta; + } + + public static void SyncDeltaBytes(string name, Serializer ser, int[] source, ref int[] data) + { + byte[] delta = null; + if (ser.IsWriter && data != null) + { + delta = GetDelta(source, data).Select(d => unchecked((byte)d)).ToArray(); + } + ser.Sync(name, ref delta, false); + if (ser.IsReader && delta != null) + { + data = GetDelta(source, delta.Select(d => (int)d).ToArray()); + } + } + + public static void SyncDeltaInts(string name, Serializer ser, int[] source, ref int[] data) + { + int[] delta = null; + if (ser.IsWriter && data != null) + { + delta = GetDelta(source, data); + } + ser.Sync(name, ref delta, false); + if (ser.IsReader && delta != null) + { + data = GetDelta(source, delta); + } + } + + public static void SyncObject(Serializer ser, object obj) { const BindingFlags defaultFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy; var objType = obj.GetType(); @@ -29,6 +83,14 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64 continue; } + var name = member.Name; + var nameAttribute = member.GetCustomAttributes(true).FirstOrDefault(a => a is SaveWithName); + if (nameAttribute != null) + { + name = ((SaveWithName) nameAttribute).Name; + } + + object currentValue = null; var fail = false; var fieldInfo = member as FieldInfo; @@ -54,34 +116,35 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64 break; case "Bit": var refBit = (Bit)currentValue; - ser.Sync(member.Name, ref refBit); + ser.Sync(name, ref refBit); currentValue = refBit; break; case "Boolean": var refBool = (bool)currentValue; - ser.Sync(member.Name, ref refBool); + ser.Sync(name, ref refBool); currentValue = refBool; break; case "Boolean[]": { var tmp = (bool[])currentValue; - ser.Sync(member.Name, ref tmp, false); + ser.Sync(name, ref tmp, false); currentValue = tmp; } break; case "Byte": var refByte = (byte)currentValue; - ser.Sync(member.Name, ref refByte); + ser.Sync(name, ref refByte); currentValue = refByte; break; case "Byte[]": refByteBuffer = new ByteBuffer((byte[])currentValue); - ser.Sync(member.Name, ref refByteBuffer); - currentValue = refByteBuffer.Arr; + ser.Sync(name, ref refByteBuffer); + currentValue = refByteBuffer.Arr.Select(d => d).ToArray(); + refByteBuffer.Dispose(); break; case "ByteBuffer": refByteBuffer = (ByteBuffer)currentValue; - ser.Sync(member.Name, ref refByteBuffer); + ser.Sync(name, ref refByteBuffer); currentValue = refByteBuffer; break; case "Func`1": @@ -89,29 +152,30 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64 break; case "Int16": var refInt16 = (short)currentValue; - ser.Sync(member.Name, ref refInt16); + ser.Sync(name, ref refInt16); currentValue = refInt16; break; case "Int32": refInt32 = (int)currentValue; - ser.Sync(member.Name, ref refInt32); + ser.Sync(name, ref refInt32); currentValue = refInt32; break; case "Int32[]": refIntBuffer = new IntBuffer((int[])currentValue); - ser.Sync(member.Name, ref refIntBuffer); - currentValue = refIntBuffer.Arr; + ser.Sync(name, ref refIntBuffer); + currentValue = refIntBuffer.Arr.Select(d => d).ToArray(); + refIntBuffer.Dispose(); break; case "IntBuffer": refIntBuffer = (IntBuffer)currentValue; - ser.Sync(member.Name, ref refIntBuffer); + ser.Sync(name, ref refIntBuffer); currentValue = refIntBuffer; break; case "Point": refPointX = ((Point)currentValue).X; refPointY = ((Point)currentValue).Y; - ser.Sync(member.Name + "_X", ref refPointX); - ser.Sync(member.Name + "_Y", ref refPointY); + ser.Sync(name + "_X", ref refPointX); + ser.Sync(name + "_Y", ref refPointY); currentValue = new Point(refPointX, refPointY); break; case "Rectangle": @@ -119,31 +183,32 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64 refPointY = ((Rectangle)currentValue).Y; var refRectWidth = ((Rectangle)currentValue).Width; var refRectHeight = ((Rectangle)currentValue).Height; - ser.Sync(member.Name + "_X", ref refPointX); - ser.Sync(member.Name + "_Y", ref refPointY); - ser.Sync(member.Name + "_Height", ref refRectHeight); - ser.Sync(member.Name + "_Width", ref refRectWidth); + ser.Sync(name + "_X", ref refPointX); + ser.Sync(name + "_Y", ref refPointY); + ser.Sync(name + "_Height", ref refRectHeight); + ser.Sync(name + "_Width", ref refRectWidth); currentValue = new Rectangle(refPointX, refPointY, refRectWidth, refRectHeight); break; case "SByte": var refSByte = (sbyte)currentValue; - ser.Sync(member.Name, ref refSByte); + ser.Sync(name, ref refSByte); currentValue = refSByte; break; case "String": var refString = (string)currentValue; var refVal = new ByteBuffer(Encoding.GetBytes(refString)); - ser.Sync(member.Name, ref refVal); + ser.Sync(name, ref refVal); currentValue = Encoding.GetString(refVal.Arr); + refVal.Dispose(); break; case "UInt16": var refUInt16 = (ushort)currentValue; - ser.Sync(member.Name, ref refUInt16); + ser.Sync(name, ref refUInt16); currentValue = refUInt16; break; case "UInt32": var refUInt32 = (uint)currentValue; - ser.Sync(member.Name, ref refUInt32); + ser.Sync(name, ref refUInt32); currentValue = refUInt32; break; default: @@ -151,7 +216,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64 if (t.IsEnum) { refInt32 = (int)currentValue; - ser.Sync(member.Name, ref refInt32); + ser.Sync(name, ref refInt32); currentValue = refInt32; } else if (t.IsArray) @@ -159,7 +224,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64 var currentValueArray = (Array) currentValue; for (var i = 0; i < currentValueArray.Length; i++) { - ser.BeginSection(string.Format("{0}_{1}", member.Name, i)); + ser.BeginSection(string.Format("{0}_{1}", name, i)); SyncObject(ser, currentValueArray.GetValue(i)); ser.EndSection(); } diff --git a/BizHawk.Emulation.Cores/Computers/Commodore64/Serial/Drive1541.FluxTransitions.cs b/BizHawk.Emulation.Cores/Computers/Commodore64/Serial/Drive1541.FluxTransitions.cs index d59965f4ec..3ff65b0bb4 100644 --- a/BizHawk.Emulation.Cores/Computers/Commodore64/Serial/Drive1541.FluxTransitions.cs +++ b/BizHawk.Emulation.Cores/Computers/Commodore64/Serial/Drive1541.FluxTransitions.cs @@ -40,12 +40,12 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Serial if (_diskBitsLeft <= 0) { _diskByteOffset++; - if (_diskByteOffset == Disk.FLUX_ENTRIES_PER_TRACK) + if (_diskByteOffset == Disk.FluxEntriesPerTrack) { _diskByteOffset = 0; } _diskBits = _trackImageData[_diskByteOffset]; - _diskBitsLeft = Disk.FLUX_BITS_PER_ENTRY; + _diskBitsLeft = Disk.FluxBitsPerEntry; } if ((_diskBits & 1) != 0) { diff --git a/BizHawk.Emulation.Cores/Computers/Commodore64/Serial/Drive1541.cs b/BizHawk.Emulation.Cores/Computers/Commodore64/Serial/Drive1541.cs index 3e2662b11b..5e00181a6e 100644 --- a/BizHawk.Emulation.Cores/Computers/Commodore64/Serial/Drive1541.cs +++ b/BizHawk.Emulation.Cores/Computers/Commodore64/Serial/Drive1541.cs @@ -191,7 +191,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Serial if (_disk != null) { _trackImageData = _disk.GetDataForTrack(_trackNumber); - _diskBits = _trackImageData[_diskByteOffset] >> (Disk.FLUX_BITS_PER_ENTRY - _diskBitsLeft); + _diskBits = _trackImageData[_diskByteOffset] >> (Disk.FluxBitsPerEntry - _diskBitsLeft); } }