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