Repository navigation
Research Report Cmfmt
Worker w-cmfmt-a2 (Attempt 2/3; Attempt 1 paused, Attempt 2 cut off by session limit), 2026-09-23.
Files: tools/probe_cmfmt.py, re_probes/cmfmt/, this report.
cm is a small id-prefix (Surface Records) plus a Havok Binary Tagfile 2016 (hknp); the parser reads every byte typed, and the writer writes back 5166/5166 Retail Payloads from 3 Archives byte-identically, 19 mutation classes are accepted 0/299, and a new Box Hull is generated which is structurally equivalent to a Retail func_collision_only Box. Not tested at runtime.
The aborted run placed two unmeasured Polytope rules into the parser; consequently, the Round-Trip fell to 5145/5166. Additionally, the Box was incorrect.
| Claim Attempt 1 | Measured | Now |
|---|---|---|
Vertex w = 0x3F000000 | i for all vertices |
21 files break this | Array padded to a multiple of 4 with exact copies of the last real vertex, 23004/23004 |
| Faces tile the index array | 116 Polytope splits index runs (e.g., in _combo/world.hkshape) |
each Face-Range in the array, summing all indices |
vertexEdges[v] = first outgoing edge |
0/22998 (last: also 0) | only "one edge starting at v" holds (22998/22998); Box carries the Retail Array of identical topology |
| Box carries Records from Template | Template amb_volume_hull_1 is a Trigger (0x4000) |
Default Record = that of Retail Solid Brushes |
| Mass = Template values, "half floats" | Incorrect: packed int16 vectors | Recalculated from Box geometry (Section 5) |
| TNA1 "not decoded" (old layout note) | Outdated |
convex_layout.txt deleted |
Type cm -> idHavokShapeResource (Registration .text 0xC0B64, String .rdata 0x2654790).
Loader .text 0x723C30, called from 0x7243AF with version = entry+0x68
(all 71672 cm entries across all archives: 21).
u32 n
n x { u32 f0 -> rt+0x08, f1 -> rt+0x10, f2 -> rt+0x0C,
f3 -> rt+0x14 (only version >= 0x12), f4 -> rt+0x18, f5 -> rt+0x1C, f6 -> rt+0x20 }
(Runtime-Stride 0x38, rt+0 = pointer to the resource)
u32 blob_size (must land exactly at the Payload end)
blob Havok "TAG0", SDKV 20160200
After Havok Load: 0x72428B..0x7242AD replaces for each Shape shape+0x30 (hknpShape.userData) with records + userData*0x38. userData is the Record Index.
Tagfile, sections always TAG0(SDKV DATA TYPE(TPTR TSTR TNA1 FSTR TBDY THSH TPAD) INDX(ITEM PTCH))
(5166/5166). Fully decoded: Type table (Names, Templates, Parents, Subtype Flags, Sizes, Members), DATA over the Type table (each byte leaf, pointer/array slot, or checked null), ITEM (12 bytes: type|flag, offset, count), PTCH (Offsets of slots that an ITEM-index refers to). The writer generates Layout, ITEM, PTCH, TSTR/FSTR/TNA1/TBDY/THSH and all section headers from the object graph; only leaf values are inherited.
Root types: hknpCompoundShape 3514, hknpCompressedMeshShape 1256,
hknpConvexPolytopeShape 396. Havok is indeed it - but without opaque bytes.
Names from tables in the image:
| Field | Meaning | Source | Evidence |
|---|---|---|---|
| f0 | idContents Bitmask | Pointer table .rdata 0x2EEC880, Index = Bit ("unused bit 9" at 9, "unused bit 24" at 24) |
Names with trigger/volume: 334/334 have bit 14 trigger (Base 7 %); fx_func_water = water
|
| f1 | surfType_t |
.rdata 0x248A9A8, 0..38 |
glass names 66/68 GLASS* (Base 5 %), fabric 15/16 (4 %), rock 13/21 (7 %); water = LIQUID |
| f2 | SURF_*-Flags | .rdata 0x248AD68 |
only 6 values: 0, COLLISION, NOIMPACT, NOAREAS, NOIMPACT|NOAREAS, SLICK|NOIMPACT|STEEP |
| f3 | 0 | - | 21673/21673 |
| f4..f6 | -1.0f | - | 21673/21673, meaning open |
f0/f1 are correlation proofs, not consumer code interpretation. The first f1 correlation (only userData-Records) was negative and incorrectly configured; Record usage also occurs via other paths (referenced in 4729 files, no userData some Records - presumably Shape-Tags in Compressed Meshes, unaccounted for).
python tools/probe_cmfmt.py --roundtrip -> re_probes/cmfmt/roundtrip.txt
| Archive | Result |
|---|---|
base/chunk_8.resources |
907/907 byte-identical |
base/chunk_16.resources |
1469/1469 |
dlc/dlc_2/base/chunk_22.resources |
2790/2790 (5 empty cm entries) |
| total | 5166/5166, 108 MB Payload |
Cm.parse is strict: type table fully resolvable, layout rule per ITEM, gaps null, non-field bytes null, every reference type-checked, PTCH == found slots, polytope count consistent.
python tools/probe_cmfmt.py --negative -> negative_controls.txt, 299 Payloads (100 per archive, distributed by size). Accepts 0 for all 19 classes: Record count +-1, blob_size +-4, end -1/-16/+8, ITEM-Count +1, Primitive-Stride +4/+1, PTCH-Offset +4, byte in field padding, object pointer on array, SDKV, TNA1 count, Record-Stride 32/24, ITEM-Stride 16/8. The writer may fail: 16->8 Array Alignment changes 298/299. Additionally at the Box-File: broken Vertex w and Face-Range outside are rejected.
python tools/probe_cmfmt.py --box OUT.cm --half-extents 32,32,64
-> re_probes/cmfmt/box_64x64x128.cm (4532 B, sha256 0f35940e…df3d, deterministic).
Inherited from the template: type table, root scalars (Flags, Type, Dispatch, convexRadius 0.01, Property-Key) and vertexEdges (identical topology; Havoks choice of outgoing edge is not reconstructed). Newly generated: Vertices, planes, faces, indices, faceLinks (twin half-edges, 22883/22883 Retail), mass, penetration, records, object graph.
Mass, measured on 1762 retail axis boxes:
- Volume = product of edge lengths including 2x convexRadius, Mass = 1000 x Volume;
- Inertia = 1.5 x full cuboid tensor, 1762/1762 within 4 pack quanta (1742 in 1);
-
hkPackedVector3: 3x int16 + upper half of a float scaler, largest component in [2^14, 2^15): packs 45980/45980 retail vectors byte-exactly, zero vector0000000000008019(28/28);majorAxisSpace=00800080008030f5(1762/1762). - Template rebuilt: inertia byte-identical, mass/volume 2 ulp off.
maxAllowedPenetration (hkHalf16 = upper 16 bits of float32): Value/(smallest half-edge + radius) in [0.3288, 0.3313] over 1762/1762. Exact rule not found; round16(0.3305 x Basis) hits 1673/1762 bit-exactly.
Record Default (0x1 solid, 1 METAL, 0x40 SURF_COLLISION, 0, -1, -1, -1): exactly the records from maps/game/dlc/c02/c2v1/c2v1_geo_lobby_func_collision_only_2_1509497262.hkshape, a retail box of identical file size (4532 B, 1 polytope, 8 vertices/6 faces, half-edges ~0.06 x 0.40 x 0.40). This retail box passes the Box Checker; its face order is different - both are thus retail practice.
Checks (check_box, --selftest): strict parse + round-trip, corners, every plane supporting, winding outside, connectivity, mass, penetration; the same checker rejects false mass (32,32,65). None of this proves that the engine loads the file.
Exclusively the Name, built in code:
- Model:
<model>+ Suffixes in fixed order (.text 0x725280):$scale=%e,%e,%e $filterclip=%x $overridematerial=%s $simplify=%i $convexdecomposition=%i,%g $part=%d $filteroccluders=true. - Entity with
clipModelName-><clipModelName>.hkshape(.hkshapereferenced at.text 0xAF3317). Retail example, exact case for a custom box:c2v1_geo_lobby_func_collision_only_2_1509497262,CLIPMODEL_AUTO,clipModelName = "maps/game/dlc/c02/c2v1/c2v1_geo_lobby_func_collision_only_2_1509497262". - World:
<map>+/_combo/world.hkshape(.text 0x4B0741, String.rdata 0x26232D0). c2v1:hknpCompoundShapefrom 34 Convex Polytopes +hkcdDynamicTree(69 Codec32 Nodes). -
CLIPMODEL_BOX/CYLINDER/SPHERE/RENDERMODEL_BOUNDS/NONEdo not carry aclipModelNamein c2v1-3 (151/161/1/218/708) - thus no name lookup; the shape comes fromsizeor similar.
--bind via entities of c2v1-3: 28151/28431 clipModelName resolves to an existing cm (Prefix level: exact suffix values are not reconstructed).
Evidence for Map Alias: the installed dlc/dlc_4/base/chunk_22.resources contains 0 of 6985 entries with c2v1_custom in the name, thus also no maps/game/dlc/c02/c2v1_custom/_combo/world.hkshape. The bundle re_probes/idclwriter/chunk_22.resources has it (byte copy of c2v1, parsed and round-tripped). Whether the engine searches for a world cm under an alias name follows from 0x4B0700 only if the alias name arrives there - unchecked.
- Rewriting World/Compound Collision requires the
hkcdDynamicTreeBVH and for Meshes thehknpCompressedMeshShapetree. Both are read and written byte-exactly but not recalculated. Reusing as a byte copy is possible; instances shifting or new world geometry requires a newly calculated BVH - not yet. - Exact rule for
maxAllowedPenetrationand Havoks choice invertexEdges. - The semantics of f4..f6 and the path of records not referenced by any userData.
- Runtime: everything above is "structurally valid after these checks".
English Wiki generated from the measured research notes in docs/. Evidence grades and uncertainty are preserved intentionally.
- WolfSDK Loader · DE
- WolfSDK Studio · DE
- FAQ · DE
- Mod format · DE
- Modding Guide
- Modding Surface
- CVar Catalog
- CVar Validation (old scrape)
- Texture Database
- Developer Mode
- Dev Menu
- Custom-Map Status