mirror of
https://github.com/astronautlevel2/Anemone3DS.git
synced 2026-01-24 16:52:43 -05:00
Compare commits
25 Commits
issue-temp
...
v2.3.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
adccc70cca | ||
|
|
b66ca12039 | ||
|
|
4a56a883fa | ||
|
|
9ebfe387a0 | ||
|
|
806d0033de | ||
|
|
4c053bb447 | ||
|
|
1ed6c46644 | ||
|
|
e22d09ba54 | ||
|
|
a0c16a64ec | ||
|
|
a1e7ed9924 | ||
|
|
7f7fdc010a | ||
|
|
b81a9aaa4c | ||
|
|
6ba1ef111e | ||
|
|
0500b24431 | ||
|
|
83071d3734 | ||
|
|
4e2bea53c1 | ||
|
|
0da2594251 | ||
|
|
c5dc7448e4 | ||
|
|
999b764c26 | ||
|
|
18cb5c616f | ||
|
|
3f2e4c03f3 | ||
|
|
163e12d38a | ||
| 02c3e617ae | |||
|
|
6161874d07 | ||
|
|
cea9b8655a |
39
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
39
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
---
|
||||||
|
name: Bug report
|
||||||
|
about: Found a bug? Report it here
|
||||||
|
title: ''
|
||||||
|
labels: ''
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Description**
|
||||||
|
A clear and concise description of what the bug is.
|
||||||
|
|
||||||
|
**Steps to reproduce**
|
||||||
|
***If you cannot reproduce the bug, please describe what you were doing in as much detail as possible***
|
||||||
|
*If you were scanning using the QR scanner, ensure you include a link to the theme you were attempting to download.*
|
||||||
|
|
||||||
|
|
||||||
|
**Expected behavior**
|
||||||
|
A clear and concise description of what you expected to happen.
|
||||||
|
|
||||||
|
**Screenshots**
|
||||||
|
Add screenshots of any error screens you encountered. Bug reports without screenshots will take longer to solve, so we recommend you get some!
|
||||||
|
|
||||||
|
|
||||||
|
**Crash dumps**
|
||||||
|
If your bug causes a crash in Luma3DS, please upload any crash dumps generated. You can find these in `SD:/luma/dumps/armX` (`X = 9 or 11`) with the name Luma3DS's exception handlers give you.
|
||||||
|
|
||||||
|
|
||||||
|
**System information**
|
||||||
|
*System model* e.g. new3DS XL
|
||||||
|
|
||||||
|
*System firmware version* e.g. 11.14.0 (this can be found in the System Settings applet by default)
|
||||||
|
|
||||||
|
*Anemone3DS version* e.g. v2.2.0 (found in the bottom-left of Anemone3DS)
|
||||||
|
|
||||||
|
*Luma3DS version* e.g. v10.2.1 (found in the menu when you hold Select on boot)
|
||||||
|
|
||||||
|
|
||||||
|
**If you are not on the [latest version of Anemone3DS](https://github.com/astronautlevel2/Anemone3DS/releases/latest), your bug report will likely be closed. Ensure the bug occurs in the latest build!**
|
||||||
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
name: Feature request
|
||||||
|
about: Suggest an idea for this project
|
||||||
|
title: ''
|
||||||
|
labels: ''
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Is your feature request related to a problem? Please describe.**
|
||||||
|
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||||
|
|
||||||
|
**Describe the solution you'd like**
|
||||||
|
A clear and concise description of what you want to happen.
|
||||||
|
|
||||||
|
**Describe alternatives you've considered**
|
||||||
|
A clear and concise description of any alternative solutions or features you've considered.
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context or screenshots about the feature request here.
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
# Main Contributors
|
# Main Contributors
|
||||||
* Alex Taber ([@astronautlevel2](https://github.com/astronautlevel2))
|
* Alex Taber ([@astronautlevel2](https://github.com/astronautlevel2))
|
||||||
* [@LiquidFenrir](https://github.com/LiquidFenrir)
|
* Théo B. ([@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))
|
||||||
|
|||||||
2
Makefile
2
Makefile
@@ -89,7 +89,7 @@ CFLAGS := -g -Wall -Wextra -O2 -mword-relocations \
|
|||||||
-ffunction-sections \
|
-ffunction-sections \
|
||||||
$(ARCH)
|
$(ARCH)
|
||||||
|
|
||||||
CFLAGS += $(INCLUDE) -DARM11 -D_3DS -D_GNU_SOURCE -DVERSION="\"$(VERSION)\"" -DUSER_AGENT="\"$(APP_TITLE)/$(VERSION)\"" -DAPP_TITLE="\"$(APP_TITLE)\""
|
CFLAGS += $(INCLUDE) -D__3DS__ -D_GNU_SOURCE -DVERSION="\"$(VERSION)\"" -DUSER_AGENT="\"$(APP_TITLE)/$(VERSION)\"" -DAPP_TITLE="\"$(APP_TITLE)\""
|
||||||
ifneq ($(strip $(CITRA_MODE)),)
|
ifneq ($(strip $(CITRA_MODE)),)
|
||||||
CFLAGS += -DCITRA_MODE
|
CFLAGS += -DCITRA_MODE
|
||||||
endif
|
endif
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||

|

|
||||||
|
|
||||||
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).
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ typedef enum {
|
|||||||
INSTALL_LOADING_REMOTE_BGM,
|
INSTALL_LOADING_REMOTE_BGM,
|
||||||
|
|
||||||
INSTALL_DUMPING_THEME,
|
INSTALL_DUMPING_THEME,
|
||||||
|
INSTALL_DUMPING_ALL_THEMES,
|
||||||
|
|
||||||
INSTALL_NONE,
|
INSTALL_NONE,
|
||||||
} InstallType;
|
} InstallType;
|
||||||
@@ -84,6 +85,7 @@ typedef enum {
|
|||||||
TEXT_INSTALL_LOADING_REMOTE_BGM,
|
TEXT_INSTALL_LOADING_REMOTE_BGM,
|
||||||
|
|
||||||
TEXT_INSTALL_DUMPING_THEME,
|
TEXT_INSTALL_DUMPING_THEME,
|
||||||
|
TEXT_INSTALL_DUMPING_ALL_THEMES,
|
||||||
|
|
||||||
// Other text
|
// Other text
|
||||||
TEXT_VERSION,
|
TEXT_VERSION,
|
||||||
@@ -160,7 +162,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(char* error, ErrorLevel level);
|
void throw_error(const 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);
|
||||||
|
|||||||
@@ -29,6 +29,8 @@
|
|||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
|
#define ILLEGAL_CHARS "><\"?;:/\\+,.|[=]"
|
||||||
|
|
||||||
extern FS_Archive ArchiveSD;
|
extern FS_Archive ArchiveSD;
|
||||||
extern FS_Archive ArchiveHomeExt;
|
extern FS_Archive ArchiveHomeExt;
|
||||||
extern FS_Archive ArchiveThemeExt;
|
extern FS_Archive ArchiveThemeExt;
|
||||||
@@ -44,5 +46,6 @@ 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
|
||||||
|
|||||||
@@ -128,11 +128,11 @@ Instructions_s extra_instructions[3] = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"\uE07B Browse ThemePlaza",
|
"\uE07B Browse ThemePlaza",
|
||||||
"\uE07C Dump Current Theme"
|
NULL
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"\uE004 Sorting menu",
|
"\uE004 Sorting menu",
|
||||||
NULL
|
"\uE005 Dumping menu"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Exit",
|
"Exit",
|
||||||
@@ -140,6 +140,27 @@ Instructions_s extra_instructions[3] = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.info_line = "Release \uE002 to cancel or hold \uE006 and release \uE002 to dump",
|
||||||
|
.instructions = {
|
||||||
|
{
|
||||||
|
"\uE079 Dump Current Theme",
|
||||||
|
"\uE07A Dump All Themes"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
NULL,
|
||||||
|
NULL
|
||||||
|
},
|
||||||
|
{
|
||||||
|
NULL,
|
||||||
|
NULL
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Exit",
|
||||||
|
NULL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -34,7 +34,7 @@
|
|||||||
#include <tremor/ivorbisfile.h>
|
#include <tremor/ivorbisfile.h>
|
||||||
#include <tremor/ivorbiscodec.h>
|
#include <tremor/ivorbiscodec.h>
|
||||||
|
|
||||||
#define BUF_TO_READ 40960 // How much data should be buffered at a time
|
#define BUF_TO_READ 48000 // How much data should be buffered at a time
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
OggVorbis_File vf;
|
OggVorbis_File vf;
|
||||||
@@ -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
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
#include "draw.h"
|
#include "draw.h"
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
|
||||||
#define THEMEPLAZA_BASE_URL "https://themeplaza.art"
|
#define THEMEPLAZA_BASE_URL "http://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
|
||||||
|
|
||||||
|
|||||||
@@ -49,8 +49,9 @@ 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 _padding2[0xb];
|
u8 shuffle_seedA[0xb];
|
||||||
bool shuffle;
|
u8 shuffle;
|
||||||
|
u8 shuffle_seedB[0xa];
|
||||||
} SaveData_dat_s;
|
} SaveData_dat_s;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -75,7 +76,8 @@ Result bgm_install(Entry_s theme);
|
|||||||
|
|
||||||
Result shuffle_install(Entry_List_s themes);
|
Result shuffle_install(Entry_List_s themes);
|
||||||
|
|
||||||
Result dump_theme(void);
|
Result dump_current_theme(void);
|
||||||
|
Result dump_all_themes(void);
|
||||||
|
|
||||||
void themes_check_installed(void * void_arg);
|
void themes_check_installed(void * void_arg);
|
||||||
|
|
||||||
|
|||||||
88
include/urls.h
Normal file
88
include/urls.h
Normal 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;
|
||||||
|
}
|
||||||
@@ -391,14 +391,7 @@ bool init_qr(void)
|
|||||||
|
|
||||||
if(mode != MODE_AMOUNT)
|
if(mode != MODE_AMOUNT)
|
||||||
{
|
{
|
||||||
char path_to_file[0x107] = {0};
|
save_zip_to_sd(filename, zip_size, zip_buf, mode);
|
||||||
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
|
||||||
|
|||||||
@@ -126,6 +126,7 @@ void init_screens(void)
|
|||||||
C2D_TextParse(&text[TEXT_INSTALL_LOADING_REMOTE_PREVIEW], staticBuf, "Downloading preview, please wait...");
|
C2D_TextParse(&text[TEXT_INSTALL_LOADING_REMOTE_PREVIEW], staticBuf, "Downloading preview, please wait...");
|
||||||
C2D_TextParse(&text[TEXT_INSTALL_LOADING_REMOTE_BGM], staticBuf, "Downloading BGM, please wait...");
|
C2D_TextParse(&text[TEXT_INSTALL_LOADING_REMOTE_BGM], staticBuf, "Downloading BGM, please wait...");
|
||||||
C2D_TextParse(&text[TEXT_INSTALL_DUMPING_THEME], staticBuf, "Dumping theme, please wait...");
|
C2D_TextParse(&text[TEXT_INSTALL_DUMPING_THEME], staticBuf, "Dumping theme, please wait...");
|
||||||
|
C2D_TextParse(&text[TEXT_INSTALL_DUMPING_ALL_THEMES], staticBuf, "Dumping official themes, please wait...");
|
||||||
|
|
||||||
for(int i = 0; i < TEXT_AMOUNT; i++)
|
for(int i = 0; i < TEXT_AMOUNT; i++)
|
||||||
C2D_TextOptimize(&text[i]);
|
C2D_TextOptimize(&text[i]);
|
||||||
@@ -275,7 +276,7 @@ void draw_base_interface(void)
|
|||||||
set_screen(top);
|
set_screen(top);
|
||||||
}
|
}
|
||||||
|
|
||||||
void throw_error(char* error, ErrorLevel level)
|
void throw_error(const char* error, ErrorLevel level)
|
||||||
{
|
{
|
||||||
Text bottom_text = TEXT_AMOUNT;
|
Text bottom_text = TEXT_AMOUNT;
|
||||||
Color text_color = COLOR_WHITE;
|
Color text_color = COLOR_WHITE;
|
||||||
|
|||||||
113
source/fs.c
113
source/fs.c
@@ -27,6 +27,7 @@
|
|||||||
#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>
|
||||||
@@ -351,3 +352,115 @@ 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);
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -401,8 +408,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];
|
||||||
}
|
}
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
@@ -648,13 +651,6 @@ int main(void)
|
|||||||
{
|
{
|
||||||
load_icons_first(current_list, false);
|
load_icons_first(current_list, false);
|
||||||
}
|
}
|
||||||
else if((kDown | kHeld) & KEY_DRIGHT)
|
|
||||||
{
|
|
||||||
draw_install(INSTALL_DUMPING_THEME);
|
|
||||||
Result res = dump_theme();
|
|
||||||
if (R_FAILED(res)) DEBUG("Dump theme result: %lx\n", res);
|
|
||||||
else load_lists(lists);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if(key_l)
|
else if(key_l)
|
||||||
{
|
{
|
||||||
@@ -677,6 +673,23 @@ int main(void)
|
|||||||
load_icons_first(current_list, false);
|
load_icons_first(current_list, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if(key_r)
|
||||||
|
{
|
||||||
|
if(((kDown | kHeld)) & KEY_DUP)
|
||||||
|
{
|
||||||
|
draw_install(INSTALL_DUMPING_THEME);
|
||||||
|
Result res = dump_current_theme();
|
||||||
|
if (R_FAILED(res)) DEBUG("Dump theme result: %lx\n", res);
|
||||||
|
else load_lists(lists);
|
||||||
|
}
|
||||||
|
else if(((kDown | kHeld)) & KEY_DDOWN)
|
||||||
|
{
|
||||||
|
draw_install(INSTALL_DUMPING_ALL_THEMES);
|
||||||
|
Result res = dump_all_themes();
|
||||||
|
if (R_FAILED(res)) DEBUG("Dump all themes result: %lx\n", res);
|
||||||
|
else load_lists(lists);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
166
source/remote.c
166
source/remote.c
@@ -24,11 +24,18 @@
|
|||||||
* 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] = {
|
||||||
{
|
{
|
||||||
@@ -324,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);
|
||||||
@@ -358,17 +368,8 @@ static void download_remote_entry(Entry_s * entry, EntryMode mode)
|
|||||||
}
|
}
|
||||||
free(download_url);
|
free(download_url);
|
||||||
|
|
||||||
char path_to_file[0x107] = { 0 };
|
save_zip_to_sd(filename, zip_size, zip_buf, mode);
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -443,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)
|
||||||
@@ -496,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;
|
||||||
@@ -524,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -588,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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -601,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
|
||||||
@@ -669,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;
|
||||||
}
|
}
|
||||||
@@ -738,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);
|
||||||
@@ -759,7 +746,6 @@ 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,
|
||||||
@@ -778,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;
|
||||||
@@ -790,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
|
||||||
@@ -852,64 +838,52 @@ 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))
|
||||||
{
|
{
|
||||||
DEBUG("httpcGetResponseHeader\n");
|
if (out->result_code == (long)0xD8A0A028L)
|
||||||
return HTTPC_ERROR;
|
present = 0;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DEBUG("httpcGetResponseHeader\n");
|
||||||
|
return HTTPC_ERROR;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// content_buf: Content-Disposition: attachment; ... filename=<filename>;? ...
|
// content_buf: Content-Disposition: attachment; ... filename=<filename>;? ...
|
||||||
|
|
||||||
char * filename = strstr(content_buf, "filename="); // filename=<filename>;? ...
|
if (present)
|
||||||
if (!filename)
|
|
||||||
{
|
{
|
||||||
const int max_chars = 250;
|
char * filename = strstr(content_buf, "filename="); // filename=<filename>;? ...
|
||||||
// needs to be heap allocated only because the call site is expected to free it
|
// in the extreme fringe case that filename is missing:
|
||||||
*out->filename = malloc(max_chars + 5); // + .zip and the null term
|
if (filename != NULL)
|
||||||
|
|
||||||
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);
|
filename = strpbrk(filename, "=") + 1; // <filename>;?
|
||||||
return ABORTED;
|
char * end = strpbrk(filename, ";");
|
||||||
|
if (end)
|
||||||
|
*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] == '"')
|
||||||
|
{
|
||||||
|
filename[strlen(filename) - 1] = '\0';
|
||||||
|
filename++;
|
||||||
|
}
|
||||||
|
|
||||||
|
*out->filename = malloc(strlen(filename) + 1);
|
||||||
|
strcpy(*out->filename, filename);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*out->filename = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
strcat(*out->filename, ".zip");
|
|
||||||
return SUCCESS;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
filename = strpbrk(filename, "=") + 1; // <filename>;?
|
|
||||||
char * end = strpbrk(filename, ";");
|
|
||||||
if (end)
|
|
||||||
*end = '\0'; // <filename>
|
|
||||||
|
|
||||||
if (filename[0] == '"')
|
|
||||||
// safe to assume the filename is quoted
|
|
||||||
{
|
{
|
||||||
filename[strlen(filename) - 1] = '\0';
|
*out->filename = NULL;
|
||||||
filename++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
char * illegal_char;
|
|
||||||
// filter out characters illegal in FAT32 filenames
|
|
||||||
while ((illegal_char = strpbrk(filename, "><\"?;:/\\+,.|[=]")))
|
|
||||||
*illegal_char = '-';
|
|
||||||
|
|
||||||
*out->filename = malloc(strlen(filename) + 1);
|
|
||||||
strcpy(*out->filename, filename);
|
|
||||||
}
|
}
|
||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
@@ -920,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;
|
||||||
@@ -956,16 +935,12 @@ 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);
|
||||||
@@ -1006,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)
|
||||||
@@ -1069,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)
|
||||||
|
|||||||
253
source/themes.c
253
source/themes.c
@@ -261,18 +261,24 @@ 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);
|
savedata->shuffle = (installmode & THEME_INSTALL_SHUFFLE) ? 1 : 0;
|
||||||
|
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);
|
||||||
@@ -328,7 +334,7 @@ dir_name_callback(void *data, const char ** ppMessage, const char * text, size_t
|
|||||||
return SWKBD_CALLBACK_OK;
|
return SWKBD_CALLBACK_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result dump_theme(void)
|
Result dump_current_theme(void)
|
||||||
{
|
{
|
||||||
const int max_chars = 255;
|
const int max_chars = 255;
|
||||||
char * output_dir = calloc(max_chars + 1, sizeof(char));
|
char * output_dir = calloc(max_chars + 1, sizeof(char));
|
||||||
@@ -415,6 +421,243 @@ Result dump_theme(void)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result dump_all_themes(void)
|
||||||
|
{
|
||||||
|
const u32 high_id = 0x0004008c;
|
||||||
|
u32 low_id = 0;
|
||||||
|
u8 regionCode, language;
|
||||||
|
Result res = CFGU_SecureInfoGetRegion(®ionCode);
|
||||||
|
if(R_FAILED(res))
|
||||||
|
return res;
|
||||||
|
|
||||||
|
res = CFGU_GetSystemLanguage(&language);
|
||||||
|
if(R_FAILED(res))
|
||||||
|
return res;
|
||||||
|
|
||||||
|
switch(regionCode)
|
||||||
|
{
|
||||||
|
case CFG_REGION_JPN:
|
||||||
|
low_id = 0x00008200;
|
||||||
|
break;
|
||||||
|
case CFG_REGION_USA:
|
||||||
|
low_id = 0x00008f00;
|
||||||
|
break;
|
||||||
|
case CFG_REGION_EUR:
|
||||||
|
low_id = 0x00009800;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* region_arr[4] = {
|
||||||
|
"JPN",
|
||||||
|
"USA",
|
||||||
|
"EUR",
|
||||||
|
"AUS",
|
||||||
|
};
|
||||||
|
|
||||||
|
const char* language_arr[12] = {
|
||||||
|
"jp",
|
||||||
|
"en",
|
||||||
|
"fr",
|
||||||
|
"de",
|
||||||
|
"it",
|
||||||
|
"es",
|
||||||
|
"zh",
|
||||||
|
"ko",
|
||||||
|
"nl",
|
||||||
|
"pt",
|
||||||
|
"ru",
|
||||||
|
"tw",
|
||||||
|
};
|
||||||
|
|
||||||
|
res = amAppInit();
|
||||||
|
if(R_FAILED(res))
|
||||||
|
return res;
|
||||||
|
|
||||||
|
Icon_s* smdh_data = calloc(1, sizeof(Icon_s));
|
||||||
|
smdh_data->_padding1[0] = 0x53; // SMDH magic
|
||||||
|
smdh_data->_padding1[1] = 0x4d;
|
||||||
|
smdh_data->_padding1[2] = 0x44;
|
||||||
|
smdh_data->_padding1[3] = 0x48;
|
||||||
|
|
||||||
|
utf8_to_utf16(smdh_data->author, (u8*)"Nintendo", 0x40);
|
||||||
|
utf8_to_utf16(smdh_data->desc, (u8*)"Official theme. For personal use only. Do not redistribute.", 0x80);
|
||||||
|
|
||||||
|
for(u32 dlc_index = 0; dlc_index <= 0xFF; ++dlc_index)
|
||||||
|
{
|
||||||
|
const u64 titleId = ((u64)high_id << 32) | (low_id | dlc_index);
|
||||||
|
|
||||||
|
u32 count = 0;
|
||||||
|
res = AMAPP_GetDLCContentInfoCount(&count, MEDIATYPE_SD, titleId);
|
||||||
|
if(res == (Result)0xd8a083fa)
|
||||||
|
{
|
||||||
|
res = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if(R_FAILED(res))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
AM_ContentInfo* contentInfos = calloc(count, sizeof(AM_ContentInfo));
|
||||||
|
u32 readcount = 0;
|
||||||
|
res = AMAPP_ListDLCContentInfos(&readcount, MEDIATYPE_SD, titleId, count, 0, contentInfos);
|
||||||
|
if(R_FAILED(res))
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
|||||||
Reference in New Issue
Block a user