# Sword of the Stars 1 (SOTS1) — Save-File Struct Reference (Community RE) Harvested from two community save-game editors for cross-checking against binary-recovered serializable types. SOTS1 uses a self-describing "Streamable" serialization system: **field order + type matter**, and most values are preceded by their own name string. Target game version: **SOTS1 v1.8+** (both editors target the 1.8.x line; repo2 says "1.80 +19"). ## Sources - **[R1] BardezAnAvatar/Sots.Sots1.SavedGameEditor** — C#. A *complete, ordered, typed* Streamable parser. Migrated from SourceForge `sots-sge`. This is the authoritative layout evidence (read/write methods reproduce exact on-disk order). Key files under `Bardez.Project.SwordOfTheStars.DataStructures/`: - `BaseSaveStructures.cs` (primitives + framing), `SharedSaveStructures.cs` (coords/colors), `SaveGameDataStructure.cs` (top level), `SummarySaveStructures.cs`, `CreateParametersSaveStructures.cs`, `SimulationSaveStructures.cs` (11,974 lines — the bulk), `CdTableSaveStructures.cs` (combat/AI table). IO: `Bardez.Project.SwordOfTheStars.IO/{Gzip,SaveFileIO}.cs`. - **[R2] ghbplayer/SOTSedit** — C#/WPF. A *name-tag scanner* (does NOT model layout; it searches the decompressed blob for length-prefixed field-name strings and reads the value that follows). Value comes from `Parse.cs` + the field catalog `SOTSEdit.cfg` (friendly-name ⇄ serialized-name ⇄ type mapping) and race/semantic hacks. Written 2011 "to learn C#"; author calls the parser weak. Both editors independently confirm the same primitives, framing markers, race IDs, and color IDs. --- ## 1. File format & serialization mechanics ### 1.1 Container - **Whole `.sav` file = gzip stream.** Decompress first (`GZipStream`, standard gzip). [R1 Gzip.cs, R2 gzip.cs] - Editors work on the **decompressed** byte stream (R1 names its test artifacts `*.sav.inflate.dat`). - No separate magic/version header is decoded by either editor beyond the top-level Summary block; version is implied by scenario/field presence, not a numeric version field. Endianness: **little-endian** throughout (`BitConverter` on x86). ### 1.2 Decompressed top-level order [R1 SaveGameData.ReadFromStream] ``` SaveGameData: 1. SummarySaveStruct summary 2. CreateParametersSaveStruct createParams 3. SimSaveStruct sim <-- the giant one (players/systems/fleets/etc.) 4. CdTable cdTable <-- combat / AI state table ``` ### 1.3 Primitive encodings [R1 BaseSaveStructures.cs] Text encoding is **windows-1252** (R1) / ASCII (R2). - **StringStruct** (raw string): `Int32 length` + `length` bytes (no NUL terminator counted). R2 calls this a "BStr": 4-byte little-endian length prefix + ASCII bytes. - **Padding rule (critical):** every *basic* value struct is **NUL-padded to a 4-byte boundary**. `PaddingSize = 4`. Padding is computed over `(sizeof(Int32 desc-length) + description.Length + valueBytes)`. - **Named value fields** (`BasicSaveStruct` subclasses) are each laid out as: `StringStruct description` (a field-name tag, often the non-descriptive `"."`) → then the value → then NUL padding to 4 bytes. Concrete leaf types: | Struct | Payload after description tag | |---|---| | `Int32SaveStruct` | 4-byte Int32 | | `Int64SaveStruct` | 8-byte Int64 | | `FloatSaveStruct` | 4-byte IEEE Single | | `BooleanSaveStruct` | 1 byte (0/1), padded to 4 | | `StringSaveStruct` | nested StringStruct (len+bytes) | | `ByteArraySaveStruct` | raw bytes (length externally known) | So the on-disk shape of a named scalar is: `[len][name-ascii][pad] [value] [pad]`. R2 exploits exactly this: it locates a field by searching for `[len][name]` and reads the value immediately after. ### 1.4 Complex-struct framing (the "BEEFBEEF" envelope) [R1 ComplexSaveStruct] Every **complex** structure is framed: ``` StringStruct description (NUL-padded to 4) UInt32 0xBEEFBEEF (begin marker) ... body (ordered child fields) ... UInt32 0x41104110 (end marker = bitwise NOT of 0xBEEFBEEF) ``` `0xBEEFBEEF` / `~0xBEEFBEEF (0x41104110)` bracket every complex object — a reliable resync/validation signature when scanning the binary. (`ISotsStructure` leaf types are NOT framed; only `ComplexSaveStruct`.) ### 1.5 Array conventions [R1 BaseSaveStructures.cs] - **ComplexArraySaveStruct**: framed (has description + BEEFBEEF), body = `Int32SaveStruct count` then `count` × T. - **NonComplexArraySaveStruct**: NOT framed; body = `Int32SaveStruct count` then `count` × T. (Distinguishing which arrays are framed vs. not is itself layout evidence — see per-struct notes.) ### 1.6 Conditional & polymorphic reads (watch for these in the binary) - **Optional-by-flag:** a boolean/int gate precedes an optional sub-object. - `SimPlayerColorSaveStruct`: `Int32 colorIndex`; **iff `colorIndex == -1`**, an `RgbColorInt32` (custom RGB) follows. Otherwise palette index only. - `SimPlayerDesignDw2SaveStruct` (weapon slot): `Boolean bId`; if true → `Int32 wId`, else → `StringSaveStruct wfn` (weapon full resource path); then `Int32 dId`. - `SimFleetShipDetails`: `Boolean hbq` gates `bq`; `Boolean hsp` gates `sp`. Fleet flight-plan/lay gated by `hfPlan` / `hLay` booleans. - `SimSystemDetailNvo.isInd` gates independent-colony sub-block; `SimSystemDetailsIndi.hindi`, `SimSystemDetailsVonNeumann.vnh` similar boolean gates. - **Polymorphism by string tag:** `SimSvSctObXscn` reads `StringSaveStruct xcsn`, then switches: `"crowdefs"`, `"gmtrigger"`, `"traps"`, `"indsys"`/default → different body subclass. - **Polymorphism by fixed position:** `SimScSctObEncObjArray` (grand-menace/encounter objects) reads a count then dispatches subclass **by index 0..8** in fixed order: `0 Infest, 1 Dsn, 2 AsteroidMonitor, 3 TD, 4 WD, 5 Hives, 6 Rsuc, 7 Dfts, 8 Ini2`. ### 1.7 Write-time quirks worth knowing (R2) - R2 edits in place and cannot safely change string length (it truncates/space-pads to the original length). R1 rewrites the whole stream and re-pads. If the binary stores string lengths, the game reads them dynamically (R1 proves round-trip works when re-padded). - R2 planet OID/PID hack: on-disk **`OID = PID * 16`** (R2 divides by 16 to show a "PlayerID"). i.e. the raw owner id field is the player index shifted left 4 bits. --- ## 2. Enums / ID tables (agreed by both editors) ### 2.1 Species / race ID [R2 Parse.cs addRace(); R1 PlayerSlot.FxSp comment] | ID | Species | |---|---| | 0 | Human | | 1 | Hiver | | 2 | Tarka(s) | | 3 | Liir | | 4 | `_NPC` / AI-rebellion / grand-menace player (R1: "??? AI Rebellion") | | 5 | Zuul | | 6 | Morrigi | ### 2.2 Player color ID (palette index) [R1 PlayerSlot.FxCrId & SimPlayerColor] `01 Red, 02 Yellow, 03 Blue, 04 Pink/Magenta, 05 Orange, 06 Green, 07 Aqua, 08 Gray, 09 Dark Green, 10 Purple`. Value **-1 ⇒ custom RGB triplet follows** (see §1.6). ### 2.3 Difficulty [R1 PlayerSettings.Difficulty] `0 Easy, 1 Normal, 2 Difficult`. ### 2.4 Sentinel values seen in fields `0x7FFFFFFF (Int32.MaxValue)` used as "tag"/unset (PlayerSlot.tag); `team = -1 (0xFFFFFFFF)` = no team; R2: value `-1` = field absent in this save. No named C# enums exist for tech IDs / weapon families — techs and weapons are **string resource names** (e.g. tech `tNm`, weapon `wfn`), not numeric enums. Weapon *family* enumeration lives only in the CdAi combat block as `aiSitWepFams` (opaque int set). --- ## 3. Summary block [R1 SummarySaveStructures.cs] (complex) ### SummarySaveStruct (fields in on-disk order) 1. `StringSaveStruct gameName` 2. `Int32 turn` 3. `Int32 numSys` (system count) 4. `Int32 checkSum` 5. `ComplexArray players` 6. `SessionSaveStruct session` 7. `Int32 mapShape` 8. `Int32 incMod` (income modifier) 9. `Int32 resMod` (research modifier) 10. `Boolean alliances` 11. `Boolean teams` 12. `Boolean encounters` 13. `StringSaveStruct scenario` ### PlayerSlotWrapper (complex): `{ PlayerSlotSaveStruct slot; Int32 rank; }` ### PlayerSlotSaveStruct (complex) — new-game slot definition, ordered: `Boolean isPlay, isDead, isReq, isRec, isFxNm` → `String fxNm` (fixed name) → `Boolean isFxSp` → `Int32 fxSp` (species, §2.1) → `Boolean isFxCr` → `NestedInt32 fxCrId` (color, §2.2) → `Boolean isFxBd` → `String fxBd` (badge) → `Boolean isFxAv` → `String fxAv` (avatar) → `Int32 tag` (often 0x7FFFFFFF) → `Int32 pwd` (password) → `Int32 team` (-1=none) → `PlayerSettingsSaveStruct settings`. ### PlayerSettingsSaveStruct (complex): `Int32 initialTreasury, initialColonies, initialTechnologies, difficulty (§2.3)`. ### SessionSaveStruct (complex) → `TmrsSaveStruct tmrs`: `Int32 tstl (0x7F7FFFFF), tctl (0x42700000), tqtl (0x7F7FFFFF), tqtle (0)` — timer limits (float bit-patterns stored as int). --- ## 4. CreateParameters block [R1 CreateParametersSaveStructures.cs] (complex) ### CreateParametersSaveStruct (ordered): `String name; Int32 id; Int32 rSeed (random seed); Int32 aid; String key;` `MapPSaveStruct mapP;` `Int32 mapS; Int32 mapF; Int32 nSys;` `Float rEnc (random-encounter rate);` `Int32 sDist;` `Float sSize; Float sRes;` `Int32 sSuit; Int32 maxP; Int32 aSpec;` `Boolean bAlly; Int32 nTeam; Boolean tmgrp;` `Int32 pSav (start savings); Int32 pCol (start colonies); Int32 pTech (start techs);` `Float incM; Float resM; ScrpSaveStruct scrp;` ### MapPSaveStruct (complex) — initial map/galaxy generation: 1. `Int32 unknown1` 2. `ComplexArray planetArray` 3. `NonComplexArray> players` (per-player int arrays; "non-complex array of players") 4. `ComplexArray npcArray` (≈ players − 1; independents/NPCs) ### PlanetSaveStruct (complex) — initial star node geometry: `SpatialCoordinate coordinates (x,y,z floats)`, `Int32 unknown1..4` (values seen: `0x7FFFFFFF`, `0x7F7FFFFF`). NOTE: this is the *map-generation* planet record; the *live* planet/colony state lives in `SimSystemDetailsSaveStruct` (§8). ### MapPNpc (complex): `Int32 unknown1, unknown2`. ### ScrpSaveStruct (complex): `Int32 spc`. ### Shared value types [R1 SharedSaveStructures.cs] - `SpatialCoordinateSaveStruct` (complex): `Float x, y, z`. - `RgbColorFloat` (leaf): `Float r,g,b`. `RgbaColorFloat` (leaf): `RgbColorFloat rgb; Float a`. - `RgbColorInt32` (leaf): `Int32 r,g,b`. --- ## 5. Simulation block — top level [R1 SimulationSaveStructures.cs] ### SimSaveStruct (complex) — the master world state, ordered: ``` String keyPath Int32 nMsz, nMlc, nMnx (next-id / size counters) NonComplexArray playerIds, designIds, systemIds, fleetIds, shipIds, tradeIds Int32 modCount, frame, gameId AttributeSaveStruct attribute RngSaveStruct rng (ByteArray unknownData ~2500 bytes: RNG state) String gameName Int32 map, incMod, resMod, enAl, enTm, gOTurn NestedInt32 gOWinPly Int32 npcm, npco, npci, npcv, npca, szad, rsad, suad ComplexArray sprjs (shared/special research projects) Float randEncAdjustment Int32 cmbtid (next combat id) ComplexArray turnstats (per-turn per-player history) NonComplexArray creps (combat reports) Int32 ninv, allExc1, allExc2, allExcCF NonComplexArray players <-- EMPIRES (§6) SimSpeciesArraySaveStruct species (galaxy species list) NonComplexArray systems <-- STAR SYSTEMS (§8) SimNodeGrid2 ndgr2 (node-line / warp graph) SimTradeManager trdmgr (§9) SimSpyManager spymgr (Int32 xsid, nspy) NonComplexArray flt <-- FLEETS (§10) NonComplexArray acts SimSvSctOb svSctOb (scenario/encounter objects, §11) Int32 zdsc, zdsi, zdst (Zuul/system-destroyer counters) ``` ### Small shared sim types - `SimPopGSaveStruct` (complex): `Int32 popT; Int32 popS; Int64 popC` (pop type / species / civ count). - `ResearchSaveStruct` (leaf): `NonComplexArray us; String nm; String ntg`. `ResearchOptionalSaveStruct`: `Int32 usc, usp`. - `TurnPly` (leaf): `Int32 ply; PlyHistSaveStruct hist`. - `PlyHistSaveStruct` (complex): `Int32 ply; PlyHistStatsSaveStruct[] stats`. - `PlyHistStatsSaveStruct` (complex): `Int64 pop; ComplexArray sacq, slost;` `Int32 trn, almem, inc, tdinc, sav, col, bat, tch; NonComplexArray cls`. - `PlyHistStatsSacq` (complex): `Int32 set, ses, seop, senp; NonComplexArray seo`. - `PlyHistClsSaveStruct` (leaf): `Int32 cls, shpt, shpl, shpk, satt, satl, satk` (ship/sat built/lost/killed by class). --- ## 6. Player / Empire [R1 SimulationSaveStructures.cs] ### SimPlayerSaveStruct (leaf wrapper): `Int32 playerId; SimPlayerDetailsSaveStruct details`. ### SimPlayerDetailsSaveStruct (complex) — the empire record, on-disk order: ``` SimPlayerTechTree techTree <-- TECH TREE (§7) Int32 homeSystem, playerIndex String playerName Int32 species (§2.1) SimPlayerColorSaveStruct colorId (palette idx or -1 + RGB, §1.6/§2.2) String badge, avatar Int32 team, sav (savings) Float idealSuit, suitTolerance, maxOH Float resRate, resModifier, resScl (research) Int32 trm, trp, tra Float outMod, rebOutMod, scOutMod, incMod, popMod, terraMod (economy multipliers) Boolean aMine; Float minPure, minRate; Int32 ngts, prGtTrf, gTraf Int32 cstR, cstE, cstT, maint, shrm, status, elim Boolean npc, rebAi, reqCL SimPlayerTeamSaveStruct teamStruct (Int32 alid, al, na, cf) Int32 hasVac, hasImm, npTrak, hasDisc, hasDiscSp, hasDiscCl, hasEnc, hasEng SimPlayerEventsSaveStruct events (Int32 evNxId; ComplexArray) NestedInt32 fngNum Int32 pvSav, pvMA, aibN Boolean cnTrd, cnRad, hgs, hadvs, harcc, cnVItl Float pddm Int32 bankWrn, bankTrn, bankPr, bankEl SimPlayerShipRecsEventsSaveStruct shipRecs Int32 nextPrjId, plcy, pswd, lret, nmeid Boolean cdp SimPlayerSpySaveStruct spy2 (Int32 defc2, rtc, evc, ttc) SimPlayerCivrSaveStruct civR (Float smx; ComplexArray) Int32 aidf Boolean srn; Int32 srcTo, lboid, lcid2 String resTnm Boolean resErrRoll SimPlayerModsSaveStruct conMods (3× SimPlayerConModSaveStruct {Float conMod, savMod}) NonComplexArray ownerIds NonComplexArray designs <-- SHIP DESIGNS (§7.2) NonComplexArray droneDesigns NonComplexArray notes (Int32 ntSys; String ntTxt; Int32 ntTrn) NonComplexArray pr (Float prm; Int32 prbt) Boolean hasAiRebellion, cta NestedInt32 aienf NonComplexArray nSprj Int32 nexp, nWeapXcl ComplexArray ovjs (objectives: Int32 id; Bool cmp; Int32 spr; String dsc; Int32 nid) ComplexArray dipStats (diplomacy; see below) ComplexArray comms (Int32 msgt; SimPlayerCommMsg msg) ComplexArray preps ComplexArray odes ComplexArray owep ComplexArray otch NestedInt32 aid Int32 ndeflay, rdtc, tnc ``` - `SimPlayerDipStat` (complex): `Int32 other; SimPlayerDipStatDetail nap, ally, cf; Int32 deadhome`. `SimPlayerDipStatDetail` (leaf): `Int32 last_, last_bty, bkn_, bty_`. - `SimPlayerCommMsg` (complex): `Int32 cid2, snd, rcp, exp, sent, rcpt, sys`. - `SimPlayerPrepSaveStruct` (complex): `Int32 oid, pid, flds, sav, home, ncol, mpwr, mcls, mmsl, nshp, nsat`. - `SimPlayerOdes/Owep/Otch` (complex, "old design/weapon/tech" build history): `Odes {Int32 ontF, otnL, odid, opid}`, `Owep {Int32 ontF, otnL, odet; String owep; Int32 owith}`, `Otch {Int32 ontF, otnL, odet; String otch; Int32 owith}`. - `SimPlayerDetailsSpecialProjectT` (leaf): `Int32 sprjT; SimPlayerDetailsSpecialProjectDetails sprj`. `...Details` (complex): `Int32 stp; SpecialProjectSpi spi; tail (polymorphic AsMon | Tech)`. `...Spi` (complex): `Int32 sPid, sts; Float cst; Int32 mxC; String name; Int32 trns`. tail `AsMon`: `Int32 rDn, sys, rMn, rMx; Float rMd`; tail `Tech`: `Int32 rDn; Float aOdd, aInc; String tch; Int32 rCst`. --- ## 7. Tech tree & ship designs ### 7.1 Tech tree [R1] - `SimPlayerTechTree` (complex): `NonComplexArray tree; NonComplexArray techs`. - `SimPlayerTechTreeBranch` (leaf): `String tNm` (tech name); `NonComplexArray branches` (child tech names). - `SimPlayerTechTreeTech` (leaf), ordered: `String tNm` (tech name) → `Int32 st` → `Int32 tResCost` (research cost) → `Int32 tResDone` (progress) → `Int32 tAcq` (turn acquired) → `Int32 tiAcq` (turns-to-acquire) → `Int32 tbd` → `Boolean tfc` → `Int32 tUnlck` (unlocked flag). **Techs are identified by string name, not a numeric enum.** ### 7.2 Ship designs [R1] - `SimPlayerDesignEntrySaveStruct` (leaf): `Int32 designId; SimPlayerDesignSaveStruct design`. - `SimPlayerDesignSaveStruct` (complex), ordered: `Boolean faiDes; Boolean dHide; Int32 dWep; String dName;` `SimPlayerDesignSectionArray sections;` `Int32 dtc; NonComplexArray dwgv`. - `SimPlayerDesignSectionArray` (leaf): `Int32 count` + `count` × `SimPlayerDesignSectionEntrySaveStruct`. - `SimPlayerDesignSectionEntrySaveStruct` (complex): `SimPlayerDesignSectionSaveStruct dsec` (Int32 unknown1, unknown2); `ComplexArray dgbnk2;` `ComplexArray dOpts`. - `SimPlayerDesignUnknown1SaveStruct` (complex) → `SimPlayerDesignDw2SaveStruct dw2` (weapon slot, conditional — see §1.6): `Boolean bId; (bId? Int32 wId : String wfn); Int32 dId`. - `SimPlayerDesignDwg` (complex): `NonComplexArray wgng`; `SimPlayerDesignGng` (leaf): `Int32 wgid; SimPlayerDesignDwgWgb wgb` (complex: `Int32 unknown1..3`). --- ## 8. Star systems & planets [R1] - `SimSystemSaveStruct` (leaf): `Int32 sysId; SimSystemDetailsSaveStruct details`. - **`SimSystemDetailsSaveStruct` (complex)** — the live system+colony record, on-disk order: ``` SpatialCoordinate pos RgbaColorFloat starColor Int32 idx Int32 size (1-10) Float suit (climate hazard) Int32 res, aRes, mRes (resources / asteroid / extra) Boolean noRebAi Int32 tRes, pop ComplexArray popG Float infra Int32 pvPop; ComplexArray pvPopG; Float pvInfra, pvSuit; Int32 pvRes, pvARes2, pvMRes; Bool pvNoRebAi (previous-turn snapshot) SimSystemDetailRtsSaveStruct rts (Float sRs, sRt, sRsc, sRtf, sRi, sRoh, sRnr — IO allocations) Int32 abdn; Boolean dstyd; Int32 tnsOh Float outMod, repCur, repMax Int32 ntdev, pbon ComplexArray pbon2 Float ibon Int32 ltis, rbfl, rbtn, rbfr, rbwn, hsrg NonComplexArray halt (Int32 haltt; Bool haltv) SimSystemDetailsVonNeumann vnm (Bool vnh gate → details: Bool vnd, vnex3, vnpex3) String name SimSystemDetailFlags1 flags1 (Int32 vFlags, eFlags, aFlags, fFlags, gFlags) Int64 bats2 (recent battles; larger=more recent) Int64 rcex SimSystemDetailFlags2 flags2 (Int32 mnRFlags, rfRFlags, clkFlags) Int32 eggScio, terrFl, tAcq, tfAcq, tDst ComplexArray dcs; Int32 dsu ComplexArray cm, pvcm (SimSystemDetailCm: Int32 msp, mv) ComplexArray cme2 (Int32 mid, mtrT, mn, mtp; ComplexArray mfx; String mdsc) ComplexArray spies Int32 pid (owner player id), defF, defSf SimSystemDetailBq bq (ComplexArray ords; Ord: Int32 desId, con, conleft, sav, ordId) NonComplexArray adct (Int32 ads, adt) Int32 numPlgs2 NonComplexArray flts, gfs, snF, mnF (fleets / gates / stations / monitors present) NonComplexArray nvos (colonies; see below) NonComplexArray nve (Int32 ePid, ets, eid) NonComplexArray nvs (Int32 pid; SimSystemDetailVsPView pview) SimSystemDetailsIndi indi (Bool hindi gate → indsp, SimPlayerColor indcl, String indnm/indav/indba) ``` - `SimSystemDetailNvo` (leaf): `Int32 pid, tShn, oId; Boolean isInd; SimSystemDetailNvoIndi indi` (independent-colony sub-block gated by `isInd`). - `SimSystemDetailVsPView` (complex — per-player *seen* snapshot of a colony): `Int32 vTrn, pop;` `ComplexArray pop2; Int32 infra; Float suit; Int32 res, aRes2, mRes; Bool noRebAi;` `Int32 pbon; ComplexArray pbon2; Float ibon; Int32 terrFl; Bool footer`. ### 8.1 R2 ⇄ R1 cross-map for planet/colony fields (verifier gold) R2 finds these serialized tags anywhere in the "Planets" region (between markers `NumSys`…`NdGr2`). They correspond to fields inside R1's `SimSystemDetailsSaveStruct` / `...VsPView`: | R2 serialized tag | type | meaning | R1 field | |---|---|---|---| | `Idx` | int | planet/system id | `idx` | | `Name` | string | name | `name` | | `Size` | int | 1-10 | `size` | | `Suit` | float | climate hazard | `suit` | | `Res` / `ARes2` / `MRes` | int | resources | `res` / `aRes` / `mRes` | | `Infra` | float | infrastructure | `infra` | | `ibon` | float | infra bonus | `ibon` | | `Pop` | int | imperial pop | `pop` | | `pbon` | int | imperial pop bonus | `pbon` | | `PopC` | long | civilian pop | (SimPopG `popC` Int64) | | `OID` | int | owner id (**= PID×16**) | `pid` (owner) | | `PID` | int | derived player id | (OID/16) | | `SRt/SRsc/SRtf/SRi/SRoh` | int | IO trade/ship/terraform/infra/overharvest | `rts.sRt/sRsc/sRtf/sRi/sRoh` | | `Abdn` | short | abandon order | `abdn` | | `Dstyd` | short | star annihilated | `dstyd` | | `ltis` | short | last-time-seen | `ltis` | | `VFlags/EFlags/AFlags/FFlags/GFlags` | int | state flags | `flags1.*` | | `Bats2` | int | recent combat | `bats2` (Int64 in R1) | | `nadct` | int | addicted (1=yes) | (in `adct` array) | | `NumFlts/NumGFs/NumSnF/NumMnF` | int | fleets/gates/stations/monitors | `flts/gfs/snF/mnF` counts | Note the **type disagreements** (verifier flags): R2 reads `Abdn`, `Dstyd`, `ltis` as **short (Int16)** while R1 models them as framed `Int32SaveStruct`; R2 reads `Bats2` as int while R1 uses Int64. R2's name-scan reads the value bytes directly after the tag+pad, so R2's width is the more literal on-value-bytes claim for those specific fields; treat as "value is small, low bytes are the datum." --- ## 9. Trade & node grid [R1] - `SimNodeGrid2` (complex): `ComplexArray paths; Int32 nextId`. `SimNodeGridPath` (complex): `Int32 npt, npid, npfr(from), npto(to), npctm, npcby, npdtn, npdtf, npenp, npuse, nptf`. - `SimTradeManager` (complex): `NonComplexArray tradeSectors; Float sctSize; List rt`. `SimTradeSector` (leaf): `Int32 tradeId; SimTradeSectorTradeSaveStruct trade`. `SimTradeSectorTradeSaveStruct` (complex): `SpatialCoordinate pos; Int32 tradeSectorGridId;` `SimTradeSectorTradeCtrSaveStruct tsctr(3 floats); Int32 tssec, tsct, tscr, ptssec, ptsct, ptscr;` `ComplexArray<...Fwarn {Int32 pId, ntrns}> fwarn; NonComplexArray systems, tsflt`. `SimTradeSectorRt` (complex): `Int32 tro, trfow, trfr, trfrs, trtow, trto, trtos, trtc`. --- ## 10. Fleets & ships [R1] - `SimFleet` (leaf): `Int32 fltId; SimFleetDetails flt`. - **`SimFleetDetails` (complex)**, ordered: `SpatialCoordinate pos; Int32 pId, locId; FlightPlanContainer fplan (Bool hfPlan gate);` `String ftName; Int32 ftTrans; SimFleetOrigin ftOrig (Int32 ×3);` `Int32 ftFlag, ftae, ftpae, ftEnc, ftMs, perm; SpatialCoordinate prvPos;` `LayContainer lay (Bool hLay gate); NonComplexArray ships`. - `SimFleetDetailsFlightPlan` (complex): `ComplexArray wpts; Float fPsp2; Int32 fPeta2;` `Fpogn2 (3 floats); SpatialCoordinate fPdpos; Int32 pnd`. `Wpt`: `Int32 wpt, tp; Nrt {Int32 nrp,nrf,nrt}`. - `SimFleetShip` (leaf): `Int32 shipId; SimFleetShipDetails ship`. - **`SimFleetShipDetails` (complex)**, ordered: `Int32 desId (design id), fltId, plrId; Float range; SimFleetShipHealth health (3 floats: command/mission/drive);` `Int32 conCap, refCap, repCap, mineCap, plg, act; Boolean dep, atq; Int32 encId;` `SimFleetShipPrish prish (Int32 prMax; NonComplexArray prSp); Int32 lct, tsd, atsp, tblt;` `Boolean hbq; SimFleetShipDetailsBq bq (gated by hbq);` `Boolean hsp; SimFleetShipDetailsSp sp (gated by hsp; two SpPop pop/pPop);` `NonComplexArray th (Float th, thm)`. `SimFleetShipDetailsBqOrd`: `Int32 desId, con, conleft, sav, ordId` (same shape as system BqOrd). --- ## 11. Combat reports & scenario/encounter objects [R1] - `SimCrepSaveStruct` (combat report, complex): `Int32 cid, trn; SpatialCoordinate pos; Int32 sid, auto, dur, cow, cdst, cpk, cpt, cdt, cdi;` `ComplexArray prep; ComplexArray wrep`. `SimCrepPrepSaveStruct`: `Int32 plr; Bool ai; Int32 ally, status, mxeng, mxcls, mxmsl;` `NonComplexArray cls; NonComplexArray sec; Int32 ndam; ComplexArray srep`. `SimCrepPrepSrepSaveStruct`: `String name; Int32 did, cls; Int64 caps2; Int32 nshp, nfld, nlst, dtak; SimDamsSaveStruct dams`. `SimDamsSaveStruct`: `Int32 dams, damp, dami, damt`. `SimCrepWrepSaveStruct`: `String wep; SimDams dams`. - `SimSvSctOb` (complex): `ScnObjStruct scn; NonComplexArray xscn; SimScSctObEncObjArray encObjs`. `SimSvSctObXscn` = polymorphic-by-string (§1.6): `traps` (ComplexArray), `gmtrigger` (Int32 gmch), `crowdefs` (Int32 sys; NonComplexArray dsys,des; Float drad), `indsys`/default (empty). - **`SimScSctObEncObjArray`** = grand-menace/encounter table, dispatched **by fixed index 0-8** (§1.6). Each subclass carries that menace's state, e.g.: - `EncInfest` (Hiver infestation): `NonComplexArray asg; ComplexArray infests; Int32 deshive, deslarva`. - `EncHives`: `Int32 qDesignId; ComplexArray hives; ComplexArray queens; ComplexArray sysMem`. - `EncAsteroidMonitor`, `EncTD`, `EncWD`, `EncRsuc`, `EncDfts` (Von Neumann; large), `EncIni2`. --- ## 12. CdTable — combat / AI persistence block [R1 CdTableSaveStructures.cs] ### CdTable (leaf, top of the 4th file-section): `ComplexArray cdt; CdPlayer cdplayer; CdAi[] cdai`. ### CdPlayer (complex) — **entirely reverse-unlabeled** (fields named `unknown1..35`); shape is known: `Int32 unknown1(=16); Bool unknown2; Float unknown3; Bool unknown4; Int32 unknown4p5; Bool unknown5..8;` `Int32 unknown9,10; NonComplexArray unknown11;` `Int32 unknown12..14; NonComplexArray unknown15,16; NonComplexArray unknown17;` `NonComplexArray unknown18; Int32 unknown19..21;` `NonComplexArray unknown22; Int32 unknown23..35`. ### CdAi (complex) — per-AI-player planner state, ordered: `AttributeSaveStruct aiAttr; NestedInt32 aiTurnPris; CdAiSit aiSit; NestedInt32 aiPlyHat;` `ComplexArray prs2; Int32 dsh, nbStab, nmBlst; CdAiAidng aidng;` `Int32 aiHivJ, sdFlT; NestedInt32 nalat; Int32 lnat, lat;` `NonComplexArray aiSys; NonComplexArray cmbR; NonComplexArray cl;` `NonComplexArray prv; NonComplexArray tecs; Int32 fct;` `ComplexArray apr`. - `CdAiSit` (complex): `NestedInt32 aiSitSecs; NestedInt32 aiSitWepFams` (**weapon-family set** lives here — opaque ints). - `CdAiCmbr` (complex): `Int32 crTrnK; Bool crPce; NestedInt32 crSys; CdAiCmbrCrplSv2 crplSv2`. `...CrplSv2`: `Int32 rpBon, rpBonT, savBonus; Bool maintHf; ComplexArray tacReports`. `TacReport`: `TrStruct trBy, trTo; TacReportDamage damageStruct; NonComplexArray ships`. `TrStruct`: `Int32 treHd, treHi, treD, treDp, treDi, treDt, treB`. `TacReportDamage` (leaf): `Int32 tRid, tRal, tRbal, tRlas, tRmis, tRmin, tRnrg, tRbio, tRbrd; Bool tRsld, tRsldd, tRsldc, tRsldi, tRsldr` — **damage-by-weapon-family breakdown**: bal(listic)/las(er)/mis(sile)/min(e)/nrg(=energy)/bio/brd(=boarding); sld=shields. `TacReportShips` (leaf): `Int32 tRships, tRsldr, tRshipL`. --- ## 13. Coverage gaps & contradictions **Coverage (what's decoded):** - R1 decodes essentially the *entire* file top-to-bottom: summary, create-params/map-gen, full sim (players, tech, designs, systems/colonies incl. per-player fog-of-war snapshots, fleets, ships, trade, node grid, combat reports, grand-menace/encounter objects) and the CdTable AI block. This is the most complete community model and the primary Rosetta source. - R2 decodes only Summary, Player Settings, Players, Species, and Planets (colony) fields, by tag search — but adds *friendly semantics* and confirms the value-bytes width of several planet fields. **Known-unknown fields (labelled `unknown*` in R1 — do NOT treat names as authoritative):** - All of `CdPlayer` (`unknown1..35`) and `CdAiAidngDnId`, `SimFleetOrigin`, `SimFleetDetailsFlightPlanFpogn2`, `SimTradeSectorTradeCtr`, `SimPlayerDesignDwgWgb`, `SimSystemDetailNvoIndcl*` bodies. - `SimFleetShipHealth` three floats guessed as command/mission/drive. - `PlanetSaveStruct.unknown1..4` (map-gen) unexplained. **Not decoded / thin:** - Tactical/real-time combat geometry: only *reports/summaries* are stored (SimCrep*, CdAiCmbr TacReport). Per-ship in-battle positions/velocities are not in these editors (likely not in the sim save at all). - `SimSystemDetailSpy` body is empty in R1 (marked "needs to be populated"); spy detail unresolved. - RNG state (`RngSaveStruct.unknownData`) is an opaque ~2500-byte blob. - R1 source comments flag `SimSystemDetailSpiesArray`, `SimSvSctObXscnXsc` and `SimScSctObEncObjDetails` as incomplete/"wrong" in places — verify encounter bodies against binary. **Contradictions between R1 and R2 (reconcile against binary):** 1. **Field widths on planet flags:** R2 reads `Abdn`, `Dstyd`, `ltis` as Int16 and `Bats2` as Int32; R1 models `abdn/ltis` as Int32 and `bats2` as Int64. → The datum is small; check the true stored width in the binary struct. 2. **PID vs OID:** R2 asserts `OID = PID*16` (owner id is player index << 4); R1 stores a single `pid` owner field and does not model the ×16 relationship. → Confirm whether the binary owner field is a raw index or a shifted/tagged handle. 3. **`_NPC` species id 4:** R2 names it `_NPC`; R1 comment guesses "AI Rebellion". Same numeric id 4, different label — likely a shared "non-player/rogue" species slot. 4. R2 treats Players and PlayerSettings as separate flat tab regions bounded by marker strings (`HomeSys`…`ISsp`, `Slot`…`Session`); R1 shows these are actually nested (settings inside the Summary PlayerSlot, live player data inside SimPlayerDetails). R2's region boundaries (`Summary`,`Slot`,`Session`,`HomeSys`,`ISsp`,`NumSys`,`NdGr2`,`PlayerIDs`,`DesignIDs`) are useful **section-marker strings** to locate blocks in the raw binary. **High-value binary-scan signatures:** `0xBEEFBEEF` / `0x41104110` complex-struct brackets; length-prefixed ASCII field-name tags (`[int32 len][name]`) preceding every named scalar; section marker strings above.