-
-
Save filipnavara/9209449de9ed1fd50fa0e83f0374dc11 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Buffers.Binary; | |
using System.Diagnostics; | |
using System.Linq; | |
using System.Text; | |
namespace Wales | |
{ | |
class Cardiff | |
{ | |
private static List<Range> ReadBomHeaderAndIndex(BinaryReader reader) | |
{ | |
var bomStoreMarker = reader.ReadBytes(8); | |
if (!bomStoreMarker.SequenceEqual(new byte[] { (byte)'B', (byte)'O', (byte)'M', (byte)'S', (byte)'t', (byte)'o', (byte)'r', (byte)'e'})) | |
throw new FormatException("Not a BOM file"); | |
var bomStoreVersion = BinaryPrimitives.ReverseEndianness(reader.ReadUInt32()); | |
if (bomStoreVersion != 1) | |
throw new FormatException("Unknown BOM file version"); | |
var bomStoreNumberOfBlocks = BinaryPrimitives.ReverseEndianness(reader.ReadUInt32()); | |
var bomStoreIndexOffset = BinaryPrimitives.ReverseEndianness(reader.ReadUInt32()); | |
// Read index table | |
reader.BaseStream.Seek(bomStoreIndexOffset, SeekOrigin.Begin); | |
var bomIndexCount = BinaryPrimitives.ReverseEndianness(reader.ReadUInt32()); | |
var bomIndex = new List<Range>((int)bomIndexCount); | |
for (int i = 0; i < bomIndexCount && i <= bomStoreNumberOfBlocks; i++) | |
{ | |
var offset = BinaryPrimitives.ReverseEndianness(reader.ReadUInt32()); | |
var length = BinaryPrimitives.ReverseEndianness(reader.ReadUInt32()); | |
bomIndex.Add(new Range((int)offset, (int)offset + (int)length)); | |
} | |
return bomIndex; | |
} | |
private static bool CompareByteRange(BinaryReader reader1, BinaryReader reader2, Range range) | |
{ | |
if (range.End.Value != range.Start.Value) | |
{ | |
reader1.BaseStream.Seek(range.Start.Value, SeekOrigin.Begin); | |
reader2.BaseStream.Seek(range.Start.Value, SeekOrigin.Begin); | |
var contents1 = reader1.ReadBytes(range.End.Value - range.Start.Value); | |
var contents2 = reader2.ReadBytes(range.End.Value - range.Start.Value); | |
// For CTSI blocks use semantic comparison | |
if (contents1.Length >= 0xa8 && contents1.Take(4).SequenceEqual(new byte[] { (byte)'I', (byte)'S', (byte)'T', (byte)'C' })) | |
{ | |
// Compare first 0x28 bytes of header | |
if (!contents1.AsSpan(0, 0x28).SequenceEqual(contents2.AsSpan(0, 0x28))) | |
return false; | |
// Compare 0-terminated file name | |
for (int i = 0; i < 0x80; i++) | |
{ | |
if (contents1[0x28 + i] != contents2[0x28 + i]) | |
return false; | |
if (contents1[0x28 + i] == 0) | |
break; | |
} | |
// Compare reminder of the data | |
return contents1.AsSpan(0xa8).SequenceEqual(contents2.AsSpan(0xa8)); | |
} | |
else | |
{ | |
return contents1.AsSpan().SequenceEqual(contents2.AsSpan()); | |
} | |
} | |
return true; | |
} | |
static int Main(string[] args) | |
{ | |
try | |
{ | |
using var reader1 = new BinaryReader(File.OpenRead(args[0])); | |
var bomIndex1 = ReadBomHeaderAndIndex(reader1); | |
using var reader2 = new BinaryReader(File.OpenRead(args[1])); | |
var bomIndex2 = ReadBomHeaderAndIndex(reader2); | |
if (!bomIndex1.SequenceEqual(bomIndex2)) | |
{ | |
Console.WriteLine("BOM index differs"); | |
return 1; | |
} | |
int i = 0; | |
foreach (var range in bomIndex1) | |
{ | |
if (!CompareByteRange(reader1, reader2, range)) | |
{ | |
Console.WriteLine($"Content different in block {i} {range.Start:x}-{range.End:x}"); | |
return 1; | |
} | |
i++; | |
} | |
Console.WriteLine($"Files are semantically identical"); | |
return 0; | |
} | |
catch (Exception e) | |
{ | |
Console.WriteLine(e.ToString()); | |
return 1; | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment