Porytiles
Loading...
Searching...
No Matches
project_tileset_artifact_key_provider.cpp
Go to the documentation of this file.
2
3#include <filesystem>
4#include <string>
5
6#include "fmt/format.h"
7
9
10namespace {
11
12using namespace porytiles2;
13
14const std::filesystem::path primary_tileset_rel_path = std::filesystem::path{"data"} / "tilesets" / "primary";
15const std::filesystem::path secondary_tileset_rel_path = std::filesystem::path{"data"} / "tilesets" / "secondary";
16
17std::filesystem::path get_tileset_path(const std::string &tileset_name, const std::filesystem::path &project_root)
18{
19 if (std::filesystem::exists(project_root / primary_tileset_rel_path / tileset_name)) {
20 return project_root / primary_tileset_rel_path / tileset_name;
21 }
22 if (std::filesystem::exists(project_root / secondary_tileset_rel_path / tileset_name)) {
23 return project_root / secondary_tileset_rel_path / tileset_name;
24 }
25 panic(fmt::format("tileset '{}' does not exist", tileset_name));
26}
27
28const std::filesystem::path porytiles_directory{"porytiles"};
29const std::filesystem::path bottom_png{"bottom.png"};
30const std::filesystem::path middle_png{"middle.png"};
31const std::filesystem::path top_png{"top.png"};
32const std::filesystem::path attributes_csv{"attributes.csv"};
33const std::filesystem::path anim{"anim"};
34const std::filesystem::path pal_overrides{"palette-overrides"};
35const std::filesystem::path metatiles_bin{"metatiles.bin"};
36const std::filesystem::path metatile_attributes_bin{"metatile_attributes.bin"};
37const std::filesystem::path tiles_png{"tiles.png"};
38const std::filesystem::path palettes{"palettes"};
39const std::filesystem::path config{"porytiles.yaml"};
40const std::filesystem::path local_config{"porytiles.local.yaml"};
41
42} // namespace
43
44namespace porytiles2 {
45
47ProjectTilesetArtifactKeyProvider::key_for(const std::string &tileset_name, const TilesetArtifact &artifact) const
48{
49 const auto tileset_path = get_tileset_path(tileset_name, project_root_);
50
51 switch (artifact.type()) {
52 // Porytiles artifacts
54 return ArtifactKey{tileset_path / porytiles_directory / bottom_png};
56 return ArtifactKey{tileset_path / porytiles_directory / middle_png};
58 return ArtifactKey{tileset_path / porytiles_directory / top_png};
60 return ArtifactKey{tileset_path / porytiles_directory / attributes_csv};
62 if (!artifact.name().has_value()) {
63 panic("missing porytiles anim frame name");
64 }
65 if (!artifact.index().has_value()) {
66 panic("missing porytiles anim frame index");
67 }
68 const auto anim_name = artifact.name().value();
69 const auto frame_num = artifact.index().value();
70 return ArtifactKey{tileset_path / porytiles_directory / anim / anim_name / fmt::format("{:02}.png", frame_num)};
71 }
73 if (!artifact.index().has_value()) {
74 panic("missing pal override index");
75 }
76 const auto override_index = artifact.index().value();
77 return ArtifactKey{
78 tileset_path / porytiles_directory / pal_overrides / fmt::format("{:02}.pal", override_index)};
79 }
80
81 // Porymap artifacts
83 return ArtifactKey{tileset_path / metatiles_bin};
85 return ArtifactKey{tileset_path / metatile_attributes_bin};
87 return ArtifactKey{tileset_path / tiles_png};
89 if (!artifact.name().has_value()) {
90 panic("missing porymap anim frame name");
91 }
92 if (!artifact.index().has_value()) {
93 panic("missing porymap anim frame index");
94 }
95 const auto anim_name = artifact.name().value();
96 const auto frame_num = artifact.index().value();
97 return ArtifactKey{tileset_path / anim / anim_name / fmt::format("{:02}.png", frame_num)};
98 }
100 if (!artifact.index().has_value()) {
101 panic("missing pal index");
102 }
103 const auto pal_index = artifact.index().value();
104 return ArtifactKey{tileset_path / palettes / fmt::format("{:02}.pal", pal_index)};
105 }
107 return ArtifactKey{tileset_path / config};
108 }
110 return ArtifactKey{tileset_path / local_config};
111 }
112
113 // Default case
114 default:
115 panic("unhandled TilesetArtifact::Type");
116 }
117}
118
120{
121 const std::filesystem::path artifact{key.key()};
122 return std::filesystem::exists(artifact);
123}
124
125bool ProjectTilesetArtifactKeyProvider::tileset_exists(const std::string &tileset_name) const
126{
127 if (std::filesystem::exists(project_root_ / primary_tileset_rel_path / tileset_name)) {
128 return true;
129 }
130 if (std::filesystem::exists(project_root_ / secondary_tileset_rel_path / tileset_name)) {
131 return true;
132 }
133 return false;
134}
135
136std::set<std::string> ProjectTilesetArtifactKeyProvider::discover_porytiles_anims(const std::string &tileset_name) const
137{
138 const auto tileset_path = get_tileset_path(tileset_name, project_root_);
139 const auto anims_dir = tileset_path / porytiles_directory / anim;
140
141 std::set<std::string> anim_names;
142
143 if (!std::filesystem::exists(anims_dir) || !std::filesystem::is_directory(anims_dir)) {
144 return anim_names;
145 }
146
147 for (const auto &entry : std::filesystem::directory_iterator(anims_dir)) {
148 if (!entry.is_directory()) {
149 // TODO: warn user about stray file in porytiles/anim folder?
150 continue;
151 }
152
153 // Check if 00.png exists (required for Porytiles animations)
154 const auto frame_00_path = entry.path() / "00.png";
155 if (!std::filesystem::exists(frame_00_path)) {
156 // TODO: this is an error condition, an anim folder with no 00.png is invalid
157 continue;
158 }
159
160 const auto anim_name = entry.path().filename().string();
161 anim_names.insert(anim_name);
162 }
163
164 return anim_names;
165}
166
168 const std::string &tileset_name, const std::string &anim_name) const
169{
170 const auto tileset_path = get_tileset_path(tileset_name, project_root_);
171 const auto anim_dir = tileset_path / porytiles_directory / anim / anim_name;
172
173 std::set<int> frame_indices;
174
175 if (!std::filesystem::exists(anim_dir) || !std::filesystem::is_directory(anim_dir)) {
176 return frame_indices;
177 }
178
179 for (const auto &entry : std::filesystem::directory_iterator(anim_dir)) {
180 if (!entry.is_regular_file()) {
181 // TODO: warn user about stray folder in porytiles/anim/anim_name folder
182 continue;
183 }
184
185 const auto filename = entry.path().filename().string();
186
187 if (filename.length() != 6 || !filename.ends_with(".png")) {
188 // TODO: warn user about stray file in porytiles/anim/anim_name folder
189 continue;
190 }
191
192 // Skip 00.png (frame 0 is required, not discovered), handled in the main discover_anims method
193 if (filename == "00.png") {
194 continue;
195 }
196
197 // Check if it's a valid two-digit number
198 const auto frame_str = filename.substr(0, 2);
199 if (!std::isdigit(frame_str[0]) || !std::isdigit(frame_str[1])) {
200 // TODO: warn user about stray file in porytiles/anim/anim_name folder
201 continue;
202 }
203 const int frame_index = std::stoi(frame_str);
204 frame_indices.insert(frame_index);
205 }
206
207 return frame_indices;
208}
209
210std::set<std::string> ProjectTilesetArtifactKeyProvider::discover_porymap_anims(const std::string &tileset_name) const
211{
212 const auto tileset_path = get_tileset_path(tileset_name, project_root_);
213 const auto anims_dir = tileset_path / anim;
214
215 std::set<std::string> anim_names;
216
217 if (!std::filesystem::exists(anims_dir) || !std::filesystem::is_directory(anims_dir)) {
218 return anim_names;
219 }
220
221 for (const auto &entry : std::filesystem::directory_iterator(anims_dir)) {
222 if (!entry.is_directory()) {
223 // TODO: warn user about stray file in anim folder?
224 continue;
225 }
226
227 // Check if 00.png exists (required for Porymap animations)
228 const auto frame_00_path = entry.path() / "00.png";
229 if (!std::filesystem::exists(frame_00_path)) {
230 // TODO: this is an error condition, an anim folder with no 00.png is invalid
231 continue;
232 }
233
234 const auto anim_name = entry.path().filename().string();
235 anim_names.insert(anim_name);
236 }
237
238 return anim_names;
239}
240
242 const std::string &tileset_name, const std::string &anim_name) const
243{
244 const auto tileset_path = get_tileset_path(tileset_name, project_root_);
245 const auto anim_dir = tileset_path / anim / anim_name;
246
247 std::set<int> frame_indices;
248
249 if (!std::filesystem::exists(anim_dir) || !std::filesystem::is_directory(anim_dir)) {
250 return frame_indices;
251 }
252
253 for (const auto &entry : std::filesystem::directory_iterator(anim_dir)) {
254 if (!entry.is_regular_file()) {
255 // TODO: warn user about stray folder in anim/anim_name folder
256 continue;
257 }
258
259 const auto filename = entry.path().filename().string();
260
261 if (filename.length() != 6 || !filename.ends_with(".png")) {
262 // TODO: warn user about stray file in porytiles/anim/anim_name folder
263 continue;
264 }
265
266 // Skip 00.png (frame 0 is required, not discovered), handled in the main discover_anims method
267 if (filename == "00.png") {
268 continue;
269 }
270
271 // Check if it's a valid two-digit number
272 const auto frame_str = filename.substr(0, 2);
273 if (!std::isdigit(frame_str[0]) || !std::isdigit(frame_str[1])) {
274 // TODO: warn user about stray file in anim/anim_name folder
275 continue;
276 }
277 const int frame_index = std::stoi(frame_str);
278 frame_indices.insert(frame_index);
279 }
280
281 return frame_indices;
282}
283
284[[nodiscard]] std::filesystem::path
285ProjectTilesetArtifactKeyProvider::tileset_root(const std::string &tileset_name) const
286{
287 return get_tileset_path(tileset_name, project_root_);
288}
289
290} // namespace porytiles2
A type-safe wrapper for artifact keys.
const std::string & key() const
Gets the underlying string value.
std::set< std::string > discover_porytiles_anims(const std::string &tileset_name) const override
Discovers the names of all Porytiles animations available for a tileset.
std::filesystem::path tileset_root(const std::string &tileset_name) const
Returns the filesystem path to the root directory of a tileset.
ArtifactKey key_for(const std::string &tileset_name, const TilesetArtifact &artifact) const override
Constructs a key for a given tileset artifact.
std::set< int > discover_porymap_anim_frames(const std::string &tileset_name, const std::string &anim_name) const override
Discovers the frame indices for a specific Porymap animation.
std::set< std::string > discover_porymap_anims(const std::string &tileset_name) const override
Discovers the names of all Porymap animations available for a tileset.
bool artifact_exists(const ArtifactKey &key) const override
Checks whether an artifact exists in the backing store for the given key.
std::set< int > discover_porytiles_anim_frames(const std::string &tileset_name, const std::string &anim_name) const override
Discovers the frame indices for a specific Porytiles animation.
bool tileset_exists(const std::string &tileset_name) const override
Checks whether a tileset exists in the backing store for the given tileset name.
Represents a Pokémon Generation III decomp tileset artifact with type and optional metadata.
Type type() const
Gets the artifact type.
std::optional< std::string > name() const
Gets the artifact name if present.
@ porytiles_anim_frame
Animation frame PNG for Porytiles-format animation.
@ attributes_csv
CSV file containing metatile attribute overrides.
@ middle_png
Middle layer PNG input image.
@ metatile_attributes_bin
Metatile attributes output for Porymap.
@ bottom_png
Bottom layer PNG input image.
@ top_png
Top layer PNG input image.
@ pal_n
JASC palette data file.
@ metatiles_bin
Metatile data output for Porymap.
@ pal_override_n
JASC palette override file.
@ tiles_png
Combined tile sheet PNG output for Porymap.
@ local_config
Tileset configuration YAML file.
@ porymap_anim_frame
Animation frame PNG for Porymap-format animation.
std::optional< unsigned int > index() const
Gets the artifact index if present.
void panic(const StringViewSourceLoc &s)
Unconditionally terminates the program with a panic message.
Definition panic.hpp:53