Initial commit

This commit is contained in:
Hector Martin
2016-11-23 14:35:12 +09:00
commit 5b1c4f85b6
296 changed files with 39925 additions and 0 deletions

View 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;
}

View 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

View 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__

View 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);
}

View 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

View 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;
}

View 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

View 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);
}

View 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

View 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);
}
}

View 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

View 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;
}

View 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

View 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

View 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;
}

View 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

View 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");
}
}

View File

@@ -0,0 +1,10 @@
#ifndef _DVD_H_
#define _DVD_H_
#include <gctypes.h>
s32 WiiDVD_StopMotorAsync(void);
void WiiDVD_ShutDown(void);
#endif

View 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

View 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

View 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 */

View 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;
}
}
}

View 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

View 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 ();
}

View 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

View 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 */

View 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 */

View 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

View 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

View 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;
}

View 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

View 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;
}

View 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

File diff suppressed because it is too large Load Diff

View 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

View 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");
}

View 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

View 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);
}
}

View 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

View 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;
}

View 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;
}

View 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

View 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);
}

View 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

View 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);
}

View 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

View 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);
}

View 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

View 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;
}

View 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

View 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);
}

View 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

View 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);
}

View 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

View 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();
}

View 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

File diff suppressed because it is too large Load Diff

View 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

View 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;
}

View 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

View 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

View 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

View 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;
}

View 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

View 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);
}

View 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

View 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;
}

View 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