diff --git a/.github/workflows/create-release-artifacts.yml b/.github/workflows/create-release-artifacts.yml index 29c12147..f9bd8341 100644 --- a/.github/workflows/create-release-artifacts.yml +++ b/.github/workflows/create-release-artifacts.yml @@ -87,13 +87,13 @@ jobs: if-no-files-found: error linux: - runs-on: ubuntu-22.04 # Oldest supported, for best glibc compatibility. + runs-on: ubuntu-24.04 # Oldest supported, for best glibc compatibility. steps: - name: Checkout repo uses: actions/checkout@v7 - name: Install deps run: | - ./.github/scripts/install-deps.sh ubuntu-22.04 + ./.github/scripts/install-deps.sh ubuntu-24.04 - name: Build binaries run: | make -k -j "$(getconf _NPROCESSORS_ONLN)" WARNFLAGS="-Werror -Wall -Wextra -pedantic -static" PKG_CONFIG="pkg-config --static" Q= diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index 054e7038..ca868c7c 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -40,15 +40,15 @@ jobs: unix: strategy: matrix: - os: [ubuntu-22.04, macos-15-intel, macos-26] + os: [ubuntu-24.04, ubuntu-26.04-arm, macos-15-intel, macos-26] cxx: [g++, clang++] buildsys: [make, cmake] exclude: # Don't use `g++` on macOS; it's just an alias to `clang++`. - { os: macos-15-intel, cxx: g++ } - { os: macos-26, cxx: g++ } include: # Use `g++-10`, the earliest GCC version we support, on the earliest Ubuntu runner. - - { os: ubuntu-22.04, cxx: g++-10, buildsys: make } - - { os: ubuntu-22.04, cxx: g++-10, buildsys: cmake } + - { os: ubuntu-24.04, cxx: g++-10, buildsys: make } + - { os: ubuntu-24.04, cxx: g++-10, buildsys: cmake } fail-fast: false runs-on: ${{ matrix.os }} steps: diff --git a/include/asm/section.hpp b/include/asm/section.hpp index b9eb4474..fbd5688f 100644 --- a/include/asm/section.hpp +++ b/include/asm/section.hpp @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -102,8 +103,7 @@ void sect_RelBytes(uint32_t n, std::vector const &exprs); void sect_RelWord(Expression const &expr, uint32_t pcShift); void sect_RelLong(Expression const &expr, uint32_t pcShift); void sect_PCRelByte(Expression const &expr, uint32_t pcShift); -bool sect_BinaryFile(std::string const &name, uint32_t startPos); -bool sect_BinaryFileSlice(std::string const &name, uint32_t startPos, uint32_t length); +bool sect_BinaryFile(std::string const &name, uint32_t startPos, std::optional length); void sect_EndSection(); void sect_PushSection(); diff --git a/include/file.hpp b/include/file.hpp index 6c9dd583..d781464d 100644 --- a/include/file.hpp +++ b/include/file.hpp @@ -47,7 +47,7 @@ public: } std::streambuf *operator->() { return &**this; } - char const *c_str(std::string const &path) const { + char const *display_name(std::string const &path) const { return std::holds_alternative(_file) ? path.c_str() : std::get(_file) == std::cin.rdbuf() ? "" : ""; diff --git a/include/gfx/png.hpp b/include/gfx/png.hpp index 87f15db6..74268514 100644 --- a/include/gfx/png.hpp +++ b/include/gfx/png.hpp @@ -5,6 +5,7 @@ #include #include +#include #include #include "gfx/rgba.hpp" @@ -17,7 +18,11 @@ struct Png { bool isIndexed = false; Png() {} + Png(std::string const &path); Png(char const *filename, std::streambuf &file); + +private: + void initialize(char const *filename, std::streambuf &file); }; #endif // RGBDS_GFX_PNG_HPP diff --git a/include/platform.hpp b/include/platform.hpp index ab7d4fc6..bbee456e 100644 --- a/include/platform.hpp +++ b/include/platform.hpp @@ -53,6 +53,12 @@ #define O_TEXT 0 // Assume that it's not defined either #endif // _MSC_VER +// MSVC doesn't have POSIX `ftruncate`, use a suitable replacement +#if defined(_MSC_VER) + #include // IWYU pragma: export + #define ftruncate _chsize_s +#endif + // Windows has stdin and stdout open as text by default, which we may not want #if defined(_MSC_VER) || defined(__MINGW32__) #include // IWYU pragma: export diff --git a/src/asm/parser.y b/src/asm/parser.y index 70200ca2..7188a1bb 100644 --- a/src/asm/parser.y +++ b/src/asm/parser.y @@ -1048,17 +1048,17 @@ include: incbin: POP_INCBIN string { - if (sect_BinaryFile($2, 0)) { + if (sect_BinaryFile($2, 0, std::nullopt)) { YYACCEPT; } } | POP_INCBIN string COMMA uconst { - if (sect_BinaryFile($2, $4)) { + if (sect_BinaryFile($2, $4, std::nullopt)) { YYACCEPT; } } | POP_INCBIN string COMMA uconst COMMA uconst { - if (sect_BinaryFileSlice($2, $4, $6)) { + if (sect_BinaryFile($2, $4, $6)) { YYACCEPT; } } diff --git a/src/asm/section.cpp b/src/asm/section.cpp index e03635a0..1ee37a2e 100644 --- a/src/asm/section.cpp +++ b/src/asm/section.cpp @@ -998,10 +998,13 @@ void sect_PCRelByte(Expression const &expr, uint32_t pcShift) { } } -bool sect_BinaryFile(std::string const &name, uint32_t startPos) { +bool sect_BinaryFile(std::string const &name, uint32_t startPos, std::optional length) { if (!requireCodeSection()) { return false; } + if (length.has_value() && *length == 0) { // Don't even bother with 0-byte slices + return false; + } FILE *file = nullptr; if (std::optional fullPath = fstk_FindFile(name); fullPath) { @@ -1022,6 +1025,16 @@ bool sect_BinaryFile(std::string const &name, uint32_t startPos) { *fileSize ); return false; + } else if (length.has_value() && *length > *fileSize - startPos) { + error( + "Specified range in `INCBIN` file \"%s\" is out of bounds (%" PRIu32 " + %" PRIu32 + " > %" PRIu64 ")", + name.c_str(), + startPos, + *length, + *fileSize + ); + return false; } // The file is seekable; skip to the specified start position fseek(file, startPos, SEEK_SET); @@ -1044,8 +1057,26 @@ bool sect_BinaryFile(std::string const &name, uint32_t startPos) { // LCOV_EXCL_STOP } - for (int byte; (byte = fgetc(file)) != EOF;) { - writeByte(byte); + if (length.has_value()) { + uint32_t remaining = *length; + for (; remaining; --remaining) { + if (int byte = fgetc(file); byte == EOF) { + break; // LCOV_EXCL_LINE + } else { + writeByte(byte); + } + } + if (remaining > 0 && !ferror(file)) { + error( + "Premature end of `INCBIN` file \"%s\" (%" PRId32 " bytes left to read)", + name.c_str(), + remaining + ); + } + } else { + for (int byte; (byte = fgetc(file)) != EOF;) { + writeByte(byte); + } } if (ferror(file)) { @@ -1056,83 +1087,6 @@ bool sect_BinaryFile(std::string const &name, uint32_t startPos) { return false; } -bool sect_BinaryFileSlice(std::string const &name, uint32_t startPos, uint32_t length) { - if (!requireCodeSection()) { - return false; - } - if (length == 0) { // Don't even bother with 0-byte slices - return false; - } - - FILE *file = nullptr; - if (std::optional fullPath = fstk_FindFile(name); fullPath) { - file = fopen(fullPath->c_str(), "rb"); - } - if (!file) { - return fstk_FileError(name, "`INCBIN`"); - } - Defer closeFile{[&] { xfclose(file); }}; - - if (std::optional fileSize = seekSize(file); fileSize.has_value()) { - if (startPos > *fileSize) { - error( - "Specified start position (%" PRIu32 ") is greater than length of \"%s\" (%" PRIu64 - ")", - startPos, - name.c_str(), - *fileSize - ); - return false; - } else if (length > *fileSize - startPos) { - error( - "Specified range in `INCBIN` file \"%s\" is out of bounds (%" PRIu32 " + %" PRIu32 - " > %" PRIu64 ")", - name.c_str(), - startPos, - length, - *fileSize - ); - return false; - } - // The file is seekable; skip to the specified start position - fseek(file, startPos, SEEK_SET); - } else { - // LCOV_EXCL_START - if (errno != ESPIPE) { - error( - "Error determining size of `INCBIN` file \"%s\": %s", name.c_str(), strerror(errno) - ); - } - // The file isn't seekable, so we'll just skip bytes one at a time - while (startPos--) { - if (fgetc(file) == EOF) { - error( - "Specified start position is greater than length of file \"%s\"", name.c_str() - ); - return false; - } - } - // LCOV_EXCL_STOP - } - - while (length--) { - if (int byte = fgetc(file); byte != EOF) { - writeByte(byte); - // LCOV_EXCL_START - } else if (ferror(file)) { - error("Error reading `INCBIN` file \"%s\": %s", name.c_str(), strerror(errno)); - } else { - error( - "Premature end of `INCBIN` file \"%s\" (%" PRId32 " bytes left to read)", - name.c_str(), - length + 1 - ); - // LCOV_EXCL_STOP - } - } - return false; -} - void sect_PushSection() { sectionStack.push_front({ .section = currentSection, diff --git a/src/fix/fix.cpp b/src/fix/fix.cpp index 9cf7d613..d447f3fe 100644 --- a/src/fix/fix.cpp +++ b/src/fix/fix.cpp @@ -381,6 +381,24 @@ static void if (options.padValue == UNSPECIFIED) { rom0Len = headerSize; } + } else if (output != STDOUT_FILENO) { + // Truncate a pre-existing output file before writing to it + if (ftruncate(output, 0) != 0) { + // LCOV_EXCL_START + int errnum = errno; + if (errnum == EINVAL) { + // We do not support FIFOs or symlinks + if (struct stat stat; fstat(output, &stat) == 0 && !S_ISREG(stat.st_mode)) { + error( + "\"%s\" is not a regular file, and thus cannot be modified in-place", name + ); + return; + } + } + error("Failed to truncate \"%s\": %s", name, strerror(errnum)); + return; + // LCOV_EXCL_STOP + } } writeLen = writeBytes(output, rom0, rom0Len); @@ -498,8 +516,7 @@ bool fix_ProcessFile(char const *name, char const *outputName) { error("Failed to open \"%s\" for reading+writing: %s", name, strerror(errno)); } else { Defer closeInput{[&] { xclose(input); }}; - struct stat stat; - if (fstat(input, &stat) == -1) { + if (struct stat stat; fstat(input, &stat) == -1) { error("Failed to stat \"%s\": %s", name, strerror(errno)); // LCOV_EXCL_LINE } else if (!S_ISREG(stat.st_mode)) { // We do not support FIFOs or symlinks // LCOV_EXCL_START diff --git a/src/gfx/main.cpp b/src/gfx/main.cpp index 38dfc388..1406632c 100644 --- a/src/gfx/main.cpp +++ b/src/gfx/main.cpp @@ -5,7 +5,6 @@ #include "gfx/main.hpp" #include -#include #include #include #include @@ -19,7 +18,6 @@ #include "cli.hpp" #include "diagnostics.hpp" -#include "file.hpp" #include "helpers.hpp" #include "style.hpp" // style_Parse #include "usage.hpp" @@ -681,14 +679,7 @@ int main(int argc, char *argv[]) { 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); - } + png = Png(options.input); } // Execute deferred pal spec parsing, now that all other params are known. diff --git a/src/gfx/png.cpp b/src/gfx/png.cpp index 739a74d8..31ac5495 100644 --- a/src/gfx/png.cpp +++ b/src/gfx/png.cpp @@ -15,6 +15,7 @@ #include #include "diagnostics.hpp" +#include "file.hpp" #include "helpers.hpp" #include "verbosity.hpp" @@ -61,7 +62,19 @@ static void readData(png_structp png, png_bytep data, size_t length) { } } +Png::Png(std::string const &path) { + if (File image; image.open(path, std::ios_base::in | std::ios_base::binary) == nullptr) { + fatal("Failed to open PNG image (\"%s\"): %s", image.display_name(path), strerror(errno)); + } else { + initialize(image.display_name(path), *image); + } +} + Png::Png(char const *filename, std::streambuf &file) { + initialize(filename, file); +} + +void Png::initialize(char const *filename, std::streambuf &file) { Input input(filename, file); verbosePrint(VERB_NOTICE, "Reading PNG file \"%s\"\n", input.filename); diff --git a/src/gfx/process.cpp b/src/gfx/process.cpp index 5cee407a..54154ac2 100644 --- a/src/gfx/process.cpp +++ b/src/gfx/process.cpp @@ -491,7 +491,11 @@ static void outputPalettes(std::vector const &palettes) { File output; if (!output.open(options.palettes, std::ios_base::out | std::ios_base::binary)) { // LCOV_EXCL_START - fatal("Failed to create \"%s\": %s", output.c_str(options.palettes), strerror(errno)); + fatal( + "Failed to create \"%s\": %s", + output.display_name(options.palettes), + strerror(errno) + ); // LCOV_EXCL_STOP } @@ -646,7 +650,7 @@ static void outputUnoptimizedTileData( File output; if (!output.open(options.output, std::ios_base::out | std::ios_base::binary)) { // LCOV_EXCL_START - fatal("Failed to create \"%s\": %s", output.c_str(options.output), strerror(errno)); + fatal("Failed to create \"%s\": %s", output.display_name(options.output), strerror(errno)); // LCOV_EXCL_STOP } @@ -699,7 +703,7 @@ static void outputUnoptimizedMaps( file.emplace(); if (!file->open(path, std::ios_base::out | std::ios_base::binary)) { // LCOV_EXCL_START - fatal("Failed to create \"%s\": %s", file->c_str(path), strerror(errno)); + fatal("Failed to create \"%s\": %s", file->display_name(path), strerror(errno)); // LCOV_EXCL_STOP } } @@ -873,7 +877,7 @@ static void outputTileData(UniqueTiles const &tiles) { File output; if (!output.open(options.output, std::ios_base::out | std::ios_base::binary)) { // LCOV_EXCL_START - fatal("Failed to create \"%s\": %s", output.c_str(options.output), strerror(errno)); + fatal("Failed to create \"%s\": %s", output.display_name(options.output), strerror(errno)); // LCOV_EXCL_STOP } @@ -913,7 +917,7 @@ static void outputTilemap(std::vector const &attrmap) { File output; if (!output.open(options.tilemap, std::ios_base::out | std::ios_base::binary)) { // LCOV_EXCL_START - fatal("Failed to create \"%s\": %s", output.c_str(options.tilemap), strerror(errno)); + fatal("Failed to create \"%s\": %s", output.display_name(options.tilemap), strerror(errno)); // LCOV_EXCL_STOP } @@ -929,7 +933,7 @@ static void File output; if (!output.open(options.attrmap, std::ios_base::out | std::ios_base::binary)) { // LCOV_EXCL_START - fatal("Failed to create \"%s\": %s", output.c_str(options.attrmap), strerror(errno)); + fatal("Failed to create \"%s\": %s", output.display_name(options.attrmap), strerror(errno)); // LCOV_EXCL_STOP } @@ -950,7 +954,7 @@ static void File output; if (!output.open(options.palmap, std::ios_base::out | std::ios_base::binary)) { // LCOV_EXCL_START - fatal("Failed to create \"%s\": %s", output.c_str(options.palmap), strerror(errno)); + fatal("Failed to create \"%s\": %s", output.display_name(options.palmap), strerror(errno)); // LCOV_EXCL_STOP } diff --git a/src/gfx/reverse.cpp b/src/gfx/reverse.cpp index 617504e4..362d2db7 100644 --- a/src/gfx/reverse.cpp +++ b/src/gfx/reverse.cpp @@ -31,7 +31,7 @@ static std::vector readInto(std::string const &path) { File file; if (!file.open(path, std::ios::in | std::ios::binary)) { - fatal("Failed to open \"%s\": %s", file.c_str(path), strerror(errno)); + fatal("Failed to open \"%s\": %s", file.display_name(path), strerror(errno)); } std::vector data(128 * 16); // Begin with some room pre-allocated @@ -87,7 +87,7 @@ static void flushPng(png_structp png) { } static void printColor(std::optional const &color) { - if (color) { + if (color.has_value()) { fprintf(stderr, "#%08x", color->toCSS()); } else { fputs(" ", stderr); @@ -207,7 +207,9 @@ void reverse() { if (!options.palettes.empty()) { File file; if (!file.open(options.palettes, std::ios::in | std::ios::binary)) { - fatal("Failed to open \"%s\": %s", file.c_str(options.palettes), strerror(errno)); + fatal( + "Failed to open \"%s\": %s", file.display_name(options.palettes), strerror(errno) + ); } palettes.clear(); @@ -478,12 +480,12 @@ void reverse() { File pngFile; if (!pngFile.open(options.input, std::ios::out | std::ios::binary)) { // LCOV_EXCL_START - fatal("Failed to create \"%s\": %s", pngFile.c_str(options.input), strerror(errno)); + fatal("Failed to create \"%s\": %s", pngFile.display_name(options.input), strerror(errno)); // LCOV_EXCL_STOP } png_structp png = png_create_write_struct( PNG_LIBPNG_VER_STRING, - const_cast(pngFile.c_str(options.input)), + const_cast(pngFile.display_name(options.input)), pngError, pngWarning ); @@ -533,22 +535,16 @@ void reverse() { png_color pngPalette[4] = {}; png_byte pngTrans[4] = {}; int nbPngColors = 0, nbPngTrans = 0; - for (auto const &color : palettes[0]) { - 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; - } + for (auto const &slot : palettes[0]) { + Rgba color = slot.has_value() ? *slot : Rgba(255, 255, 255, 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/asm/div-mod.asm b/test/asm/div-mod.asm index 2103429d..baf56f8d 100644 --- a/test/asm/div-mod.asm +++ b/test/asm/div-mod.asm @@ -13,12 +13,10 @@ MACRO test_mod def x = \1 ; dividend def y = \2 ; divisor shift 2 - def q = x / y ; quotient - def r = x % y ; remainder ; identity laws - test (V (q * y + r)) == (V x) - test (V (x + y) % y) == (V r) - test (V (x - y) % y) == (V r) + test (V x) / y * y + (V x) % y == (V x) + test (V (x + y)) % y == (V x) % y + test (V (x - y)) % y == (V x) % y ENDM MACRO test_each_mod diff --git a/test/fix/test.sh b/test/fix/test.sh index 7c48a24d..ff9dcdc5 100755 --- a/test/fix/test.sh +++ b/test/fix/test.sh @@ -35,6 +35,13 @@ rescolors="$(tput op)" RGBFIX="$src/../../rgbfix" +startTest () { + (( tests++ )) + if [[ $progress -ne 0 ]]; then + echo "${bold}${green}$1...${rescolors}${resbold}" + fi +} + tryDiff () { if ! diff -au --strip-trailing-cr "$1" "$2"; then echo "${bold}${red}${3:-$1} mismatch!${rescolors}${resbold}" @@ -44,7 +51,7 @@ tryDiff () { tryCmp () { if ! cmp "$1" "$2"; then - "$src/../../gbdiff.bash" "$1" "$2" + "$src/../../contrib/gbdiff.bash" "$1" "$2" | head echo "${bold}${red}${3:-$1} mismatch!${rescolors}${resbold}" false fi @@ -61,11 +68,7 @@ runTest () { fi for variant in '' ' piped' ' output'; do - (( tests++ )) - our_rc=0 - if [[ $progress -ne 0 ]]; then - echo "${bold}${green}$1${variant}...${rescolors}${resbold}" - fi + startTest "$1${variant}" if [[ -r "$2/$1.bin" ]]; then desired_input="$2/$1.bin" else @@ -96,7 +99,7 @@ runTest () { desired_errname=/dev/null fi sed -e "s/$subst//g" -e "s#$src_subst##g" out.out | tryDiff "$desired_outname" - "$1.out${variant}" - (( our_rc = our_rc || $? )) + our_rc=$? sed -e "s/$subst//g" -e "s#$src_subst##g" out.err | tryDiff "$desired_errname" - "$1.err${variant}" (( our_rc = our_rc || $? )) @@ -116,11 +119,36 @@ runTest () { runSpecialTest () { name="$1" shift - echo "${bold}${green}${name}...${rescolors}${resbold}" + startTest "$name" + + eval "$RGBFIX" "$@" -o out.gb '>out.out' '2>out.err' + tryDiff out.out /dev/null "${name}.out" + our_rc=$? + tryDiff out.err /dev/null "${name}.err" + (( our_rc = our_rc || $? )) + tryCmp input.gb out.gb "${name}.gb" + (( our_rc = our_rc || $? )) + + (( rc = rc || our_rc )) + if [[ $our_rc -ne 0 ]]; then + (( failed++ )) + fi +} + +runTestExpectingFailure () { + name="$1" + shift + startTest "$name" + eval "$RGBFIX" "$@" '2>out.err' - rc=$((rc || $? != 1)) + our_rc=$(($? != 1)) tryDiff "$src/${name}.err" out.err "${name}.err" - rc=$((rc || $?)) + our_rc=$((our_rc || $?)) + + rc=$((rc || our_rc)) + if [[ $our_rc -ne 0 ]]; then + (( failed++ )) + fi } rm -f padding*_* # Delete padding test cases generated but not deleted (e.g. interrupted) @@ -130,14 +158,24 @@ for i in "$src"/*.flags; do runTest "$(basename "$i" .flags)" "$src" done +# Check that RGBFIX truncates a pre-existing output file +dd if=/dev/zero of=input.gb bs=1 count=336 >/dev/null 2>&1 +dd if=/dev/zero of=out.gb bs=1 count=16384 >/dev/null 2>&1 +runSpecialTest pre-existing-output input.gb + +# Check that RGBFIX handles an output file identical to the input file +dd if=/dev/zero of=input.gb bs=1 count=336 >/dev/null 2>&1 +cp input.gb out.gb +runSpecialTest equivalent-output out.gb + # Check that RGBFIX errors out when inputting a non-existent file -runSpecialTest no-exist no-exist +runTestExpectingFailure no-exist no-exist # Check that RGBFIX errors out when not inputting any file -runSpecialTest no-input +runTestExpectingFailure no-input # Check that RGBFIX errors out when inputting multiple files with an output file -runSpecialTest multiple-to-one one two three -o multiple-to-one +runTestExpectingFailure multiple-to-one one two three -o multiple-to-one # Check the result with all different padding bytes echo "${bold}Checking padding...${resbold}" @@ -149,7 +187,8 @@ for (( i=0; i < 10; ++i )); do echo "$padding..." for suffix in '' -large -larger; do cat <<<"-p $padding" >padding$suffix.flags - tr '\377' \\$((padding / 64))$(((padding / 8) % 8))$((padding % 8)) <"$src/padding$suffix.gb" >padding$suffix.gb # OK because $FF bytes are only used for padding + # `tr`ing &377 aka $FF is OK because $FF bytes are only used for padding + tr '\377' \\$((padding / 64))$(((padding / 8) % 8))$((padding % 8)) <"$src/padding$suffix.gb" >padding$suffix.gb runTest padding${suffix} . done done diff --git a/test/gfx/reverse_curve.flags b/test/gfx/reverse_curve.flags index 6f3e38c1..dffb5de7 100644 --- a/test/gfx/reverse_curve.flags +++ b/test/gfx/reverse_curve.flags @@ -1,3 +1,4 @@ +-C -m -t reverse_curve.tilemap -a reverse_curve.attrmap diff --git a/test/gfx/reverse_curve.pal b/test/gfx/reverse_curve.pal index 9f2400f8..9b443f7a 100644 Binary files a/test/gfx/reverse_curve.pal and b/test/gfx/reverse_curve.pal differ diff --git a/test/gfx/reverse_no_curve.2bpp b/test/gfx/reverse_no_curve.2bpp new file mode 100644 index 00000000..d8eb117d --- /dev/null +++ b/test/gfx/reverse_no_curve.2bpp @@ -0,0 +1 @@ +? @Lóˆóˆ˙€˙€˙€˙‰~G|C<# \ No newline at end of file diff --git a/test/gfx/reverse_no_curve.attrmap b/test/gfx/reverse_no_curve.attrmap new file mode 100644 index 00000000..61516ade Binary files /dev/null and b/test/gfx/reverse_no_curve.attrmap differ diff --git a/test/gfx/reverse_no_curve.flags b/test/gfx/reverse_no_curve.flags new file mode 100644 index 00000000..4730c870 --- /dev/null +++ b/test/gfx/reverse_no_curve.flags @@ -0,0 +1,4 @@ +-m +-t reverse_no_curve.tilemap +-a reverse_no_curve.attrmap +-p reverse_no_curve.pal diff --git a/test/gfx/reverse_no_curve.pal b/test/gfx/reverse_no_curve.pal new file mode 100644 index 00000000..9f2400f8 Binary files /dev/null and b/test/gfx/reverse_no_curve.pal differ diff --git a/test/gfx/reverse_no_curve.tilemap b/test/gfx/reverse_no_curve.tilemap new file mode 100644 index 00000000..dee9c4c8 Binary files /dev/null and b/test/gfx/reverse_no_curve.tilemap differ diff --git a/test/gfx/rgbgfx_test.cpp b/test/gfx/rgbgfx_test.cpp index 8691d80e..c3df737a 100644 --- a/test/gfx/rgbgfx_test.cpp +++ b/test/gfx/rgbgfx_test.cpp @@ -466,7 +466,7 @@ int main(int argc, char *argv[]) { auto cgbColor = [](Rgba const &rgba) { auto field = [](uint16_t component, uint8_t shift) { - return (component & 0x1F) << shift; + return (component >> 3) << shift; }; return rgba.isTransparent() ? Rgba::transparent diff --git a/test/gfx/test.sh b/test/gfx/test.sh index 3d3b50b7..727c44db 100755 --- a/test/gfx/test.sh +++ b/test/gfx/test.sh @@ -89,20 +89,26 @@ for f in *.png; do done newTest "$RGBGFX" $flags "$f" + test_rc=0 if [[ -e "${f%.png}.err" ]]; then runTest 2>"$errtmp" - diff -au --strip-trailing-cr "${f%.png}.err" "$errtmp" || failTest + diff -au --strip-trailing-cr "${f%.png}.err" "$errtmp" || test_rc=$? else - runTest && checkOutput "${f%.png}" || failTest $? + runTest || test_rc=$? fi + checkOutput "${f%.png}" || test_rc=$? + (( test_rc )) && failTest $test_rc newTest "$RGBGFX" $flags - "<$f" + test_rc=0 if [[ -e "${f%.png}.err" ]]; then runTest 2>"$errtmp" - diff -au --strip-trailing-cr "${f%.png}.err" <(sed "s##${f//#/\\#}#g" "$errtmp") || failTest + diff -au --strip-trailing-cr "${f%.png}.err" <(sed "s##${f//#/\\#}#g" "$errtmp") || test_rc=$? else - runTest && checkOutput "${f%.png}" || failTest $? + runTest || test_rc=$? fi + checkOutput "${f%.png}" || test_rc=$? + (( test_rc )) && failTest $test_rc done for f in *.[12]bpp; do @@ -122,7 +128,7 @@ for f in *.[12]bpp; do if [[ -e "${f%.[12]bpp}.err" ]]; then newTest "$RGBGFX $flags -o $f -r 1 result.png" runTest 2>"$errtmp" - diff -au --strip-trailing-cr "${f%.[12]bpp}.err" <(sed "s##${f//#/\\#}#g" "$errtmp") || failTest + diff -au --strip-trailing-cr "${f%.[12]bpp}.err" <(sed "s##${f//#/\\#}#g" "$errtmp") || failTest $? else newTest "$RGBGFX $flags -o $f -r 1 result.png && $RGBGFX $flags -o result.2bpp result.png" runTest && tryCmp "$f" result.2bpp || failTest $? diff --git a/test/link/test.sh b/test/link/test.sh index 419485fd..2d08afc6 100755 --- a/test/link/test.sh +++ b/test/link/test.sh @@ -14,9 +14,10 @@ gbtemp2="$(mktemp)" outtemp="$(mktemp)" outtemp2="$(mktemp)" outtemp3="$(mktemp)" +errtemp="$(mktemp)" # shellcheck disable=SC2064 # (Immediate expansion is the desired behavior.) -trap "rm -f ${otemp@Q} ${gbtemp@Q} ${gbtemp2@Q} ${outtemp@Q} ${outtemp2@Q} ${outtemp3@Q}" EXIT +trap "rm -f ${otemp@Q} ${gbtemp@Q} ${gbtemp2@Q} ${outtemp@Q} ${outtemp2@Q} ${outtemp3@Q} ${errtemp@Q}" EXIT tests=0 failed=0 @@ -92,31 +93,39 @@ for i in *.asm; do continue fi - RGBLINKFLAGS=() if [ -f "${test}.flags" ]; then RGBLINKFLAGS+=("@${test}.flags") fi + ran_test=false + # Some tests have variants depending on flags - ran_flag=false for flag in '-d' '-t' '-w'; do if [ -f "${test}-no${flag}.out" ]; then continueTest "-no${flag}" rgblinkQuiet "${RGBLINKFLAGS[@]}" -o "$gbtemp" "$otemp" 2>"$outtemp" tryDiff "${test}-no${flag}.out" "$outtemp" + bin=${test}-no${flag}.out.bin + if [ -f "$bin" ]; then + tryCmpRom "$bin" + fi evaluateTest - ran_flag=true + ran_test=true fi if [ -f "${test}${flag}.out" ]; then continueTest "$flag" rgblinkQuiet "${RGBLINKFLAGS[@]}" ${flag} -o "$gbtemp" "$otemp" 2>"$outtemp" tryDiff "${test}${flag}.out" "$outtemp" + bin=${test}${flag}.out.bin + if [ -f "$bin" ]; then + tryCmpRom "$bin" + fi evaluateTest - ran_flag=true + ran_test=true fi done - if "$ran_flag"; then + if "$ran_test"; then continue fi @@ -127,10 +136,14 @@ for i in *.asm; do continueTest "${script#${test}}" rgblinkQuiet "${RGBLINKFLAGS[@]}" -l "$script" -o "$gbtemp" "$otemp" 2>"$outtemp" tryDiff "${script%.link}.out" "$outtemp" + bin=${script%.link}.out.bin + if [ -f "$bin" ]; then + tryCmpRom "$bin" + fi evaluateTest - ran_flag=true + ran_test=true done - if "$ran_flag"; then + if "$ran_test"; then continue fi @@ -195,7 +208,8 @@ test="fragment-literals" startTest "$RGBASM" -o "$otemp" "$test"/a.asm continueTest -rgblinkQuiet -o "$gbtemp" -m "$outtemp" -n "$outtemp2" "$otemp" +rgblinkQuiet -o "$gbtemp" -m "$outtemp" -n "$outtemp2" "$otemp" 2>"$errtemp" +tryDiff /dev/null "$errtemp" tryCmpRom "$test"/ref.out.bin tryDiff "$test"/ref.out.map "$outtemp" tryDiff "$test"/ref.out.sym "$outtemp2" @@ -206,7 +220,8 @@ startTest "$RGBASM" -o "$otemp" "$test"/a.asm "$RGBASM" -o "$outtemp" "$test"/b.asm continueTest -rgblinkQuiet -o "$gbtemp" -n "$outtemp2" "$otemp" "$outtemp" +rgblinkQuiet -o "$gbtemp" -n "$outtemp2" "$otemp" "$outtemp" 2>"$errtemp" +tryDiff /dev/null "$errtemp" tryCmpRom "$test"/ref.out.bin tryDiff "$test"/ref.out.sym "$outtemp2" evaluateTest @@ -227,8 +242,10 @@ startTest "$RGBASM" -o "$otemp" "$test"/a.asm "$RGBASM" -o "$outtemp" "$test"/b.asm continueTest -rgblinkQuiet -o "$gbtemp" "$otemp" -rgblinkQuiet -o "$gbtemp2" "$outtemp" +rgblinkQuiet -o "$gbtemp" "$otemp" 2>"$errtemp" +tryDiff /dev/null "$errtemp" +rgblinkQuiet -o "$gbtemp2" "$outtemp" 2>"$errtemp" +tryDiff /dev/null "$errtemp" tryCmp "$gbtemp" "$gbtemp2" evaluateTest @@ -236,7 +253,8 @@ test="load-fragment/base" startTest "$RGBASM" -o "$otemp" "$test"/a.asm continueTest -rgblinkQuiet -o "$gbtemp" -n "$outtemp" "$otemp" +rgblinkQuiet -o "$gbtemp" -n "$outtemp" "$otemp" 2>"$errtemp" +tryDiff /dev/null "$errtemp" tryCmpRom "$test"/ref.out.bin tryDiff "$test"/ref.out.sym "$outtemp" evaluateTest @@ -246,7 +264,8 @@ startTest "$RGBASM" -o "$otemp" "$test"/a.asm "$RGBASM" -o "$gbtemp2" "$test"/b.asm continueTest -rgblinkQuiet -o "$gbtemp" "$otemp" "$gbtemp2" +rgblinkQuiet -o "$gbtemp" "$otemp" "$gbtemp2" 2>"$errtemp" +tryDiff /dev/null "$errtemp" tryCmpRom "$test"/ref.out.bin evaluateTest @@ -256,7 +275,8 @@ startTest "$RGBASM" -o "$outtemp" "$test"/b.asm "$RGBASM" -o "$outtemp2" "$test"/c.asm continueTest -rgblinkQuiet -o "$gbtemp" -m "$outtemp3" -n "$gbtemp2" "$otemp" "$outtemp" "$outtemp2" +rgblinkQuiet -o "$gbtemp" -m "$outtemp3" -n "$gbtemp2" "$otemp" "$outtemp" "$outtemp2" 2>"$errtemp" +tryDiff /dev/null "$errtemp" tryCmpRom "$test"/ref.out.bin tryDiff "$test"/ref.out.map "$outtemp3" tryDiff "$test"/ref.out.sym "$gbtemp2" @@ -387,7 +407,8 @@ startTest "$RGBASM" -o "$otemp" "$test"/a.asm "$RGBASM" -o "$gbtemp2" "$test"/b.asm continueTest -rgblinkQuiet -o "$gbtemp" "$otemp" "$gbtemp2" +rgblinkQuiet -o "$gbtemp" "$otemp" "$gbtemp2" 2>"$errtemp" +tryDiff /dev/null "$errtemp" tryCmpRom "$test"/ref.out.bin evaluateTest @@ -404,7 +425,8 @@ startTest "$RGBASM" -o "$otemp" "$test"/a.asm "$RGBASM" -o "$gbtemp2" "$test"/b.asm continueTest -rgblinkQuiet -o "$gbtemp" "$otemp" "$gbtemp2" +rgblinkQuiet -o "$gbtemp" "$otemp" "$gbtemp2" 2>"$errtemp" +tryDiff /dev/null "$errtemp" tryCmpRom "$test"/ref.out.bin evaluateTest @@ -413,7 +435,8 @@ startTest "$RGBASM" -o "$otemp" "$test"/a.asm "$RGBASM" -o "$gbtemp2" "$test"/b.asm continueTest -rgblinkQuiet -o "$gbtemp" "$otemp" "$gbtemp2" +rgblinkQuiet -o "$gbtemp" "$otemp" "$gbtemp2" 2>"$errtemp" +tryDiff /dev/null "$errtemp" tryCmpRom "$test"/ref.out.bin evaluateTest @@ -440,7 +463,8 @@ startTest "$RGBASM" -o "$otemp" "$test"/a.asm "$RGBASM" -o "$gbtemp2" "$test"/b.asm continueTest -rgblinkQuiet -o "$gbtemp" -l "$test"/script.link "$otemp" "$gbtemp2" +rgblinkQuiet -o "$gbtemp" -l "$test"/script.link "$otemp" "$gbtemp2" 2>"$errtemp" +tryDiff /dev/null "$errtemp" tryCmpRom "$test"/ref.out.bin evaluateTest @@ -449,7 +473,8 @@ startTest "$RGBASM" -o "$otemp" "$test"/a.asm "$RGBASM" -o "$gbtemp2" "$test"/b.asm continueTest -rgblinkQuiet -o "$gbtemp" -l "$test"/script.link "$otemp" "$gbtemp2" +rgblinkQuiet -o "$gbtemp" -l "$test"/script.link "$otemp" "$gbtemp2" 2>"$errtemp" +tryDiff /dev/null "$errtemp" tryCmpRom "$test"/ref.out.bin evaluateTest diff --git a/test/run-tests.sh b/test/run-tests.sh index 1d209f7c..2b054758 100755 --- a/test/run-tests.sh +++ b/test/run-tests.sh @@ -18,8 +18,6 @@ Options: --only-free skip tests that build nonfree codebases --jobs build external codebases with `make -j` --os skip tests known to fail on (e.g. `macos-14`) - --installed-rgbds use the system installed RGBDS - (only compatible with external codebases) EOF } @@ -28,8 +26,7 @@ EOF nonfree=true internal=true external=true -installedrgbds=false -make_jobs= +make_args=() osname= while [[ $# -gt 0 ]]; do case "$1" in @@ -46,12 +43,9 @@ while [[ $# -gt 0 ]]; do --only-free) nonfree=false ;; - --installed-rgbds) - installedrgbds=true - ;; --jobs) shift - make_jobs="-j$1" + make_args+=(-j "$1") ;; --os) shift @@ -73,26 +67,10 @@ if ! "$internal" && ! "$external"; then false fi -if "$internal" && "$installedrgbds"; then - echo "Please specify --only-external with --installed-rgbds" - echo "(internal tests don't support running with system-installed RGBDS)" - false -fi - # Refuse to run if RGBDS isn't available -if "$installedrgbds"; then - is_installed() { - command -v "$1" >/dev/null 2>&1 - } - if ! (is_installed rgbasm && is_installed rgblink && is_installed rgbfix && is_installed rgbgfx); then - echo "Please install RGBDS before running the tests" - false - fi -else - if [[ ! ( -x ../rgbasm && -x ../rgblink && -x ../rgbfix && -x ../rgbgfx ) ]]; then - echo "Please build RGBDS before running the tests" - false - fi +if [[ ! ( -x ../rgbasm && -x ../rgblink && -x ../rgbfix && -x ../rgbgfx ) ]]; then + echo "Please build RGBDS before running the tests" + false fi # Tests included with the repository @@ -128,6 +106,6 @@ for cfg in *.cfg; do ( # Run nonfree tests only if they are opted into. if ! "$EXT_TEST_IS_NONFREE" || "$nonfree"; then - ./test.sh "$test_name" "$make_jobs" + ./test.sh "$test_name" "${make_args[@]}" fi ); done