mirror of https://github.com/mgba-emu/mgba.git
Core: Add support for cheat device buttons
This commit is contained in:
parent
32f7f35ee9
commit
f5afadb72d
1
CHANGES
1
CHANGES
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue