.wad file format of id Tech 1: PHP parsing library

Application

id Tech 1

File extension

wad

KS implementation details

License: CC0-1.0

This page hosts a formal specification of .wad file format of id Tech 1 using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.

PHP source code to parse .wad file format of id Tech 1

DoomWad.php

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

class DoomWad extends \Kaitai\Struct\Struct {
    public function __construct(\Kaitai\Struct\Stream $_io, \Kaitai\Struct\Struct $_parent = null, \DoomWad $_root = null) {
        parent::__construct($_io, $_parent, $_root);
        $this->_read();
    }

    private function _read() {
        $this->_m_magic = \Kaitai\Struct\Stream::bytesToStr($this->_io->readBytes(4), "ASCII");
        $this->_m_numIndexEntries = $this->_io->readS4le();
        $this->_m_indexOffset = $this->_io->readS4le();
    }
    protected $_m_index;
    public function index() {
        if ($this->_m_index !== null)
            return $this->_m_index;
        $_pos = $this->_io->pos();
        $this->_io->seek($this->indexOffset());
        $this->_m_index = [];
        $n = $this->numIndexEntries();
        for ($i = 0; $i < $n; $i++) {
            $this->_m_index[] = new \DoomWad\IndexEntry($this->_io, $this, $this->_root);
        }
        $this->_io->seek($_pos);
        return $this->_m_index;
    }
    protected $_m_magic;
    protected $_m_numIndexEntries;
    protected $_m_indexOffset;
    public function magic() { return $this->_m_magic; }

    /**
     * Number of entries in the lump index
     */
    public function numIndexEntries() { return $this->_m_numIndexEntries; }

    /**
     * Offset to the start of the index
     */
    public function indexOffset() { return $this->_m_indexOffset; }
}

namespace \DoomWad;

class Sectors extends \Kaitai\Struct\Struct {
    public function __construct(\Kaitai\Struct\Stream $_io, \DoomWad\IndexEntry $_parent = null, \DoomWad $_root = null) {
        parent::__construct($_io, $_parent, $_root);
        $this->_read();
    }

    private function _read() {
        $this->_m_entries = [];
        $i = 0;
        while (!$this->_io->isEof()) {
            $this->_m_entries[] = new \DoomWad\Sector($this->_io, $this, $this->_root);
            $i++;
        }
    }
    protected $_m_entries;
    public function entries() { return $this->_m_entries; }
}

namespace \DoomWad;

class Vertex extends \Kaitai\Struct\Struct {
    public function __construct(\Kaitai\Struct\Stream $_io, \DoomWad\Vertexes $_parent = null, \DoomWad $_root = null) {
        parent::__construct($_io, $_parent, $_root);
        $this->_read();
    }

    private function _read() {
        $this->_m_x = $this->_io->readS2le();
        $this->_m_y = $this->_io->readS2le();
    }
    protected $_m_x;
    protected $_m_y;
    public function x() { return $this->_m_x; }
    public function y() { return $this->_m_y; }
}

/**
 * Used for TEXTURE1 and TEXTURE2 lumps, which designate how to
 * combine wall patches to make wall textures. This essentially
 * provides a very simple form of image compression, allowing
 * certain elements ("patches") to be reused / recombined on
 * different textures for more variety in the game.
 */

namespace \DoomWad;

class Texture12 extends \Kaitai\Struct\Struct {
    public function __construct(\Kaitai\Struct\Stream $_io, \DoomWad\IndexEntry $_parent = null, \DoomWad $_root = null) {
        parent::__construct($_io, $_parent, $_root);
        $this->_read();
    }

    private function _read() {
        $this->_m_numTextures = $this->_io->readS4le();
        $this->_m_textures = [];
        $n = $this->numTextures();
        for ($i = 0; $i < $n; $i++) {
            $this->_m_textures[] = new \DoomWad\Texture12\TextureIndex($this->_io, $this, $this->_root);
        }
    }
    protected $_m_numTextures;
    protected $_m_textures;

    /**
     * Number of wall textures
     */
    public function numTextures() { return $this->_m_numTextures; }
    public function textures() { return $this->_m_textures; }
}

namespace \DoomWad\Texture12;

