From 2cf919235813d088f6e8b71000a950bac1c032e7 Mon Sep 17 00:00:00 2001 From: briaguya <70942617+briaguya0@users.noreply.github.com> Date: Thu, 23 Jul 2026 23:31:08 -0400 Subject: [PATCH 01/11] Add .meta alias fallback test kit (meta-alias-testing/) Testing branch scaffolding for the libultraship .meta alias fallback work (Kenix3/libultraship#1165, which also needs the #984 .meta resolution fix). Expands the manual test from HarbourMasters/Shipwright@f5d2e58: a .meta file aliases the boot ship logo DisplayList to a renamed copy, and we watch the title screen to see which asset loaded. This kit covers the full real/meta/ target matrix as five committed mod .o2r files, and recolors the assets (default = green, replacement = magenta) so the loaded one is obvious. - meta-alias-testing/README.md test matrix, run steps, color legend - meta-alias-testing/PLAN.md implementation + test plan - regen-assets.sh / build-o2r.sh reproduce the assets and archives - o2r/case{1..5}-*.o2r the five committed test archives - .gitignore re-include the *.o2r / *.meta fixtures - README.md note the testing branch + link the kit Co-Authored-By: Claude Opus 4.8 --- README.md | 5 + meta-alias-testing/.gitignore | 5 + meta-alias-testing/PLAN.md | 180 ++++++++++++++++++ meta-alias-testing/README.md | 98 ++++++++++ meta-alias-testing/build-o2r.sh | 41 ++++ .../o2r/case1-real-meta-target.o2r | Bin 0 -> 7738 bytes .../o2r/case2-real-meta-notarget.o2r | Bin 0 -> 4197 bytes .../o2r/case3-noreal-meta-target.o2r | Bin 0 -> 4214 bytes meta-alias-testing/o2r/case4-meta-only.o2r | Bin 0 -> 677 bytes meta-alias-testing/o2r/case5-real-nometa.o2r | Bin 0 -> 3927 bytes meta-alias-testing/regen-assets.sh | 53 ++++++ .../nintendo_rogo_static/fancyShipDL.xml | 21 ++ .../textures/nintendo_rogo_static/gShipLogoDL | 21 ++ .../nintendo_rogo_static/gShipLogoDL.meta | 6 + .../mat_fancyShipDL_f3d_material | 20 ++ .../mat_fancyShipDL_f3d_material_001 | 20 ++ .../mat_fancyShipDL_f3d_material_002 | 20 ++ .../mat_fancyShipDL_f3d_material_003 | 20 ++ .../mat_gShipLogoDL_f3d_material | 20 ++ .../mat_gShipLogoDL_f3d_material_001 | 20 ++ .../mat_gShipLogoDL_f3d_material_002 | 20 ++ .../mat_gShipLogoDL_f3d_material_003 | 20 ++ 22 files changed, 590 insertions(+) create mode 100644 meta-alias-testing/.gitignore create mode 100644 meta-alias-testing/PLAN.md create mode 100644 meta-alias-testing/README.md create mode 100755 meta-alias-testing/build-o2r.sh create mode 100644 meta-alias-testing/o2r/case1-real-meta-target.o2r create mode 100644 meta-alias-testing/o2r/case2-real-meta-notarget.o2r create mode 100644 meta-alias-testing/o2r/case3-noreal-meta-target.o2r create mode 100644 meta-alias-testing/o2r/case4-meta-only.o2r create mode 100644 meta-alias-testing/o2r/case5-real-nometa.o2r create mode 100755 meta-alias-testing/regen-assets.sh create mode 100644 meta-alias-testing/src/assets/textures/nintendo_rogo_static/fancyShipDL.xml create mode 100644 meta-alias-testing/src/assets/textures/nintendo_rogo_static/gShipLogoDL create mode 100644 meta-alias-testing/src/assets/textures/nintendo_rogo_static/gShipLogoDL.meta create mode 100644 meta-alias-testing/src/assets/textures/nintendo_rogo_static/mat_fancyShipDL_f3d_material create mode 100644 meta-alias-testing/src/assets/textures/nintendo_rogo_static/mat_fancyShipDL_f3d_material_001 create mode 100644 meta-alias-testing/src/assets/textures/nintendo_rogo_static/mat_fancyShipDL_f3d_material_002 create mode 100644 meta-alias-testing/src/assets/textures/nintendo_rogo_static/mat_fancyShipDL_f3d_material_003 create mode 100644 meta-alias-testing/src/assets/textures/nintendo_rogo_static/mat_gShipLogoDL_f3d_material create mode 100644 meta-alias-testing/src/assets/textures/nintendo_rogo_static/mat_gShipLogoDL_f3d_material_001 create mode 100644 meta-alias-testing/src/assets/textures/nintendo_rogo_static/mat_gShipLogoDL_f3d_material_002 create mode 100644 meta-alias-testing/src/assets/textures/nintendo_rogo_static/mat_gShipLogoDL_f3d_material_003 diff --git a/README.md b/README.md index 3ac29e465ff..1d84c1e95a1 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,8 @@ +> [!NOTE] +> **This is a testing branch** for the libultraship `.meta` alias fallback work +> ([Kenix3/libultraship#1165](https://github.com/Kenix3/libultraship/issues/1165)). See +> [`meta-alias-testing/`](meta-alias-testing/) for the test kit, cases, and instructions. + ![Ship of Harkinian](docs/shiptitle.darkmode.png#gh-dark-mode-only) ![Ship of Harkinian](docs/shiptitle.lightmode.png#gh-light-mode-only) diff --git a/meta-alias-testing/.gitignore b/meta-alias-testing/.gitignore new file mode 100644 index 00000000000..82fbaf57bbd --- /dev/null +++ b/meta-alias-testing/.gitignore @@ -0,0 +1,5 @@ +# The repo ignores *.o2r and *.meta globally (build artifacts / sidecars). The +# archives and source assets under this directory are intentional test fixtures, +# so re-include them here. +!o2r/*.o2r +!**/*.meta diff --git a/meta-alias-testing/PLAN.md b/meta-alias-testing/PLAN.md new file mode 100644 index 00000000000..e1016f51eaf --- /dev/null +++ b/meta-alias-testing/PLAN.md @@ -0,0 +1,180 @@ +# `.meta` alias fallback (libultraship #1165) + +## Context + +libultraship #1165 asks that a `.meta` alias file behave as *"prefer the target, otherwise +use the real asset at the original path."* The motivating use case is cross-game mod +compatibility (SoH/OoT ↔ 2Ship/MM): a base archive can ship a `.meta` that aliases +`gLinkHumanSkel → gLinkChildSkel`, so a single unmodified mod works in both games — but when +no mod is loaded and the target is absent, the game must fall back to its native asset instead +of failing. + +**Current reality (verified):** the `.meta` alias mechanism is broken end-to-end in this +libultraship checkout — matching the still-open #984. `Archive::IndexFile` +(`libultraship/src/ship/resource/archive/Archive.cpp:174`) strips the `.meta` suffix and never +indexes the sidecar under its own name, so the loader's lookup of `filePath + ".meta"` +(`ResourceLoader.cpp:193`) can never resolve. So this work necessarily makes `.meta` resolution +functional (resolving #984's core) *and* adds the #1165 fallback. Confirmed intended behavior +for all four real/target combinations (user chose full support): + +| real asset | `.meta` | alias target | result | +|---|---|---|---| +| present | present | present | **load target** | +| present | present | absent | **load real (fallback — #1165)** | +| absent | present | present | **load target (#984 reachability)** | +| absent | present | absent | **fail** | +| present | absent | — | load real (legacy — unchanged baseline) | + +The verified load path: `ResourceManager::LoadResourceProcess` (`ResourceManager.cpp:101`) loads +the file at the requested path via `LoadFileProcess`, **bails at line 154 if it's null**, then +passes it to `ResourceLoader::LoadResource`, which is the *only* place `.meta` sidecars are +resolved. All archive types (`O2rArchive`, `OtrArchive`, `FolderArchive`) funnel indexing +through the single `Archive::IndexFile`. + +## The engine fix (3 changes in `libultraship/`) + +### 1. `Archive::IndexFile` — expose `.meta` sidecars for loading +`libultraship/src/ship/resource/archive/Archive.cpp:174` + +Keep indexing the base name (so the resource stays requestable/discoverable as `foo`) **and +also** index the sidecar under its literal `foo.meta` name so `LoadFileProcess(path + ".meta")` +can find it: + +```cpp +void Archive::IndexFile(const std::string& filePath) { + if (filePath.length() > 5 && filePath.substr(filePath.length() - 5) == ".meta") { + IndexFile(filePath.substr(0, filePath.length() - 5)); // base name (as today) + (*mHashes)[CRC64(filePath.c_str())] = filePath; // NEW: literal `foo.meta` + return; + } + (*mHashes)[CRC64(filePath.c_str())] = filePath; +} +``` +A real `foo` and a `foo.meta` both resolve the base key to `foo` (real wins / same value); when +only `foo.meta` exists, the base key points at a non-existent physical `foo`, so +`LoadFileProcess("foo")` returns null (handled by change 2). + +### 2. `ResourceManager::LoadResourceProcess` — reach the loader for `.meta`-only resources +`libultraship/src/ship/resource/ResourceManager.cpp:153-158` + +Today it returns early when the direct file is missing, before the loader can resolve a `.meta`. +Only bail when *neither* the real file nor a `.meta` exists; otherwise fall through with a +possibly-null `file` (the loader handles null): + +```cpp +auto file = LoadFileProcess(identifier.Path); +if (file == nullptr && LoadFileProcess(identifier.Path + ".meta") == nullptr) { + SPDLOG_TRACE("Failed to load resource file at path {}", identifier.Path); + mResourceCache[identifier] = ResourceLoadError::NotFound; + return nullptr; +} +``` + +### 3. `ResourceLoader::LoadResource` — prefer target, else fall back to real +`libultraship/src/ship/resource/ResourceLoader.cpp:185-208` + +Remove the leading `fileToLoad == nullptr` early-return (a `.meta`-only alias legitimately has a +null `fileToLoad`) and restructure the `.meta` branch so the target is preferred, the real asset +is the fallback, and the null check moves *after* resolution: + +```cpp +if (initData == nullptr) { + auto metaFileToLoad = + Context::GetRawInstance()->GetResourceManager()->LoadFileProcess(filePath + ".meta"); + + if (metaFileToLoad != nullptr) { + auto metaInit = ReadResourceInitData(filePath, metaFileToLoad); + auto aliasedFile = Context::GetRawInstance()->GetResourceManager()->LoadFileProcess(metaInit->Path); + + if (aliasedFile != nullptr) { // target exists -> load target + fileToLoad = aliasedFile; + initData = metaInit; + } else if (fileToLoad != nullptr) { // target absent, real present -> fall back (#1165) + SPDLOG_TRACE("Meta alias target '{}' missing; falling back to real asset '{}'.", + metaInit->Path, filePath); + initData = ReadResourceInitDataLegacy(filePath, fileToLoad); + } + // else: target and real both absent -> fileToLoad stays null -> fail below + } else if (fileToLoad != nullptr) { + initData = ReadResourceInitDataLegacy(filePath, fileToLoad); + } +} + +if (fileToLoad == nullptr) { + SPDLOG_ERROR("Failed to load file at path {}.", filePath); + return nullptr; +} +``` +Fallback deliberately reuses `ReadResourceInitDataLegacy` — i.e. "behave exactly as if the +`.meta` weren't there" — reading the real asset's own embedded OTR header / XML. `ReadResourceInitData` +(the JSON parser, `ResourceLoader.cpp:156`) is reused unchanged for the target path. + +## In-game test harness (SoH) — replicating & expanding commit f5d2e58 + +Test vehicle is the boot **Ship ("LUS") logo**, drawn first at startup from the `DisplayList` +resource `textures/nintendo_rogo_static/gShipLogoDL` +(`soh/soh/Enhancements/cosmetics/CustomLogoTitle.cpp:60`, `(Gfx*)gShipLogoDL`). It renders +immediately on launch (default boot sequence) and is skippable with A/B/Start — so each case is +observable within seconds of boot. f5d2e58 exercised only the "target present" cell; we expand +to the full matrix. + +**Fixture files** under `soh/assets/custom/textures/nintendo_rogo_static/` (packed verbatim into +`soh.o2r`; f5d2e58 confirms `.meta` and `.xml` pack as-is): +- **REAL** — `gShipLogoDL` (the existing XML DisplayList; already present). +- **META** — `gShipLogoDL.meta`, JSON exactly as f5d2e58: + ```json + { "path": "textures/nintendo_rogo_static/fancyShipDL.xml", + "type": "DisplayList", "format": "XML", "version": 0 } + ``` +- **TARGET** — `fancyShipDL.xml`, a **visually distinct** DisplayList so "loaded target" is + unmistakable vs "loaded real" — e.g. copy `gShipLogoDL` but drop two of the four + `CallDisplayList` tri groups (renders a visibly partial logo) or change the `SetCombineLERP` + color. It must keep valid `CallDisplayList` paths to the shared `*_tri_*` / `*_vtx_*` / + `mat_*` sub-resources (leave those in place for every case). + +**The five cases** — arrange REAL/META/TARGET present(✓)/absent(✗), rebuild, boot, observe the LUS logo: + +| # | REAL | META | TARGET | expected boot logo | +|---|---|---|---|---| +| 1 | ✓ | ✓ | ✓ | **fancy/partial** logo (target loaded) | +| 2 | ✓ | ✓ | ✗ | **normal** ship logo (fallback — #1165) | +| 3 | ✗ | ✓ | ✓ | **fancy/partial** logo (target loaded — #984 path) | +| 4 | ✗ | ✓ | ✗ | **no logo** (blank); `SPDLOG_ERROR "Failed to load file at path textures/nintendo_rogo_static/gShipLogoDL"` | +| 5 | ✓ | ✗ | — | **normal** ship logo (baseline regression) | + +Per-case loop: +1. Add/remove the three fixture files under `soh/assets/custom/textures/nintendo_rogo_static/`. +2. Repack assets: `cmake --build /var/home/briaguya/code/Shipwright/build --target GenerateSohOtr` + (regenerates `soh.o2r` and copies it to `build/soh/`). +3. Launch `/var/home/briaguya/code/Shipwright/build/soh/soh.elf`; watch the first (LUS) boot logo. +4. Set log level to **Trace** (CVar `gDeveloperTools.LogLevel`) to see the fallback `SPDLOG_TRACE` + (case 2/3) and the failure `SPDLOG_ERROR` (case 4). + +Shortcut for cases 1/2/5 (REAL stays in `soh.o2r`): drop a small override archive +`gShipLogoDL_test.o2r` (a zip containing just META and/or TARGET) into `build/soh/mods/` — mods +load after `soh.o2r` and override the path, avoiding a full `GenerateSohOtr` repack. Cases 3/4 +require REAL absent, so those need `soh.o2r` rebuilt without `gShipLogoDL`. + +## Build / verification + +- Rebuild engine + game after the libultraship changes: + `cmake --build /var/home/briaguya/code/Shipwright/build` (builds libultraship into `soh.elf`). +- Repack assets after each fixture change: `--target GenerateSohOtr` (see above). +- **Pass criteria:** all five rows produce the expected logo; case 2 & 3 log the fallback/target + traces; case 4 logs the failure and renders no logo; case 5 confirms no regression to normal + (no-`.meta`) loading. + +## Deliverable vs scaffolding +- **Deliverable (libultraship PR, #1165 + #984 core):** the 3 engine changes above. Work in the + `libultraship` submodule on a branch (fork remote `briaguya`). +- **Scaffolding:** the SoH fixture files are local test setup. Optionally keep one demonstrator + commit (as f5d2e58 did) but the fixtures are not part of the engine PR. + +## Critical files +- `libultraship/src/ship/resource/archive/Archive.cpp:174` — `IndexFile` (change 1) +- `libultraship/src/ship/resource/ResourceManager.cpp:153` — `LoadResourceProcess` (change 2) +- `libultraship/src/ship/resource/ResourceLoader.cpp:185` — `LoadResource` (change 3); + `ReadResourceInitData:156` / `ReadResourceInitDataLegacy:85` reused unchanged +- `soh/assets/custom/textures/nintendo_rogo_static/` — test fixtures +- `soh/soh/Enhancements/cosmetics/CustomLogoTitle.cpp:60` — where `gShipLogoDL` is drawn +- `CMakeLists.txt:248` — `GenerateSohOtr` (repacks `soh.o2r` from `soh/assets/custom`) diff --git a/meta-alias-testing/README.md b/meta-alias-testing/README.md new file mode 100644 index 00000000000..9c7c3c6c9f5 --- /dev/null +++ b/meta-alias-testing/README.md @@ -0,0 +1,98 @@ +# `.meta` alias fallback — manual test kit + +Test scaffolding for the libultraship `.meta` alias fallback work +([Kenix3/libultraship#1165](https://github.com/Kenix3/libultraship/issues/1165), which also +requires the `.meta` resolution fix from +[#984](https://github.com/Kenix3/libultraship/issues/984)). + +It reproduces and expands the manual test from +[HarbourMasters/Shipwright@f5d2e58](https://github.com/HarbourMasters/Shipwright/commit/f5d2e583444de41a1a43a00b9c3fca2bf782dc2d): +a `.meta` file aliases the boot **ship logo** DisplayList to a renamed copy, and we watch the +title screen to see which asset actually loaded. `f5d2e58` only covered "alias target present"; +this kit covers the full matrix, and recolors the assets so the loaded one is obvious. + +## What a `.meta` alias should do (#1165) + +A `.meta` sidecar means *"prefer the target asset, otherwise fall back to the real asset at the +original path."* Requesting resource `foo`: + +| real `foo` | `foo.meta` | alias target | expected result | +|---|---|---|---| +| present | present | present | load **target** | +| present | present | absent | load **real** (fallback — #1165) | +| absent | present | present | load **target** (meta-only alias — #984 path) | +| absent | present | absent | **fail** | +| present | absent | — | load **real** (unchanged legacy behavior) | + +## Test vehicle & color legend + +The vehicle is `textures/nintendo_rogo_static/gShipLogoDL`, the DisplayList drawn as the first +("LUS") logo at boot (`soh/soh/Enhancements/cosmetics/CustomLogoTitle.cpp`). The materials are +recolored so you can read the result off the title screen at a glance: + +| what you see | meaning | +|---|---| +| **green** ship logo | the **real / default** asset loaded (`gShipLogoDL`) | +| **magenta** ship logo | the **replacement / alias target** loaded (`fancyShipDL.xml`) | +| **brown** ship logo (normal) | the test `.o2r` did **not** take effect (soh.o2r's untouched logo) | +| **nothing** (blank where the logo should be) | the resource **failed to load** | + +## The five cases (`o2r/`) + +Each `.o2r` overrides only the ship logo. Because a mod that contains `gShipLogoDL.meta` +*shadows* soh.o2r's `gShipLogoDL` key, "real asset absent" is simulated simply by leaving the +real DisplayList out of the archive — no `soh.o2r` rebuild required. The heavy `*_tri_*` / +`*_vtx_*` geometry is never included, so it keeps resolving from soh.o2r and each archive stays a +few KB. + +| file | real | meta | target | current engine (broken) | after the fix | +|---|:---:|:---:|:---:|---|---| +| `case1-real-meta-target.o2r` | ✓ | ✓ | ✓ | **green** (meta ignored) | **magenta** | +| `case2-real-meta-notarget.o2r` | ✓ | ✓ | ✗ | **green** | **green** (fallback) | +| `case3-noreal-meta-target.o2r` | ✗ | ✓ | ✓ | **nothing** | **magenta** | +| `case4-meta-only.o2r` | ✗ | ✓ | ✗ | **nothing** | **nothing** (correct fail) | +| `case5-real-nometa.o2r` | ✓ | ✗ | — | **green** | **green** (regression check) | + +The engine fix is proven by **case 1 (green → magenta)** and **case 3 (nothing → magenta)**. +Cases 2/4/5 confirm no regressions. (The "current engine" column is the baseline captured before +any code change — see `BASELINE.md` once recorded.) + +## How to run a case + +1. Build/run SoH as usual (executable at `build/soh/soh.elf`, with `oot.o2r` / `soh.o2r` / + `mods/` alongside it). +2. Put **exactly one** case file in the mods folder, and remove any others: + ```sh + rm -f build/soh/mods/case*.o2r + cp meta-alias-testing/o2r/case1-real-meta-target.o2r build/soh/mods/ + ``` +3. (Recommended) set the log level to **Trace** so load decisions are visible — in-game + Developer Tools → Log Level, or set CVar `gDeveloperTools.LogLevel` to `0`. +4. Launch `build/soh/soh.elf`. The first logo shown is the one under test; press **A/B/Start** + to advance past the logos. Compare against the color legend. +5. **Restart** the game between cases (resources are cached for the life of the process), and + swap the `.o2r` in `mods/` first. + +### Reading the Trace log +- Alias resolved to target: the target path (`…/fancyShipDL.xml`) is loaded. +- Fallback (case 2): a trace like `Meta alias target '…/fancyShipDL.xml' missing; falling back to + real asset '…/gShipLogoDL'.` +- Failure (case 4): `Failed to load file at path textures/nintendo_rogo_static/gShipLogoDL.` + +## Rebuilding the kit + +```sh +./meta-alias-testing/regen-assets.sh # regenerate src/assets from soh's real ship logo +./meta-alias-testing/build-o2r.sh # zip src/assets subsets into o2r/case*.o2r +``` + +## Layout +``` +meta-alias-testing/ + README.md this file + PLAN.md the implementation + test plan + regen-assets.sh derives src/assets/ from soh's gShipLogoDL (recolor + alias) + build-o2r.sh assembles o2r/case*.o2r from src/assets/ + src/assets/… generated test assets (default green, replacement magenta, the .meta) + o2r/case*.o2r the five committed test archives +``` diff --git a/meta-alias-testing/build-o2r.sh b/meta-alias-testing/build-o2r.sh new file mode 100755 index 00000000000..e0945d6f268 --- /dev/null +++ b/meta-alias-testing/build-o2r.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash +# +# Assembles the five test .o2r archives under o2r/ from the assets in src/assets/. +# An .o2r is just a zip; entries must be stored at "textures/nintendo_rogo_static/..." +# so their CRC64 matches the paths the game requests. +# +# Drop exactly ONE of these into /mods/ (e.g. build/soh/mods/) at a time. +set -euo pipefail +cd "$(dirname "$0")" +ROOT="$(pwd)" +ASSETS="$ROOT/src/assets/textures/nintendo_rogo_static" + +command -v zip >/dev/null || { echo "error: 'zip' is required" >&2; exit 1; } + +DEFAULT_FILES=(gShipLogoDL mat_gShipLogoDL_f3d_material mat_gShipLogoDL_f3d_material_001 mat_gShipLogoDL_f3d_material_002 mat_gShipLogoDL_f3d_material_003) +TARGET_FILES=(fancyShipDL.xml mat_fancyShipDL_f3d_material mat_fancyShipDL_f3d_material_001 mat_fancyShipDL_f3d_material_002 mat_fancyShipDL_f3d_material_003) +META_FILE=(gShipLogoDL.meta) + +mkdir -p "$ROOT/o2r" +TMP="$(mktemp -d)" +trap 'rm -rf "$TMP"' EXIT + +build_case() { + local out="$1"; local mname="$2"; shift 2 + local dir="$TMP/$out" + mkdir -p "$dir/textures/nintendo_rogo_static" + printf '{"name":"%s","code_version":1}\n' "$mname" > "$dir/manifest.json" + local f + for f in "$@"; do + cp "$ASSETS/$f" "$dir/textures/nintendo_rogo_static/$f" + done + rm -f "$ROOT/o2r/$out.o2r" + ( cd "$dir" && zip -q -X -r "$ROOT/o2r/$out.o2r" manifest.json textures ) + echo "built o2r/$out.o2r" +} + +build_case case1-real-meta-target "meta-test 1 real+meta+target" "${DEFAULT_FILES[@]}" "${META_FILE[@]}" "${TARGET_FILES[@]}" +build_case case2-real-meta-notarget "meta-test 2 real+meta" "${DEFAULT_FILES[@]}" "${META_FILE[@]}" +build_case case3-noreal-meta-target "meta-test 3 meta+target" "${META_FILE[@]}" "${TARGET_FILES[@]}" +build_case case4-meta-only "meta-test 4 meta-only" "${META_FILE[@]}" +build_case case5-real-nometa "meta-test 5 real (no meta)" "${DEFAULT_FILES[@]}" diff --git a/meta-alias-testing/o2r/case1-real-meta-target.o2r b/meta-alias-testing/o2r/case1-real-meta-target.o2r new file mode 100644 index 0000000000000000000000000000000000000000..ff852ed0c09c856077d25a410f36b124543a642d GIT binary patch literal 7738 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 literal 0 HcmV?d00001 diff --git a/meta-alias-testing/o2r/case2-real-meta-notarget.o2r b/meta-alias-testing/o2r/case2-real-meta-notarget.o2r new file mode 100644 index 0000000000000000000000000000000000000000..4cf5cf4a10197db8b1efeb8fc474c0dd00fe2ef4 GIT binary patch literal 4197 zcmWIWW@h1H0D*v=-(w_ai+nHwvO$;^h;tM3GSgCvOZ2je^Yf~e@)C1Xm8_I__EZZ;>`R!B`d>Pt^gFnf#$&hCy-T=T2WG3lv=Eh zTbUfPvb@Z^lGMDE{P?2$^!)hZlEjkCWUyWlpehcq``g(g>a&5oCLq=$s5d=0BeTE< zXqbzS9>@!(-5a@@9e5nR|K{2mxurP$c=4-CzD>uZE~OK&f=qB z&ZqN-r1QS{t}qpr{-h8)slOyE;mUR8g8A{nN7X`Z8~y%dAKh1RmN#>bB74y8%Nm)C zyC3c5-5z78$vOYq!@742ATJ}FZMQ`1sRk1RLpCb|gB?L<=O&iKBRM@j%{V0}t>SuQs$hKE3+9?Y<2gGmAAZU0cq}?!P2YWA(NIR(Y}cW`cVsELpp0DGTRAF>U`X zO8bP}4@BfH?AFz`kTERk>)YRSU`_#3l<*82scp=SwbF@8xTKZs&K8&*{>hTflzqJ< zOk&2pdmCGM-i94xaoZ?faiV%s(3Y7ZxoW9xn>5+j^K~YC})q3`c<^L9|DG09If9JkqLtC$Z-6W?z zc84trS;Aq98BY2>UwZ!W)R)?ffv3(#pRisP8Mft-lg?q&>eL+%HJ9HsO6)&5GfbDo zI&8Y;bS+y&dxsm%3JdppoL9NWz0W?;-XtgI*TS!&P3f~vGllCfnN@${G5747KMV#l+`f-l+gQ>Dm%3DG5IXMd>DT{8D&`MecO zlU%}uJcG7A+iW5qJMDl@#J950N)4&04+09le#vgUK1;&v_uSKS>*lVUefwATezwW) z>}G8F74P%v_ryz#uipM>)}5u-zU$fDyb9x0nVB1#t}486x+=9gwC2RGs4K>zMpjGW zN>292<#!Dq@Myi82X&;Cjh{2;ZuLHWb7 zW&7*4DYjg)^Qpeo{n2vYzVCMqw@y8LD|qHy=4)IZ9A;1ZSj8*;A@5tN=w@Nx^QCs- z3QG??yV`iwq+qhdf}>JZN46clJ5NF&Lb>DaUTGbL4el{>ek|<&%P!9Jd~(2)Orgx6 zhlg373&-?q(CP2V_Os^Ft$%G5bfqJ-%FSS}d*7u`c1)?qT#kX2d8l)XCAz_)Yr6|AF}1|VYOv3OyR9(^w%$*^6&f4M-y_` zquUi`sx2&al$Kp8rfbL<`gdJ!(*iq#-^?v1Cc7?@m{>AB=S&>`vh|O`ogKA;>v!*M zWQaFo{Ojv&s?4x?(ktH(ekKufxmkAo@u}xUj?A=JxA4V^vbfDr9vkPF{Fym9ET!q` z=PCD>yNAtJyzyb#sg|IZ-ai%9>{v9b7u!z$<^SZHLlcMP5iX_Q)-3&N0$A6@X_)?> zpgzH@T()hC)Yf03r^LLpT*|Y;*Uod^qu$TiWt#Zqh#A*kl};houFNSvBt+BRc7LhT zTq=9hf8r~qt|Q?>+CkgJKCF?52|J)w`L8H+r*`z*gmA9EJW`$0+hok1hpw1gId^4w z{_FLBcg-mId~nIp|4Bjj`zP@gZbf6Kb zm*Vo~%5+HU%G?^d#^6rJm0QJ!x1IEpS!5*P@@DP)jYe!yALiUFlaJnBwyxYX%Ha36 zF!hO2b`JJluSLb@y?@^HIYZnkYyI!{7@uIluoP+VavQ9EZ9Pr!*qo)*g{Ta;{Uz8D$E7qfkpM&#wEO&ca0sc%g+j(=|U<2EcIBX zR_3DnM-J>K~!n2vZ26K|Lf00BLIhGU3{A^p`+pA-W#uJtmM@5Z50dXckg}1NWaG-b0v; z-mO8HT@JJYzdH$aa?tHUZ@wbz`przCT}H&))eOw26#2^#pIxA82;nboV2UB%E_8d) hiyDMI{oI7?K}rV!-mJhNU|`?`LRnzI8-Yd<7yv=~wa)+m literal 0 HcmV?d00001 diff --git a/meta-alias-testing/o2r/case3-noreal-meta-target.o2r b/meta-alias-testing/o2r/case3-noreal-meta-target.o2r new file mode 100644 index 0000000000000000000000000000000000000000..91729938ade2dae3ed876f77b35eb590918b2d6a GIT binary patch literal 4214 zcmWIWW@Zs#U|`^22-x{OMstJM7GogK1c-ToI5#mbGcC2aL@%p2KW}x|Dep7Bp__tE z`RaOJ(e>2T3p5C0*tpeOsQXf|_mq+;lRgIrJw6@O-DUjw^OQ9_**h5mfTnW+&0zqW z4rV|JP9R;9T2WG3lv=EhTbUfPvb@Z^lGMDE{P?2$^!)hZlEjkCWUyX@2d=A>$!IVE z9nQ+YU`J4IZemG%T4G*uWpGAjfs0RknsG`zP%O15GchOLz`*!yNNoOW1CiRey4I)m z6B?w}PM+jd)4Isf;lY;3cpav=WYGeadm-Omr_b&e*wvotUu|f2{QCEK)ipjLpPd3)kE_Bw7A*!9CthB0|-tKPOX4+@T)u(bNe z#Z-KVGs8FGQ^pNGmT$Q&6SZ=BewZA-$@5%z4duzo9Nm54uG7NnQ^OWIze%`$Q#)e(5k%Fmv>a6pZYDyxRHYeP? z9r|=(Mo5$M!K>HTv&Lm7-FQ>3SN^MXSzg@h^?#*&cKuGc^s3%)<$HH8_7&S7^KZ-8 zCG%=l`R<2lA(t;Dv95N!(7MX>o>Q%^)=sXOwnn?w?|K#T^GUweinB#c@?zchLFpHf z2A%YGW#|CYU=cOapb@q-_)J-Zmx*cl*|vJ=_$D=oWxo)- z%~xKXVHMaa& z``RN=Iqaq6!7p-_UZ;O^Opz3pgeY;o+Z`+SR8;h=?FBe(cmT-8smax}S{ip3S zeh4gi)BDZmmHouq%$!P31hhNeyce+9!nl5QtVw@g8+BlF zQb8o+>L))>hdy1GQ`!`(`9;n|NT1g-=iKT5_0Lwp@AqAc|6OYM@>#QI_y40y_Q|Wt zKDgKCpJV7_dZqV;-}LE)hqY!;-@}+FzUq1k@7bkmo@Azck6-9OOXc`<%gXE>bT#lmP6h>e(myLlzmtY!1Y7^>J(o?wSYQwH_Kg2V0pYJ_~uadyZI_ z<~#}ZWiO_6F6sI9=LKuYZU2?)8lUJaUUu{NvAw5uPfl9E9Tn>7clP&EKQ+~wrjRY` zep#$H52~n*;kvdt_{D>-`$goYJGeKS3sv0WE0Kt?ZtUx>s%Ed-bMB|$q97GfmjyK^ z_!p#Do(*=e<5l}-F|TD`uWrw|KBI(9S4Ah6?uik2HTlD(6-V#<+3=8wY10hl<6AcR z$CmGkd3|4IuldE?Md61t-c+zCw}+ZO-+uJw7K_ZnsAQpaYTc0*s)=jz*DRZ?w#)a+ z8g0Kd(-w#I?YC;iqt7~lDUY`wTws4dpn)*s;iwnqCOiKO-(?oM=-k5LCtp`V4uLnM=v+EB+;~cBUiHnkHh!h zTstGT6sI3Aes#&W>6p}oCp(rrQv6#!^^x@b$8XnJd=$+2bpDWZ-Z$SBroz&n6k;d! zmt-YexvpF=KVJB#TF7mq-=FNG`zp@zX3kM$588cMBa?CWquspQV+=Jp=YM-x_YT;> zVPukHMzsCVJ5WHkGXRqv!;(f23#B^+(Tclw1k%93Ai(g}F%C#0^_4(2V%G-hEkOWC zTPu(W*M_6-gz9?q-V?|yi0cm$G>cGQ3O%IITgeE!T$u^En}A(L#M^Zi*ha@6Mg;6K z#Ag?%wnO+Ul!Fqx(CtAl(Gd3B1SSsrenKnX(2YjVeF&pFcnBGdo)-hWS%F0^0|O@z NDgqipSL>fl=m6m&`m+7 ze04pq=z8kv1sVi0Z2tVYNNZ}zlu4h1gC3s_>h3cB{CUb6p6s0r0YIaWvdEb0jmk03J!to3jZDVfk9PBJk1^Ebod4}%-8*2&Ffz$8BOHz%>_E3OFaq(CMi2`n5+GV} z2P;Se1A_p=TgPZ1jTFcr8?kEx1v3PIw1EN-t_??^qq-hF*gk6W1>va-C)ypq(sl>GRj{Pg_z;*!LY%w(`$5uhp#uqW)6h&|O{VqnN-Wni!) zs5dvUBtAVjBeTECQ7ueOF>0fPVcYJ#FdE0#(Hf9!UUb?oNm)(C! zp2q5J1+4O7^UVbJPFS*b(^3}Bhhp0PTa@+*yB~A;)q%}oowDJ@=`FLv$d zBWt4+%R4TLYue;HeFSC;dE7Mb!g3rUr|?#MUAYM#Feb7`J^v;CC&06fA`URQ2Irr!Hv_}t#yED zuz(tA&(s-y zf@jWUzQ*;zVfM6-RlMRK^1h{tZWi`EUuqYwu=LQgtBqGp3MNY|I4V_jWZU7p^CSc! zlsoS3mDW+%;2tyQ$HM-jwU5oiD9UI-vCT?#FTD^SNvE4^h=k30F=D~YQ zeNDUhAzS_)R$CUs6yAD9fBoVq|GxixG$DsQx?N$W+QL#tY1yS>x`v#gf7j(UEwD5A z&D?Tgvg;y=i6zr>&cyLATmLBB*-J!Y$W!tt$ZT%&BO3X{kr93Nq?L6l_ z>iwKuriovUm~s78=@fG9%AE2;LNx7d_m?WorLs5uC%$6pIub6V9kgBS!y1X0umf6^ z|B6C)YDdpa2Yze-d9o{&Dqf z63e7sxs~ibeRfCU)U)3A8a8lW&0ZA#Q*pL;&i`|41RwLQ=ceh=V0&kT2y@A`{zxcGsLYj{+@6B zZFwQ^fhfD&HfCA2>tzcbFN(Y0n7_8Tu;GcuWiBBD#TB*^wR2YS1e{ppX|ZgwMajnI zi;;nRG22&ZL|%T{v|+-L`qotA__yXxPA+1_Pj2e*&3x}a{q?7ri?6Hw^5w~z(X+Q9 z;mJp>XMd+u%oba&doIi8(bW{g?&T`WQjcY7WiGmZ gShipLogoDL + green materials +# * replacement/target -> fancyShipDL.xml + magenta materials +# Plus the alias sidecar gShipLogoDL.meta (path -> fancyShipDL.xml). +# +# The heavy geometry (*_tri_* / *_vtx_*) and the render-mode reverts are NOT +# copied here on purpose: the test .o2r files never shadow those keys, so they +# keep resolving from soh.o2r and each archive stays tiny. +set -euo pipefail +cd "$(dirname "$0")/.." # repo root + +SRC=soh/assets/custom/textures/nintendo_rogo_static +OUT=meta-alias-testing/src/assets/textures/nintendo_rogo_static +mkdir -p "$OUT" + +GREEN='' +MAGENTA='' + +# --- default / real: unchanged DisplayList, materials recolored solid green --- +cp "$SRC/gShipLogoDL" "$OUT/gShipLogoDL" +for suf in "" _001 _002 _003; do + sed -E "s#]*>#$GREEN#" \ + "$SRC/mat_gShipLogoDL_f3d_material$suf" > "$OUT/mat_gShipLogoDL_f3d_material$suf" +done + +# --- replacement / target: same geometry, its own materials, recolored magenta --- +# Repoint only the material calls (mat_gShipLogoDL_f3d_material*) at a private set; +# the mat_revert_* and *_tri_* calls keep pointing at the shared soh.o2r resources. +sed 's#mat_gShipLogoDL_f3d_material#mat_fancyShipDL_f3d_material#g' \ + "$SRC/gShipLogoDL" > "$OUT/fancyShipDL.xml" +for suf in "" _001 _002 _003; do + sed -E "s#]*>#$MAGENTA#" \ + "$SRC/mat_gShipLogoDL_f3d_material$suf" > "$OUT/mat_fancyShipDL_f3d_material$suf" +done + +# --- alias sidecar (same shape as HarbourMasters/Shipwright commit f5d2e58) --- +cat > "$OUT/gShipLogoDL.meta" <<'JSON' +{ + "path": "textures/nintendo_rogo_static/fancyShipDL.xml", + "type": "DisplayList", + "format": "XML", + "version": 0 +} +JSON + +echo "Regenerated assets in $OUT" +ls -1 "$OUT" diff --git a/meta-alias-testing/src/assets/textures/nintendo_rogo_static/fancyShipDL.xml b/meta-alias-testing/src/assets/textures/nintendo_rogo_static/fancyShipDL.xml new file mode 100644 index 00000000000..d06ca7d7b9e --- /dev/null +++ b/meta-alias-testing/src/assets/textures/nintendo_rogo_static/fancyShipDL.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/meta-alias-testing/src/assets/textures/nintendo_rogo_static/gShipLogoDL b/meta-alias-testing/src/assets/textures/nintendo_rogo_static/gShipLogoDL new file mode 100644 index 00000000000..5e5435d84f2 --- /dev/null +++ b/meta-alias-testing/src/assets/textures/nintendo_rogo_static/gShipLogoDL @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/meta-alias-testing/src/assets/textures/nintendo_rogo_static/gShipLogoDL.meta b/meta-alias-testing/src/assets/textures/nintendo_rogo_static/gShipLogoDL.meta new file mode 100644 index 00000000000..e0874a7fe3d --- /dev/null +++ b/meta-alias-testing/src/assets/textures/nintendo_rogo_static/gShipLogoDL.meta @@ -0,0 +1,6 @@ +{ + "path": "textures/nintendo_rogo_static/fancyShipDL.xml", + "type": "DisplayList", + "format": "XML", + "version": 0 +} diff --git a/meta-alias-testing/src/assets/textures/nintendo_rogo_static/mat_fancyShipDL_f3d_material b/meta-alias-testing/src/assets/textures/nintendo_rogo_static/mat_fancyShipDL_f3d_material new file mode 100644 index 00000000000..ae791741bbe --- /dev/null +++ b/meta-alias-testing/src/assets/textures/nintendo_rogo_static/mat_fancyShipDL_f3d_material @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/meta-alias-testing/src/assets/textures/nintendo_rogo_static/mat_fancyShipDL_f3d_material_001 b/meta-alias-testing/src/assets/textures/nintendo_rogo_static/mat_fancyShipDL_f3d_material_001 new file mode 100644 index 00000000000..ae791741bbe --- /dev/null +++ b/meta-alias-testing/src/assets/textures/nintendo_rogo_static/mat_fancyShipDL_f3d_material_001 @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/meta-alias-testing/src/assets/textures/nintendo_rogo_static/mat_fancyShipDL_f3d_material_002 b/meta-alias-testing/src/assets/textures/nintendo_rogo_static/mat_fancyShipDL_f3d_material_002 new file mode 100644 index 00000000000..ae791741bbe --- /dev/null +++ b/meta-alias-testing/src/assets/textures/nintendo_rogo_static/mat_fancyShipDL_f3d_material_002 @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/meta-alias-testing/src/assets/textures/nintendo_rogo_static/mat_fancyShipDL_f3d_material_003 b/meta-alias-testing/src/assets/textures/nintendo_rogo_static/mat_fancyShipDL_f3d_material_003 new file mode 100644 index 00000000000..12924296231 --- /dev/null +++ b/meta-alias-testing/src/assets/textures/nintendo_rogo_static/mat_fancyShipDL_f3d_material_003 @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/meta-alias-testing/src/assets/textures/nintendo_rogo_static/mat_gShipLogoDL_f3d_material b/meta-alias-testing/src/assets/textures/nintendo_rogo_static/mat_gShipLogoDL_f3d_material new file mode 100644 index 00000000000..83cff39dce2 --- /dev/null +++ b/meta-alias-testing/src/assets/textures/nintendo_rogo_static/mat_gShipLogoDL_f3d_material @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/meta-alias-testing/src/assets/textures/nintendo_rogo_static/mat_gShipLogoDL_f3d_material_001 b/meta-alias-testing/src/assets/textures/nintendo_rogo_static/mat_gShipLogoDL_f3d_material_001 new file mode 100644 index 00000000000..83cff39dce2 --- /dev/null +++ b/meta-alias-testing/src/assets/textures/nintendo_rogo_static/mat_gShipLogoDL_f3d_material_001 @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/meta-alias-testing/src/assets/textures/nintendo_rogo_static/mat_gShipLogoDL_f3d_material_002 b/meta-alias-testing/src/assets/textures/nintendo_rogo_static/mat_gShipLogoDL_f3d_material_002 new file mode 100644 index 00000000000..83cff39dce2 --- /dev/null +++ b/meta-alias-testing/src/assets/textures/nintendo_rogo_static/mat_gShipLogoDL_f3d_material_002 @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/meta-alias-testing/src/assets/textures/nintendo_rogo_static/mat_gShipLogoDL_f3d_material_003 b/meta-alias-testing/src/assets/textures/nintendo_rogo_static/mat_gShipLogoDL_f3d_material_003 new file mode 100644 index 00000000000..56e29951a9b --- /dev/null +++ b/meta-alias-testing/src/assets/textures/nintendo_rogo_static/mat_gShipLogoDL_f3d_material_003 @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + From 93a745bf2b34b554f4b5d87c571dbaa233bba0a3 Mon Sep 17 00:00:00 2001 From: briaguya <70942617+briaguya0@users.noreply.github.com> Date: Thu, 23 Jul 2026 23:40:41 -0400 Subject: [PATCH 02/11] Record baseline results (current engine, before fix) All five test cases plus the no-mod control observed on the unmodified libultraship submodule (2bfbde3a); results match predictions. .meta aliases are confirmed non-functional today: ignored when a real asset exists (cases 1/2 load green, not magenta), unresolvable when it doesn't (cases 3/4 blank). The fix must flip case1 green->magenta and case3 nothing->magenta. Co-Authored-By: Claude Opus 4.8 --- meta-alias-testing/BASELINE.md | 37 ++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 meta-alias-testing/BASELINE.md diff --git a/meta-alias-testing/BASELINE.md b/meta-alias-testing/BASELINE.md new file mode 100644 index 00000000000..633fe77570d --- /dev/null +++ b/meta-alias-testing/BASELINE.md @@ -0,0 +1,37 @@ +# Baseline — current engine (before the fix) + +Captured on 2026-07-23 against the **unmodified** libultraship submodule +(`2bfbde3a`), by dropping each archive into `build/soh/mods/` one at a time and +reading the color of the first (LUS) boot logo. + +Legend: green = real/default loaded · magenta = replacement/target loaded · +brown = mod didn't take effect (soh.o2r's untouched logo) · nothing = load failed. + +| case | real | meta | target | observed now | expected after fix | +|---|:---:|:---:|:---:|---|---| +| control (no mod in `mods/`) | — | — | — | **brown** (original) | brown | +| `case1-real-meta-target` | ✓ | ✓ | ✓ | **green** (meta ignored) | **magenta** | +| `case2-real-meta-notarget` | ✓ | ✓ | ✗ | **green** | green (fallback) | +| `case3-noreal-meta-target` | ✗ | ✓ | ✓ | **nothing** | **magenta** | +| `case4-meta-only` | ✗ | ✓ | ✗ | **nothing** | nothing (correct fail) | +| `case5-real-nometa` | ✓ | ✗ | — | **green** | green (regression) | + +All observations matched predictions. + +## What the baseline confirms + +- The overlay approach is sound: **case1 = green** (not brown) means a `mods/` + archive loads and shadows soh.o2r's `gShipLogoDL` *before* the boot logo draws. +- `.meta` aliases are **non-functional** on the current engine: + - When a real asset exists, the `.meta` is silently ignored (cases 1, 2 load the + real/default green asset instead of the magenta target). + - When only a `.meta` exists, the resource can't be resolved at all (cases 3, 4 + render nothing). + +## What the fix must change + +- **case1: green → magenta** — with the real asset present, the alias target is + now preferred. +- **case3: nothing → magenta** — a meta-only alias resolves to its target. +- cases 2, 4, 5 stay the same (fallback / correct-fail / no-regression), though + case2's Trace log should now show the fallback path. From dab435010379ce021e1064d12a817094e028868b Mon Sep 17 00:00:00 2001 From: briaguya <70942617+briaguya0@users.noreply.github.com> Date: Fri, 24 Jul 2026 00:05:09 -0400 Subject: [PATCH 03/11] Test engine change #1 (index .meta sidecars); bump libultraship Bumps the libultraship pointer to the change-#1 commit (index .meta under its literal name) and records results in STATUS.md. Observed vs baseline: - case1 green -> magenta (alias now resolves once the .meta is indexed) - case2 green -> nothing (expected temporary regression; no fallback until #3) - case3/4 nothing, case5 green (unchanged) Note: reproducing this branch's submodule state needs the meta-alias-fallback branch pushed to the fork (remote `briaguya`). Co-Authored-By: Claude Opus 4.8 --- libultraship | 2 +- meta-alias-testing/STATUS.md | 40 ++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 meta-alias-testing/STATUS.md diff --git a/libultraship b/libultraship index 2bfbde3a72c..ce374750142 160000 --- a/libultraship +++ b/libultraship @@ -1 +1 @@ -Subproject commit 2bfbde3a72c119f8073ad762ec6be131dff5df66 +Subproject commit ce3747501424b4568d139a687bcabad649db03ee diff --git a/meta-alias-testing/STATUS.md b/meta-alias-testing/STATUS.md new file mode 100644 index 00000000000..96c4aa9f44d --- /dev/null +++ b/meta-alias-testing/STATUS.md @@ -0,0 +1,40 @@ +# Test status — engine changes + +Tracks the five test cases + control as the three libultraship engine changes +(branch `meta-alias-fallback`) are applied **one at a time**. See `BASELINE.md` +for the pre-change reference and `README.md` for how to run each case. + +Vehicle: the boot ship logo. green = real/default loaded · magenta = +replacement/target loaded · brown = original (mod not in effect) · nothing = +load failed. + +## Engine changes + +1. `Archive::IndexFile` — also index a `.meta` sidecar under its literal + `foo.meta` name so `LoadFileProcess(path + ".meta")` resolves. **[applied]** +2. `ResourceManager::LoadResourceProcess` — don't bail when the real file is + missing but a `.meta` exists; fall through so the loader can resolve the + alias. **[pending]** +3. `ResourceLoader::LoadResource` — prefer the alias target, else fall back to + the real asset at the original path; move the null-file check after + resolution. **[pending]** + +## Results (observed 2026-07-24, matching predictions) + +| case | real/meta/target | baseline | +#1 | +#2 | +#3 (final) | expected final | +|---|:---:|---|---|---|---|---| +| control (no mod) | — | brown | brown | — | — | brown | +| `case1` | ✓/✓/✓ | green | **magenta** | — | — | magenta | +| `case2` | ✓/✓/✗ | green | **nothing** ⚠ | — | — | green | +| `case3` | ✗/✓/✓ | nothing | nothing | — | — | magenta | +| `case4` | ✗/✓/✗ | nothing | nothing | — | — | nothing | +| `case5` | ✓/✗/— | green | green | — | — | green | + +### Notes +- **+#1 flips case1 (green → magenta):** the alias resolves the moment the + `.meta` is indexed — the core #1165/#984 reachability win. +- **+#1 temporarily regresses case2 (green → nothing):** the old loader + overwrites the file handle with the missing target and has no fallback. + Expected; restored by change #3. +- **case3 stays blank** until change #2 lets the loader run for a resource that + exists only as a `.meta`. From 69b5ac31bb3125a10a4c928ed5ee3998b9e5d428 Mon Sep 17 00:00:00 2001 From: briaguya <70942617+briaguya0@users.noreply.github.com> Date: Fri, 24 Jul 2026 00:22:41 -0400 Subject: [PATCH 04/11] Test fallback change; bump libultraship Bumps libultraship to the fallback commit (ResourceLoader::LoadResource prefers target, else falls back to the real asset) and records results. Observed: case2 nothing -> green (fallback restored); case1 magenta, case3/4 nothing, case5 green (unchanged). Relabels STATUS.md columns to application order (index, fallback, reachability). Co-Authored-By: Claude Opus 4.8 --- libultraship | 2 +- meta-alias-testing/STATUS.md | 50 +++++++++++++++++++----------------- 2 files changed, 28 insertions(+), 24 deletions(-) diff --git a/libultraship b/libultraship index ce374750142..a0f86f9e1c7 160000 --- a/libultraship +++ b/libultraship @@ -1 +1 @@ -Subproject commit ce3747501424b4568d139a687bcabad649db03ee +Subproject commit a0f86f9e1c7c6a81fae01a598a51843d24856daa diff --git a/meta-alias-testing/STATUS.md b/meta-alias-testing/STATUS.md index 96c4aa9f44d..10c1879e947 100644 --- a/meta-alias-testing/STATUS.md +++ b/meta-alias-testing/STATUS.md @@ -8,33 +8,37 @@ Vehicle: the boot ship logo. green = real/default loaded · magenta = replacement/target loaded · brown = original (mod not in effect) · nothing = load failed. -## Engine changes +## Engine changes (applied in this order) -1. `Archive::IndexFile` — also index a `.meta` sidecar under its literal - `foo.meta` name so `LoadFileProcess(path + ".meta")` resolves. **[applied]** -2. `ResourceManager::LoadResourceProcess` — don't bail when the real file is - missing but a `.meta` exists; fall through so the loader can resolve the - alias. **[pending]** -3. `ResourceLoader::LoadResource` — prefer the alias target, else fall back to - the real asset at the original path; move the null-file check after - resolution. **[pending]** +1. **index** — `Archive::IndexFile`: also index a `.meta` sidecar under its + literal `foo.meta` name so `LoadFileProcess(path + ".meta")` resolves. + **[applied — `ce374750`]** +2. **fallback** — `ResourceLoader::LoadResource`: prefer the alias target, else + fall back to the real asset; defer the null-file check past resolution. + **[applied — `a0f86f9e`]** +3. **reachability** — `ResourceManager::LoadResourceProcess`: don't bail when + the real file is missing but a `.meta` exists; fall through so the loader can + resolve the alias. **[pending]** ## Results (observed 2026-07-24, matching predictions) -| case | real/meta/target | baseline | +#1 | +#2 | +#3 (final) | expected final | +| case | real/meta/target | baseline | +index | +fallback | +reachability (final) | expected final | |---|:---:|---|---|---|---|---| -| control (no mod) | — | brown | brown | — | — | brown | -| `case1` | ✓/✓/✓ | green | **magenta** | — | — | magenta | -| `case2` | ✓/✓/✗ | green | **nothing** ⚠ | — | — | green | -| `case3` | ✗/✓/✓ | nothing | nothing | — | — | magenta | -| `case4` | ✗/✓/✗ | nothing | nothing | — | — | nothing | -| `case5` | ✓/✗/— | green | green | — | — | green | +| control (no mod) | — | brown | brown | brown | — | brown | +| `case1` | ✓/✓/✓ | green | **magenta** | magenta | — | magenta | +| `case2` | ✓/✓/✗ | green | **nothing** ⚠ | **green** | — | green | +| `case3` | ✗/✓/✓ | nothing | nothing | nothing | — | magenta | +| `case4` | ✗/✓/✗ | nothing | nothing | nothing | — | nothing | +| `case5` | ✓/✗/— | green | green | green | — | green | ### Notes -- **+#1 flips case1 (green → magenta):** the alias resolves the moment the - `.meta` is indexed — the core #1165/#984 reachability win. -- **+#1 temporarily regresses case2 (green → nothing):** the old loader - overwrites the file handle with the missing target and has no fallback. - Expected; restored by change #3. -- **case3 stays blank** until change #2 lets the loader run for a resource that - exists only as a `.meta`. +- **index flips case1 (green → magenta):** the alias resolves the moment the + `.meta` is indexed — the core #1165/#984 reachability win. It temporarily + regressed case2 (green → nothing), since the old loader overwrote the file + handle with the missing target and had no fallback. +- **fallback restores case2 (nothing → green):** target missing + real asset + present now falls back to the real asset (the new #1165 behavior). The Trace + log shows `Meta alias target '…/fancyShipDL.xml' missing; falling back to real + asset '…/gShipLogoDL'.` +- **case3 stays blank** until **reachability** lets the loader run for a + resource that exists only as a `.meta`. From 2da0a7792a5b45b4e29dd1bf8dfc08d3123bf50a Mon Sep 17 00:00:00 2001 From: briaguya <70942617+briaguya0@users.noreply.github.com> Date: Fri, 24 Jul 2026 01:05:54 -0400 Subject: [PATCH 05/11] Expand test kit: layered (archive-priority) cases + resolution model MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The single-archive cases missed the multi-archive behavior the issues require (libultraship #1165's design notes + #984). Add layered .o2r sets and document the full resolution model. New layered sets (o2r/layered/, 20-* = higher priority): - L1 higher real must beat lower .meta -> green (exposes the current bug: magenta) - L1b higher real over lower meta-only -> green - L2 higher .meta beats lower real -> magenta - L3 higher meta-only over lower real -> nothing (fallback is same-archive only) - L5 cross-game motivation: base ships alias+native real, separate mod supplies the shared target; toggle -> magenta ("mod loaded") / green ("vanilla") - L6 "reach back": winning .meta resolves its target from a separate archive README now documents the resolution model: resource/resource.meta are one override identity by priority; the winner decides; the alias target resolves globally (any archive); the same-path fallback is winner-local only (L3); else fail. build-o2r.sh is now deterministic (fixed timestamps). Known gap this documents: the current engine changes fail L1 (separate override identities) — motivates the override-identity rework. Co-Authored-By: Claude Opus 4.8 --- meta-alias-testing/.gitignore | 2 +- meta-alias-testing/README.md | 79 +++++++++++++++++- meta-alias-testing/build-o2r.sh | 62 ++++++++++---- .../o2r/case1-real-meta-target.o2r | Bin 7738 -> 7738 bytes .../o2r/case2-real-meta-notarget.o2r | Bin 4197 -> 4197 bytes .../o2r/case3-noreal-meta-target.o2r | Bin 4214 -> 4214 bytes meta-alias-testing/o2r/case4-meta-only.o2r | Bin 677 -> 677 bytes meta-alias-testing/o2r/case5-real-nometa.o2r | Bin 3927 -> 3927 bytes .../o2r/layered/L1-10-lower-meta-target.o2r | Bin 0 -> 4212 bytes .../o2r/layered/L1-20-higher-real.o2r | Bin 0 -> 3915 bytes .../o2r/layered/L1b-10-lower-meta-only.o2r | Bin 0 -> 677 bytes .../o2r/layered/L1b-20-higher-real.o2r | Bin 0 -> 3916 bytes .../o2r/layered/L2-10-lower-real.o2r | Bin 0 -> 3914 bytes .../o2r/layered/L2-20-higher-meta-target.o2r | Bin 0 -> 4213 bytes .../o2r/layered/L3-10-lower-real.o2r | Bin 0 -> 3914 bytes .../o2r/layered/L3-20-higher-meta-only.o2r | Bin 0 -> 677 bytes .../o2r/layered/L5-base-real-meta.o2r | Bin 0 -> 4193 bytes meta-alias-testing/o2r/layered/L5-target.o2r | Bin 0 -> 3933 bytes .../o2r/layered/L6-10-lower-real.o2r | Bin 0 -> 3914 bytes .../o2r/layered/L6-20-mid-target.o2r | Bin 0 -> 3930 bytes .../o2r/layered/L6-30-higher-meta.o2r | Bin 0 -> 672 bytes 21 files changed, 127 insertions(+), 16 deletions(-) create mode 100644 meta-alias-testing/o2r/layered/L1-10-lower-meta-target.o2r create mode 100644 meta-alias-testing/o2r/layered/L1-20-higher-real.o2r create mode 100644 meta-alias-testing/o2r/layered/L1b-10-lower-meta-only.o2r create mode 100644 meta-alias-testing/o2r/layered/L1b-20-higher-real.o2r create mode 100644 meta-alias-testing/o2r/layered/L2-10-lower-real.o2r create mode 100644 meta-alias-testing/o2r/layered/L2-20-higher-meta-target.o2r create mode 100644 meta-alias-testing/o2r/layered/L3-10-lower-real.o2r create mode 100644 meta-alias-testing/o2r/layered/L3-20-higher-meta-only.o2r create mode 100644 meta-alias-testing/o2r/layered/L5-base-real-meta.o2r create mode 100644 meta-alias-testing/o2r/layered/L5-target.o2r create mode 100644 meta-alias-testing/o2r/layered/L6-10-lower-real.o2r create mode 100644 meta-alias-testing/o2r/layered/L6-20-mid-target.o2r create mode 100644 meta-alias-testing/o2r/layered/L6-30-higher-meta.o2r diff --git a/meta-alias-testing/.gitignore b/meta-alias-testing/.gitignore index 82fbaf57bbd..9669d92f91c 100644 --- a/meta-alias-testing/.gitignore +++ b/meta-alias-testing/.gitignore @@ -1,5 +1,5 @@ # The repo ignores *.o2r and *.meta globally (build artifacts / sidecars). The # archives and source assets under this directory are intentional test fixtures, # so re-include them here. -!o2r/*.o2r +!o2r/**/*.o2r !**/*.meta diff --git a/meta-alias-testing/README.md b/meta-alias-testing/README.md index 9c7c3c6c9f5..53af3f37828 100644 --- a/meta-alias-testing/README.md +++ b/meta-alias-testing/README.md @@ -57,6 +57,82 @@ The engine fix is proven by **case 1 (green → magenta)** and **case 3 (nothing Cases 2/4/5 confirm no regressions. (The "current engine" column is the baseline captured before any code change — see `BASELINE.md` once recorded.) +## Layered cases (`o2r/layered/`) — archive priority + +The five cases above use a single mod over `soh.o2r`. The issues also require correct behavior +when **two archives** touch the same path at different priorities (libultraship #1165's design +notes): `resource` and `resource.meta` must act as **one** override identity, resolved by archive +priority (last loaded wins). + +Each set is a pair — `L#-10-…` (lower priority) and `L#-20-…` (higher priority). Drop **both** +files of one set into `mods/`, make `20-*` the higher priority, and restart: + +- In the Mods menu, drag `20-*` **above** `10-*` (top of the list = highest priority, overrides + those below it), **or** +- set the `gSettings.EnabledMods` CVar to `L#-10-…|L#-20-…` (last in the list = highest priority). + +Mods are **not** hot-reloaded — restart the game after changing which set is present or its order. + +| set | lower (`10-`) | higher (`20-`) | correct result | current impl | +|---|---|---|---|---| +| **L1** | meta → target (magenta) | real (green) | **green** (higher real wins) | **magenta** ✗ | +| **L1b** | meta-only (target absent) | real (green) | **green** | **green** | +| **L2** | real (green) | meta → target (magenta) | **magenta** (higher meta wins) | **magenta** | +| **L3** | real (green) | meta-only (target absent) | **nothing** (real is in a lower archive, not with the `.meta`) | **nothing** | + +- **L1** is the key correctness gap: `resource` and `resource.meta` currently occupy *separate* + override slots, so a lower-priority `.meta` still overrides a higher-priority real asset. It + should render green; today it renders magenta. +- **L3** is decided (see the resolution model below): the alias's *same-path* fallback only uses a + real asset shipped **in the same archive as the `.meta`**. Here the winning archive has just the + `.meta`; the real lives in a lower archive, so there's nothing to fall back to → nothing. + +### L5 — the cross-game motivation (order-independent) + +The point of #1165: a base archive ships an alias plus its native real asset, and a **separate** +mod supplies the shared alias target (nothing overlaps on the same key, so load order doesn't +matter here). Toggle the target mod to switch between "mod loaded" and "vanilla boot": + +- `L5-base-real-meta.o2r` **+** `L5-target.o2r` in `mods/` → **magenta** (alias resolves to the + mod's shared target — "mod loaded"). +- `L5-base-real-meta.o2r` **alone** → **green** (target absent → falls back to the base's native + real asset — "vanilla boot"; same behavior as `case2`). + +Both states already work with the current implementation (the target lookup is a normal +cross-archive resolve); L5 is here as the end-to-end demonstration of the feature's purpose. + +### L6 — "reach back" to an asset in another archive + +A mod ships `gShipLogoDL.meta → fancyShipDL.xml`; the alias **target** (`fancyShipDL.xml`) lives +in a *separate* archive that may even be lower priority than the `.meta`. Three files, priority +low → high: `L6-10-lower-real` (a real that the `.meta` overrides), `L6-20-mid-target` (the target +asset), `L6-30-higher-meta` (the winning alias). + +- All three present → **magenta**. The `.meta` wins the `gShipLogoDL` identity (highest priority) + and resolves its target from the separate archive. + +This works because the **target is a different path** (`fancyShipDL.xml`), resolved by a normal +global lookup — it's found wherever it lives, regardless of priority relative to the `.meta` +(hence "reach back"). Contrast with L3, which is about the *same-path* fallback, not the target. + +## Resolution model (what a layered `.meta` does) + +For a requested path `X`: + +1. **One identity, by priority.** `X` and `X.meta` are the *same* override slot; the + highest-priority archive that supplies either one **wins** `X`. Lower archives are shadowed. +2. **Winner decides.** If the winning archive supplies a real `X`, load it. If it supplies + `X.meta`, resolve the alias (below). (A higher-priority real therefore beats a lower `.meta` — + that's L1.) +3. **Target — resolved globally.** The alias's target path is looked up like any other resource: + found in whatever loaded archive has it, highest priority wins, above **or below** the `.meta` + (L5, L6). +4. **Same-path fallback — local only.** If the target is missing, fall back to a real `X` **only + if the winning archive itself also ships one** (co-located, e.g. `soh.o2r` shipping both). The + fallback does **not** reach down into lower-priority archives (L3 → fail). Practical rule: + *ship a `.meta` and its intended fallback real in the same archive.* +5. **Nothing to load → fail** (case4). + ## How to run a case 1. Build/run SoH as usual (executable at `build/soh/soh.elf`, with `oot.o2r` / `soh.o2r` / @@ -94,5 +170,6 @@ meta-alias-testing/ regen-assets.sh derives src/assets/ from soh's gShipLogoDL (recolor + alias) build-o2r.sh assembles o2r/case*.o2r from src/assets/ src/assets/… generated test assets (default green, replacement magenta, the .meta) - o2r/case*.o2r the five committed test archives + o2r/case*.o2r the five single-archive test archives + o2r/layered/L*.o2r layered (two-archive priority) test sets ``` diff --git a/meta-alias-testing/build-o2r.sh b/meta-alias-testing/build-o2r.sh index e0945d6f268..aef944f305a 100755 --- a/meta-alias-testing/build-o2r.sh +++ b/meta-alias-testing/build-o2r.sh @@ -1,10 +1,12 @@ #!/usr/bin/env bash # -# Assembles the five test .o2r archives under o2r/ from the assets in src/assets/. +# Assembles the test .o2r archives from the assets in src/assets/. # An .o2r is just a zip; entries must be stored at "textures/nintendo_rogo_static/..." # so their CRC64 matches the paths the game requests. # -# Drop exactly ONE of these into /mods/ (e.g. build/soh/mods/) at a time. +# o2r/case*.o2r single-archive cases (drop ONE into mods/) +# o2r/layered/L*.o2r layered cases (drop a matching 10-/20- pair into mods/; +# 20-* must be the higher-priority archive — see README) set -euo pipefail cd "$(dirname "$0")" ROOT="$(pwd)" @@ -12,30 +14,62 @@ ASSETS="$ROOT/src/assets/textures/nintendo_rogo_static" command -v zip >/dev/null || { echo "error: 'zip' is required" >&2; exit 1; } -DEFAULT_FILES=(gShipLogoDL mat_gShipLogoDL_f3d_material mat_gShipLogoDL_f3d_material_001 mat_gShipLogoDL_f3d_material_002 mat_gShipLogoDL_f3d_material_003) -TARGET_FILES=(fancyShipDL.xml mat_fancyShipDL_f3d_material mat_fancyShipDL_f3d_material_001 mat_fancyShipDL_f3d_material_002 mat_fancyShipDL_f3d_material_003) -META_FILE=(gShipLogoDL.meta) +# Building blocks (by color): real=green, alias target=magenta, meta=alias->fancyShipDL.xml +REAL=(gShipLogoDL mat_gShipLogoDL_f3d_material mat_gShipLogoDL_f3d_material_001 mat_gShipLogoDL_f3d_material_002 mat_gShipLogoDL_f3d_material_003) +TARGET=(fancyShipDL.xml mat_fancyShipDL_f3d_material mat_fancyShipDL_f3d_material_001 mat_fancyShipDL_f3d_material_002 mat_fancyShipDL_f3d_material_003) +META=(gShipLogoDL.meta) -mkdir -p "$ROOT/o2r" +mkdir -p "$ROOT/o2r/layered" TMP="$(mktemp -d)" trap 'rm -rf "$TMP"' EXIT -build_case() { +build_o2r() { # local out="$1"; local mname="$2"; shift 2 - local dir="$TMP/$out" - mkdir -p "$dir/textures/nintendo_rogo_static" + local dir="$TMP/$(basename "$out")" + rm -rf "$dir"; mkdir -p "$dir/textures/nintendo_rogo_static" printf '{"name":"%s","code_version":1}\n' "$mname" > "$dir/manifest.json" local f for f in "$@"; do cp "$ASSETS/$f" "$dir/textures/nintendo_rogo_static/$f" done + # Fixed timestamps so rebuilds are byte-for-byte reproducible (no git churn). + find "$dir" -exec touch -d '1980-01-01 00:00:00' {} + rm -f "$ROOT/o2r/$out.o2r" ( cd "$dir" && zip -q -X -r "$ROOT/o2r/$out.o2r" manifest.json textures ) echo "built o2r/$out.o2r" } -build_case case1-real-meta-target "meta-test 1 real+meta+target" "${DEFAULT_FILES[@]}" "${META_FILE[@]}" "${TARGET_FILES[@]}" -build_case case2-real-meta-notarget "meta-test 2 real+meta" "${DEFAULT_FILES[@]}" "${META_FILE[@]}" -build_case case3-noreal-meta-target "meta-test 3 meta+target" "${META_FILE[@]}" "${TARGET_FILES[@]}" -build_case case4-meta-only "meta-test 4 meta-only" "${META_FILE[@]}" -build_case case5-real-nometa "meta-test 5 real (no meta)" "${DEFAULT_FILES[@]}" +# --- single-archive cases --- +build_o2r case1-real-meta-target "meta-test 1 real+meta+target" "${REAL[@]}" "${META[@]}" "${TARGET[@]}" +build_o2r case2-real-meta-notarget "meta-test 2 real+meta" "${REAL[@]}" "${META[@]}" +build_o2r case3-noreal-meta-target "meta-test 3 meta+target" "${META[@]}" "${TARGET[@]}" +build_o2r case4-meta-only "meta-test 4 meta-only" "${META[@]}" +build_o2r case5-real-nometa "meta-test 5 real (no meta)" "${REAL[@]}" + +# --- layered cases: 20-* is the higher-priority archive (loaded last, should win) --- +# L1 higher real must beat lower meta+target -> green (bug shows magenta) +build_o2r layered/L1-10-lower-meta-target "L1 lower meta+target" "${META[@]}" "${TARGET[@]}" +build_o2r layered/L1-20-higher-real "L1 higher real" "${REAL[@]}" +# L1b higher real over lower meta-only -> green +build_o2r layered/L1b-10-lower-meta-only "L1b lower meta-only" "${META[@]}" +build_o2r layered/L1b-20-higher-real "L1b higher real" "${REAL[@]}" +# L2 higher meta+target beats lower real -> magenta +build_o2r layered/L2-10-lower-real "L2 lower real" "${REAL[@]}" +build_o2r layered/L2-20-higher-meta-target "L2 higher meta+target" "${META[@]}" "${TARGET[@]}" +# L3 higher meta-only over lower real -> green or nothing (open question) +build_o2r layered/L3-10-lower-real "L3 lower real" "${REAL[@]}" +build_o2r layered/L3-20-higher-meta-only "L3 higher meta-only" "${META[@]}" + +# L5 cross-game motivation (order-independent): a base ships the alias + its native real, +# a SEPARATE mod supplies the shared alias target. +# base + target present -> magenta ("mod loaded") +# base alone -> green ("vanilla boot", falls back — same as case2) +build_o2r layered/L5-base-real-meta "L5 base real+meta" "${REAL[@]}" "${META[@]}" +build_o2r layered/L5-target "L5 shared target" "${TARGET[@]}" + +# L6 "reach back": the winning (highest) .meta overrides a lower real, and resolves its target +# from a SEPARATE archive that may be lower priority than the .meta (a different path, so it's +# a normal global lookup). All three present -> magenta. +build_o2r layered/L6-10-lower-real "L6 lower real" "${REAL[@]}" +build_o2r layered/L6-20-mid-target "L6 mid target" "${TARGET[@]}" +build_o2r layered/L6-30-higher-meta "L6 higher meta" "${META[@]}" diff --git a/meta-alias-testing/o2r/case1-real-meta-target.o2r b/meta-alias-testing/o2r/case1-real-meta-target.o2r index ff852ed0c09c856077d25a410f36b124543a642d..cb13c588005882b1b51418f7c905a2a15a290390 100644 GIT binary patch delta 323 zcmdmGv&)7rz?+$civa=@87A^2urM$H=^|waJ=200#J{dmCZoZ`z>qun0l(&CJ}H^a z%8W)#U?r0uvifdb#%vDZZGOnw0%c`#G(cHzxmGhm)Xw4A3^vbhiP%$+dD%eoTsAM@ zHHMlMFXTIU8ME=``$8;Gz41aY^PY>q%*z&E2sKYY(i~#2x1=$I(In{sVVsl9fH172 w;0&qB2c-NUqC(Os5Jr`BID~OkIs(Ell!0oPi(p7iR+fe8ER}`oyek_E04%aXIRF3v delta 323 zcmdmGv&)7rz?+$civa`zc7BhU$d|wZri+vz^h^t05dXSLnT!S#14HiQ2mG3o`J`kv zD>E7~ft5^t$m+X!8M8TrxA`G!3zU_~(Ew$=jUvs4zU^R8?x0A-4RtpET3 diff --git a/meta-alias-testing/o2r/case2-real-meta-notarget.o2r b/meta-alias-testing/o2r/case2-real-meta-notarget.o2r index 4cf5cf4a10197db8b1efeb8fc474c0dd00fe2ef4..d50dd24d1f85c6091a54ed849b040063564cb559 100644 GIT binary patch delta 180 zcmaE=@Kk{>z?+$civa=@87A^YvM?|J>1<^PJ;?$>pW6(kH@7l2Fo9Wz?+$civa`zc7BhU$QQ{1rn8kH^dt)ieQq~RAd2v K3Pg%SAQk`vmq@?> diff --git a/meta-alias-testing/o2r/case3-noreal-meta-target.o2r b/meta-alias-testing/o2r/case3-noreal-meta-target.o2r index 91729938ade2dae3ed876f77b35eb590918b2d6a..41e90c77ab60a048f24d92f1203a31e9cff48815 100644 GIT binary patch delta 181 zcmeyS@J)d)z?+#xgn@y91BewFCh|qGfax4%2tC;XOm95jzyxMYzRl{pc>%LIgtz%N zYYUW>%mG!g`8n5WMu^&JJX;|Qe_nG4ql?!V!Z^t50b%g+Wk48}Fve3DBalA@BDIr0 K9KsM0NCE(>lp`Df delta 181 zcmeyS@J)d)z?+#xgn@y9gCSt&_n3)%Q7m9OM;Ss-wgA%`&o?lE8Iy0b`fgspY!2aV zzRlVKWhHY!RcwCFwVDy4b{fxC2*aP(9Kz`0HHI(_@_IlRynGoDMkS2#6vhbTPk~77 Lyx}?~EQ0 IhBH$V05}c~rvLx| delta 82 zcmZ3=x|EeKz?+#xgn@y9gCSt&_n3)%;VfV}QyD@}uz=8Kwu0%&t&HXn#sNlS2;)1W K2ZZ6wlmr0A>L7mr diff --git a/meta-alias-testing/o2r/case5-real-nometa.o2r b/meta-alias-testing/o2r/case5-real-nometa.o2r index 708155b07103659eda9cfec3ad21395f79a96bc9..057639e8da394e3804256f3ce1c21577071079d3 100644 GIT binary patch delta 181 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 ow+r1K^x^3M@}ReNPUCgTNMl2@i+|003?&zW@LL literal 0 HcmV?d00001 diff --git a/meta-alias-testing/o2r/layered/L1b-10-lower-meta-only.o2r b/meta-alias-testing/o2r/layered/L1b-10-lower-meta-only.o2r new file mode 100644 index 0000000000000000000000000000000000000000..7dc313c8b2efb1ba5af846ad79dd86d7629cf218 GIT binary patch literal 677 zcmWIWW@h1HfB;2?ghySo41gRE<^|&1#JtS3)Z!Aotm6EB^{;Y{FK!AvecsD%=|nhE5ll@02HGkRzOHjAXSoDQBqozTC9&- znH;jRyv)3k)V!4Z_@eyu{P^OM#FETpuwD_MDh?o4WN2rPsLuwnnt)gjh~ZXX)teri zky+paG|a_E59ANi?u}f{4m=Lue{=1O+)|u=y!h26-=iX1hG&e0bMId0SthG6#@hp z-a1ADneY(6rwtU$5CGB!3Ou+rtN{lx3wN-C%!0UnA3?LwBO$<>l?|ky1qh{phLnN~ GVgLX}wyxj+ literal 0 HcmV?d00001 diff --git a/meta-alias-testing/o2r/layered/L1b-20-higher-real.o2r b/meta-alias-testing/o2r/layered/L1b-20-higher-real.o2r new file mode 100644 index 0000000000000000000000000000000000000000..acd7d073e82a18933975fd36e7b2f6b7025fb419 GIT binary patch literal 3916 zcmWIWW@h1HfB;2?=RG;6b$}cY<^|&1#JtS3)Z!Aotm6EAXSoDQBqozTC9&-nH;jR zyv)3k)V!4Z_@eyu{P^OM#FETpuwD_MDh?o4WUyNz_Edw3fgziffx(WT-rU5J`1Ih6 z%mN>vfi6DrX~rq>K(W-K%*32{0|Vo;A+hRO-LPiT-@J9&~0A@#)p)ZTD^1m|3iO>DqE$cK;=L8mqSzu*!?g zHxt}DVaeJ}OIbJ{ifQ|AQQ9Z$ejp-uVYjZfg^XcQU*GS2cecRn@K2U(rtIq_VG=X$-P_p8^ET`li`z!=iWAk7g0{>Q$yG~j+bnrby<&~6 z#Z#d$p}6ggr#TBg>XmE{cu~4E^o4-+jnZ#jl52A}H!b+4w0Ld4*tMgNtc_AE@3<(g zdH>m!@m~Mh9!Di#pH*|NXnE;*CCQxlb(c4Rc@^_sdjp<7NskPS3W_?)tJbqmEdRGy zO+j$w{yX;_8`^sP>n1t%u{&&0$Px})%y828`O@=`r@qu?3_Nu{`h@kW$gnMsoOBMG zR;TWGsJZ;6QDXnenPIvt)?w2%r)$|N+B@86R#>>-=3@@>i~7ss%FX(4kM-75ZfBLO9zDO&A+qr65j&ne7JSK`pDIQE zONcHxJNrYG?vlAL%jd0Nn&c8L{<>|7Etl+ks&93FwA{Dv`<=tBQxD$?o;jEK8rKJh z+0#B&@rr-Q`<5!YS=je{sa?3j(nHU#HeNL;m@Kj2s8rRFZHMp9lMskd?zp>GT1R1n zd(5043;X}Fi!(i+955wQC^P8cVV38@F+Ce}`g^kdthsdSUt0xT=?JZIGuZ3icj=QI zQ|d97V_+p80=_3rcdvAHE#|9qY;-r9xVU03riiPWtWQS8ghpIU6{TU`g)rxGi;vp$~T0cNyJ=kmR)~*>UohPGcDFF ze6gY|ZgZ5!#yKW`W=;-EX?ps3%Khc;Ve=Jld{}m>CFrI1PenC57R~C#wv&JPKl$d+ z#9?`aOX;^YOaGbx)^%|jrvE3XPcSQ&ZQCNX^_S==F)uBb@~rT+^PKmn_j7icCVn|$ z#`RaFQ^>U|bIK11(X_YSU#c{h%HH&!_=>6PNVt%8&~~v8Yb0XA4ro>WD+=AI9X&T8 zoa---ROj?I8MEi1E9O?tU0I(0di~#BGfF-mType(QqcYWNqhzQ$JMtE!PWs|J^-7lAq4Lc)4M)@AXgZ`k z9a>RTCy%DV(KJY28f4ynKRAXF*h*w$V9+F(29er?Tc_OZYcddMU4MOf#xJG=Qs2FN zA~G(pT=X!#XDXVum2=y&67$!$^&kCXFw9n}x}KkAWoc>r);`8GkB5Z3N#GO3F zZ&zHYmilFL{`E^sb8B(wA13dmxV*VC9n!iow}!4UxYKdvR`KC&C;em=8A-UjSv!BD z5nI%UIXBDXqqmo>D>scY`28(ReWH||gT2>lQSo{2pErHZ5Vy+sd%pFz<%PfpqU>_p zm}S|nmo0d_DDHk^{@UWgh9?@Axr7W9SJ+C_&RNA1aAJ|C#j?p3B^#SBMh5c5Y+t1j zdHHG6h6zXNTT_kW-6LX58HlU_3DZt_1`LFuZjP2Qra58mKnH6oP0_R|5h-+UkHzxHcRe50F`iW*>Uj z17sG&^}7g~MW`bJ@h-wH^d=_4E<0vI?j~TD5%G2%12%B+hYVb3LCa>Q>BQsEHb%?d0}Kz&aRhJ(Nse<=@$2LOPqD#-u< literal 0 HcmV?d00001 diff --git a/meta-alias-testing/o2r/layered/L2-10-lower-real.o2r b/meta-alias-testing/o2r/layered/L2-10-lower-real.o2r new file mode 100644 index 0000000000000000000000000000000000000000..a4222054608170e9ce38e1148c8d637a45fa5064 GIT binary patch literal 3914 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 literal 0 HcmV?d00001 diff --git a/meta-alias-testing/o2r/layered/L2-20-higher-meta-target.o2r b/meta-alias-testing/o2r/layered/L2-20-higher-meta-target.o2r new file mode 100644 index 0000000000000000000000000000000000000000..2fee137c770b73c14d0e0c88a46505ee1e14025d GIT binary patch literal 4213 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 literal 0 HcmV?d00001 diff --git a/meta-alias-testing/o2r/layered/L3-20-higher-meta-only.o2r b/meta-alias-testing/o2r/layered/L3-20-higher-meta-only.o2r new file mode 100644 index 0000000000000000000000000000000000000000..f75d260aa0b2a1a57d94eb9f7118a947b1290a3c GIT binary patch literal 677 zcmWIWW@h1HfB;2?TW2(64S*aF<^|&1#JtS3)Z!Aotm6EB^{;Y{FK!AvecsD%=|nhE5ll@02HGkRzOHjAXSoDQBqozTC9&- znH;jRyv)3k)V!4Z_@eyu{P^OM#FETpuwD_MDh?o4WN2rPsLuwnnt)gjh~ZXX)teri zky+paG|a_E59ANi?u}f{4m=Lue{=1O+)|u=y!h26-=iX1hG&e0bMId0SthG6#@hp z-a1ADneY(6rwtU$5CGB!3Ou+rtN{lx3wN-C%!0UnA3?LwBO$<>l?|ky1qh{phLnN~ GVgLYEldaeQ literal 0 HcmV?d00001 diff --git a/meta-alias-testing/o2r/layered/L5-base-real-meta.o2r b/meta-alias-testing/o2r/layered/L5-base-real-meta.o2r new file mode 100644 index 0000000000000000000000000000000000000000..4c42ba7d251265c85b2f24853a80044e60126834 GIT binary patch literal 4193 zcmWIWW@h1HfB;1XhFf8hdO!{c^8#^hVqRuiYH^8PR&jn_wNhSUZmN=%l8>oEQettc zLQ!gBj&^QpNurXDQgVJuYJ6F0QE_H|o|2VeEmr`l!4Mn3Bqxw8Nv$X;ElMrc$E{2b zSy^6YUP)?RN`8D%etLd2v8LV5GyjYvq#it16fT#401o*3aon5gEKM< ze1L|z_~?PWVA{QrtJ#6a;rnl{osnCL(~lRwy5!q*OzOgu9ZMc5{w<&SNc#Tcx9coE z3g&z|e@Hs-o9_x!Vd+l_v6K2svJ$RbS1yl>xtx+K@;Zf;ueO=SK4oili{*qbsCmwUp-p$7x zUzX2X z!8FMwT*xzM>$A-!^0Ctn=tO)g3$4_Un))E1;Om#{#_O{r%zn>3J-2S|%GtMnW$$O3 z{LXI1mS6EcuYOOw#Q5s%k7nIjdhNTO-OZ~oUX_`-vFWP93#Y46t3zu}{EE6_ENW!6 zB(7vt%_n`)D`}Po`MZziqo%=))7q_dfN8LR8fnl7TN-?(EW*pgwEXO^B*70-s~eO* zEL*m}ZkuAuB|D$$TiqWm_wD?(UV=QP|)fGv~*`{=e+vOwT6=Ovx0= z40?E&<+*T7&jy|To@_sBF5UXqRzX)fLaW>i_PY07`eetHdd%e*SjmTg?@80$D_vcS z`6?Y7-OVO$Zwp$zeAltvM^xwSzIx`tdrN&yyZIqo{vK9a7Q+UUB8iD5({s+m@h@BdDBRgmE4Y65 z-bRLaGseHZ-lob7n(^ylfzP) zo_?Nkf4O_ue8n3dmYr$|dg=XBQO%A;vwE@Z^^;VN8;48-uD_da9_<{6#i3j zws++DOB|&h@g=X5zBo_4l4W~&-w64spp=%88bX>Vre0bYQKbb{F5-xAn&fjRn7WHAy%`*Au?PcrAO`{Bc ze+yHeC}rnh@AX<#eBS%#O`kKwtup?eZ~bj~A@G4HyWBQrS+?tC3mz|uyWg0haBd??3(Zr^U^zo#P{>C{bh9f@?B(V@EL!5^F>PSuMAe# zeR`!fF*4Wm)P)PHKCZa_n<>DXkx7mjcYg^OPYl2~V_4D%Vxjb&FtmbHKmn*n1OWmJ zZyh6nOnB!7pEghr2?9Xc8h}i=HmrRfh*^lP2YQbQWERBrdkC6^l;FVqCv>yXyEO>2 zi-A_)cPF7v4!T|F%~yn7-VX-RB7Yg;vkO!WA^gP-Oflr!g>DaeQG>9j do12h5Na-NJn-v%Y3=Et=C<_dDJoEaYkZM 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 literal 0 HcmV?d00001 diff --git a/meta-alias-testing/o2r/layered/L6-20-mid-target.o2r b/meta-alias-testing/o2r/layered/L6-20-mid-target.o2r new file mode 100644 index 0000000000000000000000000000000000000000..00193489e6f560c6c3e8fe7ee22fae4b0b7fb650 GIT binary patch literal 3930 zcmWIWW@h1HfB;2?Clf9{)BAB5 zVTwx@EnvA9^6ho{?0$h=?V0}7hIYrVf1g)fbM)A+IDK1kV;GR8k>t zE2rm&$>Ezk&xO};u8F-InV3*jeO%0FOSGi0#L>71itjzWE~W)-nK?!BT$;uF2OGaV znBpDbS#g(5NBzLj-52gUExbN8Y@zd;gzGo8)3$6o%hhzNyJy?J$O!T7_a`Pi{UFr3 zVc&Ui)(ZP=Mr@rXhAWGLR%`6k=qyxtU9R82yMph_zXJvzx;qb_IB-(&-pjpmF86-+ z&uVa4@~^0#ol(ln?x!c0r5wY>rWL*sy-X_RbEn(&hc8>t;-YDvulRmNTI9tp?u|Ux z-%NejwtP<7=@whH^r)=@Zz5us#jN_$@`oXjzp3S)lI`RQ{U85Y{$ALy@n!q9aE^JI zdfYL4CuIFo?AOnEXT{@TnfcXNL|3Hc^Sdir(x%-nI_4)U2en)i{%!E#kwuT{$>)K8 zl0zn#Tz1lp zH|2Weze<J3-EclTmnvHdaswv1geuV$6+ewY?=`BD<=YR3z$ zt4!}X)#_^P4eXILzaZT;}o$cCTeA)h~Wqc9Z2bgoC zj=eUGIk5ZY^w2YYCbrjq#xzdhnjOv_cKLvBQiE9b3&Gob<<&_J3B3ws-}5#!9FQw4 zeAI6DO+JQWj=xjjGS4NRt^C4s{0j^ZZL%?1XZcoR%b&HcJpz@(UP>PPB4_D!`ZvcE zNnuHd66d?!v2ssEMbBD3VP%!O>2goXYwf&OQtySPSG)@|Zr=ZV&ypWCK^MRA&z@1l z86#@6zoswf*Zt?+&YNU&WSdfEwOnJ(EtwLv<%mki*WKGVn|>X7qc5Q3FV>mlaw&F` zVYTgyeO>XqtSf!~zWv6=RAb5V#a#Dl55pO+Rs4ar907MKGJe_AO|=acNPo1WZPDUe zm1naQj}~3}knR^Xk#pMl;JW#8k+p3Jhi7XEdo9&}+CJllz>+t;-+W%#PrS{{sq{oZ zyW`D!0h>)uqC2VnhCZfOdSCcWpI&%a zYxeX#jEUl_uBY&xUApE;X3AHN(%p5ht{r_bU2EmlnwIxzo9$_s4h=`s;b=OfJRMq5 zR40$7!O=8GUK-5hC<;zt1hx{{7#MU3ra`1OpVd3%aXectA-dEi~^p}}*om1*L$kY~H+h;?bslVD%=Vp`{to^O9%u$J8R zU%9UFiO%9>H;*6NdusROqy^kjp`Lzce=qe@Q>|$V*|P4J#d`CgirN^iYny{#Jovg_ zL~go+d$YMv#Vx)Pi5Tm~zV51O_PRaiehMxMQW14oP;-KRL5k(sUc>BQx_6IcnS+iDk-F*4f&G7sJt23*n@A);UW6L(FT_z@3Hm|7?w1GSSZ~Qbgdu-FaYXiK!5W2IwcVABJ4tMZX)cmVJ75m0(Kb@Z`ToEV;6rI5wOb; zpIxA81K}?pU|)}XyU^`HFB%Z`TmU9V{Pv&~6an6>!14vu7v*3$3hV*Y^MH5&Foi?> literal 0 HcmV?d00001 diff --git a/meta-alias-testing/o2r/layered/L6-30-higher-meta.o2r b/meta-alias-testing/o2r/layered/L6-30-higher-meta.o2r new file mode 100644 index 0000000000000000000000000000000000000000..fa7dc3a9e84eebfbc553dd0acd1e74040b07bcaa GIT binary patch literal 672 zcmWIWW@h1HfB;2?cP`aiwSgQE<^|&1#JtS3)Z!Aotm6E! z%)FA+yp;U-qWtvy_~MeplFVeVUJ;-w4j@)!XlIY8&jzxZfLITR;Z|VPn;x8zS>OXS z%*96!WvdEb0jmk03J!to3jZDVf zk9PBJk1^Ebod4}%-8+T=Z$>6LX51kTbUOnh5HD#2u~5PvT`Ncd41j_a0t6V|I)(w6 z@DRYK4HV1}0Mb?qWWu#!4LFEdxPu*J7R2>C37Ul-2?5@$Y#{wCKqv(?qyS_P0|5KI Btg-+A literal 0 HcmV?d00001 From 5f9fb7e8b03f4afb8ca4554accba696fc4b24b25 Mon Sep 17 00:00:00 2001 From: briaguya <70942617+briaguya0@users.noreply.github.com> Date: Fri, 24 Jul 2026 01:11:46 -0400 Subject: [PATCH 06/11] Add .meta loading flowchart (meta-loading.md) Mermaid diagrams of the intended .meta resolution model: winner selection by archive priority, the .meta alias/fallback decision, and the target-global vs same-path-fallback-winner-local asymmetry. Maps each leaf to the test cases and notes where it lands in the code. Mermaid so it renders on GitHub and can be ported into the LUS doxygen docs later. Linked from README's resolution model. Co-Authored-By: Claude Opus 4.8 --- meta-alias-testing/README.md | 2 +- meta-alias-testing/meta-loading.md | 79 ++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 meta-alias-testing/meta-loading.md diff --git a/meta-alias-testing/README.md b/meta-alias-testing/README.md index 53af3f37828..c76b8842e62 100644 --- a/meta-alias-testing/README.md +++ b/meta-alias-testing/README.md @@ -117,7 +117,7 @@ global lookup — it's found wherever it lives, regardless of priority relative ## Resolution model (what a layered `.meta` does) -For a requested path `X`: +See [meta-loading.md](meta-loading.md) for a flowchart of this. For a requested path `X`: 1. **One identity, by priority.** `X` and `X.meta` are the *same* override slot; the highest-priority archive that supplies either one **wins** `X`. Lower archives are shadowed. diff --git a/meta-alias-testing/meta-loading.md b/meta-alias-testing/meta-loading.md new file mode 100644 index 00000000000..2736c419aad --- /dev/null +++ b/meta-alias-testing/meta-loading.md @@ -0,0 +1,79 @@ +# `.meta` alias loading flow + +How libultraship resolves a resource request when a `.meta` alias is involved, across one or more +layered archives. This is the **intended model** (the target of the override-identity rework); it +matches the rules in [README.md](README.md#resolution-model-what-a-layered-meta-does). It's kept as +a standalone Mermaid diagram so it can be ported into the LUS doxygen docs later. + +## Terms + +- **X** — the resource path being requested (e.g. `textures/nintendo_rogo_static/gShipLogoDL`). +- **X.meta** — a JSON sidecar that describes/aliases X: a target `path` plus `type`/`format`/`version`. +- **winning archive (W)** — the highest-priority loaded archive that supplies X *or* X.meta. `X` + and `X.meta` are **one** override identity, so a single archive wins the slot (last loaded = + highest priority). + +## Resolution flow + +```mermaid +flowchart TD + A(["request X"]) --> B{"winning archive W for X?
(highest-priority archive
that has X or X.meta)"} + B -- "none" --> FAIL1["FAIL — not found"] + B -- "W" --> C{"does W contain
X.meta?"} + C -- "no" --> REAL["load W's real X
(embedded header / XML)"] + C -- "yes" --> M["read X.meta:
target path Y + type/format/version"] + M --> T{"load target Y
(normal global lookup —
any archive, by priority)"} + T -- "found" --> TGT["load Y as the aliased resource"] + T -- "missing" --> FB{"does W also ship
a real X? (co-located)"} + FB -- "yes" --> FALLBACK["fall back: load W's real X"] + FB -- "no" --> FAIL2["FAIL — target missing,
no co-located real"] +``` + +## Which path each test case takes + +| outcome (leaf) | meaning | test cases | +|---|---|---| +| `load W's real X` | winner has no `.meta`; plain load | case5, **L1**, L1b | +| `load Y (target)` | alias resolved to its target | case1, case3, **L2**, L5 (mod loaded), **L6** | +| `fall back: real X` | target missing, co-located real used | case2, L5 (vanilla boot) | +| `FAIL` | nothing to load | case4, **L3**, and "none" | + +(Bold = the layered / archive-priority cases.) + +## Priority / override identity + +`X` and `X.meta` share one override slot. Indexing an archive that contains `X.meta` also claims +the base name `X`, so whichever archive is highest priority owns the identity — and only *that* +archive's `.meta` (if any) is consulted: + +```mermaid +flowchart LR + subgraph low["lower priority"] + L["archive A:
X.meta → Y"] + end + subgraph high["higher priority (loaded later)"] + H["archive B:
real X"] + end + high -- "wins X" --> W["winner = B
→ load real X
(A's .meta ignored)"] +``` + +This is why a higher-priority real asset beats a lower-priority `.meta` (test **L1**): the winner +is chosen first, and a lower archive's `.meta` never gets a look in. + +Note the asymmetry the flow relies on: + +- The alias **target Y** is a *different* path, resolved by a normal global lookup — it's found in + whatever archive holds it, **above or below** the `.meta` ("reach back", tests L5/L6). +- The **same-path fallback** (a real `X` when Y is missing) is **winner-local** only — it never + reaches down into lower-priority archives (test **L3** fails). Practical rule: ship a `.meta` + and its intended fallback real in the *same* archive. + +## Where this maps in the code (target design) + +- **winner selection** — `ArchiveManager`'s file→archive index; an archive with `X.meta` claims + the base name `X`, and the last-loaded (highest-priority) archive wins the slot. +- **`.meta` read + alias/fallback decision** — `ResourceLoader::LoadResource`, scoped to the + winning archive; `ReadResourceInitData` parses the JSON, `ReadResourceInitDataLegacy` reads a + real/fallback asset's own header/XML. +- **file fetch** — `ResourceManager::LoadFileProcess` (global lookup for the target Y) and the + winning archive's own `LoadFile` (the co-located real `X`). From f50a6871a589fd8cfcb4a4188864d5b7f754fb10 Mon Sep 17 00:00:00 2001 From: briaguya <70942617+briaguya0@users.noreply.github.com> Date: Fri, 24 Jul 2026 01:44:46 -0400 Subject: [PATCH 07/11] Rewrite .meta model as "highest-priority provider wins" The winner-owns-identity model gave the wrong answer for the flagship cross-game case (a lower-priority .meta whose target lives in a higher archive should win). Corrected model: X.meta -> Y adds Y as an alternate provider of X, ranked by the archive where its asset lives (not where the .meta lives); the request loads the highest-priority provider (real X or aliased Y). This yields both the flagship (alias wins) and the L1 edge (higher real wins) from one rule; "fallback" is just the case where the target doesn't exist. - meta-loading.md: new flowchart + worked examples (flagship A/B and L1). - README: resolution-model section replaced with the provider rule; a warning notes the per-case tables are being recomputed against it (next step). Co-Authored-By: Claude Opus 4.8 --- meta-alias-testing/README.md | 31 +++++---- meta-alias-testing/meta-loading.md | 106 +++++++++++++---------------- 2 files changed, 62 insertions(+), 75 deletions(-) diff --git a/meta-alias-testing/README.md b/meta-alias-testing/README.md index c76b8842e62..3f0fc7faeb7 100644 --- a/meta-alias-testing/README.md +++ b/meta-alias-testing/README.md @@ -11,6 +11,12 @@ a `.meta` file aliases the boot **ship logo** DisplayList to a renamed copy, and title screen to see which asset actually loaded. `f5d2e58` only covered "alias target present"; this kit covers the full matrix, and recolors the assets so the loaded one is obvious. +> [!WARNING] +> The resolution model was corrected (see [meta-loading.md](meta-loading.md) — "highest-priority +> provider wins"). The per-case **expected-result tables below are being recomputed** against it +> and the `.o2r` sets reworked; treat `meta-loading.md` as the source of truth until this notice +> is removed. + ## What a `.meta` alias should do (#1165) A `.meta` sidecar means *"prefer the target asset, otherwise fall back to the real asset at the @@ -117,21 +123,16 @@ global lookup — it's found wherever it lives, regardless of priority relative ## Resolution model (what a layered `.meta` does) -See [meta-loading.md](meta-loading.md) for a flowchart of this. For a requested path `X`: - -1. **One identity, by priority.** `X` and `X.meta` are the *same* override slot; the - highest-priority archive that supplies either one **wins** `X`. Lower archives are shadowed. -2. **Winner decides.** If the winning archive supplies a real `X`, load it. If it supplies - `X.meta`, resolve the alias (below). (A higher-priority real therefore beats a lower `.meta` — - that's L1.) -3. **Target — resolved globally.** The alias's target path is looked up like any other resource: - found in whatever loaded archive has it, highest priority wins, above **or below** the `.meta` - (L5, L6). -4. **Same-path fallback — local only.** If the target is missing, fall back to a real `X` **only - if the winning archive itself also ships one** (co-located, e.g. `soh.o2r` shipping both). The - fallback does **not** reach down into lower-priority archives (L3 → fail). Practical rule: - *ship a `.meta` and its intended fallback real in the same archive.* -5. **Nothing to load → fail** (case4). +See **[meta-loading.md](meta-loading.md)** for the flowchart and worked examples — that's the +source of truth. In short: + +> `X.meta → Y` adds **`Y` as an alternate provider of `X`**. A request for `X` loads the +> **highest-priority provider** — a real `X`, or an aliased `Y` — ranked by the archive where that +> provider's **asset** lives (*not* where the `.meta` lives). An archive's own `.meta` beats its +> own real `X`; if nothing provides `X` or a resolvable `Y`, the load fails. + +"Fall back to the real asset" is just the case where the target `Y` doesn't exist, so a real `X` +is the only provider left. ## How to run a case diff --git a/meta-alias-testing/meta-loading.md b/meta-alias-testing/meta-loading.md index 2736c419aad..5d12bc0743b 100644 --- a/meta-alias-testing/meta-loading.md +++ b/meta-alias-testing/meta-loading.md @@ -1,79 +1,65 @@ # `.meta` alias loading flow How libultraship resolves a resource request when a `.meta` alias is involved, across one or more -layered archives. This is the **intended model** (the target of the override-identity rework); it -matches the rules in [README.md](README.md#resolution-model-what-a-layered-meta-does). It's kept as -a standalone Mermaid diagram so it can be ported into the LUS doxygen docs later. +layered archives. Kept as a standalone Mermaid diagram so it can be ported into the LUS doxygen +docs later. See [README.md](README.md) for the runnable test cases built from this model. -## Terms - -- **X** — the resource path being requested (e.g. `textures/nintendo_rogo_static/gShipLogoDL`). -- **X.meta** — a JSON sidecar that describes/aliases X: a target `path` plus `type`/`format`/`version`. -- **winning archive (W)** — the highest-priority loaded archive that supplies X *or* X.meta. `X` - and `X.meta` are **one** override identity, so a single archive wins the slot (last loaded = - highest priority). +## The rule -## Resolution flow +`X.meta → Y` adds **Y as an alternate provider of X**. A request for `X` resolves to the +**highest-priority provider**, where each provider is ranked by the archive its **asset** lives in: -```mermaid -flowchart TD - A(["request X"]) --> B{"winning archive W for X?
(highest-priority archive
that has X or X.meta)"} - B -- "none" --> FAIL1["FAIL — not found"] - B -- "W" --> C{"does W contain
X.meta?"} - C -- "no" --> REAL["load W's real X
(embedded header / XML)"] - C -- "yes" --> M["read X.meta:
target path Y + type/format/version"] - M --> T{"load target Y
(normal global lookup —
any archive, by priority)"} - T -- "found" --> TGT["load Y as the aliased resource"] - T -- "missing" --> FB{"does W also ship
a real X? (co-located)"} - FB -- "yes" --> FALLBACK["fall back: load W's real X"] - FB -- "no" --> FAIL2["FAIL — target missing,
no co-located real"] -``` +- **real `X`** — one provider per archive that ships a real `X`, ranked at that archive. +- **aliased `Y`** — for each `X.meta → Y` whose target `Y` exists, a provider ranked at the archive + that ships **`Y`** (*not* the archive that ships the `.meta`). -## Which path each test case takes +Highest-priority provider wins. If an archive ships both a real `X` and an `X.meta`, its `.meta` +takes precedence over its own real `X`. No providers → the load fails. -| outcome (leaf) | meaning | test cases | -|---|---|---| -| `load W's real X` | winner has no `.meta`; plain load | case5, **L1**, L1b | -| `load Y (target)` | alias resolved to its target | case1, case3, **L2**, L5 (mod loaded), **L6** | -| `fall back: real X` | target missing, co-located real used | case2, L5 (vanilla boot) | -| `FAIL` | nothing to load | case4, **L3**, and "none" | +"Fall back to the real asset" is **not** a separate rule — it's just the case where the alias's +target `Y` doesn't exist, so there's no aliased provider and a real `X` is what remains. -(Bold = the layered / archive-priority cases.) +## Terms -## Priority / override identity +- **X** — the requested path (e.g. `gLinkHumanSkel`). +- **X.meta** — a JSON sidecar: a target `path` (call it `Y`) plus `type` / `format` / `version`. +- **provider** — a concrete way to satisfy `X`: a real `X`, or an aliased `Y`. +- **priority** — archive load order; last loaded = highest. A provider's priority is the archive + where its **asset** lives. -`X` and `X.meta` share one override slot. Indexing an archive that contains `X.meta` also claims -the base name `X`, so whichever archive is highest priority owns the identity — and only *that* -archive's `.meta` (if any) is consulted: +## Flow ```mermaid -flowchart LR - subgraph low["lower priority"] - L["archive A:
X.meta → Y"] - end - subgraph high["higher priority (loaded later)"] - H["archive B:
real X"] - end - high -- "wins X" --> W["winner = B
→ load real X
(A's .meta ignored)"] +flowchart TD + A(["request X"]) --> B["collect providers of X:
• each archive's real X
• each X.meta → Y whose target Y exists (the asset Y)"] + B --> C{"any providers?"} + C -- "no" --> FAIL["FAIL — nothing to load"] + C -- "yes" --> W["pick the highest-priority provider
(ranked by the archive its asset lives in;
an archive's own X.meta beats its own real X)"] + W --> R{"winner is…"} + R -- "a real X" --> LR["load real X
(its own embedded header / XML)"] + R -- "an aliased Y" --> LY["load Y
(with the .meta's type / format / version)"] ``` -This is why a higher-priority real asset beats a lower-priority `.meta` (test **L1**): the winner -is chosen first, and a lower archive's `.meta` never gets a look in. +## Worked examples — the cross-game skeleton case + +Archive priority, low → high: `2ship.o2r` < `mm.o2r` < `mod.o2r`. `2ship.o2r` ships only the +`.meta`; the real `gLinkHumanSkel` lives in `mm.o2r`. Request: `gLinkHumanSkel`. + +| scenario | real `gLinkHumanSkel` | aliased `gLinkChildSkel` | highest wins → loads | +|---|---|---|---| +| **A** — mod present | `mm.o2r` (middle) | `mod.o2r` (**highest**) | **`gLinkChildSkel`** (the mod's) | +| **B** — no mod | `mm.o2r` (middle) | — (`gLinkChildSkel` absent) | **`gLinkHumanSkel`** (vanilla) | +| **L1** — real in the top archive¹ | `mod2.o2r` (**highest**) | `mod1.o2r` (lower) | **`gLinkHumanSkel`** (mod2's) | -Note the asymmetry the flow relies on: +¹ L1: `mod1.o2r` has `gLinkHumanSkel.meta → gLinkChildSkel` (and a `gLinkChildSkel`); `mod2.o2r` +has a real `gLinkHumanSkel` and is loaded after `mod1`, so it's higher priority. -- The alias **target Y** is a *different* path, resolved by a normal global lookup — it's found in - whatever archive holds it, **above or below** the `.meta` ("reach back", tests L5/L6). -- The **same-path fallback** (a real `X` when Y is missing) is **winner-local** only — it never - reaches down into lower-priority archives (test **L3** fails). Practical rule: ship a `.meta` - and its intended fallback real in the *same* archive. +The whole trick: the alias's provider is ranked by where **`Y`** lives (`mod.o2r` in A, +`mod1.o2r` in L1) — **not** where the `.meta` lives. That single fact makes A resolve to the alias +and L1 to the real, with no "alias always wins" / "real always wins" special-casing. -## Where this maps in the code (target design) +## Notes -- **winner selection** — `ArchiveManager`'s file→archive index; an archive with `X.meta` claims - the base name `X`, and the last-loaded (highest-priority) archive wins the slot. -- **`.meta` read + alias/fallback decision** — `ResourceLoader::LoadResource`, scoped to the - winning archive; `ReadResourceInitData` parses the JSON, `ReadResourceInitDataLegacy` reads a - real/fallback asset's own header/XML. -- **file fetch** — `ResourceManager::LoadFileProcess` (global lookup for the target Y) and the - winning archive's own `LoadFile` (the co-located real `X`). +- The target `Y` is resolved like any normal resource, so it can sit in an archive **above or + below** the `.meta` ("reach back"). +- If `Y` is itself aliased (`Y.meta`), the same rule applies to it — resolution is recursive. From 7abb0559c8c2239bcfaf894e4c958e32e7011674 Mon Sep 17 00:00:00 2001 From: briaguya <70942617+briaguya0@users.noreply.github.com> Date: Fri, 24 Jul 2026 02:16:56 -0400 Subject: [PATCH 08/11] Rework test cases for the "highest-priority provider" model MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rebuild the case set around meta-loading.md (provider ranking), replacing the shadowing-based layered sets: - Single-mod case1-5 unchanged in content; case4 expected flips nothing -> brown (no shadowing — soh.o2r's real gShipLogoDL is always the lowest provider). - Layered sets replaced with: - flagship-* : 3 mods (meta / real / target) replicating 2ship / mm / mod; all three -> magenta, drop the target -> green (the vanilla toggle). - L1-* : real above the alias target -> real wins. - reachback-*: alias target below the .meta -> still resolves. - README: color legend (brown = soh's vanilla real, a real result), case tables, layered docs, run/priority steps rewritten to the model. - BASELINE.md / STATUS.md moved to historical/ (old shadowing model); fresh ones to be recorded after the override-identity rework. o2r builds are deterministic, so equal building blocks show up as renames. Co-Authored-By: Claude Opus 4.8 --- meta-alias-testing/README.md | 151 ++++++++---------- meta-alias-testing/build-o2r.sh | 42 ++--- .../{ => historical}/BASELINE.md | 6 + meta-alias-testing/historical/README.md | 11 ++ meta-alias-testing/{ => historical}/STATUS.md | 8 + ...-meta-target.o2r => L1-10-meta-target.o2r} | Bin 4213 -> 4210 bytes .../{L3-10-lower-real.o2r => L1-20-real.o2r} | Bin 3914 -> 3913 bytes .../o2r/layered/L1b-10-lower-meta-only.o2r | Bin 677 -> 0 bytes .../o2r/layered/L1b-20-higher-real.o2r | Bin 3916 -> 0 bytes .../o2r/layered/L2-10-lower-real.o2r | Bin 3914 -> 0 bytes .../o2r/layered/L5-base-real-meta.o2r | Bin 4193 -> 0 bytes .../o2r/layered/L6-10-lower-real.o2r | Bin 3914 -> 0 bytes .../o2r/layered/L6-20-mid-target.o2r | Bin 3930 -> 0 bytes .../o2r/layered/L6-30-higher-meta.o2r | Bin 672 -> 0 bytes .../o2r/layered/flagship-10-meta.o2r | Bin 0 -> 684 bytes ...0-higher-real.o2r => flagship-20-real.o2r} | Bin 3915 -> 3924 bytes .../{L5-target.o2r => flagship-30-target.o2r} | Bin 3933 -> 3943 bytes ...eta-target.o2r => reachback-10-target.o2r} | Bin 4212 -> 3937 bytes ...er-meta-only.o2r => reachback-20-meta.o2r} | Bin 677 -> 677 bytes 19 files changed, 111 insertions(+), 107 deletions(-) rename meta-alias-testing/{ => historical}/BASELINE.md (86%) create mode 100644 meta-alias-testing/historical/README.md rename meta-alias-testing/{ => historical}/STATUS.md (85%) rename meta-alias-testing/o2r/layered/{L2-20-higher-meta-target.o2r => L1-10-meta-target.o2r} (84%) rename meta-alias-testing/o2r/layered/{L3-10-lower-real.o2r => L1-20-real.o2r} (84%) delete mode 100644 meta-alias-testing/o2r/layered/L1b-10-lower-meta-only.o2r delete mode 100644 meta-alias-testing/o2r/layered/L1b-20-higher-real.o2r delete mode 100644 meta-alias-testing/o2r/layered/L2-10-lower-real.o2r delete mode 100644 meta-alias-testing/o2r/layered/L5-base-real-meta.o2r delete mode 100644 meta-alias-testing/o2r/layered/L6-10-lower-real.o2r delete mode 100644 meta-alias-testing/o2r/layered/L6-20-mid-target.o2r delete mode 100644 meta-alias-testing/o2r/layered/L6-30-higher-meta.o2r create mode 100644 meta-alias-testing/o2r/layered/flagship-10-meta.o2r rename meta-alias-testing/o2r/layered/{L1-20-higher-real.o2r => flagship-20-real.o2r} (84%) rename meta-alias-testing/o2r/layered/{L5-target.o2r => flagship-30-target.o2r} (84%) rename meta-alias-testing/o2r/layered/{L1-10-lower-meta-target.o2r => reachback-10-target.o2r} (88%) rename meta-alias-testing/o2r/layered/{L3-20-higher-meta-only.o2r => reachback-20-meta.o2r} (78%) diff --git a/meta-alias-testing/README.md b/meta-alias-testing/README.md index 3f0fc7faeb7..882869a9259 100644 --- a/meta-alias-testing/README.md +++ b/meta-alias-testing/README.md @@ -11,11 +11,8 @@ a `.meta` file aliases the boot **ship logo** DisplayList to a renamed copy, and title screen to see which asset actually loaded. `f5d2e58` only covered "alias target present"; this kit covers the full matrix, and recolors the assets so the loaded one is obvious. -> [!WARNING] -> The resolution model was corrected (see [meta-loading.md](meta-loading.md) — "highest-priority -> provider wins"). The per-case **expected-result tables below are being recomputed** against it -> and the `.o2r` sets reworked; treat `meta-loading.md` as the source of truth until this notice -> is removed. +The precise resolution rules live in **[meta-loading.md](meta-loading.md)** ("highest-priority +provider wins"); this file is the runnable test cases built from them. ## What a `.meta` alias should do (#1165) @@ -38,88 +35,73 @@ recolored so you can read the result off the title screen at a glance: | what you see | meaning | |---|---| -| **green** ship logo | the **real / default** asset loaded (`gShipLogoDL`) | -| **magenta** ship logo | the **replacement / alias target** loaded (`fancyShipDL.xml`) | -| **brown** ship logo (normal) | the test `.o2r` did **not** take effect (soh.o2r's untouched logo) | -| **nothing** (blank where the logo should be) | the resource **failed to load** | +| **brown** ship logo (normal) | **soh.o2r's own** real `gShipLogoDL` loaded — the vanilla asset (always the lowest-priority provider) | +| **green** ship logo | a **mod's** real `gShipLogoDL` loaded | +| **magenta** ship logo | the **alias target** (`fancyShipDL.xml`) loaded | +| **nothing** (blank where the logo should be) | the resource **failed to load** — needs no provider at all, which soh.o2r's brown prevents (so it doesn't occur in these cases) | -## The five cases (`o2r/`) +Note brown is now a *meaningful* result (soh's vanilla), not "the mod failed to load." soh.o2r +always ships a real `gShipLogoDL`, so it's an ever-present lowest-priority provider in every case. -Each `.o2r` overrides only the ship logo. Because a mod that contains `gShipLogoDL.meta` -*shadows* soh.o2r's `gShipLogoDL` key, "real asset absent" is simulated simply by leaving the -real DisplayList out of the archive — no `soh.o2r` rebuild required. The heavy `*_tri_*` / +## The single-mod cases (`o2r/`) + +One mod dropped over the normal soh.o2r. soh.o2r's real `gShipLogoDL` (brown) is always in the +provider pool at the lowest priority; the mod's assets sit above it. The heavy `*_tri_*` / `*_vtx_*` geometry is never included, so it keeps resolving from soh.o2r and each archive stays a few KB. -| file | real | meta | target | current engine (broken) | after the fix | -|---|:---:|:---:|:---:|---|---| -| `case1-real-meta-target.o2r` | ✓ | ✓ | ✓ | **green** (meta ignored) | **magenta** | -| `case2-real-meta-notarget.o2r` | ✓ | ✓ | ✗ | **green** | **green** (fallback) | -| `case3-noreal-meta-target.o2r` | ✗ | ✓ | ✓ | **nothing** | **magenta** | -| `case4-meta-only.o2r` | ✗ | ✓ | ✗ | **nothing** | **nothing** (correct fail) | -| `case5-real-nometa.o2r` | ✓ | ✗ | — | **green** | **green** (regression check) | +| file | mod ships | providers (low → high) | → result | proves | +|---|---|---|---|---| +| `case1-real-meta-target.o2r` | real + meta + target | brown@soh, green@mod, magenta@mod (alias) | **magenta** | an archive's own `.meta` beats its own real | +| `case2-real-meta-notarget.o2r` | real + meta | brown@soh, green@mod | **green** | target absent → the mod's real | +| `case3-noreal-meta-target.o2r` | meta + target | brown@soh, magenta@mod (alias) | **magenta** | alias outranks soh's brown | +| `case4-meta-only.o2r` | meta only | brown@soh | **brown** | no shadowing — soh's real still shows through | +| `case5-real-nometa.o2r` | real only | brown@soh, green@mod | **green** | plain real, no `.meta` | -The engine fix is proven by **case 1 (green → magenta)** and **case 3 (nothing → magenta)**. -Cases 2/4/5 confirm no regressions. (The "current engine" column is the baseline captured before -any code change — see `BASELINE.md` once recorded.) +case4 is really a **log** check: brown by sight is ambiguous ("did the mod even load?"), so confirm +in the Trace log that the `.meta` was found, its target was absent, and it fell through to soh's +`gShipLogoDL`. ## Layered cases (`o2r/layered/`) — archive priority -The five cases above use a single mod over `soh.o2r`. The issues also require correct behavior -when **two archives** touch the same path at different priorities (libultraship #1165's design -notes): `resource` and `resource.meta` must act as **one** override identity, resolved by archive -priority (last loaded wins). - -Each set is a pair — `L#-10-…` (lower priority) and `L#-20-…` (higher priority). Drop **both** -files of one set into `mods/`, make `20-*` the higher priority, and restart: +These stack multiple mods to test the priority ranking — the crux of the model. Priority = mod +load order (last loaded = highest). To set it, in the Mods menu drag the higher-numbered file +**above** the lower (top of the list = highest priority), or set the `gSettings.EnabledMods` CVar +to `…-10-…|…-20-…|…-30-…` (last = highest). Mods are **not** hot-reloaded — **restart** after +changing which files are present or their order. -- In the Mods menu, drag `20-*` **above** `10-*` (top of the list = highest priority, overrides - those below it), **or** -- set the `gSettings.EnabledMods` CVar to `L#-10-…|L#-20-…` (last in the list = highest priority). +### Flagship — the cross-game case (`flagship-*`) -Mods are **not** hot-reloaded — restart the game after changing which set is present or its order. +Three mods replicating the `2ship.o2r` / `mm.o2r` / `mod.o2r` stack from #1165, priority low → high: -| set | lower (`10-`) | higher (`20-`) | correct result | current impl | -|---|---|---|---|---| -| **L1** | meta → target (magenta) | real (green) | **green** (higher real wins) | **magenta** ✗ | -| **L1b** | meta-only (target absent) | real (green) | **green** | **green** | -| **L2** | real (green) | meta → target (magenta) | **magenta** (higher meta wins) | **magenta** | -| **L3** | real (green) | meta-only (target absent) | **nothing** (real is in a lower archive, not with the `.meta`) | **nothing** | - -- **L1** is the key correctness gap: `resource` and `resource.meta` currently occupy *separate* - override slots, so a lower-priority `.meta` still overrides a higher-priority real asset. It - should render green; today it renders magenta. -- **L3** is decided (see the resolution model below): the alias's *same-path* fallback only uses a - real asset shipped **in the same archive as the `.meta`**. Here the winning archive has just the - `.meta`; the real lives in a lower archive, so there's nothing to fall back to → nothing. +- `flagship-10-meta` — `gShipLogoDL.meta → fancyShipDL` (the alias, = `2ship.o2r`) +- `flagship-20-real` — a real `gShipLogoDL`, green (the vanilla, = `mm.o2r`) +- `flagship-30-target` — `fancyShipDL`, magenta (the shared target, = the mod) -### L5 — the cross-game motivation (order-independent) +| loaded | providers (low → high) | → result | | +|---|---|---|---| +| all three | brown@soh, green@`20`, **magenta@`30`** (alias) | **magenta** | mod present → alias target wins | +| drop `flagship-30-target` | brown@soh, **green@`20`** | **green** | no mod → falls back to the vanilla real | -The point of #1165: a base archive ships an alias plus its native real asset, and a **separate** -mod supplies the shared alias target (nothing overlaps on the same key, so load order doesn't -matter here). Toggle the target mod to switch between "mod loaded" and "vanilla boot": +That drop-`30` round-trip (magenta ↔ green) is the whole point of #1165: the mod works when +present, and the game still boots the vanilla asset when it isn't. -- `L5-base-real-meta.o2r` **+** `L5-target.o2r` in `mods/` → **magenta** (alias resolves to the - mod's shared target — "mod loaded"). -- `L5-base-real-meta.o2r` **alone** → **green** (target absent → falls back to the base's native - real asset — "vanilla boot"; same behavior as `case2`). +### L1 — a real above the alias target (`L1-*`) -Both states already work with the current implementation (the target lookup is a normal -cross-archive resolve); L5 is here as the end-to-end demonstration of the feature's purpose. +- `L1-10-meta-target` (lower) — `meta → fancyShipDL` + `fancyShipDL` (magenta) +- `L1-20-real` (higher) — a real `gShipLogoDL` (green) -### L6 — "reach back" to an asset in another archive +Providers: brown@soh, magenta@`10` (alias), **green@`20`** (real) → **green**. The real outranks +the alias target, so the real wins — the mirror image of the flagship. -A mod ships `gShipLogoDL.meta → fancyShipDL.xml`; the alias **target** (`fancyShipDL.xml`) lives -in a *separate* archive that may even be lower priority than the `.meta`. Three files, priority -low → high: `L6-10-lower-real` (a real that the `.meta` overrides), `L6-20-mid-target` (the target -asset), `L6-30-higher-meta` (the winning alias). +### Reach-back — target below the `.meta` (`reachback-*`) -- All three present → **magenta**. The `.meta` wins the `gShipLogoDL` identity (highest priority) - and resolves its target from the separate archive. +- `reachback-10-target` (lower) — `fancyShipDL` (magenta) +- `reachback-20-meta` (higher) — `gShipLogoDL.meta → fancyShipDL` -This works because the **target is a different path** (`fancyShipDL.xml`), resolved by a normal -global lookup — it's found wherever it lives, regardless of priority relative to the `.meta` -(hence "reach back"). Contrast with L3, which is about the *same-path* fallback, not the target. +Providers: brown@soh, **magenta@`10`** (alias, target lives *below* the `.meta`) → **magenta**. +The target is a different path, resolved globally, so it's found wherever it lives — the `.meta` +can point "down" to it. ## Resolution model (what a layered `.meta` does) @@ -138,39 +120,44 @@ is the only provider left. 1. Build/run SoH as usual (executable at `build/soh/soh.elf`, with `oot.o2r` / `soh.o2r` / `mods/` alongside it). -2. Put **exactly one** case file in the mods folder, and remove any others: +2. Put the case's file(s) in the mods folder and remove any others — **one** file for a single-mod + case, or the whole `flagship-*` / `L1-*` / `reachback-*` set for a layered case (mind the + priority order — see the layered section): ```sh - rm -f build/soh/mods/case*.o2r + rm -f build/soh/mods/case*.o2r build/soh/mods/flagship-*.o2r build/soh/mods/L1-*.o2r build/soh/mods/reachback-*.o2r cp meta-alias-testing/o2r/case1-real-meta-target.o2r build/soh/mods/ ``` 3. (Recommended) set the log level to **Trace** so load decisions are visible — in-game Developer Tools → Log Level, or set CVar `gDeveloperTools.LogLevel` to `0`. 4. Launch `build/soh/soh.elf`. The first logo shown is the one under test; press **A/B/Start** to advance past the logos. Compare against the color legend. -5. **Restart** the game between cases (resources are cached for the life of the process), and - swap the `.o2r` in `mods/` first. +5. **Restart** the game between cases (resources are cached for the life of the process), swapping + the `mods/` contents first. ### Reading the Trace log - Alias resolved to target: the target path (`…/fancyShipDL.xml`) is loaded. -- Fallback (case 2): a trace like `Meta alias target '…/fancyShipDL.xml' missing; falling back to - real asset '…/gShipLogoDL'.` -- Failure (case 4): `Failed to load file at path textures/nintendo_rogo_static/gShipLogoDL.` +- Fallback (case2, case4, flagship-no-`30`): the target is missing and a real `gShipLogoDL` loads + instead — for case4 that real is soh.o2r's own (brown). ## Rebuilding the kit ```sh ./meta-alias-testing/regen-assets.sh # regenerate src/assets from soh's real ship logo -./meta-alias-testing/build-o2r.sh # zip src/assets subsets into o2r/case*.o2r +./meta-alias-testing/build-o2r.sh # zip src/assets subsets into o2r/*.o2r ``` ## Layout ``` meta-alias-testing/ - README.md this file - PLAN.md the implementation + test plan - regen-assets.sh derives src/assets/ from soh's gShipLogoDL (recolor + alias) - build-o2r.sh assembles o2r/case*.o2r from src/assets/ - src/assets/… generated test assets (default green, replacement magenta, the .meta) - o2r/case*.o2r the five single-archive test archives - o2r/layered/L*.o2r layered (two-archive priority) test sets + README.md this file + meta-loading.md resolution-model flowchart (source of truth) + PLAN.md the implementation + test plan + regen-assets.sh derives src/assets/ from soh's gShipLogoDL (recolor + alias) + build-o2r.sh assembles o2r/*.o2r from src/assets/ + src/assets/… generated test assets (real green, target magenta, the .meta) + o2r/case*.o2r the five single-mod cases + o2r/layered/flagship-* the 3-mod cross-game flagship set + o2r/layered/L1-* real-above-target set + o2r/layered/reachback-* target-below-the-.meta set + historical/ superseded baseline/status notes (old model) ``` diff --git a/meta-alias-testing/build-o2r.sh b/meta-alias-testing/build-o2r.sh index aef944f305a..18720cb61a2 100755 --- a/meta-alias-testing/build-o2r.sh +++ b/meta-alias-testing/build-o2r.sh @@ -46,30 +46,22 @@ build_o2r case3-noreal-meta-target "meta-test 3 meta+target" "${META[@]}" build_o2r case4-meta-only "meta-test 4 meta-only" "${META[@]}" build_o2r case5-real-nometa "meta-test 5 real (no meta)" "${REAL[@]}" -# --- layered cases: 20-* is the higher-priority archive (loaded last, should win) --- -# L1 higher real must beat lower meta+target -> green (bug shows magenta) -build_o2r layered/L1-10-lower-meta-target "L1 lower meta+target" "${META[@]}" "${TARGET[@]}" -build_o2r layered/L1-20-higher-real "L1 higher real" "${REAL[@]}" -# L1b higher real over lower meta-only -> green -build_o2r layered/L1b-10-lower-meta-only "L1b lower meta-only" "${META[@]}" -build_o2r layered/L1b-20-higher-real "L1b higher real" "${REAL[@]}" -# L2 higher meta+target beats lower real -> magenta -build_o2r layered/L2-10-lower-real "L2 lower real" "${REAL[@]}" -build_o2r layered/L2-20-higher-meta-target "L2 higher meta+target" "${META[@]}" "${TARGET[@]}" -# L3 higher meta-only over lower real -> green or nothing (open question) -build_o2r layered/L3-10-lower-real "L3 lower real" "${REAL[@]}" -build_o2r layered/L3-20-higher-meta-only "L3 higher meta-only" "${META[@]}" +# --- layered cases: NN-* prefix = intended priority (higher number = higher / loaded last) --- -# L5 cross-game motivation (order-independent): a base ships the alias + its native real, -# a SEPARATE mod supplies the shared alias target. -# base + target present -> magenta ("mod loaded") -# base alone -> green ("vanilla boot", falls back — same as case2) -build_o2r layered/L5-base-real-meta "L5 base real+meta" "${REAL[@]}" "${META[@]}" -build_o2r layered/L5-target "L5 shared target" "${TARGET[@]}" +# Flagship (cross-game): three mods replicating 2ship / mm / mod. +# mod1 meta (2ship) < mod2 real,green (mm) < mod3 target,magenta (mod) +# all three loaded -> magenta (alias target outranks the real) — "mod loaded" +# drop mod3 -> green (mod2's real, target absent) — "vanilla" +build_o2r layered/flagship-10-meta "flagship mod1 meta (2ship)" "${META[@]}" +build_o2r layered/flagship-20-real "flagship mod2 real (mm)" "${REAL[@]}" +build_o2r layered/flagship-30-target "flagship mod3 target (mod)" "${TARGET[@]}" -# L6 "reach back": the winning (highest) .meta overrides a lower real, and resolves its target -# from a SEPARATE archive that may be lower priority than the .meta (a different path, so it's -# a normal global lookup). All three present -> magenta. -build_o2r layered/L6-10-lower-real "L6 lower real" "${REAL[@]}" -build_o2r layered/L6-20-mid-target "L6 mid target" "${TARGET[@]}" -build_o2r layered/L6-30-higher-meta "L6 higher meta" "${META[@]}" +# L1: a real ABOVE the alias target -> the real wins. +# low = meta + target (magenta), high = real (green) -> green +build_o2r layered/L1-10-meta-target "L1 low meta+target" "${META[@]}" "${TARGET[@]}" +build_o2r layered/L1-20-real "L1 high real" "${REAL[@]}" + +# Reach-back: the alias target lives in an archive BELOW the .meta -> still resolves. +# low = target (magenta), high = meta -> magenta +build_o2r layered/reachback-10-target "reachback low target" "${TARGET[@]}" +build_o2r layered/reachback-20-meta "reachback high meta" "${META[@]}" diff --git a/meta-alias-testing/BASELINE.md b/meta-alias-testing/historical/BASELINE.md similarity index 86% rename from meta-alias-testing/BASELINE.md rename to meta-alias-testing/historical/BASELINE.md index 633fe77570d..e37d458058d 100644 --- a/meta-alias-testing/BASELINE.md +++ b/meta-alias-testing/historical/BASELINE.md @@ -1,5 +1,11 @@ # Baseline — current engine (before the fix) +> [!NOTE] +> **Historical.** A faithful record of what the **unmodified** engine did, but its +> "expected after fix" column uses the old (shadowing) model — since corrected to +> [meta-loading.md](../meta-loading.md). The case set has also been reworked; see +> [README.md](../README.md). + Captured on 2026-07-23 against the **unmodified** libultraship submodule (`2bfbde3a`), by dropping each archive into `build/soh/mods/` one at a time and reading the color of the first (LUS) boot logo. diff --git a/meta-alias-testing/historical/README.md b/meta-alias-testing/historical/README.md new file mode 100644 index 00000000000..385a948fe43 --- /dev/null +++ b/meta-alias-testing/historical/README.md @@ -0,0 +1,11 @@ +# Historical records + +Superseded notes kept for reference. They document the first engine approach and +an earlier (shadowing-based) resolution model that was later corrected to +[../meta-loading.md](../meta-loading.md). Fresh baseline/status will be recorded +against the reworked implementation. + +- `BASELINE.md` — what the unmodified engine did (still accurate as history; its + "after fix" expectations use the old model). +- `STATUS.md` — the change-by-change engine experiments (index / fallback), before + the override-identity rework. diff --git a/meta-alias-testing/STATUS.md b/meta-alias-testing/historical/STATUS.md similarity index 85% rename from meta-alias-testing/STATUS.md rename to meta-alias-testing/historical/STATUS.md index 10c1879e947..1171501902d 100644 --- a/meta-alias-testing/STATUS.md +++ b/meta-alias-testing/historical/STATUS.md @@ -1,5 +1,13 @@ # Test status — engine changes +> [!NOTE] +> **Superseded / historical.** This tracks the first (shadowing-based) engine +> approach and a since-corrected resolution model — some "expected" values here +> (e.g. case4, L3) no longer match. The current model is +> [meta-loading.md](../meta-loading.md) and the current cases are in +> [README.md](../README.md); this file will be replaced with fresh results after the +> override-identity rework. + Tracks the five test cases + control as the three libultraship engine changes (branch `meta-alias-fallback`) are applied **one at a time**. See `BASELINE.md` for the pre-change reference and `README.md` for how to run each case. diff --git a/meta-alias-testing/o2r/layered/L2-20-higher-meta-target.o2r b/meta-alias-testing/o2r/layered/L1-10-meta-target.o2r similarity index 84% rename from meta-alias-testing/o2r/layered/L2-20-higher-meta-target.o2r rename to meta-alias-testing/o2r/layered/L1-10-meta-target.o2r index 2fee137c770b73c14d0e0c88a46505ee1e14025d..664be502d460da449368f4703fb538c937a6cf38 100644 GIT binary patch delta 135 zcmeyW@JT^Dz?+$civa=@8O-DUO6dbR`V+%^5lQK8jO1<$1^)jPUq8Ra%7&I0Aya_(_%b1`9EJaQy>RWz-ICael4b}ToV^4 WO#Z@e$<)FFV)+Xwuvznh>;(Wh_$09a delta 141 zcmeyQ@Kr%Pz?+$civa=@85neS=^6n!Mia&5IE)lBGSf3si#A#b@^ZpuCWr9)Fvd<^ z$ZN*fGWi3S;^d#aT1*ERCQszEpPa&{&FC_D0$(=M8CI}>4Zk*12nUd9GkGb$7UPY{ c7x<%?+Ic2_lcT!F~z?+$civa=@8T=3UGHC)iniIw4*bEgiGSf3Q+PL#@!o?@|@c1x>Ouoru z##lXBfY+O8JHzCKd^VE>7+EII<<(-cW(IRv`Sh3$vx1q0e0q%DlNa!%Go9y}EXb=c RnTMNuvI)NcTM-Y)Rsi~pcS=q?z?+$civa=@8Psgd+O&Wit%>4t?8XW?`Q@oa8?D`WIN=hLyLo&VLnq(h zF=MQm%+Kr1w1Z*t0zR9`GkEowY?#4J7Ct?uBdlO%0iPbD&*b@h=}Z^6CJXQ?Oy=h1 W77OrZWdm8r0fYyc7#NCqKs*2gG#@Yk diff --git a/meta-alias-testing/o2r/layered/L1b-10-lower-meta-only.o2r b/meta-alias-testing/o2r/layered/L1b-10-lower-meta-only.o2r deleted file mode 100644 index 7dc313c8b2efb1ba5af846ad79dd86d7629cf218..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 677 zcmWIWW@h1HfB;2?ghySo41gRE<^|&1#JtS3)Z!Aotm6EB^{;Y{FK!AvecsD%=|nhE5ll@02HGkRzOHjAXSoDQBqozTC9&- znH;jRyv)3k)V!4Z_@eyu{P^OM#FETpuwD_MDh?o4WN2rPsLuwnnt)gjh~ZXX)teri zky+paG|a_E59ANi?u}f{4m=Lue{=1O+)|u=y!h26-=iX1hG&e0bMId0SthG6#@hp z-a1ADneY(6rwtU$5CGB!3Ou+rtN{lx3wN-C%!0UnA3?LwBO$<>l?|ky1qh{phLnN~ GVgLX}wyxj+ diff --git a/meta-alias-testing/o2r/layered/L1b-20-higher-real.o2r b/meta-alias-testing/o2r/layered/L1b-20-higher-real.o2r deleted file mode 100644 index acd7d073e82a18933975fd36e7b2f6b7025fb419..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3916 zcmWIWW@h1HfB;2?=RG;6b$}cY<^|&1#JtS3)Z!Aotm6EAXSoDQBqozTC9&-nH;jR zyv)3k)V!4Z_@eyu{P^OM#FETpuwD_MDh?o4WUyNz_Edw3fgziffx(WT-rU5J`1Ih6 z%mN>vfi6DrX~rq>K(W-K%*32{0|Vo;A+hRO-LPiT-@J9&~0A@#)p)ZTD^1m|3iO>DqE$cK;=L8mqSzu*!?g zHxt}DVaeJ}OIbJ{ifQ|AQQ9Z$ejp-uVYjZfg^XcQU*GS2cecRn@K2U(rtIq_VG=X$-P_p8^ET`li`z!=iWAk7g0{>Q$yG~j+bnrby<&~6 z#Z#d$p}6ggr#TBg>XmE{cu~4E^o4-+jnZ#jl52A}H!b+4w0Ld4*tMgNtc_AE@3<(g zdH>m!@m~Mh9!Di#pH*|NXnE;*CCQxlb(c4Rc@^_sdjp<7NskPS3W_?)tJbqmEdRGy zO+j$w{yX;_8`^sP>n1t%u{&&0$Px})%y828`O@=`r@qu?3_Nu{`h@kW$gnMsoOBMG zR;TWGsJZ;6QDXnenPIvt)?w2%r)$|N+B@86R#>>-=3@@>i~7ss%FX(4kM-75ZfBLO9zDO&A+qr65j&ne7JSK`pDIQE zONcHxJNrYG?vlAL%jd0Nn&c8L{<>|7Etl+ks&93FwA{Dv`<=tBQxD$?o;jEK8rKJh z+0#B&@rr-Q`<5!YS=je{sa?3j(nHU#HeNL;m@Kj2s8rRFZHMp9lMskd?zp>GT1R1n zd(5043;X}Fi!(i+955wQC^P8cVV38@F+Ce}`g^kdthsdSUt0xT=?JZIGuZ3icj=QI zQ|d97V_+p80=_3rcdvAHE#|9qY;-r9xVU03riiPWtWQS8ghpIU6{TU`g)rxGi;vp$~T0cNyJ=kmR)~*>UohPGcDFF ze6gY|ZgZ5!#yKW`W=;-EX?ps3%Khc;Ve=Jld{}m>CFrI1PenC57R~C#wv&JPKl$d+ z#9?`aOX;^YOaGbx)^%|jrvE3XPcSQ&ZQCNX^_S==F)uBb@~rT+^PKmn_j7icCVn|$ z#`RaFQ^>U|bIK11(X_YSU#c{h%HH&!_=>6PNVt%8&~~v8Yb0XA4ro>WD+=AI9X&T8 zoa---ROj?I8MEi1E9O?tU0I(0di~#BGfF-mType(QqcYWNqhzQ$JMtE!PWs|J^-7lAq4Lc)4M)@AXgZ`k z9a>RTCy%DV(KJY28f4ynKRAXF*h*w$V9+F(29er?Tc_OZYcddMU4MOf#xJG=Qs2FN zA~G(pT=X!#XDXVum2=y&67$!$^&kCXFw9n}x}KkAWoc>r);`8GkB5Z3N#GO3F zZ&zHYmilFL{`E^sb8B(wA13dmxV*VC9n!iow}!4UxYKdvR`KC&C;em=8A-UjSv!BD z5nI%UIXBDXqqmo>D>scY`28(ReWH||gT2>lQSo{2pErHZ5Vy+sd%pFz<%PfpqU>_p zm}S|nmo0d_DDHk^{@UWgh9?@Axr7W9SJ+C_&RNA1aAJ|C#j?p3B^#SBMh5c5Y+t1j zdHHG6h6zXNTT_kW-6LX58HlU_3DZt_1`LFuZjP2Qra58mKnH6oP0_R|5h-+UkHzxHcRe50F`iW*>Uj z17sG&^}7g~MW`bJ@h-wH^d=_4E<0vI?j~TD5%G2%12%B+hYVb3LCa>Q>BQsEHb%?d0}Kz&aRhJ(Nse<=@$2LOPqD#-u< diff --git a/meta-alias-testing/o2r/layered/L2-10-lower-real.o2r b/meta-alias-testing/o2r/layered/L2-10-lower-real.o2r deleted file mode 100644 index a4222054608170e9ce38e1148c8d637a45fa5064..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3914 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 diff --git a/meta-alias-testing/o2r/layered/L5-base-real-meta.o2r b/meta-alias-testing/o2r/layered/L5-base-real-meta.o2r deleted file mode 100644 index 4c42ba7d251265c85b2f24853a80044e60126834..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4193 zcmWIWW@h1HfB;1XhFf8hdO!{c^8#^hVqRuiYH^8PR&jn_wNhSUZmN=%l8>oEQettc zLQ!gBj&^QpNurXDQgVJuYJ6F0QE_H|o|2VeEmr`l!4Mn3Bqxw8Nv$X;ElMrc$E{2b zSy^6YUP)?RN`8D%etLd2v8LV5GyjYvq#it16fT#401o*3aon5gEKM< ze1L|z_~?PWVA{QrtJ#6a;rnl{osnCL(~lRwy5!q*OzOgu9ZMc5{w<&SNc#Tcx9coE z3g&z|e@Hs-o9_x!Vd+l_v6K2svJ$RbS1yl>xtx+K@;Zf;ueO=SK4oili{*qbsCmwUp-p$7x zUzX2X z!8FMwT*xzM>$A-!^0Ctn=tO)g3$4_Un))E1;Om#{#_O{r%zn>3J-2S|%GtMnW$$O3 z{LXI1mS6EcuYOOw#Q5s%k7nIjdhNTO-OZ~oUX_`-vFWP93#Y46t3zu}{EE6_ENW!6 zB(7vt%_n`)D`}Po`MZziqo%=))7q_dfN8LR8fnl7TN-?(EW*pgwEXO^B*70-s~eO* zEL*m}ZkuAuB|D$$TiqWm_wD?(UV=QP|)fGv~*`{=e+vOwT6=Ovx0= z40?E&<+*T7&jy|To@_sBF5UXqRzX)fLaW>i_PY07`eetHdd%e*SjmTg?@80$D_vcS z`6?Y7-OVO$Zwp$zeAltvM^xwSzIx`tdrN&yyZIqo{vK9a7Q+UUB8iD5({s+m@h@BdDBRgmE4Y65 z-bRLaGseHZ-lob7n(^ylfzP) zo_?Nkf4O_ue8n3dmYr$|dg=XBQO%A;vwE@Z^^;VN8;48-uD_da9_<{6#i3j zws++DOB|&h@g=X5zBo_4l4W~&-w64spp=%88bX>Vre0bYQKbb{F5-xAn&fjRn7WHAy%`*Au?PcrAO`{Bc ze+yHeC}rnh@AX<#eBS%#O`kKwtup?eZ~bj~A@G4HyWBQrS+?tC3mz|uyWg0haBd??3(Zr^U^zo#P{>C{bh9f@?B(V@EL!5^F>PSuMAe# zeR`!fF*4Wm)P)PHKCZa_n<>DXkx7mjcYg^OPYl2~V_4D%Vxjb&FtmbHKmn*n1OWmJ zZyh6nOnB!7pEghr2?9Xc8h}i=HmrRfh*^lP2YQbQWERBrdkC6^l;FVqCv>yXyEO>2 zi-A_)cPF7v4!T|F%~yn7-VX-RB7Yg;vkO!WA^gP-Oflr!g>DaeQG>9j do12h5Na-NJn-v%Y3=Et=C<_dDJ1}PJVf6 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 diff --git a/meta-alias-testing/o2r/layered/L6-20-mid-target.o2r b/meta-alias-testing/o2r/layered/L6-20-mid-target.o2r deleted file mode 100644 index 00193489e6f560c6c3e8fe7ee22fae4b0b7fb650..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3930 zcmWIWW@h1HfB;2?Clf9{)BAB5 zVTwx@EnvA9^6ho{?0$h=?V0}7hIYrVf1g)fbM)A+IDK1kV;GR8k>t zE2rm&$>Ezk&xO};u8F-InV3*jeO%0FOSGi0#L>71itjzWE~W)-nK?!BT$;uF2OGaV znBpDbS#g(5NBzLj-52gUExbN8Y@zd;gzGo8)3$6o%hhzNyJy?J$O!T7_a`Pi{UFr3 zVc&Ui)(ZP=Mr@rXhAWGLR%`6k=qyxtU9R82yMph_zXJvzx;qb_IB-(&-pjpmF86-+ z&uVa4@~^0#ol(ln?x!c0r5wY>rWL*sy-X_RbEn(&hc8>t;-YDvulRmNTI9tp?u|Ux z-%NejwtP<7=@whH^r)=@Zz5us#jN_$@`oXjzp3S)lI`RQ{U85Y{$ALy@n!q9aE^JI zdfYL4CuIFo?AOnEXT{@TnfcXNL|3Hc^Sdir(x%-nI_4)U2en)i{%!E#kwuT{$>)K8 zl0zn#Tz1lp zH|2Weze<J3-EclTmnvHdaswv1geuV$6+ewY?=`BD<=YR3z$ zt4!}X)#_^P4eXILzaZT;}o$cCTeA)h~Wqc9Z2bgoC zj=eUGIk5ZY^w2YYCbrjq#xzdhnjOv_cKLvBQiE9b3&Gob<<&_J3B3ws-}5#!9FQw4 zeAI6DO+JQWj=xjjGS4NRt^C4s{0j^ZZL%?1XZcoR%b&HcJpz@(UP>PPB4_D!`ZvcE zNnuHd66d?!v2ssEMbBD3VP%!O>2goXYwf&OQtySPSG)@|Zr=ZV&ypWCK^MRA&z@1l z86#@6zoswf*Zt?+&YNU&WSdfEwOnJ(EtwLv<%mki*WKGVn|>X7qc5Q3FV>mlaw&F` zVYTgyeO>XqtSf!~zWv6=RAb5V#a#Dl55pO+Rs4ar907MKGJe_AO|=acNPo1WZPDUe zm1naQj}~3}knR^Xk#pMl;JW#8k+p3Jhi7XEdo9&}+CJllz>+t;-+W%#PrS{{sq{oZ zyW`D!0h>)uqC2VnhCZfOdSCcWpI&%a zYxeX#jEUl_uBY&xUApE;X3AHN(%p5ht{r_bU2EmlnwIxzo9$_s4h=`s;b=OfJRMq5 zR40$7!O=8GUK-5hC<;zt1hx{{7#MU3ra`1OpVd3%aXectA-dEi~^p}}*om1*L$kY~H+h;?bslVD%=Vp`{to^O9%u$J8R zU%9UFiO%9>H;*6NdusROqy^kjp`Lzce=qe@Q>|$V*|P4J#d`CgirN^iYny{#Jovg_ zL~go+d$YMv#Vx)Pi5Tm~zV51O_PRaiehMxMQW14oP;-KRL5k(sUc>BQx_6IcnS+iDk-F*4f&G7sJt23*n@A);UW6L(FT_z@3Hm|7?w1GSSZ~Qbgdu-FaYXiK!5W2IwcVABJ4tMZX)cmVJ75m0(Kb@Z`ToEV;6rI5wOb; zpIxA81K}?pU|)}XyU^`HFB%Z`TmU9V{Pv&~6an6>!14vu7v*3$3hV*Y^MH5&Foi?> diff --git a/meta-alias-testing/o2r/layered/L6-30-higher-meta.o2r b/meta-alias-testing/o2r/layered/L6-30-higher-meta.o2r deleted file mode 100644 index fa7dc3a9e84eebfbc553dd0acd1e74040b07bcaa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 672 zcmWIWW@h1HfB;2?cP`aiwSgQE<^|&1#JtS3)Z!Aotm6E! z%)FA+yp;U-qWtvy_~MeplFVeVUJ;-w4j@)!XlIY8&jzxZfLITR;Z|VPn;x8zS>OXS z%*96!WvdEb0jmk03J!to3jZDVf zk9PBJk1^Ebod4}%-8+T=Z$>6LX51kTbUOnh5HD#2u~5PvT`Ncd41j_a0t6V|I)(w6 z@DRYK4HV1}0Mb?qWWu#!4LFEdxPu*J7R2>C37Ul-2?5@$Y#{wCKqv(?qyS_P0|5KI Btg-+A diff --git a/meta-alias-testing/o2r/layered/flagship-10-meta.o2r b/meta-alias-testing/o2r/layered/flagship-10-meta.o2r new file mode 100644 index 0000000000000000000000000000000000000000..355fd925b6912b6352bf6b8aa95742f620c9cc20 GIT binary patch literal 684 zcmWIWW@h1HfB;1XhTHpv%z+#b<^|&1#JtS3)Z!Aotm6E&H^x}-n z0)^cC6hnpF)RII64I_|%rjm|Qa(+r`d|7Hyab|v=l9gdCR{*LF5Uao>Cy*>jttcri zN-fsMtxOJCSzcydNorn7etc1WdVYLyNn%N6GFYz&P!$IdD>AgRN7QEnSxrE!2gGnI zu^=E-5a@@9e5nR|K{2mxurP$c=4-CzD>uZE~OK&f=qB&ZqN-r1QS{t}qpr{-h8)slOyE;mUR8g8A{nN7X`Z8~y%dAKh1RmN#>b zB74y8%Nm)CyC3c5-5z78$vOYq!@7420p5&Ea?H3xALw=lMj&3&2x6hc1%_6T3Mc>t zD+CBIymd?hGT|YBPa7zhApoSU6Uc;X!y0f9vv3DH$SjEKj}kNsJrV-ES=m7PS%6Rq KXh;pnAO-+tU9ZCc literal 0 HcmV?d00001 diff --git a/meta-alias-testing/o2r/layered/L1-20-higher-real.o2r b/meta-alias-testing/o2r/layered/flagship-20-real.o2r similarity index 84% rename from meta-alias-testing/o2r/layered/L1-20-higher-real.o2r rename to meta-alias-testing/o2r/layered/flagship-20-real.o2r index 4690ae85b263143bb47fa6d4cd9f3ae1684383d1..43dda7853f502f3da424b7737f64ba257233882b 100644 GIT binary patch delta 145 zcmX>tcSTM-z?+$civa=@8O$A;Czt>^CKJVF#nW;U(~C1Q3lwtmQ;ZafQWJ9&G;(t_ zH##}-aKaT%?&a}ejGugq$BeOUvLLTF;~^laIJu73i^+|7@3MfqG<>sDj$}hlH%L8%&0G-w*5&!@I delta 135 zcmca2cUn$7z?+$civa=@83aF;Kh_3vv?q$o^7$AlWMrmiq!uX@r6%TV^mgLmgv(E! z#^b{nHu*7+8Ds6lh02p{c{Lb!PEKTYn7oWvo5_}WvOS;uWFbCn#-o$%__CRNIe-E- Ylb`cxFq7AWNArx+`gBo?KomMCZd zIhq??{CGIwYA4U;@nKAw{DQ}fv2(IKuQ$_ChRGZFY$mVZ)noEx1~WzY^q4NPf|(6` gdW?~i*Yl+_-Q}9Bz^gEspPPTOJHG&14-d!{03L)X9smFU delta 129 zcmaDZcUMk4z?+$civa=@89qswN9h7Nx)a4^1$;~uiZc?6Qd1O45{uGPOE&uX@o>Ub zOkT?4!x%C76OS2V{lo`~lf8Jg7fj~B diff --git a/meta-alias-testing/o2r/layered/L1-10-lower-meta-target.o2r b/meta-alias-testing/o2r/layered/reachback-10-target.o2r similarity index 88% rename from meta-alias-testing/o2r/layered/L1-10-lower-meta-target.o2r rename to meta-alias-testing/o2r/layered/reachback-10-target.o2r index e106527e000218d00d9536aac41b5931c9108478..5ff5e1d792985150791db67eb4b498f2be25eb0c 100644 GIT binary patch delta 90 zcmeyO@K8=Dz?+$civa=@84iC4IyzBEiLWR%F*zeCF*#cyC%;@_qmvtt5F?WuGpfSP YfxMfUL|EBCnmK^*C=&xi8xM#F0Q8CzbN~PV delta 311 zcmaDT_eDV{z?+$civa=@8J1+9HJ&J>#OGtEkdt4YTBMMhT9T-}(aDWRl9@#WsEPxK z6&c#uBkHq(tR^7Vo1D+9Sf3u8ky+rApPujHqX*Js+P#sh*@4I5`){tDkz0z>j~BnX zcW#9OCBlyEuZ>G`u^j$>nuJB=6pJTNILJE?+R04=}!u=lln`t60TfVE|?!L zd{iyuw$blT_R)P6XL&Q{D6$9bzO0eSxckv=-t94lnw;~$J*<1j5a7+oBp}C(>WR&> xcs4U>F*3<9<8U?5 Date: Fri, 24 Jul 2026 02:39:25 -0400 Subject: [PATCH 09/11] Record fresh baseline (stock engine) + pin libultraship to develop (2bfbde3a) Baseline of the reworked test kit against the unmodified engine (2bfbde3a = develop's pin). All observations matched predictions: - real+meta cases load the mod's plain real, .meta ignored (green): case1/2/5, flagship, L1 - meta-only cases shadow soh's real but can't resolve (blank): case3/4, reachback Resets the submodule pointer from the earlier engine-change commits back to develop so the branch reflects the stock engine this baseline was taken on. The override-identity rework and a fresh STATUS.md come next. Co-Authored-By: Claude Opus 4.8 --- libultraship | 2 +- meta-alias-testing/BASELINE.md | 42 ++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 meta-alias-testing/BASELINE.md diff --git a/libultraship b/libultraship index a0f86f9e1c7..2bfbde3a72c 160000 --- a/libultraship +++ b/libultraship @@ -1 +1 @@ -Subproject commit a0f86f9e1c7c6a81fae01a598a51843d24856daa +Subproject commit 2bfbde3a72c119f8073ad762ec6be131dff5df66 diff --git a/meta-alias-testing/BASELINE.md b/meta-alias-testing/BASELINE.md new file mode 100644 index 00000000000..fb1d832faef --- /dev/null +++ b/meta-alias-testing/BASELINE.md @@ -0,0 +1,42 @@ +# Baseline — stock engine (before the fix) + +Captured 2026-07-24 against the **unmodified** libultraship submodule (`2bfbde3a`, += `develop`'s pin), running the reworked test kit ([README.md](README.md) / +[meta-loading.md](meta-loading.md)). Each case's mod(s) were dropped into +`build/soh/mods/` (priority order set for the layered sets), and the first (LUS) +boot logo color was read. + +Legend: **brown** = soh.o2r's vanilla real · **green** = a mod's real · **magenta** += alias target · **blank** = failed to load. + +## Results (all matched predictions) + +| case | mod(s), low → high | observed (stock) | expected after fix | +|---|---|---|---| +| control | (none) | **brown** | brown | +| `case1` | real + meta + target | **green** | magenta | +| `case2` | real + meta | **green** | green | +| `case3` | meta + target | **blank** | magenta | +| `case4` | meta only | **blank** | brown | +| `case5` | real | **green** | green | +| flagship (all 3) | meta / real / target | **green** | magenta | +| flagship (drop `-30`) | meta / real | **green** | green | +| `L1` | meta+target / real | **green** | green | +| reachback | target / meta | **blank** | magenta | + +## What the stock engine does + +The `.meta` mechanism is non-functional: `Archive::IndexFile` strips the `.meta` +suffix, so `LoadFileProcess("…​.meta")` never resolves, and a mod's `.meta` still +*claims* (shadows) the base path. So: + +- When a mod ships a real asset, its `.meta` is silently ignored → the mod's plain + real loads (**green**): case1, case2, case5, flagship, L1. +- When a mod ships only a `.meta` (no real), it shadows soh.o2r's real but can't + resolve → nothing loads (**blank**): case3, case4, reachback. + +## What the fix must change + +- **Alias resolves** → magenta: case1, case3, flagship (all 3), reachback. +- **No shadowing** → soh's vanilla shows instead of blank: case4 → brown. +- **Unchanged** (regression checks): case2, case5, L1, flagship-drop-`30`. From 0ab628442a6c5ffebae0391daa095347e64579aa Mon Sep 17 00:00:00 2001 From: briaguya <70942617+briaguya0@users.noreply.github.com> Date: Fri, 24 Jul 2026 03:49:08 -0400 Subject: [PATCH 10/11] Record after-fix status; bump libultraship to provider-model rework Bumps libultraship to the meta-provider-resolution commit and records STATUS.md: all 10 cases (control + 5 single + 4 layered) match the expected after-fix results, with five flips proving the fix (case1/case3/flagship/reachback -> magenta, case4 -> brown). Co-Authored-By: Claude Opus 4.8 --- libultraship | 2 +- meta-alias-testing/STATUS.md | 38 ++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 meta-alias-testing/STATUS.md diff --git a/libultraship b/libultraship index 2bfbde3a72c..5d30234c708 160000 --- a/libultraship +++ b/libultraship @@ -1 +1 @@ -Subproject commit 2bfbde3a72c119f8073ad762ec6be131dff5df66 +Subproject commit 5d30234c708a14ece477eed22c2879fb41a3ceae diff --git a/meta-alias-testing/STATUS.md b/meta-alias-testing/STATUS.md new file mode 100644 index 00000000000..6a028c7a291 --- /dev/null +++ b/meta-alias-testing/STATUS.md @@ -0,0 +1,38 @@ +# Status — after the provider-model rework + +Captured 2026-07-24 against the **`meta-provider-resolution`** libultraship branch (the +"highest-priority provider wins" implementation, off `develop` / `2bfbde3a`), running the +reworked test kit. Every case matches its expected after-fix result. + +Legend: **brown** = soh.o2r's vanilla real · **green** = a mod's real · **magenta** = alias +target · **blank** = failed to load. + +## Engine changes (`meta-provider-resolution`) + +1. **`Archive::IndexFile`** — index every file under its literal name (no `.meta` stripping), so a + real `foo` and its `foo.meta` are distinct index entries. +2. **`Archive::GetPriority`/`SetPriority`** + **`ArchiveManager::GetFilePriority`** — O(1) + load-order priority of the archive that owns a path (higher = wins). +3. **`ResourceManager::LoadResourceProcess`** — only bail when neither the real file nor a `.meta` + exists (so meta-only resources reach the loader). +4. **`ResourceLoader::ResolveMetaAlias` / `LoadResource`** — pick the higher-priority provider + (real asset at the path vs the `.meta`'s alias target); ties go to the alias. + +## Results (all match) + +| case | baseline (stock) | after fix | +|---|---|---| +| control | brown | **brown** ✓ | +| `case1` | green | **magenta** ✓ | +| `case2` | green | **green** ✓ | +| `case3` | blank | **magenta** ✓ | +| `case4` | blank | **brown** ✓ | +| `case5` | green | **green** ✓ | +| flagship (all 3) | green | **magenta** ✓ | +| flagship (drop `-30`) | green | **green** ✓ | +| `L1` | green | **green** ✓ | +| reachback | blank | **magenta** ✓ | + +Five flips prove the fix: **case1** green→magenta, **case3** blank→magenta, **case4** blank→brown +(no shadowing), **flagship** green→magenta, **reachback** blank→magenta. case2 / case5 / L1 / +flagship-drop-`30` are unchanged regression checks. From ff7cd32c23ed89e9cd06a2a9cd8284fbce983312 Mon Sep 17 00:00:00 2001 From: briaguya <70942617+briaguya0@users.noreply.github.com> Date: Fri, 24 Jul 2026 04:13:56 -0400 Subject: [PATCH 11/11] Bump libultraship to clang-format-14 fixup (1b190aa5) Co-Authored-By: Claude Opus 4.8 --- libultraship | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libultraship b/libultraship index 5d30234c708..1b190aa528d 160000 --- a/libultraship +++ b/libultraship @@ -1 +1 @@ -Subproject commit 5d30234c708a14ece477eed22c2879fb41a3ceae +Subproject commit 1b190aa528dc80f442013a9b47fb4459ea144399