Use ESFormats for TMDs

We already have a TMDReader, so let's actually use it.

And move ESFormats to IOS::ES, since it's definitely part of IOS.
This adds a DiscIO dependency on Core which will be fixed in a
follow-up PR.
This commit is contained in:
Léo Lam 2017-02-11 08:57:47 +01:00
parent bf1f70db0a
commit c1a139e8ac
26 changed files with 337 additions and 356 deletions

View File

@ -102,7 +102,7 @@ bool CBoot::FindMapFile(std::string* existing_map_file, std::string* writable_ma
DiscIO::CNANDContentManager::Access().GetNANDLoader(_StartupPara.m_strFilename); DiscIO::CNANDContentManager::Access().GetNANDLoader(_StartupPara.m_strFilename);
if (Loader.IsValid()) if (Loader.IsValid())
{ {
u64 TitleID = Loader.GetTitleID(); u64 TitleID = Loader.GetTMD().GetTitleId();
title_id_str = StringFromFormat("%08X_%08X", (u32)(TitleID >> 32) & 0xFFFFFFFF, title_id_str = StringFromFormat("%08X_%08X", (u32)(TitleID >> 32) & 0xFFFFFFFF,
(u32)TitleID & 0xFFFFFFFF); (u32)TitleID & 0xFFFFFFFF);
} }
@ -278,11 +278,7 @@ bool CBoot::BootUp()
PanicAlertT("Warning - starting ISO in wrong console mode!"); PanicAlertT("Warning - starting ISO in wrong console mode!");
} }
std::vector<u8> tmd_buffer = pVolume.GetTMD(); IOS::HLE::ES_DIVerify(pVolume.GetTMD());
if (!tmd_buffer.empty())
{
IOS::HLE::ES_DIVerify(tmd_buffer);
}
_StartupPara.bWii = pVolume.GetVolumeType() == DiscIO::Platform::WII_DISC; _StartupPara.bWii = pVolume.GetVolumeType() == DiscIO::Platform::WII_DISC;

View File

@ -346,11 +346,9 @@ bool CBoot::EmulatedBS2_Wii()
PowerPC::ppcState.gpr[1] = 0x816ffff0; // StackPointer PowerPC::ppcState.gpr[1] = 0x816ffff0; // StackPointer
std::vector<u8> tmd = DVDInterface::GetVolume().GetTMD(); IOS::ES::TMDReader tmd = DVDInterface::GetVolume().GetTMD();
ES::TMDReader tmd_reader{std::move(tmd)}; if (!SetupWiiMemory(tmd.GetIOSId()))
if (!SetupWiiMemory(tmd_reader.GetIOSId()))
return false; return false;
// Execute the apploader // Execute the apploader

View File

@ -78,7 +78,7 @@ bool CBoot::Boot_WiiWAD(const std::string& _pFilename)
if (!ContentLoader.IsValid()) if (!ContentLoader.IsValid())
return false; return false;
u64 titleID = ContentLoader.GetTitleID(); u64 titleID = ContentLoader.GetTMD().GetTitleId();
// create data directory // create data directory
File::CreateFullPath(Common::GetTitleDataPath(titleID, Common::FROM_SESSION_ROOT)); File::CreateFullPath(Common::GetTitleDataPath(titleID, Common::FROM_SESSION_ROOT));
@ -86,12 +86,11 @@ bool CBoot::Boot_WiiWAD(const std::string& _pFilename)
IOS::HLE::CreateVirtualFATFilesystem(); IOS::HLE::CreateVirtualFATFilesystem();
// setup Wii memory // setup Wii memory
u64 ios_title_id = 0x0000000100000000ULL | ContentLoader.GetIosVersion(); if (!SetupWiiMemory(ContentLoader.GetTMD().GetIOSId()))
if (!SetupWiiMemory(ios_title_id))
return false; return false;
// DOL // DOL
const DiscIO::SNANDContent* pContent = const DiscIO::SNANDContent* pContent =
ContentLoader.GetContentByIndex(ContentLoader.GetBootIndex()); ContentLoader.GetContentByIndex(ContentLoader.GetTMD().GetBootIndex());
if (pContent == nullptr) if (pContent == nullptr)
return false; return false;

View File

@ -922,7 +922,7 @@ bool SConfig::AutoSetup(EBootBS2 _BootBS2)
const DiscIO::CNANDContentLoader& ContentLoader = const DiscIO::CNANDContentLoader& ContentLoader =
DiscIO::CNANDContentManager::Access().GetNANDLoader(m_strFilename); 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. // WAD is valid yet cannot be booted. Install instead.
u64 installed = DiscIO::CNANDContentManager::Access().Install_WiiWAD(m_strFilename); u64 installed = DiscIO::CNANDContentManager::Access().Install_WiiWAD(m_strFilename);
@ -931,7 +931,7 @@ bool SConfig::AutoSetup(EBootBS2 _BootBS2)
return false; // do not boot return false; // do not boot
} }
SetRegion(ContentLoader.GetRegion(), &set_region_dir); SetRegion(ContentLoader.GetTMD().GetRegion(), &set_region_dir);
bWii = true; bWii = true;
m_BootType = BOOT_WII_NAND; m_BootType = BOOT_WII_NAND;

View File

@ -14,6 +14,7 @@
#include "Core/HW/DVDInterface.h" #include "Core/HW/DVDInterface.h"
#include "Core/HW/Memmap.h" #include "Core/HW/Memmap.h"
#include "Core/IOS/DI/DI.h" #include "Core/IOS/DI/DI.h"
#include "Core/IOS/ES/Formats.h"
#include "Core/IOS/IPC.h" #include "Core/IOS/IPC.h"
#include "DiscIO/Volume.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); INFO_LOG(IOS_DI, "DVDLowOpenPartition: partition_offset 0x%016" PRIx64, partition_offset);
// Read TMD to the buffer // Read TMD to the buffer
std::vector<u8> tmd_buffer = DVDInterface::GetVolume().GetTMD(); const ES::TMDReader tmd = DVDInterface::GetVolume().GetTMD();
Memory::CopyToEmu(request.io_vectors[0].address, tmd_buffer.data(), tmd_buffer.size()); const std::vector<u8> raw_tmd = tmd.GetRawTMD();
ES_DIVerify(tmd_buffer); Memory::CopyToEmu(request.io_vectors[0].address, raw_tmd.data(), raw_tmd.size());
ES_DIVerify(tmd);
return_value = 1; return_value = 1;
break; break;

