# Cathedral review — DAT-format facts (companion report) Delivered 2026-08-30 by the DAT-format verification agent (decompiled the actual `Chorizite.DatReaderWriter 2.1.7` NuGet binary with ilspycmd and cross-checked against `docs/research/named-retail/acclient.h`). Key facts for the cathedral synthesis; flagged caveats preserved. ## Sourcing caveats - Our DAT parser is the CONSUMED NuGet `Chorizite.DatReaderWriter 2.1.7` (no vendored source; facts below come from decompiling the exact linked DLL). - `references/WorldBuilder` is an uninitialized submodule in THIS worktree (read from the main worktree at a different SHA — flagged). - `references/ACViewer` does not exist in either worktree (claims about ACViewer: UNVERIFIED). ## EnvCell record (read order, decompiled Unpack) Flags (int) → self-Id (uint, duplicate) → NumSurfaces (byte) → NumPortals (byte) → NumVisibleCells (ushort) → Surfaces[](ushort) → EnvironmentId (ushort) → CellStructure (ushort) → Position (Frame) → CellPortals[] → VisibleCells[] (ushort) → [HasStaticObjs 0x2] StaticObjects (count + Stab{Id,Frame}[]) → [HasRestrictionObj 0x8] RestrictionObj. `EnvCellFlags`: SeenOutside=0x1, HasStaticObjs=0x2, HasRestrictionObj=0x8. **0x4 is unclaimed.** Retail cross-check: `CEnvCell : CObjCell` (acclient.h:32072-32091) maps 1:1; retail's `stab_list` lives on the BASE `CObjCell` (acclient.h:30914-30932) and is the **PVS list of CELL IDS** — NOT placed objects. The DatReaderWriter `Stab{Id,Frame}` placed-object struct is a coincidental homonym (zero matches for `Stab` in the named-retail header). ## CellPortal / BuildingPortal `CellPortal { PortalFlags Flags; ushort PolygonId; ushort OtherCellId; ushort OtherPortalId; }` — `PortalFlags: ExactMatch=1, PortalSide=2`. **No 0x4 member anywhere** (package, our code, WorldBuilder, and retail's own `CCellPortal` decode exactly two semantic fields: portal_side, exact_match). NOTE: this contradicts the earlier campaign note "CCellPortal DAT flag 0x4 = leads outside" — the outside link is the `OtherCellId == 0xFFFF` sentinel, not a flag bit. - ExactMatch consumers: `PortalVisibilityBuilder.cs:1168-1183` (skips reciprocal clip — decomp:433689), Walk factories (`WalkBuildingFactory.cs:76`, `WalkCellFactory.cs:94`). - PortalSide convention: `(Flags & 0x2) == 0 → normal points INTO the owning cell` (`CellPortal.cs:23-24`, `PortalInfo.cs:22-44`); `WalkCellFactory.cs:93`: `PortalSide = (Flags & 0x2) != 0 ? 0 : 1`. - `OtherCellId == 0xFFFF` = the outdoor-exit sentinel (18+ consumers); the walk world remaps it to `0xFFFFFFFFu`. `BuildingPortal` (BuildingInfo.Portals) is a DISTINCT dat type — carries its own **packed StabList of cell ids** (retail `CBldPortal` with num_stabs/stab_list + `sidedness`). The walk's `WalkBuildingFactory.cs:81` reads it directly; the older `Wb.BuildingLoader` path DISCARDS it and recomputes by BFS. ## LandBlockInfo `NumCells → Objects[Stab] → Buildings[BuildingInfo{ModelId, Frame, NumLeaves, Portals[BuildingPortal]}] → [flag] RestrictionTable`. Confirmed field-for-field; consumer `LandblockLoader.cs:69-103`. ## Water / waterfall (part d) - `LandDefs::WaterType` is COMPUTED client-side from terrain paint (types 0x10-0x14 WaterRunning..WaterDeepSea); AcDream already computes it for physics (`TerrainSurface.cs:92-128`). It is NOT packed in the records. - `SurfaceTexture.Textures` is a multi-frame list (the structural shape of a flip-book animation) but EVERY consumer in both repos freezes one fixed index (`Textures[0]`, WB `[^1]`) — **no water/texture animation is implemented anywhere**; whether retail animates via this list is UNVERIFIED. - Which object IS "the waterfall" near the cathedral: UNVERIFIED from source — two old bug comments treat it as OUTDOOR landscape-alpha content (`PortalVisibilityBuilder.cs:730-736`, `RetailPViewRenderer.cs:714-726`), consistent with a translucent stab — but note the live probes identified the terrace falls PARTICLE owners as EnvCell StaticObjects of cell 0xF4180106 (ids 4f418012-15), which is the `HasStaticObjs` list above; both can be true (mesh stab outdoors + emitter stabs in cells). ## Notable corrections this report makes to campaign lore 1. "DAT flag 0x4 = leads outside" (PV campaign memory) — REFUTED: no 0x4 exists; outside = OtherCellId 0xFFFF. 2. "stab list" ambiguity — retail stab_list = PVS cell ids on CObjCell; placed objects are a different structure; docs/comments conflating the two should be read carefully. 3. Building portals carry authored per-portal cell StabLists in the dat — the old Wb path recomputes what the dat already provides.