From fa3c06e47cc6e39e1c991df711b0f38b18ed0d9d Mon Sep 17 00:00:00 2001 From: beardypig Date: Sun, 20 Oct 2024 00:08:28 +0200 Subject: [PATCH] modernize vectgif --- vcpkg.json | 3 +- vectgif/CMakeLists.txt | 5 +- vectgif/getopt.h | 77 ---------------------- vectgif/main.cpp | 144 ++++++++++++++++++++++------------------- 4 files changed, 83 insertions(+), 146 deletions(-) delete mode 100644 vectgif/getopt.h diff --git a/vcpkg.json b/vcpkg.json index b9140f2..7c6cdfb 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -1,6 +1,7 @@ { "dependencies": [ "catch2", - "trompeloeil" + "trompeloeil", + "cxxopts" ] } diff --git a/vectgif/CMakeLists.txt b/vectgif/CMakeLists.txt index ade3333..5f456e4 100644 --- a/vectgif/CMakeLists.txt +++ b/vectgif/CMakeLists.txt @@ -1,3 +1,5 @@ +find_package(cxxopts CONFIG REQUIRED) + add_executable(vectgif main.cpp gif.h @@ -11,4 +13,5 @@ else() set(LIBRETRO_SRC vectrexia_libretro) endif() -target_link_libraries(vectgif ${LIBRETRO_SRC}) +target_link_libraries(vectgif PRIVATE ${LIBRETRO_SRC}) +target_link_libraries(vectgif PRIVATE cxxopts::cxxopts) diff --git a/vectgif/getopt.h b/vectgif/getopt.h deleted file mode 100644 index 7132e3f..0000000 --- a/vectgif/getopt.h +++ /dev/null @@ -1,77 +0,0 @@ -/* A minimal POSIX getopt() implementation in ANSI C - * - * This is free and unencumbered software released into the public domain. - * - * This implementation supports the convention of resetting the option - * parser by assigning optind to 0. This resets the internal state - * appropriately. - * - * Ref: http://pubs.opengroup.org/onlinepubs/9699919799/functions/getopt.html - */ -#ifndef GETOPT_H -#define GETOPT_H - -#include -#include -#include - -extern int optind; -extern int opterr; -extern int optopt; -extern char *optarg; - -static int -getopt(int argc, char * const argv[], const char *optstring) -{ - static int optpos = 1; - const char *arg; - (void)argc; - - /* Reset? */ - if (optind == 0) { - optind = 1; - optpos = 1; - } - - arg = argv[optind]; - if (arg && strcmp(arg, "--") == 0) { - optind++; - return -1; - } else if (!arg || arg[0] != '-' || !isalnum(arg[1])) { - return -1; - } else { - const char *opt = strchr(optstring, arg[optpos]); - optopt = arg[optpos]; - if (!opt) { - if (opterr && *optstring != ':') - fprintf(stderr, "%s: illegal option: %c\n", argv[0], optopt); - return '?'; - } else if (opt[1] == ':') { - if (arg[optpos + 1]) { - optarg = (char *)arg + optpos + 1; - optind++; - optpos = 1; - return optopt; - } else if (argv[optind + 1]) { - optarg = (char *)argv[optind + 1]; - optind += 2; - optpos = 1; - return optopt; - } else { - if (opterr && *optstring != ':') - fprintf(stderr, - "%s: option requires an argument: %c\n", - argv[0], optopt); - return *optstring == ':' ? ':' : '?'; - } - } else { - if (!arg[++optpos]) { - optind++; - optpos = 1; - } - return optopt; - } - } -} - -#endif diff --git a/vectgif/main.cpp b/vectgif/main.cpp index 9977d38..eeb6f39 100644 --- a/vectgif/main.cpp +++ b/vectgif/main.cpp @@ -1,92 +1,102 @@ #include #include +#include +#include +#include +#include +#include +#include +#include #include #include "gif.h" -#include "getopt.h" +#include + +constexpr size_t ROM_SIZE = 65536; +constexpr size_t MAX_FILENAME_SIZE = 2000; std::unique_ptr vectrex = std::make_unique(); -std::array gif_buffer{}; +std::array gif_buffer{}; int main(int argc, char *argv[]) { - int c = 0; - int ch = 0; - int r = 0; - long skipframes = 0; - long outframes = 1000; - FILE *romfile; - char giffilename[2000]{}; - uint8_t rombuffer[65536]{}; - GifWriter gw = {}; - - opterr = 0; - while ((c = getopt(argc, argv, "s:n:")) != -1) { - switch (c) { - case 's':skipframes = strtol(optarg, nullptr, 10); - break; - case 'n':outframes = strtol(optarg, nullptr, 10); - break; - default:abort(); + long skipframes = 0; + long outframes = 1000; + std::array rombuffer{}; + GifWriter gw{}; + + // Parse command line arguments using cxxopts + cxxopts::Options options("vectgif", "Generate a GIF from a Vectrex ROM"); + options.add_options() + ("s,skipframes", "Number of frames to skip", cxxopts::value()->default_value("0")) + ("n,outframes", "Number of output frames", cxxopts::value()->default_value("1000")) + ("rom", "ROM file", cxxopts::value()) + ("gif", "GIF output file", cxxopts::value()->default_value("")); + + auto result = options.parse(argc, argv); + + skipframes = result["skipframes"].as(); + outframes = result["outframes"].as(); + + if (!result.count("rom")) { + std::cerr << "vectgif: usage: vectgif [gif]\n"; + return 1; } - } - - auto nargs = (argc - optind); - if (nargs < 1 || nargs > 2) { - fprintf(stderr, "vectgif: usage: vectgif [gif]\n"); - return 1; - } - // load the ROM file - printf("[ROM]: loading ROM \"%s\"\n", argv[optind]); - romfile = fopen(argv[optind], "rb"); + std::string rom_filename = result["rom"].as(); + std::string giffilename; + if (result.count("gif") && !result["gif"].as().empty()) { + giffilename = result["gif"].as(); + } else { + giffilename = std::format("{}.gif", rom_filename); + } - while( ( ch = fgetc(romfile) ) != EOF && r < 65536) { - rombuffer[r++] = static_cast(ch); - } - fclose(romfile); + // Load the ROM file + std::ifstream romfile(rom_filename, std::ios::binary); + if (!romfile) { + std::cerr << "[ROM]: Failed to open ROM file " << rom_filename << "\n"; + return 1; + } - printf("[ROM]: size = %d\n", r); + std::cout << std::format("[ROM]: loading ROM \"{}\"\n", rom_filename); + romfile.read(reinterpret_cast(rombuffer.data()), ROM_SIZE); + auto r = static_cast(romfile.gcount()); + romfile.close(); - if (!vectrex->LoadCartridge(rombuffer, static_cast(r))) { - printf("Failed to load the ROM file\n"); - return 1; - } + std::cout << std::format("[ROM]: size = {}\n", r); - if (nargs == 1) { - snprintf(giffilename, sizeof(giffilename), "%s.gif", argv[optind]); - } - else { - snprintf(giffilename, sizeof(giffilename), "%s", argv[optind + 1]); - } + if (!vectrex->LoadCartridge(rombuffer.data(), r)) { + std::cerr << "Failed to load the ROM file\n"; + return 1; + } - GifBegin(&gw, giffilename, FRAME_WIDTH, FRAME_HEIGHT, 2, 8, false); + GifBegin(&gw, giffilename.c_str(), FRAME_WIDTH, FRAME_HEIGHT, 2, 8, false); - vectrex->Reset(); + vectrex->Reset(); - vectrex->SetPlayerOne(0x80, 0x80, 1, 1, 1, 1); - vectrex->SetPlayerTwo(0x80, 0x80, 1, 1, 1, 1); + vectrex->SetPlayerOne(0x80, 0x80, 1, 1, 1, 1); + vectrex->SetPlayerTwo(0x80, 0x80, 1, 1, 1, 1); - for (int frame = 0; frame < outframes; frame++) { - for (int s = 0; s < skipframes+1; s++) { - vectrex->Run(30000); - } + for (int frame = 0; frame < outframes; frame++) { + for (int s = 0; s < skipframes + 1; s++) { + vectrex->Run(30000); + } - auto framebuffer = vectrex->getFramebuffer(); + auto framebuffer = vectrex->getFramebuffer(); - auto gb = gif_buffer.begin(); - for (auto &fb : *framebuffer) { - *++gb = static_cast(fb.value * 0xffu); - *++gb = static_cast(fb.value * 0xffu); - *++gb = static_cast(fb.value * 0xffu); - *++gb = static_cast(fb.value * 0xffu); - } - GifWriteFrame(&gw, gif_buffer.data(), FRAME_WIDTH, FRAME_HEIGHT, 2); - if (frame % 100 == 0) { - printf("[VECTREX] frame = %d\n", frame); + auto gb = gif_buffer.begin(); + for (const auto &fb : *framebuffer) { + *gb++ = static_cast(fb.value * 0xffu); + *gb++ = static_cast(fb.value * 0xffu); + *gb++ = static_cast(fb.value * 0xffu); + *gb++ = static_cast(fb.value * 0xffu); + } + GifWriteFrame(&gw, gif_buffer.data(), FRAME_WIDTH, FRAME_HEIGHT, 2); + if (frame % 100 == 0) { + std::cout << std::format("[VECTREX] frame = {}\n", frame); + } } - } - GifEnd(&gw); + GifEnd(&gw); - return 0; + return 0; }