View File

@ -86,7 +86,7 @@ void ES::OpenInternal()
// check for cd ... // check for cd ...
if (contentLoader.IsValid()) if (contentLoader.IsValid())
{ {
m_TitleID = contentLoader.GetTitleID(); m_TitleID = contentLoader.GetTMD().GetTitleId();
m_TitleIDs.clear(); m_TitleIDs.clear();
DiscIO::cUIDsys uid_sys{Common::FromWhichRoot::FROM_SESSION_ROOT}; DiscIO::cUIDsys uid_sys{Common::FromWhichRoot::FROM_SESSION_ROOT};
@ -201,8 +201,8 @@ u32 ES::OpenTitleContent(u32 CFD, u64 TitleID, u16 Index)
SContentAccess Access; SContentAccess Access;
Access.m_Position = 0; Access.m_Position = 0;
Access.m_Index = pContent->m_Index; Access.m_Index = pContent->m_metadata.index;
Access.m_Size = pContent->m_Size; Access.m_Size = static_cast<u32>(pContent->m_metadata.size);
Access.m_TitleID = TitleID; Access.m_TitleID = TitleID;
pContent->m_Data->Open(); pContent->m_Data->Open();
@ -319,7 +319,7 @@ IPCCommandResult ES::AddTicket(const IOCtlVRequest& request)
std::vector<u8> ticket(request.in_vectors[0].size); std::vector<u8> ticket(request.in_vectors[0].size);
Memory::CopyFromEmu(ticket.data(), request.in_vectors[0].address, 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); 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); INFO_LOG(IOS_ES, "IOCTL_ES_ADDCONTENTFINISH: content fd %08x", content_fd);
// Try to find the title key from a pre-installed ticket. // 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()) if (!ticket.IsValid())
{ {
return GetDefaultReply(ES_NO_TICKET_INSTALLED); 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 // The IV for title content decryption is the lower two bytes of the
// content index, zero extended. // 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)) if (!m_addtitle_tmd.FindContentById(m_addtitle_content_id, &content_info))
{ {
return GetDefaultReply(ES_INVALID_TMD); 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); u64 TitleID = Memory::Read_U64(request.in_vectors[0].address);
const DiscIO::CNANDContentLoader& rNANDContent = AccessContentDevice(TitleID); const DiscIO::CNANDContentLoader& nand_content = AccessContentDevice(TitleID);
u16 NumberOfPrivateContent = 0; if (!nand_content.IsValid())
s32 return_value = IPC_SUCCESS; return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
if (rNANDContent.IsValid()) // Not sure if dolphin will ever fail this check
{ const u16 num_contents = nand_content.GetTMD().GetNumContents();
NumberOfPrivateContent = rNANDContent.GetNumEntries();
if ((u32)(TitleID >> 32) == 0x00010000) if ((u32)(TitleID >> 32) == 0x00010000)
Memory::Write_U32(0, request.io_vectors[0].address); Memory::Write_U32(0, request.io_vectors[0].address);
else else
Memory::Write_U32(NumberOfPrivateContent, request.io_vectors[0].address); Memory::Write_U32(num_contents, request.io_vectors[0].address);
}
else
{
return_value = static_cast<s32>(rNANDContent.GetContentSize());
}
INFO_LOG(IOS_ES, "IOCTL_ES_GETTITLECONTENTSCNT: TitleID: %08x/%08x content count %i", INFO_LOG(IOS_ES, "IOCTL_ES_GETTITLECONTENTSCNT: TitleID: %08x/%08x content count %i",
(u32)(TitleID >> 32), (u32)TitleID, (u32)(TitleID >> 32), (u32)TitleID, num_contents);
rNANDContent.IsValid() ? NumberOfPrivateContent : (u32)rNANDContent.GetContentSize());
return GetDefaultReply(return_value); return GetDefaultReply(IPC_SUCCESS);
} }
IPCCommandResult ES::GetTitleContents(const IOCtlVRequest& request) 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); u64 TitleID = Memory::Read_U64(request.in_vectors[0].address);
const DiscIO::CNANDContentLoader& rNANDContent = AccessContentDevice(TitleID); const DiscIO::CNANDContentLoader& rNANDContent = AccessContentDevice(TitleID);
s32 return_value = IPC_SUCCESS; if (!rNANDContent.IsValid())
if (rNANDContent.IsValid()) // Not sure if dolphin will ever fail this check return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
for (const auto& content : rNANDContent.GetContent())
{ {
for (u16 i = 0; i < rNANDContent.GetNumEntries(); i++) const u16 index = content.m_metadata.index;
{ Memory::Write_U32(content.m_metadata.id, request.io_vectors[0].address + index * 4);
Memory::Write_U32(rNANDContent.GetContentByIndex(i)->m_ContentID, INFO_LOG(IOS_ES, "IOCTL_ES_GETTITLECONTENTS: Index %d: %08x", index, content.m_metadata.id);
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<s32>(rNANDContent.GetContentSize());
ERROR_LOG(IOS_ES, "IOCTL_ES_GETTITLECONTENTS: Unable to open content %zu",
rNANDContent.GetContentSize());
} }
return GetDefaultReply(return_value); return GetDefaultReply(IPC_SUCCESS);
} }
IPCCommandResult ES::OpenTitleContent(const IOCtlVRequest& request) 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) for (u32 view = 0; view < number_of_views; ++view)
{ {
const std::vector<u8> ticket_view = Loader.GetTicket().GetRawTicketView(view); const std::vector<u8> 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()); ticket_view.data(), ticket_view.size());
} }
} }
@ -809,6 +794,7 @@ IPCCommandResult ES::GetViews(const IOCtlVRequest& request)
return GetDefaultReply(IPC_SUCCESS); 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) IPCCommandResult ES::GetTMDViewCount(const IOCtlVRequest& request)
{ {
_dbg_assert_msg_(IOS_ES, request.in_vectors.size() == 1, "IOCTL_ES_GETTMDVIEWCNT no in buffer"); _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); const DiscIO::CNANDContentLoader& Loader = AccessContentDevice(TitleID);
u32 TMDViewCnt = 0; u32 view_size = 0;
if (Loader.IsValid()) if (Loader.IsValid())
{ view_size = static_cast<u32>(Loader.GetTMD().GetRawView().size());
TMDViewCnt += DiscIO::CNANDContentLoader::TMD_VIEW_SIZE; Memory::Write_U32(view_size, request.io_vectors[0].address);
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);
INFO_LOG(IOS_ES, "IOCTL_ES_GETTMDVIEWCNT: title: %08x/%08x (view size %i)", (u32)(TitleID >> 32), 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); return GetDefaultReply(IPC_SUCCESS);
} }
@ -848,30 +829,11 @@ IPCCommandResult ES::GetTMDViews(const IOCtlVRequest& request)
if (Loader.IsValid()) if (Loader.IsValid())
{ {
u32 Address = request.io_vectors[0].address; const std::vector<u8> 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); Memory::CopyToEmu(request.io_vectors[0].address, raw_view.data(), raw_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<DiscIO::SNANDContent>& 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);
} }
INFO_LOG(IOS_ES, "IOCTL_ES_GETTMDVIEWS: title: %08x/%08x (buffer size: %i)", (u32)(TitleID >> 32), 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); const DiscIO::CNANDContentLoader& Loader = AccessContentDevice(TitleID);
_dbg_assert_(IOS_ES, Loader.IsValid()); _dbg_assert_(IOS_ES, Loader.IsValid());
u32 TMDCnt = 0; u32 tmd_size = 0;
if (Loader.IsValid()) if (Loader.IsValid())
{ tmd_size = static_cast<u32>(Loader.GetTMD().GetRawTMD().size());
TMDCnt += DiscIO::CNANDContentLoader::TMD_HEADER_SIZE;
TMDCnt += (u32)Loader.GetContentSize() * DiscIO::CNANDContentLoader::CONTENT_HEADER_SIZE;
}
if (request.io_vectors.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)", 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); return GetDefaultReply(IPC_SUCCESS);
} }
@ -962,20 +921,11 @@ IPCCommandResult ES::GetStoredTMD(const IOCtlVRequest& request)
if (Loader.IsValid() && request.io_vectors.size()) if (Loader.IsValid() && request.io_vectors.size())
{ {
u32 Address = request.io_vectors[0].address; const std::vector<u8> 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); Memory::CopyToEmu(request.io_vectors[0].address, raw_tmd.data(), raw_tmd.size());
Address += DiscIO::CNANDContentLoader::TMD_HEADER_SIZE;
const std::vector<DiscIO::SNANDContent>& 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);
} }
INFO_LOG(IOS_ES, "IOCTL_ES_GETSTOREDTMD: title: %08x/%08x (buffer size: %i)", 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); const DiscIO::CNANDContentLoader& ContentLoader = AccessContentDevice(TitleID);
if (ContentLoader.IsValid()) 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); const DiscIO::SNANDContent* pContent = ContentLoader.GetContentByIndex(bootInd);
if (pContent) 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); return DiscIO::CNANDContentManager::Access().GetNANDLoader(title_id, Common::FROM_SESSION_ROOT);
} }
u32 ES::ES_DIVerify(const std::vector<u8>& tmd) u32 ES::ES_DIVerify(const IOS::ES::TMDReader& tmd)
{ {
if (!tmd.IsValid())
return -1;
u64 title_id = 0xDEADBEEFDEADBEEFull; u64 title_id = 0xDEADBEEFDEADBEEFull;
u64 tmd_title_id = Common::swap64(&tmd[0x18C]); u64 tmd_title_id = tmd.GetTitleId();
DVDInterface::GetVolume().GetTitleID(&title_id); DVDInterface::GetVolume().GetTitleID(&title_id);
if (title_id != tmd_title_id) if (title_id != tmd_title_id)
@ -1211,7 +1164,8 @@ u32 ES::ES_DIVerify(const std::vector<u8>& tmd)
if (!File::Exists(tmd_path)) if (!File::Exists(tmd_path))
{ {
File::IOFile tmd_file(tmd_path, "wb"); File::IOFile tmd_file(tmd_path, "wb");
if (!tmd_file.WriteBytes(tmd.data(), tmd.size())) const std::vector<u8>& 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."); ERROR_LOG(IOS_ES, "DIVerify failed to write disc TMD to NAND.");
} }
DiscIO::cUIDsys uid_sys{Common::FromWhichRoot::FROM_SESSION_ROOT}; DiscIO::cUIDsys uid_sys{Common::FromWhichRoot::FROM_SESSION_ROOT};

View File

@ -46,7 +46,7 @@ public:
void Close() override; void Close() override;
IPCCommandResult IOCtlV(const IOCtlVRequest& request) override; IPCCommandResult IOCtlV(const IOCtlVRequest& request) override;
static u32 ES_DIVerify(const std::vector<u8>& tmd); static u32 ES_DIVerify(const IOS::ES::TMDReader& tmd);
// This should only be cleared on power reset // This should only be cleared on power reset
static std::string m_ContentFile; static std::string m_ContentFile;
@ -203,7 +203,7 @@ private:
u32 m_AccessIdentID = 0; u32 m_AccessIdentID = 0;
// For title installation (ioctls IOCTL_ES_ADDTITLE*). // For title installation (ioctls IOCTL_ES_ADDTITLE*).
::ES::TMDReader m_addtitle_tmd; IOS::ES::TMDReader m_addtitle_tmd;
u32 m_addtitle_content_id = 0xFFFFFFFF; u32 m_addtitle_content_id = 0xFFFFFFFF;
std::vector<u8> m_addtitle_content_buffer; std::vector<u8> m_addtitle_content_buffer;
}; };

