#include #include #include #include #include #include #include #include #include #include #include #include #include #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(u32 irq, void *ctx) { #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[PATH_MAX]; 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; }