mirror of
https://github.com/FIX94/hbc.git
synced 2024-12-01 23:14:20 +01:00
1010 lines
20 KiB
C
1010 lines
20 KiB
C
|
#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) {
|
||
|
char caption[MAXPATHLEN + 32];
|
||
|
char filename[MAXPATHLEN];
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
|