mirror of https://github.com/mgba-emu/mgba.git
GBA Cheats: Support for encrypted CodeBreaker cheats
This commit is contained in:
parent
24c0893cf5
commit
20f790bb61
1
CHANGES
1
CHANGES
|
@ -1,6 +1,7 @@
|
||||||
0.5.0: (Future)
|
0.5.0: (Future)
|
||||||
Features:
|
Features:
|
||||||
- Game Boy support
|
- Game Boy support
|
||||||
|
- Support for encrypted CodeBreaker GBA cheats
|
||||||
Bugfixes:
|
Bugfixes:
|
||||||
- VFS: Fix reading 7z archives without rewinding first
|
- VFS: Fix reading 7z archives without rewinding first
|
||||||
- Qt: Fix sending gameStopped twice
|
- Qt: Fix sending gameStopped twice
|
||||||
|
|
|
@ -133,6 +133,8 @@ void GBACheatSetInit(struct GBACheatSet* set, const char* name) {
|
||||||
set->incompletePatch = 0;
|
set->incompletePatch = 0;
|
||||||
set->currentBlock = 0;
|
set->currentBlock = 0;
|
||||||
set->gsaVersion = 0;
|
set->gsaVersion = 0;
|
||||||
|
set->cbRngState = 0;
|
||||||
|
set->cbMaster = 0;
|
||||||
set->remainingAddresses = 0;
|
set->remainingAddresses = 0;
|
||||||
set->hook = 0;
|
set->hook = 0;
|
||||||
int i;
|
int i;
|
||||||
|
@ -548,6 +550,10 @@ void GBACheatRefresh(struct GBACheatDevice* device, struct GBACheatSet* cheats)
|
||||||
void GBACheatSetCopyProperties(struct GBACheatSet* newSet, struct GBACheatSet* set) {
|
void GBACheatSetCopyProperties(struct GBACheatSet* newSet, struct GBACheatSet* set) {
|
||||||
newSet->gsaVersion = set->gsaVersion;
|
newSet->gsaVersion = set->gsaVersion;
|
||||||
memcpy(newSet->gsaSeeds, set->gsaSeeds, sizeof(newSet->gsaSeeds));
|
memcpy(newSet->gsaSeeds, set->gsaSeeds, sizeof(newSet->gsaSeeds));
|
||||||
|
newSet->cbRngState = set->cbRngState;
|
||||||
|
newSet->cbMaster = set->cbMaster;
|
||||||
|
memcpy(newSet->cbSeeds, set->cbSeeds, sizeof(newSet->cbSeeds));
|
||||||
|
memcpy(newSet->cbTable, set->cbTable, sizeof(newSet->cbTable));
|
||||||
if (set->hook) {
|
if (set->hook) {
|
||||||
if (newSet->hook) {
|
if (newSet->hook) {
|
||||||
--newSet->hook->refs;
|
--newSet->hook->refs;
|
||||||
|
|
|
@ -175,6 +175,10 @@ struct GBACheatSet {
|
||||||
|
|
||||||
int gsaVersion;
|
int gsaVersion;
|
||||||
uint32_t gsaSeeds[4];
|
uint32_t gsaSeeds[4];
|
||||||
|
uint32_t cbRngState;
|
||||||
|
uint32_t cbMaster;
|
||||||
|
uint8_t cbTable[0x30];
|
||||||
|
uint32_t cbSeeds[4];
|
||||||
int remainingAddresses;
|
int remainingAddresses;
|
||||||
|
|
||||||
char* name;
|
char* name;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/* Copyright (c) 2013-2015 Jeffrey Pfau
|
/* Copyright (c) 2013-2016 Jeffrey Pfau
|
||||||
*
|
*
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
@ -10,11 +10,192 @@
|
||||||
#include "gba/io.h"
|
#include "gba/io.h"
|
||||||
#include "util/string.h"
|
#include "util/string.h"
|
||||||
|
|
||||||
|
static void _cbLoadByteswap(uint8_t* buffer, uint32_t op1, uint16_t op2) {
|
||||||
|
buffer[0] = op1 >> 24;
|
||||||
|
buffer[1] = op1 >> 16;
|
||||||
|
buffer[2] = op1 >> 8;
|
||||||
|
buffer[3] = op1;
|
||||||
|
buffer[4] = op2 >> 8;
|
||||||
|
buffer[5] = op2;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _cbStoreByteswap(uint8_t* buffer, uint32_t* op1, uint16_t* op2) {
|
||||||
|
*op1 = (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
|
||||||
|
*op2 = (buffer[4] << 8) | buffer[5];
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _cbDecrypt(struct GBACheatSet* cheats, uint32_t* op1, uint16_t* op2) {
|
||||||
|
uint8_t buffer[6];
|
||||||
|
int i;
|
||||||
|
|
||||||
|
_cbLoadByteswap(buffer, *op1, *op2);
|
||||||
|
for (i = sizeof(cheats->cbTable) - 1; i >= 0; --i) {
|
||||||
|
size_t offsetX = i >> 3;
|
||||||
|
size_t offsetY = cheats->cbTable[i] >> 3;
|
||||||
|
int bitX = i & 7;
|
||||||
|
int bitY = cheats->cbTable[i] & 7;
|
||||||
|
|
||||||
|
uint8_t x = (buffer[offsetX] >> bitX) & 1;
|
||||||
|
uint8_t y = (buffer[offsetY] >> bitY) & 1;
|
||||||
|
uint8_t x2 = buffer[offsetX] & ~(1 << bitX);
|
||||||
|
if (y) {
|
||||||
|
x2 |= 1 << bitX;
|
||||||
|
}
|
||||||
|
buffer[offsetX] = x2;
|
||||||
|
|
||||||
|
// This can't be moved earlier due to pointer aliasing
|
||||||
|
uint8_t y2 = buffer[offsetY] & ~(1 << bitY);
|
||||||
|
if (x) {
|
||||||
|
y2 |= 1 << bitY;
|
||||||
|
}
|
||||||
|
buffer[offsetY] = y2;
|
||||||
|
}
|
||||||
|
_cbStoreByteswap(buffer, op1, op2);
|
||||||
|
|
||||||
|
*op1 ^= cheats->cbSeeds[0];
|
||||||
|
*op2 ^= cheats->cbSeeds[1];
|
||||||
|
|
||||||
|
_cbLoadByteswap(buffer, *op1, *op2);
|
||||||
|
uint32_t master = cheats->cbMaster;
|
||||||
|
for (i = 0; i < 5; ++i) {
|
||||||
|
buffer[i] ^= (master >> 8) ^ buffer[i + 1];
|
||||||
|
}
|
||||||
|
buffer[5] ^= master >> 8;
|
||||||
|
|
||||||
|
for (i = 5; i > 0; --i) {
|
||||||
|
buffer[i] ^= master ^ buffer[i - 1];
|
||||||
|
}
|
||||||
|
buffer[0] ^= master;
|
||||||
|
_cbStoreByteswap(buffer, op1, op2);
|
||||||
|
|
||||||
|
*op1 ^= cheats->cbSeeds[2];
|
||||||
|
*op2 ^= cheats->cbSeeds[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t _cbRand(struct GBACheatSet* cheats) {
|
||||||
|
// Roll LCG three times to get enough bits of entropy
|
||||||
|
uint32_t roll = cheats->cbRngState * 0x41C64E6D + 0x3039;
|
||||||
|
uint32_t roll2 = roll * 0x41C64E6D + 0x3039;
|
||||||
|
uint32_t roll3 = roll2 * 0x41C64E6D + 0x3039;
|
||||||
|
uint32_t mix = (roll << 14) & 0xC0000000;
|
||||||
|
mix |= (roll2 >> 1) & 0x3FFF8000;
|
||||||
|
mix |= (roll3 >> 16) & 0x7FFF;
|
||||||
|
cheats->cbRngState = roll3;
|
||||||
|
return mix;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t _cbSwapIndex(struct GBACheatSet* cheats) {
|
||||||
|
uint32_t roll = _cbRand(cheats);
|
||||||
|
uint32_t count = sizeof(cheats->cbTable);
|
||||||
|
|
||||||
|
if (roll == count) {
|
||||||
|
roll = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (roll < count) {
|
||||||
|
return roll;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t bit = 1;
|
||||||
|
|
||||||
|
while (count < 0x10000000 && count < roll) {
|
||||||
|
count <<= 4;
|
||||||
|
bit <<= 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (count < 0x80000000 && count < roll) {
|
||||||
|
count <<= 1;
|
||||||
|
bit <<= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t mask;
|
||||||
|
while (true) {
|
||||||
|
mask = 0;
|
||||||
|
if (roll >= count) {
|
||||||
|
roll -= count;
|
||||||
|
}
|
||||||
|
if (roll >= count >> 1) {
|
||||||
|
roll -= count >> 1;
|
||||||
|
mask |= ROR(bit, 1);
|
||||||
|
}
|
||||||
|
if (roll >= count >> 2) {
|
||||||
|
roll -= count >> 2;
|
||||||
|
mask |= ROR(bit, 2);
|
||||||
|
}
|
||||||
|
if (roll >= count >> 3) {
|
||||||
|
roll -= count >> 3;
|
||||||
|
mask |= ROR(bit, 3);
|
||||||
|
}
|
||||||
|
if (!roll || !(bit >> 4)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
bit >>= 4;
|
||||||
|
count >>= 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
mask &= 0xE0000000;
|
||||||
|
if (!mask || !(bit & 7)) {
|
||||||
|
return roll;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mask & ROR(bit, 3)) {
|
||||||
|
roll += count >> 3;
|
||||||
|
}
|
||||||
|
if (mask & ROR(bit, 2)) {
|
||||||
|
roll += count >> 2;
|
||||||
|
}
|
||||||
|
if (mask & ROR(bit, 1)) {
|
||||||
|
roll += count >> 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return roll;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _cbReseed(struct GBACheatSet* cheats, uint32_t op1, uint16_t op2) {
|
||||||
|
cheats->cbRngState = (op2 & 0xFF) ^ 0x1111;
|
||||||
|
size_t i;
|
||||||
|
// Populate the initial seed table
|
||||||
|
for (i = 0; i < sizeof(cheats->cbTable); ++i) {
|
||||||
|
cheats->cbTable[i] = i;
|
||||||
|
}
|
||||||
|
// Swap pseudo-random table entries based on the input code
|
||||||
|
for (i = 0; i < 0x50; ++i) {
|
||||||
|
size_t x = _cbSwapIndex(cheats);
|
||||||
|
size_t y = _cbSwapIndex(cheats);
|
||||||
|
uint8_t swap = cheats->cbTable[x];
|
||||||
|
cheats->cbTable[x] = cheats->cbTable[y];
|
||||||
|
cheats->cbTable[y] = swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Spin the RNG some to make the initial seed
|
||||||
|
cheats->cbRngState = 0x4EFAD1C3;
|
||||||
|
for (i = 0; i < ((op1 >> 24) & 0xF); ++i) {
|
||||||
|
cheats->cbRngState = _cbRand(cheats);
|
||||||
|
}
|
||||||
|
|
||||||
|
cheats->cbSeeds[2] = _cbRand(cheats);
|
||||||
|
cheats->cbSeeds[3] = _cbRand(cheats);
|
||||||
|
|
||||||
|
cheats->cbRngState = (op2 >> 8) ^ 0xF254;
|
||||||
|
for (i = 0; i < (op2 >> 8); ++i) {
|
||||||
|
cheats->cbRngState = _cbRand(cheats);
|
||||||
|
}
|
||||||
|
|
||||||
|
cheats->cbSeeds[0] = _cbRand(cheats);
|
||||||
|
cheats->cbSeeds[1] = _cbRand(cheats);
|
||||||
|
|
||||||
|
cheats->cbMaster = op1;
|
||||||
|
}
|
||||||
|
|
||||||
bool GBACheatAddCodeBreaker(struct GBACheatSet* cheats, uint32_t op1, uint16_t op2) {
|
bool GBACheatAddCodeBreaker(struct GBACheatSet* cheats, uint32_t op1, uint16_t op2) {
|
||||||
char line[14] = "XXXXXXXX XXXX";
|
char line[14] = "XXXXXXXX XXXX";
|
||||||
snprintf(line, sizeof(line), "%08X %04X", op1, op2);
|
snprintf(line, sizeof(line), "%08X %04X", op1, op2);
|
||||||
GBACheatRegisterLine(cheats, line);
|
GBACheatRegisterLine(cheats, line);
|
||||||
|
|
||||||
|
if (cheats->cbMaster) {
|
||||||
|
_cbDecrypt(cheats, &op1, &op2);
|
||||||
|
}
|
||||||
|
|
||||||
enum GBACodeBreakerType type = op1 >> 28;
|
enum GBACodeBreakerType type = op1 >> 28;
|
||||||
struct GBACheat* cheat = 0;
|
struct GBACheat* cheat = 0;
|
||||||
|
|
||||||
|
@ -75,8 +256,8 @@ bool GBACheatAddCodeBreaker(struct GBACheatSet* cheats, uint32_t op1, uint16_t o
|
||||||
cheat->width = 2;
|
cheat->width = 2;
|
||||||
break;
|
break;
|
||||||
case CB_ENCRYPT:
|
case CB_ENCRYPT:
|
||||||
mLOG(CHEATS, STUB, "CodeBreaker encryption not supported");
|
_cbReseed(cheats, op1, op2);
|
||||||
return false;
|
return true;
|
||||||
case CB_IF_NE:
|
case CB_IF_NE:
|
||||||
cheat = GBACheatListAppend(&cheats->list);
|
cheat = GBACheatListAppend(&cheats->list);
|
||||||
cheat->type = CHEAT_IF_NE;
|
cheat->type = CHEAT_IF_NE;
|
||||||
|
|
Loading…
Reference in New Issue