84 lines
No EOL
5.8 KiB
Markdown
84 lines
No EOL
5.8 KiB
Markdown
# PSX Art-Binding Recovery
|
|
|
|
## Scope
|
|
|
|
- Active target: retail PlayStation `SLUS_002.68` feeding the renderer-local cache pipeline in `Crusader-Map-Viewer/map_renderer`.
|
|
- Goal of this pass: stop treating the current unreadable PSX output as a renderer-only problem and measure whether placeholders are mainly caused by extraction-time art binding failures.
|
|
- This note records the first pragmatic recovery pass that replaces large placeholder bands with real bundle-backed art while keeping the result auditable in exported scene metadata.
|
|
|
|
## Root Cause Summary
|
|
|
|
- The current unreadable output was primarily an extraction-side problem, not a front-end draw bug.
|
|
- Before this pass, the built `.cache/scene-cache/psx-remorse` set carried `58,262` fallback placeholders versus only `1,714` bundle-mapped items.
|
|
- The fallback concentration was overwhelmingly in `section0_constructor_placements` (`94.5%` of fallback items), not in the smaller `section0_dispatch_roots` family.
|
|
- The hottest unresolved types were `0x0042`, `0x0041`, `0x0047`, `0x0045`, `0x003f`, `0x004b`, `0x0046`, `0x0044`, `0x0048`, and `0x004a`.
|
|
- Those types already appeared in the exported `DAT_800758d8` art-template layer in many maps, but almost all of their rows were `blockSize = 0` and therefore had no direct payload dwords to match against bundle offsets.
|
|
- The neighboring state banks were still populated: `DAT_800758cc` carried valid script tables for the same types, while `DAT_800758d0` stayed empty for this family and `DAT_800758d4` remained on the runtime-bounds side.
|
|
- That combination strongly suggests inherited or aliased presentation for these constructor-placement families rather than a literal absence of art.
|
|
|
|
## Implemented Recovery Rule
|
|
|
|
The cache builder now resolves PSX art in three stages instead of dropping directly from "no direct `DAT_800758d8` payload" to a placeholder:
|
|
|
|
1. Use the direct non-zero `DAT_800758d8` row when a type has a real local template payload and bundle match.
|
|
2. If the type lives in the unresolved zero-block constructor-placement family, look for a same-map donor type whose `DAT_800758cc` script blob is identical and whose `DAT_800758d8` row resolves to a real bundle.
|
|
3. If no exact script-signature donor exists, fall back to the nearest resolved same-map donor inside the current constructor-placement family band (`0x003e..0x0064`).
|
|
|
|
The exporter keeps this explicit instead of pretending the rule is fully solved:
|
|
|
|
- `mappingSource` now records whether the art came from a direct template, a `cc-signature-donor:xxxx`, or a `generic-family-donor:xxxx` path.
|
|
- `templateTypeId` and `donorTypeId` are preserved in exported `mapSource` rows.
|
|
- The unresolved type still uses its own `DAT_800758cc` selector-to-frame map; only the bundle source is borrowed.
|
|
|
|
## Measured Effect
|
|
|
|
Two measured rebuilds mattered in this pass.
|
|
|
|
### First donor pass
|
|
|
|
- Added donor reuse for the original generic family band.
|
|
- Totals moved from `58,262` fallback / `1,714` bundle-mapped to `37,054` fallback / `22,922` bundle-mapped.
|
|
|
|
Representative maps:
|
|
|
|
- `map 0`: `1078 / 111` -> `340 / 849`
|
|
- `map 9`: `664 / 111` -> `197 / 578`
|
|
- `map 43`: `707 / 97` -> `196 / 608`
|
|
- `map 64`: `1194 / 161` -> `736 / 619`
|
|
- `map 104`: `909 / 93` -> `894 / 108`
|
|
|
|
### Extended donor band
|
|
|
|
- Extended the same heuristic through the next zero-block constructor-placement band up to `0x0064`.
|
|
- Final measured totals after rebuild: `25,038` fallback / `34,938` bundle-mapped.
|
|
|
|
Representative maps after the extended pass:
|
|
|
|
- `map 0`: `66` fallback / `1123` bundle-mapped
|
|
- `map 9`: `42` fallback / `733` bundle-mapped
|
|
- `map 43`: `9` fallback / `795` bundle-mapped
|
|
- `map 64`: `160` fallback / `1195` bundle-mapped
|
|
- `map 104`: `866` fallback / `136` bundle-mapped
|
|
|
|
So the first practical conclusion is straightforward: the exporter was the main blocker for most maps, and a constrained donor heuristic can replace a large fraction of placeholders with real graphics without touching the viewer runtime.
|
|
|
|
## Remaining Unresolved Mass
|
|
|
|
- The cache is still not fully closed. `25,038` fallback items remain across `62` maps.
|
|
- The remaining fallback distribution is still concentrated in `section0_constructor_placements`.
|
|
- Current top fallback-heavy types after the donor pass are `0x0042`, `0x005a`, `0x005b`, `0x005c`, `0x0047`, `0x0059`, `0x005d`, `0x005e`, `0x0062`, and `0x0063`.
|
|
- `0x0042` remains the single largest unresolved type even after the heuristic pass.
|
|
- `map 104` is the most obvious current outlier: it still sits at `866` fallback items versus only `136` bundle-mapped items, so the next recovery pass should treat it as the best stress case rather than relying only on the now-mostly-readable early maps.
|
|
|
|
## Practical Interpretation
|
|
|
|
- The donor heuristic is good enough to prove that many placeholders were caused by missing extraction-time inheritance logic.
|
|
- It is not strong enough to count as the final executable-backed rule for the unresolved families.
|
|
- The remaining gap still sits where the earlier Ghidra work already pointed: somewhere between the constructor-side art/resource creation lane and the live post-spawn state/resource/frame reselection path.
|
|
|
|
## Next Steps
|
|
|
|
1. Trace the remaining high-volume band `0x0055..0x0063` in Ghidra with the same question used for `0x0042`: why does `DAT_800758d8` stay zero-sized while visible art still exists at runtime?
|
|
2. Use `map 104` as the primary regression target and dump its remaining fallback type/state/lane distribution before doing any broader heuristic expansion.
|
|
3. Compare unresolved zero-block types against nearby resolved donor types at the constructor/resource level, not only at the script-signature level, so borrowed bundles can be replaced with an executable-backed alias rule.
|
|
4. Keep the `DAT_800758d4` work on the bounds side unless a family-specific caller proves otherwise; this pass did not reopen that conclusion. |