From e6e5133bcfb1d1466cf7a50c2e3f59b1b7717c0e Mon Sep 17 00:00:00 2001 From: Rangi <35663410+Rangi42@users.noreply.github.com> Date: Sat, 26 Sep 2026 13:13:34 -0400 Subject: [PATCH 1/6] RGBGFX honors `-c/--colors` in `-r/--reverse` mode (#2144) f4463b17 (#1254) was an incomplete fix for #1166 --- src/gfx/reverse.cpp | 35 +++++++++++++++++---------- test/gfx/reverse_inline_palette.2bpp | 1 + test/gfx/reverse_inline_palette.flags | 1 + 3 files changed, 24 insertions(+), 13 deletions(-) create mode 100644 test/gfx/reverse_inline_palette.2bpp create mode 100644 test/gfx/reverse_inline_palette.flags diff --git a/src/gfx/reverse.cpp b/src/gfx/reverse.cpp index 8a353108..e6d92289 100644 --- a/src/gfx/reverse.cpp +++ b/src/gfx/reverse.cpp @@ -205,6 +205,7 @@ void reverse() { {grayColors[0], grayColors[1], grayColors[2], grayColors[3]} }; // If a palette file or palette spec is used as input, it overrides the default colors. + bool grayscale = false; if (!options.palettes.empty()) { File file; if (!file.open(options.palettes, std::ios::in | std::ios::binary)) { @@ -280,11 +281,15 @@ void reverse() { for (size_t i = 0; i < palettes[0].size(); ++i) { palettes[0][i] = grayColors[options.dmgValue(i)]; } + grayscale = true; } else if (options.palSpecType == Options::EMBEDDED) { warnx("An embedded palette was requested, but no palette file was specified; ignoring " "request"); + grayscale = true; } else if (options.hasExplicitPalSpec()) { palettes = std::move(options.palSpec); // We won't be using it again. + } else { + grayscale = true; } std::optional> attrmap; @@ -497,10 +502,10 @@ void reverse() { } png_set_write_fn(png, &pngFile, writePng, flushPng); - int pngColorType = options.palettes.empty() ? PNG_COLOR_TYPE_GRAY - : palettes.size() == 1 ? PNG_COLOR_TYPE_PALETTE - : PNG_COLOR_TYPE_RGB_ALPHA; - int pngDepth = options.palettes.empty() ? options.bitDepth : 8; + int pngColorType = grayscale ? PNG_COLOR_TYPE_GRAY + : palettes.size() == 1 ? PNG_COLOR_TYPE_PALETTE + : PNG_COLOR_TYPE_RGB_ALPHA; + int pngDepth = grayscale ? options.bitDepth : 8; png_set_IHDR( png, @@ -531,17 +536,21 @@ void reverse() { png_byte pngTrans[4] = {}; int nbPngColors = 0, nbPngTrans = 0; for (auto const &color : palettes[0]) { - if (!color.has_value()) { - continue; + if (color.has_value()) { + pngPalette[nbPngColors].red = color->red; + pngPalette[nbPngColors].green = color->green; + pngPalette[nbPngColors].blue = color->blue; + pngTrans[nbPngColors] = color->alpha; + if (color->alpha < 255) { + nbPngTrans = nbPngColors; + } + } else { + pngPalette[nbPngColors].red = 255; + pngPalette[nbPngColors].green = 255; + pngPalette[nbPngColors].blue = 255; + pngTrans[nbPngColors] = 255; } - pngPalette[nbPngColors].red = color->red; - pngPalette[nbPngColors].green = color->green; - pngPalette[nbPngColors].blue = color->blue; - pngTrans[nbPngColors] = color->alpha; ++nbPngColors; - if (color->alpha < 255) { - nbPngTrans = nbPngColors; - } } png_set_PLTE(png, pngInfo, pngPalette, nbPngColors); if (nbPngTrans > 0) { diff --git a/test/gfx/reverse_inline_palette.2bpp b/test/gfx/reverse_inline_palette.2bpp new file mode 100644 index 00000000..a38193d0 --- /dev/null +++ b/test/gfx/reverse_inline_palette.2bpp @@ -0,0 +1 @@ +U3U3U3U3U3U3U3U3 \ No newline at end of file diff --git a/test/gfx/reverse_inline_palette.flags b/test/gfx/reverse_inline_palette.flags new file mode 100644 index 00000000..75e5e5f1 --- /dev/null +++ b/test/gfx/reverse_inline_palette.flags @@ -0,0 +1 @@ +-c #ff0000,#00ff00,#0000ff,#ffffff From fd7715dcb051bd591d93c9dab71d6b742729cdcd Mon Sep 17 00:00:00 2001 From: Rangi Date: Fri, 25 Sep 2026 23:02:38 -0400 Subject: [PATCH 2/6] Fix inconsistent handling of transparent colors in the middle of palettes --- include/gfx/pal_spec.hpp | 4 ++ include/gfx/process.hpp | 4 +- src/gfx/main.cpp | 36 +++++++++++--- src/gfx/pal_spec.cpp | 23 +++++++++ src/gfx/process.cpp | 49 +++---------------- src/gfx/reverse.cpp | 2 - test/gfx/alpha_embedded.err | 4 -- test/gfx/alpha_embedded.out.2bpp | Bin 0 -> 192 bytes test/gfx/alpha_embedded.out.pal | Bin 0 -> 8 bytes test/gfx/middle_transparent_embedded.err | 2 + test/gfx/middle_transparent_embedded.flags | 1 + test/gfx/middle_transparent_embedded.png | Bin 0 -> 135 bytes test/gfx/middle_transparent_gbc_spec.err | 2 + test/gfx/middle_transparent_gbc_spec.flags | 1 + test/gfx/middle_transparent_gbc_spec.pal | Bin 0 -> 8 bytes test/gfx/middle_transparent_gbc_spec.png | Bin 0 -> 135 bytes test/gfx/middle_transparent_png_spec.err | 2 + test/gfx/middle_transparent_png_spec.flags | 1 + test/gfx/middle_transparent_png_spec.pal.png | Bin 0 -> 361 bytes test/gfx/middle_transparent_png_spec.png | Bin 0 -> 135 bytes 20 files changed, 75 insertions(+), 56 deletions(-) delete mode 100644 test/gfx/alpha_embedded.err create mode 100644 test/gfx/alpha_embedded.out.2bpp create mode 100644 test/gfx/alpha_embedded.out.pal create mode 100644 test/gfx/middle_transparent_embedded.err create mode 100644 test/gfx/middle_transparent_embedded.flags create mode 100644 test/gfx/middle_transparent_embedded.png create mode 100644 test/gfx/middle_transparent_gbc_spec.err create mode 100644 test/gfx/middle_transparent_gbc_spec.flags create mode 100644 test/gfx/middle_transparent_gbc_spec.pal create mode 100644 test/gfx/middle_transparent_gbc_spec.png create mode 100644 test/gfx/middle_transparent_png_spec.err create mode 100644 test/gfx/middle_transparent_png_spec.flags create mode 100644 test/gfx/middle_transparent_png_spec.pal.png create mode 100644 test/gfx/middle_transparent_png_spec.png diff --git a/include/gfx/pal_spec.hpp b/include/gfx/pal_spec.hpp index d19d7fe3..6cf3dcd8 100644 --- a/include/gfx/pal_spec.hpp +++ b/include/gfx/pal_spec.hpp @@ -4,8 +4,12 @@ #define RGBDS_GFX_PAL_SPEC_HPP #include +#include + +#include "gfx/png.hpp" void parseInlinePalSpec(char const * const rawArg); +void parseEmbeddedPalSpec(Png const &png); void parseExternalPalSpec(char const *arg); void parseDmgPalSpec(char const * const rawArg); void parseBackgroundPalSpec(char const *arg); diff --git a/include/gfx/process.hpp b/include/gfx/process.hpp index d0105ff2..afa5955e 100644 --- a/include/gfx/process.hpp +++ b/include/gfx/process.hpp @@ -3,7 +3,9 @@ #ifndef RGBDS_GFX_PROCESS_HPP #define RGBDS_GFX_PROCESS_HPP +#include "gfx/png.hpp" + void processPalettes(); -void process(); +void process(Png &&png); #endif // RGBDS_GFX_PROCESS_HPP diff --git a/src/gfx/main.cpp b/src/gfx/main.cpp index b807e12f..a2f8a82e 100644 --- a/src/gfx/main.cpp +++ b/src/gfx/main.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -479,6 +480,7 @@ static void parseArg(int ch, char *arg) { // LCOV_EXCL_START static void verboseOutputConfig() { usage.printVersion(true); + fprintf(stderr, "Using libpng %s\n", png_get_libpng_ver(nullptr)); printVVVVVVerbosity(); @@ -524,12 +526,18 @@ static void verboseOutputConfig() { break; case Options::INLINE: case Options::EXTERNAL: - fputs("\tExplicit palette spec\n\t[\n", stderr); + case Options::EMBEDDED: + if (options.palSpecType == Options::EMBEDDED) { + fputs("\tEmbedded palette spec from PNG indexed PLTE chunk\n", stderr); + } else { + fputs("\tExplicit palette spec\n", stderr); + } + fputs("\t[\n", stderr); for (auto const &pal : options.palSpec) { fputs("\t\t", stderr); for (auto const &color : pal) { if (color.has_value()) { - fprintf(stderr, "#%06x, ", color->toCSS() >> 8); + fprintf(stderr, "#%08x, ", color->toCSS()); } else { fputs("#none, ", stderr); } @@ -538,9 +546,6 @@ static void verboseOutputConfig() { } fputs("\t]\n", stderr); break; - case Options::EMBEDDED: - fputs("\tEmbedded palette spec from PNG indexed PLTE chunk\n", stderr); - break; case Options::DMG: fprintf(stderr, "\tDMG palette spec $%02" PRIx8 "\n", options.palSpecDmg); break; @@ -671,13 +676,32 @@ int main(int argc, char *argv[]) { autoOutPath(localOptions.autoPalettes, options.palettes, ".pal"); autoOutPath(localOptions.autoPalmap, options.palmap, ".palmap"); + // Read the input image now since it may be needed for parsing an embedded palette spec + bool useInputImage = !options.input.empty() && !localOptions.reverse; + Png png; + if (useInputImage) { + if (File image; + image.open(options.input, std::ios_base::in | std::ios_base::binary) == nullptr) { + fatal( + "Failed to open PNG image (\"%s\"): %s", image.c_str(options.input), strerror(errno) + ); + } else { + png = Png(image.c_str(options.input), *image); + } + } + // Execute deferred pal spec parsing, now that all other params are known. // Do not parse pal specs if `options.nbColorsPerPal` is invalid. if (options.nbColorsPerPal > 0 && options.nbColorsPerPal <= 4) { switch (options.palSpecType) { case Options::NO_SPEC: + assume(!localOptions.palSpec); + break; case Options::EMBEDDED: assume(!localOptions.palSpec); + if (useInputImage) { + parseEmbeddedPalSpec(png); + } break; case Options::INLINE: assume(localOptions.palSpec); @@ -703,7 +727,7 @@ int main(int argc, char *argv[]) { if (localOptions.reverse) { reverse(); } else { - process(); + process(std::move(png)); } } else if (!options.palettes.empty() && options.hasExplicitPalSpec() && !localOptions.reverse) { processPalettes(); diff --git a/src/gfx/pal_spec.cpp b/src/gfx/pal_spec.cpp index 045881c4..40f4bf2e 100644 --- a/src/gfx/pal_spec.cpp +++ b/src/gfx/pal_spec.cpp @@ -20,6 +20,7 @@ #include #include "diagnostics.hpp" +#include "file.hpp" #include "helpers.hpp" #include "platform.hpp" #include "util.hpp" // UpperMap, parseDigit @@ -174,6 +175,28 @@ void parseInlinePalSpec(char const * const rawArg) { } } +void parseEmbeddedPalSpec(Png const &png) { + // Generate a palette spec from one palette's length of colors in the embedded palette + if (png.palette.empty()) { + error("\"-c embedded\" was given, but the PNG does not have an embedded palette"); + return; + } + + // Ignore extraneous colors if they are unused + size_t nbColors = png.palette.size(); + if (nbColors > options.maxOpaqueColors()) { + nbColors = options.maxOpaqueColors(); + } + + // Fill in the palette spec + options.palSpec.clear(); + auto &palette = options.palSpec.emplace_back(); + assume(nbColors <= palette.size()); + for (size_t i = 0; i < nbColors; ++i) { + palette[i] = png.palette[i]; + } +} + // Appends the first line read from `file` to the end of the provided `buffer`. // Returns true if a line was read. [[nodiscard]] diff --git a/src/gfx/process.cpp b/src/gfx/process.cpp index 318445ec..5cee407a 100644 --- a/src/gfx/process.cpp +++ b/src/gfx/process.cpp @@ -8,7 +8,6 @@ #include #include #include -#include #include #include #include @@ -77,8 +76,8 @@ public: }; struct Image { - Png png{}; - ImagePalette colors{}; + Png png; + ImagePalette colors; Rgba &pixel(uint32_t x, uint32_t y) { return png.pixels[y * png.width + x]; } Rgba const &pixel(uint32_t x, uint32_t y) const { return png.pixels[y * png.width + x]; } @@ -127,14 +126,7 @@ struct Image { return {GrayscaleResult::GRAY_OK, std::nullopt}; } - explicit Image(std::string const &path) { - File input; - if (input.open(path, std::ios_base::in | std::ios_base::binary) == nullptr) { - fatal("Failed to open input image (\"%s\"): %s", input.c_str(path), strerror(errno)); - } - - png = Png(input.c_str(path), *input); - + explicit Image(Png &&png_) : png(std::move(png_)), colors() { // Validate input slice if (options.inputSlice.width == 0 && png.width % 8 != 0) { fatal("Image width (%" PRIu32 " pixels) is not a multiple of 8", png.width); @@ -332,28 +324,6 @@ struct AttrmapEntry { } }; -static void generatePalSpec(Image const &image) { - // Generate a palette spec from the first few colors in the embedded palette - std::vector const &embPal = image.png.palette; - if (embPal.empty()) { - fatal("\"-c embedded\" was given, but the PNG does not have an embedded palette"); - } - - // Ignore extraneous colors if they are unused - size_t nbColors = embPal.size(); - if (nbColors > options.maxOpaqueColors()) { - nbColors = options.maxOpaqueColors(); - } - - // Fill in the palette spec - options.palSpec.clear(); - auto &palette = options.palSpec.emplace_back(); - assume(nbColors <= palette.size()); - for (size_t i = 0; i < nbColors; ++i) { - palette[i] = embPal[i]; - } -} - static std::pair, std::vector> generatePalettes(std::vector const &colorSets, Image const &image) { // Run a "pagination" problem solver @@ -450,7 +420,7 @@ static std::pair, std::vector> if (i + skipFirst >= options.nbColorsPerPal) { error( "Each palette spec can only contain up to %" PRIu8 - " color%s plus the implict transparent color", + " color%s plus the implicit transparent color", options.nbColorsPerPal - 1, options.nbColorsPerPal - 1 == 1 ? "" : "s" ); @@ -992,8 +962,6 @@ static void } void processPalettes() { - verbosePrint(VERB_CONFIG, "Using libpng %s\n", png_get_libpng_ver(nullptr)); - std::vector colorSets; std::vector palettes; std::tie(std::ignore, palettes) = makePalsAsSpecified(colorSets); @@ -1001,11 +969,9 @@ void processPalettes() { outputPalettes(palettes); } -void process() { - verbosePrint(VERB_CONFIG, "Using libpng %s\n", png_get_libpng_ver(nullptr)); - +void process(Png &&png) { verbosePrint(VERB_NOTICE, "Reading tiles...\n"); - Image image(options.input); // This also sets `hasTransparentPixels` as a side effect + Image image(std::move(png)); // This also sets `hasTransparentPixels` as a side effect // LCOV_EXCL_START verboseDo(VERB_INFO, [&]() { @@ -1195,9 +1161,6 @@ continue_visiting_tiles:; fatal("Image does not contain any colors"); } - if (options.palSpecType == Options::EMBEDDED) { - generatePalSpec(image); - } auto [mappings, palettes] = options.palSpecType == Options::NO_SPEC || options.palSpecType == Options::DMG ? generatePalettes(colorSets, image) diff --git a/src/gfx/reverse.cpp b/src/gfx/reverse.cpp index e6d92289..617504e4 100644 --- a/src/gfx/reverse.cpp +++ b/src/gfx/reverse.cpp @@ -107,8 +107,6 @@ static void printPalette(std::array, 4> const &palette) { } void reverse() { - verbosePrint(VERB_CONFIG, "Using libpng %s\n", png_get_libpng_ver(nullptr)); - // Check for weird flag combinations if (options.output.empty()) { diff --git a/test/gfx/alpha_embedded.err b/test/gfx/alpha_embedded.err deleted file mode 100644 index eb832e43..00000000 --- a/test/gfx/alpha_embedded.err +++ /dev/null @@ -1,4 +0,0 @@ -error: Failed to fit tile colors [GB:07,09,05; GB:12,14,05; GB:20,21,06] in specified palettes -note: The following palette was specified: - - [GB:20,21,06; GB:12,14,05] -Conversion aborted after 1 error diff --git a/test/gfx/alpha_embedded.out.2bpp b/test/gfx/alpha_embedded.out.2bpp new file mode 100644 index 0000000000000000000000000000000000000000..53d788a023e74499292dba42b5f7bbae2da0c09b GIT binary patch literal 192 zcmZw8F%E)25QX8^)b<#05}5;~2A7z(XyD7P?Si%p&@p)`}% zO#V-J-bQT2`gbcq1R%o$o!cECGS-&%XlBSEK+1B}-f*N`mv#O3D+9QW*jgGxJLH{9Hp6O!W-) z42^^t-AjOqcBDplrg?g5F>nAmtPE0&tPG4mmKP99L)jqbXfQH^#hHL?Lq;YBK_DFk z#F_0ZVDTIv8w3)77^ELYqua{Bz|6offgS8U10!Pt#sv^_L3&shK+Kv3WP<<`&?F|X z${@r1LPY=o literal 0 HcmV?d00001 diff --git a/test/gfx/middle_transparent_png_spec.png b/test/gfx/middle_transparent_png_spec.png new file mode 100644 index 0000000000000000000000000000000000000000..8550ee94633cf1e8a536c717fb17e57f23917013 GIT binary patch literal 135 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJV{wqX6T`Z5GB1G~o&cW^*Z)8U z82|szaE+ Date: Fri, 25 Sep 2026 23:09:56 -0400 Subject: [PATCH 3/6] Fix typo ("implict" -> "implicit") --- test/gfx/implicit-overflow.err | 2 +- test/gfx/middle_transparent_embedded.err | 2 +- test/gfx/middle_transparent_gbc_spec.err | 2 +- test/gfx/middle_transparent_png_spec.err | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/gfx/implicit-overflow.err b/test/gfx/implicit-overflow.err index 7db00eb7..b952b260 100644 --- a/test/gfx/implicit-overflow.err +++ b/test/gfx/implicit-overflow.err @@ -1,2 +1,2 @@ -error: Each palette spec can only contain up to 1 color plus the implict transparent color +error: Each palette spec can only contain up to 1 color plus the implicit transparent color Conversion aborted after 1 error diff --git a/test/gfx/middle_transparent_embedded.err b/test/gfx/middle_transparent_embedded.err index 10e0a801..a15e6782 100644 --- a/test/gfx/middle_transparent_embedded.err +++ b/test/gfx/middle_transparent_embedded.err @@ -1,2 +1,2 @@ -error: Each palette spec can only contain up to 3 colors plus the implict transparent color +error: Each palette spec can only contain up to 3 colors plus the implicit transparent color Conversion aborted after 1 error diff --git a/test/gfx/middle_transparent_gbc_spec.err b/test/gfx/middle_transparent_gbc_spec.err index 10e0a801..a15e6782 100644 --- a/test/gfx/middle_transparent_gbc_spec.err +++ b/test/gfx/middle_transparent_gbc_spec.err @@ -1,2 +1,2 @@ -error: Each palette spec can only contain up to 3 colors plus the implict transparent color +error: Each palette spec can only contain up to 3 colors plus the implicit transparent color Conversion aborted after 1 error diff --git a/test/gfx/middle_transparent_png_spec.err b/test/gfx/middle_transparent_png_spec.err index 10e0a801..a15e6782 100644 --- a/test/gfx/middle_transparent_png_spec.err +++ b/test/gfx/middle_transparent_png_spec.err @@ -1,2 +1,2 @@ -error: Each palette spec can only contain up to 3 colors plus the implict transparent color +error: Each palette spec can only contain up to 3 colors plus the implicit transparent color Conversion aborted after 1 error From c2d1bcefc86e6f3081f5c8a2dfcf04d071af36dc Mon Sep 17 00:00:00 2001 From: Rangi <35663410+Rangi42@users.noreply.github.com> Date: Sat, 26 Sep 2026 13:40:24 -0400 Subject: [PATCH 4/6] Link to the GBDev AI policy from CONTRIBUTING.md (#2157) Co-authored-by: Eldred Habert --- docs/CONTRIBUTING.md | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index e4202200..38afe7c0 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -57,6 +57,18 @@ just modifying a file, you don't need to do anything (maybe update the copyright years). If you are adding new files, you need to use the `SPDX-License-Identifier: MIT` header. +### AI usage + +If you are using AI to assist in writing your contribution, you must follow +[GBDev's AI policy](https://gbdev.io/aipolicy.html). In particular, you are +responsible for reviewing all AI-generated code, ensuring compliance with +licensing requirements, and taking full responsibility for the contribution. +When AI tools have meaningfully assisted in producing a contribution, you must +disclose it with an `Assisted-by` tag in the Git commit message, as detailed +by the policy. + +### Contribution steps + 1. Fork this repository. 2. Checkout the `master` branch. 3. Create a new branch to work on. You could still work on `master`, but it's @@ -65,16 +77,18 @@ years). If you are adding new files, you need to use the target checks for additional warnings. Your patches shouldn't introduce any new warning (but it may be possible to remove some warning checks if it makes the code much easier). - - You can also use `cmake --preset develop` if you prefer. + + You can also use `cmake --preset develop` if you prefer. + 5. Test your changes by running `./run-tests.sh` in the `test` directory. `./run-tests.sh --help` will print its available options. (You must run `external/fetch-repos.sh` first; if you forget to, the test suite will fail and remind you mid-way.) - - You can also use `ctest --test-dir build` if you prefer. - `ctest --test-dir build --print-labels` will print its available - [test labels](https://cmake.org/cmake/help/latest/manual/ctest.1.html#label-matching). + + You can also use `ctest --test-dir build` if you prefer. + `ctest --test-dir build --print-labels` will print its available + [test labels](https://cmake.org/cmake/help/latest/manual/ctest.1.html#label-matching). + 6. Format your changes according to `clang-format`, which will reformat the coding style according to our standards defined in `.clang-format`. You can use `make format` to format all the C++ files. @@ -133,7 +147,7 @@ its contents. ### RGBLINK -Each `.asm` file corresponds to one test, or one *set* of tests. +Each `.asm` file corresponds to one test, or one _set_ of tests. All tests begin by assembling the `.asm` file into an object file, which will be linked in various ways depending on the test. @@ -183,11 +197,11 @@ RGBFIX will be invoked on the `.bin` file if it exists, or else on default-input.bin. If no `.out` file exist, RGBFIX is not expected to output anything. -If one *does* exist, RGBFIX's output **must** match the `.out` file's contents. +If one _does_ exist, RGBFIX's output **must** match the `.out` file's contents. If no `.err` file exists, RGBFIX is simply expected to be able to process the file normally. -If one *does* exist, RGBFIX's return status is ignored, but its error output +If one _does_ exist, RGBFIX's return status is ignored, but its error output **must** match the `.err` file's contents. Additionally, if a `.gb` file exists, the output of RGBFIX must match the `.gb`. @@ -210,7 +224,7 @@ Multiple kinds of output may be tested for the same input. If no `.err` file exists, RGBGFX is simply expected to be able to process the file normally. -If one *does* exist, RGBGFX's return status is ignored, but its output **must** +If one _does_ exist, RGBGFX's return status is ignored, but its output **must** match the `.err` file's contents. #### Reverse tests From 43bdeb873e3354c0943ee492915ec4827c8c0426 Mon Sep 17 00:00:00 2001 From: Rangi Date: Sat, 26 Sep 2026 14:59:43 -0400 Subject: [PATCH 5/6] Consistently use `*` for Markdown italic emphasis, not `_` --- docs/CONTRIBUTING.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index 38afe7c0..88ca2b64 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -197,11 +197,11 @@ RGBFIX will be invoked on the `.bin` file if it exists, or else on default-input.bin. If no `.out` file exist, RGBFIX is not expected to output anything. -If one _does_ exist, RGBFIX's output **must** match the `.out` file's contents. +If one *does* exist, RGBFIX's output **must** match the `.out` file's contents. If no `.err` file exists, RGBFIX is simply expected to be able to process the file normally. -If one _does_ exist, RGBFIX's return status is ignored, but its error output +If one *does* exist, RGBFIX's return status is ignored, but its error output **must** match the `.err` file's contents. Additionally, if a `.gb` file exists, the output of RGBFIX must match the `.gb`. @@ -224,7 +224,7 @@ Multiple kinds of output may be tested for the same input. If no `.err` file exists, RGBGFX is simply expected to be able to process the file normally. -If one _does_ exist, RGBGFX's return status is ignored, but its output **must** +If one *does* exist, RGBGFX's return status is ignored, but its output **must** match the `.err` file's contents. #### Reverse tests From b732f5947220ff2efcd19cebeb2aa9312af0c861 Mon Sep 17 00:00:00 2001 From: Rangi <35663410+Rangi42@users.noreply.github.com> Date: Sat, 26 Sep 2026 16:31:25 -0400 Subject: [PATCH 6/6] Improve handling of platform-specific build options (#2162) * Opt into POSIX functions with `_POSIX_C_SOURCE` before any system headers are `#include`d * Allow user to override `make develop`'s `WARNFLAGS` and `CXXFLAGS` * Use `make develop` on 32-bit Cygwin with sanitizers disabled * Do not unnecessarily redefine `fseek` and `ftell` * Disable a false-positive `-Wno-null-dereference` on 32-bit Cygwin Co-authored-by: ISSOtm --- .clang-format | 2 ++ .github/workflows/testing.yml | 8 ++++++-- Makefile | 7 ++++--- include/file.hpp | 3 ++- include/platform.hpp | 17 ++++++----------- src/asm/fstack.cpp | 3 ++- src/asm/lexer.cpp | 3 ++- src/asm/main.cpp | 5 +++-- src/asm/output.cpp | 3 ++- src/asm/section.cpp | 5 +++-- src/backtrace.cpp | 5 +++-- src/fix/fix.cpp | 3 ++- src/fix/main.cpp | 3 ++- src/fix/mbc.cpp | 5 +++-- src/gfx/main.cpp | 3 ++- src/gfx/pal_spec.cpp | 3 ++- src/link/assign.cpp | 3 ++- src/link/object.cpp | 3 ++- src/link/output.cpp | 3 ++- src/link/sdas_obj.cpp | 3 ++- src/style.cpp | 4 ++-- src/usage.cpp | 3 ++- src/util.cpp | 3 ++- 23 files changed, 60 insertions(+), 40 deletions(-) diff --git a/.clang-format b/.clang-format index db481b97..24714372 100644 --- a/.clang-format +++ b/.clang-format @@ -42,6 +42,8 @@ EmptyLineBeforeAccessModifier: Leave FixNamespaceComments: false IncludeBlocks: Regroup IncludeCategories: + - Regex: '^"platform\.hpp"$' + Priority: -1 - Regex: '^ #include #include @@ -12,7 +14,6 @@ #include #include "helpers.hpp" // assume -#include "platform.hpp" class File { std::variant _file; diff --git a/include/platform.hpp b/include/platform.hpp index 89f19aaf..ab7d4fc6 100644 --- a/include/platform.hpp +++ b/include/platform.hpp @@ -3,6 +3,12 @@ #ifndef RGBDS_PLATFORM_HPP #define RGBDS_PLATFORM_HPP +// MingGW and Cygwin may need POSIX functions which are not standard C explicitly enabled +// Make sure to keep this before any system header! +#if (defined(__MINGW32__) || defined(__CYGWIN__)) && !defined(_POSIX_C_SOURCE) + #define _POSIX_C_SOURCE 200809L +#endif + // MSVC doesn't have str(n)casecmp, use a suitable replacement #ifdef _MSC_VER #include // IWYU pragma: export @@ -55,17 +61,6 @@ #define setmode(fd, mode) (0) #endif -// Windows has 32-bit `long`, which limits `fseek` and `ftell` to 2 GiB -#if defined(_MSC_VER) || defined(__MINGW32__) - #define fseek _fseeki64 - #define ftell _ftelli64 -#endif - -// MingGW and Cygwin may need POSIX functions which are not standard C explicitly enabled -#if (defined(__MINGW32__) || defined(__CYGWIN__)) && !defined(_POSIX_C_SOURCE) - #define _POSIX_C_SOURCE 200809L -#endif - // Apple has deprecated `sprintf` since Xcode 14 (for macOS 13), but we use it solely in // contexts where both the size of the buffer *and* max size of the printed string are // known statically, which GCC thus checks for. diff --git a/src/asm/fstack.cpp b/src/asm/fstack.cpp index 7d363bc8..e00cc695 100644 --- a/src/asm/fstack.cpp +++ b/src/asm/fstack.cpp @@ -1,5 +1,7 @@ // SPDX-License-Identifier: MIT +#include "platform.hpp" // strncasecmp + #include "asm/fstack.hpp" #include @@ -22,7 +24,6 @@ #include "helpers.hpp" #include "itertools.hpp" // reversed #include "linkdefs.hpp" -#include "platform.hpp" // strncasecmp #include "verbosity.hpp" #include "asm/intern.hpp" diff --git a/src/asm/lexer.cpp b/src/asm/lexer.cpp index cd8082c8..ed1866f1 100644 --- a/src/asm/lexer.cpp +++ b/src/asm/lexer.cpp @@ -1,5 +1,7 @@ // SPDX-License-Identifier: MIT +#include "platform.hpp" + #include "asm/lexer.hpp" #include @@ -27,7 +29,6 @@ #include #include "helpers.hpp" -#include "platform.hpp" #include "style.hpp" #include "util.hpp" #include "verbosity.hpp" diff --git a/src/asm/main.cpp b/src/asm/main.cpp index 712d81e7..a7896c0f 100644 --- a/src/asm/main.cpp +++ b/src/asm/main.cpp @@ -1,5 +1,7 @@ // SPDX-License-Identifier: MIT +#include "platform.hpp" + #include "asm/main.hpp" #include @@ -23,8 +25,7 @@ #include "diagnostics.hpp" #include "helpers.hpp" #include "parser.hpp" // Generated from parser.y -#include "platform.hpp" -#include "style.hpp" // style_Parse +#include "style.hpp" // style_Parse #include "usage.hpp" #include "util.hpp" // UpperMap #include "verbosity.hpp" diff --git a/src/asm/output.cpp b/src/asm/output.cpp index f635c9e5..405d7ae0 100644 --- a/src/asm/output.cpp +++ b/src/asm/output.cpp @@ -1,5 +1,7 @@ // SPDX-License-Identifier: MIT +#include "platform.hpp" + #include "asm/output.hpp" #include @@ -16,7 +18,6 @@ #include "helpers.hpp" // assume, Defer #include "linkdefs.hpp" -#include "platform.hpp" #include "util.hpp" // xfclose #include "asm/charmap.hpp" diff --git a/src/asm/section.cpp b/src/asm/section.cpp index 5d784466..4e0f081b 100644 --- a/src/asm/section.cpp +++ b/src/asm/section.cpp @@ -1,5 +1,7 @@ // SPDX-License-Identifier: MIT +#include "platform.hpp" // fseek + #include "asm/section.hpp" #include @@ -21,8 +23,7 @@ #include "helpers.hpp" #include "itertools.hpp" // InsertionOrderedMap #include "linkdefs.hpp" -#include "platform.hpp" // fseek -#include "util.hpp" // xfclose, seekSize +#include "util.hpp" // xfclose, seekSize #include "asm/fstack.hpp" #include "asm/lexer.hpp" diff --git a/src/backtrace.cpp b/src/backtrace.cpp index c113ca7c..245a2fc4 100644 --- a/src/backtrace.cpp +++ b/src/backtrace.cpp @@ -1,12 +1,13 @@ // SPDX-License-Identifier: MIT +#include "platform.hpp" // strcasecmp + #include "backtrace.hpp" #include #include -#include "platform.hpp" // strcasecmp -#include "util.hpp" // parseWholeNumber +#include "util.hpp" // parseWholeNumber Tracing tracing; diff --git a/src/fix/fix.cpp b/src/fix/fix.cpp index 05a2e58b..9cf7d613 100644 --- a/src/fix/fix.cpp +++ b/src/fix/fix.cpp @@ -1,5 +1,7 @@ // SPDX-License-Identifier: MIT +#include "platform.hpp" + #include "fix/fix.hpp" #include @@ -14,7 +16,6 @@ #include "diagnostics.hpp" #include "helpers.hpp" -#include "platform.hpp" #include "util.hpp" // xclose, xfclose #include "fix/main.hpp" diff --git a/src/fix/main.cpp b/src/fix/main.cpp index bfdcbd84..2d93b7ce 100644 --- a/src/fix/main.cpp +++ b/src/fix/main.cpp @@ -1,5 +1,7 @@ // SPDX-License-Identifier: MIT +#include "platform.hpp" + #include "fix/main.hpp" #include @@ -15,7 +17,6 @@ #include "cli.hpp" #include "diagnostics.hpp" #include "helpers.hpp" -#include "platform.hpp" #include "style.hpp" #include "usage.hpp" #include "util.hpp" diff --git a/src/fix/mbc.cpp b/src/fix/mbc.cpp index 5546b8c2..db8815cb 100644 --- a/src/fix/mbc.cpp +++ b/src/fix/mbc.cpp @@ -1,5 +1,7 @@ // SPDX-License-Identifier: MIT +#include "platform.hpp" // strcasecmp + #include "fix/mbc.hpp" #include @@ -10,8 +12,7 @@ #include #include -#include "helpers.hpp" // unreachable_ -#include "platform.hpp" // strcasecmp +#include "helpers.hpp" // unreachable_ #include "util.hpp" #include "fix/warning.hpp" diff --git a/src/gfx/main.cpp b/src/gfx/main.cpp index a2f8a82e..38dfc388 100644 --- a/src/gfx/main.cpp +++ b/src/gfx/main.cpp @@ -1,5 +1,7 @@ // SPDX-License-Identifier: MIT +#include "platform.hpp" + #include "gfx/main.hpp" #include @@ -19,7 +21,6 @@ #include "diagnostics.hpp" #include "file.hpp" #include "helpers.hpp" -#include "platform.hpp" #include "style.hpp" // style_Parse #include "usage.hpp" #include "util.hpp" diff --git a/src/gfx/pal_spec.cpp b/src/gfx/pal_spec.cpp index 40f4bf2e..f667b36a 100644 --- a/src/gfx/pal_spec.cpp +++ b/src/gfx/pal_spec.cpp @@ -1,5 +1,7 @@ // SPDX-License-Identifier: MIT +#include "platform.hpp" + #include "gfx/pal_spec.hpp" #include @@ -22,7 +24,6 @@ #include "diagnostics.hpp" #include "file.hpp" #include "helpers.hpp" -#include "platform.hpp" #include "util.hpp" // UpperMap, parseDigit #include "gfx/main.hpp" diff --git a/src/link/assign.cpp b/src/link/assign.cpp index e3c7c47b..0b985ece 100644 --- a/src/link/assign.cpp +++ b/src/link/assign.cpp @@ -1,5 +1,7 @@ // SPDX-License-Identifier: MIT +#include "platform.hpp" + #include "link/assign.hpp" #include @@ -16,7 +18,6 @@ #include "helpers.hpp" #include "itertools.hpp" #include "linkdefs.hpp" -#include "platform.hpp" #include "verbosity.hpp" #include "link/main.hpp" diff --git a/src/link/object.cpp b/src/link/object.cpp index 8dfd7387..0ddb97d8 100644 --- a/src/link/object.cpp +++ b/src/link/object.cpp @@ -1,5 +1,7 @@ // SPDX-License-Identifier: MIT +#include "platform.hpp" + #include "link/object.hpp" #include @@ -18,7 +20,6 @@ #include "helpers.hpp" #include "linkdefs.hpp" -#include "platform.hpp" #include "util.hpp" // xfclose #include "verbosity.hpp" #include "version.hpp" diff --git a/src/link/output.cpp b/src/link/output.cpp index 47106466..837e52b2 100644 --- a/src/link/output.cpp +++ b/src/link/output.cpp @@ -1,5 +1,7 @@ // SPDX-License-Identifier: MIT +#include "platform.hpp" + #include "link/output.hpp" #include @@ -19,7 +21,6 @@ #include "extern/utf8decoder.hpp" #include "helpers.hpp" #include "linkdefs.hpp" -#include "platform.hpp" #include "util.hpp" #include "link/main.hpp" diff --git a/src/link/sdas_obj.cpp b/src/link/sdas_obj.cpp index 26562bec..65fe12e7 100644 --- a/src/link/sdas_obj.cpp +++ b/src/link/sdas_obj.cpp @@ -1,5 +1,7 @@ // SPDX-License-Identifier: MIT +#include "platform.hpp" + #include "link/sdas_obj.hpp" #include @@ -14,7 +16,6 @@ #include "helpers.hpp" // assume, literal_strlen #include "linkdefs.hpp" -#include "platform.hpp" #include "util.hpp" // parseWholeNumber #include "link/fstack.hpp" diff --git a/src/style.cpp b/src/style.cpp index 55d15a98..83bd27dc 100644 --- a/src/style.cpp +++ b/src/style.cpp @@ -3,14 +3,14 @@ // This implementation was based on https://github.com/agauniyal/rang/ // and adapted for RGBDS. +#include "platform.hpp" // isatty, strcasecmp + #include "style.hpp" #include #include // getenv #include -#include "platform.hpp" // isatty, strcasecmp - #if !STYLE_ANSI // clang-format off: maintain `include` order #define WIN32_LEAN_AND_MEAN // Include less from `windows.h` diff --git a/src/usage.cpp b/src/usage.cpp index 4bc4849d..9dc95513 100644 --- a/src/usage.cpp +++ b/src/usage.cpp @@ -1,5 +1,7 @@ // SPDX-License-Identifier: MIT +#include "platform.hpp" + #include "usage.hpp" #include @@ -8,7 +10,6 @@ #include "diagnostics.hpp" // vfatalx #include "helpers.hpp" -#include "platform.hpp" #include "style.hpp" #include "util.hpp" // parseWholeNumber #include "version.hpp" diff --git a/src/util.cpp b/src/util.cpp index 48d73f8a..4a7e6cd5 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -1,5 +1,7 @@ // SPDX-License-Identifier: MIT +#include "platform.hpp" + #include "util.hpp" #include @@ -9,7 +11,6 @@ #include // strspn #include "helpers.hpp" // assume -#include "platform.hpp" int xfclose(FILE *file) { if (file == stdin || file == stdout || file == stderr) {