Adding Code Breaker cheat support
This works on both interpreter and dynarec. Tested in MIPS, ARM and x86, still needs some more testing, some edge cases can be buggy.
This commit is contained in:
parent
52088a4d10
commit
4fd456e158
|
@ -31,6 +31,8 @@ u32 prepare_store_reg(u32 scratch_reg, u32 reg_index);
|
||||||
void generate_load_reg(u32 ireg, u32 reg_index);
|
void generate_load_reg(u32 ireg, u32 reg_index);
|
||||||
void complete_store_reg(u32 scratch_reg, u32 reg_index);
|
void complete_store_reg(u32 scratch_reg, u32 reg_index);
|
||||||
void complete_store_reg_pc_no_flags(u32 scratch_reg, u32 reg_index);
|
void complete_store_reg_pc_no_flags(u32 scratch_reg, u32 reg_index);
|
||||||
|
void thumb_cheat_hook();
|
||||||
|
void arm_cheat_hook();
|
||||||
|
|
||||||
u32 arm_update_gba_arm(u32 pc);
|
u32 arm_update_gba_arm(u32 pc);
|
||||||
u32 arm_update_gba_thumb(u32 pc);
|
u32 arm_update_gba_thumb(u32 pc);
|
||||||
|
@ -1876,6 +1878,12 @@ u32 execute_store_cpsr_body(u32 _cpsr, u32 store_mask, u32 address)
|
||||||
generate_indirect_branch_cycle_update(dual_thumb); \
|
generate_indirect_branch_cycle_update(dual_thumb); \
|
||||||
} \
|
} \
|
||||||
|
|
||||||
|
#define thumb_process_cheats() \
|
||||||
|
generate_function_call(thumb_cheat_hook);
|
||||||
|
|
||||||
|
#define arm_process_cheats() \
|
||||||
|
generate_function_call(arm_cheat_hook);
|
||||||
|
|
||||||
#define thumb_swi() \
|
#define thumb_swi() \
|
||||||
generate_swi_hle_handler(opcode & 0xFF, thumb); \
|
generate_swi_hle_handler(opcode & 0xFF, thumb); \
|
||||||
generate_function_call(execute_swi_thumb); \
|
generate_function_call(execute_swi_thumb); \
|
||||||
|
|
|
@ -288,6 +288,22 @@ arm_update_gba_builder(idle_arm, arm, add)
|
||||||
arm_update_gba_builder(idle_thumb, thumb, add)
|
arm_update_gba_builder(idle_thumb, thumb, add)
|
||||||
|
|
||||||
|
|
||||||
|
@ Cheat hooks for master function
|
||||||
|
@ This is called whenever PC == cheats-master-function
|
||||||
|
@ Just calls the C function to process cheats
|
||||||
|
|
||||||
|
#define cheat_hook_builder(mode) ;\
|
||||||
|
defsymbl(mode##_cheat_hook) ;\
|
||||||
|
save_flags() ;\
|
||||||
|
store_registers_##mode() ;\
|
||||||
|
call_c_function(process_cheats) ;\
|
||||||
|
load_registers_##mode() ;\
|
||||||
|
restore_flags() ;\
|
||||||
|
bx lr ;\
|
||||||
|
|
||||||
|
cheat_hook_builder(arm)
|
||||||
|
cheat_hook_builder(thumb)
|
||||||
|
|
||||||
|
|
||||||
@ These are b stubs for performing indirect branches. They are not
|
@ These are b stubs for performing indirect branches. They are not
|
||||||
@ linked to and don't return, instead they link elsewhere.
|
@ linked to and don't return, instead they link elsewhere.
|
||||||
|
|
545
cheats.c
545
cheats.c
|
@ -19,373 +19,230 @@
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
bool cheat_active;
|
||||||
|
struct {
|
||||||
|
u32 address;
|
||||||
|
u32 value;
|
||||||
|
} codes[MAX_CHEAT_CODES];
|
||||||
|
unsigned cheat_count;
|
||||||
|
} cheat_type;
|
||||||
|
|
||||||
cheat_type cheats[MAX_CHEATS];
|
cheat_type cheats[MAX_CHEATS];
|
||||||
u32 num_cheats;
|
u32 max_cheat = 0;
|
||||||
|
u32 cheat_master_hook = 0xffffffff;
|
||||||
|
|
||||||
void decrypt_gsa_code(u32 *address_ptr, u32 *value_ptr, cheat_variant_enum
|
static void update_hook_codebreaker(cheat_type *cheat)
|
||||||
cheat_variant)
|
|
||||||
{
|
{
|
||||||
u32 i;
|
int i;
|
||||||
u32 address = *address_ptr;
|
for(i = 0; i < cheat->cheat_count; i++)
|
||||||
u32 value = *value_ptr;
|
|
||||||
u32 r = 0xc6ef3720;
|
|
||||||
|
|
||||||
u32 seeds_v1[4] =
|
|
||||||
{
|
{
|
||||||
0x09f4fbbd, 0x9681884a, 0x352027e9, 0xf3dee5a7
|
u32 code = cheat->codes[i].address;
|
||||||
|
u32 address = code & 0xfffffff;
|
||||||
|
u32 opcode = code >> 28;
|
||||||
|
|
||||||
|
if (opcode == 1)
|
||||||
|
{
|
||||||
|
u32 pcaddr = 0x08000000 | (address & 0x1ffffff);
|
||||||
|
#ifdef HAVE_DYNAREC
|
||||||
|
if (cheat_master_hook != pcaddr)
|
||||||
|
init_caches(); /* Flush caches to install hook */
|
||||||
|
#endif
|
||||||
|
cheat_master_hook = pcaddr;
|
||||||
|
return; /* Only support for one hook */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void process_cheat_codebreaker(cheat_type *cheat, u16 pad)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
unsigned j;
|
||||||
|
for(i = 0; i < cheat->cheat_count; i++)
|
||||||
|
{
|
||||||
|
u32 code = cheat->codes[i].address;
|
||||||
|
u16 value = cheat->codes[i].value;
|
||||||
|
u32 address = code & 0xfffffff;
|
||||||
|
u32 opcode = code >> 28;
|
||||||
|
|
||||||
|
switch (opcode) {
|
||||||
|
case 0: /* Game CRC, ignored for now */
|
||||||
|
break;
|
||||||
|
case 1: /* Master code function */
|
||||||
|
break;
|
||||||
|
case 2: /* 16 bit OR */
|
||||||
|
write_memory16(address, read_memory16(address) | value);
|
||||||
|
break;
|
||||||
|
case 3: /* 8 bit write */
|
||||||
|
write_memory8(address, value);
|
||||||
|
break;
|
||||||
|
case 4: /* Slide code, writes a buffer with addr/value strides */
|
||||||
|
if (i + 1 < cheat->cheat_count)
|
||||||
|
{
|
||||||
|
u16 count = cheat->codes[++i].address;
|
||||||
|
u16 vincr = cheat->codes[ i].address >> 16;
|
||||||
|
u16 aincr = cheat->codes[ i].value;
|
||||||
|
for (j = 0; j < count; j++)
|
||||||
|
{
|
||||||
|
write_memory16(address, value);
|
||||||
|
address += aincr;
|
||||||
|
value += vincr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 5: /* Super code: copies bytes to a buffer addr */
|
||||||
|
for (j = 0; j < value * 2 && i < cheat->cheat_count; j++)
|
||||||
|
{
|
||||||
|
u8 bvalue, off = j % 6;
|
||||||
|
switch (off) {
|
||||||
|
case 0:
|
||||||
|
bvalue = cheat->codes[++i].address >> 24;
|
||||||
|
break;
|
||||||
|
case 1 ... 3:
|
||||||
|
bvalue = cheat->codes[i].address >> (24 - off*8);
|
||||||
|
break;
|
||||||
|
case 4 ... 5:
|
||||||
|
bvalue = cheat->codes[i].address >> (40 - off*8);
|
||||||
|
break;
|
||||||
};
|
};
|
||||||
u32 seeds_v3[4] =
|
write_memory8(address, bvalue);
|
||||||
{
|
address++;
|
||||||
0x7aa9648f, 0x7fae6994, 0xc0efaad5, 0x42712c57
|
}
|
||||||
|
break;
|
||||||
|
case 6: /* 16 bit AND */
|
||||||
|
write_memory16(address, read_memory16(address) & value);
|
||||||
|
break;
|
||||||
|
case 7: /* Compare mem value and execute next cheat */
|
||||||
|
if (read_memory16(address) != value)
|
||||||
|
i++;
|
||||||
|
break;
|
||||||
|
case 8: /* 16 bit write */
|
||||||
|
write_memory16(address, value);
|
||||||
|
break;
|
||||||
|
case 10: /* Compare mem value and skip next cheat */
|
||||||
|
if (read_memory16(address) == value)
|
||||||
|
i++;
|
||||||
|
break;
|
||||||
|
case 11: /* Compare mem value and skip next cheat */
|
||||||
|
if (read_memory16(address) <= value)
|
||||||
|
i++;
|
||||||
|
break;
|
||||||
|
case 12: /* Compare mem value and skip next cheat */
|
||||||
|
if (read_memory16(address) >= value)
|
||||||
|
i++;
|
||||||
|
break;
|
||||||
|
case 13: /* Check button state and execute next cheat */
|
||||||
|
switch ((address >> 4) & 0xf) {
|
||||||
|
case 0:
|
||||||
|
if (((~pad) & 0x3ff) == value)
|
||||||
|
i++;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
if ((pad & value) == value)
|
||||||
|
i++;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
if ((pad & value) == 0)
|
||||||
|
i++;
|
||||||
|
break;
|
||||||
};
|
};
|
||||||
u32 *seeds;
|
break;
|
||||||
|
case 14: /* Increase 16/32 bit memory value */
|
||||||
if(cheat_variant == CHEAT_TYPE_GAMESHARK_V1)
|
if (address & 1)
|
||||||
seeds = seeds_v1;
|
|
||||||
else
|
|
||||||
seeds = seeds_v3;
|
|
||||||
|
|
||||||
for(i = 0; i < 32; i++)
|
|
||||||
{
|
{
|
||||||
value -= ((address << 4) + seeds[2]) ^ (address + r) ^
|
u32 value32 = (u32)((s16)value); /* Sign extend to 32 bit */
|
||||||
((address >> 5) + seeds[3]);
|
address &= ~1U;
|
||||||
address -= ((value << 4) + seeds[0]) ^ (value + r) ^
|
write_memory32(address, read_memory32(address) + value32);
|
||||||
((value >> 5) + seeds[1]);
|
|
||||||
r -= 0x9e3779b9;
|
|
||||||
}
|
|
||||||
|
|
||||||
*address_ptr = address;
|
|
||||||
*value_ptr = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void add_cheats(char *cheats_filename)
|
|
||||||
{
|
|
||||||
FILE *cheats_file;
|
|
||||||
char current_line[256];
|
|
||||||
char *name_ptr;
|
|
||||||
u32 *cheat_code_ptr;
|
|
||||||
u32 address, value;
|
|
||||||
u32 num_cheat_lines;
|
|
||||||
u32 cheat_name_length;
|
|
||||||
cheat_variant_enum current_cheat_variant;
|
|
||||||
|
|
||||||
num_cheats = 0;
|
|
||||||
|
|
||||||
cheats_file = fopen(cheats_filename, "rb");
|
|
||||||
|
|
||||||
if(cheats_file)
|
|
||||||
{
|
|
||||||
while(fgets(current_line, 256, cheats_file))
|
|
||||||
{
|
|
||||||
// Get the header line first
|
|
||||||
name_ptr = strchr(current_line, ' ');
|
|
||||||
if(name_ptr)
|
|
||||||
{
|
|
||||||
*name_ptr = 0;
|
|
||||||
name_ptr++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!strcasecmp(current_line, "gameshark_v1") ||
|
|
||||||
!strcasecmp(current_line, "gameshark_v2") ||
|
|
||||||
!strcasecmp(current_line, "PAR_v1") ||
|
|
||||||
!strcasecmp(current_line, "PAR_v2"))
|
|
||||||
{
|
|
||||||
current_cheat_variant = CHEAT_TYPE_GAMESHARK_V1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
|
|
||||||
if(!strcasecmp(current_line, "gameshark_v3") ||
|
|
||||||
!strcasecmp(current_line, "PAR_v3"))
|
|
||||||
{
|
|
||||||
current_cheat_variant = CHEAT_TYPE_GAMESHARK_V3;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
current_cheat_variant = CHEAT_TYPE_INVALID;
|
write_memory16(address, read_memory16(address) + value);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(current_cheat_variant != CHEAT_TYPE_INVALID)
|
|
||||||
{
|
|
||||||
strncpy(cheats[num_cheats].cheat_name, name_ptr, CHEAT_NAME_LENGTH - 1);
|
|
||||||
cheats[num_cheats].cheat_name[CHEAT_NAME_LENGTH - 1] = 0;
|
|
||||||
cheat_name_length = strlen(cheats[num_cheats].cheat_name);
|
|
||||||
if(cheat_name_length &&
|
|
||||||
((cheats[num_cheats].cheat_name[cheat_name_length - 1] == '\n') ||
|
|
||||||
(cheats[num_cheats].cheat_name[cheat_name_length - 1] == '\r')))
|
|
||||||
{
|
|
||||||
cheats[num_cheats].cheat_name[cheat_name_length - 1] = 0;
|
|
||||||
cheat_name_length--;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(cheat_name_length &&
|
|
||||||
cheats[num_cheats].cheat_name[cheat_name_length - 1] == '\r')
|
|
||||||
{
|
|
||||||
cheats[num_cheats].cheat_name[cheat_name_length - 1] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
cheats[num_cheats].cheat_variant = current_cheat_variant;
|
|
||||||
cheat_code_ptr = cheats[num_cheats].cheat_codes;
|
|
||||||
num_cheat_lines = 0;
|
|
||||||
|
|
||||||
while(fgets(current_line, 256, cheats_file))
|
|
||||||
{
|
|
||||||
if(strlen(current_line) < 3)
|
|
||||||
break;
|
break;
|
||||||
|
case 15: /* Immediate and check and skip */
|
||||||
sscanf(current_line, "%08x %08x", &address, &value);
|
if ((read_memory16(address) & value) == 0)
|
||||||
|
|
||||||
decrypt_gsa_code(&address, &value, current_cheat_variant);
|
|
||||||
|
|
||||||
cheat_code_ptr[0] = address;
|
|
||||||
cheat_code_ptr[1] = value;
|
|
||||||
|
|
||||||
cheat_code_ptr += 2;
|
|
||||||
num_cheat_lines++;
|
|
||||||
}
|
|
||||||
|
|
||||||
cheats[num_cheats].num_cheat_lines = num_cheat_lines;
|
|
||||||
|
|
||||||
num_cheats++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose(cheats_file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void process_cheat_gs1(cheat_type *cheat)
|
|
||||||
{
|
|
||||||
u32 cheat_opcode;
|
|
||||||
u32 *code_ptr = cheat->cheat_codes;
|
|
||||||
u32 address, value;
|
|
||||||
u32 i;
|
|
||||||
|
|
||||||
for(i = 0; i < cheat->num_cheat_lines; i++)
|
|
||||||
{
|
|
||||||
address = code_ptr[0];
|
|
||||||
value = code_ptr[1];
|
|
||||||
|
|
||||||
code_ptr += 2;
|
|
||||||
|
|
||||||
cheat_opcode = address >> 28;
|
|
||||||
address &= 0xFFFFFFF;
|
|
||||||
|
|
||||||
switch(cheat_opcode)
|
|
||||||
{
|
|
||||||
case 0x0:
|
|
||||||
write_memory8(address, value);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0x1:
|
|
||||||
write_memory16(address, value);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0x2:
|
|
||||||
write_memory32(address, value);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0x3:
|
|
||||||
{
|
|
||||||
u32 num_addresses = address & 0xFFFF;
|
|
||||||
u32 address1, address2;
|
|
||||||
u32 i2;
|
|
||||||
|
|
||||||
for(i2 = 0; i2 < num_addresses; i2++)
|
|
||||||
{
|
|
||||||
address1 = code_ptr[0];
|
|
||||||
address2 = code_ptr[1];
|
|
||||||
code_ptr += 2;
|
|
||||||
i++;
|
i++;
|
||||||
|
|
||||||
write_memory32(address1, value);
|
|
||||||
if(address2 != 0)
|
|
||||||
write_memory32(address2, value);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ROM patch not supported yet
|
|
||||||
case 0x6:
|
|
||||||
break;
|
|
||||||
|
|
||||||
// GS button down not supported yet
|
|
||||||
case 0x8:
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Reencryption (DEADFACE) not supported yet
|
|
||||||
case 0xD:
|
|
||||||
if(read_memory16(address) != (value & 0xFFFF))
|
|
||||||
{
|
|
||||||
code_ptr += 2;
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0xE:
|
|
||||||
if(read_memory16(value & 0xFFFFFFF) != (address & 0xFFFF))
|
|
||||||
{
|
|
||||||
u32 skip = ((address >> 16) & 0x03);
|
|
||||||
code_ptr += skip * 2;
|
|
||||||
i += skip;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Hook routine not supported yet (not important??)
|
|
||||||
case 0x0F:
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// These are especially incomplete.
|
|
||||||
|
|
||||||
void process_cheat_gs3(cheat_type *cheat)
|
|
||||||
{
|
|
||||||
u32 cheat_opcode;
|
|
||||||
u32 *code_ptr = cheat->cheat_codes;
|
|
||||||
u32 address, value;
|
|
||||||
u32 i;
|
|
||||||
|
|
||||||
for(i = 0; i < cheat->num_cheat_lines; i++)
|
|
||||||
{
|
|
||||||
address = code_ptr[0];
|
|
||||||
value = code_ptr[1];
|
|
||||||
|
|
||||||
code_ptr += 2;
|
|
||||||
|
|
||||||
cheat_opcode = address >> 28;
|
|
||||||
address &= 0xFFFFFFF;
|
|
||||||
|
|
||||||
switch(cheat_opcode)
|
|
||||||
{
|
|
||||||
case 0x0:
|
|
||||||
cheat_opcode = address >> 24;
|
|
||||||
address = (address & 0xFFFFF) + ((address << 4) & 0xF000000);
|
|
||||||
|
|
||||||
switch(cheat_opcode)
|
|
||||||
{
|
|
||||||
case 0x0:
|
|
||||||
{
|
|
||||||
u32 iterations = value >> 24;
|
|
||||||
u32 i2;
|
|
||||||
|
|
||||||
value &= 0xFF;
|
|
||||||
|
|
||||||
for(i2 = 0; i2 <= iterations; i2++, address++)
|
|
||||||
{
|
|
||||||
write_memory8(address, value);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 0x2:
|
|
||||||
{
|
|
||||||
u32 iterations = value >> 16;
|
|
||||||
u32 i2;
|
|
||||||
|
|
||||||
value &= 0xFFFF;
|
|
||||||
|
|
||||||
for(i2 = 0; i2 <= iterations; i2++, address += 2)
|
|
||||||
{
|
|
||||||
write_memory16(address, value);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 0x4:
|
|
||||||
write_memory32(address, value);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0x4:
|
|
||||||
cheat_opcode = address >> 24;
|
|
||||||
address = (address & 0xFFFFF) + ((address << 4) & 0xF000000);
|
|
||||||
|
|
||||||
switch(cheat_opcode)
|
|
||||||
{
|
|
||||||
case 0x0:
|
|
||||||
address = read_memory32(address) + (value >> 24);
|
|
||||||
write_memory8(address, value & 0xFF);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0x2:
|
|
||||||
address = read_memory32(address) + ((value >> 16) * 2);
|
|
||||||
write_memory16(address, value & 0xFFFF);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0x4:
|
|
||||||
address = read_memory32(address);
|
|
||||||
write_memory32(address, value);
|
|
||||||
break;
|
|
||||||
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0x8:
|
|
||||||
cheat_opcode = address >> 24;
|
|
||||||
address = (address & 0xFFFFF) + ((address << 4) & 0xF000000);
|
|
||||||
|
|
||||||
switch(cheat_opcode)
|
|
||||||
{
|
|
||||||
case 0x0:
|
|
||||||
value = (value & 0xFF) + read_memory8(address);
|
|
||||||
write_memory8(address, value);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0x2:
|
|
||||||
value = (value & 0xFFFF) + read_memory16(address);
|
|
||||||
write_memory16(address, value);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0x4:
|
|
||||||
value = value + read_memory32(address);
|
|
||||||
write_memory32(address, value);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0xC:
|
|
||||||
cheat_opcode = address >> 24;
|
|
||||||
address = (address & 0xFFFFFF) + 0x4000000;
|
|
||||||
|
|
||||||
switch(cheat_opcode)
|
|
||||||
{
|
|
||||||
case 0x6:
|
|
||||||
write_memory16(address, value);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0x7:
|
|
||||||
write_memory32(address, value);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void process_cheats(void)
|
void process_cheats(void)
|
||||||
{
|
{
|
||||||
u32 i;
|
u32 i;
|
||||||
|
|
||||||
for(i = 0; i < num_cheats; i++)
|
for(i = 0; i <= max_cheat; i++)
|
||||||
{
|
{
|
||||||
if(cheats[i].cheat_active)
|
if(!cheats[i].cheat_active)
|
||||||
{
|
continue;
|
||||||
switch(cheats[i].cheat_variant)
|
|
||||||
{
|
|
||||||
case CHEAT_TYPE_GAMESHARK_V1:
|
|
||||||
process_cheat_gs1(cheats + i);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CHEAT_TYPE_GAMESHARK_V3:
|
process_cheat_codebreaker(&cheats[i], 0x3ff ^ io_registers[REG_P1]);
|
||||||
process_cheat_gs3(cheats + i);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void cheat_clear()
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < MAX_CHEATS; i++)
|
||||||
|
{
|
||||||
|
cheats[i].cheat_count = 0;
|
||||||
|
cheats[i].cheat_active = false;
|
||||||
|
}
|
||||||
|
cheat_master_hook = 0xffffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cheat_parse(unsigned index, const char *code)
|
||||||
|
{
|
||||||
|
int pos = 0;
|
||||||
|
int codelen = strlen(code);
|
||||||
|
cheat_type *ch = &cheats[index];
|
||||||
|
char buf[1024];
|
||||||
|
|
||||||
|
if (index >= MAX_CHEATS)
|
||||||
|
return;
|
||||||
|
if (codelen >= sizeof(buf))
|
||||||
|
return;
|
||||||
|
|
||||||
|
memcpy(buf, code, codelen+1);
|
||||||
|
|
||||||
|
/* Init to a known good state */
|
||||||
|
ch->cheat_count = 0;
|
||||||
|
if (index > max_cheat)
|
||||||
|
max_cheat = index;
|
||||||
|
|
||||||
|
/* Replace all the non-hex chars to spaces */
|
||||||
|
for (pos = 0; pos < codelen; pos++)
|
||||||
|
if (!((buf[pos] >= '0' && buf[pos] <= '9') ||
|
||||||
|
(buf[pos] >= 'a' && buf[pos] <= 'f') ||
|
||||||
|
(buf[pos] >= 'A' && buf[pos] <= 'F')))
|
||||||
|
buf[pos] = ' ';
|
||||||
|
|
||||||
|
/* Try to parse as Code Breaker */
|
||||||
|
pos = 0;
|
||||||
|
while (pos < codelen)
|
||||||
|
{
|
||||||
|
u32 op1; u16 op2;
|
||||||
|
if (2 != sscanf(&buf[pos], "%08x %04hx", &op1, &op2))
|
||||||
|
break;
|
||||||
|
ch->codes[ch->cheat_count].address = op1;
|
||||||
|
ch->codes[ch->cheat_count++].value = op2;
|
||||||
|
pos += 13;
|
||||||
|
while (pos < codelen && buf[pos] == ' ')
|
||||||
|
pos++;
|
||||||
|
if (ch->cheat_count >= MAX_CHEAT_CODES)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pos >= codelen)
|
||||||
|
{
|
||||||
|
/* All codes were parsed! Process hook here */
|
||||||
|
ch->cheat_active = true;
|
||||||
|
update_hook_codebreaker(ch);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO parse other types here */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
29
cheats.h
29
cheats.h
|
@ -17,28 +17,17 @@
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define CHEAT_NAME_LENGTH 17
|
#ifndef __GPSP_CHEATS_H__
|
||||||
|
#define __GPSP_CHEATS_H__
|
||||||
|
|
||||||
typedef enum
|
#define MAX_CHEATS 20
|
||||||
{
|
#define MAX_CHEAT_CODES 64
|
||||||
CHEAT_TYPE_GAMESHARK_V1,
|
|
||||||
CHEAT_TYPE_GAMESHARK_V3,
|
|
||||||
CHEAT_TYPE_INVALID
|
|
||||||
} cheat_variant_enum;
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
char cheat_name[CHEAT_NAME_LENGTH];
|
|
||||||
u32 cheat_active;
|
|
||||||
u32 cheat_codes[256];
|
|
||||||
u32 num_cheat_lines;
|
|
||||||
cheat_variant_enum cheat_variant;
|
|
||||||
} cheat_type;
|
|
||||||
|
|
||||||
void process_cheats(void);
|
void process_cheats(void);
|
||||||
void add_cheats(char *cheats_filename);
|
void cheat_parse(unsigned index, const char *code);
|
||||||
|
void cheat_clear();
|
||||||
|
|
||||||
#define MAX_CHEATS 16
|
extern u32 cheat_master_hook;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
extern cheat_type cheats[MAX_CHEATS];
|
|
||||||
extern u32 num_cheats;
|
|
||||||
|
|
8
cpu.c
8
cpu.c
|
@ -1679,6 +1679,10 @@ arm_loop:
|
||||||
collapse_flags();
|
collapse_flags();
|
||||||
cycles_per_instruction = global_cycles_per_instruction;
|
cycles_per_instruction = global_cycles_per_instruction;
|
||||||
|
|
||||||
|
/* Process cheats if we are about to execute the cheat hook */
|
||||||
|
if (pc == cheat_master_hook)
|
||||||
|
process_cheats();
|
||||||
|
|
||||||
old_pc = pc;
|
old_pc = pc;
|
||||||
|
|
||||||
/* Execute ARM instruction */
|
/* Execute ARM instruction */
|
||||||
|
@ -3294,6 +3298,10 @@ thumb_loop:
|
||||||
|
|
||||||
collapse_flags();
|
collapse_flags();
|
||||||
|
|
||||||
|
/* Process cheats if we are about to execute the cheat hook */
|
||||||
|
if (pc == cheat_master_hook)
|
||||||
|
process_cheats();
|
||||||
|
|
||||||
old_pc = pc;
|
old_pc = pc;
|
||||||
|
|
||||||
/* Execute THUMB instruction */
|
/* Execute THUMB instruction */
|
||||||
|
|
|
@ -3303,6 +3303,11 @@ s32 translate_block_arm(u32 pc, translation_region_type
|
||||||
block_data[block_data_position].block_offset = translation_ptr;
|
block_data[block_data_position].block_offset = translation_ptr;
|
||||||
arm_base_cycles();
|
arm_base_cycles();
|
||||||
|
|
||||||
|
if (pc == cheat_master_hook)
|
||||||
|
{
|
||||||
|
arm_process_cheats();
|
||||||
|
}
|
||||||
|
|
||||||
translate_arm_instruction();
|
translate_arm_instruction();
|
||||||
block_data_position++;
|
block_data_position++;
|
||||||
|
|
||||||
|
@ -3502,6 +3507,11 @@ s32 translate_block_thumb(u32 pc, translation_region_type
|
||||||
block_data[block_data_position].block_offset = translation_ptr;
|
block_data[block_data_position].block_offset = translation_ptr;
|
||||||
thumb_base_cycles();
|
thumb_base_cycles();
|
||||||
|
|
||||||
|
if (pc == cheat_master_hook)
|
||||||
|
{
|
||||||
|
thumb_process_cheats();
|
||||||
|
}
|
||||||
|
|
||||||
translate_thumb_instruction();
|
translate_thumb_instruction();
|
||||||
block_data_position++;
|
block_data_position++;
|
||||||
|
|
||||||
|
|
|
@ -2380,7 +2380,6 @@ char gamepak_filename[512];
|
||||||
|
|
||||||
u32 load_gamepak(const struct retro_game_info* info, const char *name)
|
u32 load_gamepak(const struct retro_game_info* info, const char *name)
|
||||||
{
|
{
|
||||||
char cheats_filename[256];
|
|
||||||
char *p;
|
char *p;
|
||||||
|
|
||||||
s32 file_size = load_gamepak_raw(name);
|
s32 file_size = load_gamepak_raw(name);
|
||||||
|
@ -2423,9 +2422,6 @@ u32 load_gamepak(const struct retro_game_info* info, const char *name)
|
||||||
if ((load_game_config_over(gamepak_title, gamepak_code, gamepak_maker)) == -1)
|
if ((load_game_config_over(gamepak_title, gamepak_code, gamepak_maker)) == -1)
|
||||||
load_game_config(gamepak_title, gamepak_code, gamepak_maker);
|
load_game_config(gamepak_title, gamepak_code, gamepak_maker);
|
||||||
|
|
||||||
change_ext(gamepak_filename, cheats_filename, ".cht");
|
|
||||||
add_cheats(cheats_filename);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
10
libretro.c
10
libretro.c
|
@ -639,8 +639,16 @@ bool retro_unserialize(const void* data, size_t size)
|
||||||
|
|
||||||
void retro_cheat_reset(void)
|
void retro_cheat_reset(void)
|
||||||
{
|
{
|
||||||
|
cheat_clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void retro_cheat_set(unsigned index, bool enabled, const char* code)
|
||||||
|
{
|
||||||
|
if (!enabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
cheat_parse(index, code);
|
||||||
}
|
}
|
||||||
void retro_cheat_set(unsigned index, bool enabled, const char* code) {}
|
|
||||||
|
|
||||||
static void extract_directory(char* buf, const char* path, size_t size)
|
static void extract_directory(char* buf, const char* path, size_t size)
|
||||||
{
|
{
|
||||||
|
|
2
main.c
2
main.c
|
@ -230,6 +230,8 @@ u32 update_gba(void)
|
||||||
update_gbc_sound(cpu_ticks);
|
update_gbc_sound(cpu_ticks);
|
||||||
gbc_sound_update = 0;
|
gbc_sound_update = 0;
|
||||||
|
|
||||||
|
/* If there's no cheat hook, run on vblank! */
|
||||||
|
if (cheat_master_hook == ~0U)
|
||||||
process_cheats();
|
process_cheats();
|
||||||
|
|
||||||
vcount = 0;
|
vcount = 0;
|
||||||
|
|
|
@ -44,6 +44,7 @@ void mips_indirect_branch_dual(u32 address);
|
||||||
u32 execute_read_cpsr();
|
u32 execute_read_cpsr();
|
||||||
u32 execute_read_spsr();
|
u32 execute_read_spsr();
|
||||||
void execute_swi(u32 pc);
|
void execute_swi(u32 pc);
|
||||||
|
void mips_cheat_hook();
|
||||||
|
|
||||||
u32 execute_spsr_restore(u32 address);
|
u32 execute_spsr_restore(u32 address);
|
||||||
void execute_store_cpsr(u32 new_cpsr, u32 store_mask);
|
void execute_store_cpsr(u32 new_cpsr, u32 store_mask);
|
||||||
|
@ -2422,6 +2423,12 @@ u32 execute_store_cpsr_body(u32 _cpsr, u32 store_mask, u32 address)
|
||||||
generate_indirect_branch_cycle_update(dual); \
|
generate_indirect_branch_cycle_update(dual); \
|
||||||
} \
|
} \
|
||||||
|
|
||||||
|
#define thumb_process_cheats() \
|
||||||
|
generate_function_call(mips_cheat_hook);
|
||||||
|
|
||||||
|
#define arm_process_cheats() \
|
||||||
|
generate_function_call(mips_cheat_hook);
|
||||||
|
|
||||||
#ifdef TRACE_INSTRUCTIONS
|
#ifdef TRACE_INSTRUCTIONS
|
||||||
void trace_instruction(u32 pc)
|
void trace_instruction(u32 pc)
|
||||||
{
|
{
|
||||||
|
|
|
@ -44,6 +44,7 @@
|
||||||
.global init_emitter
|
.global init_emitter
|
||||||
.global mips_lookup_pc
|
.global mips_lookup_pc
|
||||||
.global smc_write
|
.global smc_write
|
||||||
|
.global mips_cheat_hook
|
||||||
|
|
||||||
.global write_io_epilogue
|
.global write_io_epilogue
|
||||||
.global memory_map_read
|
.global memory_map_read
|
||||||
|
@ -256,6 +257,17 @@ mips_update_gba:
|
||||||
nop
|
nop
|
||||||
|
|
||||||
|
|
||||||
|
# Processes cheats whenever we hit the master PC
|
||||||
|
mips_cheat_hook:
|
||||||
|
sw $ra, REG_SAVE2($16)
|
||||||
|
save_registers
|
||||||
|
cfncall process_cheats, 8
|
||||||
|
lw $ra, REG_SAVE2($16)
|
||||||
|
restore_registers
|
||||||
|
jr $ra
|
||||||
|
nop
|
||||||
|
|
||||||
|
|
||||||
# Loads the main context and returns to it.
|
# Loads the main context and returns to it.
|
||||||
# ARM regs must be saved before branching here
|
# ARM regs must be saved before branching here
|
||||||
return_to_main:
|
return_to_main:
|
||||||
|
@ -649,6 +661,7 @@ fnptrs:
|
||||||
.long set_cpu_mode # 5
|
.long set_cpu_mode # 5
|
||||||
.long execute_spsr_restore_body # 6
|
.long execute_spsr_restore_body # 6
|
||||||
.long execute_store_cpsr_body # 7
|
.long execute_store_cpsr_body # 7
|
||||||
|
.long process_cheats # 8
|
||||||
|
|
||||||
#if !defined(HAVE_MMAP)
|
#if !defined(HAVE_MMAP)
|
||||||
|
|
||||||
|
|
|
@ -2236,6 +2236,12 @@ static void function_cc execute_swi(u32 pc)
|
||||||
generate_indirect_branch_cycle_update(dual); \
|
generate_indirect_branch_cycle_update(dual); \
|
||||||
} \
|
} \
|
||||||
|
|
||||||
|
#define thumb_process_cheats() \
|
||||||
|
generate_function_call(process_cheats);
|
||||||
|
|
||||||
|
#define arm_process_cheats() \
|
||||||
|
generate_function_call(process_cheats);
|
||||||
|
|
||||||
#define thumb_swi() \
|
#define thumb_swi() \
|
||||||
generate_swi_hle_handler(opcode & 0xFF); \
|
generate_swi_hle_handler(opcode & 0xFF); \
|
||||||
generate_update_pc((pc + 2)); \
|
generate_update_pc((pc + 2)); \
|
||||||
|
|
Loading…
Reference in New Issue