You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1433 lines
39 KiB

  1. unit uutlCompression;
  2. { Package: Utils
  3. Prefix: utl - UTiLs
  4. Beschreibung: diese Unit enthält Stream-Wrapper die Daten de/komprimieren.
  5. Teile basierend auf gzio.pas}
  6. {$mode objfpc}{$H+}
  7. {$TYPEDADDRESS ON}
  8. {$WRITEABLECONST OFF}
  9. {$DEFINE HAVE_ZLIB}
  10. {$DEFINE HAVE_LIBLZMA}
  11. {$IFNDEF WINDOWS}
  12. {$WARNING LibLZMA only supported on Windows for now}
  13. {$UNDEF HAVE_LIBLZMA}
  14. {$ENDIF}
  15. interface
  16. uses
  17. Classes, SysUtils
  18. {$IFDEF HAVE_ZLIB}
  19. , zbase, zinflate, zdeflate
  20. {$ENDIF};
  21. const
  22. Z_BUFSIZE = 16384;
  23. type
  24. TutlCustomCompressedStream = class(TOwnerStream)
  25. private
  26. fWriting: boolean;
  27. fStartPos: Int64;
  28. fKnownSize: Int64;
  29. protected
  30. function GetSize: Int64; override;
  31. function GetTotalOut: QWord; virtual; abstract;
  32. function GetTotalIn: QWord; virtual; abstract;
  33. public
  34. constructor Create(ASource: TStream);
  35. property Writing: boolean read fWriting;
  36. function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; override; overload;
  37. function Rewind: integer; virtual; abstract;
  38. end;
  39. {$IFDEF HAVE_ZLIB}
  40. const
  41. { gzip magic header }
  42. gz_magic : array[0..1] of byte = ($1F, $8B);
  43. { gzip flag byte }
  44. GZ_ASCII_FLAG = $01; { bit 0 set: file probably ascii text }
  45. GZ_HEAD_CRC = $02; { bit 1 set: header CRC present }
  46. GZ_EXTRA_FIELD = $04; { bit 2 set: extra field present }
  47. GZ_ORIG_NAME = $08; { bit 3 set: original file name present }
  48. GZ_COMMENT = $10; { bit 4 set: file comment present }
  49. GZ_RESERVED = $E0; { bits 5..7: reserved }
  50. { gzip os code byte }
  51. GZ_OS_FAT = $00;
  52. GZ_OS_AMIGA = $01;
  53. GZ_OS_VMS = $02;
  54. GZ_OS_UNIX = $03;
  55. GZ_OS_VMCMS = $04;
  56. GZ_OS_ATARI_TOS = $05;
  57. GZ_OS_HPFS = $06;
  58. GZ_OS_MACINTOSH = $07;
  59. GZ_OS_ZSYSTEM = $08;
  60. GZ_OS_CPM = $09;
  61. GZ_OS_TOPS20 = $0A;
  62. GZ_OS_NTFS = $0B;
  63. GZ_OS_QDOS = $0C;
  64. GZ_OS_ACORNRISC = $0D;
  65. GZ_OS_UNKNOWN = $FF;
  66. type
  67. TutlCustomZLibStream = class(TutlCustomCompressedStream)
  68. private
  69. fStream: z_stream;
  70. fMode: Char;
  71. fLevel: Integer;
  72. fStrategy: Integer;
  73. fBuffer: array[0..Z_BUFSIZE-1] of Byte;
  74. fBufStart: Pointer;
  75. protected
  76. function zlibInitRead: LongInt; virtual; abstract;
  77. function zlibInitWrite: LongInt; virtual; abstract;
  78. function zlibFinishRead: LongInt; virtual;
  79. function zlibFinishWrite: LongInt; virtual;
  80. function DoFlush(flags: integer): integer;
  81. function GetTotalIn: QWord; override;
  82. function GetTotalOut: QWord; override;
  83. public
  84. constructor Create(aBaseStream: TStream; aMode: string);
  85. destructor Destroy; override;
  86. property Mode: Char read fMode;
  87. property Level: Integer read fLevel;
  88. property Strategy: Integer read fStrategy;
  89. function Read(var Buffer; Count: Longint): Longint; override;
  90. function Write(const Buffer; Count: Longint): Longint; override;
  91. procedure Flush(flags: integer);
  92. function Rewind: integer; override;
  93. end;
  94. TutlZLibStream = class(TutlCustomZLibStream)
  95. protected
  96. function zlibInitRead: LongInt; override;
  97. function zlibInitWrite: LongInt; override;
  98. end;
  99. TutlZipStream = class(TutlCustomZLibStream)
  100. protected
  101. function zlibInitRead: LongInt; override;
  102. function zlibInitWrite: LongInt; override;
  103. end;
  104. TGZHeader = packed record
  105. magic: array[0..1] of byte;
  106. method: Byte;
  107. flags: Byte;
  108. time: DWord;
  109. xflags: byte;
  110. oscode: byte;
  111. end;
  112. TGZFooter = packed record
  113. crc32: DWord;
  114. size: DWord;
  115. end;
  116. TutlGZipStream = class(TutlZipStream)
  117. private
  118. fOrigFileName: string;
  119. fComment: string;
  120. fOrigTimestamp: TDateTime;
  121. fCRC: DWord;
  122. protected
  123. function zlibInitRead: LongInt; override;
  124. function zlibInitWrite: LongInt; override;
  125. function zlibFinishWrite: LongInt; override;
  126. procedure WriteHeader;
  127. procedure CheckHeader;
  128. public
  129. constructor Create(aBaseStream: TStream; aMode: string);
  130. property OrigFileame: string read fOrigFileName;
  131. property OrigTimestamp: TDateTime read fOrigTimestamp;
  132. property Comment: string read fComment;
  133. function Write(const Buffer; Count: Longint): Longint; override;
  134. procedure ReadFooter;
  135. end;
  136. {$ENDIF HAVE_ZLIB}
  137. {$IFDEF HAVE_LIBLZMA}
  138. const
  139. LZMA_DLL = 'liblzma.dll';
  140. LZMA_VER = 50020012;
  141. { Return values used by several functions in liblzma }
  142. LZMA_OK = 0;
  143. LZMA_STREAM_END = 1;
  144. LZMA_NO_CHECK = 2;
  145. LZMA_UNSUPPORTED_CHECK = 3;
  146. LZMA_GET_CHECK = 4;
  147. LZMA_MEM_ERROR = 5;
  148. LZMA_MEMLIMIT_ERROR = 6;
  149. LZMA_FORMAT_ERROR = 7;
  150. LZMA_OPTIONS_ERROR = 8;
  151. LZMA_DATA_ERROR = 9;
  152. LZMA_BUF_ERROR = 10;
  153. LZMA_PROG_ERROR = 11;
  154. { Type of the integrity check (Check ID) }
  155. LZMA_CHECK_NONE = 0;
  156. LZMA_CHECK_CRC32 = 1;
  157. LZMA_CHECK_CRC64 = 4;
  158. LZMA_CHECK_SHA256 = 10;
  159. { Type of the integrity check (Check ID) }
  160. LZMA_INDEX_ITER_ANY = 0;
  161. LZMA_INDEX_ITER_STREAM = 1;
  162. LZMA_INDEX_ITER_BLOCK = 2;
  163. LZMA_INDEX_ITER_NONEMPTY_BLOCK = 3;
  164. { The 'action' argument for lzma_code() }
  165. LZMA_RUN = 0;
  166. LZMA_SYNC_FLUSH = 1;
  167. LZMA_FULL_FLUSH = 2;
  168. LZMA_FULL_BARRIER = 4;
  169. LZMA_FINISH = 3;
  170. { Encoding }
  171. LZMA_PRESET_EXTREME = $80000000;
  172. { Decoding }
  173. LZMA_TELL_NO_CHECK = $01;
  174. LZMA_TELL_UNSUPPORTED_CHECK = $02;
  175. LZMA_TELL_ANY_CHECK = $04;
  176. LZMA_IGNORE_CHECK = $10;
  177. LZMA_CONCATENATED = $08;
  178. { Misc}
  179. LZMA_STREAM_HEADER_SIZE = 12;
  180. LZMA_BLOCK_HEADER_SIZE_MIN = 8;
  181. LZMA_BLOCK_HEADER_SIZE_MAX = 1024;
  182. LZMA_CHECK_SIZE_MAX = 64;
  183. LZMA_FILTERS_MAX = 4;
  184. LZMA_VLI_UNKNOWN: QWord = high(QWord);
  185. type
  186. {$PackRecords C}
  187. lzma_ret = integer;
  188. lzma_action = integer;
  189. lzma_check = integer;
  190. lzma_reserved_enum = (_LZMA_RESERVED_ENUM);
  191. lzma_index_iter_mode = integer;
  192. lzma_vli = QWord;
  193. lzma_bool = ByteBool;
  194. {*
  195. * Custom functions for memory handling.
  196. *}
  197. TAlloc = function(opaque: Pointer; Items, Size: size_t): Pointer; cdecl;
  198. TFree = procedure(opaque, Block: Pointer); cdecl;
  199. lzma_allocator = record
  200. XZalloc : TAlloc;
  201. XZfree : TFree;
  202. opaque : pointer;
  203. end;
  204. p_lzma_allocator = ^lzma_allocator;
  205. {*
  206. * Passing data to and from liblzma.
  207. *}
  208. lzma_stream = record
  209. next_in : PByte; //Pointer to the next input byte.
  210. avail_in : size_t; //Number of available input bytes in next_in.
  211. total_in : QWord; //Total number of bytes read by liblzma.
  212. next_out : PByte; //Pointer to the next output position.
  213. avail_out : size_t; //Amount of free space in next_out.
  214. total_out : QWord; //Total number of bytes written by liblzma.
  215. //Custom memory allocation functions
  216. //In most cases this is nil which makes liblzma use
  217. //the standard malloc() and free().
  218. allocator : p_lzma_allocator; //pointer;
  219. //Internal state is not visible to applications.
  220. internal : pointer;
  221. //Reserved space to allow possible future extensions without
  222. //breaking the ABI. Excluding the initialization of this structure,
  223. //you should not touch these, because the names of these variables
  224. //may change.
  225. reserved_ptr1 : pointer;
  226. reserved_ptr2 : pointer;
  227. reserved_ptr3 : pointer;
  228. reserved_ptr4 : pointer;
  229. reserved_int1 : QWord;
  230. reserved_int2 : QWord;
  231. reserved_int3 : size_t;
  232. reserved_int4 : size_t;
  233. reserved_enum1 : lzma_reserved_enum;
  234. reserved_enum2 : lzma_reserved_enum;
  235. end;
  236. p_lzma_stream = ^lzma_stream;
  237. lzma_stream_flags = record
  238. version: DWord;
  239. backward_size: lzma_vli;
  240. check: lzma_check;
  241. reserved_enum : array[1..4] of lzma_reserved_enum;
  242. reserved_bool : array[1..8] of lzma_bool;
  243. reserved_int : array[1..2] of DWord;
  244. end;
  245. p_lzma_stream_flags = ^lzma_stream_flags;
  246. p_lzma_index = Pointer;
  247. pp_lzma_index = ^p_lzma_index;
  248. lzma_index_iter = record
  249. stream: record
  250. flags: p_lzma_stream_flags;
  251. reserved_ptr1,
  252. reserved_ptr2,
  253. reserved_ptr3: Pointer;
  254. number,
  255. block_count,
  256. compressed_offset,
  257. uncompressed_offset,
  258. compressed_size,
  259. uncompressed_size,
  260. padding,
  261. reserved_vli1,
  262. reserved_vli2,
  263. reserved_vli3,
  264. reserved_vli4: lzma_vli;
  265. end;
  266. block: record
  267. number_in_file,
  268. compressed_file_offset,
  269. uncompressed_file_offset,
  270. number_in_stream,
  271. compressed_stream_offset,
  272. uncompressed_stream_offset,
  273. uncompressed_size,
  274. unpadded_size,
  275. total_size,
  276. reserved_vli1,
  277. reserved_vli2,
  278. reserved_vli3,
  279. reserved_vli4: lzma_vli;
  280. reserved_ptr1,
  281. reserved_ptr2,
  282. reserved_ptr3,
  283. reserved_ptr4: Pointer;
  284. end;
  285. internal: array[1..6] of record case byte of
  286. 0: (p: Pointer);
  287. 1: (s: size_t);
  288. 2: (v: lzma_vli);
  289. end;
  290. end;
  291. lzma_filter = record
  292. id: lzma_vli;
  293. options: Pointer;
  294. end;
  295. p_lzma_filter = ^lzma_filter;
  296. lzma_block = record
  297. version: dword;
  298. header_size: DWord;
  299. check: lzma_check;
  300. compressed_size: lzma_vli;
  301. uncompressed_size: lzma_vli;
  302. filters: p_lzma_filter;
  303. raw_check: array [0..LZMA_CHECK_SIZE_MAX-1] of byte;
  304. reserved_ptr1,
  305. reserved_ptr2,
  306. reserved_ptr3: Pointer;
  307. reserved_int1,
  308. reserved_int2: DWord;
  309. reserved_int3,
  310. reserved_int4,
  311. reserved_int5,
  312. reserved_int6,
  313. reserved_int7,
  314. reserved_int8: lzma_vli;
  315. reserved_enum1,
  316. reserved_enum2,
  317. reserved_enum3,
  318. reserved_enum4: lzma_reserved_enum;
  319. ignore_check: lzma_bool;
  320. reserved_bool2,
  321. reserved_bool3,
  322. reserved_bool4,
  323. reserved_bool5,
  324. reserved_bool6,
  325. reserved_bool7,
  326. reserved_bool8: lzma_bool;
  327. end;
  328. {$PackRecords Default}
  329. TlibLZMA = object
  330. Handle: THandle;
  331. PascalAllocator: lzma_allocator;
  332. lzma_version_number: function(): DWord; cdecl;
  333. { Initialize .xz Stream encoder using a preset number. }
  334. lzma_easy_encoder: function(var strm: lzma_stream; preset: DWord; check: lzma_check): lzma_ret; cdecl;
  335. { Initialize .xz Stream decoder }
  336. lzma_stream_decoder: function(var strm: lzma_stream; memlimit: QWord; flags: DWord): lzma_ret; cdecl;
  337. lzma_auto_decoder: function(var strm: lzma_stream; memlimit: QWord; flags: DWord): lzma_ret; cdecl;
  338. {*
  339. * Encode or decode data.
  340. *
  341. * Once the lzma_stream has been successfully initialized (e.g. with
  342. * lzma_stream_encoder()), the actual encoding or decoding is done
  343. * using this function. The application has to update strm->next_in,
  344. * strm->avail_in, strm->next_out, and strm->avail_out to pass input
  345. * to and get output from liblzma.
  346. *}
  347. lzma_code: function(var strm: lzma_stream; action: lzma_action): lzma_ret; cdecl;
  348. { Free memory allocated for the coder data structures }
  349. lzma_end: procedure(var strm: lzma_stream); cdecl;
  350. { All-in-one functions }
  351. lzma_easy_buffer_encode: function(preset: DWord; check: lzma_check; allocator: p_lzma_allocator; in_: PChar; in_size: size_t; out_: PChar; out_pos: pointer; out_size: size_t): lzma_ret; cdecl;
  352. lzma_stream_buffer_decode: function(memlimit: PQWord; flags: DWord; allocator: p_lzma_allocator; in_: PChar; in_pos: pointer; in_size: size_t; out_: PChar; out_pos: pointer; out_size: size_t): lzma_ret; cdecl;
  353. { Stream decoding }
  354. lzma_stream_footer_decode: function (out options: lzma_stream_flags; fin: PByte): lzma_ret; cdecl;
  355. lzma_stream_header_decode: function (out options: lzma_stream_flags; fin: PByte): lzma_ret; cdecl;
  356. lzma_stream_flags_compare: function (var a, b: lzma_stream_flags): lzma_ret; cdecl;
  357. { Index access }
  358. lzma_index_decoder: function(var strm: lzma_stream; i: pp_lzma_index; memlimit: QWord): lzma_ret; cdecl;
  359. lzma_index_buffer_decode: function(var i: p_lzma_index; var memlimit: QWord; allocator: p_lzma_allocator; in_: PByte; var in_pos: size_t; in_size: size_t): lzma_ret; cdecl;
  360. lzma_index_uncompressed_size: function(i: p_lzma_index): lzma_vli; cdecl;
  361. lzma_index_end: procedure(i: p_lzma_index; allocator: p_lzma_allocator); cdecl;
  362. lzma_index_cat: function(dest, src: p_lzma_index; const allocator: p_lzma_allocator): lzma_ret; cdecl;
  363. lzma_index_stream_flags: function(i: p_lzma_index; var flags: lzma_stream_flags): lzma_ret; cdecl;
  364. lzma_index_stream_padding: function(i: p_lzma_index; padding: lzma_vli): lzma_ret; cdecl;
  365. lzma_index_total_size: function(i: p_lzma_index): lzma_vli; cdecl;
  366. { Index iterator }
  367. lzma_index_iter_init: procedure (var iter:lzma_index_iter; i: p_lzma_index); cdecl;
  368. lzma_index_iter_locate: function (var iter:lzma_index_iter; target: lzma_vli): lzma_bool; cdecl;
  369. lzma_index_iter_next: function (var iter:lzma_index_iter; mode: lzma_index_iter_mode): lzma_bool; cdecl;
  370. { Block access }
  371. lzma_block_decoder: function(var strm: lzma_stream; var i: lzma_block): lzma_ret; cdecl;
  372. lzma_block_header_decode: function(var i: lzma_block; const allocator: p_lzma_allocator; fin: PByte): lzma_ret; cdecl;
  373. lzma_block_compressed_size: function(var i: lzma_block; unpadded_size: lzma_vli): lzma_ret; cdecl;
  374. function lzma_block_header_size_decode(b: dword): dword;
  375. procedure Load;
  376. procedure Unload;
  377. end;
  378. TutlXZStream = class(TutlCustomCompressedStream)
  379. private
  380. fStream: lzma_stream;
  381. fMode: Char;
  382. fLevel: DWORD;
  383. fBuffer: array[0..Z_BUFSIZE-1] of Byte;
  384. fBufStart: Pointer;
  385. private
  386. fIndex: p_lzma_index;
  387. fStreamNr,
  388. fBlockNr: DWord;
  389. fMaxUncompBlockSize: QWord;
  390. fUncompPos: QWord;
  391. fBlock: lzma_block;
  392. fBlockOffs: Int64;
  393. fFilters: array[0..LZMA_FILTERS_MAX] of lzma_filter; // MUST be 1 entry longer than LZMA_FILTERS_MAX for LZMA_VLI_UNKNOWN terminator
  394. protected
  395. function DoFlush(flags: integer): integer;
  396. function GetTotalIn: QWord; override;
  397. function GetTotalOut: QWord; override;
  398. function GetPosition: Int64; override;
  399. procedure BlockEnd;
  400. function ParseIndexes: boolean;
  401. procedure IterateIndexes;
  402. public
  403. constructor Create(aBaseStream: TStream; aMode: string);
  404. destructor Destroy; override;
  405. property Mode: Char read fMode;
  406. property Level: DWORD read fLevel;
  407. function Read(var Buffer; Count: Longint): Longint; override;
  408. function Write(const Buffer; Count: Longint): Longint; override;
  409. procedure Flush({%H-}flags: integer);
  410. function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; override; overload;
  411. function Rewind: integer; override;
  412. public
  413. class function CheckLibraryAvailable: boolean;
  414. end;
  415. {$ENDIF HAVE_LIBLZMA}
  416. resourcestring
  417. sInvalidMode = 'Invalid mode specified';
  418. sFailedInitZLib = 'Failed to init ZLIB';
  419. sGZipNoHeader = 'No GZip header found';
  420. sGZIPUnsupportedMethod = 'Unsupported Method';
  421. sLibLZMANotFound = 'liblzma not found or bad version: %s';
  422. sLZMAErrorOpen = 'Error opening XZ file';
  423. implementation
  424. uses
  425. dateutils, crc, dynlibs;
  426. {$IFDEF HAVE_LIBLZMA}
  427. var
  428. libLZMA: TlibLZMA;
  429. {$ENDIF HAVE_LIBLZMA}
  430. { TutlCustomCompressedStream }
  431. constructor TutlCustomCompressedStream.Create(ASource: TStream);
  432. begin
  433. inherited Create(ASource);
  434. fWriting:= false;
  435. fStartPos:= ASource.Position;
  436. fKnownSize:= -1;
  437. end;
  438. function TutlCustomCompressedStream.Seek(const Offset: Int64; Origin: TSeekOrigin): Int64;
  439. var
  440. ofs: Int64;
  441. sz : int64;
  442. tmp: array [0..Z_BUFSIZE-1] of byte;
  443. begin
  444. if Origin = soEnd then begin
  445. if fWriting then begin
  446. if Offset <= 0 then
  447. // No going back
  448. Exit(-1)
  449. else
  450. // Current = End
  451. Exit(Seek(Offset, soCurrent));
  452. end;
  453. // computes size, if possible
  454. ofs:= GetSize - Offset;
  455. // if we know the size now, translate to seek from beginning
  456. if fKnownSize >= 0 then
  457. Exit(Seek(ofs, soBeginning));
  458. Exit(-1);
  459. end;
  460. ofs:= Offset;
  461. if fWriting then begin
  462. if Origin = soBeginning then
  463. dec(ofs, GetTotalOut);
  464. if ofs < 0 then
  465. Exit(-1);
  466. { At this point, ofs is the number of zero bytes to write. }
  467. FillChar({%H-}tmp[0], Z_BUFSIZE, 0);
  468. while (ofs > 0) do begin
  469. sz := Z_BUFSIZE;
  470. if (ofs < Z_BUFSIZE) then
  471. sz := ofs;
  472. sz:= Write(tmp[0], sz);
  473. if (size <= 0) then
  474. Exit(-1);
  475. dec(ofs,sz);
  476. end;
  477. Result:= GetTotalIn;
  478. end else begin
  479. { compute absolute position }
  480. if Origin = soCurrent then
  481. inc(ofs, GetTotalOut);
  482. if ofs < 0 then
  483. Exit(-1);
  484. { For a negative seek, rewind and use positive seek }
  485. if (ofs >= GetTotalOut) then
  486. dec(ofs, GetTotalOut)
  487. else if (Rewind <> 0) then begin
  488. Exit(-1);
  489. end;
  490. { ofs is now the number of bytes to skip. }
  491. while (ofs > 0) do begin
  492. sz := Z_BUFSIZE;
  493. if (ofs < Z_BUFSIZE) then
  494. sz := ofs;
  495. sz := Read(tmp[0], sz);
  496. if (sz <= 0) then
  497. Exit(-1);
  498. dec(ofs, sz);
  499. end;
  500. Result:= GetTotalOut;
  501. end;
  502. end;
  503. function TutlCustomCompressedStream.GetSize: Int64;
  504. var
  505. tmp: array[0..Z_BUFSIZE-1] of byte;
  506. sz: Int64;
  507. begin
  508. if fKnownSize < 0 then begin
  509. if fKnownSize < -1 then
  510. // we tried before and it was not possible
  511. Exit(-1);
  512. // read until EOF
  513. repeat
  514. sz := Read({%H-}tmp[0], Z_BUFSIZE);
  515. if sz < 0 then begin
  516. fKnownSize:= -2;
  517. Exit(-1);
  518. end;
  519. until sz < Z_BUFSIZE;
  520. fKnownSize:= GetTotalOut;
  521. end;
  522. Result:= fKnownSize;
  523. end;
  524. {$IFDEF HAVE_ZLIB}
  525. { TutlCustomZLibStream }
  526. constructor TutlCustomZLibStream.Create(aBaseStream: TStream; aMode: string);
  527. var
  528. err: Integer;
  529. m: Char;
  530. begin
  531. inherited Create(aBaseStream);
  532. fMode:= #0;
  533. fLevel:= Z_DEFAULT_COMPRESSION;
  534. fStrategy:= Z_DEFAULT_STRATEGY;
  535. for m in aMode do begin
  536. case m of
  537. 'r',
  538. 'w': fMode:= m;
  539. '0'..'9': fLevel:= Ord(m) - Ord('0');
  540. 'f': fStrategy:= Z_FILTERED;
  541. 'h': fStrategy:= Z_HUFFMAN_ONLY;
  542. end;
  543. end;
  544. if not (fMode in ['r', 'w']) then
  545. raise EFOpenError.Create(sInvalidMode);
  546. fWriting:= fMode = 'w';
  547. fBufStart:= @fBuffer[0];
  548. fStream:= Default(z_stream);
  549. if fWriting then begin
  550. err:= zlibInitWrite;
  551. fStream.next_out:= fBufStart;
  552. end else begin
  553. err:= zlibInitRead;
  554. fStream.next_in:= fBufStart;
  555. end;
  556. if err <> Z_OK then
  557. raise EFOpenError.Create(sFailedInitZLib);
  558. fStream.avail_out:= Z_BUFSIZE;
  559. fStartPos:= Source.Position;
  560. end;
  561. destructor TutlCustomZLibStream.Destroy;
  562. begin
  563. if fWriting then begin
  564. DoFlush(Z_FINISH);
  565. zlibFinishWrite;
  566. end else begin
  567. zlibFinishRead;
  568. end;
  569. inherited Destroy;
  570. end;
  571. function TutlCustomZLibStream.Read(var Buffer; Count: Longint): Longint;
  572. var
  573. res: Int64;
  574. begin
  575. Result:= 0;
  576. fStream.next_out := @Buffer;
  577. fStream.avail_out := Count;
  578. while (fStream.avail_out > 0) do begin
  579. if fStream.avail_in = 0 then begin
  580. res:= Source.Read(fBufStart^, Z_BUFSIZE);
  581. if res = 0 then begin
  582. // reached input end, return
  583. Result:= Count - fStream.avail_out;
  584. Exit;
  585. end;
  586. fStream.avail_in := res;
  587. fStream.next_in := fBufStart;
  588. end;
  589. res:= inflate(fStream, Z_SYNC_FLUSH);
  590. if res = Z_STREAM_END then
  591. Exit(Count - fStream.avail_out)
  592. else if res < 0 then
  593. Exit(res);
  594. end;
  595. Result := Count;
  596. end;
  597. function TutlCustomZLibStream.Write(const Buffer; Count: Longint): Longint;
  598. var
  599. res: int64;
  600. begin
  601. fStream.next_in := @Buffer;
  602. fStream.avail_in := Count;
  603. while (fStream.avail_in > 0) do begin
  604. res:= deflate(fStream, 0);
  605. if res < 0 then
  606. Exit(res);
  607. if fStream.avail_out = 0 then begin
  608. Source.WriteBuffer(fBufStart^, Z_BUFSIZE);
  609. fStream.next_out:= fBufStart;
  610. fStream.avail_out:= Z_BUFSIZE;
  611. end;
  612. end;
  613. Result := Count;
  614. end;
  615. procedure TutlCustomZLibStream.Flush(flags: integer);
  616. begin
  617. DoFlush(flags);
  618. { TODO : Check Result}
  619. end;
  620. function TutlCustomZLibStream.DoFlush(flags: integer): integer;
  621. var
  622. len : cardinal;
  623. done : boolean;
  624. written,
  625. err: int64;
  626. begin
  627. done := false;
  628. if not fWriting then
  629. Exit(Z_STREAM_ERROR);
  630. fStream.avail_in := 0; { should be zero already anyway }
  631. while true do begin
  632. len := Z_BUFSIZE - fStream.avail_out;
  633. if (len <> 0) then begin
  634. written:= Source.Write(fBufStart^, len);
  635. if (written <> len) then begin
  636. Exit(Z_ERRNO);
  637. end;
  638. fStream.next_out := fBufStart;
  639. fStream.avail_out := Z_BUFSIZE;
  640. end;
  641. if done then
  642. break;
  643. err := deflate(fStream, flags);
  644. { Ignore the second of two consecutive flushes: }
  645. if (len = 0) and (err = Z_BUF_ERROR) then
  646. err := Z_OK;
  647. { deflate has finished flushing only when it hasn't used up
  648. all the available space in the output buffer: }
  649. done := (fStream.avail_out <> 0) or (err = Z_STREAM_END);
  650. if (err <> Z_OK) and (err <> Z_STREAM_END) then
  651. break;
  652. end; {WHILE}
  653. if (err = Z_STREAM_END) then
  654. Result:= Z_OK
  655. else
  656. Result:= err;
  657. end;
  658. function TutlCustomZLibStream.Rewind: integer;
  659. begin
  660. if fWriting then
  661. Exit(-1);
  662. fStream.avail_in := 0;
  663. fStream.next_in := fBufStart;
  664. inflateReset(fStream);
  665. Source.Seek(fStartPos, soBeginning);
  666. Result:= 0;
  667. end;
  668. function TutlCustomZLibStream.zlibFinishRead: LongInt;
  669. begin
  670. Result:= inflateEnd(fStream);
  671. end;
  672. function TutlCustomZLibStream.zlibFinishWrite: LongInt;
  673. begin
  674. Result:= deflateEnd(fStream);
  675. end;
  676. function TutlCustomZLibStream.GetTotalIn: QWord;
  677. begin
  678. Result:= fStream.total_in;
  679. end;
  680. function TutlCustomZLibStream.GetTotalOut: QWord;
  681. begin
  682. Result:= fStream.total_out;
  683. end;
  684. { TutlZLibStream }
  685. function TutlZLibStream.zlibInitRead: LongInt;
  686. begin
  687. Result:= inflateInit_(@fStream, ZLIB_VERSION, sizeof(fStream));
  688. end;
  689. function TutlZLibStream.zlibInitWrite: LongInt;
  690. begin
  691. Result:= deflateInit_(@fStream, level, ZLIB_VERSION, sizeof(fStream));
  692. end;
  693. { TutlZipStream }
  694. function TutlZipStream.zlibInitRead: LongInt;
  695. begin
  696. {
  697. Well-documented undocumented feature: passing negative window bits tells zlib not to
  698. excpect a zlib header and instead read raw deflate blocks.
  699. We use 15 bits here instead of 13 because that's what everybody else does, and it doesn't really
  700. matter for decompression anyway.
  701. }
  702. Result:= inflateInit2_(fStream, -MAX_WBITS, ZLIB_VERSION, sizeof(fStream));
  703. end;
  704. function TutlZipStream.zlibInitWrite: LongInt;
  705. begin
  706. Result:= deflateInit2_(fStream, level, Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, ZLIB_VERSION, sizeof(fStream));
  707. end;
  708. { TutlGZipStream }
  709. function TutlGZipStream.zlibInitRead: LongInt;
  710. begin
  711. Result:= inherited;
  712. end;
  713. function TutlGZipStream.zlibInitWrite: LongInt;
  714. begin
  715. Result:= inherited;
  716. end;
  717. constructor TutlGZipStream.Create(aBaseStream: TStream; aMode: string);
  718. begin
  719. inherited Create(aBaseStream, aMode);
  720. fOrigFileName:= '';
  721. fOrigTimestamp:= 0;
  722. fComment:= '';
  723. if fWriting then begin
  724. fCRC:= crc32(0, nil, 0);
  725. WriteHeader;
  726. end
  727. else begin
  728. CheckHeader;
  729. end;
  730. fStartPos:= Source.Position;
  731. end;
  732. procedure TutlGZipStream.WriteHeader;
  733. var
  734. h: TGZHeader;
  735. begin
  736. { TODO : use actual data }
  737. h.magic:= gz_magic;
  738. h.method:= Z_DEFLATED;
  739. h.flags:= 0;
  740. h.time:= 0;
  741. h.xflags:= 0;
  742. h.oscode:= 0;
  743. Source.Write(h, sizeof(h));
  744. end;
  745. procedure TutlGZipStream.CheckHeader;
  746. var
  747. h: TGZHeader;
  748. w: Word;
  749. function ReadNullTerm: AnsiString;
  750. var
  751. b: byte;
  752. begin
  753. Result:= '';
  754. b:= 0;
  755. while Source.Read(b, sizeof(b)) = sizeof(b) do begin
  756. if b = 0 then break;
  757. Result:= Result + Chr(b);
  758. end;
  759. end;
  760. begin
  761. Source.ReadBuffer(h{%H-}, sizeof(h));
  762. if not CompareMem(@h.magic, @gz_magic, sizeof(gz_magic)) then
  763. raise EFOpenError.Create(sGZipNoHeader);
  764. if (h.method <> Z_DEFLATED) or ((h.flags and GZ_RESERVED) <> 0) then
  765. raise EReadError.Create(sGZIPUnsupportedMethod);
  766. fOrigTimestamp:= UnixToDateTime(LEtoN(h.time));
  767. if (h.flags and GZ_EXTRA_FIELD) > 0 then begin
  768. w:= 0;
  769. Source.ReadBuffer(w, sizeof(w));
  770. w:= LEtoN(w);
  771. // Skip Extra Field
  772. Source.Seek(w, soFromCurrent);
  773. end;
  774. if (h.flags and GZ_ORIG_NAME) > 0 then begin
  775. fOrigFileName:= ReadNullTerm;
  776. end;
  777. if (h.flags and GZ_COMMENT) > 0 then begin
  778. fComment:= ReadNullTerm;
  779. end;
  780. if (h.flags and GZ_HEAD_CRC) > 0 then begin
  781. // Skip Header Checksum
  782. Source.Seek(2, soFromCurrent);
  783. end;
  784. end;
  785. function TutlGZipStream.zlibFinishWrite: LongInt;
  786. var
  787. f: TGZFooter;
  788. begin
  789. Result:=inherited zlibFinishWrite;
  790. f.crc32:= fCRC;
  791. f.size:= fStream.total_in;
  792. Source.WriteBuffer(f, sizeof(f));
  793. end;
  794. function TutlGZipStream.Write(const Buffer; Count: Longint): Longint;
  795. begin
  796. Result:=inherited Write(Buffer, Count);
  797. if Result > 0 then
  798. fCRC:= crc32(fCRC, @Buffer, Result);
  799. end;
  800. procedure TutlGZipStream.ReadFooter;
  801. var
  802. bp: Int64;
  803. f: TGZFooter;
  804. begin
  805. bp:= Source.Position;
  806. try
  807. Source.Seek(-Sizeof(f), soEnd);
  808. Source.ReadBuffer(f{%H-}, sizeof(f));
  809. fKnownSize:= f.size;
  810. finally
  811. Source.Position:= bp;
  812. end;
  813. end;
  814. {$ENDIF HAVE_ZLIB}
  815. {$IFDEF HAVE_LIBLZMA}
  816. { TlibLZMA }
  817. function TlibLZMA.lzma_block_header_size_decode(b: dword): dword;
  818. begin
  819. Result:= (b + 1) * 4;
  820. end;
  821. function PasAlloc({%H-}opaque: Pointer; Items, Size: size_t): Pointer; cdecl;
  822. begin
  823. Result:= AllocMem(Size * Items);
  824. end;
  825. procedure PasFree({%H-}opaque, Block: Pointer); cdecl;
  826. begin
  827. Freemem(Block);
  828. end;
  829. procedure TlibLZMA.Load;
  830. begin
  831. if Handle = 0 then
  832. Handle:= SafeLoadLibrary(LZMA_DLL);
  833. PascalAllocator.XZalloc:= @PasAlloc;
  834. PascalAllocator.XZfree:= @PasFree;
  835. if Handle <> 0 then begin
  836. Pointer(lzma_version_number):= GetProcedureAddress(Handle, 'lzma_version_number');
  837. Pointer(lzma_easy_encoder):= GetProcedureAddress(Handle, 'lzma_easy_encoder');
  838. Pointer(lzma_stream_decoder):= GetProcedureAddress(Handle, 'lzma_stream_decoder');
  839. Pointer(lzma_auto_decoder):= GetProcedureAddress(Handle, 'lzma_auto_decoder');
  840. Pointer(lzma_code):= GetProcedureAddress(Handle, 'lzma_code');
  841. Pointer(lzma_end):= GetProcedureAddress(Handle, 'lzma_end');
  842. Pointer(lzma_easy_buffer_encode):= GetProcedureAddress(Handle, 'lzma_easy_buffer_encode');
  843. Pointer(lzma_stream_buffer_decode):= GetProcedureAddress(Handle, 'lzma_stream_buffer_decode');
  844. Pointer(lzma_stream_footer_decode):= GetProcedureAddress(Handle, 'lzma_stream_footer_decode');
  845. Pointer(lzma_stream_header_decode):= GetProcedureAddress(Handle, 'lzma_stream_header_decode');
  846. Pointer(lzma_stream_flags_compare):= GetProcedureAddress(Handle, 'lzma_stream_flags_compare');
  847. Pointer(lzma_index_decoder):= GetProcedureAddress(Handle, 'lzma_index_decoder');
  848. Pointer(lzma_index_buffer_decode):= GetProcedureAddress(Handle, 'lzma_index_buffer_decode');
  849. Pointer(lzma_index_uncompressed_size):= GetProcedureAddress(Handle, 'lzma_index_uncompressed_size');
  850. Pointer(lzma_index_end):= GetProcedureAddress(Handle, 'lzma_index_end');
  851. Pointer(lzma_index_cat):= GetProcedureAddress(Handle, 'lzma_index_cat');
  852. Pointer(lzma_index_stream_flags):= GetProcedureAddress(Handle, 'lzma_index_stream_flags');
  853. Pointer(lzma_index_stream_padding):= GetProcedureAddress(Handle, 'lzma_index_stream_padding');
  854. Pointer(lzma_index_total_size):= GetProcedureAddress(Handle, 'lzma_index_total_size');
  855. Pointer(lzma_index_iter_init):= GetProcedureAddress(Handle, 'lzma_index_iter_init');
  856. Pointer(lzma_index_iter_locate):= GetProcedureAddress(Handle, 'lzma_index_iter_locate');
  857. Pointer(lzma_index_iter_next):= GetProcedureAddress(Handle, 'lzma_index_iter_next');
  858. Pointer(lzma_block_decoder):= GetProcedureAddress(Handle, 'lzma_block_decoder');
  859. Pointer(lzma_block_header_decode):= GetProcedureAddress(Handle, 'lzma_block_header_decode');
  860. Pointer(lzma_block_compressed_size):= GetProcedureAddress(Handle, 'lzma_block_compressed_size');
  861. end;
  862. end;
  863. procedure TlibLZMA.Unload;
  864. begin
  865. UnloadLibrary(Handle);
  866. FillByte(Self, sizeof(Self), 0);
  867. end;
  868. { TutlXZStream }
  869. constructor TutlXZStream.Create(aBaseStream: TStream; aMode: string);
  870. var
  871. m: Char;
  872. err: integer;
  873. begin
  874. inherited Create(aBaseStream);
  875. if not CheckLibraryAvailable then
  876. raise Exception.CreateFmt(sLibLZMANotFound, [LZMA_DLL]);
  877. fMode:= #0;
  878. fLevel:= 0;
  879. for m in aMode do begin
  880. case m of
  881. 'r',
  882. 'w': fMode:= m;
  883. '0'..'9': fLevel:= Ord(m) - Ord('0');
  884. 'e': fLevel:= fLevel or LZMA_PRESET_EXTREME;
  885. end;
  886. end;
  887. if not (fMode in ['r', 'w']) then
  888. raise EFOpenError.Create(sInvalidMode);
  889. fWriting:= fMode = 'w';
  890. fBufStart:= @fBuffer[0];
  891. fStream:= Default(lzma_stream);
  892. fBlockOffs:= -1;
  893. if fWriting then begin
  894. err:= libLZMA.lzma_easy_encoder(fStream, fLevel, LZMA_CHECK_CRC64);
  895. fStream.next_out:= fBufStart;
  896. end else begin
  897. if not ParseIndexes then
  898. raise EFOpenError.Create(sLZMAErrorOpen);
  899. IterateIndexes;
  900. fKnownSize:= libLZMA.lzma_index_uncompressed_size(fIndex);
  901. Seek(0, soBeginning);
  902. fStream.next_in:= fBufStart;
  903. err:= 0;
  904. end;
  905. if err <> Z_OK then
  906. raise EFOpenError.Create(sFailedInitZLib);
  907. fStream.avail_out:= Z_BUFSIZE;
  908. fStartPos:= Source.Position;
  909. end;
  910. destructor TutlXZStream.Destroy;
  911. begin
  912. if fWriting then begin;
  913. DoFlush(LZMA_FINISH);
  914. end;
  915. libLZMA.lzma_index_end(fIndex, nil);
  916. libLZMA.lzma_end(fStream);
  917. inherited Destroy;
  918. end;
  919. function TutlXZStream.DoFlush(flags: integer): integer;
  920. begin
  921. fStream.next_in := nil;
  922. fStream.avail_in := 0;
  923. while (libLZMA.lzma_code(fStream,flags) <> LZMA_STREAM_END) and
  924. (fStream.avail_out = 0) do begin
  925. Source.WriteBuffer(fBufStart^, Z_BUFSIZE);
  926. fStream.next_out := fBufStart;
  927. fStream.avail_out := Z_BUFSIZE;
  928. end;
  929. if fStream.avail_out < Z_BUFSIZE then
  930. Source.WriteBuffer(fBufStart^, Z_BUFSIZE - fStream.avail_out);
  931. Result:= LZMA_OK;
  932. end;
  933. procedure TutlXZStream.Flush(flags: integer);
  934. begin
  935. DoFlush(LZMA_SYNC_FLUSH);
  936. end;
  937. function TutlXZStream.Read(var Buffer; Count: Longint): Longint;
  938. var
  939. res, sz: Int64;
  940. a, b, current_read: QWord;
  941. r: lzma_ret;
  942. begin
  943. Result:= 0;
  944. // is a block loaded?
  945. if fBlockOffs < 0 then begin
  946. // if no block is loaded, we might be at EOF
  947. Exit(0);
  948. end;
  949. fStream.next_out := @Buffer;
  950. fStream.avail_out := Count;
  951. while (fStream.avail_out > 0) do begin
  952. if fStream.avail_in = 0 then begin
  953. // read remaining data from current block
  954. a:= fBlock.compressed_size;
  955. b:= fStream.total_in;
  956. sz:= a - b;
  957. if sz > Z_BUFSIZE then
  958. sz:= Z_BUFSIZE;
  959. // if this block has no more data, read next
  960. if sz = 0 then begin
  961. // finish coding current block
  962. libLZMA.lzma_code(fStream, LZMA_FINISH);
  963. current_read:= Count - fStream.avail_out;
  964. Inc(Result, current_read);
  965. inc(fUncompPos, current_read);
  966. // begin new block
  967. res:= Seek(0, soCurrent);
  968. if res < 0 then
  969. Exit;
  970. // setup writing to next part
  971. fStream.next_out:= @Tbytearray(Buffer)[Result];
  972. dec(Count, current_read);
  973. fStream.avail_out:= Count;
  974. // re-read from this new block
  975. continue;
  976. end else begin
  977. res:= Source.Read(fBufStart^, sz);
  978. if res = 0 then begin
  979. // block should have more data, but we didn't get any
  980. libLZMA.lzma_code(fStream, LZMA_FINISH);
  981. current_read:= Count - fStream.avail_out;
  982. Inc(Result, current_read);
  983. inc(fUncompPos, current_read);
  984. Exit;
  985. end;
  986. fStream.avail_in := res;
  987. fStream.next_in := fBufStart;
  988. end;
  989. end;
  990. r:= libLZMA.lzma_code(fStream, LZMA_RUN);
  991. if r <> LZMA_OK then
  992. break;
  993. end;
  994. current_read:= Count - fStream.avail_out;
  995. Inc(Result, current_read);
  996. inc(fUncompPos, current_read);
  997. end;
  998. function TutlXZStream.Write(const Buffer; Count: Longint): Longint;
  999. var
  1000. res: Integer;
  1001. begin
  1002. fStream.next_in := @Buffer;
  1003. fStream.avail_in := Count;
  1004. while (fStream.avail_in > 0) do begin
  1005. res:= libLZMA.lzma_code(fStream, LZMA_RUN);
  1006. if res < 0 then
  1007. Exit(res);
  1008. if fStream.avail_out = 0 then begin
  1009. Source.WriteBuffer(fBufStart^, Z_BUFSIZE);
  1010. fStream.next_out:= fBufStart;
  1011. fStream.avail_out:= Z_BUFSIZE;
  1012. end;
  1013. end;
  1014. Result := Count;
  1015. end;
  1016. function TutlXZStream.Rewind: integer;
  1017. begin
  1018. if fWriting then
  1019. Exit(-1);
  1020. Seek(0, soBeginning);
  1021. Result:= 0;
  1022. end;
  1023. function TutlXZStream.GetTotalIn: QWord;
  1024. begin
  1025. Result:= fStream.total_in;
  1026. end;
  1027. function TutlXZStream.GetTotalOut: QWord;
  1028. begin
  1029. Result:= fStream.total_out;
  1030. end;
  1031. function TutlXZStream.ParseIndexes: boolean;
  1032. const
  1033. MAX_INDEX_LENGTH = 16384;
  1034. MAX_QWORD = high(QWord);
  1035. BUFSIZE = 16384;
  1036. var
  1037. bp, fs: Int64;
  1038. stream_padding: lzma_vli;
  1039. combined_index: p_lzma_index;
  1040. function ReadOneStream: boolean;
  1041. var
  1042. footer,
  1043. header: array[0..LZMA_STREAM_HEADER_SIZE-1] of byte;
  1044. buf: array[0..BUFSIZE-1] of byte;
  1045. footer_flags,
  1046. header_flags: lzma_stream_flags;
  1047. inddec: lzma_stream;
  1048. this_index: p_lzma_index;
  1049. r: lzma_ret;
  1050. index_size: lzma_vli;
  1051. begin
  1052. Result:= false;
  1053. if Source.Position < LZMA_STREAM_HEADER_SIZE then
  1054. // Corrupted File
  1055. Exit;
  1056. Source.Seek(-LZMA_STREAM_HEADER_SIZE, soCurrent);
  1057. Source.ReadBuffer(footer{%H-}, sizeof(footer));
  1058. // Skip stream padding.
  1059. if (footer[8] = 0) and (footer[9] = 0) and (footer[10] = 0) and (footer[11] = 0) then begin
  1060. inc(stream_padding, 4);
  1061. Source.Seek(-4, soCurrent);
  1062. Exit(true);
  1063. end;
  1064. inc(fStreamNr);
  1065. Source.Seek(-LZMA_STREAM_HEADER_SIZE, soCurrent);
  1066. // decode footer
  1067. r:= libLZMA.lzma_stream_footer_decode(footer_flags, @footer[0]);
  1068. if r <> LZMA_OK then
  1069. Exit;
  1070. index_size:= footer_flags.backward_size;
  1071. // Safeguard, the index should be right before the footer
  1072. if (index_size > MAX_INDEX_LENGTH) or (Source.Position < index_size + LZMA_STREAM_HEADER_SIZE) then
  1073. Exit;
  1074. // Compute start of index for this stream
  1075. Source.Seek(-index_size, soCurrent);
  1076. // Decode Index
  1077. this_index:= nil;
  1078. inddec:= Default(lzma_stream);
  1079. r:= libLZMA.lzma_index_decoder(inddec, @this_index, MAX_QWORD);
  1080. try
  1081. if r <> LZMA_OK then
  1082. Exit;
  1083. repeat
  1084. inddec.avail_in:= index_size;
  1085. if inddec.avail_in > BUFSIZE then
  1086. inddec.avail_in:= BUFSIZE;
  1087. Source.ReadBuffer({%H-}buf[0], inddec.avail_in);
  1088. index_size -= inddec.avail_in;
  1089. inddec.next_in:= @buf[0];
  1090. r:= libLZMA.lzma_code(inddec, LZMA_RUN);
  1091. until (r <> LZMA_OK) or (index_size=0);
  1092. if r <> LZMA_STREAM_END then
  1093. Exit;
  1094. index_size:= libLZMA.lzma_index_total_size(this_index);
  1095. Source.Seek(- (inddec.total_in + index_size + LZMA_STREAM_HEADER_SIZE), soCurrent);
  1096. // Read and decode Stream Header
  1097. Source.ReadBuffer(header{%H-}[0], LZMA_STREAM_HEADER_SIZE);
  1098. Source.Seek(-LZMA_STREAM_HEADER_SIZE, soCurrent);
  1099. r:= libLZMA.lzma_stream_header_decode(header_flags, @header[0]);
  1100. if r <> LZMA_OK then
  1101. Exit;
  1102. // should be equal
  1103. r:= libLZMA.lzma_stream_flags_compare(header_flags, footer_flags);
  1104. if r <> LZMA_OK then
  1105. Exit;
  1106. // store decoded flags in this index
  1107. r:= libLZMA.lzma_index_stream_flags(this_index, footer_flags);
  1108. if r <> LZMA_OK then
  1109. Exit;
  1110. // store padding accumulated so far (needed for seeking in multi-stream-files)
  1111. r:= libLZMA.lzma_index_stream_padding(this_index, stream_padding);
  1112. if r <> LZMA_OK then
  1113. Exit;
  1114. if Assigned(combined_index) then begin
  1115. r:= libLZMA.lzma_index_cat(this_index, combined_index, nil);
  1116. if r <> LZMA_OK then
  1117. Exit;
  1118. end;
  1119. combined_index:= this_index;
  1120. this_index:= nil;
  1121. Result:= true;
  1122. finally
  1123. libLZMA.lzma_end(inddec);
  1124. libLZMA.lzma_index_end(this_index, nil);
  1125. end;
  1126. end;
  1127. begin
  1128. Result:= false;
  1129. bp:= Source.Position;
  1130. try
  1131. fs:= Source.Seek(0, soEnd);
  1132. // file size must be 4 byte aligned
  1133. if (fs <= 0) or (fs and 3 > 0) then
  1134. Exit;
  1135. stream_padding:= 0;
  1136. fStreamNr:= 0;
  1137. combined_index:= nil;
  1138. try
  1139. // Jump backwards through the file identifying each stream.
  1140. while Source.Position > 0 do begin
  1141. if not ReadOneStream then
  1142. Exit;
  1143. end;
  1144. fIndex:= combined_index;
  1145. combined_index:= nil;
  1146. Result:= true;
  1147. except
  1148. libLZMA.lzma_index_end(combined_index, nil);
  1149. end;
  1150. finally
  1151. if not Result then
  1152. Source.Position:= bp;
  1153. end;
  1154. end;
  1155. procedure TutlXZStream.IterateIndexes;
  1156. var
  1157. iter: lzma_index_iter;
  1158. begin
  1159. fBlockNr:= 0;
  1160. fMaxUncompBlockSize:= 0;
  1161. iter:= Default(lzma_index_iter);
  1162. libLZMA.lzma_index_iter_init(iter, fIndex);
  1163. while not libLZMA.lzma_index_iter_next(iter, LZMA_INDEX_ITER_NONEMPTY_BLOCK) do begin
  1164. if iter.block.uncompressed_size > fMaxUncompBlockSize then
  1165. fMaxUncompBlockSize:= iter.block.uncompressed_size;
  1166. inc(fBlockNr);
  1167. end;
  1168. end;
  1169. function TutlXZStream.Seek(const Offset: Int64; Origin: TSeekOrigin): Int64;
  1170. var
  1171. iter: lzma_index_iter;
  1172. header: array[0..LZMA_BLOCK_HEADER_SIZE_MAX-1] of byte;
  1173. tmp: array[0..Z_BUFSIZE] of byte;
  1174. r: lzma_ret;
  1175. ofs, sz: Int64;
  1176. begin
  1177. if fWriting then
  1178. Exit(inherited Seek(Offset, Origin));
  1179. // only support absolute position
  1180. case Origin of
  1181. soEnd: Exit(Seek(fKnownSize + Offset, soBeginning));
  1182. soCurrent: Exit(Seek(Position + Offset, soBeginning));
  1183. end;
  1184. Result:= -1;
  1185. if Offset = fKnownSize then begin
  1186. // seek to last - pretend it worked, but reading will fail
  1187. fUncompPos:= fKnownSize;
  1188. BlockEnd;
  1189. Exit;
  1190. end;
  1191. // find what block the target is in
  1192. iter:= Default(lzma_index_iter);
  1193. libLZMA.lzma_index_iter_init(iter, fIndex);
  1194. if libLZMA.lzma_index_iter_locate(iter, Offset) then begin
  1195. // not found, don't change anything
  1196. Exit;
  1197. end;
  1198. if (iter.block.compressed_file_offset <> fBlockOffs) or
  1199. (Offset < fUncompPos) then begin
  1200. // free current Block
  1201. BlockEnd;
  1202. // seek to block begin
  1203. fBlockOffs:= iter.block.compressed_file_offset;
  1204. if Source.Seek(iter.block.compressed_file_offset, soBeginning)<0 then
  1205. Exit;
  1206. //Read the block header. Start by reading a single byte which tell us how big the block header is.
  1207. Source.ReadBuffer({%H-}header[0], 1);
  1208. if header[0] = 0 then
  1209. Exit;
  1210. fBlock.version:= 0;
  1211. fBlock.check:= iter.stream.flags^.check;
  1212. fBlock.filters:= @fFilters[0];
  1213. fBlock.header_size:= libLZMA.lzma_block_header_size_decode(header[0]);
  1214. //Now read and decode the block header.
  1215. Source.ReadBuffer(header[1], fBlock.header_size-1);
  1216. r:= libLZMA.lzma_block_header_decode(fBlock, @libLZMA.PascalAllocator, @header[0]);
  1217. if r <> LZMA_OK then
  1218. Exit;
  1219. // What this actually does is it checks that the block header matches the index.
  1220. r:= libLZMA.lzma_block_compressed_size(fBlock, iter.block.unpadded_size);
  1221. if r <> LZMA_OK then
  1222. Exit;
  1223. // copy over info we need later
  1224. fBlock.uncompressed_size:= iter.block.uncompressed_size;
  1225. // Read the block data.
  1226. fStream:= Default(lzma_stream);
  1227. r:= libLZMA.lzma_block_decoder(fStream, fBlock);
  1228. if r <> LZMA_OK then
  1229. Exit;
  1230. fUncompPos:= iter.block.uncompressed_file_offset;
  1231. end;
  1232. // fast-forward to actual pos
  1233. ofs:= Offset - fUncompPos;
  1234. while (ofs > 0) do begin
  1235. sz := Z_BUFSIZE;
  1236. if (ofs < Z_BUFSIZE) then
  1237. sz := ofs;
  1238. sz := Read({%H-}tmp[0], sz);
  1239. if (sz <= 0) then
  1240. Exit;
  1241. dec(ofs, sz);
  1242. end;
  1243. Result:= fUncompPos;
  1244. end;
  1245. function TutlXZStream.GetPosition: Int64;
  1246. begin
  1247. if fWriting then
  1248. Exit(inherited GetPosition);
  1249. Result:= fUncompPos;
  1250. end;
  1251. procedure TutlXZStream.BlockEnd;
  1252. var
  1253. i: Integer;
  1254. begin
  1255. libLZMA.lzma_end(fStream);
  1256. fStream:= Default(lzma_stream);
  1257. fBlock:= Default(lzma_block);
  1258. fBlockOffs:= -1;
  1259. for i:= 0 to LZMA_FILTERS_MAX-1 do begin
  1260. if fFilters[i].id <> LZMA_VLI_UNKNOWN then
  1261. libLZMA.PascalAllocator.XZfree(nil, fFilters[i].options);
  1262. fFilters[i].id:= LZMA_VLI_UNKNOWN;
  1263. end;
  1264. end;
  1265. class function TutlXZStream.CheckLibraryAvailable: boolean;
  1266. begin
  1267. libLZMA.Load;
  1268. Result:= Assigned(libLZMA.lzma_version_number) and (libLZMA.lzma_version_number()>=LZMA_VER);
  1269. end;
  1270. {$ENDIF HAVE_LIBLZMA}
  1271. finalization
  1272. {$IFDEF HAVE_LIBLZMA}
  1273. libLZMA.Unload;
  1274. {$ENDIF HAVE_LIBLZMA}
  1275. end.