{ "_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 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 -> 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 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 /* +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="edFlag, 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//_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* mountPaths)", "status": "verified", "source": "handoff/loader-prototypes.md#m4" }, { "name": "gobio_FileSystemSet_ctor", "addr": "0x008d6d60", "convention": "thiscall", "prototype": "FileSystemSet* (FileSystemSet* this, std::vector* 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* 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* 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; 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 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, 4 researched); ctor writes 0", "status": "verified", "source": "B3 own disassembly pass 2026-09-08 (objdump/pei-i386): 0x005876c0, 0x0047d830, 0x00426e00, 0x0049fdf0, 0x004271c0, 0x0057da00, 0x008914a5" }, { "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": "NodeLine_Step", "addr": "0x00705510", "convention": "cdecl", "prototype": "void (Vector3* posOut, bool* arrivedOut, float nodespeed, float dt, void* lines, Vector3* from, Vector3* to)", "status": "unverified", "source": "handoff/formula-gaps.md#q7" }, { "name": "NodeLine_BuildStutterSegments", "addr": "0x00705280", "convention": "cdecl", "prototype": "void (vector* segsOut, vector* systems, Vector3* from, Vector3* to)", "status": "unverified", "source": "handoff/formula-gaps.md#q7" }, { "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; int a; float value; }[6] /* values not dumped */", "status": "unverified", "source": "handoff/tech-effects.md#1" }, { "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 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 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 (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" } ] }