sots-re/findings/subsystems/data-parsers.md

27 KiB
Raw Blame History

SOTS1 data-format parsers — report

Round-two deliverable: stdlib-only Python 3 readers for every textual data format shipped in sots.gob / sots_local_en.gob, proven against all 1,665 extracted files, with cross-link checks and normalized JSON artifacts.

Location: spicy:/bulk-storage/re-lab/handoff/parsers/ (this file is its sibling). Input: spicy:/bulk-storage/re-lab/gob-extract/. Python: 3.x, no third-party deps.

python3 parsers/verify.py <gob-extract-dir> <out-dir>     # re-runs everything, prints the appendix below

Headline

  • 1,595 / 1,595 data files parse (100%); the 70 skipped files are HLSL shaders (.fx/.fxh) and prose (Desc<Race>.txt, ChatTrans.txt) — not data.
  • 12 shipsections are syntactically broken in the shipped archive (11 never close their outer {, one has an extra }). They load in-game, so the engine's parser is lenient in exactly two ways: EOF closes all open blocks, and a stray top-level } is ignored. mars_data mirrors that by default and records a warning; strict=True rejects them.
  • Cross-links are clean where it matters: 0 dangling weapon→tech, section→tech, tech→section, tech→weapon-file, tech→tech, option→tech, bank→weapon-file, allows→tech, AI/scenario CSV→catalog references.
  • Real inconsistencies found (data bugs / engine-behaviour evidence): keys and identifiers are matched case-insensitively everywhere (25 refs only resolve that way), 10 _shipsections.txt ids point at files that do not exist, 3 NPC weapon name tokens are missing from Strings.csv, 4 Strings.csv keys are duplicated with a trailing space, and the three aitech*.csv AI tables are empty templates.

The readers

mars_data.py — the Mars brace-block format

parse(text, typed=True, keep_case=False, strict=False, warnings=None) / parse_file(path). Tokenizer-based (not line-based), so it handles every layout the files use. Grammar as observed:

body   := (NAME '{' body '}' | NAME value | "bare quoted item")*
value  := "quoted" | bareword          comment := // to end of line

Returns plain dicts. Repeated keys become lists (get_list(d, k) normalizes); bare quoted strings inside a block go under "_items"; the file's top level is itself a body. Barewords that look like C numbers (.5, -.8, 7e+8) or true/false are typed; quoted strings never are (numplayers "8" stays a str).

Used for: .weapon (207), .shipsection (875), .tech (1), .combat (3), .def (2), .script (4), and the 24 block-form .txt (scenarios, tutorial, credits, systemnames, skydefs, ctechvars, shipai).

Quirks handled (all present in shipped files):

