Merge branch 'master' (early part) into medusa

This commit is contained in:
Vicki Pfau 2024-06-21 00:30:38 -07:00
commit 295966ab50
41 changed files with 165 additions and 80 deletions

View File

@ -46,6 +46,7 @@ Features:
- New unlicensed GB mappers: NT (older types 1 and 2), Li Cheng, GGB-81 - New unlicensed GB mappers: NT (older types 1 and 2), Li Cheng, GGB-81
- Debugger: Add range watchpoints - Debugger: Add range watchpoints
Emulation fixes: Emulation fixes:
- GB Video: Implement DMG-style sprite ordering
- GBA Audio: Fix improperly deserializing GB audio registers (fixes mgba.io/i/2793) - 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 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) - 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: 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 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 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 receiving packets for client sockets
- Scripting: Fix empty receive calls returning unknown error on Windows - Scripting: Fix empty receive calls returning unknown error on Windows
Misc: Misc:

View File

@ -34,7 +34,7 @@ if(NOT MSVC)
set(WARNING_FLAGS "${WARNING_FLAGS} -Wno-format") set(WARNING_FLAGS "${WARNING_FLAGS} -Wno-format")
endif() endif()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS} -Werror=implicit-function-declaration -Werror=implicit-int") 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() else()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_CRT_SECURE_NO_WARNINGS /wd4003 /wd4244 /wd4146 /wd4267 /Zc:preprocessor-") 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-") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_CRT_SECURE_NO_WARNINGS /wd4003 /wd4244 /wd4146 /wd4267 /Zc:preprocessor-")

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

View File

@ -0,0 +1,6 @@
[testinfo]
skip=15
frames=1
[ports.cinema]
sgb.borders=0

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 702 B

After

Width:  |  Height:  |  Size: 703 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 711 B

View File

@ -27,6 +27,8 @@
extern "C" { extern "C" {
#endif #endif
struct option;
#define REPLACE_GETOPT /* use this getopt as the system getopt(3) */ #define REPLACE_GETOPT /* use this getopt as the system getopt(3) */
#ifdef REPLACE_GETOPT #ifdef REPLACE_GETOPT

View File

@ -204,6 +204,7 @@ static inline Socket SocketOpenTCP(int port, const struct Address* bindAddress)
err = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)); err = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable));
#endif #endif
if (err) { if (err) {
SocketCloseQuiet(sock);
return INVALID_SOCKET; return INVALID_SOCKET;
} }

View File

@ -489,8 +489,10 @@ bool mCheatParseEZFChtFile(struct mCheatDevice* device, struct VFile* vf) {
return false; return false;
} }
char* name = gbkToUtf8(&cheat[1], end - cheat - 1); char* name = gbkToUtf8(&cheat[1], end - cheat - 1);
if (name) {
strncpy(cheatName, name, sizeof(cheatName) - 1); strncpy(cheatName, name, sizeof(cheatName) - 1);
free(name); free(name);
}
cheatNameLength = strlen(cheatName); cheatNameLength = strlen(cheatName);
continue; continue;
} }
@ -501,7 +503,10 @@ bool mCheatParseEZFChtFile(struct mCheatDevice* device, struct VFile* vf) {
} }
if (strncmp(cheat, "ON", eq - cheat) != 0) { if (strncmp(cheat, "ON", eq - cheat) != 0) {
char* subname = gbkToUtf8(cheat, eq - cheat); char* subname = gbkToUtf8(cheat, eq - cheat);
if (subname) {
snprintf(&cheatName[cheatNameLength], sizeof(cheatName) - cheatNameLength - 1, ": %s", subname); snprintf(&cheatName[cheatNameLength], sizeof(cheatName) - cheatNameLength - 1, ": %s", subname);
free(subname);
}
} }
set = device->createSet(device, cheatName); set = device->createSet(device, cheatName);
set->enabled = false; set->enabled = false;

View File

