TZipFile に BZip2 サポートを追加する

前の記事で書きましたがTZipFileは圧縮方法として0(store)と8(deflate)のみサポートしていますが、後から追加することが可能になっています。そこで、BZip2のサポートを追加してみました。
内容としては、System.ZLibを元にBZip2のメソッドを呼ぶように修正していっただけ。どちらかと言うと、スタティックリンクするためのObjファイルのリンクに苦労しました。スタティックリンクは、Win32/Win64で軽く確認した所だと圧縮・解凍共に動作しています。

Objファイルとソースファイルはここにおいておきます。
Win32/Win64フォルダの下のlibbz2フォルダにobjファイルを置いてあるのでWin32/Win64フォルダをライブラリパスに追加してください。
Win32はC++BuilderXE2でWin64はVC2010でコンパイルしてあります。bzip2のソースはこちら

ユニットを追加するだけでBZip2サポートが追加されますのでユニットを追加した後、圧縮方法にzcBZIP2を指定するだけです。

ユニットは以下。

unit au2010.Compression.BZip2;

{
   ------------------------------------------------------------------
   This file is part of bzip2/libbzip2, a program and library for
   lossless, block-sorting data compression.

   bzip2/libbzip2 version 1.0.6 of 6 September 2010
   Copyright (C) 1996-2010 Julian Seward <jseward@bzip.org>

   Please read the WARNING, DISCLAIMER and PATENTS sections in the
   README file.

   This program is released under the terms of the license contained
   in the file LICENSE.
   ------------------------------------------------------------------

   Header conversion of bzlib.h
}

interface

uses
  System.Types, System.SysUtils, System.Classes, System.Zip;

{ DLL 使用時は'.'を削除 }
{.$DEFINE USE_DLL}

{$IFDEF USE_DLL}
const
  libbz2 = 'libbz2.dll';
{$ENDIF}

{$REGION 'bzlib.h translation'}
const
  BZ_RUN = 0;
  BZ_FLUSH = 1;
  BZ_FINISH = 2;

  BZ_OK = 0;
  BZ_RUN_OK = 1;
  BZ_FLUSH_OK = 2;
  BZ_FINISH_OK = 3;
  BZ_STREAM_END = 4;
  BZ_SEQUENCE_ERROR = (-1);
  BZ_PARAM_ERROR = (-2);
  BZ_MEM_ERROR = (-3);
  BZ_DATA_ERROR = (-4);
  BZ_DATA_ERROR_MAGIC = (-5);
  BZ_IO_ERROR = (-6);
  BZ_UNEXPECTED_EOF = (-7);
  BZ_OUTBUFF_FULL = (-8);
  BZ_CONFIG_ERROR = (-9);

type
  bzalloc_func = function(opaque: Pointer; Items, Size: Integer): Pointer; cdecl;
  bzfree_func = procedure(opaque, address: Pointer); cdecl;

  bz_stream = record
    next_in: PByte;
    avail_in: Cardinal;
    total_in_lo32: Cardinal;
    total_in_hi32: Cardinal;

    next_out: PByte;
    avail_out: Cardinal;
    total_out_lo32: Cardinal;
    total_out_hi32: Cardinal;

    state: Pointer;

    bzalloc: bzalloc_func;
    bzfree: bzfree_func;
    opaque: Pointer;
  end;


  (*-- Core (low-level) library functions --*)
  function BZ2_bzCompressInit(var strm: bz_stream; blockSize100k, verbosity,
    workFactor: Integer): Integer; stdcall; external {$IFDEF USE_DLL}libbz2{$ENDIF};
  function BZ2_bzCompress(var strm: bz_stream; action: Integer): Integer;
    stdcall; external {$IFDEF USE_DLL}libbz2{$ENDIF};
  function BZ2_bzCompressEnd(var strm: bz_stream): Integer; stdcall;
    external {$IFDEF USE_DLL}libbz2{$ENDIF};
  function BZ2_bzDecompressInit(var strm: bz_stream; verbosity, small: Integer): Integer;
    stdcall; external {$IFDEF USE_DLL}libbz2{$ENDIF};
  function BZ2_bzDecompress(var strm: bz_stream): Integer; stdcall; external {$IFDEF USE_DLL}libbz2{$ENDIF};
  function BZ2_bzDecompressEnd(var strm: bz_stream): Integer; stdcall; external {$IFDEF USE_DLL}libbz2{$ENDIF};

{$ENDREGION 'bzip2.h translation'}

