Find a file
shinji 3817c8d8fc Replace non-ASCII typography with ASCII throughout
Standing rule from the owner, 2026-09-19: no em dashes, anywhere. It is not
cosmetic. Blaze is Python 2.7 and refuses non-ASCII source without a PEP 263
declaration; it died on startup this morning with

  SyntaxError: Non-ASCII character '\xe2' ... but no encoding declared

the first time the stack ran from the forge clones, because the legacy tree's
copies had been mojibaked down to ASCII and the CODA copies had not.

Swept every repo. 31,105 characters across 399 files: 10,184 em dashes, plus
en dashes, curly quotes, ellipses, arrows, section signs, box-drawing, math
symbols, check marks and emoji. Also repaired already-mojibaked sequences
(the 'a-EUR-quote' triples) in archived docs - those were corrupted em dashes
from an earlier encoding accident.

Deliberately NOT touched:
  - anthem-api/localization and gameconfig, anthem-postgres/seed_data: the
    accents in fr-fr.json and the game strings are CONTENT, and this pass would
    have corrupted them.
  - .json generally, for the same reason.
  - Leading UTF-8 BOMs on MSBuild project files, vendored MinHook sources and
    one SQL schema. Those are format markers, not typography; all 31 are
    leading, none mid-file.

Verified after the sweep: every Blaze .py compiles under Python 2.7 and
'import BlazeMain_Client' succeeds; both registry YAMLs parse; the gameserver
suite is 1649 passed / 1 failed, the same single pre-existing failure
(test_damage_decode) as before; the DLL builds clean Release|x64; the launcher
still resolves 'repo : F:\anthem\coda\_stack\' with preflight ok.
2026-09-19 10:51:42 +02:00
output Initial import: server SDK builder + generated GUID->layout table (2026-08-12) 2026-08-24 23:04:44 +02:00
build_server_sdk_2026-08-12.py Replace non-ASCII typography with ASCII throughout 2026-09-19 10:51:42 +02:00
README.md Replace non-ASCII typography with ASCII throughout 2026-09-19 10:51:42 +02:00

Anthem Server SDK - reference

Built 2026-08-11/12 from two full-memory dumps. Phase 1 of the master plan.

This is the consolidated reference. The derivation is spread across SS283-310 of 04-reverse-engineering/XBOX_VERIFICATION_OF_PRIOR_RE_2026-08-07.md; read that only if you need to know how something was established. Everything needed to use the SDK is here.


1. What it is

A map from every reflected type in Anthem to its identity, its place in the class hierarchy, and its byte-level field layout - read out of a running process, not inferred.

types                32,129     named  10,083  (31%)
classes              11,688     with classId / lastSubclassId / superclass
types with layout     6,040     (every Class-category type that has fields)
fields               28,290     named  13,221 (47%),  typed 23,495 (83%)

The artifacts

file size what
code/tools/output/anthem_server_sdk_2026-08-12.json 13.9 MB the SDK. Everything below, merged
code/tools/output/anthem_types.h 2.8 MB 6,040 C structs, Ghidra-importable
code/tools/ghidra_scripts/README_AnthemTypes.md - how to import the header

Inputs, kept because each is independently checkable:

file what
anthem_typeguidmap_2026-08-11.json 32,129 registry types (GUID, size, align, fieldCount)
anthem_type_hierarchy_named_2026-08-11.json superclass links + the first 2,457 names
anthem_classids_2026-08-11.json classId / lastSubclassId per class
anthem_field_layouts_2026-08-12.json field arrays, Tarsis dump
anthem_field_layouts_menu_2026-08-12.json same, menu dump - for diffing
anthem_names_*_2026-08-12.json name candidates by source, with provenance

Source dumps (not in git): /home/shinji/Projects/anthem_dumps/Anthem_mainmenu_2026-08-11.DMP (5.0 GB) and Anthem_tarsis_2026-08-12.DMP (12.2 GB, from a second machine).


2. Schema

Keyed by descriptor VA (stable - the build has no ASLR, verified across two machines).

"0x144fd4ec0": {
  "va":          "0x144fd4ec0",
  "guid":        "86c9b59b640d75c180341a3dbb9f1cd7",   // TypeInfoData +0x08
  "nameHash":    "0xab53fbda",                          // reflection hash, seed 1032
  "name":        "DofComponentData",                    // null if unnamed
  "name_via":    "dump_string",                         // provenance
  "size":        240,                                   // +0x06 totalSize
  "align":       16,                                    // +0x28
  "fieldCount":  26,                                    // +0x2a
  "is_class":    true,
  "classId":     6918,                                  // 1-based global DFS position
  "lastSubclassId": 6944,                               // subtree end - see S4
  "super_va":    "0x144fc71b0",
  "super_name":  "ComponentData",
  "offset_frame": "absolute_object",                    // <- READ THIS
  "fields": [
    { "nameHash": "0x1f8a30c2", "name": "FocusDistance",
      "offset": 8, "typeCode": 19, "typeName": "Float32", "typeSize": 4 }
  ]
}

(!) offset_frame: "absolute_object"

Offsets are absolute within the whole object - inherited members occupy the start. This is the frame the engine uses and the one you need to read bytes.

