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

@@ -30,7 +30,7 @@
#include "common.h"
typedef struct {
u16* camera_buffer;
u16 * camera_buffer;
Handle event_stop;
Thread cam_thread, ui_thread;
@@ -45,7 +45,7 @@ typedef struct {
bool any_update;
struct quirc* context;
struct quirc * context;
} qr_data;
bool init_qr(void);

View File

@@ -38,9 +38,19 @@
#define DEBUG(...) fprintf(stderr, __VA_ARGS__)
#define POS() DEBUG("%s (line %d)...\n", __func__, __LINE__)
#define DEBUGPOS(...) \
#define DEBUGPOS(...) do {\
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

View File

@@ -33,7 +33,7 @@
#define MAX_LINES 10
typedef enum {
typedef enum InstallType_e {
INSTALL_LOADING_THEMES,
INSTALL_LOADING_SPLASHES,
INSTALL_LOADING_ICONS,
@@ -149,8 +149,8 @@ typedef struct {
const char * instructions[BUTTONS_INFO_LINES][BUTTONS_INFO_COLUNMNS];
} Instructions_s;
extern C3D_RenderTarget* top;
extern C3D_RenderTarget* bottom;
extern C3D_RenderTarget * top;
extern C3D_RenderTarget * bottom;
extern C2D_TextBuf staticBuf, dynamicBuf;
extern C2D_Text text[TEXT_AMOUNT];
@@ -162,8 +162,8 @@ void start_frame(void);
void end_frame(void);
void set_screen(C3D_RenderTarget * screen);
void throw_error(const char* error, ErrorLevel level);
bool draw_confirm(const char* conf_msg, Entry_List_s* list);
void throw_error(const char * error, ErrorLevel level);
bool draw_confirm(const char * conf_msg, Entry_List_s * list);
void draw_preview(C2D_Image preview, int preview_offset);
@@ -177,7 +177,7 @@ void draw_text_center(gfxScreen_t target, float y, float z, float scaleX, float
void draw_home(u64 start_time, u64 cur_time);
void draw_base_interface(void);
void draw_grid_interface(Entry_List_s* list, Instructions_s instructions);
void draw_interface(Entry_List_s* list, Instructions_s instructions);
void draw_grid_interface(Entry_List_s * list, Instructions_s instructions);
void draw_interface(Entry_List_s * list, Instructions_s instructions);
#endif

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

@@ -56,13 +56,13 @@ Result open_archives(void);
Result close_archives(void);
Result load_parental_controls(Parental_Restrictions_s *restrictions);
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_file_to_buf(char *file_name, u16 *zip_path, 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 file_to_buf(FS_Path path, FS_Archive archive, 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(const char * file_name, const u16 * zip_path, 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);
Result buf_to_file(u32 size, FS_Path path, FS_Archive archive, char *buf);
Result buf_to_file(u32 size, FS_Path path, FS_Archive archive, char * buf);
void remake_file(FS_Path path, FS_Archive archive, u32 size);
void save_zip_to_sd(char * filename, u32 size, char * buf, EntryMode mode);

View File

@@ -28,6 +28,7 @@
#define LOADING_H
#include "common.h"
#include "entries_list.h"
#include "music.h"
#include <jansson.h>
@@ -45,14 +46,6 @@ enum ICON_IDS_OFFSET {
ICONS_OFFSET_AMOUNT,
};
typedef enum {
SORT_NONE,
SORT_NAME,
SORT_AUTHOR,
SORT_PATH,
} SortMode;
typedef struct {
u8 _padding1[4 + 2 + 2];
@@ -61,76 +54,26 @@ typedef struct {
u16 author[0x40];
u8 _padding2[0x2000 - 0x200 + 0x30 + 0x8];
u16 small_icon[24*24];
u16 small_icon[24 * 24];
u16 big_icon[48*48];
u16 big_icon[48 * 48];
} Icon_s;
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 {
void ** thread_arg;
volatile bool run_thread;
} Thread_Arg_s;
C2D_Image * loadTextureIcon(Icon_s *icon);
void parse_smdh(Icon_s *icon, Entry_s * entry, const u16 * fallback_name);
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 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(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);
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 handle_scrolling(Entry_List_s * list);
void load_icons_thread(void * void_arg);
u32 load_data(char * filename, Entry_s entry, char ** buf);
#endif

View File

@@ -42,7 +42,7 @@ typedef struct {
float mix[12];
u8 buf_pos;
long data_read;
char *filebuf;
char * filebuf;
u32 filesize;
volatile bool stop;
@@ -50,6 +50,6 @@ typedef struct {
} audio_s;
void play_audio(audio_s *);
void stop_audio(audio_s**);
void stop_audio(audio_s **);
#endif

View File

@@ -52,6 +52,6 @@
#define CACHE_PATH_FORMAT "/3ds/" APP_TITLE "/cache/%" JSON_INTEGER_FORMAT
bool themeplaza_browser(EntryMode mode);
Result http_get(const char *url, char ** filename, char ** buf, u32 * size, InstallType install_type, const char * acceptable_mime_types);
Result http_get(const char * url, char ** filename, char ** buf, u32 * size, InstallType install_type, const char * acceptable_mime_types);
#endif

View File

@@ -31,7 +31,7 @@
#include "loading.h"
void splash_delete(void);
void splash_install(Entry_s splash);
void splash_install(const Entry_s * splash);
void splash_check_installed(void * void_arg);

View File

@@ -64,17 +64,17 @@ typedef struct {
u32 dlc_theme_content_index;
u32 use_theme_cache;
u8 _padding1[0x338 - 8*sizeof(u32)];
u8 _padding1[0x338 - 8 * sizeof(u32)];
u32 shuffle_body_sizes[MAX_SHUFFLE_THEMES];
u32 shuffle_music_sizes[MAX_SHUFFLE_THEMES];
} ThemeManage_bin_s;
Result theme_install(Entry_s theme);
Result no_bgm_install(Entry_s theme);
Result bgm_install(Entry_s theme);
Result theme_install(Entry_s * theme);
Result no_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_all_themes(void);

View File

@@ -29,9 +29,9 @@
#include "common.h"
ssize_t strulen(const u16*, ssize_t);
void struacat(u16 *input, const char *addition);
void printu(u16 *input);
u16 *strucat(u16 *destination, const u16 *source);
ssize_t strulen(const u16 *, ssize_t);
void struacat(u16 * input, const char * addition);
void printu(u16 * input);
u16 * strucat(u16 * destination, const u16 * source);
#endif

View File

@@ -36,7 +36,7 @@
#include <archive.h>
#include <archive_entry.h>
static void start_read(qr_data *data)
static void start_read(qr_data * data)
{
LightLock_Lock(&data->mut);
while(data->writer_waiting || data->writer_active)
@@ -47,7 +47,7 @@ static void start_read(qr_data *data)
AtomicIncrement(&data->num_readers_active);
LightLock_Unlock(&data->mut);
}
static void stop_read(qr_data *data)
static void stop_read(qr_data * data)
{
LightLock_Lock(&data->mut);
AtomicDecrement(&data->num_readers_active);
@@ -57,7 +57,7 @@ static void stop_read(qr_data *data)
}
LightLock_Unlock(&data->mut);
}
static void start_write(qr_data *data)
static void start_write(qr_data * data)
{
LightLock_Lock(&data->mut);
data->writer_waiting = true;
@@ -72,7 +72,7 @@ static void start_write(qr_data *data)
LightLock_Unlock(&data->mut);
}
static void stop_write(qr_data *data)
static void stop_write(qr_data * data)
{
LightLock_Lock(&data->mut);
@@ -82,16 +82,16 @@ static void stop_write(qr_data *data)
LightLock_Unlock(&data->mut);
}
static void capture_cam_thread(void *arg)
static void capture_cam_thread(void * arg)
{
qr_data *data = (qr_data *) arg;
qr_data * data = (qr_data *) arg;
Handle cam_events[3] = {0};
cam_events[0] = data->event_stop;
u32 transferUnit;
const u32 bufsz = 400 * 240 * sizeof(u16);
u16 *buffer = linearAlloc(bufsz);
u16 * buffer = linearAlloc(bufsz);
camInit();
CAMU_SetSize(SELECT_OUT1, SIZE_CTR_TOP_LCD, CONTEXT_A);
@@ -167,9 +167,9 @@ static void capture_cam_thread(void *arg)
LightEvent_Signal(&data->event_cam_info);
}
static void update_ui(void *arg)
static void update_ui(void * arg)
{
qr_data* data = (qr_data*) arg;
qr_data * data = (qr_data *) arg;
C3D_Tex tex;
static const Tex3DS_SubTexture subt3x = { 400, 240, 0.0f, 1.0f, 400.0f/512.0f, 1.0f - (240.0f/256.0f) };
@@ -190,7 +190,7 @@ static void update_ui(void *arg)
for(u32 x = 0; x < 400; x++) {
const u32 dstPos = ((((y >> 3) * (512 >> 3) + (x >> 3)) << 6) + ((x & 1) | ((y & 1) << 1) | ((x & 2) << 1) | ((y & 2) << 2) | ((x & 4) << 2) | ((y & 4) << 3)));
((u16*)tex.data)[dstPos] = data->camera_buffer[srcPos + x];
((u16 *)tex.data)[dstPos] = data->camera_buffer[srcPos + x];
}
}
data->any_update = false;
@@ -208,7 +208,7 @@ static void update_ui(void *arg)
LightEvent_Signal(&data->event_ui_info);
}
static bool start_capture_cam(qr_data *data)
static bool start_capture_cam(qr_data * data)
{
if((data->cam_thread = threadCreate(capture_cam_thread, data, 0x10000, 0x1A, 1, false)) == NULL)
{
@@ -225,11 +225,11 @@ static bool start_capture_cam(qr_data *data)
return true;
}
static bool update_qr(qr_data *data, struct quirc_data* scan_data)
static bool update_qr(qr_data * data, struct quirc_data * scan_data)
{
int w;
int h;
u8 *image = (u8*) quirc_begin(data->context, &w, &h);
u8 * image = (u8 *)quirc_begin(data->context, &w, &h);
start_read(data);
for (int y = 0; y < h; y++) {
@@ -256,7 +256,7 @@ static bool update_qr(qr_data *data, struct quirc_data* scan_data)
return false;
}
static void start_qr(qr_data *data)
static void start_qr(qr_data * data)
{
svcCreateEvent(&data->event_stop, RESET_STICKY);
LightEvent_Init(&data->event_cam_info, RESET_STICKY);
@@ -272,7 +272,7 @@ static void start_qr(qr_data *data)
quirc_resize(data->context, 400, 240);
data->camera_buffer = calloc(1, 400 * 240 * sizeof(u16));
}
static void exit_qr(qr_data *data)
static void exit_qr(qr_data * data)
{
svcSignalEvent(data->event_stop);
@@ -308,7 +308,7 @@ bool init_qr(void)
start_qr(&data);
struct quirc_data* scan_data = calloc(1, sizeof(struct quirc_data));
struct quirc_data * scan_data = calloc(1, sizeof(struct quirc_data));
const bool ready = start_capture_cam(&data);
bool finished = !ready;
@@ -333,6 +333,7 @@ bool init_qr(void)
char * zip_buf = NULL;
char * filename = NULL;
u32 zip_size;
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))
{
@@ -350,7 +351,7 @@ bool init_qr(void)
{
draw_install(INSTALL_CHECKING_DOWNLOAD);
struct archive *a = archive_read_new();
struct archive * a = archive_read_new();
archive_read_support_format_zip(a);
int r = archive_read_open_memory(a, zip_buf, zip_size);

View File

@@ -32,8 +32,8 @@
#include <time.h>
C3D_RenderTarget* top;
C3D_RenderTarget* bottom;
C3D_RenderTarget * top;
C3D_RenderTarget * bottom;
C2D_TextBuf staticBuf, dynamicBuf;
static C2D_TextBuf widthBuf;
@@ -285,7 +285,7 @@ void draw_base_interface(void)
set_screen(top);
}
void throw_error(const char* error, ErrorLevel level)
void throw_error(const char * error, ErrorLevel level)
{
Text bottom_text = TEXT_AMOUNT;
Color text_color = COLOR_WHITE;
@@ -318,7 +318,7 @@ void throw_error(const char* error, ErrorLevel level)
}
}
bool draw_confirm(const char* conf_msg, Entry_List_s* list)
bool draw_confirm(const char * conf_msg, Entry_List_s * list)
{
while(aptMainLoop())
{
@@ -368,7 +368,7 @@ void draw_loading_bar(u32 current, u32 max, InstallType type)
draw_base_interface();
draw_install_handler(type);
set_screen(bottom);
double percent = 100*((double)current/(double)max);
double percent = 100 * ((double)current / (double)max);
u32 width = (u32)percent;
width *= 2;
C2D_DrawRectSolid(60-1, 110-1, 0.5f, 200+2, 20+2, colors[COLOR_CURSOR]);
@@ -442,13 +442,13 @@ void draw_text_wrap(float x, float y, float z, float scaleX, float scaleY, Color
continue;
}
if((consumed = decode_utf8(&codepoint, (unsigned char*)text)) == -1)
if((consumed = decode_utf8(&codepoint, (unsigned char *)text)) == -1)
break;
float character_width = scaleX * (fontGetCharWidthInfo(NULL, fontGlyphIndexFromCodePoint(NULL, codepoint))->charWidth);
if((current_width += character_width) > max_width)
{
char* last_space = NULL;
char * last_space = NULL;
for(int i = idx; i >= 0; i--)
{
if(result[i] == ' ')
@@ -498,27 +498,27 @@ void draw_text_wrap_scaled(float x, float y, float z, Color color, const char *
static void draw_entry_info(Entry_s * entry)
{
char author[0x41] = {0};
utf16_to_utf8((u8*)author, entry->author, 0x40);
utf16_to_utf8((u8 *)author, entry->author, 0x40);
draw_c2d_text(20, 35, 0.5, 0.5, 0.5, colors[COLOR_WHITE], &text[TEXT_BY_AUTHOR]);
float width = 0;
C2D_TextGetDimensions(&text[TEXT_BY_AUTHOR], 0.5, 0.5, &width, NULL);
draw_text(20+width, 35, 0.5, 0.5, 0.5, colors[COLOR_WHITE], author);
char title[0x41] = {0};
utf16_to_utf8((u8*)title, entry->name, 0x40);
utf16_to_utf8((u8 *)title, entry->name, 0x40);
draw_text(20, 50, 0.5, 0.7, 0.7, colors[COLOR_WHITE], title);
char description[0x81] = {0};
utf16_to_utf8((u8*)description, entry->desc, 0x80);
utf16_to_utf8((u8 *)description, entry->desc, 0x80);
draw_text_wrap(20, 70, 0.5, 0.5, 0.5, colors[COLOR_WHITE], description, 363);
}
void draw_grid_interface(Entry_List_s* list, Instructions_s instructions)
void draw_grid_interface(Entry_List_s * list, Instructions_s instructions)
{
draw_base_interface();
EntryMode current_mode = list->mode;
C2D_Text* mode_string[MODE_AMOUNT] = {
C2D_Text * mode_string[MODE_AMOUNT] = {
&text[TEXT_THEMEPLAZA_THEME_MODE],
&text[TEXT_THEMEPLAZA_SPLASH_MODE],
};
@@ -551,7 +551,7 @@ void draw_grid_interface(Entry_List_s* list, Instructions_s instructions)
current_entry = &list->entries[i];
char name[0x41] = {0};
utf16_to_utf8((u8*)name, current_entry->name, 0x40);
utf16_to_utf8((u8 *)name, current_entry->name, 0x40);
int vertical_offset = 0;
int horizontal_offset = i - list->scroll;
@@ -563,18 +563,16 @@ void draw_grid_interface(Entry_List_s* list, Instructions_s instructions)
vertical_offset += 24;
horizontal_offset += 16;
// theoretically impossible to have no icon when from the api
/*
if(!current_entry->placeholder_color)
if(current_entry->placeholder_color == 0)
{
*/
C2D_Image * image = list->icons[i];
C2D_DrawImageAt(*image, horizontal_offset, vertical_offset, 0.5f, NULL, 1.0f, 1.0f);
/*
const C2D_Image image = get_icon_at(list, i);
C2D_DrawImageAt(image, horizontal_offset, vertical_offset, 0.5f, NULL, 1.0f, 1.0f);
}
else
{
C2D_DrawRectSolid(horizontal_offset, vertical_offset, 0.5f, list->entry_size, list->entry_size, current_entry->placeholder_color);
*/
}
if(i == selected_entry)
{
@@ -603,12 +601,12 @@ void draw_grid_interface(Entry_List_s* list, Instructions_s instructions)
draw_c2d_text(176, 219, 0.5, 0.6, 0.6, colors[COLOR_WHITE], &text[TEXT_PAGE]);
}
void draw_interface(Entry_List_s* list, Instructions_s instructions)
void draw_interface(Entry_List_s * list, Instructions_s instructions)
{
draw_base_interface();
EntryMode current_mode = list->mode;
C2D_Text* mode_string[MODE_AMOUNT] = {
C2D_Text * mode_string[MODE_AMOUNT] = {
&text[TEXT_THEME_MODE],
&text[TEXT_SPLASH_MODE],
};
@@ -617,7 +615,7 @@ void draw_interface(Entry_List_s* list, Instructions_s instructions)
if(list->entries == NULL)
{
C2D_Text* mode_found_string[MODE_AMOUNT] = {
C2D_Text * mode_found_string[MODE_AMOUNT] = {
&text[TEXT_NO_THEME_FOUND],
&text[TEXT_NO_SPLASH_FOUND],
};
@@ -625,7 +623,7 @@ void draw_interface(Entry_List_s* list, Instructions_s instructions)
draw_c2d_text_center(GFX_TOP, 80, 0.5f, 0.7f, 0.7f, colors[COLOR_YELLOW], mode_found_string[current_mode]);
draw_c2d_text_center(GFX_TOP, 110, 0.5f, 0.7f, 0.7f, colors[COLOR_YELLOW], &text[TEXT_DOWNLOAD_FROM_QR]);
C2D_Text* mode_switch_string[MODE_AMOUNT] = {
C2D_Text * mode_switch_string[MODE_AMOUNT] = {
&text[TEXT_SWITCH_TO_SPLASHES],
&text[TEXT_SWITCH_TO_THEMES],
};
@@ -691,7 +689,7 @@ void draw_interface(Entry_List_s* list, Instructions_s instructions)
current_entry = &list->entries[i];
char name[0x41] = {0};
utf16_to_utf8((u8*)name, current_entry->name, 0x40);
utf16_to_utf8((u8 *)name, current_entry->name, 0x40);
int vertical_offset = i - list->scroll;
int horizontal_offset = 0;
@@ -729,14 +727,17 @@ void draw_interface(Entry_List_s* list, Instructions_s instructions)
C2D_DrawSpriteTinted(&sprite_installed, &tint);
}
if(!current_entry->placeholder_color)
if(current_entry->placeholder_color == 0)
{
C2D_Image * image = NULL;
if(list->entries_count > list->entries_loaded*ICONS_OFFSET_AMOUNT)
image = list->icons[ICONS_VISIBLE*list->entries_loaded + (i - list->scroll)];
C2D_Image image;
if(list->entries_count > list->entries_loaded * ICONS_OFFSET_AMOUNT)
{
const int offset_to_visible_icons = ICONS_VISIBLE * list->entries_loaded;
image = get_icon_at(list, offset_to_visible_icons + (i - list->scroll));
}
else
image = list->icons[i];
C2D_DrawImageAt(*image, horizontal_offset, vertical_offset, 0.5f, NULL, 1.0f, 1.0f);
image = get_icon_at(list, i);
C2D_DrawImageAt(image, horizontal_offset, vertical_offset, 0.5f, NULL, 1.0f, 1.0f);
}
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

@@ -122,7 +122,7 @@ Result load_parental_controls(Parental_Restrictions_s *restrictions)
return 0;
}
u32 file_to_buf(FS_Path path, FS_Archive archive, char** buf)
u32 file_to_buf(FS_Path path, FS_Archive archive, char ** buf)
{
Handle file;
Result res = 0;
@@ -139,9 +139,9 @@ u32 file_to_buf(FS_Path path, FS_Archive archive, char** buf)
return (u32)size;
}
static u32 zip_to_buf(struct archive *a, char *file_name, char ** buf)
static u32 zip_to_buf(struct archive * a, const char * file_name, char ** buf)
{
struct archive_entry *entry;
struct archive_entry * entry;
bool found = false;
u64 file_size = 0;
@@ -167,9 +167,9 @@ static u32 zip_to_buf(struct archive *a, char *file_name, char ** buf)
return (u32)file_size;
}
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);
int r = archive_read_open_memory(a, zip_memory, zip_size);
@@ -182,13 +182,13 @@ u32 zip_memory_to_buf(char *file_name, void * zip_memory, size_t zip_size, char
return zip_to_buf(a, file_name, buf);
}
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);
char *path = calloc(sizeof(char), len*sizeof(u16));
utf16_to_utf8((u8*)path, zip_path, len*sizeof(u16));
char * path = calloc(sizeof(char), len * sizeof(u16));
utf16_to_utf8((u8 *)path, zip_path, len * sizeof(u16));
struct archive *a = archive_read_new();
struct archive * a = archive_read_new();
archive_read_support_format_zip(a);
int r = archive_read_open_filename(a, path, 0x4000);
@@ -202,7 +202,7 @@ u32 zip_file_to_buf(char *file_name, u16 *zip_path, char **buf)
return zip_to_buf(a, file_name, buf);
}
Result buf_to_file(u32 size, FS_Path path, FS_Archive archive, char *buf)
Result buf_to_file(u32 size, FS_Path path, FS_Archive archive, char * buf)
{
Handle handle;
Result res = 0;
@@ -212,7 +212,7 @@ Result buf_to_file(u32 size, FS_Path path, FS_Archive archive, char *buf)
return 0;
}
u32 decompress_lz_file(FS_Path file_name, FS_Archive archive, char **buf)
u32 decompress_lz_file(FS_Path file_name, FS_Archive archive, char ** buf)
{
Handle handle;
Result res = 0;
@@ -223,7 +223,7 @@ u32 decompress_lz_file(FS_Path file_name, FS_Archive archive, char **buf)
u64 size;
FSFILE_GetSize(handle, &size);
char *temp_buf = NULL;
char * temp_buf = NULL;
if(size != 0)
{
@@ -317,9 +317,9 @@ u32 decompress_lz_file(FS_Path file_name, FS_Archive archive, char **buf)
// if i figure out a dynamic programming algorithm which ends up being significantly
// faster. Otherwise, I think this is probably a fine implementation.
u32 compress_lz_file_fast(FS_Path path, FS_Archive archive, char *in_buf, u32 size)
u32 compress_lz_file_fast(FS_Path path, FS_Archive archive, char * in_buf, u32 size)
{
char *output_buf = calloc(1, size * 2);
char * output_buf = calloc(1, size * 2);
u32 output_size = 0;
u32 mask_pos = 0;
u32 bytes_processed = 0;
@@ -363,12 +363,12 @@ void remake_file(FS_Path path, FS_Archive archive, u32 size)
FSUSER_DeleteFile(archive, path);
}
FSUSER_CreateFile(archive, path, 0, size);
char *buf = calloc(size, 1);
char * buf = calloc(size, 1);
buf_to_file(size, path, archive, buf);
free(buf);
}
static SwkbdCallbackResult fat32filter(void *user, const char **ppMessage, const char *text, size_t textlen)
static SwkbdCallbackResult fat32filter(void * user, const char ** ppMessage, const char * text, size_t textlen)
{
(void)textlen;
(void)user;

View File

@@ -33,194 +33,48 @@
#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)
FSUSER_DeleteFile(ArchiveSD, fsMakePath(PATH_UTF16, entry->path));
else
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;
// pointer to rgb565, offset by the number of rows and columns specified by current_icon
// (reminder that this is z order curve storage)
u16 * dest = ((u16 *)texture->data) + (current_icon->y * texture->width) + (current_icon->x * 8);
for (int j = 0; j < 48; j += 8)
{
memcpy(dest, src, 48*8*sizeof(u16));
src += 48*8;
dest += 64*8;
memcpy(dest, src, 48 * 8 * sizeof(u16));
src += 48 * 8;
dest += texture->width * 8;
}
return image;
GSPGPU_InvalidateDataCache(texture->data, texture->size);
}
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)
{
memcpy(entry->name, fallback_name, 0x80);
utf8_to_utf16(entry->desc, (u8*)"No description", 0x100);
utf8_to_utf16(entry->author, (u8*)"Unknown author", 0x80);
utf8_to_utf16(entry->desc, (u8 *)"No description", 0x100);
utf8_to_utf16(entry->author, (u8 *)"Unknown author", 0x80);
entry->placeholder_color = C2D_Color32(rand() % 255, rand() % 255, rand() % 255, 255);
return;
}
memcpy(entry->name, icon->name, 0x40*sizeof(u16));
memcpy(entry->desc, icon->desc, 0x80*sizeof(u16));
memcpy(entry->author, icon->author, 0x40*sizeof(u16));
memcpy(entry->name, icon->name, 0x40 * sizeof(u16));
memcpy(entry->desc, icon->desc, 0x80 * 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;
u64 size = load_data("/info.smdh", entry, &info_buffer);
if(!size) return NULL;
Icon_s * smdh = (Icon_s *)info_buffer;
C2D_Image* out = loadTextureIcon(smdh);
free(info_buffer);
return out;
}
typedef int (*sort_comparator)(const void *, const void *);
static int compare_entries_by_name(const void * a, const void * b)
{
Entry_s *entry_a = (Entry_s *)a;
Entry_s *entry_b = (Entry_s *)b;
return memcmp(entry_a->name, entry_b->name, 0x40*sizeof(u16));
}
static int compare_entries_by_author(const void * a, const void * b)
{
Entry_s *entry_a = (Entry_s *)a;
Entry_s *entry_b = (Entry_s *)b;
return memcmp(entry_a->author, entry_b->author, 0x40*sizeof(u16));
}
static int compare_entries_by_filename(const void * a, const void * b)
{
Entry_s *entry_a = (Entry_s *)a;
Entry_s *entry_b = (Entry_s *)b;
return memcmp(entry_a->path, entry_b->path, 0x106*sizeof(u16));
}
static void sort_list(Entry_List_s * list, sort_comparator compare_entries)
{
if(list->entries != NULL && list->entries != NULL)
qsort(list->entries, list->entries_count, sizeof(Entry_s), compare_entries); //alphabet sort
}
void sort_by_name(Entry_List_s * list)
{
sort_list(list, compare_entries_by_name);
list->current_sort = SORT_NAME;
}
void sort_by_author(Entry_List_s * list)
{
sort_list(list, compare_entries_by_author);
list->current_sort = SORT_AUTHOR;
}
void sort_by_filename(Entry_List_s * list)
{
sort_list(list, compare_entries_by_filename);
list->current_sort = SORT_PATH;
}
Result load_entries(const char * loading_path, Entry_List_s * list)
{
Handle dir_handle;
Result res = FSUSER_OpenDirectory(&dir_handle, ArchiveSD, fsMakePath(PATH_ASCII, loading_path));
if(R_FAILED(res))
char * info_buffer = NULL;
u32 size = load_data("/info.smdh", entry, &info_buffer);
if(size != sizeof(Icon_s))
{
DEBUG("Failed to open folder: %s\n", loading_path);
return res;
free(info_buffer);
return NULL;
}
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;
return (Icon_s *)info_buffer;
}
void load_icons_first(Entry_List_s * list, bool silent)
@@ -232,7 +86,7 @@ void load_icons_first(Entry_List_s * list, bool silent)
int starti = 0, endi = 0;
if(list->entries_count <= list->entries_loaded*ICONS_OFFSET_AMOUNT)
if(list->entries_count <= list->entries_loaded * ICONS_OFFSET_AMOUNT)
{
DEBUG("small load\n");
// if the list is one that doesnt need swapping, load everything at once
@@ -242,40 +96,44 @@ void load_icons_first(Entry_List_s * list, bool silent)
{
DEBUG("extended load\n");
// otherwise, load around to prepare for swapping
starti = list->scroll - list->entries_loaded*ICONS_VISIBLE;
endi = starti + list->entries_loaded*ICONS_OFFSET_AMOUNT;
starti = list->scroll - list->entries_loaded * ICONS_VISIBLE;
endi = starti + list->entries_loaded * ICONS_OFFSET_AMOUNT;
}
list->icons = calloc(endi-starti, sizeof(C2D_Image*));
C2D_Image ** icons = list->icons;
for(int i = starti; i < endi; i++)
for(int entry_i = starti, icon_i = 0; entry_i < endi; ++entry_i, ++icon_i)
{
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)
offset += list->entries_count;
if(offset >= list->entries_count)
offset -= list->entries_count;
Entry_s current_entry = list->entries[offset];
icons[i-starti] = load_entry_icon(current_entry);
Entry_s * const current_entry = &list->entries[offset];
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;
Entry_Icon_s tmp;
for (i = 0, j = sz; i < j; i++, j--) {
C2D_Image * tmp = a[i];
a[i] = a[j];
a[j] = tmp;
memcpy(&tmp, &a[i], sizeof(Entry_Icon_s));
memcpy(&a[i], &a[j], sizeof(Entry_Icon_s));
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)
amt = size + amt;
reverse(array, size-amt-1);
@@ -293,11 +151,11 @@ void handle_scrolling(Entry_List_s * list)
{
int change = 0;
if(list->entries_count > list->entries_loaded*2 && list->previous_scroll < list->entries_loaded && list->selected_entry >= list->entries_count - list->entries_loaded)
if(list->entries_count > list->entries_loaded * 2 && list->previous_scroll < list->entries_loaded && list->selected_entry >= list->entries_count - list->entries_loaded)
{
list->scroll = list->entries_count - list->entries_loaded;
}
else if(list->entries_count > list->entries_loaded*2 && list->selected_entry < list->entries_loaded && list->previous_selected >= list->entries_count - list->entries_loaded)
else if(list->entries_count > list->entries_loaded * 2 && list->selected_entry < list->entries_loaded && list->previous_selected >= list->entries_count - list->entries_loaded)
{
list->scroll = 0;
}
@@ -341,13 +199,13 @@ static bool load_icons(Entry_List_s * current_list, Handle mutex)
handle_scrolling(current_list);
if(current_list->entries_count <= current_list->entries_loaded*ICONS_OFFSET_AMOUNT || current_list->previous_scroll == current_list->scroll)
if(current_list->entries_count <= current_list->entries_loaded * ICONS_OFFSET_AMOUNT || current_list->previous_scroll == current_list->scroll)
return false; // return if the list is one that doesnt need swapping, or if nothing changed
#define SIGN(x) (x > 0 ? 1 : ((x < 0) ? -1 : 0))
int delta = current_list->scroll - current_list->previous_scroll;
if(abs(delta) >= current_list->entries_count - current_list->entries_loaded*(ICONS_OFFSET_AMOUNT-1))
if(abs(delta) >= current_list->entries_count - current_list->entries_loaded * (ICONS_OFFSET_AMOUNT-1))
delta = -SIGN(delta) * (current_list->entries_count - abs(delta));
int starti = current_list->scroll;
@@ -360,28 +218,28 @@ static bool load_icons(Entry_List_s * current_list, Handle mutex)
}
int ctr = 0;
Entry_s ** entries = calloc(abs(delta), sizeof(Entry_s *));
int * indexes = calloc(abs(delta), sizeof(int));
Entry_s ** const entries = calloc(abs(delta), sizeof(Entry_s *));
int * const indexes = calloc(abs(delta), sizeof(int));
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++)
{
int index = 0;
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)
{
index = current_list->entries_loaded*ICONS_OFFSET_AMOUNT - delta + i - starti;
offset += current_list->entries_loaded*ICONS_UNDER - delta;
index = current_list->entries_loaded * ICONS_OFFSET_AMOUNT - delta + i - starti;
offset += current_list->entries_loaded * ICONS_UNDER - delta;
}
else
{
index = 0 - delta - 1 + i - starti;
offset -= current_list->entries_loaded*ICONS_VISIBLE;
offset -= current_list->entries_loaded * ICONS_VISIBLE;
i -= 2; //i-- twice to counter the i++, needed only for this case
}
@@ -396,7 +254,7 @@ static bool load_icons(Entry_List_s * current_list, Handle mutex)
#undef SIGN
if(abs(delta) < 4)
if(abs(delta) <= current_list->entries_loaded)
{
svcReleaseMutex(mutex);
released = true;
@@ -407,19 +265,19 @@ static bool load_icons(Entry_List_s * current_list, Handle mutex)
endi = abs(delta);
for(int i = starti; i < endi; i++)
{
Entry_s * current_entry = entries[i];
int index = indexes[i];
Entry_s * const current_entry = entries[i];
const int index = indexes[i];
const Entry_Icon_s * const current_icon = &icons[index];
C2D_Image * image = icons[index];
if (icons[index] != NULL)
Icon_s * const smdh = load_entry_icon(current_entry);
if(smdh != NULL)
{
C3D_TexDelete(image->tex);
free(image->tex);
free(image);
if(current_entry->placeholder_color == 0)
parse_smdh(smdh, current_entry, current_entry->path + strlen(current_list->loading_path));
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)
{
svcReleaseMutex(mutex);
@@ -439,15 +297,13 @@ void load_icons_thread(void * void_arg)
{
Thread_Arg_s * arg = (Thread_Arg_s *)void_arg;
Handle mutex = *(Handle *)arg->thread_arg[1];
do
{
do {
svcWaitSynchronization(mutex, U64_MAX);
volatile Entry_List_s * current_list = *(volatile Entry_List_s **)arg->thread_arg[0];
bool released = load_icons((Entry_List_s *)current_list, mutex);
Entry_List_s * const current_list = *(Entry_List_s ** volatile)arg->thread_arg[0];
const bool released = load_icons(current_list, mutex);
if(!released)
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)
@@ -457,7 +313,7 @@ bool load_preview_from_buffer(char * row_pointers, u32 size, C2D_Image * preview
free_preview(*preview_image);
C3D_Tex* tex = malloc(sizeof(C3D_Tex));
C3D_Tex * tex = malloc(sizeof(C3D_Tex));
preview_image->tex = tex;
Tex3DS_SubTexture * subt3x = malloc(sizeof(Tex3DS_SubTexture));
@@ -489,16 +345,16 @@ bool load_preview_from_buffer(char * row_pointers, u32 size, C2D_Image * preview
}
static u16 previous_path_preview[0x106] = {0};
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;
u64 size = load_data("/preview.png", entry, &preview_buffer);
char * preview_buffer = NULL;
u32 size = load_data("/preview.png", entry, &preview_buffer);
if(size)
{
@@ -567,7 +423,7 @@ bool load_preview(Entry_List_s list, C2D_Image * preview_image, int * preview_of
if(ret)
{
// 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;
@@ -578,11 +434,11 @@ void free_preview(C2D_Image preview)
if(preview.tex)
C3D_TexDelete(preview.tex);
free(preview.tex);
free((Tex3DS_SubTexture*)preview.subtex);
free((Tex3DS_SubTexture *)preview.subtex);
}
// 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);
if (audio->filesize == 0) {
@@ -596,7 +452,7 @@ Result load_audio(Entry_s entry, audio_s *audio)
ndspChnSetInterp(0, NDSP_INTERP_LINEAR);
ndspChnSetMix(0, audio->mix); // See mix comment above
FILE *file = fmemopen(audio->filebuf, audio->filesize, "rb");
FILE * file = fmemopen(audio->filebuf, audio->filesize, "rb");
DEBUG("<load_audio> Filesize: %ld\n", audio->filesize);
if(file != NULL)
{
@@ -610,7 +466,7 @@ Result load_audio(Entry_s entry, audio_s *audio)
return MAKERESULT(RL_FATAL, RS_INVALIDARG, RM_APPLICATION, RD_NO_DATA);
}
vorbis_info *vi = ov_info(&audio->vf, -1);
vorbis_info * vi = ov_info(&audio->vf, -1);
ndspChnSetRate(0, vi->rate);// Set sample rate to what's read from the ogg file
if (vi->channels == 2) {
DEBUG("<load_audio> Using stereo\n");

View File

@@ -48,8 +48,8 @@ static Thread_Arg_s iconLoadingThread_arg = {0};
static Handle update_icons_mutex;
static bool released = false;
static Thread installCheckThreads[MODE_AMOUNT] = {0};
static Thread_Arg_s installCheckThreads_arg[MODE_AMOUNT] = {0};
static Thread install_check_threads[MODE_AMOUNT] = {0};
static Thread_Arg_s install_check_threads_arg[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++)
{
installCheckThreads_arg[i].run_thread = false;
install_check_threads_arg[i].run_thread = false;
}
for(int i = 0; i < MODE_AMOUNT; i++)
{
threadJoin(installCheckThreads[i], U64_MAX);
threadFree(installCheckThreads[i]);
installCheckThreads[i] = NULL;
if(install_check_threads[i] == NULL)
continue;
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)
{
stop_install_check();
for(int i = 0; i < MODE_AMOUNT; i++)
{
Entry_List_s * current_list = &lists[i];
free_icons(current_list);
Entry_List_s * const current_list = &lists[i];
C3D_TexDelete(&current_list->icons_texture);
free(current_list->icons_info);
free(current_list->entries);
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)
{
free_lists();
@@ -208,37 +207,69 @@ static void load_lists(Entry_List_s * lists)
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->entries_per_screen_v = entries_per_screen_v[i];
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->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(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;
sort_by_name(current_list);
DEBUG("total: %i\n", current_list->entries_count);
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)
install_check_function = themes_check_installed;
else if(i == MODE_SPLASHES)
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->thread_arg = (void**)current_list;
current_arg->thread_arg = (void **)current_list;
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);
}
}
@@ -246,11 +277,11 @@ static void load_lists(Entry_List_s * lists)
start_thread();
}
static SwkbdCallbackResult jump_menu_callback(void* entries_count, const char** ppMessage, const char* text, size_t textlen)
static SwkbdCallbackResult jump_menu_callback(void * entries_count, const char ** ppMessage, const char * text, size_t textlen)
{
(void)textlen;
int typed_value = atoi(text);
if(typed_value > *(int*)entries_count)
if(typed_value > *(int *)entries_count)
{
*ppMessage = "The new position has to be\nsmaller or equal to the\nnumber of entries!";
return SWKBD_CALLBACK_CONTINUE;
@@ -505,7 +536,7 @@ int main(void)
toggle_preview:
if(!preview_mode)
{
preview_mode = load_preview(*current_list, &preview, &preview_offset);
preview_mode = load_preview(current_list, &preview, &preview_offset);
if(preview_mode)
{
end_frame();
@@ -514,7 +545,7 @@ int main(void)
if(current_mode == MODE_THEMES && dspfirm)
{
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);
else audio = NULL;
}
@@ -557,7 +588,7 @@ int main(void)
{
aptSetHomeAllowed(false);
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++)
{
@@ -574,7 +605,7 @@ int main(void)
{
aptSetHomeAllowed(false);
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++)
{
@@ -591,7 +622,7 @@ int main(void)
{
aptSetHomeAllowed(false);
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++)
{
@@ -618,7 +649,7 @@ int main(void)
{
aptSetHomeAllowed(false);
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);
else
{
@@ -723,7 +754,7 @@ int main(void)
break;
case MODE_SPLASHES:
draw_install(INSTALL_SPLASH);
splash_install(*current_entry);
splash_install(current_entry);
for(int i = 0; i < current_list->entries_count; i++)
{
Entry_s * splash = &current_list->entries[i];
@@ -889,7 +920,7 @@ int main(void)
{
for(int i = 0; i < current_list->entries_loaded; i++)
{
u16 miny = 24 + current_list->entry_size*i;
u16 miny = 24 + current_list->entry_size * i;
u16 maxy = miny + current_list->entry_size;
if(BETWEEN(miny, y, maxy) && current_list->scroll + i < current_list->entries_count)
{

View File

@@ -28,7 +28,7 @@
#include "loading.h"
// Play a given audio struct
Result update_audio(audio_s *audio)
Result update_audio(audio_s * audio)
{
u32 size = audio->wave_buf[audio->buf_pos].nsamples * 4 - audio->data_read;
DEBUG("<update_audio> Audio Size: %ld\n", size);
@@ -36,7 +36,7 @@ Result update_audio(audio_s *audio)
{
DEBUG("<update_audio> Attempting ov_read\n");
int bitstream;
u32 read = ov_read(&audio->vf, (char*)audio->wave_buf[audio->buf_pos].data_vaddr + audio->data_read, size, &bitstream); // read 1 vorbis packet into wave buffer
u32 read = ov_read(&audio->vf, (char *)audio->wave_buf[audio->buf_pos].data_vaddr + audio->data_read, size, &bitstream); // read 1 vorbis packet into wave buffer
DEBUG("<update_audio> ov_read successful\n");
if (read <= 0) // EoF or error
@@ -64,8 +64,8 @@ Result update_audio(audio_s *audio)
return MAKERESULT(RL_SUCCESS, RS_SUCCESS, RM_APPLICATION, RD_SUCCESS);
}
void thread_audio(void* data) {
audio_s *audio = (audio_s*)data;
void thread_audio(void * data) {
audio_s * audio = (audio_s *)data;
while(!audio->stop) {
update_audio(audio);
}
@@ -73,16 +73,16 @@ void thread_audio(void* data) {
ndspChnReset(0);
ov_clear(&audio->vf);
free(audio->filebuf);
linearFree((void*)audio->wave_buf[0].data_vaddr);
linearFree((void*)audio->wave_buf[1].data_vaddr);
linearFree((void *)audio->wave_buf[0].data_vaddr);
linearFree((void *)audio->wave_buf[1].data_vaddr);
}
void play_audio(audio_s *audio) {
void play_audio(audio_s * audio) {
audio->playing_thread = threadCreate(thread_audio, audio, 0x1000, 0x3F, 1, false);
}
void stop_audio(audio_s** audio_ptr) {
audio_s* audio = *audio_ptr;
void stop_audio(audio_s ** audio_ptr) {
audio_s * audio = *audio_ptr;
if(audio->playing_thread)
{
audio->stop = true;

View File

@@ -109,26 +109,18 @@ static void free_icons(Entry_List_s * list)
{
if (list != NULL)
{
if (list->icons != NULL)
{
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);
}
C3D_TexDelete(&list->icons_texture);
free(list->icons_info);
}
}
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;
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)
{
@@ -137,15 +129,15 @@ static C2D_Image * load_remote_smdh(Entry_s * entry, bool ignore_cache)
char * api_url = NULL;
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");
free(api_url);
if (R_FAILED(res))
{
free(smdh_buf);
return false;
return;
}
free(api_url);
}
if (!smdh_size)
if (smdh_size != sizeof(Icon_s))
{
free(smdh_buf);
smdh_buf = NULL;
@@ -157,29 +149,28 @@ static C2D_Image * load_remote_smdh(Entry_s * entry, bool ignore_cache)
utf8_to_utf16(fallback_name, (u8 *)"No name", 0x80);
parse_smdh(smdh, entry, fallback_name);
C2D_Image * image = loadTextureIcon(smdh);
if (not_cached)
if(smdh_buf != NULL)
{
FSUSER_CreateDirectory(ArchiveSD, fsMakePath(PATH_UTF16, entry->path), FS_ATTRIBUTE_DIRECTORY);
u16 path[0x107] = { 0 };
strucat(path, entry->path);
struacat(path, "/info.smdh");
remake_file(fsMakePath(PATH_UTF16, path), ArchiveSD, smdh_size);
buf_to_file(smdh_size, fsMakePath(PATH_UTF16, path), ArchiveSD, smdh_buf);
copy_texture_data(into_tex, smdh->big_icon, icon_info);
if (not_cached)
{
FSUSER_CreateDirectory(ArchiveSD, fsMakePath(PATH_UTF16, entry->path), FS_ATTRIBUTE_DIRECTORY);
u16 path[0x107] = { 0 };
strucat(path, entry->path);
struacat(path, "/info.smdh");
remake_file(fsMakePath(PATH_UTF16, path), ArchiveSD, smdh_size);
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)
{
free_icons(list);
list->entries_count = json_array_size(ids_array);
free(list->entries);
list->entries_count = json_array_size(ids_array);
list->entries = calloc(list->entries_count, sizeof(Entry_s));
list->icons = calloc(list->entries_count, sizeof(C2D_Image * ));
list->entries_loaded = list->entries_count;
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);
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->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_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);
}
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;
if (!memcmp(&previous_path_preview, entry->path, 0x106 * sizeof(u16))) return true;
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;
@@ -324,14 +312,14 @@ static bool load_remote_preview(Entry_s * entry, C2D_Image * preview_image, int
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;
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)
{
@@ -518,6 +506,32 @@ bool themeplaza_browser(EntryMode mode)
Entry_List_s list = { 0 };
Entry_List_s * current_list = &list;
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);
C2D_Image preview = { 0 };
@@ -615,7 +629,7 @@ bool themeplaza_browser(EntryMode mode)
{
load_remote_bgm(current_entry);
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);
}
}
@@ -812,7 +826,7 @@ typedef enum ParseResult
HTTP_GATEWAY_TIMEOUT = 504,
} ParseResult;
/*static SwkbdCallbackResult fat32filter(void *user, const char **ppMessage, const char *text, size_t textlen)
/*static SwkbdCallbackResult fat32filter(void * user, const char ** ppMessage, const char * text, size_t textlen)
{
(void)textlen;
(void)user;
@@ -1131,7 +1145,7 @@ no_error:;
// download exactly chunk_size bytes and toss them into buf.
// size contains the current offset into buf.
ret = httpcDownloadData(&context, (u8*)(*buf) + *size, chunk_size, &read_size);
ret = httpcDownloadData(&context, (u8 *)(*buf) + *size, chunk_size, &read_size);
/* FIXME: I have no idea why this doesn't work, but it causes problems. Look into it later
if (R_FAILED(ret))
{

View File

@@ -34,7 +34,7 @@ void splash_delete(void)
remove("/luma/splashbottom.bin");
}
void splash_install(Entry_s splash)
void splash_install(const Entry_s * splash)
{
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++)
{
Entry_s * splash = &list->entries[i];
top_size = load_data("/splash.bin", *splash, &top_buf);
bottom_size = load_data("/splashbottom.bin", *splash, &bottom_buf);
top_size = load_data("/splash.bin", splash, &top_buf);
bottom_size = load_data("/splashbottom.bin", splash, &bottom_buf);
if(!top_size && !bottom_size)
{

View File

@@ -32,26 +32,26 @@
#define BODY_CACHE_SIZE 0x150000
#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;
char* music = NULL;
char * music = NULL;
u32 music_size = 0;
u32 shuffle_music_sizes[MAX_SHUFFLE_THEMES] = {0};
char* body = NULL;
char * body = NULL;
u32 body_size = 0;
u32 shuffle_body_sizes[MAX_SHUFFLE_THEMES] = {0};
bool mono_audio = false;
if(installmode & THEME_INSTALL_SHUFFLE)
{
if(themes.shuffle_count < 2)
if(themes->shuffle_count < 2)
{
DEBUG("not enough themes selected for shuffle\n");
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");
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);
}
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(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)
{
free(body);
@@ -110,7 +109,7 @@ static Result install_theme_internal(Entry_List_s themes, int installmode)
}
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)
{
@@ -169,7 +168,7 @@ static Result install_theme_internal(Entry_List_s themes, int installmode)
}
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)
{
@@ -209,7 +208,7 @@ static Result install_theme_internal(Entry_List_s themes, int installmode)
res = buf_to_file(music_size, fsMakePath(PATH_ASCII, "/BgmCache.bin"), ArchiveThemeExt, music);
free(music);
char *body_buf = NULL;
char * body_buf = NULL;
u32 uncompressed_size = decompress_lz_file(fsMakePath(PATH_ASCII, "/BodyCache.bin"), ArchiveThemeExt, &body_buf);
if (body_buf[5] != 1)
{
@@ -231,7 +230,7 @@ static Result install_theme_internal(Entry_List_s themes, int installmode)
}
//----------------------------------------
char* thememanage_buf = NULL;
char * thememanage_buf = NULL;
file_to_buf(fsMakePath(PATH_ASCII, "/ThemeManage.bin"), ArchiveThemeExt, &thememanage_buf);
ThemeManage_bin_s * theme_manage = (ThemeManage_bin_s *)thememanage_buf;
@@ -268,17 +267,17 @@ static Result install_theme_internal(Entry_List_s themes, int installmode)
//----------------------------------------
//----------------------------------------
char* savedata_buf = NULL;
char * savedata_buf = NULL;
u32 savedata_size = file_to_buf(fsMakePath(PATH_ASCII, "/SaveData.dat"), ArchiveHomeExt, &savedata_buf);
SaveData_dat_s* savedata = (SaveData_dat_s*)savedata_buf;
SaveData_dat_s * savedata = (SaveData_dat_s *)savedata_buf;
memset(&savedata->theme_entry, 0, sizeof(ThemeEntry_s));
savedata->shuffle = (installmode & THEME_INSTALL_SHUFFLE) ? 1 : 0;
memset(savedata->shuffle_themes, 0, sizeof(ThemeEntry_s)*MAX_SHUFFLE_THEMES);
memset(savedata->shuffle_themes, 0, sizeof(ThemeEntry_s) * MAX_SHUFFLE_THEMES);
if(installmode & THEME_INSTALL_SHUFFLE)
{
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].index = i;
@@ -304,40 +303,40 @@ static Result install_theme_internal(Entry_List_s themes, int installmode)
return 0;
}
inline Result theme_install(Entry_s theme)
Result theme_install(Entry_s * theme)
{
Entry_List_s list = {0};
list.entries_count = 1;
list.entries = &theme;
list.entries = theme;
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};
list.entries_count = 1;
list.entries = &theme;
list.entries = theme;
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};
list.entries_count = 1;
list.entries = &theme;
list.entries = theme;
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);
}
static SwkbdCallbackResult
dir_name_callback(void *data, const char ** ppMessage, const char * text, size_t textlen)
dir_name_callback(void * data, const char ** ppMessage, const char * text, size_t textlen)
{
(void)textlen;
(void)data;
@@ -377,14 +376,14 @@ Result dump_current_theme(void)
struacat(path, output_dir);
FSUSER_CreateDirectory(ArchiveSD, fsMakePath(PATH_UTF16, path), FS_ATTRIBUTE_DIRECTORY);
char *thememanage_buf = NULL;
char * thememanage_buf = NULL;
file_to_buf(fsMakePath(PATH_ASCII, "/ThemeManage.bin"), ArchiveThemeExt, &thememanage_buf);
ThemeManage_bin_s * theme_manage = (ThemeManage_bin_s *)thememanage_buf;
u32 theme_size = theme_manage->body_size;
u32 bgm_size = theme_manage->music_size;
free(thememanage_buf);
char *temp_buf = NULL;
char * temp_buf = NULL;
file_to_buf(fsMakePath(PATH_ASCII, "/BodyCache.bin"), ArchiveThemeExt, &temp_buf);
u16 path_output[0x107] = { 0 };
memcpy(path_output, path, 0x107);
@@ -402,7 +401,7 @@ Result dump_current_theme(void)
free(temp_buf);
temp_buf = NULL;
char *smdh_file = calloc(1, 0x36c0);
char * smdh_file = calloc(1, 0x36c0);
smdh_file[0] = 0x53; // SMDH magic
smdh_file[1] = 0x4d;
smdh_file[2] = 0x44;
@@ -467,7 +466,7 @@ Result dump_all_themes(void)
return -1;
}
const char* region_arr[7] = {
const char * region_arr[7] = {
"JPN",
"USA",
"EUR",
@@ -477,7 +476,7 @@ Result dump_all_themes(void)
"TWN",
};
const char* language_arr[12] = {
const char * language_arr[12] = {
"jp",
"en",
"fr",
@@ -496,14 +495,14 @@ Result dump_all_themes(void)
if(R_FAILED(res))
return res;
Icon_s* smdh_data = calloc(1, sizeof(Icon_s));
Icon_s * smdh_data = calloc(1, sizeof(Icon_s));
smdh_data->_padding1[0] = 0x53; // SMDH magic
smdh_data->_padding1[1] = 0x4d;
smdh_data->_padding1[2] = 0x44;
smdh_data->_padding1[3] = 0x48;
utf8_to_utf16(smdh_data->author, (u8*)"Nintendo", 0x40);
utf8_to_utf16(smdh_data->desc, (u8*)"Official theme. For personal use only. Do not redistribute.", 0x80);
utf8_to_utf16(smdh_data->author, (u8 *)"Nintendo", 0x40);
utf8_to_utf16(smdh_data->desc, (u8 *)"Official theme. For personal use only. Do not redistribute.", 0x80);
for(u32 dlc_index = 0; dlc_index <= 0xFF; ++dlc_index)
{
@@ -521,7 +520,7 @@ Result dump_all_themes(void)
break;
}
AM_ContentInfo* contentInfos = calloc(count, sizeof(AM_ContentInfo));
AM_ContentInfo * contentInfos = calloc(count, sizeof(AM_ContentInfo));
u32 readcount = 0;
res = AMAPP_ListDLCContentInfos(&readcount, MEDIATYPE_SD, titleId, count, 0, contentInfos);
if(R_FAILED(res))
@@ -571,14 +570,14 @@ Result dump_all_themes(void)
char contentinfoarchive_path[40] = {0};
sprintf(contentinfoarchive_path, "meta:/ContentInfoArchive_%s_%s.bin", region_arr[regionCode], language_arr[language]);
FILE* fh = fopen(contentinfoarchive_path, "rb");
FILE * fh = fopen(contentinfoarchive_path, "rb");
if(fh != NULL)
{
for(u32 i = 0; i < readcount; ++i)
{
if(i == 0) continue;
AM_ContentInfo* content = &contentInfos[i];
AM_ContentInfo * content = &contentInfos[i];
if((content->flags & (AM_CONTENT_DOWNLOADED | AM_CONTENT_OWNED)) == (AM_CONTENT_DOWNLOADED | AM_CONTENT_OWNED))
{
long off = 0x8 + 0xC8 * i;
@@ -614,15 +613,15 @@ Result dump_all_themes(void)
FSUSER_CreateDirectory(ArchiveSD, fsMakePath(PATH_ASCII, path), FS_ATTRIBUTE_DIRECTORY);
memset(smdh_data->name, 0, sizeof(smdh_data->name));
utf8_to_utf16(smdh_data->name, (u8*)(content_data + 0), 0x40);
utf8_to_utf16(smdh_data->name, (u8 *)(content_data + 0), 0x40);
FILE* theme_file = fopen("theme:/body_LZ.bin", "rb");
FILE * theme_file = fopen("theme:/body_LZ.bin", "rb");
if(theme_file)
{
fseek(theme_file, 0, SEEK_END);
long theme_size = ftell(theme_file);
fseek(theme_file, 0, SEEK_SET);
char* theme_data = malloc(theme_size);
char * theme_data = malloc(theme_size);
fread(theme_data, 1, theme_size, theme_file);
fclose(theme_file);
@@ -633,13 +632,13 @@ Result dump_all_themes(void)
free(theme_data);
}
FILE* bgm_file = fopen("theme:/bgm.bcstm", "rb");
FILE * bgm_file = fopen("theme:/bgm.bcstm", "rb");
if(bgm_file)
{
fseek(bgm_file, 0, SEEK_END);
long bgm_size = ftell(bgm_file);
fseek(bgm_file, 0, SEEK_SET);
char* bgm_data = malloc(bgm_size);
char * bgm_data = malloc(bgm_size);
fread(bgm_data, 1, bgm_size, bgm_file);
fclose(bgm_file);
@@ -653,13 +652,13 @@ Result dump_all_themes(void)
romfsUnmount("theme");
char icondatapath[0x107] = {0};
sprintf(icondatapath, "meta:/icons/%ld.icn", extra_index);
FILE* iconfile = fopen(icondatapath, "rb");
FILE * iconfile = fopen(icondatapath, "rb");
fread(smdh_data->big_icon, 1, sizeof(smdh_data->big_icon), iconfile);
fclose(iconfile);
strcat(path, "/info.smdh");
remake_file(fsMakePath(PATH_ASCII, path), ArchiveSD, 0x36c0);
buf_to_file(0x36c0, fsMakePath(PATH_ASCII, path), ArchiveSD, (char*)smdh_data);
buf_to_file(0x36c0, fsMakePath(PATH_ASCII, path), ArchiveSD, (char *)smdh_data);
}
}
@@ -686,25 +685,25 @@ void themes_check_installed(void * void_arg)
if(list == NULL || list->entries == NULL) return;
#ifndef CITRA_MODE
char* savedata_buf = NULL;
char * savedata_buf = NULL;
u32 savedata_size = file_to_buf(fsMakePath(PATH_ASCII, "/SaveData.dat"), ArchiveHomeExt, &savedata_buf);
if(!savedata_size) return;
SaveData_dat_s* savedata = (SaveData_dat_s*)savedata_buf;
SaveData_dat_s * savedata = (SaveData_dat_s *)savedata_buf;
bool shuffle = savedata->shuffle;
free(savedata_buf);
#define HASH_SIZE_BYTES 256/8
u8 body_hash[MAX_SHUFFLE_THEMES][HASH_SIZE_BYTES];
memset(body_hash, 0, MAX_SHUFFLE_THEMES*HASH_SIZE_BYTES);
memset(body_hash, 0, MAX_SHUFFLE_THEMES * HASH_SIZE_BYTES);
char* thememanage_buf = NULL;
char * thememanage_buf = NULL;
u32 theme_manage_size = file_to_buf(fsMakePath(PATH_ASCII, "/ThemeManage.bin"), ArchiveThemeExt, &thememanage_buf);
if(!theme_manage_size) return;
ThemeManage_bin_s * theme_manage = (ThemeManage_bin_s *)thememanage_buf;
u32 single_body_size = theme_manage->body_size;
u32 shuffle_body_sizes[MAX_SHUFFLE_THEMES] = {0};
memcpy(shuffle_body_sizes, theme_manage->shuffle_body_sizes, sizeof(u32)*MAX_SHUFFLE_THEMES);
memcpy(shuffle_body_sizes, theme_manage->shuffle_body_sizes, sizeof(u32) * MAX_SHUFFLE_THEMES);
free(thememanage_buf);
if(shuffle)
@@ -715,7 +714,7 @@ void themes_check_installed(void * void_arg)
for(int i = 0; i < MAX_SHUFFLE_THEMES; i++)
{
FSUSER_UpdateSha256Context(body_buf + BODY_CACHE_SIZE*i, shuffle_body_sizes[i], body_hash[i]);
FSUSER_UpdateSha256Context(body_buf + BODY_CACHE_SIZE * i, shuffle_body_sizes[i], body_hash[i]);
}
free(body_buf);
@@ -736,7 +735,7 @@ void themes_check_installed(void * void_arg)
{
Entry_s * theme = &list->entries[i];
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;
u8 theme_body_hash[HASH_SIZE_BYTES];

View File

@@ -26,40 +26,42 @@
#include "unicode.h"
ssize_t strulen(const u16 *input, ssize_t max_len)
ssize_t strulen(const u16 * input, ssize_t max_len)
{
for (int i = 0; i < max_len; i++) if (input[i] == 0) return i;
return max_len;
}
void struacat(u16 *input, const char *addition)
void struacat(u16 * input, const char * addition)
{
ssize_t len = strulen(input, 0x106);
for (u16 i = len; i < strlen(addition) + len; i++)
const ssize_t len = strulen(input, 0x106);
const u16 stop_at = strlen(addition);
for (u16 i = 0; i < stop_at; i++)
{
input[i] = addition[i - len];
input[i + len] = addition[i];
}
input[strlen(addition) + len] = 0;
input[stop_at + len] = 0;
}
void printu(u16 *input)
void printu(u16 * input)
{
ssize_t in_len = strulen(input, 0x106);
ssize_t buf_len = in_len + 1; // Plus 1 for proper null termination
wchar_t *buf = calloc(buf_len, sizeof(wchar_t));
utf16_to_utf32((u32*)buf, input, buf_len);
wchar_t * buf = calloc(buf_len, sizeof(wchar_t));
utf16_to_utf32((u32 *)buf, input, buf_len);
char cbuf[0x106];
sprintf(cbuf, "%ls\n", buf);
DEBUG(cbuf);
free(buf);
}
u16 *strucat(u16 *destination, const u16 *source)
u16 * strucat(u16 * destination, const u16 * source)
{
ssize_t dest_len = strulen(destination, 0x106);
ssize_t source_len = strulen(source, 0x106);
memcpy(&destination[dest_len], source, source_len * sizeof(u16));
destination[min(dest_len + source_len, 0x106 - 1)] = 0;
return destination;
}