#include "validator.h" #include #include "proto/human.pb.h" #include "structs.h" #include "util/identifiers.h" namespace com::fourisland::lingo2_archipelago { namespace { class Validator { public: explicit Validator(const CollectedInfo& info) : info_(info) {} void Validate() const { for (const auto& [map_name, map_info] : info_.maps) { ValidateMap(map_name, map_info); } for (const auto& [room_identifier, room_info] : info_.rooms) { ValidateRoom(room_identifier, room_info); } for (const auto& [door_identifier, door_info] : info_.doors) { ValidateDoor(door_identifier, door_info); } for (const auto& [port_identifier, port_info] : info_.ports) { ValidatePort(port_identifier, port_info); } for (const auto& [painting_identifier, painting_info] : info_.paintings) { ValidatePainting(painting_identifier, painting_info); } for (const auto& [panel_identifier, panel_info] : info_.panels) { ValidatePanel(panel_identifier, panel_info); } for (const auto& [keyholder_identifier, keyholder_info] : info_.keyholders) { ValidateKeyholder(keyholder_identifier, keyholder_info); } for (const auto& [letter_identifier, letter_info] : info_.letters) { ValidateLetter(letter_identifier, letter_info); } for (const auto& [ending_name, ending_info] : info_.endings) { ValidateEnding(ending_name, ending_info); } for (const auto& [panel_name, panel_info] : info_.panel_names) { ValidatePanelName(panel_name, panel_info); } for (const auto& [prog_name, prog_info] : info_.progressives) { ValidateProgressive(prog_name, prog_info); } for (const auto& [group_name, group_info] : info_.door_groups) { ValidateDoorGroup(group_name, group_info); } } private: void ValidateMap(const std::string& map_name, const MapInfo& map_info) const { for (const auto& [node_path, node_info] : map_info.game_nodes) { if (node_info.uses > 1) { std::cout << "Map " << map_name << " node " << node_path << " is used in multiple places." << std::endl; } else if (node_info.uses == 0) { std::cout << "Map " << map_name << " node " << node_path << " is not used." << std::endl; } if (!node_info.defined) { std::cout << "Map " << map_name << " node " << node_path << " is not defined in the game file." << std::endl; } } if (map_info.malformed_worldport_entrance) { std::cout << "The worldport entrance for map " << map_name << " is malformed." << std::endl; } } void ValidateRoom(const RoomIdentifier& room_identifier, const RoomInfo& room_info) const { if (room_info.definitions.empty()) { std::cout << "Room " << room_identifier.ShortDebugString() << " has no definition, but was referenced:" << std::endl; for (const DoorIdentifier& door_identifier : room_info.doors_referenced_by) { std::cout << " DOOR " << door_identifier.ShortDebugString() << std::endl; } for (const PanelIdentifier& panel_identifier : room_info.panels_referenced_by) { std::cout << " PANEL " << panel_identifier.ShortDebugString() << std::endl; } for (const HumanConnection& connection : room_info.connections_referenced_by) { std::cout << " CONNECTION " << connection.ShortDebugString() << std::endl; } } else if (room_info.definitions.size() > 1) { std::cout << "Room " << room_identifier.ShortDebugString() << " was defined multiple times." << std::endl; } } bool DoesDoorNeedLocationName(const HumanDoor& h_door, const std::string& map_name) const { if (h_door.type() != DoorType::STANDARD) { return false; } if (h_door.keyholders_size() > 0 || h_door.white_ending() || h_door.complete_at() > 0) { return true; } if (h_door.panels_size() > 4) { return true; } std::set map_areas; for (const PanelIdentifier& pi : h_door.panels()) { auto full_pi = GetCompletePanelIdentifierWithoutAnswer(pi, map_name, std::nullopt); if (full_pi) { auto panel_info_it = info_.panels.find(*full_pi); if (panel_info_it != info_.panels.end()) { const PanelInfo& panel_info = panel_info_it->second; map_areas.insert(panel_info.map_area_name); } } } if (map_areas.size() > 1) { return true; } return false; } void ValidateDoor(const DoorIdentifier& door_identifier, const DoorInfo& door_info) const { if (door_info.definitions.empty()) { std::cout << "Door " << door_identifier.ShortDebugString() << " has no definition, but was referenced:" << std::endl; for (const DoorIdentifier& other_door_identifier : door_info.doors_referenced_by) { std::cout << " DOOR " << other_door_identifier.ShortDebugString() << std::endl; } for (const PanelIdentifier& panel_identifier : door_info.panels_referenced_by) { std::cout << " PANEL " << panel_identifier.ShortDebugString() << std::endl; } for (const PaintingIdentifier& painting_identifier : door_info.paintings_referenced_by) { std::cout << " PAINTING " << painting_identifier.ShortDebugString() << std::endl; } for (const PortIdentifier& port_identifier : door_info.ports_referenced_by) { std::cout << " PORT " << port_identifier.ShortDebugString() << std::endl; } for (const HumanConnection& connection : door_info.connections_referenced_by) { std::cout << " CONNECTION " << connection.ShortDebugString() << std::endl; } for (const std::string& prog_name : door_info.progressives_referenced_by) { std::cout << " PROGRESSIVE " << prog_name << std::endl; } for (const std::string& group_name : door_info.door_groups_referenced_by) { std::cout << " DOOR GROUP " << group_name << std::endl; } if (door_info.has_id) { std::cout << " An AP ID is present." << std::endl; } } else if (door_info.definitions.size() > 1) { std::cout << "Door " << door_identifier.ShortDebugString() << " was defined multiple times." << std::endl; } if (door_info.malformed_identifiers.HasAny()) { std::cout << "Door " << door_identifier.ShortDebugString() << " has malformed identifiers:" << std::endl; for (const PaintingIdentifier& painting_identifier : door_info.malformed_identifiers.paintings) { std::cout << " PAINTING " << painting_identifier.ShortDebugString() << std::endl; } for (const PanelIdentifier& panel_identifier : door_info.malformed_identifiers.panels) { std::cout << " PANEL " << panel_identifier.ShortDebugString() << std::endl; } for (const KeyholderIdentifier& keyholder_identifier : door_info.malformed_identifiers.keyholders) { std::cout << " KEYHOLDER " << keyholder_identifier.ShortDebugString() << std::endl; } } for (const HumanDoor& h_door : door_info.definitions) { if (DoesDoorNeedLocationName(h_door, door_identifier.map()) && !h_door.has_location_name()) { std::cout << "Door " << door_identifier.ShortDebugString() << " needs an explicit location name." << std::endl; } if (h_door.type() == DoorType::STANDARD || h_door.type() == DoorType::LOCATION_ONLY || h_door.type() == DoorType::GRAVESTONE || h_door.legacy_location()) { if (h_door.double_letters()) { std::cout << "Door " << door_identifier.ShortDebugString() << " is a location that depends on double_letters." << std::endl; } if (!h_door.has_location_room()) { std::cout << "Door " << door_identifier.ShortDebugString() << " is missing a location_room." << std::endl; } } bool needs_id = (h_door.type() != DoorType::EVENT || h_door.latch() || h_door.legacy_location()); if (door_info.has_id != needs_id) { if (needs_id) { std::cout << "Door " << door_identifier.ShortDebugString() << " is missing an AP ID." << std::endl; } else { std::cout << "Door " << door_identifier.ShortDebugString() << " should not have an AP ID." << std::endl; } } } } void ValidatePort(const PortIdentifier& port_identifier, const PortInfo& port_info) const { if (port_info.definitions.empty()) { std::cout << "Port " << port_identifier.ShortDebugString() << " has no definition, but was referenced:" << std::endl; for (const HumanConnection& connection : port_info.connections_referenced_by) { std::cout << " CONNECTION " << connection.ShortDebugString() << std::endl; } for (const std::string& map_name : port_info.map_worldport_entrances) { std::cout << " MAP (worldport_entrance) " << map_name << std::endl; } if (port_info.has_id) { std::cout << " An AP ID is present." << std::endl; } } else if (port_info.definitions.size() > 1) { std::cout << "Port " << port_identifier.ShortDebugString() << " was defined multiple times." << std::endl; } if (!port_info.target_connections_referenced_by.empty()) { for (const HumanPort& port : port_info.definitions) { if (port.has_required_door()) { std::cout << "Port " << port_identifier.ShortDebugString() << " ha
#include "ids_yaml_format.h"

