Attempts at changes

This commit is contained in:
Matt Nadareski
2022-05-25 21:55:36 -07:00
parent 20d90d6a60
commit 768c77a6bc
7 changed files with 675 additions and 156 deletions

View File

@@ -321,49 +321,72 @@ namespace LibMSPackSharp.CAB
}
}
// Close existing file handles
System.Close(State?.InputFileHandle);
// Close existing output file handle in case of error
System.Close(State?.OutputFileHandle);
// Allocate generic decompression state
State = new DecompressState()
if (State == null)
{
Folder = file.Folder,
Data = file.Folder.Data,
Offset = 0,
Block = 0,
Outlen = 0,
State = new DecompressState()
{
Folder = null,
Data = null,
System = System,
DecompressorState = null,
InputFileHandle = null,
InputCabinet = null,
};
System = System,
State.System.Read = SysRead;
State.System.Write = SysWrite;
}
InputCabinet = file.Folder.Data.Cab,
InputFileHandle = System.Open(file.Folder.Data.Cab.Filename, OpenMode.MSPACK_SYS_OPEN_READ),
OutputFileHandle = null, // Required for skipping existing data
InputPointer = 0,
InputEnd = 0,
};
// Do we need to change folder or reset the current folder?
if (State.Folder != file.Folder || State.Offset > file.Header.FolderOffset || State.DecompressorState == null)
{
// Do we need to open a new cab file?
if (State.InputFileHandle == null || file.Folder.Data.Cab != State.InputCabinet)
{
// Close previous file handle if from a different cab
System.Close(State?.InputFileHandle);
State.System.Read = SysRead;
State.System.Write = SysWrite;
State.InputCabinet = file.Folder.Data.Cab;
State.InputFileHandle = System.Open(file.Folder.Data.Cab.Filename, OpenMode.MSPACK_SYS_OPEN_READ);
if (State.InputFileHandle == null)
return Error = Error.MSPACK_ERR_OPEN;
}
if (State.InputFileHandle == null)
return Error = Error.MSPACK_ERR_OPEN;
// Seek to start of data blocks
// TODO: For LZX, they seem to start 8 bytes after the offset for some reason?
if (!System.Seek(State.InputFileHandle, file.Folder.Data.Offset, SeekMode.MSPACK_SYS_SEEK_START))
return Error = Error.MSPACK_ERR_SEEK;
// Seek to start of data blocks
if (!System.Seek(State.InputFileHandle, file.Folder.Data.Offset, SeekMode.MSPACK_SYS_SEEK_START))
return Error = Error.MSPACK_ERR_SEEK;
// Set up decompressor
if (InitDecompressionState(file.Folder.Header.CompType) != Error.MSPACK_ERR_OK)
return Error;
// Set up decompressor
if (InitDecompressionState(file.Folder.Header.CompType) != Error.MSPACK_ERR_OK)
return Error;
// Initialize new folder state
State.Folder = file.Folder;
State.Data = file.Folder.Data;
State.Offset = 0;
State.Block = 0;
State.Outlen = 0;
State.InputPointer = 0; // State.Input[0]
State.InputEnd = 0; // State.Input[0]
// ReadError lasts for the lifetime of a decompressor
ReadError = Error.MSPACK_ERR_OK;
}
// ReadError lasts for the lifetime of a decompressor
ReadError = Error.MSPACK_ERR_OK;
Error = Error.MSPACK_ERR_OK;
// If file has more than 0 bytes
if (filelen != 0)
{
// Set the output file handle to null
State.OutputFileHandle = null;
UpdateDecompressionState();
// Get to correct offset.
// - Use null fh to say 'no writing' to cabd_sys_write()
// - If SysRead() has an error, it will set self.ReadError
@@ -391,6 +414,7 @@ namespace LibMSPackSharp.CAB
// Close output file
System.Close(State.OutputFileHandle);
State.OutputFileHandle = null;
return Error;
}

View File

@@ -109,5 +109,4 @@ namespace LibMSPackSharp.CAB
/// </summary>
MSCABD_PARAM_SALVAGE = 3,
}
}

View File

@@ -11,6 +11,7 @@
*/
using System;
using System.Linq;
using static LibMSPackSharp.Compression.Constants;
namespace LibMSPackSharp.Compression
@@ -168,7 +169,7 @@ namespace LibMSPackSharp.Compression
/// </summary>
public void INJECT_BITS_MSB(int bitdata, int nbits)
{
BitBuffer |= (uint)(bitdata << (BITBUF_WIDTH - nbits - BitsLeft));
BitBuffer |= ((uint)bitdata << (BITBUF_WIDTH - nbits - BitsLeft));
BitsLeft += nbits;
}

