diff --git a/Source/Core/Core/Boot/Boot.cpp b/Source/Core/Core/Boot/Boot.cpp index 42d3327946..46176ecf5c 100644 --- a/Source/Core/Core/Boot/Boot.cpp +++ b/Source/Core/Core/Boot/Boot.cpp @@ -102,7 +102,7 @@ bool CBoot::FindMapFile(std::string* existing_map_file, std::string* writable_ma DiscIO::CNANDContentManager::Access().GetNANDLoader(_StartupPara.m_strFilename); if (Loader.IsValid()) { - u64 TitleID = Loader.GetTitleID(); + u64 TitleID = Loader.GetTMD().GetTitleId(); title_id_str = StringFromFormat("%08X_%08X", (u32)(TitleID >> 32) & 0xFFFFFFFF, (u32)TitleID & 0xFFFFFFFF); } @@ -278,11 +278,7 @@ bool CBoot::BootUp() PanicAlertT("Warning - starting ISO in wrong console mode!"); } - std::vector tmd_buffer = pVolume.GetTMD(); - if (!tmd_buffer.empty()) - { - IOS::HLE::ES_DIVerify(tmd_buffer); - } + IOS::HLE::ES_DIVerify(pVolume.GetTMD()); _StartupPara.bWii = pVolume.GetVolumeType() == DiscIO::Platform::WII_DISC; diff --git a/Source/Core/Core/Boot/Boot_BS2Emu.cpp b/Source/Core/Core/Boot/Boot_BS2Emu.cpp index 0dedb770bb..605579f597 100644 --- a/Source/Core/Core/Boot/Boot_BS2Emu.cpp +++ b/Source/Core/Core/Boot/Boot_BS2Emu.cpp @@ -346,11 +346,9 @@ bool CBoot::EmulatedBS2_Wii() PowerPC::ppcState.gpr[1] = 0x816ffff0; // StackPointer - std::vector tmd = DVDInterface::GetVolume().GetTMD(); + IOS::ES::TMDReader tmd = DVDInterface::GetVolume().GetTMD(); - ES::TMDReader tmd_reader{std::move(tmd)}; - - if (!SetupWiiMemory(tmd_reader.GetIOSId())) + if (!SetupWiiMemory(tmd.GetIOSId())) return false; // Execute the apploader diff --git a/Source/Core/Core/Boot/Boot_WiiWAD.cpp b/Source/Core/Core/Boot/Boot_WiiWAD.cpp index 2cef0b6ad6..5b3be7c4dc 100644 --- a/Source/Core/Core/Boot/Boot_WiiWAD.cpp +++ b/Source/Core/Core/Boot/Boot_WiiWAD.cpp @@ -78,7 +78,7 @@ bool CBoot::Boot_WiiWAD(const std::string& _pFilename) if (!ContentLoader.IsValid()) return false; - u64 titleID = ContentLoader.GetTitleID(); + u64 titleID = ContentLoader.GetTMD().GetTitleId(); // create data directory File::CreateFullPath(Common::GetTitleDataPath(titleID, Common::FROM_SESSION_ROOT)); @@ -86,12 +86,11 @@ bool CBoot::Boot_WiiWAD(const std::string& _pFilename) IOS::HLE::CreateVirtualFATFilesystem(); // setup Wii memory - u64 ios_title_id = 0x0000000100000000ULL | ContentLoader.GetIosVersion(); - if (!SetupWiiMemory(ios_title_id)) + if (!SetupWiiMemory(ContentLoader.GetTMD().GetIOSId())) return false; // DOL const DiscIO::SNANDContent* pContent = - ContentLoader.GetContentByIndex(ContentLoader.GetBootIndex()); + ContentLoader.GetContentByIndex(ContentLoader.GetTMD().GetBootIndex()); if (pContent == nullptr) return false; diff --git a/Source/Core/Core/ConfigManager.cpp b/Source/Core/Core/ConfigManager.cpp index 8b64441f7f..bc246bfc20 100644 --- a/Source/Core/Core/ConfigManager.cpp +++ b/Source/Core/Core/ConfigManager.cpp @@ -922,7 +922,7 @@ bool SConfig::AutoSetup(EBootBS2 _BootBS2) const DiscIO::CNANDContentLoader& ContentLoader = DiscIO::CNANDContentManager::Access().GetNANDLoader(m_strFilename); - if (ContentLoader.GetContentByIndex(ContentLoader.GetBootIndex()) == nullptr) + if (ContentLoader.GetContentByIndex(ContentLoader.GetTMD().GetBootIndex()) == nullptr) { // WAD is valid yet cannot be booted. Install instead. u64 installed = DiscIO::CNANDContentManager::Access().Install_WiiWAD(m_strFilename); @@ -931,7 +931,7 @@ bool SConfig::AutoSetup(EBootBS2 _BootBS2) return false; // do not boot } - SetRegion(ContentLoader.GetRegion(), &set_region_dir); + SetRegion(ContentLoader.GetTMD().GetRegion(), &set_region_dir); bWii = true; m_BootType = BOOT_WII_NAND; diff --git a/Source/Core/Core/IOS/DI/DI.cpp b/Source/Core/Core/IOS/DI/DI.cpp index f7d3006fff..bd91eb11d5 100644 --- a/Source/Core/Core/IOS/DI/DI.cpp +++ b/Source/Core/Core/IOS/DI/DI.cpp @@ -14,6 +14,7 @@ #include "Core/HW/DVDInterface.h" #include "Core/HW/Memmap.h" #include "Core/IOS/DI/DI.h" +#include "Core/IOS/ES/Formats.h" #include "Core/IOS/IPC.h" #include "DiscIO/Volume.h" @@ -105,9 +106,10 @@ IPCCommandResult DI::IOCtlV(const IOCtlVRequest& request) INFO_LOG(IOS_DI, "DVDLowOpenPartition: partition_offset 0x%016" PRIx64, partition_offset); // Read TMD to the buffer - std::vector tmd_buffer = DVDInterface::GetVolume().GetTMD(); - Memory::CopyToEmu(request.io_vectors[0].address, tmd_buffer.data(), tmd_buffer.size()); - ES_DIVerify(tmd_buffer); + const ES::TMDReader tmd = DVDInterface::GetVolume().GetTMD(); + const std::vector raw_tmd = tmd.GetRawTMD(); + Memory::CopyToEmu(request.io_vectors[0].address, raw_tmd.data(), raw_tmd.size()); + ES_DIVerify(tmd); return_value = 1; break; diff --git a/Source/Core/Core/IOS/ES/ES.cpp b/Source/Core/Core/IOS/ES/ES.cpp index 5e6a06d278..a496837c63 100644 --- a/Source/Core/Core/IOS/ES/ES.cpp +++ b/Source/Core/Core/IOS/ES/ES.cpp @@ -86,7 +86,7 @@ void ES::OpenInternal() // check for cd ... if (contentLoader.IsValid()) { - m_TitleID = contentLoader.GetTitleID(); + m_TitleID = contentLoader.GetTMD().GetTitleId(); m_TitleIDs.clear(); DiscIO::cUIDsys uid_sys{Common::FromWhichRoot::FROM_SESSION_ROOT}; @@ -201,8 +201,8 @@ u32 ES::OpenTitleContent(u32 CFD, u64 TitleID, u16 Index) SContentAccess Access; Access.m_Position = 0; - Access.m_Index = pContent->m_Index; - Access.m_Size = pContent->m_Size; + Access.m_Index = pContent->m_metadata.index; + Access.m_Size = static_cast(pContent->m_metadata.size); Access.m_TitleID = TitleID; pContent->m_Data->Open(); @@ -319,7 +319,7 @@ IPCCommandResult ES::AddTicket(const IOCtlVRequest& request) std::vector ticket(request.in_vectors[0].size); Memory::CopyFromEmu(ticket.data(), request.in_vectors[0].address, request.in_vectors[0].size); - DiscIO::AddTicket(::ES::TicketReader{std::move(ticket)}); + DiscIO::AddTicket(IOS::ES::TicketReader{std::move(ticket)}); return GetDefaultReply(IPC_SUCCESS); } @@ -413,7 +413,7 @@ IPCCommandResult ES::AddContentFinish(const IOCtlVRequest& request) INFO_LOG(IOS_ES, "IOCTL_ES_ADDCONTENTFINISH: content fd %08x", content_fd); // Try to find the title key from a pre-installed ticket. - ::ES::TicketReader ticket = DiscIO::FindSignedTicket(m_addtitle_tmd.GetTitleId()); + IOS::ES::TicketReader ticket = DiscIO::FindSignedTicket(m_addtitle_tmd.GetTitleId()); if (!ticket.IsValid()) { return GetDefaultReply(ES_NO_TICKET_INSTALLED); @@ -424,7 +424,7 @@ IPCCommandResult ES::AddContentFinish(const IOCtlVRequest& request) // The IV for title content decryption is the lower two bytes of the // content index, zero extended. - ::ES::TMDReader::Content content_info; + IOS::ES::Content content_info; if (!m_addtitle_tmd.FindContentById(m_addtitle_content_id, &content_info)) { return GetDefaultReply(ES_INVALID_TMD); @@ -471,28 +471,21 @@ IPCCommandResult ES::GetTitleContentsCount(const IOCtlVRequest& request) u64 TitleID = Memory::Read_U64(request.in_vectors[0].address); - const DiscIO::CNANDContentLoader& rNANDContent = AccessContentDevice(TitleID); - u16 NumberOfPrivateContent = 0; - s32 return_value = IPC_SUCCESS; - if (rNANDContent.IsValid()) // Not sure if dolphin will ever fail this check - { - NumberOfPrivateContent = rNANDContent.GetNumEntries(); + const DiscIO::CNANDContentLoader& nand_content = AccessContentDevice(TitleID); + if (!nand_content.IsValid()) + return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT); - if ((u32)(TitleID >> 32) == 0x00010000) - Memory::Write_U32(0, request.io_vectors[0].address); - else - Memory::Write_U32(NumberOfPrivateContent, request.io_vectors[0].address); - } + const u16 num_contents = nand_content.GetTMD().GetNumContents(); + + if ((u32)(TitleID >> 32) == 0x00010000) + Memory::Write_U32(0, request.io_vectors[0].address); else - { - return_value = static_cast(rNANDContent.GetContentSize()); - } + Memory::Write_U32(num_contents, request.io_vectors[0].address); INFO_LOG(IOS_ES, "IOCTL_ES_GETTITLECONTENTSCNT: TitleID: %08x/%08x content count %i", - (u32)(TitleID >> 32), (u32)TitleID, - rNANDContent.IsValid() ? NumberOfPrivateContent : (u32)rNANDContent.GetContentSize()); + (u32)(TitleID >> 32), (u32)TitleID, num_contents); - return GetDefaultReply(return_value); + return GetDefaultReply(IPC_SUCCESS); } IPCCommandResult ES::GetTitleContents(const IOCtlVRequest& request) @@ -505,25 +498,17 @@ IPCCommandResult ES::GetTitleContents(const IOCtlVRequest& request) u64 TitleID = Memory::Read_U64(request.in_vectors[0].address); const DiscIO::CNANDContentLoader& rNANDContent = AccessContentDevice(TitleID); - s32 return_value = IPC_SUCCESS; - if (rNANDContent.IsValid()) // Not sure if dolphin will ever fail this check + if (!rNANDContent.IsValid()) + return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT); + + for (const auto& content : rNANDContent.GetContent()) { - for (u16 i = 0; i < rNANDContent.GetNumEntries(); i++) - { - Memory::Write_U32(rNANDContent.GetContentByIndex(i)->m_ContentID, - request.io_vectors[0].address + i * 4); - INFO_LOG(IOS_ES, "IOCTL_ES_GETTITLECONTENTS: Index %d: %08x", i, - rNANDContent.GetContentByIndex(i)->m_ContentID); - } - } - else - { - return_value = static_cast(rNANDContent.GetContentSize()); - ERROR_LOG(IOS_ES, "IOCTL_ES_GETTITLECONTENTS: Unable to open content %zu", - rNANDContent.GetContentSize()); + const u16 index = content.m_metadata.index; + Memory::Write_U32(content.m_metadata.id, request.io_vectors[0].address + index * 4); + INFO_LOG(IOS_ES, "IOCTL_ES_GETTITLECONTENTS: Index %d: %08x", index, content.m_metadata.id); } - return GetDefaultReply(return_value); + return GetDefaultReply(IPC_SUCCESS); } IPCCommandResult ES::OpenTitleContent(const IOCtlVRequest& request) @@ -798,7 +783,7 @@ IPCCommandResult ES::GetViews(const IOCtlVRequest& request) for (u32 view = 0; view < number_of_views; ++view) { const std::vector ticket_view = Loader.GetTicket().GetRawTicketView(view); - Memory::CopyToEmu(request.io_vectors[0].address + view * sizeof(::ES::TicketView), + Memory::CopyToEmu(request.io_vectors[0].address + view * sizeof(IOS::ES::TicketView), ticket_view.data(), ticket_view.size()); } } @@ -809,6 +794,7 @@ IPCCommandResult ES::GetViews(const IOCtlVRequest& request) return GetDefaultReply(IPC_SUCCESS); } +// TODO: rename this to GetTMDViewSize. There is only one TMD, so the name doesn't make sense. IPCCommandResult ES::GetTMDViewCount(const IOCtlVRequest& request) { _dbg_assert_msg_(IOS_ES, request.in_vectors.size() == 1, "IOCTL_ES_GETTMDVIEWCNT no in buffer"); @@ -818,18 +804,13 @@ IPCCommandResult ES::GetTMDViewCount(const IOCtlVRequest& request) const DiscIO::CNANDContentLoader& Loader = AccessContentDevice(TitleID); - u32 TMDViewCnt = 0; + u32 view_size = 0; if (Loader.IsValid()) - { - TMDViewCnt += DiscIO::CNANDContentLoader::TMD_VIEW_SIZE; - TMDViewCnt += 2; // title version - TMDViewCnt += 2; // num entries - TMDViewCnt += (u32)Loader.GetContentSize() * (4 + 2 + 2 + 8); // content id, index, type, size - } - Memory::Write_U32(TMDViewCnt, request.io_vectors[0].address); + view_size = static_cast(Loader.GetTMD().GetRawView().size()); + Memory::Write_U32(view_size, request.io_vectors[0].address); INFO_LOG(IOS_ES, "IOCTL_ES_GETTMDVIEWCNT: title: %08x/%08x (view size %i)", (u32)(TitleID >> 32), - (u32)TitleID, TMDViewCnt); + (u32)TitleID, view_size); return GetDefaultReply(IPC_SUCCESS); } @@ -848,30 +829,11 @@ IPCCommandResult ES::GetTMDViews(const IOCtlVRequest& request) if (Loader.IsValid()) { - u32 Address = request.io_vectors[0].address; + const std::vector raw_view = Loader.GetTMD().GetRawView(); + if (raw_view.size() != request.io_vectors[0].size) + return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT); - Memory::CopyToEmu(Address, Loader.GetTMDView(), DiscIO::CNANDContentLoader::TMD_VIEW_SIZE); - Address += DiscIO::CNANDContentLoader::TMD_VIEW_SIZE; - - Memory::Write_U16(Loader.GetTitleVersion(), Address); - Address += 2; - Memory::Write_U16(Loader.GetNumEntries(), Address); - Address += 2; - - const std::vector& rContent = Loader.GetContent(); - for (size_t i = 0; i < Loader.GetContentSize(); i++) - { - Memory::Write_U32(rContent[i].m_ContentID, Address); - Address += 4; - Memory::Write_U16(rContent[i].m_Index, Address); - Address += 2; - Memory::Write_U16(rContent[i].m_Type, Address); - Address += 2; - Memory::Write_U64(rContent[i].m_Size, Address); - Address += 8; - } - - _dbg_assert_(IOS_ES, (Address - request.io_vectors[0].address) == request.io_vectors[0].size); + Memory::CopyToEmu(request.io_vectors[0].address, raw_view.data(), raw_view.size()); } INFO_LOG(IOS_ES, "IOCTL_ES_GETTMDVIEWS: title: %08x/%08x (buffer size: %i)", (u32)(TitleID >> 32), @@ -923,18 +885,15 @@ IPCCommandResult ES::GetStoredTMDSize(const IOCtlVRequest& request) const DiscIO::CNANDContentLoader& Loader = AccessContentDevice(TitleID); _dbg_assert_(IOS_ES, Loader.IsValid()); - u32 TMDCnt = 0; + u32 tmd_size = 0; if (Loader.IsValid()) - { - TMDCnt += DiscIO::CNANDContentLoader::TMD_HEADER_SIZE; - TMDCnt += (u32)Loader.GetContentSize() * DiscIO::CNANDContentLoader::CONTENT_HEADER_SIZE; - } + tmd_size = static_cast(Loader.GetTMD().GetRawTMD().size()); if (request.io_vectors.size()) - Memory::Write_U32(TMDCnt, request.io_vectors[0].address); + Memory::Write_U32(tmd_size, request.io_vectors[0].address); INFO_LOG(IOS_ES, "IOCTL_ES_GETSTOREDTMDSIZE: title: %08x/%08x (view size %i)", - (u32)(TitleID >> 32), (u32)TitleID, TMDCnt); + (u32)(TitleID >> 32), (u32)TitleID, tmd_size); return GetDefaultReply(IPC_SUCCESS); } @@ -962,20 +921,11 @@ IPCCommandResult ES::GetStoredTMD(const IOCtlVRequest& request) if (Loader.IsValid() && request.io_vectors.size()) { - u32 Address = request.io_vectors[0].address; + const std::vector raw_tmd = Loader.GetTMD().GetRawTMD(); + if (raw_tmd.size() != request.io_vectors[0].size) + return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT); - Memory::CopyToEmu(Address, Loader.GetTMDHeader(), DiscIO::CNANDContentLoader::TMD_HEADER_SIZE); - Address += DiscIO::CNANDContentLoader::TMD_HEADER_SIZE; - - const std::vector& rContent = Loader.GetContent(); - for (size_t i = 0; i < Loader.GetContentSize(); i++) - { - Memory::CopyToEmu(Address, rContent[i].m_Header, - DiscIO::CNANDContentLoader::CONTENT_HEADER_SIZE); - Address += DiscIO::CNANDContentLoader::CONTENT_HEADER_SIZE; - } - - _dbg_assert_(IOS_ES, (Address - request.io_vectors[0].address) == request.io_vectors[0].size); + Memory::CopyToEmu(request.io_vectors[0].address, raw_tmd.data(), raw_tmd.size()); } INFO_LOG(IOS_ES, "IOCTL_ES_GETSTOREDTMD: title: %08x/%08x (buffer size: %i)", @@ -1050,9 +1000,9 @@ IPCCommandResult ES::Launch(const IOCtlVRequest& request) const DiscIO::CNANDContentLoader& ContentLoader = AccessContentDevice(TitleID); if (ContentLoader.IsValid()) { - ios_to_load = 0x0000000100000000ULL | ContentLoader.GetIosVersion(); + ios_to_load = ContentLoader.GetTMD().GetIOSId(); - u32 bootInd = ContentLoader.GetBootIndex(); + u32 bootInd = ContentLoader.GetTMD().GetBootIndex(); const DiscIO::SNANDContent* pContent = ContentLoader.GetContentByIndex(bootInd); if (pContent) { @@ -1194,10 +1144,13 @@ const DiscIO::CNANDContentLoader& ES::AccessContentDevice(u64 title_id) return DiscIO::CNANDContentManager::Access().GetNANDLoader(title_id, Common::FROM_SESSION_ROOT); } -u32 ES::ES_DIVerify(const std::vector& tmd) +u32 ES::ES_DIVerify(const IOS::ES::TMDReader& tmd) { + if (!tmd.IsValid()) + return -1; + u64 title_id = 0xDEADBEEFDEADBEEFull; - u64 tmd_title_id = Common::swap64(&tmd[0x18C]); + u64 tmd_title_id = tmd.GetTitleId(); DVDInterface::GetVolume().GetTitleID(&title_id); if (title_id != tmd_title_id) @@ -1211,7 +1164,8 @@ u32 ES::ES_DIVerify(const std::vector& tmd) if (!File::Exists(tmd_path)) { File::IOFile tmd_file(tmd_path, "wb"); - if (!tmd_file.WriteBytes(tmd.data(), tmd.size())) + const std::vector& tmd_bytes = tmd.GetRawTMD(); + if (!tmd_file.WriteBytes(tmd_bytes.data(), tmd_bytes.size())) ERROR_LOG(IOS_ES, "DIVerify failed to write disc TMD to NAND."); } DiscIO::cUIDsys uid_sys{Common::FromWhichRoot::FROM_SESSION_ROOT}; diff --git a/Source/Core/Core/IOS/ES/ES.h b/Source/Core/Core/IOS/ES/ES.h index aa934100f2..045fc75f36 100644 --- a/Source/Core/Core/IOS/ES/ES.h +++ b/Source/Core/Core/IOS/ES/ES.h @@ -46,7 +46,7 @@ public: void Close() override; IPCCommandResult IOCtlV(const IOCtlVRequest& request) override; - static u32 ES_DIVerify(const std::vector& tmd); + static u32 ES_DIVerify(const IOS::ES::TMDReader& tmd); // This should only be cleared on power reset static std::string m_ContentFile; @@ -203,7 +203,7 @@ private: u32 m_AccessIdentID = 0; // For title installation (ioctls IOCTL_ES_ADDTITLE*). - ::ES::TMDReader m_addtitle_tmd; + IOS::ES::TMDReader m_addtitle_tmd; u32 m_addtitle_content_id = 0xFFFFFFFF; std::vector m_addtitle_content_buffer; }; diff --git a/Source/Core/Core/IOS/ES/Formats.cpp b/Source/Core/Core/IOS/ES/Formats.cpp index 1eaf375070..1e66ba4f51 100644 --- a/Source/Core/Core/IOS/ES/Formats.cpp +++ b/Source/Core/Core/IOS/ES/Formats.cpp @@ -16,8 +16,12 @@ #include "Common/CommonFuncs.h" #include "Common/CommonTypes.h" +namespace IOS +{ namespace ES { +constexpr size_t CONTENT_VIEW_SIZE = 0x10; + std::vector AESDecode(const u8* key, u8* iv, const u8* src, u32 size) { mbedtls_aes_context aes_ctx; @@ -49,13 +53,13 @@ void TMDReader::SetBytes(std::vector&& bytes) bool TMDReader::IsValid() const { - if (m_bytes.size() < 0x1E4) + if (m_bytes.size() < sizeof(TMDHeader)) { // TMD is too small to contain its base fields. return false; } - if (m_bytes.size() < 0x1E4 + GetNumContents() * 36u) + if (m_bytes.size() < sizeof(TMDHeader) + GetNumContents() * sizeof(Content)) { // TMD is too small to contain all its expected content entries. return false; @@ -64,19 +68,69 @@ bool TMDReader::IsValid() const return true; } +const std::vector& TMDReader::GetRawTMD() const +{ + return m_bytes; +} + +std::vector TMDReader::GetRawHeader() const +{ + return std::vector(m_bytes.begin(), m_bytes.begin() + sizeof(TMDHeader)); +} + +std::vector TMDReader::GetRawView() const +{ + // Base fields + std::vector view(m_bytes.cbegin() + offsetof(TMDHeader, tmd_version), + m_bytes.cbegin() + offsetof(TMDHeader, access_rights)); + + const auto version = m_bytes.cbegin() + offsetof(TMDHeader, title_version); + view.insert(view.end(), version, version + sizeof(TMDHeader::title_version)); + + const auto num_contents = m_bytes.cbegin() + offsetof(TMDHeader, num_contents); + view.insert(view.end(), num_contents, num_contents + sizeof(TMDHeader::num_contents)); + + // Content views (same as Content, but without the hash) + for (size_t i = 0; i < GetNumContents(); ++i) + { + const auto content_iterator = m_bytes.cbegin() + sizeof(TMDHeader) + i * sizeof(Content); + view.insert(view.end(), content_iterator, content_iterator + CONTENT_VIEW_SIZE); + } + + return view; +} + +u16 TMDReader::GetBootIndex() const +{ + return Common::swap16(m_bytes.data() + offsetof(TMDHeader, boot_index)); +} + u64 TMDReader::GetIOSId() const { - return Common::swap64(m_bytes.data() + 0x184); + return Common::swap64(m_bytes.data() + offsetof(TMDHeader, ios_id)); +} + +DiscIO::Region TMDReader::GetRegion() const +{ + if (GetTitleId() == 0x0000000100000002) + return DiscIO::RegionSwitchWii(DiscIO::GetSysMenuRegion(GetTitleVersion())); + + return DiscIO::RegionSwitchWii(static_cast(GetTitleId() & 0xff)); } u64 TMDReader::GetTitleId() const { - return Common::swap64(m_bytes.data() + 0x18C); + return Common::swap64(m_bytes.data() + offsetof(TMDHeader, title_id)); +} + +u16 TMDReader::GetTitleVersion() const +{ + return Common::swap16(m_bytes.data() + offsetof(TMDHeader, title_version)); } u16 TMDReader::GetNumContents() const { - return Common::swap16(m_bytes.data() + 0x1DE); + return Common::swap16(m_bytes.data() + offsetof(TMDHeader, num_contents)); } bool TMDReader::GetContent(u16 index, Content* content) const @@ -86,16 +140,24 @@ bool TMDReader::GetContent(u16 index, Content* content) const return false; } - const u8* content_base = m_bytes.data() + 0x1E4 + index * 36; - content->id = Common::swap32(content_base); - content->index = Common::swap16(content_base + 4); - content->type = Common::swap16(content_base + 6); - content->size = Common::swap64(content_base + 8); - std::copy(content_base + 16, content_base + 36, content->sha1.begin()); + const u8* content_base = m_bytes.data() + sizeof(TMDHeader) + index * sizeof(Content); + content->id = Common::swap32(content_base + offsetof(Content, id)); + content->index = Common::swap16(content_base + offsetof(Content, index)); + content->type = Common::swap16(content_base + offsetof(Content, type)); + content->size = Common::swap64(content_base + offsetof(Content, size)); + std::copy_n(content_base + offsetof(Content, sha1), content->sha1.size(), content->sha1.begin()); return true; } +std::vector TMDReader::GetContents() const +{ + std::vector contents(GetNumContents()); + for (size_t i = 0; i < contents.size(); ++i) + GetContent(static_cast(i), &contents[i]); + return contents; +} + bool TMDReader::FindContentById(u32 id, Content* content) const { for (u16 index = 0; index < GetNumContents(); ++index) @@ -205,3 +267,4 @@ std::vector TicketReader::GetTitleKey() const return AESDecode(common_key, iv, &m_bytes[GetOffset() + offsetof(Ticket, title_key)], 16); } } // namespace ES +} // namespace IOS diff --git a/Source/Core/Core/IOS/ES/Formats.h b/Source/Core/Core/IOS/ES/Formats.h index f161d1974a..64fd6c3a46 100644 --- a/Source/Core/Core/IOS/ES/Formats.h +++ b/Source/Core/Core/IOS/ES/Formats.h @@ -12,45 +12,50 @@ #include "Common/ChunkFile.h" #include "Common/CommonTypes.h" +#include "DiscIO/Enums.h" +namespace IOS +{ namespace ES { -std::vector AESDecode(const u8* key, u8* iv, const u8* src, u32 size); - -class TMDReader final -{ -public: - TMDReader() = default; - explicit TMDReader(const std::vector& bytes); - explicit TMDReader(std::vector&& bytes); - - void SetBytes(const std::vector& bytes); - void SetBytes(std::vector&& bytes); - - bool IsValid() const; - - u64 GetIOSId() const; - u64 GetTitleId() const; - - struct Content - { - u32 id; - u16 index; - u16 type; - u64 size; - std::array sha1; - }; - u16 GetNumContents() const; - bool GetContent(u16 index, Content* content) const; - bool FindContentById(u32 id, Content* content) const; - - void DoState(PointerWrap& p); - -private: - std::vector m_bytes; -}; - #pragma pack(push, 4) +struct TMDHeader +{ + u32 signature_type; + u8 rsa_2048_signature[256]; + u8 fill[60]; + u8 issuer[64]; + u8 tmd_version; + u8 ca_crl_version; + u8 signer_crl_version; + u64 ios_id; + u64 title_id; + u32 title_type; + u16 group_id; + u16 zero; + u16 region; + u8 ratings[16]; + u8 reserved[12]; + u8 ipc_mask[12]; + u8 reserved2[18]; + u32 access_rights; + u16 title_version; + u16 num_contents; + u16 boot_index; + u16 fill2; +}; +static_assert(sizeof(TMDHeader) == 0x1e4, "TMDHeader has the wrong size"); + +struct Content +{ + u32 id; + u16 index; + u16 type; + u64 size; + std::array sha1; +}; +static_assert(sizeof(Content) == 36, "Content has the wrong size"); + struct TimeLimit { u32 enabled; @@ -96,6 +101,42 @@ struct Ticket static_assert(sizeof(Ticket) == 356, "Ticket has the wrong size"); #pragma pack(pop) +std::vector AESDecode(const u8* key, u8* iv, const u8* src, u32 size); + +class TMDReader final +{ +public: + TMDReader() = default; + explicit TMDReader(const std::vector& bytes); + explicit TMDReader(std::vector&& bytes); + + void SetBytes(const std::vector& bytes); + void SetBytes(std::vector&& bytes); + + bool IsValid() const; + + // Returns the TMD or parts of it without any kind of parsing. Intended for use by ES. + const std::vector& GetRawTMD() const; + std::vector GetRawHeader() const; + std::vector GetRawView() const; + + u16 GetBootIndex() const; + u64 GetIOSId() const; + DiscIO::Region GetRegion() const; + u64 GetTitleId() const; + u16 GetTitleVersion() const; + + u16 GetNumContents() const; + bool GetContent(u16 index, Content* content) const; + std::vector GetContents() const; + bool FindContentById(u32 id, Content* content) const; + + void DoState(PointerWrap& p); + +private: + std::vector m_bytes; +}; + class TicketReader final { public: @@ -125,3 +166,4 @@ private: std::vector m_bytes; }; } // namespace ES +} // namespace IOS diff --git a/Source/Core/Core/IOS/IPC.cpp b/Source/Core/Core/IOS/IPC.cpp index 5a334f6c82..b74ed1c996 100644 --- a/Source/Core/Core/IOS/IPC.cpp +++ b/Source/Core/Core/IOS/IPC.cpp @@ -658,7 +658,7 @@ void SetDefaultContentFile(const std::string& file_name) es->LoadWAD(file_name); } -void ES_DIVerify(const std::vector& tmd) +void ES_DIVerify(const ES::TMDReader& tmd) { Device::ES::ES_DIVerify(tmd); } diff --git a/Source/Core/Core/IOS/IPC.h b/Source/Core/Core/IOS/IPC.h index 09b3c1179c..5bbd10befd 100644 --- a/Source/Core/Core/IOS/IPC.h +++ b/Source/Core/Core/IOS/IPC.h @@ -16,6 +16,11 @@ class PointerWrap; namespace IOS { +namespace ES +{ +class TMDReader; +} + namespace HLE { namespace Device @@ -61,7 +66,7 @@ void DoState(PointerWrap& p); // Set default content file void SetDefaultContentFile(const std::string& file_name); -void ES_DIVerify(const std::vector& tmd); +void ES_DIVerify(const ES::TMDReader& tmd); void SDIO_EventNotify(); diff --git a/Source/Core/Core/IOS/MIOS.cpp b/Source/Core/Core/IOS/MIOS.cpp index 6fa93f6d68..2a9e55402f 100644 --- a/Source/Core/Core/IOS/MIOS.cpp +++ b/Source/Core/Core/IOS/MIOS.cpp @@ -23,6 +23,7 @@ #include "Core/HW/DVDThread.h" #include "Core/HW/Memmap.h" #include "Core/HW/SystemTimers.h" +#include "Core/IOS/ES/Formats.h" #include "Core/IOS/MIOS.h" #include "Core/PowerPC/PPCSymbolDB.h" #include "Core/PowerPC/PowerPC.h" @@ -97,7 +98,7 @@ static std::vector GetMIOSBinary() if (!loader.IsValid()) return {}; - const auto* content = loader.GetContentByIndex(loader.GetBootIndex()); + const auto* content = loader.GetContentByIndex(loader.GetTMD().GetBootIndex()); if (!content) return {}; diff --git a/Source/Core/Core/IOS/WFS/WFSI.cpp b/Source/Core/Core/IOS/WFS/WFSI.cpp index 4c885fee72..16e97055d6 100644 --- a/Source/Core/Core/IOS/WFS/WFSI.cpp +++ b/Source/Core/Core/IOS/WFS/WFSI.cpp @@ -134,7 +134,7 @@ IPCCommandResult WFSI::IOCtl(const IOCtlRequest& request) // Initializes the IV from the index of the content in the TMD contents. u32 content_id = Memory::Read_U32(request.buffer_in + 8); - ES::TMDReader::Content content_info; + ES::Content content_info; if (!m_tmd.FindContentById(content_id, &content_info)) { WARN_LOG(IOS, "%s: Content id %08x not found", ioctl_name, content_id); diff --git a/Source/Core/Core/IOS/WFS/WFSI.h b/Source/Core/Core/IOS/WFS/WFSI.h index 6388121680..82dfded277 100644 --- a/Source/Core/Core/IOS/WFS/WFSI.h +++ b/Source/Core/Core/IOS/WFS/WFSI.h @@ -50,7 +50,7 @@ private: u8 m_aes_key[0x10] = {}; u8 m_aes_iv[0x10] = {}; - ::ES::TMDReader m_tmd; + IOS::ES::TMDReader m_tmd; std::string m_base_extract_path; ARCUnpacker m_arc_unpacker; diff --git a/Source/Core/DiscIO/NANDContentLoader.cpp b/Source/Core/DiscIO/NANDContentLoader.cpp index dcd875388a..0344fe1c46 100644 --- a/Source/Core/DiscIO/NANDContentLoader.cpp +++ b/Source/Core/DiscIO/NANDContentLoader.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -146,7 +147,6 @@ bool CNANDContentDataBuffer::GetRange(u32 start, u32 size, u8* buffer) } CNANDContentLoader::CNANDContentLoader(const std::string& content_name) - : m_Valid(false), m_IsWAD(false), m_TitleID(-1), m_IosVersion(0x09), m_BootIndex(-1) { m_Valid = Initialize(content_name); } @@ -159,7 +159,7 @@ const SNANDContent* CNANDContentLoader::GetContentByIndex(int index) const { for (auto& Content : m_Content) { - if (Content.m_Index == index) + if (Content.m_metadata.index == index) { return &Content; } @@ -176,13 +176,12 @@ bool CNANDContentLoader::Initialize(const std::string& name) WiiWAD wad(name); std::vector data_app; - std::vector tmd; if (wad.IsValid()) { m_IsWAD = true; m_ticket = wad.GetTicket(); - tmd = wad.GetTMD(); + m_tmd = wad.GetTMD(); data_app = wad.GetDataApp(); } else @@ -201,94 +200,64 @@ bool CNANDContentLoader::Initialize(const std::string& name) return false; } - tmd.resize(static_cast(File::GetSize(tmd_filename))); - tmd_file.ReadBytes(tmd.data(), tmd.size()); + std::vector bytes(File::GetSize(tmd_filename)); + tmd_file.ReadBytes(bytes.data(), bytes.size()); + m_tmd.SetBytes(std::move(bytes)); + + m_ticket = FindSignedTicket(m_tmd.GetTitleId()); } - std::copy(&tmd[0], &tmd[TMD_HEADER_SIZE], m_TMDHeader); - std::copy(&tmd[0x180], &tmd[0x180 + TMD_VIEW_SIZE], m_TMDView); - - m_TitleVersion = Common::swap16(&tmd[0x01DC]); - m_NumEntries = Common::swap16(&tmd[0x01DE]); - m_BootIndex = Common::swap16(&tmd[0x01E0]); - m_TitleID = Common::swap64(&tmd[0x018C]); - m_IosVersion = Common::swap16(&tmd[0x018A]); - m_Country = static_cast(m_TitleID & 0xFF); - - if (m_Country == 2) // SYSMENU - m_Country = GetSysMenuRegion(m_TitleVersion); - - if (!m_IsWAD) - m_ticket = FindSignedTicket(m_TitleID); - - InitializeContentEntries(tmd, data_app); + InitializeContentEntries(data_app); return true; } -void CNANDContentLoader::InitializeContentEntries(const std::vector& tmd, - const std::vector& data_app) +void CNANDContentLoader::InitializeContentEntries(const std::vector& data_app) { - m_Content.resize(m_NumEntries); + if (!m_ticket.IsValid()) + { + ERROR_LOG(IOS_ES, "No valid ticket for title %016" PRIx64, m_tmd.GetTitleId()); + return; + } + + const std::vector contents = m_tmd.GetContents(); + m_Content.resize(contents.size()); - std::array iv; u32 data_app_offset = 0; - + const std::vector title_key = m_ticket.GetTitleKey(); CSharedContent shared_content{Common::FromWhichRoot::FROM_SESSION_ROOT}; - for (u32 i = 0; i < m_NumEntries; i++) + for (size_t i = 0; i < contents.size(); ++i) { - const u32 entry_offset = 0x24 * i; - - SNANDContent& content = m_Content[i]; - content.m_ContentID = Common::swap32(&tmd[entry_offset + 0x01E4]); - content.m_Index = Common::swap16(&tmd[entry_offset + 0x01E8]); - content.m_Type = Common::swap16(&tmd[entry_offset + 0x01EA]); - content.m_Size = static_cast(Common::swap64(&tmd[entry_offset + 0x01EC])); - - const auto header_begin = std::next(tmd.begin(), entry_offset + 0x01E4); - const auto header_end = std::next(header_begin, ArraySize(content.m_Header)); - std::copy(header_begin, header_end, content.m_Header); - - const auto hash_begin = std::next(tmd.begin(), entry_offset + 0x01F4); - const auto hash_end = std::next(hash_begin, ArraySize(content.m_SHA1Hash)); - std::copy(hash_begin, hash_end, content.m_SHA1Hash); + const auto& content = contents.at(i); if (m_IsWAD) { - u32 rounded_size = Common::AlignUp(content.m_Size, 0x40); + // The content index is used as IV (2 bytes); the remaining 14 bytes are zeroes. + std::array iv{}; + iv[0] = static_cast(content.index >> 8) & 0xFF; + iv[1] = static_cast(content.index) & 0xFF; - iv.fill(0); - std::copy(&tmd[entry_offset + 0x01E8], &tmd[entry_offset + 0x01E8 + 2], iv.begin()); - - content.m_Data = std::make_unique(ES::AESDecode( - m_ticket.GetTitleKey().data(), iv.data(), &data_app[data_app_offset], rounded_size)); + u32 rounded_size = Common::AlignUp(static_cast(content.size), 0x40); + m_Content[i].m_Data = std::make_unique(IOS::ES::AESDecode( + title_key.data(), iv.data(), &data_app[data_app_offset], rounded_size)); data_app_offset += rounded_size; - continue; + } + else + { + std::string filename; + if (content.type & 0x8000) // shared app + filename = shared_content.GetFilenameFromSHA1(content.sha1.data()); + else + filename = StringFromFormat("%s/%08x.app", m_Path.c_str(), content.id); + + m_Content[i].m_Data = std::make_unique(filename); } - std::string filename; - if (content.m_Type & 0x8000) // shared app - filename = shared_content.GetFilenameFromSHA1(content.m_SHA1Hash); - else - filename = StringFromFormat("%s/%08x.app", m_Path.c_str(), content.m_ContentID); - - content.m_Data = std::make_unique(filename); - - // Be graceful about incorrect TMDs. - if (File::Exists(filename)) - content.m_Size = static_cast(File::GetSize(filename)); + m_Content[i].m_metadata = std::move(content); } } -DiscIO::Region CNANDContentLoader::GetRegion() const -{ - if (!IsValid()) - return DiscIO::Region::UNKNOWN_REGION; - - return RegionSwitchWii(m_Country); -} - CNANDContentManager::~CNANDContentManager() { } @@ -327,18 +296,18 @@ void CNANDContentManager::ClearCache() void CNANDContentLoader::RemoveTitle() const { - INFO_LOG(DISCIO, "RemoveTitle %08x/%08x", (u32)(m_TitleID >> 32), (u32)m_TitleID); + const u64 title_id = m_tmd.GetTitleId(); + INFO_LOG(DISCIO, "RemoveTitle %08x/%08x", (u32)(title_id >> 32), (u32)title_id); if (IsValid()) { // remove TMD? - for (u32 i = 0; i < m_NumEntries; i++) + for (const auto& content : m_Content) { - if (!(m_Content[i].m_Type & 0x8000)) // skip shared apps + if (!(content.m_metadata.type & 0x8000)) // skip shared apps { - std::string filename = - StringFromFormat("%s/%08x.app", m_Path.c_str(), m_Content[i].m_ContentID); - INFO_LOG(DISCIO, "Delete %s", filename.c_str()); - File::Delete(filename); + std::string path = StringFromFormat("%s/%08x.app", m_Path.c_str(), content.m_metadata.id); + INFO_LOG(DISCIO, "Delete %s", path.c_str()); + File::Delete(path); } } CNANDContentManager::Access().ClearCache(); // deletes 'this' @@ -425,7 +394,7 @@ u64 CNANDContentManager::Install_WiiWAD(const std::string& filename) if (content_loader.IsValid() == false) return 0; - u64 title_id = content_loader.GetTitleID(); + const u64 title_id = content_loader.GetTMD().GetTitleId(); // copy WAD's TMD header and contents to content directory @@ -440,20 +409,17 @@ u64 CNANDContentManager::Install_WiiWAD(const std::string& filename) return 0; } - tmd_file.WriteBytes(content_loader.GetTMDHeader(), CNANDContentLoader::TMD_HEADER_SIZE); + const auto& raw_tmd = content_loader.GetTMD().GetRawTMD(); + tmd_file.WriteBytes(raw_tmd.data(), raw_tmd.size()); CSharedContent shared_content{Common::FromWhichRoot::FROM_CONFIGURED_ROOT}; - for (u32 i = 0; i < content_loader.GetContentSize(); i++) + for (const auto& content : content_loader.GetContent()) { - const SNANDContent& content = content_loader.GetContent()[i]; - - tmd_file.WriteBytes(content.m_Header, CNANDContentLoader::CONTENT_HEADER_SIZE); - std::string app_filename; - if (content.m_Type & 0x8000) // shared - app_filename = shared_content.AddSharedContent(content.m_SHA1Hash); + if (content.m_metadata.type & 0x8000) // shared + app_filename = shared_content.AddSharedContent(content.m_metadata.sha1.data()); else - app_filename = StringFromFormat("%s%08x.app", content_path.c_str(), content.m_ContentID); + app_filename = StringFromFormat("%s%08x.app", content_path.c_str(), content.m_metadata.id); if (!File::Exists(app_filename)) { @@ -465,7 +431,7 @@ u64 CNANDContentManager::Install_WiiWAD(const std::string& filename) return 0; } - app_file.WriteBytes(content.m_Data->Get().data(), content.m_Size); + app_file.WriteBytes(content.m_Data->Get().data(), content.m_metadata.size); } else { @@ -488,7 +454,7 @@ u64 CNANDContentManager::Install_WiiWAD(const std::string& filename) return title_id; } -bool AddTicket(const ES::TicketReader& signed_ticket) +bool AddTicket(const IOS::ES::TicketReader& signed_ticket) { if (!signed_ticket.IsValid()) { @@ -508,21 +474,21 @@ bool AddTicket(const ES::TicketReader& signed_ticket) return ticket_file.WriteBytes(raw_ticket.data(), raw_ticket.size()); } -ES::TicketReader FindSignedTicket(u64 title_id) +IOS::ES::TicketReader FindSignedTicket(u64 title_id) { std::string ticket_filename = Common::GetTicketFileName(title_id, Common::FROM_CONFIGURED_ROOT); File::IOFile ticket_file(ticket_filename, "rb"); if (!ticket_file) { - return ES::TicketReader{}; + return IOS::ES::TicketReader{}; } std::vector signed_ticket(ticket_file.GetSize()); if (!ticket_file.ReadBytes(signed_ticket.data(), signed_ticket.size())) { - return ES::TicketReader{}; + return IOS::ES::TicketReader{}; } - return ES::TicketReader{std::move(signed_ticket)}; + return IOS::ES::TicketReader{std::move(signed_ticket)}; } } // namespace end diff --git a/Source/Core/DiscIO/NANDContentLoader.h b/Source/Core/DiscIO/NANDContentLoader.h index 654a123b5d..e945e7dcb5 100644 --- a/Source/Core/DiscIO/NANDContentLoader.h +++ b/Source/Core/DiscIO/NANDContentLoader.h @@ -23,8 +23,8 @@ namespace DiscIO { enum class Region; -bool AddTicket(const ES::TicketReader& signed_ticket); -ES::TicketReader FindSignedTicket(u64 title_id); +bool AddTicket(const IOS::ES::TicketReader& signed_ticket); +IOS::ES::TicketReader FindSignedTicket(u64 title_id); class CNANDContentData { @@ -66,13 +66,7 @@ private: struct SNANDContent { - u32 m_ContentID; - u16 m_Index; - u16 m_Type; - u32 m_Size; - u8 m_SHA1Hash[20]; - u8 m_Header[36]; // all of the above - + IOS::ES::Content m_metadata; std::unique_ptr m_Data; }; @@ -85,42 +79,19 @@ public: bool IsValid() const { return m_Valid; } void RemoveTitle() const; - u64 GetTitleID() const { return m_TitleID; } - u16 GetIosVersion() const { return m_IosVersion; } - u32 GetBootIndex() const { return m_BootIndex; } - size_t GetContentSize() const { return m_Content.size(); } const SNANDContent* GetContentByIndex(int index) const; - const u8* GetTMDView() const { return m_TMDView; } - const u8* GetTMDHeader() const { return m_TMDHeader; } - const ES::TicketReader& GetTicket() const { return m_ticket; } + const IOS::ES::TMDReader& GetTMD() const { return m_tmd; } + const IOS::ES::TicketReader& GetTicket() const { return m_ticket; } const std::vector& GetContent() const { return m_Content; } - u16 GetTitleVersion() const { return m_TitleVersion; } - u16 GetNumEntries() const { return m_NumEntries; } - DiscIO::Region GetRegion() const; - u8 GetCountryChar() const { return m_Country; } - enum - { - TMD_VIEW_SIZE = 0x58, - TMD_HEADER_SIZE = 0x1E4, - CONTENT_HEADER_SIZE = 0x24, - }; - private: bool Initialize(const std::string& name); - void InitializeContentEntries(const std::vector& tmd, const std::vector& data_app); + void InitializeContentEntries(const std::vector& data_app); - bool m_Valid; - bool m_IsWAD; + bool m_Valid = false; + bool m_IsWAD = false; std::string m_Path; - u64 m_TitleID; - u16 m_IosVersion; - u32 m_BootIndex; - u16 m_NumEntries; - u16 m_TitleVersion; - u8 m_TMDView[TMD_VIEW_SIZE]; - u8 m_TMDHeader[TMD_HEADER_SIZE]; - ES::TicketReader m_ticket; - u8 m_Country; + IOS::ES::TMDReader m_tmd; + IOS::ES::TicketReader m_ticket; std::vector m_Content; }; diff --git a/Source/Core/DiscIO/Volume.h b/Source/Core/DiscIO/Volume.h index 966bbe2ae1..1190565004 100644 --- a/Source/Core/DiscIO/Volume.h +++ b/Source/Core/DiscIO/Volume.h @@ -12,6 +12,7 @@ #include "Common/CommonFuncs.h" #include "Common/CommonTypes.h" #include "Common/StringUtil.h" +#include "Core/IOS/ES/Formats.h" #include "DiscIO/Enums.h" namespace DiscIO @@ -36,7 +37,7 @@ public: } virtual bool GetTitleID(u64*) const { return false; } - virtual std::vector GetTMD() const { return {}; } + virtual IOS::ES::TMDReader GetTMD() const { return {}; } virtual u64 PartitionOffsetToRawOffset(u64 offset) const { return offset; } virtual std::string GetGameID() const = 0; virtual std::string GetMakerID() const = 0; diff --git a/Source/Core/DiscIO/VolumeWad.cpp b/Source/Core/DiscIO/VolumeWad.cpp index 29f543586d..01080c7b52 100644 --- a/Source/Core/DiscIO/VolumeWad.cpp +++ b/Source/Core/DiscIO/VolumeWad.cpp @@ -36,6 +36,16 @@ CVolumeWAD::CVolumeWAD(std::unique_ptr reader) : m_reader(std::move Common::AlignUp(m_tick_size, 0x40); m_opening_bnr_offset = m_tmd_offset + Common::AlignUp(m_tmd_size, 0x40) + Common::AlignUp(m_data_size, 0x40); + + if (m_tmd_size > 1024 * 1024 * 4) + { + ERROR_LOG(DISCIO, "TMD is too large: %u bytes", m_tmd_size); + return; + } + + std::vector tmd_buffer(m_tmd_size); + Read(m_tmd_offset, m_tmd_size, tmd_buffer.data(), false); + m_tmd.SetBytes(std::move(tmd_buffer)); } CVolumeWAD::~CVolumeWAD() @@ -55,40 +65,26 @@ bool CVolumeWAD::Read(u64 offset, u64 length, u8* buffer, bool decrypt) const Region CVolumeWAD::GetRegion() const { - u8 country_code; - if (!Read(m_tmd_offset + 0x0193, 1, &country_code)) + if (!m_tmd.IsValid()) return Region::UNKNOWN_REGION; - - return RegionSwitchWii(country_code); + return m_tmd.GetRegion(); } Country CVolumeWAD::GetCountry() const { - // read the last digit of the titleID in the ticket - u8 country_code; - if (!Read(m_tmd_offset + 0x0193, 1, &country_code)) + if (!m_tmd.IsValid()) return Country::COUNTRY_UNKNOWN; + u8 country_code = static_cast(m_tmd.GetTitleId() & 0xff); if (country_code == 2) // SYSMENU - { - u16 title_version = 0; - Read(m_tmd_offset + 0x01dc, 2, (u8*)&title_version); - country_code = GetSysMenuRegion(Common::swap16(title_version)); - } + country_code = GetSysMenuRegion(m_tmd.GetTitleVersion()); return CountrySwitch(country_code); } -std::vector CVolumeWAD::GetTMD() const +IOS::ES::TMDReader CVolumeWAD::GetTMD() const { - if (m_tmd_size > 1024 * 1024 * 4) - { - ERROR_LOG(DISCIO, "TMD is too large: %u bytes", m_tmd_size); - return {}; - } - std::vector buffer(m_tmd_size); - Read(m_tmd_offset, m_tmd_size, buffer.data(), false); - return buffer; + return m_tmd; } std::string CVolumeWAD::GetGameID() const @@ -125,11 +121,10 @@ bool CVolumeWAD::GetTitleID(u64* buffer) const u16 CVolumeWAD::GetRevision() const { - u16 revision; - if (!m_reader->Read(m_tmd_offset + 0x1dc, 2, (u8*)&revision)) + if (!m_tmd.IsValid()) return 0; - return Common::swap16(revision); + return m_tmd.GetTitleVersion(); } Platform CVolumeWAD::GetVolumeType() const diff --git a/Source/Core/DiscIO/VolumeWad.h b/Source/Core/DiscIO/VolumeWad.h index c93315f72b..f84af08796 100644 --- a/Source/Core/DiscIO/VolumeWad.h +++ b/Source/Core/DiscIO/VolumeWad.h @@ -10,6 +10,7 @@ #include #include "Common/CommonTypes.h" +#include "Core/IOS/ES/Formats.h" #include "DiscIO/Volume.h" // --- this volume type is used for Wad files --- @@ -32,7 +33,7 @@ public: ~CVolumeWAD(); bool Read(u64 offset, u64 length, u8* buffer, bool decrypt = false) const override; bool GetTitleID(u64* buffer) const override; - std::vector GetTMD() const override; + IOS::ES::TMDReader GetTMD() const override; std::string GetGameID() const override; std::string GetMakerID() const override; u16 GetRevision() const override; @@ -51,6 +52,7 @@ public: private: std::unique_ptr m_reader; + IOS::ES::TMDReader m_tmd; u32 m_offset = 0; u32 m_tmd_offset = 0; u32 m_opening_bnr_offset = 0; diff --git a/Source/Core/DiscIO/VolumeWiiCrypted.cpp b/Source/Core/DiscIO/VolumeWiiCrypted.cpp index ae174604cc..ef291fa237 100644 --- a/Source/Core/DiscIO/VolumeWiiCrypted.cpp +++ b/Source/Core/DiscIO/VolumeWiiCrypted.cpp @@ -114,7 +114,7 @@ bool CVolumeWiiCrypted::GetTitleID(u64* buffer) const return true; } -std::vector CVolumeWiiCrypted::GetTMD() const +IOS::ES::TMDReader CVolumeWiiCrypted::GetTMD() const { u32 tmd_size; u32 tmd_address; @@ -137,7 +137,7 @@ std::vector CVolumeWiiCrypted::GetTMD() const std::vector buffer(tmd_size); Read(m_VolumeOffset + tmd_address, tmd_size, buffer.data(), false); - return buffer; + return IOS::ES::TMDReader{std::move(buffer)}; } u64 CVolumeWiiCrypted::PartitionOffsetToRawOffset(u64 offset) const diff --git a/Source/Core/DiscIO/VolumeWiiCrypted.h b/Source/Core/DiscIO/VolumeWiiCrypted.h index 7d8943ea8a..dfeb74ad66 100644 --- a/Source/Core/DiscIO/VolumeWiiCrypted.h +++ b/Source/Core/DiscIO/VolumeWiiCrypted.h @@ -11,6 +11,7 @@ #include #include "Common/CommonTypes.h" +#include "Core/IOS/ES/Formats.h" #include "DiscIO/Volume.h" // --- this volume type is used for encrypted Wii images --- @@ -32,7 +33,7 @@ public: ~CVolumeWiiCrypted(); bool Read(u64 _Offset, u64 _Length, u8* _pBuffer, bool decrypt) const override; bool GetTitleID(u64* buffer) const override; - std::vector GetTMD() const override; + IOS::ES::TMDReader GetTMD() const override; u64 PartitionOffsetToRawOffset(u64 offset) const override; std::string GetGameID() const override; std::string GetMakerID() const override; diff --git a/Source/Core/DiscIO/WiiWad.cpp b/Source/Core/DiscIO/WiiWad.cpp index 04e4ff8a7c..2576dac41f 100644 --- a/Source/Core/DiscIO/WiiWad.cpp +++ b/Source/Core/DiscIO/WiiWad.cpp @@ -90,7 +90,7 @@ bool WiiWAD::ParseWAD(IBlobReader& reader) offset += Common::AlignUp(certificate_chain_size, 0x40); m_ticket.SetBytes(CreateWADEntry(reader, ticket_size, offset)); offset += Common::AlignUp(ticket_size, 0x40); - m_tmd = CreateWADEntry(reader, tmd_size, offset); + m_tmd.SetBytes(CreateWADEntry(reader, tmd_size, offset)); offset += Common::AlignUp(tmd_size, 0x40); m_data_app = CreateWADEntry(reader, data_app_size, offset); offset += Common::AlignUp(data_app_size, 0x40); diff --git a/Source/Core/DiscIO/WiiWad.h b/Source/Core/DiscIO/WiiWad.h index ea0c3de43a..46480fdaa6 100644 --- a/Source/Core/DiscIO/WiiWad.h +++ b/Source/Core/DiscIO/WiiWad.h @@ -23,8 +23,8 @@ public: bool IsValid() const { return m_valid; } const std::vector& GetCertificateChain() const { return m_certificate_chain; } - const ES::TicketReader& GetTicket() const { return m_ticket; } - const std::vector& GetTMD() const { return m_tmd; } + const IOS::ES::TicketReader& GetTicket() const { return m_ticket; } + const IOS::ES::TMDReader& GetTMD() const { return m_tmd; } const std::vector& GetDataApp() const { return m_data_app; } const std::vector& GetFooter() const { return m_footer; } private: @@ -33,8 +33,8 @@ private: bool m_valid; std::vector m_certificate_chain; - ES::TicketReader m_ticket; - std::vector m_tmd; + IOS::ES::TicketReader m_ticket; + IOS::ES::TMDReader m_tmd; std::vector m_data_app; std::vector m_footer; }; diff --git a/Source/Core/DolphinWX/FrameTools.cpp b/Source/Core/DolphinWX/FrameTools.cpp index e1a9271744..535dc568aa 100644 --- a/Source/Core/DolphinWX/FrameTools.cpp +++ b/Source/Core/DolphinWX/FrameTools.cpp @@ -1225,23 +1225,7 @@ void CFrame::OnInstallWAD(wxCommandEvent& event) void CFrame::UpdateLoadWiiMenuItem() const { - auto* const menu_item = GetMenuBar()->FindItem(IDM_LOAD_WII_MENU); - - const auto& sys_menu_loader = DiscIO::CNANDContentManager::Access().GetNANDLoader( - TITLEID_SYSMENU, Common::FROM_CONFIGURED_ROOT); - - if (sys_menu_loader.IsValid()) - { - const int version = sys_menu_loader.GetTitleVersion(); - const char region = sys_menu_loader.GetCountryChar(); - menu_item->Enable(); - menu_item->SetItemLabel(wxString::Format(_("Load Wii System Menu %d%c"), version, region)); - } - else - { - menu_item->Enable(false); - menu_item->SetItemLabel(_("Load Wii System Menu")); - } + GetMenuBar()->Refresh(true, nullptr); } void CFrame::OnFifoPlayer(wxCommandEvent& WXUNUSED(event)) diff --git a/Source/Core/DolphinWX/ISOProperties/InfoPanel.cpp b/Source/Core/DolphinWX/ISOProperties/InfoPanel.cpp index 6a839c32be..3fc9efc14d 100644 --- a/Source/Core/DolphinWX/ISOProperties/InfoPanel.cpp +++ b/Source/Core/DolphinWX/ISOProperties/InfoPanel.cpp @@ -182,7 +182,7 @@ void InfoPanel::LoadISODetails() m_fst->SetValue(StrToWxStr(std::to_string(m_opened_iso->GetFSTSize()))); if (m_ios_version) { - ES::TMDReader tmd{m_opened_iso->GetTMD()}; + const IOS::ES::TMDReader tmd = m_opened_iso->GetTMD(); if (tmd.IsValid()) m_ios_version->SetValue(StringFromFormat("IOS%u", static_cast(tmd.GetIOSId()))); } @@ -223,7 +223,7 @@ wxStaticBoxSizer* InfoPanel::CreateISODetailsSizer() {_("Apploader Date:"), m_date}, {_("FST Size:"), m_fst}, }}; - if (!m_opened_iso->GetTMD().empty()) + if (m_opened_iso->GetTMD().IsValid()) controls.emplace_back(_("IOS Version:"), m_ios_version); const int space_10 = FromDIP(10); diff --git a/Source/Core/DolphinWX/MainMenuBar.cpp b/Source/Core/DolphinWX/MainMenuBar.cpp index 6cf1240b30..a0ec767fff 100644 --- a/Source/Core/DolphinWX/MainMenuBar.cpp +++ b/Source/Core/DolphinWX/MainMenuBar.cpp @@ -12,6 +12,7 @@ #include "Core/Core.h" #include "Core/PowerPC/PowerPC.h" #include "Core/State.h" +#include "DiscIO/Enums.h" #include "DiscIO/NANDContentLoader.h" #include "DolphinWX/Globals.h" #include "DolphinWX/WxUtils.h" @@ -546,8 +547,8 @@ void MainMenuBar::RefreshWiiSystemMenuLabel() const if (sys_menu_loader.IsValid()) { - const auto sys_menu_version = sys_menu_loader.GetTitleVersion(); - const auto sys_menu_region = sys_menu_loader.GetCountryChar(); + const u16 sys_menu_version = sys_menu_loader.GetTMD().GetTitleVersion(); + const char sys_menu_region = DiscIO::GetSysMenuRegion(sys_menu_version); item->Enable(); item->SetItemLabel( wxString::Format(_("Load Wii System Menu %u%c"), sys_menu_version, sys_menu_region));