class TextureIndex extends \Kaitai\Struct\Struct {
    public function __construct(\Kaitai\Struct\Stream $_io, \DoomWad\Texture12 $_parent = null, \DoomWad $_root = null) {
        parent::__construct($_io, $_parent, $_root);
        $this->_read();
    }

    private function _read() {
        $this->_m_offset = $this->_io->readS4le();
    }
    protected $_m_body;
    public function body() {
        if ($this->_m_body !== null)
            return $this->_m_body;
        $_pos = $this->_io->pos();
        $this->_io->seek($this->offset());
        $this->_m_body = new \DoomWad\Texture12\TextureBody($this->_io, $this, $this->_root);
        $this->_io->seek($_pos);
        return $this->_m_body;
    }
    protected $_m_offset;
    public function offset() { return $this->_m_offset; }
}

namespace \DoomWad\Texture12;

class TextureBody extends \Kaitai\Struct\Struct {
    public function __construct(\Kaitai\Struct\Stream $_io, \DoomWad\Texture12\TextureIndex $_parent = null, \DoomWad $_root = null) {
        parent::__construct($_io, $_parent, $_root);
        $this->_read();
    }

    private function _read() {
        $this->_m_name = \Kaitai\Struct\Stream::bytesToStr(\Kaitai\Struct\Stream::bytesStripRight($this->_io->readBytes(8), 0), "ASCII");
        $this->_m_masked = $this->_io->readU4le();
        $this->_m_width = $this->_io->readU2le();
        $this->_m_height = $this->_io->readU2le();
        $this->_m_columnDirectory = $this->_io->readU4le();
        $this->_m_numPatches = $this->_io->readU2le();
        $this->_m_patches = [];
        $n = $this->numPatches();
        for ($i = 0; $i < $n; $i++) {
            $this->_m_patches[] = new \DoomWad\Texture12\Patch($this->_io, $this, $this->_root);
        }
    }
    protected $_m_name;
    protected $_m_masked;
    protected $_m_width;
    protected $_m_height;
    protected $_m_columnDirectory;
    protected $_m_numPatches;
    protected $_m_patches;

    /**
     * Name of a texture, only `A-Z`, `0-9`, `[]_-` are valid
     */
    public function name() { return $this->_m_name; }
    public function masked() { return $this->_m_masked; }
    public function width() { return $this->_m_width; }
    public function height() { return $this->_m_height; }

    /**
     * Obsolete, ignored by all DOOM versions
     */
    public function columnDirectory() { return $this->_m_columnDirectory; }

    /**
     * Number of patches that are used in a texture
     */
    public function numPatches() { return $this->_m_numPatches; }
    public function patches() { return $this->_m_patches; }
}

namespace \DoomWad\Texture12;

class Patch extends \Kaitai\Struct\Struct {
    public function __construct(\Kaitai\Struct\Stream $_io, \DoomWad\Texture12\TextureBody $_parent = null, \DoomWad $_root = null) {
        parent::__construct($_io, $_parent, $_root);
        $this->_read();
    }

    private function _read() {
        $this->_m_originX = $this->_io->readS2le();
        $this->_m_originY = $this->_io->readS2le();
        $this->_m_patchId = $this->_io->readU2le();
        $this->_m_stepDir = $this->_io->readU2le();
        $this->_m_colormap = $this->_io->readU2le();
    }
    protected $_m_originX;
    protected $_m_originY;
    protected $_m_patchId;
    protected $_m_stepDir;
    protected $_m_colormap;

    /**
     * X offset to draw a patch at (pixels from left boundary of a texture)
     */
    public function originX() { return $this->_m_originX; }

    /**
     * Y offset to draw a patch at (pixels from upper boundary of a texture)
     */
    public function originY() { return $this->_m_originY; }

    /**
     * Identifier of a patch (as listed in PNAMES lump) to draw
     */
    public function patchId() { return $this->_m_patchId; }
    public function stepDir() { return $this->_m_stepDir; }
    public function colormap() { return $this->_m_colormap; }
}

namespace \DoomWad;

class Linedef extends \Kaitai\Struct\Struct {
    public function __construct(\Kaitai\Struct\Stream $_io, \DoomWad\Linedefs $_parent = null, \DoomWad $_root = null) {
        parent::__construct($_io, $_parent, $_root);
        $this->_read();
    }

