Lighthouse and libultraship: Understanding the Submodule Relationship in N64 PC Ports

Lighthouse is built directly on top of libultraship, which serves as the core N64 emulation layer and provides the foundational hardware abstraction that enables the project to run on modern platforms.

The HarbourMasters/Lighthouse project depends on libultraship as a git submodule, making it the technical backbone that supplies N64-specific types, rendering bridges, and OS abstractions. This architecture allows Lighthouse to focus on game-specific features and modding capabilities while delegating low-level emulation to a shared, maintained library.

What is libultraship in the Lighthouse Architecture?

libultraship implements the classic Nintendo 64 "libultra" SDK APIs for modern systems. In Lighthouse, it functions as a hardware abstraction layer that translates original N64 system calls into cross-platform equivalents for Windows, Linux, and macOS.

The relationship is strictly hierarchical: Lighthouse consumes libultraship's services but does not modify its internals. This separation is enforced through the git submodule mechanism, which pins a specific version of libultraship and isolates it from Lighthouse's game logic.

How Lighthouse Includes libultraship as a Submodule

The dependency is declared explicitly in the repository's .gitmodules configuration:

[submodule "libultraship"]
    path = libultraship
    url = https://github.com/HarbourMasters/libultraship.git

When developers clone Lighthouse with submodules (git clone --recursive), the libultraship source tree populates the libultraship/ directory. The build system then compiles both projects together, linking Lighthouse's code against libultraship's object files.

This approach ensures that:

  • All contributors use identical libultraship versions
  • Updates to the emulation layer can be pulled independently
  • The core N64 implementation remains reusable across HarbourMasters projects

Core Type Definitions and N64 Compatibility

Lighthouse does not redefine N64 primitive types. Instead, it forwards to libultraship's headers through thin wrapper files like src/ultratypes.h:

#include <libultraship/libultra/types.h>

This single include makes available the complete set of N64-specific structures: OSMesgQueue, OSThread, OSPiHandle, and numerous fixed-width integer aliases (s32, u16, f32, etc.).

By sourcing these definitions from libultraship, Lighthouse maintains binary and semantic compatibility with original N64 code while eliminating duplicate type declarations that could drift out of sync.

Platform-Agnostic Bridge Headers in Practice

Higher-level Lighthouse modules access libultraship's runtime services through bridge headers. The file src/port/UI/UIWidgets.hpp demonstrates this pattern:

#include <libultraship/bridge.h>
// or
#include <libultraship/libultraship.h>

These headers expose capabilities including:

  • Console variables (CVar) for runtime configuration
  • Resource loading and archive management
  • Controller input processing and remapping
  • Audio playback through the ship's sound system

The bridge abstraction allows UI code to remain platform-agnostic. For example, accessing a console variable follows a consistent API regardless of underlying OS:

#include <libultraship/bridge/consolevariablebridge.h>

void ToggleSomeFeature(void) {
    ConsoleVariable* cv = GetConsoleVariable("some_feature_enabled");
    cv->value = !cv->value;  // Flip the boolean value
}

Rendering and OS Abstraction Layer

The most intensive interaction between Lighthouse and libultraship occurs in graphics and system emulation. Key integration points include:

Component Lighthouse File libultraship Role
OS emulation src/port/OS/libultra.c Provides OSCreateThread, OSRecvMesg, and other kernel primitives
Graphics bridge src/port/Resource/GfxBridge.c Translates gsSP* display list commands to modern GPU APIs
Resource loading src/port/Resource/ Handles rom: and otrs: virtual file systems

The file src/port/OS/libultra.c contains implementations that delegate to libultraship's OS layer, while src/port/Resource/GfxBridge.c adapts the original N64 graphics binary interface (GBI) for rendering backends like OpenGL or DirectX.

This architecture means Lighthouse developers rarely write platform-specific code. The libultraship layer absorbs differences between Windows, Linux, and macOS, presenting a unified N64-compatible interface upward.

Example: Using libultraship Types in Lighthouse Code

When working with N64 synchronization primitives, Lighthouse code uses types provided by the submodule:

// Example: pulling N64 type definitions from libultraship
#include <libultraship/libultra/types.h>

void ExampleFunction(void) {
    // Use a libultra type defined in libultraship
    OSMesgQueue gfxQueue;
    // Queue initialization and use follows standard N64 patterns
    // but executes on the host platform via libultraship's implementation
}

The OSMesgQueue structure and its associated functions (OSCreateMesgQueue, OSSendMesg, OSRecvMesg) are implemented within libultraship, not duplicated in Lighthouse.

Summary

  • Lighthouse is a client of libultraship, not a peer; the submodule relationship establishes a clean dependency boundary
  • Type definitions flow upward from libultraship/libultra/types.h through Lighthouse's forwarding headers
  • Bridge headers expose runtime services (CVars, resources, input) without platform-specific code in Lighthouse
  • Rendering and OS emulation are delegated entirely to libultraship's implementations
  • Git submodules enforce version consistency and enable independent library development

Frequently Asked Questions

Why does Lighthouse use a submodule instead of copying libultraship code?

The submodule approach prevents code duplication and ensures that improvements to N64 emulation—bug fixes, performance optimizations, new platform support—flow automatically to all HarbourMasters projects. Copying code would create maintenance burden and version fragmentation across repositories.

Can Lighthouse be built without libultraship?

No. Lighthouse's codebase contains hundreds of references to libultraship headers and symbols. The project would fail to compile without the submodule present, as critical types like OSMesgQueue and rendering functions have no alternative definitions in the Lighthouse source tree.

How often does Lighthouse update its libultraship version?

Updates occur on a rolling basis as the HarbourMasters team stabilizes new libultraship features. The submodule commit pointer in .gitmodules is updated via standard git commands, and contributors typically synchronize submodules with git submodule update --remote when pulling latest Lighthouse changes.

What platforms does this architecture support?

libultraship implements backends for Windows (DirectX/OpenGL), Linux (OpenGL/Vulkan), and macOS (Metal/OpenGL). Lighthouse inherits this portability automatically—no platform-specific code is required in the game layer to target any supported system.

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 →