Dynamic icons loading & other improvements (#119)

* icons are black, but it's a start

* after testing, info_buffer in load_smdh_icon is for some reason all 0

* working! same speed as pre-threading (I think)
but the icons lag behind a bit.
Still, should allow for infinite themes

* not needed anymore

* fix icons not being loaded when switching modes

* stop trying to load icons if there arent enough entries

* swapping logic almost there...

* only need to update when on the main screen

* fix blind spot and typo

* allow optimizations maybe?
maybe will break stuff. just revert if it's the case

* fix blind spot when going up after cycling

* add swapping when changing 1 screen at a time (left/right)

* not needed anymore, since icon ids are fixed

* simpler scrolling

* dont reload everything when cycling

* other method for storing ids, maybe better logic later

* fix crash

* attempt at better and clearer algorithm for swapping

* optimization, swapping still broken but this was needed

* fix cycling/using left resulting in reversed icons

* fix icons scrolling in reverse
and fix the bug that introduced (same as switch-case method): scroll not following the selected entry when using left/right

* don't break when using left/right near the top/end

* correctly return from failed realloc during entries load

* move freeing the entries and killing the icon updating thread to exit_function

* fix icons being shifted the wrong value when cycling

* only exit using the shutdown screen method when needed

* show simple loading screen for themes and splashes

* only have the thread when needed
should consume less battery for people with low amount of splashes and themes

* fix instructions showing over the "no X found" screen

* fix instructions showing over confirm text

* fix overlapping and going out of bounds with few entries

* add quitting preview and qr mode with B

* add touchscreen controls

* cleaner BETWEEN macro

* only allow changing screens with touchscreen when arrows are visible

* tabs vs spaces

* fix selecting an entry that's not there using the touchscreen
and allow holding for selecting individual entries (not using the arrows)

* fix crash when deleting all entries and downloading one with QR

* add indicator for already installed themes/splashes
threaded as not to slow down initial loading too much, and imo cool effect as they load

* optimization for theme installed check

* make icon swapping thread higher priority to prevent problems with searching the installed themes/splashes

* add indicator with number of themes and selected theme

* add X to reload icons if it breaks

* add touchscreen controls:
- toggle shuffle
- toggle preview
- reload icons
- switch modes
- enable QR

* more usable thread args

* fix crash when closing the application too close to launch

* add hack to solve the scrolling problem.
Warning: will cause some lag for about 1-2 seconds, so I recommend using the jump menu
this will be removed once the bug is figured out
This commit is contained in:
LiquidFenrir
2017-12-28 21:30:23 +01:00
committed by Alex Taber
parent c85d9d978d
commit 45349a4ba3
17 changed files with 865 additions and 107 deletions

View File

@@ -54,10 +54,8 @@ u32 load_data(char * filename, Entry_s entry, char ** buf)
}
}
static void parse_smdh(Entry_s * entry, const ssize_t textureID, const u16 * fallback_name)
static void parse_smdh(Entry_s * entry, const u16 * fallback_name)
{
pp2d_free_texture(textureID);
char *info_buffer = NULL;
u64 size = load_data("/info.smdh", *entry, &info_buffer);
Icon_s * smdh = (Icon_s *)info_buffer;
@@ -75,6 +73,17 @@ static void parse_smdh(Entry_s * entry, const ssize_t textureID, const u16 * fal
memcpy(entry->name, smdh->name, 0x40*sizeof(u16));
memcpy(entry->desc, smdh->desc, 0x80*sizeof(u16));
memcpy(entry->author, smdh->author, 0x40*sizeof(u16));
}
static void load_smdh_icon(Entry_s entry, const ssize_t textureID)
{
pp2d_free_texture(textureID);
char *info_buffer = NULL;
u64 size = load_data("/info.smdh", entry, &info_buffer);
if(!size) return;
Icon_s * smdh = (Icon_s *)info_buffer;
const u32 width = 48, height = 48;
u32 *image = malloc(width*height*sizeof(u32));
@@ -93,8 +102,6 @@ static void parse_smdh(Entry_s * entry, const ssize_t textureID, const u16 * fal
free(info_buffer);
pp2d_load_texture_memory(textureID, (u8*)image, (u32)width, (u32)height);
free(image);
entry->icon_id = textureID;
}
static int compare_entries(const void * a, const void * b)
@@ -110,7 +117,7 @@ static void sort_list(Entry_List_s * list)
qsort(list->entries, list->entries_count, sizeof(Entry_s), compare_entries); //alphabet sort
}
Result load_entries(const char * loading_path, Entry_List_s * list)
Result load_entries(const char * loading_path, Entry_List_s * list, EntryMode mode)
{
Handle dir_handle;
Result res = FSUSER_OpenDirectory(&dir_handle, ArchiveSD, fsMakePath(PATH_ASCII, loading_path));
@@ -130,9 +137,16 @@ Result load_entries(const char * loading_path, Entry_List_s * list)
continue;
list->entries_count++;
list->entries = realloc(list->entries, list->entries_count * sizeof(Entry_s));
if(list->entries == NULL)
Entry_s * new_list = realloc(list->entries, list->entries_count * sizeof(Entry_s));
if(new_list == NULL)
{
free(list->entries);
list->entries = NULL;
res = -1;
break;
}
else
list->entries = new_list;
Entry_s * current_entry = &(list->entries[list->entries_count-1]);
memset(current_entry, 0, sizeof(Entry_s));
@@ -142,27 +156,219 @@ Result load_entries(const char * loading_path, Entry_List_s * list)
current_entry->is_zip = !strcmp(dir_entry.shortExt, "ZIP");
ssize_t iconID = list->icon_id_start + list->entries_count - 1;
DEBUG("id: %u\n", iconID);
parse_smdh(current_entry, iconID, dir_entry.name);
parse_smdh(current_entry, dir_entry.name);
}
FSDIR_Close(dir_handle);
sort_list(list);
list->mode = mode;
return res;
}
static u16 previous_path[0x106] = {0};
static void small_load(Entry_List_s * current_list)
{
DEBUG("small load\n");
ssize_t * icons_ids = (ssize_t *)current_list->icons_ids;
ssize_t * assoc_entry_ids = (ssize_t *)current_list->assoc_entry_ids;
ssize_t id = current_list->texture_id_offset;
for(int i = 0; i < current_list->entries_count; i++, id++)
{
Entry_s current_entry = current_list->entries[i];
load_smdh_icon(current_entry, id);
icons_ids[i] = id;
assoc_entry_ids[i] = i;
}
}
static void first_load(Entry_List_s * current_list)
{
DEBUG("first load\n");
ssize_t * above_icons_ids = current_list->icons_ids[ICONS_ABOVE];
ssize_t * visible_icons_ids = current_list->icons_ids[ICONS_VISIBLE];
ssize_t * under_icons_ids = current_list->icons_ids[ICONS_UNDER];
ssize_t * above_assoc_ids = current_list->assoc_entry_ids[ICONS_ABOVE];
ssize_t * visible_assoc_ids = current_list->assoc_entry_ids[ICONS_VISIBLE];
ssize_t * under_assoc_ids = current_list->assoc_entry_ids[ICONS_UNDER];
ssize_t id = current_list->texture_id_offset;
int starti = current_list->scroll;
memset(visible_icons_ids, 0, ENTRIES_PER_SCREEN*sizeof(ssize_t));
for(int i = starti; i < starti+ENTRIES_PER_SCREEN; i++, id++)
{
if(i >= current_list->entries_count) break;
Entry_s current_entry = current_list->entries[i];
load_smdh_icon(current_entry, id);
visible_icons_ids[i-starti] = id;
visible_assoc_ids[i-starti] = i;
}
memset(above_icons_ids, 0, ENTRIES_PER_SCREEN*sizeof(ssize_t));
starti -= ENTRIES_PER_SCREEN;
for(int i = starti; i < starti+ENTRIES_PER_SCREEN; i++, id++)
{
if(i >= current_list->entries_count) break;
int used_i = i;
if(i < 0)
used_i = current_list->entries_count + i;
Entry_s current_entry = current_list->entries[used_i];
load_smdh_icon(current_entry, id);
above_icons_ids[i-starti] = id;
above_assoc_ids[i-starti] = used_i;
}
memset(under_icons_ids, 0, ENTRIES_PER_SCREEN*sizeof(ssize_t));
starti += ENTRIES_PER_SCREEN*2;
for(int i = starti; i < starti+ENTRIES_PER_SCREEN; i++, id++)
{
int used_i = i;
if(i >= current_list->entries_count)
used_i = i - current_list->entries_count;
Entry_s current_entry = current_list->entries[used_i];
load_smdh_icon(current_entry, id);
under_icons_ids[i-starti] = id;
under_assoc_ids[i-starti] = used_i;
}
}
void load_icons_first(Entry_List_s * current_list, bool silent)
{
if(current_list == NULL || current_list->entries == NULL) return;
if(!silent)
draw_install(INSTALL_LOADING_ICONS);
if(current_list->entries_count <= ENTRIES_PER_SCREEN*ICONS_OFFSET_AMOUNT)
small_load(current_list); // if the list is one that doesnt need swapping, load everything at once
else
first_load(current_list);
}
static void reverse(ssize_t a[], int sz) {
int i, j;
for (i = 0, j = sz; i < j; i++, j--) {
ssize_t tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
}
static void rotate(ssize_t array[], int size, int amt) {
if (amt < 0)
amt = size + amt;
reverse(array, size-amt-1);
reverse(array+size-amt, amt-1);
reverse(array, size-1);
}
static void load_icons(Entry_List_s * current_list)
{
if(current_list == NULL || current_list->entries == NULL)
return;
if(current_list->entries_count <= ENTRIES_PER_SCREEN*ICONS_OFFSET_AMOUNT || current_list->previous_scroll == current_list->scroll)
return; // 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 - ENTRIES_PER_SCREEN*2)
delta = -SIGN(delta) * (current_list->entries_count - abs(delta));
int starti = current_list->scroll;
int endi = starti + abs(delta);
if(delta < 0)
{
endi -= abs(delta) + 1;
starti += abs(delta) - 1;
}
int ctr = 0;
Entry_s ** entries = calloc(abs(delta), sizeof(Entry_s *));
ssize_t * ids = calloc(abs(delta), sizeof(ssize_t));
#define FIRST(arr) arr[0]
#define LAST(arr) arr[ENTRIES_PER_SCREEN*ICONS_OFFSET_AMOUNT - 1]
ssize_t * icons_ids = (ssize_t *)current_list->icons_ids;
ssize_t * assoc_entry_ids = (ssize_t *)current_list->assoc_entry_ids;
for(int i = starti; i != endi; i++, ctr++)
{
ssize_t id = 0;
int offset = i;
ssize_t * assoc = NULL;
rotate(icons_ids, 3*ENTRIES_PER_SCREEN, -1*SIGN(delta));
if(delta > 0)
{
id = LAST(icons_ids);
assoc = &LAST(assoc_entry_ids);
offset += ENTRIES_PER_SCREEN*2 - delta;
}
else
{
id = FIRST(icons_ids);
assoc = &FIRST(assoc_entry_ids);
offset -= ENTRIES_PER_SCREEN;
i -= 2; //i-- twice to counter the i++, needed only for this case
}
if(offset < 0)
offset += current_list->entries_count;
if(offset >= current_list->entries_count)
offset -= current_list->entries_count;
entries[ctr] = &current_list->entries[offset];
ids[ctr] = id;
*assoc = id;
}
#undef FIRST
#undef LAST
#undef SIGN
svcSleepThread(1e6);
for(int i = 0; i < abs(delta); i++)
load_smdh_icon(*entries[i], ids[i]);
}
static bool loading_icons = false;
void load_icons_thread(void * void_arg)
{
Thread_Arg_s * arg = (Thread_Arg_s *)void_arg;
Handle update_request = *(Handle *)arg->thread_arg[1];
do
{
svcWaitSynchronization(update_request, U64_MAX);
svcClearEvent(update_request);
if(loading_icons) continue;
loading_icons = true;
volatile Entry_List_s * current_list = *(volatile Entry_List_s **)arg->thread_arg[0];
load_icons((Entry_List_s *)current_list);
loading_icons = false;
}
while(arg->run_thread);
}
static u16 previous_path_preview[0x106] = {0};
bool load_preview(Entry_List_s list, int * preview_offset)
{
if(list.entries == NULL) return false;
Entry_s entry = list.entries[list.selected_entry];
if(!memcmp(&previous_path, &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);
@@ -178,7 +384,7 @@ bool load_preview(Entry_List_s list, int * preview_offset)
u8 * image = NULL;
unsigned int width = 0, height = 0;
if((lodepng_decode32(&image, &width, &height, (u8*)preview_buffer, size)) == 0) // no error
if((lodepng_decode32(&image, &width, &height, (u8*)preview_buffer, size)) == 0) // no error
{
for(u32 i = 0; i < width; i++)
{
@@ -190,7 +396,7 @@ bool load_preview(Entry_List_s list, int * preview_offset)
}
// mark the new preview as loaded for optimisation
memcpy(&previous_path, &entry.path, 0x106*sizeof(u16));
memcpy(&previous_path_preview, &entry.path, 0x106*sizeof(u16));
// free the previously loaded preview. wont do anything if there wasnt one
pp2d_free_texture(TEXTURE_PREVIEW);