GBA: GameShark v1/v2 codes mostly complete

This commit is contained in:
Jeffrey Pfau 2015-02-07 18:46:26 -08:00
parent e30312f360
commit 3c9cc0ad87
3 changed files with 265 additions and 2 deletions

View File

@ -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);
}
}

View File

@ -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

View File

@ -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);