dolphin/Source/Core/DiscIO/VolumeGC.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

243 lines
6.1 KiB
C++

// Copyright 2008 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include <cinttypes>
#include <cstddef>
#include <map>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include <mbedtls/sha1.h>
#include "Common/Assert.h"
#include "Common/ColorUtil.h"
#include "Common/CommonTypes.h"
#include "Common/Logging/Log.h"
#include "Common/MsgHandler.h"
#include "Common/StringUtil.h"
#include "DiscIO/Blob.h"
#include "DiscIO/DiscExtractor.h"
#include "DiscIO/Enums.h"
#include "DiscIO/FileSystemGCWii.h"
#include "DiscIO/Filesystem.h"
#include "DiscIO/Volume.h"
#include "DiscIO/VolumeGC.h"
namespace DiscIO
{
VolumeGC::VolumeGC(std::unique_ptr<BlobReader> reader) : m_reader(std::move(reader))
{
ASSERT(m_reader);
m_file_system = [this]() -> std::unique_ptr<FileSystem> {
auto file_system = std::make_unique<FileSystemGCWii>(this, PARTITION_NONE);
return file_system->IsValid() ? std::move(file_system) : nullptr;
};
m_converted_banner = [this] { return LoadBannerFile(); };
}
VolumeGC::~VolumeGC()
{
}
bool VolumeGC::Read(u64 offset, u64 length, u8* buffer, const Partition& partition) const
{
if (partition != PARTITION_NONE)
return false;
return m_reader->Read(offset, length, buffer);
}
const FileSystem* VolumeGC::GetFileSystem(const Partition& partition) const
{
return m_file_system->get();
}
std::string VolumeGC::GetGameTDBID(const Partition& partition) const
{
const std::string game_id = GetGameID(partition);
// Don't return an ID for Datel discs that are using the game ID of NHL Hitz 2002
return game_id == "GNHE5d" && IsDatelDisc() ? "" : game_id;
}
Region VolumeGC::GetRegion() const
{
return RegionCodeToRegion(m_reader->ReadSwapped<u32>(0x458));
}
std::map<Language, std::string> VolumeGC::GetShortNames() const
{
return m_converted_banner->short_names;
}
std::map<Language, std::string> VolumeGC::GetLongNames() const
{
return m_converted_banner->long_names;
}
std::map<Language, std::string> VolumeGC::GetShortMakers() const
{
return m_converted_banner->short_makers;
}
std::map<Language, std::string> VolumeGC::GetLongMakers() const
{
return m_converted_banner->long_makers;
}
std::map<Language, std::string> VolumeGC::GetDescriptions() const
{
return m_converted_banner->descriptions;
}
std::vector<u32> VolumeGC::GetBanner(u32* width, u32* height) const
{
*width = m_converted_banner->image_width;
*height = m_converted_banner->image_height;
return m_converted_banner->image_buffer;
}
BlobType VolumeGC::GetBlobType() const
{
return m_reader->GetBlobType();
}
u64 VolumeGC::GetSize() const
{
return m_reader->GetDataSize();
}
bool VolumeGC::IsSizeAccurate() const
{
return m_reader->IsDataSizeAccurate();
}
u64 VolumeGC::GetRawSize() const
{
return m_reader->GetRawSize();
}
const BlobReader& VolumeGC::GetBlobReader() const
{
return *m_reader;
}
Platform VolumeGC::GetVolumeType() const
{
return Platform::GameCubeDisc;
}
bool VolumeGC::IsDatelDisc() const
{
return !GetBootDOLOffset(*this, PARTITION_NONE).has_value();
}
std::array<u8, 20> VolumeGC::GetSyncHash() const
{
mbedtls_sha1_context context;
mbedtls_sha1_init(&context);
mbedtls_sha1_starts_ret(&context);
AddGamePartitionToSyncHash(&context);
std::array<u8, 20> hash;
mbedtls_sha1_finish_ret(&context, hash.data());
return hash;
}
VolumeGC::ConvertedGCBanner VolumeGC::LoadBannerFile() const
{
GCBanner banner_file;
const u64 file_size = ReadFile(*this, PARTITION_NONE, "opening.bnr",
reinterpret_cast<u8*>(&banner_file), sizeof(GCBanner));
if (file_size < 4)
{
WARN_LOG(DISCIO, "Could not read opening.bnr.");
return {}; // Return early so that we don't access the uninitialized banner_file.id
}
constexpr u32 BNR1_MAGIC = 0x31524e42;
constexpr u32 BNR2_MAGIC = 0x32524e42;
bool is_bnr1;
if (banner_file.id == BNR1_MAGIC && file_size == BNR1_SIZE)
{
is_bnr1 = true;
}
else if (banner_file.id == BNR2_MAGIC && file_size == BNR2_SIZE)
{
is_bnr1 = false;
}
else
{
WARN_LOG(DISCIO, "Invalid opening.bnr. Type: %0x Size: %0" PRIx64, banner_file.id, file_size);
return {};
}
return ExtractBannerInformation(banner_file, is_bnr1);
}
VolumeGC::ConvertedGCBanner VolumeGC::ExtractBannerInformation(const GCBanner& banner_file,
bool is_bnr1) const
{
ConvertedGCBanner banner;
u32 number_of_languages = 0;
Language start_language = Language::Unknown;
if (is_bnr1) // NTSC
{
number_of_languages = 1;
start_language = GetRegion() == Region::NTSC_J ? Language::Japanese : Language::English;
}
else // PAL
{
number_of_languages = 6;
start_language = Language::English;
}
banner.image_width = GC_BANNER_WIDTH;
banner.image_height = GC_BANNER_HEIGHT;
banner.image_buffer = std::vector<u32>(GC_BANNER_WIDTH * GC_BANNER_HEIGHT);
Common::Decode5A3Image(banner.image_buffer.data(), banner_file.image, GC_BANNER_WIDTH,
GC_BANNER_HEIGHT);
for (u32 i = 0; i < number_of_languages; ++i)
{
const GCBannerInformation& info = banner_file.information[i];
Language language = static_cast<Language>(static_cast<int>(start_language) + i);
std::string description = DecodeString(info.description);
if (!description.empty())
banner.descriptions.emplace(language, description);
std::string short_name = DecodeString(info.short_name);
if (!short_name.empty())
banner.short_names.emplace(language, short_name);
std::string long_name = DecodeString(info.long_name);
if (!long_name.empty())
banner.long_names.emplace(language, long_name);
std::string short_maker = DecodeString(info.short_maker);
if (!short_maker.empty())
banner.short_makers.emplace(language, short_maker);
std::string long_maker = DecodeString(info.long_maker);
if (!long_maker.empty())
banner.long_makers.emplace(language, long_maker);
}
return banner;
}
VolumeGC::ConvertedGCBanner::ConvertedGCBanner() = default;
VolumeGC::ConvertedGCBanner::~ConvertedGCBanner() = default;
} // namespace DiscIO