type
  TBZAlloc = bzalloc_func;
  TBZFree = bzfree_func;

  TBZStreamRec = bz_stream;

  {** TCustomBZStream ********************************************************}

  TCustomBZStream = class(TStream)
  private
    FStream: TStream;
    FStreamStartPos: Int64;
    FStreamPos: Int64;
    FOnProgress: TNotifyEvent;
    FZStream: TBZStreamRec;
    FBuffer: array[Word] of Byte;
  protected
    constructor Create(stream: TStream);
    procedure DoProgress; dynamic;
    property OnProgress: TNotifyEvent read FOnProgress write FOnProgress;
  end;

  {** TZCompressionStream ***************************************************}

  TBZCompressionStream = class(TCustomBZStream)
  private
    function GetCompressionRate: Single;
  public
    constructor Create(dest: TStream); 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;
    function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; override;
    property CompressionRate: Single read GetCompressionRate;
    property OnProgress;
  end;

  {** TZDecompressionStream *************************************************}

  TBZDecompressionStream = class(TCustomBZStream)
  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;
    function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; override;
    property OnProgress;
  end;

  function bzAllocMem(AppData: Pointer; Items, Size: Integer): Pointer; cdecl;
  procedure bzFreeMem(AppData, Block: Pointer); cdecl;

type
  EBZError = class(Exception);
  EBZCompressionError = class(EBZError);
  EBZDecompressionError = class(EBZError);

const
  {** return code messages **************************************************}

  _bz_errmsg: array [0..14] of PAnsiChar = (
    'stream end',           // BZ_STREAM_END        (4)  //do not localize
    '',                     // BZ_FINISH_OK         (3)  //do not localize
    '',                     // BZ_FLUSH_OK          (2)  //do not localize
    '',                     // BZ_RUN_OK            (1)  //do not localize
    '',                     // BZ_OK                (0)  //do not localize
    'sequence error',       // BZ_SEQUENCE_ERROR    (-1) //do not localize
    'parameter error',      // BZ_PARAM_ERROR       (-2) //do not localize
    'insufficient memory',  // BZ_MEM_ERROR         (-3) //do not localize
    'data error',           // BZ_DATA_ERROR        (-4) //do not localize
    'magic bytes error',    // BZ_DATA_ERROR_MAGIC  (-5) //do not localize
    'io error',             // BZ_IO_ERROR          (-6) //do not localize
    'unexpected EOF',       // BZ_UNEXPECTED_EOF    (-7) //do not localize
    'out buffer full',      // BZ_OUTBUFF_FULL      (-8) //do not localize
    'config error',         // BZ_CONFIG_ERROR      (-9) //do not localize
    ''                                               //do not localize
    );
  SBZInvalid = 'Invalid BZStream operation!';

implementation

{$IFDEF MSWINDOWS}
{$IFNDEF USE_DLL}
  uses
    System.Win.crtl;
{$L libbz2\bzlib.obj}
{$L libbz2\blocksort.obj}
{$L libbz2\huffman.obj}
{$L libbz2\compress.obj}
{$L libbz2\decompress.obj}

