sots-re/ghidra/addresses.d/ag.json
alex 48db3cc425 lane AG: the gate-indexed RNG audit -- 31 unread-gate rows, 6 new draw sites, the SVSO generator named
One row per statically reachable draw site in BeginProcessTurn u ProcessTurn u
OnAllCombatDone_Tail, each carrying a measured firing or a decoded predicate on
save fields plus the 22-save corpus count. No total is reported: 31 rows (30
distinct sites plus one grouped SVSO row) are unread gates, and that visible
blank column is the deliverable.

Three gates decoded:
  0x0082cdb8 = the addiction spread roll (Chance ADDICTION_SPREAD_ODDS 0.2f);
    corpus 0 of 22 -- nadct is 0 on all 28 systems of all 22 saves. Writer found:
    ServerSystem_BeginAddiction has exactly two callers, both inside vslot15, so
    an INTER-EMPIRE route plus a species addict tech must seed it first.
  0x0088dc43 = the spy counter-mission roll, p = cmo accumulating 0.2f per turn,
    so Chance costs 0 words from the fifth turn on; corpus 0 of 22 by deat/cm.
  0x0088b613 = the raid target pick. RNG_NextInt has no zero-bound early-out, so
    a single-candidate list still draws -- which proves lane AC's B returned
    before the draw. Failed conjunct named: the raider sat on sector 832 while
    all four rt records source in 816/816/768/752. Next workload: sector 816.

Six draw sites no firing-indexed table could hold: five behind
CreateRaidEncounter's empty-candidate-list gate, and 0x00820c1b again from
Slot13RngCalleeB -- so B costs 2 words on a success, not 0.

SVSO generator closed: all 17 drawing overrides use GetGame()->+0x16c.
Rule 17 again: ServerSpyManager_vslot14 really ends at 0x0088dd2b, not 0x0088dd03.
2026-09-08 22:25:52 -04:00

180 lines
13 KiB
JSON

