mirror of
https://github.com/modmii/YAWM-ModMii-Edition.git
synced 2024-11-14 04:15:12 +01:00
Korean Key detection (#11)
* check TMD for vwii_title flag for IOS WADs
This commit is contained in:
parent
608d954afe
commit
1a825dcfce
267
source/mini_seeprom.c
Normal file
267
source/mini_seeprom.c
Normal file
@ -0,0 +1,267 @@
|
||||
/*
|
||||
mini - a Free Software replacement for the Nintendo/BroadOn IOS.
|
||||
SEEPROM support
|
||||
|
||||
Copyright (C) 2008, 2009 Sven Peter <svenpeter@gmail.com>
|
||||
Copyright (C) 2008, 2009 Haxx Enterprises <bushing@gmail.com>
|
||||
Copyright (C) 2008, 2009 Hector Martin "marcan" <marcan@marcansoft.com>
|
||||
Copyright (C) 2008, 2009 John Kelley <wiidev@kelley.ca>
|
||||
Copyright (C) 2020 Pablo Curiel "DarkMatterCore" <pabloacurielz@gmail.com>
|
||||
|
||||
# This code is licensed to you under the terms of the GNU GPL, version 2;
|
||||
# see http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
||||
*/
|
||||
|
||||
#include <ogc/machine/processor.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "mini_seeprom.h"
|
||||
|
||||
#define HW_REG_BASE 0xd800000
|
||||
#define HW_GPIO1OUT (HW_REG_BASE + 0x0e0)
|
||||
#define HW_GPIO1IN (HW_REG_BASE + 0x0e8)
|
||||
|
||||
#define HW_SEEPROM_BLK_SIZE 2
|
||||
#define HW_SEEPROM_BLK_CNT (SEEPROM_SIZE / HW_SEEPROM_BLK_SIZE)
|
||||
|
||||
#define eeprom_delay() usleep(5)
|
||||
|
||||
enum {
|
||||
GP_EEP_CS = 0x000400,
|
||||
GP_EEP_CLK = 0x000800,
|
||||
GP_EEP_MOSI = 0x001000,
|
||||
GP_EEP_MISO = 0x002000
|
||||
};
|
||||
|
||||
static void seeprom_send_bits(u16 value, u8 bits)
|
||||
{
|
||||
if (!bits || bits > 16) return;
|
||||
|
||||
while(bits--)
|
||||
{
|
||||
if (value & (1 << bits))
|
||||
{
|
||||
mask32(HW_GPIO1OUT, 0, GP_EEP_MOSI);
|
||||
} else {
|
||||
mask32(HW_GPIO1OUT, GP_EEP_MOSI, 0);
|
||||
}
|
||||
|
||||
eeprom_delay();
|
||||
|
||||
mask32(HW_GPIO1OUT, 0, GP_EEP_CLK);
|
||||
eeprom_delay();
|
||||
|
||||
mask32(HW_GPIO1OUT, GP_EEP_CLK, 0);
|
||||
eeprom_delay();
|
||||
}
|
||||
}
|
||||
|
||||
static u16 seeprom_recv_bits(u8 bits)
|
||||
{
|
||||
if (!bits || bits > 16) return 0;
|
||||
|
||||
int res = 0;
|
||||
|
||||
while(bits--)
|
||||
{
|
||||
res <<= 1;
|
||||
|
||||
mask32(HW_GPIO1OUT, 0, GP_EEP_CLK);
|
||||
eeprom_delay();
|
||||
|
||||
mask32(HW_GPIO1OUT, GP_EEP_CLK, 0);
|
||||
eeprom_delay();
|
||||
|
||||
res |= !!(read32(HW_GPIO1IN) & GP_EEP_MISO);
|
||||
}
|
||||
|
||||
return (u16)res;
|
||||
}
|
||||
|
||||
u16 seeprom_read(void *dst, u16 offset, u16 size)
|
||||
{
|
||||
/*
|
||||
* WiiUBrew told me that you interact with the SEEPROM the exact same way you do on Wii.
|
||||
* However the contents are way different. Like there's absolutely no vWii stuff here.
|
||||
*/
|
||||
if (read16(0xCD8005A0) == 0xCAFE) return 0;
|
||||
|
||||
if (!dst || offset >= SEEPROM_SIZE || !size || (offset + size) > SEEPROM_SIZE) return 0;
|
||||
|
||||
u16 cur_offset = 0;
|
||||
|
||||
u8 *ptr = (u8*)dst;
|
||||
u8 val[HW_SEEPROM_BLK_SIZE] = {0};
|
||||
|
||||
// Calculate block offsets and sizes
|
||||
u8 start_addr = (u8)(offset / HW_SEEPROM_BLK_SIZE);
|
||||
u8 start_addr_offset = (u8)(offset % HW_SEEPROM_BLK_SIZE);
|
||||
|
||||
u8 end_addr = (u8)((offset + size) / HW_SEEPROM_BLK_SIZE);
|
||||
u8 end_addr_size = (u8)((offset + size) % HW_SEEPROM_BLK_SIZE);
|
||||
|
||||
if (!end_addr_size)
|
||||
{
|
||||
end_addr--;
|
||||
end_addr_size = HW_SEEPROM_BLK_SIZE;
|
||||
}
|
||||
|
||||
if (end_addr == start_addr) end_addr_size -= start_addr_offset;
|
||||
|
||||
mask32(HW_GPIO1OUT, GP_EEP_CLK, 0);
|
||||
mask32(HW_GPIO1OUT, GP_EEP_CS, 0);
|
||||
eeprom_delay();
|
||||
|
||||
for(u16 i = start_addr; i <= end_addr; i++)
|
||||
{
|
||||
if (cur_offset >= size) break;
|
||||
|
||||
// Start command cycle
|
||||
mask32(HW_GPIO1OUT, 0, GP_EEP_CS);
|
||||
|
||||
// Send read command + address
|
||||
seeprom_send_bits(0x600 | i, 11);
|
||||
|
||||
// Receive data
|
||||
*((u16*)val) = seeprom_recv_bits(16);
|
||||
|
||||
// End of command cycle
|
||||
mask32(HW_GPIO1OUT, GP_EEP_CS, 0);
|
||||
eeprom_delay();
|
||||
|
||||
// Copy read data to destination buffer
|
||||
if (i == start_addr && start_addr_offset != 0)
|
||||
{
|
||||
// Handle unaligned read at start address
|
||||
memcpy(ptr + cur_offset, val + start_addr_offset, HW_SEEPROM_BLK_SIZE - start_addr_offset);
|
||||
cur_offset += (HW_SEEPROM_BLK_SIZE - start_addr_offset);
|
||||
} else
|
||||
if (i == end_addr && end_addr_size != HW_SEEPROM_BLK_SIZE)
|
||||
{
|
||||
// Handle unaligned read at end address
|
||||
memcpy(ptr + cur_offset, val, end_addr_size);
|
||||
cur_offset += end_addr_size;
|
||||
} else {
|
||||
// Normal read
|
||||
memcpy(ptr + cur_offset, val, HW_SEEPROM_BLK_SIZE);
|
||||
cur_offset += HW_SEEPROM_BLK_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
return cur_offset;
|
||||
}
|
||||
|
||||
#if 0
|
||||
u16 seeprom_write(const void *src, u16 offset, u16 size)
|
||||
{
|
||||
if (!src || offset >= SEEPROM_SIZE || !size || (offset + size) > SEEPROM_SIZE) return 0;
|
||||
|
||||
u32 level = 0;
|
||||
u16 cur_offset = 0;
|
||||
|
||||
const u8 *ptr = (const u8*)src;
|
||||
u8 val[HW_SEEPROM_BLK_SIZE] = {0};
|
||||
|
||||
// Calculate block offsets and sizes
|
||||
u8 start_addr = (u8)(offset / HW_SEEPROM_BLK_SIZE);
|
||||
u8 start_addr_offset = (u8)(offset % HW_SEEPROM_BLK_SIZE);
|
||||
|
||||
u8 end_addr = (u8)((offset + size) / HW_SEEPROM_BLK_SIZE);
|
||||
u8 end_addr_size = (u8)((offset + size) % HW_SEEPROM_BLK_SIZE);
|
||||
|
||||
if (!end_addr_size)
|
||||
{
|
||||
end_addr--;
|
||||
end_addr_size = HW_SEEPROM_BLK_SIZE;
|
||||
}
|
||||
|
||||
if (end_addr == start_addr) end_addr_size -= start_addr_offset;
|
||||
|
||||
// Disable CPU interruptions
|
||||
_CPU_ISR_Disable(level);
|
||||
|
||||
mask32(HW_GPIO1OUT, GP_EEP_CLK, 0);
|
||||
mask32(HW_GPIO1OUT, GP_EEP_CS, 0);
|
||||
eeprom_delay();
|
||||
|
||||
// EWEN - Enable programming commands
|
||||
mask32(HW_GPIO1OUT, 0, GP_EEP_CS);
|
||||
seeprom_send_bits(0x4FF, 11);
|
||||
mask32(HW_GPIO1OUT, GP_EEP_CS, 0);
|
||||
eeprom_delay();
|
||||
|
||||
for(u16 i = start_addr; i <= end_addr; i++)
|
||||
{
|
||||
if (cur_offset >= size) break;
|
||||
|
||||
// Copy data to write from source buffer
|
||||
if ((i == start_addr && start_addr_offset != 0) || (i == end_addr && end_addr_size != HW_SEEPROM_BLK_SIZE))
|
||||
{
|
||||
// Read data from SEEPROM to handle unaligned writes
|
||||
|
||||
// Start command cycle
|
||||
mask32(HW_GPIO1OUT, 0, GP_EEP_CS);
|
||||
|
||||
// Send read command + address
|
||||
seeprom_send_bits(0x600 | i, 11);
|
||||
|
||||
// Receive data
|
||||
*((u16*)val) = seeprom_recv_bits(16);
|
||||
|
||||
// End of command cycle
|
||||
mask32(HW_GPIO1OUT, GP_EEP_CS, 0);
|
||||
eeprom_delay();
|
||||
|
||||
if (i == start_addr && start_addr_offset != 0)
|
||||
{
|
||||
// Handle unaligned write at start address
|
||||
memcpy(val + start_addr_offset, ptr + cur_offset, HW_SEEPROM_BLK_SIZE - start_addr_offset);
|
||||
cur_offset += (HW_SEEPROM_BLK_SIZE - start_addr_offset);
|
||||
} else {
|
||||
// Handle unaligned write at end address
|
||||
memcpy(val, ptr + cur_offset, end_addr_size);
|
||||
cur_offset += end_addr_size;
|
||||
}
|
||||
} else {
|
||||
// Normal write
|
||||
memcpy(val, ptr + cur_offset, HW_SEEPROM_BLK_SIZE);
|
||||
cur_offset += HW_SEEPROM_BLK_SIZE;
|
||||
}
|
||||
|
||||
// Start command cycle
|
||||
mask32(HW_GPIO1OUT, 0, GP_EEP_CS);
|
||||
|
||||
// Send write command + address
|
||||
seeprom_send_bits(0x500 | i, 11);
|
||||
|
||||
// Send data
|
||||
seeprom_send_bits(*((u16*)val), 16);
|
||||
|
||||
// End of command cycle
|
||||
mask32(HW_GPIO1OUT, GP_EEP_CS, 0);
|
||||
eeprom_delay();
|
||||
|
||||
// Wait until SEEPROM is ready (write cycle is self-timed so no clocking needed)
|
||||
mask32(HW_GPIO1OUT, 0, GP_EEP_CS);
|
||||
|
||||
do {
|
||||
eeprom_delay();
|
||||
} while(!(read32(HW_GPIO1IN) & GP_EEP_MISO));
|
||||
|
||||
mask32(HW_GPIO1OUT, GP_EEP_CS, 0);
|
||||
eeprom_delay();
|
||||
}
|
||||
|
||||
// EWDS - Disable programming commands
|
||||
mask32(HW_GPIO1OUT, 0, GP_EEP_CS);
|
||||
seeprom_send_bits(0x400, 11);
|
||||
mask32(HW_GPIO1OUT, GP_EEP_CS, 0);
|
||||
eeprom_delay();
|
||||
|
||||
// Enable CPU interruptions
|
||||
_CPU_ISR_Restore(level);
|
||||
|
||||
return cur_offset;
|
||||
}
|
||||
#endif
|
59
source/mini_seeprom.h
Normal file
59
source/mini_seeprom.h
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
mini - a Free Software replacement for the Nintendo/BroadOn IOS.
|
||||
SEEPROM support
|
||||
|
||||
Copyright (C) 2008, 2009 Sven Peter <svenpeter@gmail.com>
|
||||
|
||||
# This code is licensed to you under the terms of the GNU GPL, version 2;
|
||||
# see http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
||||
*/
|
||||
|
||||
#ifndef __MINI_SEEPROM_H__
|
||||
#define __MINI_SEEPROM_H__
|
||||
|
||||
#define SEEPROM_SIZE 0x100
|
||||
|
||||
typedef struct
|
||||
{
|
||||
union {
|
||||
struct {
|
||||
u8 boot2version;
|
||||
u8 unknown1;
|
||||
u8 unknown2;
|
||||
u8 pad;
|
||||
u32 update_tag;
|
||||
};
|
||||
u8 data[8];
|
||||
};
|
||||
u16 checksum; // sum of data[] elements?
|
||||
} __attribute__((packed)) eep_boot2_ctr_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
union {
|
||||
u32 nand_gen; // matches offset 0x8 in nand SFFS blocks
|
||||
u8 data[4];
|
||||
};
|
||||
u16 checksum; // sum of data[] elements?
|
||||
} __attribute__((packed)) eep_nand_ctr_t;
|
||||
|
||||
struct SEEPROM
|
||||
{
|
||||
u32 ms_id; // 0x00000002
|
||||
u32 ca_id; // 0x00000001
|
||||
u32 ng_key_id;
|
||||
u8 ng_sig[60];
|
||||
eep_boot2_ctr_t boot2_counters[2];
|
||||
eep_nand_ctr_t nand_counters[3]; // current slot rotates on each write
|
||||
u8 pad0[6];
|
||||
u8 korean_key[16];
|
||||
u8 pad1[116];
|
||||
u16 prng_seed[2]; // u32 with lo word stored first, incremented every time IOS starts. Used with the PRNG key to setup IOS's PRNG (syscalls 73/74 etc.)
|
||||
u8 pad2[4];
|
||||
};
|
||||
_Static_assert(sizeof(struct SEEPROM) == SEEPROM_SIZE, "SEEPROM struct size incorrect!");
|
||||
|
||||
u16 seeprom_read(void *dst, u16 offset, u16 size);
|
||||
// u16 seeprom_write(const void *src, u16 offset, u16 size);
|
||||
|
||||
#endif /* __MINI_SEEPROM_H__ */
|
67
source/otp.c
Normal file
67
source/otp.c
Normal file
@ -0,0 +1,67 @@
|
||||
#include <ogc/machine/processor.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "otp.h"
|
||||
|
||||
#define HW_OTP_COMMAND (*(vu32*)0xCD8001EC)
|
||||
#define HW_OTP_DATA (*(vu32*)0xCD8001F0)
|
||||
|
||||
#define HW_OTP_BLK_SIZE 4
|
||||
#define HW_OTP_BLK_CNT (OTP_SIZE / HW_OTP_BLK_SIZE)
|
||||
|
||||
u8 otp_read(void *dst, u8 offset, u8 size)
|
||||
{
|
||||
if (!dst || offset >= OTP_SIZE || !size || (offset + size) > OTP_SIZE) return 0;
|
||||
|
||||
u8 cur_offset = 0;
|
||||
|
||||
u8 *ptr = (u8*)dst;
|
||||
u8 val[HW_OTP_BLK_SIZE] = {0};
|
||||
|
||||
// Calculate block offsets and sizes
|
||||
u8 start_addr = (offset / HW_OTP_BLK_SIZE);
|
||||
u8 start_addr_offset = (offset % HW_OTP_BLK_SIZE);
|
||||
|
||||
u8 end_addr = ((offset + size) / HW_OTP_BLK_SIZE);
|
||||
u8 end_addr_size = ((offset + size) % HW_OTP_BLK_SIZE);
|
||||
|
||||
if (!end_addr_size)
|
||||
{
|
||||
end_addr--;
|
||||
end_addr_size = HW_OTP_BLK_SIZE;
|
||||
}
|
||||
|
||||
if (end_addr == start_addr) end_addr_size -= start_addr_offset;
|
||||
|
||||
for(u8 i = start_addr; i <= end_addr; i++)
|
||||
{
|
||||
if (cur_offset >= size) break;
|
||||
|
||||
// Send command + address
|
||||
HW_OTP_COMMAND = (0x80000000 | i);
|
||||
|
||||
// Receive data
|
||||
*((u32*)val) = HW_OTP_DATA;
|
||||
|
||||
// Copy read data to destination buffer
|
||||
if (i == start_addr && start_addr_offset != 0)
|
||||
{
|
||||
// Handle unaligned read at start address
|
||||
memcpy(ptr + cur_offset, val + start_addr_offset, HW_OTP_BLK_SIZE - start_addr_offset);
|
||||
cur_offset += (HW_OTP_BLK_SIZE - start_addr_offset);
|
||||
} else
|
||||
if (i == end_addr && end_addr_size != HW_OTP_BLK_SIZE)
|
||||
{
|
||||
// Handle unaligned read at end address
|
||||
memcpy(ptr + cur_offset, val, end_addr_size);
|
||||
cur_offset += end_addr_size;
|
||||
} else {
|
||||
// Normal read
|
||||
memcpy(ptr + cur_offset, val, HW_OTP_BLK_SIZE);
|
||||
cur_offset += HW_OTP_BLK_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
return cur_offset;
|
||||
}
|
29
source/otp.h
Normal file
29
source/otp.h
Normal file
@ -0,0 +1,29 @@
|
||||
#ifndef __OTP_H__
|
||||
#define __OTP_H__
|
||||
|
||||
#define OTP_SIZE 0x80
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u8 boot1_hash[20];
|
||||
u8 common_key[16];
|
||||
u8 ng_id[4];
|
||||
union { // first two bytes of nand_hmac overlap last two bytes of ng_priv
|
||||
struct {
|
||||
u8 ng_priv[30];
|
||||
u8 _wtf1[18];
|
||||
};
|
||||
struct {
|
||||
u8 _wtf2[28];
|
||||
u8 nand_hmac[20];
|
||||
};
|
||||
};
|
||||
u8 nand_key[16];
|
||||
u8 rng_key[16];
|
||||
u32 unk1;
|
||||
u32 unk2; // 0x00000007
|
||||
} otp_t;
|
||||
|
||||
u8 otp_read(void *dst, u8 offset, u8 size);
|
||||
|
||||
#endif /* __OTP_H__ */
|
107
source/sys.c
107
source/sys.c
@ -1,8 +1,12 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <ogcsys.h>
|
||||
#include <ogc/es.h>
|
||||
|
||||
#include "sys.h"
|
||||
#include "aes.h"
|
||||
#include "nand.h"
|
||||
#include "mini_seeprom.h"
|
||||
#include "malloc.h"
|
||||
#include "mload.h"
|
||||
#include "ehcmodule_elf.h"
|
||||
@ -39,6 +43,23 @@ bool tmdIsStubIOS(tmd* p_tmd)
|
||||
&& p_tmd->contents[2].type == 0x8001;
|
||||
}
|
||||
|
||||
bool ES_CheckHasKoreanKey(void)
|
||||
{
|
||||
aeskey korean_key;
|
||||
unsigned char iv[16] = {};
|
||||
|
||||
__attribute__ ((__aligned__(0x10)))
|
||||
unsigned char data[16] = {0x56, 0x52, 0x6f, 0x63, 0xa1, 0x2c, 0xd1, 0x32, 0x07, 0x99, 0x82, 0x3b, 0x1b, 0x08, 0x17, 0xd0};
|
||||
|
||||
if (seeprom_read(korean_key, offsetof(struct SEEPROM, korean_key), sizeof(korean_key)) != sizeof(korean_key))
|
||||
return false;
|
||||
|
||||
AES_Decrypt(korean_key, 0x10, iv, 0x10, data, data, sizeof(data));
|
||||
|
||||
// return (!strcmp((char*) data, "thepikachugamer")) Just remembered that this is how the Trucha bug came to be
|
||||
return (!memcmp(data, "thepikachugamer", sizeof(data)));
|
||||
}
|
||||
|
||||
bool isIOSstub(u8 ios_number)
|
||||
{
|
||||
u32 tmd_size = 0;
|
||||
@ -214,6 +235,92 @@ s32 Sys_GetCerts(signed_blob **certs, u32 *len)
|
||||
return ret;
|
||||
}
|
||||
|
||||
s32 Sys_GetSharedContents(SharedContent** out, u32* count)
|
||||
{
|
||||
if (!out || !count) return false;
|
||||
|
||||
int ret = 0;
|
||||
u32 size;
|
||||
SharedContent* buf = (SharedContent*)NANDLoadFile("/shared1/content.map", &size);
|
||||
|
||||
if (!buf)
|
||||
return (s32)size;
|
||||
|
||||
else if (size % sizeof(SharedContent) != 0) {
|
||||
free(buf);
|
||||
return -996;
|
||||
}
|
||||
|
||||
*out = buf;
|
||||
*count = size / sizeof(SharedContent);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool Sys_SharedContentPresent(tmd_content* content, SharedContent shared[], u32 count)
|
||||
{
|
||||
if (!shared || !content || !count)
|
||||
return false;
|
||||
|
||||
if (!(content->type & 0x8000))
|
||||
return false;
|
||||
|
||||
for (SharedContent* s_content = shared; s_content < shared + count; s_content++)
|
||||
{
|
||||
if (memcmp(s_content->hash, content->hash, sizeof(sha1)) == 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Sys_GetcIOSInfo(int IOS, cIOSInfo* out)
|
||||
{
|
||||
int ret;
|
||||
u64 titleID = 0x0000000100000000ULL | IOS;
|
||||
ATTRIBUTE_ALIGN(0x20) char path[ISFS_MAXPATH];
|
||||
u32 size;
|
||||
cIOSInfo* buf = NULL;
|
||||
|
||||
u32 view_size = 0;
|
||||
if (ES_GetTMDViewSize(titleID, &view_size) < 0)
|
||||
return false;
|
||||
|
||||
tmd_view* view = memalign32(view_size);
|
||||
if (!view)
|
||||
return false;
|
||||
|
||||
if (ES_GetTMDView(titleID, (u8*)view, view_size) < 0)
|
||||
goto fail;
|
||||
|
||||
tmd_view_content* content0 = NULL;
|
||||
|
||||
for (tmd_view_content* con = view->contents; con < view->contents + view->num_contents; con++)
|
||||
{
|
||||
if (con->index == 0)
|
||||
content0 = con;
|
||||
}
|
||||
|
||||
if (!content0)
|
||||
goto fail;
|
||||
|
||||
sprintf(path, "/title/00000001/%08x/content/%08x.app", IOS, content0->cid);
|
||||
buf = (cIOSInfo*)NANDLoadFile(path, &size);
|
||||
|
||||
if (!buf || size != 0x40 || buf->hdr_magic != CIOS_INFO_MAGIC || buf->hdr_version != CIOS_INFO_VERSION)
|
||||
goto fail;
|
||||
|
||||
*out = *buf;
|
||||
free(view);
|
||||
free(buf);
|
||||
return true;
|
||||
|
||||
fail:
|
||||
free(view);
|
||||
free(buf);
|
||||
return false;
|
||||
}
|
||||
|
||||
void SetPRButtons(bool enabled)
|
||||
{
|
||||
gDisablePRButtons = !enabled;
|
||||
|
35
source/sys.h
35
source/sys.h
@ -1,16 +1,51 @@
|
||||
#ifndef _SYS_H_
|
||||
#define _SYS_H_
|
||||
|
||||
/* /shared1/content.map entry */
|
||||
typedef struct
|
||||
{
|
||||
char filename[8];
|
||||
sha1 hash;
|
||||
} ATTRIBUTE_PACKED SharedContent;
|
||||
|
||||
/* "cIOS build tag" */
|
||||
enum
|
||||
{
|
||||
CIOS_INFO_MAGIC = 0x1EE7C105,
|
||||
CIOS_INFO_VERSION = 1
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u32 hdr_magic; // 0x1EE7C105
|
||||
u32 hdr_version; // 1
|
||||
u32 cios_version; // Eg. 11
|
||||
u32 ios_base; // Eg. 60
|
||||
|
||||
char name[16];
|
||||
char cios_version_str[16];
|
||||
|
||||
char _padding[16];
|
||||
} cIOSInfo;
|
||||
// _Static_assert(sizeof(cIOSInfo) == 0x40, "Incorrect cIOSInfo struct size, do i really need to pack this..?");
|
||||
|
||||
#define IS_WIIU (*(vu16*)0xCD0005A0 == 0xCAFE)
|
||||
|
||||
extern u32 boot2version;
|
||||
|
||||
/* Prototypes */
|
||||
bool isIOSstub(u8 ios_number);
|
||||
bool tmdIsStubIOS(tmd*);
|
||||
bool loadIOS(int ios);
|
||||
bool ES_CheckHasKoreanKey(void);
|
||||
void Sys_Init(void);
|
||||
void Sys_Reboot(void);
|
||||
void Sys_Shutdown(void);
|
||||
void Sys_LoadMenu(void);
|
||||
s32 Sys_GetCerts(signed_blob **, u32 *);
|
||||
bool Sys_GetcIOSInfo(int IOS, cIOSInfo*);
|
||||
s32 Sys_GetSharedContents(SharedContent** out, u32* count);
|
||||
bool Sys_SharedContentPresent(tmd_content* content, SharedContent shared[], u32 count);
|
||||
void SetPRButtons(bool enabled);
|
||||
|
||||
#endif
|
||||
|
@ -3,9 +3,12 @@
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
#include <ogcsys.h>
|
||||
#include <ogc/es.h>
|
||||
|
||||
#include "sha1.h"
|
||||
#include "aes.h"
|
||||
#include "utils.h"
|
||||
#include "otp.h"
|
||||
#include "malloc.h"
|
||||
|
||||
s32 Title_ZeroSignature(signed_blob *p_sig)
|
||||
@ -315,3 +318,26 @@ out:
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
__attribute__((aligned(0x10)))
|
||||
aeskey WiiCommonKey, vWiiCommonKey;
|
||||
|
||||
void Title_SetupCommonKeys(void)
|
||||
{
|
||||
static bool keys_ok = false;
|
||||
if (keys_ok)
|
||||
return;
|
||||
|
||||
// Grab the Wii common key...
|
||||
otp_read(WiiCommonKey, offsetof(otp_t, common_key), sizeof(aeskey));
|
||||
|
||||
// ...and decrypt the vWii common key with it.
|
||||
static const unsigned char vwii_key_enc_bin[0x10] = { 0x6e, 0x18, 0xdb, 0x23, 0x84, 0x7c, 0xba, 0x6c, 0x19, 0x31, 0xa4, 0x17, 0x9b, 0xaf, 0x8e, 0x09 };
|
||||
unsigned char iv[0x10] = {};
|
||||
|
||||
memcpy(vWiiCommonKey, vwii_key_enc_bin, sizeof(vwii_key_enc_bin));
|
||||
AES_Decrypt(WiiCommonKey, sizeof(aeskey), iv, sizeof(iv), vWiiCommonKey, vWiiCommonKey, sizeof(aeskey));
|
||||
|
||||
keys_ok = true;
|
||||
return;
|
||||
};
|
||||
|
@ -1,9 +1,14 @@
|
||||
#ifndef _TITLE_H_
|
||||
#define _TITLE_H_
|
||||
|
||||
#include <ogc/es.h>
|
||||
|
||||
/* Constants */
|
||||
#define BLOCK_SIZE 0x4000
|
||||
|
||||
/* Variables */
|
||||
extern aeskey WiiCommonKey, vWiiCommonKey;
|
||||
|
||||
/* Prototypes */
|
||||
s32 Title_ZeroSignature(signed_blob *);
|
||||
s32 Title_FakesignTik(signed_blob *);
|
||||
@ -15,5 +20,6 @@ s32 Title_GetVersion(u64, u16 *);
|
||||
s32 Title_GetSysVersion(u64, u64 *);
|
||||
s32 Title_GetSize(u64, u32 *);
|
||||
s32 Title_GetIOSVersions(u8 **, u32 *);
|
||||
void Title_SetupCommonKeys(void);
|
||||
|
||||
#endif
|
||||
|
@ -8,6 +8,8 @@
|
||||
#include <wiidrc/wiidrc.h>
|
||||
|
||||
#include "sys.h"
|
||||
#include "title.h"
|
||||
#include "aes.h"
|
||||
#include "gui.h"
|
||||
#include "menu.h"
|
||||
#include "restart.h"
|
||||
@ -183,6 +185,9 @@ int main(int argc, char **argv)
|
||||
WKB_Initialize();
|
||||
WIILIGHT_Init();
|
||||
|
||||
AES_Init();
|
||||
Title_SetupCommonKeys();
|
||||
|
||||
/* Print disclaimer */
|
||||
//Disclaimer();
|
||||
|
||||
|
470
source/wad.c
470
source/wad.c
@ -9,6 +9,8 @@
|
||||
#include "title.h"
|
||||
#include "utils.h"
|
||||
#include "aes.h"
|
||||
#include "mini_seeprom.h"
|
||||
#include "otp.h"
|
||||
#include "video.h"
|
||||
#include "wad.h"
|
||||
#include "wpad.h"
|
||||
@ -555,52 +557,42 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const aeskey
|
||||
WiiCommonKey = { 0xeb, 0xe4, 0x2a, 0x22, 0x5e, 0x85, 0x93, 0xe4, 0x48, 0xd9, 0xc5, 0x45, 0x73, 0x81, 0xaa, 0xf7 },
|
||||
vWiiCommonKey = { 0x30, 0xbf, 0xc7, 0x6e, 0x7c, 0x19, 0xaf, 0xbb, 0x23, 0x16, 0x33, 0x30, 0xce, 0xd7, 0xc2, 0x8d };
|
||||
|
||||
void __Wad_FixTicket(signed_blob *s_tik)
|
||||
bool __Wad_FixTicket(signed_blob *s_tik)
|
||||
{
|
||||
tik* p_tik = SIGNATURE_PAYLOAD(s_tik);
|
||||
u8 *ckey = ((u8*)s_tik) + 0x1F1;
|
||||
|
||||
/*
|
||||
* Alright. I'd hate to pull this off on signed tickets using the vWii common key.
|
||||
* But this already does it, just without re-crypting the title key. So let's do it.
|
||||
*/
|
||||
bool fixKey = *ckey == 2;
|
||||
bool fixvWiiKey = *ckey == 2;
|
||||
if (*ckey > 1) {
|
||||
/* Set common key */
|
||||
*ckey = 0;
|
||||
|
||||
/* Fix tickets using vWii Common Key */
|
||||
if (fixKey)
|
||||
if (fixvWiiKey)
|
||||
{
|
||||
__attribute__((aligned(0x10)))
|
||||
static unsigned char keybuf[0x10], iv[0x10];
|
||||
__aligned(0x20)
|
||||
aeskey tkeybuf;
|
||||
u64 iv[2];
|
||||
|
||||
u8* titlekey = p_tik->cipher_title_key;
|
||||
u64* titleid = &p_tik->titleid;
|
||||
memcpy(tkeybuf, p_tik->cipher_title_key, sizeof(aeskey));
|
||||
iv[0] = p_tik->titleid;
|
||||
iv[1] = 0;
|
||||
|
||||
memcpy(keybuf, titlekey, sizeof(keybuf));
|
||||
memcpy(iv, titleid, sizeof(u64));
|
||||
memset(iv + 8, 0, sizeof(iv) - 8);
|
||||
AES_Decrypt(vWiiCommonKey, 0x10, iv, 0x10, tkeybuf, tkeybuf, sizeof(tkeybuf));
|
||||
|
||||
AES_Init();
|
||||
AES_Decrypt(vWiiCommonKey, 0x10, iv, 0x10, keybuf, keybuf, sizeof(keybuf));
|
||||
iv[0] = p_tik->titleid;
|
||||
iv[1] = 0;
|
||||
|
||||
memcpy(iv, titleid, sizeof(u64));
|
||||
memset(iv + 8, 0, sizeof(iv) - 8);
|
||||
AES_Encrypt(WiiCommonKey, 0x10, iv, 0x10, tkeybuf, tkeybuf, sizeof(tkeybuf));
|
||||
|
||||
AES_Encrypt(WiiCommonKey, 0x10, iv, 0x10, keybuf, keybuf, sizeof(keybuf));
|
||||
|
||||
memcpy(titlekey, keybuf, sizeof(keybuf));
|
||||
AES_Close();
|
||||
memcpy(p_tik->cipher_title_key, tkeybuf, sizeof(tkeybuf));
|
||||
}
|
||||
|
||||
/* Fakesign ticket */
|
||||
Title_FakesignTik(s_tik);
|
||||
}
|
||||
|
||||
return fixvWiiKey;
|
||||
}
|
||||
|
||||
bool __Wad_VerifyHeader(wadHeader* header)
|
||||
@ -611,65 +603,6 @@ bool __Wad_VerifyHeader(wadHeader* header)
|
||||
&& header->padding == 0x00;
|
||||
}
|
||||
|
||||
/* /shared1/content.map entry */
|
||||
typedef struct
|
||||
{
|
||||
char filename[8];
|
||||
sha1 hash;
|
||||
} ATTRIBUTE_PACKED SharedContent;
|
||||
|
||||
bool GetSharedContents(SharedContent** out, u32* count)
|
||||
{
|
||||
if (!out || !count) return false;
|
||||
|
||||
int ret;
|
||||
SharedContent* buf = NULL;
|
||||
|
||||
int fd = ret = ISFS_Open("/shared1/content.map", ISFS_OPEN_READ);
|
||||
if (ret < 0)
|
||||
return false;
|
||||
|
||||
int len = ISFS_Seek(fd, 0, SEEK_END);
|
||||
ISFS_Seek(fd, 0, SEEK_SET);
|
||||
if (len <= 0 || len % sizeof(SharedContent))
|
||||
goto fail;
|
||||
|
||||
buf = memalign32(len);
|
||||
if (!buf)
|
||||
goto fail;
|
||||
|
||||
ret = ISFS_Read(fd, buf, len);
|
||||
if (ret != len)
|
||||
goto fail;
|
||||
|
||||
ISFS_Close(fd);
|
||||
|
||||
*out = buf;
|
||||
*count = len / sizeof(SharedContent);
|
||||
|
||||
return true;
|
||||
|
||||
fail:
|
||||
ISFS_Close(fd);
|
||||
free(buf);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SharedContentPresent(tmd_content* content, SharedContent shared[], u32 count)
|
||||
{
|
||||
if (!shared || !content || !count) return false;
|
||||
|
||||
if (!(content->type & 0x8000)) return false;
|
||||
|
||||
for (SharedContent* s_content = shared; s_content < shared + count; s_content++)
|
||||
{
|
||||
if (memcmp(s_content->hash, content->hash, sizeof(sha1)) == 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Some of the safety checks can block region changing
|
||||
// Entering the Konami code turns this true, so it will
|
||||
// skip the problematic checks for region changing.
|
||||
@ -738,6 +671,8 @@ s32 Wad_Install(FILE *fp)
|
||||
if (ret != 1)
|
||||
goto err;
|
||||
|
||||
bool isvWiiTitle = __Wad_FixTicket(p_tik);
|
||||
|
||||
offset += round_up(header->tik_len, 64);
|
||||
|
||||
/* WAD TMD */
|
||||
@ -753,180 +688,263 @@ s32 Wad_Install(FILE *fp)
|
||||
|
||||
tmd_data = (tmd *)SIGNATURE_PAYLOAD(p_tmd);
|
||||
|
||||
if(TITLE_LOWER(tmd_data->sys_version) != 0 && isIOSstub(TITLE_LOWER(tmd_data->sys_version)))
|
||||
if (TITLE_UPPER(tmd_data->sys_version) == 0) // IOS
|
||||
{
|
||||
printf("\n This Title wants IOS%i but the installed version\n is a stub.\n", TITLE_LOWER(tmd_data->sys_version));
|
||||
ret = -1036;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if(get_title_ios(TITLE_ID(1, 2)) == tid)
|
||||
{
|
||||
if (tmdIsStubIOS(tmd_data))
|
||||
if ((isvWiiTitle || tmd_data->vwii_title) ^ IS_WIIU) // xor is one of my favourite binary operators of all time
|
||||
{
|
||||
printf("\n I won't install a stub System Menu IOS\n");
|
||||
printf("\n Cannot install vWii IOS on Wii\n or Wii IOS on vWii.\n");
|
||||
ret = -999;
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
if(tid == get_title_ios(TITLE_ID(0x10008, 0x48414B00 | 'E')) || tid == get_title_ios(TITLE_ID(0x10008, 0x48414B00 | 'P')) || tid == get_title_ios(TITLE_ID(0x10008, 0x48414B00 | 'J')) || tid == get_title_ios(TITLE_ID(0x10008, 0x48414B00 | 'K')))
|
||||
{
|
||||
if (tmdIsStubIOS(tmd_data))
|
||||
if(tid == get_title_ios(TITLE_ID(1, 2)))
|
||||
{
|
||||
printf("\n I won't install a stub EULA IOS\n");
|
||||
ret = -999;
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
if(tid == get_title_ios(TITLE_ID(0x10008, 0x48414C00 | 'E')) || tid == get_title_ios(TITLE_ID(0x10008, 0x48414C00 | 'P')) || tid == get_title_ios(TITLE_ID(0x10008, 0x48414C00 | 'J')) || tid == get_title_ios(TITLE_ID(0x10008, 0x48414C00 | 'K')))
|
||||
{
|
||||
if (tmdIsStubIOS(tmd_data))
|
||||
{
|
||||
printf("\n I won't install a stub rgnsel IOS\n");
|
||||
ret = -999;
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
if (tid == get_title_ios(TITLE_ID(0x10001, 0x48415858)) || tid == get_title_ios(TITLE_ID(0x10001, 0x4A4F4449)))
|
||||
{
|
||||
if (tmdIsStubIOS(tmd_data))
|
||||
{
|
||||
printf("\n Are you sure you wan't to install a stub HBC IOS?\n");
|
||||
printf("\n Press A to continue.\n");
|
||||
printf(" Press B skip.");
|
||||
|
||||
u32 buttons = WaitButtons();
|
||||
|
||||
if (!(buttons & WPAD_BUTTON_A))
|
||||
if (tmdIsStubIOS(tmd_data))
|
||||
{
|
||||
ret = -998;
|
||||
printf("\n I won't install a stub System Menu IOS\n");
|
||||
ret = -999;
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tid == TITLE_ID(1, 2))
|
||||
{
|
||||
if (skipRegionSafetyCheck || gForcedInstall)
|
||||
goto skipChecks;
|
||||
|
||||
char region = 0;
|
||||
u16 version = 0;
|
||||
|
||||
GetSysMenuRegion(&version, ®ion);
|
||||
|
||||
if (tmd_data->vwii_title)
|
||||
{
|
||||
printf("\n I won't install a vWii SM by default.\n\n");
|
||||
printf("\n If you're really sure what you're doing, next time\n");
|
||||
printf(" select your device using Konami...\n\n");
|
||||
|
||||
ret = -999;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if(region == 0)
|
||||
{
|
||||
printf("\n Unknown System menu region\n Please check the site for updates\n");
|
||||
|
||||
ret = -999;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (!VersionIsOriginal(tmd_data->title_version))
|
||||
{
|
||||
printf("\n I won't install an unkown SM versions by default.\n\n");
|
||||
printf("\n Are you installing a tweaked system menu?\n");
|
||||
printf("\n If you're really sure what you're doing, next time\n");
|
||||
printf(" select your device using Konami...\n\n");
|
||||
|
||||
ret = -999;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if(region != RegionLookupTable[(tmd_data->title_version & 0x0F)])
|
||||
{
|
||||
printf("\n I won't install the wrong regions SM by default.\n\n");
|
||||
printf("\n Are you region changing?\n");
|
||||
printf("\n If you're really sure what you're doing, next time\n");
|
||||
printf(" select your device using Konami...\n\n");
|
||||
|
||||
ret = -999;
|
||||
goto err;
|
||||
}
|
||||
skipChecks:
|
||||
if(tmd_data->title_version < 416)
|
||||
{
|
||||
if(boot2version == 4)
|
||||
// this code feels like a MESS
|
||||
else if (!IS_WIIU && (tid == TITLE_ID(1, 70) || tid == TITLE_ID(1, 80)))
|
||||
{
|
||||
printf("\n This version of the System Menu\n is not compatible with your Wii\n");
|
||||
tik* ticket = (tik*)SIGNATURE_PAYLOAD(p_tik);
|
||||
|
||||
__aligned(0x10)
|
||||
aeskey titlekey;
|
||||
u64 iv[2] = { tid };
|
||||
|
||||
|
||||
memcpy(titlekey, ticket->cipher_title_key, sizeof(aeskey));
|
||||
AES_Decrypt(WiiCommonKey, sizeof(aeskey), iv, sizeof(iv), titlekey, titlekey, sizeof(aeskey));
|
||||
|
||||
u32 content0_offset = offset;
|
||||
for (tmd_content* con = tmd_data->contents; con < tmd_data->contents + tmd_data->num_contents; con++)
|
||||
{
|
||||
if (con->index == 0) break;
|
||||
content0_offset += round_up(con->size, 0x40);
|
||||
}
|
||||
|
||||
__aligned(0x20)
|
||||
cIOSInfo build_tag = {};
|
||||
|
||||
ret = FSOPReadOpenFile(fp, (void*)&build_tag, content0_offset, sizeof(cIOSInfo));
|
||||
if (ret != 1)
|
||||
goto err;
|
||||
|
||||
iv[0] = 0;
|
||||
iv[1] = 0;
|
||||
AES_Decrypt(titlekey, sizeof(aeskey), iv, sizeof(iv), &build_tag, &build_tag, sizeof(cIOSInfo));
|
||||
|
||||
if (build_tag.hdr_magic != CIOS_INFO_MAGIC ||
|
||||
build_tag.hdr_version != CIOS_INFO_VERSION ||
|
||||
(build_tag.ios_base != 60 && ES_CheckHasKoreanKey()))
|
||||
{
|
||||
printf("\n"
|
||||
" Installing this System menu IOS will brick your Wii.\n"
|
||||
" Please remove the Korean key via KoreanKii,\n"
|
||||
" then try again.\n\n"
|
||||
);
|
||||
|
||||
ret = -999;
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(tid == get_title_ios(TITLE_ID(0x10008, 0x48414B00 | 'E'))
|
||||
|| tid == get_title_ios(TITLE_ID(0x10008, 0x48414B00 | 'P'))
|
||||
|| tid == get_title_ios(TITLE_ID(0x10008, 0x48414B00 | 'J'))
|
||||
|| tid == get_title_ios(TITLE_ID(0x10008, 0x48414B00 | 'K')))
|
||||
{
|
||||
if (tmdIsStubIOS(tmd_data))
|
||||
{
|
||||
printf("\n I won't install a stub EULA IOS\n");
|
||||
ret = -999;
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
if (!gForcedInstall && AHBPROT_DISABLED && IsPriiloaderInstalled())
|
||||
if(tid == get_title_ios(TITLE_ID(0x10008, 0x48414C00 | 'E'))
|
||||
|| tid == get_title_ios(TITLE_ID(0x10008, 0x48414C00 | 'P'))
|
||||
|| tid == get_title_ios(TITLE_ID(0x10008, 0x48414C00 | 'J'))
|
||||
|| tid == get_title_ios(TITLE_ID(0x10008, 0x48414C00 | 'K')))
|
||||
{
|
||||
cleanupPriiloader = true;
|
||||
printf("\n Priiloader is installed next to the system menu.\n\n");
|
||||
printf(" It is recommended to retain Priiloader as it can\n");
|
||||
printf(" protect your console from being bricked.\n\n");
|
||||
printf(" Press A to retain Priiloader or B to remove.");
|
||||
|
||||
u32 buttons = WaitButtons();
|
||||
|
||||
if ((buttons & WPAD_BUTTON_A))
|
||||
if (tmdIsStubIOS(tmd_data))
|
||||
{
|
||||
retainPriiloader = (BackUpPriiloader() && CompareHashes(true));
|
||||
if (retainPriiloader)
|
||||
printf("\n I won't install a stub rgnsel IOS\n");
|
||||
ret = -999;
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
if (tid == get_title_ios(TITLE_ID(0x10001, 0x48415858)) || tid == get_title_ios(TITLE_ID(0x10001, 0x4A4F4449)))
|
||||
{
|
||||
if (tmdIsStubIOS(tmd_data))
|
||||
{
|
||||
printf("\n Are you sure you wan't to install a stub HBC IOS?\n");
|
||||
printf("\n Press A to continue.\n");
|
||||
printf(" Press B skip.");
|
||||
|
||||
u32 buttons = WaitButtons();
|
||||
|
||||
if (!(buttons & WPAD_BUTTON_A))
|
||||
{
|
||||
SetPriiloaderOption(true);
|
||||
Con_ClearLine();
|
||||
printf("\r[+] Priiloader will be retained.\n");
|
||||
fflush(stdout);
|
||||
ret = -998;
|
||||
goto err;
|
||||
}
|
||||
else
|
||||
}
|
||||
}
|
||||
}
|
||||
else // not IOS
|
||||
{
|
||||
if (isIOSstub(TITLE_LOWER(tmd_data->sys_version)))
|
||||
{
|
||||
printf("\n This Title wants IOS%i but the installed version\n is a stub.\n", TITLE_LOWER(tmd_data->sys_version));
|
||||
ret = -1036;
|
||||
goto err;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (tid == TITLE_ID(1, 2))
|
||||
{
|
||||
if (skipRegionSafetyCheck || gForcedInstall)
|
||||
goto skipChecks;
|
||||
|
||||
char region = 0;
|
||||
u16 version = 0;
|
||||
|
||||
GetSysMenuRegion(&version, ®ion);
|
||||
|
||||
if (isvWiiTitle || tmd_data->vwii_title) // && !IS_WIIU ? :thinking:
|
||||
{
|
||||
printf("\n I won't install a vWii SM by default.\n\n");
|
||||
printf("\n If you're really sure what you're doing, next time\n");
|
||||
printf(" select your device using Konami...\n\n");
|
||||
|
||||
ret = -999;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if(region == 0)
|
||||
{
|
||||
printf("\n Unknown System menu region\n Please check the site for updates\n");
|
||||
|
||||
ret = -999;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (!VersionIsOriginal(tmd_data->title_version))
|
||||
{
|
||||
printf("\n I won't install an unkown SM versions by default.\n\n");
|
||||
printf("\n Are you installing a tweaked system menu?\n");
|
||||
printf("\n If you're really sure what you're doing, next time\n");
|
||||
printf(" select your device using Konami...\n\n");
|
||||
|
||||
ret = -999;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if(region != RegionLookupTable[(tmd_data->title_version & 0x0F)])
|
||||
{
|
||||
printf("\n I won't install the wrong regions SM by default.\n\n");
|
||||
printf("\n Are you region changing?\n");
|
||||
printf("\n If you're really sure what you're doing, next time\n");
|
||||
printf(" select your device using Konami...\n\n");
|
||||
|
||||
ret = -999;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if ((tmd_data->title_version & 0x1F) != 0x6 // 0x6 = KR
|
||||
&& (tmd_data->title_version >> 5) >= 15) // 4.2 or later
|
||||
{
|
||||
cIOSInfo ios_info;
|
||||
|
||||
if (!Sys_GetcIOSInfo(TITLE_LOWER(tmd_data->sys_version), &ios_info) ||
|
||||
(ios_info.ios_base != 60 && ES_CheckHasKoreanKey()))
|
||||
{
|
||||
Con_ClearLine();
|
||||
printf("\r Couldn't backup Priiloader.\n");
|
||||
fflush(stdout);
|
||||
printf("\n Press A to continue or B to skip");
|
||||
printf("\n"
|
||||
" Installing this System menu will brick your Wii.\n"
|
||||
" Please remove the Korean key via KoreanKii,\n"
|
||||
" then try again.\n\n"
|
||||
);
|
||||
|
||||
u32 buttons = WaitButtons();
|
||||
ret = -999;
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(buttons & WPAD_BUTTON_A))
|
||||
skipChecks:
|
||||
if (tmd_data->title_version < 416)
|
||||
{
|
||||
if(boot2version == 4)
|
||||
{
|
||||
printf("\n This version of the System Menu\n is not compatible with your Wii\n");
|
||||
ret = -999;
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
if (!gForcedInstall && AHBPROT_DISABLED && IsPriiloaderInstalled())
|
||||
{
|
||||
cleanupPriiloader = true;
|
||||
printf("\n Priiloader is installed next to the system menu.\n\n");
|
||||
printf(" It is recommended to retain Priiloader as it can\n");
|
||||
printf(" protect your console from being bricked.\n\n");
|
||||
printf(" Press A to retain Priiloader or B to remove.");
|
||||
|
||||
u32 buttons = WaitButtons();
|
||||
|
||||
if ((buttons & WPAD_BUTTON_A))
|
||||
{
|
||||
retainPriiloader = (BackUpPriiloader() && CompareHashes(true));
|
||||
if (retainPriiloader)
|
||||
{
|
||||
ret = -990;
|
||||
goto err;
|
||||
SetPriiloaderOption(true);
|
||||
Con_ClearLine();
|
||||
printf("\r[+] Priiloader will be retained.\n");
|
||||
fflush(stdout);
|
||||
}
|
||||
else
|
||||
{
|
||||
Con_ClearLine();
|
||||
printf("\r Couldn't backup Priiloader.\n");
|
||||
fflush(stdout);
|
||||
printf("\n Press A to continue or B to skip");
|
||||
|
||||
u32 buttons = WaitButtons();
|
||||
|
||||
if (!(buttons & WPAD_BUTTON_A))
|
||||
{
|
||||
ret = -990;
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!retainPriiloader)
|
||||
if (!retainPriiloader)
|
||||
{
|
||||
SetPriiloaderOption(false);
|
||||
Con_ClearLine();
|
||||
printf("\r[+] Priiloader will be removed.\n");
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SetPriiloaderOption(false);
|
||||
Con_ClearLine();
|
||||
printf("\r[+] Priiloader will be removed.\n");
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
if (gForcedInstall)
|
||||
{
|
||||
SetPriiloaderOption(false);
|
||||
gForcedInstall = false;
|
||||
cleanupPriiloader = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (gForcedInstall)
|
||||
{
|
||||
gForcedInstall = false;
|
||||
cleanupPriiloader = true;
|
||||
}
|
||||
|
||||
/* Fix ticket */
|
||||
__Wad_FixTicket(p_tik);
|
||||
|
||||
printf("\t\t>> Installing ticket...");
|
||||
fflush(stdout);
|
||||
|
||||
@ -946,7 +964,7 @@ skipChecks:
|
||||
goto err;
|
||||
|
||||
/* Get list of currently installed shared contents */
|
||||
GetSharedContents(&sharedContents, &sharedContentsCount);
|
||||
Sys_GetSharedContents(&sharedContents, &sharedContentsCount);
|
||||
|
||||
/* Install contents */
|
||||
for (cnt = 0; cnt < tmd_data->num_contents; cnt++)
|
||||
@ -959,7 +977,7 @@ skipChecks:
|
||||
/* Encrypted content size */
|
||||
len = round_up(content->size, 64);
|
||||
|
||||
if (SharedContentPresent(content, sharedContents, sharedContentsCount))
|
||||
if (Sys_SharedContentPresent(content, sharedContents, sharedContentsCount))
|
||||
{
|
||||
offset += len;
|
||||
continue;
|
||||
@ -1259,6 +1277,8 @@ s32 Wad_Uninstall(FILE *fp)
|
||||
|
||||
Con_ClearLine();
|
||||
|
||||
/* Why don't we do this the other way around? Delete title contents, delete TMD, delete ticket. Seems more natural. */
|
||||
|
||||
printf("\t\t>> Deleting tickets...");
|
||||
fflush(stdout);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user