{$IF defined(WIN32)}
const
  _BZ2_crc32Table: Array[0..255] of UInt32 = (
   $00000000, $04c11db7, $09823b6e, $0d4326d9,
   $130476dc, $17c56b6b, $1a864db2, $1e475005,
   $2608edb8, $22c9f00f, $2f8ad6d6, $2b4bcb61,
   $350c9b64, $31cd86d3, $3c8ea00a, $384fbdbd,
   $4c11db70, $48d0c6c7, $4593e01e, $4152fda9,
   $5f15adac, $5bd4b01b, $569796c2, $52568b75,
   $6a1936c8, $6ed82b7f, $639b0da6, $675a1011,
   $791d4014, $7ddc5da3, $709f7b7a, $745e66cd,
   $9823b6e0, $9ce2ab57, $91a18d8e, $95609039,
   $8b27c03c, $8fe6dd8b, $82a5fb52, $8664e6e5,
   $be2b5b58, $baea46ef, $b7a96036, $b3687d81,
   $ad2f2d84, $a9ee3033, $a4ad16ea, $a06c0b5d,
   $d4326d90, $d0f37027, $ddb056fe, $d9714b49,
   $c7361b4c, $c3f706fb, $ceb42022, $ca753d95,
   $f23a8028, $f6fb9d9f, $fbb8bb46, $ff79a6f1,
   $e13ef6f4, $e5ffeb43, $e8bccd9a, $ec7dd02d,
   $34867077, $30476dc0, $3d044b19, $39c556ae,
   $278206ab, $23431b1c, $2e003dc5, $2ac12072,
   $128e9dcf, $164f8078, $1b0ca6a1, $1fcdbb16,
   $018aeb13, $054bf6a4, $0808d07d, $0cc9cdca,
   $7897ab07, $7c56b6b0, $71159069, $75d48dde,
   $6b93dddb, $6f52c06c, $6211e6b5, $66d0fb02,
   $5e9f46bf, $5a5e5b08, $571d7dd1, $53dc6066,
   $4d9b3063, $495a2dd4, $44190b0d, $40d816ba,
   $aca5c697, $a864db20, $a527fdf9, $a1e6e04e,
   $bfa1b04b, $bb60adfc, $b6238b25, $b2e29692,
   $8aad2b2f, $8e6c3698, $832f1041, $87ee0df6,
   $99a95df3, $9d684044, $902b669d, $94ea7b2a,
   $e0b41de7, $e4750050, $e9362689, $edf73b3e,
   $f3b06b3b, $f771768c, $fa325055, $fef34de2,
   $c6bcf05f, $c27dede8, $cf3ecb31, $cbffd686,
   $d5b88683, $d1799b34, $dc3abded, $d8fba05a,
   $690ce0ee, $6dcdfd59, $608edb80, $644fc637,
   $7a089632, $7ec98b85, $738aad5c, $774bb0eb,
   $4f040d56, $4bc510e1, $46863638, $42472b8f,
   $5c007b8a, $58c1663d, $558240e4, $51435d53,
   $251d3b9e, $21dc2629, $2c9f00f0, $285e1d47,
   $36194d42, $32d850f5, $3f9b762c, $3b5a6b9b,
   $0315d626, $07d4cb91, $0a97ed48, $0e56f0ff,
   $1011a0fa, $14d0bd4d, $19939b94, $1d528623,
   $f12f560e, $f5ee4bb9, $f8ad6d60, $fc6c70d7,
   $e22b20d2, $e6ea3d65, $eba91bbc, $ef68060b,
   $d727bbb6, $d3e6a601, $dea580d8, $da649d6f,
   $c423cd6a, $c0e2d0dd, $cda1f604, $c960ebb3,
   $bd3e8d7e, $b9ff90c9, $b4bcb610, $b07daba7,
   $ae3afba2, $aafbe615, $a7b8c0cc, $a379dd7b,
   $9b3660c6, $9ff77d71, $92b45ba8, $9675461f,
   $8832161a, $8cf30bad, $81b02d74, $857130c3,
   $5d8a9099, $594b8d2e, $5408abf7, $50c9b640,
   $4e8ee645, $4a4ffbf2, $470cdd2b, $43cdc09c,
   $7b827d21, $7f436096, $7200464f, $76c15bf8,
   $68860bfd, $6c47164a, $61043093, $65c52d24,
   $119b4be9, $155a565e, $18197087, $1cd86d30,
   $029f3d35, $065e2082, $0b1d065b, $0fdc1bec,
   $3793a651, $3352bbe6, $3e119d3f, $3ad08088,
   $2497d08d, $2056cd3a, $2d15ebe3, $29d4f654,
   $c5a92679, $c1683bce, $cc2b1d17, $c8ea00a0,
   $d6ad50a5, $d26c4d12, $df2f6bcb, $dbee767c,
   $e3a1cbc1, $e760d676, $ea23f0af, $eee2ed18,
   $f0a5bd1d, $f464a0aa, $f9278673, $fde69bc4,
   $89b8fd09, $8d79e0be, $803ac667, $84fbdbd0,
   $9abc8bd5, $9e7d9662, $933eb0bb, $97ffad0c,
   $afb010b1, $ab710d06, $a6322bdf, $a2f33668,
   $bcb4666d, $b8757bda, $b5365d03, $b1f740b4
  );

  _BZ2_rNums: Array[0..511] of Int32 = (
   619, 720, 127, 481, 931, 816, 813, 233, 566, 247,
   985, 724, 205, 454, 863, 491, 741, 242, 949, 214,
   733, 859, 335, 708, 621, 574, 73, 654, 730, 472,
   419, 436, 278, 496, 867, 210, 399, 680, 480, 51,
   878, 465, 811, 169, 869, 675, 611, 697, 867, 561,
   862, 687, 507, 283, 482, 129, 807, 591, 733, 623,
   150, 238, 59, 379, 684, 877, 625, 169, 643, 105,
   170, 607, 520, 932, 727, 476, 693, 425, 174, 647,
   73, 122, 335, 530, 442, 853, 695, 249, 445, 515,
   909, 545, 703, 919, 874, 474, 882, 500, 594, 612,
   641, 801, 220, 162, 819, 984, 589, 513, 495, 799,
   161, 604, 958, 533, 221, 400, 386, 867, 600, 782,
   382, 596, 414, 171, 516, 375, 682, 485, 911, 276,
   98, 553, 163, 354, 666, 933, 424, 341, 533, 870,
   227, 730, 475, 186, 263, 647, 537, 686, 600, 224,
   469, 68, 770, 919, 190, 373, 294, 822, 808, 206,
   184, 943, 795, 384, 383, 461, 404, 758, 839, 887,
   715, 67, 618, 276, 204, 918, 873, 777, 604, 560,
   951, 160, 578, 722, 79, 804, 96, 409, 713, 940,
   652, 934, 970, 447, 318, 353, 859, 672, 112, 785,
   645, 863, 803, 350, 139, 93, 354, 99, 820, 908,
   609, 772, 154, 274, 580, 184, 79, 626, 630, 742,
   653, 282, 762, 623, 680, 81, 927, 626, 789, 125,
   411, 521, 938, 300, 821, 78, 343, 175, 128, 250,
   170, 774, 972, 275, 999, 639, 495, 78, 352, 126,
   857, 956, 358, 619, 580, 124, 737, 594, 701, 612,
   669, 112, 134, 694, 363, 992, 809, 743, 168, 974,
   944, 375, 748, 52, 600, 747, 642, 182, 862, 81,
   344, 805, 988, 739, 511, 655, 814, 334, 249, 515,
   897, 955, 664, 981, 649, 113, 974, 459, 893, 228,
   433, 837, 553, 268, 926, 240, 102, 654, 459, 51,
   686, 754, 806, 760, 493, 403, 415, 394, 687, 700,
   946, 670, 656, 610, 738, 392, 760, 799, 887, 653,
   978, 321, 576, 617, 626, 502, 894, 679, 243, 440,
   680, 879, 194, 572, 640, 724, 926, 56, 204, 700,
   707, 151, 457, 449, 797, 195, 791, 558, 945, 679,
   297, 59, 87, 824, 713, 663, 412, 693, 342, 606,
   134, 108, 571, 364, 631, 212, 174, 643, 304, 329,
   343, 97, 430, 751, 497, 314, 983, 374, 822, 928,
   140, 206, 73, 263, 980, 736, 876, 478, 430, 305,
   170, 514, 364, 692, 829, 82, 855, 953, 676, 246,
   369, 970, 294, 750, 807, 827, 150, 790, 288, 923,
   804, 378, 215, 828, 592, 281, 565, 555, 710, 82,
   896, 831, 547, 261, 524, 462, 293, 465, 502, 56,
   661, 821, 976, 991, 658, 869, 905, 758, 745, 193,
   768, 550, 608, 933, 378, 286, 215, 979, 792, 961,
   61, 688, 793, 644, 986, 403, 106, 366, 905, 644,
   372, 567, 466, 434, 645, 210, 389, 550, 919, 135,
   780, 773, 635, 389, 707, 100, 626, 958, 165, 504,
   920, 176, 193, 713, 857, 265, 203, 50, 668, 108,
   645, 990, 626, 197, 510, 357, 358, 850, 858, 364,
   936, 638
  );

  function _BZ2_indexIntoF: integer; cdecl; external;
  procedure _BZ2_hbMakeCodeLengths; external;
  procedure _BZ2_hbAssignCodes; external;
  procedure _BZ2_hbCreateDecodeTables; external;
  procedure _BZ2_blockSort; external;
  procedure _bz_internal_error(errcode: Integer); cdecl;
  begin

  end;
{$ELSEIF defined(WIN64)}
const
  BZ2_crc32Table: Array[0..255] of UInt32 = (
   $00000000, $04c11db7, $09823b6e, $0d4326d9,
   $130476dc, $17c56b6b, $1a864db2, $1e475005,
   $2608edb8, $22c9f00f, $2f8ad6d6, $2b4bcb61,
   $350c9b64, $31cd86d3, $3c8ea00a, $384fbdbd,
   $4c11db70, $48d0c6c7, $4593e01e, $4152fda9,
   $5f15adac, $5bd4b01b, $569796c2, $52568b75,
   $6a1936c8, $6ed82b7f, $639b0da6, $675a1011,
   $791d4014, $7ddc5da3, $709f7b7a, $745e66cd,
   $9823b6e0, $9ce2ab57, $91a18d8e, $95609039,
   $8b27c03c, $8fe6dd8b, $82a5fb52, $8664e6e5,
   $be2b5b58, $baea46ef, $b7a96036, $b3687d81,
   $ad2f2d84, $a9ee3033, $a4ad16ea, $a06c0b5d,
   $d4326d90, $d0f37027, $ddb056fe, $d9714b49,
   $c7361b4c, $c3f706fb, $ceb42022, $ca753d95,
   $f23a8028, $f6fb9d9f, $fbb8bb46, $ff79a6f1,
   $e13ef6f4, $e5ffeb43, $e8bccd9a, $ec7dd02d,
   $34867077, $30476dc0, $3d044b19, $39c556ae,
   $278206ab, $23431b1c, $2e003dc5, $2ac12072,
   $128e9dcf, $164f8078, $1b0ca6a1, $1fcdbb16,
   $018aeb13, $054bf6a4, $0808d07d, $0cc9cdca,
   $7897ab07, $7c56b6b0, $71159069, $75d48dde,
   $6b93dddb, $6f52c06c, $6211e6b5, $66d0fb02,
   $5e9f46bf, $5a5e5b08, $571d7dd1, $53dc6066,
   $4d9b3063, $495a2dd4, $44190b0d, $40d816ba,
   $aca5c697, $a864db20, $a527fdf9, $a1e6e04e,
   $bfa1b04b, $bb60adfc, $b6238b25, $b2e29692,
   $8aad2b2f, $8e6c3698, $832f1041, $87ee0df6,
   $99a95df3, $9d684044, $902b669d, $94ea7b2a,
   $e0b41de7, $e4750050, $e9362689, $edf73b3e,
   $f3b06b3b, $f771768c, $fa325055, $fef34de2,
   $c6bcf05f, $c27dede8, $cf3ecb31, $cbffd686,
   $d5b88683, $d1799b34, $dc3abded, $d8fba05a,
   $690ce0ee, $6dcdfd59, $608edb80, $644fc637,
   $7a089632, $7ec98b85, $738aad5c, $774bb0eb,
   $4f040d56, $4bc510e1, $46863638, $42472b8f,
   $5c007b8a, $58c1663d, $558240e4, $51435d53,
   $251d3b9e, $21dc2629, $2c9f00f0, $285e1d47,
   $36194d42, $32d850f5, $3f9b762c, $3b5a6b9b,
   $0315d626, $07d4cb91, $0a97ed48, $0e56f0ff,
   $1011a0fa, $14d0bd4d, $19939b94, $1d528623,
   $f12f560e, $f5ee4bb9, $f8ad6d60, $fc6c70d7,
   $e22b20d2, $e6ea3d65, $eba91bbc, $ef68060b,
   $d727bbb6, $d3e6a601, $dea580d8, $da649d6f,
   $c423cd6a, $c0e2d0dd, $cda1f604, $c960ebb3,
   $bd3e8d7e, $b9ff90c9, $b4bcb610, $b07daba7,
   $ae3afba2, $aafbe615, $a7b8c0cc, $a379dd7b,
   $9b3660c6, $9ff77d71, $92b45ba8, $9675461f,
   $8832161a, $8cf30bad, $81b02d74, $857130c3,
   $5d8a9099, $594b8d2e, $5408abf7, $50c9b640,
   $4e8ee645, $4a4ffbf2, $470cdd2b, $43cdc09c,
   $7b827d21, $7f436096, $7200464f, $76c15bf8,
   $68860bfd, $6c47164a, $61043093, $65c52d24,
   $119b4be9, $155a565e, $18197087, $1cd86d30,
   $029f3d35, $065e2082, $0b1d065b, $0fdc1bec,
   $3793a651, $3352bbe6, $3e119d3f, $3ad08088,
   $2497d08d, $2056cd3a, $2d15ebe3, $29d4f654,
   $c5a92679, $c1683bce, $cc2b1d17, $c8ea00a0,
   $d6ad50a5, $d26c4d12, $df2f6bcb, $dbee767c,
   $e3a1cbc1, $e760d676, $ea23f0af, $eee2ed18,
   $f0a5bd1d, $f464a0aa, $f9278673, $fde69bc4,
   $89b8fd09, $8d79e0be, $803ac667, $84fbdbd0,
   $9abc8bd5, $9e7d9662, $933eb0bb, $97ffad0c,
   $afb010b1, $ab710d06, $a6322bdf, $a2f33668,
   $bcb4666d, $b8757bda, $b5365d03, $b1f740b4
  );

  BZ2_rNums: Array[0..511] of Int32 = (
   619, 720, 127, 481, 931, 816, 813, 233, 566, 247,
   985, 724, 205, 454, 863, 491, 741, 242, 949, 214,
   733, 859, 335, 708, 621, 574, 73, 654, 730, 472,
   419, 436, 278, 496, 867, 210, 399, 680, 480, 51,
   878, 465, 811, 169, 869, 675, 611, 697, 867, 561,
   862, 687, 507, 283, 482, 129, 807, 591, 733, 623,
   150, 238, 59, 379, 684, 877, 625, 169, 643, 105,
   170, 607, 520, 932, 727, 476, 693, 425, 174, 647,
   73, 122, 335, 530, 442, 853, 695, 249, 445, 515,
   909, 545, 703, 919, 874, 474, 882, 500, 594, 612,
   641, 801, 220, 162, 819, 984, 589, 513, 495, 799,
   161, 604, 958, 533, 221, 400, 386, 867, 600, 782,
   382, 596, 414, 171, 516, 375, 682, 485, 911, 276,
   98, 553, 163, 354, 666, 933, 424, 341, 533, 870,
   227, 730, 475, 186, 263, 647, 537, 686, 600, 224,
   469, 68, 770, 919, 190, 373, 294, 822, 808, 206,
   184, 943, 795, 384, 383, 461, 404, 758, 839, 887,
   715, 67, 618, 276, 204, 918, 873, 777, 604, 560,
   951, 160, 578, 722, 79, 804, 96, 409, 713, 940,
   652, 934, 970, 447, 318, 353, 859, 672, 112, 785,
   645, 863, 803, 350, 139, 93, 354, 99, 820, 908,
   609, 772, 154, 274, 580, 184, 79, 626, 630, 742,
   653, 282, 762, 623, 680, 81, 927, 626, 789, 125,
   411, 521, 938, 300, 821, 78, 343, 175, 128, 250,
   170, 774, 972, 275, 999, 639, 495, 78, 352, 126,
   857, 956, 358, 619, 580, 124, 737, 594, 701, 612,
   669, 112, 134, 694, 363, 992, 809, 743, 168, 974,
   944, 375, 748, 52, 600, 747, 642, 182, 862, 81,
   344, 805, 988, 739, 511, 655, 814, 334, 249, 515,
   897, 955, 664, 981, 649, 113, 974, 459, 893, 228,
   433, 837, 553, 268, 926, 240, 102, 654, 459, 51,
   686, 754, 806, 760, 493, 403, 415, 394, 687, 700,
   946, 670, 656, 610, 738, 392, 760, 799, 887, 653,
   978, 321, 576, 617, 626, 502, 894, 679, 243, 440,
   680, 879, 194, 572, 640, 724, 926, 56, 204, 700,
   707, 151, 457, 449, 797, 195, 791, 558, 945, 679,
   297, 59, 87, 824, 713, 663, 412, 693, 342, 606,
   134, 108, 571, 364, 631, 212, 174, 643, 304, 329,
   343, 97, 430, 751, 497, 314, 983, 374, 822, 928,
   140, 206, 73, 263, 980, 736, 876, 478, 430, 305,
   170, 514, 364, 692, 829, 82, 855, 953, 676, 246,
   369, 970, 294, 750, 807, 827, 150, 790, 288, 923,
   804, 378, 215, 828, 592, 281, 565, 555, 710, 82,
   896, 831, 547, 261, 524, 462, 293, 465, 502, 56,
   661, 821, 976, 991, 658, 869, 905, 758, 745, 193,
   768, 550, 608, 933, 378, 286, 215, 979, 792, 961,
   61, 688, 793, 644, 986, 403, 106, 366, 905, 644,
   372, 567, 466, 434, 645, 210, 389, 550, 919, 135,
   780, 773, 635, 389, 707, 100, 626, 958, 165, 504,
   920, 176, 193, 713, 857, 265, 203, 50, 668, 108,
   645, 990, 626, 197, 510, 357, 358, 850, 858, 364,
   936, 638
  );

  function BZ2_indexIntoF: integer; cdecl; external;
  procedure BZ2_hbMakeCodeLengths; external;
  procedure BZ2_hbAssignCodes; external;
  procedure BZ2_hbCreateDecodeTables; external;
  procedure BZ2_blockSort; external;
  procedure bz_internal_error(errcode: Integer); cdecl;
  begin

  end;

{$IFEND}

{$ENDIF}
{$ENDIF}

function BZCompressCheck(code: Integer): Integer;
begin
  result := code;

  if code < 0 then
  begin
    raise EBZCompressionError.Create(string(_bz_errmsg[4 - code]));
  end;
end;

function BZDecompressCheck(code: Integer): Integer;
begin
  Result := code;

  if code < 0 then
  begin
    raise EBZDecompressionError.Create(string(_bz_errmsg[4 - code]));
  end;
end;

function bzAllocMem(AppData: Pointer; Items, Size: Integer): Pointer;
begin
  Result := AllocMem(Items * Size);
end;

procedure bzFreeMem(AppData, Block: Pointer);
begin
  FreeMem(Block);
end;

{ TCustomBZStream }

constructor TCustomBZStream.Create(stream: TStream);
begin
  inherited Create;
  FStream := stream;
  FStreamStartPos := Stream.Position;
  FStreamPos := FStreamStartPos;
end;

procedure TCustomBZStream.DoProgress;
begin
  if Assigned(FOnProgress) then FOnProgress(Self);
end;

{ TBZCompressionStream }

constructor TBZCompressionStream.Create(dest: TStream);
begin
  inherited Create(dest);

  FZStream.opaque := Nil;
  FZStream.bzalloc := Nil;
  FZStream.bzfree := Nil;
  FZStream.next_out := @FBuffer;
  FZStream.avail_out := SizeOf(FBuffer);

  BZCompressCheck(BZ2_bzCompressInit(FZStream, 9, 0, 30));
