From ec758e5c18629bab8e99654ab7646c8d4b5ad90c Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Mon, 2 Jan 2023 00:03:03 -0800 Subject: [PATCH] Finalize Quantum port to C# (pass 1) --- .../Quantum/Decompressor.cs | 138 ++++++++- .../MicrosoftCabinet.fdi.Quantum.cs | 280 ------------------ 2 files changed, 131 insertions(+), 287 deletions(-) delete mode 100644 BurnOutSharp.Wrappers/MicrosoftCabinet.fdi.Quantum.cs diff --git a/BurnOutSharp.Compression/Quantum/Decompressor.cs b/BurnOutSharp.Compression/Quantum/Decompressor.cs index 9fb363e0..5de1b25e 100644 --- a/BurnOutSharp.Compression/Quantum/Decompressor.cs +++ b/BurnOutSharp.Compression/Quantum/Decompressor.cs @@ -1,20 +1,144 @@ +using System; 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; + int inpos = 0; // inbuf[0] + int window = 0; // state.Window[0] + int runsrc, rundest; + uint window_posn = state.WindowPosition; + uint window_size = state.WindowSize; + + int extra, togo = outlen, match_length = 0, copy_length; + byte selector, sym; + uint match_offset = 0; + + ushort H = 0xFFFF, L = 0; + + // Read initial value of C + Q_INIT_BITSTREAM(out int bitsleft, out uint bitbuf); + ushort C = Q_READ_BITS_UINT16(16, inbuf, ref inpos, ref bitsleft, ref bitbuf); + + // Apply 2^x-1 mask + window_posn &= window_size - 1; + + // Runs can't straddle the window wraparound + if ((window_posn + togo) > window_size) + return false; + + while (togo > 0) + { + selector = (byte)GET_SYMBOL(state.Model7, ref H, ref L, ref C, inbuf, ref inpos, ref bitsleft, ref bitbuf); + switch (selector) + { + case 0: + sym = (byte)GET_SYMBOL(state.Model7Submodel00, ref H, ref L, ref C, inbuf, ref inpos, ref bitsleft, ref bitbuf); + state.Window[window + window_posn++] = sym; + togo--; + break; + case 1: + sym = (byte)GET_SYMBOL(state.Model7Submodel40, ref H, ref L, ref C, inbuf, ref inpos, ref bitsleft, ref bitbuf); + state.Window[window + window_posn++] = sym; + togo--; + break; + case 2: + sym = (byte)GET_SYMBOL(state.Model7Submodel80, ref H, ref L, ref C, inbuf, ref inpos, ref bitsleft, ref bitbuf); + state.Window[window + window_posn++] = sym; + togo--; + break; + case 3: + sym = (byte)GET_SYMBOL(state.Model7SubmodelC0, ref H, ref L, ref C, inbuf, ref inpos, ref bitsleft, ref bitbuf); + state.Window[window + window_posn++] = sym; + togo--; + break; + + // Selector 4 = fixed length of 3 + case 4: + sym = (byte)GET_SYMBOL(state.Model4, ref H, ref L, ref C, inbuf, ref inpos, ref bitsleft, ref bitbuf); + extra = Q_READ_BITS_INT32(state.q_extra_bits[sym], inbuf, ref inpos, ref bitsleft, ref bitbuf); + match_offset = (uint)(state.q_position_base[sym] + extra + 1); + match_length = 3; + break; + + // Selector 5 = fixed length of 4 + case 5: + sym = (byte)GET_SYMBOL(state.Model5, ref H, ref L, ref C, inbuf, ref inpos, ref bitsleft, ref bitbuf); + extra = Q_READ_BITS_INT32(state.q_extra_bits[sym], inbuf, ref inpos, ref bitsleft, ref bitbuf); + match_offset = (uint)(state.q_position_base[sym] + extra + 1); + match_length = 4; + break; + + // Selector 6 = variable length + case 6: + sym = (byte)GET_SYMBOL(state.Model6Length, ref H, ref L, ref C, inbuf, ref inpos, ref bitsleft, ref bitbuf); + extra = Q_READ_BITS_INT32(state.q_length_extra[sym], inbuf, ref inpos, ref bitsleft, ref bitbuf); + match_length = state.q_length_base[sym] + extra + 5; + sym = (byte)GET_SYMBOL(state.Model6Position, ref H, ref L, ref C, inbuf, ref inpos, ref bitsleft, ref bitbuf); + extra = Q_READ_BITS_INT32(state.q_extra_bits[sym], inbuf, ref inpos, ref bitsleft, ref bitbuf); + match_offset = (uint)(state.q_position_base[sym] + extra + 1); + break; + + default: + return false; + } + + // If this is a match */ + if (selector >= 4) + { + rundest = (int)(window + window_posn); + togo -= match_length; + + // Copy any wrapped around source data + if (window_posn >= match_offset) + { + // No wrap + runsrc = (int)(rundest - match_offset); + } + else + { + runsrc = (int)(rundest + (window_size - match_offset)); + copy_length = (int)(match_offset - window_posn); + if (copy_length < match_length) + { + match_length -= copy_length; + window_posn += (uint)copy_length; + while (copy_length-- > 0) + { + state.Window[rundest++] = state.Window[rundest++]; + } + + runsrc = window; + } + } + + window_posn += (uint)match_length; + + // Copy match data - no worries about destination wraps + while (match_length-- > 0) + { + state.Window[rundest++] = state.Window[runsrc++]; + } + } + } + + if (togo != 0) + return false; + + Array.Copy(state.Window, (window_posn == 0 ? window_size : window_posn) - outlen, outbuf, 0, outlen); + + state.WindowPosition = window_posn; + return true; } /// @@ -355,7 +479,7 @@ namespace BurnOutSharp.Compression.Quantum /// 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) + private static ushort 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); @@ -367,7 +491,7 @@ namespace BurnOutSharp.Compression.Quantum break; } - int v = model.Symbols[i - 1].Symbol; + ushort 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)); diff --git a/BurnOutSharp.Wrappers/MicrosoftCabinet.fdi.Quantum.cs b/BurnOutSharp.Wrappers/MicrosoftCabinet.fdi.Quantum.cs deleted file mode 100644 index 5f363039..00000000 --- a/BurnOutSharp.Wrappers/MicrosoftCabinet.fdi.Quantum.cs +++ /dev/null @@ -1,280 +0,0 @@ -// using BurnOutSharp.Compression.Quantum; -// using BurnOutSharp.Models.Compression.Quantum; -// using static BurnOutSharp.Wrappers.CabinetConstants; -// using static BurnOutSharp.Wrappers.FDIcConstants; -// using static BurnOutSharp.Wrappers.FDIConstants; -// using cab_LONG = System.Int32; -// using cab_off_t = System.UInt32; -// using cab_UBYTE = System.Byte; -// using cab_ULONG = System.UInt32; -// using cab_UWORD = System.UInt16; - -// namespace BurnOutSharp.Wrappers -// { -// internal unsafe class Quantumfdi -// { -// /// -// /// 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.Window; -// cab_UBYTE* runsrc, rundest; -// cab_ULONG window_posn = decomp_state.WindowPosition; -// cab_ULONG window_size = decomp_state.WindowSize; - -// /* used by bitstream macros */ -// int bitsleft, bitrun, bitsneed; -// cab_ULONG bitbuf; - -// /* used by GET_SYMBOL */ -// cab_ULONG range; -// cab_UWORD symf; -// int i; - -// int extra = 0, togo = outlen, match_length = 0, copy_length; -// cab_UBYTE selector, sym; -// cab_ULONG match_offset = 0; - -// cab_UWORD H = 0xFFFF, L = 0, C; - -// System.Diagnostics.Debug.WriteLine("(inlen == %d, outlen == %d)\n", inlen, outlen); - -// /* read initial value of C */ -// 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; -// /* runs can't straddle the window wraparound */ -// if ((window_posn + togo) > window_size) -// { -// System.Diagnostics.Debug.WriteLine("straddled run\n"); -// return DECR_DATAFORMAT; -// } - -// while (togo > 0) -// { -// selector = GET_SYMBOL(state.Model7, ref H, ref L, ref C, ref inpos, ref bitsleft, ref bitbuf); -// switch (selector) -// { -// case 0: -// 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: -// 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: -// 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: -// 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: -// 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: -// 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: -// 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; -// 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; - -// default: -// System.Diagnostics.Debug.WriteLine("Selector is bogus\n"); -// return DECR_ILLEGALDATA; -// } - -// /* if this is a match */ -// if (selector >= 4) -// { -// rundest = window + window_posn; -// togo -= match_length; - -// /* copy any wrapped around source data */ -// if (window_posn >= match_offset) -// { -// /* no wrap */ -// runsrc = rundest - match_offset; -// } -// else -// { -// runsrc = rundest + (window_size - match_offset); -// copy_length = match_offset - window_posn; -// if (copy_length < match_length) -// { -// match_length -= copy_length; -// window_posn += copy_length; -// while (copy_length-- > 0) *rundest++ = *runsrc++; -// runsrc = window; -// } -// } -// window_posn += match_length; - -// /* copy match data - no worries about destination wraps */ -// while (match_length-- > 0) *rundest++ = *runsrc++; -// } -// } /* while (togo > 0) */ - -// if (togo != 0) -// { -// System.Diagnostics.Debug.WriteLine("Frame overflow, this_run = %d\n", togo); -// return DECR_ILLEGALDATA; -// } - -// memcpy(decomp_state.outbuf, window + ((!window_posn) ? window_size : window_posn) - outlen, outlen); - -// 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