#include "BlueprintSerializer.h" #include #include #include "toml.hpp" #include "BuildingType.h" #include "Rotation.h" #include "ShipLayout.h" namespace { std::string rotationToString(Rotation r) { switch (r) { case Rotation::North: return "north"; case Rotation::East: return "east"; case Rotation::South: return "south"; case Rotation::West: return "west"; } return "north"; } Rotation parseRotation(const std::string& s) { if (s == "east") { return Rotation::East; } if (s == "south") { return Rotation::South; } if (s == "west") { return Rotation::West; } return Rotation::North; } } // namespace namespace BlueprintSerializer { std::string serialize(const std::vector& blueprints) { toml::array bpArr; for (const Blueprint& bp : blueprints) { toml::array bldArr; for (const BlueprintBuilding& b : bp.buildings) { toml::table bldTbl; bldTbl.insert("type", buildingTypeId(b.type)); bldTbl.insert("rotation", rotationToString(b.rotation)); bldTbl.insert("offset_x", static_cast(b.offset.x())); bldTbl.insert("offset_y", static_cast(b.offset.y())); bldTbl.insert("recipe_id", b.recipeId); if (!b.splitterFilterA.empty()) { toml::array filterArr; for (const ItemType& item : b.splitterFilterA) { filterArr.push_back(item.id); } bldTbl.insert("filter_a", std::move(filterArr)); } if (!b.splitterFilterB.empty()) { toml::array filterArr; for (const ItemType& item : b.splitterFilterB) { filterArr.push_back(item.id); } bldTbl.insert("filter_b", std::move(filterArr)); } if (b.shipLayout.has_value()) { toml::array modArr; for (const PlacedModule& pm : b.shipLayout->placedModules) { toml::table modTbl; modTbl.insert("type", pm.moduleId); modTbl.insert("x", static_cast(pm.position.x())); modTbl.insert("y", static_cast(pm.position.y())); modTbl.insert("rotation", rotationToString(pm.rotation)); modArr.push_back(std::move(modTbl)); } bldTbl.insert("modules", std::move(modArr)); } bldArr.push_back(std::move(bldTbl)); } toml::table bpTbl; bpTbl.insert("name", bp.name.toStdString()); bpTbl.insert("buildings", std::move(bldArr)); bpArr.push_back(std::move(bpTbl)); } toml::table root; root.insert("blueprint", std::move(bpArr)); std::ostringstream oss; oss << root; return oss.str(); } std::vector deserialize(const std::string& tomlContent) { toml::table root; try { root = toml::parse(tomlContent); } catch (const toml::parse_error& e) { std::ostringstream msg; msg << "TOML parse error: " << e.description() << " at " << e.source().begin; throw std::runtime_error(msg.str()); } std::vector result; const toml::array* bpArr = root["blueprint"].as_array(); if (!bpArr) { return result; } for (std::size_t i = 0; i < bpArr->size(); ++i) { const toml::table* bpTbl = (*bpArr)[i].as_table(); if (!bpTbl) { throw std::runtime_error("blueprint[" + std::to_string(i) + "] is not a table"); } Blueprint bp; bp.name = QString::fromStdString((*bpTbl)["name"].value_or(std::string{})); const toml::array* bldArr = (*bpTbl)["buildings"].as_array(); if (bldArr) { for (std::size_t j = 0; j < bldArr->size(); ++j) { const toml::table* bldTbl = (*bldArr)[j].as_table(); if (!bldTbl) { throw std::runtime_error( "blueprint[" + std::to_string(i) + "].buildings[" + std::to_string(j) + "] is not a table"); } const std::string typeStr = (*bldTbl)["type"].value_or(std::string{}); const std::optional parsedType = parseBuildingType(typeStr); if (!parsedType) { throw std::runtime_error("unknown building type: '" + typeStr + "'"); } BlueprintBuilding bb; bb.type = *parsedType; bb.rotation = parseRotation((*bldTbl)["rotation"].value_or(std::string{})); bb.offset.setX(static_cast((*bldTbl)["offset_x"].value_or(int64_t{0}))); bb.offset.setY(static_cast((*bldTbl)["offset_y"].value_or(int64_t{0}))); bb.recipeId = (*bldTbl)["recipe_id"].value_or(std::string{}); const toml::array* filterAArr = (*bldTbl)["filter_a"].as_array(); if (filterAArr) { for (std::size_t k = 0; k < filterAArr->size(); ++k) { const std::optional itemId = (*filterAArr)[k].value(); if (itemId) { bb.splitterFilterA.push_back(ItemType{*itemId}); } } } const toml::array* filterBArr = (*bldTbl)["filter_b"].as_array(); if (filterBArr) { for (std::size_t k = 0; k < filterBArr->size(); ++k) { const std::optional itemId = (*filterBArr)[k].value(); if (itemId) { bb.splitterFilterB.push_back(ItemType{*itemId}); } } } const toml::array* modArr = (*bldTbl)["modules"].as_array(); if (modArr) { ShipLayoutConfig layout; for (std::size_t k = 0; k < modArr->size(); ++k) { const toml::table* modTbl = (*modArr)[k].as_table(); if (!modTbl) { continue; } const std::optional modType = (*modTbl)["type"].value(); const std::optional x = (*modTbl)["x"].value(); const std::optional y = (*modTbl)["y"].value(); const std::optional rotStr = (*modTbl)["rotation"].value(); if (!modType || !x || !y || !rotStr) { continue; } PlacedModule pm; pm.moduleId = *modType; pm.position = QPoint(static_cast(*x), static_cast(*y)); pm.rotation = parseRotation(*rotStr); layout.placedModules.push_back(std::move(pm)); } bb.shipLayout = std::move(layout); } bp.buildings.push_back(std::move(bb)); } } result.push_back(std::move(bp)); } return result; } } // namespace BlueprintSerializer