C64: Add bare delta media savestate support.
This commit is contained in:
parent
67edd5df1e
commit
472616c4f7
|
@ -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<int[]> _tracks;
|
||||
[SaveState.DoNotSave] private readonly int[] _originalMedia;
|
||||
[SaveState.DoNotSave] public bool Valid;
|
||||
|
||||
/// <summary>
|
||||
|
@ -26,9 +19,10 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Media
|
|||
/// </summary>
|
||||
public Disk(int trackCapacity)
|
||||
{
|
||||
_tracks = new Track[trackCapacity];
|
||||
_tracks = new List<int[]>(trackCapacity);
|
||||
FillMissingTracks();
|
||||
Valid = true;
|
||||
_originalMedia = SerializeTracks(_tracks);
|
||||
Valid = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -41,75 +35,101 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Media
|
|||
/// <param name="trackCapacity">Total number of tracks on the media.</param>
|
||||
public Disk(IList<byte[]> trackData, IList<int> trackNumbers, IList<int> trackDensities, IList<int> trackLengths, int trackCapacity)
|
||||
{
|
||||
_tracks = new Track[trackCapacity];
|
||||
_tracks = Enumerable.Repeat<int[]>(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<long>();
|
||||
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<int> GetPresentTracks()
|
||||
/// <summary>
|
||||
/// Combine the tracks into a single bitstream.
|
||||
/// </summary>
|
||||
private int[] SerializeTracks(IEnumerable<int[]> tracks)
|
||||
{
|
||||
return _tracks.Where(t => t.Present).Select(t => t.Index);
|
||||
}
|
||||
return tracks.SelectMany(t => t).ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Split a bitstream into tracks.
|
||||
/// </summary>
|
||||
private IEnumerable<int[]> 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<int> source, IList<int> 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();
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue