2 Commits

Author SHA1 Message Date
Dylan G
85d09107ed response to feedback 2021-03-16 16:24:54 +00:00
Dylan G
b16df2607c Add issue templates
We've needed this for a while, due to bug reports that don't really mean anything.
2021-03-11 18:38:00 +00:00
18 changed files with 144 additions and 614 deletions

View File

@@ -1,6 +1,6 @@
# Main Contributors # Main Contributors
* Alex Taber ([@astronautlevel2](https://github.com/astronautlevel2)) * Alex Taber ([@astronautlevel2](https://github.com/astronautlevel2))
* Théo B. ([@LiquidFenrir](https://github.com/LiquidFenrir)) * [@LiquidFenrir](https://github.com/LiquidFenrir)
* Dawid Eckert ([@daedreth](https://github.com/daedreth)) * Dawid Eckert ([@daedreth](https://github.com/daedreth))
* Dylan G. ([@helloman892](https://github.com/helloman892)) * Dylan G. ([@helloman892](https://github.com/helloman892))
* Nils P. ([@ZetaDesigns](https://github.com/ZetaDesigns)) * Nils P. ([@ZetaDesigns](https://github.com/ZetaDesigns))

View File

@@ -89,7 +89,7 @@ CFLAGS := -g -Wall -Wextra -O2 -mword-relocations \
-ffunction-sections \ -ffunction-sections \
$(ARCH) $(ARCH)
CFLAGS += $(INCLUDE) -D__3DS__ -D_GNU_SOURCE -DVERSION="\"$(VERSION)\"" -DUSER_AGENT="\"$(APP_TITLE)/$(VERSION)\"" -DAPP_TITLE="\"$(APP_TITLE)\"" CFLAGS += $(INCLUDE) -DARM11 -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

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

@@ -56,7 +56,6 @@ 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;
@@ -85,7 +84,6 @@ 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,
@@ -162,7 +160,7 @@ void start_frame(void);
void end_frame(void); void end_frame(void);
void set_screen(C3D_RenderTarget * screen); void set_screen(C3D_RenderTarget * screen);
void throw_error(const char* error, ErrorLevel level); void throw_error(char* error, ErrorLevel level);
bool draw_confirm(const char* conf_msg, Entry_List_s* list); bool draw_confirm(const char* conf_msg, Entry_List_s* list);
void draw_preview(C2D_Image preview, int preview_offset); void draw_preview(C2D_Image preview, int preview_offset);

View File

@@ -29,8 +29,6 @@
#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;
@@ -46,6 +44,5 @@ u32 compress_lz_file_fast(FS_Path path, FS_Archive archive, char *in_buf, u32 si
Result buf_to_file(u32 size, FS_Path path, FS_Archive archive, char *buf); Result buf_to_file(u32 size, FS_Path path, FS_Archive archive, char *buf);
void remake_file(FS_Path path, FS_Archive archive, u32 size); void remake_file(FS_Path path, FS_Archive archive, u32 size);
void save_zip_to_sd(char * filename, u32 size, char * buf, EntryMode mode);
#endif #endif

View File

@@ -128,31 +128,10 @@ Instructions_s extra_instructions[3] = {
}, },
{ {
"\uE07B Browse ThemePlaza", "\uE07B Browse ThemePlaza",
NULL "\uE07C Dump Current Theme"
}, },
{ {
"\uE004 Sorting menu", "\uE004 Sorting menu",
"\uE005 Dumping menu"
},
{
"Exit",
NULL
}
}
},
{
.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 NULL
}, },
{ {
@@ -160,7 +139,7 @@ Instructions_s extra_instructions[3] = {
NULL NULL
} }
} }
} },
}; };
#endif #endif

View File

@@ -34,7 +34,7 @@
#include <tremor/ivorbisfile.h> #include <tremor/ivorbisfile.h>
#include <tremor/ivorbiscodec.h> #include <tremor/ivorbiscodec.h>
#define BUF_TO_READ 48000 // How much data should be buffered at a time #define BUF_TO_READ 40960 // How much data should be buffered at a time
typedef struct { typedef struct {
OggVorbis_File vf; OggVorbis_File vf;
@@ -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

@@ -31,7 +31,7 @@
#include "draw.h" #include "draw.h"
#include <ctype.h> #include <ctype.h>
#define THEMEPLAZA_BASE_URL "http://themeplaza.art" #define THEMEPLAZA_BASE_URL "https://themeplaza.art"
#define THEMEPLAZA_API_URL "/api/anemone/v1" #define THEMEPLAZA_API_URL "/api/anemone/v1"
#define THEMEPLAZA_BASE_API_URL THEMEPLAZA_BASE_URL THEMEPLAZA_API_URL #define THEMEPLAZA_BASE_API_URL THEMEPLAZA_BASE_URL THEMEPLAZA_API_URL

View File

@@ -49,9 +49,8 @@ typedef struct {
u8 _padding1[0x13b8]; u8 _padding1[0x13b8];
ThemeEntry_s theme_entry; ThemeEntry_s theme_entry;
ThemeEntry_s shuffle_themes[MAX_SHUFFLE_THEMES]; ThemeEntry_s shuffle_themes[MAX_SHUFFLE_THEMES];
u8 shuffle_seedA[0xb]; u8 _padding2[0xb];
u8 shuffle; bool shuffle;
u8 shuffle_seedB[0xa];
} SaveData_dat_s; } SaveData_dat_s;
typedef struct { typedef struct {
@@ -76,8 +75,7 @@ Result bgm_install(Entry_s theme);
Result shuffle_install(Entry_List_s themes); Result shuffle_install(Entry_List_s themes);
Result dump_current_theme(void); Result dump_theme(void);
Result dump_all_themes(void);
void themes_check_installed(void * void_arg); void themes_check_installed(void * void_arg);

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

@@ -391,7 +391,14 @@ bool init_qr(void)
if(mode != MODE_AMOUNT) if(mode != MODE_AMOUNT)
{ {
save_zip_to_sd(filename, zip_size, zip_buf, mode); char path_to_file[0x107] = {0};
sprintf(path_to_file, "%s%s", main_paths[mode], filename);
char * extension = strrchr(path_to_file, '.');
if (extension == NULL || strcmp(extension, ".zip"))
strcat(path_to_file, ".zip");
remake_file(fsMakePath(PATH_ASCII, path_to_file), ArchiveSD, zip_size);
buf_to_file(zip_size, fsMakePath(PATH_ASCII, path_to_file), ArchiveSD, zip_buf);
success = true; success = true;
} }
else else

View File

@@ -126,7 +126,6 @@ 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]);
@@ -276,7 +275,7 @@ void draw_base_interface(void)
set_screen(top); set_screen(top);
} }
void throw_error(const char* error, ErrorLevel level) void throw_error(char* error, ErrorLevel level)
{ {
Text bottom_text = TEXT_AMOUNT; Text bottom_text = TEXT_AMOUNT;
Color text_color = COLOR_WHITE; Color text_color = COLOR_WHITE;

View File

@@ -27,7 +27,6 @@
#include <strings.h> #include <strings.h>
#include "fs.h" #include "fs.h"
#include "draw.h"
#include "unicode.h" #include "unicode.h"
#include <archive.h> #include <archive.h>
@@ -352,115 +351,3 @@ void remake_file(FS_Path path, FS_Archive archive, u32 size)
buf_to_file(size, path, archive, buf); buf_to_file(size, path, archive, buf);
free(buf); free(buf);
} }
static SwkbdCallbackResult fat32filter(void *user, const char **ppMessage, const char *text, size_t textlen)
{
(void)textlen;
(void)user;
*ppMessage = "Input must not contain:\n" ILLEGAL_CHARS;
if(strpbrk(text, ILLEGAL_CHARS))
{
DEBUG("illegal filename: %s\n", text);
return SWKBD_CALLBACK_CONTINUE;
}
return SWKBD_CALLBACK_OK;
}
// assumes the input buffer is a ZIP. if it isn't, why are you calling this?
void save_zip_to_sd(char * filename, u32 size, char * buf, EntryMode mode)
{
static char path_to_file[32761]; // FAT32 paths can be quite long.
const int max_chars = 250;
char new_filename[max_chars + 5]; // .zip + \0
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 * illegal_char = curr_filename;
while ((illegal_char = strpbrk(illegal_char, ILLEGAL_CHARS)))
{
DEBUG("Illegal char found in filename: %c\n", *illegal_char);
if (*illegal_char == '.')
{
// skip initial . (this is allowed)
if (illegal_char == curr_filename)
continue;
// skip extension delimiter
if (strpbrk(illegal_char + 1, ".") == NULL)
{
illegal_char++;
continue;
}
}
*illegal_char = '-';
}
// ensure the extension is .zip
char * extension = strrchr(path_to_file, '.');
if (extension == NULL || strcmp(extension, ".zip"))
strcat(path_to_file, ".zip");
DEBUG("path: %s\n", path_to_file);
FS_Path path = fsMakePath(PATH_ASCII, path_to_file);
// check if file already exists, and if it does, prompt the user
// to overwrite or change name (or exit)
Result res = FSUSER_CreateFile(ArchiveSD, path, 0, size);
if (R_FAILED(res))
{
if (res == (long)0xC82044BE)
{
DEBUG("File already exists\n");
SwkbdState swkbd;
swkbdInit(&swkbd, SWKBD_TYPE_NORMAL, 3, max_chars / 2);
swkbdSetHintText(&swkbd, "Choose a new filename or tap Overwrite");
swkbdSetFeatures(&swkbd, SWKBD_PREDICTIVE_INPUT | SWKBD_DARKEN_TOP_SCREEN);
swkbdSetButton(&swkbd, SWKBD_BUTTON_LEFT, "Cancel", false);
swkbdSetButton(&swkbd, SWKBD_BUTTON_MIDDLE, "Overwrite", false);
swkbdSetButton(&swkbd, SWKBD_BUTTON_RIGHT, "Rename", true);
swkbdSetValidation(&swkbd, SWKBD_NOTEMPTY_NOTBLANK, SWKBD_FILTER_CALLBACK, -1);
swkbdSetFilterCallback(&swkbd, &fat32filter, NULL);
SwkbdButton button = swkbdInputText(&swkbd, new_filename, max_chars);
switch (button)
{
case SWKBD_BUTTON_RIGHT:
DEBUG("Renaming to %s\n", new_filename);
strcat(new_filename, ".zip");
filename = new_filename;
goto renamed;
case SWKBD_BUTTON_MIDDLE:
// we good
DEBUG("Overwriting %s\n", filename);
break;
case SWKBD_BUTTON_LEFT:
// do nothing
DEBUG("File rename cancelled\n");
return;
case SWKBD_BUTTON_NONE:
DEBUG("SWKBD broke wtf??? :- %x\n", swkbdGetResult(&swkbd));
return throw_error("???\nTry a USB keyboard", ERROR_LEVEL_WARNING);
}
}
else if (res == (long)0xC86044D2)
{
DEBUG("SD card is full\n");
return throw_error("SD card is full.\nDelete some themes to make space.", ERROR_LEVEL_WARNING);
}
else
{
DEBUG("error: %lx\n", res);
return throw_error("FS Error:\nGet a new SD card.", ERROR_LEVEL_ERROR);
}
}
DEBUG("Saving to SD: %s\n", path_to_file);
remake_file(path, ArchiveSD, size);
buf_to_file(size, path, ArchiveSD, buf);
}

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);
} }
} }
@@ -408,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];
} }
@@ -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;
} }
@@ -651,6 +648,13 @@ 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)
{ {
@@ -673,23 +677,6 @@ 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;
} }

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

