GBA: Almost entirely untested Pro Action Replay v3 code support

This commit is contained in:
Jeffrey Pfau 2015-03-29 04:38:13 -07:00
parent 44d3718eb0
commit 178f9a83bb
5 changed files with 306 additions and 15 deletions

View File

@ -294,6 +294,7 @@ static bool _addGSA1(struct GBACheatSet* cheats, uint32_t op1, uint32_t op2) {
return true;
case GSA_BUTTON:
// TODO: Implement button
GBALog(0, GBA_LOG_STUB, "GameShark button unimplemented");
return false;
case GSA_IF_EQ:
if (op1 == 0xDEADFACE) {
@ -331,14 +332,225 @@ static bool _addGSA1(struct GBACheatSet* cheats, uint32_t op1, uint32_t op2) {
return true;
}
static uint32_t _parAddr(uint32_t x) {
return (x & 0xFFFFF) | ((x << 4) & 0x0F000000);
}
static void _parEndBlock(struct GBACheatSet* cheats) {
size_t size = GBACheatListSize(&cheats->list) - GBACheatListIndex(&cheats->list, cheats->currentBlock);
if (cheats->currentBlock->repeat) {
cheats->currentBlock->negativeRepeat = size - cheats->currentBlock->repeat;
} else {
cheats->currentBlock->repeat = size;
}
cheats->currentBlock = 0;
}
static void _parElseBlock(struct GBACheatSet* cheats) {
size_t size = GBACheatListSize(&cheats->list) - GBACheatListIndex(&cheats->list, cheats->currentBlock);
cheats->currentBlock->repeat = size;
}
static bool _addPAR3Cond(struct GBACheatSet* cheats, uint32_t op1, uint32_t op2) {
enum GBAActionReplay3Condition condition = op1 & PAR3_COND;
int width = 1 << ((op1 & PAR3_WIDTH) >> PAR3_WIDTH_BASE);
if (width > 4) {
// TODO: Always false conditions
return false;
}
if ((op1 & PAR3_ACTION) == PAR3_ACTION_DISABLE) {
// TODO: Codes that disable
return false;
}
struct GBACheat* cheat = GBACheatListAppend(&cheats->list);
cheat->address = _parAddr(op1);
cheat->width = width;
cheat->operand = op2 & (0xFFFFFFFFU >> ((4 - width) * 8));
cheat->addressOffset = 0;
cheat->operandOffset = 0;
switch (op1 & PAR3_ACTION) {
case PAR3_ACTION_NEXT:
cheat->repeat = 1;
cheat->negativeRepeat = 0;
break;
case PAR3_ACTION_NEXT_TWO:
cheat->repeat = 2;
cheat->negativeRepeat = 0;
break;
case PAR3_ACTION_BLOCK:
cheat->repeat = 0;
cheat->negativeRepeat = 0;
if (cheats->currentBlock) {
_parEndBlock(cheats);
}
cheats->currentBlock = cheat;
break;
}
switch (condition) {
case PAR3_COND_OTHER:
// We shouldn't be able to get here
GBALog(0, GBA_LOG_ERROR, "Unexpectedly created 'other' PARv3 code");
cheat->type = CHEAT_IF_LAND;
cheat->operand = 0;
break;
case PAR3_COND_EQ:
cheat->type = CHEAT_IF_EQ;
break;
case PAR3_COND_NE:
cheat->type = CHEAT_IF_NE;
break;
case PAR3_COND_LT:
cheat->type = CHEAT_IF_LT;
break;
case PAR3_COND_GT:
cheat->type = CHEAT_IF_GT;
break;
case PAR3_COND_ULT:
cheat->type = CHEAT_IF_ULT;
break;
case PAR3_COND_UGT:
cheat->type = CHEAT_IF_UGT;
break;
case PAR3_COND_LAND:
cheat->type = CHEAT_IF_LAND;
break;
}
return true;
}
static bool _addPAR3Special(struct GBACheatSet* cheats, uint32_t op2) {
struct GBACheat* cheat;
switch (op2 & 0xFF000000) {
case PAR3_OTHER_SLOWDOWN:
// TODO: Slowdown
return false;
case PAR3_OTHER_BUTTON_1:
case PAR3_OTHER_BUTTON_2:
case PAR3_OTHER_BUTTON_4:
// TODO: Button
GBALog(0, GBA_LOG_STUB, "GameShark button unimplemented");
return false;
// TODO: Fix overriding existing patches
case PAR3_OTHER_PATCH_1:
cheats->romPatches[0].address = (op2 & 0xFFFFFF) << 1;
cheats->romPatches[0].applied = false;
cheats->romPatches[0].exists = true;
cheats->incompletePatch = &cheats->romPatches[0];
break;
case PAR3_OTHER_PATCH_2:
cheats->romPatches[1].address = (op2 & 0xFFFFFF) << 1;
cheats->romPatches[1].applied = false;
cheats->romPatches[1].exists = true;
cheats->incompletePatch = &cheats->romPatches[1];
break;
case PAR3_OTHER_PATCH_3:
cheats->romPatches[2].address = (op2 & 0xFFFFFF) << 1;
cheats->romPatches[2].applied = false;
cheats->romPatches[2].exists = true;
cheats->incompletePatch = &cheats->romPatches[2];
break;
case PAR3_OTHER_PATCH_4:
cheats->romPatches[3].address = (op2 & 0xFFFFFF) << 1;
cheats->romPatches[3].applied = false;
cheats->romPatches[3].exists = true;
cheats->incompletePatch = &cheats->romPatches[3];
break;
case PAR3_OTHER_ENDIF:
if (cheats->currentBlock) {
_parEndBlock(cheats);
return true;
}
return false;
case PAR3_OTHER_ELSE:
if (cheats->currentBlock) {
_parElseBlock(cheats);
return true;
}
return false;
case PAR3_OTHER_FILL_1:
cheat = GBACheatListAppend(&cheats->list);
cheat->address = _parAddr(op2);
cheat->width = 1;
cheats->incompleteCheat = cheat;
break;
case PAR3_OTHER_FILL_2:
cheat = GBACheatListAppend(&cheats->list);
cheat->address = _parAddr(op2);
cheat->width = 2;
cheats->incompleteCheat = cheat;
break;
case PAR3_OTHER_FILL_4:
cheat = GBACheatListAppend(&cheats->list);
cheat->address = _parAddr(op2);
cheat->width = 3;
cheats->incompleteCheat = cheat;
break;
}
return true;
}
static bool _addPAR3(struct GBACheatSet* cheats, uint32_t op1, uint32_t op2) {
// TODO
UNUSED(cheats);
UNUSED(op1);
UNUSED(op2);
UNUSED(_par3T1);
UNUSED(_par3T2);
return false;
if (cheats->incompletePatch) {
cheats->incompletePatch->newValue = op1;
cheats->incompletePatch = 0;
return true;
}
if (cheats->incompleteCheat) {
cheats->incompleteCheat->operand = op1 & (0xFFFFFFFFU >> ((4 - cheats->incompleteCheat->width) * 8));
cheats->incompleteCheat->addressOffset = op2 >> 24;
cheats->incompleteCheat->repeat = (op2 >> 16) & 0xFF;
cheats->incompleteCheat->addressOffset = (op2 & 0xFFFF) * cheats->incompleteCheat->width;
cheats->incompleteCheat = 0;
return true;
}
switch (op1) {
case 0x00000000:
return _addPAR3Special(cheats, op2);
case 0xDEADFACE:
_reseedGameShark(cheats->gsaSeeds, op2, _par3T1, _par3T2);
return true;
}
if (op1 & PAR3_COND) {
return _addPAR3Cond(cheats, op1, op2);
}
int width = 1 << ((op1 & PAR3_WIDTH) >> PAR3_WIDTH_BASE);
struct GBACheat* cheat = GBACheatListAppend(&cheats->list);
cheat->address = _parAddr(op1);
cheat->width = width;
cheat->operand = op2 & (0xFFFFFFFFU >> ((4 - width) * 8));
cheat->operandOffset = 0;
cheat->addressOffset = 0;
cheat->repeat = 1;
switch (op1 & PAR3_BASE) {
case PAR3_BASE_ASSIGN:
cheat->type = CHEAT_ASSIGN;
cheat->addressOffset = width;
if (width < 4) {
cheat->repeat = (op2 >> (width * 8)) + 1;
}
break;
case PAR3_BASE_INDIRECT:
cheat->type = CHEAT_ASSIGN_INDIRECT;
if (width < 4) {
cheat->addressOffset = (op2 >> (width * 8)) * width;
}
break;
case PAR3_BASE_ADD:
cheat->type = CHEAT_ADD;
break;
case PAR3_BASE_OTHER:
cheat->type = CHEAT_ASSIGN;
cheat->address = BASE_IO | (op1 & OFFSET_MASK);
break;
}
return true;
}
static void _addBreakpoint(struct GBACheatDevice* device, struct GBACheatSet* cheats) {
@ -415,6 +627,8 @@ void GBACheatSetInit(struct GBACheatSet* set, const char* name) {
GBACheatListInit(&set->list, 4);
StringListInit(&set->lines, 4);
set->incompleteCheat = 0;
set->incompletePatch = 0;
set->currentBlock = 0;
set->gsaVersion = 0;
set->remainingAddresses = 0;
set->hook = 0;
@ -587,6 +801,7 @@ bool GBACheatAddCodeBreaker(struct GBACheatSet* cheats, uint32_t op1, uint16_t o
cheat->address = op1 & 0x0FFFFFFF;
cheat->operand = op2;
cheat->repeat = 1;
cheat->negativeRepeat = 0;
return true;
}
@ -642,6 +857,41 @@ bool GBACheatAddGameSharkLine(struct GBACheatSet* cheats, const char* line) {
return GBACheatAddGameShark(cheats, op1, op2);
}
bool GBACheatAddProActionReplay(struct GBACheatSet* set, uint32_t op1, uint32_t op2) {
uint32_t o1 = op1;
uint32_t o2 = op2;
char line[18] = "XXXXXXXX XXXXXXXX";
snprintf(line, sizeof(line), "%08X %08X", op1, op2);
_registerLine(set, line);
switch (set->gsaVersion) {
case 0:
_setGameSharkVersion(set, 3);
// Fall through
case 1:
_decryptGameShark(&o1, &o2, set->gsaSeeds);
return _addPAR3(set, o1, o2);
}
return false;
}
bool GBACheatAddProActionReplayLine(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 GBACheatAddProActionReplay(cheats, op1, op2);
}
bool GBACheatAddAutodetect(struct GBACheatSet* set, uint32_t op1, uint32_t op2) {
uint32_t o1 = op1;
uint32_t o2 = op2;
@ -849,6 +1099,7 @@ void GBACheatRefresh(struct GBACheatDevice* device, struct GBACheatSet* cheats)
}
bool condition = true;
int conditionRemaining = 0;
int negativeConditionRemaining = 0;
_patchROM(device, cheats);
size_t nCodes = GBACheatListSize(&cheats->list);
@ -859,6 +1110,13 @@ void GBACheatRefresh(struct GBACheatDevice* device, struct GBACheatSet* cheats)
if (!condition) {
continue;
}
} else if (negativeConditionRemaining > 0) {
conditionRemaining = negativeConditionRemaining - 1;
negativeConditionRemaining = 0;
condition = !condition;
if (!condition) {
continue;
}
} else {
condition = true;
}
@ -874,6 +1132,11 @@ void GBACheatRefresh(struct GBACheatDevice* device, struct GBACheatSet* cheats)
value = operand;
performAssignment = true;
break;
case CHEAT_ASSIGN_INDIRECT:
value = operand;
address = _readMem(device->p->cpu, address + cheat->addressOffset, 4);
performAssignment = true;
break;
case CHEAT_AND:
value = _readMem(device->p->cpu, address, cheat->width) & operand;
performAssignment = true;
@ -889,34 +1152,42 @@ void GBACheatRefresh(struct GBACheatDevice* device, struct GBACheatSet* cheats)
case CHEAT_IF_EQ:
condition = _readMem(device->p->cpu, address, cheat->width) == operand;
conditionRemaining = cheat->repeat;
negativeConditionRemaining = cheat->negativeRepeat;
break;
case CHEAT_IF_NE:
condition = _readMem(device->p->cpu, address, cheat->width) != operand;
conditionRemaining = cheat->repeat;
negativeConditionRemaining = cheat->negativeRepeat;
break;
case CHEAT_IF_LT:
condition = _readMem(device->p->cpu, address, cheat->width) < operand;
conditionRemaining = cheat->repeat;
negativeConditionRemaining = cheat->negativeRepeat;
break;
case CHEAT_IF_GT:
condition = _readMem(device->p->cpu, address, cheat->width) > operand;
conditionRemaining = cheat->repeat;
negativeConditionRemaining = cheat->negativeRepeat;
break;
case CHEAT_IF_ULT:
condition = (uint32_t) _readMem(device->p->cpu, address, cheat->width) < (uint32_t) operand;
conditionRemaining = cheat->repeat;
negativeConditionRemaining = cheat->negativeRepeat;
break;
case CHEAT_IF_UGT:
condition = (uint32_t) _readMem(device->p->cpu, address, cheat->width) > (uint32_t) operand;
conditionRemaining = cheat->repeat;
negativeConditionRemaining = cheat->negativeRepeat;
break;
case CHEAT_IF_AND:
condition = _readMem(device->p->cpu, address, cheat->width) & operand;
conditionRemaining = cheat->repeat;
negativeConditionRemaining = cheat->negativeRepeat;
break;
case CHEAT_IF_LAND:
condition = _readMem(device->p->cpu, address, cheat->width) && operand;
conditionRemaining = cheat->repeat;
negativeConditionRemaining = cheat->negativeRepeat;
break;
}

View File

@ -15,6 +15,7 @@
enum GBACheatType {
CHEAT_ASSIGN,
CHEAT_ASSIGN_INDIRECT,
CHEAT_AND,
CHEAT_ADD,
CHEAT_OR,
@ -85,6 +86,11 @@ enum GBAActionReplay3Action {
};
enum GBAActionReplay3Base {
PAR3_BASE_ASSIGN = 0x00000000,
PAR3_BASE_INDIRECT = 0x40000000,
PAR3_BASE_ADD = 0x80000000,
PAR3_BASE_OTHER = 0xC0000000,
PAR3_BASE_ASSIGN_1 = 0x00000000,
PAR3_BASE_ASSIGN_2 = 0x02000000,
PAR3_BASE_ASSIGN_4 = 0x04000000,
@ -119,7 +125,10 @@ enum GBAActionReplay3Other {
enum {
PAR3_COND = 0x38000000,
PAR3_WIDTH = 0x06000000,
PAR3_ACTION = 0xC0000000
PAR3_ACTION = 0xC0000000,
PAR3_BASE = 0xC0000000,
PAR3_WIDTH_BASE = 25
};
struct GBACheat {
@ -128,6 +137,7 @@ struct GBACheat {
uint32_t address;
uint32_t operand;
uint32_t repeat;
uint32_t negativeRepeat;
int32_t addressOffset;
int32_t operandOffset;
@ -148,8 +158,6 @@ struct GBACheatSet {
struct GBACheatHook* hook;
struct GBACheatList list;
struct GBACheat* incompleteCheat;
struct GBACheatPatch {
uint32_t address;
int16_t newValue;
@ -158,6 +166,10 @@ struct GBACheatSet {
bool exists;
} romPatches[MAX_ROM_PATCHES];
struct GBACheat* incompleteCheat;
struct GBACheatPatch* incompletePatch;
struct GBACheat* currentBlock;
int gsaVersion;
uint32_t gsaSeeds[4];
int remainingAddresses;
@ -196,6 +208,9 @@ 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 GBACheatAddProActionReplay(struct GBACheatSet*, uint32_t op1, uint32_t op2);
bool GBACheatAddProActionReplayLine(struct GBACheatSet*, const char* line);
bool GBACheatAddAutodetect(struct GBACheatSet*, uint32_t op1, uint32_t op2);
bool GBACheatAddAutodetectLine(struct GBACheatSet*, const char* line);
@ -206,4 +221,4 @@ bool GBACheatAddLine(struct GBACheatSet*, const char* line);
void GBACheatRefresh(struct GBACheatDevice*, struct GBACheatSet*);
#endif
#endif

View File

@ -40,6 +40,10 @@ CheatsView::CheatsView(GameController* controller, QWidget* parent)
enterCheat(GBACheatAddGameSharkLine);
});
connect(m_ui.addPAR, &QPushButton::clicked, [this]() {
enterCheat(GBACheatAddProActionReplayLine);
});
connect(m_ui.addCB, &QPushButton::clicked, [this]() {
enterCheat(GBACheatAddCodeBreakerLine);
});
@ -113,4 +117,4 @@ void CheatsView::enterCheat(std::function<bool(GBACheatSet*, const char*)> callb
}
m_controller->threadContinue();
m_ui.codeEntry->clear();
}
}

View File

@ -37,9 +37,6 @@
</item>
<item row="8" column="1" colspan="2">
<widget class="QPushButton" name="addPAR">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Add Pro Action Replay</string>
</property>

View File

@ -24,6 +24,7 @@
void NAME ## Unshift(struct NAME* vector, size_t location, size_t difference); \
void NAME ## EnsureCapacity(struct NAME* vector, size_t capacity); \
size_t NAME ## Size(const struct NAME* vector); \
size_t NAME ## Index(const struct NAME* vector, const TYPE* member);
#define DEFINE_VECTOR(NAME, TYPE) \
void NAME ## Init(struct NAME* vector, size_t capacity) { \
@ -75,5 +76,8 @@
size_t NAME ## Size(const struct NAME* vector) { \
return vector->size; \
} \
size_t NAME ## Index(const struct NAME* vector, const TYPE* member) { \
return member - (const TYPE*) vector->vector; \
} \
#endif