.lzh file format of LHA (AKA LHarc) by Yoshizaki Haruyasu: JavaScript parsing library

LHA (LHarc, LZH) is a file format used by a popular freeware eponymous archiver, created in 1988 by Haruyasu Yoshizaki. Over the years, many ports and implementations were developed, sporting many extensions to original 1988 LZH.

File format is pretty simple and essentially consists of a stream of records.

Application

LHA (AKA LHarc) by Yoshizaki Haruyasu

File extension

lzh

KS implementation details

License: CC0-1.0

References

This page hosts a formal specification of .lzh file format of LHA (AKA LHarc) by Yoshizaki Haruyasu using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.

Usage

Runtime library

All parsing code for JavaScript generated by Kaitai Struct depends on the JavaScript runtime library. You have to install it before you can parse data.

The JavaScript runtime library is available at npm:

npm install kaitai-struct

Code

See the usage examples in the JavaScript notes.

Parse structure from an ArrayBuffer:

var arrayBuffer = ...;
var data = new Lzh(new KaitaiStream(arrayBuffer));

After that, one can get various attributes from the structure by accessing fields or properties like:

data.entries // => get entries

JavaScript source code to parse .lzh file format of LHA (AKA LHarc) by Yoshizaki Haruyasu

Lzh.js

// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild

(function (root, factory) {
  if (typeof define === 'function' && define.amd) {
    define(['kaitai-struct/KaitaiStream', './DosDatetime'], factory);
  } else if (typeof module === 'object' && module.exports) {
    module.exports = factory(require('kaitai-struct/KaitaiStream'), require('./DosDatetime'));
  } else {
    root.Lzh = factory(root.KaitaiStream, root.DosDatetime);
  }
}(typeof self !== 'undefined' ? self : this, function (KaitaiStream, DosDatetime) {
/**
 * LHA (LHarc, LZH) is a file format used by a popular freeware
 * eponymous archiver, created in 1988 by Haruyasu Yoshizaki. Over the
 * years, many ports and implementations were developed, sporting many
 * extensions to original 1988 LZH.
 * 
 * File format is pretty simple and essentially consists of a stream of
 * records.
 */

var Lzh = (function() {
  function Lzh(_io, _parent, _root) {
    this._io = _io;
    this._parent = _parent;
    this._root = _root || this;

    this._read();
  }
  Lzh.prototype._read = function() {
    this.entries = [];
    var i = 0;
    while (!this._io.isEof()) {
      this.entries.push(new Record(this._io, this, this._root));
      i++;
    }
  }

  var Record = Lzh.Record = (function() {
    function Record(_io, _parent, _root) {
      this._io = _io;
      this._parent = _parent;
      this._root = _root || this;

      this._read();
    }
    Record.prototype._read = function() {
      this.headerLen = this._io.readU1();
      if (this.headerLen > 0) {
        this.fileRecord = new FileRecord(this._io, this, this._root);
      }
    }

    return Record;
  })();

  var FileRecord = Lzh.FileRecord = (function() {
    function FileRecord(_io, _parent, _root) {
      this._io = _io;
      this._parent = _parent;
      this._root = _root || this;

      this._read();
    }
    FileRecord.prototype._read = function() {
      this._raw_header = this._io.readBytes((this._parent.headerLen - 1));
      var _io__raw_header = new KaitaiStream(this._raw_header);
      this.header = new Header(_io__raw_header, this, this._root);
      if (this.header.header1.lhaLevel == 0) {
        this.fileUncomprCrc16 = this._io.readU2le();
      }
      this.body = this._io.readBytes(this.header.header1.fileSizeCompr);
    }

    return FileRecord;
  })();

  var Header = Lzh.Header = (function() {
    function Header(_io, _parent, _root) {
      this._io = _io;
      this._parent = _parent;
      this._root = _root || this;

      this._read();
    }
    Header.prototype._read = function() {
      this.header1 = new Header1(this._io, this, this._root);
      if (this.header1.lhaLevel == 0) {
        this.filenameLen = this._io.readU1();
      }
      if (this.header1.lhaLevel == 0) {
        this.filename = KaitaiStream.bytesToStr(this._io.readBytes(this.filenameLen), "ASCII");
      }
      if (this.header1.lhaLevel == 2) {
        this.fileUncomprCrc16 = this._io.readU2le();
      }
      if (this.header1.lhaLevel == 2) {
        this.os = this._io.readU1();
      }
      if (this.header1.lhaLevel == 2) {
        this.extHeaderSize = this._io.readU2le();
      }
    }

    /**
     * Level-neutral header, same for all LHA levels. Subsequent fields order and meaning varies, based on LHA level specified in this header.
     */

    return Header;
  })();

  var Header1 = Lzh.Header1 = (function() {
    function Header1(_io, _parent, _root) {
      this._io = _io;
      this._parent = _parent;
      this._root = _root || this;

      this._read();
    }
    Header1.prototype._read = function() {
      this.headerChecksum = this._io.readU1();
      this.methodId = KaitaiStream.bytesToStr(this._io.readBytes(5), "ASCII");
      this.fileSizeCompr = this._io.readU4le();
      this.fileSizeUncompr = this._io.readU4le();
      this._raw_fileTimestamp = this._io.readBytes(4);
      var _io__raw_fileTimestamp = new KaitaiStream(this._raw_fileTimestamp);
      this.fileTimestamp = new DosDatetime(_io__raw_fileTimestamp, this, null);
      this.attr = this._io.readU1();
      this.lhaLevel = this._io.readU1();
    }

    /**
     * Compressed file size
     */

    /**
     * Uncompressed file size
     */

    /**
     * Original file date/time
     */

    /**
     * File or directory attribute
     */

    return Header1;
  })();

  return Lzh;
})();
return Lzh;
}));