mirror of
https://github.com/gbdk-2020/gbdk-2020.git
synced 2026-03-06 15:29:00 +01:00
- File not found is only a warning, not an error - metafile args are processed after args and appended to logged output - Split main arg processing function into a couple smaller ones
472 lines
17 KiB
C++
472 lines
17 KiB
C++
#include <vector>
|
|
#include <string>
|
|
#include <algorithm>
|
|
#include <cstring>
|
|
#include <set>
|
|
#include <stdio.h>
|
|
#include <fstream>
|
|
#include <cstdint>
|
|
#include <cstdlib>
|
|
#include <fstream>
|
|
#include <sstream>
|
|
|
|
#include "lodepng.h"
|
|
#include "mttile.h"
|
|
#include "export.h"
|
|
#include "map_attributes.h"
|
|
#include "palettes.h"
|
|
|
|
#include "cmp_int_color.h"
|
|
|
|
#include "image_utils.h"
|
|
#include "image_data.h"
|
|
#include "maps.h"
|
|
#include "metasprites.h"
|
|
#include "metasprites_functions.h"
|
|
#include "process_arguments.h"
|
|
#include "png_image.h"
|
|
#include "tiles.h"
|
|
|
|
#define ARG_SKIP_NONE 0
|
|
#define ARG_AT_INPUT_FILENAME 1
|
|
#define ARG_AFTER_INPUT_FILENAME 2
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
static string str_remove_path(string str_in);
|
|
static void logArguments(int startIndex, int argc, const char* argv[], PNG2AssetArguments* args);
|
|
static void initArguments(PNG2AssetArguments* args);
|
|
static void showHelp(void);
|
|
static int processArguments(int startIndex, int argc, const char* argv[], PNG2AssetArguments* args);
|
|
static int handleMetaFileArgs(PNG2AssetArguments* args);
|
|
|
|
// Strip any leading path and slashes
|
|
static string str_remove_path(string str_in) {
|
|
size_t slash_pos = str_in.find_last_of('/');
|
|
if (slash_pos != str_in.npos)
|
|
str_in = str_in.substr(slash_pos, str_in.length() - slash_pos);
|
|
|
|
slash_pos = str_in.find_last_of('\\');
|
|
if (slash_pos != str_in.npos)
|
|
str_in = str_in.substr(slash_pos, str_in.length() - slash_pos);
|
|
|
|
return str_in;
|
|
}
|
|
|
|
|
|
static void initArguments(PNG2AssetArguments* args) {
|
|
|
|
//default values for some params
|
|
args->spriteSize.width = 0;
|
|
args->spriteSize.height = 0;
|
|
args->map_attributes_size.width = 0;
|
|
args->map_attributes_size.height = 0;
|
|
args->map_attributes_packed_size.width = 0;
|
|
args->map_attributes_packed_size.height = 0;
|
|
|
|
args->pivot.x = 0xFFFFFF;
|
|
args->pivot.y = 0xFFFFFF;
|
|
args->pivot.width = 0xFFFFFF;
|
|
args->pivot.height = 0xFFFFFF;
|
|
|
|
|
|
args->max_palettes = 8;
|
|
|
|
args->keep_palette_order = false;
|
|
args->repair_indexed_pal = false;
|
|
args->output_binary = false;
|
|
args->output_transposed = false;
|
|
args->export_as_map = false;
|
|
args->use_map_attributes = false;
|
|
args->use_2x2_map_attributes = false;
|
|
args->pack_map_attributes = false;
|
|
args->convert_rgb_to_nes = false;
|
|
args->includeTileData = true;
|
|
args->includedMapOrMetaspriteData = true;
|
|
args->keep_duplicate_tiles = false;
|
|
args->keep_empty_sprite_tiles = false;
|
|
args->include_palettes = true;
|
|
args->use_structs = false;
|
|
args->flip_tiles = true;
|
|
|
|
// args->errorCode;
|
|
args->bank = BANK_NUM_UNSET;
|
|
args->area_specified = false;
|
|
args->sprite_mode = SPR_8x16;
|
|
args->bpp = 2;
|
|
args->props_default = 0;
|
|
|
|
args->tile_origin = 0; // Default to no tile index offset
|
|
args->source_total_color_count = 0;
|
|
args->source_tileset_size = 0;
|
|
args->has_source_tilesets = false;
|
|
|
|
args->has_entity_tileset = false;
|
|
|
|
args->processing_mode = MODE_MAIN_IMAGE;
|
|
|
|
args->pack_mode = Tile::GB;
|
|
args->map_entry_size_bytes = 1;
|
|
|
|
args->args_for_logging_to_output = "";
|
|
|
|
args->relative_paths = false;
|
|
args->use_metafile = false;
|
|
}
|
|
|
|
|
|
void showHelp(void) {
|
|
printf("usage: png2asset <file>.png [options]\n");
|
|
printf("-o <filename> ouput file (if not used then default is <png file>.c)\n");
|
|
printf("-c <filename> deprecated, same as -o\n");
|
|
printf("-sw <width> metasprites width size (default: png width)\n");
|
|
printf("-sh <height> metasprites height size (default: png height)\n");
|
|
printf("-sp <props> change default for sprite OAM property bytes (in hex) (default: 0x00)\n");
|
|
printf("-px <x coord> metasprites pivot x coordinate (default: metasprites width / 2)\n");
|
|
printf("-py <y coord> metasprites pivot y coordinate (default: metasprites height / 2)\n");
|
|
printf("-pw <width> metasprites collision rect width (default: metasprites width)\n");
|
|
printf("-ph <height> metasprites collision rect height (default: metasprites height)\n");
|
|
printf("-spr8x8 for 8x8 hardware sprites (use SPRITES_8x8)\n");
|
|
printf("-spr8x16 for 8x16 hardware sprites (use SPRITES_8x16) (the default)\n");
|
|
printf("-spr16x16msx MSX only: for 16x16 hardware sprites (use SPRITES_16x16)\n");
|
|
printf("-sprite_no_optimize keep empty sprite tiles, do not remove duplicate tiles\n");
|
|
printf("-b <banknum> Bank number (default: fixed bank)\n");
|
|
printf("-area <area name> Area name. Alters \"pragma bank ...\" output to constseg style. (Ex: -area LIT)\n");
|
|
printf("-keep_palette_order use png palette\n");
|
|
printf("-repair_indexed_pal try to repair indexed tile palettes (implies \"-keep_palette_order\")\n");
|
|
printf("-noflip disable tile flip\n");
|
|
printf("-map Export as map (tileset + bg) instead of default metasprite output\n");
|
|
printf("-use_map_attributes Use CGB BG Map attributes\n");
|
|
printf("-use_nes_attributes Use NES BG Map attributes\n");
|
|
printf("-use_nes_colors Convert RGB color values to NES PPU colors\n");
|
|
printf("-use_structs Group the exported info into structs (default: false) (used by ZGB Game Engine)\n");
|
|
printf("-bpp bits per pixel: 1, 2, 4 (default: 2. using 1 auto-enables \"-pack_mode 1bpp\")\n");
|
|
printf("-max_palettes max number of palettes allowed (default: 8)\n");
|
|
printf(" (note: max colors = max_palettes x num colors per palette)\n");
|
|
printf("-pack_mode gb, nes, sgb, sms, 1bpp (default: gb. using 1bpp auto-enables \"-bpp 1\")\n");
|
|
printf("-tile_origin tile index offset for maps (default: 0)\n");
|
|
|
|
printf("-tiles_only export tile data only\n");
|
|
printf("-maps_only export map tilemap only\n");
|
|
printf("-metasprites_only export metasprite descriptors only\n");
|
|
printf("-source_tileset use source tileset (image with common tiles)\n");
|
|
printf("-entity_tileset (maps only) mark matching tiles counting from 255 down, entity patterns not exported\n");
|
|
printf("-keep_duplicate_tiles do not remove duplicate tiles (default: not enabled)\n");
|
|
printf("-no_palettes do not export palette data\n");
|
|
|
|
printf("-bin export to binary format (requires -map)\n");
|
|
printf("-transposed export transposed (column-by-column instead of row-by-row)\n");
|
|
|
|
printf("-rel_paths paths to tilesets are relative to the input file path\n");
|
|
printf("-use_metafile Read extra options from file <inputfile>.meta (file missing not an error)\n");
|
|
}
|
|
|
|
|
|
static void logArguments(int startIndex, int argc, const char* argv[], PNG2AssetArguments* args) {
|
|
|
|
// Save all args for logging into output files
|
|
for (int i = startIndex; i < argc; ++i) {
|
|
args->args_for_logging_to_output.append(" ").append( str_remove_path((string)argv[i]) );
|
|
}
|
|
}
|
|
|
|
|
|
static int processArguments(int startIndex, int argc, const char* argv[], PNG2AssetArguments* args) {
|
|
|
|
//Parse argv
|
|
for (int i = startIndex; i < argc; ++i)
|
|
{
|
|
if(!strcmp(argv[i], "-sw"))
|
|
{
|
|
args->spriteSize.width = atoi(argv[++i]);
|
|
}
|
|
else if(!strcmp(argv[i], "-sh"))
|
|
{
|
|
args->spriteSize.height = atoi(argv[++i]);
|
|
}
|
|
else if(!strcmp(argv[i], "-sp"))
|
|
{
|
|
args->props_default = strtol(argv[++i], NULL, 16);
|
|
}
|
|
else if(!strcmp(argv[i], "-px"))
|
|
{
|
|
args->pivot.x = atoi(argv[++i]);
|
|
}
|
|
else if(!strcmp(argv[i], "-py"))
|
|
{
|
|
args->pivot.y = atoi(argv[++i]);
|
|
}
|
|
else if(!strcmp(argv[i], "-pw"))
|
|
{
|
|
args->pivot.width = atoi(argv[++i]);
|
|
}
|
|
else if(!strcmp(argv[i], "-ph"))
|
|
{
|
|
args->pivot.height = atoi(argv[++i]);
|
|
}
|
|
else if(!strcmp(argv[i], "-spr8x8"))
|
|
{
|
|
args->sprite_mode = SPR_8x8;
|
|
}
|
|
else if(!strcmp(argv[i], "-spr8x16"))
|
|
{
|
|
args->sprite_mode = SPR_8x16;
|
|
}
|
|
else if(!strcmp(argv[i], "-spr16x16msx"))
|
|
{
|
|
args->sprite_mode = SPR_16x16_MSX;
|
|
}
|
|
else if(!strcmp(argv[i], "-c") || !strcmp(argv[i], "-o"))
|
|
{
|
|
if ((i + 1) >= argc) {
|
|
printf("Error: -c or -o requires a filename, none specified\n");
|
|
return EXIT_FAILURE;
|
|
} else if (argv[i+1][0] == '-') {
|
|
printf("Error: next argument after -o looks like an option instead of a filename (\"%s\")\n", argv[i + 1]);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
args->output_filename = argv[++i];
|
|
}
|
|
else if(!strcmp(argv[i], "-b"))
|
|
{
|
|
args->bank = atoi(argv[++i]);
|
|
}
|
|
else if(!strcmp(argv[i], "-area"))
|
|
{
|
|
args->area_name = argv[++i];
|
|
args->area_specified = true;
|
|
}
|
|
else if(!strcmp(argv[i], "-keep_palette_order"))
|
|
{
|
|
args->keep_palette_order = true;
|
|
}
|
|
else if(!strcmp(argv[i], "-repair_indexed_pal"))
|
|
{
|
|
args->repair_indexed_pal = true;
|
|
args->keep_palette_order = true; // -repair_indexed_pal requires -keep_palette_order, so force it on
|
|
}
|
|
else if(!strcmp(argv[i], "-noflip"))
|
|
{
|
|
args->flip_tiles = false;
|
|
}
|
|
else if(!strcmp(argv[i], "-map"))
|
|
{
|
|
args->export_as_map = true;
|
|
}
|
|
else if(!strcmp(argv[i], "-use_map_attributes"))
|
|
{
|
|
args->use_map_attributes = true;
|
|
}
|
|
else if(!strcmp(argv[i], "-use_nes_attributes"))
|
|
{
|
|
args->use_map_attributes = true;
|
|
args->use_2x2_map_attributes = true;
|
|
args->pack_map_attributes = true;
|
|
}
|
|
else if(!strcmp(argv[i], "-use_nes_colors"))
|
|
{
|
|
args->convert_rgb_to_nes = true;
|
|
}
|
|
else if(!strcmp(argv[i], "-use_structs"))
|
|
{
|
|
args->use_structs = true;
|
|
}
|
|
else if(!strcmp(argv[i], "-bpp"))
|
|
{
|
|
args->bpp = atoi(argv[++i]);
|
|
// If set to "-bpp 1" auto-enable "-pack_mode 1bpp"
|
|
if (atoi(argv[i]) == 1) args->pack_mode = Tile::BPP1;
|
|
}
|
|
else if(!strcmp(argv[i], "-max_palettes"))
|
|
{
|
|
args->max_palettes = atoi(argv[++i]);
|
|
if(args->max_palettes == 0)
|
|
{
|
|
printf("-max_palettes must be larger than zero\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
}
|
|
else if(!strcmp(argv[i], "-pack_mode"))
|
|
{
|
|
std::string pack_mode_str = argv[++i];
|
|
if(pack_mode_str == "gb") args->pack_mode = Tile::GB;
|
|
else if(pack_mode_str == "nes") args->pack_mode = Tile::NES;
|
|
else if(pack_mode_str == "sgb") {
|
|
args->pack_mode = Tile::SGB;
|
|
// SGB attributes are packed in map data, so 2 bytes per map tile
|
|
args->map_entry_size_bytes = 2;
|
|
}
|
|
else if(pack_mode_str == "sms") {
|
|
args->pack_mode = Tile::SMS;
|
|
// SMS attributes are packed in map data, so 2 bytes per map tile
|
|
args->map_entry_size_bytes = 2;
|
|
}
|
|
else if(pack_mode_str == "1bpp") {
|
|
args->pack_mode = Tile::BPP1;
|
|
// If set to "-pack_mode 1bpp" auto-enable "-bpp 1"
|
|
args->bpp = 1;
|
|
}
|
|
else
|
|
{
|
|
printf("-pack_mode must be one of gb, nes, sgb, sms, 1bpp\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
}
|
|
else if(!strcmp(argv[i], "-tile_origin"))
|
|
{
|
|
args->tile_origin = atoi(argv[++i]);
|
|
}
|
|
else if(!strcmp(argv[i], "-maps_only") || !strcmp(argv[i], "-metasprites_only"))
|
|
{
|
|
args->includeTileData = false;
|
|
}
|
|
else if(!strcmp(argv[i], "-tiles_only"))
|
|
{
|
|
args->includedMapOrMetaspriteData = false;
|
|
}
|
|
else if(!strcmp(argv[i], "-keep_duplicate_tiles"))
|
|
{
|
|
args->keep_duplicate_tiles = true;
|
|
}
|
|
else if(!strcmp(argv[i], "-sprite_no_optimize"))
|
|
{
|
|
args->keep_duplicate_tiles = true;
|
|
args->keep_empty_sprite_tiles = true;
|
|
}
|
|
else if(!strcmp(argv[i], "-no_palettes"))
|
|
{
|
|
args->include_palettes = false;
|
|
}
|
|
else if(!strcmp(argv[i], "-source_tileset"))
|
|
{
|
|
// Warning / TODO:FIXME
|
|
// includeTileData gets overridden back to TRUE if any tiles not present in the source tileset are found.
|
|
// This makes understanding it's full export behavior much harder. Would be better to remove it and try to simplify logic.
|
|
args->includeTileData = false;
|
|
args->source_tilesets.push_back(argv[++i]);
|
|
}
|
|
else if(!strcmp(argv[i], "-entity_tileset"))
|
|
{
|
|
args->entity_tileset_filename = argv[++i];
|
|
args->has_entity_tileset = true;
|
|
}
|
|
else if(!strcmp(argv[i], "-bin"))
|
|
{
|
|
args->output_binary = true;
|
|
}
|
|
else if(!strcmp(argv[i], "-transposed"))
|
|
{
|
|
args->output_transposed = true;
|
|
}
|
|
else if(!strcmp(argv[i], "-rel_paths")) {
|
|
args->relative_paths = true;
|
|
}
|
|
else if(!strcmp(argv[i], "-use_metafile")) {
|
|
args->use_metafile = true;
|
|
}
|
|
else {
|
|
printf("Warning: Argument \"%s\" not recognized\n", argv[i]);
|
|
}
|
|
}
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
|
|
// Read in and process a set of arguments from a file named <inputfile>.meta
|
|
static int handleMetaFileArgs(PNG2AssetArguments* args) {
|
|
|
|
string fname = args->input_filename + ".meta";
|
|
ifstream metaFile(fname);
|
|
if ( metaFile )
|
|
{
|
|
static vector<string> argStrings;
|
|
static std::vector<char const*> metafile_argv; // Static for program scope, const to ensure c_str() pointers remain valid
|
|
|
|
// Read file contents
|
|
stringstream metaFileBuffer;
|
|
metaFileBuffer << metaFile.rdbuf();
|
|
metaFile.close();
|
|
|
|
// Split strings on spaces/newlines
|
|
string argEntry;
|
|
argStrings.clear();
|
|
while (metaFileBuffer >> argEntry) {
|
|
argStrings.push_back(argEntry);
|
|
}
|
|
|
|
// Build argv style array
|
|
int metafile_argc = static_cast<int>(argStrings.size());
|
|
metafile_argv.clear();
|
|
metafile_argv.reserve(metafile_argc + 1); // +1 for null terminator entry (optional with our usage)
|
|
for (const auto& s : argStrings) {
|
|
metafile_argv.push_back(s.c_str());
|
|
}
|
|
metafile_argv.push_back(nullptr);
|
|
|
|
// Append args to logged ones and then process them
|
|
logArguments(ARG_SKIP_NONE, metafile_argc, metafile_argv.data(), args);
|
|
if (processArguments(ARG_SKIP_NONE, metafile_argc, metafile_argv.data(), args) == EXIT_FAILURE)
|
|
return EXIT_FAILURE;
|
|
|
|
} else {
|
|
printf("Warning: -use_metafile specified but no meta file found at: %s\n", fname.c_str());
|
|
}
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
|
|
int processPNG2AssetArguments(int argc, char* argv[], PNG2AssetArguments* args) {
|
|
|
|
initArguments(args);
|
|
|
|
if (argc < 2) {
|
|
showHelp();
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
//default params
|
|
args->input_filename = argv[ARG_AT_INPUT_FILENAME];
|
|
args->output_filename = argv[ARG_AT_INPUT_FILENAME];
|
|
args->output_filename = args->output_filename.substr(0, args->output_filename.size() - 4) + ".c";
|
|
|
|
logArguments(ARG_AT_INPUT_FILENAME, argc, (const char **)argv, args);
|
|
if (processArguments(ARG_AFTER_INPUT_FILENAME, argc, (const char **)argv, args) == EXIT_FAILURE)
|
|
return EXIT_FAILURE;
|
|
|
|
if (args->use_metafile) {
|
|
if (handleMetaFileArgs(args) == EXIT_FAILURE)
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
int slash_pos = (int)args->output_filename.find_last_of('/');
|
|
if(slash_pos == -1)
|
|
slash_pos = (int)args->output_filename.find_last_of('\\');
|
|
int dot_pos = (int)args->output_filename.find_first_of('.', slash_pos == -1 ? 0 : slash_pos);
|
|
|
|
args->output_filename_h = args->output_filename.substr(0, dot_pos) + ".h";
|
|
args->output_filename_bin = args->output_filename.substr(0, dot_pos) + "_map.bin";
|
|
args->output_filename_attributes_bin = args->output_filename.substr(0, dot_pos) + "_map_attributes.bin";
|
|
args->output_filename_tiles_bin = args->output_filename.substr(0, dot_pos) + "_tiles.bin";
|
|
args->output_filename_palettes_bin = args->output_filename.substr(0, dot_pos) + "_palettes.bin";
|
|
args->data_name = args->output_filename.substr(slash_pos + 1, dot_pos - 1 - slash_pos);
|
|
replace(args->data_name.begin(), args->data_name.end(), '-', '_');
|
|
|
|
if ((args->area_specified == true) && (args->bank == BANK_NUM_UNSET)) {
|
|
printf("Error: \"-area\" specified but bank number is missing. A bank number is required, use \"-b\"\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
if((args->output_binary) && (args->export_as_map == false)) {
|
|
printf("Error: \"-bin\" export mode only works when \"-map\" is enabled\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|