#include <yaml-cpp/yaml.h>

#include <fstream>
#include <functional>

namespace com::fourisland::lingo2_archipelago {
namespace {

template <typename T>
void OperateOnSortedMap(
    const T& map, std::function<void(const std::string& name,
                                     const typename T::mapped_type& value)>
                      callback) {
  std::vector<std::string> names;
  for (const auto& it : map) {
    names.push_back(it.first);
  }

  std::sort(names.begin(), names.end());

  for (const std::string& name : names) {
    callback(name, map.at(name));
  }
}

}  // namespace

IdMappings ReadIdsFromYaml(const std::string& filename) {
  IdMappings result;

  YAML::Node document = YAML::LoadFile(filename);

  if (document["maps"]) {
    for (const auto& map_it : document["maps"]) {
      IdMappings::MapIds& map_ids =
          (*result.mutable_maps())[map_it.first.as<std::string>()];

      if (map_it.second["rooms"]) {
        for (const auto& room_it : map_it.second["rooms"]) {
          IdMappings::RoomIds& room_ids =
              (*map_ids.mutable_rooms())[room_it.first.as<std::string>()];

          if (room_it.second["panels"]) {
            for (const auto& panel_it : room_it.second["panels"]) {
              (*room_ids.mutable_panels())[panel_it.first.as<std::string>()] =
                  panel_it.second.as<uint64_t>();
            }
          }

          if (room_it.second["masteries"]) {
            for (const auto& mastery_it : room_it.second["masteries"]) {
              (*room_ids
                    .mutable_masteries())[mastery_it.first.as<std::string>()] =
                  mastery_it.second.as<uint64_t>();
            }
          }

          if (room_it.second["keyholders"]) {
            for (const auto& keyholder_it : room_it.second["keyholders"]) {
              (*room_ids.mutable_keyholders())[keyholder_it.first
                                                   .as<std::string>()] =
                  keyholder_it.second.as<uint64_t>();
            }
          }

          if (room_it.second["ports"]) {
            for (const auto& port_it : room_it.second["ports"]) {
              (*room_ids.mutable_ports())[port_it.first.as<std::string>()] =
                  port_it.second.as<uint64_t>();
            }
          }
        }
      }

      if (map_it.second["doors"]) {
        for (const auto& door_it : map_it.second["doors"]) {
          (*map_ids.mutable_doors())[door_it.first.as<std::string>()] =
              door_it.second.as<uint64_t>();
        }
      }
    }
  }

  if (document["letters"]) {
    for (const auto& letter_it : document["letters"]) {
      (*result.mutable_letters())[letter_it.first.as<std::string>()] =
          letter_it.second.as<uint64_t>();
    }
  }

  if (document["endings"]) {
    for (const auto& ending_it : document["endings"]) {
      (*result.mutable_endings())[ending_it.first.as<std::string>()] =
          ending_it.second.as<uint64_t>();
    }
  }

  if (document["special"]) {
    for (const auto& special_it : document["special"]) {
      (*result.mutable_special())[special_it.first.as<std::string>()] =
          special_it.second.as<uint64_t>();
    }
  }

  if (document["progressives"]) {
    for (const auto& prog_it : document["progressives"]) {
      (*result.mutable_progressives())[prog_it.first.as<std::string>()] =
          prog_it.second.as<uint64_t>();
    }
  }

  if (document["door_groups"]) {
    for (const auto& group_it : document["door_groups"]) {
      (*result.mutable_door_groups())[group_it.first.as<std::string>()] =
          group_it.second.as<uint64_t>();
    }
  }

  return result;
}

void WriteIdsAsYaml(const IdMappings& ids, const std::string& filename) {
  YAML::Node result;

  OperateOnSortedMap(ids.maps(), [&result](const std::string& map_name,
                                           const IdMappings::MapIds& map_ids) {
    YAML::Node map_node;

    OperateOnSortedMap(
        map_ids.rooms(), [&map_node](const std::string& room_name,
                                     const IdMappings::RoomIds& room_ids) {
          YAML::Node room_node;

          OperateOnSortedMap(
              room_ids.panels(),
              [&room_node](const std::string& panel_name, uint64_t panel_id) {
                room_node["panels"][panel_name] = panel_id;
              });

          OperateOnSortedMap(room_ids.masteries(),
                             [&room_node](const std::string& mastery_name,
                                          uint64_t mastery_id) {
                               room_node["masteries"][mastery_name] =
                                   mastery_id;
                             });

          OperateOnSortedMap(room_ids.keyholders(),
                             [&room_node](const std::string& keyholder_name,
                                          uint64_t keyholder_id) {
                               room_node["keyholders"][keyholder_name] =
                                   keyholder_id;
                             });

          OperateOnSortedMap(
              room_ids.ports(),
              [&room_node](const std::string& port_name, uint64_t port_id) {
                room_node["ports"][port_name] = port_id;
              });

          map_node["rooms"][room_name] = std::move(room_node);
        });

    OperateOnSortedMap(
        map_ids.doors(),
        [&map_node](const std::string& door_name, uint64_t door_id) {
          map_node["doors"][door_name] = door_id;
        });

    result["maps"][map_name] = std::move(map_node);
  });

  OperateOnSortedMap(ids.letters(), [&result](const std::string& letter_name,
                                              uint64_t letter_id) {
    result["letters"][letter_name] = letter_id;
  });

  OperateOnSortedMap(ids.endings(), [&result](const std::string& ending_name,
                                              uint64_t ending_id) {
    result["endings"][ending_name] = ending_id;
  });

  OperateOnSortedMap(ids.special(), [&result](const std::string& special_name,
                                              uint64_t special_id) {
    result["special"][special_name] = special_id;
  });

  OperateOnSortedMap(ids.progressives(),
                     [&result](const std::string& prog_name, uint64_t prog_id) {
                       result["progressives"][prog_name] = prog_id;
                     });

  OperateOnSortedMap(ids.door_groups(), [&result](const std::string& group_name,
                                                  uint64_t group_id) {
    result["door_groups"][group_name] = group_id;
  });

  std::ofstream output_stream(filename);
  output_stream << result << std::endl;
}

}  // namespace com::fourisland::lingo2_archipelago