parent
cfdf607373
commit
8941bac1ce
|
@ -1,194 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Linq.Expressions;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Reflection.Emit;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
using BizHawk.Common.CollectionExtensions;
|
|
||||||
|
|
||||||
namespace BizHawk.Common
|
|
||||||
{
|
|
||||||
// fields are serialized/deserialized in their memory order as reported by Marshal.OffsetOf
|
|
||||||
// to do anything useful, passed targets should be [StructLayout.Sequential] or [StructLayout.Explicit]
|
|
||||||
public static class BinaryQuickSerializer
|
|
||||||
{
|
|
||||||
private static MethodInfo FromExpression(Expression e)
|
|
||||||
=> e is MethodCallExpression caller
|
|
||||||
? caller.Method
|
|
||||||
: throw new ArgumentException(message: "Expression must be a method call", paramName: nameof(e));
|
|
||||||
|
|
||||||
private static MethodInfo Method<T>(Expression<Action<T>> f)
|
|
||||||
{
|
|
||||||
return FromExpression(f.Body);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static readonly Dictionary<Type, MethodInfo> Readhandlers = new Dictionary<Type, MethodInfo>();
|
|
||||||
private static readonly Dictionary<Type, MethodInfo> Writehandlers = new Dictionary<Type, MethodInfo>();
|
|
||||||
|
|
||||||
private static void AddR<T>(Expression<Action<BinaryReader>> f)
|
|
||||||
{
|
|
||||||
var method = Method(f);
|
|
||||||
if (!typeof(T).IsAssignableFrom(method.ReturnType))
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("Type mismatch");
|
|
||||||
}
|
|
||||||
|
|
||||||
Readhandlers.Add(typeof(T), method);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void AddW<T>(Expression<Action<BinaryWriter>> f)
|
|
||||||
{
|
|
||||||
var method = Method(f);
|
|
||||||
if (!method.GetParameters()[0].ParameterType.IsAssignableFrom(typeof(T)))
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("Type mismatch");
|
|
||||||
}
|
|
||||||
|
|
||||||
Writehandlers.Add(typeof(T), Method(f));
|
|
||||||
}
|
|
||||||
|
|
||||||
static BinaryQuickSerializer()
|
|
||||||
{
|
|
||||||
AddR<bool>(r => r.ReadBoolean());
|
|
||||||
AddW<bool>(r => r.Write(false));
|
|
||||||
|
|
||||||
AddR<sbyte>(r => r.ReadSByte());
|
|
||||||
AddW<sbyte>(r => r.Write((sbyte)0));
|
|
||||||
|
|
||||||
AddR<byte>(r => r.ReadByte());
|
|
||||||
AddW<byte>(r => r.Write((byte)0U));
|
|
||||||
|
|
||||||
AddR<short>(r => r.ReadInt16());
|
|
||||||
AddW<short>(r => r.Write((short)0));
|
|
||||||
|
|
||||||
AddR<ushort>(r => r.ReadUInt16());
|
|
||||||
AddW<ushort>(r => r.Write((ushort)0U));
|
|
||||||
|
|
||||||
AddR<int>(r => r.ReadInt32());
|
|
||||||
AddW<int>(r => r.Write(0));
|
|
||||||
|
|
||||||
AddR<uint>(r => r.ReadUInt32());
|
|
||||||
AddW<uint>(r => r.Write(0U));
|
|
||||||
|
|
||||||
AddR<long>(r => r.ReadInt64());
|
|
||||||
AddW<long>(r => r.Write(0L));
|
|
||||||
|
|
||||||
AddR<ulong>(r => r.ReadUInt64());
|
|
||||||
AddW<ulong>(r => r.Write(0UL));
|
|
||||||
}
|
|
||||||
|
|
||||||
private delegate void Reader(object target, BinaryReader r);
|
|
||||||
private delegate void Writer(object target, BinaryWriter w);
|
|
||||||
|
|
||||||
private class SerializationFactory
|
|
||||||
{
|
|
||||||
public Type Type;
|
|
||||||
public Reader Read;
|
|
||||||
public Writer Write;
|
|
||||||
|
|
||||||
public SerializationFactory(Type type, Reader read, Writer write)
|
|
||||||
{
|
|
||||||
Type = type;
|
|
||||||
Read = read;
|
|
||||||
Write = write;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static SerializationFactory CreateFactory(Type t)
|
|
||||||
{
|
|
||||||
var fields = DeepEquality.GetAllFields(t)
|
|
||||||
////.OrderBy(fi => (int)fi.GetManagedOffset()) // [StructLayout.Sequential] doesn't work with this
|
|
||||||
.OrderBy(fi => (int)Marshal.OffsetOf(t, fi.Name))
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
var rmeth = new DynamicMethod($"{t.Name}_r", null, new[] { typeof(object), typeof(BinaryReader) }, true);
|
|
||||||
var wmeth = new DynamicMethod($"{t.Name}_w", null, new[] { typeof(object), typeof(BinaryWriter) }, true);
|
|
||||||
|
|
||||||
{
|
|
||||||
var il = rmeth.GetILGenerator();
|
|
||||||
var target = il.DeclareLocal(t);
|
|
||||||
il.Emit(OpCodes.Ldarg_0);
|
|
||||||
il.Emit(OpCodes.Castclass, t);
|
|
||||||
il.Emit(OpCodes.Stloc, target);
|
|
||||||
|
|
||||||
foreach (var field in fields)
|
|
||||||
{
|
|
||||||
il.Emit(OpCodes.Ldloc, target);
|
|
||||||
il.Emit(OpCodes.Ldarg_1);
|
|
||||||
if (!Readhandlers.TryGetValue(field.FieldType, out var m))
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException($"(R) Can't handle nested type {field.FieldType}");
|
|
||||||
}
|
|
||||||
|
|
||||||
il.Emit(OpCodes.Callvirt, m);
|
|
||||||
il.Emit(OpCodes.Stfld, field);
|
|
||||||
}
|
|
||||||
|
|
||||||
il.Emit(OpCodes.Ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
var il = wmeth.GetILGenerator();
|
|
||||||
var target = il.DeclareLocal(t);
|
|
||||||
il.Emit(OpCodes.Ldarg_0);
|
|
||||||
il.Emit(OpCodes.Castclass, t);
|
|
||||||
il.Emit(OpCodes.Stloc, target);
|
|
||||||
|
|
||||||
foreach (var field in fields)
|
|
||||||
{
|
|
||||||
il.Emit(OpCodes.Ldarg_1);
|
|
||||||
il.Emit(OpCodes.Ldloc, target);
|
|
||||||
il.Emit(OpCodes.Ldfld, field);
|
|
||||||
if (!Writehandlers.TryGetValue(field.FieldType, out var m))
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException($"(W) Can't handle nested type {field.FieldType}");
|
|
||||||
}
|
|
||||||
|
|
||||||
il.Emit(OpCodes.Callvirt, m);
|
|
||||||
}
|
|
||||||
|
|
||||||
il.Emit(OpCodes.Ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new SerializationFactory(
|
|
||||||
t,
|
|
||||||
(Reader) rmeth.CreateDelegate(typeof(Reader)),
|
|
||||||
(Writer) wmeth.CreateDelegate(typeof(Writer)));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static readonly IDictionary<Type, SerializationFactory> Serializers =
|
|
||||||
new ConcurrentDictionary<Type, SerializationFactory>();
|
|
||||||
|
|
||||||
private static SerializationFactory GetFactory(Type t)
|
|
||||||
=> Serializers.GetValueOrPut(t, CreateFactory);
|
|
||||||
|
|
||||||
public static T Create<T>(BinaryReader r)
|
|
||||||
where T : new()
|
|
||||||
{
|
|
||||||
T target = new T();
|
|
||||||
Read(target, r);
|
|
||||||
return target;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static object Create(Type t, BinaryReader r)
|
|
||||||
{
|
|
||||||
object target = Activator.CreateInstance(t);
|
|
||||||
Read(target, r);
|
|
||||||
return target;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Read(object target, BinaryReader r)
|
|
||||||
{
|
|
||||||
GetFactory(target.GetType()).Read(target, r);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Write(object target, BinaryWriter w)
|
|
||||||
{
|
|
||||||
GetFactory(target.GetType()).Write(target, w);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -5,17 +5,16 @@ using Newtonsoft.Json;
|
||||||
using BizHawk.Common;
|
using BizHawk.Common;
|
||||||
using BizHawk.Emulation.Common;
|
using BizHawk.Emulation.Common;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using BizHawk.Common.IOExtensions;
|
||||||
|
|
||||||
namespace BizHawk.Emulation.Cores.WonderSwan
|
namespace BizHawk.Emulation.Cores.WonderSwan
|
||||||
{
|
{
|
||||||
partial class WonderSwan: ITextStatable
|
partial class WonderSwan: ITextStatable
|
||||||
{
|
{
|
||||||
private void InitIStatable()
|
private void InitIStatable()
|
||||||
{
|
=> savebuff = new byte[BizSwan.bizswan_binstatesize(Core)];
|
||||||
savebuff = new byte[BizSwan.bizswan_binstatesize(Core)];
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly JsonSerializer ser = new JsonSerializer { Formatting = Formatting.Indented };
|
private readonly JsonSerializer ser = new() { Formatting = Formatting.Indented };
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
private class TextStateData
|
private class TextStateData
|
||||||
|
@ -31,6 +30,7 @@ namespace BizHawk.Emulation.Cores.WonderSwan
|
||||||
LagCount = d.LagCount;
|
LagCount = d.LagCount;
|
||||||
Frame = d.Frame;
|
Frame = d.Frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SaveTextStateData(TextStateData d)
|
private void SaveTextStateData(TextStateData d)
|
||||||
{
|
{
|
||||||
d.IsLagFrame = IsLagFrame;
|
d.IsLagFrame = IsLagFrame;
|
||||||
|
@ -51,10 +51,17 @@ namespace BizHawk.Emulation.Cores.WonderSwan
|
||||||
public void LoadStateText(TextReader reader)
|
public void LoadStateText(TextReader reader)
|
||||||
{
|
{
|
||||||
var s = (TextState<TextStateData>)ser.Deserialize(reader, typeof(TextState<TextStateData>));
|
var s = (TextState<TextStateData>)ser.Deserialize(reader, typeof(TextState<TextStateData>));
|
||||||
s.Prepare();
|
if (s is not null)
|
||||||
var ff = s.GetFunctionPointersLoad();
|
{
|
||||||
BizSwan.bizswan_txtstateload(Core, ref ff);
|
s.Prepare();
|
||||||
LoadTextStateData(s.ExtraData);
|
var ff = s.GetFunctionPointersLoad();
|
||||||
|
BizSwan.bizswan_txtstateload(Core, ref ff);
|
||||||
|
LoadTextStateData(s.ExtraData);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Failed to deserialize state");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] savebuff;
|
private byte[] savebuff;
|
||||||
|
@ -64,26 +71,37 @@ namespace BizHawk.Emulation.Cores.WonderSwan
|
||||||
public void SaveStateBinary(BinaryWriter writer)
|
public void SaveStateBinary(BinaryWriter writer)
|
||||||
{
|
{
|
||||||
if (!BizSwan.bizswan_binstatesave(Core, savebuff, savebuff.Length))
|
if (!BizSwan.bizswan_binstatesave(Core, savebuff, savebuff.Length))
|
||||||
|
{
|
||||||
throw new InvalidOperationException($"{nameof(BizSwan.bizswan_binstatesave)}() returned false!");
|
throw new InvalidOperationException($"{nameof(BizSwan.bizswan_binstatesave)}() returned false!");
|
||||||
|
}
|
||||||
|
|
||||||
writer.Write(savebuff.Length);
|
writer.Write(savebuff.Length);
|
||||||
writer.Write(savebuff);
|
writer.Write(savebuff);
|
||||||
|
|
||||||
var d = new TextStateData();
|
// other variables
|
||||||
SaveTextStateData(d);
|
writer.Write(IsLagFrame);
|
||||||
BinaryQuickSerializer.Write(d, writer);
|
writer.Write(LagCount);
|
||||||
|
writer.Write(Frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LoadStateBinary(BinaryReader reader)
|
public void LoadStateBinary(BinaryReader reader)
|
||||||
{
|
{
|
||||||
int length = reader.ReadInt32();
|
var length = reader.ReadInt32();
|
||||||
if (length != savebuff.Length)
|
if (length != savebuff.Length)
|
||||||
|
{
|
||||||
throw new InvalidOperationException("Save buffer size mismatch!");
|
throw new InvalidOperationException("Save buffer size mismatch!");
|
||||||
|
}
|
||||||
|
|
||||||
reader.Read(savebuff, 0, length);
|
reader.Read(savebuff, 0, length);
|
||||||
if (!BizSwan.bizswan_binstateload(Core, savebuff, savebuff.Length))
|
if (!BizSwan.bizswan_binstateload(Core, savebuff, savebuff.Length))
|
||||||
|
{
|
||||||
throw new InvalidOperationException($"{nameof(BizSwan.bizswan_binstateload)}() returned false!");
|
throw new InvalidOperationException($"{nameof(BizSwan.bizswan_binstateload)}() returned false!");
|
||||||
|
}
|
||||||
|
|
||||||
var d = BinaryQuickSerializer.Create<TextStateData>(reader);
|
// other variables
|
||||||
LoadTextStateData(d);
|
IsLagFrame = reader.ReadBoolean();
|
||||||
|
LagCount = reader.ReadInt32();
|
||||||
|
Frame = reader.ReadInt32();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue