Memory leak fix and camera multithreading safety improvement (#239)

* memory leak fix and attempt at optimizing space

* camera rework

try to use better locking algorithm (MRSW lock on wikipedia)

* add time print (toggleable) and stuff

remove old mixed qr thingss from main

* remove the dumb 3 bytes saving

* remove useless time measure code

* forgot to close the stop event handle

* fix memory leak when loading icon from smdh

* fix entry path on folders

optimization using memcpy cause it to have the "/info.smdh" when the entry is a folder. simply remove that with a memset to 0.

Co-authored-by: Alex Taber <astronautlevel2@users.noreply.github.com>
This commit is contained in:
Théo B
2020-12-22 04:31:38 +01:00
committed by GitHub
parent 1c2e562dd6
commit 95ff2dd3ba
6 changed files with 315 additions and 238 deletions

View File

@@ -30,22 +30,24 @@
#include "common.h" #include "common.h"
typedef struct { typedef struct {
u16 *camera_buffer; u16* camera_buffer;
C2D_Image image;
C3D_Tex *tex; Handle event_stop;
Handle mutex; Thread cam_thread, ui_thread;
volatile bool finished;
volatile bool closed; LightEvent event_cam_info, event_ui_info;
volatile bool success;
Handle cancel; CondVar cond;
Handle started; LightLock mut;
u32 num_readers_active;
bool writer_waiting;
bool writer_active;
bool any_update;
bool capturing;
struct quirc* context; struct quirc* context;
} qr_data; } qr_data;
bool init_qr(void); bool init_qr(void);
void exit_qr(qr_data *data);
void take_picture(void);
#endif #endif

View File

@@ -65,7 +65,7 @@ typedef struct {
u16 desc[0x81]; u16 desc[0x81];
u16 author[0x41]; u16 author[0x41];
u32 placeholder_color; // u32 placeholder_color;
u16 path[0x106]; u16 path[0x106];
bool is_zip; bool is_zip;

View File

@@ -36,28 +36,63 @@
#include <archive.h> #include <archive.h>
#include <archive_entry.h> #include <archive_entry.h>
void exit_qr(qr_data *data) static void start_read(qr_data *data)
{ {
DEBUG("Exiting QR\n"); LightLock_Lock(&data->mut);
svcSignalEvent(data->cancel); while(data->writer_waiting || data->writer_active)
while(!data->finished) {
svcSleepThread(1000000); CondVar_Wait(&data->cond, &data->mut);
svcCloseHandle(data->cancel); }
data->capturing = false;
free(data->camera_buffer); AtomicIncrement(&data->num_readers_active);
free(data->tex); LightLock_Unlock(&data->mut);
quirc_destroy(data->context); }
static void stop_read(qr_data *data)
{
LightLock_Lock(&data->mut);
AtomicDecrement(&data->num_readers_active);
if(data->num_readers_active == 0)
{
CondVar_Signal(&data->cond);
}
LightLock_Unlock(&data->mut);
}
static void start_write(qr_data *data)
{
LightLock_Lock(&data->mut);
data->writer_waiting = true;
while(data->num_readers_active)
{
CondVar_Wait(&data->cond, &data->mut);
}
data->writer_waiting = false;
data->writer_active = true;
LightLock_Unlock(&data->mut);
}
static void stop_write(qr_data *data)
{
LightLock_Lock(&data->mut);
data->writer_active = false;
CondVar_Broadcast(&data->cond);
LightLock_Unlock(&data->mut);
} }
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 events[3] = {0};
events[0] = data->cancel;
u32 transferUnit;
u16 *buffer = calloc(1, 400 * 240 * sizeof(u16)); Handle cam_events[3] = {0};
cam_events[0] = data->event_stop;
u32 transferUnit;
const u32 bufsz = 400 * 240 * sizeof(u16);
u16 *buffer = linearAlloc(bufsz);
camInit(); camInit();
CAMU_SetSize(SELECT_OUT1, SIZE_CTR_TOP_LCD, CONTEXT_A); CAMU_SetSize(SELECT_OUT1, SIZE_CTR_TOP_LCD, CONTEXT_A);
CAMU_SetOutputFormat(SELECT_OUT1, OUTPUT_RGB_565, CONTEXT_A); CAMU_SetOutputFormat(SELECT_OUT1, OUTPUT_RGB_565, CONTEXT_A);
@@ -66,38 +101,42 @@ void capture_cam_thread(void *arg)
CAMU_SetAutoExposure(SELECT_OUT1, true); CAMU_SetAutoExposure(SELECT_OUT1, true);
CAMU_SetAutoWhiteBalance(SELECT_OUT1, true); CAMU_SetAutoWhiteBalance(SELECT_OUT1, true);
CAMU_Activate(SELECT_OUT1); CAMU_Activate(SELECT_OUT1);
CAMU_GetBufferErrorInterruptEvent(&events[2], PORT_CAM1); CAMU_GetBufferErrorInterruptEvent(&cam_events[2], PORT_CAM1);
CAMU_SetTrimming(PORT_CAM1, false); CAMU_SetTrimming(PORT_CAM1, false);
CAMU_GetMaxBytes(&transferUnit, 400, 240); CAMU_GetMaxBytes(&transferUnit, 400, 240);
CAMU_SetTransferBytes(PORT_CAM1, transferUnit, 400, 240); CAMU_SetTransferBytes(PORT_CAM1, transferUnit, 400, 240);
CAMU_ClearBuffer(PORT_CAM1); CAMU_ClearBuffer(PORT_CAM1);
CAMU_SetReceiving(&events[1], buffer, PORT_CAM1, 400 * 240 * sizeof(u16), (s16) transferUnit); CAMU_SetReceiving(&cam_events[1], buffer, PORT_CAM1, bufsz, transferUnit);
CAMU_StartCapture(PORT_CAM1); CAMU_StartCapture(PORT_CAM1);
svcSignalEvent(data->started);
bool cancel = false; bool cancel = false;
while (!cancel)
while (!cancel)
{ {
s32 index = 0; s32 index = 0;
svcWaitSynchronizationN(&index, events, 3, false, U64_MAX); svcWaitSynchronizationN(&index, cam_events, 3, false, U64_MAX);
switch(index) { switch(index) {
case 0: case 0:
DEBUG("Cancel event received\n"); DEBUG("Cancel event received\n");
cancel = true; cancel = true;
break; break;
case 1: case 1:
svcCloseHandle(events[1]); svcCloseHandle(cam_events[1]);
events[1] = 0; cam_events[1] = 0;
svcWaitSynchronization(data->mutex, U64_MAX);
memcpy(data->camera_buffer, buffer, 400 * 240 * sizeof(u16)); start_write(data);
GSPGPU_FlushDataCache(data->camera_buffer, 400 * 240 * sizeof(u16)); memcpy(data->camera_buffer, buffer, bufsz);
svcReleaseMutex(data->mutex); data->any_update = true;
CAMU_SetReceiving(&events[1], buffer, PORT_CAM1, 400 * 240 * sizeof(u16), transferUnit); stop_write(data);
CAMU_SetReceiving(&cam_events[1], buffer, PORT_CAM1, bufsz, transferUnit);
break; break;
case 2: case 2:
svcCloseHandle(events[1]); svcCloseHandle(cam_events[1]);
events[1] = 0; cam_events[1] = 0;
CAMU_ClearBuffer(PORT_CAM1); CAMU_ClearBuffer(PORT_CAM1);
CAMU_SetReceiving(&events[1], buffer, PORT_CAM1, 400 * 240 * sizeof(u16), transferUnit); CAMU_SetReceiving(&cam_events[1], buffer, PORT_CAM1, bufsz, transferUnit);
CAMU_StartCapture(PORT_CAM1); CAMU_StartCapture(PORT_CAM1);
break; break;
default: default:
@@ -115,221 +154,261 @@ void capture_cam_thread(void *arg)
CAMU_ClearBuffer(PORT_CAM1); CAMU_ClearBuffer(PORT_CAM1);
CAMU_Activate(SELECT_NONE); CAMU_Activate(SELECT_NONE);
camExit(); camExit();
free(buffer);
for(int i = 1; i < 3; i++) { linearFree(buffer);
if(events[i] != 0) { for(int i = 1; i < 3; i++)
svcCloseHandle(events[i]); {
events[i] = 0; if(cam_events[i] != 0) {
svcCloseHandle(cam_events[i]);
cam_events[i] = 0;
} }
} }
svcCloseHandle(data->mutex);
data->finished = true; LightEvent_Signal(&data->event_cam_info);
} }
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;
while (!data->finished) static const Tex3DS_SubTexture subt3x = { 400, 240, 0.0f, 1.0f, 400.0f/512.0f, 1.0f - (240.0f/256.0f) };
C3D_TexInit(&tex, 512, 256, GPU_RGB565);
C3D_TexSetFilter(&tex, GPU_LINEAR, GPU_LINEAR);
while(svcWaitSynchronization(data->event_stop, 2 * 1000 * 1000ULL) == 0x09401BFE) // timeout of 2ms occured, still have 14 for copy and render
{ {
draw_base_interface(); draw_base_interface();
// Untiled texture loading code adapted from FBI // Untiled texture loading code adapted from FBI
svcWaitSynchronization(data->mutex, U64_MAX); start_read(data);
for(u32 x = 0; x < 400 && !data->finished; x++) { if(data->any_update)
for(u32 y = 0; y < 256 && !data->finished; y++) {
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))) * sizeof(u16);
u32 srcPos = (y * 400 + x) * sizeof(u16);
memcpy(&((u8*) data->image.tex->data)[dstPos], &((u8*) data->camera_buffer)[srcPos], sizeof(u16));
}
}
svcReleaseMutex(data->mutex);
if (data->finished)
{ {
end_frame(); for(u32 y = 0; y < 240; y++) {
break; const u32 srcPos = y * 400;
} 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)));
C2D_DrawImageAt(data->image, 0.0f, 0.0f, 0.4f, NULL, 1.0f, 1.0f); ((u16*)tex.data)[dstPos] = data->camera_buffer[srcPos + x];
}
}
data->any_update = false;
}
stop_read(data);
C2D_DrawImageAt((C2D_Image){ &tex, &subt3x }, 0.0f, 0.0f, 0.4f, NULL, 1.0f, 1.0f);
set_screen(bottom); set_screen(bottom);
draw_text_center(GFX_BOTTOM, 4, 0.5, 0.5, 0.5, colors[COLOR_WHITE], "Press \uE005 To Quit"); draw_text_center(GFX_BOTTOM, 4, 0.5, 0.5, 0.5, colors[COLOR_WHITE], "Press \uE005 To Quit");
end_frame(); end_frame();
} }
data->closed = true;
C3D_TexDelete(&tex);
LightEvent_Signal(&data->event_ui_info);
} }
bool start_capture_cam(qr_data *data) static bool start_capture_cam(qr_data *data)
{ {
data->mutex = 0; if((data->cam_thread = threadCreate(capture_cam_thread, data, 0x10000, 0x1A, 1, false)) == NULL)
data->cancel = 0;
svcCreateEvent(&data->cancel, RESET_STICKY);
svcCreateMutex(&data->mutex, false);
if(threadCreate(capture_cam_thread, data, 0x10000, 0x1A, 1, true) == NULL)
{ {
throw_error("Capture cam thread creation failed\nPlease report this to the developers", ERROR_LEVEL_ERROR); throw_error("Capture cam thread creation failed\nPlease report this to the developers", ERROR_LEVEL_ERROR);
LightEvent_Signal(&data->event_cam_info);
LightEvent_Signal(&data->event_ui_info);
return false; return false;
} }
svcWaitSynchronization(data->started, U64_MAX); if((data->ui_thread = threadCreate(update_ui, data, 0x10000, 0x1A, 1, false)) == NULL)
if(threadCreate(update_ui, data, 0x10000, 0x1A, 1, true) == NULL)
{ {
exit_qr(data); LightEvent_Signal(&data->event_ui_info);
return false; return false;
} }
return true; return true;
} }
void update_qr(qr_data *data) static bool update_qr(qr_data *data, struct quirc_data* scan_data)
{ {
hidScanInput();
if (hidKeysDown() & (KEY_R | KEY_B | KEY_TOUCH)) {
exit_qr(data);
return;
}
if (!data->capturing) {
if(start_capture_cam(data))
data->capturing = true;
else {
exit_qr(data);
return;
}
}
if (data->finished) {
exit_qr(data);
return;
}
int w; int w;
int h; int h;
u8 *image = (u8*) quirc_begin(data->context, &w, &h); u8 *image = (u8*) quirc_begin(data->context, &w, &h);
for (ssize_t x = 0; x < w; x++) {
for (ssize_t y = 0; y < h; y++) { start_read(data);
u16 px = data->camera_buffer[y * 400 + x]; for (int y = 0; y < h; y++) {
image[y * w + x] = (u8)(((((px >> 11) & 0x1F) << 3) + (((px >> 5) & 0x3F) << 2) + ((px & 0x1F) << 3)) / 3); const int actual_y = y * w;
for (int x = 0; x < w; x++) {
const int actual_off = actual_y + x;
const u16 px = data->camera_buffer[actual_off];
image[actual_off] = (u8)(((((px >> 11) & 0x1F) << 3) + (((px >> 5) & 0x3F) << 2) + ((px & 0x1F) << 3)) / 3);
} }
} }
stop_read(data);
quirc_end(data->context); quirc_end(data->context);
if(quirc_count(data->context) > 0) if(quirc_count(data->context) > 0)
{ {
struct quirc_code code; struct quirc_code code;
struct quirc_data scan_data;
quirc_extract(data->context, 0, &code); quirc_extract(data->context, 0, &code);
if (!quirc_decode(&code, &scan_data)) if (!quirc_decode(&code, scan_data))
{ {
exit_qr(data); return true;
}
while (!data->finished) svcSleepThread(1000000);
draw_install(INSTALL_DOWNLOAD);
char * zip_buf = NULL;
char * filename = NULL;
u32 zip_size = http_get((char*)scan_data.payload, &filename, &zip_buf, INSTALL_DOWNLOAD, "application/zip");
if(zip_size != 0)
{
draw_install(INSTALL_CHECKING_DOWNLOAD);
struct archive *a = archive_read_new();
archive_read_support_format_zip(a);
int r = archive_read_open_memory(a, zip_buf, zip_size);
archive_read_free(a);
if(r == ARCHIVE_OK)
{
EntryMode mode = MODE_AMOUNT;
char * buf = NULL;
do {
if(zip_memory_to_buf("body_LZ.bin", zip_buf, zip_size, &buf) != 0)
{
mode = MODE_THEMES;
break;
}
free(buf);
buf = NULL;
if(zip_memory_to_buf("splash.bin", zip_buf, zip_size, &buf) != 0)
{
mode = MODE_SPLASHES;
break;
}
free(buf);
buf = NULL;
if(zip_memory_to_buf("splashbottom.bin", zip_buf, zip_size, &buf) != 0)
{
mode = MODE_SPLASHES;
break;
}
}
while(false);
free(buf);
buf = NULL;
if(mode != MODE_AMOUNT)
{
char path_to_file[0x107] = {0};
sprintf(path_to_file, "%s%s", main_paths[mode], filename);
char * extension = strrchr(path_to_file, '.');
if (extension == NULL || strcmp(extension, ".zip"))
strcat(path_to_file, ".zip");
remake_file(fsMakePath(PATH_ASCII, path_to_file), ArchiveSD, zip_size);
buf_to_file(zip_size, fsMakePath(PATH_ASCII, path_to_file), ArchiveSD, zip_buf);
data->success = true;
}
else
{
// TODO: handle more elegantly
throw_error("Zip downloaded is neither\na splash nor a theme.", ERROR_LEVEL_WARNING);
}
}
else
{
// TODO: marked for deletion
throw_error("File downloaded isn't a zip.", ERROR_LEVEL_WARNING);
}
free(zip_buf);
}
free(filename);
}
} }
return false;
}
static void start_qr(qr_data *data)
{
svcCreateEvent(&data->event_stop, RESET_STICKY);
LightEvent_Init(&data->event_cam_info, RESET_STICKY);
LightEvent_Init(&data->event_ui_info, RESET_STICKY);
LightLock_Init(&data->mut);
CondVar_Init(&data->cond);
data->cam_thread = NULL;
data->ui_thread = NULL;
data->any_update = false;
data->context = quirc_new();
quirc_resize(data->context, 400, 240);
data->camera_buffer = calloc(1, 400 * 240 * sizeof(u16));
}
static void exit_qr(qr_data *data)
{
svcSignalEvent(data->event_stop);
LightEvent_Wait(&data->event_ui_info);
LightEvent_Clear(&data->event_ui_info);
if(data->ui_thread != NULL)
{
threadJoin(data->ui_thread, U64_MAX);
threadFree(data->ui_thread);
data->ui_thread = NULL;
}
LightEvent_Wait(&data->event_cam_info);
LightEvent_Clear(&data->event_cam_info);
if(data->cam_thread != NULL)
{
threadJoin(data->cam_thread, U64_MAX);
threadFree(data->cam_thread);
data->cam_thread = NULL;
}
free(data->camera_buffer);
data->camera_buffer = NULL;
svcCloseHandle(data->event_stop);
data->event_stop = 0;
} }
bool init_qr(void) bool init_qr(void)
{ {
qr_data *data = calloc(1, sizeof(qr_data)); qr_data data;
data->capturing = false;
data->finished = false;
data->closed = false;
svcCreateEvent(&data->started, RESET_STICKY);
data->context = quirc_new();
quirc_resize(data->context, 400, 240);
data->camera_buffer = calloc(1, 400 * 240 * sizeof(u16)); memset(&data, 0, sizeof(data));
data->tex = (C3D_Tex*)malloc(sizeof(C3D_Tex)); start_qr(&data);
static const Tex3DS_SubTexture subt3x = { 512, 256, 0.0f, 1.0f, 1.0f, 0.0f };
data->image = (C2D_Image){ data->tex, &subt3x };
C3D_TexInit(data->image.tex, 512, 256, GPU_RGB565);
C3D_TexSetFilter(data->image.tex, GPU_LINEAR, GPU_LINEAR);
while (!data->finished) update_qr(data); struct quirc_data* scan_data = calloc(1, sizeof(struct quirc_data));
bool success = data->success; const bool ready = start_capture_cam(&data);
while (!data->closed) svcSleepThread(1000000); bool finished = !ready;
svcCloseHandle(data->started);
free(data);
while(!finished)
{
hidScanInput();
if (hidKeysDown() & (KEY_R | KEY_B | KEY_TOUCH))
{
break;
}
finished = update_qr(&data, scan_data);
svcSleepThread(50 * 1000 * 1000ULL); // only scan every 50ms
}
exit_qr(&data);
bool success = false;
if(finished && ready)
{
draw_install(INSTALL_DOWNLOAD);
char * zip_buf = NULL;
char * filename = NULL;
u32 zip_size = http_get((char*)scan_data->payload, &filename, &zip_buf, INSTALL_DOWNLOAD);
if(zip_size != 0)
{
draw_install(INSTALL_CHECKING_DOWNLOAD);
struct archive *a = archive_read_new();
archive_read_support_format_zip(a);
int r = archive_read_open_memory(a, zip_buf, zip_size);
archive_read_free(a);
if(r == ARCHIVE_OK)
{
EntryMode mode = MODE_AMOUNT;
char * buf = NULL;
do {
if(zip_memory_to_buf("body_LZ.bin", zip_buf, zip_size, &buf) != 0)
{
mode = MODE_THEMES;
break;
}
free(buf);
buf = NULL;
if(zip_memory_to_buf("splash.bin", zip_buf, zip_size, &buf) != 0)
{
mode = MODE_SPLASHES;
break;
}
free(buf);
buf = NULL;
if(zip_memory_to_buf("splashbottom.bin", zip_buf, zip_size, &buf) != 0)
{
mode = MODE_SPLASHES;
break;
}
}
while(false);
free(buf);
buf = NULL;
if(mode != MODE_AMOUNT)
{
char path_to_file[0x107] = {0};
sprintf(path_to_file, "%s%s", main_paths[mode], filename);
char * extension = strrchr(path_to_file, '.');
if (extension == NULL || strcmp(extension, ".zip"))
strcat(path_to_file, ".zip");
remake_file(fsMakePath(PATH_ASCII, path_to_file), ArchiveSD, zip_size);
buf_to_file(zip_size, fsMakePath(PATH_ASCII, path_to_file), ArchiveSD, zip_buf);
success = true;
}
else
{
throw_error("Zip downloaded is neither\na splash nor a theme.", ERROR_LEVEL_WARNING);
}
}
else
{
throw_error("File downloaded isn't a zip.", ERROR_LEVEL_WARNING);
}
free(zip_buf);
}
else
{
throw_error("Download failed.", ERROR_LEVEL_WARNING);
}
free(filename);
}
free(scan_data);
quirc_destroy(data.context);
return success; return success;
} }

View File

@@ -552,13 +552,18 @@ 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) if(!current_entry->placeholder_color)
{ {
*/
C2D_Image * image = list->icons[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)
{ {
@@ -713,19 +718,24 @@ void draw_interface(Entry_List_s* list, Instructions_s instructions)
C2D_DrawSpriteTinted(&sprite_installed, &tint); C2D_DrawSpriteTinted(&sprite_installed, &tint);
} }
// no icons not allowed anymore
/*
if(!current_entry->placeholder_color) if(!current_entry->placeholder_color)
{ {
*/
C2D_Image * image = NULL; C2D_Image * image = NULL;
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)]; image = list->icons[ICONS_VISIBLE*list->entries_loaded + (i - list->scroll)];
else else
image = list->icons[i]; 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);
} }
*/
} }
char entries_count_str[0x20] = {0}; char entries_count_str[0x20] = {0};

View File

@@ -83,6 +83,7 @@ C2D_Image * loadTextureIcon(Icon_s *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)
{ {
/*
if(icon == NULL) if(icon == NULL)
{ {
memcpy(entry->name, fallback_name, 0x80); memcpy(entry->name, fallback_name, 0x80);
@@ -91,28 +92,13 @@ void parse_smdh(Icon_s *icon, Entry_s * entry, const u16 * fallback_name)
entry->placeholder_color = C2D_Color32(rand() % 255, rand() % 255, rand() % 255, 255); entry->placeholder_color = C2D_Color32(rand() % 255, rand() % 255, rand() % 255, 255);
return; return;
} }
*/
memcpy(entry->name, icon->name, 0x40*sizeof(u16)); memcpy(entry->name, icon->name, 0x40*sizeof(u16));
memcpy(entry->desc, icon->desc, 0x80*sizeof(u16)); memcpy(entry->desc, icon->desc, 0x80*sizeof(u16));
memcpy(entry->author, icon->author, 0x40*sizeof(u16)); memcpy(entry->author, icon->author, 0x40*sizeof(u16));
} }
static void parse_entry_smdh(Entry_s * entry, const u16 * fallback_name)
{
char *info_buffer = NULL;
u64 size = load_data("/info.smdh", *entry, &info_buffer);
if(!size)
{
free(info_buffer);
info_buffer = NULL;
}
Icon_s * smdh = (Icon_s *)info_buffer;
parse_smdh(smdh, entry, fallback_name);
}
static C2D_Image * load_entry_icon(Entry_s entry) static C2D_Image * load_entry_icon(Entry_s entry)
{ {
char *info_buffer = NULL; char *info_buffer = NULL;
@@ -120,7 +106,9 @@ static C2D_Image * load_entry_icon(Entry_s entry)
if(!size) return NULL; if(!size) return NULL;
Icon_s * smdh = (Icon_s *)info_buffer; Icon_s * smdh = (Icon_s *)info_buffer;
return loadTextureIcon(smdh); C2D_Image* out = loadTextureIcon(smdh);
free(info_buffer);
return out;
} }
typedef int (*sort_comparator)(const void *, const void *); typedef int (*sort_comparator)(const void *, const void *);
@@ -202,20 +190,21 @@ Result load_entries(const char * loading_path, Entry_List_s * list)
} }
else else
{ {
const ssize_t len = strulen(path, 0x106);
struacat(path, "/info.smdh"); struacat(path, "/info.smdh");
u32 size = file_to_buf(fsMakePath(PATH_UTF16, path), ArchiveSD, &buf); u32 size = file_to_buf(fsMakePath(PATH_UTF16, path), ArchiveSD, &buf);
if (size == 0) continue; if (size == 0) continue;
memset(&path[len], 0, (0x106 - len) * sizeof(u16));
} }
free(buf);
list->entries_count++; list->entries_count++;
Entry_s * new_list = realloc(list->entries, list->entries_count * sizeof(Entry_s)); Entry_s * new_list = realloc(list->entries, list->entries_count * sizeof(Entry_s));
if(new_list == NULL) if(new_list == NULL)
{ {
free(list->entries); // out of memory: still allow use of currently loaded entries.
list->entries = NULL; // Many things might die, depending on the heap layout after
res = -1; list->entries_count--;
DEBUG("break\n"); free(buf);
break; break;
} }
else else
@@ -223,12 +212,11 @@ Result load_entries(const char * loading_path, Entry_List_s * list)
Entry_s * current_entry = &(list->entries[list->entries_count-1]); Entry_s * current_entry = &(list->entries[list->entries_count-1]);
memset(current_entry, 0, sizeof(Entry_s)); memset(current_entry, 0, sizeof(Entry_s));
parse_smdh((Icon_s *)buf, current_entry, dir_entry.name);
free(buf);
struacat(current_entry->path, loading_path); memcpy(current_entry->path, path, 0x106 * sizeof(u16));
strucat(current_entry->path, dir_entry.name);
current_entry->is_zip = !strcmp(dir_entry.shortExt, "ZIP"); current_entry->is_zip = !strcmp(dir_entry.shortExt, "ZIP");
parse_entry_smdh(current_entry, dir_entry.name);
} }
FSDIR_Close(dir_handle); FSDIR_Close(dir_handle);

