This commit is contained in:
Merijn Hendriks 2021-12-28 18:22:44 +01:00
parent 6266ec0fed
commit fb17a80a37
2 changed files with 298 additions and 0 deletions

View File

@ -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<PatchItem> items = new List<PatchItem>();
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<PatchItem> items = new List<PatchItem>();
List<byte> 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<byte>();
}
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;
}
}
*/

View File

@ -83,6 +83,9 @@ export class NotificationServer implements IServer
const message = Json.serialize(new PingNotificationModel()); const message = Json.serialize(new PingNotificationModel());
this.httpServer = new HttpServer("localhost", 8001); this.httpServer = new HttpServer("localhost", 8001);
this.wsServer = new WsServer(this.httpServer, message); this.wsServer = new WsServer(this.httpServer, message);
// add cached responses
// this.server.addService(url, cachedResponseService);
} }
public start(): void public start(): void