Merge pull request #262 from LiquidFenrir/better-dump
add ability to dump all your official themes
This commit is contained in:
2
Makefile
2
Makefile
@@ -89,7 +89,7 @@ CFLAGS := -g -Wall -Wextra -O2 -mword-relocations \
|
|||||||
-ffunction-sections \
|
-ffunction-sections \
|
||||||
$(ARCH)
|
$(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)),)
|
ifneq ($(strip $(CITRA_MODE)),)
|
||||||
CFLAGS += -DCITRA_MODE
|
CFLAGS += -DCITRA_MODE
|
||||||
endif
|
endif
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ typedef enum {
|
|||||||
INSTALL_LOADING_REMOTE_BGM,
|
INSTALL_LOADING_REMOTE_BGM,
|
||||||
|
|
||||||
INSTALL_DUMPING_THEME,
|
INSTALL_DUMPING_THEME,
|
||||||
|
INSTALL_DUMPING_ALL_THEMES,
|
||||||
|
|
||||||
INSTALL_NONE,
|
INSTALL_NONE,
|
||||||
} InstallType;
|
} InstallType;
|
||||||
@@ -84,6 +85,7 @@ typedef enum {
|
|||||||
TEXT_INSTALL_LOADING_REMOTE_BGM,
|
TEXT_INSTALL_LOADING_REMOTE_BGM,
|
||||||
|
|
||||||
TEXT_INSTALL_DUMPING_THEME,
|
TEXT_INSTALL_DUMPING_THEME,
|
||||||
|
TEXT_INSTALL_DUMPING_ALL_THEMES,
|
||||||
|
|
||||||
// Other text
|
// Other text
|
||||||
TEXT_VERSION,
|
TEXT_VERSION,
|
||||||
|
|||||||
@@ -29,6 +29,8 @@
|
|||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
|
#define ILLEGAL_CHARS "><\"?;:/\\+,.|[=]"
|
||||||
|
|
||||||
extern FS_Archive ArchiveSD;
|
extern FS_Archive ArchiveSD;
|
||||||
extern FS_Archive ArchiveHomeExt;
|
extern FS_Archive ArchiveHomeExt;
|
||||||
extern FS_Archive ArchiveThemeExt;
|
extern FS_Archive ArchiveThemeExt;
|
||||||
|
|||||||
@@ -128,11 +128,11 @@ Instructions_s extra_instructions[3] = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"\uE07B Browse ThemePlaza",
|
"\uE07B Browse ThemePlaza",
|
||||||
"\uE07C Dump Current Theme"
|
NULL
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"\uE004 Sorting menu",
|
"\uE004 Sorting menu",
|
||||||
NULL
|
"\uE005 Dumping menu"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Exit",
|
"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
|
#endif
|
||||||
|
|||||||
@@ -76,7 +76,8 @@ Result bgm_install(Entry_s theme);
|
|||||||
|
|
||||||
Result shuffle_install(Entry_List_s themes);
|
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);
|
void themes_check_installed(void * void_arg);
|
||||||
|
|
||||||
|
|||||||
@@ -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_PREVIEW], staticBuf, "Downloading preview, please wait...");
|
||||||
C2D_TextParse(&text[TEXT_INSTALL_LOADING_REMOTE_BGM], staticBuf, "Downloading BGM, 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_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++)
|
for(int i = 0; i < TEXT_AMOUNT; i++)
|
||||||
C2D_TextOptimize(&text[i]);
|
C2D_TextOptimize(&text[i]);
|
||||||
|
|||||||
@@ -353,7 +353,6 @@ void remake_file(FS_Path path, FS_Archive archive, u32 size)
|
|||||||
free(buf);
|
free(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define ILLEGAL_CHARS "><\"?;:/\\+,.|[=]"
|
|
||||||
static SwkbdCallbackResult fat32filter(void *user, const char **ppMessage, const char *text, size_t textlen)
|
static SwkbdCallbackResult fat32filter(void *user, const char **ppMessage, const char *text, size_t textlen)
|
||||||
{
|
{
|
||||||
(void)textlen;
|
(void)textlen;
|
||||||
|
|||||||
@@ -401,8 +401,8 @@ int main(void)
|
|||||||
{
|
{
|
||||||
if(key_l)
|
if(key_l)
|
||||||
index = 0;
|
index = 0;
|
||||||
// else if(key_r) // uncomment when we use the right menu. we don't for now
|
else if(key_r) // uncomment when we use the right menu. we don't for now
|
||||||
// index = 2;
|
index = 2;
|
||||||
}
|
}
|
||||||
instructions = extra_instructions[index];
|
instructions = extra_instructions[index];
|
||||||
}
|
}
|
||||||
@@ -648,13 +648,6 @@ int main(void)
|
|||||||
{
|
{
|
||||||
load_icons_first(current_list, false);
|
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)
|
else if(key_l)
|
||||||
{
|
{
|
||||||
@@ -677,6 +670,23 @@ int main(void)
|
|||||||
load_icons_first(current_list, false);
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
240
source/themes.c
240
source/themes.c
@@ -334,7 +334,7 @@ dir_name_callback(void *data, const char ** ppMessage, const char * text, size_t
|
|||||||
return SWKBD_CALLBACK_OK;
|
return SWKBD_CALLBACK_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result dump_theme(void)
|
Result dump_current_theme(void)
|
||||||
{
|
{
|
||||||
const int max_chars = 255;
|
const int max_chars = 255;
|
||||||
char * output_dir = calloc(max_chars + 1, sizeof(char));
|
char * output_dir = calloc(max_chars + 1, sizeof(char));
|
||||||
@@ -421,6 +421,244 @@ Result dump_theme(void)
|
|||||||
return 0;
|
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)
|
void themes_check_installed(void * void_arg)
|
||||||
{
|
{
|
||||||
Thread_Arg_s * arg = (Thread_Arg_s *)void_arg;
|
Thread_Arg_s * arg = (Thread_Arg_s *)void_arg;
|
||||||
|
|||||||
Reference in New Issue
Block a user