or LINGO 2 using Archipelago Multiworld
about summary refs log blame commit diff stats
path: root/tools/datapacker/main.cpp
blob: 1dcd109143361a833351ef45508fa887964153cb (plain) (tree)
1
2
3
4
5
6
7
8


                                        
                    


                     
                   


                   

                 








                                                

                                                  


                           
 



                                                                        

 








                                                                             
                              












                                                                         































































































































































                                                                                







                                                                           

































                                                                               




























                                                                              
                                                          














































































































                                                                               


                                                          

                                                                          





                                   


                                                                          


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

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

#include "container.h"
#include "proto/data.pb.h"
#include "proto/human.pb.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 DataPacker {
 public:
  DataPacker(const std::string& mapdir, const std::string& outputpath)
      : mapdir_(mapdir), outputpath_(outputpath) {}

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

    ProcessConnectionsFile(datadir_path / "connections.txtpb", std::nullopt);
    ProcessMaps(datadir_path);

    {
      std::ofstream outputfile(outputpath_);
      container_.all_objects().SerializeToOstream(&outputfile);
    }

    std::string output;
    google::protobuf::TextFormat::PrintToString(container_.all_objects(),
                                                &output);
    std::cout << output << std::endl;
  }

 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();

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

  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) {
    uint64_t room_id =
        container_.FindOrAddRoom(current_map_name, h_room.name(), std::nullopt);
    Room& room = *container_.all_objects().mutable_rooms(room_id);

    room.set_display_name(h_room.display_name());

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

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

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

    std::copy(
        h_room.letters().begin(), h_room.letters().end(),
        google::protobuf::RepeatedFieldBackInserter(room.mutable_letters()));
  }

  uint64_t ProcessPanel(const HumanPanel& h_panel,
                        const std::string& current_map_name,
                        const std::string& current_room_name) {
    uint64_t panel_id =
        container_.FindOrAddPanel(current_map_name, current_room_name,
                                  h_panel.name(), std::nullopt, std::nullopt);
    Panel& panel = *container_.all_objects().mutable_panels(panel_id);

    panel.set_path(h_panel.path());
    panel.set_clue(h_panel.clue());
    panel.set_answer(h_panel.answer());

    std::copy(
        h_panel.symbols().begin(), h_panel.symbols().end(),
        google::protobuf::RepeatedFieldBackInserter(panel.mutable_symbols()));
    std::copy(
        h_panel.proxies().begin(), h_panel.proxies().end(),
        google::protobuf::RepeatedFieldBackInserter(panel.mutable_proxies()));

    if (h_panel.has_required_door()) {
      std::optional<std::string> map_name =
          h_panel.required_door().has_map()
              ? std::optional<std::string>(h_panel.required_door().map())
              : std::nullopt;
      panel.set_required_door(container_.FindOrAddDoor(
          map_name, h_panel.required_door().name(), current_map_name));
    }

    return panel_id;
  }

  uint64_t ProcessPainting(const HumanPainting& h_painting,
                           const std::string& current_map_name,
                           const std::string& current_room_name) {
    uint64_t painting_id = container_.FindOrAddPainting(
        current_map_name, current_room_name, h_painting.name(), std::nullopt,
        std::nullopt);
    Painting& painting =
        *container_.all_objects().mutable_paintings(painting_id);

    painting.set_path(h_painting.path());
    painting.set_display_name(h_painting.display_name());
    painting.set_orientation(h_painting.orientation());

    if (h_painting.has_flipped()) {
      painting.set_flipped(h_painting.flipped());
    }

    if (h_painting.has_move()) {
      painting.set_move(h_painting.move());
    }

    if (h_painting.has_enter_only()) {
      painting.set_enter_only(h_painting.enter_only());
    }

    if (h_painting.has_required_door()) {
      std::optional<std::string> map_name =
          h_painting.required_door().has_map()
              ? std::optional<std::string>(h_painting.required_door().map())
              : std::nullopt;
      painting.set_required_door(container_.FindOrAddDoor(
          map_name, h_painting.required_door().name(), current_map_name));
    }

    return painting_id;
  }

  uint64_t ProcessPort(const HumanPort& h_port,
                       const std::string& current_map_name,
                       const std::string& current_room_name) {
    uint64_t port_id =
        container_.FindOrAddPort(current_map_name, current_room_name,
                                 h_port.name(), std::nullopt, std::nullopt);
    Port& port = *container_.all_objects().mutable_ports(port_id);

    port.set_path(h_port.path());
    port.set_orientation(h_port.orientation());

    if (h_port.has_required_door()) {
      std::optional<std::string> map_name =
          h_port.required_door().has_map()
              ? std::optional<std::string>(h_port.required_door().map())
              : std::nullopt;
      port.set_required_door(container_.FindOrAddDoor(
          map_name, h_port.required_door().name(), current_map_name));
    }

    return port_id;
  }

  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) {
    uint64_t door_id =
        container_.FindOrAddDoor(current_map_name, h_door.name(), std::nullopt);
    Door& door = *container_.all_objects().mutable_doors(door_id);

    if (h_door.has_location_room()) {
      door.set_room_id(container_.FindOrAddRoom(
          current_map_name, h_door.location_room(), std::nullopt));

      Room& room = *container_.all_objects().mutable_rooms(door.room_id());
      room.add_doors(door_id);
    }

    std::copy(
        h_door.receivers().begin(), h_door.receivers().end(),
        google::protobuf::RepeatedFieldBackInserter(door.mutable_receivers()));
    std::copy(
        h_door.switches().begin(), h_door.switches().end(),
        google::protobuf::RepeatedFieldBackInserter(door.mutable_switches()));

    for (const PaintingIdentifier& pi : h_door.move_paintings()) {
      std::optional<std::string> map_name =
          pi.has_map() ? std::optional<std::string>(pi.map()) : std::nullopt;
      door.add_move_paintings(container_.FindOrAddPainting(
          map_name, pi.room(), pi.name(), current_map_name, std::nullopt));
    }

    for (const PanelIdentifier& pi : h_door.panels()) {
      ProxyIdentifier* proxy = door.add_panels();

      std::optional<std::string> map_name =
          pi.has_map() ? std::optional<std::string>(pi.map()) : std::nullopt;
      proxy->set_panel(container_.FindOrAddPanel(
          map_name, pi.room(), pi.name(), current_map_name, std::nullopt));

      if (pi.has_answer()) {
        proxy->set_answer(pi.answer());
      }
    }

    if (h_door.has_control_center_color()) {
      door.set_control_center_color(h_door.control_center_color());
    }

    door.set_type(h_door.type());
  }

  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) {
    Connection f_connection;

    if (human_connection.has_from_room()) {
      f_connection.set_from_room(container_.FindOrAddRoom(
          std::nullopt, human_connection.from_room(), current_map_name));
    } else if (human_connection.has_from()) {
      ProcessSingleConnection(human_connection.from(), current_map_name,
                              f_connection);
    }

    Connection r_connection;
    r_connection.set_to_room(f_connection.from_room());

    if (human_connection.has_to_room()) {
      r_connection.set_from_room(container_.FindOrAddRoom(
          std::nullopt, human_connection.to_room(), current_map_name));
    } else if (human_connection.has_to()) {
      ProcessSingleConnection(human_connection.to(), current_map_name,
                              r_connection);
    }

    f_connection.set_to_room(r_connection.from_room());

    if (human_connection.has_door()) {
      std::optional<std::string> map_name =
          human_connection.door().has_map()
              ? std::optional<std::string>(human_connection.door().map())
              : std::nullopt;
      uint64_t door_id = container_.FindOrAddDoor(
          map_name, human_connection.door().name(), current_map_name);
      f_connection.set_required_door(door_id);
      r_connection.set_required_door(door_id);
    }

    container_.AddConnection(f_connection);
    if (!human_connection.oneway()) {
      container_.AddConnection(r_connection);
    }
  }

  void ProcessSingleConnection(
      const HumanConnection::Endpoint& endpoint,
      const std::optional<std::string>& current_map_name,
      Connection& connection) {
    if (endpoint.has_room()) {
      std::optional<std::string> map_name =
          endpoint.room().has_map()
              ? std::optional<std::string>(endpoint.room().map())
              : std::nullopt;
      connection.set_from_room(container_.FindOrAddRoom(
          map_name, endpoint.room().name(), current_map_name));
    } else if (endpoint.has_painting()) {
      std::optional<std::string> map_name =
          endpoint.painting().has_map()
              ? std::optional<std::string>(endpoint.painting().map())
              : std::nullopt;

      std::string room_name;
      if (!endpoint.painting().has_room()) {
        std::cout << "Missing room name for painting "
                  << endpoint.painting().name() << std::endl;
        room_name = "default";
      } else {
        room_name = endpoint.painting().room();
      }

      connection.set_from_room(
          container_.FindOrAddRoom(map_name, room_name, current_map_name));
      connection.set_painting(container_.FindOrAddPainting(
          map_name, room_name, endpoint.painting().name(), current_map_name,
          std::nullopt));
    } else if (endpoint.has_port()) {
      std::optional<std::string> map_name =
          endpoint.port().has_map()
              ? std::optional<std::string>(endpoint.port().map())
              : std::nullopt;

      std::string room_name;
      if (!endpoint.port().has_room()) {
        std::cout << "Missing room name for port " << endpoint.port().name()
                  << std::endl;
        room_name = "default";
      } else {
        room_name = endpoint.port().room();
      }

      connection.set_from_room(
          container_.FindOrAddRoom(map_name, room_name, current_map_name));
      connection.set_port(
          container_.FindOrAddPort(map_name, room_name, endpoint.port().name(),
                                   current_map_name, std::nullopt));
    } else if (endpoint.has_panel()) {
      std::optional<std::string> map_name =
          endpoint.panel().has_map()
              ? std::optional<std::string>(endpoint.panel().map())
              : std::nullopt;

      std::string room_name;
      if (!endpoint.panel().has_room()) {
        std::cout << "Missing room name for panel " << endpoint.panel().name()
                  << std::endl;
        room_name = "default";
      } else {
        room_name = endpoint.panel().room();
      }

      connection.set_from_room(
          container_.FindOrAddRoom(map_name, room_name, current_map_name));
      connection.mutable_panel()->set_panel(container_.FindOrAddPanel(
          map_name, room_name, endpoint.panel().name(), current_map_name,
          std::nullopt));
      if (endpoint.panel().has_answer()) {
        connection.mutable_panel()->set_answer(endpoint.panel().answer());
      }
    }
  }

  std::string mapdir_;
  std::string outputpath_;

  Container container_;
};

}  // namespace
}  // namespace com::fourisland::lingo2_archipelago

int main(int argc, char** argv) {
  if (argc != 3) {
    std::cout << "Incorrect argument count." << std::endl;
    std::cout << "Usage: datapacker [path to map directory] [output file]"
              << std::endl;
    return 1;
  }

  std::string mapdir = argv[1];
  std::string outputpath = argv[2];

  com::fourisland::lingo2_archipelago::DataPacker data_packer(mapdir,
                                                              outputpath);
  data_packer.Run();

  return 0;
}