summary refs log tree commit diff stats
path: root/tools/util/godot_scene.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tools/util/godot_scene.cpp')
-rw-r--r--tools/util/godot_scene.cpp269
1 files changed, 269 insertions, 0 deletions
diff --git a/tools/util/godot_scene.cpp b/tools/util/godot_scene.cpp new file mode 100644 index 0000000..272111d --- /dev/null +++ b/tools/util/godot_scene.cpp
@@ -0,0 +1,269 @@
1#include "godot_scene.h"
2
3#include <absl/strings/str_split.h>
4
5#include <fstream>
6#include <sstream>
7#include <variant>
8
9namespace com::fourisland::lingo2_archipelago {
10
11namespace {
12
13class GodotSceneImpl : public GodotScene {
14 public:
15 GodotSceneImpl(std::map<std::string, GodotExtResource> ext_resources,
16 std::unique_ptr<GodotNode> root,
17 std::vector<std::unique_ptr<GodotNode>> descendents)
18 : ext_resources_(std::move(ext_resources)),
19 root_(std::move(root)),
20 descendents_(std::move(descendents)) {}
21
22 virtual const GodotExtResource* GetExtResource(const std::string& id) const {
23 auto it = ext_resources_.find(id);
24 if (it != ext_resources_.end()) {
25 return &it->second;
26 } else {
27 return nullptr;
28 }
29 }
30
31 virtual const GodotNode& GetRoot() const { return *root_; }
32
33 private:
34 std::map<std::string, GodotExtResource> ext_resources_;
35 std::unique_ptr<GodotNode> root_;
36 std::vector<std::unique_ptr<GodotNode>> descendents_;
37};
38
39struct Heading {
40 std::string type;
41
42 std::string id;
43 std::string path;
44 std::string resource_type;
45
46 std::string name;
47 std::string parent;
48 GodotInstanceType instance_type;
49};
50
51Heading ParseTscnHeading(absl::string_view line) {
52 std::string original_line(line);
53 Heading heading;
54
55 if (line[0] != '[') {
56 std::ostringstream errormsg;
57 errormsg << "Heading must start with [." << std::endl
58 << "Bad heading: " << original_line;
59 throw std::invalid_argument(errormsg.str());
60 }
61
62 line.remove_prefix(1);
63 int divider = line.find_first_of(" ]");
64 if (divider == std::string_view::npos) {
65 std::ostringstream errormsg;
66 errormsg << "Malformatted heading: " << line << std::endl
67 << "Original line: " << original_line;
68 throw std::invalid_argument(errormsg.str());
69 }
70
71 heading.type = std::string(line.substr(0, divider));
72 line.remove_prefix(divider + 1);
73
74 while (!line.empty()) {
75 divider = line.find_first_of("=");
76 if (divider == std::string_view::npos) {
77 std::ostringstream errormsg;
78 errormsg << "Malformatted heading: " << line << std::endl
79 << "Original line: " << original_line;
80 throw std::invalid_argument(errormsg.str());
81 }
82
83 std::string key(line.substr(0, divider));
84 line.remove_prefix(divider + 1);
85
86 if (line[0] == '"') {
87 line.remove_prefix(1);
88 divider = line.find_first_of("\"");
89
90 if (divider == std::string_view::npos) {
91 std::ostringstream errormsg;
92 errormsg << "Malformatted heading: " << line << std::endl
93 << "Original line: " << original_line;
94 throw std::invalid_argument(errormsg.str());
95 }
96
97 std::string strval(line.substr(0, divider));
98 line.remove_prefix(divider + 2);
99
100 if (key == "name") {
101 heading.name = strval;
102 } else if (key == "parent") {
103 heading.parent = strval;
104 } else if (key == "path") {
105 heading.path = strval;
106 } else if (key == "type") {
107 heading.resource_type = strval;
108 } else if (key == "id") {
109 heading.id = strval;
110 }
111 } else if (line[0] == 'S' || line[0] == 'E') {
112 GodotInstanceType rrval;
113 char internal = line[0];
114
115 line.remove_prefix(13); // SubResource("
116 divider = line.find_first_of("\"");
117
118 if (divider == std::string_view::npos) {
119 std::ostringstream errormsg;
120 errormsg << "Malformatted heading: " << line << std::endl
121 << "Original line: " << original_line;
122 throw std::invalid_argument(errormsg.str());
123 }
124
125 std::string refid = std::string(line.substr(0, divider));
126 line.remove_prefix(divider + 3);
127
128 GodotInstanceType instance_type;
129 if (internal == 'E') {
130 instance_type = GodotExtResourceRef{.id = refid};
131 } else {
132 // SubResource is not supported right now.
133 }
134
135 if (key == "instance") {
136 heading.instance_type = instance_type;
137 } else {
138 // Other keys aren't supported right now.
139 }
140 } else {
141 divider = line.find_first_of(" ]");
142
143 if (divider == std::string_view::npos) {
144 std::ostringstream errormsg;
145 errormsg << "Malformatted heading: " << line << std::endl
146 << "Original line: " << original_line;
147 throw std::invalid_argument(errormsg.str());
148 }
149
150 int numval = std::atoi(line.substr(0, divider).data());
151 line.remove_prefix(divider + 1);
152
153 // keyvals_[key] = numval;
154 }
155 }
156
157 return heading;
158}
159
160} // namespace
161
162void GodotNode::AddChild(GodotNode& child) {
163 children_[child.GetName()] = &child;
164 child.parent_ = this;
165}
166
167std::string GodotNode::GetPath() const {
168 if (parent_ == nullptr || parent_->GetName() == "") {
169 return name_;
170 } else {
171 return parent_->GetPath() + "/" + name_;
172 }
173}
174
175const GodotNode* GodotNode::GetNode(absl::string_view path) const {
176 std::vector<std::string> names = absl::StrSplit(path, "/");
177
178 auto it = children_.find(names[0]);
179 if (it == children_.end()) {
180 return nullptr;
181 } else {
182 if (names.size() == 1) {
183 return it->second;
184 } else {
185 path.remove_prefix(names[0].size() + 1);
186
187 return it->second->GetNode(path);
188 }
189 }
190}
191
192GodotNode* GodotNode::GetNode(absl::string_view path) {
193 return const_cast<GodotNode*>(
194 const_cast<const GodotNode*>(this)->GetNode(path));
195}
196
197std::unique_ptr<GodotScene> ReadGodotSceneFromFile(const std::string& path) {
198 std::map<std::string, GodotExtResource> ext_resources;
199 auto root = std::make_unique<GodotNode>("", GodotInstanceType{});
200 std::vector<std::unique_ptr<GodotNode>> descendents;
201
202 std::ifstream input(path);
203
204 std::string line;
205 bool section_started = false;
206 Heading cur_heading;
207 std::ostringstream cur_value;
208 bool value_started = false;
209 auto handle_end_of_section = [&]() {
210 section_started = false;
211 value_started = false;
212
213 if (cur_heading.type == "sub_resource") {
214 // sub_resources_[std::get<int>(cur_heading.GetKeyval("id"))] =
215 // {cur_heading, cur_value.str(), ""};
216 } else {
217 // other_.emplace_back(cur_heading, cur_value.str());
218 }
219
220 cur_value = {};
221 };
222 while (std::getline(input, line)) {
223 if (section_started && (line.empty() || line[0] == '[')) {
224 handle_end_of_section();
225 }
226 if (!line.empty() && line[0] == '[') {
227 Heading heading = ParseTscnHeading(line);
228 if (heading.type == "gd_scene") {
229 // file_descriptor_ = heading;
230 } else if (heading.type == "ext_resource") {
231 GodotExtResource ext_resource;
232 ext_resource.path = heading.path;
233 ext_resource.type = heading.resource_type;
234
235 ext_resources[heading.id] = ext_resource;
236 } else if (heading.type == "node") {
237 if (heading.parent != "") {
238 descendents.push_back(
239 std::make_unique<GodotNode>(heading.name, heading.instance_type));
240 GodotNode* child = descendents.back().get();
241
242 if (heading.parent == ".") {
243 root->AddChild(*child);
244 } else {
245 root->GetNode(heading.parent)->AddChild(*child);
246 }
247 }
248 } else {
249 cur_heading = heading;
250 section_started = true;
251 }
252 } else if (!line.empty()) {
253 if (value_started) {
254 cur_value << std::endl;
255 } else {
256 value_started = true;
257 }
258 cur_value << line;
259 }
260 }
261 if (section_started) {
262 handle_end_of_section();
263 }
264
265 return std::make_unique<GodotSceneImpl>(
266 std::move(ext_resources), std::move(root), std::move(descendents));
267}
268
269} // namespace com::fourisland::lingo2_archipelago