Performance Improvements
Pass theme list & its entries around by reference rather than copying them. Fix bug in async icon loading that caused icons to be loaded multiple times. Original PR by @LiquidFenrir
This commit is contained in:
@@ -30,7 +30,7 @@
|
|||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
u16* camera_buffer;
|
u16 * camera_buffer;
|
||||||
|
|
||||||
Handle event_stop;
|
Handle event_stop;
|
||||||
Thread cam_thread, ui_thread;
|
Thread cam_thread, ui_thread;
|
||||||
@@ -45,7 +45,7 @@ typedef struct {
|
|||||||
|
|
||||||
bool any_update;
|
bool any_update;
|
||||||
|
|
||||||
struct quirc* context;
|
struct quirc * context;
|
||||||
} qr_data;
|
} qr_data;
|
||||||
|
|
||||||
bool init_qr(void);
|
bool init_qr(void);
|
||||||
|
|||||||
@@ -38,9 +38,19 @@
|
|||||||
#define DEBUG(...) fprintf(stderr, __VA_ARGS__)
|
#define DEBUG(...) fprintf(stderr, __VA_ARGS__)
|
||||||
#define POS() DEBUG("%s (line %d)...\n", __func__, __LINE__)
|
#define POS() DEBUG("%s (line %d)...\n", __func__, __LINE__)
|
||||||
|
|
||||||
#define DEBUGPOS(...) \
|
#define DEBUGPOS(...) do {\
|
||||||
POS(); \
|
POS(); \
|
||||||
DEBUG(__VA_ARGS__)
|
DEBUG(__VA_ARGS__); \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
static inline int min(const int a, const int b)
|
||||||
|
{
|
||||||
|
return a > b ? b : a;
|
||||||
|
}
|
||||||
|
static inline int max(const int a, const int b)
|
||||||
|
{
|
||||||
|
return a < b ? b : a;
|
||||||
|
}
|
||||||
|
|
||||||
#define FASTSCROLL_WAIT 1e8
|
#define FASTSCROLL_WAIT 1e8
|
||||||
|
|
||||||
|
|||||||
@@ -33,7 +33,7 @@
|
|||||||
|
|
||||||
#define MAX_LINES 10
|
#define MAX_LINES 10
|
||||||
|
|
||||||
typedef enum {
|
typedef enum InstallType_e {
|
||||||
INSTALL_LOADING_THEMES,
|
INSTALL_LOADING_THEMES,
|
||||||
INSTALL_LOADING_SPLASHES,
|
INSTALL_LOADING_SPLASHES,
|
||||||
INSTALL_LOADING_ICONS,
|
INSTALL_LOADING_ICONS,
|
||||||
@@ -149,8 +149,8 @@ typedef struct {
|
|||||||
const char * instructions[BUTTONS_INFO_LINES][BUTTONS_INFO_COLUNMNS];
|
const char * instructions[BUTTONS_INFO_LINES][BUTTONS_INFO_COLUNMNS];
|
||||||
} Instructions_s;
|
} Instructions_s;
|
||||||
|
|
||||||
extern C3D_RenderTarget* top;
|
extern C3D_RenderTarget * top;
|
||||||
extern C3D_RenderTarget* bottom;
|
extern C3D_RenderTarget * bottom;
|
||||||
extern C2D_TextBuf staticBuf, dynamicBuf;
|
extern C2D_TextBuf staticBuf, dynamicBuf;
|
||||||
|
|
||||||
extern C2D_Text text[TEXT_AMOUNT];
|
extern C2D_Text text[TEXT_AMOUNT];
|
||||||
@@ -162,8 +162,8 @@ void start_frame(void);
|
|||||||
void end_frame(void);
|
void end_frame(void);
|
||||||
void set_screen(C3D_RenderTarget * screen);
|
void set_screen(C3D_RenderTarget * screen);
|
||||||
|
|
||||||
void throw_error(const char* error, ErrorLevel level);
|
void throw_error(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);
|
||||||
|
|
||||||
@@ -177,7 +177,7 @@ void draw_text_center(gfxScreen_t target, float y, float z, float scaleX, float
|
|||||||
void draw_home(u64 start_time, u64 cur_time);
|
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);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
106
include/entries_list.h
Normal file
106
include/entries_list.h
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
/*
|
||||||
|
* 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 ENTRIES_LIST_H
|
||||||
|
#define ENTRIES_LIST_H
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include <jansson.h>
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SORT_NONE,
|
||||||
|
|
||||||
|
SORT_NAME,
|
||||||
|
SORT_AUTHOR,
|
||||||
|
SORT_PATH,
|
||||||
|
} SortMode;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
u16 path[0x106];
|
||||||
|
bool is_zip;
|
||||||
|
bool in_shuffle;
|
||||||
|
bool no_bgm_shuffle;
|
||||||
|
bool installed;
|
||||||
|
u32 placeholder_color; // doubles as not-info-loaded when == 0
|
||||||
|
|
||||||
|
json_int_t tp_download_id;
|
||||||
|
u16 name[0x41];
|
||||||
|
u16 desc[0x81];
|
||||||
|
u16 author[0x41];
|
||||||
|
} Entry_s;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
Tex3DS_SubTexture subtex;
|
||||||
|
u16 x, y;
|
||||||
|
} Entry_Icon_s;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
Entry_s * entries;
|
||||||
|
int entries_count;
|
||||||
|
int entries_capacity;
|
||||||
|
|
||||||
|
C3D_Tex icons_texture;
|
||||||
|
Entry_Icon_s * icons_info;
|
||||||
|
|
||||||
|
int previous_scroll;
|
||||||
|
int scroll;
|
||||||
|
|
||||||
|
int previous_selected;
|
||||||
|
int selected_entry;
|
||||||
|
|
||||||
|
int shuffle_count;
|
||||||
|
|
||||||
|
EntryMode mode;
|
||||||
|
int entries_per_screen_v; // rows of entries on 1 screen
|
||||||
|
int entries_per_screen_h; // columns of entries on 1 screen
|
||||||
|
int entries_loaded; // amount of entries on 1 screen
|
||||||
|
int entry_size; // size in pixels of an entry icon
|
||||||
|
|
||||||
|
SortMode current_sort;
|
||||||
|
|
||||||
|
json_int_t tp_current_page;
|
||||||
|
json_int_t tp_page_count;
|
||||||
|
char * tp_search;
|
||||||
|
const char * loading_path;
|
||||||
|
} Entry_List_s;
|
||||||
|
|
||||||
|
void sort_by_name(Entry_List_s * list);
|
||||||
|
void sort_by_author(Entry_List_s * list);
|
||||||
|
void sort_by_filename(Entry_List_s * list);
|
||||||
|
|
||||||
|
void delete_entry(Entry_s * entry, bool is_file);
|
||||||
|
// assumes list has been memset to 0
|
||||||
|
typedef enum InstallType_e InstallType;
|
||||||
|
Result load_entries(const char * loading_path, Entry_List_s * list, const InstallType loading_screen);
|
||||||
|
u32 load_data(const char * filename, const Entry_s * entry, char ** buf);
|
||||||
|
C2D_Image get_icon_at(Entry_List_s * list, size_t index);
|
||||||
|
|
||||||
|
// assumes list doesn't have any elements yet
|
||||||
|
void list_init_capacity(Entry_List_s * list, const int init_capacity);
|
||||||
|
// assumes list has been inited with a non zero capacity
|
||||||
|
ssize_t list_add_entry(Entry_List_s * list);
|
||||||
|
|
||||||
|
#endif
|
||||||
12
include/fs.h
12
include/fs.h
@@ -56,13 +56,13 @@ Result open_archives(void);
|
|||||||
Result close_archives(void);
|
Result close_archives(void);
|
||||||
Result load_parental_controls(Parental_Restrictions_s *restrictions);
|
Result load_parental_controls(Parental_Restrictions_s *restrictions);
|
||||||
|
|
||||||
u32 file_to_buf(FS_Path path, FS_Archive archive, char** buf);
|
u32 file_to_buf(FS_Path path, FS_Archive archive, char ** buf);
|
||||||
u32 zip_memory_to_buf(char *file_name, void * zip_memory, size_t zip_size, char ** buf);
|
u32 zip_memory_to_buf(const char * file_name, void * zip_memory, size_t zip_size, char ** buf);
|
||||||
u32 zip_file_to_buf(char *file_name, u16 *zip_path, char **buf);
|
u32 zip_file_to_buf(const char * file_name, const u16 * zip_path, char ** buf);
|
||||||
u32 decompress_lz_file(FS_Path file_name, FS_Archive archive, char **buf);
|
u32 decompress_lz_file(FS_Path file_name, FS_Archive archive, char ** buf);
|
||||||
u32 compress_lz_file_fast(FS_Path path, FS_Archive archive, char *in_buf, u32 size);
|
u32 compress_lz_file_fast(FS_Path path, FS_Archive archive, char * in_buf, u32 size);
|
||||||
|
|
||||||
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);
|
void save_zip_to_sd(char * filename, u32 size, char * buf, EntryMode mode);
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
#define LOADING_H
|
#define LOADING_H
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include "entries_list.h"
|
||||||
#include "music.h"
|
#include "music.h"
|
||||||
#include <jansson.h>
|
#include <jansson.h>
|
||||||
|
|
||||||
@@ -45,14 +46,6 @@ enum ICON_IDS_OFFSET {
|
|||||||
ICONS_OFFSET_AMOUNT,
|
ICONS_OFFSET_AMOUNT,
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
SORT_NONE,
|
|
||||||
|
|
||||||
SORT_NAME,
|
|
||||||
SORT_AUTHOR,
|
|
||||||
SORT_PATH,
|
|
||||||
} SortMode;
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
u8 _padding1[4 + 2 + 2];
|
u8 _padding1[4 + 2 + 2];
|
||||||
|
|
||||||
@@ -61,76 +54,26 @@ typedef struct {
|
|||||||
u16 author[0x40];
|
u16 author[0x40];
|
||||||
|
|
||||||
u8 _padding2[0x2000 - 0x200 + 0x30 + 0x8];
|
u8 _padding2[0x2000 - 0x200 + 0x30 + 0x8];
|
||||||
u16 small_icon[24*24];
|
u16 small_icon[24 * 24];
|
||||||
|
|
||||||
u16 big_icon[48*48];
|
u16 big_icon[48 * 48];
|
||||||
} Icon_s;
|
} Icon_s;
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
u16 name[0x41];
|
|
||||||
u16 desc[0x81];
|
|
||||||
u16 author[0x41];
|
|
||||||
|
|
||||||
u32 placeholder_color;
|
|
||||||
|
|
||||||
u16 path[0x106];
|
|
||||||
bool is_zip;
|
|
||||||
|
|
||||||
bool in_shuffle;
|
|
||||||
bool no_bgm_shuffle;
|
|
||||||
bool installed;
|
|
||||||
|
|
||||||
json_int_t tp_download_id;
|
|
||||||
} Entry_s;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
Entry_s * entries;
|
|
||||||
int entries_count;
|
|
||||||
|
|
||||||
C2D_Image ** icons;
|
|
||||||
|
|
||||||
int previous_scroll;
|
|
||||||
int scroll;
|
|
||||||
|
|
||||||
int previous_selected;
|
|
||||||
int selected_entry;
|
|
||||||
|
|
||||||
int shuffle_count;
|
|
||||||
|
|
||||||
EntryMode mode;
|
|
||||||
int entries_per_screen_v;
|
|
||||||
int entries_per_screen_h;
|
|
||||||
int entries_loaded;
|
|
||||||
int entry_size;
|
|
||||||
|
|
||||||
SortMode current_sort;
|
|
||||||
|
|
||||||
json_int_t tp_current_page;
|
|
||||||
json_int_t tp_page_count;
|
|
||||||
char * tp_search;
|
|
||||||
} Entry_List_s;
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
void ** thread_arg;
|
void ** thread_arg;
|
||||||
volatile bool run_thread;
|
volatile bool run_thread;
|
||||||
} Thread_Arg_s;
|
} Thread_Arg_s;
|
||||||
|
|
||||||
C2D_Image * loadTextureIcon(Icon_s *icon);
|
void copy_texture_data(C3D_Tex * texture, const u16 * src, const Entry_Icon_s * current_icon);
|
||||||
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);
|
||||||
|
|
||||||
void sort_by_name(Entry_List_s * list);
|
|
||||||
void sort_by_author(Entry_List_s * list);
|
|
||||||
void sort_by_filename(Entry_List_s * list);
|
|
||||||
|
|
||||||
void delete_entry(Entry_s * entry, bool is_file);
|
|
||||||
Result load_entries(const char * loading_path, Entry_List_s * list);
|
|
||||||
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);
|
||||||
bool load_preview(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(Entry_s, audio_s *);
|
Result load_audio(const Entry_s *, audio_s *);
|
||||||
void load_icons_first(Entry_List_s * current_list, bool silent);
|
void load_icons_first(Entry_List_s * current_list, bool silent);
|
||||||
void handle_scrolling(Entry_List_s * list);
|
void handle_scrolling(Entry_List_s * list);
|
||||||
void load_icons_thread(void * void_arg);
|
void load_icons_thread(void * void_arg);
|
||||||
u32 load_data(char * filename, Entry_s entry, char ** buf);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ typedef struct {
|
|||||||
float mix[12];
|
float mix[12];
|
||||||
u8 buf_pos;
|
u8 buf_pos;
|
||||||
long data_read;
|
long data_read;
|
||||||
char *filebuf;
|
char * filebuf;
|
||||||
u32 filesize;
|
u32 filesize;
|
||||||
|
|
||||||
volatile bool stop;
|
volatile bool stop;
|
||||||
@@ -50,6 +50,6 @@ typedef struct {
|
|||||||
} audio_s;
|
} audio_s;
|
||||||
|
|
||||||
void play_audio(audio_s *);
|
void play_audio(audio_s *);
|
||||||
void stop_audio(audio_s**);
|
void stop_audio(audio_s **);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -52,6 +52,6 @@
|
|||||||
#define CACHE_PATH_FORMAT "/3ds/" APP_TITLE "/cache/%" JSON_INTEGER_FORMAT
|
#define CACHE_PATH_FORMAT "/3ds/" APP_TITLE "/cache/%" JSON_INTEGER_FORMAT
|
||||||
|
|
||||||
bool themeplaza_browser(EntryMode mode);
|
bool themeplaza_browser(EntryMode mode);
|
||||||
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);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
#include "loading.h"
|
#include "loading.h"
|
||||||
|
|
||||||
void splash_delete(void);
|
void splash_delete(void);
|
||||||
void splash_install(Entry_s splash);
|
void splash_install(const Entry_s * splash);
|
||||||
|
|
||||||
void splash_check_installed(void * void_arg);
|
void splash_check_installed(void * void_arg);
|
||||||
|
|
||||||
|
|||||||
@@ -64,17 +64,17 @@ typedef struct {
|
|||||||
u32 dlc_theme_content_index;
|
u32 dlc_theme_content_index;
|
||||||
u32 use_theme_cache;
|
u32 use_theme_cache;
|
||||||
|
|
||||||
u8 _padding1[0x338 - 8*sizeof(u32)];
|
u8 _padding1[0x338 - 8 * sizeof(u32)];
|
||||||
|
|
||||||
u32 shuffle_body_sizes[MAX_SHUFFLE_THEMES];
|
u32 shuffle_body_sizes[MAX_SHUFFLE_THEMES];
|
||||||
u32 shuffle_music_sizes[MAX_SHUFFLE_THEMES];
|
u32 shuffle_music_sizes[MAX_SHUFFLE_THEMES];
|
||||||
} ThemeManage_bin_s;
|
} ThemeManage_bin_s;
|
||||||
|
|
||||||
Result theme_install(Entry_s theme);
|
Result theme_install(Entry_s * theme);
|
||||||
Result no_bgm_install(Entry_s theme);
|
Result no_bgm_install(Entry_s * theme);
|
||||||
Result bgm_install(Entry_s theme);
|
Result bgm_install(Entry_s * theme);
|
||||||
|
|
||||||
Result shuffle_install(Entry_List_s themes);
|
Result shuffle_install(const Entry_List_s * themes);
|
||||||
|
|
||||||
Result dump_current_theme(void);
|
Result dump_current_theme(void);
|
||||||
Result dump_all_themes(void);
|
Result dump_all_themes(void);
|
||||||
|
|||||||
@@ -29,9 +29,9 @@
|
|||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
ssize_t strulen(const u16*, ssize_t);
|
ssize_t strulen(const u16 *, ssize_t);
|
||||||
void struacat(u16 *input, const char *addition);
|
void struacat(u16 * input, const char * addition);
|
||||||
void printu(u16 *input);
|
void printu(u16 * input);
|
||||||
u16 *strucat(u16 *destination, const u16 *source);
|
u16 * strucat(u16 * destination, const u16 * source);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -36,7 +36,7 @@
|
|||||||
#include <archive.h>
|
#include <archive.h>
|
||||||
#include <archive_entry.h>
|
#include <archive_entry.h>
|
||||||
|
|
||||||
static void start_read(qr_data *data)
|
static void start_read(qr_data * data)
|
||||||
{
|
{
|
||||||
LightLock_Lock(&data->mut);
|
LightLock_Lock(&data->mut);
|
||||||
while(data->writer_waiting || data->writer_active)
|
while(data->writer_waiting || data->writer_active)
|
||||||
@@ -47,7 +47,7 @@ static void start_read(qr_data *data)
|
|||||||
AtomicIncrement(&data->num_readers_active);
|
AtomicIncrement(&data->num_readers_active);
|
||||||
LightLock_Unlock(&data->mut);
|
LightLock_Unlock(&data->mut);
|
||||||
}
|
}
|
||||||
static void stop_read(qr_data *data)
|
static void stop_read(qr_data * data)
|
||||||
{
|
{
|
||||||
LightLock_Lock(&data->mut);
|
LightLock_Lock(&data->mut);
|
||||||
AtomicDecrement(&data->num_readers_active);
|
AtomicDecrement(&data->num_readers_active);
|
||||||
@@ -57,7 +57,7 @@ static void stop_read(qr_data *data)
|
|||||||
}
|
}
|
||||||
LightLock_Unlock(&data->mut);
|
LightLock_Unlock(&data->mut);
|
||||||
}
|
}
|
||||||
static void start_write(qr_data *data)
|
static void start_write(qr_data * data)
|
||||||
{
|
{
|
||||||
LightLock_Lock(&data->mut);
|
LightLock_Lock(&data->mut);
|
||||||
data->writer_waiting = true;
|
data->writer_waiting = true;
|
||||||
@@ -72,7 +72,7 @@ static void start_write(qr_data *data)
|
|||||||
|
|
||||||
LightLock_Unlock(&data->mut);
|
LightLock_Unlock(&data->mut);
|
||||||
}
|
}
|
||||||
static void stop_write(qr_data *data)
|
static void stop_write(qr_data * data)
|
||||||
{
|
{
|
||||||
LightLock_Lock(&data->mut);
|
LightLock_Lock(&data->mut);
|
||||||
|
|
||||||
@@ -82,16 +82,16 @@ static void stop_write(qr_data *data)
|
|||||||
LightLock_Unlock(&data->mut);
|
LightLock_Unlock(&data->mut);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void capture_cam_thread(void *arg)
|
static void capture_cam_thread(void * arg)
|
||||||
{
|
{
|
||||||
qr_data *data = (qr_data *) arg;
|
qr_data * data = (qr_data *) arg;
|
||||||
|
|
||||||
Handle cam_events[3] = {0};
|
Handle cam_events[3] = {0};
|
||||||
cam_events[0] = data->event_stop;
|
cam_events[0] = data->event_stop;
|
||||||
|
|
||||||
u32 transferUnit;
|
u32 transferUnit;
|
||||||
const u32 bufsz = 400 * 240 * sizeof(u16);
|
const u32 bufsz = 400 * 240 * sizeof(u16);
|
||||||
u16 *buffer = linearAlloc(bufsz);
|
u16 * buffer = linearAlloc(bufsz);
|
||||||
|
|
||||||
camInit();
|
camInit();
|
||||||
CAMU_SetSize(SELECT_OUT1, SIZE_CTR_TOP_LCD, CONTEXT_A);
|
CAMU_SetSize(SELECT_OUT1, SIZE_CTR_TOP_LCD, CONTEXT_A);
|
||||||
@@ -167,9 +167,9 @@ static void capture_cam_thread(void *arg)
|
|||||||
LightEvent_Signal(&data->event_cam_info);
|
LightEvent_Signal(&data->event_cam_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void update_ui(void *arg)
|
static void update_ui(void * arg)
|
||||||
{
|
{
|
||||||
qr_data* data = (qr_data*) arg;
|
qr_data * data = (qr_data *) arg;
|
||||||
C3D_Tex tex;
|
C3D_Tex tex;
|
||||||
|
|
||||||
static const Tex3DS_SubTexture subt3x = { 400, 240, 0.0f, 1.0f, 400.0f/512.0f, 1.0f - (240.0f/256.0f) };
|
static const Tex3DS_SubTexture subt3x = { 400, 240, 0.0f, 1.0f, 400.0f/512.0f, 1.0f - (240.0f/256.0f) };
|
||||||
@@ -190,7 +190,7 @@ static void update_ui(void *arg)
|
|||||||
for(u32 x = 0; x < 400; x++) {
|
for(u32 x = 0; x < 400; x++) {
|
||||||
const u32 dstPos = ((((y >> 3) * (512 >> 3) + (x >> 3)) << 6) + ((x & 1) | ((y & 1) << 1) | ((x & 2) << 1) | ((y & 2) << 2) | ((x & 4) << 2) | ((y & 4) << 3)));
|
const u32 dstPos = ((((y >> 3) * (512 >> 3) + (x >> 3)) << 6) + ((x & 1) | ((y & 1) << 1) | ((x & 2) << 1) | ((y & 2) << 2) | ((x & 4) << 2) | ((y & 4) << 3)));
|
||||||
|
|
||||||
((u16*)tex.data)[dstPos] = data->camera_buffer[srcPos + x];
|
((u16 *)tex.data)[dstPos] = data->camera_buffer[srcPos + x];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
data->any_update = false;
|
data->any_update = false;
|
||||||
@@ -208,7 +208,7 @@ static void update_ui(void *arg)
|
|||||||
LightEvent_Signal(&data->event_ui_info);
|
LightEvent_Signal(&data->event_ui_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool start_capture_cam(qr_data *data)
|
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)
|
||||||
{
|
{
|
||||||
@@ -225,11 +225,11 @@ static bool start_capture_cam(qr_data *data)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool update_qr(qr_data *data, struct quirc_data* scan_data)
|
static bool update_qr(qr_data * data, struct quirc_data * scan_data)
|
||||||
{
|
{
|
||||||
int w;
|
int w;
|
||||||
int h;
|
int h;
|
||||||
u8 *image = (u8*) quirc_begin(data->context, &w, &h);
|
u8 * image = (u8 *)quirc_begin(data->context, &w, &h);
|
||||||
|
|
||||||
start_read(data);
|
start_read(data);
|
||||||
for (int y = 0; y < h; y++) {
|
for (int y = 0; y < h; y++) {
|
||||||
@@ -256,7 +256,7 @@ static bool update_qr(qr_data *data, struct quirc_data* scan_data)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void start_qr(qr_data *data)
|
static void start_qr(qr_data * data)
|
||||||
{
|
{
|
||||||
svcCreateEvent(&data->event_stop, RESET_STICKY);
|
svcCreateEvent(&data->event_stop, RESET_STICKY);
|
||||||
LightEvent_Init(&data->event_cam_info, RESET_STICKY);
|
LightEvent_Init(&data->event_cam_info, RESET_STICKY);
|
||||||
@@ -272,7 +272,7 @@ static void start_qr(qr_data *data)
|
|||||||
quirc_resize(data->context, 400, 240);
|
quirc_resize(data->context, 400, 240);
|
||||||
data->camera_buffer = calloc(1, 400 * 240 * sizeof(u16));
|
data->camera_buffer = calloc(1, 400 * 240 * sizeof(u16));
|
||||||
}
|
}
|
||||||
static void exit_qr(qr_data *data)
|
static void exit_qr(qr_data * data)
|
||||||
{
|
{
|
||||||
svcSignalEvent(data->event_stop);
|
svcSignalEvent(data->event_stop);
|
||||||
|
|
||||||
@@ -308,7 +308,7 @@ bool init_qr(void)
|
|||||||
|
|
||||||
start_qr(&data);
|
start_qr(&data);
|
||||||
|
|
||||||
struct quirc_data* scan_data = calloc(1, sizeof(struct quirc_data));
|
struct quirc_data * scan_data = calloc(1, sizeof(struct quirc_data));
|
||||||
const bool ready = start_capture_cam(&data);
|
const bool ready = start_capture_cam(&data);
|
||||||
bool finished = !ready;
|
bool finished = !ready;
|
||||||
|
|
||||||
@@ -333,6 +333,7 @@ bool init_qr(void)
|
|||||||
char * zip_buf = NULL;
|
char * zip_buf = NULL;
|
||||||
char * filename = NULL;
|
char * filename = NULL;
|
||||||
u32 zip_size;
|
u32 zip_size;
|
||||||
|
|
||||||
Result res = http_get((char*)scan_data->payload, &filename, &zip_buf, &zip_size, INSTALL_DOWNLOAD, "application/zip; application/x-zip-compressed");
|
Result res = http_get((char*)scan_data->payload, &filename, &zip_buf, &zip_size, INSTALL_DOWNLOAD, "application/zip; application/x-zip-compressed");
|
||||||
if (R_FAILED(res))
|
if (R_FAILED(res))
|
||||||
{
|
{
|
||||||
@@ -350,7 +351,7 @@ bool init_qr(void)
|
|||||||
{
|
{
|
||||||
draw_install(INSTALL_CHECKING_DOWNLOAD);
|
draw_install(INSTALL_CHECKING_DOWNLOAD);
|
||||||
|
|
||||||
struct archive *a = archive_read_new();
|
struct archive * a = archive_read_new();
|
||||||
archive_read_support_format_zip(a);
|
archive_read_support_format_zip(a);
|
||||||
|
|
||||||
int r = archive_read_open_memory(a, zip_buf, zip_size);
|
int r = archive_read_open_memory(a, zip_buf, zip_size);
|
||||||
|
|||||||
@@ -32,8 +32,8 @@
|
|||||||
|
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
C3D_RenderTarget* top;
|
C3D_RenderTarget * top;
|
||||||
C3D_RenderTarget* bottom;
|
C3D_RenderTarget * bottom;
|
||||||
C2D_TextBuf staticBuf, dynamicBuf;
|
C2D_TextBuf staticBuf, dynamicBuf;
|
||||||
static C2D_TextBuf widthBuf;
|
static C2D_TextBuf widthBuf;
|
||||||
|
|
||||||
@@ -285,7 +285,7 @@ void draw_base_interface(void)
|
|||||||
set_screen(top);
|
set_screen(top);
|
||||||
}
|
}
|
||||||
|
|
||||||
void throw_error(const 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;
|
||||||
@@ -318,7 +318,7 @@ 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)
|
||||||
{
|
{
|
||||||
while(aptMainLoop())
|
while(aptMainLoop())
|
||||||
{
|
{
|
||||||
@@ -368,7 +368,7 @@ void draw_loading_bar(u32 current, u32 max, InstallType type)
|
|||||||
draw_base_interface();
|
draw_base_interface();
|
||||||
draw_install_handler(type);
|
draw_install_handler(type);
|
||||||
set_screen(bottom);
|
set_screen(bottom);
|
||||||
double percent = 100*((double)current/(double)max);
|
double percent = 100 * ((double)current / (double)max);
|
||||||
u32 width = (u32)percent;
|
u32 width = (u32)percent;
|
||||||
width *= 2;
|
width *= 2;
|
||||||
C2D_DrawRectSolid(60-1, 110-1, 0.5f, 200+2, 20+2, colors[COLOR_CURSOR]);
|
C2D_DrawRectSolid(60-1, 110-1, 0.5f, 200+2, 20+2, colors[COLOR_CURSOR]);
|
||||||
@@ -442,13 +442,13 @@ void draw_text_wrap(float x, float y, float z, float scaleX, float scaleY, Color
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if((consumed = decode_utf8(&codepoint, (unsigned char*)text)) == -1)
|
if((consumed = decode_utf8(&codepoint, (unsigned char *)text)) == -1)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
float character_width = scaleX * (fontGetCharWidthInfo(NULL, fontGlyphIndexFromCodePoint(NULL, codepoint))->charWidth);
|
float character_width = scaleX * (fontGetCharWidthInfo(NULL, fontGlyphIndexFromCodePoint(NULL, codepoint))->charWidth);
|
||||||
if((current_width += character_width) > max_width)
|
if((current_width += character_width) > max_width)
|
||||||
{
|
{
|
||||||
char* last_space = NULL;
|
char * last_space = NULL;
|
||||||
for(int i = idx; i >= 0; i--)
|
for(int i = idx; i >= 0; i--)
|
||||||
{
|
{
|
||||||
if(result[i] == ' ')
|
if(result[i] == ' ')
|
||||||
@@ -498,27 +498,27 @@ void draw_text_wrap_scaled(float x, float y, float z, Color color, const char *
|
|||||||
static void draw_entry_info(Entry_s * entry)
|
static void draw_entry_info(Entry_s * entry)
|
||||||
{
|
{
|
||||||
char author[0x41] = {0};
|
char author[0x41] = {0};
|
||||||
utf16_to_utf8((u8*)author, entry->author, 0x40);
|
utf16_to_utf8((u8 *)author, entry->author, 0x40);
|
||||||
draw_c2d_text(20, 35, 0.5, 0.5, 0.5, colors[COLOR_WHITE], &text[TEXT_BY_AUTHOR]);
|
draw_c2d_text(20, 35, 0.5, 0.5, 0.5, colors[COLOR_WHITE], &text[TEXT_BY_AUTHOR]);
|
||||||
float width = 0;
|
float width = 0;
|
||||||
C2D_TextGetDimensions(&text[TEXT_BY_AUTHOR], 0.5, 0.5, &width, NULL);
|
C2D_TextGetDimensions(&text[TEXT_BY_AUTHOR], 0.5, 0.5, &width, NULL);
|
||||||
draw_text(20+width, 35, 0.5, 0.5, 0.5, colors[COLOR_WHITE], author);
|
draw_text(20+width, 35, 0.5, 0.5, 0.5, colors[COLOR_WHITE], author);
|
||||||
|
|
||||||
char title[0x41] = {0};
|
char title[0x41] = {0};
|
||||||
utf16_to_utf8((u8*)title, entry->name, 0x40);
|
utf16_to_utf8((u8 *)title, entry->name, 0x40);
|
||||||
draw_text(20, 50, 0.5, 0.7, 0.7, colors[COLOR_WHITE], title);
|
draw_text(20, 50, 0.5, 0.7, 0.7, colors[COLOR_WHITE], title);
|
||||||
|
|
||||||
char description[0x81] = {0};
|
char description[0x81] = {0};
|
||||||
utf16_to_utf8((u8*)description, entry->desc, 0x80);
|
utf16_to_utf8((u8 *)description, entry->desc, 0x80);
|
||||||
draw_text_wrap(20, 70, 0.5, 0.5, 0.5, colors[COLOR_WHITE], description, 363);
|
draw_text_wrap(20, 70, 0.5, 0.5, 0.5, colors[COLOR_WHITE], description, 363);
|
||||||
}
|
}
|
||||||
|
|
||||||
void draw_grid_interface(Entry_List_s* list, Instructions_s instructions)
|
void draw_grid_interface(Entry_List_s * list, Instructions_s instructions)
|
||||||
{
|
{
|
||||||
draw_base_interface();
|
draw_base_interface();
|
||||||
EntryMode current_mode = list->mode;
|
EntryMode current_mode = list->mode;
|
||||||
|
|
||||||
C2D_Text* mode_string[MODE_AMOUNT] = {
|
C2D_Text * mode_string[MODE_AMOUNT] = {
|
||||||
&text[TEXT_THEMEPLAZA_THEME_MODE],
|
&text[TEXT_THEMEPLAZA_THEME_MODE],
|
||||||
&text[TEXT_THEMEPLAZA_SPLASH_MODE],
|
&text[TEXT_THEMEPLAZA_SPLASH_MODE],
|
||||||
};
|
};
|
||||||
@@ -551,7 +551,7 @@ void draw_grid_interface(Entry_List_s* list, Instructions_s instructions)
|
|||||||
current_entry = &list->entries[i];
|
current_entry = &list->entries[i];
|
||||||
|
|
||||||
char name[0x41] = {0};
|
char name[0x41] = {0};
|
||||||
utf16_to_utf8((u8*)name, current_entry->name, 0x40);
|
utf16_to_utf8((u8 *)name, current_entry->name, 0x40);
|
||||||
|
|
||||||
int vertical_offset = 0;
|
int vertical_offset = 0;
|
||||||
int horizontal_offset = i - list->scroll;
|
int horizontal_offset = i - list->scroll;
|
||||||
@@ -563,18 +563,16 @@ void draw_grid_interface(Entry_List_s* list, Instructions_s instructions)
|
|||||||
vertical_offset += 24;
|
vertical_offset += 24;
|
||||||
horizontal_offset += 16;
|
horizontal_offset += 16;
|
||||||
|
|
||||||
// theoretically impossible to have no icon when from the api
|
|
||||||
/*
|
if(current_entry->placeholder_color == 0)
|
||||||
if(!current_entry->placeholder_color)
|
|
||||||
{
|
{
|
||||||
*/
|
const C2D_Image image = get_icon_at(list, i);
|
||||||
C2D_Image * image = list->icons[i];
|
C2D_DrawImageAt(image, horizontal_offset, vertical_offset, 0.5f, NULL, 1.0f, 1.0f);
|
||||||
C2D_DrawImageAt(*image, horizontal_offset, vertical_offset, 0.5f, NULL, 1.0f, 1.0f);
|
|
||||||
/*
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
C2D_DrawRectSolid(horizontal_offset, vertical_offset, 0.5f, list->entry_size, list->entry_size, current_entry->placeholder_color);
|
C2D_DrawRectSolid(horizontal_offset, vertical_offset, 0.5f, list->entry_size, list->entry_size, current_entry->placeholder_color);
|
||||||
*/
|
}
|
||||||
|
|
||||||
if(i == selected_entry)
|
if(i == selected_entry)
|
||||||
{
|
{
|
||||||
@@ -603,12 +601,12 @@ 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)
|
||||||
{
|
{
|
||||||
draw_base_interface();
|
draw_base_interface();
|
||||||
EntryMode current_mode = list->mode;
|
EntryMode current_mode = list->mode;
|
||||||
|
|
||||||
C2D_Text* mode_string[MODE_AMOUNT] = {
|
C2D_Text * mode_string[MODE_AMOUNT] = {
|
||||||
&text[TEXT_THEME_MODE],
|
&text[TEXT_THEME_MODE],
|
||||||
&text[TEXT_SPLASH_MODE],
|
&text[TEXT_SPLASH_MODE],
|
||||||
};
|
};
|
||||||
@@ -617,7 +615,7 @@ void draw_interface(Entry_List_s* list, Instructions_s instructions)
|
|||||||
|
|
||||||
if(list->entries == NULL)
|
if(list->entries == NULL)
|
||||||
{
|
{
|
||||||
C2D_Text* mode_found_string[MODE_AMOUNT] = {
|
C2D_Text * mode_found_string[MODE_AMOUNT] = {
|
||||||
&text[TEXT_NO_THEME_FOUND],
|
&text[TEXT_NO_THEME_FOUND],
|
||||||
&text[TEXT_NO_SPLASH_FOUND],
|
&text[TEXT_NO_SPLASH_FOUND],
|
||||||
};
|
};
|
||||||
@@ -625,7 +623,7 @@ void draw_interface(Entry_List_s* list, Instructions_s instructions)
|
|||||||
draw_c2d_text_center(GFX_TOP, 80, 0.5f, 0.7f, 0.7f, colors[COLOR_YELLOW], mode_found_string[current_mode]);
|
draw_c2d_text_center(GFX_TOP, 80, 0.5f, 0.7f, 0.7f, colors[COLOR_YELLOW], mode_found_string[current_mode]);
|
||||||
draw_c2d_text_center(GFX_TOP, 110, 0.5f, 0.7f, 0.7f, colors[COLOR_YELLOW], &text[TEXT_DOWNLOAD_FROM_QR]);
|
draw_c2d_text_center(GFX_TOP, 110, 0.5f, 0.7f, 0.7f, colors[COLOR_YELLOW], &text[TEXT_DOWNLOAD_FROM_QR]);
|
||||||
|
|
||||||
C2D_Text* mode_switch_string[MODE_AMOUNT] = {
|
C2D_Text * mode_switch_string[MODE_AMOUNT] = {
|
||||||
&text[TEXT_SWITCH_TO_SPLASHES],
|
&text[TEXT_SWITCH_TO_SPLASHES],
|
||||||
&text[TEXT_SWITCH_TO_THEMES],
|
&text[TEXT_SWITCH_TO_THEMES],
|
||||||
};
|
};
|
||||||
@@ -691,7 +689,7 @@ void draw_interface(Entry_List_s* list, Instructions_s instructions)
|
|||||||
current_entry = &list->entries[i];
|
current_entry = &list->entries[i];
|
||||||
|
|
||||||
char name[0x41] = {0};
|
char name[0x41] = {0};
|
||||||
utf16_to_utf8((u8*)name, current_entry->name, 0x40);
|
utf16_to_utf8((u8 *)name, current_entry->name, 0x40);
|
||||||
|
|
||||||
int vertical_offset = i - list->scroll;
|
int vertical_offset = i - list->scroll;
|
||||||
int horizontal_offset = 0;
|
int horizontal_offset = 0;
|
||||||
@@ -729,14 +727,17 @@ void draw_interface(Entry_List_s* list, Instructions_s instructions)
|
|||||||
C2D_DrawSpriteTinted(&sprite_installed, &tint);
|
C2D_DrawSpriteTinted(&sprite_installed, &tint);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!current_entry->placeholder_color)
|
if(current_entry->placeholder_color == 0)
|
||||||
{
|
{
|
||||||
C2D_Image * image = NULL;
|
C2D_Image image;
|
||||||
if(list->entries_count > list->entries_loaded*ICONS_OFFSET_AMOUNT)
|
if(list->entries_count > list->entries_loaded * ICONS_OFFSET_AMOUNT)
|
||||||
image = list->icons[ICONS_VISIBLE*list->entries_loaded + (i - list->scroll)];
|
{
|
||||||
|
const int offset_to_visible_icons = ICONS_VISIBLE * list->entries_loaded;
|
||||||
|
image = get_icon_at(list, offset_to_visible_icons + (i - list->scroll));
|
||||||
|
}
|
||||||
else
|
else
|
||||||
image = list->icons[i];
|
image = get_icon_at(list, i);
|
||||||
C2D_DrawImageAt(*image, horizontal_offset, vertical_offset, 0.5f, NULL, 1.0f, 1.0f);
|
C2D_DrawImageAt(image, horizontal_offset, vertical_offset, 0.5f, NULL, 1.0f, 1.0f);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
231
source/entries_list.c
Normal file
231
source/entries_list.c
Normal file
@@ -0,0 +1,231 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "entries_list.h"
|
||||||
|
#include "loading.h"
|
||||||
|
#include "draw.h"
|
||||||
|
#include "fs.h"
|
||||||
|
#include "unicode.h"
|
||||||
|
|
||||||
|
void delete_entry(Entry_s * entry, bool is_file)
|
||||||
|
{
|
||||||
|
if(is_file)
|
||||||
|
FSUSER_DeleteFile(ArchiveSD, fsMakePath(PATH_UTF16, entry->path));
|
||||||
|
else
|
||||||
|
FSUSER_DeleteDirectoryRecursively(ArchiveSD, fsMakePath(PATH_UTF16, entry->path));
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 load_data(const char * filename, const Entry_s * entry, char ** buf)
|
||||||
|
{
|
||||||
|
if(entry->is_zip)
|
||||||
|
{
|
||||||
|
return zip_file_to_buf(filename + 1, entry->path, buf); //the first character will always be '/' because of the other case
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
u16 path[0x106] = {0};
|
||||||
|
strucat(path, entry->path);
|
||||||
|
struacat(path, filename);
|
||||||
|
|
||||||
|
return file_to_buf(fsMakePath(PATH_UTF16, path), ArchiveSD, buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
C2D_Image get_icon_at(Entry_List_s * list, size_t index)
|
||||||
|
{
|
||||||
|
return (C2D_Image){
|
||||||
|
.tex = &list->icons_texture,
|
||||||
|
.subtex = &list->icons_info[index].subtex,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static int compare_entries_base(const Entry_s * const a, const Entry_s * const b)
|
||||||
|
{
|
||||||
|
// entry->placeholder_color == 0 means it is not filled (no name, author, description)
|
||||||
|
// if a is filled and b is filled, return == 0
|
||||||
|
// if a is not filled and b is not filled, return == 0
|
||||||
|
// if a is not filled, return < 0
|
||||||
|
// if b is an unfilled entry, return > 0
|
||||||
|
return ((int)(a->placeholder_color != 0)) - ((int)(b->placeholder_color != 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef int (*sort_comparator)(const void *, const void *);
|
||||||
|
static int compare_entries_by_name(const void * a, const void * b)
|
||||||
|
{
|
||||||
|
const Entry_s * const entry_a = (const Entry_s *)a;
|
||||||
|
const Entry_s * const entry_b = (const Entry_s *)b;
|
||||||
|
const int base = compare_entries_base(entry_a, entry_b);
|
||||||
|
if(base)
|
||||||
|
return base;
|
||||||
|
|
||||||
|
return memcmp(entry_a->name, entry_b->name, 0x40 * sizeof(u16));
|
||||||
|
}
|
||||||
|
static int compare_entries_by_author(const void * a, const void * b)
|
||||||
|
{
|
||||||
|
const Entry_s * const entry_a = (const Entry_s *)a;
|
||||||
|
const Entry_s * const entry_b = (const Entry_s *)b;
|
||||||
|
const int base = compare_entries_base(entry_a, entry_b);
|
||||||
|
if(base)
|
||||||
|
return base;
|
||||||
|
|
||||||
|
return memcmp(entry_a->author, entry_b->author, 0x40 * sizeof(u16));
|
||||||
|
}
|
||||||
|
static int compare_entries_by_filename(const void * a, const void * b)
|
||||||
|
{
|
||||||
|
const Entry_s * const entry_a = (const Entry_s *)a;
|
||||||
|
const Entry_s * const entry_b = (const Entry_s *)b;
|
||||||
|
const int base = compare_entries_base(entry_a, entry_b);
|
||||||
|
if(base)
|
||||||
|
return base;
|
||||||
|
|
||||||
|
return memcmp(entry_a->path, entry_b->path, 0x106 * sizeof(u16));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sort_list(Entry_List_s * list, sort_comparator compare_entries)
|
||||||
|
{
|
||||||
|
if(list->entries != NULL && list->entries != NULL)
|
||||||
|
qsort(list->entries, list->entries_count, sizeof(Entry_s), compare_entries); //alphabet sort
|
||||||
|
}
|
||||||
|
|
||||||
|
void sort_by_name(Entry_List_s * list)
|
||||||
|
{
|
||||||
|
sort_list(list, compare_entries_by_name);
|
||||||
|
list->current_sort = SORT_NAME;
|
||||||
|
}
|
||||||
|
void sort_by_author(Entry_List_s * list)
|
||||||
|
{
|
||||||
|
sort_list(list, compare_entries_by_author);
|
||||||
|
list->current_sort = SORT_AUTHOR;
|
||||||
|
}
|
||||||
|
void sort_by_filename(Entry_List_s * list)
|
||||||
|
{
|
||||||
|
sort_list(list, compare_entries_by_filename);
|
||||||
|
list->current_sort = SORT_PATH;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define LOADING_DIR_ENTRIES_COUNT 16
|
||||||
|
static FS_DirectoryEntry loading_dir_entries[LOADING_DIR_ENTRIES_COUNT];
|
||||||
|
Result load_entries(const char * loading_path, Entry_List_s * list, const InstallType loading_screen)
|
||||||
|
{
|
||||||
|
Handle dir_handle;
|
||||||
|
Result res = FSUSER_OpenDirectory(&dir_handle, ArchiveSD, fsMakePath(PATH_ASCII, loading_path));
|
||||||
|
if(R_FAILED(res))
|
||||||
|
{
|
||||||
|
DEBUG("Failed to open folder: %s\n", loading_path);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
list_init_capacity(list, LOADING_DIR_ENTRIES_COUNT);
|
||||||
|
|
||||||
|
u32 entries_read = LOADING_DIR_ENTRIES_COUNT;
|
||||||
|
while(entries_read == LOADING_DIR_ENTRIES_COUNT)
|
||||||
|
{
|
||||||
|
res = FSDIR_Read(dir_handle, &entries_read, LOADING_DIR_ENTRIES_COUNT, loading_dir_entries);
|
||||||
|
if(R_FAILED(res))
|
||||||
|
break;
|
||||||
|
|
||||||
|
for(u32 i = 0; i < entries_read; ++i)
|
||||||
|
{
|
||||||
|
const FS_DirectoryEntry * const dir_entry = &loading_dir_entries[i];
|
||||||
|
const bool is_zip = !strcmp(dir_entry->shortExt, "ZIP");
|
||||||
|
if(!(dir_entry->attributes & FS_ATTRIBUTE_DIRECTORY) && !is_zip)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const ssize_t new_entry_index = list_add_entry(list);
|
||||||
|
if(new_entry_index < 0)
|
||||||
|
{
|
||||||
|
// out of memory: still allow use of currently loaded entries.
|
||||||
|
// Many things might die, depending on the heap layout after
|
||||||
|
entries_read = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entry_s * const current_entry = &list->entries[new_entry_index];
|
||||||
|
memset(current_entry, 0, sizeof(Entry_s));
|
||||||
|
struacat(current_entry->path, loading_path);
|
||||||
|
strucat(current_entry->path, dir_entry->name);
|
||||||
|
current_entry->is_zip = is_zip;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FSDIR_Close(dir_handle);
|
||||||
|
|
||||||
|
list->loading_path = loading_path;
|
||||||
|
const int loading_bar_ticks = list->entries_count / 10;
|
||||||
|
|
||||||
|
for(int i = 0, j = 0; i < list->entries_count; ++i)
|
||||||
|
{
|
||||||
|
// replaces (i % loading_bar_ticks) == 0
|
||||||
|
if(++j >= loading_bar_ticks)
|
||||||
|
{
|
||||||
|
j = 0;
|
||||||
|
draw_loading_bar(i, list->entries_count, loading_screen);
|
||||||
|
}
|
||||||
|
Entry_s * const current_entry = &list->entries[i];
|
||||||
|
char * buf = NULL;
|
||||||
|
u32 buflen = load_data("/info.smdh", current_entry, &buf);
|
||||||
|
parse_smdh(buflen == sizeof(Icon_s) ? (Icon_s *)buf : NULL, current_entry, current_entry->path + strlen(loading_path));
|
||||||
|
free(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void list_init_capacity(Entry_List_s * list, const int init_capacity)
|
||||||
|
{
|
||||||
|
list->entries = malloc(init_capacity * sizeof(Entry_s));
|
||||||
|
list->entries_capacity = init_capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define LIST_CAPACITY_THRESHOLD 512
|
||||||
|
ssize_t list_add_entry(Entry_List_s * list)
|
||||||
|
{
|
||||||
|
if(list->entries_count == list->entries_capacity)
|
||||||
|
{
|
||||||
|
int next_capacity = list->entries_capacity;
|
||||||
|
// expand by doubling until we hit LIST_CAPACITY_THRESHOLD
|
||||||
|
// then simply increment by that, to have less extra space leftover
|
||||||
|
if(next_capacity < LIST_CAPACITY_THRESHOLD)
|
||||||
|
{
|
||||||
|
next_capacity *= 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
next_capacity += LIST_CAPACITY_THRESHOLD;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entry_s * const new_list = realloc(list->entries, next_capacity * sizeof(Entry_s));
|
||||||
|
if(new_list == NULL)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
list->entries = new_list;
|
||||||
|
list->entries_capacity = next_capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
return list->entries_count++;
|
||||||
|
}
|
||||||
32
source/fs.c
32
source/fs.c
@@ -122,7 +122,7 @@ Result load_parental_controls(Parental_Restrictions_s *restrictions)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 file_to_buf(FS_Path path, FS_Archive archive, char** buf)
|
u32 file_to_buf(FS_Path path, FS_Archive archive, char ** buf)
|
||||||
{
|
{
|
||||||
Handle file;
|
Handle file;
|
||||||
Result res = 0;
|
Result res = 0;
|
||||||
@@ -139,9 +139,9 @@ u32 file_to_buf(FS_Path path, FS_Archive archive, char** buf)
|
|||||||
return (u32)size;
|
return (u32)size;
|
||||||
}
|
}
|
||||||
|
|
||||||
static u32 zip_to_buf(struct archive *a, char *file_name, char ** buf)
|
static u32 zip_to_buf(struct archive * a, const char * file_name, char ** buf)
|
||||||
{
|
{
|
||||||
struct archive_entry *entry;
|
struct archive_entry * entry;
|
||||||
|
|
||||||
bool found = false;
|
bool found = false;
|
||||||
u64 file_size = 0;
|
u64 file_size = 0;
|
||||||
@@ -167,9 +167,9 @@ static u32 zip_to_buf(struct archive *a, char *file_name, char ** buf)
|
|||||||
return (u32)file_size;
|
return (u32)file_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 zip_memory_to_buf(char *file_name, void * zip_memory, size_t zip_size, char ** buf)
|
u32 zip_memory_to_buf(const char * file_name, void * zip_memory, size_t zip_size, char ** buf)
|
||||||
{
|
{
|
||||||
struct archive *a = archive_read_new();
|
struct archive * a = archive_read_new();
|
||||||
archive_read_support_format_zip(a);
|
archive_read_support_format_zip(a);
|
||||||
|
|
||||||
int r = archive_read_open_memory(a, zip_memory, zip_size);
|
int r = archive_read_open_memory(a, zip_memory, zip_size);
|
||||||
@@ -182,13 +182,13 @@ u32 zip_memory_to_buf(char *file_name, void * zip_memory, size_t zip_size, char
|
|||||||
return zip_to_buf(a, file_name, buf);
|
return zip_to_buf(a, file_name, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 zip_file_to_buf(char *file_name, u16 *zip_path, char **buf)
|
u32 zip_file_to_buf(const char * file_name, const u16 * zip_path, char ** buf)
|
||||||
{
|
{
|
||||||
ssize_t len = strulen(zip_path, 0x106);
|
ssize_t len = strulen(zip_path, 0x106);
|
||||||
char *path = calloc(sizeof(char), len*sizeof(u16));
|
char * path = calloc(sizeof(char), len * sizeof(u16));
|
||||||
utf16_to_utf8((u8*)path, zip_path, len*sizeof(u16));
|
utf16_to_utf8((u8 *)path, zip_path, len * sizeof(u16));
|
||||||
|
|
||||||
struct archive *a = archive_read_new();
|
struct archive * a = archive_read_new();
|
||||||
archive_read_support_format_zip(a);
|
archive_read_support_format_zip(a);
|
||||||
|
|
||||||
int r = archive_read_open_filename(a, path, 0x4000);
|
int r = archive_read_open_filename(a, path, 0x4000);
|
||||||
@@ -202,7 +202,7 @@ u32 zip_file_to_buf(char *file_name, u16 *zip_path, char **buf)
|
|||||||
return zip_to_buf(a, file_name, buf);
|
return zip_to_buf(a, file_name, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
Handle handle;
|
Handle handle;
|
||||||
Result res = 0;
|
Result res = 0;
|
||||||
@@ -212,7 +212,7 @@ Result buf_to_file(u32 size, FS_Path path, FS_Archive archive, char *buf)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 decompress_lz_file(FS_Path file_name, FS_Archive archive, char **buf)
|
u32 decompress_lz_file(FS_Path file_name, FS_Archive archive, char ** buf)
|
||||||
{
|
{
|
||||||
Handle handle;
|
Handle handle;
|
||||||
Result res = 0;
|
Result res = 0;
|
||||||
@@ -223,7 +223,7 @@ u32 decompress_lz_file(FS_Path file_name, FS_Archive archive, char **buf)
|
|||||||
u64 size;
|
u64 size;
|
||||||
FSFILE_GetSize(handle, &size);
|
FSFILE_GetSize(handle, &size);
|
||||||
|
|
||||||
char *temp_buf = NULL;
|
char * temp_buf = NULL;
|
||||||
|
|
||||||
if(size != 0)
|
if(size != 0)
|
||||||
{
|
{
|
||||||
@@ -317,9 +317,9 @@ u32 decompress_lz_file(FS_Path file_name, FS_Archive archive, char **buf)
|
|||||||
// if i figure out a dynamic programming algorithm which ends up being significantly
|
// if i figure out a dynamic programming algorithm which ends up being significantly
|
||||||
// faster. Otherwise, I think this is probably a fine implementation.
|
// faster. Otherwise, I think this is probably a fine implementation.
|
||||||
|
|
||||||
u32 compress_lz_file_fast(FS_Path path, FS_Archive archive, char *in_buf, u32 size)
|
u32 compress_lz_file_fast(FS_Path path, FS_Archive archive, char * in_buf, u32 size)
|
||||||
{
|
{
|
||||||
char *output_buf = calloc(1, size * 2);
|
char * output_buf = calloc(1, size * 2);
|
||||||
u32 output_size = 0;
|
u32 output_size = 0;
|
||||||
u32 mask_pos = 0;
|
u32 mask_pos = 0;
|
||||||
u32 bytes_processed = 0;
|
u32 bytes_processed = 0;
|
||||||
@@ -363,12 +363,12 @@ void remake_file(FS_Path path, FS_Archive archive, u32 size)
|
|||||||
FSUSER_DeleteFile(archive, path);
|
FSUSER_DeleteFile(archive, path);
|
||||||
}
|
}
|
||||||
FSUSER_CreateFile(archive, path, 0, size);
|
FSUSER_CreateFile(archive, path, 0, size);
|
||||||
char *buf = calloc(size, 1);
|
char * buf = calloc(size, 1);
|
||||||
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)
|
static SwkbdCallbackResult fat32filter(void * user, const char ** ppMessage, const char * text, size_t textlen)
|
||||||
{
|
{
|
||||||
(void)textlen;
|
(void)textlen;
|
||||||
(void)user;
|
(void)user;
|
||||||
|
|||||||
304
source/loading.c
304
source/loading.c
@@ -33,194 +33,48 @@
|
|||||||
|
|
||||||
#include <png.h>
|
#include <png.h>
|
||||||
|
|
||||||
void delete_entry(Entry_s * entry, bool is_file)
|
void copy_texture_data(C3D_Tex * texture, const u16 * src, const Entry_Icon_s * current_icon)
|
||||||
{
|
{
|
||||||
if(is_file)
|
// pointer to rgb565, offset by the number of rows and columns specified by current_icon
|
||||||
FSUSER_DeleteFile(ArchiveSD, fsMakePath(PATH_UTF16, entry->path));
|
// (reminder that this is z order curve storage)
|
||||||
else
|
u16 * dest = ((u16 *)texture->data) + (current_icon->y * texture->width) + (current_icon->x * 8);
|
||||||
FSUSER_DeleteDirectoryRecursively(ArchiveSD, fsMakePath(PATH_UTF16, entry->path));
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 load_data(char * filename, Entry_s entry, char ** buf)
|
|
||||||
{
|
|
||||||
if(entry.is_zip)
|
|
||||||
{
|
|
||||||
return zip_file_to_buf(filename+1, entry.path, buf); //the first character will always be '/' because of the other case
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
u16 path[0x106] = {0};
|
|
||||||
strucat(path, entry.path);
|
|
||||||
struacat(path, filename);
|
|
||||||
|
|
||||||
return file_to_buf(fsMakePath(PATH_UTF16, path), ArchiveSD, buf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Function taken and adapted from https://github.com/BernardoGiordano/Checkpoint/blob/master/3ds/source/title.cpp
|
|
||||||
C2D_Image * loadTextureIcon(Icon_s *icon)
|
|
||||||
{
|
|
||||||
if(icon == NULL)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
C2D_Image * image = calloc(1, sizeof(C2D_Image));
|
|
||||||
C3D_Tex* tex = malloc(sizeof(C3D_Tex));
|
|
||||||
static const Tex3DS_SubTexture subt3x = { 48, 48, 0.0f, 48/64.0f, 48/64.0f, 0.0f };
|
|
||||||
image->tex = tex;
|
|
||||||
image->subtex = &subt3x;
|
|
||||||
C3D_TexInit(image->tex, 64, 64, GPU_RGB565);
|
|
||||||
|
|
||||||
u16* dest = (u16*)image->tex->data + (64-48)*64;
|
|
||||||
u16* src = icon->big_icon;
|
|
||||||
for (int j = 0; j < 48; j += 8)
|
for (int j = 0; j < 48; j += 8)
|
||||||
{
|
{
|
||||||
memcpy(dest, src, 48*8*sizeof(u16));
|
memcpy(dest, src, 48 * 8 * sizeof(u16));
|
||||||
src += 48*8;
|
src += 48 * 8;
|
||||||
dest += 64*8;
|
dest += texture->width * 8;
|
||||||
}
|
}
|
||||||
|
GSPGPU_InvalidateDataCache(texture->data, texture->size);
|
||||||
return image;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
|
|
||||||
if(icon == NULL)
|
if(icon == NULL)
|
||||||
{
|
{
|
||||||
memcpy(entry->name, fallback_name, 0x80);
|
memcpy(entry->name, fallback_name, 0x80);
|
||||||
utf8_to_utf16(entry->desc, (u8*)"No description", 0x100);
|
utf8_to_utf16(entry->desc, (u8 *)"No description", 0x100);
|
||||||
utf8_to_utf16(entry->author, (u8*)"Unknown author", 0x80);
|
utf8_to_utf16(entry->author, (u8 *)"Unknown author", 0x80);
|
||||||
entry->placeholder_color = C2D_Color32(rand() % 255, rand() % 255, rand() % 255, 255);
|
entry->placeholder_color = C2D_Color32(rand() % 255, rand() % 255, rand() % 255, 255);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
memcpy(entry->name, icon->name, 0x40 * sizeof(u16));
|
||||||
memcpy(entry->name, icon->name, 0x40*sizeof(u16));
|
memcpy(entry->desc, icon->desc, 0x80 * sizeof(u16));
|
||||||
memcpy(entry->desc, icon->desc, 0x80*sizeof(u16));
|
memcpy(entry->author, icon->author, 0x40 * sizeof(u16));
|
||||||
memcpy(entry->author, icon->author, 0x40*sizeof(u16));
|
entry->placeholder_color = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static C2D_Image * load_entry_icon(Entry_s entry)
|
static Icon_s * load_entry_icon(const Entry_s * entry)
|
||||||
{
|
{
|
||||||
char *info_buffer = NULL;
|
char * info_buffer = NULL;
|
||||||
u64 size = load_data("/info.smdh", entry, &info_buffer);
|
u32 size = load_data("/info.smdh", entry, &info_buffer);
|
||||||
if(!size) return NULL;
|
if(size != sizeof(Icon_s))
|
||||||
|
{
|
||||||
Icon_s * smdh = (Icon_s *)info_buffer;
|
|
||||||
C2D_Image* out = loadTextureIcon(smdh);
|
|
||||||
free(info_buffer);
|
free(info_buffer);
|
||||||
return out;
|
return NULL;
|
||||||
}
|
|
||||||
|
|
||||||
typedef int (*sort_comparator)(const void *, const void *);
|
|
||||||
static int compare_entries_by_name(const void * a, const void * b)
|
|
||||||
{
|
|
||||||
Entry_s *entry_a = (Entry_s *)a;
|
|
||||||
Entry_s *entry_b = (Entry_s *)b;
|
|
||||||
|
|
||||||
return memcmp(entry_a->name, entry_b->name, 0x40*sizeof(u16));
|
|
||||||
}
|
|
||||||
static int compare_entries_by_author(const void * a, const void * b)
|
|
||||||
{
|
|
||||||
Entry_s *entry_a = (Entry_s *)a;
|
|
||||||
Entry_s *entry_b = (Entry_s *)b;
|
|
||||||
|
|
||||||
return memcmp(entry_a->author, entry_b->author, 0x40*sizeof(u16));
|
|
||||||
}
|
|
||||||
static int compare_entries_by_filename(const void * a, const void * b)
|
|
||||||
{
|
|
||||||
Entry_s *entry_a = (Entry_s *)a;
|
|
||||||
Entry_s *entry_b = (Entry_s *)b;
|
|
||||||
|
|
||||||
return memcmp(entry_a->path, entry_b->path, 0x106*sizeof(u16));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sort_list(Entry_List_s * list, sort_comparator compare_entries)
|
|
||||||
{
|
|
||||||
if(list->entries != NULL && list->entries != NULL)
|
|
||||||
qsort(list->entries, list->entries_count, sizeof(Entry_s), compare_entries); //alphabet sort
|
|
||||||
}
|
|
||||||
|
|
||||||
void sort_by_name(Entry_List_s * list)
|
|
||||||
{
|
|
||||||
sort_list(list, compare_entries_by_name);
|
|
||||||
list->current_sort = SORT_NAME;
|
|
||||||
}
|
|
||||||
void sort_by_author(Entry_List_s * list)
|
|
||||||
{
|
|
||||||
sort_list(list, compare_entries_by_author);
|
|
||||||
list->current_sort = SORT_AUTHOR;
|
|
||||||
}
|
|
||||||
void sort_by_filename(Entry_List_s * list)
|
|
||||||
{
|
|
||||||
sort_list(list, compare_entries_by_filename);
|
|
||||||
list->current_sort = SORT_PATH;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result load_entries(const char * loading_path, Entry_List_s * list)
|
|
||||||
{
|
|
||||||
Handle dir_handle;
|
|
||||||
Result res = FSUSER_OpenDirectory(&dir_handle, ArchiveSD, fsMakePath(PATH_ASCII, loading_path));
|
|
||||||
if(R_FAILED(res))
|
|
||||||
{
|
|
||||||
DEBUG("Failed to open folder: %s\n", loading_path);
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 entries_read = 1;
|
return (Icon_s *)info_buffer;
|
||||||
|
|
||||||
while(entries_read)
|
|
||||||
{
|
|
||||||
FS_DirectoryEntry dir_entry = {0};
|
|
||||||
res = FSDIR_Read(dir_handle, &entries_read, 1, &dir_entry);
|
|
||||||
if(R_FAILED(res) || entries_read == 0)
|
|
||||||
break;
|
|
||||||
|
|
||||||
if(!(dir_entry.attributes & FS_ATTRIBUTE_DIRECTORY) && strcmp(dir_entry.shortExt, "ZIP"))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
u16 path[0x106] = {0};
|
|
||||||
struacat(path, loading_path);
|
|
||||||
strucat(path, dir_entry.name);
|
|
||||||
char * buf = NULL;
|
|
||||||
|
|
||||||
if (!strcmp(dir_entry.shortExt, "ZIP"))
|
|
||||||
{
|
|
||||||
zip_file_to_buf("info.smdh", path, &buf);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
const ssize_t len = strulen(path, 0x106);
|
|
||||||
struacat(path, "/info.smdh");
|
|
||||||
file_to_buf(fsMakePath(PATH_UTF16, path), ArchiveSD, &buf);
|
|
||||||
memset(&path[len], 0, (0x106 - len) * sizeof(u16));
|
|
||||||
}
|
|
||||||
|
|
||||||
list->entries_count++;
|
|
||||||
Entry_s * new_list = realloc(list->entries, list->entries_count * sizeof(Entry_s));
|
|
||||||
if(new_list == NULL)
|
|
||||||
{
|
|
||||||
// out of memory: still allow use of currently loaded entries.
|
|
||||||
// Many things might die, depending on the heap layout after
|
|
||||||
list->entries_count--;
|
|
||||||
free(buf);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
list->entries = new_list;
|
|
||||||
|
|
||||||
Entry_s * current_entry = &(list->entries[list->entries_count-1]);
|
|
||||||
memset(current_entry, 0, sizeof(Entry_s));
|
|
||||||
parse_smdh((Icon_s *)buf, current_entry, dir_entry.name);
|
|
||||||
free(buf);
|
|
||||||
|
|
||||||
memcpy(current_entry->path, path, 0x106 * sizeof(u16));
|
|
||||||
current_entry->is_zip = !strcmp(dir_entry.shortExt, "ZIP");
|
|
||||||
}
|
|
||||||
|
|
||||||
FSDIR_Close(dir_handle);
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void load_icons_first(Entry_List_s * list, bool silent)
|
void load_icons_first(Entry_List_s * list, bool silent)
|
||||||
@@ -232,7 +86,7 @@ void load_icons_first(Entry_List_s * list, bool silent)
|
|||||||
|
|
||||||
int starti = 0, endi = 0;
|
int starti = 0, endi = 0;
|
||||||
|
|
||||||
if(list->entries_count <= list->entries_loaded*ICONS_OFFSET_AMOUNT)
|
if(list->entries_count <= list->entries_loaded * ICONS_OFFSET_AMOUNT)
|
||||||
{
|
{
|
||||||
DEBUG("small load\n");
|
DEBUG("small load\n");
|
||||||
// if the list is one that doesnt need swapping, load everything at once
|
// if the list is one that doesnt need swapping, load everything at once
|
||||||
@@ -242,40 +96,44 @@ void load_icons_first(Entry_List_s * list, bool silent)
|
|||||||
{
|
{
|
||||||
DEBUG("extended load\n");
|
DEBUG("extended load\n");
|
||||||
// otherwise, load around to prepare for swapping
|
// otherwise, load around to prepare for swapping
|
||||||
starti = list->scroll - list->entries_loaded*ICONS_VISIBLE;
|
starti = list->scroll - list->entries_loaded * ICONS_VISIBLE;
|
||||||
endi = starti + list->entries_loaded*ICONS_OFFSET_AMOUNT;
|
endi = starti + list->entries_loaded * ICONS_OFFSET_AMOUNT;
|
||||||
}
|
}
|
||||||
|
|
||||||
list->icons = calloc(endi-starti, sizeof(C2D_Image*));
|
for(int entry_i = starti, icon_i = 0; entry_i < endi; ++entry_i, ++icon_i)
|
||||||
|
|
||||||
C2D_Image ** icons = list->icons;
|
|
||||||
|
|
||||||
for(int i = starti; i < endi; i++)
|
|
||||||
{
|
{
|
||||||
if(!silent)
|
if(!silent)
|
||||||
draw_loading_bar(i - starti, endi-starti, INSTALL_LOADING_ICONS);
|
draw_loading_bar(icon_i, endi-starti, INSTALL_LOADING_ICONS);
|
||||||
|
|
||||||
int offset = i;
|
int offset = entry_i;
|
||||||
if(offset < 0)
|
if(offset < 0)
|
||||||
offset += list->entries_count;
|
offset += list->entries_count;
|
||||||
if(offset >= list->entries_count)
|
if(offset >= list->entries_count)
|
||||||
offset -= list->entries_count;
|
offset -= list->entries_count;
|
||||||
|
|
||||||
Entry_s current_entry = list->entries[offset];
|
Entry_s * const current_entry = &list->entries[offset];
|
||||||
icons[i-starti] = load_entry_icon(current_entry);
|
Icon_s * const smdh = load_entry_icon(current_entry);
|
||||||
|
if(smdh != NULL)
|
||||||
|
{
|
||||||
|
if(current_entry->placeholder_color == 0)
|
||||||
|
parse_smdh(smdh, current_entry, current_entry->path + strlen(list->loading_path));
|
||||||
|
copy_texture_data(&list->icons_texture, smdh->big_icon, &list->icons_info[icon_i]);
|
||||||
|
free(smdh);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void reverse(C2D_Image * a[], int sz) {
|
static void reverse(Entry_Icon_s a[], int sz) {
|
||||||
int i, j;
|
int i, j;
|
||||||
|
Entry_Icon_s tmp;
|
||||||
for (i = 0, j = sz; i < j; i++, j--) {
|
for (i = 0, j = sz; i < j; i++, j--) {
|
||||||
C2D_Image * tmp = a[i];
|
memcpy(&tmp, &a[i], sizeof(Entry_Icon_s));
|
||||||
a[i] = a[j];
|
memcpy(&a[i], &a[j], sizeof(Entry_Icon_s));
|
||||||
a[j] = tmp;
|
memcpy(&a[j], &tmp, sizeof(Entry_Icon_s));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rotate(C2D_Image * array[], int size, int amt) {
|
static void rotate(Entry_Icon_s array[], int size, int amt) {
|
||||||
if (amt < 0)
|
if (amt < 0)
|
||||||
amt = size + amt;
|
amt = size + amt;
|
||||||
reverse(array, size-amt-1);
|
reverse(array, size-amt-1);
|
||||||
@@ -293,11 +151,11 @@ void handle_scrolling(Entry_List_s * list)
|
|||||||
{
|
{
|
||||||
int change = 0;
|
int change = 0;
|
||||||
|
|
||||||
if(list->entries_count > list->entries_loaded*2 && list->previous_scroll < list->entries_loaded && list->selected_entry >= list->entries_count - list->entries_loaded)
|
if(list->entries_count > list->entries_loaded * 2 && list->previous_scroll < list->entries_loaded && list->selected_entry >= list->entries_count - list->entries_loaded)
|
||||||
{
|
{
|
||||||
list->scroll = list->entries_count - list->entries_loaded;
|
list->scroll = list->entries_count - list->entries_loaded;
|
||||||
}
|
}
|
||||||
else if(list->entries_count > list->entries_loaded*2 && list->selected_entry < list->entries_loaded && list->previous_selected >= list->entries_count - list->entries_loaded)
|
else if(list->entries_count > list->entries_loaded * 2 && list->selected_entry < list->entries_loaded && list->previous_selected >= list->entries_count - list->entries_loaded)
|
||||||
{
|
{
|
||||||
list->scroll = 0;
|
list->scroll = 0;
|
||||||
}
|
}
|
||||||
@@ -341,13 +199,13 @@ static bool load_icons(Entry_List_s * current_list, Handle mutex)
|
|||||||
|
|
||||||
handle_scrolling(current_list);
|
handle_scrolling(current_list);
|
||||||
|
|
||||||
if(current_list->entries_count <= current_list->entries_loaded*ICONS_OFFSET_AMOUNT || current_list->previous_scroll == current_list->scroll)
|
if(current_list->entries_count <= current_list->entries_loaded * ICONS_OFFSET_AMOUNT || current_list->previous_scroll == current_list->scroll)
|
||||||
return false; // return if the list is one that doesnt need swapping, or if nothing changed
|
return false; // return if the list is one that doesnt need swapping, or if nothing changed
|
||||||
|
|
||||||
#define SIGN(x) (x > 0 ? 1 : ((x < 0) ? -1 : 0))
|
#define SIGN(x) (x > 0 ? 1 : ((x < 0) ? -1 : 0))
|
||||||
|
|
||||||
int delta = current_list->scroll - current_list->previous_scroll;
|
int delta = current_list->scroll - current_list->previous_scroll;
|
||||||
if(abs(delta) >= current_list->entries_count - current_list->entries_loaded*(ICONS_OFFSET_AMOUNT-1))
|
if(abs(delta) >= current_list->entries_count - current_list->entries_loaded * (ICONS_OFFSET_AMOUNT-1))
|
||||||
delta = -SIGN(delta) * (current_list->entries_count - abs(delta));
|
delta = -SIGN(delta) * (current_list->entries_count - abs(delta));
|
||||||
|
|
||||||
int starti = current_list->scroll;
|
int starti = current_list->scroll;
|
||||||
@@ -360,28 +218,28 @@ static bool load_icons(Entry_List_s * current_list, Handle mutex)
|
|||||||
}
|
}
|
||||||
|
|
||||||
int ctr = 0;
|
int ctr = 0;
|
||||||
Entry_s ** entries = calloc(abs(delta), sizeof(Entry_s *));
|
Entry_s ** const entries = calloc(abs(delta), sizeof(Entry_s *));
|
||||||
int * indexes = calloc(abs(delta), sizeof(int));
|
int * const indexes = calloc(abs(delta), sizeof(int));
|
||||||
bool released = false;
|
bool released = false;
|
||||||
|
|
||||||
C2D_Image ** icons = current_list->icons;
|
Entry_Icon_s * const icons = current_list->icons_info;
|
||||||
|
|
||||||
for(int i = starti; i != endi; i++, ctr++)
|
for(int i = starti; i != endi; i++, ctr++)
|
||||||
{
|
{
|
||||||
int index = 0;
|
int index = 0;
|
||||||
int offset = i;
|
int offset = i;
|
||||||
|
|
||||||
rotate(icons, ICONS_OFFSET_AMOUNT*current_list->entries_loaded, -1*SIGN(delta));
|
rotate(icons, ICONS_OFFSET_AMOUNT * current_list->entries_loaded, -SIGN(delta));
|
||||||
|
|
||||||
if(delta > 0)
|
if(delta > 0)
|
||||||
{
|
{
|
||||||
index = current_list->entries_loaded*ICONS_OFFSET_AMOUNT - delta + i - starti;
|
index = current_list->entries_loaded * ICONS_OFFSET_AMOUNT - delta + i - starti;
|
||||||
offset += current_list->entries_loaded*ICONS_UNDER - delta;
|
offset += current_list->entries_loaded * ICONS_UNDER - delta;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
index = 0 - delta - 1 + i - starti;
|
index = 0 - delta - 1 + i - starti;
|
||||||
offset -= current_list->entries_loaded*ICONS_VISIBLE;
|
offset -= current_list->entries_loaded * ICONS_VISIBLE;
|
||||||
i -= 2; //i-- twice to counter the i++, needed only for this case
|
i -= 2; //i-- twice to counter the i++, needed only for this case
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -396,7 +254,7 @@ static bool load_icons(Entry_List_s * current_list, Handle mutex)
|
|||||||
|
|
||||||
#undef SIGN
|
#undef SIGN
|
||||||
|
|
||||||
if(abs(delta) < 4)
|
if(abs(delta) <= current_list->entries_loaded)
|
||||||
{
|
{
|
||||||
svcReleaseMutex(mutex);
|
svcReleaseMutex(mutex);
|
||||||
released = true;
|
released = true;
|
||||||
@@ -407,19 +265,19 @@ static bool load_icons(Entry_List_s * current_list, Handle mutex)
|
|||||||
endi = abs(delta);
|
endi = abs(delta);
|
||||||
for(int i = starti; i < endi; i++)
|
for(int i = starti; i < endi; i++)
|
||||||
{
|
{
|
||||||
Entry_s * current_entry = entries[i];
|
Entry_s * const current_entry = entries[i];
|
||||||
int index = indexes[i];
|
const int index = indexes[i];
|
||||||
|
const Entry_Icon_s * const current_icon = &icons[index];
|
||||||
|
|
||||||
C2D_Image * image = icons[index];
|
Icon_s * const smdh = load_entry_icon(current_entry);
|
||||||
if (icons[index] != NULL)
|
if(smdh != NULL)
|
||||||
{
|
{
|
||||||
C3D_TexDelete(image->tex);
|
if(current_entry->placeholder_color == 0)
|
||||||
free(image->tex);
|
parse_smdh(smdh, current_entry, current_entry->path + strlen(current_list->loading_path));
|
||||||
free(image);
|
copy_texture_data(¤t_list->icons_texture, smdh->big_icon, current_icon);
|
||||||
|
free(smdh);
|
||||||
}
|
}
|
||||||
|
|
||||||
icons[index] = load_entry_icon(*current_entry);
|
|
||||||
|
|
||||||
if(!released && i > endi/2)
|
if(!released && i > endi/2)
|
||||||
{
|
{
|
||||||
svcReleaseMutex(mutex);
|
svcReleaseMutex(mutex);
|
||||||
@@ -439,15 +297,13 @@ void load_icons_thread(void * void_arg)
|
|||||||
{
|
{
|
||||||
Thread_Arg_s * arg = (Thread_Arg_s *)void_arg;
|
Thread_Arg_s * arg = (Thread_Arg_s *)void_arg;
|
||||||
Handle mutex = *(Handle *)arg->thread_arg[1];
|
Handle mutex = *(Handle *)arg->thread_arg[1];
|
||||||
do
|
do {
|
||||||
{
|
|
||||||
svcWaitSynchronization(mutex, U64_MAX);
|
svcWaitSynchronization(mutex, U64_MAX);
|
||||||
volatile Entry_List_s * current_list = *(volatile Entry_List_s **)arg->thread_arg[0];
|
Entry_List_s * const current_list = *(Entry_List_s ** volatile)arg->thread_arg[0];
|
||||||
bool released = load_icons((Entry_List_s *)current_list, mutex);
|
const bool released = load_icons(current_list, mutex);
|
||||||
if(!released)
|
if(!released)
|
||||||
svcReleaseMutex(mutex);
|
svcReleaseMutex(mutex);
|
||||||
}
|
} 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)
|
||||||
@@ -457,7 +313,7 @@ bool load_preview_from_buffer(char * row_pointers, u32 size, C2D_Image * preview
|
|||||||
|
|
||||||
free_preview(*preview_image);
|
free_preview(*preview_image);
|
||||||
|
|
||||||
C3D_Tex* tex = malloc(sizeof(C3D_Tex));
|
C3D_Tex * tex = malloc(sizeof(C3D_Tex));
|
||||||
preview_image->tex = tex;
|
preview_image->tex = tex;
|
||||||
|
|
||||||
Tex3DS_SubTexture * subt3x = malloc(sizeof(Tex3DS_SubTexture));
|
Tex3DS_SubTexture * subt3x = malloc(sizeof(Tex3DS_SubTexture));
|
||||||
@@ -489,16 +345,16 @@ bool load_preview_from_buffer(char * row_pointers, u32 size, C2D_Image * preview
|
|||||||
}
|
}
|
||||||
|
|
||||||
static u16 previous_path_preview[0x106] = {0};
|
static u16 previous_path_preview[0x106] = {0};
|
||||||
bool load_preview(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)
|
||||||
{
|
{
|
||||||
if(list.entries == NULL) return false;
|
if(list->entries == NULL) return false;
|
||||||
|
|
||||||
Entry_s entry = list.entries[list.selected_entry];
|
const Entry_s * entry = &list->entries[list->selected_entry];
|
||||||
|
|
||||||
if(!memcmp(&previous_path_preview, &entry.path, 0x106*sizeof(u16))) return true;
|
if(!memcmp(&previous_path_preview, &entry->path, 0x106 * sizeof(u16))) return true;
|
||||||
|
|
||||||
char *preview_buffer = NULL;
|
char * preview_buffer = NULL;
|
||||||
u64 size = load_data("/preview.png", entry, &preview_buffer);
|
u32 size = load_data("/preview.png", entry, &preview_buffer);
|
||||||
|
|
||||||
if(size)
|
if(size)
|
||||||
{
|
{
|
||||||
@@ -567,7 +423,7 @@ bool load_preview(Entry_List_s list, C2D_Image * preview_image, int * preview_of
|
|||||||
if(ret)
|
if(ret)
|
||||||
{
|
{
|
||||||
// mark the new preview as loaded for optimisation
|
// mark the new preview as loaded for optimisation
|
||||||
memcpy(&previous_path_preview, &entry.path, 0x106*sizeof(u16));
|
memcpy(&previous_path_preview, &entry->path, 0x106 * sizeof(u16));
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@@ -578,11 +434,11 @@ void free_preview(C2D_Image preview)
|
|||||||
if(preview.tex)
|
if(preview.tex)
|
||||||
C3D_TexDelete(preview.tex);
|
C3D_TexDelete(preview.tex);
|
||||||
free(preview.tex);
|
free(preview.tex);
|
||||||
free((Tex3DS_SubTexture*)preview.subtex);
|
free((Tex3DS_SubTexture *)preview.subtex);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize the audio struct
|
// Initialize the audio struct
|
||||||
Result load_audio(Entry_s entry, audio_s *audio)
|
Result load_audio(const Entry_s * entry, audio_s * audio)
|
||||||
{
|
{
|
||||||
audio->filesize = load_data("/bgm.ogg", entry, &audio->filebuf);
|
audio->filesize = load_data("/bgm.ogg", entry, &audio->filebuf);
|
||||||
if (audio->filesize == 0) {
|
if (audio->filesize == 0) {
|
||||||
@@ -596,7 +452,7 @@ Result load_audio(Entry_s entry, audio_s *audio)
|
|||||||
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
|
||||||
|
|
||||||
FILE *file = fmemopen(audio->filebuf, audio->filesize, "rb");
|
FILE * file = fmemopen(audio->filebuf, audio->filesize, "rb");
|
||||||
DEBUG("<load_audio> Filesize: %ld\n", audio->filesize);
|
DEBUG("<load_audio> Filesize: %ld\n", audio->filesize);
|
||||||
if(file != NULL)
|
if(file != NULL)
|
||||||
{
|
{
|
||||||
@@ -610,7 +466,7 @@ Result load_audio(Entry_s entry, audio_s *audio)
|
|||||||
return MAKERESULT(RL_FATAL, RS_INVALIDARG, RM_APPLICATION, RD_NO_DATA);
|
return MAKERESULT(RL_FATAL, RS_INVALIDARG, RM_APPLICATION, RD_NO_DATA);
|
||||||
}
|
}
|
||||||
|
|
||||||
vorbis_info *vi = ov_info(&audio->vf, -1);
|
vorbis_info * vi = ov_info(&audio->vf, -1);
|
||||||
ndspChnSetRate(0, vi->rate);// Set sample rate to what's read from the ogg file
|
ndspChnSetRate(0, vi->rate);// Set sample rate to what's read from the ogg file
|
||||||
if (vi->channels == 2) {
|
if (vi->channels == 2) {
|
||||||
DEBUG("<load_audio> Using stereo\n");
|
DEBUG("<load_audio> Using stereo\n");
|
||||||
|
|||||||
119
source/main.c
119
source/main.c
@@ -48,8 +48,8 @@ static Thread_Arg_s iconLoadingThread_arg = {0};
|
|||||||
static Handle update_icons_mutex;
|
static Handle update_icons_mutex;
|
||||||
static bool released = false;
|
static bool released = false;
|
||||||
|
|
||||||
static Thread installCheckThreads[MODE_AMOUNT] = {0};
|
static Thread install_check_threads[MODE_AMOUNT] = {0};
|
||||||
static Thread_Arg_s installCheckThreads_arg[MODE_AMOUNT] = {0};
|
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};
|
||||||
|
|
||||||
@@ -108,13 +108,16 @@ static void stop_install_check(void)
|
|||||||
{
|
{
|
||||||
for(int i = 0; i < MODE_AMOUNT; i++)
|
for(int i = 0; i < MODE_AMOUNT; i++)
|
||||||
{
|
{
|
||||||
installCheckThreads_arg[i].run_thread = false;
|
install_check_threads_arg[i].run_thread = false;
|
||||||
}
|
}
|
||||||
for(int i = 0; i < MODE_AMOUNT; i++)
|
for(int i = 0; i < MODE_AMOUNT; i++)
|
||||||
{
|
{
|
||||||
threadJoin(installCheckThreads[i], U64_MAX);
|
if(install_check_threads[i] == NULL)
|
||||||
threadFree(installCheckThreads[i]);
|
continue;
|
||||||
installCheckThreads[i] = NULL;
|
|
||||||
|
threadJoin(install_check_threads[i], U64_MAX);
|
||||||
|
threadFree(install_check_threads[i]);
|
||||||
|
install_check_threads[i] = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,30 +135,14 @@ static void exit_thread(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void free_icons(Entry_List_s * list)
|
|
||||||
{
|
|
||||||
int amount = list->entries_count;
|
|
||||||
if(list->entries_count > list->entries_loaded*ICONS_OFFSET_AMOUNT)
|
|
||||||
amount = list->entries_loaded*ICONS_OFFSET_AMOUNT;
|
|
||||||
|
|
||||||
for(int i = 0; i < amount; i++)
|
|
||||||
{
|
|
||||||
if (list->icons[i] == NULL) continue;
|
|
||||||
C3D_TexDelete(list->icons[i]->tex);
|
|
||||||
free(list->icons[i]->tex);
|
|
||||||
free(list->icons[i]);
|
|
||||||
}
|
|
||||||
free(list->icons);
|
|
||||||
list->icons = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void free_lists(void)
|
void free_lists(void)
|
||||||
{
|
{
|
||||||
stop_install_check();
|
stop_install_check();
|
||||||
for(int i = 0; i < MODE_AMOUNT; i++)
|
for(int i = 0; i < MODE_AMOUNT; i++)
|
||||||
{
|
{
|
||||||
Entry_List_s * current_list = &lists[i];
|
Entry_List_s * const current_list = &lists[i];
|
||||||
free_icons(current_list);
|
C3D_TexDelete(¤t_list->icons_texture);
|
||||||
|
free(current_list->icons_info);
|
||||||
free(current_list->entries);
|
free(current_list->entries);
|
||||||
memset(current_list, 0, sizeof(Entry_List_s));
|
memset(current_list, 0, sizeof(Entry_List_s));
|
||||||
}
|
}
|
||||||
@@ -195,6 +182,18 @@ static void start_thread(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static u32 next_or_equal_power_of_2(u32 v)
|
||||||
|
{
|
||||||
|
v--;
|
||||||
|
v |= v >> 1;
|
||||||
|
v |= v >> 2;
|
||||||
|
v |= v >> 4;
|
||||||
|
v |= v >> 8;
|
||||||
|
v |= v >> 16;
|
||||||
|
v++;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
static void load_lists(Entry_List_s * lists)
|
static void load_lists(Entry_List_s * lists)
|
||||||
{
|
{
|
||||||
free_lists();
|
free_lists();
|
||||||
@@ -208,37 +207,69 @@ static void load_lists(Entry_List_s * lists)
|
|||||||
|
|
||||||
draw_install(loading_screen);
|
draw_install(loading_screen);
|
||||||
|
|
||||||
Entry_List_s * current_list = &lists[i];
|
Entry_List_s * const current_list = &lists[i];
|
||||||
current_list->mode = i;
|
current_list->mode = i;
|
||||||
current_list->entries_per_screen_v = entries_per_screen_v[i];
|
current_list->entries_per_screen_v = entries_per_screen_v[i];
|
||||||
current_list->entries_per_screen_h = 1;
|
current_list->entries_per_screen_h = 1;
|
||||||
current_list->entries_loaded = current_list->entries_per_screen_v * current_list->entries_per_screen_h;
|
current_list->entries_loaded = current_list->entries_per_screen_v * current_list->entries_per_screen_h;
|
||||||
current_list->entry_size = entry_size[i];
|
current_list->entry_size = entry_size[i];
|
||||||
Result res = load_entries(main_paths[i], current_list);
|
|
||||||
|
const int x_component = max(current_list->entries_per_screen_h, current_list->entries_per_screen_v);
|
||||||
|
const int y_component = min(current_list->entries_per_screen_h, current_list->entries_per_screen_v);
|
||||||
|
// A texture must have power of 2 dimensions (not necessarily the same)
|
||||||
|
// so, get the power of two greater than or equal to:
|
||||||
|
// - the size of the largest length (row or column) of icons for the width
|
||||||
|
// - the size of all of those lengths to fit the total for the height
|
||||||
|
C3D_TexInit(¤t_list->icons_texture,
|
||||||
|
next_or_equal_power_of_2(x_component * current_list->entry_size),
|
||||||
|
next_or_equal_power_of_2(y_component * current_list->entry_size * ICONS_OFFSET_AMOUNT),
|
||||||
|
GPU_RGB565);
|
||||||
|
C3D_TexSetFilter(¤t_list->icons_texture, GPU_NEAREST, GPU_NEAREST);
|
||||||
|
|
||||||
|
const float inv_width = 1.0f / current_list->icons_texture.width;
|
||||||
|
const float inv_height = 1.0f / current_list->icons_texture.height;
|
||||||
|
current_list->icons_info = (Entry_Icon_s *)calloc(x_component * y_component * ICONS_OFFSET_AMOUNT, sizeof(Entry_Icon_s));
|
||||||
|
for(int j = 0; j < y_component * ICONS_OFFSET_AMOUNT; ++j)
|
||||||
|
{
|
||||||
|
const int index = j * x_component;
|
||||||
|
for(int h = 0; h < x_component; ++h)
|
||||||
|
{
|
||||||
|
Entry_Icon_s * const icon_info = ¤t_list->icons_info[index + h];
|
||||||
|
icon_info->x = h * current_list->entry_size;
|
||||||
|
icon_info->y = j * current_list->entry_size;
|
||||||
|
icon_info->subtex.width = current_list->entry_size;
|
||||||
|
icon_info->subtex.height = current_list->entry_size;
|
||||||
|
icon_info->subtex.left = icon_info->x * inv_width;
|
||||||
|
icon_info->subtex.top = 1.0f - (icon_info->y * inv_height);
|
||||||
|
icon_info->subtex.right = icon_info->subtex.left + (icon_info->subtex.width * inv_width);
|
||||||
|
icon_info->subtex.bottom = icon_info->subtex.top - (icon_info->subtex.height * inv_height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Result res = load_entries(main_paths[i], current_list, loading_screen);
|
||||||
if(R_SUCCEEDED(res))
|
if(R_SUCCEEDED(res))
|
||||||
{
|
{
|
||||||
if(current_list->entries_count > current_list->entries_loaded*ICONS_OFFSET_AMOUNT)
|
if(current_list->entries_count > current_list->entries_loaded * ICONS_OFFSET_AMOUNT)
|
||||||
iconLoadingThread_arg.run_thread = true;
|
iconLoadingThread_arg.run_thread = true;
|
||||||
|
|
||||||
sort_by_name(current_list);
|
|
||||||
|
|
||||||
DEBUG("total: %i\n", current_list->entries_count);
|
DEBUG("total: %i\n", current_list->entries_count);
|
||||||
|
|
||||||
load_icons_first(current_list, false);
|
load_icons_first(current_list, false);
|
||||||
|
sort_by_name(current_list);
|
||||||
|
|
||||||
void (*install_check_function)(void*) = NULL;
|
void (*install_check_function)(void *) = NULL;
|
||||||
if(i == MODE_THEMES)
|
if(i == MODE_THEMES)
|
||||||
install_check_function = themes_check_installed;
|
install_check_function = themes_check_installed;
|
||||||
else if(i == MODE_SPLASHES)
|
else if(i == MODE_SPLASHES)
|
||||||
install_check_function = splash_check_installed;
|
install_check_function = splash_check_installed;
|
||||||
|
|
||||||
Thread_Arg_s * current_arg = &installCheckThreads_arg[i];
|
Thread_Arg_s * current_arg = &install_check_threads_arg[i];
|
||||||
current_arg->run_thread = true;
|
current_arg->run_thread = true;
|
||||||
current_arg->thread_arg = (void**)current_list;
|
current_arg->thread_arg = (void **)current_list;
|
||||||
|
|
||||||
if(install_check_function != NULL)
|
if(install_check_function != NULL)
|
||||||
{
|
{
|
||||||
installCheckThreads[i] = threadCreate(install_check_function, current_arg, __stacksize__, 0x3f, -2, false);
|
install_check_threads[i] = threadCreate(install_check_function, current_arg, __stacksize__, 0x3f, -2, false);
|
||||||
svcSleepThread(1e8);
|
svcSleepThread(1e8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -246,11 +277,11 @@ static void load_lists(Entry_List_s * lists)
|
|||||||
start_thread();
|
start_thread();
|
||||||
}
|
}
|
||||||
|
|
||||||
static SwkbdCallbackResult jump_menu_callback(void* entries_count, const char** ppMessage, const char* text, size_t textlen)
|
static SwkbdCallbackResult jump_menu_callback(void * entries_count, const char ** ppMessage, const char * text, size_t textlen)
|
||||||
{
|
{
|
||||||
(void)textlen;
|
(void)textlen;
|
||||||
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 = "The new position has to be\nsmaller or equal to the\nnumber of entries!";
|
||||||
return SWKBD_CALLBACK_CONTINUE;
|
return SWKBD_CALLBACK_CONTINUE;
|
||||||
@@ -505,7 +536,7 @@ int main(void)
|
|||||||
toggle_preview:
|
toggle_preview:
|
||||||
if(!preview_mode)
|
if(!preview_mode)
|
||||||
{
|
{
|
||||||
preview_mode = load_preview(*current_list, &preview, &preview_offset);
|
preview_mode = load_preview(current_list, &preview, &preview_offset);
|
||||||
if(preview_mode)
|
if(preview_mode)
|
||||||
{
|
{
|
||||||
end_frame();
|
end_frame();
|
||||||
@@ -514,7 +545,7 @@ int main(void)
|
|||||||
if(current_mode == MODE_THEMES && dspfirm)
|
if(current_mode == MODE_THEMES && dspfirm)
|
||||||
{
|
{
|
||||||
audio = calloc(1, sizeof(audio_s));
|
audio = calloc(1, sizeof(audio_s));
|
||||||
Result r = load_audio(current_list->entries[current_list->selected_entry], audio);
|
Result r = load_audio(¤t_list->entries[current_list->selected_entry], audio);
|
||||||
if (R_SUCCEEDED(r)) play_audio(audio);
|
if (R_SUCCEEDED(r)) play_audio(audio);
|
||||||
else audio = NULL;
|
else audio = NULL;
|
||||||
}
|
}
|
||||||
@@ -557,7 +588,7 @@ int main(void)
|
|||||||
{
|
{
|
||||||
aptSetHomeAllowed(false);
|
aptSetHomeAllowed(false);
|
||||||
draw_install(INSTALL_BGM);
|
draw_install(INSTALL_BGM);
|
||||||
if(R_SUCCEEDED(bgm_install(*current_entry)))
|
if(R_SUCCEEDED(bgm_install(current_entry)))
|
||||||
{
|
{
|
||||||
for(int i = 0; i < current_list->entries_count; i++)
|
for(int i = 0; i < current_list->entries_count; i++)
|
||||||
{
|
{
|
||||||
@@ -574,7 +605,7 @@ int main(void)
|
|||||||
{
|
{
|
||||||
aptSetHomeAllowed(false);
|
aptSetHomeAllowed(false);
|
||||||
draw_install(INSTALL_SINGLE);
|
draw_install(INSTALL_SINGLE);
|
||||||
if(R_SUCCEEDED(theme_install(*current_entry)))
|
if(R_SUCCEEDED(theme_install(current_entry)))
|
||||||
{
|
{
|
||||||
for(int i = 0; i < current_list->entries_count; i++)
|
for(int i = 0; i < current_list->entries_count; i++)
|
||||||
{
|
{
|
||||||
@@ -591,7 +622,7 @@ int main(void)
|
|||||||
{
|
{
|
||||||
aptSetHomeAllowed(false);
|
aptSetHomeAllowed(false);
|
||||||
draw_install(INSTALL_NO_BGM);
|
draw_install(INSTALL_NO_BGM);
|
||||||
if(R_SUCCEEDED(no_bgm_install(*current_entry)))
|
if(R_SUCCEEDED(no_bgm_install(current_entry)))
|
||||||
{
|
{
|
||||||
for(int i = 0; i < current_list->entries_count; i++)
|
for(int i = 0; i < current_list->entries_count; i++)
|
||||||
{
|
{
|
||||||
@@ -618,7 +649,7 @@ int main(void)
|
|||||||
{
|
{
|
||||||
aptSetHomeAllowed(false);
|
aptSetHomeAllowed(false);
|
||||||
draw_install(INSTALL_SHUFFLE);
|
draw_install(INSTALL_SHUFFLE);
|
||||||
Result res = shuffle_install(*current_list);
|
Result res = shuffle_install(current_list);
|
||||||
if(R_FAILED(res)) DEBUG("shuffle install result: %lx\n", res);
|
if(R_FAILED(res)) DEBUG("shuffle install result: %lx\n", res);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -723,7 +754,7 @@ int main(void)
|
|||||||
break;
|
break;
|
||||||
case MODE_SPLASHES:
|
case MODE_SPLASHES:
|
||||||
draw_install(INSTALL_SPLASH);
|
draw_install(INSTALL_SPLASH);
|
||||||
splash_install(*current_entry);
|
splash_install(current_entry);
|
||||||
for(int i = 0; i < current_list->entries_count; i++)
|
for(int i = 0; i < current_list->entries_count; i++)
|
||||||
{
|
{
|
||||||
Entry_s * splash = ¤t_list->entries[i];
|
Entry_s * splash = ¤t_list->entries[i];
|
||||||
@@ -889,7 +920,7 @@ int main(void)
|
|||||||
{
|
{
|
||||||
for(int i = 0; i < current_list->entries_loaded; i++)
|
for(int i = 0; i < current_list->entries_loaded; i++)
|
||||||
{
|
{
|
||||||
u16 miny = 24 + current_list->entry_size*i;
|
u16 miny = 24 + current_list->entry_size * i;
|
||||||
u16 maxy = miny + current_list->entry_size;
|
u16 maxy = miny + current_list->entry_size;
|
||||||
if(BETWEEN(miny, y, maxy) && current_list->scroll + i < current_list->entries_count)
|
if(BETWEEN(miny, y, maxy) && current_list->scroll + i < current_list->entries_count)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
#include "loading.h"
|
#include "loading.h"
|
||||||
|
|
||||||
// Play a given audio struct
|
// Play a given audio struct
|
||||||
Result update_audio(audio_s *audio)
|
Result update_audio(audio_s * audio)
|
||||||
{
|
{
|
||||||
u32 size = audio->wave_buf[audio->buf_pos].nsamples * 4 - audio->data_read;
|
u32 size = audio->wave_buf[audio->buf_pos].nsamples * 4 - audio->data_read;
|
||||||
DEBUG("<update_audio> Audio Size: %ld\n", size);
|
DEBUG("<update_audio> Audio Size: %ld\n", size);
|
||||||
@@ -36,7 +36,7 @@ Result update_audio(audio_s *audio)
|
|||||||
{
|
{
|
||||||
DEBUG("<update_audio> Attempting ov_read\n");
|
DEBUG("<update_audio> Attempting ov_read\n");
|
||||||
int bitstream;
|
int bitstream;
|
||||||
u32 read = ov_read(&audio->vf, (char*)audio->wave_buf[audio->buf_pos].data_vaddr + audio->data_read, size, &bitstream); // read 1 vorbis packet into wave buffer
|
u32 read = ov_read(&audio->vf, (char *)audio->wave_buf[audio->buf_pos].data_vaddr + audio->data_read, size, &bitstream); // read 1 vorbis packet into wave buffer
|
||||||
DEBUG("<update_audio> ov_read successful\n");
|
DEBUG("<update_audio> ov_read successful\n");
|
||||||
|
|
||||||
if (read <= 0) // EoF or error
|
if (read <= 0) // EoF or error
|
||||||
@@ -64,8 +64,8 @@ Result update_audio(audio_s *audio)
|
|||||||
return MAKERESULT(RL_SUCCESS, RS_SUCCESS, RM_APPLICATION, RD_SUCCESS);
|
return MAKERESULT(RL_SUCCESS, RS_SUCCESS, RM_APPLICATION, RD_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
void thread_audio(void* data) {
|
void thread_audio(void * data) {
|
||||||
audio_s *audio = (audio_s*)data;
|
audio_s * audio = (audio_s *)data;
|
||||||
while(!audio->stop) {
|
while(!audio->stop) {
|
||||||
update_audio(audio);
|
update_audio(audio);
|
||||||
}
|
}
|
||||||
@@ -73,16 +73,16 @@ void thread_audio(void* data) {
|
|||||||
ndspChnReset(0);
|
ndspChnReset(0);
|
||||||
ov_clear(&audio->vf);
|
ov_clear(&audio->vf);
|
||||||
free(audio->filebuf);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
void play_audio(audio_s *audio) {
|
void play_audio(audio_s * audio) {
|
||||||
audio->playing_thread = threadCreate(thread_audio, audio, 0x1000, 0x3F, 1, false);
|
audio->playing_thread = threadCreate(thread_audio, audio, 0x1000, 0x3F, 1, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void stop_audio(audio_s** audio_ptr) {
|
void stop_audio(audio_s ** audio_ptr) {
|
||||||
audio_s* audio = *audio_ptr;
|
audio_s * audio = *audio_ptr;
|
||||||
if(audio->playing_thread)
|
if(audio->playing_thread)
|
||||||
{
|
{
|
||||||
audio->stop = true;
|
audio->stop = true;
|
||||||
|
|||||||
@@ -109,26 +109,18 @@ static void free_icons(Entry_List_s * list)
|
|||||||
{
|
{
|
||||||
if (list != NULL)
|
if (list != NULL)
|
||||||
{
|
{
|
||||||
if (list->icons != NULL)
|
C3D_TexDelete(&list->icons_texture);
|
||||||
{
|
free(list->icons_info);
|
||||||
for (int i = 0; i < list->entries_count; i++)
|
|
||||||
{
|
|
||||||
C3D_TexDelete(list->icons[i]->tex);
|
|
||||||
free(list->icons[i]->tex);
|
|
||||||
free(list->icons[i]);
|
|
||||||
}
|
|
||||||
free(list->icons);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static C2D_Image * load_remote_smdh(Entry_s * entry, bool ignore_cache)
|
static void load_remote_smdh(Entry_s * entry, C3D_Tex * into_tex, const Entry_Icon_s * icon_info, bool ignore_cache)
|
||||||
{
|
{
|
||||||
bool not_cached = true;
|
bool not_cached = true;
|
||||||
char * smdh_buf = NULL;
|
char * smdh_buf = NULL;
|
||||||
u32 smdh_size = load_data("/info.smdh", *entry, &smdh_buf);
|
u32 smdh_size = load_data("/info.smdh", entry, &smdh_buf);
|
||||||
|
|
||||||
not_cached = !smdh_size || ignore_cache; // if the size is 0, the file wasn't there
|
not_cached = (smdh_size != sizeof(Icon_s)) || ignore_cache; // if the size is 0, the file wasn't there
|
||||||
|
|
||||||
if (not_cached)
|
if (not_cached)
|
||||||
{
|
{
|
||||||
@@ -137,15 +129,15 @@ static C2D_Image * load_remote_smdh(Entry_s * entry, bool ignore_cache)
|
|||||||
char * api_url = NULL;
|
char * api_url = NULL;
|
||||||
asprintf(&api_url, THEMEPLAZA_SMDH_FORMAT, entry->tp_download_id);
|
asprintf(&api_url, THEMEPLAZA_SMDH_FORMAT, entry->tp_download_id);
|
||||||
Result res = http_get(api_url, NULL, &smdh_buf, &smdh_size, INSTALL_NONE, "application/octet-stream");
|
Result res = http_get(api_url, NULL, &smdh_buf, &smdh_size, INSTALL_NONE, "application/octet-stream");
|
||||||
|
free(api_url);
|
||||||
if (R_FAILED(res))
|
if (R_FAILED(res))
|
||||||
{
|
{
|
||||||
free(smdh_buf);
|
free(smdh_buf);
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
free(api_url);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!smdh_size)
|
if (smdh_size != sizeof(Icon_s))
|
||||||
{
|
{
|
||||||
free(smdh_buf);
|
free(smdh_buf);
|
||||||
smdh_buf = NULL;
|
smdh_buf = NULL;
|
||||||
@@ -157,8 +149,10 @@ static C2D_Image * load_remote_smdh(Entry_s * entry, bool ignore_cache)
|
|||||||
utf8_to_utf16(fallback_name, (u8 *)"No name", 0x80);
|
utf8_to_utf16(fallback_name, (u8 *)"No name", 0x80);
|
||||||
|
|
||||||
parse_smdh(smdh, entry, fallback_name);
|
parse_smdh(smdh, entry, fallback_name);
|
||||||
C2D_Image * image = loadTextureIcon(smdh);
|
|
||||||
|
|
||||||
|
if(smdh_buf != NULL)
|
||||||
|
{
|
||||||
|
copy_texture_data(into_tex, smdh->big_icon, icon_info);
|
||||||
if (not_cached)
|
if (not_cached)
|
||||||
{
|
{
|
||||||
FSUSER_CreateDirectory(ArchiveSD, fsMakePath(PATH_UTF16, entry->path), FS_ATTRIBUTE_DIRECTORY);
|
FSUSER_CreateDirectory(ArchiveSD, fsMakePath(PATH_UTF16, entry->path), FS_ATTRIBUTE_DIRECTORY);
|
||||||
@@ -169,17 +163,14 @@ static C2D_Image * load_remote_smdh(Entry_s * entry, bool ignore_cache)
|
|||||||
buf_to_file(smdh_size, fsMakePath(PATH_UTF16, path), ArchiveSD, smdh_buf);
|
buf_to_file(smdh_size, fsMakePath(PATH_UTF16, path), ArchiveSD, smdh_buf);
|
||||||
}
|
}
|
||||||
free(smdh_buf);
|
free(smdh_buf);
|
||||||
|
}
|
||||||
return image;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void load_remote_entries(Entry_List_s * list, json_t * ids_array, bool ignore_cache, InstallType type)
|
static void load_remote_entries(Entry_List_s * list, json_t * ids_array, bool ignore_cache, InstallType type)
|
||||||
{
|
{
|
||||||
free_icons(list);
|
|
||||||
list->entries_count = json_array_size(ids_array);
|
|
||||||
free(list->entries);
|
free(list->entries);
|
||||||
|
list->entries_count = json_array_size(ids_array);
|
||||||
list->entries = calloc(list->entries_count, sizeof(Entry_s));
|
list->entries = calloc(list->entries_count, sizeof(Entry_s));
|
||||||
list->icons = calloc(list->entries_count, sizeof(C2D_Image * ));
|
|
||||||
list->entries_loaded = list->entries_count;
|
list->entries_loaded = list->entries_count;
|
||||||
|
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
@@ -195,7 +186,7 @@ static void load_remote_entries(Entry_List_s * list, json_t * ids_array, bool ig
|
|||||||
utf8_to_utf16(current_entry->path, (u8 *)entry_path, 0x106);
|
utf8_to_utf16(current_entry->path, (u8 *)entry_path, 0x106);
|
||||||
free(entry_path);
|
free(entry_path);
|
||||||
|
|
||||||
list->icons[i] = load_remote_smdh(current_entry, ignore_cache);
|
load_remote_smdh(current_entry, &list->icons_texture, &list->icons_info[i], ignore_cache);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,9 +222,6 @@ static void load_remote_list(Entry_List_s * list, json_int_t page, EntryMode mod
|
|||||||
{
|
{
|
||||||
list->tp_current_page = page;
|
list->tp_current_page = page;
|
||||||
list->mode = mode;
|
list->mode = mode;
|
||||||
list->entry_size = entry_size[mode];
|
|
||||||
list->entries_per_screen_v = entries_per_screen_v[mode];
|
|
||||||
list->entries_per_screen_h = entries_per_screen_h[mode];
|
|
||||||
|
|
||||||
json_error_t error;
|
json_error_t error;
|
||||||
json_t * root = json_loadb(page_json, json_len, 0, &error);
|
json_t * root = json_loadb(page_json, json_len, 0, &error);
|
||||||
@@ -263,16 +251,16 @@ static void load_remote_list(Entry_List_s * list, json_int_t page, EntryMode mod
|
|||||||
free(page_json);
|
free(page_json);
|
||||||
}
|
}
|
||||||
|
|
||||||
static u16 previous_path_preview[0x106] = { 0 };
|
static u16 previous_path_preview[0x106];
|
||||||
|
|
||||||
static bool load_remote_preview(Entry_s * entry, C2D_Image * preview_image, int * preview_offset)
|
static bool load_remote_preview(const Entry_s * entry, C2D_Image * preview_image, int * preview_offset)
|
||||||
{
|
{
|
||||||
bool not_cached = true;
|
bool not_cached = true;
|
||||||
|
|
||||||
if (!memcmp(&previous_path_preview, entry->path, 0x106 * sizeof(u16))) return true;
|
if (!memcmp(&previous_path_preview, entry->path, 0x106 * sizeof(u16))) return true;
|
||||||
|
|
||||||
char * preview_png = NULL;
|
char * preview_png = NULL;
|
||||||
u32 preview_size = load_data("/preview.png", *entry, &preview_png);
|
u32 preview_size = load_data("/preview.png", entry, &preview_png);
|
||||||
|
|
||||||
not_cached = !preview_size;
|
not_cached = !preview_size;
|
||||||
|
|
||||||
@@ -324,14 +312,14 @@ static bool load_remote_preview(Entry_s * entry, C2D_Image * preview_image, int
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static u16 previous_path_bgm[0x106] = { 0 };
|
static u16 previous_path_bgm[0x106];
|
||||||
|
|
||||||
static void load_remote_bgm(Entry_s * entry)
|
static void load_remote_bgm(const Entry_s * entry)
|
||||||
{
|
{
|
||||||
if (!memcmp(&previous_path_bgm, entry->path, 0x106 * sizeof(u16))) return;
|
if (!memcmp(&previous_path_bgm, entry->path, 0x106 * sizeof(u16))) return;
|
||||||
|
|
||||||
char * bgm_ogg = NULL;
|
char * bgm_ogg = NULL;
|
||||||
u32 bgm_size = load_data("/bgm.ogg", *entry, &bgm_ogg);
|
u32 bgm_size = load_data("/bgm.ogg", entry, &bgm_ogg);
|
||||||
|
|
||||||
if (!bgm_size)
|
if (!bgm_size)
|
||||||
{
|
{
|
||||||
@@ -518,6 +506,32 @@ bool themeplaza_browser(EntryMode mode)
|
|||||||
Entry_List_s list = { 0 };
|
Entry_List_s list = { 0 };
|
||||||
Entry_List_s * current_list = &list;
|
Entry_List_s * current_list = &list;
|
||||||
current_list->tp_search = strdup("");
|
current_list->tp_search = strdup("");
|
||||||
|
|
||||||
|
list.entries_per_screen_v = entries_per_screen_v[mode];
|
||||||
|
list.entries_per_screen_h = entries_per_screen_h[mode];
|
||||||
|
list.entry_size = entry_size[mode];
|
||||||
|
C3D_TexInit(¤t_list->icons_texture, 512, 256, GPU_RGB565);
|
||||||
|
C3D_TexSetFilter(¤t_list->icons_texture, GPU_NEAREST, GPU_NEAREST);
|
||||||
|
const int entries_icon_count = current_list->entries_per_screen_h * current_list->entries_per_screen_v;
|
||||||
|
current_list->icons_info = calloc(entries_icon_count, sizeof(Entry_Icon_s));
|
||||||
|
|
||||||
|
const float inv_width = 1.0f / current_list->icons_texture.width;
|
||||||
|
const float inv_height = 1.0f / current_list->icons_texture.height;
|
||||||
|
for(int i = 0; i < entries_icon_count; ++i)
|
||||||
|
{
|
||||||
|
Entry_Icon_s * const icon_info = ¤t_list->icons_info[i];
|
||||||
|
// division by how many icons can fit horizontally
|
||||||
|
const div_t d = div(i, (current_list->icons_texture.width / 48));
|
||||||
|
icon_info->x = d.rem * current_list->entry_size;
|
||||||
|
icon_info->y = d.quot * current_list->entry_size;
|
||||||
|
icon_info->subtex.width = current_list->entry_size;
|
||||||
|
icon_info->subtex.height = current_list->entry_size;
|
||||||
|
icon_info->subtex.left = icon_info->x * inv_width;
|
||||||
|
icon_info->subtex.top = 1.0f - (icon_info->y * inv_height);
|
||||||
|
icon_info->subtex.right = icon_info->subtex.left + (icon_info->subtex.width * inv_width);
|
||||||
|
icon_info->subtex.bottom = icon_info->subtex.top - (icon_info->subtex.height * inv_height);
|
||||||
|
}
|
||||||
|
|
||||||
load_remote_list(current_list, 1, mode, false);
|
load_remote_list(current_list, 1, mode, false);
|
||||||
C2D_Image preview = { 0 };
|
C2D_Image preview = { 0 };
|
||||||
|
|
||||||
@@ -615,7 +629,7 @@ bool themeplaza_browser(EntryMode mode)
|
|||||||
{
|
{
|
||||||
load_remote_bgm(current_entry);
|
load_remote_bgm(current_entry);
|
||||||
audio = calloc(1, sizeof(audio_s));
|
audio = calloc(1, sizeof(audio_s));
|
||||||
if (R_FAILED(load_audio(*current_entry, audio))) audio = NULL;
|
if (R_FAILED(load_audio(current_entry, audio))) audio = NULL;
|
||||||
if (audio != NULL) play_audio(audio);
|
if (audio != NULL) play_audio(audio);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -812,7 +826,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;
|
||||||
@@ -1131,7 +1145,7 @@ no_error:;
|
|||||||
|
|
||||||
// download exactly chunk_size bytes and toss them into buf.
|
// download exactly chunk_size bytes and toss them into buf.
|
||||||
// size contains the current offset into buf.
|
// size contains the current offset into buf.
|
||||||
ret = httpcDownloadData(&context, (u8*)(*buf) + *size, chunk_size, &read_size);
|
ret = httpcDownloadData(&context, (u8 *)(*buf) + *size, chunk_size, &read_size);
|
||||||
/* FIXME: I have no idea why this doesn't work, but it causes problems. Look into it later
|
/* FIXME: I have no idea why this doesn't work, but it causes problems. Look into it later
|
||||||
if (R_FAILED(ret))
|
if (R_FAILED(ret))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ void splash_delete(void)
|
|||||||
remove("/luma/splashbottom.bin");
|
remove("/luma/splashbottom.bin");
|
||||||
}
|
}
|
||||||
|
|
||||||
void splash_install(Entry_s splash)
|
void splash_install(const Entry_s * splash)
|
||||||
{
|
{
|
||||||
char *screen_buf = NULL;
|
char *screen_buf = NULL;
|
||||||
|
|
||||||
@@ -103,8 +103,8 @@ void splash_check_installed(void * void_arg)
|
|||||||
for(int i = 0; i < list->entries_count && arg->run_thread; i++)
|
for(int i = 0; i < list->entries_count && arg->run_thread; i++)
|
||||||
{
|
{
|
||||||
Entry_s * splash = &list->entries[i];
|
Entry_s * splash = &list->entries[i];
|
||||||
top_size = load_data("/splash.bin", *splash, &top_buf);
|
top_size = load_data("/splash.bin", splash, &top_buf);
|
||||||
bottom_size = load_data("/splashbottom.bin", *splash, &bottom_buf);
|
bottom_size = load_data("/splashbottom.bin", splash, &bottom_buf);
|
||||||
|
|
||||||
if(!top_size && !bottom_size)
|
if(!top_size && !bottom_size)
|
||||||
{
|
{
|
||||||
|
|||||||
105
source/themes.c
105
source/themes.c
@@ -32,26 +32,26 @@
|
|||||||
#define BODY_CACHE_SIZE 0x150000
|
#define BODY_CACHE_SIZE 0x150000
|
||||||
#define BGM_MAX_SIZE 0x337000
|
#define BGM_MAX_SIZE 0x337000
|
||||||
|
|
||||||
static Result install_theme_internal(Entry_List_s themes, int installmode)
|
static Result install_theme_internal(const Entry_List_s * themes, int installmode)
|
||||||
{
|
{
|
||||||
Result res = 0;
|
Result res = 0;
|
||||||
char* music = NULL;
|
char * music = NULL;
|
||||||
u32 music_size = 0;
|
u32 music_size = 0;
|
||||||
u32 shuffle_music_sizes[MAX_SHUFFLE_THEMES] = {0};
|
u32 shuffle_music_sizes[MAX_SHUFFLE_THEMES] = {0};
|
||||||
char* body = NULL;
|
char * body = NULL;
|
||||||
u32 body_size = 0;
|
u32 body_size = 0;
|
||||||
u32 shuffle_body_sizes[MAX_SHUFFLE_THEMES] = {0};
|
u32 shuffle_body_sizes[MAX_SHUFFLE_THEMES] = {0};
|
||||||
bool mono_audio = false;
|
bool mono_audio = false;
|
||||||
|
|
||||||
if(installmode & THEME_INSTALL_SHUFFLE)
|
if(installmode & THEME_INSTALL_SHUFFLE)
|
||||||
{
|
{
|
||||||
if(themes.shuffle_count < 2)
|
if(themes->shuffle_count < 2)
|
||||||
{
|
{
|
||||||
DEBUG("not enough themes selected for shuffle\n");
|
DEBUG("not enough themes selected for shuffle\n");
|
||||||
return MAKERESULT(RL_USAGE, RS_INVALIDARG, RM_COMMON, RD_INVALID_SELECTION);
|
return MAKERESULT(RL_USAGE, RS_INVALIDARG, RM_COMMON, RD_INVALID_SELECTION);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(themes.shuffle_count > MAX_SHUFFLE_THEMES)
|
if(themes->shuffle_count > MAX_SHUFFLE_THEMES)
|
||||||
{
|
{
|
||||||
DEBUG("too many themes selected for shuffle\n");
|
DEBUG("too many themes selected for shuffle\n");
|
||||||
return MAKERESULT(RL_USAGE, RS_INVALIDARG, RM_COMMON, RD_INVALID_SELECTION);
|
return MAKERESULT(RL_USAGE, RS_INVALIDARG, RM_COMMON, RD_INVALID_SELECTION);
|
||||||
@@ -68,16 +68,15 @@ static Result install_theme_internal(Entry_List_s themes, int installmode)
|
|||||||
FSUSER_OpenFile(&body_cache_handle, ArchiveThemeExt, fsMakePath(PATH_ASCII, "/BodyCache_rd.bin"), FS_OPEN_WRITE, 0);
|
FSUSER_OpenFile(&body_cache_handle, ArchiveThemeExt, fsMakePath(PATH_ASCII, "/BodyCache_rd.bin"), FS_OPEN_WRITE, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < themes->entries_count; i++)
|
||||||
for(int i = 0; i < themes.entries_count; i++)
|
|
||||||
{
|
{
|
||||||
Entry_s * current_theme = &themes.entries[i];
|
const Entry_s * current_theme = &themes->entries[i];
|
||||||
|
|
||||||
if(current_theme->in_shuffle)
|
if(current_theme->in_shuffle)
|
||||||
{
|
{
|
||||||
if(installmode & THEME_INSTALL_BODY)
|
if(installmode & THEME_INSTALL_BODY)
|
||||||
{
|
{
|
||||||
body_size = load_data("/body_LZ.bin", *current_theme, &body);
|
body_size = load_data("/body_LZ.bin", current_theme, &body);
|
||||||
if(body_size == 0)
|
if(body_size == 0)
|
||||||
{
|
{
|
||||||
free(body);
|
free(body);
|
||||||
@@ -110,7 +109,7 @@ static Result install_theme_internal(Entry_List_s themes, int installmode)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
music_size = load_data("/bgm.bcstm", *current_theme, &music);
|
music_size = load_data("/bgm.bcstm", current_theme, &music);
|
||||||
|
|
||||||
if(music_size > BGM_MAX_SIZE)
|
if(music_size > BGM_MAX_SIZE)
|
||||||
{
|
{
|
||||||
@@ -169,7 +168,7 @@ static Result install_theme_internal(Entry_List_s themes, int installmode)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Entry_s current_theme = themes.entries[themes.selected_entry];
|
const Entry_s * current_theme = &themes->entries[themes->selected_entry];
|
||||||
|
|
||||||
if(installmode & THEME_INSTALL_BODY)
|
if(installmode & THEME_INSTALL_BODY)
|
||||||
{
|
{
|
||||||
@@ -209,7 +208,7 @@ static Result install_theme_internal(Entry_List_s themes, int installmode)
|
|||||||
res = buf_to_file(music_size, fsMakePath(PATH_ASCII, "/BgmCache.bin"), ArchiveThemeExt, music);
|
res = buf_to_file(music_size, fsMakePath(PATH_ASCII, "/BgmCache.bin"), ArchiveThemeExt, music);
|
||||||
free(music);
|
free(music);
|
||||||
|
|
||||||
char *body_buf = NULL;
|
char * body_buf = NULL;
|
||||||
u32 uncompressed_size = decompress_lz_file(fsMakePath(PATH_ASCII, "/BodyCache.bin"), ArchiveThemeExt, &body_buf);
|
u32 uncompressed_size = decompress_lz_file(fsMakePath(PATH_ASCII, "/BodyCache.bin"), ArchiveThemeExt, &body_buf);
|
||||||
if (body_buf[5] != 1)
|
if (body_buf[5] != 1)
|
||||||
{
|
{
|
||||||
@@ -231,7 +230,7 @@ static Result install_theme_internal(Entry_List_s themes, int installmode)
|
|||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------
|
//----------------------------------------
|
||||||
char* thememanage_buf = NULL;
|
char * thememanage_buf = NULL;
|
||||||
file_to_buf(fsMakePath(PATH_ASCII, "/ThemeManage.bin"), ArchiveThemeExt, &thememanage_buf);
|
file_to_buf(fsMakePath(PATH_ASCII, "/ThemeManage.bin"), ArchiveThemeExt, &thememanage_buf);
|
||||||
ThemeManage_bin_s * theme_manage = (ThemeManage_bin_s *)thememanage_buf;
|
ThemeManage_bin_s * theme_manage = (ThemeManage_bin_s *)thememanage_buf;
|
||||||
|
|
||||||
@@ -268,17 +267,17 @@ static Result install_theme_internal(Entry_List_s themes, int installmode)
|
|||||||
//----------------------------------------
|
//----------------------------------------
|
||||||
|
|
||||||
//----------------------------------------
|
//----------------------------------------
|
||||||
char* savedata_buf = NULL;
|
char * savedata_buf = NULL;
|
||||||
u32 savedata_size = file_to_buf(fsMakePath(PATH_ASCII, "/SaveData.dat"), ArchiveHomeExt, &savedata_buf);
|
u32 savedata_size = file_to_buf(fsMakePath(PATH_ASCII, "/SaveData.dat"), ArchiveHomeExt, &savedata_buf);
|
||||||
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->shuffle = (installmode & THEME_INSTALL_SHUFFLE) ? 1 : 0;
|
savedata->shuffle = (installmode & THEME_INSTALL_SHUFFLE) ? 1 : 0;
|
||||||
memset(savedata->shuffle_themes, 0, sizeof(ThemeEntry_s)*MAX_SHUFFLE_THEMES);
|
memset(savedata->shuffle_themes, 0, sizeof(ThemeEntry_s) * MAX_SHUFFLE_THEMES);
|
||||||
if(installmode & THEME_INSTALL_SHUFFLE)
|
if(installmode & THEME_INSTALL_SHUFFLE)
|
||||||
{
|
{
|
||||||
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;
|
||||||
@@ -304,40 +303,40 @@ static Result install_theme_internal(Entry_List_s themes, int installmode)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline Result theme_install(Entry_s theme)
|
Result theme_install(Entry_s * theme)
|
||||||
{
|
{
|
||||||
Entry_List_s list = {0};
|
Entry_List_s list = {0};
|
||||||
list.entries_count = 1;
|
list.entries_count = 1;
|
||||||
list.entries = &theme;
|
list.entries = theme;
|
||||||
list.selected_entry = 0;
|
list.selected_entry = 0;
|
||||||
return install_theme_internal(list, THEME_INSTALL_BODY | THEME_INSTALL_BGM);
|
return install_theme_internal(&list, THEME_INSTALL_BODY | THEME_INSTALL_BGM);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline Result bgm_install(Entry_s theme)
|
Result bgm_install(Entry_s * theme)
|
||||||
{
|
{
|
||||||
Entry_List_s list = {0};
|
Entry_List_s list = {0};
|
||||||
list.entries_count = 1;
|
list.entries_count = 1;
|
||||||
list.entries = &theme;
|
list.entries = theme;
|
||||||
list.selected_entry = 0;
|
list.selected_entry = 0;
|
||||||
return install_theme_internal(list, THEME_INSTALL_BGM);
|
return install_theme_internal(&list, THEME_INSTALL_BGM);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline Result no_bgm_install(Entry_s theme)
|
Result no_bgm_install(Entry_s * theme)
|
||||||
{
|
{
|
||||||
Entry_List_s list = {0};
|
Entry_List_s list = {0};
|
||||||
list.entries_count = 1;
|
list.entries_count = 1;
|
||||||
list.entries = &theme;
|
list.entries = theme;
|
||||||
list.selected_entry = 0;
|
list.selected_entry = 0;
|
||||||
return install_theme_internal(list, THEME_INSTALL_BODY);
|
return install_theme_internal(&list, THEME_INSTALL_BODY);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline Result shuffle_install(Entry_List_s themes)
|
Result shuffle_install(const Entry_List_s * themes)
|
||||||
{
|
{
|
||||||
return install_theme_internal(themes, THEME_INSTALL_SHUFFLE | THEME_INSTALL_BODY | THEME_INSTALL_BGM);
|
return install_theme_internal(themes, THEME_INSTALL_SHUFFLE | THEME_INSTALL_BODY | THEME_INSTALL_BGM);
|
||||||
}
|
}
|
||||||
|
|
||||||
static SwkbdCallbackResult
|
static SwkbdCallbackResult
|
||||||
dir_name_callback(void *data, const char ** ppMessage, const char * text, size_t textlen)
|
dir_name_callback(void * data, const char ** ppMessage, const char * text, size_t textlen)
|
||||||
{
|
{
|
||||||
(void)textlen;
|
(void)textlen;
|
||||||
(void)data;
|
(void)data;
|
||||||
@@ -377,14 +376,14 @@ Result dump_current_theme(void)
|
|||||||
struacat(path, output_dir);
|
struacat(path, output_dir);
|
||||||
FSUSER_CreateDirectory(ArchiveSD, fsMakePath(PATH_UTF16, path), FS_ATTRIBUTE_DIRECTORY);
|
FSUSER_CreateDirectory(ArchiveSD, fsMakePath(PATH_UTF16, path), FS_ATTRIBUTE_DIRECTORY);
|
||||||
|
|
||||||
char *thememanage_buf = NULL;
|
char * thememanage_buf = NULL;
|
||||||
file_to_buf(fsMakePath(PATH_ASCII, "/ThemeManage.bin"), ArchiveThemeExt, &thememanage_buf);
|
file_to_buf(fsMakePath(PATH_ASCII, "/ThemeManage.bin"), ArchiveThemeExt, &thememanage_buf);
|
||||||
ThemeManage_bin_s * theme_manage = (ThemeManage_bin_s *)thememanage_buf;
|
ThemeManage_bin_s * theme_manage = (ThemeManage_bin_s *)thememanage_buf;
|
||||||
u32 theme_size = theme_manage->body_size;
|
u32 theme_size = theme_manage->body_size;
|
||||||
u32 bgm_size = theme_manage->music_size;
|
u32 bgm_size = theme_manage->music_size;
|
||||||
free(thememanage_buf);
|
free(thememanage_buf);
|
||||||
|
|
||||||
char *temp_buf = NULL;
|
char * temp_buf = NULL;
|
||||||
file_to_buf(fsMakePath(PATH_ASCII, "/BodyCache.bin"), ArchiveThemeExt, &temp_buf);
|
file_to_buf(fsMakePath(PATH_ASCII, "/BodyCache.bin"), ArchiveThemeExt, &temp_buf);
|
||||||
u16 path_output[0x107] = { 0 };
|
u16 path_output[0x107] = { 0 };
|
||||||
memcpy(path_output, path, 0x107);
|
memcpy(path_output, path, 0x107);
|
||||||
@@ -402,7 +401,7 @@ Result dump_current_theme(void)
|
|||||||
free(temp_buf);
|
free(temp_buf);
|
||||||
temp_buf = NULL;
|
temp_buf = NULL;
|
||||||
|
|
||||||
char *smdh_file = calloc(1, 0x36c0);
|
char * smdh_file = calloc(1, 0x36c0);
|
||||||
smdh_file[0] = 0x53; // SMDH magic
|
smdh_file[0] = 0x53; // SMDH magic
|
||||||
smdh_file[1] = 0x4d;
|
smdh_file[1] = 0x4d;
|
||||||
smdh_file[2] = 0x44;
|
smdh_file[2] = 0x44;
|
||||||
@@ -467,7 +466,7 @@ Result dump_all_themes(void)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* region_arr[7] = {
|
const char * region_arr[7] = {
|
||||||
"JPN",
|
"JPN",
|
||||||
"USA",
|
"USA",
|
||||||
"EUR",
|
"EUR",
|
||||||
@@ -477,7 +476,7 @@ Result dump_all_themes(void)
|
|||||||
"TWN",
|
"TWN",
|
||||||
};
|
};
|
||||||
|
|
||||||
const char* language_arr[12] = {
|
const char * language_arr[12] = {
|
||||||
"jp",
|
"jp",
|
||||||
"en",
|
"en",
|
||||||
"fr",
|
"fr",
|
||||||
@@ -496,14 +495,14 @@ Result dump_all_themes(void)
|
|||||||
if(R_FAILED(res))
|
if(R_FAILED(res))
|
||||||
return res;
|
return res;
|
||||||
|
|
||||||
Icon_s* smdh_data = calloc(1, sizeof(Icon_s));
|
Icon_s * smdh_data = calloc(1, sizeof(Icon_s));
|
||||||
smdh_data->_padding1[0] = 0x53; // SMDH magic
|
smdh_data->_padding1[0] = 0x53; // SMDH magic
|
||||||
smdh_data->_padding1[1] = 0x4d;
|
smdh_data->_padding1[1] = 0x4d;
|
||||||
smdh_data->_padding1[2] = 0x44;
|
smdh_data->_padding1[2] = 0x44;
|
||||||
smdh_data->_padding1[3] = 0x48;
|
smdh_data->_padding1[3] = 0x48;
|
||||||
|
|
||||||
utf8_to_utf16(smdh_data->author, (u8*)"Nintendo", 0x40);
|
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);
|
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)
|
for(u32 dlc_index = 0; dlc_index <= 0xFF; ++dlc_index)
|
||||||
{
|
{
|
||||||
@@ -521,7 +520,7 @@ Result dump_all_themes(void)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
AM_ContentInfo* contentInfos = calloc(count, sizeof(AM_ContentInfo));
|
AM_ContentInfo * contentInfos = calloc(count, sizeof(AM_ContentInfo));
|
||||||
u32 readcount = 0;
|
u32 readcount = 0;
|
||||||
res = AMAPP_ListDLCContentInfos(&readcount, MEDIATYPE_SD, titleId, count, 0, contentInfos);
|
res = AMAPP_ListDLCContentInfos(&readcount, MEDIATYPE_SD, titleId, count, 0, contentInfos);
|
||||||
if(R_FAILED(res))
|
if(R_FAILED(res))
|
||||||
@@ -571,14 +570,14 @@ Result dump_all_themes(void)
|
|||||||
char contentinfoarchive_path[40] = {0};
|
char contentinfoarchive_path[40] = {0};
|
||||||
sprintf(contentinfoarchive_path, "meta:/ContentInfoArchive_%s_%s.bin", region_arr[regionCode], language_arr[language]);
|
sprintf(contentinfoarchive_path, "meta:/ContentInfoArchive_%s_%s.bin", region_arr[regionCode], language_arr[language]);
|
||||||
|
|
||||||
FILE* fh = fopen(contentinfoarchive_path, "rb");
|
FILE * fh = fopen(contentinfoarchive_path, "rb");
|
||||||
|
|
||||||
if(fh != NULL)
|
if(fh != NULL)
|
||||||
{
|
{
|
||||||
for(u32 i = 0; i < readcount; ++i)
|
for(u32 i = 0; i < readcount; ++i)
|
||||||
{
|
{
|
||||||
if(i == 0) continue;
|
if(i == 0) continue;
|
||||||
AM_ContentInfo* content = &contentInfos[i];
|
AM_ContentInfo * content = &contentInfos[i];
|
||||||
if((content->flags & (AM_CONTENT_DOWNLOADED | AM_CONTENT_OWNED)) == (AM_CONTENT_DOWNLOADED | AM_CONTENT_OWNED))
|
if((content->flags & (AM_CONTENT_DOWNLOADED | AM_CONTENT_OWNED)) == (AM_CONTENT_DOWNLOADED | AM_CONTENT_OWNED))
|
||||||
{
|
{
|
||||||
long off = 0x8 + 0xC8 * i;
|
long off = 0x8 + 0xC8 * i;
|
||||||
@@ -614,15 +613,15 @@ Result dump_all_themes(void)
|
|||||||
FSUSER_CreateDirectory(ArchiveSD, fsMakePath(PATH_ASCII, path), FS_ATTRIBUTE_DIRECTORY);
|
FSUSER_CreateDirectory(ArchiveSD, fsMakePath(PATH_ASCII, path), FS_ATTRIBUTE_DIRECTORY);
|
||||||
|
|
||||||
memset(smdh_data->name, 0, sizeof(smdh_data->name));
|
memset(smdh_data->name, 0, sizeof(smdh_data->name));
|
||||||
utf8_to_utf16(smdh_data->name, (u8*)(content_data + 0), 0x40);
|
utf8_to_utf16(smdh_data->name, (u8 *)(content_data + 0), 0x40);
|
||||||
|
|
||||||
FILE* theme_file = fopen("theme:/body_LZ.bin", "rb");
|
FILE * theme_file = fopen("theme:/body_LZ.bin", "rb");
|
||||||
if(theme_file)
|
if(theme_file)
|
||||||
{
|
{
|
||||||
fseek(theme_file, 0, SEEK_END);
|
fseek(theme_file, 0, SEEK_END);
|
||||||
long theme_size = ftell(theme_file);
|
long theme_size = ftell(theme_file);
|
||||||
fseek(theme_file, 0, SEEK_SET);
|
fseek(theme_file, 0, SEEK_SET);
|
||||||
char* theme_data = malloc(theme_size);
|
char * theme_data = malloc(theme_size);
|
||||||
fread(theme_data, 1, theme_size, theme_file);
|
fread(theme_data, 1, theme_size, theme_file);
|
||||||
fclose(theme_file);
|
fclose(theme_file);
|
||||||
|
|
||||||
@@ -633,13 +632,13 @@ Result dump_all_themes(void)
|
|||||||
free(theme_data);
|
free(theme_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
FILE* bgm_file = fopen("theme:/bgm.bcstm", "rb");
|
FILE * bgm_file = fopen("theme:/bgm.bcstm", "rb");
|
||||||
if(bgm_file)
|
if(bgm_file)
|
||||||
{
|
{
|
||||||
fseek(bgm_file, 0, SEEK_END);
|
fseek(bgm_file, 0, SEEK_END);
|
||||||
long bgm_size = ftell(bgm_file);
|
long bgm_size = ftell(bgm_file);
|
||||||
fseek(bgm_file, 0, SEEK_SET);
|
fseek(bgm_file, 0, SEEK_SET);
|
||||||
char* bgm_data = malloc(bgm_size);
|
char * bgm_data = malloc(bgm_size);
|
||||||
fread(bgm_data, 1, bgm_size, bgm_file);
|
fread(bgm_data, 1, bgm_size, bgm_file);
|
||||||
fclose(bgm_file);
|
fclose(bgm_file);
|
||||||
|
|
||||||
@@ -653,13 +652,13 @@ Result dump_all_themes(void)
|
|||||||
romfsUnmount("theme");
|
romfsUnmount("theme");
|
||||||
char icondatapath[0x107] = {0};
|
char icondatapath[0x107] = {0};
|
||||||
sprintf(icondatapath, "meta:/icons/%ld.icn", extra_index);
|
sprintf(icondatapath, "meta:/icons/%ld.icn", extra_index);
|
||||||
FILE* iconfile = fopen(icondatapath, "rb");
|
FILE * iconfile = fopen(icondatapath, "rb");
|
||||||
fread(smdh_data->big_icon, 1, sizeof(smdh_data->big_icon), iconfile);
|
fread(smdh_data->big_icon, 1, sizeof(smdh_data->big_icon), iconfile);
|
||||||
fclose(iconfile);
|
fclose(iconfile);
|
||||||
|
|
||||||
strcat(path, "/info.smdh");
|
strcat(path, "/info.smdh");
|
||||||
remake_file(fsMakePath(PATH_ASCII, path), ArchiveSD, 0x36c0);
|
remake_file(fsMakePath(PATH_ASCII, path), ArchiveSD, 0x36c0);
|
||||||
buf_to_file(0x36c0, fsMakePath(PATH_ASCII, path), ArchiveSD, (char*)smdh_data);
|
buf_to_file(0x36c0, fsMakePath(PATH_ASCII, path), ArchiveSD, (char *)smdh_data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -686,25 +685,25 @@ void themes_check_installed(void * void_arg)
|
|||||||
if(list == NULL || list->entries == NULL) return;
|
if(list == NULL || list->entries == NULL) return;
|
||||||
|
|
||||||
#ifndef CITRA_MODE
|
#ifndef CITRA_MODE
|
||||||
char* savedata_buf = NULL;
|
char * savedata_buf = NULL;
|
||||||
u32 savedata_size = file_to_buf(fsMakePath(PATH_ASCII, "/SaveData.dat"), ArchiveHomeExt, &savedata_buf);
|
u32 savedata_size = file_to_buf(fsMakePath(PATH_ASCII, "/SaveData.dat"), ArchiveHomeExt, &savedata_buf);
|
||||||
if(!savedata_size) return;
|
if(!savedata_size) return;
|
||||||
SaveData_dat_s* savedata = (SaveData_dat_s*)savedata_buf;
|
SaveData_dat_s * savedata = (SaveData_dat_s *)savedata_buf;
|
||||||
bool shuffle = savedata->shuffle;
|
bool shuffle = savedata->shuffle;
|
||||||
free(savedata_buf);
|
free(savedata_buf);
|
||||||
|
|
||||||
#define HASH_SIZE_BYTES 256/8
|
#define HASH_SIZE_BYTES 256/8
|
||||||
u8 body_hash[MAX_SHUFFLE_THEMES][HASH_SIZE_BYTES];
|
u8 body_hash[MAX_SHUFFLE_THEMES][HASH_SIZE_BYTES];
|
||||||
memset(body_hash, 0, MAX_SHUFFLE_THEMES*HASH_SIZE_BYTES);
|
memset(body_hash, 0, MAX_SHUFFLE_THEMES * HASH_SIZE_BYTES);
|
||||||
|
|
||||||
char* thememanage_buf = NULL;
|
char * thememanage_buf = NULL;
|
||||||
u32 theme_manage_size = file_to_buf(fsMakePath(PATH_ASCII, "/ThemeManage.bin"), ArchiveThemeExt, &thememanage_buf);
|
u32 theme_manage_size = file_to_buf(fsMakePath(PATH_ASCII, "/ThemeManage.bin"), ArchiveThemeExt, &thememanage_buf);
|
||||||
if(!theme_manage_size) return;
|
if(!theme_manage_size) return;
|
||||||
ThemeManage_bin_s * theme_manage = (ThemeManage_bin_s *)thememanage_buf;
|
ThemeManage_bin_s * theme_manage = (ThemeManage_bin_s *)thememanage_buf;
|
||||||
|
|
||||||
u32 single_body_size = theme_manage->body_size;
|
u32 single_body_size = theme_manage->body_size;
|
||||||
u32 shuffle_body_sizes[MAX_SHUFFLE_THEMES] = {0};
|
u32 shuffle_body_sizes[MAX_SHUFFLE_THEMES] = {0};
|
||||||
memcpy(shuffle_body_sizes, theme_manage->shuffle_body_sizes, sizeof(u32)*MAX_SHUFFLE_THEMES);
|
memcpy(shuffle_body_sizes, theme_manage->shuffle_body_sizes, sizeof(u32) * MAX_SHUFFLE_THEMES);
|
||||||
free(thememanage_buf);
|
free(thememanage_buf);
|
||||||
|
|
||||||
if(shuffle)
|
if(shuffle)
|
||||||
@@ -715,7 +714,7 @@ void themes_check_installed(void * void_arg)
|
|||||||
|
|
||||||
for(int i = 0; i < MAX_SHUFFLE_THEMES; i++)
|
for(int i = 0; i < MAX_SHUFFLE_THEMES; i++)
|
||||||
{
|
{
|
||||||
FSUSER_UpdateSha256Context(body_buf + BODY_CACHE_SIZE*i, shuffle_body_sizes[i], body_hash[i]);
|
FSUSER_UpdateSha256Context(body_buf + BODY_CACHE_SIZE * i, shuffle_body_sizes[i], body_hash[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
free(body_buf);
|
free(body_buf);
|
||||||
@@ -736,7 +735,7 @@ void themes_check_installed(void * void_arg)
|
|||||||
{
|
{
|
||||||
Entry_s * theme = &list->entries[i];
|
Entry_s * theme = &list->entries[i];
|
||||||
char * theme_body = NULL;
|
char * theme_body = NULL;
|
||||||
u32 theme_body_size = load_data("/body_LZ.bin", *theme, &theme_body);
|
u32 theme_body_size = load_data("/body_LZ.bin", theme, &theme_body);
|
||||||
if(!theme_body_size) return;
|
if(!theme_body_size) return;
|
||||||
|
|
||||||
u8 theme_body_hash[HASH_SIZE_BYTES];
|
u8 theme_body_hash[HASH_SIZE_BYTES];
|
||||||
|
|||||||
@@ -26,40 +26,42 @@
|
|||||||
|
|
||||||
#include "unicode.h"
|
#include "unicode.h"
|
||||||
|
|
||||||
ssize_t strulen(const u16 *input, ssize_t max_len)
|
ssize_t strulen(const u16 * input, ssize_t max_len)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < max_len; i++) if (input[i] == 0) return i;
|
for (int i = 0; i < max_len; i++) if (input[i] == 0) return i;
|
||||||
return max_len;
|
return max_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
void struacat(u16 *input, const char *addition)
|
void struacat(u16 * input, const char * addition)
|
||||||
{
|
{
|
||||||
ssize_t len = strulen(input, 0x106);
|
const ssize_t len = strulen(input, 0x106);
|
||||||
for (u16 i = len; i < strlen(addition) + len; i++)
|
const u16 stop_at = strlen(addition);
|
||||||
|
for (u16 i = 0; i < stop_at; i++)
|
||||||
{
|
{
|
||||||
input[i] = addition[i - len];
|
input[i + len] = addition[i];
|
||||||
}
|
}
|
||||||
input[strlen(addition) + len] = 0;
|
input[stop_at + len] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void printu(u16 *input)
|
void printu(u16 * input)
|
||||||
{
|
{
|
||||||
ssize_t in_len = strulen(input, 0x106);
|
ssize_t in_len = strulen(input, 0x106);
|
||||||
ssize_t buf_len = in_len + 1; // Plus 1 for proper null termination
|
ssize_t buf_len = in_len + 1; // Plus 1 for proper null termination
|
||||||
wchar_t *buf = calloc(buf_len, sizeof(wchar_t));
|
wchar_t * buf = calloc(buf_len, sizeof(wchar_t));
|
||||||
utf16_to_utf32((u32*)buf, input, buf_len);
|
utf16_to_utf32((u32 *)buf, input, buf_len);
|
||||||
char cbuf[0x106];
|
char cbuf[0x106];
|
||||||
sprintf(cbuf, "%ls\n", buf);
|
sprintf(cbuf, "%ls\n", buf);
|
||||||
DEBUG(cbuf);
|
DEBUG(cbuf);
|
||||||
free(buf);
|
free(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
u16 *strucat(u16 *destination, const u16 *source)
|
u16 * strucat(u16 * destination, const u16 * source)
|
||||||
{
|
{
|
||||||
ssize_t dest_len = strulen(destination, 0x106);
|
ssize_t dest_len = strulen(destination, 0x106);
|
||||||
|
|
||||||
ssize_t source_len = strulen(source, 0x106);
|
ssize_t source_len = strulen(source, 0x106);
|
||||||
|
|
||||||
memcpy(&destination[dest_len], source, source_len * sizeof(u16));
|
memcpy(&destination[dest_len], source, source_len * sizeof(u16));
|
||||||
|
destination[min(dest_len + source_len, 0x106 - 1)] = 0;
|
||||||
return destination;
|
return destination;
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user