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
22[[nodiscard]] bool is_porytiles_managed_callback(const std::optional<std::string> &callback)
23{
24 if (!callback.has_value()) {
25 return false;
26 }
27 return callback->starts_with(anim::porytiles_managed_callback_prefix);
28}
29
30ChainableResult<void> append_incbin_declarations(
31 const IncbinDeclarationAppender *incbin_appender,
32 const std::string &tileset_name,
33 const std::string &bin_path_base,
34 std::size_t attribute_bytes)
35{
36 // Append INCBIN declarations to graphics.h
38 incbin_appender->append_graphics_declarations(tileset_name, bin_path_base, palette::num_palettes),
39 void,
40 "Failed to append graphics INCBIN declarations for '{}'.",
41 FormatParam(tileset_name, Style::bold));
42
43 // Append INCBIN declarations to metatiles.h
45 incbin_appender->append_metatiles_declarations(tileset_name, bin_path_base, attribute_bytes),
46 void,
47 "Failed to append metatiles INCBIN declarations for '{}'.",
48 FormatParam(tileset_name, Style::bold));
49
50 return {};
51}
52
53} // namespace
54
55namespace porytiles {
56
58{
59 const auto input_path = artifacts_file(project_root_, tileset_name);
60
61 if (!std::filesystem::exists(input_path)) {
62 return FormattableError{"'{}': File not found.", FormatParam{input_path.string(), Style::bold}};
63 }
64
65 std::ifstream file{input_path};
66 nlohmann::json json_data;
67 file >> json_data;
68
69 const auto version = json_data["version"].get<std::uint32_t>();
70 const auto imported = json_data["imported"].get<bool>();
71
72 if (imported) {
73 return TilesetManifest{
74 version,
75 json_data[".tiles"].get<std::string>(),
76 json_data[".palettes"].get<std::string>(),
77 json_data[".metatiles"].get<std::string>(),
78 json_data[".metatileAttributes"].get<std::string>(),
79 json_data[".callback"].get<std::string>()};
80 }
81
83}
84
85void ProjectPorytilesTilesetManager::write(const std::string &tileset_name, const TilesetManifest &artifacts) const
86{
87 const auto manifest_file = artifacts_file(project_root_, tileset_name);
88 std::filesystem::create_directories(manifest_file.parent_path());
89 std::ofstream file{manifest_file};
90
91 nlohmann::json json_data;
92 json_data["version"] = artifacts.version();
93 json_data["imported"] = artifacts.imported();
94
95 if (artifacts.imported()) {
96 json_data[".tiles"] = artifacts.tiles();
97 json_data[".palettes"] = artifacts.palettes();
98 json_data[".metatiles"] = artifacts.metatiles();
99 json_data[".metatileAttributes"] = artifacts.metatile_attributes();
100 json_data[".callback"] = artifacts.callback();
101 }
102
103 file << json_data.dump(2);
104 file << std::endl;
105}
106
107bool ProjectPorytilesTilesetManager::is_porytiles_managed(const std::string &tileset_name) const
108{
109 return std::filesystem::exists(artifacts_file(project_root_, tileset_name));
110}
111
113{
114 // Step 1: Read original metadata from headers.h
116 metadata,
117 metadata_provider_->metadata_for(tileset_name),
118 void,
119 "Failed to read metadata for tileset '{}'.",
120 FormatParam(tileset_name, Style::bold));
121
122 // Step 2: Build TilesetManifest from metadata (stores original values for restoration)
123 constexpr std::uint32_t version = 1;
124 const std::optional<std::string> original_callback_value = metadata.callback_func();
125
126 TilesetManifest artifacts{
127 version,
128 metadata.tiles_var(),
129 metadata.palettes_var(),
130 metadata.metatiles_var(),
131 metadata.metatile_attributes_var(),
132 original_callback_value.value_or("NULL")};
133
134 // Step 3: Write tileset-manifest.json
135 write(tileset_name, artifacts);
136
137 // Step 4: Get config values for path computation
138 const bool is_secondary = metadata.is_secondary();
139
141 bin_path_base,
142 is_secondary ? infra_config_->tileset_paths_secondary_bin(ConfigScopeType::tileset, tileset_name)
143 : infra_config_->tileset_paths_primary_bin(ConfigScopeType::tileset, tileset_name),
144 void,
145 "Failed to get tileset bin path config for '{}'.",
146 FormatParam(tileset_name, Style::bold));
147
148 // Step 5: Append INCBIN declarations using the project's declared attribute width
149 auto incbin_result =
150 append_incbin_declarations(incbin_appender_, tileset_name, bin_path_base, declaration_attribute_bytes_);
151 if (!incbin_result.has_value()) {
152 return incbin_result;
153 }
154
155 // Step 6: Update headers.h to use Porytiles-managed asset variables
156 // Note: Callback update is handled separately by wire_anim_code() in the use-case layer
157 return metadata_writer_->update_to_porytiles_managed(tileset_name);
158}
159
161ProjectPorytilesTilesetManager::persist_managed_new(const std::string &tileset_name, bool is_secondary) const
162{
163 // Step 1: Create tileset struct in headers.h (it doesn't exist yet)
165 metadata_writer_->create_tileset_struct(tileset_name, is_secondary),
166 void,
167 "Failed to create tileset struct in headers.h for '{}'.",
168 FormatParam(tileset_name, Style::bold));
169
170 // Invalidate the metadata cache since we just added a new struct to headers.h
171 metadata_provider_->invalidate_metadata_cache();
172
173 // Step 2: Write TilesetManifest with imported=false
174 constexpr std::uint32_t version = 1;
175 write(tileset_name, TilesetManifest::for_created_tileset(version));
176
177 // Step 3: Get config values for path computation
179 bin_path_base,
180 is_secondary ? infra_config_->tileset_paths_secondary_bin(ConfigScopeType::tileset, tileset_name)
181 : infra_config_->tileset_paths_primary_bin(ConfigScopeType::tileset, tileset_name),
182 void,
183 "Failed to get tileset bin path config for '{}'.",
184 FormatParam(tileset_name, Style::bold));
185
186 // Step 4: Append INCBIN declarations using the project's declared attribute width
187 auto incbin_result =
188 append_incbin_declarations(incbin_appender_, tileset_name, bin_path_base, declaration_attribute_bytes_);
189 if (!incbin_result.has_value()) {
190 return incbin_result;
191 }
192
193 return {};
194}
195
197ProjectPorytilesTilesetManager::wire_anim_code(const std::string &tileset_name, bool is_secondary) const
198{
199 // Check config - if wire_anim_code is disabled, delegate to remove
201 should_wire,
203 void,
204 "Failed to get wire_anim_code config for '{}'.",
205 FormatParam(tileset_name, Style::bold));
206
207 if (!should_wire) {
208 std::vector<std::string> remark_text;
209 remark_text.emplace_back("Config 'tileset.animations.wire_anim_code' is false, removing any existing wiring.");
210 remark_text.emplace_back("");
211 remark_text.append_range(should_wire.prettify(diag_->formatter()));
212 diag_->remark("wire-tileset-animation", remark_text);
213 return remove_wired_anim_code(tileset_name, is_secondary);
214 }
215
216 // Step 1: Wire include in tileset_anims.c AND declaration in tileset_anims.h
218 tileset_anims_modifier_->wire_include_for_tileset(tileset_name, is_secondary),
219 void,
220 "Failed to wire tileset_anims include/declaration for '{}'.",
221 FormatParam(tileset_name, Style::bold));
222
223 // Step 2: Generate callback function name (PascalCase, to match the generated function + declaration)
224 const std::string callback_name = anim::managed_callback_name(tileset_name);
225
226 // Step 3: Update callback field in headers.h
228 metadata_writer_->update_callback(tileset_name, callback_name),
229 void,
230 "Failed to update callback in headers.h for '{}'.",
231 FormatParam(tileset_name, Style::bold));
232
233 return {};
234}
235
237ProjectPorytilesTilesetManager::remove_wired_anim_code(const std::string &tileset_name, bool is_secondary) const
238{
239 // Step 1: Remove include from tileset_anims.c and declaration from tileset_anims.h
241 tileset_anims_modifier_->remove_include_for_tileset(tileset_name, is_secondary),
242 void,
243 "Failed to remove animation includes for '{}'.",
244 FormatParam(tileset_name, Style::bold));
245
246 // Step 2: Only update callback to NULL if it's a Porytiles-managed callback
247 // This preserves user-managed callbacks when wire_anim_code is false
249 metadata,
250 metadata_provider_->metadata_for(tileset_name),
251 void,
252 "Failed to read metadata for '{}'.",
253 FormatParam(tileset_name, Style::bold));
254
255 const auto &current_callback = metadata.callback_func();
256 if (is_porytiles_managed_callback(current_callback)) {
258 metadata_writer_->update_callback(tileset_name, "NULL"),
259 void,
260 "Failed to update callback to NULL for '{}'.",
261 FormatParam(tileset_name, Style::bold));
262 }
263
264 return {};
265}
266
267} // 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:57
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 attribute_bytes) 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::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
std::string managed_callback_name(const std::string &tileset_name)
Definition animation.hpp:32
constexpr std::string_view porytiles_managed_callback_prefix
Definition animation.hpp:28
constexpr std::size_t num_palettes
Definition palette.hpp:21
@ tileset
Configuration scoped to a specific tileset.