2 Commits

Author SHA1 Message Date
Dylan G
dc21e4d24b Patched a memory leak (I think) (thanks DeltaV) 2022-05-22 15:40:48 +01:00
Dylan G
9a51667546 Handle the case where the server does not provide Content-Disposition by getting the filename from the URL.
Currently untested.
2022-05-22 15:33:20 +01:00
9 changed files with 187 additions and 257 deletions

View File

@@ -1,6 +1,6 @@
![# Anemone3DS](https://github.com/astronautlevel2/Anemone3DS/blob/master/meta/banner.png) ![# Anemone3DS](https://github.com/astronautlevel2/Anemone3DS/blob/master/meta/banner.png)
A Theme and Splashscreen Manager for the Nintendo 3DS, written in C. A Theme and Splashscreen Manager for the Nintendo 3DS, written in C.\
# Dependencies # Dependencies
* devkitARM, which can be installed following the instructions [here](https://devkitpro.org/wiki/Getting_Started). * devkitARM, which can be installed following the instructions [here](https://devkitpro.org/wiki/Getting_Started).

View File

@@ -46,10 +46,9 @@ typedef struct {
u32 filesize; u32 filesize;
volatile bool stop; volatile bool stop;
Thread playing_thread; Handle finished;
} audio_s; } audio_s;
void play_audio(audio_s *); void play_audio(audio_s *);
void stop_audio(audio_s**);
#endif #endif

View File

@@ -1,88 +0,0 @@
/*
* This file is part of Anemone3DS
* Copyright (C) 2016-2020 Contributors in CONTRIBUTORS.md
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
* * Requiring preservation of specified reasonable legal notices or
* author attributions in that material or in the Appropriate Legal
* Notices displayed by works containing it.
* * Prohibiting misrepresentation of the origin of that material,
* or requiring that modified versions of such material be marked in
* reasonable ways as different from the original version.
*/
static char from_hex(char c)
{
return isdigit(c) ? c - '0' : tolower(c) - 'a' + 10;
}
static char to_hex(char code)
{
static char hex[] = "0123456789abcdef";
return hex[code & 15];
}
// ensure caller frees
char * url_escape(char * url)
{
char * ptr = url;
char * buf = malloc(3 * strlen(url) + 1);
char * ptr_buf = buf;
while (*ptr)
{
if (isalnum((int)*ptr) || strchr("-_.~", *ptr))
*ptr_buf++ = *ptr;
else if (*ptr == ' ')
*ptr_buf++ = '+';
else
{
*ptr_buf++ = '%';
*ptr_buf++ = to_hex(*ptr >> 4);
*ptr_buf++ = to_hex(*ptr & 15);
}
ptr++;
}
*ptr_buf = '\0';
return buf;
}
// ensure caller frees
char * url_unescape(char * url)
{
char * ptr = url;
char * buf = malloc(strlen(url) + 1);
char * ptr_buf = buf;
while (*ptr)
{
if (*ptr == '%')
{
if (ptr[1] && ptr[2])
{
*ptr_buf++ = from_hex(ptr[1]) << 4 | from_hex(ptr[2]);
ptr += 2;
}
}
else if (*ptr == '+')
*ptr_buf++ = ' ';
else
*ptr_buf++ = *ptr;
ptr++;
}
*ptr_buf = '\0';
return buf;
}

View File

@@ -377,7 +377,7 @@ renamed:
sprintf(path_to_file, "%s%s", main_paths[mode], filename); sprintf(path_to_file, "%s%s", main_paths[mode], filename);
// filter out characters illegal in FAT32 filenames // filter out characters illegal in FAT32 filenames
char * curr_filename = path_to_file + strlen(main_paths[mode]); char * curr_filename = strrchr(path_to_file, '/') + 1;
char * illegal_char = curr_filename; char * illegal_char = curr_filename;
while ((illegal_char = strpbrk(illegal_char, ILLEGAL_CHARS))) while ((illegal_char = strpbrk(illegal_char, ILLEGAL_CHARS)))
{ {

View File

@@ -608,6 +608,7 @@ Result load_audio(Entry_s entry, audio_s *audio)
} }
audio->mix[0] = audio->mix[1] = 1.0f; // Determines volume for the 12 (?) different outputs. See http://smealum.github.io/ctrulib/channel_8h.html#a30eb26f1972cc3ec28370263796c0444 audio->mix[0] = audio->mix[1] = 1.0f; // Determines volume for the 12 (?) different outputs. See http://smealum.github.io/ctrulib/channel_8h.html#a30eb26f1972cc3ec28370263796c0444
svcCreateEvent(&audio->finished, RESET_STICKY);
ndspChnSetInterp(0, NDSP_INTERP_LINEAR); ndspChnSetInterp(0, NDSP_INTERP_LINEAR);
ndspChnSetMix(0, audio->mix); // See mix comment above ndspChnSetMix(0, audio->mix); // See mix comment above

View File

@@ -37,7 +37,7 @@
bool quit = false; bool quit = false;
bool dspfirm = false; bool dspfirm = false;
static audio_s * audio = NULL; audio_s * audio = NULL;
static bool homebrew = false; static bool homebrew = false;
static bool installed_themes = false; static bool installed_themes = false;
@@ -109,12 +109,6 @@ static void stop_install_check(void)
{ {
installCheckThreads_arg[i].run_thread = false; installCheckThreads_arg[i].run_thread = false;
} }
for(int i = 0; i < MODE_AMOUNT; i++)
{
threadJoin(installCheckThreads[i], U64_MAX);
threadFree(installCheckThreads[i]);
installCheckThreads[i] = NULL;
}
} }
static void exit_thread(void) static void exit_thread(void)
@@ -127,7 +121,6 @@ static void exit_thread(void)
svcWaitSynchronization(update_icons_mutex, U64_MAX); svcWaitSynchronization(update_icons_mutex, U64_MAX);
threadJoin(iconLoadingThread, U64_MAX); threadJoin(iconLoadingThread, U64_MAX);
threadFree(iconLoadingThread); threadFree(iconLoadingThread);
iconLoadingThread = NULL;
} }
} }
@@ -145,7 +138,6 @@ static void free_icons(Entry_List_s * list)
free(list->icons[i]); free(list->icons[i]);
} }
free(list->icons); free(list->icons);
list->icons = NULL;
} }
void free_lists(void) void free_lists(void)
@@ -165,7 +157,8 @@ void exit_function(bool power_pressed)
{ {
if(audio) if(audio)
{ {
stop_audio(&audio); audio->stop = true;
svcWaitSynchronization(audio->finished, U64_MAX);
} }
free_lists(); free_lists();
svcCloseHandle(update_icons_mutex); svcCloseHandle(update_icons_mutex);
@@ -237,7 +230,7 @@ static void load_lists(Entry_List_s * lists)
if(install_check_function != NULL) if(install_check_function != NULL)
{ {
installCheckThreads[i] = threadCreate(install_check_function, current_arg, __stacksize__, 0x3f, -2, false); installCheckThreads[i] = threadCreate(install_check_function, current_arg, __stacksize__, 0x3f, -2, true);
svcSleepThread(1e8); svcSleepThread(1e8);
} }
} }
@@ -511,7 +504,9 @@ int main(void)
preview_mode = false; preview_mode = false;
if(current_mode == MODE_THEMES && audio) if(current_mode == MODE_THEMES && audio)
{ {
stop_audio(&audio); audio->stop = true;
svcWaitSynchronization(audio->finished, U64_MAX);
audio = NULL;
} }
} }
continue; continue;
@@ -521,7 +516,9 @@ int main(void)
preview_mode = false; preview_mode = false;
if(current_mode == MODE_THEMES && audio) if(current_mode == MODE_THEMES && audio)
{ {
stop_audio(&audio); audio->stop = true;
svcWaitSynchronization(audio->finished, U64_MAX);
audio = NULL;
} }
continue; continue;
} }

