lazarus/components/chmhelp/packages/chm/paslzx.pas
2006-11-27 18:37:31 +00:00

1018 lines
36 KiB
ObjectPascal

{ Copyright (C) <2005> <Andrew Haines> paslzx.pas
This library is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published by
the Free Software Foundation; either version 2 of the License, or (at your
option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
for more details.
You should have received a copy of the GNU Library General Public License
along with this library; if not, write to the Free Software Foundation,
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
}
{
See the file COPYING.modifiedLGPL, included in this distribution,
for details about the copyright.
}
{***************************************************************************
* paslzx.pas - LZX decompression routines *
* ------------------- *
* *
* maintainer: Andrew Haines <andrewd207@aol.com> *
* source: modified lzx.c from chmlib 0.37-4 *
* notes: The lzx.c file was taken from cabextract v0.5, which was, *
* itself, a modified version of the lzx decompression code *
* from unlzx. This file would not be available without the *
* invaluable help from Micha Nelissen fixing my errors. *
* *
* Licensed with permission of Stuart Caie with a modified *
* LGPL. *
* *
* platforms: Should work on any platform that FreePascal is available *
* on. However it has been tested on only an amd64(Linux) and *
* x86(Linux and Windows). Only tested on little endian pc's. *
***************************************************************************}
unit paslzx;
{$mode objfpc}{$H+}{$R+}
interface
uses
Classes, SysUtils;
const
DECR_OK = 0;
DECR_DATAFORMAT = 1;
DECR_ILLEGALDATA = 2;
DECR_NOMEMORY = 3;
// some constants defined by the LZX specification
LZX_MIN_MATCH = 2;
LZX_MAX_MATCH = 257;
LZX_NUM_CHARS = 256;
LZX_BLOCKTYPE_INVALID = 0; // also blocktypes 4-7 invalid
LZX_BLOCKTYPE_VERBATIM = 1;
LZX_BLOCKTYPE_ALIGNED = 2;
LZX_BLOCKTYPE_UNCOMPRESSED= 3;
LZX_PRETREE_NUM_ELEMENTS = 20;
LZX_ALIGNED_NUM_ELEMENTS = 8; // aligned offset tree #elements
LZX_NUM_PRIMARY_LENGTHS = 7; // this one missing from spec!
LZX_NUM_SECONDARY_LENGTHS = 249;// length tree #elements
// LZX huffman defines: tweak tablebits as desired
LZX_PRETREE_MAXSYMBOLS = LZX_PRETREE_NUM_ELEMENTS;
LZX_PRETREE_TABLEBITS = 6;
LZX_MAINTREE_MAXSYMBOLS = LZX_NUM_CHARS + 50*8;
LZX_MAINTREE_TABLEBITS = 12;
LZX_LENGTH_MAXSYMBOLS = LZX_NUM_SECONDARY_LENGTHS+1;
LZX_LENGTH_TABLEBITS = 12;
LZX_ALIGNED_MAXSYMBOLS = LZX_ALIGNED_NUM_ELEMENTS;
LZX_ALIGNED_TABLEBITS = 7;
LZX_LENTABLE_SAFETY = 64; // we allow length table decoding overruns
extra_bits: array [0..50] of Byte = (
0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14,
15, 15, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
17, 17, 17
);
position_base: array [0..50] of dword = (
0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 192,
256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576, 32768, 49152,
65536, 98304, 131072, 196608, 262144, 393216, 524288, 655360, 786432, 917504, 1048576, 1179648, 1310720, 1441792, 1572864, 1703936,
1835008, 1966080, 2097152
);
type
{ TBits }
TBufBits = class
private
bitbuf: dword;
bitsleft: LongInt;
public
procedure Init;
procedure ensure(num: LongInt; var inpos:PByte);
function peek(numbits: LongInt): dword;
function remove(numbits: LongInt): dword;
function read(numbits: LongInt; var inpos: PByte): dword;
end;
TLZX_PRETREE_TABLE = record
Table: array [0..(1 shl LZX_PRETREE_TABLEBITS) + (LZX_PRETREE_MAXSYMBOLS shl 1)-1] of Word;
Len: array [0..LZX_PRETREE_MAXSYMBOLS + LZX_LENTABLE_SAFETY-1] of Byte;
end;
TLZX_MAINTREE_TABLE = record
Table: array [0..(1 shl LZX_MAINTREE_TABLEBITS) + (LZX_MAINTREE_MAXSYMBOLS shl 1)-1] of Word;
Len: array [0..LZX_MAINTREE_MAXSYMBOLS + LZX_LENTABLE_SAFETY-1] of Byte;
end;
TLZX_LENGTH_TABLE = record
Table: array [0..(1 shl LZX_LENGTH_TABLEBITS) + (LZX_LENGTH_MAXSYMBOLS shl 1)-1] of Word;
Len: array [0..LZX_LENGTH_MAXSYMBOLS + LZX_LENTABLE_SAFETY-1] of Byte;
end;
TLZX_ALIGNED_TABLE = record
Table: array [0..(1 shl LZX_ALIGNED_TABLEBITS) + (LZX_ALIGNED_MAXSYMBOLS shl 1)-1] of Word;
Len: array [0..LZX_ALIGNED_MAXSYMBOLS + LZX_LENTABLE_SAFETY-1] of Byte;
end;
PLZXState = ^TLZXState;
TLZXState = record
window: PByte; // the actual decoding window
window_size, // window size (32Kb through 2Mb)
actual_size, // window size when it was first allocated
window_posn, // current offset within the window
R0, R1, R2: dword; // for the LRU offset system
main_elements : Word; // number of main tree elements
header_read: LongInt; // have we started decoding at all yet?
block_type: Word; // type of this block
block_length, // uncompressed length of this block
block_remaining, // uncompressed bytes still left to decode
frames_read: dword; // the number of CFDATA blocks
intel_filesize, // magic header value used for transform
intel_curpos: LongInt; // current offset in transform space
intel_started: LongInt; // have we seen any translatable data yet?
PreTreeTable: TLZX_PRETREE_TABLE;
MainTreeTable: TLZX_MAINTREE_TABLE;
LengthTable: TLZX_LENGTH_TABLE;
AlignedTAble: TLZX_ALIGNED_TABLE;
end;
// create an lzx state object
function LZXinit(window: LongInt): PLZXState;
// destroy an lzx state object
procedure LZXteardown(pState: PLZXState);
// reset an lzx stream
function LZXreset(pState: PLZXState): LongInt;
function LZXdecompress(pState: PLZXstate; inpos, outpos: PByte; inlen, outlen: LongInt): LongInt;
implementation
const
ULONG_BITS = sizeof(LongInt)shl 3;
function make_decode_table(nsyms: dword; nbits: dword; length: PByte; table: PWord): LongInt;
var
Sym: Word;
leaf: dword;
bit_num: Byte = 1;
fill: dword;
pos: dword = 0; //* the current position in the decode table */
table_mask: dword;
bit_mask: dword; //* don't do 0 length codes */
next_symbol: dword; //* base of allocation for long codes */
begin
Result := 0;
table_mask := 1 shl nbits;
bit_mask := table_mask shr 1;
next_symbol := bit_mask;
//* fill entries for codes short enough for a direct mapping */
while (bit_num <= nbits) do begin
for sym := 0 to nsyms-1 do begin
if (length[sym] = bit_num) then begin
leaf := pos;
Inc(pos, bit_mask);
if pos > table_mask then begin
Result := 1; //* table overrun */
exit;
end;
//* fill all possible lookups of this symbol with the symbol itself */
fill := bit_mask;
while fill > 0 do
begin
dec(fill);
table[leaf] := sym;
Inc(leaf);
end;
end;
end;
bit_mask := bit_mask shr 1;
Inc(bit_num);
end;
//* if there are any codes longer than nbits */
if pos <> table_mask then begin
//* clear the remainder of the table */
for sym := pos to table_mask-1 do table[sym] := 0;
//* give ourselves room for codes to grow by up to 16 more bits */
pos := pos shl 16;
table_mask := table_mask shl 16;
bit_mask := 1 shl 15;
while (bit_num <= 16) do begin
for sym := 0 to nsyms-1 do begin
if (length[sym] = bit_num) then begin
leaf := pos shr 16;
for fill := 0 to (bit_num - nbits)-1 do begin
//* if this path hasn't been taken yet, 'allocate' two entries */
if (table[leaf] = 0) then begin
table[(next_symbol shl 1)] := 0;
table[(next_symbol shl 1)+1] := 0;
table[leaf] := Word(next_symbol);
Inc(next_symbol);
end;
//* follow the path and select either left or right for next bit */
leaf := table[leaf] shl 1;
if ((pos shr (15-fill)) and 1) > 0 then Inc(leaf);
end;
table[leaf] := sym;
pos := pos + bit_mask;
if (pos > table_mask) then begin
Result := 1; //* table overflow */
exit;
end;
end;
end;
bit_mask := bit_mask shr 1;
Inc(bit_num);
end;
end;
//* full table? */
if (pos = table_mask) then begin
Result := 0;
Exit;
end;
//* either erroneous table, or all elements are 0 - let's find out. */
for sym := 0 to nsyms-1 do begin
if length[sym] > 0 then begin
Result := 1;
Exit;
end;
end;
Result := 0;
end;
type
PLZX_bits = ^TLzx_bits;
Tlzx_bits = record
bb: dword;
bl: LongInt;
ip: PByte;
end;
function READ_HUFFSYM(Table: PWord; Len: PByte; const bits: TBufBits; var inpos: PByte;
var i, j: DWord; const TableBits, MaxSymbols: DWord; out z: LongInt): LongInt;
var
hufftbl: PWord;
begin
bits.ensure(16, inpos);
hufftbl := Table;
i := hufftbl[bits.peek(TableBits)];
if (i) >= MaxSymbols then begin
j := 1 shl (ULONG_BITS - TableBits);
repeat
j := j shr 1;
i := i shl 1;
i := i or ord((bits.bitbuf and j) <> 0);
if j = 0 then begin
Result := DECR_ILLEGALDATA;
Exit;
end;
i := hufftbl[i];
until i < MaxSymbols;
end;
z := i;
j := Len[z];
bits.remove(j);
Result := 0;
end;
function lzx_read_lens(pState: PLZXState; lens: PByte; first: dword; last: dword; lb: Plzx_bits): LongInt;
var
i: dword = 0;
j: dword = 0;
x,y: dword;
z: LongInt;
inpos: PByte;
bits: TBufBits;
begin
bits := TBufBits.Create;
bits.bitbuf := lb^.bb;
bits.bitsleft := lb^.bl;
inpos := lb^.ip;
for X := 0 to 19 do begin
y := bits.read(4, inpos);
pState^.PreTreeTable.Len[x] := byte(y);
end;
if make_decode_table(LZX_PRETREE_MAXSYMBOLS, LZX_PRETREE_TABLEBITS,
@pState^.PreTreeTable.Len[0],@pState^.PreTreeTable.Table[0]) >0 then
begin
Result := DECR_ILLEGALDATA;
bits.Free;
Exit;
end;
x := first;
while x < last do begin
if READ_HUFFSYM(@pState^.PreTreeTable.Table[0], @pstate^.PreTreeTable.Len[0], bits, inpos, i, j,
LZX_PRETREE_TABLEBITS, LZX_PRETREE_MAXSYMBOLS, z) <> 0 then
begin
Result := DECR_ILLEGALDATA;
bits.Free;
Exit;
end;
if (z = 17) then begin
y := bits.read(4, inpos);
Inc(y, 4);
while y > 0 do begin
dec(y);
Lens[x] := 0;
Inc(x);
end;
end
else if (z = 18) then begin
y := bits.read(5, inpos);
Inc(y, 20);
while y > 0 do begin
dec(y);
lens[x] := 0;
inc(x);
end;
end
else if (z = 19) then begin
y := bits.read(1, inpos);
Inc(y, 4);
if READ_HUFFSYM(@pState^.PreTreeTable.Table[0], @pstate^.PreTreeTable.Len[0], bits, inpos, i, j,
LZX_PRETREE_TABLEBITS, LZX_PRETREE_MAXSYMBOLS, z) <> 0 then
begin
Result := DECR_ILLEGALDATA;
bits.Free;
Exit;
end;
z := lens[x] - z;
if (z < 0) then z := z + 17;
while y > 0 do begin
dec(y);
lens[x] := byte(z);
inc(x);
end;
end
else begin
z := lens[x] - z;
if (z < 0) then z := z + 17;
lens[x] := byte(z);
inc(x);
end;
end;
lb^.bb := bits.bitbuf;
lb^.bl := bits.bitsleft;
lb^.ip := inpos;
Result := 0;
bits.Free;
end;
//////////////////////////////////////////////////////////////////////////////////////
function LZXinit(window: LongInt): PLZXState;
var
pState: PLZXState;
wndsize: dword;
i,
posn_slots: LongInt;
begin
Result := nil;
wndsize := 1 shl window;
//* LZX supports window sizes of 2^15 (32Kb) through 2^21 (2Mb) */
//* if a previously allocated window is big enough, keep it */
if (window < 15) or (window > 21) then begin
Exit;
end;
//* allocate state and associated window */
New(pState);
pState^.window := GetMem(wndsize);
if pState^.window = nil then
begin
Dispose(pState);
Result := nil;
exit;
end;
pState^.actual_size := wndsize;
pState^.window_size := wndsize;
//* calculate required position slots */
if (window = 20) then posn_slots := 42
else if (window = 21) then posn_slots := 50
else posn_slots := window shl 1;
///** alternatively **/
///* posn_slots=i=0; while (i < wndsize) i += 1 << extra_bits[posn_slots++]; */
///* initialize other state */
pState^.R0 := 1;
pState^.R1 := 1;
pState^.R2 := 1;
pState^.main_elements := LZX_NUM_CHARS + (posn_slots shl 3);
pState^.header_read := 0;
pState^.frames_read := 0;
pState^.block_remaining := 0;
pState^.block_type := LZX_BLOCKTYPE_INVALID;
pState^.intel_curpos := 0;
pState^.intel_started := 0;
pState^.window_posn := 0;
///* initialise tables to 0 (because deltas will be applied to them) */
for i := 0 to LZX_MAINTREE_MAXSYMBOLS-1 do pState^.MainTreeTable.Len[i] := 0;
for i := 0 to LZX_LENGTH_MAXSYMBOLS-1 do pState^.LengthTable.Len[i] := 0;
Result := pState;
end;
procedure LZXteardown(pState: PLZXState);
begin
if pState <> nil then
begin
if pState^.window <> nil then
Freemem(pState^.window);
Dispose(pState);
end;
end;
function LZXreset(pState: PLZXState): LongInt;
var
i: LongInt;
begin
pState^.R0 := 1;
pState^.R1 := 1;
pState^.R2 := 1;
pState^.header_read := 0;
pState^.frames_read := 0;
pState^.block_remaining := 0;
pState^.block_type := LZX_BLOCKTYPE_INVALID;
pState^.intel_curpos := 0;
pState^.intel_started := 0;
pState^.window_posn := 0;
for i := 0 to (LZX_MAINTREE_MAXSYMBOLS + LZX_LENTABLE_SAFETY - 1) do pState^.MainTreeTable.Len[i] := 0;
for i := 0 to LZX_LENGTH_MAXSYMBOLS+LZX_LENTABLE_SAFETY-1 do pState^.LengthTable.Len[i] := 0;
Result := DECR_OK;
end;
function LZXdecompress(pState: PLZXstate; inpos, outpos: PByte; inlen,
outlen: LongInt): LongInt;
var
endinp: PByte;
window: PByte;
runsrc,
rundest: PByte;
window_posn: dword;
window_size: dword;
R0,
r1,
R2: dword;
bits: TBufBits;
match_offset,
i,j,k : dword;
lb: tlzx_bits;
togo,
this_run,
main_element,
aligned_bits: LongInt;
match_length,
length_footer,
extra,
verbatim_bits: LongInt;
data,
dataend: PByte;
curpos,
filesize,
abs_off,
rel_off: LongInt;
function READ_LENGTHS(Len: PByte; first: dword; last: dword): Longint;
begin
Result := 0;
lb.bb := bits.bitbuf;
lb.bl := bits.bitsleft;
lb.ip := inpos;
if (lzx_read_lens(pState, Len,first,last,@lb)) > 0 then begin
Result := DECR_ILLEGALDATA;
Exit;
end;
bits.bitbuf := lb.bb;
bits.bitsleft := lb.bl;
inpos := lb.ip;
end;
procedure HandleBlockTypeAligned;
var
i, j: dword;
begin
for i := 0 to 7 do begin
j:= bits.read(3, inpos);
pState^.AlignedTAble.Len[i] := Word(j);
end;
if make_decode_table(LZX_ALIGNED_MAXSYMBOLS, LZX_ALIGNED_TABLEBITS,
@pState^.AlignedTAble.Len[0],@pState^.AlignedTAble.Table[0]) >0 then
begin
Result := DECR_ILLEGALDATA;
Exit;
end;
end;
procedure HandleBlockTypeVerbatim;
begin
if (
READ_LENGTHS(@pState^.MainTreeTable.Len[0], 0, 256) = DECR_ILLEGALDATA)
or (
READ_LENGTHS(@pState^.MainTreeTable.Len[0], 256, pState^.main_elements) = DECR_ILLEGALDATA)
then begin
Result := DECR_ILLEGALDATA;
Exit;
end;
if make_decode_table(LZX_MAINTREE_MAXSYMBOLS, LZX_MAINTREE_TABLEBITS,
@pState^.MainTreeTable.Len[0], @pState^.MainTreeTable.Table[0]) >0 then
begin
Result := DECR_ILLEGALDATA;
Exit;
end;
if pState^.MainTreeTable.Len[$E8] <> 0 then
pState^.intel_started := 1;
if READ_LENGTHS(@pState^.LengthTable.Len[0], 0, LZX_NUM_SECONDARY_LENGTHS) = DECR_ILLEGALDATA then begin
Result := DECR_ILLEGALDATA;
Exit;
end;
if make_decode_table(LZX_LENGTH_MAXSYMBOLS, LZX_LENGTH_TABLEBITS,
@pState^.LengthTable.Len[0],@pState^.LengthTable.Table[0]) >0 then
begin
Result := DECR_ILLEGALDATA;
Exit;
end;
end;
begin
endinp := inpos + inlen;
window := pState^.window;
window_posn := pState^.window_posn;
window_size := pState^.window_size;
R0 := pState^.R0;
R1 := pState^.R1;
R2 := pState^.R2;
togo := outlen;//, this_run, main_element, aligned_bits;
bits := TBufBits.Create;
bits.Init;
//* read header if necessary */
if (pState^.header_read) = 0 then begin
i := 0;
j := 0;
k := bits.read(1, inpos);
if (k) > 0 then begin
i := bits.read(16, inpos);
j := bits.read(16, inpos);
end;
pState^.intel_filesize := (i shl 16) or j; ///* or 0 if not encoded */
pState^.header_read := 1;
end;
///* main decoding loop */
while (togo > 0) do begin
///* last block finished, new block expected */
if (pState^.block_remaining = 0) then begin
if (pState^.block_type = LZX_BLOCKTYPE_UNCOMPRESSED) then begin
if (pState^.block_length and 1) > 0 then Inc(inpos); //* realign bitstream to word */
bits.Init;
end;
pState^.block_type := Word(bits.read(3, inpos));
i := bits.read(16, inpos);
j := bits.read(8, inpos);
pState^.block_length := (i shl 8) or j;
pState^.block_remaining := pState^.block_length;
case (pState^.block_type) of
LZX_BLOCKTYPE_ALIGNED:
begin
HandleBlockTypeAligned;
//* rest of aligned header is same as verbatim */
HandleBlockTypeVerbatim;
end;
LZX_BLOCKTYPE_VERBATIM:
begin
HandleBlockTypeVerbatim;
end;
LZX_BLOCKTYPE_UNCOMPRESSED:
begin
pState^.intel_started := 1; //* because we can't assume otherwise */
bits.ensure(16, inpos); //* get up to 16 pad bits into the buffer */
if (bits.bitsleft > 16) then Dec(inpos ,2); //* and align the bitstream! */
R0 := inpos[0] or (inpos[1]shl 8)or(inpos[2]shl 16)or(inpos[3]shl 24);
Inc(inpos,4);
R1 := inpos[0] or (inpos[1]shl 8)or(inpos[2]shl 16)or(inpos[3]shl 24);
Inc(inpos,4);
R2 := inpos[0] or (inpos[1]shl 8)or(inpos[2]shl 16)or(inpos[3]shl 24);
Inc(inpos,4);
end;
else
Result := DECR_ILLEGALDATA;
bits.Free;
Exit;
end;
end;
//* buffer exhaustion check */
if (inpos > endinp) then begin
{* it's possible to have a file where the next run is less than
* 16 bits in size. In this case, the READ_HUFFSYM() macro used
* in building the tables will exhaust the buffer, so we should
* allow for this, but not allow those accidentally read bits to
* be used (so we check that there are at least 16 bits
* remaining - in this boundary case they aren't really part of
* the compressed data)
*}
if (inpos > (endinp+2)) or (bits.bitsleft < 16) then begin
Result := DECR_ILLEGALDATA;
bits.Free;
Exit;
end;
end;
this_run := pState^.block_remaining;
while (this_run > 0) and (togo > 0) do begin
if (this_run > togo) then this_run := togo;
Dec(togo, this_run);
Dec(pState^.block_remaining, this_run);
//* apply 2^x-1 mask */
window_posn := window_posn and (window_size - 1);
//* runs can't straddle the window wraparound */
if ((window_posn + this_run) > window_size) then begin
Result := DECR_DATAFORMAT;
bits.Free;
Exit;
end;
case (pState^.block_type) of
LZX_BLOCKTYPE_VERBATIM:
begin
while (this_run > 0) do begin
if READ_HUFFSYM(@pState^.MainTreeTable.Table[0], @pState^.MainTreeTable.Len[0],
bits, inpos, i, j, LZX_MAINTREE_TABLEBITS, LZX_MAINTREE_MAXSYMBOLS,
main_element) <> 0 then
begin
Result := DECR_ILLEGALDATA;
bits.Free;
Exit;
end;
if (main_element < LZX_NUM_CHARS) then begin
//* literal: 0 to LZX_NUM_CHARS-1 */
window[window_posn] := Byte(main_element);
Inc(window_posn);
Dec(this_run);
end
else begin
//* match: LZX_NUM_CHARS + ((slot<<3) | length_header (3 bits)) */
Dec(main_element, LZX_NUM_CHARS);
match_length := main_element and LZX_NUM_PRIMARY_LENGTHS;
if (match_length = LZX_NUM_PRIMARY_LENGTHS) then begin
if READ_HUFFSYM(@pState^.LengthTable.Table[0], @pState^.LengthTable.Len[0],
bits, inpos, i, j, LZX_LENGTH_TABLEBITS, LZX_LENGTH_MAXSYMBOLS,
length_footer) <> 0 then
begin
Result := DECR_ILLEGALDATA;
bits.Free;
Exit;
end;
Inc(match_length, length_footer);
end;
Inc(match_length, LZX_MIN_MATCH);
match_offset := main_element shr 3;
if (match_offset > 2) then begin
//* not repeated offset */
if (match_offset <> 3) then begin
extra := extra_bits[match_offset];
verbatim_bits := bits.read(extra, inpos);
match_offset := position_base[match_offset] - 2 + verbatim_bits;
end
else begin
match_offset := 1;
end;
//* update repeated offset LRU queue */
R2 := R1;
R1 := R0;
R0 := match_offset;
end
else if (match_offset = 0) then begin
match_offset := R0;
end
else if (match_offset = 1) then begin
match_offset := R1;
R1 := R0;
R0 := match_offset;
end
else begin //* match_offset == 2 */
match_offset := R2;
R2 := R0;
R0 := match_offset;
end;
rundest := window + window_posn;
runsrc := rundest - match_offset;
Inc(window_posn, match_length);
if (window_posn > window_size) then begin
Result := DECR_ILLEGALDATA;
bits.Free;
Exit;
end;
Dec(this_run, match_length);
///* copy any wrapped around source data */
while ((runsrc < window) and (match_length > 0)) do begin
Dec(match_length);
rundest^ := (runsrc + window_size)^;
Inc(rundest);
Inc(runsrc);
end;
//* copy match data - no worries about destination wraps */
while (match_length > 0) do begin
Dec(match_length);
rundest^ := runsrc^;
Inc(rundest);
Inc(runsrc);
end;
end
end;
end;
LZX_BLOCKTYPE_ALIGNED:
begin
while (this_run > 0) do begin
if READ_HUFFSYM(@pState^.MainTreeTable.Table[0], @pState^.MainTreeTable.Len[0], bits,
inpos, i, j, LZX_MAINTREE_TABLEBITS, LZX_MAINTREE_MAXSYMBOLS, main_element) <> 0 then
begin
Result := DECR_ILLEGALDATA;
bits.Free;
Exit;
end;
if (main_element < LZX_NUM_CHARS) then begin
//* literal: 0 to LZX_NUM_CHARS-1 */
window[window_posn] := Byte(main_element);
Inc(window_posn);
Dec(this_run);
end
else begin
//* match: LZX_NUM_CHARS + ((slot<<3) | length_header (3 bits)) */
Dec(main_element, LZX_NUM_CHARS);
match_length := main_element and LZX_NUM_PRIMARY_LENGTHS;
if (match_length = LZX_NUM_PRIMARY_LENGTHS) then begin
if READ_HUFFSYM(@pState^.LengthTable.Table[0], @pState^.LengthTable.Len[0],
bits, inpos, i, j, LZX_LENGTH_TABLEBITS,
LZX_LENGTH_MAXSYMBOLS, length_footer) <> 0 then
begin
Result := DECR_ILLEGALDATA;
bits.Free;
Exit;
end;
Inc(match_length, length_footer);
end;
Inc(match_length, LZX_MIN_MATCH);
match_offset := main_element shr 3;
if (match_offset > 2) then begin
//* not repeated offset */
extra := extra_bits[match_offset];
match_offset := position_base[match_offset] - 2;
if (extra > 3) then begin
//* verbatim and aligned bits */
Dec(extra, 3);
verbatim_bits := bits.read(extra, inpos);
Inc(match_offset, (verbatim_bits shl 3));
if READ_HUFFSYM(@pState^.AlignedTAble.Table[0], @pState^.AlignedTAble.Len[0],
bits, inpos, i, j, LZX_ALIGNED_TABLEBITS, LZX_ALIGNED_MAXSYMBOLS,
aligned_bits) <> 0 then
begin
Result := DECR_ILLEGALDATA;
bits.Free;
Exit;
end;
Inc(match_offset, aligned_bits);
end
else if (extra = 3) then begin
//* aligned bits only */
if READ_HUFFSYM(@pState^.AlignedTAble.Table[0], @pState^.AlignedTAble.Len[0],
bits, inpos, i, j, LZX_ALIGNED_TABLEBITS, LZX_ALIGNED_MAXSYMBOLS,
aligned_bits) <> 0 then
begin
Result := DECR_ILLEGALDATA;
bits.Free;
Exit;
end;
Inc(match_offset, aligned_bits);
end
else if (extra > 0) then begin //* extra==1, extra==2 */
//* verbatim bits only */
verbatim_bits := bits.read(extra, inpos);
Inc(match_offset, verbatim_bits);
end
else begin //* extra == 0 */
//* ??? */
match_offset := 1;
end;
//* update repeated offset LRU queue */
R2 := R1;
R1 := R0;
R0 := match_offset;
end
else if (match_offset = 0) then begin
match_offset := R0;
end
else if (match_offset = 1) then begin
match_offset := R1;
R1 := R0;
R0 := match_offset;
end
else begin //* match_offset == 2 */
match_offset := R2;
R2 := R0;
R0 := match_offset;
end;
rundest := window + window_posn;
runsrc := rundest - match_offset;
Inc(window_posn, match_length);
if (window_posn > window_size) then begin
Result := DECR_ILLEGALDATA;
bits.Free;
Exit;
end;
Dec(this_run, match_length);
//* copy any wrapped around source data */
while ((runsrc < window) and (match_length > 0)) do begin
Dec(match_length);
rundest^ := (runsrc + window_size)^;
Inc(rundest);
Inc(runsrc);
end;
//* copy match data - no worries about destination wraps */
while (match_length > 0) do begin
Dec(match_length);
rundest^ := runsrc^;
Inc(rundest);
Inc(runsrc);
end;
end;
end;
end;
LZX_BLOCKTYPE_UNCOMPRESSED:
begin
if ((inpos + this_run) > endinp) then begin
Result := DECR_ILLEGALDATA;
bits.Free;
Exit;
end;
Move(inpos^, (window + window_posn)^, this_run);
Inc(inpos, this_run);
Inc(window_posn, this_run);
end;
else
Result := DECR_ILLEGALDATA; ///* might as well */
bits.Free;
Exit;
end;
this_run := pState^.block_remaining;
end;
end;
if (togo <> 0) then begin
Result := DECR_ILLEGALDATA;
bits.Free;
Exit;
end;
if window_posn = 0 then
Move((window + window_size - outlen)^, outpos^, outlen)
else
Move((window + window_posn - outlen)^, outpos^, outlen);
pState^.window_posn := window_posn;
pState^.R0 := R0;
pState^.R1 := R1;
pState^.R2 := R2;
//* intel E8 decoding */
if ((pState^.frames_read < 32768) and (pState^.intel_filesize <> 0)) then begin
if (outlen <= 6 or not pState^.intel_started) then begin
Inc(pState^.intel_curpos, outlen);
end
else begin
data := outpos;
dataend := data + outlen - 10;
curpos := pState^.intel_curpos;
filesize := pState^.intel_filesize;
pState^.intel_curpos := curpos + outlen;
while (data < dataend) do begin
if data^ <> $E8 then begin
Inc(curpos);
Inc(Data);
continue;
end;
Inc(Data);
abs_off := data[0] or (data[1]shl 8) or (data[2]shl 16) or (data[3]shl 24);
if (abs_off >= curpos-1) and (abs_off < filesize) then begin
if (abs_off >= 0) then
rel_off := abs_off - curpos
else
rel_off := abs_off + filesize;
{$IFDEF ENDIAN_BIG}
PLongWord(data)^ := Swap(rel_off);
{$ELSE}
PLongword(data)^ := rel_off;
{$ENDIF}
end;
Inc(data, 4);
Inc(curpos, 5);
end;
end;
end;
Inc(pState^.frames_read);
bits.Free;
Result := DECR_OK;
end;
{ TBufBits }
procedure TBufBits.Init;
begin
bitsleft := 0;
bitbuf := 0;
end;
procedure TBufBits.ensure(num: LongInt; var inpos:PByte);
begin
while (bitsleft < num) do begin
bitbuf := bitbuf or (((inpos[1]shl 8) or inpos[0]) shl (ULONG_BITS-16 - bitsleft));
Inc(bitsleft, 16);
Inc(inpos, 2);
end;
end;
function TBufBits.peek(numbits: LongInt): dword;
begin
Result := bitbuf shr (ULONG_BITS - numbits);
end;
function TBufBits.remove(numbits: LongInt): dword;
begin
bitbuf := bitbuf shl numbits;
Result := bitbuf;
Dec(bitsleft, numbits);
end;
function TBufBits.read(numbits: LongInt; var inpos: PByte): dword;
begin
ensure(numbits, inpos);
Result := peek(numbits);
remove(numbits);
end;
end.