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 6b59ce8..e988ad8 100644 --- a/include/themes.h +++ b/include/themes.h @@ -76,7 +76,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 fb2eaf4..63b095e 100644 --- a/source/themes.c +++ b/source/themes.c @@ -334,7 +334,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)); @@ -421,6 +421,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;