Final-guarantee fallback for the find-CEntitySystem_Init-decompiles preprocessor. Recovers the struct members, indirect virtual-function offsets, and de-inlined helpers that CEntitySystem::Init sets up, by decompiling CEntitySystem_Init in CS2 server.dll / libserver.so and following de-inlined callees when a target is no longer accessed directly. Use this skill when the deterministic/LLM preprocessor (ida_preprocessor_scripts/find-CEntitySystem_Init-decompiles.py) could not resolve every targ...
Scanned 9/27/2026
Install to Claude Code
npx -y skills add mrc4tt/CS2_VibeSignatures --skill find-CEntitySystem_Init-decompiles --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Find CEntitySystem Init Decompiles?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/mrc4tt-find-centitysystem-init-decompiles)More formats (shields.io, HTML) on the badges page.
---
name: find-CEntitySystem_Init-decompiles
description: |
Final-guarantee fallback for the find-CEntitySystem_Init-decompiles preprocessor. Recovers the struct
members, indirect virtual-function offsets, and de-inlined helpers that CEntitySystem::Init sets up, by
decompiling CEntitySystem_Init in CS2 server.dll / libserver.so and following de-inlined callees when a
target is no longer accessed directly. Use this skill when the deterministic/LLM preprocessor
(ida_preprocessor_scripts/find-CEntitySystem_Init-decompiles.py) could not resolve every target because a
member or vfunc was inlined or de-inlined in a way the LLM_DECOMPILE references do not cover.
Trigger: CEntitySystem_m_sEntSystemName, CEntitySystem_m_eNetworkSerializationMode, CEntitySystem_m_Symbols,
CEntitySystem_m_ComponentUnserializerInfoAllocator, CEntitySystem_m_pNetworkFieldChangedEventQueue,
CEntitySystem_m_pNetworkFieldScratchData, CEntitySystem_m_pFieldChangeLimitSpew,
CEntitySystem_m_EntityMaterialAttributes, INetworkMessages_SetNetworkSerializationContextData,
IFlattenedSerializers_CreateFieldChangedEventQueue, CEntitySystem_ProcessEntityRegistration
disable-model-invocation: true
---
# Find CEntitySystem_Init-decompiles (final-guarantee fallback)
Recover every symbol that `CEntitySystem::Init` wires up, in CS2 `server.dll` / `libserver.so`, using IDA Pro
MCP tools. This is the **Agent fallback** for the `find-CEntitySystem_Init-decompiles` skill: it only runs when
the preprocessor script returned failure, which almost always means one target's access pattern **moved** —
either it was inlined into `CEntitySystem_Init` where the reference expected a separate function, or it was
de-inlined out of `CEntitySystem_Init` into a helper the reference does not know about.
Your job is to produce the missing output YAMLs regardless of that inline/de-inline boundary.
## Realworld Function References
Read the platform-relevant real-world YAMLs before searching in IDA. They provide concrete disassembly,
decompiler output, semantic anchors, and both sides of the known inline/de-inline boundary. Treat their
addresses and offsets as reference-build values only; verify every result against the current binary.
- Windows inline baseline: `ida_preprocessor_scripts/references/server/CEntitySystem_Init.windows.yaml`
- Linux baseline: `ida_preprocessor_scripts/references/server/CEntitySystem_Init.linux.yaml`
- Windows de-inlined `CEntitySystem_Init` variant:
`ida_preprocessor_scripts/references/server/CEntitySystem_Init-noinline.windows.yaml`
- Windows de-inlined material helper:
`ida_preprocessor_scripts/references/server/CEntitySystem_InitEntityMaterialAttributes.windows.yaml`
- Linux registration helper:
`ida_preprocessor_scripts/references/server/CEntitySystem_ProcessEntityRegistration.linux.yaml`
## Background — what CEntitySystem_Init does
`CEntitySystem_Init(this, const char *name, int a3, int mode, char a5)` is a long constructor-style routine.
Along the way it:
- copies the system name into `this->m_sEntSystemName`;
- stores the serialization `mode` into `this->m_eNetworkSerializationMode`;
- initializes `this->m_ComponentUnserializerInfoAllocator` (a `CUtlScratchMemoryPool`);
- registers all entity/component classes;
- calls `g_pNetworkMessages->SetNetworkSerializationContextData("string_t_table", m_eNetworkSerializationMode, &m_Symbols)`
through the **`INetworkMessages` vtable** (an indirect virtual call);
- when `m_eNetworkSerializationMode` is set, allocates a `CNetworkFieldScratchData` into
`this->m_pNetworkFieldScratchData` and calls
`g_pFlattenedSerializers->CreateFieldChangedEventQueue(m_pNetworkFieldScratchData, m_pFieldChangeLimitSpew)`
through the **`IFlattenedSerializers` vtable**, storing the result into `this->m_pNetworkFieldChangedEventQueue`;
- (Linux) tail-calls the de-inlined `CEntitySystem_ProcessEntityRegistration`;
- (Windows) runs the entity-material-attributes registration loop that touches `this->m_EntityMaterialAttributes`.
All member accesses are **relative to `this`** (the first argument — `rcx` on Windows, `rdi` on Linux — usually
copied to `rsi`/`rbx`). This is the key to robustness: `this + offset` is stable whether the access sits in
`CEntitySystem_Init` itself or in a helper that received `this` as its first argument.
## Robustness principle — follow the de-inline boundary
For **every** target below:
1. First look for its access pattern **inside `CEntitySystem_Init`**.
2. If it is not there, it has been **de-inlined into a callee**. Enumerate the functions that
`CEntitySystem_Init` calls (read the pseudocode / disassembly and collect each `sub_*` / named `call`
target), decompile the plausible ones, and search there. The owning helper takes `this` (the
`CEntitySystem *`) as its first parameter, so the same `this + offset` pattern appears. Recurse one or two
levels if needed.
3. Conversely, a target the reference expected in a **separate function** (e.g. Linux
`CEntitySystem_ProcessEntityRegistration`, or Windows `m_EntityMaterialAttributes`) may have been **inlined
back into `CEntitySystem_Init`** — in that case find the pattern directly in `CEntitySystem_Init`.
Anchor each target by its **semantic fingerprint** (the strings, constants, and neighboring calls listed
below), not by a fixed address or a fixed containing function.
## Output inventory
`struct_name` is always `CEntitySystem`. Offsets are **reference values from the 14168 build — verify against
the binary, do not assume**; they change across updates and differ per platform.
| # | Output symbol | Kind | Windows | Linux | Writer skill |
|---|---------------|------|---------|-------|--------------|
| 1 | `CEntitySystem_m_sEntSystemName` | struct member | `0xA88` | `0xA88` | `/write-structoffset-as-yaml` |
| 2 | `CEntitySystem_m_eNetworkSerializationMode` | struct member | `0xBBC` | `0xBBC` | `/write-structoffset-as-yaml` |
| 3 | `CEntitySystem_m_ComponentUnserializerInfoAllocator` | struct member | `0xCB8` | `0xCC0` | `/write-structoffset-as-yaml` |
| 4 | `CEntitySystem_m_Symbols` | struct member | `0x1EC8` | `0x1ED0` | `/write-structoffset-as-yaml` |
| 5 | `CEntitySystem_m_pNetworkFieldChangedEventQueue` | struct member | `0xC80` | `0xC88` | `/write-structoffset-as-yaml` |
| 6 | `CEntitySystem_m_pNetworkFieldScratchData` | struct member | `0xC88` | `0xC90` | `/write-structoffset-as-yaml` |
| 7 | `CEntitySystem_m_pFieldChangeLimitSpew` | struct member | `0xC90` | `0xC98` | `/write-structoffset-as-yaml` |
| 8 | `INetworkMessages_SetNetworkSerializationContextData` | indirect vcall | vtable `INetworkMessages`, offset `0xA0`, index `20` | same | `/write-vfunc-as-yaml` |
| 9 | `IFlattenedSerializers_CreateFieldChangedEventQueue` | indirect vcall | vtable `IFlattenedSerializers`, offset `0x118`, index `35` | same | `/write-vfunc-as-yaml` |
| 10 | `CEntitySystem_ProcessEntityRegistration` | function | *inlined — do NOT emit* | separate func (`func_sig`) | `/write-func-as-yaml` |
| 11 | `CEntitySystem_m_EntityMaterialAttributes` | struct member | `0x2070` | *inlined — do NOT emit* | `/write-structoffset-as-yaml` |
Platform gating: symbols 1–9 are produced on **both** platforms. Symbol 10 is **Linux-only** (on Windows the
routine is inlined into `CEntitySystem_Init`). Symbol 11 is **Windows-only** (on Linux the access lives inside
`CEntitySystem_ProcessEntityRegistration`, symbol 10).
## Step 0. Skip targets already produced
Some outputs may already exist beside the binary — written by the preprocessor before it failed, or by the
`find-CEntitySystem_InitEntityMaterialAttributes` / `find-CEntitySystem_m_EntityMaterialAttributes` fallback
pair that runs before this skill. For each output, if `<name>.<platform>.yaml` already exists next to the
binary and parses to a non-empty mapping, **skip it** and spend effort only on the missing ones. You can list
the binary directory with:
```
mcp__ida-pro-mcp__py_eval code="import idaapi, os; d=os.path.dirname(idaapi.get_input_file_path()); print('\n'.join(sorted(f for f in os.listdir(d) if f.endswith('.yaml'))))"
```
`/get-func-from-yaml` also reports existence for functions/vfuncs (returns an error when absent).
## Step 1. Load and decompile the predecessor
**ALWAYS** Use SKILL `/get-func-from-yaml` with `func_name=CEntitySystem_Init` to obtain its `func_va`.
If the skill returns an error, **STOP** and report to user (this fallback cannot run without the predecessor).
Decompile it, and keep the list of functions it calls for the de-inline search in later steps:
```
mcp__ida-pro-mcp__decompile addr="<CEntitySystem_Init.func_va>"
```
Confirm the `this` register (first argument) and note where it is copied (typically `rsi` on Windows, `rbx` on
Linux). Every struct-member offset below is `this + offset`.
## Step 2. Resolve the struct members
For each member: locate the access, read the **displacement** in the instruction (that is the offset), confirm
it is relative to `this` (the `CEntitySystem *`), and confirm `struct_name = CEntitySystem`. Then generate a
signature and write the YAML (Step 5).
### 2a. `m_sEntSystemName`
The very first meaningful call in `CEntitySystem_Init`:
```c
CUtlString::Set((CUtlString *)(this + 0xA88), name); // name == arg a2
```
Anchor: the `lea`/`add` that forms `this + off` immediately before the `CUtlString::Set` call whose second
argument is the incoming name string. `off` is `m_sEntSystemName` (ref `0xA88`).
### 2b. `m_eNetworkSerializationMode`
The **int/enum** mode stored from the `mode` parameter (`a4`) near the top:
```c
*(_DWORD *)(this + 0xBBC) = mode; // ref 0xBBC
```
It is read again later as the middle argument to the `SetNetworkSerializationContextData` vcall (Step 3a),
right next to the `"string_t_table"` string — that read site is an equally good anchor.
> Decoy: a **byte** flag `*(_BYTE *)(this + 0xBDA) = (a3 == 1 && g_pNetworkMessages != nullptr)` is set a few
> instructions away. That `0xBDA` boolean is **not** `m_eNetworkSerializationMode` — the target is the DWORD at
> `0xBBC`. Do not confuse them.
### 2c. `m_ComponentUnserializerInfoAllocator`
The `CUtlScratchMemoryPool` initialized near the top:
```c
CUtlScratchMemoryPool::Init((CUtlScratchMemoryPool *)(this + 0xCB8/*win*/), 0x400, 0, nullptr, 0);
```
Anchor: the `lea rcx/rdi, [this + off]` feeding the `CUtlScratchMemoryPool::Init` call whose first immediate is
`0x400`. `off` is `m_ComponentUnserializerInfoAllocator` (ref `0xCB8` Windows / `0xCC0` Linux).
### 2d. `m_Symbols`
The last argument to the `SetNetworkSerializationContextData` vcall (Step 3a):
```c
g_pNetworkMessages->SetNetworkSerializationContextData("string_t_table", m_eNetworkSerializationMode, this + 0x1EC8/*win*/);
```
Anchor: the `lea` that forms `this + off` as the third argument, in the block that also loads
`"string_t_table"`. `off` is `m_Symbols` (ref `0x1EC8` Windows / `0x1ED0` Linux).
### 2e. The field-change trio — `m_pNetworkFieldScratchData`, `m_pFieldChangeLimitSpew`, `m_pNetworkFieldChangedEventQueue`
All three live in the `if (m_eNetworkSerializationMode)` block, around the `CreateFieldChangedEventQueue` vcall
(Step 3b):
```c
// m_pNetworkFieldScratchData: object from operator new(0x58) with a CNetworkFieldScratchData vtable,
// stored at this + off, and passed as the 1st data argument to CreateFieldChangedEventQueue.
*(_QWORD *)(this + 0xC88/*win*/) = scratch; // ref 0xC88 win / 0xC90 linux
m_pNetworkFieldChangedEventQueue = // stored from the vcall's return value
g_pFlattenedSerializers->CreateFieldChangedEventQueue(
m_pNetworkFieldScratchData, // this + 0xC88 win / 0xC90 linux
m_pFieldChangeLimitSpew); // this + 0xC90 win / 0xC98 linux
*(_QWORD *)(this + 0xC80/*win*/) = <return value>; // m_pNetworkFieldChangedEventQueue, ref 0xC80 win / 0xC88 linux
```
Distinguish them by role:
- `m_pNetworkFieldScratchData` — the object **written** just above the call (the `operator new(0x58)` result with
a vtable) and read as the vcall's **second** argument (`rdx`/2nd data reg). Ref `0xC88` win / `0xC90` linux.
- `m_pFieldChangeLimitSpew` — read as the vcall's **third** argument (`r8`/3rd data reg). Ref `0xC90` win /
`0xC98` linux.
- `m_pNetworkFieldChangedEventQueue` — the member the vcall's **return value** is stored into. Ref `0xC80` win /
`0xC88` linux.
> These three signatures may need to span a function boundary (the block is easily de-inlined). If
> `/generate-signature-for-structoffset` cannot find a unique signature, it is acceptable to write the member
> with the **offset only** (omit `offset_sig`) — the offset is the required output.
## Step 3. Resolve the indirect virtual-call offsets
Symbols 8 and 9 are **indirect virtual calls**, not concrete functions. The YAML records the interface vtable
and slot, and a `vfunc_sig` that pins the call instruction — there is **no `func_va`**. Do not try to resolve a
concrete implementation address.
For each: find the `call qword ptr [reg + disp]` instruction, take `vfunc_offset = disp` and
`vfunc_index = disp / 8`.
### 3a. `INetworkMessages_SetNetworkSerializationContextData`
- Call site: `call qword ptr [reg + 0xA0]` where `reg` holds `g_pNetworkMessages` (type `INetworkMessages *`),
in the block that loads the `"string_t_table"` string and passes `m_eNetworkSerializationMode` and
`&m_Symbols`.
- `vtable_name = INetworkMessages`, `vfunc_offset = 0xA0` (ref), `vfunc_index = 20`.
### 3b. `IFlattenedSerializers_CreateFieldChangedEventQueue`
- Call site: `call qword ptr [reg + 0x118]` where `reg` holds `g_pFlattenedSerializers`
(type `IFlattenedSerializers *`), passing `m_pNetworkFieldScratchData` and `m_pFieldChangeLimitSpew`, with the
return stored into `m_pNetworkFieldChangedEventQueue`.
- `vtable_name = IFlattenedSerializers`, `vfunc_offset = 0x118` (ref), `vfunc_index = 35`.
`g_pNetworkMessages` and `g_pFlattenedSerializers` are already-named globals in the database; use their type to
confirm the interface, and confirm the semantic role from the neighboring arguments described above. If the
whole call has been de-inlined into a helper, follow the callees (Robustness principle) — the `[reg + disp]`
instruction and its argument setup move together.
## Step 4. Platform-specific de-inlined targets
### 4a. Linux only — `CEntitySystem_ProcessEntityRegistration` (symbol 10)
On Linux, `CEntitySystem_Init` tail-calls this de-inlined helper near its end (after the class-table loop):
```c
CEntitySystem_ProcessEntityRegistration(this); // last call before Init returns
```
Anchor: the last `call` in `CEntitySystem_Init` that takes only `this`. Decompile it to confirm it performs the
entity-material-attributes registration (the FNV-1a hashing loop over material names — constants
`0x811C9DC5` and `0x1000193`). Rename it to `CEntitySystem_ProcessEntityRegistration` with
`mcp__ida-pro-mcp__rename`, then treat it as a normal function (Step 5c).
> If (on some future Windows build) this routine is emitted as a **separate function** too, apply the same
> method on Windows. On the current Windows build it is inlined into `CEntitySystem_Init` — do **not** emit it
> there.
### 4b. Windows only — `m_EntityMaterialAttributes` (symbol 11)
On Windows this member is accessed inside the material-attributes loop, whether that loop is inlined in
`CEntitySystem_Init` or de-inlined into `CEntitySystem_InitEntityMaterialAttributes` (a helper taking `this`):
```c
lea reg, [this + 0x2070] // ref 0x2070 = m_EntityMaterialAttributes
```
Anchor: the `lea this + off` used as the map/dictionary base in the loop that FNV-1a-hashes material name
strings (constants `0x811C9DC5`, `0x1000193`; a small default string blob is the hash seed). Follow the callees
of `CEntitySystem_Init` if the loop is not inlined. `off` is `m_EntityMaterialAttributes` (ref `0x2070`).
> On Linux this access lives inside `CEntitySystem_ProcessEntityRegistration` (symbol 10) and is **not** emitted
> as a separate member YAML.
## Step 5. Generate signatures and write the YAMLs
### 5a. Struct members (symbols 1–7, and 11 on Windows)
For each resolved member:
1. **ALWAYS** Use SKILL `/generate-signature-for-structoffset` on the instruction that contains the offset to
obtain `offset_sig` / `offset_sig_disp`. (Best-effort for the field-change trio — see the note in Step 2e.)
2. **ALWAYS** Use SKILL `/write-structoffset-as-yaml` with:
- `struct_name`: `CEntitySystem`
- `member_name`: the member (e.g. `m_sEntSystemName`)
- `offset`: the resolved hex offset
- `size`: `None`
- `offset_sig`: the signature from step 1 (or `None` if not found)
- `offset_sig_disp`: from step 1 (or `None`)
### 5b. Indirect vcalls (symbols 8–9)
For each:
1. **ALWAYS** Use SKILL `/generate-signature-for-vfuncoffset` on the `call qword ptr [reg + offset]` instruction
to obtain `vfunc_sig` (the offset bytes are fixed in the signature; `vfunc_sig_disp` is `0`).
2. **ALWAYS** Use SKILL `/write-vfunc-as-yaml` with:
- `func_name`: e.g. `INetworkMessages_SetNetworkSerializationContextData`
- `vtable_name`: `INetworkMessages` / `IFlattenedSerializers`
- `vfunc_offset`: the resolved offset (`0xA0` / `0x118`)
- `vfunc_index`: `vfunc_offset / 8` (`20` / `35`)
- `vfunc_sig`: from step 1
- `func_addr`: `None` (no concrete implementation address for an interface vcall)
- `func_sig`: `None`
### 5c. Function (symbol 10, Linux only)
1. **ALWAYS** Use SKILL `/generate-signature-for-function` with `addr=<CEntitySystem_ProcessEntityRegistration_addr>`.
2. **ALWAYS** Use SKILL `/write-func-as-yaml` with:
- `func_name`: `CEntitySystem_ProcessEntityRegistration`
- `func_addr`: the resolved address
- `func_sig`: the validated signature from step 1
## Failure handling
- If the **predecessor** `CEntitySystem_Init` YAML is missing → **STOP** and report to user.
- If an individual required target cannot be located even after following callees → resolve the ones you can,
then **STOP** and report exactly which output(s) could not be found, so the user can extend the references.
- Never emit a platform-gated symbol on the wrong platform (symbol 10 Windows, symbol 11 Linux).
## Output YAML filenames
Written beside the binary by the writer skills, one file per symbol:
- Windows (`server.dll`): `<symbol>.windows.yaml`
- Linux (`libserver.so`): `<symbol>.linux.yaml`
e.g. `CEntitySystem_m_Symbols.windows.yaml`, `INetworkMessages_SetNetworkSerializationContextData.linux.yaml`,
`CEntitySystem_ProcessEntityRegistration.linux.yaml`, `CEntitySystem_m_EntityMaterialAttributes.windows.yaml`.
## Why this is robust
- Members are anchored to `this + offset`, so they are recoverable whether the access is inlined in
`CEntitySystem_Init` or de-inlined into a helper that receives `this`.
- Each target has a **semantic fingerprint** (a string, a constant, a neighboring interface call) that survives
offset changes and function-boundary moves across updates.
- The indirect vcalls are pinned by their call-site displacement, which is stable even when the surrounding code
is reorganized.
- Already-produced outputs are skipped, so this fallback composes with the preprocessor and with the
windows de-inline fallback pair instead of fighting them.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!