diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt
index 83f36fbc6a..7f737dc1a3 100644
--- a/Source/Core/Core/CMakeLists.txt
+++ b/Source/Core/Core/CMakeLists.txt
@@ -149,6 +149,11 @@ set(SRCS
IOS/DI/DI.cpp
IOS/ES/ES.cpp
IOS/ES/Formats.cpp
+ IOS/ES/Identity.cpp
+ IOS/ES/TitleContents.cpp
+ IOS/ES/TitleInformation.cpp
+ IOS/ES/TitleManagement.cpp
+ IOS/ES/Views.cpp
IOS/FS/FileIO.cpp
IOS/FS/FS.cpp
IOS/Network/ICMPLin.cpp
diff --git a/Source/Core/Core/Core.vcxproj b/Source/Core/Core/Core.vcxproj
index 9188e95c17..ae8fd022ec 100644
--- a/Source/Core/Core/Core.vcxproj
+++ b/Source/Core/Core/Core.vcxproj
@@ -176,6 +176,11 @@
+
+
+
+
+
diff --git a/Source/Core/Core/Core.vcxproj.filters b/Source/Core/Core/Core.vcxproj.filters
index 89ff3f5277..4a5ecc2a3d 100644
--- a/Source/Core/Core/Core.vcxproj.filters
+++ b/Source/Core/Core/Core.vcxproj.filters
@@ -756,6 +756,21 @@
IOS\ES
+
+ IOS\ES
+
+
+ IOS\ES
+
+
+ IOS\ES
+
+
+ IOS\ES
+
+
+ IOS\ES
+
IOS\FS
diff --git a/Source/Core/Core/IOS/ES/ES.cpp b/Source/Core/Core/IOS/ES/ES.cpp
index 5528cf1f97..c62c81ce6b 100644
--- a/Source/Core/Core/IOS/ES/ES.cpp
+++ b/Source/Core/Core/IOS/ES/ES.cpp
@@ -4,32 +4,21 @@
#include "Core/IOS/ES/ES.h"
-#include
-#include
#include
#include
-#include
#include
#include
#include
-#include
-#include
-
-#include "Common/Align.h"
-#include "Common/Assert.h"
#include "Common/ChunkFile.h"
#include "Common/FileUtil.h"
#include "Common/Logging/Log.h"
#include "Common/MsgHandler.h"
#include "Common/NandPaths.h"
-#include "Common/Swap.h"
#include "Core/ConfigManager.h"
#include "Core/HW/Memmap.h"
#include "Core/IOS/ES/Formats.h"
-#include "Core/ec_wii.h"
#include "DiscIO/NANDContentLoader.h"
-#include "DiscIO/Volume.h"
namespace IOS
{
@@ -37,19 +26,6 @@ namespace HLE
{
namespace Device
{
-struct TitleContext
-{
- void Clear();
- void DoState(PointerWrap& p);
- void Update(const DiscIO::CNANDContentLoader& content_loader);
- void Update(const IOS::ES::TMDReader& tmd_, const IOS::ES::TicketReader& ticket_);
-
- IOS::ES::TicketReader ticket;
- IOS::ES::TMDReader tmd;
- bool active = false;
- bool first_change = true;
-};
-
// Shared across all ES instances.
static std::string s_content_file;
static TitleContext s_title_context;
@@ -57,29 +33,6 @@ static TitleContext s_title_context;
// Title to launch after IOS has been reset and reloaded (similar to /sys/launch.sys).
static u64 s_title_to_launch;
-constexpr u8 s_key_sd[0x10] = {0xab, 0x01, 0xb9, 0xd8, 0xe1, 0x62, 0x2b, 0x08,
- 0xaf, 0xba, 0xd8, 0x4d, 0xbf, 0xc2, 0xa5, 0x5d};
-constexpr u8 s_key_ecc[0x1e] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01};
-constexpr u8 s_key_empty[0x10] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
-
-// default key table
-constexpr const u8* s_key_table[11] = {
- s_key_ecc, // ECC Private Key
- s_key_empty, // Console ID
- s_key_empty, // NAND AES Key
- s_key_empty, // NAND HMAC
- s_key_empty, // Common Key
- s_key_empty, // PRNG seed
- s_key_sd, // SD Key
- s_key_empty, // Unknown
- s_key_empty, // Unknown
- s_key_empty, // Unknown
- s_key_empty, // Unknown
-};
-
ES::ES(u32 device_id, const std::string& device_name) : Device(device_id, device_name)
{
}
@@ -101,6 +54,11 @@ void ES::Init()
}
}
+TitleContext& ES::GetTitleContext()
+{
+ return s_title_context;
+}
+
void TitleContext::Clear()
{
ticket.SetBytes({});
@@ -152,12 +110,44 @@ void ES::LoadWAD(const std::string& _rContentFile)
INFO_LOG(IOS_ES, "LoadWAD: Title context changed: %016" PRIx64, s_title_context.tmd.GetTitleId());
}
-void ES::DecryptContent(u32 key_index, u8* iv, u8* input, u32 size, u8* new_iv, u8* output)
+IPCCommandResult ES::GetTitleDirectory(const IOCtlVRequest& request)
{
- mbedtls_aes_context AES_ctx;
- mbedtls_aes_setkey_dec(&AES_ctx, s_key_table[key_index], 128);
- memcpy(new_iv, iv, 16);
- mbedtls_aes_crypt_cbc(&AES_ctx, MBEDTLS_AES_DECRYPT, size, new_iv, input, output);
+ if (!request.HasNumberOfValidVectors(1, 1))
+ return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
+
+ u64 TitleID = Memory::Read_U64(request.in_vectors[0].address);
+
+ char* Path = (char*)Memory::GetPointer(request.io_vectors[0].address);
+ sprintf(Path, "/title/%08x/%08x/data", (u32)(TitleID >> 32), (u32)TitleID);
+
+ INFO_LOG(IOS_ES, "IOCTL_ES_GETTITLEDIR: %s", Path);
+ return GetDefaultReply(IPC_SUCCESS);
+}
+
+IPCCommandResult ES::GetTitleID(const IOCtlVRequest& request)
+{
+ if (!request.HasNumberOfValidVectors(0, 1))
+ return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
+
+ if (!s_title_context.active)
+ return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
+
+ const u64 title_id = s_title_context.tmd.GetTitleId();
+ Memory::Write_U64(title_id, request.io_vectors[0].address);
+ INFO_LOG(IOS_ES, "IOCTL_ES_GETTITLEID: %08x/%08x", static_cast(title_id >> 32),
+ static_cast(title_id));
+ return GetDefaultReply(IPC_SUCCESS);
+}
+
+IPCCommandResult ES::SetUID(const IOCtlVRequest& request)
+{
+ if (!request.HasNumberOfValidVectors(1, 0))
+ return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
+
+ // TODO: fs permissions based on this
+ u64 TitleID = Memory::Read_U64(request.in_vectors[0].address);
+ INFO_LOG(IOS_ES, "IOCTL_ES_SETUID titleID: %08x/%08x", (u32)(TitleID >> 32), (u32)TitleID);
+ return GetDefaultReply(IPC_SUCCESS);
}
bool ES::LaunchTitle(u64 title_id, bool skip_reload)
@@ -271,34 +261,6 @@ void ES::Close()
DiscIO::CNANDContentManager::Access().ClearCache();
}
-u32 ES::OpenTitleContent(u32 CFD, u64 TitleID, u16 Index)
-{
- const DiscIO::CNANDContentLoader& Loader = AccessContentDevice(TitleID);
-
- if (!Loader.IsValid() || !Loader.GetTMD().IsValid() || !Loader.GetTicket().IsValid())
- {
- WARN_LOG(IOS_ES, "ES: loader not valid for %" PRIx64, TitleID);
- return 0xffffffff;
- }
-
- const DiscIO::SNANDContent* pContent = Loader.GetContentByIndex(Index);
-
- if (pContent == nullptr)
- {
- return 0xffffffff; // TODO: what is the correct error value here?
- }
-
- OpenedContent content;
- content.m_position = 0;
- content.m_content = pContent->m_metadata;
- content.m_title_id = TitleID;
-
- pContent->m_Data->Open();
-
- m_ContentAccessMap[CFD] = content;
- return CFD;
-}
-
IPCCommandResult ES::IOCtlV(const IOCtlVRequest& request)
{
DEBUG_LOG(IOS_ES, "%s (0x%x)", GetDeviceName().c_str(), request.request);
@@ -329,7 +291,7 @@ IPCCommandResult ES::IOCtlV(const IOCtlVRequest& request)
case IOCTL_ES_ADDTITLECANCEL:
return AddTitleCancel(request);
case IOCTL_ES_GETDEVICEID:
- return ESGetDeviceID(request);
+ return GetConsoleID(request);
case IOCTL_ES_OPENTITLECONTENT:
return OpenTitleContent(request);
case IOCTL_ES_OPENCONTENT:
@@ -366,9 +328,9 @@ IPCCommandResult ES::IOCtlV(const IOCtlVRequest& request)
return GetTMDStoredContents(request);
case IOCTL_ES_GETVIEWCNT:
- return GetViewCount(request);
+ return GetTicketViewCount(request);
case IOCTL_ES_GETVIEWS:
- return GetViews(request);
+ return GetTicketViews(request);
case IOCTL_ES_DIGETTICKETVIEW:
return DIGetTicketView(request);
@@ -428,937 +390,6 @@ IPCCommandResult ES::IOCtlV(const IOCtlVRequest& request)
return GetDefaultReply(IPC_SUCCESS);
}
-IPCCommandResult ES::AddTicket(const IOCtlVRequest& request)
-{
- if (!request.HasNumberOfValidVectors(3, 0))
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- std::vector bytes(request.in_vectors[0].size);
- Memory::CopyFromEmu(bytes.data(), request.in_vectors[0].address, request.in_vectors[0].size);
-
- IOS::ES::TicketReader ticket{std::move(bytes)};
- if (!ticket.IsValid())
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- const u32 ticket_device_id = ticket.GetDeviceId();
- const u32 device_id = EcWii::GetInstance().GetNGID();
- if (ticket_device_id != 0)
- {
- if (device_id != ticket_device_id)
- {
- WARN_LOG(IOS_ES, "Device ID mismatch: ticket %08x, device %08x", ticket_device_id, device_id);
- return GetDefaultReply(ES_DEVICE_ID_MISMATCH);
- }
- const s32 ret = ticket.Unpersonalise();
- if (ret < 0)
- {
- ERROR_LOG(IOS_ES, "AddTicket: Failed to unpersonalise ticket for %016" PRIx64 " (ret = %d)",
- ticket.GetTitleId(), ret);
- return GetDefaultReply(ret);
- }
- }
-
- if (!DiscIO::AddTicket(ticket))
- return GetDefaultReply(ES_WRITE_FAILURE);
-
- INFO_LOG(IOS_ES, "AddTicket: Imported ticket for title %016" PRIx64, ticket.GetTitleId());
- return GetDefaultReply(IPC_SUCCESS);
-}
-
-static bool WriteImportTMD(const IOS::ES::TMDReader& tmd)
-{
- const std::string tmd_path = Common::GetImportTitlePath(tmd.GetTitleId()) + "/content/title.tmd";
- File::CreateFullPath(tmd_path);
-
- File::IOFile file(tmd_path, "wb");
- return file.WriteBytes(tmd.GetRawTMD().data(), tmd.GetRawTMD().size());
-}
-
-static bool MoveImportTMDToTitleDirectory(const IOS::ES::TMDReader& tmd)
-{
- const std::string src = Common::GetImportTitlePath(tmd.GetTitleId()) + "/content/title.tmd";
- const std::string dest = Common::GetTMDFileName(tmd.GetTitleId(), Common::FROM_SESSION_ROOT);
- return File::RenameSync(src, dest);
-}
-
-static std::string GetImportContentPath(const IOS::ES::TMDReader& tmd, u32 content_id)
-{
- return Common::GetImportTitlePath(tmd.GetTitleId()) +
- StringFromFormat("/content/%08x.app", content_id);
-}
-
-IPCCommandResult ES::AddTMD(const IOCtlVRequest& request)
-{
- if (!request.HasNumberOfValidVectors(1, 0))
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- std::vector tmd(request.in_vectors[0].size);
- Memory::CopyFromEmu(tmd.data(), request.in_vectors[0].address, request.in_vectors[0].size);
-
- // Ioctlv 0x2b writes the TMD to /tmp/title.tmd (for imports) and doesn't seem to write it
- // to either /import or /title. So here we simply have to set the import TMD.
- m_addtitle_tmd.SetBytes(std::move(tmd));
- if (!m_addtitle_tmd.IsValid())
- return GetDefaultReply(ES_INVALID_TMD);
-
- DiscIO::cUIDsys uid_sys{Common::FROM_CONFIGURED_ROOT};
- uid_sys.AddTitle(m_addtitle_tmd.GetTitleId());
-
- return GetDefaultReply(IPC_SUCCESS);
-}
-
-IPCCommandResult ES::AddTitleStart(const IOCtlVRequest& request)
-{
- if (!request.HasNumberOfValidVectors(4, 0))
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- INFO_LOG(IOS_ES, "IOCTL_ES_ADDTITLESTART");
- std::vector tmd(request.in_vectors[0].size);
- Memory::CopyFromEmu(tmd.data(), request.in_vectors[0].address, request.in_vectors[0].size);
-
- m_addtitle_tmd.SetBytes(tmd);
- if (!m_addtitle_tmd.IsValid())
- {
- ERROR_LOG(IOS_ES, "Invalid TMD while adding title (size = %zd)", tmd.size());
- return GetDefaultReply(ES_INVALID_TMD);
- }
-
- DiscIO::cUIDsys uid_sys{Common::FROM_CONFIGURED_ROOT};
- uid_sys.AddTitle(m_addtitle_tmd.GetTitleId());
-
- // TODO: check and use the other vectors.
-
- return GetDefaultReply(IPC_SUCCESS);
-}
-
-IPCCommandResult ES::AddContentStart(const IOCtlVRequest& request)
-{
- if (!request.HasNumberOfValidVectors(2, 0))
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- u64 title_id = Memory::Read_U64(request.in_vectors[0].address);
- u32 content_id = Memory::Read_U32(request.in_vectors[1].address);
-
- if (m_addtitle_content_id != 0xFFFFFFFF)
- {
- ERROR_LOG(IOS_ES, "Trying to add content when we haven't finished adding "
- "another content. Unsupported.");
- return GetDefaultReply(ES_WRITE_FAILURE);
- }
- m_addtitle_content_id = content_id;
-
- m_addtitle_content_buffer.clear();
-
- INFO_LOG(IOS_ES, "IOCTL_ES_ADDCONTENTSTART: title id %016" PRIx64 ", "
- "content id %08x",
- title_id, m_addtitle_content_id);
-
- if (!m_addtitle_tmd.IsValid())
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- if (title_id != m_addtitle_tmd.GetTitleId())
- {
- ERROR_LOG(IOS_ES, "IOCTL_ES_ADDCONTENTSTART: title id %016" PRIx64 " != "
- "TMD title id %016" PRIx64 ", ignoring",
- title_id, m_addtitle_tmd.GetTitleId());
- }
-
- // We're supposed to return a "content file descriptor" here, which is
- // passed to further AddContentData / AddContentFinish. But so far there is
- // no known content installer which performs content addition concurrently.
- // Instead we just log an error (see above) if this condition is detected.
- s32 content_fd = 0;
- return GetDefaultReply(content_fd);
-}
-
-IPCCommandResult ES::AddContentData(const IOCtlVRequest& request)
-{
- if (!request.HasNumberOfValidVectors(2, 0))
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- u32 content_fd = Memory::Read_U32(request.in_vectors[0].address);
- INFO_LOG(IOS_ES, "IOCTL_ES_ADDCONTENTDATA: content fd %08x, "
- "size %d",
- content_fd, request.in_vectors[1].size);
-
- u8* data_start = Memory::GetPointer(request.in_vectors[1].address);
- u8* data_end = data_start + request.in_vectors[1].size;
- m_addtitle_content_buffer.insert(m_addtitle_content_buffer.end(), data_start, data_end);
- return GetDefaultReply(IPC_SUCCESS);
-}
-
-static bool CheckIfContentHashMatches(const std::vector& content, const IOS::ES::Content& info)
-{
- std::array sha1;
- mbedtls_sha1(content.data(), info.size, sha1.data());
- return sha1 == info.sha1;
-}
-
-IPCCommandResult ES::AddContentFinish(const IOCtlVRequest& request)
-{
- if (!request.HasNumberOfValidVectors(1, 0))
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- if (m_addtitle_content_id == 0xFFFFFFFF)
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- u32 content_fd = Memory::Read_U32(request.in_vectors[0].address);
- INFO_LOG(IOS_ES, "IOCTL_ES_ADDCONTENTFINISH: content fd %08x", content_fd);
-
- if (!m_addtitle_tmd.IsValid())
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- // Try to find the title key from a pre-installed ticket.
- IOS::ES::TicketReader ticket = DiscIO::FindSignedTicket(m_addtitle_tmd.GetTitleId());
- if (!ticket.IsValid())
- {
- return GetDefaultReply(ES_NO_TICKET_INSTALLED);
- }
-
- mbedtls_aes_context aes_ctx;
- mbedtls_aes_setkey_dec(&aes_ctx, ticket.GetTitleKey().data(), 128);
-
- // The IV for title content decryption is the lower two bytes of the
- // content index, zero extended.
- IOS::ES::Content content_info;
- if (!m_addtitle_tmd.FindContentById(m_addtitle_content_id, &content_info))
- {
- return GetDefaultReply(ES_INVALID_TMD);
- }
- u8 iv[16] = {0};
- iv[0] = (content_info.index >> 8) & 0xFF;
- iv[1] = content_info.index & 0xFF;
- std::vector decrypted_data(m_addtitle_content_buffer.size());
- mbedtls_aes_crypt_cbc(&aes_ctx, MBEDTLS_AES_DECRYPT, m_addtitle_content_buffer.size(), iv,
- m_addtitle_content_buffer.data(), decrypted_data.data());
- if (!CheckIfContentHashMatches(decrypted_data, content_info))
- {
- ERROR_LOG(IOS_ES, "AddContentFinish: Hash for content %08x doesn't match", content_info.id);
- return GetDefaultReply(ES_HASH_DOESNT_MATCH);
- }
-
- // Just write all contents to the title import directory. AddTitleFinish will
- // move the contents to the proper location.
- const std::string tmp_path = GetImportContentPath(m_addtitle_tmd, m_addtitle_content_id);
- File::CreateFullPath(tmp_path);
-
- File::IOFile fp(tmp_path, "wb");
- if (!fp.WriteBytes(decrypted_data.data(), content_info.size))
- {
- ERROR_LOG(IOS_ES, "AddContentFinish: Failed to write to %s", tmp_path.c_str());
- return GetDefaultReply(ES_WRITE_FAILURE);
- }
-
- m_addtitle_content_id = 0xFFFFFFFF;
- return GetDefaultReply(IPC_SUCCESS);
-}
-
-static void AbortImport(const u64 title_id, const std::vector& processed_paths)
-{
- for (const auto& path : processed_paths)
- File::Delete(path);
-
- const std::string import_dir = Common::GetImportTitlePath(title_id);
- File::DeleteDirRecursively(import_dir);
-}
-
-IPCCommandResult ES::AddTitleFinish(const IOCtlVRequest& request)
-{
- if (!request.HasNumberOfValidVectors(0, 0) || !m_addtitle_tmd.IsValid())
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- std::vector processed_paths;
-
- for (const auto& content_info : m_addtitle_tmd.GetContents())
- {
- const std::string source = GetImportContentPath(m_addtitle_tmd, content_info.id);
-
- // Contents may not have been all imported. This is normal and this isn't an error condition.
- if (!File::Exists(source))
- continue;
-
- std::string content_path;
- if (content_info.IsShared())
- {
- DiscIO::CSharedContent shared_content{Common::FROM_SESSION_ROOT};
- content_path = shared_content.AddSharedContent(content_info.sha1.data());
- }
- else
- {
- content_path =
- StringFromFormat("%s%08x.app", Common::GetTitleContentPath(m_addtitle_tmd.GetTitleId(),
- Common::FROM_SESSION_ROOT)
- .c_str(),
- content_info.id);
- }
-
- File::CreateFullPath(content_path);
- if (!File::RenameSync(source, content_path))
- {
- ERROR_LOG(IOS_ES, "AddTitleFinish: Failed to rename %s to %s", source.c_str(),
- content_path.c_str());
- AbortImport(m_addtitle_tmd.GetTitleId(), processed_paths);
- return GetDefaultReply(ES_WRITE_FAILURE);
- }
-
- // Do not delete shared contents even if the import fails. This is because
- // they can be used by several titles and it's not safe to delete them.
- //
- // The reason we delete private contents is to avoid having a title with half-complete
- // contents, as it can cause issues with the system menu. On the other hand, leaving
- // shared contents does not cause any issue.
- if (!content_info.IsShared())
- processed_paths.push_back(content_path);
- }
-
- if (!WriteImportTMD(m_addtitle_tmd) || !MoveImportTMDToTitleDirectory(m_addtitle_tmd))
- return GetDefaultReply(ES_WRITE_FAILURE);
-
- INFO_LOG(IOS_ES, "IOCTL_ES_ADDTITLEFINISH");
- m_addtitle_tmd.SetBytes({});
- return GetDefaultReply(IPC_SUCCESS);
-}
-
-IPCCommandResult ES::AddTitleCancel(const IOCtlVRequest& request)
-{
- if (!request.HasNumberOfValidVectors(0, 0) || !m_addtitle_tmd.IsValid())
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- AbortImport(m_addtitle_tmd.GetTitleId(), {});
- m_addtitle_tmd.SetBytes({});
- return GetDefaultReply(IPC_SUCCESS);
-}
-
-IPCCommandResult ES::ESGetDeviceID(const IOCtlVRequest& request)
-{
- if (!request.HasNumberOfValidVectors(0, 1))
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- const EcWii& ec = EcWii::GetInstance();
- INFO_LOG(IOS_ES, "IOCTL_ES_GETDEVICEID %08X", ec.GetNGID());
- Memory::Write_U32(ec.GetNGID(), request.io_vectors[0].address);
- return GetDefaultReply(IPC_SUCCESS);
-}
-
-static std::vector GetStoredContentsFromTMD(const IOS::ES::TMDReader& tmd)
-{
- if (!tmd.IsValid())
- return {};
-
- const DiscIO::CSharedContent shared{Common::FROM_SESSION_ROOT};
- const std::vector contents = tmd.GetContents();
-
- std::vector 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
-// is used for the content count (u32).
-IPCCommandResult ES::GetStoredContentsCount(const IOS::ES::TMDReader& tmd,
- const IOCtlVRequest& request)
-{
- if (request.io_vectors[0].size != sizeof(u32) || !tmd.IsValid())
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- const u16 num_contents = static_cast(GetStoredContentsFromTMD(tmd).size());
- 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,
- num_contents, tmd.GetTitleId());
- return GetDefaultReply(IPC_SUCCESS);
-}
-
-// Used by the GetStoredContents ioctlvs. This assumes that the second input vector is used
-// for the content count and the output vector is used to store a list of content IDs (u32s).
-IPCCommandResult ES::GetStoredContents(const IOS::ES::TMDReader& tmd, const IOCtlVRequest& request)
-{
- if (!tmd.IsValid())
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- if (request.in_vectors[1].size != sizeof(u32) ||
- request.io_vectors[0].size != Memory::Read_U32(request.in_vectors[1].address) * sizeof(u32))
- {
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
- }
-
- const auto contents = GetStoredContentsFromTMD(tmd);
- const u32 max_content_count = Memory::Read_U32(request.in_vectors[1].address);
- for (u32 i = 0; i < std::min(static_cast(contents.size()), max_content_count); ++i)
- Memory::Write_U32(contents[i].id, request.io_vectors[0].address + i * sizeof(u32));
-
- return GetDefaultReply(IPC_SUCCESS);
-}
-
-IPCCommandResult ES::GetStoredContentsCount(const IOCtlVRequest& request)
-{
- if (!request.HasNumberOfValidVectors(1, 1) || request.in_vectors[0].size != sizeof(u64))
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- const u64 title_id = Memory::Read_U64(request.in_vectors[0].address);
- const DiscIO::CNANDContentLoader& content_loader = AccessContentDevice(title_id);
- if (!content_loader.IsValid())
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
- return GetStoredContentsCount(content_loader.GetTMD(), request);
-}
-
-IPCCommandResult ES::GetStoredContents(const IOCtlVRequest& request)
-{
- if (!request.HasNumberOfValidVectors(2, 1) || request.in_vectors[0].size != sizeof(u64))
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- const u64 title_id = Memory::Read_U64(request.in_vectors[0].address);
- const DiscIO::CNANDContentLoader& content_loader = AccessContentDevice(title_id);
- if (!content_loader.IsValid())
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
- return GetStoredContents(content_loader.GetTMD(), request);
-}
-
-IPCCommandResult ES::GetTMDStoredContentsCount(const IOCtlVRequest& request)
-{
- if (!request.HasNumberOfValidVectors(1, 1))
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- std::vector tmd_bytes(request.in_vectors[0].size);
- Memory::CopyFromEmu(tmd_bytes.data(), request.in_vectors[0].address, tmd_bytes.size());
- return GetStoredContentsCount(IOS::ES::TMDReader{std::move(tmd_bytes)}, request);
-}
-
-IPCCommandResult ES::GetTMDStoredContents(const IOCtlVRequest& request)
-{
- if (!request.HasNumberOfValidVectors(2, 1))
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- std::vector tmd_bytes(request.in_vectors[0].size);
- Memory::CopyFromEmu(tmd_bytes.data(), request.in_vectors[0].address, tmd_bytes.size());
- return GetStoredContents(IOS::ES::TMDReader{std::move(tmd_bytes)}, request);
-}
-
-IPCCommandResult ES::OpenTitleContent(const IOCtlVRequest& request)
-{
- if (!request.HasNumberOfValidVectors(3, 0))
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- u64 TitleID = Memory::Read_U64(request.in_vectors[0].address);
- u32 Index = Memory::Read_U32(request.in_vectors[2].address);
-
- s32 CFD = OpenTitleContent(m_AccessIdentID++, TitleID, Index);
-
- INFO_LOG(IOS_ES, "IOCTL_ES_OPENTITLECONTENT: TitleID: %08x/%08x Index %i -> got CFD %x",
- (u32)(TitleID >> 32), (u32)TitleID, Index, CFD);
-
- return GetDefaultReply(CFD);
-}
-
-IPCCommandResult ES::OpenContent(const IOCtlVRequest& request)
-{
- if (!request.HasNumberOfValidVectors(1, 0))
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
- u32 Index = Memory::Read_U32(request.in_vectors[0].address);
-
- if (!s_title_context.active)
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- s32 CFD = OpenTitleContent(m_AccessIdentID++, s_title_context.tmd.GetTitleId(), Index);
- INFO_LOG(IOS_ES, "IOCTL_ES_OPENCONTENT: Index %i -> got CFD %x", Index, CFD);
-
- return GetDefaultReply(CFD);
-}
-
-IPCCommandResult ES::ReadContent(const IOCtlVRequest& request)
-{
- if (!request.HasNumberOfValidVectors(1, 1))
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- u32 CFD = Memory::Read_U32(request.in_vectors[0].address);
- u32 Size = request.io_vectors[0].size;
- u32 Addr = request.io_vectors[0].address;
-
- auto itr = m_ContentAccessMap.find(CFD);
- if (itr == m_ContentAccessMap.end())
- {
- return GetDefaultReply(-1);
- }
- OpenedContent& rContent = itr->second;
-
- u8* pDest = Memory::GetPointer(Addr);
-
- if (rContent.m_position + Size > rContent.m_content.size)
- {
- Size = static_cast(rContent.m_content.size) - rContent.m_position;
- }
-
- if (Size > 0)
- {
- if (pDest)
- {
- const DiscIO::CNANDContentLoader& ContentLoader = AccessContentDevice(rContent.m_title_id);
- // ContentLoader should never be invalid; rContent has been created by it.
- if (ContentLoader.IsValid() && ContentLoader.GetTicket().IsValid())
- {
- const DiscIO::SNANDContent* pContent =
- ContentLoader.GetContentByIndex(rContent.m_content.index);
- if (!pContent->m_Data->GetRange(rContent.m_position, Size, pDest))
- ERROR_LOG(IOS_ES, "ES: failed to read %u bytes from %u!", Size, rContent.m_position);
- }
-
- rContent.m_position += Size;
- }
- else
- {
- PanicAlert("IOCTL_ES_READCONTENT - bad destination");
- }
- }
-
- DEBUG_LOG(IOS_ES,
- "IOCTL_ES_READCONTENT: CFD %x, Address 0x%x, Size %i -> stream pos %i (Index %i)", CFD,
- Addr, Size, rContent.m_position, rContent.m_content.index);
-
- return GetDefaultReply(Size);
-}
-
-IPCCommandResult ES::CloseContent(const IOCtlVRequest& request)
-{
- if (!request.HasNumberOfValidVectors(1, 0))
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- u32 CFD = Memory::Read_U32(request.in_vectors[0].address);
-
- INFO_LOG(IOS_ES, "IOCTL_ES_CLOSECONTENT: CFD %x", CFD);
-
- auto itr = m_ContentAccessMap.find(CFD);
- if (itr == m_ContentAccessMap.end())
- {
- return GetDefaultReply(-1);
- }
-
- const DiscIO::CNANDContentLoader& ContentLoader = AccessContentDevice(itr->second.m_title_id);
- // ContentLoader should never be invalid; we shouldn't be here if ES_OPENCONTENT failed before.
- if (ContentLoader.IsValid())
- {
- const DiscIO::SNANDContent* pContent =
- ContentLoader.GetContentByIndex(itr->second.m_content.index);
- pContent->m_Data->Close();
- }
-
- m_ContentAccessMap.erase(itr);
-
- return GetDefaultReply(IPC_SUCCESS);
-}
-
-IPCCommandResult ES::SeekContent(const IOCtlVRequest& request)
-{
- if (!request.HasNumberOfValidVectors(3, 0))
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- u32 CFD = Memory::Read_U32(request.in_vectors[0].address);
- u32 Addr = Memory::Read_U32(request.in_vectors[1].address);
- u32 Mode = Memory::Read_U32(request.in_vectors[2].address);
-
- auto itr = m_ContentAccessMap.find(CFD);
- if (itr == m_ContentAccessMap.end())
- {
- return GetDefaultReply(-1);
- }
- OpenedContent& rContent = itr->second;
-
- switch (Mode)
- {
- case 0: // SET
- rContent.m_position = Addr;
- break;
-
- case 1: // CUR
- rContent.m_position += Addr;
- break;
-
- case 2: // END
- rContent.m_position = static_cast(rContent.m_content.size) + Addr;
- break;
- }
-
- DEBUG_LOG(IOS_ES, "IOCTL_ES_SEEKCONTENT: CFD %x, Address 0x%x, Mode %i -> Pos %i", CFD, Addr,
- Mode, rContent.m_position);
-
- return GetDefaultReply(rContent.m_position);
-}
-
-IPCCommandResult ES::GetTitleDirectory(const IOCtlVRequest& request)
-{
- if (!request.HasNumberOfValidVectors(1, 1))
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- u64 TitleID = Memory::Read_U64(request.in_vectors[0].address);
-
- char* Path = (char*)Memory::GetPointer(request.io_vectors[0].address);
- sprintf(Path, "/title/%08x/%08x/data", (u32)(TitleID >> 32), (u32)TitleID);
-
- INFO_LOG(IOS_ES, "IOCTL_ES_GETTITLEDIR: %s", Path);
- return GetDefaultReply(IPC_SUCCESS);
-}
-
-IPCCommandResult ES::GetTitleID(const IOCtlVRequest& request)
-{
- if (!request.HasNumberOfValidVectors(0, 1))
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- if (!s_title_context.active)
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- const u64 title_id = s_title_context.tmd.GetTitleId();
- Memory::Write_U64(title_id, request.io_vectors[0].address);
- INFO_LOG(IOS_ES, "IOCTL_ES_GETTITLEID: %08x/%08x", static_cast(title_id >> 32),
- static_cast(title_id));
- return GetDefaultReply(IPC_SUCCESS);
-}
-
-IPCCommandResult ES::SetUID(const IOCtlVRequest& request)
-{
- if (!request.HasNumberOfValidVectors(1, 0))
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- // TODO: fs permissions based on this
- u64 TitleID = Memory::Read_U64(request.in_vectors[0].address);
- INFO_LOG(IOS_ES, "IOCTL_ES_SETUID titleID: %08x/%08x", (u32)(TitleID >> 32), (u32)TitleID);
- return GetDefaultReply(IPC_SUCCESS);
-}
-
-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 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 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(type) << 32 | identifier);
- }
- }
-
- return title_ids;
-}
-
-// Returns a vector of title IDs for which there is a ticket.
-static std::vector 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 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(type) << 32 | identifier);
- }
- }
-
- return title_ids;
-}
-
-IPCCommandResult ES::GetTitleCount(const std::vector& titles, const IOCtlVRequest& request)
-{
- if (!request.HasNumberOfValidVectors(0, 1) || request.io_vectors[0].size != 4)
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- Memory::Write_U32(static_cast(titles.size()), request.io_vectors[0].address);
-
- return GetDefaultReply(IPC_SUCCESS);
-}
-
-IPCCommandResult ES::GetTitles(const std::vector& titles, const IOCtlVRequest& request)
-{
- if (!request.HasNumberOfValidVectors(1, 1))
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- const size_t max_count = Memory::Read_U32(request.in_vectors[0].address);
- for (size_t i = 0; i < std::min(max_count, titles.size()); i++)
- {
- Memory::Write_U64(titles[i], request.io_vectors[0].address + static_cast(i) * sizeof(u64));
- INFO_LOG(IOS_ES, " title %016" PRIx64, titles[i]);
- }
- return GetDefaultReply(IPC_SUCCESS);
-}
-
-IPCCommandResult ES::GetTitleCount(const IOCtlVRequest& request)
-{
- const std::vector titles = GetInstalledTitles();
- INFO_LOG(IOS_ES, "GetTitleCount: %zu titles", titles.size());
- return GetTitleCount(titles, request);
-}
-
-IPCCommandResult ES::GetTitles(const IOCtlVRequest& request)
-{
- return GetTitles(GetInstalledTitles(), request);
-}
-
-// HACK: Since we do not want to require users to install disc updates when launching
-// Wii games from the game list (which is the inaccurate game boot path anyway),
-// IOSes have to be faked for games which reload IOS to work properly.
-// To minimize the effect of this hack, we should only do this for disc titles
-// booted from the game list, though.
-static bool ShouldReturnFakeViewsForIOSes(u64 title_id)
-{
- const bool ios = IsTitleType(title_id, IOS::ES::TitleType::System) && title_id != TITLEID_SYSMENU;
- const bool disc_title =
- s_title_context.active && IOS::ES::IsDiscTitle(s_title_context.tmd.GetTitleId());
- return ios && SConfig::GetInstance().m_BootType == SConfig::BOOT_ISO && disc_title;
-}
-
-IPCCommandResult ES::GetViewCount(const IOCtlVRequest& request)
-{
- if (!request.HasNumberOfValidVectors(1, 1))
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- u64 TitleID = Memory::Read_U64(request.in_vectors[0].address);
-
- const IOS::ES::TicketReader ticket = DiscIO::FindSignedTicket(TitleID);
- u32 view_count = ticket.IsValid() ? ticket.GetNumberOfTickets() : 0;
-
- if (ShouldReturnFakeViewsForIOSes(TitleID))
- {
- view_count = 1;
- WARN_LOG(IOS_ES, "GetViewCount: Faking IOS title %016" PRIx64 " being present", TitleID);
- }
-
- INFO_LOG(IOS_ES, "IOCTL_ES_GETVIEWCNT for titleID: %08x/%08x (View Count = %u)",
- static_cast(TitleID >> 32), static_cast(TitleID), view_count);
-
- Memory::Write_U32(view_count, request.io_vectors[0].address);
- return GetDefaultReply(IPC_SUCCESS);
-}
-
-IPCCommandResult ES::GetViews(const IOCtlVRequest& request)
-{
- if (!request.HasNumberOfValidVectors(2, 1))
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- u64 TitleID = Memory::Read_U64(request.in_vectors[0].address);
- u32 maxViews = Memory::Read_U32(request.in_vectors[1].address);
-
- const IOS::ES::TicketReader ticket = DiscIO::FindSignedTicket(TitleID);
-
- if (ticket.IsValid())
- {
- u32 number_of_views = std::min(maxViews, ticket.GetNumberOfTickets());
- for (u32 view = 0; view < number_of_views; ++view)
- {
- const std::vector ticket_view = ticket.GetRawTicketView(view);
- Memory::CopyToEmu(request.io_vectors[0].address + view * sizeof(IOS::ES::TicketView),
- ticket_view.data(), ticket_view.size());
- }
- }
- else if (ShouldReturnFakeViewsForIOSes(TitleID))
- {
- Memory::Memset(request.io_vectors[0].address, 0, sizeof(IOS::ES::TicketView));
- WARN_LOG(IOS_ES, "GetViews: Faking IOS title %016" PRIx64 " being present", TitleID);
- }
-
- INFO_LOG(IOS_ES, "IOCTL_ES_GETVIEWS for titleID: %08x/%08x (MaxViews = %i)", (u32)(TitleID >> 32),
- (u32)TitleID, maxViews);
-
- return GetDefaultReply(IPC_SUCCESS);
-}
-
-IPCCommandResult ES::GetTMDViewSize(const IOCtlVRequest& request)
-{
- if (!request.HasNumberOfValidVectors(1, 1))
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- u64 TitleID = Memory::Read_U64(request.in_vectors[0].address);
-
- const DiscIO::CNANDContentLoader& Loader = AccessContentDevice(TitleID);
-
- if (!Loader.IsValid())
- return GetDefaultReply(FS_ENOENT);
-
- const u32 view_size = static_cast(Loader.GetTMD().GetRawView().size());
- Memory::Write_U32(view_size, request.io_vectors[0].address);
-
- INFO_LOG(IOS_ES, "IOCTL_ES_GETTMDVIEWCNT: title: %08x/%08x (view size %i)", (u32)(TitleID >> 32),
- (u32)TitleID, view_size);
- return GetDefaultReply(IPC_SUCCESS);
-}
-
-IPCCommandResult ES::GetTMDViews(const IOCtlVRequest& request)
-{
- if (!request.HasNumberOfValidVectors(2, 1))
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- u64 TitleID = Memory::Read_U64(request.in_vectors[0].address);
- u32 MaxCount = Memory::Read_U32(request.in_vectors[1].address);
-
- const DiscIO::CNANDContentLoader& Loader = AccessContentDevice(TitleID);
-
- INFO_LOG(IOS_ES, "IOCTL_ES_GETTMDVIEWCNT: title: %08x/%08x buffer size: %i",
- (u32)(TitleID >> 32), (u32)TitleID, MaxCount);
-
- if (!Loader.IsValid())
- return GetDefaultReply(FS_ENOENT);
-
- const std::vector raw_view = Loader.GetTMD().GetRawView();
- if (raw_view.size() != request.io_vectors[0].size)
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- Memory::CopyToEmu(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),
- (u32)TitleID, MaxCount);
- return GetDefaultReply(IPC_SUCCESS);
-}
-
-IPCCommandResult ES::DIGetTMDViewSize(const IOCtlVRequest& request)
-{
- if (!request.HasNumberOfValidVectors(1, 1))
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- // Sanity check the TMD size.
- if (request.in_vectors[0].size >= 4 * 1024 * 1024)
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- if (request.io_vectors[0].size != sizeof(u32))
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- const bool has_tmd = request.in_vectors[0].size != 0;
- size_t tmd_view_size = 0;
-
- if (has_tmd)
- {
- std::vector tmd_bytes(request.in_vectors[0].size);
- Memory::CopyFromEmu(tmd_bytes.data(), request.in_vectors[0].address, tmd_bytes.size());
- const IOS::ES::TMDReader tmd{std::move(tmd_bytes)};
-
- // Yes, this returns -1017, not ES_INVALID_TMD.
- // IOS simply checks whether the TMD has all required content entries.
- if (!tmd.IsValid())
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- tmd_view_size = tmd.GetRawView().size();
- }
- else
- {
- // If no TMD was passed in and no title is active, IOS returns -1017.
- if (!s_title_context.active)
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- tmd_view_size = s_title_context.tmd.GetRawView().size();
- }
-
- Memory::Write_U32(static_cast(tmd_view_size), request.io_vectors[0].address);
- return GetDefaultReply(IPC_SUCCESS);
-}
-
-IPCCommandResult ES::DIGetTMDView(const IOCtlVRequest& request)
-{
- if (!request.HasNumberOfValidVectors(2, 1))
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- // Sanity check the TMD size.
- if (request.in_vectors[0].size >= 4 * 1024 * 1024)
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- // Check whether the TMD view size is consistent.
- if (request.in_vectors[1].size != sizeof(u32) ||
- Memory::Read_U32(request.in_vectors[1].address) != request.io_vectors[0].size)
- {
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
- }
-
- const bool has_tmd = request.in_vectors[0].size != 0;
- std::vector tmd_view;
-
- if (has_tmd)
- {
- std::vector tmd_bytes(request.in_vectors[0].size);
- Memory::CopyFromEmu(tmd_bytes.data(), request.in_vectors[0].address, tmd_bytes.size());
- const IOS::ES::TMDReader tmd{std::move(tmd_bytes)};
-
- if (!tmd.IsValid())
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- tmd_view = tmd.GetRawView();
- }
- else
- {
- // If no TMD was passed in and no title is active, IOS returns -1017.
- if (!s_title_context.active)
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- tmd_view = s_title_context.tmd.GetRawView();
- }
-
- if (tmd_view.size() != request.io_vectors[0].size)
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- Memory::CopyToEmu(request.io_vectors[0].address, tmd_view.data(), tmd_view.size());
- return GetDefaultReply(IPC_SUCCESS);
-}
-
IPCCommandResult ES::GetConsumption(const IOCtlVRequest& request)
{
if (!request.HasNumberOfValidVectors(1, 2))
@@ -1370,156 +401,6 @@ IPCCommandResult ES::GetConsumption(const IOCtlVRequest& request)
return GetDefaultReply(IPC_SUCCESS);
}
-static bool CanDeleteTitle(u64 title_id)
-{
- // IOS only allows deleting non-system titles (or a system title higher than 00000001-00000101).
- return static_cast(title_id >> 32) != 0x00000001 || static_cast(title_id) > 0x101;
-}
-
-IPCCommandResult ES::DeleteTitle(const IOCtlVRequest& request)
-{
- if (!request.HasNumberOfValidVectors(1, 0) || request.in_vectors[0].size != 8)
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- const u64 title_id = Memory::Read_U64(request.in_vectors[0].address);
-
- if (!CanDeleteTitle(title_id))
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- const std::string title_dir =
- StringFromFormat("%s/title/%08x/%08x/", RootUserPath(Common::FROM_SESSION_ROOT).c_str(),
- static_cast(title_id >> 32), static_cast(title_id));
- if (!File::IsDirectory(title_dir) ||
- !DiscIO::CNANDContentManager::Access().RemoveTitle(title_id, Common::FROM_SESSION_ROOT))
- {
- return GetDefaultReply(FS_ENOENT);
- }
-
- if (!File::DeleteDirRecursively(title_dir))
- {
- ERROR_LOG(IOS_ES, "DeleteTitle: Failed to delete title directory: %s", title_dir.c_str());
- return GetDefaultReply(FS_EACCESS);
- }
-
- return GetDefaultReply(IPC_SUCCESS);
-}
-
-IPCCommandResult ES::DeleteTicket(const IOCtlVRequest& request)
-{
- if (!request.HasNumberOfValidVectors(1, 0))
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- u64 TitleID = Memory::Read_U64(request.in_vectors[0].address);
- INFO_LOG(IOS_ES, "IOCTL_ES_DELETETICKET: title: %08x/%08x", (u32)(TitleID >> 32), (u32)TitleID);
-
- // Presumably return -1017 when delete fails
- if (!File::Delete(Common::GetTicketFileName(TitleID, Common::FROM_SESSION_ROOT)))
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- return GetDefaultReply(IPC_SUCCESS);
-}
-
-IPCCommandResult ES::DeleteTitleContent(const IOCtlVRequest& request)
-{
- if (!request.HasNumberOfValidVectors(1, 0))
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- u64 TitleID = Memory::Read_U64(request.in_vectors[0].address);
- INFO_LOG(IOS_ES, "IOCTL_ES_DELETETITLECONTENT: title: %08x/%08x", (u32)(TitleID >> 32),
- (u32)TitleID);
-
- // Presumably return -1017 when title not installed TODO verify
- if (!DiscIO::CNANDContentManager::Access().RemoveTitle(TitleID, Common::FROM_SESSION_ROOT))
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- return GetDefaultReply(IPC_SUCCESS);
-}
-
-IPCCommandResult ES::GetStoredTMDSize(const IOCtlVRequest& request)
-{
- if (!request.HasNumberOfValidVectors(1, 1))
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- u64 TitleID = Memory::Read_U64(request.in_vectors[0].address);
- const DiscIO::CNANDContentLoader& Loader = AccessContentDevice(TitleID);
-
- if (!Loader.IsValid() || !Loader.GetTMD().IsValid())
- return GetDefaultReply(FS_ENOENT);
-
- const u32 tmd_size = static_cast(Loader.GetTMD().GetRawTMD().size());
- Memory::Write_U32(tmd_size, request.io_vectors[0].address);
-
- INFO_LOG(IOS_ES, "IOCTL_ES_GETSTOREDTMDSIZE: title: %08x/%08x (view size %i)",
- (u32)(TitleID >> 32), (u32)TitleID, tmd_size);
-
- return GetDefaultReply(IPC_SUCCESS);
-}
-
-IPCCommandResult ES::GetStoredTMD(const IOCtlVRequest& request)
-{
- if (!request.HasNumberOfValidVectors(2, 1))
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- u64 TitleID = Memory::Read_U64(request.in_vectors[0].address);
- // TODO: actually use this param in when writing to the outbuffer :/
- const u32 MaxCount = Memory::Read_U32(request.in_vectors[1].address);
- const DiscIO::CNANDContentLoader& Loader = AccessContentDevice(TitleID);
-
- if (!Loader.IsValid() || !Loader.GetTMD().IsValid())
- return GetDefaultReply(FS_ENOENT);
-
- const std::vector raw_tmd = Loader.GetTMD().GetRawTMD();
- if (raw_tmd.size() != request.io_vectors[0].size)
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- Memory::CopyToEmu(request.io_vectors[0].address, raw_tmd.data(), raw_tmd.size());
-
- INFO_LOG(IOS_ES, "IOCTL_ES_GETSTOREDTMD: title: %08x/%08x (buffer size: %i)",
- (u32)(TitleID >> 32), (u32)TitleID, MaxCount);
- return GetDefaultReply(IPC_SUCCESS);
-}
-
-IPCCommandResult ES::Encrypt(const IOCtlVRequest& request)
-{
- if (!request.HasNumberOfValidVectors(3, 2))
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- u32 keyIndex = Memory::Read_U32(request.in_vectors[0].address);
- u8* IV = Memory::GetPointer(request.in_vectors[1].address);
- u8* source = Memory::GetPointer(request.in_vectors[2].address);
- u32 size = request.in_vectors[2].size;
- u8* newIV = Memory::GetPointer(request.io_vectors[0].address);
- u8* destination = Memory::GetPointer(request.io_vectors[1].address);
-
- mbedtls_aes_context AES_ctx;
- mbedtls_aes_setkey_enc(&AES_ctx, s_key_table[keyIndex], 128);
- memcpy(newIV, IV, 16);
- mbedtls_aes_crypt_cbc(&AES_ctx, MBEDTLS_AES_ENCRYPT, size, newIV, source, destination);
-
- _dbg_assert_msg_(IOS_ES, keyIndex == 6,
- "IOCTL_ES_ENCRYPT: Key type is not SD, data will be crap");
- return GetDefaultReply(IPC_SUCCESS);
-}
-
-IPCCommandResult ES::Decrypt(const IOCtlVRequest& request)
-{
- if (!request.HasNumberOfValidVectors(3, 2))
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- u32 keyIndex = Memory::Read_U32(request.in_vectors[0].address);
- u8* IV = Memory::GetPointer(request.in_vectors[1].address);
- u8* source = Memory::GetPointer(request.in_vectors[2].address);
- u32 size = request.in_vectors[2].size;
- u8* newIV = Memory::GetPointer(request.io_vectors[0].address);
- u8* destination = Memory::GetPointer(request.io_vectors[1].address);
-
- DecryptContent(keyIndex, IV, source, size, newIV, destination);
-
- _dbg_assert_msg_(IOS_ES, keyIndex == 6,
- "IOCTL_ES_DECRYPT: Key type is not SD, data will be crap");
- return GetDefaultReply(IPC_SUCCESS);
-}
-
IPCCommandResult ES::Launch(const IOCtlVRequest& request)
{
if (!request.HasNumberOfValidVectors(2, 0))
@@ -1565,280 +446,6 @@ IPCCommandResult ES::LaunchBC(const IOCtlVRequest& request)
return GetNoReply();
}
-IPCCommandResult ES::ExportTitleInit(const IOCtlVRequest& request)
-{
- if (!request.HasNumberOfValidVectors(1, 1) || request.in_vectors[0].size != 8)
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- // No concurrent title import/export is allowed.
- if (m_export_title_context.valid)
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- const auto& content_loader = AccessContentDevice(Memory::Read_U64(request.in_vectors[0].address));
- if (!content_loader.IsValid())
- return GetDefaultReply(FS_ENOENT);
- if (!content_loader.GetTMD().IsValid())
- return GetDefaultReply(ES_INVALID_TMD);
-
- m_export_title_context.tmd = content_loader.GetTMD();
-
- const auto ticket = DiscIO::FindSignedTicket(m_export_title_context.tmd.GetTitleId());
- if (!ticket.IsValid())
- return GetDefaultReply(ES_NO_TICKET_INSTALLED);
- if (ticket.GetTitleId() != m_export_title_context.tmd.GetTitleId())
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- m_export_title_context.title_key = ticket.GetTitleKey();
-
- const auto& raw_tmd = m_export_title_context.tmd.GetRawTMD();
- if (request.io_vectors[0].size != raw_tmd.size())
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- Memory::CopyToEmu(request.io_vectors[0].address, raw_tmd.data(), raw_tmd.size());
-
- m_export_title_context.valid = true;
- return GetDefaultReply(IPC_SUCCESS);
-}
-
-IPCCommandResult ES::ExportContentBegin(const IOCtlVRequest& request)
-{
- if (!request.HasNumberOfValidVectors(2, 0) || request.in_vectors[0].size != 8 ||
- request.in_vectors[1].size != 4)
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- const u64 title_id = Memory::Read_U64(request.in_vectors[0].address);
- const u32 content_id = Memory::Read_U32(request.in_vectors[1].address);
-
- if (!m_export_title_context.valid || m_export_title_context.tmd.GetTitleId() != title_id)
- {
- ERROR_LOG(IOS_ES, "Tried to use ExportContentBegin with an invalid title export context.");
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
- }
-
- const auto& content_loader = AccessContentDevice(title_id);
- if (!content_loader.IsValid())
- return GetDefaultReply(FS_ENOENT);
-
- const auto* content = content_loader.GetContentByID(content_id);
- if (!content)
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- OpenedContent entry;
- entry.m_position = 0;
- entry.m_content = content->m_metadata;
- entry.m_title_id = title_id;
- content->m_Data->Open();
-
- u32 cid = 0;
- while (m_export_title_context.contents.find(cid) != m_export_title_context.contents.end())
- cid++;
-
- TitleExportContext::ExportContent content_export;
- content_export.content = std::move(entry);
- content_export.iv[0] = (content->m_metadata.index >> 8) & 0xFF;
- content_export.iv[1] = content->m_metadata.index & 0xFF;
-
- m_export_title_context.contents.emplace(cid, content_export);
- // IOS returns a content ID which is passed to further content calls.
- return GetDefaultReply(cid);
-}
-
-IPCCommandResult ES::ExportContentData(const IOCtlVRequest& request)
-{
- if (!request.HasNumberOfValidVectors(1, 1) || request.in_vectors[0].size != 4 ||
- request.io_vectors[0].size == 0)
- {
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
- }
-
- const u32 content_id = Memory::Read_U32(request.in_vectors[0].address);
- const u32 bytes_to_read = request.io_vectors[0].size;
-
- const auto iterator = m_export_title_context.contents.find(content_id);
- if (!m_export_title_context.valid || iterator == m_export_title_context.contents.end() ||
- iterator->second.content.m_position >= iterator->second.content.m_content.size)
- {
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
- }
-
- auto& metadata = iterator->second.content;
-
- const auto& content_loader = AccessContentDevice(metadata.m_title_id);
- const auto* content = content_loader.GetContentByID(metadata.m_content.id);
- content->m_Data->Open();
-
- const u32 length =
- std::min(static_cast(metadata.m_content.size - metadata.m_position), bytes_to_read);
- std::vector buffer(length);
-
- if (!content->m_Data->GetRange(metadata.m_position, length, buffer.data()))
- {
- ERROR_LOG(IOS_ES, "ExportContentData: ES_READ_LESS_DATA_THAN_EXPECTED");
- return GetDefaultReply(ES_READ_LESS_DATA_THAN_EXPECTED);
- }
-
- // IOS aligns the buffer to 32 bytes. Since we also need to align it to 16 bytes,
- // let's just follow IOS here.
- buffer.resize(Common::AlignUp(buffer.size(), 32));
- std::vector output(buffer.size());
-
- mbedtls_aes_context aes_ctx;
- mbedtls_aes_setkey_enc(&aes_ctx, m_export_title_context.title_key.data(), 128);
- const int ret = mbedtls_aes_crypt_cbc(&aes_ctx, MBEDTLS_AES_ENCRYPT, buffer.size(),
- iterator->second.iv.data(), buffer.data(), output.data());
- if (ret != 0)
- {
- // XXX: proper error code when IOSC_Encrypt fails.
- ERROR_LOG(IOS_ES, "ExportContentData: Failed to encrypt content.");
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
- }
-
- Memory::CopyToEmu(request.io_vectors[0].address, output.data(), output.size());
- metadata.m_position += length;
- return GetDefaultReply(IPC_SUCCESS);
-}
-
-IPCCommandResult ES::ExportContentEnd(const IOCtlVRequest& request)
-{
- if (!request.HasNumberOfValidVectors(1, 0) || request.in_vectors[0].size != 4)
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- const u32 content_id = Memory::Read_U32(request.in_vectors[0].address);
-
- const auto iterator = m_export_title_context.contents.find(content_id);
- if (!m_export_title_context.valid || iterator == m_export_title_context.contents.end() ||
- iterator->second.content.m_position != iterator->second.content.m_content.size)
- {
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
- }
-
- // XXX: Check the content hash, as IOS does?
-
- const auto& content_loader = AccessContentDevice(iterator->second.content.m_title_id);
- content_loader.GetContentByID(iterator->second.content.m_content.id)->m_Data->Close();
-
- m_export_title_context.contents.erase(iterator);
- return GetDefaultReply(IPC_SUCCESS);
-}
-
-IPCCommandResult ES::ExportTitleDone(const IOCtlVRequest& request)
-{
- if (!m_export_title_context.valid)
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- m_export_title_context.valid = false;
- return GetDefaultReply(IPC_SUCCESS);
-}
-
-IPCCommandResult ES::CheckKoreaRegion(const IOCtlVRequest& request)
-{
- if (!request.HasNumberOfValidVectors(0, 0))
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- // note by DacoTaco : name is unknown, I just tried to name it SOMETHING.
- // IOS70 has this to let system menu 4.2 check if the console is region changed. it returns
- // -1017
- // if the IOS didn't find the Korean keys and 0 if it does. 0 leads to a error 003
- INFO_LOG(IOS_ES, "IOCTL_ES_CHECKKOREAREGION: Title checked for Korean keys.");
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-}
-
-IPCCommandResult ES::GetDeviceCertificate(const IOCtlVRequest& request)
-{
- if (!request.HasNumberOfValidVectors(0, 1) || request.io_vectors[0].size != 0x180)
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- INFO_LOG(IOS_ES, "IOCTL_ES_GETDEVICECERT");
- u8* destination = Memory::GetPointer(request.io_vectors[0].address);
-
- const EcWii& ec = EcWii::GetInstance();
- MakeNGCert(destination, ec.GetNGID(), ec.GetNGKeyID(), ec.GetNGPriv(), ec.GetNGSig());
- return GetDefaultReply(IPC_SUCCESS);
-}
-
-IPCCommandResult ES::Sign(const IOCtlVRequest& request)
-{
- if (!request.HasNumberOfValidVectors(1, 2))
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- INFO_LOG(IOS_ES, "IOCTL_ES_SIGN");
- u8* ap_cert_out = Memory::GetPointer(request.io_vectors[1].address);
- u8* data = Memory::GetPointer(request.in_vectors[0].address);
- u32 data_size = request.in_vectors[0].size;
- u8* sig_out = Memory::GetPointer(request.io_vectors[0].address);
-
- if (!s_title_context.active)
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- const EcWii& ec = EcWii::GetInstance();
- MakeAPSigAndCert(sig_out, ap_cert_out, s_title_context.tmd.GetTitleId(), data, data_size,
- ec.GetNGPriv(), ec.GetNGID());
-
- return GetDefaultReply(IPC_SUCCESS);
-}
-
-IPCCommandResult ES::GetBoot2Version(const IOCtlVRequest& request)
-{
- if (!request.HasNumberOfValidVectors(0, 1))
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- INFO_LOG(IOS_ES, "IOCTL_ES_GETBOOT2VERSION");
-
- // as of 26/02/2012, this was latest bootmii version
- Memory::Write_U32(4, request.io_vectors[0].address);
- return GetDefaultReply(IPC_SUCCESS);
-}
-
-IPCCommandResult ES::DIGetTicketView(const IOCtlVRequest& request)
-{
- if (!request.HasNumberOfValidVectors(1, 1) ||
- request.io_vectors[0].size != sizeof(IOS::ES::TicketView))
- {
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
- }
-
- const bool has_ticket_vector = request.in_vectors[0].size == 0x2A4;
-
- // This ioctlv takes either a signed ticket or no ticket, in which case the ticket size must be 0.
- if (!has_ticket_vector && request.in_vectors[0].size != 0)
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- std::vector view;
-
- // If no ticket was passed in, IOS returns the ticket view for the current title.
- // Of course, this returns -1017 if no title is active and no ticket is passed.
- if (!has_ticket_vector)
- {
- if (!s_title_context.active)
- return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT);
-
- view = s_title_context.ticket.GetRawTicketView(0);
- }
- else
- {
- std::vector ticket_bytes(request.in_vectors[0].size);
- Memory::CopyFromEmu(ticket_bytes.data(), request.in_vectors[0].address, ticket_bytes.size());
- const IOS::ES::TicketReader ticket{std::move(ticket_bytes)};
-
- view = ticket.GetRawTicketView(0);
- }
-
- Memory::CopyToEmu(request.io_vectors[0].address, view.data(), view.size());
- return GetDefaultReply(IPC_SUCCESS);
-}
-
-IPCCommandResult ES::GetOwnedTitleCount(const IOCtlVRequest& request)
-{
- const std::vector titles = GetTitlesWithTickets();
- INFO_LOG(IOS_ES, "GetOwnedTitleCount: %zu titles", titles.size());
- return GetTitleCount(titles, request);
-}
-
-IPCCommandResult ES::GetOwnedTitles(const IOCtlVRequest& request)
-{
- return GetTitles(GetTitlesWithTickets(), request);
-}
-
const DiscIO::CNANDContentLoader& ES::AccessContentDevice(u64 title_id)
{
// for WADs, the passed title id and the stored title id match; along with s_content_file
diff --git a/Source/Core/Core/IOS/ES/ES.h b/Source/Core/Core/IOS/ES/ES.h
index ee2acc7c55..829729e31a 100644
--- a/Source/Core/Core/IOS/ES/ES.h
+++ b/Source/Core/Core/IOS/ES/ES.h
@@ -6,7 +6,6 @@
#include
#include