diff --git a/README.md b/README.md index 162a2239522..84a495960b2 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/libultraship b/libultraship index c57da1b4afa..12bf43255b2 160000 --- a/libultraship +++ b/libultraship @@ -1 +1 @@ -Subproject commit c57da1b4afa775b24b58b2adf93d63d3b561bb65 +Subproject commit 12bf43255b2c9e12410cb37a84e40f070ab0e435 diff --git a/meta-alias-testing/.gitignore b/meta-alias-testing/.gitignore new file mode 100644 index 00000000000..9669d92f91c --- /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/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`. 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..882869a9259 --- /dev/null +++ b/meta-alias-testing/README.md @@ -0,0 +1,163 @@ +# `.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. + +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) + +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 | +|---|---| +| **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) | + +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. + +## 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 | 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` | + +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 + +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. + +### Flagship — the cross-game case (`flagship-*`) + +Three mods replicating the `2ship.o2r` / `mm.o2r` / `mod.o2r` stack from #1165, priority low → high: + +- `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) + +| 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 | + +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. + +### L1 — a real above the alias target (`L1-*`) + +- `L1-10-meta-target` (lower) — `meta → fancyShipDL` + `fancyShipDL` (magenta) +- `L1-20-real` (higher) — a real `gShipLogoDL` (green) + +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. + +### Reach-back — target below the `.meta` (`reachback-*`) + +- `reachback-10-target` (lower) — `fancyShipDL` (magenta) +- `reachback-20-meta` (higher) — `gShipLogoDL.meta → fancyShipDL` + +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) + +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 + +1. Build/run SoH as usual (executable at `build/soh/soh.elf`, with `oot.o2r` / `soh.o2r` / + `mods/` alongside it). +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 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), swapping + the `mods/` contents first. + +### Reading the Trace log +- Alias resolved to target: the target path (`…/fancyShipDL.xml`) is loaded. +- 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/*.o2r +``` + +## Layout +``` +meta-alias-testing/ + 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/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. diff --git a/meta-alias-testing/build-o2r.sh b/meta-alias-testing/build-o2r.sh new file mode 100755 index 00000000000..18720cb61a2 --- /dev/null +++ b/meta-alias-testing/build-o2r.sh @@ -0,0 +1,67 @@ +#!/usr/bin/env bash +# +# 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. +# +# 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)" +ASSETS="$ROOT/src/assets/textures/nintendo_rogo_static" + +command -v zip >/dev/null || { echo "error: 'zip' is required" >&2; exit 1; } + +# 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/layered" +TMP="$(mktemp -d)" +trap 'rm -rf "$TMP"' EXIT + +build_o2r() { # + local out="$1"; local mname="$2"; shift 2 + 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" +} + +# --- 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: NN-* prefix = intended priority (higher number = higher / loaded last) --- + +# 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[@]}" + +# 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/historical/BASELINE.md b/meta-alias-testing/historical/BASELINE.md new file mode 100644 index 00000000000..e37d458058d --- /dev/null +++ b/meta-alias-testing/historical/BASELINE.md @@ -0,0 +1,43 @@ +# 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. + +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. 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/historical/STATUS.md b/meta-alias-testing/historical/STATUS.md new file mode 100644 index 00000000000..1171501902d --- /dev/null +++ b/meta-alias-testing/historical/STATUS.md @@ -0,0 +1,52 @@ +# 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. + +Vehicle: the boot ship logo. green = real/default loaded · magenta = +replacement/target loaded · brown = original (mod not in effect) · nothing = +load failed. + +## Engine changes (applied in this order) + +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 | +index | +fallback | +reachability (final) | expected final | +|---|:---:|---|---|---|---|---| +| 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 +- **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`. diff --git a/meta-alias-testing/meta-loading.md b/meta-alias-testing/meta-loading.md new file mode 100644 index 00000000000..5d12bc0743b --- /dev/null +++ b/meta-alias-testing/meta-loading.md @@ -0,0 +1,65 @@ +# `.meta` alias loading flow + +How libultraship resolves a resource request when a `.meta` alias is involved, across one or more +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. + +## The rule + +`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: + +- **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`). + +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. + +"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. + +## Terms + +- **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. + +## Flow + +```mermaid +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)"] +``` + +## 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) | + +¹ 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 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. + +## Notes + +- 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. 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 00000000000..cb13c588005 Binary files /dev/null and b/meta-alias-testing/o2r/case1-real-meta-target.o2r differ 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 00000000000..d50dd24d1f8 Binary files /dev/null and b/meta-alias-testing/o2r/case2-real-meta-notarget.o2r differ 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 00000000000..41e90c77ab6 Binary files /dev/null and b/meta-alias-testing/o2r/case3-noreal-meta-target.o2r differ diff --git a/meta-alias-testing/o2r/case4-meta-only.o2r b/meta-alias-testing/o2r/case4-meta-only.o2r new file mode 100644 index 00000000000..32277d3fe50 Binary files /dev/null and b/meta-alias-testing/o2r/case4-meta-only.o2r differ diff --git a/meta-alias-testing/o2r/case5-real-nometa.o2r b/meta-alias-testing/o2r/case5-real-nometa.o2r new file mode 100644 index 00000000000..057639e8da3 Binary files /dev/null and b/meta-alias-testing/o2r/case5-real-nometa.o2r differ diff --git a/meta-alias-testing/o2r/layered/L1-10-meta-target.o2r b/meta-alias-testing/o2r/layered/L1-10-meta-target.o2r new file mode 100644 index 00000000000..664be502d46 Binary files /dev/null and b/meta-alias-testing/o2r/layered/L1-10-meta-target.o2r differ diff --git a/meta-alias-testing/o2r/layered/L1-20-real.o2r b/meta-alias-testing/o2r/layered/L1-20-real.o2r new file mode 100644 index 00000000000..45382a6191a Binary files /dev/null and b/meta-alias-testing/o2r/layered/L1-20-real.o2r differ 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 00000000000..355fd925b69 Binary files /dev/null and b/meta-alias-testing/o2r/layered/flagship-10-meta.o2r differ diff --git a/meta-alias-testing/o2r/layered/flagship-20-real.o2r b/meta-alias-testing/o2r/layered/flagship-20-real.o2r new file mode 100644 index 00000000000..43dda7853f5 Binary files /dev/null and b/meta-alias-testing/o2r/layered/flagship-20-real.o2r differ diff --git a/meta-alias-testing/o2r/layered/flagship-30-target.o2r b/meta-alias-testing/o2r/layered/flagship-30-target.o2r new file mode 100644 index 00000000000..c72199a07b2 Binary files /dev/null and b/meta-alias-testing/o2r/layered/flagship-30-target.o2r differ diff --git a/meta-alias-testing/o2r/layered/reachback-10-target.o2r b/meta-alias-testing/o2r/layered/reachback-10-target.o2r new file mode 100644 index 00000000000..5ff5e1d7929 Binary files /dev/null and b/meta-alias-testing/o2r/layered/reachback-10-target.o2r differ diff --git a/meta-alias-testing/o2r/layered/reachback-20-meta.o2r b/meta-alias-testing/o2r/layered/reachback-20-meta.o2r new file mode 100644 index 00000000000..afbdd51a49b Binary files /dev/null and b/meta-alias-testing/o2r/layered/reachback-20-meta.o2r differ diff --git a/meta-alias-testing/regen-assets.sh b/meta-alias-testing/regen-assets.sh new file mode 100755 index 00000000000..51c9baf0780 --- /dev/null +++ b/meta-alias-testing/regen-assets.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash +# +# Regenerates the test assets under src/assets/ from SoH's real ship-logo +# DisplayList (soh/assets/custom/textures/nintendo_rogo_static/gShipLogoDL). +# +# Produces two visually distinct variants so a tester can tell at a glance which +# asset the engine loaded for the boot ("LUS") logo: +# * default / real -> 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 @@ + + + + + + + + + + + + + + + + + + + +