about summary refs log tree commit diff stats
path: root/tools/validator/human_processor.cpp
blob: 2c978bf834575a467f0710dab7c81417605a4d6b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
generated by cgit-pink 1.4.1 (git 2.36.1) at 2026-02-12 13:37:05 +0000
 


640
#include "human_processor.h"

#include <fmt/core.h>
#include <google/protobuf/message.h>
#include <google/protobuf/text_format.h>

#include <filesystem>
#include <fstream>
#include <iostream>
#include <map>
#include <optional>
#include <sstream>
#include <string>

#include "structs.h"
#include "util/ids_yaml_format.h"

namespace com::fourisland::lingo2_archipelago {
namespace {

template <typename T>
T ReadMessageFromFile(const std::string& path) {
  std::cout << "Processing " << path << std::endl;

  std::ifstream file(path);
  std::stringstream buffer;
  buffer << file.rdbuf();

  T message;
  google::protobuf::TextFormat::ParseFromString(buffer.str(), &message);

  return message;
}

class HumanProcessor {
 public:
  HumanProcessor(const std::string& mapdir, CollectedInfo& info)
      : mapdir_(mapdir), info_(info) {}

  void Run() {
    std::filesystem::path datadir_path = mapdir_;

    ProcessConnectionsFile(datadir_path / "connections.txtpb", std::nullopt);
    ProcessMaps(datadir_path);
    ProcessProgressivesFile(datadir_path / "progressives.txtpb");
    ProcessDoorGroupsFile(datadir_path / "door_groups.txtpb");
    ProcessIdsFile(datadir_path / "ids.yaml");
  }

 private:
  void ProcessMaps(std::filesystem::path path) {
    std::filesystem::path maps_dir = path / "maps";
    for (auto const& dir_entry :
         std::filesystem::directory_iterator(maps_dir)) {
      ProcessMap(dir_entry.path());
    }
  }

  void ProcessMap(std::filesystem::path path) {
    std::string map_name = path.filename().string();

    ProcessMetadataFile(path / "metadata.txtpb", map_name);
    ProcessConnectionsFile(path / "connections.txtpb", map_name);
    ProcessDoorsFile(path / "doors.txtpb", map_name);
    ProcessRooms(path / "rooms", map_name);
  }

  void ProcessMetadataFile(std::filesystem::path path,
                           const std::string& current_map_name) {
    if (!std::filesystem::exists(path)) {
      return;
    }

    MapInfo& map_info = info_.maps[current_map_name];

    auto metadata = ReadMessageFromFile<HumanMap>(path.string());
    for (const std::string& path : metadata.excluded_nodes()) {
      map_info.game_nodes[path].uses++;
    }
  }

  void ProcessRooms(std::filesystem::path path,
                    const std::string& current_map_name) {
    for (auto const& dir_entry : std::filesystem::directory_iterator(path)) {
      auto room = ReadMessageFromFile<HumanRoom>(dir_entry.path().string());
      ProcessRoom(room, current_map_name);
    }
  }

  void ProcessRoom(const HumanRoom& h_room,
                   const std::string& current_map_name) {
    RoomIdentifier room_identifier;
    room_identifier.set_map(current_map_name);
    room_identifier.set_name(h_room.name());

    RoomInfo& room_info = info_.rooms[room_identifier];
    room_info.definitions.push_back(h_room);

    for (const HumanPanel& h_panel : h_room.panels()) {
      ProcessPanel(h_panel, current_map_name, h_room);
    }

    for (const HumanPainting& h_painting : h_room.paintings()) {
      ProcessPainting(h_painting, current_map_name, h_room.name());
    }

    for (const HumanPort& h_port : h_room.ports()) {
      ProcessPort(h_port, current_map_name, h_room.name());
    }

    for (const HumanLetter& h_letter : h_room.letters()) {
      ProcessLetter(h_letter, current_map_name, h_room.name());
    }

    for (const HumanMastery& h_mastery : h_room.masteries()) {
      ProcessMastery(h_mastery, current_map_name, h_room.name());
    }

    for (const HumanKeyholder& h_keyholder : h_room.keyholders()) {
      ProcessKeyholder(h_keyholder, current_map_name, h_room.name());
    }

    for (const HumanEnding& h_ending : h_room.endings()) {
      ProcessEnding(h_ending, current_map_name, h_room.name());
    }
  }

