From 8ab2681bca4052b9c53fb8d255e42d28a9095821 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 10 Aug 2024 21:23:00 -0700 Subject: [PATCH] Core: Expose more ROM information from the API --- CHANGES | 1 + include/mgba/core/core.h | 3 +- include/mgba/core/interface.h | 8 +++++ include/mgba/internal/gb/gb.h | 3 +- include/mgba/internal/gba/gba.h | 3 +- src/core/library.c | 7 +++-- src/core/scripting.c | 12 ++++---- src/gb/core.c | 11 ++----- src/gb/gb.c | 37 +++++++++-------------- src/gba/core.c | 11 ++----- src/gba/gba.c | 33 ++++++++++---------- src/platform/qt/BattleChipView.cpp | 13 ++++---- src/platform/qt/CoreController.cpp | 6 ++-- src/platform/qt/GBAOverride.cpp | 6 ++-- src/platform/qt/ROMInfo.cpp | 14 ++++----- src/platform/qt/ROMInfo.ui | 48 +++++++++++++++++++++++++----- src/platform/qt/ReportView.cpp | 15 +++++----- src/platform/test/perf-main.c | 7 ++--- 18 files changed, 129 insertions(+), 109 deletions(-) diff --git a/CHANGES b/CHANGES index 9af2528d9..c2617c316 100644 --- a/CHANGES +++ b/CHANGES @@ -49,6 +49,7 @@ Misc: - Qt: Handle multiple save game files for disparate games separately (fixes mgba.io/i/2887) - Qt: Remove maligned double-click-to-fullscreen shortcut (closes mgba.io/i/2632) - Qt: Pass logging context through to video proxy thread (fixes mgba.io/i/3095) + - Qt: Show maker code and game version in ROM info - Scripting: Add `callbacks:oneshot` for single-call callbacks - Switch: Add bilinear filtering option (closes mgba.io/i/3111) - Vita: Add imc0 and xmc0 mount point support diff --git a/include/mgba/core/core.h b/include/mgba/core/core.h index 97f256c82..18295ed13 100644 --- a/include/mgba/core/core.h +++ b/include/mgba/core/core.h @@ -119,8 +119,7 @@ struct mCore { int32_t (*frameCycles)(const struct mCore*); int32_t (*frequency)(const struct mCore*); - void (*getGameTitle)(const struct mCore*, char* title); - void (*getGameCode)(const struct mCore*, char* title); + void (*getGameInfo)(const struct mCore*, struct mGameInfo* info); void (*setPeripheral)(struct mCore*, int type, void*); void* (*getPeripheral)(struct mCore*, int type); diff --git a/include/mgba/core/interface.h b/include/mgba/core/interface.h index 3e6261e9c..2f4e299dd 100644 --- a/include/mgba/core/interface.h +++ b/include/mgba/core/interface.h @@ -22,6 +22,14 @@ enum mCoreFeature { mCORE_FEATURE_OPENGL = 1, }; +struct mGameInfo { + char title[17]; + char system[4]; + char code[5]; + char maker[3]; + uint8_t version; +}; + struct mCoreCallbacks { void* context; void (*videoFrameStarted)(void* context); diff --git a/include/mgba/internal/gb/gb.h b/include/mgba/internal/gb/gb.h index 08068ec58..64579d3c1 100644 --- a/include/mgba/internal/gb/gb.h +++ b/include/mgba/internal/gb/gb.h @@ -190,8 +190,7 @@ void GBSavedataUnmask(struct GB* gb); struct Patch; void GBApplyPatch(struct GB* gb, struct Patch* patch); -void GBGetGameTitle(const struct GB* gba, char* out); -void GBGetGameCode(const struct GB* gba, char* out); +void GBGetGameInfo(const struct GB* gba, struct mGameInfo* info); void GBTestKeypadIRQ(struct GB* gb); diff --git a/include/mgba/internal/gba/gba.h b/include/mgba/internal/gba/gba.h index cc340d760..ea9d69961 100644 --- a/include/mgba/internal/gba/gba.h +++ b/include/mgba/internal/gba/gba.h @@ -182,8 +182,7 @@ void GBAUnloadMB(struct GBA* gba); bool GBALoadNull(struct GBA* gba); -void GBAGetGameCode(const struct GBA* gba, char* out); -void GBAGetGameTitle(const struct GBA* gba, char* out); +void GBAGetGameInfo(const struct GBA* gba, struct mGameInfo* info); void GBATestKeypadIRQ(struct GBA* gba); diff --git a/src/core/library.c b/src/core/library.c index 44148f7f5..1c92a0cbc 100644 --- a/src/core/library.c +++ b/src/core/library.c @@ -6,6 +6,7 @@ #include #include +#include #include #ifdef USE_SQLITE3 @@ -291,8 +292,10 @@ bool _mLibraryAddEntry(struct mLibrary* library, const char* filename, const cha core->init(core); core->loadROM(core, vf); - core->getGameTitle(core, entry.internalTitle); - core->getGameCode(core, entry.internalCode); + struct mGameInfo info; + core->getGameInfo(core, &info); + snprintf(entry.internalCode, sizeof(entry.internalCode), "%s-%s", info.system, info.code); + strlcpy(entry.internalTitle, info.title, sizeof(entry.internalTitle)); core->checksum(core, &entry.crc32, mCHECKSUM_CRC32); entry.platform = core->platform(core); entry.title = NULL; diff --git a/src/core/scripting.c b/src/core/scripting.c index ea0af459d..064027d45 100644 --- a/src/core/scripting.c +++ b/src/core/scripting.c @@ -332,15 +332,15 @@ mSCRIPT_DEFINE_STRUCT(mScriptMemoryDomain) mSCRIPT_DEFINE_END; static struct mScriptValue* _mScriptCoreGetGameTitle(const struct mCore* core) { - char title[32] = {0}; - core->getGameTitle(core, title); - return mScriptStringCreateFromASCII(title); + struct mGameInfo info; + core->getGameInfo(core, &info); + return mScriptStringCreateFromASCII(info.title); } static struct mScriptValue* _mScriptCoreGetGameCode(const struct mCore* core) { - char code[16] = {0}; - core->getGameCode(core, code); - return mScriptStringCreateFromASCII(code); + struct mGameInfo info; + core->getGameInfo(core, &info); + return mScriptStringCreateFromASCII(info.code); } static struct mScriptValue* _mScriptCoreChecksum(const struct mCore* core, int t) { diff --git a/src/gb/core.c b/src/gb/core.c index 4e8351fc6..ed8dda1e5 100644 --- a/src/gb/core.c +++ b/src/gb/core.c @@ -781,12 +781,8 @@ static int32_t _GBCoreFrequency(const struct mCore* core) { return DMG_SM83_FREQUENCY; } -static void _GBCoreGetGameTitle(const struct mCore* core, char* title) { - GBGetGameTitle(core->board, title); -} - -static void _GBCoreGetGameCode(const struct mCore* core, char* title) { - GBGetGameCode(core->board, title); +static void _GBCoreGetGameInfo(const struct mCore* core, struct mGameInfo* info) { + GBGetGameInfo(core->board, info); } static void _GBCoreSetPeripheral(struct mCore* core, int type, void* periph) { @@ -1332,8 +1328,7 @@ struct mCore* GBCoreCreate(void) { core->frameCounter = _GBCoreFrameCounter; core->frameCycles = _GBCoreFrameCycles; core->frequency = _GBCoreFrequency; - core->getGameTitle = _GBCoreGetGameTitle; - core->getGameCode = _GBCoreGetGameCode; + core->getGameInfo = _GBCoreGetGameInfo; core->setPeripheral = _GBCoreSetPeripheral; core->getPeripheral = _GBCoreGetPeripheral; core->busRead8 = _GBCoreBusRead8; diff --git a/src/gb/gb.c b/src/gb/gb.c index c6198ed58..ea0a4d7df 100644 --- a/src/gb/gb.c +++ b/src/gb/gb.c @@ -16,6 +16,7 @@ #include #include #include +#include #include const uint32_t CGB_SM83_FREQUENCY = 0x800000; @@ -1116,38 +1117,28 @@ bool GBIsROM(struct VFile* vf) { return false; } -void GBGetGameTitle(const struct GB* gb, char* out) { +void GBGetGameInfo(const struct GB* gb, struct mGameInfo* info) { + memset(info, 0, sizeof(*info)); const struct GBCartridge* cart = NULL; if (gb->memory.rom) { cart = (const struct GBCartridge*) &gb->memory.rom[0x100]; } - if (!cart) { - return; - } - if (cart->oldLicensee != 0x33) { - memcpy(out, cart->titleLong, 16); - } else { - memcpy(out, cart->titleShort, 11); - } -} -void GBGetGameCode(const struct GB* gb, char* out) { - memset(out, 0, 8); - const struct GBCartridge* cart = NULL; - if (gb->memory.rom) { - cart = (const struct GBCartridge*) &gb->memory.rom[0x100]; - } - if (!cart) { - return; - } if (cart->cgb == 0xC0) { - memcpy(out, "CGB-????", 8); + strlcpy(info->system, "CGB", sizeof(info->system)); } else { - memcpy(out, "DMG-????", 8); + strlcpy(info->system, "DMG", sizeof(info->system)); } - if (cart->oldLicensee == 0x33) { - memcpy(&out[4], cart->maker, 4); + + if (cart->oldLicensee != 0x33) { + memcpy(info->title, cart->titleLong, 16); + snprintf(info->maker, sizeof(info->maker), "%02X", cart->oldLicensee); + } else { + memcpy(info->title, cart->titleShort, 11); + memcpy(info->code, cart->maker, 4); + memcpy(info->maker, &cart->licensee, 2); } + info->version = cart->version; } void GBFrameStarted(struct GB* gb) { diff --git a/src/gba/core.c b/src/gba/core.c index 1f39afea6..b39a5f401 100644 --- a/src/gba/core.c +++ b/src/gba/core.c @@ -870,12 +870,8 @@ static int32_t _GBACoreFrequency(const struct mCore* core) { return GBA_ARM7TDMI_FREQUENCY; } -static void _GBACoreGetGameTitle(const struct mCore* core, char* title) { - GBAGetGameTitle(core->board, title); -} - -static void _GBACoreGetGameCode(const struct mCore* core, char* title) { - GBAGetGameCode(core->board, title); +static void _GBACoreGetGameInfo(const struct mCore* core, struct mGameInfo* info) { + GBAGetGameInfo(core->board, info); } static void _GBACoreSetPeripheral(struct mCore* core, int type, void* periph) { @@ -1550,8 +1546,7 @@ struct mCore* GBACoreCreate(void) { core->frameCounter = _GBACoreFrameCounter; core->frameCycles = _GBACoreFrameCycles; core->frequency = _GBACoreFrequency; - core->getGameTitle = _GBACoreGetGameTitle; - core->getGameCode = _GBACoreGetGameCode; + core->getGameInfo = _GBACoreGetGameInfo; core->setPeripheral = _GBACoreSetPeripheral; core->getPeripheral = _GBACoreGetPeripheral; core->busRead8 = _GBACoreBusRead8; diff --git a/src/gba/gba.c b/src/gba/gba.c index 2d10f5cc9..f9c4dad5d 100644 --- a/src/gba/gba.c +++ b/src/gba/gba.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #ifdef USE_ELF @@ -849,26 +850,24 @@ bool GBAIsBIOS(struct VFile* vf) { return true; } -void GBAGetGameCode(const struct GBA* gba, char* out) { - memset(out, 0, 8); - if (!gba->memory.rom) { - return; - } - - memcpy(out, "AGB-", 4); - memcpy(&out[4], &((struct GBACartridge*) gba->memory.rom)->id, 4); -} - -void GBAGetGameTitle(const struct GBA* gba, char* out) { +void GBAGetGameInfo(const struct GBA* gba, struct mGameInfo* info) { + memset(info, 0, sizeof(*info)); + strlcpy(info->system, "AGB", sizeof(info->system)); + struct GBACartridge* cart = NULL; if (gba->memory.rom) { - memcpy(out, &((struct GBACartridge*) gba->memory.rom)->title, 12); - return; + cart = (struct GBACartridge*) gba->memory.rom; + } else if (gba->isPristine && gba->memory.wram) { + cart = (struct GBACartridge*) gba->memory.wram; } - if (gba->isPristine && gba->memory.wram) { - memcpy(out, &((struct GBACartridge*) gba->memory.wram)->title, 12); - return; + + if (cart) { + memcpy(info->title, &cart->title, 12); + memcpy(info->code, &cart->id, 4); + memcpy(info->maker, &cart->maker, 2); + info->version = cart->version; + } else { + strlcpy(info->title, "(BIOS)", 12); } - strncpy(out, "(BIOS)", 12); } void GBAHitStub(struct ARMCore* cpu, uint32_t opcode) { diff --git a/src/platform/qt/BattleChipView.cpp b/src/platform/qt/BattleChipView.cpp index d72f78c41..f7d6eb4c7 100644 --- a/src/platform/qt/BattleChipView.cpp +++ b/src/platform/qt/BattleChipView.cpp @@ -30,12 +30,11 @@ BattleChipView::BattleChipView(std::shared_ptr controller, Windo m_ui.setupUi(this); m_ui.chipList->setModel(&m_model); - char title[9]; CoreController::Interrupter interrupter(m_controller); mCore* core = m_controller->thread()->core; - title[8] = '\0'; - core->getGameCode(core, title); - QString qtitle(title); + mGameInfo info; + core->getGameInfo(core, &info); + QString qtitle(info.title); #if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) int size = QFontMetrics(QFont()).height() / ((int) ceil(devicePixelRatioF()) * 12); @@ -101,11 +100,11 @@ BattleChipView::BattleChipView(std::shared_ptr controller, Windo m_controller->attachBattleChipGate(); setFlavor(4); - if (qtitle.startsWith("AGB-B4B") || qtitle.startsWith("AGB-B4W") || qtitle.startsWith("AGB-BR4") || qtitle.startsWith("AGB-BZ3")) { + if (qtitle.startsWith("B4B") || qtitle.startsWith("B4W") || qtitle.startsWith("BR4") || qtitle.startsWith("BZ3")) { m_ui.gateBattleChip->setChecked(true); - } else if (qtitle.startsWith("AGB-BRB") || qtitle.startsWith("AGB-BRK")) { + } else if (qtitle.startsWith("BRB") || qtitle.startsWith("BRK")) { m_ui.gateProgress->setChecked(true); - } else if (qtitle.startsWith("AGB-BR5") || qtitle.startsWith("AGB-BR6")) { + } else if (qtitle.startsWith("BR5") || qtitle.startsWith("BR6")) { m_ui.gateBeastLink->setChecked(true); } diff --git a/src/platform/qt/CoreController.cpp b/src/platform/qt/CoreController.cpp index 02cd4db4c..519ce45fb 100644 --- a/src/platform/qt/CoreController.cpp +++ b/src/platform/qt/CoreController.cpp @@ -1318,9 +1318,9 @@ void CoreController::updateROMInfo() { mCore* core = m_threadContext.core; core->checksum(core, &m_crc32, mCHECKSUM_CRC32); - char gameTitle[17] = { '\0' }; - core->getGameTitle(core, gameTitle); - m_internalTitle = QLatin1String(gameTitle); + mGameInfo info; + core->getGameInfo(core, &info); + m_internalTitle = QLatin1String(info.title); #ifdef USE_SQLITE3 if (db && m_crc32 && NoIntroDBLookupGameByCRC(db, m_crc32, &game)) { diff --git a/src/platform/qt/GBAOverride.cpp b/src/platform/qt/GBAOverride.cpp index 57fb2048e..dbf627268 100644 --- a/src/platform/qt/GBAOverride.cpp +++ b/src/platform/qt/GBAOverride.cpp @@ -14,9 +14,9 @@ void GBAOverride::identify(const struct mCore* core) { if (core->platform(core) != mPLATFORM_GBA) { return; } - char gameId[8]; - core->getGameCode(core, gameId); - memcpy(override.id, &gameId[4], 4); + mGameInfo info; + core->getGameInfo(core, &info); + memcpy(override.id, info.code, 4); } void GBAOverride::save(struct Configuration* config) const { diff --git a/src/platform/qt/ROMInfo.cpp b/src/platform/qt/ROMInfo.cpp index 473918f26..9f2537439 100644 --- a/src/platform/qt/ROMInfo.cpp +++ b/src/platform/qt/ROMInfo.cpp @@ -27,16 +27,16 @@ ROMInfo::ROMInfo(std::shared_ptr controller, QWidget* parent) CoreController::Interrupter interrupter(controller); mCore* core = controller->thread()->core; - char title[17] = {}; - core->getGameTitle(core, title); - m_ui.title->setText(QLatin1String(title)); - title[8] = '\0'; - core->getGameCode(core, title); - if (title[0]) { - m_ui.id->setText(QLatin1String(title)); + mGameInfo info; + core->getGameInfo(core, &info); + m_ui.title->setText(QLatin1String(info.title)); + if (info.code[0]) { + m_ui.id->setText(QLatin1String(info.code)); } else { m_ui.id->setText(tr("(unknown)")); } + m_ui.maker->setText(QLatin1String(info.maker)); + m_ui.version->setText(QString::number(info.version)); core->checksum(core, &crc32, mCHECKSUM_CRC32); diff --git a/src/platform/qt/ROMInfo.ui b/src/platform/qt/ROMInfo.ui index 727cff032..655d4810f 100644 --- a/src/platform/qt/ROMInfo.ui +++ b/src/platform/qt/ROMInfo.ui @@ -6,8 +6,8 @@ 0 0 - 236 - 146 + 178 + 198 @@ -75,13 +75,47 @@ + + + Maker Code: + + + + + + + {MAKER} + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Revision: + + + + + + + {VERSION} + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + File size: - + {SIZE} @@ -91,14 +125,14 @@ - + CRC32: - + {CRC} @@ -108,14 +142,14 @@ - + Save file: - + {SAVEFILE} diff --git a/src/platform/qt/ReportView.cpp b/src/platform/qt/ReportView.cpp index 04944ef5c..9843cae76 100644 --- a/src/platform/qt/ReportView.cpp +++ b/src/platform/qt/ReportView.cpp @@ -497,17 +497,16 @@ void ReportView::addROMInfo(QStringList& report, CoreController* controller) { report << QString("Currently paused: %1").arg(yesNo[controller->isPaused()]); mCore* core = controller->thread()->core; - char title[17] = {}; - core->getGameTitle(core, title); - report << QString("Internal title: %1").arg(QLatin1String(title)); - - title[8] = '\0'; - core->getGameCode(core, title); - if (title[0]) { - report << QString("Game code: %1").arg(QLatin1String(title)); + struct mGameInfo info; + core->getGameInfo(core, &info); + report << QString("Internal title: %1").arg(QLatin1String(info.title)); + if (info.code[0]) { + report << QString("Game code: %1").arg(QLatin1String(info.code)); } else { report << QString("Invalid game code"); } + report << QString("Game maker: %1").arg(QLatin1String(info.maker)); + report << QString("Game version: %1").arg(info.version); uint32_t crc32 = 0; core->checksum(core, &crc32, mCHECKSUM_CRC32); diff --git a/src/platform/test/perf-main.c b/src/platform/test/perf-main.c index 38d50b30c..26a3715eb 100644 --- a/src/platform/test/perf-main.c +++ b/src/platform/test/perf-main.c @@ -196,8 +196,6 @@ bool _mPerfRunCore(const char* fname, const struct mArguments* args, const struc } // TODO: Put back debugger - char gameCode[9] = { 0 }; - core->init(core); if (!perfOpts->noVideo) { core->setVideoBuffer(core, _outputBuffer, 256); @@ -226,7 +224,8 @@ bool _mPerfRunCore(const char* fname, const struct mArguments* args, const struc mCoreLoadStateNamed(core, _savestate, 0); } - core->getGameCode(core, gameCode); + struct mGameInfo info; + core->getGameInfo(core, &info); int frames = perfOpts->frames; if (!frames) { @@ -255,7 +254,7 @@ bool _mPerfRunCore(const char* fname, const struct mArguments* args, const struc } else { rendererName = "software"; } - snprintf(buffer, sizeof(buffer), "%s,%i,%" PRIu64 ",%s\n", gameCode, frames, duration, rendererName); + snprintf(buffer, sizeof(buffer), "%s-%s,%i,%" PRIu64 ",%s\n", info.system, info.code, frames, duration, rendererName); printf("%s", buffer); if (_socket != INVALID_SOCKET) { SocketSend(_socket, buffer, strlen(buffer));