Core: Add support for cheat device buttons

This commit is contained in:
Vicki Pfau 2017-11-19 10:51:14 -08:00
parent 32f7f35ee9
commit f5afadb72d
7 changed files with 114 additions and 8 deletions

View File

@ -9,6 +9,7 @@ Features:
- Ability to set default Game Boy model - Ability to set default Game Boy model
- Map viewer - Map viewer
- Automatic cheat loading and saving - Automatic cheat loading and saving
- GameShark and Action Replay button support
Bugfixes: Bugfixes:
- GB Audio: Make audio unsigned with bias (fixes mgba.io/i/749) - GB Audio: Make audio unsigned with bias (fixes mgba.io/i/749)
- GB Serialize: Fix audio state loading - GB Serialize: Fix audio state loading

View File

@ -30,7 +30,8 @@ enum mCheatType {
CHEAT_IF_UGT, CHEAT_IF_UGT,
CHEAT_IF_AND, CHEAT_IF_AND,
CHEAT_IF_LAND, CHEAT_IF_LAND,
CHEAT_IF_NAND CHEAT_IF_NAND,
CHEAT_IF_BUTTON,
}; };
struct mCheat { struct mCheat {
@ -80,6 +81,7 @@ struct mCheatDevice {
struct mCheatSets cheats; struct mCheatSets cheats;
bool autosave; bool autosave;
bool buttonDown;
}; };
struct VFile; struct VFile;
@ -102,6 +104,7 @@ bool mCheatSaveFile(struct mCheatDevice*, struct VFile*);
void mCheatAutosave(struct mCheatDevice*); void mCheatAutosave(struct mCheatDevice*);
void mCheatRefresh(struct mCheatDevice*, struct mCheatSet*); void mCheatRefresh(struct mCheatDevice*, struct mCheatSet*);
void mCheatPressButton(struct mCheatDevice*, bool down);
CXX_GUARD_END CXX_GUARD_END

View File

@ -52,6 +52,7 @@ void mCheatDeviceCreate(struct mCheatDevice* device) {
device->d.init = mCheatDeviceInit; device->d.init = mCheatDeviceInit;
device->d.deinit = mCheatDeviceDeinit; device->d.deinit = mCheatDeviceDeinit;
device->autosave = false; device->autosave = false;
device->buttonDown = false;
mCheatSetsInit(&device->cheats, 4); mCheatSetsInit(&device->cheats, 4);
} }
@ -360,6 +361,12 @@ void mCheatRefresh(struct mCheatDevice* device, struct mCheatSet* cheats) {
negativeConditionRemaining = cheat->negativeRepeat; negativeConditionRemaining = cheat->negativeRepeat;
operationsRemaining = 1; operationsRemaining = 1;
break; break;
case CHEAT_IF_BUTTON:
condition = device->buttonDown;
conditionRemaining = cheat->repeat;
negativeConditionRemaining = cheat->negativeRepeat;
operationsRemaining = 1;
break;
} }
if (performAssignment) { if (performAssignment) {
@ -384,6 +391,10 @@ void mCheatRefresh(struct mCheatDevice* device, struct mCheatSet* cheats) {
} }
} }
void mCheatPressButton(struct mCheatDevice* device, bool down) {
device->buttonDown = down;
}
void mCheatDeviceInit(void* cpu, struct mCPUComponent* component) { void mCheatDeviceInit(void* cpu, struct mCPUComponent* component) {
UNUSED(cpu); UNUSED(cpu);
struct mCheatDevice* device = (struct mCheatDevice*) component; struct mCheatDevice* device = (struct mCheatDevice*) component;

View File

@ -154,9 +154,32 @@ bool GBACheatAddGameSharkRaw(struct GBACheatSet* cheats, uint32_t op1, uint32_t
cheats->romPatches[0].exists = true; cheats->romPatches[0].exists = true;
return true; return true;
case GSA_BUTTON: case GSA_BUTTON:
// TODO: Implement button switch (op1 & 0x00F00000) {
mLOG(CHEATS, STUB, "GameShark button unimplemented"); case 0x00100000:
return false; cheat = mCheatListAppend(&cheats->d.list);
cheat->type = CHEAT_IF_BUTTON;
cheat->repeat = 1;
cheat->negativeRepeat = 0;
cheat = mCheatListAppend(&cheats->d.list);
cheat->type = CHEAT_ASSIGN;
cheat->width = 1;
cheat->address = op1 & 0x0F0FFFFF;
break;
case 0x00200000:
cheat = mCheatListAppend(&cheats->d.list);
cheat->type = CHEAT_IF_BUTTON;
cheat->repeat = 1;
cheat->negativeRepeat = 0;
cheat = mCheatListAppend(&cheats->d.list);
cheat->type = CHEAT_ASSIGN;
cheat->width = 2;
cheat->address = op1 & 0x0F0FFFFF;
break;
default:
mLOG(CHEATS, STUB, "GameShark button type unimplemented");
return false;
}
break;
case GSA_IF_EQ: case GSA_IF_EQ:
if (op1 == 0xDEADFACE) { if (op1 == 0xDEADFACE) {
GBACheatReseedGameShark(cheats->gsaSeeds, op2, _gsa1T1, _gsa1T2); GBACheatReseedGameShark(cheats->gsaSeeds, op2, _gsa1T1, _gsa1T2);

View File

@ -144,13 +144,41 @@ static bool _addPAR3Special(struct GBACheatSet* cheats, uint32_t op2) {
switch (op2 & 0xFF000000) { switch (op2 & 0xFF000000) {
case PAR3_OTHER_SLOWDOWN: case PAR3_OTHER_SLOWDOWN:
// TODO: Slowdown // TODO: Slowdown
mLOG(CHEATS, STUB, "Unimplemented PARv3 slowdown");
return false; return false;
case PAR3_OTHER_BUTTON_1: case PAR3_OTHER_BUTTON_1:
cheat = mCheatListAppend(&cheats->d.list);
cheat->type = CHEAT_IF_BUTTON;
cheat->repeat = 1;
cheat->negativeRepeat = 0;
cheat = mCheatListAppend(&cheats->d.list);
cheat->type = CHEAT_ASSIGN;
cheat->width = 1;
cheat->address = _parAddr(op2);
cheats->incompleteCheat = mCheatListIndex(&cheats->d.list, cheat);
break;
case PAR3_OTHER_BUTTON_2: case PAR3_OTHER_BUTTON_2:
cheat = mCheatListAppend(&cheats->d.list);
cheat->type = CHEAT_IF_BUTTON;
cheat->repeat = 1;
cheat->negativeRepeat = 0;
cheat = mCheatListAppend(&cheats->d.list);
cheat->type = CHEAT_ASSIGN;
cheat->width = 2;
cheat->address = _parAddr(op2);
cheats->incompleteCheat = mCheatListIndex(&cheats->d.list, cheat);
break;
case PAR3_OTHER_BUTTON_4: case PAR3_OTHER_BUTTON_4:
// TODO: Button cheat = mCheatListAppend(&cheats->d.list);
mLOG(CHEATS, STUB, "GameShark button unimplemented"); cheat->type = CHEAT_IF_BUTTON;
return false; cheat->repeat = 1;
cheat->negativeRepeat = 0;
cheat = mCheatListAppend(&cheats->d.list);
cheat->type = CHEAT_ASSIGN;
cheat->width = 4;
cheat->address = _parAddr(op2);
cheats->incompleteCheat = mCheatListIndex(&cheats->d.list, cheat);
break;
// TODO: Fix overriding existing patches // TODO: Fix overriding existing patches
case PAR3_OTHER_PATCH_1: case PAR3_OTHER_PATCH_1:
cheats->romPatches[0].address = BASE_CART0 | ((op2 & 0xFFFFFF) << 1); cheats->romPatches[0].address = BASE_CART0 | ((op2 & 0xFFFFFF) << 1);

View File

@ -1016,6 +1016,34 @@ M_TEST_DEFINE(doPARv3IfXContain1ElseContain1) {
assert_int_equal(core->rawRead8(core, 0x03000006, -1), 0x62); assert_int_equal(core->rawRead8(core, 0x03000006, -1), 0x62);
assert_int_equal(core->rawRead8(core, 0x03000007, -1), 0x71); assert_int_equal(core->rawRead8(core, 0x03000007, -1), 0x71);
assert_int_equal(core->rawRead8(core, 0x03000008, -1), 0x82); assert_int_equal(core->rawRead8(core, 0x03000008, -1), 0x82);
set->deinit(set);
}
M_TEST_DEFINE(doPARv3IfButton) {
struct mCore* core = *state;
struct mCheatDevice* device = core->cheatDevice(core);
assert_non_null(device);
struct mCheatSet* set = device->createSet(device, NULL);
assert_non_null(set);
GBACheatSetGameSharkVersion((struct GBACheatSet*) set, GBA_GS_PARV3_RAW);
assert_true(set->addLine(set, "00000000 10300000", GBA_CHEAT_PRO_ACTION_REPLAY));
assert_true(set->addLine(set, "00000001 00000000", GBA_CHEAT_PRO_ACTION_REPLAY));
core->reset(core);
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0);
mCheatRefresh(device, set);
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0);
mCheatPressButton(device, true);
mCheatRefresh(device, set);
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0);
mCheatPressButton(device, false);
core->rawWrite8(core, 0x03000000, -1, 0);
mCheatRefresh(device, set);
assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0);
set->deinit(set);
} }
M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(GBACheats, M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(GBACheats,
@ -1038,4 +1066,5 @@ M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(GBACheats,
cmocka_unit_test(doPARv3IfXContain1), cmocka_unit_test(doPARv3IfXContain1),
cmocka_unit_test(doPARv3IfXContain1Else), cmocka_unit_test(doPARv3IfXContain1Else),
cmocka_unit_test(doPARv3IfXElseContain1), cmocka_unit_test(doPARv3IfXElseContain1),
cmocka_unit_test(doPARv3IfXContain1ElseContain1)) cmocka_unit_test(doPARv3IfXContain1ElseContain1),
cmocka_unit_test(doPARv3IfButton))

View File

@ -52,6 +52,7 @@
#include "VideoView.h" #include "VideoView.h"
#include <mgba/core/version.h> #include <mgba/core/version.h>
#include <mgba/core/cheats.h>
#ifdef M_CORE_GB #ifdef M_CORE_GB
#include <mgba/internal/gb/gb.h> #include <mgba/internal/gb/gb.h>
#include <mgba/internal/gb/video.h> #include <mgba/internal/gb/video.h>
@ -1602,6 +1603,16 @@ void Window::setupMenu(QMenuBar* menubar) {
exitFullScreen->setShortcut(QKeySequence("Esc")); exitFullScreen->setShortcut(QKeySequence("Esc"));
addHiddenAction(frameMenu, exitFullScreen, "exitFullScreen"); addHiddenAction(frameMenu, exitFullScreen, "exitFullScreen");
m_shortcutController->addFunctions(toolsMenu, [this]() {
if (m_controller) {
mCheatPressButton(m_controller->cheatDevice(), true);
}
}, [this]() {
if (m_controller) {
mCheatPressButton(m_controller->cheatDevice(), false);
}
}, QKeySequence(Qt::Key_Apostrophe), tr("GameShark Button (held)"), "holdGSButton");
QMenu* autofireMenu = new QMenu(tr("Autofire"), this); QMenu* autofireMenu = new QMenu(tr("Autofire"), this);
m_shortcutController->addMenu(autofireMenu); m_shortcutController->addMenu(autofireMenu);