527 lines
22 KiB
C++
527 lines
22 KiB
C++
// dump_catalog <data-root> <out.json>
|
|
//
|
|
// Loads the whole catalog and writes one JSON document for
|
|
// tests/game_data/oracle/compare.py:
|
|
// * every weapon / section with its typed fields (`typed`) and the raw block
|
|
// rendered in the reference reader's dict shape (`raw`): keys folded,
|
|
// repeats -> lists, barewords typed;
|
|
// * the tech tree (nodes, edges, groups), the turret table, id registries;
|
|
// * the cross_check() report.
|
|
// Strings are cp1252 in the files; they are decoded to Unicode here because
|
|
// the oracle JSON was written from decoded text.
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
#include <filesystem>
|
|
#include <fstream>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "game/data/catalog.h"
|
|
#include "mars/parse/value.h"
|
|
|
|
using namespace game::data;
|
|
|
|
namespace {
|
|
|
|
// ---- minimal JSON writer --------------------------------------------------
|
|
|
|
std::string json_string(std::string_view bytes) {
|
|
static const unsigned cp1252[32] = {
|
|
0x20AC, 0x0081, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6, 0x2030, 0x0160,
|
|
0x2039, 0x0152, 0x008D, 0x017D, 0x008F, 0x0090, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022,
|
|
0x2013, 0x2014, 0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x009D, 0x017E, 0x0178};
|
|
std::string out = "\"";
|
|
for (unsigned char c : bytes) {
|
|
switch (c) {
|
|
case '"': out += "\\\""; break;
|
|
case '\\': out += "\\\\"; break;
|
|
case '\n': out += "\\n"; break;
|
|
case '\r': out += "\\r"; break;
|
|
case '\t': out += "\\t"; break;
|
|
default:
|
|
if (c < 0x20 || c > 0x7e) {
|
|
unsigned cp = c;
|
|
if (c >= 0x80 && c <= 0x9f) cp = cp1252[c - 0x80];
|
|
char buf[8];
|
|
std::snprintf(buf, sizeof buf, "\\u%04x", cp);
|
|
out += buf;
|
|
} else {
|
|
out += static_cast<char>(c);
|
|
}
|
|
}
|
|
}
|
|
out += '"';
|
|
return out;
|
|
}
|
|
|
|
std::string json_double(double v) {
|
|
char buf[64];
|
|
std::snprintf(buf, sizeof buf, "%.17g", v);
|
|
std::string s(buf);
|
|
if (s.find_first_of(".eEn") == std::string::npos) s += ".0"; // keep it a float for Python
|
|
return s;
|
|
}
|
|
|
|
struct J {
|
|
std::string s;
|
|
};
|
|
J jnull() { return {"null"}; }
|
|
J jbool(bool b) { return {b ? "true" : "false"}; }
|
|
J jint(long long v) { return {std::to_string(v)}; }
|
|
J jnum(double v) { return {json_double(v)}; }
|
|
J jstr(std::string_view v) { return {json_string(v)}; }
|
|
template <class T>
|
|
J jopt(const std::optional<T>& o);
|
|
template <>
|
|
J jopt(const std::optional<double>& o) { return o ? jnum(*o) : jnull(); }
|
|
template <>
|
|
J jopt(const std::optional<std::int64_t>& o) { return o ? jint(*o) : jnull(); }
|
|
template <>
|
|
J jopt(const std::optional<int>& o) { return o ? jint(*o) : jnull(); }
|
|
template <>
|
|
J jopt(const std::optional<bool>& o) { return o ? jbool(*o) : jnull(); }
|
|
template <>
|
|
J jopt(const std::optional<std::string>& o) { return o ? jstr(*o) : jnull(); }
|
|
|
|
struct JArr {
|
|
std::string s = "[";
|
|
bool first = true;
|
|
JArr& push(const J& v) {
|
|
if (!first) s += ',';
|
|
first = false;
|
|
s += v.s;
|
|
return *this;
|
|
}
|
|
J done() const { return {s + "]"}; }
|
|
};
|
|
struct JObj {
|
|
std::string s = "{";
|
|
bool first = true;
|
|
JObj& set(std::string_view key, const J& v) {
|
|
if (!first) s += ',';
|
|
first = false;
|
|
s += json_string(key);
|
|
s += ':';
|
|
s += v.s;
|
|
return *this;
|
|
}
|
|
J done() const { return {s + "}"}; }
|
|
};
|
|
J jstrs(const std::vector<std::string>& v) {
|
|
JArr a;
|
|
for (const auto& s : v) a.push(jstr(s));
|
|
return a.done();
|
|
}
|
|
|
|
// ---- raw block in the reference dict shape --------------------------------
|
|
|
|
J typed_bareword(const std::string& tok) {
|
|
using mars::parse::ScalarKind;
|
|
switch (mars::parse::classify(tok)) {
|
|
case ScalarKind::Int:
|
|
if (auto v = mars::parse::as_int(tok)) return jint(*v);
|
|
return jstr(tok);
|
|
case ScalarKind::Float: return jnum(*mars::parse::as_double(tok));
|
|
case ScalarKind::Bool: return jbool(*mars::parse::as_bool(tok));
|
|
case ScalarKind::Text: break;
|
|
}
|
|
return jstr(tok);
|
|
}
|
|
|
|
J raw_json(const Block& b) {
|
|
struct Slot { std::string key; std::vector<std::string> vals; };
|
|
std::vector<Slot> slots;
|
|
auto add = [&](const std::string& key, const std::string& val) {
|
|
for (Slot& s : slots)
|
|
if (s.key == key) {
|
|
s.vals.push_back(val);
|
|
return;
|
|
}
|
|
slots.push_back({key, {val}});
|
|
};
|
|
// merge attrs and sub-blocks in file order
|
|
std::size_t ai = 0, bi = 0;
|
|
while (ai < b.attrs.size() || bi < b.blocks.size()) {
|
|
bool take_attr = bi >= b.blocks.size() || (ai < b.attrs.size() && b.attrs[ai].ord < b.blocks[bi].ord);
|
|
if (take_attr) {
|
|
const Attr& a = b.attrs[ai++];
|
|
add(fold(a.key), a.quoted ? jstr(a.value).s : typed_bareword(a.value).s);
|
|
} else {
|
|
const Block& sub = b.blocks[bi++];
|
|
add(fold(sub.name), raw_json(sub).s);
|
|
}
|
|
}
|
|
for (const std::string& it : b.items) add("_items", jstr(it).s);
|
|
JObj o;
|
|
for (const Slot& s : slots) {
|
|
if (s.vals.size() == 1) {
|
|
o.set(s.key, {s.vals[0]});
|
|
} else {
|
|
JArr a;
|
|
for (const auto& v : s.vals) a.push({v});
|
|
o.set(s.key, a.done());
|
|
}
|
|
}
|
|
return o.done();
|
|
}
|
|
|
|
// ---- typed views ----------------------------------------------------------
|
|
|
|
J rangetable_json(const RangeTable& t) {
|
|
JObj o;
|
|
auto band = [&](const RangeTable::Band& b, const char* p) {
|
|
std::string pre(p);
|
|
o.set(pre + "_range", jopt(b.range));
|
|
o.set(pre + "_range_dev", jopt(b.deviation));
|
|
o.set(pre + "_range_dam", jopt(b.damage));
|
|
};
|
|
band(t.point_blank, "pb");
|
|
band(t.effective, "eff");
|
|
band(t.maximum, "max");
|
|
return o.done();
|
|
}
|
|
|
|
J planet_json(const PlanetDamage& p) {
|
|
return JObj().set("dam_pop", jopt(p.pop)).set("dam_infra", jopt(p.infra)).set("dam_terra", jopt(p.terra)).done();
|
|
}
|
|
|
|
J weapon_json(const WeaponDef& w) {
|
|
JObj t;
|
|
t.set("name", jstr(w.name))
|
|
.set("weaponclass", jstr(w.weapon_class))
|
|
.set("weaponfamily", jstr(w.weapon_family))
|
|
.set("weapondamagetype", jstr(w.weapon_damage_type))
|
|
.set("requires", jstrs(w.requires))
|
|
.set("compatible_section", jstrs(w.compatible_section))
|
|
.set("exclusive_species", jstr(w.exclusive_species))
|
|
.set("cost", jopt(w.cost))
|
|
.set("turretsize", jstr(w.turret_size))
|
|
.set("turretclass", jstr(w.turret_class))
|
|
.set("trackspeed_mod", jopt(w.track_speed_mod))
|
|
.set("burst_volleys", jopt(w.burst_volleys))
|
|
.set("recharge_time", jopt(w.recharge_time))
|
|
.set("volley_period", jopt(w.volley_period))
|
|
.set("volley_duration", jopt(w.volley_duration))
|
|
.set("buildup_delay", jopt(w.buildup_delay))
|
|
.set("solution_tolerance", jopt(w.solution_tolerance))
|
|
.set("range", jopt(w.range))
|
|
.set("range_planet", jopt(w.range_planet))
|
|
.set("muzzle_speed", jopt(w.muzzle_speed))
|
|
.set("hpbonus", jopt(w.hpbonus))
|
|
.set("dam_est", jopt(w.dam_est))
|
|
.set("hidden", jopt(w.hidden))
|
|
.set("pinpoint", jopt(w.pinpoint))
|
|
.set("blindfire", jopt(w.blindfire))
|
|
.set("secondary_pd", jopt(w.secondary_pd))
|
|
.set("model1", jstr(w.model1))
|
|
.set("model2", jstr(w.model2))
|
|
.set("model3", jstr(w.model3))
|
|
.set("muzzle_effect", jstr(w.muzzle_effect))
|
|
.set("muzzle_sound", jstr(w.muzzle_sound))
|
|
.set("icon_file", jstr(w.icon_file))
|
|
.set("icon_rect", jstr(w.icon_rect))
|
|
.set("fc_requires_los", jopt(w.fc.requires_los))
|
|
.set("fc_requires_inrange", jopt(w.fc.requires_inrange))
|
|
.set("fc_requires_enemycolony", jopt(w.fc.requires_enemycolony))
|
|
.set("fc_manual_target", jopt(w.fc.manual_target))
|
|
.set("fc_manual_toggle", jopt(w.fc.manual_toggle))
|
|
.set("fc_manual_launch", jopt(w.fc.manual_launch))
|
|
.set("fc_controllable", jopt(w.fc.controllable))
|
|
.set("fc_holdsfire", jopt(w.fc.holdsfire))
|
|
.set("fc_explicit_target", jopt(w.fc.explicit_target))
|
|
.set("fc_exclusive_launch", jopt(w.fc.exclusive_launch))
|
|
.set("fc_targets_expire", jopt(w.fc.targets_expire))
|
|
.set("rating_frate", jopt(w.ratings.fire_rate))
|
|
.set("rating_dam", jopt(w.ratings.damage))
|
|
.set("rating_acc", jopt(w.ratings.accuracy))
|
|
.set("rating_range", jopt(w.ratings.range))
|
|
.set("behavior_kind", jstr(w.behavior_kind))
|
|
.set("planet_damage", planet_json(w.planet_damage))
|
|
.set("rangetable", w.rangetable ? rangetable_json(*w.rangetable) : jnull());
|
|
if (w.bolt) {
|
|
JObj b;
|
|
b.set("rangetable", rangetable_json(w.bolt->rangetable))
|
|
.set("dam_pop", jopt(w.bolt->planet.pop))
|
|
.set("dam_infra", jopt(w.bolt->planet.infra))
|
|
.set("dam_terra", jopt(w.bolt->planet.terra))
|
|
.set("mass", jopt(w.bolt->mass))
|
|
.set("beam_origin", jopt(w.bolt->beam_origin))
|
|
.set("beam_length", jopt(w.bolt->beam_length))
|
|
.set("ricochet_mod", jopt(w.bolt->ricochet_mod))
|
|
.set("effect", jstr(w.bolt->effect))
|
|
.set("impact_effect", jstr(w.bolt->impact_effect))
|
|
.set("expire_effect", jstr(w.bolt->expire_effect));
|
|
t.set("bolt", b.done());
|
|
} else {
|
|
t.set("bolt", jnull());
|
|
}
|
|
JObj o;
|
|
o.set("stem", jstr(w.stem))
|
|
.set("file", jstr(w.file))
|
|
.set("scope", jstr(w.scope == WeaponScope::Player ? "player" : "NPC"))
|
|
.set("id", jopt(w.id))
|
|
.set("display_name", jopt(w.display_name))
|
|
.set("typed", t.done())
|
|
.set("raw", raw_json(w.raw));
|
|
return o.done();
|
|
}
|
|
|
|
J section_json(const ShipSectionDef& s) {
|
|
JObj t;
|
|
t.set("model", jstr(s.model))
|
|
.set("dam_model", jstr(s.dam_model))
|
|
.set("requires", jstrs(s.requires))
|
|
.set("section_type", jstr(s.section_type_text))
|
|
.set("section_type_enum", jstr(section_type_name(s.section_type)))
|
|
.set("section_class", jstr(s.section_class_text))
|
|
.set("section_class_enum", jstr(section_class_name(s.section_class)))
|
|
.set("design_class", jstr(s.design_class))
|
|
.set("entity_class", jstr(s.entity_class))
|
|
.set("health", jopt(s.health))
|
|
.set("mass", jopt(s.mass))
|
|
.set("cost", jopt(s.cost))
|
|
.set("cpoints", jopt(s.cpoints))
|
|
.set("crew", jopt(s.crew))
|
|
.set("command_cost", jopt(s.command_cost))
|
|
.set("maintenance_cost", jopt(s.maintenance_cost))
|
|
.set("command_quota", jopt(s.command_quota))
|
|
.set("socket_fore", jstr(s.socket_fore))
|
|
.set("socket_aft", jstr(s.socket_aft))
|
|
.set("dam_socket_fore", jstr(s.dam_socket_fore))
|
|
.set("dam_socket_aft", jstr(s.dam_socket_aft))
|
|
.set("ftlspeed", jopt(s.ftlspeed))
|
|
.set("nodespeed", jopt(s.nodespeed))
|
|
.set("range", jopt(s.range))
|
|
.set("scanrange", jopt(s.scanrange))
|
|
.set("tacticalsensorrange", jopt(s.tactical_sensor_range))
|
|
.set("engine_techera", jstr(s.engine_techera))
|
|
.set("exclude", jstrs(s.exclude))
|
|
.set("explicit_command_section", jstr(s.explicit_command_section))
|
|
.set("explicit_engine_section", jstr(s.explicit_engine_section))
|
|
.set("explicit_section", jopt(s.explicit_section))
|
|
.set("autonomous", jopt(s.autonomous))
|
|
.set("nodesign", jopt(s.nodesign));
|
|
{
|
|
JArr groups;
|
|
for (const OptionGroup& g : s.options)
|
|
groups.push(JObj().set("members", jstrs(g.members)).set("scalar", jbool(g.scalar)).done());
|
|
t.set("option", groups.done());
|
|
t.set("optiondef", s.optiondef ? jstrs(s.optiondef->members) : jnull());
|
|
}
|
|
{
|
|
JArr banks;
|
|
for (const BankDef& b : s.banks) {
|
|
JArr mounts;
|
|
for (const MountDef& m : b.mounts)
|
|
mounts.push(JObj()
|
|
.set("node", jstr(m.node))
|
|
.set("min_azimuth", jopt(m.min_azimuth))
|
|
.set("max_azimuth", jopt(m.max_azimuth))
|
|
.set("min_inclination", jopt(m.min_inclination))
|
|
.set("max_inclination", jopt(m.max_inclination))
|
|
.set("home_azimuth", jopt(m.home_azimuth))
|
|
.set("home_inclination", jopt(m.home_inclination))
|
|
.done());
|
|
banks.push(JObj()
|
|
.set("turretclass", jstr(b.turret_class))
|
|
.set("turretsize", jstr(b.turret_size))
|
|
.set("weapon", jstr(b.weapon))
|
|
.set("showturrets", jopt(b.show_turrets))
|
|
.set("invincible", jopt(b.invincible))
|
|
.set("repeated_turret_spec", jbool(b.repeated_turret_spec))
|
|
.set("mount", mounts.done())
|
|
.done());
|
|
}
|
|
t.set("bank", banks.done());
|
|
}
|
|
if (s.netforcelimits) {
|
|
const NetForceLimits& n = *s.netforcelimits;
|
|
t.set("netforcelimits", JObj()
|
|
.set("force_forward", jopt(n.force_forward))
|
|
.set("force_right", jopt(n.force_right))
|
|
.set("force_up", jopt(n.force_up))
|
|
.set("torque_yaw", jopt(n.torque_yaw))
|
|
.set("torque_pitch", jopt(n.torque_pitch))
|
|
.set("torque_roll", jopt(n.torque_roll))
|
|
.set("speed", jopt(n.speed))
|
|
.set("rotspeed", jopt(n.rotspeed))
|
|
.done());
|
|
} else {
|
|
t.set("netforcelimits", jnull());
|
|
}
|
|
{
|
|
JArr th;
|
|
for (const ThrusterDef& d : s.thrusters)
|
|
th.push(JObj().set("node", jstr(d.node)).set("effect", jstr(d.effect)).set("idle_effect", jstr(d.idle_effect)).done());
|
|
t.set("thruster", th.done());
|
|
}
|
|
JObj o;
|
|
o.set("race", jstr(s.race))
|
|
.set("species", jstr(species_name(s.species)))
|
|
.set("stem", jstr(s.stem))
|
|
.set("file", jstr(s.file))
|
|
.set("id", jopt(s.id))
|
|
.set("display_name", jopt(s.display_name))
|
|
.set("description", jopt(s.description))
|
|
.set("unlocked_by", jstrs(s.unlocked_by))
|
|
.set("typed", t.done())
|
|
.set("raw", raw_json(s.raw));
|
|
return o.done();
|
|
}
|
|
|
|
J tech_json(const TechTree& t) {
|
|
JArr nodes;
|
|
for (const TechNode& n : t.nodes) {
|
|
JArr allows;
|
|
for (std::size_t ei : n.allows) allows.push(jstr(t.edges[ei].to));
|
|
nodes.push(JObj()
|
|
.set("name", jstr(n.name))
|
|
.set("display_name", jopt(n.display_name))
|
|
.set("description", jopt(n.description))
|
|
.set("family", n.family.empty() ? jnull() : jstr(n.family))
|
|
.set("family_inferred", jstr(n.family_inferred))
|
|
.set("type", n.type.empty() ? jnull() : jstr(n.type))
|
|
.set("threat", jopt(n.threat))
|
|
.set("group", n.group.empty() ? jnull() : jstr(n.group))
|
|
.set("option_cost", jopt(n.option_cost))
|
|
.set("unlock_explicitly", jopt(n.unlock_explicitly))
|
|
.set("requires", jstrs(n.requires))
|
|
.set("benefits_inc", jstrs(n.benefits_inc))
|
|
.set("benefits_dec", jstrs(n.benefits_dec))
|
|
.set("sections", jstrs(n.sections))
|
|
.set("weapons", jstrs(n.weapon_files))
|
|
.set("allows", allows.done())
|
|
.done());
|
|
}
|
|
JArr edges;
|
|
for (const AllowsEdge& e : t.edges) {
|
|
JObj pct, all;
|
|
for (int i = 0; i < kSpeciesCount; ++i) {
|
|
auto s = static_cast<Species>(i);
|
|
all.set(species_name(s), jint(e.percent(s)));
|
|
if (e.pct_written[static_cast<std::size_t>(i)]) pct.set(species_name(s), jint(e.percent(s)));
|
|
}
|
|
edges.push(JObj()
|
|
.set("from", jstr(e.from))
|
|
.set("to", jstr(e.to))
|
|
.set("rp", jopt(e.rp))
|
|
.set("pct", pct.done())
|
|
.set("pct_effective", all.done())
|
|
.set("unparsed", jstrs(e.unparsed))
|
|
.done());
|
|
}
|
|
JObj groups;
|
|
for (const auto& kv : t.groups) groups.set(kv.first, jstrs(kv.second));
|
|
return JObj().set("nodes", nodes.done()).set("edges", edges.done()).set("groups", groups.done()).done();
|
|
}
|
|
|
|
J turrets_json(const TurretTable& t) {
|
|
JArr rows;
|
|
for (const TurretRow& r : t.rows())
|
|
rows.push(JObj()
|
|
.set("mount_size", jstr(r.mount_size))
|
|
.set("weapon_size", jstr(r.weapon_size))
|
|
.set("class", jstr(r.turret_class))
|
|
.set("health", jopt(r.health))
|
|
.set("track_speed", jopt(r.track_speed))
|
|
.set("azimuth_scale", jopt(r.azimuth_scale))
|
|
.set("inclination_scale", jopt(r.inclination_scale))
|
|
.set("model", jstr(r.model))
|
|
.done());
|
|
return rows.done();
|
|
}
|
|
|
|
J refs_json(const std::vector<CrossCheck::Ref>& v) {
|
|
JArr a;
|
|
for (const auto& r : v) a.push(JArr().push(jstr(r.from)).push(jstr(r.ref)).done());
|
|
return a.done();
|
|
}
|
|
|
|
J registry_json(const IdRegistry& r) {
|
|
JArr ids;
|
|
for (const IdEntry& e : r.entries()) ids.push(JArr().push(jint(e.id)).push(jstr(e.name)).done());
|
|
JArr del;
|
|
for (int d : r.deleted()) del.push(jint(d));
|
|
return JObj().set("ids", ids.done()).set("deleted", del.done()).done();
|
|
}
|
|
|
|
J cross_json(const CrossCheck& x) {
|
|
JArr gaps;
|
|
for (const auto& g : x.manifest_ids_without_file)
|
|
gaps.push(JObj().set("scope", jstr(g.scope)).set("id", jint(g.id)).set("name", jstr(g.name)).done());
|
|
return JObj()
|
|
.set("weapon_requires_dangling", refs_json(x.weapon_requires_dangling))
|
|
.set("weapon_requires_case_mismatch", refs_json(x.weapon_requires_case_mismatch))
|
|
.set("weapons_without_requires", jstrs(x.weapons_without_requires))
|
|
.set("section_requires_dangling", refs_json(x.section_requires_dangling))
|
|
.set("section_requires_case_mismatch", refs_json(x.section_requires_case_mismatch))
|
|
.set("section_option_dangling", refs_json(x.section_option_dangling))
|
|
.set("tech_ship_section_dangling", refs_json(x.tech_ship_section_dangling))
|
|
.set("tech_weapon_file_dangling", refs_json(x.tech_weapon_file_dangling))
|
|
.set("tech_requires_dangling", refs_json(x.tech_requires_dangling))
|
|
.set("tech_allows_dangling", refs_json(x.tech_allows_dangling))
|
|
.set("tech_allows_unparsed", refs_json(x.tech_allows_unparsed))
|
|
.set("bank_weapon_dangling", refs_json(x.bank_weapon_dangling))
|
|
.set("manifest_ids_without_file", gaps.done())
|
|
.set("files_without_manifest_id", refs_json(x.files_without_manifest_id))
|
|
.set("weapon_turret_pairs_without_row", refs_json(x.weapon_turret_pairs_without_row))
|
|
.set("bank_turret_pairs_without_row", refs_json(x.bank_turret_pairs_without_row))
|
|
.set("unresolved_weapon_names", refs_json(x.unresolved_weapon_names))
|
|
.set("missing_techname", jstrs(x.missing_techname))
|
|
.set("missing_techdesc", jstrs(x.missing_techdesc))
|
|
.set("missing_sectionname", jstrs(x.missing_sectionname))
|
|
.set("missing_sectiondesc", jstrs(x.missing_sectiondesc))
|
|
.set("tech_roots", jstrs(x.tech_roots))
|
|
.set("strings_available", jbool(x.strings_available))
|
|
.set("dangling_count", jint(static_cast<long long>(x.dangling_count())))
|
|
.done();
|
|
}
|
|
|
|
} // namespace
|
|
|
|
int main(int argc, char** argv) {
|
|
if (argc != 3) {
|
|
std::fprintf(stderr, "usage: dump_catalog <data-root> <out.json>\n");
|
|
return 2;
|
|
}
|
|
Catalog cat = load_catalog(argv[1]);
|
|
|
|
JArr weapons;
|
|
for (const WeaponDef& w : cat.weapons) weapons.push(weapon_json(w));
|
|
JArr sections;
|
|
for (const ShipSectionDef& s : cat.sections) sections.push(section_json(s));
|
|
JObj manifests;
|
|
manifests.set("Weapons", registry_json(cat.weapon_ids));
|
|
for (const auto& kv : cat.section_ids) manifests.set(kv.first, registry_json(kv.second));
|
|
JArr problems;
|
|
for (const Problem& p : cat.problems)
|
|
problems.push(JObj()
|
|
.set("kind", jstr(problem_kind_name(p.kind)))
|
|
.set("file", jstr(p.file))
|
|
.set("line", jint(p.line))
|
|
.set("key", jstr(p.key))
|
|
.set("message", jstr(p.message))
|
|
.done());
|
|
|
|
JObj doc;
|
|
doc.set("weapons", weapons.done())
|
|
.set("sections", sections.done())
|
|
.set("tech", tech_json(cat.tech))
|
|
.set("turrets", turrets_json(cat.turrets))
|
|
.set("manifests", manifests.done())
|
|
.set("strings_loaded", jbool(cat.strings_loaded))
|
|
.set("string_count", jint(static_cast<long long>(cat.strings.size())))
|
|
.set("races", jstrs(cat.races))
|
|
.set("problems", problems.done())
|
|
.set("cross_check", cross_json(cat.cross_check()));
|
|
|
|
std::ofstream out(argv[2], std::ios::binary);
|
|
if (!out) {
|
|
std::fprintf(stderr, "cannot write %s\n", argv[2]);
|
|
return 1;
|
|
}
|
|
out << doc.done().s << '\n';
|
|
std::printf("dumped %zu weapons, %zu sections, %zu techs, %zu edges, %zu problems -> %s\n", cat.weapons.size(),
|
|
cat.sections.size(), cat.tech.nodes.size(), cat.tech.edges.size(), cat.problems.size(), argv[2]);
|
|
return 0;
|
|
}
|