Skip to content

Instantly share code, notes, and snippets.

@filipnavara
Last active August 27, 2021 13:04
Show Gist options
  • Save filipnavara/9209449de9ed1fd50fa0e83f0374dc11 to your computer and use it in GitHub Desktop.
Save filipnavara/9209449de9ed1fd50fa0e83f0374dc11 to your computer and use it in GitHub Desktop.
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