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);