    private function _read() {
        $this->_m_vertexStartIdx = $this->_io->readU2le();
        $this->_m_vertexEndIdx = $this->_io->readU2le();
        $this->_m_flags = $this->_io->readU2le();
        $this->_m_lineType = $this->_io->readU2le();
        $this->_m_sectorTag = $this->_io->readU2le();
        $this->_m_sidedefRightIdx = $this->_io->readU2le();
        $this->_m_sidedefLeftIdx = $this->_io->readU2le();
    }
    protected $_m_vertexStartIdx;
    protected $_m_vertexEndIdx;
    protected $_m_flags;
    protected $_m_lineType;
    protected $_m_sectorTag;
    protected $_m_sidedefRightIdx;
    protected $_m_sidedefLeftIdx;
    public function vertexStartIdx() { return $this->_m_vertexStartIdx; }
    public function vertexEndIdx() { return $this->_m_vertexEndIdx; }
    public function flags() { return $this->_m_flags; }
    public function lineType() { return $this->_m_lineType; }
    public function sectorTag() { return $this->_m_sectorTag; }
    public function sidedefRightIdx() { return $this->_m_sidedefRightIdx; }
    public function sidedefLeftIdx() { return $this->_m_sidedefLeftIdx; }
}

namespace \DoomWad;

class Pnames extends \Kaitai\Struct\Struct {
    public function __construct(\Kaitai\Struct\Stream $_io, \DoomWad\IndexEntry $_parent = null, \DoomWad $_root = null) {
        parent::__construct($_io, $_parent, $_root);
        $this->_read();
    }

    private function _read() {
        $this->_m_numPatches = $this->_io->readU4le();
        $this->_m_names = [];
        $n = $this->numPatches();
        for ($i = 0; $i < $n; $i++) {
            $this->_m_names[] = \Kaitai\Struct\Stream::bytesToStr(\Kaitai\Struct\Stream::bytesStripRight($this->_io->readBytes(8), 0), "ASCII");
        }
    }
    protected $_m_numPatches;
    protected $_m_names;

    /**
     * Number of patches registered in this global game directory
     */
    public function numPatches() { return $this->_m_numPatches; }
    public function names() { return $this->_m_names; }
}

namespace \DoomWad;

class Thing extends \Kaitai\Struct\Struct {
    public function __construct(\Kaitai\Struct\Stream $_io, \DoomWad\Things $_parent = null, \DoomWad $_root = null) {
        parent::__construct($_io, $_parent, $_root);
        $this->_read();
    }

    private function _read() {
        $this->_m_x = $this->_io->readS2le();
        $this->_m_y = $this->_io->readS2le();
        $this->_m_angle = $this->_io->readU2le();
        $this->_m_type = $this->_io->readU2le();
        $this->_m_flags = $this->_io->readU2le();
    }
    protected $_m_x;
    protected $_m_y;
    protected $_m_angle;
    protected $_m_type;
    protected $_m_flags;
    public function x() { return $this->_m_x; }
    public function y() { return $this->_m_y; }
    public function angle() { return $this->_m_angle; }
    public function type() { return $this->_m_type; }
    public function flags() { return $this->_m_flags; }
}

namespace \DoomWad;

class Sector extends \Kaitai\Struct\Struct {
    public function __construct(\Kaitai\Struct\Stream $_io, \DoomWad\Sectors $_parent = null, \DoomWad $_root = null) {
        parent::__construct($_io, $_parent, $_root);
        $this->_read();
    }

    private function _read() {
        $this->_m_floorZ = $this->_io->readS2le();
        $this->_m_ceilZ = $this->_io->readS2le();
        $this->_m_floorFlat = \Kaitai\Struct\Stream::bytesToStr($this->_io->readBytes(8), "ASCII");
        $this->_m_ceilFlat = \Kaitai\Struct\Stream::bytesToStr($this->_io->readBytes(8), "ASCII");
        $this->_m_light = $this->_io->readS2le();
        $this->_m_specialType = $this->_io->readU2le();
        $this->_m_tag = $this->_io->readU2le();
    }
    protected $_m_floorZ;
    protected $_m_ceilZ;
    protected $_m_floorFlat;
    protected $_m_ceilFlat;
    protected $_m_light;
    protected $_m_specialType;
    protected $_m_tag;
    public function floorZ() { return $this->_m_floorZ; }
    public function ceilZ() { return $this->_m_ceilZ; }
    public function floorFlat() { return $this->_m_floorFlat; }
    public function ceilFlat() { return $this->_m_ceilFlat; }

