#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;
}