View File

@@ -69,26 +69,17 @@ void thread_audio(void* data) {
while(!audio->stop) { while(!audio->stop) {
update_audio(audio); update_audio(audio);
} }
ndspChnWaveBufClear(0);
ndspChnReset(0);
ov_clear(&audio->vf);
free(audio->filebuf); free(audio->filebuf);
ov_clear(&audio->vf);
linearFree((void*)audio->wave_buf[0].data_vaddr); linearFree((void*)audio->wave_buf[0].data_vaddr);
linearFree((void*)audio->wave_buf[1].data_vaddr); linearFree((void*)audio->wave_buf[1].data_vaddr);
while (audio->wave_buf[0].status != NDSP_WBUF_DONE || audio->wave_buf[1].status != NDSP_WBUF_DONE) svcSleepThread(1e7);
svcSignalEvent(audio->finished);
svcSleepThread(1e8);
svcCloseHandle(audio->finished);
free(audio);
} }
void play_audio(audio_s *audio) { void play_audio(audio_s *audio) {
audio->playing_thread = threadCreate(thread_audio, audio, 0x1000, 0x3F, 1, false); threadCreate(thread_audio, audio, 0x1000, 0x3F, 1, true);
}
void stop_audio(audio_s** audio_ptr) {
audio_s* audio = *audio_ptr;
if(audio->playing_thread)
{
audio->stop = true;
threadJoin(audio->playing_thread, U64_MAX);
threadFree(audio->playing_thread);
}
free(audio);
*audio_ptr = NULL;
} }

