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:
Théo B
2024-05-10 23:43:47 +02:00
committed by GitHub
parent d1f3dbf06b
commit 546d459696
22 changed files with 739 additions and 545 deletions

View File

@@ -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

View File

@@ -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
View 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

View File

@@ -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);

View File

@@ -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

View File

@@ -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);

View File

@@ -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);

View File

@@ -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))
{ {

View File

@@ -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
View 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++;
}

View File

@@ -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));

View File

@@ -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(&current_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) {

View File

@@ -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(&current_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(&current_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(&current_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 = &current_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(&current_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 = &current_list->entries[i]; Entry_s * splash = &current_list->entries[i];

View File

@@ -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(&current_list->icons_texture, 512, 256, GPU_RGB565);
C3D_TexSetFilter(&current_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 = &current_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);
} }
} }

View File

@@ -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)
{ {

View File

@@ -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];

View File

@@ -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;
} }