summaryrefslogtreecommitdiff
path: root/Source
diff options
context:
space:
mode:
authorLeo Lam <leolino.lam@gmail.com>2017-07-14 23:34:37 +0800
committerGitHub <noreply@github.com>2017-07-14 23:34:37 +0800
commitbacc35d99e3e2e1989adf26a9542a45b02e52688 (patch)
treefa0dd42b37d2ddb3dd5821b9591cbba1d3e696cb /Source
parent37e06586b8a9528e2ecd1a27f60778771db08f4a (diff)
parent1ba1b51606b2ed7f2c65da8733f59ab86b00561d (diff)
Merge pull request #5733 from leoetlino/import-context
IOS/ES: Merge the title import and export contexts
Diffstat (limited to 'Source')
-rw-r--r--Source/Core/Core/IOS/ES/ES.cpp10
-rw-r--r--Source/Core/Core/IOS/ES/ES.h51
-rw-r--r--Source/Core/Core/IOS/ES/TitleContents.cpp113
-rw-r--r--Source/Core/Core/IOS/ES/TitleManagement.cpp255
-rw-r--r--Source/Core/Core/State.cpp2
5 files changed, 212 insertions, 219 deletions
diff --git a/Source/Core/Core/IOS/ES/ES.cpp b/Source/Core/Core/IOS/ES/ES.cpp
index a0f3241e85..edcb89dca8 100644
--- a/Source/Core/Core/IOS/ES/ES.cpp
+++ b/Source/Core/Core/IOS/ES/ES.cpp
@@ -321,15 +321,7 @@ void ES::Context::DoState(PointerWrap& p)
{
p.Do(uid);
p.Do(gid);
-
- title_import.tmd.DoState(p);
- p.Do(title_import.content_id);
- p.Do(title_import.content_buffer);
-
- p.Do(title_export.valid);
- title_export.tmd.DoState(p);
- p.Do(title_export.title_key);
- p.Do(title_export.contents);
+ title_import_export.DoState(p);
p.Do(active);
p.Do(ipc_fd);
diff --git a/Source/Core/Core/IOS/ES/ES.h b/Source/Core/Core/IOS/ES/ES.h
index f87e7cf50a..929638ecb5 100644
--- a/Source/Core/Core/IOS/ES/ES.h
+++ b/Source/Core/Core/IOS/ES/ES.h
@@ -55,35 +55,21 @@ public:
ReturnCode Close(u32 fd) override;
IPCCommandResult IOCtlV(const IOCtlVRequest& request) override;
- struct OpenedContent
+ struct TitleImportExportContext
{
- bool m_opened = false;
- u64 m_title_id = 0;
- IOS::ES::Content m_content;
- u32 m_position = 0;
- u32 m_uid = 0;
- };
+ void DoState(PointerWrap& p);
- struct TitleImportContext
- {
+ bool valid = false;
IOS::ES::TMDReader tmd;
- u32 content_id = 0xFFFFFFFF;
- std::vector<u8> content_buffer;
- };
-
- // TODO: merge this with TitleImportContext. Also reuse the global content table.
- struct TitleExportContext
- {
- struct ExportContent
+ std::array<u8, 16> key{};
+ struct ContentContext
{
- OpenedContent content;
+ bool valid = false;
+ u32 id = 0;
std::array<u8, 16> iv{};
+ std::vector<u8> buffer;
};
-
- bool valid = false;
- IOS::ES::TMDReader tmd;
- std::array<u8, 16> title_key;
- std::map<u32, ExportContent> contents;
+ ContentContext content;
};
struct Context
@@ -92,8 +78,7 @@ public:
u16 gid = 0;
u32 uid = 0;
- TitleImportContext title_import;
- TitleExportContext title_export;
+ TitleImportExportContext title_import_export;
bool active = false;
// We use this to associate an IPC fd with an ES context.
s32 ipc_fd = -1;
@@ -113,6 +98,12 @@ public:
u32 GetSharedContentsCount() const;
std::vector<std::array<u8, 20>> GetSharedContents() const;
+ // Title contents
+ s32 OpenContent(const IOS::ES::TMDReader& tmd, u16 content_index, u32 uid);
+ ReturnCode CloseContent(u32 cfd, u32 uid);
+ s32 ReadContent(u32 cfd, u8* buffer, u32 size, u32 uid);
+ s32 SeekContent(u32 cfd, u32 offset, SeekMode mode, u32 uid);
+
// Title management
ReturnCode ImportTicket(const std::vector<u8>& ticket_bytes, const std::vector<u8>& cert_chain);
ReturnCode ImportTmd(Context& context, const std::vector<u8>& tmd_bytes);
@@ -342,7 +333,15 @@ private:
static const DiscIO::NANDContentLoader& AccessContentDevice(u64 title_id);
- s32 OpenContent(const IOS::ES::TMDReader& tmd, u16 content_index, u32 uid);
+ // TODO: reuse the FS code.
+ struct OpenedContent
+ {
+ bool m_opened = false;
+ u64 m_title_id = 0;
+ IOS::ES::Content m_content;
+ u32 m_position = 0;
+ u32 m_uid = 0;
+ };
using ContentTable = std::array<OpenedContent, 16>;
ContentTable m_content_table;
diff --git a/Source/Core/Core/IOS/ES/TitleContents.cpp b/Source/Core/Core/IOS/ES/TitleContents.cpp
index 33dbd6d038..d6cba3fb9f 100644
--- a/Source/Core/Core/IOS/ES/TitleContents.cpp
+++ b/Source/Core/Core/IOS/ES/TitleContents.cpp
@@ -88,23 +88,16 @@ IPCCommandResult ES::OpenActiveTitleContent(u32 caller_uid, const IOCtlVRequest&
return GetDefaultReply(OpenContent(GetTitleContext().tmd, content_index, caller_uid));
}
-IPCCommandResult ES::ReadContent(u32 uid, const IOCtlVRequest& request)
+s32 ES::ReadContent(u32 cfd, u8* buffer, u32 size, u32 uid)
{
- if (!request.HasNumberOfValidVectors(1, 1) || request.in_vectors[0].size != sizeof(u32))
- return GetDefaultReply(ES_EINVAL);
-
- const u32 cfd = Memory::Read_U32(request.in_vectors[0].address);
- u32 size = request.io_vectors[0].size;
- const u32 addr = request.io_vectors[0].address;
-
if (cfd >= m_content_table.size())
- return GetDefaultReply(ES_EINVAL);
+ return ES_EINVAL;
OpenedContent& entry = m_content_table[cfd];
if (entry.m_uid != uid)
- return GetDefaultReply(ES_EACCES);
+ return ES_EACCES;
if (!entry.m_opened)
- return GetDefaultReply(IPC_EINVAL);
+ return IPC_EINVAL;
// XXX: make this reuse the FS code... ES just does a simple "IOS_Read" call here
// instead of all this duplicated filesystem logic.
@@ -112,46 +105,45 @@ IPCCommandResult ES::ReadContent(u32 uid, const IOCtlVRequest& request)
if (entry.m_position + size > entry.m_content.size)
size = static_cast<u32>(entry.m_content.size) - entry.m_position;
- if (size > 0)
+ const DiscIO::NANDContentLoader& ContentLoader = AccessContentDevice(entry.m_title_id);
+ // ContentLoader should never be invalid; rContent has been created by it.
+ if (ContentLoader.IsValid() && ContentLoader.GetTicket().IsValid())
{
- if (addr)
- {
- const DiscIO::NANDContentLoader& ContentLoader = AccessContentDevice(entry.m_title_id);
- // ContentLoader should never be invalid; rContent has been created by it.
- if (ContentLoader.IsValid() && ContentLoader.GetTicket().IsValid())
- {
- const DiscIO::NANDContent* pContent =
- ContentLoader.GetContentByIndex(entry.m_content.index);
- pContent->m_Data->Open();
- if (!pContent->m_Data->GetRange(entry.m_position, size, Memory::GetPointer(addr)))
- ERROR_LOG(IOS_ES, "ES: failed to read %u bytes from %u!", size, entry.m_position);
- }
-
- entry.m_position += size;
- }
- else
+ const DiscIO::NANDContent* pContent = ContentLoader.GetContentByIndex(entry.m_content.index);
+ pContent->m_Data->Open();
+ if (!pContent->m_Data->GetRange(entry.m_position, size, buffer))
{
- PanicAlert("IOCTL_ES_READCONTENT - bad destination");
+ ERROR_LOG(IOS_ES, "ES: failed to read %u bytes from %u!", size, entry.m_position);
+ return ES_SHORT_READ;
}
}
- return GetDefaultReply(size);
+ entry.m_position += size;
+ return size;
}
-IPCCommandResult ES::CloseContent(u32 uid, const IOCtlVRequest& request)
+IPCCommandResult ES::ReadContent(u32 uid, const IOCtlVRequest& request)
{
- if (!request.HasNumberOfValidVectors(1, 0) || request.in_vectors[0].size != sizeof(u32))
+ if (!request.HasNumberOfValidVectors(1, 1) || request.in_vectors[0].size != sizeof(u32))
return GetDefaultReply(ES_EINVAL);
const u32 cfd = Memory::Read_U32(request.in_vectors[0].address);
+ const u32 size = request.io_vectors[0].size;
+ const u32 addr = request.io_vectors[0].address;
+
+ return GetDefaultReply(ReadContent(cfd, Memory::GetPointer(addr), size, uid));
+}
+
+ReturnCode ES::CloseContent(u32 cfd, u32 uid)
+{
if (cfd >= m_content_table.size())
- return GetDefaultReply(ES_EINVAL);
+ return ES_EINVAL;
OpenedContent& entry = m_content_table[cfd];
if (entry.m_uid != uid)
- return GetDefaultReply(ES_EACCES);
+ return ES_EACCES;
if (!entry.m_opened)
- return GetDefaultReply(IPC_EINVAL);
+ return IPC_EINVAL;
// XXX: again, this should be a simple IOS_Close.
const DiscIO::NANDContentLoader& ContentLoader = AccessContentDevice(entry.m_title_id);
@@ -164,44 +156,61 @@ IPCCommandResult ES::CloseContent(u32 uid, const IOCtlVRequest& request)
entry = {};
INFO_LOG(IOS_ES, "CloseContent: CFD %u", cfd);
- return GetDefaultReply(IPC_SUCCESS);
+ return IPC_SUCCESS;
}
-IPCCommandResult ES::SeekContent(u32 uid, const IOCtlVRequest& request)
+IPCCommandResult ES::CloseContent(u32 uid, const IOCtlVRequest& request)
{
- if (!request.HasNumberOfValidVectors(3, 0))
+ if (!request.HasNumberOfValidVectors(1, 0) || request.in_vectors[0].size != sizeof(u32))
return GetDefaultReply(ES_EINVAL);
const u32 cfd = Memory::Read_U32(request.in_vectors[0].address);
+ return GetDefaultReply(CloseContent(cfd, uid));
+}
+
+s32 ES::SeekContent(u32 cfd, u32 offset, SeekMode mode, u32 uid)
+{
if (cfd >= m_content_table.size())
- return GetDefaultReply(ES_EINVAL);
+ return ES_EINVAL;
OpenedContent& entry = m_content_table[cfd];
if (entry.m_uid != uid)
- return GetDefaultReply(ES_EACCES);
+ return ES_EACCES;
if (!entry.m_opened)
- return GetDefaultReply(IPC_EINVAL);
-
- u32 Addr = Memory::Read_U32(request.in_vectors[1].address);
- u32 Mode = Memory::Read_U32(request.in_vectors[2].address);
+ return IPC_EINVAL;
// XXX: This should be a simple IOS_Seek.
- switch (Mode)
+ switch (mode)
{
- case 0: // SET
- entry.m_position = Addr;
+ case SeekMode::IOS_SEEK_SET:
+ entry.m_position = offset;
break;
- case 1: // CUR
- entry.m_position += Addr;
+ case SeekMode::IOS_SEEK_CUR:
+ entry.m_position += offset;
break;
- case 2: // END
- entry.m_position = static_cast<u32>(entry.m_content.size) + Addr;
+ case SeekMode::IOS_SEEK_END:
+ entry.m_position = static_cast<u32>(entry.m_content.size) + offset;
break;
+
+ default:
+ return FS_EINVAL;
}
- return GetDefaultReply(entry.m_position);
+ return entry.m_position;
+}
+
+IPCCommandResult ES::SeekContent(u32 uid, const IOCtlVRequest& request)
+{
+ if (!request.HasNumberOfValidVectors(3, 0))
+ return GetDefaultReply(ES_EINVAL);
+
+ const u32 cfd = Memory::Read_U32(request.in_vectors[0].address);
+ const u32 offset = Memory::Read_U32(request.in_vectors[1].address);
+ const SeekMode mode = static_cast<SeekMode>(Memory::Read_U32(request.in_vectors[2].address));
+
+ return GetDefaultReply(SeekContent(cfd, offset, mode, uid));
}
} // namespace Device
} // namespace HLE
diff --git a/Source/Core/Core/IOS/ES/TitleManagement.cpp b/Source/Core/Core/IOS/ES/TitleManagement.cpp
index 3654f90d85..7066eacfb4 100644
--- a/Source/Core/Core/IOS/ES/TitleManagement.cpp
+++ b/Source/Core/Core/IOS/ES/TitleManagement.cpp
@@ -45,6 +45,17 @@ static ReturnCode WriteTicket(const IOS::ES::TicketReader& ticket)
return ticket_file.WriteBytes(raw_ticket.data(), raw_ticket.size()) ? IPC_SUCCESS : ES_EIO;
}
+void ES::TitleImportExportContext::DoState(PointerWrap& p)
+{
+ p.Do(valid);
+ p.Do(key);
+ tmd.DoState(p);
+ p.Do(content.valid);
+ p.Do(content.id);
+ p.Do(content.iv);
+ p.Do(content.buffer);
+}
+
ReturnCode ES::ImportTicket(const std::vector<u8>& ticket_bytes, const std::vector<u8>& cert_chain)
{
IOS::ES::TicketReader ticket{ticket_bytes};
@@ -98,8 +109,9 @@ ReturnCode ES::ImportTmd(Context& context, const std::vector<u8>& tmd_bytes)
{
// 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.
- context.title_import.tmd.SetBytes(tmd_bytes);
- if (!context.title_import.tmd.IsValid())
+ context.title_import_export = {};
+ context.title_import_export.tmd.SetBytes(tmd_bytes);
+ if (!context.title_import_export.tmd.IsValid())
return ES_EINVAL;
std::vector<u8> cert_store;
@@ -108,17 +120,20 @@ ReturnCode ES::ImportTmd(Context& context, const std::vector<u8>& tmd_bytes)
return ret;
ret = VerifyContainer(VerifyContainerType::TMD, VerifyMode::UpdateCertStore,
- context.title_import.tmd, cert_store);
+ context.title_import_export.tmd, cert_store);
if (ret != IPC_SUCCESS)
- {
- // Reset the import context so that further calls consider the state as invalid.
- context.title_import.tmd.SetBytes({});
return ret;
- }
- if (!InitImport(context.title_import.tmd.GetTitleId()))
+ if (!InitImport(context.title_import_export.tmd.GetTitleId()))
return ES_EIO;
+ // FIXME: ImportTmd does not use the ticket or the title key.
+ const auto ticket = DiscIO::FindSignedTicket(context.title_import_export.tmd.GetTitleId());
+ if (!ticket.IsValid())
+ return ES_NO_TICKET;
+ context.title_import_export.key = ticket.GetTitleKey(m_ios.GetIOSC());
+
+ context.title_import_export.valid = true;
return IPC_SUCCESS;
}
@@ -139,28 +154,28 @@ ReturnCode ES::ImportTitleInit(Context& context, const std::vector<u8>& tmd_byte
const std::vector<u8>& cert_chain)
{
INFO_LOG(IOS_ES, "ImportTitleInit");
- context.title_import.tmd.SetBytes(tmd_bytes);
- if (!context.title_import.tmd.IsValid())
+ context.title_import_export = {};
+ context.title_import_export.tmd.SetBytes(tmd_bytes);
+ if (!context.title_import_export.tmd.IsValid())
{
ERROR_LOG(IOS_ES, "Invalid TMD while adding title (size = %zd)", tmd_bytes.size());
return ES_EINVAL;
}
// Finish a previous import (if it exists).
- FinishStaleImport(context.title_import.tmd.GetTitleId());
+ FinishStaleImport(context.title_import_export.tmd.GetTitleId());
ReturnCode ret = VerifyContainer(VerifyContainerType::TMD, VerifyMode::UpdateCertStore,
- context.title_import.tmd, cert_chain);
+ context.title_import_export.tmd, cert_chain);
if (ret != IPC_SUCCESS)
- {
- context.title_import.tmd.SetBytes({});
return ret;
- }
- const auto ticket = DiscIO::FindSignedTicket(context.title_import.tmd.GetTitleId());
+ const auto ticket = DiscIO::FindSignedTicket(context.title_import_export.tmd.GetTitleId());
if (!ticket.IsValid())
return ES_NO_TICKET;
+ context.title_import_export.key = ticket.GetTitleKey(m_ios.GetIOSC());
+
std::vector<u8> cert_store;
ret = ReadCertStore(&cert_store);
if (ret != IPC_SUCCESS)
@@ -169,14 +184,12 @@ ReturnCode ES::ImportTitleInit(Context& context, const std::vector<u8>& tmd_byte
ret = VerifyContainer(VerifyContainerType::Ticket, VerifyMode::DoNotUpdateCertStore, ticket,
cert_store);
if (ret != IPC_SUCCESS)
- {
- context.title_import.tmd.SetBytes({});
return ret;
- }
- if (!InitImport(context.title_import.tmd.GetTitleId()))
+ if (!InitImport(context.title_import_export.tmd.GetTitleId()))
return ES_EIO;
+ context.title_import_export.valid = true;
return IPC_SUCCESS;
}
@@ -197,30 +210,40 @@ IPCCommandResult ES::ImportTitleInit(Context& context, const IOCtlVRequest& requ
ReturnCode ES::ImportContentBegin(Context& context, u64 title_id, u32 content_id)
{
- if (context.title_import.content_id != 0xFFFFFFFF)
+ if (context.title_import_export.content.valid)
{
ERROR_LOG(IOS_ES, "Trying to add content when we haven't finished adding "
"another content. Unsupported.");
return ES_EINVAL;
}
- context.title_import.content_id = content_id;
-
- context.title_import.content_buffer.clear();
+ context.title_import_export.content = {};
+ context.title_import_export.content.id = content_id;
INFO_LOG(IOS_ES, "ImportContentBegin: title %016" PRIx64 ", content ID %08x", title_id,
- context.title_import.content_id);
+ context.title_import_export.content.id);
- if (!context.title_import.tmd.IsValid())
+ if (!context.title_import_export.valid)
return ES_EINVAL;
- if (title_id != context.title_import.tmd.GetTitleId())
+ if (title_id != context.title_import_export.tmd.GetTitleId())
{
ERROR_LOG(IOS_ES, "ImportContentBegin: title id %016" PRIx64 " != "
"TMD title id %016" PRIx64 ", ignoring",
- title_id, context.title_import.tmd.GetTitleId());
+ title_id, context.title_import_export.tmd.GetTitleId());
return ES_EINVAL;
}
+ // The IV for title content decryption is the lower two bytes of the
+ // content index, zero extended.
+ IOS::ES::Content content_info;
+ if (!context.title_import_export.tmd.FindContentById(context.title_import_export.content.id,
+ &content_info))
+ return ES_EINVAL;
+ context.title_import_export.content.iv[0] = (content_info.index >> 8) & 0xFF;
+ context.title_import_export.content.iv[1] = content_info.index & 0xFF;
+
+ context.title_import_export.content.valid = true;
+
// 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.
@@ -242,8 +265,8 @@ IPCCommandResult ES::ImportContentBegin(Context& context, const IOCtlVRequest& r
ReturnCode ES::ImportContentData(Context& context, u32 content_fd, const u8* data, u32 data_size)
{
INFO_LOG(IOS_ES, "ImportContentData: content fd %08x, size %d", content_fd, data_size);
- context.title_import.content_buffer.insert(context.title_import.content_buffer.end(), data,
- data + data_size);
+ context.title_import_export.content.buffer.insert(
+ context.title_import_export.content.buffer.end(), data, data + data_size);
return IPC_SUCCESS;
}
@@ -274,29 +297,16 @@ ReturnCode ES::ImportContentEnd(Context& context, u32 content_fd)
{
INFO_LOG(IOS_ES, "ImportContentEnd: content fd %08x", content_fd);
- if (context.title_import.content_id == 0xFFFFFFFF)
- return ES_EINVAL;
-
- if (!context.title_import.tmd.IsValid())
- return ES_EINVAL;
-
- // Try to find the title key from a pre-installed ticket.
- IOS::ES::TicketReader ticket = DiscIO::FindSignedTicket(context.title_import.tmd.GetTitleId());
- if (!ticket.IsValid())
- return ES_NO_TICKET;
-
- // The IV for title content decryption is the lower two bytes of the
- // content index, zero extended.
- IOS::ES::Content content_info;
- if (!context.title_import.tmd.FindContentById(context.title_import.content_id, &content_info))
+ if (!context.title_import_export.valid || !context.title_import_export.content.valid)
return ES_EINVAL;
- u8 iv[16] = {0};
- iv[0] = (content_info.index >> 8) & 0xFF;
- iv[1] = content_info.index & 0xFF;
std::vector<u8> decrypted_data = Common::AES::Decrypt(
- ticket.GetTitleKey(m_ios.GetIOSC()).data(), iv, context.title_import.content_buffer.data(),
- context.title_import.content_buffer.size());
+ context.title_import_export.key.data(), context.title_import_export.content.iv.data(),
+ context.title_import_export.content.buffer.data(),
+ context.title_import_export.content.buffer.size());
+ IOS::ES::Content content_info;
+ context.title_import_export.tmd.FindContentById(context.title_import_export.content.id,
+ &content_info);
if (!CheckIfContentHashMatches(decrypted_data, content_info))
{
ERROR_LOG(IOS_ES, "ImportContentEnd: Hash for content %08x doesn't match", content_info.id);
@@ -311,13 +321,14 @@ ReturnCode ES::ImportContentEnd(Context& context, u32 content_fd)
}
else
{
- content_path = GetImportContentPath(context.title_import.tmd.GetTitleId(),
- context.title_import.content_id);
+ content_path = GetImportContentPath(context.title_import_export.tmd.GetTitleId(),
+ context.title_import_export.content.id);
}
File::CreateFullPath(content_path);
- const std::string temp_path = Common::RootUserPath(Common::FROM_SESSION_ROOT) +
- StringFromFormat("/tmp/%08x.app", context.title_import.content_id);
+ const std::string temp_path =
+ Common::RootUserPath(Common::FROM_SESSION_ROOT) +
+ StringFromFormat("/tmp/%08x.app", context.title_import_export.content.id);
File::CreateFullPath(temp_path);
{
@@ -335,7 +346,7 @@ ReturnCode ES::ImportContentEnd(Context& context, u32 content_fd)
return ES_EIO;
}
- context.title_import.content_id = 0xFFFFFFFF;
+ context.title_import_export.content = {};
return IPC_SUCCESS;
}
@@ -350,12 +361,12 @@ IPCCommandResult ES::ImportContentEnd(Context& context, const IOCtlVRequest& req
ReturnCode ES::ImportTitleDone(Context& context)
{
- if (!context.title_import.tmd.IsValid() || context.title_import.content_id != 0xFFFFFFFF)
+ if (!context.title_import_export.valid || context.title_import_export.content.valid)
return ES_EINVAL;
// Make sure all listed, non-optional contents have been imported.
- const u64 title_id = context.title_import.tmd.GetTitleId();
- const std::vector<IOS::ES::Content> contents = context.title_import.tmd.GetContents();
+ const u64 title_id = context.title_import_export.tmd.GetTitleId();
+ const std::vector<IOS::ES::Content> contents = context.title_import_export.tmd.GetContents();
const IOS::ES::SharedContentMap shared_content_map{Common::FROM_SESSION_ROOT};
const bool has_all_required_contents =
std::all_of(contents.cbegin(), contents.cend(), [&](const IOS::ES::Content& content) {
@@ -373,14 +384,14 @@ ReturnCode ES::ImportTitleDone(Context& context)
if (!has_all_required_contents)
return ES_EINVAL;
- if (!WriteImportTMD(context.title_import.tmd))
+ if (!WriteImportTMD(context.title_import_export.tmd))
return ES_EIO;
- if (!FinishImport(context.title_import.tmd))
+ if (!FinishImport(context.title_import_export.tmd))
return ES_EIO;
INFO_LOG(IOS_ES, "ImportTitleDone: title %016" PRIx64, title_id);
- context.title_import.tmd.SetBytes({});
+ context.title_import_export = {};
return IPC_SUCCESS;
}
@@ -394,19 +405,25 @@ IPCCommandResult ES::ImportTitleDone(Context& context, const IOCtlVRequest& requ
ReturnCode ES::ImportTitleCancel(Context& context)
{
- if (!context.title_import.tmd.IsValid())
+ // The TMD buffer can exist without a valid title import context.
+ if (context.title_import_export.tmd.GetBytes().empty() ||
+ context.title_import_export.content.valid)
return ES_EINVAL;
- FinishStaleImport(context.title_import.tmd.GetTitleId());
+ if (context.title_import_export.valid)
+ {
+ const u64 title_id = context.title_import_export.tmd.GetTitleId();
+ FinishStaleImport(title_id);
+ INFO_LOG(IOS_ES, "ImportTitleCancel: title %016" PRIx64, title_id);
+ }
- INFO_LOG(IOS_ES, "ImportTitleCancel: title %016" PRIx64, context.title_import.tmd.GetTitleId());
- context.title_import.tmd.SetBytes({});
+ context.title_import_export = {};
return IPC_SUCCESS;
}
IPCCommandResult ES::ImportTitleCancel(Context& context, const IOCtlVRequest& request)
{
- if (!request.HasNumberOfValidVectors(0, 0) || !context.title_import.tmd.IsValid())
+ if (!request.HasNumberOfValidVectors(0, 0))
return GetDefaultReply(ES_EINVAL);
return GetDefaultReply(ImportTitleCancel(context));
@@ -555,30 +572,32 @@ IPCCommandResult ES::DeleteContent(const IOCtlVRequest& request)
ReturnCode ES::ExportTitleInit(Context& context, u64 title_id, u8* tmd_bytes, u32 tmd_size)
{
// No concurrent title import/export is allowed.
- if (context.title_export.valid)
+ if (context.title_import_export.valid)
return ES_EINVAL;
const auto tmd = FindInstalledTMD(title_id);
if (!tmd.IsValid())
return FS_ENOENT;
- context.title_export.tmd = tmd;
+ context.title_import_export = {};
+ context.title_import_export.tmd = tmd;
- const auto ticket = DiscIO::FindSignedTicket(context.title_export.tmd.GetTitleId());
+ const auto ticket = DiscIO::FindSignedTicket(context.title_import_export.tmd.GetTitleId());
if (!ticket.IsValid())
return ES_NO_TICKET;
- if (ticket.GetTitleId() != context.title_export.tmd.GetTitleId())
+ if (ticket.GetTitleId() != context.title_import_export.tmd.GetTitleId())
return ES_EINVAL;
- context.title_export.title_key = ticket.GetTitleKey(m_ios.GetIOSC());
+ // FIXME: this is wrong. The title key is *not* used here. Key #5 or a null key is.
+ context.title_import_export.key = ticket.GetTitleKey(m_ios.GetIOSC());
- const std::vector<u8>& raw_tmd = context.title_export.tmd.GetBytes();
+ const std::vector<u8>& raw_tmd = context.title_import_export.tmd.GetBytes();
if (tmd_size != raw_tmd.size())
return ES_EINVAL;
std::copy_n(raw_tmd.cbegin(), raw_tmd.size(), tmd_bytes);
- context.title_export.valid = true;
+ context.title_import_export.valid = true;
return IPC_SUCCESS;
}
@@ -596,38 +615,33 @@ IPCCommandResult ES::ExportTitleInit(Context& context, const IOCtlVRequest& requ
ReturnCode ES::ExportContentBegin(Context& context, u64 title_id, u32 content_id)
{
- if (!context.title_export.valid || context.title_export.tmd.GetTitleId() != title_id)
+ context.title_import_export.content = {};
+ if (!context.title_import_export.valid ||
+ context.title_import_export.tmd.GetTitleId() != title_id)
{
ERROR_LOG(IOS_ES, "Tried to use ExportContentBegin with an invalid title export context.");
return ES_EINVAL;
}
- const auto& content_loader = AccessContentDevice(title_id);
- if (!content_loader.IsValid())
- return FS_ENOENT;
-
- const auto* content = content_loader.GetContentByID(content_id);
- if (!content)
+ IOS::ES::Content content_info;
+ if (!context.title_import_export.tmd.FindContentById(content_id, &content_info))
return ES_EINVAL;
- OpenedContent entry;
- entry.m_position = 0;
- entry.m_content = content->m_metadata;
- entry.m_title_id = title_id;
- content->m_Data->Open();
+ context.title_import_export.content.id = content_id;
+ context.title_import_export.content.valid = true;
- u32 cfd = 0;
- while (context.title_export.contents.find(cfd) != context.title_export.contents.end())
- cfd++;
+ const s32 ret = OpenContent(context.title_import_export.tmd, content_info.index, 0);
+ if (ret < 0)
+ {
+ context.title_import_export = {};
+ return static_cast<ReturnCode>(ret);
+ }
- 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;
+ context.title_import_export.content.iv[0] = (content_info.index >> 8) & 0xFF;
+ context.title_import_export.content.iv[1] = content_info.index & 0xFF;
- context.title_export.contents.emplace(cfd, content_export);
// IOS returns a content ID which is passed to further content calls.
- return static_cast<ReturnCode>(cfd);
+ return static_cast<ReturnCode>(ret);
}
IPCCommandResult ES::ExportContentBegin(Context& context, const IOCtlVRequest& request)
@@ -644,26 +658,20 @@ IPCCommandResult ES::ExportContentBegin(Context& context, const IOCtlVRequest& r
ReturnCode ES::ExportContentData(Context& context, u32 content_fd, u8* data, u32 data_size)
{
- const auto iterator = context.title_export.contents.find(content_fd);
- if (!context.title_export.valid || iterator == context.title_export.contents.end() ||
- iterator->second.content.m_position >= iterator->second.content.m_content.size)
+ if (!context.title_import_export.valid || !context.title_import_export.content.valid || !data ||
+ data_size == 0)
{
+ CloseContent(content_fd, 0);
+ context.title_import_export = {};
return ES_EINVAL;
}
- 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<u32>(metadata.m_content.size - metadata.m_position), data_size);
- std::vector<u8> buffer(length);
-
- if (!content->m_Data->GetRange(metadata.m_position, length, buffer.data()))
+ std::vector<u8> buffer(data_size);
+ const s32 read_size = ReadContent(content_fd, buffer.data(), static_cast<u32>(buffer.size()), 0);
+ if (read_size < 0)
{
- ERROR_LOG(IOS_ES, "ExportContentData: ES_SHORT_READ");
+ CloseContent(content_fd, 0);
+ context.title_import_export = {};
return ES_SHORT_READ;
}
@@ -671,11 +679,10 @@ ReturnCode ES::ExportContentData(Context& context, u32 content_fd, u8* data, u32
// let's just follow IOS here.
buffer.resize(Common::AlignUp(buffer.size(), 32));
- const std::vector<u8> output =
- Common::AES::Encrypt(context.title_export.title_key.data(), iterator->second.iv.data(),
- buffer.data(), buffer.size());
- std::copy_n(output.cbegin(), output.size(), data);
- metadata.m_position += length;
+ const std::vector<u8> output = Common::AES::Encrypt(context.title_import_export.key.data(),
+ context.title_import_export.content.iv.data(),
+ buffer.data(), buffer.size());
+ std::copy(output.cbegin(), output.cend(), data);
return IPC_SUCCESS;
}
@@ -696,20 +703,9 @@ IPCCommandResult ES::ExportContentData(Context& context, const IOCtlVRequest& re
ReturnCode ES::ExportContentEnd(Context& context, u32 content_fd)
{
- const auto iterator = context.title_export.contents.find(content_fd);
- if (!context.title_export.valid || iterator == context.title_export.contents.end() ||
- iterator->second.content.m_position != iterator->second.content.m_content.size)
- {
+ if (!context.title_import_export.valid || !context.title_import_export.content.valid)
return ES_EINVAL;
- }
-
- // 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();
-
- context.title_export.contents.erase(iterator);
- return IPC_SUCCESS;
+ return CloseContent(content_fd, 0);
}
IPCCommandResult ES::ExportContentEnd(Context& context, const IOCtlVRequest& request)
@@ -723,10 +719,7 @@ IPCCommandResult ES::ExportContentEnd(Context& context, const IOCtlVRequest& req
ReturnCode ES::ExportTitleDone(Context& context)
{
- if (!context.title_export.valid)
- return ES_EINVAL;
-
- context.title_export.valid = false;
+ context.title_import_export = {};
return IPC_SUCCESS;
}
diff --git a/Source/Core/Core/State.cpp b/Source/Core/Core/State.cpp
index bc2a0f20aa..5fbc446e16 100644
--- a/Source/Core/Core/State.cpp
+++ b/Source/Core/Core/State.cpp
@@ -73,7 +73,7 @@ static Common::Event g_compressAndDumpStateSyncEvent;
static std::thread g_save_thread;
// Don't forget to increase this after doing changes on the savestate system
-static const u32 STATE_VERSION = 87; // Last changed in PR 5707
+static const u32 STATE_VERSION = 88; // Last changed in PR 5733
// Maps savestate versions to Dolphin versions.
// Versions after 42 don't need to be added to this list,