all repos — mgba @ 1326626777dacd7a7882e39c0f6dccc752549a1e

mGBA Game Boy Advance Emulator

src/gba/cheats/codebreaker.c (view raw)

  1/* Copyright (c) 2013-2016 Jeffrey Pfau
  2 *
  3 * This Source Code Form is subject to the terms of the Mozilla Public
  4 * License, v. 2.0. If a copy of the MPL was not distributed with this
  5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  6#include "gba/cheats.h"
  7
  8#include "gba/cheats/cheats-private.h"
  9#include "gba/gba.h"
 10#include "gba/io.h"
 11#include "util/string.h"
 12
 13static void _cbLoadByteswap(uint8_t* buffer, uint32_t op1, uint16_t op2) {
 14	buffer[0] = op1 >> 24;
 15	buffer[1] = op1 >> 16;
 16	buffer[2] = op1 >> 8;
 17	buffer[3] = op1;
 18	buffer[4] = op2 >> 8;
 19	buffer[5] = op2;
 20}
 21
 22static void _cbStoreByteswap(uint8_t* buffer, uint32_t* op1, uint16_t* op2) {
 23	*op1 = (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
 24	*op2 = (buffer[4] << 8) | buffer[5];
 25}
 26
 27static void _cbDecrypt(struct GBACheatSet* cheats, uint32_t* op1, uint16_t* op2) {
 28	uint8_t buffer[6];
 29	int i;
 30
 31	_cbLoadByteswap(buffer, *op1, *op2);
 32	for (i = sizeof(cheats->cbTable) - 1; i >= 0; --i) {
 33		size_t offsetX = i >> 3;
 34		size_t offsetY = cheats->cbTable[i] >> 3;
 35		int bitX = i & 7;
 36		int bitY = cheats->cbTable[i] & 7;
 37
 38		uint8_t x = (buffer[offsetX] >> bitX) & 1;
 39		uint8_t y = (buffer[offsetY] >> bitY) & 1;
 40		uint8_t x2 = buffer[offsetX] & ~(1 << bitX);
 41		if (y) {
 42			x2 |= 1 << bitX;
 43		}
 44		buffer[offsetX] = x2;
 45
 46		// This can't be moved earlier due to pointer aliasing
 47		uint8_t y2 = buffer[offsetY] & ~(1 << bitY);
 48		if (x) {
 49			y2 |= 1 << bitY;
 50		}
 51		buffer[offsetY] = y2;
 52	}
 53	_cbStoreByteswap(buffer, op1, op2);
 54
 55	*op1 ^= cheats->cbSeeds[0];
 56	*op2 ^= cheats->cbSeeds[1];
 57
 58	_cbLoadByteswap(buffer, *op1, *op2);
 59	uint32_t master = cheats->cbMaster;
 60	for (i = 0; i < 5; ++i) {
 61		buffer[i] ^= (master >> 8) ^ buffer[i + 1];
 62	}
 63	buffer[5] ^= master >> 8;
 64
 65	for (i = 5; i > 0; --i) {
 66		buffer[i] ^= master ^ buffer[i - 1];
 67	}
 68	buffer[0] ^= master;
 69	_cbStoreByteswap(buffer, op1, op2);
 70
 71	*op1 ^= cheats->cbSeeds[2];
 72	*op2 ^= cheats->cbSeeds[3];
 73}
 74
 75static uint32_t _cbRand(struct GBACheatSet* cheats) {
 76	// Roll LCG three times to get enough bits of entropy
 77	uint32_t roll = cheats->cbRngState * 0x41C64E6D + 0x3039;
 78	uint32_t roll2 = roll * 0x41C64E6D + 0x3039;
 79	uint32_t roll3 = roll2 * 0x41C64E6D + 0x3039;
 80	uint32_t mix = (roll << 14) & 0xC0000000;
 81	mix |= (roll2 >> 1) & 0x3FFF8000;
 82	mix |= (roll3 >> 16) & 0x7FFF;
 83	cheats->cbRngState = roll3;
 84	return mix;
 85}
 86
 87static size_t _cbSwapIndex(struct GBACheatSet* cheats) {
 88	uint32_t roll = _cbRand(cheats);
 89	uint32_t count = sizeof(cheats->cbTable);
 90
 91	if (roll == count) {
 92		roll = 0;
 93	}
 94
 95	if (roll < count) {
 96		return roll;
 97	}
 98
 99	uint32_t bit = 1;
100
101	while (count < 0x10000000 && count < roll) {
102		count <<= 4;
103		bit <<= 4;
104	}
105
106	while (count < 0x80000000 && count < roll) {
107		count <<= 1;
108		bit <<= 1;
109	}
110
111	uint32_t mask;
112	while (true) {
113		mask = 0;
114		if (roll >= count) {
115			roll -= count;
116		}
117		if (roll >= count >> 1) {
118			roll -= count >> 1;
119			mask |= ROR(bit, 1);
120		}
121		if (roll >= count >> 2) {
122			roll -= count >> 2;
123			mask |= ROR(bit, 2);
124		}
125		if (roll >= count >> 3) {
126			roll -= count >> 3;
127			mask |= ROR(bit, 3);
128		}
129		if (!roll || !(bit >> 4)) {
130			break;
131		}
132		bit >>= 4;
133		count >>= 4;
134	}
135
136	mask &= 0xE0000000;
137	if (!mask || !(bit & 7)) {
138		return roll;
139	}
140
141	if (mask & ROR(bit, 3)) {
142		roll += count >> 3;
143	}
144	if (mask & ROR(bit, 2)) {
145		roll += count >> 2;
146	}
147	if (mask & ROR(bit, 1)) {
148		roll += count >> 1;
149	}
150
151	return roll;
152}
153
154static void _cbReseed(struct GBACheatSet* cheats, uint32_t op1, uint16_t op2) {
155	cheats->cbRngState = (op2 & 0xFF) ^ 0x1111;
156	size_t i;
157	// Populate the initial seed table
158	for (i = 0; i < sizeof(cheats->cbTable); ++i) {
159		cheats->cbTable[i] = i;
160	}
161	// Swap pseudo-random table entries based on the input code
162	for (i = 0; i < 0x50; ++i) {
163		size_t x = _cbSwapIndex(cheats);
164		size_t y = _cbSwapIndex(cheats);
165		uint8_t swap = cheats->cbTable[x];
166		cheats->cbTable[x] = cheats->cbTable[y];
167		cheats->cbTable[y] = swap;
168	}
169
170	// Spin the RNG some to make the initial seed
171	cheats->cbRngState = 0x4EFAD1C3;
172	for (i = 0; i < ((op1 >> 24) & 0xF); ++i) {
173		cheats->cbRngState = _cbRand(cheats);
174	}
175
176	cheats->cbSeeds[2] = _cbRand(cheats);
177	cheats->cbSeeds[3] = _cbRand(cheats);
178
179	cheats->cbRngState = (op2 >> 8) ^ 0xF254;
180	for (i = 0; i < (op2 >> 8); ++i) {
181		cheats->cbRngState = _cbRand(cheats);
182	}
183
184	cheats->cbSeeds[0] = _cbRand(cheats);
185	cheats->cbSeeds[1] = _cbRand(cheats);
186
187	cheats->cbMaster = op1;
188}
189
190bool GBACheatAddCodeBreaker(struct GBACheatSet* cheats, uint32_t op1, uint16_t op2) {
191	char line[14] = "XXXXXXXX XXXX";
192	snprintf(line, sizeof(line), "%08X %04X", op1, op2);
193	GBACheatRegisterLine(cheats, line);
194
195	if (cheats->cbMaster) {
196		_cbDecrypt(cheats, &op1, &op2);
197	}
198
199	enum GBACodeBreakerType type = op1 >> 28;
200	struct GBACheat* cheat = 0;
201
202	if (cheats->incompleteCheat) {
203		cheats->incompleteCheat->repeat = op1 & 0xFFFF;
204		cheats->incompleteCheat->addressOffset = op2;
205		cheats->incompleteCheat->operandOffset = 0;
206		cheats->incompleteCheat = 0;
207		return true;
208	}
209
210	switch (type) {
211	case CB_GAME_ID:
212		// TODO: Run checksum
213		return true;
214	case CB_HOOK:
215		if (cheats->hook) {
216			return false;
217		}
218		cheats->hook = malloc(sizeof(*cheats->hook));
219		cheats->hook->address = BASE_CART0 | (op1 & (SIZE_CART0 - 1));
220		cheats->hook->mode = MODE_THUMB;
221		cheats->hook->refs = 1;
222		cheats->hook->reentries = 0;
223		return true;
224	case CB_OR_2:
225		cheat = GBACheatListAppend(&cheats->list);
226		cheat->type = CHEAT_OR;
227		cheat->width = 2;
228		break;
229	case CB_ASSIGN_1:
230		cheat = GBACheatListAppend(&cheats->list);
231		cheat->type = CHEAT_ASSIGN;
232		cheat->width = 1;
233		break;
234	case CB_FILL:
235		cheat = GBACheatListAppend(&cheats->list);
236		cheat->type = CHEAT_ASSIGN;
237		cheat->width = 2;
238		cheats->incompleteCheat = cheat;
239		break;
240	case CB_FILL_8:
241		mLOG(CHEATS, STUB, "CodeBreaker code %08X %04X not supported", op1, op2);
242		return false;
243	case CB_AND_2:
244		cheat = GBACheatListAppend(&cheats->list);
245		cheat->type = CHEAT_AND;
246		cheat->width = 2;
247		break;
248	case CB_IF_EQ:
249		cheat = GBACheatListAppend(&cheats->list);
250		cheat->type = CHEAT_IF_EQ;
251		cheat->width = 2;
252		break;
253	case CB_ASSIGN_2:
254		cheat = GBACheatListAppend(&cheats->list);
255		cheat->type = CHEAT_ASSIGN;
256		cheat->width = 2;
257		break;
258	case CB_ENCRYPT:
259		_cbReseed(cheats, op1, op2);
260		return true;
261	case CB_IF_NE:
262		cheat = GBACheatListAppend(&cheats->list);
263		cheat->type = CHEAT_IF_NE;
264		cheat->width = 2;
265		break;
266	case CB_IF_GT:
267		cheat = GBACheatListAppend(&cheats->list);
268		cheat->type = CHEAT_IF_GT;
269		cheat->width = 2;
270		break;
271	case CB_IF_LT:
272		cheat = GBACheatListAppend(&cheats->list);
273		cheat->type = CHEAT_IF_LT;
274		cheat->width = 2;
275		break;
276	case CB_IF_SPECIAL:
277		switch (op1 & 0x0FFFFFFF) {
278		case 0x20:
279			cheat = GBACheatListAppend(&cheats->list);
280			cheat->type = CHEAT_IF_AND;
281			cheat->width = 2;
282			cheat->address = BASE_IO | REG_JOYSTAT;
283			cheat->operand = op2;
284			cheat->repeat = 1;
285			return true;
286		default:
287			mLOG(CHEATS, STUB, "CodeBreaker code %08X %04X not supported", op1, op2);
288			return false;
289		}
290	case CB_ADD_2:
291		cheat = GBACheatListAppend(&cheats->list);
292		cheat->type = CHEAT_ADD;
293		cheat->width = 2;
294		break;
295	case CB_IF_AND:
296		cheat = GBACheatListAppend(&cheats->list);
297		cheat->type = CHEAT_IF_AND;
298		cheat->width = 2;
299		break;
300	}
301
302	cheat->address = op1 & 0x0FFFFFFF;
303	cheat->operand = op2;
304	cheat->repeat = 1;
305	cheat->negativeRepeat = 0;
306	return true;
307}
308
309bool GBACheatAddCodeBreakerLine(struct GBACheatSet* cheats, const char* line) {
310	uint32_t op1;
311	uint16_t op2;
312	line = hex32(line, &op1);
313	if (!line) {
314		return false;
315	}
316	while (*line == ' ') {
317		++line;
318	}
319	line = hex16(line, &op2);
320	if (!line) {
321		return false;
322	}
323	return GBACheatAddCodeBreaker(cheats, op1, op2);
324}