urt-rs / docs /research /BSP_LOADING.md
nmarshall's picture
Recover leaf references and bounded area portal state
44e63ec
|
Raw History Blame Contribute Delete
6.85 kB

Native BSP plane, node and leaf loading

Reproduce: python3 scripts/analyze_bsp_loading.py. Tests: python3 scripts/test_bsp_loading.py. Evidence: bsp-loading.json, containing hash-verified graphs for CM_LoadMap and three loaders in both Linux ELF64 engines. This follows plane classification and leaf metadata.

These are bounded synthetic instruction compositions with supplied allocation returns, not original executable/callee execution. Every payload probe starts after the first allocator call and stops before an epilogue or later allocator. First allocation sizes and descriptor routing are separate instruction slices; they do not execute loader entry validation. The leaf auxiliary-allocation continuation also supplies an allocator return. No real map is loaded in this batch.

LOAD-001 — Plane conversion and derived fields (RE-321)

CM_LoadMap copies 0x90 bytes into a local header and compares version against 46 in the retained graph. The bounded routing slice passes header +24 to CMod_LoadPlanes, corresponding to descriptor 2 under the eight-byte prefix/eight-byte descriptor layout. Header +32 and +40 route nodes and leaves to descriptors 3 and 4. Native header read safety, magic checks and the complete file-bound validation contract remain open.

Plane representation Fields
Disk record, 16 bytes normal float bits +0/+4/+8; distance bits +12
Native record, 20 bytes same four words; type byte +16; signbits byte +17; padding +18/+19

The tested allocation request is 20 * (count + 12) bytes. The twelve spare plane records are not initialized by the sampled payload loops. Their later use remains open. Payload writes preserve all four original words, including negative zero. Padding remains the supplied 0xa5 sentinel; this does not establish real allocator initialization.

For finite normals, type is the first component index whose value equals +1.0, otherwise 3. There is no local normalization check in the sampled conversion. Thus (1,-2,3) gets type 0 and (-1,0,0) gets type 3. This producer rule explains why negative axis normals reach the general classifier. Signbits are the three component < 0.0 predicates; negative zero does not set a bit. The comparison constant comes from the verified file-backed ELF section.

Twenty-four paired plane payload probes cover counts 1–9, 16, 17 and 31, including odd/even peel and pair-loop paths. The 31-record case samples every normal in {-1,0,1}^3, negative zero, fractional components and two nonunit normals with +1 components. Exceptional values and MXCSR-dependent behavior remain open.

LOAD-002 — Native node reconstruction (RE-322)

Node representation Fields used by this loader
Disk record, 36 bytes signed plane index +0; child words +4/+8
Native record, 16 bytes plane pointer +0; unchanged child words +8/+12

The allocation request is 16 * count. The loader sign-extends the disk plane index and computes plane_base + 20 * index; it does not locally check that index before writing the pointer in sampled paths. A deliberately invalid -1 index therefore produces plane_base - 20 without dereferencing that plane. This observation is not a valid-map or memory-safety guarantee. Child bits are copied unchanged; their node/negative-leaf meaning is corroborated by the earlier traversal evidence.

Disk offsets +12 through +32 are not copied into native collision nodes. This is a collision-loader projection, not permission for the asset converter to discard source bounds or other consumers' requirements. Twenty-four paired payload probes exercise counts across remainder and four-record unrolling, preserve all child words and write exactly the native records.

LOAD-003 — Leaf projection and allocation state (RE-323)

The allocation request is 24 * (count + 2) bytes. The two spare leaf records remain untouched in these payload probes.

Disk offset (48-byte record) Native offset (24-byte record)
+0 cluster word +0
+4 area word +4
+40 +8
+44 +12
+32 +16
+36 +20

Disk offsets +8 through +28 are not copied. Reference-word semantics and complete brush/surface array ownership must still be connected to their loaders and consumers; the table records exact word movement without assuming native/disk layout identity.

With cluster and area running words explicitly seeded to zero, tested signed values update each running word to the maximum nonnegative ID plus one. Cluster -1 leaves the seeded cluster count unchanged. Repeated IDs do not grow counts. The maximum-word storage addresses match across the observed unrolled lanes within each engine. The collector records their roles separately.

After the payload, the loader requests 8 * area_count bytes for one auxiliary allocation. With an explicitly supplied return for that call, the next instruction slice requests 4 * area_count * area_count bytes using a 32-bit square before sign extension. The report samples small valid counts only; overflow, allocator zeroing, portal/area structures and reset/lifetime remain open.

Twenty-four paired leaf payload probes cover odd/even peel and pair-loop paths. The report also retains the static entry checks: plane length alignment, node/leaf reciprocal-division size gates and nonpositive-count error requests. Recorded error texts are MOD_LoadBmodel: funny lump size, Map with no planes, Map has no nodes and Map with no leafs. These guards are not exercised; complete malformed-lump behavior is not claimed.

Validation and follow-up

Eight independent tests cover copy/type/sign/padding behavior, signed node pointer arithmetic and child bits, leaf word reordering/maxima, allocation arithmetic, header descriptor routing, exact per-record byte writes, paired outcomes and restricted evaluator byte/conditional/signed-branch semantics. No probe writes into a spare record. Larger instruction traces retain counts/hashes; small traces retain addresses.

RE-324: validate complete header/lump bounds, alignment/division/error paths, endian handling and count overflow; recover allocator/reset/zeroing and spare-record initialization; recover leaf brush/surface references, area/portal structures and lifetime; audit every actual BSP variant and compose loading with full recursive query/callback paths; obtain original-runtime and other-platform differential fixtures. Rendering geometry and Rust/Bevy/Solari conversion remain separate open work.

Follow-up: leaf references and area portal state connects projected reference words to consumers and bounds local connectivity/portal behavior. Complete actual loading/validation/ownership and recursive flooding remain open under RE-324/RE-328.