diff --git a/CHANGES b/CHANGES
index 70aac8dbc..734eecd57 100644
--- a/CHANGES
+++ b/CHANGES
@@ -59,6 +59,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)
@@ -77,6 +78,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
@@ -89,6 +91,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
@@ -109,6 +112,8 @@ 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)
+ - 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/README.md b/README.md
index 801dcaaf9..bd4c7585e 100644
--- a/README.md
+++ b/README.md
@@ -85,7 +85,7 @@ Supported Platforms
-------------------
- Windows 7 or newer
-- OS X 10.8 (Mountain Lion)[[4]](#osxver) or newer
+- OS X 10.9 (Mavericks)[[4]](#osxver) or newer
- Linux
- FreeBSD
@@ -292,7 +292,7 @@ Missing features on DS are
[3] 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.
-[4] 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.
+[4] 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/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/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/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);
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) {
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/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) {
diff --git a/src/gb/renderers/software.c b/src/gb/renderers/software.c
index a5e41162f..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) {
+ 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) {
+ 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) {
+ 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)) {
diff --git a/src/gba/gba.c b/src/gba/gba.c
index 68d77d9f0..495196b72 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;
@@ -139,7 +140,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 +148,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 +166,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 +232,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 +373,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/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/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
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
diff --git a/src/platform/qt/CMakeLists.txt b/src/platform/qt/CMakeLists.txt
index d45a4a773..5a2d61fb7 100644
--- a/src/platform/qt/CMakeLists.txt
+++ b/src/platform/qt/CMakeLists.txt
@@ -42,10 +42,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++")
@@ -275,7 +283,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)
@@ -340,11 +352,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()
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/ConfigController.cpp b/src/platform/qt/ConfigController.cpp
index e6162237b..66c89b4d5 100644
--- a/src/platform/qt/ConfigController.cpp
+++ b/src/platform/qt/ConfigController.cpp
@@ -13,6 +13,12 @@
#include
+static const mOption s_frontendOptions[] = {
+ { "ecard", true, '\0' },
+ { "mb", true, '\0' },
+ { 0 }
+};
+
using namespace QGBA;
ConfigOption::ConfigOption(const QString& name, QObject* parent)
@@ -125,18 +131,57 @@ 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 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);
+ 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;
+ }
+ if (optionName == QLatin1String("mb")) {
+ self->m_argvOptions[optionName] = QString::fromUtf8(arg);
+ return true;
+ }
+ return false;
+ };
+ m_subparsers[1].apply = nullptr;
+ m_subparsers[1].extraOptions = nullptr;
+ m_subparsers[1].longOptions = s_frontendOptions;
+ m_subparsers[1].opts = this;
}
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;
+ }
+
+ 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;
@@ -192,6 +237,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();
@@ -299,6 +352,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..57fbda9c5 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);
@@ -80,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);
@@ -91,6 +95,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();
@@ -106,11 +114,18 @@ public slots:
void write();
private:
+ void addArgvOption(const QString& key, const QVariant& value);
+
Configuration* defaults() { return &m_config.defaultsTable; }
mCoreConfig m_config;
mCoreOptions m_opts{};
-
+ mArguments m_args{};
+ mGraphicsOpts m_graphicsOpts{};
+ std::array m_subparsers;
+ bool m_parsed = false;
+
+ 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 d633fdede..06fc0afdb 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());
@@ -885,8 +885,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.isEmpty() || 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;
@@ -905,6 +928,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 044fae403..6476f2317 100644
--- a/src/platform/qt/CoreController.h
+++ b/src/platform/qt/CoreController.h
@@ -160,6 +160,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/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);
}
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/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
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);
diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp
index d4cdcbfb8..bdfd5b903 100644
--- a/src/platform/qt/Window.cpp
+++ b/src/platform/qt/Window.cpp
@@ -212,8 +212,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;
@@ -236,9 +236,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) {
@@ -975,8 +990,19 @@ 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) {
+ QVariant eCardList = m_config->takeArgvOption(QString("ecard"));
+ if (eCardList.canConvert(QMetaType::QStringList)) {
+ m_controller->scanCards(eCardList.toStringList());
+ }
+ }
+#endif
+
#ifdef USE_DISCORD_RPC
DiscordCoordinator::gameStarted(m_controller);
#endif
@@ -2071,6 +2097,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);
diff --git a/src/platform/qt/Window.h b/src/platform/qt/Window.h
index fe30ba2bd..3dea3d1e1 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/CMakeLists.txt b/src/platform/sdl/CMakeLists.txt
index 86c449076..f394f8e67 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)
diff --git a/src/platform/sdl/main.c b/src/platform/sdl/main.c
index d558ed6e0..ac37590d0 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, renderer.core->inputInfo);
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 530dcba85..a80f7fc17 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);
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}