View File

@ -16,8 +16,12 @@
#include "Common/CommonFuncs.h" #include "Common/CommonFuncs.h"
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
namespace IOS
{
namespace ES namespace ES
{ {
constexpr size_t CONTENT_VIEW_SIZE = 0x10;
std::vector<u8> AESDecode(const u8* key, u8* iv, const u8* src, u32 size) std::vector<u8> AESDecode(const u8* key, u8* iv, const u8* src, u32 size)
{ {
mbedtls_aes_context aes_ctx; mbedtls_aes_context aes_ctx;
@ -49,13 +53,13 @@ void TMDReader::SetBytes(std::vector<u8>&& bytes)
bool TMDReader::IsValid() const bool TMDReader::IsValid() const
{ {
if (m_bytes.size() < 0x1E4) if (m_bytes.size() < sizeof(TMDHeader))
{ {
// TMD is too small to contain its base fields. // TMD is too small to contain its base fields.
return false; 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. // TMD is too small to contain all its expected content entries.
return false; return false;
@ -64,19 +68,69 @@ bool TMDReader::IsValid() const
return true; return true;
} }
const std::vector<u8>& TMDReader::GetRawTMD() const
{
return m_bytes;
}
std::vector<u8> TMDReader::GetRawHeader() const
{
return std::vector<u8>(m_bytes.begin(), m_bytes.begin() + sizeof(TMDHeader));
}
std::vector<u8> TMDReader::GetRawView() const
{
// Base fields
std::vector<u8> 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 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<u8>(GetTitleId() & 0xff));
} }
u64 TMDReader::GetTitleId() const 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 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 bool TMDReader::GetContent(u16 index, Content* content) const
@ -86,16 +140,24 @@ bool TMDReader::GetContent(u16 index, Content* content) const
return false; return false;
} }
const u8* content_base = m_bytes.data() + 0x1E4 + index * 36; const u8* content_base = m_bytes.data() + sizeof(TMDHeader) + index * sizeof(Content);
content->id = Common::swap32(content_base); content->id = Common::swap32(content_base + offsetof(Content, id));
content->index = Common::swap16(content_base + 4); content->index = Common::swap16(content_base + offsetof(Content, index));
content->type = Common::swap16(content_base + 6); content->type = Common::swap16(content_base + offsetof(Content, type));
content->size = Common::swap64(content_base + 8); content->size = Common::swap64(content_base + offsetof(Content, size));
std::copy(content_base + 16, content_base + 36, content->sha1.begin()); std::copy_n(content_base + offsetof(Content, sha1), content->sha1.size(), content->sha1.begin());
return true; return true;
} }
std::vector<Content> TMDReader::GetContents() const
{
std::vector<Content> contents(GetNumContents());
for (size_t i = 0; i < contents.size(); ++i)
GetContent(static_cast<u16>(i), &contents[i]);
return contents;
}
bool TMDReader::FindContentById(u32 id, Content* content) const bool TMDReader::FindContentById(u32 id, Content* content) const
{ {
for (u16 index = 0; index < GetNumContents(); ++index) for (u16 index = 0; index < GetNumContents(); ++index)
@ -205,3 +267,4 @@ std::vector<u8> TicketReader::GetTitleKey() const
return AESDecode(common_key, iv, &m_bytes[GetOffset() + offsetof(Ticket, title_key)], 16); return AESDecode(common_key, iv, &m_bytes[GetOffset() + offsetof(Ticket, title_key)], 16);
} }
} // namespace ES } // namespace ES
} // namespace IOS

