diff --git a/BurnOutSharp.Compression/Quantum.cs b/BurnOutSharp.Compression/Quantum.cs deleted file mode 100644 index 369b5b78..00000000 --- a/BurnOutSharp.Compression/Quantum.cs +++ /dev/null @@ -1,203 +0,0 @@ -using System.Linq; -using BurnOutSharp.Models.Compression.Quantum; -using static BurnOutSharp.Models.Compression.Quantum.Constants; - -namespace BurnOutSharp.Compression -{ - public class Quantum - { - // TODO: Implement Quantum decompression - - /* Bitstream reading macros (Quantum / normal byte order) - * - * Q_INIT_BITSTREAM should be used first to set up the system - * Q_READ_BITS(var,n) takes N bits from the buffer and puts them in var. - * unlike LZX, this can loop several times to get the - * requisite number of bits. - * Q_FILL_BUFFER adds more data to the bit buffer, if there is room - * for another 16 bits. - * Q_PEEK_BITS(n) extracts (without removing) N bits from the bit - * buffer - * Q_REMOVE_BITS(n) removes N bits from the bit buffer - * - * These bit access routines work by using the area beyond the MSB and the - * LSB as a free source of zeroes. This avoids having to mask any bits. - * So we have to know the bit width of the bitbuffer variable. This is - * defined as Uint_BITS. - * - * Uint_BITS should be at least 16 bits. Unlike LZX's Huffman decoding, - * Quantum's arithmetic decoding only needs 1 bit at a time, it doesn't - * need an assured number. Retrieving larger bitstrings can be done with - * multiple reads and fills of the bitbuffer. The code should work fine - * for machines where Uint >= 32 bits. - * - * Also note that Quantum reads bytes in normal order; LZX is in - * little-endian order. - */ - - // #define Q_INIT_BITSTREAM do { bitsleft = 0; bitbuf = 0; } while (0) - - // #define Q_FILL_BUFFER do { \ - // if (bitsleft <= (CAB_Uint_BITS - 16)) { \ - // bitbuf |= ((inpos[0]<<8)|inpos[1]) << (CAB_Uint_BITS-16 - bitsleft); \ - // bitsleft += 16; inpos += 2; \ - // } \ - // } while (0) - - // #define Q_PEEK_BITS(n) (bitbuf >> (CAB_Uint_BITS - (n))) - // #define Q_REMOVE_BITS(n) ((bitbuf <<= (n)), (bitsleft -= (n))) - - // #define Q_READ_BITS(v,n) do { \ - // (v) = 0; \ - // for (bitsneed = (n); bitsneed; bitsneed -= bitrun) { \ - // Q_FILL_BUFFER; \ - // bitrun = (bitsneed > bitsleft) ? bitsleft : bitsneed; \ - // (v) = ((v) << bitrun) | Q_PEEK_BITS(bitrun); \ - // Q_REMOVE_BITS(bitrun); \ - // } \ - // } while (0) - - // #define Q_MENTRIES(model) (decomp_state.qtm.model).entries) - // #define Q_MSYM(model,symidx) (decomp_state.qtm.model).syms[(symidx)].sym) - // #define Q_MSYMFREQ(model,symidx) (decomp_state.qtm.model).syms[(symidx)].cumfreq) - - /* GET_SYMBOL(model, var) fetches the next symbol from the stated model - * and puts it in var. it may need to read the bitstream to do this. - */ - // #define GET_SYMBOL(m, var) do { \ - // range = ((H - L) & 0xFFFF) + 1; \ - // symf = ((((C - L + 1) * Q_MSYMFREQ(m,0)) - 1) / range) & 0xFFFF; \ - // \ - // for (i=1; i < Q_MENTRIES(m); i++) { \ - // if (Q_MSYMFREQ(m,i) <= symf) break; \ - // } \ - // (var) = Q_MSYM(m,i-1); \ - // \ - // range = (H - L) + 1; \ - // H = L + ((Q_MSYMFREQ(m,i-1) * range) / Q_MSYMFREQ(m,0)) - 1; \ - // L = L + ((Q_MSYMFREQ(m,i) * range) / Q_MSYMFREQ(m,0)); \ - // while (1) { \ - // if ((L & 0x8000) != (H & 0x8000)) { \ - // if ((L & 0x4000) && !(H & 0x4000)) { \ - // /* underflow case */ \ - // C ^= 0x4000; L &= 0x3FFF; H |= 0x4000; \ - // } \ - // else break; \ - // } \ - // L <<= 1; H = (H << 1) | 1; \ - // Q_FILL_BUFFER; \ - // C = (C << 1) | Q_PEEK_BITS(1); \ - // Q_REMOVE_BITS(1); \ - // } \ - // \ - // Quantum.UpdateModel(&(decomp_state.qtm.m)), i); \ - // } while (0) - - /// - /// Initialize a Quantum model that decodes symbols from s to (s + n - 1) - /// - /// - public static void InitModel(Model model, ModelSymbol[] symbols, int entryCount, int initialSymbol) - { - // Set the basic values - model.ShiftsLeft = 4; - model.Entries = entryCount; - model.Symbols = symbols; - - // Clear out the look-up table - model.LookupTable = Enumerable.Repeat(0xFF, model.LookupTable.Length).ToArray(); - - // Loop through and build the look-up table - for (ushort i = 0; i < entryCount; i++) - { - // Set up a look-up entry for symbol - model.LookupTable[i + initialSymbol] = i; - - // Actual symbol - model.Symbols[i].Symbol = (ushort)(i + initialSymbol); - - // Current frequency of that symbol - model.Symbols[i].CumulativeFrequency = (ushort)(entryCount - i); - } - - // Set the last symbol frequency to 0 - model.Symbols[entryCount].CumulativeFrequency = 0; - } - - /// - /// Update the quantum model for a particular symbol - /// - /// - public static void UpdateModel(Model model, int symbol) - { - // Update the cumulative frequency for all symbols less than the provided - for (int i = 0; i < symbol; i++) - { - model.Symbols[i].CumulativeFrequency += 8; - } - - // If the first symbol still has a cumulative frequency under 3800 - if (model.Symbols[0].CumulativeFrequency <= 3800) - return; - - // If we have more than 1 shift left in the model - if (--model.ShiftsLeft != 0) - { - // Loop through the entries from highest to lowest, - // performing the shift on the cumulative frequencies - for (int i = model.Entries - 1; i >= 0; i--) - { - // -1, not -2; the 0 entry saves this - model.Symbols[i].CumulativeFrequency >>= 1; - if (model.Symbols[i].CumulativeFrequency <= model.Symbols[i + 1].CumulativeFrequency) - model.Symbols[i].CumulativeFrequency = (ushort)(model.Symbols[i + 1].CumulativeFrequency + 1); - } - } - - // If we have no shifts left in the model - else - { - // Reset the shifts left value to 50 - model.ShiftsLeft = 50; - - // Loop through the entries setting the cumulative frequencies - for (int i = 0; i < model.Entries; i++) - { - // No -1, want to include the 0 entry - // This converts cumfreqs into frequencies, then shifts right - model.Symbols[i].CumulativeFrequency -= model.Symbols[i + 1].CumulativeFrequency; - model.Symbols[i].CumulativeFrequency++; // Avoid losing things entirely - model.Symbols[i].CumulativeFrequency >>= 1; - } - - // Now sort by frequencies, decreasing order -- this must be an - // inplace selection sort, or a sort with the same (in)stability - // characteristics - for (int i = 0; i < model.Entries - 1; i++) - { - for (int j = i + 1; j < model.Entries; j++) - { - if (model.Symbols[i].CumulativeFrequency < model.Symbols[j].CumulativeFrequency) - { - var temp = model.Symbols[i]; - model.Symbols[i] = model.Symbols[j]; - model.Symbols[j] = temp; - } - } - } - - // Then convert frequencies back to cumfreq - for (int i = model.Entries - 1; i >= 0; i--) - { - model.Symbols[i].CumulativeFrequency += model.Symbols[i + 1].CumulativeFrequency; - } - - // Then update the other part of the table - for (int i = 0; i < model.Entries; i++) - { - model.LookupTable[model.Symbols[i].Symbol] = (ushort)i; - } - } - } - } -} \ No newline at end of file diff --git a/BurnOutSharp.Compression/Quantum/Decompressor.cs b/BurnOutSharp.Compression/Quantum/Decompressor.cs new file mode 100644 index 00000000..9fb363e0 --- /dev/null +++ b/BurnOutSharp.Compression/Quantum/Decompressor.cs @@ -0,0 +1,402 @@ +using System.Linq; +using BurnOutSharp.Models.Compression.Quantum; +using static BurnOutSharp.Models.Compression.Quantum.Constants; + +namespace BurnOutSharp.Compression.Quantum +{ + public class Decompressor + { + // TODO: Implement Quantum decompression + + /// + /// Decompress a byte array using a given State + /// + public static bool Decompress(State state, int inlen, byte[] inbuf, int outlen, byte[] outbuf) + { + // Port from MicrosoftCabinet.fdi.Quantum.cs + return false; + } + + /// + /// Initialize a Quantum decompressor state + /// + public static bool InitState(int window, int level, State state) + { + uint windowSize = (uint)(1 << window); + int msz = window * 2, i; + uint j; + + // QTM supports window sizes of 2^10 (1Kb) through 2^21 (2Mb) + // If a previously allocated window is big enough, keep it + if (window < 10 || window > 21) + return false; + + // If we don't have the proper window size + if (state.ActualSize < windowSize) + state.Window = null; + + // If we have no window + if (state.Window == null) + { + state.Window = new byte[windowSize]; + state.ActualSize = windowSize; + } + + // Set the window size and position + state.WindowSize = windowSize; + state.WindowPosition = 0; + + // Initialize static slot/extrabits tables + for (i = 0, j = 0; i < 27; i++) + { + state.q_length_extra[i] = (byte)((i == 26) ? 0 : (i < 2 ? 0 : i - 2) >> 2); + state.q_length_base[i] = (byte)j; j += (uint)(1 << ((i == 26) ? 5 : state.q_length_extra[i])); + } + + for (i = 0, j = 0; i < 42; i++) + { + state.q_extra_bits[i] = (byte)((i < 2 ? 0 : i - 2) >> 1); + state.q_position_base[i] = j; j += (uint)(1 << state.q_extra_bits[i]); + } + + // Initialize arithmetic coding models + InitModel(state.Model7, state.Model7Symbols, 7, 0); + + InitModel(state.Model7Submodel00, state.Model7Submodel00Symbols, 0x40, 0x00); + InitModel(state.Model7Submodel40, state.Model7Submodel40Symbols, 0x40, 0x40); + InitModel(state.Model7Submodel80, state.Model7Submodel80Symbols, 0x40, 0x80); + InitModel(state.Model7SubmodelC0, state.Model7SubmodelC0Symbols, 0x40, 0xC0); + + // Model 4 depends on table size, ranges from 20 to 24 + InitModel(state.Model4, state.Model4Symbols, (msz < 24) ? msz : 24, 0); + + // Model 5 depends on table size, ranges from 20 to 36 + InitModel(state.Model5, symbols: state.Model5Symbols, (msz < 36) ? msz : 36, 0); + + // Model 6 Position depends on table size, ranges from 20 to 42 + InitModel(state.Model6Position, state.Model6PositionSymbols, msz, 0); + InitModel(state.Model6Length, state.Model6LengthSymbols, 27, 0); + + return true; + } + + /// + /// Initialize a Quantum model that decodes symbols from s to (s + n - 1) + /// + /// + public static void InitModel(Model model, ModelSymbol[] symbols, int entryCount, int initialSymbol) + { + // Set the basic values + model.ShiftsLeft = 4; + model.Entries = entryCount; + model.Symbols = symbols; + + // Clear out the look-up table + model.LookupTable = Enumerable.Repeat(0xFF, model.LookupTable.Length).ToArray(); + + // Loop through and build the look-up table + for (ushort i = 0; i < entryCount; i++) + { + // Set up a look-up entry for symbol + model.LookupTable[i + initialSymbol] = i; + + // Actual symbol + model.Symbols[i].Symbol = (ushort)(i + initialSymbol); + + // Current frequency of that symbol + model.Symbols[i].CumulativeFrequency = (ushort)(entryCount - i); + } + + // Set the last symbol frequency to 0 + model.Symbols[entryCount].CumulativeFrequency = 0; + } + + /// + /// Update the quantum model for a particular symbol + /// + /// + public static void UpdateModel(Model model, int symbol) + { + // Update the cumulative frequency for all symbols less than the provided + for (int i = 0; i < symbol; i++) + { + model.Symbols[i].CumulativeFrequency += 8; + } + + // If the first symbol still has a cumulative frequency under 3800 + if (model.Symbols[0].CumulativeFrequency <= 3800) + return; + + // If we have more than 1 shift left in the model + if (--model.ShiftsLeft != 0) + { + // Loop through the entries from highest to lowest, + // performing the shift on the cumulative frequencies + for (int i = model.Entries - 1; i >= 0; i--) + { + // -1, not -2; the 0 entry saves this + model.Symbols[i].CumulativeFrequency >>= 1; + if (model.Symbols[i].CumulativeFrequency <= model.Symbols[i + 1].CumulativeFrequency) + model.Symbols[i].CumulativeFrequency = (ushort)(model.Symbols[i + 1].CumulativeFrequency + 1); + } + } + + // If we have no shifts left in the model + else + { + // Reset the shifts left value to 50 + model.ShiftsLeft = 50; + + // Loop through the entries setting the cumulative frequencies + for (int i = 0; i < model.Entries; i++) + { + // No -1, want to include the 0 entry + // This converts cumfreqs into frequencies, then shifts right + model.Symbols[i].CumulativeFrequency -= model.Symbols[i + 1].CumulativeFrequency; + model.Symbols[i].CumulativeFrequency++; // Avoid losing things entirely + model.Symbols[i].CumulativeFrequency >>= 1; + } + + // Now sort by frequencies, decreasing order -- this must be an + // inplace selection sort, or a sort with the same (in)stability + // characteristics + for (int i = 0; i < model.Entries - 1; i++) + { + for (int j = i + 1; j < model.Entries; j++) + { + if (model.Symbols[i].CumulativeFrequency < model.Symbols[j].CumulativeFrequency) + { + var temp = model.Symbols[i]; + model.Symbols[i] = model.Symbols[j]; + model.Symbols[j] = temp; + } + } + } + + // Then convert frequencies back to cumfreq + for (int i = model.Entries - 1; i >= 0; i--) + { + model.Symbols[i].CumulativeFrequency += model.Symbols[i + 1].CumulativeFrequency; + } + + // Then update the other part of the table + for (int i = 0; i < model.Entries; i++) + { + model.LookupTable[model.Symbols[i].Symbol] = (ushort)i; + } + } + } + + #region Macros + + /* Bitstream reading macros (Quantum / normal byte order) + * + * Q_INIT_BITSTREAM should be used first to set up the system + * Q_READ_BITS(var,n) takes N bits from the buffer and puts them in var. + * unlike LZX, this can loop several times to get the + * requisite number of bits. + * Q_FILL_BUFFER adds more data to the bit buffer, if there is room + * for another 16 bits. + * Q_PEEK_BITS(n) extracts (without removing) N bits from the bit + * buffer + * Q_REMOVE_BITS(n) removes N bits from the bit buffer + * + * These bit access routines work by using the area beyond the MSB and the + * LSB as a free source of zeroes. This avoids having to mask any bits. + * So we have to know the bit width of the bitbuffer variable. This is + * defined as Uint_BITS. + * + * Uint_BITS should be at least 16 bits. Unlike LZX's Huffman decoding, + * Quantum's arithmetic decoding only needs 1 bit at a time, it doesn't + * need an assured number. Retrieving larger bitstrings can be done with + * multiple reads and fills of the bitbuffer. The code should work fine + * for machines where Uint >= 32 bits. + * + * Also note that Quantum reads bytes in normal order; LZX is in + * little-endian order. + */ + + // #define Q_INIT_BITSTREAM do { bitsleft = 0; bitbuf = 0; } while (0) + + // #define Q_FILL_BUFFER do { \ + // if (bitsleft <= (16)) { \ + // bitbuf |= ((inpos[0]<<8)|inpos[1]) << (32-16 - bitsleft); \ + // bitsleft += 16; inpos += 2; \ + // } \ + // } while (0) + + // #define Q_PEEK_BITS(n) (bitbuf >> (32 - (n))) + // #define Q_REMOVE_BITS(n) ((bitbuf <<= (n)), (bitsleft -= (n))) + + // #define Q_READ_BITS(v,n) do { \ + // (v) = 0; \ + // for (bitsneed = (n); bitsneed; bitsneed -= bitrun) { \ + // Q_FILL_BUFFER; \ + // bitrun = (bitsneed > bitsleft) ? bitsleft : bitsneed; \ + // (v) = ((v) << bitrun) | Q_PEEK_BITS(bitrun); \ + // Q_REMOVE_BITS(bitrun); \ + // } \ + // } while (0) + + // #define Q_MENTRIES(model) (state.qtm.model).Entries) + // #define Q_MSYM(model,symidx) (state.qtm.model).syms[(symidx)].sym) + // #define Q_MSYMFREQ(model,symidx) (state.qtm.model).syms[(symidx)].cumfreq) + + /* GET_SYMBOL(model, var) fetches the next symbol from the stated model + * and puts it in var. it may need to read the bitstream to do this. + */ + // #define GET_SYMBOL(m, var) do { \ + // range = ((H - L) & 0xFFFF) + 1; \ + // symf = ((((C - L + 1) * (state.qtm.m).syms[(0)].cumfreq) - 1) / range) & 0xFFFF; \ + // \ + // for (i=1; i < (state.qtm.m).Entries); i++) { \ + // if ((state.qtm.m).syms[(i)].cumfreq) <= symf) break; \ + // } \ + // (var) = (state.qtm.m).syms[(i-1)].sym) \ + // \ + // range = (H - L) + 1; \ + // H = L + (((state.qtm.m).syms[(i-1)].cumfreq) * range) / (state.qtm.m).syms[(0)].cumfreq) - 1; \ + // L = L + (((state.qtm.m).syms[(i)].cumfreq) * range) / (state.qtm.m).syms[(0)].cumfreq); \ + // while (1) { \ + // if ((L & 0x8000) != (H & 0x8000)) { \ + // if ((L & 0x4000) && !(H & 0x4000)) { \ + // /* underflow case */ \ + // C ^= 0x4000; L &= 0x3FFF; H |= 0x4000; \ + // } \ + // else break; \ + // } \ + // L <<= 1; H = (H << 1) | 1; \ + // Q_FILL_BUFFER; \ + // C = (C << 1) | Q_PEEK_BITS(1); \ + // Q_REMOVE_BITS(1); \ + // } \ + // \ + // Quantum.UpdateModel(&(state.qtm.m)), i); \ + // } while (0) + + /// + /// Should be used first to set up the system + /// + private static void Q_INIT_BITSTREAM(out int bitsleft, out uint bitbuf) + { + bitsleft = 0; bitbuf = 0; + } + + /// + /// Adds more data to the bit buffer, if there is room for another 16 bits. + /// + private static void Q_FILL_BUFFER(byte[] inbuf, ref int inpos, ref int bitsleft, ref uint bitbuf) + { + if (bitsleft <= 16) + { + bitbuf |= (uint)((inbuf[inpos + 0] << 8) | inbuf[inpos + 1]) << (16 - bitsleft); + bitsleft += 16; inpos += 2; + } + } + + /// + /// Extracts (without removing) N bits from the bit buffer + /// + private static uint Q_PEEK_BITS(int n, uint bitbuf) + { + return bitbuf >> (32 - n); + } + + /// + /// Removes N bits from the bit buffer + /// + private static void Q_REMOVE_BITS(int n, ref int bitsleft, ref uint bitbuf) + { + bitbuf <<= n; + bitsleft -= n; + } + + /// + /// Takes N bits from the buffer and puts them in v. Unlike LZX, this can loop + /// several times to get the requisite number of bits. + /// + private static ushort Q_READ_BITS_UINT16(int n, byte[] inbuf, ref int inpos, ref int bitsleft, ref uint bitbuf) + { + ushort v = 0; int bitrun; + for (int bitsneed = n; bitsneed != 0; bitsneed -= bitrun) + { + Q_FILL_BUFFER(inbuf, ref inpos, ref bitsleft, ref bitbuf); + + bitrun = (bitsneed > bitsleft) ? bitsleft : bitsneed; + v = (ushort)((v << bitrun) | Q_PEEK_BITS(bitrun, bitbuf)); + + Q_REMOVE_BITS(bitrun, ref bitsleft, ref bitbuf); + } + + return v; + } + + /// + /// Takes N bits from the buffer and puts them in v. Unlike LZX, this can loop + /// several times to get the requisite number of bits. + /// + private static int Q_READ_BITS_INT32(int n, byte[] inbuf, ref int inpos, ref int bitsleft, ref uint bitbuf) + { + int v = 0; int bitrun; + for (int bitsneed = n; bitsneed != 0; bitsneed -= bitrun) + { + Q_FILL_BUFFER(inbuf, ref inpos, ref bitsleft, ref bitbuf); + + bitrun = (bitsneed > bitsleft) ? bitsleft : bitsneed; + v = (int)((v << bitrun) | Q_PEEK_BITS(bitrun, bitbuf)); + + Q_REMOVE_BITS(bitrun, ref bitsleft, ref bitbuf); + } + + return v; + } + + /// + /// Fetches the next symbol from the stated model and puts it in v. + /// It may need to read the bitstream to do this. + /// + private static int GET_SYMBOL(Model model, ref ushort H, ref ushort L, ref ushort C, byte[] inbuf, ref int inpos, ref int bitsleft, ref uint bitbuf) + { + uint range = (uint)(((H - L) & 0xFFFF) + 1); + ushort symf = (ushort)(((((C - L + 1) * model.Symbols[0].CumulativeFrequency) - 1) / range) & 0xFFFF); + + int i; + for (i = 1; i < model.Entries; i++) + { + if (model.Symbols[i].CumulativeFrequency <= symf) + break; + } + + int v = model.Symbols[i - 1].Symbol; + range = (uint)(H - L + 1); + H = (ushort)(L + ((model.Symbols[i - 1].CumulativeFrequency * range) / model.Symbols[0].CumulativeFrequency) - 1); + L = (ushort)(L + ((model.Symbols[i].CumulativeFrequency * range) / model.Symbols[0].CumulativeFrequency)); + + while (true) + { + if ((L & 0x8000) != (H & 0x8000)) + { + if ((L & 0x4000) != 0 && (H & 0x4000) == 0) + { + // Underflow case + C ^= 0x4000; L &= 0x3FFF; H |= 0x4000; + } + else + { + break; + } + } + + L <<= 1; H = (ushort)((H << 1) | 1); + Q_FILL_BUFFER(inbuf, ref inpos, ref bitsleft, ref bitbuf); + C = (ushort)((C << 1) | Q_PEEK_BITS(1, bitbuf)); + Q_REMOVE_BITS(1, ref bitsleft, ref bitbuf); + } + + Decompressor.UpdateModel(model, i); + return v; + } + + #endregion + } +} \ No newline at end of file diff --git a/BurnOutSharp.Compression/Quantum/State.cs b/BurnOutSharp.Compression/Quantum/State.cs new file mode 100644 index 00000000..4aee4912 --- /dev/null +++ b/BurnOutSharp.Compression/Quantum/State.cs @@ -0,0 +1,150 @@ +using BurnOutSharp.Models.Compression.Quantum; + +namespace BurnOutSharp.Compression.Quantum +{ + /// + public class State + { + /// + /// The actual decoding window + /// + public byte[] Window; + + /// + /// Window size (1Kb through 2Mb) + /// + public uint WindowSize; + + /// + /// Window size when it was first allocated + /// + public uint ActualSize; + + /// + /// Current offset within the window + /// + public uint WindowPosition; + + #region Models + + /// + /// Model for Model 4 + /// + public Model Model4; + + /// + /// Model for Model 5 + /// + public Model Model5; + + /// + /// Model for Model 6 Position + /// + public Model Model6Position; + + /// + /// Model for Model 6 Length + /// + public Model Model6Length; + + /// + /// Model for Model 7 + /// + public Model Model7; + + /// + /// Model for Model 7, Submodel 00 + /// + public Model Model7Submodel00; + + /// + /// Model for Model 7, Submodel 40 + /// + public Model Model7Submodel40; + + /// + /// Model for Model 7, Submodel 80 + /// + public Model Model7Submodel80; + + /// + /// Model for Model 7, Submodel C0 + /// + public Model Model7SubmodelC0; + + #endregion + + #region Symbol Tables + + /// + /// Symbol table for Model 4 + /// + public ModelSymbol[] Model4Symbols = new ModelSymbol[0x18 + 1]; + + /// + /// Symbol table for Model 5 + /// + public ModelSymbol[] Model5Symbols = new ModelSymbol[0x24 + 1]; + + /// + /// Symbol table for Model 6 Position + /// + public ModelSymbol[] Model6PositionSymbols = new ModelSymbol[0x2a + 1]; + + /// + /// Symbol table for Model 6 Length + /// + public ModelSymbol[] Model6LengthSymbols = new ModelSymbol[0x1b + 1]; + + /// + /// Symbol table for Model 7 + /// + public ModelSymbol[] Model7Symbols = new ModelSymbol[7 + 1]; + + /// + /// Symbol table for Model 7, Submodel 00 + /// + public ModelSymbol[] Model7Submodel00Symbols = new ModelSymbol[0x40 + 1]; + + /// + /// Symbol table for Model 7, Submodel 40 + /// + public ModelSymbol[] Model7Submodel40Symbols = new ModelSymbol[0x40 + 1]; + + /// + /// Symbol table for Model 7, Submodel 80 + /// + public ModelSymbol[] Model7Submodel80Symbols = new ModelSymbol[0x40 + 1]; + + /// + /// Symbol table for Model 7, Submodel C0 + /// + public ModelSymbol[] Model7SubmodelC0Symbols = new ModelSymbol[0x40 + 1]; + + #endregion + + #region Decompression Tables + + /// + /// XXXXX + /// + public byte[] q_length_base = new byte[27]; + + /// + /// XXXXX + /// + public byte[] q_length_extra = new byte[27]; + + /// + /// XXXXX + /// + public byte[] q_extra_bits = new byte[42]; + + /// + /// XXXXX + /// + public uint[] q_position_base = new uint[42]; + + #endregion + } +} \ No newline at end of file diff --git a/BurnOutSharp.Wrappers/MicrosoftCabinet.fdi.Quantum.cs b/BurnOutSharp.Wrappers/MicrosoftCabinet.fdi.Quantum.cs index 3aeb8d22..5f363039 100644 --- a/BurnOutSharp.Wrappers/MicrosoftCabinet.fdi.Quantum.cs +++ b/BurnOutSharp.Wrappers/MicrosoftCabinet.fdi.Quantum.cs @@ -1,4 +1,4 @@ -// using BurnOutSharp.Compression; +// using BurnOutSharp.Compression.Quantum; // using BurnOutSharp.Models.Compression.Quantum; // using static BurnOutSharp.Wrappers.CabinetConstants; // using static BurnOutSharp.Wrappers.FDIcConstants; @@ -11,120 +11,18 @@ // namespace BurnOutSharp.Wrappers // { -// /// -// internal class QuantumState +// internal unsafe class Quantumfdi // { -// /// -// /// the actual decoding window -// /// -// public byte[] window; - -// /// -// /// window size (1Kb through 2Mb) -// /// -// public uint window_size; - -// /// -// /// window size when it was first allocated -// /// -// public uint actual_size; - -// /// -// /// current offset within the window -// /// -// public uint window_posn; - -// public Model model7; -// public ModelSymbol[] m7sym = new ModelSymbol[7 + 1]; - -// public Model model4; -// public Model model5; -// public Model model6pos; -// public Model model6len; -// public ModelSymbol[] m4sym = new ModelSymbol[0x18 + 1]; -// public ModelSymbol[] m5sym = new ModelSymbol[0x24 + 1]; -// public ModelSymbol[] m6psym = new ModelSymbol[0x2a + 1]; -// public ModelSymbol[] m6lsym = new ModelSymbol[0x1b + 1]; - -// public Model model00; -// public Model model40; -// public Model model80; -// public Model modelC0; -// public ModelSymbol[] m00sym = new ModelSymbol[0x40 + 1]; -// public ModelSymbol[] m40sym = new ModelSymbol[0x40 + 1]; -// public ModelSymbol[] m80sym = new ModelSymbol[0x40 + 1]; -// public ModelSymbol[] mC0sym = new ModelSymbol[0x40 + 1]; -// } - -// internal class Quantumfdi -// { -// /// -// /// QTMfdi_init (internal) -// /// -// internal static int QTMfdi_init(int window, int level, fdi_decomp_state decomp_state) -// { -// uint wndsize = (uint)(1 << window); -// int msz = window * 2, i; -// uint j; - -// /* QTM supports window sizes of 2^10 (1Kb) through 2^21 (2Mb) */ -// /* if a previously allocated window is big enough, keep it */ -// if (window < 10 || window > 21) return DECR_DATAFORMAT; -// if (decomp_state.qtm.actual_size < wndsize) -// { -// if (decomp_state.qtm.window != null) decomp_state.fdi.free(decomp_state.qtm.window); -// decomp_state.qtm.window = null; -// } -// if (decomp_state.qtm.window == null) -// { -// if ((decomp_state.qtm.window = decomp_state.fdi.alloc((int)wndsize)) == null) return DECR_NOMEMORY; -// decomp_state.qtm.actual_size = wndsize; -// } -// decomp_state.qtm.window_size = wndsize; -// decomp_state.qtm.window_posn = 0; - -// /* initialize static slot/extrabits tables */ -// for (i = 0, j = 0; i < 27; i++) -// { -// decomp_state.q_length_extra[i] = (byte)((i == 26) ? 0 : (i < 2 ? 0 : i - 2) >> 2); -// decomp_state.q_length_base[i] = (byte)j; j += (uint)(1 << ((i == 26) ? 5 : decomp_state.q_length_extra[i])); -// } -// for (i = 0, j = 0; i < 42; i++) -// { -// decomp_state.q_extra_bits[i] = (byte)((i < 2 ? 0 : i - 2) >> 1); -// decomp_state.q_position_base[i] = j; j += (uint)(1 << decomp_state.q_extra_bits[i]); -// } - -// /* initialize arithmetic coding models */ - -// Quantum.InitModel(decomp_state.qtm.model7, decomp_state.qtm.m7sym, 7, 0); - -// Quantum.InitModel(decomp_state.qtm.model00, decomp_state.qtm.m00sym, 0x40, 0x00); -// Quantum.InitModel(decomp_state.qtm.model40, decomp_state.qtm.m40sym, 0x40, 0x40); -// Quantum.InitModel(decomp_state.qtm.model80, decomp_state.qtm.m80sym, 0x40, 0x80); -// Quantum.InitModel(decomp_state.qtm.modelC0, decomp_state.qtm.mC0sym, 0x40, 0xC0); - -// /* model 4 depends on table size, ranges from 20 to 24 */ -// Quantum.InitModel(decomp_state.qtm.model4, decomp_state.qtm.m4sym, (msz < 24) ? msz : 24, 0); -// /* model 5 depends on table size, ranges from 20 to 36 */ -// Quantum.InitModel(decomp_state.qtm.model5, decomp_state.qtm.m5sym, (msz < 36) ? msz : 36, 0); -// /* model 6pos depends on table size, ranges from 20 to 42 */ -// Quantum.InitModel(decomp_state.qtm.model6pos, decomp_state.qtm.m6psym, msz, 0); -// Quantum.InitModel(decomp_state.qtm.model6len, decomp_state.qtm.m6lsym, 27, 0); - -// return DECR_OK; -// } - // /// // /// QTMfdi_decomp(internal) // /// // internal static int QTMfdi_decomp(int inlen, int outlen, fdi_decomp_state decomp_state) // { // cab_UBYTE* inpos = decomp_state.inbuf; -// cab_UBYTE* window = decomp_state.qtm.window; +// cab_UBYTE* window = decomp_state.Window; // cab_UBYTE* runsrc, rundest; -// cab_ULONG window_posn = decomp_state.qtm.window_posn; -// cab_ULONG window_size = decomp_state.qtm.window_size; +// cab_ULONG window_posn = decomp_state.WindowPosition; +// cab_ULONG window_size = decomp_state.WindowSize; // /* used by bitstream macros */ // int bitsleft, bitrun, bitsneed; @@ -135,7 +33,7 @@ // cab_UWORD symf; // int i; -// int extra, togo = outlen, match_length = 0, copy_length; +// int extra = 0, togo = outlen, match_length = 0, copy_length; // cab_UBYTE selector, sym; // cab_ULONG match_offset = 0; @@ -144,8 +42,8 @@ // System.Diagnostics.Debug.WriteLine("(inlen == %d, outlen == %d)\n", inlen, outlen); // /* read initial value of C */ -// Q_INIT_BITSTREAM; -// Q_READ_BITS(C, 16); +// Q_INIT_BITSTREAM(out bitsleft, out bitbuf); +// C = Q_READ_BITS_UINT16(16, ref inpos, ref bitsleft, ref bitbuf); // /* apply 2^x-1 mask */ // window_posn &= window_size - 1; @@ -158,45 +56,53 @@ // while (togo > 0) // { -// GET_SYMBOL(model7, selector); +// selector = GET_SYMBOL(state.Model7, ref H, ref L, ref C, ref inpos, ref bitsleft, ref bitbuf); // switch (selector) // { // case 0: -// GET_SYMBOL(model00, sym); window[window_posn++] = sym; togo--; +// sym = GET_SYMBOL(state.Model7Submodel00, ref H, ref L, ref C, ref inpos, ref bitsleft, ref bitbuf); +// window[window_posn++] = sym; +// togo--; // break; // case 1: -// GET_SYMBOL(model40, sym); window[window_posn++] = sym; togo--; +// sym = GET_SYMBOL(state.Model7Submodel40, ref H, ref L, ref C, ref inpos, ref bitsleft, ref bitbuf); +// window[window_posn++] = sym; +// togo--; // break; // case 2: -// GET_SYMBOL(model80, sym); window[window_posn++] = sym; togo--; +// sym = GET_SYMBOL(state.Model7Submodel80, ref H, ref L, ref C, ref inpos, ref bitsleft, ref bitbuf); +// window[window_posn++] = sym; +// togo--; // break; // case 3: -// GET_SYMBOL(modelC0, sym); window[window_posn++] = sym; togo--; +// sym = GET_SYMBOL(state.Model7SubmodelC0, ref H, ref L, ref C, ref inpos, ref bitsleft, ref bitbuf); +// window[window_posn++] = sym; +// togo--; // break; +// // Selector 4 = fixed length of 3 // case 4: -// /* selector 4 = fixed length of 3 */ -// GET_SYMBOL(model4, sym); -// Q_READ_BITS(extra, decomp_state.q_extra_bits[sym]); +// sym = GET_SYMBOL(state.Model4, ref H, ref L, ref C, ref inpos, ref bitsleft, ref bitbuf); +// extra = Q_READ_BITS_INT32(state.q_extra_bits[sym], ref inpos, ref bitsleft, ref bitbuf); // match_offset = decomp_state.q_position_base[sym] + extra + 1; // match_length = 3; // break; +// // Selector 5 = fixed length of 4 // case 5: -// /* selector 5 = fixed length of 4 */ -// GET_SYMBOL(model5, sym); -// Q_READ_BITS(extra, decomp_state.q_extra_bits[sym]); +// sym = GET_SYMBOL(state.Model5, ref H, ref L, ref C, ref inpos, ref bitsleft, ref bitbuf); +// extra = Q_READ_BITS_INT32(state.q_extra_bits[sym], ref inpos, ref bitsleft, ref bitbuf); // match_offset = decomp_state.q_position_base[sym] + extra + 1; // match_length = 4; // break; +// // Selector 6 = variable length // case 6: -// /* selector 6 = variable length */ -// GET_SYMBOL(model6len, sym); -// Q_READ_BITS(extra, decomp_state.q_length_extra[sym]); +// sym = GET_SYMBOL(state.Model6Length, ref H, ref L, ref C, ref inpos, ref bitsleft, ref bitbuf); +// extra = Q_READ_BITS_INT32(state.q_length_extra[sym], ref inpos, ref bitsleft, ref bitbuf); // match_length = decomp_state.q_length_base[sym] + extra + 5; -// GET_SYMBOL(model6pos, sym); -// Q_READ_BITS(extra, decomp_state.q_extra_bits[sym]); +// sym = GET_SYMBOL(state.Model6Position, ref H, ref L, ref C, ref inpos, ref bitsleft, ref bitbuf); +// extra = Q_READ_BITS_INT32(state.q_extra_bits[sym], ref inpos, ref bitsleft, ref bitbuf); // match_offset = decomp_state.q_position_base[sym] + extra + 1; // break; @@ -244,8 +150,131 @@ // memcpy(decomp_state.outbuf, window + ((!window_posn) ? window_size : window_posn) - outlen, outlen); -// decomp_state.qtm.window_posn = window_posn; +// decomp_state.WindowPosition = window_posn; // return DECR_OK; // } + +// /// +// /// Should be used first to set up the system +// /// +// private static void Q_INIT_BITSTREAM(out int bitsleft, out uint bitbuf) +// { +// bitsleft = 0; bitbuf = 0; +// } + +// /// +// /// Adds more data to the bit buffer, if there is room for another 16 bits. +// /// +// private static void Q_FILL_BUFFER(ref byte* inpos, ref int bitsleft, ref uint bitbuf) +// { +// if (bitsleft <= 16) +// { +// bitbuf |= (uint)((inpos[0] << 8) | inpos[1]) << (16 - bitsleft); +// bitsleft += 16; inpos += 2; +// } +// } + +// /// +// /// Extracts (without removing) N bits from the bit buffer +// /// +// private static uint Q_PEEK_BITS(int n, uint bitbuf) +// { +// return bitbuf >> (32 - n); +// } + +// /// +// /// Removes N bits from the bit buffer +// /// +// private static void Q_REMOVE_BITS(int n, ref int bitsleft, ref uint bitbuf) +// { +// bitbuf <<= n; +// bitsleft -= n; +// } + +// /// +// /// Takes N bits from the buffer and puts them in v. Unlike LZX, this can loop +// /// several times to get the requisite number of bits. +// /// +// private static ushort Q_READ_BITS_UINT16(int n, ref byte* inpos, ref int bitsleft, ref uint bitbuf) +// { +// ushort v = 0; int bitrun; +// for (int bitsneed = n; bitsneed != 0; bitsneed -= bitrun) +// { +// Q_FILL_BUFFER(ref inpos, ref bitsleft, ref bitbuf); + +// bitrun = (bitsneed > bitsleft) ? bitsleft : bitsneed; +// v = (ushort)((v << bitrun) | Q_PEEK_BITS(bitrun, bitbuf)); + +// Q_REMOVE_BITS(bitrun, ref bitsleft, ref bitbuf); +// } + +// return v; +// } + +// /// +// /// Takes N bits from the buffer and puts them in v. Unlike LZX, this can loop +// /// several times to get the requisite number of bits. +// /// +// private static int Q_READ_BITS_INT32(int n, ref byte* inpos, ref int bitsleft, ref uint bitbuf) +// { +// int v = 0; int bitrun; +// for (int bitsneed = n; bitsneed != 0; bitsneed -= bitrun) +// { +// Q_FILL_BUFFER(ref inpos, ref bitsleft, ref bitbuf); + +// bitrun = (bitsneed > bitsleft) ? bitsleft : bitsneed; +// v = (int)((v << bitrun) | Q_PEEK_BITS(bitrun, bitbuf)); + +// Q_REMOVE_BITS(bitrun, ref bitsleft, ref bitbuf); +// } + +// return v; +// } + +// /// +// /// Fetches the next symbol from the stated model and puts it in v. +// /// It may need to read the bitstream to do this. +// /// +// private static int GET_SYMBOL(Model model, ref ushort H, ref ushort L, ref ushort C, ref byte* inpos, ref int bitsleft, ref uint bitbuf) +// { +// uint range = (uint)(((H - L) & 0xFFFF) + 1); +// ushort symf = (ushort)(((((C - L + 1) * model.Symbols[0].CumulativeFrequency) - 1) / range) & 0xFFFF); + +// int i; +// for (i = 1; i < model.Entries; i++) +// { +// if (model.Symbols[i].CumulativeFrequency <= symf) +// break; +// } + +// int v = model.Symbols[i - 1].Symbol; +// range = (uint)(H - L + 1); +// H = (ushort)(L + ((model.Symbols[i - 1].CumulativeFrequency * range) / model.Symbols[0].CumulativeFrequency) - 1); +// L = (ushort)(L + ((model.Symbols[i].CumulativeFrequency * range) / model.Symbols[0].CumulativeFrequency)); + +// while (true) +// { +// if ((L & 0x8000) != (H & 0x8000)) +// { +// if ((L & 0x4000) != 0 && (H & 0x4000) == 0) +// { +// // Underflow case +// C ^= 0x4000; L &= 0x3FFF; H |= 0x4000; +// } +// else +// { +// break; +// } +// } + +// L <<= 1; H = (ushort)((H << 1) | 1); +// Q_FILL_BUFFER(ref inpos, ref bitsleft, ref bitbuf); +// C = (ushort)((C << 1) | Q_PEEK_BITS(1, bitbuf)); +// Q_REMOVE_BITS(1, ref bitsleft, ref bitbuf); +// } + +// Decompressor.UpdateModel(model, i); +// return v; +// } // } // } \ No newline at end of file