View File

@@ -77,11 +77,11 @@ namespace LibMSPackSharp.Compression
public static bool MakeDecodeTableMSB(int nsyms, int nbits, byte[] length, ushort[] table)
{
ushort sym, next_symbol;
uint leaf, fill;
long leaf, fill;
byte bit_num;
uint pos = 0; // The current position in the decode table
uint table_mask = (uint)1 << nbits;
uint bit_mask = table_mask >> 1; // Don't do 0 length codes
long pos = 0; // The current position in the decode table
long table_mask = 1 << nbits;
long bit_mask = table_mask >> 1; // Don't do 0 length codes
// Fill entries for codes short enough for a direct mapping
for (bit_num = 1; bit_num <= nbits; bit_num++)
@@ -116,7 +116,7 @@ namespace LibMSPackSharp.Compression
}
// next_symbol = base of allocation for long codes
next_symbol = ((table_mask >> 1) < nsyms) ? (ushort)nsyms : (ushort)(table_mask >> 1);
next_symbol = (ushort)(((table_mask >> 1) < nsyms) ? nsyms : (table_mask >> 1));
// Give ourselves room for codes to grow by up to 16 more bits.
// codes now start at bit nbits+16 and end at (nbits+16-codelength)

View File

@@ -76,6 +76,7 @@
using System;
using System.IO;
using System.Linq;
using static LibMSPackSharp.Compression.Constants;
namespace LibMSPackSharp.Compression
@@ -189,7 +190,6 @@ namespace LibMSPackSharp.Compression
OutputPointer = 0,
OutputEnd = 0,
OutputIsE8 = true,
};
lzx.ResetState();
@@ -282,13 +282,530 @@ namespace LibMSPackSharp.Compression
/// should be considered unusable and lzxd_decompress() should not be
/// called again on this stream.
/// </summary>
/// <param name="o">LZX decompression state, as allocated by lzxd_init().</param>
/// <param name="out_bytes">the number of bytes of data to decompress.</param>
/// <returns>an error code, or MSPACK_ERR_OK if successful</returns>
public Error Decompress(long out_bytes)
{
int warned = 0;
int match_length, length_footer, extra, verbatim_bits, bytes_todo;
int this_run, main_element, aligned_bits, j, warned = 0;
byte[] buf = new byte[12];
int runsrc, rundest;
int frame_size = 0, end_frame, match_offset;
// Easy answers
if (out_bytes < 0)
return Error.MSPACK_ERR_ARGS;
if (Error != Error.MSPACK_ERR_OK)
return Error;
// Flush out any stored-up bytes before we begin
int i = OutputEnd - OutputPointer;
if (i > out_bytes)
i = (int)out_bytes;
if (i != 0)
{
if (System.Write(OutputFileHandle, IntelStarted ? E8Buffer : Window, OutputPointer, i) != i)
return Error = Error.MSPACK_ERR_WRITE;
OutputPointer += i;
Offset += i;
out_bytes -= i;
}
if (out_bytes == 0)
return Error.MSPACK_ERR_OK;
end_frame = (int)((uint)((Offset + out_bytes) / LZX_FRAME_SIZE) + 1);
while (Frame < end_frame)
{
// Have we reached the reset interval? (if there is one?)
if (ResetInterval != 0 && ((Frame & ResetInterval) == 0))
{
if (BlockRemaining != 0)
{
// This is a file format error, we can make a best effort to extract what we can
Console.WriteLine($"{BlockRemaining} bytes remaining at reset interval");
if (warned == 0)
{
System.Message(null, "WARNING; invalid reset interval detected during LZX decompression");
warned++;
}
}
// Re-read the intel header and reset the huffman lengths
ResetState();
}
// LZX DELTA format has chunk_size, not present in LZX format
if (IsDelta)
{
ENSURE_BITS(16);
REMOVE_BITS_MSB(16);
}
// Read header if necessary
if (HeaderRead == 0)
{
// Read 1 bit. If bit=0, intel filesize = 0.
// If bit=1, read intel filesize (32 bits)
j = 0;
i = (int)READ_BITS_MSB(1);
if (i != 0)
{
i = (int)READ_BITS_MSB(16);
j = (int)READ_BITS_MSB(16);
}
IntelFileSize = (i << 16) | j;
HeaderRead = 1;
}
// Calculate size of frame: all frames are 32k except the final frame
// which is 32kb or less. this can only be calculated when lzx->length
// has been filled in.
frame_size = LZX_FRAME_SIZE;
if (Length != 0 && (Length - Offset) < frame_size)
frame_size = (int)(Length - Offset);
// Decode until one more frame is available
bytes_todo = (int)(FramePosition + frame_size - WindowPosition);
while (bytes_todo > 0)
{
// Initialise new block, if one is needed
if (BlockRemaining == 0)
{
// Realign if previous block was an odd-sized UNCOMPRESSED block
if (BlockType == LZXBlockType.LZX_BLOCKTYPE_UNCOMPRESSED && (BlockLength & 1) != 0)
{
READ_IF_NEEDED();
InputPointer++;
}
// Read block type (3 bits) and block length (24 bits)
BlockType = (LZXBlockType)READ_BITS_MSB(3);
i = (int)READ_BITS_MSB(16);
j = (int)READ_BITS_MSB(8);
BlockRemaining = BlockLength = (i << 8) | j;
Console.WriteLine($"new block t{BlockType} len {BlockLength}");
// Read individual block headers
switch (BlockType)
{
case LZXBlockType.LZX_BLOCKTYPE_ALIGNED:
// Read lengths of and build aligned huffman decoding tree
for (i = 0; i < 8; i++)
{
j = (int)READ_BITS_MSB(3);
ALIGNED_len[i] = (byte)j;
}
// Rest of aligned header is same as verbatim
// Read lengths of and build main huffman decoding tree
READ_LENGTHS(MAINTREE_len, 0, 256);
READ_LENGTHS(MAINTREE_len, 256, LZX_NUM_CHARS + NumOffsets);
BUILD_TABLE(MAINTREE_table, MAINTREE_len, LZX_MAINTREE_TABLEBITS, LZX_MAINTREE_MAXSYMBOLS);
// If the literal 0xE8 is anywhere in the block...
if (MAINTREE_len[0xE8] != 0)
IntelStarted = true;
// Read lengths of and build lengths huffman decoding tree
READ_LENGTHS(LENGTH_len, 0, LZX_NUM_SECONDARY_LENGTHS);
BUILD_TABLE_MAYBE_EMPTY();
break;
case LZXBlockType.LZX_BLOCKTYPE_VERBATIM:
// Read lengths of and build main huffman decoding tree
READ_LENGTHS(MAINTREE_len, 0, 256);
READ_LENGTHS(MAINTREE_len, 256, LZX_NUM_CHARS + NumOffsets);
BUILD_TABLE(MAINTREE_table, MAINTREE_len, LZX_MAINTREE_TABLEBITS, LZX_MAINTREE_MAXSYMBOLS);
// If the literal 0xE8 is anywhere in the block...
if (MAINTREE_len[0xE8] != 0)
IntelStarted = true;
// Read lengths of and build lengths huffman decoding tree
READ_LENGTHS(LENGTH_len, 0, LZX_NUM_SECONDARY_LENGTHS);
BUILD_TABLE_MAYBE_EMPTY();
break;
case LZXBlockType.LZX_BLOCKTYPE_UNCOMPRESSED:
// Because we can't assume otherwise
IntelStarted = true;
// Read 1-16 (not 0-15) bits to align to bytes
if (BitsLeft == 0)
ENSURE_BITS(16);
BitsLeft = 0;
BitBuffer = 0;
// Read 12 bytes of stored R0 / R1 / R2 values
for (rundest = 0, i = 0; i < 12; i++)
{
READ_IF_NEEDED();
buf[rundest++] = InputBuffer[InputPointer++];
}
R[0] = (uint)(buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24));
R[1] = (uint)(buf[4] | (buf[5] << 8) | (buf[6] << 16) | (buf[7] << 24));
R[2] = (uint)(buf[8] | (buf[9] << 8) | (buf[10] << 16) | (buf[11] << 24));
break;
default:
Console.WriteLine($"Bad block type {BlockType}");
return Error = Error.MSPACK_ERR_DECRUNCH;
}
}
// Decode more of the block:
// run = min(what's available, what's needed)
this_run = BlockRemaining;
if (this_run > bytes_todo)
this_run = bytes_todo;
// Assume we decode exactly this_run bytes, for now
bytes_todo -= this_run;
BlockRemaining -= this_run;
// Decode at least this_run bytes
switch (BlockType)
{
case LZXBlockType.LZX_BLOCKTYPE_ALIGNED:
case LZXBlockType.LZX_BLOCKTYPE_VERBATIM:
while (this_run > 0)
{
main_element = (int)READ_HUFFSYM_MSB(MAINTREE_table, MAINTREE_len, LZX_MAINTREE_TABLEBITS, LZX_MAINTREE_MAXSYMBOLS);
if (main_element < LZX_NUM_CHARS)
{
// Literal: 0 to LZX_NUM_CHARS-1
Window[WindowPosition++] = (byte)main_element;
this_run--;
}
else
{
// Match: LZX_NUM_CHARS ((slot<<3) | length_header (3 bits))
main_element -= LZX_NUM_CHARS;
// Get match length
match_length = main_element & LZX_NUM_PRIMARY_LENGTHS;
if (match_length == LZX_NUM_PRIMARY_LENGTHS)
{
if (LENGTH_empty != 0)
{
Console.WriteLine("LENGTH symbol needed but tree is empty");
return Error = Error.MSPACK_ERR_DECRUNCH;
}
length_footer = (int)READ_HUFFSYM_MSB(LENGTH_table, LENGTH_len, LZX_LENGTH_TABLEBITS, LZX_LENGTH_MAXSYMBOLS);
match_length += length_footer;
}
match_length += LZX_MIN_MATCH;
// Get match offset
switch ((match_offset = (main_element >> 3)))
{
case 0:
match_offset = (int)R[0];
break;
case 1:
match_offset = (int)R[1];
R[1] = R[0];
R[0] = (uint)match_offset;
break;
case 2:
match_offset = (int)R[2];
R[2] = R[0];
R[0] = (uint)match_offset;
break;
default:
if (BlockType == LZXBlockType.LZX_BLOCKTYPE_VERBATIM)
{
if (match_offset == 3)
{
match_offset = 1;
}
else
{
extra = (match_offset >= 36) ? 17 : LZXExtraBits[match_offset];
verbatim_bits = (int)READ_BITS_MSB(extra);
match_offset = (int)(LZXPositionBase[match_offset] - 2 + verbatim_bits);
}
}
else // LZX_BLOCKTYPE_ALIGNED
{
extra = (match_offset >= 36) ? 17 : LZXExtraBits[match_offset];
match_offset = (int)(LZXPositionBase[match_offset] - 2);
if (extra > 3) // >3: verbatim and aligned bits
{
extra -= 3;
verbatim_bits = (int)READ_BITS_MSB(extra);
match_offset += (verbatim_bits << 3);
aligned_bits = (int)READ_HUFFSYM_MSB(ALIGNED_table, ALIGNED_len, LZX_ALIGNED_TABLEBITS, LZX_ALIGNED_MAXSYMBOLS);
match_offset += aligned_bits;
}
else if (extra == 3) // 3: aligned bits only
{
aligned_bits = (int)READ_HUFFSYM_MSB(ALIGNED_table, ALIGNED_len, LZX_ALIGNED_TABLEBITS, LZX_ALIGNED_MAXSYMBOLS);
match_offset += aligned_bits;
}
else if (extra > 0) // 1-2: verbatim bits only
{
verbatim_bits = (int)READ_BITS_MSB(extra);
match_offset += verbatim_bits;
}
else // 0: not defined in LZX specification!
{
match_offset = 1;
}
}
// Update repeated offset LRU queue
R[2] = R[1];
R[1] = R[0];
R[0] = (uint)match_offset;
break;
}
// LZX DELTA uses max match length to signal even longer match
if (match_length == LZX_MAX_MATCH && IsDelta)
{
int extra_len = 0;
ENSURE_BITS(3); // 4 entry huffman tree
if (PEEK_BITS_MSB(1) == 0)
{
REMOVE_BITS_MSB(1); // '0' -> 8 extra length bits
extra_len = (int)READ_BITS_MSB(8);
}
else if (PEEK_BITS_MSB(2) == 2)
{
REMOVE_BITS_MSB(2); // '10' -> 10 extra length bits + 0x100
extra_len = (int)READ_BITS_MSB(10);
extra_len += 0x100;
}
else if (PEEK_BITS_MSB(3) == 6)
{
REMOVE_BITS_MSB(3); // '110' -> 12 extra length bits + 0x500
extra_len = (int)READ_BITS_MSB(12);
extra_len += 0x500;
}
else
{
REMOVE_BITS_MSB(3); // '111' -> 15 extra length bits
extra_len = (int)READ_BITS_MSB(15);
}
match_length += extra_len;
}
if ((WindowPosition + match_length) > WindowSize)
{
Console.WriteLine("Match ran over window wrap");
return Error = Error.MSPACK_ERR_DECRUNCH;
}
// Copy match
rundest = WindowPosition;
i = match_length;
// Does match offset wrap the window?
if (match_offset > WindowPosition)
{
if (match_offset > Offset && (match_offset - WindowPosition) > ReferenceDataSize)
{
Console.WriteLine("Match offset beyond LZX stream");
return Error = Error.MSPACK_ERR_DECRUNCH;
}
// j = length from match offset to end of window
j = match_offset - WindowPosition;
if (j > (int)WindowSize)
{
Console.WriteLine("Match offset beyond window boundaries");
return Error = Error.MSPACK_ERR_DECRUNCH;
}
runsrc = (int)(WindowSize - j);
if (j < i)
{
// If match goes over the window edge, do two copy runs
i -= j;
while (j-- > 0)
{
Window[rundest++] = Window[runsrc++];
}
runsrc = 0;
}
while (i-- > 0)
{
Window[rundest++] = Window[runsrc++];
}
}
else
{
runsrc = rundest - match_offset;
while (i-- > 0)
{
Window[rundest++] = Window[runsrc++];
}
}
this_run -= match_length;
WindowPosition += match_length;
}
}
break;
case LZXBlockType.LZX_BLOCKTYPE_UNCOMPRESSED:
// As this_run is limited not to wrap a frame, this also means it
// won't wrap the window (as the window is a multiple of 32k)
rundest = WindowPosition;
WindowPosition += this_run;
while (this_run > 0)
{
if ((i = InputEnd - InputPointer) == 0)
{
READ_IF_NEEDED();
}
else
{
if (i > this_run)
i = this_run;
Array.Copy(InputBuffer, InputPointer, Window, rundest, i);
rundest += 1;
InputPointer += i;
this_run -= i;
}
}
break;
default:
return Error = Error.MSPACK_ERR_DECRUNCH; // Might as well
}
// Did the final match overrun our desired this_run length?
if (this_run < 0)
{
if ((uint)(-this_run) > BlockRemaining)
{
Console.Write($"Overrun went past end of block by {-this_run} ({BlockRemaining} remaining)");
return Error = Error.MSPACK_ERR_DECRUNCH;
}
BlockRemaining -= -this_run;
}
}
// Streams don't extend over frame boundaries
if ((WindowPosition - FramePosition) != frame_size)
{
Console.WriteLine($"Decode beyond output frame limits {WindowPosition - FramePosition} != {frame_size}");
return Error = Error.MSPACK_ERR_DECRUNCH;
}
// Re-align input bitstream
if (BitsLeft > 0)
ENSURE_BITS(16);
if ((BitsLeft & 15) != 0)
REMOVE_BITS_MSB(BitsLeft & 15);
// Check that we've used all of the previous frame first
if (OutputPointer != OutputEnd)
{
Console.Write($"{OutputEnd - OutputPointer} avail bytes, new {frame_size} frame");
return Error = Error.MSPACK_ERR_DECRUNCH;
}
// Does this intel block _really_ need decoding?
if (IntelStarted && IntelFileSize != 0 && Frame < 32768 && frame_size > 10)
{
int data = 0;
int dataend = frame_size - 10;
int curpos = (int)Offset;
int filesize = IntelFileSize;
int abs_off, rel_off;
// Copy E8 block to the e8 buffer and tweak if needed
OutputPointer = 0;
Array.Copy(Window, FramePosition, E8Buffer, data, frame_size);
while (data < dataend)
{
if (E8Buffer[data++] != 0xE8)
{
curpos++;
continue;
}
abs_off = E8Buffer[data + 0] | (E8Buffer[data + 1] << 8) | (E8Buffer[data + 2] << 16) | (E8Buffer[data + 3] << 24);
if ((abs_off >= -curpos) && (abs_off < filesize))
{
rel_off = (abs_off >= 0) ? abs_off - curpos : abs_off + filesize;
E8Buffer[data + 0] = (byte)rel_off;
E8Buffer[data + 1] = (byte)(rel_off >> 8);
E8Buffer[data + 2] = (byte)(rel_off >> 16);
E8Buffer[data + 3] = (byte)(rel_off >> 24);
}
data += 4;
curpos += 5;
}
}
else
{
IntelStarted = false;
OutputPointer = (int)FramePosition;
}
OutputEnd = frame_size;
// Write a frame
i = (int)(out_bytes < frame_size ? out_bytes : frame_size);
if (System.Write(OutputFileHandle, IntelStarted ? E8Buffer : Window, OutputPointer, i) != i)
return Error = Error.MSPACK_ERR_WRITE;
OutputPointer += i;
Offset += i;
out_bytes -= i;
// Advance frame start position
FramePosition += (uint)frame_size;
Frame++;
// Wrap window / frame position pointers
if (WindowPosition == WindowSize)
WindowPosition = 0;
if (FramePosition == WindowSize)
FramePosition = 0;
}
if (out_bytes != 0)
{
Console.WriteLine($"{out_bytes} bytes left to output");
return Error = Error.MSPACK_ERR_DECRUNCH;
}
return Error.MSPACK_ERR_OK;
}
/// <summary>
/// Use the cleaned up code for decompression based on wimlib
/// </summary>
public Error DecompressNew(long out_bytes)
{
int warned = 0;
// Easy answers
if (out_bytes < 0)
@@ -304,7 +821,7 @@ namespace LibMSPackSharp.Compression
if (leftover_bytes != 0)
{
try { System.Write(OutputFileHandle, OutputIsE8 ? E8Buffer : Window, OutputPointer, leftover_bytes); }
try { System.Write(OutputFileHandle, Window, OutputPointer, leftover_bytes); }
catch { return Error = Error.MSPACK_ERR_WRITE; }
OutputPointer += leftover_bytes;
@@ -355,7 +872,7 @@ namespace LibMSPackSharp.Compression
int bytes_todo = (int)(FramePosition + frame_size - WindowPosition);
while (bytes_todo > 0)
{
ReadBlockHeader(buf);
ReadBlockHeader();
if (Error != Error.MSPACK_ERR_OK)
return Error;
@@ -397,9 +914,7 @@ namespace LibMSPackSharp.Compression
}
else
{
if (i > this_run)
i = this_run;
i = Math.Min(i, this_run);
Array.Copy(InputBuffer, InputPointer, Window, rundest, i);
rundest += i;
@@ -409,7 +924,7 @@ namespace LibMSPackSharp.Compression
}
// Realign if this was an odd-sized UNCOMPRESSED block
if (InputFileHandle.Position != InputFileHandle.Length - 1 && (BlockLength & 1) != 0)
if (InputPointer != InputEnd - 1 && (BlockLength & 1) != 0)
{
READ_IF_NEEDED();
if (Error != Error.MSPACK_ERR_OK)
@@ -462,20 +977,14 @@ namespace LibMSPackSharp.Compression
// Does this intel block _really_ need decoding?
if (IntelStarted)
{
UndoE8Preprocessing(frame_size);
}
else
{
OutputIsE8 = false;
OutputPointer = (int)FramePosition;
}
UndoE8Preprocessing((int)FramePosition, out_bytes);
OutputPointer = (int)FramePosition;
OutputEnd = (int)(OutputPointer + frame_size);
// Write a frame
int new_out_bytes = (int)((out_bytes < frame_size) ? out_bytes : frame_size);
try { System.Write(OutputFileHandle, OutputIsE8 ? E8Buffer : Window, OutputPointer, new_out_bytes); }
try { System.Write(OutputFileHandle, Window, OutputPointer, new_out_bytes); }
catch { return Error = Error.MSPACK_ERR_WRITE; }
OutputPointer += new_out_bytes;
@@ -537,9 +1046,19 @@ namespace LibMSPackSharp.Compression
private Error READ_LENGTHS(byte[] lengths, uint first, uint last)
{
int i_ptr = InputPointer;
int i_end = InputEnd;
uint bit_buffer = BitBuffer;
int bits_left = BitsLeft;
if (ReadLens(lengths, first, last) != Error.MSPACK_ERR_OK)
return Error;
InputPointer = i_ptr;
InputEnd = i_end;
BitBuffer = bit_buffer;
BitsLeft = bits_left;
return Error = Error.MSPACK_ERR_OK;
}
@@ -593,7 +1112,7 @@ namespace LibMSPackSharp.Compression
main_element -= LZX_NUM_CHARS;
// The length header consists of the lower 3 bits of the main element.
// The position slot is the rest of it.
// The position slot is the rest of it.
int length_header = main_element & LZX_NUM_PRIMARY_LENGTHS;
int position_slot = main_element >> 3;
@@ -747,7 +1266,7 @@ namespace LibMSPackSharp.Compression
return Error = Error.MSPACK_ERR_OK;
}
private Error ReadBlockHeader(byte[] buffer)
private Error ReadBlockHeader()
{
ENSURE_BITS(4);
@@ -763,20 +1282,20 @@ namespace LibMSPackSharp.Compression
}
else
{
int tmp;
uint tmp;
block_size = 0;
tmp = (int)READ_BITS_MSB(8);
block_size |= tmp;
tmp = (int)READ_BITS_MSB(8);
tmp = (uint)READ_BITS_MSB(8);
block_size |= (int)tmp;
tmp = (uint)READ_BITS_MSB(8);
block_size <<= 8;
block_size |= tmp;
block_size |= (int)tmp;
if (WindowSize >= 65536)
{
tmp = (int)READ_BITS_MSB(8);
tmp = (uint)READ_BITS_MSB(8);
block_size <<= 8;
block_size |= tmp;
block_size |= (int)tmp;
}
}
@@ -851,26 +1370,24 @@ namespace LibMSPackSharp.Compression
break;
case LZXBlockType.LZX_BLOCKTYPE_UNCOMPRESSED:
// Read 1-16 (not 0-15) bits to align to bytes
if (BitsLeft == 0)
ENSURE_BITS(16);
BitsLeft = 0; BitBuffer = 0;
// Read 12 bytes of stored R[0] / R[1] / R[2] values
for (int rundest = 0, k = 0; k < 12; k++)
{
READ_IF_NEEDED();
if (Error != Error.MSPACK_ERR_OK)
return Error;
buffer[rundest++] = InputBuffer[InputPointer++];
ENSURE_BITS(16);
BlockRemaining -= 2;
}
else
{
BitsLeft = 0;
BitBuffer = 0;
}
// TODO: uint[] R should be a part of a state object
R[0] = (uint)(buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24));
R[1] = (uint)(buffer[4] | (buffer[5] << 8) | (buffer[6] << 16) | (buffer[7] << 24));
R[2] = (uint)(buffer[8] | (buffer[9] << 8) | (buffer[10] << 16) | (buffer[11] << 24));
R[0] = BitConverter.ToUInt32(new Span<byte>(InputBuffer, InputPointer + 0, 4).ToArray().Reverse().ToArray(), 0);
R[1] = BitConverter.ToUInt32(new Span<byte>(InputBuffer, InputPointer + 4, 4).ToArray().Reverse().ToArray(), 0);
R[2] = BitConverter.ToUInt32(new Span<byte>(InputBuffer, InputPointer + 8, 4).ToArray().Reverse().ToArray(), 0);
InputPointer += 12;
BlockRemaining -= 12;
break;
@@ -884,69 +1401,71 @@ namespace LibMSPackSharp.Compression
private Error ReadLens(byte[] lens, uint first, uint last)
{
uint x, y;
int z;
// Read lengths for pretree (20 symbols, lengths stored in fixed 4 bits)
for (int i = 0; i < LZX_PRETREE_MAXSYMBOLS; i++)
for (x = 0; x < LZX_PRETREE_MAXSYMBOLS; x++)
{
uint y = (uint)READ_BITS_MSB(4);
PRETREE_len[i] = (byte)y;
y = (uint)READ_BITS_MSB(4);
PRETREE_len[x] = (byte)y;
}
BUILD_TABLE(PRETREE_table, PRETREE_len, LZX_PRETREE_TABLEBITS, LZX_PRETREE_MAXSYMBOLS);
if (Error != Error.MSPACK_ERR_OK)
return Error;
for (uint lensPtr = first; lensPtr < last;)
for (x = first; x < last;)
{
uint num_zeroes, num_same;
int tree_code = (int)READ_HUFFSYM_MSB(PRETREE_table, PRETREE_len, LZX_PRETREE_TABLEBITS, LZX_PRETREE_MAXSYMBOLS);
switch (tree_code)
z = (int)READ_HUFFSYM_MSB(PRETREE_table, PRETREE_len, LZX_PRETREE_TABLEBITS, LZX_PRETREE_MAXSYMBOLS);
switch (z)
{
// Code = 17, run of ([read 4 bits]+4) zeros
case 17:
num_zeroes = (uint)READ_BITS_MSB(4);
num_zeroes += 4;
while (num_zeroes-- != 0)
y = (uint)READ_BITS_MSB(4);
y += 4;
while (y-- != 0)
{
lens[lensPtr++] = 0;
lens[x++] = 0;
}
break;
// Code = 18, run of ([read 5 bits]+20) zeros
case 18:
num_zeroes = (uint)READ_BITS_MSB(5);
num_zeroes += 20;
while (num_zeroes-- != 0)
y = (uint)READ_BITS_MSB(5);
y += 20;
while (y-- != 0)
{
lens[lensPtr++] = 0;
lens[x++] = 0;
}
break;
// Code = 19, run of ([read 1 bit]+4) [read huffman symbol]
case 19:
num_same = (uint)READ_BITS_MSB(1);
num_same += 4;
y = (uint)READ_BITS_MSB(1);
y += 4;
tree_code = (int)READ_HUFFSYM_MSB(PRETREE_table, PRETREE_len, LZX_PRETREE_TABLEBITS, LZX_PRETREE_MAXSYMBOLS);
tree_code = lens[lensPtr] - tree_code;
if (tree_code < 0)
tree_code += 17;
z = (int)READ_HUFFSYM_MSB(PRETREE_table, PRETREE_len, LZX_PRETREE_TABLEBITS, LZX_PRETREE_MAXSYMBOLS);
z = lens[x] - z;
if (z < 0)
z += 17;
while (num_same-- != 0)
while (y-- != 0)
{
lens[lensPtr++] = (byte)tree_code;
lens[x++] = (byte)z;
}
break;
// Code = 0 to 16, delta current length entry
default:
tree_code = lens[lensPtr] - tree_code;
if (tree_code < 0)
tree_code += 17;
z = lens[x] - z;
if (z < 0)
z += 17;
lens[lensPtr++] = (byte)tree_code;
lens[x++] = (byte)z;
break;
}
}
@@ -975,81 +1494,50 @@ namespace LibMSPackSharp.Compression
}
}
private void UndoE8Preprocessing(uint frame_size)
private void UndoE8Preprocessing(int data, long out_bytes)
{
if (frame_size > 10)
int p8 = data;
if (out_bytes > 10)
{
// Finish any bytes that weren't processed by the vectorized implementation.
int start = WindowPosition;
int p8_end = (int)(WindowPosition + frame_size - 10);
int p8_end = (int)(out_bytes - 10);
do
{
if (Window[WindowPosition] == 0xe8)
if (Window[p8] == 0xe8)
{
int target = p8 + 1;
int input_pos = p8 - data;
int abs_offset, rel_offset;
// XXX: This assumes unaligned memory accesses are okay.
abs_offset = BitConverter.ToInt32(Window, WindowPosition + 1);
abs_offset = BitConverter.ToInt32(new Span<byte>(Window, target, 4).ToArray().Reverse().ToArray(), 0);
if (abs_offset >= 0)
{
if (abs_offset < 12_000_000)
{
// "good translation"
rel_offset = abs_offset - start;
Array.Copy(BitConverter.GetBytes(rel_offset), 0, Window, WindowPosition + 1, 4);
rel_offset = abs_offset - input_pos;
Array.Copy(BitConverter.GetBytes(rel_offset).Reverse().ToArray(), 0, Window, target, 4);
}
}
else
{
if (abs_offset >= -start)
if (abs_offset >= -input_pos)
{
// "compensating translation"
rel_offset = abs_offset + 12_000_000;
Array.Copy(BitConverter.GetBytes(rel_offset), 0, Window, WindowPosition + 1, 4);
Array.Copy(BitConverter.GetBytes(rel_offset).Reverse().ToArray(), 0, Window, target, 4);
}
}
WindowPosition += 5;
p8 += 5;
}
else
{
WindowPosition++;
p8++;
}
} while (WindowPosition < p8_end);
}
int data = 0;
int dataend = (int)(frame_size - 10);
int curpos = (int)Offset;
int filesize = IntelFileSize;
int abs_off, rel_off;
// Copy e8 block to the e8 buffer and tweak if needed
OutputIsE8 = true;
OutputPointer = data;
Array.Copy(Window, FramePosition, E8Buffer, data, frame_size);
while (data < dataend)
{
if (E8Buffer[data++] != 0xE8)
{
curpos++;
continue;
}
abs_off = E8Buffer[data + 0] | (E8Buffer[data + 1] << 8) | (E8Buffer[data + 2] << 16) | (E8Buffer[data + 3] << 24);
if ((abs_off >= -curpos) && (abs_off < filesize))
{
rel_off = (abs_off >= 0) ? abs_off - curpos : abs_off + filesize;
E8Buffer[data + 0] = (byte)rel_off;
E8Buffer[data + 1] = (byte)(rel_off >> 8);
E8Buffer[data + 2] = (byte)(rel_off >> 16);
E8Buffer[data + 3] = (byte)(rel_off >> 24);
}
data += 4;
curpos += 5;
} while (p8 < p8_end);
}
}
}

View File

@@ -126,12 +126,6 @@ namespace LibMSPackSharp.Compression
public byte LENGTH_empty { get; set; }
// This is used purely for doing the intel E8 transform
public byte[] E8Buffer { get; set; } = new byte[LZX_FRAME_SIZE];
/// <summary>
/// Is the output pointer referring to E8?
/// </summary>
public bool OutputIsE8 { get; set; }
}
}

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Concurrent;
using System.IO;
using System.Text.RegularExpressions;
using BurnOutSharp.Interfaces;
using BurnOutSharp.Tools;
using LibMSPackSharp;
@@ -52,6 +53,18 @@ namespace BurnOutSharp.FileType
return null;
}
// If there are additional previous CABs, add those
while (!string.IsNullOrWhiteSpace(cabFile.NextName))
{
// TODO: Implement
}
// If there are additional next CABs, add those
while (!string.IsNullOrWhiteSpace(cabFile.NextName))
{
// TODO: Implement
}
// Loop through the found internal files
var sub = cabFile.Files;
while (sub != null)