11 Commits

Author SHA1 Message Date
Dylan G
adccc70cca Fixed useless 404 error when a remote preview has no BGM 2022-07-31 20:41:22 +01:00
Dylan G
b66ca12039 Merge pull request #272 from LiquidFenrir/slight-music-rework
make audio safer
2022-06-17 23:05:22 +01:00
LiquidFenrir
4a56a883fa make audio safer
- centralized stop function
- freeing the struct not from the thread while waiting on handle in it
- thread not detached
- maybe fixes hang on exit from HM in ndsp status check loop
2022-06-14 13:02:15 +02:00
Dylan G
9ebfe387a0 Merge pull request #270 from LiquidFenrir/exit-fixes
fix multiple crashes
2022-06-10 23:07:34 +01:00
LiquidFenrir
806d0033de fix multiple crashes
on exit:
- bad timing when the install checks threads run, could crash
- quitting through HOME when a bgm was previewed in the browser
on http get failure:
- the quit flag would be enabled, but the browser wouldn't honour it
2022-06-05 11:49:09 +02:00
Dylan G
4c053bb447 Merge pull request #269 from LiquidFenrir/patch-2
fix dumping official body and bgm, and double free
2022-06-03 20:46:14 +01:00
Théo B
1ed6c46644 fix dumping official body and bgm, and double free
fixes #268
2022-06-03 21:40:23 +02:00
Dylan G
e22d09ba54 Typo in README
a stray backslash found its way into the first line
2022-05-30 14:24:23 +01:00
Dylan G
a0c16a64ec Search strings now escaped properly (fixes #267) 2022-05-30 12:04:18 +01:00
Dylan G
a1e7ed9924 Added header-only URL encoding/decoding 2022-05-30 12:03:11 +01:00
Dylan G
7f7fdc010a Fixed faulty FS error when a filename contains a / 2022-05-30 10:43:12 +01:00
9 changed files with 248 additions and 142 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,9 +46,10 @@ typedef struct {
u32 filesize; u32 filesize;
volatile bool stop; volatile bool stop;
Handle finished; Thread playing_thread;
} audio_s; } audio_s;
void play_audio(audio_s *); void play_audio(audio_s *);
void stop_audio(audio_s**);
#endif #endif

88
include/urls.h Normal file
View File

