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

View File

@ -346,11 +346,9 @@ bool CBoot::EmulatedBS2_Wii()
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_reader.GetIOSId()))
if (!SetupWiiMemory(tmd.GetIOSId()))
return false;
// Execute the apploader

View File

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

View File

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

View File

@ -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<u8> 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<u8> raw_tmd = tmd.GetRawTMD();
Memory::CopyToEmu(request.io_vectors[0].address, raw_tmd.data(), raw_tmd.size());
ES_DIVerify(tmd);
return_value = 1;
break;

View File

@ -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<u32>(pContent->m_metadata.size);
Access.m_TitleID = TitleID;
pContent->m_Data->Open();
@ -319,7 +319,7 @@ IPCCommandResult ES::AddTicket(const IOCtlVRequest& request)
std::vector<u8> 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<s32>(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<s32>(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<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());
}
}
@ -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<u32>(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<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);
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);
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<u32>(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<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);
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);
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<u8>& 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<u8>& tmd)
if (!File::Exists(tmd_path))
{
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.");
}
DiscIO::cUIDsys uid_sys{Common::FromWhichRoot::FROM_SESSION_ROOT};

View File

@ -46,7 +46,7 @@ public:
void Close() 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
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<u8> m_addtitle_content_buffer;
};

View File

@ -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<u8> 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<u8>&& 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<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
{
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
{
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<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
{
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);
}
} // namespace ES
} // namespace IOS

View File

@ -12,45 +12,50 @@
#include "Common/ChunkFile.h"
#include "Common/CommonTypes.h"
#include "DiscIO/Enums.h"
namespace IOS
{
namespace ES
{
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;
u64 GetIOSId() const;
u64 GetTitleId() const;
struct Content
{
u32 id;
u16 index;
u16 type;
u64 size;
std::array<u8, 20> 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<u8> 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<u8, 20> 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<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
{
public:
@ -125,3 +166,4 @@ private:
std::vector<u8> m_bytes;
};
} // namespace ES
} // namespace IOS

View File

@ -658,7 +658,7 @@ void SetDefaultContentFile(const std::string& 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);
}

View File

@ -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<u8>& tmd);
void ES_DIVerify(const ES::TMDReader& tmd);
void SDIO_EventNotify();

View File

@ -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<u8> GetMIOSBinary()
if (!loader.IsValid())
return {};
const auto* content = loader.GetContentByIndex(loader.GetBootIndex());
const auto* content = loader.GetContentByIndex(loader.GetTMD().GetBootIndex());
if (!content)
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.
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);

View File

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

View File

@ -4,6 +4,7 @@
#include <algorithm>
#include <array>
#include <cinttypes>
#include <cstddef>
#include <cstdio>
#include <cstring>
@ -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<u8> data_app;
std::vector<u8> 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<size_t>(File::GetSize(tmd_filename)));
tmd_file.ReadBytes(tmd.data(), tmd.size());
std::vector<u8> 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<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);
InitializeContentEntries(data_app);
return true;
}
void CNANDContentLoader::InitializeContentEntries(const std::vector<u8>& tmd,
const std::vector<u8>& data_app)
void CNANDContentLoader::InitializeContentEntries(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;
const std::vector<u8> 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<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);
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<u8, 16> iv{};
iv[0] = static_cast<u8>(content.index >> 8) & 0xFF;
iv[1] = static_cast<u8>(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<CNANDContentDataBuffer>(ES::AESDecode(
m_ticket.GetTitleKey().data(), iv.data(), &data_app[data_app_offset], rounded_size));
u32 rounded_size = Common::AlignUp(static_cast<u32>(content.size), 0x40);
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;
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<CNANDContentDataFile>(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<CNANDContentDataFile>(filename);
// Be graceful about incorrect TMDs.
if (File::Exists(filename))
content.m_Size = static_cast<u32>(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<u8> 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

View File

@ -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<CNANDContentData> 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<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:
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_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<SNANDContent> m_Content;
};

View File

@ -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<u8> 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;

View File

@ -36,6 +36,16 @@ CVolumeWAD::CVolumeWAD(std::unique_ptr<IBlobReader> 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<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()
@ -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<u8>(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<u8> 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<u8> 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

View File

@ -10,6 +10,7 @@
#include <vector>
#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<u8> 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<IBlobReader> m_reader;
IOS::ES::TMDReader m_tmd;
u32 m_offset = 0;
u32 m_tmd_offset = 0;
u32 m_opening_bnr_offset = 0;

View File

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

View File

@ -11,6 +11,7 @@
#include <vector>
#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<u8> 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;

View File

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

View File

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

View File

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

View File

@ -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<u32>(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);

View File

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