diff --git a/CMakeLists.txt b/CMakeLists.txt index fb56f9bc..1badda2f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,8 +29,18 @@ option(MORE_WARNINGS "Turn on more warnings" OFF) # Ignored on MSVC if(MSVC) # MSVC's standard library triggers warning C5105, # "macro expansion producing 'defined' has undefined behavior" - add_compile_options(/std:c11 /W1 /MP /wd5105) + add_compile_options(/MP /wd5105) add_definitions(/D_CRT_SECURE_NO_WARNINGS) + # Also, CMake appears not to pass the C11-enabling flag, so we must add it manually... but only for C! + if(NOT CMAKE_C_FLAGS MATCHES "std:c11") # The flag may already have been injected by an earlier CMake invocation, so don't add it twice + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std:c11" CACHE STRING "Flags used by the C compiler during all build types." FORCE) + endif() + + if(SANITIZERS) + set(SAN_FLAGS /fsanitize=address) + add_compile_options(${SAN_FLAGS}) + add_link_options(${SAN_FLAGS}) + endif() else() add_compile_options(-Wall -pedantic) add_definitions(-D_POSIX_C_SOURCE=200809L -D_ISOC11_SOURCE) @@ -41,7 +51,7 @@ else() -fsanitize=object-size -fsanitize=bool -fsanitize=enum -fsanitize=alignment -fsanitize=null -fsanitize=address) add_compile_options(${SAN_FLAGS}) - link_libraries(${SAN_FLAGS}) + add_link_options(${SAN_FLAGS}) # A non-zero optimization level is desired in debug mode, but allow overriding it nonetheless # TODO: this overrides anything previously set... that's a bit sloppy! set(CMAKE_C_FLAGS_DEBUG "-g -Og -fno-omit-frame-pointer -fno-optimize-sibling-calls" CACHE STRING "" FORCE) diff --git a/Makefile b/Makefile index 9330fa62..7e3b6cd5 100644 --- a/Makefile +++ b/Makefile @@ -129,7 +129,7 @@ rgbgfx: ${rgbgfx_obj} $Q${CXX} ${REALLDFLAGS} ${PNGLDFLAGS} -o $@ ${rgbgfx_obj} ${REALCXXFLAGS} -x c++ src/version.c ${PNGLDLIBS} test/gfx/randtilegen: test/gfx/randtilegen.c - $Q${CC} ${REALLDFLAGS} ${PNGLDFLAGS} -o $@ $^ ${REALCFLAGS} -Wno-vla ${PNGCFLAGS} ${PNGLDLIBS} + $Q${CC} ${REALLDFLAGS} ${PNGLDFLAGS} -o $@ $^ ${REALCFLAGS} ${PNGCFLAGS} ${PNGLDLIBS} test/gfx/rgbgfx_test: test/gfx/rgbgfx_test.cpp $Q${CXX} ${REALLDFLAGS} ${PNGLDFLAGS} -o $@ $^ ${REALCXXFLAGS} ${PNGLDLIBS} diff --git a/include/platform.h b/include/platform.h index f6bdbf9e..bb2fcc65 100644 --- a/include/platform.h +++ b/include/platform.h @@ -46,12 +46,14 @@ # include #endif -/* MSVC doesn't support `[static N]` for array arguments from C99 */ +/* MSVC doesn't support `[static N]` for array arguments from C99 or C11 */ #ifdef _MSC_VER # define MIN_NB_ELMS(N) +# define ARR_QUALS(...) # define NONNULL(ptr) *ptr #else # define MIN_NB_ELMS(N) static (N) +# define ARR_QUALS(...) __VA_ARGS__ # define NONNULL(ptr) ptr[static 1] #endif diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index c8baf0d7..4044e66d 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,8 +1,5 @@ add_executable(randtilegen gfx/randtilegen.c) -if(NOT MSVC) - target_compile_options(randtilegen PRIVATE -Wno-vla) -endif() add_executable(rgbgfx_test gfx/rgbgfx_test.cpp) diff --git a/test/gfx/randtilegen.c b/test/gfx/randtilegen.c index 0a56c14d..639ace57 100644 --- a/test/gfx/randtilegen.c +++ b/test/gfx/randtilegen.c @@ -14,7 +14,7 @@ */ #include -#include +#include #include #include #include @@ -22,41 +22,138 @@ #include #include -#include "helpers.h" +#include "platform.h" -FILE *rngRecorder; // File to which the random bytes will be read -uint32_t randBits = 0; // Storage for bits read from the input stream but not yet used -uint8_t randCount = 0; // How many bits are currently stored in the above +#define STR(x) #x +#define XSTR(x) STR(x) -static uint32_t getRandomBits(uint8_t count) { - // Trying to read one more byte with `randCount` at least this high will drop some bits! - // If the count is no higher than that limit, then the loop is guaranteed to exit without - // reading more bytes. - assert(count <= sizeof(randBits) * 8 + 1); +struct Attributes { + unsigned char palette; + unsigned char nbColors; +}; - // Read bytes until we have enough bits to serve the request - while (count > randCount) { - int data = getchar(); +static unsigned long long randbits = 0; +static unsigned char randcount = 0; + +static _Noreturn void fatal(char const *error) { + fprintf(stderr, "FATAL: %s\n", error); + exit(1); +} + +static FILE *seed; + +static unsigned long long getRandomBits(unsigned count) { + while (count > randcount) { + // Get new random bytes from stdin (assumed to be a stream of random data) to fulfill the + // random bits request + int data = getc(seed); if (data == EOF) { exit(0); } - randBits |= (uint32_t)data << randCount; - randCount += 8; - fputc(data, rngRecorder); + randbits |= (unsigned long long)data << randcount; + randcount += 8; } - - uint32_t result = randBits & (((uint32_t)1 << count) - 1); - randBits >>= count; - randCount -= count; + unsigned long long result = randbits & ((1ull << count) - 1); + randbits >>= count; + randcount -= count; return result; } -/** - * Flush any remaining bits in the RNG storage - */ -static void flushRng(void) { - randCount = 0; - randBits = 0; +static void generate_tile_attributes(struct Attributes * restrict attributes) { + /* + * Images have ten colors, grouped into two groups of 5 colors. The palette index indicates two + * things: which one of those groups will be used, and which colors out of those 5 will be used + * by the tile. The low bit indicates the group, and the rest of the value indicates the subset + * of colors. The remainder of the number is treated as a bitfield, where each bit represents a + * color: for instance, a value of 13 in the upper bits (binary 01101) indicates that colors 0, + * 2 and 3 from that group will be used. Values of 0 and 31 are naturally invalid because they + * indicate zero and five colors respectively, and 30 is also excluded to ensure that the + * particular subset of colors 1, 2, 3 and 4 never shows up. This guarantees that every tile + * will be representable using a palette containing color 0 (since those that don't contain + * color 0 will have three colors at most), which in turn ensures that only 4 palettes per group + * (and thus 8 total) are needed to cover the image: 0, 1, 2, 3; 0, 1, 2, 4; 0, 1, 3, 4; and 0, + * 2, 3, 4. This also implies that making color 0 transparent (in both groups) adds a + * transparent color to every palette. + */ + unsigned char pal; + do { + pal = getRandomBits(5); + } while (pal == 0 || (pal > 29)); + attributes->palette = 2 * pal + getRandomBits(1); + + // Use an array to look up the number of colors in the palette; this is faster (and simpler) + // than doing a population count over the bits + static char const popcount[] = {0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, + 4, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4}; + attributes->nbColors = popcount[pal]; +} + +static void generate_tile_data(unsigned char tiledata[ARR_QUALS(restrict) MIN_NB_ELMS(8)][8], + unsigned colorcount) { + switch (colorcount) { + case 2: // 1bpp + for (uint8_t y = 0; y < 8; y++) { + for (uint8_t x = 0; x < 8; x++) { + tiledata[y][x] = getRandomBits(1); + } + } + break; + + case 4: // 2bpp + for (uint8_t y = 0; y < 8; y++) { + for (uint8_t x = 0; x < 8; x++) { + tiledata[y][x] = getRandomBits(2); + } + } + break; + + case 3: // 2bpp with resampling + for (uint8_t y = 0; y < 8; y++) { + for (uint8_t x = 0; x < 8; x++) { + do { + tiledata[y][x] = getRandomBits(2); + } while (tiledata[y][x] == 3); + } + } + } +} + +// Can't mark as `const`, as the array type is otherwise not compatible (augh) +static void + copy_tile_data(unsigned char destination[ARR_QUALS(restrict) MIN_NB_ELMS(8)][8], + unsigned char /* const */ source[ARR_QUALS(restrict) MIN_NB_ELMS(8)][8]) { + // Apply a random rotation to the copy + // coord ^ 7 = inverted coordinate; coord ^ 0 = regular coordinate + unsigned xmask = getRandomBits(1) * 7; + unsigned ymask = getRandomBits(1) * 7; + for (unsigned y = 0; y < 8; y++) { + for (unsigned x = 0; x < 8; x++) { + destination[y][x] = source[y ^ ymask][x ^ xmask]; + } + } +} + +static void generate_palettes(uint16_t palettes[ARR_QUALS(restrict) MIN_NB_ELMS(60)][4]) { + uint16_t colors[10]; + // Generate 10 random colors (two groups of 5 colors) + for (unsigned p = 0; p < 10; p++) { + colors[p] = getRandomBits(15); + } + // Potentially make the first color of each group transparent + if (!getRandomBits(2)) { + colors[0] |= 0x8000; + colors[5] |= 0x8000; + } + + for (unsigned p = 0; p < 60; p++) { + uint16_t const *group = colors + 5 * (p & 1); + uint16_t *palette = palettes[p]; + for (unsigned index = 0; index < 5; index++) { + if (p & (2 << index)) { + *(palette++) = group[index]; + } + } + } } /** @@ -66,15 +163,26 @@ static uint8_t _5to8(uint8_t five) { return five << 3 | five >> 2; } -struct Attribute { - unsigned char palette; - unsigned char nbColors; -}; -#define NB_TILES 10 * 10 - -static void writePng(png_structp png, png_infop pngInfo, uint8_t width, uint8_t height, uint16_t palettes[][4], struct Attribute const *attributes, uint8_t tileData[][8][8]) { +// Can't mark as `const`, as the array type is otherwise not compatible (augh) +static void write_image(char const *filename, uint16_t /* const */ palettes[MIN_NB_ELMS(60)][4], + unsigned char /* const */ (*tileData)[8][8], + struct Attributes const *attributes, uint8_t width, uint8_t height) { uint8_t const nbTiles = width * height; - + png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + png_infop pngInfo = png_create_info_struct(png); + + if (setjmp(png_jmpbuf(png))) { + fprintf(stderr, "FATAL: An error occurred while writing image \"%s\"", filename); + exit(1); + } + + FILE *file = fopen(filename, "wb"); + if (file == NULL) { + fprintf(stderr, "FATAL: Failed to open \"%s\": %s\n", filename, strerror(errno)); + exit(1); + } + png_init_io(png, file); + png_set_IHDR(png, pngInfo, width * 8, height * 8, 8, PNG_COLOR_TYPE_RGB_ALPHA, getRandomBits(1) ? PNG_INTERLACE_NONE : PNG_INTERLACE_ADAM7, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); @@ -82,19 +190,24 @@ static void writePng(png_structp png, png_infop pngInfo, uint8_t width, uint8_t // While it would be nice to write the image little by little, I really don't want to handle // interlacing myself. (We're doing interlacing to test that RGBGFX correctly handles it.) uint8_t const SIZEOF_PIXEL = 4; // Each pixel is 4 bytes (RGBA @ 8 bits/component) - uint8_t data[height * 8 * width * 8 * SIZEOF_PIXEL]; - uint8_t *rowPtrs[height * 8]; + assert(width != 0); + assert(height != 0); + uint8_t *data = malloc(height * 8 * width * 8 * SIZEOF_PIXEL); + uint8_t **rowPtrs = malloc(height * 8 * sizeof(*rowPtrs)); + if (data == NULL || rowPtrs == NULL) { + fatal("Out of memory"); + } for (uint8_t y = 0; y < height * 8; ++y) { rowPtrs[y] = &data[y * width * 8 * SIZEOF_PIXEL]; } for (uint8_t p = 0; p < nbTiles; p++) { - uint8_t tx = 8 * (p % width), ty = 8 * (p / width); + uint8_t const tx = 8 * (p % width), ty = 8 * (p / width); for (uint8_t y = 0; y < 8; y++) { uint8_t * const row = rowPtrs[ty + y]; for (uint8_t x = 0; x < 8; x++) { uint8_t * const pixel = &row[(tx + x) * SIZEOF_PIXEL]; - uint16_t color = palettes[attributes[p].palette][tileData[p][y][x]]; + uint16_t const color = palettes[attributes[p].palette][tileData[p][y][x]]; pixel[0] = _5to8(color & 0x1F); pixel[1] = _5to8(color >> 5 & 0x1F); pixel[2] = _5to8(color >> 10 & 0x1F); @@ -102,174 +215,106 @@ static void writePng(png_structp png, png_infop pngInfo, uint8_t width, uint8_t } } } + png_set_rows(png, pngInfo, rowPtrs); png_write_png(png, pngInfo, PNG_TRANSFORM_IDENTITY, NULL); + fclose(file); + free(rowPtrs); + free(data); + png_destroy_write_struct(&png, &pngInfo); } -static void generate_random_image(png_structp png, png_infop pngInfo) { - struct Attribute attributes[NB_TILES]; - uint8_t tileData[NB_TILES][8][8]; - // These two are in tiles, not pixels, and in range [3; 10], hence `NB_TILES` above - // Both width and height are 4-bit values, so nbTiles is 8-bit (OK!) - uint8_t const width = getRandomBits(3) + 3, height = getRandomBits(3) + 3, - nbTiles = width * height; - - for (uint8_t p = 0; p < nbTiles; p++) { - uint8_t pal; - do { - pal = getRandomBits(5); - } while (pal == 0 || (pal > 29)); - attributes[p].palette = 2 * pal + getRandomBits(1); - // Population count (nb of bits set), the simple way - static uint8_t const popcount[] = {1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, - 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4}; - attributes[p].nbColors = popcount[pal - 1]; - // Handle single-color tiles the simple way, without trying to pull more random bits - if (attributes[p].nbColors < 2) { - memset(tileData[p], 0, sizeof(tileData[p])); +static void generate_random_image(char const *filename) { +#define MIN_TILES_PER_SIDE 3 +#define MAX_TILES ((MIN_TILES_PER_SIDE + 7) * (MIN_TILES_PER_SIDE + 7)) + struct Attributes attributes[MAX_TILES]; + unsigned char tileData[MAX_TILES][8][8]; + uint8_t width = getRandomBits(3) + MIN_TILES_PER_SIDE, + height = getRandomBits(3) + MIN_TILES_PER_SIDE; + for (uint8_t tile = 0; tile < (width * height); tile++) { + generate_tile_attributes(attributes + tile); + // If a tile contains only one color, then there's no tile data to generate: all pixels will + // use color 0 + if (attributes[tile].nbColors < 2) { + memset(tileData[tile], 0, sizeof(tileData[tile])); continue; } - uint8_t index, total; - for (index = 0, total = 0; index < p; index++) { - if (attributes[index].nbColors == attributes[p].nbColors) { + + // Find tiles with the same number of colors + unsigned index = 0, total = 0; + for (; index < tile; index++) { + if (attributes[index].nbColors == attributes[tile].nbColors) { total++; } } - // index == p at exit + assert(index == tile); // This is used as a sentinel later on to indicate no tile was found + if (total) { + // If there are such tiles, there's a random chance that this tile will replicate one of + // those tiles (potentially rotated) index = getRandomBits(8); if (index < total) { total = index + 1; for (index = 0; total; index++) { - if (attributes[index].nbColors == attributes[p].nbColors) { + if (attributes[index].nbColors == attributes[tile].nbColors) { total--; } - if (!total) { - index--; - } + } + if (total == 0) { + index--; } } else { - index = p; + index = tile; // Restore the sentinel } } - if (index != p) { - unsigned rotation = getRandomBits(2); - for (uint8_t y = 0; y < 8; y++) { - for (uint8_t x = 0; x < 8; x++) { - tileData[p][y][x] = - tileData[index][y ^ ((rotation & 2) ? 7 : 0)][x ^ ((rotation & 1) ? 7 : 0)]; - } - } - } else { - switch (attributes[p].nbColors) { - case 2: // Two-color tiles only need one random bit per pixel - for (uint8_t y = 0; y < 8; y++) - for (uint8_t x = 0; x < 8; x++) - tileData[p][y][x] = getRandomBits(1); - break; - case 4: // 4-color tiles can use two random bits per pixel - for (uint8_t y = 0; y < 8; y++) - for (uint8_t x = 0; x < 8; x++) - tileData[p][y][x] = getRandomBits(2); - break; - case 3: // 3-color tiles must draw two random bits, but reject them if out of range - for (uint8_t y = 0; y < 8; y++) { - for (uint8_t x = 0; x < 8; x++) { - do { - index = getRandomBits(2); - } while (index == 3); - tileData[p][y][x] = index; - } - } - break; - default: // 1-color tiles were handled earlier - unreachable_(); - } - } - } - uint16_t colors[10]; - for (uint8_t p = 0; p < 10; p++) { - colors[p] = getRandomBits(15); - } - // Randomly make color #0 of all palettes transparent - if (!getRandomBits(2)) { - colors[0] |= 0x8000; - colors[5] |= 0x8000; + if (index == tile) { + generate_tile_data(tileData[tile], attributes[index].nbColors); + } else { + copy_tile_data(tileData[tile], tileData[index]); + } } uint16_t palettes[60][4]; - for (uint8_t p = 0; p < 60; p++) { - uint16_t const *subpal = &colors[p & 1 ? 5 : 0]; - uint8_t total = 0; - for (uint8_t index = 0; index < 5; index++) { - if (p & (2 << index)) { - palettes[p][total++] = subpal[index]; - } - } - } - - writePng(png, pngInfo, width, height, palettes, attributes, tileData); + generate_palettes(palettes); + write_image(filename, palettes, tileData, attributes, width, height); } int main(int argc, char **argv) { - if (argc < 2) { - fputs("usage: randtilegen [ [...]]\n", stderr); + if (argc < 3 || argc > 4) { + fprintf(stderr, "usage: %s []\n", argv[0]); return 2; } - size_t maxBasenameLen = 0; - for (int index = 1; index < argc; index++) { - size_t length = strlen(argv[index]); - if (length > maxBasenameLen) { - maxBasenameLen = length; + seed = fopen(argv[1], "rb"); + if (!seed) { + fprintf(stderr, "FATAL: Cannot open seed file (%s)\n", strerror(errno)); + return 1; + } + + size_t const nameLen = strlen(argv[2]); + unsigned long long maxcount = ULLONG_MAX; + if (argc > 3) { + char *error; + maxcount = strtoull(argv[3], &error, 0); + if (*error != '\0') { + fatal("invalid count"); } } - char filename[maxBasenameLen + sizeof("65535.png")]; - for (uint16_t i = 0;; i++) { // 65k images ought to be enough - for (int index = 1; index < argc; index++) { - int len = sprintf(filename, "%s%" PRIu16 ".rng", argv[index], i); - rngRecorder = fopen(filename, "wb"); - if (!rngRecorder) { - perror(filename); - return 1; - } - - filename[len - 3] = 'p'; // `.rng` -> `.png` - FILE *img = fopen(filename, "wb"); - if (!img) { - perror(filename); - return 1; - } - png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - if (!png) { - perror("png_create_write_struct"); - return 1; - } - png_infop pngInfo = png_create_info_struct(png); - if (!pngInfo) { - perror("png_create_info_struct"); - return 1; - } - if (setjmp(png_jmpbuf(png))) { - fprintf(stderr, "FATAL: an error occurred while writing image \"%s\"\n", filename); - return 1; - } - - // Ensure that image generation starts on byte boundaries - // (This is necessary so that all involved random bits are recorded in the `.rng` file) - flushRng(); - - png_init_io(png, img); - generate_random_image(png, pngInfo); - png_destroy_write_struct(&png, &pngInfo); - fclose(img); - fclose(rngRecorder); - } - - if (i == UINT16_MAX) { - break; - } + char *filename = malloc(nameLen + sizeof(XSTR(ULLONG_MAX) ".png")); + if (!filename) { + fatal("out of memory"); } + memcpy(filename, argv[2], nameLen); + + for (unsigned long long count = 0; count < maxcount; count++) { + sprintf(&filename[nameLen], "%llu.png", count); + generate_random_image(filename); + // Reset the global random state so that subsequent images don't share a random byte + randbits = 0; + randcount = 0; + } + + return 0; } diff --git a/test/gfx/rgbgfx_test.cpp b/test/gfx/rgbgfx_test.cpp index 4a8aabd2..0c41112e 100644 --- a/test/gfx/rgbgfx_test.cpp +++ b/test/gfx/rgbgfx_test.cpp @@ -1,5 +1,18 @@ -#include -#include + +// For `execProg` (Windows is its special little snowflake again) +#ifndef _MSC_VER + #include + #include + + #include + #include +#else + #define WIN32_LEAN_AND_MEAN // Include less from `windows.h` to avoid conflicts + #include + #include + #include + #undef max // This macro conflicts with `std::numeric_limits<...>::max()` +#endif #include #include @@ -10,11 +23,10 @@ #include #include #include -#include #include #include #include -#include +#include #include #include "defaultinitalloc.hpp" @@ -267,12 +279,12 @@ public: ~Png() { png_destroy_read_struct(&png, &info, nullptr); } }; -static int execProg(char const *name, char * const *argv, - posix_spawn_file_actions_t const *actions = nullptr) { +static char *execProg(char const *name, char * const *argv) { +#ifndef _MSC_VER pid_t pid; - int err = posix_spawn(&pid, argv[0], actions, nullptr, argv, nullptr); + int err = posix_spawn(&pid, argv[0], nullptr, nullptr, argv, nullptr); if (err != 0) { - return err; + return strerror(err); } siginfo_t info; @@ -286,7 +298,74 @@ static int execProg(char const *name, char * const *argv, fatal("%s returned with status %d", name, info.si_status); } - return 0; +#else /* defined(_MSC_VER) */ + + auto winStrerror = [](DWORD errnum) { + LPTSTR buf; + if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM + | FORMAT_MESSAGE_MAX_WIDTH_MASK, + nullptr, errnum, 0, (LPTSTR)&buf, 0, nullptr) + == 0) { + fatal("Failed to get error message for error 0x%x", errnum); + } + return buf; + }; + + char cmdLine[32768]; // Max command line size on Windows + char *ptr = cmdLine; + for (size_t i = 0; argv[i]; ++i) { + char const *src = argv[i]; + // I miss you, `stpcpy` + while (*src) { + *ptr++ = *src++; + } + *ptr++ = ' '; + } + *ptr = '\0'; + + STARTUPINFOA startupInfo; + GetStartupInfoA(&startupInfo); + STARTUPINFOA childStartupInfo{sizeof(startupInfo), + nullptr, + nullptr, + nullptr, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + nullptr, + 0, + 0, + 0}; + + PROCESS_INFORMATION child; + if (CreateProcessA(nullptr, cmdLine, nullptr, nullptr, true, 0, nullptr, nullptr, + &childStartupInfo, &child) + == 0) { + return winStrerror(GetLastError()); + } + + DWORD status; + do { + if (GetExitCodeProcess(child.hProcess, &status) == 0) { + fatal("Error waiting for %s: %ls", name, winStrerror(GetLastError())); + } + } while (status == STILL_ACTIVE); + CloseHandle(child.hProcess); + CloseHandle(child.hThread); + + if (status != 0) { + fatal("%s returned with status %ld", name, status); + } +#endif + + return nullptr; } int main(int argc, char **argv) { @@ -296,20 +375,13 @@ int main(int argc, char **argv) { } { - posix_spawn_file_actions_t action; - // Putting these directly in the array makes them const or something. char path[] = "./randtilegen", file[] = "out"; - char *args[] = {path, file, nullptr}; + char *args[] = {path, argv[1], file, nullptr}; - posix_spawn_file_actions_init(&action); - posix_spawn_file_actions_addopen(&action, 0, argv[1], O_RDONLY, 0); - - if (int ret = execProg("randtilegen", args, &action); ret != 0) { + if (auto ret = execProg("randtilegen", args); ret != nullptr) { fatal("Failed to excute ./randtilegen (%s). Is it in the current working directory?", - strerror(ret)); + ret); } - - posix_spawn_file_actions_destroy(&action); } { @@ -321,9 +393,8 @@ int main(int argc, char **argv) { // Also copy the trailing `nullptr` std::copy_n(&argv[2], argc - 1, std::back_inserter(args)); - if (int ret = execProg("rgbgfx conversion", args.data()); ret != 0) { - fatal("Failed to execute ../../rgbgfx (%s). Is it in the parent directory?", - strerror(ret)); + if (auto ret = execProg("rgbgfx conversion", args.data()); ret != nullptr) { + fatal("Failed to execute ../../rgbgfx (%s). Is it in the parent directory?", ret); } } @@ -340,8 +411,8 @@ int main(int argc, char **argv) { // Also copy the trailing `nullptr` std::copy_n(&argv[2], argc - 1, std::back_inserter(args)); - if (int ret = execProg("rgbgfx reversal", args.data()); ret != 0) { - fatal("Failed to execute ../../rgbgfx -r (%s)", strerror(ret)); + if (auto ret = execProg("rgbgfx reversal", args.data()); ret != nullptr) { + fatal("Failed to execute ../../rgbgfx -r (%s)", ret); } }