Used the known-good decoder rather than my own broken one. My version had three errors: the delta stream starts at table+16 not +8, the tag bits are 0x40/0x80/0xC0 rather than 0/1/2, and slots carry the +16 fixup base. Sanity check passes -- on REGN the tool returns header slots 0x70-0x84 exactly. On MCOL, over all 11: the header-region relocated slots are exactly 0x54, 0x58, 0x5C and 0x74 (11/11); 0x5C resolves to 0x80, the first byte after the header (11/11); and 0x74 resolves to eight bytes before the first array pointer (10/11). So MCOL has four top-level pointers where REGN has six. 92.7% of gaps between consecutive relocated words are 32 bytes, in 7-127 contiguous runs per object, which with the 0x74 offset reads as arrays of 32-byte records each carrying a pointer at +8. The four targets are two float blocks, a block of small ints / u16 pairs, and counts followed by the record array -- the shape of a mesh, stated as a reading of the shape since none of the blocks is decoded. Open: the record layout, what the index block indexes, the one object where 0x74 does not land 8 before the array, and whether the 7.3% non-32 gaps are just run boundaries.
90 lines
3.9 KiB
Markdown
90 lines
3.9 KiB
Markdown
# `MCOL` — the same container as `REGN`, and the same map parameters
|
||
|
||
**🟡 Opened 2026-08-26.** `MCOL` sits beside `REGN` in `hidden/MiscBin.pak`, 11
|
||
of each, and has never been decoded. This page establishes what it shares with
|
||
`REGN` — which is a lot, and gives the next attempt a large head start.
|
||
|
||
## ✅ Same container
|
||
|
||
Over all **11** objects:
|
||
|
||
| | |
|
||
|---|---|
|
||
| `POF0` fixup table at `data_size@+4` + 16 | **11 / 11** |
|
||
| bbox pad words are `1.0` / `1.0` / `0.0` at `+0x1C`, `+0x2C`, `+0x3C` | **11 / 11** |
|
||
| `extent == max − min` for the `0x30` block | **11 / 11** |
|
||
|
||
So the header prefix is byte-for-byte the same shape as `REGN`'s: magic, data
|
||
size, then bbox min / bbox max / extent as `f32[4]`, then a triple at `0x40`.
|
||
The `POF0` mechanism applies, which means **the `chunk + 0x10` base and the
|
||
loader's own pointer list are available here too** — the two things that cracked
|
||
`REGN`.
|
||
|
||
## ✅ And the same map parameters, exactly
|
||
|
||
The bounding boxes and the `0x40` triple are not merely similar — the
|
||
distributions are **identical**:
|
||
|
||
| | `MCOL` | `REGN` |
|
||
|---|---|---|
|
||
| bbox ±250 000 | 2 | 2 |
|
||
| bbox ±50 000 | 6 | 6 |
|
||
| bbox ±25 000 | 3 | 3 |
|
||
| `0x40` = 50 000 | 2 | 2 |
|
||
| `0x40` = 10 000 | 9 | 9 |
|
||
|
||
Eleven maps, and for each one an `MCOL` and a `REGN` describing the same volume
|
||
at the same cell size. `0x40` is the **cell size** in `REGN`; the same values in
|
||
the same multiplicities here is strong evidence it is the cell size in `MCOL`
|
||
too — though note this is a match of *distributions*, not a demonstrated
|
||
object-to-object pairing, which would need the two linked by name or by a stage's
|
||
tables.
|
||
|
||
## ❔ What is not yet known
|
||
|
||
* **Everything past `0x40`.** `MCOL`'s words at `0x50`–`0x84` do **not** look
|
||
like `REGN`'s (`REGN` has grid dims at `0x50`, six `u16` counts at `0x60` and
|
||
six section pointers at `0x70`; `MCOL` has a large value, two mid-range values
|
||
and `112` at `0x50`, mostly zeros at `0x60`, and `0x05050501` at `0x70`). The
|
||
headers agree on the spatial prefix and diverge after it.
|
||
* **Everything past `0x40`** — but see below; the pointer layout is now known.
|
||
|
||
|
||
## ✅ The pointer layout, from `POF0`
|
||
|
||
**2026-08-26.** Running the known-good decoder (`regn_decode.py` on
|
||
`auto/regn-reader`) rather than my own broken one. Sanity check first: on `REGN`
|
||
it returns header slots `0x70`–`0x84` exactly — the six section pointers — so the
|
||
tool and my use of it are right.
|
||
|
||
On `MCOL`, over all **11** objects:
|
||
|
||
| | |
|
||
|---|---|
|
||
| header-region relocated slots are **exactly `0x54`, `0x58`, `0x5C`, `0x74`** | **11 / 11** |
|
||
| slot `0x5C` resolves to **`0x80`** — the first byte after the header | **11 / 11** |
|
||
| slot `0x74` resolves to **(first array pointer − 8)** | **10 / 11** |
|
||
|
||
So `MCOL` has **four** top-level pointers where `REGN` has six, and one of them
|
||
(`0x5C`) always addresses the data immediately following the header.
|
||
|
||
**The bulk of the relocations form record arrays.** 92.7 % of the gaps between
|
||
consecutive relocated words are **32 bytes**, arranged in 7–127 contiguous runs
|
||
per object. Combined with `0x74` landing 8 bytes before the first of them, the
|
||
reading is an array of **32-byte records each carrying one pointer at `+8`**.
|
||
|
||
What the four targets look like:
|
||
|
||
0x5C -> 0x80 c685620b 4596789d 4694b3b5 44a9a634 floats
|
||
0x54 -> … c6826964 456b1aa4 469ab065 c685cfe8 floats
|
||
0x58 -> … 00000001 00020003 00040005 00050004 small ints / u16 pairs
|
||
0x74 -> … 00000001 00000001 00007710 00000000 counts, then the array
|
||
|
||
Two float blocks, an index block and a record array is the shape of a mesh —
|
||
which is what a name like `MCOL` beside a navigation mesh would suggest. 🟡 That
|
||
is a reading of the shape; none of the four blocks has been decoded.
|
||
|
||
❔ Still open: the record layout, what the index block indexes, the **one object
|
||
in eleven** where `0x74` does not land 8 before the array, and the 7.3 % of gaps
|
||
that are not 32 (they are the boundaries between runs, but that has not been
|
||
checked). |