{
"entries": [
{
"name": "ServerSystem_GetAddictionPhase",
"addr": "0x00745e40",
"convention": "thiscall",
"prototype": "int __thiscall (ServerSystem* this, int speciesIdx) // 91 B, RET 4. Reads this->addiction[speciesIdx] at +0x1e4+4k -- the int[7] table serialised as `nadct` + sparse (`ads`,`adt`) pairs. Returns 0 when the slot is 0; otherwise 1/2/3 by (Frame - stamp) against *StrategyVar_ADDICTION_PHASE2_START (10) and *_PHASE3_START (15). EVERY non-zero return path ends in `setne al`, so a caller reading AL gets the BOOLEAN `addiction[k] != 0` and the phase is discarded -- which is how ServerTradeManagerImpl_vslot15 uses it",
"status": "verified",
"source": "findings/control-flow/gate-indexed-rng-audit.md (lane AG 2026-09-09); disassembled from dumps/sots.exe to the next function start"
},
{
"name": "ServerSystem_BeginAddiction",
"addr": "0x0074ef70",
"convention": "thiscall",
"prototype": "void __thiscall (ServerSystem* this, int speciesIdx) // 621 B, RET 4. Refuses if this->+0x100 (owner) is null, if bit 5 of owner->[0x348 + 4*speciesIdx] is set, or if ServerSystem_GetAddictionPhase already reports the slot stamped. Otherwise computes a money term, posts EVENTSUM/EVENTMSG_ADDICTION_OUTBREAK and writes this->addiction[speciesIdx] = GetGame()->Frame(+0xc). DRAWS NOTHING. ONLY TWO CALLERS IMAGE-WIDE, both inside ServerTradeManagerImpl_vslot15 (0x0082cd50 the un-rolled seed grant, 0x0082cdc4 behind the Chance) -- so this function is the sole way any system becomes addicted, and the Chance-gated spread cannot fire until the un-rolled grant has seeded it",
"status": "verified",
"source": "findings/control-flow/gate-indexed-rng-audit.md (lane AG 2026-09-09)"
},
{
"name": "SpeciesDef_AddictTechForSpecies",
"addr": "0x0053b620",
"convention": "thiscall",
"prototype": "void __thiscall (int speciesIdx /*ECX*/, int* out /*EAX*/) // jump table at 0x0053b6e8, seven entries; Ghidra's size 78 truncates before the table (rule 17). Writes the 10000-based TechID of that species' ADDICT TECH: k=0 0x2799, 1 0x279a, 2 0x279b, 3 0x279c, 4 and 5 -> 0xC5 (the image's `no tech` sentinel), 6 0x279d. Out-of-range logs `Species %s: No addict tech.` (0x009f9630). Called once, from InitTable 0x005453a0 at 0x005458a7, filling SpeciesDef+0x88 -- which is BIT 4 of ServerPlayer+0x348+4k in ServerPlayer_RebuildSpeciesTechFlags, the bit that gates the un-rolled addiction grant. NOT a wire techId, so tools/techid_table.py does not name these",
"status": "verified",
"source": "findings/control-flow/gate-indexed-rng-audit.md (lane AG 2026-09-09)"
},
{
"name": "StrategyVar_ADDICTION_SPREAD_ODDS",
"addr": "0x00af0c4c",
"convention": "data",
"prototype": "float -- the probability ServerTradeManagerImpl_vslot15 passes to Mars::RNG::Chance at 0x0082cdb8, reached as `mov ecx,ds:0x00af0c50; fld [ecx]` (0x00af0c50 holds a pointer to this storage word). Image default 0.2f, strictly inside (0,1), so Chance takes NEITHER early-out and the site costs EXACTLY ONE WORD per evaluation. Registered by a GlobalConst static-init stub against Data/Strategy/StrategyVars.txt with the %f parser 0x008b7020",
"status": "verified",
"source": "findings/control-flow/gate-indexed-rng-audit.md (lane AG 2026-09-09); name resolved from the M1 loader stub, findings/subsystems/loader-prototypes.md"
},
{
"name": "StrategyVar_ADDICTION_PHASE2_START",
"addr": "0x00aeca54",
"convention": "data",
"prototype": "int -- image default 10. ServerSystem_GetAddictionPhase returns 2 rather than 1 once (Frame - addiction[k]) exceeds this. Pointer at 0x00aeca58. Dead for the vslot15 gate, which only reads the boolean",
"status": "verified",
"source": "findings/control-flow/gate-indexed-rng-audit.md (lane AG 2026-09-09)"
},
{
"name": "StrategyVar_ADDICTION_PHASE3_START",
"addr": "0x00aeca5c",
"convention": "data",
"prototype": "int -- image default 15. ServerSystem_GetAddictionPhase returns 3 once (Frame - addiction[k]) exceeds this. Pointer at 0x00aeca60",
"status": "verified",
"source": "findings/control-flow/gate-indexed-rng-audit.md (lane AG 2026-09-09)"
},
{
"name": "ServerSystem_IsIndependent",
"addr": "0x00743fa0",
"convention": "thiscall",
"prototype": "bool __thiscall (ServerSystem* this) // 12 B. `return this->+0x1c8 != 0` -- the IndependenceInfo* that the wire carries as `hindi` (present) + `indi`. ServerSpyManager_vslot14 rejects a spy whose target system passes this, which is conjunct G3 of the 0x0088dc43 counter-mission roll",
"status": "verified",
"source": "findings/control-flow/gate-indexed-rng-audit.md (lane AG 2026-09-09)"
},
{
"name": "ServerSpyManager_vslot14_RealEnd",
"addr": "0x0088dd2b",
"convention": "marker",
"prototype": "The last byte of ServerSpyManager_vslot14's body (the `ret`). GHIDRA REPORTS 387 BYTES, i.e. an end of 0x0088dd03, WHICH IS INSIDE THE FUNCTION -- a third instance of rule 17 after 0x007aa240 and 0x00893290. A five-entry jump table for the mission-stage switch sits at 0x0088dd2c..0x0088dd3f, past both the Ghidra end and the real end; padding then runs to the next function start 0x0088dd40",
"status": "verified",
"source": "findings/control-flow/gate-indexed-rng-audit.md (lane AG 2026-09-09)"
},
{
"name": "TradeSector_FleetRaidsOwnRoutes",
"addr": "0x0080ecf0",
"convention": "cdecl",
"prototype": "bool __cdecl (StarFleet* fleet) // 91 B. `fleet && GetLocationAsTradeSector(fleet) && !FUN_00813ab0(fleet,0x80000,0) && TradeSector_PlayerTradesHere(sector, fleet->owner) && !FUN_006fe1d0(fleet, 0x800)`. TradeManager_Slot13RngCalleeB uses it to choose between two candidate filters. IT IS DETERMINISTICALLY FALSE for any fleet that reached B through vslot13: TradeSector_FleetMayRaid (0x0080ed50) demands the OPPOSITE on the last two conjuncts, so the `raid your own routes` arm of B is unreachable from the tail. PlayerAlliances_Relation returning 3 for self is the second, independent reason",
"status": "verified",
"source": "findings/control-flow/gate-indexed-rng-audit.md (lane AG 2026-09-09)"
},
{
"name": "TradeManager_ResolveRouteEndpointOwner",
"addr": "0x00841cd0",
"convention": "thiscall",
"prototype": "ServerTradeManager method, 974 B. The function that DISAMBIGUATES Game::TradeRoute's endpoint fields: `if (rt->trfrs(+0x10) == 0) endpoint = GetGame()->Systems(+0x44)[rt->trfr(+0xc)] else endpoint = registry_lookup(GetGame()+0x84, rt->trfrs)`. So trfr / trto are SYSTEM INDICES into StrategyServer's system vector and trfrs / trtos are non-system endpoint IDS with 0 meaning `use the index`. Only the endpoint-resolution head of this function was read; the rest is unread",
"status": "mapped",
"source": "findings/control-flow/gate-indexed-rng-audit.md (lane AG 2026-09-09)"
},
{
"name": "TradeManager_ComputeRaidInterceptPoint",
"addr": "0x00820af0",
"convention": "thiscall",
"prototype": "bool __thiscall (ServerTradeManager* this, Vector3* out, TradeRoute* rt, void* arg3) // 425 B, RET 0xC. Places the raid interception point along the route: a = Systems[rt->trfr]->Pos, b = Systems[rt->trto]->Pos, dist = |b-a|; frac = 1.0 when the route owner's species (+0x5c) is 1 or when either of two species tests on arg3 passes, ELSE frac = float(NextFloat()) drawn at 0x00820c1b; out = a + dir*(frac*dist + <double 0x009e20a0>). TWO CALLERS: 0x0082cf65 (inside CreateRaidEncounter's subtree) and 0x0088b62c (TradeManager_Slot13RngCalleeB, immediately after the target pick)",
"status": "verified",
"source": "findings/control-flow/gate-indexed-rng-audit.md (lane AG 2026-09-09)"
},
{
"name": "TradeManager_RaidInterceptPoint_DrawSite",
"addr": "0x00820c1b",
"convention": "site",
"prototype": "call RNG_NextFloat (0x0047d830) inside TradeManager_ComputeRaidInterceptPoint, ECX = GetGame()->RNG(+0x16c) + 4. ONE STRATEGIC-GENERATOR WORD, unless the species short-circuit at 0x00820bc2/0x00820bd7/0x00820bf2 sets frac = 1.0 first. NOT IN ANY EARLIER INVENTORY. It makes TradeManager_Slot13RngCalleeB cost TWO words on a success (this plus RNG_NextInt at 0x0088b613), not the 0 lane AC measured on a state whose candidate list was empty",
"status": "verified",
"source": "findings/control-flow/gate-indexed-rng-audit.md (lane AG 2026-09-09)"
},
{
"name": "TradeManager_BuildRaidCandidates",
"addr": "0x0083b110",
"convention": "thiscall",
"prototype": "ServerTradeManager method called at 0x008938ed as the FIRST act of ServerTradeManager_CreateRaidEncounter; fills the local vector at [ebp-0x1c]. When it comes back EMPTY, CreateRaidEncounter returns false at 0x008938fa -- BEFORE every draw in the function and its subtree. This is the gate lane H measured as `entered 3 times, 0 words'. Body not read",
"status": "mapped",
"source": "findings/control-flow/gate-indexed-rng-audit.md (lane AG 2026-09-09)"
},
{
"name": "CreateRaidEncounter_EmptyCandidateListReturn",
"addr": "0x008938fa",
"convention": "site",
"prototype": "`jne 0x00893932` -- taken when TradeManager_BuildRaidCandidates produced a non-empty vector; the fall-through frees the vector and returns false with AL = 0. EVERY draw in CreateRaidEncounter and its subtree lies past this branch: 0x008939ee, then through 0x00892640 the five sites 0x0088f1eb, 0x0088f356, 0x0083b460, 0x00848fd9, 0x00820c1b. tail-rng-ledger.md section 11.1's `a success costs 0 or 1 further word' is right about the corpus and wrong about the code",
"status": "verified",
"source": "findings/control-flow/gate-indexed-rng-audit.md (lane AG 2026-09-09)"
},
{
"name": "TradeManager_BuildRaidEncounter",
"addr": "0x00892640",
"convention": "thiscall",
"prototype": "ServerTradeManager method, single caller ServerTradeManager_CreateRaidEncounter at 0x00893a25 (i.e. past the empty-candidate-list gate AND past the target pick at 0x008939ee). Calls TradeManager_SpawnEncounterSquadron 0x0088f070 (@0x008926ce), 0x00848e50 (@0x008926d9) and 0x0082ce00 (@0x00892700). The whole draw subtree of a SUCCESSFUL raid encounter hangs off it and has never been reached by any measurement in this campaign",
"status": "verified",
"source": "findings/control-flow/gate-indexed-rng-audit.md (lane AG 2026-09-09)"
},
{
"name": "SpawnEncounterSquadron_DrawSite1",
"addr": "0x0088f1eb",
"convention": "site",
"prototype": "call RNG_NextInt (0x004271c0) inside TradeManager_SpawnEncounterSquadron 0x0088f070, generator loaded at 0x0088f1da from +0x16c. One word plus its rejection loop. Reachable only past CreateRaidEncounter_EmptyCandidateListReturn",
"status": "verified",
"source": "findings/control-flow/gate-indexed-rng-audit.md (lane AG 2026-09-09)"
},
{
"name": "SpawnEncounterSquadron_DrawSite2",
"addr": "0x0088f356",
"convention": "site",
"prototype": "call RNG_NextInt (0x004271c0) inside TradeManager_SpawnEncounterSquadron 0x0088f070, generator loaded at 0x0088f334. Same reachability",
"status": "verified",
"source": "findings/control-flow/gate-indexed-rng-audit.md (lane AG 2026-09-09)"
},
{
"name": "RaidEncounter_SquadronHelper",
"addr": "0x0083b1e0",
"convention": "thiscall",
"prototype": "Called only from TradeManager_SpawnEncounterSquadron at 0x0088f0f7. Draws RNG_NextInt at 0x0083b460 on the strategic generator (loaded at 0x0083b44c). Body not read; listed because the sweep places it inside CreateRaidEncounter's unreached subtree",
"status": "mapped",
"source": "findings/control-flow/gate-indexed-rng-audit.md (lane AG 2026-09-09)"
},
{
"name": "RaidEncounter_SquadronHelper_DrawSite",
"addr": "0x0083b460",
"convention": "site",
"prototype": "call RNG_NextInt (0x004271c0) inside 0x0083b1e0, generator from +0x16c at 0x0083b44c",
"status": "verified",
"source": "findings/control-flow/gate-indexed-rng-audit.md (lane AG 2026-09-09)"
},
{
"name": "RaidEncounter_Helper848e50",
"addr": "0x00848e50",
"convention": "thiscall",
"prototype": "Called only from TradeManager_BuildRaidEncounter at 0x008926d9. Draws RNG_NextInt at 0x00848fd9 on the strategic generator (loaded at 0x00848fc8). Body not read",
"status": "mapped",
"source": "findings/control-flow/gate-indexed-rng-audit.md (lane AG 2026-09-09)"
},
{
"name": "RaidEncounter_Helper848e50_DrawSite",
"addr": "0x00848fd9",
"convention": "site",
"prototype": "call RNG_NextInt (0x004271c0) inside 0x00848e50, generator from +0x16c at 0x00848fc8",
"status": "verified",
"source": "findings/control-flow/gate-indexed-rng-audit.md (lane AG 2026-09-09)"
},
{
"name": "RaidEncounter_Helper82ce00",
"addr": "0x0082ce00",
"convention": "thiscall",
"prototype": "Called only from TradeManager_BuildRaidEncounter at 0x00892700; calls TradeManager_ComputeRaidInterceptPoint at 0x0082cf65, which is where the NextFloat at 0x00820c1b is spent on this path. Body not read",
"status": "mapped",
"source": "findings/control-flow/gate-indexed-rng-audit.md (lane AG 2026-09-09)"
}
]
}