mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-31 01:59:52 -06:00
Make netplay's "same game" check more robust
Instead of comparing the game ID, revision, disc number and name, we can compare a hash of important parts of the disc including all the aforementioned data but also additional data such as the FST. The primary reason why I'm making this change is to let us catch more desyncs before they happen, but this should also fix https://bugs.dolphin-emu.org/issues/12115. As a bonus, the UI can now distinguish the case where a client doesn't have the game at all from the case where a client has the wrong version of the game.
This commit is contained in:
@ -5,6 +5,7 @@
|
||||
#include "UICommon/GameFile.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cinttypes>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
@ -13,11 +14,13 @@
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <mbedtls/sha1.h>
|
||||
#include <pugixml.hpp>
|
||||
|
||||
#include "Common/ChunkFile.h"
|
||||
@ -532,7 +535,7 @@ std::vector<DiscIO::Language> GameFile::GetLanguages() const
|
||||
return languages;
|
||||
}
|
||||
|
||||
std::string GameFile::GetUniqueIdentifier() const
|
||||
std::string GameFile::GetNetPlayName() const
|
||||
{
|
||||
std::vector<std::string> info;
|
||||
if (!GetGameID().empty())
|
||||
@ -566,6 +569,66 @@ std::string GameFile::GetUniqueIdentifier() const
|
||||
return name + " (" + ss.str() + ")";
|
||||
}
|
||||
|
||||
std::array<u8, 20> GameFile::GetSyncHash() const
|
||||
{
|
||||
std::array<u8, 20> hash{};
|
||||
|
||||
if (m_platform == DiscIO::Platform::ELFOrDOL)
|
||||
{
|
||||
std::string buffer;
|
||||
if (File::ReadFileToString(m_file_path, buffer))
|
||||
mbedtls_sha1_ret(reinterpret_cast<unsigned char*>(buffer.data()), buffer.size(), hash.data());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (std::unique_ptr<DiscIO::Volume> volume = DiscIO::CreateVolume(m_file_path))
|
||||
hash = volume->GetSyncHash();
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
NetPlay::SyncIdentifier GameFile::GetSyncIdentifier() const
|
||||
{
|
||||
const u64 dol_elf_size = m_platform == DiscIO::Platform::ELFOrDOL ? m_file_size : 0;
|
||||
return NetPlay::SyncIdentifier{dol_elf_size, m_game_id, m_revision,
|
||||
m_disc_number, m_is_datel_disc, GetSyncHash()};
|
||||
}
|
||||
|
||||
NetPlay::SyncIdentifierComparison
|
||||
GameFile::CompareSyncIdentifier(const NetPlay::SyncIdentifier& sync_identifier) const
|
||||
{
|
||||
const bool is_elf_or_dol = m_platform == DiscIO::Platform::ELFOrDOL;
|
||||
if ((is_elf_or_dol ? m_file_size : 0) != sync_identifier.dol_elf_size)
|
||||
return NetPlay::SyncIdentifierComparison::DifferentGame;
|
||||
|
||||
const auto trim = [](const std::string& str, size_t n) {
|
||||
return std::string_view(str.data(), std::min(n, str.size()));
|
||||
};
|
||||
|
||||
if (trim(m_game_id, 3) != trim(sync_identifier.game_id, 3))
|
||||
return NetPlay::SyncIdentifierComparison::DifferentGame;
|
||||
|
||||
if (m_disc_number != sync_identifier.disc_number || m_is_datel_disc != sync_identifier.is_datel)
|
||||
return NetPlay::SyncIdentifierComparison::DifferentGame;
|
||||
|
||||
const NetPlay::SyncIdentifierComparison mismatch_result =
|
||||
is_elf_or_dol || m_is_datel_disc ? NetPlay::SyncIdentifierComparison::DifferentGame :
|
||||
NetPlay::SyncIdentifierComparison::DifferentVersion;
|
||||
|
||||
if (m_game_id != sync_identifier.game_id)
|
||||
{
|
||||
const bool game_id_is_title_id = m_game_id.size() > 6 || sync_identifier.game_id.size() > 6;
|
||||
return game_id_is_title_id ? NetPlay::SyncIdentifierComparison::DifferentGame : mismatch_result;
|
||||
}
|
||||
|
||||
if (m_revision != sync_identifier.revision)
|
||||
return mismatch_result;
|
||||
|
||||
return GetSyncHash() == sync_identifier.sync_hash ? NetPlay::SyncIdentifierComparison::SameGame :
|
||||
mismatch_result;
|
||||
}
|
||||
|
||||
std::string GameFile::GetWiiFSPath() const
|
||||
{
|
||||
ASSERT(DiscIO::IsWii(m_platform));
|
||||
|
@ -4,11 +4,13 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Core/SyncIdentifier.h"
|
||||
#include "DiscIO/Blob.h"
|
||||
#include "DiscIO/Enums.h"
|
||||
|
||||
@ -80,7 +82,16 @@ public:
|
||||
u16 GetRevision() const { return m_revision; }
|
||||
// 0 is the first disc, 1 is the second disc
|
||||
u8 GetDiscNumber() const { return m_disc_number; }
|
||||
std::string GetUniqueIdentifier() const;
|
||||
std::string GetNetPlayName() const;
|
||||
|
||||
// This function is slow
|
||||
std::array<u8, 20> GetSyncHash() const;
|
||||
// This function is slow
|
||||
NetPlay::SyncIdentifier GetSyncIdentifier() const;
|
||||
// This function is slow if all of game_id, revision, disc_number, is_datel are identical
|
||||
NetPlay::SyncIdentifierComparison
|
||||
CompareSyncIdentifier(const NetPlay::SyncIdentifier& sync_identifier) const;
|
||||
|
||||
std::string GetWiiFSPath() const;
|
||||
DiscIO::Region GetRegion() const { return m_region; }
|
||||
DiscIO::Country GetCountry() const { return m_country; }
|
||||
|
Reference in New Issue
Block a user