From 22194bdb64bbb02f7d0a85a943c4e05ad0d663dc Mon Sep 17 00:00:00 2001 From: anod <182859762@qq.com> Date: Fri, 13 Jan 2023 10:07:30 +0800 Subject: [PATCH] =?UTF-8?q?=E5=85=88=E6=8A=8Acheatlib=E7=9A=84=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E6=94=BE=E4=B8=8A=E5=8E=BB=EF=BC=8C=E6=9C=AC=E5=9C=B0?= =?UTF-8?q?=E6=94=B9=E4=B8=AA=E7=89=88=E6=9C=AC=E8=AF=95=E8=AF=95windows?= =?UTF-8?q?=E4=B8=8A=E7=9A=84=E8=BF=90=E8=A1=8C=E7=BB=93=E6=9E=9C=EF=BC=8C?= =?UTF-8?q?=E4=BF=AE=E5=AE=8Cbug=E5=86=8D=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/arm11/acl.h | 67 +++---- source/arm11/acl.c | 419 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 453 insertions(+), 33 deletions(-) create mode 100644 source/arm11/acl.c diff --git a/include/arm11/acl.h b/include/arm11/acl.h index 49c4c12..c472bbd 100644 --- a/include/arm11/acl.h +++ b/include/arm11/acl.h @@ -9,60 +9,61 @@ #define ACL_ENTRY_HOLE 1 #define ACL_ENTRY_KEY 2 +#define ACHTLIB_SUCCESS 0 +#define ACHTLIB_NOT_OPEN 1 +#define ACHTLIB_NOT_FOUND 2 +#define ACHTLIB_INVALID 3 +#define ACHTLIB_NOMEM 4 + typedef int acl_error_t; -typedef void * acl_lib_t; -typedef void * acl_list_t; -typedef void * acl_config_t; -typedef void * acl_entry_t; typedef const char* acl_text_t; typedef uint8_t acl_boolean_t; -typedef uint8_t acl_cheatid_t; +typedef uint32_t acl_chtid_t; +typedef uint32_t acl_chtpos_t; +typedef char acl_region_t; typedef uint32_t acl_entryid_t; -typedef uint8_t acl_entrytype_t; -typedef uint16_t acl_entrylen_t; +typedef uint16_t acl_elemlen_t; typedef uint16_t acl_index_t; +typedef uint32_t acl_count_t; typedef uint32_t acl_armcode_t; acl_error_t acl_open_lib( - INPUT(acl_text_t) path_for_cht_file, - OUTPUT(acl_lib_t) library_instance + INPUT(acl_text_t) path_for_cht_file +); + +acl_error_t acl_close_lib( + void ); acl_error_t acl_select_game( - INPUT(acl_lib_t) library_instance, - INPUT(acl_text_t) code_for_game, - INPUT(acl_boolean_t) check_language_for_code, - OUTPUT(acl_list_t) cheat_list_instance + INPUT(acl_text_t) code_for_game, + INPUT(acl_boolean_t) check_language_for_code, + OUTPUT(acl_count_t) cheat_set_count_for_game ); -acl_error_t acl_load_cheat( - INPUT(acl_list_t) cheat_list_instance, - INPUT(acl_cheatid_t) cheat_id, - OUTPUT(acl_config_t) config_instance +acl_error_t acl_query_cheat_set( + INPUT(acl_index_t) cheat_set_index, + OUTPUT(acl_chtid_t) cheat_set_id, + OUTPUT(acl_region_t) cheat_set_region ); -acl_error_t acl_visit_config( - INPUT(acl_config_t) config_instance, - INPUT(acl_entryid_t) entry_id, - OUTPUT(acl_entry_t) entry_instance +acl_error_t acl_select_cheat_set( + INPUT(acl_index_t) cheat_set_id ); -acl_entrytype_t acl_entry_type( - INPUT(acl_entry_t) entry_instance +acl_error_t acl_select_entry( + INPUT(acl_entryid_t) entry_id, + OUTPUT(acl_elemlen_t) entry_element_count ); -acl_entrylen_t acl_entry_length( - INPUT(acl_entry_t) entry_instance +acl_error_t acl_entry_get_label( + INPUT(acl_index_t) index_of_entry_element, + OUTPUT(acl_text_t) label_of_entry_element ); -acl_text_t acl_entry_label( - INPUT(acl_entry_t) entry_instance, - INPUT(acl_index_t) index_of_entry -); - -acl_armcode_t acl_entry_value( - IPUT(acl_entry_t) entry_instance, - INPUT(acl_index_t) index_of_entry +acl_error_t acl_entry_get_armcode( + INPUT(acl_index_t) index_of_entry_element, + OUTPUT(acl_armcode_t) arm_code_of_entry_element ); #endif//_ANOD_CHEAT_LIBRARY_ \ No newline at end of file diff --git a/source/arm11/acl.c b/source/arm11/acl.c new file mode 100644 index 0000000..f48dc17 --- /dev/null +++ b/source/arm11/acl.c @@ -0,0 +1,419 @@ +#include "arm11/acl.h" +#include "fs.h" + +typedef struct +{ + uint32_t typeloc; + uint32_t cfgid; +} CheatCfg; + +typedef struct +{ + FHandle fd; // acl文件 + uint16_t serc; // serial前3字节总数量 + CheatCfg *list; // 调用select_game后保存serial对应的一组cheat配置 + uint16_t listc; // list的个数 + uint32_t setid; // 选中的cheat_set的id +} CheatLib; + +typedef struct +{ + uint32_t entid; // entry的id + uint32_t offset; // entry对应数据在cheat_set的偏移 + uint32_t datasz; // 此entry对应的数据长度(hole对应key数量,key对应gamecode数量) +} CheatEntry; + +typedef struct +{ + uint32_t seek; // 在acl文件中的基础偏移 + uint32_t entc; // 此cheat_set所有的entry总数 + CheatEntry entry; // 当选选中的entry的数据 + char *strings; // 字符串表 + void *entdata; // 当前选中的entry对应的列表数据 +} CheatSet; + +static CheatLib gblcht = {0, 0, NULL, 0, 0}; +static CheatSet gblset = {0, 0, {0,0,0}, NULL}; + +#define ACL_MAGIC_CODE 0x4c4341 +#define ACL_GBA_CODELEN 4 +#define ACL_HEADER_LEN 8 +#define ACL_SERIAL_LEN(n) ((n)*3) +#define ACL_INDEX_OFFSET(i) ((i)*sizeof(uint16_t)) +#define ACL_CONFIG_OFFSET(i) ((i)*sizeof(CheatCfg)) + +#define serial_count(n) ((n) & 0xffff) +#define max_cheat_count(n) ((n) >> 16) + +acl_error_t acl_open_lib( acl_text_t filename ) +{ + FHandle cheatfile; + uint32_t readed; + if( RES_OK != fOpen(&cheatfile, filename, FA_OPEN_EXISTING | FA_READ) ) + return ACHTLIB_NOT_FOUND; + + // 验证magic number + uint32_t num; + if( RES_OK != fRead(cheatfile, &num, sizeof(num), &readed) + || readed != sizeof(num) ) + { + fClose( cheatfile ); + return ACHTLIB_INVALID; + } + + if ( (num & 0xffffff) != ACL_MAGIC_CODE ) + { + fClose( cheatfile ); + return ACHTLIB_INVALID; + } + + if( RES_OK != fRead( cheatfile, &num, sizeof(num), &readed) + || readed != sizeof( num ) ) + { + fClose( cheatfile ); + return ACHTLIB_INVALID; + } + + CheatCfg *cfglist = (CheatCfg*)malloc( max_cheat_count(num) * sizeof(CheatCfg) ); + if( cfglist == NULL ) + { + fClose( cheatfile ); + return ACHTLIB_NOMEM; + } + cfglist[0].cfgid = 0; + + gblcht.fd = cheatfile; + gblcht.serc = serial_count(num); + gblcht.list = cfglist; + + return ACHTLIB_SUCCESS; +} + +acl_error_t acl_close_lib( void ) +{ + if( gblcht.serc != 0 ) + { + fClose( gblcht.fd ); + gblcht.fd = 0; + + free( gblcht.list ); + gblcht.list = NULL; + + gblcht.serc = gblcht.listc = 0; + + if( gblset.strings ){ + free( gblset.strings ); + gblset.strings = NULL; + } + + if( gblset.entdata ){ + free( gblset.entdata ); + gblset.entdata = NULL; + } + + return ACHTLIB_SUCCESS; + } + else return ACHTLIB_INVALID; +} + +static int32_t bin_search( acl_text_t game ) +{ + if( RES_OK != fLseek(gblcht.fd, ACL_HEADER_LEN) ){ + return -ACHTLIB_INVALID; + } + + int size = ACL_SERIAL_LEN(gblcht.serc); + const char *serials = (char*)malloc( size ); + if( serials == NULL ){ + return -ACHTLIB_NOMEM; + } + + // TODO + uint32_t readed; + if( RES_OK != fRead(gblcht.fd, serials, size, &readed) + || readed != size ) + { + free( serials ); + return -ACHTLIB_INVALID; + } + + int start = 0, end = gblcht.serc - 1; + size = ACL_SERIAL_LEN(1); + int found = -1; + while( start < end ) + { + int mid = (start+end) >> 1; + int res = strncmp( game, &serials[ACL_SERIAL_LEN(mid)], size ); + if( res == 0 ) + { + found = mid; + break; + } + else if( res > 0 )// serial < game + { + start = mid + 1; + } + else end = mid - 1; + } + free( serials ); + + if( found >= 0 ) + { + return found; + } + else return -ACHTLIB_NOT_FOUND; +} + +#define config_type(n) ( (n) & 0xff ) +acl_error_t acl_select_game( acl_text_t game, acl_boolean_t filter, acl_count_t *n ) +{ + CheatLib *inst = (CheatLib*)&gblcht; + if( inst->serc == 0 ) return ACHTLIB_NOT_OPEN; + if( strlen(game) != ACL_GBA_CODELEN ) return ACHTLIB_NOT_FOUND; + + int32_t index = bin_search( game ); + if( index < 0 ) return index * -1; + + uint32_t offset = ACL_HEADER_LEN + ACL_SERIAL_LEN(inst->serc); + offset += ACL_INDEX_OFFSET(index); + + uint32_t readed; + uint16_t groups[2]; + if( RES_OK != fLseek(inst->fd, offset) ) + return ACHTLIB_INVALID; + + if( RES_OK != fRead(inst->fd, groups, sizeof(groups), &readed) + || readed != sizeof(groups) ) + return ACHTLIB_INVALID; + + offset = ACL_HEADER_LEN + ACL_SERIAL_LEN(inst->serc) + ACL_INDEX_OFFSET(inst->serc+1); + offset += ACL_CONFIG_OFFSET(groups[0]); + + uint32_t gcount = groups[1] - groups[0]; + if( RES_OK != fLseek(inst->fd, offset) ) + return ACHTLIB_INVALID; + + CheatCfg *cfg = inst->list; + if( RES_OK != fRead(inst->fd, cfg, gcount * sizeof(CheatCfg), &readed) // 可能需要多次fread + || readed != gcount * sizeof(CheatCfg) ) + { + return ACHTLIB_INVALID; + } + + if( filter ) + { + char reg = game[3]; + index = -1; + for( int i=0; i < gcount; ++i ) + { + if( config_type(cfg[i].typeloc) == reg ) + { + if( index < 0 ) index = i; + } + else if( index >= 0 ) + { + gcount = i-index; + memcpy( cfg, cfg+index, gcount * sizeof(CheatCfg) ); + break; + } + } + } + + inst->listc = gcount; + inst->setid = 0; + *n = gcount; + + return ACHTLIB_SUCCESS; +} + +acl_error_t acl_query_cheat_set( acl_index_t index, acl_chtid_t *chtid, acl_region_t *region ) +{ + CheatLib *inst = &gblcht; + if( inst->serc == 0 ) return ACHTLIB_NOT_OPEN; + if( inst->listc == 0 ) return ACHTLIB_INVALID; + + if( index >= inst->listc ) return ACHTLIB_NOT_FOUND; + else + { + CheatCfg *cfg = inst->list; + *chtid = cfg[index].cfgid; + *region = config_type( cfg[index].typeloc ); + return ACHTLIB_SUCCESS; + } +} + +static acl_error_t init_cheatset( uint32_t offset ) +{ + gblset.seek = offset; + if( gblset.strings ) + { + free( gblset.strings ); + gblset.strings = NULL; + } + if( gblset.entdata ) + { + free( gblset.entdata ); + gblset.entdata = NULL; + } + + uint32_t readed; + if( RES_OK != fLseek(gblcht.fd, offset) ) + return ACHTLIB_INVALID; + + uint16_t entc; + if( RES_OK != fRead(gblcht.fd, &entc, sizeof(entc), &readed) + || readed != sizeof( entc ) ) + return ACHTLIB_INVALID; + gblset.entc = entc; + + if( RES_OK != fRead(gblcht.fd, &gblset.entry, sizeof(CheatEntry), &readed) + || readed != sizeof(CheatEntry) ) + return ACHTLIB_INVALID; + + if( gblset.entry.entid != 0 ) return ACHTLIB_INVALID; + offset = gblset.seek + sizeof(entc) + entc*sizeof(CheatEntry); + if( RES_OK != fLseek(gblcht.fd, offset) ) + return ACHTLIB_INVALID; + + uint32_t size = gblset.entry.offset - sizeof(entc) - entc*sizeof(CheatEntry); + char *table = (char*)malloc( size ); + if( !table ) return ACHTLIB_NOMEM; + + if( RES_OK != fRead(gblcht.fd, table, size, &readed) // TODO: 多次read + || readed != size ) + { + free( table ); + return ACHTLIB_INVALID; + } + + gblset.strings = table; + return ACHTLIB_SUCCESS; +} + +static acl_error_t load_data() +{ + if( gblset.entdata != NULL ) + { + free( gblset.entdata ); + gblset.entdata = NULL; + } + + int elemlen = gblset.entry.entid > 0xffff ? sizdof(uint32_t) : sizeof(uint16_t); + int total = gblset.entry.datasz * elemlen; + if( total == 0 ) return ACHTLIB_SUCCESS; + + uint8_t *d = (uint8_t*)malloc( total ); + if( d == NULL ) return ACHTLIB_NOMEM; + + if( RES_OK != fLseek(gblcht.fd, gblset.entry.offset)) + return ACHTLIB_INVALID; + + uint32_t readed; + if( RES_OK != fRead(gblcht.fd, d, total, &readed) + || readed != total ) + { + free( d ); + return ACHTLIB_INVALID; + } + + gblset.entdata = d; + return ACHTLIB_SUCCESS; +} + +#define config_offset(n) ( (n & ~0xff) >> 3 ) + +acl_error_t acl_select_cheat_set( acl_cheatid_t id ) +{ + CheatLib *inst = &gblcht; + if( inst->serc == 0 ) return ACHTLIB_NOT_OPEN; + if( inst->listc == 0 ) return ACHTLIB_INVALID; + + acl_boolean_t found = 0; + uint32_t offset = 0; + CheatCfg *cfg = inst->list; + for( int i=0; i < inst->listc; ++i ) + { + if( cfg[i].cfgid == id ) + { + offset = config_offset( cfg[i].typeloc ); + acl_error_t r = init_cheatset( offset ); + if( r == ACHTLIB_SUCCESS ) + { + inst->setid = id; + break; + } + else return r; + } + } + if( found ) return load_data(); + else return ACHTLIB_NOT_FOUND; +} + +#define READ_CACHE_SIZE 16 +acl_error_t acl_select_entry( acl_entryid_t id, acl_elemlen_t *count) +{ + if( gblcht.serc == 0 ) return ACHTLIB_NOT_OPEN; + if( gblcht.listc == 0 ) return ACHTLIB_INVALID; + if( gblcht.setid == 0 ) return ACHTLIB_INVALID; + + if( gblset.entry.entid != id ) + { + // update gblset.entry + if( RES_OK != fLseek(gblcht.fd, gblset.seek + sizeof(uint16_t)) ) + return ACHTLIB_INVALID; + + CheatEntry cache[READ_CACHE_SIZE]; + uint32_t readed; + for( int i = 0; i < gblset.entc; i += READ_CACHE_SIZE ) + { + if( RES_OK != fRead(gblcht.fd, cache, sizeof(cache), &readed) + || readed != sizeof(cache) ) + return ACHTLIB_INVALID; + + acl_boolean_t found = 0; + for( int j=0; j < READ_CACHE_SIZE; ++j ) + { + if( cache[j].entid == id ) + { + memcpy( &gblset.entry, &cache[i], sizeof(CheatEntry) ); + found = 1; + break; + } + } + if( found ) break; + } + } + *count = gblset.entry.datasz; + + return load_data(); +} + +acl_error_t acl_entry_get_label( acl_index_t index, acl_text_t *label ) +{ + if( gblcht.serc == 0 ) return ACHTLIB_NOT_OPEN; + if( gblcht.listc == 0 ) return ACHTLIB_INVALID; + if( gblcht.setid == 0 ) return ACHTLIB_INVALID; + + if( index < gblset.entry.datasz ) + { + uint16_t *d = (uint16_t*)gblset.entdata; + *label = gblset.strings + d[index]; + return ACHTLIB_SUCCESS; + } + else return ACHTLIB_NOT_FOUND; +} + +acl_error_t acl_entry_get_armcode( acl_index_t index, acl_armcode_t *code ) +{ + if( gblcht.serc == 0 ) return ACHTLIB_NOT_OPEN; + if( gblcht.listc == 0 ) return ACHTLIB_INVALID; + if( gblcht.setid == 0 ) return ACHTLIB_INVALID; + + if( index < gblset.entry.datasz ) + { + uint32_t *d = (uint32_t*)gblset.entdata; + *code = d[index]; + return ACHTLIB_SUCCESS; + } + else return ACHTLIB_NOT_FOUND; +} \ No newline at end of file