How the Lighthouse Decompilation Project Is Structured: A Deep Dive into the Banjo-Kazooie Source Code

The Lighthouse repository follows a layered architecture with CMake-driven builds, global headers in include/, low-level N64 emulation via libultralib, game logic in src/, and YAML-based asset pipelines.

The Lighthouse decompilation project is a community effort to recreate Banjo-Kazooie as clean, buildable C code that runs on modern platforms. Understanding how the Lighthouse decompilation project structure works is essential for contributors aiming to extend the engine or debug gameplay systems. This guide walks through each architectural layer, from build scripts to game logic, with concrete examples from the source tree.


Root Directory and CMake Build System

The repository root contains the orchestration layer. The top-level CMakeLists.txt defines the entire build process: it pulls in the libultraship submodule, sets platform-specific compiler flags, and registers custom CMake targets.

Two targets are critical for first-time setup:

  • ExtractAssets — runs the Torch-based extractor to produce bk.o2r from a legal ROM
  • GeneratePortO2R — packages port-specific textures and shaders into lighthouse.o2r

These targets are invoked automatically via the commands documented in docs/BUILDING.md. The root also houses CI configuration (.github/workflows/), documentation (docs/), and the README.md with quick-start instructions.


Global Headers in include/

The include/ directory is the single source of truth for data structures that mirror the original N64 binary layout. Every source file includes these headers, making them the backbone of the decompilation's memory model.

Key files:

  • structs.h — defines Object, Level, Player, and other core structs passed throughout the engine
  • variables.h — holds global state such as gCurrentLevel and gPlayerHealth

Because these headers are universally included, changes here propagate across the entire codebase. This design matches the original game's reliance on fixed-memory layouts for its actors and world state.


Low-Level N64 Emulation via lib/ultralib/

Rather than shipping Nintendo's original proprietary SDK, the project depends on libultraship, a community-maintained submodule hosted in lib/ultralib/. This library provides:

  • RSP/CPU primitives — vector mathematics, DMA transfers, and inter-processor messaging
  • Audio synthesis — sample playback, reverb, and sequencer logic
  • Utility macros — assertions via ultra_assert.h and platform-agnostic wrappers

Higher-level game code calls into this layer exactly as the original binary did, preserving timing-sensitive behavior. The ultra_assert.h header, for example, is referenced across the codebase for runtime consistency checks.


Game Logic in src/

The src/ directory contains the decompiled game implementation, organized to reflect the original module boundaries.

The SM/ Subdirectory

Despite its name, SM/ holds the core Banjo-Kazooie gameplay systems. Key files include:

These files implement character movement, collision response, and object interaction. They rely on the global structs from include/ and call into libultralib for hardware-abstraction needs.

Cut-Scene System in cutscenes/

Each scripted sequence lives in its own module:

The cut-scene manager registers callbacks that pause normal gameplay, play the sequence, then restore control.

Development and Reference Subdirectories

  • emptyLvl/ — a minimal test level for rapid iteration without loading the full game world
  • unused/ — legacy code retained for reference; excluded from the final binary

Example: Level Initialization from quarrie.c

This pattern appears throughout src/SM/ for every playable area:

#include "include/structs.h"
#include "include/variables.h"

/* Called once ROM assets are unpacked */
void init_quarrie(void) {
    /* Set player start position */
    gPlayer->position = (Vec3){ .x = 100.0f, .y = 0.0f, .z = -250.0f };
    
    /* Stream level geometry via DMA-style loader */
    load_level_geometry("quarrie_lvl.bin");
    
    /* Register per-frame update hook */
    register_update_callback(quarrie_update);
}

The function composes global state manipulation (gPlayer), asset loading (load_level_geometry), and engine registration (register_update_callback) — the three pillars of level initialization in this architecture.


Example: Cut-Scene Trigger from cutscene_trigger.c

#include "include/structs.h"

/* Invoked on player-object collision */
void trigger_cutscene(Object *obj) {
    if (obj->state == OBJ_STATE_ACTIVE) {
        /* Queue cut-scene by ID; manager handles the rest */
        start_cutscene(CUTSCENE_SPARKLE);
    }
}

The trigger inspects object state, then delegates to the centralized cut-scene system. This separation keeps gameplay code focused on conditions while the cutscenes/ modules handle presentation.


Asset Pipelines in assets/ and docs/

Runtime content is defined declaratively in YAML files under assets/yaml/us/rev1/. The build tooling (documented as "retro" in the README) consumes these definitions to generate .o2r and .otr archives.

  • soundfont.yaml — describes instrument banks and sample mappings
  • Texture and model definitions — specify source files and compression settings

The docs/BUILDING.md file contains the exact command sequences for generating these archives, which the engine loads at startup. This pipeline decouples asset authoring from engine compilation, enabling iterative content work without full rebuilds.


Continuous Integration and Distribution

GitHub Actions workflows in .github/workflows/ automate three stages on every commit:

  1. Asset extraction — runs ExtractAssets on a runner configured with the extractor (ROM not included in repository)
  2. Multi-platform builds — compiles for Windows (MSVC), macOS (Xcode), and Linux (GCC/Clang) via CMake generators
  3. Packaging — produces zip archives, AppImages, and DMG bundles for end-user testing

This pipeline ensures reproducible binaries and catches build regressions before they reach contributors.


Summary

  • CMake-driven builds — CMakeLists.txt orchestrates extraction, compilation, and packaging through documented targets
  • Centralized headers — include/structs.h and include/variables.h provide the memory model shared across all modules
  • Hardware abstraction — lib/ultralib/ (libultraship) implements N64-specific primitives without proprietary code
  • Modular game logic — src/SM/ for gameplay, src/cutscenes/ for scripted sequences, with clear separation of concerns
  • Declarative assets — YAML definitions in assets/ feed the build pipeline for runtime archives
  • Automated quality assurance — CI workflows validate every commit across platforms and package for distribution

Frequently Asked Questions

What programming language is the Lighthouse decompilation written in?

The project is written in C, matching the original Banjo-Kazooie development approach. Headers use standard C syntax with preprocessor macros for platform adaptability. The build system is CMake, and tooling scripts use Python for asset extraction.

How does Lighthouse handle original Nintendo 64 hardware calls?

All low-level hardware access routes through libultraship, a community replacement for Nintendo's proprietary ultralib. This submodule implements RSP vector operations, audio synthesis, and memory management without including copyrighted code. Game logic in src/ calls these APIs exactly as the original binary did.

Where is the actual game code located in the repository?

The decompiled gameplay implementation lives in src/, with subdirectories organized by function: src/SM/ for core mechanics, src/cutscenes/ for scripted sequences, src/emptyLvl/ for debugging, and src/unused/ for reference material. Each .c file typically corresponds to a specific system or level from the original game.

What files do I need to edit to add a new level?

Start with three locations: create level initialization code in src/SM/ (following the quarrie.c pattern), add any required struct definitions to include/structs.h, and define assets in assets/yaml/us/rev1/ if the level needs new textures or audio. Update CMakeLists.txt if you introduce new source files that require separate compilation units.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →