mirror of https://github.com/mgba-emu/mgba.git
Core: Improve support for ROM patch cheats, supporting disabling overlapping patches
This commit is contained in:
parent
a59ffbc9a6
commit
5781566717
1
CHANGES
1
CHANGES
|
@ -92,6 +92,7 @@ Misc:
|
||||||
- Core: Add savedataUpdated callback
|
- Core: Add savedataUpdated callback
|
||||||
- Core: Add shutdown callback
|
- Core: Add shutdown callback
|
||||||
- Core: Rework thread state synchronization
|
- Core: Rework thread state synchronization
|
||||||
|
- Core: Improve support for ROM patch cheats, supporting disabling overlapping patches
|
||||||
- GB: Allow pausing event loop while CPU is blocked
|
- GB: Allow pausing event loop while CPU is blocked
|
||||||
- GB: Add support for sleep and shutdown callbacks
|
- GB: Add support for sleep and shutdown callbacks
|
||||||
- GB I/O: Implement preliminary support for PCM12/PCM34 (closes mgba.io/i/1468)
|
- GB I/O: Implement preliminary support for PCM12/PCM34 (closes mgba.io/i/1468)
|
||||||
|
|
|
@ -12,6 +12,7 @@ CXX_GUARD_START
|
||||||
|
|
||||||
#include <mgba/core/cpu.h>
|
#include <mgba/core/cpu.h>
|
||||||
#include <mgba/core/log.h>
|
#include <mgba/core/log.h>
|
||||||
|
#include <mgba-util/table.h>
|
||||||
#include <mgba-util/vector.h>
|
#include <mgba-util/vector.h>
|
||||||
|
|
||||||
enum mCheatType {
|
enum mCheatType {
|
||||||
|
@ -44,9 +45,20 @@ struct mCheat {
|
||||||
int32_t operandOffset;
|
int32_t operandOffset;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct mCheatPatch {
|
||||||
|
uint32_t address;
|
||||||
|
int segment;
|
||||||
|
uint32_t value;
|
||||||
|
int width;
|
||||||
|
bool applied;
|
||||||
|
uint32_t checkValue;
|
||||||
|
bool check;
|
||||||
|
};
|
||||||
|
|
||||||
mLOG_DECLARE_CATEGORY(CHEATS);
|
mLOG_DECLARE_CATEGORY(CHEATS);
|
||||||
|
|
||||||
DECLARE_VECTOR(mCheatList, struct mCheat);
|
DECLARE_VECTOR(mCheatList, struct mCheat);
|
||||||
|
DECLARE_VECTOR(mCheatPatchList, struct mCheatPatch);
|
||||||
|
|
||||||
struct mCheatDevice;
|
struct mCheatDevice;
|
||||||
struct mCheatSet {
|
struct mCheatSet {
|
||||||
|
@ -66,6 +78,7 @@ struct mCheatSet {
|
||||||
|
|
||||||
char* name;
|
char* name;
|
||||||
bool enabled;
|
bool enabled;
|
||||||
|
struct mCheatPatchList romPatches;
|
||||||
struct StringList lines;
|
struct StringList lines;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -78,6 +91,7 @@ struct mCheatDevice {
|
||||||
struct mCheatSet* (*createSet)(struct mCheatDevice*, const char* name);
|
struct mCheatSet* (*createSet)(struct mCheatDevice*, const char* name);
|
||||||
|
|
||||||
struct mCheatSets cheats;
|
struct mCheatSets cheats;
|
||||||
|
struct Table unpatchedMemory;
|
||||||
bool autosave;
|
bool autosave;
|
||||||
bool buttonDown;
|
bool buttonDown;
|
||||||
};
|
};
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
CXX_GUARD_START
|
CXX_GUARD_START
|
||||||
|
|
||||||
#include <mgba/core/cheats.h>
|
#include <mgba/core/cheats.h>
|
||||||
#include <mgba-util/vector.h>
|
|
||||||
|
|
||||||
enum GBCheatType {
|
enum GBCheatType {
|
||||||
GB_CHEAT_AUTODETECT,
|
GB_CHEAT_AUTODETECT,
|
||||||
|
@ -20,22 +19,6 @@ enum GBCheatType {
|
||||||
GB_CHEAT_VBA
|
GB_CHEAT_VBA
|
||||||
};
|
};
|
||||||
|
|
||||||
struct GBCheatPatch {
|
|
||||||
uint16_t address;
|
|
||||||
int8_t newValue;
|
|
||||||
int8_t oldValue;
|
|
||||||
int segment;
|
|
||||||
bool applied;
|
|
||||||
bool checkByte;
|
|
||||||
};
|
|
||||||
|
|
||||||
DECLARE_VECTOR(GBCheatPatchList, struct GBCheatPatch);
|
|
||||||
|
|
||||||
struct GBCheatSet {
|
|
||||||
struct mCheatSet d;
|
|
||||||
struct GBCheatPatchList romPatches;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct mCheatDevice* GBCheatDeviceCreate(void);
|
struct mCheatDevice* GBCheatDeviceCreate(void);
|
||||||
|
|
||||||
CXX_GUARD_END
|
CXX_GUARD_END
|
||||||
|
|
|
@ -134,23 +134,14 @@ struct GBACheatHook {
|
||||||
size_t reentries;
|
size_t reentries;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct GBACheatPatch {
|
|
||||||
uint32_t address;
|
|
||||||
int16_t newValue;
|
|
||||||
int16_t oldValue;
|
|
||||||
bool applied;
|
|
||||||
};
|
|
||||||
|
|
||||||
DECLARE_VECTOR(GBACheatPatchList, struct GBACheatPatch);
|
DECLARE_VECTOR(GBACheatPatchList, struct GBACheatPatch);
|
||||||
|
|
||||||
struct GBACheatSet {
|
struct GBACheatSet {
|
||||||
struct mCheatSet d;
|
struct mCheatSet d;
|
||||||
struct GBACheatHook* hook;
|
struct GBACheatHook* hook;
|
||||||
|
|
||||||
struct GBACheatPatchList romPatches;
|
|
||||||
|
|
||||||
size_t incompleteCheat;
|
size_t incompleteCheat;
|
||||||
struct GBACheatPatch* incompletePatch;
|
struct mCheatPatch* incompletePatch;
|
||||||
size_t currentBlock;
|
size_t currentBlock;
|
||||||
|
|
||||||
int gsaVersion;
|
int gsaVersion;
|
||||||
|
|
|
@ -18,6 +18,33 @@ mLOG_DEFINE_CATEGORY(CHEATS, "Cheats", "core.cheats");
|
||||||
|
|
||||||
DEFINE_VECTOR(mCheatList, struct mCheat);
|
DEFINE_VECTOR(mCheatList, struct mCheat);
|
||||||
DEFINE_VECTOR(mCheatSets, struct mCheatSet*);
|
DEFINE_VECTOR(mCheatSets, struct mCheatSet*);
|
||||||
|
DEFINE_VECTOR(mCheatPatchList, struct mCheatPatch);
|
||||||
|
|
||||||
|
struct mCheatPatchedMem {
|
||||||
|
uint32_t originalValue;
|
||||||
|
int refs;
|
||||||
|
bool dirty;
|
||||||
|
};
|
||||||
|
|
||||||
|
static uint32_t _patchMakeKey(struct mCheatPatch* patch) {
|
||||||
|
// NB: This assumes patches have only one valid size per platform
|
||||||
|
uint32_t patchKey = patch->address;
|
||||||
|
switch (patch->width) {
|
||||||
|
case 2:
|
||||||
|
patchKey >>= 1;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
patchKey >>= 2;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// TODO: More than one segment
|
||||||
|
if (patch->segment > 0) {
|
||||||
|
patchKey |= patch->segment << 16;
|
||||||
|
}
|
||||||
|
return patchKey;
|
||||||
|
}
|
||||||
|
|
||||||
static int32_t _readMem(struct mCore* core, uint32_t address, int width) {
|
static int32_t _readMem(struct mCore* core, uint32_t address, int width) {
|
||||||
switch (width) {
|
switch (width) {
|
||||||
|
@ -31,6 +58,18 @@ static int32_t _readMem(struct mCore* core, uint32_t address, int width) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int32_t _readMemSegment(struct mCore* core, uint32_t address, int segment, int width) {
|
||||||
|
switch (width) {
|
||||||
|
case 1:
|
||||||
|
return core->rawRead8(core, address, segment);
|
||||||
|
case 2:
|
||||||
|
return core->rawRead16(core, address, segment);
|
||||||
|
case 4:
|
||||||
|
return core->rawRead32(core, address, segment);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void _writeMem(struct mCore* core, uint32_t address, int width, int32_t value) {
|
static void _writeMem(struct mCore* core, uint32_t address, int width, int32_t value) {
|
||||||
switch (width) {
|
switch (width) {
|
||||||
case 1:
|
case 1:
|
||||||
|
@ -45,6 +84,83 @@ static void _writeMem(struct mCore* core, uint32_t address, int width, int32_t v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void _patchMem(struct mCore* core, uint32_t address, int segment, int width, int32_t value) {
|
||||||
|
switch (width) {
|
||||||
|
case 1:
|
||||||
|
core->rawWrite8(core, address, segment, value);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
core->rawWrite16(core, address, segment, value);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
core->rawWrite32(core, address, segment, value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _patchROM(struct mCheatDevice* device, struct mCheatSet* cheats) {
|
||||||
|
if (!device->p) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; i < mCheatPatchListSize(&cheats->romPatches); ++i) {
|
||||||
|
struct mCheatPatch* patch = mCheatPatchListGetPointer(&cheats->romPatches, i);
|
||||||
|
int segment = -1;
|
||||||
|
if (patch->check && patch->segment < 0) {
|
||||||
|
int maxSegment = 0;
|
||||||
|
for (segment = 0; segment < maxSegment; ++segment) {
|
||||||
|
uint32_t value = _readMemSegment(device->p, patch->address, segment, patch->width);
|
||||||
|
if (value == patch->checkValue) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (segment == maxSegment) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
patch->segment = segment;
|
||||||
|
|
||||||
|
uint32_t patchKey = _patchMakeKey(patch);
|
||||||
|
struct mCheatPatchedMem* patchData = TableLookup(&device->unpatchedMemory, patchKey);
|
||||||
|
if (!patchData) {
|
||||||
|
patchData = malloc(sizeof(*patchData));
|
||||||
|
patchData->originalValue = _readMemSegment(device->p, patch->address, segment, patch->width);
|
||||||
|
patchData->refs = 1;
|
||||||
|
patchData->dirty = false;
|
||||||
|
TableInsert(&device->unpatchedMemory, patchKey, patchData);
|
||||||
|
} else if (!patch->applied) {
|
||||||
|
++patchData->refs;
|
||||||
|
patchData->dirty = true;
|
||||||
|
} else if (!patchData->dirty) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
_patchMem(device->p, patch->address, segment, patch->width, patch->value);
|
||||||
|
patch->applied = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _unpatchROM(struct mCheatDevice* device, struct mCheatSet* cheats) {
|
||||||
|
if (!device->p) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; i < mCheatPatchListSize(&cheats->romPatches); ++i) {
|
||||||
|
struct mCheatPatch* patch = mCheatPatchListGetPointer(&cheats->romPatches, i);
|
||||||
|
if (!patch->applied) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
uint32_t patchKey = _patchMakeKey(patch);
|
||||||
|
struct mCheatPatchedMem* patchData = TableLookup(&device->unpatchedMemory, patchKey);
|
||||||
|
--patchData->refs;
|
||||||
|
patchData->dirty = true;
|
||||||
|
if (patchData->refs <= 0) {
|
||||||
|
_patchMem(device->p, patch->address, patch->segment, patch->width, patchData->originalValue);
|
||||||
|
TableRemove(&device->unpatchedMemory, patchKey);
|
||||||
|
}
|
||||||
|
patch->applied = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void mCheatDeviceInit(void*, struct mCPUComponent*);
|
static void mCheatDeviceInit(void*, struct mCPUComponent*);
|
||||||
static void mCheatDeviceDeinit(struct mCPUComponent*);
|
static void mCheatDeviceDeinit(struct mCPUComponent*);
|
||||||
|
|
||||||
|
@ -55,11 +171,13 @@ void mCheatDeviceCreate(struct mCheatDevice* device) {
|
||||||
device->autosave = false;
|
device->autosave = false;
|
||||||
device->buttonDown = false;
|
device->buttonDown = false;
|
||||||
mCheatSetsInit(&device->cheats, 4);
|
mCheatSetsInit(&device->cheats, 4);
|
||||||
|
TableInit(&device->unpatchedMemory, 4, free);
|
||||||
}
|
}
|
||||||
|
|
||||||
void mCheatDeviceDestroy(struct mCheatDevice* device) {
|
void mCheatDeviceDestroy(struct mCheatDevice* device) {
|
||||||
mCheatDeviceClear(device);
|
mCheatDeviceClear(device);
|
||||||
mCheatSetsDeinit(&device->cheats);
|
mCheatSetsDeinit(&device->cheats);
|
||||||
|
TableDeinit(&device->unpatchedMemory);
|
||||||
free(device);
|
free(device);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,6 +193,7 @@ void mCheatDeviceClear(struct mCheatDevice* device) {
|
||||||
void mCheatSetInit(struct mCheatSet* set, const char* name) {
|
void mCheatSetInit(struct mCheatSet* set, const char* name) {
|
||||||
mCheatListInit(&set->list, 4);
|
mCheatListInit(&set->list, 4);
|
||||||
StringListInit(&set->lines, 4);
|
StringListInit(&set->lines, 4);
|
||||||
|
mCheatPatchListInit(&set->romPatches, 4);
|
||||||
if (name) {
|
if (name) {
|
||||||
set->name = strdup(name);
|
set->name = strdup(name);
|
||||||
} else {
|
} else {
|
||||||
|
@ -93,7 +212,10 @@ void mCheatSetDeinit(struct mCheatSet* set) {
|
||||||
free(set->name);
|
free(set->name);
|
||||||
}
|
}
|
||||||
StringListDeinit(&set->lines);
|
StringListDeinit(&set->lines);
|
||||||
|
mCheatPatchListDeinit(&set->romPatches);
|
||||||
|
if (set->deinit) {
|
||||||
set->deinit(set);
|
set->deinit(set);
|
||||||
|
}
|
||||||
free(set);
|
free(set);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,7 +239,9 @@ bool mCheatAddLine(struct mCheatSet* set, const char* line, int type) {
|
||||||
|
|
||||||
void mCheatAddSet(struct mCheatDevice* device, struct mCheatSet* cheats) {
|
void mCheatAddSet(struct mCheatDevice* device, struct mCheatSet* cheats) {
|
||||||
*mCheatSetsAppend(&device->cheats) = cheats;
|
*mCheatSetsAppend(&device->cheats) = cheats;
|
||||||
|
if (cheats->add) {
|
||||||
cheats->add(cheats, device);
|
cheats->add(cheats, device);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void mCheatRemoveSet(struct mCheatDevice* device, struct mCheatSet* cheats) {
|
void mCheatRemoveSet(struct mCheatDevice* device, struct mCheatSet* cheats) {
|
||||||
|
@ -131,7 +255,9 @@ void mCheatRemoveSet(struct mCheatDevice* device, struct mCheatSet* cheats) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mCheatSetsShift(&device->cheats, i, 1);
|
mCheatSetsShift(&device->cheats, i, 1);
|
||||||
|
if (cheats->remove) {
|
||||||
cheats->remove(cheats, device);
|
cheats->remove(cheats, device);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool mCheatParseFile(struct mCheatDevice* device, struct VFile* vf) {
|
bool mCheatParseFile(struct mCheatDevice* device, struct VFile* vf) {
|
||||||
|
@ -503,8 +629,14 @@ void mCheatAutosave(struct mCheatDevice* device) {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void mCheatRefresh(struct mCheatDevice* device, struct mCheatSet* cheats) {
|
void mCheatRefresh(struct mCheatDevice* device, struct mCheatSet* cheats) {
|
||||||
|
if (cheats->enabled) {
|
||||||
|
_patchROM(device, cheats);
|
||||||
|
}
|
||||||
|
if (cheats->refresh) {
|
||||||
cheats->refresh(cheats, device);
|
cheats->refresh(cheats, device);
|
||||||
|
}
|
||||||
if (!cheats->enabled) {
|
if (!cheats->enabled) {
|
||||||
|
_unpatchROM(device, cheats);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
129
src/gb/cheats.c
129
src/gb/cheats.c
|
@ -10,58 +10,6 @@
|
||||||
#include <mgba/internal/gb/memory.h>
|
#include <mgba/internal/gb/memory.h>
|
||||||
#include <mgba-util/string.h>
|
#include <mgba-util/string.h>
|
||||||
|
|
||||||
DEFINE_VECTOR(GBCheatPatchList, struct GBCheatPatch);
|
|
||||||
|
|
||||||
static void _patchROM(struct mCheatDevice* device, struct GBCheatSet* cheats) {
|
|
||||||
if (!device->p) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
size_t i;
|
|
||||||
for (i = 0; i < GBCheatPatchListSize(&cheats->romPatches); ++i) {
|
|
||||||
struct GBCheatPatch* patch = GBCheatPatchListGetPointer(&cheats->romPatches, i);
|
|
||||||
if (patch->applied) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
int segment = 0;
|
|
||||||
if (patch->checkByte) {
|
|
||||||
struct GB* gb = device->p->board;
|
|
||||||
int maxSegment = (gb->memory.romSize + GB_SIZE_CART_BANK0 - 1) / GB_SIZE_CART_BANK0;
|
|
||||||
for (; segment < maxSegment; ++segment) {
|
|
||||||
int8_t value = GBView8(device->p->cpu, patch->address, segment);
|
|
||||||
if (value == patch->oldValue) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (segment == maxSegment) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// TODO: More than one segment
|
|
||||||
GBPatch8(device->p->cpu, patch->address, patch->newValue, &patch->oldValue, segment);
|
|
||||||
patch->applied = true;
|
|
||||||
patch->segment = segment;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _unpatchROM(struct mCheatDevice* device, struct GBCheatSet* cheats) {
|
|
||||||
if (!device->p) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
size_t i;
|
|
||||||
for (i = 0; i < GBCheatPatchListSize(&cheats->romPatches); ++i) {
|
|
||||||
struct GBCheatPatch* patch = GBCheatPatchListGetPointer(&cheats->romPatches, i);
|
|
||||||
if (!patch->applied) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
GBPatch8(device->p->cpu, patch->address, patch->oldValue, &patch->newValue, patch->segment);
|
|
||||||
patch->applied = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 void GBCheatSetCopyProperties(struct mCheatSet* set, struct mCheatSet* oldSet);
|
||||||
static void GBCheatParseDirectives(struct mCheatSet* set, const struct StringList* directives);
|
static void GBCheatParseDirectives(struct mCheatSet* set, const struct StringList* directives);
|
||||||
static void GBCheatDumpDirectives(struct mCheatSet* set, struct StringList* directives);
|
static void GBCheatDumpDirectives(struct mCheatSet* set, struct StringList* directives);
|
||||||
|
@ -69,23 +17,21 @@ static bool GBCheatAddLine(struct mCheatSet*, const char* line, int type);
|
||||||
|
|
||||||
static struct mCheatSet* GBCheatSetCreate(struct mCheatDevice* device, const char* name) {
|
static struct mCheatSet* GBCheatSetCreate(struct mCheatDevice* device, const char* name) {
|
||||||
UNUSED(device);
|
UNUSED(device);
|
||||||
struct GBCheatSet* set = malloc(sizeof(*set));
|
struct mCheatSet* set = malloc(sizeof(*set));
|
||||||
mCheatSetInit(&set->d, name);
|
mCheatSetInit(set, name);
|
||||||
|
|
||||||
GBCheatPatchListInit(&set->romPatches, 0);
|
set->deinit = NULL;
|
||||||
|
set->add = NULL;
|
||||||
|
set->remove = NULL;
|
||||||
|
|
||||||
set->d.deinit = GBCheatSetDeinit;
|
set->addLine = GBCheatAddLine;
|
||||||
set->d.add = GBCheatAddSet;
|
set->copyProperties = GBCheatSetCopyProperties;
|
||||||
set->d.remove = GBCheatRemoveSet;
|
|
||||||
|
|
||||||
set->d.addLine = GBCheatAddLine;
|
set->parseDirectives = GBCheatParseDirectives;
|
||||||
set->d.copyProperties = GBCheatSetCopyProperties;
|
set->dumpDirectives = GBCheatDumpDirectives;
|
||||||
|
|
||||||
set->d.parseDirectives = GBCheatParseDirectives;
|
set->refresh = NULL;
|
||||||
set->d.dumpDirectives = GBCheatDumpDirectives;
|
return set;
|
||||||
|
|
||||||
set->d.refresh = GBCheatRefresh;
|
|
||||||
return &set->d;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct mCheatDevice* GBCheatDeviceCreate(void) {
|
struct mCheatDevice* GBCheatDeviceCreate(void) {
|
||||||
|
@ -95,23 +41,8 @@ struct mCheatDevice* GBCheatDeviceCreate(void) {
|
||||||
return device;
|
return device;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void GBCheatSetDeinit(struct mCheatSet* set) {
|
static bool GBCheatAddCodebreaker(struct mCheatSet* cheats, uint16_t address, uint8_t data) {
|
||||||
struct GBCheatSet* gbset = (struct GBCheatSet*) set;
|
struct mCheat* cheat = mCheatListAppend(&cheats->list);
|
||||||
GBCheatPatchListDeinit(&gbset->romPatches);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void GBCheatAddSet(struct mCheatSet* cheats, struct mCheatDevice* device) {
|
|
||||||
struct GBCheatSet* gbset = (struct GBCheatSet*) cheats;
|
|
||||||
_patchROM(device, gbset);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void GBCheatRemoveSet(struct mCheatSet* cheats, struct mCheatDevice* device) {
|
|
||||||
struct GBCheatSet* gbset = (struct GBCheatSet*) cheats;
|
|
||||||
_unpatchROM(device, gbset);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool GBCheatAddCodebreaker(struct GBCheatSet* cheats, uint16_t address, uint8_t data) {
|
|
||||||
struct mCheat* cheat = mCheatListAppend(&cheats->d.list);
|
|
||||||
cheat->type = CHEAT_ASSIGN;
|
cheat->type = CHEAT_ASSIGN;
|
||||||
cheat->width = 1;
|
cheat->width = 1;
|
||||||
cheat->address = address;
|
cheat->address = address;
|
||||||
|
@ -121,11 +52,11 @@ static bool GBCheatAddCodebreaker(struct GBCheatSet* cheats, uint16_t address, u
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool GBCheatAddGameShark(struct GBCheatSet* cheats, uint32_t op) {
|
static bool GBCheatAddGameShark(struct mCheatSet* cheats, uint32_t op) {
|
||||||
return GBCheatAddCodebreaker(cheats, ((op & 0xFF) << 8) | ((op >> 8) & 0xFF), (op >> 16) & 0xFF);
|
return GBCheatAddCodebreaker(cheats, ((op & 0xFF) << 8) | ((op >> 8) & 0xFF), (op >> 16) & 0xFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool GBCheatAddGameSharkLine(struct GBCheatSet* cheats, const char* line) {
|
static bool GBCheatAddGameSharkLine(struct mCheatSet* cheats, const char* line) {
|
||||||
uint32_t op;
|
uint32_t op;
|
||||||
if (!hex32(line, &op)) {
|
if (!hex32(line, &op)) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -133,7 +64,7 @@ static bool GBCheatAddGameSharkLine(struct GBCheatSet* cheats, const char* line)
|
||||||
return GBCheatAddGameShark(cheats, op);
|
return GBCheatAddGameShark(cheats, op);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool GBCheatAddGameGenieLine(struct GBCheatSet* cheats, const char* line) {
|
static bool GBCheatAddGameGenieLine(struct mCheatSet* cheats, const char* line) {
|
||||||
uint16_t op1;
|
uint16_t op1;
|
||||||
uint16_t op2;
|
uint16_t op2;
|
||||||
uint16_t op3 = 0x1000;
|
uint16_t op3 = 0x1000;
|
||||||
|
@ -156,24 +87,26 @@ static bool GBCheatAddGameGenieLine(struct GBCheatSet* cheats, const char* line)
|
||||||
uint16_t address = (op1 & 0xF) << 8;
|
uint16_t address = (op1 & 0xF) << 8;
|
||||||
address |= (op2 >> 4) & 0xFF;
|
address |= (op2 >> 4) & 0xFF;
|
||||||
address |= ((op2 & 0xF) ^ 0xF) << 12;
|
address |= ((op2 & 0xF) ^ 0xF) << 12;
|
||||||
struct GBCheatPatch* patch = GBCheatPatchListAppend(&cheats->romPatches);
|
struct mCheatPatch* patch = mCheatPatchListAppend(&cheats->romPatches);
|
||||||
patch->address = address;
|
patch->address = address;
|
||||||
patch->newValue = op1 >> 4;
|
patch->value = op1 >> 4;
|
||||||
patch->applied = false;
|
patch->applied = false;
|
||||||
|
patch->width = 1;
|
||||||
|
patch->segment = -1;
|
||||||
if (op3 < 0x1000) {
|
if (op3 < 0x1000) {
|
||||||
uint32_t value = ((op3 & 0xF00) << 20) | (op3 & 0xF);
|
uint32_t value = ((op3 & 0xF00) << 20) | (op3 & 0xF);
|
||||||
value = ROR(value, 2);
|
value = ROR(value, 2);
|
||||||
value |= value >> 24;
|
value |= value >> 24;
|
||||||
value ^= 0xBA;
|
value ^= 0xBA;
|
||||||
patch->oldValue = value;
|
patch->checkValue = value;
|
||||||
patch->checkByte = true;
|
patch->check = true;
|
||||||
} else {
|
} else {
|
||||||
patch->checkByte = false;
|
patch->check = false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool GBCheatAddVBALine(struct GBCheatSet* cheats, const char* line) {
|
static bool GBCheatAddVBALine(struct mCheatSet* cheats, const char* line) {
|
||||||
uint16_t address;
|
uint16_t address;
|
||||||
uint8_t value;
|
uint8_t value;
|
||||||
const char* lineNext = hex16(line, &address);
|
const char* lineNext = hex16(line, &address);
|
||||||
|
@ -183,7 +116,7 @@ static bool GBCheatAddVBALine(struct GBCheatSet* cheats, const char* line) {
|
||||||
if (!hex8(line, &value)) {
|
if (!hex8(line, &value)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
struct mCheat* cheat = mCheatListAppend(&cheats->d.list);
|
struct mCheat* cheat = mCheatListAppend(&cheats->list);
|
||||||
cheat->type = CHEAT_ASSIGN;
|
cheat->type = CHEAT_ASSIGN;
|
||||||
cheat->width = 1;
|
cheat->width = 1;
|
||||||
cheat->address = address;
|
cheat->address = address;
|
||||||
|
@ -193,8 +126,7 @@ static bool GBCheatAddVBALine(struct GBCheatSet* cheats, const char* line) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GBCheatAddLine(struct mCheatSet* set, const char* line, int type) {
|
bool GBCheatAddLine(struct mCheatSet* cheats, const char* line, int type) {
|
||||||
struct GBCheatSet* cheats = (struct GBCheatSet*) set;
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case GB_CHEAT_AUTODETECT:
|
case GB_CHEAT_AUTODETECT:
|
||||||
break;
|
break;
|
||||||
|
@ -242,15 +174,6 @@ bool GBCheatAddLine(struct mCheatSet* set, const char* line, int type) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void GBCheatRefresh(struct mCheatSet* cheats, struct mCheatDevice* device) {
|
|
||||||
struct GBCheatSet* gbset = (struct GBCheatSet*) cheats;
|
|
||||||
if (cheats->enabled) {
|
|
||||||
_patchROM(device, gbset);
|
|
||||||
} else {
|
|
||||||
_unpatchROM(device, gbset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void GBCheatSetCopyProperties(struct mCheatSet* set, struct mCheatSet* oldSet) {
|
static void GBCheatSetCopyProperties(struct mCheatSet* set, struct mCheatSet* oldSet) {
|
||||||
UNUSED(set);
|
UNUSED(set);
|
||||||
UNUSED(oldSet);
|
UNUSED(oldSet);
|
||||||
|
|
|
@ -13,8 +13,6 @@
|
||||||
|
|
||||||
#define MAX_LINE_LENGTH 128
|
#define MAX_LINE_LENGTH 128
|
||||||
|
|
||||||
DEFINE_VECTOR(GBACheatPatchList, struct GBACheatPatch);
|
|
||||||
|
|
||||||
static void _addBreakpoint(struct mCheatDevice* device, struct GBACheatSet* cheats) {
|
static void _addBreakpoint(struct mCheatDevice* device, struct GBACheatSet* cheats) {
|
||||||
if (!device->p || !cheats->hook) {
|
if (!device->p || !cheats->hook) {
|
||||||
return;
|
return;
|
||||||
|
@ -37,36 +35,6 @@ static void _removeBreakpoint(struct mCheatDevice* device, struct GBACheatSet* c
|
||||||
GBAClearBreakpoint(device->p->board, cheats->hook->address, cheats->hook->mode, cheats->hook->patchedOpcode);
|
GBAClearBreakpoint(device->p->board, cheats->hook->address, cheats->hook->mode, cheats->hook->patchedOpcode);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _patchROM(struct mCheatDevice* device, struct GBACheatSet* cheats) {
|
|
||||||
if (!device->p) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
size_t i;
|
|
||||||
for (i = 0; i < GBACheatPatchListSize(&cheats->romPatches); ++i) {
|
|
||||||
struct GBACheatPatch* patch = GBACheatPatchListGetPointer(&cheats->romPatches, i);
|
|
||||||
if (patch->applied) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
GBAPatch16(device->p->cpu, patch->address, patch->newValue, &patch->oldValue);
|
|
||||||
patch->applied = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _unpatchROM(struct mCheatDevice* device, struct GBACheatSet* cheats) {
|
|
||||||
if (!device->p) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
size_t i;
|
|
||||||
for (i = 0; i < GBACheatPatchListSize(&cheats->romPatches); ++i) {
|
|
||||||
struct GBACheatPatch* patch = GBACheatPatchListGetPointer(&cheats->romPatches, i);
|
|
||||||
if (!patch->applied) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
GBAPatch16(device->p->cpu, patch->address, patch->oldValue, NULL);
|
|
||||||
patch->applied = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void GBACheatSetDeinit(struct mCheatSet* set);
|
static void GBACheatSetDeinit(struct mCheatSet* set);
|
||||||
static void GBACheatAddSet(struct mCheatSet* cheats, struct mCheatDevice* device);
|
static void GBACheatAddSet(struct mCheatSet* cheats, struct mCheatDevice* device);
|
||||||
static void GBACheatRemoveSet(struct mCheatSet* cheats, struct mCheatDevice* device);
|
static void GBACheatRemoveSet(struct mCheatSet* cheats, struct mCheatDevice* device);
|
||||||
|
@ -101,7 +69,6 @@ static struct mCheatSet* GBACheatSetCreate(struct mCheatDevice* device, const ch
|
||||||
|
|
||||||
set->d.refresh = GBACheatRefresh;
|
set->d.refresh = GBACheatRefresh;
|
||||||
|
|
||||||
GBACheatPatchListInit(&set->romPatches, 4);
|
|
||||||
return &set->d;
|
return &set->d;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,18 +87,15 @@ static void GBACheatSetDeinit(struct mCheatSet* set) {
|
||||||
free(gbaset->hook);
|
free(gbaset->hook);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GBACheatPatchListDeinit(&gbaset->romPatches);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void GBACheatAddSet(struct mCheatSet* cheats, struct mCheatDevice* device) {
|
static void GBACheatAddSet(struct mCheatSet* cheats, struct mCheatDevice* device) {
|
||||||
struct GBACheatSet* gbaset = (struct GBACheatSet*) cheats;
|
struct GBACheatSet* gbaset = (struct GBACheatSet*) cheats;
|
||||||
_addBreakpoint(device, gbaset);
|
_addBreakpoint(device, gbaset);
|
||||||
_patchROM(device, gbaset);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void GBACheatRemoveSet(struct mCheatSet* cheats, struct mCheatDevice* device) {
|
static void GBACheatRemoveSet(struct mCheatSet* cheats, struct mCheatDevice* device) {
|
||||||
struct GBACheatSet* gbaset = (struct GBACheatSet*) cheats;
|
struct GBACheatSet* gbaset = (struct GBACheatSet*) cheats;
|
||||||
_unpatchROM(device, gbaset);
|
|
||||||
_removeBreakpoint(device, gbaset);
|
_removeBreakpoint(device, gbaset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -276,14 +240,9 @@ bool GBACheatAddLine(struct mCheatSet* set, const char* line, int type) {
|
||||||
|
|
||||||
static void GBACheatRefresh(struct mCheatSet* cheats, struct mCheatDevice* device) {
|
static void GBACheatRefresh(struct mCheatSet* cheats, struct mCheatDevice* device) {
|
||||||
struct GBACheatSet* gbaset = (struct GBACheatSet*) cheats;
|
struct GBACheatSet* gbaset = (struct GBACheatSet*) cheats;
|
||||||
if (cheats->enabled) {
|
if (cheats->enabled && gbaset->hook && !gbaset->hook->reentries) {
|
||||||
_patchROM(device, gbaset);
|
|
||||||
if (gbaset->hook && !gbaset->hook->reentries) {
|
|
||||||
_addBreakpoint(device, gbaset);
|
_addBreakpoint(device, gbaset);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
_unpatchROM(device, gbaset);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void GBACheatSetCopyProperties(struct mCheatSet* set, struct mCheatSet* oldSet) {
|
static void GBACheatSetCopyProperties(struct mCheatSet* set, struct mCheatSet* oldSet) {
|
||||||
|
|
|
@ -93,7 +93,7 @@ void GBACheatSetGameSharkVersion(struct GBACheatSet* cheats, enum GBACheatGameSh
|
||||||
bool GBACheatAddGameSharkRaw(struct GBACheatSet* cheats, uint32_t op1, uint32_t op2) {
|
bool GBACheatAddGameSharkRaw(struct GBACheatSet* cheats, uint32_t op1, uint32_t op2) {
|
||||||
enum GBAGameSharkType type = op1 >> 28;
|
enum GBAGameSharkType type = op1 >> 28;
|
||||||
struct mCheat* cheat = 0;
|
struct mCheat* cheat = 0;
|
||||||
struct GBACheatPatch* romPatch;
|
struct mCheatPatch* romPatch;
|
||||||
|
|
||||||
if (cheats->incompleteCheat != COMPLETE) {
|
if (cheats->incompleteCheat != COMPLETE) {
|
||||||
struct mCheat* incompleteCheat = mCheatListGetPointer(&cheats->d.list, cheats->incompleteCheat);
|
struct mCheat* incompleteCheat = mCheatListGetPointer(&cheats->d.list, cheats->incompleteCheat);
|
||||||
|
@ -149,10 +149,12 @@ bool GBACheatAddGameSharkRaw(struct GBACheatSet* cheats, uint32_t op1, uint32_t
|
||||||
cheats->incompleteCheat = mCheatListIndex(&cheats->d.list, cheat);
|
cheats->incompleteCheat = mCheatListIndex(&cheats->d.list, cheat);
|
||||||
break;
|
break;
|
||||||
case GSA_PATCH:
|
case GSA_PATCH:
|
||||||
romPatch = GBACheatPatchListAppend(&cheats->romPatches);
|
romPatch = mCheatPatchListAppend(&cheats->d.romPatches);
|
||||||
romPatch->address = BASE_CART0 | ((op1 & 0xFFFFFF) << 1);
|
romPatch->address = BASE_CART0 | ((op1 & 0xFFFFFF) << 1);
|
||||||
romPatch->newValue = op2;
|
romPatch->value = op2;
|
||||||
romPatch->applied = false;
|
romPatch->applied = false;
|
||||||
|
romPatch->width = 2;
|
||||||
|
romPatch->check = false;
|
||||||
return true;
|
return true;
|
||||||
case GSA_BUTTON:
|
case GSA_BUTTON:
|
||||||
switch (op1 & 0x00F00000) {
|
switch (op1 & 0x00F00000) {
|
||||||
|
|
|
@ -230,9 +230,11 @@ static bool _addPAR3Special(struct GBACheatSet* cheats, uint32_t op2) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (romPatch >= 0) {
|
if (romPatch >= 0) {
|
||||||
struct GBACheatPatch* patch = GBACheatPatchListAppend(&cheats->romPatches);
|
struct mCheatPatch* patch = mCheatPatchListAppend(&cheats->d.romPatches);
|
||||||
patch->address = BASE_CART0 | ((op2 & 0xFFFFFF) << 1);
|
patch->address = BASE_CART0 | ((op2 & 0xFFFFFF) << 1);
|
||||||
patch->applied = false;
|
patch->applied = false;
|
||||||
|
patch->check = false;
|
||||||
|
patch->width = 2;
|
||||||
cheats->incompletePatch = patch;
|
cheats->incompletePatch = patch;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -240,8 +242,8 @@ static bool _addPAR3Special(struct GBACheatSet* cheats, uint32_t op2) {
|
||||||
|
|
||||||
bool GBACheatAddProActionReplayRaw(struct GBACheatSet* cheats, uint32_t op1, uint32_t op2) {
|
bool GBACheatAddProActionReplayRaw(struct GBACheatSet* cheats, uint32_t op1, uint32_t op2) {
|
||||||
if (cheats->incompletePatch) {
|
if (cheats->incompletePatch) {
|
||||||
cheats->incompletePatch->newValue = op1;
|
cheats->incompletePatch->value = op1;
|
||||||
cheats->incompletePatch = 0;
|
cheats->incompletePatch = NULL;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (cheats->incompleteCheat != COMPLETE) {
|
if (cheats->incompleteCheat != COMPLETE) {
|
||||||
|
|
Loading…
Reference in New Issue