mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 18:22:07 +00:00
Implement rgbgfx -O (#1240)
This commit is contained in:
@@ -10,6 +10,7 @@ _rgbgfx_completions() {
|
||||
[V]="version:normal"
|
||||
[C]="color-curve:normal"
|
||||
[m]="mirror-tiles:normal"
|
||||
[O]="group-outputs:normal"
|
||||
[u]="unique-tiles:normal"
|
||||
[v]="verbose:normal"
|
||||
[Z]="columns:normal"
|
||||
|
||||
@@ -16,6 +16,7 @@ local args=(
|
||||
'(-a --attr-map -A --output-attr-map)'{-A,--output-attr-map}'[Shortcut for -a <file>.attrmap]'
|
||||
'(-C --color-curve)'{-C,--color-curve}'[Generate palettes using GBC color curve]'
|
||||
'(-m --mirror-tiles)'{-m,--mirror-tiles}'[Eliminate mirrored tiles from output]'
|
||||
'(-O --group-outputs)'{-O,--group-outputs}'[Base "shortcut" options on the output path, not input]'
|
||||
'(-p --palette -P --output-palette)'{-P,--output-palette}'[Shortcut for -p <file>.pal]'
|
||||
'(-q --palette-map -Q --output-palette-map)'{-Q,--output-palette-map}'[Shortcut for -p <file>.palmap]'
|
||||
'(-t --tilemap -T --output-tilemap)'{-T,--output-tilemap}'[Shortcut for -t <file>.tilemap]'
|
||||
|
||||
59
man/rgbgfx.1
59
man/rgbgfx.1
@@ -10,7 +10,7 @@
|
||||
.Nd Game Boy graphics converter
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Op Fl CmuVZ
|
||||
.Op Fl CmOuVZ
|
||||
.Op Fl v Op Fl v No ...
|
||||
.Op Fl a Ar attrmap | Fl A
|
||||
.Op Fl b Ar base_ids
|
||||
@@ -100,11 +100,8 @@ follows the same order
|
||||
and has the same size.
|
||||
.It Fl A , Fl Fl output-attr-map
|
||||
Same as
|
||||
.Fl a Ar path ,
|
||||
where
|
||||
.Ar path
|
||||
is the input image's path with the extension set to
|
||||
.Pa .attrmap .
|
||||
.Fl a Ar base_path Ns .attrmap
|
||||
.Pq see Sx Automatic output paths .
|
||||
.It Fl b Ar base_ids , Fl Fl base-tiles Ar base_ids
|
||||
Set the base IDs for tile map output.
|
||||
.Ar base_ids
|
||||
@@ -207,6 +204,13 @@ This may not be more than 256.
|
||||
.Pp
|
||||
Note that attribute map output only has 3 bits for the palette ID, so a limit higher than 8 may yield incomplete data unless relying on a palette map
|
||||
.Pq see Fl q .
|
||||
.It Fl O , Fl Fl group-outputs
|
||||
Sets the
|
||||
.Sq base path
|
||||
to be the output tile data path from
|
||||
.Fl o
|
||||
instead of the input image path
|
||||
.Pq see Sx Automatic output paths .
|
||||
.It Fl o Ar out_file , Fl Fl output Ar out_file
|
||||
Output the tile data in native 2bpp format or in 1bpp
|
||||
.Pq depending on Fl d
|
||||
@@ -215,21 +219,15 @@ to this file.
|
||||
Output the image's palette set to this file.
|
||||
.It Fl P , Fl Fl output-palette
|
||||
Same as
|
||||
.Fl p Ar path ,
|
||||
where
|
||||
.Ar path
|
||||
is the input image's path with the extension set to
|
||||
.Pa .pal .
|
||||
.Fl p Ar base_path Ns .pal
|
||||
.Pq see Sx Automatic output paths .
|
||||
.It Fl q Ar pal_file , Fl Fl palette-map Ar pal_file
|
||||
Output the image's palette map to this file.
|
||||
This is useful if the input image contains more than 8 palettes, as the attribute map only contains the lower 3 bits of the palette indices.
|
||||
.It Fl Q , Fl Fl output-palette-map
|
||||
Same as
|
||||
.Fl q Ar path ,
|
||||
where
|
||||
.Ar path
|
||||
is the input image's path with the extension set to
|
||||
.Pa .palmap .
|
||||
.Fl q Ar base_path Ns .palmap
|
||||
.Pq see Sx Automatic output paths .
|
||||
.It Fl r Ar width , Fl Fl reverse Ar width
|
||||
Switches
|
||||
.Nm
|
||||
@@ -264,11 +262,8 @@ and/or
|
||||
to keep track of duplicate tiles.
|
||||
.It Fl T , Fl Fl output-tilemap
|
||||
Same as
|
||||
.Fl t Ar path ,
|
||||
where
|
||||
.Ar path
|
||||
is the input image's path with the extension set to
|
||||
.Pa .tilemap .
|
||||
.Fl t Ar base_path Ns .tilemap
|
||||
.Pq see Sx Automatic output paths .
|
||||
.It Fl u , Fl Fl unique-tiles
|
||||
Deduplicate identical tiles. Only one of each unique tile will be saved in the tile data file.
|
||||
Useful with a tile map
|
||||
@@ -533,6 +528,28 @@ The contents of individual bytes follows the GBC's native format:
|
||||
.El
|
||||
.Pp
|
||||
Note that if more than 8 palettes are used, only the lowest 3 bits of the palette ID are output.
|
||||
.Ss Automatic output paths
|
||||
For convenience,
|
||||
.Nm
|
||||
provides shortcuts to generate all files in the same directory.
|
||||
This is done by using the uppercase version of a flag
|
||||
.Pq for example, Fl A No instead of Fl a .
|
||||
The
|
||||
.Ar base_path
|
||||
is the input image path
|
||||
.Pq or the output tile data path from Fl o , No if Fl O No was given
|
||||
with its extension, if any, removed.
|
||||
.Pp
|
||||
For example, these two are equivalent:
|
||||
.Bd -literal -indent Ds
|
||||
$ rgbgfx img/player.png -o build/player.2bpp -P
|
||||
$ rgbgfx img/player.png -o build/player.2bpp -p img/player.pal
|
||||
.Pp
|
||||
And so are these two:
|
||||
.Bd -literal -indent Ds
|
||||
$ rgbgfx img/player.png -o build/player.2bpp -O -P
|
||||
$ rgbgfx img/player.png -o build/player.2bpp -p build/player.pal
|
||||
.El
|
||||
.Sh REVERSE MODE
|
||||
.Nm
|
||||
can produce a PNG image from valid data.
|
||||
|
||||
@@ -34,7 +34,16 @@
|
||||
using namespace std::literals::string_view_literals;
|
||||
|
||||
Options options;
|
||||
char const *externalPalSpec = nullptr;
|
||||
|
||||
static struct LocalOptions {
|
||||
char const *externalPalSpec;
|
||||
bool autoAttrmap;
|
||||
bool autoTilemap;
|
||||
bool autoPalettes;
|
||||
bool autoPalmap;
|
||||
bool groupOutputs;
|
||||
} localOptions;
|
||||
|
||||
static uintmax_t nbErrors;
|
||||
|
||||
[[noreturn]] void giveUp() {
|
||||
@@ -91,7 +100,7 @@ void Options::verbosePrint(uint8_t level, char const *fmt, ...) const {
|
||||
}
|
||||
|
||||
// Short options
|
||||
static char const *optstring = "-Aa:b:Cc:Dd:FfhL:mN:n:o:Pp:Qq:r:s:Tt:U:uVvx:Z";
|
||||
static char const *optstring = "-Aa:b:Cc:Dd:FfhL:mN:n:Oo:Pp:Qq:r:s:Tt:U:uVvx:Z";
|
||||
|
||||
/*
|
||||
* Equivalent long options
|
||||
@@ -118,6 +127,7 @@ static struct option const longopts[] = {
|
||||
{"mirror-tiles", no_argument, NULL, 'm'},
|
||||
{"nb-tiles", required_argument, NULL, 'N'},
|
||||
{"nb-palettes", required_argument, NULL, 'n'},
|
||||
{"group-outputs", no_argument, NULL, 'O'},
|
||||
{"output", required_argument, NULL, 'o'},
|
||||
{"output-palette", no_argument, NULL, 'P'},
|
||||
{"palette", required_argument, NULL, 'p'},
|
||||
@@ -136,7 +146,7 @@ static struct option const longopts[] = {
|
||||
};
|
||||
|
||||
static void printUsage(void) {
|
||||
fputs("Usage: rgbgfx [-r stride] [-CmuVZ] [-v [-v ...]] [-a <attr_map> | -A]\n"
|
||||
fputs("Usage: rgbgfx [-r stride] [-CmOuVZ] [-v [-v ...]] [-a <attr_map> | -A]\n"
|
||||
" [-b <base_ids>] [-c <colors>] [-d <depth>] [-L <slice>] [-N <nb_tiles>]\n"
|
||||
" [-n <nb_pals>] [-o <out_file>] [-p <pal_file> | -P] [-q <pal_map> | -Q]\n"
|
||||
" [-s <nb_colors>] [-t <tile_map> | -T] [-x <nb_tiles>] <file>\n"
|
||||
@@ -321,23 +331,21 @@ static std::vector<size_t> readAtFile(std::filesystem::path const &path,
|
||||
}
|
||||
|
||||
/*
|
||||
* Parses an arg vector, modifying `options` as options are read.
|
||||
* The three booleans are for the "auto path" flags, since their processing must be deferred to the
|
||||
* end of option parsing.
|
||||
* Parses an arg vector, modifying `options` and `localOptions` as options are read.
|
||||
* The `localOptions` struct is for flags which must be processed after the option parsing finishes.
|
||||
*
|
||||
* Returns NULL if the vector was fully parsed, or a pointer (which is part of the arg vector) to an
|
||||
* "at-file" path if one is encountered.
|
||||
*/
|
||||
static char *parseArgv(int argc, char **argv, bool &autoAttrmap, bool &autoTilemap,
|
||||
bool &autoPalettes, bool &autoPalmap) {
|
||||
static char *parseArgv(int argc, char **argv) {
|
||||
for (int ch; (ch = musl_getopt_long_only(argc, argv, optstring, longopts, nullptr)) != -1;) {
|
||||
char *arg = musl_optarg; // Make a copy for scanning
|
||||
switch (ch) {
|
||||
case 'A':
|
||||
autoAttrmap = true;
|
||||
localOptions.autoAttrmap = true;
|
||||
break;
|
||||
case 'a':
|
||||
autoAttrmap = false;
|
||||
localOptions.autoAttrmap = false;
|
||||
if (options.attrmap.has_value())
|
||||
warning("Overriding attrmap file %s", options.attrmap->c_str());
|
||||
options.attrmap = musl_optarg;
|
||||
@@ -385,7 +393,7 @@ static char *parseArgv(int argc, char **argv, bool &autoAttrmap, bool &autoTilem
|
||||
// size to be split; thus, we defer that
|
||||
// TODO: this does not validate the `fmt` part of any external spec but the last
|
||||
// one, but I guess that's okay
|
||||
externalPalSpec = musl_optarg;
|
||||
localOptions.externalPalSpec = musl_optarg;
|
||||
}
|
||||
break;
|
||||
case 'd':
|
||||
@@ -481,25 +489,28 @@ static char *parseArgv(int argc, char **argv, bool &autoAttrmap, bool &autoTilem
|
||||
error("Number of palettes (-n) may not be 0!");
|
||||
}
|
||||
break;
|
||||
case 'O':
|
||||
localOptions.groupOutputs = true;
|
||||
break;
|
||||
case 'o':
|
||||
if (options.output.has_value())
|
||||
warning("Overriding tile data file %s", options.output->c_str());
|
||||
options.output = musl_optarg;
|
||||
break;
|
||||
case 'P':
|
||||
autoPalettes = true;
|
||||
localOptions.autoPalettes = true;
|
||||
break;
|
||||
case 'p':
|
||||
autoPalettes = false;
|
||||
localOptions.autoPalettes = false;
|
||||
if (options.palettes.has_value())
|
||||
warning("Overriding palettes file %s", options.palettes->c_str());
|
||||
options.palettes = musl_optarg;
|
||||
break;
|
||||
case 'Q':
|
||||
autoPalmap = true;
|
||||
localOptions.autoPalmap = true;
|
||||
break;
|
||||
case 'q':
|
||||
autoPalmap = false;
|
||||
localOptions.autoPalmap = false;
|
||||
if (options.palmap.has_value())
|
||||
warning("Overriding palette map file %s", options.palmap->c_str());
|
||||
options.palmap = musl_optarg;
|
||||
@@ -525,10 +536,10 @@ static char *parseArgv(int argc, char **argv, bool &autoAttrmap, bool &autoTilem
|
||||
}
|
||||
break;
|
||||
case 'T':
|
||||
autoTilemap = true;
|
||||
localOptions.autoTilemap = true;
|
||||
break;
|
||||
case 't':
|
||||
autoTilemap = false;
|
||||
localOptions.autoTilemap = false;
|
||||
if (options.tilemap.has_value())
|
||||
warning("Overriding tilemap file %s", options.tilemap->c_str());
|
||||
options.tilemap = musl_optarg;
|
||||
@@ -577,8 +588,6 @@ static char *parseArgv(int argc, char **argv, bool &autoAttrmap, bool &autoTilem
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
bool autoAttrmap = false, autoTilemap = false, autoPalettes = false, autoPalmap = false;
|
||||
|
||||
struct AtFileStackEntry {
|
||||
int parentInd; // Saved offset into parent argv
|
||||
std::vector<char *> argv; // This context's arg pointer vec
|
||||
@@ -592,8 +601,7 @@ int main(int argc, char *argv[]) {
|
||||
int curArgc = argc;
|
||||
char **curArgv = argv;
|
||||
for (;;) {
|
||||
char *atFileName =
|
||||
parseArgv(curArgc, curArgv, autoAttrmap, autoTilemap, autoPalettes, autoPalmap);
|
||||
char *atFileName = parseArgv(curArgc, curArgv);
|
||||
if (atFileName) {
|
||||
// Copy `argv[0]` for error reporting, and because option parsing skips it
|
||||
AtFileStackEntry &stackEntry =
|
||||
@@ -649,22 +657,24 @@ int main(int argc, char *argv[]) {
|
||||
auto autoOutPath = [](bool autoOptEnabled, std::optional<std::filesystem::path> &path,
|
||||
char const *extension) {
|
||||
if (autoOptEnabled) {
|
||||
if (!options.input.has_value()) {
|
||||
fputs("FATAL: No input image specified\n", stderr);
|
||||
auto image = localOptions.groupOutputs ? options.output : options.input;
|
||||
if (!image.has_value()) {
|
||||
fprintf(stderr, "FATAL: No %s specified\n", localOptions.groupOutputs
|
||||
? "output tile data file" : "input image");
|
||||
printUsage();
|
||||
exit(1);
|
||||
}
|
||||
path.emplace(*options.input).replace_extension(extension);
|
||||
path.emplace(*image).replace_extension(extension);
|
||||
}
|
||||
};
|
||||
autoOutPath(autoAttrmap, options.attrmap, ".attrmap");
|
||||
autoOutPath(autoTilemap, options.tilemap, ".tilemap");
|
||||
autoOutPath(autoPalettes, options.palettes, ".pal");
|
||||
autoOutPath(autoPalmap, options.palmap, ".palmap");
|
||||
autoOutPath(localOptions.autoAttrmap, options.attrmap, ".attrmap");
|
||||
autoOutPath(localOptions.autoTilemap, options.tilemap, ".tilemap");
|
||||
autoOutPath(localOptions.autoPalettes, options.palettes, ".pal");
|
||||
autoOutPath(localOptions.autoPalmap, options.palmap, ".palmap");
|
||||
|
||||
// Execute deferred external pal spec parsing, now that all other params are known
|
||||
if (externalPalSpec) {
|
||||
parseExternalPalSpec(externalPalSpec);
|
||||
if (localOptions.externalPalSpec) {
|
||||
parseExternalPalSpec(localOptions.externalPalSpec);
|
||||
}
|
||||
|
||||
if (options.verbosity >= Options::VERB_CFG) {
|
||||
|
||||
Reference in New Issue
Block a user