sots-re/campaign/open-questions.md

10 KiB
Raw Blame History

Open questions

Each links to the finding that raised it. Promoted to backlog or closed by re-quartermaster.

  • Compiler archive mismatch — imports say MSVCR100/MSVCP100 (VS2010), but Ghidra applied windows_vs12_32 datatypes (closest available). Confirm the exact toolchain from the PE Rich header; decide which datatype archive best fits. (from 01-fingerprint)
  • Battle-load root cause — inferred as single-threaded engine + core-count-sensitive code; must be confirmed by a ForceSingleCore=0 vs =1 diff once the main loop is mapped. (from 00-strategy)
  • Save-editor struct coverage — community editors cover players/tech/systems; fleet/combat state coverage may be partial. Treat as strong hints, verify each field against real saves.
  • Lockstep determinism — @SYNC ERROR DETECTED: Frame %d indicates a deterministic lockstep sim. Big for reimplementation + old-vs-new differential (determinism makes equivalence checkable frame-by-frame). Confirm scope. (from strings-and-config)
  • Mods override mechanism — .gob = gobio::ZipFileSystem with a NativeFileSystem loose-file override; this is how ./Mods wins over archived files. Verify load order. (from strings-and-config)
  • Battle-load root cause — the shipped fix is a blunt process-affinity pin (FUN_0089ee70) with NO CPU-topology detection, so there is no adaptive branch. The real cause is emergent multi-core contention: which thread(s)? Investigate the 3 CreateThread sites (FUN_00902350, FUN_00736e30, startup FUN_008a0e50), the streaming-sound thread, and lockstep sync waits. (from ghidra-recon)
  • Save-struct contradictions (R1 vs R2) — field widths (Abdn/Dstyd/ltis Int16 vs Int32; Bats2), R2's OID = PID*16 owner-handle claim, species id 4 (_NPC vs 'AI Rebellion'). Resolve against the binary's Streamable read code. (from save-editor-structs)
  • Unlabeled save blocks — CdPlayer (unknown1..35), empty SimSystemDetailSpy, opaque ~2500 B RNG blob. Analyst targets once the Streamable readers are located. (from save-editor-structs)
  • Missing HUD scripts — exe references GUI/Combat/CombatHUD.script, SensorHUD.script, NoHUD.script but none ship in the gobs or loose; likely dev-only overrides via the gobio native-FS fallback. Confirm via CombatScreen load path. Also: .script files are display configs, not widget layouts (corrects round-one note). (from ui-screen-map)
  • Tech allows default per-race % — tech_tree.json edges only carry races written in the allows string; the default for an unlisted race (believed 100%) is engine code. Ghidra target in the tech loader. (from data-parsers)
  • Engine parser leniency — 12 shipped shipsections are syntactically broken (unclosed {, extra }) yet load; keys and identifiers are case-insensitive. Reimplementation must match this leniency. .effect is its own TXT/BEGIN-END format, not brace-block (corrects round one). (from data-parsers)
  • Struct recovery leftovers — PlayerColorID exact on-disk width (writer FUN_008b9cb0 undecompiled); ServerSystem+0x10 owner type; TechTree per-tech body; CdPlayer block. R2's OID = PID*16 is an id-allocation pattern, not in this code. (from struct-recovery)
  • Resolved (R1/R2 contradictions) — Bats2/rcex are int64 (R2 wrong); Abdn/Dstyd bools, ltis int; TRM/CstR/E/T/shrm/RefCap/RepCap/PlayerView Infra are floats; pswd string; TShn/ETS/diplomacy counters int16 in memory, int32 on disk; Nexp carries xid/xmin/xmax/xper; FtOrig is Vector3. (from struct-recovery)
  • Battle-load, narrowed — sim/combat load run on the main thread; the only other threads are net watchdog, TIME_CRITICAL audio streaming (g_musicCS), and a star-map mesh builder. Hypothesis: audio-thread critical-section contention or D3D9 runtime/driver threads on many cores. Needs a dynamic profile (x32dbg / ETW) under the software-GPU stack. (from turn-spine)
  • Spine leftovers — StrategyServer struct partial (41 fields); static-initialiser region 0x009be000–0x009c1400 undisassembled; Mars::Stream vftable not located; several small ProcessTurn phase fns unnamed. (from turn-spine)
  • Save framing ambiguities (settle on first real save) — padding joint [len][name][value][pad] vs split; bool vs int for names with len%4==0 (no type byte); on-disk tags for Summary/CreateParameters and count/element tags inside framed arrays unknown (positional for now). A real 3-char bool tag (NPC,Dep,hsp) settles padding. (from SAVE_FORMAT)
  • Strict-parse gaps vs real save (verifier output, round 3) — (1) ServerSystem: 7/28 systems carry VFlags where Name/vnh were expected — a conditional layout (uncolonized/special systems?); (2) halt[].haltv is a 31/35-byte struct, not a bool; (3) optional indi (IndependenceInfo?) precedes NVE/PID in some systems; (4) PrisonerHold.prisoners[] entries lack PrNSp; (5) /createParams/key is int; (6) turnstats.hist.stats.tch is int; (7) optional trailing zdsi/zdst. Padding = joint (settled). 7 resyncs / 2728 raw bytes ≈ RNG blob. (from turn2-strict-issues)
  • RESOLVED: strict-parse gaps — 5/8 were reader bugs (ASCII plausibility test applied to string values; empty string == int 0), 3 real: PrNSp only when PrMax>0; indi unconditional in NVO nodes (system-level gated by hindi); zdsi/zdst pairs under zdsc. Key is an empty string; tch int. Summary/CreateParams tag lists confirmed. Reader patch in flight. (from schema-gaps-resolved)
  • RESOLVED: save framing — padding is joint; "." = NULL tag (VectorHelper count+elements, Vector3 bodies); empty strings are 4 zero bytes; CDT is a tagged frame + opaque CD frames. All three real saves parse --strict clean. Note: SvSctOb IS present in all saves (patch doc §7 was wrong). (from SAVE_FORMAT)
  • (parked) Combat loader job API — does tactical-battle loading go through the job helpers 0x8fc160/0x8fa5b0 with the TryEnterCriticalSection→Sleep(1000) completion poll (0x0071ea60)? If yes, that is a plausible many-core stall (lost try-lock = +1 s per poll). End Turn also has a deliberate Sleep(computed) ≈0.85 s at 0x00838da9. (from battle-load-profile)
  • Ship-design code rules (Ghidra, from SHIP_DESIGN_RULES.md §8) — hidden default rider designs are built WITHOUT tech gating (where?); is section class-equality enforced or merely unoffered; confirm the turret class-accept table (standard ← {standard,missile,grapple}, strafe ← {standard}; grapple weakest); hull-class tech injection (IND_CruisCon/IND_DreadCon) + station exemption; option defaults/auto-upgrade and what option_cost multiplies; weapon cost per bank vs per mount; mass/speed formulas; faiDes/dWep meaning. (from SHIP_DESIGN_RULES)
  • Corrections from turn internals — ServerPlayer+0xf9 is the AI flag (not bTurnDone); FUN_007d7f70 is encounter detection; ProcessTurn phase 2 is trade, not diplomacy; the spine's FUN_00840fe0 'income' is special projects — income is FUN_00863030 ComputeBudget. TECHBEN_* data blocks are inert (effects hard-coded via the 116-entry tech-name table @0x00a19718). Open: TRA/TRP producers, FPsp2 derivation, PERGATETRAFFIC_* readers, exact BnkEl/BnkPr expression. (from strategic-turn-internals)
  • Mars::Application::Initialize signature (P2-M0 finding) — a C++ __thiscall detour that called the original crashed inside Initialize; the trace hook now uses a convention-agnostic asm stub. Verify the real signature (stack args? EDX use? return value?) before any compare/replace hook on it. Same caution for every [unverified] entry in ghidra/addresses.json. (from m0)
  • Formula gaps surfaced by the game/sim port (feed back to Ghidra) — which bankruptcy limit carries the 3.3 factor and when the bankruptcy start turn is stamped; the suitability→carrying-capacity hazard curve shape; the trade-points→money system-income tail; the POPBONUS_INC population increment; the expense-slider request term; which running total the tech-income bonus / savings aid read; node-line speed clamp at the influence radius; whether DecayAllResearch also hits the current target. (from game-sim)
  • RESOLVED: Initialize signature — bool __thiscall Application::Initialize(this, AppStartup*), RET 4; Run = void(this); OnTick = bool(this). Plain thiscall wrappers are safe for Run/OnTick; Initialize needs the extra arg. (from loader-prototypes)
  • PARITY BUG (fix in mars/text + Python flat_kv): duplicate config keys are FIRST-occurrence-wins in the engine (entry erased on use; duplicates log 'multiply defined'); our readers implement last-wins. Also the brace parser DROPS a final key/value pair that lacks a trailing newline, and uses whitespace-only delimiting (braces not delimiters) with `" ' `` quotes and no escapes — mars/parse must match these for compare mode; verify on the 12 broken sections + a no-trailing-newline fixture. (from loader-prototypes)
  • Not traced end-to-end — Species/_NPC/weapons/*.weapon loading and the .effect dictionary entry EffectDictionary_Load (0x008b42b0, unverified). (from loader-prototypes)
  • RESOLVED: End-Turn oracle — deterministic and cross-process: compare autosaves by sha256, no masking. Only when diffing a re-save of a loaded post-turn autosave: mask Player.Status (→0) and recompute/ignore Summary.Checksum (additive). (from determinism-oracle)
  • SAVE_FORMAT tag corrections (fix Python reader + spec) — real on-disk tags: otnF (not ontF) in Odes/Owep/Otch, nextid (not nextId) in NdGr2, Design = FAIDes/DHide/DWep/DName; ords/wpts are real tags. Python's positional R() matching hid these. RNG: float mapping (float)(y*2^-32), next_int mask, and lazy-vs-eager twist at left==0 still need binary confirmation. (from mars-stream)
  • RESOLVED: tech allows default — unlisted species = 1.0 (confirmed in FUN_005822d0 tree-creation roll, strategic-turn-internals §2); game/data uses 100. Still open from game/data: what the engine's converter does with the 34 malformed tokens (force_right o, crew false, 1.0f, 0-5, 90\); repeated scalars in a block are last-wins per the sequential if/else consumers (loader-prototypes §M3) — confirm on a fixture in compare mode. (from game-data)