Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
alexbevi
GitHub Repository: alexbevi/BizHawk
Path: blob/master/BizHawk.Emulation.Cores/Computers/Commodore64/SaveState.cs
2 views
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);
                    }
                }
            }
		}
	}
}