Porytiles
Loading...
Searching...
No Matches
project_vanilla_anim_importer.cpp
Go to the documentation of this file.
2
3#include <vector>
4
12
13namespace {
14
15using namespace porytiles;
16
24[[nodiscard]] std::filesystem::path fourBpp_to_png_path(const std::string &path_4bpp)
25{
26 std::filesystem::path p{path_4bpp};
27 p.replace_extension(".png");
28 return p;
29}
30
31} // namespace
32
33namespace porytiles {
34
36ProjectVanillaAnimImporter::import_animations(const std::string &tileset_name) const
37{
38 std::map<std::string, Animation<IndexPixel>> result;
39
40 // Step 1: Get tileset metadata
41 ProjectTilesetMetadataProvider metadata_provider{project_root_, format_, diag_};
42
43 if (!metadata_provider.exists(tileset_name)) {
44 return FormattableError{"Tileset '{}' does not exist.", FormatParam{tileset_name, Style::bold}};
45 }
46
47 auto metadata_result = metadata_provider.metadata_for(tileset_name);
48 if (!metadata_result.has_value()) {
50 FormattableError{"Failed to get tileset metadata."}, metadata_result};
51 }
52 auto metadata = std::move(metadata_result).value();
53
54 if (!metadata.has_animations()) {
55 // No animations - return empty map
56 return result;
57 }
58
59 const std::string callback_func = metadata.callback_func().value();
60 const auto tileset_cased = extract_tileset_cased_name(tileset_name);
61
62 // Step 2: Parse animation parameters from tileset_anims.c
63 const auto tileset_anims_path = project_root_ / "src" / "tileset_anims.c";
64
65 if (!std::filesystem::exists(tileset_anims_path)) {
66 return FormattableError{
67 "'{}': Animation parameters file not found.", FormatParam{tileset_anims_path.string(), Style::bold}};
68 }
69
70 AnimCodeParser anim_parser{format_, diag_};
71 auto anim_params_result =
72 anim_parser.parse_from_callback(tileset_anims_path, callback_func, tileset_cased, /*porytiles_managed=*/false);
73 if (!anim_params_result.has_value()) {
76 "Failed to parse animation parameters for '{}' from '{}'.",
77 FormatParam{tileset_name, Style::bold},
78 FormatParam{tileset_anims_path, Style::bold}},
79 anim_params_result};
80 }
81 std::map<DynamicCasedName, AnimParams> anim_params_map = std::move(anim_params_result).value();
82
83 if (anim_params_map.empty()) {
84 // No animations found in callback chain
85 return result;
86 }
87
88 // Step 3: Parse INCBIN declarations for frame paths. Frame variables are looked up by the exact identifier
89 // recorded in each AnimParams (frame_array_identifier + "_Frame" + frame name), so parse every INCBIN
90 // declaration rather than filtering by a reconstructed tileset prefix. Exact lookup also avoids collisions like
91 // pokefirered's BattleFrontier, which reuses gTilesetAnims_General_* names for its own shared animations.
92 CParserFacade c_parser{tileset_anims_path, format_};
93 auto incbin_decls_result = c_parser.parse_incbin_arrays();
94 if (!incbin_decls_result.has_value()) {
96 FormattableError{"Failed to parse INCBIN declarations."}, incbin_decls_result};
97 }
98 auto incbin_decls = std::move(incbin_decls_result).value();
99
100 // Build map: frame variable name -> .png file path
101 std::map<std::string, std::filesystem::path> frame_paths;
102 for (const auto &decl : incbin_decls) {
103 if (decl.variable_name().find("_Frame") != std::string::npos && !decl.paths().empty()) {
104 frame_paths[decl.variable_name()] = project_root_ / fourBpp_to_png_path(decl.paths().front());
105 }
106 }
107
108 // Step 4: For each animation, construct Animation<IndexPixel> with frame data
109 for (const auto &[anim_name, params] : anim_params_map) {
110 Animation<IndexPixel> anim{anim_name.to_snake_case()};
111 anim.params(params);
112
113 for (const auto &frame_name : params.frame_names()) {
114 PngIndexedImageLoader png_loader;
115 const std::string frame_name_snake = frame_name.to_snake_case();
116 const std::string frame_var = params.frame_array_identifier() + "_Frame" + frame_name.to_pascal_case();
117
118 auto it = frame_paths.find(frame_var);
119 if (it == frame_paths.end()) {
120 return FormattableError{
121 "frame variable '{}' not found in INCBIN declarations for frame '{}'",
122 FormatParam{frame_var, Style::bold},
123 FormatParam{frame_name_snake, Style::bold}};
124 }
125
126 const auto &frame_png_path = it->second;
127 if (!std::filesystem::exists(frame_png_path)) {
128 return FormattableError{
129 "frame PNG '{}' not found for frame '{}'",
130 FormatParam{frame_png_path.string(), Style::bold},
131 FormatParam{frame_name_snake, Style::bold}};
132 }
133
134 // Use shared helper to load frame PNG and extract tiles
135 auto frame_load_result =
136 load_animation_frame_from_png<IndexPixel>(frame_png_path, frame_name_snake, png_loader);
137 if (!frame_load_result.has_value()) {
140 "failed to load frame '{}' for animation '{}'",
141 FormatParam{frame_name_snake, Style::bold},
142 FormatParam{anim_name, Style::bold}},
143 frame_load_result};
144 }
145
146 auto &load_result = frame_load_result.value();
147 anim.put_frame(frame_name_snake, std::move(load_result.frame));
148
149 // Update dimensions in params if not already set (first frame determines dimensions)
150 auto animation_params = anim.params();
151 if (animation_params.width_tiles() == 0 && animation_params.height_tiles() == 0) {
152 animation_params.width_tiles(load_result.width_tiles);
153 animation_params.height_tiles(load_result.height_tiles);
154 anim.params(std::move(animation_params));
155 }
156 }
157
158 result[anim_name.to_snake_case()] = std::move(anim);
159 }
160
161 return result;
162}
163
164} // namespace porytiles
Shared helper for loading animation frames from PNG files.
Parses C code to extract tileset animation parameters using callback chain discovery.
A complete tileset animation with name, configuration, and frame data.
Definition animation.hpp:89
const AnimParams & params() const
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:57
An image loader that reads PNG files to create an Image with an index pixel type.
Provides a pokeemerald project filesystem-based implementation for TilesetMetadataProvider.
ChainableResult< std::map< std::string, Animation< IndexPixel > > > import_animations(const std::string &tileset_name) const
Import animations from a vanilla tileset.
static const Style bold
Bold text formatting.
DynamicCasedName extract_tileset_cased_name(const std::string &tileset_name)
Extracts the tileset short name and wraps it in a DynamicCasedName.
Utility functions for string manipulation and formatting.