How Lighthouse Supports Multiple Game Regions Through Language Packs: A Technical Deep Dive

Lighthouse enables multiple game regions by loading modular language packs that swap regional assets at runtime through manifest-driven asset re-pointing and UI string replacement.

This open-source Banjo-Kazooie PC port uses a flexible language pack system to let a single binary support any number of regional variants. By separating regional assets into loadable O2R archives and tracking language state through generation counters, Lighthouse achieves region switching without rebuilding the executable.


Understanding Lighthouse Language Packs Architecture

The Lighthouse language pack system rests on three architectural pillars: isolated asset trees, declarative manifests, and runtime re-pointing. Each pillar works together to enable clean separation between the base game and regional variants.

Separate Asset Trees Per Region

All regional content lives under lang/<region>/ paths inside O2R archives. The base game ships with its native region (typically lang/us/ for North American releases), while language packs provide parallel structures like lang/es/, lang/fr/, lang/jp/, and so on.

This path-based isolation ensures that asset names remain consistent across regions while the resource manager routes requests to the appropriate tree. Dialog, quiz answers, Grunty's questions, and even regional exclusives like Japanese parade credits all follow this convention.

According to the Lighthouse source, this structure is established during pack export when the Torch tool extracts assets into editable YAML hierarchies under workdir/src/assets/lang/<region>/.


The langinfo.yml Manifest System

Every language pack includes a langinfo.yml manifest that declares essential metadata for runtime integration.

Manifest Structure and Fields

The manifest specifies:

  • region: Two-letter region code matching the asset path prefix
  • langinfo array: Display name, index, and script type (Latin = 0, Japanese = 1)
  • strings (optional): UI string overrides for menu items and system messages

# Example: Spanish language pack (langinfo.yml)

region: es
langinfo:
  - { name: Español, index: 0, script: 0 }
strings:
  "RETURN TO GAME": "VOLVER AL JUEGO"
  "ARE YOU SURE?": "$EST%S SEGURO?"

The index field controls menu ordering, while script determines font rendering behavior—critical for Japanese text which requires different glyph handling than Latin scripts.


Building Language Packs With dialog_pack Mode

The Torch modding toolchain provides a dialog_pack: true configuration flag that streamlines language-only pack creation.

Pack Mode Workflow

When dialog_pack is enabled in the pack's config.yml, Torch:

  1. Preserves only translated assets, filtering out base game content that remains unchanged
  2. Prefixes assets automatically under lang/<region>/ during import
  3. Generates the langinfo manifest from pack metadata
  4. Outputs a language-only O2R to mods/~lang/<name>.o2r

# Example: pack configuration (torch config.yml)

config:
  dialog_pack: true          # keep only translated assets

  output:
    binary: bkes.o2r         # resulting language pack name

This approach minimizes pack size and eliminates redundancy—language packs contain only what differs from the base region.

Build Commands


# Export base game assets to editable YAML

torch modding export <baserom.z64> -s lighthouse -d workdir

# Edit translation files under workdir/src/assets/lang/es/...

# Import and generate language pack

torch modding import o2r <baserom.z64> -s lighthouse -d workdir

# Result: mods/~lang/bkes.o2r

Runtime Region Switching Implementation

The runtime region switching system in src/port/Localization/Localization.cpp handles three critical operations: asset re-pointing, model cache invalidation, and UI string substitution.

Asset Re-Pointing via Generation Counters

Lighthouse tracks language state through a language generation counter exposed via ResourceMgr_GetLanguageGeneration(). When the player selects a different language through the options menu, this counter increments, signaling all subsystems to refresh region-dependent content.

The ResourceMgr_IsAssetRepointed(uint32_t assetId) function checks whether an asset ID has been overridden by the active language pack:

// Asset re-point check (simplified from source)
bool ResourceMgr_IsAssetRepointed(uint32_t assetId) {
    return sRepointedAssets.contains(assetId);
}

This lookup populates sRepointedAssets from the active pack's asset manifest, enabling transparent redirection without modifying game code.

Model Cache Invalidation

Regional variants often include different 3D models—for Japanese-specific assets or modified character geometry. Localization.cpp handles this through explicit cache clearing:

// From Localization.cpp around line 588
// Invalidates cached models when language generation changes
// Forces reload on next draw call with new regional assets