end;

destructor TBZCompressionStream.Destroy;
begin
  FZStream.next_in := nil;
  FZStream.avail_in := 0;

  try
    if FStream.Position <> FStreamPos then FStream.Position := FStreamPos;

    while BZCompressCheck(BZ2_bzCompress(FZStream, BZ_FINISH)) <> BZ_STREAM_END do
    begin
      FStream.WriteBuffer(FBuffer, SizeOf(FBuffer) - FZStream.avail_out);

      FZStream.next_out := @FBuffer;
      FZStream.avail_out := SizeOf(FBuffer);
    end;

    if FZStream.avail_out < SizeOf(FBuffer) then
    begin
      FStream.WriteBuffer(FBuffer, SizeOf(FBuffer) - FZStream.avail_out);
    end;
  finally
    BZ2_bzCompressEnd(FZStream);
  end;

  inherited Destroy;
end;

function TBZCompressionStream.GetCompressionRate: Single;
var
  TotalIn: UInt64;
  TotalOut: UInt64;
begin
  TotalIn := (FZStream.total_in_hi32 Shl 32) + FZStream.total_in_lo32;
  TotalOut := (FZStream.total_out_hi32 Shl 32) + FZStream.total_out_lo32;

  if TotalIn = 0 then result := 0
  else result := (1.0 - (TotalOut / TotalIn)) * 100.0;