@@ -0,0 +1,88 @@
/*
* 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 = strrchr(path_to_file, '/') + 1; char * curr_filename = path_to_file + strlen(main_paths[mode]);
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,7 +608,6 @@ 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;
audio_s * audio = NULL; static audio_s * audio = NULL;
static bool homebrew = false; static bool homebrew = false;
static bool installed_themes = false; static bool installed_themes = false;
@@ -109,6 +109,12 @@ 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)
@@ -121,6 +127,7 @@ 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;
} }
} }
@@ -138,6 +145,7 @@ 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)
@@ -157,8 +165,7 @@ void exit_function(bool power_pressed)
{ {
if(audio) if(audio)
{ {
audio->stop = true; stop_audio(&audio);
svcWaitSynchronization(audio->finished, U64_MAX);
} }
free_lists(); free_lists();
svcCloseHandle(update_icons_mutex); svcCloseHandle(update_icons_mutex);
@@ -230,7 +237,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, true); installCheckThreads[i] = threadCreate(install_check_function, current_arg, __stacksize__, 0x3f, -2, false);
svcSleepThread(1e8); svcSleepThread(1e8);
} }
} }
@@ -504,9 +511,7 @@ int main(void)
preview_mode = false; preview_mode = false;
if(current_mode == MODE_THEMES && audio) if(current_mode == MODE_THEMES && audio)
{ {
audio->stop = true; stop_audio(&audio);
svcWaitSynchronization(audio->finished, U64_MAX);
audio = NULL;
} }
} }
continue; continue;
@@ -516,9 +521,7 @@ int main(void)
preview_mode = false; preview_mode = false;
if(current_mode == MODE_THEMES && audio) if(current_mode == MODE_THEMES && audio)
{ {
audio->stop = true; stop_audio(&audio);
svcWaitSynchronization(audio->finished, U64_MAX);
audio = NULL;
} }
continue; continue;
} }

View File

@@ -69,17 +69,26 @@ void thread_audio(void* data) {
while(!audio->stop) { while(!audio->stop) {
update_audio(audio); update_audio(audio);
} }
free(audio->filebuf); ndspChnWaveBufClear(0);
ndspChnReset(0);
ov_clear(&audio->vf); ov_clear(&audio->vf);
free(audio->filebuf);
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) {
threadCreate(thread_audio, audio, 0x1000, 0x3F, 1, true); audio->playing_thread = threadCreate(thread_audio, audio, 0x1000, 0x3F, 1, false);
}
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,6 +31,11 @@
#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] = {
{ {
@@ -326,10 +331,13 @@ static void load_remote_bgm(Entry_s * entry)
draw_install(INSTALL_LOADING_REMOTE_BGM); draw_install(INSTALL_LOADING_REMOTE_BGM);
Result res = http_get(bgm_url, NULL, &bgm_ogg, &bgm_size, INSTALL_LOADING_REMOTE_BGM, "application/ogg, audio/ogg"); Result res = http_get_with_not_found_flag(bgm_url, NULL, &bgm_ogg, &bgm_size, INSTALL_LOADING_REMOTE_BGM, "application/ogg, audio/ogg", false);
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);
@@ -436,18 +444,11 @@ 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);
for (unsigned int i = 0; i < strlen(search); i++) list->tp_search = url_escape(search);
{ 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);
} }
else free(search);
{
free(search);
}
} }
static void change_selected(Entry_List_s * list, int change_value) static void change_selected(Entry_List_s * list, int change_value)
@@ -489,7 +490,7 @@ bool themeplaza_browser(EntryMode mode)
bool extra_mode = false; bool extra_mode = false;
while (aptMainLoop()) while (aptMainLoop() && !quit)
{ {
if (current_list->entries == NULL) if (current_list->entries == NULL)
break; break;
@@ -517,12 +518,6 @@ 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;
} }
@@ -581,9 +576,7 @@ bool themeplaza_browser(EntryMode mode)
preview_mode = false; preview_mode = false;
if (mode == MODE_THEMES && audio != NULL) if (mode == MODE_THEMES && audio != NULL)
{ {
audio->stop = true; stop_audio(&audio);
svcWaitSynchronization(audio->finished, U64_MAX);
audio = NULL;
} }
} }
} }
@@ -594,9 +587,7 @@ bool themeplaza_browser(EntryMode mode)
preview_mode = false; preview_mode = false;
if (mode == MODE_THEMES && audio != NULL) if (mode == MODE_THEMES && audio != NULL)
{ {
audio->stop = true; stop_audio(&audio);
svcWaitSynchronization(audio->finished, U64_MAX);
audio = NULL;
} }
} }
else else
@@ -662,9 +653,7 @@ bool themeplaza_browser(EntryMode mode)
preview_mode = false; preview_mode = false;
if (mode == MODE_THEMES && audio) if (mode == MODE_THEMES && audio)
{ {
audio->stop = true; stop_audio(&audio);
svcWaitSynchronization(audio->finished, U64_MAX);
audio = NULL;
} }
continue; continue;
} }
@@ -731,6 +720,11 @@ bool themeplaza_browser(EntryMode mode)
} }
} }
if (audio)
{
stop_audio(&audio);
}
free_preview(preview); free_preview(preview);
free_icons(current_list); free_icons(current_list);
@@ -770,7 +764,7 @@ 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) /*static SwkbdCallbackResult fat32filter(void *user, const char **ppMessage, const char *text, size_t textlen)
{ {
(void)textlen; (void)textlen;
(void)user; (void)user;
@@ -782,7 +776,7 @@ static SwkbdCallbackResult fat32filter(void *user, const char **ppMessage, const
} }
return SWKBD_CALLBACK_OK; return SWKBD_CALLBACK_OK;
} }*/
// 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
@@ -900,6 +894,11 @@ 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;
@@ -936,6 +935,7 @@ 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,7 +981,10 @@ 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:
case HTTP_GONE: ; if (!not_found_is_error)
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)
@@ -1044,9 +1047,13 @@ 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);
Result res = httpcCloseContext(&context); 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)

View File

@@ -505,6 +505,7 @@ 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;
} }
@@ -551,107 +552,105 @@ Result dump_all_themes(void)
FILE* fh = fopen(contentinfoarchive_path, "rb"); FILE* fh = fopen(contentinfoarchive_path, "rb");
for(u32 i = 0; i < readcount; ++i) if(fh != NULL)
{ {
if(i == 0) continue; for(u32 i = 0; i < readcount; ++i)
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; if(i == 0) continue;
fseek(fh, off, SEEK_SET); AM_ContentInfo* content = &contentInfos[i];
char content_data[0xc8] = {0}; if((content->flags & (AM_CONTENT_DOWNLOADED | AM_CONTENT_OWNED)) == (AM_CONTENT_DOWNLOADED | AM_CONTENT_OWNED))
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); long off = 0x8 + 0xC8 * i;
fclose(fh); fseek(fh, off, SEEK_SET);
free(contentInfos); char content_data[0xc8] = {0};
romfsUnmount("meta"); fread(content_data, 1, 0xc8, fh);
FSUSER_CloseArchive(ncch_archive); u32 extra_index = 0;
free(contentInfos); memcpy(&extra_index, content_data + 0xC0, 4);
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);
} }
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); fclose(fh);
fh = NULL; 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);