Porytiles
Loading...
Searching...
No Matches
project_porytiles_tileset_manager.cpp
Go to the documentation of this file.
2
3#include <filesystem>
4#include <fstream>
5#include <string_view>
6
7#include "nlohmann/json.hpp"
8
12
13namespace {
14
15using namespace porytiles;
16
17std::filesystem::path artifacts_file(const std::filesystem::path &project_root, const std::string &tileset_name)
18{
19 return project_root / "porytiles" / "tilesets" / tileset_name / "tileset-manifest.json";
20}
21
22constexpr std::string_view porytiles_managed_callback_prefix = "InitTilesetAnim_PorytilesManaged_";
23
24[[nodiscard]] bool is_porytiles_managed_callback(const std::optional<std::string> &callback)
25{
26 if (!callback.has_value()) {
27 return false;
28 }
29 return callback->starts_with(porytiles_managed_callback_prefix);
30}
31
32ChainableResult<void> append_incbin_declarations(
33 const IncbinDeclarationAppender *incbin_appender,
34 const std::string &tileset_name,
35 const std::string &bin_path_base,
36 std::size_t metatile_attr_size)
37{
38 // Append INCBIN declarations to graphics.h
40 incbin_appender->append_graphics_declarations(tileset_name, bin_path_base, pal::num_pals),
41 void,
42 "Failed to append graphics INCBIN declarations for '{}'.",
43 FormatParam(tileset_name, Style::bold));
44
45 // Append INCBIN declarations to metatiles.h
47 incbin_appender->append_metatiles_declarations(tileset_name, bin_path_base, metatile_attr_size),
48 void,
49 "Failed to append metatiles INCBIN declarations for '{}'.",
50 FormatParam(tileset_name, Style::bold));
51
52 return {};
53}
54
55} // namespace
56
57namespace porytiles {
58
60{
61 const auto input_path = artifacts_file(project_root_, tileset_name);
62
63 if (!std::filesystem::exists(input_path)) {
64 return FormattableError{"'{}': File not found.", FormatParam{input_path.string(), Style::bold}};
65 }
66
67 std::ifstream file{input_path};
68 nlohmann::json json_data;
69 file >> json_data;
70
71 const auto version = json_data["version"].get<std::uint32_t>();
72 const auto imported = json_data["imported"].get<bool>();
73
74 if (imported) {
75 return TilesetManifest{
76 version,
77 json_data[".tiles"].get<std::string>(),
78 json_data[".palettes"].get<std::string>(),
79 json_data[".metatiles"].get<std::string>(),
80 json_data[".metatileAttributes"].get<std::string>(),
81 json_data[".callback"].get<std::string>()};
82 }
83
85}
86
87void ProjectPorytilesTilesetManager::write(const std::string &tileset_name, const TilesetManifest &artifacts) const
88{
89 const auto manifest_file = artifacts_file(project_root_, tileset_name);
90 std::filesystem::create_directories(manifest_file.parent_path());
91 std::ofstream file{manifest_file};
92
93 nlohmann::json json_data;
94 json_data["version"] = artifacts.version();
95 json_data["imported"] = artifacts.imported();
96
97 if (artifacts.imported()) {
98 json_data[".tiles"] = artifacts.tiles();
99 json_data[".palettes"] = artifacts.palettes();
100 json_data[".metatiles"] = artifacts.metatiles();
101 json_data[".metatileAttributes"] = artifacts.metatile_attributes();
102 json_data[".callback"] = artifacts.callback();
103 }
104
105 file << json_data.dump(2);
106 file << std::endl;
107}
108
109bool ProjectPorytilesTilesetManager::is_porytiles_managed(const std::string &tileset_name) const
110{
111 return std::filesystem::exists(artifacts_file(project_root_, tileset_name));
112}
113
115{
116 // Step 1: Read original metadata from headers.h
118 metadata,
119 metadata_provider_->metadata_for(tileset_name),
120 void,
121 "Failed to read metadata for tileset '{}'.",
122 FormatParam(tileset_name, Style::bold));
123
124 // Step 2: Build TilesetManifest from metadata (stores original values for restoration)
125 constexpr std::uint32_t version = 1;
126 const std::optional<std::string> original_callback_value = metadata.callback_func();
127
128 TilesetManifest artifacts{
129 version,
130 metadata.tiles_var(),
131 metadata.palettes_var(),
132 metadata.metatiles_var(),
133 metadata.metatile_attributes_var(),
134 original_callback_value.value_or("NULL")};
135
136 // Step 3: Write tileset-manifest.json
137 write(tileset_name, artifacts);
138
139 // Step 4: Get config values for path computation
140 const bool is_secondary = metadata.is_secondary();
141
143 bin_path_base,
144 is_secondary ? infra_config_->tileset_paths_secondary_bin(ConfigScopeType::tileset, tileset_name)
145 : infra_config_->tileset_paths_primary_bin(ConfigScopeType::tileset, tileset_name),
146 void,
147 "Failed to get tileset bin path config for '{}'.",
148 FormatParam(tileset_name, Style::bold));
149
150 // Step 5: Resolve metatile attribute size from config
152 metatile_attr_size,
153 infra_config_->metatile_attr_size(ConfigScopeType::tileset, tileset_name),
154 void,
155 "Failed to get metatile attribute size config for '{}'.",
156 FormatParam(tileset_name, Style::bold));
157
158 // Step 6: Append INCBIN declarations
159 auto incbin_result = append_incbin_declarations(incbin_appender_, tileset_name, bin_path_base, metatile_attr_size);
160 if (!incbin_result.has_value()) {
161 return incbin_result;
162 }
163
164 // Step 7: Update headers.h to use Porytiles-managed asset variables
165 // Note: Callback update is handled separately by wire_anim_code() in the use-case layer
166 return metadata_writer_->update_to_porytiles_managed(tileset_name);
167}
168
170ProjectPorytilesTilesetManager::persist_managed_new(const std::string &tileset_name, bool is_secondary) const
171{
172 // Step 1: Create tileset struct in headers.h (it doesn't exist yet)
174 metadata_writer_->create_tileset_struct(tileset_name, is_secondary),
175 void,
176 "Failed to create tileset struct in headers.h for '{}'.",
177 FormatParam(tileset_name, Style::bold));
178
179 // Invalidate the metadata cache since we just added a new struct to headers.h
180 metadata_provider_->invalidate_metadata_cache();
181
182 // Step 2: Write TilesetManifest with imported=false
183 constexpr std::uint32_t version = 1;
184 write(tileset_name, TilesetManifest::for_created_tileset(version));
185
186 // Step 3: Get config values for path computation
188 bin_path_base,
189 is_secondary ? infra_config_->tileset_paths_secondary_bin(ConfigScopeType::tileset, tileset_name)
190 : infra_config_->tileset_paths_primary_bin(ConfigScopeType::tileset, tileset_name),
191 void,
192 "Failed to get tileset bin path config for '{}'.",
193 FormatParam(tileset_name, Style::bold));
194
195 // Step 4: Resolve metatile attribute size from config
197 metatile_attr_size,
198 infra_config_->metatile_attr_size(ConfigScopeType::tileset, tileset_name),
199 void,
200 "Failed to get metatile attribute size config for '{}'.",
201 FormatParam(tileset_name, Style::bold));
202
203 // Step 5: Append INCBIN declarations
204 auto incbin_result = append_incbin_declarations(incbin_appender_, tileset_name, bin_path_base, metatile_attr_size);
205 if (!incbin_result.has_value()) {
206 return incbin_result;
207 }
208
209 return {};
210}
211
213ProjectPorytilesTilesetManager::wire_anim_code(const std::string &tileset_name, bool is_secondary) const
214{
215 // Check config - if wire_anim_code is disabled, delegate to remove
217 should_wire,
219 void,
220 "Failed to get wire_anim_code config for '{}'.",
221 FormatParam(tileset_name, Style::bold));
222
223 if (!should_wire) {
224 std::vector<std::string> remark_text;
225 remark_text.emplace_back("Config 'tileset.animations.wire_anim_code' is false, removing any existing wiring.");
226 remark_text.emplace_back("");
227 remark_text.append_range(should_wire.prettify(diag_->formatter()));
228 diag_->remark("wire-tileset-animation", remark_text);
229 return remove_wired_anim_code(tileset_name, is_secondary);
230 }
231
232 // Step 1: Wire include in tileset_anims.c AND declaration in tileset_anims.h
234 tileset_anims_modifier_->wire_include_for_tileset(tileset_name, is_secondary),
235 void,
236 "Failed to wire tileset_anims include/declaration for '{}'.",
237 FormatParam(tileset_name, Style::bold));
238
239 // Step 2: Generate callback function name
240 const std::string shorthand = extract_tileset_shorthand(tileset_name);
241 const std::string callback_name = "InitTilesetAnim_PorytilesManaged_" + shorthand;
242
243 // Step 3: Update callback field in headers.h
245 metadata_writer_->update_callback(tileset_name, callback_name),
246 void,
247 "Failed to update callback in headers.h for '{}'.",
248 FormatParam(tileset_name, Style::bold));
249
250 return {};
251}
252
254ProjectPorytilesTilesetManager::remove_wired_anim_code(const std::string &tileset_name, bool is_secondary) const
255{
256 // Step 1: Remove include from tileset_anims.c and declaration from tileset_anims.h
258 tileset_anims_modifier_->remove_include_for_tileset(tileset_name, is_secondary),
259 void,
260 "Failed to remove animation includes for '{}'.",
261 FormatParam(tileset_name, Style::bold));
262
263 // Step 2: Only update callback to NULL if it's a Porytiles-managed callback
264 // This preserves user-managed callbacks when wire_anim_code is false
266 metadata,
267 metadata_provider_->metadata_for(tileset_name),
268 void,
269 "Failed to read metadata for '{}'.",
270 FormatParam(tileset_name, Style::bold));
271
272 const auto &current_callback = metadata.callback_func();
273 if (is_porytiles_managed_callback(current_callback)) {
275 metadata_writer_->update_callback(tileset_name, "NULL"),
276 void,
277 "Failed to update callback to NULL for '{}'.",
278 FormatParam(tileset_name, Style::bold));
279 }
280
281 return {};
282}
283
284} // 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_CALL_CHAIN_ERR(expr, return_type,...)
Unwraps a void ChainableResult, chaining a new error message on failure.
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
Appends INCBIN declarations for Porytiles-managed tileset assets.
ChainableResult< void > append_graphics_declarations(const std::string &tileset_name, const std::string &bin_path_base, std::size_t num_palettes) const
Appends INCBIN declarations for a Porytiles-managed tileset to graphics.h.
ChainableResult< void > append_metatiles_declarations(const std::string &tileset_name, const std::string &bin_path_base, std::size_t metatile_attr_size) const
Appends INCBIN declarations for a Porytiles-managed tileset to metatiles.h.
ChainableResult< ConfigValue< std::string > > tileset_paths_secondary_bin(ConfigScopeType type, const std::string &scope) const
ChainableResult< ConfigValue< std::size_t > > metatile_attr_size(ConfigScopeType type, const std::string &scope) const
ChainableResult< ConfigValue< std::string > > tileset_paths_primary_bin(ConfigScopeType type, const std::string &scope) const
ChainableResult< ConfigValue< bool > > tileset_animations_wire_anim_code(ConfigScopeType type, const std::string &scope) const
void write(const std::string &tileset_name, const TilesetManifest &artifacts) const
Writes an TilesetManifest object to the porytiles utility directory.
ChainableResult< void > persist_managed_new(const std::string &tileset_name, bool is_secondary=false) const override
Persists managed state for a new tileset.
ChainableResult< TilesetManifest > read(const std::string &tileset_name) const
Reads an TilesetManifest object from the porytiles utility directory.
ChainableResult< void > persist_managed_existing(const std::string &tileset_name) const override
Persists managed state for an existing tileset.
ChainableResult< void > remove_wired_anim_code(const std::string &tileset_name, bool is_secondary) const override
Removes wired animation code for a tileset from the project.
ChainableResult< void > wire_anim_code(const std::string &tileset_name, bool is_secondary) const override
Wires animation code for a tileset that already has its manifest persisted.
bool is_porytiles_managed(const std::string &tileset_name) const override
Checks whether a tileset has an tileset-manifest.json file.
ChainableResult< void > wire_include_for_tileset(const std::string &tileset_name, bool is_secondary) const
Adds an #include directive for the tileset's generated animation code.
ChainableResult< void > remove_include_for_tileset(const std::string &tileset_name, bool is_secondary) const
Removes the #include directive for the tileset's generated animation code.
void invalidate_metadata_cache() const
Invalidates the lazy-loaded metadata and artifact paths caches, forcing a re-parse on next access.
ChainableResult< ProjectTilesetMetadata > metadata_for(const std::string &tileset_name) const
Retrieves metadata for a specific tileset from the struct cache.
ChainableResult< void > create_tileset_struct(const std::string &tileset_name, bool is_secondary) const
Creates a new tileset struct and appends it to headers.h.
ChainableResult< void > update_to_porytiles_managed(const std::string &tileset_name) const
Updates asset fields to Porytiles-managed values.
ChainableResult< void > update_callback(const std::string &tileset_name, const std::string &new_callback_value) const
Updates the .callback field for a specific tileset.
static const Style bold
Bold text formatting.
Stores manifest metadata for Porytiles-managed tilesets.
static TilesetManifest for_created_tileset(std::uint32_t version)
Creates a TilesetManifest for a tileset created from scratch by Porytiles.
bool imported() const
Returns whether this tileset was imported from vanilla pokeemerald.
const std::string & callback() const
Returns the original .callback field value.
const std::string & tiles() const
Returns the original .tiles field value.
const std::string & metatile_attributes() const
Returns the original .metatileAttributes field value.
std::uint32_t version() const
Returns the schema version for format migration support.
const std::string & palettes() const
Returns the original .palettes field value.
const std::string & metatiles() const
Returns the original .metatiles field value.
virtual void remark(const std::string &tag, const std::vector< std::string > &lines) const =0
Display a tagged remark message.
const TextFormatter & formatter() const
constexpr std::size_t num_pals
Definition palette.hpp:21
std::string extract_tileset_shorthand(const std::string &tileset_name)
Extracts the Pascal-case tileset short name from the full name.
@ tileset
Configuration scoped to a specific tileset.
Utility functions for string manipulation and formatting.