Porytiles
Loading...
Searching...
No Matches
project_tileset_metadata_provider.cpp
Go to the documentation of this file.
2
3#include <filesystem>
4#include <map>
5#include <set>
6#include <string>
7#include <vector>
8
16
17namespace {
18
19using namespace porytiles;
20
21const std::filesystem::path headers_rel_path = std::filesystem::path{"src"} / "data" / "tilesets" / "headers.h";
22
23// Project source file paths for INCBIN parsing
24const std::filesystem::path graphics_rel_path = std::filesystem::path{"src"} / "data" / "tilesets" / "graphics.h";
25const std::filesystem::path metatiles_rel_path = std::filesystem::path{"src"} / "data" / "tilesets" / "metatiles.h";
26const std::filesystem::path src_graphics_rel_path = std::filesystem::path{"src"} / "graphics.c";
27
42[[nodiscard]] ChainableResult<void> ensure_metadata_parsed(
43 const std::filesystem::path &project_root,
44 bool &metadata_parsed,
45 std::map<std::string, ProjectTilesetMetadata> &tileset_metadata,
46 const TextFormatter *format)
47{
48 if (metadata_parsed) {
49 return {};
50 }
51
52 const auto headers_path = project_root / headers_rel_path;
53 CParserFacade parser{headers_path, format};
54
56 struct_decls,
57 parser.parse_struct_initializers("gTileset_"),
58 void,
59 format->format("Failed to parse tileset headers from '{}'.", FormatParam{headers_path.string(), Style::bold}));
60
61 for (auto &struct_decl : struct_decls) {
62 const auto &tileset_name = struct_decl.variable_name();
63
64 auto is_secondary_opt = struct_decl.field_value("isSecondary");
65 bool is_secondary = is_secondary_opt.has_value() && is_secondary_opt.value() == "TRUE";
66
67 auto tiles_var = struct_decl.field_value("tiles");
68 auto palettes_var = struct_decl.field_value("palettes");
69 auto metatiles_var = struct_decl.field_value("metatiles");
70 auto metatile_attributes_var = struct_decl.field_value("metatileAttributes");
71 auto callback_var = struct_decl.field_value("callback");
72
73 if (!tiles_var.has_value() || !palettes_var.has_value() || !metatiles_var.has_value() ||
74 !metatile_attributes_var.has_value()) {
75 continue;
76 }
77
78 std::optional<std::string> callback_func = std::nullopt;
79 if (callback_var.has_value() && callback_var.value() != "NULL") {
80 callback_func = callback_var.value();
81 }
82
83 tileset_metadata.emplace(
84 tileset_name,
86 tileset_name,
87 is_secondary,
88 tiles_var.value(),
89 palettes_var.value(),
90 metatiles_var.value(),
91 metatile_attributes_var.value(),
92 callback_func});
93 }
94
95 metadata_parsed = true;
96 return {};
97}
98
115[[nodiscard]] ChainableResult<void> ensure_artifact_paths_parsed(
116 const std::filesystem::path &project_root,
117 bool &metadata_parsed,
118 std::map<std::string, ProjectTilesetMetadata> &tileset_metadata,
119 bool &artifact_paths_parsed,
120 std::map<std::string, ProjectTilesetArtifactPaths> &tileset_artifact_paths,
121 const TextFormatter *format)
122{
123 if (artifact_paths_parsed) {
124 return {};
125 }
126
128 ensure_metadata_parsed(project_root, metadata_parsed, tileset_metadata, format),
129 void,
130 "Failed to resolve artifact paths.");
131
132 // Parse all INCBIN sources into a local map
133 std::map<std::string, IncbinDeclaration> incbin_vars;
134
135 const auto graphics_path = project_root / graphics_rel_path;
136 CParserFacade graphics_parser{graphics_path, format};
138 graphics_incbins,
139 graphics_parser.parse_incbin_arrays(),
140 void,
141 format->format(
142 "Failed to parse graphics INCBINs from '{}'.", FormatParam{graphics_path.string(), Style::bold}));
143 for (auto &incbin : graphics_incbins) {
144 incbin_vars.emplace(incbin.variable_name(), std::move(incbin));
145 }
146
147 const auto metatiles_path = project_root / metatiles_rel_path;
148 CParserFacade metatiles_parser{metatiles_path, format};
150 metatiles_incbins,
151 metatiles_parser.parse_incbin_arrays(),
152 void,
153 format->format(
154 "Failed to parse metatiles INCBINs from '{}'.", FormatParam{metatiles_path.string(), Style::bold}));
155 for (auto &incbin : metatiles_incbins) {
156 incbin_vars.emplace(incbin.variable_name(), std::move(incbin));
157 }
158
159 const auto src_graphics_path = project_root / src_graphics_rel_path;
160 CParserFacade src_graphics_parser{src_graphics_path, format};
162 src_graphics_incbins,
163 src_graphics_parser.parse_incbin_arrays(),
164 void,
165 format->format(
166 "Failed to parse graphics INCBINs from '{}'.", FormatParam{src_graphics_path.string(), Style::bold}));
167 for (auto &incbin : src_graphics_incbins) {
168 incbin_vars.emplace(incbin.variable_name(), std::move(incbin));
169 }
170
171 // Resolve paths per-tileset
172 for (const auto &[tileset_name, metadata] : tileset_metadata) {
173 auto tiles_it = incbin_vars.find(metadata.tiles_var());
174 auto palettes_it = incbin_vars.find(metadata.palettes_var());
175 auto metatiles_it = incbin_vars.find(metadata.metatiles_var());
176 auto attrs_it = incbin_vars.find(metadata.metatile_attributes_var());
177
178 if (tiles_it == incbin_vars.end() || palettes_it == incbin_vars.end() || metatiles_it == incbin_vars.end() ||
179 attrs_it == incbin_vars.end()) {
180 continue;
181 }
182
183 if (tiles_it->second.paths().empty() || palettes_it->second.paths().empty() ||
184 metatiles_it->second.paths().empty() || attrs_it->second.paths().empty()) {
185 continue;
186 }
187
188 std::vector<std::filesystem::path> palette_paths;
189 palette_paths.reserve(palettes_it->second.paths().size());
190 for (const auto &path_str : palettes_it->second.paths()) {
191 palette_paths.emplace_back(path_str);
192 }
193
194 tileset_artifact_paths.emplace(
195 tileset_name,
197 std::filesystem::path{tiles_it->second.paths().front()},
198 std::move(palette_paths),
199 std::filesystem::path{metatiles_it->second.paths().front()},
200 std::filesystem::path{attrs_it->second.paths().front()}});
201 }
202
203 artifact_paths_parsed = true;
204 return {};
205}
206
207} // namespace
208
209namespace porytiles {
210
211bool ProjectTilesetMetadataProvider::exists(const std::string &tileset_name) const
212{
213 const auto metadata_result = metadata_for(tileset_name);
214 return metadata_result.has_value();
215}
216
218{
219 PT_TRY_ASSIGN_PASS_ERR(metadata, metadata_for(tileset_name), bool);
220 return metadata.is_secondary();
221}
222
224{
225 PT_TRY_ASSIGN_PASS_ERR(metadata, metadata_for(tileset_name), bool);
226 return metadata.has_animations();
227}
228
230{
232 ensure_metadata_parsed(project_root_, metadata_parsed_, tileset_metadata_, format_),
233 std::set<std::string>,
234 "Failed to enumerate tilesets.");
235
236 std::set<std::string> names;
237 for (const auto &[tileset_name, metadata] : tileset_metadata_) {
238 names.insert(tileset_name);
239 }
240 return names;
241}
242
244ProjectTilesetMetadataProvider::metadata_for(const std::string &tileset_name) const
245{
247 ensure_metadata_parsed(project_root_, metadata_parsed_, tileset_metadata_, format_),
249 format_->format("Failed to get metadata for tileset '{}'.", FormatParam{tileset_name, Style::bold}));
250
251 if (!tileset_metadata_.contains(tileset_name)) {
252 return FormattableError{
253 format_->format("Tileset '{}' not found in headers.h file.", FormatParam{tileset_name, Style::bold})};
254 }
255
256 return tileset_metadata_.at(tileset_name);
257}
258
260ProjectTilesetMetadataProvider::artifact_paths_for(const std::string &tileset_name) const
261{
263 ensure_artifact_paths_parsed(
264 project_root_,
265 metadata_parsed_,
266 tileset_metadata_,
267 artifact_paths_parsed_,
268 tileset_artifact_paths_,
269 format_),
271 format_->format("Failed to get artifact paths for tileset '{}'.", FormatParam{tileset_name, Style::bold}));
272
273 if (!tileset_artifact_paths_.contains(tileset_name)) {
274 return FormattableError{format_->format(
275 "Could not resolve artifact paths for tileset '{}'.", FormatParam{tileset_name, Style::bold})};
276 }
277
278 return tileset_artifact_paths_.at(tileset_name);
279}
280
282{
283 metadata_parsed_ = false;
284 tileset_metadata_.clear();
285 artifact_paths_parsed_ = false;
286 tileset_artifact_paths_.clear();
287}
288
289} // namespace porytiles
#define PT_TRY_ASSIGN_CHAIN_ERR(var, expr, return_type,...)
Unwraps a ChainableResult, chaining a new error message on failure.
#define PT_TRY_ASSIGN_PASS_ERR(var, expr, return_type)
Unwraps a ChainableResult, passing through the error chain with an empty FormattableError when types ...
#define PT_TRY_CALL_CHAIN_ERR(expr, return_type,...)
Unwraps a void ChainableResult, chaining a new error message on failure.
High-level facade for parsing C/C++ source files.
A result type that maintains a chainable sequence of errors for debugging and error reporting.
A text parameter with associated styling for formatted output.
General-purpose error implementation with formatted message support.
Definition error.hpp:63
Represents resolved filesystem paths for tileset artifacts from INCBIN declarations.
void invalidate_metadata_cache() const
Invalidates the lazy-loaded metadata and artifact paths caches, forcing a re-parse on next access.
ChainableResult< ProjectTilesetArtifactPaths > artifact_paths_for(const std::string &tileset_name) const
Resolves artifact paths for a tileset by parsing INCBIN declarations.
ChainableResult< std::set< std::string > > tilesets() const override
Returns all tileset names known to this provider.
bool exists(const std::string &tileset_name) const override
Checks whether a tileset exists in the backing store for the given tileset name.
ChainableResult< ProjectTilesetMetadata > metadata_for(const std::string &tileset_name) const
Retrieves metadata for a specific tileset from the struct cache.
ChainableResult< bool > has_animations(const std::string &tileset_name) const override
Determines whether a tileset has animation support.
ChainableResult< bool > is_secondary(const std::string &tileset_name) const override
Determines whether a tileset is a secondary tileset.
Represents raw parsed metadata from a tileset struct in headers.h.
static const Style bold
Bold text formatting.
Abstract base class for applying text styling with context-aware formatting.
virtual std::string format(const std::string &format_str, const std::vector< FormatParam > &params) const
Formats a string with styled parameters using fmtlib syntax.