#include "rc_hash.h" #include "rc_runtime.h" #include #include #include typedef struct rc_bsach_header_t { char magic[8]; u32 game_id; u32 console_id; u32 forum_topic_id; u32 flags; u32 is_final; u32 achievement_count; u32 leaderboard_count; u32 reserved1[3]; u8 hash[16]; char title[32]; char publisher[32]; char developer[32]; char genre[32]; char release_date[32]; char console_name[32]; } __packed rc_bsach_header_t; typedef struct rc_achievement_t { u32 id; u32 points; u32 flags; u32 creation_time; u32 modified_time; u32 description_len; u32 mem_addr_len; u32 completion_time; u32 reserved1[8]; char title[32]; char author[32]; char reserved2[128]; char description[256]; char mem_addr[512]; } __packed rc_achievement_t; typedef struct rc_leaderboard_t { u32 id; u32 hidden; u32 lower_is_better; u32 description_len; u32 mem_len; u32 reserved1[11]; char title[32]; char format[32]; char reserved2[128]; char description[256]; char mem[512]; } __packed rc_leaderboard_t; rc_runtime_t runtime; void eventHandler(const rc_runtime_event_t *runtime_event) { iprintf("%d : %d : %d\n", runtime_event->id, runtime_event->value, runtime_event->type); if(runtime_event->type == RC_RUNTIME_EVENT_ACHIEVEMENT_TRIGGERED) { iprintf("!!!\n"); rc_runtime_deactivate_achievement(&runtime, runtime_event->id); } } unsigned peek(unsigned address, unsigned num_bytes, void *ud) { switch(num_bytes) { case 1: return *(u8 *)address; case 2: return *(u16 *)address; case 4: return *(u32 *)address; } return 0; } void error(const char *str) { iprintf("\x1B[41mError:\x1B[47m %s\n", str); } void verbose(const char *str) { iprintf("\x1B[42mVerbose:\x1B[47m %s\n", str); } void register_achievements(const char *path, char *file_hash) { FILE *file = fopen(path, "rb"); if(file) { rc_bsach_header_t header; fread(&header, 1, sizeof(header), file); iprintf("Loading: %s\n", header.title); char hash_str[33]; u8 *d = header.hash; sniprintf(hash_str, 33, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15] ); if(strcmp(hash_str, file_hash) != 0) { iprintf("Invalid hash:\n%s\n\nExpected:\n%s\n", file_hash, hash_str); } for(int i = 0; i < header.achievement_count; i++) { rc_achievement_t achievement; fread(&achievement, 1, sizeof(achievement), file); rc_runtime_activate_achievement(&runtime, achievement.id, achievement.mem_addr, NULL, 0); iprintf("- %s\n", achievement.title); } fclose(file); } } int main(void) { consoleDemoInit(); if(!fatInitDefault()) iprintf("FAT init fail\n"); rc_hash_init_error_message_callback(error); rc_hash_init_verbose_message_callback(verbose); char hash[33]; if(!rc_hash_generate_from_file(hash, RC_CONSOLE_NINTENDO_DS, "sd:/Mario Kart DS.nds")) printf("hash fail\n"); rc_runtime_init(&runtime); register_achievements("sd:/Mario Kart DS.bsach", hash); while(1) { swiWaitForVBlank(); scanKeys(); int pressed = keysDown(); if(pressed & KEY_START) break; rc_runtime_do_frame(&runtime, eventHandler, peek, NULL, NULL); } }