mirror of https://github.com/mgba-emu/mgba.git
Core: Expose more ROM information from the API
This commit is contained in:
parent
ff216ad83b
commit
8ab2681bca
1
CHANGES
1
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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <mgba/core/library.h>
|
||||
|
||||
#include <mgba/core/core.h>
|
||||
#include <mgba-util/string.h>
|
||||
#include <mgba-util/vfs.h>
|
||||
|
||||
#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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
37
src/gb/gb.c
37
src/gb/gb.c
|
@ -16,6 +16,7 @@
|
|||
#include <mgba-util/memory.h>
|
||||
#include <mgba-util/math.h>
|
||||
#include <mgba-util/patch.h>
|
||||
#include <mgba-util/string.h>
|
||||
#include <mgba-util/vfs.h>
|
||||
|
||||
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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <mgba-util/crc32.h>
|
||||
#include <mgba-util/math.h>
|
||||
#include <mgba-util/memory.h>
|
||||
#include <mgba-util/string.h>
|
||||
#include <mgba-util/vfs.h>
|
||||
|
||||
#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) {
|
||||
|
|
|
@ -30,12 +30,11 @@ BattleChipView::BattleChipView(std::shared_ptr<CoreController> 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<CoreController> 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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -27,16 +27,16 @@ ROMInfo::ROMInfo(std::shared_ptr<CoreController> 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);
|
||||
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>236</width>
|
||||
<height>146</height>
|
||||
<width>178</width>
|
||||
<height>198</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
|
@ -75,13 +75,47 @@
|
|||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="text">
|
||||
<string>Maker Code:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QLabel" name="maker">
|
||||
<property name="text">
|
||||
<string notr="true">{MAKER}</string>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_8">
|
||||
<property name="text">
|
||||
<string>Revision:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QLabel" name="version">
|
||||
<property name="text">
|
||||
<string notr="true">{VERSION}</string>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>File size:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<item row="5" column="1">
|
||||
<widget class="QLabel" name="size">
|
||||
<property name="text">
|
||||
<string notr="true">{SIZE}</string>
|
||||
|
@ -91,14 +125,14 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<item row="6" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>CRC32:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<item row="6" column="1">
|
||||
<widget class="QLabel" name="crc">
|
||||
<property name="text">
|
||||
<string notr="true">{CRC}</string>
|
||||
|
@ -108,14 +142,14 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<item row="7" column="0">
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string>Save file:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<item row="7" column="1">
|
||||
<widget class="QLabel" name="savefile">
|
||||
<property name="text">
|
||||
<string notr="true">{SAVEFILE}</string>
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
|
|
Loading…
Reference in New Issue