hbc/channel/channelapp/source/main.c
2016-11-23 14:35:12 +09:00

826 lines
16 KiB
C

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