1694 lines
71 KiB
JSON
1694 lines
71 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="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/<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": "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; 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"
|
|
}
|
|
]
|
|
}
|