.pak file format of Dune 2 game engine: Java parsing library

Application

Dune 2 game engine

File extension

pak

KS implementation details

License: CC0-1.0
Minimal Kaitai Struct required: 0.8

This page hosts a formal specification of .pak file format of Dune 2 game engine 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 Java generated by Kaitai Struct depends on the Java runtime library. You have to install it before you can parse data.

The Java runtime library is published in the Maven Central Repository. Refer to the artifact page for instructions how to add it into your project with the build tool that you use.

Code

Parse a local file and get structure in memory:

Dune2Pak data = Dune2Pak.fromFile("path/to/local/file.pak");

Or parse structure from a byte array:

byte[] someArray = new byte[] { ... };
Dune2Pak data = new Dune2Pak(new ByteBufferKaitaiStream(someArray));

After that, one can get various attributes from the structure by invoking getter methods like:

data.dir() // => get dir

Java source code to parse .pak file format of Dune 2 game engine

Dune2Pak.java

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

import io.kaitai.struct.ByteBufferKaitaiStream;
import io.kaitai.struct.KaitaiStruct;
import io.kaitai.struct.KaitaiStream;
import java.io.IOException;
import java.util.ArrayList;
import java.nio.charset.Charset;


/**
 * @see <a href="https://moddingwiki.shikadi.net/wiki/PAK_Format_(Westwood)">Source</a>
 */
public class Dune2Pak extends KaitaiStruct {
    public static Dune2Pak fromFile(String fileName) throws IOException {
        return new Dune2Pak(new ByteBufferKaitaiStream(fileName));
    }

    public Dune2Pak(KaitaiStream _io) {
        this(_io, null, null);
    }

    public Dune2Pak(KaitaiStream _io, KaitaiStruct _parent) {
        this(_io, _parent, null);
    }

    public Dune2Pak(KaitaiStream _io, KaitaiStruct _parent, Dune2Pak _root) {
        super(_io);
        this._parent = _parent;
        this._root = _root == null ? this : _root;
        _read();
    }
    private void _read() {
        this._raw_dir = this._io.readBytes(dirSize());
        KaitaiStream _io__raw_dir = new ByteBufferKaitaiStream(_raw_dir);
        this.dir = new Files(_io__raw_dir, this, _root);
    }
    public static class Files extends KaitaiStruct {
        public static Files fromFile(String fileName) throws IOException {
            return new Files(new ByteBufferKaitaiStream(fileName));
        }

        public Files(KaitaiStream _io) {
            this(_io, null, null);
        }

        public Files(KaitaiStream _io, Dune2Pak _parent) {
            this(_io, _parent, null);
        }

        public Files(KaitaiStream _io, Dune2Pak _parent, Dune2Pak _root) {
            super(_io);
            this._parent = _parent;
            this._root = _root;
            _read();
        }
        private void _read() {
            this.files = new ArrayList<File>();
            {
                int i = 0;
                while (!this._io.isEof()) {
                    this.files.add(new File(this._io, this, _root, i));
                    i++;
                }
            }
        }
        private ArrayList<File> files;
        private Dune2Pak _root;
        private Dune2Pak _parent;
        public ArrayList<File> files() { return files; }
        public Dune2Pak _root() { return _root; }
        public Dune2Pak _parent() { return _parent; }
    }
    public static class File extends KaitaiStruct {

        public File(KaitaiStream _io, long idx) {
            this(_io, null, null, idx);
        }

        public File(KaitaiStream _io, Dune2Pak.Files _parent, long idx) {
            this(_io, _parent, null, idx);
        }

        public File(KaitaiStream _io, Dune2Pak.Files _parent, Dune2Pak _root, long idx) {
            super(_io);
            this._parent = _parent;
            this._root = _root;
            this.idx = idx;
            _read();
        }
        private void _read() {
            this.ofs = this._io.readU4le();
            if (ofs() != 0) {
                this.fileName = new String(this._io.readBytesTerm((byte) 0, false, true, true), Charset.forName("ASCII"));
            }
        }
        private Long nextOfs0;
        public Long nextOfs0() {
            if (this.nextOfs0 != null)
                return this.nextOfs0;
            if (ofs() != 0) {
                long _tmp = (long) (_root().dir().files().get((int) (idx() + 1)).ofs());
                this.nextOfs0 = _tmp;
            }
            return this.nextOfs0;
        }
        private Integer nextOfs;
        public Integer nextOfs() {
            if (this.nextOfs != null)
                return this.nextOfs;
            if (ofs() != 0) {
                int _tmp = (int) ((nextOfs0() == 0 ? _root()._io().size() : nextOfs0()));
                this.nextOfs = _tmp;
            }
            return this.nextOfs;
        }
        private byte[] body;
        public byte[] body() {
            if (this.body != null)
                return this.body;
            if (ofs() != 0) {
                KaitaiStream io = _root()._io();
                long _pos = io.pos();
                io.seek(ofs());
                this.body = io.readBytes((nextOfs() - ofs()));
                io.seek(_pos);
            }
            return this.body;
        }
        private long ofs;
        private String fileName;
        private long idx;
        private Dune2Pak _root;
        private Dune2Pak.Files _parent;
        public long ofs() { return ofs; }
        public String fileName() { return fileName; }
        public long idx() { return idx; }
        public Dune2Pak _root() { return _root; }
        public Dune2Pak.Files _parent() { return _parent; }
    }
    private Long dirSize;
    public Long dirSize() {
        if (this.dirSize != null)
            return this.dirSize;
        long _pos = this._io.pos();
        this._io.seek(0);
        this.dirSize = this._io.readU4le();
        this._io.seek(_pos);
        return this.dirSize;
    }
    private Files dir;
    private Dune2Pak _root;
    private KaitaiStruct _parent;
    private byte[] _raw_dir;
    public Files dir() { return dir; }
    public Dune2Pak _root() { return _root; }
    public KaitaiStruct _parent() { return _parent; }
    public byte[] _raw_dir() { return _raw_dir; }
}