View File

@@ -31,11 +31,6 @@
#include "fs.h" #include "fs.h"
#include "unicode.h" #include "unicode.h"
#include "music.h" #include "music.h"
#include "urls.h"
// forward declaration of special case used only here
// TODO: replace this travesty with a proper handler
static Result http_get_with_not_found_flag(const char * url, char ** filename, char ** buf, u32 * size, InstallType install_type, const char * acceptable_mime_types, bool not_found_is_error);
static Instructions_s browser_instructions[MODE_AMOUNT] = { static Instructions_s browser_instructions[MODE_AMOUNT] = {
{ {
@@ -331,13 +326,10 @@ static void load_remote_bgm(Entry_s * entry)
draw_install(INSTALL_LOADING_REMOTE_BGM); draw_install(INSTALL_LOADING_REMOTE_BGM);
Result res = http_get_with_not_found_flag(bgm_url, NULL, &bgm_ogg, &bgm_size, INSTALL_LOADING_REMOTE_BGM, "application/ogg, audio/ogg", false); Result res = http_get(bgm_url, NULL, &bgm_ogg, &bgm_size, INSTALL_LOADING_REMOTE_BGM, "application/ogg, audio/ogg");
free(bgm_url); free(bgm_url);
if (R_FAILED(res)) if (R_FAILED(res))
return; return;
// if bgm doesn't exist on the server
if (R_SUMMARY(res) == RS_NOTFOUND && R_MODULE(res) == RM_FILE_SERVER)
return;
u16 path[0x107] = { 0 }; u16 path[0x107] = { 0 };
strucat(path, entry->path); strucat(path, entry->path);
@@ -444,11 +436,18 @@ static void search_menu(Entry_List_s * list)
if (button == SWKBD_BUTTON_CONFIRM) if (button == SWKBD_BUTTON_CONFIRM)
{ {
free(list->tp_search); free(list->tp_search);
list->tp_search = url_escape(search); for (unsigned int i = 0; i < strlen(search); i++)
DEBUG("Search escaped: %s -> %s\n", search, list->tp_search); {
if (search[i] == ' ')
search[i] = '+';
}
list->tp_search = search;
load_remote_list(list, 1, list->mode, false); load_remote_list(list, 1, list->mode, false);
} }
free(search); else
{
free(search);
}
} }
static void change_selected(Entry_List_s * list, int change_value) static void change_selected(Entry_List_s * list, int change_value)
@@ -490,7 +489,7 @@ bool themeplaza_browser(EntryMode mode)
bool extra_mode = false; bool extra_mode = false;
while (aptMainLoop() && !quit) while (aptMainLoop())
{ {
if (current_list->entries == NULL) if (current_list->entries == NULL)
break; break;
@@ -518,6 +517,12 @@ bool themeplaza_browser(EntryMode mode)
exit: exit:
quit = true; quit = true;
downloaded = false; downloaded = false;
if (audio)
{
audio->stop = true;
svcWaitSynchronization(audio->finished, U64_MAX);
audio = NULL;
}
break; break;
} }
@@ -576,7 +581,9 @@ bool themeplaza_browser(EntryMode mode)
preview_mode = false; preview_mode = false;
if (mode == MODE_THEMES && audio != NULL) if (mode == MODE_THEMES && audio != NULL)
{ {
stop_audio(&audio); audio->stop = true;
svcWaitSynchronization(audio->finished, U64_MAX);
audio = NULL;
} }
} }
} }
@@ -587,7 +594,9 @@ bool themeplaza_browser(EntryMode mode)
preview_mode = false; preview_mode = false;
if (mode == MODE_THEMES && audio != NULL) if (mode == MODE_THEMES && audio != NULL)
{ {
stop_audio(&audio); audio->stop = true;
svcWaitSynchronization(audio->finished, U64_MAX);
audio = NULL;
} }
} }
else else
@@ -653,7 +662,9 @@ bool themeplaza_browser(EntryMode mode)
preview_mode = false; preview_mode = false;
if (mode == MODE_THEMES && audio) if (mode == MODE_THEMES && audio)
{ {
stop_audio(&audio); audio->stop = true;
svcWaitSynchronization(audio->finished, U64_MAX);
audio = NULL;
} }
continue; continue;
} }
@@ -720,11 +731,6 @@ bool themeplaza_browser(EntryMode mode)
} }
} }
if (audio)
{
stop_audio(&audio);
}
free_preview(preview); free_preview(preview);
free_icons(current_list); free_icons(current_list);
@@ -764,19 +770,46 @@ typedef enum ParseResult
HTTP_GATEWAY_TIMEOUT = 504, HTTP_GATEWAY_TIMEOUT = 504,
} ParseResult; } ParseResult;
/*static SwkbdCallbackResult fat32filter(void *user, const char **ppMessage, const char *text, size_t textlen) // unescapes URL-encoded string non-destructively
static char * unescape(const char * url)
{ {
(void)textlen; char * unescaped = malloc(strlen(url) + 1);
(void)user; // source shamelessly ripped from https://stackoverflow.com/a/14530993/4073959
*ppMessage = "Input must not contain:\n><\"?;:/\\+,.|[=]"; char a;
if(strpbrk(text, "><\"?;:/\\+,.|[=]")) char b;
while (*url)
{ {
DEBUG("illegal filename: %s\n", text); if ((*url == '%') &&
return SWKBD_CALLBACK_CONTINUE; ((a = url[1]) && (b = url[2])) &&
(isxdigit(a) && isxdigit(b))) {
if (a >= 'a')
a -= 'a' - 'A';
if (a >= 'A')
a -= ('A' - 10);
else
a -= '0';
if (b >= 'a')
b -= 'a' - 'A';
if (b >= 'A')
b -= ('A' - 10);
else
b -= '0';
*unescaped++ = 16 * a + b;
url += 3;
}
else if (*url == '+')
{
*unescaped++ = ' ';
url++;
}
else
{
*unescaped++ = *url++;
}
} }
*unescaped++ = '\0';
return SWKBD_CALLBACK_OK; return unescaped;
}*/ }
// the good paths for this function return SUCCESS, ABORTED, or REDIRECT; // the good paths for this function return SUCCESS, ABORTED, or REDIRECT;
// all other paths are failures // all other paths are failures
@@ -894,11 +927,6 @@ static ParseResult parse_header(struct header * out, httpcContext * context, con
* call example: written = http_get("url", &filename, &buffer_to_download_to, &filesize, INSTALL_DOWNLOAD, "application/json"); * call example: written = http_get("url", &filename, &buffer_to_download_to, &filesize, INSTALL_DOWNLOAD, "application/json");
*/ */
Result http_get(const char * url, char ** filename, char ** buf, u32 * size, InstallType install_type, const char * acceptable_mime_types) Result http_get(const char * url, char ** filename, char ** buf, u32 * size, InstallType install_type, const char * acceptable_mime_types)
{
return http_get_with_not_found_flag(url, filename, buf, size, install_type, acceptable_mime_types, true);
}
static Result http_get_with_not_found_flag(const char * url, char ** filename, char ** buf, u32 * size, InstallType install_type, const char * acceptable_mime_types, bool not_found_is_error)
{ {
Result ret; Result ret;
httpcContext context; httpcContext context;
@@ -935,7 +963,6 @@ redirect: // goto here if we need to redirect
#define ERROR_BUFFER_SIZE 0x80 #define ERROR_BUFFER_SIZE 0x80
char err_buf[ERROR_BUFFER_SIZE]; char err_buf[ERROR_BUFFER_SIZE];
Result res;
ParseResult parse = parse_header(&_header, &context, acceptable_mime_types); ParseResult parse = parse_header(&_header, &context, acceptable_mime_types);
switch (parse) switch (parse)
{ {
@@ -981,10 +1008,7 @@ redirect: // goto here if we need to redirect
snprintf(err_buf, ERROR_BUFFER_SIZE, ZIP_NOT_AVAILABLE); snprintf(err_buf, ERROR_BUFFER_SIZE, ZIP_NOT_AVAILABLE);
goto error; goto error;
case HTTP_NOT_FOUND: case HTTP_NOT_FOUND:
if (!not_found_is_error) case HTTP_GONE: ;
goto not_found_non_error;
[[fallthrough]];
case HTTP_GONE:
const char * http_error = parse == HTTP_NOT_FOUND ? "404 Not Found" : "410 Gone"; const char * http_error = parse == HTTP_NOT_FOUND ? "404 Not Found" : "410 Gone";
DEBUG("HTTP %s; URL: %s\n", http_error, url); DEBUG("HTTP %s; URL: %s\n", http_error, url);
if (strstr(url, THEMEPLAZA_BASE_URL) && parse == HTTP_NOT_FOUND) if (strstr(url, THEMEPLAZA_BASE_URL) && parse == HTTP_NOT_FOUND)
@@ -1047,13 +1071,9 @@ redirect: // goto here if we need to redirect
goto no_error; goto no_error;
error: error:
throw_error(err_buf, ERROR_LEVEL_WARNING); throw_error(err_buf, ERROR_LEVEL_WARNING);
res = httpcCloseContext(&context); Result res = httpcCloseContext(&context);
if (R_FAILED(res)) return res; if (R_FAILED(res)) return res;
return MAKERESULT(RL_TEMPORARY, RS_CANCELED, RM_APPLICATION, RD_NO_DATA); return MAKERESULT(RL_TEMPORARY, RS_CANCELED, RM_APPLICATION, RD_NO_DATA);
not_found_non_error:
res = httpcCloseContext(&context);
if (R_FAILED(res)) return res;
return MAKERESULT(RL_SUCCESS, RS_NOTFOUND, RM_FILE_SERVER, RD_NO_DATA);
no_error:; no_error:;
u32 chunk_size; u32 chunk_size;
if (_header.file_size) if (_header.file_size)
@@ -1112,6 +1132,15 @@ no_error:;
*buf = new_buf; *buf = new_buf;
DEBUG("size: %lu\n", *size); DEBUG("size: %lu\n", *size);
if (filename) { DEBUG("filename: %s\n", *filename); } if (filename) {
if (*filename == NULL)
{
// Content-Disposition extraction failed somehow
char * tmp = unescape(url);
*filename = strdup(basename(tmp));
free(tmp);
}
DEBUG("filename: %s\n", *filename);
}
return MAKERESULT(RL_SUCCESS, RS_SUCCESS, RM_APPLICATION, RD_SUCCESS); return MAKERESULT(RL_SUCCESS, RS_SUCCESS, RM_APPLICATION, RD_SUCCESS);
} }

View File

@@ -505,7 +505,6 @@ Result dump_all_themes(void)
res = AMAPP_ListDLCContentInfos(&readcount, MEDIATYPE_SD, titleId, count, 0, contentInfos); res = AMAPP_ListDLCContentInfos(&readcount, MEDIATYPE_SD, titleId, count, 0, contentInfos);
if(R_FAILED(res)) if(R_FAILED(res))
{ {
free(contentInfos);
break; break;
} }
@@ -552,105 +551,107 @@ Result dump_all_themes(void)
FILE* fh = fopen(contentinfoarchive_path, "rb"); FILE* fh = fopen(contentinfoarchive_path, "rb");
if(fh != NULL) for(u32 i = 0; i < readcount; ++i)
{ {
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))
{ {
if(i == 0) continue; long off = 0x8 + 0xC8 * i;
AM_ContentInfo* content = &contentInfos[i]; fseek(fh, off, SEEK_SET);
if((content->flags & (AM_CONTENT_DOWNLOADED | AM_CONTENT_OWNED)) == (AM_CONTENT_DOWNLOADED | AM_CONTENT_OWNED)) 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))
{ {
long off = 0x8 + 0xC8 * i; DEBUG("theme open romfs error: %08lx\n", res);
fseek(fh, off, SEEK_SET); fclose(fh);
char content_data[0xc8] = {0}; free(contentInfos);
fread(content_data, 1, 0xc8, fh); romfsUnmount("meta");
u32 extra_index = 0; FSUSER_CloseArchive(ncch_archive);
memcpy(&extra_index, content_data + 0xC0, 4); free(contentInfos);
break;
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);
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_SET);
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_SET);
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); romfsMountFromFile(theme_fh, 0, "theme");
fh = NULL;
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); free(contentInfos);
contentInfos = NULL; contentInfos = NULL;
romfsUnmount("meta"); romfsUnmount("meta");
// don't need to close the file opened for the metadata, romfsUnmount took ownership // don't need to close the file opened for the metadata, romfsUnmount took ownership
FSUSER_CloseArchive(ncch_archive); FSUSER_CloseArchive(ncch_archive);
} }
free(smdh_data); free(smdh_data);