36 KiB
PSX Map Storage Model Versus PC
Scope
This note is a focused hypothesis document for how Crusader PSX stores map data on disk, how the executable turns that data into a renderable world, and how that model differs from the already better-understood PC build.
It is intentionally narrower than map-rendering.md:
- this file concentrates on storage layout and platform differences
- the rendering note concentrates on the full runtime path from load to draw
The claims below are evidence-backed where possible and explicitly marked as current best read where they remain inferential.
Bottom Line
Current best read:
- The PSX build does not store a level as one direct item table equivalent to the PC map arrays.
- A PSX level is a multi-section
LSET*.WDLbundle that combines authored placement-like records, per-type runtime banks, palette/audio payloads, and at least one additional detached or compressed runtime-state lane. - The executable turns those authored rows into live objects early, then relies on per-type state-script and runtime reselection logic to decide what finally appears on screen.
- The PC build appears much closer to a direct world-state model in memory: explicit map X/Y/Z arrays, entity tables, tile visibility grids, and item/shape sorting over stable world coordinates.
- So the main platform difference is architectural: PSX level files look more like self-contained level bundles that feed an object-construction pipeline, while PC runtime/map handling looks more like direct world-state tables plus separately loaded shape/type metadata.
2026-04-12 Live Loader Ownership Deltas
Live MCP pass on active SLUS_002.68 tightened loader-owned naming/comments in the map extraction lane:
0x80067838renamed fromsection_pack_source_80067838topsx_level_section_pack_base.0x800676d8renamed fromlevel_clut_table_ptrtopsx_level_clut_table_ptr.- Added decompiler evidence comments at
0x800398e8,0x80039af0,0x80039250,0x80024c60, and0x8003aba8.
Extractor-relevant clarified schema in this pass:
psx_level_section_pack_baseis the contiguous section-pack base established bywdl_resource_bundle_load_by_index; subsequent header-offset adds derivepsx_type_policy_table_ptr,psx_control_opcode_stream_table, andpsx_level_clut_table_ptr.- The compressed-state lane is explicit and size-stable:
psx_level_state_compressed_blob(0x8006b5d8) inflates viapsx_lzss_unpack_into_level_bufferintopsx_level_decompressed_state_buffer(0x8006769c) with target size0x3e00before runtime-header apply and root-record dispatch. psx_lzss_pack_level_bufferis the save-side counterpart (caller0x80049890) and repacks the same level-state lane, confirming this blob family is persistent runtime substrate rather than a direct authored placement stream.psx_load_type_state_banksinstalls per-type runtime payload pointers intopsx_type_state_script_bank/psx_type_simple_component_bank/psx_type_companion_extents_bank; constructors consumepsx_type_simple_component_bank[type]at0x80024c60to seed object behavior program fields.
2026-04-12 Live Section-0 Descriptor Dispatch Deltas
Live MCP pass on active SLUS_002.68 tightened section-0 record-family dispatch evidence for unresolved graphics-heavy types.
Ghidra artifacts updated in this pass:
- Renamed
0x800626f8frompsx_descriptor_generic_3e_50topsx_descriptor_row_spawn_compound_main_visible_family. - Added explicit pointer labels for neighboring unresolved-family table entries:
0x8006323c->psx_type_descriptor_ptr_00490x8006326c->psx_type_descriptor_ptr_00550x800632a4->psx_type_descriptor_ptr_0063
- Added decompiler comments at
0x800626f8,0x80063220,0x800256b0, and0x800258ccdocumenting row sharing and call convergence.
Schema clarification closed in this pass:
- Descriptor row
0x800626f8is a 3-callback family row:- slot0
0x80013618->psx_spawn_compound_record_advance_state_once - slot1
0x80013688->psx_object_refresh_main_visible_and_cleanup - slot2
0x800254c8->psx_object_release_to_free_list
- slot0
- Both section-0 authored families route into this same row via indirect descriptor dispatch:
- root-dispatch records (
psx_dispatch_section0_dispatch_roots,0x800256b0) - constructor-placement records (
psx_dispatch_section0_constructor_placements,0x800258cc)
- root-dispatch records (
- Type
0x0042pointer entry0x80063220resolves to this row, and neighboring unresolved-family entries in the requested band (0x0049,0x0055..0x0063) also resolve to the same row.
Practical consequence for exporter/runtime interpretation:
- There is currently no recovered type-unique constructor callback split between
0x0042,0x0049, and0x0055..0x0063at section-0 descriptor-dispatch entry. - These families should be treated as sharing a common spawn/update/release callback chassis, with visual divergence more likely coming from per-type banks (
psx_type_art_active_header_bank,psx_type_state_script_bank, policy words, and live state latches) than from descriptor-row callback identity.
2026-04-12 Live Object Spawn To Visible Frame Closure
Live MCP pass on active SLUS_002.68 closed the requested spawn/selector/transition lane for map-spawned objects, centered on:
psx_object_create_simple_record(0x800249f4)psx_object_create_compound_record(0x80024eec)psx_spawn_compound_record_advance_state_once(0x80013618)psx_spawn_simple_record_set_active_flag(0x8001372c)psx_object_select_state_from_transition_table(0x8001bca0)psx_object_advance_state_script(0x80025d68)psx_type42_transition_selector_tick(0x80018578)
Renames/data labels applied in this pass:
0x8003a37c:FUN_8003a37c->psx_queue_global_draw_tint_pulse_once0x80067544:DAT_80067544->psx_global_draw_tint_pulse_phase0x80067614:DAT_80067614->psx_global_draw_tint_pulse_mode
Key clarified chain (authored row -> live frame/resource decision):
- Root/transition spawn dispatch builds record payloads and calls object constructors (
0x80031500,0x80018164). - Constructors copy authored lane/state word directly into
obj+0x1c(u5lane) and seed selector/script cursor (u4lane) viapsx_object_select_state_script. psx_object_select_state_scriptinstalls selector atobj+0x9eand script cursor (obj+0x8c/0x90), but does not write final frame token.psx_object_select_state_from_transition_tableandpsx_type42_transition_selector_tickcan reseat selector and toggle low control bits (notablyobj+0x1c bit0x0002) before latch.psx_object_advance_state_scriptlatches live frame/state token intoobj+0x94from the active script stream (obj+0x90) and updatesobj+0x96step/countdown.- Projection/draw consume
obj+0x94directly as frame index token:- projection:
psx_project_object_main_visible(0x80040d44),psx_project_object_special_visible_queue(0x80040f78) - geometry helpers:
psx_resource_frame_origin_x/y,psx_resource_frame_width/height - submit path:
psx_draw_main_visible_object(0x80041458) chooses sprite-vs-image-table by bound resource kind (obj+0x10), then usesobj+0x94for per-frame lookup.
- projection:
State/selector semantics tightened in this pass:
obj+0x1cbroad lane/routing bits come from authored constructor copy first; transition logic mainly toggles low control bits (0x0002, plus local masks), not full-word replacement.obj+0x9eis the current selector identity.obj+0x94is the final live frame/state token used by rendering geometry/resource frame queries.obj+0x96is step/countdown control between selector install and next frame-token latch.
Extractor/renderer implications:
- Do not treat authored selector bytes (
u4) as final frame identity. - Model a two-stage decision: selector install/reseat (
obj+0x9e) then latch (obj+0x94) before projection. - For unresolved map families (including type
0x0042), replicate transition/reselection hooks (psx_object_select_state_from_transition_table,psx_type42_transition_selector_tick) before sampling visible frame. - Keep
u5as authored lane seed, but treat runtime low-bit toggles as post-spawn modifiers, not separate authored-family evidence.
Evidence For The PSX Model
1. LSET*.WDL is a structured level bundle, not a single map table
Executable-backed evidence already shows the selected LSET*.WDL is treated as the live level-bundle format:
lset_level_bundle_loadbuilds\LSETn\Lx.WDLpaths directlywdl_resource_bundle_load_by_indexreads a fixed0x38-byte header whose first nine dwords act like section sizes- the loader does not hand one raw placement blob to the renderer; it lays out multiple runtime destinations
The currently identified runtime destinations are:
psx_level_root_record_stream(DAT_800678f4)psx_section0_dispatch_root_records(DAT_80067720)psx_section0_constructor_placement_records(DAT_800678f0)psx_type_art_template_bank(DAT_800758d8)psx_type_simple_component_bank(DAT_800758d0)psx_type_state_script_bank(DAT_800758cc)psx_type_companion_extents_bank(DAT_800758d4)DAT_800675f8per-type flagspsx_level_detached_blob(DAT_8006767c)- optional decompressed state into
psx_level_decompressed_state_buffer(DAT_8006769c)
That alone already separates PSX from the naive "one file == one placement table" model.
2. The PSX level has at least two authored record families
The old viewer-side region00/region01 names are no longer the best model. Live Ghidra work supports two authored families instead:
psx_section0_dispatch_root_recordsatDAT_80067720psx_section0_constructor_placement_recordsatDAT_800678f0
Their roles differ:
- dispatch roots are generic runtime-object descriptors handled by per-type dispatchers
- constructor placements are tighter spawn inputs and already behave more like direct object-placement rows
This matters because it means the PSX map is not one homogeneous table of final world items. The file stores different authored row classes that the runtime interprets differently.
3. PSX level files also carry per-type runtime banks
psx_load_type_state_banks is the clearest storage-side evidence that part of the PSX level file is not map rows at all but type-local runtime support data.
Its current decompilation shows one serialized bank blob splitting into three parallel per-type lanes:
- state-script pointers into
DAT_800758cc - simple-component payload pointers into
DAT_800758d0 - companion-extents pointers into
DAT_800758d4
Separately, the template/art bank DAT_800758d8 is assigned through its own late descriptor stream, not through the same small early-table hypothesis that was used in earlier viewer work.
So PSX storage combines:
- authored rows that say what kinds of things the level contains
- per-type banks that say how those things behave or present at runtime
That is a stronger sign of a bundle-plus-runtime-data model than of a direct item-grid serialization.
4. The PSX level contains a separate decompressed state lane
The current FUN_8003b00c read is also important. It is a sliding-window decompressor that inflates one source blob into DAT_8006769c.
Current best read:
- the decompressed size target is
0x3e00 - this lane is separate from the late art bank and separate from the
psx_load_type_state_banksblobs - it therefore looks like additional runtime/state substrate, not just another view of the same authored placement rows
That makes the PSX storage model even less like a flat map table. The file appears to contain both authored placement-like records and a second prepacked runtime-state component that is unpacked during load.
5. PSX rendering does not consume the authored selector byte directly
The storage model is also constrained by the runtime draw path.
Current verified chain:
- constructors write authored coordinates into
obj+0x3c/+0x40/+0x44as16.16fixed-point - constructors preserve the original authored source-record pointer at
obj+0xa0 psx_object_select_state_scriptseeds the initial live script fromDAT_800758ccpsx_object_advance_state_scriptcan advance that script and trigger sentinel-driven control flow- some families can later reseat the active script from motion/heading logic
- final draw uses the current live state word, not just the original placement selector
That means part of what looks like "map appearance" on PSX is not stored directly as final art in the level rows. The level rows seed object creation, but runtime script/state logic still decides the final visible frame or variant in many cases.
What The PSX LSET*.WDL Probably Contains
Current best file-level model for a retail level bundle:
- Fixed top-level header.
- Audio/SPU-related blob near the front.
- One or more authored/map candidate regions.
- Small control/index region.
- Large late graphics bank region.
- Detached and optionally decompressed runtime-state payloads referenced by the loader.
For LSET1/L0.WDL, the currently validated carve is:
audio_or_spu_blobat0x34 .. 0x7010post_audio_region_00as a small table/directory blockpost_audio_region_01andpost_audio_region_02as the strongest map/meta candidatespost_audio_region_04as the strongest graphics bank candidate
For LSET1/L1.WDL, the same overall pattern reappears even though the raw row values do not line up one-to-one at the same offsets.
That cross-file repeatability is one of the main reasons the current PSX storage model is credible.
How This Seems To Differ From The PC Build
1. PC evidence points to direct world-state tables
The PC notes show much more direct world-state storage in runtime memory:
0x7ded: map X coordinate array0x7df1: map Y coordinate array0x7df5: map Z array0x7df9: entity state array0x7e1e: entity type table
The PC renderer and camera code also work directly against explicit map/tile/grid structures:
- a
6x5tile-grid spatial index at0x846a - dirty/render bitmasks for tiles
- camera/scroll globals
- direct world-to-screen transforms over stable world coordinates
That looks like a more exposed runtime world model: map entries, entity tables, and visibility structures are explicit and persistent as named tables rather than being mostly reconstructed from bundle-local banks on load.
2. PC rendering appears shape/item centric, not bundle/state-script centric
The current PC map renderer and notes are built around:
TYPEFLAG.DATshape metadata- world-space item coordinates
- dependency-based item sorting
- direct world-to-screen projection with stable item footpads and shape offsets
The PC-side projection note is likewise straightforward:
screen_x = \frac{x - y}{4}
screen_y = \frac{x + y}{8} - z
That is not proof that the PC game stores levels in one trivial file format, but it does show that the practical renderer/world model is closer to direct item placement plus shape metadata than what the PSX executable is doing.
By contrast, PSX presentation currently depends on:
- authored placement rows
- constructor family choice
- per-type art bank
- per-type state-script bank
- live script advancement and possible reselection
- palette override bytes stored in the original authored record
- one of two world-facing render lanes
So the PSX runtime path is more layered even before final draw.
3. PC map state looks less self-contained inside one level asset bundle
Current PSX evidence suggests a single LSET*.WDL level bundle carries many classes of data together:
- placement-like authored rows
- late sprite/graphics bank
- type-specific runtime banks
- audio-related blob
- detached and decompressed runtime-state data
The PC notes, by comparison, point toward a more distributed asset model:
- map/world coordinates in explicit arrays
- shape/type metadata from files such as
TYPEFLAG.DAT - draw sorting and visibility handled over stable world entries
So the practical difference is not just "PlayStation uses WDL too". It is that the PSX LSET files appear to be denser, more self-contained level packages, while the PC game seems to expose a more table-oriented world model with auxiliary metadata coming from other resources.
4. PSX map appearance is more runtime-derived than PC map appearance
Current PSX evidence shows a stronger distinction between:
- what the level file stores directly
- what the live runtime derives after object creation
Examples:
- the authored selector byte is only the start of state resolution
DAT_800758d4now reads as companion extents for collision/contact logic, not the missing direct art table- heading-based or target-based reselection can overwrite the live state used for visible resource/frame choice
- some control-script paths can reroute camera behavior or queue deferred world changes after spawn
The PC evidence does not currently suggest the same degree of post-spawn reinterpretation for ordinary map storage. The DOS/Windows build still has rich runtime behavior, but the world representation looks closer to direct entries with stable positions, shape references, and tile-based visibility.
What I Think The PSX Loader Is Really Doing
Current best reconstruction:
- Open
SPEC_A.WDLand selectedLSET*.WDL. - Read the bundle header and section-size table.
- Load palette/audio/front matter.
- Materialize authored level streams into runtime pointers such as
DAT_800678f4,DAT_80067720, andDAT_800678f0. - Load or override per-type banks into
DAT_800758cc/d0/d4, plus the late art-template bank intoDAT_800758d8. - Load an additional detached blob and optionally decompress a runtime-state payload into
DAT_8006769c. - Dispatch the authored rows through constructor/dispatcher families to build live objects.
- Let runtime state scripts, motion reselection, and palette override bytes determine final visible presentation.
That model fits more of the verified evidence than either of the older simplified ideas:
- "the first small record stream is the whole map"
- "there is one flat type-to-bundle table that directly explains all visible art"
Confidence And Open Questions
High confidence:
LSET*.WDLis a structured multi-section level bundle- the PSX level uses at least two authored record families
DAT_800758d8/d0/cc/d4are runtime per-type banks, not one homogeneous table- there is a separate decompressed state lane at
DAT_8006769c - PSX visible art is not chosen from one flat authored selector byte alone
- PC runtime evidence exposes direct map coordinate arrays and a tile-grid visibility system
Medium confidence:
post_audio_region_01andpost_audio_region_02are the main authored/map metadata regions- the PSX build is materially more self-contained per level bundle than the PC build
- the remaining placeholder-heavy PSX families fail mainly because the final live state-to-art bridge is still incomplete, not because the whole storage model is wrong
Open questions:
- exact semantics of
post_audio_region_01versuspost_audio_region_02 - exact on-disk mapping for every runtime destination loaded by the bundle loader
- exact role of
DAT_8006769cin ordinary map logic versus specialized level/session state - exact family-specific rules that map live script state to drawable resource/frame for unresolved types such as
0x0042and0x0049 - how closely any PSX authored record family corresponds to a specific PC map file structure, if at all
2026-04-12 Live Bank-Role Clarification (SLUS_002.68)
This pass focused only on runtime-bank and art/resource install semantics in the live MCP session.
0x8003917crenamed topsx_install_type_state_script_component_extents_banks0x80045ffcrenamed topsx_install_type_art_active_header_and_built_resource- The bank globals are now explicitly separated by role:
psx_type_art_active_header_bank(0x800758d8) = current per-type art lane written by header installs and art install helperpsx_type_art_built_resource_bank(0x800758c8) = resolved/reused built resource pointer lane (kind-specific)psx_type_state_script_bank(0x800758cc) = per-type state-script stream basepsx_type_simple_component_bank(0x800758d0) = per-type simple behavior/component payload basepsx_type_companion_extents_bank(0x800758d4) = per-type companion extents payload basepsx_type_policy_table_ptr(0x800675f8) = per-level policy bit table consumed by interaction/order/draw logic
Loader-to-renderer chain tightened with comments at:
- art install loop callsites
0x800396ccand0x800399b4 - state-bank install callsites
0x8003970c,0x800399f4, and0x80039ad0 - active-header table write lane
0x8003977c - built resource + mirrored active lane commits
0x800460c8and0x800460d4 - policy table install/read bridge
0x800398f0and0x80041604
Practical exporter implication: per-type art binding should treat active-header and built-resource lanes as distinct install phases that can alias after commit, while piece loading should continue to source behavior/script/extents independently from the state-bank lanes rather than assuming one fused per-type blob.
2026-04-12 Live Marker/Control Runtime-Island Clarification (SLUS_002.68)
This pass focused on the post-load marker/control/runtime island around psx_selector_to_map_id_table (0x80063e54), psx_map_id_to_gate_slot_table (0x80063e68), and psx_marker_channel_runtime_block (0x800675ec).
Direct live outcomes:
- Confirmed post-load sequencing in
psx_level_post_load_runtime_reset(0x80039ef4): - restore or mode-action
8, optional reciprocal selector/map gate check before mode-action2, always mode-action4, thenpsx_marker_channel_runtime_state_snapshot. - The selector/map pair is a control-gating table pair, not art binding:
psx_selector_to_map_id_tableis consumed by passcode/apply and post-load checks.psx_map_id_to_gate_slot_tableis consumed by post-load and section0/slot dispatch lanes (0x8002153c/0x800215b4,0x80020fbc,0x8002f2a0,0x8002f7f4).- Confirmed runtime-block persistence semantics:
psx_marker_channel_runtime_state_snapshot(0x80031878) packs mode/step/flags with sentinels.psx_marker_channel_runtime_state_restore(0x80031a3c) restores only on sentinel match, else falls back to reset helpers.
Live naming/comment deltas in this pass:
0x80030ed4->psx_marker_channel_event_queue_reset0x80030cf0->psx_marker_channel_event_queue_accumulate0x80030dfc->psx_marker_channel_event_queue_remove_at0x80030ebc->psx_marker_channel_event_queue_get_buffer_and_count0x80031738->psx_marker_channel_get_condition_value0x80067798->psx_marker_channel_event_queue_count0x8008f384->psx_marker_channel_event_queue_entries- Added durability comments at
0x8002f190,0x800311c4,0x8002f7f4,0x80039fe8,0x80031878,0x80031a3c, and0x80031738.
Practical exporter/runtimeDiagnostic implication:
- Treat this island as runtime control-state and channel gating, not direct map-art selection.
- If a diagnostic export wants stable post-load presentation behavior, include:
- reciprocal selector/map-gate state,
- runtime-block snapshot/restore validity,
- and queued packed marker-action state (event queue count and entries) because mode-action branches can repopulate and consume that queue around post-load transitions.
Detailed Extraction Plan: PSX Map To PC-Like Render Input
The goal here is not to prove that the PSX data really is the same as the PC format. It is to extract a PSX level into a render-oriented structure that is similar enough to the PC pipeline to reuse the existing renderer architecture where that makes sense.
The safest target is therefore a two-layer export:
- a PC-like flattened render layer for practical drawing
- a reversible PSX evidence layer that preserves the original bundle-driven/runtime-driven structure
That keeps the render path usable without throwing away the information needed to correct mistakes later.
Target Output Shape
The export should produce one per-map artifact with three conceptual parts.
1. Map header
Minimum fields:
- source file path such as
LSET1/L0.WDL - bundle header summary and section boundaries
- palette source metadata
- unresolved-confidence flags
- exporter version and evidence notes
2. PC-like render item list
This is the part meant to resemble the existing PC renderer input.
Each exported render item should ideally contain:
- stable item id
type- world
x/y/z - projected
screen_x/screen_yor full screen rect when known - bundle/frame reference or resolved art reference
- palette index or override when known
- render lane (
stage1orstage2when known) - approximate draw-order key or dependency metadata
- coarse shape/bounds for debugging and future occlusion work
This layer is the nearest PSX equivalent to the PC renderer's world item list.
3. PSX evidence layer
Each item in the flattened render list should still point back to the original PSX evidence used to build it.
Useful fields:
- source authored family:
section0_dispatch_rootsorsection0_constructor_placements - source file offset or runtime pointer equivalent
- raw row bytes or decoded raw words
- original selector byte
- chosen live script word if simulated
- resolved
DAT_800758cc/d0/d4/d8bank references - companion extents from
DAT_800758d4 - original source-record bytes used for palette override reads
- whether the item is direct, inferred, or still provisional
Without this layer, the flattened format will become impossible to repair once new runtime evidence lands.
Extraction Phases
Phase 1: file-level bundle extraction
Input:
- selected
LSET*.WDL - any required shared companion bundle such as
SPEC_A.WDL
Required outputs:
- section header table
- raw post-audio region boundaries
- detached blob candidates
- late graphics bank region
- candidate compressed-state source blob
Implementation notes:
- treat the file as a bundle with explicit section boundaries, not as one raw placement blob
- record all offsets and sizes in the export header even if the semantics are still provisional
- keep both raw offsets and normalized region-relative offsets
Success condition:
- the exporter can reproduce the known
L0.WDLandL1.WDLboundary pattern consistently
Phase 2: authored-row family extraction
Input:
- the regions currently believed to feed
DAT_80067720andDAT_800678f0
Required outputs:
- decoded
section0_dispatch_roots - decoded
section0_constructor_placements - family-local row counts
- raw row byte snapshots for each record
Implementation notes:
- keep the two authored families separate all the way through export
- do not collapse them into a single inferred placement table yet
- include per-row type, authored selector, authored
x/y/z, flags, and file offsets when available
Success condition:
- the row counts and type distributions are stable across multiple maps and match the current live viewer/export evidence
Phase 3: runtime-bank extraction
Input:
- the bank blobs that feed
DAT_800758d8/d0/cc/d4
Required outputs:
- per-type template/art descriptors from
DAT_800758d8 - per-type simple payloads from
DAT_800758d0 - per-type state-script descriptors from
DAT_800758cc - per-type companion extents from
DAT_800758d4
Implementation notes:
- preserve the exact source region for each bank
- keep unresolved bank sub-splits explicit instead of silently normalizing them
- export the companion extents as runtime-bounds metadata, not as final art selectors
Success condition:
- a consumer can look up all currently known bank lanes by type from the exported metadata without rereading the raw bundle
Phase 4: detached and decompressed state extraction
Input:
- detached blob source for
DAT_8006767c - compressed source that inflates into
DAT_8006769c
Required outputs:
- raw detached blob dump
- decompressed
DAT_8006769cequivalent - metadata that identifies the source offset, compressed size, and output size
Implementation notes:
- treat this as a separate lane from authored rows and per-type banks
- do not flatten this into the item list unless a specific consumer proves how it maps to items
- keep checksums or hashes so later passes can confirm reproducibility
Success condition:
- the exporter can regenerate the same decompressed payload for repeated runs on the same map
Phase 5: constructor-faithful object seeding
Input:
- authored-row families
- per-type banks
Required outputs:
- one provisional live object per constructor-backed authored row
- separate dispatch-root-backed provisional objects when the type path supports them
Minimum object fields:
type- authored
x/y/z world_x/world_y/world_zin PSX fixed-point form when known- original selector byte
- initial live script reference
- resource/template reference
- source authored family and source row offset
- palette override source bytes
Implementation notes:
- keep both raw authored coordinates and normalized runtime coordinates
- preserve the original source-record pointer semantics in metadata even though there is no literal RAM pointer in offline export
- mark whether an object came from direct constructor logic or from a more generic dispatch-root path
Success condition:
- the exported object model is rich enough to simulate initial state selection and later art/frame resolution without re-reading raw rows
Phase 6: initial live-state simulation
Input:
- seeded objects
DAT_800758ccstate-script bank
Required outputs:
- initial live script word per object
- script-sentinel interpretation trace where available
- any immediate reselection or control-side effects that happen before the object becomes visible
Implementation notes:
- this phase should be deterministic and conservative
- only apply behavior that is currently proven from executable evidence
- keep a trace record that shows whether the chosen state came directly from the authored selector or from a later reselection rule
Success condition:
- for solved families, the chosen script word matches current executable-backed expectations
Phase 7: art and palette resolution
Input:
- seeded live objects
- resolved live script words
- template bank, source-record bytes, and known palette override rules
Required outputs:
- chosen resource or bundle reference
- chosen frame index when known
- chosen palette index or override byte when known
- confidence label:
verified,family-rule, orprovisional
Implementation notes:
- use narrow family-specific rules only where current evidence supports them
- do not use
DAT_800758d4as a fallback art selector - preserve both default palette assumptions and authored override bytes so color work stays reversible
Success condition:
- the export produces a partially solved but clearly labeled art assignment rather than a misleadingly "complete" one built on weak heuristics
Phase 8: projection into a PC-like item list
Input:
- resolved or provisional live objects
Required outputs:
- flattened render items with PC-like world coordinates and draw metadata
Projection rules already supported by current evidence:
screen_x = y - xscreen_y = 2*z - (x + y)/2
Implementation notes:
- keep stage-1 and stage-2 lane identity in metadata even if both are drawn into one final frame in the first renderer pass
- include screen rectangles when known because the runtime already caches them at
obj+0x20..+0x2e - include debug fields that show both raw PSX coordinates and PC-like renderer coordinates
Success condition:
- the flattened list can be fed through the existing renderer with only PSX-specific sort/projection differences layered on top
Phase 9: render-order approximation
Input:
- flattened item list
- render lane metadata
- available bounds and screen rectangles
Required outputs:
- stable per-item sort keys or dependency edges
Implementation notes:
- do not assume simple
ysorting is enough - use whatever executable-backed visible-list ordering facts are currently known
- keep dependency metadata explicit so the renderer can later upgrade from scalar sort keys to graph ordering if needed
Success condition:
- the rendered scene is stable and debuggable even when some art remains provisional
Phase 10: validation against the PC-like renderer contract
Input:
- final flattened item list
- evidence layer and raw extraction metadata
Required outputs:
- scene JSON or equivalent artifact ready for the renderer
- validation report listing which items are verified, inferred, or unresolved
Minimum validation checks:
- row counts are reproducible across runs
- extracted banks and decompressed blobs are reproducible across runs
- constructor-placement
zvalues produce the expected multi-level output rather than a flat plane - known solved art families map to the same bundle/frame choices as the current viewer evidence
- stage-1 versus stage-2 objects remain distinguishable in metadata
Recommended Export Format
The closest useful analogue to the PC renderer input is a scene format with:
items: flattened render itemsmapSource: original PSX authored-row metadatastateLayers: decoded per-type runtime banksbundleInfo: resolved art/palette referencesevidence: loader boundaries, decompression metadata, confidence flags, and unresolved notes
If the renderer needs something even closer to the PC item list, derive it as a second view rather than making it the only exported representation.
Good compromise:
- primary export: reversible PSX scene JSON
- secondary export: flattened PC-like render list derived from that scene JSON
What "Similar To PC" Should Mean In Practice
The output should be similar to the PC format in renderer ergonomics, not in pretending the source data matches structurally.
That means:
- one row per drawable or potentially drawable object
- stable world coordinates
- stable art/frame/palette fields when known
- enough bounds and sort metadata for a normal scene renderer
- enough source metadata to trace every rendered item back to its PSX origin
It should not mean:
- forcing all PSX authored families into one fake item table
- discarding stage/lane distinctions
- flattening away state-script provenance
- treating unresolved art heuristics as solved data
Short Practical Sequence
If this had to be implemented in the current codebase as the next extraction push, the order should be:
- Stabilize section-boundary extraction for all
LSETmaps. - Export
section0_dispatch_rootsandsection0_constructor_placementsas separate row families with raw bytes. - Export
DAT_800758d8/d0/cc/d4intostateLayerswith exact source offsets. - Export the detached and decompressed state lanes separately.
- Build a constructor-faithful provisional object list.
- Simulate only the currently verified state-selection rules.
- Resolve only the currently verified art and palette rules.
- Flatten into a PC-like render list while keeping evidence links back to the PSX source.
- Render with explicit unresolved markers instead of aggressive heuristics.
- Iterate family by family until the provisional cases shrink.
Practical Conclusion
If the goal is to reproduce PSX maps faithfully, the safest current approach is:
- treat
LSET*.WDLas a bundle, not a flat placement file - keep authored record families separate
- keep type banks separate from placement data
- keep the decompressed state lane separate from both
- model the runtime as object construction followed by script/state-driven presentation
- compare against PC at the level of architecture, not by forcing PSX rows into the PC item-table mental model
The strongest current difference is therefore architectural rather than cosmetic:
- PC looks like a more direct world/item/tile representation with auxiliary type metadata
- PSX looks like a bundled authored-record plus runtime-bank package that must be interpreted through constructors and state-script logic before the final map image exists