@ -169,14 +169,14 @@ void mCoreConfigDeinit(struct mCoreConfig* config) {
#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2 #if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
bool mCoreConfigLoad(struct mCoreConfig* config) { bool mCoreConfigLoad(struct mCoreConfig* config) {
char path[PATH_MAX]; char path[PATH_MAX + 1];
mCoreConfigDirectory(path, PATH_MAX); mCoreConfigDirectory(path, PATH_MAX);
strncat(path, PATH_SEP "config.ini", PATH_MAX - strlen(path)); strncat(path, PATH_SEP "config.ini", PATH_MAX - strlen(path));
return mCoreConfigLoadPath(config, path); return mCoreConfigLoadPath(config, path);
} }
bool mCoreConfigSave(const struct mCoreConfig* config) { bool mCoreConfigSave(const struct mCoreConfig* config) {
char path[PATH_MAX]; char path[PATH_MAX + 1];
mCoreConfigDirectory(path, PATH_MAX); mCoreConfigDirectory(path, PATH_MAX);
strncat(path, PATH_SEP "config.ini", PATH_MAX - strlen(path)); strncat(path, PATH_SEP "config.ini", PATH_MAX - strlen(path));
return mCoreConfigSavePath(config, path); return mCoreConfigSavePath(config, path);
@ -304,7 +304,7 @@ void mCoreConfigPortablePath(char* out, size_t outLength) {
CFRelease(suburl); CFRelease(suburl);
} }
#endif #endif
strncat(out, PATH_SEP "portable.ini", outLength - strlen(out)); strncat(out, PATH_SEP "portable.ini", outLength - strlen(out) - 1);
#endif #endif
} }

View File

