gbdk-2020 | GameBoy Development Kit |
| download: https://git.y1.nz/archives/gbdk.tar.gz | |
| README | Files | Log | Refs | LICENSE |
gbdk-support/png2asset/process_arguments.cpp
1 #include <vector>
2 #include <string>
3 #include <algorithm>
4 #include <cstring>
5 #include <set>
6 #include <stdio.h>
7 #include <fstream>
8 #include <cstdint>
9 #include <cstdlib>
10 #include <fstream>
11 #include <sstream>
12
13 #include "lodepng.h"
14 #include "mttile.h"
15 #include "export.h"
16 #include "map_attributes.h"
17 #include "palettes.h"
18
19 #include "cmp_int_color.h"
20
21 #include "image_utils.h"
22 #include "image_data.h"
23 #include "maps.h"
24 #include "metasprites.h"
25 #include "metasprites_functions.h"
26 #include "process_arguments.h"
27 #include "png_image.h"
28 #include "tiles.h"
29
30 #define ARG_SKIP_NONE 0
31 #define ARG_AT_INPUT_FILENAME 1
32 #define ARG_AFTER_INPUT_FILENAME 2
33
34
35
36 using namespace std;
37
38 static string str_remove_path(string str_in);
39 static void logArguments(int startIndex, int argc, const char* argv[], PNG2AssetArguments* args);
40 static void initArguments(PNG2AssetArguments* args);
41 static void showHelp(void);
42 static int processArguments(int startIndex, int argc, const char* argv[], PNG2AssetArguments* args);
43 static int handleMetaFileArgs(PNG2AssetArguments* args);
44
45 // Strip any leading path and slashes
46 static string str_remove_path(string str_in) {
47 size_t slash_pos = str_in.find_last_of('/');
48 if (slash_pos != str_in.npos)
49 str_in = str_in.substr(slash_pos, str_in.length() - slash_pos);
50
51 slash_pos = str_in.find_last_of('\\');
52 if (slash_pos != str_in.npos)
53 str_in = str_in.substr(slash_pos, str_in.length() - slash_pos);
54
55 return str_in;
56 }
57
58
59 static void initArguments(PNG2AssetArguments* args) {
60
61 //default values for some params
62 args->spriteSize.width = 0;
63 args->spriteSize.height = 0;
64 args->map_attributes_size.width = 0;
65 args->map_attributes_size.height = 0;
66 args->map_attributes_packed_size.width = 0;
67 args->map_attributes_packed_size.height = 0;
68
69 args->pivot.x = 0xFFFFFF;
70 args->pivot.y = 0xFFFFFF;
71 args->pivot.width = 0xFFFFFF;
72 args->pivot.height = 0xFFFFFF;
73
74
75 args->max_palettes = 8;
76
77 args->keep_palette_order = false;
78 args->repair_indexed_pal = false;
79 args->output_binary = false;
80 args->output_transposed = false;
81 args->export_as_map = false;
82 args->use_map_attributes = false;
83 args->use_2x2_map_attributes = false;
84 args->pack_map_attributes = false;
85 args->convert_rgb_to_nes = false;
86 args->includeTileData = true;
87 args->includedMapOrMetaspriteData = true;
88 args->keep_duplicate_tiles = false;
89 args->keep_empty_sprite_tiles = false;
90 args->include_palettes = true;
91 args->use_structs = false;
92 args->flip_tiles = true;
93
94 // args->errorCode;
95 args->bank = BANK_NUM_UNSET;
96 args->area_specified = false;
97 args->sprite_mode = SPR_8x16;
98 args->bpp = 2;
99 args->props_default = 0;
100
101 args->tile_origin = 0; // Default to no tile index offset
102 args->source_total_color_count = 0;
103 args->source_tileset_size = 0;
104 args->has_source_tilesets = false;
105
106 args->has_entity_tileset = false;
107
108 args->processing_mode = MODE_MAIN_IMAGE;
109
110 args->pack_mode = Tile::GB;
111 args->map_entry_size_bytes = 1;
112
113 args->args_for_logging_to_output = "";
114
115 args->relative_paths = false;
116 args->use_metafile = false;
117 }
118
119
120 void showHelp(void) {
121 printf("usage: png2asset <file>.png [options]\n");
122 printf("-o <filename> ouput file (if not used then default is <png file>.c)\n");
123 printf("-c <filename> deprecated, same as -o\n");
124 printf("-sw <width> metasprites width size (default: png width)\n");
125 printf("-sh <height> metasprites height size (default: png height)\n");
126 printf("-sp <props> change default for sprite OAM property bytes (in hex) (default: 0x00)\n");
127 printf("-px <x coord> metasprites pivot x coordinate (default: metasprites width / 2)\n");
128 printf("-py <y coord> metasprites pivot y coordinate (default: metasprites height / 2)\n");
129 printf("-pw <width> metasprites collision rect width (default: metasprites width)\n");
130 printf("-ph <height> metasprites collision rect height (default: metasprites height)\n");
131 printf("-spr8x8 for 8x8 hardware sprites (use SPRITES_8x8)\n");
132 printf("-spr8x16 for 8x16 hardware sprites (use SPRITES_8x16) (the default)\n");
133 printf("-spr16x16msx MSX only: for 16x16 hardware sprites (use SPRITES_16x16)\n");
134 printf("-sprite_no_optimize keep empty sprite tiles, do not remove duplicate tiles\n");
135 printf("-b <banknum> Bank number (default: fixed bank)\n");
136 printf("-area <area name> Area name. Alters \"pragma bank ...\" output to constseg style. (Ex: -area LIT)\n");
137 printf("-keep_palette_order use png palette\n");
138 printf("-repair_indexed_pal try to repair indexed tile palettes (implies \"-keep_palette_order\")\n");
139 printf("-noflip disable tile flip\n");
140 printf("-map Export as map (tileset + bg) instead of default metasprite output\n");
141 printf("-use_map_attributes Use GBC/SMS/GG BG Map attributes\n");
142 printf("-use_nes_attributes Use NES BG Map attributes\n");
143 printf("-use_nes_colors Convert RGB color values to NES PPU colors\n");
144 printf("-use_structs Group the exported info into structs (default: false) (used by ZGB Game Engine)\n");
145 printf("-bpp bits per pixel: 1, 2, 4 (default: 2. using 1 auto-enables \"-pack_mode 1bpp\")\n");
146 printf("-max_palettes max number of palettes allowed (default: 8)\n");
147 printf(" (note: max colors = max_palettes x num colors per palette)\n");
148 printf("-pack_mode gb, nes, sgb, sms, 1bpp (default: gb. using 1bpp auto-enables \"-bpp 1\")\n");
149 printf("-tile_origin tile index offset for maps (default: 0)\n");
150
151 printf("-tiles_only export tile data only\n");
152 printf("-maps_only export map tilemap only\n");
153 printf("-metasprites_only export metasprite descriptors only\n");
154 printf("-source_tileset use source tileset (image with common tiles)\n");
155 printf("-entity_tileset (maps only) mark matching tiles counting from 255 down, entity patterns not exported\n");
156 printf("-keep_duplicate_tiles do not remove duplicate tiles (default: not enabled)\n");
157 printf("-no_palettes do not export palette data\n");
158
159 printf("-bin export to binary format (requires -map)\n");
160 printf("-transposed export transposed (column-by-column instead of row-by-row)\n");
161
162 printf("-rel_paths paths to tilesets are relative to the input file path\n");
163 printf("-use_metafile Read extra options from file <inputfile>.meta (file missing not an error)\n");
164 }
165
166
167 static void logArguments(int startIndex, int argc, const char* argv[], PNG2AssetArguments* args) {
168
169 // Save all args for logging into output files
170 for (int i = startIndex; i < argc; ++i) {
171 args->args_for_logging_to_output.append(" ").append( str_remove_path((string)argv[i]) );
172 }
173 }
174
175
176 static int processArguments(int startIndex, int argc, const char* argv[], PNG2AssetArguments* args) {
177
178 //Parse argv
179 for (int i = startIndex; i < argc; ++i)
180 {
181 if(!strcmp(argv[i], "-sw"))
182 {
183 args->spriteSize.width = atoi(argv[++i]);
184 }
185 else if(!strcmp(argv[i], "-sh"))
186 {
187 args->spriteSize.height = atoi(argv[++i]);
188 }
189 else if(!strcmp(argv[i], "-sp"))
190 {
191 args->props_default = strtol(argv[++i], NULL, 16);
192 }
193 else if(!strcmp(argv[i], "-px"))
194 {
195 args->pivot.x = atoi(argv[++i]);
196 }
197 else if(!strcmp(argv[i], "-py"))
198 {
199 args->pivot.y = atoi(argv[++i]);
200 }
201 else if(!strcmp(argv[i], "-pw"))
202 {
203 args->pivot.width = atoi(argv[++i]);
204 }
205 else if(!strcmp(argv[i], "-ph"))
206 {
207 args->pivot.height = atoi(argv[++i]);
208 }
209 else if(!strcmp(argv[i], "-spr8x8"))
210 {
211 args->sprite_mode = SPR_8x8;
212 }
213 else if(!strcmp(argv[i], "-spr8x16"))
214 {
215 args->sprite_mode = SPR_8x16;
216 }
217 else if(!strcmp(argv[i], "-spr16x16msx"))
218 {
219 args->sprite_mode = SPR_16x16_MSX;
220 }
221 else if(!strcmp(argv[i], "-c") || !strcmp(argv[i], "-o"))
222 {
223 if ((i + 1) >= argc) {
224 printf("Error: -c or -o requires a filename, none specified\n");
225 return EXIT_FAILURE;
226 } else if (argv[i+1][0] == '-') {
227 printf("Error: next argument after -o looks like an option instead of a filename (\"%s\")\n", argv[i + 1]);
228 return EXIT_FAILURE;
229 }
230
231 args->output_filename = argv[++i];
232 }
233 else if(!strcmp(argv[i], "-b"))
234 {
235 args->bank = atoi(argv[++i]);
236 }
237 else if(!strcmp(argv[i], "-area"))
238 {
239 args->area_name = argv[++i];
240 args->area_specified = true;
241 }
242 else if(!strcmp(argv[i], "-keep_palette_order"))
243 {
244 args->keep_palette_order = true;
245 }
246 else if(!strcmp(argv[i], "-repair_indexed_pal"))
247 {
248 args->repair_indexed_pal = true;
249 args->keep_palette_order = true; // -repair_indexed_pal requires -keep_palette_order, so force it on
250 }
251 else if(!strcmp(argv[i], "-noflip"))
252 {
253 args->flip_tiles = false;
254 }
255 else if(!strcmp(argv[i], "-map"))
256 {
257 args->export_as_map = true;
258 }
259 else if(!strcmp(argv[i], "-use_map_attributes"))
260 {
261 args->use_map_attributes = true;
262 }
263 else if(!strcmp(argv[i], "-use_nes_attributes"))
264 {
265 args->use_map_attributes = true;
266 args->use_2x2_map_attributes = true;
267 args->pack_map_attributes = true;
268 }
269 else if(!strcmp(argv[i], "-use_nes_colors"))
270 {
271 args->convert_rgb_to_nes = true;
272 }
273 else if(!strcmp(argv[i], "-use_structs"))
274 {
275 args->use_structs = true;
276 }
277 else if(!strcmp(argv[i], "-bpp"))
278 {
279 args->bpp = atoi(argv[++i]);
280 // If set to "-bpp 1" auto-enable "-pack_mode 1bpp"
281 if (atoi(argv[i]) == 1) args->pack_mode = Tile::BPP1;
282 }
283 else if(!strcmp(argv[i], "-max_palettes"))
284 {
285 args->max_palettes = atoi(argv[++i]);
286 if(args->max_palettes == 0)
287 {
288 printf("-max_palettes must be larger than zero\n");
289 return EXIT_FAILURE;
290 }
291 }
292 else if(!strcmp(argv[i], "-pack_mode"))
293 {
294 std::string pack_mode_str = argv[++i];
295 if(pack_mode_str == "gb") args->pack_mode = Tile::GB;
296 else if(pack_mode_str == "nes") args->pack_mode = Tile::NES;
297 else if(pack_mode_str == "sgb") {
298 args->pack_mode = Tile::SGB;
299 // SGB attributes are packed in map data, so 2 bytes per map tile
300 args->map_entry_size_bytes = 2;
301 }
302 else if(pack_mode_str == "sms") {
303 args->pack_mode = Tile::SMS;
304 // SMS attributes are packed in map data, so 2 bytes per map tile
305 args->map_entry_size_bytes = 2;
306 }
307 else if(pack_mode_str == "1bpp") {
308 args->pack_mode = Tile::BPP1;
309 // If set to "-pack_mode 1bpp" auto-enable "-bpp 1"
310 args->bpp = 1;
311 }
312 else
313 {
314 printf("-pack_mode must be one of gb, nes, sgb, sms, 1bpp\n");
315 return EXIT_FAILURE;
316 }
317 }
318 else if(!strcmp(argv[i], "-tile_origin"))
319 {
320 args->tile_origin = atoi(argv[++i]);
321 }
322 else if(!strcmp(argv[i], "-maps_only") || !strcmp(argv[i], "-metasprites_only"))
323 {
324 args->includeTileData = false;
325 }
326 else if(!strcmp(argv[i], "-tiles_only"))
327 {
328 args->includedMapOrMetaspriteData = false;
329 }
330 else if(!strcmp(argv[i], "-keep_duplicate_tiles"))
331 {
332 args->keep_duplicate_tiles = true;
333 }
334 else if(!strcmp(argv[i], "-sprite_no_optimize"))
335 {
336 args->keep_duplicate_tiles = true;
337 args->keep_empty_sprite_tiles = true;
338 }
339 else if(!strcmp(argv[i], "-no_palettes"))
340 {
341 args->include_palettes = false;
342 }
343 else if(!strcmp(argv[i], "-source_tileset"))
344 {
345 // Warning / TODO:FIXME
346 // includeTileData gets overridden back to TRUE if any tiles not present in the source tileset are found.
347 // This makes understanding it's full export behavior much harder. Would be better to remove it and try to simplify logic.
348 args->includeTileData = false;
349 args->source_tilesets.push_back(argv[++i]);
350 }
351 else if(!strcmp(argv[i], "-entity_tileset"))
352 {
353 args->entity_tileset_filename = argv[++i];
354 args->has_entity_tileset = true;
355 }
356 else if(!strcmp(argv[i], "-bin"))
357 {
358 args->output_binary = true;
359 }
360 else if(!strcmp(argv[i], "-transposed"))
361 {
362 args->output_transposed = true;
363 }
364 else if(!strcmp(argv[i], "-rel_paths")) {
365 args->relative_paths = true;
366 }
367 else if(!strcmp(argv[i], "-use_metafile")) {
368 args->use_metafile = true;
369 }
370 else {
371 printf("Warning: Argument \"%s\" not recognized\n", argv[i]);
372 }
373 }
374
375 return EXIT_SUCCESS;
376 }
377
378
379 // Read in and process a set of arguments from a file named <inputfile>.meta
380 static int handleMetaFileArgs(PNG2AssetArguments* args) {
381
382 string fname = args->input_filename + ".meta";
383 ifstream metaFile(fname);
384 if ( metaFile )
385 {
386 static vector<string> argStrings;
387 static std::vector<char const*> metafile_argv; // Static for program scope, const to ensure c_str() pointers remain valid
388
389 // Read file contents
390 stringstream metaFileBuffer;
391 metaFileBuffer << metaFile.rdbuf();
392 metaFile.close();
393
394 // Split strings on spaces/newlines
395 string argEntry;
396 argStrings.clear();
397 while (metaFileBuffer >> argEntry) {
398 argStrings.push_back(argEntry);
399 }
400
401 // Build argv style array
402 int metafile_argc = static_cast<int>(argStrings.size());
403 metafile_argv.clear();
404 metafile_argv.reserve(metafile_argc + 1); // +1 for null terminator entry (optional with our usage)
405 for (const auto& s : argStrings) {
406 metafile_argv.push_back(s.c_str());
407 }
408 metafile_argv.push_back(nullptr);
409
410 // Append args to logged ones and then process them
411 logArguments(ARG_SKIP_NONE, metafile_argc, metafile_argv.data(), args);
412 if (processArguments(ARG_SKIP_NONE, metafile_argc, metafile_argv.data(), args) == EXIT_FAILURE)
413 return EXIT_FAILURE;
414
415 } else {
416 printf("Warning: -use_metafile specified but no meta file found at: %s\n", fname.c_str());
417 }
418
419 return EXIT_SUCCESS;
420 }
421
422
423 int processPNG2AssetArguments(int argc, char* argv[], PNG2AssetArguments* args) {
424
425 initArguments(args);
426
427 if (argc < 2) {
428 showHelp();
429 return EXIT_SUCCESS;
430 }
431
432 //default params
433 args->input_filename = argv[ARG_AT_INPUT_FILENAME];
434 args->output_filename = argv[ARG_AT_INPUT_FILENAME];
435 args->output_filename = args->output_filename.substr(0, args->output_filename.size() - 4) + ".c";
436
437 logArguments(ARG_AT_INPUT_FILENAME, argc, (const char **)argv, args);
438 if (processArguments(ARG_AFTER_INPUT_FILENAME, argc, (const char **)argv, args) == EXIT_FAILURE)
439 return EXIT_FAILURE;
440
441 if (args->use_metafile) {
442 if (handleMetaFileArgs(args) == EXIT_FAILURE)
443 return EXIT_FAILURE;
444 }
445
446 int slash_pos = (int)args->output_filename.find_last_of('/');
447 if(slash_pos == -1)
448 slash_pos = (int)args->output_filename.find_last_of('\\');
449 int dot_pos = (int)args->output_filename.find_first_of('.', slash_pos == -1 ? 0 : slash_pos);
450
451 args->output_filename_h = args->output_filename.substr(0, dot_pos) + ".h";
452 args->output_filename_bin = args->output_filename.substr(0, dot_pos) + "_map.bin";
453 args->output_filename_attributes_bin = args->output_filename.substr(0, dot_pos) + "_map_attributes.bin";
454 args->output_filename_tiles_bin = args->output_filename.substr(0, dot_pos) + "_tiles.bin";
455 args->output_filename_palettes_bin = args->output_filename.substr(0, dot_pos) + "_palettes.bin";
456 args->data_name = args->output_filename.substr(slash_pos + 1, dot_pos - 1 - slash_pos);
457 replace(args->data_name.begin(), args->data_name.end(), '-', '_');
458
459 if ((args->area_specified == true) && (args->bank == BANK_NUM_UNSET)) {
460 printf("Error: \"-area\" specified but bank number is missing. A bank number is required, use \"-b\"\n");
461 return EXIT_FAILURE;
462 }
463
464 if((args->output_binary) && (args->export_as_map == false)) {
465 printf("Error: \"-bin\" export mode only works when \"-map\" is enabled\n");
466 return EXIT_FAILURE;
467 }
468
469 return EXIT_SUCCESS;
470 }
471
This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.