View File

@ -12,25 +12,39 @@
#include "Common/ChunkFile.h" #include "Common/ChunkFile.h"
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include "DiscIO/Enums.h"
namespace IOS
{
namespace ES namespace ES
{ {
std::vector<u8> AESDecode(const u8* key, u8* iv, const u8* src, u32 size); #pragma pack(push, 4)
struct TMDHeader
class TMDReader final
{ {
public: u32 signature_type;
TMDReader() = default; u8 rsa_2048_signature[256];
explicit TMDReader(const std::vector<u8>& bytes); u8 fill[60];
explicit TMDReader(std::vector<u8>&& bytes); u8 issuer[64];
u8 tmd_version;
void SetBytes(const std::vector<u8>& bytes); u8 ca_crl_version;
void SetBytes(std::vector<u8>&& bytes); u8 signer_crl_version;
u64 ios_id;
bool IsValid() const; u64 title_id;
u32 title_type;
u64 GetIOSId() const; u16 group_id;
u64 GetTitleId() const; 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 struct Content
{ {
@ -40,17 +54,8 @@ public:
u64 size; u64 size;
std::array<u8, 20> sha1; std::array<u8, 20> sha1;
}; };
u16 GetNumContents() const; static_assert(sizeof(Content) == 36, "Content has the wrong size");
bool GetContent(u16 index, Content* content) const;
bool FindContentById(u32 id, Content* content) const;
void DoState(PointerWrap& p);
private:
std::vector<u8> m_bytes;
};
#pragma pack(push, 4)
struct TimeLimit struct TimeLimit
{ {
u32 enabled; u32 enabled;
@ -96,6 +101,42 @@ struct Ticket
static_assert(sizeof(Ticket) == 356, "Ticket has the wrong size"); static_assert(sizeof(Ticket) == 356, "Ticket has the wrong size");
#pragma pack(pop) #pragma pack(pop)
std::vector<u8> AESDecode(const u8* key, u8* iv, const u8* src, u32 size);
class TMDReader final
{
public:
TMDReader() = default;
explicit TMDReader(const std::vector<u8>& bytes);
explicit TMDReader(std::vector<u8>&& bytes);
void SetBytes(const std::vector<u8>& bytes);
void SetBytes(std::vector<u8>&& bytes);
bool IsValid() const;
// Returns the TMD or parts of it without any kind of parsing. Intended for use by ES.
const std::vector<u8>& GetRawTMD() const;
std::vector<u8> GetRawHeader() const;
std::vector<u8> 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<Content> GetContents() const;
bool FindContentById(u32 id, Content* content) const;
void DoState(PointerWrap& p);
private:
std::vector<u8> m_bytes;
};
class TicketReader final class TicketReader final
{ {
public: public:
@ -125,3 +166,4 @@ private:
std::vector<u8> m_bytes; std::vector<u8> m_bytes;
}; };
} // namespace ES } // namespace ES
} // namespace IOS

View File

@ -658,7 +658,7 @@ void SetDefaultContentFile(const std::string& file_name)
es->LoadWAD(file_name); es->LoadWAD(file_name);
} }
void ES_DIVerify(const std::vector<u8>& tmd) void ES_DIVerify(const ES::TMDReader& tmd)
{ {
Device::ES::ES_DIVerify(tmd); Device::ES::ES_DIVerify(tmd);
} }

View File

@ -16,6 +16,11 @@ class PointerWrap;
namespace IOS namespace IOS
{ {
namespace ES
{
class TMDReader;
}
namespace HLE namespace HLE
{ {
namespace Device namespace Device
@ -61,7 +66,7 @@ void DoState(PointerWrap& p);
// Set default content file // Set default content file
void SetDefaultContentFile(const std::string& file_name); void SetDefaultContentFile(const std::string& file_name);
void ES_DIVerify(const std::vector<u8>& tmd); void ES_DIVerify(const ES::TMDReader& tmd);
void SDIO_EventNotify(); void SDIO_EventNotify();

View File

@ -23,6 +23,7 @@
#include "Core/HW/DVDThread.h" #include "Core/HW/DVDThread.h"
#include "Core/HW/Memmap.h" #include "Core/HW/Memmap.h"
#include "Core/HW/SystemTimers.h" #include "Core/HW/SystemTimers.h"
#include "Core/IOS/ES/Formats.h"
#include "Core/IOS/MIOS.h" #include "Core/IOS/MIOS.h"
#include "Core/PowerPC/PPCSymbolDB.h" #include "Core/PowerPC/PPCSymbolDB.h"
#include "Core/PowerPC/PowerPC.h" #include "Core/PowerPC/PowerPC.h"
@ -97,7 +98,7 @@ static std::vector<u8> GetMIOSBinary()
if (!loader.IsValid()) if (!loader.IsValid())
return {}; return {};
const auto* content = loader.GetContentByIndex(loader.GetBootIndex()); const auto* content = loader.GetContentByIndex(loader.GetTMD().GetBootIndex());
if (!content) if (!content)
return {}; return {};

View File

@ -134,7 +134,7 @@ IPCCommandResult WFSI::IOCtl(const IOCtlRequest& request)
// Initializes the IV from the index of the content in the TMD contents. // Initializes the IV from the index of the content in the TMD contents.
u32 content_id = Memory::Read_U32(request.buffer_in + 8); 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)) if (!m_tmd.FindContentById(content_id, &content_info))
{ {
WARN_LOG(IOS, "%s: Content id %08x not found", ioctl_name, content_id); WARN_LOG(IOS, "%s: Content id %08x not found", ioctl_name, content_id);

View File

@ -50,7 +50,7 @@ private:
u8 m_aes_key[0x10] = {}; u8 m_aes_key[0x10] = {};
u8 m_aes_iv[0x10] = {}; u8 m_aes_iv[0x10] = {};
::ES::TMDReader m_tmd; IOS::ES::TMDReader m_tmd;
std::string m_base_extract_path; std::string m_base_extract_path;
ARCUnpacker m_arc_unpacker; ARCUnpacker m_arc_unpacker;

View File

@ -4,6 +4,7 @@
#include <algorithm> #include <algorithm>
#include <array> #include <array>
#include <cinttypes>
#include <cstddef> #include <cstddef>
#include <cstdio> #include <cstdio>
#include <cstring> #include <cstring>
@ -146,7 +147,6 @@ bool CNANDContentDataBuffer::GetRange(u32 start, u32 size, u8* buffer)
} }
CNANDContentLoader::CNANDContentLoader(const std::string& content_name) 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); m_Valid = Initialize(content_name);
} }
@ -159,7 +159,7 @@ const SNANDContent* CNANDContentLoader::GetContentByIndex(int index) const
{ {
for (auto& Content : m_Content) for (auto& Content : m_Content)
{ {
if (Content.m_Index == index) if (Content.m_metadata.index == index)
{ {
return &Content; return &Content;
} }
@ -176,13 +176,12 @@ bool CNANDContentLoader::Initialize(const std::string& name)
WiiWAD wad(name); WiiWAD wad(name);
std::vector<u8> data_app; std::vector<u8> data_app;
std::vector<u8> tmd;
if (wad.IsValid()) if (wad.IsValid())
{ {
m_IsWAD = true; m_IsWAD = true;
m_ticket = wad.GetTicket(); m_ticket = wad.GetTicket();
tmd = wad.GetTMD(); m_tmd = wad.GetTMD();
data_app = wad.GetDataApp(); data_app = wad.GetDataApp();
} }
else else
@ -201,92 +200,62 @@ bool CNANDContentLoader::Initialize(const std::string& name)
return false; return false;
} }
tmd.resize(static_cast<size_t>(File::GetSize(tmd_filename))); std::vector<u8> bytes(File::GetSize(tmd_filename));
tmd_file.ReadBytes(tmd.data(), tmd.size()); 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); InitializeContentEntries(data_app);
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<u8>(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);
return true; return true;
} }
void CNANDContentLoader::InitializeContentEntries(const std::vector<u8>& tmd, void CNANDContentLoader::InitializeContentEntries(const std::vector<u8>& data_app)
const std::vector<u8>& 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<IOS::ES::Content> contents = m_tmd.GetContents();
m_Content.resize(contents.size());
std::array<u8, 16> iv;
u32 data_app_offset = 0; u32 data_app_offset = 0;
const std::vector<u8> title_key = m_ticket.GetTitleKey();
CSharedContent shared_content{Common::FromWhichRoot::FROM_SESSION_ROOT}; 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; const auto& content = contents.at(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<u32>(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);
if (m_IsWAD) 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<u8, 16> iv{};
iv[0] = static_cast<u8>(content.index >> 8) & 0xFF;
iv[1] = static_cast<u8>(content.index) & 0xFF;
iv.fill(0); u32 rounded_size = Common::AlignUp(static_cast<u32>(content.size), 0x40);
std::copy(&tmd[entry_offset + 0x01E8], &tmd[entry_offset + 0x01E8 + 2], iv.begin());
content.m_Data = std::make_unique<CNANDContentDataBuffer>(ES::AESDecode(
m_ticket.GetTitleKey().data(), iv.data(), &data_app[data_app_offset], rounded_size));
m_Content[i].m_Data = std::make_unique<CNANDContentDataBuffer>(IOS::ES::AESDecode(
title_key.data(), iv.data(), &data_app[data_app_offset], rounded_size));
data_app_offset += rounded_size; data_app_offset += rounded_size;
continue;
} }
std::string filename;
if (content.m_Type & 0x8000) // shared app
filename = shared_content.GetFilenameFromSHA1(content.m_SHA1Hash);
else else
filename = StringFromFormat("%s/%08x.app", m_Path.c_str(), content.m_ContentID);
content.m_Data = std::make_unique<CNANDContentDataFile>(filename);
// Be graceful about incorrect TMDs.
if (File::Exists(filename))
content.m_Size = static_cast<u32>(File::GetSize(filename));
}
}
DiscIO::Region CNANDContentLoader::GetRegion() const
{ {
if (!IsValid()) std::string filename;
return DiscIO::Region::UNKNOWN_REGION; 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);
return RegionSwitchWii(m_Country); m_Content[i].m_Data = std::make_unique<CNANDContentDataFile>(filename);
}
m_Content[i].m_metadata = std::move(content);
}
} }
CNANDContentManager::~CNANDContentManager() CNANDContentManager::~CNANDContentManager()
@ -327,18 +296,18 @@ void CNANDContentManager::ClearCache()
void CNANDContentLoader::RemoveTitle() const 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()) if (IsValid())
{ {
// remove TMD? // 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 = std::string path = StringFromFormat("%s/%08x.app", m_Path.c_str(), content.m_metadata.id);
StringFromFormat("%s/%08x.app", m_Path.c_str(), m_Content[i].m_ContentID); INFO_LOG(DISCIO, "Delete %s", path.c_str());
INFO_LOG(DISCIO, "Delete %s", filename.c_str()); File::Delete(path);
File::Delete(filename);
} }
} }
CNANDContentManager::Access().ClearCache(); // deletes 'this' CNANDContentManager::Access().ClearCache(); // deletes 'this'
@ -425,7 +394,7 @@ u64 CNANDContentManager::Install_WiiWAD(const std::string& filename)
if (content_loader.IsValid() == false) if (content_loader.IsValid() == false)
return 0; 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 // copy WAD's TMD header and contents to content directory
@ -440,20 +409,17 @@ u64 CNANDContentManager::Install_WiiWAD(const std::string& filename)
return 0; 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}; 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; std::string app_filename;
if (content.m_Type & 0x8000) // shared if (content.m_metadata.type & 0x8000) // shared
app_filename = shared_content.AddSharedContent(content.m_SHA1Hash); app_filename = shared_content.AddSharedContent(content.m_metadata.sha1.data());
else 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)) if (!File::Exists(app_filename))
{ {
@ -465,7 +431,7 @@ u64 CNANDContentManager::Install_WiiWAD(const std::string& filename)
return 0; 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 else
{ {
@ -488,7 +454,7 @@ u64 CNANDContentManager::Install_WiiWAD(const std::string& filename)
return title_id; return title_id;
} }
bool AddTicket(const ES::TicketReader& signed_ticket) bool AddTicket(const IOS::ES::TicketReader& signed_ticket)
{ {
if (!signed_ticket.IsValid()) 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()); 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); std::string ticket_filename = Common::GetTicketFileName(title_id, Common::FROM_CONFIGURED_ROOT);
File::IOFile ticket_file(ticket_filename, "rb"); File::IOFile ticket_file(ticket_filename, "rb");
if (!ticket_file) if (!ticket_file)
{ {
return ES::TicketReader{}; return IOS::ES::TicketReader{};
} }
std::vector<u8> signed_ticket(ticket_file.GetSize()); std::vector<u8> signed_ticket(ticket_file.GetSize());
if (!ticket_file.ReadBytes(signed_ticket.data(), signed_ticket.size())) 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 } // namespace end

View File

@ -23,8 +23,8 @@ namespace DiscIO
{ {
enum class Region; enum class Region;
bool AddTicket(const ES::TicketReader& signed_ticket); bool AddTicket(const IOS::ES::TicketReader& signed_ticket);
ES::TicketReader FindSignedTicket(u64 title_id); IOS::ES::TicketReader FindSignedTicket(u64 title_id);
class CNANDContentData class CNANDContentData
{ {
@ -66,13 +66,7 @@ private:
struct SNANDContent struct SNANDContent
{ {
u32 m_ContentID; IOS::ES::Content m_metadata;
u16 m_Index;
u16 m_Type;
u32 m_Size;
u8 m_SHA1Hash[20];
u8 m_Header[36]; // all of the above
std::unique_ptr<CNANDContentData> m_Data; std::unique_ptr<CNANDContentData> m_Data;
}; };
@ -85,42 +79,19 @@ public:
bool IsValid() const { return m_Valid; } bool IsValid() const { return m_Valid; }
void RemoveTitle() const; 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 SNANDContent* GetContentByIndex(int index) const;
const u8* GetTMDView() const { return m_TMDView; } const IOS::ES::TMDReader& GetTMD() const { return m_tmd; }
const u8* GetTMDHeader() const { return m_TMDHeader; } const IOS::ES::TicketReader& GetTicket() const { return m_ticket; }
const ES::TicketReader& GetTicket() const { return m_ticket; }
const std::vector<SNANDContent>& GetContent() const { return m_Content; } const std::vector<SNANDContent>& 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: private:
bool Initialize(const std::string& name); bool Initialize(const std::string& name);
void InitializeContentEntries(const std::vector<u8>& tmd, const std::vector<u8>& data_app); void InitializeContentEntries(const std::vector<u8>& data_app);
bool m_Valid; bool m_Valid = false;
bool m_IsWAD; bool m_IsWAD = false;
std::string m_Path; std::string m_Path;
u64 m_TitleID; IOS::ES::TMDReader m_tmd;
u16 m_IosVersion; IOS::ES::TicketReader m_ticket;
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;
std::vector<SNANDContent> m_Content; std::vector<SNANDContent> m_Content;
}; };

View File

@ -12,6 +12,7 @@
#include "Common/CommonFuncs.h" #include "Common/CommonFuncs.h"
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include "Common/StringUtil.h" #include "Common/StringUtil.h"
#include "Core/IOS/ES/Formats.h"
#include "DiscIO/Enums.h" #include "DiscIO/Enums.h"
namespace DiscIO namespace DiscIO
@ -36,7 +37,7 @@ public:
} }
virtual bool GetTitleID(u64*) const { return false; } virtual bool GetTitleID(u64*) const { return false; }
virtual std::vector<u8> GetTMD() const { return {}; } virtual IOS::ES::TMDReader GetTMD() const { return {}; }
virtual u64 PartitionOffsetToRawOffset(u64 offset) const { return offset; } virtual u64 PartitionOffsetToRawOffset(u64 offset) const { return offset; }
virtual std::string GetGameID() const = 0; virtual std::string GetGameID() const = 0;
virtual std::string GetMakerID() const = 0; virtual std::string GetMakerID() const = 0;

View File

@ -36,6 +36,16 @@ CVolumeWAD::CVolumeWAD(std::unique_ptr<IBlobReader> reader) : m_reader(std::move
Common::AlignUp(m_tick_size, 0x40); Common::AlignUp(m_tick_size, 0x40);
m_opening_bnr_offset = m_opening_bnr_offset =
m_tmd_offset + Common::AlignUp(m_tmd_size, 0x40) + Common::AlignUp(m_data_size, 0x40); 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<u8> 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() CVolumeWAD::~CVolumeWAD()
@ -55,40 +65,26 @@ bool CVolumeWAD::Read(u64 offset, u64 length, u8* buffer, bool decrypt) const
Region CVolumeWAD::GetRegion() const Region CVolumeWAD::GetRegion() const
{ {
u8 country_code; if (!m_tmd.IsValid())
if (!Read(m_tmd_offset + 0x0193, 1, &country_code))
return Region::UNKNOWN_REGION; return Region::UNKNOWN_REGION;
return m_tmd.GetRegion();
return RegionSwitchWii(country_code);
} }
Country CVolumeWAD::GetCountry() const Country CVolumeWAD::GetCountry() const
{ {
// read the last digit of the titleID in the ticket if (!m_tmd.IsValid())
u8 country_code;
if (!Read(m_tmd_offset + 0x0193, 1, &country_code))
return Country::COUNTRY_UNKNOWN; return Country::COUNTRY_UNKNOWN;
u8 country_code = static_cast<u8>(m_tmd.GetTitleId() & 0xff);
if (country_code == 2) // SYSMENU if (country_code == 2) // SYSMENU
{ country_code = GetSysMenuRegion(m_tmd.GetTitleVersion());
u16 title_version = 0;
Read(m_tmd_offset + 0x01dc, 2, (u8*)&title_version);
country_code = GetSysMenuRegion(Common::swap16(title_version));
}
return CountrySwitch(country_code); return CountrySwitch(country_code);
} }
std::vector<u8> CVolumeWAD::GetTMD() const IOS::ES::TMDReader CVolumeWAD::GetTMD() const
{ {
if (m_tmd_size > 1024 * 1024 * 4) return m_tmd;
{
ERROR_LOG(DISCIO, "TMD is too large: %u bytes", m_tmd_size);
return {};
}
std::vector<u8> buffer(m_tmd_size);
Read(m_tmd_offset, m_tmd_size, buffer.data(), false);
return buffer;
} }
std::string CVolumeWAD::GetGameID() const std::string CVolumeWAD::GetGameID() const
@ -125,11 +121,10 @@ bool CVolumeWAD::GetTitleID(u64* buffer) const
u16 CVolumeWAD::GetRevision() const u16 CVolumeWAD::GetRevision() const
{ {
u16 revision; if (!m_tmd.IsValid())
if (!m_reader->Read(m_tmd_offset + 0x1dc, 2, (u8*)&revision))
return 0; return 0;
return Common::swap16(revision); return m_tmd.GetTitleVersion();
} }
Platform CVolumeWAD::GetVolumeType() const Platform CVolumeWAD::GetVolumeType() const

View File

@ -10,6 +10,7 @@
#include <vector> #include <vector>
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include "Core/IOS/ES/Formats.h"
#include "DiscIO/Volume.h" #include "DiscIO/Volume.h"
// --- this volume type is used for Wad files --- // --- this volume type is used for Wad files ---
@ -32,7 +33,7 @@ public:
~CVolumeWAD(); ~CVolumeWAD();
bool Read(u64 offset, u64 length, u8* buffer, bool decrypt = false) const override; bool Read(u64 offset, u64 length, u8* buffer, bool decrypt = false) const override;
bool GetTitleID(u64* buffer) const override; bool GetTitleID(u64* buffer) const override;
std::vector<u8> GetTMD() const override; IOS::ES::TMDReader GetTMD() const override;
std::string GetGameID() const override; std::string GetGameID() const override;
std::string GetMakerID() const override; std::string GetMakerID() const override;
u16 GetRevision() const override; u16 GetRevision() const override;
@ -51,6 +52,7 @@ public:
private: private:
std::unique_ptr<IBlobReader> m_reader; std::unique_ptr<IBlobReader> m_reader;
IOS::ES::TMDReader m_tmd;
u32 m_offset = 0; u32 m_offset = 0;
u32 m_tmd_offset = 0; u32 m_tmd_offset = 0;
u32 m_opening_bnr_offset = 0; u32 m_opening_bnr_offset = 0;

View File

@ -114,7 +114,7 @@ bool CVolumeWiiCrypted::GetTitleID(u64* buffer) const
return true; return true;
} }
std::vector<u8> CVolumeWiiCrypted::GetTMD() const IOS::ES::TMDReader CVolumeWiiCrypted::GetTMD() const
{ {
u32 tmd_size; u32 tmd_size;
u32 tmd_address; u32 tmd_address;
@ -137,7 +137,7 @@ std::vector<u8> CVolumeWiiCrypted::GetTMD() const
std::vector<u8> buffer(tmd_size); std::vector<u8> buffer(tmd_size);
Read(m_VolumeOffset + tmd_address, tmd_size, buffer.data(), false); 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 u64 CVolumeWiiCrypted::PartitionOffsetToRawOffset(u64 offset) const

View File

@ -11,6 +11,7 @@
#include <vector> #include <vector>
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include "Core/IOS/ES/Formats.h"
#include "DiscIO/Volume.h" #include "DiscIO/Volume.h"
// --- this volume type is used for encrypted Wii images --- // --- this volume type is used for encrypted Wii images ---
@ -32,7 +33,7 @@ public:
~CVolumeWiiCrypted(); ~CVolumeWiiCrypted();
bool Read(u64 _Offset, u64 _Length, u8* _pBuffer, bool decrypt) const override; bool Read(u64 _Offset, u64 _Length, u8* _pBuffer, bool decrypt) const override;
bool GetTitleID(u64* buffer) const override; bool GetTitleID(u64* buffer) const override;
std::vector<u8> GetTMD() const override; IOS::ES::TMDReader GetTMD() const override;
u64 PartitionOffsetToRawOffset(u64 offset) const override; u64 PartitionOffsetToRawOffset(u64 offset) const override;
std::string GetGameID() const override; std::string GetGameID() const override;
std::string GetMakerID() const override; std::string GetMakerID() const override;

View File

@ -90,7 +90,7 @@ bool WiiWAD::ParseWAD(IBlobReader& reader)
offset += Common::AlignUp(certificate_chain_size, 0x40); offset += Common::AlignUp(certificate_chain_size, 0x40);
m_ticket.SetBytes(CreateWADEntry(reader, ticket_size, offset)); m_ticket.SetBytes(CreateWADEntry(reader, ticket_size, offset));
offset += Common::AlignUp(ticket_size, 0x40); 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); offset += Common::AlignUp(tmd_size, 0x40);
m_data_app = CreateWADEntry(reader, data_app_size, offset); m_data_app = CreateWADEntry(reader, data_app_size, offset);
offset += Common::AlignUp(data_app_size, 0x40); offset += Common::AlignUp(data_app_size, 0x40);

View File

@ -23,8 +23,8 @@ public:
bool IsValid() const { return m_valid; } bool IsValid() const { return m_valid; }
const std::vector<u8>& GetCertificateChain() const { return m_certificate_chain; } const std::vector<u8>& GetCertificateChain() const { return m_certificate_chain; }
const ES::TicketReader& GetTicket() const { return m_ticket; } const IOS::ES::TicketReader& GetTicket() const { return m_ticket; }
const std::vector<u8>& GetTMD() const { return m_tmd; } const IOS::ES::TMDReader& GetTMD() const { return m_tmd; }
const std::vector<u8>& GetDataApp() const { return m_data_app; } const std::vector<u8>& GetDataApp() const { return m_data_app; }
const std::vector<u8>& GetFooter() const { return m_footer; } const std::vector<u8>& GetFooter() const { return m_footer; }
private: private:
@ -33,8 +33,8 @@ private:
bool m_valid; bool m_valid;
std::vector<u8> m_certificate_chain; std::vector<u8> m_certificate_chain;
ES::TicketReader m_ticket; IOS::ES::TicketReader m_ticket;
std::vector<u8> m_tmd; IOS::ES::TMDReader m_tmd;
std::vector<u8> m_data_app; std::vector<u8> m_data_app;
std::vector<u8> m_footer; std::vector<u8> m_footer;
}; };

