zstream.pp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675
  1. unit ZStream;
  2. {**********************************************************************
  3. This file is part of the Free Pascal free component library.
  4. Copyright (c) 2007 by Daniel Mantione
  5. member of the Free Pascal development team
  6. Implements a Tstream descendents that allow you to read and write
  7. compressed data according to the Deflate algorithm described in
  8. RFC1951.
  9. See the file COPYING.FPC, included in this distribution,
  10. for details about the copyright.
  11. This program is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  14. **********************************************************************}
  15. { GZip specs: https://datatracker.ietf.org/doc/html/rfc1952 }
  16. {$mode objfpc}
  17. {***************************************************************************}
  18. interface
  19. {***************************************************************************}
  20. uses classes,zbase,gzio;
  21. type
  22. Tcompressionlevel=(
  23. clnone, {Do not use compression, just copy data.}
  24. clfastest, {Use fast (but less) compression.}
  25. cldefault, {Use default compression}
  26. clmax {Use maximum compression}
  27. );
  28. Tgzopenmode=(
  29. gzopenread, {Open file for reading.}
  30. gzopenwrite {Open file for writing.}
  31. );
  32. Tcustomzlibstream=class(Townerstream)
  33. protected
  34. Fstream:z_stream;
  35. Fbuffer:pointer;
  36. Fonprogress:Tnotifyevent;
  37. procedure progress(sender:Tobject);
  38. property onprogress:Tnotifyevent read Fonprogress write Fonprogress;
  39. public
  40. constructor create(stream:Tstream);
  41. destructor destroy;override;
  42. end;
  43. { Tcompressionstream }
  44. Tcompressionstream=class(Tcustomzlibstream)
  45. private
  46. procedure ClearOutBuffer;
  47. protected
  48. raw_written,compressed_written: int64;
  49. public
  50. constructor create(level:Tcompressionlevel;
  51. dest:Tstream;
  52. Askipheader:boolean=false);
  53. destructor destroy;override;
  54. function write(const buffer;count:longint):longint;override;
  55. procedure flush;
  56. function get_compressionrate:single;
  57. property OnProgress;
  58. end;
  59. Tdecompressionstream=class(Tcustomzlibstream)
  60. protected
  61. raw_read,compressed_read:int64;
  62. skipheader:boolean;
  63. procedure reset;
  64. function GetPosition() : Int64; override;
  65. public
  66. constructor create(Asource:Tstream;Askipheader:boolean=false);
  67. destructor destroy;override;
  68. function read(var buffer;count:longint):longint;override;
  69. function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; override;
  70. function get_compressionrate:single;
  71. property OnProgress;
  72. end;
  73. TGZFileStream = Class(TStream)
  74. protected
  75. Fgzfile:gzfile;
  76. Ffilemode:Tgzopenmode;
  77. public
  78. constructor create(filename:ansistring;filemode:Tgzopenmode);
  79. function read(var buffer;count:longint):longint;override;
  80. function write(const buffer;count:longint):longint;override;
  81. function seek(offset:longint;origin:word):longint;override;
  82. destructor destroy;override;
  83. end;
  84. TGZipCompressionStream = class(TStream)
  85. private
  86. FLevel: TCompressionLevel;
  87. FCrc32Val: Longword;
  88. FUncompressedSize: Cardinal;
  89. FDest: TStream;
  90. FCompressionStream: TCompressionStream;
  91. procedure WriteHeader;
  92. procedure WriteFooter;
  93. public
  94. constructor Create(ADest: TStream); overload;
  95. constructor Create(ALevel: TCompressionLevel; ADest: TStream); overload;
  96. destructor Destroy; override;
  97. function Write(const Buffer; Count: Longint): Longint; override;
  98. end;
  99. TGZipDecompressionStream = class(TStream)
  100. private
  101. FCrc32Val: Longword;
  102. FUncompressedSize: Cardinal;
  103. FSource: TStream;
  104. FDecompressionStream: TDecompressionStream;
  105. procedure Assert(ACond: Boolean; AMsg: string = '');
  106. procedure ReadHeader;
  107. procedure ReadFooter;
  108. public
  109. constructor Create(ASource: TStream);
  110. destructor Destroy; override;
  111. function Read(var Buffer; Count: Longint): Longint; override;
  112. function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; override;
  113. end;
  114. Ezliberror=class(Estreamerror)
  115. end;
  116. Egzfileerror=class(Ezliberror)
  117. end;
  118. Ecompressionerror=class(Ezliberror)
  119. end;
  120. Edecompressionerror=class(Ezliberror)
  121. end;
  122. {***************************************************************************}
  123. implementation
  124. {***************************************************************************}
  125. uses zdeflate,zinflate;
  126. const bufsize=16384; {Size of the buffer used for temporarily storing
  127. data from the child stream.}
  128. Crc_32_Tab : Array[0..255] of LongWord = (
  129. $00000000, $77073096, $ee0e612c, $990951ba, $076dc419, $706af48f, $e963a535, $9e6495a3,
  130. $0edb8832, $79dcb8a4, $e0d5e91e, $97d2d988, $09b64c2b, $7eb17cbd, $e7b82d07, $90bf1d91,
  131. $1db71064, $6ab020f2, $f3b97148, $84be41de, $1adad47d, $6ddde4eb, $f4d4b551, $83d385c7,
  132. $136c9856, $646ba8c0, $fd62f97a, $8a65c9ec, $14015c4f, $63066cd9, $fa0f3d63, $8d080df5,
  133. $3b6e20c8, $4c69105e, $d56041e4, $a2677172, $3c03e4d1, $4b04d447, $d20d85fd, $a50ab56b,
  134. $35b5a8fa, $42b2986c, $dbbbc9d6, $acbcf940, $32d86ce3, $45df5c75, $dcd60dcf, $abd13d59,
  135. $26d930ac, $51de003a, $c8d75180, $bfd06116, $21b4f4b5, $56b3c423, $cfba9599, $b8bda50f,
  136. $2802b89e, $5f058808, $c60cd9b2, $b10be924, $2f6f7c87, $58684c11, $c1611dab, $b6662d3d,
  137. $76dc4190, $01db7106, $98d220bc, $efd5102a, $71b18589, $06b6b51f, $9fbfe4a5, $e8b8d433,
  138. $7807c9a2, $0f00f934, $9609a88e, $e10e9818, $7f6a0dbb, $086d3d2d, $91646c97, $e6635c01,
  139. $6b6b51f4, $1c6c6162, $856530d8, $f262004e, $6c0695ed, $1b01a57b, $8208f4c1, $f50fc457,
  140. $65b0d9c6, $12b7e950, $8bbeb8ea, $fcb9887c, $62dd1ddf, $15da2d49, $8cd37cf3, $fbd44c65,
  141. $4db26158, $3ab551ce, $a3bc0074, $d4bb30e2, $4adfa541, $3dd895d7, $a4d1c46d, $d3d6f4fb,
  142. $4369e96a, $346ed9fc, $ad678846, $da60b8d0, $44042d73, $33031de5, $aa0a4c5f, $dd0d7cc9,
  143. $5005713c, $270241aa, $be0b1010, $c90c2086, $5768b525, $206f85b3, $b966d409, $ce61e49f,
  144. $5edef90e, $29d9c998, $b0d09822, $c7d7a8b4, $59b33d17, $2eb40d81, $b7bd5c3b, $c0ba6cad,
  145. $edb88320, $9abfb3b6, $03b6e20c, $74b1d29a, $ead54739, $9dd277af, $04db2615, $73dc1683,
  146. $e3630b12, $94643b84, $0d6d6a3e, $7a6a5aa8, $e40ecf0b, $9309ff9d, $0a00ae27, $7d079eb1,
  147. $f00f9344, $8708a3d2, $1e01f268, $6906c2fe, $f762575d, $806567cb, $196c3671, $6e6b06e7,
  148. $fed41b76, $89d32be0, $10da7a5a, $67dd4acc, $f9b9df6f, $8ebeeff9, $17b7be43, $60b08ed5,
  149. $d6d6a3e8, $a1d1937e, $38d8c2c4, $4fdff252, $d1bb67f1, $a6bc5767, $3fb506dd, $48b2364b,
  150. $d80d2bda, $af0a1b4c, $36034af6, $41047a60, $df60efc3, $a867df55, $316e8eef, $4669be79,
  151. $cb61b38c, $bc66831a, $256fd2a0, $5268e236, $cc0c7795, $bb0b4703, $220216b9, $5505262f,
  152. $c5ba3bbe, $b2bd0b28, $2bb45a92, $5cb36a04, $c2d7ffa7, $b5d0cf31, $2cd99e8b, $5bdeae1d,
  153. $9b64c2b0, $ec63f226, $756aa39c, $026d930a, $9c0906a9, $eb0e363f, $72076785, $05005713,
  154. $95bf4a82, $e2b87a14, $7bb12bae, $0cb61b38, $92d28e9b, $e5d5be0d, $7cdcefb7, $0bdbdf21,
  155. $86d3d2d4, $f1d4e242, $68ddb3f8, $1fda836e, $81be16cd, $f6b9265b, $6fb077e1, $18b74777,
  156. $88085ae6, $ff0f6a70, $66063bca, $11010b5c, $8f659eff, $f862ae69, $616bffd3, $166ccf45,
  157. $a00ae278, $d70dd2ee, $4e048354, $3903b3c2, $a7672661, $d06016f7, $4969474d, $3e6e77db,
  158. $aed16a4a, $d9d65adc, $40df0b66, $37d83bf0, $a9bcae53, $debb9ec5, $47b2cf7f, $30b5ffe9,
  159. $bdbdf21c, $cabac28a, $53b39330, $24b4a3a6, $bad03605, $cdd70693, $54de5729, $23d967bf,
  160. $b3667a2e, $c4614ab8, $5d681b02, $2a6f2b94, $b40bbe37, $c30c8ea1, $5a05df1b, $2d02ef8d
  161. );
  162. resourcestring Sgz_open_error='Could not open gzip compressed file %s.';
  163. Sgz_read_only='Gzip compressed file was opened for reading.';
  164. Sgz_write_only='Gzip compressed file was opened for writing.';
  165. Sgz_invalid_header='Invalid GZip header';
  166. Sgz_invalid_algorithm='Invalid compression algorithm';
  167. Sgz_invalid_crc32='Invalid crc32 checksum';
  168. Sgz_invalid_output_size='Invalid output size';
  169. Sseek_failed='Seek in deflate compressed stream failed.';
  170. function UpdateCrc32(Crc: Longword; const Buffer; Count: Longint): Longword;
  171. var
  172. PBuf: PByte;
  173. i: Longint;
  174. begin
  175. PBuf := @Buffer;
  176. Result := Crc xor $FFFFFFFF;
  177. for i := 1 to Count do
  178. begin
  179. Result := Crc_32_Tab[(Result xor PBuf^) and $ff] xor (Result shr 8);
  180. Inc(PBuf);
  181. end;
  182. Result := Result xor $FFFFFFFF;
  183. end;
  184. constructor Tcustomzlibstream.create(stream:Tstream);
  185. begin
  186. assert(stream<>nil);
  187. inherited create(stream);
  188. getmem(Fbuffer,bufsize);
  189. end;
  190. procedure Tcustomzlibstream.progress(sender:Tobject);
  191. begin
  192. if Fonprogress<>nil then
  193. Fonprogress(sender);
  194. end;
  195. destructor Tcustomzlibstream.destroy;
  196. begin
  197. freemem(Fbuffer);
  198. inherited destroy;
  199. end;
  200. {***************************************************************************}
  201. constructor Tcompressionstream.create(level:Tcompressionlevel;
  202. dest:Tstream;
  203. Askipheader:boolean=false);
  204. var err,l:smallint;
  205. begin
  206. inherited create(dest);
  207. Fstream.next_out:=Fbuffer;
  208. Fstream.avail_out:=bufsize;
  209. case level of
  210. clnone:
  211. l:=Z_NO_COMPRESSION;
  212. clfastest:
  213. l:=Z_BEST_SPEED;
  214. cldefault:
  215. l:=Z_DEFAULT_COMPRESSION;
  216. clmax:
  217. l:=Z_BEST_COMPRESSION;
  218. end;
  219. if Askipheader then
  220. err:=deflateInit2(Fstream,l,Z_DEFLATED,-MAX_WBITS,DEF_MEM_LEVEL,0)
  221. else
  222. err:=deflateInit(Fstream,l);
  223. if err<>Z_OK then
  224. raise Ecompressionerror.create(zerror(err));
  225. end;
  226. function Tcompressionstream.write(const buffer;count:longint):longint;
  227. var err:smallint;
  228. lastavail:longint;
  229. begin
  230. Fstream.next_in:=@buffer;
  231. Fstream.avail_in:=count;
  232. lastavail:=count;
  233. while Fstream.avail_in<>0 do
  234. begin
  235. if Fstream.avail_out=0 then
  236. ClearOutBuffer;
  237. inc(raw_written,lastavail-Fstream.avail_in);
  238. lastavail:=Fstream.avail_in;
  239. err:=deflate(Fstream,Z_NO_FLUSH);
  240. if err<>Z_OK then
  241. raise Ecompressionerror.create(zerror(err));
  242. end;
  243. inc(raw_written,lastavail-Fstream.avail_in);
  244. write:=count;
  245. end;
  246. function Tcompressionstream.get_compressionrate:single;
  247. begin
  248. get_compressionrate:=100*compressed_written/raw_written;
  249. end;
  250. procedure TCompressionstream.ClearOutBuffer;
  251. begin
  252. { Flush the buffer to the stream and update progress }
  253. source.writebuffer(Fbuffer^,bufsize-Fstream.avail_out);
  254. inc(compressed_written,bufsize-Fstream.avail_out);
  255. progress(self);
  256. { reset output buffer }
  257. Fstream.next_out:=Fbuffer;
  258. Fstream.avail_out:=bufsize;
  259. end;
  260. procedure Tcompressionstream.flush;
  261. var err:smallint;
  262. begin
  263. {Compress remaining data still in internal zlib data buffers.}
  264. repeat
  265. if Fstream.avail_out=0 then
  266. ClearOutBuffer;
  267. err:=deflate(Fstream,Z_FINISH);
  268. if err=Z_STREAM_END then
  269. break;
  270. if (err<>Z_OK) then
  271. raise Ecompressionerror.create(zerror(err));
  272. until false;
  273. if Fstream.avail_out<bufsize then
  274. ClearOutBuffer;
  275. end;
  276. destructor Tcompressionstream.destroy;
  277. begin
  278. try
  279. Flush;
  280. finally
  281. deflateEnd(Fstream);
  282. inherited destroy;
  283. end;
  284. end;
  285. {***************************************************************************}
  286. constructor Tdecompressionstream.create(Asource:Tstream;Askipheader:boolean=false);
  287. var err:smallint;
  288. begin
  289. inherited create(Asource);
  290. skipheader:=Askipheader;
  291. if Askipheader then
  292. err:=inflateInit2(Fstream,-MAX_WBITS)
  293. else
  294. err:=inflateInit(Fstream);
  295. if err<>Z_OK then
  296. raise Edecompressionerror.create(zerror(err));
  297. end;
  298. function Tdecompressionstream.read(var buffer;count:longint):longint;
  299. var err:smallint;
  300. lastavail:longint;
  301. begin
  302. Fstream.next_out:=@buffer;
  303. Fstream.avail_out:=count;
  304. lastavail:=count;
  305. while Fstream.avail_out<>0 do
  306. begin
  307. if Fstream.avail_in=0 then
  308. begin
  309. {Refill the buffer.}
  310. Fstream.next_in:=Fbuffer;
  311. Fstream.avail_in:=source.read(Fbuffer^,bufsize);
  312. inc(compressed_read,Fstream.avail_in);
  313. inc(raw_read,lastavail-Fstream.avail_out);
  314. lastavail:=Fstream.avail_out;
  315. progress(self);
  316. end;
  317. err:=inflate(Fstream,Z_NO_FLUSH);
  318. if err=Z_STREAM_END then
  319. break;
  320. if err<>Z_OK then
  321. raise Edecompressionerror.create(zerror(err));
  322. end;
  323. if err=Z_STREAM_END then
  324. dec(compressed_read,Fstream.avail_in);
  325. inc(raw_read,lastavail-Fstream.avail_out);
  326. read:=count-Fstream.avail_out;
  327. end;
  328. procedure Tdecompressionstream.reset;
  329. var err:smallint;
  330. begin
  331. source.seek(-compressed_read,sofromcurrent);
  332. raw_read:=0;
  333. compressed_read:=0;
  334. inflateEnd(Fstream);
  335. if skipheader then
  336. err:=inflateInit2(Fstream,-MAX_WBITS)
  337. else
  338. err:=inflateInit(Fstream);
  339. if err<>Z_OK then
  340. raise Edecompressionerror.create(zerror(err));
  341. end;
  342. function Tdecompressionstream.GetPosition() : Int64;
  343. begin
  344. GetPosition := raw_read;
  345. end;
  346. function Tdecompressionstream.Seek(const Offset: Int64; Origin: TSeekOrigin): Int64;
  347. var c,off: int64;
  348. buf: array[0..8191] of Byte;
  349. begin
  350. off:=Offset;
  351. if origin=soCurrent then
  352. inc(off,raw_read);
  353. if (origin=soEnd) or (off<0) then
  354. raise Edecompressionerror.create(Sseek_failed);
  355. seek:=off;
  356. if off<raw_read then
  357. reset
  358. else
  359. dec(off,raw_read);
  360. while off>0 do
  361. begin
  362. c:=off;
  363. if c>SizeOf(buf) then
  364. c:=SizeOf(buf);
  365. if read(buf,c)<>c then
  366. raise Edecompressionerror.create(Sseek_failed);
  367. dec(off,c);
  368. end;
  369. end;
  370. function Tdecompressionstream.get_compressionrate:single;
  371. begin
  372. get_compressionrate:=100*compressed_read/raw_read;
  373. end;
  374. destructor Tdecompressionstream.destroy;
  375. begin
  376. inflateEnd(Fstream);
  377. inherited destroy;
  378. end;
  379. {***************************************************************************}
  380. constructor Tgzfilestream.create(filename:ansistring;filemode:Tgzopenmode);
  381. begin
  382. if filemode=gzopenread then
  383. Fgzfile:=gzopen(filename,'rb')
  384. else
  385. Fgzfile:=gzopen(filename,'wb');
  386. Ffilemode:=filemode;
  387. if Fgzfile=nil then
  388. raise Egzfileerror.createfmt(Sgz_open_error,[filename]);
  389. end;
  390. function Tgzfilestream.read(var buffer;count:longint):longint;
  391. begin
  392. if Ffilemode=gzopenwrite then
  393. raise Egzfileerror.create(Sgz_write_only);
  394. read:=gzread(Fgzfile,@buffer,count);
  395. end;
  396. function Tgzfilestream.write(const buffer;count:longint):longint;
  397. begin
  398. if Ffilemode=gzopenread then
  399. raise Egzfileerror.create(Sgz_write_only);
  400. write:=gzwrite(Fgzfile,@buffer,count);
  401. end;
  402. function Tgzfilestream.seek(offset:longint;origin:word):longint;
  403. begin
  404. seek:=gzseek(Fgzfile,offset,origin);
  405. if seek=-1 then
  406. raise egzfileerror.create(Sseek_failed);
  407. end;
  408. destructor Tgzfilestream.destroy;
  409. begin
  410. gzclose(Fgzfile);
  411. inherited destroy;
  412. end;
  413. { TGZipCompressionStream }
  414. constructor TGZipCompressionStream.Create(ADest: TStream);
  415. begin
  416. Create(clDefault, ADest);
  417. end;
  418. constructor TGZipCompressionStream.Create(ALevel: TCompressionLevel; ADest: TStream);
  419. begin
  420. inherited Create;
  421. FLevel := ALevel;
  422. FCrc32Val := 0;
  423. FUncompressedSize := 0;
  424. FDest := ADest;
  425. WriteHeader;
  426. FCompressionStream := TCompressionStream.Create(FLevel, FDest, True);
  427. end;
  428. destructor TGZipCompressionStream.Destroy;
  429. begin
  430. FCompressionStream.Flush;
  431. FCompressionStream.Free;
  432. WriteFooter;
  433. inherited;
  434. end;
  435. procedure TGZipCompressionStream.WriteHeader;
  436. begin
  437. FDest.WriteByte($1f); // signature 1 of 2
  438. FDest.WriteByte($8b); // signature 2 of 2
  439. FDest.WriteByte($08); // deflate algorithm
  440. FDest.WriteByte($00); // no flags
  441. FDest.WriteDWord($00); // modification time unknown. Source is stream, not a file
  442. if FLevel = clmax then // XFL = extra flags = compression level
  443. FDest.WriteByte($02)
  444. else if FLevel = clfastest then
  445. FDest.WriteByte($04)
  446. else
  447. FDest.WriteByte($00);
  448. FDest.WriteByte($ff); // OS file system unknown. Source is stream, not a file
  449. end;
  450. procedure TGZipCompressionStream.WriteFooter;
  451. var
  452. i: Integer;
  453. begin
  454. // write crc32 in 4 bytes, least significant byte first
  455. for i := 1 to 4 do
  456. begin
  457. FDest.WriteByte(FCrc32Val and $ff);
  458. FCrc32Val := FCrc32Val shr 8;
  459. end;
  460. // write uncompressed size in 4 bytes, least significant byte first
  461. for i := 1 to 4 do
  462. begin
  463. FDest.WriteByte(FUncompressedSize and $ff);
  464. FUncompressedSize := FUncompressedSize shr 8;
  465. end;
  466. end;
  467. function TGZipCompressionStream.Write(const Buffer; Count: Longint): Longint;
  468. begin
  469. FCrc32Val := UpdateCrc32(FCrc32Val, Buffer, Count);
  470. Inc(FUncompressedSize, Count);
  471. Result := FCompressionStream.Write(Buffer, Count);
  472. end;
  473. { TGZipDecompressionStream }
  474. constructor TGZipDecompressionStream.Create(ASource: TStream);
  475. begin
  476. inherited Create;
  477. FSource := ASource;
  478. FCrc32Val := 0;
  479. FUncompressedSize := 0;
  480. ReadHeader;
  481. FDecompressionStream := TDecompressionStream.Create(FSource, True);
  482. end;
  483. destructor TGZipDecompressionStream.Destroy;
  484. begin
  485. FDecompressionStream.Free;
  486. inherited;
  487. end;
  488. procedure TGZipDecompressionStream.Assert(ACond: Boolean; AMsg: string = '');
  489. begin
  490. if not ACond then
  491. raise EDecompressionError.Create(AMsg);
  492. end;
  493. procedure TGZipDecompressionStream.ReadHeader;
  494. var
  495. Flags: Byte;
  496. XLEN: Cardinal;
  497. begin
  498. Assert(FSource.ReadByte = $1f, Sgz_invalid_header);
  499. Assert(FSource.ReadByte = $8b, Sgz_invalid_header);
  500. Assert(FSource.ReadByte = $08, Sgz_invalid_algorithm);
  501. Flags := FSource.ReadByte;
  502. FSource.ReadDWord; // skip modification time. Dest is stream, not a file
  503. FSource.ReadByte; // skip compression level, is not needed
  504. FSource.ReadByte; // skip OS file system. Dest is stream, not a file
  505. if (Flags and $4) <> 0 then // FLG.FEXTRA
  506. begin
  507. XLEN := FSource.ReadByte + FSource.ReadByte shl 8; // least significant byte first
  508. while XLEN > 0 do
  509. begin
  510. FSource.ReadByte;
  511. Dec(XLEN);
  512. end;
  513. end;
  514. if (Flags and $8) <> 0 then // FLG.FNAME
  515. begin
  516. repeat
  517. until FSource.ReadByte = 0; // zero-terminated file name
  518. end;
  519. if (Flags and $10) <> 0 then // FLG.FCOMMENT
  520. begin
  521. repeat
  522. until FSource.ReadByte = 0; // zero-terminated file comment
  523. end;
  524. if (Flags and $2) <> 0 then // FLG.FHCRC
  525. FSource.ReadWord; // skip CRC16, check not implemented
  526. end;
  527. procedure TGZipDecompressionStream.ReadFooter;
  528. var
  529. Crc32: Longword;
  530. OrigSize: Longword;
  531. begin
  532. // The TDecompressionStream reads in buffers, so the footer
  533. // may already be skipped. Therefore, we need to Seek to the footer.
  534. // If FSource is non-seekable, we skip checking Crc32 and OrigSize.
  535. try
  536. FSource.Seek(-8, soEnd);
  537. except
  538. Exit; // skip Crc32 and OrigSize checking
  539. end;
  540. Crc32 := FSource.ReadByte + FSource.ReadByte shl 8 + FSource.ReadByte shl 16 + FSource.ReadByte shl 24;
  541. Assert(FCrc32Val = Crc32, Sgz_invalid_crc32);
  542. OrigSize := FSource.ReadByte + FSource.ReadByte shl 8 + FSource.ReadByte shl 16 + FSource.ReadByte shl 24;
  543. Assert(FUncompressedSize = OrigSize, Sgz_invalid_output_size);
  544. end;
  545. function TGZipDecompressionStream.Read(var Buffer; Count: Longint): Longint;
  546. begin
  547. Result := FDecompressionStream.Read(Buffer, Count);
  548. Inc(FUncompressedSize, Result);
  549. FCrc32Val := UpdateCrc32(FCrc32Val, Buffer, Result);
  550. if Result < Count then
  551. ReadFooter;
  552. end;
  553. function TGZipDecompressionStream.Seek(const Offset: Int64; Origin: TSeekOrigin): Int64;
  554. begin
  555. // accept Seek(0, soBeginning) if nothing read yet
  556. // this is needed for the TStream.CopyFrom() method
  557. if (Offset = 0) and (Origin = TSeekOrigin.soBeginning) and (FUncompressedSize = 0) then
  558. Result := 0
  559. else
  560. Result := inherited Seek(Offset, Origin);
  561. end;
  562. end.