2
0

itsftransform.pas 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450
  1. { Copyright (C) <2010> <Andrew Haines> itfstransform.pas
  2. This library is free software; you can redistribute it and/or modify it
  3. under the terms of the GNU Library General Public License as published by
  4. the Free Software Foundation; either version 2 of the License, or (at your
  5. option) any later version.
  6. This program is distributed in the hope that it will be useful, but WITHOUT
  7. ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  8. FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
  9. for more details.
  10. You should have received a copy of the GNU Library General Public License
  11. along with this library; if not, write to the Free Software Foundation,
  12. Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301, USA.
  13. }
  14. {
  15. See the file COPYING.modifiedLGPL, included in this distribution,
  16. for details about the copyright.
  17. }
  18. unit ITSFTransform;
  19. { $DEFINE DEBUG_HELP2}
  20. {$mode objfpc}{$H+}
  21. interface
  22. uses
  23. Classes, paslzx, ITOLITLSTypes;
  24. type
  25. TGetObject = function(AName: String): TMemoryStream of object;
  26. TITSFTransformList = class;
  27. TITSFTransform = class;
  28. { TITSFTransform }
  29. TITSFTransform = class
  30. private
  31. FDataStream: TStream;
  32. FPrefix: String;
  33. FParentTransform: TITSFTransform;
  34. public
  35. GetObject: TGetObject; // GetObject(Name: String): TMemoryStream;
  36. OutStream: TMemoryStream;
  37. constructor Create(AGetObject: TGetObject; AParentTransform: TITSFTransform); virtual;
  38. function WantData(APrefix: String; ADataStream: TStream; const DataStart, DataLength: Integer; AOutStream: TMemoryStream): Boolean; virtual;
  39. property ParentTransform : TITSFTransform read FParentTransform;
  40. property Prefix: String read FPrefix write FPrefix;
  41. property DataStream: TStream read FDataStream write FDataStream;
  42. class function GUID: TGuid; virtual;
  43. class function GUIDString: String; virtual;
  44. end;
  45. TITSFTransformClass = class of TITSFTransform;
  46. { TPlainTransform }
  47. TPlainTransform = class(TITSFTransform)
  48. function WantData(APrefix: String; ADataStream: TStream; const DataStart, DataLength: Integer; AOutStream: TMemoryStream): Boolean; override;
  49. end;
  50. { TLZXv3Transform }
  51. TLZXv3Transform = class(TITSFTransform)
  52. private
  53. Entries: array of QWord;
  54. Data: TLZXv3ControlData;
  55. Table: TLZXv3ResetTable;
  56. function GetControlData: Boolean;
  57. function GetResetTable: Boolean;
  58. function FindChunkCompressedSize(AEntryIndex: Integer): DWord;
  59. function FindFirstChunkFromOffset(AOffset, ASize: QWord; out ChunkCount: DWord): Integer;
  60. function ExtractChunks(AFirstChunkIndex, AChunkCount: DWord; out ExtractedOffsetStart: QWord): TMemoryStream;
  61. public
  62. function WantData(APrefix: String; ADataStream: TStream; const DataStart, DataLength: Integer; AOutStream: TMemoryStream): Boolean; override;
  63. class function GUID: TGuid; override;
  64. end;
  65. { TITSFTransformList }
  66. TITSFTransformList = class(TFPList)
  67. private
  68. function GetTransform(AGuid: TGuid): TITSFTransformClass;
  69. function GetTransformIndex(AIndex: DWord): TITSFTransformClass;
  70. function GetTransformInstance(AIndex: DWord): TITSFTransform;
  71. procedure SetTransformInstance(AIndex: DWord; const AValue: TITSFTransform);
  72. public
  73. function AddTransform(ATransform: TITSFTransformClass): Integer;
  74. procedure Delete(AIndex: Integer);
  75. property Transform[AGuid: TGuid]: TITSFTransformClass read GetTransform;
  76. property TransformIndex[AIndex: DWord]: TITSFTransformClass read GetTransformIndex;
  77. property TransformInstance[AIndex: DWord]: TITSFTransform read GetTransformInstance write SetTransformInstance;
  78. end;
  79. function RegisteredTransforms: TITSFTransformList;
  80. implementation
  81. uses
  82. SysUtils;
  83. var
  84. LocTransforms: TITSFTransformList = nil;
  85. type
  86. PITSFTranformItem = ^TITSFTranformItem;
  87. TITSFTranformItem = record
  88. //Guid: TGuid;
  89. Transform: TITSFTransformClass;
  90. Instance: TITSFTransform;
  91. end;
  92. function RegisteredTransforms: TITSFTransformList;
  93. begin
  94. if LocTransforms = nil then
  95. LocTransforms := TITSFTransformList.Create;
  96. Result := LocTransforms;
  97. end;
  98. { TITSFTransform }
  99. constructor TITSFTransform.Create(AGetObject: TGetObject; AParentTransform: TITSFTransform);
  100. begin
  101. GetObject:=AGetObject;
  102. FParentTransform := AParentTransform;
  103. end;
  104. function TITSFTransform.WantData(APrefix: String; ADataStream: TStream;
  105. const DataStart, DataLength: Integer; AOutStream: TMemoryStream): Boolean;
  106. begin
  107. Prefix := APrefix;
  108. DataStream := ADataStream;
  109. OutStream := AOutStream;
  110. {$IFDEF DEBUG_HELP2}
  111. WriteLn('WantData Class = ', ClassName);
  112. {$ENDIF}
  113. end;
  114. class function TITSFTransform.GUID: TGuid;
  115. const
  116. AGuid: TGuid = '{00000000-0000-0000-0000-000000000000}';
  117. begin
  118. Result := AGuid;
  119. end;
  120. class function TITSFTransform.GUIDString: String;
  121. begin
  122. Result := GUIDToString(GUID);
  123. end;
  124. { TITSFTransformList }
  125. function TITSFTransformList.GetTransform(AGuid: TGuid): TITSFTransformClass;
  126. var
  127. Item: PITSFTranformItem;
  128. i: Integer;
  129. GUID: TGuid;
  130. begin
  131. Result := nil;
  132. for i := 0 to Count-1 do
  133. begin
  134. Item := PITSFTranformItem(Items[i]);
  135. GUID := Item^.Transform.GUID;
  136. if CompareByte(GUID,AGuid, 16) = 0 then
  137. Exit(Item^.Transform);
  138. end;
  139. end;
  140. function TITSFTransformList.GetTransformIndex(AIndex: DWord): TITSFTransformClass;
  141. begin
  142. Result := PITSFTranformItem(Items[AIndex])^.Transform;
  143. end;
  144. function TITSFTransformList.GetTransformInstance(AIndex: DWord): TITSFTransform;
  145. begin
  146. Result := PITSFTranformItem(Items[AIndex])^.Instance;
  147. end;
  148. procedure TITSFTransformList.SetTransformInstance(AIndex: DWord;
  149. const AValue: TITSFTransform);
  150. begin
  151. PITSFTranformItem(Items[AIndex])^.Instance := AValue;
  152. end;
  153. function TITSFTransformList.AddTransform(ATransform: TITSFTransformClass): Integer;
  154. var
  155. Item: PITSFTranformItem;
  156. begin
  157. if not Assigned(ATransform) then
  158. Exit;
  159. New(Item);
  160. Item^.Transform:= ATransform;
  161. Item^.Instance := nil;
  162. Add(Item);
  163. end;
  164. procedure TITSFTransformList.Delete(AIndex: Integer);
  165. var
  166. Item: PITSFTranformItem;
  167. begin
  168. Item := PITSFTranformItem(Items[AIndex]);
  169. Dispose(Item);
  170. Inherited Delete(AIndex);
  171. end;
  172. { TLZXv3Transform }
  173. function TLZXv3Transform.FindFirstChunkFromOffset(AOffset, ASize: QWord; out ChunkCount: DWord): Integer;
  174. var
  175. EndChunk: DWord;
  176. begin
  177. Result := AOffset div Table.BlockSize;
  178. EndChunk := (AOffset + ASize) div Table.BlockSize;
  179. ChunkCount:=EndChunk-Result;
  180. //if ChunkCount = 0 then
  181. Inc(ChunkCount);
  182. end;
  183. function TLZXv3Transform.GetControlData: Boolean;
  184. var
  185. ControlDataStream: TStream;
  186. ESize: LongWord;
  187. begin
  188. Result := False;
  189. try
  190. ControlDataStream := GetObject(Prefix+'ControlData');
  191. if ControlDataStream = nil then
  192. Exit;
  193. ESize := NtoLE(ControlDataStream.ReadDWord);
  194. while ESize <> 7 do
  195. begin
  196. ControlDataStream.Seek(ESize*4, soFromCurrent);
  197. ESize := LEtoN(ControlDataStream.ReadDWord);
  198. end;
  199. if ESize = 7 then
  200. ControlDataStream.Read(Data, SizeOf(TLZXv3ControlData));
  201. finally
  202. if Assigned(ControlDataStream) then
  203. ControlDataStream.Free;
  204. end;
  205. Result := ESize = 7;
  206. //WriteLn('GetControlData = ', REsult);
  207. end;
  208. function TLZXv3Transform.GetResetTable: Boolean;
  209. var
  210. WholePrefix: String;
  211. ResetStream: TStream;
  212. {$IFDEF ENDIAN_BIG}
  213. i: Integer;
  214. {$ENDIF}
  215. begin
  216. Result := False;
  217. WholePrefix:=Prefix+'Transform/'+GUIDString+'/';
  218. ResetStream := GetObject(WholePrefix+'InstanceData/ResetTable');
  219. if ResetStream = nil then
  220. Exit;
  221. ResetStream.Read(Table, SizeOf(TLZXv3ResetTable));
  222. SetLength(Entries, Table.EntryCount);
  223. ResetStream.Read(Entries[0], Table.EntryCount*8);
  224. {$IFDEF ENDIAN_BIG}
  225. for i := Low(Entries) to High(Entries) do
  226. Entries[i] := LEtoN(Entries[i]);
  227. {$ENDIF}
  228. {$IFDEF DEBUG_HELP2}
  229. //for i := Low(Entries) to High(Entries) do
  230. // WriteLn('Entry[',i,'] = ',Entries[i] ,' UnCompressStart = ', i*$8000);
  231. {$ENDIF}
  232. ResetStream.Free;
  233. Result := True;
  234. end;
  235. function TLZXv3Transform.FindChunkCompressedSize(AEntryIndex: Integer): DWord;
  236. begin
  237. if AEntryIndex < High(Entries) then
  238. Result := Entries[AEntryIndex+1] - Entries[AEntryIndex]
  239. else
  240. Result := DataStream.Size-Entries[AEntryIndex];
  241. end;
  242. function TLZXv3Transform.ExtractChunks(AFirstChunkIndex, AChunkCount: DWord;
  243. out ExtractedOffsetStart: QWord): TMemoryStream;
  244. var
  245. LZX: PLZXState;
  246. CStart,
  247. CSize: DWord;
  248. //CBuf: Pointer;
  249. Buf: TMemoryStream;
  250. CBuf: Pointer;
  251. UBuf: Pointer;
  252. USize: Dword;
  253. URes: DWord;
  254. WinCode: DWord;
  255. WinSize: QWord;
  256. BlockMask: Byte;
  257. begin
  258. BlockMask := (Data.ResetInterval shl 1) - 1;
  259. // must start on a even numbered block
  260. while (AFirstChunkIndex mod Data.ResetInterval <> 0) and (AFirstChunkIndex > 0) do
  261. begin
  262. Dec(AFirstChunkIndex);
  263. Inc(AChunkCount);
  264. end;
  265. ExtractedOffsetStart := Table.BlockSize*AFirstChunkIndex;
  266. {$IFDEF DEBUG_HELP2}
  267. WriteLn('Getting Data, StartChunk=', AFirstChunkIndex,' Count = ', AChunkCount);
  268. WriteLn('Version = ', Data.Version);
  269. WriteLn('Window Size = ',Data.WindowSize);
  270. WriteLn('Block Size = ',Hexstr(Table.BlockSize,16));
  271. WriteLn('Block Size = ',Table.BlockSize);
  272. {$ENDIF}
  273. WinSize := (Data.WindowSize * Table.BlockSize);
  274. WinCode := 0;
  275. while WinSize > 1 do
  276. begin
  277. Inc(WinCode);
  278. //WriteLn(HexStr(WinSize, 16));
  279. WinSize := WinSize shr 1;
  280. end;
  281. LZX := LZXinit(WinCode);//ata.WindowSize);
  282. CBuf := GetMem(Table.BlockSize);
  283. UBuf := GetMem(Table.BlockSize);
  284. Result := TMemoryStream.Create;
  285. Buf := TMemoryStream.Create;
  286. CStart := Entries[AFirstChunkIndex];
  287. CSize := Entries[AFirstChunkIndex+AChunkCount]+FindChunkCompressedSize(AFirstChunkIndex+AChunkCount);
  288. ParentTransform.WantData(Prefix, DataStream, CStart, CSize, Buf);
  289. Buf.Position:=0;
  290. while AChunkCount > 0 do
  291. begin
  292. Dec(AChunkCount);
  293. CSize := FindChunkCompressedSize(AFirstChunkIndex);
  294. CSize := Buf.Read(CBuf^, CSize);
  295. if AFirstChunkIndex mod Data.ResetInterval = 0 then
  296. begin
  297. LZXreset(LZX);
  298. {$IFDEF DEBUG_HELP2}
  299. WriteLn('Reset LZX Window');
  300. {$ENDIF}
  301. end;
  302. URes := LZXdecompress(LZX, CBuf, UBuf, CSize, Table.BlockSize);
  303. //CBuf.Size := 0;
  304. {$IFDEF DEBUG_HELP2}
  305. WriteLn('Decompress = ', URes);
  306. {$ENDIF}
  307. Result.Write(UBuf^, Table.BlockSize);
  308. Inc(AFirstChunkIndex);
  309. end;
  310. Buf.Free;
  311. Freemem(UBuf);
  312. Freemem(CBuf);
  313. Result.Position:=0;
  314. LZXteardown(LZX);
  315. end;
  316. function TLZXv3Transform.WantData(APrefix: String; ADataStream: TStream; const DataStart,
  317. DataLength: Integer; AOutStream: TMemoryStream): Boolean;
  318. var
  319. LZXData: TLZXv3ControlData;
  320. ResetTable: TLZXv3ResetTable;
  321. ChunkStart,
  322. ChunkCount: DWord;
  323. RawChunks: TStream;
  324. ChunkDataStart: QWord;
  325. begin
  326. inherited WantData(APrefix, ADataStream, DataStart, DataLength, AOutStream);
  327. {$IFDEF DEBUG_HELP2}
  328. WriteLn('WantData Pre=',APrefix,' DS=', DataStart,' DL=',DataLength);
  329. {$ENDIF}
  330. Result := False;
  331. if not (GetControlData and GetResetTable) then
  332. Exit;
  333. {$IFDEF DEBUG_HELP2}
  334. WriteLn('Got Needed Info');
  335. {$ENDIF}
  336. ChunkStart := FindFirstChunkFromOffset(DataStart,DataLength, ChunkCount);
  337. RawChunks := ExtractChunks(ChunkStart, ChunkCount, ChunkDataStart);
  338. RawChunks.Position := DataStart-ChunkDataStart;
  339. AOutStream.CopyFrom(RawChunks, DataLength);
  340. RawChunks.Free;
  341. Result := True;
  342. end;
  343. class function TLZXv3Transform.GUID: TGuid;
  344. const
  345. AGuid: TGuid = '{0A9007C6-4076-11D3-8789-0000F8105754}';
  346. begin
  347. Result := AGuid;
  348. end;
  349. { TPlainTransform }
  350. function TPlainTransform.WantData(APrefix: String; ADataStream: TStream; const DataStart, DataLength: Integer;
  351. AOutStream: TMemoryStream): Boolean;
  352. begin
  353. inherited WantData(APrefix, ADataStream, DataStart, DataLength, AOutStream);
  354. ADataStream.Position:=DataStart;
  355. AOutStream.CopyFrom(ADataStream, DataLength);
  356. Result := True;
  357. end;
  358. initialization
  359. RegisteredTransforms.AddTransform(TPlainTransform);
  360. RegisteredTransforms.AddTransform(TLZXv3Transform);
  361. finalization
  362. if Assigned(LocTransforms) then
  363. begin
  364. while LocTransforms.Count > 0 do
  365. begin
  366. if Assigned(PITSFTranformItem(LocTransforms.Items[0])^.Instance) then
  367. (PITSFTranformItem(LocTransforms.Items[0])^.Instance).Free;
  368. LocTransforms.Delete(0);
  369. end;
  370. LocTransforms.Free;
  371. end
  372. end.