View File

@ -1225,23 +1225,7 @@ void CFrame::OnInstallWAD(wxCommandEvent& event)
void CFrame::UpdateLoadWiiMenuItem() const void CFrame::UpdateLoadWiiMenuItem() const
{ {
auto* const menu_item = GetMenuBar()->FindItem(IDM_LOAD_WII_MENU); GetMenuBar()->Refresh(true, nullptr);
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"));
}
} }
void CFrame::OnFifoPlayer(wxCommandEvent& WXUNUSED(event)) void CFrame::OnFifoPlayer(wxCommandEvent& WXUNUSED(event))

View File

@ -182,7 +182,7 @@ void InfoPanel::LoadISODetails()
m_fst->SetValue(StrToWxStr(std::to_string(m_opened_iso->GetFSTSize()))); m_fst->SetValue(StrToWxStr(std::to_string(m_opened_iso->GetFSTSize())));
if (m_ios_version) if (m_ios_version)
{ {
ES::TMDReader tmd{m_opened_iso->GetTMD()}; const IOS::ES::TMDReader tmd = m_opened_iso->GetTMD();
if (tmd.IsValid()) if (tmd.IsValid())
m_ios_version->SetValue(StringFromFormat("IOS%u", static_cast<u32>(tmd.GetIOSId()))); m_ios_version->SetValue(StringFromFormat("IOS%u", static_cast<u32>(tmd.GetIOSId())));
} }
@ -223,7 +223,7 @@ wxStaticBoxSizer* InfoPanel::CreateISODetailsSizer()
{_("Apploader Date:"), m_date}, {_("Apploader Date:"), m_date},
{_("FST Size:"), m_fst}, {_("FST Size:"), m_fst},
}}; }};
if (!m_opened_iso->GetTMD().empty()) if (m_opened_iso->GetTMD().IsValid())
controls.emplace_back(_("IOS Version:"), m_ios_version); controls.emplace_back(_("IOS Version:"), m_ios_version);
const int space_10 = FromDIP(10); const int space_10 = FromDIP(10);

