{ Copyright (C) <2005> 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 * * 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.