Merge pull request #5085 from leoetlino/es-nandutils
IOS: Add NandUtils
This commit is contained in:
commit
910360f7e7
|
@ -150,6 +150,7 @@ set(SRCS
|
||||||
IOS/ES/ES.cpp
|
IOS/ES/ES.cpp
|
||||||
IOS/ES/Formats.cpp
|
IOS/ES/Formats.cpp
|
||||||
IOS/ES/Identity.cpp
|
IOS/ES/Identity.cpp
|
||||||
|
IOS/ES/NandUtils.cpp
|
||||||
IOS/ES/TitleContents.cpp
|
IOS/ES/TitleContents.cpp
|
||||||
IOS/ES/TitleInformation.cpp
|
IOS/ES/TitleInformation.cpp
|
||||||
IOS/ES/TitleManagement.cpp
|
IOS/ES/TitleManagement.cpp
|
||||||
|
|
|
@ -177,6 +177,7 @@
|
||||||
<ClCompile Include="IOS\ES\ES.cpp" />
|
<ClCompile Include="IOS\ES\ES.cpp" />
|
||||||
<ClCompile Include="IOS\ES\Formats.cpp" />
|
<ClCompile Include="IOS\ES\Formats.cpp" />
|
||||||
<ClCompile Include="IOS\ES\Identity.cpp" />
|
<ClCompile Include="IOS\ES\Identity.cpp" />
|
||||||
|
<ClCompile Include="IOS\ES\NandUtils.cpp" />
|
||||||
<ClCompile Include="IOS\ES\TitleContents.cpp" />
|
<ClCompile Include="IOS\ES\TitleContents.cpp" />
|
||||||
<ClCompile Include="IOS\ES\TitleInformation.cpp" />
|
<ClCompile Include="IOS\ES\TitleInformation.cpp" />
|
||||||
<ClCompile Include="IOS\ES\TitleManagement.cpp" />
|
<ClCompile Include="IOS\ES\TitleManagement.cpp" />
|
||||||
|
@ -427,6 +428,7 @@
|
||||||
<ClInclude Include="IOS\DI\DI.h" />
|
<ClInclude Include="IOS\DI\DI.h" />
|
||||||
<ClInclude Include="IOS\ES\ES.h" />
|
<ClInclude Include="IOS\ES\ES.h" />
|
||||||
<ClInclude Include="IOS\ES\Formats.h" />
|
<ClInclude Include="IOS\ES\Formats.h" />
|
||||||
|
<ClInclude Include="IOS\ES\NandUtils.h" />
|
||||||
<ClInclude Include="IOS\FS\FileIO.h" />
|
<ClInclude Include="IOS\FS\FileIO.h" />
|
||||||
<ClInclude Include="IOS\FS\FS.h" />
|
<ClInclude Include="IOS\FS\FS.h" />
|
||||||
<ClInclude Include="IOS\Network\ICMPLin.h" />
|
<ClInclude Include="IOS\Network\ICMPLin.h" />
|
||||||
|
|
|
@ -759,6 +759,9 @@
|
||||||
<ClCompile Include="IOS\ES\Identity.cpp">
|
<ClCompile Include="IOS\ES\Identity.cpp">
|
||||||
<Filter>IOS\ES</Filter>
|
<Filter>IOS\ES</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="IOS\ES\NandUtils.cpp">
|
||||||
|
<Filter>IOS\ES</Filter>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="IOS\ES\TitleContents.cpp">
|
<ClCompile Include="IOS\ES\TitleContents.cpp">
|
||||||
<Filter>IOS\ES</Filter>
|
<Filter>IOS\ES</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
@ -1370,6 +1373,9 @@
|
||||||
<ClInclude Include="IOS\ES\ES.h">
|
<ClInclude Include="IOS\ES\ES.h">
|
||||||
<Filter>IOS\ES</Filter>
|
<Filter>IOS\ES</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="IOS\ES\NandUtils.h">
|
||||||
|
<Filter>IOS\ES</Filter>
|
||||||
|
</ClInclude>
|
||||||
<ClInclude Include="IOS\FS\FileIO.h">
|
<ClInclude Include="IOS\FS\FileIO.h">
|
||||||
<Filter>IOS\FS</Filter>
|
<Filter>IOS\FS</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
|
|
@ -0,0 +1,165 @@
|
||||||
|
// Copyright 2017 Dolphin Emulator Project
|
||||||
|
// Licensed under GPLv2+
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cctype>
|
||||||
|
#include <iterator>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "Common/CommonTypes.h"
|
||||||
|
#include "Common/FileUtil.h"
|
||||||
|
#include "Common/Logging/Log.h"
|
||||||
|
#include "Common/NandPaths.h"
|
||||||
|
#include "Common/StringUtil.h"
|
||||||
|
#include "Core/IOS/ES/Formats.h"
|
||||||
|
#include "Core/IOS/ES/NandUtils.h"
|
||||||
|
#include "DiscIO/NANDContentLoader.h"
|
||||||
|
|
||||||
|
namespace IOS
|
||||||
|
{
|
||||||
|
namespace ES
|
||||||
|
{
|
||||||
|
static TMDReader FindTMD(u64 title_id, const std::string& tmd_path)
|
||||||
|
{
|
||||||
|
File::IOFile file(tmd_path, "rb");
|
||||||
|
if (!file)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
std::vector<u8> tmd_bytes(file.GetSize());
|
||||||
|
if (!file.ReadBytes(tmd_bytes.data(), tmd_bytes.size()))
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return TMDReader{std::move(tmd_bytes)};
|
||||||
|
}
|
||||||
|
|
||||||
|
TMDReader FindImportTMD(u64 title_id)
|
||||||
|
{
|
||||||
|
return FindTMD(title_id, Common::GetImportTitlePath(title_id) + "/content/title.tmd");
|
||||||
|
}
|
||||||
|
|
||||||
|
TMDReader FindInstalledTMD(u64 title_id)
|
||||||
|
{
|
||||||
|
return FindTMD(title_id, Common::GetTMDFileName(title_id, Common::FROM_SESSION_ROOT));
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool IsValidPartOfTitleID(const std::string& string)
|
||||||
|
{
|
||||||
|
if (string.length() != 8)
|
||||||
|
return false;
|
||||||
|
return std::all_of(string.begin(), string.end(),
|
||||||
|
[](const auto character) { return std::isxdigit(character) != 0; });
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::vector<u64> GetTitlesInTitleOrImport(const std::string& titles_dir)
|
||||||
|
{
|
||||||
|
if (!File::IsDirectory(titles_dir))
|
||||||
|
{
|
||||||
|
ERROR_LOG(IOS_ES, "%s is not a directory", titles_dir.c_str());
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<u64> title_ids;
|
||||||
|
|
||||||
|
// The /title and /import directories contain one directory per title type, and each of them has
|
||||||
|
// a directory per title (where the name is the low 32 bits of the title ID in %08x format).
|
||||||
|
const auto entries = File::ScanDirectoryTree(titles_dir, true);
|
||||||
|
for (const File::FSTEntry& title_type : entries.children)
|
||||||
|
{
|
||||||
|
if (!title_type.isDirectory || !IsValidPartOfTitleID(title_type.virtualName))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (title_type.children.empty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (const File::FSTEntry& title_identifier : title_type.children)
|
||||||
|
{
|
||||||
|
if (!title_identifier.isDirectory || !IsValidPartOfTitleID(title_identifier.virtualName))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const u32 type = std::stoul(title_type.virtualName, nullptr, 16);
|
||||||
|
const u32 identifier = std::stoul(title_identifier.virtualName, nullptr, 16);
|
||||||
|
title_ids.push_back(static_cast<u64>(type) << 32 | identifier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return title_ids;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<u64> GetInstalledTitles()
|
||||||
|
{
|
||||||
|
return GetTitlesInTitleOrImport(Common::RootUserPath(Common::FROM_SESSION_ROOT) + "/title");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<u64> GetTitleImports()
|
||||||
|
{
|
||||||
|
return GetTitlesInTitleOrImport(Common::RootUserPath(Common::FROM_SESSION_ROOT) + "/import");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<u64> GetTitlesWithTickets()
|
||||||
|
{
|
||||||
|
const std::string tickets_dir = Common::RootUserPath(Common::FROM_SESSION_ROOT) + "/ticket";
|
||||||
|
if (!File::IsDirectory(tickets_dir))
|
||||||
|
{
|
||||||
|
ERROR_LOG(IOS_ES, "/ticket is not a directory");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<u64> title_ids;
|
||||||
|
|
||||||
|
// The /ticket directory contains one directory per title type, and each of them contains
|
||||||
|
// one ticket per title (where the name is the low 32 bits of the title ID in %08x format).
|
||||||
|
const auto entries = File::ScanDirectoryTree(tickets_dir, true);
|
||||||
|
for (const File::FSTEntry& title_type : entries.children)
|
||||||
|
{
|
||||||
|
if (!title_type.isDirectory || !IsValidPartOfTitleID(title_type.virtualName))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (title_type.children.empty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (const File::FSTEntry& ticket : title_type.children)
|
||||||
|
{
|
||||||
|
const std::string name_without_ext = ticket.virtualName.substr(0, 8);
|
||||||
|
if (ticket.isDirectory || !IsValidPartOfTitleID(name_without_ext) ||
|
||||||
|
name_without_ext + ".tik" != ticket.virtualName)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const u32 type = std::stoul(title_type.virtualName, nullptr, 16);
|
||||||
|
const u32 identifier = std::stoul(name_without_ext, nullptr, 16);
|
||||||
|
title_ids.push_back(static_cast<u64>(type) << 32 | identifier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return title_ids;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Content> GetStoredContentsFromTMD(const TMDReader& tmd)
|
||||||
|
{
|
||||||
|
if (!tmd.IsValid())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
const DiscIO::CSharedContent shared{Common::FROM_SESSION_ROOT};
|
||||||
|
const std::vector<Content> contents = tmd.GetContents();
|
||||||
|
|
||||||
|
std::vector<Content> stored_contents;
|
||||||
|
|
||||||
|
std::copy_if(contents.begin(), contents.end(), std::back_inserter(stored_contents),
|
||||||
|
[&tmd, &shared](const auto& content) {
|
||||||
|
if (content.IsShared())
|
||||||
|
{
|
||||||
|
const std::string path = shared.GetFilenameFromSHA1(content.sha1.data());
|
||||||
|
return path != "unk" && File::Exists(path);
|
||||||
|
}
|
||||||
|
return File::Exists(
|
||||||
|
Common::GetTitleContentPath(tmd.GetTitleId(), Common::FROM_SESSION_ROOT) +
|
||||||
|
StringFromFormat("%08x.app", content.id));
|
||||||
|
});
|
||||||
|
|
||||||
|
return stored_contents;
|
||||||
|
}
|
||||||
|
} // namespace ES
|
||||||
|
} // namespace IOS
|
|
@ -0,0 +1,30 @@
|
||||||
|
// Copyright 2017 Dolphin Emulator Project
|
||||||
|
// Licensed under GPLv2+
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "Common/CommonTypes.h"
|
||||||
|
|
||||||
|
namespace IOS
|
||||||
|
{
|
||||||
|
namespace ES
|
||||||
|
{
|
||||||
|
struct Content;
|
||||||
|
class TMDReader;
|
||||||
|
|
||||||
|
TMDReader FindImportTMD(u64 title_id);
|
||||||
|
TMDReader FindInstalledTMD(u64 title_id);
|
||||||
|
|
||||||
|
// Get installed titles (in /title) without checking for TMDs at all.
|
||||||
|
std::vector<u64> GetInstalledTitles();
|
||||||
|
// Get titles which are being imported (in /import) without checking for TMDs at all.
|
||||||
|
std::vector<u64> GetTitleImports();
|
||||||
|
// Get titles for which there is a ticket (in /ticket).
|
||||||
|
std::vector<u64> GetTitlesWithTickets();
|
||||||
|
|
||||||
|
std::vector<Content> GetStoredContentsFromTMD(const TMDReader& tmd);
|
||||||
|
} // namespace ES
|
||||||
|
} // namespace IOS
|
|
@ -4,11 +4,8 @@
|
||||||
|
|
||||||
#include "Core/IOS/ES/ES.h"
|
#include "Core/IOS/ES/ES.h"
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cctype>
|
|
||||||
#include <cinttypes>
|
#include <cinttypes>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <iterator>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
@ -18,6 +15,7 @@
|
||||||
#include "Common/StringUtil.h"
|
#include "Common/StringUtil.h"
|
||||||
#include "Core/HW/Memmap.h"
|
#include "Core/HW/Memmap.h"
|
||||||
#include "Core/IOS/ES/Formats.h"
|
#include "Core/IOS/ES/Formats.h"
|
||||||
|
#include "Core/IOS/ES/NandUtils.h"
|
||||||
#include "DiscIO/NANDContentLoader.h"
|
#include "DiscIO/NANDContentLoader.h"
|
||||||
|
|
||||||
namespace IOS
|
namespace IOS
|
||||||
|
@ -26,31 +24,6 @@ namespace HLE
|
||||||
{
|
{
|
||||||
namespace Device
|
namespace Device
|
||||||
{
|
{
|
||||||
static std::vector<IOS::ES::Content> GetStoredContentsFromTMD(const IOS::ES::TMDReader& tmd)
|
|
||||||
{
|
|
||||||
if (!tmd.IsValid())
|
|
||||||
return {};
|
|
||||||
|
|
||||||
const DiscIO::CSharedContent shared{Common::FROM_SESSION_ROOT};
|
|
||||||
const std::vector<IOS::ES::Content> contents = tmd.GetContents();
|
|
||||||
|
|
||||||
std::vector<IOS::ES::Content> stored_contents;
|
|
||||||
|
|
||||||
std::copy_if(contents.begin(), contents.end(), std::back_inserter(stored_contents),
|
|
||||||
[&tmd, &shared](const auto& content) {
|
|
||||||
if (content.IsShared())
|
|
||||||
{
|
|
||||||
const std::string path = shared.GetFilenameFromSHA1(content.sha1.data());
|
|
||||||
return path != "unk" && File::Exists(path);
|
|
||||||
}
|
|
||||||
return File::Exists(
|
|
||||||
Common::GetTitleContentPath(tmd.GetTitleId(), Common::FROM_SESSION_ROOT) +
|
|
||||||
StringFromFormat("%08x.app", content.id));
|
|
||||||
});
|
|
||||||
|
|
||||||
return stored_contents;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Used by the GetStoredContents ioctlvs. This assumes that the first output vector
|
// Used by the GetStoredContents ioctlvs. This assumes that the first output vector
|
||||||
// is used for the content count (u32).
|
// is used for the content count (u32).
|
||||||
IPCCommandResult ES::GetStoredContentsCount(const IOS::ES::TMDReader& tmd,
|
IPCCommandResult ES::GetStoredContentsCount(const IOS::ES::TMDReader& tmd,
|
||||||
|
@ -59,7 +32,7 @@ IPCCommandResult ES::GetStoredContentsCount(const IOS::ES::TMDReader& tmd,
|
||||||
if (request.io_vectors[0].size != sizeof(u32) || !tmd.IsValid())
|
if (request.io_vectors[0].size != sizeof(u32) || !tmd.IsValid())
|
||||||
return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
|
return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
|
||||||
|
|
||||||
const u16 num_contents = static_cast<u16>(GetStoredContentsFromTMD(tmd).size());
|
const u16 num_contents = static_cast<u16>(IOS::ES::GetStoredContentsFromTMD(tmd).size());
|
||||||
Memory::Write_U32(num_contents, request.io_vectors[0].address);
|
Memory::Write_U32(num_contents, request.io_vectors[0].address);
|
||||||
|
|
||||||
INFO_LOG(IOS_ES, "GetStoredContentsCount (0x%x): %u content(s) for %016" PRIx64, request.request,
|
INFO_LOG(IOS_ES, "GetStoredContentsCount (0x%x): %u content(s) for %016" PRIx64, request.request,
|
||||||
|
@ -80,7 +53,7 @@ IPCCommandResult ES::GetStoredContents(const IOS::ES::TMDReader& tmd, const IOCt
|
||||||
return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
|
return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto contents = GetStoredContentsFromTMD(tmd);
|
const auto contents = IOS::ES::GetStoredContentsFromTMD(tmd);
|
||||||
const u32 max_content_count = Memory::Read_U32(request.in_vectors[1].address);
|
const u32 max_content_count = Memory::Read_U32(request.in_vectors[1].address);
|
||||||
for (u32 i = 0; i < std::min(static_cast<u32>(contents.size()), max_content_count); ++i)
|
for (u32 i = 0; i < std::min(static_cast<u32>(contents.size()), max_content_count); ++i)
|
||||||
Memory::Write_U32(contents[i].id, request.io_vectors[0].address + i * sizeof(u32));
|
Memory::Write_U32(contents[i].id, request.io_vectors[0].address + i * sizeof(u32));
|
||||||
|
@ -94,10 +67,10 @@ IPCCommandResult ES::GetStoredContentsCount(const IOCtlVRequest& request)
|
||||||
return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
|
return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
|
||||||
|
|
||||||
const u64 title_id = Memory::Read_U64(request.in_vectors[0].address);
|
const u64 title_id = Memory::Read_U64(request.in_vectors[0].address);
|
||||||
const DiscIO::CNANDContentLoader& content_loader = AccessContentDevice(title_id);
|
const IOS::ES::TMDReader tmd = IOS::ES::FindInstalledTMD(title_id);
|
||||||
if (!content_loader.IsValid())
|
if (!tmd.IsValid())
|
||||||
return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
|
return GetDefaultReply(FS_ENOENT);
|
||||||
return GetStoredContentsCount(content_loader.GetTMD(), request);
|
return GetStoredContentsCount(tmd, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
IPCCommandResult ES::GetStoredContents(const IOCtlVRequest& request)
|
IPCCommandResult ES::GetStoredContents(const IOCtlVRequest& request)
|
||||||
|
@ -106,10 +79,10 @@ IPCCommandResult ES::GetStoredContents(const IOCtlVRequest& request)
|
||||||
return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
|
return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
|
||||||
|
|
||||||
const u64 title_id = Memory::Read_U64(request.in_vectors[0].address);
|
const u64 title_id = Memory::Read_U64(request.in_vectors[0].address);
|
||||||
const DiscIO::CNANDContentLoader& content_loader = AccessContentDevice(title_id);
|
const IOS::ES::TMDReader tmd = IOS::ES::FindInstalledTMD(title_id);
|
||||||
if (!content_loader.IsValid())
|
if (!tmd.IsValid())
|
||||||
return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
|
return GetDefaultReply(FS_ENOENT);
|
||||||
return GetStoredContents(content_loader.GetTMD(), request);
|
return GetStoredContents(tmd, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
IPCCommandResult ES::GetTMDStoredContentsCount(const IOCtlVRequest& request)
|
IPCCommandResult ES::GetTMDStoredContentsCount(const IOCtlVRequest& request)
|
||||||
|
@ -132,92 +105,6 @@ IPCCommandResult ES::GetTMDStoredContents(const IOCtlVRequest& request)
|
||||||
return GetStoredContents(IOS::ES::TMDReader{std::move(tmd_bytes)}, request);
|
return GetStoredContents(IOS::ES::TMDReader{std::move(tmd_bytes)}, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool IsValidPartOfTitleID(const std::string& string)
|
|
||||||
{
|
|
||||||
if (string.length() != 8)
|
|
||||||
return false;
|
|
||||||
return std::all_of(string.begin(), string.end(),
|
|
||||||
[](const auto character) { return std::isxdigit(character) != 0; });
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns a vector of title IDs. IOS does not check the TMD at all here.
|
|
||||||
static std::vector<u64> GetInstalledTitles()
|
|
||||||
{
|
|
||||||
const std::string titles_dir = Common::RootUserPath(Common::FROM_SESSION_ROOT) + "/title";
|
|
||||||
if (!File::IsDirectory(titles_dir))
|
|
||||||
{
|
|
||||||
ERROR_LOG(IOS_ES, "/title is not a directory");
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<u64> title_ids;
|
|
||||||
|
|
||||||
// The /title directory contains one directory per title type, and each of them contains
|
|
||||||
// a directory per title (where the name is the low 32 bits of the title ID in %08x format).
|
|
||||||
const auto entries = File::ScanDirectoryTree(titles_dir, true);
|
|
||||||
for (const File::FSTEntry& title_type : entries.children)
|
|
||||||
{
|
|
||||||
if (!title_type.isDirectory || !IsValidPartOfTitleID(title_type.virtualName))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (title_type.children.empty())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
for (const File::FSTEntry& title_identifier : title_type.children)
|
|
||||||
{
|
|
||||||
if (!title_identifier.isDirectory || !IsValidPartOfTitleID(title_identifier.virtualName))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
const u32 type = std::stoul(title_type.virtualName, nullptr, 16);
|
|
||||||
const u32 identifier = std::stoul(title_identifier.virtualName, nullptr, 16);
|
|
||||||
title_ids.push_back(static_cast<u64>(type) << 32 | identifier);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return title_ids;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns a vector of title IDs for which there is a ticket.
|
|
||||||
static std::vector<u64> GetTitlesWithTickets()
|
|
||||||
{
|
|
||||||
const std::string titles_dir = Common::RootUserPath(Common::FROM_SESSION_ROOT) + "/ticket";
|
|
||||||
if (!File::IsDirectory(titles_dir))
|
|
||||||
{
|
|
||||||
ERROR_LOG(IOS_ES, "/ticket is not a directory");
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<u64> title_ids;
|
|
||||||
|
|
||||||
// The /ticket directory contains one directory per title type, and each of them contains
|
|
||||||
// one ticket per title (where the name is the low 32 bits of the title ID in %08x format).
|
|
||||||
const auto entries = File::ScanDirectoryTree(titles_dir, true);
|
|
||||||
for (const File::FSTEntry& title_type : entries.children)
|
|
||||||
{
|
|
||||||
if (!title_type.isDirectory || !IsValidPartOfTitleID(title_type.virtualName))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (title_type.children.empty())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
for (const File::FSTEntry& ticket : title_type.children)
|
|
||||||
{
|
|
||||||
const std::string name_without_ext = ticket.virtualName.substr(0, 8);
|
|
||||||
if (ticket.isDirectory || !IsValidPartOfTitleID(name_without_ext) ||
|
|
||||||
name_without_ext + ".tik" != ticket.virtualName)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const u32 type = std::stoul(title_type.virtualName, nullptr, 16);
|
|
||||||
const u32 identifier = std::stoul(name_without_ext, nullptr, 16);
|
|
||||||
title_ids.push_back(static_cast<u64>(type) << 32 | identifier);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return title_ids;
|
|
||||||
}
|
|
||||||
|
|
||||||
IPCCommandResult ES::GetTitleCount(const std::vector<u64>& titles, const IOCtlVRequest& request)
|
IPCCommandResult ES::GetTitleCount(const std::vector<u64>& titles, const IOCtlVRequest& request)
|
||||||
{
|
{
|
||||||
if (!request.HasNumberOfValidVectors(0, 1) || request.io_vectors[0].size != 4)
|
if (!request.HasNumberOfValidVectors(0, 1) || request.io_vectors[0].size != 4)
|
||||||
|
@ -244,14 +131,14 @@ IPCCommandResult ES::GetTitles(const std::vector<u64>& titles, const IOCtlVReque
|
||||||
|
|
||||||
IPCCommandResult ES::GetTitleCount(const IOCtlVRequest& request)
|
IPCCommandResult ES::GetTitleCount(const IOCtlVRequest& request)
|
||||||
{
|
{
|
||||||
const std::vector<u64> titles = GetInstalledTitles();
|
const std::vector<u64> titles = IOS::ES::GetInstalledTitles();
|
||||||
INFO_LOG(IOS_ES, "GetTitleCount: %zu titles", titles.size());
|
INFO_LOG(IOS_ES, "GetTitleCount: %zu titles", titles.size());
|
||||||
return GetTitleCount(titles, request);
|
return GetTitleCount(titles, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
IPCCommandResult ES::GetTitles(const IOCtlVRequest& request)
|
IPCCommandResult ES::GetTitles(const IOCtlVRequest& request)
|
||||||
{
|
{
|
||||||
return GetTitles(GetInstalledTitles(), request);
|
return GetTitles(IOS::ES::GetInstalledTitles(), request);
|
||||||
}
|
}
|
||||||
|
|
||||||
IPCCommandResult ES::GetStoredTMDSize(const IOCtlVRequest& request)
|
IPCCommandResult ES::GetStoredTMDSize(const IOCtlVRequest& request)
|
||||||
|
@ -259,17 +146,15 @@ IPCCommandResult ES::GetStoredTMDSize(const IOCtlVRequest& request)
|
||||||
if (!request.HasNumberOfValidVectors(1, 1))
|
if (!request.HasNumberOfValidVectors(1, 1))
|
||||||
return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
|
return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
|
||||||
|
|
||||||
u64 TitleID = Memory::Read_U64(request.in_vectors[0].address);
|
const u64 title_id = Memory::Read_U64(request.in_vectors[0].address);
|
||||||
const DiscIO::CNANDContentLoader& Loader = AccessContentDevice(TitleID);
|
const IOS::ES::TMDReader tmd = IOS::ES::FindInstalledTMD(title_id);
|
||||||
|
if (!tmd.IsValid())
|
||||||
if (!Loader.IsValid() || !Loader.GetTMD().IsValid())
|
|
||||||
return GetDefaultReply(FS_ENOENT);
|
return GetDefaultReply(FS_ENOENT);
|
||||||
|
|
||||||
const u32 tmd_size = static_cast<u32>(Loader.GetTMD().GetRawTMD().size());
|
const u32 tmd_size = static_cast<u32>(tmd.GetRawTMD().size());
|
||||||
Memory::Write_U32(tmd_size, 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, "GetStoredTMDSize: %u bytes for %016" PRIx64, tmd_size, title_id);
|
||||||
(u32)(TitleID >> 32), (u32)TitleID, tmd_size);
|
|
||||||
|
|
||||||
return GetDefaultReply(IPC_SUCCESS);
|
return GetDefaultReply(IPC_SUCCESS);
|
||||||
}
|
}
|
||||||
|
@ -279,35 +164,34 @@ IPCCommandResult ES::GetStoredTMD(const IOCtlVRequest& request)
|
||||||
if (!request.HasNumberOfValidVectors(2, 1))
|
if (!request.HasNumberOfValidVectors(2, 1))
|
||||||
return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
|
return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
|
||||||
|
|
||||||
u64 TitleID = Memory::Read_U64(request.in_vectors[0].address);
|
const u64 title_id = Memory::Read_U64(request.in_vectors[0].address);
|
||||||
// TODO: actually use this param in when writing to the outbuffer :/
|
const IOS::ES::TMDReader tmd = IOS::ES::FindInstalledTMD(title_id);
|
||||||
const u32 MaxCount = Memory::Read_U32(request.in_vectors[1].address);
|
if (!tmd.IsValid())
|
||||||
const DiscIO::CNANDContentLoader& Loader = AccessContentDevice(TitleID);
|
|
||||||
|
|
||||||
if (!Loader.IsValid() || !Loader.GetTMD().IsValid())
|
|
||||||
return GetDefaultReply(FS_ENOENT);
|
return GetDefaultReply(FS_ENOENT);
|
||||||
|
|
||||||
const std::vector<u8> raw_tmd = Loader.GetTMD().GetRawTMD();
|
// TODO: actually use this param in when writing to the outbuffer :/
|
||||||
|
const u32 MaxCount = Memory::Read_U32(request.in_vectors[1].address);
|
||||||
|
|
||||||
|
const std::vector<u8> raw_tmd = tmd.GetRawTMD();
|
||||||
if (raw_tmd.size() != request.io_vectors[0].size)
|
if (raw_tmd.size() != request.io_vectors[0].size)
|
||||||
return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
|
return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
|
||||||
|
|
||||||
Memory::CopyToEmu(request.io_vectors[0].address, raw_tmd.data(), raw_tmd.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)",
|
INFO_LOG(IOS_ES, "GetStoredTMD: title %016" PRIx64 " (buffer size: %u)", title_id, MaxCount);
|
||||||
(u32)(TitleID >> 32), (u32)TitleID, MaxCount);
|
|
||||||
return GetDefaultReply(IPC_SUCCESS);
|
return GetDefaultReply(IPC_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
IPCCommandResult ES::GetOwnedTitleCount(const IOCtlVRequest& request)
|
IPCCommandResult ES::GetOwnedTitleCount(const IOCtlVRequest& request)
|
||||||
{
|
{
|
||||||
const std::vector<u64> titles = GetTitlesWithTickets();
|
const std::vector<u64> titles = IOS::ES::GetTitlesWithTickets();
|
||||||
INFO_LOG(IOS_ES, "GetOwnedTitleCount: %zu titles", titles.size());
|
INFO_LOG(IOS_ES, "GetOwnedTitleCount: %zu titles", titles.size());
|
||||||
return GetTitleCount(titles, request);
|
return GetTitleCount(titles, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
IPCCommandResult ES::GetOwnedTitles(const IOCtlVRequest& request)
|
IPCCommandResult ES::GetOwnedTitles(const IOCtlVRequest& request)
|
||||||
{
|
{
|
||||||
return GetTitles(GetTitlesWithTickets(), request);
|
return GetTitles(IOS::ES::GetTitlesWithTickets(), request);
|
||||||
}
|
}
|
||||||
|
|
||||||
IPCCommandResult ES::GetBoot2Version(const IOCtlVRequest& request)
|
IPCCommandResult ES::GetBoot2Version(const IOCtlVRequest& request)
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include "Common/StringUtil.h"
|
#include "Common/StringUtil.h"
|
||||||
#include "Core/HW/Memmap.h"
|
#include "Core/HW/Memmap.h"
|
||||||
#include "Core/IOS/ES/Formats.h"
|
#include "Core/IOS/ES/Formats.h"
|
||||||
|
#include "Core/IOS/ES/NandUtils.h"
|
||||||
#include "Core/ec_wii.h"
|
#include "Core/ec_wii.h"
|
||||||
#include "DiscIO/NANDContentLoader.h"
|
#include "DiscIO/NANDContentLoader.h"
|
||||||
|
|
||||||
|
@ -403,13 +404,11 @@ IPCCommandResult ES::ExportTitleInit(const IOCtlVRequest& request)
|
||||||
if (m_export_title_context.valid)
|
if (m_export_title_context.valid)
|
||||||
return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
|
return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
|
||||||
|
|
||||||
const auto& content_loader = AccessContentDevice(Memory::Read_U64(request.in_vectors[0].address));
|
const auto tmd = IOS::ES::FindInstalledTMD(Memory::Read_U64(request.in_vectors[0].address));
|
||||||
if (!content_loader.IsValid())
|
if (!tmd.IsValid())
|
||||||
return GetDefaultReply(FS_ENOENT);
|
return GetDefaultReply(FS_ENOENT);
|
||||||
if (!content_loader.GetTMD().IsValid())
|
|
||||||
return GetDefaultReply(ES_INVALID_TMD);
|
|
||||||
|
|
||||||
m_export_title_context.tmd = content_loader.GetTMD();
|
m_export_title_context.tmd = tmd;
|
||||||
|
|
||||||
const auto ticket = DiscIO::FindSignedTicket(m_export_title_context.tmd.GetTitleId());
|
const auto ticket = DiscIO::FindSignedTicket(m_export_title_context.tmd.GetTitleId());
|
||||||
if (!ticket.IsValid())
|
if (!ticket.IsValid())
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include "Core/ConfigManager.h"
|
#include "Core/ConfigManager.h"
|
||||||
#include "Core/HW/Memmap.h"
|
#include "Core/HW/Memmap.h"
|
||||||
#include "Core/IOS/ES/Formats.h"
|
#include "Core/IOS/ES/Formats.h"
|
||||||
|
#include "Core/IOS/ES/NandUtils.h"
|
||||||
#include "DiscIO/NANDContentLoader.h"
|
#include "DiscIO/NANDContentLoader.h"
|
||||||
|
|
||||||
namespace IOS
|
namespace IOS
|
||||||
|
@ -96,16 +97,15 @@ IPCCommandResult ES::GetTMDViewSize(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& Loader = AccessContentDevice(TitleID);
|
const IOS::ES::TMDReader tmd = IOS::ES::FindInstalledTMD(TitleID);
|
||||||
|
|
||||||
if (!Loader.IsValid())
|
if (!tmd.IsValid())
|
||||||
return GetDefaultReply(FS_ENOENT);
|
return GetDefaultReply(FS_ENOENT);
|
||||||
|
|
||||||
const u32 view_size = static_cast<u32>(Loader.GetTMD().GetRawView().size());
|
const u32 view_size = static_cast<u32>(tmd.GetRawView().size());
|
||||||
Memory::Write_U32(view_size, request.io_vectors[0].address);
|
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),
|
INFO_LOG(IOS_ES, "GetTMDViewSize: %u bytes for title %016" PRIx64, view_size, TitleID);
|
||||||
(u32)TitleID, view_size);
|
|
||||||
return GetDefaultReply(IPC_SUCCESS);
|
return GetDefaultReply(IPC_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,22 +117,18 @@ IPCCommandResult ES::GetTMDViews(const IOCtlVRequest& request)
|
||||||
u64 TitleID = Memory::Read_U64(request.in_vectors[0].address);
|
u64 TitleID = Memory::Read_U64(request.in_vectors[0].address);
|
||||||
u32 MaxCount = Memory::Read_U32(request.in_vectors[1].address);
|
u32 MaxCount = Memory::Read_U32(request.in_vectors[1].address);
|
||||||
|
|
||||||
const DiscIO::CNANDContentLoader& Loader = AccessContentDevice(TitleID);
|
const IOS::ES::TMDReader tmd = IOS::ES::FindInstalledTMD(TitleID);
|
||||||
|
|
||||||
INFO_LOG(IOS_ES, "IOCTL_ES_GETTMDVIEWCNT: title: %08x/%08x buffer size: %i",
|
if (!tmd.IsValid())
|
||||||
(u32)(TitleID >> 32), (u32)TitleID, MaxCount);
|
|
||||||
|
|
||||||
if (!Loader.IsValid())
|
|
||||||
return GetDefaultReply(FS_ENOENT);
|
return GetDefaultReply(FS_ENOENT);
|
||||||
|
|
||||||
const std::vector<u8> raw_view = Loader.GetTMD().GetRawView();
|
const std::vector<u8> raw_view = tmd.GetRawView();
|
||||||
if (raw_view.size() != request.io_vectors[0].size)
|
if (raw_view.size() != request.io_vectors[0].size)
|
||||||
return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
|
return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
|
||||||
|
|
||||||
Memory::CopyToEmu(request.io_vectors[0].address, raw_view.data(), raw_view.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),
|
INFO_LOG(IOS_ES, "GetTMDView: %u bytes for title %016" PRIx64, MaxCount, TitleID);
|
||||||
(u32)TitleID, MaxCount);
|
|
||||||
return GetDefaultReply(IPC_SUCCESS);
|
return GetDefaultReply(IPC_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@ namespace DiscIO
|
||||||
{
|
{
|
||||||
enum class Region;
|
enum class Region;
|
||||||
|
|
||||||
|
// TODO: move some of these to Core/IOS/ES.
|
||||||
bool AddTicket(const IOS::ES::TicketReader& signed_ticket);
|
bool AddTicket(const IOS::ES::TicketReader& signed_ticket);
|
||||||
IOS::ES::TicketReader FindSignedTicket(u64 title_id);
|
IOS::ES::TicketReader FindSignedTicket(u64 title_id);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue