Allegro data file: Java parsing library

Allegro library for C (mostly used for game and multimedia apps programming) used its own container file format.

In general, it allows storage of arbitrary binary data blocks bundled together with some simple key-value style metadata ("properties") for every block. Allegro also pre-defines some simple formats for bitmaps, fonts, MIDI music, sound samples and palettes. Allegro library v4.0+ also support LZSS compression.

This spec applies to Allegro data files for library versions 2.2 up to 4.4.

Application

Allegro library (v2.2-v4.4)

File extension

dat

KS implementation details

License: CC0-1.0

References

This page hosts a formal specification of Allegro data file 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:

AllegroDat data = AllegroDat.fromFile("path/to/local/file.dat");

Or parse structure from a byte array:

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

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

data.packMagic() // => get pack magic

Java source code to parse Allegro data file

AllegroDat.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.Map;
import java.util.HashMap;
import java.util.Arrays;
import java.util.ArrayList;
import java.nio.charset.Charset;


/**
 * Allegro library for C (mostly used for game and multimedia apps
 * programming) used its own container file format.
 * 
 * In general, it allows storage of arbitrary binary data blocks
 * bundled together with some simple key-value style metadata
 * ("properties") for every block. Allegro also pre-defines some simple
 * formats for bitmaps, fonts, MIDI music, sound samples and
 * palettes. Allegro library v4.0+ also support LZSS compression.
 * 
 * This spec applies to Allegro data files for library versions 2.2 up
 * to 4.4.
 * @see <a href="https://liballeg.org/stabledocs/en/datafile.html">Source</a>
 */
public class AllegroDat extends KaitaiStruct {
    public static AllegroDat fromFile(String fileName) throws IOException {
        return new AllegroDat(new ByteBufferKaitaiStream(fileName));
    }

    public enum PackEnum {
        UNPACKED(1936484398);

        private final long id;
        PackEnum(long id) { this.id = id; }
        public long id() { return id; }
        private static final Map<Long, PackEnum> byId = new HashMap<Long, PackEnum>(1);
        static {
            for (PackEnum e : PackEnum.values())
                byId.put(e.id(), e);
        }
        public static PackEnum byId(long id) { return byId.get(id); }
    }

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

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

    public AllegroDat(KaitaiStream _io, KaitaiStruct _parent, AllegroDat _root) {
        super(_io);
        this._parent = _parent;
        this._root = _root == null ? this : _root;
        _read();
    }
    private void _read() {
        this.packMagic = PackEnum.byId(this._io.readU4be());
        this.datMagic = this._io.readBytes(4);
        if (!(Arrays.equals(datMagic(), new byte[] { 65, 76, 76, 46 }))) {
            throw new KaitaiStream.ValidationNotEqualError(new byte[] { 65, 76, 76, 46 }, datMagic(), _io(), "/seq/1");
        }
        this.numObjects = this._io.readU4be();
        this.objects = new ArrayList<DatObject>();
        for (int i = 0; i < numObjects(); i++) {
            this.objects.add(new DatObject(this._io, this, _root));
        }
    }

    /**
     * Simple monochrome monospaced font, 95 characters, 8x16 px
     * characters.
     */
    public static class DatFont16 extends KaitaiStruct {
        public static DatFont16 fromFile(String fileName) throws IOException {
            return new DatFont16(new ByteBufferKaitaiStream(fileName));
        }

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

        public DatFont16(KaitaiStream _io, AllegroDat.DatFont _parent) {
            this(_io, _parent, null);
        }

        public DatFont16(KaitaiStream _io, AllegroDat.DatFont _parent, AllegroDat _root) {
            super(_io);
            this._parent = _parent;
            this._root = _root;
            _read();
        }
        private void _read() {
            this.chars = new ArrayList<byte[]>();
            for (int i = 0; i < 95; i++) {
                this.chars.add(this._io.readBytes(16));
            }
        }
        private ArrayList<byte[]> chars;
        private AllegroDat _root;
        private AllegroDat.DatFont _parent;
        public ArrayList<byte[]> chars() { return chars; }
        public AllegroDat _root() { return _root; }
        public AllegroDat.DatFont _parent() { return _parent; }
    }
    public static class DatBitmap extends KaitaiStruct {
        public static DatBitmap fromFile(String fileName) throws IOException {
            return new DatBitmap(new ByteBufferKaitaiStream(fileName));
        }

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

