Tile map file

From Boktai Hacking Wiki

Tile maps store visual data for the GBA's background layers. Each tile map file contains:

  • The tile set used by this tile map file, as an array of standard 8x8 px 4 bpp tiles. Optional, if the tile map file does not contain tile set data, then they must be loaded by an external mechanism (generally a script). Tile maps for menus are usually storing the tile sets themselves, while normal overworld/dungeon maps depend on a global tile set.
  • An array of metatiles: Each metatile is 2x2 tiles in size. This serves as a compression mechanism.
  • An array of layers: Each tile map file may one or multiple different "layers". tile map files only contain a single layer, but menus can have one layer per page in the menu for example. All layers in a tile map file share the same tile set and metatile set.

Compression

Most tile map files are stored LZ77 (swi 0x11) compressed. When loading a tile map file, the game checks if the file starts with the ASCII characters "MP" (hex: 0x4d, 0x50). If it does, then the file is not compressed, and used by the game as-is. If it does not start with these characters, the game will LZ77 decompress it to EWRAM. Note that the first 4 bytes after decompression must be discarded (they will contain the uncompressed length of the file).

File format

struct TilemapHeader {
  /* offset 0x00 */ char magic[2];     // Always the two ASCII characters "MP"
  /* offset 0x02 */ u16 unknown_0x02;
  /* offset 0x04 */ u16 layerCount;    // Number of layers in this file
  /* offset 0x06 */ u16 tileCount;     // Number of tiles in this file's tileset (if it exists)
  /* offset 0x08 */ u16 metatileCount; // Number of metatiles in this file
  /* offset 0x0a */ u16 loadOffset;    // Offset into VRAM at which the tiles should be copied to, in tiles.

  // Byte offset from start of this struct to the layerDefinitions[] array
  /* offset 0x0c */ u32 offsetToLayerDefinitions;

  // Byte offset from start of this struct to the tileset[] array
  // If it's zero, then this file does not contain a tileset.
  /* offset 0x10 */ u32 offsetToTileset;

  // Byte offset from start of this struct to the metatiles[] array
  /* offset 0x14 */ u32 offsetToMetatiles;

  TilemapLayer layerDefintions[layerCount];

  // Only if offsetToTiles != 0. Array of 8x8 px 4 bpp GBA tiles to use.
  u8 tileset[][32];

  // Each metatile contains four GBA BG screen entries:
  // https://problemkaputt.de/gbatek-lcd-vram-bg-screen-data-format-bg-map.htm
  // This includes the tile number and the palette bits (TODO: Does it include the flip bits?).
  // For each metatile, element 0=top left, 1=top right, 2=bottom left, 3=bottom right.
  u16 metatiles[][4];

  // Each layer is an array of TilemapLayer.width * TilemapLayer.height metatile numbers,
  // in a specific order (see "Layer data order" section below).
  // Each individual u16 in this array references a metatile, which is 16x16 px in size.
  u16 layerData[];
};

struct TilemapLayer {
  /* offset 0x00 */ u16 width;  // Width of layer in metatiles
  /* offset 0x02 */ u16 height; // Height of layer in metatiles
  /* offset 0x04 */ u16 id;
  /* offset 0x06 */ u16 unknown_0x06;

  // Byte offset from start of **the TilemapHeader struct** to the layer data
  /* offset 0x08 */ u32 offsetToTilemap;
};

Layers can be loaded by engine call 0x7a27, either by the layer's index in the layerDefinition array, or by the TilemapLayer.id field.

Layer data order

The metatile data of each layer is stored in a particular order:

  1. First, the entire layer is divided into blocks of 16x16 metatiles (= 256x256 px).
  2. Each of those blocks is stored sequentially in the layer data, in row major order.
  3. Within each 16x16 block, the individual metatiles are also laid out in row major order.

This implies that the width and the height of each layer must be evenly divisible by 16 metatiles.

As an example, let's look at a layer that's 32x32 metatiles in size. There are 4 blocks in this layer. First, the top left quarter of the layer is stored in its entirety in the tile map file, followed by the top right quarter, followed by the bottom left quarter, and finally the bottom right quarter.

See also