@@ -24,18 +24,11 @@
* reasonable ways as different from the original version. * reasonable ways as different from the original version.
*/ */
#include <ctype.h>
#include "remote.h" #include "remote.h"
#include "loading.h" #include "loading.h"
#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 +324,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);
@@ -368,8 +358,17 @@ static void download_remote_entry(Entry_s * entry, EntryMode mode)
} }
free(download_url); free(download_url);
save_zip_to_sd(filename, zip_size, zip_buf, mode); char path_to_file[0x107] = { 0 };
sprintf(path_to_file, "%s%s", main_paths[mode], filename);
free(filename); free(filename);
char * extension = strrchr(path_to_file, '.');
if (extension == NULL || strcmp(extension, ".zip"))
strcat(path_to_file, ".zip");
DEBUG("Saving to SD: %s\n", path_to_file);
remake_file(fsMakePath(PATH_ASCII, path_to_file), ArchiveSD, zip_size);
buf_to_file(zip_size, fsMakePath(PATH_ASCII, path_to_file), ArchiveSD, zip_buf);
free(zip_buf); free(zip_buf);
} }
@@ -444,11 +443,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);
} }
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)
@@ -490,7 +496,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 +524,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 +588,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 +601,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 +669,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 +738,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);
@@ -746,6 +759,7 @@ typedef enum ParseResult
SUCCESS, // 200/203 (203 indicates a successful request with a transformation applied by a proxy) SUCCESS, // 200/203 (203 indicates a successful request with a transformation applied by a proxy)
REDIRECT, // 301/302/307/308 REDIRECT, // 301/302/307/308
HTTPC_ERROR, HTTPC_ERROR,
ABORTED,
SERVER_IS_MISBEHAVING, SERVER_IS_MISBEHAVING,
SEE_OTHER = 303, // Theme Plaza returns these SEE_OTHER = 303, // Theme Plaza returns these
HTTP_UNAUTHORIZED = 401, HTTP_UNAUTHORIZED = 401,
@@ -764,7 +778,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;
@@ -776,7 +790,7 @@ typedef enum ParseResult
} }
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
@@ -838,53 +852,65 @@ static ParseResult parse_header(struct header * out, httpcContext * context, con
if (out->filename) if (out->filename)
{ {
bool present = 1;
out->result_code = httpcGetResponseHeader(context, "Content-Disposition", content_buf, 1024); out->result_code = httpcGetResponseHeader(context, "Content-Disposition", content_buf, 1024);
if (R_FAILED(out->result_code)) if (R_FAILED(out->result_code))
{
if (out->result_code == (long)0xD8A0A028L)
present = 0;
else
{ {
DEBUG("httpcGetResponseHeader\n"); DEBUG("httpcGetResponseHeader\n");
return HTTPC_ERROR; return HTTPC_ERROR;
} }
}
// content_buf: Content-Disposition: attachment; ... filename=<filename>;? ... // content_buf: Content-Disposition: attachment; ... filename=<filename>;? ...
if (present)
{
char * filename = strstr(content_buf, "filename="); // filename=<filename>;? ... char * filename = strstr(content_buf, "filename="); // filename=<filename>;? ...
// in the extreme fringe case that filename is missing: if (!filename)
if (filename != NULL)
{ {
const int max_chars = 250;
// needs to be heap allocated only because the call site is expected to free it
*out->filename = malloc(max_chars + 5); // + .zip and the null term
SwkbdState swkbd;
swkbdInit(&swkbd, SWKBD_TYPE_NORMAL, 2, max_chars / 2);
swkbdSetHintText(&swkbd, "Choose a filename");
swkbdSetFeatures(&swkbd, SWKBD_PREDICTIVE_INPUT | SWKBD_DARKEN_TOP_SCREEN);
swkbdSetButton(&swkbd, SWKBD_BUTTON_LEFT, "Cancel", false);
swkbdSetButton(&swkbd, SWKBD_BUTTON_RIGHT, "Download", true);
swkbdSetValidation(&swkbd, SWKBD_NOTEMPTY_NOTBLANK, SWKBD_FILTER_CALLBACK, -1);
swkbdSetFilterCallback(&swkbd, &fat32filter, NULL);
SwkbdButton button = swkbdInputText(&swkbd, *out->filename, max_chars);
if (button != SWKBD_BUTTON_CONFIRM)
{
out->result_code = swkbdGetResult(&swkbd);
return ABORTED;
}
strcat(*out->filename, ".zip");
return SUCCESS;
}
filename = strpbrk(filename, "=") + 1; // <filename>;? filename = strpbrk(filename, "=") + 1; // <filename>;?
char * end = strpbrk(filename, ";"); char * end = strpbrk(filename, ";");
if (end) if (end)
*end = '\0'; // <filename> *end = '\0'; // <filename>
// safe to assume the filename is quoted
// (if it isn't, then we already have a null-terminated string <filename>)
if (filename[0] == '"') if (filename[0] == '"')
// safe to assume the filename is quoted
{ {
filename[strlen(filename) - 1] = '\0'; filename[strlen(filename) - 1] = '\0';
filename++; filename++;
} }
char * illegal_char;
// filter out characters illegal in FAT32 filenames
while ((illegal_char = strpbrk(filename, "><\"?;:/\\+,.|[=]")))
*illegal_char = '-';
*out->filename = malloc(strlen(filename) + 1); *out->filename = malloc(strlen(filename) + 1);
strcpy(*out->filename, filename); strcpy(*out->filename, filename);
} }
else
{
*out->filename = NULL;
}
}
else
{
*out->filename = NULL;
}
}
return SUCCESS; return SUCCESS;
} }
@@ -894,11 +920,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,12 +956,16 @@ 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)
{ {
case SUCCESS: case SUCCESS:
break; break;
case ABORTED:
ret = httpcCloseContext(&context);
if(R_FAILED(ret))
return ret;
return MAKERESULT(RL_SUCCESS, RS_CANCELED, RM_APPLICATION, RD_CANCEL_REQUESTED);
case HTTPC_ERROR: case HTTPC_ERROR:
DEBUG("httpc error %lx\n", _header.result_code); DEBUG("httpc error %lx\n", _header.result_code);
snprintf(err_buf, ERROR_BUFFER_SIZE, "Error in HTTPC sysmodule - 0x%08lx.\nIf you are seeing this, please contact an\nAnemone developer on the Theme Plaza Discord.", _header.result_code); snprintf(err_buf, ERROR_BUFFER_SIZE, "Error in HTTPC sysmodule - 0x%08lx.\nIf you are seeing this, please contact an\nAnemone developer on the Theme Plaza Discord.", _header.result_code);
@@ -981,10 +1006,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 +1069,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)

View File

@@ -261,24 +261,18 @@ static Result install_theme_internal(Entry_List_s themes, int installmode)
SaveData_dat_s* savedata = (SaveData_dat_s*)savedata_buf; SaveData_dat_s* savedata = (SaveData_dat_s*)savedata_buf;
memset(&savedata->theme_entry, 0, sizeof(ThemeEntry_s)); memset(&savedata->theme_entry, 0, sizeof(ThemeEntry_s));
savedata->theme_entry.type = 3;
savedata->theme_entry.index = 0xff;
savedata->shuffle = (installmode & THEME_INSTALL_SHUFFLE) ? 1 : 0; savedata->shuffle = (installmode & THEME_INSTALL_SHUFFLE);
memset(savedata->shuffle_themes, 0, sizeof(ThemeEntry_s)*MAX_SHUFFLE_THEMES);
if(installmode & THEME_INSTALL_SHUFFLE) if(installmode & THEME_INSTALL_SHUFFLE)
{ {
memset(savedata->shuffle_themes, 0, sizeof(ThemeEntry_s)*MAX_SHUFFLE_THEMES);
for(int i = 0; i < themes.shuffle_count; i++) for(int i = 0; i < themes.shuffle_count; i++)
{ {
savedata->shuffle_themes[i].type = 3; savedata->shuffle_themes[i].type = 3;
savedata->shuffle_themes[i].index = i; savedata->shuffle_themes[i].index = i;
} }
const u8 shuffle_seed[0xB] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
memcpy(savedata->shuffle_seedA, shuffle_seed, 0xB);
memcpy(savedata->shuffle_seedB, shuffle_seed, 0xA);
}
else
{
savedata->theme_entry.type = 3;
savedata->theme_entry.index = 0xff;
} }
res = buf_to_file(savedata_size, fsMakePath(PATH_ASCII, "/SaveData.dat"), ArchiveHomeExt, savedata_buf); res = buf_to_file(savedata_size, fsMakePath(PATH_ASCII, "/SaveData.dat"), ArchiveHomeExt, savedata_buf);
@@ -334,7 +328,7 @@ dir_name_callback(void *data, const char ** ppMessage, const char * text, size_t
return SWKBD_CALLBACK_OK; return SWKBD_CALLBACK_OK;
} }
Result dump_current_theme(void) Result dump_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,243 +415,6 @@ Result dump_current_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(&regionCode);
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))
{
free(contentInfos);
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");
if(fh != NULL)
{
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);
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);
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;