    /**
     * Light level of the sector [0..255]. Original engine uses
     * COLORMAP to render lighting, so only 32 actual levels are
     * available (i.e. 0..7, 8..15, etc).
     */
    public function light() { return $this->_m_light; }
    public function specialType() { return $this->_m_specialType; }

    /**
     * Tag number. When the linedef with the same tag number is
     * activated, some effect will be triggered in this sector.
     */
    public function tag() { return $this->_m_tag; }
}

namespace \DoomWad\Sector;

class SpecialSector {
    const NORMAL = 0;
    const D_LIGHT_FLICKER = 1;
    const D_LIGHT_STROBE_FAST = 2;
    const D_LIGHT_STROBE_SLOW = 3;
    const D_LIGHT_STROBE_HURT = 4;
    const D_DAMAGE_HELLSLIME = 5;
    const D_DAMAGE_NUKAGE = 7;
    const D_LIGHT_GLOW = 8;
    const SECRET = 9;
    const D_SECTOR_DOOR_CLOSE_IN_30 = 10;
    const D_DAMAGE_END = 11;
    const D_LIGHT_STROBE_SLOW_SYNC = 12;
    const D_LIGHT_STROBE_FAST_SYNC = 13;
    const D_SECTOR_DOOR_RAISE_IN_5_MINS = 14;
    const D_FRICTION_LOW = 15;
    const D_DAMAGE_SUPER_HELLSLIME = 16;
    const D_LIGHT_FIRE_FLICKER = 17;
    const D_DAMAGE_LAVA_WIMPY = 18;
    const D_DAMAGE_LAVA_HEFTY = 19;
    const D_SCROLL_EAST_LAVA_DAMAGE = 20;
    const LIGHT_PHASED = 21;
    const LIGHT_SEQUENCE_START = 22;
    const LIGHT_SEQUENCE_SPECIAL1 = 23;
    const LIGHT_SEQUENCE_SPECIAL2 = 24;
}

namespace \DoomWad;

class Vertexes extends \Kaitai\Struct\Struct {
    public function __construct(\Kaitai\Struct\Stream $_io, \DoomWad\IndexEntry $_parent = null, \DoomWad $_root = null) {
        parent::__construct($_io, $_parent, $_root);
        $this->_read();
    }

    private function _read() {
        $this->_m_entries = [];
        $i = 0;
        while (!$this->_io->isEof()) {
            $this->_m_entries[] = new \DoomWad\Vertex($this->_io, $this, $this->_root);
            $i++;
        }
    }
    protected $_m_entries;
    public function entries() { return $this->_m_entries; }
}

namespace \DoomWad;

class Sidedef extends \Kaitai\Struct\Struct {
    public function __construct(\Kaitai\Struct\Stream $_io, \DoomWad\Sidedefs $_parent = null, \DoomWad $_root = null) {
        parent::__construct($_io, $_parent, $_root);
        $this->_read();
    }

    private function _read() {
        $this->_m_offsetX = $this->_io->readS2le();
        $this->_m_offsetY = $this->_io->readS2le();
        $this->_m_upperTextureName = \Kaitai\Struct\Stream::bytesToStr($this->_io->readBytes(8), "ASCII");
        $this->_m_lowerTextureName = \Kaitai\Struct\Stream::bytesToStr($this->_io->readBytes(8), "ASCII");
        $this->_m_normalTextureName = \Kaitai\Struct\Stream::bytesToStr($this->_io->readBytes(8), "ASCII");
        $this->_m_sectorId = $this->_io->readS2le();
    }
    protected $_m_offsetX;
    protected $_m_offsetY;
    protected $_m_upperTextureName;
    protected $_m_lowerTextureName;
    protected $_m_normalTextureName;
    protected $_m_sectorId;
    public function offsetX() { return $this->_m_offsetX; }
    public function offsetY() { return $this->_m_offsetY; }
    public function upperTextureName() { return $this->_m_upperTextureName; }
    public function lowerTextureName() { return $this->_m_lowerTextureName; }
    public function normalTextureName() { return $this->_m_normalTextureName; }
    public function sectorId() { return $this->_m_sectorId; }
}

namespace \DoomWad;

class Things extends \Kaitai\Struct\Struct {
    public function __construct(\Kaitai\Struct\Stream $_io, \DoomWad\IndexEntry $_parent = null, \DoomWad $_root = null) {
        parent::__construct($_io, $_parent, $_root);
        $this->_read();
    }

    private function _read() {
        $this->_m_entries = [];
        $i = 0;
        while (!$this->_io->isEof()) {
            $this->_m_entries[] = new \DoomWad\Thing($this->_io, $this, $this->_root);
            $i++;
        }
    }
    protected $_m_entries;
    public function entries() { return $this->_m_entries; }
}

namespace \DoomWad;

class Linedefs extends \Kaitai\Struct\Struct {
    public function __construct(\Kaitai\Struct\Stream $_io, \DoomWad\IndexEntry $_parent = null, \DoomWad $_root = null) {
        parent::__construct($_io, $_parent, $_root);
        $this->_read();
    }

    private function _read() {
        $this->_m_entries = [];
        $i = 0;
        while (!$this->_io->isEof()) {
            $this->_m_entries[] = new \DoomWad\Linedef($this->_io, $this, $this->_root);
            $i++;
        }
    }
    protected $_m_entries;
    public function entries() { return $this->_m_entries; }
}

namespace \DoomWad;

class IndexEntry extends \Kaitai\Struct\Struct {
    public function __construct(\Kaitai\Struct\Stream $_io, \DoomWad $_parent = null, \DoomWad $_root = null) {
        parent::__construct($_io, $_parent, $_root);
        $this->_read();
    }

    private function _read() {
        $this->_m_offset = $this->_io->readS4le();
        $this->_m_size = $this->_io->readS4le();
        $this->_m_name = \Kaitai\Struct\Stream::bytesToStr(\Kaitai\Struct\Stream::bytesStripRight($this->_io->readBytes(8), 0), "ASCII");
    }
    protected $_m_contents;
    public function contents() {
        if ($this->_m_contents !== null)
            return $this->_m_contents;
        $io = $this->_root()->_io();
        $_pos = $io->pos();
        $io->seek($this->offset());
        switch ($this->name()) {
            case "SECTORS":
                $this->_m__raw_contents = $io->readBytes($this->size());
                $io = new \Kaitai\Struct\Stream($this->_m__raw_contents);
                $this->_m_contents = new \DoomWad\Sectors($io, $this, $this->_root);
                break;
            case "TEXTURE1":
                $this->_m__raw_contents = $io->readBytes($this->size());
                $io = new \Kaitai\Struct\Stream($this->_m__raw_contents);
                $this->_m_contents = new \DoomWad\Texture12($io, $this, $this->_root);
                break;
            case "VERTEXES":
                $this->_m__raw_contents = $io->readBytes($this->size());
                $io = new \Kaitai\Struct\Stream($this->_m__raw_contents);
                $this->_m_contents = new \DoomWad\Vertexes($io, $this, $this->_root);
                break;
            case "BLOCKMAP":
                $this->_m__raw_contents = $io->readBytes($this->size());
                $io = new \Kaitai\Struct\Stream($this->_m__raw_contents);
                $this->_m_contents = new \DoomWad\Blockmap($io, $this, $this->_root);
                break;
            case "PNAMES":
                $this->_m__raw_contents = $io->readBytes($this->size());
                $io = new \Kaitai\Struct\Stream($this->_m__raw_contents);
                $this->_m_contents = new \DoomWad\Pnames($io, $this, $this->_root);
                break;
            case "TEXTURE2":
                $this->_m__raw_contents = $io->readBytes($this->size());
                $io = new \Kaitai\Struct\Stream($this->_m__raw_contents);
                $this->_m_contents = new \DoomWad\Texture12($io, $this, $this->_root);
                break;
            case "THINGS":
                $this->_m__raw_contents = $io->readBytes($this->size());
                $io = new \Kaitai\Struct\Stream($this->_m__raw_contents);
                $this->_m_contents = new \DoomWad\Things($io, $this, $this->_root);
                break;
            case "LINEDEFS":
                $this->_m__raw_contents = $io->readBytes($this->size());
                $io = new \Kaitai\Struct\Stream($this->_m__raw_contents);
                $this->_m_contents = new \DoomWad\Linedefs($io, $this, $this->_root);
                break;
            case "SIDEDEFS":
                $this->_m__raw_contents = $io->readBytes($this->size());
                $io = new \Kaitai\Struct\Stream($this->_m__raw_contents);
                $this->_m_contents = new \DoomWad\Sidedefs($io, $this, $this->_root);
                break;
            default:
                $this->_m_contents = $io->readBytes($this->size());
                break;
        }
        $io->seek($_pos);
        return $this->_m_contents;
    }
    protected $_m_offset;
    protected $_m_size;
    protected $_m_name;
    protected $_m__raw_contents;
    public function offset() { return $this->_m_offset; }
    public function size() { return $this->_m_size; }
    public function name() { return $this->_m_name; }
    public function _raw_contents() { return $this->_m__raw_contents; }
}

