Porytiles
Loading...
Searching...
No Matches
project_tileset_artifact_key_provider.cpp
Go to the documentation of this file.
2
3#include <filesystem>
4#include <format>
5#include <set>
6#include <string>
7
12
13namespace {
14
15using namespace porytiles;
16
17// Artifact paths
18const std::filesystem::path src_dir{"src"};
19const std::filesystem::path porytiles_generated_include{"include/porytiles_generated/tilesets"};
20const std::filesystem::path porytiles_src{"porytiles_src"};
21const std::filesystem::path porytiles_bin{"porytiles_bin"};
22const std::filesystem::path anim_dir{"anim"};
23const std::filesystem::path generated_anim_code_header{"generated_anim_code.h"};
24const std::filesystem::path bottom_png{"bottom.png"};
25const std::filesystem::path middle_png{"middle.png"};
26const std::filesystem::path top_png{"top.png"};
27const std::filesystem::path attributes_csv{"attributes.csv"};
28const std::filesystem::path porytiles_palettes{"palettes"};
29const std::filesystem::path porymap_palettes{"palettes"};
30const std::filesystem::path anim_json{"anim.json"};
31const std::filesystem::path metatiles_bin{"metatiles.bin"};
32const std::filesystem::path attributes_bin{"metatile_attributes.bin"};
33const std::filesystem::path tiles_png{"tiles.png"};
34
46[[nodiscard]] ChainableResult<std::set<std::string>> scan_subdirectories(
47 const std::filesystem::path &dir_path, const std::string &artifact_type, const TextFormatter *format)
48{
49 if (!std::filesystem::exists(dir_path) || !std::filesystem::is_directory(dir_path)) {
50 return std::set<std::string>{};
51 }
52
53 std::set<std::string> result;
54 for (const auto &entry : std::filesystem::directory_iterator{dir_path}) {
55 if (!entry.is_directory()) {
56 continue;
57 }
58 const std::string dir_name = entry.path().filename().string();
59 const auto expected_snake = DynamicCasedName{dir_name}.to_snake_case();
60 if (expected_snake != dir_name) {
62 "{} directory name '{}' must be snake_case (expected '{}')",
63 FormatParam{artifact_type, Style::bold},
64 FormatParam{dir_name, Style::bold},
65 FormatParam{expected_snake, Style::bold})}};
66 }
67 result.insert(dir_name);
68 }
69 return result;
70}
71
84scan_png_files(const std::filesystem::path &dir_path, const std::string &artifact_type, const TextFormatter *format)
85{
86 if (!std::filesystem::exists(dir_path) || !std::filesystem::is_directory(dir_path)) {
87 return std::set<std::string>{};
88 }
89
90 std::set<std::string> result;
91 for (const auto &entry : std::filesystem::directory_iterator{dir_path}) {
92 if (!entry.is_regular_file()) {
93 continue;
94 }
95 const auto &path = entry.path();
96 if (path.extension() != ".png") {
97 continue;
98 }
99 const std::string stem = path.stem().string();
100 const auto expected_snake = DynamicCasedName{stem}.to_snake_case();
101 if (expected_snake != stem) {
103 "{} file name '{}' must be snake_case (expected '{}.png')",
104 FormatParam{artifact_type, Style::bold},
105 FormatParam{path.filename().string(), Style::bold},
106 FormatParam{expected_snake, Style::bold})}};
107 }
108 result.insert(stem);
109 }
110 return result;
111}
112
113} // namespace
114
115namespace porytiles {
116
117// Porymap artifacts
120{
121 // Get primary/secondary status of tileset
123 is_secondary,
124 metadata_provider_->is_secondary(tileset_name),
126 "Failed to determine primary/secondary status.");
127
128 // Get base path from config
129 PT_UNWRAP_TILESET_CONFIG_PTR(config_, tileset_paths_secondary_bin, tileset_name, ArtifactKey);
130 PT_UNWRAP_TILESET_CONFIG_PTR(config_, tileset_paths_primary_bin, tileset_name, ArtifactKey);
131 auto base_path = is_secondary ? tileset_paths_secondary_bin : tileset_paths_primary_bin;
132 const std::string snake_tileset_dir = extract_tileset_cased_name(tileset_name).to_snake_case();
133 std::filesystem::path path =
134 std::filesystem::path{base_path.value()} / snake_tileset_dir / porytiles_bin / metatiles_bin;
135
136 return ArtifactKey{path.string()};
137}
138
141{
142 // Get primary/secondary status of tileset
144 is_secondary,
145 metadata_provider_->is_secondary(tileset_name),
147 "Failed to determine primary/secondary status.");
148
149 // Get base path from config
150 PT_UNWRAP_TILESET_CONFIG_PTR(config_, tileset_paths_secondary_bin, tileset_name, ArtifactKey);
151 PT_UNWRAP_TILESET_CONFIG_PTR(config_, tileset_paths_primary_bin, tileset_name, ArtifactKey);
152 auto base_path = is_secondary ? tileset_paths_secondary_bin : tileset_paths_primary_bin;
153 const std::string snake_tileset_dir = extract_tileset_cased_name(tileset_name).to_snake_case();
154 std::filesystem::path path =
155 std::filesystem::path{base_path.value()} / snake_tileset_dir / porytiles_bin / attributes_bin;
156
157 return ArtifactKey{path.string()};
158}
159
161{
162 // Get primary/secondary status of tileset
164 is_secondary,
165 metadata_provider_->is_secondary(tileset_name),
167 "Failed to determine primary/secondary status.");
168
169 // Get base path from config
170 PT_UNWRAP_TILESET_CONFIG_PTR(config_, tileset_paths_secondary_bin, tileset_name, ArtifactKey);
171 PT_UNWRAP_TILESET_CONFIG_PTR(config_, tileset_paths_primary_bin, tileset_name, ArtifactKey);
172 auto base_path = is_secondary ? tileset_paths_secondary_bin : tileset_paths_primary_bin;
173 const std::string snake_tileset_dir = extract_tileset_cased_name(tileset_name).to_snake_case();
174 std::filesystem::path path =
175 std::filesystem::path{base_path.value()} / snake_tileset_dir / porytiles_bin / tiles_png;
176
177 return ArtifactKey{path.string()};
178}
179
181ProjectTilesetArtifactKeyProvider::key_for_porymap_palette_n(const std::string &tileset_name, std::size_t index) const
182{
183 // Get primary/secondary status of tileset
185 is_secondary,
186 metadata_provider_->is_secondary(tileset_name),
188 "Failed to determine primary/secondary status.");
189
190 // Get base path from config
191 PT_UNWRAP_TILESET_CONFIG_PTR(config_, tileset_paths_secondary_bin, tileset_name, ArtifactKey);
192 PT_UNWRAP_TILESET_CONFIG_PTR(config_, tileset_paths_primary_bin, tileset_name, ArtifactKey);
193 auto base_path = is_secondary ? tileset_paths_secondary_bin : tileset_paths_primary_bin;
194 const std::string snake_tileset_dir = extract_tileset_cased_name(tileset_name).to_snake_case();
195 std::filesystem::path path = std::filesystem::path{base_path.value()} / snake_tileset_dir / porytiles_bin /
196 porymap_palettes / palette_filename(index);
197
198 return ArtifactKey{path.string()};
199}
200
202 const std::string &tileset_name, const std::string &anim_name, const std::string &frame_name) const
203{
204 // Get primary/secondary status of tileset
206 is_secondary,
207 metadata_provider_->is_secondary(tileset_name),
209 "Failed to determine primary/secondary status.");
210
211 // Get base path from config
212 PT_UNWRAP_TILESET_CONFIG_PTR(config_, tileset_paths_secondary_bin, tileset_name, ArtifactKey);
213 PT_UNWRAP_TILESET_CONFIG_PTR(config_, tileset_paths_primary_bin, tileset_name, ArtifactKey);
214 auto base_path = is_secondary ? tileset_paths_secondary_bin : tileset_paths_primary_bin;
215 const std::string snake_tileset_dir = extract_tileset_cased_name(tileset_name).to_snake_case();
216 std::filesystem::path path = std::filesystem::path{base_path.value()} / snake_tileset_dir / porytiles_bin /
217 anim_dir / anim_name / (frame_name + std::string{".png"});
218
219 return ArtifactKey{path.string()};
220}
221
224{
225 const std::string snake_tileset_dir = extract_tileset_cased_name(tileset_name).to_snake_case();
226 std::filesystem::path path = porytiles_generated_include / snake_tileset_dir / generated_anim_code_header;
227 return ArtifactKey{path.string()};
228}
229
230// Porytiles artifacts
232ProjectTilesetArtifactKeyProvider::key_for_bottom_png(const std::string &tileset_name) const
233{
234 // Get primary/secondary status of tileset
236 is_secondary,
237 metadata_provider_->is_secondary(tileset_name),
239 "Failed to determine primary/secondary status.");
240
241 // Get base path from config
242 PT_UNWRAP_TILESET_CONFIG_PTR(config_, tileset_paths_secondary_bin, tileset_name, ArtifactKey);
243 PT_UNWRAP_TILESET_CONFIG_PTR(config_, tileset_paths_primary_bin, tileset_name, ArtifactKey);
244 auto base_path = is_secondary ? tileset_paths_secondary_bin : tileset_paths_primary_bin;
245 const std::string snake_tileset_dir = extract_tileset_cased_name(tileset_name).to_snake_case();
246 std::filesystem::path path =
247 std::filesystem::path{base_path.value()} / snake_tileset_dir / porytiles_src / bottom_png;
248
249 return ArtifactKey{path.string()};
250}
251
253ProjectTilesetArtifactKeyProvider::key_for_middle_png(const std::string &tileset_name) const
254{
255 // Get primary/secondary status of tileset
257 is_secondary,
258 metadata_provider_->is_secondary(tileset_name),
260 "Failed to determine primary/secondary status.");
261
262 // Get base path from config
263 PT_UNWRAP_TILESET_CONFIG_PTR(config_, tileset_paths_secondary_bin, tileset_name, ArtifactKey);
264 PT_UNWRAP_TILESET_CONFIG_PTR(config_, tileset_paths_primary_bin, tileset_name, ArtifactKey);
265 auto base_path = is_secondary ? tileset_paths_secondary_bin : tileset_paths_primary_bin;
266 const std::string snake_tileset_dir = extract_tileset_cased_name(tileset_name).to_snake_case();
267 std::filesystem::path path =
268 std::filesystem::path{base_path.value()} / snake_tileset_dir / porytiles_src / middle_png;
269
270 return ArtifactKey{path.string()};
271}
272
274{
275 // Get primary/secondary status of tileset
277 is_secondary,
278 metadata_provider_->is_secondary(tileset_name),
280 "Failed to determine primary/secondary status.");
281
282 // Get base path from config
283 PT_UNWRAP_TILESET_CONFIG_PTR(config_, tileset_paths_secondary_bin, tileset_name, ArtifactKey);
284 PT_UNWRAP_TILESET_CONFIG_PTR(config_, tileset_paths_primary_bin, tileset_name, ArtifactKey);
285 auto base_path = is_secondary ? tileset_paths_secondary_bin : tileset_paths_primary_bin;
286 const std::string snake_tileset_dir = extract_tileset_cased_name(tileset_name).to_snake_case();
287 std::filesystem::path path = std::filesystem::path{base_path.value()} / snake_tileset_dir / porytiles_src / top_png;
288
289 return ArtifactKey{path.string()};
290}
291
294{
295 // Get primary/secondary status of tileset
297 is_secondary,
298 metadata_provider_->is_secondary(tileset_name),
300 "Failed to determine primary/secondary status.");
301
302 // Get base path from config
303 PT_UNWRAP_TILESET_CONFIG_PTR(config_, tileset_paths_secondary_bin, tileset_name, ArtifactKey);
304 PT_UNWRAP_TILESET_CONFIG_PTR(config_, tileset_paths_primary_bin, tileset_name, ArtifactKey);
305 auto base_path = is_secondary ? tileset_paths_secondary_bin : tileset_paths_primary_bin;
306 const std::string snake_tileset_dir = extract_tileset_cased_name(tileset_name).to_snake_case();
307 std::filesystem::path path =
308 std::filesystem::path{base_path.value()} / snake_tileset_dir / porytiles_src / attributes_csv;
309
310 return ArtifactKey{path.string()};
311}
312
314ProjectTilesetArtifactKeyProvider::key_for_porytiles_palette_n(const std::string &tileset_name, std::size_t index) const
315{
316 // Get primary/secondary status of tileset
318 is_secondary,
319 metadata_provider_->is_secondary(tileset_name),
321 "Failed to determine primary/secondary status.");
322
323 // Get base path from config
324 PT_UNWRAP_TILESET_CONFIG_PTR(config_, tileset_paths_secondary_bin, tileset_name, ArtifactKey);
325 PT_UNWRAP_TILESET_CONFIG_PTR(config_, tileset_paths_primary_bin, tileset_name, ArtifactKey);
326 auto base_path = is_secondary ? tileset_paths_secondary_bin : tileset_paths_primary_bin;
327 const std::string snake_tileset_dir = extract_tileset_cased_name(tileset_name).to_snake_case();
328 std::filesystem::path path = std::filesystem::path{base_path.value()} / snake_tileset_dir / porytiles_src /
329 porytiles_palettes / palette_filename(index);
330
331 return ArtifactKey{path.string()};
332}
333
335 const std::string &tileset_name, const std::string &anim_name, const std::string &frame_name) const
336{
337 // Get primary/secondary status of tileset
339 is_secondary,
340 metadata_provider_->is_secondary(tileset_name),
342 "Failed to determine primary/secondary status.");
343
344 // Get base path from config
345 PT_UNWRAP_TILESET_CONFIG_PTR(config_, tileset_paths_secondary_bin, tileset_name, ArtifactKey);
346 PT_UNWRAP_TILESET_CONFIG_PTR(config_, tileset_paths_primary_bin, tileset_name, ArtifactKey);
347 auto base_path = is_secondary ? tileset_paths_secondary_bin : tileset_paths_primary_bin;
348 const std::string snake_tileset_dir = extract_tileset_cased_name(tileset_name).to_snake_case();
349 std::filesystem::path path = std::filesystem::path{base_path.value()} / snake_tileset_dir / porytiles_src /
350 anim_dir / anim_name / (frame_name + std::string{".png"});
351
352 return ArtifactKey{path.string()};
353}
354
357{
358 // Get primary/secondary status of tileset
360 is_secondary,
361 metadata_provider_->is_secondary(tileset_name),
363 "Failed to determine primary/secondary status.");
364
365 // Get base path from config
366 PT_UNWRAP_TILESET_CONFIG_PTR(config_, tileset_paths_secondary_bin, tileset_name, ArtifactKey);
367 PT_UNWRAP_TILESET_CONFIG_PTR(config_, tileset_paths_primary_bin, tileset_name, ArtifactKey);
368 auto base_path = is_secondary ? tileset_paths_secondary_bin : tileset_paths_primary_bin;
369 const std::string snake_tileset_dir = extract_tileset_cased_name(tileset_name).to_snake_case();
370 std::filesystem::path path =
371 std::filesystem::path{base_path.value()} / snake_tileset_dir / porytiles_src / anim_dir / anim_json;
372
373 return ArtifactKey{path.string()};
374}
375
377{
378 // Keys are relative to project_root_, so prepend for filesystem operations
379 const std::filesystem::path artifact = project_root_ / key.key();
380 return std::filesystem::exists(artifact);
381}
382
385{
387 is_secondary,
388 metadata_provider_->is_secondary(tileset_name),
389 std::set<std::string>,
390 "Failed to determine primary/secondary status.");
391 PT_UNWRAP_TILESET_CONFIG_PTR(config_, tileset_paths_secondary_bin, tileset_name, std::set<std::string>);
392 PT_UNWRAP_TILESET_CONFIG_PTR(config_, tileset_paths_primary_bin, tileset_name, std::set<std::string>);
393 auto base_path = is_secondary ? tileset_paths_secondary_bin : tileset_paths_primary_bin;
394 const std::string snake_tileset_dir = extract_tileset_cased_name(tileset_name).to_snake_case();
395
396 const std::filesystem::path anim_path =
397 project_root_ / std::filesystem::path{base_path.value()} / snake_tileset_dir / porytiles_bin / anim_dir;
398
399 return scan_subdirectories(anim_path, "Porymap animation", format_);
400}
401
403 const std::string &tileset_name, const std::string &anim_name) const
404{
406 is_secondary,
407 metadata_provider_->is_secondary(tileset_name),
408 std::set<std::string>,
409 "Failed to determine primary/secondary status.");
410 PT_UNWRAP_TILESET_CONFIG_PTR(config_, tileset_paths_secondary_bin, tileset_name, std::set<std::string>);
411 PT_UNWRAP_TILESET_CONFIG_PTR(config_, tileset_paths_primary_bin, tileset_name, std::set<std::string>);
412 auto base_path = is_secondary ? tileset_paths_secondary_bin : tileset_paths_primary_bin;
413 const std::string snake_tileset_dir = extract_tileset_cased_name(tileset_name).to_snake_case();
414
415 const std::filesystem::path frame_dir = project_root_ / std::filesystem::path{base_path.value()} /
416 snake_tileset_dir / porytiles_bin / anim_dir / anim_name;
417
418 return scan_png_files(frame_dir, "Porymap animation frame", format_);
419}
420
423{
425 is_secondary,
426 metadata_provider_->is_secondary(tileset_name),
427 std::set<std::string>,
428 "Failed to determine primary/secondary status.");
429 PT_UNWRAP_TILESET_CONFIG_PTR(config_, tileset_paths_secondary_src, tileset_name, std::set<std::string>);
430 PT_UNWRAP_TILESET_CONFIG_PTR(config_, tileset_paths_primary_src, tileset_name, std::set<std::string>);
431 auto base_path = is_secondary ? tileset_paths_secondary_src : tileset_paths_primary_src;
432 const std::string snake_tileset_dir = extract_tileset_cased_name(tileset_name).to_snake_case();
433
434 const std::filesystem::path anim_path =
435 project_root_ / std::filesystem::path{base_path.value()} / snake_tileset_dir / porytiles_src / anim_dir;
436
437 return scan_subdirectories(anim_path, "Porytiles animation", format_);
438}
439
441 const std::string &tileset_name, const std::string &anim_name) const
442{
444 is_secondary,
445 metadata_provider_->is_secondary(tileset_name),
446 std::set<std::string>,
447 "Failed to determine primary/secondary status.");
448 PT_UNWRAP_TILESET_CONFIG_PTR(config_, tileset_paths_secondary_src, tileset_name, std::set<std::string>);
449 PT_UNWRAP_TILESET_CONFIG_PTR(config_, tileset_paths_primary_src, tileset_name, std::set<std::string>);
450 auto base_path = is_secondary ? tileset_paths_secondary_src : tileset_paths_primary_src;
451 const std::string snake_tileset_dir = extract_tileset_cased_name(tileset_name).to_snake_case();
452
453 const std::filesystem::path frame_dir = project_root_ / std::filesystem::path{base_path.value()} /
454 snake_tileset_dir / porytiles_src / anim_dir / anim_name;
455
456 return scan_png_files(frame_dir, "Porytiles animation frame", format_);
457}
458
459} // namespace porytiles
#define PT_TRY_ASSIGN_CHAIN_ERR(var, expr, return_type,...)
Unwraps a ChainableResult, chaining a new error message on failure.
A type-safe wrapper for artifact keys.
const std::string & key() const
A result type that maintains a chainable sequence of errors for debugging and error reporting.
A smart string wrapper that preserves word structure for lossless case format conversion.
std::string to_snake_case() const
Outputs all words flattened and joined with underscores.
A text parameter with associated styling for formatted output.
General-purpose error implementation with formatted message support.
Definition error.hpp:57
ChainableResult< ArtifactKey > key_for_porytiles_palette_n(const std::string &tileset_name, std::size_t index) const override
bool artifact_exists(const ArtifactKey &key) const override
Checks whether an artifact exists in the backing store for the given key.
ChainableResult< ArtifactKey > key_for_porymap_anim_params(const std::string &tileset_name) const override
Returns the key for Porymap animation parameters.
ChainableResult< std::set< std::string > > discover_porymap_anim_frames(const std::string &tileset_name, const std::string &anim_name) const override
Discovers the frame indices for a specific Porymap animation.
ChainableResult< ArtifactKey > key_for_middle_png(const std::string &tileset_name) const override
ChainableResult< ArtifactKey > key_for_metatile_attributes_bin(const std::string &tileset_name) const override
ChainableResult< ArtifactKey > key_for_porymap_palette_n(const std::string &tileset_name, std::size_t index) const override
ChainableResult< ArtifactKey > key_for_top_png(const std::string &tileset_name) const override
ChainableResult< std::set< std::string > > discover_porytiles_anims(const std::string &tileset_name) const override
Discovers the names of all Porytiles animations available for a tileset.
ChainableResult< ArtifactKey > key_for_porytiles_anim_params(const std::string &tileset_name) const override
Returns the key for the anim.json file (Porytiles animation configuration).
ChainableResult< std::set< std::string > > discover_porytiles_anim_frames(const std::string &tileset_name, const std::string &anim_name) const override
Discovers the frame indices for a specific Porytiles animation.
ChainableResult< ArtifactKey > key_for_bottom_png(const std::string &tileset_name) const override
ChainableResult< ArtifactKey > key_for_porytiles_anim_frame(const std::string &tileset_name, const std::string &anim_name, const std::string &frame_name) const override
ChainableResult< ArtifactKey > key_for_tiles_png(const std::string &tileset_name) const override
ChainableResult< ArtifactKey > key_for_attributes_csv(const std::string &tileset_name) const override
ChainableResult< std::set< std::string > > discover_porymap_anims(const std::string &tileset_name) const override
Discovers the names of all Porymap animations available for a tileset.
ChainableResult< ArtifactKey > key_for_metatiles_bin(const std::string &tileset_name) const override
ChainableResult< ArtifactKey > key_for_porymap_anim_frame(const std::string &tileset_name, const std::string &anim_name, const std::string &frame_name) const override
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.
virtual ChainableResult< bool > is_secondary(const std::string &tileset_name) const =0
Determines whether a tileset is a secondary tileset.
DynamicCasedName extract_tileset_cased_name(const std::string &tileset_name)
Extracts the tileset short name and wraps it in a DynamicCasedName.
std::string palette_filename(std::size_t palette_index)
Constructs a palette filename from a palette index.
Utility functions for string manipulation and formatting.
#define PT_UNWRAP_TILESET_CONFIG_PTR(ptr, config, tileset_name, return_type)
Unwraps a tileset-scoped config value via pointer access, returning early if the value is not availab...