Blaster Master:Data Structures
Each level and each ROM bank containing level data has a simple and consistent data format. Knowing only the number of levels in a ROM bank, it is possible to identify the location and type of all data within a bank by following the pointers.
This document is a formal definition of the ROM's data formats with the intent of documenting the data structure unambiguously, if not a bit difficult to understand.
Simple Data Formats
- Pointer: A two byte pointer that always references data in the same bank. Level data is always paged in at 0x8000, so the pointer value will always be between 0x8000 and 0xBFFF. Pointers are little endian (0xABCD would be stored in the ROM as CD AB). To convert pointer addresses to ROM offsets, simply subtract 0x8000 and add the base offset of the ROM bank.
- Palette: Each level uses the same sprite palettes. Each level has it's own background palette. Each level palette is 16 bytes, four palettes of four colors each. The "background color" for each four-color palette is disregarded and will always be black.
Each bank begins with a couple of pointers for each of the levels stored in that bank. Each level's data follows.
Level Data Pointers[*] Level Pointers Level Data[*] Level Data
[*] Indicates a variable number of instances of said data type. Each Bank begins with a number of Level Data Pointers. The Level Data Pointers data structure is four bytes, composed of two pointers, as defined below. The Level Data Pointers are followed by Level Data for each level.
Level Data is not a rigid data structure, but rather a number of data structures referenced by a set of pointers. See below.
Level Data Pointers
Pointer Level Map Data Pointer Level Scroll Data
Level Map Data points to the Level Map Pointers for a given level. Level Scroll Data points to the level's scroll map.
Each level is stored in a single contiguous area of ROM memory, though the format does not strictly require this. In a hack different types of data for each level could be placed arbitrarily (within a single bank) as long as the pointers correctly identify the location of the data.
Each level starts with a number of pointers to each type of data for a level's map. This table of pointers is identified at the beginning of a bank via Level Map Data, as defined in the Bank Format section.
Level Map Data
Pointer Bg Palette Pointer Combo Attribute Table Pointer Combo Definiton Table Pointer Structure Definition Table Pointer Block Definition Table Pointer Map Data
The level map is constructed from a series of successive building blocks. Combos are 16 pixels wide and tall and are constructed from four patterns. Structures are 32 pixels wide and tall, constructed from four combos. Blocks are 64 pixels wide and tall, constructed from four structures. That is to say that blocks are made up of structures, which are, in turn, made up of combos, which are made up of patterns. The level map is composed of blocks.
The terms block, structure, and combo are somewhat arbitrary and chosen because they are commonly used for similar data structures in ROM documentation. Blocks, structures, and combos may also be referred to as blocks, sub-blocks, and ultra-sub-blocks (or blocks, SB, and USB) in other documentation.
The combo definition table is a list of Combo Definitions, the number of which varies between levels. The number of Combo Definitions present for a given level can be calculated by subtracting the Combo Definition Table pointer from the Structure Definition Table pointer and dividing the result by four (this is based on the assumption that the Structure Definition Table immediately follows the Combo Definition Table, as in the original unaltered ROM).
Each combo is composed of four patterns, specified by the Combo Definition.
Byte Top Left Pattern Index Byte Top Right Pattern Index Byte Bottom Left Pattern Index Byte Bottom Right Pattern Index
Each combo definition is complemented by combo attribute data. Combo attribute data is stored separately form the Combo Definition Table in a Combo Attribute Table. Combo attribute data is one byte per combo, where the bits have the following meaning (least significant bit first).
Bit 0-1 Palette Used (from 0 to 3). Bit 2 Used in conjunction with bit 3. Indicates a door is an entrance to an overhead area. Bit 3 Door (to another level or to/from overhead section). Bit 4 Damage, used for lava, spikes, etc. Bit 5 Ladder. Combine with Bit 7 to indicate the top of a ladder or bit 6 to indicate ladder is under water. Bit 6 Water. Bit 7 Solid.
Breakable combos are created by specifying both bits 7 and 6 (solid and water). The doorways that connect two scrolling regions are specified with both bits 7 and 8 (solid and door). These physics are for the tank levels. The meanings of physics bits are different in overhead levels.
A structure is a composite of four combos. The structures are defined in a manner similar to a combo. A structure does not define any physics or other attributes of its own. Instead, simply it inherits the characteristics of its combos.
The number of Structure Definitions present for a given level can be calculated by subtracting the Structure Definition Table pointer from the Block Definition Table pointer and dividing the result by four (this is based on the assumption that the Block Definition Table immediately follows the Structure Definition Table, as in the original unaltered ROM).
Byte Top Left Combo Index Byte Top Right Combo Index Byte Bottom Left Combo Index Byte Bottom Right Combo Index
A block is a composite of four structures. The blocks are also are defined in a manner similar to a combo. Like a structure, the characteristics of a block are simply those inherited from it's constituents.
The number of Block Definitions present for a given level can be calculated by subtracting the Block Definition Table pointer from the Level Map pointer and dividing the result by four (this is based on the assumption that the Level Map immediately follows the Block Definition Table, as in the original unaltered ROM).
Byte Top Left Structure Index Byte Top Right Structure Index Byte Bottom Left Structure Index Byte Bottom Right Structure Index
The level map is constructed from blocks. The map is defined as a grid of blocks 32 wide and tall.
Byte[0x20, 0x20] Block Indecies
The data pertaining to scrolling is defined elsewhere. The map wraps around seamlessly during game-play.
Level Scroll Data
Level scroll data works by defining borders where the game will stop scrolling, creating rectangular scrolling regions. For the purposes of scrolling, the map is divided into a grid with "screen sized" (256x256 pixel) cells. Each grid cell can have a scroll boundary on any side (top, bottom, left, or right).
The player can travel from one scrolling region to another via a connecting doorway. Non-rectangular scrolling regions, unless very carefully constructed, will result in graphical glitches. Allowing a player to freely move through a scroll boundary without the use of a connecting doorway will also cause graphical glitches.
Bit[8, 8] Vertical scroll flags Bit[8, 8] Horizontal scroll flags
The scroll map is 16 bytes. Scroll data is a bi-planar array of bits. Each byte specifies scroll data for one row of screens, most significant byte first (left-to-right on the map corresponds to high-to-low bits).
The first eight bytes specify 64 vertical scrolling flags, one for each cell of the hypothetical scrolling grid, and the second eight bytes specify horizontal scrolling flags. A scroll border is indicated by a set bit, which indicates scrolling would stop at the top or left of the screen the data corresponds to. For example, if bit 6 of the tenth byte is set, then the player can't scroll between the first and second screens in the second row of the map. In other words, this would indicate there is a scroll boundary on the left edge of map cell (1, 1).
Scroll Grid Offset
The level scrolling grid is offset by 32 pixels to the right and 32 pixels down from the top-left corner of the map. This means that the scroll boundaries all fall in the center of a block. Each block on the edge of a screen in the scrolling grid is shared with an adjacent screen.