Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
461385c
Add OoT asset support: factories, tooling, and scene/room export
briaguya0 Mar 29, 2026
d713580
Add recovered POC/draft notes document
briaguya0 Mar 29, 2026
34b76c0
add .gitignores that were lost
briaguya0 Mar 29, 2026
3385cda
Add ROM identification, DMA table extraction, and manifests directory
briaguya0 Mar 29, 2026
5e70571
Move config.yml to soh/assets/yml/ and add scaffolding
briaguya0 Mar 29, 2026
b4f3506
Implement Yaz0 decompression and fix missing definitions
briaguya0 Mar 29, 2026
27cefa8
Fix segment 0x80 handling for OoT code-section assets
briaguya0 Mar 29, 2026
20e8dee
Fix crash on zero-size blobs (e.g. OoT LimbTable)
briaguya0 Mar 29, 2026
7ee15ea
Fix empty blob export, add test logging and compare tool
briaguya0 Mar 29, 2026
512df41
Add *.o2r and torch.hash.yml to gitignore
briaguya0 Mar 29, 2026
7ba1eb5
Optimize test_assets.sh: batch hashing and log torch output to file
briaguya0 Mar 29, 2026
e31b7cc
Replace test_assets.sh with Python version (60x faster)
briaguya0 Mar 29, 2026
4069ced
Add BUILD_OOT CMake option and stub OoTTextFactory
briaguya0 Mar 29, 2026
1a516be
Fix limb DList auto-discovery and naming (574 → 1 object failure)
briaguya0 Mar 29, 2026
cbb9328
Auto-create SkelLimbs 0-byte blob for skeleton limb tables
briaguya0 Mar 29, 2026
50ede2d
Fix MTX binary export to write raw int32 values
briaguya0 Mar 29, 2026
ccf13c6
Fix PatchVirtualAddr to prefer segment 0x80 for overlay textures
briaguya0 Mar 29, 2026
67bf3d1
Skip DList entries in scene/room XMLs during YAML conversion
briaguya0 Mar 29, 2026
525ec56
Skip all DList entries in room XML files during YAML conversion
briaguya0 Mar 29, 2026
a40e4f6
Add deferred alternate header processing for scenes
briaguya0 Mar 29, 2026
8a88bc6
Fix alternate header sub-asset naming and cutscene suffix
briaguya0 Mar 29, 2026
7cfbbfd
Fix sub-asset naming for scene alternate headers
briaguya0 Mar 29, 2026
4f13d32
Use neighbor-based size for pathway count inference
briaguya0 Mar 29, 2026
b0c8968
Create 0-byte ActorEntry companion files for SetActorList
briaguya0 Mar 29, 2026
80d3352
Add asset alias mechanism for Set_ DList duplicates
briaguya0 Mar 29, 2026
ee6afd7
Add command-aware cutscene size calculation
briaguya0 Mar 29, 2026
9b46d7e
Update cutscene plan with re-serialization details
briaguya0 Mar 29, 2026
59eb03f
Implement cutscene re-serialization with macro packing
briaguya0 Mar 29, 2026
15ec464
Fix actor cue rotY/rotZ packing in cutscene re-serialization
briaguya0 Mar 29, 2026
d893d54
Document remaining OoT asset work
briaguya0 Mar 29, 2026
70a3576
Fix cutscene command ID remapping (ROM IDs vs OTR output IDs)
briaguya0 Mar 30, 2026
bc51929
Revert incorrect command ID remapping, keep bounds checking
briaguya0 Mar 30, 2026
5499945
Skip unimplemented cutscene commands matching OTRExporter behavior
briaguya0 Mar 30, 2026
4145d78
Document spot04 pathway edge case in scene factory
briaguya0 Mar 30, 2026
7dd60dd
Add comprehensive ZPath bug analysis for spot04 pathway edge case
briaguya0 Mar 30, 2026
b6e64a8
Fix pathway count for alternate headers without YAML declarations
briaguya0 Mar 30, 2026
dbb3666
Fix CRC double byte-swap in version file
briaguya0 Mar 30, 2026
9414e26
Add plan for 135 missing scene sub-assets
briaguya0 Mar 30, 2026
2657542
Add OOT:CUTSCENE factory for standalone YAML-declared cutscenes
briaguya0 Mar 30, 2026
462bcde
Remove duplicated cutscene code from SetCutscenes handler
briaguya0 Mar 30, 2026
d77adf8
Add OOT:PATH factory for standalone YAML-declared paths
briaguya0 Mar 30, 2026
b90c58e
Extract SerializePathways to eliminate pathway code duplication
briaguya0 Mar 30, 2026
dd5265a
Create background companion files for mesh type 1 rooms
briaguya0 Mar 30, 2026
e13234d
Extract CreateBackgroundCompanion to deduplicate background code
briaguya0 Mar 30, 2026
5cad83d
Document scene DList investigation — YAML approach doesn't work
briaguya0 Mar 30, 2026
f64e691
Update scene DList plan with conflict analysis
briaguya0 Mar 30, 2026
ebd634d
Fix 18 missing scene-level DLists via YAML entry ordering
briaguya0 Mar 30, 2026
ab8fedb
Keep all DLists from room files, rely on YAML ordering
briaguya0 Mar 30, 2026
1d3bb23
Add verified OoT audio factory plan (598 assets)
briaguya0 Mar 30, 2026
349fb6c
Break down audio plan into incremental verifiable steps
briaguya0 Mar 30, 2026
7fb4799
Add audio metadata extraction to zapd_to_torch.py (Step 0)
briaguya0 Mar 30, 2026
db03b5d
Add OOT:AUDIO factory skeleton (Step 1)
briaguya0 Mar 30, 2026
376927b
Extract sequences from audio segment (Steps 2+3)
briaguya0 Mar 30, 2026
8f94549
Fix aliased sequences (size==0 means ptr is index to another seq)
briaguya0 Mar 30, 2026
0320fdd
Add detailed audio sample extraction plan with correct patterns
briaguya0 Mar 30, 2026
b153d2e
Extract 449 audio samples using safe BinaryReader pattern (Step 4)
briaguya0 Mar 30, 2026
bda2b48
Add audio sample dedup/naming analysis
briaguya0 Mar 30, 2026
99a2b20
Fix cross-bank audio sample naming to match ZAPDTR behavior
briaguya0 Mar 30, 2026
c5694cc
Add audio font extraction plan (Step 5)
briaguya0 Mar 30, 2026
ca2c5ec
Extract 38 audio fonts (OSFT companion files, Step 5)
briaguya0 Mar 30, 2026
9d3a285
Fix last 2 font failures via drum→instrument stack residue mapping
briaguya0 Mar 30, 2026
2e86358
Implement OoTTextFactory for message_data_static assets
briaguya0 Mar 30, 2026
4886150
Add portVersion analysis documenting Shipwright lifecycle
briaguya0 Mar 30, 2026
f76a38f
Fix portVersion: add endianness byte and pass version to Torch
briaguya0 Mar 30, 2026
7ace490
Document VTX YAML dependency on reference O2R
briaguya0 Mar 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
build-*/
cmake-build-*/
*.otr
*.o2r
*.log
torch.hash.yml
.DS_Store
debug/
!src/factories/debug/
Expand All @@ -14,9 +16,12 @@ build/
code/
.vscode/
tools/
!soh/tools/
modding/*
.cache

# Generated YAML asset definitions (per-version dirs are gitignored via soh/assets/yml/.gitignore)

# Doxygen output
docs/html
docs/latex
64 changes: 63 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ option(BUILD_PM64 "Build with Paper Mario support" ON)
option(BUILD_FZERO "Build with F-Zero X support" ON)
option(BUILD_MARIO_ARTIST "Build with Mario Artist support" ON)
option(BUILD_NAUDIO "Build with NAudio support" ON)
option(BUILD_OOT "Build with Ocarina of Time support" ON)

if(EMSCRIPTEN)
set(BUILD_SM64 OFF) # TODO: This is broken for some reason
Expand Down Expand Up @@ -70,7 +71,7 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR})
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/lib)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src)
file(GLOB_RECURSE CXX_FILES ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/**/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/lib/strhash64/*.cpp)
file(GLOB C_FILES ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c ${CMAKE_CURRENT_SOURCE_DIR}/src/**/*.c ${CMAKE_CURRENT_SOURCE_DIR}/lib/libmio0/*.c ${CMAKE_CURRENT_SOURCE_DIR}/lib/libyay0/*.c)
file(GLOB C_FILES ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c ${CMAKE_CURRENT_SOURCE_DIR}/src/**/*.c ${CMAKE_CURRENT_SOURCE_DIR}/lib/libmio0/*.c ${CMAKE_CURRENT_SOURCE_DIR}/lib/libyay0/*.c ${CMAKE_CURRENT_SOURCE_DIR}/lib/libyaz0/*.c)
set(SRC_DIR ${CXX_FILES} ${C_FILES} ${LGFXD_FILES})

if(BUILD_SM64)
Expand Down Expand Up @@ -115,11 +116,23 @@ else()
list(FILTER SRC_DIR EXCLUDE REGEX "${CMAKE_CURRENT_SOURCE_DIR}/src/factories/naudio/*")
endif()

if(BUILD_OOT)
add_definitions(-DOOT_SUPPORT)
else()
list(FILTER SRC_DIR EXCLUDE REGEX "${CMAKE_CURRENT_SOURCE_DIR}/src/factories/oot/*")
endif()

if(ENABLE_ASAN)
add_compile_options(-fsanitize=address)
add_link_options(-fsanitize=address)
endif()

option(ENABLE_COVERAGE "Enable code coverage instrumentation" OFF)
if(ENABLE_COVERAGE)
add_compile_options(--coverage -fno-inline)
add_link_options(--coverage)
endif()

# Build
if (USE_STANDALONE)
add_definitions(-DSTANDALONE)
Expand Down Expand Up @@ -288,3 +301,52 @@ if(NOT USE_STANDALONE)
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src)
target_include_directories(${PROJECT_NAME} PUBLIC ${yaml-cpp_SOURCE_DIR}/include)
endif()

# Unit Tests
option(BUILD_TESTS "Build unit tests" OFF)
if(BUILD_TESTS)
set(BUILD_GMOCK OFF CACHE BOOL "" FORCE)
set(INSTALL_GTEST OFF CACHE BOOL "" FORCE)
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG v1.15.2
)
FetchContent_MakeAvailable(googletest)

enable_testing()

file(GLOB TEST_FILES ${CMAKE_CURRENT_SOURCE_DIR}/tests/*.cpp)

set(TEST_SRC_DIR ${SRC_DIR})
list(FILTER TEST_SRC_DIR EXCLUDE REGEX "main\\.cpp$")

add_executable(torch_tests ${TEST_FILES} ${TEST_SRC_DIR})
target_include_directories(torch_tests PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/lib
${CMAKE_CURRENT_SOURCE_DIR}/src
)
target_link_libraries(torch_tests PRIVATE gtest_main yaml-cpp tinyxml2 N64Graphics BinaryTools spdlog)
add_test(NAME torch_tests COMMAND torch_tests)

# Integration Tests
option(BUILD_INTEGRATION_TESTS "Build integration tests (require ROM files)" OFF)
if(BUILD_INTEGRATION_TESTS)
file(GLOB INTEGRATION_TEST_FILES ${CMAKE_CURRENT_SOURCE_DIR}/tests/integration/*.cpp)

add_executable(torch_integration_tests ${INTEGRATION_TEST_FILES} ${TEST_SRC_DIR})
target_include_directories(torch_integration_tests PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/lib
${CMAKE_CURRENT_SOURCE_DIR}/src
${CMAKE_CURRENT_SOURCE_DIR}/tests/integration
)
target_compile_definitions(torch_integration_tests PRIVATE
INTEGRATION_TEST_DIR="${CMAKE_CURRENT_SOURCE_DIR}/tests/integration"
INTEGRATION_ROM_DIR="${CMAKE_CURRENT_SOURCE_DIR}/tests/roms"
)
target_link_libraries(torch_integration_tests PRIVATE gtest_main yaml-cpp tinyxml2 N64Graphics BinaryTools spdlog)
add_test(NAME torch_integration_tests COMMAND torch_integration_tests)
endif()
endif()
167 changes: 167 additions & 0 deletions docs/oot-asset-plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
# Plan: Get All OoT Assets Working in Torch

## Context

We're replacing Shipwright's ZAPDTR/OTRExporter with Torch for OoT asset extraction. The scaffolding is complete (DMA tables, verification scripts, 2 blobs verified). Now we need to implement support for all ~35,386 assets in the reference O2R. This requires both new OoT-specific factories in Torch and YAML configs for every asset.

## Strategy

Two parallel workstreams:
1. **Factories** — implement Torch factories for each OoT asset type
2. **YAMLs** — convert Shipwright's XML configs to Torch YAML format (scripted, not manual)

Order by: existing factory reuse first, then new factories by asset count (biggest impact first), deferring complex/unknown types.

## Phase 0: Foundation

### 0.1: Build system setup
- Add `BUILD_OOT` option to `CMakeLists.txt` (follow SM64/SF64 pattern)
- Add `OOT_SUPPORT` define
- Create `src/factories/oot/` directory
- Add filter regex for oot factories

### 0.2: Resource types
- Add OoT-specific type codes to `src/factories/ResourceType.h`
- Codes needed (from OTRExporter): OSKL, OSLB, OANM, OROM, OCOL, OCVT, OPTH, OPAM, OTXT, OAUD, OSFT, OSMP, OSEQ

### 0.3: XML-to-YAML conversion script
- Python script: reads XML + `soh/dma/pal_gc.json` → outputs YAML
- Lives at `soh/tools/xml_to_yaml.py` (or similar)
- Run incrementally as each phase adds type support
- Maps XML types to Torch YAML types, inserts DMA phys_start as segment offset

## Phase 1: Generic Types (~22,000 assets, ~62%)

Uses existing Torch factories. Covers: TEXTURE (8,989), GFX/DList (3,443), VTX (81), MTX (2), BLOB (62), ARRAY (85).

### Key risk: VTX format mismatch
Reference O2R uses `ResourceType::Array (0x4F415252)` with array_type=25 for vertex data — NOT `ResourceType::Vertex (0x4F565458)` or `GenericArray (0x47415252)`. Need to verify which type code the existing VTX/Array factories produce and adjust.

### Key risk: GFX auto-discovery
The DListFactory auto-discovers VTX/TEXTURE/LIGHTS from display list parsing. Must verify auto-discovered assets match reference paths and hashes. Test with a small object file first.

### Steps
1. Generate YAMLs for `textures/` (~151 files, ~2,000 assets) — TEXTURE only
2. Verify a batch against manifest
3. Generate YAMLs for `objects/` (~381 files) — GFX + TEXTURE + VTX + BLOB
4. Handle auto-discovery: GFX factory finds VTX/TEX/LIGHTS from display lists
5. Generate YAMLs for `overlays/` (~32 files) and `code/` (~4 files)
6. Verify

## Phase 2: Skeleton System (~4,076 assets)

New factories needed: OoT Limb, Skeleton, Animation.

### 2.1: OoT Limb Factory (`src/factories/oot/LimbFactory.cpp`)
- Type: OSLB (0x4F534C42)
- Limb types: Standard, LOD
- Parses: joint position (Vec3s), child/sibling indices, dList pointer
- Auto-discovers DLists referenced by limbs
- ~2,723 assets

### 2.2: OoT Skeleton Factory (`src/factories/oot/SkeletonFactory.cpp`)
- Type: OSKL (0x4F534B4C)
- Skeleton types: Normal, Flex
- Contains string paths to limb files
- ~197 assets

### 2.3: OoT Animation Factory (`src/factories/oot/AnimationFactory.cpp`)
- Type: OANM (0x4F414E4D)
- Standard animations + LegacyAnimation (12) + CurveAnimation (4)
- ~1,156 assets

### Steps
1. Study OTRExporter's skeleton/limb/animation exporters for binary format
2. Implement Limb factory first (standalone, simplest)
3. Implement Skeleton factory (references Limbs)
4. Implement Animation factory
5. Generate YAMLs for object files that contain skeleton/limb/animation entries
6. Verify against manifest

## Phase 3: Scene/Room System (~14,355 assets)

Most complex phase. New factories: Scene/Room, Collision, Path, Cutscene.

### 3.1: OoT Collision Factory (`src/factories/oot/CollisionFactory.cpp`)
- Type: OCOL (0x4F434F4C)
- Vertices, polygons, surface types, camera data, waterboxes
- ~203 assets

### 3.2: OoT Path Factory (`src/factories/oot/PathFactory.cpp`)
- Type: OPTH (0x4F505448)
- Array of Vec3s waypoints
- ~28 assets

### 3.3: OoT Cutscene Factory (`src/factories/oot/CutsceneFactory.cpp`)
- Type: OCVT (0x4F435654)
- Command stream: camera, actor actions, text boxes
- ~73 assets

### 3.4: OoT Scene/Room Factory (`src/factories/oot/SceneRoomFactory.cpp`)
- Type: OROM (0x4F524F4D) for both scenes and rooms
- Command-based format with headers, alternate headers
- References collision, paths, cutscenes as sub-assets
- Rooms contain DLists, VTX, textures
- ~489 assets (101 scenes + 388 rooms)
- Scenes also generate many auto-discovered assets (textures in rooms, etc.)

### Steps
1. Start with Collision and Path (simpler, standalone)
2. Implement Cutscene
3. Implement Scene/Room (depends on Collision, Path, Cutscene)
4. Start with a simple scene (few rooms, no alternate headers)
5. Handle alternate header sets
6. Generate scene YAMLs (complex: multi-segment configs)
7. Verify

## Phase 4: PlayerAnimation (~573 assets)

### 4.1: OoT PlayerAnimation Factory (`src/factories/oot/PlayerAnimFactory.cpp`)
- Type: OPAM (0x4F50414D)
- Raw frame data for Link's animations
- All from one DMA file (`link_animetion`)
- May be implementable as BLOB-like factory with OPAM type code

## Phase 5: Audio (~598 assets)

**Investigation needed first.** OoT audio uses different type codes and format version (v2) from NAudio v0/v1. Cannot reuse existing NAudio factories.

### 5.1: OoT Audio Root — Type OAUD, 1 asset
### 5.2: OoT SoundFont — Type OSFT, 38 assets
### 5.3: OoT Sample — Type OSMP, 459 assets
### 5.4: OoT Sequence — Type OSEQ, 110 assets

Strategy: reference Shipwright importers (`AudioSampleFactory.cpp`, `AudioSoundFontFactory.cpp`, `AudioSequenceFactory.cpp`) and OTRExporter serializers for binary format spec.

## Phase 6: Text (~6 assets)

### 6.1: OoT Text Factory (`src/factories/oot/TextFactory.cpp`)
- Type: OTXT (0x4F545854)
- Message table + message data per language
- 6 assets total

## Phase 7: Remaining (~3 assets)

- SYMBOL (1) → BLOB
- LIMBTABLE (1) → simple array or blob
- version/portVersion (2) → static metadata

## Key Files

- `src/factories/ResourceType.h` — add OoT type codes
- `src/Companion.cpp` — register OoT factories (lines 215-226 pattern)
- `CMakeLists.txt` — add BUILD_OOT option
- `src/factories/oot/` — new directory for all OoT factories
- `src/factories/DisplayListFactory.cpp` — GFX auto-discovery (verify OoT compat)
- `soh/dma/pal_gc.json` — ROM offsets for YAML generation
- OTRExporter source at `~/code/claude/Shipwright/OTRExporter/OTRExporter/` — serializers (write binary format)
- Shipwright importers at `~/code/claude/Shipwright/soh/soh/resource/importer/` — deserializers (read binary format back, effectively a spec)
- Shipwright type defs at `~/code/claude/Shipwright/soh/soh/resource/type/` — struct definitions and `SohResourceType.h` for type codes

## Verification

Each phase verified incrementally:
- Per-asset: `./soh/check.sh <rom> <asset-path>` against manifest
- Per-category: batch check all assets of a type
- Full: `./soh/verify.sh` for complete O2R comparison
- Target: all 35,386 files match reference SHA256 hashes
51 changes: 51 additions & 0 deletions docs/oot-audio-font-residue-analysis.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Fix 2 remaining font failures: drum→instrument stack residue

## Context
2 fonts (10_Fire_Temple, 35_Game_Over) fail because invalid instruments before any valid
instrument have non-zero residue bytes. These come from ZAPDTR's stack reuse: the compiler
places `DrumEntry drum` and `InstrumentEntry instrument` at the same stack location since
their scopes don't overlap. The behavior is **deterministic** — same ROM always produces
same output.

## Root Cause (verified)
In ZAPDTR's `ParseSoundFont` (ZAudio.cpp:215-305):
1. Drum loop: `DrumEntry drum = {0};` then overwrites fields with ROM data
2. Instrument loop: `InstrumentEntry instrument;` — POD fields uninitialized
3. Compiler reuses same stack slot → instrument POD fields contain drum residue

### Field mapping (DrumEntry → InstrumentEntry at same stack address):
| DrumEntry field | Offset | InstrumentEntry field | Value for last drum |
|-------------------|--------|-----------------------|---------------------|
| releaseRate | 0 | isValidInstrument | (we set explicitly) |
| **pan** | 1 | **loaded** | last drum's pan |
| **loaded** | 2 | **normalRangeLo** | last drum's loaded |
| (padding) | 3 | **normalRangeHi** | 0 |
| offset (low byte) | 4 | **releaseRate** | 0 |

### Evidence:
- Font 10: drums all have pan=64(0x40) → invalid inst loaded=0x40 ✓
- Font 35: drums all have pan=74(0x4A) → invalid inst loaded=0x4A ✓
- Both: normalRangeHi=0, releaseRate=0 (from drum padding/offset=0) ✓

## Fix
In `OoTAudioFactory.cpp`, after the drum loop, seed the instrument residue from the
last drum's fields using the mapping above:

```cpp
// After drum loop:
if (!drums.empty()) {
auto& [rr, pan, loaded, tuning, env, ref] = drums.back();
// DrumEntry→InstrumentEntry stack mapping
lastLoaded = pan; // drum.pan (offset 1) → inst.loaded (offset 1)
lastNormalRangeLo = loaded; // drum.loaded (offset 2) → inst.normalRangeLo (offset 2)
lastNormalRangeHi = 0; // padding (offset 3) → inst.normalRangeHi (offset 3)
lastReleaseRate = 0; // drum.offset=0 (offset 4) → inst.releaseRate (offset 4)
}
```

## Files to modify
- `src/factories/oot/OoTAudioFactory.cpp` — add drum residue seeding after drum loop

## Verification
- `python3 soh/tools/test_assets.py soh/roms/pal_gc_0227d7.z64 --category audio`
- Expected: 598/598 pass (was 596/598)
Loading