diff --git a/ghidra/addresses.d/lane-sd.json b/ghidra/addresses.d/lane-sd.json index 6595a82..3a82721 100644 --- a/ghidra/addresses.d/lane-sd.json +++ b/ghidra/addresses.d/lane-sd.json @@ -48,6 +48,14 @@ "prototype": "Mars::String -- the section's file name on a Game::ShipSectionDef; capacity word at +0x1c8, so the payload is the inline buffer when capacity < 0x10 and *(char**)(def+0x1b4) otherwise. The composer compares it case-insensitively against \"DEPointDefence\" and \"CRPointDefence\" at 0x006ae4e6/0x006ae4fd: a section whose name matches gets a point-defence weapon in EVERY qualifying bank instead of every D'-th one", "status": "verified", "source": "findings/subsystems/ship-design-composer.md -- lane SD 2026-09-08; instruction-stream read" + }, + { + "name": "AIChooseWeaponForBank", + "addr": "0x006ad2a0", + "convention": "cdecl", + "prototype": "int (StrategyAIAgent* agent, int hullClass, std::vector* restrictTo, int bankClass, int bankSize, void* ctx, int flag) /* seven stack arguments, caller cleans 0x38 for the two calls at 0x006ae3c1/0x006ae3dc. Returns a weapon id, or 0 when nothing fits. The composer calls it in the weapon-assignment loop and then TWICE more just before the point-defence pass: once with a one-element list holding the literal 0x25 (read as the point-defence weapon family -- INFERRED, from the DEPointDefence/CRPointDefence section names the same pass matches and the 0.2 probability it rolls) and once unrestricted. A null from the unrestricted call skips the whole point-defence block; a null from the restricted one makes every iteration short-circuit without drawing. Both make the loop-carried draw cost zero, which is why lane SD's probe records the two return values separately */", + "status": "verified", + "source": "findings/subsystems/ship-design-composer.md -- lane SD 2026-09-08; instruction-stream read of the two call sites and the gates they feed" } ] } \ No newline at end of file