Initial commit
This commit is contained in:
713
channel/channelapp/source/appentry.c
Normal file
713
channel/channelapp/source/appentry.c
Normal file
@@ -0,0 +1,713 @@
|
||||
#include <malloc.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <sys/dir.h>
|
||||
|
||||
#include <ogcsys.h>
|
||||
#include <ogc/cond.h>
|
||||
#include <sdcard/gcsd.h>
|
||||
#include <sdcard/wiisd_io.h>
|
||||
#include <ogc/usbstorage.h>
|
||||
#include <fat.h>
|
||||
|
||||
#include "../config.h"
|
||||
#include "tex.h"
|
||||
#include "panic.h"
|
||||
|
||||
#include "appentry.h"
|
||||
|
||||
typedef struct {
|
||||
const char *name;
|
||||
const DISC_INTERFACE *device;
|
||||
bool last_status;
|
||||
} device_t;
|
||||
|
||||
static app_entry *entries_all[MAX_ENTRIES];
|
||||
static u32 entry_count_all = 0;
|
||||
|
||||
app_entry *entries[MAX_ENTRIES];
|
||||
u32 entry_count = 0;
|
||||
|
||||
const char *app_path = "/apps";
|
||||
const char *app_fn_boot_elf = "boot.elf";
|
||||
const char *app_fn_boot_dol = "boot.dol";
|
||||
const char *app_fn_theme = "theme.zip";
|
||||
const char *app_fn_meta = "meta.xml";
|
||||
const char *app_fn_icon = "icon.png";
|
||||
|
||||
static device_t devices[DEVICE_COUNT] = {
|
||||
{ "sd", &__io_wiisd, false },
|
||||
{ "usb", &__io_usbstorage, false },
|
||||
{ "carda", &__io_gcsda, false },
|
||||
{ "cardb", &__io_gcsdb, false }
|
||||
};
|
||||
|
||||
static int device_active = -1;
|
||||
static int device_prefered = -1;
|
||||
static app_filter current_filter = APP_FILTER_ALL;
|
||||
static app_sort current_sort = APP_SORT_NAME;
|
||||
static bool cmp_descending = false;
|
||||
static bool cmp_release_date = false;
|
||||
|
||||
static int cmp_app_entry (const void *p1, const void *p2) {
|
||||
const app_entry *a1;
|
||||
const app_entry *a2;
|
||||
|
||||
if (cmp_descending) {
|
||||
a1 = *((const app_entry **) p2);
|
||||
a2 = *((const app_entry **) p1);
|
||||
} else {
|
||||
a1 = *((const app_entry **) p1);
|
||||
a2 = *((const app_entry **) p2);
|
||||
}
|
||||
|
||||
if (!a1->meta && !a2->meta)
|
||||
return stricmp (a1->dirname, a2->dirname);
|
||||
|
||||
if (!a1->meta && a2->meta)
|
||||
return 1;
|
||||
|
||||
if (a1->meta && !a2->meta)
|
||||
return -1;
|
||||
|
||||
if (cmp_release_date) {
|
||||
if (!a1->meta->release_date && !a2->meta->release_date)
|
||||
return 0;
|
||||
|
||||
if (!a1->meta->release_date && a2->meta->release_date)
|
||||
return -1;
|
||||
|
||||
if (a1->meta->release_date && !a2->meta->release_date)
|
||||
return 1;
|
||||
|
||||
if (a1->meta->release_date > a2->meta->release_date)
|
||||
return 1;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!a1->meta->name && !a2->meta->name)
|
||||
return stricmp (a1->dirname, a2->dirname);
|
||||
|
||||
if (!a1->meta->name && a2->meta->name)
|
||||
return 1;
|
||||
|
||||
if (a1->meta->name && !a2->meta->name)
|
||||
return -1;
|
||||
|
||||
return stricmp (a1->meta->name, a2->meta->name);
|
||||
}
|
||||
|
||||
static void app_entry_filter(app_filter filter) {
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < MAX_ENTRIES; ++i)
|
||||
entries[i] = NULL;
|
||||
|
||||
entry_count = 0;
|
||||
|
||||
for (i = 0; i < MAX_ENTRIES; ++i) {
|
||||
if (!entries_all[i])
|
||||
continue;
|
||||
|
||||
switch (filter) {
|
||||
case APP_FILTER_ICONSONLY:
|
||||
if (!entries_all[i]->icon)
|
||||
continue;
|
||||
break;
|
||||
|
||||
case APP_FILTER_DATEONLY:
|
||||
if (!entries_all[i]->meta) //|| !entries_all[i]->meta->release_date)
|
||||
continue;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
entries[entry_count] = entries_all[i];
|
||||
entry_count++;
|
||||
}
|
||||
|
||||
current_filter = filter;
|
||||
|
||||
if (entry_count)
|
||||
qsort(entries, entry_count, sizeof(app_entry *), cmp_app_entry);
|
||||
}
|
||||
|
||||
app_sort app_entry_get_sort(void) {
|
||||
return current_sort;
|
||||
}
|
||||
|
||||
void app_entry_set_sort(app_sort sort) {
|
||||
switch (sort) {
|
||||
case APP_SORT_DATE:
|
||||
cmp_descending = true;
|
||||
cmp_release_date = true;
|
||||
current_filter = APP_FILTER_DATEONLY;
|
||||
current_sort = APP_SORT_DATE;
|
||||
break;
|
||||
|
||||
default:
|
||||
cmp_descending = false;
|
||||
cmp_release_date = false;
|
||||
current_filter = APP_FILTER_ALL;
|
||||
current_sort = APP_SORT_NAME;
|
||||
break;
|
||||
}
|
||||
|
||||
if (settings.sort_order != current_sort)
|
||||
settings.sort_order = current_sort;
|
||||
}
|
||||
|
||||
static app_entry *app_entry_load_single (const char *dirname) {
|
||||
app_entry *entry;
|
||||
app_entry_type type;
|
||||
char tmp[MAXPATHLEN + 32];
|
||||
struct stat st;
|
||||
|
||||
type = AET_BOOT_ELF;
|
||||
sprintf(tmp, "%s/%s/%s", app_path, dirname, app_fn_boot_elf);
|
||||
|
||||
if (stat(tmp, &st)) {
|
||||
type = AET_BOOT_DOL;
|
||||
sprintf(tmp, "%s/%s/%s", app_path, dirname, app_fn_boot_dol);
|
||||
|
||||
if (stat(tmp, &st)) {
|
||||
type = AET_THEME;
|
||||
sprintf(tmp, "%s/%s/%s", app_path, dirname, app_fn_theme);
|
||||
|
||||
if (stat(tmp, &st))
|
||||
return NULL;
|
||||
|
||||
if (st.st_size > MAX_THEME_ZIP_SIZE)
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!st.st_size || st.st_size > LD_MAX_SIZE)
|
||||
return NULL;
|
||||
|
||||
entry = (app_entry *) pmalloc(sizeof(app_entry));
|
||||
|
||||
entry->type = type;
|
||||
entry->size = st.st_size;
|
||||
entry->dirname = pstrdup(dirname);
|
||||
entry->icon = NULL;
|
||||
entry->meta = NULL;
|
||||
|
||||
sprintf(tmp, "%s/%s/%s", app_path, dirname, app_fn_meta);
|
||||
if (stat(tmp, &st) == 0)
|
||||
entry->meta = meta_parse(tmp);
|
||||
|
||||
sprintf(tmp, "%s/%s/%s", app_path, dirname, app_fn_icon);
|
||||
if (stat(tmp, &st) == 0)
|
||||
entry->icon = tex_from_png_file(tmp, &st, APP_ENTRY_ICON_WIDTH,
|
||||
APP_ENTRY_ICON_HEIGHT);
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
void app_entries_free(void) {
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < MAX_ENTRIES; ++i) {
|
||||
entries[i] = NULL;
|
||||
|
||||
if (!entries_all[i])
|
||||
continue;
|
||||
|
||||
free (entries_all[i]->dirname);
|
||||
tex_free (entries_all[i]->icon);
|
||||
meta_free (entries_all[i]->meta);
|
||||
|
||||
free(entries_all[i]);
|
||||
entries_all[i] = NULL;
|
||||
}
|
||||
|
||||
entry_count_all = 0;
|
||||
entry_count = 0;
|
||||
}
|
||||
|
||||
static void app_entry_load_all (void) {
|
||||
app_entry *entry;
|
||||
DIR *d;
|
||||
struct dirent *de;
|
||||
|
||||
app_entries_free ();
|
||||
|
||||
d = opendir(app_path);
|
||||
if (!d)
|
||||
return;
|
||||
|
||||
while ((de = readdir(d))) {
|
||||
// ignore dotfiles / dotdirs
|
||||
if (de->d_name[0] == '\0' || de->d_name[0] == '.')
|
||||
continue;
|
||||
|
||||
// All apps have their own dir
|
||||
if (de->d_type != DT_DIR)
|
||||
continue;
|
||||
|
||||
entry = app_entry_load_single(de->d_name);
|
||||
|
||||
if (!entry)
|
||||
continue;
|
||||
|
||||
entries_all[entry_count_all] = entry;
|
||||
|
||||
entry_count_all++;
|
||||
|
||||
if (entry_count_all >= MAX_ENTRIES)
|
||||
break;
|
||||
}
|
||||
|
||||
closedir(d);
|
||||
|
||||
app_entry_filter(current_filter);
|
||||
}
|
||||
|
||||
bool app_entry_remove(app_entry *app) {
|
||||
u32 i;
|
||||
bool res = false;
|
||||
|
||||
for (i = 0; i < MAX_ENTRIES; ++i)
|
||||
if (entries_all[i] == app) {
|
||||
free(entries_all[i]->dirname);
|
||||
tex_free(entries_all[i]->icon);
|
||||
meta_free(entries_all[i]->meta);
|
||||
|
||||
free(entries_all[i]);
|
||||
entries_all[i] = NULL;
|
||||
entry_count_all--;
|
||||
|
||||
res = true;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (res)
|
||||
app_entry_filter(current_filter);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
app_entry *app_entry_add(const char *dirname) {
|
||||
app_entry *entry = NULL;
|
||||
u32 i;
|
||||
bool filter = false;
|
||||
|
||||
gprintf("adding entry '%s'\n", dirname);
|
||||
|
||||
for (i = 0; i < MAX_ENTRIES; ++i) {
|
||||
if (entries_all[i]) {
|
||||
if (!strcasecmp(entries_all[i]->dirname, dirname)) {
|
||||
gprintf("removing old entry '%s'\n", entries_all[i]->dirname);
|
||||
|
||||
free(entries_all[i]->dirname);
|
||||
tex_free(entries_all[i]->icon);
|
||||
meta_free(entries_all[i]->meta);
|
||||
|
||||
free(entries_all[i]);
|
||||
entries_all[i] = NULL;
|
||||
entry_count_all--;
|
||||
|
||||
filter = true;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (entry_count_all >= MAX_ENTRIES)
|
||||
goto exit;
|
||||
|
||||
entry = app_entry_load_single(dirname);
|
||||
|
||||
if (!entry)
|
||||
goto exit;
|
||||
|
||||
for (i = 0; i < MAX_ENTRIES; ++i)
|
||||
if (!entries_all[i]) {
|
||||
entries_all[i] = entry;
|
||||
entry_count_all++;
|
||||
|
||||
filter = true;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
exit:
|
||||
if (filter)
|
||||
app_entry_filter(current_filter);
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
static bool _mount(int index) {
|
||||
devices[index].last_status = fatMountSimple(devices[index].name,
|
||||
devices[index].device);
|
||||
|
||||
if (!devices[index].last_status) {
|
||||
devices[index].device->shutdown();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
AE_CMD_IDLE = 0,
|
||||
AE_CMD_EXIT,
|
||||
AE_CMD_SCAN,
|
||||
AE_CMD_POLLSTATUS
|
||||
} ae_cmd;
|
||||
|
||||
typedef struct {
|
||||
bool running;
|
||||
ae_cmd cmd;
|
||||
ae_action action;
|
||||
bool status[DEVICE_COUNT];
|
||||
bool umount;
|
||||
int mount;
|
||||
bool loading;
|
||||
mutex_t cmutex;
|
||||
cond_t cond;
|
||||
} ae_args;
|
||||
|
||||
static lwp_t ae_thread;
|
||||
static u8 ae_stack[APPENTRY_THREAD_STACKSIZE] ATTRIBUTE_ALIGN (32);
|
||||
static ae_args ta_ae;
|
||||
|
||||
static void *ae_func (void *arg) {
|
||||
ae_args *ta = (ae_args *) arg;
|
||||
int i;
|
||||
char cwd[16];
|
||||
|
||||
ta->running = true;
|
||||
|
||||
for (i = 0; i < DEVICE_COUNT; ++i)
|
||||
ta_ae.status[i] = false;
|
||||
|
||||
LWP_MutexLock (ta->cmutex);
|
||||
|
||||
while (true) {
|
||||
|
||||
ta->cmd = AE_CMD_IDLE;
|
||||
LWP_CondWait(ta->cond, ta->cmutex);
|
||||
|
||||
if (ta->cmd == AE_CMD_EXIT)
|
||||
break;
|
||||
|
||||
switch (ta->cmd) {
|
||||
case AE_CMD_SCAN:
|
||||
|
||||
if (device_active >= 0) {
|
||||
if (!ta->umount && devices[device_active].device->isInserted())
|
||||
continue;
|
||||
|
||||
gprintf("device lost: %s\n", devices[device_active].name);
|
||||
|
||||
ta->umount = false;
|
||||
|
||||
fatUnmount(devices[device_active].name);
|
||||
devices[device_active].device->shutdown();
|
||||
devices[device_active].last_status = false;
|
||||
device_active = -1;
|
||||
|
||||
ta->action = AE_ACT_REMOVE;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ta->mount >= 0) {
|
||||
if (_mount(ta->mount))
|
||||
device_active = ta->mount;
|
||||
ta->mount = -1;
|
||||
}
|
||||
|
||||
if ((device_active < 0) && (device_prefered >= 0)) {
|
||||
if (_mount(device_prefered))
|
||||
device_active = device_prefered;
|
||||
}
|
||||
|
||||
if (device_active < 0) {
|
||||
for (i = 0; i < DEVICE_COUNT; ++i)
|
||||
if (_mount(i)) {
|
||||
device_active = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (device_active >= 0) {
|
||||
gprintf("device mounted: %s\n", devices[device_active].name);
|
||||
|
||||
ta->loading = true;
|
||||
|
||||
strcpy(cwd, devices[device_active].name);
|
||||
strcat(cwd, ":/");
|
||||
|
||||
gprintf("chdir to '%s'\n", cwd);
|
||||
if (chdir(cwd))
|
||||
gprintf("chdir failed: %d\n", errno);
|
||||
|
||||
app_entry_load_all();
|
||||
|
||||
ta->loading = false;
|
||||
|
||||
ta->action = AE_ACT_ADD;
|
||||
}
|
||||
|
||||
continue;
|
||||
|
||||
case AE_CMD_POLLSTATUS:
|
||||
for (i = 0; i < DEVICE_COUNT; ++i) {
|
||||
if (i == device_active) {
|
||||
ta->status[i] = devices[i].last_status =
|
||||
devices[i].device->isInserted();
|
||||
|
||||
if (!ta->umount && !devices[i].last_status)
|
||||
ta->umount = true;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (devices[i].last_status) {
|
||||
if (!devices[i].device->isInserted()) {
|
||||
ta->status[i] = devices[i].last_status = false;
|
||||
devices[i].device->shutdown();
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!devices[i].device->startup()) {
|
||||
ta->status[i] = devices[i].last_status = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
ta->status[i] = devices[i].last_status =
|
||||
devices[i].device->isInserted();
|
||||
|
||||
if (!devices[i].last_status)
|
||||
devices[i].device->shutdown();
|
||||
}
|
||||
|
||||
continue;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
LWP_MutexUnlock (ta->cmutex);
|
||||
|
||||
gprintf("app entry thread done\n");
|
||||
|
||||
ta->running = false;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void app_entry_init(void) {
|
||||
s32 res;
|
||||
|
||||
memset(&entries_all, 0, sizeof(app_entry *) * MAX_ENTRIES);
|
||||
memset(&entries, 0, sizeof(app_entry *) * MAX_ENTRIES);
|
||||
|
||||
gprintf("starting app entry thread\n");
|
||||
|
||||
ta_ae.cmd = AE_CMD_IDLE;
|
||||
ta_ae.action = AE_ACT_NONE;
|
||||
ta_ae.umount = false;
|
||||
ta_ae.mount = -1;
|
||||
ta_ae.loading = false;
|
||||
|
||||
res = LWP_MutexInit (&ta_ae.cmutex, false);
|
||||
|
||||
if (res) {
|
||||
gprintf ("error creating cmutex: %ld\n", res);
|
||||
return;
|
||||
}
|
||||
|
||||
res = LWP_CondInit (&ta_ae.cond);
|
||||
|
||||
if (res) {
|
||||
gprintf ("error creating cond: %ld\n", res);
|
||||
return;
|
||||
}
|
||||
|
||||
memset(&ae_stack, 0, APPENTRY_THREAD_STACKSIZE);
|
||||
res = LWP_CreateThread(&ae_thread, ae_func, &ta_ae, ae_stack,
|
||||
APPENTRY_THREAD_STACKSIZE, APPENTRY_THREAD_PRIO);
|
||||
|
||||
if (res) {
|
||||
gprintf("error creating thread: %ld\n", res);
|
||||
}
|
||||
}
|
||||
|
||||
void app_entry_deinit (void) {
|
||||
u8 i;
|
||||
|
||||
if (ta_ae.running) {
|
||||
gprintf ("stopping app entry thread\n");
|
||||
|
||||
for (i = 0; i < 25; ++i) {
|
||||
if (LWP_MutexTryLock (ta_ae.cmutex) == 0) {
|
||||
break;
|
||||
}
|
||||
usleep (20 * 1000);
|
||||
}
|
||||
if (i < 25) {
|
||||
gprintf ("sending app entry thread the exit cmd\n");
|
||||
ta_ae.cmd = AE_CMD_EXIT;
|
||||
LWP_SetThreadPriority (ae_thread, LWP_PRIO_HIGHEST);
|
||||
LWP_CondBroadcast (ta_ae.cond);
|
||||
LWP_MutexUnlock (ta_ae.cmutex);
|
||||
LWP_JoinThread(ae_thread, NULL);
|
||||
LWP_CondDestroy (ta_ae.cond);
|
||||
LWP_MutexDestroy (ta_ae.cmutex);
|
||||
app_entries_free ();
|
||||
} else {
|
||||
gprintf("app entry thread didn't shutdown gracefully!\n");
|
||||
}
|
||||
|
||||
if (device_active >= 0) {
|
||||
fatUnmount(devices[device_active].name);
|
||||
devices[device_active].device->shutdown();
|
||||
}
|
||||
|
||||
USB_Deinitialize();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void app_entry_scan(void) {
|
||||
if (!ta_ae.running)
|
||||
return;
|
||||
|
||||
if (LWP_MutexTryLock(ta_ae.cmutex) != 0)
|
||||
return;
|
||||
|
||||
ta_ae.cmd = AE_CMD_SCAN;
|
||||
LWP_CondBroadcast(ta_ae.cond);
|
||||
LWP_MutexUnlock(ta_ae.cmutex);
|
||||
}
|
||||
|
||||
ae_action app_entry_action(void) {
|
||||
ae_action res;
|
||||
|
||||
if (!ta_ae.running)
|
||||
return AE_ACT_NONE;
|
||||
|
||||
if (LWP_MutexTryLock(ta_ae.cmutex) != 0)
|
||||
return AE_ACT_NONE;
|
||||
|
||||
res = ta_ae.action;
|
||||
ta_ae.action = AE_ACT_NONE;
|
||||
LWP_MutexUnlock(ta_ae.cmutex);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void app_entry_poll_status(bool reset) {
|
||||
if (!ta_ae.running)
|
||||
return;
|
||||
|
||||
if (LWP_MutexTryLock(ta_ae.cmutex) != 0)
|
||||
return;
|
||||
|
||||
ta_ae.cmd = AE_CMD_POLLSTATUS;
|
||||
LWP_CondBroadcast(ta_ae.cond);
|
||||
LWP_MutexUnlock(ta_ae.cmutex);
|
||||
}
|
||||
|
||||
int app_entry_get_status(bool *status) {
|
||||
u8 i;
|
||||
|
||||
if (status)
|
||||
for (i = 0; i < DEVICE_COUNT; ++i)
|
||||
status[i] = ta_ae.status[i];
|
||||
|
||||
return device_active;
|
||||
}
|
||||
|
||||
void app_entry_set_prefered(int device) {
|
||||
if (device < 0) {
|
||||
device_prefered = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (device > DEVICE_COUNT - 1) {
|
||||
device_prefered = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
device_prefered = device;
|
||||
|
||||
if (settings.device != device_prefered)
|
||||
settings.device = device_prefered;
|
||||
}
|
||||
|
||||
void app_entry_set_device(int device) {
|
||||
if (device < 0)
|
||||
return;
|
||||
|
||||
if (device > DEVICE_COUNT - 1)
|
||||
return;
|
||||
|
||||
ta_ae.umount = true;
|
||||
ta_ae.mount = device;
|
||||
}
|
||||
|
||||
bool app_entry_get_path(char *buf) {
|
||||
if (device_active < 0)
|
||||
return false;
|
||||
|
||||
strcpy(buf, devices[device_active].name);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool app_entry_get_filename(char *buf, app_entry *app) {
|
||||
if (device_active < 0)
|
||||
return false;
|
||||
|
||||
sprintf(buf, "%s:%s/%s/", devices[device_active].name,
|
||||
app_path, app->dirname);
|
||||
|
||||
switch(app->type) {
|
||||
case AET_BOOT_ELF:
|
||||
strcat(buf, app_fn_boot_elf);
|
||||
return true;
|
||||
case AET_BOOT_DOL:
|
||||
strcat(buf, app_fn_boot_dol);
|
||||
return true;
|
||||
case AET_THEME:
|
||||
strcat(buf, app_fn_theme);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
app_entry *app_entry_find(char *dirname) {
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < entry_count; ++i)
|
||||
if (!strcasecmp(entries[i]->dirname, dirname))
|
||||
return entries[i];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool app_entry_is_loading(void) {
|
||||
if (!ta_ae.running)
|
||||
return false;
|
||||
|
||||
return ta_ae.loading;
|
||||
}
|
||||
|
||||
80
channel/channelapp/source/appentry.h
Normal file
80
channel/channelapp/source/appentry.h
Normal file
@@ -0,0 +1,80 @@
|
||||
#ifndef _APPENTRY_H_
|
||||
#define _APPENTRY_H_
|
||||
|
||||
#include <gctypes.h>
|
||||
|
||||
#include "gfx.h"
|
||||
#include "xml.h"
|
||||
|
||||
#define DEVICE_COUNT 4
|
||||
#define MAX_THEME_ZIP_SIZE (20 * 1024 * 1024)
|
||||
|
||||
typedef enum {
|
||||
AET_BOOT_ELF = 0,
|
||||
AET_BOOT_DOL,
|
||||
AET_THEME
|
||||
} app_entry_type;
|
||||
|
||||
typedef struct {
|
||||
app_entry_type type;
|
||||
u32 size;
|
||||
char *dirname;
|
||||
gfx_entity *icon;
|
||||
meta_info *meta;
|
||||
} app_entry;
|
||||
|
||||
typedef enum {
|
||||
AE_ACT_NONE = 0,
|
||||
AE_ACT_REMOVE,
|
||||
AE_ACT_ADD
|
||||
} ae_action;
|
||||
|
||||
typedef enum {
|
||||
APP_FILTER_ALL = 0,
|
||||
APP_FILTER_ICONSONLY,
|
||||
APP_FILTER_DATEONLY
|
||||
} app_filter;
|
||||
|
||||
typedef enum {
|
||||
APP_SORT_NAME = 0,
|
||||
APP_SORT_DATE
|
||||
} app_sort;
|
||||
|
||||
extern const char *app_path;
|
||||
extern const char *app_fn_boot_elf;
|
||||
extern const char *app_fn_boot_dol;
|
||||
extern const char *app_fn_theme;
|
||||
extern const char *app_fn_meta;
|
||||
extern const char *app_fn_icon;
|
||||
|
||||
extern app_entry *entries[MAX_ENTRIES];
|
||||
extern u32 entry_count;
|
||||
|
||||
void app_entry_init (void);
|
||||
void app_entry_deinit (void);
|
||||
|
||||
void app_entries_free(void);
|
||||
|
||||
void app_entry_scan(void);
|
||||
ae_action app_entry_action(void);
|
||||
|
||||
void app_entry_poll_status(bool reset);
|
||||
int app_entry_get_status(bool *status);
|
||||
|
||||
void app_entry_set_prefered(int device);
|
||||
void app_entry_set_device(int device);
|
||||
|
||||
bool app_entry_get_path(char *buf);
|
||||
bool app_entry_get_filename(char *buf, app_entry *app);
|
||||
app_entry *app_entry_find(char *dirname);
|
||||
|
||||
app_sort app_entry_get_sort(void);
|
||||
void app_entry_set_sort(app_sort sort);
|
||||
|
||||
app_entry *app_entry_add(const char *dirname);
|
||||
bool app_entry_remove(app_entry *app);
|
||||
|
||||
bool app_entry_is_loading(void);
|
||||
|
||||
#endif
|
||||
|
||||
340
channel/channelapp/source/asm.h
Normal file
340
channel/channelapp/source/asm.h
Normal file
@@ -0,0 +1,340 @@
|
||||
// this file was taken from libogc, see http://www.devkitpro.org/
|
||||
|
||||
#ifndef __ASM_H__
|
||||
#define __ASM_H__
|
||||
|
||||
#ifdef _LANGUAGE_ASSEMBLY
|
||||
/* Condition Register Bit Fields */
|
||||
|
||||
#define cr0 0
|
||||
#define cr1 1
|
||||
#define cr2 2
|
||||
#define cr3 3
|
||||
#define cr4 4
|
||||
#define cr5 5
|
||||
#define cr6 6
|
||||
#define cr7 7
|
||||
|
||||
|
||||
/* General Purpose Registers (GPRs) */
|
||||
|
||||
#define r0 0
|
||||
#define r1 1
|
||||
#define sp 1
|
||||
#define r2 2
|
||||
#define toc 2
|
||||
#define r3 3
|
||||
#define r4 4
|
||||
#define r5 5
|
||||
#define r6 6
|
||||
#define r7 7
|
||||
#define r8 8
|
||||
#define r9 9
|
||||
#define r10 10
|
||||
#define r11 11
|
||||
#define r12 12
|
||||
#define r13 13
|
||||
#define r14 14
|
||||
#define r15 15
|
||||
#define r16 16
|
||||
#define r17 17
|
||||
#define r18 18
|
||||
#define r19 19
|
||||
#define r20 20
|
||||
#define r21 21
|
||||
#define r22 22
|
||||
#define r23 23
|
||||
#define r24 24
|
||||
#define r25 25
|
||||
#define r26 26
|
||||
#define r27 27
|
||||
#define r28 28
|
||||
#define r29 29
|
||||
#define r30 30
|
||||
#define r31 31
|
||||
|
||||
|
||||
/* Floating Point Registers (FPRs) */
|
||||
|
||||
#define fr0 0
|
||||
#define fr1 1
|
||||
#define fr2 2
|
||||
#define fr3 3
|
||||
#define fr4 4
|
||||
#define fr5 5
|
||||
#define fr6 6
|
||||
#define fr7 7
|
||||
#define fr8 8
|
||||
#define fr9 9
|
||||
#define fr10 10
|
||||
#define fr11 11
|
||||
#define fr12 12
|
||||
#define fr13 13
|
||||
#define fr14 14
|
||||
#define fr15 15
|
||||
#define fr16 16
|
||||
#define fr17 17
|
||||
#define fr18 18
|
||||
#define fr19 19
|
||||
#define fr20 20
|
||||
#define fr21 21
|
||||
#define fr22 22
|
||||
#define fr23 23
|
||||
#define fr24 24
|
||||
#define fr25 25
|
||||
#define fr26 26
|
||||
#define fr27 27
|
||||
#define fr28 28
|
||||
#define fr29 29
|
||||
#define fr30 30
|
||||
#define fr31 31
|
||||
|
||||
#define vr0 0
|
||||
#define vr1 1
|
||||
#define vr2 2
|
||||
#define vr3 3
|
||||
#define vr4 4
|
||||
#define vr5 5
|
||||
#define vr6 6
|
||||
#define vr7 7
|
||||
#define vr8 8
|
||||
#define vr9 9
|
||||
#define vr10 10
|
||||
#define vr11 11
|
||||
#define vr12 12
|
||||
#define vr13 13
|
||||
#define vr14 14
|
||||
#define vr15 15
|
||||
#define vr16 16
|
||||
#define vr17 17
|
||||
#define vr18 18
|
||||
#define vr19 19
|
||||
#define vr20 20
|
||||
#define vr21 21
|
||||
#define vr22 22
|
||||
#define vr23 23
|
||||
#define vr24 24
|
||||
#define vr25 25
|
||||
#define vr26 26
|
||||
#define vr27 27
|
||||
#define vr28 28
|
||||
#define vr29 29
|
||||
#define vr30 30
|
||||
#define vr31 31
|
||||
|
||||
#endif //_LANGUAGE_ASSEMBLY
|
||||
|
||||
#define SPRG0 272
|
||||
#define SPRG1 273
|
||||
#define SPRG2 274
|
||||
#define SPRG3 275
|
||||
|
||||
#define PMC1 953
|
||||
#define PMC2 954
|
||||
#define PMC3 957
|
||||
#define PMC4 958
|
||||
|
||||
#define MMCR0 952
|
||||
#define MMCR1 956
|
||||
|
||||
|
||||
#define LINK_REGISTER_CALLEE_UPDATE_ROOM 4
|
||||
#define EXCEPTION_NUMBER 8
|
||||
#define SRR0_OFFSET 12
|
||||
#define SRR1_OFFSET 16
|
||||
#define GPR0_OFFSET 20
|
||||
#define GPR1_OFFSET 24
|
||||
#define GPR2_OFFSET 28
|
||||
#define GPR3_OFFSET 32
|
||||
#define GPR4_OFFSET 36
|
||||
#define GPR5_OFFSET 40
|
||||
#define GPR6_OFFSET 44
|
||||
#define GPR7_OFFSET 48
|
||||
#define GPR8_OFFSET 52
|
||||
#define GPR9_OFFSET 56
|
||||
#define GPR10_OFFSET 60
|
||||
#define GPR11_OFFSET 64
|
||||
#define GPR12_OFFSET 68
|
||||
#define GPR13_OFFSET 72
|
||||
#define GPR14_OFFSET 76
|
||||
#define GPR15_OFFSET 80
|
||||
#define GPR16_OFFSET 84
|
||||
#define GPR17_OFFSET 88
|
||||
#define GPR18_OFFSET 92
|
||||
#define GPR19_OFFSET 96
|
||||
#define GPR20_OFFSET 100
|
||||
#define GPR21_OFFSET 104
|
||||
#define GPR22_OFFSET 108
|
||||
#define GPR23_OFFSET 112
|
||||
#define GPR24_OFFSET 116
|
||||
#define GPR25_OFFSET 120
|
||||
#define GPR26_OFFSET 124
|
||||
#define GPR27_OFFSET 128
|
||||
#define GPR28_OFFSET 132
|
||||
#define GPR29_OFFSET 136
|
||||
#define GPR30_OFFSET 140
|
||||
#define GPR31_OFFSET 144
|
||||
|
||||
#define GQR0_OFFSET 148
|
||||
#define GQR1_OFFSET 152
|
||||
#define GQR2_OFFSET 156
|
||||
#define GQR3_OFFSET 160
|
||||
#define GQR4_OFFSET 164
|
||||
#define GQR5_OFFSET 168
|
||||
#define GQR6_OFFSET 172
|
||||
#define GQR7_OFFSET 176
|
||||
|
||||
#define CR_OFFSET 180
|
||||
#define LR_OFFSET 184
|
||||
#define CTR_OFFSET 188
|
||||
#define XER_OFFSET 192
|
||||
#define MSR_OFFSET 196
|
||||
#define DAR_OFFSET 200
|
||||
|
||||
#define STATE_OFFSET 204
|
||||
#define MODE_OFFSET 206
|
||||
|
||||
#define FPR0_OFFSET 208
|
||||
#define FPR1_OFFSET 216
|
||||
#define FPR2_OFFSET 224
|
||||
#define FPR3_OFFSET 232
|
||||
#define FPR4_OFFSET 240
|
||||
#define FPR5_OFFSET 248
|
||||
#define FPR6_OFFSET 256
|
||||
#define FPR7_OFFSET 264
|
||||
#define FPR8_OFFSET 272
|
||||
#define FPR9_OFFSET 280
|
||||
#define FPR10_OFFSET 288
|
||||
#define FPR11_OFFSET 296
|
||||
#define FPR12_OFFSET 304
|
||||
#define FPR13_OFFSET 312
|
||||
#define FPR14_OFFSET 320
|
||||
#define FPR15_OFFSET 328
|
||||
#define FPR16_OFFSET 336
|
||||
#define FPR17_OFFSET 344
|
||||
#define FPR18_OFFSET 352
|
||||
#define FPR19_OFFSET 360
|
||||
#define FPR20_OFFSET 368
|
||||
#define FPR21_OFFSET 376
|
||||
#define FPR22_OFFSET 384
|
||||
#define FPR23_OFFSET 392
|
||||
#define FPR24_OFFSET 400
|
||||
#define FPR25_OFFSET 408
|
||||
#define FPR26_OFFSET 416
|
||||
#define FPR27_OFFSET 424
|
||||
#define FPR28_OFFSET 432
|
||||
#define FPR29_OFFSET 440
|
||||
#define FPR30_OFFSET 448
|
||||
#define FPR31_OFFSET 456
|
||||
|
||||
#define FPSCR_OFFSET 464
|
||||
|
||||
#define PSR0_OFFSET 472
|
||||
#define PSR1_OFFSET 480
|
||||
#define PSR2_OFFSET 488
|
||||
#define PSR3_OFFSET 496
|
||||
#define PSR4_OFFSET 504
|
||||
#define PSR5_OFFSET 512
|
||||
#define PSR6_OFFSET 520
|
||||
#define PSR7_OFFSET 528
|
||||
#define PSR8_OFFSET 536
|
||||
#define PSR9_OFFSET 544
|
||||
#define PSR10_OFFSET 552
|
||||
#define PSR11_OFFSET 560
|
||||
#define PSR12_OFFSET 568
|
||||
#define PSR13_OFFSET 576
|
||||
#define PSR14_OFFSET 584
|
||||
#define PSR15_OFFSET 592
|
||||
#define PSR16_OFFSET 600
|
||||
#define PSR17_OFFSET 608
|
||||
#define PSR18_OFFSET 616
|
||||
#define PSR19_OFFSET 624
|
||||
#define PSR20_OFFSET 632
|
||||
#define PSR21_OFFSET 640
|
||||
#define PSR22_OFFSET 648
|
||||
#define PSR23_OFFSET 656
|
||||
#define PSR24_OFFSET 664
|
||||
#define PSR25_OFFSET 672
|
||||
#define PSR26_OFFSET 680
|
||||
#define PSR27_OFFSET 688
|
||||
#define PSR28_OFFSET 696
|
||||
#define PSR29_OFFSET 704
|
||||
#define PSR30_OFFSET 712
|
||||
#define PSR31_OFFSET 720
|
||||
/*
|
||||
* maintain the EABI requested 8 bytes aligment
|
||||
* As SVR4 ABI requires 16, make it 16 (as some
|
||||
* exception may need more registers to be processed...)
|
||||
*/
|
||||
#define EXCEPTION_FRAME_END 728
|
||||
|
||||
#define IBAT0U 528
|
||||
#define IBAT0L 529
|
||||
#define IBAT1U 530
|
||||
#define IBAT1L 531
|
||||
#define IBAT2U 532
|
||||
#define IBAT2L 533
|
||||
#define IBAT3U 534
|
||||
#define IBAT3L 535
|
||||
#define IBAT4U 560
|
||||
#define IBAT4L 561
|
||||
#define IBAT5U 562
|
||||
#define IBAT5L 563
|
||||
#define IBAT6U 564
|
||||
#define IBAT6L 565
|
||||
#define IBAT7U 566
|
||||
#define IBAT7L 567
|
||||
|
||||
#define DBAT0U 536
|
||||
#define DBAT0L 537
|
||||
#define DBAT1U 538
|
||||
#define DBAT1L 538
|
||||
#define DBAT2U 540
|
||||
#define DBAT2L 541
|
||||
#define DBAT3U 542
|
||||
#define DBAT3L 543
|
||||
#define DBAT4U 568
|
||||
#define DBAT4L 569
|
||||
#define DBAT5U 570
|
||||
#define DBAT5L 571
|
||||
#define DBAT6U 572
|
||||
#define DBAT6L 573
|
||||
#define DBAT7U 574
|
||||
#define DBAT7L 575
|
||||
|
||||
#define HID0 1008
|
||||
#define HID1 1009
|
||||
#define HID2 920
|
||||
#define HID4 1011
|
||||
|
||||
#define GQR0 912
|
||||
#define GQR1 913
|
||||
#define GQR2 914
|
||||
#define GQR3 915
|
||||
#define GQR4 916
|
||||
#define GQR5 917
|
||||
#define GQR6 918
|
||||
#define GQR7 919
|
||||
|
||||
#define L2CR 1017
|
||||
|
||||
#define WPAR 921
|
||||
|
||||
#define DMAU 922
|
||||
#define DMAL 923
|
||||
|
||||
#define MSR_RI 0x00000002
|
||||
#define MSR_DR 0x00000010
|
||||
#define MSR_IR 0x00000020
|
||||
#define MSR_IP 0x00000040
|
||||
#define MSR_SE 0x00000400
|
||||
#define MSR_ME 0x00001000
|
||||
#define MSR_FP 0x00002000
|
||||
#define MSR_POW 0x00004000
|
||||
#define MSR_EE 0x00008000
|
||||
|
||||
#define PPC_ALIGNMENT 8
|
||||
|
||||
#define PPC_CACHE_ALIGNMENT 32
|
||||
|
||||
#endif //__ASM_H__
|
||||
84
channel/channelapp/source/blob.c
Normal file
84
channel/channelapp/source/blob.c
Normal file
@@ -0,0 +1,84 @@
|
||||
#include <ogcsys.h>
|
||||
#include <ogc/machine/processor.h>
|
||||
|
||||
#include "../config.h"
|
||||
#include "debug.h"
|
||||
#include "panic.h"
|
||||
#include "blob.h"
|
||||
|
||||
#define MAX_BLOBS 8
|
||||
#define BLOB_MINSLACK (512*1024)
|
||||
|
||||
typedef struct {
|
||||
void *the_blob;
|
||||
size_t blob_size;
|
||||
void *old_arena2hi;
|
||||
} blob_t;
|
||||
|
||||
blob_t blobs[MAX_BLOBS];
|
||||
int num_blobs = 0;
|
||||
|
||||
// supports only stack-type allocs (free last alloced)
|
||||
void *blob_alloc(size_t size)
|
||||
{
|
||||
u32 level;
|
||||
u32 addr;
|
||||
void *old_arena2hi;
|
||||
|
||||
_CPU_ISR_Disable(level);
|
||||
if (num_blobs >= MAX_BLOBS) {
|
||||
_CPU_ISR_Restore(level);
|
||||
gprintf("too many blobs\n");
|
||||
panic();
|
||||
}
|
||||
|
||||
old_arena2hi = SYS_GetArena2Hi();
|
||||
addr = (((u32)old_arena2hi) - size) & (~0x1f);
|
||||
|
||||
if (addr < (BLOB_MINSLACK + (u32)SYS_GetArena2Lo())) {
|
||||
_CPU_ISR_Restore(level);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
blobs[num_blobs].old_arena2hi = old_arena2hi;
|
||||
blobs[num_blobs].the_blob = (void*)addr;
|
||||
blobs[num_blobs].blob_size = size;
|
||||
num_blobs++;
|
||||
|
||||
SYS_SetArena2Hi((void*)addr);
|
||||
_CPU_ISR_Restore(level);
|
||||
gprintf("allocated blob size %d at 0x%08lx\n", size, addr);
|
||||
return (void*)addr;
|
||||
}
|
||||
|
||||
void blob_free(void *p)
|
||||
{
|
||||
u32 level;
|
||||
if (!p)
|
||||
return;
|
||||
|
||||
_CPU_ISR_Disable(level);
|
||||
|
||||
if (num_blobs == 0) {
|
||||
_CPU_ISR_Restore(level);
|
||||
gprintf("blob_free with no blobs\n");
|
||||
panic();
|
||||
}
|
||||
|
||||
num_blobs--;
|
||||
if (p != blobs[num_blobs].the_blob) {
|
||||
_CPU_ISR_Restore(level);
|
||||
gprintf("mismatched blob_free (%p != %p)\n", p, blobs[num_blobs].the_blob);
|
||||
panic();
|
||||
}
|
||||
if (SYS_GetArena2Hi() != p) {
|
||||
_CPU_ISR_Restore(level);
|
||||
gprintf("someone else used MEM2 (%p != %p)\n", p, SYS_GetArena2Hi());
|
||||
panic();
|
||||
}
|
||||
|
||||
SYS_SetArena2Hi(blobs[num_blobs].old_arena2hi);
|
||||
_CPU_ISR_Restore(level);
|
||||
gprintf("freed blob size %d at %p\n", blobs[num_blobs].blob_size, p);
|
||||
}
|
||||
|
||||
9
channel/channelapp/source/blob.h
Normal file
9
channel/channelapp/source/blob.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#ifndef _BLOB_H_
|
||||
#define _BLOB_H_
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
void *blob_alloc(size_t size);
|
||||
void blob_free(void *p);
|
||||
|
||||
#endif
|
||||
330
channel/channelapp/source/browser.c
Normal file
330
channel/channelapp/source/browser.c
Normal file
@@ -0,0 +1,330 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <math.h>
|
||||
|
||||
#include <ogcsys.h>
|
||||
#include <ogc/lwp_watchdog.h>
|
||||
|
||||
#include "../config.h"
|
||||
#include "theme.h"
|
||||
#include "view.h"
|
||||
#include "controls.h"
|
||||
#include "i18n.h"
|
||||
|
||||
#include "browser.h"
|
||||
|
||||
#define AE_OFFSET 4
|
||||
#define TRANS_STEPS 20
|
||||
#define MAX_COLUMNS 4
|
||||
#define ROWS 5
|
||||
|
||||
static bool first_set = true;
|
||||
static u16 top_offset = 0;
|
||||
|
||||
static view *v_browser = NULL;
|
||||
|
||||
static int columns_current = 1;
|
||||
static int columns_new = 1;
|
||||
|
||||
static bool inited_widgets = false;
|
||||
|
||||
view * browser_init(void) {
|
||||
v_browser = view_new (AE_OFFSET + (MAX_COLUMNS * ROWS * 2), NULL,
|
||||
0, 0, 0, 0);
|
||||
browser_theme_reinit();
|
||||
|
||||
return v_browser;
|
||||
}
|
||||
|
||||
void browser_deinit(void) {
|
||||
view_free(v_browser);
|
||||
inited_widgets = false;
|
||||
v_browser = NULL;
|
||||
}
|
||||
|
||||
void browser_theme_reinit(void) {
|
||||
int i;
|
||||
if (inited_widgets)
|
||||
for (i = 0; i < v_browser->widget_count; ++i)
|
||||
widget_free(&v_browser->widgets[i]);
|
||||
|
||||
widget_image (&v_browser->widgets[0], 24, 192, 0,
|
||||
theme_gfx[THEME_ARROW_LEFT], NULL, true,
|
||||
theme_gfx[THEME_ARROW_LEFT_FOCUS]);
|
||||
widget_image (&v_browser->widgets[1],
|
||||
view_width - 24 - theme_gfx[THEME_ARROW_RIGHT]->w, 192, 0,
|
||||
theme_gfx[THEME_ARROW_RIGHT], NULL, true,
|
||||
theme_gfx[THEME_ARROW_RIGHT_FOCUS]);
|
||||
widget_image (&v_browser->widgets[2],
|
||||
view_width - 32 - theme_gfx[THEME_GECKO_ACTIVE]->w -
|
||||
theme_gfx[THEME_LAN_ACTIVE]->w, 412, 0,
|
||||
theme_gfx[THEME_GECKO_ACTIVE], theme_gfx[THEME_GECKO],
|
||||
false, NULL);
|
||||
widget_image (&v_browser->widgets[3],
|
||||
view_width - 32 - theme_gfx[THEME_GECKO_ACTIVE]->w, 412, 0,
|
||||
theme_gfx[THEME_LAN_ACTIVE], theme_gfx[THEME_LAN],
|
||||
false, NULL);
|
||||
widget_set_flag (&v_browser->widgets[2], WF_ENABLED, false);
|
||||
widget_set_flag (&v_browser->widgets[3], WF_ENABLED, false);
|
||||
|
||||
widget_set_flag (&v_browser->widgets[0], WF_VISIBLE, false);
|
||||
widget_set_flag (&v_browser->widgets[1], WF_VISIBLE, false);
|
||||
|
||||
inited_widgets = true;
|
||||
}
|
||||
|
||||
static void browser_set_top_offset(const app_entry *app) {
|
||||
u32 i;
|
||||
|
||||
if (!app || (entry_count < 1)) {
|
||||
top_offset = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < entry_count; ++i)
|
||||
if (entries[i] == app) {
|
||||
top_offset = i;
|
||||
break;
|
||||
}
|
||||
|
||||
top_offset /= columns_new * ROWS;
|
||||
top_offset *= columns_new * ROWS;
|
||||
|
||||
if (top_offset > entry_count - 1)
|
||||
top_offset -= columns_new * ROWS;
|
||||
}
|
||||
void browser_gen_view(browser_action action, const app_entry *app) {
|
||||
bool less, more;
|
||||
app_entry *entry;
|
||||
s8 focus;
|
||||
|
||||
u32 i, j;
|
||||
u16 y;
|
||||
|
||||
u8 o1, o2;
|
||||
s16 xal, xar, x1, x2;
|
||||
float xm;
|
||||
float vala = 0;
|
||||
float val1[MAX_COLUMNS * ROWS];
|
||||
float val2[MAX_COLUMNS * ROWS];
|
||||
float stepa = M_TWOPI / TRANS_STEPS;
|
||||
float step = M_PI / (TRANS_STEPS - 6);
|
||||
s16 s;
|
||||
float f1, f2;
|
||||
|
||||
switch (action) {
|
||||
case BA_REMOVE:
|
||||
break;
|
||||
|
||||
case BA_ADD:
|
||||
case BA_REFRESH:
|
||||
browser_set_top_offset(app);
|
||||
break;
|
||||
|
||||
case BA_NEXT:
|
||||
if (entry_count <= top_offset + (columns_new * ROWS))
|
||||
return;
|
||||
top_offset += columns_current * ROWS;
|
||||
break;
|
||||
|
||||
case BA_PREV:
|
||||
if (top_offset < 1)
|
||||
return;
|
||||
if (top_offset < columns_current * ROWS)
|
||||
return;
|
||||
top_offset -= columns_current * ROWS;
|
||||
break;
|
||||
}
|
||||
|
||||
if (action == BA_REMOVE) {
|
||||
less = false;
|
||||
more = false;
|
||||
} else {
|
||||
less = top_offset > 0;
|
||||
more = entry_count > top_offset + (columns_new * ROWS);
|
||||
}
|
||||
|
||||
memset(val1, 0, sizeof(float) * MAX_COLUMNS * ROWS);
|
||||
memset(val2, 0, sizeof(float) * MAX_COLUMNS * ROWS);
|
||||
|
||||
if (first_set) {
|
||||
o1 = AE_OFFSET;
|
||||
o2 = AE_OFFSET + (MAX_COLUMNS * ROWS);
|
||||
} else {
|
||||
o1 = AE_OFFSET + (MAX_COLUMNS * ROWS);
|
||||
o2 = AE_OFFSET;
|
||||
}
|
||||
|
||||
first_set = !first_set;
|
||||
focus = o2;
|
||||
|
||||
xal = v_browser->widgets[0].coords.x;
|
||||
xar = v_browser->widgets[1].coords.x;
|
||||
|
||||
if (columns_current == 1)
|
||||
x1 = (view_width - theme_gfx[THEME_APP_ENTRY]->w) / 2;
|
||||
else
|
||||
x1 = (view_width - (theme_gfx[THEME_GRID_APP_ENTRY]->w *
|
||||
columns_current)) / 2;
|
||||
|
||||
if (columns_new == 1)
|
||||
x2 = (view_width - theme_gfx[THEME_APP_ENTRY]->w) / 2;
|
||||
else
|
||||
x2 = (view_width - (theme_gfx[THEME_GRID_APP_ENTRY]->w *
|
||||
columns_new)) / 2;
|
||||
|
||||
if (action == BA_PREV) {
|
||||
xm = view_width / 2;
|
||||
x2 = -view_width + x2;
|
||||
} else {
|
||||
xm = -view_width / 2;
|
||||
x2 = view_width + x2;
|
||||
}
|
||||
|
||||
y = 64;
|
||||
|
||||
for (i = 0; i < (MAX_COLUMNS * ROWS); ++i)
|
||||
widget_free(&v_browser->widgets[o2 + i]);
|
||||
|
||||
if (action != BA_REMOVE)
|
||||
for (i = 0; i < (columns_new * ROWS); ++i) {
|
||||
if (entry_count > top_offset + i) {
|
||||
entry = entries[top_offset + i];
|
||||
|
||||
if (entry && (entry == app))
|
||||
focus += i;
|
||||
|
||||
if (columns_new == 1)
|
||||
widget_app_entry(&v_browser->widgets[o2 + i],
|
||||
x2, y, 0, entry);
|
||||
else
|
||||
widget_grid_app_entry(&v_browser->widgets[o2 + i],
|
||||
x2 + ((i % columns_new) *
|
||||
theme_gfx[THEME_GRID_APP_ENTRY]->w),
|
||||
y, 0, entry);
|
||||
}
|
||||
|
||||
if (((i+1) % columns_new) == 0)
|
||||
y += theme_gfx[THEME_APP_ENTRY]->h;
|
||||
}
|
||||
|
||||
for (i = 0; i < TRANS_STEPS; ++i) {
|
||||
vala += stepa;
|
||||
s = roundf (156.0 * (cosf (vala) - 1));
|
||||
|
||||
// adjust L/R button positions
|
||||
v_browser->widgets[0].coords.x = xal + s;
|
||||
v_browser->widgets[1].coords.x = xar - s;
|
||||
|
||||
for (j = 0; j < MAX_COLUMNS * ROWS; ++j) {
|
||||
if ((i > j / columns_current) &&
|
||||
(i < TRANS_STEPS - (ROWS - j / columns_current)))
|
||||
val1[j] += step;
|
||||
if ((i > j / columns_new) &&
|
||||
(i < TRANS_STEPS - (ROWS - j / columns_new)))
|
||||
val2[j] += step;
|
||||
|
||||
f1 = roundf (xm * (cosf (val1[j]) - 1));
|
||||
f2 = roundf (xm * (cosf (val2[j]) - 1));
|
||||
|
||||
v_browser->widgets[o1 + j].coords.x = x1 - f1 +
|
||||
((j % columns_current) * theme_gfx[THEME_GRID_APP_ENTRY]->w);
|
||||
v_browser->widgets[o2 + j].coords.x = x2 - f2 +
|
||||
((j % columns_new) * theme_gfx[THEME_GRID_APP_ENTRY]->w);
|
||||
}
|
||||
|
||||
view_plot (v_browser, 0, NULL, NULL, NULL);
|
||||
|
||||
if (i == TRANS_STEPS / 2) {
|
||||
widget_set_flag (&v_browser->widgets[0], WF_VISIBLE, true);
|
||||
widget_set_flag (&v_browser->widgets[1], WF_VISIBLE, true);
|
||||
widget_set_flag (&v_browser->widgets[0], WF_VISIBLE, less);
|
||||
widget_set_flag (&v_browser->widgets[1], WF_VISIBLE, more);
|
||||
|
||||
view_set_focus (v_browser, focus);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < (MAX_COLUMNS * ROWS); ++i)
|
||||
widget_free(&v_browser->widgets[o1 + i]);
|
||||
|
||||
columns_current = columns_new;
|
||||
|
||||
if (action == BA_REMOVE)
|
||||
top_offset = 0;
|
||||
}
|
||||
|
||||
void browser_set_focus(u32 bd) {
|
||||
if (columns_current == 1) {
|
||||
if (bd & PADS_UP) {
|
||||
view_set_focus_prev(v_browser);
|
||||
return;
|
||||
}
|
||||
|
||||
if (bd & PADS_DOWN) {
|
||||
view_set_focus_next(v_browser);
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
} else {
|
||||
if (bd & PADS_LEFT) {
|
||||
view_set_focus_prev(v_browser);
|
||||
return;
|
||||
}
|
||||
|
||||
if (bd & PADS_RIGHT) {
|
||||
view_set_focus_next(v_browser);
|
||||
return;
|
||||
}
|
||||
|
||||
if (bd & PADS_UP) {
|
||||
view_move_focus(v_browser, -columns_current);
|
||||
return;
|
||||
}
|
||||
|
||||
if (bd & PADS_DOWN) {
|
||||
view_move_focus(v_browser, columns_current);
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
app_entry *browser_sel(void) {
|
||||
if ((entry_count < 1) || (v_browser->focus < AE_OFFSET))
|
||||
return NULL;
|
||||
|
||||
u32 i;
|
||||
if (first_set)
|
||||
i = top_offset + v_browser->focus - AE_OFFSET;
|
||||
else
|
||||
i = top_offset + v_browser->focus - (MAX_COLUMNS * ROWS) - AE_OFFSET;
|
||||
|
||||
return entries[i];
|
||||
}
|
||||
|
||||
void browser_switch_mode(void) {
|
||||
const app_entry *app = browser_sel();
|
||||
int mode = 0;
|
||||
|
||||
if (columns_current == 1) {
|
||||
if (widescreen)
|
||||
columns_new = 4;
|
||||
else
|
||||
columns_new = 3;
|
||||
|
||||
mode = 1;
|
||||
} else {
|
||||
columns_new = 1;
|
||||
}
|
||||
|
||||
if (v_browser)
|
||||
browser_gen_view(BA_REFRESH, app);
|
||||
|
||||
if (settings.browse_mode != mode)
|
||||
settings.browse_mode = mode;
|
||||
}
|
||||
|
||||
26
channel/channelapp/source/browser.h
Normal file
26
channel/channelapp/source/browser.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#ifndef _BROWSER_H_
|
||||
#define _BROWSER_H_
|
||||
|
||||
#include <gctypes.h>
|
||||
|
||||
#include "view.h"
|
||||
|
||||
typedef enum {
|
||||
BA_ADD = 0,
|
||||
BA_REMOVE,
|
||||
BA_REFRESH,
|
||||
BA_NEXT,
|
||||
BA_PREV
|
||||
} browser_action;
|
||||
|
||||
view * browser_init(void);
|
||||
void browser_deinit(void);
|
||||
void browser_theme_reinit(void);
|
||||
|
||||
void browser_gen_view(browser_action action, const app_entry *app);
|
||||
void browser_set_focus(u32 bd);
|
||||
app_entry *browser_sel(void);
|
||||
void browser_switch_mode(void);
|
||||
|
||||
#endif
|
||||
|
||||
233
channel/channelapp/source/bubbles.c
Normal file
233
channel/channelapp/source/bubbles.c
Normal file
@@ -0,0 +1,233 @@
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "../config.h"
|
||||
#include "gfx.h"
|
||||
#include "theme.h"
|
||||
|
||||
#include "bubbles.h"
|
||||
|
||||
#include <ogc/lwp_watchdog.h>
|
||||
|
||||
#define BUBBLE_DELTA (MAX_BUBBLE_COUNT-MIN_BUBBLE_COUNT)
|
||||
|
||||
typedef struct {
|
||||
float x;
|
||||
float py;
|
||||
float speed;
|
||||
float xm;
|
||||
float val;
|
||||
float step;
|
||||
int popped;
|
||||
int popcnt;
|
||||
int tex;
|
||||
} bubble;
|
||||
|
||||
static gfx_entity *tex_bubbles[3];
|
||||
|
||||
static gfx_queue_entry entries_bubbles[MAX_BUBBLE_COUNT];
|
||||
static gfx_queue_entry entries_sub_bubbles[MAX_BUBBLE_COUNT][BUBBLE_POP_MAX];
|
||||
|
||||
static bubble bubbles[MAX_BUBBLE_COUNT];
|
||||
static bubble sub_bubbles[MAX_BUBBLE_COUNT][BUBBLE_POP_MAX];
|
||||
|
||||
static int bubble_count = -1;
|
||||
|
||||
static void bubble_rand(int i) {
|
||||
int tex;
|
||||
|
||||
tex = IRAND (3);
|
||||
|
||||
bubbles[i].x = IRAND (view_width);
|
||||
bubbles[i].py = view_height + IRAND (200);
|
||||
bubbles[i].speed = 1.2 + FRAND (4 - tex);
|
||||
bubbles[i].xm = 3.0 + (IRAND ((tex + 1)) * bubbles[i].speed);
|
||||
bubbles[i].val = 0;
|
||||
bubbles[i].step = M_TWOPI / (64 + FRAND (64.0));
|
||||
bubbles[i].popped = 0;
|
||||
bubbles[i].popcnt = 0;
|
||||
bubbles[i].tex = tex;
|
||||
|
||||
gfx_qe_entity(&entries_bubbles[i], tex_bubbles[tex], bubbles[i].x,
|
||||
bubbles[i].py, -2, COL_DEFAULT);
|
||||
|
||||
entries_bubbles[i].entity.scale = BUBBLE_SIZE_MIN +
|
||||
FRAND (BUBBLE_SIZE_MAX - BUBBLE_SIZE_MIN);
|
||||
entries_bubbles[i].entity.rad = FRAND (M_PI_4);
|
||||
}
|
||||
|
||||
static void bubble_update_count(void) {
|
||||
static int div = 0;
|
||||
s32 minute;
|
||||
static int new_count;
|
||||
int t;
|
||||
|
||||
// time() might be expensive due to RTC reading
|
||||
// so slow it down a bit
|
||||
if ((div++ >= 600) || (bubble_count < 0)) {
|
||||
div = 0;
|
||||
t = time(NULL);
|
||||
minute = (t / 60 - BUBBLE_MIN_TIME) % BUBBLE_TIME_CYCLE;
|
||||
|
||||
if (minute <= BUBBLE_MAX_OFFSET)
|
||||
new_count = (BUBBLE_DELTA * minute / BUBBLE_MAX_OFFSET) +
|
||||
MIN_BUBBLE_COUNT;
|
||||
else
|
||||
new_count = (BUBBLE_DELTA * (BUBBLE_TIME_CYCLE - minute) /
|
||||
(BUBBLE_TIME_CYCLE - BUBBLE_MAX_OFFSET)) +
|
||||
MIN_BUBBLE_COUNT;
|
||||
|
||||
if (new_count < MIN_BUBBLE_COUNT) // should never happen
|
||||
new_count = MIN_BUBBLE_COUNT;
|
||||
|
||||
if (new_count > MAX_BUBBLE_COUNT) // should never happen
|
||||
new_count = MAX_BUBBLE_COUNT;
|
||||
}
|
||||
|
||||
if (((div % 6) == 0) || (bubble_count < 0)) {
|
||||
if (bubble_count < 0)
|
||||
bubble_count = 0;
|
||||
if (bubble_count < new_count) {
|
||||
while (bubble_count < new_count)
|
||||
bubble_rand(bubble_count++);
|
||||
} else if (bubble_count > new_count)
|
||||
bubble_count--;
|
||||
}
|
||||
}
|
||||
|
||||
static void bubble_pop(int i) {
|
||||
int j;
|
||||
bubbles[i].popped = 1;
|
||||
bubbles[i].popcnt = IRAND(BUBBLE_POP_MAX - BUBBLE_POP_MIN) + BUBBLE_POP_MIN;
|
||||
entries_bubbles[i].entity.color = 0x00000000;
|
||||
|
||||
for (j = 0; j < bubbles[i].popcnt; j++) {
|
||||
int tex;
|
||||
float sa;
|
||||
float dx,dy;
|
||||
|
||||
sa = FRAND(M_TWOPI);
|
||||
dx = sin(sa) * FRAND(BUBBLE_POP_SPREAD_X);
|
||||
dy = (cos(sa) - 1.5) * FRAND(BUBBLE_POP_SPREAD_Y);
|
||||
|
||||
tex = bubbles[i].tex;
|
||||
|
||||
sub_bubbles[i][j].x = bubbles[i].x + dx;
|
||||
sub_bubbles[i][j].py = entries_bubbles[i].entity.coords.y + dy;
|
||||
sub_bubbles[i][j].speed = bubbles[i].speed - 0.5 + FRAND (4 - tex);
|
||||
sub_bubbles[i][j].xm = bubbles[i].xm * (0.8 + FRAND(1.0));
|
||||
sub_bubbles[i][j].val = bubbles[i].val;
|
||||
sub_bubbles[i][j].step = bubbles[i].step * (0.8 + FRAND(0.4));
|
||||
sub_bubbles[i][j].popped = 0;
|
||||
sub_bubbles[i][j].popcnt = 0;
|
||||
|
||||
gfx_qe_entity (&entries_sub_bubbles[i][j], tex_bubbles[tex],
|
||||
sub_bubbles[i][j].x, sub_bubbles[i][j].py,
|
||||
-2, COL_DEFAULT);
|
||||
|
||||
entries_sub_bubbles[i][j].entity.scale = (BUBBLE_POP_SIZE_MIN +
|
||||
FRAND (BUBBLE_POP_SIZE_MAX - BUBBLE_POP_SIZE_MIN)) *
|
||||
entries_bubbles[i].entity.scale;
|
||||
entries_sub_bubbles[i][j].entity.rad = entries_bubbles[i].entity.rad;
|
||||
}
|
||||
}
|
||||
|
||||
void bubbles_init(void) {
|
||||
srand (gettime ());
|
||||
bubbles_theme_reinit();
|
||||
bubble_update_count();
|
||||
}
|
||||
|
||||
void bubbles_deinit(void) {
|
||||
}
|
||||
|
||||
void bubbles_theme_reinit(void) {
|
||||
tex_bubbles[0] = theme_gfx[THEME_BUBBLE1];
|
||||
tex_bubbles[1] = theme_gfx[THEME_BUBBLE2];
|
||||
tex_bubbles[2] = theme_gfx[THEME_BUBBLE3];
|
||||
|
||||
bubble_count = -1;
|
||||
bubble_update_count();
|
||||
}
|
||||
|
||||
void bubble_update(bool wm, s32 x, s32 y) {
|
||||
int i, j, deadcnt;
|
||||
gfx_coordinates *coords;
|
||||
f32 radius;
|
||||
|
||||
bubble_update_count();
|
||||
|
||||
for (i = 0; i < bubble_count; ++i) {
|
||||
coords = &entries_bubbles[i].entity.coords;
|
||||
radius = entries_bubbles[i].entity.scale *
|
||||
entries_bubbles[i].entity.entity->w / 2 * BUBBLE_POP_RADIUS;
|
||||
if (!bubbles[i].popped && wm) {
|
||||
float cx = coords->x + entries_bubbles[i].entity.entity->w/2;
|
||||
float cy = coords->y - entries_bubbles[i].entity.entity->h/2;
|
||||
if ((abs(x - cx) < radius) && (abs(y - cy) < radius))
|
||||
bubble_pop(i);
|
||||
}
|
||||
|
||||
if (bubbles[i].popped) {
|
||||
deadcnt = 0;
|
||||
|
||||
for (j = 0; j < bubbles[i].popcnt; j++) {
|
||||
coords = &entries_sub_bubbles[i][j].entity.coords;
|
||||
radius = entries_sub_bubbles[i][j].entity.scale *
|
||||
entries_sub_bubbles[i][j].entity.entity->w / 2 *
|
||||
BUBBLE_POP_RADIUS;
|
||||
|
||||
if (!sub_bubbles[i][j].popped && wm) {
|
||||
float cx = coords->x + entries_bubbles[i].entity.entity->w/2;
|
||||
float cy = coords->y - entries_bubbles[i].entity.entity->h/2;
|
||||
if ((abs(x - cx) < radius) && (abs(y - cy) < radius)) {
|
||||
entries_sub_bubbles[i][j].entity.color = 0x00000000;
|
||||
sub_bubbles[i][j].popped = 1;
|
||||
}
|
||||
}
|
||||
|
||||
sub_bubbles[i][j].py -= sub_bubbles[i][j].speed;
|
||||
coords->y = sub_bubbles[i][j].py;
|
||||
|
||||
if ((coords->y < -100) || sub_bubbles[i][j].popped) {
|
||||
deadcnt++;
|
||||
continue;
|
||||
}
|
||||
|
||||
coords->x = sub_bubbles[i][j].x + roundf (sub_bubbles[i][j].xm *
|
||||
sinf (sub_bubbles[i][j].val));
|
||||
sub_bubbles[i][j].val += sub_bubbles[i][j].step;
|
||||
}
|
||||
|
||||
if(deadcnt >= bubbles[i].popcnt) {
|
||||
bubble_rand(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
gfx_frame_push (entries_sub_bubbles[i], bubbles[i].popcnt);
|
||||
} else {
|
||||
bubbles[i].py -= bubbles[i].speed;
|
||||
coords->y = bubbles[i].py;
|
||||
|
||||
if (coords->y < -100) {
|
||||
bubble_rand(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
coords->x = bubbles[i].x + roundf (bubbles[i].xm *
|
||||
sinf (bubbles[i].val));
|
||||
bubbles[i].val += bubbles[i].step;
|
||||
}
|
||||
}
|
||||
|
||||
gfx_frame_push(entries_bubbles, bubble_count);
|
||||
}
|
||||
|
||||
void bubble_popall(void) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < bubble_count; ++i)
|
||||
if (!bubbles[i].popped)
|
||||
bubble_pop(i);
|
||||
}
|
||||
|
||||
15
channel/channelapp/source/bubbles.h
Normal file
15
channel/channelapp/source/bubbles.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#ifndef _BUBBLES_H_
|
||||
#define _BUBBLES_H_
|
||||
|
||||
#include <gctypes.h>
|
||||
|
||||
void bubbles_init(void);
|
||||
void bubbles_deinit(void);
|
||||
void bubbles_theme_reinit(void);
|
||||
|
||||
void bubble_update(bool wm, s32 x, s32 y);
|
||||
|
||||
void bubble_popall(void);
|
||||
|
||||
#endif
|
||||
|
||||
240
channel/channelapp/source/controls.c
Normal file
240
channel/channelapp/source/controls.c
Normal file
@@ -0,0 +1,240 @@
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <ogcsys.h>
|
||||
#include <ogc/lwp_watchdog.h>
|
||||
|
||||
#include "../config.h"
|
||||
|
||||
#include "controls.h"
|
||||
#include "gfx.h"
|
||||
|
||||
static s32 pointer_owner = -1;
|
||||
WPADData *wpads[WPAD_MAX_WIIMOTES];
|
||||
|
||||
static int rumbling = 0;
|
||||
|
||||
void controls_init (void) {
|
||||
int i;
|
||||
|
||||
i = WPAD_Init ();
|
||||
|
||||
if(i < 0) {
|
||||
gprintf("WPAD_Init failed: %d\n",i);
|
||||
return;
|
||||
}
|
||||
|
||||
for(i=0;i<WPAD_MAX_WIIMOTES;i++) {
|
||||
WPAD_SetDataFormat (i, WPAD_FMT_BTNS_ACC_IR);
|
||||
WPAD_SetVRes (i, view_width + 128, view_height + 128);
|
||||
}
|
||||
WPAD_SetIdleTimeout (120);
|
||||
}
|
||||
|
||||
void controls_deinit (void) {
|
||||
WPAD_Shutdown ();
|
||||
}
|
||||
|
||||
static u32 wpad_button_transform(WPADData *wd, u32 btns) {
|
||||
btns &= ~PADW_BUTTON_NET_INIT;
|
||||
btns &= ~PADW_BUTTON_SCREENSHOT;
|
||||
|
||||
switch(wd->exp.type) {
|
||||
case WPAD_EXP_NUNCHUK:
|
||||
if(btns & WPAD_NUNCHUK_BUTTON_Z)
|
||||
btns |= PADW_BUTTON_NET_INIT;
|
||||
if(btns & WPAD_NUNCHUK_BUTTON_C)
|
||||
btns |= PADW_BUTTON_SCREENSHOT;
|
||||
break;
|
||||
case WPAD_EXP_CLASSIC:
|
||||
if(btns & WPAD_CLASSIC_BUTTON_LEFT)
|
||||
btns |= WPAD_BUTTON_LEFT;
|
||||
if(btns & WPAD_CLASSIC_BUTTON_RIGHT)
|
||||
btns |= WPAD_BUTTON_RIGHT;
|
||||
if(btns & WPAD_CLASSIC_BUTTON_UP)
|
||||
btns |= WPAD_BUTTON_UP;
|
||||
if(btns & WPAD_CLASSIC_BUTTON_DOWN)
|
||||
btns |= WPAD_BUTTON_DOWN;
|
||||
if(btns & WPAD_CLASSIC_BUTTON_A)
|
||||
btns |= WPAD_BUTTON_A;
|
||||
if(btns & WPAD_CLASSIC_BUTTON_B)
|
||||
btns |= WPAD_BUTTON_B;
|
||||
if(btns & WPAD_CLASSIC_BUTTON_X)
|
||||
btns |= WPAD_BUTTON_1;
|
||||
if(btns & WPAD_CLASSIC_BUTTON_Y)
|
||||
btns |= WPAD_BUTTON_2;
|
||||
if((btns & WPAD_CLASSIC_BUTTON_FULL_L) || (btns & WPAD_CLASSIC_BUTTON_MINUS))
|
||||
btns |= WPAD_BUTTON_MINUS;
|
||||
if((btns & WPAD_CLASSIC_BUTTON_FULL_R) || (btns & WPAD_CLASSIC_BUTTON_PLUS))
|
||||
btns |= WPAD_BUTTON_PLUS;
|
||||
if(btns & WPAD_CLASSIC_BUTTON_HOME)
|
||||
btns |= WPAD_BUTTON_HOME;
|
||||
if(btns & WPAD_CLASSIC_BUTTON_ZR)
|
||||
btns |= PADW_BUTTON_NET_INIT;
|
||||
if(btns & WPAD_CLASSIC_BUTTON_ZL)
|
||||
btns |= PADW_BUTTON_SCREENSHOT;
|
||||
break;
|
||||
case WPAD_EXP_GUITARHERO3:
|
||||
if(btns & WPAD_GUITAR_HERO_3_BUTTON_STRUM_UP)
|
||||
btns |= WPAD_BUTTON_UP;
|
||||
if(btns & WPAD_GUITAR_HERO_3_BUTTON_STRUM_DOWN)
|
||||
btns |= WPAD_BUTTON_DOWN;
|
||||
if(btns & WPAD_GUITAR_HERO_3_BUTTON_GREEN)
|
||||
btns |= WPAD_BUTTON_A;
|
||||
if(btns & WPAD_GUITAR_HERO_3_BUTTON_RED)
|
||||
btns |= WPAD_BUTTON_B;
|
||||
if(btns & WPAD_GUITAR_HERO_3_BUTTON_PLUS)
|
||||
btns |= WPAD_BUTTON_PLUS;
|
||||
if(btns & WPAD_GUITAR_HERO_3_BUTTON_MINUS)
|
||||
btns |= WPAD_BUTTON_MINUS;
|
||||
if(btns & WPAD_GUITAR_HERO_3_BUTTON_YELLOW)
|
||||
btns |= WPAD_BUTTON_LEFT;
|
||||
if(btns & WPAD_GUITAR_HERO_3_BUTTON_BLUE)
|
||||
btns |= WPAD_BUTTON_RIGHT;
|
||||
if(btns & WPAD_GUITAR_HERO_3_BUTTON_ORANGE)
|
||||
btns |= WPAD_BUTTON_1;
|
||||
break;
|
||||
}
|
||||
return (btns&0xffff) << 16;
|
||||
}
|
||||
|
||||
void controls_scan (u32 *down, u32 *held, u32 *up) {
|
||||
u32 bd, bh, bu;
|
||||
int i;
|
||||
s32 last_owner;
|
||||
|
||||
PAD_ScanPads ();
|
||||
|
||||
bd = PAD_ButtonsDown (0);
|
||||
bh = PAD_ButtonsHeld (0);
|
||||
bu = PAD_ButtonsUp (0);
|
||||
|
||||
WPAD_ScanPads ();
|
||||
|
||||
for(i=0;i<WPAD_MAX_WIIMOTES;i++)
|
||||
if(WPAD_Probe (i, NULL) == WPAD_ERR_NONE) {
|
||||
wpads[i] = WPAD_Data(i);
|
||||
} else {
|
||||
wpads[i] = NULL;
|
||||
}
|
||||
|
||||
last_owner = pointer_owner;
|
||||
|
||||
// kill pointer owner if it stops pointing
|
||||
if((pointer_owner >= 0) && (!wpads[pointer_owner] || !wpads[pointer_owner]->ir.valid)) {
|
||||
pointer_owner = -1;
|
||||
}
|
||||
|
||||
// find a new pointer owner if necessary
|
||||
if(pointer_owner < 0) {
|
||||
for(i=0;i<WPAD_MAX_WIIMOTES;i++) {
|
||||
if(wpads[i] && wpads[i]->ir.valid) {
|
||||
pointer_owner = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// pointer owner owns buttons
|
||||
if(pointer_owner >= 0) {
|
||||
bd |= wpad_button_transform(wpads[pointer_owner], wpads[pointer_owner]->btns_d);
|
||||
bh |= wpad_button_transform(wpads[pointer_owner], wpads[pointer_owner]->btns_h);
|
||||
bu |= wpad_button_transform(wpads[pointer_owner], wpads[pointer_owner]->btns_u);
|
||||
} else {
|
||||
// otherwise just mix all buttons together
|
||||
for(i=0;i<WPAD_MAX_WIIMOTES;i++) {
|
||||
if(wpads[i]) {
|
||||
bd |= wpad_button_transform(wpads[i], wpads[i]->btns_d);
|
||||
bh |= wpad_button_transform(wpads[i], wpads[i]->btns_h);
|
||||
bu |= wpad_button_transform(wpads[i], wpads[i]->btns_u);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(last_owner >= 0 && last_owner != pointer_owner && rumbling) {
|
||||
WPAD_Rumble (last_owner, 0);
|
||||
if(pointer_owner >= 0)
|
||||
WPAD_Rumble(pointer_owner, 1);
|
||||
}
|
||||
|
||||
if (down)
|
||||
*down = bd;
|
||||
|
||||
if (held)
|
||||
*held = bh;
|
||||
|
||||
if (up)
|
||||
*up = bu;
|
||||
}
|
||||
|
||||
bool controls_ir (s32 *x, s32 *y, f32 *roll) {
|
||||
if (pointer_owner >= 0 && wpads[pointer_owner] && wpads[pointer_owner]->ir.valid) {
|
||||
*x = wpads[pointer_owner]->ir.x - 64;
|
||||
*y = wpads[pointer_owner]->ir.y - 64;
|
||||
|
||||
if(roll)
|
||||
*roll = -wpads[pointer_owner]->ir.angle / 180.0 * M_PI;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void controls_rumble(int rumble) {
|
||||
if(pointer_owner >= 0)
|
||||
WPAD_Rumble(pointer_owner, rumble);
|
||||
rumbling = rumble;
|
||||
}
|
||||
|
||||
static s32 wpad_sticky(int chan) {
|
||||
s32 sy = 0;
|
||||
if(wpads[chan]) {
|
||||
switch(wpads[chan]->exp.type) {
|
||||
case WPAD_EXP_NUNCHUK:
|
||||
sy = wpads[chan]->exp.nunchuk.js.pos.y - wpads[chan]->exp.nunchuk.js.center.y;
|
||||
break;
|
||||
case WPAD_EXP_CLASSIC:
|
||||
sy = (wpads[chan]->exp.classic.ljs.pos.y - wpads[chan]->exp.classic.ljs.center.y) * 4;
|
||||
break;
|
||||
case WPAD_EXP_GUITARHERO3:
|
||||
sy = (wpads[chan]->exp.gh3.js.pos.y - wpads[chan]->exp.gh3.js.center.y) * 4;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return sy;
|
||||
}
|
||||
|
||||
#define DEADZONE 10
|
||||
|
||||
s32 deadzone(s32 v) {
|
||||
if(v > DEADZONE)
|
||||
return v - DEADZONE;
|
||||
if(v < -DEADZONE)
|
||||
return v + DEADZONE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
s32 controls_sticky(void) {
|
||||
s32 sy;
|
||||
int i;
|
||||
sy = deadzone(PAD_StickY(0));
|
||||
if(pointer_owner >= 0) {
|
||||
sy += deadzone(wpad_sticky(pointer_owner));
|
||||
} else {
|
||||
for(i=0;i<WPAD_MAX_WIIMOTES;i++) {
|
||||
sy += deadzone(wpad_sticky(i));
|
||||
}
|
||||
}
|
||||
return sy;
|
||||
}
|
||||
|
||||
void controls_set_ir_threshold(int on) {
|
||||
int i;
|
||||
for(i=0;i<WPAD_MAX_WIIMOTES;i++) {
|
||||
if(on)
|
||||
WPAD_SetIdleThresholds(i, WPAD_THRESH_DEFAULT_BUTTONS, 10, WPAD_THRESH_DEFAULT_ACCEL, WPAD_THRESH_DEFAULT_JOYSTICK, WPAD_THRESH_DEFAULT_BALANCEBOARD, WPAD_THRESH_DEFAULT_MOTION_PLUS);
|
||||
else
|
||||
WPAD_SetIdleThresholds(i, WPAD_THRESH_DEFAULT_BUTTONS, WPAD_THRESH_DEFAULT_IR, WPAD_THRESH_DEFAULT_ACCEL, WPAD_THRESH_DEFAULT_JOYSTICK, WPAD_THRESH_DEFAULT_BALANCEBOARD, WPAD_THRESH_DEFAULT_MOTION_PLUS);
|
||||
}
|
||||
}
|
||||
36
channel/channelapp/source/controls.h
Normal file
36
channel/channelapp/source/controls.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#ifndef _CONTROLS_H_
|
||||
#define _CONTROLS_H_
|
||||
|
||||
#include <gctypes.h>
|
||||
#include <wiiuse/wpad.h>
|
||||
|
||||
// fake wiimote buttons for mapping extension buttons
|
||||
#define PADW_BUTTON_NET_INIT 0x2000
|
||||
#define PADW_BUTTON_SCREENSHOT 0x4000
|
||||
|
||||
#define PADS_A (PAD_BUTTON_A | (WPAD_BUTTON_A << 16))
|
||||
#define PADS_B (PAD_BUTTON_B | (WPAD_BUTTON_B << 16))
|
||||
#define PADS_MINUS (PAD_TRIGGER_L | (WPAD_BUTTON_MINUS << 16))
|
||||
#define PADS_PLUS (PAD_TRIGGER_R | (WPAD_BUTTON_PLUS << 16))
|
||||
#define PADS_1 (PAD_BUTTON_X | (WPAD_BUTTON_1 << 16))
|
||||
#define PADS_2 (PAD_BUTTON_Y | (WPAD_BUTTON_2 << 16))
|
||||
#define PADS_HOME (PAD_BUTTON_START | (WPAD_BUTTON_HOME << 16))
|
||||
#define PADS_UP (PAD_BUTTON_UP | (WPAD_BUTTON_UP << 16))
|
||||
#define PADS_DOWN (PAD_BUTTON_DOWN | (WPAD_BUTTON_DOWN << 16))
|
||||
#define PADS_LEFT (PAD_BUTTON_LEFT | (WPAD_BUTTON_LEFT << 16))
|
||||
#define PADS_RIGHT (PAD_BUTTON_RIGHT | (WPAD_BUTTON_RIGHT << 16))
|
||||
#define PADS_DPAD (PADS_UP | PADS_DOWN | PADS_LEFT | PADS_RIGHT)
|
||||
#define PADS_NET_INIT (PAD_TRIGGER_Z | (PADW_BUTTON_NET_INIT << 16))
|
||||
#define PADS_SCREENSHOT (PADW_BUTTON_SCREENSHOT << 16)
|
||||
|
||||
void controls_init (void);
|
||||
void controls_deinit (void);
|
||||
|
||||
void controls_scan (u32 *down, u32 *held, u32 *up);
|
||||
bool controls_ir (s32 *x, s32 *y, f32 *roll);
|
||||
void controls_rumble(int rumble);
|
||||
s32 controls_sticky(void);
|
||||
void controls_set_ir_threshold(int on);
|
||||
|
||||
#endif
|
||||
|
||||
55
channel/channelapp/source/cursors.c
Normal file
55
channel/channelapp/source/cursors.c
Normal file
@@ -0,0 +1,55 @@
|
||||
#include "../config.h"
|
||||
|
||||
#include <gctypes.h>
|
||||
|
||||
#include "cursor_drag_png.h"
|
||||
#include "cursor_drag_shade_png.h"
|
||||
#include "cursor_pic_png.h"
|
||||
#include "cursor_shade_png.h"
|
||||
|
||||
#include "cursors.h"
|
||||
|
||||
static cursor cursors[CUR_MAX];
|
||||
|
||||
void cursors_init (void) {
|
||||
cursors[CUR_STD].tex[0] = tex_from_png (cursor_shade_png,
|
||||
cursor_shade_png_size, 96,96);
|
||||
cursors[CUR_STD].tex[1] = tex_from_png (cursor_pic_png, cursor_pic_png_size,
|
||||
96, 96);
|
||||
|
||||
cursors[CUR_STD].hotspot_x = cursors[CUR_STD].tex[1]->w / 2;
|
||||
cursors[CUR_STD].hotspot_y = cursors[CUR_STD].tex[1]->h / 2;
|
||||
|
||||
cursors[CUR_DRAG].tex[0] = tex_from_png (cursor_drag_shade_png,
|
||||
cursor_drag_shade_png_size,
|
||||
96, 96);
|
||||
cursors[CUR_DRAG].tex[1] = tex_from_png (cursor_drag_png,
|
||||
cursor_drag_png_size, 96, 96);
|
||||
cursors[CUR_DRAG].hotspot_x = cursors[CUR_DRAG].tex[1]->w / 2;
|
||||
cursors[CUR_DRAG].hotspot_y = cursors[CUR_DRAG].tex[1]->h / 2;
|
||||
}
|
||||
|
||||
void cursors_deinit (void) {
|
||||
u8 i;
|
||||
|
||||
for (i = 0; i < CUR_MAX; ++i) {
|
||||
tex_free (cursors[i].tex[0]);
|
||||
tex_free (cursors[i].tex[1]);
|
||||
}
|
||||
}
|
||||
|
||||
void cursors_queue (gfx_queue_entry *queue, cursor_type type, s16 x, s16 y,
|
||||
f32 roll) {
|
||||
gfx_qe_entity (&queue[0], cursors[type].tex[0],
|
||||
x - cursors[type].hotspot_x + 2,
|
||||
y - cursors[type].hotspot_y + 4, TEX_LAYER_CURSOR,
|
||||
COL_DEFAULT);
|
||||
gfx_qe_entity (&queue[1], cursors[type].tex[1],
|
||||
x - cursors[type].hotspot_x,
|
||||
y - cursors[type].hotspot_y, TEX_LAYER_CURSOR + 1,
|
||||
COL_DEFAULT);
|
||||
|
||||
queue[0].entity.rad = roll;
|
||||
queue[1].entity.rad = roll;
|
||||
}
|
||||
|
||||
28
channel/channelapp/source/cursors.h
Normal file
28
channel/channelapp/source/cursors.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#ifndef _CURSORS_H_
|
||||
#define _CURSORS_H_
|
||||
|
||||
#include <gctypes.h>
|
||||
|
||||
#include "gfx.h"
|
||||
#include "tex.h"
|
||||
|
||||
typedef enum {
|
||||
CUR_STD = 0,
|
||||
CUR_DRAG,
|
||||
|
||||
CUR_MAX
|
||||
} cursor_type;
|
||||
|
||||
typedef struct {
|
||||
gfx_entity *tex[2];
|
||||
|
||||
s16 hotspot_x, hotspot_y;
|
||||
} cursor;
|
||||
|
||||
void cursors_init (void);
|
||||
void cursors_deinit (void);
|
||||
void cursors_queue (gfx_queue_entry *queue, cursor_type type, s16 x, s16 y,
|
||||
f32 roll);
|
||||
|
||||
#endif
|
||||
|
||||
88
channel/channelapp/source/debug.c
Normal file
88
channel/channelapp/source/debug.c
Normal file
@@ -0,0 +1,88 @@
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include <ogcsys.h>
|
||||
#include <ogc/machine/processor.h>
|
||||
|
||||
#include "../config.h"
|
||||
|
||||
#ifdef DEBUG_APP
|
||||
|
||||
//#define DEBUG_MEMSTATS
|
||||
|
||||
static int gprintf_enabled = 1;
|
||||
|
||||
void gprintf_enable(int enable) {
|
||||
gprintf_enabled = enable;
|
||||
}
|
||||
|
||||
int gprintf(const char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
u32 level;
|
||||
int ret;
|
||||
|
||||
if (!gprintf_enabled)
|
||||
return 0;
|
||||
|
||||
level = IRQ_Disable();
|
||||
va_start(ap, format);
|
||||
ret = vprintf(format, ap);
|
||||
va_end(ap);
|
||||
IRQ_Restore(level);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/********* you know you love it **********/
|
||||
static char ascii(char s) {
|
||||
if(s < 0x20) return '.';
|
||||
if(s > 0x7E) return '.';
|
||||
return s;
|
||||
}
|
||||
|
||||
void hexdump(const void *d, int len) {
|
||||
u8 *data;
|
||||
int i, off;
|
||||
data = (u8*)d;
|
||||
for (off=0; off<len; off += 16) {
|
||||
gprintf("%08x ",off);
|
||||
for(i=0; i<16; i++)
|
||||
if((i+off)>=len) gprintf(" ");
|
||||
else gprintf("%02x ",data[off+i]);
|
||||
|
||||
gprintf(" ");
|
||||
for(i=0; i<16; i++)
|
||||
if((i+off)>=len) gprintf(" ");
|
||||
else gprintf("%c",ascii(data[off+i]));
|
||||
gprintf("\n");
|
||||
}
|
||||
}
|
||||
/********* you know you love it **********/
|
||||
|
||||
#ifndef UINT_MAX
|
||||
#define UINT_MAX ((u32)((s32)-1))
|
||||
#endif
|
||||
void memstats(int reset) {
|
||||
#ifdef DEBUG_MEMSTATS
|
||||
static u32 min_free = UINT_MAX;
|
||||
static u32 temp_free;
|
||||
static u32 level;
|
||||
|
||||
if (reset)
|
||||
min_free = UINT_MAX;
|
||||
|
||||
_CPU_ISR_Disable(level);
|
||||
|
||||
temp_free = (u32) SYS_GetArena2Hi() - (u32) SYS_GetArena2Lo();
|
||||
|
||||
_CPU_ISR_Restore(level);
|
||||
|
||||
if (temp_free < min_free) {
|
||||
min_free = temp_free;
|
||||
gprintf("MEM2 free: %8u\n", min_free);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
623
channel/channelapp/source/dialogs.c
Normal file
623
channel/channelapp/source/dialogs.c
Normal file
@@ -0,0 +1,623 @@
|
||||
#include <string.h>
|
||||
#include <malloc.h>
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
|
||||
#include <ogcsys.h>
|
||||
|
||||
#include "../config.h"
|
||||
#include "controls.h"
|
||||
#include "theme.h"
|
||||
#include "font.h"
|
||||
#include "widgets.h"
|
||||
#include "view.h"
|
||||
#include "xml.h"
|
||||
#include "panic.h"
|
||||
|
||||
#include "dialogs.h"
|
||||
|
||||
#include "i18n.h"
|
||||
|
||||
#define TRANS_STEPS 15
|
||||
|
||||
static const char *app_entry_desc_default;
|
||||
|
||||
static const char *caption_info;
|
||||
static const char *caption_confirm;
|
||||
static const char *caption_warning;
|
||||
static const char *caption_error;
|
||||
static const char *caption_ok;
|
||||
static const char *caption_cancel;
|
||||
static const char *caption_yes;
|
||||
static const char *caption_no;
|
||||
static const char *caption_delete;
|
||||
static const char *caption_load;
|
||||
static const char *caption_back;
|
||||
static const char *caption_options;
|
||||
static const char *caption_device;
|
||||
static const char *caption_device_names[DEVICE_COUNT];
|
||||
static const char *caption_sort_by;
|
||||
static const char *caption_sort_name;
|
||||
static const char *caption_sort_date;
|
||||
|
||||
static const char *l_version;
|
||||
static const char *l_coder;
|
||||
|
||||
static const char *string_about_pre;
|
||||
static const char *string_about_post;
|
||||
static const char *string_about_trans;
|
||||
static const char *string_about_theme;
|
||||
static char *string_about_gen;
|
||||
|
||||
void dialogs_theme_reinit (void) {
|
||||
app_entry_desc_default = _("no description available");
|
||||
|
||||
caption_info = _("Information");
|
||||
caption_confirm = _("Confirmation");
|
||||
caption_warning = _("Warning");
|
||||
caption_error = _("Error");
|
||||
caption_ok = _("Ok");
|
||||
caption_cancel = _("Cancel");
|
||||
caption_yes = _("Yes");
|
||||
caption_no = _("No");
|
||||
caption_delete = _("Delete");
|
||||
caption_load = _("Load");
|
||||
caption_back = _("Back");
|
||||
caption_options = _("Options");
|
||||
caption_device = _("Device:");
|
||||
caption_device_names[0] = _("Internal SD Slot");
|
||||
caption_device_names[1] = _("USB device");
|
||||
caption_device_names[2] = _("SDGecko Slot A");
|
||||
caption_device_names[3] = _("SDGecko Slot B");
|
||||
caption_sort_by = _("Sort applications by:");
|
||||
caption_sort_name = _("Name");
|
||||
caption_sort_date = _("Date");
|
||||
|
||||
string_about_pre =
|
||||
"Credits\n\n"
|
||||
|
||||
"The Homebrew Channel was made possible by the following people:\n\n"
|
||||
|
||||
"dhewg (EWRONGCHAN) - application code, geckoloader code\n"
|
||||
"blasty (ESTONED) - application code\n"
|
||||
"marcan (EFAILURE) - reload stub, banner, installer, packaging\n"
|
||||
"bushing (EWANTPONY) - socket code, loader code\n"
|
||||
"segher (EBUGFOUND) - nandloader stub code\n"
|
||||
"souLLy (ENOTHERE) - banner graphics\n"
|
||||
"drmr (EMORECOWBELL) - banner audio, channel graphics\n"
|
||||
"mha (E404) - update server and hosting";
|
||||
|
||||
string_about_post =
|
||||
"Powered by devkitPPC and libogc, by shagkur, WinterMute, "
|
||||
"and everyone else who contributed\n\n"
|
||||
|
||||
"Thanks to all the beta testers\n\n"
|
||||
|
||||
"Kind regards to the following people too:\n\n"
|
||||
|
||||
"sepp256 - dropped some good GX hints\n"
|
||||
"chishm, svpe, rodries, hermes - libfat port\n"
|
||||
"alien - some graphics\n"
|
||||
"jodi - the lulz\n\n"
|
||||
|
||||
"And last but not least, thanks to the authors of the following libraries:\n\n"
|
||||
|
||||
"wiiuse - para's Wiimote library, now integrated with libogc\n"
|
||||
"libpng - the official PNG library\n"
|
||||
"Mini-XML - small and efficient XML parsing library\n"
|
||||
"FreeType - the free TrueType/OpenType font renderer\n";
|
||||
|
||||
string_about_trans = _("<YourLanguageHere> translation by <YourNickNameHere>");
|
||||
|
||||
if (!i18n_have_mo())
|
||||
string_about_trans = "";
|
||||
|
||||
string_about_theme = _("Theme:");
|
||||
|
||||
if (string_about_gen)
|
||||
free(string_about_gen);
|
||||
|
||||
string_about_gen = pmalloc(strlen(string_about_pre) +
|
||||
strlen(string_about_post) +
|
||||
strlen(string_about_trans) +
|
||||
strlen(string_about_theme) + 128);
|
||||
|
||||
l_version = _("Version: %s");
|
||||
l_coder = _("Author: %s");
|
||||
}
|
||||
|
||||
void dialogs_init (void) {
|
||||
string_about_gen = NULL;
|
||||
dialogs_theme_reinit();
|
||||
}
|
||||
|
||||
void dialogs_deinit (void) {
|
||||
free(string_about_gen);
|
||||
}
|
||||
|
||||
void dialog_fade (view *v, bool fade_in) {
|
||||
float val;
|
||||
float step;
|
||||
s16 y;
|
||||
float yf;
|
||||
u32 c;
|
||||
u8 stepa;
|
||||
u8 i;
|
||||
|
||||
if (fade_in) {
|
||||
val = 0;
|
||||
step = M_PI / (2 * TRANS_STEPS);
|
||||
y = v->coords.y + view_height;
|
||||
c = DIALOG_MASK_COLOR & 0xffffff00;
|
||||
stepa = (DIALOG_MASK_COLOR & 0xff) / TRANS_STEPS;
|
||||
} else {
|
||||
val = M_PI;
|
||||
step = M_PI / (2 * TRANS_STEPS);
|
||||
y = v->coords.y;
|
||||
c = DIALOG_MASK_COLOR;
|
||||
stepa = -(DIALOG_MASK_COLOR & 0xff) / TRANS_STEPS;
|
||||
}
|
||||
|
||||
yf = view_height;
|
||||
|
||||
for (i = 0; i < TRANS_STEPS; ++i) {
|
||||
v->coords.y = y - roundf (yf * sinf (val));
|
||||
val += step;
|
||||
c += stepa;
|
||||
|
||||
view_plot (v, c, NULL, NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
view * dialog_app (const app_entry *entry, const view *sub_view) {
|
||||
view *v;
|
||||
u16 x, gap;
|
||||
char *name;
|
||||
char coder[64];
|
||||
char version[64];
|
||||
const char *desc;
|
||||
u16 ym, hm, yb;
|
||||
|
||||
if (entry->meta && entry->meta->name)
|
||||
name = entry->meta->name;
|
||||
else
|
||||
name = entry->dirname;
|
||||
|
||||
if (entry->meta && entry->meta->coder)
|
||||
snprintf (coder, sizeof (coder), l_coder, entry->meta->coder);
|
||||
else
|
||||
*coder = 0;
|
||||
|
||||
if (entry->meta && entry->meta->version)
|
||||
snprintf (version, sizeof (version), l_version, entry->meta->version);
|
||||
else
|
||||
*version = 0;
|
||||
|
||||
if (entry->meta && entry->meta->long_description)
|
||||
desc = entry->meta->long_description;
|
||||
else
|
||||
desc = app_entry_desc_default;
|
||||
|
||||
v = view_new (11, sub_view, (view_width - theme_gfx[THEME_DIALOG]->w) / 2,
|
||||
44, TEX_LAYER_DIALOGS, PADS_B);
|
||||
|
||||
widget_image(&v->widgets[0], 0, 0, 0, theme_gfx[THEME_DIALOG],
|
||||
NULL, false, NULL);
|
||||
|
||||
widget_label (&v->widgets[1], 32, 16, 1, name,
|
||||
theme_gfx[THEME_DIALOG]->w - 64, FA_CENTERED, FA_ASCENDER,
|
||||
FONT_DLGTITLE);
|
||||
|
||||
if (entry->icon)
|
||||
widget_image(&v->widgets[2], 32, 48, 1, entry->icon, NULL, false, NULL);
|
||||
|
||||
widget_label (&v->widgets[3], 48 + APP_ENTRY_ICON_WIDTH, 72, 1, version,
|
||||
theme_gfx[THEME_DIALOG]->w - 72 - APP_ENTRY_ICON_WIDTH,
|
||||
FA_LEFT, FA_DESCENDER, FONT_LABEL);
|
||||
|
||||
widget_label (&v->widgets[4], 48 + APP_ENTRY_ICON_WIDTH, 72, 1, coder,
|
||||
theme_gfx[THEME_DIALOG]->w - 72 - APP_ENTRY_ICON_WIDTH,
|
||||
FA_LEFT, FA_ASCENDER, FONT_LABEL);
|
||||
|
||||
yb = theme_gfx[THEME_DIALOG]->h - theme_gfx[THEME_BUTTON_TINY]->h - 16;
|
||||
ym = 48 + APP_ENTRY_ICON_HEIGHT + 8;
|
||||
hm = yb - ym - 8;
|
||||
|
||||
widget_memo_deco (&v->widgets[5], 32, ym, 1,
|
||||
theme_gfx[THEME_DIALOG]->w - 64, hm, desc, FA_LEFT);
|
||||
|
||||
gap = (theme_gfx[THEME_DIALOG]->w -
|
||||
theme_gfx[THEME_BUTTON_TINY]->w * 3) / 4;
|
||||
|
||||
x = gap;
|
||||
widget_button (&v->widgets[8], x, yb, 1, BTN_TINY, caption_delete);
|
||||
|
||||
x += gap + theme_gfx[THEME_BUTTON_TINY]->w;
|
||||
widget_button (&v->widgets[9], x, yb, 1, BTN_TINY, caption_load);
|
||||
|
||||
x += gap + theme_gfx[THEME_BUTTON_TINY]->w;
|
||||
widget_button (&v->widgets[10], x, yb, 1, BTN_TINY, caption_back);
|
||||
|
||||
view_set_focus (v, 10);
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
view * dialog_progress (const view *sub_view, const char *caption, u32 max) {
|
||||
view *v;
|
||||
|
||||
v = view_new (1, sub_view, (view_width - theme_gfx[THEME_PROGRESS]->w) / 2,
|
||||
(view_height - theme_gfx[THEME_PROGRESS]->h) / 2,
|
||||
TEX_LAYER_DIALOGS, 0);
|
||||
|
||||
widget_progress (&v->widgets[0], 0, 0, 0, caption, max);
|
||||
widget_set_progress (&v->widgets[0], 0);
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
void dialog_set_progress (const view *v, u32 progress) {
|
||||
widget_set_progress (&v->widgets[0], progress);
|
||||
}
|
||||
|
||||
view * dialog_about (const view *sub_view) {
|
||||
view *v;
|
||||
u16 ym, hm, yb, hmn;
|
||||
u8 l, hf;
|
||||
|
||||
strcpy(string_about_gen, string_about_pre);
|
||||
if (string_about_trans && strlen(string_about_trans)) {
|
||||
strcat(string_about_gen, "\n\n");
|
||||
strcat(string_about_gen, string_about_trans);
|
||||
}
|
||||
|
||||
if (theme.description && strlen(theme.description)) {
|
||||
strcat(string_about_gen, "\n\n");
|
||||
strcat(string_about_gen, string_about_theme);
|
||||
strcat(string_about_gen, " ");
|
||||
strcat(string_about_gen, theme.description);
|
||||
}
|
||||
|
||||
strcat(string_about_gen, "\n\n");
|
||||
strcat(string_about_gen, string_about_post);
|
||||
|
||||
v = view_new (3, sub_view, (view_width - theme_gfx[THEME_DIALOG]->w) / 2,
|
||||
44, TEX_LAYER_DIALOGS, 0);
|
||||
|
||||
widget_image (&v->widgets[0], 0, 0, 0, theme_gfx[THEME_DIALOG],
|
||||
NULL, false, NULL);
|
||||
|
||||
widget_image (&v->widgets[1], (theme_gfx[THEME_DIALOG]->w -
|
||||
theme_gfx[THEME_ABOUT]->w) / 2, 16, 1,
|
||||
theme_gfx[THEME_ABOUT], NULL, false, NULL);
|
||||
|
||||
yb = theme_gfx[THEME_DIALOG]->h - 16;
|
||||
ym = 16 + theme_gfx[THEME_ABOUT]->h + 8;
|
||||
hm = yb - ym - 8;
|
||||
hf = font_get_y_spacing(FONT_MEMO);
|
||||
l = hm / hf;
|
||||
hmn = l * hf;
|
||||
|
||||
if (hmn < hm) {
|
||||
ym += (hm - hmn) / 2;
|
||||
hm = hmn;
|
||||
}
|
||||
|
||||
widget_memo (&v->widgets[2], 32, ym, 1, theme_gfx[THEME_DIALOG]->w - 64,
|
||||
hm, string_about_gen, FA_CENTERED);
|
||||
|
||||
v->widgets[2].cur = CUR_STD;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
static view *dialog_message(const view *sub_view, dialog_message_type type,
|
||||
dialog_message_buttons buttons, const char *text,
|
||||
u8 focus) {
|
||||
view *v;
|
||||
u8 c;
|
||||
u16 x, gap;
|
||||
const char *caption = NULL, *b1 = NULL, *b2 = NULL;
|
||||
u16 ym, hm, yb;
|
||||
u8 hf;
|
||||
gfx_entity *icon = NULL;
|
||||
|
||||
c = 6;
|
||||
switch (buttons) {
|
||||
case DLGB_OK:
|
||||
c++;
|
||||
break;
|
||||
|
||||
case DLGB_OKCANCEL:
|
||||
case DLGB_YESNO:
|
||||
c += 2;
|
||||
break;
|
||||
}
|
||||
|
||||
v = view_new (c, sub_view, (view_width - theme_gfx[THEME_DIALOG]->w) / 2,
|
||||
44, TEX_LAYER_DIALOGS, PADS_B);
|
||||
|
||||
widget_image (&v->widgets[0], 0, 0, 0, theme_gfx[THEME_DIALOG],
|
||||
NULL, false, NULL);
|
||||
|
||||
switch (type) {
|
||||
case DLGMT_INFO:
|
||||
caption = caption_info;
|
||||
icon = theme_gfx[THEME_DLG_INFO];
|
||||
break;
|
||||
|
||||
case DLGMT_CONFIRM:
|
||||
caption = caption_confirm;
|
||||
icon = theme_gfx[THEME_DLG_CONFIRM];
|
||||
break;
|
||||
|
||||
case DLGMT_WARNING:
|
||||
caption = caption_warning;
|
||||
icon = theme_gfx[THEME_DLG_WARNING];
|
||||
break;
|
||||
|
||||
case DLGMT_ERROR:
|
||||
caption = caption_error;
|
||||
icon = theme_gfx[THEME_DLG_ERROR];
|
||||
break;
|
||||
}
|
||||
|
||||
widget_image (&v->widgets[1], 128, 24, 0, icon, NULL, false, NULL);
|
||||
widget_label (&v->widgets[2], 32, 32, 1, caption,
|
||||
theme_gfx[THEME_DIALOG]->w - 64, FA_CENTERED, FA_ASCENDER,
|
||||
FONT_DLGTITLE);
|
||||
|
||||
hf = font_get_height (FONT_DLGTITLE);
|
||||
yb = theme_gfx[THEME_DIALOG]->h - theme_gfx[THEME_BUTTON_SMALL]->h - 32;
|
||||
ym = 32 + hf + 8;
|
||||
hm = yb - ym - 8;
|
||||
|
||||
widget_memo_deco (&v->widgets[3], 32, ym, 1,
|
||||
theme_gfx[THEME_DIALOG]->w - 64, hm, text, FA_CENTERED);
|
||||
|
||||
switch (buttons) {
|
||||
case DLGB_OK:
|
||||
b1 = caption_ok;
|
||||
b2 = NULL;
|
||||
break;
|
||||
|
||||
case DLGB_OKCANCEL:
|
||||
b1 = caption_ok;
|
||||
b2 = caption_cancel;
|
||||
break;
|
||||
|
||||
case DLGB_YESNO:
|
||||
b1 = caption_yes;
|
||||
b2 = caption_no;
|
||||
break;
|
||||
}
|
||||
|
||||
if (b2) {
|
||||
gap = (theme_gfx[THEME_DIALOG]->w -
|
||||
theme_gfx[THEME_BUTTON_SMALL]->w * 2) / 3;
|
||||
|
||||
x = gap;
|
||||
widget_button (&v->widgets[6], x, yb, 1, BTN_SMALL, b1);
|
||||
|
||||
x += gap + theme_gfx[THEME_BUTTON_SMALL]->w;
|
||||
widget_button (&v->widgets[7], x, yb, 1, BTN_SMALL, b2);
|
||||
} else {
|
||||
gap = (theme_gfx[THEME_DIALOG]->w -
|
||||
theme_gfx[THEME_BUTTON_SMALL]->w) / 2;
|
||||
|
||||
x = gap;
|
||||
widget_button (&v->widgets[6], x, yb, 1, BTN_SMALL, b1);
|
||||
}
|
||||
|
||||
view_set_focus (v, 6 + focus);
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
s8 show_message (const view *sub_view, dialog_message_type type,
|
||||
dialog_message_buttons buttons, const char *text, u8 focus) {
|
||||
view *v;
|
||||
u8 fhw;
|
||||
u32 bd;
|
||||
s8 res;
|
||||
s16 mm;
|
||||
|
||||
v = dialog_message (sub_view, type, buttons, text, focus);
|
||||
|
||||
dialog_fade (v, true);
|
||||
|
||||
fhw = font_get_y_spacing(FONT_MEMO);
|
||||
|
||||
while (true) {
|
||||
view_plot (v, DIALOG_MASK_COLOR, &bd, NULL, NULL);
|
||||
|
||||
if (bd & PADS_LEFT)
|
||||
view_set_focus_prev (v);
|
||||
|
||||
if (bd & PADS_RIGHT)
|
||||
view_set_focus_next (v);
|
||||
|
||||
mm = 0;
|
||||
if (bd & PADS_UP)
|
||||
mm += fhw;
|
||||
|
||||
if (bd & PADS_DOWN)
|
||||
mm -= fhw;
|
||||
|
||||
mm += controls_sticky() / 8;
|
||||
|
||||
if (v->drag && (v->drag_widget == 4))
|
||||
mm += -v->drag_y / 32;
|
||||
|
||||
widget_scroll_memo_deco (&v->widgets[3], mm);
|
||||
|
||||
if ((bd & PADS_A) && (v->focus != -1))
|
||||
break;
|
||||
}
|
||||
|
||||
res = v->focus - 6;
|
||||
|
||||
dialog_fade (v, false);
|
||||
|
||||
view_free (v);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
#define DLG_DEV_FIRST 4
|
||||
|
||||
dialog_options_result show_options_dialog(const view *sub_view) {
|
||||
u32 frame = 0;
|
||||
view *v;
|
||||
dialog_options_result ret;
|
||||
int device;
|
||||
app_sort sort;
|
||||
bool status[DEVICE_COUNT];
|
||||
u32 i, bd;
|
||||
|
||||
app_entry_poll_status(true);
|
||||
|
||||
v = view_new (12, sub_view, (view_width - theme_gfx[THEME_DIALOG]->w) / 2,
|
||||
44, TEX_LAYER_DIALOGS, PADS_B);
|
||||
|
||||
widget_image (&v->widgets[0], 0, 0, 0, theme_gfx[THEME_DIALOG],
|
||||
NULL, false, NULL);
|
||||
widget_label (&v->widgets[1], 32, 16, 1, caption_options,
|
||||
theme_gfx[THEME_DIALOG]->w - 64, FA_CENTERED, FA_ASCENDER, FONT_DLGTITLE);
|
||||
|
||||
widget_label (&v->widgets[2], 32, 60, 1, caption_device,
|
||||
theme_gfx[THEME_DIALOG]->w - 64, FA_LEFT, FA_DESCENDER, FONT_LABEL);
|
||||
widget_label (&v->widgets[3], 32, 212, 1, caption_sort_by,
|
||||
theme_gfx[THEME_DIALOG]->w - 64, FA_LEFT, FA_DESCENDER, FONT_LABEL);
|
||||
|
||||
widget_button (&v->widgets[4], 52, 64, 1, BTN_SMALL, NULL);
|
||||
widget_button (&v->widgets[5], 268, 64, 1, BTN_SMALL, NULL);
|
||||
widget_button (&v->widgets[6], 52, 128, 1, BTN_SMALL, NULL);
|
||||
widget_button (&v->widgets[7], 268, 128, 1, BTN_SMALL, NULL);
|
||||
|
||||
widget_button (&v->widgets[8], 52, 216, 1, BTN_SMALL, NULL);
|
||||
widget_button (&v->widgets[9], 268, 216, 1, BTN_SMALL, NULL);
|
||||
|
||||
widget_button (&v->widgets[10], 32,
|
||||
theme_gfx[THEME_DIALOG]->h -
|
||||
theme_gfx[THEME_BUTTON_SMALL]->h - 16 , 1, BTN_SMALL,
|
||||
caption_ok);
|
||||
widget_button (&v->widgets[11], theme_gfx[THEME_DIALOG]->w -
|
||||
theme_gfx[THEME_BUTTON_SMALL]->w - 32,
|
||||
theme_gfx[THEME_DIALOG]->h -
|
||||
theme_gfx[THEME_BUTTON_SMALL]->h - 16 , 1, BTN_SMALL,
|
||||
caption_back);
|
||||
|
||||
device = app_entry_get_status(status);
|
||||
sort = app_entry_get_sort();
|
||||
|
||||
ret.confirmed = false;
|
||||
ret.device = device;
|
||||
ret.sort = sort;
|
||||
|
||||
for (i = 0; i < DEVICE_COUNT; ++i) {
|
||||
if (i == device)
|
||||
widget_button_set_caption(&v->widgets[DLG_DEV_FIRST + i],
|
||||
FONT_BUTTON,
|
||||
caption_device_names[i]);
|
||||
else
|
||||
widget_button_set_caption(&v->widgets[DLG_DEV_FIRST + i],
|
||||
FONT_BUTTON_DESEL,
|
||||
caption_device_names[i]);
|
||||
|
||||
widget_set_flag (&v->widgets[DLG_DEV_FIRST + i], WF_ENABLED, status[i]);
|
||||
}
|
||||
|
||||
if (ret.sort == APP_SORT_DATE) {
|
||||
widget_button_set_caption(&v->widgets[8],
|
||||
FONT_BUTTON_DESEL,
|
||||
caption_sort_name);
|
||||
widget_button_set_caption(&v->widgets[9],
|
||||
FONT_BUTTON,
|
||||
caption_sort_date);
|
||||
} else {
|
||||
widget_button_set_caption(&v->widgets[8],
|
||||
FONT_BUTTON,
|
||||
caption_sort_name);
|
||||
widget_button_set_caption(&v->widgets[9],
|
||||
FONT_BUTTON_DESEL,
|
||||
caption_sort_date);
|
||||
}
|
||||
|
||||
view_set_focus (v, 11);
|
||||
|
||||
dialog_fade (v, true);
|
||||
|
||||
while (true) {
|
||||
app_entry_get_status(status);
|
||||
|
||||
for (i = 0; i < DEVICE_COUNT; ++i)
|
||||
widget_set_flag (&v->widgets[DLG_DEV_FIRST + i], WF_ENABLED,
|
||||
status[i]);
|
||||
|
||||
view_plot (v, DIALOG_MASK_COLOR, &bd, NULL, NULL);
|
||||
frame++;
|
||||
|
||||
if (bd & PADS_LEFT)
|
||||
view_set_focus_prev (v);
|
||||
|
||||
if (bd & PADS_RIGHT)
|
||||
view_set_focus_next (v);
|
||||
|
||||
if (bd & PADS_UP)
|
||||
if (v->focus == view_move_focus(v, -2))
|
||||
view_move_focus(v, -4);
|
||||
|
||||
if (bd & PADS_DOWN)
|
||||
if (v->focus == view_move_focus(v, 2))
|
||||
view_move_focus(v, 4);
|
||||
|
||||
if (bd & (PADS_B | PADS_1))
|
||||
break;
|
||||
|
||||
if ((bd & PADS_A) && (v->focus != -1)) {
|
||||
if ((v->focus >= DLG_DEV_FIRST) &&
|
||||
(v->focus < DLG_DEV_FIRST + DEVICE_COUNT)) {
|
||||
widget_button_set_caption(&v->widgets[DLG_DEV_FIRST + ret.device],
|
||||
FONT_BUTTON_DESEL,
|
||||
caption_device_names[ret.device]);
|
||||
ret.device = v->focus - DLG_DEV_FIRST;
|
||||
widget_button_set_caption(&v->widgets[DLG_DEV_FIRST + ret.device],
|
||||
FONT_BUTTON,
|
||||
caption_device_names[ret.device]);
|
||||
} else if (v->focus == 8) {
|
||||
ret.sort = APP_SORT_NAME;
|
||||
widget_button_set_caption(&v->widgets[8],
|
||||
FONT_BUTTON,
|
||||
caption_sort_name);
|
||||
widget_button_set_caption(&v->widgets[9],
|
||||
FONT_BUTTON_DESEL,
|
||||
caption_sort_date);
|
||||
} else if (v->focus == 9) {
|
||||
ret.sort = APP_SORT_DATE;
|
||||
widget_button_set_caption(&v->widgets[8],
|
||||
FONT_BUTTON_DESEL,
|
||||
caption_sort_name);
|
||||
widget_button_set_caption(&v->widgets[9],
|
||||
FONT_BUTTON,
|
||||
caption_sort_date);
|
||||
} else if ((v->focus == 10) || (v->focus == 11)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((frame % 30) == 0)
|
||||
app_entry_poll_status(false);
|
||||
}
|
||||
|
||||
if ((bd & PADS_A) && (v->focus == 10))
|
||||
ret.confirmed = true;
|
||||
|
||||
dialog_fade (v, false);
|
||||
|
||||
view_free (v);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
46
channel/channelapp/source/dialogs.h
Normal file
46
channel/channelapp/source/dialogs.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#ifndef _DIALOGS_H_
|
||||
#define _DIALOGS_H_
|
||||
|
||||
#include <gctypes.h>
|
||||
|
||||
#include "gfx.h"
|
||||
#include "view.h"
|
||||
|
||||
typedef enum {
|
||||
DLGMT_INFO = 0,
|
||||
DLGMT_CONFIRM,
|
||||
DLGMT_WARNING,
|
||||
DLGMT_ERROR
|
||||
} dialog_message_type;
|
||||
|
||||
typedef enum {
|
||||
DLGB_OK,
|
||||
DLGB_OKCANCEL,
|
||||
DLGB_YESNO
|
||||
} dialog_message_buttons;
|
||||
|
||||
typedef struct {
|
||||
bool confirmed;
|
||||
int device;
|
||||
app_sort sort;
|
||||
} dialog_options_result;
|
||||
|
||||
extern u16 width_dialog, height_dialog;
|
||||
|
||||
void dialogs_init (void);
|
||||
void dialogs_theme_reinit (void);
|
||||
void dialogs_deinit (void);
|
||||
|
||||
void dialog_fade (view *v, bool fade_in);
|
||||
|
||||
view * dialog_app (const app_entry *entry, const view *sub_view);
|
||||
view * dialog_progress (const view *sub_view, const char *caption, u32 max);
|
||||
void dialog_set_progress (const view *v, u32 progress);
|
||||
view * dialog_about (const view *sub_view);
|
||||
|
||||
s8 show_message (const view *sub_view, dialog_message_type type,
|
||||
dialog_message_buttons buttons, const char *text, u8 focus);
|
||||
dialog_options_result show_options_dialog(const view *sub_view);
|
||||
|
||||
#endif
|
||||
|
||||
79
channel/channelapp/source/dvd.c
Normal file
79
channel/channelapp/source/dvd.c
Normal file
@@ -0,0 +1,79 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ogcsys.h>
|
||||
#include <gccore.h>
|
||||
|
||||
#include "../config.h"
|
||||
|
||||
static u32 inbuf[8] __attribute__((aligned(0x20)));
|
||||
static u32 outbuf[8] __attribute__((aligned(0x20)));
|
||||
|
||||
static s32 _dvd_fd = -1;
|
||||
|
||||
static const char _dvd_path[] __attribute__((aligned(0x20))) = "/dev/di";
|
||||
|
||||
typedef enum {
|
||||
WDVD_OPEN = 0,
|
||||
WDVD_STOP,
|
||||
WDVD_CLOSE,
|
||||
WDVD_DONE
|
||||
} wiidvd_state;
|
||||
|
||||
static wiidvd_state _state;
|
||||
|
||||
static s32 __WiiDVD_Callback(s32 result, void *userdata)
|
||||
{
|
||||
s32 res = -1;
|
||||
|
||||
switch (_state) {
|
||||
case WDVD_OPEN:
|
||||
_dvd_fd = result;
|
||||
if (_dvd_fd < 0)
|
||||
return 0;
|
||||
|
||||
memset(inbuf, 0, 0x20);
|
||||
inbuf[0x00] = 0xe3000000;
|
||||
inbuf[0x01] = 0;
|
||||
inbuf[0x02] = 0;
|
||||
|
||||
_state = WDVD_STOP;
|
||||
res = IOS_IoctlAsync( _dvd_fd, 0xe3, inbuf, 0x20, outbuf, 0x20,
|
||||
__WiiDVD_Callback, NULL);
|
||||
break;
|
||||
|
||||
case WDVD_STOP:
|
||||
_state = WDVD_CLOSE;
|
||||
res = IOS_CloseAsync(_dvd_fd, __WiiDVD_Callback, NULL);
|
||||
break;
|
||||
|
||||
case WDVD_CLOSE:
|
||||
_dvd_fd = -1;
|
||||
_state = WDVD_DONE;
|
||||
res = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
s32 WiiDVD_StopMotorAsync(void) {
|
||||
_state = WDVD_OPEN;
|
||||
|
||||
gprintf("starting DVD motor stop callback chain\n");
|
||||
return IOS_OpenAsync(_dvd_path, 0, __WiiDVD_Callback, NULL);
|
||||
}
|
||||
|
||||
void WiiDVD_ShutDown(void) {
|
||||
s32 fd = _dvd_fd;
|
||||
|
||||
if (fd > 0) {
|
||||
_dvd_fd = -1;
|
||||
_state = WDVD_DONE;
|
||||
IOS_Close(fd);
|
||||
gprintf("killed DVD motor stop callback chain\n");
|
||||
}
|
||||
}
|
||||
|
||||
10
channel/channelapp/source/dvd.h
Normal file
10
channel/channelapp/source/dvd.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#ifndef _DVD_H_
|
||||
#define _DVD_H_
|
||||
|
||||
#include <gctypes.h>
|
||||
|
||||
s32 WiiDVD_StopMotorAsync(void);
|
||||
void WiiDVD_ShutDown(void);
|
||||
|
||||
#endif
|
||||
|
||||
413
channel/channelapp/source/ecdsa.c
Normal file
413
channel/channelapp/source/ecdsa.c
Normal file
@@ -0,0 +1,413 @@
|
||||
// Copyright 2007-2009 Segher Boessenkool <segher@kernel.crashing.org>
|
||||
// Licensed under the terms of the GNU GPL, version 2
|
||||
// http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <ogcsys.h>
|
||||
|
||||
#include "sha1.h"
|
||||
#include "ecdsa.h"
|
||||
|
||||
#include "../config.h"
|
||||
|
||||
#ifdef ENABLE_UPDATES
|
||||
|
||||
// order of the addition group of points
|
||||
static u8 ec_N[30] =
|
||||
"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
"\x13\xe9\x74\xe7\x2f\x8a\x69\x22\x03\x1d\x26\x03\xcf\xe0\xd7";
|
||||
|
||||
// base point
|
||||
static u8 ec_G[60] =
|
||||
"\x00\xfa\xc9\xdf\xcb\xac\x83\x13\xbb\x21\x39\xf1\xbb\x75\x5f"
|
||||
"\xef\x65\xbc\x39\x1f\x8b\x36\xf8\xf8\xeb\x73\x71\xfd\x55\x8b"
|
||||
"\x01\x00\x6a\x08\xa4\x19\x03\x35\x06\x78\xe5\x85\x28\xbe\xbf"
|
||||
"\x8a\x0b\xef\xf8\x67\xa7\xca\x36\x71\x6f\x7e\x01\xf8\x10\x52";
|
||||
|
||||
static const u8 square[16] =
|
||||
"\x00\x01\x04\x05\x10\x11\x14\x15\x40\x41\x44\x45\x50\x51\x54\x55";
|
||||
|
||||
static void bn_zero(u8 *d, u32 n)
|
||||
{
|
||||
memset(d, 0, n);
|
||||
}
|
||||
|
||||
static void bn_copy(u8 *d, const u8 *a, u32 n)
|
||||
{
|
||||
memcpy(d, a, n);
|
||||
}
|
||||
|
||||
static int bn_compare(const u8 *a, const u8 *b, u32 n)
|
||||
{
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
if (a[i] < b[i])
|
||||
return -1;
|
||||
if (a[i] > b[i])
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bn_sub_modulus(u8 *a, const u8 *N, u32 n)
|
||||
{
|
||||
u32 i;
|
||||
u32 dig;
|
||||
u8 c;
|
||||
|
||||
c = 0;
|
||||
for (i = n - 1; i < n; i--) {
|
||||
dig = N[i] + c;
|
||||
c = (a[i] < dig);
|
||||
a[i] -= dig;
|
||||
}
|
||||
}
|
||||
|
||||
static void bn_add(u8 *d, const u8 *a, const u8 *b, const u8 *N, u32 n)
|
||||
{
|
||||
u32 i;
|
||||
u32 dig;
|
||||
u8 c;
|
||||
|
||||
c = 0;
|
||||
for (i = n - 1; i < n; i--) {
|
||||
dig = a[i] + b[i] + c;
|
||||
c = (dig >= 0x100);
|
||||
d[i] = dig;
|
||||
}
|
||||
|
||||
if (c)
|
||||
bn_sub_modulus(d, N, n);
|
||||
|
||||
if (bn_compare(d, N, n) >= 0)
|
||||
bn_sub_modulus(d, N, n);
|
||||
}
|
||||
|
||||
static void bn_mul(u8 *d, const u8 *a, const u8 *b, const u8 *N, u32 n)
|
||||
{
|
||||
u32 i;
|
||||
u8 mask;
|
||||
|
||||
bn_zero(d, n);
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
for (mask = 0x80; mask != 0; mask >>= 1) {
|
||||
bn_add(d, d, d, N, n);
|
||||
if ((a[i] & mask) != 0)
|
||||
bn_add(d, d, b, N, n);
|
||||
}
|
||||
}
|
||||
|
||||
static void bn_exp(u8 *d, const u8 *a, const u8 *N, u32 n, const u8 *e, u32 en)
|
||||
{
|
||||
u8 t[512];
|
||||
u32 i;
|
||||
u8 mask;
|
||||
|
||||
bn_zero(d, n);
|
||||
d[n-1] = 1;
|
||||
for (i = 0; i < en; i++)
|
||||
for (mask = 0x80; mask != 0; mask >>= 1) {
|
||||
bn_mul(t, d, d, N, n);
|
||||
if ((e[i] & mask) != 0)
|
||||
bn_mul(d, t, a, N, n);
|
||||
else
|
||||
bn_copy(d, t, n);
|
||||
}
|
||||
}
|
||||
|
||||
// only for prime N -- stupid but lazy, see if I care
|
||||
static void bn_inv(u8 *d, const u8 *a, const u8 *N, u32 n)
|
||||
{
|
||||
u8 t[512], s[512];
|
||||
|
||||
bn_copy(t, N, n);
|
||||
bn_zero(s, n);
|
||||
s[n-1] = 2;
|
||||
bn_sub_modulus(t, s, n);
|
||||
bn_exp(d, a, N, n, t, n);
|
||||
}
|
||||
|
||||
static void elt_copy(u8 *d, const u8 *a)
|
||||
{
|
||||
memcpy(d, a, 30);
|
||||
}
|
||||
|
||||
static void elt_zero(u8 *d)
|
||||
{
|
||||
memset(d, 0, 30);
|
||||
}
|
||||
|
||||
static int elt_is_zero(const u8 *d)
|
||||
{
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < 30; i++)
|
||||
if (d[i] != 0)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void elt_add(u8 *d, u8 *a, const u8 *b)
|
||||
{
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < 30; i++)
|
||||
d[i] = a[i] ^ b[i];
|
||||
}
|
||||
|
||||
static void elt_mul_x(u8 *d, u8 *a)
|
||||
{
|
||||
u8 carry, x, y;
|
||||
u32 i;
|
||||
|
||||
carry = a[0] & 1;
|
||||
|
||||
x = 0;
|
||||
for (i = 0; i < 29; i++) {
|
||||
y = a[i + 1];
|
||||
d[i] = x ^ (y >> 7);
|
||||
x = y << 1;
|
||||
}
|
||||
d[29] = x ^ carry;
|
||||
|
||||
d[20] ^= carry << 2;
|
||||
}
|
||||
|
||||
static void elt_mul(u8 *d, u8 *a, u8 *b)
|
||||
{
|
||||
u32 i, n;
|
||||
u8 mask;
|
||||
|
||||
elt_zero(d);
|
||||
|
||||
i = 0;
|
||||
mask = 1;
|
||||
for (n = 0; n < 233; n++) {
|
||||
elt_mul_x(d, d);
|
||||
|
||||
if ((a[i] & mask) != 0)
|
||||
elt_add(d, d, b);
|
||||
|
||||
mask >>= 1;
|
||||
if (mask == 0) {
|
||||
mask = 0x80;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void elt_square_to_wide(u8 *d, u8 *a)
|
||||
{
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < 30; i++) {
|
||||
d[2*i] = square[a[i] >> 4];
|
||||
d[2*i + 1] = square[a[i] & 15];
|
||||
}
|
||||
}
|
||||
|
||||
static void wide_reduce(u8 *d)
|
||||
{
|
||||
u32 i;
|
||||
u8 x;
|
||||
|
||||
for (i = 0; i < 30; i++) {
|
||||
x = d[i];
|
||||
|
||||
d[i + 19] ^= x >> 7;
|
||||
d[i + 20] ^= x << 1;
|
||||
|
||||
d[i + 29] ^= x >> 1;
|
||||
d[i + 30] ^= x << 7;
|
||||
}
|
||||
|
||||
x = d[30] & ~1;
|
||||
|
||||
d[49] ^= x >> 7;
|
||||
d[50] ^= x << 1;
|
||||
|
||||
d[59] ^= x >> 1;
|
||||
|
||||
d[30] &= 1;
|
||||
}
|
||||
|
||||
static void elt_square(u8 *d, u8 *a)
|
||||
{
|
||||
u8 wide[60];
|
||||
|
||||
elt_square_to_wide(wide, a);
|
||||
wide_reduce(wide);
|
||||
|
||||
elt_copy(d, wide + 30);
|
||||
}
|
||||
|
||||
static void itoh_tsujii(u8 *d, u8 *a, u8 *b, u32 j)
|
||||
{
|
||||
u8 t[30];
|
||||
|
||||
elt_copy(t, a);
|
||||
while (j--) {
|
||||
elt_square(d, t);
|
||||
elt_copy(t, d);
|
||||
}
|
||||
|
||||
elt_mul(d, t, b);
|
||||
}
|
||||
|
||||
static void elt_inv(u8 *d, u8 *a)
|
||||
{
|
||||
u8 t[30];
|
||||
u8 s[30];
|
||||
|
||||
itoh_tsujii(t, a, a, 1);
|
||||
itoh_tsujii(s, t, a, 1);
|
||||
itoh_tsujii(t, s, s, 3);
|
||||
itoh_tsujii(s, t, a, 1);
|
||||
itoh_tsujii(t, s, s, 7);
|
||||
itoh_tsujii(s, t, t, 14);
|
||||
itoh_tsujii(t, s, a, 1);
|
||||
itoh_tsujii(s, t, t, 29);
|
||||
itoh_tsujii(t, s, s, 58);
|
||||
itoh_tsujii(s, t, t, 116);
|
||||
elt_square(d, s);
|
||||
}
|
||||
|
||||
static int point_is_zero(const u8 *p)
|
||||
{
|
||||
return elt_is_zero(p) && elt_is_zero(p + 30);
|
||||
}
|
||||
|
||||
static void point_double(u8 *r, u8 *p)
|
||||
{
|
||||
u8 s[30], t[30];
|
||||
u8 *px, *py, *rx, *ry;
|
||||
|
||||
px = p;
|
||||
py = p + 30;
|
||||
rx = r;
|
||||
ry = r + 30;
|
||||
|
||||
if (elt_is_zero(px)) {
|
||||
elt_zero(rx);
|
||||
elt_zero(ry);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
elt_inv(t, px);
|
||||
elt_mul(s, py, t);
|
||||
elt_add(s, s, px);
|
||||
|
||||
elt_square(t, px);
|
||||
|
||||
elt_square(rx, s);
|
||||
elt_add(rx, rx, s);
|
||||
rx[29] ^= 1;
|
||||
|
||||
elt_mul(ry, s, rx);
|
||||
elt_add(ry, ry, rx);
|
||||
elt_add(ry, ry, t);
|
||||
}
|
||||
|
||||
static void point_add(u8 *r, u8 *p, const u8 *q)
|
||||
{
|
||||
u8 s[30], t[30], u[30];
|
||||
u8 *px, *py, *rx, *ry;
|
||||
const u8 *qx, *qy;
|
||||
|
||||
px = p;
|
||||
py = p + 30;
|
||||
qx = q;
|
||||
qy = q + 30;
|
||||
rx = r;
|
||||
ry = r + 30;
|
||||
|
||||
if (point_is_zero(p)) {
|
||||
elt_copy(rx, qx);
|
||||
elt_copy(ry, qy);
|
||||
return;
|
||||
}
|
||||
|
||||
if (point_is_zero(q)) {
|
||||
elt_copy(rx, px);
|
||||
elt_copy(ry, py);
|
||||
return;
|
||||
}
|
||||
|
||||
elt_add(u, px, qx);
|
||||
|
||||
if (elt_is_zero(u)) {
|
||||
elt_add(u, py, qy);
|
||||
if (elt_is_zero(u))
|
||||
point_double(r, p);
|
||||
else {
|
||||
elt_zero(rx);
|
||||
elt_zero(ry);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
elt_inv(t, u);
|
||||
elt_add(u, py, qy);
|
||||
elt_mul(s, t, u);
|
||||
|
||||
elt_square(t, s);
|
||||
elt_add(t, t, s);
|
||||
elt_add(t, t, qx);
|
||||
t[29] ^= 1;
|
||||
|
||||
elt_mul(u, s, t);
|
||||
elt_add(s, u, py);
|
||||
elt_add(rx, t, px);
|
||||
elt_add(ry, s, rx);
|
||||
}
|
||||
|
||||
static void point_mul(u8 *d, u8 *a, const u8 *b) // a is bignum
|
||||
{
|
||||
u32 i;
|
||||
u8 mask;
|
||||
|
||||
elt_zero(d);
|
||||
elt_zero(d + 30);
|
||||
|
||||
for (i = 0; i < 30; i++)
|
||||
for (mask = 0x80; mask != 0; mask >>= 1) {
|
||||
point_double(d, d);
|
||||
if ((a[i] & mask) != 0)
|
||||
point_add(d, d, b);
|
||||
}
|
||||
}
|
||||
|
||||
int check_ecdsa(const u8 *Q, u8 *R, u8 *S, u8 *hash)
|
||||
{
|
||||
u8 Sinv[30];
|
||||
u8 e[30];
|
||||
u8 w1[30], w2[30];
|
||||
u8 r1[60], r2[60];
|
||||
|
||||
bn_inv(Sinv, S, ec_N, 30);
|
||||
|
||||
elt_zero(e);
|
||||
memcpy(e + 10, hash, 20);
|
||||
|
||||
bn_mul(w1, e, Sinv, ec_N, 30);
|
||||
bn_mul(w2, R, Sinv, ec_N, 30);
|
||||
|
||||
point_mul(r1, w1, ec_G);
|
||||
point_mul(r2, w2, Q);
|
||||
|
||||
point_add(r1, r1, r2);
|
||||
|
||||
if (bn_compare(r1, ec_N, 30) >= 0)
|
||||
bn_sub_modulus(r1, ec_N, 30);
|
||||
|
||||
return (bn_compare(r1, R, 30) == 0);
|
||||
}
|
||||
|
||||
#endif
|
||||
8
channel/channelapp/source/ecdsa.h
Normal file
8
channel/channelapp/source/ecdsa.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#ifndef _ECDSA_H_
|
||||
#define _ECDSA_H_
|
||||
|
||||
#include <ogcsys.h>
|
||||
|
||||
int check_ecdsa(const u8 *Q, u8 *R, u8 *S, u8 *hash);
|
||||
|
||||
#endif
|
||||
594
channel/channelapp/source/elf_abi.h
Normal file
594
channel/channelapp/source/elf_abi.h
Normal file
@@ -0,0 +1,594 @@
|
||||
/*
|
||||
* Copyright (c) 1995, 1996, 2001, 2002
|
||||
* Erik Theisen. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This is the ELF ABI header file
|
||||
* formerly known as "elf_abi.h".
|
||||
*/
|
||||
|
||||
#ifndef _ELF_ABI_H
|
||||
#define _ELF_ABI_H
|
||||
|
||||
#include <gctypes.h>
|
||||
|
||||
/*
|
||||
* This version doesn't work for 64-bit ABIs - Erik.
|
||||
*/
|
||||
|
||||
/*
|
||||
* These typedefs need to be handled better.
|
||||
*/
|
||||
typedef u32 Elf32_Addr; /* Unsigned program address */
|
||||
typedef u32 Elf32_Off; /* Unsigned file offset */
|
||||
typedef s32 Elf32_Sword; /* Signed large integer */
|
||||
typedef u32 Elf32_Word; /* Unsigned large integer */
|
||||
typedef u16 Elf32_Half; /* Unsigned medium integer */
|
||||
|
||||
/* e_ident[] identification indexes */
|
||||
#define EI_MAG0 0 /* file ID */
|
||||
#define EI_MAG1 1 /* file ID */
|
||||
#define EI_MAG2 2 /* file ID */
|
||||
#define EI_MAG3 3 /* file ID */
|
||||
#define EI_CLASS 4 /* file class */
|
||||
#define EI_DATA 5 /* data encoding */
|
||||
#define EI_VERSION 6 /* ELF header version */
|
||||
#define EI_OSABI 7 /* OS/ABI specific ELF extensions */
|
||||
#define EI_ABIVERSION 8 /* ABI target version */
|
||||
#define EI_PAD 9 /* start of pad bytes */
|
||||
#define EI_NIDENT 16 /* Size of e_ident[] */
|
||||
|
||||
/* e_ident[] magic number */
|
||||
#define ELFMAG0 0x7f /* e_ident[EI_MAG0] */
|
||||
#define ELFMAG1 'E' /* e_ident[EI_MAG1] */
|
||||
#define ELFMAG2 'L' /* e_ident[EI_MAG2] */
|
||||
#define ELFMAG3 'F' /* e_ident[EI_MAG3] */
|
||||
#define ELFMAG "\177ELF" /* magic */
|
||||
#define SELFMAG 4 /* size of magic */
|
||||
|
||||
/* e_ident[] file class */
|
||||
#define ELFCLASSNONE 0 /* invalid */
|
||||
#define ELFCLASS32 1 /* 32-bit objs */
|
||||
#define ELFCLASS64 2 /* 64-bit objs */
|
||||
#define ELFCLASSNUM 3 /* number of classes */
|
||||
|
||||
/* e_ident[] data encoding */
|
||||
#define ELFDATANONE 0 /* invalid */
|
||||
#define ELFDATA2LSB 1 /* Little-Endian */
|
||||
#define ELFDATA2MSB 2 /* Big-Endian */
|
||||
#define ELFDATANUM 3 /* number of data encode defines */
|
||||
|
||||
/* e_ident[] OS/ABI specific ELF extensions */
|
||||
#define ELFOSABI_NONE 0 /* No extension specified */
|
||||
#define ELFOSABI_HPUX 1 /* Hewlett-Packard HP-UX */
|
||||
#define ELFOSABI_NETBSD 2 /* NetBSD */
|
||||
#define ELFOSABI_LINUX 3 /* Linux */
|
||||
#define ELFOSABI_SOLARIS 6 /* Sun Solaris */
|
||||
#define ELFOSABI_AIX 7 /* AIX */
|
||||
#define ELFOSABI_IRIX 8 /* IRIX */
|
||||
#define ELFOSABI_FREEBSD 9 /* FreeBSD */
|
||||
#define ELFOSABI_TRU64 10 /* Compaq TRU64 UNIX */
|
||||
#define ELFOSABI_MODESTO 11 /* Novell Modesto */
|
||||
#define ELFOSABI_OPENBSD 12 /* OpenBSD */
|
||||
/* 64-255 Architecture-specific value range */
|
||||
|
||||
/* e_ident[] ABI Version */
|
||||
#define ELFABIVERSION 0
|
||||
|
||||
/* e_ident */
|
||||
#define IS_ELF(ehdr) ((ehdr).e_ident[EI_MAG0] == ELFMAG0 && \
|
||||
(ehdr).e_ident[EI_MAG1] == ELFMAG1 && \
|
||||
(ehdr).e_ident[EI_MAG2] == ELFMAG2 && \
|
||||
(ehdr).e_ident[EI_MAG3] == ELFMAG3)
|
||||
|
||||
/* ELF Header */
|
||||
typedef struct elfhdr{
|
||||
unsigned char e_ident[EI_NIDENT]; /* ELF Identification */
|
||||
Elf32_Half e_type; /* object file type */
|
||||
Elf32_Half e_machine; /* machine */
|
||||
Elf32_Word e_version; /* object file version */
|
||||
Elf32_Addr e_entry; /* virtual entry point */
|
||||
Elf32_Off e_phoff; /* program header table offset */
|
||||
Elf32_Off e_shoff; /* section header table offset */
|
||||
Elf32_Word e_flags; /* processor-specific flags */
|
||||
Elf32_Half e_ehsize; /* ELF header size */
|
||||
Elf32_Half e_phentsize; /* program header entry size */
|
||||
Elf32_Half e_phnum; /* number of program header entries */
|
||||
Elf32_Half e_shentsize; /* section header entry size */
|
||||
Elf32_Half e_shnum; /* number of section header entries */
|
||||
Elf32_Half e_shstrndx; /* section header table's "section
|
||||
header string table" entry offset */
|
||||
} Elf32_Ehdr;
|
||||
|
||||
/* e_type */
|
||||
#define ET_NONE 0 /* No file type */
|
||||
#define ET_REL 1 /* relocatable file */
|
||||
#define ET_EXEC 2 /* executable file */
|
||||
#define ET_DYN 3 /* shared object file */
|
||||
#define ET_CORE 4 /* core file */
|
||||
#define ET_NUM 5 /* number of types */
|
||||
#define ET_LOOS 0xfe00 /* reserved range for operating */
|
||||
#define ET_HIOS 0xfeff /* system specific e_type */
|
||||
#define ET_LOPROC 0xff00 /* reserved range for processor */
|
||||
#define ET_HIPROC 0xffff /* specific e_type */
|
||||
|
||||
/* e_machine */
|
||||
#define EM_NONE 0 /* No Machine */
|
||||
#define EM_M32 1 /* AT&T WE 32100 */
|
||||
#define EM_SPARC 2 /* SPARC */
|
||||
#define EM_386 3 /* Intel 80386 */
|
||||
#define EM_68K 4 /* Motorola 68000 */
|
||||
#define EM_88K 5 /* Motorola 88000 */
|
||||
#if 0
|
||||
#define EM_486 6 /* RESERVED - was Intel 80486 */
|
||||
#endif
|
||||
#define EM_860 7 /* Intel 80860 */
|
||||
#define EM_MIPS 8 /* MIPS R3000 Big-Endian only */
|
||||
#define EM_S370 9 /* IBM System/370 Processor */
|
||||
#define EM_MIPS_RS4_BE 10 /* MIPS R4000 Big-Endian */
|
||||
#if 0
|
||||
#define EM_SPARC64 11 /* RESERVED - was SPARC v9
|
||||
64-bit unoffical */
|
||||
#endif
|
||||
/* RESERVED 11-14 for future use */
|
||||
#define EM_PARISC 15 /* HPPA */
|
||||
/* RESERVED 16 for future use */
|
||||
#define EM_VPP500 17 /* Fujitsu VPP500 */
|
||||
#define EM_SPARC32PLUS 18 /* Enhanced instruction set SPARC */
|
||||
#define EM_960 19 /* Intel 80960 */
|
||||
#define EM_PPC 20 /* PowerPC */
|
||||
#define EM_PPC64 21 /* 64-bit PowerPC */
|
||||
#define EM_S390 22 /* IBM System/390 Processor */
|
||||
/* RESERVED 23-35 for future use */
|
||||
#define EM_V800 36 /* NEC V800 */
|
||||
#define EM_FR20 37 /* Fujitsu FR20 */
|
||||
#define EM_RH32 38 /* TRW RH-32 */
|
||||
#define EM_RCE 39 /* Motorola RCE */
|
||||
#define EM_ARM 40 /* Advanced Risc Machines ARM */
|
||||
#define EM_ALPHA 41 /* Digital Alpha */
|
||||
#define EM_SH 42 /* Hitachi SH */
|
||||
#define EM_SPARCV9 43 /* SPARC Version 9 */
|
||||
#define EM_TRICORE 44 /* Siemens TriCore embedded processor */
|
||||
#define EM_ARC 45 /* Argonaut RISC Core */
|
||||
#define EM_H8_300 46 /* Hitachi H8/300 */
|
||||
#define EM_H8_300H 47 /* Hitachi H8/300H */
|
||||
#define EM_H8S 48 /* Hitachi H8S */
|
||||
#define EM_H8_500 49 /* Hitachi H8/500 */
|
||||
#define EM_IA_64 50 /* Intel Merced */
|
||||
#define EM_MIPS_X 51 /* Stanford MIPS-X */
|
||||
#define EM_COLDFIRE 52 /* Motorola Coldfire */
|
||||
#define EM_68HC12 53 /* Motorola M68HC12 */
|
||||
#define EM_MMA 54 /* Fujitsu MMA Multimedia Accelerator*/
|
||||
#define EM_PCP 55 /* Siemens PCP */
|
||||
#define EM_NCPU 56 /* Sony nCPU embeeded RISC */
|
||||
#define EM_NDR1 57 /* Denso NDR1 microprocessor */
|
||||
#define EM_STARCORE 58 /* Motorola Start*Core processor */
|
||||
#define EM_ME16 59 /* Toyota ME16 processor */
|
||||
#define EM_ST100 60 /* STMicroelectronic ST100 processor */
|
||||
#define EM_TINYJ 61 /* Advanced Logic Corp. Tinyj emb.fam*/
|
||||
#define EM_X86_64 62 /* AMD x86-64 */
|
||||
#define EM_PDSP 63 /* Sony DSP Processor */
|
||||
/* RESERVED 64,65 for future use */
|
||||
#define EM_FX66 66 /* Siemens FX66 microcontroller */
|
||||
#define EM_ST9PLUS 67 /* STMicroelectronics ST9+ 8/16 mc */
|
||||
#define EM_ST7 68 /* STmicroelectronics ST7 8 bit mc */
|
||||
#define EM_68HC16 69 /* Motorola MC68HC16 microcontroller */
|
||||
#define EM_68HC11 70 /* Motorola MC68HC11 microcontroller */
|
||||
#define EM_68HC08 71 /* Motorola MC68HC08 microcontroller */
|
||||
#define EM_68HC05 72 /* Motorola MC68HC05 microcontroller */
|
||||
#define EM_SVX 73 /* Silicon Graphics SVx */
|
||||
#define EM_ST19 74 /* STMicroelectronics ST19 8 bit mc */
|
||||
#define EM_VAX 75 /* Digital VAX */
|
||||
#define EM_CHRIS 76 /* Axis Communications embedded proc. */
|
||||
#define EM_JAVELIN 77 /* Infineon Technologies emb. proc. */
|
||||
#define EM_FIREPATH 78 /* Element 14 64-bit DSP Processor */
|
||||
#define EM_ZSP 79 /* LSI Logic 16-bit DSP Processor */
|
||||
#define EM_MMIX 80 /* Donald Knuth's edu 64-bit proc. */
|
||||
#define EM_HUANY 81 /* Harvard University mach-indep objs */
|
||||
#define EM_PRISM 82 /* SiTera Prism */
|
||||
#define EM_AVR 83 /* Atmel AVR 8-bit microcontroller */
|
||||
#define EM_FR30 84 /* Fujitsu FR30 */
|
||||
#define EM_D10V 85 /* Mitsubishi DV10V */
|
||||
#define EM_D30V 86 /* Mitsubishi DV30V */
|
||||
#define EM_V850 87 /* NEC v850 */
|
||||
#define EM_M32R 88 /* Mitsubishi M32R */
|
||||
#define EM_MN10300 89 /* Matsushita MN10200 */
|
||||
#define EM_MN10200 90 /* Matsushita MN10200 */
|
||||
#define EM_PJ 91 /* picoJava */
|
||||
#define EM_NUM 92 /* number of machine types */
|
||||
|
||||
/* Version */
|
||||
#define EV_NONE 0 /* Invalid */
|
||||
#define EV_CURRENT 1 /* Current */
|
||||
#define EV_NUM 2 /* number of versions */
|
||||
|
||||
/* Section Header */
|
||||
typedef struct {
|
||||
Elf32_Word sh_name; /* name - index into section header
|
||||
string table section */
|
||||
Elf32_Word sh_type; /* type */
|
||||
Elf32_Word sh_flags; /* flags */
|
||||
Elf32_Addr sh_addr; /* address */
|
||||
Elf32_Off sh_offset; /* file offset */
|
||||
Elf32_Word sh_size; /* section size */
|
||||
Elf32_Word sh_link; /* section header table index link */
|
||||
Elf32_Word sh_info; /* extra information */
|
||||
Elf32_Word sh_addralign; /* address alignment */
|
||||
Elf32_Word sh_entsize; /* section entry size */
|
||||
} Elf32_Shdr;
|
||||
|
||||
/* Special Section Indexes */
|
||||
#define SHN_UNDEF 0 /* undefined */
|
||||
#define SHN_LORESERVE 0xff00 /* lower bounds of reserved indexes */
|
||||
#define SHN_LOPROC 0xff00 /* reserved range for processor */
|
||||
#define SHN_HIPROC 0xff1f /* specific section indexes */
|
||||
#define SHN_LOOS 0xff20 /* reserved range for operating */
|
||||
#define SHN_HIOS 0xff3f /* specific semantics */
|
||||
#define SHN_ABS 0xfff1 /* absolute value */
|
||||
#define SHN_COMMON 0xfff2 /* common symbol */
|
||||
#define SHN_XINDEX 0xffff /* Index is an extra table */
|
||||
#define SHN_HIRESERVE 0xffff /* upper bounds of reserved indexes */
|
||||
|
||||
/* sh_type */
|
||||
#define SHT_NULL 0 /* inactive */
|
||||
#define SHT_PROGBITS 1 /* program defined information */
|
||||
#define SHT_SYMTAB 2 /* symbol table section */
|
||||
#define SHT_STRTAB 3 /* string table section */
|
||||
#define SHT_RELA 4 /* relocation section with addends*/
|
||||
#define SHT_HASH 5 /* symbol hash table section */
|
||||
#define SHT_DYNAMIC 6 /* dynamic section */
|
||||
#define SHT_NOTE 7 /* note section */
|
||||
#define SHT_NOBITS 8 /* no space section */
|
||||
#define SHT_REL 9 /* relation section without addends */
|
||||
#define SHT_SHLIB 10 /* reserved - purpose unknown */
|
||||
#define SHT_DYNSYM 11 /* dynamic symbol table section */
|
||||
#define SHT_INIT_ARRAY 14 /* Array of constructors */
|
||||
#define SHT_FINI_ARRAY 15 /* Array of destructors */
|
||||
#define SHT_PREINIT_ARRAY 16 /* Array of pre-constructors */
|
||||
#define SHT_GROUP 17 /* Section group */
|
||||
#define SHT_SYMTAB_SHNDX 18 /* Extended section indeces */
|
||||
#define SHT_NUM 19 /* number of section types */
|
||||
#define SHT_LOOS 0x60000000 /* Start OS-specific */
|
||||
#define SHT_HIOS 0x6fffffff /* End OS-specific */
|
||||
#define SHT_LOPROC 0x70000000 /* reserved range for processor */
|
||||
#define SHT_HIPROC 0x7fffffff /* specific section header types */
|
||||
#define SHT_LOUSER 0x80000000 /* reserved range for application */
|
||||
#define SHT_HIUSER 0xffffffff /* specific indexes */
|
||||
|
||||
/* Section names */
|
||||
#define ELF_BSS ".bss" /* uninitialized data */
|
||||
#define ELF_COMMENT ".comment" /* version control information */
|
||||
#define ELF_DATA ".data" /* initialized data */
|
||||
#define ELF_DATA1 ".data1" /* initialized data */
|
||||
#define ELF_DEBUG ".debug" /* debug */
|
||||
#define ELF_DYNAMIC ".dynamic" /* dynamic linking information */
|
||||
#define ELF_DYNSTR ".dynstr" /* dynamic string table */
|
||||
#define ELF_DYNSYM ".dynsym" /* dynamic symbol table */
|
||||
#define ELF_FINI ".fini" /* termination code */
|
||||
#define ELF_FINI_ARRAY ".fini_array" /* Array of destructors */
|
||||
#define ELF_GOT ".got" /* global offset table */
|
||||
#define ELF_HASH ".hash" /* symbol hash table */
|
||||
#define ELF_INIT ".init" /* initialization code */
|
||||
#define ELF_INIT_ARRAY ".init_array" /* Array of constuctors */
|
||||
#define ELF_INTERP ".interp" /* Pathname of program interpreter */
|
||||
#define ELF_LINE ".line" /* Symbolic line numnber information */
|
||||
#define ELF_NOTE ".note" /* Contains note section */
|
||||
#define ELF_PLT ".plt" /* Procedure linkage table */
|
||||
#define ELF_PREINIT_ARRAY ".preinit_array" /* Array of pre-constructors */
|
||||
#define ELF_REL_DATA ".rel.data" /* relocation data */
|
||||
#define ELF_REL_FINI ".rel.fini" /* relocation termination code */
|
||||
#define ELF_REL_INIT ".rel.init" /* relocation initialization code */
|
||||
#define ELF_REL_DYN ".rel.dyn" /* relocaltion dynamic link info */
|
||||
#define ELF_REL_RODATA ".rel.rodata" /* relocation read-only data */
|
||||
#define ELF_REL_TEXT ".rel.text" /* relocation code */
|
||||
#define ELF_RODATA ".rodata" /* read-only data */
|
||||
#define ELF_RODATA1 ".rodata1" /* read-only data */
|
||||
#define ELF_SHSTRTAB ".shstrtab" /* section header string table */
|
||||
#define ELF_STRTAB ".strtab" /* string table */
|
||||
#define ELF_SYMTAB ".symtab" /* symbol table */
|
||||
#define ELF_SYMTAB_SHNDX ".symtab_shndx"/* symbol table section index */
|
||||
#define ELF_TBSS ".tbss" /* thread local uninit data */
|
||||
#define ELF_TDATA ".tdata" /* thread local init data */
|
||||
#define ELF_TDATA1 ".tdata1" /* thread local init data */
|
||||
#define ELF_TEXT ".text" /* code */
|
||||
|
||||
/* Section Attribute Flags - sh_flags */
|
||||
#define SHF_WRITE 0x1 /* Writable */
|
||||
#define SHF_ALLOC 0x2 /* occupies memory */
|
||||
#define SHF_EXECINSTR 0x4 /* executable */
|
||||
#define SHF_MERGE 0x10 /* Might be merged */
|
||||
#define SHF_STRINGS 0x20 /* Contains NULL terminated strings */
|
||||
#define SHF_INFO_LINK 0x40 /* sh_info contains SHT index */
|
||||
#define SHF_LINK_ORDER 0x80 /* Preserve order after combining*/
|
||||
#define SHF_OS_NONCONFORMING 0x100 /* Non-standard OS specific handling */
|
||||
#define SHF_GROUP 0x200 /* Member of section group */
|
||||
#define SHF_TLS 0x400 /* Thread local storage */
|
||||
#define SHF_MASKOS 0x0ff00000 /* OS specific */
|
||||
#define SHF_MASKPROC 0xf0000000 /* reserved bits for processor */
|
||||
/* specific section attributes */
|
||||
|
||||
/* Section Group Flags */
|
||||
#define GRP_COMDAT 0x1 /* COMDAT group */
|
||||
#define GRP_MASKOS 0x0ff00000 /* Mask OS specific flags */
|
||||
#define GRP_MASKPROC 0xf0000000 /* Mask processor specific flags */
|
||||
|
||||
/* Symbol Table Entry */
|
||||
typedef struct elf32_sym {
|
||||
Elf32_Word st_name; /* name - index into string table */
|
||||
Elf32_Addr st_value; /* symbol value */
|
||||
Elf32_Word st_size; /* symbol size */
|
||||
unsigned char st_info; /* type and binding */
|
||||
unsigned char st_other; /* 0 - no defined meaning */
|
||||
Elf32_Half st_shndx; /* section header index */
|
||||
} Elf32_Sym;
|
||||
|
||||
/* Symbol table index */
|
||||
#define STN_UNDEF 0 /* undefined */
|
||||
|
||||
/* Extract symbol info - st_info */
|
||||
#define ELF32_ST_BIND(x) ((x) >> 4)
|
||||
#define ELF32_ST_TYPE(x) (((unsigned int) x) & 0xf)
|
||||
#define ELF32_ST_INFO(b,t) (((b) << 4) + ((t) & 0xf))
|
||||
#define ELF32_ST_VISIBILITY(x) ((x) & 0x3)
|
||||
|
||||
/* Symbol Binding - ELF32_ST_BIND - st_info */
|
||||
#define STB_LOCAL 0 /* Local symbol */
|
||||
#define STB_GLOBAL 1 /* Global symbol */
|
||||
#define STB_WEAK 2 /* like global - lower precedence */
|
||||
#define STB_NUM 3 /* number of symbol bindings */
|
||||
#define STB_LOOS 10 /* reserved range for operating */
|
||||
#define STB_HIOS 12 /* system specific symbol bindings */
|
||||
#define STB_LOPROC 13 /* reserved range for processor */
|
||||
#define STB_HIPROC 15 /* specific symbol bindings */
|
||||
|
||||
/* Symbol type - ELF32_ST_TYPE - st_info */
|
||||
#define STT_NOTYPE 0 /* not specified */
|
||||
#define STT_OBJECT 1 /* data object */
|
||||
#define STT_FUNC 2 /* function */
|
||||
#define STT_SECTION 3 /* section */
|
||||
#define STT_FILE 4 /* file */
|
||||
#define STT_NUM 5 /* number of symbol types */
|
||||
#define STT_TLS 6 /* Thread local storage symbol */
|
||||
#define STT_LOOS 10 /* reserved range for operating */
|
||||
#define STT_HIOS 12 /* system specific symbol types */
|
||||
#define STT_LOPROC 13 /* reserved range for processor */
|
||||
#define STT_HIPROC 15 /* specific symbol types */
|
||||
|
||||
/* Symbol visibility - ELF32_ST_VISIBILITY - st_other */
|
||||
#define STV_DEFAULT 0 /* Normal visibility rules */
|
||||
#define STV_INTERNAL 1 /* Processor specific hidden class */
|
||||
#define STV_HIDDEN 2 /* Symbol unavailable in other mods */
|
||||
#define STV_PROTECTED 3 /* Not preemptible, not exported */
|
||||
|
||||
|
||||
/* Relocation entry with implicit addend */
|
||||
typedef struct
|
||||
{
|
||||
Elf32_Addr r_offset; /* offset of relocation */
|
||||
Elf32_Word r_info; /* symbol table index and type */
|
||||
} Elf32_Rel;
|
||||
|
||||
/* Relocation entry with explicit addend */
|
||||
typedef struct
|
||||
{
|
||||
Elf32_Addr r_offset; /* offset of relocation */
|
||||
Elf32_Word r_info; /* symbol table index and type */
|
||||
Elf32_Sword r_addend;
|
||||
} Elf32_Rela;
|
||||
|
||||
/* Extract relocation info - r_info */
|
||||
#define ELF32_R_SYM(i) ((i) >> 8)
|
||||
#define ELF32_R_TYPE(i) ((unsigned char) (i))
|
||||
#define ELF32_R_INFO(s,t) (((s) << 8) + (unsigned char)(t))
|
||||
|
||||
/* Program Header */
|
||||
typedef struct {
|
||||
Elf32_Word p_type; /* segment type */
|
||||
Elf32_Off p_offset; /* segment offset */
|
||||
Elf32_Addr p_vaddr; /* virtual address of segment */
|
||||
Elf32_Addr p_paddr; /* physical address - ignored? */
|
||||
Elf32_Word p_filesz; /* number of bytes in file for seg. */
|
||||
Elf32_Word p_memsz; /* number of bytes in mem. for seg. */
|
||||
Elf32_Word p_flags; /* flags */
|
||||
Elf32_Word p_align; /* memory alignment */
|
||||
} Elf32_Phdr;
|
||||
|
||||
/* Segment types - p_type */
|
||||
#define PT_NULL 0 /* unused */
|
||||
#define PT_LOAD 1 /* loadable segment */
|
||||
#define PT_DYNAMIC 2 /* dynamic linking section */
|
||||
#define PT_INTERP 3 /* the RTLD */
|
||||
#define PT_NOTE 4 /* auxiliary information */
|
||||
#define PT_SHLIB 5 /* reserved - purpose undefined */
|
||||
#define PT_PHDR 6 /* program header */
|
||||
#define PT_TLS 7 /* Thread local storage template */
|
||||
#define PT_NUM 8 /* Number of segment types */
|
||||
#define PT_LOOS 0x60000000 /* reserved range for operating */
|
||||
#define PT_HIOS 0x6fffffff /* system specific segment types */
|
||||
#define PT_LOPROC 0x70000000 /* reserved range for processor */
|
||||
#define PT_HIPROC 0x7fffffff /* specific segment types */
|
||||
|
||||
/* Segment flags - p_flags */
|
||||
#define PF_X 0x1 /* Executable */
|
||||
#define PF_W 0x2 /* Writable */
|
||||
#define PF_R 0x4 /* Readable */
|
||||
#define PF_MASKOS 0x0ff00000 /* OS specific segment flags */
|
||||
#define PF_MASKPROC 0xf0000000 /* reserved bits for processor */
|
||||
/* specific segment flags */
|
||||
/* Dynamic structure */
|
||||
typedef struct
|
||||
{
|
||||
Elf32_Sword d_tag; /* controls meaning of d_val */
|
||||
union
|
||||
{
|
||||
Elf32_Word d_val; /* Multiple meanings - see d_tag */
|
||||
Elf32_Addr d_ptr; /* program virtual address */
|
||||
} d_un;
|
||||
} Elf32_Dyn;
|
||||
|
||||
extern Elf32_Dyn _DYNAMIC[];
|
||||
|
||||
/* Dynamic Array Tags - d_tag */
|
||||
#define DT_NULL 0 /* marks end of _DYNAMIC array */
|
||||
#define DT_NEEDED 1 /* string table offset of needed lib */
|
||||
#define DT_PLTRELSZ 2 /* size of relocation entries in PLT */
|
||||
#define DT_PLTGOT 3 /* address PLT/GOT */
|
||||
#define DT_HASH 4 /* address of symbol hash table */
|
||||
#define DT_STRTAB 5 /* address of string table */
|
||||
#define DT_SYMTAB 6 /* address of symbol table */
|
||||
#define DT_RELA 7 /* address of relocation table */
|
||||
#define DT_RELASZ 8 /* size of relocation table */
|
||||
#define DT_RELAENT 9 /* size of relocation entry */
|
||||
#define DT_STRSZ 10 /* size of string table */
|
||||
#define DT_SYMENT 11 /* size of symbol table entry */
|
||||
#define DT_INIT 12 /* address of initialization func. */
|
||||
#define DT_FINI 13 /* address of termination function */
|
||||
#define DT_SONAME 14 /* string table offset of shared obj */
|
||||
#define DT_RPATH 15 /* string table offset of library
|
||||
search path */
|
||||
#define DT_SYMBOLIC 16 /* start sym search in shared obj. */
|
||||
#define DT_REL 17 /* address of rel. tbl. w addends */
|
||||
#define DT_RELSZ 18 /* size of DT_REL relocation table */
|
||||
#define DT_RELENT 19 /* size of DT_REL relocation entry */
|
||||
#define DT_PLTREL 20 /* PLT referenced relocation entry */
|
||||
#define DT_DEBUG 21 /* bugger */
|
||||
#define DT_TEXTREL 22 /* Allow rel. mod. to unwritable seg */
|
||||
#define DT_JMPREL 23 /* add. of PLT's relocation entries */
|
||||
#define DT_BIND_NOW 24 /* Process relocations of object */
|
||||
#define DT_INIT_ARRAY 25 /* Array with addresses of init fct */
|
||||
#define DT_FINI_ARRAY 26 /* Array with addresses of fini fct */
|
||||
#define DT_INIT_ARRAYSZ 27 /* Size in bytes of DT_INIT_ARRAY */
|
||||
#define DT_FINI_ARRAYSZ 28 /* Size in bytes of DT_FINI_ARRAY */
|
||||
#define DT_RUNPATH 29 /* Library search path */
|
||||
#define DT_FLAGS 30 /* Flags for the object being loaded */
|
||||
#define DT_ENCODING 32 /* Start of encoded range */
|
||||
#define DT_PREINIT_ARRAY 32 /* Array with addresses of preinit fct*/
|
||||
#define DT_PREINIT_ARRAYSZ 33 /* size in bytes of DT_PREINIT_ARRAY */
|
||||
#define DT_NUM 34 /* Number used. */
|
||||
#define DT_LOOS 0x60000000 /* reserved range for OS */
|
||||
#define DT_HIOS 0x6fffffff /* specific dynamic array tags */
|
||||
#define DT_LOPROC 0x70000000 /* reserved range for processor */
|
||||
#define DT_HIPROC 0x7fffffff /* specific dynamic array tags */
|
||||
|
||||
/* Dynamic Tag Flags - d_un.d_val */
|
||||
#define DF_ORIGIN 0x01 /* Object may use DF_ORIGIN */
|
||||
#define DF_SYMBOLIC 0x02 /* Symbol resolutions starts here */
|
||||
#define DF_TEXTREL 0x04 /* Object contains text relocations */
|
||||
#define DF_BIND_NOW 0x08 /* No lazy binding for this object */
|
||||
#define DF_STATIC_TLS 0x10 /* Static thread local storage */
|
||||
|
||||
/* Standard ELF hashing function */
|
||||
unsigned long elf_hash(const unsigned char *name);
|
||||
|
||||
#define ELF_TARG_VER 1 /* The ver for which this code is intended */
|
||||
|
||||
/*
|
||||
* XXX - PowerPC defines really don't belong in here,
|
||||
* but we'll put them in for simplicity.
|
||||
*/
|
||||
|
||||
/* Values for Elf32/64_Ehdr.e_flags. */
|
||||
#define EF_PPC_EMB 0x80000000 /* PowerPC embedded flag */
|
||||
|
||||
/* Cygnus local bits below */
|
||||
#define EF_PPC_RELOCATABLE 0x00010000 /* PowerPC -mrelocatable flag*/
|
||||
#define EF_PPC_RELOCATABLE_LIB 0x00008000 /* PowerPC -mrelocatable-lib
|
||||
flag */
|
||||
|
||||
/* PowerPC relocations defined by the ABIs */
|
||||
#define R_PPC_NONE 0
|
||||
#define R_PPC_ADDR32 1 /* 32bit absolute address */
|
||||
#define R_PPC_ADDR24 2 /* 26bit address, 2 bits ignored. */
|
||||
#define R_PPC_ADDR16 3 /* 16bit absolute address */
|
||||
#define R_PPC_ADDR16_LO 4 /* lower 16bit of absolute address */
|
||||
#define R_PPC_ADDR16_HI 5 /* high 16bit of absolute address */
|
||||
#define R_PPC_ADDR16_HA 6 /* adjusted high 16bit */
|
||||
#define R_PPC_ADDR14 7 /* 16bit address, 2 bits ignored */
|
||||
#define R_PPC_ADDR14_BRTAKEN 8
|
||||
#define R_PPC_ADDR14_BRNTAKEN 9
|
||||
#define R_PPC_REL24 10 /* PC relative 26 bit */
|
||||
#define R_PPC_REL14 11 /* PC relative 16 bit */
|
||||
#define R_PPC_REL14_BRTAKEN 12
|
||||
#define R_PPC_REL14_BRNTAKEN 13
|
||||
#define R_PPC_GOT16 14
|
||||
#define R_PPC_GOT16_LO 15
|
||||
#define R_PPC_GOT16_HI 16
|
||||
#define R_PPC_GOT16_HA 17
|
||||
#define R_PPC_PLTREL24 18
|
||||
#define R_PPC_COPY 19
|
||||
#define R_PPC_GLOB_DAT 20
|
||||
#define R_PPC_JMP_SLOT 21
|
||||
#define R_PPC_RELATIVE 22
|
||||
#define R_PPC_LOCAL24PC 23
|
||||
#define R_PPC_UADDR32 24
|
||||
#define R_PPC_UADDR16 25
|
||||
#define R_PPC_REL32 26
|
||||
#define R_PPC_PLT32 27
|
||||
#define R_PPC_PLTREL32 28
|
||||
#define R_PPC_PLT16_LO 29
|
||||
#define R_PPC_PLT16_HI 30
|
||||
#define R_PPC_PLT16_HA 31
|
||||
#define R_PPC_SDAREL16 32
|
||||
#define R_PPC_SECTOFF 33
|
||||
#define R_PPC_SECTOFF_LO 34
|
||||
#define R_PPC_SECTOFF_HI 35
|
||||
#define R_PPC_SECTOFF_HA 36
|
||||
/* Keep this the last entry. */
|
||||
#define R_PPC_NUM 37
|
||||
|
||||
/* The remaining relocs are from the Embedded ELF ABI, and are not
|
||||
in the SVR4 ELF ABI. */
|
||||
#define R_PPC_EMB_NADDR32 101
|
||||
#define R_PPC_EMB_NADDR16 102
|
||||
#define R_PPC_EMB_NADDR16_LO 103
|
||||
#define R_PPC_EMB_NADDR16_HI 104
|
||||
#define R_PPC_EMB_NADDR16_HA 105
|
||||
#define R_PPC_EMB_SDAI16 106
|
||||
#define R_PPC_EMB_SDA2I16 107
|
||||
#define R_PPC_EMB_SDA2REL 108
|
||||
#define R_PPC_EMB_SDA21 109 /* 16 bit offset in SDA */
|
||||
#define R_PPC_EMB_MRKREF 110
|
||||
#define R_PPC_EMB_RELSEC16 111
|
||||
#define R_PPC_EMB_RELST_LO 112
|
||||
#define R_PPC_EMB_RELST_HI 113
|
||||
#define R_PPC_EMB_RELST_HA 114
|
||||
#define R_PPC_EMB_BIT_FLD 115
|
||||
#define R_PPC_EMB_RELSDA 116 /* 16 bit relative offset in SDA */
|
||||
|
||||
/* Diab tool relocations. */
|
||||
#define R_PPC_DIAB_SDA21_LO 180 /* like EMB_SDA21, but lower 16 bit */
|
||||
#define R_PPC_DIAB_SDA21_HI 181 /* like EMB_SDA21, but high 16 bit */
|
||||
#define R_PPC_DIAB_SDA21_HA 182 /* like EMB_SDA21, adjusted high 16 */
|
||||
#define R_PPC_DIAB_RELSDA_LO 183 /* like EMB_RELSDA, but lower 16 bit */
|
||||
#define R_PPC_DIAB_RELSDA_HI 184 /* like EMB_RELSDA, but high 16 bit */
|
||||
#define R_PPC_DIAB_RELSDA_HA 185 /* like EMB_RELSDA, adjusted high 16 */
|
||||
|
||||
/* This is a phony reloc to handle any old fashioned TOC16 references
|
||||
that may still be in object files. */
|
||||
#define R_PPC_TOC16 255
|
||||
|
||||
#endif /* _ELF_H */
|
||||
|
||||
774
channel/channelapp/source/font.c
Normal file
774
channel/channelapp/source/font.c
Normal file
@@ -0,0 +1,774 @@
|
||||
#include <string.h>
|
||||
#include <ogcsys.h>
|
||||
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
|
||||
#include "../config.h"
|
||||
#include "panic.h"
|
||||
#include "theme.h"
|
||||
#include "font.h"
|
||||
|
||||
#include "droid_ttf.h"
|
||||
#include "droidbold_ttf.h"
|
||||
|
||||
#define ROUNDUP4B(x) ((x + 4 - 1) & ~(4 - 1))
|
||||
|
||||
#define X_RATIO (widescreen?WIDESCREEN_RATIO:1.0)
|
||||
|
||||
//#define FONT_CHECKER
|
||||
|
||||
FT_Library library;
|
||||
|
||||
typedef struct {
|
||||
int codepoint;
|
||||
int next_idx;
|
||||
int glyph_index;
|
||||
int valid;
|
||||
gfx_entity *texture;
|
||||
int x, y;
|
||||
int w, h;
|
||||
FT_Pos dx, dy;
|
||||
} font_glyph;
|
||||
|
||||
typedef struct {
|
||||
const void *data;
|
||||
u32 data_len;
|
||||
FT_Face face;
|
||||
int max_glyphs;
|
||||
int num_glyphs;
|
||||
int em_w, em_h;
|
||||
int em_height;
|
||||
int ascender;
|
||||
int descender;
|
||||
int height;
|
||||
font_glyph *glyphs;
|
||||
} font_face;
|
||||
|
||||
static font_face *fonts[FONT_MAX];
|
||||
|
||||
typedef struct {
|
||||
int size;
|
||||
const void *data;
|
||||
const u32 *data_len; // bin2s stupidity
|
||||
} font_default;
|
||||
|
||||
const font_default default_fonts[FONT_MAX] =
|
||||
{
|
||||
[FONT_LABEL] = { 16, droidbold_ttf, &droidbold_ttf_size},
|
||||
[FONT_DLGTITLE] = { 20, droidbold_ttf, &droidbold_ttf_size},
|
||||
[FONT_MEMO] = { 16, droid_ttf, &droid_ttf_size},
|
||||
[FONT_APPNAME] = { 20, droidbold_ttf, &droidbold_ttf_size},
|
||||
[FONT_APPDESC] = { 16, droidbold_ttf, &droidbold_ttf_size},
|
||||
[FONT_BUTTON] = { 20, droidbold_ttf, &droidbold_ttf_size},
|
||||
[FONT_BUTTON_DESEL] = { 20, droid_ttf, &droid_ttf_size},
|
||||
};
|
||||
|
||||
#define GLYPH_CACHE_ROOT 64
|
||||
#define GLYPH_CACHE_INIT 32
|
||||
#define GLYPH_CACHE_GROW 32
|
||||
|
||||
font_glyph *font_get_char(font_id id, int codepoint);
|
||||
|
||||
#if 0
|
||||
static const char *utf8(u32 codepoint)
|
||||
{
|
||||
static char buf[5];
|
||||
if (codepoint < 0x80) {
|
||||
buf[0] = codepoint;
|
||||
buf[1] = 0;
|
||||
} else if (codepoint < 0x800) {
|
||||
buf[0] = 0xC0 | (codepoint>>6);
|
||||
buf[1] = 0x80 | (codepoint&0x3F);
|
||||
buf[2] = 0;
|
||||
} else if (codepoint < 0x10000) {
|
||||
buf[0] = 0xE0 | (codepoint>>12);
|
||||
buf[1] = 0x80 | ((codepoint>>6)&0x3F);
|
||||
buf[2] = 0x80 | (codepoint&0x3F);
|
||||
buf[3] = 0;
|
||||
} else if (codepoint < 0x110000) {
|
||||
buf[0] = 0xF0 | (codepoint>>18);
|
||||
buf[1] = 0x80 | ((codepoint>>12)&0x3F);
|
||||
buf[2] = 0x80 | ((codepoint>>6)&0x3F);
|
||||
buf[3] = 0x80 | (codepoint&0x3F);
|
||||
buf[4] = 0;
|
||||
} else {
|
||||
buf[0] = '?';
|
||||
buf[1] = 0;
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
#endif
|
||||
|
||||
int font_load(font_id id, bool use_theme)
|
||||
{
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
const void *data;
|
||||
u32 data_len;
|
||||
int em;
|
||||
|
||||
font_face *font;
|
||||
|
||||
if (fonts[id])
|
||||
return 0;
|
||||
|
||||
gprintf("Loading font ID %d\n", id);
|
||||
|
||||
data = default_fonts[id].data;
|
||||
data_len = *default_fonts[id].data_len;
|
||||
em = default_fonts[id].size;
|
||||
|
||||
if (use_theme && theme_fonts[id].data) {
|
||||
data = theme_fonts[id].data;
|
||||
data_len = theme_fonts[id].data_len;
|
||||
}
|
||||
|
||||
if (use_theme && theme_fonts[id].size)
|
||||
em = theme_fonts[id].size;
|
||||
|
||||
// maybe we can reuse the entire font, look for other fonts
|
||||
for (i=0; i<FONT_MAX; i++) {
|
||||
if (fonts[i] && fonts[i]->data == data && fonts[i]->em_h == em) {
|
||||
gprintf("Cloned font object from font ID %d\n", i);
|
||||
fonts[id] = fonts[i];
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
font = pmalloc(sizeof(*font));
|
||||
memset(font, 0, sizeof(*font));
|
||||
|
||||
font->data = data;
|
||||
font->data_len = data_len;
|
||||
font->em_h = font->em_w = em;
|
||||
|
||||
font->face = NULL;
|
||||
|
||||
// maybe we can reuse the FT_Font object, look for other fonts
|
||||
for (i=0; i<FONT_MAX; i++) {
|
||||
if (fonts[i] && fonts[i]->data == font->data) {
|
||||
gprintf("Reused FreeType font subobject from font ID %d\n", i);
|
||||
font->face = fonts[i]->face;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!font->face) {
|
||||
ret = FT_New_Memory_Face(library, font->data, font->data_len, 0, &font->face);
|
||||
if (ret != 0) {
|
||||
gprintf("Failed to load font\n");
|
||||
// try to fall back
|
||||
if (use_theme && theme_fonts[id].data) {
|
||||
free(font);
|
||||
theme_fonts[id].data = NULL;
|
||||
return font_load(id, false);
|
||||
}
|
||||
return ret;
|
||||
} else {
|
||||
gprintf("Loaded font at %p\n", font->data);
|
||||
}
|
||||
}
|
||||
gprintf("Font contains %ld faces\n", font->face->num_faces);
|
||||
gprintf("Face contains %ld glyphs\n", font->face->num_glyphs);
|
||||
|
||||
if (widescreen)
|
||||
font->em_w = (int)(font->em_w / WIDESCREEN_RATIO + 0.5);
|
||||
|
||||
ret = FT_Set_Pixel_Sizes(font->face, font->em_w, font->em_h);
|
||||
if (ret) {
|
||||
gprintf("FT_Set_Pixel_Sizes failed: %d\n", ret);
|
||||
free(font);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!FT_HAS_KERNING(font->face))
|
||||
gprintf("Font has no usable kerning data\n");
|
||||
else
|
||||
gprintf("Font has kerning data\n");
|
||||
|
||||
font->max_glyphs = GLYPH_CACHE_ROOT+GLYPH_CACHE_INIT;
|
||||
font->num_glyphs = GLYPH_CACHE_ROOT; //base "hashtable" set
|
||||
font->glyphs = pmalloc(sizeof(font_glyph) * font->max_glyphs);
|
||||
memset(font->glyphs, 0, sizeof(font_glyph) * font->max_glyphs);
|
||||
for(i=0; i<font->max_glyphs; i++) {
|
||||
font->glyphs[i].codepoint = -1;
|
||||
font->glyphs[i].next_idx = -1;
|
||||
}
|
||||
|
||||
fonts[id] = font;
|
||||
|
||||
font_glyph *glyph = font_get_char(id, 'M');
|
||||
|
||||
font->em_height = glyph->h; // height of capital M
|
||||
font->ascender = (font->face->size->metrics.ascender+32)>>6;
|
||||
font->descender = (font->face->size->metrics.descender+32)>>6;
|
||||
font->height = (font->face->size->metrics.height+32)>>6;
|
||||
|
||||
gprintf("Ascender is %d\n", font->ascender);
|
||||
gprintf("Descender is %d\n", font->descender);
|
||||
gprintf("Height is %d\n", font->height);
|
||||
|
||||
gprintf("Font ID %d loaded\n", id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
font_glyph *font_get_char(font_id id, int codepoint)
|
||||
{
|
||||
int ret;
|
||||
font_glyph *glyph;
|
||||
|
||||
font_load(id,1);
|
||||
font_face *font = fonts[id];
|
||||
|
||||
glyph = &font->glyphs[codepoint&(GLYPH_CACHE_ROOT-1)];
|
||||
|
||||
if (glyph->codepoint != -1) {
|
||||
while(1) {
|
||||
if (glyph->codepoint == codepoint) {
|
||||
//gprintf("FONT: Glyph %d (%s) is cached\n", codepoint, utf8(codepoint));
|
||||
return glyph;
|
||||
}
|
||||
if (glyph->next_idx == -1) {
|
||||
glyph->next_idx = font->num_glyphs;
|
||||
if (font->num_glyphs == font->max_glyphs) {
|
||||
font->glyphs = prealloc(font->glyphs, sizeof(font_glyph) * (font->max_glyphs + GLYPH_CACHE_GROW));
|
||||
memset(&font->glyphs[font->max_glyphs], 0, GLYPH_CACHE_GROW*sizeof(font_glyph));
|
||||
font->max_glyphs += GLYPH_CACHE_GROW;
|
||||
gprintf("FONT: expanded glyph cache size to %d\n", font->max_glyphs);
|
||||
}
|
||||
glyph = &font->glyphs[font->num_glyphs];
|
||||
font->num_glyphs++;
|
||||
break;
|
||||
}
|
||||
glyph = &font->glyphs[glyph->next_idx];
|
||||
}
|
||||
}
|
||||
|
||||
memset(glyph, 0, sizeof(*glyph));
|
||||
glyph->codepoint = codepoint;
|
||||
glyph->next_idx = -1;
|
||||
glyph->valid = 0;
|
||||
glyph->texture = pmalloc(sizeof(*glyph->texture));
|
||||
glyph->dx = 0;
|
||||
glyph->dy = 0;
|
||||
|
||||
FT_GlyphSlot slot = font->face->glyph;
|
||||
FT_UInt glyph_index;
|
||||
|
||||
ret = FT_Set_Pixel_Sizes(font->face, font->em_w, font->em_h);
|
||||
if (ret)
|
||||
return glyph;
|
||||
|
||||
glyph_index = FT_Get_Char_Index(font->face, codepoint);
|
||||
ret = FT_Load_Glyph(font->face, glyph_index, FT_LOAD_DEFAULT);
|
||||
if (ret)
|
||||
return glyph;
|
||||
|
||||
ret = FT_Render_Glyph(slot, FT_RENDER_MODE_NORMAL);
|
||||
if (ret)
|
||||
return glyph;
|
||||
|
||||
glyph->dx = slot->advance.x;
|
||||
glyph->dy = slot->advance.y;
|
||||
glyph->glyph_index = glyph_index;
|
||||
|
||||
int cw = slot->bitmap.width;
|
||||
int ch = slot->bitmap.rows;
|
||||
|
||||
glyph->w = cw;
|
||||
glyph->h = ch;
|
||||
|
||||
if (!cw || !ch)
|
||||
return glyph;
|
||||
|
||||
int tw = (cw+7)/8;
|
||||
int th = (ch+3)/4;
|
||||
|
||||
int tpitch = tw * 32;
|
||||
|
||||
u8 *pix = pmemalign(32, tw*th*32);
|
||||
memset(pix, 0, tw*th*32);
|
||||
|
||||
int x,y;
|
||||
u8 *p = slot->bitmap.buffer;
|
||||
for(y=0; y<ch; y++) {
|
||||
u8 *lp = p;
|
||||
int ty = y/4;
|
||||
int py = y%4;
|
||||
u8 *lpix = pix + ty*tpitch + py*8;
|
||||
for(x=0; x<cw; x++) {
|
||||
int tx = x/8;
|
||||
int px = x%8;
|
||||
#ifndef FONT_CHECKER
|
||||
lpix[32*tx + px] = *lp++;
|
||||
#else
|
||||
if ((x+y)&1)
|
||||
lpix[32*tx + px] = 0xff;
|
||||
else
|
||||
lpix[32*tx + px] = 0;
|
||||
#endif
|
||||
}
|
||||
p += slot->bitmap.pitch;
|
||||
}
|
||||
|
||||
gfx_gen_tex (glyph->texture, 8*tw, 4*th, pix, GFXT_A8);
|
||||
|
||||
glyph->x = slot->bitmap_left;
|
||||
glyph->y = slot->bitmap_top;
|
||||
glyph->valid = 1;
|
||||
|
||||
//gprintf("FONT: Rendered and cached glyph %d (%s) at pos %d size %dx%d\n", codepoint, utf8(codepoint), glyph-font->glyphs, cw, ch);
|
||||
//gprintf("Free MEM1: %ld MEM2: %ld\n", SYS_GetArena1Size(), SYS_GetArena2Size());
|
||||
return glyph;
|
||||
}
|
||||
|
||||
void font_kern(font_id id, int left, int right, FT_Pos *dx, FT_Pos *dy)
|
||||
{
|
||||
FT_Vector delta;
|
||||
if (!FT_HAS_KERNING(fonts[id]->face)) {
|
||||
return;
|
||||
}
|
||||
if (FT_Get_Kerning(fonts[id]->face, left, right, FT_KERNING_UNFITTED, &delta))
|
||||
return;
|
||||
//gprintf("Kern font %d for glyphs %d,%d is %ld,%ld\n", id, left, right, delta.x, delta.y);
|
||||
*dx += delta.x;
|
||||
*dy += delta.y;
|
||||
}
|
||||
|
||||
|
||||
void font_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
memset(fonts, 0, sizeof(fonts));
|
||||
|
||||
ret = FT_Init_FreeType(&library);
|
||||
if (ret != 0) {
|
||||
gprintf("FreeType init failed (%d)\n", ret);
|
||||
return;
|
||||
}
|
||||
gprintf("FreeType initialized\n");
|
||||
}
|
||||
|
||||
void font_clear (void) {
|
||||
int id,id2;
|
||||
for (id=0; id<FONT_MAX; id++) {
|
||||
int g;
|
||||
if (!fonts[id])
|
||||
continue;
|
||||
|
||||
// kill all clones first
|
||||
for (id2=0; id2<FONT_MAX; id2++) {
|
||||
if (id2 != id && fonts[id2] && fonts[id2] == fonts[id])
|
||||
fonts[id2] = NULL;
|
||||
}
|
||||
|
||||
for (g=0; g<fonts[id]->num_glyphs; g++) {
|
||||
if (fonts[id]->glyphs[g].texture)
|
||||
free(fonts[id]->glyphs[g].texture);
|
||||
fonts[id]->glyphs[g].texture = NULL;
|
||||
}
|
||||
free(fonts[id]->glyphs);
|
||||
fonts[id]->glyphs = NULL;
|
||||
|
||||
if (fonts[id]->face) {
|
||||
FT_Face face = fonts[id]->face;
|
||||
FT_Done_Face(face);
|
||||
// other fonts can share this face object, so nuke all
|
||||
for (id2=0; id2<FONT_MAX; id2++) {
|
||||
if (fonts[id2] && fonts[id2]->face == face)
|
||||
fonts[id2]->face = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
free(fonts[id]);
|
||||
fonts[id] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void font_deinit (void) {
|
||||
font_clear();
|
||||
FT_Done_FreeType(library);
|
||||
}
|
||||
|
||||
static u32 utf8_get_char(const char **s)
|
||||
{
|
||||
const char *c = *s;
|
||||
u32 mbc = '?';
|
||||
|
||||
if (!c[0])
|
||||
return 0;
|
||||
|
||||
if (c[0] <= 0x7f) {
|
||||
mbc = c[0];
|
||||
c++;
|
||||
} else if (c[0] >= 0xc0 && c[0] <= 0xdf) {
|
||||
if ((c[1]&0xc0) != 0x80) {
|
||||
c++;
|
||||
if (c[0])
|
||||
c++;
|
||||
} else {
|
||||
mbc = (c[1]&0x3f) | ((c[0]&0x1f)<<6);
|
||||
c+=2;
|
||||
}
|
||||
} else if (c[0] >= 0xe0 && c[0] <= 0xef) {
|
||||
if (((c[1]&0xc0) != 0x80) || ((c[2]&0xc0) != 0x80)) {
|
||||
c++;
|
||||
if (c[0])
|
||||
c++;
|
||||
if (c[0])
|
||||
c++;
|
||||
} else {
|
||||
mbc = (c[2]&0x3f) | ((c[1]&0x3f)<<6) | ((c[0]&0x1f)<<12);
|
||||
c+=3;
|
||||
}
|
||||
} else if (c[0] >= 0xf0 && c[0] <= 0xf7) {
|
||||
if (((c[1]&0xc0) != 0x80) || ((c[2]&0xc0) != 0x80) || ((c[3]&0xc0) != 0x80)) {
|
||||
c++;
|
||||
if (c[0])
|
||||
c++;
|
||||
if (c[0])
|
||||
c++;
|
||||
if (c[0])
|
||||
c++;
|
||||
} else {
|
||||
mbc = (c[3]&0x3f) | ((c[2]&0x3f)<<6) | ((c[1]&0x3f)<<12) | ((c[0]&0x1f)<<18);
|
||||
c+=4;
|
||||
}
|
||||
} else {
|
||||
c++;
|
||||
}
|
||||
*s = c;
|
||||
return mbc;
|
||||
}
|
||||
|
||||
int font_get_ascender(font_id id) {
|
||||
font_load(id,1);
|
||||
|
||||
return fonts[id]->ascender;
|
||||
}
|
||||
|
||||
int font_get_height(font_id id) {
|
||||
font_load(id,1);
|
||||
|
||||
return fonts[id]->ascender - fonts[id]->descender;
|
||||
}
|
||||
|
||||
int font_get_em_height(font_id id) {
|
||||
font_load(id,1);
|
||||
|
||||
return fonts[id]->em_height;
|
||||
}
|
||||
|
||||
int font_get_y_spacing(font_id id) {
|
||||
font_load(id,1);
|
||||
|
||||
return fonts[id]->height;
|
||||
}
|
||||
|
||||
int font_get_min_y(font_id id) {
|
||||
font_load(id,1);
|
||||
|
||||
return -fonts[id]->descender;
|
||||
}
|
||||
|
||||
u16 font_get_string_width (font_id id, const char *s, int count) {
|
||||
int i = 0;
|
||||
u32 mbc;
|
||||
int cx = 0;
|
||||
int cy = 0;
|
||||
|
||||
FT_Pos cdx = 0, cdy = 0;
|
||||
u32 previous = 0;
|
||||
|
||||
font_load(id,1);
|
||||
|
||||
while((mbc = utf8_get_char(&s))) {
|
||||
if (mbc == '\n') {
|
||||
if (*s)
|
||||
mbc = ' ';
|
||||
else
|
||||
continue;
|
||||
}
|
||||
if (mbc == '\r')
|
||||
continue;
|
||||
font_glyph *glyph = font_get_char(id, mbc);
|
||||
|
||||
if (previous)
|
||||
font_kern(id, previous, glyph->glyph_index, &cdx, &cdy);
|
||||
|
||||
cx += (cdx+32) >> 6;
|
||||
cy += (cdy+32) >> 6;
|
||||
|
||||
cdx = glyph->dx;
|
||||
cdy = glyph->dy;
|
||||
previous = glyph->glyph_index;
|
||||
i++;
|
||||
if (i == count)
|
||||
break;
|
||||
}
|
||||
|
||||
cx += (cdx+32) >> 6;
|
||||
cy += (cdy+32) >> 6;
|
||||
|
||||
return cx;
|
||||
}
|
||||
|
||||
int font_get_char_count (font_id id, const char *s, u16 max_width) {
|
||||
//u16 res = 0;
|
||||
int i = 0;
|
||||
u32 mbc;
|
||||
int cx = 0;
|
||||
int cy = 0;
|
||||
|
||||
FT_Pos cdx = 0, cdy = 0;
|
||||
u32 previous = 0;
|
||||
|
||||
max_width /= X_RATIO;
|
||||
|
||||
font_load(id,1);
|
||||
|
||||
while((mbc = utf8_get_char(&s))) {
|
||||
if (mbc == '\n') {
|
||||
if (*s)
|
||||
mbc = ' ';
|
||||
else
|
||||
continue;
|
||||
}
|
||||
if (mbc == '\r')
|
||||
continue;
|
||||
font_glyph *glyph = font_get_char(id, mbc);
|
||||
|
||||
if (previous)
|
||||
font_kern(id, previous, glyph->glyph_index, &cdx, &cdy);
|
||||
|
||||
cx += (cdx+32) >> 6;
|
||||
cy += (cdy+32) >> 6;
|
||||
|
||||
if (max_width && (cx >= max_width))
|
||||
return i;
|
||||
|
||||
cdx = glyph->dx;
|
||||
cdy = glyph->dy;
|
||||
previous = glyph->glyph_index;
|
||||
i++;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
int font_wrap_string (char ***lines, font_id id, const char *s,
|
||||
u16 max_width) {
|
||||
const char *p = s;
|
||||
char **res = NULL;
|
||||
int line = 0;
|
||||
int start = 0;
|
||||
int end = 0;
|
||||
int ls = -1;
|
||||
bool lb;
|
||||
|
||||
int cx = 0;
|
||||
int cy = 0;
|
||||
|
||||
int i = 0;
|
||||
u32 mbc;
|
||||
|
||||
FT_Pos cdx = 0, cdy = 0;
|
||||
u32 previous = 0;
|
||||
|
||||
max_width /= X_RATIO;
|
||||
|
||||
font_load(id,1);
|
||||
|
||||
while (true) {
|
||||
i = p - s;
|
||||
mbc = utf8_get_char(&p);
|
||||
lb = false;
|
||||
|
||||
if (mbc == ' ')
|
||||
ls = p - s;
|
||||
|
||||
if ((mbc == '\n') || mbc == 0) {
|
||||
lb = true;
|
||||
end = i;
|
||||
i = p - s;
|
||||
} else if (mbc == '\r') {
|
||||
continue;
|
||||
} else {
|
||||
font_glyph *glyph = font_get_char(id, mbc);
|
||||
|
||||
if (previous)
|
||||
font_kern(id, previous, glyph->glyph_index, &cdx, &cdy);
|
||||
|
||||
cx += (cdx+32) >> 6;
|
||||
cy += (cdy+32) >> 6;
|
||||
|
||||
int w = (glyph->dx+32) >> 6;
|
||||
if ((glyph->w + glyph->x) > w)
|
||||
w = glyph->w + glyph->x;
|
||||
|
||||
if ((cx + w) >= max_width) {
|
||||
lb = true;
|
||||
if (ls <= start) {
|
||||
if (i == start)
|
||||
i++;
|
||||
end = i;
|
||||
p = &s[i];
|
||||
} else {
|
||||
end = ls;
|
||||
i = ls;
|
||||
p = &s[i];
|
||||
}
|
||||
}
|
||||
|
||||
cdx = glyph->dx;
|
||||
cdy = glyph->dy;
|
||||
previous = glyph->glyph_index;
|
||||
}
|
||||
|
||||
if (lb) {
|
||||
res = prealloc (res, (line + 1) * sizeof (char **));
|
||||
if (end <= start)
|
||||
res[line] = NULL;
|
||||
else {
|
||||
res[line] = strndup (&s[start], end - start);
|
||||
}
|
||||
|
||||
line++;
|
||||
start = i;
|
||||
cx = 0;
|
||||
cy = 0;
|
||||
cdx = 0;
|
||||
cdy = 0;
|
||||
previous = 0;
|
||||
}
|
||||
|
||||
if (mbc == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
*lines = res;
|
||||
return line;
|
||||
}
|
||||
|
||||
void font_free_lines (char **lines, u32 count) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; ++i)
|
||||
free (lines[i]);
|
||||
|
||||
free (lines);
|
||||
}
|
||||
|
||||
void font_plot_string (gfx_queue_entry *entries, int count, font_id id,
|
||||
const char *s, u16 x, u16 y, u16 layer, u16 width,
|
||||
font_xalign xalign, font_yalign yalign) {
|
||||
int cx;
|
||||
int cy;
|
||||
|
||||
|
||||
if (!count)
|
||||
return;
|
||||
|
||||
cx = x;
|
||||
cy = y;
|
||||
|
||||
cx /= X_RATIO;
|
||||
|
||||
switch (xalign) {
|
||||
case FA_LEFT:
|
||||
break;
|
||||
|
||||
case FA_CENTERED:
|
||||
cx += (width/X_RATIO - font_get_string_width (id, s, count)) / 2;
|
||||
break;
|
||||
|
||||
case FA_RIGHT:
|
||||
cx += width/X_RATIO - font_get_string_width (id, s, count);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (yalign) {
|
||||
case FA_ASCENDER:
|
||||
cy += fonts[id]->ascender;
|
||||
break;
|
||||
case FA_EM_TOP:
|
||||
cy += fonts[id]->em_height;
|
||||
break;
|
||||
case FA_EM_CENTERED:
|
||||
cy += fonts[id]->em_height/2;
|
||||
break;
|
||||
case FA_BASELINE:
|
||||
break;
|
||||
case FA_DESCENDER:
|
||||
cy += fonts[id]->descender;
|
||||
break;
|
||||
}
|
||||
|
||||
FT_Pos cdx = 0, cdy = 0;
|
||||
u32 previous = 0;
|
||||
int first = 1;
|
||||
u32 mbc;
|
||||
|
||||
while ((mbc = utf8_get_char(&s))) {
|
||||
if (mbc == '\n') {
|
||||
if (*s)
|
||||
mbc = ' ';
|
||||
else
|
||||
break;
|
||||
}
|
||||
if (mbc == '\r')
|
||||
continue;
|
||||
font_glyph *glyph = font_get_char(id, mbc);
|
||||
|
||||
if (previous)
|
||||
font_kern(id, previous, glyph->glyph_index, &cdx, &cdy);
|
||||
|
||||
cx += (cdx+32) >> 6;
|
||||
cy += (cdy+32) >> 6;
|
||||
|
||||
if (!glyph->valid) {
|
||||
entries->type = GFXQ_NULL;
|
||||
goto next_glyph;
|
||||
}
|
||||
|
||||
// fudgity fudge, helps make text left-align slightly better/nicer,
|
||||
// especially with differing fonts.
|
||||
// do this only for chars with glyph->x > 0. Those with glyph->x < 0
|
||||
// are usually kerned together and look better a bit to the left anyway.
|
||||
if(xalign == FA_LEFT && first && glyph->x > 0)
|
||||
cx -= glyph->x;
|
||||
|
||||
gfx_qe_entity (entries, glyph->texture,
|
||||
((float)(cx + glyph->x)) * X_RATIO,
|
||||
cy - glyph->y,
|
||||
layer, theme_fonts[id].color);
|
||||
|
||||
//gprintf("Render %d (%s) at %d %d -> %d %d size %d %d\n", mbc, utf8(mbc), cx, cy, cx + glyph->x, cy - glyph->y + fonts[id]->height, glyph->texture->w, glyph->texture->h);
|
||||
|
||||
first = 0;
|
||||
|
||||
next_glyph:
|
||||
count--;
|
||||
entries++;
|
||||
|
||||
if (!count)
|
||||
break;
|
||||
|
||||
cdx = glyph->dx;
|
||||
cdy = glyph->dy;
|
||||
previous = glyph->glyph_index;
|
||||
}
|
||||
|
||||
if (count) {
|
||||
gprintf("BUG: %d queue entries empty, padding with NULLs\n", count);
|
||||
while (count--) {
|
||||
entries->type = GFXQ_NULL;
|
||||
// this superfluous statement carefully chosen for optimal crashiness
|
||||
entries++->entity.coords.z = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
54
channel/channelapp/source/font.h
Normal file
54
channel/channelapp/source/font.h
Normal file
@@ -0,0 +1,54 @@
|
||||
#ifndef _FONT_H_
|
||||
#define _FONT_H_
|
||||
|
||||
#include <gctypes.h>
|
||||
|
||||
#include "gfx.h"
|
||||
|
||||
typedef enum {
|
||||
FONT_LABEL = 0,
|
||||
FONT_DLGTITLE,
|
||||
FONT_MEMO,
|
||||
FONT_APPNAME,
|
||||
FONT_APPDESC,
|
||||
FONT_BUTTON,
|
||||
FONT_BUTTON_DESEL,
|
||||
|
||||
FONT_MAX
|
||||
} font_id;
|
||||
|
||||
typedef enum {
|
||||
FA_LEFT = 0,
|
||||
FA_CENTERED,
|
||||
FA_RIGHT
|
||||
} font_xalign;
|
||||
|
||||
typedef enum {
|
||||
FA_ASCENDER = 0,
|
||||
FA_EM_TOP,
|
||||
FA_EM_CENTERED,
|
||||
FA_BASELINE,
|
||||
FA_DESCENDER,
|
||||
} font_yalign;
|
||||
|
||||
|
||||
void font_init (void);
|
||||
void font_clear (void);
|
||||
void font_deinit (void);
|
||||
|
||||
int font_get_height (font_id id);
|
||||
int font_get_em_height (font_id id);
|
||||
int font_get_ascender (font_id id);
|
||||
int font_get_y_spacing (font_id id);
|
||||
int font_get_min_y(font_id id);
|
||||
|
||||
int font_get_char_count (font_id id, const char *s, u16 max_width);
|
||||
int font_wrap_string (char ***lines, font_id id, const char *s, u16 max_width);
|
||||
void font_free_lines (char **lines, u32 count);
|
||||
|
||||
void font_plot_string (gfx_queue_entry *entries, int count, font_id id,
|
||||
const char *s, u16 x, u16 y, u16 layer, u16 width,
|
||||
font_xalign xalign, font_yalign yalign);
|
||||
|
||||
#endif
|
||||
|
||||
539
channel/channelapp/source/gfx.c
Normal file
539
channel/channelapp/source/gfx.c
Normal file
@@ -0,0 +1,539 @@
|
||||
#include <malloc.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <gccore.h>
|
||||
#include <ogc/machine/processor.h>
|
||||
#include <ogc/lwp_watchdog.h>
|
||||
#include <ogc/conf.h>
|
||||
|
||||
#include "../config.h"
|
||||
#include "gfx.h"
|
||||
#include "panic.h"
|
||||
#include "title.h"
|
||||
|
||||
#define DEFAULT_FIFO_SIZE (256 * 1024)
|
||||
#define CLIPPING_X (view_width / 2 + 64)
|
||||
#define CLIPPING_Y (view_height / 2 + 64)
|
||||
|
||||
#define FONT_NEAREST_NEIGHBOR
|
||||
|
||||
static u32 *xfb;
|
||||
static GXRModeObj *vmode;
|
||||
|
||||
static float px_width, px_height;
|
||||
static u8 *gp_fifo;
|
||||
|
||||
static Mtx view;
|
||||
|
||||
static u8 origin_stack_size;
|
||||
static const gfx_coordinates *origin_stack[GFX_ORIGIN_STACK_SIZE];
|
||||
static gfx_coordinates origin_current;
|
||||
|
||||
static u32 *efb_buffer = NULL;
|
||||
|
||||
u16 view_width = 0;
|
||||
u16 view_height = 0;
|
||||
bool widescreen = false;
|
||||
|
||||
void gfx_init_video (void) {
|
||||
u8 i;
|
||||
|
||||
VIDEO_Init ();
|
||||
|
||||
vmode = VIDEO_GetPreferredMode (NULL);
|
||||
|
||||
#ifdef ENABLE_WIDESCREEN
|
||||
if (CONF_GetAspectRatio() == CONF_ASPECT_16_9) {
|
||||
gprintf("aspect: 16:9\n");
|
||||
view_width = 852; // 16:9
|
||||
view_height = 480;
|
||||
|
||||
vmode->viWidth = 672;
|
||||
|
||||
widescreen = true;
|
||||
|
||||
if (is_vwii()) {
|
||||
// poke DMCU to turn off pillarboxing
|
||||
write32(0xd8006a0, 0x30000004);
|
||||
mask32(0xd8006a8, 0, 2);
|
||||
}
|
||||
} else {
|
||||
#endif
|
||||
gprintf("aspect: 4:3\n");
|
||||
view_width = 640;
|
||||
view_height = 480;
|
||||
|
||||
vmode->viWidth = 672;
|
||||
|
||||
if (is_vwii()) {
|
||||
// poke DMCU to turn on pillarboxing
|
||||
write32(0xd8006a0, 0x10000002);
|
||||
mask32(0xd8006a8, 0, 2);
|
||||
}
|
||||
#ifdef ENABLE_WIDESCREEN
|
||||
}
|
||||
#endif
|
||||
|
||||
if (vmode == &TVPal576IntDfScale || vmode == &TVPal576ProgScale) {
|
||||
vmode->viXOrigin = (VI_MAX_WIDTH_PAL - vmode->viWidth) / 2;
|
||||
vmode->viYOrigin = (VI_MAX_HEIGHT_PAL - vmode->viHeight) / 2;
|
||||
} else {
|
||||
vmode->viXOrigin = (VI_MAX_WIDTH_NTSC - vmode->viWidth) / 2;
|
||||
vmode->viYOrigin = (VI_MAX_HEIGHT_NTSC - vmode->viHeight) / 2;
|
||||
}
|
||||
|
||||
px_width = vmode->fbWidth / (float) view_width;
|
||||
px_height = vmode->efbHeight / (float) view_height;
|
||||
|
||||
VIDEO_SetBlack (TRUE);
|
||||
VIDEO_Configure (vmode);
|
||||
VIDEO_Flush ();
|
||||
VIDEO_WaitVSync();
|
||||
|
||||
xfb = (u32 *) SYS_AllocateFramebuffer (vmode);
|
||||
DCInvalidateRange(xfb, VIDEO_GetFrameBufferSize(vmode));
|
||||
xfb = MEM_K0_TO_K1 (xfb);
|
||||
|
||||
VIDEO_ClearFrameBuffer (vmode, xfb, COLOR_BLACK);
|
||||
|
||||
VIDEO_SetNextFramebuffer (xfb);
|
||||
VIDEO_SetBlack (TRUE);
|
||||
VIDEO_Flush ();
|
||||
|
||||
gp_fifo = (u8 *) pmemalign (32, DEFAULT_FIFO_SIZE);
|
||||
|
||||
for (i = 0; i < 4; ++i)
|
||||
VIDEO_WaitVSync();
|
||||
}
|
||||
|
||||
void gfx_init () {
|
||||
Mtx44 p;
|
||||
|
||||
GX_AbortFrame ();
|
||||
|
||||
GXColor gxbackground = { 0, 0, 0, 0xff };
|
||||
|
||||
memset (gp_fifo, 0, DEFAULT_FIFO_SIZE);
|
||||
|
||||
GX_Init (gp_fifo, DEFAULT_FIFO_SIZE);
|
||||
GX_SetCopyClear (gxbackground, 0x00ffffff);
|
||||
|
||||
// GX voodoo - properly comment me some day
|
||||
// the 0.05 fudge factors for some reason help align textures on the pixel grid
|
||||
// it's still not perfect though
|
||||
GX_SetViewport (0, 0, vmode->fbWidth+0.05, vmode->efbHeight+0.05, 0, 1);
|
||||
GX_SetDispCopyYScale ((f32) vmode->xfbHeight / (f32) vmode->efbHeight);
|
||||
GX_SetScissor (0, 0, vmode->fbWidth, vmode->efbHeight);
|
||||
GX_SetDispCopySrc (0, 0, vmode->fbWidth, vmode->efbHeight);
|
||||
GX_SetDispCopyDst (vmode->fbWidth, vmode->xfbHeight);
|
||||
GX_SetCopyFilter (vmode->aa, vmode->sample_pattern, GX_TRUE,
|
||||
vmode->vfilter);
|
||||
GX_SetFieldMode (vmode->field_rendering,
|
||||
((vmode->viHeight == 2 * vmode->xfbHeight) ?
|
||||
GX_ENABLE : GX_DISABLE));
|
||||
|
||||
GX_SetPixelFmt (GX_PF_RGB8_Z24, GX_ZC_LINEAR);
|
||||
GX_SetCullMode (GX_CULL_NONE);
|
||||
|
||||
GX_SetDispCopyGamma (GX_GM_1_0);
|
||||
|
||||
guOrtho(p, view_height / 2, -(view_height / 2),
|
||||
-(view_width / 2), view_width / 2, 100, 1000);
|
||||
|
||||
|
||||
GX_LoadProjectionMtx (p, GX_ORTHOGRAPHIC);
|
||||
|
||||
GX_ClearVtxDesc ();
|
||||
GX_SetVtxDesc (GX_VA_POS, GX_DIRECT);
|
||||
GX_SetVtxDesc (GX_VA_CLR0, GX_DIRECT);
|
||||
GX_SetVtxDesc (GX_VA_TEX0, GX_DIRECT);
|
||||
|
||||
GX_SetVtxAttrFmt (GX_VTXFMT0, GX_VA_POS, GX_POS_XYZ, GX_F32, 0);
|
||||
GX_SetVtxAttrFmt (GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0);
|
||||
GX_SetVtxAttrFmt (GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_F32, 0);
|
||||
|
||||
memset (&view, 0, sizeof (Mtx));
|
||||
guMtxIdentity(view);
|
||||
|
||||
GX_Flush ();
|
||||
GX_DrawDone ();
|
||||
|
||||
GX_SetColorUpdate (GX_TRUE);
|
||||
GX_CopyDisp (xfb, GX_TRUE);
|
||||
GX_Flush ();
|
||||
GX_DrawDone ();
|
||||
GX_CopyDisp (xfb, GX_TRUE);
|
||||
GX_Flush ();
|
||||
GX_DrawDone ();
|
||||
|
||||
VIDEO_SetBlack (FALSE);
|
||||
VIDEO_Flush ();
|
||||
}
|
||||
|
||||
void gfx_deinit (void) {
|
||||
u8 i;
|
||||
|
||||
GX_AbortFrame ();
|
||||
VIDEO_SetBlack (TRUE);
|
||||
VIDEO_Flush();
|
||||
|
||||
for (i = 0; i < 4; ++i)
|
||||
VIDEO_WaitVSync();
|
||||
}
|
||||
|
||||
void gfx_get_efb_size(u16 *x, u16*y) {
|
||||
*x = vmode->fbWidth;
|
||||
*y = vmode->efbHeight;
|
||||
}
|
||||
|
||||
void gfx_set_efb_buffer(u32 *buffer) {
|
||||
efb_buffer = buffer;
|
||||
}
|
||||
|
||||
// entity generators
|
||||
|
||||
void gfx_gen_gradient (gfx_entity *entity, u16 w, u16 h,
|
||||
u32 c1, u32 c2, u32 c3, u32 c4) {
|
||||
entity->type = GFXE_GRADIENT;
|
||||
entity->w = w;
|
||||
entity->h = h;
|
||||
|
||||
entity->gradient.c1 = c1;
|
||||
entity->gradient.c2 = c2;
|
||||
entity->gradient.c3 = c3;
|
||||
entity->gradient.c4 = c4;
|
||||
}
|
||||
|
||||
void gfx_gen_tex (gfx_entity *entity, u16 w, u16 h, u8 *pixels, gfx_tex_type type) {
|
||||
entity->type = GFXE_TEXTURE;
|
||||
entity->w = w;
|
||||
entity->h = h;
|
||||
entity->texture.pixels = pixels;
|
||||
entity->texture.type = type;
|
||||
|
||||
switch(type) {
|
||||
case GFXT_RGBA8:
|
||||
DCFlushRange (pixels, w * h * 4);
|
||||
GX_InitTexObj (&entity->texture.obj, pixels, w, h, GX_TF_RGBA8, GX_CLAMP,
|
||||
GX_CLAMP, GX_FALSE);
|
||||
break;
|
||||
case GFXT_A8:
|
||||
DCFlushRange (pixels, w * h);
|
||||
GX_InitTexObj (&entity->texture.obj, pixels, w, h, GX_TF_I8, GX_CLAMP,
|
||||
GX_CLAMP, GX_FALSE);
|
||||
#ifdef FONT_NEAREST_NEIGHBOR
|
||||
GX_InitTexObjFilterMode(&entity->texture.obj, GX_NEAR, GX_NEAR);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// queue entry generators
|
||||
|
||||
void gfx_qe_origin_push (gfx_queue_entry *entry, gfx_coordinates *coords) {
|
||||
entry->type = GFXQ_ORIGIN;
|
||||
entry->origin.coords = coords;
|
||||
}
|
||||
|
||||
void gfx_qe_origin_pop (gfx_queue_entry *entry) {
|
||||
entry->type = GFXQ_ORIGIN;
|
||||
entry->origin.coords = NULL;
|
||||
}
|
||||
|
||||
void gfx_qe_scissor_reset (gfx_queue_entry *entry) {
|
||||
entry->type = GFXQ_SCISSOR;
|
||||
entry->scissor.x = 0;
|
||||
entry->scissor.y = 0;
|
||||
entry->scissor.z = 0;
|
||||
entry->scissor.w = 0;
|
||||
entry->scissor.h = 0;
|
||||
}
|
||||
|
||||
void gfx_qe_scissor (gfx_queue_entry *entry, u16 x, u16 y, u16 z, u16 w, u16 h) {
|
||||
entry->type = GFXQ_SCISSOR;
|
||||
entry->scissor.x = x;
|
||||
entry->scissor.y = y;
|
||||
entry->scissor.z = z;
|
||||
entry->scissor.w = w;
|
||||
entry->scissor.h = h;
|
||||
}
|
||||
|
||||
void gfx_qe_entity (gfx_queue_entry *entry, gfx_entity *entity, f32 x, f32 y,
|
||||
s16 layer, u32 color) {
|
||||
entry->type = GFXQ_ENTITY;
|
||||
entry->entity.coords.x = x;
|
||||
entry->entity.coords.y = y + entity->h;
|
||||
entry->entity.coords.z = layer;
|
||||
entry->entity.scale = 1.0f;
|
||||
entry->entity.rad = 0.0f;
|
||||
entry->entity.entity = entity;
|
||||
entry->entity.color = color;
|
||||
}
|
||||
|
||||
// helper functions
|
||||
|
||||
static void get_origin (s16 *x, s16 *y, s16 *z, u8 m) {
|
||||
u8 i;
|
||||
|
||||
*x = -view_width / 2;
|
||||
*y = view_height / 2;
|
||||
*z = VIEW_Z_ORIGIN;
|
||||
|
||||
for (i = 0; i < origin_stack_size - m; ++i) {
|
||||
*x += origin_stack[i]->x;
|
||||
*y -= origin_stack[i]->y;
|
||||
*z += origin_stack[i]->z;
|
||||
}
|
||||
}
|
||||
|
||||
static void set_scissor (const gfx_queue_entry *entry) {
|
||||
s16 x, y, z, w, h;
|
||||
|
||||
if (entry->scissor.w != 0 && entry->scissor.h != 0) {
|
||||
// TODO 1ast parm hardcoded for memo
|
||||
get_origin (&x, &y, &z, 1);
|
||||
x = roundf ((float) (x + view_width / 2 + entry->scissor.x) * px_width);
|
||||
y = roundf ((float) (-y + view_height / 2 + entry->scissor.y) *
|
||||
px_height);
|
||||
w = roundf ((float) entry->scissor.w * px_width);
|
||||
h = roundf ((float) entry->scissor.h * px_height);
|
||||
|
||||
GX_SetScissor (x, y, w, h);
|
||||
} else {
|
||||
GX_SetScissor (0, 0, vmode->fbWidth, vmode->efbHeight);
|
||||
}
|
||||
}
|
||||
|
||||
static void plot_vert_c (f32 x, f32 y, f32 z, u32 c) {
|
||||
GX_Position3f32 (x, y, z);
|
||||
GX_Color1u32 (c);
|
||||
GX_TexCoord2f32 (0.0f, 0.0f);
|
||||
}
|
||||
|
||||
static void plot_gradient (const gfx_queue_entry *entry, Mtx v) {
|
||||
Mtx m, m2;
|
||||
Mtx mv;
|
||||
f32 xf, yf, zf, wf, hf;
|
||||
|
||||
xf = origin_current.x + entry->entity.coords.x;
|
||||
yf = origin_current.y - entry->entity.coords.y;
|
||||
zf = origin_current.z + entry->entity.coords.z;
|
||||
|
||||
wf = entry->entity.entity->w;
|
||||
hf = entry->entity.entity->h;
|
||||
|
||||
guMtxIdentity(m);
|
||||
guMtxTransApply(m, m, -wf/2, -hf/2, 0);
|
||||
|
||||
if (entry->entity.scale != 1.0f) {
|
||||
guMtxScale(m2, entry->entity.scale, entry->entity.scale, 0.0);
|
||||
guMtxConcat(m2, m, m);
|
||||
}
|
||||
|
||||
if (entry->entity.rad != 0.0f) {
|
||||
guMtxRotRad(m2, 'z', entry->entity.rad);
|
||||
guMtxConcat(m2, m, m);
|
||||
}
|
||||
|
||||
guMtxTransApply(m, m, wf/2+xf, hf/2+yf, zf);
|
||||
guMtxConcat(v, m, mv);
|
||||
|
||||
GX_LoadPosMtxImm (mv, GX_PNMTX0);
|
||||
|
||||
GX_Begin (GX_QUADS, GX_VTXFMT0, 4);
|
||||
plot_vert_c (0, hf, 0, entry->entity.entity->gradient.c1);
|
||||
plot_vert_c (wf, hf, 0, entry->entity.entity->gradient.c2);
|
||||
plot_vert_c (wf, 0, 0, entry->entity.entity->gradient.c3);
|
||||
plot_vert_c (0, 0, 0, entry->entity.entity->gradient.c4);
|
||||
GX_End ();
|
||||
}
|
||||
|
||||
static void plot_vert (f32 x, f32 y, f32 z, f32 s, f32 t, u32 c) {
|
||||
GX_Position3f32 (x, y, z);
|
||||
GX_Color1u32 (c);
|
||||
GX_TexCoord2f32 (s, t);
|
||||
}
|
||||
|
||||
static void plot_texture (const gfx_queue_entry *entry, Mtx v) {
|
||||
Mtx m, m2;
|
||||
Mtx mv;
|
||||
f32 xf, yf, zf, wf, hf;
|
||||
|
||||
yf = origin_current.y - entry->entity.coords.y;
|
||||
zf = origin_current.z + entry->entity.coords.z;
|
||||
hf = entry->entity.entity->h;
|
||||
|
||||
if (widescreen && entry->entity.entity->texture.type == GFXT_A8) {
|
||||
// align origin to pixel grid
|
||||
xf = (int)(origin_current.x / WIDESCREEN_RATIO);
|
||||
xf = xf*WIDESCREEN_RATIO + entry->entity.coords.x;
|
||||
// width is specified in physical pixels
|
||||
wf = entry->entity.entity->w * WIDESCREEN_RATIO;
|
||||
} else {
|
||||
xf = origin_current.x + entry->entity.coords.x;
|
||||
|
||||
wf = entry->entity.entity->w;
|
||||
}
|
||||
|
||||
if (yf > CLIPPING_Y)
|
||||
return;
|
||||
|
||||
if ((yf + hf) < -CLIPPING_Y)
|
||||
return;
|
||||
|
||||
if (xf > CLIPPING_X)
|
||||
return;
|
||||
|
||||
if ((xf + wf) < -CLIPPING_X)
|
||||
return;
|
||||
|
||||
GX_LoadTexObj (&entry->entity.entity->texture.obj, GX_TEXMAP0);
|
||||
|
||||
guMtxIdentity(m);
|
||||
guMtxTransApply(m, m, -wf/2, -hf/2, 0);
|
||||
|
||||
if (entry->entity.scale != 1.0f) {
|
||||
guMtxScale(m2, entry->entity.scale, entry->entity.scale, 0.0);
|
||||
guMtxConcat(m2, m, m);
|
||||
}
|
||||
|
||||
if (entry->entity.rad != 0.0f) {
|
||||
guMtxRotRad(m2, 'z', entry->entity.rad);
|
||||
guMtxConcat(m2, m, m);
|
||||
}
|
||||
|
||||
guMtxTransApply(m, m, wf/2+xf, hf/2+yf, zf);
|
||||
guMtxConcat(v, m, mv);
|
||||
|
||||
switch (entry->entity.entity->texture.type) {
|
||||
case GFXT_RGBA8:
|
||||
GX_SetTevOp (GX_TEVSTAGE0, GX_MODULATE);
|
||||
break;
|
||||
case GFXT_A8:
|
||||
GX_SetNumTevStages(1);
|
||||
GX_SetTevColorIn(GX_TEVSTAGE0,GX_CC_ZERO,GX_CC_ZERO,GX_CC_ZERO,GX_CC_RASC);
|
||||
GX_SetTevAlphaIn(GX_TEVSTAGE0,GX_CA_ZERO,GX_CA_TEXA,GX_CA_RASA,GX_CA_ZERO);
|
||||
GX_SetTevColorOp(GX_TEVSTAGE0,GX_TEV_ADD,GX_TB_ZERO,GX_CS_SCALE_1,GX_TRUE,GX_TEVPREV);
|
||||
GX_SetTevAlphaOp(GX_TEVSTAGE0,GX_TEV_ADD,GX_TB_ZERO,GX_CS_SCALE_1,GX_TRUE,GX_TEVPREV);
|
||||
break;
|
||||
}
|
||||
|
||||
GX_LoadPosMtxImm (mv, GX_PNMTX0);
|
||||
|
||||
GX_Begin (GX_QUADS, GX_VTXFMT0, 4);
|
||||
plot_vert (0, hf, 0, 0.0, 0.0, entry->entity.color);
|
||||
plot_vert (wf, hf, 0, 1.0, 0.0, entry->entity.color);
|
||||
plot_vert (wf, 0, 0, 1.0, 1.0, entry->entity.color);
|
||||
plot_vert (0, 0, 0, 0.0, 1.0, entry->entity.color);
|
||||
GX_End ();
|
||||
}
|
||||
|
||||
// higher level frame functions
|
||||
|
||||
void gfx_frame_start (void) {
|
||||
origin_stack_size = 0;
|
||||
origin_current.x = -view_width / 2;
|
||||
origin_current.y = view_height / 2;
|
||||
origin_current.z = VIEW_Z_ORIGIN;
|
||||
|
||||
GX_InvVtxCache ();
|
||||
GX_InvalidateTexAll ();
|
||||
|
||||
GX_SetNumChans (1);
|
||||
GX_SetNumTexGens (1);
|
||||
GX_SetTexCoordGen (GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY);
|
||||
|
||||
GX_SetTevOp (GX_TEVSTAGE0, GX_MODULATE);
|
||||
GX_SetTevOrder (GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0);
|
||||
|
||||
GX_SetBlendMode (GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA,
|
||||
GX_LO_NOOP);
|
||||
}
|
||||
|
||||
void gfx_frame_push (const gfx_queue_entry *entries, int count) {
|
||||
while (count--) {
|
||||
switch (entries->type) {
|
||||
case GFXQ_ORIGIN:
|
||||
if (entries->origin.coords) {
|
||||
origin_current.x += entries->origin.coords->x;
|
||||
origin_current.y -= entries->origin.coords->y;
|
||||
origin_current.z += entries->origin.coords->z;
|
||||
origin_stack[origin_stack_size] = entries->origin.coords;
|
||||
origin_stack_size++;
|
||||
} else {
|
||||
if (origin_stack_size > 0) {
|
||||
origin_current.x -= origin_stack[origin_stack_size - 1]->x;
|
||||
origin_current.y += origin_stack[origin_stack_size - 1]->y;
|
||||
origin_current.z -= origin_stack[origin_stack_size - 1]->z;
|
||||
origin_stack_size--;
|
||||
} else {
|
||||
origin_current.x += -view_width / 2;
|
||||
origin_current.y -= view_height / 2;
|
||||
origin_current.z += view_height / 2;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case GFXQ_SCISSOR:
|
||||
set_scissor (entries);
|
||||
break;
|
||||
|
||||
case GFXQ_ENTITY:
|
||||
switch (entries->entity.entity->type) {
|
||||
case GFXE_GRADIENT:
|
||||
GX_SetTevOp (GX_TEVSTAGE0, GX_PASSCLR);
|
||||
plot_gradient (entries, view);
|
||||
|
||||
break;
|
||||
|
||||
case GFXE_TEXTURE:
|
||||
plot_texture (entries, view);
|
||||
break;
|
||||
}
|
||||
case GFXQ_NULL:
|
||||
break;
|
||||
default:
|
||||
gprintf("Unknown queue entry type 0x%x\n", entries->type);
|
||||
break;
|
||||
}
|
||||
entries++;
|
||||
}
|
||||
}
|
||||
|
||||
void gfx_frame_end (void) {
|
||||
GX_DrawDone ();
|
||||
|
||||
#ifdef ENABLE_SCREENSHOTS
|
||||
if (efb_buffer) {
|
||||
u16 x, y;
|
||||
GXColor c;
|
||||
u32 val;
|
||||
u32 *p = efb_buffer;
|
||||
|
||||
for (y = 0; y < vmode->efbHeight; ++y) {
|
||||
for (x = 0; x < vmode->fbWidth; ++x) {
|
||||
GX_PeekARGB(x, y, &c);
|
||||
val = ((u32) c.a) << 24;
|
||||
val |= ((u32) c.r) << 16;
|
||||
val |= ((u32) c.g) << 8;
|
||||
val |= c.b;
|
||||
|
||||
*p++ = val;
|
||||
}
|
||||
}
|
||||
|
||||
efb_buffer = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
GX_SetZMode (GX_TRUE, GX_LEQUAL, GX_TRUE);
|
||||
GX_SetColorUpdate (GX_TRUE);
|
||||
|
||||
VIDEO_WaitVSync ();
|
||||
GX_CopyDisp (xfb, GX_TRUE);
|
||||
GX_Flush ();
|
||||
}
|
||||
|
||||
96
channel/channelapp/source/gfx.h
Normal file
96
channel/channelapp/source/gfx.h
Normal file
@@ -0,0 +1,96 @@
|
||||
#ifndef _GFX_H_
|
||||
#define _GFX_H_
|
||||
|
||||
#include <ogc/gx.h>
|
||||
|
||||
#define COL_DEFAULT 0xFFFFFFFF
|
||||
|
||||
#define WIDESCREEN_RATIO (852.0/640.0)
|
||||
|
||||
typedef struct {
|
||||
f32 x, y, z;
|
||||
} gfx_coordinates;
|
||||
|
||||
typedef enum {
|
||||
GFXT_RGBA8,
|
||||
GFXT_A8
|
||||
} gfx_tex_type;
|
||||
|
||||
typedef enum {
|
||||
GFXE_GRADIENT,
|
||||
GFXE_TEXTURE
|
||||
} gfx_entity_type;
|
||||
|
||||
typedef struct {
|
||||
gfx_entity_type type;
|
||||
u16 w, h;
|
||||
|
||||
union {
|
||||
struct {
|
||||
u32 c1, c2, c3, c4;
|
||||
} gradient;
|
||||
|
||||
struct {
|
||||
u8 *pixels;
|
||||
gfx_tex_type type;
|
||||
GXTexObj obj;
|
||||
} texture;
|
||||
};
|
||||
} gfx_entity;
|
||||
|
||||
typedef enum {
|
||||
GFXQ_NULL = -1,
|
||||
GFXQ_ORIGIN,
|
||||
GFXQ_SCISSOR,
|
||||
GFXQ_ENTITY
|
||||
} gfx_queue_type;
|
||||
|
||||
typedef struct {
|
||||
gfx_queue_type type;
|
||||
|
||||
union {
|
||||
struct {
|
||||
gfx_coordinates *coords;
|
||||
} origin;
|
||||
|
||||
struct {
|
||||
u16 x, y, z, w, h;
|
||||
} scissor;
|
||||
|
||||
struct {
|
||||
gfx_coordinates coords;
|
||||
f32 scale;
|
||||
f32 rad;
|
||||
gfx_entity *entity;
|
||||
u32 color;
|
||||
} entity;
|
||||
};
|
||||
} gfx_queue_entry;
|
||||
|
||||
extern bool widescreen;
|
||||
extern u16 view_height, view_width;
|
||||
|
||||
void gfx_init_video (void);
|
||||
void gfx_init (void);
|
||||
void gfx_deinit (void);
|
||||
|
||||
void gfx_get_efb_size(u16 *x, u16 *y);
|
||||
void gfx_set_efb_buffer(u32 *buffer);
|
||||
|
||||
void gfx_gen_gradient (gfx_entity *entity, u16 w, u16 h,
|
||||
u32 c1, u32 c2, u32 c3, u32 c4);
|
||||
void gfx_gen_tex (gfx_entity *entity, u16 w, u16 h, u8 *pixels, gfx_tex_type type);
|
||||
|
||||
void gfx_qe_origin_push (gfx_queue_entry *entry, gfx_coordinates *coords);
|
||||
void gfx_qe_origin_pop (gfx_queue_entry *entry);
|
||||
void gfx_qe_scissor_reset (gfx_queue_entry *entry);
|
||||
void gfx_qe_scissor (gfx_queue_entry *entry, u16 x, u16 y, u16 z, u16 w, u16 h);
|
||||
void gfx_qe_entity (gfx_queue_entry *entry, gfx_entity *entity, f32 x, f32 y,
|
||||
s16 layer, u32 color);
|
||||
|
||||
void gfx_frame_start (void);
|
||||
void gfx_frame_push (const gfx_queue_entry *entries, int count);
|
||||
void gfx_frame_end (void);
|
||||
|
||||
#endif
|
||||
|
||||
67
channel/channelapp/source/ggets.c
Normal file
67
channel/channelapp/source/ggets.c
Normal file
@@ -0,0 +1,67 @@
|
||||
/* File ggets.c - goodgets is a safe alternative to gets */
|
||||
/* By C.B. Falconer. Public domain 2002-06-22 */
|
||||
/* attribution appreciated. */
|
||||
|
||||
/* Revised 2002-06-26 New prototype.
|
||||
2002-06-27 Incomplete final lines
|
||||
2006-06-14 Simplified, using getc, not fgets
|
||||
2006-06-15 Fixed memory leak at EOF
|
||||
*/
|
||||
|
||||
/* fggets and ggets [which is fggets(ln, stdin)] set *ln to
|
||||
a buffer filled with the next complete line from the text
|
||||
stream f. The storage has been allocated within fggets,
|
||||
and is normally reduced to be an exact fit. The trailing
|
||||
\n has been removed, so the resultant line is ready for
|
||||
dumping with puts. The buffer will be as large as is
|
||||
required to hold the complete line.
|
||||
|
||||
Note: this means a final file line without a \n terminator
|
||||
has an effective \n appended, as EOF occurs within the read.
|
||||
|
||||
If no error occurs fggets returns 0. If an EOF occurs on
|
||||
the input file, EOF is returned. For memory allocation
|
||||
errors some positive value is returned. In this case *ln
|
||||
may point to a partial line. For other errors memory is
|
||||
freed and *ln is set to NULL.
|
||||
|
||||
Freeing of assigned storage is the callers responsibility
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "panic.h"
|
||||
#include "ggets.h"
|
||||
|
||||
#define INITSIZE 112 /* power of 2 minus 16, helps malloc */
|
||||
#define DELTASIZE (INITSIZE + 16)
|
||||
|
||||
enum {OK = 0, NOMEM};
|
||||
|
||||
int fggets(char* *ln, FILE *f)
|
||||
{
|
||||
int cursize, ch, ix;
|
||||
char *buffer;
|
||||
|
||||
*ln = NULL; /* default */
|
||||
buffer = pmalloc(INITSIZE);
|
||||
cursize = INITSIZE;
|
||||
|
||||
ix = 0;
|
||||
while ((EOF != (ch = getc(f))) && ('\n' != ch)) {
|
||||
if (ix >= (cursize - 1)) { /* extend buffer */
|
||||
cursize += DELTASIZE;
|
||||
buffer = prealloc(buffer, (size_t)cursize);
|
||||
}
|
||||
buffer[ix++] = ch;
|
||||
}
|
||||
if ((EOF == ch) && (0 == ix)) {
|
||||
free(buffer);
|
||||
return EOF;
|
||||
}
|
||||
|
||||
buffer[ix] = '\0';
|
||||
*ln = prealloc(buffer, (size_t)ix + 1);
|
||||
return OK;
|
||||
} /* fggets */
|
||||
/* End of ggets.c */
|
||||
45
channel/channelapp/source/ggets.h
Normal file
45
channel/channelapp/source/ggets.h
Normal file
@@ -0,0 +1,45 @@
|
||||
/* File ggets.h - goodgets is a safe alternative to gets */
|
||||
/* By C.B. Falconer. Public domain 2002-06-22 */
|
||||
/* attribution appreciated. */
|
||||
|
||||
|
||||
/* Revised 2002-06-26 New prototype.
|
||||
2002-06-27 Incomplete final lines
|
||||
*/
|
||||
|
||||
/* fggets and ggets [which is fggets(ln, stdin)] set *ln to
|
||||
a buffer filled with the next complete line from the text
|
||||
stream f. The storage has been allocated within fggets,
|
||||
and is normally reduced to be an exact fit. The trailing
|
||||
\n has been removed, so the resultant line is ready for
|
||||
dumping with puts. The buffer will be as large as is
|
||||
required to hold the complete line.
|
||||
|
||||
Note: this means a final file line without a \n terminator
|
||||
has an effective \n appended, as EOF occurs within the read.
|
||||
|
||||
If no error occurs fggets returns 0. If an EOF occurs on
|
||||
the input file, EOF is returned. For memory allocation
|
||||
errors some positive value is returned. In this case *ln
|
||||
may point to a partial line. For other errors memory is
|
||||
freed and *ln is set to NULL.
|
||||
|
||||
Freeing of assigned storage is the callers responsibility
|
||||
*/
|
||||
|
||||
#ifndef ggets_h_
|
||||
# define ggets_h_
|
||||
|
||||
# ifdef __cplusplus
|
||||
extern "C" {
|
||||
# endif
|
||||
|
||||
int fggets(char* *ln, FILE *f);
|
||||
|
||||
#define ggets(ln) fggets(ln, stdin)
|
||||
|
||||
# ifdef __cplusplus
|
||||
}
|
||||
# endif
|
||||
#endif
|
||||
/* END ggets.h */
|
||||
455
channel/channelapp/source/http.c
Normal file
455
channel/channelapp/source/http.c
Normal file
@@ -0,0 +1,455 @@
|
||||
#include <malloc.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <ogcsys.h>
|
||||
#include <network.h>
|
||||
#include <ogc/machine/processor.h>
|
||||
#include <ogc/lwp_watchdog.h>
|
||||
#include <ogc/cond.h>
|
||||
|
||||
#include "../config.h"
|
||||
|
||||
#ifdef ENABLE_UPDATES
|
||||
|
||||
#include "tcp.h"
|
||||
#include "panic.h"
|
||||
|
||||
#include "http.h"
|
||||
|
||||
extern u32 ms_id;
|
||||
extern u32 ng_id;
|
||||
|
||||
typedef enum {
|
||||
HTTPCMD_IDLE = 0,
|
||||
HTTPCMD_EXIT,
|
||||
HTTPCMD_FETCH
|
||||
} http_cmd;
|
||||
|
||||
typedef struct {
|
||||
bool running;
|
||||
bool done;
|
||||
|
||||
http_cmd cmd;
|
||||
char *host;
|
||||
u16 port;
|
||||
char *path;
|
||||
u32 max_size;
|
||||
|
||||
u16 sysmenu_version;
|
||||
char *region;
|
||||
char *area;
|
||||
|
||||
mutex_t mutex;
|
||||
mutex_t cmutex;
|
||||
cond_t cond;
|
||||
http_state state;
|
||||
http_res res;
|
||||
u32 http_status;
|
||||
u32 content_length;
|
||||
u32 progress;
|
||||
u8 *data;
|
||||
} http_arg;
|
||||
|
||||
static lwp_t http_thread;
|
||||
static u8 http_stack[HTTP_THREAD_STACKSIZE] ATTRIBUTE_ALIGN (32);
|
||||
|
||||
static http_arg ta_http;
|
||||
|
||||
static u16 get_tmd_version(u64 title) {
|
||||
STACK_ALIGN(u8, tmdbuf, 1024, 32);
|
||||
u32 tmd_view_size = 0;
|
||||
s32 res;
|
||||
|
||||
res = ES_GetTMDViewSize(title, &tmd_view_size);
|
||||
if (res < 0)
|
||||
return 0;
|
||||
|
||||
if (tmd_view_size > 1024)
|
||||
return 0;
|
||||
|
||||
res = ES_GetTMDView(title, tmdbuf, tmd_view_size);
|
||||
if (res < 0)
|
||||
return 0;
|
||||
|
||||
return (tmdbuf[88] << 8) | tmdbuf[89];
|
||||
}
|
||||
|
||||
static bool http_split_url (char **host, char **path, const char *url) {
|
||||
const char *p;
|
||||
char *c;
|
||||
|
||||
if (strncasecmp (url, "http://", 7))
|
||||
return false;
|
||||
|
||||
p = url + 7;
|
||||
c = strchr (p, '/');
|
||||
|
||||
if (c[0] == 0)
|
||||
return false;
|
||||
|
||||
*host = strndup (p, c - p);
|
||||
*path = pstrdup (c);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void * http_func (void *arg) {
|
||||
http_arg *ta = (http_arg *) arg;
|
||||
int s;
|
||||
char *request, *r;
|
||||
int linecount;
|
||||
char *line;
|
||||
bool b;
|
||||
s64 t;
|
||||
|
||||
ta->running = true;
|
||||
LWP_MutexLock (ta->cmutex);
|
||||
LWP_MutexLock (ta_http.mutex);
|
||||
ta->state = HTTPS_IDLE;
|
||||
ta->done = true;
|
||||
LWP_MutexUnlock (ta_http.mutex);
|
||||
|
||||
while (true) {
|
||||
while (ta->cmd == HTTPCMD_IDLE)
|
||||
LWP_CondWait(ta->cond, ta->cmutex);
|
||||
|
||||
if (ta->cmd == HTTPCMD_EXIT)
|
||||
break;
|
||||
|
||||
ta->cmd = HTTPCMD_IDLE;
|
||||
|
||||
s = tcp_connect (ta->host, ta->port);
|
||||
|
||||
LWP_MutexLock (ta_http.mutex);
|
||||
if (s < 0) {
|
||||
ta->state = HTTPS_IDLE;
|
||||
ta->done = true;
|
||||
ta->res = HTTPR_ERR_CONNECT;
|
||||
LWP_MutexUnlock (ta_http.mutex);
|
||||
continue;
|
||||
}
|
||||
|
||||
ta->state = HTTPS_REQUESTING;
|
||||
LWP_MutexUnlock (ta_http.mutex);
|
||||
|
||||
request = (char *) pmalloc (2048);
|
||||
r = request;
|
||||
r += sprintf (r, "GET %s HTTP/1.1\r\n", ta->path);
|
||||
r += sprintf (r, "Host: %s\r\n", ta->host);
|
||||
r += sprintf (r, "Cache-Control: no-cache\r\n");
|
||||
r += sprintf (r, "User-Agent: TheHomebrewChannel/%s Wii/%08lx"
|
||||
" (%lu; %u; %s-%s)\r\n", CHANNEL_VERSION_STR,
|
||||
ng_id, ms_id, ta->sysmenu_version, ta->region,
|
||||
ta->area);
|
||||
|
||||
strcat (r, "\r\n");
|
||||
|
||||
b = tcp_write (s, (u8 *) request, strlen (request), NULL, NULL);
|
||||
|
||||
free (request);
|
||||
|
||||
if (!b) {
|
||||
LWP_MutexLock (ta_http.mutex);
|
||||
ta->state = HTTPS_IDLE;
|
||||
ta->done = true;
|
||||
ta->res = HTTPR_ERR_REQUEST;
|
||||
LWP_MutexUnlock (ta_http.mutex);
|
||||
net_close (s);
|
||||
continue;
|
||||
}
|
||||
|
||||
linecount = 0;
|
||||
t = gettime ();
|
||||
while (true) {
|
||||
line = tcp_readln (s, 0xff, t, HTTP_TIMEOUT);
|
||||
if (!line) {
|
||||
LWP_MutexLock (ta_http.mutex);
|
||||
ta->http_status = 404;
|
||||
ta->res = HTTPR_ERR_REQUEST;
|
||||
break;
|
||||
}
|
||||
|
||||
if (strlen (line) < 1) {
|
||||
free (line);
|
||||
LWP_MutexLock (ta_http.mutex);
|
||||
break;
|
||||
}
|
||||
|
||||
sscanf (line, "HTTP/1.%*u %lu", &ta->http_status);
|
||||
sscanf (line, "Content-Length: %lu", &ta->content_length);
|
||||
|
||||
free (line);
|
||||
|
||||
linecount++;
|
||||
if (linecount == 32) {
|
||||
LWP_MutexLock (ta_http.mutex);
|
||||
ta->http_status = 404;
|
||||
ta->res = HTTPR_ERR_REQUEST;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ta->content_length < 1)
|
||||
ta->http_status = 404;
|
||||
|
||||
if (ta->http_status != 200) {
|
||||
ta->res = HTTPR_ERR_STATUS;
|
||||
ta->state = HTTPS_IDLE;
|
||||
ta->done = true;
|
||||
LWP_MutexUnlock (ta_http.mutex);
|
||||
net_close (s);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ta->content_length > ta->max_size) {
|
||||
ta->res = HTTPR_ERR_TOOBIG;
|
||||
ta->state = HTTPS_IDLE;
|
||||
ta->done = true;
|
||||
LWP_MutexUnlock (ta_http.mutex);
|
||||
net_close (s);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
ta->state = HTTPS_RECEIVING;
|
||||
|
||||
// safety, for char[] parsing
|
||||
ta->data = (u8 *) pmalloc (ta->content_length + 1);
|
||||
LWP_MutexUnlock (ta_http.mutex);
|
||||
|
||||
b = tcp_read (s, ta->data, ta->content_length, &ta->mutex,
|
||||
&ta->progress);
|
||||
|
||||
if (!b) {
|
||||
LWP_MutexLock (ta_http.mutex);
|
||||
free (ta->data);
|
||||
ta->data = NULL;
|
||||
ta->res = HTTPR_ERR_RECEIVE;
|
||||
ta->state = HTTPS_IDLE;
|
||||
ta->done = true;
|
||||
LWP_MutexUnlock (ta_http.mutex);
|
||||
net_close (s);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
gprintf("done with download\n");
|
||||
|
||||
LWP_MutexLock (ta_http.mutex);
|
||||
ta->data[ta->content_length] = 0;
|
||||
ta->res = HTTPR_OK;
|
||||
ta->state = HTTPS_IDLE;
|
||||
ta->done = true;
|
||||
LWP_MutexUnlock (ta_http.mutex);
|
||||
|
||||
net_close (s);
|
||||
}
|
||||
|
||||
LWP_MutexUnlock (ta->cmutex);
|
||||
|
||||
gprintf("http thread done\n");
|
||||
|
||||
ta->running = false;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void http_init (void) {
|
||||
s32 res;
|
||||
|
||||
gprintf ("starting http thread\n");
|
||||
|
||||
memset (&ta_http, 0, sizeof (http_arg));
|
||||
|
||||
ta_http.sysmenu_version = get_tmd_version(0x0000000100000002ll);
|
||||
|
||||
switch (CONF_GetRegion()) {
|
||||
case CONF_REGION_JP:
|
||||
ta_http.region = pstrdup("JP");
|
||||
break;
|
||||
case CONF_REGION_US:
|
||||
ta_http.region = pstrdup("US");
|
||||
break;
|
||||
case CONF_REGION_EU:
|
||||
ta_http.region = pstrdup("EU");
|
||||
break;
|
||||
case CONF_REGION_KR:
|
||||
ta_http.region = pstrdup("KR");
|
||||
break;
|
||||
case CONF_REGION_CN:
|
||||
ta_http.region = pstrdup("CN");
|
||||
break;
|
||||
default:
|
||||
ta_http.region = pstrdup("UNK");
|
||||
break;
|
||||
}
|
||||
|
||||
switch (CONF_GetArea()) {
|
||||
case CONF_AREA_JPN:
|
||||
ta_http.area = pstrdup("JPN");
|
||||
break;
|
||||
case CONF_AREA_USA:
|
||||
ta_http.area = pstrdup("USA");
|
||||
break;
|
||||
case CONF_AREA_EUR:
|
||||
ta_http.area = pstrdup("EUR");
|
||||
break;
|
||||
case CONF_AREA_AUS:
|
||||
ta_http.area = pstrdup("AUS");
|
||||
break;
|
||||
case CONF_AREA_BRA:
|
||||
ta_http.area = pstrdup("BRA");
|
||||
break;
|
||||
case CONF_AREA_TWN:
|
||||
ta_http.area = pstrdup("TWN");
|
||||
break;
|
||||
case CONF_AREA_ROC:
|
||||
ta_http.area = pstrdup("ROC");
|
||||
break;
|
||||
case CONF_AREA_KOR:
|
||||
ta_http.area = pstrdup("KOR");
|
||||
break;
|
||||
case CONF_AREA_HKG:
|
||||
ta_http.area = pstrdup("HKG");
|
||||
break;
|
||||
case CONF_AREA_ASI:
|
||||
ta_http.area = pstrdup("ASI");
|
||||
break;
|
||||
case CONF_AREA_LTN:
|
||||
ta_http.area = pstrdup("LTN");
|
||||
break;
|
||||
case CONF_AREA_SAF:
|
||||
ta_http.area = pstrdup("SAF");
|
||||
break;
|
||||
case CONF_AREA_CHN:
|
||||
ta_http.area = pstrdup("CHN");
|
||||
break;
|
||||
default:
|
||||
ta_http.area = pstrdup("UNK");
|
||||
break;
|
||||
}
|
||||
|
||||
res = LWP_MutexInit (&ta_http.mutex, false);
|
||||
|
||||
if (res) {
|
||||
gprintf ("error creating mutex: %ld\n", res);
|
||||
return;
|
||||
}
|
||||
|
||||
res = LWP_MutexInit (&ta_http.cmutex, false);
|
||||
|
||||
if (res) {
|
||||
gprintf ("error creating cmutex: %ld\n", res);
|
||||
return;
|
||||
}
|
||||
|
||||
res = LWP_CondInit (&ta_http.cond);
|
||||
|
||||
if (res) {
|
||||
gprintf ("error creating cond: %ld\n", res);
|
||||
return;
|
||||
}
|
||||
|
||||
memset (&http_stack, 0, HTTP_THREAD_STACKSIZE);
|
||||
res = LWP_CreateThread (&http_thread, http_func, &ta_http, http_stack,
|
||||
HTTP_THREAD_STACKSIZE, HTTP_THREAD_PRIO);
|
||||
|
||||
gprintf("created http thread\n");
|
||||
|
||||
if (res) {
|
||||
gprintf ("error creating thread: %ld\n", res);
|
||||
}
|
||||
}
|
||||
|
||||
void http_deinit (void) {
|
||||
int i;
|
||||
|
||||
if (ta_http.running) {
|
||||
gprintf ("stopping http thread\n");
|
||||
|
||||
for (i = 0; i < 25; ++i) {
|
||||
if (LWP_MutexTryLock (ta_http.cmutex) == 0) {
|
||||
break;
|
||||
}
|
||||
usleep (20 * 1000);
|
||||
}
|
||||
if (i < 25) {
|
||||
gprintf ("sending http entry thread the exit cmd\n");
|
||||
ta_http.cmd = HTTPCMD_EXIT;
|
||||
LWP_SetThreadPriority (http_thread, LWP_PRIO_HIGHEST);
|
||||
LWP_CondBroadcast (ta_http.cond);
|
||||
LWP_MutexUnlock (ta_http.cmutex);
|
||||
LWP_JoinThread(http_thread, NULL);
|
||||
LWP_CondDestroy (ta_http.cond);
|
||||
LWP_MutexDestroy (ta_http.cmutex);
|
||||
LWP_MutexDestroy (ta_http.mutex);
|
||||
free(ta_http.region);
|
||||
free(ta_http.area);
|
||||
return;
|
||||
}
|
||||
gprintf("http thread didn't shutdown gracefully!\n");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
bool http_ready (void) {
|
||||
return ta_http.running && ta_http.done;
|
||||
}
|
||||
|
||||
bool http_request (const char *url, u32 max_size) {
|
||||
if (!http_ready () || !http_split_url (&ta_http.host, &ta_http.path, url))
|
||||
return false;
|
||||
|
||||
LWP_MutexLock (ta_http.cmutex);
|
||||
ta_http.done = false;
|
||||
ta_http.cmd = HTTPCMD_FETCH;
|
||||
ta_http.port = 80;
|
||||
ta_http.max_size = max_size;
|
||||
|
||||
LWP_CondBroadcast (ta_http.cond);
|
||||
LWP_MutexUnlock (ta_http.cmutex);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool http_get_state (http_state *state, u32 *content_length, u32 *progress) {
|
||||
if (!ta_http.running)
|
||||
return false;
|
||||
|
||||
LWP_MutexLock (ta_http.mutex);
|
||||
*state = ta_http.state;
|
||||
*content_length = ta_http.content_length;
|
||||
*progress = ta_http.progress;
|
||||
LWP_MutexUnlock (ta_http.mutex);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool http_get_result (http_res *res, u32 *http_status, u8 **content,
|
||||
u32 *length) {
|
||||
if (!http_ready())
|
||||
return false;
|
||||
|
||||
*res = ta_http.res;
|
||||
*http_status = ta_http.http_status;
|
||||
if (ta_http.res == HTTPR_OK) {
|
||||
*content = ta_http.data;
|
||||
*length = ta_http.content_length;
|
||||
} else {
|
||||
*content = NULL;
|
||||
*length = 0;
|
||||
}
|
||||
|
||||
free (ta_http.host);
|
||||
ta_http.host = NULL;
|
||||
free (ta_http.path);
|
||||
ta_http.path = NULL;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
33
channel/channelapp/source/http.h
Normal file
33
channel/channelapp/source/http.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#ifndef _HTTP_H_
|
||||
#define _HTTP_H_
|
||||
|
||||
#include <gctypes.h>
|
||||
|
||||
typedef enum {
|
||||
HTTPS_IDLE = 0,
|
||||
HTTPS_CONNECTING,
|
||||
HTTPS_REQUESTING,
|
||||
HTTPS_RECEIVING
|
||||
} http_state;
|
||||
|
||||
typedef enum {
|
||||
HTTPR_OK,
|
||||
HTTPR_ERR_CONNECT,
|
||||
HTTPR_ERR_REQUEST,
|
||||
HTTPR_ERR_STATUS,
|
||||
HTTPR_ERR_TOOBIG,
|
||||
HTTPR_ERR_RECEIVE
|
||||
} http_res;
|
||||
|
||||
void http_init (void);
|
||||
void http_deinit (void);
|
||||
|
||||
bool http_ready (void);
|
||||
|
||||
bool http_request (const char *url, u32 max_size);
|
||||
bool http_get_state (http_state *state, u32 *content_length, u32 *progress);
|
||||
bool http_get_result (http_res *res, u32 *http_status, u8 **content,
|
||||
u32 *length);
|
||||
|
||||
#endif
|
||||
|
||||
86
channel/channelapp/source/i18n.c
Normal file
86
channel/channelapp/source/i18n.c
Normal file
@@ -0,0 +1,86 @@
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <gctypes.h>
|
||||
|
||||
#include "../config.h"
|
||||
|
||||
struct mo_entry {
|
||||
u32 length;
|
||||
u32 offset;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct mo_hdr {
|
||||
u32 magic;
|
||||
u32 revision;
|
||||
u32 count;
|
||||
struct mo_entry *source;
|
||||
struct mo_entry *target;
|
||||
u32 hash_size;
|
||||
void *hashes;
|
||||
} __attribute__((packed));
|
||||
|
||||
static struct mo_hdr mo;
|
||||
static int mo_inited = false;
|
||||
static const char *mo_data = NULL;
|
||||
|
||||
#define MAGIC_SWAB 0xde120495
|
||||
#define MAGIC 0x950412de
|
||||
|
||||
#define SWAB16(x) ((((x)>>8)&0xFF) | (((x)&0xFF)<<8))
|
||||
#define SWAB32(x) ((SWAB16((x)&0xFFFF)<<16)|(SWAB16(((x)>>16)&0xFFFF)))
|
||||
|
||||
#define ENDIAN(x) ((mo.magic == MAGIC_SWAB) ? SWAB32(x) : (x))
|
||||
|
||||
void i18n_set_mo(const void *mo_file) {
|
||||
mo_inited = true;
|
||||
if(!mo_file) {
|
||||
mo_data = NULL;
|
||||
gprintf("i18n: unset mo file\n");
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(&mo, mo_file, sizeof(struct mo_hdr));
|
||||
if(mo.magic == MAGIC_SWAB) {
|
||||
mo.revision = SWAB32(mo.revision);
|
||||
mo.count = SWAB32(mo.count);
|
||||
mo.source = (struct mo_entry*)SWAB32((u32)mo.source);
|
||||
mo.target = (struct mo_entry*)SWAB32((u32)mo.target);
|
||||
mo.hash_size = SWAB32(mo.hash_size);
|
||||
mo.hashes = (void*)SWAB32((u32)mo.magic);
|
||||
} else if(mo.magic != MAGIC) {
|
||||
gprintf("i18n: bad mo file magic 0x%08lx\n",mo.magic);
|
||||
return;
|
||||
}
|
||||
|
||||
if(mo.revision != 0)
|
||||
gprintf("i18n: bad mo file revision 0x%08lx\n",mo.revision);
|
||||
|
||||
mo_data = (char*)mo_file;
|
||||
mo.source = (struct mo_entry*)(mo_data + (u32)mo.source);
|
||||
gprintf("i18n: configured mo file at %p\n",mo_data);
|
||||
mo.target = (struct mo_entry*)(mo_data + (u32)mo.target);
|
||||
}
|
||||
|
||||
const char *i18n(char *english_string) {
|
||||
int i;
|
||||
|
||||
if(!mo_inited)
|
||||
gprintf("i18n: warning: attempted to translate '%s' before init\n", english_string);
|
||||
|
||||
if(!mo_data)
|
||||
return english_string;
|
||||
|
||||
for(i = 0; i < mo.count; i++) {
|
||||
if(!strcmp(english_string, &mo_data[ENDIAN(mo.source[i].offset)]))
|
||||
return &mo_data[ENDIAN(mo.target[i].offset)];
|
||||
}
|
||||
|
||||
gprintf("i18n: could not find string for %s\n", english_string);
|
||||
|
||||
return english_string;
|
||||
}
|
||||
|
||||
u32 i18n_have_mo(void) {
|
||||
return mo_data ? 1 : 0;
|
||||
}
|
||||
13
channel/channelapp/source/i18n.h
Normal file
13
channel/channelapp/source/i18n.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#ifndef _I18N_H_
|
||||
#define _I18N_H_
|
||||
|
||||
#include <gctypes.h>
|
||||
|
||||
#define _(x) i18n(x)
|
||||
|
||||
void i18n_set_mo(const void *mo_file);
|
||||
const char *i18n(char *english_string);
|
||||
u32 i18n_have_mo(void);
|
||||
|
||||
#endif
|
||||
|
||||
129
channel/channelapp/source/isfs.c
Normal file
129
channel/channelapp/source/isfs.c
Normal file
@@ -0,0 +1,129 @@
|
||||
#include <malloc.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <gccore.h>
|
||||
|
||||
#include "../config.h"
|
||||
#include "blob.h"
|
||||
#include "panic.h"
|
||||
#include "isfs.h"
|
||||
|
||||
#define ROUNDUP32B(x) ((x + 32 - 1) & ~(32 - 1))
|
||||
|
||||
static fstats __st ATTRIBUTE_ALIGN(32);
|
||||
|
||||
s32 isfs_get(const char *path, u8 **buf, u32 expected, u32 maxsize, bool use_blob) {
|
||||
s32 ret;
|
||||
s32 fd;
|
||||
u8 *__buf;
|
||||
|
||||
gprintf("reading %s (expecting %lu bytes)\n", path, expected);
|
||||
ret = ISFS_Open(path, ISFS_OPEN_READ);
|
||||
if (ret < 0) {
|
||||
gprintf("ISFS_Open failed (%ld)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
fd = ret;
|
||||
|
||||
ret = ISFS_GetFileStats(fd, &__st);
|
||||
if (ret < 0) {
|
||||
gprintf("ISFS_GetFileStats failed (%ld)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
DCInvalidateRange(&__st, sizeof(__st));
|
||||
|
||||
if ((__st.file_length == 0) ||
|
||||
(maxsize && (__st.file_length > maxsize)) ||
|
||||
(expected && (__st.file_length != expected))) {
|
||||
gprintf("invalid size: %lu\n", __st.file_length);
|
||||
ISFS_Close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (use_blob) {
|
||||
__buf = blob_alloc(ROUNDUP32B(__st.file_length + 1));
|
||||
if (!__buf)
|
||||
panic();
|
||||
} else {
|
||||
__buf = pmemalign(32, ROUNDUP32B(__st.file_length + 1));
|
||||
}
|
||||
|
||||
gprintf("reading %lu bytes\n", __st.file_length);
|
||||
ret = ISFS_Read(fd, __buf, __st.file_length);
|
||||
if (ret < 0) {
|
||||
gprintf("ISFS_Read failed (%ld)\n", ret);
|
||||
free(__buf);
|
||||
ISFS_Close(fd);
|
||||
return ret;
|
||||
}
|
||||
DCInvalidateRange(__buf, __st.file_length + 1);
|
||||
__buf[__st.file_length] = 0;
|
||||
|
||||
if (ret != __st.file_length) {
|
||||
gprintf("ISFS_Read short read (%ld)\n", ret);
|
||||
free(__buf);
|
||||
ISFS_Close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = ISFS_Close(fd);
|
||||
if (ret < 0) {
|
||||
gprintf("ISFS_Close failed (%ld)\n", ret);
|
||||
free(__buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*buf = __buf;
|
||||
return __st.file_length;
|
||||
}
|
||||
|
||||
s32 isfs_put(const char *path, const void *buf, u32 len) {
|
||||
s32 fd, ret;
|
||||
|
||||
ISFS_Delete(path);
|
||||
|
||||
gprintf("writing %s (%lu bytes)\n", path, len);
|
||||
ret = ISFS_CreateFile(path, 0, 3, 3, 0);
|
||||
if (ret < 0) {
|
||||
gprintf("ISFS_CreateFile failed (%ld)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = ISFS_Open(path, 3);
|
||||
if (ret < 0) {
|
||||
gprintf("ISFS_Open failed (%ld)\n", ret);
|
||||
ISFS_Delete(path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
fd = ret;
|
||||
|
||||
DCFlushRange((void *) buf, len);
|
||||
|
||||
ret = ISFS_Write(fd, buf, len);
|
||||
if (ret < 0) {
|
||||
gprintf("ISFS_Write failed (%ld)\n", ret);
|
||||
ISFS_Close(fd);
|
||||
ISFS_Delete(path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (ret != len) {
|
||||
gprintf("ISFS_Write short write (%ld)\n", ret);
|
||||
ISFS_Close(fd);
|
||||
ISFS_Delete(path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = ISFS_Close(fd);
|
||||
if (ret < 0) {
|
||||
gprintf("ISFS_Close failed (%ld)\n", ret);
|
||||
ISFS_Delete(path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
9
channel/channelapp/source/isfs.h
Normal file
9
channel/channelapp/source/isfs.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#ifndef ___ISFS_H__
|
||||
#define ___ISFS_H__
|
||||
|
||||
#include <gctypes.h>
|
||||
|
||||
s32 isfs_get(const char *path, u8 **buf, u32 expected, u32 maxsize, bool use_blob);
|
||||
s32 isfs_put(const char *path, const void *buf, u32 len);
|
||||
|
||||
#endif
|
||||
1009
channel/channelapp/source/loader.c
Normal file
1009
channel/channelapp/source/loader.c
Normal file
File diff suppressed because it is too large
Load Diff
48
channel/channelapp/source/loader.h
Normal file
48
channel/channelapp/source/loader.h
Normal file
@@ -0,0 +1,48 @@
|
||||
#ifndef _LOADER_H_
|
||||
#define _LOADER_H_
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include <gctypes.h>
|
||||
|
||||
#include "loader_reloc.h"
|
||||
#include "appentry.h"
|
||||
#include "view.h"
|
||||
|
||||
typedef enum {
|
||||
LT_UNKNOWN = 0,
|
||||
LT_EXECUTABLE,
|
||||
LT_ZIP_APP,
|
||||
LT_ZIP_THEME
|
||||
} loader_type;
|
||||
|
||||
typedef struct {
|
||||
loader_type type;
|
||||
|
||||
u8 *data;
|
||||
u32 data_len;
|
||||
|
||||
char args[ARGS_MAX_LEN];
|
||||
u32 args_len;
|
||||
|
||||
char dirname[MAXPATHLEN];
|
||||
u32 bytes;
|
||||
} loader_result;
|
||||
|
||||
void loader_init (void);
|
||||
void loader_deinit (void);
|
||||
|
||||
void loader_tcp_init (void);
|
||||
void loader_signal_threads (void);
|
||||
bool loader_gecko_initialized (void);
|
||||
bool loader_tcp_initializing (void);
|
||||
bool loader_tcp_initialized (void);
|
||||
bool loader_handshaked (void);
|
||||
|
||||
void loader_load(loader_result *result, view *sub_view, app_entry *entry);
|
||||
bool loader_load_executable(entry_point *ep, loader_result *result,
|
||||
view *sub_view);
|
||||
bool loader_handle_zip_app(loader_result *result, view *sub_view);
|
||||
|
||||
#endif
|
||||
|
||||
283
channel/channelapp/source/loader_reloc.c
Normal file
283
channel/channelapp/source/loader_reloc.c
Normal file
@@ -0,0 +1,283 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <ogcsys.h>
|
||||
#include <ogc/machine/processor.h>
|
||||
|
||||
#include "../config.h"
|
||||
#include "loader.h"
|
||||
|
||||
#include "elf_abi.h"
|
||||
#include "asm.h"
|
||||
|
||||
extern void __exception_closeall ();
|
||||
|
||||
typedef struct _dolheader {
|
||||
u32 text_pos[7];
|
||||
u32 data_pos[11];
|
||||
u32 text_start[7];
|
||||
u32 data_start[11];
|
||||
u32 text_size[7];
|
||||
u32 data_size[11];
|
||||
u32 bss_start;
|
||||
u32 bss_size;
|
||||
u32 entry_point;
|
||||
} dolheader;
|
||||
|
||||
static void patch_crt0 (entry_point *ep) {
|
||||
u8 *p = (u8 *) *ep;
|
||||
|
||||
if (p[0x20] == 0x41) {
|
||||
gprintf ("patching crt0\n");
|
||||
p[0x20] = 0x40;
|
||||
|
||||
DCFlushRange ((void *) &p[0x20], 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void set_argv (entry_point *ep, const char *args, u16 arg_len) {
|
||||
u32 *p = (u32 *) *ep;
|
||||
struct __argv *argv;
|
||||
char *cmdline;
|
||||
|
||||
if (p[1] != ARGV_MAGIC) {
|
||||
gprintf ("application does not support argv\n");
|
||||
return;
|
||||
}
|
||||
|
||||
gprintf ("setting argv\n");
|
||||
|
||||
argv = (struct __argv *) &p[2];
|
||||
cmdline = (char *) LD_ARGS_ADDR;
|
||||
|
||||
memcpy (cmdline, args, arg_len);
|
||||
|
||||
#ifdef DEBUG_APP
|
||||
size_t i;
|
||||
|
||||
gprintf ("cmdline='");
|
||||
for (i = 0; i < arg_len; ++i)
|
||||
if (cmdline[i] == 0)
|
||||
gprintf ("\\0");
|
||||
else
|
||||
gprintf ("%c", cmdline[i]);
|
||||
gprintf ("'\n");
|
||||
#endif
|
||||
|
||||
argv->argvMagic = ARGV_MAGIC;
|
||||
argv->commandLine = cmdline;
|
||||
argv->length = arg_len;
|
||||
|
||||
DCFlushRange (&p[2], 4);
|
||||
DCFlushRange ((void *) LD_ARGS_ADDR, arg_len);
|
||||
}
|
||||
|
||||
static bool reloc_dol (entry_point *ep, const u8 *addr, u32 size,
|
||||
bool check_overlap) {
|
||||
u32 i;
|
||||
dolheader *dolfile;
|
||||
|
||||
dolfile = (dolheader *) addr;
|
||||
for (i = 0; i < 7; i++) {
|
||||
if (!dolfile->text_size[i])
|
||||
continue;
|
||||
|
||||
gprintf ("loading text section %lu @ 0x%08lx (0x%08lx bytes)\n", i,
|
||||
dolfile->text_start[i], dolfile->text_size[i]);
|
||||
|
||||
if (dolfile->text_pos[i] + dolfile->text_size[i] > size)
|
||||
return false;
|
||||
|
||||
if (check_overlap && ((dolfile->text_start[i] < LD_MIN_ADDR) ||
|
||||
((dolfile->text_start[i] + dolfile->text_size[i] >
|
||||
LD_MAX_ADDR))))
|
||||
return false;
|
||||
|
||||
|
||||
memmove ((void *) dolfile->text_start[i], addr + dolfile->text_pos[i],
|
||||
dolfile->text_size[i]);
|
||||
|
||||
DCFlushRange ((void *) dolfile->text_start[i], dolfile->text_size[i]);
|
||||
ICInvalidateRange ((void *) dolfile->text_start[i],
|
||||
dolfile->text_size[i]);
|
||||
}
|
||||
|
||||
for(i = 0; i < 11; i++) {
|
||||
if (!dolfile->data_size[i])
|
||||
continue;
|
||||
|
||||
gprintf ("loading data section %lu @ 0x%08lx (0x%08lx bytes)\n", i,
|
||||
dolfile->data_start[i], dolfile->data_size[i]);
|
||||
|
||||
if (dolfile->data_pos[i] + dolfile->data_size[i] > size)
|
||||
return false;
|
||||
|
||||
if (check_overlap && ((dolfile->data_start[i] < LD_MIN_ADDR) ||
|
||||
(dolfile->data_start[i] + dolfile->data_size[i] > LD_MAX_ADDR)))
|
||||
return false;
|
||||
|
||||
memmove ((void*) dolfile->data_start[i], addr + dolfile->data_pos[i],
|
||||
dolfile->data_size[i]);
|
||||
|
||||
DCFlushRange ((void *) dolfile->data_start[i], dolfile->data_size[i]);
|
||||
}
|
||||
|
||||
*ep = (entry_point) dolfile->entry_point;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static s8 is_valid_elf (const u8 *addr, u32 size) {
|
||||
Elf32_Ehdr *ehdr; /* Elf header structure pointer */
|
||||
|
||||
ehdr = (Elf32_Ehdr *) addr;
|
||||
|
||||
if (!IS_ELF (*ehdr))
|
||||
return 0;
|
||||
|
||||
if (ehdr->e_ident[EI_CLASS] != ELFCLASS32)
|
||||
return -1;
|
||||
|
||||
if (ehdr->e_ident[EI_DATA] != ELFDATA2MSB)
|
||||
return -1;
|
||||
|
||||
if (ehdr->e_ident[EI_VERSION] != EV_CURRENT)
|
||||
return -1;
|
||||
|
||||
if (ehdr->e_type != ET_EXEC)
|
||||
return -1;
|
||||
|
||||
if (ehdr->e_machine != EM_PPC)
|
||||
return -1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool reloc_elf (entry_point *ep, const u8 *addr, u32 size,
|
||||
bool check_overlap) {
|
||||
Elf32_Ehdr *ehdr;
|
||||
Elf32_Phdr *phdrs;
|
||||
u8 *image;
|
||||
int i;
|
||||
|
||||
ehdr = (Elf32_Ehdr *) addr;
|
||||
|
||||
if(ehdr->e_phoff == 0 || ehdr->e_phnum == 0) {
|
||||
gprintf("ELF has no phdrs\n");
|
||||
return false;
|
||||
}
|
||||
if(ehdr->e_phentsize != sizeof(Elf32_Phdr)) {
|
||||
gprintf("Invalid ELF phdr size\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
phdrs = (Elf32_Phdr*)(addr + ehdr->e_phoff);
|
||||
|
||||
for(i=0;i<ehdr->e_phnum;i++) {
|
||||
|
||||
if(phdrs[i].p_type != PT_LOAD) {
|
||||
gprintf("skip PHDR %d of type %ld\n", i, phdrs[i].p_type);
|
||||
} else {
|
||||
phdrs[i].p_paddr &= 0x3FFFFFFF;
|
||||
phdrs[i].p_paddr |= 0x80000000;
|
||||
|
||||
gprintf ("PHDR %d 0x%08lx [0x%lx] -> 0x%08lx [0x%lx] <", i,
|
||||
phdrs[i].p_offset, phdrs[i].p_filesz,
|
||||
phdrs[i].p_paddr, phdrs[i].p_memsz);
|
||||
|
||||
if(phdrs[i].p_flags & PF_R)
|
||||
gprintf("R");
|
||||
if(phdrs[i].p_flags & PF_W)
|
||||
gprintf("W");
|
||||
if(phdrs[i].p_flags & PF_X)
|
||||
gprintf("X");
|
||||
gprintf(">\n");
|
||||
|
||||
if(phdrs[i].p_filesz > phdrs[i].p_memsz) {
|
||||
gprintf ("-> file size > mem size\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(phdrs[i].p_filesz) {
|
||||
if (check_overlap && ((phdrs[i].p_paddr < LD_MIN_ADDR) ||
|
||||
(phdrs[i].p_paddr + phdrs[i].p_filesz) > LD_MAX_ADDR)) {
|
||||
gprintf ("-> failed overlap check\n");
|
||||
return false;
|
||||
}
|
||||
gprintf ("-> load 0x%lx\n", phdrs[i].p_filesz);
|
||||
image = (u8 *) (addr + phdrs[i].p_offset);
|
||||
memmove ((void *) phdrs[i].p_paddr, (const void *) image,
|
||||
phdrs[i].p_filesz);
|
||||
|
||||
DCFlushRange ((void *) phdrs[i].p_paddr, phdrs[i].p_memsz);
|
||||
if(phdrs[i].p_flags & PF_X)
|
||||
ICInvalidateRange ((void *) phdrs[i].p_paddr, phdrs[i].p_memsz);
|
||||
} else {
|
||||
gprintf ("-> skip\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*ep = (entry_point) ((ehdr->e_entry & 0x3FFFFFFF) | 0x80000000);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool loader_reloc (entry_point *ep, const u8 *addr, u32 size, const char *args,
|
||||
u16 arg_len, bool check_overlap) {
|
||||
s8 res;
|
||||
bool b;
|
||||
|
||||
res = is_valid_elf (addr, size);
|
||||
|
||||
if (res < 0)
|
||||
return false;
|
||||
|
||||
if (res == 1)
|
||||
b = reloc_elf (ep, addr, size, check_overlap);
|
||||
else
|
||||
b = reloc_dol (ep, addr, size, check_overlap);
|
||||
|
||||
if (b) {
|
||||
patch_crt0 (ep);
|
||||
|
||||
if (args && arg_len)
|
||||
set_argv (ep, args, arg_len);
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
static const u32 exec_stub[] = {
|
||||
0x7c6903a6, // mtctr r3
|
||||
0x3d208133, // lis r9, 0x8133
|
||||
0x3d408180, // lis r10, 0x8180
|
||||
0x90090000, // 1: stw r0, 0(r9)
|
||||
0x39290004, // addi r9, r9, 4
|
||||
0x7c295000, // cmpd r9, r10
|
||||
0x4180fff4, // blt 1b
|
||||
0x4e800420 // bctr
|
||||
};
|
||||
|
||||
void loader_exec (entry_point ep) {
|
||||
gprintf ("shutting down services and vectoring...\n");
|
||||
SYS_ResetSystem (SYS_SHUTDOWN, 0, 0);
|
||||
|
||||
__exception_closeall ();
|
||||
|
||||
// these pokes make ninty SDK dols work, I'm told
|
||||
*(vu32*)0x800000F8 = 0x0E7BE2C0; // Bus Clock Speed
|
||||
*(vu32*)0x800000FC = 0x2B73A840; // CPU Clock Speed
|
||||
|
||||
void *target = (void *)((int)(SYS_GetArena2Hi() - sizeof exec_stub) & ~31);
|
||||
void (*f_exec_stub) (int) = target;
|
||||
|
||||
memcpy(target, exec_stub, sizeof exec_stub);
|
||||
DCFlushRange(target, sizeof exec_stub);
|
||||
ICInvalidateRange(target, sizeof exec_stub);
|
||||
|
||||
f_exec_stub((u32)ep);
|
||||
|
||||
gprintf ("this cant be good\n");
|
||||
}
|
||||
|
||||
13
channel/channelapp/source/loader_reloc.h
Normal file
13
channel/channelapp/source/loader_reloc.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#ifndef _LOADER_RELOC_H_
|
||||
#define _LOADER_RELOC_H_
|
||||
|
||||
#include <gctypes.h>
|
||||
|
||||
typedef void (*entry_point) (void);
|
||||
|
||||
bool loader_reloc (entry_point *ep, const u8 *addr, u32 size, const char *args,
|
||||
u16 arg_len, bool check_overlap);
|
||||
void loader_exec (entry_point ep);
|
||||
|
||||
#endif
|
||||
|
||||
141
channel/channelapp/source/m_main.c
Normal file
141
channel/channelapp/source/m_main.c
Normal file
@@ -0,0 +1,141 @@
|
||||
#include <stdio.h>
|
||||
#include <malloc.h>
|
||||
|
||||
#include <ogcsys.h>
|
||||
#include <network.h>
|
||||
|
||||
#include "../config.h"
|
||||
#include "theme.h"
|
||||
#include "view.h"
|
||||
#include "loader.h"
|
||||
#include "i18n.h"
|
||||
#include "panic.h"
|
||||
|
||||
#include "m_main.h"
|
||||
|
||||
static view *v_m_main;
|
||||
|
||||
static const char *text_no_ip;
|
||||
static const char *text_has_ip;
|
||||
|
||||
static bool bootmii_ios = false;
|
||||
|
||||
static bool bootmii_is_installed(u64 title_id) {
|
||||
u32 tmd_view_size;
|
||||
u8 *tmdbuf;
|
||||
bool ret;
|
||||
|
||||
if (ES_GetTMDViewSize(title_id, &tmd_view_size) < 0)
|
||||
return false;
|
||||
|
||||
if (tmd_view_size < 90)
|
||||
return false;
|
||||
|
||||
if (tmd_view_size > 1024)
|
||||
return false;
|
||||
|
||||
tmdbuf = pmemalign(32, 1024);
|
||||
|
||||
if (ES_GetTMDView(title_id, tmdbuf, tmd_view_size) < 0) {
|
||||
free(tmdbuf);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (tmdbuf[50] == 'B' && tmdbuf[51] == 'M')
|
||||
ret = true;
|
||||
else
|
||||
ret = false;
|
||||
|
||||
free(tmdbuf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool inited_widgets = false;
|
||||
|
||||
view * m_main_init (void) {
|
||||
bootmii_ios = bootmii_is_installed(TITLEID_BOOTMII);
|
||||
|
||||
v_m_main = view_new (8, NULL, 0, 0, 0, 0);
|
||||
|
||||
m_main_theme_reinit();
|
||||
m_main_update();
|
||||
|
||||
view_set_focus(v_m_main, 0);
|
||||
|
||||
return v_m_main;
|
||||
}
|
||||
|
||||
void m_main_deinit(void) {
|
||||
view_free (v_m_main);
|
||||
inited_widgets = false;
|
||||
v_m_main = NULL;
|
||||
}
|
||||
|
||||
void m_main_theme_reinit(void) {
|
||||
u16 x, y, yadd;
|
||||
int i;
|
||||
char buffer[20];
|
||||
|
||||
text_no_ip = _("Network not initialized");
|
||||
text_has_ip = _("Your Wii's IP is %u.%u.%u.%u");
|
||||
|
||||
if (inited_widgets)
|
||||
for (i = 0; i < v_m_main->widget_count; ++i)
|
||||
widget_free(&v_m_main->widgets[i]);
|
||||
|
||||
if (bootmii_ios)
|
||||
yadd = 16;
|
||||
else
|
||||
yadd = 32;
|
||||
|
||||
x = (view_width - theme_gfx[THEME_BUTTON]->w) / 2;
|
||||
y = 80;
|
||||
|
||||
widget_button (&v_m_main->widgets[0], x, y, 0, BTN_NORMAL, _("Back"));
|
||||
y += theme_gfx[THEME_BUTTON]->h + yadd;
|
||||
widget_button (&v_m_main->widgets[1], x, y, 0, BTN_NORMAL, _("About"));
|
||||
y += theme_gfx[THEME_BUTTON]->h + yadd;
|
||||
|
||||
if (bootmii_ios) {
|
||||
widget_button (&v_m_main->widgets[2], x, y, 0, BTN_NORMAL,
|
||||
_("Launch BootMii"));
|
||||
y += theme_gfx[THEME_BUTTON]->h + yadd;
|
||||
}
|
||||
|
||||
widget_button (&v_m_main->widgets[3], x, y, 0, BTN_NORMAL, _("Exit to System Menu"));
|
||||
y += theme_gfx[THEME_BUTTON]->h + yadd;
|
||||
|
||||
widget_button (&v_m_main->widgets[4], x, y, 0, BTN_NORMAL, _("Shutdown"));
|
||||
|
||||
widget_label (&v_m_main->widgets[5], view_width / 3 * 2 - 16, 32, 0,
|
||||
CHANNEL_VERSION_STR, view_width / 3 - 32, FA_RIGHT,
|
||||
FA_ASCENDER, FONT_LABEL);
|
||||
|
||||
sprintf(buffer, "IOS%ld v%ld.%ld", IOS_GetVersion(), IOS_GetRevisionMajor(),
|
||||
IOS_GetRevisionMinor());
|
||||
|
||||
widget_label (&v_m_main->widgets[6], view_width / 3 * 2 - 16,
|
||||
32 + font_get_y_spacing(FONT_LABEL), 0, buffer,
|
||||
view_width / 3 - 32, FA_RIGHT, FA_ASCENDER, FONT_LABEL);
|
||||
|
||||
inited_widgets = true;
|
||||
}
|
||||
|
||||
void m_main_update (void) {
|
||||
u32 ip;
|
||||
char buffer[64];
|
||||
|
||||
if (loader_tcp_initialized ()) {
|
||||
ip = net_gethostip ();
|
||||
sprintf (buffer, text_has_ip, (ip >> 24) & 0xff, (ip >> 16) & 0xff,
|
||||
(ip >> 8) & 0xff, ip & 0xff);
|
||||
widget_label (&v_m_main->widgets[7], 48, 32, 0, buffer,
|
||||
view_width / 3 * 2 - 32, FA_LEFT, FA_ASCENDER, FONT_LABEL);
|
||||
} else {
|
||||
widget_label (&v_m_main->widgets[7], 48, 32, 0, text_no_ip,
|
||||
view_width / 3 * 2 - 32, FA_LEFT, FA_ASCENDER, FONT_LABEL);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
15
channel/channelapp/source/m_main.h
Normal file
15
channel/channelapp/source/m_main.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#ifndef _M_MAIN_H_
|
||||
#define _M_MAIN_H_
|
||||
|
||||
#include <gctypes.h>
|
||||
|
||||
#include "view.h"
|
||||
|
||||
view * m_main_init (void);
|
||||
void m_main_deinit (void);
|
||||
void m_main_theme_reinit (void);
|
||||
|
||||
void m_main_update (void);
|
||||
|
||||
#endif
|
||||
|
||||
825
channel/channelapp/source/main.c
Normal file
825
channel/channelapp/source/main.c
Normal file
@@ -0,0 +1,825 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <malloc.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <unistd.h>
|
||||
#include <locale.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include <ogcsys.h>
|
||||
#include <ogc/machine/processor.h>
|
||||
#include <ogc/conf.h>
|
||||
#include <debug.h>
|
||||
|
||||
#include "../config.h"
|
||||
#include "controls.h"
|
||||
#include "appentry.h"
|
||||
#include "playtime.h"
|
||||
#include "gfx.h"
|
||||
#include "tex.h"
|
||||
#include "theme.h"
|
||||
#include "cursors.h"
|
||||
#include "font.h"
|
||||
#include "widgets.h"
|
||||
#include "view.h"
|
||||
#include "bubbles.h"
|
||||
#include "dialogs.h"
|
||||
#include "browser.h"
|
||||
#include "m_main.h"
|
||||
#include "loader.h"
|
||||
#ifdef ENABLE_UPDATES
|
||||
#include "http.h"
|
||||
#include "update.h"
|
||||
#endif
|
||||
#include "i18n.h"
|
||||
#include "dvd.h"
|
||||
#include "manage.h"
|
||||
#include "xml.h"
|
||||
#include "title.h"
|
||||
|
||||
// Languages
|
||||
#include "spanish_mo.h"
|
||||
#include "dutch_mo.h"
|
||||
#include "german_mo.h"
|
||||
#include "french_mo.h"
|
||||
#include "italian_mo.h"
|
||||
#include "japanese_mo.h"
|
||||
|
||||
#ifdef DEBUG_APP
|
||||
//#define GDBSTUB
|
||||
#else
|
||||
#undef GDBSTUB
|
||||
#endif
|
||||
|
||||
#define WARN_SPLIT 360
|
||||
|
||||
extern const u8 stub_bin[];
|
||||
extern const u8 stub_bin_end;
|
||||
extern const u32 stub_bin_size;
|
||||
|
||||
u64 *conf_magic = STUB_ADDR_MAGIC;
|
||||
u64 *conf_title_id = STUB_ADDR_TITLE;
|
||||
|
||||
static bool should_exit;
|
||||
static bool shutdown;
|
||||
#ifdef GDBSTUB
|
||||
static bool gdb;
|
||||
#endif
|
||||
static const char *text_delete;
|
||||
static const char *text_error_delete;
|
||||
|
||||
s32 __IOS_LoadStartupIOS(void) {
|
||||
#if 0
|
||||
__ES_Init();
|
||||
__IOS_LaunchNewIOS(58);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define MEM2_PROT 0x0D8B420A
|
||||
#define ES_MODULE_START (u16*)0x939F0000
|
||||
|
||||
static const u16 ticket_check[] = {
|
||||
0x685B, // ldr r3,[r3,#4] ; get TMD pointer
|
||||
0x22EC, 0x0052, // movls r2, 0x1D8
|
||||
0x189B, // adds r3, r3, r2; add offset of access rights field in TMD
|
||||
0x681B, // ldr r3, [r3] ; load access rights (haxxme!)
|
||||
0x4698, // mov r8, r3 ; store it for the DVD video bitcheck later
|
||||
0x07DB // lsls r3, r3, #31; check AHBPROT bit
|
||||
};
|
||||
|
||||
static int patch_ahbprot_reset(void)
|
||||
{
|
||||
u16 *patchme;
|
||||
|
||||
if ((read32(0x0D800064) == 0xFFFFFFFF) ? 1 : 0) {
|
||||
write16(MEM2_PROT, 2);
|
||||
for (patchme=ES_MODULE_START; patchme < ES_MODULE_START+0x4000; ++patchme) {
|
||||
if (!memcmp(patchme, ticket_check, sizeof(ticket_check)))
|
||||
{
|
||||
// write16/uncached poke doesn't work for MEM2
|
||||
patchme[4] = 0x23FF; // li r3, 0xFF
|
||||
DCFlushRange(patchme+4, 2);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
} else {
|
||||
return -2;
|
||||
}
|
||||
}
|
||||
|
||||
static void reset_cb(void) {
|
||||
#ifdef GDBSTUB
|
||||
gdb = true;
|
||||
#else
|
||||
should_exit = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void power_cb(void) {
|
||||
should_exit = true;
|
||||
shutdown = true;
|
||||
}
|
||||
|
||||
void config_language(void) {
|
||||
s32 language;
|
||||
const void *mo;
|
||||
|
||||
// this is probably wrong, but we don't let the C library do any UTF-8 text
|
||||
// processing worth mentioning anyway, and I'm scared of what might happen
|
||||
// if I change this :P
|
||||
setlocale(LC_CTYPE, "C-ISO-8859-1");
|
||||
|
||||
#ifdef FORCE_LANG
|
||||
language = FORCE_LANG;
|
||||
#else
|
||||
language = CONF_GetLanguage();
|
||||
if (language < 0)
|
||||
language = CONF_LANG_ENGLISH;
|
||||
#endif
|
||||
|
||||
mo = NULL;
|
||||
|
||||
switch (language) {
|
||||
case CONF_LANG_ENGLISH:
|
||||
mo = NULL;
|
||||
break;
|
||||
|
||||
case CONF_LANG_SPANISH:
|
||||
mo = spanish_mo;
|
||||
break;
|
||||
|
||||
case CONF_LANG_FRENCH:
|
||||
mo = french_mo;
|
||||
break;
|
||||
|
||||
case CONF_LANG_GERMAN:
|
||||
mo = german_mo;
|
||||
break;
|
||||
|
||||
case CONF_LANG_DUTCH:
|
||||
mo = dutch_mo;
|
||||
break;
|
||||
|
||||
case CONF_LANG_ITALIAN:
|
||||
mo = italian_mo;
|
||||
break;
|
||||
|
||||
case CONF_LANG_JAPANESE:
|
||||
if (theme.langs & TLANG_JA) {
|
||||
mo = japanese_mo;
|
||||
} else {
|
||||
gprintf("language ja disabled due to missing theme support\n");
|
||||
language = CONF_LANG_ENGLISH;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
gprintf("unsupported language %ld, defaulting to English\n",
|
||||
language);
|
||||
language = CONF_LANG_ENGLISH;
|
||||
}
|
||||
|
||||
gprintf("configuring language: %ld with mo file at %p\n", language, mo);
|
||||
i18n_set_mo(mo);
|
||||
}
|
||||
|
||||
|
||||
static void main_pre(void) {
|
||||
#ifdef DEBUG_APP
|
||||
if(usb_isgeckoalive(USBGECKO_CHANNEL))
|
||||
CON_EnableGecko(USBGECKO_CHANNEL, 1);
|
||||
|
||||
#ifdef GDBSTUB
|
||||
DEBUG_Init(GDBSTUB_DEVICE_USB, USBGECKO_CHANNEL);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
AUDIO_Init(NULL);
|
||||
DSP_Init();
|
||||
AUDIO_StopDMA();
|
||||
AUDIO_RegisterDMACallback(NULL);
|
||||
|
||||
gfx_init_video();
|
||||
PAD_Init();
|
||||
|
||||
SYS_SetResetCallback(reset_cb);
|
||||
title_init();
|
||||
SYS_SetPowerCallback(power_cb);
|
||||
|
||||
gprintf("installing stub (%lu)\n", stub_bin_size);
|
||||
|
||||
#ifdef DEBUG_APP
|
||||
if (stub_bin_size > 0x1800)
|
||||
gprintf("WARNING: stub too big!\n");
|
||||
#endif
|
||||
|
||||
memcpy((u32 *) 0x80001800, stub_bin, stub_bin_size);
|
||||
DCFlushRange((u32 *) 0x80001800, stub_bin_size);
|
||||
|
||||
*conf_magic = 0;
|
||||
|
||||
gprintf ("startup\n");
|
||||
gprintf("IOS Version: IOS%ld %ld.%ld\n",
|
||||
IOS_GetVersion(), IOS_GetRevisionMajor(), IOS_GetRevisionMinor());
|
||||
|
||||
ISFS_Initialize();
|
||||
|
||||
WiiDVD_StopMotorAsync();
|
||||
|
||||
gfx_init();
|
||||
app_entry_init();
|
||||
theme_xml_init();
|
||||
theme_init(NULL, 0);
|
||||
config_language();
|
||||
loader_init();
|
||||
controls_init();
|
||||
cursors_init();
|
||||
font_init();
|
||||
widgets_init();
|
||||
view_init();
|
||||
dialogs_init();
|
||||
}
|
||||
|
||||
static void load_text(void)
|
||||
{
|
||||
text_delete = _("Do you really want to delete this application?");
|
||||
text_error_delete = _("Error deleting '%s'");
|
||||
}
|
||||
|
||||
static void refresh_theme(view *v, app_entry *app, u8 *data, u32 data_len) {
|
||||
browser_gen_view(BA_REMOVE, NULL);
|
||||
view_fade(v, TEX_LAYER_CURSOR + 1, 0, 0, 0, 0, 32, 8);
|
||||
font_clear();
|
||||
theme_deinit();
|
||||
theme_init(data, data_len);
|
||||
config_language();
|
||||
widgets_theme_reinit();
|
||||
bubbles_theme_reinit();
|
||||
view_theme_reinit();
|
||||
browser_theme_reinit();
|
||||
m_main_theme_reinit();
|
||||
dialogs_theme_reinit();
|
||||
view_fade(v, TEX_LAYER_CURSOR + 1, 0xff, 0xff, 0xff, 0xff, 31, -8);
|
||||
browser_gen_view(BA_REFRESH, app);
|
||||
load_text();
|
||||
}
|
||||
|
||||
void main_real(void) {
|
||||
view *v_last, *v_current, *v_m_main, *v_browser, *v_detail, *v_about;
|
||||
|
||||
u8 fhw;
|
||||
|
||||
u32 bd, bh;
|
||||
s8 clicked;
|
||||
s16 mm;
|
||||
|
||||
#ifdef ENABLE_UPDATES
|
||||
s16 update_check = -1;
|
||||
bool update_in_progress = false;
|
||||
bool update_available = false;
|
||||
#endif
|
||||
|
||||
app_entry *app_sel;
|
||||
bool loading = false;
|
||||
|
||||
loader_result ld_res;
|
||||
entry_point ep;
|
||||
bool reloced;
|
||||
bool ahb_access;
|
||||
bool launch_bootmii;
|
||||
|
||||
u64 frame;
|
||||
bool exit_about;
|
||||
|
||||
char charbuf[MAXPATHLEN];
|
||||
|
||||
load_text();
|
||||
|
||||
playtime_destroy();
|
||||
|
||||
if (settings_load()) {
|
||||
app_entry_set_prefered(settings.device);
|
||||
app_entry_set_sort(settings.sort_order);
|
||||
|
||||
if (settings.browse_mode == 1)
|
||||
browser_switch_mode();
|
||||
}
|
||||
|
||||
#ifdef ENABLE_UPDATES
|
||||
update_check = 60;
|
||||
#endif
|
||||
|
||||
app_sel = NULL;
|
||||
v_browser = browser_init();
|
||||
v_m_main = m_main_init ();
|
||||
view_bubbles = true;
|
||||
|
||||
#ifdef ENABLE_UPDATES
|
||||
http_init ();
|
||||
#endif
|
||||
|
||||
fhw = font_get_y_spacing(FONT_MEMO);
|
||||
|
||||
should_exit = false;
|
||||
shutdown = false;
|
||||
#ifdef GDBSTUB
|
||||
gdb = false;
|
||||
#endif
|
||||
|
||||
reloced = false;
|
||||
ahb_access = false;
|
||||
launch_bootmii = false;
|
||||
|
||||
frame = 0;
|
||||
exit_about = false;
|
||||
|
||||
v_last = v_current = v_browser;
|
||||
v_detail = NULL;
|
||||
v_about = NULL;
|
||||
|
||||
view_fade(v_current, TEX_LAYER_CURSOR + 1, 0xff, 0xff, 0xff, 0xff, 31, -8);
|
||||
|
||||
view_enable_cursor (true);
|
||||
|
||||
while (!should_exit) {
|
||||
#ifdef GDBSTUB
|
||||
if (gdb) {
|
||||
gprintf("attach gdb now!\n");
|
||||
_break();
|
||||
gdb = false;
|
||||
SYS_SetResetCallback (reset_cb);
|
||||
}
|
||||
#endif
|
||||
memstats(false);
|
||||
|
||||
if (v_current == v_browser) {
|
||||
switch (app_entry_action()) {
|
||||
case AE_ACT_NONE:
|
||||
break;
|
||||
|
||||
case AE_ACT_REMOVE:
|
||||
app_sel = browser_sel();
|
||||
if (app_sel) {
|
||||
memset(settings.app_sel, 0, sizeof(settings.app_sel));
|
||||
strcpy(settings.app_sel, app_sel->dirname);
|
||||
}
|
||||
|
||||
bubble_popall();
|
||||
browser_gen_view(BA_REMOVE, NULL);
|
||||
app_entries_free();
|
||||
memstats(true);
|
||||
break;
|
||||
|
||||
case AE_ACT_ADD:
|
||||
if (strlen(settings.app_sel) > 0)
|
||||
app_sel = app_entry_find(settings.app_sel);
|
||||
else
|
||||
app_sel = NULL;
|
||||
|
||||
view_show_throbber(false);
|
||||
browser_gen_view(BA_ADD, app_sel);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (loading != app_entry_is_loading()) {
|
||||
loading = !loading;
|
||||
|
||||
view_show_throbber(loading);
|
||||
}
|
||||
|
||||
if (loading)
|
||||
view_throbber_tickle();
|
||||
|
||||
if ((frame % 30) == 0)
|
||||
app_entry_scan();
|
||||
|
||||
view_plot (v_current, DIALOG_MASK_COLOR, &bd, &bh, NULL);
|
||||
|
||||
frame++;
|
||||
if (v_last != v_current) {
|
||||
frame = 0;
|
||||
v_last = v_current;
|
||||
}
|
||||
|
||||
if (bd & PADS_HOME) {
|
||||
if (v_current == v_browser) {
|
||||
m_main_update ();
|
||||
v_current = v_m_main;
|
||||
view_set_focus (v_m_main, 0);
|
||||
|
||||
continue;
|
||||
} else {
|
||||
if (v_current == v_m_main)
|
||||
v_current = v_browser;
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (v_current == v_m_main) {
|
||||
if (bd & PADS_B) {
|
||||
v_current = v_browser;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bd & PADS_UP)
|
||||
view_set_focus_prev (v_current);
|
||||
|
||||
if (bd & PADS_DOWN)
|
||||
view_set_focus_next (v_current);
|
||||
|
||||
if (bd & PADS_A) {
|
||||
switch (v_m_main->focus) {
|
||||
case 0:
|
||||
v_current = v_browser;
|
||||
continue;
|
||||
|
||||
case 1:
|
||||
v_about = dialog_about (v_m_main);
|
||||
v_current = v_about;
|
||||
|
||||
view_enable_cursor (false);
|
||||
|
||||
dialog_fade (v_current, true);
|
||||
|
||||
exit_about = false;
|
||||
|
||||
continue;
|
||||
|
||||
case 2:
|
||||
launch_bootmii = true;
|
||||
should_exit = true;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
should_exit = true;
|
||||
continue;
|
||||
|
||||
case 4:
|
||||
should_exit = true;
|
||||
shutdown = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (v_current == v_browser) {
|
||||
widget_set_flag (&v_browser->widgets[2], WF_ENABLED,
|
||||
loader_gecko_initialized ());
|
||||
|
||||
if (!loader_tcp_initializing ())
|
||||
widget_set_flag (&v_browser->widgets[3], WF_ENABLED,
|
||||
loader_tcp_initialized ());
|
||||
|
||||
if (loader_tcp_initializing () && (frame % 20 == 0))
|
||||
widget_toggle_flag (&v_browser->widgets[3], WF_ENABLED);
|
||||
|
||||
if (bd & PADS_NET_INIT)
|
||||
loader_tcp_init ();
|
||||
|
||||
#ifdef ENABLE_UPDATES
|
||||
if (loader_tcp_initialized () && (update_check > 0))
|
||||
update_check--;
|
||||
|
||||
if (update_check == 0) {
|
||||
update_check = -1;
|
||||
update_in_progress = update_signal ();
|
||||
}
|
||||
|
||||
if (update_in_progress) {
|
||||
if (!loading && !update_busy(&update_available))
|
||||
update_in_progress = false;
|
||||
}
|
||||
|
||||
if (update_available) {
|
||||
update_available = false;
|
||||
|
||||
reloced = update_execute (v_current, &ep);
|
||||
should_exit = reloced;
|
||||
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
if (bd & PADS_1) {
|
||||
dialog_options_result options;
|
||||
options = show_options_dialog(v_current);
|
||||
|
||||
if (options.confirmed) {
|
||||
app_entry_set_sort(options.sort);
|
||||
app_entry_set_device(options.device);
|
||||
app_entry_set_prefered(options.device);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bd & PADS_2) {
|
||||
browser_switch_mode();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bd & PADS_DPAD) {
|
||||
browser_set_focus(bd);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bd & PADS_A) {
|
||||
clicked = view_widget_at_ir (v_browser);
|
||||
|
||||
if (clicked == 0) {
|
||||
browser_gen_view(BA_PREV, NULL);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (clicked == 1) {
|
||||
browser_gen_view(BA_NEXT, NULL);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (clicked == 3) {
|
||||
loader_tcp_init ();
|
||||
continue;
|
||||
}
|
||||
|
||||
app_sel = browser_sel();
|
||||
|
||||
if (!app_sel)
|
||||
continue;
|
||||
|
||||
v_detail = dialog_app (app_sel, v_browser);
|
||||
v_current = v_detail;
|
||||
|
||||
dialog_fade (v_current, true);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((bd | bh) & PADS_MINUS)
|
||||
browser_gen_view(BA_PREV, NULL);
|
||||
|
||||
if ((bd | bh) & PADS_PLUS)
|
||||
browser_gen_view(BA_NEXT, NULL);
|
||||
|
||||
if (loader_handshaked ()) {
|
||||
loader_load (&ld_res, v_browser, NULL);
|
||||
|
||||
switch (ld_res.type) {
|
||||
case LT_UNKNOWN:
|
||||
break;
|
||||
|
||||
case LT_EXECUTABLE:
|
||||
if (loader_load_executable(&ep, &ld_res, v_browser)) {
|
||||
reloced = true;
|
||||
ahb_access = true;
|
||||
should_exit = true;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case LT_ZIP_APP:
|
||||
if (loader_handle_zip_app(&ld_res, v_browser)) {
|
||||
app_sel = app_entry_add(ld_res.dirname);
|
||||
|
||||
if (app_sel)
|
||||
browser_gen_view(BA_REFRESH, app_sel);
|
||||
memstats(true);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case LT_ZIP_THEME:
|
||||
refresh_theme(v_current, app_sel, ld_res.data,
|
||||
ld_res.data_len);
|
||||
fhw = font_get_y_spacing(FONT_MEMO);
|
||||
memstats(true);
|
||||
break;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!reloced)
|
||||
loader_signal_threads ();
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (v_current == v_detail) {
|
||||
if (bd & PADS_LEFT)
|
||||
view_set_focus_prev (v_current);
|
||||
|
||||
if (bd & PADS_RIGHT)
|
||||
view_set_focus_next (v_current);
|
||||
|
||||
mm = 0;
|
||||
if (bd & PADS_UP)
|
||||
mm += fhw;
|
||||
|
||||
if (bd & PADS_DOWN)
|
||||
mm -= fhw;
|
||||
|
||||
mm += controls_sticky() / 8;
|
||||
|
||||
if (v_current->drag && (v_current->drag_widget == 6)) {
|
||||
mm += -v_current->drag_y / 32;
|
||||
} else if (bd & PADS_B) {
|
||||
dialog_fade (v_current, false);
|
||||
|
||||
v_current = v_browser;
|
||||
view_free (v_detail);
|
||||
v_detail = NULL;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
widget_scroll_memo_deco (&v_detail->widgets[5], mm);
|
||||
|
||||
if ((bd & PADS_A) && v_detail->focus == 10) {
|
||||
dialog_fade (v_current, false);
|
||||
|
||||
v_current = v_browser;
|
||||
view_free (v_detail);
|
||||
v_detail = NULL;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((bd & PADS_A) && v_detail->focus == 8) {
|
||||
dialog_fade (v_current, false);
|
||||
|
||||
v_current = v_browser;
|
||||
view_free (v_detail);
|
||||
v_detail = NULL;
|
||||
|
||||
if (show_message (v_current, DLGMT_CONFIRM, DLGB_YESNO,
|
||||
text_delete, 1) == 1)
|
||||
continue;
|
||||
|
||||
browser_gen_view(BA_REMOVE, NULL);
|
||||
if (!manage_run(v_current, app_sel->dirname, NULL, 0, 0)) {
|
||||
sprintf(charbuf, text_error_delete, app_sel->dirname);
|
||||
show_message(v_current, DLGMT_ERROR, DLGB_OK, charbuf, 0);
|
||||
} else {
|
||||
app_entry_remove(app_sel);
|
||||
app_sel = NULL;
|
||||
}
|
||||
browser_gen_view(BA_REFRESH, NULL);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((bd & PADS_A) && v_detail->focus == 9) {
|
||||
dialog_fade (v_current, false);
|
||||
|
||||
v_current = v_browser;
|
||||
|
||||
view_free (v_detail);
|
||||
v_detail = NULL;
|
||||
|
||||
loader_load(&ld_res, v_browser, app_sel);
|
||||
|
||||
switch (ld_res.type) {
|
||||
case LT_UNKNOWN:
|
||||
break;
|
||||
|
||||
case LT_EXECUTABLE:
|
||||
if (loader_load_executable(&ep, &ld_res, v_browser)) {
|
||||
reloced = true;
|
||||
|
||||
if (app_sel && app_sel->meta)
|
||||
ahb_access = app_sel->meta->ahb_access;
|
||||
|
||||
should_exit = true;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case LT_ZIP_APP:
|
||||
free(ld_res.data);
|
||||
break;
|
||||
|
||||
case LT_ZIP_THEME:
|
||||
refresh_theme(v_current, app_sel, ld_res.data,
|
||||
ld_res.data_len);
|
||||
break;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (v_current == v_about) {
|
||||
if (bd & (PADS_A | PADS_B))
|
||||
exit_about = true;
|
||||
|
||||
if (exit_about) {
|
||||
dialog_fade (v_current, false);
|
||||
|
||||
v_current = v_m_main;
|
||||
view_free (v_about);
|
||||
v_about = NULL;
|
||||
|
||||
view_enable_cursor (true);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((frame > 60 * 2) && (frame % 3) == 0)
|
||||
widget_scroll_memo (&v_current->widgets[2], -1);
|
||||
}
|
||||
}
|
||||
|
||||
gprintf ("exiting\n");
|
||||
|
||||
view_enable_cursor (false);
|
||||
|
||||
if (v_current->sub_view)
|
||||
dialog_fade (v_current, false);
|
||||
|
||||
view_fade (v_current, TEX_LAYER_CURSOR + 1, 0, 0, 0, 0, 32, 8);
|
||||
|
||||
WiiDVD_ShutDown();
|
||||
controls_deinit();
|
||||
|
||||
app_sel = browser_sel();
|
||||
if (app_sel) {
|
||||
size_t i, s = strlen(app_sel->dirname);
|
||||
memset(settings.app_sel, 0, sizeof(settings.app_sel));
|
||||
for (i = 0; i < s; ++i)
|
||||
settings.app_sel[i] = tolower((int) app_sel->dirname[i]);
|
||||
}
|
||||
|
||||
// because the tcp thread is the only one that can block after the exit
|
||||
// command is sent, deinit it first. this gives the other threads a chance
|
||||
// to do any pending work if the tcp thread does block.
|
||||
loader_deinit ();
|
||||
#ifdef ENABLE_UPDATES
|
||||
http_deinit ();
|
||||
#endif
|
||||
app_entry_deinit ();
|
||||
|
||||
gfx_deinit ();
|
||||
cursors_deinit ();
|
||||
|
||||
browser_deinit ();
|
||||
m_main_deinit ();
|
||||
|
||||
dialogs_deinit ();
|
||||
view_deinit ();
|
||||
widgets_deinit ();
|
||||
font_deinit ();
|
||||
theme_deinit();
|
||||
|
||||
settings_save();
|
||||
|
||||
if (launch_bootmii) {
|
||||
gprintf ("launching BootMii\n");
|
||||
__IOS_LaunchNewIOS(BOOTMII_IOS);
|
||||
}
|
||||
|
||||
if (reloced) {
|
||||
if (ahb_access) {
|
||||
gprintf ("patching IOS for AHB access post-reload...\n");
|
||||
int res = patch_ahbprot_reset();
|
||||
if (res)
|
||||
gprintf ("patch failed (%d)\n", res);
|
||||
}
|
||||
|
||||
gprintf ("reloading to IOS%ld...\n", APPS_IOS_VERSION);
|
||||
__IOS_LaunchNewIOS(APPS_IOS_VERSION);
|
||||
|
||||
if (ahb_access) {
|
||||
gprintf ("reenabling DVD access...\n");
|
||||
mask32(0x0D800180, 1<<21, 0);
|
||||
}
|
||||
|
||||
gprintf ("branching to %p\n", ep);
|
||||
loader_exec (ep);
|
||||
}
|
||||
|
||||
if (shutdown) {
|
||||
gprintf ("shutting down\n");
|
||||
SYS_ResetSystem(SYS_POWEROFF, 0, 0);
|
||||
}
|
||||
|
||||
gprintf ("returning to sysmenu\n");
|
||||
SYS_ResetSystem (SYS_RETURNTOMENU, 0, 0);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
main_pre();
|
||||
main_real();
|
||||
gprintf("uh oh\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
561
channel/channelapp/source/manage.c
Normal file
561
channel/channelapp/source/manage.c
Normal file
@@ -0,0 +1,561 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/dir.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <malloc.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <ogcsys.h>
|
||||
|
||||
#include "../config.h"
|
||||
#include "appentry.h"
|
||||
#include "theme.h"
|
||||
#include "unzip.h"
|
||||
#include "dialogs.h"
|
||||
#include "i18n.h"
|
||||
#include "panic.h"
|
||||
|
||||
#include "manage.h"
|
||||
|
||||
s32 dir_exists(char *dirname) {
|
||||
struct stat st;
|
||||
|
||||
if (stat(dirname, &st) != 0)
|
||||
return 0;
|
||||
|
||||
if (S_ISDIR(st.st_mode))
|
||||
return 1;
|
||||
|
||||
gprintf("'%s' exists, but is no directory\n", dirname);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static s32 mkdir_hier(char *dirname) {
|
||||
char dir[MAXPATHLEN];
|
||||
size_t i;
|
||||
s32 res;
|
||||
|
||||
strcpy(dir, dirname);
|
||||
|
||||
if (dir[strlen(dir) - 1] != '/')
|
||||
strcat(dir, "/");
|
||||
|
||||
i = 1;
|
||||
while (dir[i]) {
|
||||
if (dir[i] == '/') {
|
||||
dir[i] = 0;
|
||||
|
||||
res = dir_exists(dir);
|
||||
|
||||
if (res < 0)
|
||||
return res;
|
||||
|
||||
if (!res) {
|
||||
gprintf("mkdir '%s'\n", dir);
|
||||
if (mkdir(dir, 0755)) {
|
||||
gprintf("mkdir failed: %d\n", errno);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
dir[i] = '/';
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static s32 rmdir_hier_iter(const char *dirname) {
|
||||
s32 res = -1;
|
||||
DIR* d;
|
||||
struct dirent *de;
|
||||
|
||||
gprintf("rmdir_hier_iter '%s'\n", dirname);
|
||||
|
||||
d = opendir(dirname);
|
||||
if (!d) {
|
||||
gprintf("opendir '%s' failed\n", dirname);
|
||||
return -1;
|
||||
}
|
||||
|
||||
char newpath[MAXPATHLEN];
|
||||
|
||||
while ((de = readdir(d))) {
|
||||
if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
|
||||
continue;
|
||||
|
||||
strcpy(newpath, dirname);
|
||||
if (newpath[strlen(newpath) - 1] != '/')
|
||||
strcat(newpath, "/");
|
||||
strcat(newpath, de->d_name);
|
||||
|
||||
if (de->d_type == DT_DIR) {
|
||||
res = rmdir_hier_iter(newpath);
|
||||
if (res)
|
||||
goto exit;
|
||||
}
|
||||
|
||||
gprintf("unlinking '%s'\n", newpath);
|
||||
res = unlink(newpath);
|
||||
if (res) {
|
||||
gprintf("error unlinking '%s'\n", newpath);
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
res = 0;
|
||||
|
||||
exit:
|
||||
closedir(d);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static s32 rmdir_hier(const char *dirname) {
|
||||
char buf[MAXPATHLEN];
|
||||
|
||||
sprintf(buf, "%s/%s", app_path, dirname);
|
||||
|
||||
gprintf("rmdir_hier '%s'\n", buf);
|
||||
s32 res = rmdir_hier_iter(buf);
|
||||
if (!res) {
|
||||
gprintf("unlinking dir '%s'\n", (buf));
|
||||
res = unlink(buf);
|
||||
if (res)
|
||||
gprintf("error unlinking '%s'\n", buf);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool manage_is_zip(const u8 *data) {
|
||||
return memcmp(data, "PK\x03\x04", 4) == 0;
|
||||
}
|
||||
|
||||
bool manage_check_zip_app(u8 *data, u32 data_len, char *dirname, u32 *bytes) {
|
||||
unzFile uf;
|
||||
int res, i;
|
||||
bool ret = false;
|
||||
unz_global_info gi;
|
||||
|
||||
uf = unzOpen(data, data_len);
|
||||
if (!uf) {
|
||||
gprintf("unzOpen failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
res = unzGetGlobalInfo (uf, &gi);
|
||||
if (res != UNZ_OK) {
|
||||
gprintf("unzGetGlobalInfo failed: %d\n", res);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if ((gi.number_entry < 2) || (gi.number_entry > 1024)) {
|
||||
gprintf("invalid file count\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
char filename[256];
|
||||
unz_file_info fi;
|
||||
u8 got_elf = 0;
|
||||
u8 got_dol = 0;
|
||||
u8 got_theme = 0;
|
||||
|
||||
dirname[0] = 0;
|
||||
*bytes = 0;
|
||||
|
||||
for (i = 0; i < gi.number_entry; ++i) {
|
||||
res = unzGetCurrentFileInfo(uf, &fi, filename, sizeof(filename) ,NULL, 0, NULL, 0);
|
||||
if (res != UNZ_OK) {
|
||||
gprintf("unzGetCurrentFileInfo failed: %d\n", res);
|
||||
goto error;
|
||||
}
|
||||
|
||||
gprintf("found '%s' %lu -> %lu\n", filename, fi.compressed_size, fi.uncompressed_size);
|
||||
|
||||
if (filename[0] == '/' || strchr(filename, '\\') || strchr(filename, ':')) {
|
||||
gprintf("invalid char in filename\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (fi.flag & 1) {
|
||||
gprintf("encrypted entry\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (fi.uncompressed_size > 0) {
|
||||
*bytes += fi.uncompressed_size;
|
||||
|
||||
if (!dirname[0]) {
|
||||
char *p = strchr(filename, '/');
|
||||
if (p) {
|
||||
strncpy(dirname, filename, p - filename + 1);
|
||||
dirname[p - filename + 1] = 0;
|
||||
|
||||
gprintf("dirname='%s'\n", dirname);
|
||||
} else {
|
||||
gprintf("missing pathname\n");
|
||||
goto error;
|
||||
}
|
||||
} else {
|
||||
if (strncmp(filename, dirname, strlen(dirname))) {
|
||||
gprintf("additional pathname\n");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if (!strcasecmp(filename + strlen(dirname), app_fn_boot_elf))
|
||||
got_elf = 1;
|
||||
else if (!strcasecmp(filename + strlen(dirname), app_fn_boot_dol))
|
||||
got_dol = 1;
|
||||
else if (!strcasecmp(filename + strlen(dirname), app_fn_theme))
|
||||
got_theme = 1;
|
||||
}
|
||||
|
||||
if (i != gi.number_entry - 1) {
|
||||
res = unzGoToNextFile(uf);
|
||||
if (res != UNZ_OK) {
|
||||
gprintf("unzGoToNextFile failed: %d\n", res);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (strlen(dirname) && ((got_elf + got_dol + got_theme) == 1)) {
|
||||
ret = true;
|
||||
dirname[strlen(dirname) - 1] = 0;
|
||||
}
|
||||
|
||||
error:
|
||||
res = unzClose(uf);
|
||||
if (res)
|
||||
gprintf("unzClose failed: %d\n", res);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool manage_check_zip_theme(u8 *data, u32 data_len) {
|
||||
unzFile uf;
|
||||
int res, i;
|
||||
bool ret = false;
|
||||
unz_global_info gi;
|
||||
|
||||
if (data_len > MAX_THEME_ZIP_SIZE) {
|
||||
gprintf("theme size too big: %lu\n", data_len);
|
||||
return false;
|
||||
}
|
||||
|
||||
uf = unzOpen(data, data_len);
|
||||
if (!uf) {
|
||||
gprintf("unzOpen failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
res = unzGetGlobalInfo (uf, &gi);
|
||||
if (res != UNZ_OK) {
|
||||
gprintf("unzGetGlobalInfo failed: %d\n", res);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if ((gi.number_entry < 1) || (gi.number_entry > 64)) {
|
||||
gprintf("invalid file count\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
char filename[256];
|
||||
unz_file_info fi;
|
||||
bool got_xml = false;
|
||||
|
||||
for (i = 0; i < gi.number_entry; ++i) {
|
||||
res = unzGetCurrentFileInfo(uf, &fi, filename, sizeof(filename) ,NULL, 0, NULL, 0);
|
||||
if (res != UNZ_OK) {
|
||||
gprintf("unzGetCurrentFileInfo failed: %d\n", res);
|
||||
goto error;
|
||||
}
|
||||
|
||||
gprintf("found '%s' %lu -> %lu\n", filename, fi.compressed_size, fi.uncompressed_size);
|
||||
|
||||
if (filename[0] == '/' || strchr(filename, '\\') || strchr(filename, ':')) {
|
||||
gprintf("invalid char in filename\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (fi.flag & 1) {
|
||||
gprintf("encrypted entry\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (fi.uncompressed_size > 0) {
|
||||
char *p = strchr(filename, '/');
|
||||
if (p) {
|
||||
gprintf("directories not accepted\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!strcasecmp(filename, theme_fn_xml)) {
|
||||
got_xml = true;
|
||||
} else if (!theme_is_valid_fn(filename)) {
|
||||
gprintf("not a valid theme filename\n");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if (i != gi.number_entry - 1) {
|
||||
res = unzGoToNextFile(uf);
|
||||
if (res != UNZ_OK) {
|
||||
gprintf("unzGoToNextFile failed: %d\n", res);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (got_xml)
|
||||
ret = true;
|
||||
|
||||
error:
|
||||
res = unzClose(uf);
|
||||
if (res)
|
||||
gprintf("unzClose failed: %d\n", res);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool manage_extract_zip(u8 *data, u32 data_len,
|
||||
mutex_t *mutex, u32 *progress) {
|
||||
unzFile uf;
|
||||
int res, i;
|
||||
bool ret = false;
|
||||
unz_global_info gi;
|
||||
u8 *buf = NULL;
|
||||
|
||||
uf = unzOpen(data, data_len);
|
||||
if (!uf) {
|
||||
gprintf("unzOpen failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
res = unzGetGlobalInfo (uf, &gi);
|
||||
if (res != UNZ_OK) {
|
||||
gprintf("unzGetGlobalInfo failed: %d\n", res);
|
||||
goto error;
|
||||
}
|
||||
|
||||
unz_file_info fi;
|
||||
char filename[256];
|
||||
char sd_filename[MAXPATHLEN];
|
||||
char *p;
|
||||
int fd;
|
||||
|
||||
buf = (u8 *) pmalloc(8 * 1024);
|
||||
|
||||
for (i = 0; i < gi.number_entry; ++i) {
|
||||
res = unzGetCurrentFileInfo(uf, &fi, filename, sizeof(filename) ,NULL, 0, NULL, 0);
|
||||
if (res != UNZ_OK) {
|
||||
gprintf("unzGetCurrentFileInfo failed: %d\n", res);
|
||||
goto error;
|
||||
}
|
||||
|
||||
gprintf("extracting '%s' %lu -> %lu\n", filename, fi.compressed_size, fi.uncompressed_size);
|
||||
|
||||
if (fi.uncompressed_size > 0) {
|
||||
res = unzOpenCurrentFile(uf);
|
||||
if (res != UNZ_OK) {
|
||||
gprintf("unzOpenCurrentFile failed: %d\n", res);
|
||||
goto error;
|
||||
}
|
||||
|
||||
sprintf(sd_filename, "%s/%s", app_path, filename);
|
||||
|
||||
p = strrchr(sd_filename, '/');
|
||||
if (!p) {
|
||||
gprintf("invalid path: %s\n", sd_filename);
|
||||
goto error;
|
||||
}
|
||||
|
||||
*p = 0;
|
||||
res = mkdir_hier(sd_filename);
|
||||
if (res) {
|
||||
gprintf("mkdir_hier failed: %d\n", res);
|
||||
goto error;
|
||||
}
|
||||
*p = '/';
|
||||
|
||||
fd = open(sd_filename, O_CREAT|O_WRONLY|O_TRUNC);
|
||||
if (fd < 0) {
|
||||
gprintf("error opening file: %d\n", fd);
|
||||
goto error;
|
||||
}
|
||||
|
||||
do {
|
||||
res = unzReadCurrentFile(uf, buf, 8 * 1024);
|
||||
|
||||
if (res < 0) {
|
||||
gprintf("unzReadCurrentFile failed: %d\n", res);
|
||||
break;
|
||||
}
|
||||
|
||||
if (res > 0) {
|
||||
if (res != write(fd, buf, res)) {
|
||||
gprintf("short write");
|
||||
res = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
LWP_MutexLock (*mutex);
|
||||
*progress += res;
|
||||
LWP_MutexUnlock (*mutex);
|
||||
}
|
||||
} while (res > 0);
|
||||
|
||||
close(fd);
|
||||
unzCloseCurrentFile(uf);
|
||||
|
||||
if (res < 0) {
|
||||
gprintf("error extracting file: %d\n", res);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if (i != gi.number_entry - 1) {
|
||||
res = unzGoToNextFile(uf);
|
||||
if (res != UNZ_OK) {
|
||||
gprintf("unzGoToNextFile failed: %d\n", res);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ret = true;
|
||||
|
||||
error:
|
||||
free(buf);
|
||||
|
||||
res = unzClose(uf);
|
||||
if (res)
|
||||
gprintf("unzClose failed: %d\n", res);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static lwpq_t manage_queue;
|
||||
static lwp_t manage_thread;
|
||||
static u8 manage_stack[MANAGE_THREAD_STACKSIZE] ATTRIBUTE_ALIGN (32);
|
||||
|
||||
typedef struct {
|
||||
bool running;
|
||||
|
||||
bool extract;
|
||||
|
||||
// extract
|
||||
u8 *data;
|
||||
u32 data_len;
|
||||
mutex_t mutex;
|
||||
u32 progress;
|
||||
|
||||
// delete
|
||||
const char *dirname;
|
||||
|
||||
bool success;
|
||||
} manage_zip_arg;
|
||||
|
||||
static void *manage_func (void *arg) {
|
||||
manage_zip_arg *ta = (manage_zip_arg *) arg;
|
||||
|
||||
if (ta->extract)
|
||||
ta->success = manage_extract_zip(ta->data, ta->data_len, &ta->mutex, &ta->progress);
|
||||
else
|
||||
ta->success = 0 == rmdir_hier(ta->dirname);
|
||||
|
||||
ta->running = false;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool manage_run(view *sub_view, const char *dirname,
|
||||
u8 *data, u32 data_len, u32 bytes) {
|
||||
s32 res;
|
||||
u32 progress = 0;
|
||||
char caption[MAXPATHLEN];
|
||||
|
||||
view *v;
|
||||
|
||||
manage_zip_arg ta;
|
||||
|
||||
res = LWP_MutexInit(&ta.mutex, false);
|
||||
if (res) {
|
||||
gprintf ("error creating mutex: %ld\n", res);
|
||||
return false;
|
||||
}
|
||||
|
||||
ta.running = true;
|
||||
ta.data = data;
|
||||
ta.data_len = data_len;
|
||||
ta.progress = 0;
|
||||
ta.dirname = dirname;
|
||||
|
||||
ta.extract = data != NULL;
|
||||
|
||||
memset(&manage_stack, 0, MANAGE_THREAD_STACKSIZE);
|
||||
LWP_InitQueue(&manage_queue);
|
||||
res = LWP_CreateThread(&manage_thread, manage_func, &ta, manage_stack,
|
||||
MANAGE_THREAD_STACKSIZE, MANAGE_THREAD_PRIO);
|
||||
|
||||
if (res) {
|
||||
gprintf ("error creating thread: %ld\n", res);
|
||||
LWP_CloseQueue(manage_queue);
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *text;
|
||||
|
||||
if (data) {
|
||||
text = _("Extracting");
|
||||
sprintf(caption, "%s '%s'...", text, dirname);
|
||||
v = dialog_progress(sub_view, caption, bytes);
|
||||
dialog_fade(v, true);
|
||||
} else {
|
||||
v = sub_view;
|
||||
view_show_throbber(true);
|
||||
}
|
||||
|
||||
while (true) {
|
||||
LWP_MutexLock(ta.mutex);
|
||||
if (!ta.running) {
|
||||
LWP_MutexUnlock(ta.mutex);
|
||||
break;
|
||||
}
|
||||
|
||||
progress = ta.progress;
|
||||
|
||||
LWP_MutexUnlock (ta.mutex);
|
||||
|
||||
if (data)
|
||||
dialog_set_progress(v, progress);
|
||||
else
|
||||
view_throbber_tickle();
|
||||
|
||||
view_plot(v, DIALOG_MASK_COLOR, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
if (data) {
|
||||
dialog_fade(v, false);
|
||||
view_free(v);
|
||||
} else {
|
||||
view_show_throbber(false);
|
||||
}
|
||||
|
||||
//LWP_SuspendThread(manage_thread);
|
||||
LWP_CloseQueue(manage_queue);
|
||||
|
||||
LWP_MutexDestroy(ta.mutex);
|
||||
|
||||
return ta.success;
|
||||
}
|
||||
|
||||
|
||||
18
channel/channelapp/source/manage.h
Normal file
18
channel/channelapp/source/manage.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef _MANAGE_H_
|
||||
#define _MANAGE_H_
|
||||
|
||||
#include <gctypes.h>
|
||||
|
||||
#include "view.h"
|
||||
|
||||
s32 dir_exists(char *dirname);
|
||||
|
||||
bool manage_is_zip(const u8 *data);
|
||||
bool manage_check_zip_app(u8 *data, u32 data_len, char *dirname, u32 *bytes);
|
||||
bool manage_check_zip_theme(u8 *data, u32 data_len);
|
||||
|
||||
bool manage_run(view *sub_view, const char *dirname,
|
||||
u8 *data, u32 data_len, u32 bytes);
|
||||
|
||||
#endif
|
||||
|
||||
137
channel/channelapp/source/panic.c
Normal file
137
channel/channelapp/source/panic.c
Normal file
@@ -0,0 +1,137 @@
|
||||
#include <string.h>
|
||||
#include <ogc/machine/processor.h>
|
||||
|
||||
#include "panic.h"
|
||||
|
||||
// steal from libogc
|
||||
extern unsigned char console_font_8x16[];
|
||||
|
||||
#define BLACK 0x00800080
|
||||
#define RED 0x4C544CFF
|
||||
|
||||
static vu32 *fb;
|
||||
|
||||
static void pchar(u32 cx, u32 cy, char c)
|
||||
{
|
||||
int x,y;
|
||||
unsigned char *p = &console_font_8x16[16*c];
|
||||
|
||||
for (y=0; y<16; y++) {
|
||||
char v = *p++;
|
||||
for (x=0; x<4; x++) {
|
||||
u32 c = RED;
|
||||
switch (v&0xc0) {
|
||||
case 0x00:
|
||||
c = BLACK;
|
||||
break;
|
||||
case 0x40:
|
||||
c = (RED & 0x0000FF00) | (BLACK & 0xFFFF00FF);
|
||||
break;
|
||||
case 0x80:
|
||||
c = (RED & 0xFFFF00FF) | (BLACK & 0x0000FF00);
|
||||
break;
|
||||
case 0xc0:
|
||||
c = RED;
|
||||
break;
|
||||
}
|
||||
fb[320*(cy+y)+cx+x] = c;
|
||||
v<<=2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void putsc(u32 y, char *s) {
|
||||
u32 x = (320-strlen(s)*4)/2;
|
||||
while(*s) {
|
||||
pchar(x, y, *s++);
|
||||
x += 4;
|
||||
}
|
||||
}
|
||||
|
||||
static void hex(u32 v, char *p) {
|
||||
int i;
|
||||
for (i=0; i<8; i++) {
|
||||
if ((v>>28) >= 10)
|
||||
*p++ = 'A' + (v>>28) - 10;
|
||||
else
|
||||
*p++ = '0' + (v>>28);
|
||||
v <<= 4;
|
||||
}
|
||||
}
|
||||
|
||||
#define HW_REG_BASE 0xd800000
|
||||
#define HW_RESETS (HW_REG_BASE + 0x194)
|
||||
|
||||
// written to be as generic and reliable as possible
|
||||
void _panic(u32 file, u32 line)
|
||||
{
|
||||
u32 level;
|
||||
u32 fbr;
|
||||
int count = 30;
|
||||
int x,y;
|
||||
u32 bcolor = BLACK;
|
||||
u16 vtr;
|
||||
int lines;
|
||||
char guru[] = "Guru Meditation #00000000.00000000";
|
||||
_CPU_ISR_Disable(level);
|
||||
|
||||
fbr = read32(0xc00201c) & 0x1fffffff;
|
||||
if (fbr&0x10000000)
|
||||
fbr <<= 5;
|
||||
|
||||
fb = (vu32*)(fbr | 0xC0000000);
|
||||
|
||||
vtr = read16(0xc002000);
|
||||
if ((vtr & 0xf) > 10)
|
||||
lines = vtr >> 4;
|
||||
else
|
||||
lines = 2 * (vtr >> 4);
|
||||
|
||||
for(y=(lines-1); y>=116; y--)
|
||||
for(x=0; x<320; x++)
|
||||
fb[y*320+x] = fb[(y-116)*320+x];
|
||||
for(y=0; y<116; y++)
|
||||
for(x=0; x<320; x++)
|
||||
fb[y*320+x] = BLACK;
|
||||
|
||||
hex(file, &guru[17]);
|
||||
hex(line, &guru[26]);
|
||||
|
||||
putsc(42, "Software Failure. Press reset button to continue.");
|
||||
putsc(62, guru);
|
||||
|
||||
// wait for reset button
|
||||
while(read32(0xcc003000)&0x10000) {
|
||||
// blink
|
||||
if (count >= 25) {
|
||||
if (bcolor == RED)
|
||||
bcolor = BLACK;
|
||||
else
|
||||
bcolor = RED;
|
||||
|
||||
for(y=28; y<34; y++)
|
||||
for(x=14; x<306; x++)
|
||||
fb[y*320+x] = bcolor;
|
||||
for(y=34; y<84; y++) {
|
||||
for(x=14; x<17; x++)
|
||||
fb[y*320+x] = bcolor;
|
||||
for(x=303; x<306; x++)
|
||||
fb[y*320+x] = bcolor;
|
||||
}
|
||||
for(y=84; y<90; y++)
|
||||
for(x=14; x<306; x++)
|
||||
fb[y*320+x] = bcolor;
|
||||
|
||||
count = 0;
|
||||
}
|
||||
|
||||
// hacky waitvsync
|
||||
while(read16(0xc00202c) >= 200);
|
||||
while(read16(0xc00202c) < 200);
|
||||
count++;
|
||||
}
|
||||
|
||||
// needs AHBPROT, is evil. so sue me.
|
||||
write32(HW_RESETS, read32(HW_RESETS)&(~1));
|
||||
while(1);
|
||||
}
|
||||
53
channel/channelapp/source/panic.h
Normal file
53
channel/channelapp/source/panic.h
Normal file
@@ -0,0 +1,53 @@
|
||||
#ifndef _PANIC_H_
|
||||
#define _PANIC_H_
|
||||
|
||||
#include <string.h>
|
||||
#include <ogcsys.h>
|
||||
#include <malloc.h>
|
||||
|
||||
// to skip 'source/'
|
||||
#define FOFF 7
|
||||
|
||||
#define S2I(s) (((s)[FOFF+0]<<24)|((s)[FOFF+1]<<16)|((s)[FOFF+2]<<8)|((s)[FOFF+3]))
|
||||
|
||||
#define panic() _panic(S2I(__FILE__)^0xDEADBEEF, __LINE__)
|
||||
void _panic(u32 file, u32 line) __attribute__((noreturn));
|
||||
|
||||
#define pmalloc(s) _pmalloc(s, S2I(__FILE__)^0xDEADBEEF, __LINE__)
|
||||
static inline void *_pmalloc(size_t s, u32 file, u32 line)
|
||||
{
|
||||
void *p = malloc(s);
|
||||
if (!p)
|
||||
_panic(file, line);
|
||||
return p;
|
||||
}
|
||||
|
||||
#define pmemalign(a,s) _pmemalign(a, s, S2I(__FILE__)^0xDEADBEEF, __LINE__)
|
||||
static inline void *_pmemalign(size_t a, size_t s, u32 file, u32 line)
|
||||
{
|
||||
void *p = memalign(a, s);
|
||||
if (!p)
|
||||
_panic(file, line);
|
||||
return p;
|
||||
}
|
||||
|
||||
#define prealloc(p,s) _prealloc(p, s, S2I(__FILE__)^0xDEADBEEF, __LINE__)
|
||||
static inline void *_prealloc(void *p, size_t s, u32 file, u32 line)
|
||||
{
|
||||
p = realloc(p, s);
|
||||
if (!p)
|
||||
_panic(file, line);
|
||||
return p;
|
||||
}
|
||||
|
||||
#define pstrdup(s) _pstrdup(s, S2I(__FILE__)^0xDEADBEEF, __LINE__)
|
||||
static inline void *_pstrdup(const char *s, u32 file, u32 line)
|
||||
{
|
||||
char *p;
|
||||
p = strdup(s);
|
||||
if (!p)
|
||||
_panic(file, line);
|
||||
return p;
|
||||
}
|
||||
|
||||
#endif
|
||||
39
channel/channelapp/source/playtime.c
Normal file
39
channel/channelapp/source/playtime.c
Normal file
@@ -0,0 +1,39 @@
|
||||
#include <gccore.h>
|
||||
#include <ogc/conf.h>
|
||||
#include <ogc/lwp_watchdog.h>
|
||||
#include <time.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../config.h"
|
||||
#include "playtime.h"
|
||||
|
||||
static char _playtime_path[] __attribute__((aligned(32))) =
|
||||
"/title/00000001/00000002/data/play_rec.dat";
|
||||
|
||||
void playtime_destroy(void) {
|
||||
s32 res;
|
||||
s32 pt_fd = -1;
|
||||
static u8 pt_buf[4] __attribute__((aligned(32)));
|
||||
|
||||
gprintf("destroying playtime\n");
|
||||
|
||||
pt_fd = IOS_Open(_playtime_path, IPC_OPEN_RW);
|
||||
if(pt_fd < 0) {
|
||||
gprintf("playtime open failed: %ld\n", pt_fd);
|
||||
return;
|
||||
}
|
||||
|
||||
memset(pt_buf, 0, sizeof(pt_buf));
|
||||
|
||||
res = IOS_Write(pt_fd, &pt_buf, sizeof(pt_buf));
|
||||
if (res != sizeof(pt_buf)) {
|
||||
IOS_Close(pt_fd);
|
||||
gprintf("error destroying playtime (%ld)\n", res);
|
||||
return;
|
||||
}
|
||||
|
||||
IOS_Close(pt_fd);
|
||||
}
|
||||
|
||||
25
channel/channelapp/source/playtime.h
Normal file
25
channel/channelapp/source/playtime.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#ifndef _PLAYTIME_H_
|
||||
#define _PLAYTIME_H_
|
||||
|
||||
#include <gctypes.h>
|
||||
|
||||
typedef union {
|
||||
struct {
|
||||
u32 checksum;
|
||||
u16 name[0x28];
|
||||
u32 _pad1;
|
||||
u64 ticks_boot;
|
||||
u64 ticks_last;
|
||||
u32 title_id;
|
||||
u32 _pad2[5];
|
||||
};
|
||||
struct {
|
||||
u32 _checksum;
|
||||
u32 data[0x1f];
|
||||
};
|
||||
} __attribute__((packed)) playtime_t;
|
||||
|
||||
void playtime_destroy(void);
|
||||
|
||||
#endif
|
||||
|
||||
172
channel/channelapp/source/sha1.c
Normal file
172
channel/channelapp/source/sha1.c
Normal file
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
SHA-1 in C
|
||||
By Steve Reid <steve@edmweb.com>
|
||||
100% Public Domain
|
||||
|
||||
Test Vectors (from FIPS PUB 180-1)
|
||||
"abc"
|
||||
A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
|
||||
"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
|
||||
84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
|
||||
A million repetitions of "a"
|
||||
34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
|
||||
*/
|
||||
|
||||
/* #define LITTLE_ENDIAN * This should be #define'd if true. */
|
||||
#define SHA1HANDSOFF
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "sha1.h"
|
||||
|
||||
#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
|
||||
|
||||
/* blk0() and blk() perform the initial expand. */
|
||||
/* I got the idea of expanding during the round function from SSLeay */
|
||||
#ifdef LITTLE_ENDIAN
|
||||
#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
|
||||
|(rol(block->l[i],8)&0x00FF00FF))
|
||||
#else
|
||||
#define blk0(i) block->l[i]
|
||||
#endif
|
||||
#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
|
||||
^block->l[(i+2)&15]^block->l[i&15],1))
|
||||
|
||||
/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
|
||||
#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
|
||||
#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
|
||||
#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
|
||||
#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
|
||||
#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
|
||||
|
||||
|
||||
/* Hash a single 512-bit block. This is the core of the algorithm. */
|
||||
|
||||
void SHA1Transform(unsigned long state[5], const unsigned char buffer[64])
|
||||
{
|
||||
unsigned long a, b, c, d, e;
|
||||
typedef union {
|
||||
unsigned char c[64];
|
||||
unsigned long l[16];
|
||||
} CHAR64LONG16;
|
||||
CHAR64LONG16* block;
|
||||
#ifdef SHA1HANDSOFF
|
||||
static unsigned char workspace[64];
|
||||
block = (CHAR64LONG16*)workspace;
|
||||
memcpy(block, buffer, 64);
|
||||
#else
|
||||
block = (CHAR64LONG16*)buffer;
|
||||
#endif
|
||||
/* Copy context->state[] to working vars */
|
||||
a = state[0];
|
||||
b = state[1];
|
||||
c = state[2];
|
||||
d = state[3];
|
||||
e = state[4];
|
||||
/* 4 rounds of 20 operations each. Loop unrolled. */
|
||||
R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
|
||||
R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
|
||||
R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
|
||||
R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
|
||||
R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
|
||||
R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
|
||||
R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
|
||||
R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
|
||||
R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
|
||||
R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
|
||||
R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
|
||||
R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
|
||||
R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
|
||||
R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
|
||||
R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
|
||||
R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
|
||||
R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
|
||||
R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
|
||||
R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
|
||||
R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
|
||||
/* Add the working vars back into context.state[] */
|
||||
state[0] += a;
|
||||
state[1] += b;
|
||||
state[2] += c;
|
||||
state[3] += d;
|
||||
state[4] += e;
|
||||
/* Wipe variables */
|
||||
a = b = c = d = e = 0;
|
||||
}
|
||||
|
||||
|
||||
/* SHA1Init - Initialize new context */
|
||||
|
||||
void SHA1Init(SHA1_CTX* context)
|
||||
{
|
||||
/* SHA1 initialization constants */
|
||||
context->state[0] = 0x67452301;
|
||||
context->state[1] = 0xEFCDAB89;
|
||||
context->state[2] = 0x98BADCFE;
|
||||
context->state[3] = 0x10325476;
|
||||
context->state[4] = 0xC3D2E1F0;
|
||||
context->count[0] = context->count[1] = 0;
|
||||
}
|
||||
|
||||
|
||||
/* Run your data through this. */
|
||||
|
||||
void SHA1Update(SHA1_CTX* context, const unsigned char* data, unsigned int len)
|
||||
{
|
||||
unsigned int i, j;
|
||||
|
||||
j = (context->count[0] >> 3) & 63;
|
||||
if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++;
|
||||
context->count[1] += (len >> 29);
|
||||
if ((j + len) > 63) {
|
||||
memcpy(&context->buffer[j], data, (i = 64-j));
|
||||
SHA1Transform(context->state, context->buffer);
|
||||
for ( ; i + 63 < len; i += 64) {
|
||||
SHA1Transform(context->state, &data[i]);
|
||||
}
|
||||
j = 0;
|
||||
}
|
||||
else i = 0;
|
||||
memcpy(&context->buffer[j], &data[i], len - i);
|
||||
}
|
||||
|
||||
|
||||
/* Add padding and return the message digest. */
|
||||
|
||||
void SHA1Final(unsigned char digest[20], SHA1_CTX* context)
|
||||
{
|
||||
unsigned long i, j;
|
||||
unsigned char finalcount[8];
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
|
||||
>> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */
|
||||
}
|
||||
SHA1Update(context, (unsigned char *)"\200", 1);
|
||||
while ((context->count[0] & 504) != 448) {
|
||||
SHA1Update(context, (unsigned char *)"\0", 1);
|
||||
}
|
||||
SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */
|
||||
for (i = 0; i < 20; i++) {
|
||||
digest[i] = (unsigned char)
|
||||
((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
|
||||
}
|
||||
/* Wipe variables */
|
||||
i = j = 0;
|
||||
memset(context->buffer, 0, 64);
|
||||
memset(context->state, 0, 20);
|
||||
memset(context->count, 0, 8);
|
||||
memset(&finalcount, 0, 8);
|
||||
#ifdef SHA1HANDSOFF /* make SHA1Transform overwrite it's own static vars */
|
||||
SHA1Transform(context->state, context->buffer);
|
||||
#endif
|
||||
}
|
||||
|
||||
void SHA1(unsigned char *ptr, unsigned int size, unsigned char *outbuf) {
|
||||
SHA1_CTX ctx;
|
||||
|
||||
SHA1Init(&ctx);
|
||||
SHA1Update(&ctx, ptr, size);
|
||||
SHA1Final(outbuf, &ctx);
|
||||
}
|
||||
|
||||
18
channel/channelapp/source/sha1.h
Normal file
18
channel/channelapp/source/sha1.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef __SHA1_H__
|
||||
#define __SHA1_H__
|
||||
|
||||
typedef struct {
|
||||
unsigned long state[5];
|
||||
unsigned long count[2];
|
||||
unsigned char buffer[64];
|
||||
} SHA1_CTX;
|
||||
|
||||
void SHA1Transform(unsigned long state[5], const unsigned char buffer[64]);
|
||||
void SHA1Init(SHA1_CTX* context);
|
||||
void SHA1Update(SHA1_CTX* context, const unsigned char* data, unsigned int len);
|
||||
void SHA1Final(unsigned char digest[20], SHA1_CTX* context);
|
||||
|
||||
void SHA1(unsigned char *ptr, unsigned int size, unsigned char *outbuf);
|
||||
|
||||
#endif
|
||||
|
||||
297
channel/channelapp/source/tcp.c
Normal file
297
channel/channelapp/source/tcp.c
Normal file
@@ -0,0 +1,297 @@
|
||||
#include <sys/types.h>
|
||||
#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/mutex.h>
|
||||
#include <ogc/lwp_watchdog.h>
|
||||
|
||||
#include "../config.h"
|
||||
#include "panic.h"
|
||||
|
||||
#include "tcp.h"
|
||||
|
||||
s32 tcp_socket (void) {
|
||||
s32 s, res;
|
||||
u32 val;
|
||||
|
||||
s = net_socket (PF_INET, SOCK_STREAM, 0);
|
||||
if (s < 0) {
|
||||
gprintf ("net_socket failed: %ld\n", s);
|
||||
return s;
|
||||
}
|
||||
|
||||
val = 1;
|
||||
net_setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val));
|
||||
|
||||
res = net_fcntl (s, F_GETFL, 0);
|
||||
if (res < 0) {
|
||||
gprintf ("F_GETFL failed: %ld\n", res);
|
||||
net_close (s);
|
||||
return res;
|
||||
}
|
||||
|
||||
res = net_fcntl (s, F_SETFL, res | 4);
|
||||
if (res < 0) {
|
||||
gprintf ("F_SETFL failed: %ld\n", res);
|
||||
net_close (s);
|
||||
return res;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
s32 tcp_connect (char *host, u16 port) {
|
||||
struct hostent *hp;
|
||||
struct sockaddr_in sa;
|
||||
s32 s, res;
|
||||
s64 t;
|
||||
|
||||
hp = net_gethostbyname (host);
|
||||
if (!hp || !(hp->h_addrtype == PF_INET)) {
|
||||
gprintf ("net_gethostbyname failed: %d\n", errno);
|
||||
return errno;
|
||||
}
|
||||
|
||||
s = tcp_socket ();
|
||||
if (s < 0)
|
||||
return s;
|
||||
|
||||
memset (&sa, 0, sizeof (struct sockaddr_in));
|
||||
sa.sin_family= PF_INET;
|
||||
sa.sin_len = sizeof (struct sockaddr_in);
|
||||
sa.sin_port= htons (port);
|
||||
memcpy ((char *) &sa.sin_addr, hp->h_addr_list[0], hp->h_length);
|
||||
|
||||
t = gettime ();
|
||||
while (true) {
|
||||
if (ticks_to_millisecs (diff_ticks (t, gettime ())) >
|
||||
TCP_CONNECT_TIMEOUT) {
|
||||
gprintf ("tcp_connect timeout\n");
|
||||
net_close (s);
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
res = net_connect (s, (struct sockaddr *) &sa,
|
||||
sizeof (struct sockaddr_in));
|
||||
|
||||
if (res < 0) {
|
||||
if (res == -EISCONN)
|
||||
break;
|
||||
|
||||
if (res == -EINPROGRESS || res == -EALREADY) {
|
||||
usleep (20 * 1000);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
gprintf ("net_connect failed: %ld\n", res);
|
||||
net_close (s);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
s32 tcp_listen (u16 port, s32 backlog) {
|
||||
s32 s, res;
|
||||
struct sockaddr_in sa;
|
||||
|
||||
s = tcp_socket ();
|
||||
if (s < 0)
|
||||
return s;
|
||||
|
||||
memset(&sa, 0, sizeof (struct sockaddr_in));
|
||||
sa.sin_family = PF_INET;
|
||||
sa.sin_port = htons (port);
|
||||
sa.sin_addr.s_addr = net_gethostip ();
|
||||
sa.sin_len = sizeof (struct sockaddr_in);
|
||||
|
||||
res = net_bind (s, (struct sockaddr *) &sa, sizeof (struct sockaddr_in));
|
||||
if (res < 0) {
|
||||
gprintf ("net_bind failed: %ld\n", res);
|
||||
net_close (s);
|
||||
return res;
|
||||
}
|
||||
|
||||
res = net_listen (s, backlog);
|
||||
if (res < 0) {
|
||||
gprintf ("net_listen failed: %ld\n", res);
|
||||
net_close (s);
|
||||
return res;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
char * tcp_readln (s32 s, u16 max_length, s64 start_time, u16 timeout) {
|
||||
char *buf;
|
||||
u16 c;
|
||||
s32 res;
|
||||
char *ret;
|
||||
|
||||
buf = (char *) pmalloc (max_length);
|
||||
|
||||
c = 0;
|
||||
ret = NULL;
|
||||
while (true) {
|
||||
if (ticks_to_millisecs (diff_ticks (start_time, gettime ())) > timeout)
|
||||
break;
|
||||
|
||||
res = net_read (s, &buf[c], 1);
|
||||
|
||||
if ((res == 0) || (res == -EAGAIN)) {
|
||||
usleep (20 * 1000);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (res < 0) {
|
||||
gprintf ("tcp_readln failed: %ld\n", res);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if ((c > 0) && (buf[c - 1] == '\r') && (buf[c] == '\n')) {
|
||||
if (c == 1) {
|
||||
ret = pstrdup ("");
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
ret = strndup (buf, c - 1);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
c++;
|
||||
|
||||
if (c == max_length)
|
||||
break;
|
||||
}
|
||||
|
||||
free (buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool tcp_read (s32 s, u8 *buffer, u32 length, const mutex_t *mutex, u32 *progress) {
|
||||
u32 step, left, block, received;
|
||||
s64 t;
|
||||
s32 res;
|
||||
|
||||
step = 0;
|
||||
left = length;
|
||||
received = 0;
|
||||
|
||||
t = gettime ();
|
||||
while (left) {
|
||||
if (ticks_to_millisecs (diff_ticks (t, gettime ())) >
|
||||
TCP_BLOCK_RECV_TIMEOUT) {
|
||||
gprintf ("tcp_read timeout\n");
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
block = left;
|
||||
if (block > 2048)
|
||||
block = 2048;
|
||||
|
||||
res = net_read (s, buffer, block);
|
||||
|
||||
if ((res == 0) || (res == -EAGAIN)) {
|
||||
usleep (20 * 1000);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (res < 0) {
|
||||
gprintf ("net_read failed: %ld\n", res);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
received += res;
|
||||
left -= res;
|
||||
buffer += res;
|
||||
|
||||
if ((received / TCP_BLOCK_SIZE) > step) {
|
||||
t = gettime ();
|
||||
step++;
|
||||
}
|
||||
|
||||
if (mutex && progress) {
|
||||
LWP_MutexLock (*mutex);
|
||||
*progress = received;
|
||||
LWP_MutexUnlock (*mutex);
|
||||
}
|
||||
}
|
||||
|
||||
return left == 0;
|
||||
}
|
||||
|
||||
bool tcp_write (s32 s, const u8 *buffer, u32 length, const mutex_t *mutex,
|
||||
u32 *progress) {
|
||||
const u8 *p;
|
||||
u32 step, left, block, sent;
|
||||
s64 t;
|
||||
s32 res;
|
||||
|
||||
step = 0;
|
||||
p = buffer;
|
||||
left = length;
|
||||
sent = 0;
|
||||
|
||||
t = gettime ();
|
||||
while (left) {
|
||||
if (ticks_to_millisecs (diff_ticks (t, gettime ())) >
|
||||
TCP_BLOCK_SEND_TIMEOUT) {
|
||||
|
||||
gprintf ("tcp_write timeout\n");
|
||||
break;
|
||||
}
|
||||
|
||||
block = left;
|
||||
if (block > 2048)
|
||||
block = 2048;
|
||||
|
||||
res = net_write (s, p, block);
|
||||
|
||||
if ((res == 0) || (res == -EAGAIN)) {
|
||||
usleep (20 * 1000);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (res < 0) {
|
||||
gprintf ("net_write failed: %ld\n", res);
|
||||
break;
|
||||
}
|
||||
|
||||
sent += res;
|
||||
left -= res;
|
||||
p += res;
|
||||
|
||||
if ((sent / TCP_BLOCK_SIZE) > step) {
|
||||
t = gettime ();
|
||||
step++;
|
||||
}
|
||||
|
||||
if (mutex && progress) {
|
||||
LWP_MutexLock (*mutex);
|
||||
*progress = sent;
|
||||
LWP_MutexUnlock (*mutex);
|
||||
}
|
||||
}
|
||||
|
||||
return left == 0;
|
||||
}
|
||||
|
||||
17
channel/channelapp/source/tcp.h
Normal file
17
channel/channelapp/source/tcp.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifndef _TCP_H_
|
||||
#define _TCP_H_
|
||||
|
||||
#include <gctypes.h>
|
||||
#include <ogc/mutex.h>
|
||||
|
||||
s32 tcp_socket (void);
|
||||
s32 tcp_connect (char *host, u16 port);
|
||||
s32 tcp_listen (u16 port, s32 backlog);
|
||||
|
||||
char * tcp_readln (s32 s, u16 max_length, s64 start_time, u16 timeout);
|
||||
bool tcp_read (s32 s, u8 *buffer, u32 length, const mutex_t *mutex, u32 *progress);
|
||||
bool tcp_write (s32 s, const u8 *buffer, u32 length, const mutex_t *mutex,
|
||||
u32 *progress);
|
||||
|
||||
#endif
|
||||
|
||||
262
channel/channelapp/source/tex.c
Normal file
262
channel/channelapp/source/tex.c
Normal file
@@ -0,0 +1,262 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <malloc.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <zlib.h>
|
||||
#include <png.h>
|
||||
|
||||
#include "../config.h"
|
||||
#include "tex.h"
|
||||
#include "panic.h"
|
||||
|
||||
#define SCREENSHOT_FILENAME "/hbc-%03lu.png"
|
||||
|
||||
static u32 screenshot_index = 0;
|
||||
|
||||
void tex_free (gfx_entity *entity) {
|
||||
if (!entity)
|
||||
return;
|
||||
|
||||
free (entity->texture.pixels);
|
||||
free (entity);
|
||||
}
|
||||
|
||||
static void pngcb_error (png_structp png_ptr, png_const_charp error_msg) {
|
||||
gprintf ("ERROR: Couldn't load PNG image: %s\n", error_msg);
|
||||
}
|
||||
|
||||
static void pngcb_read (png_structp png_ptr, png_bytep data,
|
||||
png_size_t length) {
|
||||
u8 **p = (u8 **) png_get_io_ptr (png_ptr);
|
||||
memcpy (data, *p, length);
|
||||
*p += length;
|
||||
}
|
||||
|
||||
gfx_entity * tex_from_png(const u8 *data, u32 size, u16 width, u16 height) {
|
||||
int res;
|
||||
png_structp png_ptr;
|
||||
png_infop info_ptr;
|
||||
png_uint_32 w, h;
|
||||
png_bytepp rows;
|
||||
|
||||
u8 *pixels;
|
||||
u8 *s, *d;
|
||||
u32 x, y;
|
||||
u8 r;
|
||||
|
||||
gfx_entity *entity;
|
||||
|
||||
res = png_sig_cmp ((u8 *) data, 0, 4);
|
||||
if (res) {
|
||||
gprintf ("png_sig_cmp failed: %d\n", res);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, pngcb_error,
|
||||
NULL);
|
||||
|
||||
if (!png_ptr) {
|
||||
gprintf ("png_create_read_struct failed\n");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
info_ptr = png_create_info_struct (png_ptr);
|
||||
if (!info_ptr) {
|
||||
gprintf ("png_create_info_struct failed\n");
|
||||
png_destroy_read_struct (&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (setjmp (png_jmpbuf (png_ptr))) {
|
||||
gprintf ("setjmp failed\n");
|
||||
png_destroy_read_struct (&png_ptr, &info_ptr, (png_infopp)NULL);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
png_set_read_fn (png_ptr, &data, pngcb_read);
|
||||
png_set_user_limits (png_ptr, width, height);
|
||||
png_set_add_alpha (png_ptr, 0xff, PNG_FILLER_BEFORE);
|
||||
png_read_png (png_ptr, info_ptr, PNG_TRANSFORM_PACKING |
|
||||
PNG_TRANSFORM_EXPAND | PNG_TRANSFORM_SWAP_ALPHA,
|
||||
(png_voidp)NULL);
|
||||
|
||||
w = png_get_image_width (png_ptr, info_ptr);
|
||||
h = png_get_image_height (png_ptr, info_ptr);
|
||||
|
||||
if ((w != width) || (h != height)) {
|
||||
gprintf ("invalid png dimension!\n");
|
||||
png_destroy_read_struct (&png_ptr, &info_ptr, (png_infopp)NULL);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rows = png_get_rows (png_ptr, info_ptr);
|
||||
|
||||
pixels = (u8 *) pmemalign (32, w * h * 4);
|
||||
d = pixels;
|
||||
|
||||
for (y = 0; y < h; y += 4) {
|
||||
for (x = 0; x < w; x += 4) {
|
||||
for (r = 0; r < 4; ++r) {
|
||||
s = &rows[y + r][x << 2];
|
||||
|
||||
*d++ = s[0];
|
||||
*d++ = s[1];
|
||||
*d++ = s[4];
|
||||
*d++ = s[5];
|
||||
*d++ = s[8];
|
||||
*d++ = s[9];
|
||||
*d++ = s[12];
|
||||
*d++ = s[13];
|
||||
}
|
||||
|
||||
for (r = 0; r < 4; ++r) {
|
||||
s = &rows[y + r][x << 2];
|
||||
|
||||
*d++ = s[2];
|
||||
*d++ = s[3];
|
||||
*d++ = s[6];
|
||||
*d++ = s[7];
|
||||
*d++ = s[10];
|
||||
*d++ = s[11];
|
||||
*d++ = s[14];
|
||||
*d++ = s[15];
|
||||
}
|
||||
|
||||
CHKBUFACC(d - 1, pixels, w * h * 4);
|
||||
}
|
||||
}
|
||||
|
||||
png_destroy_read_struct (&png_ptr, &info_ptr, (png_infopp)NULL);
|
||||
|
||||
entity = (gfx_entity *) pmalloc (sizeof (gfx_entity));
|
||||
gfx_gen_tex (entity, w, h, pixels, GFXT_RGBA8);
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
gfx_entity * tex_from_png_file(const char *fn, const struct stat *st,
|
||||
u16 width, u16 height) {
|
||||
u8 *buf;
|
||||
int fd;
|
||||
gfx_entity *entity;
|
||||
|
||||
fd = open (fn, O_RDONLY);
|
||||
|
||||
if (fd == -1)
|
||||
return NULL;
|
||||
|
||||
buf = (u8 *) pmalloc (st->st_size);
|
||||
|
||||
if (st->st_size != read (fd, buf, st->st_size)) {
|
||||
free (buf);
|
||||
close (fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
entity = tex_from_png (buf, st->st_size, width, height);
|
||||
|
||||
close (fd);
|
||||
free (buf);
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
gfx_entity * tex_from_tex_vsplit(gfx_entity *ge, u16 hstart, u16 hend)
|
||||
{
|
||||
gfx_entity *entity;
|
||||
u16 h = hend - hstart;
|
||||
|
||||
entity = (gfx_entity *) pmalloc (sizeof (gfx_entity));
|
||||
gfx_gen_tex (entity, ge->w, h, ge->texture.pixels + ge->w * hstart * 4, GFXT_RGBA8);
|
||||
return entity;
|
||||
}
|
||||
|
||||
void save_rgba_png(u32 *buffer, u16 x, u16 y) {
|
||||
char fn[16];
|
||||
struct stat st;
|
||||
FILE *fp = NULL;
|
||||
png_structp png_ptr = NULL;
|
||||
png_infop info_ptr;
|
||||
png_bytep *row_pointers;
|
||||
u16 i;
|
||||
|
||||
while (true) {
|
||||
if (screenshot_index > 999)
|
||||
return;
|
||||
|
||||
sprintf(fn, SCREENSHOT_FILENAME, screenshot_index);
|
||||
if (!stat(fn, &st)) {
|
||||
screenshot_index++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (errno == ENOENT)
|
||||
break;
|
||||
|
||||
gprintf("error looking for a screenshot spot %d\n", errno);
|
||||
return;
|
||||
}
|
||||
|
||||
row_pointers = (png_bytep *) pmalloc(y * sizeof(png_bytep));
|
||||
|
||||
fp = fopen(fn, "wb");
|
||||
if (!fp) {
|
||||
gprintf("couldnt open %s for writing\n", fn);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
setbuf(fp, NULL);
|
||||
|
||||
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
|
||||
if (!png_ptr) {
|
||||
gprintf ("png_create_write_struct failed\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
info_ptr = png_create_info_struct(png_ptr);
|
||||
if (!info_ptr) {
|
||||
gprintf ("png_create_info_struct failed\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (setjmp(png_jmpbuf(png_ptr))) {
|
||||
gprintf ("setjmp failed\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
png_init_io(png_ptr, fp);
|
||||
|
||||
png_set_compression_level(png_ptr, Z_BEST_COMPRESSION);
|
||||
png_set_IHDR(png_ptr, info_ptr, x, y, 8,
|
||||
PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE,
|
||||
PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
|
||||
|
||||
png_write_info(png_ptr, info_ptr);
|
||||
|
||||
for (i = 0; i < y; ++i)
|
||||
row_pointers[i] = (png_bytep) (buffer + i * x);
|
||||
|
||||
png_set_swap_alpha(png_ptr);
|
||||
|
||||
png_write_image(png_ptr, row_pointers);
|
||||
png_write_end(png_ptr, info_ptr);
|
||||
|
||||
gprintf("saved %s\n", fn);
|
||||
screenshot_index++;
|
||||
|
||||
exit:
|
||||
if (png_ptr)
|
||||
png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
|
||||
|
||||
free(row_pointers);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
19
channel/channelapp/source/tex.h
Normal file
19
channel/channelapp/source/tex.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#ifndef _TEX_H_
|
||||
#define _TEX_H_
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <gctypes.h>
|
||||
|
||||
#include "gfx.h"
|
||||
|
||||
void tex_free (gfx_entity *entity);
|
||||
|
||||
gfx_entity * tex_from_png(const u8 *data, u32 size, u16 width, u16 height);
|
||||
gfx_entity * tex_from_png_file(const char *fn, const struct stat *st,
|
||||
u16 width, u16 height);
|
||||
gfx_entity * tex_from_tex_vsplit (gfx_entity *ge, u16 hstart, u16 hend);
|
||||
|
||||
void save_rgba_png(u32 *buffer, u16 x, u16 y);
|
||||
|
||||
#endif
|
||||
|
||||
474
channel/channelapp/source/theme.c
Normal file
474
channel/channelapp/source/theme.c
Normal file
@@ -0,0 +1,474 @@
|
||||
#include <malloc.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include <ogcsys.h>
|
||||
#include <ogc/machine/processor.h>
|
||||
|
||||
#include "../config.h"
|
||||
#include "tex.h"
|
||||
#include "unzip.h"
|
||||
#include "title.h"
|
||||
#include "appentry.h"
|
||||
#include "isfs.h"
|
||||
#include "blob.h"
|
||||
#include "panic.h"
|
||||
|
||||
#include "theme.h"
|
||||
|
||||
// view.c
|
||||
#include "background_png.h"
|
||||
#include "background_wide_png.h"
|
||||
#include "logo_png.h"
|
||||
// bubbles.c
|
||||
#include "bubble1_png.h"
|
||||
#include "bubble2_png.h"
|
||||
#include "bubble3_png.h"
|
||||
// browser.c
|
||||
#include "apps_previous_png.h"
|
||||
#include "apps_previous_hover_png.h"
|
||||
#include "apps_next_png.h"
|
||||
#include "apps_next_hover_png.h"
|
||||
#include "icon_usbgecko_png.h"
|
||||
#include "icon_usbgecko_active_png.h"
|
||||
#include "icon_network_png.h"
|
||||
#include "icon_network_active_png.h"
|
||||
#include "throbber_png.h"
|
||||
// dialogs.c
|
||||
#include "about_png.h"
|
||||
#include "dialog_background_png.h"
|
||||
#include "dlg_info_png.h"
|
||||
#include "dlg_confirm_png.h"
|
||||
#include "dlg_warning_png.h"
|
||||
#include "dlg_error_png.h"
|
||||
// widgets.c
|
||||
#include "button_png.h"
|
||||
#include "button_focus_png.h"
|
||||
#include "button_small_png.h"
|
||||
#include "button_small_focus_png.h"
|
||||
#include "button_tiny_png.h"
|
||||
#include "button_tiny_focus_png.h"
|
||||
#include "apps_list_png.h"
|
||||
#include "apps_list_hover_png.h"
|
||||
#include "apps_grid_png.h"
|
||||
#include "apps_grid_hover_png.h"
|
||||
#include "progress_png.h"
|
||||
#include "content_arrow_up_png.h"
|
||||
#include "content_arrow_down_png.h"
|
||||
|
||||
#define _ENTRY(n, w, h, fn) \
|
||||
{ \
|
||||
n##_png, &n##_png_size, w, h, \
|
||||
NULL, NULL, 0, \
|
||||
fn \
|
||||
}
|
||||
|
||||
#define _ENTRY_WS(n, w, h, w_ws, fn) \
|
||||
{ \
|
||||
n##_png, &n##_png_size, w, h, \
|
||||
n##_wide_png, &n##_wide_png_size, w_ws, \
|
||||
fn \
|
||||
}
|
||||
|
||||
#define ENTRY(n, w, h) _ENTRY(n, w, h, #n)
|
||||
#define ENTRY_RO(n, w, h) _ENTRY(n, w, h, NULL)
|
||||
#define ENTRY_WS(n, w, h, w_ws) _ENTRY_WS(n, w, h, w_ws, #n)
|
||||
#define ENTRY_WS_RO(n, w, h, w_ws) _ENTRY_WS(n, w, h, w_ws, NULL)
|
||||
|
||||
gfx_entity *theme_gfx[THEME_LAST];
|
||||
|
||||
theme_font theme_fonts[FONT_MAX];
|
||||
|
||||
const char *theme_fn_xml = "theme.xml";
|
||||
|
||||
static const struct {
|
||||
const u8 *data;
|
||||
const u32 *size;
|
||||
const u16 width;
|
||||
const u16 height;
|
||||
|
||||
const u8 *data_ws;
|
||||
const u32 *size_ws;
|
||||
const u16 width_ws;
|
||||
|
||||
const char *filename;
|
||||
} theme_data[THEME_LAST] = {
|
||||
ENTRY_WS(background, 640, 480, 852),
|
||||
ENTRY_RO(logo, 320, 64),
|
||||
|
||||
ENTRY(bubble1, 64, 64),
|
||||
ENTRY(bubble2, 64, 64),
|
||||
ENTRY(bubble3, 64, 64),
|
||||
|
||||
ENTRY(apps_previous, 64, 64),
|
||||
ENTRY(apps_previous_hover, 64, 64),
|
||||
ENTRY(apps_next, 64, 64),
|
||||
ENTRY(apps_next_hover, 64, 64),
|
||||
ENTRY(icon_usbgecko, 32, 32),
|
||||
ENTRY(icon_usbgecko_active, 32, 32),
|
||||
ENTRY(icon_network, 32, 32),
|
||||
ENTRY(icon_network_active, 32, 32),
|
||||
ENTRY(throbber, 64, 64),
|
||||
|
||||
ENTRY_RO(about, 236, 104),
|
||||
ENTRY(dialog_background, 520, 360),
|
||||
ENTRY(dlg_info, 32, 32),
|
||||
ENTRY(dlg_confirm, 32, 32),
|
||||
ENTRY(dlg_warning, 32, 32),
|
||||
ENTRY(dlg_error, 32, 32),
|
||||
|
||||
ENTRY(button, 340, 48),
|
||||
ENTRY(button_focus, 340, 48),
|
||||
ENTRY(button_small, 200, 48),
|
||||
ENTRY(button_small_focus, 200, 48),
|
||||
ENTRY(button_tiny, 132, 48),
|
||||
ENTRY(button_tiny_focus, 132, 48),
|
||||
ENTRY(apps_list, 432, 64),
|
||||
ENTRY(apps_list_hover, 432, 64),
|
||||
ENTRY(apps_grid, 144, 64),
|
||||
ENTRY(apps_grid_hover, 144, 64),
|
||||
ENTRY(progress, 400, 112),
|
||||
ENTRY(content_arrow_up, 32, 8),
|
||||
ENTRY(content_arrow_down, 32, 8)
|
||||
};
|
||||
|
||||
static bool theme_get_index(u32 *index, bool *ws, const char *filename) {
|
||||
u32 i;
|
||||
char buf[64];
|
||||
|
||||
for (i = 0; i < THEME_LAST; ++i) {
|
||||
if (theme_data[i].filename) {
|
||||
strcpy(buf, theme_data[i].filename);
|
||||
strcat(buf, ".png");
|
||||
|
||||
if (!strcasecmp(buf, filename)) {
|
||||
*index = i;
|
||||
*ws = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (theme_data[i].data_ws) {
|
||||
strcpy(buf, theme_data[i].filename);
|
||||
strcat(buf, "_wide.png");
|
||||
|
||||
if (!strcasecmp(buf, filename)) {
|
||||
*index = i;
|
||||
*ws = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void theme_load_fonts(unzFile uf) {
|
||||
int i,j,res;
|
||||
for (i=0; i<FONT_MAX; i++) {
|
||||
if (theme.fonts[i].color != NO_COLOR)
|
||||
theme_fonts[i].color = theme.fonts[i].color;
|
||||
else if (theme.default_font.color != NO_COLOR)
|
||||
theme_fonts[i].color = theme.default_font.color;
|
||||
|
||||
if (theme.fonts[i].size)
|
||||
theme_fonts[i].size = theme.fonts[i].size;
|
||||
else if (theme.default_font.size)
|
||||
theme_fonts[i].size = theme.default_font.size;
|
||||
|
||||
const char *file = NULL;
|
||||
if (theme.fonts[i].file)
|
||||
file = theme.fonts[i].file;
|
||||
else if (theme.default_font.file)
|
||||
file = theme.default_font.file;
|
||||
|
||||
if (file) {
|
||||
// maybe we've already loaded this
|
||||
for (j=0; j<FONT_MAX; j++) {
|
||||
if (theme_fonts[j].file && !strcasecmp(theme_fonts[j].file,file) && theme_fonts[j].data) {
|
||||
theme_fonts[i].data = theme_fonts[j].data;
|
||||
theme_fonts[i].data_len = theme_fonts[j].data_len;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!theme_fonts[i].data) {
|
||||
unz_file_info fi;
|
||||
gprintf("Loading font file %s\n", file);
|
||||
|
||||
res = unzLocateFile(uf, file, 2);
|
||||
if (res != UNZ_OK) {
|
||||
gprintf("Could not locate font file %s\n", file);
|
||||
continue;
|
||||
}
|
||||
|
||||
res = unzGetCurrentFileInfo(uf, &fi, NULL, 0, NULL, 0, NULL, 0);
|
||||
if (res != UNZ_OK) {
|
||||
gprintf("unzGetCurrentFileInfo failed: %d\n", res);
|
||||
continue;
|
||||
}
|
||||
if (fi.uncompressed_size == 0) {
|
||||
gprintf("Font file is empty\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
void *buf;
|
||||
res = unzOpenCurrentFile(uf);
|
||||
if (res != UNZ_OK) {
|
||||
gprintf("unzOpenCurrentFile failed: %d\n", res);
|
||||
continue;
|
||||
}
|
||||
|
||||
buf = (u8 *) pmalloc(fi.uncompressed_size);
|
||||
|
||||
res = unzReadCurrentFile(uf, buf, fi.uncompressed_size);
|
||||
if (res < 0) {
|
||||
gprintf("unzReadCurrentFile failed: %d\n", res);
|
||||
unzCloseCurrentFile(uf);
|
||||
free(buf);
|
||||
continue;
|
||||
}
|
||||
unzCloseCurrentFile(uf);
|
||||
|
||||
theme_fonts[i].file = file;
|
||||
theme_fonts[i].data = buf;
|
||||
theme_fonts[i].data_len = fi.uncompressed_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void theme_load(u8 *data, u32 data_len) {
|
||||
unzFile uf;
|
||||
int res, i;
|
||||
unz_global_info gi;
|
||||
u8 *buf = NULL;
|
||||
|
||||
uf = unzOpen(data, data_len);
|
||||
if (!uf) {
|
||||
gprintf("unzOpen failed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
res = unzGetGlobalInfo (uf, &gi);
|
||||
if (res != UNZ_OK) {
|
||||
gprintf("unzGetGlobalInfo failed: %d\n", res);
|
||||
unzClose(uf);
|
||||
return;
|
||||
}
|
||||
|
||||
unz_file_info fi;
|
||||
char filename[256];
|
||||
u32 index;
|
||||
bool ws;
|
||||
|
||||
for (i = 0; i < gi.number_entry; ++i) {
|
||||
res = unzGetCurrentFileInfo(uf, &fi, filename, sizeof(filename),
|
||||
NULL, 0, NULL, 0);
|
||||
if (res != UNZ_OK) {
|
||||
gprintf("unzGetCurrentFileInfo failed: %d\n", res);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if ((fi.uncompressed_size > 0) &&
|
||||
((!strcasecmp(filename, theme_fn_xml)) ||
|
||||
((theme_get_index(&index, &ws, filename)) &&
|
||||
(!theme_data[index].data_ws || (widescreen == ws))))) {
|
||||
res = unzOpenCurrentFile(uf);
|
||||
if (res != UNZ_OK) {
|
||||
gprintf("unzOpenCurrentFile failed: %d\n", res);
|
||||
goto error;
|
||||
}
|
||||
|
||||
buf = (u8 *) pmalloc(fi.uncompressed_size + 1);
|
||||
|
||||
res = unzReadCurrentFile(uf, buf, fi.uncompressed_size);
|
||||
if (res < 0) {
|
||||
gprintf("unzReadCurrentFile failed: %d\n", res);
|
||||
unzCloseCurrentFile(uf);
|
||||
free(buf);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!strcasecmp(filename, theme_fn_xml)) {
|
||||
gprintf("parsing theme.xml\n");
|
||||
buf[fi.uncompressed_size] = 0;
|
||||
load_theme_xml((char *) buf);
|
||||
} else {
|
||||
u16 w, h;
|
||||
|
||||
gprintf("loading from theme: %s (%lu @%lu)\n", filename,
|
||||
(u32) fi.uncompressed_size, index);
|
||||
|
||||
if (ws) {
|
||||
w = theme_data[index].width_ws;
|
||||
h = theme_data[index].height;
|
||||
} else {
|
||||
w = theme_data[index].width;
|
||||
h = theme_data[index].height;
|
||||
}
|
||||
|
||||
theme_gfx[index] = tex_from_png(buf, fi.uncompressed_size, w, h);
|
||||
}
|
||||
|
||||
free(buf);
|
||||
}
|
||||
|
||||
if (i != gi.number_entry - 1) {
|
||||
res = unzGoToNextFile(uf);
|
||||
if (res != UNZ_OK) {
|
||||
gprintf("unzGoToNextFile failed: %d\n", res);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
theme_load_fonts(uf);
|
||||
|
||||
error:
|
||||
res = unzClose(uf);
|
||||
if (res)
|
||||
gprintf("unzClose failed: %d\n", res);
|
||||
}
|
||||
|
||||
void theme_init(u8 *data, u32 data_len) {
|
||||
s32 res;
|
||||
u32 i;
|
||||
const char *titlepath;
|
||||
STACK_ALIGN(char, fn, ISFS_MAXPATH, 32);
|
||||
|
||||
memset(&theme, 0, sizeof(theme));
|
||||
|
||||
for (i = 0; i < THEME_LAST; ++i)
|
||||
theme_gfx[i] = NULL;
|
||||
|
||||
for (i = 0; i < FONT_MAX; ++i) {
|
||||
theme_fonts[i].file = NULL;
|
||||
theme_fonts[i].data = NULL;
|
||||
theme_fonts[i].data_len = 0;
|
||||
theme_fonts[i].size = 0;
|
||||
theme_fonts[i].color = 0xffffffff;
|
||||
}
|
||||
|
||||
theme.progress.ul = 0xc8e1edff;
|
||||
theme.progress.ur = 0xc8e1edff;
|
||||
theme.progress.lr = 0x183848ff;
|
||||
theme.progress.ll = 0x183848ff;
|
||||
|
||||
titlepath = title_get_path();
|
||||
if (titlepath[0])
|
||||
sprintf(fn, "%s/%s", titlepath, app_fn_theme);
|
||||
else
|
||||
fn[0] = 0;
|
||||
|
||||
if (data) {
|
||||
theme_load(data, data_len);
|
||||
if (fn[0])
|
||||
isfs_put(fn, data, data_len);
|
||||
} else {
|
||||
res = -1;
|
||||
|
||||
if (fn[0]) {
|
||||
res = isfs_get(fn, &data, 0, MAX_THEME_ZIP_SIZE, true);
|
||||
if (res > 0) {
|
||||
data_len = res;
|
||||
gprintf("theme loaded from nand (%lu bytes)\n", data_len);
|
||||
theme_load(data, data_len);
|
||||
}
|
||||
}
|
||||
|
||||
if (res <= 0) {
|
||||
data = NULL;
|
||||
data_len = 0;
|
||||
theme.description = pstrdup("Dark Water by drmr");
|
||||
}
|
||||
}
|
||||
|
||||
gprintf("theme.description=%s\n", theme.description);
|
||||
gprintf("theme.progress ul=0x%08lx\n", theme.progress.ul);
|
||||
gprintf("theme.progress ur=0x%08lx\n", theme.progress.ur);
|
||||
gprintf("theme.progress lr=0x%08lx\n", theme.progress.lr);
|
||||
gprintf("theme.progress ll=0x%08lx\n", theme.progress.ll);
|
||||
gprintf("theme.langs=0x%x\n", theme.langs);
|
||||
|
||||
for (i = 0; i < THEME_LAST; ++i) {
|
||||
if (!theme_gfx[i]) {
|
||||
if (data && theme_data[i].filename) {
|
||||
gprintf("%s unavailable, using default\n",
|
||||
theme_data[i].filename);
|
||||
}
|
||||
|
||||
if (widescreen && theme_data[i].data_ws)
|
||||
theme_gfx[i] = tex_from_png(theme_data[i].data_ws,
|
||||
*(theme_data[i].size_ws),
|
||||
theme_data[i].width_ws,
|
||||
theme_data[i].height);
|
||||
else
|
||||
theme_gfx[i] = tex_from_png(theme_data[i].data,
|
||||
*(theme_data[i].size),
|
||||
theme_data[i].width,
|
||||
theme_data[i].height);
|
||||
}
|
||||
|
||||
if (!theme_gfx[i])
|
||||
gprintf("WARNING: '%s' unavailable!\n", theme_data[i].filename);
|
||||
}
|
||||
|
||||
if (data)
|
||||
blob_free(data);
|
||||
}
|
||||
|
||||
void theme_deinit(void) {
|
||||
u32 i,j;
|
||||
|
||||
if (theme.description) {
|
||||
free(theme.description);
|
||||
theme.description = NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < FONT_MAX; ++i) {
|
||||
// data can be shared...
|
||||
if (theme_fonts[i].data) {
|
||||
for (j = 0; j < FONT_MAX; ++j) {
|
||||
if (i != j && theme_fonts[i].data == theme_fonts[j].data)
|
||||
theme_fonts[j].data = NULL;
|
||||
}
|
||||
free(theme_fonts[i].data);
|
||||
theme_fonts[i].data = NULL;
|
||||
}
|
||||
// filename is owned by xml.c, no need to free here
|
||||
memset(&theme_fonts[i], 0, sizeof(theme_fonts[i]));
|
||||
}
|
||||
|
||||
for (i = 0; i < THEME_LAST; ++i) {
|
||||
tex_free(theme_gfx[i]);
|
||||
theme_gfx[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
bool theme_is_valid_fn(const char *filename) {
|
||||
u32 index;
|
||||
bool ws;
|
||||
|
||||
int l = strlen(filename);
|
||||
|
||||
if (l >= 5 &&
|
||||
tolower((unsigned char)filename[l-1]) == 'f' &&
|
||||
tolower((unsigned char)filename[l-2]) == 't' &&
|
||||
tolower((unsigned char)filename[l-3]) == 't' &&
|
||||
filename[l-4] == '.')
|
||||
return true;
|
||||
|
||||
// allow random txt files (e.g. README.txt)
|
||||
if (l >= 5 &&
|
||||
tolower((unsigned char)filename[l-1]) == 't' &&
|
||||
tolower((unsigned char)filename[l-2]) == 'x' &&
|
||||
tolower((unsigned char)filename[l-3]) == 't' &&
|
||||
filename[l-4] == '.')
|
||||
return true;
|
||||
|
||||
return theme_get_index(&index, &ws, filename);
|
||||
}
|
||||
|
||||
76
channel/channelapp/source/theme.h
Normal file
76
channel/channelapp/source/theme.h
Normal file
@@ -0,0 +1,76 @@
|
||||
#ifndef _THEME_H_
|
||||
#define _THEME_H_
|
||||
|
||||
#include <gctypes.h>
|
||||
|
||||
#include "gfx.h"
|
||||
#include "xml.h"
|
||||
|
||||
typedef enum {
|
||||
// view.c
|
||||
THEME_BACKGROUND = 0,
|
||||
THEME_LOGO,
|
||||
|
||||
// bubbles.c
|
||||
THEME_BUBBLE1,
|
||||
THEME_BUBBLE2,
|
||||
THEME_BUBBLE3,
|
||||
|
||||
// browser.c
|
||||
THEME_ARROW_LEFT,
|
||||
THEME_ARROW_LEFT_FOCUS,
|
||||
THEME_ARROW_RIGHT,
|
||||
THEME_ARROW_RIGHT_FOCUS,
|
||||
THEME_GECKO,
|
||||
THEME_GECKO_ACTIVE,
|
||||
THEME_LAN,
|
||||
THEME_LAN_ACTIVE,
|
||||
THEME_THROBBER,
|
||||
|
||||
// dialogc.c
|
||||
THEME_ABOUT,
|
||||
THEME_DIALOG,
|
||||
THEME_DLG_INFO,
|
||||
THEME_DLG_CONFIRM,
|
||||
THEME_DLG_WARNING,
|
||||
THEME_DLG_ERROR,
|
||||
|
||||
// widgets.c
|
||||
THEME_BUTTON,
|
||||
THEME_BUTTON_FOCUS,
|
||||
THEME_BUTTON_SMALL,
|
||||
THEME_BUTTON_SMALL_FOCUS,
|
||||
THEME_BUTTON_TINY,
|
||||
THEME_BUTTON_TINY_FOCUS,
|
||||
THEME_APP_ENTRY,
|
||||
THEME_APP_ENTRY_FOCUS,
|
||||
THEME_GRID_APP_ENTRY,
|
||||
THEME_GRID_APP_ENTRY_FOCUS,
|
||||
THEME_PROGRESS,
|
||||
THEME_CONTENT_ARROW_UP,
|
||||
THEME_CONTENT_ARROW_DOWN,
|
||||
|
||||
THEME_LAST
|
||||
} theme_entry;
|
||||
|
||||
typedef struct {
|
||||
const char *file;
|
||||
void *data;
|
||||
u32 data_len;
|
||||
int size;
|
||||
u32 color;
|
||||
} theme_font;
|
||||
|
||||
extern gfx_entity *theme_gfx[THEME_LAST];
|
||||
|
||||
extern theme_font theme_fonts[FONT_MAX];
|
||||
|
||||
extern const char *theme_fn_xml;
|
||||
|
||||
void theme_init(u8 *data, u32 data_len);
|
||||
void theme_deinit(void);
|
||||
|
||||
bool theme_is_valid_fn(const char *filename);
|
||||
|
||||
#endif
|
||||
|
||||
95
channel/channelapp/source/title.c
Normal file
95
channel/channelapp/source/title.c
Normal file
@@ -0,0 +1,95 @@
|
||||
#include <gccore.h>
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "title.h"
|
||||
|
||||
u32 ng_id = 0;
|
||||
u32 ms_id = 0;
|
||||
|
||||
static u64 title_id = 0;
|
||||
static char title_path[ISFS_MAXPATH] __attribute__((aligned(0x20)));
|
||||
|
||||
static u8 buf[0x1000] __attribute__((aligned(0x20)));
|
||||
|
||||
static void title_get_ngid(void) {
|
||||
s32 res;
|
||||
|
||||
res = ES_GetDeviceID(&ng_id);
|
||||
if (res < 0) {
|
||||
gprintf("ES_GetDeviceID failed: %ld\n", res);
|
||||
}
|
||||
}
|
||||
|
||||
static void title_get_msid(void) {
|
||||
s32 ret;
|
||||
|
||||
// check for dpki
|
||||
ret = ES_GetDeviceCert(buf);
|
||||
if (ret < 0) {
|
||||
gprintf("ES_GetDeviceCert failed: %ld\n", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
ms_id = buf[0x99] - '0';
|
||||
|
||||
if (ms_id == 3) {
|
||||
gprintf("We're on dpki\n");
|
||||
} else if (ms_id == 2) {
|
||||
gprintf("We're on retail\n");
|
||||
} else {
|
||||
gprintf("Unknown ms-id %ld?\n", ms_id);
|
||||
}
|
||||
}
|
||||
|
||||
static void title_get_title_path(void) {
|
||||
s32 res;
|
||||
|
||||
res = ES_GetTitleID(&title_id);
|
||||
if (res < 0) {
|
||||
gprintf("ES_GetTitleID failed: %ld\n", res);
|
||||
return;
|
||||
}
|
||||
|
||||
res = ES_GetDataDir(title_id, title_path);
|
||||
if (res < 0) {
|
||||
gprintf("ES_GetDataDir failed: %ld\n", res);
|
||||
return;
|
||||
}
|
||||
|
||||
gprintf("data path is '%s'\n", title_path);
|
||||
}
|
||||
|
||||
const char *title_get_path(void) {
|
||||
return title_path;
|
||||
}
|
||||
|
||||
static bool title_is_installed(u64 title_id) {
|
||||
s32 ret;
|
||||
u32 x;
|
||||
|
||||
ret = ES_GetTitleContentsCount(title_id, &x);
|
||||
|
||||
if (ret < 0)
|
||||
return false; // title was never installed
|
||||
|
||||
if (x <= 0)
|
||||
return false; // title was installed but deleted via Channel Management
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#define TITLEID_200 0x0000000100000200ll
|
||||
|
||||
bool is_vwii(void) {
|
||||
return title_is_installed(TITLEID_200);
|
||||
}
|
||||
|
||||
void title_init(void) {
|
||||
memset(title_path, 0, sizeof(title_path));
|
||||
title_get_msid();
|
||||
title_get_ngid();
|
||||
title_get_title_path();
|
||||
}
|
||||
12
channel/channelapp/source/title.h
Normal file
12
channel/channelapp/source/title.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#ifndef _TITLE_H_
|
||||
#define _TITLE_H_
|
||||
|
||||
#include <gctypes.h>
|
||||
#include "../config.h"
|
||||
|
||||
const char *title_get_path(void);
|
||||
void title_init(void);
|
||||
bool is_vwii(void);
|
||||
|
||||
#endif
|
||||
|
||||
1576
channel/channelapp/source/unzip.c
Normal file
1576
channel/channelapp/source/unzip.c
Normal file
File diff suppressed because it is too large
Load Diff
331
channel/channelapp/source/unzip.h
Normal file
331
channel/channelapp/source/unzip.h
Normal file
@@ -0,0 +1,331 @@
|
||||
/* unzip.h -- IO for uncompress .zip files using zlib
|
||||
Version 1.01e, February 12th, 2005
|
||||
|
||||
Copyright (C) 1998-2005 Gilles Vollant
|
||||
|
||||
This unzip package allow extract file from .ZIP file, compatible with PKZip 2.04g
|
||||
WinZip, InfoZip tools and compatible.
|
||||
|
||||
Multi volume ZipFile (span) are not supported.
|
||||
Encryption compatible with pkzip 2.04g only supported
|
||||
Old compressions used by old PKZip 1.x are not supported
|
||||
|
||||
|
||||
I WAIT FEEDBACK at mail info@winimage.com
|
||||
Visit also http://www.winimage.com/zLibDll/unzip.htm for evolution
|
||||
|
||||
Condition of use and distribution are the same than zlib :
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
|
||||
*/
|
||||
|
||||
/* for more info about .ZIP format, see
|
||||
http://www.info-zip.org/pub/infozip/doc/appnote-981119-iz.zip
|
||||
http://www.info-zip.org/pub/infozip/doc/
|
||||
PkWare has also a specification at :
|
||||
ftp://ftp.pkware.com/probdesc.zip
|
||||
*/
|
||||
|
||||
#ifndef _UNZIP_H_
|
||||
#define _UNZIP_H_
|
||||
|
||||
#include <zlib.h>
|
||||
|
||||
#include "unzip_io.h"
|
||||
|
||||
#if defined(STRICTUNZIP) || defined(STRICTZIPUNZIP)
|
||||
/* like the STRICT of WIN32, we define a pointer that cannot be converted
|
||||
from (void*) without cast */
|
||||
typedef struct TagunzFile__ { int unused; } unzFile__;
|
||||
typedef unzFile__ *unzFile;
|
||||
#else
|
||||
typedef voidp unzFile;
|
||||
#endif
|
||||
|
||||
|
||||
#define UNZ_OK (0)
|
||||
#define UNZ_END_OF_LIST_OF_FILE (-100)
|
||||
#define UNZ_ERRNO (Z_ERRNO)
|
||||
#define UNZ_EOF (0)
|
||||
#define UNZ_PARAMERROR (-102)
|
||||
#define UNZ_BADZIPFILE (-103)
|
||||
#define UNZ_INTERNALERROR (-104)
|
||||
#define UNZ_CRCERROR (-105)
|
||||
|
||||
/* tm_unz contain date/time info */
|
||||
typedef struct tm_unz_s
|
||||
{
|
||||
uInt tm_sec; /* seconds after the minute - [0,59] */
|
||||
uInt tm_min; /* minutes after the hour - [0,59] */
|
||||
uInt tm_hour; /* hours since midnight - [0,23] */
|
||||
uInt tm_mday; /* day of the month - [1,31] */
|
||||
uInt tm_mon; /* months since January - [0,11] */
|
||||
uInt tm_year; /* years - [1980..2044] */
|
||||
} tm_unz;
|
||||
|
||||
/* unz_global_info structure contain global data about the ZIPfile
|
||||
These data comes from the end of central dir */
|
||||
typedef struct unz_global_info_s
|
||||
{
|
||||
uLong number_entry; /* total number of entries in
|
||||
the central dir on this disk */
|
||||
uLong size_comment; /* size of the global comment of the zipfile */
|
||||
} unz_global_info;
|
||||
|
||||
|
||||
/* unz_file_info contain information about a file in the zipfile */
|
||||
typedef struct unz_file_info_s
|
||||
{
|
||||
uLong version; /* version made by 2 bytes */
|
||||
uLong version_needed; /* version needed to extract 2 bytes */
|
||||
uLong flag; /* general purpose bit flag 2 bytes */
|
||||
uLong compression_method; /* compression method 2 bytes */
|
||||
uLong dosDate; /* last mod file date in Dos fmt 4 bytes */
|
||||
uLong crc; /* crc-32 4 bytes */
|
||||
uLong compressed_size; /* compressed size 4 bytes */
|
||||
uLong uncompressed_size; /* uncompressed size 4 bytes */
|
||||
uLong size_filename; /* filename length 2 bytes */
|
||||
uLong size_file_extra; /* extra field length 2 bytes */
|
||||
uLong size_file_comment; /* file comment length 2 bytes */
|
||||
|
||||
uLong disk_num_start; /* disk number start 2 bytes */
|
||||
uLong internal_fa; /* internal file attributes 2 bytes */
|
||||
uLong external_fa; /* external file attributes 4 bytes */
|
||||
|
||||
tm_unz tmu_date;
|
||||
} unz_file_info;
|
||||
|
||||
extern int ZEXPORT unzStringFileNameCompare OF ((const char* fileName1,
|
||||
const char* fileName2,
|
||||
int iCaseSensitivity));
|
||||
/*
|
||||
Compare two filename (fileName1,fileName2).
|
||||
If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp)
|
||||
If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi
|
||||
or strcasecmp)
|
||||
If iCaseSenisivity = 0, case sensitivity is defaut of your operating system
|
||||
(like 1 on Unix, 2 on Windows)
|
||||
*/
|
||||
|
||||
extern unzFile ZEXPORT unzOpen OF((void *buffer, size_t buf_len));
|
||||
/*
|
||||
Open a Zip file. path contain the full pathname (by example,
|
||||
If the zipfile cannot be opened (file doesn't exist or in not valid), the
|
||||
return value is NULL.
|
||||
Else, the return value is a unzFile Handle, usable with other function
|
||||
of this unzip package.
|
||||
*/
|
||||
|
||||
extern int ZEXPORT unzClose OF((unzFile file));
|
||||
/*
|
||||
Close a ZipFile opened with unzipOpen.
|
||||
If there is files inside the .Zip opened with unzOpenCurrentFile (see later),
|
||||
these files MUST be closed with unzipCloseCurrentFile before call unzipClose.
|
||||
return UNZ_OK if there is no problem. */
|
||||
|
||||
extern int ZEXPORT unzGetGlobalInfo OF((unzFile file,
|
||||
unz_global_info *pglobal_info));
|
||||
/*
|
||||
Write info about the ZipFile in the *pglobal_info structure.
|
||||
No preparation of the structure is needed
|
||||
return UNZ_OK if there is no problem. */
|
||||
|
||||
|
||||
extern int ZEXPORT unzGetGlobalComment OF((unzFile file,
|
||||
char *szComment,
|
||||
uLong uSizeBuf));
|
||||
/*
|
||||
Get the global comment string of the ZipFile, in the szComment buffer.
|
||||
uSizeBuf is the size of the szComment buffer.
|
||||
return the number of byte copied or an error code <0
|
||||
*/
|
||||
|
||||
|
||||
/***************************************************************************/
|
||||
/* Unzip package allow you browse the directory of the zipfile */
|
||||
|
||||
extern int ZEXPORT unzGoToFirstFile OF((unzFile file));
|
||||
/*
|
||||
Set the current file of the zipfile to the first file.
|
||||
return UNZ_OK if there is no problem
|
||||
*/
|
||||
|
||||
extern int ZEXPORT unzGoToNextFile OF((unzFile file));
|
||||
/*
|
||||
Set the current file of the zipfile to the next file.
|
||||
return UNZ_OK if there is no problem
|
||||
return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest.
|
||||
*/
|
||||
|
||||
extern int ZEXPORT unzLocateFile OF((unzFile file,
|
||||
const char *szFileName,
|
||||
int iCaseSensitivity));
|
||||
/*
|
||||
Try locate the file szFileName in the zipfile.
|
||||
For the iCaseSensitivity signification, see unzStringFileNameCompare
|
||||
|
||||
return value :
|
||||
UNZ_OK if the file is found. It becomes the current file.
|
||||
UNZ_END_OF_LIST_OF_FILE if the file is not found
|
||||
*/
|
||||
|
||||
|
||||
/* ****************************************** */
|
||||
/* Ryan supplied functions */
|
||||
/* unz_file_info contain information about a file in the zipfile */
|
||||
typedef struct unz_file_pos_s
|
||||
{
|
||||
uLong pos_in_zip_directory; /* offset in zip file directory */
|
||||
uLong num_of_file; /* # of file */
|
||||
} unz_file_pos;
|
||||
|
||||
extern int ZEXPORT unzGetFilePos(
|
||||
unzFile file,
|
||||
unz_file_pos* file_pos);
|
||||
|
||||
extern int ZEXPORT unzGoToFilePos(
|
||||
unzFile file,
|
||||
unz_file_pos* file_pos);
|
||||
|
||||
/* ****************************************** */
|
||||
|
||||
extern int ZEXPORT unzGetCurrentFileInfo OF((unzFile file,
|
||||
unz_file_info *pfile_info,
|
||||
char *szFileName,
|
||||
uLong fileNameBufferSize,
|
||||
void *extraField,
|
||||
uLong extraFieldBufferSize,
|
||||
char *szComment,
|
||||
uLong commentBufferSize));
|
||||
/*
|
||||
Get Info about the current file
|
||||
if pfile_info!=NULL, the *pfile_info structure will contain somes info about
|
||||
the current file
|
||||
if szFileName!=NULL, the filemane string will be copied in szFileName
|
||||
(fileNameBufferSize is the size of the buffer)
|
||||
if extraField!=NULL, the extra field information will be copied in extraField
|
||||
(extraFieldBufferSize is the size of the buffer).
|
||||
This is the Central-header version of the extra field
|
||||
if szComment!=NULL, the comment string of the file will be copied in szComment
|
||||
(commentBufferSize is the size of the buffer)
|
||||
*/
|
||||
|
||||
/***************************************************************************/
|
||||
/* for reading the content of the current zipfile, you can open it, read data
|
||||
from it, and close it (you can close it before reading all the file)
|
||||
*/
|
||||
|
||||
extern int ZEXPORT unzOpenCurrentFile OF((unzFile file));
|
||||
/*
|
||||
Open for reading data the current file in the zipfile.
|
||||
If there is no error, the return value is UNZ_OK.
|
||||
*/
|
||||
|
||||
extern int ZEXPORT unzOpenCurrentFilePassword OF((unzFile file,
|
||||
const char* password));
|
||||
/*
|
||||
Open for reading data the current file in the zipfile.
|
||||
password is a crypting password
|
||||
If there is no error, the return value is UNZ_OK.
|
||||
*/
|
||||
|
||||
extern int ZEXPORT unzOpenCurrentFile2 OF((unzFile file,
|
||||
int* method,
|
||||
int* level,
|
||||
int raw));
|
||||
/*
|
||||
Same than unzOpenCurrentFile, but open for read raw the file (not uncompress)
|
||||
if raw==1
|
||||
*method will receive method of compression, *level will receive level of
|
||||
compression
|
||||
note : you can set level parameter as NULL (if you did not want known level,
|
||||
but you CANNOT set method parameter as NULL
|
||||
*/
|
||||
|
||||
extern int ZEXPORT unzOpenCurrentFile3 OF((unzFile file,
|
||||
int* method,
|
||||
int* level,
|
||||
int raw,
|
||||
const char* password));
|
||||
/*
|
||||
Same than unzOpenCurrentFile, but open for read raw the file (not uncompress)
|
||||
if raw==1
|
||||
*method will receive method of compression, *level will receive level of
|
||||
compression
|
||||
note : you can set level parameter as NULL (if you did not want known level,
|
||||
but you CANNOT set method parameter as NULL
|
||||
*/
|
||||
|
||||
|
||||
extern int ZEXPORT unzCloseCurrentFile OF((unzFile file));
|
||||
/*
|
||||
Close the file in zip opened with unzOpenCurrentFile
|
||||
Return UNZ_CRCERROR if all the file was read but the CRC is not good
|
||||
*/
|
||||
|
||||
extern int ZEXPORT unzReadCurrentFile OF((unzFile file,
|
||||
voidp buf,
|
||||
unsigned len));
|
||||
/*
|
||||
Read bytes from the current file (opened by unzOpenCurrentFile)
|
||||
buf contain buffer where data must be copied
|
||||
len the size of buf.
|
||||
|
||||
return the number of byte copied if somes bytes are copied
|
||||
return 0 if the end of file was reached
|
||||
return <0 with error code if there is an error
|
||||
(UNZ_ERRNO for IO error, or zLib error for uncompress error)
|
||||
*/
|
||||
|
||||
extern z_off_t ZEXPORT unztell OF((unzFile file));
|
||||
/*
|
||||
Give the current position in uncompressed data
|
||||
*/
|
||||
|
||||
extern int ZEXPORT unzeof OF((unzFile file));
|
||||
/*
|
||||
return 1 if the end of file was reached, 0 elsewhere
|
||||
*/
|
||||
|
||||
extern int ZEXPORT unzGetLocalExtrafield OF((unzFile file,
|
||||
voidp buf,
|
||||
unsigned len));
|
||||
/*
|
||||
Read extra field from the current file (opened by unzOpenCurrentFile)
|
||||
This is the local-header version of the extra field (sometimes, there is
|
||||
more info in the local-header version than in the central-header)
|
||||
|
||||
if buf==NULL, it return the size of the local extra field
|
||||
|
||||
if buf!=NULL, len is the size of the buffer, the extra header is copied in
|
||||
buf.
|
||||
the return value is the number of bytes copied in buf, or (if <0)
|
||||
the error code
|
||||
*/
|
||||
|
||||
/***************************************************************************/
|
||||
|
||||
/* Get the current file offset */
|
||||
extern uLong ZEXPORT unzGetOffset (unzFile file);
|
||||
|
||||
/* Set the current file offset */
|
||||
extern int ZEXPORT unzSetOffset (unzFile file, uLong pos);
|
||||
|
||||
#endif
|
||||
|
||||
170
channel/channelapp/source/unzip_io.c
Normal file
170
channel/channelapp/source/unzip_io.c
Normal file
@@ -0,0 +1,170 @@
|
||||
/* ioapi_mem2.c -- IO base function header for compress/uncompress .zip
|
||||
files using zlib + zip or unzip API
|
||||
|
||||
This version of ioapi is designed to access memory rather than files.
|
||||
We do use a region of memory to put data in to and take it out of. We do
|
||||
not have auto-extending buffers and do not inform anyone else that the
|
||||
data has been written. It is really intended for accessing a zip archive
|
||||
embedded in an application such that I can write an installer with no
|
||||
external files. Creation of archives has not been attempted, although
|
||||
parts of the framework are present.
|
||||
|
||||
Based on Unzip ioapi.c version 0.22, May 19th, 2003
|
||||
|
||||
Copyright (C) 1998-2003 Gilles Vollant
|
||||
(C) 2003 Justin Fletcher
|
||||
|
||||
Dynamically allocated memory version. Troels K 2004
|
||||
mem_close deletes the data: file is single-session. No filenames.
|
||||
|
||||
This file is under the same license as the Unzip tool it is distributed
|
||||
with.
|
||||
*/
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <zlib.h>
|
||||
|
||||
#include "panic.h"
|
||||
#include "unzip_io.h"
|
||||
|
||||
static voidpf ZCALLBACK mem_open OF((
|
||||
voidpf opaque,
|
||||
void* buffer,
|
||||
size_t buf_len,
|
||||
int mode));
|
||||
|
||||
static uLong ZCALLBACK mem_read OF((
|
||||
voidpf opaque,
|
||||
voidpf stream,
|
||||
void* buf,
|
||||
uLong size));
|
||||
|
||||
static long ZCALLBACK mem_tell OF((
|
||||
voidpf opaque,
|
||||
voidpf stream));
|
||||
|
||||
static long ZCALLBACK mem_seek OF((
|
||||
voidpf opaque,
|
||||
voidpf stream,
|
||||
uLong offset,
|
||||
int origin));
|
||||
|
||||
static int ZCALLBACK mem_close OF((
|
||||
voidpf opaque,
|
||||
voidpf stream));
|
||||
|
||||
static int ZCALLBACK mem_error OF((
|
||||
voidpf opaque,
|
||||
voidpf stream));
|
||||
|
||||
typedef struct _MEMFILE
|
||||
{
|
||||
void* buffer; /* Base of the region of memory we're using */
|
||||
long length; /* Size of the region of memory we're using */
|
||||
long position; /* Current offset in the area */
|
||||
} MEMFILE;
|
||||
|
||||
static voidpf ZCALLBACK mem_open (opaque, buffer, buf_len, mode)
|
||||
voidpf opaque;
|
||||
void* buffer;
|
||||
size_t buf_len;
|
||||
int mode;
|
||||
{
|
||||
MEMFILE* handle = pmalloc(sizeof(*handle));
|
||||
|
||||
handle->position = 0;
|
||||
handle->buffer = buffer;
|
||||
handle->length = buf_len;
|
||||
return handle;
|
||||
}
|
||||
|
||||
static uLong ZCALLBACK mem_read (opaque, stream, buf, size)
|
||||
voidpf opaque;
|
||||
voidpf stream;
|
||||
void* buf;
|
||||
uLong size;
|
||||
{
|
||||
MEMFILE* handle = (MEMFILE*) stream;
|
||||
|
||||
if ( (handle->position + size) > handle->length)
|
||||
{
|
||||
size = handle->length - handle->position;
|
||||
}
|
||||
memcpy(buf, ((char*)handle->buffer) + handle->position, size);
|
||||
handle->position+=size;
|
||||
return size;
|
||||
}
|
||||
|
||||
static long ZCALLBACK mem_tell (opaque, stream)
|
||||
voidpf opaque;
|
||||
voidpf stream;
|
||||
{
|
||||
MEMFILE *handle = (MEMFILE *)stream;
|
||||
return handle->position;
|
||||
}
|
||||
|
||||
static long ZCALLBACK mem_seek (opaque, stream, offset, origin)
|
||||
voidpf opaque;
|
||||
voidpf stream;
|
||||
uLong offset;
|
||||
int origin;
|
||||
{
|
||||
MEMFILE* handle = (MEMFILE*)stream;
|
||||
|
||||
int bOK = 1;
|
||||
switch (origin)
|
||||
{
|
||||
case SEEK_SET :
|
||||
//bOK = (offset >= 0) && (offset <= size);
|
||||
if (bOK) handle->position = offset;
|
||||
break;
|
||||
case SEEK_CUR :
|
||||
bOK = ((offset + handle->position) >= 0) && (((offset + handle->position) <= handle->length));
|
||||
if (bOK) handle->position = offset + handle->position;
|
||||
break;
|
||||
case SEEK_END:
|
||||
bOK = ((handle->length - offset) >= 0) && (((handle->length - offset) <= handle->length));
|
||||
if (bOK) handle->position = offset + handle->length - 0;
|
||||
break;
|
||||
default:
|
||||
bOK = 0;
|
||||
break;
|
||||
}
|
||||
return bOK ? 0 : -1;
|
||||
}
|
||||
|
||||
int ZCALLBACK mem_close (opaque, stream)
|
||||
voidpf opaque;
|
||||
voidpf stream;
|
||||
{
|
||||
MEMFILE *handle = (MEMFILE *)stream;
|
||||
|
||||
free (handle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ZCALLBACK mem_error (opaque, stream)
|
||||
voidpf opaque;
|
||||
voidpf stream;
|
||||
{
|
||||
//MEMFILE *handle = (MEMFILE *)stream;
|
||||
/* We never return errors */
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mem_simple_create_file(zlib_filefunc_def* api)
|
||||
{
|
||||
api->zopen_file = mem_open;
|
||||
api->zread_file = mem_read;
|
||||
api->zwrite_file = NULL;
|
||||
api->ztell_file = mem_tell;
|
||||
api->zseek_file = mem_seek;
|
||||
api->zclose_file = mem_close;
|
||||
api->zerror_file = mem_error;
|
||||
api->opaque = NULL;
|
||||
}
|
||||
|
||||
58
channel/channelapp/source/unzip_io.h
Normal file
58
channel/channelapp/source/unzip_io.h
Normal file
@@ -0,0 +1,58 @@
|
||||
#ifndef _UNZIP_IO_H_
|
||||
#define _UNZIP_IO_H_
|
||||
|
||||
#define ZLIB_FILEFUNC_SEEK_CUR (1)
|
||||
#define ZLIB_FILEFUNC_SEEK_END (2)
|
||||
#define ZLIB_FILEFUNC_SEEK_SET (0)
|
||||
|
||||
#define ZLIB_FILEFUNC_MODE_READ (1)
|
||||
#define ZLIB_FILEFUNC_MODE_WRITE (2)
|
||||
#define ZLIB_FILEFUNC_MODE_READWRITEFILTER (3)
|
||||
|
||||
#define ZLIB_FILEFUNC_MODE_EXISTING (4)
|
||||
#define ZLIB_FILEFUNC_MODE_CREATE (8)
|
||||
|
||||
|
||||
#ifndef ZCALLBACK
|
||||
|
||||
#if (defined(WIN32) || defined (WINDOWS) || defined (_WINDOWS)) && defined(CALLBACK) && defined (USEWINDOWS_CALLBACK)
|
||||
#define ZCALLBACK CALLBACK
|
||||
#else
|
||||
#define ZCALLBACK
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
typedef voidpf (ZCALLBACK *open_file_func) OF((voidpf opaque, void* buffer, size_t buf_len, int mode));
|
||||
typedef uLong (ZCALLBACK *read_file_func) OF((voidpf opaque, voidpf stream, void* buf, uLong size));
|
||||
typedef uLong (ZCALLBACK *write_file_func) OF((voidpf opaque, voidpf stream, const void* buf, uLong size));
|
||||
typedef long (ZCALLBACK *tell_file_func) OF((voidpf opaque, voidpf stream));
|
||||
typedef long (ZCALLBACK *seek_file_func) OF((voidpf opaque, voidpf stream, uLong offset, int origin));
|
||||
typedef int (ZCALLBACK *close_file_func) OF((voidpf opaque, voidpf stream));
|
||||
typedef int (ZCALLBACK *testerror_file_func) OF((voidpf opaque, voidpf stream));
|
||||
|
||||
typedef struct zlib_filefunc_def_s
|
||||
{
|
||||
open_file_func zopen_file;
|
||||
read_file_func zread_file;
|
||||
write_file_func zwrite_file;
|
||||
tell_file_func ztell_file;
|
||||
seek_file_func zseek_file;
|
||||
close_file_func zclose_file;
|
||||
testerror_file_func zerror_file;
|
||||
voidpf opaque;
|
||||
} zlib_filefunc_def;
|
||||
|
||||
|
||||
extern void mem_simple_create_file(zlib_filefunc_def* api);
|
||||
|
||||
#define ZREAD(filefunc,filestream,buf,size) ((*((filefunc).zread_file))((filefunc).opaque,filestream,buf,size))
|
||||
#define ZWRITE(filefunc,filestream,buf,size) ((*((filefunc).zwrite_file))((filefunc).opaque,filestream,buf,size))
|
||||
#define ZTELL(filefunc,filestream) ((*((filefunc).ztell_file))((filefunc).opaque,filestream))
|
||||
#define ZSEEK(filefunc,filestream,pos,mode) ((*((filefunc).zseek_file))((filefunc).opaque,filestream,pos,mode))
|
||||
#define ZCLOSE(filefunc,filestream) ((*((filefunc).zclose_file))((filefunc).opaque,filestream))
|
||||
#define ZERROR(filefunc,filestream) ((*((filefunc).zerror_file))((filefunc).opaque,filestream))
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
316
channel/channelapp/source/update.c
Normal file
316
channel/channelapp/source/update.c
Normal file
@@ -0,0 +1,316 @@
|
||||
|
||||
#include <malloc.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <ogcsys.h>
|
||||
|
||||
#include "../config.h"
|
||||
|
||||
#ifdef ENABLE_UPDATES
|
||||
|
||||
#include "view.h"
|
||||
#include "dialogs.h"
|
||||
#include "http.h"
|
||||
#include "i18n.h"
|
||||
#include "sha1.h"
|
||||
#include "ecdsa.h"
|
||||
|
||||
#include "update.h"
|
||||
|
||||
#define UPDATE_HEADER "SIG0"
|
||||
#define UPDATE_OFFSET_R 4
|
||||
#define UPDATE_OFFSET_S 4+30
|
||||
#define UPDATE_OFFSET_XML 64
|
||||
|
||||
|
||||
static const u8 update_key[] = UPDATE_PUBLIC_KEY;
|
||||
|
||||
static u8 nibble2hex(u8 byte)
|
||||
{
|
||||
byte &= 0xf;
|
||||
if (byte <= 9)
|
||||
return '0' + byte;
|
||||
else
|
||||
return 'a' + byte - 0xa;
|
||||
}
|
||||
|
||||
static void hash2ascii(u8 *hash, u8 *ascii)
|
||||
{
|
||||
u32 i;
|
||||
for (i = 0; i < 20; i++) {
|
||||
ascii[2*i] = nibble2hex(hash[i] >> 4);
|
||||
ascii[(2*i) + 1] = nibble2hex(hash[i]);
|
||||
}
|
||||
ascii[40] = 0;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
u8 *sha_data;
|
||||
u32 sha_len;
|
||||
|
||||
u8 *ecdsa_r;
|
||||
u8 *ecdsa_s;
|
||||
|
||||
bool valid;
|
||||
bool running;
|
||||
} update_arg;
|
||||
|
||||
static lwpq_t update_queue;
|
||||
static lwp_t update_thread;
|
||||
static u8 update_stack[UPDATE_THREAD_STACKSIZE] ATTRIBUTE_ALIGN (32);
|
||||
static update_arg ta_ua;
|
||||
|
||||
static void *update_func(void *arg) {
|
||||
u8 hash[20];
|
||||
update_arg *ua = (update_arg *) arg;
|
||||
|
||||
SHA1(ua->sha_data, ua->sha_len, hash);
|
||||
if (check_ecdsa(update_key, ua->ecdsa_r, ua->ecdsa_s, hash))
|
||||
ua->valid = true;
|
||||
|
||||
ua->running = false;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
UPDATE_IDLE = 0,
|
||||
UPDATE_FETCH_META,
|
||||
UPDATE_VALIDATE,
|
||||
UPDATE_NO_UPDATE,
|
||||
UPDATE_CAN_UPDATE
|
||||
} update_state;
|
||||
|
||||
static const char *update_url = UPDATE_URL;
|
||||
static update_state us = UPDATE_IDLE;
|
||||
static update_info *info = NULL;
|
||||
|
||||
bool update_signal(void) {
|
||||
if ((us == UPDATE_FETCH_META) || us == UPDATE_CAN_UPDATE)
|
||||
return false;
|
||||
|
||||
gprintf("starting update check\n");
|
||||
|
||||
if (http_request(update_url, 8 * 1024)) {
|
||||
us = UPDATE_FETCH_META;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static u8 *update_data = NULL;
|
||||
static u32 update_len = 0;
|
||||
|
||||
bool update_busy(bool *update_available) {
|
||||
http_res http_result;
|
||||
u32 status;
|
||||
s32 res;
|
||||
|
||||
if ((us != UPDATE_FETCH_META) && (us != UPDATE_VALIDATE))
|
||||
return false;
|
||||
|
||||
if (us == UPDATE_FETCH_META) {
|
||||
if (!http_get_result(&http_result, &status, &update_data, &update_len))
|
||||
return true;
|
||||
|
||||
*update_available = false;
|
||||
us = UPDATE_NO_UPDATE;
|
||||
|
||||
gprintf("update check done, status=%u len=%u\n", status, update_len);
|
||||
|
||||
if (http_result != HTTPR_OK)
|
||||
return false;
|
||||
|
||||
gprintf("fetched update xml\n");
|
||||
|
||||
if (update_len < 64) {
|
||||
gprintf("update.sxml length is invalid\n");
|
||||
free(update_data);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (memcmp(UPDATE_HEADER, update_data, 4)) {
|
||||
gprintf("SIG0 header not found.\n");
|
||||
free(update_data);
|
||||
return false;
|
||||
}
|
||||
|
||||
memset(&ta_ua, 0, sizeof(ta_ua));
|
||||
ta_ua.sha_data = update_data + UPDATE_OFFSET_XML;
|
||||
ta_ua.sha_len = update_len - UPDATE_OFFSET_XML;
|
||||
ta_ua.ecdsa_r = update_data + UPDATE_OFFSET_R;
|
||||
ta_ua.ecdsa_s = update_data + UPDATE_OFFSET_S;
|
||||
ta_ua.running = true;
|
||||
|
||||
memset(&update_stack, 0, UPDATE_THREAD_STACKSIZE);
|
||||
LWP_InitQueue(&update_queue);
|
||||
res = LWP_CreateThread(&update_thread, update_func, &ta_ua,
|
||||
update_stack, UPDATE_THREAD_STACKSIZE,
|
||||
UPDATE_THREAD_PRIO);
|
||||
|
||||
if (res) {
|
||||
gprintf("error creating thread: %ld\n", res);
|
||||
LWP_CloseQueue(update_queue);
|
||||
free(update_data);
|
||||
return false;
|
||||
}
|
||||
|
||||
us = UPDATE_VALIDATE;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ta_ua.running)
|
||||
return true;
|
||||
|
||||
LWP_CloseQueue(update_queue);
|
||||
|
||||
us = UPDATE_NO_UPDATE;
|
||||
|
||||
if (!ta_ua.valid) {
|
||||
gprintf("update.sxml signature is invalid.\n");
|
||||
free(update_data);
|
||||
return false;
|
||||
}
|
||||
|
||||
gprintf("update.sxml signature is valid.\n");
|
||||
|
||||
info = update_parse((char *) update_data + UPDATE_OFFSET_XML);
|
||||
free(update_data);
|
||||
|
||||
if (!info) {
|
||||
gprintf("Failed to parse update.sxml\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!info->hash) {
|
||||
gprintf("no <hash> node in update.sxml\n");
|
||||
update_free(info);
|
||||
info = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strnlen(info->hash, 40) != 40) {
|
||||
gprintf("Invalid <hash> length\n");
|
||||
update_free(info);
|
||||
info = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (info->date > CHANNEL_VERSION_DATE) {
|
||||
gprintf("update available\n");
|
||||
us = UPDATE_CAN_UPDATE;
|
||||
*update_available = true;
|
||||
} else {
|
||||
gprintf("hbc is up2date\n");
|
||||
update_free(info);
|
||||
info = NULL;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#define UPDATE_MSG_SIZE 4096
|
||||
|
||||
bool update_execute (view *sub_view, entry_point *ep) {
|
||||
http_state state;
|
||||
http_res res;
|
||||
u32 status, length, progress;
|
||||
u8 *data;
|
||||
view *v = sub_view;
|
||||
bool downloading = false;
|
||||
static char text[UPDATE_MSG_SIZE];
|
||||
bool b = false;
|
||||
u8 hash[20];
|
||||
u8 ascii_hash[41];
|
||||
|
||||
if (us != UPDATE_CAN_UPDATE)
|
||||
return false;
|
||||
|
||||
us = UPDATE_IDLE;
|
||||
|
||||
snprintf (text, UPDATE_MSG_SIZE, _("An update to the Homebrew Channel "
|
||||
"(version %s, replacing the installed version %s) "
|
||||
"is available for installation, do you want "
|
||||
"to update now?\n\n"
|
||||
"Release notes:\n\n%s"),
|
||||
info->version, CHANNEL_VERSION_STR, info->notes);
|
||||
text[UPDATE_MSG_SIZE-1] = 0;
|
||||
|
||||
if (show_message (sub_view, DLGMT_CONFIRM, DLGB_YESNO, text, 0) == 1) {
|
||||
update_free(info);
|
||||
info = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!http_request(info->uri, 4 * 1024 * 1024)) {
|
||||
gprintf ("error requesting update download\n");
|
||||
update_free(info);
|
||||
info = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
view_plot (v, DIALOG_MASK_COLOR, NULL, NULL, NULL);
|
||||
|
||||
if (!http_get_state (&state, &length, &progress))
|
||||
break;
|
||||
|
||||
if ((!downloading) && (state == HTTPS_RECEIVING)) {
|
||||
downloading = true;
|
||||
|
||||
snprintf (text, 32, _("Downloading Update (%u kB)"), length / 1024);
|
||||
|
||||
v = dialog_progress (sub_view, text, length);
|
||||
|
||||
dialog_fade (v, true);
|
||||
}
|
||||
|
||||
if (downloading)
|
||||
dialog_set_progress (v, progress);
|
||||
|
||||
if (http_get_result (&res, &status, &data, &length)) {
|
||||
gprintf("update download done, status=%u len=%u\n", status, length);
|
||||
|
||||
if (res == HTTPR_OK) {
|
||||
SHA1(data, length, hash);
|
||||
hash2ascii(hash, ascii_hash);
|
||||
gprintf("Calculated hash : %s\n", ascii_hash);
|
||||
gprintf("Expected hash : %s\n", info->hash);
|
||||
|
||||
if (memcmp(ascii_hash, info->hash, 40)) {
|
||||
gprintf("installer.elf hash is invalid.\n");
|
||||
free(data);
|
||||
update_free(info);
|
||||
info = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
gprintf("installer.elf hash is valid.\n");
|
||||
b = true;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
update_free(info);
|
||||
info = NULL;
|
||||
|
||||
if (downloading)
|
||||
dialog_fade (v, false);
|
||||
|
||||
if (!b) {
|
||||
show_message (sub_view, DLGMT_ERROR, DLGB_OK, _("Download failed"), 0);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
b = loader_reloc(ep, data, length,
|
||||
"installer.elf\0-updatehbc\0\0", 26, true);
|
||||
|
||||
free (data);
|
||||
|
||||
return b;
|
||||
}
|
||||
#endif
|
||||
13
channel/channelapp/source/update.h
Normal file
13
channel/channelapp/source/update.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#ifndef _UPDATE_H_
|
||||
#define _UPDATE_H_
|
||||
|
||||
#include <gctypes.h>
|
||||
|
||||
#include "loader_reloc.h"
|
||||
|
||||
bool update_signal(void);
|
||||
bool update_busy(bool *update_available);
|
||||
bool update_execute(view *sub_view, entry_point *ep);
|
||||
|
||||
#endif
|
||||
|
||||
404
channel/channelapp/source/view.c
Normal file
404
channel/channelapp/source/view.c
Normal file
@@ -0,0 +1,404 @@
|
||||
#include <stdio.h>
|
||||
#include <malloc.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <ogcsys.h>
|
||||
|
||||
#include "../config.h"
|
||||
#include "controls.h"
|
||||
#include "theme.h"
|
||||
#include "cursors.h"
|
||||
#include "widgets.h"
|
||||
#include "bubbles.h"
|
||||
#include "panic.h"
|
||||
#include "view.h"
|
||||
|
||||
#define FOCUS_FLAGS (WF_VISIBLE | WF_ENABLED | WF_FOCUSABLE)
|
||||
#define CURSOR_FLAGS (WF_VISIBLE | WF_ENABLED)
|
||||
|
||||
#define RUMBLE_TIME 3
|
||||
#define RUMBLE_DELAY 12
|
||||
|
||||
u8 *cursor_data;
|
||||
u32 cursor_data_size;
|
||||
u8 *cursor_shade_data;
|
||||
u32 cursor_shade_data_size;
|
||||
|
||||
static gfx_queue_entry entry_bg;
|
||||
static gfx_queue_entry entry_logo;
|
||||
static gfx_queue_entry entry_throbber;
|
||||
|
||||
static gfx_entity gradient_subview;
|
||||
static gfx_queue_entry entry_subview;
|
||||
|
||||
static bool cursor_enabled;
|
||||
static bool rumble_enabled;
|
||||
static bool throbber_enabled;
|
||||
|
||||
static gfx_queue_entry cur[2];
|
||||
|
||||
static s8 rumble_timeout = -RUMBLE_DELAY;
|
||||
|
||||
bool view_bubbles = false;
|
||||
|
||||
void view_init (void) {
|
||||
cursor_enabled = false;
|
||||
rumble_enabled = (CONF_GetPadMotorMode() == 0) ? 0 : 1;
|
||||
throbber_enabled = false;
|
||||
|
||||
view_theme_reinit();
|
||||
bubbles_init();
|
||||
}
|
||||
|
||||
void view_deinit (void) {
|
||||
cursor_enabled = false;
|
||||
bubbles_deinit();
|
||||
}
|
||||
|
||||
void view_theme_reinit(void) {
|
||||
gfx_qe_entity(&entry_bg, theme_gfx[THEME_BACKGROUND],
|
||||
0, 0, -3, COL_DEFAULT);
|
||||
gfx_qe_entity(&entry_logo, theme_gfx[THEME_LOGO], 0, 416, 0, COL_DEFAULT);
|
||||
gfx_qe_entity(&entry_throbber, theme_gfx[THEME_THROBBER],
|
||||
(view_width - theme_gfx[THEME_THROBBER]->w) / 2,
|
||||
(view_height - theme_gfx[THEME_THROBBER]->h) / 2,
|
||||
0, COL_DEFAULT);
|
||||
}
|
||||
|
||||
view * view_new (u8 widget_count, const view *sub_view, s16 x, s16 y, s16 z,
|
||||
u32 drag_btn) {
|
||||
view *v;
|
||||
|
||||
v = (view *) pmalloc (sizeof (view));
|
||||
memset (v, 0, sizeof (view));
|
||||
|
||||
v->coords.x = x;
|
||||
v->coords.y = y;
|
||||
v->coords.z = z;
|
||||
gfx_qe_origin_push (&v->qe_coords_push, &v->coords);
|
||||
gfx_qe_origin_pop (&v->qe_coords_pop);
|
||||
|
||||
v->widget_count = widget_count;
|
||||
|
||||
v->widgets = (widget *) pmalloc (widget_count * sizeof (widget));
|
||||
memset (v->widgets, 0, widget_count * sizeof (widget));
|
||||
|
||||
v->sub_view = sub_view;
|
||||
|
||||
v->focus = -1;
|
||||
v->cursor = -1;
|
||||
|
||||
v->drag = false;
|
||||
v->drag_btn = drag_btn;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
void view_free (view *v) {
|
||||
u8 i;
|
||||
|
||||
if (!v)
|
||||
return;
|
||||
|
||||
for (i = 0; i < v->widget_count; ++i)
|
||||
widget_free(&v->widgets[i]);
|
||||
|
||||
free(v->widgets);
|
||||
free(v);
|
||||
}
|
||||
|
||||
static void view_push_view (const view *v, u32 subview_alpha) {
|
||||
int i, j;
|
||||
widget *w;
|
||||
widget_layer *l;
|
||||
u32 f;
|
||||
|
||||
if (v->sub_view) {
|
||||
view_push_view (v->sub_view, false);
|
||||
gfx_gen_gradient (&gradient_subview, view_width + 10,
|
||||
view_height + 10, subview_alpha,
|
||||
subview_alpha, subview_alpha, subview_alpha);
|
||||
|
||||
gfx_qe_entity (&entry_subview, &gradient_subview, -5, -5,
|
||||
v->coords.z - 1, COL_DEFAULT);
|
||||
|
||||
gfx_frame_push (&entry_subview, 1);
|
||||
}
|
||||
|
||||
gfx_frame_push (&v->qe_coords_push, 1);
|
||||
|
||||
for (i = 0; i < v->widget_count; ++i) {
|
||||
w = &v->widgets[i];
|
||||
|
||||
if (w->flags & WF_VISIBLE) {
|
||||
gfx_frame_push (&w->qe_coords_push, 1);
|
||||
for (j = 0; j < w->layer_count; ++j) {
|
||||
l = &w->layers[j];
|
||||
|
||||
if (l->flags) {
|
||||
f = w->flags ^ l->flags_invert;
|
||||
f = f & l->flags & WF_FLAGS_MASK;
|
||||
|
||||
if (!f)
|
||||
continue;
|
||||
|
||||
if (l->flags & WF_FLAGS_AND &&
|
||||
f != (l->flags & WF_FLAGS_MASK))
|
||||
continue;
|
||||
}
|
||||
|
||||
gfx_frame_push (w->layers[j].gfx_entries,
|
||||
w->layers[j].gfx_entry_count);
|
||||
}
|
||||
|
||||
gfx_frame_push (&w->qe_coords_pop, 1);
|
||||
}
|
||||
}
|
||||
|
||||
gfx_frame_push (&v->qe_coords_pop, 1);
|
||||
}
|
||||
|
||||
void view_plot (view *v, u32 alpha, u32 *down, u32 *held, u32 *up) {
|
||||
u32 bd, bh, bu;
|
||||
bool wm;
|
||||
s32 x, y;
|
||||
f32 roll;
|
||||
s8 w, c;
|
||||
cursor_type ct;
|
||||
#ifdef ENABLE_SCREENSHOTS
|
||||
u16 ss_x, ss_y;
|
||||
u32 *ss_buf = NULL;
|
||||
#endif
|
||||
|
||||
controls_scan (&bd, &bh, &bu);
|
||||
wm = controls_ir (&x, &y, &roll);
|
||||
w = view_widget_at_xy (v, x - v->coords.x, y - v->coords.y);
|
||||
ct = CUR_STD;
|
||||
|
||||
if (v->drag_btn) {
|
||||
if (bu & v->drag_btn)
|
||||
v->drag = false;
|
||||
|
||||
if ((w != -1) && (bd & v->drag_btn)) {
|
||||
v->drag = true;
|
||||
|
||||
v->drag_widget = w;
|
||||
|
||||
v->drag_start_x = x;
|
||||
v->drag_start_y = y;
|
||||
}
|
||||
|
||||
if (v->drag && (bh & v->drag_btn)) {
|
||||
v->drag_x = x - v->drag_start_x;
|
||||
v->drag_y = y - v->drag_start_y;
|
||||
|
||||
ct = v->widgets[v->drag_widget].cur;
|
||||
}
|
||||
}
|
||||
|
||||
c = v->cursor;
|
||||
|
||||
if (v->cursor != -1) {
|
||||
widget_set_flag (&v->widgets[v->cursor], WF_CURSOR, false);
|
||||
v->cursor = -1;
|
||||
}
|
||||
|
||||
wm = wm && cursor_enabled;
|
||||
|
||||
if (wm) {
|
||||
view_set_focus (v, -1);
|
||||
|
||||
if (w != -1) {
|
||||
if ((v->widgets[w].flags & CURSOR_FLAGS) == CURSOR_FLAGS) {
|
||||
widget_set_flag (&v->widgets[w], WF_CURSOR, true);
|
||||
v->cursor = w;
|
||||
|
||||
if (rumble_enabled && (c != w) && (ct != CUR_DRAG) &&
|
||||
(v->widgets[w].flags & WF_RUMBLE) &&
|
||||
(rumble_timeout == -RUMBLE_DELAY)) {
|
||||
rumble_timeout = RUMBLE_TIME;
|
||||
controls_rumble(1);
|
||||
}
|
||||
}
|
||||
|
||||
if ((v->widgets[w].flags & FOCUS_FLAGS) == FOCUS_FLAGS)
|
||||
view_set_focus (v, w);
|
||||
}
|
||||
}
|
||||
|
||||
gfx_frame_start ();
|
||||
|
||||
gfx_frame_push (&entry_bg, 1);
|
||||
|
||||
if (view_bubbles)
|
||||
bubble_update(wm, x, y);
|
||||
|
||||
gfx_frame_push (&entry_logo, 1);
|
||||
|
||||
view_push_view (v, alpha);
|
||||
|
||||
if (throbber_enabled)
|
||||
gfx_frame_push(&entry_throbber, 1);
|
||||
|
||||
if (wm) {
|
||||
if ((w != -1) && (ct != CUR_DRAG))
|
||||
ct = v->widgets[w].cur;
|
||||
|
||||
cursors_queue (cur, ct, x, y, roll);
|
||||
|
||||
gfx_frame_push (cur, 2);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_SCREENSHOTS
|
||||
// ZR + ZL on Classic Controller
|
||||
if ((bh & PADS_NET_INIT) && (bd & PADS_SCREENSHOT)) {
|
||||
gfx_get_efb_size(&ss_x, &ss_y);
|
||||
ss_buf = (u32 *) pmalloc(ss_x * ss_y * sizeof(u32));
|
||||
|
||||
gfx_set_efb_buffer(ss_buf);
|
||||
}
|
||||
#endif
|
||||
|
||||
gfx_frame_end ();
|
||||
|
||||
#ifdef ENABLE_SCREENSHOTS
|
||||
if (ss_buf) {
|
||||
save_rgba_png(ss_buf, ss_x, ss_y);
|
||||
free(ss_buf);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (down)
|
||||
*down = bd;
|
||||
|
||||
if (held)
|
||||
*held = bh;
|
||||
|
||||
if (up)
|
||||
*up = bu;
|
||||
|
||||
if (rumble_timeout > -RUMBLE_DELAY)
|
||||
rumble_timeout--;
|
||||
|
||||
if (rumble_timeout < 1)
|
||||
controls_rumble(0);
|
||||
}
|
||||
|
||||
void view_fade (view *v, s16 z, u32 c1, u32 c2, u32 c3, u32 c4, u8 steps,
|
||||
s8 modifier) {
|
||||
view *vf;
|
||||
u8 i;
|
||||
|
||||
vf = view_new (1, v, 0, 0, 0, 0);
|
||||
|
||||
widget_gradient (&vf->widgets[0], -32, -32, z, view_width + 64,
|
||||
view_height + 64, c1, c2, c3, c4);
|
||||
|
||||
for (i = 0; i < steps; ++i) {
|
||||
widget_fade_gradient (&vf->widgets[0], modifier);
|
||||
|
||||
view_plot (vf, 0, NULL, NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void view_set_focus (view *v, s8 new_focus) {
|
||||
if (v->focus != -1)
|
||||
v->widgets[v->focus].flags &= ~((u32) WF_FOCUSED);
|
||||
|
||||
v->focus = new_focus;
|
||||
|
||||
if (v->focus != -1)
|
||||
v->widgets[v->focus].flags |= WF_FOCUSED;
|
||||
}
|
||||
|
||||
u8 view_set_focus_prev (view *v) {
|
||||
s16 i;
|
||||
|
||||
if (v->focus < 1)
|
||||
return v->focus;
|
||||
|
||||
for (i = v->focus - 1; i >= 0; --i)
|
||||
if ((v->widgets[i].flags & FOCUS_FLAGS) == FOCUS_FLAGS) {
|
||||
view_set_focus (v, i);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return v->focus;
|
||||
}
|
||||
|
||||
u8 view_set_focus_next (view *v) {
|
||||
u8 i;
|
||||
|
||||
if (v->focus == v->widget_count - 1)
|
||||
return v->focus;
|
||||
|
||||
for (i = v->focus + 1; i < v->widget_count; ++i)
|
||||
if ((v->widgets[i].flags & FOCUS_FLAGS) == FOCUS_FLAGS) {
|
||||
view_set_focus (v, i);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return v->focus;
|
||||
}
|
||||
|
||||
u8 view_move_focus(view *v, s8 mod) {
|
||||
if (v->focus + mod < 1)
|
||||
return v->focus;
|
||||
|
||||
if (v->focus + mod > v->widget_count - 1)
|
||||
return v->focus;
|
||||
|
||||
if ((v->widgets[v->focus + mod].flags & FOCUS_FLAGS) == FOCUS_FLAGS)
|
||||
view_set_focus (v, v->focus + mod);
|
||||
|
||||
return v->focus;
|
||||
}
|
||||
|
||||
void view_enable_cursor (bool enable) {
|
||||
cursor_enabled = enable;
|
||||
}
|
||||
|
||||
s8 view_widget_at_xy (const view *v, s32 x, s32 y) {
|
||||
s16 i;
|
||||
widget *w;
|
||||
s16 wx, wy;
|
||||
|
||||
for (i = v->widget_count - 1; i >= 0; --i) {
|
||||
w = &v->widgets[i];
|
||||
|
||||
if (w->type == WT_MEMO) {
|
||||
wx = w->coords.x;
|
||||
wy = w->memo.y_max;
|
||||
} else {
|
||||
wx = w->coords.x;
|
||||
wy = w->coords.y;
|
||||
}
|
||||
|
||||
if (w->width && w->height && (x >= wx) && (y >= wy) &&
|
||||
(x <= wx + w->width) && (y <= wy + w->height))
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
s8 view_widget_at_ir (const view *v) {
|
||||
s32 x, y;
|
||||
|
||||
if (!cursor_enabled || !controls_ir (&x, &y, NULL))
|
||||
return -1;
|
||||
|
||||
return view_widget_at_xy (v, x - v->coords.x, y - v->coords.y);
|
||||
}
|
||||
|
||||
void view_show_throbber(bool show) {
|
||||
entry_throbber.entity.rad = 0;
|
||||
throbber_enabled = show;
|
||||
}
|
||||
|
||||
void view_throbber_tickle(void) {
|
||||
entry_throbber.entity.rad -= 0.1f;
|
||||
}
|
||||
|
||||
54
channel/channelapp/source/view.h
Normal file
54
channel/channelapp/source/view.h
Normal file
@@ -0,0 +1,54 @@
|
||||
#ifndef _VIEW_H_
|
||||
#define _VIEW_H_
|
||||
|
||||
#include <gctypes.h>
|
||||
|
||||
#include "widgets.h"
|
||||
|
||||
typedef struct _view {
|
||||
gfx_coordinates coords;
|
||||
gfx_queue_entry qe_coords_push, qe_coords_pop;
|
||||
|
||||
u8 widget_count;
|
||||
widget *widgets;
|
||||
|
||||
const struct _view *sub_view;
|
||||
|
||||
s8 focus;
|
||||
s8 cursor;
|
||||
|
||||
bool drag;
|
||||
s8 drag_widget;
|
||||
u32 drag_btn;
|
||||
s32 drag_start_x, drag_start_y, drag_x, drag_y;
|
||||
} view;
|
||||
|
||||
extern bool view_bubbles;
|
||||
extern bool allow_screenshot;
|
||||
|
||||
void view_init ();
|
||||
void view_deinit ();
|
||||
void view_theme_reinit(void);
|
||||
|
||||
view * view_new (u8 widget_count, const view *sub_view, s16 x, s16 y, s16 z,
|
||||
u32 drag_btn);
|
||||
void view_free (view *v);
|
||||
|
||||
void view_plot (view *v, u32 alpha, u32 *down, u32 *held, u32 *up);
|
||||
void view_fade (view *v, s16 z, u32 c1, u32 c2, u32 c3, u32 c4, u8 steps,
|
||||
s8 modifier);
|
||||
|
||||
void view_set_focus (view *v, s8 new_focus);
|
||||
u8 view_set_focus_prev (view *v);
|
||||
u8 view_set_focus_next (view *v);
|
||||
u8 view_move_focus(view *v, s8 mod);
|
||||
|
||||
void view_enable_cursor (bool enable);
|
||||
s8 view_widget_at_xy (const view *v, s32 x, s32 y);
|
||||
s8 view_widget_at_ir (const view *v);
|
||||
|
||||
void view_show_throbber(bool show);
|
||||
void view_throbber_tickle(void);
|
||||
|
||||
#endif
|
||||
|
||||
613
channel/channelapp/source/widgets.c
Normal file
613
channel/channelapp/source/widgets.c
Normal file
@@ -0,0 +1,613 @@
|
||||
#include <malloc.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "../config.h"
|
||||
#include "theme.h"
|
||||
#include "i18n.h"
|
||||
#include "panic.h"
|
||||
|
||||
#include "widgets.h"
|
||||
|
||||
static const char *app_entry_default_description;
|
||||
|
||||
void widgets_theme_reinit (void) {
|
||||
app_entry_default_description = _("<no description>");
|
||||
}
|
||||
|
||||
void widgets_init (void) {
|
||||
widgets_theme_reinit();
|
||||
}
|
||||
|
||||
void widgets_deinit (void) {
|
||||
}
|
||||
|
||||
void widget_free (widget *w) {
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < w->layer_count; ++i)
|
||||
free (w->layers[i].gfx_entries);
|
||||
|
||||
switch (w->type) {
|
||||
case WT_LABEL:
|
||||
case WT_BUTTON:
|
||||
case WT_APP_ENTRY:
|
||||
case WT_MEMO:
|
||||
case WT_IMAGE:
|
||||
break;
|
||||
|
||||
case WT_PROGRESS:
|
||||
free (w->progress.gradient);
|
||||
break;
|
||||
|
||||
case WT_GRADIENT:
|
||||
free (w->gradient.gradient);
|
||||
break;
|
||||
}
|
||||
|
||||
free (w->layers);
|
||||
|
||||
memset(w, 0, sizeof(widget));
|
||||
}
|
||||
|
||||
void widget_set_flag (widget *w, u32 flag, bool set) {
|
||||
if (set)
|
||||
w->flags |= flag;
|
||||
else
|
||||
w->flags &= ~((u32) flag);
|
||||
}
|
||||
|
||||
void widget_toggle_flag (widget *w, u32 flag) {
|
||||
widget_set_flag (w, flag, (w->flags & flag) ? false : true);
|
||||
}
|
||||
|
||||
void widget_set_progress (widget *w, u32 progress) {
|
||||
float m = 0;
|
||||
char p[5];
|
||||
|
||||
if (progress > 0)
|
||||
m = (float) progress / w->progress.max;
|
||||
|
||||
if (m > 1)
|
||||
m = 1;
|
||||
|
||||
gfx_gen_gradient (w->progress.gradient, roundf ((400 - 36 * 2 + 2) * m),
|
||||
112 - 36 * 2 + 2, theme.progress.ul, theme.progress.ur,
|
||||
theme.progress.lr, theme.progress.ll);
|
||||
gfx_qe_entity (&w->layers[0].gfx_entries[0], w->progress.gradient, 35, 35,
|
||||
0, COL_DEFAULT);
|
||||
|
||||
sprintf (p, "%3.f%%", roundf (100.0 * m));
|
||||
font_plot_string (&w->layers[2].gfx_entries[0], 4, FONT_LABEL, p,
|
||||
32, theme_gfx[THEME_PROGRESS]->h - 20, 2,
|
||||
theme_gfx[THEME_PROGRESS]->w - 70,
|
||||
FA_RIGHT, FA_EM_CENTERED);
|
||||
}
|
||||
|
||||
#define CCLAMP(x) (((x)>255)?255:(((x)<0)?0:(x)))
|
||||
|
||||
void widget_fade_gradient (widget *w, s8 modifier) {
|
||||
w->layers[0].gfx_entries[0].entity.entity->gradient.c1 =
|
||||
CCLAMP(w->layers[0].gfx_entries[0].entity.entity->gradient.c1 + modifier);
|
||||
w->layers[0].gfx_entries[0].entity.entity->gradient.c2 =
|
||||
CCLAMP(w->layers[0].gfx_entries[0].entity.entity->gradient.c2 + modifier);
|
||||
w->layers[0].gfx_entries[0].entity.entity->gradient.c3 =
|
||||
CCLAMP(w->layers[0].gfx_entries[0].entity.entity->gradient.c3 + modifier);
|
||||
w->layers[0].gfx_entries[0].entity.entity->gradient.c4 =
|
||||
CCLAMP(w->layers[0].gfx_entries[0].entity.entity->gradient.c4 + modifier);
|
||||
}
|
||||
|
||||
bool widget_scroll_memo (widget *w, s16 modifier) {
|
||||
s16 y;
|
||||
|
||||
y = w->coords.y + modifier;
|
||||
|
||||
if (y < w->memo.y_min)
|
||||
y = w->memo.y_min;
|
||||
|
||||
if (y > w->memo.y_max)
|
||||
y = w->memo.y_max;
|
||||
|
||||
if (w->coords.y == y)
|
||||
return false;
|
||||
|
||||
w->coords.y = y;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool widget_scroll_memo_deco (widget *w, s16 modifier) {
|
||||
bool res;
|
||||
|
||||
res = widget_scroll_memo (&w[1], modifier);
|
||||
|
||||
widget_set_flag (&w[0], WF_VISIBLE, w[1].coords.y < w[1].memo.y_max);
|
||||
widget_set_flag (&w[2], WF_VISIBLE, w[1].coords.y > w[1].memo.y_min);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static void widget_init (widget *w, widget_type type, u32 focusable,
|
||||
s16 x, s16 y, s16 z, u16 width, u16 height,
|
||||
int layer_count) {
|
||||
w->type = type;
|
||||
w->flags = WF_VISIBLE | WF_ENABLED;
|
||||
|
||||
w->width = width;
|
||||
w->height = height;
|
||||
|
||||
w->cur = CUR_STD;
|
||||
|
||||
if (focusable)
|
||||
w->flags |= WF_FOCUSABLE;
|
||||
|
||||
w->coords.x = x;
|
||||
w->coords.y = y;
|
||||
w->coords.z = TEX_LAYER_WIDGETS + z;
|
||||
gfx_qe_origin_push (&w->qe_coords_push, &w->coords);
|
||||
gfx_qe_origin_pop (&w->qe_coords_pop);
|
||||
|
||||
w->layer_count = layer_count;
|
||||
w->layers = (widget_layer *)
|
||||
pmalloc (w->layer_count * sizeof (widget_layer));
|
||||
}
|
||||
|
||||
void widget_label (widget *w, s16 x, s16 y, s16 z, const char *caption,
|
||||
u16 width, font_xalign xalign, font_yalign yalign,
|
||||
font_id font) {
|
||||
widget_layer *l;
|
||||
|
||||
widget_init (w, WT_LABEL, false, x, y, z, 0, 0, 1);
|
||||
|
||||
l = &w->layers[0];
|
||||
l->flags = 0;
|
||||
l->gfx_entry_count = font_get_char_count (font, caption, width);
|
||||
l->gfx_entries = (gfx_queue_entry *)
|
||||
pmalloc (l->gfx_entry_count * sizeof (gfx_queue_entry));
|
||||
font_plot_string (&l->gfx_entries[0], l->gfx_entry_count, font, caption,
|
||||
0, 0, 0, width, xalign, yalign);
|
||||
}
|
||||
|
||||
void widget_image(widget *w, s16 x, s16 y, s16 z, gfx_entity *image,
|
||||
gfx_entity *image_disabled, bool rumble,
|
||||
gfx_entity *image_cursor) {
|
||||
int c;
|
||||
widget_layer *l;
|
||||
|
||||
c = 1;
|
||||
|
||||
if (image_disabled)
|
||||
c++;
|
||||
|
||||
if (image_cursor)
|
||||
c++;
|
||||
|
||||
widget_init (w, WT_IMAGE, false, x, y, z, image->w, image->h, c);
|
||||
|
||||
if (rumble)
|
||||
w->flags |= WF_RUMBLE;
|
||||
|
||||
c = 0;
|
||||
|
||||
l = &w->layers[c];
|
||||
if(image_cursor) {
|
||||
l->flags = WF_ENABLED | WF_FLAGS_AND | WF_CURSOR;
|
||||
l->flags_invert = WF_CURSOR;
|
||||
} else {
|
||||
l->flags = WF_ENABLED;
|
||||
l->flags_invert = 0;
|
||||
}
|
||||
|
||||
l->gfx_entry_count = 1;
|
||||
l->gfx_entries = (gfx_queue_entry *)
|
||||
pmalloc (l->gfx_entry_count * sizeof (gfx_queue_entry));
|
||||
gfx_qe_entity (&l->gfx_entries[0], image, 0, 0, 0, COL_DEFAULT);
|
||||
|
||||
c++;
|
||||
|
||||
if (image_disabled) {
|
||||
l = &w->layers[c];
|
||||
l->flags = WF_ENABLED;
|
||||
l->flags_invert = WF_ENABLED;
|
||||
l->gfx_entry_count = 1;
|
||||
l->gfx_entries = (gfx_queue_entry *) pmalloc (l->gfx_entry_count *
|
||||
sizeof (gfx_queue_entry));
|
||||
gfx_qe_entity (&l->gfx_entries[0], image_disabled, 0, 0, 1, COL_DEFAULT);
|
||||
|
||||
c++;
|
||||
}
|
||||
|
||||
if (image_cursor) {
|
||||
l = &w->layers[c];
|
||||
l->flags = WF_ENABLED | WF_FLAGS_AND | WF_CURSOR;
|
||||
l->flags_invert = 0;
|
||||
l->gfx_entry_count = 1;
|
||||
l->gfx_entries = (gfx_queue_entry *) pmalloc (l->gfx_entry_count *
|
||||
sizeof (gfx_queue_entry));
|
||||
gfx_qe_entity (&l->gfx_entries[0], image_cursor, 0, 0, 2, COL_DEFAULT);
|
||||
}
|
||||
}
|
||||
|
||||
void widget_button (widget *w, s16 x, s16 y, s16 z, button_size size,
|
||||
const char *caption) {
|
||||
gfx_entity *tex, *tex_focus;
|
||||
widget_layer *l;
|
||||
|
||||
switch (size) {
|
||||
case BTN_SMALL:
|
||||
tex = theme_gfx[THEME_BUTTON_SMALL];
|
||||
tex_focus = theme_gfx[THEME_BUTTON_SMALL_FOCUS];
|
||||
break;
|
||||
|
||||
case BTN_TINY:
|
||||
tex = theme_gfx[THEME_BUTTON_TINY];
|
||||
tex_focus = theme_gfx[THEME_BUTTON_TINY_FOCUS];
|
||||
break;
|
||||
|
||||
default:
|
||||
tex = theme_gfx[THEME_BUTTON];
|
||||
tex_focus = theme_gfx[THEME_BUTTON_FOCUS];
|
||||
break;
|
||||
}
|
||||
|
||||
widget_init (w, WT_BUTTON, true, x, y, z, tex->w, tex->h, 4);
|
||||
|
||||
w->flags |= WF_RUMBLE;
|
||||
|
||||
l = &w->layers[0];
|
||||
l->flags = WF_FOCUSED | WF_FLAGS_AND | WF_ENABLED;
|
||||
l->flags_invert = WF_FOCUSED;
|
||||
l->gfx_entry_count = 1;
|
||||
l->gfx_entries = (gfx_queue_entry *)
|
||||
pmalloc (l->gfx_entry_count * sizeof (gfx_queue_entry));
|
||||
|
||||
gfx_qe_entity (&l->gfx_entries[0], tex, 0, 0, 0, COL_DEFAULT);
|
||||
|
||||
l = &w->layers[1];
|
||||
l->flags = WF_ENABLED;
|
||||
l->flags_invert = WF_ENABLED;
|
||||
l->gfx_entry_count = 1;
|
||||
l->gfx_entries = (gfx_queue_entry *)
|
||||
pmalloc (l->gfx_entry_count * sizeof (gfx_queue_entry));
|
||||
|
||||
gfx_qe_entity (&l->gfx_entries[0], tex, 0, 0, 0, WIDGET_DISABLED_COLOR);
|
||||
|
||||
l = &w->layers[2];
|
||||
l->flags = WF_FOCUSED | WF_FLAGS_AND | WF_ENABLED;
|
||||
l->flags_invert = 0;
|
||||
l->gfx_entry_count = 1;
|
||||
l->gfx_entries = (gfx_queue_entry *)
|
||||
pmalloc (l->gfx_entry_count * sizeof (gfx_queue_entry));
|
||||
gfx_qe_entity (&l->gfx_entries[0], tex_focus, 0, 0, 0, COL_DEFAULT);
|
||||
|
||||
l = &w->layers[3];
|
||||
l->flags = 0;
|
||||
l->flags_invert = 0;
|
||||
l->gfx_entry_count = 0;
|
||||
l->gfx_entries = NULL;
|
||||
|
||||
widget_button_set_caption(w, FONT_BUTTON, caption);
|
||||
}
|
||||
|
||||
void widget_button_set_caption(widget *w, font_id font, const char *caption) {
|
||||
int c;
|
||||
u16 bw, bh, oy;
|
||||
widget_layer *l;
|
||||
|
||||
l = &w->layers[3];
|
||||
free(l->gfx_entries);
|
||||
l->gfx_entry_count = 0;
|
||||
l->gfx_entries = NULL;
|
||||
|
||||
if (!caption)
|
||||
return;
|
||||
|
||||
bw = w->layers[0].gfx_entries[0].entity.entity->w;
|
||||
bh = w->layers[0].gfx_entries[0].entity.entity->h;
|
||||
|
||||
c = font_get_char_count(font, caption, bw - 32);
|
||||
|
||||
oy = bh/2;
|
||||
|
||||
l->gfx_entry_count = c;
|
||||
l->gfx_entries = (gfx_queue_entry *)
|
||||
pmalloc (l->gfx_entry_count * sizeof (gfx_queue_entry));
|
||||
|
||||
font_plot_string (&l->gfx_entries[0], c, font, caption, 16, oy, 1,
|
||||
bw - 32, FA_CENTERED, FA_EM_CENTERED);
|
||||
}
|
||||
|
||||
void widget_grid_app_entry(widget *w, s16 x, s16 y, s16 z,
|
||||
const app_entry *entry) {
|
||||
|
||||
widget_layer *l;
|
||||
|
||||
if (entry->icon)
|
||||
widget_init(w, WT_APP_ENTRY, true, x, y, z,
|
||||
theme_gfx[THEME_GRID_APP_ENTRY]->w,
|
||||
theme_gfx[THEME_GRID_APP_ENTRY]->h, 4);
|
||||
else
|
||||
widget_init(w, WT_APP_ENTRY, true, x, y, z,
|
||||
theme_gfx[THEME_GRID_APP_ENTRY]->w,
|
||||
theme_gfx[THEME_GRID_APP_ENTRY]->h, 3);
|
||||
|
||||
// Enable rumbling for this widget
|
||||
w->flags |= WF_RUMBLE;
|
||||
|
||||
l = &w->layers[0];
|
||||
l->flags = WF_FOCUSED | WF_FLAGS_AND | WF_ENABLED;
|
||||
l->flags_invert = WF_FOCUSED;
|
||||
l->gfx_entry_count = 1;
|
||||
l->gfx_entries = (gfx_queue_entry *)
|
||||
pmalloc (l->gfx_entry_count * sizeof (gfx_queue_entry));
|
||||
gfx_qe_entity (&l->gfx_entries[0], theme_gfx[THEME_GRID_APP_ENTRY],
|
||||
0, 0, 0, COL_DEFAULT);
|
||||
|
||||
l = &w->layers[1];
|
||||
l->flags = WF_ENABLED;
|
||||
l->flags_invert = WF_ENABLED;
|
||||
l->gfx_entry_count = 1;
|
||||
l->gfx_entries = (gfx_queue_entry *)
|
||||
pmalloc (l->gfx_entry_count * sizeof (gfx_queue_entry));
|
||||
|
||||
gfx_qe_entity (&l->gfx_entries[0], theme_gfx[THEME_GRID_APP_ENTRY],
|
||||
0, 0, 0, WIDGET_DISABLED_COLOR);
|
||||
|
||||
l = &w->layers[2];
|
||||
l->flags = WF_FOCUSED | WF_FLAGS_AND | WF_ENABLED;
|
||||
l->flags_invert = 0;
|
||||
l->gfx_entry_count = 1;
|
||||
l->gfx_entries = (gfx_queue_entry *)
|
||||
pmalloc (l->gfx_entry_count * sizeof (gfx_queue_entry));
|
||||
gfx_qe_entity (&l->gfx_entries[0], theme_gfx[THEME_GRID_APP_ENTRY_FOCUS],
|
||||
0, 0, 0, COL_DEFAULT);
|
||||
|
||||
if (entry->icon) {
|
||||
l = &w->layers[3];
|
||||
l->flags = 0;
|
||||
l->flags_invert = 0;
|
||||
l->gfx_entry_count = 1;
|
||||
|
||||
l->gfx_entry_count = 1;
|
||||
l->gfx_entries = (gfx_queue_entry *)
|
||||
pmalloc (l->gfx_entry_count * sizeof (gfx_queue_entry));
|
||||
|
||||
gfx_qe_entity (&l->gfx_entries[0], entry->icon, GRID_APP_ENTRY_ICON_X,
|
||||
APP_ENTRY_ICON_Y, 1, COL_DEFAULT);
|
||||
}
|
||||
}
|
||||
|
||||
void widget_app_entry (widget *w, s16 x, s16 y, s16 z, const app_entry *entry) {
|
||||
const char *line1, *line2;
|
||||
int l1, l2;
|
||||
|
||||
widget_layer *l;
|
||||
|
||||
widget_init (w, WT_APP_ENTRY, true, x, y, z, theme_gfx[THEME_APP_ENTRY]->w,
|
||||
theme_gfx[THEME_APP_ENTRY]->h, 4);
|
||||
|
||||
w->flags |= WF_RUMBLE;
|
||||
|
||||
l = &w->layers[0];
|
||||
l->flags = WF_FOCUSED | WF_FLAGS_AND | WF_ENABLED;
|
||||
l->flags_invert = WF_FOCUSED;
|
||||
l->gfx_entry_count = 1;
|
||||
l->gfx_entries = (gfx_queue_entry *)
|
||||
pmalloc (l->gfx_entry_count * sizeof (gfx_queue_entry));
|
||||
gfx_qe_entity (&l->gfx_entries[0], theme_gfx[THEME_APP_ENTRY],
|
||||
0, 0, 0, COL_DEFAULT);
|
||||
|
||||
l = &w->layers[1];
|
||||
l->flags = WF_ENABLED;
|
||||
l->flags_invert = WF_ENABLED;
|
||||
l->gfx_entry_count = 1;
|
||||
l->gfx_entries = (gfx_queue_entry *)
|
||||
pmalloc (l->gfx_entry_count * sizeof (gfx_queue_entry));
|
||||
|
||||
gfx_qe_entity (&l->gfx_entries[0], theme_gfx[THEME_APP_ENTRY],
|
||||
0, 0, 0, WIDGET_DISABLED_COLOR);
|
||||
|
||||
l = &w->layers[2];
|
||||
l->flags = WF_FOCUSED | WF_FLAGS_AND | WF_ENABLED;
|
||||
l->flags_invert = 0;
|
||||
l->gfx_entry_count = 1;
|
||||
l->gfx_entries = (gfx_queue_entry *)
|
||||
pmalloc (l->gfx_entry_count * sizeof (gfx_queue_entry));
|
||||
gfx_qe_entity (&l->gfx_entries[0], theme_gfx[THEME_APP_ENTRY_FOCUS],
|
||||
0, 0, 0, COL_DEFAULT);
|
||||
|
||||
line1 = NULL;
|
||||
line2 = NULL;
|
||||
|
||||
if (entry->meta) {
|
||||
line1 = entry->meta->name;
|
||||
line2 = entry->meta->short_description;
|
||||
}
|
||||
|
||||
if (!line1)
|
||||
line1 = entry->dirname;
|
||||
|
||||
if (!line2)
|
||||
line2 = app_entry_default_description;
|
||||
|
||||
l1 = font_get_char_count(FONT_APPNAME, line1,
|
||||
theme_gfx[THEME_APP_ENTRY]->w -
|
||||
APP_ENTRY_TEXT1_X - 16);
|
||||
l2 = font_get_char_count(FONT_APPDESC, line2,
|
||||
theme_gfx[THEME_APP_ENTRY]->w -
|
||||
APP_ENTRY_TEXT2_X - 16);
|
||||
|
||||
l = &w->layers[3];
|
||||
l->flags = 0;
|
||||
l->flags_invert = 0;
|
||||
l->gfx_entry_count = l1 + l2;
|
||||
|
||||
if (entry->icon)
|
||||
l->gfx_entry_count++;
|
||||
|
||||
l->gfx_entries = (gfx_queue_entry *)
|
||||
pmalloc (l->gfx_entry_count * sizeof (gfx_queue_entry));
|
||||
font_plot_string (&l->gfx_entries[0], l1, FONT_APPNAME, line1,
|
||||
APP_ENTRY_TEXT1_X, APP_ENTRY_TEXT1_Y, 1,
|
||||
theme_gfx[THEME_APP_ENTRY]->w - APP_ENTRY_TEXT1_X -
|
||||
16, FA_LEFT, FA_ASCENDER);
|
||||
font_plot_string (&l->gfx_entries[l1], l2, FONT_APPDESC, line2,
|
||||
APP_ENTRY_TEXT2_X, APP_ENTRY_TEXT2_Y, 1,
|
||||
theme_gfx[THEME_APP_ENTRY]->w - APP_ENTRY_TEXT2_X -
|
||||
16, FA_LEFT, FA_DESCENDER);
|
||||
|
||||
if (entry->icon)
|
||||
gfx_qe_entity (&l->gfx_entries[l1 + l2], entry->icon,
|
||||
APP_ENTRY_ICON_X, APP_ENTRY_ICON_Y, 1, COL_DEFAULT);
|
||||
}
|
||||
|
||||
void widget_progress (widget *w, s16 x, s16 y, s16 z, const char *caption,
|
||||
u32 max) {
|
||||
widget_layer *l;
|
||||
|
||||
widget_init (w, WT_PROGRESS, false, x, y, z, 0, 0, 3);
|
||||
|
||||
l = &w->layers[0];
|
||||
l->flags = 0;
|
||||
l->flags_invert = 0;
|
||||
l->gfx_entry_count = 1;
|
||||
l->gfx_entries = (gfx_queue_entry *)
|
||||
pmalloc (l->gfx_entry_count * sizeof (gfx_queue_entry));
|
||||
|
||||
l = &w->layers[1];
|
||||
l->flags = 0;
|
||||
l->flags_invert = 0;
|
||||
l->gfx_entry_count = 1;
|
||||
l->gfx_entries = (gfx_queue_entry *)
|
||||
pmalloc (l->gfx_entry_count * sizeof (gfx_queue_entry));
|
||||
gfx_qe_entity (&l->gfx_entries[0], theme_gfx[THEME_PROGRESS],
|
||||
0, 0, 1, COL_DEFAULT);
|
||||
|
||||
int chars = font_get_char_count(FONT_LABEL, caption,
|
||||
theme_gfx[THEME_PROGRESS]->w - 64);
|
||||
|
||||
l = &w->layers[2];
|
||||
l->flags = 0;
|
||||
l->flags_invert = 0;
|
||||
l->gfx_entry_count = 4 + chars;
|
||||
l->gfx_entries = (gfx_queue_entry *)
|
||||
pmalloc (l->gfx_entry_count * sizeof (gfx_queue_entry));
|
||||
font_plot_string (&l->gfx_entries[4], chars, FONT_LABEL, caption, 38, 19, 2,
|
||||
theme_gfx[THEME_PROGRESS]->w - 64, FA_LEFT, FA_EM_CENTERED);
|
||||
|
||||
w->progress.max = max;
|
||||
w->progress.gradient = (gfx_entity *) pmalloc (sizeof (gfx_entity));
|
||||
}
|
||||
|
||||
void widget_gradient (widget *w, s16 x, s16 y, s16 z,
|
||||
u16 width, u16 height,
|
||||
u32 c1, u32 c2, u32 c3,
|
||||
u32 c4) {
|
||||
widget_layer *l;
|
||||
|
||||
widget_init (w, WT_GRADIENT, false, x, y, z, 0, 0, 1);
|
||||
|
||||
w->gradient.gradient = (gfx_entity *) pmalloc (sizeof (gfx_entity));
|
||||
gfx_gen_gradient (w->gradient.gradient, width, height, c1, c2, c3, c4);
|
||||
|
||||
l = &w->layers[0];
|
||||
l->flags = 0;
|
||||
l->flags_invert = 0;
|
||||
l->gfx_entry_count = 1;
|
||||
l->gfx_entries = (gfx_queue_entry *)
|
||||
pmalloc (l->gfx_entry_count * sizeof (gfx_queue_entry));
|
||||
|
||||
gfx_qe_entity (&l->gfx_entries[0], w->gradient.gradient, 0, 0, 0,
|
||||
COL_DEFAULT);
|
||||
}
|
||||
|
||||
void widget_memo (widget *w, s16 x, s16 y, s16 z, u16 width, u16 height,
|
||||
const char *text, font_xalign align) {
|
||||
widget_layer *l;
|
||||
int hf;
|
||||
int count, i, c;
|
||||
char **lines;
|
||||
int cl, ot;
|
||||
int oy;
|
||||
|
||||
hf = font_get_y_spacing(FONT_MEMO);
|
||||
|
||||
count = font_wrap_string (&lines, FONT_MEMO, text, width);
|
||||
|
||||
c = 0;
|
||||
for (i = 0; i < count; ++i)
|
||||
if (lines[i])
|
||||
c++;
|
||||
|
||||
ot = 0;
|
||||
cl = (height + hf - 1) / hf;
|
||||
if (cl > count)
|
||||
ot = ((cl - count) / 2) * hf;
|
||||
|
||||
widget_init (w, WT_MEMO, false, x, y + ot, z, width, height, c + 2);
|
||||
|
||||
if (c > cl)
|
||||
w->cur = CUR_DRAG;
|
||||
|
||||
l = &w->layers[0];
|
||||
l->flags = 0;
|
||||
l->flags_invert = 0;
|
||||
l->gfx_entry_count = 1;
|
||||
l->gfx_entries = (gfx_queue_entry *)
|
||||
pmalloc (l->gfx_entry_count * sizeof (gfx_queue_entry));
|
||||
|
||||
gfx_qe_scissor (&l->gfx_entries[0], x, y, z, width, height);
|
||||
|
||||
c = 1;
|
||||
oy = 0;
|
||||
|
||||
w->memo.y_max = w->coords.y;
|
||||
|
||||
for (i = 0; i < count; ++i) {
|
||||
if (!lines[i]) {
|
||||
oy += hf;
|
||||
continue;
|
||||
}
|
||||
|
||||
l = &w->layers[c];
|
||||
l->flags = 0;
|
||||
l->flags_invert = 0;
|
||||
l->gfx_entry_count = font_get_char_count(FONT_MEMO, lines[i], 0);
|
||||
l->gfx_entries = (gfx_queue_entry *) pmalloc (l->gfx_entry_count *
|
||||
sizeof (gfx_queue_entry));
|
||||
font_plot_string (&l->gfx_entries[0], l->gfx_entry_count, FONT_MEMO,
|
||||
lines[i], 0, oy, 0, width, align, FA_ASCENDER);
|
||||
c++;
|
||||
oy += hf;
|
||||
}
|
||||
|
||||
font_free_lines (lines, count);
|
||||
|
||||
l = &w->layers[c];
|
||||
l->flags = 0;
|
||||
l->flags_invert = 0;
|
||||
l->gfx_entry_count = 1;
|
||||
l->gfx_entries = (gfx_queue_entry *)
|
||||
pmalloc (l->gfx_entry_count * sizeof (gfx_queue_entry));
|
||||
|
||||
gfx_qe_scissor_reset (&l->gfx_entries[0]);
|
||||
|
||||
w->memo.y_min = w->memo.y_max - oy + height;
|
||||
}
|
||||
|
||||
void widget_memo_deco (widget *w, s16 x, s16 y, s16 z, u16 width, u16 height,
|
||||
const char *text, font_xalign align) {
|
||||
widget_image (&w[0], x + (width - theme_gfx[THEME_CONTENT_ARROW_UP]->w) / 2,
|
||||
y, z, theme_gfx[THEME_CONTENT_ARROW_UP], NULL, false, NULL);
|
||||
widget_memo (&w[1], x, y + theme_gfx[THEME_CONTENT_ARROW_UP]->h + 8, z,
|
||||
width, height - theme_gfx[THEME_CONTENT_ARROW_UP]->h -
|
||||
8 - theme_gfx[THEME_CONTENT_ARROW_DOWN]->h - 8, text,
|
||||
align);
|
||||
widget_image (&w[2],
|
||||
x + (width - theme_gfx[THEME_CONTENT_ARROW_DOWN]->w) / 2,
|
||||
y + height - theme_gfx[THEME_CONTENT_ARROW_DOWN]->h, z,
|
||||
theme_gfx[THEME_CONTENT_ARROW_DOWN], NULL, false, NULL);
|
||||
widget_scroll_memo_deco (w, 0);
|
||||
}
|
||||
|
||||
122
channel/channelapp/source/widgets.h
Normal file
122
channel/channelapp/source/widgets.h
Normal file
@@ -0,0 +1,122 @@
|
||||
#ifndef _WIDGETS_H_
|
||||
#define _WIDGETS_H_
|
||||
|
||||
#include <gctypes.h>
|
||||
|
||||
#include "gfx.h"
|
||||
#include "cursors.h"
|
||||
#include "appentry.h"
|
||||
#include "font.h"
|
||||
|
||||
#define WF_VISIBLE 1
|
||||
#define WF_ENABLED 2
|
||||
#define WF_FOCUSABLE 4
|
||||
#define WF_RUMBLE 8
|
||||
|
||||
#define WF_CURSOR 32
|
||||
#define WF_FOCUSED 64
|
||||
|
||||
#define WF_FLAGS_MASK 0xff
|
||||
#define WF_FLAGS_OR 0
|
||||
#define WF_FLAGS_AND 256
|
||||
|
||||
typedef enum {
|
||||
WT_LABEL,
|
||||
WT_IMAGE,
|
||||
WT_BUTTON,
|
||||
WT_APP_ENTRY,
|
||||
WT_PROGRESS,
|
||||
WT_GRADIENT,
|
||||
WT_MEMO
|
||||
} widget_type;
|
||||
|
||||
typedef struct {
|
||||
u32 flags;
|
||||
u32 flags_invert;
|
||||
u16 gfx_entry_count;
|
||||
gfx_queue_entry *gfx_entries;
|
||||
} widget_layer;
|
||||
|
||||
typedef struct {
|
||||
widget_type type;
|
||||
u32 flags;
|
||||
u16 width, height;
|
||||
|
||||
cursor_type cur;
|
||||
|
||||
gfx_coordinates coords;
|
||||
gfx_queue_entry qe_coords_push, qe_coords_pop;
|
||||
|
||||
int layer_count;
|
||||
widget_layer *layers;
|
||||
|
||||
union {
|
||||
struct {
|
||||
gfx_entity *mask;
|
||||
} image;
|
||||
|
||||
struct {
|
||||
u32 max;
|
||||
gfx_entity *gradient;
|
||||
} progress;
|
||||
|
||||
struct {
|
||||
gfx_entity *gradient;
|
||||
} gradient;
|
||||
|
||||
struct {
|
||||
s16 y_min, y_max;
|
||||
} memo;
|
||||
};
|
||||
} widget;
|
||||
|
||||
typedef enum {
|
||||
BTN_NORMAL = 0,
|
||||
BTN_SMALL,
|
||||
BTN_TINY
|
||||
} button_size;
|
||||
|
||||
void widgets_init (void);
|
||||
void widgets_theme_reinit (void);
|
||||
void widgets_deinit (void);
|
||||
|
||||
void widget_free (widget *w);
|
||||
|
||||
void widget_set_flag (widget *w, u32 flag, bool set);
|
||||
void widget_toggle_flag (widget *w, u32 flag);
|
||||
void widget_set_progress (widget *w, u32 progress);
|
||||
void widget_fade_gradient (widget *w, s8 modifier);
|
||||
bool widget_scroll_memo (widget *w, s16 modifier);
|
||||
bool widget_scroll_memo_deco (widget *w, s16 modifier);
|
||||
|
||||
void widget_label (widget *w, s16 x, s16 y, s16 z, const char *caption,
|
||||
u16 width, font_xalign xalign, font_yalign yalign,
|
||||
font_id font);
|
||||
|
||||
void widget_image(widget *w, s16 x, s16 y, s16 z,
|
||||
gfx_entity *image, gfx_entity *image_disabled,
|
||||
bool rumble, gfx_entity *image_cursor);
|
||||
|
||||
void widget_button (widget *w, s16 x, s16 y, s16 z,
|
||||
button_size size, const char *caption);
|
||||
void widget_button_set_caption(widget *w, font_id font, const char *caption);
|
||||
|
||||
void widget_app_entry (widget *w, s16 x, s16 y, s16 z,
|
||||
const app_entry *entry);
|
||||
void widget_grid_app_entry (widget *w, s16 x, s16 y, s16 z,
|
||||
const app_entry *entry);
|
||||
|
||||
void widget_progress (widget *w, s16 x, s16 y, s16 z,
|
||||
const char *caption, u32 max);
|
||||
void widget_gradient (widget *w, s16 x, s16 y, s16 z,
|
||||
u16 width, u16 height,
|
||||
u32 c1, u32 c2, u32 c3, u32 c4);
|
||||
void widget_memo (widget *w, s16 x, s16 y, s16 z,
|
||||
u16 width, u16 height,
|
||||
const char *text, font_xalign align);
|
||||
void widget_memo_deco (widget *w, s16 x, s16 y, s16 z,
|
||||
u16 width, u16 height,
|
||||
const char *text, font_xalign align);
|
||||
|
||||
#endif
|
||||
|
||||
612
channel/channelapp/source/xml.c
Normal file
612
channel/channelapp/source/xml.c
Normal file
@@ -0,0 +1,612 @@
|
||||
#include <malloc.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <ogcsys.h>
|
||||
#include <ogc/machine/processor.h>
|
||||
#include <mxml.h>
|
||||
|
||||
#include "../config.h"
|
||||
#include "title.h"
|
||||
#include "isfs.h"
|
||||
#include "xml.h"
|
||||
#include "panic.h"
|
||||
|
||||
#include "banner_bin.h"
|
||||
|
||||
#define FN_BANNER "banner.bin"
|
||||
#define FN_SETTINGS "settings.xml"
|
||||
#define MAX_SETTINGS_XML_SIZE (16 * 1024)
|
||||
|
||||
settings_t settings;
|
||||
theme_t theme;
|
||||
|
||||
static settings_t _loaded_settings;
|
||||
static bool write_banner = true;
|
||||
|
||||
static inline char *_xmldup(const char *str) {
|
||||
if (!str)
|
||||
return NULL;
|
||||
|
||||
return pstrdup(str);
|
||||
}
|
||||
|
||||
static char *_get_cdata(mxml_node_t *node) {
|
||||
if (!node)
|
||||
return NULL;
|
||||
|
||||
mxml_node_t *n = node->child;
|
||||
|
||||
while (n) {
|
||||
if (n->type == MXML_OPAQUE)
|
||||
return n->value.opaque;
|
||||
|
||||
n = mxmlWalkNext(n, node, MXML_NO_DESCEND);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static char *_get_elem_cdata(mxml_node_t *node, const char *element) {
|
||||
if (!node)
|
||||
return NULL;
|
||||
|
||||
return _get_cdata(mxmlFindElement(node, node, element,
|
||||
NULL, NULL, MXML_DESCEND_FIRST));
|
||||
}
|
||||
|
||||
static int _get_elem_int(mxml_node_t *node, const char *element, int std) {
|
||||
char *cdata;
|
||||
|
||||
if (!node)
|
||||
return std;
|
||||
|
||||
cdata = _get_cdata(mxmlFindElement(node, node, element,
|
||||
NULL, NULL, MXML_DESCEND_FIRST));
|
||||
|
||||
if (!cdata)
|
||||
return std;
|
||||
|
||||
return atoi(cdata);
|
||||
}
|
||||
|
||||
static bool _get_color(mxml_node_t *node, const char *element, u32 *color) {
|
||||
u32 res, i;
|
||||
int c;
|
||||
const char *const channel[] = { "red", "green", "blue", "alpha" };
|
||||
|
||||
if (!node)
|
||||
return false;
|
||||
|
||||
node = mxmlFindElement(node, node, element, NULL, NULL, MXML_DESCEND_FIRST);
|
||||
|
||||
if (!node)
|
||||
return false;
|
||||
|
||||
res = 0;
|
||||
for (i = 0; i < 4; ++i) {
|
||||
res <<= 8;
|
||||
c = _get_elem_int(node, channel[i], -1);
|
||||
if ((c < 0) || (c > 255))
|
||||
return false;
|
||||
res |= c & 0xff;
|
||||
}
|
||||
|
||||
*color = res;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool _get_gradient(mxml_node_t *node, const char *element,
|
||||
theme_gradient_t *gradient) {
|
||||
u32 i, c[4];
|
||||
const char *const corner[] = {
|
||||
"upper_left", "upper_right", "lower_right", "lower_left"
|
||||
};
|
||||
|
||||
if (!node)
|
||||
return false;
|
||||
|
||||
node = mxmlFindElement(node, node, element, NULL, NULL, MXML_DESCEND_FIRST);
|
||||
|
||||
if (!node)
|
||||
return false;
|
||||
|
||||
for (i = 0; i < 4; ++i)
|
||||
if (!_get_color(node, corner[i], &c[i]))
|
||||
return false;
|
||||
|
||||
gradient->ul = c[0];
|
||||
gradient->ur = c[1];
|
||||
gradient->ll = c[2];
|
||||
gradient->lr = c[3];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// sync with font.h
|
||||
static const char *targets[FONT_MAX] = {
|
||||
"label",
|
||||
"dlgtitle",
|
||||
"memo",
|
||||
"appname",
|
||||
"appdesc",
|
||||
"button",
|
||||
"button_desel"
|
||||
};
|
||||
|
||||
static void _get_font(mxml_node_t *node) {
|
||||
theme_font_t f;
|
||||
f.file = _xmldup(_get_elem_cdata(node, "file"));
|
||||
f.size = _get_elem_int(node, "size", 0);
|
||||
f.color = NO_COLOR;
|
||||
_get_color(node, "color", &f.color);
|
||||
|
||||
const char *t = mxmlElementGetAttr(node, "target");
|
||||
if (t && t[0]) {
|
||||
char *trg = pstrdup(t);
|
||||
char *tok;
|
||||
tok = strtok(trg, ", ");
|
||||
while (tok) {
|
||||
int i;
|
||||
for (i=0; i<FONT_MAX; i++) {
|
||||
if (!strcmp(tok, targets[i])) {
|
||||
if (f.file) {
|
||||
if (theme.fonts[i].file)
|
||||
free(theme.fonts[i].file);
|
||||
theme.fonts[i].file = f.file;
|
||||
}
|
||||
if (f.size)
|
||||
theme.fonts[i].size = f.size;
|
||||
if (f.color != NO_COLOR)
|
||||
theme.fonts[i].color = f.color;
|
||||
}
|
||||
}
|
||||
tok = strtok(NULL, ",");
|
||||
}
|
||||
free(trg);
|
||||
} else {
|
||||
if (f.file) {
|
||||
if (theme.default_font.file)
|
||||
free(theme.default_font.file);
|
||||
theme.default_font.file = f.file;
|
||||
}
|
||||
if (f.size)
|
||||
theme.default_font.size = f.size;
|
||||
if (f.color != NO_COLOR)
|
||||
theme.default_font.color = f.color;
|
||||
}
|
||||
}
|
||||
|
||||
static char *_get_args(u16 *length, mxml_node_t *node, const char *element) {
|
||||
*length = 0;
|
||||
|
||||
if (!node)
|
||||
return NULL;
|
||||
|
||||
node = mxmlFindElement(node, node, element, NULL, NULL, MXML_DESCEND_FIRST);
|
||||
|
||||
if (!node)
|
||||
return NULL;
|
||||
|
||||
mxml_node_t *n;
|
||||
u16 len = 0;
|
||||
char *arg;
|
||||
|
||||
for (n = mxmlFindElement(node, node, "arg", NULL, NULL, MXML_DESCEND_FIRST);
|
||||
n != NULL; n = mxmlFindElement(n, node, "arg", NULL, NULL,
|
||||
MXML_NO_DESCEND)) {
|
||||
arg = _get_cdata(n);
|
||||
|
||||
if (arg) {
|
||||
if (len)
|
||||
len++;
|
||||
len += strlen(arg);
|
||||
}
|
||||
}
|
||||
|
||||
if (!len)
|
||||
return NULL;
|
||||
|
||||
len++;
|
||||
|
||||
if (len > ARGS_MAX_LEN)
|
||||
return NULL;
|
||||
|
||||
char *ret;
|
||||
|
||||
ret = pmalloc(len);
|
||||
|
||||
len = 0;
|
||||
|
||||
for (n = mxmlFindElement(node, node, "arg", NULL, NULL, MXML_DESCEND_FIRST);
|
||||
n != NULL; n = mxmlFindElement(n, node, "arg", NULL, NULL,
|
||||
MXML_NO_DESCEND)) {
|
||||
arg = _get_cdata(n);
|
||||
|
||||
if (arg) {
|
||||
if (len) {
|
||||
ret[len] = 0;
|
||||
len++;
|
||||
}
|
||||
|
||||
strcpy(ret + len, arg);
|
||||
len += strlen(arg);
|
||||
}
|
||||
}
|
||||
|
||||
ret[len] = 0;
|
||||
|
||||
*length = len;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
meta_info *meta_parse(char *fn) {
|
||||
int fd;
|
||||
mxml_node_t *root, *node;
|
||||
meta_info *res;
|
||||
char *s;
|
||||
struct tm t;
|
||||
|
||||
fd = open(fn, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
gprintf("error opening '%s'\n", fn);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
root = mxmlLoadFd(NULL, fd, MXML_OPAQUE_CALLBACK);
|
||||
|
||||
close(fd);
|
||||
|
||||
if (!root) {
|
||||
gprintf("error parsing '%s'\n", fn);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
node = mxmlFindElement(root, root, "app", NULL, NULL, MXML_DESCEND_FIRST);
|
||||
|
||||
if (!node) {
|
||||
gprintf("no app node '%s'\n", fn);
|
||||
mxmlDelete(root);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
res = (meta_info *) pmalloc(sizeof(meta_info));
|
||||
memset(res, 0, sizeof(meta_info));
|
||||
|
||||
res->name = _xmldup(_get_elem_cdata(node, "name"));
|
||||
res->coder = _xmldup(_get_elem_cdata(node, "author"));
|
||||
if (!res->coder)
|
||||
res->coder = _xmldup(_get_elem_cdata(node, "coder"));
|
||||
res->version = _xmldup(_get_elem_cdata(node, "version"));
|
||||
res->short_description = _xmldup(_get_elem_cdata(node, "short_description"));
|
||||
res->long_description = _xmldup(_get_elem_cdata(node, "long_description"));
|
||||
|
||||
s = _get_elem_cdata(node, "release_date");
|
||||
|
||||
if (s) {
|
||||
memset(&t, 0, sizeof(struct tm));
|
||||
if (sscanf(s, "%4d%2d%2d%2d%2d%2d", &t.tm_year, &t.tm_mon, &t.tm_mday,
|
||||
&t.tm_hour, &t.tm_min, &t.tm_sec) == 6) {
|
||||
t.tm_year -= 1900;
|
||||
|
||||
res->release_date = mktime (&t);
|
||||
} else if (sscanf(s, "%4d%2d%2d", &t.tm_year, &t.tm_mon, &t.tm_mday) == 3) {
|
||||
t.tm_hour = 1;
|
||||
t.tm_min = 0;
|
||||
t.tm_sec = 0;
|
||||
t.tm_year -= 1900;
|
||||
|
||||
res->release_date = mktime (&t);
|
||||
}
|
||||
}
|
||||
|
||||
res->args = _get_args(&res->argslen, node, "arguments");
|
||||
|
||||
if (mxmlFindElement(node, node, "ahb_access", NULL, NULL,
|
||||
MXML_DESCEND_FIRST))
|
||||
res->ahb_access = true;
|
||||
if (mxmlFindElement(node, node, "no_ios_reload", NULL, NULL,
|
||||
MXML_DESCEND_FIRST))
|
||||
res->ahb_access = true;
|
||||
|
||||
mxmlDelete(root);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void meta_free(meta_info *info) {
|
||||
if (!info)
|
||||
return;
|
||||
|
||||
free(info->name);
|
||||
free(info->coder);
|
||||
free(info->version);
|
||||
free(info->short_description);
|
||||
free(info->long_description);
|
||||
free(info->args);
|
||||
|
||||
free(info);
|
||||
}
|
||||
|
||||
update_info *update_parse(char *buf) {
|
||||
mxml_node_t *root, *node;
|
||||
char *s;
|
||||
update_info *res;
|
||||
|
||||
root = mxmlLoadString(NULL, buf, MXML_OPAQUE_CALLBACK);
|
||||
|
||||
if (!root) {
|
||||
gprintf("error parsing update.xml!\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
node = mxmlFindElement(root, root, "app", NULL, NULL, MXML_DESCEND_FIRST);
|
||||
|
||||
if (!node) {
|
||||
gprintf("no app node for update.xml!'\n");
|
||||
mxmlDelete(root);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
res = (update_info *) pmalloc(sizeof(update_info));
|
||||
memset(res, 0, sizeof(update_info));
|
||||
|
||||
res->version = _xmldup(_get_elem_cdata(node, "version"));
|
||||
|
||||
s = _get_elem_cdata(node, "date");
|
||||
if (s) {
|
||||
if (sscanf(s, "%llu", &res->date) != 1)
|
||||
res->date = 0;
|
||||
}
|
||||
|
||||
res->notes = _xmldup(_get_elem_cdata(node, "notes"));
|
||||
res->uri = _xmldup(_get_elem_cdata(node, "uri"));
|
||||
res->hash = _xmldup(_get_elem_cdata(node, "hash"));
|
||||
|
||||
mxmlDelete(root);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void update_free(update_info *info) {
|
||||
if (!info)
|
||||
return;
|
||||
|
||||
free(info->version);
|
||||
free(info->notes);
|
||||
free(info->uri);
|
||||
|
||||
free(info);
|
||||
}
|
||||
|
||||
bool settings_load(void) {
|
||||
s32 res;
|
||||
u8 *buf;
|
||||
mxml_node_t *root, *node;
|
||||
char *s;
|
||||
const char *titlepath;
|
||||
STACK_ALIGN(char, fn, ISFS_MAXPATH, 32);
|
||||
|
||||
settings.device = -1;
|
||||
settings.sort_order = 0;
|
||||
settings.browse_mode = 0;
|
||||
memset(settings.app_sel, 0, sizeof(settings.app_sel));
|
||||
|
||||
memcpy(&_loaded_settings, &settings, sizeof(settings_t));
|
||||
|
||||
titlepath = title_get_path();
|
||||
if (!titlepath[0])
|
||||
return false;
|
||||
|
||||
sprintf(fn, "%s/" FN_BANNER, titlepath);
|
||||
|
||||
buf = NULL;
|
||||
res = isfs_get(fn, &buf, banner_bin_size, 0, false);
|
||||
if (res < 0) {
|
||||
gprintf("deleting banner: %ld\n", res);
|
||||
ISFS_Delete(fn);
|
||||
} else if (memcmp(buf, banner_bin, banner_bin_size)) {
|
||||
gprintf("banner.bin mismatch, deleting\n");
|
||||
ISFS_Delete(fn);
|
||||
} else {
|
||||
write_banner = false;
|
||||
}
|
||||
|
||||
if (buf) {
|
||||
free(buf);
|
||||
buf = NULL;
|
||||
}
|
||||
|
||||
sprintf(fn, "%s/" FN_SETTINGS, titlepath);
|
||||
res = isfs_get(fn, &buf, 0, MAX_SETTINGS_XML_SIZE, false);
|
||||
if (res < 0) {
|
||||
gprintf("isfs_get failed: %ld\n", res);
|
||||
return false;
|
||||
}
|
||||
|
||||
root = mxmlLoadString(NULL, (char *) buf, MXML_OPAQUE_CALLBACK);
|
||||
free(buf);
|
||||
buf = NULL;
|
||||
|
||||
if (!root) {
|
||||
gprintf("error parsing settings.xml!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
node = mxmlFindElement(root, root, "hbc", NULL, NULL, MXML_DESCEND_FIRST);
|
||||
|
||||
if (!node) {
|
||||
gprintf("no hbc node for settings.xml!'\n");
|
||||
mxmlDelete(root);
|
||||
return false;
|
||||
}
|
||||
|
||||
settings.device = _get_elem_int(node, "device", -1);
|
||||
settings.sort_order = _get_elem_int(node, "sort_order", 0);
|
||||
settings.browse_mode = _get_elem_int(node, "browse_mode", 0);
|
||||
s = _get_elem_cdata(node, "app_sel");
|
||||
if (s) {
|
||||
strncpy(settings.app_sel, s, sizeof(settings.app_sel));
|
||||
settings.app_sel[sizeof(settings.app_sel) - 1] = 0;
|
||||
}
|
||||
|
||||
mxmlDelete(root);
|
||||
|
||||
memcpy(&_loaded_settings, &settings, sizeof(settings_t));
|
||||
//hexdump(&_loaded_settings,sizeof(settings_t));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool settings_save(void) {
|
||||
s32 res;
|
||||
char *x;
|
||||
const char *titlepath;
|
||||
int size;
|
||||
|
||||
STACK_ALIGN(char, fn, ISFS_MAXPATH, 32);
|
||||
|
||||
titlepath = title_get_path();
|
||||
if (!titlepath[0])
|
||||
return false;
|
||||
|
||||
if (!memcmp(&_loaded_settings, &settings, sizeof(settings_t)))
|
||||
return false;
|
||||
|
||||
//hexdump(&settings,sizeof(settings_t));
|
||||
x = (char *) pmemalign(32, MAX_SETTINGS_XML_SIZE);
|
||||
|
||||
mxml_node_t *xml;
|
||||
mxml_node_t *data;
|
||||
mxml_node_t *node;
|
||||
|
||||
xml = mxmlNewXML("1.0");
|
||||
|
||||
data = mxmlNewElement(xml, "hbc");
|
||||
|
||||
node = mxmlNewElement(data, "device");
|
||||
mxmlNewInteger(node, settings.device);
|
||||
|
||||
node = mxmlNewElement(data, "sort_order");
|
||||
mxmlNewInteger(node, settings.sort_order);
|
||||
|
||||
node = mxmlNewElement(data, "browse_mode");
|
||||
mxmlNewInteger(node, settings.browse_mode);
|
||||
|
||||
if (strlen(settings.app_sel) > 0) {
|
||||
node = mxmlNewElement(data, "app_sel");
|
||||
mxmlNewText(node, 0, settings.app_sel);
|
||||
}
|
||||
|
||||
size = mxmlSaveString (xml, x, MAX_SETTINGS_XML_SIZE, MXML_NO_CALLBACK);
|
||||
x[MAX_SETTINGS_XML_SIZE - 1] = 0;
|
||||
|
||||
if ((size < 16) || (size > MAX_SETTINGS_XML_SIZE - 1)) {
|
||||
gprintf("mxmlSaveString failed!\n");
|
||||
free(x);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (write_banner) {
|
||||
sprintf(fn, "%s/" FN_BANNER, titlepath);
|
||||
res = isfs_put(fn, banner_bin, banner_bin_size);
|
||||
if ((res < 0) && (res != -105)) {
|
||||
gprintf("isfs_put banner failed: %ld\n", res);
|
||||
free(x);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
sprintf(fn, "%s/" FN_SETTINGS, titlepath);
|
||||
|
||||
res = isfs_put(fn, x, strlen(x));
|
||||
if (res < 0) {
|
||||
gprintf("isfs_put xml failed: %ld\n", res);
|
||||
free(x);
|
||||
sprintf(fn, "%s/" FN_BANNER, titlepath);
|
||||
ISFS_Delete(fn);
|
||||
return false;
|
||||
}
|
||||
|
||||
free(x);
|
||||
return true;
|
||||
}
|
||||
|
||||
void theme_xml_init(void) {
|
||||
memset(&theme, 0, sizeof(theme));
|
||||
}
|
||||
|
||||
bool load_theme_xml(char *buf) {
|
||||
int i;
|
||||
mxml_node_t *root, *node, *fnode;
|
||||
|
||||
// free prior theme
|
||||
if (theme.description)
|
||||
free(theme.description);
|
||||
if (theme.default_font.file)
|
||||
free(theme.default_font.file);
|
||||
for (i=0; i<FONT_MAX; i++)
|
||||
if (theme.fonts[i].file)
|
||||
free(theme.fonts[i].file);
|
||||
|
||||
root = mxmlLoadString(NULL, buf, MXML_OPAQUE_CALLBACK);
|
||||
|
||||
if (!root) {
|
||||
gprintf("error parsing theme.xml!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
node = mxmlFindElement(root, root, "theme", NULL, NULL, MXML_DESCEND_FIRST);
|
||||
|
||||
if (!node) {
|
||||
gprintf("no theme node for theme.xml!'\n");
|
||||
mxmlDelete(root);
|
||||
return false;
|
||||
}
|
||||
|
||||
theme.langs = 0;
|
||||
const char *l = mxmlElementGetAttr(node, "langs");
|
||||
if (l && l[0]) {
|
||||
char *lng = pstrdup(l);
|
||||
char *tok;
|
||||
tok = strtok(lng, ", ");
|
||||
while (tok) {
|
||||
if (!strcmp(tok, "ja"))
|
||||
theme.langs |= TLANG_JA;
|
||||
if (!strcmp(tok, "ko"))
|
||||
theme.langs |= TLANG_KO;
|
||||
if (!strcmp(tok, "zh"))
|
||||
theme.langs |= TLANG_ZH;
|
||||
tok = strtok(NULL, ",");
|
||||
}
|
||||
free(lng);
|
||||
}
|
||||
|
||||
theme.description = _xmldup(_get_elem_cdata(node, "description"));
|
||||
if (theme.description && (strlen(theme.description) > 64))
|
||||
theme.description[64] = 0;
|
||||
|
||||
theme.default_font.color = NO_COLOR;
|
||||
theme.default_font.file = NULL;
|
||||
theme.default_font.size = 0;
|
||||
for (i=0; i<FONT_MAX; i++) {
|
||||
theme.fonts[i].color = NO_COLOR;
|
||||
theme.fonts[i].file = NULL;
|
||||
theme.fonts[i].size = 0;
|
||||
}
|
||||
|
||||
_get_color(node, "font_color", &theme.default_font.color);
|
||||
_get_gradient(node, "progress_gradient", &theme.progress);
|
||||
|
||||
fnode = mxmlFindElement(node, root, "font", NULL, NULL, MXML_DESCEND_FIRST);
|
||||
while (fnode != NULL) {
|
||||
_get_font(fnode);
|
||||
fnode = mxmlFindElement(fnode, root, "font", NULL, NULL, MXML_NO_DESCEND);
|
||||
}
|
||||
|
||||
mxmlDelete(root);
|
||||
|
||||
return true;
|
||||
}
|
||||
79
channel/channelapp/source/xml.h
Normal file
79
channel/channelapp/source/xml.h
Normal file
@@ -0,0 +1,79 @@
|
||||
#ifndef _XML_H_
|
||||
#define _XML_H_
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include <gctypes.h>
|
||||
|
||||
#include "font.h"
|
||||
|
||||
#define NO_COLOR 0x0DEADF00
|
||||
|
||||
typedef struct {
|
||||
char *name;
|
||||
char *coder;
|
||||
char *version;
|
||||
time_t release_date;
|
||||
char *short_description;
|
||||
char *long_description;
|
||||
char *args;
|
||||
u16 argslen;
|
||||
bool ahb_access;
|
||||
} meta_info;
|
||||
|
||||
typedef struct {
|
||||
char *version;
|
||||
u64 date;
|
||||
char *notes;
|
||||
char *uri;
|
||||
char *hash;
|
||||
} update_info;
|
||||
|
||||
typedef struct {
|
||||
int device;
|
||||
int sort_order;
|
||||
int browse_mode;
|
||||
char app_sel[64];
|
||||
} settings_t;
|
||||
|
||||
typedef struct {
|
||||
u32 ul, ur, lr, ll;
|
||||
} theme_gradient_t;
|
||||
|
||||
typedef struct {
|
||||
char *file;
|
||||
int size;
|
||||
u32 color;
|
||||
} theme_font_t;
|
||||
|
||||
typedef enum {
|
||||
TLANG_JA = 0x01,
|
||||
TLANG_KO = 0x02,
|
||||
TLANG_ZH = 0x04,
|
||||
} theme_lang_t;
|
||||
|
||||
typedef struct {
|
||||
theme_gradient_t progress;
|
||||
char *description;
|
||||
theme_font_t default_font;
|
||||
theme_font_t fonts[FONT_MAX];
|
||||
theme_lang_t langs;
|
||||
} theme_t;
|
||||
|
||||
extern settings_t settings;
|
||||
extern theme_t theme;
|
||||
|
||||
meta_info *meta_parse(char *fn);
|
||||
void meta_free(meta_info *info);
|
||||
|
||||
update_info *update_parse(char *buf);
|
||||
void update_free(update_info *info);
|
||||
|
||||
bool settings_load(void);
|
||||
bool settings_save(void);
|
||||
|
||||
void theme_xml_init(void);
|
||||
bool load_theme_xml(char *buf);
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user