The older Xbox-derived anthem_type_sdk_named.json uses offsets relative to the declaring type. The two disagree on every shared field (9,339 pairs, zero identical; the delta is the inherited prefix, so it varies by which ancestor declares the field). Never mix them. The Xbox SDK's offsets are deliberately not merged here.


3. Using it

Read a field out of an instance: value = instance_base + field.offset, field.typeSize bytes. That is the whole contract.

Ghidra: File -> Parse C Source -> anthem_types.h, x86-64/Visual Studio profile. Then Ctrl+L on a decompiler variable to apply a struct. 1,653 Class-typed members are real struct X * cross-references, so following a pointer lands on a typed object.

Find a type by name:

sdk = json.load(open("code/tools/output/anthem_server_sdk_2026-08-12.json"))["types"]
by_name = {v["name"]: v for v in sdk.values() if v.get("name")}
by_name["NetObjectSystemSettings"]["fields"]

Types the project previously lacked and now has: NetObjectSystemSettings (13 fields - absent from the Xbox SDK entirely), SpawnReferenceObjectData (20), SubWorldReferenceObjectData (15), ReferenceObjectData (14), NetworkSettings (35), ClientSettings (51).


4. ClassIds

classId is a 1-based position in a global DFS of the class forest; lastSubclassId closes the subtree. So isSubclassOf(a, b) is an interval test: b.classId <= a.classId <= b.lastSubclassId.

DataContainer   [  74, 7923]   7,850 descendants
GameObjectData  [1900, 4346]
EntityData      [1907, 3899]

11,688 classes, ids forming a gap-free bijection onto 1..11,688, 173 roots whose intervals tile the space with no gaps or overlaps.

(!) These ids are NOT on the ghost wire (S285). They are a property of the build, useful for hierarchy queries; do not expect to find one in a packet.


5. What is validated, and how

Four independent checks, each of which could have failed:

check constrains result
natural alignment of every field offsets 21,232 constrained fields, 0 misaligned
zero overlap with real per-field sizes offsets AND sizes 0 of 28,290 collide
nameHash agreement vs the Xbox-derived SDK two platform binaries 1,925 of 1,927
byte-identical layouts across two dumps two machines, two worlds 8,242 of 8,264 arrays

The 22 that differed in the last check were the defect that found the typeCode bug (S305) - they were non-Class descriptors misread as classes, and filtering to typeCode == 3 removed them.

(!) Not validated: a live decode. No check here demonstrates that pointing a layout at a real object yields correct values. That is blocked on the absence of a vtable->type mapping - nothing in TypeInfoData carries one, and RTTI is stripped for game classes (only 7 CRT/third-party descriptors exist in the whole image). Closing it needs per-class constructor analysis. See SS307-308.


6. Limits

limit why
31% of types named, 47% of field names source-exhausted. A PC memory dump and an Xbox decompilation converge on the same ~10,000 names; the Xbox corpus added 2 (S309). Needs a build with reflection names compiled in
26,089 types have no struct enums, primitives, arrays - no ClassInfoData, so no field array
engine classes absent StreamManagerGhost, LevelSetup, BlueprintOwner are not reflected DataContainer types and were never in the registry
enum members absent the enum descriptor's member table is not at the offsets a Class uses; needs fresh RE, capped payoff (S310)
function names out of scope - reflection names types and fields, never functions

7. Regenerating

In order. Each step needs the previous.

D=/home/shinji/Projects/anthem_dumps/Anthem_tarsis_2026-08-12.DMP
python3 code/tools/mdmp_gonogo_2026-08-11.py          "$D"   # PASS before anything else
python3 code/tools/enumerate_typeguidmap_2026-08-12.py "$D"
python3 code/tools/sweep_classids_2026-08-12.py        "$D"
python3 code/tools/extract_field_layouts_2026-08-12.py "$D"
python3 code/tools/name_by_string_harvest_2026-08-12.py "$D" --all-memory
python3 code/tools/name_combined_2026-08-12.py
python3 code/tools/build_server_sdk_2026-08-12.py
python3 code/tools/emit_ghidra_types_2026-08-12.py
python3 code/tools/validate_sdk_alignment_2026-08-12.py        # must report 0 misaligned

mdmp_reader_2026-08-11.py is the shared minidump reader - mmap + bisect, and it self-tests VA translation against the reflection size table at 0x14372e060. Run it standalone on any new dump first.


8. Traps that cost time building this

Each of these cost at least one wrong conclusion:

  1. Read the value at the address the code actually uses. *DAT_144713698 is a pointer to the table; reading 0x144713698 itself gives the pointer cell and its neighbours (S298).
  2. Filter by typeCode == 3 before treating a descriptor as a ClassInfoData. Array/ValueType/ Enum descriptors have different layouts, and +0x30/+0x38 mean something else on them (SS297, 301).
  3. A field's type comes from its TypeInfo*, not from the FieldInfoData flags. The documented (flags>>5)&0x1f reads 0 for 93% of fields (S305).
  4. Ask what a check would report if the thing were wrong. The sizeof assertions in S304 passed by construction and constrained nothing.
  5. Diff two captures of the same build. It is the cheapest defect detector available and found what a 1,925-type agreement missed (S301).
  6. Vtables hold thunks. Searching for a function's VA finds nothing; scan for jmp rel32 to it, then search for the thunk (S294).