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:
@@ -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,
|
||||||
|
|||||||
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
|
||||||
@@ -57,8 +57,8 @@ 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);
|
||||||
|
|
||||||
|
|||||||
@@ -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];
|
||||||
|
|
||||||
@@ -66,71 +59,21 @@ typedef struct {
|
|||||||
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
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -70,11 +70,11 @@ typedef struct {
|
|||||||
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);
|
||||||
|
|||||||
@@ -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))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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)
|
||||||
{
|
{
|
||||||
@@ -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++;
|
||||||
|
}
|
||||||
@@ -139,7 +139,7 @@ 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;
|
||||||
|
|
||||||
@@ -167,7 +167,7 @@ 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);
|
||||||
@@ -182,7 +182,7 @@ 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));
|
||||||
|
|||||||
256
source/loading.c
256
source/loading.c
@@ -33,58 +33,22 @@
|
|||||||
|
|
||||||
#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);
|
||||||
@@ -94,133 +58,23 @@ void parse_smdh(Icon_s *icon, Entry_s * entry, const u16 * fallback_name)
|
|||||||
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 *);
|
return (Icon_s *)info_buffer;
|
||||||
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;
|
|
||||||
|
|
||||||
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)
|
||||||
@@ -246,36 +100,40 @@ void load_icons_first(Entry_List_s * list, bool silent)
|
|||||||
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);
|
||||||
@@ -360,18 +218,18 @@ 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)
|
||||||
{
|
{
|
||||||
@@ -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)
|
||||||
@@ -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;
|
||||||
@@ -582,7 +438,7 @@ void free_preview(C2D_Image preview)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 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) {
|
||||||
|
|||||||
107
source/main.c
107
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,23 +207,55 @@ 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)
|
||||||
@@ -232,13 +263,13 @@ static void load_lists(Entry_List_s * lists)
|
|||||||
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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];
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
#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;
|
||||||
@@ -45,13 +45,13 @@ static Result install_theme_internal(Entry_List_s themes, int installmode)
|
|||||||
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
@@ -278,7 +277,7 @@ static Result install_theme_internal(Entry_List_s themes, int installmode)
|
|||||||
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,34 +303,34 @@ 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);
|
||||||
}
|
}
|
||||||
@@ -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];
|
||||||
|
|||||||
@@ -34,12 +34,13 @@ ssize_t strulen(const u16 *input, ssize_t 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)
|
||||||
@@ -61,5 +62,6 @@ u16 *strucat(u16 *destination, const u16 *source)
|
|||||||
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