  void ProcessPanel(const HumanPanel& h_panel,
                    const std::string& current_map_name,
                    const HumanRoom& h_room) {
    PanelIdentifier panel_identifier;
    panel_identifier.set_map(current_map_name);
    panel_identifier.set_room(h_room.name());
    panel_identifier.set_name(h_panel.name());

    PanelInfo& panel_info = info_.panels[panel_identifier];
    panel_info.definitions.push_back(h_panel);

    MapInfo& map_info = info_.maps[current_map_name];
    map_info.game_nodes[h_panel.path()].uses++;

    for (const Proxy& h_proxy : h_panel.proxies()) {
      ProxyInfo& proxy_info = panel_info.proxies[h_proxy.answer()];
      proxy_info.definitions.push_back(h_proxy);

      map_info.game_nodes[h_proxy.path()].uses++;
    }

    if (h_panel.has_required_door()) {
      DoorIdentifier required_door_identifier =
          *GetCompleteDoorIdentifier(h_panel.required_door(), current_map_name);
      DoorInfo& required_door_info = info_.doors[required_door_identifier];
      required_door_info.panels_referenced_by.push_back(panel_identifier);
    }

    if (h_panel.has_required_room()) {
      RoomIdentifier required_room_identifier =
          *GetCompleteRoomIdentifier(h_panel.required_room(), current_map_name);
      RoomInfo& required_room_info = info_.rooms[required_room_identifier];
      required_room_info.panels_referenced_by.push_back(panel_identifier);
    }

    std::string map_area_name = current_map_name;
    if (h_room.has_panel_display_name()) {
      map_area_name =
          fmt::format("{} ({})", current_map_name, h_room.panel_display_name());
    }

    panel_info.map_area_name = map_area_name;

    std::string panelsanity_name;
    if (h_panel.has_display_name()) {
      panelsanity_name =
          fmt::format("{} - {}", map_area_name, h_panel.display_name());
    } else {
      panelsanity_name = fmt::format("{} - {}", map_area_name, h_panel.name());
    }
    info_.panel_names[panelsanity_name].panels_used_by.push_back(
        panel_identifier);
  }

  void ProcessPainting(const HumanPainting& h_painting,
                       const std::string& current_map_name,
                       const std::string& current_room_name) {
    PaintingIdentifier painting_identifier;
    painting_identifier.set_map(current_map_name);
    painting_identifier.set_room(current_room_name);
    painting_identifier.set_name(h_painting.name());

    PaintingInfo& painting_info = info_.paintings[painting_identifier];
    painting_info.definitions.push_back(h_painting);

    MapInfo& map_info = info_.maps[current_map_name];
    map_info.game_nodes[h_painting.path()].uses++;

    if (h_painting.has_required_door()) {
      DoorIdentifier required_door_identifier = *GetCompleteDoorIdentifier(
          h_painting.required_door(), current_map_name);
      DoorInfo& required_door_info = info_.doors[required_door_identifier];
      required_door_info.paintings_referenced_by.push_back(painting_identifier);
    }
  }

  void ProcessPort(const HumanPort& h_port, const std::string& current_map_name,
                   const std::string& current_room_name) {
    PortIdentifier port_identifier;
    port_identifier.set_map(current_map_name);
    port_identifier.set_room(current_room_name);
    port_identifier.set_name(h_port.name());

    PortInfo& port_info = info_.ports[port_identifier];
    port_info.definitions.push_back(h_port);

    MapInfo& map_info = info_.maps[current_map_name];
    map_info.game_nodes[h_port.path()].uses++;

    if (h_port.has_required_door()) {
      DoorIdentifier required_door_identifier =
          *GetCompleteDoorIdentifier(h_port.required_door(), current_map_name);
      DoorInfo& required_door_info = info_.doors[required_door_identifier];
      required_door_info.ports_referenced_by.push_back(port_identifier);
    }
  }

  void ProcessLetter(const HumanLetter& h_letter,
                     const std::string& current_map_name,
                     const std::string& current_room_name) {
    LetterIdentifier letter_identifier =
        std::make_tuple(h_letter.key()[0], h_letter.level2());
    LetterInfo& letter_info = info_.letters[letter_identifier];

    RoomIdentifier room_identifier;
    room_identifier.set_map(current_map_name);
    room_identifier.set_name(current_room_name);
    letter_info.defined_in.push_back(room_identifier);

    MapInfo& map_info = info_.maps[current_map_name];
    map_info.game_nodes[h_letter.path()].uses++;
  }

  void ProcessMastery(const HumanMastery& h_mastery,
                      const std::string& current_map_name,
                      const std::string& current_room_name) {
    MapInfo& map_info = info_.maps[current_map_name];
    map_info.game_nodes[h_mastery.path()].uses++;
  }

