/* This file is part of libmspack. * (C) 2003-2010 Stuart Caie. * * libmspack is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License (LGPL) version 2.1 * * For further details, see the file COPYING.LIB distributed with libmspack */ using System; using System.Text; using LibMSPackSharp.Compression; namespace LibMSPackSharp.KWAJ { public class Implementation { #region Generic KWAJ Definitions private const byte kwajh_Signature1 = 0x00; private const byte kwajh_Signature2 = 0x04; private const byte kwajh_CompMethod = 0x08; private const byte kwajh_DataOffset = 0x0a; private const byte kwajh_Flags = 0x0c; private const byte kwajh_SIZEOF = 0x0e; #endregion #region KWAJ Decompression Definitions // Input buffer size during decompression - not worth parameterising IMHO private const int KWAJ_INPUT_SIZE = (2048); // Huffman codes that are 9 bits or less are decoded immediately public const int KWAJ_TABLEBITS = (9); // Number of codes in each huffman table public const int KWAJ_MATCHLEN1_SYMS = (16); public const int KWAJ_MATCHLEN2_SYMS = (16); public const int KWAJ_LITLEN_SYMS = (32); public const int KWAJ_OFFSET_SYMS = (64); public const int KWAJ_LITERAL_SYMS = (256); // Define decoding table sizes public const int KWAJ_TABLESIZE = (1 << KWAJ_TABLEBITS); public static int KWAJ_MATCHLEN1_TBLSIZE { get { if (KWAJ_TABLESIZE < (KWAJ_MATCHLEN1_SYMS * 2)) return (KWAJ_MATCHLEN1_SYMS * 4); else return (KWAJ_TABLESIZE + (KWAJ_MATCHLEN1_SYMS * 2)); } } public static int KWAJ_MATCHLEN2_TBLSIZE { get { if (KWAJ_TABLESIZE < (KWAJ_MATCHLEN2_SYMS * 2)) return (KWAJ_MATCHLEN2_SYMS * 4); else return (KWAJ_TABLESIZE + (KWAJ_MATCHLEN2_SYMS * 2)); } } public static int KWAJ_LITLEN_TBLSIZE { get { if (KWAJ_TABLESIZE < (KWAJ_LITLEN_SYMS * 2)) return (KWAJ_LITLEN_SYMS * 4); else return (KWAJ_TABLESIZE + (KWAJ_LITLEN_SYMS * 2)); } } public static int KWAJ_OFFSET_TBLSIZE { get { if (KWAJ_TABLESIZE < (KWAJ_OFFSET_SYMS * 2)) return (KWAJ_OFFSET_SYMS * 4); else return (KWAJ_TABLESIZE + (KWAJ_OFFSET_SYMS * 2)); } } public static int KWAJ_LITERAL_TBLSIZE { get { if (KWAJ_TABLESIZE < (KWAJ_LITERAL_SYMS * 2)) return (KWAJ_LITERAL_SYMS * 4); else return (KWAJ_TABLESIZE + (KWAJ_LITERAL_SYMS * 2)); } } #endregion #region KWAJD_OPEN /// /// Opens a KWAJ file without decompressing, reads header /// public static Header Open(Decompressor d, string filename) { DecompressorImpl self = d as DecompressorImpl; if (self == null) return null; SystemImpl sys = self.System; object fh = sys.Open(sys, filename, OpenMode.MSPACK_SYS_OPEN_READ); HeaderImpl hdr = new HeaderImpl(); if (fh != null && hdr != null) { hdr.FileHandle = fh; self.Error = ReadHeaders(sys, fh, hdr); } else { if (fh == null) self.Error = Error.MSPACK_ERR_OPEN; if (hdr == null) self.Error = Error.MSPACK_ERR_NOMEMORY; } if (self.Error != Error.MSPACK_ERR_OK) { if (fh != null) sys.Close(fh); sys.Free(hdr); hdr = null; } return hdr; } #endregion #region KWAJD_CLOSE /// /// Closes a KWAJ file /// public static void Close(Decompressor d, Header hdr) { DecompressorImpl self = d as DecompressorImpl; HeaderImpl hdr_p = hdr as HeaderImpl; if (self?.System == null || hdr_p == null) return; // Close the file handle associated self.System.Close(hdr_p.FileHandle); // Free the memory associated self.System.Free(hdr); self.Error = Error.MSPACK_ERR_OK; } #endregion #region KWAJD_READ_HEADERS /// /// Reads the headers of a KWAJ format file /// public static Error ReadHeaders(SystemImpl sys, object fh, Header hdr) { int i; // Read in the header byte[] buf = new byte[16]; if (sys.Read(fh, buf, 0, kwajh_SIZEOF) != kwajh_SIZEOF) { return Error.MSPACK_ERR_READ; } // Check for "KWAJ" signature if ((BitConverter.ToUInt32(buf, kwajh_Signature1) != 0x4A41574B) || (BitConverter.ToUInt32(buf, kwajh_Signature2) != 0xD127F088)) { return Error.MSPACK_ERR_SIGNATURE; } // Basic header fields hdr.CompressionType = (CompressionType)BitConverter.ToUInt16(buf, kwajh_CompMethod); hdr.DataOffset = BitConverter.ToUInt16(buf, kwajh_DataOffset); hdr.Headers = (OptionalHeaderFlag)BitConverter.ToUInt16(buf, kwajh_Flags); hdr.Length = 0; hdr.Filename = null; hdr.Extra = null; hdr.ExtraLength = 0; // Optional headers // 4 bytes: length of unpacked file if (hdr.Headers.HasFlag(OptionalHeaderFlag.MSKWAJ_HDR_HASLENGTH)) { if (sys.Read(fh, buf, 0, 4) != 4) return Error.MSPACK_ERR_READ; hdr.Length = BitConverter.ToUInt32(buf, 0); } // 2 bytes: unknown purpose if (hdr.Headers.HasFlag(OptionalHeaderFlag.MSKWAJ_HDR_HASUNKNOWN1)) { if (sys.Read(fh, buf, 0, 2) != 2) return Error.MSPACK_ERR_READ; } // 2 bytes: length of section, then [length] bytes: unknown purpose if (hdr.Headers.HasFlag(OptionalHeaderFlag.MSKWAJ_HDR_HASUNKNOWN2)) { if (sys.Read(fh, buf, 0, 2) != 2) return Error.MSPACK_ERR_READ; i = BitConverter.ToUInt16(buf, 0); if (sys.Seek(fh, i, SeekMode.MSPACK_SYS_SEEK_CUR)) return Error.MSPACK_ERR_SEEK; } // Filename and extension if (hdr.Headers.HasFlag(OptionalHeaderFlag.MSKWAJ_HDR_HASFILENAME) || hdr.Headers.HasFlag(OptionalHeaderFlag.MSKWAJ_HDR_HASFILEEXT)) { int len; // Allocate memory for maximum length filename char[] fn = new char[13]; int fnPtr = 0; // Copy filename if present if (hdr.Headers.HasFlag(OptionalHeaderFlag.MSKWAJ_HDR_HASFILENAME)) { // Read and copy up to 9 bytes of a null terminated string if ((len = sys.Read(fh, buf, 0, 9)) < 2) return Error.MSPACK_ERR_READ; for (i = 0; i < len; i++) { if ((fn[fnPtr++] = (char)buf[i]) == '\0') break; } // If string was 9 bytes with no null terminator, reject it if (i == 9 && buf[8] != '\0') return Error.MSPACK_ERR_DATAFORMAT; // Seek to byte after string ended in file if (sys.Seek(fh, i + 1 - len, SeekMode.MSPACK_SYS_SEEK_CUR)) return Error.MSPACK_ERR_SEEK; fnPtr--; // Remove the null terminator } // Copy extension if present if (hdr.Headers.HasFlag(OptionalHeaderFlag.MSKWAJ_HDR_HASFILEEXT)) { fn[fnPtr++] = '.'; // Read and copy up to 4 bytes of a null terminated string if ((len = sys.Read(fh, buf, 0, 4)) < 2) return Error.MSPACK_ERR_READ; for (i = 0; i < len; i++) { if ((fn[fnPtr++] = (char)buf[i]) == '\0') break; } // If string was 4 bytes with no null terminator, reject it if (i == 4 && buf[3] != '\0') return Error.MSPACK_ERR_DATAFORMAT; // Seek to byte after string ended in file if (sys.Seek(fh, i + 1 - len, SeekMode.MSPACK_SYS_SEEK_CUR)) return Error.MSPACK_ERR_SEEK; fnPtr--; // Remove the null terminator } fn[fnPtr] = '\0'; } // 2 bytes: extra text length then [length] bytes of extra text data if (hdr.Headers.HasFlag(OptionalHeaderFlag.MSKWAJ_HDR_HASEXTRATEXT)) { if (sys.Read(fh, buf, 0, 2) != 2) return Error.MSPACK_ERR_READ; i = BitConverter.ToUInt16(buf, 0); byte[] extra = new byte[i + 1]; if (sys.Read(fh, extra, 0, i) != i) return Error.MSPACK_ERR_READ; extra[i] = 0x00; hdr.Extra = Encoding.ASCII.GetString(extra, 0, extra.Length); hdr.ExtraLength = (ushort)i; } return Error.MSPACK_ERR_OK; } #endregion #region KWAJD_EXTRACT /// /// Decompresses a KWAJ file /// public static Error Extract(Decompressor d, Header hdr, string filename) { DecompressorImpl self = d as DecompressorImpl; if (self == null) return Error.MSPACK_ERR_ARGS; if (hdr == null) return self.Error = Error.MSPACK_ERR_ARGS; SystemImpl sys = self.System; object fh = (hdr as HeaderImpl)?.FileHandle; if (fh == null) return Error.MSPACK_ERR_ARGS; // Seek to the compressed data if (sys.Seek(fh, hdr.DataOffset, SeekMode.MSPACK_SYS_SEEK_START)) return self.Error = Error.MSPACK_ERR_SEEK; // Open file for output object outfh; if ((outfh = sys.Open(sys, filename, OpenMode.MSPACK_SYS_OPEN_WRITE)) == null) return self.Error = Error.MSPACK_ERR_OPEN; self.Error = Error.MSPACK_ERR_OK; // Decompress based on format if (hdr.CompressionType == CompressionType.MSKWAJ_COMP_NONE || hdr.CompressionType == CompressionType.MSKWAJ_COMP_XOR) { // NONE is a straight copy. XOR is a copy xored with 0xFF byte[] buf = new byte[KWAJ_INPUT_SIZE]; int read, i; while ((read = sys.Read(fh, buf, 0, KWAJ_INPUT_SIZE)) > 0) { if (hdr.CompressionType == CompressionType.MSKWAJ_COMP_XOR) { for (i = 0; i < read; i++) { buf[i] ^= 0xFF; } } if (sys.Write(outfh, buf, 0, read) != read) { self.Error = Error.MSPACK_ERR_WRITE; break; } } if (read < 0) self.Error = Error.MSPACK_ERR_READ; sys.Free(buf); } else if (hdr.CompressionType == CompressionType.MSKWAJ_COMP_SZDD) { self.Error = LZSS.Decompress(sys, fh, outfh, KWAJ_INPUT_SIZE, LZSSMode.LZSS_MODE_EXPAND); } else if (hdr.CompressionType == CompressionType.MSKWAJ_COMP_LZH) { InternalStream lzh = LZHInit(sys, fh, outfh); self.Error = (lzh != null) ? LZHDecompress(lzh) : Error.MSPACK_ERR_NOMEMORY; LZHFree(lzh); } else if (hdr.CompressionType == CompressionType.MSKWAJ_COMP_MSZIP) { MSZIPDStream zip = MSZIP.Init(sys, fh, outfh, KWAJ_INPUT_SIZE, false); self.Error = (zip != null) ? MSZIP.DecompressKWAJ(zip) : Error.MSPACK_ERR_NOMEMORY; MSZIP.Free(zip); } else { self.Error = Error.MSPACK_ERR_DATAFORMAT; } // Close output file sys.Close(outfh); return self.Error; } #endregion #region KWAJD_DECOMPRESS /// /// Unpacks directly from input to output /// public static Error Decompress(Decompressor d, string input, string output) { DecompressorImpl self = d as DecompressorImpl; if (self == null) return Error.MSPACK_ERR_ARGS; Header hdr; if ((hdr = Open(d, input)) == null) return self.Error; Error error = Extract(d, hdr, output); Close(d, hdr); return self.Error = error; } #endregion #region KWAJD_ERROR /// /// Returns the last error that occurred /// public static Error LastError(Decompressor d) { DecompressorImpl self = d as DecompressorImpl; return (self != null) ? self.Error : Error.MSPACK_ERR_ARGS; } #endregion #region LZH_INIT, LZH_DECOMPRESS, LZH_FREE /* In the KWAJ LZH format, there is no special 'eof' marker, it just * ends. Depending on how many bits are left in the final byte when * the stream ends, that might be enough to start another literal or * match. The only easy way to detect that we've come to an end is to * guard all bit-reading. We allow fake bits to be read once we reach * the end of the stream, but we check if we then consumed any of * those fake bits, after doing the READ_BITS / READ_HUFFSYM. This * isn't how the default readbits.h read_input() works (it simply lets * 2 fake bytes in then stops), so we implement our own. */ private static InternalStream LZHInit(SystemImpl sys, object input, object output) { if (sys == null || input == null || output == null) return null; return new InternalStream() { Sys = sys, Input = input, Output = output, }; } private static Error LZHDecompress(InternalStream lzh) { uint bit_buffer = 0, bits_left = 0, len = 0, j = 0; int i; ushort sym = 0; int i_ptr = 0, i_end = 0; bool lit_run = false; int pos = 0, offset; int[] types = new int[6]; // Reset global state lzh.INIT_BITS(); lzh.RESTORE_BITS(ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); for (i = 0; i < LZSS.LZSS_WINDOW_SIZE; i++) { lzh.Window[i] = LZSS.LZSS_WINDOW_FILL; } // Read 6 encoding types (for byte alignment) but only 5 are needed for (i = 0; i < 6; i++) { //READ_BITS_SAFE(val, n) lzh.READ_BITS(ref types[i], 4, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); if (lzh.InputEnd != 0 && bits_left < lzh.InputEnd) return Error.MSPACK_ERR_OK; } // Read huffman table symbol lengths and build huffman trees //BUILD_TREE(tbl, type) lzh.STORE_BITS(i_ptr, i_end, bit_buffer, bits_left); Error err = LZHReadLens(lzh, (uint)types[0], KWAJ_MATCHLEN1_SYMS, lzh.MATCHLEN1_len); if (err != Error.MSPACK_ERR_OK) return err; lzh.RESTORE_BITS(ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); if (InternalStream.MakeDecodeTable(KWAJ_MATCHLEN1_SYMS, (uint)KWAJ_MATCHLEN1_TBLSIZE, lzh.MATCHLEN1_len, lzh.MATCHLEN1_table) != 0) return Error.MSPACK_ERR_DATAFORMAT; //BUILD_TREE(tbl, type) lzh.STORE_BITS(i_ptr, i_end, bit_buffer, bits_left); err = LZHReadLens(lzh, (uint)types[1], KWAJ_MATCHLEN2_SYMS, lzh.MATCHLEN2_len); if (err != Error.MSPACK_ERR_OK) return err; lzh.RESTORE_BITS(ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); if (InternalStream.MakeDecodeTable(KWAJ_MATCHLEN2_SYMS, (uint)KWAJ_MATCHLEN2_TBLSIZE, lzh.MATCHLEN2_len, lzh.MATCHLEN2_table) != 0) return Error.MSPACK_ERR_DATAFORMAT; //BUILD_TREE(tbl, type) lzh.STORE_BITS(i_ptr, i_end, bit_buffer, bits_left); err = LZHReadLens(lzh, (uint)types[2], KWAJ_LITLEN_SYMS, lzh.LITLEN_len); if (err != Error.MSPACK_ERR_OK) return err; lzh.RESTORE_BITS(ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); if (InternalStream.MakeDecodeTable(KWAJ_LITLEN_SYMS, (uint)KWAJ_LITLEN_TBLSIZE, lzh.LITLEN_len, lzh.LITLEN_table) != 0) return Error.MSPACK_ERR_DATAFORMAT; //BUILD_TREE(tbl, type) lzh.STORE_BITS(i_ptr, i_end, bit_buffer, bits_left); err = LZHReadLens(lzh, (uint)types[3], KWAJ_OFFSET_SYMS, lzh.OFFSET_len); if (err != Error.MSPACK_ERR_OK) return err; lzh.RESTORE_BITS(ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); if (InternalStream.MakeDecodeTable(KWAJ_OFFSET_SYMS, (uint)KWAJ_OFFSET_TBLSIZE, lzh.OFFSET_len, lzh.OFFSET_table) != 0) return Error.MSPACK_ERR_DATAFORMAT; //BUILD_TREE(tbl, type) lzh.STORE_BITS(i_ptr, i_end, bit_buffer, bits_left); err = LZHReadLens(lzh, (uint)types[4], KWAJ_LITERAL_SYMS, lzh.LITERAL_len); if (err != Error.MSPACK_ERR_OK) return err; lzh.RESTORE_BITS(ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); if (InternalStream.MakeDecodeTable(KWAJ_LITERAL_SYMS, (uint)KWAJ_LITERAL_TBLSIZE, lzh.LITERAL_len, lzh.LITERAL_table) != 0) return Error.MSPACK_ERR_DATAFORMAT; while (lzh.InputEnd == 0) { if (lit_run) { //READ_HUFFSYM_SAFE(tbl, val) lzh.READ_HUFFSYM(lzh.MATCHLEN2_table, ref len, KWAJ_MATCHLEN2_TBLSIZE, lzh.MATCHLEN2_len, KWAJ_MATCHLEN2_SYMS, ref i, ref sym, ref i_ptr, ref i_end, ref bits_left, ref bit_buffer); if (lzh.InputEnd != 0 && bits_left < lzh.InputEnd) return Error.MSPACK_ERR_OK; } else { //READ_HUFFSYM_SAFE(tbl, val) lzh.READ_HUFFSYM(lzh.MATCHLEN1_table, ref len, KWAJ_MATCHLEN1_TBLSIZE, lzh.MATCHLEN1_len, KWAJ_MATCHLEN1_SYMS, ref i, ref sym, ref i_ptr, ref i_end, ref bits_left, ref bit_buffer); if (lzh.InputEnd != 0 && bits_left < lzh.InputEnd) return Error.MSPACK_ERR_OK; } if (len > 0) { len += 2; lit_run = false; // Not the end of a literal run //READ_HUFFSYM_SAFE(tbl, val) lzh.READ_HUFFSYM(lzh.OFFSET_table, ref j, KWAJ_OFFSET_TBLSIZE, lzh.OFFSET_len, KWAJ_OFFSET_SYMS, ref i, ref sym, ref i_ptr, ref i_end, ref bits_left, ref bit_buffer); if (lzh.InputEnd != 0 && bits_left < lzh.InputEnd) return Error.MSPACK_ERR_OK; offset = (int)(j << 6); //READ_BITS_SAFE(val, n) int tempj = (int)j; lzh.READ_BITS(ref tempj, 6, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); if (lzh.InputEnd != 0 && bits_left < lzh.InputEnd) return Error.MSPACK_ERR_OK; j = (uint)tempj; offset |= tempj; // Copy match as output and into the ring buffer while (len-- > 0) { lzh.Window[pos] = lzh.Window[(pos + 4096 - offset) & 4095]; if (lzh.Sys.Write(lzh.Output, lzh.Window, pos, 1) != 1) return Error.MSPACK_ERR_WRITE; pos++; pos &= 4095; } } else { //READ_HUFFSYM_SAFE(tbl, val) lzh.READ_HUFFSYM(lzh.LITLEN_table, ref len, KWAJ_LITLEN_TBLSIZE, lzh.LITLEN_len, KWAJ_LITLEN_SYMS, ref i, ref sym, ref i_ptr, ref i_end, ref bits_left, ref bit_buffer); if (lzh.InputEnd != 0 && bits_left < lzh.InputEnd) return Error.MSPACK_ERR_OK; len++; lit_run = (len == 32) ? false : true; // End of a literal run? while (len-- > 0) { //READ_HUFFSYM_SAFE(tbl, val) lzh.READ_HUFFSYM(lzh.LITERAL_table, ref j, KWAJ_LITERAL_TBLSIZE, lzh.LITERAL_len, KWAJ_LITERAL_SYMS, ref i, ref sym, ref i_ptr, ref i_end, ref bits_left, ref bit_buffer); if (lzh.InputEnd != 0 && bits_left < lzh.InputEnd) return Error.MSPACK_ERR_OK; // Copy as output and into the ring buffer lzh.Window[pos] = (byte)j; if (lzh.Sys.Write(lzh.Output, lzh.Window, pos, 1) != 1) return Error.MSPACK_ERR_WRITE; pos++; pos &= 4095; } } } return Error.MSPACK_ERR_OK; } private static void LZHFree(InternalStream lzh) { if (lzh == null || lzh.Sys == null) return; SystemImpl sys = lzh.Sys; sys.Free(lzh); } public static Error LZHReadLens(InternalStream lzh, uint type, uint numsyms, byte[] lens) { uint bit_buffer = 0, bits_left = 0; int i_ptr = 0, i_end = 0; uint i; int c = 0, sel = 0; lzh.RESTORE_BITS(ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); switch (type) { case 0: i = numsyms; c = (i == 16) ? 4 : (i == 32) ? 5 : (i == 64) ? 6 : (i == 256) ? 8 : 0; for (i = 0; i < numsyms; i++) { lens[i] = (byte)c; } break; case 1: //READ_BITS_SAFE(val, n) lzh.READ_BITS(ref c, 4, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); if (lzh.InputEnd != 0 && bits_left < lzh.InputEnd) return Error.MSPACK_ERR_OK; lens[0] = (byte)c; for (i = 1; i < numsyms; i++) { //READ_BITS_SAFE(val, n) lzh.READ_BITS(ref sel, 1, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); if (lzh.InputEnd != 0 && bits_left < lzh.InputEnd) return Error.MSPACK_ERR_OK; if (sel == 0) { lens[i] = (byte)c; } else { //READ_BITS_SAFE(val, n) lzh.READ_BITS(ref sel, 1, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); if (lzh.InputEnd != 0 && bits_left < lzh.InputEnd) return Error.MSPACK_ERR_OK; if (sel == 0) { lens[i] = (byte)++c; } else { //READ_BITS_SAFE(val, n) lzh.READ_BITS(ref c, 4, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); if (lzh.InputEnd != 0 && bits_left < lzh.InputEnd) return Error.MSPACK_ERR_OK; lens[i] = (byte)c; } } } break; case 2: //READ_BITS_SAFE(val, n) lzh.READ_BITS(ref c, 4, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); if (lzh.InputEnd != 0 && bits_left < lzh.InputEnd) return Error.MSPACK_ERR_OK; lens[0] = (byte)c; for (i = 1; i < numsyms; i++) { //READ_BITS_SAFE(val, n) lzh.READ_BITS(ref sel, 2, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); if (lzh.InputEnd != 0 && bits_left < lzh.InputEnd) return Error.MSPACK_ERR_OK; if (sel == 3) { //READ_BITS_SAFE(val, n) lzh.READ_BITS(ref c, 4, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); if (lzh.InputEnd != 0 && bits_left < lzh.InputEnd) return Error.MSPACK_ERR_OK; } else { c += (char)sel - 1; } lens[i] = (byte)c; } break; case 3: for (i = 0; i < numsyms; i++) { //READ_BITS_SAFE(val, n) lzh.READ_BITS(ref c, 4, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); if (lzh.InputEnd != 0 && bits_left < lzh.InputEnd) return Error.MSPACK_ERR_OK; lens[i] = (byte)c; } break; } lzh.STORE_BITS(i_ptr, i_end, bit_buffer, bits_left); return Error.MSPACK_ERR_OK; } public static Error LZHReadInput(InternalStream lzh) { int read; if (lzh.InputEnd != 0) { lzh.InputEnd += 8; lzh.InputBuffer[0] = 0; read = 1; } else { read = lzh.Sys.Read(lzh.Input, lzh.InputBuffer, 0, KWAJ_INPUT_SIZE); if (read < 0) return Error.MSPACK_ERR_READ; if (read == 0) { lzh.InputLength = 8; lzh.InputBuffer[0] = 0; read = 1; } } // Update InputPointer and InputLength lzh.InputPointer = 0; lzh.InputLength = read; return Error.MSPACK_ERR_OK; } #endregion } }