        public DatBitmap(KaitaiStream _io, AllegroDat.DatObject _parent) {
            this(_io, _parent, null);
        }

        public DatBitmap(KaitaiStream _io, AllegroDat.DatObject _parent, AllegroDat _root) {
            super(_io);
            this._parent = _parent;
            this._root = _root;
            _read();
        }
        private void _read() {
            this.bitsPerPixel = this._io.readS2be();
            this.width = this._io.readU2be();
            this.height = this._io.readU2be();
            this.image = this._io.readBytesFull();
        }
        private short bitsPerPixel;
        private int width;
        private int height;
        private byte[] image;
        private AllegroDat _root;
        private AllegroDat.DatObject _parent;
        public short bitsPerPixel() { return bitsPerPixel; }
        public int width() { return width; }
        public int height() { return height; }
        public byte[] image() { return image; }
        public AllegroDat _root() { return _root; }
        public AllegroDat.DatObject _parent() { return _parent; }
    }
    public static class DatFont extends KaitaiStruct {
        public static DatFont fromFile(String fileName) throws IOException {
            return new DatFont(new ByteBufferKaitaiStream(fileName));
        }

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

        public DatFont(KaitaiStream _io, AllegroDat.DatObject _parent) {
            this(_io, _parent, null);
        }

        public DatFont(KaitaiStream _io, AllegroDat.DatObject _parent, AllegroDat _root) {
            super(_io);
            this._parent = _parent;
            this._root = _root;
            _read();
        }
        private void _read() {
            this.fontSize = this._io.readS2be();
            switch (fontSize()) {
            case 8: {
                this.body = new DatFont8(this._io, this, _root);
                break;
            }
            case 16: {
                this.body = new DatFont16(this._io, this, _root);
                break;
            }
            case 0: {
                this.body = new DatFont39(this._io, this, _root);
                break;
            }
            }
        }
        private short fontSize;
        private KaitaiStruct body;
        private AllegroDat _root;
        private AllegroDat.DatObject _parent;
        public short fontSize() { return fontSize; }
        public KaitaiStruct body() { return body; }
        public AllegroDat _root() { return _root; }
        public AllegroDat.DatObject _parent() { return _parent; }
    }

    /**
     * Simple monochrome monospaced font, 95 characters, 8x8 px
     * characters.
     */
    public static class DatFont8 extends KaitaiStruct {
        public static DatFont8 fromFile(String fileName) throws IOException {
            return new DatFont8(new ByteBufferKaitaiStream(fileName));
        }

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

        public DatFont8(KaitaiStream _io, AllegroDat.DatFont _parent) {
            this(_io, _parent, null);
        }

        public DatFont8(KaitaiStream _io, AllegroDat.DatFont _parent, AllegroDat _root) {
            super(_io);
            this._parent = _parent;
            this._root = _root;
            _read();
        }
        private void _read() {
            this.chars = new ArrayList<byte[]>();
            for (int i = 0; i < 95; i++) {
                this.chars.add(this._io.readBytes(8));
            }
        }
        private ArrayList<byte[]> chars;
        private AllegroDat _root;
        private AllegroDat.DatFont _parent;
        public ArrayList<byte[]> chars() { return chars; }
        public AllegroDat _root() { return _root; }
        public AllegroDat.DatFont _parent() { return _parent; }
    }
    public static class DatObject extends KaitaiStruct {
        public static DatObject fromFile(String fileName) throws IOException {
            return new DatObject(new ByteBufferKaitaiStream(fileName));
        }

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

        public DatObject(KaitaiStream _io, AllegroDat _parent) {
            this(_io, _parent, null);
        }

