先把cheatlib的代码放上去,本地改个版本试试windows上的运行结果,修完bug再更新

This commit is contained in:
anod 2023-01-13 10:07:30 +08:00
parent a2bada407d
commit 22194bdb64
2 changed files with 453 additions and 33 deletions

View File

@ -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_

419
source/arm11/acl.c Normal file
View File

@ -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;
}