mirror of https://github.com/mgba-emu/mgba.git
Qt: Configurable game overrides for Game Pak devices
This commit is contained in:
parent
f4fcdf35d4
commit
f33e9c060f
|
@ -38,9 +38,13 @@ static const int RTC_BYTES[8] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
void GBAGPIOInit(struct GBACartridgeGPIO* gpio, uint16_t* base) {
|
void GBAGPIOInit(struct GBACartridgeGPIO* gpio, uint16_t* base) {
|
||||||
|
gpio->gpioBase = base;
|
||||||
|
GBAGPIOClear(gpio);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GBAGPIOClear(struct GBACartridgeGPIO* gpio) {
|
||||||
gpio->gpioDevices = GPIO_NONE;
|
gpio->gpioDevices = GPIO_NONE;
|
||||||
gpio->direction = GPIO_WRITE_ONLY;
|
gpio->direction = GPIO_WRITE_ONLY;
|
||||||
gpio->gpioBase = base;
|
|
||||||
gpio->pinState = 0;
|
gpio->pinState = 0;
|
||||||
gpio->direction = 0;
|
gpio->direction = 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#define IS_GPIO_REGISTER(reg) ((reg) == GPIO_REG_DATA || (reg) == GPIO_REG_DIRECTION || (reg) == GPIO_REG_CONTROL)
|
#define IS_GPIO_REGISTER(reg) ((reg) == GPIO_REG_DATA || (reg) == GPIO_REG_DIRECTION || (reg) == GPIO_REG_CONTROL)
|
||||||
|
|
||||||
enum GPIODevice {
|
enum GPIODevice {
|
||||||
|
GPIO_NO_OVERRIDE = 0x8000,
|
||||||
GPIO_NONE = 0,
|
GPIO_NONE = 0,
|
||||||
GPIO_RTC = 1,
|
GPIO_RTC = 1,
|
||||||
GPIO_RUMBLE = 2,
|
GPIO_RUMBLE = 2,
|
||||||
|
@ -114,6 +115,7 @@ struct GBACartridgeGPIO {
|
||||||
};
|
};
|
||||||
|
|
||||||
void GBAGPIOInit(struct GBACartridgeGPIO* gpio, uint16_t* gpioBase);
|
void GBAGPIOInit(struct GBACartridgeGPIO* gpio, uint16_t* gpioBase);
|
||||||
|
void GBAGPIOClear(struct GBACartridgeGPIO* gpio);
|
||||||
void GBAGPIOWrite(struct GBACartridgeGPIO* gpio, uint32_t address, uint16_t value);
|
void GBAGPIOWrite(struct GBACartridgeGPIO* gpio, uint32_t address, uint16_t value);
|
||||||
|
|
||||||
void GBAGPIOInitRTC(struct GBACartridgeGPIO* gpio);
|
void GBAGPIOInitRTC(struct GBACartridgeGPIO* gpio);
|
||||||
|
|
|
@ -173,7 +173,12 @@ bool GBAOverrideFind(const struct Configuration* config, struct GBACartridgeOver
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBAOverrideApply(struct GBA* gba, const struct GBACartridgeOverride* override) {
|
void GBAOverrideApply(struct GBA* gba, const struct GBACartridgeOverride* override) {
|
||||||
|
if (override->savetype != SAVEDATA_AUTODETECT) {
|
||||||
GBASavedataForceType(&gba->memory.savedata, override->savetype);
|
GBASavedataForceType(&gba->memory.savedata, override->savetype);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (override->hardware != GPIO_NO_OVERRIDE) {
|
||||||
|
GBAGPIOClear(&gba->memory.gpio);
|
||||||
|
|
||||||
if (override->hardware & GPIO_RTC) {
|
if (override->hardware & GPIO_RTC) {
|
||||||
GBAGPIOInitRTC(&gba->memory.gpio);
|
GBAGPIOInitRTC(&gba->memory.gpio);
|
||||||
|
@ -194,6 +199,9 @@ void GBAOverrideApply(struct GBA* gba, const struct GBACartridgeOverride* overri
|
||||||
if (override->hardware & GPIO_TILT) {
|
if (override->hardware & GPIO_TILT) {
|
||||||
GBAGPIOInitTilt(&gba->memory.gpio);
|
GBAGPIOInitTilt(&gba->memory.gpio);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (override->idleLoop != 0xFFFFFFFF) {
|
||||||
gba->busyLoop = override->idleLoop;
|
gba->busyLoop = override->idleLoop;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -113,6 +113,11 @@ bool GBASavedataClone(struct GBASavedata* savedata, struct VFile* out) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBASavedataForceType(struct GBASavedata* savedata, enum SavedataType type) {
|
void GBASavedataForceType(struct GBASavedata* savedata, enum SavedataType type) {
|
||||||
|
if (savedata->type != SAVEDATA_AUTODETECT) {
|
||||||
|
struct VFile* vf = savedata->vf;
|
||||||
|
GBASavedataDeinit(savedata);
|
||||||
|
GBASavedataInit(savedata, vf);
|
||||||
|
}
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case SAVEDATA_FLASH512:
|
case SAVEDATA_FLASH512:
|
||||||
case SAVEDATA_FLASH1M:
|
case SAVEDATA_FLASH1M:
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
#include "arm.h"
|
#include "arm.h"
|
||||||
#include "gba.h"
|
#include "gba.h"
|
||||||
#include "gba-config.h"
|
#include "gba-config.h"
|
||||||
#include "gba-overrides.h"
|
|
||||||
#include "gba-serialize.h"
|
#include "gba-serialize.h"
|
||||||
|
|
||||||
#include "debugger/debugger.h"
|
#include "debugger/debugger.h"
|
||||||
|
@ -146,6 +145,9 @@ static THREAD_ENTRY _GBAThreadRun(void* context) {
|
||||||
if (GBAOverrideFind(threadContext->overrides, &override)) {
|
if (GBAOverrideFind(threadContext->overrides, &override)) {
|
||||||
GBAOverrideApply(&gba, &override);
|
GBAOverrideApply(&gba, &override);
|
||||||
}
|
}
|
||||||
|
if (threadContext->hasOverride) {
|
||||||
|
GBAOverrideApply(&gba, &threadContext->override);
|
||||||
|
}
|
||||||
|
|
||||||
if (threadContext->bios && GBAIsBIOS(threadContext->bios)) {
|
if (threadContext->bios && GBAIsBIOS(threadContext->bios)) {
|
||||||
GBALoadBIOS(&gba, threadContext->bios);
|
GBALoadBIOS(&gba, threadContext->bios);
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
|
|
||||||
#include "gba.h"
|
#include "gba.h"
|
||||||
#include "gba-input.h"
|
#include "gba-input.h"
|
||||||
|
#include "gba-overrides.h"
|
||||||
|
|
||||||
#include "util/threading.h"
|
#include "util/threading.h"
|
||||||
|
|
||||||
|
@ -72,6 +73,9 @@ struct GBAThread {
|
||||||
struct GBAAVStream* stream;
|
struct GBAAVStream* stream;
|
||||||
struct Configuration* overrides;
|
struct Configuration* overrides;
|
||||||
|
|
||||||
|
bool hasOverride;
|
||||||
|
struct GBACartridgeOverride override;
|
||||||
|
|
||||||
// Run-time options
|
// Run-time options
|
||||||
int frameskip;
|
int frameskip;
|
||||||
float fpsTarget;
|
float fpsTarget;
|
||||||
|
|
|
@ -140,6 +140,11 @@ GameController::~GameController() {
|
||||||
delete[] m_drawContext;
|
delete[] m_drawContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GameController::setOverride(const GBACartridgeOverride& override) {
|
||||||
|
m_threadContext.override = override;
|
||||||
|
m_threadContext.hasOverride = true;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef USE_GDB_STUB
|
#ifdef USE_GDB_STUB
|
||||||
ARMDebugger* GameController::debugger() {
|
ARMDebugger* GameController::debugger() {
|
||||||
return m_threadContext.debugger;
|
return m_threadContext.debugger;
|
||||||
|
|
|
@ -56,6 +56,9 @@ public:
|
||||||
void setInputController(InputController* controller) { m_inputController = controller; }
|
void setInputController(InputController* controller) { m_inputController = controller; }
|
||||||
void setOverrides(Configuration* overrides) { m_threadContext.overrides = overrides; }
|
void setOverrides(Configuration* overrides) { m_threadContext.overrides = overrides; }
|
||||||
|
|
||||||
|
void setOverride(const GBACartridgeOverride& override);
|
||||||
|
void clearOverride() { m_threadContext.hasOverride = false; }
|
||||||
|
|
||||||
#ifdef USE_GDB_STUB
|
#ifdef USE_GDB_STUB
|
||||||
ARMDebugger* debugger();
|
ARMDebugger* debugger();
|
||||||
void setDebugger(ARMDebugger*);
|
void setDebugger(ARMDebugger*);
|
||||||
|
|
|
@ -38,46 +38,99 @@ GamePakView::GamePakView(GameController* controller, QWidget* parent)
|
||||||
m_ui.time->setDateTime(QDateTime::currentDateTime());
|
m_ui.time->setDateTime(QDateTime::currentDateTime());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
connect(m_ui.hwAutodetect, &QAbstractButton::toggled, [this] (bool enabled) {
|
||||||
|
m_ui.hwRTC->setEnabled(!enabled);
|
||||||
|
m_ui.hwGyro->setEnabled(!enabled);
|
||||||
|
m_ui.hwLight->setEnabled(!enabled);
|
||||||
|
m_ui.hwTilt->setEnabled(!enabled);
|
||||||
|
m_ui.hwRumble->setEnabled(!enabled);
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(m_ui.savetype, SIGNAL(currentIndexChanged(int)), this, SLOT(updateOverrides()));
|
||||||
|
connect(m_ui.hwAutodetect, SIGNAL(clicked()), this, SLOT(updateOverrides()));
|
||||||
|
connect(m_ui.hwRTC, SIGNAL(clicked()), this, SLOT(updateOverrides()));
|
||||||
|
connect(m_ui.hwGyro, SIGNAL(clicked()), this, SLOT(updateOverrides()));
|
||||||
|
connect(m_ui.hwLight, SIGNAL(clicked()), this, SLOT(updateOverrides()));
|
||||||
|
connect(m_ui.hwTilt, SIGNAL(clicked()), this, SLOT(updateOverrides()));
|
||||||
|
connect(m_ui.hwRumble, SIGNAL(clicked()), this, SLOT(updateOverrides()));
|
||||||
|
|
||||||
if (controller->isLoaded()) {
|
if (controller->isLoaded()) {
|
||||||
gameStarted(controller->thread());
|
gameStarted(controller->thread());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GamePakView::updateOverrides() {
|
||||||
|
GBACartridgeOverride override = {
|
||||||
|
"",
|
||||||
|
static_cast<SavedataType>(m_ui.savetype->currentIndex() - 1),
|
||||||
|
GPIO_NO_OVERRIDE,
|
||||||
|
0xFFFFFFFF
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!m_ui.hwAutodetect->isChecked()) {
|
||||||
|
override.hardware = GPIO_NONE;
|
||||||
|
if (m_ui.hwRTC->isChecked()) {
|
||||||
|
override.hardware |= GPIO_RTC;
|
||||||
|
}
|
||||||
|
if (m_ui.hwGyro->isChecked()) {
|
||||||
|
override.hardware |= GPIO_GYRO;
|
||||||
|
}
|
||||||
|
if (m_ui.hwLight->isChecked()) {
|
||||||
|
override.hardware |= GPIO_LIGHT_SENSOR;
|
||||||
|
}
|
||||||
|
if (m_ui.hwTilt->isChecked()) {
|
||||||
|
override.hardware |= GPIO_TILT;
|
||||||
|
}
|
||||||
|
if (m_ui.hwRumble->isChecked()) {
|
||||||
|
override.hardware |= GPIO_RUMBLE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (override.savetype != SAVEDATA_AUTODETECT || override.hardware != GPIO_NO_OVERRIDE) {
|
||||||
|
m_controller->setOverride(override);
|
||||||
|
} else {
|
||||||
|
m_controller->clearOverride();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void GamePakView::gameStarted(GBAThread* thread) {
|
void GamePakView::gameStarted(GBAThread* thread) {
|
||||||
if (!thread->gba) {
|
if (!thread->gba) {
|
||||||
gameStopped();
|
gameStopped();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
SavedataType savetype = thread->gba->memory.savedata.type;
|
m_ui.savetype->setCurrentIndex(thread->gba->memory.savedata.type + 1);
|
||||||
if (m_ui.savetype->currentIndex() > 0) {
|
|
||||||
if (savetype > SAVEDATA_FORCE_NONE) {
|
|
||||||
VFile* vf = thread->gba->memory.savedata.vf;
|
|
||||||
GBASavedataDeinit(&thread->gba->memory.savedata);
|
|
||||||
GBASavedataInit(&thread->gba->memory.savedata, vf);
|
|
||||||
}
|
|
||||||
savetype = static_cast<SavedataType>(m_ui.savetype->currentIndex() - 1);
|
|
||||||
GBASavedataForceType(&thread->gba->memory.savedata, savetype);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savetype > SAVEDATA_AUTODETECT) {
|
|
||||||
m_ui.savetype->setCurrentIndex(savetype + 1);
|
|
||||||
}
|
|
||||||
m_ui.savetype->setEnabled(false);
|
m_ui.savetype->setEnabled(false);
|
||||||
|
|
||||||
m_ui.sensorRTC->setChecked(thread->gba->memory.gpio.gpioDevices & GPIO_RTC);
|
m_ui.hwAutodetect->setEnabled(false);
|
||||||
m_ui.sensorGyro->setChecked(thread->gba->memory.gpio.gpioDevices & GPIO_GYRO);
|
m_ui.hwRTC->setEnabled(false);
|
||||||
m_ui.sensorLight->setChecked(thread->gba->memory.gpio.gpioDevices & GPIO_LIGHT_SENSOR);
|
m_ui.hwGyro->setEnabled(false);
|
||||||
m_ui.sensorTilt->setChecked(thread->gba->memory.gpio.gpioDevices & GPIO_TILT);
|
m_ui.hwLight->setEnabled(false);
|
||||||
|
m_ui.hwTilt->setEnabled(false);
|
||||||
|
m_ui.hwRumble->setEnabled(false);
|
||||||
|
|
||||||
|
m_ui.hwRTC->setChecked(thread->gba->memory.gpio.gpioDevices & GPIO_RTC);
|
||||||
|
m_ui.hwGyro->setChecked(thread->gba->memory.gpio.gpioDevices & GPIO_GYRO);
|
||||||
|
m_ui.hwLight->setChecked(thread->gba->memory.gpio.gpioDevices & GPIO_LIGHT_SENSOR);
|
||||||
|
m_ui.hwTilt->setChecked(thread->gba->memory.gpio.gpioDevices & GPIO_TILT);
|
||||||
|
m_ui.hwRumble->setChecked(thread->gba->memory.gpio.gpioDevices & GPIO_RUMBLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GamePakView::gameStopped() {
|
void GamePakView::gameStopped() {
|
||||||
m_ui.savetype->setCurrentIndex(0);
|
m_ui.savetype->setCurrentIndex(0);
|
||||||
m_ui.savetype->setEnabled(true);
|
m_ui.savetype->setEnabled(true);
|
||||||
|
|
||||||
m_ui.sensorRTC->setChecked(false);
|
m_ui.hwAutodetect->setEnabled(true);
|
||||||
m_ui.sensorGyro->setChecked(false);
|
m_ui.hwRTC->setEnabled(!m_ui.hwAutodetect->isChecked());
|
||||||
m_ui.sensorLight->setChecked(false);
|
m_ui.hwGyro->setEnabled(!m_ui.hwAutodetect->isChecked());
|
||||||
m_ui.sensorTilt->setChecked(false);
|
m_ui.hwLight->setEnabled(!m_ui.hwAutodetect->isChecked());
|
||||||
|
m_ui.hwTilt->setEnabled(!m_ui.hwAutodetect->isChecked());
|
||||||
|
m_ui.hwRumble->setEnabled(!m_ui.hwAutodetect->isChecked());
|
||||||
|
|
||||||
|
m_ui.hwRTC->setChecked(false);
|
||||||
|
m_ui.hwGyro->setChecked(false);
|
||||||
|
m_ui.hwLight->setChecked(false);
|
||||||
|
m_ui.hwTilt->setChecked(false);
|
||||||
|
m_ui.hwRumble->setChecked(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GamePakView::setLuminanceValue(int value) {
|
void GamePakView::setLuminanceValue(int value) {
|
||||||
|
|
|
@ -23,6 +23,7 @@ public:
|
||||||
GamePakView(GameController* controller, QWidget* parent = nullptr);
|
GamePakView(GameController* controller, QWidget* parent = nullptr);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
void updateOverrides();
|
||||||
void gameStarted(GBAThread*);
|
void gameStarted(GBAThread*);
|
||||||
void gameStopped();
|
void gameStopped();
|
||||||
void setLuminanceValue(int);
|
void setLuminanceValue(int);
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>259</width>
|
<width>251</width>
|
||||||
<height>373</height>
|
<height>391</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
|
@ -39,15 +39,12 @@
|
||||||
<item row="2" column="0">
|
<item row="2" column="0">
|
||||||
<widget class="QLabel" name="label_2">
|
<widget class="QLabel" name="label_2">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Sensors</string>
|
<string>Hardware</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="1">
|
<item row="2" column="1">
|
||||||
<widget class="QCheckBox" name="sensorAutodetect">
|
<widget class="QCheckBox" name="hwAutodetect">
|
||||||
<property name="enabled">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Autodetect</string>
|
<string>Autodetect</string>
|
||||||
</property>
|
</property>
|
||||||
|
@ -57,7 +54,7 @@
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="1">
|
<item row="3" column="1">
|
||||||
<widget class="QCheckBox" name="sensorRTC">
|
<widget class="QCheckBox" name="hwRTC">
|
||||||
<property name="enabled">
|
<property name="enabled">
|
||||||
<bool>false</bool>
|
<bool>false</bool>
|
||||||
</property>
|
</property>
|
||||||
|
@ -67,7 +64,7 @@
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="4" column="1">
|
<item row="4" column="1">
|
||||||
<widget class="QCheckBox" name="sensorGyro">
|
<widget class="QCheckBox" name="hwGyro">
|
||||||
<property name="enabled">
|
<property name="enabled">
|
||||||
<bool>false</bool>
|
<bool>false</bool>
|
||||||
</property>
|
</property>
|
||||||
|
@ -77,7 +74,7 @@
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="5" column="1">
|
<item row="5" column="1">
|
||||||
<widget class="QCheckBox" name="sensorTilt">
|
<widget class="QCheckBox" name="hwTilt">
|
||||||
<property name="enabled">
|
<property name="enabled">
|
||||||
<bool>false</bool>
|
<bool>false</bool>
|
||||||
</property>
|
</property>
|
||||||
|
@ -87,7 +84,7 @@
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="6" column="1">
|
<item row="6" column="1">
|
||||||
<widget class="QCheckBox" name="sensorLight">
|
<widget class="QCheckBox" name="hwLight">
|
||||||
<property name="enabled">
|
<property name="enabled">
|
||||||
<bool>false</bool>
|
<bool>false</bool>
|
||||||
</property>
|
</property>
|
||||||
|
@ -137,6 +134,16 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="7" column="1">
|
||||||
|
<widget class="QCheckBox" name="hwRumble">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Rumble</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
|
Loading…
Reference in New Issue