UX Overhaul (#305)

* Fix bgm checking bug

* Converted install and menu options into button menus rather than combos

* Fix sort order

* Various touch screen changes so that most functions can be done via touch

 * Dim theme list when navigating menus

* Translation framework implemented

* bug when no themes loaded fixed

* Fix theme preview regression introduced in d037691
This commit is contained in:
Alex Taber
2024-05-12 14:24:43 -04:00
committed by GitHub
parent 546d459696
commit a43cbcca74
23 changed files with 1060 additions and 536 deletions

BIN
assets/back.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 696 B

BIN
assets/bgm_only.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

BIN
assets/dump.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

Before

Width:  |  Height:  |  Size: 404 B

After

Width:  |  Height:  |  Size: 404 B

View File

Before

Width:  |  Height:  |  Size: 179 B

After

Width:  |  Height:  |  Size: 179 B

BIN
assets/qr.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

View File

@@ -11,10 +11,10 @@ battery4.png
battery5.png battery5.png
browse.png browse.png
charging.png charging.png
download.png install.png
exit.png exit.png
installed.png installed.png
list.png menu.png
no_home.png no_home.png
preview.png preview.png
select.png select.png
@@ -22,3 +22,7 @@ shuffle.png
shuffle_no_bgm.png shuffle_no_bgm.png
sort.png sort.png
start.png start.png
qr.png
bgm_only.png
back.png
dump.png

View File

@@ -54,6 +54,8 @@ static inline int max(const int a, const int b)
#define FASTSCROLL_WAIT 1e8 #define FASTSCROLL_WAIT 1e8
#define BETWEEN(min, x, max) (min < x && x < max)
typedef enum { typedef enum {
MODE_THEMES = 0, MODE_THEMES = 0,
MODE_SPLASHES, MODE_SPLASHES,
@@ -61,6 +63,14 @@ typedef enum {
MODE_AMOUNT, MODE_AMOUNT,
} EntryMode; } EntryMode;
typedef enum {
DRAW_MODE_LIST = 0,
DRAW_MODE_INSTALL,
DRAW_MODE_EXTRA,
DRAW_MODE_AMOUNT,
} DrawMode;
extern const char * main_paths[MODE_AMOUNT]; extern const char * main_paths[MODE_AMOUNT];
extern const int entries_per_screen_v[MODE_AMOUNT]; extern const int entries_per_screen_v[MODE_AMOUNT];
extern const int entries_per_screen_h[MODE_AMOUNT]; extern const int entries_per_screen_h[MODE_AMOUNT];

View File

@@ -4,6 +4,6 @@
#include "common.h" #include "common.h"
size_t bin_to_abgr(char ** bufp, size_t size); size_t bin_to_abgr(char ** bufp, size_t size);
size_t png_to_abgr(char ** bufp, size_t size); size_t png_to_abgr(char ** bufp, size_t size, u32 *height);
#endif #endif

View File

@@ -163,7 +163,7 @@ 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(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, DrawMode draw_mode);
void draw_preview(C2D_Image preview, int preview_offset); void draw_preview(C2D_Image preview, int preview_offset);
@@ -178,6 +178,6 @@ void draw_home(u64 start_time, u64 cur_time);
void draw_base_interface(void); void draw_base_interface(void);
void draw_grid_interface(Entry_List_s * list, Instructions_s instructions); void draw_grid_interface(Entry_List_s * list, Instructions_s instructions);
void draw_interface(Entry_List_s * list, Instructions_s instructions); void draw_interface(Entry_List_s * list, Instructions_s instructions, DrawMode draw_mode);
#endif #endif

View File

@@ -1,166 +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.
*/
#ifndef INSTRUCTIONS_H
#define INSTRUCTIONS_H
#include "draw.h"
#include "colors.h"
Instructions_s normal_instructions[MODE_AMOUNT] = {
{
.info_line = NULL,
.instructions = {
{
"\uE000 Hold to install",
"\uE001 Queue shuffle theme"
},
{
"\uE002 Hold for more",
"\uE003 Preview theme"
},
{
"\uE004 Switch to splashes",
"\uE005 Scan QR code"
},
{
"Exit",
"Delete from SD"
}
}
},
{
.info_line = NULL,
.instructions = {
{
"\uE000 Install splash",
"\uE001 Delete installed splash"
},
{
"\uE002 Hold for more",
"\uE003 Preview splash"
},
{
"\uE004 Switch to themes",
"\uE005 Scan QR code"
},
{
"Exit",
"Delete from SD"
}
}
}
};
Instructions_s install_instructions = {
.info_line = "Release \uE000 to cancel or hold \uE006 and release \uE000 to install",
.instructions = {
{
"\uE079 Normal install",
"\uE07A Shuffle install"
},
{
"\uE07B BGM-only install",
"\uE07C No-BGM install"
},
{
NULL,
NULL
},
{
"Exit",
NULL
}
}
};
Instructions_s extra_instructions[3] = {
{
.info_line = "Release \uE002 to cancel or hold \uE006 and release \uE002 to sort",
.instructions = {
{
"\uE079 Sort by name",
"\uE07A Sort by author"
},
{
"\uE07B Sort by filename",
NULL
},
{
NULL,
NULL
},
{
"Exit",
NULL
}
}
},
{
.info_line = "Release \uE002 to cancel or hold \uE006 and release \uE002 to do stuff",
.instructions = {
{
"\uE079 Jump in the list",
"\uE07A Reload broken icons"
},
{
"\uE07B Browse ThemePlaza",
NULL
},
{
"\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
},
{
"Exit",
NULL
}
}
}
};
#endif

View File

@@ -68,7 +68,7 @@ void copy_texture_data(C3D_Tex * texture, const u16 * src, const Entry_Icon_s *
void parse_smdh(Icon_s * icon, Entry_s * entry, const u16 * fallback_name); void parse_smdh(Icon_s * icon, Entry_s * entry, const u16 * fallback_name);
bool load_preview_from_buffer(char * row_pointers, u32 size, C2D_Image * preview_image, int * preview_offset); bool load_preview_from_buffer(char * row_pointers, u32 size, C2D_Image * preview_image, int * preview_offset, int height);
bool load_preview(const Entry_List_s * list, C2D_Image * preview_image, int * preview_offset); bool load_preview(const Entry_List_s * list, C2D_Image * preview_image, int * preview_offset);
void free_preview(C2D_Image preview_image); void free_preview(C2D_Image preview_image);
Result load_audio(const Entry_s *, audio_s *); Result load_audio(const Entry_s *, audio_s *);

188
include/ui_strings.h Normal file
View File

@@ -0,0 +1,188 @@
/*
* 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.
*/
#ifndef UISTRINGS_H
#define UISTRINGS_H
#include "colors.h"
#include "draw.h"
#include "common.h"
#define SPLASHES_STRINGS 2
#define THEMES_STRINGS 6
typedef struct {
const char *quit;
const char *thread_error;
const char *zip_not_theme_splash;
const char *file_not_zip;
const char *download_failed;
} Camera_Strings_s;
typedef struct {
const char *theme_mode;
const char *splash_mode;
const char *no_themes;
const char *no_splashes;
const char *qr_download;
const char *switch_splashes;
const char *switch_themes;
const char *quit;
const char *by;
const char *selected;
const char *sel;
const char *tp_theme_mode;
const char *tp_splash_mode;
const char *search;
const char *page;
const char *err_quit;
const char *warn_continue;
const char *yes_no;
const char *load_themes;
const char *load_splash;
const char *load_icons;
const char *install_splash;
const char *delete_splash;
const char *install_theme;
const char *install_shuffle;
const char *install_bgm;
const char *install_no_bgm;
const char *downloading;
const char *checking_dl;
const char *delete_sd;
const char *download_themes;
const char *download_splashes;
const char *download_preview;
const char *download_bgm;
const char *dump_single;
const char *dump_all_official;
float start_pos;
} Draw_Strings_s;
typedef struct {
const char *illegal_input;
const char *new_or_overwrite;
const char *cancel;
const char *overwrite;
const char *rename;
const char *swkbd_fail;
const char *sd_full;
const char *fs_error;
} FS_Strings_s;
typedef struct {
const char *no_preview;
} Loading_Strings_s;
typedef struct {
const char *position_too_big;
const char *position_zero;
const char *jump_q;
const char *cancel;
const char *jump;
const char *no_theme_extdata;
const char *loading_qr;
const char *no_wifi;
const char *qr_homebrew;
const char *camera_broke;
const char *too_many_themes;
const char *not_enough_themes;
const char *uninstall_confirm;
const char *delete_confirm;
} Main_Strings_s;
typedef struct {
const char *no_results;
const char *check_wifi;
const char *new_page_big;
const char *new_page_zero;
const char *jump_page;
const char *cancel;
const char *jump;
const char *tags;
const char *search;
const char *parental_fail;
const char *zip_not_found;
const char *generic_httpc_error;
const char *http303_tp;
const char *http303;
const char *http404;
const char *http_err_url;
const char *http_errcode_generic;
const char *http401;
const char *http403;
const char *http407;
const char *http414;
const char *http418;
const char *http426;
const char *http451;
const char *http500;
const char *http502;
const char *http503;
const char *http504;
const char *http_unexpected;
} Remote_Strings_s;
typedef struct {
const char *no_splash_found;
const char *splash_disabled;
} Splashes_Strings_s;
typedef struct {
const char *no_body_found;
const char *mono_warn;
const char *illegal_char;
const char *name_folder;
const char *cancel;
const char *done;
} Themes_Strings_s;
typedef struct {
Instructions_s normal_instructions[MODE_AMOUNT];
Instructions_s install_instructions;
Instructions_s extra_instructions[3];
Camera_Strings_s camera;
Draw_Strings_s draw;
FS_Strings_s fs;
Loading_Strings_s loading;
Main_Strings_s main;
Remote_Strings_s remote;
Instructions_s remote_instructions[MODE_AMOUNT];
Instructions_s remote_extra_instructions;
Splashes_Strings_s splashes;
Themes_Strings_s themes;
} Language_s;
typedef enum {
LANGUAGE_EN,
LANGUAGE_AMOUNT,
} Language_Name;
Language_s init_strings(CFG_Language lang);
extern Language_s language;
#endif

View File

@@ -32,6 +32,7 @@
#include "fs.h" #include "fs.h"
#include "loading.h" #include "loading.h"
#include "remote.h" #include "remote.h"
#include "ui_strings.h"
#include <archive.h> #include <archive.h>
#include <archive_entry.h> #include <archive_entry.h>
@@ -200,7 +201,7 @@ static void update_ui(void * arg)
C2D_DrawImageAt((C2D_Image){ &tex, &subt3x }, 0.0f, 0.0f, 0.4f, NULL, 1.0f, 1.0f); C2D_DrawImageAt((C2D_Image){ &tex, &subt3x }, 0.0f, 0.0f, 0.4f, NULL, 1.0f, 1.0f);
set_screen(bottom); set_screen(bottom);
draw_text_center(GFX_BOTTOM, 4, 0.5, 0.5, 0.5, colors[COLOR_WHITE], "Press \uE005 To Quit"); draw_text_center(GFX_BOTTOM, 4, 0.5, 0.5, 0.5, colors[COLOR_WHITE], language.camera.quit);
end_frame(); end_frame();
} }
@@ -212,7 +213,7 @@ static bool start_capture_cam(qr_data * data)
{ {
if((data->cam_thread = threadCreate(capture_cam_thread, data, 0x10000, 0x1A, 1, false)) == NULL) if((data->cam_thread = threadCreate(capture_cam_thread, data, 0x10000, 0x1A, 1, false)) == NULL)
{ {
throw_error("Capture cam thread creation failed\nPlease report this to the developers", ERROR_LEVEL_ERROR); throw_error(language.camera.thread_error, ERROR_LEVEL_ERROR);
LightEvent_Signal(&data->event_cam_info); LightEvent_Signal(&data->event_cam_info);
LightEvent_Signal(&data->event_ui_info); LightEvent_Signal(&data->event_ui_info);
return false; return false;
@@ -397,18 +398,18 @@ bool init_qr(void)
} }
else else
{ {
throw_error("Zip downloaded is neither\na splash nor a theme.", ERROR_LEVEL_WARNING); throw_error(language.camera.zip_not_theme_splash, ERROR_LEVEL_WARNING);
} }
} }
else else
{ {
throw_error("File downloaded isn't a zip.", ERROR_LEVEL_WARNING); throw_error(language.camera.file_not_zip, ERROR_LEVEL_WARNING);
} }
free(zip_buf); free(zip_buf);
} }
else else
{ {
throw_error("Download failed.", ERROR_LEVEL_WARNING); throw_error(language.camera.download_failed, ERROR_LEVEL_WARNING);
} }
free(filename); free(filename);

