mirror of
https://github.com/SabreTools/BinaryObjectScanner.git
synced 2026-02-15 05:36:03 +00:00
775 lines
28 KiB
C#
775 lines
28 KiB
C#
/* 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
|
|
|
|
/// <summary>
|
|
/// Opens a KWAJ file without decompressing, reads header
|
|
/// </summary>
|
|
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
|
|
|
|
/// <summary>
|
|
/// Closes a KWAJ file
|
|
/// </summary>
|
|
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
|
|
|
|
/// <summary>
|
|
/// Reads the headers of a KWAJ format file
|
|
/// </summary>
|
|
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
|
|
|
|
/// <summary>
|
|
/// Decompresses a KWAJ file
|
|
/// </summary>
|
|
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
|
|
|
|
/// <summary>
|
|
/// Unpacks directly from input to output
|
|
/// </summary>
|
|
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
|
|
|
|
/// <summary>
|
|
/// Returns the last error that occurred
|
|
/// </summary>
|
|
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
|
|
}
|
|
}
|