Skip to main content

O2R Format

O2R files are ZIP archives. Each file inside the archive is a binary resource with a common 64-byte header followed by resource-specific data.

File Structure

Every resource in an O2R archive follows this pattern:

[64-byte common header] [resource-specific payload]

See header.md for the common header format.

String Encoding

Strings in the binary format are length-prefixed:

FieldTypeNotes
lengthu32Number of bytes in the string (not including this length field)
datachar[length]UTF-8 string data (no null terminator stored)

Resource Types

Fast3D Resources (libultraship)

TypeFourCCHexVersionFile
DisplayListODLT0x4F444C54V0display-list.md
LightLGTS0x46669697V0light.md
MatrixOMTX0x4F4D5458V0matrix.md
TextureOTEX0x4F544558V0, V1texture.md
VertexOVTX0x4F565458V0vertex.md

SOH Game Resources

TypeFourCCHexVersionFile
AnimationOANM0x4F414E4DV0animation.md
PlayerAnimationOPAM0x4F50414DV0player-animation.md
ArrayOARR0x4F415252V0array.md
AudioSampleOSMP0x4F534D50V2audio-sample.md
AudioSequenceOSEQ0x4F534551V2audio-sequence.md
AudioSoundFontOSFT0x4F534654V2audio-soundfont.md
BackgroundOBGI0x4F424749V0background.md
CollisionHeaderOCOL0x4F434F4CV0collision-header.md
CutsceneOCUT0x4F435654V0cutscene.md
PathOPTH0x4F505448V0path.md
SkeletonOSKL0x4F534B4CV0skeleton.md
SkeletonLimbOSLB0x4F534C42V0skeleton-limb.md
TextOTXT0x4F545854V0text.md
SceneOROM0x4F524F4DV0scene.md

Scene Commands (FourCC: ORCM / 0x4F52434D)

CommandFile
All scene commandsscene-commands.md

Ship Core (libultraship)

TypeFourCCHex
BlobOBLB0x4F424C42
JsonJSON0x4A534F4E
ShaderSHAD0x53484144

Understanding the Resource Loading Pipeline

When you first launch Ship of Harkinian, it will extract the assets from your ROM and create an oot.o2r archive with all assets separated into individual files in a special binary format. Anything within this file is available to be replaced, but when creating resource tooling, it is important that the format your tool exports matches this binary format exactly.

Resources in the ROM go through a multi-step process to be loaded into the game:

Step 1: ZAPD Extraction

ZAPD is used in combination with the ROM XMLs to pull resources out of the ROM and into memory as various resource types in ZAPDTR/ZAPD/*:

  • ZRoom - Room/scene data
  • ZDisplayList - Display lists
  • ZTextMM - Text resources
  • And many more...

Step 2: OTR Export

OTRExporter is used to write these ZAPD resources into the custom O2R format within the O2R file.

Key Changes:

  • Sometimes the result is very similar to what was pulled from the ROM
  • Sometimes it's changed significantly
  • Anything that references another resource gets that reference replaced with either:
    • A string path to the resource, OR
    • A CRC64 hash of the path to that resource
  • This replaces the pointer references that the ROM used

Step 3: Resource Factory Import

Resources are read from the O2R by resource factories located in mm/2s2h/resource/importer/*

These factories place resources into LUS objects that mostly reflect their in-game counterparts, with possible subtle differences.

Step 4: Game Code Usage

The game then loads these objects from LUS using calls like:

std::static_pointer_cast<SOH::TextMM>(
Ship::Context::GetInstance()->GetResourceManager()->LoadResource(filePath)
);
tip

The best way to learn the system is to pick a simple resource type (like text or audio samples) and trace it through all four steps of the pipeline.

Example: Text Resources End-to-End

Following a text resource through all stages:

  1. ZAPD: ZAPDTR/ZAPD/ZTextMM.cpp - Parses raw ROM data into ZTextMM
  2. OTRExporter: OTRExporter/OTRExporter/TextMMExporter.cpp - Converts ZTextMM into custom format stored in O2R
  3. Factory: mm/2s2h/resource/importer/TextMMFactory.cpp - Reads from O2R into LUS's SOH::ResourceType::TSH_TextMM
  4. Game Code: mm/2s2h/z_message_OTR.cpp - Loads data from LUS and uses it

Creating New Tooling

When creating tooling for custom resources:

Requirements:

  • Export data in the EXACT format that the Resource Factory (Step 3) expects
  • Match what OTRExporter (Step 2) would have written to the O2R
  • Match types precisely (u8 vs s16, etc.)
  • Match order exactly
  • Include CRC64 hashes where the factory expects them
Critical

The format must match perfectly. Any mismatch in type, order, or structure will cause loading to fail.

Example: Fast64 O2R Export

Working Diff for 2Ship Models

DisplayLists, Materials, and Skeletons:

Full Scene Export (Work in Progress):