@ -50,7 +50,7 @@ const char* mLogCategoryName(int category) {
} }
const char* mLogCategoryId(int category) { const char* mLogCategoryId(int category) {
if (category < MAX_CATEGORY) { if (category >= 0 && category < MAX_CATEGORY) {
return _categoryIds[category]; return _categoryIds[category];
} }
return NULL; 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)) { if (!context->filter || mLogFilterTest(context->filter, category, level)) {
context->log(context, category, level, format, args); context->log(context, category, level, format, args);
} }
va_end(args);
} }
void mLogFilterInit(struct mLogFilter* filter) { void mLogFilterInit(struct mLogFilter* filter) {

View File

@ -846,6 +846,7 @@ void GDBStubUpdate(struct GDBStub* stub) {
} else { } else {
goto connectionLost; goto connectionLost;
} }
SocketSetTCPPush(stub->connection, 1);
} }
while (true) { while (true) {
if (stub->shouldBlock) { if (stub->shouldBlock) {

View File

@ -17,7 +17,7 @@ struct NoIntroDB {
}; };
struct NoIntroDB* NoIntroDBLoad(const char* path) { 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)) { if (sqlite3_open_v2(path, &db->db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX, NULL)) {
goto error; goto error;
@ -60,9 +60,6 @@ struct NoIntroDB* NoIntroDBLoad(const char* path) {
return db; return db;
error: error:
if (db->crc32) {
sqlite3_finalize(db->crc32);
}
NoIntroDBDestroy(db); NoIntroDBDestroy(db);
return NULL; return NULL;
@ -285,8 +282,12 @@ bool NoIntroDBLoadClrMamePro(struct NoIntroDB* db, struct VFile* vf) {
} }
void NoIntroDBDestroy(struct NoIntroDB* db) { void NoIntroDBDestroy(struct NoIntroDB* db) {
if (db->crc32) {
sqlite3_finalize(db->crc32); sqlite3_finalize(db->crc32);
}
if (db->db) {
sqlite3_close(db->db); sqlite3_close(db->db);
}
free(db); free(db);
} }

View File

@ -114,7 +114,7 @@ int main(int argc, char* argv[]) {
int ok = 1; int ok = 1;
mCoreConfigDirectory(bin, sizeof(bin)); mCoreConfigDirectory(bin, sizeof(bin));
strncat(bin, "/updater.log", sizeof(bin)); strncat(bin, "/updater.log", sizeof(bin) - 1);
logfile = fopen(bin, "w"); logfile = fopen(bin, "w");
mCoreConfigInit(&config, "updater"); mCoreConfigInit(&config, "updater");

View File

@ -382,10 +382,7 @@ static void _copyVf(struct VFile* dest, struct VFile* src) {
static void _compress(struct VFile* dest, struct VFile* src) { static void _compress(struct VFile* dest, struct VFile* src) {
uint8_t writeBuffer[0x800]; uint8_t writeBuffer[0x800];
uint8_t compressBuffer[0x400]; uint8_t compressBuffer[0x400];
z_stream zstr; z_stream zstr = {0};
zstr.zalloc = Z_NULL;
zstr.zfree = Z_NULL;
zstr.opaque = Z_NULL;
zstr.avail_in = 0; zstr.avail_in = 0;
zstr.avail_out = sizeof(compressBuffer); zstr.avail_out = sizeof(compressBuffer);
zstr.next_out = (Bytef*) 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) { static bool _decompress(struct VFile* dest, struct VFile* src, size_t compressedLength) {
uint8_t fbuffer[0x400]; uint8_t fbuffer[0x400];
uint8_t zbuffer[0x800]; uint8_t zbuffer[0x800];
z_stream zstr; z_stream zstr = {0};
zstr.zalloc = Z_NULL;
zstr.zfree = Z_NULL;
zstr.opaque = Z_NULL;
zstr.avail_in = 0; zstr.avail_in = 0;
zstr.avail_out = sizeof(zbuffer); zstr.avail_out = sizeof(zbuffer);
zstr.next_out = (Bytef*) zbuffer; zstr.next_out = (Bytef*) zbuffer;

View File

@ -619,16 +619,16 @@ static void _GBCoreReset(struct mCore* core) {
switch (gb->model) { switch (gb->model) {
case GB_MODEL_DMG: case GB_MODEL_DMG:
case GB_MODEL_MGB: // TODO 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; break;
case GB_MODEL_SGB: case GB_MODEL_SGB:
case GB_MODEL_SGB2: // TODO 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; break;
case GB_MODEL_CGB: case GB_MODEL_CGB:
case GB_MODEL_AGB: case GB_MODEL_AGB:
case GB_MODEL_SCGB: 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; break;
default: default:
break; break;

View File

@ -405,7 +405,9 @@ void GBUnloadROM(struct GB* gb) {
if (gb->romVf) { if (gb->romVf) {
#ifndef FIXED_ROM_BUFFER #ifndef FIXED_ROM_BUFFER
if (gb->isPristine && gb->memory.rom) {
gb->romVf->unmap(gb->romVf, gb->memory.rom, gb->pristineRomSize); gb->romVf->unmap(gb->romVf, gb->memory.rom, gb->pristineRomSize);
}
#endif #endif
gb->romVf->close(gb->romVf); gb->romVf->close(gb->romVf);
gb->romVf = NULL; gb->romVf = NULL;

View File

@ -14,6 +14,7 @@
#include <mgba/internal/sm83/sm83.h> #include <mgba/internal/sm83/sm83.h>
#include <mgba-util/memory.h> #include <mgba-util/memory.h>
#include <mgba-util/vfs.h>
mLOG_DEFINE_CATEGORY(GB_MEM, "GB Memory", "gb.memory"); 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) { if (memory->rtcAccess) {
return memory->rtcRegs[memory->activeRtcReg]; return memory->rtcRegs[memory->activeRtcReg];
} else if (memory->sramAccess) { } else if (memory->sramAccess) {
if (segment < 0 && memory->sram) { if (memory->sram) {
if (segment < 0) {
return memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)]; return memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)];
} else if ((size_t) segment * GB_SIZE_EXTERNAL_RAM < gb->sramSize) { } 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 memory->sram[(address & (GB_SIZE_EXTERNAL_RAM - 1)) + segment *GB_SIZE_EXTERNAL_RAM];
} else {
return 0xFF;
} }
}
return 0xFF;
} else if (memory->mbcRead) { } else if (memory->mbcRead) {
return memory->mbcRead(memory, address); return memory->mbcRead(memory, address);
} else if (memory->mbcType == GB_HuC3) { } else if (memory->mbcType == GB_HuC3) {
@ -1005,6 +1007,11 @@ void _pristineCow(struct GB* gb) {
if (gb->memory.rom == gb->memory.romBase) { if (gb->memory.rom == gb->memory.romBase) {
gb->memory.romBase = newRom; 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; gb->memory.rom = newRom;
GBMBCSwitchBank(gb, gb->memory.currentBank); GBMBCSwitchBank(gb, gb->memory.currentBank);
gb->isPristine = false; gb->isPristine = false;

View File

@ -571,20 +571,38 @@ static void _cleanOAM(struct GBVideoSoftwareRenderer* renderer, int y) {
} }
int o = 0; int o = 0;
int i; 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) { for (i = 0; i < GB_VIDEO_MAX_OBJ && o < GB_VIDEO_MAX_LINE_OBJ; ++i) {
uint8_t oy = renderer->d.oam->obj[i].y; uint8_t oy = renderer->d.oam->obj[i].y;
if (y < oy - 16 || y >= oy - 16 + spriteHeight) { if (y < oy - 16 || y >= oy - 16 + spriteHeight) {
continue; continue;
} }
// TODO: Sort ids[o] = (renderer->d.oam->obj[i].x << 7) | i;
renderer->obj[o].obj = renderer->d.oam->obj[i];
renderer->obj[o].index = i;
++o; ++o;
if (o == 10) {
break;
}
} }
renderer->objMax = o; 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) { static void GBVideoSoftwareRendererDrawRange(struct GBVideoRenderer* renderer, int startX, int endX, int y) {

View File

@ -695,7 +695,7 @@ static void _GBACoreReset(struct mCore* core) {
if (!found) { if (!found) {
char path[PATH_MAX]; char path[PATH_MAX];
mCoreConfigDirectory(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); bios = VFileOpen(path, O_RDONLY);
if (bios && GBAIsBIOS(bios)) { if (bios && GBAIsBIOS(bios)) {
found = true; found = true;

View File

@ -159,6 +159,10 @@ static const struct GBACartridgeOverride _overrides[] = {
// Shin Bokura no Taiyou: Gyakushuu no Sabata // Shin Bokura no Taiyou: Gyakushuu no Sabata
{ "U33J", SAVEDATA_EEPROM, HW_RTC | HW_LIGHT_SENSOR, IDLE_LOOP_NONE, false }, { "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 // Super Mario Advance 2
{ "AA2J", SAVEDATA_EEPROM, HW_NONE, 0x800052E, false }, { "AA2J", SAVEDATA_EEPROM, HW_NONE, 0x800052E, false },
{ "AA2E", SAVEDATA_EEPROM, HW_NONE, 0x800052E, false }, { "AA2E", SAVEDATA_EEPROM, HW_NONE, 0x800052E, false },

View File

@ -184,7 +184,7 @@ size_t GBASavedataSize(const struct GBASavedata* savedata) {
bool GBASavedataLoad(struct GBASavedata* savedata, struct VFile* in) { bool GBASavedataLoad(struct GBASavedata* savedata, struct VFile* in) {
if (savedata->data) { if (savedata->data) {
if (!in && savedata->type != SAVEDATA_FORCE_NONE) { if (!in || savedata->type == SAVEDATA_FORCE_NONE) {
return false; return false;
} }
ssize_t size = GBASavedataSize(savedata); ssize_t size = GBASavedataSize(savedata);

View File

@ -463,7 +463,7 @@ static void _GBASIOLockstepNodeProcessEvents(struct mTiming* timing, void* user,
struct GBASIOLockstepNode* node = user; struct GBASIOLockstepNode* node = user;
mLockstepLock(&node->p->d); mLockstepLock(&node->p->d);
int32_t cycles = cycles = node->nextEvent; int32_t cycles = node->nextEvent;
node->nextEvent -= cyclesLate; node->nextEvent -= cyclesLate;
node->eventDiff += cyclesLate; node->eventDiff += cyclesLate;
if (node->p->d.attached < 2) { if (node->p->d.attached < 2) {

View File

@ -1053,6 +1053,7 @@ bool mGLES2ShaderLoad(struct VideoShader* shader, struct VDir* dir) {
for (n = 0; n < inShaders; ++n) { for (n = 0; n < inShaders; ++n) {
mGLES2ShaderDeinit(&shaderBlock[n]); mGLES2ShaderDeinit(&shaderBlock[n]);
} }
free(shaderBlock);
} }
} }
} }

View File

@ -343,6 +343,7 @@ constexpr const char* ConfigController::mruName(ConfigController::MRU mru) {
case MRU::Script: case MRU::Script:
return "recentScripts"; return "recentScripts";
} }
Q_UNREACHABLE();
} }
void ConfigController::write() { void ConfigController::write() {

View File

@ -80,7 +80,7 @@ CoreController::CoreController(mCore* core, QObject* parent)
m_threadContext.resetCallback = [](mCoreThread* context) { m_threadContext.resetCallback = [](mCoreThread* context) {
CoreController* controller = static_cast<CoreController*>(context->userData); CoreController* controller = static_cast<CoreController*>(context->userData);
for (auto action : controller->m_resetActions) { for (auto& action : controller->m_resetActions) {
action(); action();
} }
@ -522,9 +522,6 @@ void CoreController::setRewinding(bool rewind) {
} }
void CoreController::rewind(int states) { void CoreController::rewind(int states) {
if (!states) {
return;
}
if (!m_threadContext.core->opts.rewindEnable) { if (!m_threadContext.core->opts.rewindEnable) {
emit statusPosted(tr("Rewinding not currently enabled")); emit statusPosted(tr("Rewinding not currently enabled"));
} }

View File

@ -16,6 +16,7 @@
#endif #endif
#include <mgba/core/core.h> #include <mgba/core/core.h>
#include <mgba-util/string.h>
#include <mgba-util/vfs.h> #include <mgba-util/vfs.h>
using namespace QGBA; using namespace QGBA;
@ -161,7 +162,7 @@ CoreController* CoreManager::loadBIOS(int platform, const QString& path) {
mCoreConfigSetOverrideIntValue(&core->config, "skipBios", 0); mCoreConfigSetOverrideIntValue(&core->config, "skipBios", 0);
QByteArray bytes(info.baseName().toUtf8()); 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(); bytes = info.dir().canonicalPath().toUtf8();
mDirectorySetAttachBase(&core->dirs, VDirOpen(bytes.constData())); mDirectorySetAttachBase(&core->dirs, VDirOpen(bytes.constData()));

View File

@ -130,9 +130,9 @@ void QGBA::Display::configure(ConfigController* config) {
config->updateOption("showOSD"); config->updateOption("showOSD");
config->updateOption("showFrameCounter"); config->updateOption("showFrameCounter");
#if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(BUILD_GLES3) #if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(BUILD_GLES3)
if (opts->shader) { if (opts->shader && supportsShaders()) {
struct VDir* shader = VDirOpen(opts->shader); struct VDir* shader = VDirOpen(opts->shader);
if (shader && supportsShaders()) { if (shader) {
setShaders(shader); setShaders(shader);
shader->close(shader); shader->close(shader);
} }

View File

@ -48,6 +48,11 @@ using QOpenGLFunctions_Baseline = QOpenGLFunctions_3_2_Core;
using namespace QGBA; using namespace QGBA;
enum ThreadStartFrom {
START = 1,
PROXY = 2,
};
QHash<QSurfaceFormat, bool> DisplayGL::s_supports; QHash<QSurfaceFormat, bool> DisplayGL::s_supports;
uint qHash(const QSurfaceFormat& format, uint seed) { uint qHash(const QSurfaceFormat& format, uint seed) {
@ -238,7 +243,16 @@ void DisplayGL::startDrawing(std::shared_ptr<CoreController> controller) {
messagePainter()->resize(size(), devicePixelRatio()); messagePainter()->resize(size(), devicePixelRatio());
#endif #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"); QMetaObject::invokeMethod(m_painter.get(), "start");
if (!m_gl) { if (!m_gl) {
if (shouldDisableUpdates()) { if (shouldDisableUpdates()) {
@ -313,6 +327,7 @@ void DisplayGL::stopDrawing() {
hide(); hide();
} }
setUpdatesEnabled(true); setUpdatesEnabled(true);
m_threadStartPending &= ~1;
} }
m_context.reset(); m_context.reset();
} }
@ -438,6 +453,7 @@ void DisplayGL::setupProxyThread() {
#if defined(_WIN32) && defined(USE_EPOXY) #if defined(_WIN32) && defined(USE_EPOXY)
epoxy_handle_external_wglMakeCurrent(); epoxy_handle_external_wglMakeCurrent();
#endif #endif
QMetaObject::invokeMethod(this, "startThread", Q_ARG(int, ThreadStartFrom::PROXY));
}); });
connect(m_painter.get(), &PainterGL::texSwapped, m_proxyContext.get(), [this]() { connect(m_painter.get(), &PainterGL::texSwapped, m_proxyContext.get(), [this]() {
if (!m_context->hardwareAccelerated()) { if (!m_context->hardwareAccelerated()) {
@ -918,8 +934,9 @@ void PainterGL::setShaders(struct VDir* dir) {
mGLES2ShaderDetach(reinterpret_cast<mGLES2Context*>(m_backend)); mGLES2ShaderDetach(reinterpret_cast<mGLES2Context*>(m_backend));
mGLES2ShaderFree(&m_shader); mGLES2ShaderFree(&m_shader);
} }
mGLES2ShaderLoad(&m_shader, dir); if (mGLES2ShaderLoad(&m_shader, dir)) {
mGLES2ShaderAttach(reinterpret_cast<mGLES2Context*>(m_backend), static_cast<mGLES2Shader*>(m_shader.passes), m_shader.nPasses); mGLES2ShaderAttach(reinterpret_cast<mGLES2Context*>(m_backend), static_cast<mGLES2Shader*>(m_shader.passes), m_shader.nPasses);
}
if (!m_started) { if (!m_started) {
m_gl->doneCurrent(); m_gl->doneCurrent();

View File

@ -113,6 +113,7 @@ protected:
virtual void resizeEvent(QResizeEvent*) override; virtual void resizeEvent(QResizeEvent*) override;
private slots: private slots:
void startThread(int);
void setupProxyThread(); void setupProxyThread();
private: private:
@ -123,6 +124,7 @@ private:
bool m_isDrawing = false; bool m_isDrawing = false;
bool m_hasStarted = false; bool m_hasStarted = false;
int m_threadStartPending = 0;
std::unique_ptr<PainterGL> m_painter; std::unique_ptr<PainterGL> m_painter;
QThread m_drawThread; QThread m_drawThread;
QThread m_proxyThread; QThread m_proxyThread;

View File

@ -79,8 +79,8 @@ public slots:
private: private:
mLogFilter m_filter; mLogFilter m_filter;
bool m_logToFile; bool m_logToFile = false;
bool m_logToStdout; bool m_logToStdout = false;
std::unique_ptr<QFile> m_logFile; std::unique_ptr<QFile> m_logFile;
std::unique_ptr<QTextStream> m_logStream; std::unique_ptr<QTextStream> m_logStream;

View File

@ -178,7 +178,7 @@ void MemorySearch::refresh() {
mCoreMemorySearchResult* result = mCoreMemorySearchResultsGetPointer(&m_results, i); mCoreMemorySearchResult* result = mCoreMemorySearchResultsGetPointer(&m_results, i);
QTableWidgetItem* item = new QTableWidgetItem(QString("%1").arg(result->address, 8, 16, QChar('0'))); QTableWidgetItem* item = new QTableWidgetItem(QString("%1").arg(result->address, 8, 16, QChar('0')));
m_ui.results->setItem(i, 0, item); m_ui.results->setItem(i, 0, item);
QTableWidgetItem* type; QTableWidgetItem* type = nullptr;
QByteArray string; QByteArray string;
if (result->type == mCORE_MEMORY_SEARCH_INT && m_ui.numHex->isChecked()) { if (result->type == mCORE_MEMORY_SEARCH_INT && m_ui.numHex->isChecked()) {
switch (result->width) { switch (result->width) {
@ -213,7 +213,12 @@ void MemorySearch::refresh() {
string.append(core->rawRead8(core, result->address + i, result->segment)); string.append(core->rawRead8(core, result->address + i, result->segment));
} }
item = new QTableWidgetItem(QLatin1String(string)); // TODO item = new QTableWidgetItem(QLatin1String(string)); // TODO
break;
case mCORE_MEMORY_SEARCH_GUESS:
item = nullptr;
break;
} }
Q_ASSERT(item);
} }
QString divisor; QString divisor;
if (result->guessDivisor > 1) { if (result->guessDivisor > 1) {
@ -231,7 +236,12 @@ void MemorySearch::refresh() {
break; break;
case mCORE_MEMORY_SEARCH_STRING: case mCORE_MEMORY_SEARCH_STRING:
type = new QTableWidgetItem("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, 1, item);
m_ui.results->setItem(i, 2, type); m_ui.results->setItem(i, 2, type);
m_ui.opDelta->setEnabled(true); m_ui.opDelta->setEnabled(true);

View File

@ -51,7 +51,7 @@ SettingsView::SettingsView(ConfigController* controller, InputController* inputC
#ifdef M_CORE_GB #ifdef M_CORE_GB
m_pageIndex[Page::GB] = 9; 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.gbModel->addItem(GameBoy::modelName(model), model);
m_ui.sgbModel->addItem(GameBoy::modelName(model), model); m_ui.sgbModel->addItem(GameBoy::modelName(model), model);
m_ui.cgbModel->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"); QLocale englishLocale("en");
m_ui.languages->addItem(englishLocale.nativeLanguageName(), englishLocale); m_ui.languages->addItem(englishLocale.nativeLanguageName(), englishLocale);
QDir ts(":/translations/"); QDir ts(":/translations/");
for (auto name : ts.entryList()) { for (auto& name : ts.entryList()) {
if (!name.endsWith(".qm") || !name.startsWith(binaryName)) { if (!name.endsWith(".qm") || !name.startsWith(binaryName)) {
continue; continue;
} }

View File

@ -57,7 +57,7 @@ private:
int m_shortcut = 0; int m_shortcut = 0;
int m_button = -1; int m_button = -1;
int m_axis = -1; int m_axis = -1;
GamepadAxisEvent::Direction m_direction; GamepadAxisEvent::Direction m_direction = GamepadAxisEvent::NEUTRAL;
}; };
class ShortcutController : public QObject { class ShortcutController : public QObject {

View File

@ -1179,14 +1179,14 @@ void Window::recordFrame() {
} }
void Window::showFPS() { void Window::showFPS() {
if (m_frameList.isEmpty()) {
updateTitle();
return;
}
qint64 total = 0; qint64 total = 0;
for (qint64 t : m_frameList) { for (qint64 t : m_frameList) {
total += t; total += t;
} }
if (!total) {
updateTitle();
return;
}
double fps = (m_frameList.size() * 1e10) / total; double fps = (m_frameList.size() * 1e10) / total;
m_frameList.clear(); m_frameList.clear();
fps = round(fps) / 10.f; fps = round(fps) / 10.f;

View File

@ -146,7 +146,7 @@ bool InputController::loadConfiguration(uint32_t type) {
if (!mInputMapLoad(&m_inputMap, type, m_config->input())) { if (!mInputMapLoad(&m_inputMap, type, m_config->input())) {
return false; return false;
} }
auto driver = m_inputDrivers.value(type); auto& driver = m_inputDrivers.value(type);
if (!driver) { if (!driver) {
return false; return false;
} }
@ -179,7 +179,8 @@ void InputController::saveConfiguration() {
} }
void InputController::saveConfiguration(uint32_t type) { 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) { if (driver) {
driver->saveConfiguration(m_config); driver->saveConfiguration(m_config);
} }
@ -197,7 +198,7 @@ void InputController::saveProfile(uint32_t type, const QString& profile) {
} }
QString InputController::profileForType(uint32_t type) { QString InputController::profileForType(uint32_t type) {
auto driver = m_inputDrivers.value(type); auto& driver = m_inputDrivers.value(type);
if (!driver) { if (!driver) {
return {}; return {};
} }
@ -205,7 +206,7 @@ QString InputController::profileForType(uint32_t type) {
} }
void InputController::setGamepadDriver(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()) { if (!driver || !driver->supportsGamepads()) {
return; return;
} }
@ -216,13 +217,13 @@ QStringList InputController::connectedGamepads(uint32_t type) const {
if (!type) { if (!type) {
type = m_gamepadDriver; type = m_gamepadDriver;
} }
auto driver = m_inputDrivers.value(type); auto& driver = m_inputDrivers.value(type);
if (!driver) { if (!driver) {
return {}; return {};
} }
QStringList pads; QStringList pads;
for (auto pad : driver->connectedGamepads()) { for (auto& pad : driver->connectedGamepads()) {
pads.append(pad->visibleName()); pads.append(pad->visibleName());
} }
return pads; return pads;
@ -232,7 +233,7 @@ int InputController::gamepadIndex(uint32_t type) const {
if (!type) { if (!type) {
type = m_gamepadDriver; type = m_gamepadDriver;
} }
auto driver = m_inputDrivers.value(type); auto& driver = m_inputDrivers.value(type);
if (!driver) { if (!driver) {
return -1; return -1;
} }
@ -243,7 +244,7 @@ void InputController::setGamepad(uint32_t type, int index) {
if (!type) { if (!type) {
type = m_gamepadDriver; type = m_gamepadDriver;
} }
auto driver = m_inputDrivers.value(type); auto& driver = m_inputDrivers.value(type);
if (!driver) { if (!driver) {
return; return;
} }
@ -261,7 +262,7 @@ void InputController::setPreferredGamepad(uint32_t type, int index) {
if (!type) { if (!type) {
type = m_gamepadDriver; type = m_gamepadDriver;
} }
auto driver = m_inputDrivers.value(type); auto& driver = m_inputDrivers.value(type);
if (!driver) { if (!driver) {
return; return;
} }
@ -295,7 +296,7 @@ InputMapper InputController::mapper(InputSource* source) {
} }
void InputController::setSensorDriver(uint32_t type) { void InputController::setSensorDriver(uint32_t type) {
auto driver = m_inputDrivers.value(type); auto& driver = m_inputDrivers.value(type);
if (!driver || !driver->supportsSensors()) { if (!driver || !driver->supportsSensors()) {
return; return;
} }
@ -304,7 +305,7 @@ void InputController::setSensorDriver(uint32_t type) {
mRumble* InputController::rumble() { mRumble* InputController::rumble() {
auto driver = m_inputDrivers.value(m_sensorDriver); auto& driver = m_inputDrivers.value(m_sensorDriver);
if (driver) { if (driver) {
return driver->rumble(); return driver->rumble();
} }
@ -312,7 +313,7 @@ mRumble* InputController::rumble() {
} }
mRotationSource* InputController::rotationSource() { mRotationSource* InputController::rotationSource() {
auto driver = m_inputDrivers.value(m_sensorDriver); auto& driver = m_inputDrivers.value(m_sensorDriver);
if (driver) { if (driver) {
return driver->rotationSource(); return driver->rotationSource();
} }
@ -337,7 +338,7 @@ void InputController::update() {
int InputController::pollEvents() { int InputController::pollEvents() {
int activeButtons = 0; int activeButtons = 0;
for (auto pad : gamepads()) { for (auto& pad : gamepads()) {
InputMapper im(mapper(pad)); InputMapper im(mapper(pad));
activeButtons |= im.mapKeys(pad->currentButtons()); activeButtons |= im.mapKeys(pad->currentButtons());
activeButtons |= im.mapAxes(pad->currentAxes()); activeButtons |= im.mapAxes(pad->currentAxes());
@ -352,7 +353,7 @@ int InputController::pollEvents() {
} }
Gamepad* InputController::gamepad(uint32_t type) { Gamepad* InputController::gamepad(uint32_t type) {
auto driver = m_inputDrivers.value(type); auto& driver = m_inputDrivers.value(type);
if (!driver) { if (!driver) {
return nullptr; 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); GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, false, type, this);
clearPendingEvent(event->platformKey()); clearPendingEvent(event->platformKey());
sendGamepadEvent(event); sendGamepadEvent(event);

View File

@ -149,7 +149,7 @@ MAKE_SCALAR_SETTER(Bool, BOOL)
void mScriptStorageGetBucketPath(const char* bucket, char* out) { void mScriptStorageGetBucketPath(const char* bucket, char* out) {
mCoreConfigDirectory(out, PATH_MAX); 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 #ifdef _WIN32
// TODO: Move this to vfs somewhere // TODO: Move this to vfs somewhere
WCHAR wout[MAX_PATH]; WCHAR wout[MAX_PATH];
@ -161,7 +161,7 @@ void mScriptStorageGetBucketPath(const char* bucket, char* out) {
char suffix[STORAGE_LEN_MAX + 6]; char suffix[STORAGE_LEN_MAX + 6];
snprintf(suffix, sizeof(suffix), "%s.json", bucket); 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) { static struct json_object* _tableToJson(struct mScriptValue* rootVal) {
@ -307,6 +307,9 @@ bool mScriptStorageBucketFlush(struct mScriptStorageBucket* bucket) {
char path[PATH_MAX]; char path[PATH_MAX];
mScriptStorageGetBucketPath(bucket->name, path); mScriptStorageGetBucketPath(bucket->name, path);
struct VFile* vf = VFileOpen(path, O_WRONLY | O_CREAT | O_TRUNC); struct VFile* vf = VFileOpen(path, O_WRONLY | O_CREAT | O_TRUNC);
if (!vf) {
return false;
}
return _mScriptStorageBucketFlushVF(bucket, vf); return _mScriptStorageBucketFlushVF(bucket, vf);
} }
@ -329,6 +332,9 @@ bool mScriptStorageSaveBucket(struct mScriptContext* context, const char* bucket
char path[PATH_MAX]; char path[PATH_MAX];
mScriptStorageGetBucketPath(bucketName, path); mScriptStorageGetBucketPath(bucketName, path);
struct VFile* vf = VFileOpen(path, O_WRONLY | O_CREAT | O_TRUNC); struct VFile* vf = VFileOpen(path, O_WRONLY | O_CREAT | O_TRUNC);
if (!vf) {
return false;
}
return mScriptStorageSaveBucketVF(context, bucketName, vf); return mScriptStorageSaveBucketVF(context, bucketName, vf);
} }

View File

@ -309,6 +309,9 @@ ssize_t _vfzRead(struct VFile* vf, void* buffer, size_t size) {
if (!vfz->buffer) { if (!vfz->buffer) {
vfz->bufferSize = BLOCK_SIZE; vfz->bufferSize = BLOCK_SIZE;
vfz->buffer = malloc(BLOCK_SIZE); vfz->buffer = malloc(BLOCK_SIZE);
if (vfz->readSize) {
abort();
}
} }
while (bytesRead < size) { 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->uz = vdz->uz;
vfz->z = vdz->z; vfz->z = vdz->z;
vfz->buffer = 0; vfz->buffer = 0;