quirk where handling
keys case-insensitive: Requires/requires, badge/Badge, Planet_Impact_Effect tech, defs, weapons keys lower-cased by default
identifier values case-insensitive: WEP_HCLAS vs tech WEP_HCLas, Ind_AstMon, SECTIONNAME_DEHammerHead vs file DEHammerhead, manifest DEWar.SHIPSECTION 18 weapon + 7 section requires, string keys, manifests all cross-links compare .lower()
block opens on the same line as a pair: turretsize small mount { _NPC/_Wreckage_E.shipsection tokenizer, not line-based
block name and brace on one line: weapon { MasterTechList.tech (67×) same
outer block never closed (EOF at depth 1) 11 shipsections (Hiver 6, Liir 4, Morrigi 1) lenient close + warning
one } too many Human/CrPropaganda.shipsection ignored + warning
// inside quoted strings; backslash Windows paths; "" empty values globals.txt, GUI scripts no escape processing, comment scan is quote-aware
bare quoted list items with no key Data/Strategy/systemnames.txt _items list
scalar and block under the same key: option DRV_PlsmFoc beside option { option A ... } 153 shipsections (229 occurrences) both merge into one list; consumers accept str-or-dict members
repeated turretsize/turretclass inside one bank{} 19 banks (Zuul DN*, …) kept as list; verify takes last
mixed CRLF/LF, cp1252 bytes everywhere decode cp1252

No /* */ comments, no =, no escapes, no BOMs were found.

flat_kv.py — KEY value tuning tables and positional rows

parse_kv(text, typed=True, on_dup='last') → {KEY: value}; a value of several unquoted tokens becomes a list; duplicates(text) lists repeated keys (none in shipped files). color("r g b") → tuple. parse_rows(text) → list of token rows for the positional tables (_turrets.txt, _defaultweapons.txt, damfx*.txt, playercolors.txt, BadgeTable.txt, AvatarTable.txt, WeaponIconPlacements.txt). Comment stripping is quote-aware (ENDGAME_FILL_COLOR "0 0 0" // 48 29 2" is a real line).

manifest.py — id manifests and CSVs

parse_manifest(text) → Manifest(entries=[(id, filename)], deleted=[ids], problems=[…]); validates id uniqueness, honours // DELETED - n, and by_name() folds case. parse_csv(text) drops #/// comment rows and blank rows, strips cells, honours RFC-4180 quoting (Strings.csv has one multi-line cell and quoted commas; stock_diplomacy_messages.csv quotes every cell including its "# species" header). parse_csv_with_header() recovers a # <a>,<b> schema row when present.

effect_txt.py — Effects/*.effect

Not brace-block, contrary to the round-one write-up. A TXT magic line, then KEY value lines and KEY + indented BEGIN … END groups. Order is semantic (PARTICLEDATATYPE n precedes the CREATION/VARIATION/OVERLIFE curves that belong to it; MODIFIER repeats once per type), so each level is returned as an ordered [key, value] list; to_dict() gives the unordered view. 415/415 parse; BEGIN/END always balanced.

verify.py

Parses every file with the reader its type calls for, computes key-frequency schema stats per block path, runs the cross-link suite, writes the artifacts, and prints the appendix below. Exit code 0 = no parse failures.

link refs dangling notes
weapon requires → tech 189 (177 weapons; 12 list 2+ techs = AND) 0 18 case-only mismatches; 13 player weapons have no requires (missiles, mirv warheads, boarding shuttles, spyship, wraith)
shipsection requires → tech 970 0 7 case-only (Ind_AstMon, DRN_BTLRdrs)
shipsection option/optiondef → tech 3,842 0
tech ship{section} → shipsection 149 0 section names are race-agnostic; matched against the union of race catalogs
tech weapon{filename} → file 67 0
tech requires → tech or GRP_x 154 0 9 groups (group TORPS ↔ requires GRP_TORPS): PRJCTR 4, PD 3, HVYBEAM 3, SPINAL 2, TORPS 12, MINES 7, BIOMISSILE 5, JAMMING 3, SHIELDS 6
tech allows edges 354 0 every edge parses to child RP:n [Race:%…]; 12 roots (*_ROOT) are never a target
_weapons.txt ↔ Weapons/*.weapon 123 ids, deleted 36/58/59 0 either way Species/_NPC/weapons/ (84) has no manifest — referenced by path from NPC bank{weapon} (404 refs, 0 dangling)
_shipsections.txt ↔ files 885 ids 10 ids with no file Tarkas ids 3,4,16,32,43,108,109 (CRAbsorber, CRAIC, CRDeflector, DEAbsorber, DEDeflector, DEDisruptor, CRDisruptor); DEWar in Human 98, Liir 86, Morrigi 39. These are reserved wire ids, consistent with the "never reuse ids" header.
TECHNAME_/TECHDESC_ 293 0 / 0
SECTIONNAME_/SECTIONDESC_ (267 stems) 0 / 67 62 of the missing DESC are _NPC-only, 5 are hidden player stems (_assaultshuttle, _biomissile, _boardingpod, _nodemissile, _spy)
every @TOKEN in brace files 378 3 @WEAPON_NPC_REFUGEEDRONE, @WEAPON_SILQUEEN_CANNON, @WEAPON_VNSYSK_CUTTINGBEAM (NPC weapons; would render as raw token)
_turrets.txt vs weapon/bank (size,class) 42 rows 0 / 0 only when compared case-insensitively (Large, Missile, Standard, COL, PlanetMissile appear)
_defaultweapons.txt → weapon file 31 0
AI affinity_section/raider_sections → section, weapon_replacements → weapon, affinity_weapon → weaponfamily 200 / – / 6 / 9 0
aitechpri/aitechgrp/aitechmode.csv 0 rows – comment-only templates: AI tech priorities are code-owned
Scenario *Techs.csv → tech, *FleetTemplates.csv → section 127 / 87 0

Schema facts worth knowing (from schema_stats.json)

  • weapon: 207 instances, 60 distinct scalar keys; behaviour sub-block is one of bolt(66, always with rangetable), beam(52), torpedo(19, with rangetable), rider(17), missile(13), chainlightning(10), col(9), mine(7), disintegrator(3), grapple, projectedshield, mirv(2 each), nodecannon, siege, mesonprojector, spyship, wraith(1 each). Always present: name weaponclass turretsize turretclass burst_volleys recharge_time.
  • shipsection: 875 instances, 87 distinct keys; always model health mass; bank(3,721) → mount(7,375); thruster(1,105); netforcelimits(875); option(1,226 block instances); anim(247). section_type values are case-mixed (engine/Engine, command/Command) and 41 sections have none (riders, NPC hulls); section_class has 16 with none.
  • tech: 293 nodes; keys name(293) allows(354) threat(169) family(155) requires(154) group(45) type(39, always P) option_cost(18) unlock_explicitly(6); sub-blocks ship(71) weapon(67) strategy(61). family is written on only 155 nodes — tech_tree.json adds family_inferred from the name prefix.
  • Strings.csv: 5,200 data rows → 5,196 keys (4 duplicated via trailing space, identical text). Key prefixes: AIDIP 755, EVENTMSG 415, TECHNAME/TECHDESC 293 each, SECTIONNAME 283, WEAPON 139, TECHBEN 25 …

Artifacts (parsers/out/)

file size content
tech_tree.json 217 KB 293 nodes (name, display name/desc, family, family_inferred, type, threat, group, option_cost, requires, TECHBEN inc/dec, sections, weapons, allows) + 354 edges {from, to, rp, pct{Race:%}} + groups
weapons.json 345 KB 207 weapons: full parsed body + stem, file, scope(player/NPC), id, display_name
shipsections.json 2.7 MB 875 sections: full body + race, stem, id, display_name, description, unlocked_by[]
strings.json 396 KB 5,196 token → text
schema_stats.json 33 KB per block path: instance count, key frequencies, sub-block frequencies
crosslink.json 27 KB machine-readable form of the cross-link findings
tech_tree.dot 54 KB Graphviz digraph, nodes coloured by family, edges labelled RP + race % (no graphviz on this box; dot -Tsvg elsewhere)
report.md verbatim verify.py output (appended below)

Caveat on edges[].pct: a race absent from an allows string has no override in the data. The default (believed 100%) is engine code, not asserted by the artifact.


Appendix — verify.py output

Parse results

reader file kind files parsed failed
mars_data brace:combat 3 3 0
mars_data brace:def 2 2 0
mars_data brace:script 4 4 0
mars_data brace:shipsection 875 875 0
mars_data brace:tech 1 1 0
mars_data brace:txt 24 24 0
mars_data brace:weapon 207 207 0
manifest.parse_csv csv 28 28 0
effect_txt effect 415 415 0
flat_kv.parse_kv kv 20 20 0
manifest.parse_manifest manifest 8 8 0
flat_kv.parse_rows rows 8 8 0

Total: 1595 parsed, 0 failed (skipped: 70 HLSL/prose files that are not data).

Lenient recoveries (engine-compatible; strict=True would reject these):

  • Species/Morrigi/sections/DECommand.shipsection: end of file inside block (depth 1)
  • Species/Liir/sections/CRAbsorber.shipsection: end of file inside block (depth 1)
  • Species/Liir/sections/CRCommand.shipsection: end of file inside block (depth 1)
  • Species/Liir/sections/CRFireControl.shipsection: end of file inside block (depth 1)
  • Species/Liir/sections/CRShield.shipsection: end of file inside block (depth 1)
  • Species/Human/sections/CrPropaganda.shipsection: line 105: stray '}' at top level
  • Species/Hiver/sections/DEAntiMatter.shipsection: end of file inside block (depth 1)
  • Species/Hiver/sections/DEDeflector.shipsection: end of file inside block (depth 1)
  • Species/Hiver/sections/DEDisruptor.shipsection: end of file inside block (depth 1)
  • Species/Hiver/sections/DEJammer.shipsection: end of file inside block (depth 1)
  • Species/Hiver/sections/DEShield.shipsection: end of file inside block (depth 1)
  • Species/Hiver/sections/DESquadronCnc.shipsection: end of file inside block (depth 1)

Schema stats (key frequency per block type)

Full table in schema_stats.json. Block paths with instance counts and the keys seen in them (count = number of block instances carrying the key):

weapon.weapon (207 instances)

keys: name:207, weaponclass:207, turretsize:207, turretclass:207, burst_volleys:207, recharge_time:207, cost:204, icon_file:204, icon_rect:204, muzzle_sound:203, muzzle_sound_minrange:200, muzzle_effect:190, requires:189, dam_est:179, volley_period:176, rating_frate:176, rating_dam:176, rating_acc:176, rating_range:176, fc_requires_los:173, fc_requires_inrange:173, fc_requires_enemycolony:173, fc_manual_target:173, fc_manual_toggle:173, fc_manual_launch:173, fc_controllable:173, fc_holdsfire:173, range:167, range_planet:167, hpbonus:152, model1:143, muzzle_speed:140, weaponfamily:130, volley_duration:107, solution_tolerance:89, turretmodel1:67, trackspeed_mod:55, model2:43, pinpoint:41, buildup_delay:40, blindfire:37, weapondamagetype:21, munitionsize:15, model3:14, fc_explicit_target:14, fc_exclusive_launch:14, fc_targets_expire:14, turretmodel2:14, target_queue_size:13, compatible_section:10, hidden:9, lock_period:4, buildup_sound:3, buildup_sound_minrange:3, exclusive_species:2, secondary_pd:2, turretmodel3:2, buildup_effect:1, centered_muzzle_effect:1, anim:1

sub-blocks: bolt:66, beam:52, torpedo:19, rider:17, missile:13, chainlightning:10, col:9, mine:7, disintegrator:3, grapple:2, projectedshield:2, mirv:2, nodecannon:1, siege:1, mesonprojector:1, spyship:1, wraith:1

shipsection.shipsection (875 instances)

keys: requires:970, mass:877, model:875, health:875, section_class:859, section_type:834, cost:831, cpoints:824, crew:644, preview_ofs:549, socket_aft:543, socket_fore:487, option:229, entity_class:189, range:182, engine_sound:178, autonomous:178, ftlspeed:168, dam_model:154, engine_idle_sound:133, design_class:126, engine_sound_minrange:125, engine_techera:115, command_cost:111, maintenance_cost:98, nodespeed:75, node_cannon_fling:68, dam_socket_aft:68, dam_socket_fore:67, section_sound:65, section_sound_repeat:65, nodesign:63, section_lod_type:60, view_dist:60, explicit_section:60, defence_platform:49, death_effect:39, tacticalsensorrange:38, scanrange:35, command_quota:34, station:33, death_area_impact_effect:30, death_dam:30, death_damradius:30, explicit_command_section:30, explicit_engine_section:30, refueling_capacity:21, split_traffic_volume:20, repair_capacity:18, aicontrol:17, rebelai_scanrange:17, rebelai_tacticalsensorrange:17, colonizer_pop:12, colonizer_infra:12, colonizer_terra:12, refinery:12, freighter:10, monitor:8, shield_model_offset:7, construction_capacity:7, huge:6, mining_capacity:6, mining_rate:6, spytender:6, propaganda:6, ewar:6, science:6, spy:6, exclude:6, freighterq:5, police:5, tradingpost:5, prisoner_capacity:4, node_bore:3, section_sound_minrange:3, gravboat_bonus:3, gateship:2, ramscoop:2, construction_devourer:1, construction_refresh:1, construction_salvagerate:1, mining_trap:1, colony_trap:1, imperial_population:1, civilian_population:1, protectorate:1, node_missile:1

sub-blocks: bank:3721, option:1226, thruster:1105, netforcelimits:875, anim:247, optiondef:12

tech.tech (293 instances)

keys: allows:354, name:293, threat:169, family:155, requires:154, group:45, type:39, option_cost:18, unlock_explicitly:6

sub-blocks: ship:71, weapon:67, strategy:61

All block paths: combat(3), combat.combatenv(3), combat.endcondition(3), combat.orbit(17), combat.planet(1), combat.summary(3), def(2), def.badge(27), def.light(196), script(4), script.distanceline(1), script.fleetline(1), script.gateline(1), script.hiver(1), script.hiver.dead(1), script.hiver.good(1), script.hiver.ideal(1), script.hiver.ideal_2(1), script.hiver.poor(1), script.human(1), script.human.dead(1), script.human.good(1), script.human.ideal(1), script.human.ideal_2(1), script.human.poor(1), script.liir(1), script.liir.dead(1), script.liir.good(1), script.liir.ideal(1), script.liir.ideal_2(1), script.liir.poor(1), script.morrigi(1), script.morrigi.dead(1), script.morrigi.good(1), script.morrigi.ideal(1), script.morrigi.ideal_2(1), script.morrigi.poor(1), script.moveline(1), script.nodeline(1), script.sprite(11), script.tarkas(1), script.tarkas.dead(1), script.tarkas.good(1), script.tarkas.ideal(1), script.tarkas.ideal_2(1), script.tarkas.poor(1), script.techcolors(1), script.techtreebackground(1), script.techtreemodelinfo(12), script.tradeline(1), script.zuul(1), script.zuul.dead(1), script.zuul.good(1), script.zuul.ideal(1), script.zuul.ideal_2(1), script.zuul.poor(1), shipsection(875), shipsection.shipsection(875), shipsection.shipsection.anim(247), shipsection.shipsection.bank(3721), shipsection.shipsection.bank.mount(7375), shipsection.shipsection.netforcelimits(875), shipsection.shipsection.option(1226), shipsection.shipsection.optiondef(12), shipsection.shipsection.thruster(1105), tech(1), tech.tech(293), tech.tech.ship(71), tech.tech.strategy(61), tech.tech.weapon(67), weapon(207), weapon.weapon(207), weapon.weapon.beam(52), weapon.weapon.bolt(66), weapon.weapon.bolt.rangetable(66), weapon.weapon.chainlightning(10), weapon.weapon.col(9), weapon.weapon.disintegrator(3), weapon.weapon.grapple(2), weapon.weapon.mesonprojector(1), weapon.weapon.mine(7), weapon.weapon.mirv(2), weapon.weapon.missile(13), weapon.weapon.nodecannon(1), weapon.weapon.projectedshield(2), weapon.weapon.rider(17), weapon.weapon.siege(1), weapon.weapon.spyship(1), weapon.weapon.torpedo(19), weapon.weapon.torpedo.rangetable(19), weapon.weapon.wraith(1)

  • weapon requires -> tech: 189 refs in 177 weapons (12 weapons list 2+ techs), 0 dangling, 18 case-mismatched; 30 weapons have no requires (NPC: 17, player: ['_mis_mirv_warhead.weapon', '_mis_planet_mirv_warhead.weapon', 'bal_grapple.weapon', 'brd_prisonershuttle.weapon', 'brd_shuttle.weapon', 'brd_tarkahunter.weapon', 'mis.weapon', 'mis_defplat.weapon', 'mis_planet.weapon', 'mis_planet_hvy.weapon', 'mis_planet_mirv.weapon', 'spyship.weapon', 'wraith.weapon']).
    • case: Weapons/bem_beamer_uv.weapon requires WEP_UVBmr (tech is WEP_UvBmr)
    • case: Weapons/bem_part.weapon requires WEP_prtBm (tech is WEP_PrtBm)
    • case: Weapons/bem_part_spinal.weapon requires WEP_prtBm (tech is WEP_PrtBm)
    • case: Weapons/can_plasma.weapon requires WEP_plsmcan (tech is WEP_PlsmCan)
    • case: Weapons/hvy_bem_hclas.weapon requires WEP_HCLAS (tech is WEP_HCLas)
    • case: Weapons/hvy_bem_hclas_free.weapon requires WEP_HCLAS (tech is WEP_HCLas)
    • case: Weapons/hvy_bem_lancer.weapon requires WEP_lancer (tech is WEP_Lancer)
    • case: Weapons/hvy_bem_lancer_free.weapon requires WEP_lancer (tech is WEP_Lancer)
    • case: Weapons/las_green.weapon requires WEP_grnlas (tech is WEP_GrnLas)
    • case: Weapons/las_uv.weapon requires WEP_UVlas (tech is WEP_UvLas)
    • case: Weapons/las_xray.weapon requires WEP_xrylas (tech is WEP_XryLas)
    • case: Weapons/trp_plasma.weapon requires WEP_plsmtrp (tech is WEP_PlsmTrp)
    • case: Species/_NPC/weapons/Herald_bem_beamer_uv.weapon requires WEP_UVBmr (tech is WEP_UvBmr)
    • case: Species/_NPC/weapons/Refugee_sml.weapon requires WEP_UVBmr (tech is WEP_UvBmr)
    • case: Species/_NPC/weapons/SiliciodQueenCannon.weapon requires WEP_plsmcan (tech is WEP_PlsmCan)
    • case: Species/_NPC/weapons/SwarmerCannon.weapon requires WEP_plsmcan (tech is WEP_PlsmCan)
    • case: Species/_NPC/weapons/Wreckage_bem_part.weapon requires WEP_prtBm (tech is WEP_PrtBm)
    • case: Species/_NPC/weapons/Wreckage_las_xray.weapon requires WEP_xrylas (tech is WEP_XryLas)
  • shipsection requires -> tech: 970 refs, 0 dangling, 7 case-mismatched.
    • case: Species/Zuul/sections/_AsteroidMonitor.shipsection requires Ind_AstMon
    • case: Species/Tarkas/sections/DNCarrier.shipsection requires DRN_BTLRdrs
    • case: Species/Tarkas/sections/_AsteroidMonitor.shipsection requires Ind_AstMon
    • case: Species/Morrigi/sections/_AsteroidMonitor.shipsection requires Ind_AstMon
    • case: Species/Liir/sections/_AsteroidMonitor.shipsection requires Ind_AstMon
    • case: Species/Human/sections/_AsteroidMonitor.shipsection requires Ind_AstMon
    • case: Species/Hiver/sections/_AsteroidMonitor.shipsection requires Ind_AstMon
  • shipsection option{option T}/optiondef -> tech: 3842 refs, 0 dangling. Two forms coexist: option { option A option B } (a mutually-exclusive choice group) and a bare section-level option T (229 occurrences in 153 files, e.g. option DRV_PlsmFoc on engine sections) -- both merge under the key option, so consumers must accept str-or-dict list members.
  • tech ship{section} -> shipsection: 149 refs, 0 dangling (matched against the union of all race catalogs, case-insensitive).
  • tech weapon{filename} -> file: 67 refs, 0 dangling.
  • tech requires -> tech/GRP_: 154 refs, 0 dangling. Groups: {'PRJCTR': 4, 'PD': 3, 'HVYBEAM': 3, 'SPINAL': 2, 'TORPS': 12, 'MINES': 7, 'BIOMISSILE': 5, 'JAMMING': 3, 'SHIELDS': 6}.
  • tech allows edges: 354, 0 point at unknown techs, 0 unparsable.
  • techs never allowed by anything (roots/orphans): 12: IND_ROOT, EWP_ROOT, SLD_Root, NRG_Root, TRP_ROOT, WHD_ROOT, BAL_ROOT, DRV_ROOT, BIO_ROOT, CCC_ROOT, DRN_ROOT, XNC_ROOT
  • duplicate tech names: none
  • id manifests <-> files:
    • Weapons: 123 ids (deleted [36, 58, 59]); listed-but-no-file 0; file-but-unlisted 0
    • Human: 145 ids (deleted none); listed-but-no-file 1; file-but-unlisted 0
      • MISSING FILE for id 98: dewar.shipsection
    • Zuul: 122 ids (deleted none); listed-but-no-file 0; file-but-unlisted 0
    • Hiver: 137 ids (deleted none); listed-but-no-file 0; file-but-unlisted 0
    • Tarkas: 139 ids (deleted none); listed-but-no-file 7; file-but-unlisted 0
      • MISSING FILE for id 3: crabsorber.shipsection
      • MISSING FILE for id 4: craic.shipsection
      • MISSING FILE for id 16: crdeflector.shipsection
      • MISSING FILE for id 109: crdisruptor.shipsection
      • MISSING FILE for id 32: deabsorber.shipsection
      • MISSING FILE for id 43: dedeflector.shipsection
      • MISSING FILE for id 108: dedisruptor.shipsection
    • Liir: 136 ids (deleted none); listed-but-no-file 1; file-but-unlisted 0
      • MISSING FILE for id 86: dewar.shipsection
    • Morrigi: 142 ids (deleted none); listed-but-no-file 1; file-but-unlisted 0
      • MISSING FILE for id 39: dewar.shipsection
    • _NPC: 64 ids (deleted none); listed-but-no-file 0; file-but-unlisted 0
    • Species/_NPC/weapons/*.weapon (84 files) have no manifest at all; they are referenced by filename from _NPC shipsection bank{weapon} lines.
  • localization:
    • Strings.csv: 5200 data rows -> 5196 keys. 4 keys occur twice because one copy carries a trailing space (parse_csv strips cells; the later row wins):
      • SECTIONNAME_CRGravboat: 'Gravboat' then 'Gravboat'
      • SECTIONNAME_CRFusion_Carver: 'Fusion Void Carver' then 'Fusion Void Carver'
      • SECTIONNAME_CRAntimatter_Carver: 'AM Void Carver' then 'AM Void Carver'
      • SECTIONNAME_CRAntimatter_Mastery: 'AM Void Mastery' then 'AM Void Mastery'
    • TECHNAME_/TECHDESC_ for 293 techs: 0 / 0 missing. [] []
    • SECTIONNAME_/SECTIONDESC_ for 267 distinct section stems: 0 / 67 missing.
      • missing SECTIONNAME_: 0 are _NPC-only stems (never shown in the design UI); player-race stems: 0 []
      • missing SECTIONDESC_: 62 are _NPC-only stems (never shown in the design UI); player-race stems: 5 ['_assaultshuttle', '_biomissile', '_boardingpod', '_nodemissile', '_spy']
    • weapon name @TOKEN: 3 unresolved of 207; 0 weapons carry no name.
      • UNRESOLVED Species/_NPC/weapons/Drone_shuttle.weapon name @WEAPON_NPC_REFUGEEDRONE
      • UNRESOLVED Species/_NPC/weapons/SiliciodQueenCannon.weapon name @WEAPON_SILQUEEN_CANNON
      • UNRESOLVED Species/_NPC/weapons/VNSYSK_Beam.weapon name @WEAPON_VNSYSK_CUTTINGBEAM
    • all @TOKEN refs in brace-block files: 378 refs, 3 unresolved.
      • UNRESOLVED Species/_NPC/weapons/Drone_shuttle.weapon @WEAPON_NPC_REFUGEEDRONE
      • UNRESOLVED Species/_NPC/weapons/SiliciodQueenCannon.weapon @WEAPON_SILQUEEN_CANNON
      • UNRESOLVED Species/_NPC/weapons/VNSYSK_Beam.weapon @WEAPON_VNSYSK_CUTTINGBEAM
  • _turrets.txt (42 rows; size/class values compared case-insensitively -- the data mixes Large/large, Missile/missile, Standard/standard):
    • weapon (turretsize,turretclass) pairs with no turret row: none
    • section bank (turretsize,turretclass) pairs with no turret row: none
    • banks with no turretsize at all (NPC fixed-weapon banks): 2; banks that repeat turretsize/turretclass inside one bank{} (last value taken): 19
  • shipsection bank{weapon <file>} -> file: 404 refs, 0 dangling.
  • _defaultweapons.txt: 31 rows, 0 name a missing weapon file.
  • Data/Strategy/AI/aitechpri.csv: 0 data rows -- the shipped file is a comment-only template (schema documented in its header, no entries); the AI's tech priorities must therefore come from code.
  • Data/Strategy/AI/aitechgrp.csv: 0 data rows -- the shipped file is a comment-only template (schema documented in its header, no entries); the AI's tech priorities must therefore come from code.
  • Data/Strategy/AI/aitechmode.csv: 0 data rows -- the shipped file is a comment-only template (schema documented in its header, no entries); the AI's tech priorities must therefore come from code.
  • AI/affinity_section.csv: 200 rows; unknown sections: none
  • AI/raider_sections.csv: unknown sections: none
  • AI/weapon_replacements.csv: 6 rows; unknown weapon stems: none
  • AI/affinity_weapon.csv: families ['conventionalbeam', 'conventionalmine', 'emitter', 'energycannon', 'gauss', 'heavybeam', 'laser', 'missile', 'torpedo']; not a weaponfamily in any .weapon: none. weaponfamily values in data: {'energycannon': 19, 'heavybeam': 7, 'missile': 14, 'gauss': 27, 'laser': 12, 'conventionalbeam': 21, 'emitter': 9, 'conventionalmine': 6, 'torpedo': 15}
  • Scenarios/HiverInvasion_FleetTemplates.csv: 81 rows; unknown sections: none
  • Scenarios/HiverInvasion_Techs.csv: 67 rows; unknown techs: none
  • Scenarios/UpstartApes_EmpireTechs.csv: 60 rows; unknown techs: none
  • Scenarios/UpstartApes_GiftFleetTemplates.csv: 6 rows; unknown sections: none

Artifacts

  • tech_tree.json (225 KB)
  • weapons.json (345 KB)
  • shipsections.json (2752 KB)
  • strings.json (396 KB)
  • schema_stats.json (33 KB)
  • crosslink.json (24 KB)
  • tech_tree.dot (54 KB)
  • tech_tree.json: 293 nodes, 354 edges; weapons.json: 207; shipsections.json: 875; strings.json: 5196 keys