WiiUtils: Check hashes to determine if a title is installed and up-to-date
Nintendo's official title installation code and ES both only look at content IDs but we should probably check for content hashes in addition to checking for IDs for at least two reasons: 1. Some of the installed contents could be corrupted -- this cannot be easily detected without checking hashes. 2. Some mod distributors do not bother to update content IDs, which means that installing updates from the UI would not actually update the installed game. This is confusing for users. To keep the existing semantic (for IOS especially), the new content hash checks are opt-in for callers of GetStoredContentsFromTMD. This commit changes WiiUtils's WAD installation logic to enable the content hash checks.
This commit is contained in:
parent
2952f99f69
commit
f7d7bbf55f
|
@ -81,6 +81,12 @@ public:
|
|||
s32 ipc_fd = -1;
|
||||
};
|
||||
|
||||
enum class CheckContentHashes : bool
|
||||
{
|
||||
Yes = true,
|
||||
No = false,
|
||||
};
|
||||
|
||||
IOS::ES::TMDReader FindImportTMD(u64 title_id) const;
|
||||
IOS::ES::TMDReader FindInstalledTMD(u64 title_id) const;
|
||||
IOS::ES::TicketReader FindSignedTicket(u64 title_id) const;
|
||||
|
@ -92,7 +98,9 @@ public:
|
|||
// Get titles for which there is a ticket (in /ticket).
|
||||
std::vector<u64> GetTitlesWithTickets() const;
|
||||
|
||||
std::vector<IOS::ES::Content> GetStoredContentsFromTMD(const IOS::ES::TMDReader& tmd) const;
|
||||
std::vector<IOS::ES::Content>
|
||||
GetStoredContentsFromTMD(const IOS::ES::TMDReader& tmd,
|
||||
CheckContentHashes check_content_hashes = CheckContentHashes::No) const;
|
||||
u32 GetSharedContentsCount() const;
|
||||
std::vector<std::array<u8, 20>> GetSharedContents() const;
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <vector>
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <mbedtls/sha1.h>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
|
@ -163,7 +164,9 @@ std::vector<u64> ES::GetTitlesWithTickets() const
|
|||
return title_ids;
|
||||
}
|
||||
|
||||
std::vector<IOS::ES::Content> ES::GetStoredContentsFromTMD(const IOS::ES::TMDReader& tmd) const
|
||||
std::vector<IOS::ES::Content>
|
||||
ES::GetStoredContentsFromTMD(const IOS::ES::TMDReader& tmd,
|
||||
CheckContentHashes check_content_hashes) const
|
||||
{
|
||||
if (!tmd.IsValid())
|
||||
return {};
|
||||
|
@ -174,10 +177,29 @@ std::vector<IOS::ES::Content> ES::GetStoredContentsFromTMD(const IOS::ES::TMDRea
|
|||
std::vector<IOS::ES::Content> stored_contents;
|
||||
|
||||
std::copy_if(contents.begin(), contents.end(), std::back_inserter(stored_contents),
|
||||
[this, &tmd, &map](const IOS::ES::Content& content) {
|
||||
[this, &tmd, &map, check_content_hashes](const IOS::ES::Content& content) {
|
||||
const auto fs = m_ios.GetFS();
|
||||
|
||||
const std::string path = GetContentPath(tmd.GetTitleId(), content, map);
|
||||
return !path.empty() &&
|
||||
m_ios.GetFS()->GetMetadata(PID_KERNEL, PID_KERNEL, path).Succeeded();
|
||||
if (path.empty())
|
||||
return false;
|
||||
|
||||
// Check whether the content file exists.
|
||||
const auto file = fs->OpenFile(PID_KERNEL, PID_KERNEL, path, FS::Mode::Read);
|
||||
if (!file.Succeeded())
|
||||
return false;
|
||||
|
||||
// If content hash checks are disabled, all we have to do is check for existence.
|
||||
if (check_content_hashes == CheckContentHashes::No)
|
||||
return true;
|
||||
|
||||
// Otherwise, check whether the installed content SHA1 matches the expected hash.
|
||||
std::vector<u8> content_data(file->GetStatus()->size);
|
||||
if (!file->Read(content_data.data(), content_data.size()))
|
||||
return false;
|
||||
std::array<u8, 20> sha1{};
|
||||
mbedtls_sha1_ret(content_data.data(), content_data.size(), sha1.data());
|
||||
return sha1 == content.sha1;
|
||||
});
|
||||
|
||||
return stored_contents;
|
||||
|
|
|
@ -205,6 +205,7 @@ public:
|
|||
/// Reposition the file offset for a file descriptor.
|
||||
virtual Result<u32> SeekFile(Fd fd, u32 offset, SeekMode mode) = 0;
|
||||
/// Get status for a file descriptor.
|
||||
/// Guaranteed to succeed for a valid file descriptor.
|
||||
virtual Result<FileStatus> GetFileStatus(Fd fd) = 0;
|
||||
|
||||
/// Create a file with the specified path and metadata.
|
||||
|
|
|
@ -149,7 +149,8 @@ bool InstallWAD(IOS::HLE::Kernel& ios, const DiscIO::VolumeWAD& wad, InstallType
|
|||
const u64 title_id = wad.GetTMD().GetTitleId();
|
||||
|
||||
// Skip the install if the WAD is already installed.
|
||||
const auto installed_contents = ios.GetES()->GetStoredContentsFromTMD(wad.GetTMD());
|
||||
const auto installed_contents = ios.GetES()->GetStoredContentsFromTMD(
|
||||
wad.GetTMD(), IOS::HLE::Device::ES::CheckContentHashes::Yes);
|
||||
if (wad.GetTMD().GetContents() == installed_contents)
|
||||
{
|
||||
// Clear the "temporary title ID" flag in case the user tries to permanently install a title
|
||||
|
|
Loading…
Reference in New Issue