The three sections recorded as undecoded are fixed-stride arrays and counts[0..2] are their record counts: 12, 96 and 48 bytes. Section 1's remainder is exactly 0 in 11/11 objects and section 2's exactly 96 in 11/11, which is what makes these strides rather than a coincidence of division. Section 0 is a point list: 13467 of 13467 records lie inside their object's own header bounding box. Section 2 is a plane list, 12 f32: four zeros, a unit normal (|n|=1 in 133573/133573), a signed distance, a point inside the bbox (133573/133573), and a trailing 1.0 (133573/133573). The decisive check is algebraic -- n.p + d must vanish for a real plane, and over all 133573 records the relative residual has a median of 2.29e-08 and a maximum of 2.15e-07. That is float round-off, not a fit. So a REGN object carries a point list and a plane list beside its uniform grid, which fits collision or region-boundary geometry and sits next to MCOL. Still open: section 1 (96 B, 60631 records), what queries the planes, the zeros at [0..3], and the constant 96-byte tail.
204 lines
8.8 KiB
Markdown
204 lines
8.8 KiB
Markdown
# `REGN` — a per-map spatial grid (and `MCOL` beside it)
|
||
|
||
**Status: ✅ `CONFIRMED` for the header**, which self-checks on all 11 objects on
|
||
the disc. ❔ the four data sections are undecoded. **New to this corpus** — no
|
||
document mentioned `REGN`, `MCOL` or `hidden/MiscBin.pak` before 2026-08-24.
|
||
|
||
## Where it is
|
||
|
||
`hidden/MiscBin.pak` — 40 entries, **none** name-resolved, in three groups:
|
||
|
||
| magic | count | sizes |
|
||
|---|---|---|
|
||
| `REGN` | **11** | 49 KB … 3.1 MB |
|
||
| `MCOL` | **11** | 37 KB … 81 KB |
|
||
| (other) | 18 | — |
|
||
|
||
Eleven of each, which pairs them: one `REGN` and one `MCOL` per map. `MCOL` is
|
||
untouched here; the name and the size range read like mesh collision.
|
||
|
||
## The header, and why it is believable
|
||
|
||
```
|
||
0x00 char[4] 'REGN'
|
||
0x04 u32 size of the data area (the POF0 table starts at 0x04-value + 16)
|
||
0x10 f32[4] bbox min (x, y, z, 1.0)
|
||
0x20 f32[4] bbox max (x, y, z, 1.0)
|
||
0x30 f32[4] extent (max - min)
|
||
0x40 f32[4] cell size
|
||
0x50 u32[4] grid dimensions
|
||
0x60 u16[6] six counts
|
||
0x70 u32[4] four section offsets (the first is always 0x80)
|
||
```
|
||
|
||
**The check that makes this a decode rather than a guess** — over all 11 objects:
|
||
|
||
* `extent == cell × dims` holds **11 of 11**, exactly;
|
||
* `counts[3]` equals the **cell count**: `1000` on every 10×10×10 map and `125`
|
||
on every 5×5×5 one.
|
||
|
||
Two independent fields reproducing the grid is what rules out coincidence.
|
||
|
||
## The three map sizes on the disc
|
||
|
||
| half-extent | cell | dims | objects |
|
||
|---|---|---|---|
|
||
| 250 000 | 50 000 | 10×10×10 | 2 |
|
||
| 50 000 | 10 000 | 10×10×10 | 6 |
|
||
| 25 000 | 10 000 | 5×5×5 | 3 |
|
||
|
||
So every map is a cube partitioned into 125 or 1 000 uniform cells — 10 km cells
|
||
in a 100 km cube for the common case, and one pair of maps five times larger.
|
||
|
||
## ✅ Section 3 is the cell index — and it self-checks 11/11
|
||
|
||
The fourth section is a **one entry per cell** table of 8-byte records
|
||
`(count, offset)`, immediately followed by the records those offsets point at:
|
||
|
||
```
|
||
index = offsets[3] .. offsets[3] + cells*8
|
||
payload = align16(index end) ..
|
||
record = 32 bytes (offsets step by 0x20)
|
||
```
|
||
|
||
Checked over all 11 objects: the lowest offset any cell refers to equals
|
||
**`align16(offsets[3] + cells × 8)` — 11 of 11**. On the six 10×10×10 maps
|
||
`cells × 8` is already 16-aligned and the payload butts straight up against the
|
||
index; on the three 5×5×5 maps `125 × 8 = 1000` is not, and the payload starts
|
||
8 bytes later, which is what makes the alignment rule visible rather than assumed.
|
||
|
||
Two more invariants from the same sweep:
|
||
|
||
* every occupied cell has count **exactly 1** — total items equals occupied cells
|
||
on all 11 objects, so this is "one record per cell", not a bucket list;
|
||
* **`counts[4] = occupied cells + 2`**, exactly, on all 11 (e.g. 997/995,
|
||
880/878, 125/123). The `+2` is unexplained — two sentinels, or two cells
|
||
counted differently.
|
||
|
||
Most cells are occupied: 878–998 of 1 000, 123 of 125.
|
||
|
||
## ✅ It is a serialised object graph with a `POF0` fixup table
|
||
|
||
Every one of the 11 objects contains the tag **`POF0`**, always near the tail,
|
||
and its position is exactly **`header[0x04] + 16`** — on **11 of 11**:
|
||
|
||
```
|
||
e993b93e header +0x04 = 0x0b100 POF0 at 45328 = 45312 + 16
|
||
e4155d94 header +0x04 = 0x235d70 POF0 at 2317696 = 2317680 + 16
|
||
… 11 of 11 identical relation
|
||
```
|
||
|
||
`POF0` is a **pointer-offset (fixup) table**: the file is a serialised C++ object
|
||
graph, and the loader patches the recorded slots into real pointers. That
|
||
explains a detail that would otherwise be odd — the "offsets" inside the cell
|
||
index are absolute **file** offsets, because that is what a fixup table rewrites.
|
||
|
||
So `header[0x04]` is the size of the data area, and everything past
|
||
`header[0x04] + 16` is relocation bookkeeping rather than content.
|
||
|
||
## 🔴 Two readings of the cell payload, both refuted by generalising
|
||
|
||
Both came from the smallest object and both died the moment they were checked
|
||
against the other ten — recorded because the temptation to keep them was real:
|
||
|
||
* **"the `f32` at record `+0x1c` is the grid's bounding-sphere radius."** On
|
||
`e993b93e` it is 86 689 against `√3 × 50 000 = 86 603`, a ratio of **1.001**.
|
||
On the other ten the ratio runs **0.13 – 0.27**. Fitted to one sample.
|
||
* **"a record's `(count, offset)` pairs point at leaf arrays of `count × 4`
|
||
bytes."** True for the first record of `e993b93e`; across the objects the
|
||
offset deltas fail that rule on **every** object checked (0 of 11 clean).
|
||
|
||
What survives is only descriptive: the payload area is dominated by **float
|
||
data** — the "strings" a printable-run scan finds are all byte patterns like
|
||
`0x46`/`0x47` high bytes, i.e. medium-magnitude floats, not text.
|
||
|
||
## ❔ The other three sections
|
||
|
||
Their offsets scale with the object, and `counts[0..2]` scale with them —
|
||
`(318, 1172, 2584)` for the 350 KB map against `(2936, 14967, 31155)` for the
|
||
3.1 MB one, a roughly `1 : 4.5 : 10` ratio that holds across all eleven. The
|
||
smallest object (49 KB) is nearly empty by comparison — `(8, 6, 18)` — which
|
||
makes it the cheapest one to decode first.
|
||
|
||
## Why this matters, stated without overclaiming
|
||
|
||
A mission's enemy count **rises and falls** as waves arrive and are destroyed, so
|
||
somewhere there is a scheduler with parameters — what spawns, where, and on what
|
||
trigger. A per-map uniform grid indexed by cell is exactly the structure such a
|
||
thing is indexed *by*.
|
||
|
||
🔴 **But nothing here shows spawn parameters yet.** The header is a spatial
|
||
partition and no more; the sections are unread. Treat this as the location of the
|
||
world's spatial data, not as the wave table.
|
||
|
||
## ✅ Update: `REGN` is a stage's **MapPath**
|
||
|
||
The per-stage definition record (see
|
||
[stage-definition-table.md](stage-definition-table.md)) has a field
|
||
`MapPath = test.rgn`, and `name_hash("test.rgn") = 0x3506e972`, which is one of
|
||
the `REGN` objects in `MiscBin.pak`. The sibling field `MapMesh = test.col`
|
||
hashes to `0x2cf7eb47`, an `MCOL` object in the same pak.
|
||
|
||
So `.rgn`/`REGN` is stage navigation/path data referenced by the stage record,
|
||
and `.col`/`MCOL` is the stage collision mesh. This does not by itself validate
|
||
either of the two refuted cell-payload readings recorded above, but it does
|
||
explain why the payload looks like a grid of route data.
|
||
|
||
|
||
## ✅ Sections 0, 1 and 2 have record strides — and section 2 is a PLANE list
|
||
|
||
**2026-08-26.** The three sections the page called undecoded are fixed-stride
|
||
record arrays, and `counts[0..2]` are their record counts. Dividing each
|
||
section's byte span by its count over all 11 objects:
|
||
|
||
| section | stride | evidence |
|
||
|---|---|---|
|
||
| 0 | **12 bytes** | `count × 12` fits with a remainder of 0–12 in **11 / 11** |
|
||
| 1 | **96 bytes** | `count × 96` fits with a remainder of **exactly 0 in 11 / 11** |
|
||
| 2 | **48 bytes** | `count × 48` fits with a remainder of **exactly 96 in 11 / 11** |
|
||
|
||
Section 1 landing on a zero remainder in every object, and section 2 on the same
|
||
96-byte tail in every object, is what makes these strides rather than a
|
||
coincidence of division.
|
||
|
||
### ✅ Section 0 is a point list
|
||
|
||
12 bytes is three `f32`. Over **13 467** records across all 11 objects, **every
|
||
one lies inside that object's own header bounding box — 13 467 / 13 467 (100 %)**.
|
||
Values land on the box corners (`±250 000`) and inside.
|
||
|
||
### ✅ Section 2 is a plane list — 48 bytes, and the plane equation closes
|
||
|
||
Read as twelve `f32`:
|
||
|
||
[0..3] zero
|
||
[4..6] unit normal |n| = 1 ± 0.02 in 133 573 / 133 573 (100 %)
|
||
[7] signed distance d
|
||
[8..10] a point on the plane, inside the bbox in 133 573 / 133 573 (100 %)
|
||
[11] 1.0 exactly in 133 573 / 133 573 (100 %)
|
||
|
||
**The check that makes this a decode**: for a genuine plane, `n·p + d` must be
|
||
zero. Over all **133 573** records:
|
||
|
||
|n·p + d| / scale : median 2.29e-08 p90 6.78e-08 max 2.15e-07
|
||
|
||
That is float round-off, not a fit — the relation holds to the last bits of a
|
||
32-bit float in **every record on the disc**. Three independent 100 % properties
|
||
(unit normal, point in bbox, trailing 1.0) and an exact algebraic identity are
|
||
well past what a wrong reading survives.
|
||
|
||
So a `REGN` object carries, alongside its uniform grid, a **point list** and a
|
||
**plane list** — which is the shape of collision or region-boundary geometry,
|
||
and consistent with `MCOL` sitting beside it.
|
||
|
||
### ❔ Still open
|
||
|
||
* **Section 1** (96 bytes/record, 60 631 records disc-wide) is untouched. Its
|
||
content is float-dominated with both large coordinates and mid-range values.
|
||
96 bytes is 24 floats — a plausible shape for a 4×4 matrix plus extras, or for
|
||
two 48-byte plane records, but nothing is measured.
|
||
* **What the planes are for.** "Collision or region boundary" is a reading of the
|
||
shape; nothing here shows what queries them.
|
||
* The **zeros at `[0..3]`** of every plane record, and the constant `96`-byte
|
||
tail after section 2, are unexplained.
|