429 lines
14 KiB
ObjectPascal
429 lines
14 KiB
ObjectPascal
![]() |
unit ULZMADecoder;
|
||
|
|
||
|
{$IFDEF FPC}
|
||
|
{$MODE Delphi}
|
||
|
{$ENDIF}
|
||
|
|
||
|
interface
|
||
|
|
||
|
uses ULZMABase,UBitTreeDecoder,ULZOutWindow,URangeDecoder,KOL,ULZMACommon;
|
||
|
|
||
|
type PLZMALenDecoder = ^TLZMALenDecoder;
|
||
|
PLZMALiteralDecoder = ^TLZMALiteralDecoder;
|
||
|
|
||
|
PLZMADecoder = ^TLZMADecoder;
|
||
|
TLZMADecoder = object(TObj)
|
||
|
private
|
||
|
FOnProgress:TLZMAProgress;
|
||
|
procedure DoProgress(const Action:TLZMAProgressAction;const Value:integer);
|
||
|
public
|
||
|
m_OutWindow:PLZOutWindow;
|
||
|
m_RangeDecoder:PRangeDecoder;
|
||
|
|
||
|
m_IsMatchDecoders: array [0..ULZMABase.kNumStates shl ULZMABase.kNumPosStatesBitsMax-1] of smallint;
|
||
|
m_IsRepDecoders: array [0..ULZMABase.kNumStates-1] of smallint;
|
||
|
m_IsRepG0Decoders: array [0..ULZMABase.kNumStates-1] of smallint;
|
||
|
m_IsRepG1Decoders: array [0..ULZMABase.kNumStates-1] of smallint;
|
||
|
m_IsRepG2Decoders: array [0..ULZMABase.kNumStates-1] of smallint;
|
||
|
m_IsRep0LongDecoders: array [0..ULZMABase.kNumStates shl ULZMABase.kNumPosStatesBitsMax-1] of smallint;
|
||
|
|
||
|
m_PosSlotDecoder: array [0..ULZMABase.kNumLenToPosStates-1] of PBitTreeDecoder;
|
||
|
m_PosDecoders: array [0..ULZMABase.kNumFullDistances - ULZMABase.kEndPosModelIndex-1] of smallint;
|
||
|
|
||
|
m_PosAlignDecoder:PBitTreeDecoder;
|
||
|
|
||
|
m_LenDecoder:PLZMALenDecoder;
|
||
|
m_RepLenDecoder:PLZMALenDecoder;
|
||
|
|
||
|
m_LiteralDecoder:PLZMALiteralDecoder;
|
||
|
|
||
|
m_DictionarySize:integer;
|
||
|
m_DictionarySizeCheck:integer;
|
||
|
|
||
|
m_PosStateMask:integer;
|
||
|
|
||
|
constructor Create;
|
||
|
destructor Destroy;virtual;
|
||
|
function SetDictionarySize(const dictionarySize:integer):boolean;
|
||
|
function SetLcLpPb(const lc,lp,pb:integer):boolean;
|
||
|
procedure _Init;
|
||
|
function Code(const inStream,outStream:PStream;outSize:int64):boolean;
|
||
|
function SetDecoderProperties(const properties:array of byte):boolean;
|
||
|
property OnProgress:TLZMAProgress read FOnProgress write FOnProgress;
|
||
|
end;
|
||
|
|
||
|
TLZMALenDecoder = object(TObj)
|
||
|
public
|
||
|
m_Choice:array [0..1] of smallint;
|
||
|
m_LowCoder: array[0..ULZMABase.kNumPosStatesMax-1] of PBitTreeDecoder;
|
||
|
m_MidCoder: array[0..ULZMABase.kNumPosStatesMax-1] of PBitTreeDecoder;
|
||
|
m_HighCoder: PBitTreeDecoder;
|
||
|
m_NumPosStates:integer;
|
||
|
constructor Create;
|
||
|
destructor Destroy;virtual;
|
||
|
procedure _Create(const numPosStates:integer);
|
||
|
procedure _Init;
|
||
|
function Decode(const rangeDecoder:PRangeDecoder;const posState:integer):integer;
|
||
|
end;
|
||
|
|
||
|
PLZMADecoder2 = ^TLZMADecoder2;
|
||
|
TLZMADecoder2 = object(TObj)
|
||
|
public
|
||
|
m_Decoders: array [0..$300-1] of smallint;
|
||
|
procedure _Init;
|
||
|
function DecodeNormal(const ArangeDecoder:PRangeDecoder):byte;
|
||
|
function DecodeWithMatchByte(const ArangeDecoder:PRangeDecoder;AmatchByte:byte):byte;
|
||
|
end;
|
||
|
|
||
|
TLZMALiteralDecoder = object(TObj)
|
||
|
public
|
||
|
m_Coders: array of PLZMADecoder2;
|
||
|
m_NumPrevBits:integer;
|
||
|
m_NumPosBits:integer;
|
||
|
m_PosMask:integer;
|
||
|
procedure _Create(const AnumPosBits, AnumPrevBits:integer);
|
||
|
procedure _Init;
|
||
|
function GetDecoder(const Apos:integer;const AprevByte:byte):PLZMADecoder2;
|
||
|
destructor Destroy;virtual;
|
||
|
end;
|
||
|
|
||
|
implementation
|
||
|
|
||
|
constructor TLZMALenDecoder.Create;
|
||
|
begin
|
||
|
New(m_HighCoder, Create(ULZMABase.kNumHighLenBits));
|
||
|
m_NumPosStates:=0;
|
||
|
end;
|
||
|
|
||
|
destructor TLZMALenDecoder.Destroy;
|
||
|
var i:integer;
|
||
|
begin
|
||
|
m_HighCoder.free;
|
||
|
for i:=low(m_LowCoder) to high(m_LowCoder) do begin
|
||
|
if m_LowCoder[i]<>nil then m_LowCoder[i].free;
|
||
|
if m_MidCoder[i]<>nil then m_MidCoder[i].free;
|
||
|
end;
|
||
|
inherited;
|
||
|
end;
|
||
|
|
||
|
procedure TLZMALenDecoder._Create(const numPosStates:integer);
|
||
|
begin
|
||
|
while m_NumPosStates < numPosStates do begin
|
||
|
New(m_LowCoder[m_NumPosStates], Create(ULZMABase.kNumLowLenBits));
|
||
|
New(m_MidCoder[m_NumPosStates], Create(ULZMABase.kNumMidLenBits));
|
||
|
inc(m_NumPosStates);
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
procedure TLZMALenDecoder._Init;
|
||
|
var posState:integer;
|
||
|
begin
|
||
|
URangeDecoder.InitBitModels(m_Choice);
|
||
|
for posState := 0 to m_NumPosStates-1 do begin
|
||
|
m_LowCoder[posState]._Init;
|
||
|
m_MidCoder[posState]._Init;
|
||
|
end;
|
||
|
m_HighCoder._Init;
|
||
|
end;
|
||
|
|
||
|
function TLZMALenDecoder.Decode(const rangeDecoder:PRangeDecoder;const posState:integer):integer;
|
||
|
var symbol:integer;
|
||
|
begin
|
||
|
if (rangeDecoder.DecodeBit(m_Choice, 0) = 0) then begin
|
||
|
result:=m_LowCoder[posState].Decode(rangeDecoder);
|
||
|
exit;
|
||
|
end;
|
||
|
symbol := ULZMABase.kNumLowLenSymbols;
|
||
|
if (rangeDecoder.DecodeBit(m_Choice, 1) = 0) then
|
||
|
symbol := symbol + m_MidCoder[posState].Decode(rangeDecoder)
|
||
|
else symbol := symbol + ULZMABase.kNumMidLenSymbols + m_HighCoder.Decode(rangeDecoder);
|
||
|
result:=symbol;
|
||
|
end;
|
||
|
|
||
|
procedure TLZMADecoder2._Init;
|
||
|
begin
|
||
|
URangeDecoder.InitBitModels(m_Decoders);
|
||
|
end;
|
||
|
|
||
|
function TLZMADecoder2.DecodeNormal(const ArangeDecoder:PRangeDecoder):byte;
|
||
|
var symbol:integer;
|
||
|
begin
|
||
|
symbol := 1;
|
||
|
repeat
|
||
|
symbol := (symbol shl 1) or ArangeDecoder.DecodeBit(m_Decoders, symbol);
|
||
|
until not (symbol < $100);
|
||
|
result:=symbol;
|
||
|
end;
|
||
|
|
||
|
function TLZMADecoder2.DecodeWithMatchByte(const ArangeDecoder:PRangeDecoder;AmatchByte:byte):byte;
|
||
|
var symbol:integer;
|
||
|
matchbit:integer;
|
||
|
bit:integer;
|
||
|
begin
|
||
|
symbol := 1;
|
||
|
repeat
|
||
|
matchBit := (AmatchByte shr 7) and 1;
|
||
|
AmatchByte := AmatchByte shl 1;
|
||
|
bit := ArangeDecoder.DecodeBit(m_Decoders, ((1 + matchBit) shl 8) + symbol);
|
||
|
symbol := (symbol shl 1) or bit;
|
||
|
if (matchBit <> bit) then begin
|
||
|
while (symbol < $100) do begin
|
||
|
symbol := (symbol shl 1) or ArangeDecoder.DecodeBit(m_Decoders, symbol);
|
||
|
end;
|
||
|
break;
|
||
|
end;
|
||
|
until not (symbol < $100);
|
||
|
result:=symbol;
|
||
|
end;
|
||
|
|
||
|
procedure TLZMALiteralDecoder._Create(const AnumPosBits, AnumPrevBits:integer);
|
||
|
var numStates,i:integer;
|
||
|
begin
|
||
|
if (length(m_Coders) <> 0) and (m_NumPrevBits = AnumPrevBits) and (m_NumPosBits = AnumPosBits) then
|
||
|
exit;
|
||
|
m_NumPosBits := AnumPosBits;
|
||
|
m_PosMask := (1 shl AnumPosBits) - 1;
|
||
|
m_NumPrevBits := AnumPrevBits;
|
||
|
numStates := 1 shl (m_NumPrevBits + m_NumPosBits);
|
||
|
setlength(m_Coders,numStates);
|
||
|
for i :=0 to numStates-1 do
|
||
|
New(m_Coders[i], Create);
|
||
|
end;
|
||
|
|
||
|
destructor TLZMALiteralDecoder.Destroy;
|
||
|
var i:integer;
|
||
|
begin
|
||
|
for i :=low(m_Coders) to high(m_Coders) do
|
||
|
if m_Coders[i]<>nil then m_Coders[i].Free;
|
||
|
inherited;
|
||
|
end;
|
||
|
|
||
|
procedure TLZMALiteralDecoder._Init;
|
||
|
var numStates,i:integer;
|
||
|
begin
|
||
|
numStates := 1 shl (m_NumPrevBits + m_NumPosBits);
|
||
|
for i := 0 to numStates -1 do
|
||
|
m_Coders[i]._Init;
|
||
|
end;
|
||
|
|
||
|
function TLZMALiteralDecoder.GetDecoder(const Apos:integer;const AprevByte:byte):PLZMADecoder2;
|
||
|
begin
|
||
|
result:=m_Coders[((Apos and m_PosMask) shl m_NumPrevBits) + ((AprevByte and $FF) shr (8 - m_NumPrevBits))];
|
||
|
end;
|
||
|
|
||
|
constructor TLZMADecoder.Create;
|
||
|
var i:integer;
|
||
|
begin
|
||
|
FOnProgress:=nil;
|
||
|
New(m_OutWindow, Create);
|
||
|
New(m_RangeDecoder, Create);
|
||
|
New(m_PosAlignDecoder, Create(ULZMABase.kNumAlignBits));
|
||
|
New(m_LenDecoder, Create);
|
||
|
New(m_RepLenDecoder, Create);
|
||
|
New(m_LiteralDecoder, Create);
|
||
|
m_DictionarySize:= -1;
|
||
|
m_DictionarySizeCheck:= -1;
|
||
|
for i := 0 to ULZMABase.kNumLenToPosStates -1 do
|
||
|
New(m_PosSlotDecoder[i], Create(ULZMABase.kNumPosSlotBits));
|
||
|
end;
|
||
|
|
||
|
destructor TLZMADecoder.Destroy;
|
||
|
var i:integer;
|
||
|
begin
|
||
|
m_OutWindow.Free;
|
||
|
m_RangeDecoder.Free;
|
||
|
m_PosAlignDecoder.Free;
|
||
|
m_LenDecoder.Free;
|
||
|
m_RepLenDecoder.Free;
|
||
|
m_LiteralDecoder.Free;
|
||
|
for i := 0 to ULZMABase.kNumLenToPosStates -1 do
|
||
|
m_PosSlotDecoder[i].Free;
|
||
|
end;
|
||
|
|
||
|
function TLZMADecoder.SetDictionarySize(const dictionarySize:integer):boolean;
|
||
|
begin
|
||
|
if dictionarySize < 0 then
|
||
|
result:=false
|
||
|
else begin
|
||
|
if m_DictionarySize <> dictionarySize then begin
|
||
|
m_DictionarySize := dictionarySize;
|
||
|
m_DictionarySizeCheck := max(m_DictionarySize, 1);
|
||
|
m_OutWindow._Create(max(m_DictionarySizeCheck, (1 shl 12)));
|
||
|
end;
|
||
|
result:=true;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
function TLZMADecoder.SetLcLpPb(const lc,lp,pb:integer):boolean;
|
||
|
var numPosStates:integer;
|
||
|
begin
|
||
|
if (lc > ULZMABase.kNumLitContextBitsMax) or (lp > 4) or (pb > ULZMABase.kNumPosStatesBitsMax) then begin
|
||
|
result:=false;
|
||
|
exit;
|
||
|
end;
|
||
|
m_LiteralDecoder._Create(lp, lc);
|
||
|
numPosStates := 1 shl pb;
|
||
|
m_LenDecoder._Create(numPosStates);
|
||
|
m_RepLenDecoder._Create(numPosStates);
|
||
|
m_PosStateMask := numPosStates - 1;
|
||
|
result:=true;
|
||
|
end;
|
||
|
|
||
|
procedure TLZMADecoder._Init;
|
||
|
var i:integer;
|
||
|
begin
|
||
|
m_OutWindow._Init(false);
|
||
|
|
||
|
URangeDecoder.InitBitModels(m_IsMatchDecoders);
|
||
|
URangeDecoder.InitBitModels(m_IsRep0LongDecoders);
|
||
|
URangeDecoder.InitBitModels(m_IsRepDecoders);
|
||
|
URangeDecoder.InitBitModels(m_IsRepG0Decoders);
|
||
|
URangeDecoder.InitBitModels(m_IsRepG1Decoders);
|
||
|
URangeDecoder.InitBitModels(m_IsRepG2Decoders);
|
||
|
URangeDecoder.InitBitModels(m_PosDecoders);
|
||
|
|
||
|
m_LiteralDecoder._Init();
|
||
|
for i := 0 to ULZMABase.kNumLenToPosStates -1 do
|
||
|
m_PosSlotDecoder[i]._Init;
|
||
|
m_LenDecoder._Init;
|
||
|
m_RepLenDecoder._Init;
|
||
|
m_PosAlignDecoder._Init;
|
||
|
m_RangeDecoder._Init;
|
||
|
end;
|
||
|
|
||
|
function TLZMADecoder.Code(const inStream,outStream:PStream;outSize:int64):boolean;
|
||
|
var state,rep0,rep1,rep2,rep3:integer;
|
||
|
nowPos64:int64;
|
||
|
prevByte:byte;
|
||
|
posState:integer;
|
||
|
decoder2:PLZMADecoder2;
|
||
|
len,distance,posSlot,numDirectBits:integer;
|
||
|
lpos:int64;
|
||
|
progint:int64;
|
||
|
begin
|
||
|
DoProgress(LPAMax,outSize);
|
||
|
m_RangeDecoder.SetStream(inStream);
|
||
|
m_OutWindow.SetStream(outStream);
|
||
|
_Init;
|
||
|
|
||
|
state := ULZMABase.StateInit;
|
||
|
rep0 := 0; rep1 := 0; rep2 := 0; rep3 := 0;
|
||
|
|
||
|
nowPos64 := 0;
|
||
|
prevByte := 0;
|
||
|
progint:=outsize div CodeProgressInterval;
|
||
|
lpos:=progint;
|
||
|
while (outSize < 0) or (nowPos64 < outSize) do begin
|
||
|
if (nowPos64 >=lpos) then begin
|
||
|
DoProgress(LPAPos,nowPos64);
|
||
|
lpos:=lpos+progint;
|
||
|
end;
|
||
|
posState := nowPos64 and m_PosStateMask;
|
||
|
if (m_RangeDecoder.DecodeBit(m_IsMatchDecoders, (state shl ULZMABase.kNumPosStatesBitsMax) + posState) = 0) then begin
|
||
|
decoder2 := m_LiteralDecoder.GetDecoder(nowPos64, prevByte);
|
||
|
if not ULZMABase.StateIsCharState(state) then
|
||
|
prevByte := decoder2.DecodeWithMatchByte(m_RangeDecoder, m_OutWindow.GetByte(rep0))
|
||
|
else prevByte := decoder2.DecodeNormal(m_RangeDecoder);
|
||
|
m_OutWindow.PutByte(prevByte);
|
||
|
state := ULZMABase.StateUpdateChar(state);
|
||
|
inc(nowPos64);
|
||
|
end else begin
|
||
|
if (m_RangeDecoder.DecodeBit(m_IsRepDecoders, state) = 1) then begin
|
||
|
len := 0;
|
||
|
if (m_RangeDecoder.DecodeBit(m_IsRepG0Decoders, state) = 0) then begin
|
||
|
if (m_RangeDecoder.DecodeBit(m_IsRep0LongDecoders, (state shl ULZMABase.kNumPosStatesBitsMax) + posState) = 0) then begin
|
||
|
state := ULZMABase.StateUpdateShortRep(state);
|
||
|
len := 1;
|
||
|
end;
|
||
|
end else begin
|
||
|
if m_RangeDecoder.DecodeBit(m_IsRepG1Decoders, state) = 0 then
|
||
|
distance := rep1
|
||
|
else begin
|
||
|
if (m_RangeDecoder.DecodeBit(m_IsRepG2Decoders, state) = 0) then
|
||
|
distance := rep2
|
||
|
else begin
|
||
|
distance := rep3;
|
||
|
rep3 := rep2;
|
||
|
end;
|
||
|
rep2 := rep1;
|
||
|
end;
|
||
|
rep1 := rep0;
|
||
|
rep0 := distance;
|
||
|
end;
|
||
|
if len = 0 then begin
|
||
|
len := m_RepLenDecoder.Decode(m_RangeDecoder, posState) + ULZMABase.kMatchMinLen;
|
||
|
state := ULZMABase.StateUpdateRep(state);
|
||
|
end;
|
||
|
end else begin
|
||
|
rep3 := rep2;
|
||
|
rep2 := rep1;
|
||
|
rep1 := rep0;
|
||
|
len := ULZMABase.kMatchMinLen + m_LenDecoder.Decode(m_RangeDecoder, posState);
|
||
|
state := ULZMABase.StateUpdateMatch(state);
|
||
|
posSlot := m_PosSlotDecoder[ULZMABase.GetLenToPosState(len)].Decode(m_RangeDecoder);
|
||
|
if posSlot >= ULZMABase.kStartPosModelIndex then begin
|
||
|
numDirectBits := (posSlot shr 1) - 1;
|
||
|
rep0 := ((2 or (posSlot and 1)) shl numDirectBits);
|
||
|
if posSlot < ULZMABase.kEndPosModelIndex then
|
||
|
rep0 := rep0 + UBitTreeDecoder.ReverseDecode(m_PosDecoders,
|
||
|
rep0 - posSlot - 1, m_RangeDecoder, numDirectBits)
|
||
|
else begin
|
||
|
rep0 := rep0 + (m_RangeDecoder.DecodeDirectBits(
|
||
|
numDirectBits - ULZMABase.kNumAlignBits) shl ULZMABase.kNumAlignBits);
|
||
|
rep0 := rep0 + m_PosAlignDecoder.ReverseDecode(m_RangeDecoder);
|
||
|
if rep0 < 0 then begin
|
||
|
if rep0 = -1 then
|
||
|
break;
|
||
|
result:=false;
|
||
|
exit;
|
||
|
end;
|
||
|
end;
|
||
|
end else rep0 := posSlot;
|
||
|
end;
|
||
|
if (rep0 >= nowPos64) or (rep0 >= m_DictionarySizeCheck) then begin
|
||
|
m_OutWindow.Flush();
|
||
|
result:=false;
|
||
|
exit;
|
||
|
end;
|
||
|
m_OutWindow.CopyBlock(rep0, len);
|
||
|
nowPos64 := nowPos64 + len;
|
||
|
prevByte := m_OutWindow.GetByte(0);
|
||
|
end;
|
||
|
end;
|
||
|
m_OutWindow.Flush();
|
||
|
m_OutWindow.ReleaseStream();
|
||
|
m_RangeDecoder.ReleaseStream();
|
||
|
DoProgress(LPAPos,nowPos64);
|
||
|
result:=true;
|
||
|
end;
|
||
|
|
||
|
function TLZMADecoder.SetDecoderProperties(const properties:array of byte):boolean;
|
||
|
var val,lc,remainder,lp,pb,dictionarysize,i:integer;
|
||
|
begin
|
||
|
if length(properties) < 5 then begin
|
||
|
result:=false;
|
||
|
exit;
|
||
|
end;
|
||
|
val := properties[0] and $FF;
|
||
|
lc := val mod 9;
|
||
|
remainder := val div 9;
|
||
|
lp := remainder mod 5;
|
||
|
pb := remainder div 5;
|
||
|
dictionarySize := 0;
|
||
|
for i := 0 to 3 do
|
||
|
dictionarySize := dictionarysize + ((properties[1 + i]) and $FF) shl (i * 8);
|
||
|
if (not SetLcLpPb(lc, lp, pb)) then begin
|
||
|
result:=false;
|
||
|
exit;
|
||
|
end;
|
||
|
result:=SetDictionarySize(dictionarySize);
|
||
|
end;
|
||
|
|
||
|
procedure TLZMADecoder.DoProgress(const Action:TLZMAProgressAction;const Value:integer);
|
||
|
begin
|
||
|
if assigned(fonprogress) then
|
||
|
fonprogress(action,value);
|
||
|
end;
|
||
|
|
||
|
end.
|