  void ProcessKeyholder(const HumanKeyholder& h_keyholder,
                        const std::string& current_map_name,
                        const std::string& current_room_name) {
    KeyholderIdentifier keyholder_identifier;
    keyholder_identifier.set_map(current_map_name);
    keyholder_identifier.set_room(current_room_name);
    keyholder_identifier.set_name(h_keyholder.name());

    KeyholderInfo& keyholder_info = info_.keyholders[keyholder_identifier];
    keyholder_info.definitions.push_back(h_keyholder);

    MapInfo& map_info = info_.maps[current_map_name];
    map_info.game_nodes[h_keyholder.path()].uses++;
  }

  void ProcessEnding(const HumanEnding& h_ending,
                     const std::string& current_map_name,
                     const std::string& current_room_name) {
    EndingInfo& ending_info = info_.endings[h_ending.name()];

    RoomIdentifier room_identifier;
    room_identifier.set_map(current_map_name);
    room_identifier.set_name(current_room_name);
    ending_info.defined_in.push_back(room_identifier);

    MapInfo& map_info = info_.maps[current_map_name];
    map_info.game_nodes[h_ending.path()].uses++;
  }

  void ProcessDoorsFile(std::filesystem::path path,
                        const std::string& current_map_name) {
    if (!std::filesystem::exists(path)) {
      return;
    }

    auto doors = ReadMessageFromFile<HumanDoors>(path.string());

    for (const HumanDoor& door : doors.doors()) {
      ProcessDoor(door, current_map_name);
    }
  }

  void ProcessDoor(const HumanDoor& h_door,
                   const std::string& current_map_name) {
    DoorIdentifier door_identifier;
    door_identifier.set_map(current_map_name);
    door_identifier.set_name(h_door.name());

    DoorInfo& door_info = info_.doors[door_identifier];
    door_info.definitions.push_back(h_door);

    if (h_door.has_location_room()) {
      RoomIdentifier location_room_identifier;
      location_room_identifier.set_map(current_map_name);
      location_room_identifier.set_name(h_door.location_room());
      info_.rooms[location_room_identifier].doors_referenced_by.push_back(
          door_identifier);
    }

    for (const PaintingIdentifier& pi : h_door.move_paintings()) {
      auto complete_painting_identifier =
          GetCompletePaintingIdentifier(pi, current_map_name, std::nullopt);
      if (complete_painting_identifier) {
        PaintingInfo& move_painting_info =
            info_.paintings[*complete_painting_identifier];
        move_painting_info.doors_referenced_by.push_back(door_identifier);
      } else {
        door_info.malformed_identifiers.paintings.push_back(pi);
      }
    }

    for (const PanelIdentifier& pi : h_door.panels()) {
      auto complete_panel_identifier = GetCompletePanelIdentifierWithoutAnswer(
          pi, current_map_name, std::nullopt);
      if (complete_panel_identifier) {
        PanelInfo& panel_info = info_.panels[*complete_panel_identifier];
        panel_info.doors_referenced_by.push_back(door_identifier);

        if (pi.has_answer()) {
          panel_info.proxies[pi.answer()].doors_referenced_by.push_back(
              door_identifier);
        }
      } else {
        door_info.malformed_identifiers.panels.push_back(pi);
      }
    }

    for (const KeyholderIdentifier& ki : h_door.keyholders()) {
      auto complete_keyholder_identifier =
          GetCompleteKeyholderIdentifierWithoutKey(ki, current_map_name,
                                                   std::nullopt);
      if (complete_keyholder_identifier) {
        KeyholderInfo& keyholder_info =
            info_.keyholders[*complete_keyholder_identifier];
        keyholder_info.doors_referenced_by.push_back(door_identifier);
      } else {
        door_info.malformed_identifiers.keyholders.push_back(ki);
      }
    }

    for (const RoomIdentifier& ri : h_door.rooms()) {
      RoomIdentifier complete_room_identifier =
          *GetCompleteRoomIdentifier(ri, current_map_name);
      RoomInfo& room_info = info_.rooms[complete_room_identifier];
      room_info.doors_referenced_by.push_back(door_identifier);
    }

    for (const DoorIdentifier& di : h_door.doors()) {
      DoorIdentifier complete_door_identifier =
          *GetCompleteDoorIdentifier(di, current_map_name);
      DoorInfo& other_door_info = info_.doors[complete_door_identifier];
      other_door_info.doors_referenced_by.push_back(door_identifier);
    }

    for (const std::string& ei : h_door.endings()) {
      EndingInfo& ending_info = info_.endings[ei];
      ending_info.doors_referenced_by.push_back(door_identifier);
    }
  }

  void ProcessConnectionsFile(std::filesystem::path path,
                              std::optional<std::string> current_map_name) {
    if (!std::filesystem::exists(path)) {
      return;
    }

    auto connections = ReadMessageFromFile<HumanConnections>(path.string());

    for (const HumanConnection& connection : connections.connections()) {
      ProcessConnection(connection, current_map_name);
    }
  }

  void ProcessConnection(const HumanConnection& human_connection,
                         const std::optional<std::string>& current_map_name) {
    if (human_connection.has_from_room()) {
      if (current_map_name) {
        RoomIdentifier room_identifier;
        room_identifier.set_map(*current_map_name);
        room_identifier.set_name(human_connection.from_room());

        RoomInfo& room_info = info_.rooms[room_identifier];
        room_info.connections_referenced_by.push_back(human_connection);
      } else {
        // Not sure where else to store this right now.
        std::cout << "A global connection used from_room." << std::endl;
      }
    } else if (human_connection.has_from()) {
      ProcessSingleConnection(human_connection, human_connection.from(),
                              current_map_name,
                              /*is_target=*/!human_connection.oneway() &&
                                  !human_connection.bypass_target_door());
    }

    if (human_connection.has_to_room()) {
      if (current_map_name) {
        RoomIdentifier room_identifier;
        room_identifier.set_map(*current_map_name);
        room_identifier.set_name(human_connection.to_room());

        RoomInfo& room_info = info_.rooms[room_identifier];
        room_info.connections_referenced_by.push_back(human_connection);
      } else {
        // Not sure where else to store this right now.
        std::cout << "A global connection used to_room." << std::endl;
      }
    } else if (human_connection.has_to()) {
      ProcessSingleConnection(
          human_connection, human_connection.to(), current_map_name,
          /*is_target=*/!human_connection.bypass_target_door());
    }

    if (human_connection.has_door()) {
      auto door_identifier =
          GetCompleteDoorIdentifier(human_connection.door(), current_map_name);
      if (door_identifier) {
        DoorInfo& door_info = info_.doors[*door_identifier];
        door_info.connections_referenced_by.push_back(human_connection);
      } else {
        // Not sure where else to store this right now.
        std::cout
            << "A connection used the following malformed door identifier: "
            << human_connection.door().ShortDebugString() << std::endl;
      }
    }
  }

  void ProcessSingleConnection(
      const HumanConnection& human_connection,
      const HumanConnection::Endpoint& endpoint,
      const std::optional<std::string>& current_map_name, bool is_target) {
    if (endpoint.has_room()) {
      auto room_identifier =
          GetCompleteRoomIdentifier(endpoint.room(), current_map_name);
      if (room_identifier) {
        RoomInfo& room_info = info_.rooms[*room_identifier];
        room_info.connections_referenced_by.push_back(human_connection);
      } else {
        // Not sure where else to store this right now.
        std::cout
            << "A connection used the following malformed room identifier: "
            << endpoint.room().ShortDebugString() << std::endl;
      }
    } else if (endpoint.has_painting()) {
      auto painting_identifier = GetCompletePaintingIdentifier(
          endpoint.painting(), current_map_name, std::nullopt);
      if (painting_identifier) {
        PaintingInfo& painting_info = info_.paintings[*painting_identifier];
        painting_info.connections_referenced_by.push_back(human_connection);

        if (is_target) {
          painting_info.target_connections_referenced_by.push_back(
              human_connection);
        }
      } else {
        // Not sure where else to store this right now.
        std::cout
            << "A connection used the following malformed painting identifier: "
            << endpoint.painting().ShortDebugString() << std::endl;
      }
    } else if (endpoint.has_port()) {
      auto port_identifier = GetCompletePortIdentifier(
          endpoint.port(), current_map_name, std::nullopt);
      if (port_identifier) {
        PortInfo& port_info = info_.ports[*port_identifier];
        port_info.connections_referenced_by.push_back(human_connection);

        if (is_target) {
          port_info.target_connections_referenced_by.push_back(
              human_connection);
        }
      } else {
        // Not sure where else to store this right now.
        std::cout
            << "A connection used the following malformed port identifier: "
            << endpoint.port().ShortDebugString() << std::endl;
      }
    } else if (endpoint.has_panel()) {
      auto panel_identifier = GetCompletePanelIdentifierWithoutAnswer(
          endpoint.panel(), current_map_name, std::nullopt);
      if (panel_identifier) {
        PanelInfo& panel_info = info_.panels[*panel_identifier];
        panel_info.connections_referenced_by.push_back(human_connection);

        if (endpoint.panel().has_answer()) {
          panel_info.proxies[endpoint.panel().answer()]
              .connections_referenced_by.push_back(human_connection);
        }

        if (is_target) {
          panel_info.target_connections_referenced_by.push_back(
              human_connection);
        }
      }
    }
  }

