mirror of https://github.com/mgba-emu/mgba.git
GBA Cheats: Better autodetection
This commit is contained in:
parent
ac0238ef07
commit
18c6e6c330
1
CHANGES
1
CHANGES
|
@ -1,6 +1,7 @@
|
|||
0.6.0: (Future)
|
||||
Features:
|
||||
- GBA: Support printing debug strings from inside a game
|
||||
- GBA: Better cheat type autodetection
|
||||
Bugfixes:
|
||||
- LR35902: Fix core never exiting with certain event patterns
|
||||
- GB Timer: Improve DIV reset behavior
|
||||
|
|
|
@ -138,19 +138,26 @@ static bool GBACheatAddAutodetect(struct GBACheatSet* set, uint32_t op1, uint32_
|
|||
char line[18] = "XXXXXXXX XXXXXXXX";
|
||||
snprintf(line, sizeof(line), "%08X %08X", op1, op2);
|
||||
|
||||
int gsaP, parP;
|
||||
switch (set->gsaVersion) {
|
||||
case 0:
|
||||
// Try to detect GameShark version
|
||||
GBACheatDecryptGameShark(&o1, &o2, GBACheatGameSharkSeeds);
|
||||
if ((o1 & 0xF0000000) == 0xF0000000 && !(o2 & 0xFFFFFCFE)) {
|
||||
GBACheatSetGameSharkVersion(set, 1);
|
||||
return GBACheatAddGameSharkRaw(set, o1, o2);
|
||||
}
|
||||
gsaP = GBACheatGameSharkProbability(o1, o2);
|
||||
o1 = op1;
|
||||
o2 = op2;
|
||||
GBACheatDecryptGameShark(&o1, &o2, GBACheatProActionReplaySeeds);
|
||||
if ((o1 & 0xFE000000) == 0xC4000000 && !(o2 & 0xFFFF0000)) {
|
||||
parP = GBACheatProActionReplayProbability(o1, o2);
|
||||
o1 = op1;
|
||||
o2 = op2;
|
||||
if (gsaP > parP) {
|
||||
GBACheatSetGameSharkVersion(set, 1);
|
||||
GBACheatDecryptGameShark(&o1, &o2, set->gsaSeeds);
|
||||
return GBACheatAddGameSharkRaw(set, o1, o2);
|
||||
} else {
|
||||
// If probabilities are equal, assume PARv3
|
||||
GBACheatSetGameSharkVersion(set, 3);
|
||||
GBACheatDecryptGameShark(&o1, &o2, set->gsaSeeds);
|
||||
return GBACheatAddProActionReplayRaw(set, o1, o2);
|
||||
}
|
||||
break;
|
||||
|
@ -314,3 +321,56 @@ static void GBACheatDumpDirectives(struct mCheatSet* set, struct StringList* dir
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int GBACheatAddressIsReal(uint32_t address) {
|
||||
switch (address >> BASE_OFFSET) {
|
||||
case REGION_BIOS:
|
||||
return -0x80;
|
||||
break;
|
||||
case REGION_WORKING_RAM:
|
||||
if ((address & OFFSET_MASK) > SIZE_WORKING_RAM) {
|
||||
return -0x40;
|
||||
}
|
||||
return 0x20;
|
||||
case REGION_WORKING_IRAM:
|
||||
if ((address & OFFSET_MASK) > SIZE_WORKING_IRAM) {
|
||||
return -0x40;
|
||||
}
|
||||
return 0x20;
|
||||
case REGION_IO:
|
||||
if ((address & OFFSET_MASK) > SIZE_IO) {
|
||||
return -0x80;
|
||||
}
|
||||
return 0x10;
|
||||
case REGION_OAM:
|
||||
if ((address & OFFSET_MASK) > SIZE_OAM) {
|
||||
return -0x80;
|
||||
}
|
||||
return -0x8;
|
||||
case REGION_VRAM:
|
||||
if ((address & OFFSET_MASK) > SIZE_VRAM) {
|
||||
return -0x80;
|
||||
}
|
||||
return -0x8;
|
||||
case REGION_PALETTE_RAM:
|
||||
if ((address & OFFSET_MASK) > SIZE_PALETTE_RAM) {
|
||||
return -0x80;
|
||||
}
|
||||
return -0x8;
|
||||
case REGION_CART0:
|
||||
case REGION_CART0_EX:
|
||||
case REGION_CART1:
|
||||
case REGION_CART1_EX:
|
||||
case REGION_CART2:
|
||||
case REGION_CART2_EX:
|
||||
return -0x8;
|
||||
case REGION_CART_SRAM:
|
||||
case REGION_CART_SRAM_MIRROR:
|
||||
if ((address & OFFSET_MASK) > SIZE_CART_SRAM) {
|
||||
return -0x80;
|
||||
}
|
||||
return -0x8;
|
||||
default:
|
||||
return -0xC0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -172,4 +172,6 @@ bool GBACheatAddProActionReplayLine(struct GBACheatSet*, const char* line);
|
|||
|
||||
bool GBACheatAddVBALine(struct GBACheatSet*, const char* line);
|
||||
|
||||
int GBACheatAddressIsReal(uint32_t address);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -226,3 +226,63 @@ bool GBACheatAddGameSharkLine(struct GBACheatSet* cheats, const char* line) {
|
|||
}
|
||||
return GBACheatAddGameShark(cheats, op1, op2);
|
||||
}
|
||||
|
||||
int GBACheatGameSharkProbability(uint32_t op1, uint32_t op2) {
|
||||
int probability = 0;
|
||||
if (op2 == 0x001DC0DE) {
|
||||
return 0x100;
|
||||
}
|
||||
uint32_t address = op1 & 0x0FFFFFFF;
|
||||
switch (op1 >> 28) {
|
||||
case GSA_ASSIGN_1:
|
||||
probability += 0x20;
|
||||
if (op2 & 0xFFFFFF00) {
|
||||
probability -= 0x10;
|
||||
}
|
||||
probability += GBACheatAddressIsReal(address);
|
||||
break;
|
||||
case GSA_ASSIGN_2:
|
||||
probability += 0x20;
|
||||
if (op2 & 0xFFFF0000) {
|
||||
probability -= 0x10;
|
||||
}
|
||||
probability += GBACheatAddressIsReal(address);
|
||||
break;
|
||||
case GSA_ASSIGN_4:
|
||||
probability += 0x20;
|
||||
probability += GBACheatAddressIsReal(address);
|
||||
break;
|
||||
case GSA_PATCH:
|
||||
probability += 0x20;
|
||||
if (op2 & 0xCFFF0000) {
|
||||
probability -= 0x10;
|
||||
}
|
||||
break;
|
||||
case GSA_BUTTON:
|
||||
probability += 0x10;
|
||||
break;
|
||||
case GSA_IF_EQ:
|
||||
probability += 0x20;
|
||||
if (op2 & 0xFFFF0000) {
|
||||
probability -= 0x10;
|
||||
}
|
||||
probability += GBACheatAddressIsReal(address);
|
||||
break;
|
||||
case GSA_IF_EQ_RANGE:
|
||||
probability += 0x20;
|
||||
probability += GBACheatAddressIsReal(op2);
|
||||
if (op1 & 0x0F000000) {
|
||||
probability -= 0x10;
|
||||
}
|
||||
break;
|
||||
case GSA_HOOK:
|
||||
probability += 0x20;
|
||||
if (op2 & 0xFFFF0000) {
|
||||
probability -= 0x10;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
probability -= 0x40;
|
||||
}
|
||||
return probability;
|
||||
}
|
||||
|
|
|
@ -14,5 +14,6 @@ void GBACheatDecryptGameShark(uint32_t* op1, uint32_t* op2, const uint32_t* seed
|
|||
void GBACheatReseedGameShark(uint32_t* seeds, uint16_t params, const uint8_t* t1, const uint8_t* t2);
|
||||
void GBACheatSetGameSharkVersion(struct GBACheatSet* cheats, int version);
|
||||
bool GBACheatAddGameSharkRaw(struct GBACheatSet* cheats, uint32_t op1, uint32_t op2);
|
||||
int GBACheatGameSharkProbability(uint32_t op1, uint32_t op2);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -326,3 +326,72 @@ bool GBACheatAddProActionReplayLine(struct GBACheatSet* cheats, const char* line
|
|||
}
|
||||
return GBACheatAddProActionReplay(cheats, op1, op2);
|
||||
}
|
||||
|
||||
int GBACheatProActionReplayProbability(uint32_t op1, uint32_t op2) {
|
||||
int probability = 0;
|
||||
if (op2 == 0x001DC0DE) {
|
||||
return 0x100;
|
||||
}
|
||||
if (op1 == 0xDEADFACE && !(op2 & 0xFFFF0000)) {
|
||||
return 0x100;
|
||||
}
|
||||
if (!op1) {
|
||||
probability += 0x20;
|
||||
uint32_t address = _parAddr(op2);
|
||||
switch (op2 & 0xFE000000) {
|
||||
case PAR3_OTHER_FILL_1:
|
||||
case PAR3_OTHER_FILL_2:
|
||||
case PAR3_OTHER_FILL_4:
|
||||
probability += GBACheatAddressIsReal(address);
|
||||
break;
|
||||
case PAR3_OTHER_PATCH_1:
|
||||
case PAR3_OTHER_PATCH_2:
|
||||
case PAR3_OTHER_PATCH_3:
|
||||
case PAR3_OTHER_PATCH_4:
|
||||
// TODO: Detect ROM address
|
||||
break;
|
||||
case PAR3_OTHER_END:
|
||||
case PAR3_OTHER_SLOWDOWN:
|
||||
case PAR3_OTHER_BUTTON_1:
|
||||
case PAR3_OTHER_BUTTON_2:
|
||||
case PAR3_OTHER_BUTTON_4:
|
||||
case PAR3_OTHER_ENDIF:
|
||||
case PAR3_OTHER_ELSE:
|
||||
if (op2 & 0x01FFFFFF) {
|
||||
probability -= 0x20;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
probability -= 0x40;
|
||||
break;
|
||||
}
|
||||
return probability;
|
||||
}
|
||||
int width = ((op1 & PAR3_WIDTH) >> (PAR3_WIDTH_BASE - 3));
|
||||
if (op1 & PAR3_COND) {
|
||||
probability += 0x20;
|
||||
if (width == 32) {
|
||||
return 0;
|
||||
}
|
||||
if (op2 & ~((1 << width) - 1)) {
|
||||
probability -= 0x10;
|
||||
}
|
||||
} else {
|
||||
uint32_t address = _parAddr(op1);
|
||||
probability += 0x20;
|
||||
switch (op1 & PAR3_BASE) {
|
||||
case PAR3_BASE_ADD:
|
||||
if (op2 & ~((1 << width) - 1)) {
|
||||
probability -= 0x10;
|
||||
}
|
||||
case PAR3_BASE_ASSIGN:
|
||||
case PAR3_BASE_INDIRECT:
|
||||
probability += GBACheatAddressIsReal(address);
|
||||
// Fall through
|
||||
break;
|
||||
case PAR3_BASE_OTHER:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return probability;
|
||||
}
|
||||
|
|
|
@ -11,5 +11,6 @@
|
|||
extern const uint32_t GBACheatProActionReplaySeeds[4];
|
||||
|
||||
bool GBACheatAddProActionReplayRaw(struct GBACheatSet* cheats, uint32_t op1, uint32_t op2);
|
||||
int GBACheatProActionReplayProbability(uint32_t op1, uint32_t op2);
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue