123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450 |
- { 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
- begin
- while LocTransforms.Count > 0 do
- begin
- if Assigned(PITSFTranformItem(LocTransforms.Items[0])^.Instance) then
- (PITSFTranformItem(LocTransforms.Items[0])^.Instance).Free;
- LocTransforms.Delete(0);
- end;
- LocTransforms.Free;
- end
- end.
|