26[[nodiscard]] std::filesystem::path fourBpp_to_png_path(
const std::string &path_4bpp)
28 std::filesystem::path p{path_4bpp};
29 p.replace_extension(
".png");
40 std::map<std::string, Animation<IndexPixel>> result;
45 if (!metadata_provider.exists(tileset_name)) {
49 auto metadata_result = metadata_provider.metadata_for(tileset_name);
50 if (!metadata_result.has_value()) {
54 auto metadata = std::move(metadata_result).value();
56 if (!metadata.has_animations()) {
61 const std::string callback_func = metadata.callback_func().value();
63 const std::string pascal_tileset = tileset_cased.to_pascal_case();
66 const auto tileset_anims_path = project_root_ /
"src" /
"tileset_anims.c";
68 if (!std::filesystem::exists(tileset_anims_path)) {
74 auto anim_params_result =
75 anim_parser.parse_from_callback(tileset_anims_path, callback_func, tileset_cased,
false);
76 if (!anim_params_result.has_value()) {
79 "Failed to parse animation parameters for '{}' from '{}'.",
84 std::map<DynamicCasedName, AnimParams> anim_params_map = std::move(anim_params_result).value();
86 if (anim_params_map.empty()) {
93 std::string detected_anim_prefix =
"gTilesetAnims_";
94 const std::string g_incbin_prefix =
"gTilesetAnims_" + pascal_tileset +
"_";
95 auto incbin_decls_result = c_parser.parse_incbin_arrays(g_incbin_prefix);
96 if (!incbin_decls_result.has_value()) {
98 FormattableError{
"Failed to parse INCBIN declarations."}, incbin_decls_result};
100 auto incbin_decls = std::move(incbin_decls_result).value();
108 auto incbins_cover_expected_anims = [&](
const std::string &prefix,
109 const std::vector<IncbinDeclaration> &decls) ->
bool {
110 for (
const auto ¶ms : anim_params_map | std::views::values) {
111 const std::string pascal_anim_name = params.cased_name().to_c_identifier();
112 const std::string first_frame_var = prefix + pascal_tileset +
"_" + pascal_anim_name +
"_Frame0";
114 for (
const auto &decl : decls) {
115 if (decl.variable_name() == first_frame_var) {
127 if (incbin_decls.empty() || !incbins_cover_expected_anims(
"gTilesetAnims_", incbin_decls)) {
128 const std::string s_incbin_prefix =
"sTilesetAnims_" + pascal_tileset +
"_";
129 auto s_incbin_decls_result = c_parser.parse_incbin_arrays(s_incbin_prefix);
130 if (s_incbin_decls_result.has_value()) {
131 auto s_incbin_decls = std::move(s_incbin_decls_result).value();
132 if (!s_incbin_decls.empty()) {
133 incbin_decls = std::move(s_incbin_decls);
134 detected_anim_prefix =
"sTilesetAnims_";
140 std::map<std::string, std::filesystem::path> frame_paths;
141 for (
const auto &decl : incbin_decls) {
142 if (decl.variable_name().find(
"_Frame") != std::string::npos && !decl.paths().empty()) {
143 frame_paths[decl.variable_name()] = project_root_ / fourBpp_to_png_path(decl.paths().front());
148 for (
const auto &[anim_name, params] : anim_params_map) {
156 for (
const auto &frame_name : params.frame_names()) {
158 const std::string frame_name_snake = frame_name.to_snake_case();
159 const std::string frame_var =
160 detected_anim_prefix + pascal_tileset +
"_" + pascal_anim_name +
"_Frame" + frame_name.to_pascal_case();
162 auto it = frame_paths.find(frame_var);
163 if (it == frame_paths.end()) {
165 "frame variable '{}' not found in INCBIN declarations for frame '{}'",
170 const auto &frame_png_path = it->second;
171 if (!std::filesystem::exists(frame_png_path)) {
173 "frame PNG '{}' not found for frame '{}'",
179 auto frame_load_result =
180 load_animation_frame_from_png<IndexPixel>(frame_png_path, frame_name_snake, png_loader);
181 if (!frame_load_result.has_value()) {
184 "failed to load frame '{}' for animation '{}'",
190 auto &load_result = frame_load_result.value();
191 anim.put_frame(frame_name_snake, std::move(load_result.frame));
194 auto animation_params = anim.params();
195 if (animation_params.width_tiles() == 0 && animation_params.height_tiles() == 0) {
196 animation_params.width_tiles(load_result.width_tiles);
197 animation_params.height_tiles(load_result.height_tiles);
198 anim.params(std::move(animation_params));
202 result[anim_name.to_snake_case()] = std::move(anim);
Shared helper for loading animation frames from PNG files.
Parses C code to extract tileset animation parameters using callback chain discovery.
const DynamicCasedName & cased_name() const
Returns the structured name for this animation, preserving case format information.
A complete tileset animation with name, configuration, and frame data.
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.
std::string to_c_identifier() const
Outputs PascalCase within each segment, with segments joined by underscores.
An image loader that reads PNG files to create an Image with an index pixel type.
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.