        public DatObject(KaitaiStream _io, AllegroDat _parent, AllegroDat _root) {
            super(_io);
            this._parent = _parent;
            this._root = _root;
            _read();
        }
        private void _read() {
            this.properties = new ArrayList<Property>();
            {
                Property _it;
                int i = 0;
                do {
                    _it = new Property(this._io, this, _root);
                    this.properties.add(_it);
                    i++;
                } while (!(!(_it.isValid())));
            }
            this.lenCompressed = this._io.readS4be();
            this.lenUncompressed = this._io.readS4be();
            switch (type()) {
            case "BMP ": {
                this._raw_body = this._io.readBytes(lenCompressed());
                KaitaiStream _io__raw_body = new ByteBufferKaitaiStream(_raw_body);
                this.body = new DatBitmap(_io__raw_body, this, _root);
                break;
            }
            case "RLE ": {
                this._raw_body = this._io.readBytes(lenCompressed());
                KaitaiStream _io__raw_body = new ByteBufferKaitaiStream(_raw_body);
                this.body = new DatRleSprite(_io__raw_body, this, _root);
                break;
            }
            case "FONT": {
                this._raw_body = this._io.readBytes(lenCompressed());
                KaitaiStream _io__raw_body = new ByteBufferKaitaiStream(_raw_body);
                this.body = new DatFont(_io__raw_body, this, _root);
                break;
            }
            default: {
                this.body = this._io.readBytes(lenCompressed());
                break;
            }
            }
        }
        private String type;
        public String type() {
            if (this.type != null)
                return this.type;
            this.type = properties().get(properties().size() - 1).magic();
            return this.type;
        }
        private ArrayList<Property> properties;
        private int lenCompressed;
        private int lenUncompressed;
        private Object body;
        private AllegroDat _root;
        private AllegroDat _parent;
        private byte[] _raw_body;
        public ArrayList<Property> properties() { return properties; }
        public int lenCompressed() { return lenCompressed; }
        public int lenUncompressed() { return lenUncompressed; }
        public Object body() { return body; }
        public AllegroDat _root() { return _root; }
        public AllegroDat _parent() { return _parent; }
        public byte[] _raw_body() { return _raw_body; }
    }

    /**
     * New bitmap font format introduced since Allegro 3.9: allows
     * flexible designation of character ranges, 8-bit colored
     * characters, etc.
     */
    public static class DatFont39 extends KaitaiStruct {
        public static DatFont39 fromFile(String fileName) throws IOException {
            return new DatFont39(new ByteBufferKaitaiStream(fileName));
        }

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

        public DatFont39(KaitaiStream _io, AllegroDat.DatFont _parent) {
            this(_io, _parent, null);
        }

        public DatFont39(KaitaiStream _io, AllegroDat.DatFont _parent, AllegroDat _root) {
            super(_io);
            this._parent = _parent;
            this._root = _root;
            _read();
        }
        private void _read() {
            this.numRanges = this._io.readS2be();
            this.ranges = new ArrayList<Range>();
            for (int i = 0; i < numRanges(); i++) {
                this.ranges.add(new Range(this._io, this, _root));
            }
        }
        public static class Range extends KaitaiStruct {
            public static Range fromFile(String fileName) throws IOException {
                return new Range(new ByteBufferKaitaiStream(fileName));
            }

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

            public Range(KaitaiStream _io, AllegroDat.DatFont39 _parent) {
                this(_io, _parent, null);
            }

            public Range(KaitaiStream _io, AllegroDat.DatFont39 _parent, AllegroDat _root) {
                super(_io);
                this._parent = _parent;
                this._root = _root;
                _read();
            }
            private void _read() {
                this.mono = this._io.readU1();
                this.startChar = this._io.readU4be();
                this.endChar = this._io.readU4be();
                this.chars = new ArrayList<FontChar>();
                for (int i = 0; i < ((endChar() - startChar()) + 1); i++) {
                    this.chars.add(new FontChar(this._io, this, _root));
                }
            }
            private int mono;
            private long startChar;
            private long endChar;
            private ArrayList<FontChar> chars;
            private AllegroDat _root;
            private AllegroDat.DatFont39 _parent;
            public int mono() { return mono; }

            /**
             * First character in range
             */
            public long startChar() { return startChar; }

            /**
             * Last character in range (inclusive)
             */
            public long endChar() { return endChar; }
            public ArrayList<FontChar> chars() { return chars; }
            public AllegroDat _root() { return _root; }
            public AllegroDat.DatFont39 _parent() { return _parent; }
        }
        public static class FontChar extends KaitaiStruct {
            public static FontChar fromFile(String fileName) throws IOException {
                return new FontChar(new ByteBufferKaitaiStream(fileName));
            }

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

            public FontChar(KaitaiStream _io, AllegroDat.DatFont39.Range _parent) {
                this(_io, _parent, null);
            }

