dolphin/Source/Core/DiscIO/Volume.cpp
JosJuice a41166bb37 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.
2020-08-02 22:46:53 +02:00

146 lines
4.2 KiB
C++

// Copyright 2009 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "DiscIO/Volume.h"
#include <algorithm>
#include <map>
#include <memory>
#include <optional>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
#include <mbedtls/sha1.h>
#include "Common/CommonTypes.h"
#include "Common/StringUtil.h"
#include "Core/IOS/ES/Formats.h"
#include "DiscIO/Blob.h"
#include "DiscIO/Enums.h"
#include "DiscIO/VolumeDisc.h"
#include "DiscIO/VolumeGC.h"
#include "DiscIO/VolumeWad.h"
#include "DiscIO/VolumeWii.h"
namespace DiscIO
{
const IOS::ES::TicketReader Volume::INVALID_TICKET{};
const IOS::ES::TMDReader Volume::INVALID_TMD{};
const std::vector<u8> Volume::INVALID_CERT_CHAIN{};
template <typename T>
static void AddToSyncHash(mbedtls_sha1_context* context, const T& data)
{
static_assert(std::is_trivially_copyable_v<T>);
mbedtls_sha1_update_ret(context, reinterpret_cast<const u8*>(&data), sizeof(data));
}
void Volume::ReadAndAddToSyncHash(mbedtls_sha1_context* context, u64 offset, u64 length,
const Partition& partition) const
{
std::vector<u8> buffer(length);
if (Read(offset, length, buffer.data(), partition))
mbedtls_sha1_update_ret(context, buffer.data(), buffer.size());
}
void Volume::AddTMDToSyncHash(mbedtls_sha1_context* context, const Partition& partition) const
{
// We want to hash some important parts of the TMD, but nothing that changes when fakesigning.
// (Fakesigned WADs are very popular, and we don't want people with properly signed WADs to
// unnecessarily be at a disadvantage due to most netplay partners having fakesigned WADs.)
const IOS::ES::TMDReader& tmd = GetTMD(partition);
if (!tmd.IsValid())
return;
AddToSyncHash(context, tmd.GetIOSId());
AddToSyncHash(context, tmd.GetTitleId());
AddToSyncHash(context, tmd.GetTitleFlags());
AddToSyncHash(context, tmd.GetGroupId());
AddToSyncHash(context, tmd.GetRegion());
AddToSyncHash(context, tmd.GetTitleVersion());
AddToSyncHash(context, tmd.GetBootIndex());
for (const IOS::ES::Content& content : tmd.GetContents())
AddToSyncHash(context, content);
}
std::map<Language, std::string> Volume::ReadWiiNames(const std::vector<char16_t>& data)
{
std::map<Language, std::string> names;
for (size_t i = 0; i < NUMBER_OF_LANGUAGES; ++i)
{
const size_t name_start = NAME_CHARS_LENGTH * i;
if (name_start + NAME_CHARS_LENGTH <= data.size())
{
const std::string name = UTF16BEToUTF8(data.data() + name_start, NAME_CHARS_LENGTH);
if (!name.empty())
names[static_cast<Language>(i)] = name;
}
}
return names;
}
static std::unique_ptr<VolumeDisc> CreateDisc(std::unique_ptr<BlobReader>& reader)
{
// Check for Wii
const std::optional<u32> wii_magic = reader->ReadSwapped<u32>(0x18);
if (wii_magic == u32(0x5D1C9EA3))
return std::make_unique<VolumeWii>(std::move(reader));
// Check for GC
const std::optional<u32> gc_magic = reader->ReadSwapped<u32>(0x1C);
if (gc_magic == u32(0xC2339F3D))
return std::make_unique<VolumeGC>(std::move(reader));
// No known magic words found
return nullptr;
}
std::unique_ptr<VolumeDisc> CreateDisc(const std::string& path)
{
std::unique_ptr<BlobReader> reader(CreateBlobReader(path));
return reader ? CreateDisc(reader) : nullptr;
}
static std::unique_ptr<VolumeWAD> CreateWAD(std::unique_ptr<BlobReader>& reader)
{
// Check for WAD
// 0x206962 for boot2 wads
const std::optional<u32> wad_magic = reader->ReadSwapped<u32>(0x02);
if (wad_magic == u32(0x00204973) || wad_magic == u32(0x00206962))
return std::make_unique<VolumeWAD>(std::move(reader));
// No known magic words found
return nullptr;
}
std::unique_ptr<VolumeWAD> CreateWAD(const std::string& path)
{
std::unique_ptr<BlobReader> reader(CreateBlobReader(path));
return reader ? CreateWAD(reader) : nullptr;
}
std::unique_ptr<Volume> CreateVolume(const std::string& path)
{
std::unique_ptr<BlobReader> reader(CreateBlobReader(path));
if (reader == nullptr)
return nullptr;
std::unique_ptr<VolumeDisc> disc = CreateDisc(reader);
if (disc)
return disc;
std::unique_ptr<VolumeWAD> wad = CreateWAD(reader);
if (wad)
return wad;
return nullptr;
}
} // namespace DiscIO