|
- unit uutlCompression;
-
- { Package: Utils
- Prefix: utl - UTiLs
- Beschreibung: diese Unit enthält Stream-Wrapper die Daten de/komprimieren.
- Teile basierend auf gzio.pas}
-
- {$mode objfpc}{$H+}
- {$TYPEDADDRESS ON}
- {$WRITEABLECONST OFF}
-
- {$DEFINE HAVE_ZLIB}
- {$DEFINE HAVE_LIBLZMA}
-
- {$IFNDEF WINDOWS}
- {$WARNING LibLZMA only supported on Windows for now}
- {$UNDEF HAVE_LIBLZMA}
- {$ENDIF}
-
- interface
-
- uses
- Classes, SysUtils
- {$IFDEF HAVE_ZLIB}
- , zbase, zinflate, zdeflate
- {$ENDIF};
-
- const
- Z_BUFSIZE = 16384;
-
- type
- TutlCustomCompressedStream = class(TOwnerStream)
- private
- fWriting: boolean;
- fStartPos: Int64;
- fKnownSize: Int64;
- protected
- function GetSize: Int64; override;
- function GetTotalOut: QWord; virtual; abstract;
- function GetTotalIn: QWord; virtual; abstract;
- public
- constructor Create(ASource: TStream);
- property Writing: boolean read fWriting;
- function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; override; overload;
- function Rewind: integer; virtual; abstract;
- end;
-
- {$IFDEF HAVE_ZLIB}
- const
- { gzip magic header }
- gz_magic : array[0..1] of byte = ($1F, $8B);
-
- { gzip flag byte }
-
- GZ_ASCII_FLAG = $01; { bit 0 set: file probably ascii text }
- GZ_HEAD_CRC = $02; { bit 1 set: header CRC present }
- GZ_EXTRA_FIELD = $04; { bit 2 set: extra field present }
- GZ_ORIG_NAME = $08; { bit 3 set: original file name present }
- GZ_COMMENT = $10; { bit 4 set: file comment present }
- GZ_RESERVED = $E0; { bits 5..7: reserved }
-
- { gzip os code byte }
-
- GZ_OS_FAT = $00;
- GZ_OS_AMIGA = $01;
- GZ_OS_VMS = $02;
- GZ_OS_UNIX = $03;
- GZ_OS_VMCMS = $04;
- GZ_OS_ATARI_TOS = $05;
- GZ_OS_HPFS = $06;
- GZ_OS_MACINTOSH = $07;
- GZ_OS_ZSYSTEM = $08;
- GZ_OS_CPM = $09;
- GZ_OS_TOPS20 = $0A;
- GZ_OS_NTFS = $0B;
- GZ_OS_QDOS = $0C;
- GZ_OS_ACORNRISC = $0D;
- GZ_OS_UNKNOWN = $FF;
-
- type
- TutlCustomZLibStream = class(TutlCustomCompressedStream)
- private
- fStream: z_stream;
- fMode: Char;
- fLevel: Integer;
- fStrategy: Integer;
- fBuffer: array[0..Z_BUFSIZE-1] of Byte;
- fBufStart: Pointer;
- protected
- function zlibInitRead: LongInt; virtual; abstract;
- function zlibInitWrite: LongInt; virtual; abstract;
- function zlibFinishRead: LongInt; virtual;
- function zlibFinishWrite: LongInt; virtual;
- function DoFlush(flags: integer): integer;
- function GetTotalIn: QWord; override;
- function GetTotalOut: QWord; override;
- public
- constructor Create(aBaseStream: TStream; aMode: string);
- destructor Destroy; override;
- property Mode: Char read fMode;
- property Level: Integer read fLevel;
- property Strategy: Integer read fStrategy;
-
- function Read(var Buffer; Count: Longint): Longint; override;
- function Write(const Buffer; Count: Longint): Longint; override;
- procedure Flush(flags: integer);
- function Rewind: integer; override;
- end;
-
- TutlZLibStream = class(TutlCustomZLibStream)
- protected
- function zlibInitRead: LongInt; override;
- function zlibInitWrite: LongInt; override;
- end;
-
- TutlZipStream = class(TutlCustomZLibStream)
- protected
- function zlibInitRead: LongInt; override;
- function zlibInitWrite: LongInt; override;
- end;
-
- TGZHeader = packed record
- magic: array[0..1] of byte;
- method: Byte;
- flags: Byte;
- time: DWord;
- xflags: byte;
- oscode: byte;
- end;
- TGZFooter = packed record
- crc32: DWord;
- size: DWord;
- end;
-
- TutlGZipStream = class(TutlZipStream)
- private
- fOrigFileName: string;
- fComment: string;
- fOrigTimestamp: TDateTime;
- fCRC: DWord;
- protected
- function zlibInitRead: LongInt; override;
- function zlibInitWrite: LongInt; override;
- function zlibFinishWrite: LongInt; override;
- procedure WriteHeader;
- procedure CheckHeader;
- public
- constructor Create(aBaseStream: TStream; aMode: string);
- property OrigFileame: string read fOrigFileName;
- property OrigTimestamp: TDateTime read fOrigTimestamp;
- property Comment: string read fComment;
- function Write(const Buffer; Count: Longint): Longint; override;
-
- procedure ReadFooter;
- end;
- {$ENDIF HAVE_ZLIB}
-
- {$IFDEF HAVE_LIBLZMA}
- const
- LZMA_DLL = 'liblzma.dll';
- LZMA_VER = 50020012;
- { Return values used by several functions in liblzma }
- LZMA_OK = 0;
- LZMA_STREAM_END = 1;
- LZMA_NO_CHECK = 2;
- LZMA_UNSUPPORTED_CHECK = 3;
- LZMA_GET_CHECK = 4;
- LZMA_MEM_ERROR = 5;
- LZMA_MEMLIMIT_ERROR = 6;
- LZMA_FORMAT_ERROR = 7;
- LZMA_OPTIONS_ERROR = 8;
- LZMA_DATA_ERROR = 9;
- LZMA_BUF_ERROR = 10;
- LZMA_PROG_ERROR = 11;
- { Type of the integrity check (Check ID) }
- LZMA_CHECK_NONE = 0;
- LZMA_CHECK_CRC32 = 1;
- LZMA_CHECK_CRC64 = 4;
- LZMA_CHECK_SHA256 = 10;
- { Type of the integrity check (Check ID) }
- LZMA_INDEX_ITER_ANY = 0;
- LZMA_INDEX_ITER_STREAM = 1;
- LZMA_INDEX_ITER_BLOCK = 2;
- LZMA_INDEX_ITER_NONEMPTY_BLOCK = 3;
- { The 'action' argument for lzma_code() }
- LZMA_RUN = 0;
- LZMA_SYNC_FLUSH = 1;
- LZMA_FULL_FLUSH = 2;
- LZMA_FULL_BARRIER = 4;
- LZMA_FINISH = 3;
- { Encoding }
- LZMA_PRESET_EXTREME = $80000000;
- { Decoding }
- LZMA_TELL_NO_CHECK = $01;
- LZMA_TELL_UNSUPPORTED_CHECK = $02;
- LZMA_TELL_ANY_CHECK = $04;
- LZMA_IGNORE_CHECK = $10;
- LZMA_CONCATENATED = $08;
- { Misc}
- LZMA_STREAM_HEADER_SIZE = 12;
- LZMA_BLOCK_HEADER_SIZE_MIN = 8;
- LZMA_BLOCK_HEADER_SIZE_MAX = 1024;
- LZMA_CHECK_SIZE_MAX = 64;
- LZMA_FILTERS_MAX = 4;
- LZMA_VLI_UNKNOWN: QWord = high(QWord);
-
-
- type
- {$PackRecords C}
- lzma_ret = integer;
- lzma_action = integer;
- lzma_check = integer;
- lzma_reserved_enum = (_LZMA_RESERVED_ENUM);
- lzma_index_iter_mode = integer;
- lzma_vli = QWord;
- lzma_bool = ByteBool;
- {*
- * Custom functions for memory handling.
- *}
- TAlloc = function(opaque: Pointer; Items, Size: size_t): Pointer; cdecl;
- TFree = procedure(opaque, Block: Pointer); cdecl;
- lzma_allocator = record
- XZalloc : TAlloc;
- XZfree : TFree;
- opaque : pointer;
- end;
- p_lzma_allocator = ^lzma_allocator;
- {*
- * Passing data to and from liblzma.
- *}
- lzma_stream = record
- next_in : PByte; //Pointer to the next input byte.
- avail_in : size_t; //Number of available input bytes in next_in.
- total_in : QWord; //Total number of bytes read by liblzma.
- next_out : PByte; //Pointer to the next output position.
- avail_out : size_t; //Amount of free space in next_out.
- total_out : QWord; //Total number of bytes written by liblzma.
- //Custom memory allocation functions
- //In most cases this is nil which makes liblzma use
- //the standard malloc() and free().
- allocator : p_lzma_allocator; //pointer;
- //Internal state is not visible to applications.
- internal : pointer;
- //Reserved space to allow possible future extensions without
- //breaking the ABI. Excluding the initialization of this structure,
- //you should not touch these, because the names of these variables
- //may change.
- reserved_ptr1 : pointer;
- reserved_ptr2 : pointer;
- reserved_ptr3 : pointer;
- reserved_ptr4 : pointer;
- reserved_int1 : QWord;
- reserved_int2 : QWord;
- reserved_int3 : size_t;
- reserved_int4 : size_t;
- reserved_enum1 : lzma_reserved_enum;
- reserved_enum2 : lzma_reserved_enum;
- end;
- p_lzma_stream = ^lzma_stream;
- lzma_stream_flags = record
- version: DWord;
- backward_size: lzma_vli;
- check: lzma_check;
- reserved_enum : array[1..4] of lzma_reserved_enum;
- reserved_bool : array[1..8] of lzma_bool;
- reserved_int : array[1..2] of DWord;
- end;
- p_lzma_stream_flags = ^lzma_stream_flags;
- p_lzma_index = Pointer;
- pp_lzma_index = ^p_lzma_index;
- lzma_index_iter = record
- stream: record
- flags: p_lzma_stream_flags;
- reserved_ptr1,
- reserved_ptr2,
- reserved_ptr3: Pointer;
- number,
- block_count,
- compressed_offset,
- uncompressed_offset,
- compressed_size,
- uncompressed_size,
- padding,
- reserved_vli1,
- reserved_vli2,
- reserved_vli3,
- reserved_vli4: lzma_vli;
- end;
- block: record
- number_in_file,
- compressed_file_offset,
- uncompressed_file_offset,
- number_in_stream,
- compressed_stream_offset,
- uncompressed_stream_offset,
- uncompressed_size,
- unpadded_size,
- total_size,
-
- reserved_vli1,
- reserved_vli2,
- reserved_vli3,
- reserved_vli4: lzma_vli;
-
- reserved_ptr1,
- reserved_ptr2,
- reserved_ptr3,
- reserved_ptr4: Pointer;
- end;
- internal: array[1..6] of record case byte of
- 0: (p: Pointer);
- 1: (s: size_t);
- 2: (v: lzma_vli);
- end;
- end;
- lzma_filter = record
- id: lzma_vli;
- options: Pointer;
- end;
- p_lzma_filter = ^lzma_filter;
- lzma_block = record
- version: dword;
- header_size: DWord;
- check: lzma_check;
- compressed_size: lzma_vli;
- uncompressed_size: lzma_vli;
- filters: p_lzma_filter;
- raw_check: array [0..LZMA_CHECK_SIZE_MAX-1] of byte;
- reserved_ptr1,
- reserved_ptr2,
- reserved_ptr3: Pointer;
- reserved_int1,
- reserved_int2: DWord;
- reserved_int3,
- reserved_int4,
- reserved_int5,
- reserved_int6,
- reserved_int7,
- reserved_int8: lzma_vli;
- reserved_enum1,
- reserved_enum2,
- reserved_enum3,
- reserved_enum4: lzma_reserved_enum;
- ignore_check: lzma_bool;
-
- reserved_bool2,
- reserved_bool3,
- reserved_bool4,
- reserved_bool5,
- reserved_bool6,
- reserved_bool7,
- reserved_bool8: lzma_bool;
- end;
- {$PackRecords Default}
-
- TlibLZMA = object
- Handle: THandle;
- PascalAllocator: lzma_allocator;
-
- lzma_version_number: function(): DWord; cdecl;
- { Initialize .xz Stream encoder using a preset number. }
- lzma_easy_encoder: function(var strm: lzma_stream; preset: DWord; check: lzma_check): lzma_ret; cdecl;
- { Initialize .xz Stream decoder }
- lzma_stream_decoder: function(var strm: lzma_stream; memlimit: QWord; flags: DWord): lzma_ret; cdecl;
- lzma_auto_decoder: function(var strm: lzma_stream; memlimit: QWord; flags: DWord): lzma_ret; cdecl;
- {*
- * Encode or decode data.
- *
- * Once the lzma_stream has been successfully initialized (e.g. with
- * lzma_stream_encoder()), the actual encoding or decoding is done
- * using this function. The application has to update strm->next_in,
- * strm->avail_in, strm->next_out, and strm->avail_out to pass input
- * to and get output from liblzma.
- *}
- lzma_code: function(var strm: lzma_stream; action: lzma_action): lzma_ret; cdecl;
- { Free memory allocated for the coder data structures }
- lzma_end: procedure(var strm: lzma_stream); cdecl;
- { All-in-one functions }
- 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;
- 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;
- { Stream decoding }
- lzma_stream_footer_decode: function (out options: lzma_stream_flags; fin: PByte): lzma_ret; cdecl;
- lzma_stream_header_decode: function (out options: lzma_stream_flags; fin: PByte): lzma_ret; cdecl;
- lzma_stream_flags_compare: function (var a, b: lzma_stream_flags): lzma_ret; cdecl;
- { Index access }
- lzma_index_decoder: function(var strm: lzma_stream; i: pp_lzma_index; memlimit: QWord): lzma_ret; cdecl;
- 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;
- lzma_index_uncompressed_size: function(i: p_lzma_index): lzma_vli; cdecl;
- lzma_index_end: procedure(i: p_lzma_index; allocator: p_lzma_allocator); cdecl;
- lzma_index_cat: function(dest, src: p_lzma_index; const allocator: p_lzma_allocator): lzma_ret; cdecl;
- lzma_index_stream_flags: function(i: p_lzma_index; var flags: lzma_stream_flags): lzma_ret; cdecl;
- lzma_index_stream_padding: function(i: p_lzma_index; padding: lzma_vli): lzma_ret; cdecl;
- lzma_index_total_size: function(i: p_lzma_index): lzma_vli; cdecl;
- { Index iterator }
- lzma_index_iter_init: procedure (var iter:lzma_index_iter; i: p_lzma_index); cdecl;
- lzma_index_iter_locate: function (var iter:lzma_index_iter; target: lzma_vli): lzma_bool; cdecl;
- lzma_index_iter_next: function (var iter:lzma_index_iter; mode: lzma_index_iter_mode): lzma_bool; cdecl;
- { Block access }
- lzma_block_decoder: function(var strm: lzma_stream; var i: lzma_block): lzma_ret; cdecl;
- lzma_block_header_decode: function(var i: lzma_block; const allocator: p_lzma_allocator; fin: PByte): lzma_ret; cdecl;
- lzma_block_compressed_size: function(var i: lzma_block; unpadded_size: lzma_vli): lzma_ret; cdecl;
- function lzma_block_header_size_decode(b: dword): dword;
-
- procedure Load;
- procedure Unload;
- end;
-
- TutlXZStream = class(TutlCustomCompressedStream)
- private
- fStream: lzma_stream;
- fMode: Char;
- fLevel: DWORD;
- fBuffer: array[0..Z_BUFSIZE-1] of Byte;
- fBufStart: Pointer;
- private
- fIndex: p_lzma_index;
- fStreamNr,
- fBlockNr: DWord;
- fMaxUncompBlockSize: QWord;
- fUncompPos: QWord;
- fBlock: lzma_block;
- fBlockOffs: Int64;
- fFilters: array[0..LZMA_FILTERS_MAX] of lzma_filter; // MUST be 1 entry longer than LZMA_FILTERS_MAX for LZMA_VLI_UNKNOWN terminator
- protected
- function DoFlush(flags: integer): integer;
- function GetTotalIn: QWord; override;
- function GetTotalOut: QWord; override;
- function GetPosition: Int64; override;
- procedure BlockEnd;
- function ParseIndexes: boolean;
- procedure IterateIndexes;
- public
- constructor Create(aBaseStream: TStream; aMode: string);
- destructor Destroy; override;
- property Mode: Char read fMode;
- property Level: DWORD read fLevel;
-
- function Read(var Buffer; Count: Longint): Longint; override;
- function Write(const Buffer; Count: Longint): Longint; override;
- procedure Flush({%H-}flags: integer);
- function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; override; overload;
- function Rewind: integer; override;
- public
- class function CheckLibraryAvailable: boolean;
- end;
- {$ENDIF HAVE_LIBLZMA}
-
- resourcestring
- sInvalidMode = 'Invalid mode specified';
- sFailedInitZLib = 'Failed to init ZLIB';
- sGZipNoHeader = 'No GZip header found';
- sGZIPUnsupportedMethod = 'Unsupported Method';
- sLibLZMANotFound = 'liblzma not found or bad version: %s';
- sLZMAErrorOpen = 'Error opening XZ file';
-
- implementation
-
- uses
- dateutils, crc, dynlibs;
-
- {$IFDEF HAVE_LIBLZMA}
- var
- libLZMA: TlibLZMA;
- {$ENDIF HAVE_LIBLZMA}
-
- { TutlCustomCompressedStream }
-
- constructor TutlCustomCompressedStream.Create(ASource: TStream);
- begin
- inherited Create(ASource);
- fWriting:= false;
- fStartPos:= ASource.Position;
- fKnownSize:= -1;
- end;
-
- function TutlCustomCompressedStream.Seek(const Offset: Int64; Origin: TSeekOrigin): Int64;
- var
- ofs: Int64;
- sz : int64;
- tmp: array [0..Z_BUFSIZE-1] of byte;
- begin
- if Origin = soEnd then begin
- if fWriting then begin
- if Offset <= 0 then
- // No going back
- Exit(-1)
- else
- // Current = End
- Exit(Seek(Offset, soCurrent));
- end;
-
- // computes size, if possible
- ofs:= GetSize - Offset;
- // if we know the size now, translate to seek from beginning
- if fKnownSize >= 0 then
- Exit(Seek(ofs, soBeginning));
- Exit(-1);
- end;
-
- ofs:= Offset;
-
- if fWriting then begin
- if Origin = soBeginning then
- dec(ofs, GetTotalOut);
- if ofs < 0 then
- Exit(-1);
-
- { At this point, ofs is the number of zero bytes to write. }
- FillChar({%H-}tmp[0], Z_BUFSIZE, 0);
- while (ofs > 0) do begin
- sz := Z_BUFSIZE;
- if (ofs < Z_BUFSIZE) then
- sz := ofs;
-
- sz:= Write(tmp[0], sz);
- if (size <= 0) then
- Exit(-1);
-
- dec(ofs,sz);
- end;
-
- Result:= GetTotalIn;
- end else begin
- { compute absolute position }
- if Origin = soCurrent then
- inc(ofs, GetTotalOut);
-
- if ofs < 0 then
- Exit(-1);
-
- { For a negative seek, rewind and use positive seek }
- if (ofs >= GetTotalOut) then
- dec(ofs, GetTotalOut)
- else if (Rewind <> 0) then begin
- Exit(-1);
- end;
-
- { ofs is now the number of bytes to skip. }
- while (ofs > 0) do begin
- sz := Z_BUFSIZE;
- if (ofs < Z_BUFSIZE) then
- sz := ofs;
-
- sz := Read(tmp[0], sz);
- if (sz <= 0) then
- Exit(-1);
- dec(ofs, sz);
- end;
-
- Result:= GetTotalOut;
- end;
- end;
-
- function TutlCustomCompressedStream.GetSize: Int64;
- var
- tmp: array[0..Z_BUFSIZE-1] of byte;
- sz: Int64;
- begin
- if fKnownSize < 0 then begin
- if fKnownSize < -1 then
- // we tried before and it was not possible
- Exit(-1);
-
- // read until EOF
- repeat
- sz := Read({%H-}tmp[0], Z_BUFSIZE);
- if sz < 0 then begin
- fKnownSize:= -2;
- Exit(-1);
- end;
- until sz < Z_BUFSIZE;
- fKnownSize:= GetTotalOut;
- end;
- Result:= fKnownSize;
- end;
-
- {$IFDEF HAVE_ZLIB}
-
- { TutlCustomZLibStream }
-
- constructor TutlCustomZLibStream.Create(aBaseStream: TStream; aMode: string);
- var
- err: Integer;
- m: Char;
- begin
- inherited Create(aBaseStream);
- fMode:= #0;
- fLevel:= Z_DEFAULT_COMPRESSION;
- fStrategy:= Z_DEFAULT_STRATEGY;
- for m in aMode do begin
- case m of
- 'r',
- 'w': fMode:= m;
- '0'..'9': fLevel:= Ord(m) - Ord('0');
- 'f': fStrategy:= Z_FILTERED;
- 'h': fStrategy:= Z_HUFFMAN_ONLY;
- end;
- end;
- if not (fMode in ['r', 'w']) then
- raise EFOpenError.Create(sInvalidMode);
-
- fWriting:= fMode = 'w';
- fBufStart:= @fBuffer[0];
- fStream:= Default(z_stream);
- if fWriting then begin
- err:= zlibInitWrite;
- fStream.next_out:= fBufStart;
- end else begin
- err:= zlibInitRead;
- fStream.next_in:= fBufStart;
- end;
- if err <> Z_OK then
- raise EFOpenError.Create(sFailedInitZLib);
-
- fStream.avail_out:= Z_BUFSIZE;
- fStartPos:= Source.Position;
- end;
-
- destructor TutlCustomZLibStream.Destroy;
- begin
- if fWriting then begin
- DoFlush(Z_FINISH);
- zlibFinishWrite;
- end else begin
- zlibFinishRead;
- end;
- inherited Destroy;
- end;
-
- function TutlCustomZLibStream.Read(var Buffer; Count: Longint): Longint;
- var
- res: Int64;
- begin
- Result:= 0;
- fStream.next_out := @Buffer;
- fStream.avail_out := Count;
- while (fStream.avail_out > 0) do begin
- if fStream.avail_in = 0 then begin
- res:= Source.Read(fBufStart^, Z_BUFSIZE);
- if res = 0 then begin
- // reached input end, return
- Result:= Count - fStream.avail_out;
- Exit;
- end;
- fStream.avail_in := res;
- fStream.next_in := fBufStart;
- end;
- res:= inflate(fStream, Z_SYNC_FLUSH);
- if res = Z_STREAM_END then
- Exit(Count - fStream.avail_out)
- else if res < 0 then
- Exit(res);
- end;
- Result := Count;
- end;
-
- function TutlCustomZLibStream.Write(const Buffer; Count: Longint): Longint;
- var
- res: int64;
- begin
- fStream.next_in := @Buffer;
- fStream.avail_in := Count;
- while (fStream.avail_in > 0) do begin
- res:= deflate(fStream, 0);
- if res < 0 then
- Exit(res);
- if fStream.avail_out = 0 then begin
- Source.WriteBuffer(fBufStart^, Z_BUFSIZE);
- fStream.next_out:= fBufStart;
- fStream.avail_out:= Z_BUFSIZE;
- end;
- end;
- Result := Count;
- end;
-
- procedure TutlCustomZLibStream.Flush(flags: integer);
- begin
- DoFlush(flags);
- { TODO : Check Result}
- end;
-
- function TutlCustomZLibStream.DoFlush(flags: integer): integer;
- var
- len : cardinal;
- done : boolean;
- written,
- err: int64;
- begin
- done := false;
-
- if not fWriting then
- Exit(Z_STREAM_ERROR);
-
- fStream.avail_in := 0; { should be zero already anyway }
-
- while true do begin
- len := Z_BUFSIZE - fStream.avail_out;
-
- if (len <> 0) then begin
- written:= Source.Write(fBufStart^, len);
- if (written <> len) then begin
- Exit(Z_ERRNO);
- end;
- fStream.next_out := fBufStart;
- fStream.avail_out := Z_BUFSIZE;
- end;
-
- if done then
- break;
- err := deflate(fStream, flags);
-
- { Ignore the second of two consecutive flushes: }
- if (len = 0) and (err = Z_BUF_ERROR) then
- err := Z_OK;
-
- { deflate has finished flushing only when it hasn't used up
- all the available space in the output buffer: }
-
- done := (fStream.avail_out <> 0) or (err = Z_STREAM_END);
- if (err <> Z_OK) and (err <> Z_STREAM_END) then
- break;
- end; {WHILE}
-
- if (err = Z_STREAM_END) then
- Result:= Z_OK
- else
- Result:= err;
- end;
-
- function TutlCustomZLibStream.Rewind: integer;
- begin
- if fWriting then
- Exit(-1);
-
- fStream.avail_in := 0;
- fStream.next_in := fBufStart;
-
- inflateReset(fStream);
- Source.Seek(fStartPos, soBeginning);
- Result:= 0;
- end;
-
- function TutlCustomZLibStream.zlibFinishRead: LongInt;
- begin
- Result:= inflateEnd(fStream);
- end;
-
- function TutlCustomZLibStream.zlibFinishWrite: LongInt;
- begin
- Result:= deflateEnd(fStream);
- end;
-
- function TutlCustomZLibStream.GetTotalIn: QWord;
- begin
- Result:= fStream.total_in;
- end;
-
- function TutlCustomZLibStream.GetTotalOut: QWord;
- begin
- Result:= fStream.total_out;
- end;
-
-
- { TutlZLibStream }
-
- function TutlZLibStream.zlibInitRead: LongInt;
- begin
- Result:= inflateInit_(@fStream, ZLIB_VERSION, sizeof(fStream));
- end;
-
- function TutlZLibStream.zlibInitWrite: LongInt;
- begin
- Result:= deflateInit_(@fStream, level, ZLIB_VERSION, sizeof(fStream));
- end;
-
- { TutlZipStream }
-
- function TutlZipStream.zlibInitRead: LongInt;
- begin
- {
- Well-documented undocumented feature: passing negative window bits tells zlib not to
- excpect a zlib header and instead read raw deflate blocks.
- We use 15 bits here instead of 13 because that's what everybody else does, and it doesn't really
- matter for decompression anyway.
- }
- Result:= inflateInit2_(fStream, -MAX_WBITS, ZLIB_VERSION, sizeof(fStream));
- end;
-
- function TutlZipStream.zlibInitWrite: LongInt;
- begin
- Result:= deflateInit2_(fStream, level, Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, ZLIB_VERSION, sizeof(fStream));
- end;
-
- { TutlGZipStream }
-
- function TutlGZipStream.zlibInitRead: LongInt;
- begin
- Result:= inherited;
- end;
-
- function TutlGZipStream.zlibInitWrite: LongInt;
- begin
- Result:= inherited;
- end;
-
- constructor TutlGZipStream.Create(aBaseStream: TStream; aMode: string);
- begin
- inherited Create(aBaseStream, aMode);
- fOrigFileName:= '';
- fOrigTimestamp:= 0;
- fComment:= '';
- if fWriting then begin
- fCRC:= crc32(0, nil, 0);
- WriteHeader;
- end
- else begin
- CheckHeader;
- end;
- fStartPos:= Source.Position;
- end;
-
- procedure TutlGZipStream.WriteHeader;
- var
- h: TGZHeader;
- begin
- { TODO : use actual data }
- h.magic:= gz_magic;
- h.method:= Z_DEFLATED;
- h.flags:= 0;
- h.time:= 0;
- h.xflags:= 0;
- h.oscode:= 0;
- Source.Write(h, sizeof(h));
- end;
-
- procedure TutlGZipStream.CheckHeader;
- var
- h: TGZHeader;
- w: Word;
-
- function ReadNullTerm: AnsiString;
- var
- b: byte;
- begin
- Result:= '';
- b:= 0;
- while Source.Read(b, sizeof(b)) = sizeof(b) do begin
- if b = 0 then break;
- Result:= Result + Chr(b);
- end;
- end;
-
- begin
- Source.ReadBuffer(h{%H-}, sizeof(h));
- if not CompareMem(@h.magic, @gz_magic, sizeof(gz_magic)) then
- raise EFOpenError.Create(sGZipNoHeader);
- if (h.method <> Z_DEFLATED) or ((h.flags and GZ_RESERVED) <> 0) then
- raise EReadError.Create(sGZIPUnsupportedMethod);
-
- fOrigTimestamp:= UnixToDateTime(LEtoN(h.time));
-
- if (h.flags and GZ_EXTRA_FIELD) > 0 then begin
- w:= 0;
- Source.ReadBuffer(w, sizeof(w));
- w:= LEtoN(w);
- // Skip Extra Field
- Source.Seek(w, soFromCurrent);
- end;
- if (h.flags and GZ_ORIG_NAME) > 0 then begin
- fOrigFileName:= ReadNullTerm;
- end;
- if (h.flags and GZ_COMMENT) > 0 then begin
- fComment:= ReadNullTerm;
- end;
- if (h.flags and GZ_HEAD_CRC) > 0 then begin
- // Skip Header Checksum
- Source.Seek(2, soFromCurrent);
- end;
- end;
-
- function TutlGZipStream.zlibFinishWrite: LongInt;
- var
- f: TGZFooter;
- begin
- Result:=inherited zlibFinishWrite;
- f.crc32:= fCRC;
- f.size:= fStream.total_in;
- Source.WriteBuffer(f, sizeof(f));
- end;
-
- function TutlGZipStream.Write(const Buffer; Count: Longint): Longint;
- begin
- Result:=inherited Write(Buffer, Count);
- if Result > 0 then
- fCRC:= crc32(fCRC, @Buffer, Result);
- end;
-
- procedure TutlGZipStream.ReadFooter;
- var
- bp: Int64;
- f: TGZFooter;
- begin
- bp:= Source.Position;
- try
- Source.Seek(-Sizeof(f), soEnd);
- Source.ReadBuffer(f{%H-}, sizeof(f));
- fKnownSize:= f.size;
- finally
- Source.Position:= bp;
- end;
- end;
-
- {$ENDIF HAVE_ZLIB}
-
- {$IFDEF HAVE_LIBLZMA}
-
- { TlibLZMA }
-
- function TlibLZMA.lzma_block_header_size_decode(b: dword): dword;
- begin
- Result:= (b + 1) * 4;
- end;
-
- function PasAlloc({%H-}opaque: Pointer; Items, Size: size_t): Pointer; cdecl;
- begin
- Result:= AllocMem(Size * Items);
- end;
-
- procedure PasFree({%H-}opaque, Block: Pointer); cdecl;
- begin
- Freemem(Block);
- end;
-
- procedure TlibLZMA.Load;
- begin
- if Handle = 0 then
- Handle:= SafeLoadLibrary(LZMA_DLL);
- PascalAllocator.XZalloc:= @PasAlloc;
- PascalAllocator.XZfree:= @PasFree;
- if Handle <> 0 then begin
- Pointer(lzma_version_number):= GetProcedureAddress(Handle, 'lzma_version_number');
- Pointer(lzma_easy_encoder):= GetProcedureAddress(Handle, 'lzma_easy_encoder');
- Pointer(lzma_stream_decoder):= GetProcedureAddress(Handle, 'lzma_stream_decoder');
- Pointer(lzma_auto_decoder):= GetProcedureAddress(Handle, 'lzma_auto_decoder');
- Pointer(lzma_code):= GetProcedureAddress(Handle, 'lzma_code');
- Pointer(lzma_end):= GetProcedureAddress(Handle, 'lzma_end');
- Pointer(lzma_easy_buffer_encode):= GetProcedureAddress(Handle, 'lzma_easy_buffer_encode');
- Pointer(lzma_stream_buffer_decode):= GetProcedureAddress(Handle, 'lzma_stream_buffer_decode');
- Pointer(lzma_stream_footer_decode):= GetProcedureAddress(Handle, 'lzma_stream_footer_decode');
- Pointer(lzma_stream_header_decode):= GetProcedureAddress(Handle, 'lzma_stream_header_decode');
- Pointer(lzma_stream_flags_compare):= GetProcedureAddress(Handle, 'lzma_stream_flags_compare');
- Pointer(lzma_index_decoder):= GetProcedureAddress(Handle, 'lzma_index_decoder');
- Pointer(lzma_index_buffer_decode):= GetProcedureAddress(Handle, 'lzma_index_buffer_decode');
- Pointer(lzma_index_uncompressed_size):= GetProcedureAddress(Handle, 'lzma_index_uncompressed_size');
- Pointer(lzma_index_end):= GetProcedureAddress(Handle, 'lzma_index_end');
- Pointer(lzma_index_cat):= GetProcedureAddress(Handle, 'lzma_index_cat');
- Pointer(lzma_index_stream_flags):= GetProcedureAddress(Handle, 'lzma_index_stream_flags');
- Pointer(lzma_index_stream_padding):= GetProcedureAddress(Handle, 'lzma_index_stream_padding');
- Pointer(lzma_index_total_size):= GetProcedureAddress(Handle, 'lzma_index_total_size');
- Pointer(lzma_index_iter_init):= GetProcedureAddress(Handle, 'lzma_index_iter_init');
- Pointer(lzma_index_iter_locate):= GetProcedureAddress(Handle, 'lzma_index_iter_locate');
- Pointer(lzma_index_iter_next):= GetProcedureAddress(Handle, 'lzma_index_iter_next');
- Pointer(lzma_block_decoder):= GetProcedureAddress(Handle, 'lzma_block_decoder');
- Pointer(lzma_block_header_decode):= GetProcedureAddress(Handle, 'lzma_block_header_decode');
- Pointer(lzma_block_compressed_size):= GetProcedureAddress(Handle, 'lzma_block_compressed_size');
- end;
- end;
-
- procedure TlibLZMA.Unload;
- begin
- UnloadLibrary(Handle);
- FillByte(Self, sizeof(Self), 0);
- end;
-
- { TutlXZStream }
-
- constructor TutlXZStream.Create(aBaseStream: TStream; aMode: string);
- var
- m: Char;
- err: integer;
- begin
- inherited Create(aBaseStream);
- if not CheckLibraryAvailable then
- raise Exception.CreateFmt(sLibLZMANotFound, [LZMA_DLL]);
- fMode:= #0;
- fLevel:= 0;
- for m in aMode do begin
- case m of
- 'r',
- 'w': fMode:= m;
- '0'..'9': fLevel:= Ord(m) - Ord('0');
- 'e': fLevel:= fLevel or LZMA_PRESET_EXTREME;
- end;
- end;
- if not (fMode in ['r', 'w']) then
- raise EFOpenError.Create(sInvalidMode);
-
- fWriting:= fMode = 'w';
- fBufStart:= @fBuffer[0];
- fStream:= Default(lzma_stream);
- fBlockOffs:= -1;
- if fWriting then begin
- err:= libLZMA.lzma_easy_encoder(fStream, fLevel, LZMA_CHECK_CRC64);
- fStream.next_out:= fBufStart;
- end else begin
- if not ParseIndexes then
- raise EFOpenError.Create(sLZMAErrorOpen);
- IterateIndexes;
- fKnownSize:= libLZMA.lzma_index_uncompressed_size(fIndex);
- Seek(0, soBeginning);
- fStream.next_in:= fBufStart;
- err:= 0;
- end;
- if err <> Z_OK then
- raise EFOpenError.Create(sFailedInitZLib);
-
- fStream.avail_out:= Z_BUFSIZE;
- fStartPos:= Source.Position;
- end;
-
- destructor TutlXZStream.Destroy;
- begin
- if fWriting then begin;
- DoFlush(LZMA_FINISH);
- end;
- libLZMA.lzma_index_end(fIndex, nil);
- libLZMA.lzma_end(fStream);
- inherited Destroy;
- end;
-
- function TutlXZStream.DoFlush(flags: integer): integer;
- begin
- fStream.next_in := nil;
- fStream.avail_in := 0;
- while (libLZMA.lzma_code(fStream,flags) <> LZMA_STREAM_END) and
- (fStream.avail_out = 0) do begin
- Source.WriteBuffer(fBufStart^, Z_BUFSIZE);
- fStream.next_out := fBufStart;
- fStream.avail_out := Z_BUFSIZE;
- end;
- if fStream.avail_out < Z_BUFSIZE then
- Source.WriteBuffer(fBufStart^, Z_BUFSIZE - fStream.avail_out);
- Result:= LZMA_OK;
- end;
-
- procedure TutlXZStream.Flush(flags: integer);
- begin
- DoFlush(LZMA_SYNC_FLUSH);
- end;
-
- function TutlXZStream.Read(var Buffer; Count: Longint): Longint;
- var
- res, sz: Int64;
- a, b, current_read: QWord;
- r: lzma_ret;
- begin
- Result:= 0;
- // is a block loaded?
- if fBlockOffs < 0 then begin
- // if no block is loaded, we might be at EOF
- Exit(0);
- end;
- fStream.next_out := @Buffer;
- fStream.avail_out := Count;
- while (fStream.avail_out > 0) do begin
- if fStream.avail_in = 0 then begin
- // read remaining data from current block
- a:= fBlock.compressed_size;
- b:= fStream.total_in;
- sz:= a - b;
- if sz > Z_BUFSIZE then
- sz:= Z_BUFSIZE;
- // if this block has no more data, read next
- if sz = 0 then begin
- // finish coding current block
- libLZMA.lzma_code(fStream, LZMA_FINISH);
- current_read:= Count - fStream.avail_out;
- Inc(Result, current_read);
- inc(fUncompPos, current_read);
- // begin new block
- res:= Seek(0, soCurrent);
- if res < 0 then
- Exit;
- // setup writing to next part
- fStream.next_out:= @Tbytearray(Buffer)[Result];
- dec(Count, current_read);
- fStream.avail_out:= Count;
- // re-read from this new block
- continue;
- end else begin
- res:= Source.Read(fBufStart^, sz);
- if res = 0 then begin
- // block should have more data, but we didn't get any
- libLZMA.lzma_code(fStream, LZMA_FINISH);
- current_read:= Count - fStream.avail_out;
- Inc(Result, current_read);
- inc(fUncompPos, current_read);
- Exit;
- end;
- fStream.avail_in := res;
- fStream.next_in := fBufStart;
- end;
- end;
- r:= libLZMA.lzma_code(fStream, LZMA_RUN);
- if r <> LZMA_OK then
- break;
- end;
- current_read:= Count - fStream.avail_out;
- Inc(Result, current_read);
- inc(fUncompPos, current_read);
- end;
-
- function TutlXZStream.Write(const Buffer; Count: Longint): Longint;
- var
- res: Integer;
- begin
- fStream.next_in := @Buffer;
- fStream.avail_in := Count;
- while (fStream.avail_in > 0) do begin
- res:= libLZMA.lzma_code(fStream, LZMA_RUN);
- if res < 0 then
- Exit(res);
- if fStream.avail_out = 0 then begin
- Source.WriteBuffer(fBufStart^, Z_BUFSIZE);
- fStream.next_out:= fBufStart;
- fStream.avail_out:= Z_BUFSIZE;
- end;
- end;
- Result := Count;
- end;
-
- function TutlXZStream.Rewind: integer;
- begin
- if fWriting then
- Exit(-1);
- Seek(0, soBeginning);
- Result:= 0;
- end;
-
- function TutlXZStream.GetTotalIn: QWord;
- begin
- Result:= fStream.total_in;
- end;
-
- function TutlXZStream.GetTotalOut: QWord;
- begin
- Result:= fStream.total_out;
- end;
-
- function TutlXZStream.ParseIndexes: boolean;
- const
- MAX_INDEX_LENGTH = 16384;
- MAX_QWORD = high(QWord);
- BUFSIZE = 16384;
- var
- bp, fs: Int64;
- stream_padding: lzma_vli;
- combined_index: p_lzma_index;
-
- function ReadOneStream: boolean;
- var
- footer,
- header: array[0..LZMA_STREAM_HEADER_SIZE-1] of byte;
- buf: array[0..BUFSIZE-1] of byte;
- footer_flags,
- header_flags: lzma_stream_flags;
- inddec: lzma_stream;
- this_index: p_lzma_index;
- r: lzma_ret;
- index_size: lzma_vli;
- begin
- Result:= false;
- if Source.Position < LZMA_STREAM_HEADER_SIZE then
- // Corrupted File
- Exit;
- Source.Seek(-LZMA_STREAM_HEADER_SIZE, soCurrent);
- Source.ReadBuffer(footer{%H-}, sizeof(footer));
-
- // Skip stream padding.
- if (footer[8] = 0) and (footer[9] = 0) and (footer[10] = 0) and (footer[11] = 0) then begin
- inc(stream_padding, 4);
- Source.Seek(-4, soCurrent);
- Exit(true);
- end;
-
- inc(fStreamNr);
- Source.Seek(-LZMA_STREAM_HEADER_SIZE, soCurrent);
-
- // decode footer
- r:= libLZMA.lzma_stream_footer_decode(footer_flags, @footer[0]);
- if r <> LZMA_OK then
- Exit;
- index_size:= footer_flags.backward_size;
- // Safeguard, the index should be right before the footer
- if (index_size > MAX_INDEX_LENGTH) or (Source.Position < index_size + LZMA_STREAM_HEADER_SIZE) then
- Exit;
- // Compute start of index for this stream
- Source.Seek(-index_size, soCurrent);
-
- // Decode Index
- this_index:= nil;
- inddec:= Default(lzma_stream);
- r:= libLZMA.lzma_index_decoder(inddec, @this_index, MAX_QWORD);
- try
- if r <> LZMA_OK then
- Exit;
- repeat
- inddec.avail_in:= index_size;
- if inddec.avail_in > BUFSIZE then
- inddec.avail_in:= BUFSIZE;
- Source.ReadBuffer({%H-}buf[0], inddec.avail_in);
- index_size -= inddec.avail_in;
- inddec.next_in:= @buf[0];
- r:= libLZMA.lzma_code(inddec, LZMA_RUN);
- until (r <> LZMA_OK) or (index_size=0);
-
- if r <> LZMA_STREAM_END then
- Exit;
-
- index_size:= libLZMA.lzma_index_total_size(this_index);
- Source.Seek(- (inddec.total_in + index_size + LZMA_STREAM_HEADER_SIZE), soCurrent);
- // Read and decode Stream Header
- Source.ReadBuffer(header{%H-}[0], LZMA_STREAM_HEADER_SIZE);
- Source.Seek(-LZMA_STREAM_HEADER_SIZE, soCurrent);
- r:= libLZMA.lzma_stream_header_decode(header_flags, @header[0]);
- if r <> LZMA_OK then
- Exit;
- // should be equal
- r:= libLZMA.lzma_stream_flags_compare(header_flags, footer_flags);
- if r <> LZMA_OK then
- Exit;
- // store decoded flags in this index
- r:= libLZMA.lzma_index_stream_flags(this_index, footer_flags);
- if r <> LZMA_OK then
- Exit;
- // store padding accumulated so far (needed for seeking in multi-stream-files)
- r:= libLZMA.lzma_index_stream_padding(this_index, stream_padding);
- if r <> LZMA_OK then
- Exit;
-
- if Assigned(combined_index) then begin
- r:= libLZMA.lzma_index_cat(this_index, combined_index, nil);
- if r <> LZMA_OK then
- Exit;
- end;
-
- combined_index:= this_index;
- this_index:= nil;
- Result:= true;
- finally
- libLZMA.lzma_end(inddec);
- libLZMA.lzma_index_end(this_index, nil);
- end;
- end;
-
- begin
- Result:= false;
- bp:= Source.Position;
- try
- fs:= Source.Seek(0, soEnd);
- // file size must be 4 byte aligned
- if (fs <= 0) or (fs and 3 > 0) then
- Exit;
-
- stream_padding:= 0;
- fStreamNr:= 0;
- combined_index:= nil;
- try
- // Jump backwards through the file identifying each stream.
- while Source.Position > 0 do begin
- if not ReadOneStream then
- Exit;
- end;
- fIndex:= combined_index;
- combined_index:= nil;
- Result:= true;
- except
- libLZMA.lzma_index_end(combined_index, nil);
- end;
- finally
- if not Result then
- Source.Position:= bp;
- end;
- end;
-
- procedure TutlXZStream.IterateIndexes;
- var
- iter: lzma_index_iter;
- begin
- fBlockNr:= 0;
- fMaxUncompBlockSize:= 0;
-
- iter:= Default(lzma_index_iter);
- libLZMA.lzma_index_iter_init(iter, fIndex);
- while not libLZMA.lzma_index_iter_next(iter, LZMA_INDEX_ITER_NONEMPTY_BLOCK) do begin
- if iter.block.uncompressed_size > fMaxUncompBlockSize then
- fMaxUncompBlockSize:= iter.block.uncompressed_size;
- inc(fBlockNr);
- end;
- end;
-
- function TutlXZStream.Seek(const Offset: Int64; Origin: TSeekOrigin): Int64;
- var
- iter: lzma_index_iter;
- header: array[0..LZMA_BLOCK_HEADER_SIZE_MAX-1] of byte;
- tmp: array[0..Z_BUFSIZE] of byte;
- r: lzma_ret;
- ofs, sz: Int64;
- begin
- if fWriting then
- Exit(inherited Seek(Offset, Origin));
-
- // only support absolute position
- case Origin of
- soEnd: Exit(Seek(fKnownSize + Offset, soBeginning));
- soCurrent: Exit(Seek(Position + Offset, soBeginning));
- end;
-
- Result:= -1;
-
- if Offset = fKnownSize then begin
- // seek to last - pretend it worked, but reading will fail
- fUncompPos:= fKnownSize;
- BlockEnd;
- Exit;
- end;
-
- // find what block the target is in
- iter:= Default(lzma_index_iter);
- libLZMA.lzma_index_iter_init(iter, fIndex);
- if libLZMA.lzma_index_iter_locate(iter, Offset) then begin
- // not found, don't change anything
- Exit;
- end;
-
- if (iter.block.compressed_file_offset <> fBlockOffs) or
- (Offset < fUncompPos) then begin
-
- // free current Block
- BlockEnd;
-
- // seek to block begin
- fBlockOffs:= iter.block.compressed_file_offset;
- if Source.Seek(iter.block.compressed_file_offset, soBeginning)<0 then
- Exit;
- //Read the block header. Start by reading a single byte which tell us how big the block header is.
- Source.ReadBuffer({%H-}header[0], 1);
- if header[0] = 0 then
- Exit;
-
- fBlock.version:= 0;
- fBlock.check:= iter.stream.flags^.check;
- fBlock.filters:= @fFilters[0];
- fBlock.header_size:= libLZMA.lzma_block_header_size_decode(header[0]);
-
- //Now read and decode the block header.
- Source.ReadBuffer(header[1], fBlock.header_size-1);
-
- r:= libLZMA.lzma_block_header_decode(fBlock, @libLZMA.PascalAllocator, @header[0]);
- if r <> LZMA_OK then
- Exit;
-
- // What this actually does is it checks that the block header matches the index.
- r:= libLZMA.lzma_block_compressed_size(fBlock, iter.block.unpadded_size);
- if r <> LZMA_OK then
- Exit;
-
- // copy over info we need later
- fBlock.uncompressed_size:= iter.block.uncompressed_size;
-
- // Read the block data.
- fStream:= Default(lzma_stream);
- r:= libLZMA.lzma_block_decoder(fStream, fBlock);
- if r <> LZMA_OK then
- Exit;
-
- fUncompPos:= iter.block.uncompressed_file_offset;
- end;
-
- // fast-forward to actual pos
- ofs:= Offset - fUncompPos;
- while (ofs > 0) do begin
- sz := Z_BUFSIZE;
- if (ofs < Z_BUFSIZE) then
- sz := ofs;
-
- sz := Read({%H-}tmp[0], sz);
- if (sz <= 0) then
- Exit;
- dec(ofs, sz);
- end;
-
- Result:= fUncompPos;
- end;
-
- function TutlXZStream.GetPosition: Int64;
- begin
- if fWriting then
- Exit(inherited GetPosition);
- Result:= fUncompPos;
- end;
-
- procedure TutlXZStream.BlockEnd;
- var
- i: Integer;
- begin
- libLZMA.lzma_end(fStream);
- fStream:= Default(lzma_stream);
- fBlock:= Default(lzma_block);
- fBlockOffs:= -1;
-
- for i:= 0 to LZMA_FILTERS_MAX-1 do begin
- if fFilters[i].id <> LZMA_VLI_UNKNOWN then
- libLZMA.PascalAllocator.XZfree(nil, fFilters[i].options);
- fFilters[i].id:= LZMA_VLI_UNKNOWN;
- end;
- end;
-
- class function TutlXZStream.CheckLibraryAvailable: boolean;
- begin
- libLZMA.Load;
- Result:= Assigned(libLZMA.lzma_version_number) and (libLZMA.lzma_version_number()>=LZMA_VER);
- end;
-
- {$ENDIF HAVE_LIBLZMA}
-
- finalization
- {$IFDEF HAVE_LIBLZMA}
- libLZMA.Unload;
- {$ENDIF HAVE_LIBLZMA}
- end.
|