13 KiB
13 KiB
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_32datatypes (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=0vs=1diff 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 %dindicates 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::ZipFileSystemwith aNativeFileSystemloose-file override; this is how./Modswins 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 3CreateThreadsites (FUN_00902350,FUN_00736e30, startupFUN_008a0e50), the streaming-sound thread, and lockstep sync waits. (from ghidra-recon) - Save-struct contradictions (R1 vs R2) — field widths (
Abdn/Dstyd/ltisInt16 vs Int32;Bats2), R2'sOID = PID*16owner-handle claim, species id 4 (_NPCvs 'AI Rebellion'). Resolve against the binary's Streamable read code. (from save-editor-structs) - Unlabeled save blocks —
CdPlayer(unknown1..35), emptySimSystemDetailSpy, 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.scriptbut none ship in the gobs or loose; likely dev-only overrides via the gobio native-FS fallback. Confirm viaCombatScreenload path. Also:.scriptfiles are display configs, not widget layouts (corrects round-one note). (from ui-screen-map) - Tech
allowsdefault per-race % —tech_tree.jsonedges only carry races written in theallowsstring; 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..effectis its ownTXT/BEGIN-ENDformat, not brace-block (corrects round one). (from data-parsers) - Struct recovery leftovers —
PlayerColorIDexact on-disk width (writerFUN_008b9cb0undecompiled);ServerSystem+0x10owner type;TechTreeper-tech body;CdPlayerblock. R2'sOID = PID*16is an id-allocation pattern, not in this code. (from struct-recovery) - Resolved (R1/R2 contradictions) —
Bats2/rcexare int64 (R2 wrong);Abdn/Dstydbools,ltisint;TRM/CstR/E/T/shrm/RefCap/RepCap/PlayerViewInfraare floats;pswdstring;TShn/ETS/diplomacy counters int16 in memory, int32 on disk;Nexpcarriesxid/xmin/xmax/xper;FtOrigis 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 —
StrategyServerstruct partial (41 fields); static-initialiser region 0x009be000–0x009c1400 undisassembled;Mars::Streamvftable 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 carryVFlagswhereName/vnhwere expected — a conditional layout (uncolonized/special systems?); (2)halt[].haltvis a 31/35-byte struct, not a bool; (3) optionalindi(IndependenceInfo?) precedesNVE/PIDin some systems; (4)PrisonerHold.prisoners[]entries lackPrNSp; (5)/createParams/keyis int; (6)turnstats.hist.stats.tchis int; (7) optional trailingzdsi/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:
PrNSponly whenPrMax>0;indiunconditional inNVOnodes (system-level gated byhindi);zdsi/zdstpairs underzdsc.Keyis an empty string;tchint. 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;CDTis a tagged frame + opaqueCDframes. All three real saves parse--strictclean. Note:SvSctObIS 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/0x8fa5b0with theTryEnterCriticalSection→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 deliberateSleep(computed)≈0.85 s at0x00838da9. (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 whatoption_costmultiplies; weapon cost per bank vs per mount; mass/speed formulas;faiDes/dWepmeaning. (from SHIP_DESIGN_RULES) - Corrections from turn internals —
ServerPlayer+0xf9is the AI flag (not bTurnDone);FUN_007d7f70is encounter detection; ProcessTurn phase 2 is trade, not diplomacy; the spine'sFUN_00840fe0'income' is special projects — income isFUN_00863030ComputeBudget.TECHBEN_*data blocks are inert (effects hard-coded via the 116-entry tech-name table @0x00a19718). Open: TRA/TRP producers,FPsp2derivation,PERGATETRAFFIC_*readers, exact BnkEl/BnkPr expression. (from strategic-turn-internals) Mars::Application::Initializesignature (P2-M0 finding) — a C++__thiscalldetour 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 anycompare/replacehook on it. Same caution for every[unverified]entry inghidra/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_INCpopulation increment; the expense-slider request term; which running total the tech-income bonus / savings aid read; node-line speed clamp at the influence radius; whetherDecayAllResearchalso 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/*.weaponloading and the.effectdictionary entryEffectDictionary_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/ignoreSummary.Checksum(additive). (from determinism-oracle) - SAVE_FORMAT tag corrections (fix Python reader + spec) — real on-disk tags:
otnF(notontF) in Odes/Owep/Otch,nextid(notnextId) in NdGr2, Design =FAIDes/DHide/DWep/DName;ords/wptsare real tags. Python's positional R() matching hid these. RNG: float mapping(float)(y*2^-32),next_intmask, and lazy-vs-eager twist atleft==0still need binary confirmation. (from mars-stream) - RESOLVED: tech
allowsdefault — unlisted species = 1.0 (confirmed inFUN_005822d0tree-creation roll, strategic-turn-internals §2);game/datauses 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) - RESOLVED: SAVE_FORMAT tag names — all corrections confirmed by bytes and applied to the Python reader + spec (§10). Residual doc debt:
findings/objects/save-editor-structs.mdandverify/design-rules/SHIP_DESIGN_RULES.mdstill quote R1'sontF/faiDes/nextIdspellings (R1-provenance; annotate rather than rewrite). (from SAVE_FORMAT) - Notes disagree on MSVC-2010
std::stringlayout —struct-recovery.md§0 vsturn-spine.md§1.1 give different offsets; pin both from the_Myres >= 16SSO branch inStream::WriteStringand correct the loser. (from re-windows-2000s-howto) - RESOLVED: formula gaps (all 8) — see formula-gaps; game/sim's low-confidence functions can now be pinned. RESOLVED: std::string =
_Bx@0,_Mysize@0x10,_Myres@0x14,_Alval@0x18, sizeof 0x1c (struct-recovery §0 right; turn-spine §1.1 WRONG — annotated). CORRECTION: the 116-entry table @0x00a19718 is a name-membership list, not the effects table; useg_TechIdNames(196) +OnTechResearched. Open: values of the 6-entry AI-tech bonus table (0x00a17888); producer of ServerPlayer +0x224/+0x228/+0x22c beyond the setup-record copy (0x0077b620). (from tech-effects) - RESOLVED: RNG semantics —
Seedisthiscall(this, uint32)RET 4 (MT19937 init + immediate twist);Twisttakesthisin ECX only; twist is LAZY (if (left==0) Twist()inside the draw),leftlives at+0x9c4, confirming the save blob layout. float mapping, needs a targeted check (not a red flag):NextFloatdecompiles as returningfloat10, but that is simply how x86 float returns look (value inST(0)), so it is weak evidence of extended-precision computation; a singley * 2^-32multiply rounds once either way. B3 should still compare the mapping explicitly (exact constant and whether the draw isy*2^-32,(y>>8)*2^-24, or divided by 2^32-1), and the x87-vs-SSE float-parity policy from the RE how-to is still needed before the x64 standalone.RNG_NextIntsignature still unverified. (own Ghidra pass) - Struct-modelling hazard (found by M2) — an MSVC-2010
std::vectormember is three words, so a naive C translation put the next pointer at+0x18when it is really at+0x14; the hook silently reported the unmodelled word (string bytes"TION") as a real field. Any hand-modelled game struct must pin its size withstatic_assertand account for 3-word vectors. (from M2) - x87 precision-control mode at runtime — 53-bit MSVC default vs 24-bit if D3D9 grabbed the FPU; moves only the last bit (~0.094% of words).
float_from_pc24()models it and the B3 hook logsfpu_cwper call, so the first B3 trace settles it. (from B3) - Some truths are unreachable by compare — B3's draw-divisor bug differed on 0.78% of draws yet flipped 0 decisions in 1e6, so a behavioural compare would have passed while the code was wrong. Static reading of the instruction stream remains necessary; compare validates, it does not discover. (from B3)