sots-re/ghidra/addresses.json

2598 lines
No EOL
107 KiB
JSON

{
"_schema": "facts about Sword of the Stars.exe (GOG 1.8.1, build 19833). rva = file address - 0x00400000. Runtime address = GetModuleHandle(NULL) + rva (exe is ASLR-relocated). Prototypes marked 'unverified' are best current reading; a Ghidra round confirms them.",
"image_base": "0x00400000",
"entries": [
{
"name": "Mars_Application_Initialize",
"addr": "0x008a0e50",
"convention": "thiscall",
"prototype": "bool (Application* this, AppStartup* startup) /* ONE stack arg, RET 4, returns AL; EDX unused. startup = {HINSTANCE hInst; std::vector<std::string> args; ...; int nShow; const char* wndClass; int iconId} built in WinMain */",
"status": "verified",
"source": "handoff/loader-prototypes.md#application"
},
{
"name": "Mars_Application_Run",
"addr": "0x0089f5b0",
"convention": "thiscall",
"prototype": "void (Application* this) /* no stack args, plain RET, EDX not an input, EAX = last PumpMessages result (ignore) */",
"status": "verified",
"source": "handoff/loader-prototypes.md#application"
},
{
"name": "Process_PinAffinity",
"addr": "0x0089ee70",
"convention": "cdecl",
"prototype": "void (int coreIndex)",
"status": "unverified",
"source": "findings/objects/ghidra-recon.md"
},
{
"name": "CfgVar_RegisterKey",
"addr": "0x008b76a0",
"convention": "thiscall",
"prototype": "GlobalConst* (GlobalConst* this, void* storage, const char* key, ParseFn parser, const char* file) // RET 0x10",
"status": "verified",
"source": "findings/subsystems/loader-prototypes.md"
},
{
"name": "DemoApp_OnTick",
"addr": "0x0089a640",
"convention": "thiscall",
"prototype": "bool (DemoApp* this) /* no stack args; returns 0 when quitting (this+0x1b2), else 1 */",
"status": "verified",
"source": "handoff/loader-prototypes.md#application"
},
{
"name": "StrategyClient_EndTurn",
"addr": "0x00783be0",
"convention": "thiscall",
"prototype": "void (StrategyClient* this)",
"status": "unverified",
"source": "findings/control-flow/turn-spine.md"
},
{
"name": "StrategyServer_BeginProcessTurn",
"addr": "0x007d98e0",
"convention": "thiscall",
"prototype": "void (StrategyServer* this)",
"status": "unverified",
"source": "findings/control-flow/turn-spine.md"
},
{
"name": "StrategyServer_ProcessTurn",
"addr": "0x007dc6c0",
"convention": "thiscall",
"prototype": "void (StrategyServer* this)",
"status": "unverified",
"source": "findings/control-flow/turn-spine.md"
},
{
"name": "ServerPlayer_ComputeBudget",
"addr": "0x00863030",
"convention": "thiscall",
"prototype": "void (ServerPlayer* this, Budget* out, bool projected) /* Budget = { int slot[22]; std::vector<{Tech* node, int points}> researchAlloc (3 words @+0x58); int overBudget @+0x64 }. The array is 22 ints, not 25: the three words after slot 21 are the allocation vector and grow by one 8-byte element exactly when the player has a research target. Slots: 0 Sav, 1 systemIncome+, 2 trade, 3 shipCarriedPop, 4 secondaryManager, 5 savingsInterest, 6 bonusIncome, 7 systemIncome-, 8 maintenance, 9 researchMoneyKept (written only when ResT is set), 10 debtInterest, 11 construction, 12 expenses, 13 researchMoneyGiven, 14 savingsGiven, 15 available, 16 researchMoney, 17 researchPoints, 18 TRA, 19 researchPointsGiven, 20 TRP, 21 totalResearchPoints */",
"status": "verified-by-trace",
"source": "handoff/formula-gaps.md#q5 + sots-engine docs/B1.md (live trace, 4623 calls)"
},
{
"name": "TechTree_ProcessResearch",
"addr": "0x005876c0",
"convention": "thiscall",
"prototype": "void (TechTree* this, Mars::RNG* rng, std::vector<{TechDef* target, int points}>* alloc, int* overbudget) /* args confirmed at the single call site: rng = StrategyServer+0x16c object, passed as the RNG *object* and re-based to &mt with lea ecx,[rng+4] before each NextFloat. Loop: node = this->nodes[*(int*)entry.target]; lo/hi from Cost x 50/150 /100 (32-bit imul, signed /100); spend = min(points, hi-progress) with NO clamp at 0; *overbudget += points-spend; odds and roll are both narrowed to float32 before the compare; decay loop hits every state-2 node with progress != 0; the trailing unlock-event loop makes no RNG draw and writes no node */",
"status": "verified",
"source": "B3 own disassembly pass 2026-09-08 (objdump/pei-i386): 0x005876c0, 0x0047d830, 0x00426e00, 0x0049fdf0, 0x004271c0, 0x0057da00, 0x008914a5"
},
{
"name": "ServerSystem_Read",
"addr": "0x0075d4b0",
"convention": "thiscall",
"prototype": "void (ServerSystem* this /*+8 IStreamable*/, Stream* s)",
"status": "verified-by-save",
"source": "findings/objects/struct-recovery.md"
},
{
"name": "ServerSystem_Write",
"addr": "0x00749630",
"convention": "thiscall",
"prototype": "void (ServerSystem* this /*+8*/, Stream* s)",
"status": "verified-by-save",
"source": "findings/objects/struct-recovery.md"
},
{
"name": "ServerPlayer_Read",
"addr": "0x008804d0",
"convention": "thiscall",
"prototype": "void (ServerPlayer* this /*+0x3a0*/, Stream* s)",
"status": "verified-by-save",
"source": "findings/objects/struct-recovery.md"
},
{
"name": "ServerPlayer_Write",
"addr": "0x008563e0",
"convention": "thiscall",
"prototype": "void (ServerPlayer* this /*+0x3a0*/, Stream* s)",
"status": "verified-by-save",
"source": "findings/objects/struct-recovery.md"
},
{
"name": "StarFleet_Read",
"addr": "0x00702470",
"convention": "thiscall",
"prototype": "void (StarFleet* this /*+8*/, Stream* s)",
"status": "verified-by-save",
"source": "findings/objects/struct-recovery.md"
},
{
"name": "StarFleet_Write",
"addr": "0x00701070",
"convention": "thiscall",
"prototype": "void (StarFleet* this /*+8*/, Stream* s)",
"status": "verified-by-save",
"source": "findings/objects/struct-recovery.md"
},
{
"name": "StarShip_Read",
"addr": "0x00853fa0",
"convention": "thiscall",
"prototype": "void (StarShip* this /*+8*/, Stream* s)",
"status": "verified-by-save",
"source": "findings/objects/struct-recovery.md"
},
{
"name": "StarShip_Write",
"addr": "0x008291f0",
"convention": "thiscall",
"prototype": "void (StarShip* this /*+8*/, Stream* s)",
"status": "verified-by-save",
"source": "findings/objects/struct-recovery.md"
},
{
"name": "StrategyServer_Read",
"addr": "0x007d27a0",
"convention": "thiscall",
"prototype": "void (StrategyServer* this, Stream* s)",
"status": "verified-by-save",
"source": "findings/objects/struct-recovery.md"
},
{
"name": "StrategyServer_Write",
"addr": "0x0079fa70",
"convention": "thiscall",
"prototype": "void (StrategyServer* this, Stream* s)",
"status": "verified-by-save",
"source": "findings/objects/struct-recovery.md"
},
{
"name": "Stream_WriteNetworkObjectId",
"addr": "0x00816490",
"convention": "thiscall",
"prototype": "void (Stream* this, const char* tag, NetworkObject* obj)",
"status": "unverified",
"source": "findings/objects/struct-recovery.md"
},
{
"name": "RNG_Twist",
"addr": "0x00426e00",
"convention": "thiscall",
"prototype": "void (uint32_t* mtBlock) // ECX = &mt = RNG object + 4 (NOT the object). N=624 M=397 MATRIX_A 0x9908b0df, 227/396 split; ends with left(&mt+0x9c4) = 624 and next(&mt+0x9c0) = &mt[0]. RET 0",
"status": "verified",
"source": "B3 own disassembly pass 2026-09-08 (objdump/pei-i386): 0x005876c0, 0x0047d830, 0x00426e00, 0x0049fdf0, 0x004271c0, 0x0057da00, 0x008914a5"
},
{
"name": "RNG_Seed",
"addr": "0x0049fdf0",
"convention": "thiscall",
"prototype": "RNG* (RNG* this, uint32_t seed) // RET 4; writes vftable 0x009e9aec, seeds from &mt = this+4: mt[0]=seed, mt[i]=0x6c078965*(mt[i-1]^(mt[i-1]>>30))+i for i=1..623, then Twist(&mt). OBJECT layout: {vftable @+0; uint32 mt[624] @+4 .. +0x9c3; uint32* next @+0x9c4; int left @+0x9c8} = 0x9cc bytes. Beware: NextFloat/NextInt/Twist take &mt, so *their* this+0x9c0/+0x9c4 are next/left",
"status": "verified",
"source": "B3 own disassembly pass 2026-09-08 (objdump/pei-i386): 0x005876c0, 0x0047d830, 0x00426e00, 0x0049fdf0, 0x004271c0, 0x0057da00, 0x008914a5"
},
{
"name": "RNG_NextFloat",
"addr": "0x0047d830",
"convention": "thiscall",
"prototype": "float (uint32_t* mtBlock) // ECX = &mt = RNG object + 4. Lazy twist (if left==0 Twist()), y = *next++, left--, standard MT tempering, then value = (double)(uint32)y * 1/(2^32-1) -- the multiplier at 0x009e61b0 is 0x3df0000000001000 = 1/4294967295.0, NOT 2^-32; the unsigned fix-up at 0x009e61b8 is +2^32 after a signed fild. Left in st(0) at the current x87 precision and narrowed by the caller; range [0,1] INCLUSIVE",
"status": "verified",
"source": "B3 own disassembly pass 2026-09-08 (objdump/pei-i386): 0x005876c0, 0x0047d830, 0x00426e00, 0x0049fdf0, 0x004271c0, 0x0057da00, 0x008914a5"
},
{
"name": "RNG_NextInt",
"addr": "0x004271c0",
"convention": "thiscall",
"prototype": "uint32_t (uint32_t* mtBlock, const uint32_t* n) // ECX = &mt; the bound is passed BY POINTER, RET 4. mask = smallest 2^k-1 >= *n via the or/shift cascade; draws (lazy twist + tempering) until (y & mask) <= *n -- note the loop condition is `ja`, so the result is uniform on [0, *n] INCLUSIVE, not [0, n)",
"status": "verified",
"source": "B3 own disassembly pass 2026-09-08 (objdump/pei-i386): 0x005876c0, 0x0047d830, 0x00426e00, 0x0049fdf0, 0x004271c0, 0x0057da00, 0x008914a5"
},
{
"name": "RNG_size",
"offset": "0x9cc",
"convention": "offset",
"prototype": "sizeof(Mars::RNG) -- {vftable @+0; uint32 mt[624] @+4; uint32* next @+0x9c4; int left @+0x9c8}",
"status": "verified",
"source": "B3 own disassembly pass 2026-09-08 (objdump/pei-i386): 0x005876c0, 0x0047d830, 0x00426e00, 0x0049fdf0, 0x004271c0, 0x0057da00, 0x008914a5"
},
{
"name": "RNG_off_State",
"offset": "0x4",
"convention": "offset",
"prototype": "uint32 mt[624]; also the pointer Twist/NextFloat/NextInt receive in ECX",
"status": "verified",
"source": "B3 own disassembly pass 2026-09-08 (objdump/pei-i386): 0x005876c0, 0x0047d830, 0x00426e00, 0x0049fdf0, 0x004271c0, 0x0057da00, 0x008914a5"
},
{
"name": "RNG_off_Next",
"offset": "0x9c4",
"convention": "offset",
"prototype": "uint32* next; always &mt[624 - left], and recomputed from left on load, so `left` alone pins the stream position",
"status": "verified",
"source": "B3 own disassembly pass 2026-09-08 (objdump/pei-i386): 0x005876c0, 0x0047d830, 0x00426e00, 0x0049fdf0, 0x004271c0, 0x0057da00, 0x008914a5"
},
{
"name": "RNG_off_Left",
"offset": "0x9c8",
"convention": "offset",
"prototype": "int left; words still unread in the current block",
"status": "verified",
"source": "B3 own disassembly pass 2026-09-08 (objdump/pei-i386): 0x005876c0, 0x0047d830, 0x00426e00, 0x0049fdf0, 0x004271c0, 0x0057da00, 0x008914a5"
},
{
"name": "TechDef_off_TechId",
"offset": "0x0",
"convention": "offset",
"prototype": "int tech id -- the index into TechTree_off_Nodes",
"status": "verified",
"source": "B3 own disassembly pass 2026-09-08 (objdump/pei-i386): 0x005876c0, 0x0047d830, 0x00426e00, 0x0049fdf0, 0x004271c0, 0x0057da00, 0x008914a5"
},
{
"name": "ResearchAlloc_stride",
"offset": "0x8",
"convention": "offset",
"prototype": "sizeof({TechDef* target, int points}) -- the element of the vector ProcessResearch walks",
"status": "verified",
"source": "B3 own disassembly pass 2026-09-08 (objdump/pei-i386): 0x005876c0, 0x0047d830, 0x00426e00, 0x0049fdf0, 0x004271c0, 0x0057da00, 0x008914a5"
},
{
"name": "Mars_Application_MountModules",
"addr": "0x008a0a20",
"convention": "cdecl",
"prototype": "void (void) /* sots.ini [Modules] Mount<N> -> gobio::Init */",
"status": "verified",
"source": "handoff/loader-prototypes.md#m4"
},
{
"name": "Game_GetDefaultMounts",
"addr": "0x008991c0",
"convention": "cdecl",
"prototype": "void (int* count, const char*** table) /* count 3 @0x00b2d514, table @0x00a35e98 */",
"status": "verified",
"source": "handoff/loader-prototypes.md#m4"
},
{
"name": "GlobalConst_ctor",
"addr": "0x008b76a0",
"convention": "thiscall",
"prototype": "GlobalConst* (GlobalConst* this, void* storage, const char* key, GlobalConstParseFn parser, const char* file) /* RET 0x10; static-init stub call. GlobalConst 0x10 bytes: {+0 void* storage (int/float/float[4]), +4 const char* key, +8 parser, +c const char* file} */",
"status": "verified",
"source": "handoff/loader-prototypes.md#m1"
},
{
"name": "GlobalConst_Register",
"addr": "0x008b7610",
"convention": "cdecl",
"prototype": "void (GlobalConst* g)",
"status": "verified",
"source": "handoff/loader-prototypes.md#m1"
},
{
"name": "GlobalConst_ParseInt",
"addr": "0x008b7000",
"convention": "cdecl",
"prototype": "void (int* storage, const char* text)",
"status": "verified",
"source": "handoff/loader-prototypes.md#m1"
},
{
"name": "GlobalConst_ParseFloat",
"addr": "0x008b7020",
"convention": "cdecl",
"prototype": "void (float* storage, const char* text)",
"status": "verified",
"source": "handoff/loader-prototypes.md#m1"
},
{
"name": "GlobalConst_ParseFloatScaled",
"addr": "0x008b70e0",
"convention": "cdecl",
"prototype": "void (float* storage, const char* text) /* sscanf(%f) into *storage, then *storage = (float)((double)*storage * *(double*)0x00af5210) */",
"status": "verified",
"source": "handoff/loader-prototypes.md#m1"
},
{
"name": "GlobalConst_ParseColour",
"addr": "0x008b7110",
"convention": "cdecl",
"prototype": "void (float rgba[4], const char* text) /* '%d %d %d %d', defaults 255, /255, clamp 0..1 */",
"status": "verified",
"source": "handoff/loader-prototypes.md#m1"
},
{
"name": "GlobalConst_ParseRect",
"addr": "0x008b7040",
"convention": "cdecl",
"prototype": "void (int rect[4], const char* text) /* '%d %d %d %d' (x y w h); 2 keys (COMBATSETUP_CARD_*_RECT); layout inferred from the M1 trace, int vs float unconfirmed */",
"status": "verified-by-trace",
"source": "sots-engine docs/M1.md"
},
{
"name": "GlobalConst_ParseVec3",
"addr": "0x008b7080",
"convention": "cdecl",
"prototype": "void (float xyz[3], const char* text) /* '%f %f %f'; 5 keys (HIVER_SPAWN_*_GATE_HEIGHT / _SHIP_OFFSET ...); inferred from the M1 trace */",
"status": "verified-by-trace",
"source": "sots-engine docs/M1.md"
},
{
"name": "GlobalConst_ParseString",
"addr": "0x008b7670",
"convention": "cdecl",
"prototype": "void (std::string* storage, const char* text) /* *storage = text; MSVC2010 std::string 0x18 bytes {+0 char buf[16] | char* ptr when capacity > 15, +0x10 size, +0x14 capacity}; 146 keys (*_NAME, *_SOUND, *_TEXTURENAME, DERELICT_SECTION_nn ...); seen in the M1 trace */",
"status": "verified-by-trace",
"source": "sots-engine docs/M1.md"
},
{
"name": "GlobalConsts_LoadAll",
"addr": "0x008b76d0",
"convention": "cdecl",
"prototype": "void (void) /* called once from Application::Initialize; sets g_GlobalConstsLoaded */",
"status": "verified",
"source": "handoff/loader-prototypes.md#m1"
},
{
"name": "GlobalConsts_LoadFile",
"addr": "0x008b73c0",
"convention": "cdecl",
"prototype": "void (const char* file, GlobalConstMap* consts) /* consts = std::map<const char*,GlobalConst*,stricmp> of the keys registered for this file; MSVC2010 layout: map {+4 head node, +8 size}; node {+0 left, +4 parent, +8 right, +0xc const char* key, +0x10 GlobalConst*, +0x14 color, +0x15 isnil}; head: isnil=1, parent=root. Consumed keys are erased from the map. Hook this for the file-level read */",
"status": "verified",
"source": "handoff/loader-prototypes.md#m1"
},
{
"name": "g_GlobalConstsLoaded",
"addr": "0x00b2d740",
"convention": "data",
"prototype": "bool",
"status": "verified",
"source": "handoff/loader-prototypes.md#m1"
},
{
"name": "g_GlobalConstRegistry",
"addr": "0x00b2d744",
"convention": "data",
"prototype": "std::map<const char*,GlobalConst*,stricmp_less> /* +4 (0x00b2d748) = header node pointer */",
"status": "verified",
"source": "handoff/loader-prototypes.md#m1"
},
{
"name": "g_GlobalConstFloatScale",
"addr": "0x00af5210",
"convention": "data",
"prototype": "double /* pi/180 as a DOUBLE (0x3f91df46a2529d39); GlobalConst_ParseFloatScaled does float word *= this in x87 and stores float */",
"status": "verified",
"source": "handoff/loader-prototypes.md#m1"
},
{
"name": "Script_ctor",
"addr": "0x008cd700",
"convention": "thiscall",
"prototype": "Script* (Script* this) /* sizeof(Script) = 0x28 */",
"status": "verified",
"source": "handoff/loader-prototypes.md#m3"
},
{
"name": "Script_dtor",
"addr": "0x008cd720",
"convention": "thiscall",
"prototype": "void (Script* this)",
"status": "verified",
"source": "handoff/loader-prototypes.md#m3"
},
{
"name": "Script_Open",
"addr": "0x008cd7d0",
"convention": "thiscall",
"prototype": "bool (Script* this, const char* path) /* gobio::ReadFile into this->buf */",
"status": "verified",
"source": "handoff/loader-prototypes.md#m3"
},
{
"name": "Script_Close",
"addr": "0x008cd6c0",
"convention": "thiscall",
"prototype": "void (Script* this)",
"status": "verified",
"source": "handoff/loader-prototypes.md#m3"
},
{
"name": "Script_ReadToken",
"addr": "0x008cd2f0",
"convention": "thiscall",
"prototype": "int (Script* this, char* out, uint outMax) /* 0 token, 1 no buffer/past end, 2 reached end */",
"status": "verified",
"source": "handoff/loader-prototypes.md#m3"
},
{
"name": "Script_Next",
"addr": "0x008cd3e0",
"convention": "thiscall",
"prototype": "int (Script* this, ScriptToken* tok) /* tok: {int type; char key[1024]; char value[1024]}; type 1 kv, 2 block open, 3 '}' */",
"status": "verified",
"source": "handoff/loader-prototypes.md#m3"
},
{
"name": "Script_SkipBlock",
"addr": "0x008cd4b0",
"convention": "thiscall",
"prototype": "int (Script* this, int depth) /* 0 ok, nonzero EOF */",
"status": "verified",
"source": "handoff/loader-prototypes.md#m3"
},
{
"name": "Script_ScanToken",
"addr": "0x008cd1f0",
"convention": "custom",
"prototype": "/* EAX=cursor, ECX=&quotedFlag, EDX=out; stack: end, outEnd, &len \u2014 internal, do not hook */",
"status": "verified",
"source": "handoff/loader-prototypes.md#m3"
},
{
"name": "ConfigParser_ParseFile",
"addr": "0x008cd820",
"convention": "cdecl",
"prototype": "void (IConfigCallback* cb, const char* path, const char* blockName, bool* found) /* cb->vft[1](Script*) per matching block */",
"status": "verified",
"source": "handoff/loader-prototypes.md#m3"
},
{
"name": "RefCounted_Release",
"addr": "0x008a7230",
"convention": "fastcall",
"prototype": "int (IBuffer* obj) /* ECX=obj; --refcount, vft[0](1) at 0 */",
"status": "verified",
"source": "handoff/loader-prototypes.md#m4"
},
{
"name": "WeaponDictionary_Init",
"addr": "0x0059a4c0",
"convention": "thiscall",
"prototype": "void (WeaponDictionary* this) /* reads Weapons/_weapons.txt manifest */",
"status": "verified",
"source": "handoff/loader-prototypes.md#m2"
},
{
"name": "WeaponDictionary_LoadWeapon",
"addr": "0x0059a230",
"convention": "thiscall",
"prototype": "void (WeaponDictionary* this, const char* path, int id, WeaponDef** out)",
"status": "verified",
"source": "handoff/loader-prototypes.md#m2"
},
{
"name": "WeaponDictionary_FindByName",
"addr": "0x00590a10",
"convention": "thiscall",
"prototype": "WeaponDef* (WeaponDictionary* this, const char* name) /* _stricmp over def->name @+0x40 */",
"status": "verified",
"source": "handoff/loader-prototypes.md#m2"
},
{
"name": "WeaponDictionary_FindByNameSorted",
"addr": "0x00591e30",
"convention": "thiscall",
"prototype": "WeaponDef* (WeaponDictionary* this, const char* name) /* 'Weapon not found: \"%s\" - Was it added to the index file?' */",
"status": "verified",
"source": "handoff/loader-prototypes.md#m2"
},
{
"name": "WeaponDef_ctor",
"addr": "0x00598a20",
"convention": "thiscall",
"prototype": "WeaponDef* (WeaponDef* this, int index, int id, const char* path) /* sizeof 0x278 */",
"status": "verified",
"source": "handoff/loader-prototypes.md#m3"
},
{
"name": "WeaponDef_ParseScript",
"addr": "0x00599070",
"convention": "thiscall",
"prototype": "bool (WeaponDef* this, Script* script, int* rc, TechTree* tree)",
"status": "verified",
"source": "handoff/loader-prototypes.md#m3"
},
{
"name": "SectionDictionary_ctor",
"addr": "0x00576f40",
"convention": "thiscall",
"prototype": "SectionDictionary* (SectionDictionary* this, TechTree* tree) /* reads Species/<Race>/_shipsections.txt for species 0..6 */",
"status": "verified",
"source": "handoff/loader-prototypes.md#m2"
},
{
"name": "SectionDictionary_LoadSection",
"addr": "0x00576cd0",
"convention": "thiscall",
"prototype": "SectionDef* (SectionDictionary* this, const char* path, int species, int id)",
"status": "verified",
"source": "handoff/loader-prototypes.md#m2"
},
{
"name": "SectionDef_ctor",
"addr": "0x00574020",
"convention": "thiscall",
"prototype": "SectionDef* (SectionDef* this, int index, int species, int id) /* sizeof 0x3d8 */",
"status": "verified",
"source": "handoff/loader-prototypes.md#m3"
},
{
"name": "SectionDef_ParseScript",
"addr": "0x005744e0",
"convention": "thiscall",
"prototype": "bool (SectionDef* this, Script* script, int* rc, TechTree* tree)",
"status": "verified",
"source": "handoff/loader-prototypes.md#m3"
},
{
"name": "Species_GetDirName",
"addr": "0x00545ec0",
"convention": "cdecl",
"prototype": "const char* (uint species)",
"status": "verified",
"source": "handoff/loader-prototypes.md#m2"
},
{
"name": "MasterTechTree_ctor",
"addr": "0x0058b870",
"convention": "thiscall",
"prototype": "MasterTechTree* (MasterTechTree* this) /* TechTree/MasterTechList.tech */",
"status": "verified",
"source": "handoff/loader-prototypes.md#m3"
},
{
"name": "MasterTechTree_LoadTechFile",
"addr": "0x0058b770",
"convention": "thiscall",
"prototype": "void (MasterTechTree* this, const char* path, int flag)",
"status": "verified",
"source": "handoff/loader-prototypes.md#m3"
},
{
"name": "MasterTechTree_ParseTech",
"addr": "0x0058b050",
"convention": "thiscall",
"prototype": "void (MasterTechTree* this, Script* script, int flag) /* one tech{} block */",
"status": "verified",
"source": "handoff/loader-prototypes.md#m3"
},
{
"name": "DemoApp_LoadGameData",
"addr": "0x00899280",
"convention": "thiscall",
"prototype": "void (DemoApp* this) /* MasterTechTree -> +0x110, WeaponDictionary -> +0x118, SectionDictionary -> +0x114 */",
"status": "verified",
"source": "handoff/loader-prototypes.md#m2"
},
{
"name": "DemoApp_EnsureGameData",
"addr": "0x00899870",
"convention": "thiscall",
"prototype": "void (DemoApp* this) /* once, from OnTick when this+0x10c is set */",
"status": "verified",
"source": "handoff/loader-prototypes.md#m2"
},
{
"name": "gobio_Init",
"addr": "0x008d7010",
"convention": "cdecl",
"prototype": "void (std::vector<std::string>* mountPaths)",
"status": "verified",
"source": "handoff/loader-prototypes.md#m4"
},
{
"name": "gobio_FileSystemSet_ctor",
"addr": "0x008d6d60",
"convention": "thiscall",
"prototype": "FileSystemSet* (FileSystemSet* this, std::vector<std::string>* mounts)",
"status": "verified",
"source": "handoff/loader-prototypes.md#m4"
},
{
"name": "gobio_FileSystemSet_Open",
"addr": "0x008d5060",
"convention": "thiscall",
"prototype": "bool (FileSystemSet* this, const char* path)",
"status": "verified",
"source": "handoff/loader-prototypes.md#m4"
},
{
"name": "gobio_FileSystemSet_ListFiles",
"addr": "0x008d7090",
"convention": "thiscall",
"prototype": "void (FileSystemSet* this, const char* pattern, std::vector<std::string>* out)",
"status": "verified",
"source": "handoff/loader-prototypes.md#m4"
},
{
"name": "gobio_ReadFile",
"addr": "0x008d5140",
"convention": "cdecl",
"prototype": "bool (const char* path, IBuffer** out) /* whole file by relative path; THE hook point for M4 */",
"status": "verified",
"source": "handoff/loader-prototypes.md#m4"
},
{
"name": "gobio_Open",
"addr": "0x008d50d0",
"convention": "cdecl",
"prototype": "bool (const char* path)",
"status": "verified",
"source": "handoff/loader-prototypes.md#m4"
},
{
"name": "gobio_Close",
"addr": "0x008d4d20",
"convention": "cdecl",
"prototype": "void (void)",
"status": "verified",
"source": "handoff/loader-prototypes.md#m4"
},
{
"name": "gobio_Size",
"addr": "0x008d4d50",
"convention": "cdecl",
"prototype": "uint (void)",
"status": "verified",
"source": "handoff/loader-prototypes.md#m4"
},
{
"name": "gobio_Read",
"addr": "0x008d4d80",
"convention": "cdecl",
"prototype": "uint (void* dst, uint n)",
"status": "verified",
"source": "handoff/loader-prototypes.md#m4"
},
{
"name": "gobio_Exists",
"addr": "0x008d50f0",
"convention": "cdecl",
"prototype": "bool (const char* path)",
"status": "verified",
"source": "handoff/loader-prototypes.md#m4"
},
{
"name": "gobio_ListFiles",
"addr": "0x008d71d0",
"convention": "cdecl",
"prototype": "void (const char* pattern, std::vector<std::string>* out)",
"status": "verified",
"source": "handoff/loader-prototypes.md#m4"
},
{
"name": "gobio_Buffer_Create",
"addr": "0x008d4c90",
"convention": "custom",
"prototype": "/* size in EBX, IBuffer** out on stack \u2014 internal, do not hook */",
"status": "verified",
"source": "handoff/loader-prototypes.md#m4"
},
{
"name": "gobio_Buffer_Data",
"addr": "0x00682d50",
"convention": "thiscall",
"prototype": "void* (IBuffer* this) /* vft[1] and vft[2]; = this+8 */",
"status": "verified",
"source": "handoff/loader-prototypes.md#m4"
},
{
"name": "gobio_Buffer_Size",
"addr": "0x008d4c80",
"convention": "thiscall",
"prototype": "uint (IBuffer* this) /* vft[3]; = this+0xc */",
"status": "verified",
"source": "handoff/loader-prototypes.md#m4"
},
{
"name": "gobio_NativeFileSystem_ctor",
"addr": "0x008d57a0",
"convention": "thiscall",
"prototype": "NativeFileSystem* (NativeFileSystem* this, const char* root) /* sizeof 0x28; vtable 0x00a398ac */",
"status": "verified",
"source": "handoff/loader-prototypes.md#m4"
},
{
"name": "gobio_NativeFileSystem_Open",
"addr": "0x008d4ef0",
"convention": "thiscall",
"prototype": "bool (NativeFileSystem* this, const char* path) /* vft[1] fopen(root+path,'rb') */",
"status": "verified",
"source": "handoff/loader-prototypes.md#m4"
},
{
"name": "gobio_NativeFileSystem_Read",
"addr": "0x008d4c30",
"convention": "thiscall",
"prototype": "uint (NativeFileSystem* this, void* dst, uint n) /* vft[4] */",
"status": "verified",
"source": "handoff/loader-prototypes.md#m4"
},
{
"name": "gobio_ZipFileSystem_ctor",
"addr": "0x008d6c40",
"convention": "thiscall",
"prototype": "ZipFileSystem* (ZipFileSystem* this, const char* zipPath) /* sizeof 0x34; vtable 0x00a39934 */",
"status": "verified",
"source": "handoff/loader-prototypes.md#m4"
},
{
"name": "gobio_ZipFileSystem_Open",
"addr": "0x008d56a0",
"convention": "thiscall",
"prototype": "bool (ZipFileSystem* this, const char* path) /* vft[1]; case- and slash-insensitive */",
"status": "verified",
"source": "handoff/loader-prototypes.md#m4"
},
{
"name": "gobio_ZipFileSystem_Read",
"addr": "0x008d4b40",
"convention": "thiscall",
"prototype": "uint (ZipFileSystem* this, void* dst, uint n) /* vft[4] */",
"status": "verified",
"source": "handoff/loader-prototypes.md#m4"
},
{
"name": "g_gobio",
"addr": "0x00b2e270",
"convention": "data",
"prototype": "FileSystemSet* /* -> 0x00b2e274 after gobio::Init */",
"status": "verified",
"source": "handoff/loader-prototypes.md#m4"
},
{
"name": "g_DefaultMountTable",
"addr": "0x00a35e98",
"convention": "data",
"prototype": "const char*[3] /* used when sots.ini has no [Modules] Mount<N>; contains sots_local_en.gob, sots.gob */",
"status": "verified",
"source": "handoff/loader-prototypes.md#m4"
},
{
"name": "Stream_OpenFile",
"addr": "0x008cfcb0",
"convention": "cdecl",
"prototype": "Stream* (const char* path) /* sniffs 'TXT' magic, returns a TextFileStream (0x420 bytes) opened for read */",
"status": "verified",
"source": "handoff/loader-prototypes.md#effect"
},
{
"name": "TextFileStream_ctor",
"addr": "0x0091e460",
"convention": "thiscall",
"prototype": "TextFileStream* (TextFileStream* this) /* vtable 0x00a428c0 */",
"status": "verified",
"source": "handoff/loader-prototypes.md#effect"
},
{
"name": "TextFileStream_Open",
"addr": "0x008cfb90",
"convention": "thiscall",
"prototype": "bool (TextFileStream* this, const char* path, int mode /*1*/)",
"status": "verified",
"source": "handoff/loader-prototypes.md#effect"
},
{
"name": "FileStream_ReadLine",
"addr": "0x008cfa30",
"convention": "thiscall",
"prototype": "uint (Stream* this, char* out, int max)",
"status": "verified",
"source": "handoff/loader-prototypes.md#effect"
},
{
"name": "TextFileStream_MatchLabel",
"addr": "0x0091e760",
"convention": "thiscall",
"prototype": "const char* (TextFileStream* this, const char* line, const char* label) /* NULL on END/mismatch */",
"status": "verified",
"source": "handoff/loader-prototypes.md#effect"
},
{
"name": "TextFileStream_ReadString",
"addr": "0x0091ec90",
"convention": "thiscall",
"prototype": "bool (TextFileStream* this, const char* label, char* out, int outMax) /* vft[1] */",
"status": "verified",
"source": "handoff/loader-prototypes.md#effect"
},
{
"name": "TextFileStream_ReadBool",
"addr": "0x0091eba0",
"convention": "thiscall",
"prototype": "bool (TextFileStream* this, const char* label, bool* out) /* vft[2] */",
"status": "verified",
"source": "handoff/loader-prototypes.md#effect"
},
{
"name": "TextFileStream_ReadFloat",
"addr": "0x0091eb30",
"convention": "thiscall",
"prototype": "bool (TextFileStream* this, const char* label, float* out) /* vft[3] */",
"status": "verified",
"source": "handoff/loader-prototypes.md#effect"
},
{
"name": "TextFileStream_ReadInt",
"addr": "0x0091eab0",
"convention": "thiscall",
"prototype": "bool (TextFileStream* this, const char* label, int* out, int dflt) /* vft[4] */",
"status": "verified",
"source": "handoff/loader-prototypes.md#effect"
},
{
"name": "TextFileStream_ReadNested",
"addr": "0x0091e8e0",
"convention": "thiscall",
"prototype": "void (TextFileStream* this, const char* label, IStreamable* obj /*NULL=skip*/) /* vft[5]; BEGIN..END */",
"status": "verified",
"source": "handoff/loader-prototypes.md#effect"
},
{
"name": "ParticleSystem_Read",
"addr": "0x008db650",
"convention": "thiscall",
"prototype": "void (ParticleSystem* this, Stream* s) /* .effect body */",
"status": "verified",
"source": "handoff/loader-prototypes.md#effect"
},
{
"name": "ParticleSystem_Write",
"addr": "0x008dad60",
"convention": "thiscall",
"prototype": "void (ParticleSystem* this, Stream* s)",
"status": "verified",
"source": "handoff/loader-prototypes.md#effect"
},
{
"name": "EffectDictionary_Load",
"addr": "0x008b42b0",
"convention": "thiscall",
"prototype": "void* (EffectDictionary* this, const char* name) /* 'effects/' + name; Stream::OpenFile; 0x1c0-byte effect */",
"status": "unverified",
"source": "handoff/loader-prototypes.md#effect"
},
{
"name": "Stream_ReadString",
"addr": "0x008b9d90",
"convention": "cdecl",
"prototype": "void (Stream* s, const char* label, std::string* out, int flag) /* wrapper over vft[1] */",
"status": "verified",
"source": "handoff/loader-prototypes.md#effect"
},
{
"name": "Stream_ReadInt",
"addr": "0x008b9d20",
"convention": "cdecl",
"prototype": "void (Stream* s, const char* label, int* out, int dflt) /* wrapper over vft[4] */",
"status": "verified",
"source": "handoff/loader-prototypes.md#effect"
},
{
"name": "Stream_ReadBool",
"addr": "0x008b9c00",
"convention": "cdecl",
"prototype": "void (Stream* s, const char* label, bool* out) /* wrapper over vft[2] */",
"status": "verified",
"source": "handoff/loader-prototypes.md#effect"
},
{
"name": "Stream_ReadFloat",
"addr": "0x008b9bc0",
"convention": "cdecl",
"prototype": "void (Stream* s, const char* label, float* out, int flag) /* wrapper over vft[3] */",
"status": "verified",
"source": "handoff/loader-prototypes.md#effect"
},
{
"name": "ServerPlayer_OnTechResearched",
"addr": "0x00891790",
"convention": "thiscall",
"prototype": "void (ServerPlayer* this, TechDef* def, bool silent) /* vft slot 4 of 0x00a327a4; hard-coded tech effects */",
"status": "verified",
"source": "handoff/tech-effects.md#1"
},
{
"name": "MasterTechTree_IsTech",
"addr": "0x0057d5d0",
"convention": "thiscall",
"prototype": "bool (MasterTechTree* this, TechDef* def, int techId) /* techId = 10000+index into g_TechIdNames; 0xc5 = none */",
"status": "verified",
"source": "handoff/tech-effects.md#0"
},
{
"name": "MasterTechTree_GetTechDef",
"addr": "0x0057d610",
"convention": "thiscall",
"prototype": "TechDef* (MasterTechTree* this, int techId)",
"status": "verified",
"source": "handoff/tech-effects.md#0"
},
{
"name": "TechTree_HasResearched",
"addr": "0x0057d810",
"convention": "thiscall",
"prototype": "bool (TechTree* this, int techId)",
"status": "verified",
"source": "handoff/tech-effects.md#0"
},
{
"name": "TechTree_Cost",
"addr": "0x0057da00",
"convention": "thiscall",
"prototype": "int (TechTree* this, TechNode* node) // RET 4. node==0 -> INT_MAX; node->costRP(+0x18)==INT_MAX -> INT_MAX; costRP<=0 or this->owner(+0xc)==0 or mult<=0 -> 0; else max(1, _ftol2((float)TechCostMult(owner, node->def) * costRP)). Reads only (no RNG, no writes), so a reimplementation may call it on a scratch tree whose +0xc is the real owner",
"status": "verified",
"source": "B3 own disassembly pass 2026-09-08 (objdump/pei-i386): 0x005876c0, 0x0047d830, 0x00426e00, 0x0049fdf0, 0x004271c0, 0x0057da00, 0x008914a5"
},
{
"name": "ServerPlayer_TechCostMult",
"addr": "0x0080db50",
"convention": "thiscall",
"prototype": "float (ServerPlayer* this, TechDef* def) // 1.0 - 0.25 per applicable species research-bonus tech owned; read-only",
"status": "verified",
"source": "B3 own disassembly pass 2026-09-08 (objdump/pei-i386): 0x005876c0, 0x0047d830, 0x00426e00, 0x0049fdf0, 0x004271c0, 0x0057da00, 0x008914a5"
},
{
"name": "TechTree_SetResearched",
"addr": "0x00581e10",
"convention": "thiscall",
"prototype": "void (TechTree* this, TechDef* def, int flags) // state 4 + turn/order stamps + owner callback + child unlock cascade. Makes no direct RNG draw; the owner callback is not audited, so a compare that runs it is out of scope",
"status": "verified",
"source": "B3 own disassembly pass 2026-09-08 (objdump/pei-i386): 0x005876c0, 0x0047d830, 0x00426e00, 0x0049fdf0, 0x004271c0, 0x0057da00, 0x008914a5"
},
{
"name": "TechTree_off_Owner",
"offset": "0xc",
"convention": "offset",
"prototype": "ServerPlayer* owner (0 for a tree with no player)",
"status": "verified",
"source": "B3 own disassembly pass 2026-09-08 (objdump/pei-i386): 0x005876c0, 0x0047d830, 0x00426e00, 0x0049fdf0, 0x004271c0, 0x0057da00, 0x008914a5"
},
{
"name": "TechTree_off_Nodes",
"offset": "0x10",
"convention": "offset",
"prototype": "std::vector<TechNode*> indexed by tech id (MSVC2010: 3 words {first@+0x10, last@+0x14, end@+0x18}); entries may be NULL",
"status": "verified",
"source": "B3 own disassembly pass 2026-09-08 (objdump/pei-i386): 0x005876c0, 0x0047d830, 0x00426e00, 0x0049fdf0, 0x004271c0, 0x0057da00, 0x008914a5"
},
{
"name": "TechNode_size",
"offset": "0x34",
"convention": "offset",
"prototype": "sizeof(TechNode) -- the ctor's operator new argument",
"status": "verified",
"source": "B3 own disassembly pass 2026-09-08 (objdump/pei-i386): 0x005876c0, 0x0047d830, 0x00426e00, 0x0049fdf0, 0x004271c0, 0x0057da00, 0x008914a5"
},
{
"name": "TechNode_off_Def",
"offset": "0x0",
"convention": "offset",
"prototype": "TechDef* def; *(int*)def is the tech id used to index TechTree_off_Nodes",
"status": "verified",
"source": "B3 own disassembly pass 2026-09-08 (objdump/pei-i386): 0x005876c0, 0x0047d830, 0x00426e00, 0x0049fdf0, 0x004271c0, 0x0057da00, 0x008914a5"
},
{
"name": "TechNode_off_State",
"offset": "0x14",
"convention": "offset",
"prototype": "int state (0 hidden, 1 parent researched, 2 available, 3 available AND selected as the current research target, 4 researched); ctor writes 0. The decay sweep tests == 2, so a state-3 node never decays -- confirmed by the B3 live trace",
"status": "verified",
"source": "B3 own disassembly pass 2026-09-08 (objdump/pei-i386): 0x005876c0, 0x0047d830, 0x00426e00, 0x0049fdf0, 0x004271c0, 0x0057da00, 0x008914a5 + B3 live trace 2026-09-08"
},
{
"name": "TechNode_off_CostRP",
"offset": "0x18",
"convention": "offset",
"prototype": "int costRP, INT_MAX = no researched parent yet; ctor writes 0x7fffffff",
"status": "verified",
"source": "B3 own disassembly pass 2026-09-08 (objdump/pei-i386): 0x005876c0, 0x0047d830, 0x00426e00, 0x0049fdf0, 0x004271c0, 0x0057da00, 0x008914a5"
},
{
"name": "TechNode_off_Progress",
"offset": "0x1c",
"convention": "offset",
"prototype": "int progress in RP; the only node word ProcessResearch itself writes besides the flag",
"status": "verified",
"source": "B3 own disassembly pass 2026-09-08 (objdump/pei-i386): 0x005876c0, 0x0047d830, 0x00426e00, 0x0049fdf0, 0x004271c0, 0x0057da00, 0x008914a5"
},
{
"name": "TechNode_off_Flag",
"offset": "0x2c",
"convention": "offset",
"prototype": "int flag (1 default from the ctor, 0 completed below 80% of cost, 2 over-budget event raised)",
"status": "verified",
"source": "B3 own disassembly pass 2026-09-08 (objdump/pei-i386): 0x005876c0, 0x0047d830, 0x00426e00, 0x0049fdf0, 0x004271c0, 0x0057da00, 0x008914a5"
},
{
"name": "MasterTechTree_ResolveTechIds",
"addr": "0x00581c10",
"convention": "thiscall",
"prototype": "void (MasterTechTree* this) /* fills TechDef*[196] at this+0 from g_TechIdNames */",
"status": "verified",
"source": "handoff/tech-effects.md#0"
},
{
"name": "IsCombatTechName",
"addr": "0x006965c0",
"convention": "cdecl",
"prototype": "int (std::string* name) /* membership in g_CombatTechNames (116) */",
"status": "verified",
"source": "handoff/tech-effects.md#0"
},
{
"name": "ServerPlayer_RebuildSpeciesTechFlags",
"addr": "0x0082bf10",
"convention": "fastcall",
"prototype": "void (ServerPlayer* this) /* flags[7] at +0x348 from SpeciesDef xnc tech ids */",
"status": "verified",
"source": "handoff/tech-effects.md#0"
},
{
"name": "ServerPlayer_ApplyAITechBonus",
"addr": "0x0080e330",
"convention": "thiscall",
"prototype": "void (ServerPlayer* this, TechDef* def)",
"status": "verified",
"source": "handoff/tech-effects.md#1"
},
{
"name": "ServerPlayer_SetAIBenefit",
"addr": "0x008186b0",
"convention": "thiscall",
"prototype": "void (ServerPlayer* this, bool on)",
"status": "verified",
"source": "handoff/tech-effects.md#1"
},
{
"name": "TechDef_GetPlagueCureMask",
"addr": "0x00537240",
"convention": "cdecl",
"prototype": "bool (TechDef* def, uint* maskOut)",
"status": "verified",
"source": "handoff/tech-effects.md#1"
},
{
"name": "ComputeTechBitmasks",
"addr": "0x004d7960",
"convention": "cdecl",
"prototype": "uint* (uint out[2], TechTree* tree) /* EBX-passed tree in caller; tables 0x00adf378 / 0x00adf478 */",
"status": "unverified",
"source": "handoff/tech-effects.md#3"
},
{
"name": "SpeciesDef_Get",
"addr": "0x00545cc0",
"convention": "cdecl",
"prototype": "SpeciesDef* (uint species) /* 0x00b10a00 + i*0x184 */",
"status": "verified",
"source": "handoff/tech-effects.md#4"
},
{
"name": "SpeciesDef_InitTable",
"addr": "0x005453a0",
"convention": "cdecl",
"prototype": "void (void)",
"status": "verified",
"source": "handoff/tech-effects.md#4"
},
{
"name": "ServerPlayer_UpdateBankruptcyLimits",
"addr": "0x00818600",
"convention": "fastcall",
"prototype": "void (ServerPlayer* this)",
"status": "verified",
"source": "handoff/formula-gaps.md#q1"
},
{
"name": "ServerPlayer_SetBankruptcyState",
"addr": "0x0080e260",
"convention": "thiscall",
"prototype": "void (ServerPlayer* this, int level)",
"status": "verified",
"source": "handoff/formula-gaps.md#q1"
},
{
"name": "ServerPlayer_BankruptcyLevel",
"addr": "0x0080db10",
"convention": "fastcall",
"prototype": "int (ServerPlayer* this)",
"status": "verified",
"source": "handoff/formula-gaps.md#q1"
},
{
"name": "ServerSystem_HazardMod",
"addr": "0x00747ae0",
"convention": "cdecl",
"prototype": "double (double suit, double ideal, double tol) /* clamp01(1-|suit-ideal|/(tol+0.1)) */",
"status": "verified",
"source": "handoff/formula-gaps.md#q2"
},
{
"name": "ServerSystem_TradePointsToMoney",
"addr": "0x007505b0",
"convention": "thiscall",
"prototype": "double (ServerSystem* this, double tradePoints) /* RET 8 */",
"status": "verified",
"source": "handoff/formula-gaps.md#q3"
},
{
"name": "ServerSystem_CalcSuitMod",
"addr": "0x007484d0",
"convention": "thiscall",
"prototype": "double (ServerSystem* this, int species)",
"status": "verified",
"source": "handoff/formula-gaps.md#q3"
},
{
"name": "ServerSystem_AccrueSystemBonus",
"addr": "0x0074d4f0",
"convention": "fastcall",
"prototype": "void (ServerSystem* this)",
"status": "verified",
"source": "handoff/formula-gaps.md#q4"
},
{
"name": "ServerSystem_SystemBonusPopTarget",
"addr": "0x0074b5a0",
"convention": "thiscall",
"prototype": "int (ServerSystem* this, float frac)",
"status": "verified",
"source": "handoff/formula-gaps.md#q4"
},
{
"name": "g_TechIdNames",
"addr": "0x009ff9e4",
"convention": "data",
"prototype": "struct { const char* name; int x; }[196] /* TechId = 10000+i */",
"status": "verified",
"source": "handoff/tech-effects.md#0"
},
{
"name": "g_CombatTechNames",
"addr": "0x00a19718",
"convention": "data",
"prototype": "const char*[116]",
"status": "verified",
"source": "handoff/tech-effects.md#0"
},
{
"name": "g_SpeciesDefTable",
"addr": "0x00b10a00",
"convention": "data",
"prototype": "SpeciesDef[7] (0x184 B each, .bss, filled by SpeciesDef_InitTable)",
"status": "verified",
"source": "handoff/tech-effects.md#4"
},
{
"name": "g_TechBitmaskTableA",
"addr": "0x00adf378",
"convention": "data",
"prototype": "struct { int techId; uint bit; }[32]",
"status": "verified",
"source": "handoff/tech-effects.md#3"
},
{
"name": "g_TechBitmaskTableB",
"addr": "0x00adf478",
"convention": "data",
"prototype": "struct { int techId; uint bit; }[29]",
"status": "verified",
"source": "handoff/tech-effects.md#3"
},
{
"name": "g_AITechValueTable",
"addr": "0x00a17888",
"convention": "data",
"prototype": "struct { int techId; float rebellionOdds; float bonus; }[6] /* rows: CCC_AI/CCC_AIAdmin/CCC_AIFac odds 0.1f bonus 0.5f, CCC_AIFRCON odds 0.2f bonus 0, CCC_AIVrus/CCC_AISlv odds 0 bonus 0 */",
"status": "verified",
"source": "B2 own disassembly pass 2026-09-08 (ReVa read-memory + objdump -b binary -m i386): ServerPlayer::OnTechResearched 0x00891790, RebuildSpeciesTechFlags 0x0082bf10, ApplyAITechBonus 0x0080e330, SetAIBenefit 0x008186b0, UpdateNodeBoreParams 0x008182c0, SelectNodeBoreParams 0x006e18e0, GetPlagueCureMask 0x00537240"
},
{
"name": "g_PERGATETRAFFIC_DRV_TpGate",
"addr": "0x00b23e2c",
"convention": "data",
"prototype": "int (config storage; PTR slot 0x00aedfe0)",
"status": "verified",
"source": "handoff/tech-effects.md#1"
},
{
"name": "g_PERGATETRAFFIC_DRV_GatAmp",
"addr": "0x00b23e30",
"convention": "data",
"prototype": "int (config storage; PTR slot 0x00aedfe4)",
"status": "verified",
"source": "handoff/tech-effects.md#1"
},
{
"name": "ServerPlayer_off_PlyrIdx",
"offset": "0x28",
"convention": "offset",
"prototype": "int PlyrIdx",
"status": "verified-by-save",
"source": "findings/objects/struct-recovery.md#2 (Read/Write serializers) + findings/subsystems/strategic-turn-internals.md#1.3"
},
{
"name": "ServerPlayer_off_OwnedSystems",
"offset": "0x30",
"convention": "offset",
"prototype": "std::vector<ServerSystem*> owned systems (MSVC2010 release layout: 3 words {first@+0, last@+4, end@+8}, handle ids, 4 B stride); save tag NumOwn/OwnId",
"status": "verified-by-save",
"source": "findings/objects/struct-recovery.md#2 (Read/Write serializers) + findings/subsystems/strategic-turn-internals.md#1.3"
},
{
"name": "ServerPlayer_off_Species",
"offset": "0x5c",
"convention": "offset",
"prototype": "int Species (0 Human .. 6 Morrigi)",
"status": "verified-by-save",
"source": "findings/objects/struct-recovery.md#2 (Read/Write serializers) + findings/subsystems/strategic-turn-internals.md#1.3"
},
{
"name": "ServerPlayer_off_ResRate",
"offset": "0xbc",
"convention": "offset",
"prototype": "float ResRate (research share of available money, 0..1)",
"status": "verified-by-save",
"source": "findings/objects/struct-recovery.md#2 (Read/Write serializers) + findings/subsystems/strategic-turn-internals.md#1.3"
},
{
"name": "ServerPlayer_off_ResMod",
"offset": "0xc0",
"convention": "offset",
"prototype": "float ResMod",
"status": "verified-by-save",
"source": "findings/objects/struct-recovery.md#2 (Read/Write serializers) + findings/subsystems/strategic-turn-internals.md#1.3"
},
{
"name": "ServerPlayer_off_ResScl",
"offset": "0xc4",
"convention": "offset",
"prototype": "float ResScl",
"status": "verified-by-save",
"source": "findings/objects/struct-recovery.md#2 (Read/Write serializers) + findings/subsystems/strategic-turn-internals.md#1.3"
},
{
"name": "ServerPlayer_off_TRM",
"offset": "0xd0",
"convention": "offset",
"prototype": "float TRM (timed research multiplier bonuses)",
"status": "verified-by-save",
"source": "findings/objects/struct-recovery.md#2 (Read/Write serializers) + findings/subsystems/strategic-turn-internals.md#1.3"
},
{
"name": "ServerPlayer_off_TRA",
"offset": "0xd4",
"convention": "offset",
"prototype": "int TRA (per-turn research-point contribution)",
"status": "verified-by-save",
"source": "findings/objects/struct-recovery.md#2 (Read/Write serializers) + findings/subsystems/strategic-turn-internals.md#1.3"
},
{
"name": "ServerPlayer_off_TRP",
"offset": "0xd8",
"convention": "offset",
"prototype": "int TRP (per-turn research-point contribution)",
"status": "verified-by-save",
"source": "findings/objects/struct-recovery.md#2 (Read/Write serializers) + findings/subsystems/strategic-turn-internals.md#1.3"
},
{
"name": "ServerPlayer_off_Elim",
"offset": "0xf8",
"convention": "offset",
"prototype": "bool Elim",
"status": "verified-by-save",
"source": "findings/objects/struct-recovery.md#2 (Read/Write serializers) + findings/subsystems/strategic-turn-internals.md#1.3"
},
{
"name": "ServerPlayer_off_IsAI",
"offset": "0xf9",
"convention": "offset",
"prototype": "bool isAI /* gate ComputeBudget reads for the construction slot and for picking the AI difficulty-mod row */",
"status": "verified",
"source": "findings/objects/struct-recovery.md#2 (Read/Write serializers) + findings/subsystems/strategic-turn-internals.md#1.3"
},
{
"name": "ServerPlayer_off_NPC",
"offset": "0xfb",
"convention": "offset",
"prototype": "bool NPC",
"status": "verified-by-save",
"source": "findings/objects/struct-recovery.md#2 (Read/Write serializers) + findings/subsystems/strategic-turn-internals.md#1.3"
},
{
"name": "ServerPlayer_off_RebAI",
"offset": "0xfc",
"convention": "offset",
"prototype": "bool RebAI",
"status": "verified-by-save",
"source": "findings/objects/struct-recovery.md#2 (Read/Write serializers) + findings/subsystems/strategic-turn-internals.md#1.3"
},
{
"name": "ServerPlayer_off_Maint",
"offset": "0x15c",
"convention": "offset",
"prototype": "int Maint (raw fleet upkeep, before the difficulty divisor)",
"status": "verified-by-save",
"source": "findings/objects/struct-recovery.md#2 (Read/Write serializers) + findings/subsystems/strategic-turn-internals.md#1.3"
},
{
"name": "ServerPlayer_off_shrm",
"offset": "0x160",
"convention": "offset",
"prototype": "float shrm (shared research modifier)",
"status": "verified-by-save",
"source": "findings/objects/struct-recovery.md#2 (Read/Write serializers) + findings/subsystems/strategic-turn-internals.md#1.3"
},
{
"name": "ServerPlayer_off_Nexp",
"offset": "0x204",
"convention": "offset",
"prototype": "std::vector<ExpenseEntry> expense sliders (3 words; entry 16 B {int xid, int xmin, int xmax, float xper}); save tag Nexp",
"status": "verified-by-save",
"source": "findings/objects/struct-recovery.md#2 (Read/Write serializers) + findings/subsystems/strategic-turn-internals.md#1.3"
},
{
"name": "ServerPlayer_off_SetupIncomeMult",
"offset": "0x228",
"convention": "offset",
"prototype": "float income multiplier from the game-setup handicap block; ComputeBudget bonus slot = ftol((this - 1.0) x net)",
"status": "verified",
"source": "findings/objects/struct-recovery.md#2 (Read/Write serializers) + findings/subsystems/strategic-turn-internals.md#1.3"
},
{
"name": "ServerPlayer_off_SetupResearchMult",
"offset": "0x22c",
"convention": "offset",
"prototype": "float research multiplier from the game-setup handicap block; a factor of the research-points slot",
"status": "verified",
"source": "findings/objects/struct-recovery.md#2 (Read/Write serializers) + findings/subsystems/strategic-turn-internals.md#1.3"
},
{
"name": "ServerPlayer_off_Sav",
"offset": "0x284",
"convention": "offset",
"prototype": "int Sav (treasury)",
"status": "verified-by-save",
"source": "findings/objects/struct-recovery.md#2 (Read/Write serializers) + findings/subsystems/strategic-turn-internals.md#1.3"
},
{
"name": "ServerPlayer_off_ResearchTarget",
"offset": "0x294",
"convention": "offset",
"prototype": "Tech* current research target (ResT); NULL = none",
"status": "verified-by-save",
"source": "findings/objects/struct-recovery.md#2 (Read/Write serializers) + findings/subsystems/strategic-turn-internals.md#1.3"
},
{
"name": "ServerPlayer_off_IncMod",
"offset": "0x30c",
"convention": "offset",
"prototype": "float IncMod",
"status": "verified-by-save",
"source": "findings/objects/struct-recovery.md#2 (Read/Write serializers) + findings/subsystems/strategic-turn-internals.md#1.3"
},
{
"name": "ServerPlayer_off_Aid",
"offset": "0x310",
"convention": "offset",
"prototype": "std::vector<PlayerAid> (3 words; entry 0x18 B, {+0x8 int researchPercent, +0xc int researchActive, +0x10 int savings, +0x14 int savingsActive})",
"status": "verified",
"source": "findings/objects/struct-recovery.md#2 (Read/Write serializers) + findings/subsystems/strategic-turn-internals.md#1.3"
},
{
"name": "ServerPlayer_off_SuitTol",
"offset": "0xb4",
"convention": "offset",
"prototype": "float SuitTol -- raised by the two adaptation techs; also caps the hazard money cost",
"status": "verified",
"source": "B2 own disassembly pass 2026-09-08 (ReVa read-memory + objdump -b binary -m i386): ServerPlayer::OnTechResearched 0x00891790, RebuildSpeciesTechFlags 0x0082bf10, ApplyAITechBonus 0x0080e330, SetAIBenefit 0x008186b0, UpdateNodeBoreParams 0x008182c0, SelectNodeBoreParams 0x006e18e0, GetPlagueCureMask 0x00537240"
},
{
"name": "ServerPlayer_off_MaxOH",
"offset": "0xb8",
"convention": "offset",
"prototype": "float MaxOH -- max over-harvest slider, raised by a float32 max() against 0.1f",
"status": "verified",
"source": "B2 own disassembly pass 2026-09-08 (ReVa read-memory + objdump -b binary -m i386): ServerPlayer::OnTechResearched 0x00891790, RebuildSpeciesTechFlags 0x0082bf10, ApplyAITechBonus 0x0080e330, SetAIBenefit 0x008186b0, UpdateNodeBoreParams 0x008182c0, SelectNodeBoreParams 0x006e18e0, GetPlagueCureMask 0x00537240"
},
{
"name": "ServerPlayer_off_TechTree",
"offset": "0xf4",
"convention": "offset",
"prototype": "TechTree* the player's own tech tree; its +4 is the MasterTechTree the IsTech chain uses",
"status": "verified",
"source": "B2 own disassembly pass 2026-09-08 (ReVa read-memory + objdump -b binary -m i386): ServerPlayer::OnTechResearched 0x00891790, RebuildSpeciesTechFlags 0x0082bf10, ApplyAITechBonus 0x0080e330, SetAIBenefit 0x008186b0, UpdateNodeBoreParams 0x008182c0, SelectNodeBoreParams 0x006e18e0, GetPlagueCureMask 0x00537240"
},
{
"name": "ServerPlayer_off_AIBn",
"offset": "0xfe",
"convention": "offset",
"prototype": "bool AIBn -- AI benefit active; gates ApplyAITechBonus and flips the bonus sign",
"status": "verified",
"source": "B2 own disassembly pass 2026-09-08 (ReVa read-memory + objdump -b binary -m i386): ServerPlayer::OnTechResearched 0x00891790, RebuildSpeciesTechFlags 0x0082bf10, ApplyAITechBonus 0x0080e330, SetAIBenefit 0x008186b0, UpdateNodeBoreParams 0x008182c0, SelectNodeBoreParams 0x006e18e0, GetPlagueCureMask 0x00537240"
},
{
"name": "ServerPlayer_off_CnTrd",
"offset": "0xff",
"convention": "offset",
"prototype": "bool CnTrd -- trade routes allowed (set by CCC_FtlEcon unless RebAI)",
"status": "verified",
"source": "B2 own disassembly pass 2026-09-08 (ReVa read-memory + objdump -b binary -m i386): ServerPlayer::OnTechResearched 0x00891790, RebuildSpeciesTechFlags 0x0082bf10, ApplyAITechBonus 0x0080e330, SetAIBenefit 0x008186b0, UpdateNodeBoreParams 0x008182c0, SelectNodeBoreParams 0x006e18e0, GetPlagueCureMask 0x00537240"
},
{
"name": "ServerPlayer_off_CnRad",
"offset": "0x100",
"convention": "offset",
"prototype": "bool CnRad -- commerce raiding allowed (CCC_ComRaid)",
"status": "verified",
"source": "B2 own disassembly pass 2026-09-08 (ReVa read-memory + objdump -b binary -m i386): ServerPlayer::OnTechResearched 0x00891790, RebuildSpeciesTechFlags 0x0082bf10, ApplyAITechBonus 0x0080e330, SetAIBenefit 0x008186b0, UpdateNodeBoreParams 0x008182c0, SelectNodeBoreParams 0x006e18e0, GetPlagueCureMask 0x00537240"
},
{
"name": "ServerPlayer_off_CnVItl",
"offset": "0x101",
"convention": "offset",
"prototype": "bool CnVItl -- may view other empires' intel (CCC_DatCor)",
"status": "verified",
"source": "B2 own disassembly pass 2026-09-08 (ReVa read-memory + objdump -b binary -m i386): ServerPlayer::OnTechResearched 0x00891790, RebuildSpeciesTechFlags 0x0082bf10, ApplyAITechBonus 0x0080e330, SetAIBenefit 0x008186b0, UpdateNodeBoreParams 0x008182c0, SelectNodeBoreParams 0x006e18e0, GetPlagueCureMask 0x00537240"
},
{
"name": "ServerPlayer_off_hgs",
"offset": "0x102",
"convention": "offset",
"prototype": "bool hgs -- gravitic-syncing drive researched (DRV_GrvSyn); client-synced only",
"status": "verified",
"source": "B2 own disassembly pass 2026-09-08 (ReVa read-memory + objdump -b binary -m i386): ServerPlayer::OnTechResearched 0x00891790, RebuildSpeciesTechFlags 0x0082bf10, ApplyAITechBonus 0x0080e330, SetAIBenefit 0x008186b0, UpdateNodeBoreParams 0x008182c0, SelectNodeBoreParams 0x006e18e0, GetPlagueCureMask 0x00537240"
},
{
"name": "ServerPlayer_off_hadvs",
"offset": "0x103",
"convention": "offset",
"prototype": "bool hadvs -- advanced sensors (CCC_AdvSens)",
"status": "verified",
"source": "B2 own disassembly pass 2026-09-08 (ReVa read-memory + objdump -b binary -m i386): ServerPlayer::OnTechResearched 0x00891790, RebuildSpeciesTechFlags 0x0082bf10, ApplyAITechBonus 0x0080e330, SetAIBenefit 0x008186b0, UpdateNodeBoreParams 0x008182c0, SelectNodeBoreParams 0x006e18e0, GetPlagueCureMask 0x00537240"
},
{
"name": "ServerPlayer_off_harcc",
"offset": "0x104",
"convention": "offset",
"prototype": "bool harcc -- arcologies (IND_ArcCon)",
"status": "verified",
"source": "B2 own disassembly pass 2026-09-08 (ReVa read-memory + objdump -b binary -m i386): ServerPlayer::OnTechResearched 0x00891790, RebuildSpeciesTechFlags 0x0082bf10, ApplyAITechBonus 0x0080e330, SetAIBenefit 0x008186b0, UpdateNodeBoreParams 0x008182c0, SelectNodeBoreParams 0x006e18e0, GetPlagueCureMask 0x00537240"
},
{
"name": "ServerPlayer_off_pddm",
"offset": "0x108",
"convention": "offset",
"prototype": "float pddm -- multiplied by 0.25 by IND_HrdStrct",
"status": "verified",
"source": "B2 own disassembly pass 2026-09-08 (ReVa read-memory + objdump -b binary -m i386): ServerPlayer::OnTechResearched 0x00891790, RebuildSpeciesTechFlags 0x0082bf10, ApplyAITechBonus 0x0080e330, SetAIBenefit 0x008186b0, UpdateNodeBoreParams 0x008182c0, SelectNodeBoreParams 0x006e18e0, GetPlagueCureMask 0x00537240"
},
{
"name": "ServerPlayer_off_ConMod",
"offset": "0x10c",
"convention": "offset",
"prototype": "float ConMod[3] -- construction cost per hull class; techs subtract from all three, IND_OrbDry only from [1] and [2]",
"status": "verified",
"source": "B2 own disassembly pass 2026-09-08 (ReVa read-memory + objdump -b binary -m i386): ServerPlayer::OnTechResearched 0x00891790, RebuildSpeciesTechFlags 0x0082bf10, ApplyAITechBonus 0x0080e330, SetAIBenefit 0x008186b0, UpdateNodeBoreParams 0x008182c0, SelectNodeBoreParams 0x006e18e0, GetPlagueCureMask 0x00537240"
},
{
"name": "ServerPlayer_off_SavMod",
"offset": "0x118",
"convention": "offset",
"prototype": "float SavMod[3] -- IND_OrbFound subtracts 0.05f from all three",
"status": "verified",
"source": "B2 own disassembly pass 2026-09-08 (ReVa read-memory + objdump -b binary -m i386): ServerPlayer::OnTechResearched 0x00891790, RebuildSpeciesTechFlags 0x0082bf10, ApplyAITechBonus 0x0080e330, SetAIBenefit 0x008186b0, UpdateNodeBoreParams 0x008182c0, SelectNodeBoreParams 0x006e18e0, GetPlagueCureMask 0x00537240"
},
{
"name": "ServerPlayer_off_OutMod",
"offset": "0x124",
"convention": "offset",
"prototype": "float OutMod -- industrial output multiplier; several techs add, IND_HrdStrct multiplies by 0.9f",
"status": "verified",
"source": "B2 own disassembly pass 2026-09-08 (ReVa read-memory + objdump -b binary -m i386): ServerPlayer::OnTechResearched 0x00891790, RebuildSpeciesTechFlags 0x0082bf10, ApplyAITechBonus 0x0080e330, SetAIBenefit 0x008186b0, UpdateNodeBoreParams 0x008182c0, SelectNodeBoreParams 0x006e18e0, GetPlagueCureMask 0x00537240"
},
{
"name": "ServerPlayer_off_PopMod",
"offset": "0x130",
"convention": "offset",
"prototype": "float PopMod",
"status": "verified",
"source": "B2 own disassembly pass 2026-09-08 (ReVa read-memory + objdump -b binary -m i386): ServerPlayer::OnTechResearched 0x00891790, RebuildSpeciesTechFlags 0x0082bf10, ApplyAITechBonus 0x0080e330, SetAIBenefit 0x008186b0, UpdateNodeBoreParams 0x008182c0, SelectNodeBoreParams 0x006e18e0, GetPlagueCureMask 0x00537240"
},
{
"name": "ServerPlayer_off_TerraMod",
"offset": "0x134",
"convention": "offset",
"prototype": "float TerraMod",
"status": "verified",
"source": "B2 own disassembly pass 2026-09-08 (ReVa read-memory + objdump -b binary -m i386): ServerPlayer::OnTechResearched 0x00891790, RebuildSpeciesTechFlags 0x0082bf10, ApplyAITechBonus 0x0080e330, SetAIBenefit 0x008186b0, UpdateNodeBoreParams 0x008182c0, SelectNodeBoreParams 0x006e18e0, GetPlagueCureMask 0x00537240"
},
{
"name": "ServerPlayer_off_AMine",
"offset": "0x138",
"convention": "offset",
"prototype": "bool AMine -- asteroid mining (IND_AstMine)",
"status": "verified",
"source": "B2 own disassembly pass 2026-09-08 (ReVa read-memory + objdump -b binary -m i386): ServerPlayer::OnTechResearched 0x00891790, RebuildSpeciesTechFlags 0x0082bf10, ApplyAITechBonus 0x0080e330, SetAIBenefit 0x008186b0, UpdateNodeBoreParams 0x008182c0, SelectNodeBoreParams 0x006e18e0, GetPlagueCureMask 0x00537240"
},
{
"name": "ServerPlayer_off_MinRate",
"offset": "0x140",
"convention": "offset",
"prototype": "float MinRate -- mining rate; IND_MsMine adds 1.0",
"status": "verified",
"source": "B2 own disassembly pass 2026-09-08 (ReVa read-memory + objdump -b binary -m i386): ServerPlayer::OnTechResearched 0x00891790, RebuildSpeciesTechFlags 0x0082bf10, ApplyAITechBonus 0x0080e330, SetAIBenefit 0x008186b0, UpdateNodeBoreParams 0x008182c0, SelectNodeBoreParams 0x006e18e0, GetPlagueCureMask 0x00537240"
},
{
"name": "ServerPlayer_off_PrGtTrf",
"offset": "0x148",
"convention": "offset",
"prototype": "int PrGtTrf -- per-gate traffic capacity; the two gate techs raise it with a SIGNED INTEGER max against a config int, not a float compare",
"status": "verified",
"source": "B2 own disassembly pass 2026-09-08 (ReVa read-memory + objdump -b binary -m i386): ServerPlayer::OnTechResearched 0x00891790, RebuildSpeciesTechFlags 0x0082bf10, ApplyAITechBonus 0x0080e330, SetAIBenefit 0x008186b0, UpdateNodeBoreParams 0x008182c0, SelectNodeBoreParams 0x006e18e0, GetPlagueCureMask 0x00537240"
},
{
"name": "ServerPlayer_off_CstR",
"offset": "0x150",
"convention": "offset",
"prototype": "float CstR, CstE, CstT at +0x150/+0x154/+0x158 -- set to 10.0f / 2.0f / 1.0f (FLD1) by DRV_FarCast",
"status": "verified",
"source": "B2 own disassembly pass 2026-09-08 (ReVa read-memory + objdump -b binary -m i386): ServerPlayer::OnTechResearched 0x00891790, RebuildSpeciesTechFlags 0x0082bf10, ApplyAITechBonus 0x0080e330, SetAIBenefit 0x008186b0, UpdateNodeBoreParams 0x008182c0, SelectNodeBoreParams 0x006e18e0, GetPlagueCureMask 0x00537240"
},
{
"name": "ServerPlayer_off_TechMaskA",
"offset": "0x190",
"convention": "offset",
"prototype": "uint32 design-option mask A; +0x194 is mask B. Rewritten wholesale by ComputeTechBitmasks on every completion; not serialised",
"status": "verified",
"source": "B2 own disassembly pass 2026-09-08 (ReVa read-memory + objdump -b binary -m i386): ServerPlayer::OnTechResearched 0x00891790, RebuildSpeciesTechFlags 0x0082bf10, ApplyAITechBonus 0x0080e330, SetAIBenefit 0x008186b0, UpdateNodeBoreParams 0x008182c0, SelectNodeBoreParams 0x006e18e0, GetPlagueCureMask 0x00537240"
},
{
"name": "ServerPlayer_off_TranslationKnown",
"offset": "0x1a4",
"convention": "offset",
"prototype": "uint32 sticky 'level-1 translation researched for species sp' bit mask, one bit per species, NPC (4) excluded. Second pass of RebuildSpeciesTechFlags; only ever ORed",
"status": "verified",
"source": "B2 own disassembly pass 2026-09-08 (ReVa read-memory + objdump -b binary -m i386): ServerPlayer::OnTechResearched 0x00891790, RebuildSpeciesTechFlags 0x0082bf10, ApplyAITechBonus 0x0080e330, SetAIBenefit 0x008186b0, UpdateNodeBoreParams 0x008182c0, SelectNodeBoreParams 0x006e18e0, GetPlagueCureMask 0x00537240"
},
{
"name": "ServerPlayer_off_HasVac",
"offset": "0x288",
"convention": "offset",
"prototype": "uint32 HasVac; +0x28c HasImm. Both get |= the plague-cure mask of the completing tech",
"status": "verified",
"source": "B2 own disassembly pass 2026-09-08 (ReVa read-memory + objdump -b binary -m i386): ServerPlayer::OnTechResearched 0x00891790, RebuildSpeciesTechFlags 0x0082bf10, ApplyAITechBonus 0x0080e330, SetAIBenefit 0x008186b0, UpdateNodeBoreParams 0x008182c0, SelectNodeBoreParams 0x006e18e0, GetPlagueCureMask 0x00537240"
},
{
"name": "ServerPlayer_off_NPTrk",
"offset": "0x290",
"convention": "offset",
"prototype": "uint32 NPTrk -- bit per species whose node-space traffic is visible; set by that species' node-track tech",
"status": "verified",
"source": "B2 own disassembly pass 2026-09-08 (ReVa read-memory + objdump -b binary -m i386): ServerPlayer::OnTechResearched 0x00891790, RebuildSpeciesTechFlags 0x0082bf10, ApplyAITechBonus 0x0080e330, SetAIBenefit 0x008186b0, UpdateNodeBoreParams 0x008182c0, SelectNodeBoreParams 0x006e18e0, GetPlagueCureMask 0x00537240"
},
{
"name": "ServerPlayer_off_NodeBore",
"offset": "0x308",
"convention": "offset",
"prototype": "int (*)[3] -- pointer to a separately allocated 3-word node-bore parameter block, NULL while no bore drive is researched (the updater allocates and frees it)",
"status": "verified",
"source": "B2 own disassembly pass 2026-09-08 (ReVa read-memory + objdump -b binary -m i386): ServerPlayer::OnTechResearched 0x00891790, RebuildSpeciesTechFlags 0x0082bf10, ApplyAITechBonus 0x0080e330, SetAIBenefit 0x008186b0, UpdateNodeBoreParams 0x008182c0, SelectNodeBoreParams 0x006e18e0, GetPlagueCureMask 0x00537240"
},
{
"name": "ServerPlayer_off_CaptureDesigns",
"offset": "0x330",
"convention": "offset",
"prototype": "bool cdp -- set once CCC_SpyBm and IND_SlvgTech are both researched; the test is in the completion tail, not in either tech's branch",
"status": "verified",
"source": "B2 own disassembly pass 2026-09-08 (ReVa read-memory + objdump -b binary -m i386): ServerPlayer::OnTechResearched 0x00891790, RebuildSpeciesTechFlags 0x0082bf10, ApplyAITechBonus 0x0080e330, SetAIBenefit 0x008186b0, UpdateNodeBoreParams 0x008182c0, SelectNodeBoreParams 0x006e18e0, GetPlagueCureMask 0x00537240"
},
{
"name": "ServerPlayer_off_SpeciesTechFlags",
"offset": "0x348",
"convention": "offset",
"prototype": "uint32 flags[7] xenotech bits per target species, followed by a count word 7 at +0x364 (0x20 bytes assigned as a unit)",
"status": "verified",
"source": "B2 own disassembly pass 2026-09-08 (ReVa read-memory + objdump -b binary -m i386): ServerPlayer::OnTechResearched 0x00891790, RebuildSpeciesTechFlags 0x0082bf10, ApplyAITechBonus 0x0080e330, SetAIBenefit 0x008186b0, UpdateNodeBoreParams 0x008182c0, SelectNodeBoreParams 0x006e18e0, GetPlagueCureMask 0x00537240"
},
{
"name": "ServerPlayer_off_ResearchRollPending",
"offset": "0x3b4",
"convention": "offset",
"prototype": "bool -- a pending plague-cure roll; run and cleared when the completing tech is the current research target",
"status": "verified",
"source": "B2 own disassembly pass 2026-09-08 (ReVa read-memory + objdump -b binary -m i386): ServerPlayer::OnTechResearched 0x00891790, RebuildSpeciesTechFlags 0x0082bf10, ApplyAITechBonus 0x0080e330, SetAIBenefit 0x008186b0, UpdateNodeBoreParams 0x008182c0, SelectNodeBoreParams 0x006e18e0, GetPlagueCureMask 0x00537240"
},
{
"name": "ServerPlayer_off_AIRebellion",
"offset": "0x3b8",
"convention": "offset",
"prototype": "AIRebellion* -- non-null while an AI rebellion object exists; the two AI techs notify it",
"status": "verified",
"source": "B2 own disassembly pass 2026-09-08 (ReVa read-memory + objdump -b binary -m i386): ServerPlayer::OnTechResearched 0x00891790, RebuildSpeciesTechFlags 0x0082bf10, ApplyAITechBonus 0x0080e330, SetAIBenefit 0x008186b0, UpdateNodeBoreParams 0x008182c0, SelectNodeBoreParams 0x006e18e0, GetPlagueCureMask 0x00537240"
},
{
"name": "TechTree_off_Master",
"offset": "0x4",
"convention": "offset",
"prototype": "MasterTechTree* -- the `this` MasterTechTree::IsTech / GetTechDef are called on",
"status": "verified",
"source": "B2 own disassembly pass 2026-09-08 (ReVa read-memory + objdump -b binary -m i386): ServerPlayer::OnTechResearched 0x00891790, RebuildSpeciesTechFlags 0x0082bf10, ApplyAITechBonus 0x0080e330, SetAIBenefit 0x008186b0, UpdateNodeBoreParams 0x008182c0, SelectNodeBoreParams 0x006e18e0, GetPlagueCureMask 0x00537240"
},
{
"name": "ServerPlayer_UpdateNodeBoreParams",
"addr": "0x008182c0",
"convention": "fastcall",
"prototype": "void (ServerPlayer* this) -- re-selects the node-bore parameters from the researched set and stores them in the +0x308 block, allocating it on first use and freeing it when no bore drive is researched",
"status": "verified",
"source": "B2 own disassembly pass 2026-09-08 (ReVa read-memory + objdump -b binary -m i386): ServerPlayer::OnTechResearched 0x00891790, RebuildSpeciesTechFlags 0x0082bf10, ApplyAITechBonus 0x0080e330, SetAIBenefit 0x008186b0, UpdateNodeBoreParams 0x008182c0, SelectNodeBoreParams 0x006e18e0, GetPlagueCureMask 0x00537240"
},
{
"name": "SelectNodeBoreParams",
"addr": "0x006e18e0",
"convention": "cdecl",
"prototype": "bool (int out[3], ServerPlayer* p) -- highest researched bore drive wins: DRV_RAD {95,60,5}, else DRV_REND {65,35,4}, else DRV_RIP {45,15,3}; false (and out left at {INT_MAX,INT_MAX,0}) when none. No species gate",
"status": "verified",
"source": "B2 own disassembly pass 2026-09-08 (ReVa read-memory + objdump -b binary -m i386): ServerPlayer::OnTechResearched 0x00891790, RebuildSpeciesTechFlags 0x0082bf10, ApplyAITechBonus 0x0080e330, SetAIBenefit 0x008186b0, UpdateNodeBoreParams 0x008182c0, SelectNodeBoreParams 0x006e18e0, GetPlagueCureMask 0x00537240"
},
{
"name": "ServerPlayer_SpeciesOfTranslationTech",
"addr": "0x0080e410",
"convention": "custom",
"prototype": "int (TechDef* def in EBX) -- index of the species whose SpeciesDef+0x74 node-track tech this def is, or -1",
"status": "verified",
"source": "B2 own disassembly pass 2026-09-08 (ReVa read-memory + objdump -b binary -m i386): ServerPlayer::OnTechResearched 0x00891790, RebuildSpeciesTechFlags 0x0082bf10, ApplyAITechBonus 0x0080e330, SetAIBenefit 0x008186b0, UpdateNodeBoreParams 0x008182c0, SelectNodeBoreParams 0x006e18e0, GetPlagueCureMask 0x00537240"
},
{
"name": "AITechRow",
"addr": "0x00690f70",
"convention": "cdecl",
"prototype": "struct {int techId; float rebellionOdds; float bonus;}* (TechDef* def) -- linear search of g_AITechValueTable, NULL when the def is none of them",
"status": "verified",
"source": "B2 own disassembly pass 2026-09-08 (ReVa read-memory + objdump -b binary -m i386): ServerPlayer::OnTechResearched 0x00891790, RebuildSpeciesTechFlags 0x0082bf10, ApplyAITechBonus 0x0080e330, SetAIBenefit 0x008186b0, UpdateNodeBoreParams 0x008182c0, SelectNodeBoreParams 0x006e18e0, GetPlagueCureMask 0x00537240"
},
{
"name": "g_AITechValueCount",
"addr": "0x00aea2ec",
"convention": "data",
"prototype": "int -- number of rows in g_AITechValueTable (6)",
"status": "verified",
"source": "B2 own disassembly pass 2026-09-08 (ReVa read-memory + objdump -b binary -m i386): ServerPlayer::OnTechResearched 0x00891790, RebuildSpeciesTechFlags 0x0082bf10, ApplyAITechBonus 0x0080e330, SetAIBenefit 0x008186b0, UpdateNodeBoreParams 0x008182c0, SelectNodeBoreParams 0x006e18e0, GetPlagueCureMask 0x00537240"
},
{
"name": "ServerPlayer_off_ServerLink",
"offset": "0x8",
"convention": "offset",
"prototype": "void* -- the StrategyServer subobject pointer; StrategyServer = *(void**)(player+8) - 4. Its +0x16c is the Mars::RNG* the strategic sim draws from, and its +8 the event manager",
"status": "verified",
"source": "B2 own disassembly pass 2026-09-08 (ReVa read-memory + objdump -b binary -m i386): ServerPlayer::RollResearchEvent 0x0088df20, ResearchEventOdds 0x00820380, ServerPlayer::OnTechResearched 0x00891790"
},
{
"name": "StrategyServer_off_RNG",
"offset": "0x16c",
"convention": "offset",
"prototype": "Mars::RNG* -- the strategic generator (same object TechTree::ProcessResearch is handed)",
"status": "verified",
"source": "B2 own disassembly pass 2026-09-08 (ReVa read-memory + objdump -b binary -m i386): ServerPlayer::RollResearchEvent 0x0088df20, ResearchEventOdds 0x00820380, ServerPlayer::OnTechResearched 0x00891790"
},
{
"name": "ServerPlayer_RollResearchEvent",
"addr": "0x0088df20",
"convention": "fastcall",
"prototype": "void (ServerPlayer* this) -- odds = ResearchEventOdds(this, this->ResT); draws EXACTLY ONE NextFloat from StrategyServer's generator, unconditionally, then fires 0x00889d60 when odds > roll. Called from OnTechResearched when the completing def is the current research target and the pending-roll byte at +0x3b4 is set. This is the extra RNG draw B3 observed on a completion",
"status": "verified",
"source": "B2 own disassembly pass 2026-09-08 (ReVa read-memory + objdump -b binary -m i386): ServerPlayer::RollResearchEvent 0x0088df20, ResearchEventOdds 0x00820380, ServerPlayer::OnTechResearched 0x00891790"
},
{
"name": "ServerPlayer_ResearchEventOdds",
"addr": "0x00820380",
"convention": "thiscall",
"prototype": "float (ServerPlayer* this, TechDef* def) -- 0 when def is null; a plague-family path via 0x00535480, else the AI-rebellion path via AITechRow (odds column) gated on !NPC. Read-only, makes no draw",
"status": "verified",
"source": "B2 own disassembly pass 2026-09-08 (ReVa read-memory + objdump -b binary -m i386): ServerPlayer::RollResearchEvent 0x0088df20, ResearchEventOdds 0x00820380, ServerPlayer::OnTechResearched 0x00891790"
},
{
"name": "ServerSystem_ProcessTurn",
"addr": "0x007598e0",
"convention": "thiscall",
"prototype": "void (ServerSystem* this) /* NO stack arguments -- plain RET, nothing reads [ebp+8]; Ghidra's decompile shows a spurious second parameter. Body order: unowned Infra decay -> ApplyInfraBonus -> ApplyPopBonus -> independent pop drift -> IsStable/ntdev -> AccrueSystemBonus -> ProcessPlague -> ProcessBuildQueue -> imperial growth -> civilian growth -> AdjustResources(-out[2]) -> TRes=0 -> RefuelInOrbit(1) -> Bats2 tick -> rcex tick -> haltv[0..2]=false -> ProcessSlaves -> ProcessRebellion -> addiction sweep. Consumes no RNG itself; ProcessPlague / civilian growth / ProcessSlaves are draw-free to depth 1, and ProcessRebellion is the only consumer */",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "StrategyServer_MoveFleet",
"addr": "0x007d9ee0",
"convention": "thiscall",
"prototype": "bool (StrategyServer* this, StarFleet* fleet, float dt) /* RET 8; dt is a 4-byte float pushed with fstp DWORD [esp]; returns AL, true only when the fleet ends exactly on a waypoint destination at the deepest recursion level. range = MinRange(fleet, +0.05f) -- ADDED, not subtracted; out of range with MinRange(0)==0 zeroes the RANGE, not the step; move = min(min(range, step), distance) with no floor at 0; recursion iff fraction < (double)0.9999f with dt' = float32((1-fraction)*dt) */",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "StrategyServer_ProcessFleetMovement",
"addr": "0x007da9a0",
"convention": "thiscall",
"prototype": "void (StrategyServer* this) /* no stack args, plain RET. Pursuit schedule, not a departing/in-transit split: classify every fleet whose current waypoint targets a fleet by owner relation (0 = pursuer, else follower); pass1 prey dt 0.5, pass2 pursuers dt 0.5 (an arrival retires the pair), pass3 remaining prey dt 0.5, pass4 everything unscheduled dt 1.0 (an uncaught pursuer gets 0.5), pass5 followers dt 1.0. Then FPdpos, gate traffic, OnFleetArrived, and clears flag 0x100 on every fleet. Makes no RNG draw of its own */",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "StarFleet_MinRange",
"addr": "0x006ff6a0",
"convention": "thiscall",
"prototype": "float (StarFleet* this, float bias) /* RET 4; min over ships of ship->Range seeded with FLT_MAX (an empty fleet is unconstrained), then + bias, narrowed to float32. No ship is skipped -- range-exempt tankers still clamp the fleet */",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "StarFleet_HasAllFlags",
"addr": "0x006fe1d0",
"convention": "thiscall",
"prototype": "bool (StarFleet* this, uint32 mask) /* (flags & mask) == mask, flags at fleet+0x10c */",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "StarFleet_SetFlag",
"addr": "0x006fe1a0",
"convention": "thiscall",
"prototype": "void (StarFleet* this, uint32 mask, bool on) /* the only two writers of fleet+0x10c in the image */",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "StarFleet_ResolveWaypoint",
"addr": "0x00701390",
"convention": "thiscall",
"prototype": "void* (StarFleet* this) /* no args; NULL when the waypoint vector is empty or the front waypoint's target id does not resolve in the entity hash at fleet->galaxy(+0x10)+0x80 */",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "IsGateTransitWaypoint",
"addr": "0x0056e6e0",
"convention": "cdecl",
"prototype": "bool (int waypointType) /* 7-entry jump table: true only for 4 and 5; false default */",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "IsNodeWaypoint",
"addr": "0x0056e720",
"convention": "cdecl",
"prototype": "bool (int waypointType) /* 7-entry jump table: true only for 3. NOTE the node-LINE case of the movement switch is type 2, which this does NOT accept */",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "ProbabilisticJump",
"addr": "0x007b6700",
"convention": "thiscall",
"prototype": "void (StrategyServer* this, StarFleet* fleet, void* dest) /* waypoint type 5. v = float32(NextFloat() * player->CstE); arrives iff !(v > player->CstT) (equality arrives); on a miss the fleet is placed at dest + randomUnitVector * v -- scattered AROUND the destination by v, not advanced a fraction along the vector -- which costs a second raw draw. 1 draw on success, 2 on a miss */",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "NodeLine_Step",
"addr": "0x00705510",
"convention": "cdecl",
"prototype": "void (Vector3* posOut, bool* arrivedOut, float nodespeed, float dt, vector<StarSystem*>* systems, const Vector3* from, const Vector3* to) /* builds the stutter segments, reverses them and pops from the back (so ascending by start); plain nodespeed between spheres, a constant per-segment speed inside one; arrived = (time < dt) || |along - length| < FLT_EPSILON, and on arrival the destination is copied verbatim */",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "NodeLine_BuildStutterSegments",
"addr": "0x00705280",
"convention": "cdecl",
"prototype": "void (vector<StutterSegment>* out, vector<StarSystem*>* systems, const Vector3* from, const Vector3* to) /* chord parameters scaled to world distance and clamped to [0, length]; a chord with |start-end| <= 0.01f is dropped; std::sort ascending by start; then ONE forward pass over adjacent pairs sets BOTH boundaries of an overlap to end_i + 0.5*(end_i - start_{i+1}) -- the mirror of the midpoint, pushed forward past both chords. Nothing is dropped or clipped back, so a swallowed chord comes out inverted */",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "SegmentSphereIntersect",
"addr": "0x008a64f0",
"convention": "cdecl",
"prototype": "bool (float* tNear, float* tFar, const Sphere4* s, const Vector3* from, const Vector3* to) /* wraps the quadratic solver 0x008a62c0; rejects a sphere the segment does not reach and reports an open end as -FLT_MAX / +FLT_MAX */",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "DistPointToSegment",
"addr": "0x008e8eb0",
"convention": "cdecl",
"prototype": "float (const Vector3* p, const Vector3* a, const Vector3* b) /* projection parameter clamped to [0,1]. NOTE 0x008c8eb0 is a varargs formatter, not this */",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "ServerSystem_MaxPop",
"addr": "0x0074ab20",
"convention": "thiscall",
"prototype": "int (ServerSystem* this, ServerPlayer* p, int flag) /* RET 8 -- (player, flag), NOT (species, groupType). Species is derived from p->Species and the group type is hard-coded to 0, which makes the cross-species and INDSYS branches dead in this specialisation. Returns the int64 result clamped to [0, INT32_MAX], low dword only */",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "ServerSystem_MaxPopGeneric",
"addr": "0x0074a4a0",
"convention": "thiscall",
"prototype": "int64 (ServerSystem* this, int groupType, int species, ServerPlayer* p, float* suitOverride) /* RET 0x10 */",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "ServerSystem_IsStable",
"addr": "0x0074ad90",
"convention": "thiscall",
"prototype": "bool (ServerSystem* this) /* false without an owner, with Infra < 1.0, or with Suit != IdealSuit */",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "ServerSystem_ApplyInfraBonus",
"addr": "0x00746780",
"convention": "thiscall",
"prototype": "void (ServerSystem* this) /* returns early unless ibon > 0 and Infra < 1; resets ntdev to 0 when the system is not the owner's home system; applied = min(ibon, float32(1 - Infra)); Infra becomes EXACTLY 1.0f when applied == the remainder, else float32(Infra + applied); ibon -= applied */",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "ServerSystem_ApplyPopBonus",
"addr": "0x0074b510",
"convention": "thiscall",
"prototype": "void (ServerSystem* this) /* nothing unless pbon > 0; an unowned system drops the whole pool; returns when Pop >= MaxPop; resets ntdev for a non-home system; Pop += min(cap - Pop, pbon); pbon -= same. Pop and pbon are int32 */",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "ServerSystem_ApplyInfraDelta",
"addr": "0x00748270",
"convention": "thiscall",
"prototype": "void (ServerSystem* this, float delta) /* no-op once Infra >= 1; clamps to 1 and re-normalises the system's own output rates when it lands there */",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "ServerSystem_ApplySuitDelta",
"addr": "0x007481c0",
"convention": "thiscall",
"prototype": "void (ServerSystem* this, float delta) /* clamps at the ideal from whichever side it approached, so suitability never overshoots; re-normalises the rates when it lands exactly on the ideal */",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "ServerSystem_ComputeOutputFromRates",
"addr": "0x00751bb0",
"convention": "thiscall",
"prototype": "void (ServerSystem* this, int out[12], float rates[7]) /* RET 8. `out` is 12 dwords of MIXED type: 0 total (truncated), 1 strip-mined resources, 2 resources consumed, 3 money, 7 gross construction, 8 points to the queue, 9 points SPENT on repairs, 10 float32 infra delta, 11 float32 suitability delta. `rates` is copied to the stack first, so the caller's struct is untouched. NOT side-effect free: it repairs damaged ships in orbit via 0x00751590(points, estimateOnly=0) */",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "ServerSystem_NormaliseOutputRates",
"addr": "0x00747390",
"convention": "cdecl",
"prototype": "void (float rates[7], ServerSystem* sys, float* pinned) /* `pinned` defaults to &rates[0] (trade) and every call site passes NULL or that. Suppress terraform at the ideal (exact ==) and infra when float32(ibon+Infra) >= 1; zero any channel <= (double)1e-4f; clamp ONLY the pinned channel to [0,1]; sum the other three in float32; an exactly-zero sum seeds them with 1e-4f (honouring the suppressions); each becomes (r/sum)*(1-pinned) */",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "ServerSystem_ProcessBuildQueue",
"addr": "0x00752500",
"convention": "thiscall",
"prototype": "void (ServerSystem* this, int* resourcesConsumedOut) /* computes the turn's output vector, writes out[2] (resources consumed) through the pointer, applies out[11] via ApplySuitDelta and out[10] via ApplyInfraDelta, and feeds out[8] to BuildQueue::ProcessTurn. Increments TnsOH while SRoh > 0 && out[1] > 0 */",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "BuildQueue_ProcessTurn",
"addr": "0x00890d50",
"convention": "thiscall",
"prototype": "int (BuildQueue* this, ServerSystem* sys, int points) /* RET 8; `points` is BY VALUE and the leftover is the RETURN value. An order needing more than what is left absorbs everything and stops the pass; a design that costs money asks this->vft[9](sys, (int64)cost) and a refusal SKIPS that order and continues rather than stopping. Removal is a separate sweep afterwards that unlinks every order with conleft <= 0 */",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "ServerSystem_AdjustResources",
"addr": "0x00745f30",
"convention": "thiscall",
"prototype": "void (ServerSystem* this, int delta, int mode) /* RET 8; ProcessTurn passes -out[2] with mode 0 when the owner has AMine, else mode 3. Mode 3 takes it all from Res; mode 0 splits it proportionally over Res / ARes2 / MRes */",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "ServerSystem_PopGrowthDelta",
"addr": "0x00748100",
"convention": "thiscall",
"prototype": "int64 (ServerSystem* this, int groupType, int species) /* RET 8; 0 without an owner or when haltv[groupType] is set */",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "PopGrowthFraction",
"addr": "0x00536fb0",
"convention": "thiscall",
"prototype": "float (ServerPlayer* p, int groupType, int species, float suit, float factor) /* the growth curve. base = 1 - clamp01(min(|ideal - clamp(suit,0,20)|, SuitTol) / SuitTol) -- there is NO pop/capacity term anywhere; g = clamp01(pow(base, clamp(POPULATION_GROWTH_EXP, 0.01f, 1000))) then x MOD x PopMod x factor x groupdef[+4], each gated on a strict > 0 and each stored back to a float32 */",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "ServerSystem_SlaveDeathRate",
"addr": "0x0074b110",
"convention": "thiscall",
"prototype": "float (ServerSystem* this, int species) /* RET 4; 1.0 when unowned. mod = (bit0 ? 0.8f : 1) - 0.2*bit1 - 0.2*bit2, no clamp; rate = ((|Ideal-Suit| x BYHAZARD + DEATH_RATE) + SRs x BYOUTPUT) x mod, every step narrowed to float32 */",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "ServerSystem_ProcessSlaves",
"addr": "0x007537b0",
"convention": "thiscall",
"prototype": "void (ServerSystem* this) /* the worst plague's rate is ADDED to the death rate; SLAVES_MIN/MAX_DEATHS are each disabled by ANY negative value; deaths are clamped into [0, adjusted slave count]. Consumes no RNG */",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "ServerSystem_ProcessPlague",
"addr": "0x00756a90",
"convention": "thiscall",
"prototype": "void (ServerSystem* this) /* consumes no RNG, at depth 1 */",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "ServerSystem_ProcessRebellion",
"addr": "0x007583b0",
"convention": "thiscall",
"prototype": "void (ServerSystem* this) /* the ONLY RNG consumer in a colony turn, and its draw count is data-dependent: one RandChance per iteration of a 64-bit rebel counter (0x0074fbe0), a short-circuiting per-species roll loop (0x00753c60), one outcome roll (0x00756350) and one 0.2f continuation roll */",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "ServerSystem_GrowCivilianPops",
"addr": "0x00754220",
"convention": "thiscall",
"prototype": "void (ServerSystem* this) /* consumes no RNG, at depth 1 */",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "RNG_Chance",
"addr": "0x008e6dd0",
"convention": "stdcall",
"prototype": "bool (RNG* this /*ecx, the object*/, float p) /* p <= 0 -> false and p >= 1 -> true, both WITHOUT a draw; otherwise exactly one NextFloat and `r < p` */",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "ServerPlayer_AddMoraleEvent",
"addr": "0x00839d60",
"convention": "thiscall",
"prototype": "void (ServerPlayer* this, MoraleEvent* ev, ServerSystem* sys) /* the actual apply. 0x00752a10 is only the MoraleEvent constructor; the per-species delta is carried in ev->deltas[species] (int[7] at ev+0x18), not as an argument */",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "MoraleEvent_ctor",
"addr": "0x00752a10",
"convention": "thiscall",
"prototype": "MoraleEvent* (MoraleEvent* this) /* 0x50 bytes: {vptr, ints (+0x10 = event id), vptr2 @+0x14, int deltas[7] @+0x18, std::string name @+0x34} */",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "ServerSystem_off_Owner",
"offset": "0x00000010",
"convention": "offset",
"prototype": "StrategyServer* owner (the RAW base; the RNG accessor uses owner-4)",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "ServerSystem_off_Idx",
"offset": "0x0000005c",
"convention": "offset",
"prototype": "int Idx",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "ServerSystem_off_Size",
"offset": "0x00000060",
"convention": "offset",
"prototype": "int Size (1..10); the capacity base is Size * 100000000 as an exact int64 product",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "ServerSystem_off_Suit",
"offset": "0x00000064",
"convention": "offset",
"prototype": "float Suit",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "ServerSystem_off_Res",
"offset": "0x00000068",
"convention": "offset",
"prototype": "int Res",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "ServerSystem_off_ARes2",
"offset": "0x0000006c",
"convention": "offset",
"prototype": "int ARes2",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "ServerSystem_off_MRes",
"offset": "0x00000070",
"convention": "offset",
"prototype": "int MRes",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "ServerSystem_off_TRes",
"offset": "0x00000074",
"convention": "offset",
"prototype": "int TRes -- zeroed every turn by ProcessTurn",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "ServerSystem_off_haltv",
"offset": "0x00000078",
"convention": "offset",
"prototype": "bool haltv[3] -- growth halt per group; cleared every turn by ProcessTurn",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "ServerSystem_off_OutMod",
"offset": "0x0000007c",
"convention": "offset",
"prototype": "float OutMod",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "ServerSystem_off_TAcq",
"offset": "0x00000080",
"convention": "offset",
"prototype": "int TAcq (turn acquired)",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "ServerSystem_off_Rates",
"offset": "0x00000088",
"convention": "offset",
"prototype": "StarSystem::OutputRates (0x1c): float SRt, SRsc, SRtf, SRi, SRoh, SRs; int SRnr",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "ServerSystem_off_SRoh",
"offset": "0x00000098",
"convention": "offset",
"prototype": "float SRoh -- the over-harvest slider (Rates + 0x10)",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "ServerSystem_off_BuildQueue",
"offset": "0x000000a4",
"convention": "offset",
"prototype": "BuildQueue*",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "ServerSystem_off_Name",
"offset": "0x000000a8",
"convention": "offset",
"prototype": "std::string Name (0x1c, MSVC SSO; capacity word at +0xbc)",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "ServerSystem_off_Abdn",
"offset": "0x000000c4",
"convention": "offset",
"prototype": "bool Abdn",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "ServerSystem_off_Bats2",
"offset": "0x000000f0",
"convention": "offset",
"prototype": "int64 Bats2 -- player i's 4-bit battle-recent counter at bits [4i, 4i+4), i < 15",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "ServerSystem_off_Rcex",
"offset": "0x000000f8",
"convention": "offset",
"prototype": "int64 rcex -- the same shape for the explored/recon-recent counter",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "ServerSystem_off_PID",
"offset": "0x00000100",
"convention": "offset",
"prototype": "ServerPlayer* PID (null = unowned)",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "ServerSystem_off_Morale",
"offset": "0x0000011c",
"convention": "offset",
"prototype": "Morale cm (0x20: vptr + int[7])",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "ServerSystem_off_Fleets",
"offset": "0x0000016c",
"convention": "offset",
"prototype": "std::vector<StarFleet*>",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "ServerSystem_off_Pop",
"offset": "0x0000018c",
"convention": "offset",
"prototype": "int Pop (imperial)",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "ServerSystem_off_Infra",
"offset": "0x00000190",
"convention": "offset",
"prototype": "float Infra",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "ServerSystem_off_pbon",
"offset": "0x00000194",
"convention": "offset",
"prototype": "int pbon -- the pending population bonus (int32, not a float)",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "ServerSystem_off_ibon",
"offset": "0x00000198",
"convention": "offset",
"prototype": "float ibon -- the pending infrastructure bonus",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "ServerSystem_off_Pop2",
"offset": "0x000001a0",
"convention": "offset",
"prototype": "Population Pop2 (civilians): {vptr, vector<PopulationGroup> @+4}, 24-byte entries {?, int type @+4, int species @+8, int64 count @+0x10}",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "ServerSystem_off_pbon2",
"offset": "0x000001b4",
"convention": "offset",
"prototype": "Population pbon2 -- the civilian analogue of pbon",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "ServerSystem_off_Indi",
"offset": "0x000001c8",
"convention": "offset",
"prototype": "IndependenceInfo* indi (int indsp at +4)",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "ServerSystem_off_rbfl",
"offset": "0x000001dc",
"convention": "offset",
"prototype": "int rbfl -- rebelling-species bitmask",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "ServerSystem_off_adt",
"offset": "0x000001e4",
"convention": "offset",
"prototype": "int adt[7] -- per-species addiction start turn; 0 means never addicted",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "ServerSystem_off_BatsMask",
"offset": "0x000002a0",
"convention": "offset",
"prototype": "uint32 -- bit i is cleared when player i's Bats2 counter reaches 0",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "ServerSystem_off_RcexMask",
"offset": "0x000002a4",
"convention": "offset",
"prototype": "uint32 -- the same for rcex",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "ServerSystem_off_TnsOH",
"offset": "0x000002b8",
"convention": "offset",
"prototype": "int TnsOH (turns over-harvesting)",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "ServerSystem_off_ntdev",
"offset": "0x000002c4",
"convention": "offset",
"prototype": "int ntdev -- turns developing; ++ when IsStable, reset to 0 otherwise AND by either bonus-apply helper on a non-home system. This is the second SYSTEMBONUS_MINTURNS gate, not rbtn",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "ServerSystem_off_rbtn",
"offset": "0x000002cc",
"convention": "offset",
"prototype": "int rbtn",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "StrategyServer_off_ModCount",
"offset": "0x00000008",
"convention": "offset",
"prototype": "int ModCount (the turn counter), relative to the RAW server base a ServerSystem's +0x10 points at",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "StrategyServer_off_Players",
"offset": "0x00000050",
"convention": "offset",
"prototype": "std::vector<ServerPlayer*> (begin @+0x50, end @+0x54); numPlayers = (end-begin)>>2",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "StrategyServer_off_Fleets",
"offset": "0x00000064",
"convention": "offset",
"prototype": "std::vector<StarFleet*> (begin @+0x64, end @+0x68) -- one flat global list, not per player",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "StrategyServer_off_EntityHash",
"offset": "0x00000084",
"convention": "offset",
"prototype": "16-bucket hash of entity id -> object, hashed by (key & 0xF); 0x008b9240 is the lookup",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "StrategyServer_off_RNGPtr",
"offset": "0x00000168",
"convention": "offset",
"prototype": "Mars::RNG* relative to the RAW base (== the -4-adjusted base's +0x16c)",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "StrategyServer_off_ArrivedSet",
"offset": "0x00000204",
"convention": "offset",
"prototype": "std::set<pair<FleetId,LocationId>> cleared at the head of ProcessFleetMovement",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "StrategyServer_off_InMotionSet",
"offset": "0x00000214",
"convention": "offset",
"prototype": "std::set<pair<FleetId,LocationId>> that MoveFleet fills for fleets still in motion; OnFleetArrived takes the set difference",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "StarFleet_off_Id",
"offset": "0x00000004",
"convention": "offset",
"prototype": "int id (also the entity-hash key)",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "StarFleet_off_Galaxy",
"offset": "0x00000010",
"convention": "offset",
"prototype": "the object whose +0x80 holds the entity hash the waypoint target is resolved in",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "StarFleet_off_Pos",
"offset": "0x00000018",
"convention": "offset",
"prototype": "Vector3 Pos",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "StarFleet_off_PrvPos",
"offset": "0x0000004c",
"convention": "offset",
"prototype": "Vector3 PrvPos -- set to the ENTRY position when the fleet moved",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "StarFleet_off_PID",
"offset": "0x00000058",
"convention": "offset",
"prototype": "ServerPlayer* owner",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "StarFleet_off_Location",
"offset": "0x000000a0",
"convention": "offset",
"prototype": "Location*; kind at Location+0x14 (0 system, 1 fleet, 2 point)",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "StarFleet_off_Ships",
"offset": "0x000000a4",
"convention": "offset",
"prototype": "std::vector<StarShip*> (begin @+0xa4, end @+0xa8)",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "StarFleet_off_GateTraffic",
"offset": "0x000000c0",
"convention": "offset",
"prototype": "int16 -- SIGNED, summed into the owner's gate traffic",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "StarFleet_off_Waypoints",
"offset": "0x000000c8",
"convention": "offset",
"prototype": "std::vector<Waypoint> _Myfirst (proxy @+0xc4, last @+0xcc, end @+0xd0)",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "StarFleet_off_Speed",
"offset": "0x000000d8",
"convention": "offset",
"prototype": "float FPsp2 -- the fleet's strategic speed",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "StarFleet_off_DestPos",
"offset": "0x000000ec",
"convention": "offset",
"prototype": "Vector3 -- set after every pass to the current waypoint target's position. The notes called this FPogn2; by the FlightPlan layout it is FPdpos",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "StarFleet_off_Flags",
"offset": "0x0000010c",
"convention": "offset",
"prototype": "uint32; bit 0x100 = held this turn (cleared for every fleet at the end of ProcessFleetMovement), bit 0x2 cleared in the destination pass, bit 0x1 set by MoveFleet when the fleet moved",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "Waypoint_stride",
"offset": "0x0000001c",
"convention": "offset",
"prototype": "sizeof(Waypoint) -- pinned by the divide-by-28 reciprocal multiply in the iterator arithmetic",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "Waypoint_off_Target",
"offset": "0x00000004",
"convention": "offset",
"prototype": "int -- the destination entity id",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "Waypoint_off_Type",
"offset": "0x00000008",
"convention": "offset",
"prototype": "int -- 2 node line, 3 node route, 4 gate teleport, 5 probabilistic jump, otherwise a straight run",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "StarShip_off_Flags",
"offset": "0x00000018",
"convention": "offset",
"prototype": "uint64 flag pair; bit 0x1000 exempts the ship from movement fuel and marks it a tanker",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "StarShip_off_Range",
"offset": "0x00000020",
"convention": "offset",
"prototype": "float Range -- remaining strategic range",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "StarShip_off_MaxRange",
"offset": "0x00000078",
"convention": "offset",
"prototype": "float -- the cap the refuel helper clamps Range to",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "ServerPlayer_off_HomeSystem",
"offset": "0x0000002c",
"convention": "offset",
"prototype": "ServerSystem* -- the bonus-apply helpers skip the ntdev reset for this system",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "ServerPlayer_off_GateTraffic",
"offset": "0x0000014c",
"convention": "offset",
"prototype": "int GTraf -- assigned (not accumulated) once per turn",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "ServerPlayer_off_CstE",
"offset": "0x00000154",
"convention": "offset",
"prototype": "float CstE -- the probabilistic jump's efficiency",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "ServerPlayer_off_CstT",
"offset": "0x00000158",
"convention": "offset",
"prototype": "float CstT -- the probabilistic jump's threshold",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "g_ptr_SYSTEMBONUS_MINTURNS",
"addr": "0x00aeca10",
"convention": "data",
"prototype": "int** -- the GlobalConst pointer slot; the storage word is *slot",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "g_ptr_SYSTEMBONUS_POPBONUS",
"addr": "0x00aeca18",
"convention": "data",
"prototype": "float** -- pointer slot",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "g_ptr_SYSTEMBONUS_INFRABONUS",
"addr": "0x00aeca20",
"convention": "data",
"prototype": "float** -- pointer slot",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "g_ptr_SYSTEMBONUS_POPBONUS_INC",
"addr": "0x00aeca38",
"convention": "data",
"prototype": "float** -- pointer slot",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "g_ptr_SYSTEMBONUS_INFRABONUS_INC",
"addr": "0x00aeca40",
"convention": "data",
"prototype": "float** -- pointer slot",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "g_ptr_ADDICTION_PHASE2_START",
"addr": "0x00aeca58",
"convention": "data",
"prototype": "int** -- pointer slot (image default 10)",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "g_ptr_ADDICTION_PHASE3_START",
"addr": "0x00aeca60",
"convention": "data",
"prototype": "int** -- pointer slot (image default 15)",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "g_IndependentPopDriftRate",
"addr": "0x00aeca80",
"convention": "data",
"prototype": "float -- 0.05f, a hard-coded literal in .data, NOT a GlobalConst; both ServerSystem::ProcessTurn calls to the independent pop-drift helper pass it",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "g_ptr_STUTTER_SYSTEM_INFLUENCE_RADIUS",
"addr": "0x00aebc44",
"convention": "data",
"prototype": "float** -- pointer slot; storage 0x00b212cc (shipped value 2)",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "g_ptr_STUTTER_MIN_SPEED",
"addr": "0x00aebc48",
"convention": "data",
"prototype": "float** -- pointer slot; storage 0x00b212d0 (shipped value 0.33)",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
},
{
"name": "g_ptr_STUTTER_MAX_SPEED",
"addr": "0x00aebc4c",
"convention": "data",
"prototype": "float** -- pointer slot; storage 0x00b212d4 (shipped value 0.33). NOTE min == max in the shipped data, so the stutter ramp collapses to a constant 0.33x inside any influence sphere",
"status": "verified",
"source": "handoff/b4-colony-movement.md"
}
]
}