# `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). ## 🟑 The 32-byte record β€” a cell entry, and there are two interleaved arrays **2026-08-26.** Reading each record as 8 big-endian words (record start = pointer slot βˆ’ 8), the first entries of the smallest object are: @0x6780 00000001 00000001 00007710 00000000 00000000 00000000 00000000 47295092 @0x67A0 01000001 00000001 00007730 00000000 … @0x67C0 02000001 00000001 00007750 00000000 46023555 C6023555 C6023555 471FA1A7 @0x6820 00010001 00000001 000077B0 00000000 … Word 0 read as **four bytes** is `(x, y, z, 1)` β€” a **3-D cell index**. That object's grid is 5Γ—5Γ—5 (bbox Β±25 000, cell 10 000), and the values run 0–4 in the first byte and step the second byte at the right point. Words 4–6 are a position and word 7 a positive scalar β€” a bounding sphere. Word 1 is a count and word 2 the relocated pointer. **Every record pointer lands in the same region β€” 8 976 / 8 976 (100 %)** β€” and each points 0xFA0 further on with the same stride, so there are **two parallel arrays**, not one: array A at `0x6780` and array B at `0x7720`. ### The 50 % is the tell, not a failure Testing the cell-index reading over *all* relocated records gives almost exactly half: byte 3 == 1 4 491 / 8 976 (50.03 %) bytes 0..2 a valid cell index 4 488 (50.00 %) the record's sphere reaches that cell 4 485 (49.97 %) Three independent criteria all landing on 50.0 % is not a partial fit β€” it says **half the records are not this type**. The `POF0` slot list interleaves both arrays, and I was testing array B's records against array A's layout. Reported as a rate it would read like a half-working hypothesis; split by array it is two clean populations. 🟑 So array A is a **per-cell record** β€” cell index, count, pointer into array B, bounding sphere β€” the same role `REGN`'s section 3 plays. ❔ Array B's layout is unread, and the split has not yet been re-run per array to confirm 100 % on A. ### βœ… Split by array, and it goes to 100 % Done. Separating the records by address and re-running the same three criteria: | | array A (2 509) | array B (6 467) | |---|---|---| | byte 3 of word 0 == 1 | **100.00 %** | 30.65 % | | bytes 0–2 a valid cell index | **100.00 %** | 30.60 % | | the record's sphere reaches that cell | **99.92 %** | β€” | **Array A is the per-cell record**, exactly as read: cell index `(x, y, z)`, a count, a pointer into array B, and a bounding sphere β€” the same role `REGN`'s section 3 plays. Array B is a different record type; its ~30 % is incidental, and it is the control that shows A's 100 % is not something any 32-byte block would score. Worth noting the split was crude β€” "first half by address", giving 2 509 vs 6 467 rather than an even cut β€” and A still came out clean at 100 %. A rough partition that isolates a perfect population is stronger evidence than a careful one that isolates a good-ish population. ### βœ… A β†’ B is one-to-one, and every count is 1 Filtering array A by the cell-index criteria (so only genuine A records) and following each one's pointer, over all **11** objects: | | | |---|---| | every A record points at a **distinct** B record | **11 / 11** | | every A record's count field is **exactly 1** | **11 / 11** | Per object the A-record count is the number of **occupied cells** β€” 110, 118, 117, 488, 488, 575, 514, 468, 492, 546, 572 β€” and the total of the count fields equals it exactly. That is the *same* design `REGN` uses: this corpus already records for `REGN` that "every occupied cell has count exactly 1 β€” total items equals occupied cells". Two sibling formats, same cell-index convention. It is a further independent confirmation of the A-record reading, since the filter and the cardinality are unrelated criteria. ### ❌ Array B resisted a first pass, and the tests I reached for were bad ones I could not read B's record layout this iteration, and both attempts failed in ways worth recording rather than retrying: * **The boundary was off by 8 again.** Dumping from `pointer βˆ’ 8` (as A's layout needed) produced records that begin with what is plainly the *tail* of the previous structure β€” a zero and `0x47295092`, the same radius value A's first record carries. Same mistake as the `0x74` check two iterations ago. * **The `u16`-index test had no power.** The `0x5C` block holds ~1 232 points, so "is this `u16` below the point count" is satisfied by almost any small value β€” and duly reported 100 % at seven different offsets. That is the *fourth* time in this pair of formats that a bound-check against a large collection has produced a meaningless 100 %. ❔ So B's layout is open. **What would have power**: B records are 1:1 with occupied cells and A already carries the cell's bounding sphere, so a candidate field in B can be tested by whether it is spatially consistent with *that specific cell* β€” the same design that worked for `REGN`'s faces, and the same design that these two bound-checks lack. ## βœ… Array B decoded β€” `{count, u16 index array}`, and the chain closes **2026-08-26.** Two corrections got there. **Where the other relocations live.** `MCOL` has 9 020 relocated words and only 4 488 are A-record pointers. I assumed the rest sat at `B+8`, mirroring A β€” **refuted, 0 / 4 488**. Measuring their offset from the nearest preceding B record instead: at B + 4 4 488 (99.0 %) before the first B 44 (1.0 %) and those 44 are exactly the four header pointers Γ— 11 objects. **Nothing unaccounted for.** **So B is `{u32 count, pointer}`** β€” pointer at `+4`, not `+8`. Read that way: B@0x7720 count 0x2B ptr 0x84D0 B@0x7740 count 0x1C ptr 0x8526 0x8526 βˆ’ 0x84D0 = 0x56 = 2 Γ— 0x2B B@0x7760 count 0x02 ptr 0x855E 0x855E βˆ’ 0x8526 = 0x38 = 2 Γ— 0x1C B@0x7780 count 0x02 ptr 0x8562 = 2 Γ— 2 The pointers advance by exactly twice the count β€” a **packed `u16` array**, no padding. Over all 11 objects: | | | |---|---| | consecutive B pointers differ by **exactly `2 Γ— count`** | **4 477 / 4 477 (100.00 %)** | | the `u16` entries are valid `0x5C` point indices | 18 379 / 18 379 | The first row is the load-bearing one: an exact arithmetic identity over 4 477 consecutive pairs. (The second is the same weak bound-check flagged above β€” the point block is large, so almost any `u16` passes. It is consistent, not evidence.) ### The chain position β†’ cell β†’ A record {cell index, count 1, β†’B, bounding sphere} β†’ B record {count n, β†’u16[n]} β†’ n indices into the 0x5C point block ← wrong, see below which is the same shape as `REGN`'s `cell β†’ item β†’ refs β†’ geometry`, as the two formats' shared header and shared count-1 convention already suggested. ❔ Still open: the `0x54` and `0x58` blocks (neither is reached by this chain). What the `u16` entries index is **not** the point block β€” see immediately below. ## ❌ The `u16` entries do NOT index the point block β€” and the weak test said they did **2026-08-26.** Two tests, and the contrast between them is the point of this section. **Not a triangle list.** If the `u16` array held triangle corners, every count would be divisible by 3. Counts modulo 3 across all objects: n % 3 == 0 639 n % 3 == 1 2 063 n % 3 == 2 1 786 Spread across all three residues. Refuted. **Not cell-local either.** A B record is reached *through* a specific cell, so a point it references should lie in that cell. With a random-point control: | | | |---|---| | referenced point inside the cell that reached it | 142 / 17 871 = **0.79 %** | | a random point inside that cell (control) | 85 / 17 871 = **0.48 %** | Chance. So whatever the `u16`s address, it is not the `0x5C` point block in any spatially meaningful way. ### ⚠️ This is the bound-check hazard caught in the act One section above, the same `u16` entries scored **18 379 / 18 379 (100 %)** on "are these valid `0x5C` point indices". I flagged that at the time as the weak bound-check rather than evidence, and recorded it as *consistent* rather than as a finding. **That caution was correct**: the powered version of the same question now returns chance. This is the fifth appearance of the pattern across `REGN` and `MCOL`, and the first time both halves have been run side by side on the same field, so it is worth stating exactly: > A bound-check asks *"could this be an index?"*. Almost always, yes β€” the answer > is set by how large the target collection is, not by whether the field means > anything. The powered version asks *"does the thing it points at make sense > where it was reached from?"*, and only that version can be wrong. Had the 100 % been written up as the decode, this page would now carry a confident and false statement about `MCOL`'s geometry. ❔ What the `u16`s index is open. A datum for the next attempt: the maximum `u16` is consistently β‰ˆ **0.75 Γ—** the point count (923/1 232, 1 019/1 360, 1 163/1 552, 59/80) β€” too consistent to be coincidence, and not explained. > **Resolved in the next section.** 0.75 is `12 / 16`: "the point count" was > computed with an assumed 12-byte stride that the block lengths refute. The > `u16`s *do* index this block β€” at stride 16. Both this test and the 100 % > bound-check above were reading the block wrongly; only the powered one could > say so. ## βœ… The `0x5C` block is **bounding spheres at stride 16** β€” and that explains the 0.75 **2026-08-26.** The unexplained β‰ˆ0.75 ratio left at the end of the section above was **12 / 16**: my own stride. I had been reading the `0x5C` block as 12-byte points because `REGN`'s vertex section is 12 bytes, and never checked the assumption. It does not survive the cheapest possible check β€” **`len(0x5C)` is not a multiple of 12** in 5 of the 11 objects, so a 12-byte stride was never arithmetically possible: | object | `len(0x5C)` | Γ·12 | Γ·16 | max `u16` | |---|---|---|---|---| | `2cf7eb47` | 960 | 80.00 | 60 | 59 | | `cbb99d34` | 192 | 16.00 | 12 | 11 | | `d84a95fb` | 3 488 | 290.67 ❌ | 218 | 217 | | `db066592` | 12 640 | 1 053.33 ❌ | 790 | 789 | | `db61c506` | 2 144 | 178.67 ❌ | 134 | 133 | | `dc44fe0c` | 2 784 | 232.00 | 174 | 173 | | `dc4b0896` | 14 784 | 1 232.00 | 924 | 923 | | `dd89a110` | 18 624 | 1 552.00 | 1 164 | 1 163 | | `df4628c2` | 4 160 | 346.67 ❌ | 260 | 259 | | `e084c13c` | 192 | 16.00 | 12 | 11 | | `e16460cf` | 16 320 | 1 360.00 | 1 020 | 1 019 | At stride 16 the relation is not "β‰ˆ0.75Γ—" but **exact, in 11 / 11 objects**: max u16 == len(0x5C) / 16 βˆ’ 1 The `u16` array indexes the `0x5C` block at stride 16, and *covers it fully* β€” the largest index is always the last element. ### The record is `{ centre f32[3], radius f32 }` Read at stride 16, the first three floats lie inside the object's own bounding box in **every record of every object**, and are never unit-length, so this is a position and not a plane normal. The fourth float is a positive scalar which is **not** `|centre|`. The powered test is the one the previous section said was needed: a `u16` is reached *through a specific cell*, so the thing it names should be present in that cell. Treating the record as a sphere and the cell as its grid box: | | | |---|---| | **referenced sphere intersects the cell that reached it** | **18 559 / 18 577 = 99.90 %** | | a random sphere from the same object (control) | 2 233 / 18 577 = 12.02 % | And both halves of the record are load-bearing β€” ablating either one costs most of the signal: | | | |---|---| | centre + radius | **99.90 %** | | centre alone, radius treated as 0 | 26.75 % | | centre kept, radius shuffled within the object | 70.19 % | | radius kept, centre shuffled within the object | 23.74 % | So the `0x5C` block is a **broad-phase bounding-sphere array**, and each grid cell's `u16` list names the primitives that reach into that cell β€” the standard shape for a collision mesh, and the sibling of `REGN`'s `cell β†’ tetrahedra`. ### The list is a *subset* of what the spheres allow, which is the expected direction Testing the converse β€” is the `u16` set **exactly** the set of spheres that intersect the cell? β€” gives **17 / 4 488 cells (0.38 %)**, with 46 525 spheres intersecting a cell but absent from its list. That is the right direction and not a problem: a bounding sphere is a conservative bound on the primitive inside it, so "sphere overlaps cell" must be implied by membership but cannot imply it. The tighter true geometry lives in the `0x54` / `0x58` blocks, still undecoded. ❔ **18 exceptions (0.10 %)** go the wrong way β€” listed, but the sphere misses the cell, by `dist / radius` of 1.004 to 1.129. They are spread over 7 of the 11 objects with no object dominating, so this looks like a small build-time margin rather than a decode error, but it is **not explained** and is recorded as open. ### The chain, corrected position β†’ cell β†’ A record {cell index (x,y,z,1), count 1, β†’B at +8, bounding sphere} β†’ B record {u32 count, β†’u16[n] at +4} β†’ n indices into the 0x5C array of 16-byte bounding spheres ❔ Still open: the `0x54` and `0x58` blocks β€” the actual collision geometry that these spheres bound. Their lengths are **not** a constant multiple of the sphere count (`0x58 / n` is β‰ˆ6.0 for the large objects but 6.13 and 6.67 for the two smallest), so at least one of them is variable-stride or has its own count. ## βœ…βœ… `MCOL` is a **closed triangle collision mesh** β€” decoded **2026-08-26.** The header's `0x50` word, which the first section on this page dismissed as "a large value", is **two `u16` counts**, and they give both remaining blocks a stride: 0x50 u16 vertex count 0x52 u16 triangle count Every derived length is exact in **11 / 11** objects: | | | |---|---| | `len(0x54) == align16(12 Γ— vertices)` | **11 / 11** | | `len(0x58) == align16(6 Γ— triangles)` | **11 / 11** | | triangle count `==` sphere count (`len(0x5C)/16`) | **11 / 11** | | object | verts | tris | `0x54` | `0x58` | spheres | |---|---|---|---|---|---| | `2cf7eb47` | 34 | 60 | 416 | 368 | 60 | | `cbb99d34` | 8 | 12 | 96 | 80 | 12 | | `d84a95fb` | 111 | 218 | 1 344 | 1 312 | 218 | | `db066592` | 405 | 790 | 4 864 | 4 752 | 790 | | `db61c506` | 73 | 134 | 880 | 816 | 134 | | `dc44fe0c` | 89 | 174 | 1 072 | 1 056 | 174 | | `dc4b0896` | 502 | 924 | 6 032 | 5 552 | 924 | | `dd89a110` | 632 | 1 164 | 7 584 | 6 992 | 1 164 | | `df4628c2` | 134 | 260 | 1 616 | 1 568 | 260 | | `e084c13c` | 8 | 12 | 96 | 80 | 12 | | `e16460cf` | 554 | 1 020 | 6 656 | 6 128 | 1 020 | The two smallest objects are the tell on their own: **8 vertices and 12 triangles**, and their `0x54` block is exactly the eight `Β±250 000` corners of the map's bounding box. That is a cube as a triangle mesh β€” a map whose only collision is its outer wall. ### Sphere *i* bounds triangle *i*, and it is tight The algebraic test, which cannot pass by accident: | | | |---|---| | all three vertices of triangle *i* inside sphere *i* | **4 768 / 4 768 = 100.00 %** | | …and the sphere is **tight** (`maxβ€–vβˆ’cβ€– / r` within 1 %) | **4 768 / 4 768 = 100.00 %** | | sphere *i* encloses a *random* triangle (control) | 63 / 4 768 = **1.32 %** | `maxβ€–vβˆ’cβ€– / r` has median **0.99990** across every triangle of every object, so the radius is the enclosing radius times a fixed **1.0001** epsilon. That is the same 1.0001 visible by hand on the cube, where the sphere of a box-face triangle came out as `250 000·√2 Γ— 1.0001`. ### And the mesh is closed | | | |---|---| | edges shared by exactly two triangles | **7 152 / 7 152 = 100.00 %** | | degenerate triangles | **0** | | vertices never referenced by a triangle | **0** | A watertight manifold with no orphans β€” which is what a collision hull must be, and a result that a wrong stride or a wrong index width could not produce. `cbb99d34` has Euler characteristic `V βˆ’ E + F = 8 βˆ’ 18 + 12 = 2` (one closed surface, the box); `2cf7eb47` has 4, i.e. two closed components. ### The cell lists are a correct broad phase Re-running the completeness test with the *actual triangles* instead of their bounding spheres, using a separating-axis triangle/box test: | | | |---|---| | cells whose `u16` list is exactly the overlapping triangle set | 3 973 / 4 488 = **88.52 %** | | triangles overlapping a cell but **absent** from its list | **3** | | triangles listed but not overlapping | 730 (3.9 % of 18 577 entries) | Three misses in 18 577 entries: the lists are **complete**, which is the property a broad phase must have β€” a collision query that walks one cell's list cannot miss a triangle it should have hit. The 730 extras are conservative and harmless. Their size identifies the builder's own test as sitting **between** an exact triangle/box test and a cheap AABB one: replacing the SAT test with "triangle AABB overlaps cell" gives **0** listed-but-not-overlapping (so every listed triangle's AABB does reach its cell) but 7 913 missing, so the builder is stricter than AABB and looser than exact β€” an exact test with a margin. This also explains the **18 sphere misses** left open in the section above: they were never sphere-vs-cell facts, they are that same margin. ### The full format 0x00 magic 'MCOL' 0x04 data size (POF0 table at data_size + 16) 0x10 bbox min f32[4] 0x20 bbox max f32[4] 0x30 extent f32[4] 0x40 cell size f32[3] 0x50 u16 vertex count 0x52 u16 triangle count 0x54 β†’ vertex array f32[3] Γ— nv, padded to 16 0x58 β†’ triangle array u16[3] Γ— nt, padded to 16 0x5C β†’ bounding spheres {centre f32[3], radius f32} Γ— nt (= 0x80) 0x74 β†’ grid cell array 32-byte A records, pointer at +8 position β†’ cell β†’ A {cell index (x,y,z,1), count 1, β†’B, bounding sphere} β†’ B {u32 count, β†’u16[n] at +4} β†’ n triangle indices Reproduce with `tools/re-capture/mcol_probe.py verify` (recorded in [`../data/mcol-verify.txt`](../data/mcol-verify.txt)); `mcol_probe.py obj` writes any object out as a Wavefront OBJ. ❔ Still open: `MCOL` objects are **not name-resolved**, so which map each one belongs to β€” and the object-to-object pairing with `REGN` that the header distributions only hint at β€” is still unknown. And the runtime consumer has not been found, the same gap `REGN` has. ## βœ…βœ… All 40 `MiscBin` entries are name-resolved, and the `REGN`↔`MCOL` pairing is now demonstrated **2026-08-26.** The names are not in `MiscBin`. They are the values of three fields of the per-stage `StageResource` object (IDXD schema `3c9ae32e`, present in every `dat/GP_MAIN_GAME_.pak`), and each hashes with the ordinary pak `name_hash` straight to a TOC entry: Phase_1 MapPath = 'S14_p1_AsteroidVolume_wp.rgn' β†’ REGN MapMesh = 'S14_p1_AsteroidVolume_wp.col' β†’ MCOL CollisionMeshes = 'CollisionSet_S14.bin' **40 / 40 entries resolved, with no hash collisions** β€” the 11 `REGN`, the 11 `MCOL`, and the 18 remaining 1 675 148-byte entries, which are the `CollisionSet_S01…S16.bin`, `CollisionSet_Tutorial.bin` and `CollisionSet_test.bin` blobs. Nothing in the archive is unaccounted for. The route in was the `.pe`: `MapMesh` and `MapPath` sit adjacent in the string table at file offset 651 540, next to `CollisionMeshes` and `3DSetup.tbl`. ### The pairing, upgraded from "matching distributions" to an object-level link The first section of this page could only say that `MCOL` and `REGN` had the *same distribution* of bounding boxes and cell sizes, and flagged that this was not a demonstrated object-to-object pairing. It is now: a phase record names one `.rgn` and one `.col`, and for all **11 / 11** pairs the two share a stem and agree exactly on bounding box and cell size. | stem | phases | verts / tris | |---|---|---| | `test` | 3 | 34 / 60 | | `mapmesh_box_500km` | 70 | 8 / 12 | | `S01_AsteroidVolume_wp` | 3 | 111 / 218 | | `S04_AsteroidVolume_wp` | 2 | 405 / 790 | | `S05_AsteroidVolume_wp` | 2 | 89 / 174 | | `S08_p1_AsteroidVolume_wp` | 1 | 134 / 260 | | `S08_p2_AsteroidVolume_wp` | 1 | 8 / 12 | | `S13_AsteroidVolume_wp` | 1 | 73 / 134 | | `S14_p1_AsteroidVolume_wp` | 1 | 502 / 924 | | `S14_p2_AsteroidVolume_wp` | 1 | 632 / 1 164 | | `S28_p1_AsteroidVolume_wp` | 1 | 554 / 1 020 | `_AsteroidVolume_` also says what the meshes *are*: the asteroid fields, which is why the collision hull is a closed manifold and why most stages need none β€” **70 of the 87 phases use `mapmesh_box_500km`**, the bare arena wall. ### The name confirms the decode, and gives the world unit `mapmesh_box_500km.col` is the object this page decoded as **8 vertices and 12 triangles spanning exactly Β±250 000** β€” a cube. Its name says that cube is **500 km** across, and the decoded span is **500 000.0** units exactly, so > **one world unit is one metre.** The arithmetic is exact and it runs the other way as a check on the format work: a wrong stride or index width could not have produced a box whose measured size matches its own filename. > 🟑 **Downgraded 2026-08-26.** The *format* check stands. The *interpretation* > does not: decoding `CollisionSet_*.bin` gave the first chance to see what a > metre implies, and it makes a craft the game's own tables call "small" 133 m > long and the largest object 447 km β€” 89 % of the arena. Either the ships really > are kilometre-scale or `500km` names something other than the span. See > [`collisionset.md`](collisionset.md). > > ## βœ… **RESTORED the same day β€” the objection was reading two different families as one** > > Both halves of the objection dissolve once the `CollisionSet` names are grouped > by prefix (`tools/re-capture/collisionset.py list`, bbox diagonals): > > | prefix | n | min | median | max | > |---|---|---|---|---| > | `rou_` | 78 | 51.8 | 636.2 | 9 534.9 | > | `mob_` | 30 | 363.2 | 1 523.0 | 17 288.3 | > | `S0*` (asteroid fields) | 29 | 18 885.4 | 39 752.2 | 84 713.5 | > | **`rob_`** | **4** | **44 378.7** | **50 179.3** | **447 521.9** | > > * **The 447 km "largest object" is `rob_f002_cmesh`, from the four-member > `rob_` family β€” and that family is not ship dimensions at all.** The proof is > inside it: `rob_f001_bdy_cmesh` measures **50 179**, and `f001` is the > *player's own Delta Saber*, whose disc record gives `Size_Radius` **10.0**. A > 50 km player fighter is impossible under *every* unit convention, metres > included, so whatever `rob_` spans β€” spawn volumes, route bounds β€” it is not > hull size. It cannot be evidence about the world unit either way. > * **The "small" 133 m craft is small.** `rou_e010_cmesh` = 133.2 is the > **7th smallest of the 78** `rou_` meshes, against a family median of 636.2 and > a max of 9 534.9. Read against its own family rather than against intuition, > the game calling `e010` small agrees with the metre exactly. > > And the metre now has a second, fully independent confirmation from the dynamic > side β€” see [`../flight-speed-law.md`](../flight-speed-law.md): 20 s of straight > neutral-throttle flight covers **443.6 world units per wall-second**, the mission > clock runs **1.26Γ—** wall time, and 443.6 / 1.26 = **352 units per game-second** > against a HUD reading of **350**. A static box named 500 km measuring 500 000.0 > units, and a HUD speed matching displacement per game-second, are two unrelated > routes to the same answer. > > > **βœ… CONFIRMED: one world unit is one metre.** Reproduce with `tools/re-capture/miscbin_names.py [--pairs]` (recorded in [`../data/miscbin-names.txt`](../data/miscbin-names.txt)). ❔ Still open: the 18 `CollisionSet_*.bin` objects (magic `0x00000810`, and all **exactly 1 675 148 bytes**, which is odd for per-stage data) are named but not decoded. And the runtime consumer now has a name β€” the `.pe` RTTI carries `CMapColliderBridge` and `CSingleton` at offset 9 044 264 β€” but has not been followed into the code.