hbc-FIX94/channel/channelapp/source/loader.c

1010 lines
20 KiB
C
Raw Normal View History

2016-11-23 06:35:12 +01:00
#include <sys/errno.h>
#include <malloc.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <ogcsys.h>
#include <network.h>
#include <ogc/lwp_watchdog.h>
#include <ogc/cond.h>
#include <zlib.h>
#include "../config.h"
#include "blob.h"
#include "tcp.h"
#include "appentry.h"
#include "manage.h"
#include "dialogs.h"
#include "i18n.h"
#include "panic.h"
#include "loader.h"
#define USBGECKO_RETRIES 1000
static const char *text_err_read;
static const char *text_err_receive;
static const char *text_err_uncompress;
static const char *text_err_invalid_bin;
static const char *text_extract_zip;
static const char *text_err_invalid_zip;
static const char *text_err_extract_zip;
static const char *text_warn_overwrite;
static const char *text_err_oom;
static inline u16 buf_u16(const u8 *buf, u8 pos) {
return (buf[pos] << 8) | buf[pos + 1];
}
static inline u32 buf_u32(const u8 *buf, u8 pos) {
return (buf[pos] << 24) | (buf[pos + 1] << 16) |
(buf[pos + 2] << 8) | buf[pos + 3];
}
// gecko handshake thread
static lwp_t ld_gecko_thread;
static u8 ld_gecko_stack[LD_THREAD_STACKSIZE] ATTRIBUTE_ALIGN (32);
typedef enum {
LDGECKOCMD_IDLE = 0,
LDGECKOCMD_EXIT,
LDGECKOCMD_POLL,
} ld_gecko_cmd;
typedef struct {
bool running;
bool handshaked;
ld_gecko_cmd cmd;
u32 data_len;
u32 data_len_un;
u16 args_len;
mutex_t cmutex;
cond_t cond;
} ld_gecko_arg;
static void * ld_gecko_func (void *arg) {
ld_gecko_arg *ta = (ld_gecko_arg *) arg;
u8 buf[16];
u8 left;
int res;
s64 t;
u16 wiiload_version;
ta->running = true;
LWP_MutexLock (ta->cmutex);
while (true) {
usb_flush(USBGECKO_CHANNEL);
while (ta->cmd == LDGECKOCMD_IDLE)
LWP_CondWait(ta->cond, ta->cmutex);
if (ta->cmd == LDGECKOCMD_EXIT)
break;
ta->cmd = LDGECKOCMD_IDLE;
ta->handshaked = false;
left = 16;
t = gettime ();
while (left) {
res = usb_recvbuffer_safe_ex(USBGECKO_CHANNEL, &buf[16 - left],
left, USBGECKO_RETRIES);
if (res) {
left -= res;
continue;
}
if (ticks_to_millisecs (diff_ticks (t, gettime ())) > LD_TIMEOUT)
break;
}
if (left)
continue;
wiiload_version = buf_u16(buf, 4);
ta->args_len = buf_u16(buf, 6);
ta->data_len = buf_u32(buf, 8);
ta->data_len_un = buf_u32(buf, 12);
if (strncmp((char *) buf, "HAXX", 4) ||
(wiiload_version < WIILOAD_MIN_VERSION) ||
(ta->args_len > ARGS_MAX_LEN) ||
(!ta->data_len || ta->data_len > LD_MAX_SIZE) ||
(ta->data_len_un > LD_MAX_SIZE)) {
continue;
}
gprintf_enable(0);
ta->handshaked = true;
}
LWP_MutexUnlock (ta->cmutex);
gprintf("gecko handshake thread done\n");
ta->running = false;
return NULL;
}
// tcp handshake thread
static lwp_t ld_tcp_thread;
static u8 ld_tcp_stack[LD_THREAD_STACKSIZE] ATTRIBUTE_ALIGN (32);
typedef enum {
LDTCPCMD_IDLE = 0,
LDTCPCMD_EXIT,
LDTCPCMD_INIT,
LDTCPCMD_ACCEPT
} ld_tcp_cmd;
typedef enum {
LDTCPS_UNINITIALIZED = 0,
LDTCPS_INITIALIZING,
LDTCPS_INITIALIZED
} ld_tcp_state;
typedef struct {
bool running;
ld_tcp_cmd cmd;
ld_tcp_state state;
bool handshaked;
u16 args_len;
u32 data_len;
u32 data_len_un;
int s;
char *client;
mutex_t cmutex;
cond_t cond;
} ld_tcp_arg;
static void * ld_tcp_func (void *arg) {
ld_tcp_arg *ta = (ld_tcp_arg *) arg;
int s = -1, sn;
struct sockaddr_in sa;
u32 mask, len_sa;
s32 res;
u8 buf[16];
u16 wiiload_version;
u8 retries;
mask = 0;
ta->running = true;
LWP_MutexLock (ta->cmutex);
ta->cmd = LDTCPCMD_INIT;
ta->state = LDTCPS_UNINITIALIZED;
ta->handshaked = false;
while (true) {
while (ta->cmd == LDTCPCMD_IDLE)
LWP_CondWait(ta->cond, ta->cmutex);
ta->handshaked = false;
if (ta->cmd == LDTCPCMD_EXIT) {
break;
}
if (ta->cmd == LDTCPCMD_INIT) {
ta->cmd = LDTCPCMD_IDLE;
if (ta->state == LDTCPS_INITIALIZED) {
gprintf ("reinit net\n");
net_close (s);
}
retries = 32;
ta->state = LDTCPS_INITIALIZING;
while (retries) {
res = net_init_async(NULL, NULL);
if (res) {
gprintf("net_init_async failed: %ld\n", res);
break;
}
res = net_get_status();
while (res == -EBUSY) {
if (ta->cmd == LDTCPCMD_EXIT) {
gprintf("exit while net_init_async still busy\n");
res = -1;
break;
}
usleep(50 * 1000);
res = net_get_status();
}
if ((res == -EAGAIN) || (res == -ETIMEDOUT)) {
gprintf ("net_init failed: %ld, trying again...\n", res);
retries--;
usleep(50 * 1000);
continue;
}
break;
}
if (res < 0) {
gprintf ("net_init failed: %ld\n", res);
ta->state = LDTCPS_UNINITIALIZED;
continue;
}
gprintf ("net_init success: %ld\n", res);
mask = net_gethostip () & 0xffff0000;
s = tcp_listen (LD_TCP_PORT, 3);
if ((s == -ENETRESET) && retries) {
gprintf("ENETRESET, reiniting\n");
net_deinit();
ta->cmd = LDTCPCMD_INIT;
continue;
}
if (s < 0) {
gprintf ("tcp_listen failed: %d\n",s);
ta->state = LDTCPS_UNINITIALIZED;
continue;
}
ta->state = LDTCPS_INITIALIZED;
continue;
}
if (ta->cmd == LDTCPCMD_ACCEPT) {
ta->cmd = LDTCPCMD_IDLE;
memset (&sa, 0, sizeof (struct sockaddr_in));
sa.sin_family = AF_INET;
sa.sin_len = sizeof (struct sockaddr_in);
len_sa = sizeof (struct sockaddr_in);
sn = net_accept (s, (struct sockaddr *) &sa, &len_sa);
if (sn == -EAGAIN)
continue;
if (sn == -ENETRESET) {
gprintf("ENETRESET, reiniting\n");
net_deinit();
ta->cmd = LDTCPCMD_INIT;
continue;
}
if (sn < 0) {
gprintf ("net_accept failed: %d\n", sn);
net_close (s);
ta->state = LDTCPS_UNINITIALIZED;
continue;
}
if ((sa.sin_addr.s_addr & 0xffff0000) != mask) {
gprintf ("non local ip (%lx)\n", sa.sin_addr.s_addr);
net_close (sn);
continue;
}
if (!tcp_read (sn, buf, 16, NULL, NULL)) {
net_close (sn);
continue;
}
wiiload_version = buf_u16(buf, 4);
ta->args_len = buf_u16(buf, 6);
ta->data_len = buf_u32(buf, 8);
ta->data_len_un = buf_u32(buf, 12);
if (strncmp((char *) buf, "HAXX", 4) ||
(wiiload_version < WIILOAD_MIN_VERSION) ||
(ta->args_len > ARGS_MAX_LEN) ||
(!ta->data_len || ta->data_len > LD_MAX_SIZE) ||
(ta->data_len_un > LD_MAX_SIZE)) {
gprintf ("invalid upload request\n");
net_close (sn);
continue;
}
ta->s = sn;
ta->client = inet_ntoa (sa.sin_addr);
ta->handshaked = true;
continue;
}
}
if (ta->state == LDTCPS_INITIALIZED) {
gprintf ("net_shutdown\n");
res = net_shutdown (s, 2);
if (res)
gprintf ("net_shutdown failed: %ld\n", res);
gprintf ("net_close\n");
res = net_close (s);
if (res)
gprintf ("net_close failed: %ld\n", res);
}
gprintf ("tcp thread deiniting\n");
net_deinit();
gprintf ("tcp thread exiting\n");
ta->state = LDTCPS_UNINITIALIZED;
ta->running = false;
LWP_MutexUnlock (ta->cmutex);
return NULL;
}
// thread setup
static ld_gecko_arg ta_gecko;
static ld_tcp_arg ta_tcp;
void loader_init (void) {
int res;
if (usb_isgeckoalive (USBGECKO_CHANNEL)) {
gprintf ("starting gecko thread\n");
memset (&ta_gecko, 0, sizeof (ld_gecko_arg));
memset (&ld_gecko_stack, 0, LD_THREAD_STACKSIZE);
res = LWP_MutexInit (&ta_gecko.cmutex, false);
if (res) {
gprintf ("error creating cmutex: %d\n", res);
return;
}
res = LWP_CondInit (&ta_gecko.cond);
if (res) {
gprintf ("error creating cond: %d\n", res);
return;
}
res = LWP_CreateThread (&ld_gecko_thread, ld_gecko_func, &ta_gecko,
ld_gecko_stack, LD_THREAD_STACKSIZE,
LD_THREAD_PRIO);
if (res) {
gprintf ("error creating thread: %d\n", res);
}
}
gprintf ("starting tcp thread\n");
memset (&ta_tcp, 0, sizeof (ld_tcp_arg));
memset (&ld_tcp_stack, 0, LD_THREAD_STACKSIZE);
res = LWP_MutexInit (&ta_tcp.cmutex, false);
if (res) {
gprintf ("error creating cmutex: %d\n", res);
return;
}
res = LWP_CondInit (&ta_tcp.cond);
if (res) {
gprintf ("error creating cond: %d\n", res);
return;
}
res = LWP_CreateThread (&ld_tcp_thread, ld_tcp_func, &ta_tcp, ld_tcp_stack,
LD_THREAD_STACKSIZE, LD_THREAD_PRIO);
if (res) {
gprintf ("error creating thread: %d\n", res);
}
}
void loader_deinit (void) {
u8 i;
// the tcp thread does stuff on exit; the ideal order is stopping it first
if (ta_tcp.running) {
gprintf ("stopping tcp thread\n");
for (i = 0; i < 25; ++i) {
if (LWP_MutexTryLock (ta_tcp.cmutex) == 0)
break;
usleep (20 * 1000);
}
if (i >= 25) {
gprintf("tcp thread didn't shutdown gracefully!\n");
} else {
gprintf ("sending tcp entry thread the exit cmd\n");
ta_tcp.cmd = LDTCPCMD_EXIT;
LWP_SetThreadPriority (ld_tcp_thread, LWP_PRIO_HIGHEST);
LWP_CondBroadcast (ta_tcp.cond);
LWP_MutexUnlock (ta_tcp.cmutex);
for (i = 0; i < 25; ++i) {
if (LWP_MutexTryLock (ta_tcp.cmutex) == 0) {
if (!ta_tcp.running)
break;
LWP_MutexUnlock (ta_tcp.cmutex);
}
usleep (20 * 1000);
}
if (i >= 25) {
gprintf("tcp thread didn't shutdown gracefully!\n");
} else {
LWP_MutexUnlock (ta_tcp.cmutex);
LWP_JoinThread(ld_tcp_thread, NULL);
LWP_CondDestroy (ta_tcp.cond);
LWP_MutexDestroy (ta_tcp.cmutex);
}
}
}
if (ta_gecko.running) {
gprintf ("stopping gecko thread\n");
for (i = 0; i < 25; ++i) {
if (LWP_MutexTryLock (ta_gecko.cmutex) == 0)
break;
usleep (20 * 1000);
}
if (i < 25) {
gprintf ("sending gecko entry thread the exit cmd\n");
ta_gecko.cmd = LDGECKOCMD_EXIT;
LWP_SetThreadPriority (ld_gecko_thread, LWP_PRIO_HIGHEST);
LWP_CondBroadcast (ta_gecko.cond);
LWP_MutexUnlock (ta_gecko.cmutex);
LWP_JoinThread(ld_gecko_thread, NULL);
LWP_CondDestroy (ta_gecko.cond);
LWP_MutexDestroy (ta_gecko.cmutex);
} else {
gprintf("gecko thread didn't shutdown gracefully!\n");
}
}
}
void loader_tcp_init (void) {
if (LWP_MutexTryLock(ta_tcp.cmutex) != 0)
return;
if (ta_tcp.running && ta_tcp.state == LDTCPS_UNINITIALIZED) {
ta_tcp.cmd = LDTCPCMD_INIT;
LWP_CondBroadcast(ta_tcp.cond);
}
LWP_MutexUnlock(ta_tcp.cmutex);
}
void loader_signal_threads (void) {
if (LWP_MutexTryLock(ta_gecko.cmutex) == 0) {
if (loader_gecko_initialized () && !ta_gecko.handshaked) {
ta_gecko.cmd = LDGECKOCMD_POLL;
LWP_CondBroadcast(ta_gecko.cond);
}
LWP_MutexUnlock(ta_gecko.cmutex);
}
if (LWP_MutexTryLock(ta_tcp.cmutex) == 0) {
if (loader_tcp_initialized () && !ta_tcp.handshaked) {
ta_tcp.cmd = LDTCPCMD_ACCEPT;
LWP_CondBroadcast(ta_tcp.cond);
}
LWP_MutexUnlock(ta_tcp.cmutex);
}
}
bool loader_gecko_initialized (void) {
return ta_gecko.running;
}
bool loader_tcp_initializing (void) {
return ta_tcp.running && ta_tcp.state == LDTCPS_INITIALIZING;
}
bool loader_tcp_initialized (void) {
return ta_tcp.running && ta_tcp.state == LDTCPS_INITIALIZED;
}
bool loader_handshaked (void) {
bool handshaked = false;
if (LWP_MutexTryLock(ta_gecko.cmutex) == 0) {
handshaked = handshaked || (ta_gecko.running && ta_gecko.handshaked);
LWP_MutexUnlock(ta_gecko.cmutex);
}
if (LWP_MutexTryLock(ta_tcp.cmutex) == 0) {
handshaked = handshaked || (ta_tcp.running && ta_tcp.handshaked);
LWP_MutexUnlock(ta_tcp.cmutex);
}
return handshaked;
}
// loading thread
static lwp_t ld_load_thread;
static u8 ld_load_stack[LD_THREAD_STACKSIZE] ATTRIBUTE_ALIGN (32);
typedef enum {
LDC_FILE = 0,
LDC_GECKO,
LDC_TCP
} ld_load_cmd;
typedef enum {
LDS_RUNNING = 0,
LDS_SUCCESS,
LDS_ERR_READ,
LDS_ERR_RECEIVE,
LDS_ERR_UNCOMPRESS
} ld_load_state;
typedef struct {
ld_load_cmd cmd;
u32 data_len;
u8 *data;
u32 data_len_un;
u8 *data_un;
u16 args_len;
int fd;
mutex_t mutex;
ld_load_state state;
u32 progress;
} ld_load_arg;
static void * ld_load_func (void *arg) {
ld_load_arg *ta = (ld_load_arg *) arg;
u8 *d;
u32 left, received;
s32 block;
int res;
s64 t;
d = ta->data;
received = 0;
left = ta->data_len + ta->args_len;
switch (ta->cmd) {
case LDC_FILE:
while (left) {
block = left;
if (block > 32 * 1024)
block = 32 * 1024;
block = read (ta->fd, d, block);
if (block < 0) {
gprintf ("read failed: %ld\n", block);
close (ta->fd);
LWP_MutexLock (ta->mutex);
ta->state = LDS_ERR_READ;
LWP_MutexUnlock (ta->mutex);
return NULL;
} else {
d += block;
received += block;
left -= block;
LWP_MutexLock (ta->mutex);
ta->progress = received;
LWP_MutexUnlock (ta->mutex);
}
}
close (ta->fd);
break;
case LDC_GECKO:
block = 0;
t = gettime ();
while (left) {
res = usb_recvbuffer_ex(USBGECKO_CHANNEL, d, left, USBGECKO_RETRIES);
if (res) {
d += res;
received += res;
left -= res;
block += res;
if (block >= 1024) {
block = 0;
LWP_MutexLock (ta->mutex);
ta->progress = received;
LWP_MutexUnlock (ta->mutex);
t = gettime ();
}
}
if (ticks_to_millisecs (diff_ticks (t, gettime ())) > LD_TIMEOUT) {
LWP_MutexLock (ta->mutex);
ta->state = LDS_ERR_RECEIVE;
LWP_MutexUnlock (ta->mutex);
gprintf_enable(1);
return NULL;
}
}
gprintf_enable(1);
break;
case LDC_TCP:
if (!tcp_read (ta->fd, ta->data, left, &ta->mutex, &ta->progress)) {
LWP_MutexLock (ta->mutex);
ta->state = LDS_ERR_RECEIVE;
LWP_MutexUnlock (ta->mutex);
net_close (ta->fd);
return NULL;
}
net_close (ta->fd);
break;
}
if (ta->data_un) {
int res;
uLongf len = ta->data_len_un;
res = uncompress(ta->data_un, &len, ta->data, ta->data_len);
if (res != Z_OK) {
gprintf("error uncompressing: %d\n", res);
LWP_MutexLock (ta->mutex);
ta->state = LDS_ERR_UNCOMPRESS;
LWP_MutexUnlock (ta->mutex);
return NULL;
}
if (len != ta->data_len_un) {
gprintf("short uncompress: %lu\n", (u32) len);
LWP_MutexLock (ta->mutex);
ta->state = LDS_ERR_UNCOMPRESS;
LWP_MutexUnlock (ta->mutex);
return NULL;
}
}
LWP_MutexLock (ta->mutex);
ta->state = LDS_SUCCESS;
LWP_MutexUnlock (ta->mutex);
return NULL;
}
// public loading function
void loader_load(loader_result *result, view *sub_view, app_entry *entry) {
2017-07-29 12:32:45 +02:00
char caption[PATH_MAX + 32];
char filename[PATH_MAX];
2016-11-23 06:35:12 +01:00
ld_load_arg ta;
s32 res;
bool running;
u32 progress;
view *v;
text_err_read = _("Error while reading the application from the SD card");
text_err_receive = _("Error while receiving the application");
text_err_uncompress = _("Error uncompressing the received data");
text_err_invalid_bin = _("This is not a valid Wii application");
text_err_invalid_zip = _("This is not a usable ZIP file");
text_err_oom = _("Not enough memory");
memset(result, 0, sizeof (loader_result));
memset(&ta, 0, sizeof (ld_load_arg));
memset(filename, 0, sizeof(filename));
bool gecko_handshaked = false;
bool tcp_handshaked = false;
if (LWP_MutexTryLock (ta_gecko.cmutex) == 0) {
gecko_handshaked = ta_gecko.handshaked;
ta_gecko.cmd = LDGECKOCMD_IDLE;
LWP_MutexUnlock (ta_gecko.cmutex);
}
if (LWP_MutexTryLock (ta_tcp.cmutex) == 0) {
tcp_handshaked = ta_tcp.handshaked;
ta_gecko.cmd = LDTCPCMD_IDLE;
LWP_MutexUnlock (ta_tcp.cmutex);
}
if (entry) {
char *name;
if (!app_entry_get_filename(filename, entry))
return;
gprintf ("loading %s\n", filename);
ta.cmd = LDC_FILE;
ta.fd = open (filename, O_RDONLY);
if (ta.fd < 0)
return;
ta.data_len = entry->size;
if (entry->meta && entry->meta->name)
name = entry->meta->name;
else
name = entry->dirname;
sprintf (caption, _("Loading %s"), name);
} else if (gecko_handshaked) {
ta.cmd = LDC_GECKO;
ta_gecko.handshaked = false;
ta.data_len = ta_gecko.data_len;
ta.data_len_un = ta_gecko.data_len_un;
ta.args_len = ta_gecko.args_len;
sprintf (caption, _("Receiving over USBGecko"));
} else if (tcp_handshaked) {
ta.cmd = LDC_TCP;
ta_tcp.handshaked = false;
ta.data_len = ta_tcp.data_len;
ta.data_len_un = ta_tcp.data_len_un;
ta.args_len = ta_tcp.args_len;
ta.fd = ta_tcp.s;
sprintf (caption, _("Receiving from %s"), ta_tcp.client);
} else {
return;
}
if (ta.data_len == ta.data_len_un)
ta.data_len_un = 0;
if (ta.data_len_un) {
ta.data_un = (u8 *) blob_alloc(ta.data_len_un);
if (!ta.data_un) {
if (ta.cmd == LDC_TCP)
net_close (ta.fd);
show_message (sub_view, DLGMT_ERROR, DLGB_OK, text_err_oom, 0);
return;
}
}
ta.data = (u8 *) blob_alloc(ta.data_len + ta.args_len);
if (!ta.data) {
blob_free(ta.data_un);
if (ta.cmd == LDC_TCP)
net_close (ta.fd);
show_message (sub_view, DLGMT_ERROR, DLGB_OK, text_err_oom, 0);
return;
}
res = LWP_MutexInit (&ta.mutex, false);
if (res) {
gprintf ("error creating mutex: %ld\n", res);
blob_free (ta.data);
blob_free (ta.data_un);
if (ta.cmd == LDC_TCP)
net_close (ta.fd);
panic(); // if this happens, let's find out
return;
}
memset (&ld_load_stack, 0, LD_THREAD_STACKSIZE);
res = LWP_CreateThread (&ld_load_thread, ld_load_func, &ta, ld_load_stack,
LD_THREAD_STACKSIZE, LD_THREAD_PRIO);
if (res) {
gprintf ("error creating thread: %ld\n", res);
blob_free (ta.data);
blob_free (ta.data_un);
if (ta.cmd == LDC_TCP)
net_close (ta.fd);
panic(); // if this happens, let's find out
return;
}
v = dialog_progress (sub_view, caption, ta.data_len + ta.args_len);
dialog_fade (v, true);
running = true;
while (running) {
LWP_MutexLock (ta.mutex);
running = ta.state == LDS_RUNNING;
progress = ta.progress;
LWP_MutexUnlock (ta.mutex);
dialog_set_progress (v, progress);
view_plot (v, DIALOG_MASK_COLOR, NULL, NULL, NULL);
}
dialog_fade (v, false);
view_free (v);
LWP_MutexDestroy (ta.mutex);
if (ta.data_len_un) {
if (ta.args_len) {
memcpy(result->args, &ta.data[ta.data_len], ta.args_len);
result->args_len = ta.args_len;
}
blob_free(ta.data);
ta.data = ta.data_un;
ta.data_len = ta.data_len_un;
ta.data_un = NULL;
ta.data_len_un = 0;
}
const char *text = NULL;
switch (ta.state) {
case LDS_RUNNING:
case LDS_SUCCESS:
break;
case LDS_ERR_READ:
text = text_err_read;
break;
case LDS_ERR_RECEIVE:
text = text_err_receive;
break;
case LDS_ERR_UNCOMPRESS:
text = text_err_uncompress;
break;
}
if (text) {
blob_free(ta.data);
show_message (sub_view, DLGMT_ERROR, DLGB_OK, text, 0);
return;
}
result->data = ta.data;
result->data_len = ta.data_len;
if (ta.cmd == LDC_FILE) {
switch(entry->type) {
case AET_BOOT_ELF:
case AET_BOOT_DOL:
break;
case AET_THEME:
result->type = LT_ZIP_THEME;
return;
}
result->type = LT_EXECUTABLE;
strcpy(result->args, filename);
result->args_len = strlen (result->args);
if (entry->meta && entry->meta->args) {
result->args_len++;
memcpy(result->args + result->args_len,
entry->meta->args, entry->meta->argslen);
result->args_len += entry->meta->argslen;
}
result->args[result->args_len + 1] = 0;
result->args_len += 2;
return;
}
if (manage_is_zip(ta.data)) {
gprintf("we got a .zip\n");
if (manage_check_zip_app(ta.data, ta.data_len, result->dirname,
&result->bytes))
result->type = LT_ZIP_APP;
else if (manage_check_zip_theme(ta.data, ta.data_len))
result->type = LT_ZIP_THEME;
if (result->type == LT_UNKNOWN) {
blob_free(ta.data);
ta.data = NULL;
ta.data_len = 0;
show_message (sub_view, DLGMT_ERROR, DLGB_OK,
text_err_invalid_zip, 0);
}
return;
}
result->type = LT_EXECUTABLE;
}
bool loader_load_executable(entry_point *ep, loader_result *result,
view *sub_view) {
bool res = loader_reloc(ep, result->data, result->data_len,
result->args, result->args_len, true);
if (!res)
show_message (sub_view, DLGMT_ERROR, DLGB_OK, text_err_invalid_bin, 0);
blob_free(result->data);
return res;
}
bool loader_handle_zip_app(loader_result *result, view *sub_view) {
char buf[1024];
char buf2[1024];
text_extract_zip = _("Extract the received ZIP file?\n%s of free space are required.");
text_warn_overwrite = _("WARNING: Files in '%s' will be overwritten");
text_err_extract_zip = _("Error while extracting the ZIP file");
if ((result->bytes / 1024u) > 999)
sprintf(buf2, "%.02f MB",
(float)(result->bytes) / (float)(1024 * 1024));
else
sprintf(buf2, "%lu KB", result->bytes / 1024u);
sprintf(buf, text_extract_zip, buf2);
if (!app_entry_get_path(buf2)) {
blob_free(result->data);
return false;
}
strcat(buf2, ":/");
strcat(buf2, result->dirname);
if (dir_exists(buf2)) {
strcat(buf, "\n\n");
sprintf(buf2, text_warn_overwrite, result->dirname);
strcat(buf, buf2);
}
if (show_message(sub_view, DLGMT_CONFIRM, DLGB_YESNO,
buf, 0) != 0) {
blob_free(result->data);
return false;
}
if (!manage_run(sub_view, result->dirname, result->data,
result->data_len, result->bytes)) {
show_message (sub_view, DLGMT_ERROR, DLGB_OK, text_err_extract_zip, 0);
blob_free(result->data);
return false;
}
blob_free(result->data);
return true;
}