Zdexby TheHolyOneZ

The Zdex dump format

One JSON file per game build, schema_version 1, 2, 3 or 4. Zircon writes it natively and is the reference implementation, but Zdex indexes anything that follows this shape — if your dumper can emit it, its dumps belong here too. Since schema 2 the same file carries either an Unreal Engine or a Unity IL2CPP dump; header.runtime says which. Schema 3 adds a way to say a dump was read two different ways and merged, and to record where the two readings disagreed. Schema 4 adds Unity’s other scripting backend, Mono, with the per-assembly version and build id that come with it.

In one paragraph
  • A UTF-8 JSON object: a header (tool, source process, engine guess, engine offsets, globals) and a packages[] array.
  • Every class, struct and enum has a path such as /Script/Engine.Actor. Paths are the identity; every cross-reference (super class, interfaces, member types, parameter types) is a path.
  • Sizes and offsets are bytes. A property's size is its total size (element size × array_dim). Addresses are module-relative hex strings.
  • Unknown keys are ignored, so a dumper can add its own fields. Missing optional keys are fine. Only the keys marked required are required.

Top level

{
  "schema_version": 4,
  "header": {
    "tool_version": "0.7.0",
    "runtime": "unreal",
    "created_utc": "2026-09-15T10:21:04Z",
    "source":  { "kind": "external", "process": "Game-Win64-Shipping.exe", "main_module": "Game-Win64-Shipping.exe",
                 "module_base": "0x7ff64be00000", "image_size": 171896832 },
    "engine":  { "version": "5.6", "confidence": 0.6, "evidence": ["version string: ++UE5+Release-5.6"] },
    "offsets": [ { "name": "UObject.ClassPrivate", "value": 16 }, … ],
    "globals": [ "GObjects=0x98670c0", "FNamePool=0x9783590" ],
    "sources": [ "live", "static" ],
    "conflicts": [ { "path": "UnityEngine.Vector3, UnityEngine.CoreModule", "field": "token",
                     "live": "0x20000e2", "other": "0x20000e3", "used": "live" } ]
  },
  "packages": [
    { "name": "/Script/Engine", "classes": [ … ], "structs": [ … ], "enums": [ … ] }
  ]
}
KeyRequiredMeaning
schema_versionyesInteger, 1, 2, 3 or 4. Each version is the one before it plus the keys marked (2), (3) or (4) below; every one of them has a default that means "this dump does not say", so an older reader can ignore them and a newer file with none of them set means the same thing as an older one.
header.runtime (2)no"unreal" (the default), "il2cpp", or "mono" (4). Decides how the dump is labelled, which downloads are offered, and how type names are rendered. Zdex reads it from the first 64 KB, so it belongs in the header.
header.tool_versionnoFree text naming the dumper and version. Shown on the dump page.
header.created_utcnoISO-8601 UTC timestamp of the dump. Becomes the build's "dumped" date.
header.sourcenokind is one of internal, external, dump, static; process, main_module, module_base (hex string), image_size (bytes).
header.enginenoversion such as "5.6", confidence 0–1, evidence[] of human-readable strings. An IL2CPP dump has no engine version; its evidence[] records how the runtime's structures were derived.
header.offsets[]noDerived engine offsets {name, value}, for example UObject.ClassPrivate. Negative values mean "not found" and are dropped.
header.globals[]noStrings of the form NAME=0xHEX, module-relative.
header.sources[] (3)noWhich readings produced this dump: "live", "static", or both. A Unity game can be read by injecting and asking the runtime, by solving global-metadata.dat off disk with no process at all, or both at once and merged. Absent or one entry is an ordinary single-reading dump.
header.conflicts[] (3)noWhere two readings of the same build disagreed: {path, member, field, live, other, used}. Written in full and never pruned — on a packed or obfuscated build the disagreement is the finding, so it is recorded rather than quietly resolved. used names the side the dump carries.
names[]noOptional FName pool. Ignored on import.
packages[]yesEach with name (a package path, or an assembly file name such as Assembly-CSharp.dll) and optional classes[], structs[], enums[].
packages[].assembly_version (4)noThe assembly’s own version, e.g. "1.0.0.0". Mono dumps only; absent on Unreal and IL2CPP.
packages[].mvid (4)noThe assembly’s Module Version ID. The CLI writes a fresh one on every build, so it identifies a build exactly. Mono dumps only.

Classes and structs

{
  "name": "Actor", "path": "/Script/Engine.Actor", "cpp_prefix": "A",
  "super": "/Script/CoreUObject.Object",
  "size": 680, "alignment": 8, "inherited_size": 40, "vtable_rva": "0x7d655e8",
  "interfaces": [ "/Script/Engine.Interface_AssetUserData" ],
  "properties": [ … ], "functions": [ … ]
}
KeyRequiredMeaning
name, pathyesShort name and the full object path. The path is the identity across the whole file and across builds.
cpp_prefixnoU, A, F, E, I… Used to build the C++ name (AActor). Defaults by kind when missing.
supernoPath of the parent class or struct.
size, alignment, inherited_sizenoBytes. inherited_size is where this type's own members start (aligned).
vtable_rvanoClasses only, hex string, module-relative.
interfaces[]noPaths of implemented interfaces.
properties[], functions[]noSee below.

Schema 2 keys on a class or struct

