An overlay is a chunk of compiled code that lives in ROM separately from the main binary. It can be loaded into RAM on demand, executed, and then unloaded to free memory. This is how map-specific logic and battle actors are swapped in without keeping everything resident.
Comparison with vanilla
Vanilla did not have this abstraction and instead copied code directly from ROM into RAM with [dma_copy()]. Vanilla overlays were also linked statically to fixed addresses rather than dynamically. Some other N64 games such as Ocarina of Time do use dynamic linking.
Usage
// Load the overlay src/battle/actor/my_actor
Overlay* ovl = ovl_load("my_actor", OVL_ACTOR);
// Run an exported function
void (*fn)(void) = ovl_import(ovl, "my_function");
fn();
// Use an exported global variable
int* var = ovl_import(ovl, "my_variable");
*var += 5;
// Unload the overlay when you're done with it (this frees memory)
ovl_unload(ovl);Source files
Each [OverlayType] has a source glob. Files or directories matching the glob are each built as an overlay, with the stem of the match as the overlay name. If the match is a directory, all C/C++ files inside it (excluding *.inc.c and *.inc.cpp) are compiled and linked together. If the match is a single file, that file alone is the overlay.
For example, src/world/area_kmr/kmr_02/ is a directory matching src/world/area/*/*/, so it becomes an [OVL_MAP] overlay named kmr_02. src/battle/actor/goomba.c matches src/battle/actor/*, so it becomes an [OVL_ACTOR] overlay named goomba.
Source files are sourced from both src and assets subdirectories.
Importing and exporting
Symbols marked with the export macro are accessible via [ovl_import()]. For example:
// Export a function
export void my_function(void) { /* ... */ }
// Export a global variable
export int foo = 10;Overlays are singletons
Loading the same name/type pair multiple times returns the same overlay:
Overlay* first = ovl_load("my_overlay", OVL_ACTOR);
Overlay* second = ovl_load("my_overlay", OVL_ACTOR);
ASSERT(first == second);This means that variables are shared if a single overlay is loaded multiple times.
Dynamic linking
Most overlay types are dynamically linked — they can be loaded to any address, allowing many of that type to be loaded simultaneously.
The exception is [OVL_MAP], which is always loaded to a fixed address. Therefore only one map overlay can be loaded at a time. Loading another map whilst one is already loaded is undefined behaviour; unload it first.
Adding a new overlay type
To add a new dynamically linked overlay type:
- Add a new variant to [OverlayType] before
OVL_NUM_TYPES. - Add the glob to
overlay_typesinconfigure.py.