diff --git a/Source/Core/Core/IOS/ES/ES.cpp b/Source/Core/Core/IOS/ES/ES.cpp index d3f162cc1d..75eb42f58e 100644 --- a/Source/Core/Core/IOS/ES/ES.cpp +++ b/Source/Core/Core/IOS/ES/ES.cpp @@ -383,10 +383,16 @@ IPCCommandResult ES::IOCtlV(const IOCtlVRequest& request) return GetTitleID(request); case IOCTL_ES_SETUID: return SetUID(request); + + case IOCTL_ES_GETOWNEDTITLECNT: + return GetOwnedTitleCount(request); + case IOCTL_ES_GETOWNEDTITLES: + return GetOwnedTitles(request); case IOCTL_ES_GETTITLECNT: return GetTitleCount(request); case IOCTL_ES_GETTITLES: return GetTitles(request); + case IOCTL_ES_GETVIEWCNT: return GetViewCount(request); case IOCTL_ES_GETVIEWS: @@ -435,8 +441,6 @@ IPCCommandResult ES::IOCtlV(const IOCtlVRequest& request) return GetBoot2Version(request); case IOCTL_ES_DIGETTICKETVIEW: return DIGetTicketView(request); - case IOCTL_ES_GETOWNEDTITLECNT: - return GetOwnedTitleCount(request); default: request.DumpUnknown(GetDeviceName(), LogTypes::IOS); break; @@ -927,6 +931,47 @@ static std::vector GetInstalledTitles() 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 IOCtlVRequest& request) { if (!request.HasNumberOfValidVectors(0, 1) || request.io_vectors[0].size != 4) @@ -1529,9 +1574,26 @@ IPCCommandResult ES::GetOwnedTitleCount(const IOCtlVRequest& request) if (!request.HasNumberOfValidVectors(0, 1)) return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT); - INFO_LOG(IOS_ES, "IOCTL_ES_GETOWNEDTITLECNT"); - // TODO: unimplemented. - Memory::Write_U32(0, request.io_vectors[0].address); + const std::vector titles = GetTitlesWithTickets(); + Memory::Write_U32(static_cast(titles.size()), request.io_vectors[0].address); + + INFO_LOG(IOS_ES, "GetOwnedTitleCount: %zu titles", titles.size()); + return GetDefaultReply(IPC_SUCCESS); +} + +IPCCommandResult ES::GetOwnedTitles(const IOCtlVRequest& request) +{ + if (!request.HasNumberOfValidVectors(1, 1)) + return GetDefaultReply(ES_PARAMETER_SIZE_OR_ALIGNMENT); + + const std::vector titles = GetTitlesWithTickets(); + + 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) * 8); + INFO_LOG(IOS_ES, " title %016" PRIx64, titles[i]); + } return GetDefaultReply(IPC_SUCCESS); } diff --git a/Source/Core/Core/IOS/ES/ES.h b/Source/Core/Core/IOS/ES/ES.h index 8adb54e8e9..e7bbc76867 100644 --- a/Source/Core/Core/IOS/ES/ES.h +++ b/Source/Core/Core/IOS/ES/ES.h @@ -167,8 +167,12 @@ private: IPCCommandResult GetTitleDirectory(const IOCtlVRequest& request); IPCCommandResult GetTitleID(const IOCtlVRequest& request); IPCCommandResult SetUID(const IOCtlVRequest& request); + + IPCCommandResult GetOwnedTitleCount(const IOCtlVRequest& request); + IPCCommandResult GetOwnedTitles(const IOCtlVRequest& request); IPCCommandResult GetTitleCount(const IOCtlVRequest& request); IPCCommandResult GetTitles(const IOCtlVRequest& request); + IPCCommandResult GetViewCount(const IOCtlVRequest& request); IPCCommandResult GetViews(const IOCtlVRequest& request); IPCCommandResult GetTMDViewSize(const IOCtlVRequest& request); @@ -195,7 +199,6 @@ private: IPCCommandResult Sign(const IOCtlVRequest& request); IPCCommandResult GetBoot2Version(const IOCtlVRequest& request); IPCCommandResult DIGetTicketView(const IOCtlVRequest& request); - IPCCommandResult GetOwnedTitleCount(const IOCtlVRequest& request); static bool LaunchIOS(u64 ios_title_id); static bool LaunchPPCTitle(u64 title_id, bool skip_reload); diff --git a/Source/Core/DiscIO/NANDContentLoader.cpp b/Source/Core/DiscIO/NANDContentLoader.cpp index 914e23191e..c08e5bbef8 100644 --- a/Source/Core/DiscIO/NANDContentLoader.cpp +++ b/Source/Core/DiscIO/NANDContentLoader.cpp @@ -158,7 +158,7 @@ CNANDContentLoader::~CNANDContentLoader() bool CNANDContentLoader::IsValid() const { - return m_Valid && m_tmd.IsValid(); + return m_Valid; } const SNANDContent* CNANDContentLoader::GetContentByID(u32 id) const