diff --git a/libmspack/kwajd_stream.cs b/libmspack/kwajd_stream.cs index a8f05eb..73eebb6 100644 --- a/libmspack/kwajd_stream.cs +++ b/libmspack/kwajd_stream.cs @@ -1,4 +1,5 @@ using static SabreTools.Compression.libmspack.kwaj; +using static SabreTools.Compression.libmspack.lzss; namespace SabreTools.Compression.libmspack { diff --git a/libmspack/lzx.cs b/libmspack/lzx.cs new file mode 100644 index 0000000..3b9e0fa --- /dev/null +++ b/libmspack/lzx.cs @@ -0,0 +1,147 @@ +namespace SabreTools.Compression.libmspack +{ + public static class lzx + { + // Some constants defined by the LZX specification + public const int LZX_MIN_MATCH = 2; + public const int LZX_MAX_MATCH = 257; + public const int LZX_NUM_CHARS = 256; + public const int LZX_BLOCKTYPE_INVALID = 0; /* also blocktypes 4-7 invalid */ + public const int LZX_BLOCKTYPE_VERBATIM = 1; + public const int LZX_BLOCKTYPE_ALIGNED = 2; + public const int LZX_BLOCKTYPE_UNCOMPRESSED = 3; + public const int LZX_PRETREE_NUM_ELEMENTS = 20; + public const int LZX_ALIGNED_NUM_ELEMENTS = 8; /* aligned offset tree #elements */ + public const int LZX_NUM_PRIMARY_LENGTHS = 7; /* this one missing from spec! */ + public const int LZX_NUM_SECONDARY_LENGTHS = 249; /* length tree #elements */ + + // LZX huffman defines: tweak tablebits as desired + public const int LZX_PRETREE_MAXSYMBOLS = LZX_PRETREE_NUM_ELEMENTS; + public const int LZX_PRETREE_TABLEBITS = 6; + public const int LZX_MAINTREE_MAXSYMBOLS = LZX_NUM_CHARS + 290 * 8; + public const int LZX_MAINTREE_TABLEBITS = 12; + public const int LZX_LENGTH_MAXSYMBOLS = LZX_NUM_SECONDARY_LENGTHS + 1; + public const int LZX_LENGTH_TABLEBITS = 12; + public const int LZX_ALIGNED_MAXSYMBOLS = LZX_ALIGNED_NUM_ELEMENTS; + public const int LZX_ALIGNED_TABLEBITS = 7; + public const int LZX_LENTABLE_SAFETY = 64; /* table decoding overruns are allowed */ + + public const int LZX_FRAME_SIZE = 32768; /* the size of a frame in LZX */ + + /// + /// Allocates and initialises LZX decompression state for decoding an LZX + /// stream. + /// + /// This routine uses system->alloc() to allocate memory. If memory + /// allocation fails, or the parameters to this function are invalid, + /// NULL is returned. + /// + /// + /// An mspack_system structure used to read from + /// the input stream and write to the output + /// stream, also to allocate and free memory. + /// + /// An input stream with the LZX data. + /// An output stream to write the decoded data to. + /// + /// The size of the decoding window, which must be + /// between 15 and 21 inclusive for regular LZX + /// data, or between 17 and 25 inclusive for + /// LZX DELTA data. + /// + /// + /// The interval at which the LZX bitstream is + /// reset, in multiples of LZX frames (32678 + /// bytes), e.g. a value of 2 indicates the input + /// stream resets after every 65536 output bytes. + /// A value of 0 indicates that the bitstream never + /// resets, such as in CAB LZX streams. + /// + /// The number of bytes to use as an input bitstream buffer. + /// + /// The length in bytes of the entirely + /// decompressed output stream, if known in + /// advance. It is used to correctly perform the + /// Intel E8 transformation, which must stop 6 + /// bytes before the very end of the + /// decompressed stream. It is not otherwise used + /// or adhered to. If the full decompressed + /// length is known in advance, set it here. + /// If it is NOT known, use the value 0, and call + /// lzxd_set_output_length() once it is + /// known. If never set, 4 of the final 6 bytes + /// of the output stream may be incorrect. + /// + /// + /// Should be zero for all regular LZX data, + /// non-zero for LZX DELTA encoded data. + /// + /// + /// A pointer to an initialised lzxd_stream structure, or NULL if + /// there was not enough memory or parameters to the function were wrong. + /// + public static lzxd_stream lzxd_init(mspack_system system, mspack_file input, mspack_file output, int window_bits, int reset_interval, int input_buffer_size, long output_length, char is_delta) => null; + + /// + /// See description of output_length in lzxd_init() + /// + public static void lzxd_set_output_length(lzxd_stream lzx, long output_length) { } + + /// + /// Reads LZX DELTA reference data into the window and allows + /// lzxd_decompress() to reference it. + /// + /// Call this before the first call to lzxd_decompress(). + /// + /// The LZX stream to apply this reference data to + /// + /// An mspack_system implementation to use with the + /// input param. Only read() will be called. + /// + /// + /// An input file handle to read reference data using + /// system->read(). + /// + /// + /// The length of the reference data. Cannot be longer + /// than the LZX window size. + /// + /// An error code, or MSPACK_ERR_OK if successful + public static MSPACK_ERR lzxd_set_reference_data(lzxd_stream lzx, mspack_system system, mspack_file input, uint length) => MSPACK_ERR.MSPACK_ERR_OK; + + /// + /// Decompresses entire or partial LZX streams. + /// + /// The number of bytes of data that should be decompressed is given as the + /// out_bytes parameter. If more bytes are decoded than are needed, they + /// will be kept over for a later invocation. + /// + /// The output bytes will be passed to the system->write() function given in + /// lzxd_init(), using the output file handle given in lzxd_init(). More than + /// one call may be made to system->write(). + /// + /// Input bytes will be read in as necessary using the system->read() + /// function given in lzxd_init(), using the input file handle given in + /// lzxd_init(). This will continue until system->read() returns 0 bytes, + /// or an error. Errors will be passed out of the function as + /// MSPACK_ERR_READ errors. Input streams should convey an "end of input + /// stream" by refusing to supply all the bytes that LZX asks for when they + /// reach the end of the stream, rather than return an error code. + /// + /// If any error code other than MSPACK_ERR_OK is returned, the stream + /// should be considered unusable and lzxd_decompress() should not be + /// called again on this stream. + /// + /// LZX decompression state, as allocated by lzxd_init(). + /// The number of bytes of data to decompress. + /// An error code, or MSPACK_ERR_OK if successful + public static MSPACK_ERR lzxd_decompress(lzxd_stream lzx, long out_bytes) => MSPACK_ERR.MSPACK_ERR_OK; + + /// + /// Frees all state associated with an LZX data stream. This will call + /// system->free() using the system pointer given in lzxd_init(). + /// + /// LZX decompression state to free. + public static void lzxd_free(lzxd_stream lzx) { } + } +} \ No newline at end of file diff --git a/libmspack/lzxd_stream.cs b/libmspack/lzxd_stream.cs new file mode 100644 index 0000000..db81352 --- /dev/null +++ b/libmspack/lzxd_stream.cs @@ -0,0 +1,180 @@ +using static SabreTools.Compression.libmspack.lzx; + +namespace SabreTools.Compression.libmspack +{ + public unsafe class lzxd_stream + { + /// + /// I/O routines + /// + public mspack_system sys { get; set; } + + /// + /// Input file handle + /// + public mspack_file input { get; set; } + + /// + /// Output file handle + /// + public mspack_file output { get; set; } + + /// + /// Number of bytes actually output + /// + public long offset { get; set; } + + /// + /// Overall decompressed length of stream + /// + public long length { get; set; } + + /// + /// Decoding window + /// + public byte* window { get; set; } + + /// + /// Window size + /// + public uint window_size { get; set; } + + /// + /// LZX DELTA reference data size + /// + public uint ref_data_size { get; set; } + + /// + /// Number of match_offset entries in table + /// + public uint num_offsets { get; set; } + + /// + /// Decompression offset within window + /// + public uint window_posn { get; set; } + + /// + /// Current frame offset within in window + /// + public uint frame_posn { get; set; } + + /// + /// The number of 32kb frames processed + /// + public uint frame { get; set; } + + /// + /// Which frame do we reset the compressor? + /// + public uint reset_interval { get; set; } + + /// + /// For the LRU offset system + /// + public uint R0 { get; set; } + + /// + /// For the LRU offset system + /// + public uint R1 { get; set; } + + /// + /// For the LRU offset system + /// + public uint R2 { get; set; } + + /// + /// Uncompressed length of this LZX block + /// + public uint block_length { get; set; } + + /// + /// Uncompressed bytes still left to decode + /// + public uint block_remaining { get; set; } + + /// + /// Magic header value used for transform + /// + public int intel_filesize { get; set; } + + /// + /// Has intel E8 decoding started? + /// + public byte intel_started { get; set; } + + /// + /// Type of the current block + /// + public byte block_type { get; set; } + + /// + /// Have we started decoding at all yet? + /// + public byte header_read { get; set; } + + /// + /// Have we reached the end of input? + /// + public byte input_end { get; set; } + + /// + /// Does stream follow LZX DELTA spec? + /// + public byte is_delta { get; set; } + + public MSPACK_ERR error { get; set; } + + #region I/O buffering + + public byte* inbuf { get; set; } + + public byte* i_ptr { get; set; } + + public byte* i_end { get; set; } + + public byte* o_ptr { get; set; } + + public byte* o_end { get; set; } + + public uint bit_buffer { get; set; } + + public uint bits_left { get; set; } + + public uint inbuf_size { get; set; } + + #endregion + + #region Huffman code lengths + + public byte[] PRETREE_len { get; set; } = new byte[LZX_PRETREE_MAXSYMBOLS + LZX_LENTABLE_SAFETY]; + + public byte[] MAINTREE_len { get; set; } = new byte[LZX_MAINTREE_MAXSYMBOLS + LZX_LENTABLE_SAFETY]; + + public byte[] LENGTH_len { get; set; } = new byte[LZX_LENGTH_MAXSYMBOLS + LZX_LENTABLE_SAFETY]; + + public byte[] ALIGNED_len { get; set; } = new byte[LZX_ALIGNED_MAXSYMBOLS + LZX_LENTABLE_SAFETY]; + + #endregion + + #region Huffman decoding tables + + public ushort[] PRETREE_table { get; set; } = new ushort[(1 << LZX_PRETREE_TABLEBITS) + (LZX_PRETREE_MAXSYMBOLS * 2)]; + + public ushort[] MAINTREE_table { get; set; } = new ushort[(1 << LZX_MAINTREE_TABLEBITS) + (LZX_MAINTREE_MAXSYMBOLS * 2)]; + + public ushort[] LENGTH_table { get; set; } = new ushort[(1 << LZX_LENGTH_TABLEBITS) + (LZX_LENGTH_MAXSYMBOLS * 2)]; + + public ushort[] ALIGNED_table { get; set; } = new ushort[(1 << LZX_ALIGNED_TABLEBITS) + (LZX_ALIGNED_MAXSYMBOLS * 2)]; + + public byte LENGTH_empty { get; set; } + + #endregion + + /// + /// This is used purely for doing the intel E8 transform + /// + public byte[] e8_buf { get; set; } = new byte[LZX_FRAME_SIZE]; + } +} \ No newline at end of file