Evidence

How this was worked out

Every rule on this site was read from the decompiled executables, their shaders or the shipped data. It was then re-implemented twice, in Python and in JavaScript, and the two were checked against each other bit for bit. This page lists the tools, the formats, the checks and what's still unverified.

1 Approach

1
Parse the data
Write readers for every container and mesh format, and dump the tables, meshes and textures.
2
Find the code
Ghidra auto-analysis, then search by string references, magic numbers, constants and known class layouts.
3
Read it exactly
Batch-decompile, walk callers and callees, and check the disassembly where the decompiler is wrong.
4
Re-implement twice
A Python reference and a JS/three.js demo, following the game's float32 arithmetic.
5
Cross-check
Parity tests compare the two implementations value for value. Render and inspect.

Kenshi

  • Binary: kenshi_x64.exe 1.0.68 and OgreMain_x64.dll. Full Ghidra 12.2 analysis (about an hour, JDK 25).
  • Names from KenshiLib's reconstructed headers. Its addresses map to this build with a constant shift per region (+0x40, +0x780).
  • String anchors: "target_opposite" → the XML loader, "Created Morph " → the mesh cache.
  • Formats: GameData (.base, .mod, .bod2), OGRE .mesh v1.8/1.100 and .skeleton, including two exporter bugs in chunk lengths.
  • Shader: character.hlsl ships as source.

Saints Row: The Third

  • The Linux port's exe is in an encrypted container, so the install was switched to the Windows depot under Proton. The packfiles are identical.
  • Binary: SaintsRowTheThird_DX11.exe (32-bit, no symbols). Ghidra analysis takes about 5 minutes.
  • Anchors: magic numbers (0x1337BEEF, 0x0BADBEEF), the constant 32767.0, Lua binding names (pcr_*).
  • Shaders: DXBC pulled from .fxo_pc_dx11, then DXBC → SPIR-V → GLSL with vkd3d-compiler and spirv-cross.
  • Creator UI: plain Lua in store_character.str2_pc.

