From c77ede571f8bbaadb3412bfc92a23d832be17304 Mon Sep 17 00:00:00 2001 From: "fix94.1" Date: Sat, 6 Jul 2013 22:35:05 +0000 Subject: [PATCH] -added a wad installer to install wiiware and virtual console to your emu nand -updated english.ini --- source/channel/nand.hpp | 4 +- source/libwbfs/rijndael.c | 27 ++- source/libwbfs/wiidisc.c | 6 +- source/libwbfs/wiidisc.h | 2 + source/menu/menu.cpp | 1 + source/menu/menu.hpp | 5 + source/menu/menu_explorer.cpp | 13 +- source/menu/menu_wad.cpp | 329 ++++++++++++++++++++++++++++++ wii/wiiflow/Languages/english.ini | 6 + 9 files changed, 381 insertions(+), 12 deletions(-) create mode 100644 source/menu/menu_wad.cpp diff --git a/source/channel/nand.hpp b/source/channel/nand.hpp index 5994e2d0..be31fec3 100644 --- a/source/channel/nand.hpp +++ b/source/channel/nand.hpp @@ -52,7 +52,8 @@ typedef struct _namelist typedef struct _uid { u64 TitleID; - u32 unused; + u16 padding; + u16 uid; } ATTRIBUTE_PACKED uid; using namespace std; @@ -85,6 +86,7 @@ public: u8 *GetTMD(u64 title, u32 *size); u8 *GetEmuFile(const char *path, u32 *size, s32 len = -1); void SetPaths(const char *emuPath, const char *currentPart); + const char *GetPath(void) { return FullNANDPath; }; void CreatePath(const char *path, ...); void CreateTitleTMD(dir_discHdr *hdr); diff --git a/source/libwbfs/rijndael.c b/source/libwbfs/rijndael.c index de91e96a..1b1edcc4 100644 --- a/source/libwbfs/rijndael.c +++ b/source/libwbfs/rijndael.c @@ -11,7 +11,8 @@ #include #include - +#include "gecko/gecko.hpp" +#include "loader/utils.h" #define u8 unsigned char /* 8 bits */ #define u32 unsigned long /* 32 bits */ #define u64 unsigned long long @@ -364,16 +365,16 @@ void aes_set_key(u8 *key) } // CBC mode decryption -void aes_decrypt(u8 *iv, u8 *inbuf, u8 *outbuf, unsigned long long len) +void aes_decrypt(u8 *iv, u8 *inbuf, u8 *outbuf, u64 len) { u8 block[16]; - unsigned int blockno = 0, i; + u32 blockno = 0, i; //printf("aes_decrypt(%p, %p, %p, %lld)\n", iv, inbuf, outbuf, len); for (blockno = 0; blockno <= (len / sizeof(block)); blockno++) { - unsigned int fraction; + u32 fraction; if (blockno == (len / sizeof(block))) // last block { fraction = len % sizeof(block); @@ -398,16 +399,16 @@ void aes_decrypt(u8 *iv, u8 *inbuf, u8 *outbuf, unsigned long long len) } // CBC mode encryption -void aes_encrypt(u8 *iv, u8 *inbuf, u8 *outbuf, unsigned long long len) +void aes_encrypt(u8 *iv, u8 *inbuf, u8 *outbuf, u64 len) { u8 block[16]; - unsigned int blockno = 0, i; + u32 blockno = 0, i; // debug_printf("aes_decrypt(%p, %p, %p, %lld)\n", iv, inbuf, outbuf, len); for (blockno = 0; blockno <= (len / sizeof(block)); blockno++) { - unsigned int fraction; + u32 fraction; if (blockno == (len / sizeof(block))) // last block { fraction = len % sizeof(block); @@ -430,3 +431,15 @@ void aes_encrypt(u8 *iv, u8 *inbuf, u8 *outbuf, unsigned long long len) } } + +// CBC mode decryption +#define WAD_BUF 0x10000 + +void aes_decrypt_partial(u8 *inbuf, u8 *outbuf, u8 block[16], u8 *ctext_ptr, u32 tmp_blockno) +{ + memcpy(block, inbuf + tmp_blockno * 16, 16); + decrypt((char*)block); + u32 i; + for(i = 0; i < 16; i++) + outbuf[tmp_blockno * 16 + i] = ctext_ptr[i] ^ block[i]; +} diff --git a/source/libwbfs/wiidisc.c b/source/libwbfs/wiidisc.c index 174971fb..74d577ed 100644 --- a/source/libwbfs/wiidisc.c +++ b/source/libwbfs/wiidisc.c @@ -6,7 +6,7 @@ #include "wiidisc.h" void aes_set_key(u8 *key); -void aes_decrypt(u8 *iv, u8 *inbuf, u8 *outbuf, unsigned long long len); +void aes_decrypt(u8 *iv, u8 *inbuf, u8 *outbuf, u64 len); int wd_last_error = 0; @@ -15,7 +15,7 @@ int wd_get_last_error(void) return wd_last_error; } -static void _decrypt_title_key(u8 *tik, u8 *title_key) +void decrypt_title_key(u8 *tik, u8 *title_key) { u8 common_key[16] = { 0xeb, 0xe4, 0x2a, 0x22, 0x5e, 0x85, 0x93, 0xe4, @@ -251,7 +251,7 @@ static void do_partition(wiidisc_t*d) wbfs_fatal("malloc cert\n"); partition_raw_read(d, cert_offset, cert, cert_size); - _decrypt_title_key(tik, d->disc_key); + decrypt_title_key(tik, d->disc_key); partition_raw_read(d, h3_offset, 0, 0x18000); diff --git a/source/libwbfs/wiidisc.h b/source/libwbfs/wiidisc.h index f1502109..2227ed5c 100644 --- a/source/libwbfs/wiidisc.h +++ b/source/libwbfs/wiidisc.h @@ -61,6 +61,8 @@ extern "C" // effectively remove not copied partition from the partition table. void wd_fix_partition_table(partition_selector_t selector, u8 *partition_table); + // needed for wad decryption + void decrypt_title_key(u8 *tik, u8 *title_key); #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/source/menu/menu.cpp b/source/menu/menu.cpp index 1118de50..d00d4505 100644 --- a/source/menu/menu.cpp +++ b/source/menu/menu.cpp @@ -1150,6 +1150,7 @@ void CMenu::_buildMenus(void) _initCoverBanner(); _initExplorer(); _initBoot(); + _initWad(); _loadCFCfg(); } diff --git a/source/menu/menu.hpp b/source/menu/menu.hpp index 60ca52fa..13b2c175 100644 --- a/source/menu/menu.hpp +++ b/source/menu/menu.hpp @@ -847,6 +847,7 @@ private: void _initHomeAndExitToMenu(); void _initCoverBanner(); void _initExplorer(); + void _initWad(); // void _textSource(void); void _textPluginSettings(void); @@ -875,6 +876,7 @@ private: void _textBoot(void); void _textCoverBanner(void); void _textExplorer(void); + void _textWad(void); // void _refreshBoot(); void _refreshExplorer(s8 direction = 0); @@ -908,6 +910,7 @@ private: void _hideExitTo(bool instant = false); void _hideCoverBanner(bool instant = false); void _hideExplorer(bool instant = false); + void _hideWad(bool instant = false); // void _showError(void); void _showMain(void); @@ -939,6 +942,7 @@ private: void _showExitTo(void); void _showCoverBanner(void); void _showExplorer(void); + void _showWad(void); void _updateSourceBtns(void); void _updatePluginText(void); void _updatePluginCheckboxes(void); @@ -984,6 +988,7 @@ private: void _gameSettings(void); void _CoverBanner(void); void _Explorer(void); + void _Wad(const char *wad_path = NULL); void _CheatSettings(); bool _Source(); void _PluginSettings(); diff --git a/source/menu/menu_explorer.cpp b/source/menu/menu_explorer.cpp index 856dc90d..2a297ea4 100644 --- a/source/menu/menu_explorer.cpp +++ b/source/menu/menu_explorer.cpp @@ -17,6 +17,7 @@ #include #include #include "menu.hpp" +#include "channel/nand.hpp" #include "defines.h" extern const u8 blank_png[]; @@ -24,6 +25,7 @@ extern const u8 blank_png[]; TexData m_explorerBg; s16 entries[8]; s16 entries_sel[8]; +char file[MAX_FAT_PATH]; char dir[MAX_FAT_PATH]; char entries_char[7][NAME_MAX+1]; u8 explorer_partition = 0; @@ -116,11 +118,13 @@ void CMenu::_Explorer(void) } else { - const char *file = fmt("%s%s", dir, entries_char[i-1]); + memset(file, 0, MAX_FAT_PATH); + strncpy(file, fmt("%s%s", dir, entries_char[i-1]), MAX_FAT_PATH); if(strcasestr(file, ".mp3") != NULL || strcasestr(file, ".ogg") != NULL) MusicPlayer.LoadFile(file, false); else if(strcasestr(file, ".iso") != NULL || strcasestr(file, ".wbfs") != NULL) { + _hideExplorer(); /* create header for id and path */ dir_discHdr tmpHdr; memset(&tmpHdr, 0, sizeof(dir_discHdr)); @@ -141,6 +145,13 @@ void CMenu::_Explorer(void) currentPartition = explorer_partition; _launchGC(&tmpHdr, false); } + _showExplorer(); + } + else if(strcasestr(file, ".wad") != NULL) + { + _hideExplorer(); + _Wad(file); + _showExplorer(); } } } diff --git a/source/menu/menu_wad.cpp b/source/menu/menu_wad.cpp new file mode 100644 index 00000000..37988d44 --- /dev/null +++ b/source/menu/menu_wad.cpp @@ -0,0 +1,329 @@ +/**************************************************************************** + * Copyright (C) 2013 FIX94 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ****************************************************************************/ +#include "menu/menu.hpp" +#include "libwbfs/wiidisc.h" +#include "channel/nand.hpp" + +extern "C" { +#include "loader/sha1.h" +extern void aes_set_key(u8 *key); +extern void aes_decrypt_partial(u8 *inbuf, u8 *outbuf, u8 block[16], + u8 *ctext_ptr, u32 tmp_blockno); +}; + +#define WAD_BUF 0x10000 + +struct _hdr { + u32 header_len; + u16 type; + u16 padding; + u32 certs_len; + u32 crl_len; + u32 tik_len; + u32 tmd_len; + u32 data_len; + u32 footer_len; +} ATTRIBUTE_PACKED hdr; + +void skip_align(FILE *f, u32 size) +{ + size_t align_missing = (ALIGN(64, size) - size); + if(align_missing == 0) + return; + fseek(f, align_missing, SEEK_CUR); +} + +int installWad(const char *path) +{ + gprintf("Installing %s\n", path); + const char *EmuNAND = NandHandle.GetPath(); + + if(!fsop_FileExist(path)) + return -1; + + u32 size = 0; + fsop_GetFileSizeBytes(path, &size); + if(size < sizeof(hdr)) + return -2; + + FILE *wad_file = fopen(path, "rb"); + fread(&hdr, sizeof(hdr), 1, wad_file); + skip_align(wad_file, sizeof(hdr)); + + if(size < (hdr.certs_len + hdr.crl_len + hdr.tik_len + hdr.tmd_len + hdr.data_len + hdr.footer_len) + || hdr.tik_len == 0 || hdr.tmd_len == 0 || hdr.data_len == 0) + { + fclose(wad_file); + return -3; + } + fseek(wad_file, ALIGN(64, hdr.certs_len), SEEK_CUR); + fseek(wad_file, ALIGN(64, hdr.crl_len), SEEK_CUR); + + gprintf("Reading tik\n"); + u8 *tik_buf = (u8*)MEM2_alloc(hdr.tik_len); + fread(tik_buf, hdr.tik_len, 1, wad_file); + skip_align(wad_file, hdr.tik_len); + + gprintf("Decrypting key\n"); + u8 tik_key[16]; + decrypt_title_key(tik_buf, tik_key); + aes_set_key(tik_key); + + gprintf("Reading tmd\n"); + signed_blob *tmd_buf = (signed_blob*)MEM2_alloc(hdr.tmd_len); + fread(tmd_buf, hdr.tmd_len, 1, wad_file); + skip_align(wad_file, hdr.tmd_len); + + const tmd *tmd_ptr = (const tmd*)SIGNATURE_PAYLOAD(tmd_buf); + u64 tid = tmd_ptr->title_id; + + /* ONLY allow wii channels for now */ + if((u32)(tid>>32) != 0x00010001) + { + gprintf("No Wii Channel!\n"); + free(tmd_buf); + free(tik_buf); + return -4; + } + u32 uid_size = 0; + u8 *uid_buf = fsop_ReadFile(fmt("%s/sys/uid.sys", EmuNAND), &uid_size); + if(uid_buf == NULL) + { + gprintf("No uid.sys found!\n"); + free(tmd_buf); + free(tik_buf); + return -5; + } + else if(uid_size % 0xC != 0) + { + gprintf("uid.sys size is invalid!\n"); + free(tmd_buf); + free(tik_buf); + free(uid_buf); + return -6; + } + + bool chan_exist = false; + uid *uid_file = (uid*)uid_buf; + u32 chans = uid_size/sizeof(uid); + for(u32 i = 0; i < chans; ++i) + { + if(uid_file[i].TitleID == tid) + chan_exist = true; + } + if(chan_exist == false) + { + gprintf("Updating uid.sys\n"); + u32 new_uid_size = (chans+1)*sizeof(uid); + u8 *new_uid_buf = (u8*)MEM2_alloc(new_uid_size); + memset(new_uid_buf, 0, new_uid_size); + /* copy over old uid */ + memcpy(new_uid_buf, uid_buf, chans*sizeof(uid)); + uid *new_uid_file = (uid*)new_uid_buf; + new_uid_file[chans].TitleID = tid; + new_uid_file[chans].uid = 0x1000+chans; + fsop_WriteFile(fmt("%s/sys/uid.sys", EmuNAND), new_uid_file, new_uid_size); + } + /* clear old tik */ + fsop_deleteFile(fmt("%s/ticket/%08x/%08x.tik", EmuNAND, (u32)(tid>>32), (u32)tid&0xFFFFFFFF)); + /* clear old content */ + fsop_deleteFolder(fmt("%s/title/%08x/%08x/content", EmuNAND, (u32)(tid>>32), (u32)tid&0xFFFFFFFF)); + + /* (re)create folder structure */ + fsop_MakeFolder(fmt("%s/ticket", EmuNAND)); + fsop_MakeFolder(fmt("%s/ticket/%08x", EmuNAND, (u32)(tid>>32))); + + fsop_MakeFolder(fmt("%s/title", EmuNAND)); + fsop_MakeFolder(fmt("%s/title/%08x", EmuNAND, (u32)(tid>>32))); + fsop_MakeFolder(fmt("%s/title/%08x/%08x", EmuNAND, (u32)(tid>>32), (u32)tid&0xFFFFFFFF)); + fsop_MakeFolder(fmt("%s/title/%08x/%08x/content", EmuNAND, (u32)(tid>>32), (u32)tid&0xFFFFFFFF)); + fsop_MakeFolder(fmt("%s/title/%08x/%08x/data", EmuNAND, (u32)(tid>>32), (u32)tid&0xFFFFFFFF)); + + int hash_errors = 0; + + /* decrypt and write app files */ + for(u16 cnt = 0; cnt < tmd_ptr->num_contents; cnt++) + { + u8 iv[16]; + const tmd_content *content = &tmd_ptr->contents[cnt]; + u16 content_index = content->index; + memset(iv, 0, 16); + memcpy(iv, &content_index, 2); + /* longass filename */ + const char *app_name = fmt("%s/title/%08x/%08x/content/%08x.app", EmuNAND, + (u32)(tid>>32), (u32)tid&0xFFFFFFFF, content->cid); + FILE *app_file = fopen(app_name, "wb"); + + u64 read = 0; + u8 *encBuf = (u8*)MEM2_alloc(WAD_BUF); + u8 *decBuf = (u8*)MEM2_alloc(WAD_BUF); + + u8 block[16]; + u8 prev_block[16]; + u8 *ctext_ptr = iv; + + SHA1_CTX ctx; + SHA1Init(&ctx); + + u32 size_enc_full = ALIGN(16, content->size); + while(read < size_enc_full) + { + u64 size_enc = (size_enc_full - read); + if (size_enc > WAD_BUF) + size_enc = WAD_BUF; + + memset(encBuf, 0, WAD_BUF); + fread(encBuf, size_enc, 1, wad_file); + + u32 partnr = 0; + u32 lastnr = (size_enc / sizeof(block)); + + for(partnr = 0; partnr < lastnr; partnr++) + { + aes_decrypt_partial(encBuf, decBuf, block, ctext_ptr, partnr); + memcpy(prev_block, encBuf + (partnr * 16), 16); + ctext_ptr = prev_block; + } + /* we need to work with the real size here */ + u64 size_dec = (content->size - read); + if(size_dec > WAD_BUF) + size_dec = WAD_BUF; + SHA1Update(&ctx, decBuf, size_dec); + fwrite(decBuf, size_dec, 1, app_file); + /* dont forget to increase the read size */ + read += size_enc; + } + sha1 app_sha1; + SHA1Final(app_sha1, &ctx); + skip_align(wad_file, size_enc_full); + gprintf("Wrote %s\n", app_name); + fclose(app_file); + + if(memcmp(app_sha1, content->hash, sizeof(sha1)) == 0) + gprintf("sha1 matches on %08x.app, success!\n", content->cid); + else + { + gprintf("sha1 mismatch on %08x.app!\n", content->cid); + hash_errors++; + } + } + + fsop_WriteFile(fmt("%s/ticket/%08x/%08x.tik", EmuNAND, (u32)(tid>>32), (u32)tid&0xFFFFFFFF), tik_buf, hdr.tik_len); + fsop_WriteFile(fmt("%s/title/%08x/%08x/content/title.tmd", EmuNAND, (u32)(tid>>32), (u32)tid&0xFFFFFFFF), tmd_buf, hdr.tmd_len); + + free(tik_buf); + free(tmd_buf); + + return hash_errors; +} + +s16 m_wadBtnInstall; +s16 m_wadLblTitle; +s16 m_wadLblDialog; + +void CMenu::_showWad() +{ + m_btnMgr.show(m_wadBtnInstall); + m_btnMgr.show(m_wadLblTitle); + m_btnMgr.show(m_wadLblDialog); + /* partition selection */ + m_btnMgr.show(m_configLblPartitionName); + m_btnMgr.show(m_configLblPartition); + m_btnMgr.show(m_configBtnPartitionP); + m_btnMgr.show(m_configBtnPartitionM); +} + +void CMenu::_hideWad(bool instant) +{ + m_btnMgr.hide(m_wadBtnInstall, instant); + m_btnMgr.hide(m_wadLblTitle, instant); + m_btnMgr.hide(m_wadLblDialog, instant); + /* partition selection */ + m_btnMgr.hide(m_configLblPartitionName); + m_btnMgr.hide(m_configLblPartition); + m_btnMgr.hide(m_configBtnPartitionP); + m_btnMgr.hide(m_configBtnPartitionM); +} + +void CMenu::_Wad(const char *wad_path) +{ + if(wad_path == NULL) + return; + u8 part = currentPartition; + m_btnMgr.setText(m_wadLblDialog, wfmt(_fmt("wad3", L"Selected %s, after the installation you return to the explorer."), (strrchr(wad_path, '/')+1))); + m_btnMgr.setText(m_configLblPartition, upperCase(DeviceName[currentPartition])); + _showWad(); + + while(!m_exit) + { + _mainLoopCommon(); + if(BTN_HOME_PRESSED || BTN_B_PRESSED) + break; + else if(BTN_A_PRESSED) + { + if(m_btnMgr.selected(m_wadBtnInstall)) + { + _hideWad(true); + m_btnMgr.setText(m_wbfsLblMessage, _t("wad4", L"Installing WAD, please wait...")); + m_btnMgr.show(m_wbfsLblMessage, true); + /* who cares about making a thread, just refresh a second */ + for(u8 i = 0; i < 60; ++i) + _mainLoopCommon(); + /* setup emu nand paths */ + const char *emu_char = m_cfg.getString(CHANNEL_DOMAIN, "path", "/").c_str(); + NandHandle.SetPaths(emu_char, DeviceName[currentPartition]); + int result = installWad(wad_path); + if(result < 0) + m_btnMgr.setText(m_wbfsLblMessage, wfmt(_fmt("wad5", L"Installation error %i!"), result)); + else + m_btnMgr.setText(m_wbfsLblMessage, wfmt(_fmt("wad6", L"Installation finished with %i hash fails."), result)); + } + else if((m_btnMgr.selected(m_configBtnPartitionP) || m_btnMgr.selected(m_configBtnPartitionM))) + { + s8 direction = m_btnMgr.selected(m_configBtnPartitionP) ? 1 : -1; + _setPartition(direction); + m_btnMgr.setText(m_configLblPartition, upperCase(DeviceName[currentPartition])); + } + } + + } + currentPartition = part; + _hideWad(); + /* onscreen message might be onscreen still */ + m_btnMgr.hide(m_wbfsLblMessage); +} + + +void CMenu::_initWad() +{ + m_wadLblTitle = _addTitle("WAD/TITLE", theme.titleFont, L"", 20, 30, 600, 60, theme.titleFontColor, FTGX_JUSTIFY_CENTER | FTGX_ALIGN_MIDDLE); + m_wadLblDialog = _addLabel("WAD/DIALOG", theme.lblFont, L"", 40, 90, 560, 200, theme.lblFontColor, FTGX_JUSTIFY_LEFT | FTGX_ALIGN_MIDDLE); + m_wadBtnInstall = _addButton("WAD/INSTALL_BTN", theme.btnFont, L"", 420, 400, 200, 56, theme.btnFontColor); + + _setHideAnim(m_wadLblTitle, "WAD/TITLE", 0, 0, -2.f, 0.f); + _setHideAnim(m_wadLblDialog, "WAD/DIALOG", 0, 0, -2.f, 0.f); + _setHideAnim(m_wadBtnInstall, "WAD/INSTALL_BTN", 0, 0, -2.f, 0.f); + + _hideWad(true); + _textWad(); +} + +void CMenu::_textWad() +{ + m_btnMgr.setText(m_wadLblTitle, _t("wad1", L"Install WAD")); + m_btnMgr.setText(m_wadBtnInstall, _t("wad2", L"Go")); +} diff --git a/wii/wiiflow/Languages/english.ini b/wii/wiiflow/Languages/english.ini index 7f77d2c4..0fa6c2cd 100644 --- a/wii/wiiflow/Languages/english.ini +++ b/wii/wiiflow/Languages/english.ini @@ -321,6 +321,12 @@ vmpall=All vmpmore=More vmpnone=None vmpnormal=Normal +wad1=Install WAD +wad2=Go +wad3=Selected %s, after the installation you return to the explorer. +wad4=Installing WAD, please wait... +wad5=Installation error %i! +wad6=Installation finished with %i hash fails. wbfsadddlg=Please insert the disc you want to copy, then click on Go. wbfscpydlg=If you are sure you want to copy this game to SD, click on Go. wbfsop1=Install Game