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 prefixlanginfoarray: 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:
- Preserves only translated assets, filtering out base game content that remains unchanged
- Prefixes assets automatically under
lang/<region>/during import - Generates the
langinfomanifest from pack metadata - 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.
Menu Integration and Player Selection
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:
- Generation counter increment
langinfometadata reload- UI rebuild via
buildPackFileSelectInfo()whenResourceMgr_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.ymlmanifests declare metadata, display names, and UI string overridesdialog_packmode 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →