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:
@@ -30,22 +30,24 @@
|
||||
#include "common.h"
|
||||
|
||||
typedef struct {
|
||||
u16 *camera_buffer;
|
||||
C2D_Image image;
|
||||
C3D_Tex *tex;
|
||||
Handle mutex;
|
||||
volatile bool finished;
|
||||
volatile bool closed;
|
||||
volatile bool success;
|
||||
Handle cancel;
|
||||
Handle started;
|
||||
u16* camera_buffer;
|
||||
|
||||
Handle event_stop;
|
||||
Thread cam_thread, ui_thread;
|
||||
|
||||
LightEvent event_cam_info, event_ui_info;
|
||||
|
||||
CondVar cond;
|
||||
LightLock mut;
|
||||
u32 num_readers_active;
|
||||
bool writer_waiting;
|
||||
bool writer_active;
|
||||
|
||||
bool any_update;
|
||||
|
||||
bool capturing;
|
||||
struct quirc* context;
|
||||
} qr_data;
|
||||
|
||||
bool init_qr(void);
|
||||
void exit_qr(qr_data *data);
|
||||
void take_picture(void);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -65,7 +65,7 @@ typedef struct {
|
||||
u16 desc[0x81];
|
||||
u16 author[0x41];
|
||||
|
||||
u32 placeholder_color;
|
||||
// u32 placeholder_color;
|
||||
|
||||
u16 path[0x106];
|
||||
bool is_zip;
|
||||
|
||||
465
source/camera.c
465
source/camera.c
@@ -36,28 +36,63 @@
|
||||
#include <archive.h>
|
||||
#include <archive_entry.h>
|
||||
|
||||
void exit_qr(qr_data *data)
|
||||
static void start_read(qr_data *data)
|
||||
{
|
||||
DEBUG("Exiting QR\n");
|
||||
svcSignalEvent(data->cancel);
|
||||
while(!data->finished)
|
||||
svcSleepThread(1000000);
|
||||
svcCloseHandle(data->cancel);
|
||||
data->capturing = false;
|
||||
LightLock_Lock(&data->mut);
|
||||
while(data->writer_waiting || data->writer_active)
|
||||
{
|
||||
CondVar_Wait(&data->cond, &data->mut);
|
||||
}
|
||||
|
||||
free(data->camera_buffer);
|
||||
free(data->tex);
|
||||
quirc_destroy(data->context);
|
||||
AtomicIncrement(&data->num_readers_active);
|
||||
LightLock_Unlock(&data->mut);
|
||||
}
|
||||
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;
|
||||
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();
|
||||
CAMU_SetSize(SELECT_OUT1, SIZE_CTR_TOP_LCD, 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_SetAutoWhiteBalance(SELECT_OUT1, true);
|
||||
CAMU_Activate(SELECT_OUT1);
|
||||
CAMU_GetBufferErrorInterruptEvent(&events[2], PORT_CAM1);
|
||||
CAMU_GetBufferErrorInterruptEvent(&cam_events[2], PORT_CAM1);
|
||||
CAMU_SetTrimming(PORT_CAM1, false);
|
||||
CAMU_GetMaxBytes(&transferUnit, 400, 240);
|
||||
CAMU_SetTransferBytes(PORT_CAM1, transferUnit, 400, 240);
|
||||
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);
|
||||
svcSignalEvent(data->started);
|
||||
|
||||
bool cancel = false;
|
||||
while (!cancel)
|
||||
|
||||
while (!cancel)
|
||||
{
|
||||
s32 index = 0;
|
||||
svcWaitSynchronizationN(&index, events, 3, false, U64_MAX);
|
||||
svcWaitSynchronizationN(&index, cam_events, 3, false, U64_MAX);
|
||||
switch(index) {
|
||||
case 0:
|
||||
DEBUG("Cancel event received\n");
|
||||
cancel = true;
|
||||
break;
|
||||
case 1:
|
||||
svcCloseHandle(events[1]);
|
||||
events[1] = 0;
|
||||
svcWaitSynchronization(data->mutex, U64_MAX);
|
||||
memcpy(data->camera_buffer, buffer, 400 * 240 * sizeof(u16));
|
||||
GSPGPU_FlushDataCache(data->camera_buffer, 400 * 240 * sizeof(u16));
|
||||
svcReleaseMutex(data->mutex);
|
||||
CAMU_SetReceiving(&events[1], buffer, PORT_CAM1, 400 * 240 * sizeof(u16), transferUnit);
|
||||
svcCloseHandle(cam_events[1]);
|
||||
cam_events[1] = 0;
|
||||
|
||||
start_write(data);
|
||||
memcpy(data->camera_buffer, buffer, bufsz);
|
||||
data->any_update = true;
|
||||
stop_write(data);
|
||||
|
||||
CAMU_SetReceiving(&cam_events[1], buffer, PORT_CAM1, bufsz, transferUnit);
|
||||
break;
|
||||
case 2:
|
||||
svcCloseHandle(events[1]);
|
||||
events[1] = 0;
|
||||
svcCloseHandle(cam_events[1]);
|
||||
cam_events[1] = 0;
|
||||
|
||||
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);
|
||||
break;
|
||||
default:
|
||||
@@ -115,221 +154,261 @@ void capture_cam_thread(void *arg)
|
||||
CAMU_ClearBuffer(PORT_CAM1);
|
||||
CAMU_Activate(SELECT_NONE);
|
||||
camExit();
|
||||
free(buffer);
|
||||
for(int i = 1; i < 3; i++) {
|
||||
if(events[i] != 0) {
|
||||
svcCloseHandle(events[i]);
|
||||
events[i] = 0;
|
||||
|
||||
linearFree(buffer);
|
||||
for(int i = 1; i < 3; i++)
|
||||
{
|
||||
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;
|
||||
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();
|
||||
|
||||
// Untiled texture loading code adapted from FBI
|
||||
svcWaitSynchronization(data->mutex, U64_MAX);
|
||||
for(u32 x = 0; x < 400 && !data->finished; x++) {
|
||||
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)
|
||||
start_read(data);
|
||||
if(data->any_update)
|
||||
{
|
||||
end_frame();
|
||||
break;
|
||||
}
|
||||
for(u32 y = 0; y < 240; y++) {
|
||||
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);
|
||||
draw_text_center(GFX_BOTTOM, 4, 0.5, 0.5, 0.5, colors[COLOR_WHITE], "Press \uE005 To Quit");
|
||||
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;
|
||||
data->cancel = 0;
|
||||
svcCreateEvent(&data->cancel, RESET_STICKY);
|
||||
svcCreateMutex(&data->mutex, false);
|
||||
if(threadCreate(capture_cam_thread, data, 0x10000, 0x1A, 1, true) == NULL)
|
||||
if((data->cam_thread = threadCreate(capture_cam_thread, data, 0x10000, 0x1A, 1, false)) == NULL)
|
||||
{
|
||||
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;
|
||||
}
|
||||
svcWaitSynchronization(data->started, U64_MAX);
|
||||
if(threadCreate(update_ui, data, 0x10000, 0x1A, 1, true) == NULL)
|
||||
if((data->ui_thread = threadCreate(update_ui, data, 0x10000, 0x1A, 1, false)) == NULL)
|
||||
{
|
||||
exit_qr(data);
|
||||
LightEvent_Signal(&data->event_ui_info);
|
||||
return false;
|
||||
}
|
||||
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 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++) {
|
||||
u16 px = data->camera_buffer[y * 400 + x];
|
||||
image[y * w + x] = (u8)(((((px >> 11) & 0x1F) << 3) + (((px >> 5) & 0x3F) << 2) + ((px & 0x1F) << 3)) / 3);
|
||||
|
||||
start_read(data);
|
||||
for (int y = 0; y < h; y++) {
|
||||
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);
|
||||
if(quirc_count(data->context) > 0)
|
||||
{
|
||||
struct quirc_code code;
|
||||
struct quirc_data scan_data;
|
||||
quirc_extract(data->context, 0, &code);
|
||||
if (!quirc_decode(&code, &scan_data))
|
||||
if (!quirc_decode(&code, scan_data))
|
||||
{
|
||||
exit_qr(data);
|
||||
|
||||
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 true;
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
qr_data *data = calloc(1, sizeof(qr_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);
|
||||
qr_data data;
|
||||
|
||||
data->camera_buffer = calloc(1, 400 * 240 * sizeof(u16));
|
||||
memset(&data, 0, sizeof(data));
|
||||
|
||||
data->tex = (C3D_Tex*)malloc(sizeof(C3D_Tex));
|
||||
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);
|
||||
start_qr(&data);
|
||||
|
||||
while (!data->finished) update_qr(data);
|
||||
bool success = data->success;
|
||||
while (!data->closed) svcSleepThread(1000000);
|
||||
svcCloseHandle(data->started);
|
||||
free(data);
|
||||
struct quirc_data* scan_data = calloc(1, sizeof(struct quirc_data));
|
||||
const bool ready = start_capture_cam(&data);
|
||||
bool finished = !ready;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -552,13 +552,18 @@ 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)
|
||||
{
|
||||
*/
|
||||
C2D_Image * image = list->icons[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)
|
||||
{
|
||||
@@ -713,19 +718,24 @@ void draw_interface(Entry_List_s* list, Instructions_s instructions)
|
||||
C2D_DrawSpriteTinted(&sprite_installed, &tint);
|
||||
}
|
||||
|
||||
// no icons not allowed anymore
|
||||
/*
|
||||
if(!current_entry->placeholder_color)
|
||||
{
|
||||
*/
|
||||
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)];
|
||||
else
|
||||
image = list->icons[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);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
char entries_count_str[0x20] = {0};
|
||||
|
||||
@@ -83,6 +83,7 @@ C2D_Image * loadTextureIcon(Icon_s *icon)
|
||||
|
||||
void parse_smdh(Icon_s *icon, Entry_s * entry, const u16 * fallback_name)
|
||||
{
|
||||
/*
|
||||
if(icon == NULL)
|
||||
{
|
||||
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);
|
||||
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));
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
char *info_buffer = NULL;
|
||||
@@ -120,7 +106,9 @@ static C2D_Image * load_entry_icon(Entry_s entry)
|
||||
if(!size) return NULL;
|
||||
|
||||
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 *);
|
||||
@@ -202,20 +190,21 @@ Result load_entries(const char * loading_path, Entry_List_s * list)
|
||||
}
|
||||
else
|
||||
{
|
||||
const ssize_t len = strulen(path, 0x106);
|
||||
struacat(path, "/info.smdh");
|
||||
u32 size = file_to_buf(fsMakePath(PATH_UTF16, path), ArchiveSD, &buf);
|
||||
if (size == 0) continue;
|
||||
memset(&path[len], 0, (0x106 - len) * sizeof(u16));
|
||||
}
|
||||
|
||||
free(buf);
|
||||
list->entries_count++;
|
||||
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;
|
||||
DEBUG("break\n");
|
||||
// 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
|
||||
@@ -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]);
|
||||
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);
|
||||
strucat(current_entry->path, dir_entry.name);
|
||||
|
||||
memcpy(current_entry->path, path, 0x106 * sizeof(u16));
|
||||
current_entry->is_zip = !strcmp(dir_entry.shortExt, "ZIP");
|
||||
parse_entry_smdh(current_entry, dir_entry.name);
|
||||
}
|
||||
|
||||
FSDIR_Close(dir_handle);
|
||||
|
||||
@@ -359,7 +359,6 @@ int main(void)
|
||||
bool preview_mode = false;
|
||||
int preview_offset = 0;
|
||||
|
||||
bool qr_mode = false;
|
||||
bool install_mode = false;
|
||||
bool extra_mode = false;
|
||||
C2D_Image preview = {0};
|
||||
@@ -407,7 +406,6 @@ int main(void)
|
||||
instructions = extra_instructions[index];
|
||||
}
|
||||
|
||||
if(qr_mode) take_picture();
|
||||
else if(preview_mode)
|
||||
{
|
||||
draw_preview(preview, preview_offset);
|
||||
@@ -440,14 +438,14 @@ int main(void)
|
||||
|
||||
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:
|
||||
current_mode++;
|
||||
current_mode %= MODE_AMOUNT;
|
||||
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:
|
||||
draw_base_interface();
|
||||
@@ -480,7 +478,7 @@ int main(void)
|
||||
|
||||
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:
|
||||
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;
|
||||
|
||||
int selected_entry = current_list->selected_entry;
|
||||
|
||||
Reference in New Issue
Block a user