Skip to content
Rena Kunisaki edited this page Jul 4, 2023 · 19 revisions

Maps are one of the most important and most complex subsystems in the game. They define the world that the player navigates.

Internally, the game sometimes refers to maps as "tracks", since this engine was originally developed for Diddy Kong Racing.

See Also

Layers

There are five layers of map grids, which maps are placed on to create a game world. The only way to move between layers is via warps or scripted events.

  • Layer -2: Deep underground; contains only the DarkIce Mines boss arena.
  • Layer -1: Underground; contains a few caves.
  • Layer 0: Surface; contains most maps.
  • Layer 1: Surface; contains Drakor boss arena and ThornTail shop.
  • Layer 2: Space; contains Arwing levels.

Since the grids do not contain any height information, the choice of layer for a map is arbitrary. Layers -1 and 1 are used to allow one map to be placed over/under another; layers -2 and 2 are most likely used to prevent the other layers from being too large.

Map IDs and Directories

Each map can be referred to by its Map ID or its Directory ID. The game uses these IDs in various places, which can be confusing. A table which gives the corresponding Map ID for each Directory ID is embedded in the executable.

The Directory ID is an index into a list of directory names. Several unused indices point to the name animtest or to names of directories that are missing or empty. This directory contains the map's assets.

In many cases, such as save files, instead of specifying a map ID, the game specifies a set of global coordinates and a map layer.

Coordinate Systems

Several coordinate systems are used by various parts of the game:

  • Global Coordinates: Used by objects in RAM, the player's saved location, etc. These specify an exact location within a map layer, and are usually paired with a layer number.
  • Global Grid Coordinates: Equal to Global Coordinates, but with X and Z each divided by 640 and truncated to integer. Y is often omitted.
  • Map Coordinates: Used by romlist entries.
  • Map Grid Coordinates: Equal to Map Coordinates, but with X and Z each divided by 640 and truncated to integer.

Assets

Each map's directory contains the following files: (Note the inconsistent use of case)

  • ANIM.BIN, ANIM.TAB: Animation data
  • ANIMCURV.bin, ANIMCURV.tab: Animation curves
  • MODELIND.bin: A table mapping model IDs to indices into MODELS.tab
  • MODELS.bin, MODELS.tab: Character and object models
  • OBJSEQ.bin, OBJSEQ.tab: Object animation sequence data
  • OBJSEQ2C.tab: Assigns animation curves to object sequences
  • TEX0.bin, TEX0.tab: Texture graphics (primarily for the map geometry)
  • TEX1.bin, TEX1.tab: Texture graphics (primarily for character models)
  • VOXMAP.bin, VOXMAP.tab: Voxel data, relates to camera, possibly unused

Each map contains a copy of every model, texture, and animation it uses. This improves loading times by reducing the amount of disc seeking needed.

Each map directory also contains at least one modXX.zlb.bin file and corresponding modXX.tab, where XX is a number. These contain the map block models.

Several map directories also have unused tab files. There are also several modXX.zlb.bin and modXX.tab files in the disc root, which seem to be unused. In the Kiosk Demo version, there are also modXX.bin and modXX.lzo files.

MAPS.bin

This file (and MAPS.tab) contain information about each map's layout.

MAPS.tab contains one entry for each map, indexed by map ID (not directory ID):

Offset Type Name Description
000000 s32 infoOffset Offset of map info
000004 s32 blockTable Offset of map block list
000008 s32 rects1 rects that somehow define visible regions
00000C s32 rects2 more rects
000010 s32 rects3 more rects
000014 s32 rects4 more rects
000018 s32 listSize a FACEFEED header which gives the size to allocate for the romlist

Each field is an offset into MAPS.bin.

  • In the kiosk demo version, the romlist is in MAPS.bin following the FACEFEED header. In the final version, it's moved to an external file, with only the FACEFEED header left in MAPS.bin. The header's uncompressed size tells the game how much memory to allocate before it loads the file.
  • The final version will still load a romlist from MAPS.bin if the external file is missing, but this is never the case. (TODO: check if any old romlists are left here.)

infoOffset

Location of a structure defining the basic map geometry:

Offset Type Name Description
000000 u16 sizeX Number of columns
000002 u16 sizeZ Number of rows
000004 u16 originX Which column is the origin
000006 u16 originZ Which row is the origin
000008 u32 ?
00000C u32[4] ? possibly related to rects
00001C s16 nBlocks XXX more info
00001E u16 ? possibly flags or padding

The origin is used when placing this map on the global grid. For example if a map is at global coordinates (100,100), and its origin is (2,3), then its first block will be placed at global coordinates (98,97). There does not necessarily need to be a block at the origin, and the origin does not need to be within the specified rectangle.