namespace \DoomWad;

class Sidedefs extends \Kaitai\Struct\Struct {
    public function __construct(\Kaitai\Struct\Stream $_io, \DoomWad\IndexEntry $_parent = null, \DoomWad $_root = null) {
        parent::__construct($_io, $_parent, $_root);
        $this->_read();
    }

    private function _read() {
        $this->_m_entries = [];
        $i = 0;
        while (!$this->_io->isEof()) {
            $this->_m_entries[] = new \DoomWad\Sidedef($this->_io, $this, $this->_root);
            $i++;
        }
    }
    protected $_m_entries;
    public function entries() { return $this->_m_entries; }
}

namespace \DoomWad;

class Blockmap extends \Kaitai\Struct\Struct {
    public function __construct(\Kaitai\Struct\Stream $_io, \DoomWad\IndexEntry $_parent = null, \DoomWad $_root = null) {
        parent::__construct($_io, $_parent, $_root);
        $this->_read();
    }

    private function _read() {
        $this->_m_originX = $this->_io->readS2le();
        $this->_m_originY = $this->_io->readS2le();
        $this->_m_numCols = $this->_io->readS2le();
        $this->_m_numRows = $this->_io->readS2le();
        $this->_m_linedefsInBlock = [];
        $n = ($this->numCols() * $this->numRows());
        for ($i = 0; $i < $n; $i++) {
            $this->_m_linedefsInBlock[] = new \DoomWad\Blockmap\Blocklist($this->_io, $this, $this->_root);
        }
    }
    protected $_m_originX;
    protected $_m_originY;
    protected $_m_numCols;
    protected $_m_numRows;
    protected $_m_linedefsInBlock;

    /**
     * Grid origin, X coord
     */
    public function originX() { return $this->_m_originX; }

    /**
     * Grid origin, Y coord
     */
    public function originY() { return $this->_m_originY; }

    /**
     * Number of columns
     */
    public function numCols() { return $this->_m_numCols; }

    /**
     * Number of rows
     */
    public function numRows() { return $this->_m_numRows; }

    /**
     * Lists of linedefs for every block
     */
    public function linedefsInBlock() { return $this->_m_linedefsInBlock; }
}

namespace \DoomWad\Blockmap;

class Blocklist extends \Kaitai\Struct\Struct {
    public function __construct(\Kaitai\Struct\Stream $_io, \DoomWad\Blockmap $_parent = null, \DoomWad $_root = null) {
        parent::__construct($_io, $_parent, $_root);
        $this->_read();
    }

    private function _read() {
        $this->_m_offset = $this->_io->readU2le();
    }
    protected $_m_linedefs;

    /**
     * List of linedefs found in this block
     */
    public function linedefs() {
        if ($this->_m_linedefs !== null)
            return $this->_m_linedefs;
        $_pos = $this->_io->pos();
        $this->_io->seek(($this->offset() * 2));
        $this->_m_linedefs = [];
        $i = 0;
        do {
            $_ = $this->_io->readS2le();
            $this->_m_linedefs[] = $_;
            $i++;
        } while (!($_ == -1));
        $this->_io->seek($_pos);
        return $this->_m_linedefs;
    }
    protected $_m_offset;

    /**
     * Offset to the list of linedefs
     */
    public function offset() { return $this->_m_offset; }
}