GBA: Cheats can now be grouped and named

This commit is contained in:
Jeffrey Pfau 2015-02-09 02:54:17 -08:00
parent 3d44065826
commit 31a9bafae8
5 changed files with 178 additions and 68 deletions

View File

@ -7,10 +7,14 @@
#include "gba/gba.h" #include "gba/gba.h"
#include "gba/io.h" #include "gba/io.h"
#include "util/vfs.h"
#define MAX_LINE_LENGTH 128
const uint32_t GBA_CHEAT_DEVICE_ID = 0xABADC0DE; const uint32_t GBA_CHEAT_DEVICE_ID = 0xABADC0DE;
DEFINE_VECTOR(GBACheatList, struct GBACheat); DEFINE_VECTOR(GBACheatList, struct GBACheat);
DEFINE_VECTOR(GBACheatSets, struct GBACheatSet*);
static const uint32_t _gsa1S[4] = { 0x09F4FBBD, 0x9681884A, 0x352027E9, 0xF3DEE5A7 }; static const uint32_t _gsa1S[4] = { 0x09F4FBBD, 0x9681884A, 0x352027E9, 0xF3DEE5A7 };
static const uint32_t _par3S[4] = { 0x7AA9648F, 0x7FAE6994, 0xC0EFAAD5, 0x42712C57 }; static const uint32_t _par3S[4] = { 0x7AA9648F, 0x7FAE6994, 0xC0EFAAD5, 0x42712C57 };
@ -292,18 +296,21 @@ static bool _addGSA1(struct GBACheatSet* cheats, uint32_t op1, uint32_t op2) {
cheat->repeat = (op1 >> 16) & 0xFF; cheat->repeat = (op1 >> 16) & 0xFF;
return true; return true;
case GSA_HOOK: case GSA_HOOK:
if (cheats->hookAddress) { if (cheats->hook) {
return false; return false;
} }
cheats->hookAddress = BASE_CART0 | (op1 & (SIZE_CART0 - 1)); cheats->hook = malloc(sizeof(*cheats->hook));
cheats->hookMode = MODE_THUMB; cheats->hook->address = BASE_CART0 | (op1 & (SIZE_CART0 - 1));
cheats->hook->mode = MODE_THUMB;
cheats->hook->refs = 1;
cheats->hook->reentries = 0;
return true; return true;
default: default:
return false; return false;
} }
cheat->operand = op2; cheat->operand = op2;
cheat->repeat = 1; cheat->repeat = 1;
return false; return true;
} }
static bool _addGSA3(struct GBACheatSet* cheats, uint32_t op1, uint32_t op2) { static bool _addGSA3(struct GBACheatSet* cheats, uint32_t op1, uint32_t op2) {
@ -314,45 +321,53 @@ static bool _addGSA3(struct GBACheatSet* cheats, uint32_t op1, uint32_t op2) {
return false; return false;
} }
static void _addBreakpoint(struct GBACheatDevice* device) { static void _addBreakpoint(struct GBACheatDevice* device, struct GBACheatSet* cheats) {
if (!device->cheats || !device->p) { if (!device->p || !cheats->hook) {
return; return;
} }
GBASetBreakpoint(device->p, &device->d, device->cheats->hookAddress, device->cheats->hookMode, &device->cheats->patchedOpcode); ++cheats->hook->reentries;
} if (cheats->hook->reentries > 1) {
static void _removeBreakpoint(struct GBACheatDevice* device) {
if (!device->cheats || !device->p) {
return; return;
} }
GBAClearBreakpoint(device->p, device->cheats->hookAddress, device->cheats->hookMode, device->cheats->patchedOpcode); GBASetBreakpoint(device->p, &device->d, cheats->hook->address, cheats->hook->mode, &cheats->hook->patchedOpcode);
} }
static void _patchROM(struct GBACheatDevice* device) { static void _removeBreakpoint(struct GBACheatDevice* device, struct GBACheatSet* cheats) {
if (!device->cheats || !device->p) { if (!device->p || !cheats->hook) {
return;
}
--cheats->hook->reentries;
if (cheats->hook->reentries > 0) {
return;
}
GBAClearBreakpoint(device->p, cheats->hook->address, cheats->hook->mode, cheats->hook->patchedOpcode);
}
static void _patchROM(struct GBACheatDevice* device, struct GBACheatSet* cheats) {
if (!device->p) {
return; return;
} }
int i; int i;
for (i = 0; i < MAX_ROM_PATCHES; ++i) { for (i = 0; i < MAX_ROM_PATCHES; ++i) {
if (!device->cheats->romPatches[i].exists || device->cheats->romPatches[i].applied) { if (!cheats->romPatches[i].exists || cheats->romPatches[i].applied) {
continue; continue;
} }
GBAPatch16(device->p->cpu, device->cheats->romPatches[i].address, device->cheats->romPatches[i].newValue, &device->cheats->romPatches[i].oldValue); GBAPatch16(device->p->cpu, cheats->romPatches[i].address, cheats->romPatches[i].newValue, &cheats->romPatches[i].oldValue);
device->cheats->romPatches[i].applied = true; cheats->romPatches[i].applied = true;
} }
} }
static void _unpatchROM(struct GBACheatDevice* device) { static void _unpatchROM(struct GBACheatDevice* device, struct GBACheatSet* cheats) {
if (!device->cheats || !device->p) { if (!device->p) {
return; return;
} }
int i; int i;
for (i = 0; i < MAX_ROM_PATCHES; ++i) { for (i = 0; i < MAX_ROM_PATCHES; ++i) {
if (!device->cheats->romPatches[i].exists || !device->cheats->romPatches[i].applied) { if (!cheats->romPatches[i].exists || !cheats->romPatches[i].applied) {
continue; continue;
} }
GBAPatch16(device->p->cpu, device->cheats->romPatches[i].address, device->cheats->romPatches[i].oldValue, 0); GBAPatch16(device->p->cpu, cheats->romPatches[i].address, cheats->romPatches[i].oldValue, 0);
device->cheats->romPatches[i].applied = false; cheats->romPatches[i].applied = false;
} }
} }
@ -363,24 +378,47 @@ void GBACheatDeviceCreate(struct GBACheatDevice* device) {
device->d.id = GBA_CHEAT_DEVICE_ID; device->d.id = GBA_CHEAT_DEVICE_ID;
device->d.init = GBACheatDeviceInit; device->d.init = GBACheatDeviceInit;
device->d.deinit = GBACheatDeviceDeinit; device->d.deinit = GBACheatDeviceDeinit;
GBACheatSetsInit(&device->cheats, 4);
} }
void GBACheatSetInit(struct GBACheatSet* set) { void GBACheatDeviceDestroy(struct GBACheatDevice* device) {
set->hookAddress = 0; size_t i;
set->hookMode = MODE_THUMB; for (i = 0; i < GBACheatSetsSize(&device->cheats); ++i) {
struct GBACheatSet* set = *GBACheatSetsGetPointer(&device->cheats, i);
GBACheatSetDeinit(set);
free(set);
}
GBACheatSetsDeinit(&device->cheats);
}
void GBACheatSetInit(struct GBACheatSet* set, const char* name) {
GBACheatListInit(&set->list, 4); GBACheatListInit(&set->list, 4);
set->incompleteCheat = 0; set->incompleteCheat = 0;
set->patchedOpcode = 0;
set->gsaVersion = 0; set->gsaVersion = 0;
set->remainingAddresses = 0; set->remainingAddresses = 0;
set->hook = 0;
int i; int i;
for (i = 0; i < MAX_ROM_PATCHES; ++i) { for (i = 0; i < MAX_ROM_PATCHES; ++i) {
set->romPatches[i].exists = false; set->romPatches[i].exists = false;
} }
if (name) {
set->name = strdup(name);
} else {
set->name = 0;
}
} }
void GBACheatSetDeinit(struct GBACheatSet* set) { void GBACheatSetDeinit(struct GBACheatSet* set) {
GBACheatListDeinit(&set->list); GBACheatListDeinit(&set->list);
if (set->name) {
free(set->name);
}
if (set->hook) {
--set->hook->refs;
if (set->hook->refs == 0) {
free(set->hook);
}
}
} }
void GBACheatAttachDevice(struct GBA* gba, struct GBACheatDevice* device) { void GBACheatAttachDevice(struct GBA* gba, struct GBACheatDevice* device) {
@ -391,12 +429,10 @@ void GBACheatAttachDevice(struct GBA* gba, struct GBACheatDevice* device) {
ARMHotplugAttach(gba->cpu, GBA_COMPONENT_CHEAT_DEVICE); ARMHotplugAttach(gba->cpu, GBA_COMPONENT_CHEAT_DEVICE);
} }
void GBACheatInstallSet(struct GBACheatDevice* device, struct GBACheatSet* cheats) { void GBACheatAddSet(struct GBACheatDevice* device, struct GBACheatSet* cheats) {
_removeBreakpoint(device); *GBACheatSetsAppend(&device->cheats) = cheats;
_unpatchROM(device); _patchROM(device, cheats);
device->cheats = cheats; _addBreakpoint(device, cheats);
_addBreakpoint(device);
_patchROM(device);
} }
bool GBACheatAddCodeBreaker(struct GBACheatSet* cheats, uint32_t op1, uint16_t op2) { bool GBACheatAddCodeBreaker(struct GBACheatSet* cheats, uint32_t op1, uint16_t op2) {
@ -416,11 +452,14 @@ bool GBACheatAddCodeBreaker(struct GBACheatSet* cheats, uint32_t op1, uint16_t o
// TODO: Run checksum // TODO: Run checksum
return true; return true;
case CB_HOOK: case CB_HOOK:
if (cheats->hookAddress) { if (cheats->hook) {
return false; return false;
} }
cheats->hookAddress = BASE_CART0 | (op1 & (SIZE_CART0 - 1)); cheats->hook = malloc(sizeof(*cheats->hook));
cheats->hookMode = MODE_THUMB; cheats->hook->address = BASE_CART0 | (op1 & (SIZE_CART0 - 1));
cheats->hook->mode = MODE_THUMB;
cheats->hook->refs = 1;
cheats->hook->reentries = 0;
return true; return true;
case CB_OR_2: case CB_OR_2:
cheat = GBACheatListAppend(&cheats->list); cheat = GBACheatListAppend(&cheats->list);
@ -571,6 +610,51 @@ bool GBACheatAddGameSharkLine(struct GBACheatSet* cheats, const char* line) {
return GBACheatAddGameShark(cheats, op1, op2); return GBACheatAddGameShark(cheats, op1, op2);
} }
bool GBACheatParseFile(struct GBACheatDevice* device, struct VFile* vf) {
char cheat[MAX_LINE_LENGTH];
struct GBACheatSet* set = 0;
struct GBACheatSet* newSet;
while (true) {
size_t i;
ssize_t bytesRead = vf->readline(vf, cheat, sizeof(cheat));
if (bytesRead == 0) {
break;
}
if (bytesRead < 0) {
return false;
}
switch (cheat[0]) {
case '#':
i = 1;
while (isspace(cheat[i])) {
++i;
}
newSet = malloc(sizeof(*set));
GBACheatSetInit(newSet, &cheat[i]);
if (set) {
GBACheatAddSet(device, set);
newSet->gsaVersion = set->gsaVersion;
memcpy(newSet->gsaSeeds, set->gsaSeeds, sizeof(newSet->gsaSeeds));
newSet->hook = set->hook;
++newSet->hook->refs;
}
set = newSet;
break;
default:
if (!set) {
set = malloc(sizeof(*set));
GBACheatSetInit(set, 0);
}
GBACheatAddLine(set, cheat);
break;
}
}
if (set) {
GBACheatAddSet(device, set);
}
return true;
}
bool GBACheatAddLine(struct GBACheatSet* cheats, const char* line) { bool GBACheatAddLine(struct GBACheatSet* cheats, const char* line) {
uint32_t op1; uint32_t op1;
uint16_t op2; uint16_t op2;
@ -599,12 +683,12 @@ bool GBACheatAddLine(struct GBACheatSet* cheats, const char* line) {
return GBACheatAddGameShark(cheats, op1, realOp2); return GBACheatAddGameShark(cheats, op1, realOp2);
} }
void GBACheatRefresh(struct GBACheatDevice* device) { void GBACheatRefresh(struct GBACheatDevice* device, struct GBACheatSet* cheats) {
bool condition = true; bool condition = true;
int conditionRemaining = 0; int conditionRemaining = 0;
_patchROM(device); _patchROM(device, cheats);
size_t nCodes = GBACheatListSize(&device->cheats->list); size_t nCodes = GBACheatListSize(&cheats->list);
size_t i; size_t i;
for (i = 0; i < nCodes; ++i) { for (i = 0; i < nCodes; ++i) {
if (conditionRemaining > 0) { if (conditionRemaining > 0) {
@ -615,7 +699,7 @@ void GBACheatRefresh(struct GBACheatDevice* device) {
} else { } else {
condition = true; condition = true;
} }
struct GBACheat* cheat = GBACheatListGetPointer(&device->cheats->list, i); struct GBACheat* cheat = GBACheatListGetPointer(&cheats->list, i);
int32_t value = 0; int32_t value = 0;
int32_t operand = cheat->operand; int32_t operand = cheat->operand;
uint32_t operationsRemaining = cheat->repeat; uint32_t operationsRemaining = cheat->repeat;
@ -686,10 +770,20 @@ void GBACheatRefresh(struct GBACheatDevice* device) {
void GBACheatDeviceInit(struct ARMCore* cpu, struct ARMComponent* component) { void GBACheatDeviceInit(struct ARMCore* cpu, struct ARMComponent* component) {
struct GBACheatDevice* device = (struct GBACheatDevice*) component; struct GBACheatDevice* device = (struct GBACheatDevice*) component;
device->p = (struct GBA*) cpu->master; device->p = (struct GBA*) cpu->master;
_addBreakpoint(device); size_t i;
for (i = 0; i < GBACheatSetsSize(&device->cheats); ++i) {
struct GBACheatSet* cheats = *GBACheatSetsGetPointer(&device->cheats, i);
_addBreakpoint(device, cheats);
_patchROM(device, cheats);
}
} }
void GBACheatDeviceDeinit(struct ARMComponent* component) { void GBACheatDeviceDeinit(struct ARMComponent* component) {
struct GBACheatDevice* device = (struct GBACheatDevice*) component; struct GBACheatDevice* device = (struct GBACheatDevice*) component;
_removeBreakpoint(device); size_t i;
for (i = GBACheatSetsSize(&device->cheats); i--;) {
struct GBACheatSet* cheats = *GBACheatSetsGetPointer(&device->cheats, i);
_unpatchROM(device, cheats);
_removeBreakpoint(device, cheats);
}
} }

View File

@ -133,15 +133,21 @@ struct GBACheat {
int32_t operandOffset; int32_t operandOffset;
}; };
struct GBACheatHook {
uint32_t address;
enum ExecutionMode mode;
uint32_t patchedOpcode;
size_t refs;
size_t reentries;
};
DECLARE_VECTOR(GBACheatList, struct GBACheat); DECLARE_VECTOR(GBACheatList, struct GBACheat);
struct GBACheatSet { struct GBACheatSet {
uint32_t hookAddress; struct GBACheatHook* hook;
enum ExecutionMode hookMode;
struct GBACheatList list; struct GBACheatList list;
struct GBACheat* incompleteCheat; struct GBACheat* incompleteCheat;
uint32_t patchedOpcode;
struct GBACheatPatch { struct GBACheatPatch {
uint32_t address; uint32_t address;
@ -154,22 +160,31 @@ struct GBACheatSet {
int gsaVersion; int gsaVersion;
uint32_t gsaSeeds[4]; uint32_t gsaSeeds[4];
int remainingAddresses; int remainingAddresses;
char* name;
}; };
DECLARE_VECTOR(GBACheatSets, struct GBACheatSet*);
struct GBACheatDevice { struct GBACheatDevice {
struct ARMComponent d; struct ARMComponent d;
struct GBA* p; struct GBA* p;
struct GBACheatSet* cheats; struct GBACheatSets cheats;
}; };
void GBACheatDeviceCreate(struct GBACheatDevice*); struct VFile;
void GBACheatSetInit(struct GBACheatSet*); void GBACheatDeviceCreate(struct GBACheatDevice*);
void GBACheatDeviceDestroy(struct GBACheatDevice*);
void GBACheatSetInit(struct GBACheatSet*, const char* name);
void GBACheatSetDeinit(struct GBACheatSet*); void GBACheatSetDeinit(struct GBACheatSet*);
void GBACheatAttachDevice(struct GBA* gba, struct GBACheatDevice*); void GBACheatAttachDevice(struct GBA* gba, struct GBACheatDevice*);
void GBACheatInstallSet(struct GBACheatDevice*, struct GBACheatSet*);
void GBACheatAddSet(struct GBACheatDevice*, struct GBACheatSet*);
void GBACheatRemoveSet(struct GBACheatDevice*, struct GBACheatSet*);
bool GBACheatAddCodeBreaker(struct GBACheatSet*, uint32_t op1, uint16_t op2); bool GBACheatAddCodeBreaker(struct GBACheatSet*, uint32_t op1, uint16_t op2);
bool GBACheatAddCodeBreakerLine(struct GBACheatSet*, const char* line); bool GBACheatAddCodeBreakerLine(struct GBACheatSet*, const char* line);
@ -177,8 +192,9 @@ bool GBACheatAddCodeBreakerLine(struct GBACheatSet*, const char* line);
bool GBACheatAddGameShark(struct GBACheatSet*, uint32_t op1, uint32_t op2); bool GBACheatAddGameShark(struct GBACheatSet*, uint32_t op1, uint32_t op2);
bool GBACheatAddGameSharkLine(struct GBACheatSet*, const char* line); bool GBACheatAddGameSharkLine(struct GBACheatSet*, const char* line);
bool GBACheatParseFile(struct GBACheatDevice*, struct VFile*);
bool GBACheatAddLine(struct GBACheatSet*, const char* line); bool GBACheatAddLine(struct GBACheatSet*, const char* line);
void GBACheatRefresh(struct GBACheatDevice*); void GBACheatRefresh(struct GBACheatDevice*, struct GBACheatSet*);
#endif #endif

View File

@ -674,9 +674,17 @@ void GBABreakpoint(struct ARMCore* cpu, int immediate) {
case GBA_COMPONENT_CHEAT_DEVICE: case GBA_COMPONENT_CHEAT_DEVICE:
if (gba->cpu->components[GBA_COMPONENT_CHEAT_DEVICE]) { if (gba->cpu->components[GBA_COMPONENT_CHEAT_DEVICE]) {
struct GBACheatDevice* device = (struct GBACheatDevice*) gba->cpu->components[GBA_COMPONENT_CHEAT_DEVICE]; struct GBACheatDevice* device = (struct GBACheatDevice*) gba->cpu->components[GBA_COMPONENT_CHEAT_DEVICE];
if (device->cheats) { struct GBACheatHook* hook = 0;
GBACheatRefresh(device); size_t i;
ARMRunFake(cpu, device->cheats->patchedOpcode); for (i = 0; i < GBACheatSetsSize(&device->cheats); ++i) {
struct GBACheatSet* cheats = *GBACheatSetsGetPointer(&device->cheats, i);
if (cheats->hook && cheats->hook->address == _ARMPCAddress(cpu)) {
GBACheatRefresh(device, cheats);
hook = cheats->hook;
}
}
if (hook) {
ARMRunFake(cpu, hook->patchedOpcode);
} }
} }
break; break;

View File

@ -106,7 +106,6 @@ static THREAD_ENTRY _GBAThreadRun(void* context) {
struct ARMCore cpu; struct ARMCore cpu;
struct Patch patch; struct Patch patch;
struct GBACheatDevice cheatDevice; struct GBACheatDevice cheatDevice;
struct GBACheatSet cheats;
struct GBAThread* threadContext = context; struct GBAThread* threadContext = context;
struct ARMComponent* components[GBA_COMPONENT_MAX] = {}; struct ARMComponent* components[GBA_COMPONENT_MAX] = {};
int numComponents = GBA_COMPONENT_MAX; int numComponents = GBA_COMPONENT_MAX;
@ -167,23 +166,14 @@ static THREAD_ENTRY _GBAThreadRun(void* context) {
GBASkipBIOS(&cpu); GBASkipBIOS(&cpu);
} }
GBACheatDeviceCreate(&cheatDevice);
GBACheatSetInit(&cheats);
if (!threadContext->cheats) { if (!threadContext->cheats) {
threadContext->cheats = &cheats; GBACheatDeviceCreate(&cheatDevice);
threadContext->cheats = &cheatDevice;
} }
if (threadContext->cheatsFile) { if (threadContext->cheatsFile) {
char cheat[32]; GBACheatParseFile(threadContext->cheats, threadContext->cheatsFile);
while (true) {
size_t bytesRead = threadContext->cheatsFile->readline(threadContext->cheatsFile, cheat, sizeof(cheat));
if (!bytesRead) {
break;
}
GBACheatAddLine(threadContext->cheats, cheat);
}
} }
GBACheatInstallSet(&cheatDevice, threadContext->cheats); GBACheatAttachDevice(&gba, threadContext->cheats);
GBACheatAttachDevice(&gba, &cheatDevice);
if (threadContext->debugger) { if (threadContext->debugger) {
threadContext->debugger->log = GBADebuggerLogShim; threadContext->debugger->log = GBADebuggerLogShim;
@ -253,7 +243,9 @@ static THREAD_ENTRY _GBAThreadRun(void* context) {
threadContext->gba = 0; threadContext->gba = 0;
ARMDeinit(&cpu); ARMDeinit(&cpu);
GBADestroy(&gba); GBADestroy(&gba);
GBACheatSetDeinit(&cheats); if (&cheatDevice == threadContext->cheats) {
GBACheatDeviceDestroy(&cheatDevice);
}
threadContext->sync.videoFrameOn = false; threadContext->sync.videoFrameOn = false;
ConditionWake(&threadContext->sync.videoFrameAvailableCond); ConditionWake(&threadContext->sync.videoFrameAvailableCond);

View File

@ -110,7 +110,7 @@ struct GBAThread {
struct GBASerializedState** rewindBuffer; struct GBASerializedState** rewindBuffer;
int rewindBufferWriteOffset; int rewindBufferWriteOffset;
struct GBACheatSet* cheats; struct GBACheatDevice* cheats;
}; };
void GBAMapOptionsToContext(const struct GBAOptions*, struct GBAThread*); void GBAMapOptionsToContext(const struct GBAOptions*, struct GBAThread*);