From 1cb3157110c897eddc4e43a06ae956e9466adb2e Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Wed, 14 Dec 2022 13:17:29 -0800 Subject: [PATCH] Add more notes, including Quantum --- BurnOutSharp/FileType/MicrosoftCAB.MSCAB.cs | 3 + BurnOutSharp/FileType/MicrosoftCAB.MSZIP.cs | 2 +- BurnOutSharp/FileType/MicrosoftCAB.Quantum.cs | 153 ++++++++++++++---- 3 files changed, 128 insertions(+), 30 deletions(-) diff --git a/BurnOutSharp/FileType/MicrosoftCAB.MSCAB.cs b/BurnOutSharp/FileType/MicrosoftCAB.MSCAB.cs index 394566ff..5e8dbddd 100644 --- a/BurnOutSharp/FileType/MicrosoftCAB.MSCAB.cs +++ b/BurnOutSharp/FileType/MicrosoftCAB.MSCAB.cs @@ -800,12 +800,15 @@ namespace BurnOutSharp.FileType break; case CompressionType.TYPE_MSZIP: // TODO: UNIMPLEMENTED + decompressed = dataBlock.CompressedData; break; case CompressionType.TYPE_QUANTUM: // TODO: UNIMPLEMENTED + decompressed = dataBlock.CompressedData; break; case CompressionType.TYPE_LZX: // TODO: UNIMPLEMENTED + decompressed = dataBlock.CompressedData; break; default: return null; diff --git a/BurnOutSharp/FileType/MicrosoftCAB.MSZIP.cs b/BurnOutSharp/FileType/MicrosoftCAB.MSZIP.cs index f244a916..55951105 100644 --- a/BurnOutSharp/FileType/MicrosoftCAB.MSZIP.cs +++ b/BurnOutSharp/FileType/MicrosoftCAB.MSZIP.cs @@ -723,6 +723,7 @@ namespace BurnOutSharp.FileType } else { + // Decode distance from input stream ulong length = data.ReadBitsLSB(LiteralExtraBits[symbol]); length += (ulong)LiteralLengths[symbol]; @@ -731,7 +732,6 @@ namespace BurnOutSharp.FileType ulong distance = data.ReadBitsLSB(DistanceExtraBits[code]); distance += (ulong)DistanceOffsets[code]; - // Decode distance from input stream // Move backwards distance bytes in the output // stream, and copy length bytes from this diff --git a/BurnOutSharp/FileType/MicrosoftCAB.Quantum.cs b/BurnOutSharp/FileType/MicrosoftCAB.Quantum.cs index 82f4368f..19db2e12 100644 --- a/BurnOutSharp/FileType/MicrosoftCAB.Quantum.cs +++ b/BurnOutSharp/FileType/MicrosoftCAB.Quantum.cs @@ -1,9 +1,13 @@ -namespace BurnOutSharp.FileType +using System.Runtime.InteropServices; + +/// +/// +/// +/// +/// +/// +namespace BurnOutSharp.FileType { - #region TEMPORARY AREA FOR QUANTUM COMPRESSION FORMAT - - // See http://www.russotto.net/quantumcomp.html for details about implementation - internal enum SelectorModel { /// @@ -42,36 +46,48 @@ SELECTOR_6_POSITION = 6, /// - /// LZ model, 5+ character matches, max 27 entries, start at symbol 0 + /// LZ model, 5+ character matches, 27 entries, start at symbol 0 /// SELECTOR_6_LENGTH = 7, } #region LZ Compression Tables - internal static class QuantumConstants + public static class QuantumConstants { - internal static readonly uint[] PositionBaseTable = new uint[] + /// + /// Base position for each position slot (0..41) + /// Used by selectors 4, 5, and 6 + /// + public static readonly uint[] PositionBaseTable = new uint[] { - 0x00000, 0x00001, 0x00002, 0x00003, 0x00004, 0x00006, 0x00008, 0x0000c, - 0x00010, 0x00018, 0x00020, 0x00030, 0x00040, 0x00060, 0x00080, 0x000c0, - 0x00100, 0x00180, 0x00200, 0x00300, 0x00400, 0x00600, 0x00800, 0x00c00, - 0x01000, 0x01800, 0x02000, 0x03000, 0x04000, 0x06000, 0x08000, 0x0c000, - 0x10000, 0x18000, 0x20000, 0x30000, 0x40000, 0x60000, 0x80000, 0xc0000, + 0x000000, 0x000001, 0x000002, 0x000003, 0x000004, 0x000006, 0x000008, 0x00000c, + 0x000010, 0x000018, 0x000020, 0x000030, 0x000040, 0x000060, 0x000080, 0x0000c0, + 0x000100, 0x000180, 0x000200, 0x000300, 0x000400, 0x000600, 0x000800, 0x000c00, + 0x001000, 0x001800, 0x002000, 0x003000, 0x004000, 0x006000, 0x008000, 0x00c000, + 0x010000, 0x018000, 0x020000, 0x030000, 0x040000, 0x060000, 0x080000, 0x0c0000, 0x100000, 0x180000, }; - internal static readonly int[] PositionExtraBitsTable = new int[] + /// + /// Extra bits for each position slot (0..41) + /// Used by selectors 4, 5, and 6 + /// + public static readonly int[] PositionExtraBitsTable = new int[] { - 0, 0, 0, 0, 1, 1, 2, 2, - 3, 3, 4, 4, 5, 5, 6, 6, - 7, 7, 8, 8, 9, 9, 10, 10, + 0, 0, 0, 0, 1, 1, 2, 2, + 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, }; - internal static readonly byte[] LengthBaseTable = new byte[] + /// + /// Base length for each length slot (0..26) + /// Used by selector 6 + /// + public static readonly byte[] LengthBaseTable = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x08, 0x0a, 0x0c, 0x0e, 0x12, 0x16, 0x1a, 0x1e, 0x26, @@ -79,7 +95,11 @@ 0xbe, 0xde, 0xfe }; - internal static readonly int[] LengthExtraBitsTable = new int[] + /// + /// Extra bits for each length slot (0..26) + /// Used by selector 6 + /// + public static readonly int[] LengthExtraBitsTable = new int[] { 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, @@ -90,7 +110,7 @@ /// /// Number of position slots for (tsize - 10) /// - internal static readonly int[] NumberOfPositionSlots = new int[] + public static readonly int[] NumberOfPositionSlots = new int[] { 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, }; @@ -98,7 +118,85 @@ #endregion - internal static class QuantumCompressor + /// + /// Quantum archive file structure + /// + [StructLayout(LayoutKind.Sequential)] + public class QuantumArchive + { + /// + /// Quantum signature: 0x44 0x53 + /// + public ushort Signature; + + /// + /// Quantum major version number + /// + public byte MajorVersion; + + /// + /// Quantum minor version number + /// + public byte MinorVersion; + + /// + /// Number of files within this archive + /// + public ushort FileCount; + + /// + /// Table size required for decompression + /// + public byte TableSize; + + /// + /// Compression flags + /// + public byte CompressionFlags; + + /// + /// This is immediately followed by the list of files + /// + public QuantumFileDescriptor[] FileList; + + // Immediately following the list of files is the compressed data. + } + + /// + /// Strings are prefixed with their length. If the length is less than + /// 128 then it is stored directly in one byte. If it is greater than 127 + /// then the high bit of the first byte is set to 1 and the remaining + /// fifteen bits contain the actual length in big-endian format. + /// + public class QuantumFileDescriptor + { + /// + /// File name, variable length string, not zero-terminated + /// + public string FileName; + + /// + /// Comment field, variable length string, not zero-terminated + /// + public string CommentField; + + /// + /// Fully expanded file size in bytes + /// + public uint ExpandedFileSize; + + /// + /// File time (DOS format) + /// + public ushort FileTime; + + /// + /// File date (DOS format) + /// + public ushort FileDate; + } + + public static class QuantumCompressor { // TODO: Determine how these values are set private static uint CS_C = 0; @@ -150,7 +248,7 @@ return 0; } - public static int GetSymbol(Model model) + public static int GetSymbol(QuantumModel model) { int freq = GetFrequency(model.Symbols[0].CumulativeFrequency); @@ -172,20 +270,17 @@ } } - internal class ModelSymbol + public class QuantumModelSymbol { public ushort Symbol { get; private set; } public ushort CumulativeFrequency { get; private set; } } - internal class Model + public class QuantumModel { - public int Entries { get; private set; } + public int Entries { get; set; } - public ModelSymbol[] Symbols { get; private set; } + public QuantumModelSymbol[] Symbols { get; set; } } - - #endregion - }