780 lines
24 KiB
ObjectPascal
780 lines
24 KiB
ObjectPascal
{**************************************************************************************************}
|
|
{ }
|
|
{ Project JEDI Code Library (JCL) }
|
|
{ }
|
|
{ The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); }
|
|
{ you may not use this file except in compliance with the License. You may obtain a copy of the }
|
|
{ License at http://www.mozilla.org/MPL/ }
|
|
{ }
|
|
{ Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF }
|
|
{ ANY KIND, either express or implied. See the License for the specific language governing rights }
|
|
{ and limitations under the License. }
|
|
{ }
|
|
{ The Original Code is JclCompression.pas. }
|
|
{ }
|
|
{ The Initial Developer of the Original Code is Matthias Thoma. }
|
|
{ All Rights Reserved. }
|
|
{ }
|
|
{ Contributors: }
|
|
{ }
|
|
{**************************************************************************************************}
|
|
{ }
|
|
{ Alternatively, the contents of this file may be used under the terms of the GNU Lesser General }
|
|
{ Public License (the "LGPL License"), in which case the provisions of the LGPL License are }
|
|
{ applicable instead of those above. If you wish to allow use of your version of this file only }
|
|
{ under the terms of the LGPL License and not to allow others to use your version of this file }
|
|
{ under the MPL, indicate your decision by deleting the provisions above and replace them with the }
|
|
{ notice and other provisions required by the LGPL License. If you do not delete the provisions }
|
|
{ above, a recipient may use your version of this file under either the MPL or the LGPL License. }
|
|
{ }
|
|
{ For more information about the LGPL: }
|
|
{ http://www.gnu.org/copyleft/lesser.html }
|
|
{ }
|
|
{**************************************************************************************************}
|
|
{ }
|
|
{ This unit is still in alpha state. It is likely that it will change a lot. Suggestions are }
|
|
{ welcome. }
|
|
{ }
|
|
{**************************************************************************************************}
|
|
|
|
// Last modified: $Date: 2005/10/25 04:46:31 $
|
|
// For history see end of file
|
|
|
|
unit JclCompression;
|
|
|
|
interface
|
|
|
|
uses
|
|
{$IFDEF MSWINDOWS}
|
|
Windows,
|
|
{$ENDIF MSWINDOWS}
|
|
{$IFDEF UNIX}
|
|
Types,
|
|
{$ENDIF UNIX}
|
|
{$IFDEF HAS_UNIT_LIBC}
|
|
Libc,
|
|
{$ENDIF HAS_UNIT_LIBC}
|
|
SysUtils, Classes,
|
|
JclBase,
|
|
zlibh;
|
|
|
|
{**************************************************************************************************}
|
|
{
|
|
TJclCompressionStream
|
|
- -
|
|
----------------------- --------------------------
|
|
- -
|
|
TJclCompressStream TJclDecompressStream
|
|
- -
|
|
--------------------------------- ---------------------------------
|
|
- - - - - -
|
|
- - - - - -
|
|
TJclZLibCompressStream - TBZIP2CompressStram TJclZLibDecompressStream - TBZIP2DeCompressStream
|
|
- -
|
|
- TGZDecompressStream
|
|
TGZCompressStream
|
|
|
|
}
|
|
{**************************************************************************************************}
|
|
|
|
type
|
|
TJclCompressionStream = class(TStream)
|
|
private
|
|
FOnProgress: TNotifyEvent;
|
|
FBuffer: Pointer;
|
|
FBufferSize: Cardinal;
|
|
FStream: TStream;
|
|
protected
|
|
function SetBufferSize(Size: Cardinal): Cardinal; virtual;
|
|
procedure Progress(Sender: TObject); dynamic;
|
|
property OnProgress: TNotifyEvent read FOnProgress write FOnProgress;
|
|
public
|
|
constructor Create(Stream: TStream);
|
|
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;
|
|
procedure Reset; virtual;
|
|
end;
|
|
|
|
TJclCompressStream = class(TJclCompressionStream)
|
|
public
|
|
function Flush: Integer; dynamic; abstract;
|
|
constructor Create(Destination: TStream);
|
|
end;
|
|
|
|
TJclDecompressStream = class(TJclCompressionStream)
|
|
public
|
|
constructor Create(Source: TStream);
|
|
end;
|
|
|
|
// ZIP Support
|
|
TJclCompressionLevel = Integer;
|
|
|
|
TJclZLibCompressStream = class(TJclCompressStream)
|
|
private
|
|
FWindowBits: Integer;
|
|
FMemLevel: Integer;
|
|
FMethod: Integer;
|
|
FStrategy: Integer;
|
|
FDeflateInitialized: Boolean;
|
|
FCompressionLevel: Integer;
|
|
protected
|
|
ZLibRecord: TZStreamRec;
|
|
procedure SetCompressionLevel(Value: Integer);
|
|
procedure SetStrategy(Value: Integer);
|
|
procedure SetMemLevel(Value: Integer);
|
|
procedure SetMethod(Value: Integer);
|
|
procedure SetWindowBits(Value: Integer);
|
|
public
|
|
constructor Create(Destination: TStream; CompressionLevel: TJclCompressionLevel = -1);
|
|
destructor Destroy; override;
|
|
function Flush: Integer; override;
|
|
procedure Reset; override;
|
|
function Seek(Offset: Longint; Origin: Word): Longint; override;
|
|
function Write(const Buffer; Count: Longint): Longint; override;
|
|
property WindowBits: Integer read FWindowBits write SetWindowBits;
|
|
property MemLevel: Integer read FMemLevel write SetMemLevel;
|
|
property Method: Integer read FMethod write SetMethod;
|
|
property Strategy: Integer read FStrategy write SetStrategy;
|
|
property CompressionLevel: Integer read FCompressionLevel write SetCompressionLevel;
|
|
end;
|
|
|
|
TJclZLibDecompressStream = class(TJclDecompressStream)
|
|
private
|
|
FWindowBits: Integer;
|
|
FInflateInitialized: Boolean;
|
|
protected
|
|
ZLibRecord: TZStreamRec;
|
|
procedure SetWindowBits(Value: Integer);
|
|
public
|
|
constructor Create(Source: TStream; WindowBits: Integer = DEF_WBITS);
|
|
destructor Destroy; override;
|
|
function Read(var Buffer; Count: Longint): Longint; override;
|
|
function Seek(Offset: Longint; Origin: Word): Longint; override;
|
|
property WindowBits: Integer read FWindowBits write SetWindowBits;
|
|
end;
|
|
|
|
// GZIP Support
|
|
TJclGZIPCompressionStream = class(TJclCompressionStream)
|
|
end;
|
|
|
|
TJclGZIPDecompressionStream = class(TJclDecompressStream)
|
|
end;
|
|
|
|
// RAR Support
|
|
TJclRARCompressionStream = class(TJclCompressionStream)
|
|
end;
|
|
|
|
TJclRARDecompressionStream = class(TJclDecompressStream)
|
|
end;
|
|
|
|
// TAR Support
|
|
TJclTARCompressionStream = class(TJclCompressionStream)
|
|
end;
|
|
|
|
TJclTARDecompressionStream = class(TJclDecompressStream)
|
|
end;
|
|
|
|
// BZIP2 Support
|
|
(*
|
|
TJclBZIP2CompressStream = class(TJclCompressStream)
|
|
private
|
|
FDeflateInitialized: Boolean;
|
|
|
|
protected
|
|
BZLibRecord: TBZStream;
|
|
public
|
|
function Flush: Integer; override;
|
|
function Seek(Offset: Longint; Origin: Word): Longint; override;
|
|
function Write(const Buffer; Count: Longint): Longint; override;
|
|
|
|
constructor Create(Destination: TStream; CompressionLevel: TJclCompressionLevel = -1);
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
TJclBZIP2DecompressStream = class(TJclDecompressStream)
|
|
private
|
|
FInflateInitialized: Boolean;
|
|
|
|
protected
|
|
BZLibRecord: TBZStream;
|
|
|
|
public
|
|
function Read(var Buffer; Count: Longint): Longint; override;
|
|
function Seek(Offset: Longint; Origin: Word): Longint; override;
|
|
|
|
constructor Create(Source: TStream); overload;
|
|
destructor Destroy; override;
|
|
end;
|
|
*)
|
|
|
|
EJclCompressionError = class(EJclError);
|
|
|
|
implementation
|
|
|
|
uses
|
|
JclResources;
|
|
|
|
const
|
|
JclDefaultBufferSize = 131072; // 128k
|
|
|
|
//=== { TJclCompressionStream } ==============================================
|
|
|
|
constructor TJclCompressionStream.Create(Stream: TStream);
|
|
begin
|
|
inherited Create;
|
|
FBuffer := nil;
|
|
SetBufferSize(JclDefaultBufferSize);
|
|
end;
|
|
|
|
destructor TJclCompressionStream.Destroy;
|
|
begin
|
|
SetBufferSize(0);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
function TJclCompressionStream.Read(var Buffer; Count: Longint): Longint;
|
|
begin
|
|
raise EJclCompressionError.CreateRes(@RsCompressionReadNotSupported);
|
|
end;
|
|
|
|
function TJclCompressionStream.Write(const Buffer; Count: Longint): Longint;
|
|
begin
|
|
raise EJclCompressionError.CreateRes(@RsCompressionWriteNotSupported);
|
|
end;
|
|
|
|
function TJclCompressionStream.Seek(Offset: Longint; Origin: Word): Longint;
|
|
begin
|
|
raise EJclCompressionError.CreateRes(@RsCompressionSeekNotSupported);
|
|
end;
|
|
|
|
procedure TJclCompressionStream.Reset;
|
|
begin
|
|
raise EJclCompressionError.CreateRes(@RsCompressionResetNotSupported);
|
|
end;
|
|
|
|
function TJclCompressionStream.SetBufferSize(Size: Cardinal): Cardinal;
|
|
begin
|
|
if FBuffer <> nil then
|
|
FreeMem(FBuffer, FBufferSize);
|
|
|
|
FBufferSize := Size;
|
|
|
|
if FBufferSize > 0 then
|
|
GetMem(FBuffer, FBufferSize)
|
|
else
|
|
FBuffer := nil;
|
|
|
|
Result := FBufferSize;
|
|
end;
|
|
|
|
procedure TJclCompressionStream.Progress(Sender: TObject);
|
|
begin
|
|
if Assigned(FOnProgress) then
|
|
FOnProgress(Sender);
|
|
end;
|
|
|
|
//=== { TJclCompressStream } =================================================
|
|
|
|
constructor TJclCompressStream.Create(Destination: TStream);
|
|
begin
|
|
inherited Create(Destination);
|
|
FStream := Destination;
|
|
end;
|
|
|
|
//=== { TJclDecompressStream } ===============================================
|
|
|
|
constructor TJclDecompressStream.Create(Source: TStream);
|
|
begin
|
|
inherited Create(Source);
|
|
FStream := Source;
|
|
end;
|
|
|
|
|
|
//=== { TJclZLibCompressionStream } ==========================================
|
|
|
|
{ Error checking helper }
|
|
|
|
function ZLibCheck(const ErrCode: Integer): Integer;
|
|
begin
|
|
Result := ErrCode;
|
|
if ErrCode < 0 then
|
|
case ErrCode of
|
|
Z_ERRNO:
|
|
raise EJclCompressionError.CreateRes(@RsCompressionZLibZErrNo);
|
|
Z_STREAM_ERROR:
|
|
raise EJclCompressionError.CreateRes(@RsCompressionZLibZStreamError);
|
|
Z_DATA_ERROR:
|
|
raise EJclCompressionError.CreateRes(@RsCompressionZLibZDataError);
|
|
Z_MEM_ERROR:
|
|
raise EJclCompressionError.CreateRes(@RsCompressionZLibZMemError);
|
|
Z_BUF_ERROR:
|
|
raise EJclCompressionError.CreateRes(@RsCompressionZLibZBufError);
|
|
Z_VERSION_ERROR:
|
|
raise EJclCompressionError.CreateRes(@RsCompressionZLibZVersionError);
|
|
else
|
|
raise EJclCompressionError.CreateRes(@RsCompressionZLibError);
|
|
end;
|
|
end;
|
|
|
|
constructor TJclZLibCompressStream.Create(Destination: TStream; CompressionLevel: TJclCompressionLevel);
|
|
begin
|
|
inherited Create(Destination);
|
|
|
|
Assert(FBuffer <> nil);
|
|
Assert(FBufferSize > 0);
|
|
|
|
// Initialize ZLib StreamRecord
|
|
with ZLibRecord do
|
|
begin
|
|
zalloc := nil; // Use build-in memory allocation functionality
|
|
zfree := nil;
|
|
next_in := nil;
|
|
avail_in := 0;
|
|
next_out := FBuffer;
|
|
avail_out := FBufferSize;
|
|
end;
|
|
|
|
FWindowBits := DEF_WBITS;
|
|
FMemLevel := DEF_MEM_LEVEL;
|
|
FMethod := Z_DEFLATED;
|
|
FStrategy := Z_DEFAULT_STRATEGY;
|
|
FCompressionLevel := CompressionLevel;
|
|
FDeflateInitialized := False;
|
|
end;
|
|
|
|
destructor TJclZLibCompressStream.Destroy;
|
|
begin
|
|
Flush;
|
|
if FDeflateInitialized then
|
|
begin
|
|
ZLibRecord.next_in := nil;
|
|
ZLibRecord.avail_in := 0;
|
|
ZLibRecord.avail_out := 0;
|
|
ZLibRecord.next_out := nil;
|
|
|
|
ZLibCheck(deflateEnd(ZLibRecord));
|
|
end;
|
|
|
|
inherited Destroy;
|
|
end;
|
|
|
|
function TJclZLibCompressStream.Write(const Buffer; Count: Longint): Longint;
|
|
begin
|
|
if not FDeflateInitialized then
|
|
begin
|
|
ZLibCheck(deflateInit2(ZLibRecord, FCompressionLevel, FMethod, FWindowBits, FMemLevel, FStrategy));
|
|
FDeflateInitialized := True;
|
|
end;
|
|
|
|
ZLibRecord.next_in := @Buffer;
|
|
ZLibRecord.avail_in := Count;
|
|
|
|
while ZLibRecord.avail_in > 0 do
|
|
begin
|
|
ZLibCheck(deflate(ZLibRecord, Z_NO_FLUSH));
|
|
|
|
if ZLibRecord.avail_out = 0 then // Output buffer empty. Write to stream and go on...
|
|
begin
|
|
FStream.WriteBuffer(FBuffer^, FBufferSize);
|
|
Progress(Self);
|
|
ZLibRecord.next_out := FBuffer;
|
|
ZLibRecord.avail_out := FBufferSize;
|
|
end;
|
|
end;
|
|
|
|
Result := Count;
|
|
end;
|
|
|
|
function TJclZLibCompressStream.Flush: Integer;
|
|
begin
|
|
Result := 0;
|
|
|
|
if FDeflateInitialized then
|
|
begin
|
|
ZLibRecord.next_in := nil;
|
|
ZLibRecord.avail_in := 0;
|
|
|
|
while (ZLibCheck(deflate(ZLibRecord, Z_FINISH)) <> Z_STREAM_END) and
|
|
(ZLibRecord.avail_out = 0) do
|
|
begin
|
|
FStream.WriteBuffer(FBuffer^, FBufferSize);
|
|
Progress(Self);
|
|
|
|
ZLibRecord.next_out := FBuffer;
|
|
ZLibRecord.avail_out := FBufferSize;
|
|
Inc(Result, FBufferSize);
|
|
end;
|
|
|
|
if ZLibRecord.avail_out < FBufferSize then
|
|
begin
|
|
FStream.WriteBuffer(FBuffer^, FBufferSize-ZLibRecord.avail_out);
|
|
Progress(Self);
|
|
Inc(Result, FBufferSize - ZLibRecord.avail_out);
|
|
ZLibRecord.next_out := FBuffer;
|
|
ZLibRecord.avail_out := FBufferSize;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TJclZLibCompressStream.Seek(Offset: Longint; Origin: Word): Longint;
|
|
begin
|
|
if (Offset = 0) and (Origin = soFromCurrent) then
|
|
Result := ZLibRecord.total_in
|
|
else
|
|
if (Offset = 0) and (Origin = soFromBeginning) and (ZLibRecord.total_in = 0) then
|
|
Result := 0
|
|
else
|
|
Result := inherited Seek(Offset, Origin);
|
|
end;
|
|
|
|
procedure TJclZLibCompressStream.SetWindowBits(Value: Integer);
|
|
begin
|
|
FWindowBits := Value;
|
|
end;
|
|
|
|
procedure TJclZLibCompressStream.SetMethod(Value: Integer);
|
|
begin
|
|
FMethod := Value;
|
|
end;
|
|
|
|
procedure TJclZLibCompressStream.SetStrategy(Value: Integer);
|
|
begin
|
|
FStrategy := Value;
|
|
if FDeflateInitialized then
|
|
ZLibCheck(deflateParams(ZLibRecord, FCompressionLevel, FStrategy));
|
|
end;
|
|
|
|
procedure TJclZLibCompressStream.SetMemLevel(Value: Integer);
|
|
begin
|
|
FMemLevel := Value;
|
|
end;
|
|
|
|
procedure TJclZLibCompressStream.SetCompressionLevel(Value: Integer);
|
|
begin
|
|
FCompressionLevel := Value;
|
|
if FDeflateInitialized then
|
|
ZLibCheck(deflateParams(ZLibRecord, FCompressionLevel, FStrategy));
|
|
end;
|
|
|
|
procedure TJclZLibCompressStream.Reset;
|
|
begin
|
|
if FDeflateInitialized then
|
|
begin
|
|
Flush;
|
|
ZLibCheck(deflateReset(ZLibRecord));
|
|
end;
|
|
end;
|
|
|
|
|
|
//=== { TJclZLibDecompressionStream } =======================================
|
|
|
|
constructor TJclZLibDecompressStream.Create(Source: TStream; WindowBits: Integer = DEF_WBITS);
|
|
begin
|
|
inherited Create(Source);
|
|
|
|
// Initialize ZLib StreamRecord
|
|
with ZLibRecord do
|
|
begin
|
|
zalloc := nil; // Use build-in memory allocation functionality
|
|
zfree := nil;
|
|
next_in := nil;
|
|
avail_in := 0;
|
|
next_out := FBuffer;
|
|
avail_out := FBufferSize;
|
|
end;
|
|
|
|
FInflateInitialized := False;
|
|
FWindowBits := WindowBits;
|
|
end;
|
|
|
|
destructor TJclZLibDecompressStream.Destroy;
|
|
begin
|
|
if FInflateInitialized then
|
|
begin
|
|
FStream.Seek(-ZLibRecord.avail_in, soFromCurrent);
|
|
ZLibCheck(inflateEnd(ZLibRecord));
|
|
end;
|
|
|
|
inherited Destroy;
|
|
end;
|
|
|
|
function TJclZLibDecompressStream.Read(var Buffer; Count: Longint): Longint;
|
|
begin
|
|
if not FInflateInitialized then
|
|
begin
|
|
ZLibCheck(InflateInit2(ZLibRecord, FWindowBits));
|
|
FInflateInitialized := True;
|
|
end;
|
|
|
|
ZLibRecord.next_out := @Buffer;
|
|
ZLibRecord.avail_out := Count;
|
|
|
|
while ZLibRecord.avail_out > 0 do // as long as we have data
|
|
begin
|
|
if ZLibRecord.avail_in = 0 then
|
|
begin
|
|
ZLibRecord.avail_in := FStream.Read(FBuffer^, FBufferSize);
|
|
|
|
if ZLibRecord.avail_in = 0 then
|
|
begin
|
|
Result := Count - Longint(ZLibRecord.avail_out);
|
|
Exit;
|
|
end;
|
|
|
|
ZLibRecord.next_in := FBuffer;
|
|
end;
|
|
|
|
if ZLibRecord.avail_in > 0 then
|
|
begin
|
|
ZLibCheck(inflate(ZLibRecord, Z_NO_FLUSH));
|
|
Progress(Self);
|
|
end;
|
|
end;
|
|
|
|
Result := Count;
|
|
end;
|
|
|
|
function TJclZLibDecompressStream.Seek(Offset: Longint; Origin: Word): Longint;
|
|
begin
|
|
if (Offset = 0) and (Origin = soFromCurrent) then
|
|
Result := ZLibRecord.total_out
|
|
else
|
|
Result := inherited Seek(Offset, Origin);
|
|
end;
|
|
|
|
procedure TJclZLibDecompressStream.SetWindowBits(Value: Integer);
|
|
begin
|
|
FWindowBits := Value;
|
|
end;
|
|
|
|
//=== { TJclBZLibCompressionStream } =========================================
|
|
(*
|
|
{ Error checking helper }
|
|
|
|
function BZIP2LibCheck(const ErrCode: Integer): Integer;
|
|
begin
|
|
Result := ErrCode;
|
|
|
|
if ErrCode < 0 then
|
|
begin
|
|
case ErrCode of
|
|
Z_ERRNO: raise EJclCompressionError.CreateRes(@RsCompressionZLibZErrNo);
|
|
Z_STREAM_ERROR: raise EJclCompressionError.CreateRes(@RsCompressionZLibZStreamError);
|
|
Z_DATA_ERROR: raise EJclCompressionError.CreateRes(@RsCompressionZLibZDataError);
|
|
Z_MEM_ERROR: raise EJclCompressionError.CreateRes(@RsCompressionZLibZMemError);
|
|
Z_BUF_ERROR: raise EJclCompressionError.CreateRes(@RsCompressionZLibZBufError);
|
|
Z_VERSION_ERROR: raise EJclCompressionError.CreateRes(@RsCompressionZLibZVersionError);
|
|
else
|
|
raise EJclCompressionError.CreateRes(@RsCompressionZLibError);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
constructor TJclBZIP2CompressStream.Create(Destination: TStream; CompressionLevel: TJclCompressionLevel);
|
|
begin
|
|
inherited Create(Destination);
|
|
|
|
Assert(FBuffer <> nil);
|
|
Assert(FBufferSize > 0);
|
|
|
|
// Initialize ZLib StreamRecord
|
|
with BZLibRecord do
|
|
begin
|
|
bzalloc := nil; // Use build-in memory allocation functionality
|
|
bzfree := nil;
|
|
next_in := nil;
|
|
avail_in := 0;
|
|
next_out := FBuffer;
|
|
avail_out := FBufferSize;
|
|
|
|
end;
|
|
|
|
FDeflateInitialized := False;
|
|
end;
|
|
|
|
destructor TJclBZIP2CompressStream.Destroy;
|
|
begin
|
|
Flush;
|
|
if FDeflateInitialized then
|
|
BZIP2LibCheck(BZ2_bzCompressEnd(@BZLibRecord));
|
|
|
|
inherited Destroy;
|
|
end;
|
|
|
|
function TJclBZIP2CompressStream.Write(const Buffer; Count: Longint): Longint;
|
|
begin
|
|
if not FDeflateInitialized then
|
|
begin
|
|
BZIP2LibCheck(BZ2_bzCompressInit(@BZLibRecord,9,0,0));
|
|
FDeflateInitialized := True;
|
|
end;
|
|
|
|
BZLibRecord.next_in := @Buffer;
|
|
BZLibRecord.avail_in := Count;
|
|
|
|
while BZLibRecord.avail_in > 0 do
|
|
begin
|
|
BZIP2LibCheck(BZ2_bzCompress(@BZLibRecord, BZ_RUN));
|
|
|
|
if BZLibRecord.avail_out = 0 then // Output buffer empty. Write to stream and go on...
|
|
begin
|
|
FStream.WriteBuffer(FBuffer^, FBufferSize);
|
|
Progress(Self);
|
|
BZLibRecord.next_out := FBuffer;
|
|
BZLibRecord.avail_out := FBufferSize;
|
|
end;
|
|
end;
|
|
|
|
Result := Count;
|
|
end;
|
|
|
|
function TJclBZIP2CompressStream.Flush: Integer;
|
|
begin
|
|
Result := 0;
|
|
|
|
if FDeflateInitialized then
|
|
begin
|
|
BZLibRecord.next_in := nil;
|
|
BZLibRecord.avail_in := 0;
|
|
|
|
while (BZIP2LibCheck(BZ2_bzCompress(@BZLibRecord, BZ_FLUSH)) <> Z_STREAM_END) and (BZLibRecord.avail_out = 0) do
|
|
begin
|
|
FStream.WriteBuffer(FBuffer^, FBufferSize);
|
|
Progress(Self);
|
|
|
|
BZLibRecord.next_out := FBuffer;
|
|
BZLibRecord.avail_out := FBufferSize;
|
|
Result := Result + FBufferSize;
|
|
end;
|
|
|
|
if BZLibRecord.avail_out < FBufferSize then
|
|
begin
|
|
FStream.WriteBuffer(FBuffer^, FBufferSize-BZLibRecord.avail_out);
|
|
Progress(Self);
|
|
Result := Result + FBufferSize-BZLibRecord.avail_out;
|
|
BZLibRecord.next_out := FBuffer;
|
|
BZLibRecord.avail_out := FBufferSize;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TJclBZIP2CompressStream.Seek(Offset: Longint; Origin: Word): Longint;
|
|
begin
|
|
if (Offset = 0) and (Origin = soFromCurrent) then
|
|
Result := BZLibRecord.total_in_lo32
|
|
else
|
|
if (Offset = 0) and (Origin = soFromBeginning) and (BZLibRecord.total_in_lo32 = 0) then
|
|
Result := 0
|
|
else
|
|
Result := inherited Seek(Offset, Origin);
|
|
end;
|
|
|
|
//=== { TJclZLibDecompressionStream } ========================================
|
|
|
|
constructor TJclBZIP2DecompressStream.Create(Source: TStream);
|
|
begin
|
|
inherited Create(Source);
|
|
|
|
// Initialize ZLib StreamRecord
|
|
with BZLibRecord do
|
|
begin
|
|
bzalloc := nil; // Use build-in memory allocation functionality
|
|
bzfree := nil;
|
|
opaque := nil;
|
|
next_in := nil;
|
|
state := nil;
|
|
avail_in := 0;
|
|
next_out := FBuffer;
|
|
avail_out := FBufferSize;
|
|
end;
|
|
|
|
FInflateInitialized := False;
|
|
end;
|
|
|
|
destructor TJclBZIP2DecompressStream.Destroy;
|
|
begin
|
|
if FInflateInitialized then
|
|
begin
|
|
FStream.Seek(-BZLibRecord.avail_in, soFromCurrent);
|
|
BZIP2LibCheck(BZ2_bzDecompressEnd(@BZLibRecord));
|
|
end;
|
|
|
|
inherited Destroy;
|
|
end;
|
|
|
|
function TJclBZIP2DecompressStream.Read(var Buffer; Count: Longint): Longint;
|
|
var
|
|
avail_out_ctr: Integer;
|
|
|
|
begin
|
|
if not FInflateInitialized then
|
|
begin
|
|
BZIP2LibCheck(BZ2_bzDecompressInit(@BZLibRecord,0,0));
|
|
FInflateInitialized := True;
|
|
end;
|
|
|
|
BZLibRecord.next_out := @Buffer;
|
|
BZLibRecord.avail_out := Count;
|
|
avail_out_ctr := Count;
|
|
|
|
while avail_out_ctr > 0 do // as long as we have data
|
|
begin
|
|
if BZLibRecord.avail_in = 0 then
|
|
begin
|
|
BZLibRecord.avail_in := FStream.Read(FBuffer^, FBufferSize);
|
|
if BZLibRecord.avail_in = 0 then
|
|
begin
|
|
Result := Count - avail_out_ctr;
|
|
Exit;
|
|
end;
|
|
|
|
BZLibRecord.next_in := FBuffer;
|
|
end;
|
|
|
|
|
|
if BZLibRecord.avail_in > 0 then
|
|
begin
|
|
BZIP2LibCheck(BZ2_bzDecompress(@BZLibRecord));
|
|
avail_out_ctr := Count - BZLibRecord.avail_out;
|
|
end
|
|
end;
|
|
|
|
Result := Count;
|
|
end;
|
|
|
|
function TJclBZIP2DecompressStream.Seek(Offset: Longint; Origin: Word): Longint;
|
|
begin
|
|
if (Offset = 0) and (Origin = soFromCurrent) then
|
|
Result := BZLibRecord.total_out_lo32
|
|
else
|
|
Result := inherited Seek(Offset, Origin);
|
|
end;
|
|
*)
|
|
|
|
// History:
|
|
// $Log: JclCompression.pas,v $
|
|
// Revision 1.9 2005/10/25 04:46:31 rrossmair
|
|
// - fix for issue #0003276 (as provided by reporter)
|
|
//
|
|
// Revision 1.8 2005/03/08 08:33:15 marquardt
|
|
// overhaul of exceptions and resourcestrings, minor style cleaning
|
|
//
|
|
// Revision 1.7 2005/02/27 14:55:25 marquardt
|
|
// changed overloaded constructors to constructor with default parameter (BCB friendly)
|
|
//
|
|
// Revision 1.6 2005/02/24 16:34:39 marquardt
|
|
// remove divider lines, add section lines (unfinished)
|
|
//
|
|
// Revision 1.5 2005/02/24 07:36:24 marquardt
|
|
// resolved the compiler warnings, style cleanup, removed code from JclContainerIntf.pas
|
|
//
|
|
// Revision 1.4 2004/11/17 03:24:43 mthoma
|
|
// Just noticed that I checked in the wrong version... this one is bugfixed and contains
|
|
// $date and $log
|
|
//
|
|
|
|
end.
|
|
|