The Sims

  • A downloaded DVD image said to carry malware: everything was handled statically. Python zipfile, unshield and unrar-free unpacked it, ClamAV and a PE sweep checked it, and nothing was ever run.
  • Binary: the unprotected no-CD Sims.exe 1.91 (32-bit; the DVD's own exe is SafeDisc-encrypted). It was verified to be the game minus SafeDisc. Ghidra analysis takes about 5 minutes.
  • Anchors: three-letter strings (fat, fit, skn, lgt, med, drk) found by absolute-address search, and MSVC RTTI class names (cWinDesignCharacter) resolved to vtables.
  • Behaviour: SimAntics code in Baby.iff decoded with a small BHAV reader, connecting births to the Make New Character primitive.
  • Every binary format has a text twin (CMX/BCF, SKN/BMF), which made decoding and checking easy.

2 Verification

TestWhat it comparesResult
Kenshi NPC facesGeneric NPC pose weights, every race and gender, Python vs JS555 faces, 0 mismatches
Kenshi Body geometryBone sizes, positional sizes, posture and skinned vertices for male, female, Shek and Hive meshes0 bone-size mismatches, vertices within 3·10⁻⁶
SR3 Creator and NPC maths50 RNG draws, 4 preset/randomise cases (110 values, all key weights, 23,931 vertex offsets, normal-map weights), 6 NPC spawns0 mismatches, bit for bit
SR3 mesh parserAll 1151 item meshes. Submesh VIDs against the table's VID_ListAll parse. 1211 of 1254 VID lists match, and the rest are table slips
Not yet done
Neither game has been compared side by side with in-game screenshots. The two re-implementations agree with each other and with the decompiled code. The next step is, for example, a Holy Nation Greenlander man, who should show one of 17-gamedata.quack_5_Male0…4, or Saints Row's male_asian + obese compared face to face.

Bugs found by looking

Several mistakes were only caught by rendering and looking. They're worth recording because each one was a wrong reading of a format:

Mirrored clothing text

The game is left-handed and three.js right-handed. Mirroring X fixes it, along with the triangle winding (three.js flips that itself) and the bitangent sign.

Crooks and Castles shirt logo reading correctly

A stray vertex on the headset

52 item meshes have two vertex buffers, so each morph target has two blocks with buffer-relative indices. Reading one block per target misplaced every later target.

Astro A40 headset rendering correctly
  • Floating NPC heads: geometry groups were missing.
  • Black NPC faces: heads were exported without tangents, so the shader normalised zero vectors.
  • Transparent hair: two shipped textures carry brightness in the opacity channel.
  • A wrong sign in Ghidra's output: the decompiler dropped an xorps sign flip in the key-weight function, found in the disassembly.

3 Formats at a glance

Kenshi

FormatNotes
GameDataint32 + length-prefixed strings. Records hold bools, floats, ints, vec3/4, strings, filenames and references. Later files override fields by stringID
OGRE meshPoses = (name, submesh, [(vertex, offset)]), no normals. Head island at v ∈ [−1, 0]
OGRE skeletonBone chunk lengths exclude the header. Animation tracks: chunks 0x4100 / 0x4110
Editor XML<Config gender><Category><Range|ColourRange>

Saints Row: The Third

FormatNotes
.vpp_pc v6Packfiles. Per-file zlib streams without a final block, or one condensed stream
.xtblXML tables with an embedded schema
.ccmesh_pc/.gcmesh_pcCPU header + GPU data. Static-mesh header 0x424BD00D, render blocks, a submesh VID table at the end, and multiple vertex buffers
.cmorph_pc0x1337BEEF / 0x0BADBEEF. A block per target per vertex buffer, with int16 deltas and per-axis scales
peg v13Textures: DXT1/3/5, A8R8G8B8. Normal maps are DXT5nm
.le_stringsA bucketed hash table: Volition-CRC key, then UTF-16 text

The Sims

FormatNotes
.farFAR!byAZ v1: uncompressed files plus a manifest (size, size, offset, name)
.cmx / .cmx.bcfSkeletons, suits (bone, mesh) and animation headers; length-byte strings. Bone rotations are stored inverted
.skn / .bmfFaces, per-bone vertex ranges, UVs, blend weights (/32768) and bone-space vertices; the binary form swaps each blend pair
.iff 2.5Big-endian chunk headers, little-endian data. STR# 200 = body strings; BHAV = SimAntics code

4 The demos

Each game also got a browser demo: a character creator rebuilt in three.js on top of the JavaScript re-implementation. The demos include every option each game has, because showing the full extent of the variety is the point. They're built from the games' own meshes and textures, so they aren't published.

5 Function maps

Kenshi: kenshi_x64.exe 1.0.68 and OgreMain_x64.dll
AddressFunction
0x140077090AppearanceManager::loadEditorXMLData
0x140071440 / 0x140071520 / 0x1400717d0DataRange::clamp / DataRangePose::clamp / DataRangeVector::clamp
0x14006e340DataRangeVector::getValue (skin ramp)
0x14006fcf0 / 0x140070010 / 0x140070460randomiseRanges (plain / pose / vector)
0x14006e050 / 0x14006e080randomFeatureSingle
0x14006e1a0getRandomValue (weighted pick)
0x14007b310 / 0x14007b7c0 / 0x14007b940 / 0x14007adc0randomFace / randomPoses / randomBody / randomHair
0x14007f040randomiseAll
0x14007c070getMorphedBodyMesh (NPC face cache and seeding)
0x140071ac0createMorphedMesh (bake)
0x140071dc0getBodyMeshForEditor
0x140581770RootObjectFactory::process (spawn, morph index)
0x140539020AppearanceBase::buildBody (faces weirdness, alwaysUnique)
0x14052e6c0AppearanceHuman::updateProportions (all bone sizes and positional sizes)
0x14052cf80AppearanceHuman::setPosture (postures, shoulder set, neck set clip times)
0x1409b0f40lerp(t, a, b) (physique factors)
0x140533b50AppearanceHuman::updateCharaterTexture
0x140531430AppearanceHuman::setupCharacterEntityTexture (skintone uniform)
0x140531040 / 0x140532600AppearanceBase::setHairTexture / setupHairMaterial
0x140532490 / 0x14052d480AppearanceBase::attachItem_Hair / attachEntity (skeleton sharing)
0x140074680AppearanceManager::setupFaces (head and hair lists)
0x1405eb5c0CharacterEditWindow::updateAppearanceSliderPose
0x1409b1c10 / 0x1409b32b0rand01 / roundHalfAway
OgreMain 0x1801e20e0 / 0x1801e1dc0OldBone::updateFromParentImpl / _getOffsetTransform
Saints Row: The Third: SaintsRowTheThird_DX11.exe (names are mine)
AddressFunction
0x00da2920 / 0x00da2a90Volition CRC of a string (lower-cased)
0x009f5c60 → 0x009f5af0 → 0x009f5960 → 0x009f5850parse player_creation.xtbl: Morph_Set → Morph_Info → Morph_Key
0x0074d2a0 / 0x00e532a0.cmorph_pc header check / pointer fix-up (0x1337BEEF, 0x0BADBEEF)
0x0074d7a0 / 0x0074d320load a .cmorph_pc (+ .gmorph_pc) by name
0x009f65c0weights of one slider's keys + delta accumulation
0x009f7cc0per-frame player morph update (subtract old, add new)
0x009f6a80 / 0x009f5200bake base + deltas into the vertex buffer / float → int16
0x009f4da0set a slider value
0x009f4e00reset all sliders to Initial_Value
0x00835d90apply a player preset (mesh, reset, grid, figure, colours)
0x008367d0register the pcr_* Lua functions
0x00836640 / 0x00833fe0 / 0x00831700 / 0x00834bc0Lua pcr_apply_preset / pcr_apply_morph_value / pcr_report_avail_morphs / pcr_randomize_eye_colors
0x00833860apply a morph value (special case for body gender)
0x009f90e0 / 0x009fafd0normal-map strengths / parse player_creation_normal_maps.xtbl
0x009fa0c0normal-map blend pass (fat → fat^¼, base = 1 − …)
0x009fa8e0diffuse composite pass (skin HSB, then layers by Layer)
0x009fb1d0customisation init: render targets, cust_normal_map_blend parameters, default layers
0x00832690 / 0x00833780 / 0x009f9760skin colours: parse / apply / specular parameters
0x00834cd0 / 0x00833460hair colours: parse / bind Hair_* shader constants
0x00daf560 / 0x00daf5b0 / 0x00daf6c0MT19937 seed / next / fill the 0x2000-entry table
0x00daf7d0 / 0x00daf850 / 0x00daf890rand01 / rand_int / rand_float
0x00e7dc00 / 0x00e7d730register Lua maths functions / Lua rand_float
0x00be2d70parse character_definitions.xtbl (heads, hair balls, props, height)
0x00be6bf0 / 0x00be76a0parse npc_color_palette.xtbl / pick primary-secondary-tertiary
0x00950660NPC appearance on creation: palette, props, head, hair shader ball, material colours
0x009fc810 / 0x009fc980parse a wear option's Obscured_VIDs / VID_List
0x00745560load the four colour pools (character, hair, makeup, tattoo) and derived colours
0x007454b0get colour pool n (count, entries)
0x00db1290read an xtbl Color (R, G, B / 255)
0x008344c0Lua pcr_report_composite_colors (pool 2 = makeup)
The Sims: Sims.exe 1.91 (no-CD build)
AddressFunction
0x408c57cached CAS candidate list for (skin, gender, age, kind)
0x40a3c6 / 0x4096c5build the head / body lists from name patterns
0x409953 … 0x40a1afformal, swimwear, pyjamas, winter, high fashion lists
0x47274f / 0x4727bd / 0x472855pattern engine: literal / alternative / any n
0x4728e9 / 0x472b1dprefix match length / best match (ratio of matched length)
0x40a5ea / 0x4eadc1validate outfits against build / assign defaults
0x40afbdnude, underwear and hand body strings
0x545b04 / 0x546fd4cWinDesignCharacter: create / command handler
0x48432f / 0x4843bdzodiac from personality / set a sign's preset
0x5bc6cb / 0x5bc722 / 0x46f556engine RNG seed / draw; simulation RNG draw
0x42a9d1 / 0x426392townie pool top-up / generic townie
0x44ae48Make New Character (SimAntics primitive 19)

6 Behind these pages

This site summarises longer write-ups, one per game, plus the full malware check of the Sims download. They have the full formulas, every table and the decompiled functions themselves, with the shaders for Saints Row and the decoded SimAntics for The Sims. They stay private along with the demos, because they quote the games' code and data at length.