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)
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
* 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;
volatile bool stop;
Thread playing_thread;
Handle finished;
} audio_s;
void play_audio(audio_s *);
void stop_audio(audio_s**);
#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);
// 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;
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
svcCreateEvent(&audio->finished, RESET_STICKY);
ndspChnSetInterp(0, NDSP_INTERP_LINEAR);
ndspChnSetMix(0, audio->mix); // See mix comment above

View File

@@ -37,7 +37,7 @@
bool quit = false;
bool dspfirm = false;
static audio_s * audio = NULL;
audio_s * audio = NULL;
static bool homebrew = false;
static bool installed_themes = false;
@@ -109,12 +109,6 @@ static void stop_install_check(void)
{
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)
@@ -127,7 +121,6 @@ static void exit_thread(void)
svcWaitSynchronization(update_icons_mutex, U64_MAX);
threadJoin(iconLoadingThread, U64_MAX);
threadFree(iconLoadingThread);
iconLoadingThread = NULL;
}
}
@@ -145,7 +138,6 @@ static void free_icons(Entry_List_s * list)
free(list->icons[i]);
}
free(list->icons);
list->icons = NULL;
}
void free_lists(void)
@@ -165,7 +157,8 @@ void exit_function(bool power_pressed)
{
if(audio)
{
stop_audio(&audio);
audio->stop = true;
svcWaitSynchronization(audio->finished, U64_MAX);
}
free_lists();
svcCloseHandle(update_icons_mutex);
@@ -237,7 +230,7 @@ static void load_lists(Entry_List_s * lists)
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);
}
}
@@ -511,7 +504,9 @@ int main(void)
preview_mode = false;
if(current_mode == MODE_THEMES && audio)
{
stop_audio(&audio);
audio->stop = true;
svcWaitSynchronization(audio->finished, U64_MAX);
audio = NULL;
}
}
continue;
@@ -521,7 +516,9 @@ int main(void)
preview_mode = false;
if(current_mode == MODE_THEMES && audio)
{
stop_audio(&audio);
audio->stop = true;
svcWaitSynchronization(audio->finished, U64_MAX);
audio = NULL;
}
continue;
}

View File