end;

function TBZCompressionStream.Read(var buffer; count: Integer): Longint;
begin
  raise EBZCompressionError.Create(SBZInvalid);
end;

function TBZCompressionStream.Seek(const Offset: Int64;
  Origin: TSeekOrigin): Int64;
begin
  if (offset = 0) and (origin = soCurrent) then
  begin
    result := FZStream.total_in_hi32 Shl 32 + FZStream.total_in_lo32;
  end
  else raise EBZCompressionError.Create(SBZInvalid);
end;

function TBZCompressionStream.Seek(offset: Integer; origin: Word): Longint;
begin
  if (offset = 0) and (origin = soFromCurrent) then
  begin
    result := FZStream.total_in_hi32 Shl 32 + FZStream.total_in_lo32;
  end
  else raise EBZCompressionError.Create(SBZInvalid);
end;

function TBZCompressionStream.Write(const buffer; count: Integer): Longint;
begin
  FZStream.next_in := @buffer;
  FZStream.avail_in := count;

  if FStream.Position <> FStreamPos then FStream.Position := FStreamPos;

  while FZStream.avail_in > 0 do
  begin
    BZCompressCheck(BZ2_bzCompress(FZStream, BZ_RUN));

    if FZStream.avail_out = 0 then
    begin
      FStream.WriteBuffer(FBuffer, SizeOf(FBuffer));

      FZStream.next_out := @FBuffer;
      FZStream.avail_out := SizeOf(FBuffer);

      FStreamPos := FStream.Position;

      DoProgress;
    end;
  end;

  result := Count;
