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:
parent
bf1f70db0a
commit
c1a139e8ac
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
|
||||||
{
|
|
||||||
NumberOfPrivateContent = rNANDContent.GetNumEntries();
|
|
||||||
|
|
||||||
if ((u32)(TitleID >> 32) == 0x00010000)
|
const u16 num_contents = nand_content.GetTMD().GetNumContents();
|
||||||
Memory::Write_U32(0, request.io_vectors[0].address);
|
|
||||||
else
|
if ((u32)(TitleID >> 32) == 0x00010000)
|
||||||
Memory::Write_U32(NumberOfPrivateContent, request.io_vectors[0].address);
|
Memory::Write_U32(0, request.io_vectors[0].address);
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
Memory::Write_U32(num_contents, request.io_vectors[0].address);
|
||||||
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};
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -12,45 +12,50 @@
|
||||||
|
|
||||||
#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);
|
|
||||||
|
|
||||||
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)
|
#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
|
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
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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 {};
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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,94 +200,64 @@ 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;
|
}
|
||||||
|
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;
|
m_Content[i].m_metadata = std::move(content);
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DiscIO::Region CNANDContentLoader::GetRegion() const
|
|
||||||
{
|
|
||||||
if (!IsValid())
|
|
||||||
return DiscIO::Region::UNKNOWN_REGION;
|
|
||||||
|
|
||||||
return RegionSwitchWii(m_Country);
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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));
|
||||||
|
|
Loading…
Reference in New Issue