JarUnPacker/prereq/fwzip/FWZipZLib.pas
2023-02-02 12:02:14 +03:00

683 lines
18 KiB
ObjectPascal
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

////////////////////////////////////////////////////////////////////////////////
//
// ****************************************************************************
// * Project : FWZip
// * Unit Name : FWZipZLib
// * Purpose : Базовые стримы сжатия и распаковки.
// * : Вынесено из ZLibEx в отдельный модуль
// * : для совместимости со старыми версиями Delphi
// * Author : Александр (Rouse_) Багель
// * Copyright : © Fangorn Wizards Lab 1998 - 2015.
// * Version : 1.0.11
// * Home Page : http://rouse.drkb.ru
// * Home Blog : http://alexander-bagel.blogspot.ru
// ****************************************************************************
// * Stable Release : http://rouse.drkb.ru/components.php#fwzip
// * Latest Source : https://github.com/AlexanderBagel/FWZip
// ****************************************************************************
//
// Используемые источники:
// ftp://ftp.info-zip.org/pub/infozip/doc/appnote-iz-latest.zip
// http://zlib.net/zlib-1.2.5.tar.gz
// http://www.base2ti.com/
//
{*************************************************************************************************
* ZLibEx.pas *
* *
* copyright (c) 2000-2013 base2 technologies *
* copyright (c) 1995-2002 Borland Software Corporation *
* *
*************************************************************************************************}
unit FWZipZLib;
{$mode delphi}
{$codepage UTF8}
interface
{$I fwzip.inc}
uses
Classes,
SysUtils,
{$IFDEF USE_ZLIB_EX}
ZLibExApi
{$ELSE}
{$IFDEF USE_ZLIB_DLL}
ZLib_external
{$ELSE}
ZLib
{$ENDIF}
{$ENDIF};
type
TStreamPos = Int64;
TZStrategy = (
zsDefault,
zsFiltered,
zsHuffman,
zsRLE,
zsFixed
);
TCompressionLevel = (
clNone,
clFastest,
clDefault,
clMax,
clLevel1,
clLevel2,
clLevel3,
clLevel4,
clLevel5,
clLevel6,
clLevel7,
clLevel8,
clLevel9
);
TZError = (
zeError,
zeStreamError,
zeDataError,
zeMemoryError,
zeBufferError,
zeVersionError
);
TZFlush = (
zfNoFlush,
zfPartialFlush,
zfSyncFlush,
zfFullFlush,
zfFinish,
zfBlock,
zfTrees
);
const
ZLevels: Array [TCompressionLevel] of Integer = (
Z_NO_COMPRESSION, // zcNone
Z_BEST_SPEED, // zcFastest
Z_DEFAULT_COMPRESSION, // zcDefault
Z_BEST_COMPRESSION, // zcMax
1, // zcLevel1
2, // zcLevel2
3, // zcLevel3
4, // zcLevel4
5, // zcLevel5
6, // zcLevel6
7, // zcLevel7
8, // zcLevel8
9 // zcLevel9
);
{** compression methods ***********************************************************************}
Z_DEFLATED = 8;
{** compression levels ************************************************************************}
Z_NO_COMPRESSION = 0;
Z_BEST_SPEED = 1;
Z_BEST_COMPRESSION = 9;
Z_DEFAULT_COMPRESSION = (-1);
{** flush constants ***************************************************************************}
Z_NO_FLUSH = 0;
Z_PARTIAL_FLUSH = 1;
Z_SYNC_FLUSH = 2;
Z_FULL_FLUSH = 3;
Z_FINISH = 4;
Z_BLOCK = 5;
Z_TREES = 6;
{** compression strategies ********************************************************************}
Z_FILTERED = 1;
Z_HUFFMAN_ONLY = 2;
Z_RLE = 3;
Z_FIXED = 4;
Z_DEFAULT_STRATEGY = 0;
ZStrategies: Array [TZStrategy] of Integer = (
Z_DEFAULT_STRATEGY, // zsDefault
Z_FILTERED, // zsFiltered
Z_HUFFMAN_ONLY, // zsHuffman
Z_RLE, // zsRLE
Z_FIXED // zsFixed
);
ZErrors: Array [TZError] of Integer = (
Z_ERRNO, // zeError
Z_STREAM_ERROR, // zeStreamError
Z_DATA_ERROR, // zeDataError
Z_MEM_ERROR, // zeMemoryError
Z_BUF_ERROR, // zeBufferError
Z_VERSION_ERROR // zeVersionError
);
ZFlushes: Array [TZFlush] of Integer = (
Z_NO_FLUSH, // zfNoFlush
Z_PARTIAL_FLUSH, // zfPartialFlush
Z_SYNC_FLUSH, // zfSyncFlush
Z_FULL_FLUSH, // zfFullFlush
Z_FINISH, // zfFinish
Z_BLOCK, // zfBlock
Z_TREES // zfTrees
);
{** return code messages **********************************************************************}
z_errmsg: Array [0..9] of String = (
'Need dictionary', // Z_NEED_DICT (2)
'Stream end', // Z_STREAM_END (1)
'OK', // Z_OK (0)
'File error', // Z_ERRNO (-1)
'Stream error', // Z_STREAM_ERROR (-2)
'Data error', // Z_DATA_ERROR (-3)
'Insufficient memory', // Z_MEM_ERROR (-4)
'Buffer error', // Z_BUF_ERROR (-5)
'Incompatible version', // Z_VERSION_ERROR (-6)
''
);
type
{** TCustomZStream ****************************************************************************}
TCustomZStream = class(TStream)
private
FStream : TStream;
FStreamPos : TStreamPos;
FOnProgress: TNotifyEvent;
FZStream : TZStreamRec;
FBuffer : Array [Word] of Byte;
function GetStreamPosition: TStreamPos;
procedure SetStreamPosition(value: TStreamPos);
protected
constructor Create(stream: TStream);
function StreamRead(var buffer; count: Longint): Longint;
function StreamWrite(const buffer; count: Longint): Longint;
function StreamSeek(offset: Longint; origin: Word): Longint;
procedure StreamReadBuffer(var buffer; count: Longint);
procedure StreamWriteBuffer(const buffer; count: Longint);
procedure DoProgress; dynamic;
property StreamPosition: TStreamPos read GetStreamPosition write SetStreamPosition;
property OnProgress: TNotifyEvent read FOnProgress write FOnProgress;
end;
{** TZCompressionStream ***********************************************************************}
TZCompressionStream = class(TCustomZStream)
private
function GetCompressionRate: Single;
public
constructor Create(dest: TStream;
compressionLevel: TCompressionLevel = clDefault); overload;
constructor Create(dest: TStream; compressionLevel: TCompressionLevel;
windowBits, memLevel: Integer; strategy: TZStrategy); overload;
destructor Destroy; override;
function Read(var buffer; count: Longint): Longint; override;
function Write(const buffer; count: Longint): Longint; override;
function Seek(offset: Longint; origin: Word): Longint; override;
property CompressionRate: Single read GetCompressionRate;
property OnProgress;
end;
{** TZDecompressionStream *********************************************************************}
TZDecompressionStream = class(TCustomZStream)
public
constructor Create(source: TStream); overload;
constructor Create(source: TStream; windowBits: Integer); overload;
destructor Destroy; override;
function Read(var buffer; count: Longint): Longint; override;
function Write(const buffer; count: Longint): Longint; override;
function Seek(offset: Longint; origin: Word): Longint; override;
property OnProgress;
end;
type
EZLibErrorClass = class of EZlibError;
EZLibError = class(Exception)
private
FErrorCode: Integer;
public
constructor Create(code: Integer; const dummy: String = ''); overload;
constructor Create(error: TZError; const dummy: String = ''); overload;
property ErrorCode: Integer read FErrorCode write FErrorCode;
end;
EZCompressionError = class(EZLibError);
EZDecompressionError = class(EZLibError);
implementation
const
SZInvalid = 'Invalid ZStream operation!';
function ZCompressCheck(code: Integer): Integer;
begin
result := code;
if code < 0 then
begin
raise EZCompressionError.Create(code);
end;
end;
function ZDecompressCheck(code: Integer; raiseBufferError: Boolean = True): Integer;
begin
Result := code;
if code < 0 then
begin
if (code <> Z_BUF_ERROR) or raiseBufferError then
begin
raise EZDecompressionError.Create(code);
end;
end;
end;
{** zlib deflate routines ***********************************************************************}
function ZDeflateInit(var stream: TZStreamRec;
level: TCompressionLevel): Integer;
begin
result := deflateInit_(stream, ZLevels[level], ZLIB_VERSION,
SizeOf(TZStreamRec));
end;
function ZDeflateInit2(var stream: TZStreamRec;
level: TCompressionLevel; windowBits, memLevel: Integer;
strategy: TZStrategy): Integer;
begin
{$IFDEF OLDEST_ZLIB}
result := ZDeflateInit(stream, level);
{$ELSE}
result := deflateInit2_(stream, ZLevels[level], Z_DEFLATED, windowBits,
memLevel, ZStrategies[strategy], ZLIB_VERSION, SizeOf(TZStreamRec));
{$ENDIF}
end;
function ZDeflate(var stream: TZStreamRec; flush: TZFlush): Integer;
begin
result := deflate(stream, ZFlushes[flush]);
end;
function ZDeflateEnd(var stream: TZStreamRec): Integer;
begin
result := deflateEnd(stream);
end;
{** zlib inflate routines ***********************************************************************}
function ZInflateInit(var stream: TZStreamRec): Integer;
begin
result := inflateInit_(stream, ZLIB_VERSION, SizeOf(TZStreamRec));
end;
function ZInflateInit2(var stream: TZStreamRec;
windowBits: Integer): Integer;
begin
{$IFDEF OLDEST_ZLIB}
result := ZInflateInit(stream);
{$ELSE}
result := inflateInit2_(stream, windowBits, ZLIB_VERSION,
SizeOf(TZStreamRec));
{$ENDIF}
end;
function ZInflate(var stream: TZStreamRec; flush: TZFlush): Integer;
begin
result := inflate(stream, ZFlushes[flush]);
end;
function ZInflateEnd(var stream: TZStreamRec): Integer;
begin
result := inflateEnd(stream);
end;
function ZInflateReset(var stream: TZStreamRec): Integer;
begin
result := inflateReset(stream);
end;
{** EZLibError **********************************************************************************}
constructor EZLibError.Create(code: Integer; const dummy: String);
begin
inherited Create(z_errmsg[2 - code]);
FErrorCode := code;
end;
constructor EZLibError.Create(error: TZError; const dummy: String);
begin
Create(ZErrors[error], dummy);
end;
{** TCustomZStream ******************************************************************************}
constructor TCustomZStream.Create(stream: TStream);
begin
inherited Create;
FStream := stream;
FStreamPos := stream.Position;
{$IFDEF OLDEST_ZLIB}
FZStream.zalloc := zlibAllocMem;
FZStream.zfree := zlibFreeMem;
{$ENDIF}
end;
function TCustomZStream.StreamRead(var buffer; count: Longint): Longint;
begin
if FStream.Position <> FStreamPos then FStream.Position := FStreamPos;
result := FStream.Read(buffer,count);
FStreamPos := FStreamPos + result;
end;
function TCustomZStream.StreamWrite(const buffer; count: Longint): Longint;
begin
if FStream.Position <> FStreamPos then FStream.Position := FStreamPos;
result := FStream.Write(buffer,count);
FStreamPos := FStreamPos + result;
end;
function TCustomZStream.StreamSeek(offset: Longint; origin: Word): Longint;
begin
if FStream.Position <> FStreamPos then FStream.Position := FStreamPos;
result := FStream.Seek(offset,origin);
FStreamPos := FStream.Position;
end;
procedure TCustomZStream.StreamReadBuffer(var buffer; count: Longint);
begin
if FStream.Position <> FStreamPos then FStream.Position := FStreamPos;
FStream.ReadBuffer(buffer,count);
FStreamPos := FStreamPos + count;
end;
procedure TCustomZStream.StreamWriteBuffer(const buffer; count: Longint);
begin
if FStream.Position <> FStreamPos then FStream.Position := FStreamPos;
FStream.WriteBuffer(buffer,count);
FStreamPos := FStreamPos + count;
end;
procedure TCustomZStream.DoProgress;
begin
if Assigned(FOnProgress) then FOnProgress(Self);
end;
function TCustomZStream.GetStreamPosition: TStreamPos;
begin
result := FStream.Position;
end;
procedure TCustomZStream.SetStreamPosition(value: TStreamPos);
begin
FStream.Position := value;
FStreamPos := FStream.Position;
end;
{** TZCompressionStream *************************************************************************}
constructor TZCompressionStream.Create(dest: TStream;
compressionLevel: TCompressionLevel);
begin
inherited Create(dest);
FZStream.next_out := @FBuffer;
FZStream.avail_out := SizeOf(FBuffer);
ZCompressCheck(ZDeflateInit(FZStream, compressionLevel));
end;
constructor TZCompressionStream.Create(dest: TStream;
compressionLevel: TCompressionLevel; windowBits, memLevel: Integer;
strategy: TZStrategy);
begin
{$IFDEF USE_AUTOGENERATED_ZLIB_HEADER}
Create(dest, compressionLevel);
{$ELSE}
inherited Create(dest);
FZStream.next_out := @FBuffer;
FZStream.avail_out := SizeOf(FBuffer);
ZCompressCheck(ZDeflateInit2(FZStream, compressionLevel, windowBits,
memLevel, strategy));
{$ENDIF}
end;
destructor TZCompressionStream.Destroy;
begin
FZStream.next_in := Nil;
FZStream.avail_in := 0;
try
while ZCompressCheck(ZDeflate(FZStream, zfFinish)) <> Z_STREAM_END do
begin
StreamWriteBuffer(FBuffer, SizeOf(FBuffer) - FZStream.avail_out);
FZStream.next_out := @FBuffer;
FZStream.avail_out := SizeOf(FBuffer);
end;
if FZStream.avail_out < SizeOf(FBuffer) then
begin
StreamWriteBuffer(FBuffer, SizeOf(FBuffer) - FZStream.avail_out);
end;
finally
ZDeflateEnd(FZStream);
end;
inherited Destroy;
end;
function TZCompressionStream.Read(var buffer; count: Longint): Longint;
begin
raise EZCompressionError.Create(SZInvalid);
end;
function TZCompressionStream.Write(const buffer; count: Longint): Longint;
var
writeCount: Longint;
begin
result := count;
FZStream.next_in := @buffer;
FZStream.avail_in := count;
while FZStream.avail_in > 0 do
begin
ZCompressCheck(ZDeflate(FZStream, zfNoFlush));
if FZStream.avail_out = 0 then
begin
writeCount := StreamWrite(FBuffer,SizeOf(FBuffer));
if writeCount = SizeOf(FBuffer) then
begin
FZStream.next_out := @FBuffer;
FZStream.avail_out := SizeOf(FBuffer);
DoProgress;
end
else
begin
StreamPosition := StreamPosition - writeCount;
result := Cardinal(count) - Cardinal(FZStream.avail_in);
FZStream.avail_in := 0;
end;
end;
end;
end;
function TZCompressionStream.Seek(offset: Longint; origin: Word): Longint;
begin
if (offset = 0) and (origin = soFromCurrent) then
begin
result := FZStream.total_in;
end
else raise EZCompressionError.Create(SZInvalid);
end;
function TZCompressionStream.GetCompressionRate: Single;
begin
if FZStream.total_in = 0 then result := 0
else result := (1.0 - (FZStream.total_out / FZStream.total_in)) * 100.0;
end;
{** TZDecompressionStream ***********************************************************************}
constructor TZDecompressionStream.Create(source: TStream);
begin
inherited Create(source);
FZStream.next_in := @FBuffer;
FZStream.avail_in := 0;
ZDecompressCheck(ZInflateInit(FZStream));
end;
constructor TZDecompressionStream.Create(source: TStream;
windowBits: Integer);
begin
{$IFDEF USE_AUTOGENERATED_ZLIB_HEADER}
Create(source);
{$ELSE}
inherited Create(source);
FZStream.next_in := @FBuffer;
FZStream.avail_in := 0;
ZDecompressCheck(ZInflateInit2(FZStream, windowBits));
{$ENDIF}
end;
destructor TZDecompressionStream.Destroy;
begin
ZInflateEnd(FZStream);
inherited Destroy;
end;
function TZDecompressionStream.Read(var buffer; count: Longint): Longint;
var
zresult: Integer;
begin
FZStream.next_out := @buffer;
FZStream.avail_out := count;
zresult := Z_OK;
while (FZStream.avail_out > 0) and (zresult <> Z_STREAM_END) do
begin
if FZStream.avail_in = 0 then
begin
FZStream.avail_in := StreamRead(FBuffer,SizeOf(FBuffer));
if FZStream.avail_in = 0 then
begin
result := Cardinal(count) - Cardinal(FZStream.avail_out);
Exit;
end;
FZStream.next_in := @FBuffer;
DoProgress;
end;
zresult := ZDecompressCheck(ZInflate(FZStream, zfNoFlush));
end;
if (zresult = Z_STREAM_END) and (FZStream.avail_in > 0) then
begin
StreamPosition := StreamPosition - FZStream.avail_in;
FZStream.avail_in := 0;
end;
result := Cardinal(count) - Cardinal(FZStream.avail_out);
end;
function TZDecompressionStream.Write(const Buffer; Count: Longint): Longint;
begin
raise EZDecompressionError.Create(SZInvalid);
end;
function TZDecompressionStream.Seek(Offset: Longint; Origin: Word): Longint;
var
buf: Array [0..8191] of Byte;
i : Integer;
begin
if (offset = 0) and (origin = soFromBeginning) then
begin
ZDecompressCheck(ZInflateReset(FZStream));
FZStream.next_in := @FBuffer;
FZStream.avail_in := 0;
StreamPosition := 0;
end
else if ((offset >= 0) and (origin = soFromCurrent)) or
(((Cardinal(offset) - Cardinal(FZStream.total_out)) > 0) and
(origin = soFromBeginning)) then
begin
if origin = soFromBeginning then Dec(offset, FZStream.total_out);
if offset > 0 then
begin
for i := 1 to offset div SizeOf(buf) do ReadBuffer(buf, SizeOf(buf));
ReadBuffer(buf, offset mod SizeOf(buf));
end;
end
else if (offset = 0) and (origin = soFromEnd) then
begin
while Read(buf, SizeOf(buf)) > 0 do ;
end
else raise EZDecompressionError.Create(SZInvalid);
result := FZStream.total_out;
end;
end.