From 4e2bea53c1a8d68bdfe2893761432e81d452d5bc Mon Sep 17 00:00:00 2001 From: LiquidFenrir Date: Tue, 21 Dec 2021 23:58:03 +0100 Subject: [PATCH] add ability to dump all your official themes icon and name get extracted from the dlc data requires libctru from commit 5f13628dac75206f0c97d29a7427ce8284d910f1 or older (added the am commands necessary to find the dlc data) Makefile changed to reflect the macro change in unreleased libctru --- Makefile | 2 +- include/draw.h | 2 + include/fs.h | 2 + include/instructions.h | 25 ++++- include/themes.h | 3 +- source/draw.c | 1 + source/fs.c | 1 - source/main.c | 28 +++-- source/themes.c | 240 ++++++++++++++++++++++++++++++++++++++++- 9 files changed, 289 insertions(+), 15 deletions(-) diff --git a/Makefile b/Makefile index b2fd36f..8a0c2c9 100644 --- a/Makefile +++ b/Makefile @@ -89,7 +89,7 @@ CFLAGS := -g -Wall -Wextra -O2 -mword-relocations \ -ffunction-sections \ $(ARCH) -CFLAGS += $(INCLUDE) -DARM11 -D_3DS -D_GNU_SOURCE -DVERSION="\"$(VERSION)\"" -DUSER_AGENT="\"$(APP_TITLE)/$(VERSION)\"" -DAPP_TITLE="\"$(APP_TITLE)\"" +CFLAGS += $(INCLUDE) -D__3DS__ -D_GNU_SOURCE -DVERSION="\"$(VERSION)\"" -DUSER_AGENT="\"$(APP_TITLE)/$(VERSION)\"" -DAPP_TITLE="\"$(APP_TITLE)\"" ifneq ($(strip $(CITRA_MODE)),) CFLAGS += -DCITRA_MODE endif diff --git a/include/draw.h b/include/draw.h index bbbcf1e..d4a51cd 100644 --- a/include/draw.h +++ b/include/draw.h @@ -56,6 +56,7 @@ typedef enum { INSTALL_LOADING_REMOTE_BGM, INSTALL_DUMPING_THEME, + INSTALL_DUMPING_ALL_THEMES, INSTALL_NONE, } InstallType; @@ -84,6 +85,7 @@ typedef enum { TEXT_INSTALL_LOADING_REMOTE_BGM, TEXT_INSTALL_DUMPING_THEME, + TEXT_INSTALL_DUMPING_ALL_THEMES, // Other text TEXT_VERSION, diff --git a/include/fs.h b/include/fs.h index 3078c2a..93ab316 100644 --- a/include/fs.h +++ b/include/fs.h @@ -29,6 +29,8 @@ #include "common.h" +#define ILLEGAL_CHARS "><\"?;:/\\+,.|[=]" + extern FS_Archive ArchiveSD; extern FS_Archive ArchiveHomeExt; extern FS_Archive ArchiveThemeExt; diff --git a/include/instructions.h b/include/instructions.h index 58f13f2..5d7cc36 100644 --- a/include/instructions.h +++ b/include/instructions.h @@ -128,11 +128,11 @@ Instructions_s extra_instructions[3] = { }, { "\uE07B Browse ThemePlaza", - "\uE07C Dump Current Theme" + NULL }, { "\uE004 Sorting menu", - NULL + "\uE005 Dumping menu" }, { "Exit", @@ -140,6 +140,27 @@ Instructions_s extra_instructions[3] = { } } }, + { + .info_line = "Release \uE002 to cancel or hold \uE006 and release \uE002 to dump", + .instructions = { + { + "\uE079 Dump Current Theme", + "\uE07A Dump All Themes" + }, + { + NULL, + NULL + }, + { + NULL, + NULL + }, + { + "Exit", + NULL + } + } + } }; #endif diff --git a/include/themes.h b/include/themes.h index dcb0031..7cd1a40 100644 --- a/include/themes.h +++ b/include/themes.h @@ -75,7 +75,8 @@ Result bgm_install(Entry_s theme); Result shuffle_install(Entry_List_s themes); -Result dump_theme(void); +Result dump_current_theme(void); +Result dump_all_themes(void); void themes_check_installed(void * void_arg); diff --git a/source/draw.c b/source/draw.c index 53803ce..165d42a 100644 --- a/source/draw.c +++ b/source/draw.c @@ -126,6 +126,7 @@ void init_screens(void) C2D_TextParse(&text[TEXT_INSTALL_LOADING_REMOTE_PREVIEW], staticBuf, "Downloading preview, please wait..."); C2D_TextParse(&text[TEXT_INSTALL_LOADING_REMOTE_BGM], staticBuf, "Downloading BGM, please wait..."); C2D_TextParse(&text[TEXT_INSTALL_DUMPING_THEME], staticBuf, "Dumping theme, please wait..."); + C2D_TextParse(&text[TEXT_INSTALL_DUMPING_ALL_THEMES], staticBuf, "Dumping official themes, please wait..."); for(int i = 0; i < TEXT_AMOUNT; i++) C2D_TextOptimize(&text[i]); diff --git a/source/fs.c b/source/fs.c index abc302c..c71de7a 100644 --- a/source/fs.c +++ b/source/fs.c @@ -353,7 +353,6 @@ void remake_file(FS_Path path, FS_Archive archive, u32 size) free(buf); } -#define ILLEGAL_CHARS "><\"?;:/\\+,.|[=]" static SwkbdCallbackResult fat32filter(void *user, const char **ppMessage, const char *text, size_t textlen) { (void)textlen; diff --git a/source/main.c b/source/main.c index aed495b..2b5e405 100644 --- a/source/main.c +++ b/source/main.c @@ -401,8 +401,8 @@ int main(void) { if(key_l) index = 0; - // else if(key_r) // uncomment when we use the right menu. we don't for now - // index = 2; + else if(key_r) // uncomment when we use the right menu. we don't for now + index = 2; } instructions = extra_instructions[index]; } @@ -648,13 +648,6 @@ int main(void) { load_icons_first(current_list, false); } - else if((kDown | kHeld) & KEY_DRIGHT) - { - draw_install(INSTALL_DUMPING_THEME); - Result res = dump_theme(); - if (R_FAILED(res)) DEBUG("Dump theme result: %lx\n", res); - else load_lists(lists); - } } else if(key_l) { @@ -677,6 +670,23 @@ int main(void) load_icons_first(current_list, false); } } + else if(key_r) + { + if(((kDown | kHeld)) & KEY_DUP) + { + draw_install(INSTALL_DUMPING_THEME); + Result res = dump_current_theme(); + if (R_FAILED(res)) DEBUG("Dump theme result: %lx\n", res); + else load_lists(lists); + } + else if(((kDown | kHeld)) & KEY_DDOWN) + { + draw_install(INSTALL_DUMPING_ALL_THEMES); + Result res = dump_all_themes(); + if (R_FAILED(res)) DEBUG("Dump all themes result: %lx\n", res); + else load_lists(lists); + } + } } continue; } diff --git a/source/themes.c b/source/themes.c index 6d28503..2179793 100644 --- a/source/themes.c +++ b/source/themes.c @@ -328,7 +328,7 @@ dir_name_callback(void *data, const char ** ppMessage, const char * text, size_t return SWKBD_CALLBACK_OK; } -Result dump_theme(void) +Result dump_current_theme(void) { const int max_chars = 255; char * output_dir = calloc(max_chars + 1, sizeof(char)); @@ -415,6 +415,244 @@ Result dump_theme(void) return 0; } +Result dump_all_themes(void) +{ + const u32 high_id = 0x0004008c; + u32 low_id = 0; + u8 regionCode, language; + Result res = CFGU_SecureInfoGetRegion(®ionCode); + if(R_FAILED(res)) + return res; + + res = CFGU_GetSystemLanguage(&language); + if(R_FAILED(res)) + return res; + + switch(regionCode) + { + case CFG_REGION_JPN: + low_id = 0x00008200; + break; + case CFG_REGION_USA: + low_id = 0x00008f00; + break; + case CFG_REGION_EUR: + low_id = 0x00009800; + break; + default: + return -1; + } + + const char* region_arr[4] = { + "JPN", + "USA", + "EUR", + "AUS", + }; + + const char* language_arr[12] = { + "jp", + "en", + "fr", + "de", + "it", + "es", + "zh", + "ko", + "nl", + "pt", + "ru", + "tw", + }; + + res = amAppInit(); + if(R_FAILED(res)) + return res; + + Icon_s* smdh_data = calloc(1, sizeof(Icon_s)); + smdh_data->_padding1[0] = 0x53; // SMDH magic + smdh_data->_padding1[1] = 0x4d; + smdh_data->_padding1[2] = 0x44; + smdh_data->_padding1[3] = 0x48; + + utf8_to_utf16(smdh_data->author, (u8*)"Nintendo", 0x40); + utf8_to_utf16(smdh_data->desc, (u8*)"Official theme. For personal use only. Do not redistribute.", 0x80); + + for(u32 dlc_index = 0; dlc_index <= 0xFF; ++dlc_index) + { + const u64 titleId = ((u64)high_id << 32) | (low_id | dlc_index); + + u32 count = 0; + res = AMAPP_GetDLCContentInfoCount(&count, MEDIATYPE_SD, titleId); + if(res == (Result)0xd8a083fa) + { + res = 0; + break; + } + else if(R_FAILED(res)) + { + break; + } + + AM_ContentInfo* contentInfos = calloc(count, sizeof(AM_ContentInfo)); + u32 readcount = 0; + res = AMAPP_ListDLCContentInfos(&readcount, MEDIATYPE_SD, titleId, count, 0, contentInfos); + if(R_FAILED(res)) + { + break; + } + + u32 archivePath[4] = {low_id | dlc_index, high_id, MEDIATYPE_SD, 0}; + FS_Path ncch_path; + ncch_path.type = PATH_BINARY; + ncch_path.size = 0x10; + ncch_path.data = archivePath; + + FS_Archive ncch_archive; + res = FSUSER_OpenArchive(&ncch_archive, ARCHIVE_SAVEDATA_AND_CONTENT, ncch_path); + if(R_FAILED(res)) + { + free(contentInfos); + break; + } + + u32 metadataPath[5] = {0, 0, 0, 0, 0}; + FS_Path metadata_path; + metadata_path.type = PATH_BINARY; + metadata_path.size = 0x14; + metadata_path.data = metadataPath; + + Handle metadata_fh; + res = FSUSER_OpenFile(&metadata_fh, ncch_archive, metadata_path, FS_OPEN_READ, 0); + if(R_FAILED(res)) + { + FSUSER_CloseArchive(ncch_archive); + free(contentInfos); + break; + } + + res = romfsMountFromFile(metadata_fh, 0, "meta"); + if(R_FAILED(res)) + { + FSFILE_Close(metadata_fh); + FSUSER_CloseArchive(ncch_archive); + free(contentInfos); + break; + } + + char contentinfoarchive_path[40] = {0}; + sprintf(contentinfoarchive_path, "meta:/ContentInfoArchive_%s_%s.bin", region_arr[regionCode], language_arr[language]); + + FILE* fh = fopen(contentinfoarchive_path, "rb"); + + for(u32 i = 0; i < readcount; ++i) + { + if(i == 0) continue; + AM_ContentInfo* content = &contentInfos[i]; + if((content->flags & (AM_CONTENT_DOWNLOADED | AM_CONTENT_OWNED)) == (AM_CONTENT_DOWNLOADED | AM_CONTENT_OWNED)) + { + long off = 0x8 + 0xC8 * i; + fseek(fh, off, SEEK_SET); + char content_data[0xc8] = {0}; + fread(content_data, 1, 0xc8, fh); + u32 extra_index = 0; + memcpy(&extra_index, content_data + 0xC0, 4); + + metadataPath[1] = content->index; + + Handle theme_fh; + res = FSUSER_OpenFile(&theme_fh, ncch_archive, metadata_path, FS_OPEN_READ, 0); + if(R_FAILED(res)) + { + DEBUG("theme open romfs error: %08lx\n", res); + fclose(fh); + free(contentInfos); + romfsUnmount("meta"); + FSUSER_CloseArchive(ncch_archive); + free(contentInfos); + break; + } + + romfsMountFromFile(theme_fh, 0, "theme"); + + char themename[0x41] = {0}; + memcpy(themename, content_data, 0x40); + char * illegal_char = themename; + while ((illegal_char = strpbrk(illegal_char, ILLEGAL_CHARS))) + { + *illegal_char = '-'; + } + + char path[0x107] = { 0 }; + sprintf(path, "/Themes/Dump-%02lx-%ld-%s", dlc_index, extra_index, themename); + DEBUG("theme folder to create: %s\n", path); + FSUSER_CreateDirectory(ArchiveSD, fsMakePath(PATH_ASCII, path), FS_ATTRIBUTE_DIRECTORY); + + memset(smdh_data->name, 0, sizeof(smdh_data->name)); + utf8_to_utf16(smdh_data->name, (u8*)(content_data + 0), 0x40); + + FILE* theme_file = fopen("theme:/body_LZ.bin", "rb"); + if(theme_file) + { + fseek(theme_file, 0, SEEK_END); + long theme_size = ftell(theme_file); + fseek(theme_file, 0, SEEK_CUR); + char* theme_data = malloc(theme_size); + fread(theme_data, 1, theme_size, theme_file); + fclose(theme_file); + + char themepath[0x107] = {0}; + sprintf(themepath, "%s/body_LZ.bin", path); + remake_file(fsMakePath(PATH_ASCII, themepath), ArchiveSD, theme_size); + buf_to_file(theme_size, fsMakePath(PATH_ASCII, themepath), ArchiveSD, theme_data); + free(theme_data); + } + + FILE* bgm_file = fopen("theme:/bgm.bcstm", "rb"); + if(bgm_file) + { + fseek(bgm_file, 0, SEEK_END); + long bgm_size = ftell(bgm_file); + fseek(bgm_file, 0, SEEK_CUR); + char* bgm_data = malloc(bgm_size); + fread(bgm_data, 1, bgm_size, bgm_file); + fclose(bgm_file); + + char bgmpath[0x107] = {0}; + sprintf(bgmpath, "%s/bgm.bcstm", path); + remake_file(fsMakePath(PATH_ASCII, bgmpath), ArchiveSD, bgm_size); + buf_to_file(bgm_size, fsMakePath(PATH_ASCII, bgmpath), ArchiveSD, bgm_data); + free(bgm_data); + } + + romfsUnmount("theme"); + char icondatapath[0x107] = {0}; + sprintf(icondatapath, "meta:/icons/%ld.icn", extra_index); + FILE* iconfile = fopen(icondatapath, "rb"); + fread(smdh_data->big_icon, 1, sizeof(smdh_data->big_icon), iconfile); + fclose(iconfile); + + strcat(path, "/info.smdh"); + remake_file(fsMakePath(PATH_ASCII, path), ArchiveSD, 0x36c0); + buf_to_file(0x36c0, fsMakePath(PATH_ASCII, path), ArchiveSD, (char*)smdh_data); + } + } + + fclose(fh); + fh = NULL; + free(contentInfos); + contentInfos = NULL; + + romfsUnmount("meta"); + // don't need to close the file opened for the metadata, romfsUnmount took ownership + FSUSER_CloseArchive(ncch_archive); + } + + free(smdh_data); + amExit(); + return res; +} + void themes_check_installed(void * void_arg) { Thread_Arg_s * arg = (Thread_Arg_s *)void_arg;