# How Romhacks Are Extracted and Loaded as Mods in Lighthouse

> Discover how Lighthouse extracts romhacks into slim overlay mods. Learn about the multi-stage pipeline, mod placement, and enabling single romhack overlays with a game restart.

- Repository: [Harbour Masters/Lighthouse](https://github.com/HarbourMasters/Lighthouse)
- Tags: how-to-guide
- Published: 2026-08-04

---

**Lighthouse converts romhacks into *.o2r* overlay files through a multi-stage extraction pipeline that generates slim mod overlays, places them in `mods/~romhacks/`, enables exactly one romhack at a time, and requires a restart to apply the modified `aGameConfig`.**

Romhacking in [HarbourMasters/Lighthouse](https://github.com/HarbourMasters/Lighthouse)—the PC port of Banjo-Kazooie—works differently than traditional patching. Instead of distributing modified ROM files, Lighthouse treats romhacks as **specialized mod overlays** that override the base game's configuration blob. This article traces the complete extraction and loading pipeline from the UI button press through to the exclusive enablement mechanism.

---

## The Romhack-as-Mod Architecture

A Lighthouse romhack is fundamentally an ***.o2r* overlay** containing a modified `aGameConfig`—the serialized game configuration that dictates level data, object behaviors, and other core parameters. Unlike standard mods that add assets alongside the base game, romhack overlays replace critical game state.

This design offers two advantages:

- **Isolation**: Each romhack lives in its own overlay file, preventing file conflicts.
- **Exclusivity**: The engine enforces that exactly one romhack loads at any time, avoiding configuration collisions.

---

## User-Initiated Extraction Flow

### Step 1: UI Registration and ROM Selection

The entry point resides in [`src/port/UI/LighthouseModMenuWindow.cpp`](https://github.com/HarbourMasters/Lighthouse/blob/main/src/port/UI/LighthouseModMenuWindow.cpp). The *"Generate Romhack from ROM"* button registers with Lighthouse's widget system at lines 605–622:

```cpp
// LighthouseModMenuWindow.cpp (lines 605–622)
generateRomhackWidget = { 
    .name = "Generate Romhack from ROM", 
    .type = WidgetType::WIDGET_BUTTON 
};
generateRomhackWidget.Callback([](WidgetInfo&) {
    LighthouseGui::RegisterPopup(
        "Generate Romhack from ROM",
        "Select a romhack ROM to extract as a mod overlay. Torch will generate a slim mod o2r in your mods/~romhacks/ folder...",
        "Select ROM", "Cancel",
        []() { RequestInlineModExtraction(); }, nullptr);
});
LighthouseGui::mLighthouseMenu->AddSearchWidget({ 
    generateRomhackWidget, "Settings", "Romhack Menu", "Top", 
    "generate romhack rom extract overlay" 
});

```

When confirmed, the popup callback invokes `RequestInlineModExtraction()` (lines 714–718), which opens a file picker for the source N64 ROM.

### Step 2: Launching the Extraction Worker

`RequestInlineModExtraction()` delegates to `StartInlineRomExtraction(false)`, creating a `GameExtractor` instance and spawning a detached worker thread:

```cpp
// LighthouseModMenuWindow.cpp (lines 889–898)
static void BeginInlineExtraction(std::shared_ptr<GameExtractor> extractor, bool langPack) {
    sInlineFile = extractor->GetRomPath();
    sInlineExtracting = true;
    std::thread([ex = std::move(extractor)]() mutable {
        const bool ok = ex->GenerateOTR(sInlineCount, sInlineTotal, "bk");
        sInlineResult = ok ? 1 : 2;
        sInlineExtracting = false;
    }).detach();
}

```

The `GameExtractor::GenerateOTR()` method—declared in [`src/port/Extractor/GameExtractor.h`](https://github.com/HarbourMasters/Lighthouse/blob/main/src/port/Extractor/GameExtractor.h)—performs the actual heavy lifting:

1. Reads and validates the N64 ROM header
2. Parses assets using the Torch extraction pipeline
3. Generates a **slim overlay** (*.o2r*) containing base assets plus the romhack's `aGameConfig`

Progress propagates through atomic counters:
- `sInlineCount` — assets processed
- `sInlineTotal` — total assets to process  
- `sPhase` — current extraction phase
- `sInlineResult` — completion status (`0`=running, `1`=success, `2`=failure)

---

## Post-Extraction Processing and Overlay Placement

### Step 3: Moving the Generated Overlay

`DrawInlineModExtraction()` polls `sInlineResult` each frame. On success (`result == 1`), the code relocates the overlay to the romhacks directory (lines 441–455):

```cpp
// LighthouseModMenuWindow.cpp (post-extraction handling)
if (result == 1) {
    std::filesystem::path produced(GameExtractor::sLastOutputPath);
    std::filesystem::path romhacksDir = 
        std::filesystem::path(Ship::Context::GetPathRelativeToAppDirectory("mods")) 
        / ROMHACKS_DIR;  // "~romhacks"
    std::filesystem::path dest = romhacksDir / produced.filename();
    
    std::filesystem::create_directories(romhacksDir);
    std::filesystem::rename(produced, dest);
    
    SetSoleEnabledRomhack(dest.stem().string());  // enable exclusively
    // ... "Mod Installed" popup triggers GameEngine::RequestRelaunch()
}

```

The `ROMHACKS_DIR` macro expands to `"~romhacks"` (defined at line 86), establishing the canonical path `mods/~romhacks/`.

---

## Exclusive Romhack Enablement

### Step 4: Setting the Sole Active Romhack

The `SetSoleEnabledRomhack()` function (lines 57–89) implements Lighthouse's **mutual exclusion guarantee**:

```cpp
// LighthouseModMenuWindow.cpp (lines 57–89)
void SetSoleEnabledRomhack(const std::string& romhackBasename) {
    // 1. Verify base game compatibility first
    if (!BaseGameSupportsRomhacks()) {
        // Show warning: bk.o2r must be US v1.0
        return;
    }
    
    // 2. Scan entire mods/ tree and adjust enable states
    // 3. Enable only the specified romhack overlay
    // 4. Update CVars directly (bypassing in-memory list)
    SaveConsoleVariablesNextFrame();
}

```

Before enabling any overlay, `BaseGameSupportsRomhacks()` verifies that `bk.o2r` matches the US v1.0 release (lines 66–71). Mismatched base game versions trigger a warning popup and abort the enablement—romhack overlays often depend on specific memory layouts and asset offsets.

The function performs a **complete mods tree scan**, identifying overlays via `IsRomhackOverlay()` (lines 216–223), then updates `EnabledMods` and `DisabledMods` CVars directly rather than through the usual UI-mediated path. This raw CVar manipulation is necessary because the mod list hasn't been refreshed yet.

---

## Romhack Detection and Compatibility Systems

### SHA-1 Validation for Custom Code

Romhacks may include custom code patches. Lighthouse validates these against known hashes stored in [`src/port/Romhack/RomhackTable.h`](https://github.com/HarbourMasters/Lighthouse/blob/main/src/port/Romhack/RomhackTable.h) (lines 14–21):

```cpp
// RomhackTable.h
struct RomhackEntry {
    const char* name;
    const char* sha1Hash;      // Custom code blob hash
    uint32_t    configVersion; // aGameConfig format version
    // ...
};

```

The [`RomhackConfig.cpp`](https://github.com/HarbourMasters/Lighthouse/blob/main/RomhackConfig.cpp) parser cross-references extracted overlays against this table, ensuring compatible custom-code handling and proper `aGameConfig` deserialization.

### Runtime Detection Utilities

Two detection functions support the mod manager's filtering needs:

```cpp
// LighthouseModMenuWindow.cpp (lines 216–223)
bool IsRomhackOverlay(const std::string& name);
bool IsInRomhacksFolder(const std::filesystem::path& path);

```

These enable UI differentiation between standard mods and romhack overlays throughout the interface.

---

## Restart and Final Application

### Step 5: Applying the Romhack

After `SetSoleEnabledRomhack()` succeeds, Lighthouse displays a "Mod Installed" popup with a single actionable outcome: `GameEngine::RequestRelaunch()`. Restart is mandatory because:

- `aGameConfig` loads during early initialization
- Overlay mounting occurs before the main loop begins
- CVars written to disk must be re-read from fresh process state

On relaunch, the enabled romhack overlay mounts over `bk.o2r`, its modified `aGameConfig` deserializes through [`RomhackConfig.cpp`](https://github.com/HarbourMasters/Lighthouse/blob/main/RomhackConfig.cpp), and the custom code patches apply based on [`RomhackTable.h`](https://github.com/HarbourMasters/Lighthouse/blob/main/RomhackTable.h) validation.

---

## Summary

- **Extraction**: `GameExtractor::GenerateOTR()` creates slim *.o2r* overlays from N64 ROMs via a background thread with atomic progress reporting.
- **Placement**: Successful extractions move to `mods/~romhacks/` (the `ROMHACKS_DIR` location).
- **Exclusivity**: `SetSoleEnabledRomhack()` enforces exactly one active romhack by full-tree scanning and direct CVar manipulation.
- **Compatibility**: US v1.0 base game verification and SHA-1 hash tables in [`RomhackTable.h`](https://github.com/HarbourMasters/Lighthouse/blob/main/RomhackTable.h) prevent invalid configurations.
- **Activation**: Restart requirement ensures clean `aGameConfig` loading in the next process instance.

---

## Frequently Asked Questions

### What file format do Lighthouse romhacks use?

Lighthouse romhacks use the ***.o2r*** (OTR overlay) format—identical to standard mods but with a modified `aGameConfig` blob that overrides base game parameters. These reside exclusively in `mods/~romhacks/` rather than the general mods directory.

### Can multiple romhacks be active simultaneously?

**No.** The `SetSoleEnabledRomhack()` function explicitly disables all other romhack overlays before enabling a new one. This mutual exclusion prevents `aGameConfig` collisions that would destabilize the engine. You must restart to switch romhacks.

### Why does the base game need to be US v1.0?

Romhacks often depend on specific memory layouts, asset offsets, and code patterns present only in the US v1.0 release. The `BaseGameSupportsRomhacks()` check in [`LighthouseModMenuWindow.cpp`](https://github.com/HarbourMasters/Lighthouse/blob/main/LighthouseModMenuWindow.cpp) (lines 66–71) validates your `bk.o2r` against this reference version before allowing any romhack enablement.

### Where are romhack SHA-1 hashes defined?

Known romhack custom-code hashes are registered in [`src/port/Romhack/RomhackTable.h`](https://github.com/HarbourMasters/Lighthouse/blob/main/src/port/Romhack/RomhackTable.h) (lines 14–21). The [`RomhackConfig.cpp`](https://github.com/HarbourMasters/Lighthouse/blob/main/RomhackConfig.cpp) parser uses this table to identify compatible romhacks and apply appropriate deserialization logic for their `aGameConfig` structures.