mirror of https://github.com/mgba-emu/mgba.git
144 lines
3.6 KiB
C
144 lines
3.6 KiB
C
/* Copyright (c) 2013-2015 Jeffrey Pfau
|
|
*
|
|
* 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
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
#include "gba/cheats.h"
|
|
|
|
#include "gba/cheats/cheats-private.h"
|
|
#include "gba/gba.h"
|
|
#include "gba/io.h"
|
|
#include "util/string.h"
|
|
|
|
bool GBACheatAddCodeBreaker(struct GBACheatSet* cheats, uint32_t op1, uint16_t op2) {
|
|
char line[14] = "XXXXXXXX XXXX";
|
|
snprintf(line, sizeof(line), "%08X %04X", op1, op2);
|
|
GBACheatRegisterLine(cheats, line);
|
|
|
|
enum GBACodeBreakerType type = op1 >> 28;
|
|
struct GBACheat* cheat = 0;
|
|
|
|
if (cheats->incompleteCheat) {
|
|
cheats->incompleteCheat->repeat = op1 & 0xFFFF;
|
|
cheats->incompleteCheat->addressOffset = op2;
|
|
cheats->incompleteCheat->operandOffset = 0;
|
|
cheats->incompleteCheat = 0;
|
|
return true;
|
|
}
|
|
|
|
switch (type) {
|
|
case CB_GAME_ID:
|
|
// TODO: Run checksum
|
|
return true;
|
|
case CB_HOOK:
|
|
if (cheats->hook) {
|
|
return false;
|
|
}
|
|
cheats->hook = malloc(sizeof(*cheats->hook));
|
|
cheats->hook->address = BASE_CART0 | (op1 & (SIZE_CART0 - 1));
|
|
cheats->hook->mode = MODE_THUMB;
|
|
cheats->hook->refs = 1;
|
|
cheats->hook->reentries = 0;
|
|
return true;
|
|
case CB_OR_2:
|
|
cheat = GBACheatListAppend(&cheats->list);
|
|
cheat->type = CHEAT_OR;
|
|
cheat->width = 2;
|
|
break;
|
|
case CB_ASSIGN_1:
|
|
cheat = GBACheatListAppend(&cheats->list);
|
|
cheat->type = CHEAT_ASSIGN;
|
|
cheat->width = 1;
|
|
break;
|
|
case CB_FILL:
|
|
cheat = GBACheatListAppend(&cheats->list);
|
|
cheat->type = CHEAT_ASSIGN;
|
|
cheat->width = 2;
|
|
cheats->incompleteCheat = cheat;
|
|
break;
|
|
case CB_FILL_8:
|
|
GBALog(0, GBA_LOG_STUB, "[Cheat] CodeBreaker code %08X %04X not supported", op1, op2);
|
|
return false;
|
|
case CB_AND_2:
|
|
cheat = GBACheatListAppend(&cheats->list);
|
|
cheat->type = CHEAT_AND;
|
|
cheat->width = 2;
|
|
break;
|
|
case CB_IF_EQ:
|
|
cheat = GBACheatListAppend(&cheats->list);
|
|
cheat->type = CHEAT_IF_EQ;
|
|
cheat->width = 2;
|
|
break;
|
|
case CB_ASSIGN_2:
|
|
cheat = GBACheatListAppend(&cheats->list);
|
|
cheat->type = CHEAT_ASSIGN;
|
|
cheat->width = 2;
|
|
break;
|
|
case CB_ENCRYPT:
|
|
GBALog(0, GBA_LOG_STUB, "[Cheat] CodeBreaker encryption not supported");
|
|
return false;
|
|
case CB_IF_NE:
|
|
cheat = GBACheatListAppend(&cheats->list);
|
|
cheat->type = CHEAT_IF_NE;
|
|
cheat->width = 2;
|
|
break;
|
|
case CB_IF_GT:
|
|
cheat = GBACheatListAppend(&cheats->list);
|
|
cheat->type = CHEAT_IF_GT;
|
|
cheat->width = 2;
|
|
break;
|
|
case CB_IF_LT:
|
|
cheat = GBACheatListAppend(&cheats->list);
|
|
cheat->type = CHEAT_IF_LT;
|
|
cheat->width = 2;
|
|
break;
|
|
case CB_IF_SPECIAL:
|
|
switch (op1 & 0x0FFFFFFF) {
|
|
case 0x20:
|
|
cheat = GBACheatListAppend(&cheats->list);
|
|
cheat->type = CHEAT_IF_AND;
|
|
cheat->width = 2;
|
|
cheat->address = BASE_IO | REG_JOYSTAT;
|
|
cheat->operand = op2;
|
|
cheat->repeat = 1;
|
|
return true;
|
|
default:
|
|
GBALog(0, GBA_LOG_STUB, "[Cheat] CodeBreaker code %08X %04X not supported", op1, op2);
|
|
return false;
|
|
}
|
|
case CB_ADD_2:
|
|
cheat = GBACheatListAppend(&cheats->list);
|
|
cheat->type = CHEAT_ADD;
|
|
cheat->width = 2;
|
|
break;
|
|
case CB_IF_AND:
|
|
cheat = GBACheatListAppend(&cheats->list);
|
|
cheat->type = CHEAT_IF_AND;
|
|
cheat->width = 2;
|
|
break;
|
|
}
|
|
|
|
cheat->address = op1 & 0x0FFFFFFF;
|
|
cheat->operand = op2;
|
|
cheat->repeat = 1;
|
|
cheat->negativeRepeat = 0;
|
|
return true;
|
|
}
|
|
|
|
bool GBACheatAddCodeBreakerLine(struct GBACheatSet* cheats, const char* line) {
|
|
uint32_t op1;
|
|
uint16_t op2;
|
|
line = hex32(line, &op1);
|
|
if (!line) {
|
|
return false;
|
|
}
|
|
while (*line == ' ') {
|
|
++line;
|
|
}
|
|
line = hex16(line, &op2);
|
|
if (!line) {
|
|
return false;
|
|
}
|
|
return GBACheatAddCodeBreaker(cheats, op1, op2);
|
|
}
|