Decomp updates
This commit is contained in:
parent
f6a5155675
commit
c4fa8a6b05
62 changed files with 9413 additions and 20 deletions
240
docs/psx/prealpha.md
Normal file
240
docs/psx/prealpha.md
Normal file
|
|
@ -0,0 +1,240 @@
|
|||
# Crusader 2 Pre-Pre Alpha (PlayStation) Recon
|
||||
|
||||
## Scope
|
||||
|
||||
- Target disc tree: `E:\emu\psx\Crusader 2 Pre-Pre Alpha`
|
||||
- Active Ghidra program for this pass: `/psx/prealpha/SLUS_002.68`
|
||||
- Comparison baseline: retail PlayStation `Crusader: No Remorse` findings in `docs/psx/psx.md`
|
||||
- Goal of this pass: identify concrete differences from the released PSX build and record any interesting early-build leftovers visible in the executable and disc layout.
|
||||
|
||||
## Immediate Conclusions
|
||||
|
||||
- This pre-alpha does **not** currently look like a fully distinct `Crusader 2` executable identity.
|
||||
- The executable still carries direct `Crusader: No Remorse` branding and other No Remorse-facing UI text.
|
||||
- The level-loader code is already very close to the retail PSX loader: it still embeds all seven `\LSETn\L` prefixes and the same threshold split at map indices `10/20/30/40/50/60`.
|
||||
- The disc content is dramatically reduced compared with retail: only `3` shipped level bundles, `1` XA file, no shipped movie `.STR` files, and no `LICENSEA.DAT` or `ZZZ.ZZZ` root files.
|
||||
- The executable still retains content/system references that do not match the current unpacked pre-alpha disc, including `\AUDIO\TALK1.XA;1` and a `LoadExec` helper for `MENU.EXE`, `ENGINE.EXE`, and `PSX.EXE`.
|
||||
- Current safest read: this build looks more like an earlier trimmed branch of the No Remorse PSX code/content pipeline than a clearly rebranded or content-rich standalone `Crusader 2` PSX build.
|
||||
|
||||
## Disc Tree Differences From Retail PSX
|
||||
|
||||
Top-level inventory contrast from the unpacked trees:
|
||||
|
||||
| Area | Retail `Crusader: No Remorse` | Pre-pre alpha | Current read |
|
||||
|---|---:|---:|---|
|
||||
| `SLUS_002.68` size | `675,840` bytes | `667,648` bytes | pre-alpha executable is smaller by `8,192` bytes |
|
||||
| `.WDL` files | `66` | `7` | heavy content reduction |
|
||||
| `.STR` files | `33` | `0` | no shipped movie streams in the pre-alpha tree |
|
||||
| `.XA` files | `2` | `1` | only `MULTI8.XA` remains |
|
||||
| `LSET` folders | `LSET1` through `LSET7` | `LSET1` only | only `L0.WDL`, `L1.WDL`, `L2.WDL` shipped |
|
||||
| `MOVIES/` contents | populated | empty | folder exists but no `.STR` payloads |
|
||||
| root extras | `LICENSEA.DAT`, `ZZZ.ZZZ` present | both absent | pre-alpha root is much leaner |
|
||||
|
||||
Pre-alpha top-level tree at this point:
|
||||
|
||||
- `AUDIO/MULTI8.XA`
|
||||
- `FMV.BIN`
|
||||
- `LEGAL.SCR`
|
||||
- `LSET1/L0.WDL`
|
||||
- `LSET1/L1.WDL`
|
||||
- `LSET1/L2.WDL`
|
||||
- `MENUS/M13.WDL`
|
||||
- `MENUS/M21.WDL`
|
||||
- `MENUS/M5.WDL`
|
||||
- `SLUS_002.68`
|
||||
- `SPEC_A.WDL`
|
||||
- `SYSTEM.CNF`
|
||||
|
||||
Important contrast with retail:
|
||||
|
||||
- retail ships `62` level bundles across `LSET1..LSET7`
|
||||
- this pre-alpha tree currently ships only `3` level bundles in `LSET1`
|
||||
- retail ships `33` movie streams; this pre-alpha has an empty `MOVIES/` folder
|
||||
- retail ships `MULTI8.XA` and `TALK1.XA`; this pre-alpha tree currently exposes only `MULTI8.XA`
|
||||
|
||||
## Executable Findings
|
||||
|
||||
### 1. Executable identity still says `No Remorse`
|
||||
|
||||
The strongest branding strings in `/psx/prealpha/SLUS_002.68` still point at the first game, not a visible `Crusader 2` identity.
|
||||
|
||||
Relevant strings:
|
||||
|
||||
- `80061d08`: `Crusader: No Remorse (save# `
|
||||
- `80010944`: `This is no time to show Remorse! ... Are you sure you want to Quit?`
|
||||
|
||||
Negative checks from this pass:
|
||||
|
||||
- no recovered `Crusader II` string
|
||||
- no recovered `Regret` string
|
||||
- no recovered alternate save-title branding
|
||||
|
||||
Current safest read:
|
||||
|
||||
- at least at the executable/UI-string layer, this pre-alpha still looks branded as a No Remorse derivative rather than a separately named sequel build
|
||||
|
||||
### 2. The level loader is already essentially retail-shaped
|
||||
|
||||
Function `80038084` was renamed in Ghidra during this pass to `wdl_resource_bundle_load_by_index`.
|
||||
|
||||
Why the rename is justified:
|
||||
|
||||
- the function loads one of seven hardcoded level-path prefixes based on map-index thresholds
|
||||
- it appends the decimal map index and the shared extension tail to form the final level path
|
||||
- it also loads `\SPEC_A.WDL` during the same setup path
|
||||
|
||||
Direct evidence:
|
||||
|
||||
- `80010de0..80010e30` contains the contiguous prefix table:
|
||||
- `\LSET1\L`
|
||||
- `\LSET2\L`
|
||||
- `\LSET3\L`
|
||||
- `\LSET4\L`
|
||||
- `\LSET5\L`
|
||||
- `\LSET6\L`
|
||||
- `\LSET7\L`
|
||||
- `80010e34` contains `\SPEC_A.WDL`
|
||||
- the decompiled threshold ladder in `wdl_resource_bundle_load_by_index` still splits on `9`, `0x13`, `0x1d`, `0x27`, `0x31`, and `0x3b`, which is the same practical `10/20/30/40/50/60` bucket logic previously closed in the retail PSX note
|
||||
|
||||
Important contrast with the disc tree:
|
||||
|
||||
- the code still expects the full seven-folder retail-style layout
|
||||
- the current pre-alpha disc only ships `LSET1/L0..L2`
|
||||
|
||||
This is one of the strongest current signs that the executable pipeline was still aligned with a much larger planned content set than the media actually present in this build.
|
||||
|
||||
### 3. Mission and passcode scaffolding are still close to retail No Remorse
|
||||
|
||||
Recovered strings show that the pre-alpha executable still preserves the same visible mission/passcode UI scaffolding seen in the retail PSX work.
|
||||
|
||||
Relevant strings:
|
||||
|
||||
- `800101a4..80010340`: `Mission Briefing ^Mission 1` through `Mission Briefing ^Mission 15`
|
||||
- `80060b70`: `Congratulations! You have completed your mission. The passcode for the next mission is:`
|
||||
- `80060bd4`: the alternate completion message without the visible passcode line
|
||||
- `80060b50`: `BCDFGHJKLMNPQRSTVWXZ0123456789`
|
||||
|
||||
Current read:
|
||||
|
||||
- the same `15` mission-facing text slots are still present
|
||||
- the same consonant/digit passcode alphabet is still present
|
||||
- this does **not** look like a radically different mission-shell rewrite
|
||||
|
||||
This pushes the current pre-alpha closer to `early/reduced No Remorse PSX branch` than to `already content-diverged sequel shell`.
|
||||
|
||||
### 4. Audio and movie state looks incomplete relative to both retail code and the current disc
|
||||
|
||||
The pre-alpha tree contains only one XA file:
|
||||
|
||||
- `AUDIO/MULTI8.XA`
|
||||
|
||||
But function `80045648` still contains the two-path XA switch logic:
|
||||
|
||||
- param `0` selects `\AUDIO\MULTI8.XA;1`
|
||||
- nonzero selects `\AUDIO\TALK1.XA;1`
|
||||
|
||||
That function was annotated in Ghidra during this pass because the current unpacked disc does **not** contain `TALK1.XA`.
|
||||
|
||||
Interesting consequence:
|
||||
|
||||
- the executable still expects a second XA stream family that is absent from the current pre-alpha disc tree
|
||||
|
||||
Movie side status is even more reduced:
|
||||
|
||||
- `MOVIES/` exists but is empty
|
||||
- no `.STR` files are present in the pre-alpha tree
|
||||
- the main executable did **not** yield `FMV` or `MDEC` strings in this pass
|
||||
- `FMV.BIN` still exists on disk, but the current main executable looks less movie-facing than retail at the string level
|
||||
|
||||
Current safest read:
|
||||
|
||||
- the audio/movie pipeline was not fully stripped from code expectations, but the shipped pre-alpha disc content is notably incomplete compared with retail
|
||||
|
||||
### 5. A split-executable `LoadExec` path still exists
|
||||
|
||||
Function `80046aac` was not renamed yet, but it was comment-annotated in Ghidra during this pass.
|
||||
|
||||
That helper selects one of three executable paths:
|
||||
|
||||
- `cdrom:\ENGINE.EXE;1`
|
||||
- `cdrom:\MENU.EXE;1`
|
||||
- `cdrom:\PSX.EXE;1`
|
||||
|
||||
Then it performs a PSX `LoadExec(...)` handoff after shutting down active systems.
|
||||
|
||||
Important contrast with the current pre-alpha disc tree:
|
||||
|
||||
- none of `ENGINE.EXE`, `MENU.EXE`, or `PSX.EXE` exist in the unpacked root
|
||||
- only `SLUS_002.68` is present as the obvious boot executable
|
||||
|
||||
Current safest read:
|
||||
|
||||
- this build still preserves a real split-executable chainload helper from an earlier architecture or earlier content-layout plan
|
||||
- the current unpacked disc no longer matches that design literally
|
||||
|
||||
I did **not** yet promote a stronger functional name for `80046aac` because the surviving call sites were not fully classified in this pass.
|
||||
|
||||
### 6. Content-name tables remain close to retail rather than showing obvious sequel-only replacements
|
||||
|
||||
The nearby item/ammo/gun tables are broadly familiar from the retail PSX note.
|
||||
|
||||
Recovered examples:
|
||||
|
||||
- ammo table still includes `JL-2 AMMO`, `AR-7 AMMO`, `GL-303 AMMO`, `RP-22 AMMO`, `SG-A1 AMMO`
|
||||
- item table still includes `INHIBITOR`, `CREDITS`, `SCI PLANS`, `BLAST PAC`, `DET PAC`, `DATA LINK`, `LAND MINE`, `SPIDER BOMB`, `MEDICAL KIT`, `ENERGY CUBE`, `FUSION PAC`, `CHEMICAL BATTERY`, `FISSION BATTERY`, `FUSION BATTERY`, `GRAVITON GENERATOR`, `IONIC GENERATOR`, `PLASMA GENERATOR`
|
||||
- gun table still includes `RP-16`, `RP-22`, `RP-32`, `SG-A1`, `AC-88`, `PA-31`, `EM-4`, `PL-1`, `UV-9`, `GL-303`, `AR-7`, `JL-2`, `JL-9`
|
||||
|
||||
Current read:
|
||||
|
||||
- the visible catalog tables are still basically in the retail No Remorse family
|
||||
- this pass did **not** surface any obviously new `Crusader 2`-specific naming layer in the executable text
|
||||
|
||||
### 7. Developer-facing PSYQ graphics/debug strings are still present
|
||||
|
||||
This is not necessarily unique to the pre-alpha, but it is worth preserving because it may help later graphics-focused passes.
|
||||
|
||||
Recovered strings include:
|
||||
|
||||
- `ResetGraph(%d)...`
|
||||
- `SetGraphReverse(%d)...`
|
||||
- `SetGraphDebug:level:%d,type:%d reverse:%d`
|
||||
- `LoadImage`
|
||||
- `StoreImage`
|
||||
- `MoveImage`
|
||||
|
||||
The recovered function `SetGraphDebug` simply stores the requested debug level and prints the diagnostic line when nonzero.
|
||||
|
||||
Current practical value:
|
||||
|
||||
- if later PSX rendering work needs debug/graphics control anchors, these strings and helpers are already easy to relocate in the pre-alpha database
|
||||
|
||||
## Interesting Build-Level Implications
|
||||
|
||||
The combined disc-plus-executable picture currently supports a fairly specific interpretation.
|
||||
|
||||
Most likely current model:
|
||||
|
||||
1. the pre-alpha executable is still largely a No Remorse-derived PSX codebase
|
||||
2. the content payload is heavily reduced compared with retail
|
||||
3. some earlier architectural leftovers survived into this build, especially the split-`LoadExec` helper and the missing-file `TALK1.XA` path
|
||||
4. the loader still expects a much larger planned level inventory than the current disc actually ships
|
||||
|
||||
That does **not** yet prove there is no meaningful `Crusader 2` gameplay/content divergence hidden deeper in the WDL data or untyped runtime tables. It does mean the first clean static answer is: this build is still much closer to `unfinished No Remorse PSX branch with reduced assets` than to an already distinctively rebranded sequel executable.
|
||||
|
||||
## Ghidra Updates From This Pass
|
||||
|
||||
Applied in the live `/psx/prealpha/SLUS_002.68` database:
|
||||
|
||||
- renamed `80038084` to `wdl_resource_bundle_load_by_index`
|
||||
- added a disassembly comment at `80046aac` documenting the `LoadExec` targets `MENU.EXE`, `ENGINE.EXE`, and `PSX.EXE`
|
||||
- added a disassembly comment at `80045648` documenting the `MULTI8.XA` vs `TALK1.XA` selection and the current missing-file mismatch
|
||||
|
||||
## Highest-Value Next Steps
|
||||
|
||||
1. Decompile and classify the callers around `80046aac` so the surviving `MENU.EXE` / `ENGINE.EXE` / `PSX.EXE` path can be labeled as boot flow, frontend transition, or dead leftover with less ambiguity.
|
||||
2. Compare the pre-alpha `L0.WDL`, `L1.WDL`, and `L2.WDL` structure against the retail `LSET` extractor results to see whether they are just reduced No Remorse maps or genuinely divergent content/layout revisions.
|
||||
3. Diff the pre-alpha `SPEC_A.WDL` and `MENUS/*.WDL` files against the retail PSX note to see whether the menu/front-end assets changed more than the executable strings did.
|
||||
4. Trace the mission-briefing and passcode consumers directly in `/psx/prealpha/SLUS_002.68` to see whether the pre-alpha still uses the same mission ordering and password-generation rules as retail.
|
||||
5. Search the pre-alpha executable for early script/event-dispatch tables or type/resource tables that differ from the retail PSX resource-stream model, especially around the WDL loader outputs.
|
||||
6. Inspect `FMV.BIN` separately in Ghidra or as raw data to determine whether it still contains movie-system code/data even though the current disc ships no `.STR` files.
|
||||
7. Run a first asset-focused comparison pass on the three shipped pre-alpha maps in the public renderer/export tooling once the bundle/resource bindings are stable enough to avoid the earlier invalid direct-bundle hypothesis.
|
||||
806
docs/psx/psx.md
Normal file
806
docs/psx/psx.md
Normal file
|
|
@ -0,0 +1,806 @@
|
|||
# Crusader: No Remorse (PlayStation) Recon
|
||||
|
||||
## Scope
|
||||
|
||||
- Target disc tree: `E:\emu\psx\Crusader - No Remorse`
|
||||
- Goal of this pass: identify the boot executable, separate likely code from content, and find the most practical first extraction routes for PS1 assets.
|
||||
|
||||
## Immediate Conclusions
|
||||
|
||||
- `SYSTEM.CNF` is the disc boot file and points directly at `cdrom:\SLUS_002.68;1`.
|
||||
- `SLUS_002.68` is the main game executable. It begins with a valid `PS-X EXE` header.
|
||||
- No other top-level file currently looks like a second normal PS1 executable.
|
||||
- Disc content is dominated by standard PS1 media (`.STR`, `.XA`) plus a large number of game-specific `.WDL` blobs.
|
||||
- `FMV.BIN` looks like movie-playback support data or a resource blob, not a bootable executable.
|
||||
- `ZZZ.ZZZ` looks much more like media/container data than code, and its size exactly matches `MOVIES/FMV3.STR`.
|
||||
|
||||
## Disc Boot Evidence
|
||||
|
||||
`SYSTEM.CNF` contents:
|
||||
|
||||
```ini
|
||||
BOOT = cdrom:\SLUS_002.68;1
|
||||
TCB = 4
|
||||
EVENT = 10
|
||||
STACK = 801FFFFC
|
||||
```
|
||||
|
||||
`SLUS_002.68` header evidence:
|
||||
|
||||
- Magic: `PS-X EXE`
|
||||
- Initial PC: `0x8004BD90`
|
||||
- Load address: `0x80010000`
|
||||
- Image size: `0x000A4800` (`675,840` bytes)
|
||||
|
||||
This is the clearest Ghidra import candidate for code analysis.
|
||||
|
||||
## Top-Level File Classification
|
||||
|
||||
Top-level items:
|
||||
|
||||
- `SLUS_002.68`
|
||||
- `SYSTEM.CNF`
|
||||
- `FMV.BIN`
|
||||
- `ZZZ.ZZZ`
|
||||
- `SPEC_A.WDL`
|
||||
- `LEGAL.SCR`
|
||||
- `LICENSEA.DAT`
|
||||
- `AUDIO/`
|
||||
- `MOVIES/`
|
||||
- `MENUS/`
|
||||
- `LSET1/` through `LSET7/`
|
||||
|
||||
Recursive extension summary from the extracted disc tree:
|
||||
|
||||
| Extension | Count | Total bytes | Current classification |
|
||||
|---|---:|---:|---|
|
||||
| `.STR` | 33 | 415,027,744 | PS1 movie streams |
|
||||
| `.XA` | 2 | 91,897,856 | PS1 XA audio |
|
||||
| `.WDL` | 66 | 76,198,860 | custom game asset/level blobs |
|
||||
| `.ZZZ` | 1 | 26,148,984 | likely media/container data |
|
||||
| `.68` | 1 | 675,840 | main PS1 executable |
|
||||
| `.SCR` | 1 | 153,600 | data asset |
|
||||
| `.BIN` | 1 | 90,812 | support/resource blob |
|
||||
| `.DAT` | 1 | 28,032 | data asset |
|
||||
| `.CNF` | 1 | 70 | boot config |
|
||||
|
||||
## File Family Findings
|
||||
|
||||
### 1. `SLUS_002.68`
|
||||
|
||||
- This is the boot target from `SYSTEM.CNF`.
|
||||
- It has a normal PS1 executable header.
|
||||
- It should be the first import into Ghidra.
|
||||
- Current working assumption: this is the only primary native code binary on the disc.
|
||||
|
||||
### 2. `AUDIO/*.XA`
|
||||
|
||||
- Files found:
|
||||
- `AUDIO/MULTI8.XA`
|
||||
- `AUDIO/TALK1.XA`
|
||||
- These are standard PS1 XA audio files.
|
||||
- `MULTI8.XA` is divisible by both `2352` and `2048`, which is consistent with sector-oriented media data.
|
||||
- `TALK1.XA` is divisible by `2048` but not exactly by `2352`.
|
||||
- Most practical extraction route: use standard PS1/XA tooling rather than custom RE first.
|
||||
|
||||
### 3. `MOVIES/*.STR`
|
||||
|
||||
- Files found: `FMV0.STR` through `FMV32.STR`.
|
||||
- These are the strongest candidates for standard PS1 video streams.
|
||||
- The raw headers look consistent with sectorized PS1 stream data rather than executable code.
|
||||
- Most practical extraction route: treat them as PS1 STR video and run them through PS1 media tooling first.
|
||||
|
||||
### 4. `FMV.BIN`
|
||||
|
||||
This file does not look like a normal executable.
|
||||
|
||||
First bytes begin with:
|
||||
|
||||
```text
|
||||
\MOVIES\FMV%d.STR
|
||||
MDEC_rest:bad option(%d)
|
||||
MDEC_in_sync
|
||||
MDEC_out_sync
|
||||
DMA=(%d,%d), ADDR=(0x%08x->0x%08x)
|
||||
FIFO=(%d,%d),BUSY=%d,DREQ=(%d,%d),RGB24=%d,STP=%d
|
||||
```
|
||||
|
||||
Current best read:
|
||||
|
||||
- `FMV.BIN` is movie-related support data, code tables, or debug/resource text for the MDEC/FMVs.
|
||||
- It clearly references the external movie path pattern `\MOVIES\FMV%d.STR`.
|
||||
- It is worth a secondary Ghidra import only if the goal is to understand the movie subsystem specifically.
|
||||
- It is not the disc boot executable.
|
||||
|
||||
### 5. `ZZZ.ZZZ`
|
||||
|
||||
Key findings:
|
||||
|
||||
- Size: `26,148,984` bytes
|
||||
- That size exactly matches `MOVIES/FMV3.STR`.
|
||||
- The file begins with stream-like binary data rather than an executable header.
|
||||
- It also yielded movie-adjacent string evidence such as `MDEC`.
|
||||
|
||||
Current best read:
|
||||
|
||||
- `ZZZ.ZZZ` is probably not code.
|
||||
- It is a strong candidate for either:
|
||||
- a renamed movie stream, or
|
||||
- a duplicate/alternate copy of `FMV3.STR`
|
||||
|
||||
Most practical next check:
|
||||
|
||||
1. compare a few sectors or hashes against `MOVIES/FMV3.STR`
|
||||
2. try opening `ZZZ.ZZZ` directly in PS1 STR-capable tooling
|
||||
|
||||
### 6. `LSET*/L*.WDL`
|
||||
|
||||
These are the most important unknown asset family for content extraction.
|
||||
|
||||
Representative level sample: `LSET1/L0.WDL`
|
||||
|
||||
- Size: `1,312,624` bytes
|
||||
- Header starts with structured values, not raw pixels:
|
||||
|
||||
```text
|
||||
0x00000034 0x00006FDC 0x0000376C 0x00001000
|
||||
0x00000160 0x00000498 0x0000025C 0x00000FE0
|
||||
0x00000070 0x00072EC4 0x00034B6C 0x00007448
|
||||
0x0007407C 0x00010824 0x00000002 0x00000000
|
||||
```
|
||||
|
||||
- This does not behave like a flat framebuffer dump.
|
||||
- It looks more like a custom structured level/container blob with internal offsets, lengths, or section pointers.
|
||||
- Additional offset targets inside the file, such as `0x376C`, `0x6FDC`, and `0x10824`, land on repeating structured records rather than code.
|
||||
|
||||
Strict TIM-style scan results for `L0.WDL` found plausible embedded PS1 image headers at:
|
||||
|
||||
- `0xE7A84`
|
||||
- `0x117DEC`
|
||||
- `0x12CECC`
|
||||
- `0x135F18`
|
||||
- `0x1369F4`
|
||||
- `0x136B38`
|
||||
- `0x136C40`
|
||||
|
||||
Current best read:
|
||||
|
||||
- `LSET*.WDL` likely holds mixed level resources.
|
||||
- At least some of those resources may include standard embedded PS1 TIM-like image blocks.
|
||||
- These files are the strongest current target for a custom extractor.
|
||||
|
||||
Executable-guided extraction status:
|
||||
|
||||
- `lset_level_bundle_load` in the imported PSX executable now confirms the executable builds `\LSETn\Lx.WDL` paths directly and treats those files as the live level-bundle format.
|
||||
- The same loader reads a small level header blob first, then a large SPU/audio blob, then dispatches the remaining level resource stream through `level_resource_stream_load`.
|
||||
- `image_resource_bind_vram_slot` and `image_bundle_load_to_vram` show that resource types `4` and `5` are image/sprite-oriented resources: they resolve VRAM placement and upload image data through `LoadImage`.
|
||||
- `sprite_rle_decode_rows` is now confirmed as the row-based decompressor used when a type-5 frame record has its compressed bit set.
|
||||
- Current consequence: sprite extraction now has a real executable-backed path, while map extraction has a reliable raw-carving path even though the full tile/object semantics are not decoded yet.
|
||||
|
||||
### 7. `MENUS/*.WDL` and `SPEC_A.WDL`
|
||||
|
||||
Representative menu sample: `MENUS/M13.WDL`
|
||||
|
||||
- Size: `1,475,928` bytes
|
||||
- Starts with dense repeating values like:
|
||||
|
||||
```text
|
||||
0x9D499D29 0x9D299D29 0x9D4A9D4A 0xA14A9D4A
|
||||
0x9D49A14A 0x9D299D29 0x9D4A9D4A 0xA14A9D4A
|
||||
```
|
||||
|
||||
Representative special sample: `SPEC_A.WDL`
|
||||
|
||||
- Size: `545,424` bytes
|
||||
- Starts with the same raw-looking pattern as `M13.WDL`.
|
||||
|
||||
Current best read:
|
||||
|
||||
- These do not begin with obvious pointer tables.
|
||||
- They look more like raw or lightly wrapped image/screen asset data than like level containers.
|
||||
- `M13.WDL` had no strict TIM hits in the quick scan.
|
||||
- `SPEC_A.WDL` did have a few plausible stricter TIM-style hits at:
|
||||
- `0x449A8`
|
||||
- `0x80ED8`
|
||||
|
||||
So the `.WDL` family probably is not one single uniform format. Current evidence supports at least two subfamilies:
|
||||
|
||||
- structured level/resource blobs (`LSET*/*.WDL`)
|
||||
- raw-looking menu/special screen blobs (`MENUS/*.WDL`, `SPEC_A.WDL`)
|
||||
|
||||
Executable-guided extraction status:
|
||||
|
||||
- `SPEC_A.WDL` does not behave like the `LSET*.WDL` container family.
|
||||
- The executable-backed extractor work currently treats it as a raw blob with embedded image candidates rather than as a contiguous section table.
|
||||
- A validated carve on `SPEC_A.WDL` currently finds strict TIM-style hits at `0x1B5CC` and `0x80ED8`.
|
||||
|
||||
## Executable-Backed Extraction Model
|
||||
|
||||
These findings are now grounded in both file inspection and the imported `SLUS_002.68` executable.
|
||||
|
||||
### Level bundles: `LSET*/*.WDL`
|
||||
|
||||
Validated current extraction model for `LSET1/L0.WDL`:
|
||||
|
||||
- top-level header size: `0x34`
|
||||
- immediately following blob: `0x6FDC` bytes
|
||||
- post-audio resource area starts at: `0x7010`
|
||||
- high-confidence internal boundaries recovered from the header and validated with the extractor:
|
||||
- `0x7448`
|
||||
- `0x34B6C`
|
||||
- `0x72EC4`
|
||||
- `0x7407C`
|
||||
|
||||
Current carved regions from `L0.WDL`:
|
||||
|
||||
| Region | Offset | Size | Current interpretation |
|
||||
|---|---:|---:|---|
|
||||
| `audio_or_spu_blob` | `0x34` | `0x6FDC` | SPU/sequence data loaded by the audio init path |
|
||||
| `post_audio_region_00` | `0x7010` | `0x438` | small table/directory block |
|
||||
| `post_audio_region_01` | `0x7448` | `0x2D724` | strong map/placement candidate |
|
||||
| `post_audio_region_02` | `0x34B6C` | `0x3E358` | strong map/placement candidate |
|
||||
| `post_audio_region_03` | `0x72EC4` | `0x11B8` | small control/index block |
|
||||
| `post_audio_region_04` | `0x7407C` | `0xCC6F4` | strongest current sprite/graphics bank candidate |
|
||||
|
||||
Important consequence:
|
||||
|
||||
- for map work, the best current extraction targets are `post_audio_region_01` and `post_audio_region_02`
|
||||
- for sprite/graphics work, the best current extraction target is `post_audio_region_04`
|
||||
|
||||
Important correction from the next executable pass:
|
||||
|
||||
- a previously suspected text-like block in the broader PSX resource system is now confirmed separately in executable analysis as a menu/prompt text resource, not map data
|
||||
- a heavily used level-side table is also now confirmed as a per-type flag/behavior table used by collision/order logic, not a raw map grid
|
||||
- so the late-level extraction focus stays on `LSET*.WDL` post-audio regions, not on every large runtime table seen in the executable
|
||||
|
||||
The validated strict TIM carver currently finds one confirmed embedded TIM block in `L0.WDL` at:
|
||||
|
||||
- `0xBBA54`
|
||||
|
||||
That hit lands inside `post_audio_region_04`, which supports treating the late large region as the current best graphics bank candidate.
|
||||
|
||||
The same structure now reproduces on `LSET1/L1.WDL` too:
|
||||
|
||||
- header size: `0x34`
|
||||
- audio blob: `0x3244`
|
||||
- post-audio start: `0x3278`
|
||||
- high-confidence boundaries: `0x6F48`, `0x334D8`, `0x602C4`, `0x732D4`
|
||||
- late graphics candidate region: `0x732D4 .. EOF`
|
||||
- current strict TIM hit: `0xB4DC8`
|
||||
|
||||
So the current working model is no longer based on just one level file: `LSET` bundles appear to share a stable pattern of:
|
||||
|
||||
1. fixed `0x34` header
|
||||
2. SPU/audio blob
|
||||
3. several map/meta candidate regions
|
||||
4. one large late graphics-oriented region
|
||||
|
||||
What is still not stable yet:
|
||||
|
||||
- the internal semantics of `post_audio_region_01` and `post_audio_region_02` are still unresolved
|
||||
- `L0.WDL` starts with rows that look structured when viewed as `u16x6`, but `L1.WDL` does not preserve the same obvious interpretation at the same region boundary
|
||||
- current safest reading is that these are still raw candidate map/meta payloads, not yet a decoded placement format
|
||||
|
||||
### Menu / special blobs: `SPEC_A.WDL`, `MENUS/*.WDL`
|
||||
|
||||
These currently behave like raw image-oriented blobs, not like the structured `LSET` family.
|
||||
|
||||
Validated current extraction model for `SPEC_A.WDL`:
|
||||
|
||||
- whole-file raw blob fallback works cleanly
|
||||
- strict TIM hits currently validate at:
|
||||
- `0x1B5CC`
|
||||
- `0x80ED8`
|
||||
|
||||
Representative secondary check on `MENUS/M13.WDL`:
|
||||
|
||||
- whole-file raw blob fallback also works cleanly there
|
||||
- current strict TIM hit validates at:
|
||||
- `0x493EC`
|
||||
|
||||
This keeps menus/special screens as a secondary image-carving problem instead of a map/container problem.
|
||||
|
||||
## Working Extractor
|
||||
|
||||
Current extractor script:
|
||||
|
||||
- `tools/psx_extract_wdl.py`
|
||||
|
||||
What it does right now:
|
||||
|
||||
- recognizes the validated `LSET*.WDL` top-level layout
|
||||
- carves the audio blob and header-directed post-audio regions
|
||||
- scans the whole file for strict TIM blocks and extracts them
|
||||
- falls back to raw-blob carving for `SPEC_A.WDL` / menu-like files
|
||||
- emits an exploratory `u16x6` CSV view for the first post-audio LSET candidate region so raw row patterns can be inspected without claiming final semantics
|
||||
- scans the large late LSET graphics region for type-5 sprite bundle headers
|
||||
- decodes row-RLE compressed sprite frames and writes raw frame payloads plus grayscale preview images
|
||||
- writes carved output under `out/psx_wdl/<stem>/`
|
||||
|
||||
Current practical usage:
|
||||
|
||||
```powershell
|
||||
c:/Users/Maddo/.PYENV/PYENV-WIN/versions/3.14.3/python.exe tools/psx_extract_wdl.py "E:/emu/psx/Crusader - No Remorse/LSET1/L0.WDL"
|
||||
c:/Users/Maddo/.PYENV/PYENV-WIN/versions/3.14.3/python.exe tools/psx_extract_wdl.py "E:/emu/psx/Crusader - No Remorse/SPEC_A.WDL"
|
||||
```
|
||||
|
||||
This is enough to start extracting:
|
||||
|
||||
- raw map-candidate blocks from level bundles
|
||||
- strict TIM sprite/image blocks from both level and menu/special blobs
|
||||
- exploratory raw row exports for the first LSET post-audio candidate region
|
||||
- actual extracted sprite frames from at least some type-5 bundles inside the late LSET graphics region
|
||||
|
||||
Current caution:
|
||||
|
||||
- the `u16x6` export is only a raw inspection aid
|
||||
- `L0.WDL` gives structured-looking rows such as `0041,177B,0F7F,0000,0002,0020`, but `L1.WDL` shows very different values at the same relative region
|
||||
- so this export should be treated as evidence-gathering for map decoding, not as a solved object-placement parser yet
|
||||
|
||||
## Confirmed Sprite Extraction
|
||||
|
||||
The extractor now produces actual sprite-frame outputs from at least part of the late LSET graphics bank.
|
||||
|
||||
The late-graphics scan is now widened beyond the original first-32-bundle probe. Current `L0.WDL` extraction finds `159` candidate bundles in `post_audio_region_04`, and the larger-first overview now clearly includes not just floor/wall tiles but also several object/UI-like assets such as framed panels, cabinets, a portrait, a hand-shaped sprite, a bone, and other small pickup-like art.
|
||||
|
||||
Confirmed current example from `LSET1/L0.WDL`:
|
||||
|
||||
- graphics-region-relative bundle offset: `0xE5B8`
|
||||
- whole-file bundle offset: `0x82634`
|
||||
- mode: `2` (current best read: 4bpp indexed)
|
||||
- frame count: `3`
|
||||
- first extracted frame dimensions: `40 x 66`
|
||||
- runtime default bundle palette index: `12`
|
||||
|
||||
Confirmed output files:
|
||||
|
||||
- raw frame bytes: `out/psx_wdl/L0/sprite_bundles/bundle_0000E5B8/frame_000.bin`
|
||||
- preview image: `out/psx_wdl/L0/sprite_bundles/bundle_0000E5B8/frame_000.png`
|
||||
- colored preview image: `out/psx_wdl/L0/sprite_bundles/bundle_0000E5B8/frame_000_color.png`
|
||||
- colored sprite atlas: `out/psx_wdl/L0/sprite_bundles/bundle_0000E5B8/atlas_color.png`
|
||||
- bundle metadata: `out/psx_wdl/L0/sprite_bundles/bundle_0000E5B8/bundle.json`
|
||||
- palette metadata: `out/psx_wdl/L0/sprite_bundles/bundle_0000E5B8/palette.json`
|
||||
|
||||
Current result:
|
||||
|
||||
- this is no longer just a graphics-bank hypothesis
|
||||
- the workflow can now extract actual sprite/image frame payloads and render preview images from at least some LSET graphics bundles
|
||||
- the workflow can now also render colored previews and a proper per-bundle RGBA atlas using the bundle default palette index recovered from the PSX executable
|
||||
- widened grayscale overviews now confirm that the late graphics bank contains recognizable object and UI art, not just texture/noise candidates
|
||||
|
||||
Current palette model:
|
||||
|
||||
- the executable-backed palette source for `LSET*.WDL` is the first `0x1000` bytes loaded into `DAT_800676d8` by `lset_level_bundle_load`
|
||||
- `level_palette_upload_cluts` uploads that `0x1000` block as `8 x 16` raw 16-color CLUTs and then caches the raw CLUT handles in `DAT_800a9f48`
|
||||
- `level_palette_expand_5bit_to_16color` separately builds a second `0x1000` grayscale-expanded table, but `DAT_800a9f48` is populated only from the first 8 raw rows
|
||||
- the bundle header field at `+0x14` is the default palette-table index used when no override is supplied at draw time
|
||||
- the remaining blocker is not locating the raw CLUT block anymore; it is recovering the per-placement palette override metadata that can replace the bundle default during map/tile rendering
|
||||
|
||||
Current color blocker:
|
||||
|
||||
- both main texture draw helpers (`FUN_80044bdc` and `FUN_80044e9c`) fall back to the bundle default palette index only when no override is present
|
||||
- the important caller path at `FUN_80041458` ORs in a high-byte palette override from object/tile metadata pointed to by object field `+0xa0`
|
||||
- that means standalone bundle previews can still be wrong even when the bundle parser and raw CLUT table are both correct
|
||||
- the extractor now emits wider `u16x12` raw CSV views for `post_audio_region_01` and `post_audio_region_02` because the relevant override state appears to live beyond the first 6 words of those candidate placement records
|
||||
- the current top-ranked portrait bundle (`bundle_00064478`, default palette index `106`) is a useful color-validation anchor because the grayscale frame is obviously correct while all raw-palette candidates remain visibly wrong
|
||||
- another important unresolved issue is the exact on-disk location of the second-stage runtime header after the initial `0x3520` front image block. The loader assembly proves the runtime sequence is: `0x3520` front block -> `12`-byte header -> palette blob -> audio blob -> `4`-byte stream count, but the raw file bytes at offset `0x3520` do not yet reconcile cleanly with those expected sizes.
|
||||
- `cd_file_read` itself does not transform or decompress bytes; it performs sector-based buffered CD reads. So the remaining palette-source problem is now narrowed to file-layout interpretation rather than hidden read-time decoding.
|
||||
|
||||
### Runtime Dump Grounding: cabinet console bundle
|
||||
|
||||
The new RAM/VRAM dump pair was used to ground the known-colored cabinet console bundle against live runtime state instead of continuing static palette guessing.
|
||||
|
||||
Verified cabinet anchor:
|
||||
|
||||
- bundle: `out/psx_wdl/L0/sprite_bundles/bundle_000A1B04`
|
||||
- mode: `1`
|
||||
- frame `0`: `56 x 68`
|
||||
- default bundle palette index: `0`
|
||||
|
||||
Verified live-texture result from `binary/Crusader - No Remorse (USA) GPU RAM.bin`:
|
||||
|
||||
- the frame payload from `bundle_000A1B04/frame_000.bin` exists in live VRAM as one exact `8bpp` texture match
|
||||
- exact match location: texel `x=258`, `y=256`
|
||||
- texture page: `(1,1)`
|
||||
- in-page offset: `(2,0)`
|
||||
- no flipped exact match was found
|
||||
|
||||
This is a strong confirmation that the current `mode 1` pixel decode is correct. The remaining problem is CLUT selection, not texture extraction.
|
||||
|
||||
Verified CLUT result from the same dump:
|
||||
|
||||
- the active CLUT band used by `level_palette_upload_cluts` still sits at rows `0xF0..0xF7`
|
||||
- the important successful step was simpler than the later screen-match ranking pass: in `live_vram_clut_atlas.png`, the very first candidate at the top-left corner is the correct formula for this visible cabinet family
|
||||
- that top-left candidate is the contiguous `256`-entry palette taken directly from live GPU row `0xF0` at `x=0`
|
||||
- in other words, current best read for this `mode 1` family is: `byte value -> direct index into the 256-word slice [row 0xF0, x 0..255]`
|
||||
- equivalently, this behaves like `16` adjacent live `16-color` CLUTs flattened into one `256`-entry lookup table for the sprite byte stream
|
||||
- the later numeric ranking pass that preferred handle `64` / row `0xF4` was misleading for this case and should not be treated as the correct palette formula
|
||||
|
||||
Important consequence:
|
||||
|
||||
- the dump-grounded success case is not `bundle default row 0` from `L0.WDL` and not the later `row 0xF4` ranking result
|
||||
- the working palette source is the live VRAM CLUT row `0xF0`, `x=0`, treated as one contiguous `256`-entry table
|
||||
- this means the current extractor problem for `mode 1` bundles is better described as `recover the runtime CLUT-row formula` rather than `pick one cached CLUT handle index`
|
||||
- for the visible wall-console bundle, that runtime formula now has a concrete verified answer even though the higher-level metadata path that selects it is still unresolved
|
||||
|
||||
Wider decode result using the corrected formula:
|
||||
|
||||
- a focused batch renderer was run over the detected `mode 1` bundles in `LSET1/L0.WDL` post-audio graphics region `04`
|
||||
- using the same live palette source `row 0xF0 / x=0`, the pass rendered `92` `mode 1` bundles with plausible colored output instead of only the single cabinet proof case
|
||||
- the strongest batch proof is the generated overview:
|
||||
- `out/psx_wdl/L0/mode1_live_clut_row_f0_x0/overview_live_row_f0_x0.png`
|
||||
- per-bundle outputs and summary metadata now live under:
|
||||
- `out/psx_wdl/L0/mode1_live_clut_row_f0_x0/`
|
||||
- `out/psx_wdl/L0/mode1_live_clut_row_f0_x0/summary.json`
|
||||
- that wider pass now shows many object-like assets decoding plausibly under the same rule: cabinets, panels, tanks, wall fixtures, floor markers, weapons, pickups, and small machinery props
|
||||
|
||||
Generated runtime-grounded artifacts:
|
||||
|
||||
- `binary/psx_framebuffer_left.png`
|
||||
- `binary/psx_framebuffer_console_crop.png`
|
||||
- `out/psx_wdl/L0/sprite_bundles/bundle_000A1B04/live_vram_clut_atlas.png`
|
||||
- `out/psx_wdl/L0/sprite_bundles/bundle_000A1B04/live_vram_clut_top_matches.png`
|
||||
- `out/psx_wdl/L0/sprite_bundles/bundle_000A1B04/live_vram_clut_best.png`
|
||||
- `out/psx_wdl/L0/sprite_bundles/bundle_000A1B04/live_vram_clut_rank.txt`
|
||||
- `out/psx_wdl/L0/mode1_live_clut_row_f0_x0/overview_live_row_f0_x0.png`
|
||||
- `out/psx_wdl/L0/mode1_live_clut_row_f0_x0/summary.json`
|
||||
|
||||
One important caveat from the dump-grounded pass:
|
||||
|
||||
- none of the `8` raw `256-color` palette blocks carved from `LSET1/L0.WDL` matched the live CLUT rows byte-for-byte in this dump
|
||||
- that means either the dump was captured from a different loaded level/resource set, or the active runtime palette source for this on-screen console is not the raw `L0.WDL` palette blob currently being tested
|
||||
|
||||
Palette follow-up note:
|
||||
|
||||
- most extracted PSX sprite data is now structurally correct, but palette selection is still only partially solved
|
||||
- the current exporter should therefore be treated as `good enough to continue map work`, not as a final automatic color pipeline
|
||||
- we need a later pass to recover the runtime palette-selection rule well enough to assign the correct palette automatically for every bundle family instead of relying on the currently verified `mode 1` rule plus heuristics
|
||||
|
||||
## PSX Map Decode Plan
|
||||
|
||||
Current objective:
|
||||
|
||||
- decode the PSX `LSET*.WDL` map/resource layout well enough to render PSX maps through the existing public map renderer pipeline instead of building a one-off viewer
|
||||
|
||||
Current working split:
|
||||
|
||||
- `post_audio_region_01` is now the first high-confidence map-placement candidate
|
||||
- `post_audio_region_02` still looks more like compressed or mixed resource payload than directly renderable placement rows
|
||||
- `post_audio_region_04` remains the late graphics bank and should be treated as the PSX art source for any eventual renderer integration
|
||||
|
||||
Immediate working hypothesis:
|
||||
|
||||
- `post_audio_region_01` is a fixed-row authored layout stream
|
||||
- the raw `u16x12` view is already showing that each `24`-byte row behaves like two adjacent `6`-word records with similar field structure
|
||||
- the strongest early evidence is that neighboring left/right halves carry similar value ranges and repeat the same small control words in the tail fields, which is what we would expect from paired cell/object placements rather than opaque compressed data
|
||||
|
||||
Practical renderer goal:
|
||||
|
||||
- adapt the PSX decode into the same broad source model the public renderer already uses for PC fixed maps: coordinates, shape/frame identity, and a few raw metadata bytes/words kept for inspection
|
||||
- do not block on fully naming every field before producing a first renderer-fed PSX map source
|
||||
|
||||
Planned work order:
|
||||
|
||||
1. Lock down `post_audio_region_01` row structure across more `LSET` files and confirm whether `24` bytes is the true authored row size.
|
||||
2. Separate the two half-rows into individual candidate placement records and track stable min/max ranges for each word position.
|
||||
3. Identify which words are likely coordinates by checking for bounded map-like ranges and local spatial continuity between neighboring rows.
|
||||
4. Identify which words are likely tile/object ids by checking whether the same values recur in ways that match repeated wall/floor/object motifs.
|
||||
5. Correlate the placement stream against `post_audio_region_04` bundle offsets or bundle-local ids to recover the art linkage.
|
||||
6. Determine whether `post_audio_region_02` is a secondary map layer, a lookup table for region `01`, or a different compressed resource class entirely.
|
||||
7. Prototype a PSX map-source exporter that emits JSON in a renderer-friendly form even if some fields are still labeled as raw words.
|
||||
8. Add a PSX-specific loader path to the existing map renderer instead of creating a separate PSX viewer.
|
||||
9. Once the first map renders, iterate on field naming, layer semantics, and art binding rather than trying to solve the whole format up front.
|
||||
|
||||
Current evidence-backed next step:
|
||||
|
||||
- the extractor now needs to keep emitting a paired-record export for `post_audio_region_01` so the candidate row model can be checked quickly across multiple maps without reinterpreting the CSV by hand each time
|
||||
|
||||
Current renderer-compatibility result:
|
||||
|
||||
- a first PSX-compatible static real-art probe scene is now exported for the public map renderer
|
||||
- exporter script:
|
||||
- `tools/psx_export_map_debug_scene.py`
|
||||
- current generated public-report outputs:
|
||||
- `k:\ghidra\Crusader_Decomp_Public\map_renderer\site\data\maps\psx-remorse\map-0\scene.json`
|
||||
- multiple copied frame atlases such as `k:\ghidra\Crusader_Decomp_Public\map_renderer\site\data\maps\psx-remorse\map-0\bundle_0003917C_frame_000.png`
|
||||
- `k:\ghidra\Crusader_Decomp_Public\map_renderer\site\data\catalog.json`
|
||||
- `k:\ghidra\Crusader_Decomp_Public\map_renderer\site\data\catalogs\psx-remorse.csv`
|
||||
- current scene characteristics:
|
||||
- source: filtered `LSET1/L0.WDL` `post_audio_region_01` paired-record candidates
|
||||
- rendered items: `1050`
|
||||
- unique bundle-backed shape definitions: `49`
|
||||
- copied atlas/frame PNGs: `62`
|
||||
- bounds: `3896 x 8431`
|
||||
- scene format version: `psx-region01-bundle-probe-v1`
|
||||
- current probe stats: `u0` span `62..111`, fallback frame count `187`
|
||||
|
||||
Current art-binding hypothesis used by this probe:
|
||||
|
||||
- region-01 `u0` is treated as a provisional direct bundle index into the extracted `sprite_bundles/` set
|
||||
- region-01 `u4` is treated as a provisional frame index within that bundle, clamped to the highest available frame when out of range
|
||||
- this is evidence-backed enough to render real PSX art in the existing map renderer, but not strong enough yet to call the binding solved
|
||||
- the strongest negative check so far is that the region-01 `u5` values (`0x20`, `0x22`, `0x30`) do not match the bundle default palette indexes, so the palette-selection/control path is still missing
|
||||
|
||||
Current invalidation result:
|
||||
|
||||
- this direct `u0 -> bundle index` mapping is now considered invalid for real renderer output
|
||||
- the produced scene repeats a small set of obviously wrong assets, including portrait/UI-like art, in places that do not make spatial sense for the map
|
||||
- executable-side tracing shows that art selection is type-driven through `DAT_800758cc/d0/d4/d8` resource tables loaded by `level_resource_stream_load`, not by directly indexing the raw `post_audio_region_04` bundle scan
|
||||
|
||||
New loader/data evidence from this pass:
|
||||
|
||||
- `post_audio_region_00` now has dedicated extractor diagnostics:
|
||||
- `out/psx_wdl/L0/post_audio_region_00_00007010_u16x6.csv`
|
||||
- `out/psx_wdl/L0/post_audio_region_00_00007010_u16x12.csv`
|
||||
- `out/psx_wdl/L0/post_audio_region_00_00007010_u32x5.csv`
|
||||
- `out/psx_wdl/L0/post_audio_region_00_00007010_stream_probe.json`
|
||||
- the new raw probe confirms that `post_audio_region_00` begins with a little-endian count value `0x20`
|
||||
- after an initial short header/preamble, the bytes from about `0x3c` onward look like tightly packed `12`-byte records in the same broad shape family as the old candidate placement rows:
|
||||
- example bytes at `0x3c`: `4a 00 03 16 e7 0e 00 00 01 00 20 00`
|
||||
- little-endian words: `0x004A, 0x1603, 0x0EE7, 0x0000, 0x0001, 0x0020`
|
||||
- that record family is a better next target than the invalidated direct bundle probe because it already exposes a small type-like word (`0x004A`) plus coordinate-like words without forcing an arbitrary raw-bundle index
|
||||
|
||||
What this first public renderer pass means:
|
||||
|
||||
- the existing renderer app can now load a PSX scene bundle from the static report without any PC `FIXED.DAT` dependency
|
||||
- this is currently a real-art probe of filtered placement candidates, not a final decoded PSX map
|
||||
- the renderer now displays extracted bundle art from `post_audio_region_04` instead of synthetic colored stand-ins
|
||||
- the current output is still useful because it shows that filtered region-01 records can drive recognizable, repeatedly used PSX art through the existing renderer pipeline
|
||||
- one bad extracted origin (`1x6` sprite with `xoff=65535`) initially blew out the fit bounds; the exporter now sanitizes implausible origins before writing scene metadata
|
||||
|
||||
Current app compatibility notes:
|
||||
|
||||
- the public renderer app was updated so non-`FIXED.DAT` map sources do not advertise a bogus binary export path
|
||||
- for the PSX probe scene, `Download Map Binary` is intentionally disabled while `Download PNG`, `Download Map JSON`, and `Download Atlas PNG` remain available
|
||||
- the static app successfully loads the `PSX LSET1/L0 Region 01 Art Probe` catalog entry and currently fits it at about `8%` zoom instead of the earlier collapsed `2%` fit
|
||||
|
||||
Immediate implications for the next decode pass:
|
||||
|
||||
- the public renderer integration path is now proven enough to use as a live debug target for PSX map-format work
|
||||
- the next priority is to replace the invalidated `u0 -> bundle index` hypothesis with a real type/resource lookup recovered from `level_resource_stream_load`
|
||||
- `post_audio_region_00` is now a top-tier candidate for that work because its new diagnostics expose a count-prefixed preamble plus compact typed records that look more loader-compatible than the old region-01 art probe
|
||||
- the palette override path is still the main blocker to correct final color selection even when the bundle/frame choice is plausible
|
||||
- once the bundle key and palette control path are recovered, the same scene-export path can graduate from `real-art probe` to actual PSX map rendering
|
||||
|
||||
## PSX Script / Usecode Equivalent
|
||||
|
||||
Current status:
|
||||
|
||||
- there is no evidence yet that the PSX build carries the exact same external `USECODE`/`EUSECODE.FLX` style asset pipeline used by the DOS version
|
||||
- the current PSX executable-backed work has mostly exposed compiled resource loaders, animation/audio handlers, and image upload/decode paths rather than a separate obvious bytecode container
|
||||
|
||||
Current working question:
|
||||
|
||||
- the likely PSX equivalent, if one exists, may be either:
|
||||
- compiled gameplay logic directly inside `SLUS_002.68`, or
|
||||
- a separate embedded event/script resource format inside the `LSET`/other disc blobs that is not yet isolated
|
||||
|
||||
Immediate plan:
|
||||
|
||||
1. scan the PSX executable and current renamed function set for script/event-dispatch terminology or obvious VM-style control loops
|
||||
2. compare any candidate dispatch path against the DOS usecode model only at the behavioral level, not by assuming the asset format is shared
|
||||
3. keep this as a secondary track while map decoding takes priority
|
||||
|
||||
## Practical Extraction Paths
|
||||
|
||||
### Standard media first
|
||||
|
||||
The easiest wins are the standard PS1 media formats:
|
||||
|
||||
- `MOVIES/*.STR`: treat as PS1 video streams
|
||||
- `AUDIO/*.XA`: treat as XA audio
|
||||
- `ZZZ.ZZZ`: try as a movie stream too, especially against `FMV3.STR`
|
||||
|
||||
This does not need custom reverse engineering first.
|
||||
|
||||
### Custom `.WDL` extraction second
|
||||
|
||||
The `.WDL` files are the main custom-content frontier.
|
||||
|
||||
Current executable-backed extraction order:
|
||||
|
||||
1. run `tools/psx_extract_wdl.py` over representative `LSET*.WDL` files
|
||||
2. treat `post_audio_region_01` and `post_audio_region_02` as the current best map-data extraction targets
|
||||
3. treat `post_audio_region_04` as the current best sprite/graphics extraction target
|
||||
4. carve any strict TIM blocks first, because those now have executable support via the type `4` / type `5` image handlers
|
||||
5. separately carve `SPEC_A.WDL` / `MENUS/*.WDL` as raw image-oriented blobs
|
||||
|
||||
The level files and menu/special files should not be assumed to share one parser until that is proven.
|
||||
|
||||
## Recommended Ghidra Import Candidates
|
||||
|
||||
### Primary
|
||||
|
||||
1. `E:\emu\psx\Crusader - No Remorse\SLUS_002.68`
|
||||
|
||||
Reason:
|
||||
|
||||
- confirmed by `SYSTEM.CNF`
|
||||
- valid `PS-X EXE`
|
||||
- main native code image
|
||||
|
||||
### Secondary, only if useful for subsystem RE
|
||||
|
||||
2. `E:\emu\psx\Crusader - No Remorse\FMV.BIN`
|
||||
|
||||
Reason:
|
||||
|
||||
- clearly tied to FMV playback
|
||||
- contains path and MDEC-related strings
|
||||
- could be worth importing as a raw binary/data blob if the movie subsystem becomes a target
|
||||
|
||||
### Not primary code imports
|
||||
|
||||
These currently look like content, not executables:
|
||||
|
||||
- `E:\emu\psx\Crusader - No Remorse\ZZZ.ZZZ`
|
||||
- `E:\emu\psx\Crusader - No Remorse\SPEC_A.WDL`
|
||||
- `E:\emu\psx\Crusader - No Remorse\LSET1\L0.WDL`
|
||||
- `E:\emu\psx\Crusader - No Remorse\MENUS\M13.WDL`
|
||||
|
||||
They may still be worth loading as raw binaries later for format RE, but they are not first-choice code imports.
|
||||
|
||||
## Current Working Model
|
||||
|
||||
- `SLUS_002.68` = main PS1 executable
|
||||
- `FMV.BIN` = FMV helper/support blob
|
||||
- `MOVIES/*.STR` = standard movie streams
|
||||
- `AUDIO/*.XA` = standard XA audio
|
||||
- `ZZZ.ZZZ` = likely renamed or duplicated movie stream data
|
||||
- `LSET*.WDL` = structured level/resource containers
|
||||
- `MENUS/*.WDL` and `SPEC_A.WDL` = raw-looking screen/menu resource blobs, possibly with some embedded standard PS1 image content
|
||||
|
||||
## Executable Catalog Findings
|
||||
|
||||
This batch focused on the imported `SLUS_002.68` executable as a catalog source rather than on the raw `WDL` bundles alone.
|
||||
|
||||
### Map inventory and mission-facing structure
|
||||
|
||||
Current executable-backed map findings:
|
||||
|
||||
- `wdl_resource_bundle_load_by_index` now has a direct string-backed proof for the shipped folder layout. The loader copies one of seven hardcoded path prefixes `\LSET1\L` through `\LSET7\L` based on map-index thresholds `10`, `20`, `30`, `40`, `50`, and `60`, then formats the final `.WDL` path.
|
||||
- The extracted disc tree currently ships `62` level bundles total:
|
||||
- `LSET1`: `L0` through `L9`
|
||||
- `LSET2`: `L10` through `L19`
|
||||
- `LSET3`: `L20` through `L29`
|
||||
- `LSET4`: `L30` through `L39`
|
||||
- `LSET5`: `L40` through `L49`
|
||||
- `LSET6`: `L50` through `L58`
|
||||
- `LSET7`: `L62` through `L64`
|
||||
- So the shipped PSX map-bundle range is `L0..L64` with a real on-disc gap at `L59..L61`.
|
||||
- The executable also preserves only `15` plain-text `Mission Briefing ^Mission N` strings, for `Mission 1` through `Mission 15`.
|
||||
|
||||
Current safest read:
|
||||
|
||||
- the PSX disc contains `62` shipped map/resource bundles used by the `LSET` loader
|
||||
- the player-facing campaign/briefing flow exposed by the executable is `15` numbered missions
|
||||
- any extra bundle coverage beyond that mission-facing set is currently better treated as lower-level map/resource inventory, not automatically as `15 == all shipped WDLs`
|
||||
|
||||
Per-bundle shipped inventory from the extracted disc tree:
|
||||
|
||||
| Bundle range | Folder | Count | Size range (bytes) |
|
||||
|---|---|---:|---:|
|
||||
| `L0..L9` | `LSET1` | 10 | `987,932 .. 1,312,624` |
|
||||
| `L10..L19` | `LSET2` | 10 | `1,107,380 .. 1,314,992` |
|
||||
| `L20..L29` | `LSET3` | 10 | `904,384 .. 1,221,556` |
|
||||
| `L30..L39` | `LSET4` | 10 | `1,104,316 .. 1,321,656` |
|
||||
| `L40..L49` | `LSET5` | 10 | `1,120,084 .. 1,303,732` |
|
||||
| `L50..L58` | `LSET6` | 9 | `1,012,956 .. 1,341,684` |
|
||||
| `L62..L64` | `LSET7` | 3 | `965,072 .. 1,150,428` |
|
||||
|
||||
### Passcodes and password-screen cheat status
|
||||
|
||||
Current executable-backed passcode findings:
|
||||
|
||||
- The mission-complete passcode display path at `80022cd4` and `80022f1c` synthesizes a `4`-character code from generated indexes.
|
||||
- Those indexes are mapped through the hardcoded alphabet at `80063ef0`:
|
||||
|
||||
```text
|
||||
BCDFGHJKLMNPQRSTVWXZ0123456789
|
||||
```
|
||||
|
||||
- The resulting `4` characters are written into the temporary display buffer at `80063f6e..80063f71`, null-terminated at `80063f72`, and shown through the completion message at `80063f10`:
|
||||
|
||||
```text
|
||||
^Congratulations!^ You have completed your mission.^^The passcode for the next mission is:^
|
||||
```
|
||||
|
||||
- So PSX mission passcodes are definitely real executable-generated `4`-character values, not just external manual text.
|
||||
|
||||
Current best password-screen cheat list from public PSX references:
|
||||
|
||||
- `XXXX` = hidden pictures
|
||||
- `L0SR` or `L0SER` = cheat-mode password reported by public sources; the conflicting transcription is almost certainly a `0` vs `O` issue and is not yet closed directly from the executable
|
||||
|
||||
Important executable-side caveat:
|
||||
|
||||
- none of the known public PSX mission passwords checked in this pass (`FWQP`, `HWQP`, `LRTN`) appear as plain ASCII strings inside `SLUS_002.68`
|
||||
- the same is true for the public cheat-password candidates `XXXX`, `L0SR`, and `L0SER`
|
||||
- current safest read is therefore `password entry and/or validation is numeric or transformed`, not `a plain embedded string table of passcodes`
|
||||
- this pass closed the visible generation/display side, but it did **not** yet directly close the hidden cheat-password compare path
|
||||
|
||||
### Weapons and items
|
||||
|
||||
The executable does preserve user-facing text tables for equipment.
|
||||
|
||||
Recovered ammo names:
|
||||
|
||||
- `INVALID AMMO`
|
||||
- `JL-2 AMMO`
|
||||
- `AR-7 AMMO`
|
||||
- `GL-303 AMMO`
|
||||
- `RP-22 AMMO`
|
||||
- `SG-A1 AMMO`
|
||||
|
||||
Recovered item names:
|
||||
|
||||
- `NULL ITEM`
|
||||
- `INHIBITOR`
|
||||
- `CREDITS`
|
||||
- `SCI PLANS`
|
||||
- `BLAST PAC`
|
||||
- `DET PAC`
|
||||
- `DATA LINK`
|
||||
- `LAND MINE`
|
||||
- `SPIDER BOMB`
|
||||
- `MEDICAL KIT`
|
||||
- `ENERGY CUBE`
|
||||
- `FUSION PAC`
|
||||
- `CHEMICAL BATTERY`
|
||||
- `FISSION BATTERY`
|
||||
- `FUSION BATTERY`
|
||||
- `GRAVITON GENERATOR`
|
||||
- `IONIC GENERATOR`
|
||||
- `PLASMA GENERATOR`
|
||||
|
||||
Recovered weapon names:
|
||||
|
||||
- `RP-16`
|
||||
- `RP-22`
|
||||
- `RP-32`
|
||||
- `SG-A1`
|
||||
- `AC-88`
|
||||
- `PA-31`
|
||||
- `EM-4`
|
||||
- `PL-1`
|
||||
- `UV-9`
|
||||
- `GL-303`
|
||||
- `AR-7`
|
||||
- `JL-2`
|
||||
- `JL-9`
|
||||
|
||||
Current safest read:
|
||||
|
||||
- these are real executable-backed display-name tables, not guessed carryovers from the DOS build
|
||||
- the PSX build still uses a recognizable Crusader equipment taxonomy even where some item labels differ from the more familiar DOS-side vocabulary
|
||||
|
||||
JL-2 / JL-9 follow-up:
|
||||
|
||||
- neither `JL-2` nor `JL-9` appears in the known DOS `Weapon_GetNameForShapeNo` tables already extracted in this repo for retail Remorse or Regret; those tables stop at the older DOS weapon families such as `BA-40`, `BA-41`, `PA-21`, `EM-4`, `SG-A1`, `RP-22`, `RP-32`, `AR-7`, `GL-303`, `PA-31`, `PL-1`, `AC-88`, `UV-9`, and the Regret-only additions `BK-16`, `LNR-81`, `XP-5`
|
||||
- that makes `JL-2` and `JL-9` strong PSX-only naming additions rather than inherited PC names
|
||||
- `JL-2` is also the only one of the two with an explicit PSX ammo label (`JL-2 AMMO`) in the nearby executable text table, while no matching `JL-9 AMMO` string has been recovered
|
||||
- the extracted PSX `pickups_and_weapons` sprite category contains repeated weapon-pickup art across a large spread of maps, but this pass still does not have a defensible sprite-to-name mapping for specific `JL-2` or `JL-9` pickup appearances
|
||||
|
||||
### Enemies
|
||||
|
||||
This pass did **not** recover a comparable plain-text enemy-name table from `SLUS_002.68`.
|
||||
|
||||
What is closed:
|
||||
|
||||
- the PSX executable has clean user-facing text for mission briefings, passcode UI, ammo, items, and weapons
|
||||
- the same executable does **not** expose an equally obvious plain-text enemy catalog in its main printable-string regions
|
||||
|
||||
Current safest read:
|
||||
|
||||
- enemy identities in the PSX build are probably carried primarily as numeric resource/type ids, spawn tables, or script/resource references rather than as a direct display-name list
|
||||
- the next enemy-focused pass should start from enemy spawn/type dispatch or resource-stream type tables, not from more blind string hunting
|
||||
|
||||
## Highest-Value Next Steps
|
||||
|
||||
1. Run `tools/psx_extract_wdl.py` over more `LSET*.WDL` samples and compare whether the high-offset region pattern stays stable across level sets.
|
||||
2. Recover the password-entry validation path directly so the hidden PSX cheat-password compare logic can be proven from code instead of only cross-referenced from public password lists.
|
||||
3. Focus map decoding on `post_audio_region_01` and `post_audio_region_02`, starting with table structures, coordinate ranges, and repeated record widths.
|
||||
4. Focus sprite/graphics decoding on `post_audio_region_04`, including more aggressive TIM validation and possible packed-image expansion.
|
||||
5. Recover the exact type IDs consumed by `level_resource_stream_load` so the sprite/image resource records can be labeled more precisely.
|
||||
6. Compare carved `post_audio_region_04` image assets against on-screen level graphics to separate sprite sheets from tiles.
|
||||
7. Run the raw-blob fallback across `MENUS/*.WDL` to identify which menu files contain usable embedded TIM data and which are likely packed 15-bit images.
|
||||
Loading…
Add table
Add a link
Reference in a new issue