#include "CommandSerializer.h" #include #include #include "BuildingType.h" #include "Command.h" #include "Rotation.h" #include "ShipLayout.h" namespace { char rotationToChar(Rotation rotation) { switch (rotation) { case Rotation::North: return 'N'; case Rotation::East: return 'E'; case Rotation::South: return 'S'; case Rotation::West: return 'W'; } return 'E'; } Rotation rotationFromString(const std::string& token) { if (token == "N") { return Rotation::North; } if (token == "S") { return Rotation::South; } if (token == "W") { return Rotation::West; } return Rotation::East; } // Reads " ( )*" from the stream. Sets ok=false on // a stream failure. ShipLayoutConfig parseLayout(std::istringstream& in, bool& ok) { ShipLayoutConfig layout; int count = 0; if (!(in >> count) || count < 0) { ok = false; return layout; } for (int i = 0; i < count; ++i) { PlacedModule placed; std::string rotToken; int x = 0; int y = 0; if (!(in >> placed.moduleId >> x >> y >> rotToken)) { ok = false; return layout; } placed.position = QPoint(x, y); placed.rotation = rotationFromString(rotToken); layout.placedModules.push_back(placed); } return layout; } // Reads " ()* ()*" from the stream. void parseFilters(std::istringstream& in, std::vector& filterA, std::vector& filterB, bool& ok) { int countA = 0; if (!(in >> countA) || countA < 0) { ok = false; return; } for (int i = 0; i < countA; ++i) { std::string id; if (!(in >> id)) { ok = false; return; } filterA.push_back(ItemType{id}); } int countB = 0; if (!(in >> countB) || countB < 0) { ok = false; return; } for (int i = 0; i < countB; ++i) { std::string id; if (!(in >> id)) { ok = false; return; } filterB.push_back(ItemType{id}); } } // " ( )*" void appendLayout(std::ostringstream& out, const ShipLayoutConfig& layout) { out << layout.placedModules.size(); for (const PlacedModule& placed : layout.placedModules) { out << ' ' << placed.moduleId << ' ' << placed.position.x() << ' ' << placed.position.y() << ' ' << rotationToChar(placed.rotation); } } // " ()* ()*" void appendFilters(std::ostringstream& out, const std::vector& filterA, const std::vector& filterB) { out << filterA.size(); for (const ItemType& type : filterA) { out << ' ' << type.id; } out << ' ' << filterB.size(); for (const ItemType& type : filterB) { out << ' ' << type.id; } } } // namespace std::string serializeCommand(const Command& command) { std::ostringstream out; switch (command.kind) { case CommandKind::PlaceBuilding: { const PlaceBuildingCommand& c = static_cast(command); out << "place " << buildingTypeId(c.type) << ' ' << c.anchor.x() << ' ' << c.anchor.y() << ' ' << rotationToChar(c.rotation); if (c.recipeId.has_value()) { out << " recipe " << *c.recipeId; } if (c.shipLayout.has_value()) { out << " layout "; appendLayout(out, *c.shipLayout); } if (c.hasSplitterFilters) { out << " filters "; appendFilters(out, c.splitterFilterA, c.splitterFilterB); } break; } case CommandKind::Demolish: out << "demolish " << static_cast(command).id.value(); break; case CommandKind::RotateInPlace: { const RotateInPlaceCommand& c = static_cast(command); out << "rotate " << c.id.value() << ' ' << rotationToChar(c.newRotation); break; } case CommandKind::SetRecipe: { const SetRecipeCommand& c = static_cast(command); out << "setrecipe " << c.id.value() << ' ' << c.recipeId; break; } case CommandKind::SetShipLayout: { const SetShipLayoutCommand& c = static_cast(command); out << "setlayout " << c.id.value() << ' '; appendLayout(out, c.layout); break; } case CommandKind::SetSiteSplitterFilters: { const SetSiteSplitterFiltersCommand& c = static_cast(command); out << "sitefilters " << c.id.value() << ' '; appendFilters(out, c.filterA, c.filterB); break; } case CommandKind::SetSplitterFilters: { const SetSplitterFiltersCommand& c = static_cast(command); out << "splitterfilters " << c.tile.x() << ' ' << c.tile.y() << ' '; appendFilters(out, c.filterA, c.filterB); break; } case CommandKind::ClearBeltTiles: { const ClearBeltTilesCommand& c = static_cast(command); out << "clearbelt " << c.tiles.size(); for (const QPoint& tile : c.tiles) { out << ' ' << tile.x() << ' ' << tile.y(); } break; } case CommandKind::ApplySchematicChoice: out << "schematic " << static_cast(command).choiceIndex; break; case CommandKind::Reset: // A reset rolls the replay file; it is never written as a stream entry. break; } return out.str(); } std::shared_ptr parseCommand(const std::string& tokens) { std::istringstream in(tokens); std::string verb; if (!(in >> verb)) { return nullptr; } bool ok = true; if (verb == "place") { std::shared_ptr c = std::make_shared(); std::string typeToken; std::string rotToken; int x = 0; int y = 0; if (!(in >> typeToken >> x >> y >> rotToken)) { return nullptr; } const std::optional type = parseBuildingType(typeToken); if (!type.has_value()) { return nullptr; } c->type = *type; c->anchor = QPoint(x, y); c->rotation = rotationFromString(rotToken); std::string segment; while (in >> segment) { if (segment == "recipe") { std::string id; if (!(in >> id)) { return nullptr; } c->recipeId = id; } else if (segment == "layout") { c->shipLayout = parseLayout(in, ok); if (!ok) { return nullptr; } } else if (segment == "filters") { parseFilters(in, c->splitterFilterA, c->splitterFilterB, ok); if (!ok) { return nullptr; } c->hasSplitterFilters = true; } else { return nullptr; } } return c; } if (verb == "demolish") { std::shared_ptr c = std::make_shared(); BuildingId id = 0; if (!(in >> id)) { return nullptr; } c->id = id; return c; } if (verb == "rotate") { std::shared_ptr c = std::make_shared(); std::string rotToken; BuildingId id = 0; if (!(in >> id >> rotToken)) { return nullptr; } c->id = id; c->newRotation = rotationFromString(rotToken); return c; } if (verb == "setrecipe") { std::shared_ptr c = std::make_shared(); BuildingId id = 0; if (!(in >> id >> c->recipeId)) { return nullptr; } c->id = id; return c; } if (verb == "setlayout") { std::shared_ptr c = std::make_shared(); BuildingId id = 0; if (!(in >> id)) { return nullptr; } c->id = id; c->layout = parseLayout(in, ok); if (!ok) { return nullptr; } return c; } if (verb == "sitefilters") { std::shared_ptr c = std::make_shared(); BuildingId id = 0; if (!(in >> id)) { return nullptr; } c->id = id; parseFilters(in, c->filterA, c->filterB, ok); if (!ok) { return nullptr; } return c; } if (verb == "splitterfilters") { std::shared_ptr c = std::make_shared(); int x = 0; int y = 0; if (!(in >> x >> y)) { return nullptr; } c->tile = QPoint(x, y); parseFilters(in, c->filterA, c->filterB, ok); if (!ok) { return nullptr; } return c; } if (verb == "clearbelt") { std::shared_ptr c = std::make_shared(); int count = 0; if (!(in >> count) || count < 0) { return nullptr; } for (int i = 0; i < count; ++i) { int x = 0; int y = 0; if (!(in >> x >> y)) { return nullptr; } c->tiles.push_back(QPoint(x, y)); } return c; } if (verb == "schematic") { std::shared_ptr c = std::make_shared(); if (!(in >> c->choiceIndex)) { return nullptr; } return c; } return nullptr; }