BizHawk/BizHawk.Emulation.Cores/Computers/Commodore64/SaveState.cs

318 lines
8.5 KiB
C#

using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Reflection;
using System.Text;
using BizHawk.Common;
namespace BizHawk.Emulation.Cores.Computers.Commodore64
{
internal static class SaveState
{
public class DoNotSave : Attribute
{
}
public class SaveWithName : Attribute
{
public string Name { get; set; }
public SaveWithName(string name)
{
Name = name;
}
}
private static readonly Encoding Encoding = Encoding.Unicode;
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;
}
private static byte[] CompressInts(int[] data)
{
unchecked
{
var length = data.Length;
var bytes = new byte[length * 4];
for (int i = 0, j = 0; i < length; i++)
{
var c = data[i];
bytes[j++] = (byte)(c);
bytes[j++] = (byte)(c >> 8);
bytes[j++] = (byte)(c >> 16);
bytes[j++] = (byte)(c >> 24);
}
using (var mem = new MemoryStream())
{
using (var compressor = new DeflateStream(mem, CompressionMode.Compress))
{
var writer = new BinaryWriter(compressor);
writer.Write(bytes.Length);
writer.Write(bytes);
compressor.Flush();
}
mem.Flush();
return mem.ToArray();
}
}
}
private static int[] DecompressInts(byte[] data)
{
unchecked
{
using (var mem = new MemoryStream(data))
{
using (var decompressor = new DeflateStream(mem, CompressionMode.Decompress))
{
var reader = new BinaryReader(decompressor);
var length = reader.ReadInt32();
var bytes = reader.ReadBytes(length);
var result = new int[length >> 2];
for (int i = 0, j = 0; i < length; i++)
{
int d = bytes[i++];
d |= bytes[i++] << 8;
d |= bytes[i++] << 16;
d |= bytes[i] << 24;
result[j++] = d;
}
return result;
}
}
}
}
public static void SyncDelta(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();
var members = objType.GetMembers(defaultFlags);
foreach (var member in members)
{
if (member.GetCustomAttributes(true).Any(a => a is DoNotSave))
{
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;
Type valueType = null;
if ((member.MemberType == MemberTypes.Field) && member.ReflectedType != null)
{
valueType = fieldInfo.FieldType;
currentValue = fieldInfo.GetValue(obj);
}
if (currentValue != null)
{
ByteBuffer refByteBuffer;
int refInt32;
IntBuffer refIntBuffer;
int refPointX;
int refPointY;
switch (valueType.Name)
{
case "Action`1":
case "Action`2":
break;
case "Bit":
var refBit = (Bit)currentValue;
ser.Sync(name, ref refBit);
currentValue = refBit;
break;
case "Boolean":
var refBool = (bool)currentValue;
ser.Sync(name, ref refBool);
currentValue = refBool;
break;
case "Boolean[]":
{
var tmp = (bool[])currentValue;
ser.Sync(name, ref tmp, false);
currentValue = tmp;
}
break;
case "Byte":
var refByte = (byte)currentValue;
ser.Sync(name, ref refByte);
currentValue = refByte;
break;
case "Byte[]":
refByteBuffer = new ByteBuffer((byte[])currentValue);
ser.Sync(name, ref refByteBuffer);
currentValue = refByteBuffer.Arr.Select(d => d).ToArray();
refByteBuffer.Dispose();
break;
case "ByteBuffer":
refByteBuffer = (ByteBuffer)currentValue;
ser.Sync(name, ref refByteBuffer);
currentValue = refByteBuffer;
break;
case "Func`1":
case "Func`2":
break;
case "Int16":
var refInt16 = (short)currentValue;
ser.Sync(name, ref refInt16);
currentValue = refInt16;
break;
case "Int32":
refInt32 = (int)currentValue;
ser.Sync(name, ref refInt32);
currentValue = refInt32;
break;
case "Int32[]":
refIntBuffer = new IntBuffer((int[])currentValue);
ser.Sync(name, ref refIntBuffer);
currentValue = refIntBuffer.Arr.Select(d => d).ToArray();
refIntBuffer.Dispose();
break;
case "IntBuffer":
refIntBuffer = (IntBuffer)currentValue;
ser.Sync(name, ref refIntBuffer);
currentValue = refIntBuffer;
break;
case "Point":
refPointX = ((Point)currentValue).X;
refPointY = ((Point)currentValue).Y;
ser.Sync(name + "_X", ref refPointX);
ser.Sync(name + "_Y", ref refPointY);
currentValue = new Point(refPointX, refPointY);
break;
case "Rectangle":
refPointX = ((Rectangle)currentValue).X;
refPointY = ((Rectangle)currentValue).Y;
var refRectWidth = ((Rectangle)currentValue).Width;
var refRectHeight = ((Rectangle)currentValue).Height;
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(name, ref refSByte);
currentValue = refSByte;
break;
case "String":
var refString = (string)currentValue;
var refVal = new ByteBuffer(Encoding.GetBytes(refString));
ser.Sync(name, ref refVal);
currentValue = Encoding.GetString(refVal.Arr);
refVal.Dispose();
break;
case "UInt16":
var refUInt16 = (ushort)currentValue;
ser.Sync(name, ref refUInt16);
currentValue = refUInt16;
break;
case "UInt32":
var refUInt32 = (uint)currentValue;
ser.Sync(name, ref refUInt32);
currentValue = refUInt32;
break;
default:
var t = currentValue.GetType();
if (t.IsEnum)
{
refInt32 = (int)currentValue;
ser.Sync(name, ref refInt32);
currentValue = refInt32;
}
else if (t.IsArray)
{
var currentValueArray = (Array)currentValue;
for (var i = 0; i < currentValueArray.Length; i++)
{
ser.BeginSection(string.Format("{0}_{1}", name, i));
SyncObject(ser, currentValueArray.GetValue(i));
ser.EndSection();
}
}
else if (t.IsValueType)
{
fail = true;
}
else if (t.IsClass)
{
fail = true;
foreach (var method in t.GetMethods().Where(method => method.Name == "SyncState"))
{
ser.BeginSection(fieldInfo.Name);
method.Invoke(currentValue, new object[] { ser });
ser.EndSection();
fail = false;
break;
}
}
else
{
fail = true;
}
break;
}
}
if (!fail)
{
if (member.MemberType == MemberTypes.Property)
{
var propInfo = member as PropertyInfo;
if (propInfo.CanWrite)
{
var setMethod = propInfo.GetSetMethod();
if (setMethod != null)
{
setMethod.Invoke(obj, new[] { currentValue });
}
}
}
else if (member.MemberType == MemberTypes.Field)
{
fieldInfo.SetValue(obj, currentValue);
}
}
}
}
}
}