Archive::Lha 0.02

LHa for UNIXをベースにごっそり機能をそぎ落としてPerlライブラリ化したArchive::Lha 0.02をCPANにアップしました。現状では解凍専用。しかも正式に対応しているのはlh0、5、7ヘッダのみという状態ですが、解凍ルーチンとは直接関係ないファイルの扱いその他はPure Perlで簡単にいじれるようにしてあるので、ご自分の環境にあわせて都合のよいように修正してください。
基本的な使い方はこんな感じ。

use strict;
use warnings;
use Carp;
use Archive::Lha::Stream;
use Archive::Lha::Header;
use Archive::Lha::Decode;

my $stream = Archive::Lha::Stream->new(file => 'archive.lzh');
while(defined(my $level = $stream->search_header)) {
  my $header = Archive::Lha::Header->new(level => $level, stream => $stream);
  $stream->seek($header->data_top);
  open my $fh, '>:raw', $header->pathname;
  binmode $fh;
  my $decoder = Archive::Lha::Decode->new(
    header => $header,
    read   => sub { $stream->read(@_) },
    write  => sub { print $fh @_ }, 
  );
  my $crc16 = $decoder->decode;
  croak "crc mismatch" if $crc16 != $header->crc16;
}

見ての通り、読み込むアーカイブのストリームを用意して、ヘッダを検索。返ってきたヘッダの種類をもとにヘッダを解析して出力ファイル名を取得。それを環境にあわせて適宜修正してから出力先を用意して(一時ファイル出したり、メモリに余裕があるなら変数にためておいてもOK)、解凍開始。解凍が済んだらCRCチェックをかけてファイルの整合性を確認、という流れ。

一般的なLHaシリーズは高速化のために解凍用のテーブルなどは極力グローバルで持つようになっていますが、Archive::Lhaは速度を犠牲にして毎回動的生成していますので、POEなどで非同期にごりごり解凍してもたぶん大丈夫。Pure Perlな部分も多いので何メガ、何十メガもある大きなアーカイブを解凍するのにはあまり向きませんし、現状ギガ以上のファイルに対応するための拡張ヘッダには対応していませんが、解凍自体にはそれほどメモリは使いませんので、かなり大きなアーカイブでも時間をかければそのうち解凍できます(実際、Pure Perlのプロトタイプ版で数百ファイルを固めてある35メガほどのアーカイブを解凍してみましたが、6時間ほどかかったものの無事解凍できました。現状のXS版では数分のレベル。ロジックを変えればもう少し速くなるのはわかっていますが、自分の用途としては現状で十分なのでどうしたものかなあ、と)。

注意点としては、一般的なバイナリの入出力の際にも言えることですが、ファイルを開くときには:rawをつけたりbinmodeしたりするのをお忘れなく、ということと(改行文字の扱いなどが変わるとCRCの計算結果も変わってきます)、特に異なるOSでつくられたファイルを解凍するときにはファイル名の変換をお忘れなく、ということ(MS-DOS時代のファイルはたいてい全部大文字で格納されていますし、UNIX系のシステムで使うならたいていshiftjis/cp932からeuc-jpないしutf-8への変換が必要になるはず)。

毎回上のようなロジックを書くのは面倒だという向きにはtokuhiromさんがplhaというコマンドラインツールを寄せてくださったのでそちらをご利用ください。

なお、うちではまだunlha32.dll系のツールでつくったlh0、5、7のアーカイブと、15年ほど前にX68000でつくったlh5のアーカイブしかテストしていません。丹念に探せばもう少し古い層のアーカイブもあるはずですが、MacLHAや古いLHarcなどのツールで固めたものは未検証ですので、その手のを解凍する必要がある方は差し障りのないサンプルをお寄せいただければありがたいです。