From 895f40414dbde0ffcb78825d3d4a941ed3b27355 Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Sun, 1 Jan 2023 22:02:54 -0800 Subject: [PATCH] Some Quantum things to models / compression --- BurnOutSharp.Compression/Quantum.cs | 109 ++++++++++++++ .../Compression/Quantum/Constants.cs | 45 ++++++ .../Compression/Quantum/Model.cs | 11 +- .../Compression/Quantum/ModelSymbol.cs | 7 +- .../MicrosoftCabinet.fdi.Quantum.cs | 141 +++++++----------- BurnOutSharp.Wrappers/MicrosoftCabinet.fdi.cs | 114 +------------- 6 files changed, 221 insertions(+), 206 deletions(-) create mode 100644 BurnOutSharp.Models/Compression/Quantum/Constants.cs diff --git a/BurnOutSharp.Compression/Quantum.cs b/BurnOutSharp.Compression/Quantum.cs index 43b5f5bd..d06e30dd 100644 --- a/BurnOutSharp.Compression/Quantum.cs +++ b/BurnOutSharp.Compression/Quantum.cs @@ -1,9 +1,118 @@ +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 + + /// + /// 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.Models/Compression/Quantum/Constants.cs b/BurnOutSharp.Models/Compression/Quantum/Constants.cs new file mode 100644 index 00000000..04af0e32 --- /dev/null +++ b/BurnOutSharp.Models/Compression/Quantum/Constants.cs @@ -0,0 +1,45 @@ +namespace BurnOutSharp.Models.Compression.Quantum +{ + public static class Constants + { + /// + /// Mask for Quantum Compression Level + /// + public const ushort MASK_QUANTUM_LEVEL = 0x00F0; + + /// + /// Lowest Quantum Level (1) + /// + public const ushort QUANTUM_LEVEL_LO = 0x0010; + + /// + /// Highest Quantum Level (7) + /// + public const ushort QUANTUM_LEVEL_HI = 0x0070; + + /// + /// Amount to shift over to get int + /// + public const ushort SHIFT_QUANTUM_LEVEL = 4; + + /// + /// Mask for Quantum Compression Memory + /// + public const ushort MASK_QUANTUM_MEM = 0x1F00; + + /// + /// Lowest Quantum Memory (10) + /// + public const ushort QUANTUM_MEM_LO = 0x0A00; + + /// + /// Highest Quantum Memory (21) + /// + public const ushort QUANTUM_MEM_HI = 0x1500; + + /// + /// Amount to shift over to get int + /// + public const ushort SHIFT_QUANTUM_MEM = 8; + } +} \ No newline at end of file diff --git a/BurnOutSharp.Models/Compression/Quantum/Model.cs b/BurnOutSharp.Models/Compression/Quantum/Model.cs index 6c8cbf12..de9471c4 100644 --- a/BurnOutSharp.Models/Compression/Quantum/Model.cs +++ b/BurnOutSharp.Models/Compression/Quantum/Model.cs @@ -1,10 +1,15 @@ namespace BurnOutSharp.Models.Compression.Quantum { + /// /// - public class Model + public sealed class Model { - public int Entries { get; set; } + public int ShiftsLeft; - public ModelSymbol[] Symbols { get; set; } + public int Entries; + + public ModelSymbol[] Symbols; + + public ushort[] LookupTable = new ushort[256]; } } \ No newline at end of file diff --git a/BurnOutSharp.Models/Compression/Quantum/ModelSymbol.cs b/BurnOutSharp.Models/Compression/Quantum/ModelSymbol.cs index 4998a6fd..0e0f2641 100644 --- a/BurnOutSharp.Models/Compression/Quantum/ModelSymbol.cs +++ b/BurnOutSharp.Models/Compression/Quantum/ModelSymbol.cs @@ -1,10 +1,11 @@ namespace BurnOutSharp.Models.Compression.Quantum { + /// /// - public class ModelSymbol + public sealed class ModelSymbol { - public ushort Symbol { get; private set; } + public ushort Symbol; - public ushort CumulativeFrequency { get; private set; } + public ushort CumulativeFrequency; } } \ No newline at end of file diff --git a/BurnOutSharp.Wrappers/MicrosoftCabinet.fdi.Quantum.cs b/BurnOutSharp.Wrappers/MicrosoftCabinet.fdi.Quantum.cs index 7fa1d293..3aeb8d22 100644 --- a/BurnOutSharp.Wrappers/MicrosoftCabinet.fdi.Quantum.cs +++ b/BurnOutSharp.Wrappers/MicrosoftCabinet.fdi.Quantum.cs @@ -1,3 +1,5 @@ +// using BurnOutSharp.Compression; +// using BurnOutSharp.Models.Compression.Quantum; // using static BurnOutSharp.Wrappers.CabinetConstants; // using static BurnOutSharp.Wrappers.FDIcConstants; // using static BurnOutSharp.Wrappers.FDIConstants; @@ -9,94 +11,53 @@ // namespace BurnOutSharp.Wrappers // { -// internal unsafe class Quantumfdi +// /// +// internal class QuantumState // { // /// -// /// QTMupdatemodel (internal) +// /// the actual decoding window // /// -// internal static void QTMupdatemodel(QTMmodel model, int sym) -// { -// for (int i = 0; i < sym; i++) -// { -// model.syms[i].cumfreq += 8; -// } - -// if (model.syms[0].cumfreq > 3800) -// { -// if (--model.shiftsleft != 0) -// { -// for (int i = model.entries - 1; i >= 0; i--) -// { -// // -1, not -2; the 0 entry saves this -// model.syms[i].cumfreq >>= 1; -// if (model.syms[i].cumfreq <= model.syms[i + 1].cumfreq) -// model.syms[i].cumfreq = (ushort)(model.syms[i + 1].cumfreq + 1); -// } -// } -// else -// { -// model.shiftsleft = 50; -// 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.syms[i].cumfreq -= model.syms[i + 1].cumfreq; -// model.syms[i].cumfreq++; /* avoid losing things entirely */ -// model.syms[i].cumfreq >>= 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.syms[i].cumfreq < model.syms[j].cumfreq) -// { -// QTMmodelsym temp = model.syms[i]; -// model.syms[i] = model.syms[j]; -// model.syms[j] = temp; -// } -// } -// } - -// // Then convert frequencies back to cumfreq -// for (int i = model.entries - 1; i >= 0; i--) -// { -// model.syms[i].cumfreq += model.syms[i + 1].cumfreq; -// } - -// // Then update the other part of the table -// for (int i = 0; i < model.entries; i++) -// { -// model.tabloc[model.syms[i].sym] = (ushort)i; -// } -// } -// } -// } +// public byte[] window; // /// -// /// QTMfdi_initmodel (internal) -// /// -// /// Initialize a model which decodes symbols from [s] to [s]+[n]-1 +// /// window size (1Kb through 2Mb) // /// -// internal static void QTMfdi_initmodel(QTMmodel m, QTMmodelsym[] sym, int n, int s) -// { -// int i; -// m.shiftsleft = 4; -// m.entries = n; -// m.syms = sym; -// memset(m.tabloc, 0xFF, sizeof(m.tabloc)); /* clear out look-up table */ -// for (i = 0; i < n; i++) -// { -// m.tabloc[i + s] = (ushort)i; /* set up a look-up entry for symbol */ -// m.syms[i].sym = (ushort)(i + s); /* actual symbol */ -// m.syms[i].cumfreq = (ushort)(n - i); /* current frequency of that symbol */ -// } -// m.syms[n].cumfreq = 0; -// } +// 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) // /// @@ -136,20 +97,20 @@ // /* initialize arithmetic coding models */ -// QTMfdi_initmodel(decomp_state.qtm.model7, decomp_state.qtm.m7sym, 7, 0); +// Quantum.InitModel(decomp_state.qtm.model7, decomp_state.qtm.m7sym, 7, 0); -// QTMfdi_initmodel(decomp_state.qtm.model00, decomp_state.qtm.m00sym, 0x40, 0x00); -// QTMfdi_initmodel(decomp_state.qtm.model40, decomp_state.qtm.m40sym, 0x40, 0x40); -// QTMfdi_initmodel(decomp_state.qtm.model80, decomp_state.qtm.m80sym, 0x40, 0x80); -// QTMfdi_initmodel(decomp_state.qtm.modelC0, decomp_state.qtm.mC0sym, 0x40, 0xC0); +// 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 */ -// QTMfdi_initmodel(decomp_state.qtm.model4, decomp_state.qtm.m4sym, (msz < 24) ? msz : 24, 0); +// 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 */ -// QTMfdi_initmodel(decomp_state.qtm.model5, decomp_state.qtm.m5sym, (msz < 36) ? msz : 36, 0); +// 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 */ -// QTMfdi_initmodel(decomp_state.qtm.model6pos, decomp_state.qtm.m6psym, msz, 0); -// QTMfdi_initmodel(decomp_state.qtm.model6len, decomp_state.qtm.m6lsym, 27, 0); +// 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; // } diff --git a/BurnOutSharp.Wrappers/MicrosoftCabinet.fdi.cs b/BurnOutSharp.Wrappers/MicrosoftCabinet.fdi.cs index dee12406..5dc244d7 100644 --- a/BurnOutSharp.Wrappers/MicrosoftCabinet.fdi.cs +++ b/BurnOutSharp.Wrappers/MicrosoftCabinet.fdi.cs @@ -2,6 +2,7 @@ // using System.Collections.Generic; // using System.IO; // using System.Linq; +// using BurnOutSharp.Compression; // using static BurnOutSharp.Wrappers.CabinetConstants; // using static BurnOutSharp.Wrappers.FDIcConstants; // using static BurnOutSharp.Wrappers.FDIConstants; @@ -697,46 +698,6 @@ // /// // public const ushort tcompSHIFT_LZX_WINDOW = 8; -// /// -// /// Mask for Quantum Compression Level -// /// -// public const ushort tcompMASK_QUANTUM_LEVEL = 0x00F0; - -// /// -// /// Lowest Quantum Level (1) -// /// -// public const ushort tcompQUANTUM_LEVEL_LO = 0x0010; - -// /// -// /// Highest Quantum Level (7) -// /// -// public const ushort tcompQUANTUM_LEVEL_HI = 0x0070; - -// /// -// /// Amount to shift over to get int -// /// -// public const ushort tcompSHIFT_QUANTUM_LEVEL = 4; - -// /// -// /// Mask for Quantum Compression Memory -// /// -// public const ushort tcompMASK_QUANTUM_MEM = 0x1F00; - -// /// -// /// Lowest Quantum Memory (10) -// /// -// public const ushort tcompQUANTUM_MEM_LO = 0x0A00; - -// /// -// /// Highest Quantum Memory (21) -// /// -// public const ushort tcompQUANTUM_MEM_HI = 0x1500; - -// /// -// /// Amount to shift over to get int -// /// -// public const ushort tcompSHIFT_QUANTUM_MEM = 8; - // /// // /// Reserved bits (high 3 bits) // /// @@ -1336,73 +1297,6 @@ // public byte* inpos; // } -// /* Quantum stuff */ - -// /// -// internal class QTMmodelsym -// { -// public ushort sym; - -// public ushort cumfreq; -// } - -// /// -// internal class QTMmodel -// { -// public int shiftsleft; - -// public int entries; - -// public QTMmodelsym[] syms; - -// public ushort[] tabloc = new ushort[256]; -// } - -// /// -// internal class QTMstate -// { -// /// -// /// 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 QTMmodel model7; -// public QTMmodelsym[] m7sym = new QTMmodelsym[7 + 1]; - -// public QTMmodel model4; -// public QTMmodel model5; -// public QTMmodel model6pos; -// public QTMmodel model6len; -// public QTMmodelsym[] m4sym = new QTMmodelsym[0x18 + 1]; -// public QTMmodelsym[] m5sym = new QTMmodelsym[0x24 + 1]; -// public QTMmodelsym[] m6psym = new QTMmodelsym[0x2a + 1]; -// public QTMmodelsym[] m6lsym = new QTMmodelsym[0x1b + 1]; - -// public QTMmodel model00; -// public QTMmodel model40; -// public QTMmodel model80; -// public QTMmodel modelC0; -// public QTMmodelsym[] m00sym = new QTMmodelsym[0x40 + 1]; -// public QTMmodelsym[] m40sym = new QTMmodelsym[0x40 + 1]; -// public QTMmodelsym[] m80sym = new QTMmodelsym[0x40 + 1]; -// public QTMmodelsym[] mC0sym = new QTMmodelsym[0x40 + 1]; -// } - // /* LZX stuff */ // /// @@ -1699,7 +1593,7 @@ // #region methods // public ZIPstate zip; -// public QTMstate qtm; +// public QuantumState qtm; // public LZXstate lzx; // #endregion @@ -1792,7 +1686,7 @@ // // Q_REMOVE_BITS(1); \ // // } \ // // \ -// // QTMupdatemodel(&(decomp_state.qtm.m)), i); \ +// // Quantum.UpdateModel(&(decomp_state.qtm.m)), i); \ // // } while (0) // /* Bitstream reading macros (LZX / intel little-endian byte order) @@ -2115,7 +2009,7 @@ // #region methods // public ZIPstate zip; -// public QTMstate qtm; +// public QuantumState qtm; // public LZXstate lzx; // #endregion