The engine detects generation changes during frame updates and triggers GfxPatcher_InvalidatedAnimatedTextures() alongside model cache clearing.

UI String Replacement

Menu text and system messages route through LocalizeUiString, which checks for pack-provided translations before falling back to base strings:

// Runtime UI string replacement (simplified from Localization.cpp around line 612)
REGISTER_LISTENER(LocalizeUiString, EVENT_PRIORITY_NORMAL, [](IEvent* ev) {
    const char* packStr = ResourceMgr_GetLangString(*ev->str);
    if (packStr != *ev->str) 
        *ev->str = packStr;   // apply pack translation
});

The strings: map in langinfo.yml feeds into ResourceMgr_GetLangString(), enabling per-pack customization of hardcoded UI labels.


The in-game options menu presents available languages through LighthouseModMenuWindow.cpp (around line 868), which enumerates all loaded langinfo manifests.

Dynamic Language List Population

The menu code queries the resource manager for discovered language packs, building a selectable list from their display names. Selection triggers:

  1. Generation counter increment
  2. langinfo metadata reload
  3. UI rebuild via buildPackFileSelectInfo() when ResourceMgr_HasLangStrings() returns true

This helper reconstructs file-select screen elements that display regional information—save slot names, play time formatting, and region-specific metadata.


Additive Region-Only Content

Beyond replacement assets, language packs support additive content for assets exclusive to other regional releases. This enables, for example, a US build to display PAL/Japanese-only parade credits or quiz questions.

Additive Asset Declaration

Packs declare additive assets in modding.yml:


# Example: additive asset for Japanese parade content

assets:
  - name: parade_credits_jp
    type: overlay
    additive: true
    source: jp/parade_credits.bin

At runtime, these assets register as overrides only when the corresponding pack is active, extending rather than replacing base content.


Key Source Files and Responsibilities

File Core Responsibility
docs/modding/LANGUAGE PACKS.md Author documentation; pack structure, langinfo.yml specification, and Torch workflows
src/port/Localization/Localization.cpp UI string swapping, model cache invalidation, file-select rebuild coordination
src/port/Localization/Language.cpp langinfo manifest parsing, language generation API implementation
src/port/UI/LighthouseModMenuWindow.cpp Options menu language list, selection handling, generation counter updates
src/port/ResourceMgr.cpp Asset re-pointing core, IsAssetRepointed() and generation counter management

Summary

Lighthouse supports multiple game regions through a declarative, pack-based architecture:

  • Path isolation keeps regional assets under lang/<region>/ without name collisions
  • langinfo.yml manifests declare metadata, display names, and UI string overrides
  • dialog_pack mode generates minimal, translation-only O2R archives via Torch
  • Generation counters trigger runtime asset re-pointing and cache invalidation
  • Additive assets enable region-exclusive content without base game modification
  • Event-driven UI replacement swaps strings and rebuilds menus on language change

This system allows end users to drop language packs into mods/~lang/ and switch regions instantly from the options menu—no executable patches required.


Frequently Asked Questions

What file format do Lighthouse language packs use?

Lighthouse language packs use O2R archives (a custom container format) placed in mods/~lang/. These archives contain lang/<region>/ asset trees and a langinfo.yml manifest. The O2R format compresses and indexes assets for efficient runtime loading while remaining compatible with the Torch modding toolchain.

Can a language pack modify gameplay mechanics or only text?

Language packs focus on regional assets and text, but the additive asset system allows including any asset type—including scripts. However, the dialog_pack: true flag specifically filters for translation-relevant content. For broader modifications, creators typically use standard mod packages rather than language packs.

How does Lighthouse handle Japanese text rendering differently?

Japanese requires script type 1 in langinfo.yml, which activates font glyph substitution and adjusted layout handling. The engine loads Japanese-specific font textures and applies different spacing rules through ResourceMgr_HasLangStrings()-gated code paths in UI rendering functions.

What happens if a language pack is missing an asset?

The runtime falls back to base region assets transparently. If ResourceMgr_IsAssetRepointed() returns false for a given asset ID, the engine loads from the default lang/us/ (or equivalent base) path. This partial override capability lets language packs ship incomplete translations without breaking game functionality.

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 →