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
232
src/gba/cheats.c
232
src/gba/cheats.c
|
@ -12,6 +12,9 @@ const uint32_t GBA_CHEAT_DEVICE_ID = 0xABADC0DE;
|
||||||
|
|
||||||
DEFINE_VECTOR(GBACheatList, struct GBACheat);
|
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) {
|
static int32_t _readMem(struct ARMCore* cpu, uint32_t address, int width) {
|
||||||
switch (width) {
|
switch (width) {
|
||||||
case 1:
|
case 1:
|
||||||
|
@ -105,6 +108,126 @@ static const char* _hex16(const char* line, uint16_t* out) {
|
||||||
return line;
|
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) {
|
static void _addBreakpoint(struct GBACheatDevice* device) {
|
||||||
if (!device->cheats || !device->p) {
|
if (!device->cheats || !device->p) {
|
||||||
return;
|
return;
|
||||||
|
@ -119,6 +242,34 @@ static void _removeBreakpoint(struct GBACheatDevice* device) {
|
||||||
GBAClearBreakpoint(device->p, device->cheats->hookAddress, device->cheats->hookMode, device->cheats->patchedOpcode);
|
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 GBACheatDeviceInit(struct ARMCore*, struct ARMComponent*);
|
||||||
static void GBACheatDeviceDeinit(struct ARMComponent*);
|
static void GBACheatDeviceDeinit(struct ARMComponent*);
|
||||||
|
|
||||||
|
@ -134,6 +285,8 @@ void GBACheatSetInit(struct GBACheatSet* set) {
|
||||||
GBACheatListInit(&set->list, 4);
|
GBACheatListInit(&set->list, 4);
|
||||||
set->incompleteCheat = 0;
|
set->incompleteCheat = 0;
|
||||||
set->patchedOpcode = 0;
|
set->patchedOpcode = 0;
|
||||||
|
set->gsaVersion = 0;
|
||||||
|
set->remainingAddresses = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBACheatSetDeinit(struct GBACheatSet* set) {
|
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) {
|
void GBACheatInstallSet(struct GBACheatDevice* device, struct GBACheatSet* cheats) {
|
||||||
_removeBreakpoint(device);
|
_removeBreakpoint(device);
|
||||||
|
_unpatchROM(device);
|
||||||
device->cheats = cheats;
|
device->cheats = cheats;
|
||||||
_addBreakpoint(device);
|
_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) {
|
||||||
|
@ -278,9 +433,86 @@ bool GBACheatAddCodeBreakerLine(struct GBACheatSet* cheats, const char* line) {
|
||||||
return GBACheatAddCodeBreaker(cheats, op1, op2);
|
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) {
|
void GBACheatRefresh(struct GBACheatDevice* device) {
|
||||||
bool condition = true;
|
bool condition = true;
|
||||||
int conditionRemaining = 0;
|
int conditionRemaining = 0;
|
||||||
|
_patchROM(device);
|
||||||
|
|
||||||
size_t nCodes = GBACheatListSize(&device->cheats->list);
|
size_t nCodes = GBACheatListSize(&device->cheats->list);
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
#include "arm.h"
|
#include "arm.h"
|
||||||
#include "util/vector.h"
|
#include "util/vector.h"
|
||||||
|
|
||||||
|
#define MAX_ROM_PATCHES 4
|
||||||
|
|
||||||
enum GBACheatType {
|
enum GBACheatType {
|
||||||
CHEAT_ASSIGN,
|
CHEAT_ASSIGN,
|
||||||
CHEAT_AND,
|
CHEAT_AND,
|
||||||
|
@ -42,6 +44,18 @@ enum GBACodeBreakerType {
|
||||||
CB_IF_AND = 0xF,
|
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 {
|
struct GBACheat {
|
||||||
enum GBACheatType type;
|
enum GBACheatType type;
|
||||||
int width;
|
int width;
|
||||||
|
@ -62,6 +76,18 @@ struct GBACheatSet {
|
||||||
|
|
||||||
struct GBACheat* incompleteCheat;
|
struct GBACheat* incompleteCheat;
|
||||||
uint32_t patchedOpcode;
|
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 {
|
struct GBACheatDevice {
|
||||||
|
@ -82,6 +108,11 @@ void GBACheatInstallSet(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);
|
||||||
|
|
||||||
|
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*);
|
void GBACheatRefresh(struct GBACheatDevice*);
|
||||||
|
|
||||||
#endif
|
#endif
|
|
@ -179,7 +179,7 @@ static THREAD_ENTRY _GBAThreadRun(void* context) {
|
||||||
if (!bytesRead) {
|
if (!bytesRead) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
GBACheatAddCodeBreakerLine(threadContext->cheats, cheat);
|
GBACheatAddLine(threadContext->cheats, cheat);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GBACheatInstallSet(&cheatDevice, threadContext->cheats);
|
GBACheatInstallSet(&cheatDevice, threadContext->cheats);
|
||||||
|
|
Loading…
Reference in New Issue