@@ -69,26 +69,17 @@ void thread_audio(void* data) {
while(!audio->stop) {
update_audio(audio);
}
ndspChnWaveBufClear(0);
ndspChnReset(0);
ov_clear(&audio->vf);
free(audio->filebuf);
ov_clear(&audio->vf);
linearFree((void*)audio->wave_buf[0].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) {
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;
threadCreate(thread_audio, audio, 0x1000, 0x3F, 1, true);
}

View File

@@ -31,11 +31,6 @@
#include "fs.h"
#include "unicode.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] = {
{
@@ -331,13 +326,10 @@ static void load_remote_bgm(Entry_s * entry)
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);
if (R_FAILED(res))
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 };
strucat(path, entry->path);
@@ -444,12 +436,19 @@ static void search_menu(Entry_List_s * list)
if (button == SWKBD_BUTTON_CONFIRM)
{
free(list->tp_search);
list->tp_search = url_escape(search);
DEBUG("Search escaped: %s -> %s\n", search, list->tp_search);
for (unsigned int i = 0; i < strlen(search); i++)
{
if (search[i] == ' ')
search[i] = '+';
}
list->tp_search = search;
load_remote_list(list, 1, list->mode, false);
}
else
{
free(search);
}
}
static void change_selected(Entry_List_s * list, int change_value)
{
@@ -490,7 +489,7 @@ bool themeplaza_browser(EntryMode mode)
bool extra_mode = false;
while (aptMainLoop() && !quit)
while (aptMainLoop())
{
if (current_list->entries == NULL)
break;
@@ -518,6 +517,12 @@ bool themeplaza_browser(EntryMode mode)
exit:
quit = true;
downloaded = false;
if (audio)
{
audio->stop = true;
svcWaitSynchronization(audio->finished, U64_MAX);
audio = NULL;
}
break;
}
@@ -576,7 +581,9 @@ bool themeplaza_browser(EntryMode mode)
preview_mode = false;
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;
if (mode == MODE_THEMES && audio != NULL)
{
stop_audio(&audio);
audio->stop = true;
svcWaitSynchronization(audio->finished, U64_MAX);
audio = NULL;
}
}
else
@@ -653,7 +662,9 @@ bool themeplaza_browser(EntryMode mode)
preview_mode = false;
if (mode == MODE_THEMES && audio)
{
stop_audio(&audio);
audio->stop = true;
svcWaitSynchronization(audio->finished, U64_MAX);
audio = NULL;
}
continue;
}
@@ -720,11 +731,6 @@ bool themeplaza_browser(EntryMode mode)
}
}
if (audio)
{
stop_audio(&audio);
}
free_preview(preview);
free_icons(current_list);
@@ -764,19 +770,46 @@ typedef enum ParseResult
HTTP_GATEWAY_TIMEOUT = 504,
} 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;
(void)user;
*ppMessage = "Input must not contain:\n><\"?;:/\\+,.|[=]";
if(strpbrk(text, "><\"?;:/\\+,.|[=]"))
char * unescaped = malloc(strlen(url) + 1);
// source shamelessly ripped from https://stackoverflow.com/a/14530993/4073959
char a;
char b;
while (*url)
{
DEBUG("illegal filename: %s\n", text);
return SWKBD_CALLBACK_CONTINUE;
if ((*url == '%') &&
((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 unescaped;
}
return SWKBD_CALLBACK_OK;
}*/
// the good paths for this function return SUCCESS, ABORTED, or REDIRECT;
// 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");
*/
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;
httpcContext context;
@@ -935,7 +963,6 @@ redirect: // goto here if we need to redirect
#define ERROR_BUFFER_SIZE 0x80
char err_buf[ERROR_BUFFER_SIZE];
Result res;
ParseResult parse = parse_header(&_header, &context, acceptable_mime_types);
switch (parse)
{
@@ -981,10 +1008,7 @@ redirect: // goto here if we need to redirect
snprintf(err_buf, ERROR_BUFFER_SIZE, ZIP_NOT_AVAILABLE);
goto error;
case HTTP_NOT_FOUND:
if (!not_found_is_error)
goto not_found_non_error;
[[fallthrough]];
case HTTP_GONE:
case HTTP_GONE: ;
const char * http_error = parse == HTTP_NOT_FOUND ? "404 Not Found" : "410 Gone";
DEBUG("HTTP %s; URL: %s\n", http_error, url);
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;
error:
throw_error(err_buf, ERROR_LEVEL_WARNING);
res = httpcCloseContext(&context);
Result res = httpcCloseContext(&context);
if (R_FAILED(res)) return res;
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:;
u32 chunk_size;
if (_header.file_size)
@@ -1112,6 +1132,15 @@ no_error:;
*buf = new_buf;
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);
}

View File

@@ -505,7 +505,6 @@ Result dump_all_themes(void)
res = AMAPP_ListDLCContentInfos(&readcount, MEDIATYPE_SD, titleId, count, 0, contentInfos);
if(R_FAILED(res))
{
free(contentInfos);
break;
}
@@ -552,8 +551,6 @@ Result dump_all_themes(void)
FILE* fh = fopen(contentinfoarchive_path, "rb");
if(fh != NULL)
{
for(u32 i = 0; i < readcount; ++i)
{
if(i == 0) continue;
@@ -574,6 +571,11 @@ Result dump_all_themes(void)
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;
}
@@ -600,7 +602,7 @@ Result dump_all_themes(void)
{
fseek(theme_file, 0, SEEK_END);
long theme_size = ftell(theme_file);
fseek(theme_file, 0, SEEK_SET);
fseek(theme_file, 0, SEEK_CUR);
char* theme_data = malloc(theme_size);
fread(theme_data, 1, theme_size, theme_file);
fclose(theme_file);
@@ -617,7 +619,7 @@ Result dump_all_themes(void)
{
fseek(bgm_file, 0, SEEK_END);
long bgm_size = ftell(bgm_file);
fseek(bgm_file, 0, SEEK_SET);
fseek(bgm_file, 0, SEEK_CUR);
char* bgm_data = malloc(bgm_size);
fread(bgm_data, 1, bgm_size, bgm_file);
fclose(bgm_file);
@@ -644,7 +646,6 @@ Result dump_all_themes(void)
fclose(fh);
fh = NULL;
}
free(contentInfos);
contentInfos = NULL;