#include "persist.hpp" #include "bsdl_model.hpp" #include "connect.hpp" #include "modules.hpp" #include "parts.hpp" #include "pins.hpp" #include "signals.hpp" #include "system.hpp" #include #include #include #include #include namespace { // Tab-delimited tokeniser. Empty trailing fields are preserved. std::vector split_tab(const std::string &line) { std::vector out; std::string cur; for (char c : line) { if (c == '\t') { out.push_back(std::move(cur)); cur.clear(); } else cur.push_back(c); } out.push_back(std::move(cur)); return out; } } // namespace bool save_system(const System *sys, const std::string &filename, std::string &error) { if (!sys) { error = "no system to save"; return false; } std::ofstream f(filename); if (!f) { error = "cannot open " + filename + " for writing"; return false; } f << "# essim system snapshot v1\n"; for (auto &mkv : *sys->modules()) { Module *mod = mkv.second; f << "M\t" << mod->name << "\n"; for (auto &pkv : *mod) { Part *p = pkv.second; f << "P\t" << p->name << "\t" << p->connector_type << "\n"; if (!p->bsdl_path.empty()) f << "B\t" << p->bsdl_path << "\n"; for (auto &nkv : *p) { Pin *pin = nkv.second; Signal *s = pin->signal(); f << "N\t" << pin->name << "\t" << (s ? s->name : ""); const char *otag = nc_origin_tag(pin->nc_origin); if (*otag) f << "\t" << otag; f << "\n"; } } // Signal types: only persist non-default (Other) overrides. for (auto &skv : *mod->signals) { Signal *s = skv.second; if (s->type == SignalType::Other) continue; f << "S\t" << s->name << "\t" << signal_type_name(s->type) << "\n"; } } for (auto &ckv : *sys->connections()) { Connection *c = ckv.second; f << "C\t" << c->name << "\t" << (c->m1 ? c->m1->name : "") << "\t" << (c->p1 ? c->p1->name : "") << "\t" << (c->m2 ? c->m2->name : "") << "\t" << (c->p2 ? c->p2->name : "") << "\t" << c->transform_name << "\n"; for (auto &wp : c->pin_map) { Pin *a = wp.first; Pin *b = wp.second; f << "W" << "\t" << (a && a->prnt && a->prnt->prnt ? a->prnt->prnt->name : "") << "\t" << (a && a->prnt ? a->prnt->name : "") << "\t" << (a ? a->name : "") << "\t" << (b && b->prnt && b->prnt->prnt ? b->prnt->prnt->name : "") << "\t" << (b && b->prnt ? b->prnt->name : "") << "\t" << (b ? b->name : "") << "\n"; } } return f.good(); } System *restore_system(const std::string &filename, std::string &error) { std::ifstream f(filename); if (!f) { error = "cannot open " + filename; return nullptr; } System *sys = new System(); Module *cur_mod = nullptr; Part *cur_part = nullptr; Connection *cur_conn = nullptr; std::string line; int lineno = 0; auto fail = [&](const std::string &msg) { error = "line " + std::to_string(lineno) + ": " + msg; delete sys; return nullptr; }; while (std::getline(f, line)) { ++lineno; if (line.empty() || line[0] == '#') continue; auto fs = split_tab(line); if (fs.empty()) continue; const std::string &tag = fs[0]; try { if (tag == "M") { if (fs.size() < 2) return fail("M needs "); cur_mod = sys->modules()->merge(fs[1]); cur_part = nullptr; } else if (tag == "P") { if (!cur_mod) return fail("P outside module"); if (fs.size() < 2) return fail("P needs "); cur_part = new Part(fs[1]); if (fs.size() >= 3) cur_part->connector_type = fs[2]; cur_mod->add(cur_part); } else if (tag == "N") { if (!cur_part || !cur_mod) return fail("N outside part"); if (fs.size() < 3) return fail("N needs "); Pin *pin = new Pin(fs[1]); cur_part->add(pin); if (!fs[2].empty()) { Signal *s = cur_mod->signals->merge(fs[2]); s->add(pin); pin->connect(s); } else if (fs.size() >= 4) { NcOrigin o; if (nc_origin_from_tag(fs[3], o)) pin->nc_origin = o; } } else if (tag == "S") { if (!cur_mod) return fail("S outside module"); if (fs.size() < 3) return fail("S needs "); Signal *s; try { s = cur_mod->signals->get(fs[1]); } catch (const std::exception &) { continue; } SignalType t; if (signal_type_from_name(fs[2], t)) s->type = t; } else if (tag == "C") { if (fs.size() < 7) return fail("C needs "); Module *m1 = sys->modules()->get(fs[2]); Module *m2 = sys->modules()->get(fs[4]); Part *p1 = m1->get(fs[3]); Part *p2 = m2->get(fs[5]); cur_conn = new Connection(fs[1], m1, p1, m2, p2); cur_conn->transform_name = fs[6]; sys->connections()->add(cur_conn); } else if (tag == "W") { if (!cur_conn) return fail("W outside connection"); if (fs.size() < 7) return fail("W needs "); Module *m1 = sys->modules()->get(fs[1]); Module *m2 = sys->modules()->get(fs[4]); Part *p1 = m1->get(fs[2]); Part *p2 = m2->get(fs[5]); Pin *pin1 = p1->get(fs[3]); Pin *pin2 = p2->get(fs[6]); cur_conn->pin_map.emplace_back(pin1, pin2); } else if (tag == "B") { if (!cur_part) return fail("B outside part"); if (fs.size() < 2) return fail("B needs "); cur_part->bsdl_path = fs[1]; } else { return fail("unknown tag '" + tag + "'"); } } catch (const std::exception &e) { return fail(std::string(tag) + ": " + e.what()); } } // Re-derive BSDL-sourced pin specs from each attached model: we persisted the // .bsd path (`B` tag), not the derived data, so re-parse and re-apply here. // Models that no longer parse are skipped silently. for (auto &mkv : *sys->modules()) { for (auto &pkv : *mkv.second) { Part *p = pkv.second; if (p->bsdl_path.empty()) continue; BsdlModel m = BsdlModel::from_file(p->bsdl_path); if (m.valid()) apply_bsdl(p, m); } } return sys; }