From 770b121b10ba9ff500e1e8ad946d57b87d8e58dc Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 20 Apr 2022 18:53:05 -0700 Subject: [PATCH 01/20] Qt: Handle relative DATADIR --- src/platform/qt/CMakeLists.txt | 6 +++++- src/platform/qt/GBAApp.cpp | 3 +++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/platform/qt/CMakeLists.txt b/src/platform/qt/CMakeLists.txt index fc87441a9..203554f39 100644 --- a/src/platform/qt/CMakeLists.txt +++ b/src/platform/qt/CMakeLists.txt @@ -272,7 +272,11 @@ if(BUILD_GL OR BUILD_GLES2 OR BUILD_EPOXY) endif() install(FILES ${CMAKE_SOURCE_DIR}/res/nointro.dat DESTINATION ${DATADIR} COMPONENT ${BINARY_NAME}-qt) if(NOT WIN32 AND NOT APPLE) - list(APPEND QT_DEFINES DATADIR="${CMAKE_INSTALL_PREFIX}/${DATADIR}") + if(DATADIR MATCHES "^\\.[.\\]") + list(APPEND QT_DEFINES DATADIR="${DATADIR}") + else() + list(APPEND QT_DEFINES DATADIR="${CMAKE_INSTALL_PREFIX}/${DATADIR}") + endif() endif() find_package(Qt5LinguistTools) diff --git a/src/platform/qt/GBAApp.cpp b/src/platform/qt/GBAApp.cpp index 466cefa9f..d53985313 100644 --- a/src/platform/qt/GBAApp.cpp +++ b/src/platform/qt/GBAApp.cpp @@ -207,6 +207,9 @@ QString GBAApp::getOpenDirectoryName(QWidget* owner, const QString& title, const QString GBAApp::dataDir() { #ifdef DATADIR QString path = QString::fromUtf8(DATADIR); + if (path.startsWith("./") || path.startsWith("../")) { + path = QCoreApplication::applicationDirPath() + "/" + path; + } #else QString path = QCoreApplication::applicationDirPath(); #ifdef Q_OS_MAC From 4556d4b121dd482b0438e6d32750c020c7b25081 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 21 Apr 2022 18:42:48 -0700 Subject: [PATCH 02/20] Feature: Modernize argument parsing API --- include/mgba/feature/commandline.h | 21 ++- src/feature/commandline.c | 142 ++++++++++++++------ src/platform/example/client-server/server.c | 8 +- src/platform/qt/ConfigController.cpp | 19 ++- src/platform/qt/ConfigController.h | 11 +- src/platform/qt/Window.cpp | 19 ++- src/platform/qt/Window.h | 4 +- src/platform/qt/main.cpp | 33 ++--- src/platform/sdl/main.c | 22 +-- src/platform/test/fuzz-main.c | 10 +- src/platform/test/perf-main.c | 10 +- src/platform/test/rom-test-main.c | 10 +- 12 files changed, 200 insertions(+), 109 deletions(-) diff --git a/include/mgba/feature/commandline.h b/include/mgba/feature/commandline.h index c543652e9..0f9c543d1 100644 --- a/include/mgba/feature/commandline.h +++ b/include/mgba/feature/commandline.h @@ -31,12 +31,20 @@ struct mArguments { bool showVersion; }; +struct mOption { + const char* name; + bool arg; + char shortEquiv; +}; + struct mCoreConfig; struct mSubParser { const char* usage; bool (*parse)(struct mSubParser* parser, int option, const char* arg); + bool (*parseLong)(struct mSubParser* parser, const char* option, const char* arg); void (*apply)(struct mSubParser* parser, struct mCoreConfig* config); const char* extraOptions; + const struct mOption* longOptions; void* opts; }; @@ -45,15 +53,14 @@ struct mGraphicsOpts { bool fullscreen; }; -bool parseArguments(struct mArguments* args, int argc, char* const* argv, - struct mSubParser* subparser); -void applyArguments(const struct mArguments* args, struct mSubParser* subparser, struct mCoreConfig* config); -void freeArguments(struct mArguments* args); - -void usage(const char* arg0, const char* extraOptions); +void usage(const char* arg0, const char* prologue, const char* epilogue, const struct mSubParser* subparsers, int nSubparsers); void version(const char* arg0); -void initParserForGraphics(struct mSubParser* parser, struct mGraphicsOpts* opts); +bool mArgumentsParse(struct mArguments* args, int argc, char* const* argv, struct mSubParser* subparsers, int nSubparsers); +void mArgumentsApply(const struct mArguments* args, struct mSubParser* subparsers, int nSubparsers, struct mCoreConfig* config); +void mArgumentsDeinit(struct mArguments* args); + +void mSubParserGraphicsInit(struct mSubParser* parser, struct mGraphicsOpts* opts); CXX_GUARD_END diff --git a/src/feature/commandline.c b/src/feature/commandline.c index 6cab35094..72e5f6c26 100644 --- a/src/feature/commandline.c +++ b/src/feature/commandline.c @@ -16,16 +16,12 @@ #include #endif -#define GRAPHICS_OPTIONS "123456f" +#define GRAPHICS_OPTIONS "12345678f" #define GRAPHICS_USAGE \ - "\nGraphics options:\n" \ - " -1 1x viewport\n" \ - " -2 2x viewport\n" \ - " -3 3x viewport\n" \ - " -4 4x viewport\n" \ - " -5 5x viewport\n" \ - " -6 6x viewport\n" \ - " -f Start full-screen" + "Graphics options:\n" \ + " -1, -2, -3, -4, -5, -6, -7, -8 Scale viewport by 1-8 times\n" \ + " -f, --fullscreen Start full-screen\n" \ + " --scale X Scale viewport by X times" static const struct option _options[] = { { "bios", required_argument, 0, 'b' }, @@ -45,7 +41,14 @@ static const struct option _options[] = { { 0, 0, 0, 0 } }; +static const struct mOption _graphicsLongOpts[] = { + { "fullscreen", false, 'f' }, + { "scale", true, '\0' }, + { 0, 0, 0 } +}; + static bool _parseGraphicsArg(struct mSubParser* parser, int option, const char* arg); +static bool _parseLongGraphicsArg(struct mSubParser* parser, const char* option, const char* arg); static void _applyGraphicsArgs(struct mSubParser* parser, struct mCoreConfig* config); static void _tableInsert(struct Table* table, const char* pair) { @@ -65,9 +68,9 @@ static void _tableApply(const char* key, void* value, void* user) { mCoreConfigSetOverrideValue(config, key, value); } -bool parseArguments(struct mArguments* args, int argc, char* const* argv, struct mSubParser* subparser) { +bool mArgumentsParse(struct mArguments* args, int argc, char* const* argv, struct mSubParser* subparsers, int nSubparsers) { int ch; - char options[64] = + char options[128] = "b:c:C:hl:p:s:t:" #ifdef USE_EDITLINE "d" @@ -76,23 +79,51 @@ bool parseArguments(struct mArguments* args, int argc, char* const* argv, struct "g" #endif ; + + struct option longOptions[128] = {0}; + memcpy(longOptions, _options, sizeof(_options)); + memset(args, 0, sizeof(*args)); args->frameskip = -1; args->logLevel = INT_MIN; HashTableInit(&args->configOverrides, 0, free); - if (subparser && subparser->extraOptions) { - // TODO: modularize options to subparsers - strncat(options, subparser->extraOptions, sizeof(options) - strlen(options) - 1); + int lastLongOpt; + + int i, j; + for (i = 0; _options[i].name; ++i); // Seek to end + lastLongOpt = i; + + for (i = 0; i < nSubparsers; ++i) { + if (subparsers[i].extraOptions) { + strncat(options, subparsers[i].extraOptions, sizeof(options) - strlen(options) - 1); + } + if (subparsers[i].longOptions) { + for (j = 0; subparsers[i].longOptions[j].name; ++j) { + longOptions[lastLongOpt].name = subparsers[i].longOptions[j].name; + longOptions[lastLongOpt].has_arg = subparsers[i].longOptions[j].arg ? required_argument : no_argument; + longOptions[lastLongOpt].flag = NULL; + longOptions[lastLongOpt].val = subparsers[i].longOptions[j].shortEquiv; + ++lastLongOpt; + } + } } + bool ok = false; int index = 0; - while ((ch = getopt_long(argc, argv, options, _options, &index)) != -1) { - const struct option* opt = &_options[index]; + while ((ch = getopt_long(argc, argv, options, longOptions, &index)) != -1) { + const struct option* opt = &longOptions[index]; switch (ch) { case '\0': if (strcmp(opt->name, "version") == 0) { args->showVersion = true; } else { - return false; + for (i = 0; i < nSubparsers; ++i) { + if (subparsers[i].parseLong) { + ok = subparsers[i].parseLong(&subparsers[i], opt->name, optarg) || ok; + } + } + if (!ok) { + return false; + } } break; case 'b': @@ -136,11 +167,14 @@ bool parseArguments(struct mArguments* args, int argc, char* const* argv, struct args->savestate = strdup(optarg); break; default: - if (subparser) { - if (!subparser->parse(subparser, ch, optarg)) { - return false; + for (i = 0; i < nSubparsers; ++i) { + if (subparsers[i].parse) { + ok = subparsers[i].parse(&subparsers[i], ch, optarg) || ok; } } + if (!ok) { + return false; + } break; } } @@ -156,7 +190,7 @@ bool parseArguments(struct mArguments* args, int argc, char* const* argv, struct return true; } -void applyArguments(const struct mArguments* args, struct mSubParser* subparser, struct mCoreConfig* config) { +void mArgumentsApply(const struct mArguments* args, struct mSubParser* subparsers, int nSubparsers, struct mCoreConfig* config) { if (args->frameskip >= 0) { mCoreConfigSetOverrideIntValue(config, "frameskip", args->frameskip); } @@ -168,12 +202,15 @@ void applyArguments(const struct mArguments* args, struct mSubParser* subparser, mCoreConfigSetOverrideIntValue(config, "useBios", true); } HashTableEnumerate(&args->configOverrides, _tableApply, config); - if (subparser) { - subparser->apply(subparser, config); + int i; + for (i = 0; i < nSubparsers; ++i) { + if (subparsers[i].apply) { + subparsers[i].apply(&subparsers[i], config); + } } } -void freeArguments(struct mArguments* args) { +void mArgumentsDeinit(struct mArguments* args) { free(args->fname); args->fname = 0; @@ -192,12 +229,14 @@ void freeArguments(struct mArguments* args) { HashTableDeinit(&args->configOverrides); } -void initParserForGraphics(struct mSubParser* parser, struct mGraphicsOpts* opts) { +void mSubParserGraphicsInit(struct mSubParser* parser, struct mGraphicsOpts* opts) { parser->usage = GRAPHICS_USAGE; parser->opts = opts; parser->parse = _parseGraphicsArg; + parser->parseLong = _parseLongGraphicsArg; parser->apply = _applyGraphicsArgs; parser->extraOptions = GRAPHICS_OPTIONS; + parser->longOptions = _graphicsLongOpts; opts->multiplier = 0; opts->fullscreen = false; } @@ -215,6 +254,8 @@ bool _parseGraphicsArg(struct mSubParser* parser, int option, const char* arg) { case '4': case '5': case '6': + case '7': + case '8': if (graphicsOpts->multiplier) { return false; } @@ -225,6 +266,18 @@ bool _parseGraphicsArg(struct mSubParser* parser, int option, const char* arg) { } } +bool _parseLongGraphicsArg(struct mSubParser* parser, const char* option, const char* arg) { + struct mGraphicsOpts* graphicsOpts = parser->opts; + if (strcmp(option, "scale") == 0) { + if (graphicsOpts->multiplier) { + return false; + } + graphicsOpts->multiplier = atoi(arg); + return graphicsOpts->multiplier != 0; + } + return false; +} + void _applyGraphicsArgs(struct mSubParser* parser, struct mCoreConfig* config) { struct mGraphicsOpts* graphicsOpts = parser->opts; if (graphicsOpts->fullscreen) { @@ -232,25 +285,36 @@ void _applyGraphicsArgs(struct mSubParser* parser, struct mCoreConfig* config) { } } -void usage(const char* arg0, const char* extraOptions) { +void usage(const char* arg0, const char* prologue, const char* epilogue, const struct mSubParser* subparsers, int nSubparsers) { printf("usage: %s [option ...] file\n", arg0); - puts("\nGeneric options:"); - puts(" -b, --bios FILE GBA BIOS file to use"); - puts(" -c, --cheats FILE Apply cheat codes from a file"); - puts(" -C, --config OPTION=VALUE Override config value"); + if (prologue) { + puts(prologue); + } + puts("\nGeneric options:\n" + " -b, --bios FILE GBA BIOS file to use\n" + " -c, --cheats FILE Apply cheat codes from a file\n" + " -C, --config OPTION=VALUE Override config value\n" #ifdef USE_EDITLINE - puts(" -d, --debug Use command-line debugger"); + " -d, --debug Use command-line debugger\n" #endif #ifdef USE_GDB_STUB - puts(" -g, --gdb Start GDB session (default port 2345)"); + " -g, --gdb Start GDB session (default port 2345)\n" #endif - puts(" -l, --log-level N Log level mask"); - puts(" -t, --savestate FILE Load savestate when starting"); - puts(" -p, --patch FILE Apply a specified patch file when running"); - puts(" -s, --frameskip N Skip every N frames"); - puts(" --version Print version and exit"); - if (extraOptions) { - puts(extraOptions); + " -l, --log-level N Log level mask\n" + " -t, --savestate FILE Load savestate when starting\n" + " -p, --patch FILE Apply a specified patch file when running\n" + " -s, --frameskip N Skip every N frames\n" + " --version Print version and exit" + ); + int i; + for (i = 0; i < nSubparsers; ++i) { + if (subparsers[i].usage) { + puts(""); + puts(subparsers[i].usage); + } + } + if (epilogue) { + puts(epilogue); } } diff --git a/src/platform/example/client-server/server.c b/src/platform/example/client-server/server.c index fce25888c..ba0347b69 100644 --- a/src/platform/example/client-server/server.c +++ b/src/platform/example/client-server/server.c @@ -16,14 +16,14 @@ int main(int argc, char** argv) { // Arguments from the command line are parsed by the parseArguments function. // The NULL here shows that we don't give it any arguments beyond the default ones. struct mArguments args = {}; - bool parsed = parseArguments(&args, argc, argv, NULL); + bool parsed = mArgumentsParse(&args, argc, argv, NULL, 0); // Parsing can succeed without finding a filename, but we need one. if (!args.fname) { parsed = false; } if (!parsed || args.showHelp) { // If parsing failed, or the user passed --help, show usage. - usage(argv[0], NULL); + usage(argv[0], NULL, NULL, NULL, 0); didFail = !parsed; goto cleanup; } @@ -65,7 +65,7 @@ int main(int argc, char** argv) { SocketSubsystemDeinit(); cleanup: - freeArguments(&args); + mArgumentsDeinit(&args); return didFail; } @@ -114,7 +114,7 @@ bool _mExampleRun(const struct mArguments* args, Socket client) { // loaded into the config system, as well as manually overriding the // "idleOptimization" setting to ensure cores that can detect idle loops // will attempt the detection. - applyArguments(args, NULL, &core->config); + mArgumentsApply(args, NULL, 0, &core->config); mCoreConfigSetDefaultValue(&core->config, "idleOptimization", "detect"); // Tell the core to apply the configuration in the associated config object. diff --git a/src/platform/qt/ConfigController.cpp b/src/platform/qt/ConfigController.cpp index e6162237b..89ccae253 100644 --- a/src/platform/qt/ConfigController.cpp +++ b/src/platform/qt/ConfigController.cpp @@ -130,13 +130,22 @@ ConfigController::ConfigController(QObject* parent) ConfigController::~ConfigController() { mCoreConfigDeinit(&m_config); mCoreConfigFreeOpts(&m_opts); + + if (m_parsed) { + mArgumentsDeinit(&m_args); + } } -bool ConfigController::parseArguments(mArguments* args, int argc, char* argv[], mSubParser* subparser) { - if (::parseArguments(args, argc, argv, subparser)) { +bool ConfigController::parseArguments(int argc, char* argv[]) { + if (m_parsed) { + return false; + } + mSubParserGraphicsInit(&m_subparsers[0], &m_graphicsOpts); + if (mArgumentsParse(&m_args, argc, argv, m_subparsers.data(), m_subparsers.size())) { mCoreConfigFreeOpts(&m_opts); - applyArguments(args, subparser, &m_config); + mArgumentsApply(&m_args, m_subparsers.data(), m_subparsers.size(), &m_config); mCoreConfigMap(&m_config, &m_opts); + m_parsed = true; return true; } return false; @@ -299,6 +308,10 @@ void ConfigController::makePortable() { m_settings = std::move(settings2); } +void ConfigController::usage(const char* arg0) const { + ::usage(arg0, nullptr, nullptr, m_subparsers.data(), m_subparsers.size()); +} + bool ConfigController::isPortable() { return mCoreConfigIsPortable(); } diff --git a/src/platform/qt/ConfigController.h b/src/platform/qt/ConfigController.h index df7cb816d..74afea99b 100644 --- a/src/platform/qt/ConfigController.h +++ b/src/platform/qt/ConfigController.h @@ -12,6 +12,7 @@ #include #include +#include #include #include @@ -70,7 +71,7 @@ public: ~ConfigController(); const mCoreOptions* options() const { return &m_opts; } - bool parseArguments(mArguments* args, int argc, char* argv[], mSubParser* subparser = nullptr); + bool parseArguments(int argc, char* argv[]); ConfigOption* addOption(const char* key); void updateOption(const char* key); @@ -91,6 +92,10 @@ public: const mCoreConfig* config() const { return &m_config; } mCoreConfig* config() { return &m_config; } + const mArguments* args() const { return &m_args; } + const mGraphicsOpts* graphicsOpts() const { return &m_graphicsOpts; } + void usage(const char* arg0) const; + static const QString& configDir(); static bool isPortable(); @@ -110,6 +115,10 @@ private: mCoreConfig m_config; mCoreOptions m_opts{}; + mArguments m_args{}; + mGraphicsOpts m_graphicsOpts{}; + std::array m_subparsers; + bool m_parsed = false; QHash m_optionSet; std::unique_ptr m_settings; diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 726d57903..3edac41c2 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -179,8 +179,8 @@ Window::~Window() { #endif } -void Window::argumentsPassed(mArguments* args) { - loadConfig(); +void Window::argumentsPassed() { + const mArguments* args = m_config->args(); if (args->patch) { m_pendingPatch = args->patch; @@ -203,9 +203,24 @@ void Window::argumentsPassed(mArguments* args) { } #endif + if (m_config->graphicsOpts()->multiplier) { + m_savedScale = m_config->graphicsOpts()->multiplier; + +#if defined(M_CORE_GBA) + QSize size(GBA_VIDEO_HORIZONTAL_PIXELS, GBA_VIDEO_VERTICAL_PIXELS); +#elif defined(M_CORE_GB) + QSize size(GB_VIDEO_HORIZONTAL_PIXELS, GB_VIDEO_VERTICAL_PIXELS); +#endif + resizeFrame(size * m_savedScale); + } + if (args->fname) { setController(m_manager->loadGame(args->fname), args->fname); } + + if (m_config->graphicsOpts()->fullscreen) { + enterFullScreen(); + } } void Window::resizeFrame(const QSize& size) { diff --git a/src/platform/qt/Window.h b/src/platform/qt/Window.h index db319080c..6cc68150a 100644 --- a/src/platform/qt/Window.h +++ b/src/platform/qt/Window.h @@ -24,8 +24,6 @@ #include "LogController.h" #include "SettingsView.h" -struct mArguments; - namespace QGBA { class AudioProcessor; @@ -59,7 +57,7 @@ public: void setConfig(ConfigController*); ConfigController* config() { return m_config; } - void argumentsPassed(mArguments*); + void argumentsPassed(); void resizeFrame(const QSize& size); diff --git a/src/platform/qt/main.cpp b/src/platform/qt/main.cpp index 8a7efef90..67668b011 100644 --- a/src/platform/qt/main.cpp +++ b/src/platform/qt/main.cpp @@ -68,22 +68,18 @@ int main(int argc, char* argv[]) { QLocale::setDefault(locale); } - mArguments args; - mGraphicsOpts graphicsOpts; - mSubParser subparser; - initParserForGraphics(&subparser, &graphicsOpts); - bool loaded = configController.parseArguments(&args, argc, argv, &subparser); - if (loaded) { - if (args.showHelp) { - usage(argv[0], subparser.usage); - freeArguments(&args); + if (configController.parseArguments(argc, argv)) { + if (configController.args()->showHelp) { + configController.usage(argv[0]); return 0; } - if (args.showVersion) { + if (configController.args()->showVersion) { version(argv[0]); - freeArguments(&args); return 0; } + } else { + configController.usage(argv[0]); + return 1; } QApplication::setApplicationName(projectName); @@ -117,19 +113,8 @@ int main(int argc, char* argv[]) { application.installTranslator(&langTranslator); Window* w = application.newWindow(); - if (loaded) { - w->argumentsPassed(&args); - } else { - w->loadConfig(); - } - freeArguments(&args); - - if (graphicsOpts.multiplier) { - w->resizeFrame(QSize(GBA_VIDEO_HORIZONTAL_PIXELS * graphicsOpts.multiplier, GBA_VIDEO_VERTICAL_PIXELS * graphicsOpts.multiplier)); - } - if (graphicsOpts.fullscreen) { - w->enterFullScreen(); - } + w->loadConfig(); + w->argumentsPassed(); w->show(); diff --git a/src/platform/sdl/main.c b/src/platform/sdl/main.c index 5025e7b6c..b53f9b5ed 100644 --- a/src/platform/sdl/main.c +++ b/src/platform/sdl/main.c @@ -80,37 +80,37 @@ int main(int argc, char** argv) { struct mSubParser subparser; - initParserForGraphics(&subparser, &graphicsOpts); - bool parsed = parseArguments(&args, argc, argv, &subparser); + mSubParserGraphicsInit(&subparser, &graphicsOpts); + bool parsed = mArgumentsParse(&args, argc, argv, &subparser, 1); if (!args.fname && !args.showVersion) { parsed = false; } if (!parsed || args.showHelp) { - usage(argv[0], subparser.usage); - freeArguments(&args); + usage(argv[0], NULL, NULL, &subparser, 1); + mArgumentsDeinit(&args); return !parsed; } if (args.showVersion) { version(argv[0]); - freeArguments(&args); + mArgumentsDeinit(&args); return 0; } if (SDL_Init(SDL_INIT_VIDEO) < 0) { printf("Could not initialize video: %s\n", SDL_GetError()); - freeArguments(&args); + mArgumentsDeinit(&args); return 1; } renderer.core = mCoreFind(args.fname); if (!renderer.core) { printf("Could not run game. Are you sure the file exists and is a compatible game?\n"); - freeArguments(&args); + mArgumentsDeinit(&args); return 1; } if (!renderer.core->init(renderer.core)) { - freeArguments(&args); + mArgumentsDeinit(&args); return 1; } @@ -134,7 +134,7 @@ int main(int argc, char** argv) { mInputMapInit(&renderer.core->inputMap, &GBAInputInfo); mCoreInitConfig(renderer.core, PORT); - applyArguments(&args, &subparser, &renderer.core->config); + mArgumentsApply(&args, &subparser, 1, &renderer.core->config); mCoreConfigLoadDefaults(&renderer.core->config, &opts); mCoreLoadConfig(renderer.core); @@ -168,7 +168,7 @@ int main(int argc, char** argv) { } if (!renderer.init(&renderer)) { - freeArguments(&args); + mArgumentsDeinit(&args); mCoreConfigDeinit(&renderer.core->config); renderer.core->deinit(renderer.core); return 1; @@ -199,7 +199,7 @@ int main(int argc, char** argv) { mSDLDeinit(&renderer); - freeArguments(&args); + mArgumentsDeinit(&args); mCoreConfigFreeOpts(&opts); mCoreConfigDeinit(&renderer.core->config); renderer.core->deinit(renderer.core); diff --git a/src/platform/test/fuzz-main.c b/src/platform/test/fuzz-main.c index bb58fd003..84e71fffa 100644 --- a/src/platform/test/fuzz-main.c +++ b/src/platform/test/fuzz-main.c @@ -22,7 +22,7 @@ #define FUZZ_OPTIONS "F:NO:S:V:" #define FUZZ_USAGE \ - "\nAdditional options:\n" \ + "Additional options:\n" \ " -F FRAMES Run for the specified number of FRAMES before exiting\n" \ " -N Disable video rendering entirely\n" \ " -O OFFSET Offset to apply savestate overlay\n" \ @@ -53,12 +53,12 @@ int main(int argc, char** argv) { }; struct mArguments args; - bool parsed = parseArguments(&args, argc, argv, &subparser); + bool parsed = mArgumentsParse(&args, argc, argv, &subparser, 1); if (!args.fname) { parsed = false; } if (!parsed || args.showHelp) { - usage(argv[0], FUZZ_USAGE); + usage(argv[0], NULL, NULL, &subparser, 1); return !parsed; } if (args.showVersion) { @@ -71,7 +71,7 @@ int main(int argc, char** argv) { } core->init(core); mCoreInitConfig(core, "fuzz"); - applyArguments(&args, NULL, &core->config); + mArgumentsApply(&args, NULL, 0, &core->config); mCoreConfigSetDefaultValue(&core->config, "idleOptimization", "remove"); @@ -161,7 +161,7 @@ int main(int argc, char** argv) { } loadError: - freeArguments(&args); + mArgumentsDeinit(&args); if (outputBuffer) { free(outputBuffer); } diff --git a/src/platform/test/perf-main.c b/src/platform/test/perf-main.c index 23cb1d4a8..668205296 100644 --- a/src/platform/test/perf-main.c +++ b/src/platform/test/perf-main.c @@ -44,7 +44,7 @@ size_t romBufferSize; #define PERF_OPTIONS "DF:L:NPS:T" #define PERF_USAGE \ - "\nBenchmark options:\n" \ + "Benchmark options:\n" \ " -F FRAMES Run for the specified number of FRAMES before exiting\n" \ " -N Disable video rendering entirely\n" \ " -T Use threaded video rendering\n" \ @@ -131,12 +131,12 @@ int main(int argc, char** argv) { }; struct mArguments args = {}; - bool parsed = parseArguments(&args, argc, argv, &subparser); + bool parsed = mArgumentsParse(&args, argc, argv, &subparser, 1); if (!args.fname && !perfOpts.server) { parsed = false; } if (!parsed || args.showHelp) { - usage(argv[0], PERF_USAGE); + usage(argv[0], NULL, NULL, &subparser, 1); didFail = !parsed; goto cleanup; } @@ -171,7 +171,7 @@ int main(int argc, char** argv) { _savestate->close(_savestate); } cleanup: - freeArguments(&args); + mArgumentsDeinit(&args); #ifdef __3DS__ gfxExit(); @@ -217,7 +217,7 @@ bool _mPerfRunCore(const char* fname, const struct mArguments* args, const struc mCoreConfigMap(&core->config, &opts); opts.audioSync = false; opts.videoSync = false; - applyArguments(args, NULL, &core->config); + mArgumentsApply(args, NULL, 0, &core->config); mCoreConfigLoadDefaults(&core->config, &opts); mCoreConfigSetDefaultValue(&core->config, "idleOptimization", "detect"); mCoreLoadConfig(core); diff --git a/src/platform/test/rom-test-main.c b/src/platform/test/rom-test-main.c index 916df85b7..2468c37e9 100644 --- a/src/platform/test/rom-test-main.c +++ b/src/platform/test/rom-test-main.c @@ -24,7 +24,7 @@ #define ROM_TEST_OPTIONS "S:R:" #define ROM_TEST_USAGE \ - "\nAdditional options:\n" \ + "Additional options:\n" \ " -S SWI Run until specified SWI call before exiting\n" \ " -R REGISTER General purpose register to return as exit code\n" \ @@ -81,12 +81,12 @@ int main(int argc, char * argv[]) { }; struct mArguments args; - bool parsed = parseArguments(&args, argc, argv, &subparser); + bool parsed = mArgumentsParse(&args, argc, argv, &subparser, 1); if (!args.fname) { parsed = false; } if (!parsed || args.showHelp) { - usage(argv[0], ROM_TEST_USAGE); + usage(argv[0], NULL, NULL, &subparser, 1); return !parsed; } if (args.showVersion) { @@ -99,7 +99,7 @@ int main(int argc, char * argv[]) { } core->init(core); mCoreInitConfig(core, "romTest"); - applyArguments(&args, NULL, &core->config); + mArgumentsApply(&args, NULL, 0, &core->config); mCoreConfigSetDefaultValue(&core->config, "idleOptimization", "remove"); @@ -184,7 +184,7 @@ int main(int argc, char * argv[]) { cleanExit = true; loadError: - freeArguments(&args); + mArgumentsDeinit(&args); mCoreConfigDeinit(&core->config); core->deinit(core); From ce7e53d53d3cec595887ee944fc9a9a7d6cd06d5 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 21 Apr 2022 19:22:29 -0700 Subject: [PATCH 03/20] Qt: Add e-Card passing to the command line (closes #2474) --- CHANGES | 1 + src/platform/qt/ConfigController.cpp | 28 +++++++++++++++++++++++++- src/platform/qt/ConfigController.h | 5 ++++- src/platform/qt/CoreController.cpp | 30 +++++++++++++++++++++++++++- src/platform/qt/CoreController.h | 1 + src/platform/qt/Window.cpp | 8 ++++++++ 6 files changed, 70 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index 221c201db..6beccae59 100644 --- a/CHANGES +++ b/CHANGES @@ -69,6 +69,7 @@ Misc: - Qt: Enable -b for Boot BIOS menu option (fixes mgba.io/i/2074) - Qt: Add tile range selection to tile viewer (closes mgba.io/i/2455) - Qt: Show warning if XQ audio is toggled while loaded (fixes mgba.io/i/2295) + - Qt: Add e-Card passing to the command line (closes mgba.io/i/2474) - Windows: Attach to console if present - Vita: Add bilinear filtering option (closes mgba.io/i/344) diff --git a/src/platform/qt/ConfigController.cpp b/src/platform/qt/ConfigController.cpp index 89ccae253..92d963008 100644 --- a/src/platform/qt/ConfigController.cpp +++ b/src/platform/qt/ConfigController.cpp @@ -125,6 +125,28 @@ ConfigController::ConfigController(QObject* parent) mCoreConfigSetDefaultIntValue(&m_config, "sgb.borders", 1); mCoreConfigSetDefaultIntValue(&m_config, "useCgbColors", 1); mCoreConfigMap(&m_config, &m_opts); + + mSubParserGraphicsInit(&m_subparsers[0], &m_graphicsOpts); + + m_subparsers[1].usage = "Frontend options:\n" + " --ecard FILENAME Scan an e-Reader card in the first loaded game\n" + " Can be paassed multiple times for multiple cards"; + m_subparsers[1].parse = nullptr; + m_subparsers[1].parseLong = [](struct mSubParser* parser, const char* option, const char* arg) { + if (option == QLatin1String("ecard")) { + QStringList* eCards = static_cast(parser->opts); + eCards->append(QString::fromUtf8(arg)); + return true; + } + return false; + }; + m_subparsers[1].apply = nullptr; + m_subparsers[1].extraOptions = nullptr; + m_subparsers[1].longOptions = (const mOption[]) { + { "ecard", true, '\0' }, + { 0 } + }; + m_subparsers[1].opts = &m_eCards; } ConfigController::~ConfigController() { @@ -140,7 +162,7 @@ bool ConfigController::parseArguments(int argc, char* argv[]) { if (m_parsed) { return false; } - mSubParserGraphicsInit(&m_subparsers[0], &m_graphicsOpts); + if (mArgumentsParse(&m_args, argc, argv, m_subparsers.data(), m_subparsers.size())) { mCoreConfigFreeOpts(&m_opts); mArgumentsApply(&m_args, m_subparsers.data(), m_subparsers.size(), &m_config); @@ -312,6 +334,10 @@ void ConfigController::usage(const char* arg0) const { ::usage(arg0, nullptr, nullptr, m_subparsers.data(), m_subparsers.size()); } +QStringList ConfigController::takeECardList() { + return QStringList(std::move(m_eCards)); +} + bool ConfigController::isPortable() { return mCoreConfigIsPortable(); } diff --git a/src/platform/qt/ConfigController.h b/src/platform/qt/ConfigController.h index 74afea99b..13a638f59 100644 --- a/src/platform/qt/ConfigController.h +++ b/src/platform/qt/ConfigController.h @@ -96,6 +96,8 @@ public: const mGraphicsOpts* graphicsOpts() const { return &m_graphicsOpts; } void usage(const char* arg0) const; + QStringList takeECardList(); + static const QString& configDir(); static bool isPortable(); @@ -117,8 +119,9 @@ private: mCoreOptions m_opts{}; mArguments m_args{}; mGraphicsOpts m_graphicsOpts{}; - std::array m_subparsers; + std::array m_subparsers; bool m_parsed = false; + QStringList m_eCards; QHash m_optionSet; std::unique_ptr m_settings; diff --git a/src/platform/qt/CoreController.cpp b/src/platform/qt/CoreController.cpp index 94043d680..f4ff03d16 100644 --- a/src/platform/qt/CoreController.cpp +++ b/src/platform/qt/CoreController.cpp @@ -897,8 +897,31 @@ void CoreController::scanCard(const QString& path) { QImage image(path); if (image.isNull()) { QFile file(path); - file.open(QIODevice::ReadOnly); + if (!file.open(QIODevice::ReadOnly)) { + return; + } m_eReaderData = file.read(2912); + + file.seek(0); + QStringList lines; + QDir basedir(QFileInfo(path).dir()); + + while (true) { + QByteArray line = file.readLine().trimmed(); + if (line.isEmpty()) { + break; + } + QString filepath(QString::fromUtf8(line)); + if (filepath[0] == QChar('#')) { + continue; + } + if (QFileInfo(filepath).isRelative()) { + lines.append(basedir.filePath(filepath)); + } else { + lines.append(filepath); + } + } + scanCards(lines); } else if (image.size() == QSize(989, 44) || image.size() == QSize(639, 44)) { const uchar* bits = image.constBits(); size_t size; @@ -917,6 +940,11 @@ void CoreController::scanCard(const QString& path) { #endif } +void CoreController::scanCards(const QStringList& paths) { + for (const QString& path : paths) { + scanCard(path); + } +} void CoreController::importSharkport(const QString& path) { #ifdef M_CORE_GBA diff --git a/src/platform/qt/CoreController.h b/src/platform/qt/CoreController.h index ead578003..1dd61fec9 100644 --- a/src/platform/qt/CoreController.h +++ b/src/platform/qt/CoreController.h @@ -159,6 +159,7 @@ public slots: void loadSave(VFile*, bool temporary); void loadPatch(const QString&); void scanCard(const QString&); + void scanCards(const QStringList&); void replaceGame(const QString&); void yankPak(); diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 3edac41c2..cfcb93599 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -925,8 +925,16 @@ void Window::gameStarted() { action->setActive(true); } } + interrupter.resume(); + m_actions.rebuildMenu(menuBar(), this, *m_shortcutController); +#ifdef M_CORE_GBA + if (m_controller->platform() == mPLATFORM_GBA) { + m_controller->scanCards(m_config->takeECardList()); + } +#endif + #ifdef USE_DISCORD_RPC DiscordCoordinator::gameStarted(m_controller); #endif From b10a5b7f02f1400089418954bd2d2eb0ba2b1430 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 21 Apr 2022 21:31:13 -0700 Subject: [PATCH 04/20] Qt: More flexible argv parsing --- src/platform/qt/ConfigController.cpp | 40 ++++++++++++++++++---------- src/platform/qt/ConfigController.h | 11 +++++--- src/platform/qt/CoreController.cpp | 2 +- src/platform/qt/Window.cpp | 5 +++- 4 files changed, 38 insertions(+), 20 deletions(-) diff --git a/src/platform/qt/ConfigController.cpp b/src/platform/qt/ConfigController.cpp index 92d963008..f7039e188 100644 --- a/src/platform/qt/ConfigController.cpp +++ b/src/platform/qt/ConfigController.cpp @@ -13,6 +13,11 @@ #include +static const mOption s_frontendOptions[] = { + { "ecard", true, '\0' }, + { 0 } +}; + using namespace QGBA; ConfigOption::ConfigOption(const QString& name, QObject* parent) @@ -129,24 +134,27 @@ ConfigController::ConfigController(QObject* parent) mSubParserGraphicsInit(&m_subparsers[0], &m_graphicsOpts); m_subparsers[1].usage = "Frontend options:\n" - " --ecard FILENAME Scan an e-Reader card in the first loaded game\n" - " Can be paassed multiple times for multiple cards"; + " --ecard FILE Scan an e-Reader card in the first loaded game\n" + " Can be paassed multiple times for multiple cards\n" m_subparsers[1].parse = nullptr; m_subparsers[1].parseLong = [](struct mSubParser* parser, const char* option, const char* arg) { - if (option == QLatin1String("ecard")) { - QStringList* eCards = static_cast(parser->opts); - eCards->append(QString::fromUtf8(arg)); + ConfigController* self = static_cast(parser->opts); + QString optionName(QString::fromUtf8(option)); + if (optionName == QLatin1String("ecard")) { + QStringList ecards; + if (self->m_argvOptions.contains(optionName)) { + ecards = self->m_argvOptions[optionName].toStringList(); + } + ecards.append(QString::fromUtf8(arg)); + self->m_argvOptions[optionName] = ecards; return true; } return false; }; m_subparsers[1].apply = nullptr; m_subparsers[1].extraOptions = nullptr; - m_subparsers[1].longOptions = (const mOption[]) { - { "ecard", true, '\0' }, - { 0 } - }; - m_subparsers[1].opts = &m_eCards; + m_subparsers[1].longOptions = s_frontendOptions; + m_subparsers[1].opts = this; } ConfigController::~ConfigController() { @@ -223,6 +231,14 @@ QVariant ConfigController::getQtOption(const QString& key, const QString& group) return value; } +QVariant ConfigController::getArgvOption(const QString& key) const { + return m_argvOptions.value(key); +} + +QVariant ConfigController::takeArgvOption(const QString& key) { + return m_argvOptions.take(key); +} + void ConfigController::saveOverride(const Override& override) { override.save(overrides()); write(); @@ -334,10 +350,6 @@ void ConfigController::usage(const char* arg0) const { ::usage(arg0, nullptr, nullptr, m_subparsers.data(), m_subparsers.size()); } -QStringList ConfigController::takeECardList() { - return QStringList(std::move(m_eCards)); -} - bool ConfigController::isPortable() { return mCoreConfigIsPortable(); } diff --git a/src/platform/qt/ConfigController.h b/src/platform/qt/ConfigController.h index 13a638f59..57fbda9c5 100644 --- a/src/platform/qt/ConfigController.h +++ b/src/platform/qt/ConfigController.h @@ -81,6 +81,9 @@ public: QVariant getQtOption(const QString& key, const QString& group = QString()) const; + QVariant getArgvOption(const QString& key) const; + QVariant takeArgvOption(const QString& key); + QList getMRU() const; void setMRU(const QList& mru); @@ -96,8 +99,6 @@ public: const mGraphicsOpts* graphicsOpts() const { return &m_graphicsOpts; } void usage(const char* arg0) const; - QStringList takeECardList(); - static const QString& configDir(); static bool isPortable(); @@ -113,6 +114,8 @@ public slots: void write(); private: + void addArgvOption(const QString& key, const QVariant& value); + Configuration* defaults() { return &m_config.defaultsTable; } mCoreConfig m_config; @@ -121,8 +124,8 @@ private: mGraphicsOpts m_graphicsOpts{}; std::array m_subparsers; bool m_parsed = false; - QStringList m_eCards; - + + QHash m_argvOptions; QHash m_optionSet; std::unique_ptr m_settings; static QString s_configDir; diff --git a/src/platform/qt/CoreController.cpp b/src/platform/qt/CoreController.cpp index f4ff03d16..5433054f3 100644 --- a/src/platform/qt/CoreController.cpp +++ b/src/platform/qt/CoreController.cpp @@ -912,7 +912,7 @@ void CoreController::scanCard(const QString& path) { break; } QString filepath(QString::fromUtf8(line)); - if (filepath[0] == QChar('#')) { + if (filepath.isEmpty() || filepath[0] == QChar('#')) { continue; } if (QFileInfo(filepath).isRelative()) { diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index cfcb93599..8c69efc4f 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -931,7 +931,10 @@ void Window::gameStarted() { #ifdef M_CORE_GBA if (m_controller->platform() == mPLATFORM_GBA) { - m_controller->scanCards(m_config->takeECardList()); + QVariant eCardList = m_config->takeArgvOption(QString("ecard")); + if (eCardList.canConvert(QMetaType::QStringList)) { + m_controller->scanCards(eCardList.toStringList()); + } } #endif From f326d02e33f38abf089319064289e766b3b576b3 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 21 Apr 2022 22:06:22 -0700 Subject: [PATCH 05/20] Qt: Boot both a multiboot image and ROM with CLI args (closes #1941) --- CHANGES | 1 + include/mgba/internal/gba/gba.h | 3 ++ src/gba/gba.c | 42 +++++++++++++++------------- src/gba/hle-bios.c | 4 +-- src/gba/hle-bios.s | 8 +++--- src/platform/qt/ConfigController.cpp | 6 ++++ src/platform/qt/Window.cpp | 9 ++++++ 7 files changed, 48 insertions(+), 25 deletions(-) diff --git a/CHANGES b/CHANGES index 6beccae59..a227b1077 100644 --- a/CHANGES +++ b/CHANGES @@ -70,6 +70,7 @@ Misc: - Qt: Add tile range selection to tile viewer (closes mgba.io/i/2455) - Qt: Show warning if XQ audio is toggled while loaded (fixes mgba.io/i/2295) - Qt: Add e-Card passing to the command line (closes mgba.io/i/2474) + - Qt: Boot both a multiboot image and ROM with CLI args (closes mgba.io/i/1941) - Windows: Attach to console if present - Vita: Add bilinear filtering option (closes mgba.io/i/344) diff --git a/include/mgba/internal/gba/gba.h b/include/mgba/internal/gba/gba.h index d98cdab39..2563b96b3 100644 --- a/include/mgba/internal/gba/gba.h +++ b/include/mgba/internal/gba/gba.h @@ -98,6 +98,7 @@ struct GBA { uint32_t romCrc32; struct VFile* romVf; struct VFile* biosVf; + struct VFile* mbVf; struct mAVStream* stream; struct mKeyCallback* keyCallback; @@ -171,6 +172,8 @@ void GBALoadBIOS(struct GBA* gba, struct VFile* vf); void GBAApplyPatch(struct GBA* gba, struct Patch* patch); bool GBALoadMB(struct GBA* gba, struct VFile* vf); +void GBAUnloadMB(struct GBA* gba); + bool GBALoadNull(struct GBA* gba); void GBAGetGameCode(const struct GBA* gba, char* out); diff --git a/src/gba/gba.c b/src/gba/gba.c index 285b06615..a39823666 100644 --- a/src/gba/gba.c +++ b/src/gba/gba.c @@ -139,7 +139,7 @@ void GBAUnloadROM(struct GBA* gba) { if (gba->romVf) { #ifndef FIXED_ROM_BUFFER - if (gba->isPristine) { + if (gba->isPristine && gba->memory.rom) { gba->romVf->unmap(gba->romVf, gba->memory.rom, gba->pristineRomSize); } #endif @@ -147,6 +147,8 @@ void GBAUnloadROM(struct GBA* gba) { gba->romVf = NULL; } gba->memory.rom = NULL; + gba->memory.romSize = 0; + gba->memory.romMask = 0; gba->isPristine = false; if (!gba->memory.savedata.dirty) { @@ -163,6 +165,7 @@ void GBAUnloadROM(struct GBA* gba) { void GBADestroy(struct GBA* gba) { GBAUnloadROM(gba); + GBAUnloadMB(gba); if (gba->biosVf) { gba->biosVf->unmap(gba->biosVf, gba->memory.bios, SIZE_BIOS); @@ -228,16 +231,18 @@ void GBAReset(struct ARMCore* cpu) { bool isELF = false; #ifdef USE_ELF - struct ELF* elf = ELFOpen(gba->romVf); - if (elf) { - isELF = true; - ELFClose(elf); + if (gba->mbVf) { + struct ELF* elf = ELFOpen(gba->mbVf); + if (elf) { + isELF = true; + ELFClose(elf); + } } #endif - if (GBAIsMB(gba->romVf) && !isELF) { - gba->romVf->seek(gba->romVf, 0, SEEK_SET); - gba->romVf->read(gba->romVf, gba->memory.wram, gba->pristineRomSize); + if (GBAIsMB(gba->mbVf) && !isELF) { + gba->mbVf->seek(gba->mbVf, 0, SEEK_SET); + gba->mbVf->read(gba->mbVf, gba->memory.wram, SIZE_WORKING_RAM); } gba->lastJump = 0; @@ -367,25 +372,24 @@ bool GBALoadNull(struct GBA* gba) { } bool GBALoadMB(struct GBA* gba, struct VFile* vf) { - GBAUnloadROM(gba); - gba->romVf = vf; - gba->pristineRomSize = vf->size(vf); + GBAUnloadMB(gba); + gba->mbVf = vf; vf->seek(vf, 0, SEEK_SET); - if (gba->pristineRomSize > SIZE_WORKING_RAM) { - gba->pristineRomSize = SIZE_WORKING_RAM; - } - gba->isPristine = true; memset(gba->memory.wram, 0, SIZE_WORKING_RAM); - gba->yankedRomSize = 0; - gba->memory.romSize = 0; - gba->memory.romMask = 0; - gba->romCrc32 = doCrc32(gba->memory.wram, gba->pristineRomSize); + vf->read(vf, gba->memory.wram, SIZE_WORKING_RAM); if (gba->cpu && gba->memory.activeRegion == REGION_WORKING_RAM) { gba->cpu->memory.setActiveRegion(gba->cpu, gba->cpu->gprs[ARM_PC]); } return true; } +void GBAUnloadMB(struct GBA* gba) { + if (gba->mbVf) { + gba->mbVf->close(gba->mbVf); + gba->mbVf = NULL; + } +} + bool GBALoadROM(struct GBA* gba, struct VFile* vf) { if (!vf) { return false; diff --git a/src/gba/hle-bios.c b/src/gba/hle-bios.c index 27dff4642..91a48711b 100644 --- a/src/gba/hle-bios.c +++ b/src/gba/hle-bios.c @@ -5,8 +5,8 @@ const uint8_t hleBios[SIZE_BIOS] = { 0x06, 0x00, 0x00, 0xea, 0x66, 0x00, 0x00, 0xea, 0x0c, 0x00, 0x00, 0xea, 0xfe, 0xff, 0xff, 0xea, 0xfe, 0xff, 0xff, 0xea, 0x00, 0x00, 0xa0, 0xe1, - 0x59, 0x00, 0x00, 0xea, 0xfe, 0xff, 0xff, 0xea, 0x02, 0x03, 0xa0, 0xe3, - 0x03, 0x10, 0xd0, 0xe5, 0xea, 0x00, 0x51, 0xe3, 0x4c, 0x01, 0x9f, 0x15, + 0x59, 0x00, 0x00, 0xea, 0xfe, 0xff, 0xff, 0xea, 0x58, 0x01, 0x9f, 0xe5, + 0x00, 0x10, 0x90, 0xe5, 0x00, 0x00, 0x51, 0xe3, 0x02, 0x03, 0xa0, 0x03, 0x10, 0xff, 0x2f, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x29, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0xe3, 0x01, 0xd3, 0xa0, 0x03, 0x20, 0xd0, 0x4d, 0x02, 0x00, 0x58, 0x2d, 0xe9, 0x02, 0xc0, 0x5e, 0xe5, diff --git a/src/gba/hle-bios.s b/src/gba/hle-bios.s index f2148fb1b..3f9ae905e 100644 --- a/src/gba/hle-bios.s +++ b/src/gba/hle-bios.s @@ -17,10 +17,10 @@ b irqBase b fiqBase resetBase: -mov r0, #0x8000000 -ldrb r1, [r0, #3] -cmp r1, #0xEA -ldrne r0, =0x20000C0 +ldr r0, =0x20000C0 +ldr r1, [r0] +cmp r1, #0 +moveq r0, #0x8000000 bx r0 .word 0 .word 0xE129F000 diff --git a/src/platform/qt/ConfigController.cpp b/src/platform/qt/ConfigController.cpp index f7039e188..66c89b4d5 100644 --- a/src/platform/qt/ConfigController.cpp +++ b/src/platform/qt/ConfigController.cpp @@ -15,6 +15,7 @@ static const mOption s_frontendOptions[] = { { "ecard", true, '\0' }, + { "mb", true, '\0' }, { 0 } }; @@ -136,6 +137,7 @@ ConfigController::ConfigController(QObject* parent) m_subparsers[1].usage = "Frontend options:\n" " --ecard FILE Scan an e-Reader card in the first loaded game\n" " Can be paassed multiple times for multiple cards\n" + " --mb FILE Boot a multiboot image with FILE inserted into the ROM slot"; m_subparsers[1].parse = nullptr; m_subparsers[1].parseLong = [](struct mSubParser* parser, const char* option, const char* arg) { ConfigController* self = static_cast(parser->opts); @@ -149,6 +151,10 @@ ConfigController::ConfigController(QObject* parent) self->m_argvOptions[optionName] = ecards; return true; } + if (optionName == QLatin1String("mb")) { + self->m_argvOptions[optionName] = QString::fromUtf8(arg); + return true; + } return false; }; m_subparsers[1].apply = nullptr; diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 8c69efc4f..87b6a8530 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -2057,6 +2057,15 @@ void Window::setController(CoreController* controller, const QString& fname) { connect(m_controller.get(), &CoreController::failed, this, &Window::gameFailed); connect(m_controller.get(), &CoreController::unimplementedBiosCall, this, &Window::unimplementedBiosCall); +#ifdef M_CORE_GBA + if (m_controller->platform() == mPLATFORM_GBA) { + QVariant mb = m_config->takeArgvOption(QString("mb")); + if (mb.canConvert(QMetaType::QString)) { + m_controller->replaceGame(mb.toString()); + } + } +#endif + #ifdef USE_GDB_STUB if (m_gdbController) { m_gdbController->setController(m_controller); From 979be972dcf57e12ba18e2227c3015720edbd5a3 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 22 Apr 2022 01:34:53 -0700 Subject: [PATCH 06/20] GB Video: Ignore CGB attributes when doing SGB VRAM transfers (fixes #2503) --- src/gb/renderers/software.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gb/renderers/software.c b/src/gb/renderers/software.c index a5e41162f..7fae32bed 100644 --- a/src/gb/renderers/software.c +++ b/src/gb/renderers/software.c @@ -917,7 +917,7 @@ static void GBVideoSoftwareRendererDrawBackground(struct GBVideoSoftwareRenderer bgTile = ((int8_t*) maps)[topX + topY]; } int p = highlight ? PAL_HIGHLIGHT_BG : PAL_BG; - if (renderer->model >= GB_MODEL_CGB) { + if (renderer->model >= GB_MODEL_CGB && renderer->sgbTransfer != 1) { GBObjAttributes attrs = attr[topX + topY]; p |= GBObjAttributesGetCGBPalette(attrs) * 4; if (GBObjAttributesIsPriority(attrs) && GBRegisterLCDCIsBgEnable(renderer->lcdc)) { @@ -952,7 +952,7 @@ static void GBVideoSoftwareRendererDrawBackground(struct GBVideoSoftwareRenderer bgTile = ((int8_t*) maps)[topX + topY]; } int p = highlight ? PAL_HIGHLIGHT_BG : PAL_BG; - if (renderer->model >= GB_MODEL_CGB) { + if (renderer->model >= GB_MODEL_CGB && renderer->sgbTransfer != 1) { GBObjAttributes attrs = attr[topX + topY]; p |= GBObjAttributesGetCGBPalette(attrs) * 4; if (GBObjAttributesIsPriority(attrs) && GBRegisterLCDCIsBgEnable(renderer->lcdc)) { From dcdd6d2dd2064e488ccf245fe6bacbc775323ce4 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 22 Apr 2022 02:59:15 -0700 Subject: [PATCH 07/20] GB Audio: Properly apply per-model audio differences --- CHANGES | 1 + src/gb/gb.c | 34 +++++++++++++++++----------------- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/CHANGES b/CHANGES index a227b1077..1c49d3429 100644 --- a/CHANGES +++ b/CHANGES @@ -19,6 +19,7 @@ Emulation fixes: - ARM7: Fix unsigned multiply timing - GB: Copy logo from ROM if not running the BIOS intro (fixes mgba.io/i/2378) - GB Audio: Fix channel 1/2 reseting edge cases (fixes mgba.io/i/1925) + - GB Audio: Properly apply per-model audio differences - GB Memory: Add cursory cartridge open bus emulation (fixes mgba.io/i/2032) - GB Serialize: Fix loading MBC1 states that affect bank 0 (fixes mgba.io/i/2402) - GB Video: Draw SGB border pieces that overlap GB graphics (fixes mgba.io/i/1339) diff --git a/src/gb/gb.c b/src/gb/gb.c index ee6699839..0582fff70 100644 --- a/src/gb/gb.c +++ b/src/gb/gb.c @@ -556,6 +556,23 @@ void GBReset(struct SM83Core* cpu) { } } + switch (gb->model) { + case GB_MODEL_DMG: + case GB_MODEL_SGB: + case GB_MODEL_AUTODETECT: //Silence warnings + gb->audio.style = GB_AUDIO_DMG; + break; + case GB_MODEL_MGB: + case GB_MODEL_SGB2: + gb->audio.style = GB_AUDIO_MGB; + break; + case GB_MODEL_AGB: + case GB_MODEL_CGB: + case GB_MODEL_SCGB: + gb->audio.style = GB_AUDIO_CGB; + break; + } + GBVideoReset(&gb->video); GBTimerReset(&gb->timer); GBIOReset(gb); @@ -779,23 +796,6 @@ void GBDetectModel(struct GB* gb) { gb->model = GB_MODEL_DMG; } } - - switch (gb->model) { - case GB_MODEL_DMG: - case GB_MODEL_SGB: - case GB_MODEL_AUTODETECT: //Silence warnings - gb->audio.style = GB_AUDIO_DMG; - break; - case GB_MODEL_MGB: - case GB_MODEL_SGB2: - gb->audio.style = GB_AUDIO_MGB; - break; - case GB_MODEL_AGB: - case GB_MODEL_CGB: - case GB_MODEL_SCGB: - gb->audio.style = GB_AUDIO_CGB; - break; - } } int GBValidModels(const uint8_t* bank0) { From 69d4518ad525d51ee9704b18c0c75fb1599d5f84 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 22 Apr 2022 18:51:44 -0700 Subject: [PATCH 08/20] GB Video: Ignore OAM when doing SGB VRAM transfers (fixes #2503) --- src/gb/renderers/software.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gb/renderers/software.c b/src/gb/renderers/software.c index 7fae32bed..487018e20 100644 --- a/src/gb/renderers/software.c +++ b/src/gb/renderers/software.c @@ -627,7 +627,7 @@ static void GBVideoSoftwareRendererDrawRange(struct GBVideoRenderer* renderer, i if (startX == 0) { _cleanOAM(softwareRenderer, y); } - if (GBRegisterLCDCIsObjEnable(softwareRenderer->lcdc) && !softwareRenderer->d.disableOBJ) { + if (GBRegisterLCDCIsObjEnable(softwareRenderer->lcdc) && !softwareRenderer->d.disableOBJ && softwareRenderer->sgbTransfer != 1) { int i; for (i = 0; i < softwareRenderer->objMax; ++i) { GBVideoSoftwareRendererDrawObj(softwareRenderer, &softwareRenderer->obj[i], startX, endX, y); From 765cc08136fdb723cea57dd3f58c16cac655af22 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 30 Apr 2022 19:23:04 -0700 Subject: [PATCH 09/20] GB Video: Only apply SGBC VRAM hack on SGBC --- src/gb/renderers/software.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/gb/renderers/software.c b/src/gb/renderers/software.c index 487018e20..f24911f2b 100644 --- a/src/gb/renderers/software.c +++ b/src/gb/renderers/software.c @@ -627,7 +627,7 @@ static void GBVideoSoftwareRendererDrawRange(struct GBVideoRenderer* renderer, i if (startX == 0) { _cleanOAM(softwareRenderer, y); } - if (GBRegisterLCDCIsObjEnable(softwareRenderer->lcdc) && !softwareRenderer->d.disableOBJ && softwareRenderer->sgbTransfer != 1) { + if (GBRegisterLCDCIsObjEnable(softwareRenderer->lcdc) && !softwareRenderer->d.disableOBJ && (softwareRenderer->model != GB_MODEL_SCGB || softwareRenderer->sgbTransfer != 1)) { int i; for (i = 0; i < softwareRenderer->objMax; ++i) { GBVideoSoftwareRendererDrawObj(softwareRenderer, &softwareRenderer->obj[i], startX, endX, y); @@ -917,7 +917,7 @@ static void GBVideoSoftwareRendererDrawBackground(struct GBVideoSoftwareRenderer bgTile = ((int8_t*) maps)[topX + topY]; } int p = highlight ? PAL_HIGHLIGHT_BG : PAL_BG; - if (renderer->model >= GB_MODEL_CGB && renderer->sgbTransfer != 1) { + if (renderer->model >= GB_MODEL_CGB && (!(renderer->model & GB_MODEL_SGB) || renderer->sgbTransfer != 1)) { GBObjAttributes attrs = attr[topX + topY]; p |= GBObjAttributesGetCGBPalette(attrs) * 4; if (GBObjAttributesIsPriority(attrs) && GBRegisterLCDCIsBgEnable(renderer->lcdc)) { @@ -952,7 +952,7 @@ static void GBVideoSoftwareRendererDrawBackground(struct GBVideoSoftwareRenderer bgTile = ((int8_t*) maps)[topX + topY]; } int p = highlight ? PAL_HIGHLIGHT_BG : PAL_BG; - if (renderer->model >= GB_MODEL_CGB && renderer->sgbTransfer != 1) { + if (renderer->model >= GB_MODEL_CGB && (!(renderer->model & GB_MODEL_SGB) || renderer->sgbTransfer != 1)) { GBObjAttributes attrs = attr[topX + topY]; p |= GBObjAttributesGetCGBPalette(attrs) * 4; if (GBObjAttributesIsPriority(attrs) && GBRegisterLCDCIsBgEnable(renderer->lcdc)) { From 492ba2478c58ae5db090b07c816edd2965907cdd Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 1 May 2022 17:02:50 -0700 Subject: [PATCH 10/20] Core: Fix crash if library can't be opened --- CHANGES | 1 + src/core/library.c | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/CHANGES b/CHANGES index 1c49d3429..e0b9f1d47 100644 --- a/CHANGES +++ b/CHANGES @@ -38,6 +38,7 @@ Emulation fixes: Other fixes: - Core: Don't attempt to restore rewind diffs past start of rewind - Core: Fix the runloop resuming after a game has crashed (fixes mgba.io/i/2451) + - Core: Fix crash if library can't be opened - FFmpeg: Fix crash when encoding audio with some containers - FFmpeg: Fix GIF recording (fixes mgba.io/i/2393) - GB: Fix temporary saves diff --git a/src/core/library.c b/src/core/library.c index 966fa132b..06bed44ee 100644 --- a/src/core/library.c +++ b/src/core/library.c @@ -199,6 +199,10 @@ error: } void mLibraryDestroy(struct mLibrary* library) { + if (!library) { + return; + } + sqlite3_finalize(library->insertPath); sqlite3_finalize(library->insertRom); sqlite3_finalize(library->insertRoot); From 8f133caec8926562e71b746d2866ee1ae31b9edb Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 1 May 2022 22:29:31 -0700 Subject: [PATCH 11/20] Debugger: Add command to load external symbol file (fixes #2480) --- CHANGES | 1 + src/debugger/cli-debugger.c | 51 ++++++++++++++++++++++++++++++++----- 2 files changed, 46 insertions(+), 6 deletions(-) diff --git a/CHANGES b/CHANGES index e0b9f1d47..304577ecf 100644 --- a/CHANGES +++ b/CHANGES @@ -51,6 +51,7 @@ Misc: - Core: Suspend runloop when a core crashes - Debugger: Save and restore CLI history - Debugger: GDB now works while the game is paused + - Debugger: Add command to load external symbol file (fixes mgba.io/i/2480) - GB MBC: Filter out MBC errors when cartridge is yanked (fixes mgba.io/i/2488) - GB Video: Add default SGB border - GBA: Automatically skip BIOS if ROM has invalid logo diff --git a/src/debugger/cli-debugger.c b/src/debugger/cli-debugger.c index 4295bea5c..2aa024bf2 100644 --- a/src/debugger/cli-debugger.c +++ b/src/debugger/cli-debugger.c @@ -8,15 +8,17 @@ #include #include -#include -#include -#include -#include -#include - #ifdef ENABLE_SCRIPTING #include #endif +#include +#include +#include +#ifdef USE_ELF +#include +#endif +#include +#include #if !defined(NDEBUG) && !defined(_WIN32) #include @@ -74,6 +76,7 @@ static void _source(struct CLIDebugger*, struct CLIDebugVector*); static void _backtrace(struct CLIDebugger*, struct CLIDebugVector*); static void _finish(struct CLIDebugger*, struct CLIDebugVector*); static void _setStackTraceMode(struct CLIDebugger*, struct CLIDebugVector*); +static void _loadSymbols(struct CLIDebugger*, struct CLIDebugVector*); static void _setSymbol(struct CLIDebugger*, struct CLIDebugVector*); static void _findSymbol(struct CLIDebugger*, struct CLIDebugVector*); @@ -101,6 +104,7 @@ static struct CLIDebuggerCommandSummary _debuggerCommands[] = { { "stack", _setStackTraceMode, "S", "Change the stack tracing mode" }, { "status", _printStatus, "", "Print the current status" }, { "symbol", _findSymbol, "I", "Find the symbol name for an address" }, + { "load-symbols", _loadSymbols, "S", "Load symbols from an external file" }, { "trace", _trace, "Is", "Trace a number of instructions" }, { "w/1", _writeByte, "II", "Write a byte at a specified offset" }, { "w/2", _writeHalfword, "II", "Write a halfword at a specified offset" }, @@ -133,6 +137,7 @@ static struct CLIDebuggerCommandAlias _debuggerCommandAliases[] = { { "h", "help" }, { "i", "status" }, { "info", "status" }, + { "loadsyms", "load-symbols" }, { "lb", "listb" }, { "lw", "listw" }, { "n", "next" }, @@ -1291,6 +1296,40 @@ static void _setStackTraceMode(struct CLIDebugger* debugger, struct CLIDebugVect } } +static void _loadSymbols(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { + struct mDebuggerSymbols* symbolTable = debugger->d.core->symbolTable; + if (!symbolTable) { + debugger->backend->printf(debugger->backend, "No symbol table available.\n"); + return; + } + if (!dv || dv->next) { + debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS); + return; + } + if (dv->type != CLIDV_CHAR_TYPE) { + debugger->backend->printf(debugger->backend, "%s\n", ERROR_INVALID_ARGS); + return; + } + struct VFile* vf = VFileOpen(dv->charValue, O_RDONLY); + if (!vf) { + debugger->backend->printf(debugger->backend, "%s\n", "Could not open symbol file"); + return; + } +#ifdef USE_ELF + struct ELF* elf = ELFOpen(vf); + if (elf) { +#ifdef USE_DEBUGGERS + mCoreLoadELFSymbols(symbolTable, elf); +#endif + ELFClose(elf); + } else +#endif + { + mDebuggerLoadARMIPSSymbols(symbolTable, vf); + } + vf->close(vf); +} + static void _setSymbol(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { struct mDebuggerSymbols* symbolTable = debugger->d.core->symbolTable; if (!symbolTable) { From 0395994b6b0f05e6d647e94b1abb31ff6f2f446a Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 4 May 2022 14:51:51 -0700 Subject: [PATCH 12/20] SDL: Fix MSYS2 build --- src/platform/sdl/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform/sdl/CMakeLists.txt b/src/platform/sdl/CMakeLists.txt index 047f26333..47f05cc4d 100644 --- a/src/platform/sdl/CMakeLists.txt +++ b/src/platform/sdl/CMakeLists.txt @@ -65,7 +65,7 @@ endif() set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libsdl${SDL_VERSION_DEBIAN}" PARENT_SCOPE) file(GLOB PLATFORM_SRC ${CMAKE_SOURCE_DIR}/src/platform/sdl/sdl-*.c) -set(PLATFORM_LIBRARY ${SDL_LIBRARY} ${SDLMAIN_LIBRARY} ${PIXMAN-1_LIBRARIES}) +set(PLATFORM_LIBRARY ${SDL_LIBRARY} ${SDLMAIN_LIBRARY} ${SDL_LIBRARY} ${PIXMAN-1_LIBRARIES}) include_directories(${CMAKE_SOURCE_DIR}/src/platform/sdl ${PIXMAN-1_INCLUDE_DIRS} ${SDL_INCLUDE_DIR}) set(SDL_INCLUDE_DIR "${SDL_INCLUDE_DIR}" PARENT_SCOPE) From 891af6a5f67be4286de145630560311b9c02fde3 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 7 May 2022 20:31:13 -0700 Subject: [PATCH 13/20] Windows: Reduce installer compression memory footprint --- src/platform/windows/setup/setup.iss.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/platform/windows/setup/setup.iss.in b/src/platform/windows/setup/setup.iss.in index 2dc423d92..3aad9ef1b 100644 --- a/src/platform/windows/setup/setup.iss.in +++ b/src/platform/windows/setup/setup.iss.in @@ -52,10 +52,10 @@ OutputBaseFilename={#AppName}-setup-{#CleanVersionString}-win{#WinBits} UsePreviousLanguage=False DisableWelcomePage=False VersionInfoDescription={#AppName} is an open-source Game Boy Advance emulator -VersionInfoCopyright= 20132020 Jeffrey Pfau +VersionInfoCopyright= 20132022 Jeffrey Pfau VersionInfoProductName={#AppName} VersionInfoVersion={#AppVer} -Compression=lzma2/ultra64 +Compression=lzma2/ultra SolidCompression=True VersionInfoTextVersion={#VersionString} VersionInfoProductVersion={#AppVer} From e2ea9330aa365a9779fe8248e626f35dfda03761 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 7 May 2022 23:05:49 -0700 Subject: [PATCH 14/20] macOS: Bump minimum to 10.9 --- README.md | 4 ++-- README_DE.md | 4 ++-- README_ES.md | 4 ++-- README_ZH_CN.md | 4 ++-- src/platform/qt/CMakeLists.txt | 14 +++++++++++--- 5 files changed, 19 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 096cd6df3..a7138af7b 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,7 @@ Supported Platforms ------------------- - Windows 7 or newer -- OS X 10.8 (Mountain Lion)[[3]](#osxver) or newer +- OS X 10.9 (Mavericks)[[3]](#osxver) or newer - Linux - FreeBSD - Nintendo 3DS @@ -235,7 +235,7 @@ Footnotes [2] Flash memory size detection does not work in some cases. These can be configured at runtime, but filing a bug is recommended if such a case is encountered. -[3] 10.8 is only needed for the Qt port. It may be possible to build or running the Qt port on 10.7 or older, but this is not officially supported. The SDL port is known to work on 10.5, and may work on older. +[3] 10.9 is only needed for the Qt port. It may be possible to build or running the Qt port on 10.7 or older, but this is not officially supported. The SDL port is known to work on 10.5, and may work on older. [downloads]: http://mgba.io/downloads.html [source]: https://github.com/mgba-emu/mgba/ diff --git a/README_DE.md b/README_DE.md index f14a65498..9b653c5d2 100644 --- a/README_DE.md +++ b/README_DE.md @@ -80,7 +80,7 @@ Unterstützte Plattformen ------------------------ - Windows 7 oder neuer -- OS X 10.8 (Mountain Lion)[[3]](#osxver) oder neuer +- OS X 10.9 (Mavericks)[[3]](#osxver) oder neuer - Linux - FreeBSD - Nintendo 3DS @@ -234,7 +234,7 @@ Fußnoten [2] In manchen Fällen ist es nicht möglich, die Größe des Flash-Speichers automatisch zu ermitteln. Diese kann dann zur Laufzeit konfiguriert werden, es wird jedoch empfohlen, den Fehler zu melden. -[3] 10.8 wird nur für die Qt-Portierung benötigt. Es ist wahrscheinlich möglich, die Qt-Portierung unter macOS 10.7 und älter zu bauen und zu nutzen, aber das wird nicht offiziell unterstützt. Die SDL-Portierung ist dafür bekannt, mit 10.7 und möglicherweise auf älteren Versionen zu funktionieren. +[3] 10.9 wird nur für die Qt-Portierung benötigt. Es ist wahrscheinlich möglich, die Qt-Portierung unter macOS 10.7 und älter zu bauen und zu nutzen, aber das wird nicht offiziell unterstützt. Die SDL-Portierung ist dafür bekannt, mit 10.7 und möglicherweise auf älteren Versionen zu funktionieren. [downloads]: http://mgba.io/downloads.html [source]: https://github.com/mgba-emu/mgba/ diff --git a/README_ES.md b/README_ES.md index 0a5df3e9b..f174e114c 100644 --- a/README_ES.md +++ b/README_ES.md @@ -80,7 +80,7 @@ Plataformas soportadas ------------------- - Windows 7 o más reciente -- OS X 10.8 (Mountain Lion)[[3]](#osxver) o más reciente +- OS X 10.9 (Mavericks)[[3]](#osxver) o más reciente - Linux - FreeBSD - Nintendo 3DS @@ -234,7 +234,7 @@ Notas a pie [2] La detección del tamaño de la memoria flash no funciona en algunos casos. Se pueden configurar en tiempo de ejecución, pero se recomienda ingresar un bug si se encuentra un caso así. -[3] 10.8 sólo se necesita para la versión con Qt. Puede ser posible compilar o hacer funcionar la versión Qt en 10.7 o versiones más antigas, pero esto no está oficialmente soportado. La versión SDL funciona en 10.5, y puede funcionar en versiones anteriores. +[3] 10.9 sólo se necesita para la versión con Qt. Puede ser posible compilar o hacer funcionar la versión Qt en 10.7 o versiones más antigas, pero esto no está oficialmente soportado. La versión SDL funciona en 10.5, y puede funcionar en versiones anteriores. [downloads]: http://mgba.io/downloads.html [source]: https://github.com/mgba-emu/mgba/ diff --git a/README_ZH_CN.md b/README_ZH_CN.md index 89cc6dc24..757f1d0c6 100644 --- a/README_ZH_CN.md +++ b/README_ZH_CN.md @@ -78,7 +78,7 @@ mGBA 是一个运行 Game Boy Advance 游戏的模拟器。mGBA 的目标是比 ------------------- - Windows 7 或更新 -- OS X 10.8(山狮 / Mountain Lion)[[3]](#osxver) 或更新 +- OS X 10.9(Mavericks)[[3]](#osxver) 或更新 - Linux - FreeBSD - Nintendo 3DS @@ -232,7 +232,7 @@ Footnotes [2] 闪存大小检测在某些情况下不起作用。 这些可以在运行时中进行配置,但如果遇到此类情况,建议提交错误。 -[3] 仅 Qt 端口需要 10.8。应该可以在 10.7 或更早版本上构建或运行 Qt 端口,但这类操作不受官方支持。已知 SDL 端口可以在 10.5 上运行,并且可能能够在旧版本上运行。 +[3] 仅 Qt 端口需要 10.9。应该可以在 10.7 或更早版本上构建或运行 Qt 端口,但这类操作不受官方支持。已知 SDL 端口可以在 10.5 上运行,并且可能能够在旧版本上运行。 [downloads]: http://mgba.io/downloads.html [source]: https://github.com/mgba-emu/mgba/ diff --git a/src/platform/qt/CMakeLists.txt b/src/platform/qt/CMakeLists.txt index 203554f39..1ab809f22 100644 --- a/src/platform/qt/CMakeLists.txt +++ b/src/platform/qt/CMakeLists.txt @@ -41,10 +41,18 @@ if(APPLE) list(APPEND QT_DEFINES USE_SHARE_WIDGET) endif() - if(Qt5Widgets_VERSION MATCHES "^5.1[0-9]") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mmacosx-version-min=10.8") + if(Qt5Widgets_VERSION MATCHES "^5.15") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mmacosx-version-min=10.13") + elseif(Qt5Widgets_VERSION MATCHES "^5.1[234]") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mmacosx-version-min=10.12") + elseif(Qt5Widgets_VERSION MATCHES "^5.11") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mmacosx-version-min=10.11") + elseif(Qt5Widgets_VERSION MATCHES "^5.(9|10)") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mmacosx-version-min=10.10") + elseif(Qt5Widgets_VERSION MATCHES "^5.8") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mmacosx-version-min=10.9") else() - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mmacosx-version-min=10.7") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mmacosx-version-min=10.8") endif() if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") From 4fb85142542fb71ddf5c1fc7d765abed112d152b Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 7 May 2022 23:10:48 -0700 Subject: [PATCH 15/20] PSP2: Fix build on recent vitasdk --- src/platform/psp2/sce-vfs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/platform/psp2/sce-vfs.c b/src/platform/psp2/sce-vfs.c index d5a52f177..138d7278c 100644 --- a/src/platform/psp2/sce-vfs.c +++ b/src/platform/psp2/sce-vfs.c @@ -6,6 +6,7 @@ #include #include +#include #include #include From 2a4dcace5b1455f29e3208b0a0580b6fac73b0ba Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 7 May 2022 23:13:34 -0700 Subject: [PATCH 16/20] PSP2: Fix perf build too --- src/platform/psp2/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/platform/psp2/CMakeLists.txt b/src/platform/psp2/CMakeLists.txt index 393f63a4d..fa9847f14 100644 --- a/src/platform/psp2/CMakeLists.txt +++ b/src/platform/psp2/CMakeLists.txt @@ -24,6 +24,7 @@ set(OS_LIB -lvita2d -l${M_LIBRARY} -lSceGxm_stub -lSceIme_stub -lSceMotion_stub + -lSceNetCtl_stub -lScePgf_stub -lScePhotoExport_stub -lScePower_stub From a1ea2066f79be3dacb5097a8cf6e76cbbe2b180e Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 8 May 2022 00:50:08 -0700 Subject: [PATCH 17/20] macOS: Update static Qt libs --- src/platform/qt/CMakeLists.txt | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/platform/qt/CMakeLists.txt b/src/platform/qt/CMakeLists.txt index 1ab809f22..037be083e 100644 --- a/src/platform/qt/CMakeLists.txt +++ b/src/platform/qt/CMakeLists.txt @@ -349,11 +349,10 @@ if(QT_STATIC) elseif(APPLE) find_package(Cups) find_package(Qt5PrintSupport) - find_library(QTFREETYPE NAMES qtfreetype) - find_library(QTHARFBUZZ NAMES qtharfbuzzng qtharfbuzz) - find_library(QTPLATFORMSUPPORT NAMES Qt5PlatformSupport) - list(APPEND QT_LIBRARIES Cups Qt5::PrintSupport Qt5::QCocoaIntegrationPlugin Qt5::CoreAudioPlugin Qt5::AVFServicePlugin Qt5::QCocoaPrinterSupportPlugin ${QTPLATFORMSUPPORT} "-framework AVFoundation" "-framework CoreMedia" "-framework SystemConfiguration" "-framework Security") - set_target_properties(Qt5::Core PROPERTIES INTERFACE_LINK_LIBRARIES "${QTPCRE};${QTHARFBUZZ};${QTFREETYPE}") + list(APPEND QT_LIBRARIES Cups Qt5::PrintSupport Qt5::QCocoaIntegrationPlugin Qt5::CoreAudioPlugin Qt5::AVFServicePlugin Qt5::QCocoaPrinterSupportPlugin) + list(APPEND QT_LIBRARIES Qt5AccessibilitySupport Qt5CglSupport Qt5ClipboardSupport Qt5FontDatabaseSupport Qt5GraphicsSupport Qt5ThemeSupport) + list(APPEND QT_LIBRARIES "-framework AVFoundation" "-framework CoreMedia" "-framework SystemConfiguration" "-framework Security") + set_target_properties(Qt5::Core PROPERTIES INTERFACE_LINK_LIBRARIES "${QTPCRE}") elseif(UNIX) list(APPEND QT_LIBRARIES Qt5::FontDatabaseSupport Qt5::XcbQpa) endif() From 8410ea8b5fc282748515f13e23819f668a1370e7 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 11 May 2022 23:52:11 -0700 Subject: [PATCH 18/20] Qt: Fix deprecation warnings --- src/platform/qt/CoreController.cpp | 4 ++-- src/platform/qt/DebuggerConsoleController.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/platform/qt/CoreController.cpp b/src/platform/qt/CoreController.cpp index 5433054f3..535bbdaf8 100644 --- a/src/platform/qt/CoreController.cpp +++ b/src/platform/qt/CoreController.cpp @@ -194,11 +194,11 @@ CoreController::CoreController(mCore* core, QObject* parent) } va_list argc; va_copy(argc, args); - message = QString().vsprintf(format, argc); + message = QString::vasprintf(format, argc); va_end(argc); QMetaObject::invokeMethod(controller, "statusPosted", Q_ARG(const QString&, message)); } - message = QString().vsprintf(format, args); + message = QString::vasprintf(format, args); QMetaObject::invokeMethod(controller, "logPosted", Q_ARG(int, level), Q_ARG(int, category), Q_ARG(const QString&, message)); if (level == mLOG_FATAL) { mCoreThreadMarkCrashed(controller->thread()); diff --git a/src/platform/qt/DebuggerConsoleController.cpp b/src/platform/qt/DebuggerConsoleController.cpp index a7566b170..3a9446b15 100644 --- a/src/platform/qt/DebuggerConsoleController.cpp +++ b/src/platform/qt/DebuggerConsoleController.cpp @@ -69,7 +69,7 @@ void DebuggerConsoleController::printf(struct CLIDebuggerBackend* be, const char DebuggerConsoleController* self = consoleBe->self; va_list args; va_start(args, fmt); - self->log(QString().vsprintf(fmt, args)); + self->log(QString::vasprintf(fmt, args)); va_end(args); } From cbe5c305b6d6f16ff0148b0ac49a9fc269652385 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 12 May 2022 00:22:01 -0700 Subject: [PATCH 19/20] GBA: Fix mbVf initialization --- src/gba/gba.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/gba/gba.c b/src/gba/gba.c index a39823666..4bf7721c8 100644 --- a/src/gba/gba.c +++ b/src/gba/gba.c @@ -92,13 +92,14 @@ static void GBAInit(void* cpu, struct mCPUComponent* component) { gba->keysActive = 0; gba->keysLast = 0x400; - gba->rotationSource = 0; - gba->luminanceSource = 0; - gba->rtcSource = 0; - gba->rumble = 0; + gba->rotationSource = NULL; + gba->luminanceSource = NULL; + gba->rtcSource = NULL; + gba->rumble = NULL; - gba->romVf = 0; - gba->biosVf = 0; + gba->romVf = NULL; + gba->mbVf = NULL; + gba->biosVf = NULL; gba->stream = NULL; gba->keyCallback = NULL; From aad85a795064eb576edc80f1f3153671e6d90eaf Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 12 May 2022 23:37:41 -0700 Subject: [PATCH 20/20] Qt: Fix more deprecation warnings --- src/platform/qt/CheatsView.cpp | 4 ++++ src/platform/qt/FrameView.h | 4 ++++ src/platform/qt/LogController.cpp | 6 ++++++ src/platform/qt/MemoryModel.cpp | 7 ++++++- src/platform/qt/MessagePainter.cpp | 4 ++++ src/platform/qt/ShortcutView.cpp | 4 ++++ 6 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/platform/qt/CheatsView.cpp b/src/platform/qt/CheatsView.cpp index 5c5594067..d26e63bcd 100644 --- a/src/platform/qt/CheatsView.cpp +++ b/src/platform/qt/CheatsView.cpp @@ -149,7 +149,11 @@ void CheatsView::enterCheat() { index = m_model.index(m_model.rowCount() - 1, 0, QModelIndex()); m_ui.cheatList->selectionModel()->select(index, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); } +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + QStringList cheats = m_ui.codeEntry->toPlainText().split('\n', Qt::SkipEmptyParts); +#else QStringList cheats = m_ui.codeEntry->toPlainText().split('\n', QString::SkipEmptyParts); +#endif for (const QString& string : cheats) { m_model.beginAppendRow(index); mCheatAddLine(set, string.toUtf8().constData(), m_codeType); diff --git a/src/platform/qt/FrameView.h b/src/platform/qt/FrameView.h index 02844d7ed..5e32a3de6 100644 --- a/src/platform/qt/FrameView.h +++ b/src/platform/qt/FrameView.h @@ -97,7 +97,11 @@ private: int m_glowFrame; QTimer m_glowTimer; +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + QRecursiveMutex m_mutex; +#else QMutex m_mutex{QMutex::Recursive}; +#endif VFile* m_currentFrame = nullptr; VFile* m_nextFrame = nullptr; mCore* m_vl = nullptr; diff --git a/src/platform/qt/LogController.cpp b/src/platform/qt/LogController.cpp index 994f484f7..8b11e76e4 100644 --- a/src/platform/qt/LogController.cpp +++ b/src/platform/qt/LogController.cpp @@ -11,6 +11,12 @@ using namespace QGBA; +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) +#define endl Qt::endl +#else +#define endl std::endl +#endif + LogController LogController::s_global(mLOG_ALL); int LogController::s_qtCat{-1}; diff --git a/src/platform/qt/MemoryModel.cpp b/src/platform/qt/MemoryModel.cpp index d0f3df1f6..4c991982d 100644 --- a/src/platform/qt/MemoryModel.cpp +++ b/src/platform/qt/MemoryModel.cpp @@ -80,7 +80,12 @@ MemoryModel::MemoryModel(QWidget* parent) setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); - m_margins = QMargins(metrics.width("0FFFFFF0 ") + 3, m_cellHeight + 1, metrics.width(" AAAAAAAAAAAAAAAA") + 3, 0); + m_margins = QMargins(3, m_cellHeight + 1, 3, 0); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) + m_margins += QMargins(metrics.horizontalAdvance("0FFFFFF0 "), 0, metrics.horizontalAdvance(" AAAAAAAAAAAAAAAA"), 0); +#else + m_margins += QMargins(metrics.width("0FFFFFF0 "), 0, metrics.width(" AAAAAAAAAAAAAAAA"), 0); +#endif m_cellSize = QSizeF((viewport()->size().width() - (m_margins.left() + m_margins.right())) / 16.0, m_cellHeight); connect(verticalScrollBar(), &QSlider::sliderMoved, [this](int position) { diff --git a/src/platform/qt/MessagePainter.cpp b/src/platform/qt/MessagePainter.cpp index 401d7cf4b..d036b4ca7 100644 --- a/src/platform/qt/MessagePainter.cpp +++ b/src/platform/qt/MessagePainter.cpp @@ -84,7 +84,11 @@ void MessagePainter::paint(QPainter* painter) { painter->setRenderHint(QPainter::Antialiasing); painter->setFont(m_frameFont); painter->setPen(Qt::black); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) + painter->translate(-metrics.horizontalAdvance(frame), 0); +#else painter->translate(-metrics.width(frame), 0); +#endif const static int ITERATIONS = 11; for (int i = 0; i < ITERATIONS; ++i) { painter->save(); diff --git a/src/platform/qt/ShortcutView.cpp b/src/platform/qt/ShortcutView.cpp index badd7d5af..e82c2dd9b 100644 --- a/src/platform/qt/ShortcutView.cpp +++ b/src/platform/qt/ShortcutView.cpp @@ -137,7 +137,11 @@ void ShortcutView::closeEvent(QCloseEvent*) { void ShortcutView::showEvent(QShowEvent*) { QString longString("Ctrl+Alt+Shift+Tab"); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) + int width = QFontMetrics(QFont()).horizontalAdvance(longString); +#else int width = QFontMetrics(QFont()).width(longString); +#endif QHeaderView* header = m_ui.shortcutTable->header(); header->resizeSection(0, header->length() - width * 2); header->resizeSection(1, width);