diff --git a/AxiReplay.csproj b/AxiReplay.csproj new file mode 100644 index 0000000..dbdcea4 --- /dev/null +++ b/AxiReplay.csproj @@ -0,0 +1,7 @@ + + + + netstandard2.0 + + + diff --git a/ReplayData.cs b/ReplayData.cs new file mode 100644 index 0000000..ab3dd58 --- /dev/null +++ b/ReplayData.cs @@ -0,0 +1,101 @@ +using System; +using System.Runtime.InteropServices; +using System.Text; + +namespace AxiReplay +{ + [StructLayout(LayoutKind.Explicit,Size = 44)] + public struct ReplayHandler + { + [FieldOffset(0)] + public int Format; + [FieldOffset(sizeof(int) * 1)] + public int RomID; + [FieldOffset(sizeof(int) * 2)] + public int RomType; + [FieldOffset(sizeof(int) * 3)] + public int DataOffset; + [FieldOffset(sizeof(int) * 4)] + public int TitleOffset; + [FieldOffset(sizeof(int) * 5)] + public int NoteOffset; + [FieldOffset(sizeof(int) * 6)] + public int AllFrame; + [FieldOffset(sizeof(int) * 7)] + public int AllTime; + [FieldOffset(sizeof(int) * 8)] + public int SingleLenght; + [FieldOffset(sizeof(int) * 9)] + public long CreateTime; + } + + [StructLayout(LayoutKind.Explicit)] + public struct ReplayStep + { + [FieldOffset(0)] + public UInt64 All64Data; + [FieldOffset(0)] + public Int32 FrameStartID; + [FieldOffset(4)] + public UInt64 InPut; + } + + public static class ReplayData + { + public static int HandlerLenght = sizeof(int) * 9 + sizeof(long); + public enum ReplayFormat : byte + { + None = 0, + FM32IPBYTE, + FM32IP16, + FM32IP32, + FM32IP64, + } + public static void GetStringByteData(string str,out byte[] data,out int lenghtWithEnd,Encoding encoding) + { + data = encoding.GetBytes(str); + lenghtWithEnd = data.Length + 1; + } + + public static byte[] GetHandlerData(ReplayHandler replayhandler) + { + int size = Marshal.SizeOf(typeof(ReplayHandler)); + byte[] arr = new byte[size]; + + IntPtr ptr = Marshal.AllocHGlobal(size); + try + { + Marshal.StructureToPtr(replayhandler, ptr, false); + Marshal.Copy(ptr, arr, 0, size); + } + finally + { + Marshal.FreeHGlobal(ptr); + } + + return arr; + } + + public static ReplayHandler GetReplayHandlerFromData(byte[] data) + { + if (data == null || data.Length < ReplayData.HandlerLenght) + { + throw new ArgumentException("Invalid data length or null data."); + } + + IntPtr ptr = Marshal.AllocHGlobal(ReplayData.HandlerLenght); + try + { + // 将byte数组的内容复制到非托管内存中 + Marshal.Copy(data, 0, ptr, ReplayData.HandlerLenght); + // 从非托管内存将内容转换回ReplayHandler结构体 + return (ReplayHandler)Marshal.PtrToStructure(ptr, typeof(ReplayHandler)); + } + finally + { + // 释放非托管内存 + Marshal.FreeHGlobal(ptr); + } + } + } +} diff --git a/ReplayReader.cs b/ReplayReader.cs new file mode 100644 index 0000000..7410eb7 --- /dev/null +++ b/ReplayReader.cs @@ -0,0 +1,173 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using static AxiReplay.ReplayData; + +namespace AxiReplay +{ + public class ReplayReader : IDisposable + { + public ReplayData.ReplayFormat mFormat { get; private set; } + public Encoding TexEncoding { get; private set; } + ReplayHandler handler; + string mTitle; + string mNote; + int mAllFrame; + int mAllTime; + long mData; + int mSingleInputLenght; + int mSingleDataLenght; + FileStream mStream; + BinaryReader mBinaryReader; + + int mCurrFrame = -1; + byte[] mNextOutbytes; + ReplayStep currStep; + ReplayStep nextStep; + bool bEnd; + + List dbgList = new List(); + bool bdbg = false; + string dumpPath; + + public ReplayReader(string path, bool bWithDump = false, string dumppath = null) + { + dbgList.Clear(); + bdbg = bWithDump; + dumpPath = dumppath; + mStream = new FileStream(path, FileMode.Open, FileAccess.Read); + mBinaryReader = new BinaryReader(mStream); + byte[] Outbytes; + Outbytes = mBinaryReader.ReadBytes(ReplayData.HandlerLenght); + handler = ReplayData.GetReplayHandlerFromData(Outbytes); + mFormat = (ReplayFormat)handler.Format; + switch (mFormat) + { + case ReplayData.ReplayFormat.FM32IP64: mSingleInputLenght = sizeof(UInt64); break; + case ReplayData.ReplayFormat.FM32IP32: mSingleInputLenght = sizeof(UInt32); break; + case ReplayData.ReplayFormat.FM32IP16: mSingleInputLenght = sizeof(UInt16); break; + case ReplayData.ReplayFormat.FM32IPBYTE: mSingleInputLenght = sizeof(byte); break; + } + //Frame+Lenght + mSingleDataLenght = (sizeof(UInt32)) + mSingleInputLenght; + nextStep = new ReplayStep(); + nextStep.FrameStartID = -1; + bEnd = false; + + dbgList.Add($"Format => {handler.Format}"); + dbgList.Add($"DataOffset => {handler.DataOffset}"); + dbgList.Add($"CreateTime => {handler.CreateTime}"); + dbgList.Add($"AllFrame => {handler.AllFrame}"); + dbgList.Add($"SingleLenght => {handler.SingleLenght}"); + + + mNextOutbytes = new byte[mSingleDataLenght]; + + if (bWithDump) + { + int TestFrameIdx = -1; + while (!bEnd) + { + UpdateNextFrame(TestFrameIdx++); + } + File.WriteAllLines(dumppath, dbgList); + } + else + { + UpdateNextFrame(0); + } + } + + + void UpdateNextFrame(int targetFrame) + { + //如果已经超过 + while (targetFrame > nextStep.FrameStartID) + { + if (nextStep.FrameStartID >= handler.AllFrame) + { + bEnd = true; + break; + } + mBinaryReader.Read(mNextOutbytes, 0, mSingleDataLenght); + switch (mFormat) + { + case ReplayFormat.FM32IP64: + { + nextStep.FrameStartID = BitConverter.ToInt32(mNextOutbytes, 0); + nextStep.InPut = BitConverter.ToUInt64(mNextOutbytes, sizeof(UInt32)); + } + break; + case ReplayFormat.FM32IP32: + { + nextStep.All64Data = BitConverter.ToUInt64(mNextOutbytes, 0); + } + break; + case ReplayFormat.FM32IP16: + { + nextStep.All64Data = BitConverter.ToUInt64(mNextOutbytes, 0); + } + break; + case ReplayFormat.FM32IPBYTE: + { + nextStep.All64Data = BitConverter.ToUInt64(mNextOutbytes, 0); + } + break; + } + dbgList.Add($"{nextStep.FrameStartID} | {nextStep.InPut}"); + + targetFrame++; + } + } + + int byFrameIdx = 0; + /// + /// 往前推进帧的,指定帧下标 + /// + public bool NextFramebyFrameIdx(int FrameID,out ReplayStep data) + { + bool res = TakeFrame(FrameID - byFrameIdx, out data); + byFrameIdx = FrameID; + return res; + } + + /// + /// 往前推进1帧的Input(返回是否变化) + /// + public bool NextFrame(out ReplayStep data) + { + return TakeFrame(1,out data); + } + + /// + /// 往前推进指定帧数量的Input (返回是否变化) + /// + /// + public bool TakeFrame(int addFrame,out ReplayStep data) + { + bool Changed = false; + mCurrFrame += addFrame; + if (mCurrFrame >= nextStep.FrameStartID) + { + Changed = currStep.InPut != nextStep.InPut; + currStep = nextStep; + data = currStep; + UpdateNextFrame(mCurrFrame); + } + else + { + data = currStep; + } + return Changed; + } + + public void Dispose() + { + mStream.Dispose(); + mBinaryReader.Dispose(); + //TODO + } + + } +} diff --git a/ReplayWriter.cs b/ReplayWriter.cs new file mode 100644 index 0000000..3d0f516 --- /dev/null +++ b/ReplayWriter.cs @@ -0,0 +1,156 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace AxiReplay +{ + public class ReplayWriter : IDisposable + { + public ReplayData.ReplayFormat mFormat { get; private set; } + public Encoding TexEncoding { get; private set; } + ReplayHandler handler; + string mTitle; + string mNote; + int mAllFrame; + int mAllTime; + long mData; + int mSingleInputLenght; + int mSingleDataLenght; + MemoryStream mStream; + BinaryWriter mBinaryWriter; + + int mCurrFrame; + UInt64 mCurrInput; + ReplayStep wirteStep; + + List dbgList = new List(); + + public ReplayWriter(string Title, string Note, ReplayData.ReplayFormat format, Encoding encoding) + { + mTitle = Title; + mNote = Note; + TexEncoding = encoding; + mFormat = format; + switch (mFormat) + { + case ReplayData.ReplayFormat.FM32IP64: mSingleInputLenght = sizeof(UInt64); break; + case ReplayData.ReplayFormat.FM32IP32: mSingleInputLenght = sizeof(UInt32); break; + case ReplayData.ReplayFormat.FM32IP16: mSingleInputLenght = sizeof(UInt16); break; + case ReplayData.ReplayFormat.FM32IPBYTE: mSingleInputLenght = sizeof(byte); break; + } + mSingleDataLenght = (sizeof(UInt32)) + mSingleInputLenght; + + mStream = new MemoryStream(); + mBinaryWriter = new BinaryWriter(mStream); + + mCurrFrame = -1; + mCurrInput = int.MaxValue; + wirteStep = new ReplayStep(); + + dbgList.Clear(); + + } + + int byFrameIdx = 0; + /// + /// 往前推进帧的,指定帧下标 + /// + /// + public void NextFramebyFrameIdx(int FrameID,UInt64 frameInput) + { + TakeFrame(FrameID - byFrameIdx, frameInput); + byFrameIdx = FrameID; + } + + /// + /// 往前推进1帧的Input + /// + /// + public void NextFrame(UInt64 frameInput) + { + TakeFrame(1, frameInput); + } + + /// + /// 往前推进指定帧数量的Input + /// + /// + public void TakeFrame(int addFrame, UInt64 frameInput) + { + if (addFrame < 0) + { + + } + mCurrFrame += addFrame; + if (mCurrInput == frameInput) + return; + mCurrInput = frameInput; + + wirteStep.FrameStartID = mCurrFrame; + wirteStep.InPut = mCurrInput; + dbgList.Add($"{mCurrFrame} | {mCurrInput}"); + + switch (mFormat) + { + case ReplayData.ReplayFormat.FM32IP64: + mBinaryWriter.Write(wirteStep.FrameStartID); + mBinaryWriter.Write(wirteStep.InPut); + break; + case ReplayData.ReplayFormat.FM32IP32: + mBinaryWriter.Write(BitConverter.GetBytes(wirteStep.All64Data), 0, 4 + 4); + break; + case ReplayData.ReplayFormat.FM32IP16: + mBinaryWriter.Write(BitConverter.GetBytes(wirteStep.All64Data), 0, 4 + 2); + break; + case ReplayData.ReplayFormat.FM32IPBYTE: + mBinaryWriter.Write(BitConverter.GetBytes(wirteStep.All64Data), 0, 4 + 1); + break; + } + } + + public void SaveData(string path, bool bWithDump = false, string dumppath = null) + { + ReplayData.GetStringByteData(mTitle, out byte[] titleData, out int titleLenghtWithEnd, TexEncoding); + ReplayData.GetStringByteData(mNote, out byte[] noteData, out int noteLenghtWithEnd, TexEncoding); + + ReplayHandler handler = new ReplayHandler(); + handler.Format = (int)this.mFormat; + handler.DataOffset = ReplayData.HandlerLenght; + handler.CreateTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); + handler.AllFrame = wirteStep.FrameStartID; + handler.SingleLenght = mSingleDataLenght; + + using (FileStream fs = new FileStream(path, FileMode.Create)) + { + using (BinaryWriter bw = new BinaryWriter(fs)) + { + //写入Handler + bw.Write(ReplayData.GetHandlerData(handler)); + //写入Data + bw.Write(mStream.ToArray()); + } + } + + if (bWithDump) + { + List temp = new List(); + temp.Add($"Format => {handler.Format}"); + temp.Add($"DataOffset => {handler.DataOffset}"); + temp.Add($"CreateTime => {handler.CreateTime}"); + temp.Add($"AllFrame => {handler.AllFrame}"); + temp.Add($"SingleLenght => {handler.SingleLenght}"); + dbgList.InsertRange(0,temp); + File.WriteAllLines(dumppath, dbgList); + } + } + + public void Dispose() + { + mStream.Dispose(); + mBinaryWriter.Dispose(); + //TODO + } + + } +}