mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 18:22:07 +00:00
Check RGBGFX warning/error format strings with format_ macro
This commit is contained in:
@@ -72,15 +72,21 @@ extern Options options;
|
|||||||
/*
|
/*
|
||||||
* Prints a warning, and does not change the error count
|
* Prints a warning, and does not change the error count
|
||||||
*/
|
*/
|
||||||
void warning(char const *fmt, ...);
|
void warning(char const *fmt, ...) format_(printf, 1, 2);
|
||||||
/*
|
/*
|
||||||
* Prints an error, and increments the error count
|
* Prints an error, and increments the error count
|
||||||
*/
|
*/
|
||||||
void error(char const *fmt, ...);
|
void error(char const *fmt, ...) format_(printf, 1, 2);
|
||||||
|
/*
|
||||||
|
* Prints an error, and increments the error count
|
||||||
|
* Does not take format arguments so `format_` and `-Wformat-security` won't complain about
|
||||||
|
* calling `errorMessage(msg)`.
|
||||||
|
*/
|
||||||
|
void errorMessage(char const *msg);
|
||||||
/*
|
/*
|
||||||
* Prints a fatal error, increments the error count, and gives up
|
* Prints a fatal error, increments the error count, and gives up
|
||||||
*/
|
*/
|
||||||
[[noreturn]] void fatal(char const *fmt, ...);
|
[[noreturn]] void fatal(char const *fmt, ...) format_(printf, 1, 2);
|
||||||
|
|
||||||
struct Palette {
|
struct Palette {
|
||||||
// An array of 4 GBC-native (RGB555) colors
|
// An array of 4 GBC-native (RGB555) colors
|
||||||
|
|||||||
@@ -72,6 +72,13 @@ void error(char const *fmt, ...) {
|
|||||||
nbErrors++;
|
nbErrors++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void errorMessage(char const *msg) {
|
||||||
|
fprintf(stderr, "error: %s\n", msg);
|
||||||
|
|
||||||
|
if (nbErrors != std::numeric_limits<decltype(nbErrors)>::max())
|
||||||
|
nbErrors++;
|
||||||
|
}
|
||||||
|
|
||||||
[[noreturn]] void fatal(char const *fmt, ...) {
|
[[noreturn]] void fatal(char const *fmt, ...) {
|
||||||
va_list ap;
|
va_list ap;
|
||||||
|
|
||||||
@@ -334,7 +341,7 @@ static std::vector<size_t> readAtFile(std::string const &path, std::vector<char>
|
|||||||
* Returns `nullptr` if the vector was fully parsed, or a pointer (which is part of the arg vector)
|
* Returns `nullptr` 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.
|
* to an "at-file" path if one is encountered.
|
||||||
*/
|
*/
|
||||||
static char *parseArgv(int argc, char **argv) {
|
static char *parseArgv(int argc, char *argv[]) {
|
||||||
for (int ch; (ch = musl_getopt_long_only(argc, argv, optstring, longopts, nullptr)) != -1;) {
|
for (int ch; (ch = musl_getopt_long_only(argc, argv, optstring, longopts, nullptr)) != -1;) {
|
||||||
char *arg = musl_optarg; // Make a copy for scanning
|
char *arg = musl_optarg; // Make a copy for scanning
|
||||||
uint16_t number;
|
uint16_t number;
|
||||||
@@ -406,7 +413,7 @@ static char *parseArgv(int argc, char **argv) {
|
|||||||
if (*arg != '\0') {
|
if (*arg != '\0') {
|
||||||
error("Bit depth (-b) argument must be a valid number, not \"%s\"", musl_optarg);
|
error("Bit depth (-b) argument must be a valid number, not \"%s\"", musl_optarg);
|
||||||
} else if (options.bitDepth != 1 && options.bitDepth != 2) {
|
} else if (options.bitDepth != 1 && options.bitDepth != 2) {
|
||||||
error("Bit depth must be 1 or 2, not %" PRIu8);
|
error("Bit depth must be 1 or 2, not %" PRIu8, options.bitDepth);
|
||||||
options.bitDepth = 2;
|
options.bitDepth = 2;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -59,13 +59,12 @@ void parseInlinePalSpec(char const * const rawArg) {
|
|||||||
std::string_view arg(rawArg);
|
std::string_view arg(rawArg);
|
||||||
using size_type = decltype(arg)::size_type;
|
using size_type = decltype(arg)::size_type;
|
||||||
|
|
||||||
auto parseError = [&rawArg, &arg](size_type ofs, size_type len, char const *fmt,
|
auto parseError = [&rawArg, &arg](size_type ofs, size_type len, char const *msg) {
|
||||||
auto &&...args) {
|
|
||||||
(void)arg; // With NDEBUG, `arg` is otherwise not used
|
(void)arg; // With NDEBUG, `arg` is otherwise not used
|
||||||
assert(ofs <= arg.length());
|
assert(ofs <= arg.length());
|
||||||
assert(len <= arg.length());
|
assert(len <= arg.length());
|
||||||
|
|
||||||
error(fmt, args...);
|
errorMessage(msg);
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"In inline palette spec: %s\n"
|
"In inline palette spec: %s\n"
|
||||||
" ",
|
" ",
|
||||||
@@ -237,31 +236,31 @@ static std::optional<Rgba> parseColor(std::string const &str, std::string::size_
|
|||||||
uint16_t i) {
|
uint16_t i) {
|
||||||
std::optional<uint8_t> r = parseDec<uint8_t>(str, n);
|
std::optional<uint8_t> r = parseDec<uint8_t>(str, n);
|
||||||
if (!r) {
|
if (!r) {
|
||||||
error("Failed to parse color #%" PRIu16 " (\"%s\"): invalid red component", i + 1,
|
error("Failed to parse color #%d (\"%s\"): invalid red component", i + 1,
|
||||||
str.c_str());
|
str.c_str());
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
skipWhitespace(str, n);
|
skipWhitespace(str, n);
|
||||||
if (n == str.length()) {
|
if (n == str.length()) {
|
||||||
error("Failed to parse color #%" PRIu16 " (\"%s\"): missing green component", i + 1,
|
error("Failed to parse color #%d (\"%s\"): missing green component", i + 1,
|
||||||
str.c_str());
|
str.c_str());
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
std::optional<uint8_t> g = parseDec<uint8_t>(str, n);
|
std::optional<uint8_t> g = parseDec<uint8_t>(str, n);
|
||||||
if (!g) {
|
if (!g) {
|
||||||
error("Failed to parse color #%" PRIu16 " (\"%s\"): invalid green component", i + 1,
|
error("Failed to parse color #%d (\"%s\"): invalid green component", i + 1,
|
||||||
str.c_str());
|
str.c_str());
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
skipWhitespace(str, n);
|
skipWhitespace(str, n);
|
||||||
if (n == str.length()) {
|
if (n == str.length()) {
|
||||||
error("Failed to parse color #%" PRIu16 " (\"%s\"): missing blue component", i + 1,
|
error("Failed to parse color #%d (\"%s\"): missing blue component", i + 1,
|
||||||
str.c_str());
|
str.c_str());
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
std::optional<uint8_t> b = parseDec<uint8_t>(str, n);
|
std::optional<uint8_t> b = parseDec<uint8_t>(str, n);
|
||||||
if (!b) {
|
if (!b) {
|
||||||
error("Failed to parse color #%" PRIu16 " (\"%s\"): invalid blue component", i + 1,
|
error("Failed to parse color #%d (\"%s\"): invalid blue component", i + 1,
|
||||||
str.c_str());
|
str.c_str());
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
@@ -295,11 +294,11 @@ static void parsePSPFile(std::filebuf &file) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*nbColors > options.nbColorsPerPal * options.nbPalettes) {
|
if (uint16_t nbPalColors = options.nbColorsPerPal * options.nbPalettes; *nbColors > nbPalColors) {
|
||||||
warning("PSP file contains %" PRIu16 " colors, but there can only be %" PRIu16
|
warning("PSP file contains %" PRIu16 " colors, but there can only be %" PRIu16
|
||||||
"; ignoring extra",
|
"; ignoring extra",
|
||||||
*nbColors, options.nbColorsPerPal * options.nbPalettes);
|
*nbColors, nbPalColors);
|
||||||
nbColors = options.nbColorsPerPal * options.nbPalettes;
|
nbColors = nbPalColors;
|
||||||
}
|
}
|
||||||
|
|
||||||
options.palSpec.clear();
|
options.palSpec.clear();
|
||||||
@@ -314,8 +313,7 @@ static void parsePSPFile(std::filebuf &file) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (n != line.length()) {
|
if (n != line.length()) {
|
||||||
error("Failed to parse color #%" PRIu16
|
error("Failed to parse color #%d (\"%s\"): trailing characters after blue component",
|
||||||
" (\"%s\"): trailing characters after blue component",
|
|
||||||
i + 1, line.c_str());
|
i + 1, line.c_str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -388,7 +386,7 @@ static void parseHEXFile(std::filebuf &file) {
|
|||||||
|
|
||||||
if (line.length() != 6
|
if (line.length() != 6
|
||||||
|| line.find_first_not_of("0123456789ABCDEFabcdef"sv) != std::string::npos) {
|
|| line.find_first_not_of("0123456789ABCDEFabcdef"sv) != std::string::npos) {
|
||||||
error("Failed to parse color #%" PRIu16 " (\"%s\"): invalid \"rrggbb\" line",
|
error("Failed to parse color #%d (\"%s\"): invalid \"rrggbb\" line",
|
||||||
nbColors + 1, line.c_str());
|
nbColors + 1, line.c_str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -431,11 +429,11 @@ static void parseACTFile(std::filebuf &file) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nbColors > options.nbColorsPerPal * options.nbPalettes) {
|
if (uint16_t nbPalColors = options.nbColorsPerPal * options.nbPalettes; nbColors > nbPalColors) {
|
||||||
warning("ACT file contains %" PRIu16 " colors, but there can only be %" PRIu16
|
warning("ACT file contains %" PRIu16 " colors, but there can only be %" PRIu16
|
||||||
"; ignoring extra",
|
"; ignoring extra",
|
||||||
nbColors, options.nbColorsPerPal * options.nbPalettes);
|
nbColors, nbPalColors);
|
||||||
nbColors = options.nbColorsPerPal * options.nbPalettes;
|
nbColors = nbPalColors;
|
||||||
}
|
}
|
||||||
|
|
||||||
options.palSpec.clear();
|
options.palSpec.clear();
|
||||||
@@ -481,18 +479,18 @@ static void parseACOFile(std::filebuf &file) {
|
|||||||
}
|
}
|
||||||
uint16_t nbColors = readBE<uint16_t>(buf);
|
uint16_t nbColors = readBE<uint16_t>(buf);
|
||||||
|
|
||||||
if (nbColors > options.nbColorsPerPal * options.nbPalettes) {
|
if (uint16_t nbPalColors = options.nbColorsPerPal * options.nbPalettes; nbColors > nbPalColors) {
|
||||||
warning("ACO file contains %" PRIu16 " colors, but there can only be %" PRIu16
|
warning("ACO file contains %" PRIu16 " colors, but there can only be %" PRIu16
|
||||||
"; ignoring extra",
|
"; ignoring extra",
|
||||||
nbColors, options.nbColorsPerPal * options.nbPalettes);
|
nbColors, nbPalColors);
|
||||||
nbColors = options.nbColorsPerPal * options.nbPalettes;
|
nbColors = nbPalColors;
|
||||||
}
|
}
|
||||||
|
|
||||||
options.palSpec.clear();
|
options.palSpec.clear();
|
||||||
|
|
||||||
for (uint16_t i = 0; i < nbColors; ++i) {
|
for (uint16_t i = 0; i < nbColors; ++i) {
|
||||||
if (file.sgetn(buf, 10) != 10) {
|
if (file.sgetn(buf, 10) != 10) {
|
||||||
error("Failed to read color #%" PRIu16 " from palette file", i + 1);
|
error("Failed to read color #%d from palette file", i + 1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -106,9 +106,9 @@ class Png {
|
|||||||
|
|
||||||
if (nbBytesRead != expectedLen) {
|
if (nbBytesRead != expectedLen) {
|
||||||
fatal("Error reading input image (\"%s\"): file too short (expected at least %zd more "
|
fatal("Error reading input image (\"%s\"): file too short (expected at least %zd more "
|
||||||
"bytes after reading %lld)",
|
"bytes after reading %zu)",
|
||||||
self->c_str(), length - nbBytesRead,
|
self->c_str(), length - nbBytesRead,
|
||||||
self->file->pubseekoff(0, std::ios_base::cur));
|
(size_t)self->file->pubseekoff(0, std::ios_base::cur));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1060,7 +1060,7 @@ void process() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (nbColorsInTile > options.maxOpaqueColors()) {
|
if (nbColorsInTile > options.maxOpaqueColors()) {
|
||||||
fatal("Tile at (%" PRIu32 ", %" PRIu32 ") has %zu opaque colors, more than %" PRIu8 "!",
|
fatal("Tile at (%" PRIu32 ", %" PRIu32 ") has %" PRIu8 " opaque colors, more than %" PRIu8 "!",
|
||||||
tile.x, tile.y, nbColorsInTile, options.maxOpaqueColors());
|
tile.x, tile.y, nbColorsInTile, options.maxOpaqueColors());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ void reverse() {
|
|||||||
}
|
}
|
||||||
if (options.inputSlice.width != 0 && options.inputSlice.width != options.reversedWidth * 8) {
|
if (options.inputSlice.width != 0 && options.inputSlice.width != options.reversedWidth * 8) {
|
||||||
warning("Specified input slice width (%" PRIu16
|
warning("Specified input slice width (%" PRIu16
|
||||||
") doesn't match provided reversing width (%" PRIu8 " * 8)",
|
") doesn't match provided reversing width (%" PRIu16 " * 8)",
|
||||||
options.inputSlice.width, options.reversedWidth);
|
options.inputSlice.width, options.reversedWidth);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,8 +121,8 @@ void reverse() {
|
|||||||
fatal("Cannot generate empty image");
|
fatal("Cannot generate empty image");
|
||||||
}
|
}
|
||||||
if (nbTileInstances > options.maxNbTiles[0] + options.maxNbTiles[1]) {
|
if (nbTileInstances > options.maxNbTiles[0] + options.maxNbTiles[1]) {
|
||||||
warning("Read %zu tiles, more than the limit of %zu + %zu", nbTileInstances,
|
warning("Read %zu tiles, more than the limit of %" PRIu16 " + %" PRIu16,
|
||||||
options.maxNbTiles[0], options.maxNbTiles[1]);
|
nbTileInstances, options.maxNbTiles[0], options.maxNbTiles[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t width = options.reversedWidth, height; // In tiles
|
size_t width = options.reversedWidth, height; // In tiles
|
||||||
@@ -167,8 +167,8 @@ void reverse() {
|
|||||||
} while (nbRead != 0);
|
} while (nbRead != 0);
|
||||||
|
|
||||||
if (palettes.size() > options.nbPalettes) {
|
if (palettes.size() > options.nbPalettes) {
|
||||||
warning("Read %zu palettes, more than the specified limit of %zu", palettes.size(),
|
warning("Read %zu palettes, more than the specified limit of %" PRIu8,
|
||||||
options.nbPalettes);
|
palettes.size(), options.nbPalettes);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.palSpecType == Options::EXPLICIT && palettes != options.palSpec) {
|
if (options.palSpecType == Options::EXPLICIT && palettes != options.palSpec) {
|
||||||
@@ -195,7 +195,8 @@ void reverse() {
|
|||||||
bool bad = false;
|
bool bad = false;
|
||||||
for (auto attr : *attrmap) {
|
for (auto attr : *attrmap) {
|
||||||
if ((attr & 0b111) > palettes.size()) {
|
if ((attr & 0b111) > palettes.size()) {
|
||||||
error("Referencing palette %u, but there are only %zu!");
|
error("Referencing palette %u, but there are only %zu!",
|
||||||
|
attr & 0b111, palettes.size());
|
||||||
bad = true;
|
bad = true;
|
||||||
}
|
}
|
||||||
if (attr & 0x08 && !tilemap) {
|
if (attr & 0x08 && !tilemap) {
|
||||||
|
|||||||
@@ -260,7 +260,7 @@ static void generate_random_image(char const *filename) {
|
|||||||
write_image(filename, palettes, tileData, attributes, width, height);
|
write_image(filename, palettes, tileData, attributes, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char *argv[]) {
|
||||||
if (argc < 3 || argc > 4) {
|
if (argc < 3 || argc > 4) {
|
||||||
fprintf(stderr, "usage: %s <input file> <basename> [<maxcount>]\n", argv[0]);
|
fprintf(stderr, "usage: %s <input file> <basename> [<maxcount>]\n", argv[0]);
|
||||||
return 2;
|
return 2;
|
||||||
|
|||||||
@@ -112,9 +112,9 @@ class Png {
|
|||||||
|
|
||||||
if (nbBytesRead != expectedLen) {
|
if (nbBytesRead != expectedLen) {
|
||||||
fatal("Error reading input image (\"%s\"): file too short (expected at least %zd more "
|
fatal("Error reading input image (\"%s\"): file too short (expected at least %zd more "
|
||||||
"bytes after reading %lld)",
|
"bytes after reading %zu)",
|
||||||
self->path.c_str(), length - nbBytesRead,
|
self->path.c_str(), length - nbBytesRead,
|
||||||
self->file.pubseekoff(0, std::ios_base::cur));
|
(size_t)self->file.pubseekoff(0, std::ios_base::cur));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -369,7 +369,7 @@ static char *execProg(char const *name, char * const *argv) {
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char *argv[]) {
|
||||||
if (argc < 2) {
|
if (argc < 2) {
|
||||||
fprintf(stderr, "usage: %s <rng file> [rgbgfx flags]\n", argv[0]);
|
fprintf(stderr, "usage: %s <rng file> [rgbgfx flags]\n", argv[0]);
|
||||||
exit(0);
|
exit(0);
|
||||||
|
|||||||
Reference in New Issue
Block a user