Porytiles
Loading...
Searching...
No Matches
command_import_tileset.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <memory>
4#include <string>
5#include <unistd.h>
6
7#include "CLI/CLI.hpp"
8#include "fruit/fruit.h"
9
52
53#include "command.hpp"
54#include "option.hpp"
55
56class ImportTilesetCommand final : public Command {
57 public:
58 explicit ImportTilesetCommand(CLI::App &parent_app) : Command{parent_app, kCommandName, kCommandDesc, kCommandGroup}
59 {
60 CLI::App &cmd = get_app();
61 cmd.add_option("<tileset-name>", tileset_name_, "Name of the tileset to import")->required();
62 project_root_opt_.RegisterOpt(cmd);
63 porytiles::register_config_options(cmd, cli_storage_);
64 }
65
66 void Run() override
67 {
68 using namespace porytiles;
69
70 // Use Fruit DI to inject TextFormatter based on no_color flag
71 const bool no_color = !isatty(STDERR_FILENO); // Disable color when stderr is not a terminal
72 fruit::Injector injector{di::get_formatter_component, no_color};
73 auto text_formatter = injector.get<TextFormatter *>();
74
75 // Create unfiltered diag for config bootstrapping (so config-loading warnings always show)
76 auto stderr_diag = std::make_unique<StderrStyledUserDiagnostics>(text_formatter);
77
78 std::filesystem::path project_root = project_root_opt_.project_root();
79 std::filesystem::path fieldmap_header_root_relative{"include/fieldmap.h"};
80 std::filesystem::path behaviors_header_root_relative{"include/constants/metatile_behaviors.h"};
81 std::filesystem::path global_fieldmap_header_root_relative{"include/global.fieldmap.h"};
82
83 // Setup layered configuration (CLI options have highest priority)
84 std::vector<std::unique_ptr<ConfigProvider>> providers{};
85 providers.push_back(std::make_unique<CliOptionProvider>(cli_storage_));
86 auto yaml_provider = std::make_unique<YamlFileProvider>(text_formatter, stderr_diag.get(), project_root);
87 auto *yaml_provider_ptr = yaml_provider.get();
88 providers.push_back(std::move(yaml_provider));
89 providers.push_back(
90 std::make_unique<HeaderDefineProvider>(project_root, fieldmap_header_root_relative, text_formatter));
91 providers.push_back(std::make_unique<MetatilesHeaderProvider>(project_root, text_formatter));
92 providers.push_back(std::make_unique<DefaultProvider>());
93 LazyLayeredConfig config{text_formatter, std::move(providers)};
94
95 // Eagerly validate all YAML config files for unknown keys
96 if (yaml_provider_ptr->preload_and_validate(ConfigScopeType::tileset, tileset_name_)) {
97 const auto validation_err = ChainableResult<void>{FormattableError{
98 "Configuration validation failed for tileset '{}'.", FormatParam{tileset_name_, Style::bold}}};
99 stderr_diag->fatal(validation_err);
100 throw CLI::RuntimeError{1};
101 }
102
103 // Eagerly validate metatile-attr-size to fail fast before any file I/O
104 auto attr_size_check = config.metatile_attr_size(ConfigScopeType::tileset, tileset_name_);
105 if (!attr_size_check.has_value()) {
106 stderr_diag->fatal(attr_size_check);
107 throw CLI::RuntimeError{1};
108 }
109 const std::size_t metatile_attr_size = attr_size_check.value().value();
110
111 // Helper to safely extract filter patterns from config, falling back to empty on error
112 auto get_filter_patterns =
113 [&](ChainableResult<ConfigValue<std::vector<std::string>>> result) -> std::vector<std::string> {
114 if (result.has_value()) {
115 return std::move(result).value().value();
116 }
117 stderr_diag->fatal(result);
118 return {};
119 };
120
121 // Build diagnostic filters from config values
122 DiagnosticTagFilter warning_filter{
123 get_filter_patterns(config.diagnostic_warnings_exclude(ConfigScopeType::tileset, tileset_name_)),
124 get_filter_patterns(config.diagnostic_warnings_include(ConfigScopeType::tileset, tileset_name_))};
125
126 DiagnosticTagFilter remark_filter{
127 get_filter_patterns(config.diagnostic_remarks_exclude(ConfigScopeType::tileset, tileset_name_)),
128 get_filter_patterns(config.diagnostic_remarks_include(ConfigScopeType::tileset, tileset_name_))};
129
130 // Wrap with filter decorator for all subsequent operations
131 auto diag = std::make_unique<FilteredUserDiagnostics>(
132 text_formatter, stderr_diag.get(), std::move(warning_filter), std::move(remark_filter));
133
134 std::unique_ptr<TilePrinter> tile_printer = std::make_unique<AsciiTilePrinter>(text_formatter);
135 std::unique_ptr<PalettePrinter> pal_printer = std::make_unique<ColorPalettePrinter>(text_formatter);
136
137 // Initialize stateless services
138 PngRgbaImageLoader png_rgba_loader{};
139 PngIndexedImageLoader png_indexed_loader{};
140 PngRgbaImageSaver png_rgba_saver{};
141 PngIndexedImageSaver png_indexed_saver{};
142 JascPalLoader jasc_loader{text_formatter};
143 JascPalSaver jasc_saver{text_formatter};
144 AnimJsonParser anim_json_parser{text_formatter};
145 AnimCodeParser anim_code_parser{text_formatter, diag.get()};
146 AnimCodeGenerator anim_code_generator{};
147
148 // Setup behavior map provider
149 HeaderBehaviorMapProvider behavior_map_provider{
150 project_root / behaviors_header_root_relative, text_formatter, diag.get()};
151
152 // Setup metadata provider, tileset manager
153 ProjectTilesetMetadataProvider metadata_provider{project_root, text_formatter, diag.get()};
154 ProjectTilesetMetadataWriter metadata_writer{project_root, text_formatter};
155 IncbinDeclarationAppender incbin_appender{project_root, text_formatter};
156 ProjectTilesetAnimsModifier tileset_anims_modifier{project_root, &config, diag.get()};
157 ProjectPorytilesTilesetManager tileset_manager{
158 project_root,
159 &metadata_provider,
160 &metadata_writer,
161 &config,
162 diag.get(),
163 &incbin_appender,
164 &tileset_anims_modifier};
165
166 // Detect base game
167 BaseGameDetector base_game_detector{project_root, text_formatter, diag.get()};
168 auto base_game_result = base_game_detector.detect();
169 if (!base_game_result.has_value()) {
170 diag->fatal(base_game_result);
171 throw CLI::RuntimeError{1};
172 }
173 const BaseGame base_game = base_game_result.value();
174 if (base_game == BaseGame::pokeruby) {
175 diag->fatal(
177 FormattableError{"Importing pokeruby-based projects is not currently supported."}});
178 throw CLI::RuntimeError{1};
179 }
180
181 // Conditionally create terrain/encounter providers for FireRed
182 std::unique_ptr<HeaderTerrainTypeMapProvider> terrain_provider;
183 std::unique_ptr<HeaderEncounterTypeMapProvider> encounter_provider;
184 if (base_game == BaseGame::pokefirered) {
185 terrain_provider = std::make_unique<HeaderTerrainTypeMapProvider>(
186 project_root / global_fieldmap_header_root_relative, text_formatter, diag.get());
187 encounter_provider = std::make_unique<HeaderEncounterTypeMapProvider>(
188 project_root / global_fieldmap_header_root_relative, text_formatter, diag.get());
189 }
190
191 // Setup attributes CSV loader (after base game detection for format validation)
192 AttributesCsvLoader attributes_csv_loader{
193 text_formatter, &behavior_map_provider, base_game, terrain_provider.get(), encounter_provider.get()};
194
195 // Setup the tileset repository
197 project_root, &config, &metadata_provider, text_formatter, diag.get()};
198 ProjectTilesetArtifactReader artifact_reader{
199 project_root,
200 metatile_attr_size,
201 &png_rgba_loader,
202 &png_indexed_loader,
203 &jasc_loader,
204 &attributes_csv_loader,
205 &anim_json_parser,
206 &anim_code_parser,
207 &metadata_provider};
208 ProjectTilesetArtifactWriter artifact_writer{
209 &config,
210 &config,
211 project_root,
212 base_game,
213 metatile_attr_size,
214 text_formatter,
215 diag.get(),
216 &png_rgba_saver,
217 &png_indexed_saver,
218 &jasc_saver,
219 &anim_json_parser,
220 &anim_code_generator,
221 &behavior_map_provider,
222 terrain_provider.get(),
223 encounter_provider.get()};
224 ProjectArtifactChecksumProvider checksum_provider{project_root};
225 TilesetRepo repo{
226 &checksum_provider, &metadata_provider, &key_provider, &artifact_reader, &artifact_writer, diag.get()};
227
229 project_root,
230 metatile_attr_size,
231 &config,
232 text_formatter,
233 diag.get(),
234 tile_printer.get(),
235 pal_printer.get(),
236 &metadata_provider,
237 &png_indexed_loader,
238 &jasc_loader,
239 };
240 PrimaryTilesetDecompiler decompiler{&config, text_formatter, diag.get(), tile_printer.get(), pal_printer.get()};
241 TilesetCompiler compiler{&config, text_formatter, diag.get(), tile_printer.get(), pal_printer.get()};
242 ImportPrimaryTileset import_use_case{
243 &importer,
244 &decompiler,
245 &compiler,
246 &repo,
247 &metadata_provider,
248 &tileset_manager,
249 &config,
250 &config,
251 diag.get()};
252
253 // Run the use case
254 auto import_result = import_use_case.import(tileset_name_);
255 if (!import_result.has_value()) {
256 const auto fail_result = ChainableResult<std::unique_ptr<Tileset>>{
257 FormattableError{"Failed to import tileset '{}'.", FormatParam{tileset_name_, Style::bold}},
258 import_result};
259 diag->fatal(fail_result);
260 throw CLI::RuntimeError{1};
261 }
262 }
263
264 private:
265 static constexpr auto kCommandName = "import-tileset";
266 static constexpr auto kCommandDesc = "Import a pre-existing tileset into Porytiles.";
267 static constexpr auto kCommandGroup = "COMMANDS";
268 std::string tileset_name_;
269 OptProjectRoot project_root_opt_;
270 porytiles::CliOptionStorage cli_storage_;
271};
Command is an abstract class that provides basic command functionality for the Porytiles CLI driver.
Definition command.hpp:18
CLI::App & get_app() const
Definition command.hpp:44
ImportTilesetCommand(CLI::App &parent_app)
std::filesystem::path project_root() const
Definition option.hpp:58
void RegisterOpt(CLI::App &app) override
Definition option.hpp:46
Generates C header code for tileset animations.
Parses C code to extract tileset animation parameters using callback chain discovery.
Parses and writes animation configuration JSON files (anim.json).
A service that loads metatile attributes from a CSV file.
Detects the base game of a decompilation project.
ChainableResult< BaseGame > detect() const
Detects the base game for the project.
A result type that maintains a chainable sequence of errors for debugging and error reporting.
Regex-based include/exclude filter for diagnostic tags.
A text parameter with associated styling for formatted output.
General-purpose error implementation with formatted message support.
Definition error.hpp:63
Loads behavior mappings from a C/C++ header file containing behavior definitions.
Use case for importing a primary Tileset.
ChainableResult< void > import(const std::string &tileset_name) const
Appends INCBIN declarations for Porytiles-managed tileset assets.
An implementation of FilePalLoader that loads palettes from JASC-PAL (Paintshop Pro) pal files.
An implementation of FilePalSaver that saves palettes to JASC-PAL (Paintshop Pro) pal files.
A Config implementation that lazily pulls a config value by consulting multiple priority-ordered back...
An image loader that reads PNG files to create an Image with an index pixel type.
An image saver that saves PNG files from an Image with an index pixel type.
An image loader that reads PNG files to create an Image with an Rgba32 pixel type.
An image saver that saves PNG files from an Image with an Rgba32 pixel type.
Pokeemerald project filesystem-based implementation of ArtifactChecksumProvider.
Manages Porytiles-owned tilesets via TilesetManifest JSON files.
Infra-layer implementation of PrimaryTilesetImporter for pokeemerald project structure.
Modifies tileset_anims.c to include Porytiles-managed animation code headers.
Provides a pokeemerald project filesystem-based implementation for TilesetArtifactKeyProvider.
Provides a filesystem-based implementation for TilesetArtifactReader.
Provides a filesystem-based implementation for TilesetArtifactWriter.
Provides a pokeemerald project filesystem-based implementation for TilesetMetadataProvider.
Provides surgical update capability for tileset metadata in headers.h files.
Abstract base class for applying text styling with context-aware formatting.
Service that compiles a Tileset (primary or secondary).
Repository interface for the Tileset aggregate root.
BaseGame
Identifies the target base game for a decompilation project.
Definition base_game.hpp:20
void register_config_options(CLI::App &app, CliOptionStorage &storage)
Registers all config options with a CLI11 App.
Storage struct for CLI option values.