View File

@@ -359,7 +359,6 @@ int main(void)
bool preview_mode = false; bool preview_mode = false;
int preview_offset = 0; int preview_offset = 0;
bool qr_mode = false;
bool install_mode = false; bool install_mode = false;
bool extra_mode = false; bool extra_mode = false;
C2D_Image preview = {0}; C2D_Image preview = {0};
@@ -407,7 +406,6 @@ int main(void)
instructions = extra_instructions[index]; instructions = extra_instructions[index];
} }
if(qr_mode) take_picture();
else if(preview_mode) else if(preview_mode)
{ {
draw_preview(preview, preview_offset); draw_preview(preview, preview_offset);
@@ -440,14 +438,14 @@ int main(void)
if(!install_mode && !extra_mode) if(!install_mode && !extra_mode)
{ {
if(!preview_mode && !qr_mode && kDown & KEY_L) //toggle between splashes and themes if(!preview_mode && kDown & KEY_L) //toggle between splashes and themes
{ {
switch_mode: switch_mode:
current_mode++; current_mode++;
current_mode %= MODE_AMOUNT; current_mode %= MODE_AMOUNT;
continue; continue;
} }
else if(!qr_mode && !preview_mode && kDown & KEY_R) //toggle QR mode else if(!preview_mode && kDown & KEY_R) //toggle QR mode
{ {
enable_qr: enable_qr:
draw_base_interface(); draw_base_interface();
@@ -480,7 +478,7 @@ int main(void)
continue; continue;
} }
else if(!qr_mode && kDown & KEY_Y && current_list->entries != NULL) //toggle preview mode else if(kDown & KEY_Y && current_list->entries != NULL) //toggle preview mode
{ {
toggle_preview: toggle_preview:
if(!preview_mode) if(!preview_mode)
@@ -525,7 +523,7 @@ int main(void)
} }
} }
if(qr_mode || preview_mode || current_list->entries == NULL) if(preview_mode || current_list->entries == NULL)
goto touch; goto touch;
int selected_entry = current_list->selected_entry; int selected_entry = current_list->selected_entry;