end;

{ TBZDecompressionStream }

constructor TBZDecompressionStream.Create(source: TStream);
begin
  Create(source, 15);
end;

constructor TBZDecompressionStream.Create(source: TStream; WindowBits: Integer);
begin
  inherited Create(source);
  FZStream.next_in := @FBuffer;
  FZStream.avail_in := 0;

  FZStream.opaque := Nil;
  FZStream.bzalloc := Nil;
  FZStream.bzfree := Nil;

  BZDecompressCheck(BZ2_bzDecompressInit(FZStream, 0, 0));
end;

destructor TBZDecompressionStream.Destroy;
begin
  BZ2_bzDecompressEnd(FZStream);
  inherited;
end;

function TBZDecompressionStream.Read(var buffer; count: Integer): Longint;
var
  zresult: Integer;
begin
  FZStream.next_out := @buffer;
  FZStream.avail_out := count;

  if FStream.Position <> FStreamPos then FStream.Position := FStreamPos;

  zresult := BZ_OK;

  while (FZStream.avail_out > 0) and (zresult <> BZ_STREAM_END) do
  begin
    if FZStream.avail_in = 0 then
    begin
      FZStream.avail_in := FStream.Read(FBuffer, SizeOf(FBuffer));

      if FZStream.avail_in = 0 then
      begin
        result := NativeUInt(count) - FZStream.avail_out;

        Exit;
      end;

      FZStream.next_in := @FBuffer;
      FStreamPos := FStream.Position;

      DoProgress;
    end;

    zresult := BZDecompressCheck(BZ2_bzDecompress(FZStream));
  end;

  if (zresult = BZ_STREAM_END) and (FZStream.avail_in > 0) then
  begin
    FStream.Position := FStream.Position - FZStream.avail_in;
    FStreamPos := FStream.Position;

    FZStream.avail_in := 0;
  end;

  result := NativeUInt(count) - FZStream.avail_out;
