From a8f7012cf7b332807d9881be01a9332a76723e74 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Sun, 8 Jan 2017 18:55:37 +0100 Subject: [PATCH] IOS/ES: Implement basic title installation. There are several things wrong with this implementation. The first being that since we still don't have a proper ticket/tmd handling library, we hardcode offsets once again to fetch TMD fields. The second being that we don't stream data to the disk and we buffer everything in memory. The third being that we don't properly fetch the content index for decryption, which is prone to breaking. But hey, it works well enough to install the DQX channel! --- .../Core/IPC_HLE/WII_IPC_HLE_Device_es.cpp | 139 ++++++++++++++++++ .../Core/Core/IPC_HLE/WII_IPC_HLE_Device_es.h | 6 + 2 files changed, 145 insertions(+) diff --git a/Source/Core/Core/IPC_HLE/WII_IPC_HLE_Device_es.cpp b/Source/Core/Core/IPC_HLE/WII_IPC_HLE_Device_es.cpp index 2d228d7982..dd20bbdb7d 100644 --- a/Source/Core/Core/IPC_HLE/WII_IPC_HLE_Device_es.cpp +++ b/Source/Core/Core/IPC_HLE/WII_IPC_HLE_Device_es.cpp @@ -53,6 +53,7 @@ #include "Core/HW/DVDInterface.h" #include "Core/HW/Memmap.h" #include "Core/HW/Wiimote.h" +#include "Core/IPC_HLE/ESFormats.h" #include "Core/IPC_HLE/WII_IPC_HLE_Device_es.h" #include "Core/IPC_HLE/WII_IPC_HLE_Device_usb_bt_emu.h" #include "Core/IPC_HLE/WII_IPC_HLE_WiiMote.h" @@ -151,6 +152,10 @@ void CWII_IPC_HLE_Device_es::DoState(PointerWrap& p) p.Do(m_AccessIdentID); p.Do(m_TitleIDs); + m_addtitle_tmd.DoState(p); + p.Do(m_addtitle_content_id); + p.Do(m_addtitle_content_buffer); + u32 Count = (u32)(m_ContentAccessMap.size()); p.Do(Count); @@ -276,6 +281,140 @@ IPCCommandResult CWII_IPC_HLE_Device_es::IOCtlV(u32 _CommandAddress) break; } + case IOCTL_ES_ADDTITLESTART: + { + _dbg_assert_msg_(WII_IPC_ES, Buffer.NumberInBuffer == 4, + "IOCTL_ES_ADDTITLESTART wrong number of inputs"); + + INFO_LOG(WII_IPC_ES, "IOCTL_ES_ADDTITLESTART"); + std::vector tmd(Buffer.InBuffer[0].m_Size); + Memory::CopyFromEmu(tmd.data(), Buffer.InBuffer[0].m_Address, Buffer.InBuffer[0].m_Size); + + m_addtitle_tmd.SetBytes(tmd); + if (!m_addtitle_tmd.IsValid()) + { + ERROR_LOG(WII_IPC_ES, "Invalid TMD while adding title (size = %zd)", tmd.size()); + Memory::Write_U32(ES_INVALID_TMD, _CommandAddress + 0x4); + return GetDefaultReply(); + } + + // Write the TMD to title storage. + std::string tmd_path = + Common::GetTMDFileName(m_addtitle_tmd.GetTitleId(), Common::FROM_SESSION_ROOT); + File::CreateFullPath(tmd_path); + + File::IOFile fp(tmd_path, "wb"); + fp.WriteBytes(tmd.data(), tmd.size()); + break; + } + + case IOCTL_ES_ADDCONTENTSTART: + { + _dbg_assert_msg_(WII_IPC_ES, Buffer.NumberInBuffer == 2, + "IOCTL_ES_ADDCONTENTSTART wrong number of inputs"); + + u64 title_id = Memory::Read_U64(Buffer.InBuffer[0].m_Address); + u32 content_id = Memory::Read_U32(Buffer.InBuffer[1].m_Address); + + if (m_addtitle_content_id != 0xFFFFFFFF) + { + ERROR_LOG(WII_IPC_ES, "Trying to add content when we haven't finished adding " + "another content. Unsupported."); + Memory::Write_U32(ES_WRITE_FAILURE, _CommandAddress + 0x4); + return GetDefaultReply(); + } + m_addtitle_content_id = content_id; + + m_addtitle_content_buffer.clear(); + + INFO_LOG(WII_IPC_ES, "IOCTL_ES_ADDCONTENTSTART: title id %016" PRIx64 ", " + "content id %08x", + title_id, m_addtitle_content_id); + + if (title_id != m_addtitle_tmd.GetTitleId()) + { + ERROR_LOG(WII_IPC_ES, "IOCTL_ES_ADDCONTENTSTART: title id %016" PRIx64 " != " + "TMD title id %016lx, ignoring", + title_id, m_addtitle_tmd.GetTitleId()); + } + + // We're supposed to return a "content file descriptor" here, which is + // passed to further AddContentData / AddContentFinish. But so far there is + // no known content installer which performs content addition concurrently. + // Instead we just log an error (see above) if this condition is detected. + s32 content_fd = 0; + Memory::Write_U32(content_fd, _CommandAddress + 0x4); + return GetDefaultReply(); + } + + case IOCTL_ES_ADDCONTENTDATA: + { + _dbg_assert_msg_(WII_IPC_ES, Buffer.NumberInBuffer == 2, + "IOCTL_ES_ADDCONTENTDATA wrong number of inputs"); + + u32 content_fd = Memory::Read_U32(Buffer.InBuffer[0].m_Address); + INFO_LOG(WII_IPC_ES, "IOCTL_ES_ADDCONTENTDATA: content fd %08x, " + "size %d", + content_fd, Buffer.InBuffer[1].m_Size); + + u8* data_start = Memory::GetPointer(Buffer.InBuffer[1].m_Address); + u8* data_end = data_start + Buffer.InBuffer[1].m_Size; + m_addtitle_content_buffer.insert(m_addtitle_content_buffer.end(), data_start, data_end); + break; + } + + case IOCTL_ES_ADDCONTENTFINISH: + { + _dbg_assert_msg_(WII_IPC_ES, Buffer.NumberInBuffer == 1, + "IOCTL_ES_ADDCONTENTFINISH wrong number of inputs"); + + u32 content_fd = Memory::Read_U32(Buffer.InBuffer[0].m_Address); + INFO_LOG(WII_IPC_ES, "IOCTL_ES_ADDCONTENTFINISH: content fd %08x", content_fd); + + // Try to find the title key from a pre-installed ticket. + std::vector ticket = DiscIO::FindSignedTicket(m_addtitle_tmd.GetTitleId()); + if (ticket.size() == 0) + { + Memory::Write_U32(ES_NO_TICKET_INSTALLED, _CommandAddress + 0x4); + return GetDefaultReply(); + } + + mbedtls_aes_context aes_ctx; + mbedtls_aes_setkey_dec(&aes_ctx, DiscIO::GetKeyFromTicket(ticket).data(), 128); + + // The IV for title content decryption is the lower two bytes of the + // content index, zero extended. + TMDReader::Content content_info; + if (!m_addtitle_tmd.FindContentById(m_addtitle_content_id, &content_info)) + { + Memory::Write_U32(ES_INVALID_TMD, _CommandAddress + 0x4); + return GetDefaultReply(); + } + u8 iv[16] = {0}; + iv[0] = (content_info.index >> 8) & 0xFF; + iv[1] = content_info.index & 0xFF; + std::vector decrypted_data(m_addtitle_content_buffer.size()); + mbedtls_aes_crypt_cbc(&aes_ctx, MBEDTLS_AES_DECRYPT, m_addtitle_content_buffer.size(), iv, + m_addtitle_content_buffer.data(), decrypted_data.data()); + + std::string path = StringFromFormat( + "%s%08x.app", + Common::GetTitleContentPath(m_addtitle_tmd.GetTitleId(), Common::FROM_SESSION_ROOT).c_str(), + m_addtitle_content_id); + + File::IOFile fp(path, "wb"); + fp.WriteBytes(decrypted_data.data(), decrypted_data.size()); + + m_addtitle_content_id = 0xFFFFFFFF; + break; + } + + case IOCTL_ES_ADDTITLEFINISH: + { + INFO_LOG(WII_IPC_ES, "IOCTL_ES_ADDTITLEFINISH"); + break; + } + case IOCTL_ES_GETDEVICEID: { _dbg_assert_msg_(WII_IPC_ES, Buffer.NumberPayloadBuffer == 1, diff --git a/Source/Core/Core/IPC_HLE/WII_IPC_HLE_Device_es.h b/Source/Core/Core/IPC_HLE/WII_IPC_HLE_Device_es.h index d679e66d49..9ca52d821e 100644 --- a/Source/Core/Core/IPC_HLE/WII_IPC_HLE_Device_es.h +++ b/Source/Core/Core/IPC_HLE/WII_IPC_HLE_Device_es.h @@ -10,6 +10,7 @@ #include #include "Common/CommonTypes.h" +#include "Core/IPC_HLE/ESFormats.h" #include "Core/IPC_HLE/WII_IPC_HLE.h" #include "Core/IPC_HLE/WII_IPC_HLE_Device.h" @@ -143,6 +144,11 @@ private: static u8* keyTable[11]; + // For title installation (ioctls IOCTL_ES_ADDTITLE*). + TMDReader m_addtitle_tmd; + u32 m_addtitle_content_id = 0xFFFFFFFF; + std::vector m_addtitle_content_buffer; + const DiscIO::CNANDContentLoader& AccessContentDevice(u64 title_id); u32 OpenTitleContent(u32 CFD, u64 TitleID, u16 Index);