diff --git a/src/gba/cheats.c b/src/gba/cheats.c index 6addfa506..311fece6c 100644 --- a/src/gba/cheats.c +++ b/src/gba/cheats.c @@ -7,10 +7,14 @@ #include "gba/gba.h" #include "gba/io.h" +#include "util/vfs.h" + +#define MAX_LINE_LENGTH 128 const uint32_t GBA_CHEAT_DEVICE_ID = 0xABADC0DE; DEFINE_VECTOR(GBACheatList, struct GBACheat); +DEFINE_VECTOR(GBACheatSets, struct GBACheatSet*); static const uint32_t _gsa1S[4] = { 0x09F4FBBD, 0x9681884A, 0x352027E9, 0xF3DEE5A7 }; 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; return true; case GSA_HOOK: - if (cheats->hookAddress) { + if (cheats->hook) { return false; } - cheats->hookAddress = BASE_CART0 | (op1 & (SIZE_CART0 - 1)); - cheats->hookMode = MODE_THUMB; + cheats->hook = malloc(sizeof(*cheats->hook)); + cheats->hook->address = BASE_CART0 | (op1 & (SIZE_CART0 - 1)); + cheats->hook->mode = MODE_THUMB; + cheats->hook->refs = 1; + cheats->hook->reentries = 0; return true; default: return false; } cheat->operand = op2; cheat->repeat = 1; - return false; + return true; } 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; } -static void _addBreakpoint(struct GBACheatDevice* device) { - if (!device->cheats || !device->p) { +static void _addBreakpoint(struct GBACheatDevice* device, struct GBACheatSet* cheats) { + if (!device->p || !cheats->hook) { return; } - GBASetBreakpoint(device->p, &device->d, device->cheats->hookAddress, device->cheats->hookMode, &device->cheats->patchedOpcode); -} - -static void _removeBreakpoint(struct GBACheatDevice* device) { - if (!device->cheats || !device->p) { + ++cheats->hook->reentries; + if (cheats->hook->reentries > 1) { 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) { - if (!device->cheats || !device->p) { +static void _removeBreakpoint(struct GBACheatDevice* device, struct GBACheatSet* cheats) { + 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; } int 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; } - GBAPatch16(device->p->cpu, device->cheats->romPatches[i].address, device->cheats->romPatches[i].newValue, &device->cheats->romPatches[i].oldValue); - device->cheats->romPatches[i].applied = true; + GBAPatch16(device->p->cpu, cheats->romPatches[i].address, cheats->romPatches[i].newValue, &cheats->romPatches[i].oldValue); + cheats->romPatches[i].applied = true; } } -static void _unpatchROM(struct GBACheatDevice* device) { - if (!device->cheats || !device->p) { +static void _unpatchROM(struct GBACheatDevice* device, struct GBACheatSet* cheats) { + if (!device->p) { return; } int 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; } - GBAPatch16(device->p->cpu, device->cheats->romPatches[i].address, device->cheats->romPatches[i].oldValue, 0); - device->cheats->romPatches[i].applied = false; + GBAPatch16(device->p->cpu, cheats->romPatches[i].address, cheats->romPatches[i].oldValue, 0); + cheats->romPatches[i].applied = false; } } @@ -363,24 +378,47 @@ void GBACheatDeviceCreate(struct GBACheatDevice* device) { device->d.id = GBA_CHEAT_DEVICE_ID; device->d.init = GBACheatDeviceInit; device->d.deinit = GBACheatDeviceDeinit; + GBACheatSetsInit(&device->cheats, 4); } -void GBACheatSetInit(struct GBACheatSet* set) { - set->hookAddress = 0; - set->hookMode = MODE_THUMB; +void GBACheatDeviceDestroy(struct GBACheatDevice* device) { + size_t i; + 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); set->incompleteCheat = 0; - set->patchedOpcode = 0; set->gsaVersion = 0; set->remainingAddresses = 0; + set->hook = 0; int i; for (i = 0; i < MAX_ROM_PATCHES; ++i) { set->romPatches[i].exists = false; } + if (name) { + set->name = strdup(name); + } else { + set->name = 0; + } } void GBACheatSetDeinit(struct GBACheatSet* set) { 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) { @@ -391,12 +429,10 @@ void GBACheatAttachDevice(struct GBA* gba, struct GBACheatDevice* device) { ARMHotplugAttach(gba->cpu, GBA_COMPONENT_CHEAT_DEVICE); } -void GBACheatInstallSet(struct GBACheatDevice* device, struct GBACheatSet* cheats) { - _removeBreakpoint(device); - _unpatchROM(device); - device->cheats = cheats; - _addBreakpoint(device); - _patchROM(device); +void GBACheatAddSet(struct GBACheatDevice* device, struct GBACheatSet* cheats) { + *GBACheatSetsAppend(&device->cheats) = cheats; + _patchROM(device, cheats); + _addBreakpoint(device, cheats); } 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 return true; case CB_HOOK: - if (cheats->hookAddress) { + if (cheats->hook) { return false; } - cheats->hookAddress = BASE_CART0 | (op1 & (SIZE_CART0 - 1)); - cheats->hookMode = MODE_THUMB; + cheats->hook = malloc(sizeof(*cheats->hook)); + cheats->hook->address = BASE_CART0 | (op1 & (SIZE_CART0 - 1)); + cheats->hook->mode = MODE_THUMB; + cheats->hook->refs = 1; + cheats->hook->reentries = 0; return true; case CB_OR_2: cheat = GBACheatListAppend(&cheats->list); @@ -571,6 +610,51 @@ bool GBACheatAddGameSharkLine(struct GBACheatSet* cheats, const char* line) { 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) { uint32_t op1; uint16_t op2; @@ -599,12 +683,12 @@ bool GBACheatAddLine(struct GBACheatSet* cheats, const char* line) { return GBACheatAddGameShark(cheats, op1, realOp2); } -void GBACheatRefresh(struct GBACheatDevice* device) { +void GBACheatRefresh(struct GBACheatDevice* device, struct GBACheatSet* cheats) { bool condition = true; int conditionRemaining = 0; - _patchROM(device); + _patchROM(device, cheats); - size_t nCodes = GBACheatListSize(&device->cheats->list); + size_t nCodes = GBACheatListSize(&cheats->list); size_t i; for (i = 0; i < nCodes; ++i) { if (conditionRemaining > 0) { @@ -615,7 +699,7 @@ void GBACheatRefresh(struct GBACheatDevice* device) { } else { condition = true; } - struct GBACheat* cheat = GBACheatListGetPointer(&device->cheats->list, i); + struct GBACheat* cheat = GBACheatListGetPointer(&cheats->list, i); int32_t value = 0; int32_t operand = cheat->operand; uint32_t operationsRemaining = cheat->repeat; @@ -686,10 +770,20 @@ void GBACheatRefresh(struct GBACheatDevice* device) { void GBACheatDeviceInit(struct ARMCore* cpu, struct ARMComponent* component) { struct GBACheatDevice* device = (struct GBACheatDevice*) component; 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) { 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); + } } diff --git a/src/gba/cheats.h b/src/gba/cheats.h index 69f02a40d..588b030c8 100644 --- a/src/gba/cheats.h +++ b/src/gba/cheats.h @@ -133,15 +133,21 @@ struct GBACheat { 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); struct GBACheatSet { - uint32_t hookAddress; - enum ExecutionMode hookMode; + struct GBACheatHook* hook; struct GBACheatList list; struct GBACheat* incompleteCheat; - uint32_t patchedOpcode; struct GBACheatPatch { uint32_t address; @@ -154,22 +160,31 @@ struct GBACheatSet { int gsaVersion; uint32_t gsaSeeds[4]; int remainingAddresses; + + char* name; }; +DECLARE_VECTOR(GBACheatSets, struct GBACheatSet*); + struct GBACheatDevice { struct ARMComponent d; 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 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 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 GBACheatAddGameSharkLine(struct GBACheatSet*, const char* line); +bool GBACheatParseFile(struct GBACheatDevice*, struct VFile*); bool GBACheatAddLine(struct GBACheatSet*, const char* line); -void GBACheatRefresh(struct GBACheatDevice*); +void GBACheatRefresh(struct GBACheatDevice*, struct GBACheatSet*); #endif \ No newline at end of file diff --git a/src/gba/gba.c b/src/gba/gba.c index 642653cd6..b8eb1be12 100644 --- a/src/gba/gba.c +++ b/src/gba/gba.c @@ -674,9 +674,17 @@ void GBABreakpoint(struct ARMCore* cpu, int immediate) { case GBA_COMPONENT_CHEAT_DEVICE: if (gba->cpu->components[GBA_COMPONENT_CHEAT_DEVICE]) { struct GBACheatDevice* device = (struct GBACheatDevice*) gba->cpu->components[GBA_COMPONENT_CHEAT_DEVICE]; - if (device->cheats) { - GBACheatRefresh(device); - ARMRunFake(cpu, device->cheats->patchedOpcode); + struct GBACheatHook* hook = 0; + size_t i; + 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; diff --git a/src/gba/supervisor/thread.c b/src/gba/supervisor/thread.c index 5026fe62e..93fb07439 100644 --- a/src/gba/supervisor/thread.c +++ b/src/gba/supervisor/thread.c @@ -106,7 +106,6 @@ static THREAD_ENTRY _GBAThreadRun(void* context) { struct ARMCore cpu; struct Patch patch; struct GBACheatDevice cheatDevice; - struct GBACheatSet cheats; struct GBAThread* threadContext = context; struct ARMComponent* components[GBA_COMPONENT_MAX] = {}; int numComponents = GBA_COMPONENT_MAX; @@ -167,23 +166,14 @@ static THREAD_ENTRY _GBAThreadRun(void* context) { GBASkipBIOS(&cpu); } - GBACheatDeviceCreate(&cheatDevice); - GBACheatSetInit(&cheats); if (!threadContext->cheats) { - threadContext->cheats = &cheats; + GBACheatDeviceCreate(&cheatDevice); + threadContext->cheats = &cheatDevice; } if (threadContext->cheatsFile) { - char cheat[32]; - while (true) { - size_t bytesRead = threadContext->cheatsFile->readline(threadContext->cheatsFile, cheat, sizeof(cheat)); - if (!bytesRead) { - break; - } - GBACheatAddLine(threadContext->cheats, cheat); - } + GBACheatParseFile(threadContext->cheats, threadContext->cheatsFile); } - GBACheatInstallSet(&cheatDevice, threadContext->cheats); - GBACheatAttachDevice(&gba, &cheatDevice); + GBACheatAttachDevice(&gba, threadContext->cheats); if (threadContext->debugger) { threadContext->debugger->log = GBADebuggerLogShim; @@ -253,7 +243,9 @@ static THREAD_ENTRY _GBAThreadRun(void* context) { threadContext->gba = 0; ARMDeinit(&cpu); GBADestroy(&gba); - GBACheatSetDeinit(&cheats); + if (&cheatDevice == threadContext->cheats) { + GBACheatDeviceDestroy(&cheatDevice); + } threadContext->sync.videoFrameOn = false; ConditionWake(&threadContext->sync.videoFrameAvailableCond); diff --git a/src/gba/supervisor/thread.h b/src/gba/supervisor/thread.h index 8c4153266..837deaae8 100644 --- a/src/gba/supervisor/thread.h +++ b/src/gba/supervisor/thread.h @@ -110,7 +110,7 @@ struct GBAThread { struct GBASerializedState** rewindBuffer; int rewindBufferWriteOffset; - struct GBACheatSet* cheats; + struct GBACheatDevice* cheats; }; void GBAMapOptionsToContext(const struct GBAOptions*, struct GBAThread*);