  void ProcessProgressivesFile(std::filesystem::path path) {
    if (!std::filesystem::exists(path)) {
      return;
    }

    auto h_progs = ReadMessageFromFile<HumanProgressives>(path.string());

    for (const HumanProgressive& h_prog : h_progs.progressives()) {
      ProcessProgressive(h_prog);
    }
  }

  void ProcessProgressive(const HumanProgressive& h_prog) {
    ProgressiveInfo& prog_info = info_.progressives[h_prog.name()];
    prog_info.definitions.push_back(h_prog);

    for (const DoorIdentifier& di : h_prog.doors()) {
      if (!di.has_map()) {
        prog_info.malformed_doors.push_back(di);
        continue;
      }

      DoorInfo& door_info = info_.doors[di];
      door_info.progressives_referenced_by.push_back(h_prog.name());
    }
  }

  void ProcessDoorGroupsFile(std::filesystem::path path) {
    if (!std::filesystem::exists(path)) {
      return;
    }

    auto h_groups = ReadMessageFromFile<HumanDoorGroups>(path.string());

    for (const HumanDoorGroup& h_group : h_groups.door_groups()) {
      ProcessDoorGroup(h_group);
    }
  }

  void ProcessDoorGroup(const HumanDoorGroup& h_group) {
    DoorGroupInfo& group_info = info_.door_groups[h_group.name()];
    group_info.definitions.push_back(h_group);

    for (const DoorIdentifier& di : h_group.doors()) {
      if (!di.has_map()) {
        group_info.malformed_doors.push_back(di);
        continue;
      }

      DoorInfo& door_info = info_.doors[di];
      door_info.door_groups_referenced_by.push_back(h_group.name());
    }
  }

  void ProcessIdsFile(std::filesystem::path path) {
    auto ids = ReadIdsFromYaml(path.string());

    DoorIdentifier di;
    PanelIdentifier pi;
    KeyholderIdentifier ki;

    for (const auto& [map_name, map] : ids.maps()) {
      di.set_map(map_name);
      pi.set_map(map_name);
      ki.set_map(map_name);

      for (const auto& [door_name, ap_id] : map.doors()) {
        di.set_name(door_name);

        DoorInfo& door_info = info_.doors[di];
        door_info.has_id = true;
      }

      for (const auto& [room_name, room] : map.rooms()) {
        pi.set_room(room_name);
        ki.set_room(room_name);

        for (const auto& [panel_name, ap_id] : room.panels()) {
          pi.set_name(panel_name);

          PanelInfo& panel_info = info_.panels[pi];
          panel_info.has_id = true;
        }

        for (const auto& [mastery_name, ap_id] : room.masteries()) {
          // TODO: Mastery
        }

        for (const auto& [keyholder_name, ap_id] : room.keyholders()) {
          ki.set_name(keyholder_name);

          KeyholderInfo& keyholder_info = info_.keyholders[ki];
          keyholder_info.has_id = true;
        }
      }
    }

    for (const auto& [tag, id] : ids.special()) {
      // TODO: Specials
    }

    for (const auto& [letter_name, ap_id] : ids.letters()) {
      LetterIdentifier li =
          std::make_tuple(letter_name[0], letter_name[1] == '2');
      LetterInfo& letter_info = info_.letters[li];
      letter_info.has_id = true;
    }

    for (const auto& [ending_name, ap_id] : ids.endings()) {
      EndingInfo& ending_info = info_.endings[ending_name];
      ending_info.has_id = true;
    }

    for (const auto& [prog_name, ap_id] : ids.progressives()) {
      ProgressiveInfo& prog_info = info_.progressives[prog_name];
      prog_info.has_id = true;
    }

    for (const auto& [group_name, ap_id] : ids.door_groups()) {
      DoorGroupInfo& group_info = info_.door_groups[group_name];
      group_info.has_id = true;
    }
  }

  std::string mapdir_;
  CollectedInfo& info_;
};

}  // namespace

void ProcessHumanData(const std::string& mapdir, CollectedInfo& info) {
  HumanProcessor human_processor(mapdir, info);
  human_processor.Run();
}

}  // namespace com::fourisland::lingo2_archipelago