16[[nodiscard]] std::string generate_incbin_statements(
17 const std::string &tileset_name,
18 const std::filesystem::path &tileset_path_from_project_root,
22 std::ostringstream out;
27 for (
const auto &frame_name : params.frame_names()) {
28 const std::string frame_file = (tileset_path_from_project_root /
"porytiles_bin" /
"anim" /
29 anim_name.
to_snake_case() / std::format(
"{}.4bpp", frame_name.to_snake_case()))
32 const auto statement = std::format(
33 "const u16 gTilesetAnims_{}{}_{}_Frame{}[] = INCBIN_U16(\"{}\");\n",
37 frame_name.to_pascal_case(),
46[[nodiscard]] std::string
49 std::ostringstream out;
52 const std::string array_name =
55 out << std::format(
"const u16 *const {}[] = {{\n", array_name);
58 for (std::size_t i = 0; i < params.
frame_order().size(); ++i) {
60 " gTilesetAnims_{}{}_{}_Frame{}",
76[[nodiscard]] std::string generate_queue_function(
79 std::ostringstream out;
82 const std::string array_name =
84 const std::string func_name =
87 const std::string tile_offset_expr =
88 is_primary ? std::format(
"TILE_OFFSET_4BPP({})", params.
tile_offset())
89 : std::format(
"TILE_OFFSET_4BPP(NUM_TILES_IN_PRIMARY + {})", params.tile_offset());
91 out << std::format(
"static void {}(u16 timer)\n", func_name);
93 out << std::format(
" u16 i = timer % ARRAY_COUNT({});\n", array_name);
95 " AppendTilesetAnimToBuffer({}[i], (u16 *)(BG_VRAM + {}), {} * TILE_SIZE_4BPP);\n",
104[[nodiscard]] std::string
105generate_driver_function(
const std::string &tileset_name,
const std::map<DynamicCasedName, AnimParams> &animations)
107 std::ostringstream out;
111 out << std::format(
"static void {}(u16 timer)\n", func_name);
115 std::map<std::size_t, std::vector<std::pair<DynamicCasedName, AnimParams>>> by_frame_factor;
116 for (
const auto &[anim_name, params] : animations) {
117 by_frame_factor[params.
frame_factor()].emplace_back(anim_name, params);
120 for (
const auto &[frame_factor, anims] : by_frame_factor) {
122 std::map<std::size_t, std::vector<std::pair<DynamicCasedName, AnimParams>>> by_offset;
123 for (
const auto &[anim_name, params] : anims) {
124 by_offset[params.
frame_offset()].emplace_back(anim_name, params);
127 for (
const auto &[frame_offset, offset_anims] : by_offset) {
128 out << std::format(
" if (timer % {} == {})\n", frame_factor, frame_offset);
131 for (
const auto &anim_name : offset_anims | std::views::keys) {
133 " QueueAnimTiles_{}{}_{}(timer / {});\n",
149[[nodiscard]] std::string generate_init_function(
150 const std::string &tileset_name,
const std::map<DynamicCasedName, AnimParams> &animations,
bool is_primary)
152 std::ostringstream out;
154 const std::string init_func_name =
160 for (
const auto ¶ms : animations | std::views::values) {
161 max_counter_max = std::max(max_counter_max, params.
counter_max());
164 const std::string counter_var = is_primary ?
"sPrimaryTilesetAnimCounter" :
"sSecondaryTilesetAnimCounter";
165 const std::string counter_max_var =
166 is_primary ?
"sPrimaryTilesetAnimCounterMax" :
"sSecondaryTilesetAnimCounterMax";
167 const std::string callback_var = is_primary ?
"sPrimaryTilesetAnimCallback" :
"sSecondaryTilesetAnimCallback";
169 out << std::format(
"void {}(void)\n", init_func_name);
171 out << std::format(
" {} = 0;\n", counter_var);
172 out << std::format(
" {} = {};\n", counter_max_var, max_counter_max);
173 out << std::format(
" {} = {};\n", callback_var, driver_func_name);
184 const std::string &tileset_name,
185 const std::filesystem::path &tileset_path_from_project_root,
186 const std::map<DynamicCasedName, AnimParams> &animations,
187 bool is_primary)
const
189 if (animations.empty()) {
193 std::ostringstream out;
195 const std::string tileset_name_no_prefix = tileset_name.substr(std::size(
"gTileset_") - 1);
199 const std::string guard_name = std::format(
"GUARD_GENERATED_ANIM_CODE_{}_H", pascal_tileset_name);
200 out << std::format(
"#ifndef {}\n", guard_name);
201 out << std::format(
"#define {}\n\n", guard_name);
204 out <<
"// Ensure INCBIN_U16 is available\n";
205 out <<
"#ifndef INCBIN_U16\n";
206 out <<
"#include \"gba/defines.h\"\n";
210 out <<
" * This file is auto-generated by Porytiles.\n";
211 out <<
" * DO NOT EDIT THIS FILE MANUALLY (unless you know what you are doing).\n";
215 out <<
"// ============================================\n";
216 out <<
"// Frame Data (INCBIN statements)\n";
217 out <<
"// ============================================\n\n";
219 for (
const auto &[anim_name, params] : animations) {
220 out << generate_incbin_statements(pascal_tileset_name, tileset_path_from_project_root, anim_name, params);
225 out <<
"// ============================================\n";
226 out <<
"// Frame Pointer Arrays\n";
227 out <<
"// ============================================\n\n";
229 for (
const auto &[anim_name, params] : animations) {
230 out << generate_frame_array(pascal_tileset_name, anim_name, params);
235 out <<
"// ============================================\n";
236 out <<
"// Queue Functions\n";
237 out <<
"// ============================================\n\n";
239 for (
const auto &[anim_name, params] : animations) {
240 out << generate_queue_function(pascal_tileset_name, anim_name, params, is_primary);
245 out <<
"// ============================================\n";
246 out <<
"// Driver Function\n";
247 out <<
"// ============================================\n\n";
249 out << generate_driver_function(pascal_tileset_name, animations);
253 out <<
"// ============================================\n";
254 out <<
"// Init Function\n";
255 out <<
"// ============================================\n\n";
257 out << generate_init_function(pascal_tileset_name, animations, is_primary);
260 out << std::format(
"\n#endif // {}\n", guard_name);
ChainableResult< std::string > generate(const std::string &tileset_name, const std::filesystem::path &tileset_path_from_project_root, const std::map< DynamicCasedName, AnimParams > &animations, bool is_primary) const
Generates the complete generated_anim_code.h content.
Configuration parameters for a single tileset animation.
const std::vector< DynamicCasedName > & frame_order() const
Returns the playback sequence.
std::size_t frame_offset() const
Returns the frame offset (remainder value for timer modulo check).
std::size_t tile_offset() const
Returns the VRAM tile offset for this animation.
std::size_t frame_factor() const
Returns the frame factor (modulus divisor for timer).
std::size_t counter_max() const
Returns the animation counter maximum value.
std::size_t tile_count() const
Returns the number of tiles per animation frame.
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.
std::string to_pascal_case() const
Outputs all words flattened and joined in PascalCase (each word capitalized, no separators).
constexpr std::size_t default_counter_max
constexpr std::string porytiles_managed_prefix