diff --git a/include/gfx/main.hpp b/include/gfx/main.hpp index a85b929f..7a030917 100644 --- a/include/gfx/main.hpp +++ b/include/gfx/main.hpp @@ -67,8 +67,21 @@ struct Options { extern Options options; +/** + * Prints the error count, and exits with failure + */ +[[noreturn]] void giveUp(); +/** + * Prints a warning, and does not change the error count + */ void warning(char const *fmt, ...); +/** + * Prints an error, and increments the error count + */ void error(char const *fmt, ...); +/** + * Prints a fatal error, increments the error count, and gives up + */ [[noreturn]] void fatal(char const *fmt, ...); struct Palette { diff --git a/src/gfx/main.cpp b/src/gfx/main.cpp index 092282df..cacb8553 100644 --- a/src/gfx/main.cpp +++ b/src/gfx/main.cpp @@ -37,6 +37,11 @@ Options options; char const *externalPalSpec = nullptr; static uintmax_t nbErrors; +[[noreturn]] void giveUp() { + fprintf(stderr, "Conversion aborted after %ju error%s\n", nbErrors, nbErrors == 1 ? "" : "s"); + exit(1); +} + void warning(char const *fmt, ...) { va_list ap; @@ -72,8 +77,7 @@ void error(char const *fmt, ...) { if (nbErrors != std::numeric_limits::max()) nbErrors++; - fprintf(stderr, "Conversion aborted after %ju error%s\n", nbErrors, nbErrors == 1 ? "" : "s"); - exit(1); + giveUp(); } void Options::verbosePrint(uint8_t level, char const *fmt, ...) const { @@ -718,9 +722,7 @@ int main(int argc, char *argv[]) { // Do not do anything if option parsing went wrong if (nbErrors) { - fprintf(stderr, "Conversion aborted after %ju error%s\n", nbErrors, - nbErrors == 1 ? "" : "s"); - return 1; + giveUp(); } if (options.reverse()) { @@ -730,9 +732,7 @@ int main(int argc, char *argv[]) { } if (nbErrors) { - fprintf(stderr, "Conversion aborted after %ju error%s\n", nbErrors, - nbErrors == 1 ? "" : "s"); - return 1; + giveUp(); } return 0; } diff --git a/src/gfx/process.cpp b/src/gfx/process.cpp index a857c688..07a7e6f3 100644 --- a/src/gfx/process.cpp +++ b/src/gfx/process.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -557,8 +558,6 @@ static std::tuple, std::vector> // Fill in the palette spec options.palSpec.emplace_back(); // A single palette, with `#00000000`s (transparent) assert(options.palSpec.size() == 1); - // TODO: abort if ignored colors are being used; do it now for a friendlier error - // message if (embPalSize > options.maxOpaqueColors()) { // Ignore extraneous colors if they are unused embPalSize = options.maxOpaqueColors(); } @@ -571,13 +570,24 @@ static std::tuple, std::vector> // Convert the palette spec to actual palettes std::vector palettes(options.palSpec.size()); for (auto [spec, pal] : zip(options.palSpec, palettes)) { - for (size_t i = 0; i < options.nbColorsPerPal; ++i) { + for (size_t i = 0; i < options.nbColorsPerPal && spec[i].isOpaque(); ++i) { pal[i] = spec[i].cgbColor(); } } + auto listColors = [](auto const &list) { + static char buf[sizeof(", $XXXX, $XXXX, $XXXX, $XXXX")]; + char *ptr = buf; + for (uint16_t cgbColor : list) { + sprintf(ptr, ", $%04x", cgbColor); + ptr += 7; + } + return &buf[2]; + }; + // Iterate through proto-palettes, and try mapping them to the specified palettes DefaultInitVec mappings(protoPalettes.size()); + bool bad = false; for (size_t i = 0; i < protoPalettes.size(); ++i) { ProtoPalette const &protoPal = protoPalettes[i]; // Find the palette... @@ -587,8 +597,21 @@ static std::tuple, std::vector> return std::find(pal.begin(), pal.end(), color) != pal.end(); }); }); - assert(iter != palettes.end()); // TODO: produce a proper error message - mappings[i] = iter - palettes.begin(); + + if (iter == palettes.end()) { + assert(!protoPal.empty()); + error("Could not fit tile colors [%s] in specified palettes", listColors(protoPal)); + bad = true; + } + mappings[i] = iter - palettes.begin(); // Bogus value, but whatever + } + if (bad) { + fprintf(stderr, "note: The following palette%s specified:\n", + palettes.size() == 1 ? " was" : "s were"); + for (Palette const &pal : palettes) { + fprintf(stderr, " [%s]\n", listColors(pal)); + } + giveUp(); } return {mappings, palettes}; diff --git a/test/gfx/bad_manual_pals.err b/test/gfx/bad_manual_pals.err new file mode 100644 index 00000000..100436c6 --- /dev/null +++ b/test/gfx/bad_manual_pals.err @@ -0,0 +1,6 @@ +error: Could not fit tile colors [$6c8a, $7f55, $7fff] in specified palettes +error: Could not fit tile colors [$6c8a, $7f55] in specified palettes +note: The following palettes were specified: + [$7fff, $7f55] + [$7fff, $6c8a] +Conversion aborted after 2 errors diff --git a/test/gfx/bad_manual_pals.flags b/test/gfx/bad_manual_pals.flags new file mode 100644 index 00000000..e50cdf66 --- /dev/null +++ b/test/gfx/bad_manual_pals.flags @@ -0,0 +1 @@ +-c #fff,#a9d4fe:#fff,#5721d9 diff --git a/test/gfx/bad_manual_pals.png b/test/gfx/bad_manual_pals.png new file mode 100644 index 00000000..57fdd36f Binary files /dev/null and b/test/gfx/bad_manual_pals.png differ