end;

function TBZDecompressionStream.Seek(const Offset: Int64;
  Origin: TSeekOrigin): Int64;
var
  buf: array[0..8191] of Char;
  i: Integer;
  vOffset: Int64;
begin
  vOffset := Offset;
  if (vOffset = 0) and (origin = soBeginning) then
  begin
    BZDecompressCheck(BZ2_bzDecompressEnd(FZStream));

    FZStream.next_in := @FBuffer;
    FZStream.avail_in := 0;
    FStream.Position := FStreamStartPos;
    FStreamPos := FStreamStartPos;

    BZDecompressCheck(BZ2_bzDecompressInit(FZStream, 0, 0));
  end
  else if ((vOffset >= 0) and (origin = soCurrent)) or
    (((NativeUInt(vOffset) - (FZStream.total_out_lo32 Shl 32) + FZStream.total_out_lo32) > 0) and (origin = soBeginning)) then
  begin
    if origin = soBeginning then Dec(vOffset, (FZStream.total_out_lo32 Shl 32) + FZStream.total_out_lo32);

    if vOffset > 0 then
    begin
      for i := 1 to vOffset div SizeOf(buf) do ReadBuffer(buf, SizeOf(buf));
      ReadBuffer(buf, vOffset mod SizeOf(buf));
    end;
  end
  else if (vOffset = 0) and (origin = soEnd) then
  begin
    while Read(buf, SizeOf(buf)) > 0 do ;
  end
  else raise EBZDecompressionError.Create(SBZInvalid);

  result := (FZStream.total_out_lo32 Shl 32) + FZStream.total_out_lo32;
end;

function TBZDecompressionStream.Seek(offset: Integer; origin: Word): Longint;
var
  buf: array[0..8191] of Char;
  i: Integer;
begin
  if (offset = 0) and (origin = soFromBeginning) then
  begin
    BZDecompressCheck(BZ2_bzDecompressEnd(FZStream));

    FZStream.next_in := @FBuffer;
    FZStream.avail_in := 0;
    FStream.Position := FStreamStartPos;
    FStreamPos := FStreamStartPos;

    BZDecompressCheck(BZ2_bzDecompressInit(FZStream, 0, 0));
  end
  else if ((offset >= 0) and (origin = soFromCurrent)) or
    (((NativeUInt(offset) - (FZStream.total_out_lo32 Shl 32) + FZStream.total_out_lo32) > 0) and (origin = soFromBeginning)) then
  begin
    if origin = soFromBeginning then Dec(offset, (FZStream.total_out_lo32 Shl 32) + FZStream.total_out_lo32);

    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 EBZDecompressionError.Create(SBZInvalid);

  result := (FZStream.total_out_lo32 Shl 32) + FZStream.total_out_lo32;
end;

function TBZDecompressionStream.Write(const buffer; count: Integer): Longint;
begin
  raise EBZDecompressionError.Create(SBZInvalid);
end;


initialization

  TZipFile.RegisterCompressionHandler(zcBZIP2,
    function(InStream: TStream; const ZipFile: TZipFile; const Item: TZipHeader): TStream
    begin
      Result := TBZCompressionStream.Create(InStream);
    end,
    function(InStream: TStream; const ZipFile: TZipFile; const Item: TZipHeader): TStream
    begin
      Result := TBZDecompressionStream.Create(InStream);
    end);

end.