diff --git a/Source/Core/Core/IOS/ES/Formats.cpp b/Source/Core/Core/IOS/ES/Formats.cpp index 8790dd2ff1..214fd8a31f 100644 --- a/Source/Core/Core/IOS/ES/Formats.cpp +++ b/Source/Core/Core/IOS/ES/Formats.cpp @@ -61,6 +61,11 @@ bool Content::IsShared() const return (type & 0x8000) != 0; } +bool Content::IsOptional() const +{ + return (type & 0x4000) != 0; +} + SignedBlobReader::SignedBlobReader(const std::vector& bytes) : m_bytes(bytes) { } diff --git a/Source/Core/Core/IOS/ES/Formats.h b/Source/Core/Core/IOS/ES/Formats.h index 943f93d8da..dd1352b367 100644 --- a/Source/Core/Core/IOS/ES/Formats.h +++ b/Source/Core/Core/IOS/ES/Formats.h @@ -83,6 +83,7 @@ static_assert(sizeof(TMDHeader) == 0x1e4, "TMDHeader has the wrong size"); struct Content { bool IsShared() const; + bool IsOptional() const; u32 id; u16 index; u16 type; diff --git a/Source/Core/Core/IOS/ES/TitleManagement.cpp b/Source/Core/Core/IOS/ES/TitleManagement.cpp index 7a41fd1c88..143c4a86d3 100644 --- a/Source/Core/Core/IOS/ES/TitleManagement.cpp +++ b/Source/Core/Core/IOS/ES/TitleManagement.cpp @@ -350,7 +350,25 @@ IPCCommandResult ES::ImportContentEnd(Context& context, const IOCtlVRequest& req ReturnCode ES::ImportTitleDone(Context& context) { - if (!context.title_import.tmd.IsValid()) + if (!context.title_import.tmd.IsValid() || context.title_import.content_id != 0xFFFFFFFF) + 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 contents = context.title_import.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) { + if (content.IsOptional()) + return true; + + if (content.IsShared()) + return shared_content_map.GetFilenameFromSHA1(content.sha1).has_value(); + + return File::Exists(Common::GetTitleContentPath(title_id, Common::FROM_SESSION_ROOT) + + StringFromFormat("%08x.app", content.id)); + }); + if (!has_all_required_contents) return ES_EINVAL; if (!WriteImportTMD(context.title_import.tmd)) @@ -359,7 +377,7 @@ ReturnCode ES::ImportTitleDone(Context& context) if (!FinishImport(context.title_import.tmd)) return ES_EIO; - INFO_LOG(IOS_ES, "ImportTitleDone: title %016" PRIx64, context.title_import.tmd.GetTitleId()); + INFO_LOG(IOS_ES, "ImportTitleDone: title %016" PRIx64, title_id); context.title_import.tmd.SetBytes({}); return IPC_SUCCESS; }