2023-03-07 18:45:37 +08:00

436 lines
14 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "arm11/acl.h"
#include "arm11/cheat.h"
#include "arm11/fmt.h"
#include "util.h"
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
typedef u32 instruction_t;
typedef instruction_t* CodeLocation;
#define CodeAtLocation(p) (*(p))
#define MAKE_ENT(hole,key) (((key)<<16) | (hole))
#define ENT_KEY(id) ((id)>>16)
#define ENT_HOLE(id) ((id)&0xffff)
#define ROM_LOC ((CodeLocation)0x20000000u)
#define GBA_KEYCODE(k) (0x3ff & (~(k)))
#define SIZE_32M (32*1024*1024)
#define MAX_HOOKPOINT 10
typedef struct {
u32 chtId; // acl_chtid_t
u16 entLen; // entArr.length
acl_index_t *entArr; // 下标表示hole值表示key
} CurrentCheat;
static CurrentCheat setting = {0, 0, NULL};
cheat_error_t init_current_cheat( u32 id, u16 len )
{
if( setting.entArr ) fini_current_cheat();
if( id == 0 ) return CCHT_OK;
setting.chtId = id;
setting.entLen = len;
if( len )
{
acl_index_t *p = (acl_index_t*)malloc( len * sizeof(acl_index_t) );
if( p == NULL ) return CCHT_NO_MEM;
memset(p, 0, len*sizeof(acl_index_t));
setting.entArr = p;
}
return CCHT_OK;
}
cheat_error_t put_current_cheat( acl_entryid_t entid )
{
if( setting.chtId == 0 ) return CCHT_NOT_INIT;
u16 index = ENT_HOLE(entid);
u16 option = ENT_KEY(entid);
if( index == 0 ) return CCHT_INVALID;
if( index <= setting.entLen )
{
setting.entArr[index-1] = option;
return CCHT_OK;
}
else return CCHT_INVALID;
}
cheat_error_t info_current_cheat( u32 *id, u32 *len )
{
if( setting.chtId == 0 ) return CCHT_NOT_INIT;
if( id != NULL ) *id = setting.chtId;
if( len != NULL ) *len = setting.entLen;
return CCHT_OK;
}
cheat_error_t get_current_cheat( u32 index, acl_entryid_t *id )
{
if( setting.chtId == 0 ) return CCHT_NOT_INIT;
if( index < setting.entLen )
{
acl_index_t val = setting.entArr[index];
if( id != NULL ) *id = MAKE_ENT( index+1, val );
return CCHT_OK;
}
else return CCHT_INVALID;
}
cheat_error_t include_current_cheat( acl_entryid_t id )
{
if( setting.chtId == 0 ) return CCHT_NOT_INIT;
int index = ENT_HOLE(id);
if( index == 0 ) return CCHT_OK;
else if( index < setting.entLen )
{
acl_index_t val = setting.entArr[index-1];
if( val == ENT_KEY(id) ) return CCHT_OK;
else return CCHT_INVALID;
}
else return CCHT_INVALID;
}
cheat_error_t fini_current_cheat()
{
if( setting.entArr ) free( setting.entArr );
setting.entArr = NULL;
setting.entLen = 0;
setting.chtId = 0;
return CCHT_OK;
}
// *******************************************
// code for patch rom with cheat instruction
// *******************************************
#define GBACPU_PREFETCH 2
#define GBACPU_PREFETCH_BYTE (sizeof(instruction_t)*GBACPU_PREFETCH)
const instruction_t HOOKPOINT_INSTR[] = {
0xe92d8000, // STMDB sp!, {pc}
0xe51ff004, // LDR pc, [pc, #-4]
0
};
#define HP_INSTR_SIZE sizeof(HOOKPOINT_INSTR)
#define HP_INSTR_LEN (HP_INSTR_SIZE/sizeof(instruction_t))
#define HP_WRAPPER_ADDR 2
const instruction_t IRQ_WRAPPER_INSTR[] = {
0xe92d4000, // 0
0xe3a0e402,
0xe28ee701,
0xe24ee004,
0xe90e08ff,
0xeb000000, // 5 - short jump
0xe3a0e402,
0xe28ee701,
0xe24ee028,
0xe89e08ff,
0xe8bd4000, // 10
0, // irq
0,
0,
0xe8bd8000
};
#define IW_INSTR_SIZE sizeof(IRQ_WRAPPER_INSTR)
#define IW_INSTR_LEN (IW_INSTR_SIZE/sizeof(instruction_t))
#define IW_CALL_HANDLER 5
#define IW_ORIGNAL_IRQ 11
const instruction_t KEY_ONOFF_INSTR[] = {
0xe3a00301, // mov r0, #67108864 ; 0x4000000
0xe5900130, // ldr r0, [r0, #304] ; 0x130
0xe1df12b4, // ldrh r1, [pc, #36] ; 0x34
0xe1df22b2, // ldrh r2, [pc, #34] ; 0x36
0xe0000002, // and r0, r0, r2
0xe59f201c, // ldr r2, [pc, #28] ; 0x38
0xe5d23000, // ldrb r3, [r2]
0xe1500001, // cmp r0, r1
0x02233001, // eoreq r3, r3, #1 ; 0x1
0x05c23000, // streqb r3, [r2]
0xe3530000, // cmp r3, #0 ; 0x0
0x1a000002, // bne 0x3c
0xe12fff1e, // bx lr
0x03ff0000, // mask-data and key-data
0 // addr to store on/off flag
};
#define KOO_INSTR_SIZE sizeof(KEY_ONOFF_INSTR)
#define KOO_INSTR_LEN (KOO_INSTR_SIZE/sizeof(instruction_t))
#define KOO_INSTR_KEYDATA 13
#define KOO_INSTR_MEMADDR 14
const instruction_t KEY_ENABLE_INSTR[] = {
0xe3a00301, // mov r0, #67108864 ; 0x4000000
0xe5900130, // ldr r0, [r0, #304] ; 0x130
0xe1df11b0, // ldrh r1, [pc, #16]
0xe1df20be, // ldrh r2, [pc, #14]
0xe0000002, // and r0, r0, r2
0xe1500001, // cmp r0, r1
0x0a000001, // beq 0x24
0xe12fff1e, // bx lr
0x03ff0000 // mask-data and key-data
};
#define KEN_INSTR_SIZE sizeof(KEY_ENABLE_INSTR)
#define KEN_INSTR_LEN (KEN_INSTR_SIZE/sizeof(instruction_t))
#define KEN_INSTR_KEYDATA 8
const instruction_t MEM_OVERWRITE_INSTR[] = {
0xe28f0028, // add r0, pc, #40 ; 0x28
0xe4901004, // ldr r1, [r0], #4
0xe3510000, // cmp r1, #0 ; 0x0
0x0a000006, // beq 0x2c
0xe4902004, // ldr r2, [r0], #4
0xe20230ff, // and r3, r2, #255 ; 0xff
0xe1a02422, // mov r2, r2, lsr #8
0xe4c13001, // strb r3, [r1], #1
0xe2522001, // subs r2, r2, #1 ; 0x1
0x1afffffc, // bne 0x1c
0xeafffff5, // b 0x4
0xe12fff1e // bx lr
};
#define MO_INSTR_SIZE sizeof(MEM_OVERWRITE_INSTR)
#define MO_INSTR_LEN (MO_INSTR_SIZE/sizeof(instruction_t))
static int end_of_rom( CodeLocation addr, u32 size )
{
CodeLocation prom = addr + size/sizeof(instruction_t) - 1;
while( addr < prom && (CodeAtLocation(prom) == 0x00000000 || CodeAtLocation(prom) == 0xffffffff) )
{
prom--;
}
return sizeof(instruction_t) * (prom + 2 - addr); // consider the first 0x00000000/0xffffffff as in-using
}
#define MASK_PC0( m ) (pc[0]&m)
#define MASK_PC1( m ) (pc[1]&m)
#define MASK_PC2( m ) (pc[2]&m)
#define MASK_PC3( m ) (pc[3]&m)
#define MASK_PC4( m ) (pc[4]&m)
#define MASK_PC5( m ) (pc[5]&m)
#define MASK_PC6( m ) (pc[6]&m)
#define MASK_PC7( m ) (pc[7]&m)
#define MASK_PC8( m ) (pc[8]&m)
#define MASK_PC9( m ) (pc[9]&m)
static int rom_search_hookpoint( CodeLocation addr, int addrlen, CodeLocation hookpoint[MAX_HOOKPOINT] )
{
int hookpoint_idx = 0;
for( int i=0; i < addrlen; ++i )
{
CodeLocation pc = addr + i;
if( MASK_PC0(0XFFFF0FFF) == 0XE3A00301 &&
MASK_PC1(0XFFF00FFF) == 0XE2800C02 &&
MASK_PC2(0XFFF00FFF) == 0XE5D00008 &&
MASK_PC2(0XFFFF0000) != 0XE59F0000 )
hookpoint[hookpoint_idx++] = pc;
else
if( MASK_PC0(0XFFFF0FFF) == 0XE3A00301 &&
MASK_PC1(0XFFF00FFF) == 0XE2800C02 &&
MASK_PC2(0XFFF00FFF) == 0XE5900000 &&
MASK_PC2(0XFFFF0000) != 0XE59F0000 )
hookpoint[hookpoint_idx++] = pc;
else
if( MASK_PC0(0XFFFF0000) == 0XE92D0000 &&
MASK_PC1(0XFFFF0FFF) == 0XE3A00301 &&
MASK_PC2(0XFFF00FFF) == 0XE5B00200 &&
MASK_PC3(0XFFFF0000) != 0XE59F0000 )
hookpoint[hookpoint_idx++] = pc;
else
if( MASK_PC0(0xffff0fff) == 0xe3a00640 &&
MASK_PC1(0xfff00fff) == 0xe5b00200 &&
MASK_PC2(0xfff00000) == 0xe1d00000 &&
MASK_PC5(0xffff0000) != 0xe59f0000 &&
MASK_PC6(0xffff0000) != 0xe59f0000 &&
MASK_PC7(0xffff0000) != 0xe59f0000 )
hookpoint[hookpoint_idx++] = pc;
else
if( MASK_PC0(0XFFFF0FFF) == 0XE3A00301 &&
MASK_PC1(0XFFF00FFF) == 0XE5B00200 &&
MASK_PC2(0XFFF00FFF) == 0XE1D000B8 )
hookpoint[hookpoint_idx++] = pc;
else
if( MASK_PC0(0XFFFF0000) == 0XE59F0000 &&
MASK_PC1(0XFFF000FF) == 0XE5900000 &&
MASK_PC2(0XFFFF0000) == 0XE1A00000 &&
MASK_PC1(0XFFFF0000) != 0XE59F0000 &&
MASK_PC3(0XFFFF0000) != 0XE59F0000 )
hookpoint[hookpoint_idx++] = pc;
if( hookpoint_idx >= MAX_HOOKPOINT ) break;
}
return hookpoint_idx;
}
static int cht_calc_needsize( int mode, int numirq )
{
int total = IW_INSTR_SIZE * numirq + MO_INSTR_SIZE;
if( mode == CHEAT_MODE_ENABYKEY )
{
total += KEN_INSTR_SIZE;
}
else if( mode == CHEAT_MODE_KEYONOFF )
{
total += KOO_INSTR_SIZE;
}
// get the cheat data length
for( int i=0; i < setting.entLen; ++i )
{
u16 val = setting.entArr[i];
if( val != 0 )
{
acl_elemlen_t len;
acl_select_entry( MAKE_ENT(i, val), &len );
total += len;
}
}
return total + 1;
}
static CodeLocation rom_fit_newsize( CodeLocation rom, int realend, int totalsize, u32 *newsize )
{
u32 size = realend + totalsize;
if( size > SIZE_32M )
{
// get the biggest space that all bytes is 0
CodeLocation found=NULL, current=NULL;
int szfound=0, szcurrent=0;
for( int i=0; i < realend; ++i )
{
if( rom[i] == 0 )
{
if( szcurrent == 0 ) current = rom+i;
szcurrent++;
}
else if( szcurrent > 0 )
{
if( szcurrent > szfound )
{
found = current;
szfound = szcurrent;
}
current = NULL;
szcurrent = 0;
}
}
// the zero-filled space is big enough, 3 is a guess value
if( szfound > totalsize * 3 )
{
return found + 80;
}
else return NULL;
}
else if( nextPow2(size) != nextPow2(realend) )
{
// 似乎没什么要动的
*newsize = size + 1; // reserve a word for padding
return rom + realend / sizeof(instruction_t);
}
return rom + realend / sizeof(instruction_t);
}
static void rom_patch_hookpoint( CodeLocation start, CodeLocation hookpoint[MAX_HOOKPOINT], int hookcnt )
{
u32 addr = 0x8000000 + (start-ROM_LOC)*sizeof(instruction_t);
for( int i=0; i < hookcnt; ++i )
{
CodeLocation hook_point = hookpoint[i];
memcpy( hook_point, HOOKPOINT_INSTR, HP_INSTR_SIZE );
hook_point[HP_WRAPPER_ADDR] = addr + IW_INSTR_SIZE * i;
}
}
static void rom_append_newirq( CodeLocation start, CodeLocation hookpoint[MAX_HOOKPOINT], int hookcnt )
{
CodeLocation p = start;
u32 offset = IW_INSTR_LEN - IW_CALL_HANDLER - GBACPU_PREFETCH;// 意思是从iw_call_handler到本函数结尾的长度并扣除CPU偏移
for( int i=0; i < hookcnt; ++i )
{
memcpy( p, IRQ_WRAPPER_INSTR, IW_INSTR_SIZE );
memcpy( p+IW_ORIGNAL_IRQ, hookpoint[i], HP_INSTR_SIZE );
p[IW_CALL_HANDLER] |= offset + (hookcnt-1-i) * IW_INSTR_LEN;
p += IW_INSTR_LEN;
}
}
static void rom_append_cheatproc( int mode, CodeLocation start, u16 bindkey, u32 storagemem )
{
if( mode == CHEAT_MODE_KEYONOFF )
{
memcpy( start, KEY_ONOFF_INSTR, KOO_INSTR_SIZE );
start[ KOO_INSTR_KEYDATA ] |= bindkey;
start[ KOO_INSTR_MEMADDR ] = storagemem;
start += KOO_INSTR_LEN;
}
else if( mode == CHEAT_MODE_ENABYKEY )
{
memcpy( start, KEY_ENABLE_INSTR, KEN_INSTR_SIZE );
start[ KEN_INSTR_KEYDATA ] |= bindkey;
start += KEN_INSTR_LEN;
}
memcpy( start, MEM_OVERWRITE_INSTR, MO_INSTR_SIZE );
start += MO_INSTR_LEN;
for( int i=0; i < setting.entLen; ++i )
{
u16 val = setting.entArr[i];
if( val != 0 )
{
acl_elemlen_t len;
acl_select_entry( MAKE_ENT(i+1, val), &len );
for( int j=0; j < len; ++j )
{
acl_entry_get_armcode(j, start++);
}
}
}
*start++ = 0;
}
cheat_error_t apply_cheat( int mode, u32 szrom, u16 bindkey, u32 storagemem, u32 *outsize )
{
// try ignore patch
if( mode == CHEAT_MODE_DISABLED ) return CCHT_OK;
if( setting.chtId == 0 ) return CCHT_OK;
if( ACHTLIB_SUCCESS != acl_select_cheat_set( setting.chtId ) )
return CCHT_NO_CHEAT;
CodeLocation romdata = ROM_LOC;
int realend = end_of_rom(romdata, szrom);
// find hook point
CodeLocation hookpoint[MAX_HOOKPOINT];
memset( hookpoint, 0, sizeof(hookpoint) );
int n_hookpoint = rom_search_hookpoint( romdata, realend/sizeof(u32) - 3, hookpoint ); // hook point need at least 3 instructions
if( n_hookpoint == 0 ) return CCHT_NO_IRQ;
// find free space to put new code
int total_size = cht_calc_needsize( mode, n_hookpoint );
CodeLocation page = rom_fit_newsize( romdata, realend, total_size, outsize );
if( page == NULL ) return CCHT_NO_SPACE;
// patching the rom
rom_append_newirq( page, hookpoint, n_hookpoint );
rom_patch_hookpoint( page, hookpoint, n_hookpoint );
rom_append_cheatproc( mode, page + n_hookpoint * IW_INSTR_LEN, GBA_KEYCODE(bindkey), storagemem );
debug_printf( "patch: mode=%d,size=%ld,hp=%d,key=%d\n", mode, *outsize, n_hookpoint, bindkey );
return CCHT_OK;
}