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) {
|
||||
gpio->gpioBase = base;
|
||||
GBAGPIOClear(gpio);
|
||||
}
|
||||
|
||||
void GBAGPIOClear(struct GBACartridgeGPIO* gpio) {
|
||||
gpio->gpioDevices = GPIO_NONE;
|
||||
gpio->direction = GPIO_WRITE_ONLY;
|
||||
gpio->gpioBase = base;
|
||||
gpio->pinState = 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)
|
||||
|
||||
enum GPIODevice {
|
||||
GPIO_NO_OVERRIDE = 0x8000,
|
||||
GPIO_NONE = 0,
|
||||
GPIO_RTC = 1,
|
||||
GPIO_RUMBLE = 2,
|
||||
|
@ -114,6 +115,7 @@ struct GBACartridgeGPIO {
|
|||
};
|
||||
|
||||
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 GBAGPIOInitRTC(struct GBACartridgeGPIO* gpio);
|
||||
|
|
|
@ -173,27 +173,35 @@ bool GBAOverrideFind(const struct Configuration* config, struct GBACartridgeOver
|
|||
}
|
||||
|
||||
void GBAOverrideApply(struct GBA* gba, const struct GBACartridgeOverride* override) {
|
||||
GBASavedataForceType(&gba->memory.savedata, override->savetype);
|
||||
|
||||
if (override->hardware & GPIO_RTC) {
|
||||
GBAGPIOInitRTC(&gba->memory.gpio);
|
||||
if (override->savetype != SAVEDATA_AUTODETECT) {
|
||||
GBASavedataForceType(&gba->memory.savedata, override->savetype);
|
||||
}
|
||||
|
||||
if (override->hardware & GPIO_GYRO) {
|
||||
GBAGPIOInitGyro(&gba->memory.gpio);
|
||||
if (override->hardware != GPIO_NO_OVERRIDE) {
|
||||
GBAGPIOClear(&gba->memory.gpio);
|
||||
|
||||
if (override->hardware & GPIO_RTC) {
|
||||
GBAGPIOInitRTC(&gba->memory.gpio);
|
||||
}
|
||||
|
||||
if (override->hardware & GPIO_GYRO) {
|
||||
GBAGPIOInitGyro(&gba->memory.gpio);
|
||||
}
|
||||
|
||||
if (override->hardware & GPIO_RUMBLE) {
|
||||
GBAGPIOInitRumble(&gba->memory.gpio);
|
||||
}
|
||||
|
||||
if (override->hardware & GPIO_LIGHT_SENSOR) {
|
||||
GBAGPIOInitLightSensor(&gba->memory.gpio);
|
||||
}
|
||||
|
||||
if (override->hardware & GPIO_TILT) {
|
||||
GBAGPIOInitTilt(&gba->memory.gpio);
|
||||
}
|
||||
}
|
||||
|
||||
if (override->hardware & GPIO_RUMBLE) {
|
||||
GBAGPIOInitRumble(&gba->memory.gpio);
|
||||
if (override->idleLoop != 0xFFFFFFFF) {
|
||||
gba->busyLoop = override->idleLoop;
|
||||
}
|
||||
|
||||
if (override->hardware & GPIO_LIGHT_SENSOR) {
|
||||
GBAGPIOInitLightSensor(&gba->memory.gpio);
|
||||
}
|
||||
|
||||
if (override->hardware & GPIO_TILT) {
|
||||
GBAGPIOInitTilt(&gba->memory.gpio);
|
||||
}
|
||||
|
||||
gba->busyLoop = override->idleLoop;
|
||||
}
|
||||
|
|
|
@ -113,6 +113,11 @@ bool GBASavedataClone(struct GBASavedata* savedata, struct VFile* out) {
|
|||
}
|
||||
|
||||
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) {
|
||||
case SAVEDATA_FLASH512:
|
||||
case SAVEDATA_FLASH1M:
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
#include "arm.h"
|
||||
#include "gba.h"
|
||||
#include "gba-config.h"
|
||||
#include "gba-overrides.h"
|
||||
#include "gba-serialize.h"
|
||||
|
||||
#include "debugger/debugger.h"
|
||||
|
@ -146,6 +145,9 @@ static THREAD_ENTRY _GBAThreadRun(void* context) {
|
|||
if (GBAOverrideFind(threadContext->overrides, &override)) {
|
||||
GBAOverrideApply(&gba, &override);
|
||||
}
|
||||
if (threadContext->hasOverride) {
|
||||
GBAOverrideApply(&gba, &threadContext->override);
|
||||
}
|
||||
|
||||
if (threadContext->bios && GBAIsBIOS(threadContext->bios)) {
|
||||
GBALoadBIOS(&gba, threadContext->bios);
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
#include "gba.h"
|
||||
#include "gba-input.h"
|
||||
#include "gba-overrides.h"
|
||||
|
||||
#include "util/threading.h"
|
||||
|
||||
|
@ -72,6 +73,9 @@ struct GBAThread {
|
|||
struct GBAAVStream* stream;
|
||||
struct Configuration* overrides;
|
||||
|
||||
bool hasOverride;
|
||||
struct GBACartridgeOverride override;
|
||||
|
||||
// Run-time options
|
||||
int frameskip;
|
||||
float fpsTarget;
|
||||
|
|
|
@ -140,6 +140,11 @@ GameController::~GameController() {
|
|||
delete[] m_drawContext;
|
||||
}
|
||||
|
||||
void GameController::setOverride(const GBACartridgeOverride& override) {
|
||||
m_threadContext.override = override;
|
||||
m_threadContext.hasOverride = true;
|
||||
}
|
||||
|
||||
#ifdef USE_GDB_STUB
|
||||
ARMDebugger* GameController::debugger() {
|
||||
return m_threadContext.debugger;
|
||||
|
|
|
@ -56,6 +56,9 @@ public:
|
|||
void setInputController(InputController* controller) { m_inputController = controller; }
|
||||
void setOverrides(Configuration* overrides) { m_threadContext.overrides = overrides; }
|
||||
|
||||
void setOverride(const GBACartridgeOverride& override);
|
||||
void clearOverride() { m_threadContext.hasOverride = false; }
|
||||
|
||||
#ifdef USE_GDB_STUB
|
||||
ARMDebugger* debugger();
|
||||
void setDebugger(ARMDebugger*);
|
||||
|
|
|
@ -38,46 +38,99 @@ GamePakView::GamePakView(GameController* controller, QWidget* parent)
|
|||
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()) {
|
||||
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) {
|
||||
if (!thread->gba) {
|
||||
gameStopped();
|
||||
return;
|
||||
}
|
||||
SavedataType savetype = thread->gba->memory.savedata.type;
|
||||
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->setCurrentIndex(thread->gba->memory.savedata.type + 1);
|
||||
m_ui.savetype->setEnabled(false);
|
||||
|
||||
m_ui.sensorRTC->setChecked(thread->gba->memory.gpio.gpioDevices & GPIO_RTC);
|
||||
m_ui.sensorGyro->setChecked(thread->gba->memory.gpio.gpioDevices & GPIO_GYRO);
|
||||
m_ui.sensorLight->setChecked(thread->gba->memory.gpio.gpioDevices & GPIO_LIGHT_SENSOR);
|
||||
m_ui.sensorTilt->setChecked(thread->gba->memory.gpio.gpioDevices & GPIO_TILT);
|
||||
m_ui.hwAutodetect->setEnabled(false);
|
||||
m_ui.hwRTC->setEnabled(false);
|
||||
m_ui.hwGyro->setEnabled(false);
|
||||
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() {
|
||||
m_ui.savetype->setCurrentIndex(0);
|
||||
m_ui.savetype->setEnabled(true);
|
||||
|
||||
m_ui.sensorRTC->setChecked(false);
|
||||
m_ui.sensorGyro->setChecked(false);
|
||||
m_ui.sensorLight->setChecked(false);
|
||||
m_ui.sensorTilt->setChecked(false);
|
||||
m_ui.hwAutodetect->setEnabled(true);
|
||||
m_ui.hwRTC->setEnabled(!m_ui.hwAutodetect->isChecked());
|
||||
m_ui.hwGyro->setEnabled(!m_ui.hwAutodetect->isChecked());
|
||||
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) {
|
||||
|
|
|
@ -23,6 +23,7 @@ public:
|
|||
GamePakView(GameController* controller, QWidget* parent = nullptr);
|
||||
|
||||
private slots:
|
||||
void updateOverrides();
|
||||
void gameStarted(GBAThread*);
|
||||
void gameStopped();
|
||||
void setLuminanceValue(int);
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>259</width>
|
||||
<height>373</height>
|
||||
<width>251</width>
|
||||
<height>391</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
|
@ -39,15 +39,12 @@
|
|||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Sensors</string>
|
||||
<string>Hardware</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QCheckBox" name="sensorAutodetect">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<widget class="QCheckBox" name="hwAutodetect">
|
||||
<property name="text">
|
||||
<string>Autodetect</string>
|
||||
</property>
|
||||
|
@ -57,7 +54,7 @@
|
|||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QCheckBox" name="sensorRTC">
|
||||
<widget class="QCheckBox" name="hwRTC">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
|
@ -67,7 +64,7 @@
|
|||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QCheckBox" name="sensorGyro">
|
||||
<widget class="QCheckBox" name="hwGyro">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
|
@ -77,7 +74,7 @@
|
|||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="QCheckBox" name="sensorTilt">
|
||||
<widget class="QCheckBox" name="hwTilt">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
|
@ -87,7 +84,7 @@
|
|||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<widget class="QCheckBox" name="sensorLight">
|
||||
<widget class="QCheckBox" name="hwLight">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
|
@ -137,6 +134,16 @@
|
|||
</property>
|
||||
</widget>
|
||||
</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>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
Loading…
Reference in New Issue