KeyRequiredMeaning
namespacenoThe C# namespace, kept apart from path because splitting UnityEngine.UI.Button back apart at the dots is guesswork.
is_valuetype, is_interface, is_abstract, is_genericnoWhat the runtime says the type is. is_generic marks an open definition — List`1 rather than List<int> — whose field offsets are not answerable.
explicit_layoutnoThe type placed its own fields ([StructLayout(LayoutKind.Explicit)]), so members are allowed to overlap.
tokennoMetadata token.
source (3)no"live", "static" or "both" — which reading this type came from in a merged dump. A generic instantiation only exists once something has run, so it is live-only; a type nothing ever touched is never in the runtime's class cache, so it is static-only. Absent on a single-reading dump.
accessors[]noC# properties: {name, type, getter, setter, flags}, the getter and setter being method names in the same type. Unreal has no equivalent.

On an IL2CPP dump, path is the assembly-qualified name — UnityEngine.Vector3, UnityEngine.CoreModule — because a C# name is unique only inside its assembly, and every assembly declares a <Module>. It is the same job /Script/Engine does at the front of an Unreal path.

Properties

{ "name": "bHidden", "type": { "kind": "bool", "raw": "BoolProperty", "size": 1 },
  "offset": 88, "size": 1, "is_bitfield": true, "bit_index": 7, "byte_mask": 128, "field_mask": 128,
  "flags": 545460846597, "flag_names": ["Edit", "BlueprintVisible", "Net"], "default": "false" }
KeyRequiredMeaning
name, type, sizeyestype is a type reference (below). size is the total size in bytes: element size × array_dim.
offsetnoByte offset inside the owning type, measured the same way the type's size is. Absent means 0. Members marked offset_unresolved are listed but not placed in the layout.
boxed_offset (2)noIL2CPP value types only: the offset the runtime reported, which is measured from the start of a boxed object and so includes the object header. offset is that minus the header. Both are kept; neither is inferred from the other by a reader.
is_static (2)noThe field has storage per type rather than per instance, so its offset is into a different block and is not part of the instance layout.
offset_unresolved (2)noNo offset is known and none has been invented: an open generic's fields, a const, a thread-static.
array_dimnoStatic array element count, only when greater than 1.
is_bitfield, bit_index, byte_mask, field_masknoFor bool bitfields sharing one byte.
flags, flag_names[]noRaw EPropertyFlags as an integer and the decoded names.
defaultnoDefault value as a string, taken from the class default object.

Type references

Recursive. kind is one of bool int8 uint8 int16 uint16 int32 uint32 int64 uint64 float double string name text struct enum objectptr classptr weakptr softptr softclassptr lazyptr interface fieldpath delegate multicast_delegate array set map optional unknown. raw is the engine's property class name (StructProperty). name is the referenced path for struct/enum/object kinds. params[] holds inner types: array and set have one, map has key and value, enum may carry its underlying integer type.

{ "kind": "map", "raw": "MapProperty", "size": 80,
  "params": [ { "kind": "name", "raw": "NameProperty", "size": 8 },
              { "kind": "struct", "raw": "StructProperty", "size": 24, "name": "/Script/CoreUObject.Vector" } ] }

Functions and parameters

{ "name": "SetActorHiddenInGame", "flags": 1409286144, "flag_names": ["Final", "Native", "Public", "BlueprintCallable"],
  "native_rva": "0x1b2c4f0",
  "params": [ { "name": "bNewHidden", "type": { "kind": "bool", "raw": "BoolProperty", "size": 1 }, "size": 1, "offset": 0 } ],
  "script": [ { "offset": 0, "text": "…" } ], "script_size": 42, "script_complete": true }
KeyRequiredMeaning
nameyesFunction name inside its class.
native_rvanoHex string, module-relative address of the native implementation. On IL2CPP it is relative to GameAssembly.dll.
il_rva (4)noHex string, where the IL body sits in the assembly. Mono compiles a method the first time it is called, so there is no stable native address the way there is on IL2CPP — the IL is what does not move.
token (2)noMetadata token.
shared_body (2)noMore than one method lives at native_rva — a never-referenced method compiled to a shared stub, or identical bodies folded by the linker. Counted from the dump, not assumed.
flags, flag_names[]noEFunctionFlags raw and decoded.
params[]noEach with name, type, size, optional offset, is_out, is_const, is_return. There is no separate return field: the parameter with is_return: true is the return value.
script[], script_size, script_completenoDecompiled Blueprint bytecode as {offset, text} lines; script_complete: false when decoding stopped early.

Enums

{ "name": "ENetRole", "path": "/Script/Engine.ENetRole", "underlying": "uint8",
  "values": [ { "name": "ENetRole::ROLE_None", "value": 0 }, { "name": "ENetRole::ROLE_Authority", "value": 3 } ] }

underlying defaults to uint8. Values wider than the underlying type are widened on import; negative values in unsigned enums are wrapped. values_resolved: false (2) means the member names were recovered but their values were not, and the numbers should not be trusted — numbering by position would look right and be wrong for every enum that assigns its own values.

Rules of thumb for other dumpers

  • Emit paths exactly as the engine reports them (GetPathName()); Zdex diffs builds by path.
  • Sizes are bytes, offsets are bytes, addresses are hex strings relative to module_base.
  • Leave out what you do not know rather than guessing; every key except the ones marked required is optional.
  • Gzip the file before uploading. The first 64 KB must contain "schema_version" and "header" — write the header first. An IL2CPP dump can run to several hundred megabytes uncompressed and twenty compressed; the upload limit applies to what is sent.
  • Test with the API: POST /upload/init → chunks → finish; a rejected file comes back with a message naming what is missing.

Something in the format holding you back? Say so through the contact form; the schema grows by adding keys, never by breaking existing ones.