View File

@@ -49,7 +49,7 @@ size_t bin_to_abgr(char ** bufp, size_t size)
return out_size; return out_size;
} }
size_t png_to_abgr(char ** bufp, size_t size) size_t png_to_abgr(char ** bufp, size_t size, u32 *height)
{ {
size_t out_size = 0; size_t out_size = 0;
if(size < 8 || png_sig_cmp((png_bytep)*bufp, 0, 8)) if(size < 8 || png_sig_cmp((png_bytep)*bufp, 0, 8))
@@ -70,7 +70,7 @@ size_t png_to_abgr(char ** bufp, size_t size)
png_read_info(png, info); png_read_info(png, info);
u32 width = png_get_image_width(png, info); u32 width = png_get_image_width(png, info);
u32 height = png_get_image_height(png, info); *height = png_get_image_height(png, info);
png_byte color_type = png_get_color_type(png, info); png_byte color_type = png_get_color_type(png, info);
png_byte bit_depth = png_get_bit_depth(png, info); png_byte bit_depth = png_get_bit_depth(png, info);
@@ -107,10 +107,10 @@ size_t png_to_abgr(char ** bufp, size_t size)
png_read_update_info(png, info); png_read_update_info(png, info);
row_pointers = malloc(sizeof(png_bytep) * height); row_pointers = malloc(sizeof(png_bytep) * *height);
out_size = sizeof(u32) * (width * height); out_size = sizeof(u32) * (width * *height);
u32 * out = malloc(out_size); u32 * out = malloc(out_size);
for(u32 y = 0; y < height; y++) for(u32 y = 0; y < *height; y++)
{ {
row_pointers[y] = (png_bytep)(out + (width * y)); row_pointers[y] = (png_bytep)(out + (width * y));
} }

View File

@@ -27,6 +27,7 @@
#include "draw.h" #include "draw.h"
#include "unicode.h" #include "unicode.h"
#include "colors.h" #include "colors.h"
#include "ui_strings.h"
#include "sprites.h" #include "sprites.h"
@@ -77,56 +78,56 @@ void init_screens(void)
C2D_TextParse(&text[TEXT_VERSION], staticBuf, VERSION); C2D_TextParse(&text[TEXT_VERSION], staticBuf, VERSION);
C2D_TextParse(&text[TEXT_THEME_MODE], staticBuf, "Theme mode"); C2D_TextParse(&text[TEXT_THEME_MODE], staticBuf, language.draw.theme_mode);
C2D_TextParse(&text[TEXT_SPLASH_MODE], staticBuf, "Splash mode"); C2D_TextParse(&text[TEXT_SPLASH_MODE], staticBuf, language.draw.splash_mode);
C2D_TextParse(&text[TEXT_NO_THEME_FOUND], staticBuf, "No theme found"); C2D_TextParse(&text[TEXT_NO_THEME_FOUND], staticBuf, language.draw.no_themes);
C2D_TextParse(&text[TEXT_NO_SPLASH_FOUND], staticBuf, "No splash found"); C2D_TextParse(&text[TEXT_NO_SPLASH_FOUND], staticBuf, language.draw.no_splashes);
C2D_TextParse(&text[TEXT_DOWNLOAD_FROM_QR], staticBuf, "Press \uE005 to download from QR"); C2D_TextParse(&text[TEXT_DOWNLOAD_FROM_QR], staticBuf, language.draw.qr_download);
C2D_TextParse(&text[TEXT_SWITCH_TO_SPLASHES], staticBuf, "Or \uE004 to switch to splashes"); C2D_TextParse(&text[TEXT_SWITCH_TO_SPLASHES], staticBuf, language.draw.switch_splashes);
C2D_TextParse(&text[TEXT_SWITCH_TO_THEMES], staticBuf, "Or \uE004 to switch to themes"); C2D_TextParse(&text[TEXT_SWITCH_TO_THEMES], staticBuf, language.draw.switch_themes);
C2D_TextParse(&text[TEXT_OR_START_TO_QUIT], staticBuf, "Or to quit"); C2D_TextParse(&text[TEXT_OR_START_TO_QUIT], staticBuf, language.draw.quit);
C2D_TextParse(&text[TEXT_BY_AUTHOR], staticBuf, "By "); C2D_TextParse(&text[TEXT_BY_AUTHOR], staticBuf, language.draw.by);
C2D_TextParse(&text[TEXT_SELECTED], staticBuf, "Selected:"); C2D_TextParse(&text[TEXT_SELECTED], staticBuf, language.draw.selected);
C2D_TextParse(&text[TEXT_SELECTED_SHORT], staticBuf, "Sel.:"); C2D_TextParse(&text[TEXT_SELECTED_SHORT], staticBuf, language.draw.sel);
C2D_TextParse(&text[TEXT_THEMEPLAZA_THEME_MODE], staticBuf, "ThemePlaza Theme mode"); C2D_TextParse(&text[TEXT_THEMEPLAZA_THEME_MODE], staticBuf, language.draw.tp_theme_mode);
C2D_TextParse(&text[TEXT_THEMEPLAZA_SPLASH_MODE], staticBuf, "ThemePlaza Splash mode"); C2D_TextParse(&text[TEXT_THEMEPLAZA_SPLASH_MODE], staticBuf, language.draw.tp_splash_mode);
C2D_TextParse(&text[TEXT_SEARCH], staticBuf, "Search..."); C2D_TextParse(&text[TEXT_SEARCH], staticBuf, language.draw.search);
C2D_TextParse(&text[TEXT_PAGE], staticBuf, "Page:"); C2D_TextParse(&text[TEXT_PAGE], staticBuf, language.draw.page);
C2D_TextParse(&text[TEXT_ERROR_QUIT], staticBuf, "Press \uE000 to quit."); C2D_TextParse(&text[TEXT_ERROR_QUIT], staticBuf, language.draw.err_quit);
C2D_TextParse(&text[TEXT_ERROR_CONTINUE], staticBuf, "Press \uE000 to continue."); C2D_TextParse(&text[TEXT_ERROR_CONTINUE], staticBuf, language.draw.warn_continue);
C2D_TextParse(&text[TEXT_CONFIRM_YES_NO], staticBuf, "\uE000 Yes \uE001 No"); C2D_TextParse(&text[TEXT_CONFIRM_YES_NO], staticBuf, language.draw.yes_no);
C2D_TextParse(&text[TEXT_INSTALL_LOADING_THEMES], staticBuf, "Loading themes, please wait..."); C2D_TextParse(&text[TEXT_INSTALL_LOADING_THEMES], staticBuf, language.draw.load_themes);
C2D_TextParse(&text[TEXT_INSTALL_LOADING_SPLASHES], staticBuf, "Loading splashes, please wait..."); C2D_TextParse(&text[TEXT_INSTALL_LOADING_SPLASHES], staticBuf, language.draw.load_splash);
C2D_TextParse(&text[TEXT_INSTALL_LOADING_ICONS], staticBuf, "Loading icons, please wait..."); C2D_TextParse(&text[TEXT_INSTALL_LOADING_ICONS], staticBuf, language.draw.load_icons);
C2D_TextParse(&text[TEXT_INSTALL_SPLASH], staticBuf, "Installing a splash..."); C2D_TextParse(&text[TEXT_INSTALL_SPLASH], staticBuf, language.draw.install_splash);
C2D_TextParse(&text[TEXT_INSTALL_SPLASH_DELETE], staticBuf, "Deleting installed splash..."); C2D_TextParse(&text[TEXT_INSTALL_SPLASH_DELETE], staticBuf, language.draw.delete_splash);
C2D_TextParse(&text[TEXT_INSTALL_SINGLE], staticBuf, "Installing a single theme..."); C2D_TextParse(&text[TEXT_INSTALL_SINGLE], staticBuf, language.draw.install_theme);
C2D_TextParse(&text[TEXT_INSTALL_SHUFFLE], staticBuf, "Installing shuffle themes..."); C2D_TextParse(&text[TEXT_INSTALL_SHUFFLE], staticBuf, language.draw.install_shuffle);
C2D_TextParse(&text[TEXT_INSTALL_BGM], staticBuf, "Installing BGM-only theme..."); C2D_TextParse(&text[TEXT_INSTALL_BGM], staticBuf, language.draw.install_bgm);
C2D_TextParse(&text[TEXT_INSTALL_NO_BGM], staticBuf, "Installing theme without BGM..."); C2D_TextParse(&text[TEXT_INSTALL_NO_BGM], staticBuf, language.draw.install_no_bgm);
C2D_TextParse(&text[TEXT_INSTALL_DOWNLOAD], staticBuf, "Downloading..."); C2D_TextParse(&text[TEXT_INSTALL_DOWNLOAD], staticBuf, language.draw.downloading);
C2D_TextParse(&text[TEXT_INSTALL_CHECKING_DOWNLOAD], staticBuf, "Checking downloaded file..."); C2D_TextParse(&text[TEXT_INSTALL_CHECKING_DOWNLOAD], staticBuf, language.draw.checking_dl);
C2D_TextParse(&text[TEXT_INSTALL_ENTRY_DELETE], staticBuf, "Deleting from SD..."); C2D_TextParse(&text[TEXT_INSTALL_ENTRY_DELETE], staticBuf, language.draw.delete_sd);
C2D_TextParse(&text[TEXT_INSTALL_LOADING_REMOTE_THEMES], staticBuf, "Downloading theme list, please wait..."); C2D_TextParse(&text[TEXT_INSTALL_LOADING_REMOTE_THEMES], staticBuf, language.draw.download_themes);
C2D_TextParse(&text[TEXT_INSTALL_LOADING_REMOTE_SPLASHES], staticBuf, "Downloading splash list, please wait..."); C2D_TextParse(&text[TEXT_INSTALL_LOADING_REMOTE_SPLASHES], staticBuf, language.draw.download_splashes);
C2D_TextParse(&text[TEXT_INSTALL_LOADING_REMOTE_PREVIEW], staticBuf, "Downloading preview, please wait..."); C2D_TextParse(&text[TEXT_INSTALL_LOADING_REMOTE_PREVIEW], staticBuf, language.draw.download_preview);
C2D_TextParse(&text[TEXT_INSTALL_LOADING_REMOTE_BGM], staticBuf, "Downloading BGM, please wait..."); C2D_TextParse(&text[TEXT_INSTALL_LOADING_REMOTE_BGM], staticBuf, language.draw.download_bgm);
C2D_TextParse(&text[TEXT_INSTALL_DUMPING_THEME], staticBuf, "Dumping theme, please wait..."); C2D_TextParse(&text[TEXT_INSTALL_DUMPING_THEME], staticBuf, language.draw.dump_single);
C2D_TextParse(&text[TEXT_INSTALL_DUMPING_ALL_THEMES], staticBuf, "Dumping official themes, please wait..."); C2D_TextParse(&text[TEXT_INSTALL_DUMPING_ALL_THEMES], staticBuf, language.draw.dump_all_official);
for(int i = 0; i < TEXT_AMOUNT; i++) for(int i = 0; i < TEXT_AMOUNT; i++)
C2D_TextOptimize(&text[i]); C2D_TextOptimize(&text[i]);
@@ -318,12 +319,12 @@ 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, DrawMode draw_mode)
{ {
while(aptMainLoop()) while(aptMainLoop())
{ {
Instructions_s instructions = {0}; Instructions_s instructions = {0};
draw_interface(list, instructions); draw_interface(list, instructions, draw_mode);
set_screen(top); set_screen(top);
draw_text_center(GFX_TOP, BUTTONS_Y_LINE_1, 0.5f, 0.7f, 0.7f, colors[COLOR_YELLOW], conf_msg); draw_text_center(GFX_TOP, BUTTONS_Y_LINE_1, 0.5f, 0.7f, 0.7f, colors[COLOR_YELLOW], conf_msg);
draw_c2d_text_center(GFX_TOP, BUTTONS_Y_LINE_3, 0.5f, 0.6f, 0.6f, colors[COLOR_WHITE], &text[TEXT_CONFIRM_YES_NO]); draw_c2d_text_center(GFX_TOP, BUTTONS_Y_LINE_3, 0.5f, 0.6f, 0.6f, colors[COLOR_WHITE], &text[TEXT_CONFIRM_YES_NO]);
@@ -535,7 +536,7 @@ void draw_grid_interface(Entry_List_s * list, Instructions_s instructions)
draw_c2d_text(7, 3, 0.5f, 0.6f, 0.6f, colors[COLOR_WHITE], &text[TEXT_SEARCH]); draw_c2d_text(7, 3, 0.5f, 0.6f, 0.6f, colors[COLOR_WHITE], &text[TEXT_SEARCH]);
draw_image(sprites_list_idx, 320-96, 0); draw_image(sprites_back_idx, 320-96, 0);
draw_image(sprites_exit_idx, 320-72, 0); draw_image(sprites_exit_idx, 320-72, 0);
draw_image(sprites_preview_idx, 320-48, 0); draw_image(sprites_preview_idx, 320-48, 0);
@@ -601,7 +602,7 @@ void draw_grid_interface(Entry_List_s * list, Instructions_s instructions)
draw_c2d_text(176, 219, 0.5, 0.6, 0.6, colors[COLOR_WHITE], &text[TEXT_PAGE]); draw_c2d_text(176, 219, 0.5, 0.6, 0.6, colors[COLOR_WHITE], &text[TEXT_PAGE]);
} }
void draw_interface(Entry_List_s * list, Instructions_s instructions) void draw_interface(Entry_List_s * list, Instructions_s instructions, DrawMode draw_mode)
{ {
draw_base_interface(); draw_base_interface();
EntryMode current_mode = list->mode; EntryMode current_mode = list->mode;
@@ -613,7 +614,7 @@ void draw_interface(Entry_List_s * list, Instructions_s instructions)
draw_c2d_text_center(GFX_TOP, 4, 0.5f, 0.5f, 0.5f, colors[COLOR_WHITE], mode_string[current_mode]); draw_c2d_text_center(GFX_TOP, 4, 0.5f, 0.5f, 0.5f, colors[COLOR_WHITE], mode_string[current_mode]);
if(list->entries == NULL) if(list->entries == NULL || list->entries_count == 0)
{ {
C2D_Text * mode_found_string[MODE_AMOUNT] = { C2D_Text * mode_found_string[MODE_AMOUNT] = {
&text[TEXT_NO_THEME_FOUND], &text[TEXT_NO_THEME_FOUND],
@@ -633,7 +634,7 @@ void draw_interface(Entry_List_s * list, Instructions_s instructions)
C2D_ImageTint yellow_tint; C2D_ImageTint yellow_tint;
C2D_PlainImageTint(&yellow_tint, colors[COLOR_YELLOW], 1.0f); C2D_PlainImageTint(&yellow_tint, colors[COLOR_YELLOW], 1.0f);
C2D_SpriteSetPos(&sprite_start, 162, 173); C2D_SpriteSetPos(&sprite_start, language.draw.start_pos, 173);
C2D_SpriteSetScale(&sprite_start, 1.25f, 1.4f); C2D_SpriteSetScale(&sprite_start, 1.25f, 1.4f);
C2D_DrawSpriteTinted(&sprite_start, &yellow_tint); C2D_DrawSpriteTinted(&sprite_start, &yellow_tint);
C2D_SpriteSetScale(&sprite_start, 1.0f, 1.0f); C2D_SpriteSetScale(&sprite_start, 1.0f, 1.0f);
@@ -641,7 +642,7 @@ void draw_interface(Entry_List_s * list, Instructions_s instructions)
set_screen(bottom); set_screen(bottom);
draw_image(sprites_sort_idx, 320-144, 0); draw_image(sprites_sort_idx, 320-144, 0);
draw_image(sprites_download_idx, 320-120, 0); draw_image(sprites_qr_idx, 320-120, 0);
draw_image(sprites_browse_idx, 320-96, 0); draw_image(sprites_browse_idx, 320-96, 0);
draw_image(sprites_exit_idx, 320-72, 0); draw_image(sprites_exit_idx, 320-72, 0);
draw_image(sprites_preview_idx, 320-48, 0); draw_image(sprites_preview_idx, 320-48, 0);
@@ -667,18 +668,41 @@ void draw_interface(Entry_List_s * list, Instructions_s instructions)
free(shuffle_count_string); free(shuffle_count_string);
} }
draw_image(sprites_sort_idx, 320-144, 0); if (draw_mode == DRAW_MODE_LIST)
draw_image(sprites_download_idx, 320-120, 0); {
draw_image(sprites_browse_idx, 320-96, 0); draw_image(sprites_install_idx, 320-120, 0);
draw_image(sprites_exit_idx, 320-72, 0); draw_image(sprites_qr_idx, 320-96, 0);
draw_image(sprites_preview_idx, 320-48, 0); draw_image(sprites_exit_idx, 320-72, 0);
draw_image(sprites_preview_idx, 320-48, 0);
draw_text(320-24+2.5, -3, 0.6, 1.0f, 0.9f, colors[COLOR_WHITE], mode_switch_char[!current_mode]); draw_text(320-24+2.5, -3, 0.6, 1.0f, 0.9f, colors[COLOR_WHITE], mode_switch_char[!current_mode]);
draw_image(sprites_menu_idx, 320-144, 0);
if (current_mode == MODE_THEMES)
{
draw_image(sprites_shuffle_idx, 320-168, 0);
}
}
else
{
if (draw_mode == DRAW_MODE_INSTALL)
{
draw_image(sprites_install_idx, 320-24, 0);
draw_image(sprites_shuffle_idx, 320-48, 0);
draw_image(sprites_shuffle_no_bgm_idx, 320-72, 0);
draw_image(sprites_bgm_only_idx, 320-96, 0);
draw_image(sprites_back_idx, 320-120, 0);
} else if (draw_mode == DRAW_MODE_EXTRA)
{
draw_image(sprites_browse_idx, 320-24, 0);
draw_image(sprites_dump_idx, 320-48, 0);
draw_image(sprites_sort_idx, 320-72, 0);
draw_image(sprites_back_idx, 320-96, 0);
}
}
// Show arrows if there are themes out of bounds // Show arrows if there are themes out of bounds
//---------------------------------------------------------------- //----------------------------------------------------------------
if(list->scroll > 0) if(list->scroll > 0)
draw_image(sprites_arrow_up_idx, 152, 4); draw_image(sprites_arrow_up_idx, 136, 220);
if(list->scroll + list->entries_loaded < list->entries_count) if(list->scroll + list->entries_loaded < list->entries_count)
draw_image(sprites_arrow_down_idx, 152, 220); draw_image(sprites_arrow_down_idx, 152, 220);
@@ -763,4 +787,8 @@ void draw_interface(Entry_List_s * list, Instructions_s instructions)
draw_c2d_text(176, 219, 0.5, 0.6, 0.6, colors[COLOR_WHITE], &text[TEXT_SELECTED]); draw_c2d_text(176, 219, 0.5, 0.6, 0.6, colors[COLOR_WHITE], &text[TEXT_SELECTED]);
else else
draw_c2d_text(176, 219, 0.5, 0.6, 0.6, colors[COLOR_WHITE], &text[TEXT_SELECTED_SHORT]); draw_c2d_text(176, 219, 0.5, 0.6, 0.6, colors[COLOR_WHITE], &text[TEXT_SELECTED_SHORT]);
if(draw_mode != DRAW_MODE_LIST)
{
C2D_DrawRectSolid(0, 24, 1.0f, 320, 240-48, C2D_Color32(0, 0, 0, 128));
}
} }

View File

@@ -29,6 +29,7 @@
#include "fs.h" #include "fs.h"
#include "draw.h" #include "draw.h"
#include "unicode.h" #include "unicode.h"
#include "ui_strings.h"
#include <archive.h> #include <archive.h>
#include <archive_entry.h> #include <archive_entry.h>
@@ -372,7 +373,8 @@ static SwkbdCallbackResult fat32filter(void * user, const char ** ppMessage, con
{ {
(void)textlen; (void)textlen;
(void)user; (void)user;
*ppMessage = "Input must not contain:\n" ILLEGAL_CHARS;
*ppMessage = language.fs.illegal_input;
if(strpbrk(text, ILLEGAL_CHARS)) if(strpbrk(text, ILLEGAL_CHARS))
{ {
DEBUG("illegal filename: %s\n", text); DEBUG("illegal filename: %s\n", text);
@@ -434,12 +436,12 @@ renamed:
SwkbdState swkbd; SwkbdState swkbd;
swkbdInit(&swkbd, SWKBD_TYPE_NORMAL, 3, max_chars / 2); swkbdInit(&swkbd, SWKBD_TYPE_NORMAL, 3, max_chars / 2);
swkbdSetHintText(&swkbd, "Choose a new filename or tap Overwrite"); swkbdSetHintText(&swkbd, language.fs.new_or_overwrite);
swkbdSetFeatures(&swkbd, SWKBD_PREDICTIVE_INPUT | SWKBD_DARKEN_TOP_SCREEN); swkbdSetFeatures(&swkbd, SWKBD_PREDICTIVE_INPUT | SWKBD_DARKEN_TOP_SCREEN);
swkbdSetButton(&swkbd, SWKBD_BUTTON_LEFT, "Cancel", false); swkbdSetButton(&swkbd, SWKBD_BUTTON_LEFT, language.fs.cancel, false);
swkbdSetButton(&swkbd, SWKBD_BUTTON_MIDDLE, "Overwrite", false); swkbdSetButton(&swkbd, SWKBD_BUTTON_MIDDLE, language.fs.overwrite, false);
swkbdSetButton(&swkbd, SWKBD_BUTTON_RIGHT, "Rename", true); swkbdSetButton(&swkbd, SWKBD_BUTTON_RIGHT, language.fs.rename, true);
swkbdSetValidation(&swkbd, SWKBD_NOTEMPTY_NOTBLANK, SWKBD_FILTER_CALLBACK, -1); swkbdSetValidation(&swkbd, SWKBD_NOTEMPTY_NOTBLANK, SWKBD_FILTER_CALLBACK, -1);
swkbdSetFilterCallback(&swkbd, &fat32filter, NULL); swkbdSetFilterCallback(&swkbd, &fat32filter, NULL);
@@ -462,18 +464,18 @@ renamed:
return; return;
case SWKBD_BUTTON_NONE: case SWKBD_BUTTON_NONE:
DEBUG("SWKBD broke wtf??? :- %x\n", swkbdGetResult(&swkbd)); DEBUG("SWKBD broke wtf??? :- %x\n", swkbdGetResult(&swkbd));
return throw_error("???\nTry a USB keyboard", ERROR_LEVEL_WARNING); return throw_error(language.fs.swkbd_fail, ERROR_LEVEL_WARNING);
} }
} }
else if (res == (long)0xC86044D2) else if (res == (long)0xC86044D2)
{ {
DEBUG("SD card is full\n"); DEBUG("SD card is full\n");
return throw_error("SD card is full.\nDelete some themes to make space.", ERROR_LEVEL_WARNING); return throw_error(language.fs.sd_full, ERROR_LEVEL_WARNING);
} }
else else
{ {
DEBUG("error: %lx\n", res); DEBUG("error: %lx\n", res);
return throw_error("FS Error:\nGet a new SD card.", ERROR_LEVEL_ERROR); return throw_error(language.fs.fs_error, ERROR_LEVEL_ERROR);
} }
} }

View File

@@ -30,6 +30,7 @@
#include "music.h" #include "music.h"
#include "draw.h" #include "draw.h"
#include "conversion.h" #include "conversion.h"
#include "ui_strings.h"
#include <png.h> #include <png.h>
@@ -306,9 +307,8 @@ void load_icons_thread(void * void_arg)
} while(arg->run_thread); } while(arg->run_thread);
} }
bool load_preview_from_buffer(char * row_pointers, u32 size, C2D_Image * preview_image, int * preview_offset) bool load_preview_from_buffer(char * row_pointers, u32 size, C2D_Image * preview_image, int * preview_offset, int height)
{ {
int height = SCREEN_HEIGHT * 2;
int width = (uint32_t)((size / 4) / height); int width = (uint32_t)((size / 4) / height);
free_preview(*preview_image); free_preview(*preview_image);
@@ -355,10 +355,11 @@ bool load_preview(const Entry_List_s * list, C2D_Image * preview_image, int * pr
char * preview_buffer = NULL; char * preview_buffer = NULL;
u32 size = load_data("/preview.png", entry, &preview_buffer); u32 size = load_data("/preview.png", entry, &preview_buffer);
u32 height = 480;
if(size) if(size)
{ {
if (!(size = png_to_abgr(&preview_buffer, size))) if (!(size = png_to_abgr(&preview_buffer, size, &height)))
{ {
return false; return false;
} }
@@ -409,7 +410,7 @@ bool load_preview(const Entry_List_s * list, C2D_Image * preview_image, int * pr
if (!found_splash) if (!found_splash)
{ {
free(rgba_buffer); free(rgba_buffer);
throw_error("No preview found.", ERROR_LEVEL_WARNING); throw_error(language.loading.no_preview, ERROR_LEVEL_WARNING);
return false; return false;
} }
@@ -417,7 +418,7 @@ bool load_preview(const Entry_List_s * list, C2D_Image * preview_image, int * pr
preview_buffer = rgba_buffer; preview_buffer = rgba_buffer;
} }
bool ret = load_preview_from_buffer(preview_buffer, size, preview_image, preview_offset); bool ret = load_preview_from_buffer(preview_buffer, size, preview_image, preview_offset, height);
free(preview_buffer); free(preview_buffer);
if(ret) if(ret)

View File

@@ -32,7 +32,7 @@
#include "camera.h" #include "camera.h"
#include "music.h" #include "music.h"
#include "remote.h" #include "remote.h"
#include "instructions.h" #include "ui_strings.h"
#include <time.h> #include <time.h>
bool quit = false; bool quit = false;
@@ -53,6 +53,8 @@ static Thread_Arg_s install_check_threads_arg[MODE_AMOUNT] = {0};
static Entry_List_s lists[MODE_AMOUNT] = {0}; static Entry_List_s lists[MODE_AMOUNT] = {0};
Language_s language = {0};
int __stacksize__ = 64 * 1024; int __stacksize__ = 64 * 1024;
Result archive_result; Result archive_result;
u32 old_time_limit; u32 old_time_limit;
@@ -254,8 +256,8 @@ static void load_lists(Entry_List_s * lists)
DEBUG("total: %i\n", current_list->entries_count); DEBUG("total: %i\n", current_list->entries_count);
load_icons_first(current_list, false);
sort_by_name(current_list); sort_by_name(current_list);
load_icons_first(current_list, false);
void (*install_check_function)(void *) = NULL; void (*install_check_function)(void *) = NULL;
if(i == MODE_THEMES) if(i == MODE_THEMES)
@@ -283,12 +285,12 @@ static SwkbdCallbackResult jump_menu_callback(void * entries_count, const char *
int typed_value = atoi(text); int typed_value = atoi(text);
if(typed_value > *(int *)entries_count) if(typed_value > *(int *)entries_count)
{ {
*ppMessage = "The new position has to be\nsmaller or equal to the\nnumber of entries!"; *ppMessage = language.main.position_too_big;
return SWKBD_CALLBACK_CONTINUE; return SWKBD_CALLBACK_CONTINUE;
} }
else if(typed_value == 0) else if(typed_value == 0)
{ {
*ppMessage = "The new position has to\nbe positive!"; *ppMessage = language.main.position_zero;
return SWKBD_CALLBACK_CONTINUE; return SWKBD_CALLBACK_CONTINUE;
} }
return SWKBD_CALLBACK_OK; return SWKBD_CALLBACK_OK;
@@ -309,11 +311,11 @@ static void jump_menu(Entry_List_s * list)
sprintf(numbuf, "%i", list->selected_entry); sprintf(numbuf, "%i", list->selected_entry);
swkbdSetInitialText(&swkbd, numbuf); swkbdSetInitialText(&swkbd, numbuf);
sprintf(numbuf, "Where do you want to jump to?\nMay cause icons to reload."); sprintf(numbuf, language.main.jump_q);
swkbdSetHintText(&swkbd, numbuf); swkbdSetHintText(&swkbd, numbuf);
swkbdSetButton(&swkbd, SWKBD_BUTTON_LEFT, "Cancel", false); swkbdSetButton(&swkbd, SWKBD_BUTTON_LEFT, language.main.cancel, false);
swkbdSetButton(&swkbd, SWKBD_BUTTON_RIGHT, "Jump", true); swkbdSetButton(&swkbd, SWKBD_BUTTON_RIGHT, language.main.jump, true);
swkbdSetValidation(&swkbd, SWKBD_NOTEMPTY_NOTBLANK, 0, max_chars); swkbdSetValidation(&swkbd, SWKBD_NOTEMPTY_NOTBLANK, 0, max_chars);
swkbdSetFilterCallback(&swkbd, jump_menu_callback, &list->entries_count); swkbdSetFilterCallback(&swkbd, jump_menu_callback, &list->entries_count);
@@ -375,6 +377,9 @@ int main(void)
{ {
srand(time(NULL)); srand(time(NULL));
init_services(); init_services();
CFG_Language lang;
CFGU_GetSystemLanguage(&lang);
language = init_strings(lang);
init_screens(); init_screens();
svcCreateMutex(&update_icons_mutex, true); svcCreateMutex(&update_icons_mutex, true);
@@ -400,7 +405,9 @@ int main(void)
int preview_offset = 0; int preview_offset = 0;
bool install_mode = false; bool install_mode = false;
DrawMode draw_mode = DRAW_MODE_LIST;
bool extra_mode = false; bool extra_mode = false;
int extra_index = 1;
C2D_Image preview = {0}; C2D_Image preview = {0};
while(aptMainLoop()) while(aptMainLoop())
@@ -421,7 +428,7 @@ int main(void)
#ifndef CITRA_MODE #ifndef CITRA_MODE
if(R_FAILED(archive_result) && current_mode == MODE_THEMES) if(R_FAILED(archive_result) && current_mode == MODE_THEMES)
{ {
throw_error("Theme extdata does not exist!\nSet a default theme from the home menu.", ERROR_LEVEL_ERROR); throw_error(language.main.no_theme_extdata, ERROR_LEVEL_ERROR);
quit = true; quit = true;
continue; continue;
} }
@@ -434,22 +441,12 @@ int main(void)
current_list = &lists[current_mode]; current_list = &lists[current_mode];
Instructions_s instructions = normal_instructions[current_mode]; Instructions_s instructions = language.normal_instructions[current_mode];
if(install_mode) if(install_mode)
instructions = install_instructions; instructions = language.install_instructions;
if(extra_mode) if(extra_mode)
{ {
int index = 1; instructions = language.extra_instructions[extra_index];
bool key_l = (kDown | kHeld) & KEY_L;
bool key_r = (kDown | kHeld) & KEY_R;
if(key_l ^ key_r)
{
if(key_l)
index = 0;
else if(key_r) // uncomment when we use the right menu. we don't for now
index = 2;
}
instructions = extra_instructions[index];
} }
if(preview_mode) if(preview_mode)
@@ -472,7 +469,7 @@ int main(void)
svcWaitSynchronization(update_icons_mutex, U64_MAX); svcWaitSynchronization(update_icons_mutex, U64_MAX);
} }
draw_interface(current_list, instructions); draw_interface(current_list, instructions, draw_mode);
svcSleepThread(1e7); svcSleepThread(1e7);
released = false; released = false;
@@ -502,7 +499,7 @@ int main(void)
{ {
enable_qr: enable_qr:
draw_base_interface(); draw_base_interface();
draw_text_center(GFX_TOP, 100, 0.5f, 0.6f, 0.6f, colors[COLOR_WHITE], "Loading QR Scanner..."); draw_text_center(GFX_TOP, 100, 0.5f, 0.6f, 0.6f, colors[COLOR_WHITE], language.main.loading_qr);
end_frame(); end_frame();
if(R_SUCCEEDED(camInit())) if(R_SUCCEEDED(camInit()))
{ {
@@ -518,15 +515,15 @@ int main(void)
} }
else else
{ {
throw_error("Please connect to Wi-Fi before scanning QR codes", ERROR_LEVEL_WARNING); throw_error(language.main.no_wifi, ERROR_LEVEL_WARNING);
} }
} }
else else
{ {
if(homebrew) if(homebrew)
throw_error("QR scanning doesnt work from the Homebrew\nLauncher, use the ThemePlaza browser instead.", ERROR_LEVEL_WARNING); throw_error(language.main.qr_homebrew, ERROR_LEVEL_WARNING);
else else
throw_error("Your camera seems to have a problem,\nunable to scan QR codes.", ERROR_LEVEL_WARNING); throw_error(language.main.camera_broke, ERROR_LEVEL_WARNING);
} }
continue; continue;
@@ -580,92 +577,138 @@ int main(void)
if(install_mode) if(install_mode)
{ {
if(kUp & KEY_A) if ((kDown | kHeld) & KEY_TOUCH)
install_mode = false;
if(!install_mode)
{ {
if((kDown | kHeld) & KEY_DLEFT) touchPosition touch = {0};
hidTouchRead(&touch);
u16 x = touch.px;
u16 y = touch.py;
if (kDown & KEY_TOUCH)
{ {
aptSetHomeAllowed(false); if (y < 24)
draw_install(INSTALL_BGM);
if(R_SUCCEEDED(bgm_install(current_entry)))
{ {
for(int i = 0; i < current_list->entries_count; i++) if (BETWEEN(320-24, x, 320))
{ {
Entry_s * theme = &current_list->entries[i]; goto install_theme_single;
if(theme == current_entry) } else if (BETWEEN(320-48, x, 320-24))
theme->installed = true; {
else goto install_theme_shuffle;
theme->installed = false; } else if (BETWEEN(320-72, x, 320-48))
{
goto install_theme_no_bgm;
} else if (BETWEEN(320-96, x, 320-72))
{
goto install_theme_bgm_only;
} else if (BETWEEN(320-120, x, 320-96))
{
goto install_leave;
} }
installed_themes = true;
} }
} }
else if((kDown | kHeld) & KEY_DUP) }
if(kDown & KEY_B)
{
install_leave:
install_mode = false;
draw_mode = DRAW_MODE_LIST;
}
else if(kDown & KEY_DLEFT)
{
install_theme_bgm_only:
install_mode = false;
draw_mode = DRAW_MODE_LIST;
aptSetHomeAllowed(false);
draw_install(INSTALL_BGM);
if(R_SUCCEEDED(bgm_install(current_entry)))
{
for(int i = 0; i < current_list->entries_count; i++)
{
#define BETWEEN(min, x, max) (min < x && x < max)
Entry_s * theme = &current_list->entries[i];
if(theme == current_entry)
theme->installed = true;
else
theme->installed = false;
}
installed_themes = true;
}
}
else if(kDown & KEY_DUP)
{
install_theme_single:
install_mode = false;
draw_mode = DRAW_MODE_LIST;
aptSetHomeAllowed(false);
draw_install(INSTALL_SINGLE);
if(R_SUCCEEDED(theme_install(current_entry)))
{
for(int i = 0; i < current_list->entries_count; i++)
{
Entry_s * theme = &current_list->entries[i];
if(theme == current_entry)
theme->installed = true;
else
theme->installed = false;
}
installed_themes = true;
}
}
else if(kDown & KEY_DRIGHT)
{
install_theme_no_bgm:
install_mode = false;
draw_mode = DRAW_MODE_LIST;
aptSetHomeAllowed(false);
draw_install(INSTALL_NO_BGM);
if(R_SUCCEEDED(no_bgm_install(current_entry)))
{
for(int i = 0; i < current_list->entries_count; i++)
{
Entry_s * theme = &current_list->entries[i];
if(theme == current_entry)
theme->installed = true;
else
theme->installed = false;
}
installed_themes = true;
}
}
else if(kDown & KEY_DDOWN)
{
install_theme_shuffle:
install_mode = false;
draw_mode = DRAW_MODE_LIST;
if(current_list->shuffle_count > MAX_SHUFFLE_THEMES)
{
throw_error(language.main.too_many_themes, ERROR_LEVEL_WARNING);
}
else if(current_list->shuffle_count < 2)
{
throw_error(language.main.not_enough_themes, ERROR_LEVEL_WARNING);
}
else
{ {
aptSetHomeAllowed(false); aptSetHomeAllowed(false);
draw_install(INSTALL_SINGLE); draw_install(INSTALL_SHUFFLE);
if(R_SUCCEEDED(theme_install(current_entry))) Result res = shuffle_install(current_list);
{ if(R_FAILED(res)) DEBUG("shuffle install result: %lx\n", res);
for(int i = 0; i < current_list->entries_count; i++)
{
Entry_s * theme = &current_list->entries[i];
if(theme == current_entry)
theme->installed = true;
else
theme->installed = false;
}
installed_themes = true;
}
}
else if((kDown | kHeld) & KEY_DRIGHT)
{
aptSetHomeAllowed(false);
draw_install(INSTALL_NO_BGM);
if(R_SUCCEEDED(no_bgm_install(current_entry)))
{
for(int i = 0; i < current_list->entries_count; i++)
{
Entry_s * theme = &current_list->entries[i];
if(theme == current_entry)
theme->installed = true;
else
theme->installed = false;
}
installed_themes = true;
}
}
else if((kDown | kHeld) & KEY_DDOWN)
{
if(current_list->shuffle_count > MAX_SHUFFLE_THEMES)
{
throw_error("You have too many themes selected.", ERROR_LEVEL_WARNING);
}
else if(current_list->shuffle_count < 2)
{
throw_error("You don't have enough themes selected.", ERROR_LEVEL_WARNING);
}
else else
{ {
aptSetHomeAllowed(false); for(int i = 0; i < current_list->entries_count; i++)
draw_install(INSTALL_SHUFFLE);
Result res = shuffle_install(current_list);
if(R_FAILED(res)) DEBUG("shuffle install result: %lx\n", res);
else
{ {
for(int i = 0; i < current_list->entries_count; i++) Entry_s * theme = &current_list->entries[i];
if(theme->in_shuffle)
{ {
Entry_s * theme = &current_list->entries[i]; theme->in_shuffle = false;
if(theme->in_shuffle) theme->installed = true;
{
theme->in_shuffle = false;
theme->installed = true;
}
else theme->installed = false;
} }
current_list->shuffle_count = 0; else theme->installed = false;
installed_themes = true;
} }
current_list->shuffle_count = 0;
installed_themes = true;
} }
} }
} }
@@ -673,71 +716,151 @@ int main(void)
} }
else if(extra_mode) else if(extra_mode)
{ {
if(kUp & KEY_X) if((kDown | kHeld) & KEY_TOUCH)
extra_mode = false;
if(!extra_mode)
{ {
bool key_l = (kDown | kHeld) & KEY_L; touchPosition touch = {0};
bool key_r = (kDown | kHeld) & KEY_R; hidTouchRead(&touch);
if(!(key_l ^ key_r)) u16 x = touch.px;
u16 y = touch.py;
if (kDown & KEY_TOUCH)
{ {
if((kDown | kHeld) & KEY_DLEFT) if (y < 24)
{ {
browse_themeplaza: if (BETWEEN(320-24, x, 320))
if(themeplaza_browser(current_mode))
{ {
current_mode = MODE_THEMES; goto browse_themeplaza;
load_lists(lists); } else if (BETWEEN(320-48, x, 320-24))
{
goto dump_single;
} else if (BETWEEN(320-72, x, 320-48))
{
switch (current_list->current_sort)
{
case SORT_NAME:
goto sort_author;
break;
case SORT_AUTHOR:
goto sort_path;
break;
case SORT_PATH:
goto sort_name;
break;
default:
break;
}
} else if (BETWEEN(320-96, x, 320-72))
{
extra_mode = false;
extra_index = 1;
draw_mode = DRAW_MODE_LIST;
} }
} }
else if((kDown | kHeld) & KEY_DUP) }
}
else if(extra_index == 1)
{
if(kDown & KEY_B)
{
extra_mode = false;
draw_mode = DRAW_MODE_LIST;
}
else if(kDown & KEY_DLEFT)
{
browse_themeplaza:
if(themeplaza_browser(current_mode))
{ {
jump: current_mode = MODE_THEMES;
jump_menu(current_list); load_lists(lists);
}
extra_mode = false;
draw_mode = DRAW_MODE_LIST;
extra_index = 1;
}
else if(kDown & KEY_DUP)
{
jump:
jump_menu(current_list);
extra_mode = false;
draw_mode = DRAW_MODE_LIST;
extra_index = 1;
}
else if((kDown | kHeld) & KEY_DDOWN)
{
load_icons_first(current_list, false);
}
} }
else if(key_l) else if(kDown & KEY_DDOWN)
{ {
if((kDown | kHeld) & KEY_DLEFT) load_icons_first(current_list, false);
{ extra_mode = false;
sort_path: draw_mode = DRAW_MODE_LIST;
sort_by_filename(current_list); extra_index = 1;
load_icons_first(current_list, false);
}
else if(((kDown | kHeld)) & KEY_DUP)
{
sort_name:
sort_by_name(current_list);
load_icons_first(current_list, false);
}
else if(((kDown | kHeld)) & KEY_DDOWN)
{
sort_author:
sort_by_author(current_list);
load_icons_first(current_list, false);
}
} }
else if(key_r) else if (kDown & KEY_R)
{ {
if(((kDown | kHeld)) & KEY_DUP) extra_index = 2;
{ }
draw_install(INSTALL_DUMPING_THEME); else if(kDown & KEY_L)
Result res = dump_current_theme(); {
if (R_FAILED(res)) DEBUG("Dump theme result: %lx\n", res); extra_index = 0;
else load_lists(lists); }
} }
else if(((kDown | kHeld)) & KEY_DDOWN) else if(extra_index == 0)
{ {
draw_install(INSTALL_DUMPING_ALL_THEMES); if(kDown & KEY_DLEFT)
Result res = dump_all_themes(); {
if (R_FAILED(res)) DEBUG("Dump all themes result: %lx\n", res); sort_path:
else load_lists(lists); sort_by_filename(current_list);
} load_icons_first(current_list, false);
extra_mode = false;
draw_mode = DRAW_MODE_LIST;
extra_index = 1;
}
else if(kDown & KEY_DUP)
{
sort_name:
sort_by_name(current_list);
load_icons_first(current_list, false);
extra_mode = false;
draw_mode = DRAW_MODE_LIST;
extra_index = 1;
}
else if(kDown & KEY_DDOWN)
{
sort_author:
sort_by_author(current_list);
load_icons_first(current_list, false);
extra_mode = false;
draw_mode = DRAW_MODE_LIST;
extra_index = 1;
}
else if (kDown & KEY_B)
{
extra_index = 1;
}
}
else if(extra_index == 2)
{
if(kDown & KEY_DUP)
{
dump_single:
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);
extra_mode = false;
draw_mode = DRAW_MODE_LIST;
extra_index = 1;
}
else if(kDown & 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);
extra_mode = false;
draw_mode = DRAW_MODE_LIST;
extra_index = 1;
}
else if(kDown &KEY_B)
{
extra_index = 1;
} }
} }
continue; continue;
@@ -751,6 +874,7 @@ int main(void)
{ {
case MODE_THEMES: case MODE_THEMES:
install_mode = true; install_mode = true;
draw_mode = DRAW_MODE_INSTALL;
break; break;
case MODE_SPLASHES: case MODE_SPLASHES:
draw_install(INSTALL_SPLASH); draw_install(INSTALL_SPLASH);
@@ -776,7 +900,7 @@ int main(void)
toggle_shuffle(current_list); toggle_shuffle(current_list);
break; break;
case MODE_SPLASHES: case MODE_SPLASHES:
if(draw_confirm("Are you sure you would like to delete\nthe installed splash?", current_list)) if(draw_confirm(language.main.uninstall_confirm, current_list, draw_mode))
{ {
draw_install(INSTALL_SPLASH_DELETE); draw_install(INSTALL_SPLASH_DELETE);
splash_delete(); splash_delete();
@@ -789,10 +913,11 @@ int main(void)
else if(kDown & KEY_X) else if(kDown & KEY_X)
{ {
extra_mode = true; extra_mode = true;
draw_mode = DRAW_MODE_EXTRA;
} }
else if(kDown & KEY_SELECT) else if(kDown & KEY_SELECT)
{ {
if(draw_confirm("Are you sure you would like to delete this?", current_list)) if(draw_confirm(language.main.delete_confirm, current_list, draw_mode))
{ {
draw_install(INSTALL_ENTRY_DELETE); draw_install(INSTALL_ENTRY_DELETE);
delete_entry(current_entry, current_entry->is_zip); delete_entry(current_entry, current_entry->is_zip);
@@ -851,43 +976,49 @@ int main(void)
u16 x = touch.px; u16 x = touch.px;
u16 y = touch.py; u16 y = touch.py;
u16 arrowStartX = 152; u16 arrowStartX = 136;
u16 arrowEndX = arrowStartX+16; u16 arrowEndX = arrowStartX+16;
#define BETWEEN(min, x, max) (min < x && x < max)
if(kDown & KEY_TOUCH) if(kDown & KEY_TOUCH)
{ {
if(y < 24) if(y < 24)
{ {
if(current_list->entries != NULL && BETWEEN(arrowStartX, x, arrowEndX) && current_list->scroll > 0) if(BETWEEN(320-168, x, 320-144))
{ {
change_selected(current_list, -current_list->entries_per_screen_v); if (current_mode == MODE_THEMES)
{
toggle_shuffle(current_list);
}
} }
else if(BETWEEN(320-144, x, 320-120)) else if(BETWEEN(320-144, x, 320-120))
{ {
switch(current_list->current_sort) extra_mode = true;
{ draw_mode = DRAW_MODE_EXTRA;
case SORT_NAME:
goto sort_author;
break;
case SORT_AUTHOR:
goto sort_path;
break;
case SORT_PATH:
goto sort_name;
break;
default:
break;
}
} }
else if(BETWEEN(320-120, x, 320-96)) else if(BETWEEN(320-120, x, 320-96))
{ {
goto enable_qr; if (current_mode == MODE_THEMES)
{
install_mode = true;
draw_mode = DRAW_MODE_INSTALL;
} else if (current_mode == MODE_SPLASHES)
{
draw_install(INSTALL_SPLASH);
splash_install(current_entry);
for(int i = 0; i < current_list->entries_count; i++)
{
Entry_s * splash = &current_list->entries[i];
if(splash == current_entry)
splash->installed = true;
else
splash->installed = false;
}
}
} }
else if(BETWEEN(320-96, x, 320-72)) else if(BETWEEN(320-96, x, 320-72))
{ {
goto browse_themeplaza; goto enable_qr;
} }
else if(BETWEEN(320-72, x, 320-48)) else if(BETWEEN(320-72, x, 320-48))
{ {
@@ -904,7 +1035,11 @@ int main(void)
} }
else if(y >= 216) else if(y >= 216)
{ {
if(current_list->entries != NULL && BETWEEN(arrowStartX, x, arrowEndX) && current_list->scroll < current_list->entries_count - current_list->entries_per_screen_v) if(current_list->entries != NULL && BETWEEN(arrowStartX, x, arrowEndX) && current_list->scroll > 0)
{
change_selected(current_list, -current_list->entries_per_screen_v);
}
else if(current_list->entries != NULL && BETWEEN(arrowStartX + 16, x, arrowEndX + 16) && current_list->scroll < current_list->entries_count - current_list->entries_per_screen_v)
{ {
change_selected(current_list, current_list->entries_per_screen_v); change_selected(current_list, current_list->entries_per_screen_v);
} }

View File

@@ -33,78 +33,12 @@
#include "music.h" #include "music.h"
#include "urls.h" #include "urls.h"
#include "conversion.h" #include "conversion.h"
#include "ui_strings.h"
// forward declaration of special case used only here // forward declaration of special case used only here
// TODO: replace this travesty with a proper handler // 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 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] = {
{
.info_line = NULL,
.instructions = {
{
"\uE000 Download theme",
"\uE001 Go back"
},
{
"\uE002 Hold for more",
"\uE003 Preview theme"
},
{
"\uE004 Previous page",
"\uE005 Next page"
},
{
"Exit",
NULL
}
}
},
{
.info_line = NULL,
.instructions = {
{
"\uE000 Download splash",
"\uE001 Go back"
},
{
"\uE002 Hold for more",
"\uE003 Preview splash"
},
{
"\uE004 Previous page",
"\uE005 Next page"
},
{
"Exit",
NULL
}
}
}
};
static Instructions_s extra_instructions = {
.info_line = "Release \uE002 to cancel or hold \uE006 and release \uE002 to do stuff",
.instructions = {
{
"\uE079 Jump to page",
"\uE07A Search tags"
},
{
"\uE07B Toggle splash/theme",
"\uE07C Reload without cache"
},
{
NULL,
NULL
},
{
"Exit",
NULL
}
}
};
static void free_icons(Entry_List_s * list) static void free_icons(Entry_List_s * list)
{ {
if (list != NULL) if (list != NULL)
@@ -237,7 +171,7 @@ static void load_remote_list(Entry_List_s * list, json_int_t page, EntryMode mod
load_remote_entries(list, value, ignore_cache, loading_screen); load_remote_entries(list, value, ignore_cache, loading_screen);
else if (json_is_string(value) && !strcmp(key, THEMEPLAZA_JSON_ERROR_MESSAGE) else if (json_is_string(value) && !strcmp(key, THEMEPLAZA_JSON_ERROR_MESSAGE)
&& !strcmp(json_string_value(value), THEMEPLAZA_JSON_ERROR_MESSAGE_NOT_FOUND)) && !strcmp(json_string_value(value), THEMEPLAZA_JSON_ERROR_MESSAGE_NOT_FOUND))
throw_error("No results for this search.", ERROR_LEVEL_WARNING); throw_error(language.remote.no_results, ERROR_LEVEL_WARNING);
} }
} }
else else
@@ -246,7 +180,7 @@ static void load_remote_list(Entry_List_s * list, json_int_t page, EntryMode mod
json_decref(root); json_decref(root);
} }
else else
throw_error("Couldn't download Theme Plaza data.\nMake sure WiFi is on.", ERROR_LEVEL_WARNING); throw_error(language.remote.check_wifi, ERROR_LEVEL_WARNING);
free(page_json); free(page_json);
} }
@@ -288,14 +222,15 @@ static bool load_remote_preview(const Entry_s * entry, C2D_Image * preview_image
char * preview_buf = malloc(preview_size); char * preview_buf = malloc(preview_size);
u32 preview_buf_size = preview_size; u32 preview_buf_size = preview_size;
memcpy(preview_buf, preview_png, preview_size); memcpy(preview_buf, preview_png, preview_size);
u32 height = 480;
if (!(preview_buf_size = png_to_abgr(&preview_buf, preview_buf_size))) if (!(preview_buf_size = png_to_abgr(&preview_buf, preview_buf_size, &height)))
{ {
free(preview_buf); free(preview_buf);
return false; return false;
} }
bool ret = load_preview_from_buffer(preview_buf, preview_buf_size, preview_image, preview_offset); bool ret = load_preview_from_buffer(preview_buf, preview_buf_size, preview_image, preview_offset, height);
free(preview_buf); free(preview_buf);
if (ret && not_cached) // only save the preview if it loaded correctly - isn't corrupted if (ret && not_cached) // only save the preview if it loaded correctly - isn't corrupted
@@ -380,12 +315,12 @@ jump_menu_callback(void * page_number, const char ** ppMessage, const char * tex
int typed_value = atoi(text); int typed_value = atoi(text);
if (typed_value > *(json_int_t *)page_number) if (typed_value > *(json_int_t *)page_number)
{ {
*ppMessage = "The new page has to be\nsmaller or equal to the\nnumber of pages!"; *ppMessage = language.remote.new_page_big;
return SWKBD_CALLBACK_CONTINUE; return SWKBD_CALLBACK_CONTINUE;
} }
else if (typed_value == 0) else if (typed_value == 0)
{ {
*ppMessage = "The new position has to\nbe positive!"; *ppMessage = language.remote.new_page_zero;
return SWKBD_CALLBACK_CONTINUE; return SWKBD_CALLBACK_CONTINUE;
} }
return SWKBD_CALLBACK_OK; return SWKBD_CALLBACK_OK;
@@ -408,11 +343,11 @@ static void jump_menu(Entry_List_s * list)
JSON_INTEGER_FORMAT, list->tp_current_page); JSON_INTEGER_FORMAT, list->tp_current_page);
swkbdSetInitialText(&swkbd, numbuf); swkbdSetInitialText(&swkbd, numbuf);
sprintf(numbuf, "Which page do you want to jump to?"); sprintf(numbuf, language.remote.jump_page);
swkbdSetHintText(&swkbd, numbuf); swkbdSetHintText(&swkbd, numbuf);
swkbdSetButton(&swkbd, SWKBD_BUTTON_LEFT, "Cancel", false); swkbdSetButton(&swkbd, SWKBD_BUTTON_LEFT, language.remote.cancel, false);
swkbdSetButton(&swkbd, SWKBD_BUTTON_RIGHT, "Jump", true); swkbdSetButton(&swkbd, SWKBD_BUTTON_RIGHT, language.remote.jump, true);
swkbdSetValidation(&swkbd, SWKBD_NOTEMPTY_NOTBLANK, 0, max_chars); swkbdSetValidation(&swkbd, SWKBD_NOTEMPTY_NOTBLANK, 0, max_chars);
swkbdSetFilterCallback(&swkbd, jump_menu_callback, &list->tp_page_count); swkbdSetFilterCallback(&swkbd, jump_menu_callback, &list->tp_page_count);
@@ -434,10 +369,10 @@ static void search_menu(Entry_List_s * list)
SwkbdState swkbd; SwkbdState swkbd;
swkbdInit(&swkbd, SWKBD_TYPE_NORMAL, 2, max_chars); swkbdInit(&swkbd, SWKBD_TYPE_NORMAL, 2, max_chars);
swkbdSetHintText(&swkbd, "Which tags do you want to search for?"); swkbdSetHintText(&swkbd, language.remote.tags);
swkbdSetButton(&swkbd, SWKBD_BUTTON_LEFT, "Cancel", false); swkbdSetButton(&swkbd, SWKBD_BUTTON_LEFT, language.remote.cancel, false);
swkbdSetButton(&swkbd, SWKBD_BUTTON_RIGHT, "Search", true); swkbdSetButton(&swkbd, SWKBD_BUTTON_RIGHT, language.remote.search, true);
swkbdSetValidation(&swkbd, SWKBD_NOTBLANK, 0, max_chars); swkbdSetValidation(&swkbd, SWKBD_NOTBLANK, 0, max_chars);
SwkbdButton button = swkbdInputText(&swkbd, search, max_chars); SwkbdButton button = swkbdInputText(&swkbd, search, max_chars);
@@ -493,7 +428,7 @@ bool themeplaza_browser(EntryMode mode)
SwkbdResult swkbd_res = swkbdGetResult(&swkbd); SwkbdResult swkbd_res = swkbdGetResult(&swkbd);
if (swkbd_res != SWKBD_PARENTAL_OK) if (swkbd_res != SWKBD_PARENTAL_OK)
{ {
throw_error("Parental Control validation failed!\nBrowser Access restricted.", ERROR_LEVEL_WARNING); throw_error(language.remote.parental_fail, ERROR_LEVEL_WARNING);
return downloaded; return downloaded;
} }
} }
@@ -556,9 +491,9 @@ bool themeplaza_browser(EntryMode mode)
} }
else else
{ {
Instructions_s instructions = browser_instructions[mode]; Instructions_s instructions = language.remote_instructions[mode];
if (extra_mode) if (extra_mode)
instructions = extra_instructions; instructions = language.remote_extra_instructions;
draw_grid_interface(current_list, instructions); draw_grid_interface(current_list, instructions);
} }
@@ -950,7 +885,6 @@ static ParseResult parse_header(struct header * out, httpcContext * context, con
return SUCCESS; return SUCCESS;
} }
#define ZIP_NOT_AVAILABLE "ZIP not found at this URL\nIf you believe this is an error, please\ncontact the site administrator"
/* /*
* 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");
@@ -962,6 +896,7 @@ Result http_get(const char * url, char ** filename, char ** buf, u32 * size, Ins
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 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)
{ {
const char *zip_not_available = language.remote.zip_not_found;
Result ret; Result ret;
httpcContext context; httpcContext context;
char redirect_url[0x824] = {0}; char redirect_url[0x824] = {0};
@@ -1005,7 +940,7 @@ redirect: // goto here if we need to redirect
break; break;
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, language.remote.generic_httpc_error, _header.result_code);
throw_error(err_buf, ERROR_LEVEL_ERROR); throw_error(err_buf, ERROR_LEVEL_ERROR);
quit = true; quit = true;
httpcCloseContext(&context); httpcCloseContext(&context);
@@ -1013,12 +948,12 @@ redirect: // goto here if we need to redirect
case SEE_OTHER: case SEE_OTHER:
if (strstr(url, THEMEPLAZA_BASE_URL)) if (strstr(url, THEMEPLAZA_BASE_URL))
{ {
snprintf(err_buf, ERROR_BUFFER_SIZE, "HTTP 303 See Other (Theme Plaza)\nHas this theme been approved?"); snprintf(err_buf, ERROR_BUFFER_SIZE, language.remote.http303_tp);
goto error; goto error;
} }
else else
{ {
snprintf(err_buf, ERROR_BUFFER_SIZE, "HTTP 303 See Other\nDownload the resource directly\nor contact the site administrator."); snprintf(err_buf, ERROR_BUFFER_SIZE, language.remote.http303);
goto error; goto error;
} }
case REDIRECT: case REDIRECT:
@@ -1040,7 +975,7 @@ redirect: // goto here if we need to redirect
goto redirect; goto redirect;
case SERVER_IS_MISBEHAVING: case SERVER_IS_MISBEHAVING:
DEBUG("Server is misbehaving (provided resource with incorrect MIME)\n"); DEBUG("Server is misbehaving (provided resource with incorrect MIME)\n");
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) if (!not_found_is_error)
@@ -1050,59 +985,59 @@ redirect: // goto here if we need to redirect
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)
snprintf(err_buf, ERROR_BUFFER_SIZE, "HTTP 404 Not Found\nHas this theme been approved?"); snprintf(err_buf, ERROR_BUFFER_SIZE, language.remote.http404);
else else
snprintf(err_buf, ERROR_BUFFER_SIZE, "HTTP %s\nCheck that the URL is correct.", http_error); snprintf(err_buf, ERROR_BUFFER_SIZE, language.remote.http_err_url, http_error);
goto error; goto error;
case HTTP_UNACCEPTABLE: case HTTP_UNACCEPTABLE:
DEBUG("HTTP 406 Unacceptable; Accept: %s\n", acceptable_mime_types); DEBUG("HTTP 406 Unacceptable; Accept: %s\n", acceptable_mime_types);
snprintf(err_buf, ERROR_BUFFER_SIZE, ZIP_NOT_AVAILABLE); snprintf(err_buf, ERROR_BUFFER_SIZE, zip_not_available);
goto error; goto error;
case HTTP_UNAUTHORIZED: case HTTP_UNAUTHORIZED:
case HTTP_FORBIDDEN: case HTTP_FORBIDDEN:
case HTTP_PROXY_UNAUTHORIZED: case HTTP_PROXY_UNAUTHORIZED:
DEBUG("HTTP %u: device not authenticated\n", parse); DEBUG("HTTP %u: device not authenticated\n", parse);
snprintf(err_buf, ERROR_BUFFER_SIZE, "HTTP %s\nContact the site administrator.", parse == HTTP_UNAUTHORIZED snprintf(err_buf, ERROR_BUFFER_SIZE, language.remote.http_errcode_generic, parse == HTTP_UNAUTHORIZED
? "401 Unauthorized" ? language.remote.http401
: parse == HTTP_FORBIDDEN : parse == HTTP_FORBIDDEN
? "403 Forbidden" ? language.remote.http403
: "407 Proxy Authentication Required"); : language.remote.http407);
goto error; goto error;
case HTTP_URI_TOO_LONG: case HTTP_URI_TOO_LONG:
DEBUG("HTTP 414; URL is too long, maybe too many redirects?\n"); DEBUG("HTTP 414; URL is too long, maybe too many redirects?\n");
snprintf(err_buf, ERROR_BUFFER_SIZE, "HTTP 414 URI Too Long\nThe QR code points to a really long URL.\nDownload the file directly."); snprintf(err_buf, ERROR_BUFFER_SIZE, language.remote.http414);
goto error; goto error;
case HTTP_IM_A_TEAPOT: case HTTP_IM_A_TEAPOT:
DEBUG("HTTP 418 I'm a teapot\n"); DEBUG("HTTP 418 I'm a teapot\n");
snprintf(err_buf, ERROR_BUFFER_SIZE, "HTTP 418 I'm a teapot\nContact the site administrator."); snprintf(err_buf, ERROR_BUFFER_SIZE, language.remote.http418);
goto error; goto error;
case HTTP_UPGRADE_REQUIRED: case HTTP_UPGRADE_REQUIRED:
DEBUG("HTTP 426; HTTP/2 required\n"); DEBUG("HTTP 426; HTTP/2 required\n");
snprintf(err_buf, ERROR_BUFFER_SIZE, "HTTP 426 Upgrade Required\nThe 3DS cannot connect to this server.\nContact the site administrator."); snprintf(err_buf, ERROR_BUFFER_SIZE, language.remote.http426);
goto error; goto error;
case HTTP_LEGAL_REASONS: case HTTP_LEGAL_REASONS:
DEBUG("HTTP 451; URL: %s\n", url); DEBUG("HTTP 451; URL: %s\n", url);
snprintf(err_buf, ERROR_BUFFER_SIZE, "HTTP 451 Unavailable for Legal Reasons\nSome entity is preventing access\nto the host server for legal reasons."); snprintf(err_buf, ERROR_BUFFER_SIZE, language.remote.http451);
goto error; goto error;
case HTTP_INTERNAL_SERVER_ERROR: case HTTP_INTERNAL_SERVER_ERROR:
DEBUG("HTTP 500; URL: %s\n", url); DEBUG("HTTP 500; URL: %s\n", url);
snprintf(err_buf, ERROR_BUFFER_SIZE, "HTTP 500 Internal Server Error\nContact the site administrator."); snprintf(err_buf, ERROR_BUFFER_SIZE, language.remote.http500);
goto error; goto error;
case HTTP_BAD_GATEWAY: case HTTP_BAD_GATEWAY:
DEBUG("HTTP 502; URL: %s\n", url); DEBUG("HTTP 502; URL: %s\n", url);
snprintf(err_buf, ERROR_BUFFER_SIZE, "HTTP 502 Bad Gateway\nContact the site administrator."); snprintf(err_buf, ERROR_BUFFER_SIZE, language.remote.http502);
goto error; goto error;
case HTTP_SERVICE_UNAVAILABLE: case HTTP_SERVICE_UNAVAILABLE:
DEBUG("HTTP 503; URL: %s\n", url); DEBUG("HTTP 503; URL: %s\n", url);
snprintf(err_buf, ERROR_BUFFER_SIZE, "HTTP 503 Service Unavailable\nContact the site administrator."); snprintf(err_buf, ERROR_BUFFER_SIZE, language.remote.http503);
goto error; goto error;
case HTTP_GATEWAY_TIMEOUT: case HTTP_GATEWAY_TIMEOUT:
DEBUG("HTTP 504; URL: %s\n", url); DEBUG("HTTP 504; URL: %s\n", url);
snprintf(err_buf, ERROR_BUFFER_SIZE, "HTTP 504 Gateway Timeout\nContact the site administrator."); snprintf(err_buf, ERROR_BUFFER_SIZE, language.remote.http504);
goto error; goto error;
default: default:
DEBUG("HTTP %u; URL: %s\n", parse, url); DEBUG("HTTP %u; URL: %s\n", parse, url);
snprintf(err_buf, ERROR_BUFFER_SIZE, "HTTP %u\nIf you believe this is unexpected, please\ncontact the site administrator.", parse); snprintf(err_buf, ERROR_BUFFER_SIZE, language.remote.http_unexpected, parse);
goto error; goto error;
} }

View File

@@ -27,6 +27,7 @@
#include "unicode.h" #include "unicode.h"
#include "fs.h" #include "fs.h"
#include "draw.h" #include "draw.h"
#include "ui_strings.h"
void splash_delete(void) void splash_delete(void)
{ {
@@ -54,7 +55,7 @@ void splash_install(const Entry_s * splash)
if(size == 0 && bottom_size == 0) if(size == 0 && bottom_size == 0)
{ {
throw_error("No splash.bin or splashbottom.bin found.\nIs this a splash?", ERROR_LEVEL_WARNING); throw_error(language.splashes.no_splash_found, ERROR_LEVEL_WARNING);
} }
else else
{ {
@@ -65,7 +66,7 @@ void splash_install(const Entry_s * splash)
if(config_buf[0xC] == 0) if(config_buf[0xC] == 0)
{ {
free(config_buf); free(config_buf);
throw_error("WARNING: Splashes are disabled in Luma Config", ERROR_LEVEL_WARNING); throw_error(language.splashes.splash_disabled, ERROR_LEVEL_WARNING);
} }
} }
} }

View File

@@ -28,6 +28,7 @@
#include "unicode.h" #include "unicode.h"
#include "fs.h" #include "fs.h"
#include "draw.h" #include "draw.h"
#include "ui_strings.h"
#define BODY_CACHE_SIZE 0x150000 #define BODY_CACHE_SIZE 0x150000
#define BGM_MAX_SIZE 0x337000 #define BGM_MAX_SIZE 0x337000
@@ -81,7 +82,7 @@ static Result install_theme_internal(const Entry_List_s * themes, int installmod
{ {
free(body); free(body);
DEBUG("body not found\n"); DEBUG("body not found\n");
throw_error("No body_LZ.bin found - is this a theme?", ERROR_LEVEL_WARNING); throw_error(language.themes.no_body_found, ERROR_LEVEL_WARNING);
return MAKERESULT(RL_PERMANENT, RS_CANCELED, RM_APPLICATION, RD_NOT_FOUND); return MAKERESULT(RL_PERMANENT, RS_CANCELED, RM_APPLICATION, RD_NOT_FOUND);
} }
@@ -118,9 +119,12 @@ static Result install_theme_internal(const Entry_List_s * themes, int installmod
return MAKERESULT(RL_PERMANENT, RS_CANCELED, RM_APPLICATION, RD_TOO_LARGE); return MAKERESULT(RL_PERMANENT, RS_CANCELED, RM_APPLICATION, RD_TOO_LARGE);
} }
if (music[0x62] == 1) if (music_size > 0)
{ {
mono_audio = true; if (music[0x62] == 1)
{
mono_audio = true;
}
} }
} }
@@ -177,7 +181,7 @@ static Result install_theme_internal(const Entry_List_s * themes, int installmod
{ {
free(body); free(body);
DEBUG("body not found\n"); DEBUG("body not found\n");
throw_error("No body_LZ.bin found - is this a theme?", ERROR_LEVEL_WARNING); throw_error(language.themes.no_body_found, ERROR_LEVEL_WARNING);
return MAKERESULT(RL_PERMANENT, RS_CANCELED, RM_APPLICATION, RD_NOT_FOUND); return MAKERESULT(RL_PERMANENT, RS_CANCELED, RM_APPLICATION, RD_NOT_FOUND);
} }
@@ -298,7 +302,7 @@ static Result install_theme_internal(const Entry_List_s * themes, int installmod
//---------------------------------------- //----------------------------------------
if (mono_audio) if (mono_audio)
{ {
throw_error("One or more installed themes use mono audio.\nMono audio causes a number of issues.\nCheck the wiki for more information.", ERROR_LEVEL_WARNING); throw_error(language.themes.mono_warn, ERROR_LEVEL_WARNING);
} }
return 0; return 0;
} }
@@ -342,7 +346,7 @@ dir_name_callback(void * data, const char ** ppMessage, const char * text, size_
(void)data; (void)data;
if(strpbrk(text, "><\"?;:/\\+,.|[=]")) if(strpbrk(text, "><\"?;:/\\+,.|[=]"))
{ {
*ppMessage = "Illegal character used."; *ppMessage = language.themes.illegal_char;
return SWKBD_CALLBACK_CONTINUE; return SWKBD_CALLBACK_CONTINUE;
} }
return SWKBD_CALLBACK_OK; return SWKBD_CALLBACK_OK;
@@ -356,10 +360,10 @@ Result dump_current_theme(void)
SwkbdState swkbd; SwkbdState swkbd;
swkbdInit(&swkbd, SWKBD_TYPE_WESTERN, 2, max_chars); swkbdInit(&swkbd, SWKBD_TYPE_WESTERN, 2, max_chars);
swkbdSetHintText(&swkbd, "Name of output folder"); swkbdSetHintText(&swkbd, language.themes.name_folder);
swkbdSetButton(&swkbd, SWKBD_BUTTON_LEFT, "Cancel", false); swkbdSetButton(&swkbd, SWKBD_BUTTON_LEFT, language.themes.cancel, false);
swkbdSetButton(&swkbd, SWKBD_BUTTON_RIGHT, "Done", true); swkbdSetButton(&swkbd, SWKBD_BUTTON_RIGHT, language.themes.done, true);
swkbdSetValidation(&swkbd, SWKBD_NOTEMPTY_NOTBLANK, 0, max_chars); swkbdSetValidation(&swkbd, SWKBD_NOTEMPTY_NOTBLANK, 0, max_chars);
swkbdSetFilterCallback(&swkbd, dir_name_callback, NULL); swkbdSetFilterCallback(&swkbd, dir_name_callback, NULL);

381
source/ui_strings.c Normal file
View File

@@ -0,0 +1,381 @@
/*
* This file is part of Anemone3DS
* Copyright (C) 2016-2024 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.
*/
#include "ui_strings.h"
#include "fs.h"
const Language_s language_english = {
.normal_instructions =
{
{
.info_line = NULL,
.instructions = {
{
"\uE000 Install Theme(s)",
"\uE001 Queue shuffle theme"
},
{
"\uE002 More options",
"\uE003 Preview theme"
},
{
"\uE004 Switch to splashes",
"\uE005 Scan QR code"
},
{
"Exit",
"Delete from SD"
}
}
},
{
.info_line = NULL,
.instructions = {
{
"\uE000 Install splash",
"\uE001 Delete installed splash"
},
{
"\uE002 More options",
"\uE003 Preview splash"
},
{
"\uE004 Switch to themes",
"\uE005 Scan QR code"
},
{
"Exit",
"Delete from SD"
}
}
}
},
.install_instructions =
{
.info_line = "\uE001 Cancel theme install",
.instructions = {
{
"\uE079 Normal install",
"\uE07A Shuffle install"
},
{
"\uE07B BGM-only install",
"\uE07C No-BGM install"
},
{
NULL,
NULL
},
{
"Exit",
NULL
}
}
},
.extra_instructions =
{
{
.info_line = "\uE001 Leave sorting menu",
.instructions = {
{
"\uE079 Sort by name",
"\uE07A Sort by author"
},
{
"\uE07B Sort by filename",
NULL
},
{
NULL,
NULL
},
{
"Exit",
NULL
}
}
},
{
.info_line = "\uE001 Leave extra menu",
.instructions = {
{
"\uE079 Jump in the list",
"\uE07A Reload broken icons"
},
{
"\uE07B Browse ThemePlaza",
NULL
},
{
"\uE004 Sorting menu",
"\uE005 Dumping menu"
},
{
"Exit",
NULL
}
}
},
{
.info_line = "\uE001 Leave dump menu",
.instructions = {
{
"\uE079 Dump Current Theme",
"\uE07A Dump All Themes"
},
{
NULL,
NULL
},
{
NULL,
NULL
},
{
"Exit",
NULL
}
}
}
},
.camera =
{
.quit = "Press \uE005 To Quit",
.thread_error = "Capture cam thread creation failed\nPlease report this to the developers",
.zip_not_theme_splash = "Zip downloaded is neither\na splash nor a theme",
.file_not_zip = "File downloaded isn't a zip.",
.download_failed = "Download failed.",
},
.draw =
{
.theme_mode = "Theme mode",
.splash_mode = "Splash mode",
.no_themes = "No theme found",
.no_splashes = "No splash found",
.qr_download = "Press \uE005 to download from QR",
.switch_splashes = "Or \uE004 to switch to splashes",
.switch_themes = "Or \uE004 to switch to themes",
.quit = "Or to quit",
.start_pos = 162, // Adjust x pos of start glyph to line up with quit string
.by = "By ",
.selected = "Selected:",
.sel = "Sel.:",
.tp_theme_mode = "ThemePlaza Theme mode",
.tp_splash_mode = "ThemePlaza Splash mode",
.search = "Search...",
.page = "Page:",
.err_quit = "Press \uE000 to quit.",
.warn_continue = "Press \uE000 to continue.",
.yes_no = "\uE000 Yes \uE001 No",
.load_themes = "Loading themes, please wait...",
.load_splash = "Loading splashes, please wait...",
.load_icons = "Loading icons, please wait...",
.install_splash = "Installing a splash...",
.delete_splash = "Deleting installed splash...",
.install_theme = "Installing a single theme...",
.install_shuffle = "Installing shuffle themes...",
.install_bgm = "Installing BGM-only theme...",
.install_no_bgm = "Installing theme without BGM...",
.downloading = "Downloading...",
.checking_dl = "Checking downloaded file...",
.delete_sd = "Deleting from SD...",
.download_themes = "Downloading theme list, please wait...",
.download_splashes = "Downloading splash list, please wait...",
.download_preview = "Downloading preview, please wait...",
.download_bgm = "Downloading BGM, please wait...",
.dump_single = "Dumping theme, please wait...",
.dump_all_official = "Dumping official themes, please wait...",
},
.fs =
{
.illegal_input = "Input must not contain:\n" ILLEGAL_CHARS,
.new_or_overwrite = "Choose a new filename or tap Overwrite",
.cancel = "Cancel",
.overwrite = "Overwrite",
.rename = "Rename",
.swkbd_fail = "???\nTry a USB keyboard", // Should never be used
.sd_full = "SD card is full.\nDelete some themes to make space.",
.fs_error = "Error:\nGet a new SD card.",
},
.loading =
{
.no_preview = "No preview found.",
},
.main =
{
.position_too_big = "The new position has to be\nsmaller or equal to the\nnumber of entries!",
.position_zero = "The new position has to\nbe positive!",
.jump_q = "Where do you want to jump to?\nMay cause icons to reload.",
.cancel = "Cancel",
.jump = "Jump",
.no_theme_extdata = "Theme extdata does not exist!\nSet a default theme from the home menu.",
.loading_qr = "Loading QR Scanner...",
.no_wifi = "Please connect to Wi-Fi before scanning QR codes",
.qr_homebrew = "QR scanning doesnt work from the Homebrew\nLauncher, use the ThemePlaza browser instead.",
.camera_broke = "Your camera seems to have a problem,\nunable to scan QR codes.",
.too_many_themes = "You have too many themes selected.",
.not_enough_themes = "You don't have enough themes selected.",
.uninstall_confirm = "Are you sure you would like to delete\nthe installed splash?",
.delete_confirm = "Are you sure you would like to delete this?",
},
.remote =
{
.no_results = "No results for this search.",
.check_wifi = "Couldn't download Theme Plaza data.\nMake sure WiFi is on.",
.new_page_big = "The new page has to be\nsmaller or equal to the\nnumber of pages!",
.new_page_zero = "The new position has to\nbe positive!",
.jump_page = "Which page do you want to jump to?",
.cancel = "Cancel",
.jump = "Jump",
.tags = "Which tags do you want to search for?",
.search = "Search",
.parental_fail = "Parental Control validation failed!\nBrowser Access restricted.",
.zip_not_found = "ZIP not found at this URL\nIf you believe this is an error, please\ncontact the site administrator",
.generic_httpc_error = "Error in HTTPC sysmodule - 0x%08lx.\nIf you are seeing this, please contact an\nAnemone developer on the Theme Plaza Discord.",
.http303_tp = "HTTP 303 See Other (Theme Plaza)\nHas this theme been approved?",
.http303 = "HTTP 303 See Other\nDownload the resource directly\nor contact the site administrator.",
.http404 = "HTTP 404 Not Found\nHas this theme been approved?",
.http_err_url = "HTTP %s\nCheck that the URL is correct.",
.http_errcode_generic = "HTTP %s\nContact the site administrator.",
.http401 = "401 Unauthorized",
.http403 = "403 Forbidden",
.http407 = "407 Proxy Authentication Required",
.http414 = "HTTP 414 URI Too Long\nThe QR code points to a really long URL.\nDownload the file directly.",
.http418 = "HTTP 418 I'm a teapot\nContact the site administrator.",
.http426 = "HTTP 426 Upgrade Required\nThe 3DS cannot connect to this server.\nContact the site administrator.",
.http451 = "HTTP 451 Unavailable for Legal Reasons\nSome entity is preventing access\nto the host server for legal reasons.",
.http500 = "HTTP 500 Internal Server Error\nContact the site administrator.",
.http502 = "HTTP 502 Bad Gateway\nContact the site administrator.",
.http503 = "HTTP 503 Service Unavailable\nContact the site administrator.",
.http504 = "HTTP 504 Gateway Timeout\nContact the site administrator.",
.http_unexpected = "HTTP %u\nIf you believe this is unexpected, please\ncontact the site administrator.",
},
.remote_instructions =
{
{
.info_line = NULL,
.instructions = {
{
"\uE000 Download theme",
"\uE001 Go back"
},
{
"\uE002 Hold for more",
"\uE003 Preview theme"
},
{
"\uE004 Previous page",
"\uE005 Next page"
},
{
"Exit",
NULL
}
}
},
{
.info_line = NULL,
.instructions = {
{
"\uE000 Download splash",
"\uE001 Go back"
},
{
"\uE002 Hold for more",
"\uE003 Preview splash"
},
{
"\uE004 Previous page",
"\uE005 Next page"
},
{
"Exit",
NULL
}
}
}
},
.remote_extra_instructions =
{
.info_line = "Release \uE002 to cancel or hold \uE006 and release \uE002 to do stuff",
.instructions = {
{
"\uE079 Jump to page",
"\uE07A Search tags"
},
{
"\uE07B Toggle splash/theme",
"\uE07C Reload without cache"
},
{
NULL,
NULL
},
{
"Exit",
NULL
}
}
},
.splashes =
{
.no_splash_found = "No splash.bin or splashbottom.bin found.\nIs this a splash?",
.splash_disabled = "WARNING: Splashes are disabled in Luma Config",
},
.themes =
{
.no_body_found = "No body_LZ.bin found - is this a theme?",
.mono_warn = "One or more installed themes use mono audio.\nMono audio causes a number of issues.\nCheck the wiki for more information.",
.illegal_char = "Illegal character used.",
.name_folder = "Name of output folder",
.cancel = "Cancel",
.done = "Done"
}
};
Language_s init_strings(CFG_Language lang)
{
switch (lang)
{
case CFG_LANGUAGE_JP:
case CFG_LANGUAGE_FR:
case CFG_LANGUAGE_DE:
case CFG_LANGUAGE_IT:
case CFG_LANGUAGE_ES:
case CFG_LANGUAGE_ZH:
case CFG_LANGUAGE_KO:
case CFG_LANGUAGE_NL:
case CFG_LANGUAGE_PT:
case CFG_LANGUAGE_RU:
case CFG_LANGUAGE_TW:
case CFG_LANGUAGE_EN:
return language_english;
default:
return language_english;
}
}