mirror of https://github.com/mgba-emu/mgba.git
GB: Preliminary cheat support
This commit is contained in:
parent
0fa6da495d
commit
890b063ea5
|
@ -0,0 +1,147 @@
|
||||||
|
/* Copyright (c) 2013-2016 Jeffrey Pfau
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
#include "cheats.h"
|
||||||
|
|
||||||
|
#include "util/string.h"
|
||||||
|
|
||||||
|
static void GBCheatSetDeinit(struct mCheatSet* set);
|
||||||
|
static void GBCheatAddSet(struct mCheatSet* cheats, struct mCheatDevice* device);
|
||||||
|
static void GBCheatRemoveSet(struct mCheatSet* cheats, struct mCheatDevice* device);
|
||||||
|
static void GBCheatRefresh(struct mCheatSet* cheats, struct mCheatDevice* device);
|
||||||
|
static void GBCheatSetCopyProperties(struct mCheatSet* set, struct mCheatSet* oldSet);
|
||||||
|
static bool GBCheatAddLine(struct mCheatSet*, const char* line, int type);
|
||||||
|
|
||||||
|
static struct mCheatSet* GBCheatSetCreate(struct mCheatDevice* device, const char* name) {
|
||||||
|
UNUSED(device);
|
||||||
|
struct GBCheatSet* set = malloc(sizeof(*set));
|
||||||
|
mCheatSetInit(&set->d, name);
|
||||||
|
|
||||||
|
set->d.deinit = GBCheatSetDeinit;
|
||||||
|
set->d.add = GBCheatAddSet;
|
||||||
|
set->d.remove = GBCheatRemoveSet;
|
||||||
|
|
||||||
|
set->d.addLine = GBCheatAddLine;
|
||||||
|
set->d.copyProperties = GBCheatSetCopyProperties;
|
||||||
|
|
||||||
|
set->d.refresh = GBCheatRefresh;
|
||||||
|
return &set->d;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct mCheatDevice* GBCheatDeviceCreate(void) {
|
||||||
|
struct mCheatDevice* device = malloc(sizeof(*device));
|
||||||
|
mCheatDeviceCreate(device);
|
||||||
|
device->createSet = GBCheatSetCreate;
|
||||||
|
return device;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void GBCheatSetDeinit(struct mCheatSet* set) {
|
||||||
|
struct GBCheatSet* gbset = (struct GBCheatSet*) set;
|
||||||
|
UNUSED(gbset);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void GBCheatAddSet(struct mCheatSet* cheats, struct mCheatDevice* device) {
|
||||||
|
struct GBCheatSet* gbset = (struct GBCheatSet*) cheats;
|
||||||
|
UNUSED(gbset);
|
||||||
|
UNUSED(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void GBCheatRemoveSet(struct mCheatSet* cheats, struct mCheatDevice* device) {
|
||||||
|
struct GBCheatSet* gbset = (struct GBCheatSet*) cheats;
|
||||||
|
UNUSED(gbset);
|
||||||
|
UNUSED(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool GBCheatAddGameShark(struct GBCheatSet* cheats, uint32_t op) {
|
||||||
|
struct mCheat* cheat = mCheatListAppend(&cheats->d.list);
|
||||||
|
cheat->type = CHEAT_ASSIGN;
|
||||||
|
cheat->width = 1;
|
||||||
|
cheat->address = ((op & 0xFF) << 8) | ((op >> 8) & 0xFF);
|
||||||
|
cheat->operand = (op >> 16) & 0xFF;
|
||||||
|
cheat->repeat = 1;
|
||||||
|
cheat->negativeRepeat = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool GBCheatAddGameSharkLine(struct GBCheatSet* cheats, const char* line) {
|
||||||
|
uint32_t op;
|
||||||
|
if (!hex32(line, &op)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return GBCheatAddGameShark(cheats, op);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool GBCheatAddVBALine(struct GBCheatSet* cheats, const char* line) {
|
||||||
|
uint16_t address;
|
||||||
|
uint8_t value;
|
||||||
|
const char* lineNext = hex16(line, &address);
|
||||||
|
if (!lineNext && lineNext[0] != ':') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!hex8(line, &value)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
struct mCheat* cheat = mCheatListAppend(&cheats->d.list);
|
||||||
|
cheat->type = CHEAT_ASSIGN;
|
||||||
|
cheat->width = 1;
|
||||||
|
cheat->address = address;
|
||||||
|
cheat->operand = value;
|
||||||
|
cheat->repeat = 1;
|
||||||
|
cheat->negativeRepeat = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GBCheatAddLine(struct mCheatSet* set, const char* line, int type) {
|
||||||
|
struct GBCheatSet* cheats = (struct GBCheatSet*) set;
|
||||||
|
switch (type) {
|
||||||
|
case GB_CHEAT_AUTODETECT:
|
||||||
|
break;
|
||||||
|
case GB_CHEAT_GAME_GENIE:
|
||||||
|
return false;
|
||||||
|
case GB_CHEAT_GAMESHARK:
|
||||||
|
return GBCheatAddGameSharkLine(cheats, line);
|
||||||
|
case GB_CHEAT_VBA:
|
||||||
|
return GBCheatAddVBALine(cheats, line);
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t op1;
|
||||||
|
uint8_t op2;
|
||||||
|
uint8_t op3;
|
||||||
|
const char* lineNext = hex16(line, &op1);
|
||||||
|
if (!lineNext) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (lineNext[0] == ':') {
|
||||||
|
return GBCheatAddVBALine(cheats, line);
|
||||||
|
}
|
||||||
|
lineNext = hex8(lineNext, &op2);
|
||||||
|
if (!lineNext) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (lineNext[0] == '-') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
lineNext = hex8(lineNext, &op3);
|
||||||
|
if (!lineNext) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
uint32_t realOp = op1 << 16;
|
||||||
|
realOp |= op2 << 8;
|
||||||
|
realOp |= op3;
|
||||||
|
return GBCheatAddGameShark(cheats, realOp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void GBCheatRefresh(struct mCheatSet* cheats, struct mCheatDevice* device) {
|
||||||
|
struct GBCheatSet* gbset = (struct GBCheatSet*) cheats;
|
||||||
|
UNUSED(gbset);
|
||||||
|
UNUSED(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void GBCheatSetCopyProperties(struct mCheatSet* set, struct mCheatSet* oldSet) {
|
||||||
|
UNUSED(set);
|
||||||
|
UNUSED(oldSet);
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
/* Copyright (c) 2013-2016 Jeffrey Pfau
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
#ifndef GB_CHEATS_H
|
||||||
|
#define GB_CHEATS_H
|
||||||
|
|
||||||
|
#include "util/common.h"
|
||||||
|
|
||||||
|
#include "core/cheats.h"
|
||||||
|
#include "util/vector.h"
|
||||||
|
|
||||||
|
#define MAX_ROM_PATCHES 4
|
||||||
|
|
||||||
|
enum GBACheatType {
|
||||||
|
GB_CHEAT_AUTODETECT,
|
||||||
|
GB_CHEAT_GAMESHARK,
|
||||||
|
GB_CHEAT_GAME_GENIE,
|
||||||
|
GB_CHEAT_VBA
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GBCheatPatch {
|
||||||
|
uint16_t address;
|
||||||
|
int8_t newValue;
|
||||||
|
int8_t oldValue;
|
||||||
|
bool applied;
|
||||||
|
bool exists;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GBCheatSet {
|
||||||
|
struct mCheatSet d;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct mCheatDevice* GBCheatDeviceCreate(void);
|
||||||
|
|
||||||
|
#endif
|
|
@ -6,6 +6,7 @@
|
||||||
#include "core.h"
|
#include "core.h"
|
||||||
|
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
|
#include "gb/cheats.h"
|
||||||
#include "gb/cli.h"
|
#include "gb/cli.h"
|
||||||
#include "gb/gb.h"
|
#include "gb/gb.h"
|
||||||
#include "gb/renderers/software.h"
|
#include "gb/renderers/software.h"
|
||||||
|
@ -19,6 +20,7 @@ struct GBCore {
|
||||||
uint8_t keys;
|
uint8_t keys;
|
||||||
struct mCPUComponent* components[CPU_COMPONENT_MAX];
|
struct mCPUComponent* components[CPU_COMPONENT_MAX];
|
||||||
struct mDebuggerPlatform* debuggerPlatform;
|
struct mDebuggerPlatform* debuggerPlatform;
|
||||||
|
struct mCheatDevice* cheatDevice;
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool _GBCoreInit(struct mCore* core) {
|
static bool _GBCoreInit(struct mCore* core) {
|
||||||
|
@ -34,6 +36,7 @@ static bool _GBCoreInit(struct mCore* core) {
|
||||||
core->cpu = cpu;
|
core->cpu = cpu;
|
||||||
core->board = gb;
|
core->board = gb;
|
||||||
gbcore->debuggerPlatform = NULL;
|
gbcore->debuggerPlatform = NULL;
|
||||||
|
gbcore->cheatDevice = NULL;
|
||||||
|
|
||||||
GBCreate(gb);
|
GBCreate(gb);
|
||||||
memset(gbcore->components, 0, sizeof(gbcore->components));
|
memset(gbcore->components, 0, sizeof(gbcore->components));
|
||||||
|
@ -60,6 +63,13 @@ static void _GBCoreDeinit(struct mCore* core) {
|
||||||
#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
|
#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
|
||||||
mDirectorySetDeinit(&core->dirs);
|
mDirectorySetDeinit(&core->dirs);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
struct GBCore* gbcore = (struct GBCore*) core;
|
||||||
|
free(gbcore->debuggerPlatform);
|
||||||
|
if (gbcore->cheatDevice) {
|
||||||
|
mCheatDeviceDestroy(gbcore->cheatDevice);
|
||||||
|
}
|
||||||
|
free(gbcore->cheatDevice);
|
||||||
free(core);
|
free(core);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -357,6 +367,17 @@ static void _GBCoreDetachDebugger(struct mCore* core) {
|
||||||
core->debugger = NULL;
|
core->debugger = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct mCheatDevice* _GBCoreCheatDevice(struct mCore* core) {
|
||||||
|
struct GBCore* gbcore = (struct GBCore*) core;
|
||||||
|
if (!gbcore->cheatDevice) {
|
||||||
|
gbcore->cheatDevice = GBCheatDeviceCreate();
|
||||||
|
((struct LR35902Core*) core->cpu)->components[CPU_COMPONENT_CHEAT_DEVICE] = &gbcore->cheatDevice->d;
|
||||||
|
LR35902HotplugAttach(core->cpu, CPU_COMPONENT_CHEAT_DEVICE);
|
||||||
|
gbcore->cheatDevice->p = core;
|
||||||
|
}
|
||||||
|
return gbcore->cheatDevice;
|
||||||
|
}
|
||||||
|
|
||||||
struct mCore* GBCoreCreate(void) {
|
struct mCore* GBCoreCreate(void) {
|
||||||
struct GBCore* gbcore = malloc(sizeof(*gbcore));
|
struct GBCore* gbcore = malloc(sizeof(*gbcore));
|
||||||
struct mCore* core = &gbcore->d;
|
struct mCore* core = &gbcore->d;
|
||||||
|
@ -416,5 +437,6 @@ struct mCore* GBCoreCreate(void) {
|
||||||
core->cliDebuggerSystem = _GBCoreCliDebuggerSystem;
|
core->cliDebuggerSystem = _GBCoreCliDebuggerSystem;
|
||||||
core->attachDebugger = _GBCoreAttachDebugger;
|
core->attachDebugger = _GBCoreAttachDebugger;
|
||||||
core->detachDebugger = _GBCoreDetachDebugger;
|
core->detachDebugger = _GBCoreDetachDebugger;
|
||||||
|
core->cheatDevice = _GBCoreCheatDevice;
|
||||||
return core;
|
return core;
|
||||||
}
|
}
|
||||||
|
|
12
src/gb/gb.c
12
src/gb/gb.c
|
@ -8,6 +8,7 @@
|
||||||
#include "gb/io.h"
|
#include "gb/io.h"
|
||||||
|
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
|
#include "core/cheats.h"
|
||||||
#include "util/crc32.h"
|
#include "util/crc32.h"
|
||||||
#include "util/memory.h"
|
#include "util/memory.h"
|
||||||
#include "util/math.h"
|
#include "util/math.h"
|
||||||
|
@ -383,3 +384,14 @@ void GBGetGameCode(struct GB* gb, char* out) {
|
||||||
memcpy(&out[4], cart->maker, 4);
|
memcpy(&out[4], cart->maker, 4);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GBFrameEnded(struct GB* gb) {
|
||||||
|
if (gb->cpu->components && gb->cpu->components[CPU_COMPONENT_CHEAT_DEVICE]) {
|
||||||
|
struct mCheatDevice* device = (struct mCheatDevice*) gb->cpu->components[CPU_COMPONENT_CHEAT_DEVICE];
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; i < mCheatSetsSize(&device->cheats); ++i) {
|
||||||
|
struct mCheatSet* cheats = *mCheatSetsGetPointer(&device->cheats, i);
|
||||||
|
mCheatRefresh(device, cheats);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -116,6 +116,7 @@ int32_t GBVideoProcessEvents(struct GBVideo* video, int32_t cycles) {
|
||||||
mCoreSyncPostFrame(video->p->sync);
|
mCoreSyncPostFrame(video->p->sync);
|
||||||
video->frameskipCounter = video->frameskip;
|
video->frameskipCounter = video->frameskip;
|
||||||
}
|
}
|
||||||
|
GBFrameEnded(video->p);
|
||||||
++video->frameCounter;
|
++video->frameCounter;
|
||||||
|
|
||||||
struct mCoreThread* thread = mCoreThreadGet();
|
struct mCoreThread* thread = mCoreThreadGet();
|
||||||
|
|
|
@ -1235,7 +1235,7 @@ void Window::setupMenu(QMenuBar* menubar) {
|
||||||
|
|
||||||
QAction* cheats = new QAction(tr("&Cheats..."), toolsMenu);
|
QAction* cheats = new QAction(tr("&Cheats..."), toolsMenu);
|
||||||
connect(cheats, SIGNAL(triggered()), this, SLOT(openCheatsWindow()));
|
connect(cheats, SIGNAL(triggered()), this, SLOT(openCheatsWindow()));
|
||||||
m_gbaActions.append(cheats);
|
m_gameActions.append(cheats);
|
||||||
addControlledAction(toolsMenu, cheats, "cheatsWindow");
|
addControlledAction(toolsMenu, cheats, "cheatsWindow");
|
||||||
|
|
||||||
#ifdef USE_GDB_STUB
|
#ifdef USE_GDB_STUB
|
||||||
|
|
Loading…
Reference in New Issue