Block List

Each map is made up of one or more blocks, each of which has its own model. Each block is 640x640 units on the X and Z axes, and variable in height.

MAPS.bin contains a list of blocks, which is sizeX * sizeZ u32 values, each specifying one block:

unk1 =  val >> 31; //probably unused
mod  = (val >> 23) & 0x00FF;
sub  = (val >> 17) & 0x003F;
unk2 =  val        & 0x01FF;

If mod is 0xFF, there is no block here. Otherwise, if mod is >= 5, add 1 to mod. mod is then used to identify the file the block is in: /%s/mod%d.bin and /%s/mod%d.tab, where %s is the map's directory and %d is the mod number. Each modXX file contains the block models, where sub specifies which model to use.

The file TRKBLK.tab specifies an offset for each map. This file is an array of u16 indexed by the map's directory ID. The offset found here plus the block's sub value gives the index into the tab file to use for this block.

Internally, the game refers to the files BLOCKS.bin and BLOCKS.tab. These files do not exist; the code that would read them instead reads the modXX files.

A map's dimensions appear to be limited to a total area of 512 blocks (regardless how many blocks are actually present); it's not known how the Drakor boss map is able to exceed this. (see 0x80059248)

Block Model Data

The tab file works like other tab files: it contains an array of u32 values in which the highest 8 bits are flags (meaning currently unknown) and the rest are an offset into the corresponding bin file.

At the resulting offset in the bin file is a ZLB archive containing the block model, which has a similar format to character models:

Of Type Name Description
00 u32 unused00 set from 80060b90 - XXX confirm unused
04 u16 flags_0x4 40=need init hits? 1=toggled on render
06 ?
08 s32 length file size
0C Mtx43 mtx unsure; seems unused in file
3C ?
4C pointer GCpolygons invisible, used for hit detection; can be null
50 pointer polygonGroups related to hit detection; can be null
54 u32* textures texture IDs
58 vec3s* vertexPositions vertex coordinates
5C u16* vertexColors vertex colors (RGBA4444)
60 vec2s* vertexTexCoords vertex texture coords
64 Shader* shaders defines how to render polygons
68 DisplayListPtr* displayLists native display lists, referenced by render streams
6C LineHit* linehits
70 HitsBinEntry* hits data from HITS.bin, size from HITS.tab; 0 in file
74 ?32 ? set to 0 in initHits
78 BitStream* renderInstrsMain normal geometry
7C BitStream* renderInstrsTransp transparent geometry, glow effects
80 BitStream* renderInstrsWater reflective geometry, water
84 u16 nRenderInstrsMain stream size in bytes
86 u16 nRenderInstrsTransp
88 u16 nRenderInstrsWater
8A s16 yMin related to bounds
8C s16 yMax
8E s16 yOffset must be added to vertex Y positions
90 u16 nVtxs number of vertex positions
92 u16 nUnk guessed
94 u16 nColors number of vertex colors
96 u16 nTexCoords number of texture coords
98 u16 nPolygons number of GCpolygons
9A u16 nPolyGroups number of polygon groups
9C u16 nHits number of HITS.bin entries (0 in file)
9E ?16 hitField_9e set to 0 in initHits
A0 u8 nTextures number of textures
A1 u8 nDlists number of display lists
A2 u8 nShaders number of shaders
A3 u8 probably nSomething or padding
A4 char[11] name size guessed from OBJECTS.bin; not used; eg "mod6.12"
AF ? total struct size = 0xB8

(some names inferred from debug messages)

The three BitStream fields point to bit-packed render instructions that render the block's model:

Render Streams

Each stream is a series of bit-packed opcodes and parameters (high bits first). Decoding is as follows: Read 4-bit opcode, execute, repeat.

Opcodes:

  • 0: Unused, but does the same as 4
  • 1: Select a texture and shader
    • Read index (6 bits)
    • Set the current shader to block->shaders[index]
  • 2: Call a display list
    • Read index (8 bits)
    • If the current shader doesn't have the "hidden" flag set (or there is no current shader), call block->displayLists[index]
  • 3: Change the vertex format for VAT 5 (map blocks) or 6 (character models)
    • 1 bit: POS format (0:INDEX8, 1:INDEX16)
    • Character models have 1 bit for NRM format here; blocks do not.
    • if (curShader.attrFlags & 2) != 0:
      • 1 bit: COL0 size (0:INDEX8, 1:INDEX16)
      • else, this field is not present (0 bits)
    • 1 bit: TEX size (0:INDEX8, 1:INDEX16); applies to all enabled texture slots
      • If no shader, only TEX0 is used; otherwise, number of slots = curShader.nLayers
      • disabled slots are set to format 0 (NONE)
      • Character models always have shaders
  • 4: Read matrix data
    • 4 bits: number of matrices
    • 8 bits per matrix: index into matrix data
    • Unsure where the matrix data is, or where this data is written to (presumably somewhere in XF memory)
    • For map blocks, the matrix indices are read, but not used
  • 5: End of stream
  • Else: unused, probably behaves the same as others above

