diff --git a/CHANGES b/CHANGES index 67d1f0611..fb0b70887 100644 --- a/CHANGES +++ b/CHANGES @@ -46,6 +46,7 @@ Features: - New unlicensed GB mappers: NT (older types 1 and 2), Li Cheng, GGB-81 - Debugger: Add range watchpoints Emulation fixes: + - GB Video: Implement DMG-style sprite ordering - GBA Audio: Fix improperly deserializing GB audio registers (fixes mgba.io/i/2793) - GBA Memory: Make VRAM access stalls only apply to BG RAM - GBA SIO: Fix SIOCNT SI pin value after attaching player 2 (fixes mgba.io/i/2805) @@ -62,6 +63,9 @@ Other fixes: - Qt: Disable attempted linking betwen incompatible platforms (fixes mgba.io/i/2702) - Qt: Fix modifier key names in shortcut editor (fixes mgba.io/i/2817) - Qt: Fix a handful of edge cases with graphics viewers (fixes mgba.io/i/2827) + - Qt: Fix full-buffer rewind + - Qt: Fix crash if loading a shader fails + - Qt: Fix black screen when starting with a game (fixes mgba.io/i/2781) - Scripting: Fix receiving packets for client sockets - Scripting: Fix empty receive calls returning unknown error on Windows Misc: diff --git a/CMakeLists.txt b/CMakeLists.txt index fcec15990..71f1a6152 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,7 +34,7 @@ if(NOT MSVC) set(WARNING_FLAGS "${WARNING_FLAGS} -Wno-format") endif() set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS} -Werror=implicit-function-declaration -Werror=implicit-int") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNING_FLAGS}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNING_FLAGS} -Woverloaded-virtual") else() set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_CRT_SECURE_NO_WARNINGS /wd4003 /wd4244 /wd4146 /wd4267 /Zc:preprocessor-") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_CRT_SECURE_NO_WARNINGS /wd4003 /wd4244 /wd4146 /wd4267 /Zc:preprocessor-") diff --git a/cinema/gb/acid/cgb-acid2/baseline_0000.png b/cinema/gb/acid/cgb-acid2/baseline_0000.png new file mode 100644 index 000000000..4baff3425 Binary files /dev/null and b/cinema/gb/acid/cgb-acid2/baseline_0000.png differ diff --git a/cinema/gb/acid/cgb-acid2/test.gbc b/cinema/gb/acid/cgb-acid2/test.gbc new file mode 100644 index 000000000..5f71bd360 Binary files /dev/null and b/cinema/gb/acid/cgb-acid2/test.gbc differ diff --git a/cinema/gb/acid/config.ini b/cinema/gb/acid/config.ini new file mode 100644 index 000000000..e6b9f2af6 --- /dev/null +++ b/cinema/gb/acid/config.ini @@ -0,0 +1,6 @@ +[testinfo] +skip=15 +frames=1 + +[ports.cinema] +sgb.borders=0 diff --git a/cinema/gb/acid/dmg-acid2/baseline_0000.png b/cinema/gb/acid/dmg-acid2/baseline_0000.png new file mode 100644 index 000000000..fde56ebc3 Binary files /dev/null and b/cinema/gb/acid/dmg-acid2/baseline_0000.png differ diff --git a/cinema/gb/acid/dmg-acid2/test.gb b/cinema/gb/acid/dmg-acid2/test.gb new file mode 100644 index 000000000..a25ef9485 Binary files /dev/null and b/cinema/gb/acid/dmg-acid2/test.gb differ diff --git a/cinema/gb/mooneye-gb/manual-only/sprite_priority/baseline_0000.png b/cinema/gb/mooneye-gb/manual-only/sprite_priority/baseline_0000.png index 64aac5965..cb3569baf 100644 Binary files a/cinema/gb/mooneye-gb/manual-only/sprite_priority/baseline_0000.png and b/cinema/gb/mooneye-gb/manual-only/sprite_priority/baseline_0000.png differ diff --git a/cinema/gb/mooneye-gb/manual-only/sprite_priority/xbaseline_0000.png b/cinema/gb/mooneye-gb/manual-only/sprite_priority/xbaseline_0000.png deleted file mode 100644 index 2201a6ac5..000000000 Binary files a/cinema/gb/mooneye-gb/manual-only/sprite_priority/xbaseline_0000.png and /dev/null differ diff --git a/include/mgba-util/platform/windows/getopt.h b/include/mgba-util/platform/windows/getopt.h index 5ea2dbe6f..22a196ef6 100644 --- a/include/mgba-util/platform/windows/getopt.h +++ b/include/mgba-util/platform/windows/getopt.h @@ -27,6 +27,8 @@ extern "C" { #endif +struct option; + #define REPLACE_GETOPT /* use this getopt as the system getopt(3) */ #ifdef REPLACE_GETOPT diff --git a/include/mgba-util/socket.h b/include/mgba-util/socket.h index 9f07491bd..96edc3d92 100644 --- a/include/mgba-util/socket.h +++ b/include/mgba-util/socket.h @@ -204,6 +204,7 @@ static inline Socket SocketOpenTCP(int port, const struct Address* bindAddress) err = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)); #endif if (err) { + SocketCloseQuiet(sock); return INVALID_SOCKET; } diff --git a/src/core/cheats.c b/src/core/cheats.c index 688b32ebe..60f73abcc 100644 --- a/src/core/cheats.c +++ b/src/core/cheats.c @@ -489,8 +489,10 @@ bool mCheatParseEZFChtFile(struct mCheatDevice* device, struct VFile* vf) { return false; } char* name = gbkToUtf8(&cheat[1], end - cheat - 1); - strncpy(cheatName, name, sizeof(cheatName) - 1); - free(name); + if (name) { + strncpy(cheatName, name, sizeof(cheatName) - 1); + free(name); + } cheatNameLength = strlen(cheatName); continue; } @@ -501,7 +503,10 @@ bool mCheatParseEZFChtFile(struct mCheatDevice* device, struct VFile* vf) { } if (strncmp(cheat, "ON", eq - cheat) != 0) { char* subname = gbkToUtf8(cheat, eq - cheat); - snprintf(&cheatName[cheatNameLength], sizeof(cheatName) - cheatNameLength - 1, ": %s", subname); + if (subname) { + snprintf(&cheatName[cheatNameLength], sizeof(cheatName) - cheatNameLength - 1, ": %s", subname); + free(subname); + } } set = device->createSet(device, cheatName); set->enabled = false; diff --git a/src/core/config.c b/src/core/config.c index 6bfcb8e8b..715628f0f 100644 --- a/src/core/config.c +++ b/src/core/config.c @@ -169,14 +169,14 @@ void mCoreConfigDeinit(struct mCoreConfig* config) { #if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2 bool mCoreConfigLoad(struct mCoreConfig* config) { - char path[PATH_MAX]; + char path[PATH_MAX + 1]; mCoreConfigDirectory(path, PATH_MAX); strncat(path, PATH_SEP "config.ini", PATH_MAX - strlen(path)); return mCoreConfigLoadPath(config, path); } bool mCoreConfigSave(const struct mCoreConfig* config) { - char path[PATH_MAX]; + char path[PATH_MAX + 1]; mCoreConfigDirectory(path, PATH_MAX); strncat(path, PATH_SEP "config.ini", PATH_MAX - strlen(path)); return mCoreConfigSavePath(config, path); @@ -304,7 +304,7 @@ void mCoreConfigPortablePath(char* out, size_t outLength) { CFRelease(suburl); } #endif - strncat(out, PATH_SEP "portable.ini", outLength - strlen(out)); + strncat(out, PATH_SEP "portable.ini", outLength - strlen(out) - 1); #endif } diff --git a/src/core/log.c b/src/core/log.c index 2d90fe05e..5fe2fe9ee 100644 --- a/src/core/log.c +++ b/src/core/log.c @@ -50,7 +50,7 @@ const char* mLogCategoryName(int category) { } const char* mLogCategoryId(int category) { - if (category < MAX_CATEGORY) { + if (category >= 0 && category < MAX_CATEGORY) { return _categoryIds[category]; } return NULL; @@ -88,6 +88,7 @@ void mLogExplicit(struct mLogger* context, int category, enum mLogLevel level, c if (!context->filter || mLogFilterTest(context->filter, category, level)) { context->log(context, category, level, format, args); } + va_end(args); } void mLogFilterInit(struct mLogFilter* filter) { diff --git a/src/debugger/gdb-stub.c b/src/debugger/gdb-stub.c index ca8c0d1ec..6c318179c 100644 --- a/src/debugger/gdb-stub.c +++ b/src/debugger/gdb-stub.c @@ -846,6 +846,7 @@ void GDBStubUpdate(struct GDBStub* stub) { } else { goto connectionLost; } + SocketSetTCPPush(stub->connection, 1); } while (true) { if (stub->shouldBlock) { diff --git a/src/feature/sqlite3/no-intro.c b/src/feature/sqlite3/no-intro.c index d54e188ff..649364de6 100644 --- a/src/feature/sqlite3/no-intro.c +++ b/src/feature/sqlite3/no-intro.c @@ -17,7 +17,7 @@ struct NoIntroDB { }; struct NoIntroDB* NoIntroDBLoad(const char* path) { - struct NoIntroDB* db = malloc(sizeof(*db)); + struct NoIntroDB* db = calloc(1, sizeof(*db)); if (sqlite3_open_v2(path, &db->db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX, NULL)) { goto error; @@ -60,9 +60,6 @@ struct NoIntroDB* NoIntroDBLoad(const char* path) { return db; error: - if (db->crc32) { - sqlite3_finalize(db->crc32); - } NoIntroDBDestroy(db); return NULL; @@ -285,8 +282,12 @@ bool NoIntroDBLoadClrMamePro(struct NoIntroDB* db, struct VFile* vf) { } void NoIntroDBDestroy(struct NoIntroDB* db) { - sqlite3_finalize(db->crc32); - sqlite3_close(db->db); + if (db->crc32) { + sqlite3_finalize(db->crc32); + } + if (db->db) { + sqlite3_close(db->db); + } free(db); } diff --git a/src/feature/updater-main.c b/src/feature/updater-main.c index bc29e5cd4..cd72fc4f3 100644 --- a/src/feature/updater-main.c +++ b/src/feature/updater-main.c @@ -114,7 +114,7 @@ int main(int argc, char* argv[]) { int ok = 1; mCoreConfigDirectory(bin, sizeof(bin)); - strncat(bin, "/updater.log", sizeof(bin)); + strncat(bin, "/updater.log", sizeof(bin) - 1); logfile = fopen(bin, "w"); mCoreConfigInit(&config, "updater"); diff --git a/src/feature/video-logger.c b/src/feature/video-logger.c index ae2fa046e..fb2065b42 100644 --- a/src/feature/video-logger.c +++ b/src/feature/video-logger.c @@ -382,10 +382,7 @@ static void _copyVf(struct VFile* dest, struct VFile* src) { static void _compress(struct VFile* dest, struct VFile* src) { uint8_t writeBuffer[0x800]; uint8_t compressBuffer[0x400]; - z_stream zstr; - zstr.zalloc = Z_NULL; - zstr.zfree = Z_NULL; - zstr.opaque = Z_NULL; + z_stream zstr = {0}; zstr.avail_in = 0; zstr.avail_out = sizeof(compressBuffer); zstr.next_out = (Bytef*) compressBuffer; @@ -425,10 +422,7 @@ static void _compress(struct VFile* dest, struct VFile* src) { static bool _decompress(struct VFile* dest, struct VFile* src, size_t compressedLength) { uint8_t fbuffer[0x400]; uint8_t zbuffer[0x800]; - z_stream zstr; - zstr.zalloc = Z_NULL; - zstr.zfree = Z_NULL; - zstr.opaque = Z_NULL; + z_stream zstr = {0}; zstr.avail_in = 0; zstr.avail_out = sizeof(zbuffer); zstr.next_out = (Bytef*) zbuffer; diff --git a/src/gb/core.c b/src/gb/core.c index 2e631686f..ebf40385a 100644 --- a/src/gb/core.c +++ b/src/gb/core.c @@ -619,16 +619,16 @@ static void _GBCoreReset(struct mCore* core) { switch (gb->model) { case GB_MODEL_DMG: case GB_MODEL_MGB: // TODO - strncat(path, PATH_SEP "gb_bios.bin", PATH_MAX - strlen(path)); + strncat(path, PATH_SEP "gb_bios.bin", PATH_MAX - strlen(path) - 1); break; case GB_MODEL_SGB: case GB_MODEL_SGB2: // TODO - strncat(path, PATH_SEP "sgb_bios.bin", PATH_MAX - strlen(path)); + strncat(path, PATH_SEP "sgb_bios.bin", PATH_MAX - strlen(path) - 1); break; case GB_MODEL_CGB: case GB_MODEL_AGB: case GB_MODEL_SCGB: - strncat(path, PATH_SEP "gbc_bios.bin", PATH_MAX - strlen(path)); + strncat(path, PATH_SEP "gbc_bios.bin", PATH_MAX - strlen(path) - 1); break; default: break; diff --git a/src/gb/gb.c b/src/gb/gb.c index a3a870f28..6e3b92b37 100644 --- a/src/gb/gb.c +++ b/src/gb/gb.c @@ -405,7 +405,9 @@ void GBUnloadROM(struct GB* gb) { if (gb->romVf) { #ifndef FIXED_ROM_BUFFER - gb->romVf->unmap(gb->romVf, gb->memory.rom, gb->pristineRomSize); + if (gb->isPristine && gb->memory.rom) { + gb->romVf->unmap(gb->romVf, gb->memory.rom, gb->pristineRomSize); + } #endif gb->romVf->close(gb->romVf); gb->romVf = NULL; diff --git a/src/gb/memory.c b/src/gb/memory.c index 5c4d97876..325761c1c 100644 --- a/src/gb/memory.c +++ b/src/gb/memory.c @@ -14,6 +14,7 @@ #include #include +#include mLOG_DEFINE_CATEGORY(GB_MEM, "GB Memory", "gb.memory"); @@ -474,13 +475,14 @@ uint8_t GBView8(struct SM83Core* cpu, uint16_t address, int segment) { if (memory->rtcAccess) { return memory->rtcRegs[memory->activeRtcReg]; } else if (memory->sramAccess) { - if (segment < 0 && memory->sram) { - return memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)]; - } else if ((size_t) segment * GB_SIZE_EXTERNAL_RAM < gb->sramSize) { - return memory->sram[(address & (GB_SIZE_EXTERNAL_RAM - 1)) + segment *GB_SIZE_EXTERNAL_RAM]; - } else { - return 0xFF; + if (memory->sram) { + if (segment < 0) { + return memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)]; + } else if ((size_t) segment * GB_SIZE_EXTERNAL_RAM < gb->sramSize) { + return memory->sram[(address & (GB_SIZE_EXTERNAL_RAM - 1)) + segment *GB_SIZE_EXTERNAL_RAM]; + } } + return 0xFF; } else if (memory->mbcRead) { return memory->mbcRead(memory, address); } else if (memory->mbcType == GB_HuC3) { @@ -1005,6 +1007,11 @@ void _pristineCow(struct GB* gb) { if (gb->memory.rom == gb->memory.romBase) { gb->memory.romBase = newRom; } + if (gb->romVf) { + gb->romVf->unmap(gb->romVf, gb->memory.rom, gb->memory.romSize); + gb->romVf->close(gb->romVf); + gb->romVf = NULL; + } gb->memory.rom = newRom; GBMBCSwitchBank(gb, gb->memory.currentBank); gb->isPristine = false; diff --git a/src/gb/renderers/software.c b/src/gb/renderers/software.c index a5e41162f..d157920fa 100644 --- a/src/gb/renderers/software.c +++ b/src/gb/renderers/software.c @@ -571,20 +571,38 @@ static void _cleanOAM(struct GBVideoSoftwareRenderer* renderer, int y) { } int o = 0; int i; + int16_t ids[GB_VIDEO_MAX_LINE_OBJ]; for (i = 0; i < GB_VIDEO_MAX_OBJ && o < GB_VIDEO_MAX_LINE_OBJ; ++i) { uint8_t oy = renderer->d.oam->obj[i].y; if (y < oy - 16 || y >= oy - 16 + spriteHeight) { continue; } - // TODO: Sort - renderer->obj[o].obj = renderer->d.oam->obj[i]; - renderer->obj[o].index = i; + ids[o] = (renderer->d.oam->obj[i].x << 7) | i; ++o; - if (o == 10) { - break; - } } renderer->objMax = o; + if (renderer->model < GB_MODEL_CGB) { + // Terrble n^2 sort, but it's only 10 elements so it shouldn't be that bad + int16_t ids2[GB_VIDEO_MAX_LINE_OBJ]; + int min = -1; + int j; + for (i = 0; i < o; ++i) { + int min2 = 0xFFFF; + for (j = 0; j < o; ++j) { + if (ids[j] > min && ids[j] < min2) { + min2 = ids[j]; + } + } + min = min2; + ids2[i] = min; + } + memcpy(ids, ids2, sizeof(ids)); + } + for (i = 0; i < o; ++i) { + int id = ids[i] & 0x7F; + renderer->obj[i].obj = renderer->d.oam->obj[id]; + renderer->obj[i].index = id; + } } static void GBVideoSoftwareRendererDrawRange(struct GBVideoRenderer* renderer, int startX, int endX, int y) { diff --git a/src/gba/core.c b/src/gba/core.c index 46dea505f..db2f14ab9 100644 --- a/src/gba/core.c +++ b/src/gba/core.c @@ -695,7 +695,7 @@ static void _GBACoreReset(struct mCore* core) { if (!found) { char path[PATH_MAX]; mCoreConfigDirectory(path, PATH_MAX); - strncat(path, PATH_SEP "gba_bios.bin", PATH_MAX - strlen(path)); + strncat(path, PATH_SEP "gba_bios.bin", PATH_MAX - strlen(path) - 1); bios = VFileOpen(path, O_RDONLY); if (bios && GBAIsBIOS(bios)) { found = true; diff --git a/src/gba/overrides.c b/src/gba/overrides.c index e15db50d8..ee286cfe0 100644 --- a/src/gba/overrides.c +++ b/src/gba/overrides.c @@ -159,6 +159,10 @@ static const struct GBACartridgeOverride _overrides[] = { // Shin Bokura no Taiyou: Gyakushuu no Sabata { "U33J", SAVEDATA_EEPROM, HW_RTC | HW_LIGHT_SENSOR, IDLE_LOOP_NONE, false }, + // Stuart Little 2 + { "ASLE", SAVEDATA_FORCE_NONE, HW_NONE, IDLE_LOOP_NONE, false }, + { "ASLF", SAVEDATA_FORCE_NONE, HW_NONE, IDLE_LOOP_NONE, false }, + // Super Mario Advance 2 { "AA2J", SAVEDATA_EEPROM, HW_NONE, 0x800052E, false }, { "AA2E", SAVEDATA_EEPROM, HW_NONE, 0x800052E, false }, diff --git a/src/gba/savedata.c b/src/gba/savedata.c index 455ee0802..2d377a875 100644 --- a/src/gba/savedata.c +++ b/src/gba/savedata.c @@ -184,7 +184,7 @@ size_t GBASavedataSize(const struct GBASavedata* savedata) { bool GBASavedataLoad(struct GBASavedata* savedata, struct VFile* in) { if (savedata->data) { - if (!in && savedata->type != SAVEDATA_FORCE_NONE) { + if (!in || savedata->type == SAVEDATA_FORCE_NONE) { return false; } ssize_t size = GBASavedataSize(savedata); diff --git a/src/gba/sio/lockstep.c b/src/gba/sio/lockstep.c index 3ea1b5cc9..8e641c539 100644 --- a/src/gba/sio/lockstep.c +++ b/src/gba/sio/lockstep.c @@ -463,7 +463,7 @@ static void _GBASIOLockstepNodeProcessEvents(struct mTiming* timing, void* user, struct GBASIOLockstepNode* node = user; mLockstepLock(&node->p->d); - int32_t cycles = cycles = node->nextEvent; + int32_t cycles = node->nextEvent; node->nextEvent -= cyclesLate; node->eventDiff += cyclesLate; if (node->p->d.attached < 2) { diff --git a/src/platform/opengl/gles2.c b/src/platform/opengl/gles2.c index 88ac000a5..ffbac5a04 100644 --- a/src/platform/opengl/gles2.c +++ b/src/platform/opengl/gles2.c @@ -1053,6 +1053,7 @@ bool mGLES2ShaderLoad(struct VideoShader* shader, struct VDir* dir) { for (n = 0; n < inShaders; ++n) { mGLES2ShaderDeinit(&shaderBlock[n]); } + free(shaderBlock); } } } diff --git a/src/platform/qt/ConfigController.cpp b/src/platform/qt/ConfigController.cpp index 1116dba79..73097df09 100644 --- a/src/platform/qt/ConfigController.cpp +++ b/src/platform/qt/ConfigController.cpp @@ -343,6 +343,7 @@ constexpr const char* ConfigController::mruName(ConfigController::MRU mru) { case MRU::Script: return "recentScripts"; } + Q_UNREACHABLE(); } void ConfigController::write() { diff --git a/src/platform/qt/CoreController.cpp b/src/platform/qt/CoreController.cpp index a711ea634..c71bb4da7 100644 --- a/src/platform/qt/CoreController.cpp +++ b/src/platform/qt/CoreController.cpp @@ -80,7 +80,7 @@ CoreController::CoreController(mCore* core, QObject* parent) m_threadContext.resetCallback = [](mCoreThread* context) { CoreController* controller = static_cast(context->userData); - for (auto action : controller->m_resetActions) { + for (auto& action : controller->m_resetActions) { action(); } @@ -522,9 +522,6 @@ void CoreController::setRewinding(bool rewind) { } void CoreController::rewind(int states) { - if (!states) { - return; - } if (!m_threadContext.core->opts.rewindEnable) { emit statusPosted(tr("Rewinding not currently enabled")); } diff --git a/src/platform/qt/CoreManager.cpp b/src/platform/qt/CoreManager.cpp index cac1c2560..7bd6bd309 100644 --- a/src/platform/qt/CoreManager.cpp +++ b/src/platform/qt/CoreManager.cpp @@ -16,6 +16,7 @@ #endif #include +#include #include using namespace QGBA; @@ -161,7 +162,7 @@ CoreController* CoreManager::loadBIOS(int platform, const QString& path) { mCoreConfigSetOverrideIntValue(&core->config, "skipBios", 0); QByteArray bytes(info.baseName().toUtf8()); - strncpy(core->dirs.baseName, bytes.constData(), sizeof(core->dirs.baseName)); + strlcpy(core->dirs.baseName, bytes.constData(), sizeof(core->dirs.baseName)); bytes = info.dir().canonicalPath().toUtf8(); mDirectorySetAttachBase(&core->dirs, VDirOpen(bytes.constData())); diff --git a/src/platform/qt/Display.cpp b/src/platform/qt/Display.cpp index 23952acb4..09aa716e1 100644 --- a/src/platform/qt/Display.cpp +++ b/src/platform/qt/Display.cpp @@ -130,9 +130,9 @@ void QGBA::Display::configure(ConfigController* config) { config->updateOption("showOSD"); config->updateOption("showFrameCounter"); #if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(BUILD_GLES3) - if (opts->shader) { + if (opts->shader && supportsShaders()) { struct VDir* shader = VDirOpen(opts->shader); - if (shader && supportsShaders()) { + if (shader) { setShaders(shader); shader->close(shader); } diff --git a/src/platform/qt/DisplayGL.cpp b/src/platform/qt/DisplayGL.cpp index c78a8a4d6..ae1d8e750 100644 --- a/src/platform/qt/DisplayGL.cpp +++ b/src/platform/qt/DisplayGL.cpp @@ -48,6 +48,11 @@ using QOpenGLFunctions_Baseline = QOpenGLFunctions_3_2_Core; using namespace QGBA; +enum ThreadStartFrom { + START = 1, + PROXY = 2, +}; + QHash DisplayGL::s_supports; uint qHash(const QSurfaceFormat& format, uint seed) { @@ -238,7 +243,16 @@ void DisplayGL::startDrawing(std::shared_ptr controller) { messagePainter()->resize(size(), devicePixelRatio()); #endif - CoreController::Interrupter interrupter(controller); + startThread(ThreadStartFrom::START); +} + +void DisplayGL::startThread(int from) { + m_threadStartPending |= from; + if (m_threadStartPending < 3) { + return; + } + + CoreController::Interrupter interrupter(m_context); QMetaObject::invokeMethod(m_painter.get(), "start"); if (!m_gl) { if (shouldDisableUpdates()) { @@ -313,6 +327,7 @@ void DisplayGL::stopDrawing() { hide(); } setUpdatesEnabled(true); + m_threadStartPending &= ~1; } m_context.reset(); } @@ -438,6 +453,7 @@ void DisplayGL::setupProxyThread() { #if defined(_WIN32) && defined(USE_EPOXY) epoxy_handle_external_wglMakeCurrent(); #endif + QMetaObject::invokeMethod(this, "startThread", Q_ARG(int, ThreadStartFrom::PROXY)); }); connect(m_painter.get(), &PainterGL::texSwapped, m_proxyContext.get(), [this]() { if (!m_context->hardwareAccelerated()) { @@ -918,8 +934,9 @@ void PainterGL::setShaders(struct VDir* dir) { mGLES2ShaderDetach(reinterpret_cast(m_backend)); mGLES2ShaderFree(&m_shader); } - mGLES2ShaderLoad(&m_shader, dir); - mGLES2ShaderAttach(reinterpret_cast(m_backend), static_cast(m_shader.passes), m_shader.nPasses); + if (mGLES2ShaderLoad(&m_shader, dir)) { + mGLES2ShaderAttach(reinterpret_cast(m_backend), static_cast(m_shader.passes), m_shader.nPasses); + } if (!m_started) { m_gl->doneCurrent(); diff --git a/src/platform/qt/DisplayGL.h b/src/platform/qt/DisplayGL.h index 3ed31a9d8..a0c0c17d4 100644 --- a/src/platform/qt/DisplayGL.h +++ b/src/platform/qt/DisplayGL.h @@ -113,6 +113,7 @@ protected: virtual void resizeEvent(QResizeEvent*) override; private slots: + void startThread(int); void setupProxyThread(); private: @@ -123,6 +124,7 @@ private: bool m_isDrawing = false; bool m_hasStarted = false; + int m_threadStartPending = 0; std::unique_ptr m_painter; QThread m_drawThread; QThread m_proxyThread; diff --git a/src/platform/qt/LogController.h b/src/platform/qt/LogController.h index 548be8b4d..6bd4e5707 100644 --- a/src/platform/qt/LogController.h +++ b/src/platform/qt/LogController.h @@ -79,8 +79,8 @@ public slots: private: mLogFilter m_filter; - bool m_logToFile; - bool m_logToStdout; + bool m_logToFile = false; + bool m_logToStdout = false; std::unique_ptr m_logFile; std::unique_ptr m_logStream; diff --git a/src/platform/qt/MemorySearch.cpp b/src/platform/qt/MemorySearch.cpp index 8589f9cfe..f61fb9e52 100644 --- a/src/platform/qt/MemorySearch.cpp +++ b/src/platform/qt/MemorySearch.cpp @@ -178,7 +178,7 @@ void MemorySearch::refresh() { mCoreMemorySearchResult* result = mCoreMemorySearchResultsGetPointer(&m_results, i); QTableWidgetItem* item = new QTableWidgetItem(QString("%1").arg(result->address, 8, 16, QChar('0'))); m_ui.results->setItem(i, 0, item); - QTableWidgetItem* type; + QTableWidgetItem* type = nullptr; QByteArray string; if (result->type == mCORE_MEMORY_SEARCH_INT && m_ui.numHex->isChecked()) { switch (result->width) { @@ -213,7 +213,12 @@ void MemorySearch::refresh() { string.append(core->rawRead8(core, result->address + i, result->segment)); } item = new QTableWidgetItem(QLatin1String(string)); // TODO + break; + case mCORE_MEMORY_SEARCH_GUESS: + item = nullptr; + break; } + Q_ASSERT(item); } QString divisor; if (result->guessDivisor > 1) { @@ -231,7 +236,12 @@ void MemorySearch::refresh() { break; case mCORE_MEMORY_SEARCH_STRING: type = new QTableWidgetItem("string"); + break; + case mCORE_MEMORY_SEARCH_GUESS: + break; } + Q_ASSERT(type); + m_ui.results->setItem(i, 1, item); m_ui.results->setItem(i, 2, type); m_ui.opDelta->setEnabled(true); diff --git a/src/platform/qt/SettingsView.cpp b/src/platform/qt/SettingsView.cpp index 6037e2151..408f12e51 100644 --- a/src/platform/qt/SettingsView.cpp +++ b/src/platform/qt/SettingsView.cpp @@ -51,7 +51,7 @@ SettingsView::SettingsView(ConfigController* controller, InputController* inputC #ifdef M_CORE_GB m_pageIndex[Page::GB] = 9; - for (auto model : GameBoy::modelList()) { + for (auto& model : GameBoy::modelList()) { m_ui.gbModel->addItem(GameBoy::modelName(model), model); m_ui.sgbModel->addItem(GameBoy::modelName(model), model); m_ui.cgbModel->addItem(GameBoy::modelName(model), model); @@ -350,7 +350,7 @@ SettingsView::SettingsView(ConfigController* controller, InputController* inputC QLocale englishLocale("en"); m_ui.languages->addItem(englishLocale.nativeLanguageName(), englishLocale); QDir ts(":/translations/"); - for (auto name : ts.entryList()) { + for (auto& name : ts.entryList()) { if (!name.endsWith(".qm") || !name.startsWith(binaryName)) { continue; } diff --git a/src/platform/qt/ShortcutController.h b/src/platform/qt/ShortcutController.h index 7eed7e1d7..6e72649b5 100644 --- a/src/platform/qt/ShortcutController.h +++ b/src/platform/qt/ShortcutController.h @@ -57,7 +57,7 @@ private: int m_shortcut = 0; int m_button = -1; int m_axis = -1; - GamepadAxisEvent::Direction m_direction; + GamepadAxisEvent::Direction m_direction = GamepadAxisEvent::NEUTRAL; }; class ShortcutController : public QObject { diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 447fd5570..4a354aaf7 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -1179,14 +1179,14 @@ void Window::recordFrame() { } void Window::showFPS() { - if (m_frameList.isEmpty()) { - updateTitle(); - return; - } qint64 total = 0; for (qint64 t : m_frameList) { total += t; } + if (!total) { + updateTitle(); + return; + } double fps = (m_frameList.size() * 1e10) / total; m_frameList.clear(); fps = round(fps) / 10.f; diff --git a/src/platform/qt/input/InputController.cpp b/src/platform/qt/input/InputController.cpp index ac86a32da..fd4e0cef5 100644 --- a/src/platform/qt/input/InputController.cpp +++ b/src/platform/qt/input/InputController.cpp @@ -146,7 +146,7 @@ bool InputController::loadConfiguration(uint32_t type) { if (!mInputMapLoad(&m_inputMap, type, m_config->input())) { return false; } - auto driver = m_inputDrivers.value(type); + auto& driver = m_inputDrivers.value(type); if (!driver) { return false; } @@ -179,7 +179,8 @@ void InputController::saveConfiguration() { } void InputController::saveConfiguration(uint32_t type) { - auto driver = m_inputDrivers.value(type); + mInputMapSave(&m_inputMap, type, m_config->input()); + auto& driver = m_inputDrivers.value(type); if (driver) { driver->saveConfiguration(m_config); } @@ -197,7 +198,7 @@ void InputController::saveProfile(uint32_t type, const QString& profile) { } QString InputController::profileForType(uint32_t type) { - auto driver = m_inputDrivers.value(type); + auto& driver = m_inputDrivers.value(type); if (!driver) { return {}; } @@ -205,7 +206,7 @@ QString InputController::profileForType(uint32_t type) { } void InputController::setGamepadDriver(uint32_t type) { - auto driver = m_inputDrivers.value(type); + auto& driver = m_inputDrivers.value(type); if (!driver || !driver->supportsGamepads()) { return; } @@ -216,13 +217,13 @@ QStringList InputController::connectedGamepads(uint32_t type) const { if (!type) { type = m_gamepadDriver; } - auto driver = m_inputDrivers.value(type); + auto& driver = m_inputDrivers.value(type); if (!driver) { return {}; } QStringList pads; - for (auto pad : driver->connectedGamepads()) { + for (auto& pad : driver->connectedGamepads()) { pads.append(pad->visibleName()); } return pads; @@ -232,7 +233,7 @@ int InputController::gamepadIndex(uint32_t type) const { if (!type) { type = m_gamepadDriver; } - auto driver = m_inputDrivers.value(type); + auto& driver = m_inputDrivers.value(type); if (!driver) { return -1; } @@ -243,7 +244,7 @@ void InputController::setGamepad(uint32_t type, int index) { if (!type) { type = m_gamepadDriver; } - auto driver = m_inputDrivers.value(type); + auto& driver = m_inputDrivers.value(type); if (!driver) { return; } @@ -261,7 +262,7 @@ void InputController::setPreferredGamepad(uint32_t type, int index) { if (!type) { type = m_gamepadDriver; } - auto driver = m_inputDrivers.value(type); + auto& driver = m_inputDrivers.value(type); if (!driver) { return; } @@ -295,7 +296,7 @@ InputMapper InputController::mapper(InputSource* source) { } void InputController::setSensorDriver(uint32_t type) { - auto driver = m_inputDrivers.value(type); + auto& driver = m_inputDrivers.value(type); if (!driver || !driver->supportsSensors()) { return; } @@ -304,7 +305,7 @@ void InputController::setSensorDriver(uint32_t type) { mRumble* InputController::rumble() { - auto driver = m_inputDrivers.value(m_sensorDriver); + auto& driver = m_inputDrivers.value(m_sensorDriver); if (driver) { return driver->rumble(); } @@ -312,7 +313,7 @@ mRumble* InputController::rumble() { } mRotationSource* InputController::rotationSource() { - auto driver = m_inputDrivers.value(m_sensorDriver); + auto& driver = m_inputDrivers.value(m_sensorDriver); if (driver) { return driver->rotationSource(); } @@ -337,7 +338,7 @@ void InputController::update() { int InputController::pollEvents() { int activeButtons = 0; - for (auto pad : gamepads()) { + for (auto& pad : gamepads()) { InputMapper im(mapper(pad)); activeButtons |= im.mapKeys(pad->currentButtons()); activeButtons |= im.mapAxes(pad->currentAxes()); @@ -352,7 +353,7 @@ int InputController::pollEvents() { } Gamepad* InputController::gamepad(uint32_t type) { - auto driver = m_inputDrivers.value(type); + auto& driver = m_inputDrivers.value(type); if (!driver) { return nullptr; } @@ -460,7 +461,7 @@ void InputController::testGamepad(uint32_t type) { } } } - for (auto axis : oldAxes) { + for (auto& axis : oldAxes) { GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, false, type, this); clearPendingEvent(event->platformKey()); sendGamepadEvent(event); diff --git a/src/script/storage.c b/src/script/storage.c index b1b740162..e1299d53e 100644 --- a/src/script/storage.c +++ b/src/script/storage.c @@ -149,7 +149,7 @@ MAKE_SCALAR_SETTER(Bool, BOOL) void mScriptStorageGetBucketPath(const char* bucket, char* out) { mCoreConfigDirectory(out, PATH_MAX); - strncat(out, PATH_SEP "storage" PATH_SEP, PATH_MAX); + strncat(out, PATH_SEP "storage" PATH_SEP, PATH_MAX - 1); #ifdef _WIN32 // TODO: Move this to vfs somewhere WCHAR wout[MAX_PATH]; @@ -161,7 +161,7 @@ void mScriptStorageGetBucketPath(const char* bucket, char* out) { char suffix[STORAGE_LEN_MAX + 6]; snprintf(suffix, sizeof(suffix), "%s.json", bucket); - strncat(out, suffix, PATH_MAX); + strncat(out, suffix, PATH_MAX - 1); } static struct json_object* _tableToJson(struct mScriptValue* rootVal) { @@ -307,6 +307,9 @@ bool mScriptStorageBucketFlush(struct mScriptStorageBucket* bucket) { char path[PATH_MAX]; mScriptStorageGetBucketPath(bucket->name, path); struct VFile* vf = VFileOpen(path, O_WRONLY | O_CREAT | O_TRUNC); + if (!vf) { + return false; + } return _mScriptStorageBucketFlushVF(bucket, vf); } @@ -329,6 +332,9 @@ bool mScriptStorageSaveBucket(struct mScriptContext* context, const char* bucket char path[PATH_MAX]; mScriptStorageGetBucketPath(bucketName, path); struct VFile* vf = VFileOpen(path, O_WRONLY | O_CREAT | O_TRUNC); + if (!vf) { + return false; + } return mScriptStorageSaveBucketVF(context, bucketName, vf); } diff --git a/src/util/vfs/vfs-zip.c b/src/util/vfs/vfs-zip.c index 2f8234b94..65e1c1e32 100644 --- a/src/util/vfs/vfs-zip.c +++ b/src/util/vfs/vfs-zip.c @@ -309,6 +309,9 @@ ssize_t _vfzRead(struct VFile* vf, void* buffer, size_t size) { if (!vfz->buffer) { vfz->bufferSize = BLOCK_SIZE; vfz->buffer = malloc(BLOCK_SIZE); + if (vfz->readSize) { + abort(); + } } while (bytesRead < size) { @@ -714,7 +717,7 @@ struct VFile* _vdzOpenFile(struct VDir* vd, const char* path, int mode) { } } - struct VFileZip* vfz = malloc(sizeof(struct VFileZip)); + struct VFileZip* vfz = calloc(1, sizeof(struct VFileZip)); vfz->uz = vdz->uz; vfz->z = vdz->z; vfz->buffer = 0;