            public FontChar(KaitaiStream _io, AllegroDat.DatFont39.Range _parent, AllegroDat _root) {
                super(_io);
                this._parent = _parent;
                this._root = _root;
                _read();
            }
            private void _read() {
                this.width = this._io.readU2be();
                this.height = this._io.readU2be();
                this.body = this._io.readBytes((width() * height()));
            }
            private int width;
            private int height;
            private byte[] body;
            private AllegroDat _root;
            private AllegroDat.DatFont39.Range _parent;
            public int width() { return width; }
            public int height() { return height; }
            public byte[] body() { return body; }
            public AllegroDat _root() { return _root; }
            public AllegroDat.DatFont39.Range _parent() { return _parent; }
        }
        private short numRanges;
        private ArrayList<Range> ranges;
        private AllegroDat _root;
        private AllegroDat.DatFont _parent;
        public short numRanges() { return numRanges; }
        public ArrayList<Range> ranges() { return ranges; }
        public AllegroDat _root() { return _root; }
        public AllegroDat.DatFont _parent() { return _parent; }
    }
    public static class Property extends KaitaiStruct {
        public static Property fromFile(String fileName) throws IOException {
            return new Property(new ByteBufferKaitaiStream(fileName));
        }

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

        public Property(KaitaiStream _io, AllegroDat.DatObject _parent) {
            this(_io, _parent, null);
        }

        public Property(KaitaiStream _io, AllegroDat.DatObject _parent, AllegroDat _root) {
            super(_io);
            this._parent = _parent;
            this._root = _root;
            _read();
        }
        private void _read() {
            this.magic = new String(this._io.readBytes(4), Charset.forName("UTF-8"));
            if (isValid()) {
                this.type = new String(this._io.readBytes(4), Charset.forName("UTF-8"));
            }
            if (isValid()) {
                this.lenBody = this._io.readU4be();
            }
            if (isValid()) {
                this.body = new String(this._io.readBytes(lenBody()), Charset.forName("UTF-8"));
            }
        }
        private Boolean isValid;
        public Boolean isValid() {
            if (this.isValid != null)
                return this.isValid;
            boolean _tmp = (boolean) (magic().equals("prop"));
            this.isValid = _tmp;
            return this.isValid;
        }
        private String magic;
        private String type;
        private Long lenBody;
        private String body;
        private AllegroDat _root;
        private AllegroDat.DatObject _parent;
        public String magic() { return magic; }
        public String type() { return type; }
        public Long lenBody() { return lenBody; }
        public String body() { return body; }
        public AllegroDat _root() { return _root; }
        public AllegroDat.DatObject _parent() { return _parent; }
    }
    public static class DatRleSprite extends KaitaiStruct {
        public static DatRleSprite fromFile(String fileName) throws IOException {
            return new DatRleSprite(new ByteBufferKaitaiStream(fileName));
        }

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

        public DatRleSprite(KaitaiStream _io, AllegroDat.DatObject _parent) {
            this(_io, _parent, null);
        }

        public DatRleSprite(KaitaiStream _io, AllegroDat.DatObject _parent, AllegroDat _root) {
            super(_io);
            this._parent = _parent;
            this._root = _root;
            _read();
        }
        private void _read() {
            this.bitsPerPixel = this._io.readS2be();
            this.width = this._io.readU2be();
            this.height = this._io.readU2be();
            this.lenImage = this._io.readU4be();
            this.image = this._io.readBytesFull();
        }
        private short bitsPerPixel;
        private int width;
        private int height;
        private long lenImage;
        private byte[] image;
        private AllegroDat _root;
        private AllegroDat.DatObject _parent;
        public short bitsPerPixel() { return bitsPerPixel; }
        public int width() { return width; }
        public int height() { return height; }
        public long lenImage() { return lenImage; }
        public byte[] image() { return image; }
        public AllegroDat _root() { return _root; }
        public AllegroDat.DatObject _parent() { return _parent; }
    }
    private PackEnum packMagic;
    private byte[] datMagic;
    private long numObjects;
    private ArrayList<DatObject> objects;
    private AllegroDat _root;
    private KaitaiStruct _parent;
    public PackEnum packMagic() { return packMagic; }
    public byte[] datMagic() { return datMagic; }
    public long numObjects() { return numObjects; }
    public ArrayList<DatObject> objects() { return objects; }
    public AllegroDat _root() { return _root; }
    public KaitaiStruct _parent() { return _parent; }
}