2019-03-21 16:04:56 -06:00
|
|
|
// Copyright 2019 Dolphin Emulator Project
|
|
|
|
// Licensed under GPLv2+
|
|
|
|
// Refer to the license.txt file included.
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
2019-08-06 03:39:09 -06:00
|
|
|
#include <future>
|
2019-03-26 10:22:18 -06:00
|
|
|
#include <map>
|
2019-08-06 03:39:09 -06:00
|
|
|
#include <mutex>
|
2019-03-26 10:22:18 -06:00
|
|
|
#include <optional>
|
2019-03-21 16:04:56 -06:00
|
|
|
#include <string>
|
|
|
|
#include <vector>
|
|
|
|
|
2019-03-30 05:45:00 -06:00
|
|
|
#include <mbedtls/md5.h>
|
|
|
|
#include <mbedtls/sha1.h>
|
|
|
|
|
2019-03-21 16:04:56 -06:00
|
|
|
#include "Common/CommonTypes.h"
|
2019-07-14 07:01:07 -06:00
|
|
|
#include "Core/IOS/ES/Formats.h"
|
2019-03-30 09:20:45 -06:00
|
|
|
#include "DiscIO/DiscScrubber.h"
|
2019-03-21 16:04:56 -06:00
|
|
|
#include "DiscIO/Volume.h"
|
|
|
|
|
|
|
|
// To be used as follows:
|
|
|
|
//
|
2019-08-23 05:20:09 -06:00
|
|
|
// VolumeVerifier verifier(volume, redump_verification, hashes_to_calculate);
|
2019-03-21 16:04:56 -06:00
|
|
|
// verifier.Start();
|
|
|
|
// while (verifier.GetBytesProcessed() != verifier.GetTotalBytes())
|
|
|
|
// verifier.Process();
|
|
|
|
// verifier.Finish();
|
|
|
|
// auto result = verifier.GetResult();
|
|
|
|
//
|
|
|
|
// Start, Process and Finish may take some time to run.
|
|
|
|
//
|
|
|
|
// GetResult() can be called before the processing is finished, but the result will be incomplete.
|
|
|
|
|
|
|
|
namespace DiscIO
|
|
|
|
{
|
|
|
|
class FileInfo;
|
|
|
|
|
2019-08-23 05:20:09 -06:00
|
|
|
template <typename T>
|
|
|
|
struct Hashes
|
|
|
|
{
|
|
|
|
T crc32;
|
|
|
|
T md5;
|
|
|
|
T sha1;
|
|
|
|
};
|
|
|
|
|
|
|
|
class RedumpVerifier final
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
enum class Status
|
|
|
|
{
|
|
|
|
Unknown,
|
|
|
|
GoodDump,
|
|
|
|
BadDump,
|
|
|
|
Error,
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Result
|
|
|
|
{
|
|
|
|
Status status = Status::Unknown;
|
|
|
|
std::string message;
|
|
|
|
};
|
|
|
|
|
|
|
|
void Start(const Volume& volume);
|
|
|
|
Result Finish(const Hashes<std::vector<u8>>& hashes);
|
|
|
|
|
|
|
|
private:
|
2019-08-24 04:53:13 -06:00
|
|
|
enum class DownloadStatus
|
|
|
|
{
|
|
|
|
NotAttempted,
|
|
|
|
Success,
|
|
|
|
Fail,
|
|
|
|
FailButOldCacheAvailable,
|
|
|
|
SystemNotAvailable,
|
|
|
|
};
|
|
|
|
|
|
|
|
struct DownloadState
|
|
|
|
{
|
|
|
|
std::mutex mutex;
|
|
|
|
DownloadStatus status = DownloadStatus::NotAttempted;
|
|
|
|
};
|
|
|
|
|
2019-08-23 05:20:09 -06:00
|
|
|
struct PotentialMatch
|
|
|
|
{
|
|
|
|
u64 size;
|
|
|
|
Hashes<std::vector<u8>> hashes;
|
|
|
|
};
|
|
|
|
|
2019-08-24 04:53:13 -06:00
|
|
|
static DownloadStatus DownloadDatfile(const std::string& system, DownloadStatus old_status);
|
|
|
|
static std::vector<u8> ReadDatfile(const std::string& system);
|
2019-11-02 09:47:08 -06:00
|
|
|
std::vector<PotentialMatch> ScanDatfile(const std::vector<u8>& data, const std::string& system);
|
2019-08-23 05:20:09 -06:00
|
|
|
|
|
|
|
std::string m_game_id;
|
|
|
|
u16 m_revision;
|
|
|
|
u8 m_disc_number;
|
|
|
|
u64 m_size;
|
|
|
|
|
|
|
|
std::future<std::vector<PotentialMatch>> m_future;
|
|
|
|
Result m_result;
|
2019-08-24 04:53:13 -06:00
|
|
|
|
|
|
|
static DownloadState m_gc_download_state;
|
|
|
|
static DownloadState m_wii_download_state;
|
2019-08-23 05:20:09 -06:00
|
|
|
};
|
|
|
|
|
2019-03-21 16:04:56 -06:00
|
|
|
class VolumeVerifier final
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
enum class Severity
|
|
|
|
{
|
|
|
|
None, // Only used internally
|
|
|
|
Low,
|
|
|
|
Medium,
|
|
|
|
High,
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Problem
|
|
|
|
{
|
|
|
|
Severity severity;
|
|
|
|
std::string text;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Result
|
|
|
|
{
|
2019-03-30 05:45:00 -06:00
|
|
|
Hashes<std::vector<u8>> hashes;
|
2019-03-21 16:04:56 -06:00
|
|
|
std::string summary_text;
|
|
|
|
std::vector<Problem> problems;
|
2019-08-23 05:20:09 -06:00
|
|
|
RedumpVerifier::Result redump;
|
2019-03-21 16:04:56 -06:00
|
|
|
};
|
|
|
|
|
2019-08-23 05:20:09 -06:00
|
|
|
VolumeVerifier(const Volume& volume, bool redump_verification, Hashes<bool> hashes_to_calculate);
|
2019-05-27 08:19:49 -06:00
|
|
|
~VolumeVerifier();
|
|
|
|
|
2019-03-21 16:04:56 -06:00
|
|
|
void Start();
|
|
|
|
void Process();
|
|
|
|
u64 GetBytesProcessed() const;
|
|
|
|
u64 GetTotalBytes() const;
|
|
|
|
void Finish();
|
|
|
|
const Result& GetResult() const;
|
|
|
|
|
|
|
|
private:
|
2019-03-26 10:22:18 -06:00
|
|
|
struct BlockToVerify
|
|
|
|
{
|
|
|
|
Partition partition;
|
|
|
|
u64 offset;
|
|
|
|
u64 block_index;
|
|
|
|
};
|
|
|
|
|
2020-02-09 11:05:44 -07:00
|
|
|
std::vector<Partition> CheckPartitions();
|
2019-03-21 16:04:56 -06:00
|
|
|
bool CheckPartition(const Partition& partition); // Returns false if partition should be ignored
|
2019-03-26 10:22:18 -06:00
|
|
|
std::string GetPartitionName(std::optional<u32> type) const;
|
2019-05-27 08:12:20 -06:00
|
|
|
void CheckCorrectlySigned(const Partition& partition, std::string error_text);
|
2019-03-21 16:04:56 -06:00
|
|
|
bool IsDebugSigned() const;
|
|
|
|
bool ShouldHaveChannelPartition() const;
|
|
|
|
bool ShouldHaveInstallPartition() const;
|
|
|
|
bool ShouldHaveMasterpiecePartitions() const;
|
|
|
|
bool ShouldBeDualLayer() const;
|
2020-02-09 11:05:44 -07:00
|
|
|
void CheckDiscSize(const std::vector<Partition>& partitions);
|
|
|
|
u64 GetBiggestReferencedOffset(const std::vector<Partition>& partitions) const;
|
2019-06-18 07:37:10 -06:00
|
|
|
u64 GetBiggestReferencedOffset(const FileInfo& file_info) const;
|
2019-03-21 16:04:56 -06:00
|
|
|
void CheckMisc();
|
2019-11-27 06:35:25 -07:00
|
|
|
void CheckSuperPaperMario();
|
2019-03-30 05:45:00 -06:00
|
|
|
void SetUpHashing();
|
2019-08-06 03:39:09 -06:00
|
|
|
void WaitForAsyncOperations() const;
|
|
|
|
bool ReadChunkAndWaitForAsyncOperations(u64 bytes_to_read);
|
2019-03-21 16:04:56 -06:00
|
|
|
|
2019-05-27 08:12:20 -06:00
|
|
|
void AddProblem(Severity severity, std::string text);
|
2019-03-21 16:04:56 -06:00
|
|
|
|
|
|
|
const Volume& m_volume;
|
|
|
|
Result m_result;
|
2019-05-27 08:23:52 -06:00
|
|
|
bool m_is_tgc = false;
|
|
|
|
bool m_is_datel = false;
|
|
|
|
bool m_is_not_retail = false;
|
2019-03-21 16:04:56 -06:00
|
|
|
|
2019-08-23 05:20:09 -06:00
|
|
|
bool m_redump_verification;
|
|
|
|
RedumpVerifier m_redump_verifier;
|
|
|
|
|
2020-01-25 11:41:58 -07:00
|
|
|
bool m_read_errors_occurred = false;
|
|
|
|
|
2019-05-27 08:23:52 -06:00
|
|
|
Hashes<bool> m_hashes_to_calculate{};
|
|
|
|
bool m_calculating_any_hash = false;
|
|
|
|
unsigned long m_crc32_context = 0;
|
2019-03-30 05:45:00 -06:00
|
|
|
mbedtls_md5_context m_md5_context;
|
|
|
|
mbedtls_sha1_context m_sha1_context;
|
|
|
|
|
2019-08-06 03:39:09 -06:00
|
|
|
std::vector<u8> m_data;
|
|
|
|
std::mutex m_volume_mutex;
|
|
|
|
std::future<void> m_crc32_future;
|
|
|
|
std::future<void> m_md5_future;
|
|
|
|
std::future<void> m_sha1_future;
|
|
|
|
std::future<void> m_content_future;
|
|
|
|
std::future<void> m_block_future;
|
|
|
|
|
2019-03-30 09:20:45 -06:00
|
|
|
DiscScrubber m_scrubber;
|
2019-07-14 07:01:07 -06:00
|
|
|
IOS::ES::TicketReader m_ticket;
|
2019-03-30 10:42:31 -06:00
|
|
|
std::vector<u64> m_content_offsets;
|
|
|
|
u16 m_content_index = 0;
|
2019-03-26 10:22:18 -06:00
|
|
|
std::vector<BlockToVerify> m_blocks;
|
|
|
|
size_t m_block_index = 0; // Index in m_blocks, not index in a specific partition
|
|
|
|
std::map<Partition, size_t> m_block_errors;
|
2019-03-30 09:20:45 -06:00
|
|
|
std::map<Partition, size_t> m_unused_block_errors;
|
2019-03-26 10:22:18 -06:00
|
|
|
|
2019-06-18 07:37:10 -06:00
|
|
|
u64 m_biggest_referenced_offset = 0;
|
|
|
|
u64 m_biggest_verified_offset = 0;
|
|
|
|
|
2019-05-27 08:23:52 -06:00
|
|
|
bool m_started = false;
|
|
|
|
bool m_done = false;
|
|
|
|
u64 m_progress = 0;
|
|
|
|
u64 m_max_progress = 0;
|
2019-03-21 16:04:56 -06:00
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace DiscIO
|