diff --git a/src/common/Haru.ByteBanger.ts b/src/common/Haru.ByteBanger.ts new file mode 100644 index 0000000..11d2b81 --- /dev/null +++ b/src/common/Haru.ByteBanger.ts @@ -0,0 +1,295 @@ +export enum DiffResultType +{ + Success = 0, + OriginalFilePathInvalid, + OriginalFileNotFound, + OriginalFileReadFailed, + PatchedFilePathInvalid, + PatchedFileNotFound, + PatchedFileReadFailed, + FilesMatch +} + +export enum PatchResultType +{ + Success = 0, + InputLengthMismatch, + InputChecksumMismatch, + AlreadyPatched, + OutputChecksumMismatch +} + +export class PatchItem +{ + // +} + +export class PatchInfo +{ + // +} + +class DiffResult +{ + public readonly Result: DiffResultType + public readonly PatchInfo: PatchInfo + + public constructor(result: DiffResultType, patchInfo: PatchInfo) + { + this.Result = result; + this.PatchInfo = patchInfo; + } +} + +export class PatchResult +{ + public readonly Result: PatchResultType + public readonly PatchedData: Uint8Array + + public constructor(result: PatchResultType, patchedData: Uint8Array) + { + this.Result = result; + this.PatchedData = patchedData; + } +} + +/* +public class PatchItem +{ + public int Offset { get; set; } + public byte[] Data { get; set; } + + public static PatchItem FromReader(BinaryReader br) + { + int offset = br.ReadInt32(); + int dataLength = br.ReadInt32(); + byte[] data = br.ReadBytes(dataLength); + + return new PatchItem + { + Offset = offset, + Data = data + }; + } + + internal void ToWriter(BinaryWriter bw) + { + // offset // 4B + bw.Write(Offset); + + // length // 4B + bw.Write(Data.Length); + + // data // xB + bw.Write(Data, 0, Data.Length); + } +} + +public class PatchInfo +{ + public const string BYBA = "BYBA"; + public byte[] OriginalChecksum { get; set; } + public int OriginalLength { get; set; } + public byte[] PatchedChecksum { get; set; } + public int PatchedLength { get; set; } + public PatchItem[] Items { get; set; } + + public static PatchInfo FromBytes(byte[] bytes) + { + if (bytes.Length < 82) throw new Exception("Input data too short, cannot be a valid patch"); + + PatchInfo pi = new PatchInfo(); + + using (MemoryStream ms = new MemoryStream(bytes)) + using (BinaryReader br = new BinaryReader(ms)) + { + byte[] buf = null; + + buf = br.ReadBytes(4); + if (Encoding.ASCII.GetString(buf) != BYBA) throw new Exception("Invalid identifier"); + + if (br.ReadByte() != 1) throw new Exception("Invalid major file version (1 expected)"); + if (br.ReadByte() != 0) throw new Exception("Invalid minor file version (0 expected)"); + + pi.OriginalLength = br.ReadInt32(); + pi.OriginalChecksum = br.ReadBytes(32); + pi.PatchedLength = br.ReadInt32(); + pi.PatchedChecksum = br.ReadBytes(32); + + int itemCount = br.ReadInt32(); + + List items = new List(); + for (int i = 0; i < itemCount; i++) + items.Add(PatchItem.FromReader(br)); + pi.Items = items.ToArray(); + } + + return pi; + } + + public byte[] ToBytes() + { + byte[] data; + + using (MemoryStream ms = new MemoryStream()) + { + using (BinaryWriter bw = new BinaryWriter(ms, Encoding.ASCII, true)) + { + // identifier "BYBA" // 4B + byte[] byba = Encoding.ASCII.GetBytes(BYBA); + bw.Write(byba, 0, byba.Length); + + // version "1.0" // 2B + bw.Write((byte)1); + bw.Write((byte)0); + + // original len // 4B + bw.Write(OriginalLength); + + // original chk // 32B + bw.Write(OriginalChecksum, 0, OriginalChecksum.Length); + + // patched len // 4B + bw.Write(PatchedLength); + + // patched chk // 32B + bw.Write(PatchedChecksum, 0, PatchedChecksum.Length); + + // item count // 4B + bw.Write(Items.Length); + + // data + foreach (PatchItem pi in Items) + pi.ToWriter(bw); + } + + data = new byte[ms.Length]; + ms.Seek(0, SeekOrigin.Begin); + ms.Read(data, 0, (int)ms.Length); + } + + return data; + } +} + +public static class PatchUtil +{ + public static DiffResult Diff(byte[] original, byte[] patched) + { + PatchInfo pi = new PatchInfo + { + OriginalLength = original.Length, + PatchedLength = patched.Length + }; + + using (SHA256CryptoServiceProvider sha256 = new SHA256CryptoServiceProvider()) + { + pi.OriginalChecksum = sha256.ComputeHash(original); + pi.PatchedChecksum = sha256.ComputeHash(patched); + } + + if ((pi.OriginalLength == pi.PatchedLength) && ArraysMatch(pi.OriginalChecksum, pi.PatchedChecksum)) + return new DiffResult(DiffResultType.FilesMatch, null); + + int minLength = Math.Min(pi.OriginalLength, pi.PatchedLength); + + List items = new List(); + List currentData = null; + int diffOffsetStart = 0; + + for (int i = 0; i < minLength; i++) + { + if (original[i] != patched[i]) + { + if (currentData == null) + { + diffOffsetStart = i; + currentData = new List(); + } + + currentData.Add(patched[i]); + } + else + { + if (currentData != null) + items.Add(new PatchItem { Offset = diffOffsetStart, Data = currentData.ToArray() }); + + currentData = null; + diffOffsetStart = 0; + } + } + + if (currentData != null) + items.Add(new PatchItem { Offset = diffOffsetStart, Data = currentData.ToArray() }); + + if (pi.PatchedLength > pi.OriginalLength) + { + byte[] buf = new byte[pi.PatchedLength - pi.OriginalLength]; + Array.Copy(patched, pi.OriginalLength, buf, 0, buf.Length); + items.Add(new PatchItem { Offset = pi.OriginalLength, Data = buf }); + } + + pi.Items = items.ToArray(); + + return new DiffResult(DiffResultType.Success, pi); + } + + public static DiffResult Diff(string originalFile, string patchedFile) + { + if (string.IsNullOrWhiteSpace(originalFile)) return new DiffResult(DiffResultType.OriginalFilePathInvalid, null); + if (string.IsNullOrWhiteSpace(patchedFile)) return new DiffResult(DiffResultType.PatchedFilePathInvalid, null); + if (!File.Exists(originalFile)) return new DiffResult(DiffResultType.OriginalFileNotFound, null); + if (!File.Exists(patchedFile)) return new DiffResult(DiffResultType.PatchedFileNotFound, null); + + byte[] originalData, patchedData; + + try { originalData = File.ReadAllBytes(originalFile); } + catch { return new DiffResult(DiffResultType.OriginalFileReadFailed, null); } + + try { patchedData = File.ReadAllBytes(patchedFile); } + catch { return new DiffResult(DiffResultType.PatchedFileReadFailed, null); } + + return Diff(originalData, patchedData); + } + + public static PatchResult Patch(byte[] input, PatchInfo pi) + { + byte[] inputHash; + using (SHA256CryptoServiceProvider sha256 = new SHA256CryptoServiceProvider()) + { + inputHash = sha256.ComputeHash(input); + } + + if (ArraysMatch(inputHash, pi.PatchedChecksum)) return new PatchResult(PatchResultType.AlreadyPatched, null); + if (!ArraysMatch(inputHash, pi.OriginalChecksum)) return new PatchResult(PatchResultType.InputChecksumMismatch, null); + if (input.Length != pi.OriginalLength) return new PatchResult(PatchResultType.InputLengthMismatch, null); + + byte[] patchedData = new byte[pi.PatchedLength]; + long minLen = Math.Min(pi.OriginalLength, pi.PatchedLength); + Array.Copy(input, patchedData, minLen); + + foreach (PatchItem itm in pi.Items) + Array.Copy(itm.Data, 0, patchedData, itm.Offset, itm.Data.Length); + + byte[] patchedHash; + using (SHA256CryptoServiceProvider sha256 = new SHA256CryptoServiceProvider()) + { + patchedHash = sha256.ComputeHash(patchedData); + } + + if (!ArraysMatch(patchedHash, pi.PatchedChecksum)) return new PatchResult(PatchResultType.OutputChecksumMismatch, null); + + return new PatchResult(PatchResultType.Success, patchedData); + } + + private static bool ArraysMatch(byte[] a, byte[] b) + { + if (a.Length != b.Length) return false; + + for (int i = 0; i < a.Length; i++) + if (a[i] != b[i]) return false; + + return true; + } +} +*/ \ No newline at end of file diff --git a/src/server/Haru.Eft.Servers.ts b/src/server/Haru.Eft.Servers.ts index b311149..3f2aba6 100644 --- a/src/server/Haru.Eft.Servers.ts +++ b/src/server/Haru.Eft.Servers.ts @@ -83,6 +83,9 @@ export class NotificationServer implements IServer const message = Json.serialize(new PingNotificationModel()); this.httpServer = new HttpServer("localhost", 8001); this.wsServer = new WsServer(this.httpServer, message); + + // add cached responses + // this.server.addService(url, cachedResponseService); } public start(): void