diff --git a/MixBrowser/MixBrowser.csproj b/MixBrowser/MixBrowser.csproj index 8ce997f6b4..4bad552aca 100644 --- a/MixBrowser/MixBrowser.csproj +++ b/MixBrowser/MixBrowser.csproj @@ -18,6 +18,7 @@ DEBUG;TRACE prompt 4 + false pdbonly @@ -36,6 +37,7 @@ + diff --git a/MixBrowser/MixEntry.cs b/MixBrowser/MixEntry.cs index 2b950c069b..34830cef95 100644 --- a/MixBrowser/MixEntry.cs +++ b/MixBrowser/MixEntry.cs @@ -57,5 +57,7 @@ namespace MixBrowser uint hash = HashFilename(s); Names.Add(hash, s); } + + public const int Size = 12; } } diff --git a/MixBrowser/MixFile.cs b/MixBrowser/MixFile.cs new file mode 100644 index 0000000000..375826a8e0 --- /dev/null +++ b/MixBrowser/MixFile.cs @@ -0,0 +1,146 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.IO; + +namespace MixBrowser +{ + class MixFile + { + readonly string filename; + readonly List index; + readonly bool isRmix, isEncrypted; + + public ICollection Content + { + get { return index.AsReadOnly(); } + } + + public MixFile(string filename) + { + this.filename = filename; + using (Stream s = File.OpenRead(filename)) + { + BinaryReader reader = new BinaryReader(s); + uint signature = reader.ReadUInt32(); + + isRmix = 0 == (signature & ~(uint)(MixFileFlags.Checksum | MixFileFlags.Encrypted)); + + if (isRmix) + { + isEncrypted = 0 != (signature & (uint)MixFileFlags.Encrypted); + index = ParseRaHeader(s); + } + else + { + isEncrypted = false; + s.Seek(0, SeekOrigin.Begin); + index = ParseTdHeader(s); + } + } + } + + List ParseRaHeader(Stream s) + { + if (!isEncrypted) + return ParseTdHeader(s); + + long headerStart = 84; + BinaryReader reader = new BinaryReader(s); + byte[] keyblock = reader.ReadBytes(80); + byte[] blowfishKey = MixDecrypt.MixDecrypt.BlowfishKey(keyblock); + + uint[] h = ReadUints(reader, 2); + + Blowfish fish = new Blowfish(blowfishKey); + uint[] decrypted = fish.Decrypt(h); + + MemoryStream ms = new MemoryStream(); + BinaryWriter writer = new BinaryWriter(ms); + foreach (uint t in decrypted) + writer.Write(t); + writer.Flush(); + + ms.Position = 0; + BinaryReader reader2 = new BinaryReader(ms); + + ushort numFiles = reader.ReadUInt16(); + uint datasize = reader.ReadUInt32(); + + Console.WriteLine("{0} files, {1} kb", numFiles, datasize >> 10); + + s.Position = headerStart; + reader = new BinaryReader(s); + + h = ReadUints(reader, 2 + numFiles * MixEntry.Size / 4); + decrypted = fish.Decrypt(h); + + ms = new MemoryStream(); + writer = new BinaryWriter(ms); + foreach (uint t in decrypted) + writer.Write(t); + writer.Flush(); + + ms.Position = 0; + + return ParseTdHeader(ms); + } + + uint[] ReadUints(BinaryReader r, int count) + { + uint[] ret = new uint[count]; + for (int i = 0; i < ret.Length; i++) + ret[i] = r.ReadUInt32(); + + return ret; + } + + List ParseTdHeader(Stream s) + { + List items = new List(); + + BinaryReader reader = new BinaryReader(s); + ushort numFiles = reader.ReadUInt16(); + uint dataSize = reader.ReadUInt32(); + + for (int i = 0; i < numFiles; i++) + items.Add(new MixEntry(reader)); + + return items; + } + + public Stream GetContent(uint hash) + { + foreach( MixEntry e in index ) + if (e.Hash == hash) + { + using (Stream s = File.OpenRead(filename)) + { + s.Seek(2 + 4 + (isRmix ? 4 : 0), SeekOrigin.Begin); + if (isEncrypted) + s.Seek(80, SeekOrigin.Current); + + s.Seek(index.Count * MixEntry.Size + e.Offset, SeekOrigin.Current); + byte[] data = new byte[ e.Length ]; + s.Read( data, 0, (int)e.Length ); + return new MemoryStream(data); + } + } + + throw new FileNotFoundException(); + } + + public Stream GetContent(string filename) + { + return GetContent(MixEntry.HashFilename(filename)); + } + + } + + [Flags] + enum MixFileFlags : uint + { + Checksum = 0x10000, + Encrypted = 0x20000, + } +} diff --git a/MixBrowser/Program.cs b/MixBrowser/Program.cs index 8194d2d8a8..af854a2ffd 100644 --- a/MixBrowser/Program.cs +++ b/MixBrowser/Program.cs @@ -31,58 +31,7 @@ namespace MixBrowser return; } - Stream s = File.Open(fn, FileMode.Open, FileAccess.Read); - BinaryReader reader = new BinaryReader(s); - - uint signature = reader.ReadUInt32(); - - if (0 == (signature & ~(uint)(MixFileFlags.Checksum | MixFileFlags.Encrypted))) - { - Console.WriteLine("{0} - Red Alert MIX", Path.GetFileName(fn)); - if (0 != (signature & (uint)MixFileFlags.Checksum)) - Console.WriteLine("Checksum: YES"); - if (0 != (signature & (uint)MixFileFlags.Encrypted)) - { - Console.WriteLine("Encrypted: YES"); - - //get the blowfish key - byte[] bfkey = MixDecrypt.MixDecrypt.BlowfishKey(reader.ReadBytes(80)); - - Blowfish fish = new Blowfish(bfkey); - - uint[] data = { reader.ReadUInt32(), reader.ReadUInt32() }; - uint[] decrypted = fish.Decrypt(data); - - MemoryStream ms = new MemoryStream(); - BinaryWriter w = new BinaryWriter(ms); - foreach (uint u in decrypted) - w.Write(u); - w.Flush(); - ms.Seek(0, SeekOrigin.Begin); - - BinaryReader reader2 = new BinaryReader(ms); - - ushort numfiles2 = reader2.ReadUInt16(); - uint datasize2 = reader2.ReadUInt32(); - - Console.WriteLine("{0} files - {1} KB", numfiles2, datasize2 >> 10); - - return; - } - } - else - Console.WriteLine("{0} - Tiberian Dawn MIX", Path.GetFileName(fn) ); - - s.Seek(0, SeekOrigin.Begin); - reader = new BinaryReader(s); - - ushort numfiles = reader.ReadUInt16(); - uint datasize = reader.ReadUInt32(); - Console.WriteLine("{0} files - {1} KB", numfiles, datasize >> 10); - - List index = new List(); - for (ushort i = 0; i < numfiles; i++) - index.Add(new MixEntry(reader)); + MixFile file = new MixFile(fn); if (File.Exists("files.txt")) foreach (string filename in File.ReadAllLines("files.txt")) @@ -90,15 +39,8 @@ namespace MixBrowser else Console.WriteLine("-- files.txt doesnt exist --"); - foreach (MixEntry e in index) + foreach (MixEntry e in file.Content) Console.WriteLine(e); } } - - [Flags] - enum MixFileFlags : uint - { - Checksum = 0x10000, - Encrypted = 0x20000, - } }