diff --git a/Source/Core/Core/WiiUtils.cpp b/Source/Core/Core/WiiUtils.cpp index 0abc8de162..07db5c7210 100644 --- a/Source/Core/Core/WiiUtils.cpp +++ b/Source/Core/Core/WiiUtils.cpp @@ -19,6 +19,7 @@ #include #include +#include "Common/Align.h" #include "Common/Assert.h" #include "Common/CommonTypes.h" #include "Common/FileUtil.h" @@ -223,6 +224,67 @@ bool IsTitleInstalled(u64 title_id) [](const std::string& file) { return file != "title.tmd"; }); } +bool IsTMDImported(IOS::HLE::FS::FileSystem& fs, u64 title_id) +{ + const auto entries = fs.ReadDirectory(0, 0, Common::GetTitleContentPath(title_id)); + return entries && std::any_of(entries->begin(), entries->end(), + [](const std::string& file) { return file == "title.tmd"; }); +} + +IOS::ES::TMDReader FindBackupTMD(IOS::HLE::FS::FileSystem& fs, u64 title_id) +{ + auto file = fs.OpenFile(IOS::PID_KERNEL, IOS::PID_KERNEL, + "/title/00000001/00000002/data/tmds.sys", IOS::HLE::FS::Mode::Read); + if (!file) + return {}; + + // structure of this file is as follows: + // - 32 bytes descriptor of a TMD, which contains a title ID and a length + // - the TMD, with padding aligning to 32 bytes + // - repeat for as many TMDs as stored + while (true) + { + std::array descriptor; + if (!file->Read(descriptor.data(), descriptor.size())) + return {}; + + const u64 tid = Common::swap64(descriptor.data()); + const u32 tmd_length = Common::swap32(descriptor.data() + 8); + if (tid == title_id) + { + // found the right TMD + std::vector tmd_bytes(tmd_length); + if (!file->Read(tmd_bytes.data(), tmd_length)) + return {}; + return IOS::ES::TMDReader(std::move(tmd_bytes)); + } + + // not the right TMD, skip this one and go to the next + if (!file->Seek(Common::AlignUp(tmd_length, 32), IOS::HLE::FS::SeekMode::Current)) + return {}; + } +} + +bool EnsureTMDIsImported(IOS::HLE::FS::FileSystem& fs, IOS::HLE::Device::ES& es, u64 title_id) +{ + if (IsTMDImported(fs, title_id)) + return true; + + auto tmd = FindBackupTMD(fs, title_id); + if (!tmd.IsValid()) + return false; + + IOS::HLE::Device::ES::Context context; + context.uid = IOS::SYSMENU_UID; + context.gid = IOS::SYSMENU_GID; + const auto import_result = + es.ImportTmd(context, tmd.GetBytes(), Titles::SYSTEM_MENU, IOS::ES::TITLE_TYPE_DEFAULT); + if (import_result != IOS::HLE::IPC_SUCCESS) + return false; + + return es.ImportTitleDone(context) == IOS::HLE::IPC_SUCCESS; +} + // Common functionality for system updaters. class SystemUpdater { diff --git a/Source/Core/Core/WiiUtils.h b/Source/Core/Core/WiiUtils.h index 3936dfac8a..1c496b92ee 100644 --- a/Source/Core/Core/WiiUtils.h +++ b/Source/Core/Core/WiiUtils.h @@ -6,10 +6,12 @@ #include #include +#include #include #include #include "Common/CommonTypes.h" +#include "Core/IOS/ES/Formats.h" // Small utility functions for common Wii related tasks. @@ -23,6 +25,16 @@ namespace IOS::HLE class Kernel; } +namespace IOS::HLE::FS +{ +class FileSystem; +} + +namespace IOS::HLE::Device +{ +class ES; +} + namespace WiiUtils { enum class InstallType @@ -40,6 +52,18 @@ bool UninstallTitle(u64 title_id); bool IsTitleInstalled(u64 title_id); +// Checks if there's a title.tmd imported for the given title ID. +bool IsTMDImported(IOS::HLE::FS::FileSystem& fs, u64 title_id); + +// Searches for a TMD matching the given title ID in /title/00000001/00000002/data/tmds.sys. +// Returns it if it exists, otherwise returns an empty invalid TMD. +IOS::ES::TMDReader FindBackupTMD(IOS::HLE::FS::FileSystem& fs, u64 title_id); + +// Checks if there's a title.tmd imported for the given title ID. If there is not, we attempt to +// re-import it from the TMDs stored in /title/00000001/00000002/data/tmds.sys. +// Returns true if, after this function call, we have an imported title.tmd, or false if not. +bool EnsureTMDIsImported(IOS::HLE::FS::FileSystem& fs, IOS::HLE::Device::ES& es, u64 title_id); + enum class UpdateResult { Succeeded,