Each block is a separate model, but textures are all read from the map's directory and TEXPRE.bin.

Although a block's size is fixed at 640 units on the X and Z axes, there is nothing preventing a block from having geometry outside of this range; however, since only a small number of blocks are rendered at one time, geometry far beyond this range might spontaneously appear and disappear as the player moves around.

Grids

Individual Maps

The blocks listed in MAPS.bin are arranged into a rectangular grid, with dimensions specified by the list header. This defines how each block is placed relative to the map's origin.

The list header specifies which grid cell is the origin. Specifically, the coordinates 0,0,0 relative to this cell are the origin point. Many maps have the origin in a cell that does not contain a block, and a few have it outside the rectangle entirely.

The origin point is used as a reference for placing objects, as well as placing the map's blocks on the global map grid.

Many maps' rectangles are much larger than necessary, with the excess regions filled with empty blocks. Many also contain block IDs that aren't found anywhere on the game disc.

Global Map Grid

The file globalma.bin places each map on one of five "layer" grids, assigning it an absolute position in the game world. These grids define how maps are placed relative to each other, and are used to translate global coordinates to map coordinates.

This file is an array of structs, which ends when map < 0.

Offset Type Name Note
000000 s16 x Global grid X coordinate
000002 s16 z Global grid Z coordinate
000004 s16 layer Range -2 to 2, cast to s8
000006 s16 map Map ID found here; if < 0, marks end of list.
000008 s16[2] link Linked map IDs

The specified map is placed on the specified layer grid such that its origin falls at the specified coordinates. Cells with no block are ignored, allowing maps to overlap.

The two linked map IDs are used to improve load times. If an entry is not 0xFFFF, that map's romlist will be loaded when the player is in this map. eg the objects in swapholbot are loaded when the player is in swaphol and vice versa. (Curiously, dragrock has itself listed here.)

globalmap.bin is an older, unused version of this file.

Collision

Collision with map geometry is defined by a secondary mesh (contained in the block model) plus a set of lines (contained in HITS.bin). The latter is mostly only used for jumping up/down from ledges; deleting the file entirely has little impact on the game.

Collision Mesh

The collision mesh uses the same vertices as the visible geometry (but not necessarily connected in the same way). It is made entirely of triangles.

The GCpolygons field in the block header points to an array of polygon definitions, each of which defines one triangle. The nPolygons field gives the length of this array.

Offs Type Name Note
0000 u16 v0 Vertex 0 (index into vertexPositions array)
0002 u16 v1 Vertex 1
0004 u16 v2 Vertex 2
0006 u16 subBlocks Which sub-blocks this polygon covers

The actual triangle's coordinates are the raw vertex coordinates divided by 8. (This is hardcoded, not derived from the GX POSSHFT value.)

The name GCpolygons is extracted from default.dol. GC here doesn't seem to stand for GameCube; perhaps Geometry Collision?

Sub-Blocks

The subBlocks field is used to optimize collision detection by not checking against far-away polygons. The high byte represents the Z axis, and the low byte represents the X axis.

For the X axis: divide the block into 8 strips (from Z=0 to Z=640, each 80 units wide); each bit is 1 if any part of the polygon overlaps this strip. The lowest bit corresponds to X=(0..80), and the highest corresponds to X=(560..640). The Z axis works the same as the X axis.

Polygon Groups

The polygonGroups field points to an array of polygon group definitions, with length given in the nPolyGroups field.

Offs Type Name Note
0000 u16 firstPolygon Index into GCpolygons[]
0002 s16 x1 Group boundary box
0004 s16 x2
0006 s16 y1
0008 s16 y2
000A s16 z1
000C s16 z2
000E ?
000F ?
0010 u8 id Game accesses these fields as u32
0011 u8 surfaceType
0012 u16 flags

The collision mesh appears to have been automatically generated from the visible geometry, since it includes areas that are completely unreachable, polygons that are only meant to be glow effects, etc.

Surface Types

These define how objects interact with the polygon: what sound effects play when walking on or striking it; whether it hurts to touch; etc.

ID Description
00 Generic; eg underwater ground, out-of-bounds trees
01 Grass
02 Sand
03 Snow
08 Instant Death (only used in DragRockBot)
09 Ice Platform? (used under cannon in Ice Mountain)
0D Ice (slippery)
0E Water (makes splash effects)
10 Gold (used in CloudRunner mine)
12 Rough stone
13 Magic Cave walls and floors
18 Wood
19 Stone and other hard surfaces (includes hot stone in DarkIce Mines)
1A Lava (sets you on fire)
1B Ice walls
1D Conveyor belts
21 Unknown; seen in Arwing levels
22 Metal

HITS.bin

Defines invisible planes that characters interact with. Examples of their function include:

  • Defines the regions of ladders and climbable walls
  • Defines how the player behaves when walking off a platform (falling off, jumping off, or being blocked)
  • Defines whether the player can jump up to a ledge
  • Creates "invisible walls"; eg Arwing landing spots have an outline of the Arwing to prevent the player from walking through it. (Unclear why this method is used as opposed to actual invisible walls.)
  • Creates barriers that block enemies, the camera, etc, but not the player
  • Defines the boundaries where you can push a block

Each entry is in the format:

Offs Type Name Description
0000 s16 x1 Position within the map block
0002 s16 x2
0004 s16 y1
0006 s16 y2
0008 s16 z1
000A s16 z2
000C u8[2] height
000E u8 flags
000F u8 type See list below
0010 ?16
0012 ?16

The height values are interpreted depending on the highest bit of the flags byte. If zero, the bytes are two different heights (y1 and y2). If one, both bytes are read as a single s16 value and applied to both ends of the line.

The result is a plane or triangle with corners: (x1,y1,z1), (x1,y1+height1,z1), (x2,y2+height2,z2), (x2,y2,z2). The block's Y offset is not applied to these coordinates.

Types

These define how characters interact with the plane. The highest two bits of this value have some other meaning.

ID Description
01 General barriers
02 Ledge which, if you walk off, you automatically grab the edge
03 Climbable wall
04 Ledge you can jump off
05 Ledge you can jump off (unsure how this differs; used in Animtest)
06 Ledge you can climb/jump up to (automatic by height)
0A Ladder
0D Barrier for pushing blocks
0E Tunnel you have to crawl through
10 Presumably lets you climb out of water
11 Unknown; the game clears the highest 2 bits and changes the type to 0x13

Files

HITS.tab gives the offset of each entry, indexed by the block's mod# (plus the offset in TRKBLK.tab) plus its sub#. The number of entries is determined by subtracting the tab entry from the following entry.

Oddities

Several maps have leftover entries out of bounds or in the sky, which form shapes suggesting there used to be geometry here. Some of these possibly could be moved in-bounds at some point?

(TODO: check if any of these actually belong to different maps and are incorrectly being shown)

A few maps also seem to have nonsensical entries at the beginning of some blocks; for example, ThornTail Hollow has some lines floating in the sky and deep underground where nothing can reach them. As best I can tell, the game does, in fact, read these lines (i.e. they don't appear to be an artifact of a bug in my own code); whether they're actually used, or just included by mistake, is unknown.

Display List Bounding Boxes

Each display list pointer (referenced by the displayLists field in the block header) includes a bounding box definition:

Offs Type Name Description
0000 void* list Points to list data (raw GX commands)
0004 u16 size List data size in bytes
0006 vec3s[2] bbox Bounding box
0012 ?
0013 u8 shaderId
0014 u16 specialBitAddr offset relating to shaders
0016 u16 offset of some sort
0018 u32 always 07 00 00 00? (Leftover from N64 segmented addresses?)

The bounding box is used to determine whether the list should be rendered. (More info needed here...)

Misc Files

MAPINFO.bin

This file contains the name, type, and some unknown parameters for each map. This info is mostly unused by the game; most fields are left over from development.

Offset Type Name Note
000000 char[28] name Null-padded but not terminated
00001C u8 type Map type
00001D u8 ? Always 6
00001E u16 objType Unused
  • name: Would be displayed in a debug menu; unused in final version.
  • type: Type of map:
    • 0: Normal map
    • 1: Normal sub-map (unsure what's different about this)
    • 2: Special map (unused, deletes all objects when loaded)
    • 3: Special sub-map (unused, deletes all objects when loaded)
    • 4: Special map (Arwing levels, title screen, world map)
    • Only types 0, 1, and 4 are used, with the exception of one unused map which uses type 3.
  • objType: Not used. In previous versions, was an ObjDef ID to use as the player object instead of Sabre/Krystal. Only valid for maps of type 1.

VOXMAP

Each map directory contains VOXMAP.bin and VOXMAP.tab. Their purpose is unknown. Replacing the ones in swaphol with those of warlock, or removing them entirely, doesn't have any noticeable effect.

Clone this wiki locally