mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-10-14 10:06:42 +02:00
443 lines
12 KiB
ObjectPascal
443 lines
12 KiB
ObjectPascal
{ Copyright (C) <2010> <Andrew Haines> itfstransform.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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301, USA.
|
|
}
|
|
{
|
|
See the file COPYING.modifiedLGPL, included in this distribution,
|
|
for details about the copyright.
|
|
}
|
|
unit ITSFTransform;
|
|
|
|
{ $DEFINE DEBUG_HELP2}
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, paslzx, ITOLITLSTypes;
|
|
|
|
type
|
|
TGetObject = function(AName: String): TMemoryStream of object;
|
|
|
|
TITSFTransformList = class;
|
|
TITSFTransform = class;
|
|
|
|
{ TITSFTransform }
|
|
TITSFTransform = class
|
|
private
|
|
FDataStream: TStream;
|
|
FPrefix: String;
|
|
FParentTransform: TITSFTransform;
|
|
public
|
|
GetObject: TGetObject; // GetObject(Name: String): TMemoryStream;
|
|
OutStream: TMemoryStream;
|
|
constructor Create(AGetObject: TGetObject; AParentTransform: TITSFTransform); virtual;
|
|
|
|
function WantData(APrefix: String; ADataStream: TStream; const DataStart, DataLength: Integer; AOutStream: TMemoryStream): Boolean; virtual;
|
|
|
|
property ParentTransform : TITSFTransform read FParentTransform;
|
|
property Prefix: String read FPrefix write FPrefix;
|
|
property DataStream: TStream read FDataStream write FDataStream;
|
|
class function GUID: TGuid; virtual;
|
|
class function GUIDString: String; virtual;
|
|
end;
|
|
|
|
TITSFTransformClass = class of TITSFTransform;
|
|
|
|
{ TPlainTransform }
|
|
|
|
TPlainTransform = class(TITSFTransform)
|
|
function WantData(APrefix: String; ADataStream: TStream; const DataStart, DataLength: Integer; AOutStream: TMemoryStream): Boolean; override;
|
|
end;
|
|
|
|
{ TLZXv3Transform }
|
|
|
|
TLZXv3Transform = class(TITSFTransform)
|
|
private
|
|
Entries: array of QWord;
|
|
Data: TLZXv3ControlData;
|
|
Table: TLZXv3ResetTable;
|
|
function GetControlData: Boolean;
|
|
function GetResetTable: Boolean;
|
|
|
|
function FindChunkCompressedSize(AEntryIndex: Integer): DWord;
|
|
function FindFirstChunkFromOffset(AOffset, ASize: QWord; out ChunkCount: DWord): Integer;
|
|
|
|
function ExtractChunks(AFirstChunkIndex, AChunkCount: DWord; out ExtractedOffsetStart: QWord): TMemoryStream;
|
|
public
|
|
function WantData(APrefix: String; ADataStream: TStream; const DataStart, DataLength: Integer; AOutStream: TMemoryStream): Boolean; override;
|
|
|
|
class function GUID: TGuid; override;
|
|
end;
|
|
|
|
{ TITSFTransformList }
|
|
|
|
TITSFTransformList = class(TFPList)
|
|
private
|
|
function GetTransform(AGuid: TGuid): TITSFTransformClass;
|
|
function GetTransformIndex(AIndex: DWord): TITSFTransformClass;
|
|
function GetTransformInstance(AIndex: DWord): TITSFTransform;
|
|
procedure SetTransformInstance(AIndex: DWord; const AValue: TITSFTransform);
|
|
public
|
|
function AddTransform(ATransform: TITSFTransformClass): Integer;
|
|
procedure Delete(AIndex: Integer);
|
|
property Transform[AGuid: TGuid]: TITSFTransformClass read GetTransform;
|
|
property TransformIndex[AIndex: DWord]: TITSFTransformClass read GetTransformIndex;
|
|
property TransformInstance[AIndex: DWord]: TITSFTransform read GetTransformInstance write SetTransformInstance;
|
|
end;
|
|
|
|
function RegisteredTransforms: TITSFTransformList;
|
|
|
|
|
|
implementation
|
|
uses
|
|
SysUtils;
|
|
|
|
var
|
|
LocTransforms: TITSFTransformList = nil;
|
|
|
|
type
|
|
PITSFTranformItem = ^TITSFTranformItem;
|
|
TITSFTranformItem = record
|
|
//Guid: TGuid;
|
|
Transform: TITSFTransformClass;
|
|
Instance: TITSFTransform;
|
|
end;
|
|
|
|
function RegisteredTransforms: TITSFTransformList;
|
|
begin
|
|
if LocTransforms = nil then
|
|
LocTransforms := TITSFTransformList.Create;
|
|
Result := LocTransforms;
|
|
end;
|
|
|
|
{ TITSFTransform }
|
|
|
|
constructor TITSFTransform.Create(AGetObject: TGetObject; AParentTransform: TITSFTransform);
|
|
begin
|
|
GetObject:=AGetObject;
|
|
FParentTransform := AParentTransform;
|
|
end;
|
|
|
|
function TITSFTransform.WantData(APrefix: String; ADataStream: TStream;
|
|
const DataStart, DataLength: Integer; AOutStream: TMemoryStream): Boolean;
|
|
begin
|
|
Prefix := APrefix;
|
|
DataStream := ADataStream;
|
|
OutStream := AOutStream;
|
|
{$IFDEF DEBUG_HELP2}
|
|
WriteLn('WantData Class = ', ClassName);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
class function TITSFTransform.GUID: TGuid;
|
|
const
|
|
AGuid: TGuid = '{00000000-0000-0000-0000-000000000000}';
|
|
begin
|
|
Result := AGuid;
|
|
end;
|
|
|
|
class function TITSFTransform.GUIDString: String;
|
|
begin
|
|
Result := GUIDToString(GUID);
|
|
end;
|
|
|
|
{ TITSFTransformList }
|
|
|
|
function TITSFTransformList.GetTransform(AGuid: TGuid): TITSFTransformClass;
|
|
var
|
|
Item: PITSFTranformItem;
|
|
i: Integer;
|
|
GUID: TGuid;
|
|
begin
|
|
Result := nil;
|
|
for i := 0 to Count-1 do
|
|
begin
|
|
Item := PITSFTranformItem(Items[i]);
|
|
GUID := Item^.Transform.GUID;
|
|
if CompareByte(GUID,AGuid, 16) = 0 then
|
|
Exit(Item^.Transform);
|
|
end;
|
|
end;
|
|
|
|
function TITSFTransformList.GetTransformIndex(AIndex: DWord): TITSFTransformClass;
|
|
begin
|
|
Result := PITSFTranformItem(Items[AIndex])^.Transform;
|
|
end;
|
|
|
|
function TITSFTransformList.GetTransformInstance(AIndex: DWord): TITSFTransform;
|
|
begin
|
|
Result := PITSFTranformItem(Items[AIndex])^.Instance;
|
|
end;
|
|
|
|
procedure TITSFTransformList.SetTransformInstance(AIndex: DWord;
|
|
const AValue: TITSFTransform);
|
|
begin
|
|
PITSFTranformItem(Items[AIndex])^.Instance := AValue;
|
|
end;
|
|
|
|
function TITSFTransformList.AddTransform(ATransform: TITSFTransformClass): Integer;
|
|
var
|
|
Item: PITSFTranformItem;
|
|
begin
|
|
if not Assigned(ATransform) then
|
|
Exit;
|
|
New(Item);
|
|
Item^.Transform:= ATransform;
|
|
Item^.Instance := nil;
|
|
Add(Item);
|
|
end;
|
|
|
|
procedure TITSFTransformList.Delete(AIndex: Integer);
|
|
var
|
|
Item: PITSFTranformItem;
|
|
begin
|
|
Item := PITSFTranformItem(Items[AIndex]);
|
|
Dispose(Item);
|
|
Inherited Delete(AIndex);
|
|
end;
|
|
|
|
{ TLZXv3Transform }
|
|
|
|
function TLZXv3Transform.FindFirstChunkFromOffset(AOffset, ASize: QWord; out ChunkCount: DWord): Integer;
|
|
var
|
|
EndChunk: DWord;
|
|
begin
|
|
Result := AOffset div Table.BlockSize;
|
|
EndChunk := (AOffset + ASize) div Table.BlockSize;
|
|
ChunkCount:=EndChunk-Result;
|
|
//if ChunkCount = 0 then
|
|
Inc(ChunkCount);
|
|
end;
|
|
|
|
function TLZXv3Transform.GetControlData: Boolean;
|
|
var
|
|
ControlDataStream: TStream;
|
|
ESize: LongWord;
|
|
begin
|
|
Result := False;
|
|
try
|
|
ControlDataStream := GetObject(Prefix+'ControlData');
|
|
|
|
if ControlDataStream = nil then
|
|
Exit;
|
|
|
|
|
|
ESize := NtoLE(ControlDataStream.ReadDWord);
|
|
while ESize <> 7 do
|
|
begin
|
|
ControlDataStream.Seek(ESize*4, soFromCurrent);
|
|
ESize := LEtoN(ControlDataStream.ReadDWord);
|
|
end;
|
|
if ESize = 7 then
|
|
ControlDataStream.Read(Data, SizeOf(TLZXv3ControlData));
|
|
|
|
finally
|
|
if Assigned(ControlDataStream) then
|
|
ControlDataStream.Free;
|
|
end;
|
|
Result := ESize = 7;
|
|
//WriteLn('GetControlData = ', REsult);
|
|
|
|
end;
|
|
|
|
function TLZXv3Transform.GetResetTable: Boolean;
|
|
var
|
|
WholePrefix: String;
|
|
ResetStream: TStream;
|
|
{$IFDEF ENDIAN_BIG}
|
|
i: Integer;
|
|
{$ENDIF}
|
|
begin
|
|
Result := False;
|
|
WholePrefix:=Prefix+'Transform/'+GUIDString+'/';
|
|
ResetStream := GetObject(WholePrefix+'InstanceData/ResetTable');
|
|
if ResetStream = nil then
|
|
Exit;
|
|
ResetStream.Read(Table, SizeOf(TLZXv3ResetTable));
|
|
SetLength(Entries, Table.EntryCount);
|
|
ResetStream.Read(Entries[0], Table.EntryCount*8);
|
|
{$IFDEF ENDIAN_BIG}
|
|
for i := Low(Entries) to High(Entries) do
|
|
Entries[i] := LEtoN(Entries[i]);
|
|
{$ENDIF}
|
|
{$IFDEF DEBUG_HELP2}
|
|
//for i := Low(Entries) to High(Entries) do
|
|
// WriteLn('Entry[',i,'] = ',Entries[i] ,' UnCompressStart = ', i*$8000);
|
|
{$ENDIF}
|
|
ResetStream.Free;
|
|
|
|
Result := True;
|
|
end;
|
|
|
|
function TLZXv3Transform.FindChunkCompressedSize(AEntryIndex: Integer): DWord;
|
|
begin
|
|
if AEntryIndex < High(Entries) then
|
|
Result := Entries[AEntryIndex+1] - Entries[AEntryIndex]
|
|
else
|
|
Result := DataStream.Size-Entries[AEntryIndex];
|
|
end;
|
|
|
|
function TLZXv3Transform.ExtractChunks(AFirstChunkIndex, AChunkCount: DWord;
|
|
out ExtractedOffsetStart: QWord): TMemoryStream;
|
|
var
|
|
LZX: PLZXState;
|
|
CStart,
|
|
CSize: DWord;
|
|
//CBuf: Pointer;
|
|
Buf: TMemoryStream;
|
|
CBuf: Pointer;
|
|
UBuf: Pointer;
|
|
USize: Dword;
|
|
URes: DWord;
|
|
WinCode: DWord;
|
|
WinSize: QWord;
|
|
BlockMask: Byte;
|
|
begin
|
|
BlockMask := (Data.ResetInterval shl 1) - 1;
|
|
|
|
// must start on a even numbered block
|
|
while (AFirstChunkIndex mod Data.ResetInterval <> 0) and (AFirstChunkIndex > 0) do
|
|
begin
|
|
Dec(AFirstChunkIndex);
|
|
Inc(AChunkCount);
|
|
end;
|
|
|
|
ExtractedOffsetStart := Table.BlockSize*AFirstChunkIndex;
|
|
|
|
{$IFDEF DEBUG_HELP2}
|
|
WriteLn('Getting Data, StartChunk=', AFirstChunkIndex,' Count = ', AChunkCount);
|
|
WriteLn('Version = ', Data.Version);
|
|
WriteLn('Window Size = ',Data.WindowSize);
|
|
WriteLn('Block Size = ',Hexstr(Table.BlockSize,16));
|
|
WriteLn('Block Size = ',Table.BlockSize);
|
|
{$ENDIF}
|
|
|
|
WinSize := (Data.WindowSize * Table.BlockSize);
|
|
WinCode := 0;
|
|
while WinSize > 1 do
|
|
begin
|
|
Inc(WinCode);
|
|
//WriteLn(HexStr(WinSize, 16));
|
|
WinSize := WinSize shr 1;
|
|
end;
|
|
|
|
LZX := LZXinit(WinCode);//ata.WindowSize);
|
|
|
|
CBuf := GetMem(Table.BlockSize);
|
|
UBuf := GetMem(Table.BlockSize);
|
|
|
|
Result := TMemoryStream.Create;
|
|
Buf := TMemoryStream.Create;
|
|
|
|
CStart := Entries[AFirstChunkIndex];
|
|
CSize := Entries[AFirstChunkIndex+AChunkCount]+FindChunkCompressedSize(AFirstChunkIndex+AChunkCount);
|
|
ParentTransform.WantData(Prefix, DataStream, CStart, CSize, Buf);
|
|
Buf.Position:=0;
|
|
|
|
while AChunkCount > 0 do
|
|
begin
|
|
Dec(AChunkCount);
|
|
|
|
CSize := FindChunkCompressedSize(AFirstChunkIndex);
|
|
|
|
CSize := Buf.Read(CBuf^, CSize);
|
|
if AFirstChunkIndex mod Data.ResetInterval = 0 then
|
|
begin
|
|
LZXreset(LZX);
|
|
{$IFDEF DEBUG_HELP2}
|
|
WriteLn('Reset LZX Window');
|
|
{$ENDIF}
|
|
end;
|
|
URes := LZXdecompress(LZX, CBuf, UBuf, CSize, Table.BlockSize);
|
|
//CBuf.Size := 0;
|
|
{$IFDEF DEBUG_HELP2}
|
|
WriteLn('Decompress = ', URes);
|
|
{$ENDIF}
|
|
|
|
Result.Write(UBuf^, Table.BlockSize);
|
|
Inc(AFirstChunkIndex);
|
|
end;
|
|
Buf.Free;
|
|
Freemem(UBuf);
|
|
Freemem(CBuf);
|
|
Result.Position:=0;
|
|
LZXteardown(LZX);
|
|
end;
|
|
|
|
|
|
function TLZXv3Transform.WantData(APrefix: String; ADataStream: TStream; const DataStart,
|
|
DataLength: Integer; AOutStream: TMemoryStream): Boolean;
|
|
var
|
|
LZXData: TLZXv3ControlData;
|
|
ResetTable: TLZXv3ResetTable;
|
|
ChunkStart,
|
|
ChunkCount: DWord;
|
|
RawChunks: TStream;
|
|
ChunkDataStart: QWord;
|
|
begin
|
|
inherited WantData(APrefix, ADataStream, DataStart, DataLength, AOutStream);
|
|
{$IFDEF DEBUG_HELP2}
|
|
WriteLn('WantData Pre=',APrefix,' DS=', DataStart,' DL=',DataLength);
|
|
{$ENDIF}
|
|
|
|
Result := False;
|
|
if not (GetControlData and GetResetTable) then
|
|
Exit;
|
|
{$IFDEF DEBUG_HELP2}
|
|
WriteLn('Got Needed Info');
|
|
{$ENDIF}
|
|
ChunkStart := FindFirstChunkFromOffset(DataStart,DataLength, ChunkCount);
|
|
|
|
RawChunks := ExtractChunks(ChunkStart, ChunkCount, ChunkDataStart);
|
|
|
|
RawChunks.Position := DataStart-ChunkDataStart;
|
|
AOutStream.CopyFrom(RawChunks, DataLength);
|
|
RawChunks.Free;
|
|
|
|
Result := True;
|
|
end;
|
|
|
|
class function TLZXv3Transform.GUID: TGuid;
|
|
const
|
|
AGuid: TGuid = '{0A9007C6-4076-11D3-8789-0000F8105754}';
|
|
begin
|
|
Result := AGuid;
|
|
end;
|
|
|
|
{ TPlainTransform }
|
|
|
|
function TPlainTransform.WantData(APrefix: String; ADataStream: TStream; const DataStart, DataLength: Integer;
|
|
AOutStream: TMemoryStream): Boolean;
|
|
begin
|
|
inherited WantData(APrefix, ADataStream, DataStart, DataLength, AOutStream);
|
|
ADataStream.Position:=DataStart;
|
|
AOutStream.CopyFrom(ADataStream, DataLength);
|
|
Result := True;
|
|
end;
|
|
|
|
initialization
|
|
RegisteredTransforms.AddTransform(TPlainTransform);
|
|
RegisteredTransforms.AddTransform(TLZXv3Transform);
|
|
|
|
finalization
|
|
if Assigned(LocTransforms) then
|
|
LocTransforms.Free;
|
|
|
|
end.
|
|
|
|
|