View File

@ -12,6 +12,7 @@
#include "Core/Core.h" #include "Core/Core.h"
#include "Core/PowerPC/PowerPC.h" #include "Core/PowerPC/PowerPC.h"
#include "Core/State.h" #include "Core/State.h"
#include "DiscIO/Enums.h"
#include "DiscIO/NANDContentLoader.h" #include "DiscIO/NANDContentLoader.h"
#include "DolphinWX/Globals.h" #include "DolphinWX/Globals.h"
#include "DolphinWX/WxUtils.h" #include "DolphinWX/WxUtils.h"
@ -546,8 +547,8 @@ void MainMenuBar::RefreshWiiSystemMenuLabel() const
if (sys_menu_loader.IsValid()) if (sys_menu_loader.IsValid())
{ {
const auto sys_menu_version = sys_menu_loader.GetTitleVersion(); const u16 sys_menu_version = sys_menu_loader.GetTMD().GetTitleVersion();
const auto sys_menu_region = sys_menu_loader.GetCountryChar(); const char sys_menu_region = DiscIO::GetSysMenuRegion(sys_menu_version);
item->Enable(); item->Enable();
item->SetItemLabel( item->SetItemLabel(
wxString::Format(_("Load Wii System Menu %u%c"), sys_menu_version, sys_menu_region)); wxString::Format(_("Load Wii System Menu %u%c"), sys_menu_version, sys_menu_region));