mirror of https://github.com/mgba-emu/mgba.git
GBA: GameShark v1/v2 codes mostly complete
This commit is contained in:
parent
e30312f360
commit
3c9cc0ad87
234
src/gba/cheats.c
234
src/gba/cheats.c
|
@ -12,6 +12,9 @@ const uint32_t GBA_CHEAT_DEVICE_ID = 0xABADC0DE;
|
|||
|
||||
DEFINE_VECTOR(GBACheatList, struct GBACheat);
|
||||
|
||||
static const uint32_t _gsa1S[4] = { 0x09F4FBBD, 0x9681884A, 0x352027E9, 0xF3DEE5A7 };
|
||||
static const uint32_t _gsa3S[4] = { 0x7AA9648F, 0x7FAE6994, 0xC0EFAAD5, 0x42712C57 };
|
||||
|
||||
static int32_t _readMem(struct ARMCore* cpu, uint32_t address, int width) {
|
||||
switch (width) {
|
||||
case 1:
|
||||
|
@ -105,6 +108,126 @@ static const char* _hex16(const char* line, uint16_t* out) {
|
|||
return line;
|
||||
}
|
||||
|
||||
// http://en.wikipedia.org/wiki/Tiny_Encryption_Algorithm
|
||||
static void _decryptGameShark(uint32_t* op1, uint32_t* op2, const uint32_t* seeds) {
|
||||
uint32_t sum = 0xC6EF3720;
|
||||
int i;
|
||||
for (i = 0; i < 32; ++i) {
|
||||
*op2 -= ((*op1 << 4) + seeds[2]) ^ (*op1 + sum) ^ ((*op1 >> 5) + seeds[3]);
|
||||
*op1 -= ((*op2 << 4) + seeds[0]) ^ (*op2 + sum) ^ ((*op2 >> 5) + seeds[1]);
|
||||
sum -= 0x9E3779B9;
|
||||
}
|
||||
}
|
||||
|
||||
static bool _addGSA1(struct GBACheatSet* cheats, uint32_t op1, uint32_t op2) {
|
||||
enum GBAGameSharkType type = op1 >> 28;
|
||||
struct GBACheat* cheat = 0;
|
||||
|
||||
if (cheats->incompleteCheat) {
|
||||
if (cheats->remainingAddresses > 0) {
|
||||
cheat = GBACheatListAppend(&cheats->list);
|
||||
cheat->type = CHEAT_ASSIGN;
|
||||
cheat->width = 4;
|
||||
cheat->address = op1;
|
||||
cheat->operand = cheats->incompleteCheat->operand;
|
||||
cheat->repeat = 1;
|
||||
--cheats->remainingAddresses;
|
||||
}
|
||||
if (cheats->remainingAddresses > 0) {
|
||||
cheat = GBACheatListAppend(&cheats->list);
|
||||
cheat->type = CHEAT_ASSIGN;
|
||||
cheat->width = 4;
|
||||
cheat->address = op2;
|
||||
cheat->operand = cheats->incompleteCheat->operand;
|
||||
cheat->repeat = 1;
|
||||
--cheats->remainingAddresses;
|
||||
}
|
||||
if (cheats->remainingAddresses == 0) {
|
||||
cheats->incompleteCheat = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case GSA_ASSIGN_1:
|
||||
cheat = GBACheatListAppend(&cheats->list);
|
||||
cheat->type = CHEAT_ASSIGN;
|
||||
cheat->width = 1;
|
||||
cheat->address = op1 & 0x0FFFFFFF;
|
||||
break;
|
||||
case GSA_ASSIGN_2:
|
||||
cheat = GBACheatListAppend(&cheats->list);
|
||||
cheat->type = CHEAT_ASSIGN;
|
||||
cheat->width = 2;
|
||||
cheat->address = op1 & 0x0FFFFFFF;
|
||||
break;
|
||||
case GSA_ASSIGN_4:
|
||||
cheat = GBACheatListAppend(&cheats->list);
|
||||
cheat->type = CHEAT_ASSIGN;
|
||||
cheat->width = 4;
|
||||
cheat->address = op1 & 0x0FFFFFFF;
|
||||
break;
|
||||
case GSA_ASSIGN_LIST:
|
||||
cheats->remainingAddresses = (op1 & 0xFFFF) - 1;
|
||||
cheat = GBACheatListAppend(&cheats->list);
|
||||
cheat->type = CHEAT_ASSIGN;
|
||||
cheat->width = 4;
|
||||
cheat->address = op2;
|
||||
cheats->incompleteCheat = cheat;
|
||||
break;
|
||||
case GSA_PATCH:
|
||||
if (cheats->nRomPatches >= MAX_ROM_PATCHES) {
|
||||
return false;
|
||||
}
|
||||
cheats->romPatches[cheats->nRomPatches].address = (op1 & 0xFFFFFF) << 1;
|
||||
cheats->romPatches[cheats->nRomPatches].newValue = op2;
|
||||
cheats->romPatches[cheats->nRomPatches].applied = false;
|
||||
++cheats->nRomPatches;
|
||||
return true;
|
||||
case GSA_BUTTON:
|
||||
// TODO: Implement button
|
||||
return false;
|
||||
case GSA_IF_EQ:
|
||||
if (op1 == 0xDEADFACE) {
|
||||
// TODO: Change seeds
|
||||
return false;
|
||||
}
|
||||
cheat = GBACheatListAppend(&cheats->list);
|
||||
cheat->type = CHEAT_IF_EQ;
|
||||
cheat->width = 2;
|
||||
cheat->address = op1 & 0x0FFFFFFF;
|
||||
break;
|
||||
case GSA_IF_EQ_RANGE:
|
||||
cheat = GBACheatListAppend(&cheats->list);
|
||||
cheat->type = CHEAT_IF_EQ;
|
||||
cheat->width = 2;
|
||||
cheat->address = op2 & 0x0FFFFFFF;
|
||||
cheat->operand = op1 & 0xFFFF;
|
||||
cheat->repeat = (op1 >> 16) & 0xFF;
|
||||
return true;
|
||||
case GSA_HOOK:
|
||||
if (cheats->hookAddress) {
|
||||
return false;
|
||||
}
|
||||
cheats->hookAddress = BASE_CART0 | (op1 & (SIZE_CART0 - 1));
|
||||
cheats->hookMode = MODE_THUMB;
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
cheat->operand = op2;
|
||||
cheat->repeat = 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool _addGSA3(struct GBACheatSet* cheats, uint32_t op1, uint32_t op2) {
|
||||
// TODO
|
||||
UNUSED(cheats);
|
||||
UNUSED(op1);
|
||||
UNUSED(op2);
|
||||
return false;
|
||||
}
|
||||
|
||||
static void _addBreakpoint(struct GBACheatDevice* device) {
|
||||
if (!device->cheats || !device->p) {
|
||||
return;
|
||||
|
@ -119,6 +242,34 @@ static void _removeBreakpoint(struct GBACheatDevice* device) {
|
|||
GBAClearBreakpoint(device->p, device->cheats->hookAddress, device->cheats->hookMode, device->cheats->patchedOpcode);
|
||||
}
|
||||
|
||||
static void _patchROM(struct GBACheatDevice* device) {
|
||||
if (!device->cheats || !device->p) {
|
||||
return;
|
||||
}
|
||||
int i;
|
||||
for (i = 0; i < device->cheats->nRomPatches; ++i) {
|
||||
if (device->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;
|
||||
}
|
||||
}
|
||||
|
||||
static void _unpatchROM(struct GBACheatDevice* device) {
|
||||
if (!device->cheats || !device->p) {
|
||||
return;
|
||||
}
|
||||
int i;
|
||||
for (i = 0; i < device->cheats->nRomPatches; ++i) {
|
||||
if (!device->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;
|
||||
}
|
||||
}
|
||||
|
||||
static void GBACheatDeviceInit(struct ARMCore*, struct ARMComponent*);
|
||||
static void GBACheatDeviceDeinit(struct ARMComponent*);
|
||||
|
||||
|
@ -134,6 +285,8 @@ void GBACheatSetInit(struct GBACheatSet* set) {
|
|||
GBACheatListInit(&set->list, 4);
|
||||
set->incompleteCheat = 0;
|
||||
set->patchedOpcode = 0;
|
||||
set->gsaVersion = 0;
|
||||
set->remainingAddresses = 0;
|
||||
}
|
||||
|
||||
void GBACheatSetDeinit(struct GBACheatSet* set) {
|
||||
|
@ -150,8 +303,10 @@ void GBACheatAttachDevice(struct GBA* gba, struct GBACheatDevice* device) {
|
|||
|
||||
void GBACheatInstallSet(struct GBACheatDevice* device, struct GBACheatSet* cheats) {
|
||||
_removeBreakpoint(device);
|
||||
_unpatchROM(device);
|
||||
device->cheats = cheats;
|
||||
_addBreakpoint(device);
|
||||
_patchROM(device);
|
||||
}
|
||||
|
||||
bool GBACheatAddCodeBreaker(struct GBACheatSet* cheats, uint32_t op1, uint16_t op2) {
|
||||
|
@ -278,9 +433,86 @@ bool GBACheatAddCodeBreakerLine(struct GBACheatSet* cheats, const char* line) {
|
|||
return GBACheatAddCodeBreaker(cheats, op1, op2);
|
||||
}
|
||||
|
||||
bool GBACheatAddGameShark(struct GBACheatSet* set, uint32_t op1, uint32_t op2) {
|
||||
uint32_t o1 = op1;
|
||||
uint32_t o2 = op2;
|
||||
switch (set->gsaVersion) {
|
||||
case 0:
|
||||
// Try to detect GameShark version
|
||||
_decryptGameShark(&o1, &o2, _gsa1S);
|
||||
if ((o1 & 0xF0000000) == 0xF0000000 && !(o2 & 0xFFFFFCFE)) {
|
||||
set->gsaVersion = 1;
|
||||
memcpy(set->gsaSeeds, _gsa1S, 4 * sizeof(uint32_t));
|
||||
return _addGSA1(set, o1, o2);
|
||||
}
|
||||
o1 = op1;
|
||||
o2 = op2;
|
||||
_decryptGameShark(&o1, &o2, _gsa3S);
|
||||
if ((o1 & 0xFE000000) == 0xC4000000 && !(o2 & 0xFFFF0000)) {
|
||||
set->gsaVersion = 3;
|
||||
memcpy(set->gsaSeeds, _gsa3S, 4 * sizeof(uint32_t));
|
||||
return _addGSA3(set, o1, o2);
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
_decryptGameShark(&o1, &o2, set->gsaSeeds);
|
||||
return _addGSA1(set, o1, o2);
|
||||
case 3:
|
||||
_decryptGameShark(&o1, &o2, set->gsaSeeds);
|
||||
return _addGSA3(set, o1, o2);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GBACheatAddGameSharkLine(struct GBACheatSet* cheats, const char* line) {
|
||||
uint32_t op1;
|
||||
uint32_t op2;
|
||||
line = _hex32(line, &op1);
|
||||
if (!line) {
|
||||
return false;
|
||||
}
|
||||
while (*line == ' ') {
|
||||
++line;
|
||||
}
|
||||
line = _hex32(line, &op2);
|
||||
if (!line) {
|
||||
return false;
|
||||
}
|
||||
return GBACheatAddGameShark(cheats, op1, op2);
|
||||
}
|
||||
|
||||
bool GBACheatAddLine(struct GBACheatSet* cheats, const char* line) {
|
||||
uint32_t op1;
|
||||
uint16_t op2;
|
||||
uint16_t op3;
|
||||
line = _hex32(line, &op1);
|
||||
if (!line) {
|
||||
return false;
|
||||
}
|
||||
while (isspace(line[0])) {
|
||||
++line;
|
||||
}
|
||||
line = _hex16(line, &op2);
|
||||
if (!line) {
|
||||
return false;
|
||||
}
|
||||
if (isspace(line[0])) {
|
||||
return GBACheatAddCodeBreaker(cheats, op1, op2);
|
||||
}
|
||||
line = _hex16(line, &op3);
|
||||
if (!line) {
|
||||
return false;
|
||||
}
|
||||
uint32_t realOp2 = op2;
|
||||
realOp2 <<= 16;
|
||||
realOp2 |= op3;
|
||||
return GBACheatAddGameShark(cheats, op1, realOp2);
|
||||
}
|
||||
|
||||
void GBACheatRefresh(struct GBACheatDevice* device) {
|
||||
bool condition = true;
|
||||
int conditionRemaining = 0;
|
||||
_patchROM(device);
|
||||
|
||||
size_t nCodes = GBACheatListSize(&device->cheats->list);
|
||||
size_t i;
|
||||
|
@ -358,4 +590,4 @@ void GBACheatDeviceInit(struct ARMCore* cpu, struct ARMComponent* component) {
|
|||
void GBACheatDeviceDeinit(struct ARMComponent* component) {
|
||||
struct GBACheatDevice* device = (struct GBACheatDevice*) component;
|
||||
_removeBreakpoint(device);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
#include "arm.h"
|
||||
#include "util/vector.h"
|
||||
|
||||
#define MAX_ROM_PATCHES 4
|
||||
|
||||
enum GBACheatType {
|
||||
CHEAT_ASSIGN,
|
||||
CHEAT_AND,
|
||||
|
@ -42,6 +44,18 @@ enum GBACodeBreakerType {
|
|||
CB_IF_AND = 0xF,
|
||||
};
|
||||
|
||||
enum GBAGameSharkType {
|
||||
GSA_ASSIGN_1 = 0x0,
|
||||
GSA_ASSIGN_2 = 0x1,
|
||||
GSA_ASSIGN_4 = 0x2,
|
||||
GSA_ASSIGN_LIST = 0x3,
|
||||
GSA_PATCH = 0x6,
|
||||
GSA_BUTTON = 0x8,
|
||||
GSA_IF_EQ = 0xD,
|
||||
GSA_IF_EQ_RANGE = 0xE,
|
||||
GSA_HOOK = 0xF
|
||||
};
|
||||
|
||||
struct GBACheat {
|
||||
enum GBACheatType type;
|
||||
int width;
|
||||
|
@ -62,6 +76,18 @@ struct GBACheatSet {
|
|||
|
||||
struct GBACheat* incompleteCheat;
|
||||
uint32_t patchedOpcode;
|
||||
|
||||
struct GBACheatPatch {
|
||||
uint32_t address;
|
||||
int16_t newValue;
|
||||
int16_t oldValue;
|
||||
bool applied;
|
||||
} romPatches[MAX_ROM_PATCHES];
|
||||
int nRomPatches;
|
||||
|
||||
int gsaVersion;
|
||||
uint32_t gsaSeeds[4];
|
||||
int remainingAddresses;
|
||||
};
|
||||
|
||||
struct GBACheatDevice {
|
||||
|
@ -82,6 +108,11 @@ void GBACheatInstallSet(struct GBACheatDevice*, struct GBACheatSet*);
|
|||
bool GBACheatAddCodeBreaker(struct GBACheatSet*, uint32_t op1, uint16_t op2);
|
||||
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 GBACheatAddLine(struct GBACheatSet*, const char* line);
|
||||
|
||||
void GBACheatRefresh(struct GBACheatDevice*);
|
||||
|
||||
#endif
|
|
@ -179,7 +179,7 @@ static THREAD_ENTRY _GBAThreadRun(void* context) {
|
|||
if (!bytesRead) {
|
||||
break;
|
||||
}
|
||||
GBACheatAddCodeBreakerLine(threadContext->cheats, cheat);
|
||||
GBACheatAddLine(threadContext->cheats, cheat);
|
||||
}
|
||||
}
|
||||
GBACheatInstallSet(&cheatDevice, threadContext->cheats);
|
||||
|
|
Loading…
Reference in New Issue