#include "godot_scene.h" #include #include #include #include namespace com::fourisland::lingo2_archipelago { namespace { class GodotSceneImpl : public GodotScene { public: GodotSceneImpl(std::map ext_resources, std::unique_ptr root, std::vector> descendents) : ext_resources_(std::move(ext_resources)), root_(std::move(root)), descendents_(std::move(descendents)) {} virtual const GodotExtResource* GetExtResource(const std::string& id) const { auto it = ext_resources_.find(id); if (it != ext_resources_.end()) { return &it->second; } else { return nullptr; } } virtual const GodotNode& GetRoot() const { return *root_; } private: std::map ext_resources_; std::unique_ptr root_; std::vector> descendents_; }; struct Heading { std::string type; std::string id; std::string path; std::string resource_type; std::string name; std::string parent; GodotInstanceType instance_type; }; Heading ParseTscnHeading(absl::string_view line) { std::string original_line(line); Heading heading; if (line[0] != '[') { std::ostringstream errormsg; errormsg << "Heading must start with [." << std::endl << "Bad heading: " << original_line; throw std::invalid_argument(errormsg.str()); } line.remove_prefix(1); int divider = line.find_first_of(" ]"); if (divider == std::string_view::npos) { std::ostringstream errormsg; errormsg << "Malformatted heading: " << line << std::endl << "Original line: " << original_line; throw std::invalid_argument(errormsg.str()); } heading.type = std::string(line.substr(0, divider)); line.remove_prefix(divider + 1); while (!line.empty()) { divider = line.find_first_of("="); if (divider == std::string_view::npos) { std::ostringstream errormsg; errormsg << "Malformatted heading: " << line << std::endl << "Original line: " << original_line; throw std::invalid_argument(errormsg.str()); } std::string key(line.substr(0, divider)); line.remove_prefix(divider + 1); if (line[0] == '"') { line.remove_prefix(1); divider = line.find_first_of("\""); if (divider == std::string_view::npos) { std::ostringstream errormsg; errormsg << "Malformatted heading: " << line << std::endl << "Original line: " << original_line; throw std::invalid_argument(errormsg.str()); } std::string strval(line.substr(0, divider)); line.remove_prefix(divider + 2); if (key == "name") { heading.name = strval; } else if (key == "parent") { heading.parent = strval; } else if (key == "path") { heading.path = strval; } else if (key == "type") { heading.resource_type = strval; } else if (key == "id") { heading.id = strval; } } else if (line[0] == 'S' || line[0] == 'E') { GodotInstanceType rrval; char internal = line[0]; line.remove_prefix(13); // SubResource(" divider = line.find_first_of("\""); if (divider == std::string_view::npos) { std::ostringstream errormsg; errormsg << "Malformatted heading: " << line << std::endl << "Original line: " << original_line; throw std::invalid_argument(errormsg.str()); } std::string refid = std::string(line.substr(0, divider)); line.remove_prefix(divider + 3); GodotInstanceType instance_type; if (internal == 'E') { instance_type = GodotExtResourceRef{.id = refid}; } else { // SubResource is not supported right now. } if (key == "instance") { heading.instance_type = instance_type; } else { // Other keys aren't supported right now. } } else { divider = line.find_first_of(" ]"); if (divider == std::string_view::npos) { std::ostringstream errormsg; errormsg << "Malformatted heading: " << line << std::endl << "Original line: " << original_line; throw std::invalid_argument(errormsg.str()); } int numval = std::atoi(line.substr(0, divider).data()); line.remove_prefix(divider + 1); // keyvals_[key] = numval; } } return heading; } } // namespace void GodotNode::AddChild(GodotNode& child) { children_[child.GetName()] = &child; child.parent_ = this; } std::string GodotNode::GetPath() const { if (parent_ == nullptr || parent_->GetName() == "") { return name_; } else { return parent_->GetPath() + "/" + name_; } } const GodotNode* GodotNode::GetNode(absl::string_view path) const { std::vector names = absl::StrSplit(path, "/"); auto it = children_.find(names[0]); if (it == children_.end()) { return nullptr; } else { if (names.size() == 1) { return it->second; } else { path.remove_prefix(names[0].size() + 1); return it->second->GetNode(path); } } } GodotNode* GodotNode::GetNode(absl::string_view path) { return const_cast( const_cast(this)->GetNode(path)); } std::unique_ptr ReadGodotSceneFromFile(const std::string& path) { std::map ext_resources; auto root = std::make_unique("", GodotInstanceType{}); std::vector> descendents; std::ifstream input(path); std::string line; bool section_started = false; Heading cur_heading; std::ostringstream cur_value; bool value_started = false; auto handle_end_of_section = [&]() { section_started = false; value_started = false; if (cur_heading.type == "sub_resource") { // sub_resources_[std::get(cur_heading.GetKeyval("id"))] = // {cur_heading, cur_value.str(), ""}; } else { // other_.emplace_back(cur_heading, cur_value.str()); } cur_value = {}; }; while (std::getline(input, line)) { if (section_started && (line.empty() || line[0] == '[')) { handle_end_of_section(); } if (!line.empty() && line[0] == '[') { Heading heading = ParseTscnHeading(line); if (heading.type == "gd_scene") { // file_descriptor_ = heading; } else if (heading.type == "ext_resource") { GodotExtResource ext_resource; ext_resource.path = heading.path; ext_resource.type = heading.resource_type; ext_resources[heading.id] = ext_resource; } else if (heading.type == "node") { if (heading.parent != "") { descendents.push_back( std::make_unique(heading.name, heading.instance_type)); GodotNode* child = descendents.back().get(); if (heading.parent == ".") { root->AddChild(*child); } else { root->GetNode(heading.parent)->AddChild(*child); } } } else { cur_heading = heading; section_started = true; } } else if (!line.empty()) { if (value_started) { cur_value << std::endl; } else { value_started = true; } cur_value << line; } } if (section_started) { handle_end_of_section(); } return std::make_unique( std::move(ext_resources), std::move(root), std::move(descendents)); } } // namespace com::fourisland::lingo2_archipelago