dolphin/Source/Core/DolphinTool/VerifyCommand.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

197 lines
5.6 KiB
C++
Raw Normal View History

// Copyright 2021 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "DolphinTool/VerifyCommand.h"
2023-06-14 23:33:11 +00:00
#include <cstdlib>
2023-06-14 23:33:11 +00:00
#include <string>
#include <vector>
#include <OptionParser.h>
2023-06-16 23:59:30 +00:00
#include <fmt/format.h>
#include <fmt/ostream.h>
2023-06-14 23:33:11 +00:00
#include "Common/StringUtil.h"
#include "Core/AchievementManager.h"
2024-05-28 03:15:00 +00:00
#include "DiscIO/Volume.h"
2023-06-14 23:33:11 +00:00
#include "DiscIO/VolumeVerifier.h"
#include "UICommon/UICommon.h"
namespace DolphinTool
{
2023-06-14 23:33:11 +00:00
static std::string HashToHexString(const std::vector<u8>& hash)
{
std::stringstream ss;
ss << std::hex;
for (int i = 0; i < static_cast<int>(hash.size()); ++i)
{
ss << std::setw(2) << std::setfill('0') << static_cast<int>(hash[i]);
}
return ss.str();
}
static void PrintFullReport(const DiscIO::VolumeVerifier::Result& result)
{
if (!result.hashes.crc32.empty())
2023-06-16 23:59:30 +00:00
fmt::print(std::cout, "CRC32: {}\n", HashToHexString(result.hashes.crc32));
2023-06-14 23:33:11 +00:00
else
2023-06-16 23:59:30 +00:00
fmt::print(std::cout, "CRC32 not computed\n");
2023-06-14 23:33:11 +00:00
if (!result.hashes.md5.empty())
2023-06-16 23:59:30 +00:00
fmt::print(std::cout, "MD5: {}\n", HashToHexString(result.hashes.md5));
2023-06-14 23:33:11 +00:00
else
2023-06-16 23:59:30 +00:00
fmt::print(std::cout, "MD5 not computed\n");
2023-06-14 23:33:11 +00:00
if (!result.hashes.sha1.empty())
2023-06-16 23:59:30 +00:00
fmt::print(std::cout, "SHA1: {}\n", HashToHexString(result.hashes.sha1));
2023-06-14 23:33:11 +00:00
else
2023-06-16 23:59:30 +00:00
fmt::print(std::cout, "SHA1 not computed\n");
2023-06-14 23:33:11 +00:00
2023-06-16 23:59:30 +00:00
fmt::print(std::cout, "Problems Found: {}\n", result.problems.empty() ? "No" : "Yes");
2023-06-14 23:33:11 +00:00
for (const auto& problem : result.problems)
{
2023-06-16 23:59:30 +00:00
fmt::print(std::cout, "\nSeverity: ");
2023-06-14 23:33:11 +00:00
switch (problem.severity)
{
case DiscIO::VolumeVerifier::Severity::Low:
2023-06-16 23:59:30 +00:00
fmt::print(std::cout, "Low");
2023-06-14 23:33:11 +00:00
break;
case DiscIO::VolumeVerifier::Severity::Medium:
2023-06-16 23:59:30 +00:00
fmt::print(std::cout, "Medium");
2023-06-14 23:33:11 +00:00
break;
case DiscIO::VolumeVerifier::Severity::High:
2023-06-16 23:59:30 +00:00
fmt::print(std::cout, "High");
2023-06-14 23:33:11 +00:00
break;
case DiscIO::VolumeVerifier::Severity::None:
2023-06-16 23:59:30 +00:00
fmt::print(std::cout, "None");
2023-06-14 23:33:11 +00:00
break;
default:
ASSERT(false);
break;
}
2023-06-16 23:59:30 +00:00
fmt::print(std::cout, "\nSummary: {}\n\n", problem.text);
2023-06-14 23:33:11 +00:00
}
}
int VerifyCommand(const std::vector<std::string>& args)
{
2023-06-14 20:14:03 +00:00
optparse::OptionParser parser;
2023-06-14 20:14:03 +00:00
parser.usage("usage: verify [options]...");
2023-06-14 20:14:03 +00:00
parser.add_option("-u", "--user")
2023-06-17 00:33:38 +00:00
.type("string")
.action("store")
.help("User folder path, required for temporary processing files. "
2023-06-17 00:33:38 +00:00
"Will be automatically created if this option is not set.")
.set_default("");
2023-06-14 20:14:03 +00:00
parser.add_option("-i", "--input")
.type("string")
.action("store")
2024-05-28 03:15:00 +00:00
.help("Path to input file.")
.metavar("FILE");
2023-06-14 20:14:03 +00:00
parser.add_option("-a", "--algorithm")
.type("string")
.action("store")
.help("Optional. Compute and print the digest using the selected algorithm, then exit. "
"[%choices]")
.choices({"crc32", "md5", "sha1", "rchash"});
2023-06-14 20:14:03 +00:00
const optparse::Values& options = parser.parse_args(args);
// Initialize the dolphin user directory, required for temporary processing files
// If this is not set, destructive file operations could occur due to path confusion
2023-06-17 00:33:38 +00:00
UICommon::SetUserDirectory(options["user"]);
UICommon::Init();
// Validate options
2023-06-17 00:33:38 +00:00
if (!options.is_set("input"))
{
2023-06-16 23:59:30 +00:00
fmt::print(std::cerr, "Error: No input set\n");
return EXIT_FAILURE;
}
2023-06-17 00:33:38 +00:00
const std::string& input_file_path = options["input"];
bool rc_hash_calculate = false;
std::string rc_hash_result = "0";
DiscIO::Hashes<bool> hashes_to_calculate{};
2023-06-14 20:14:03 +00:00
const bool algorithm_is_set = options.is_set("algorithm");
if (!algorithm_is_set)
{
hashes_to_calculate = DiscIO::VolumeVerifier::GetDefaultHashesToCalculate();
}
else
{
2023-06-17 00:33:38 +00:00
const std::string& algorithm = options["algorithm"];
if (algorithm == "crc32")
hashes_to_calculate.crc32 = true;
else if (algorithm == "md5")
hashes_to_calculate.md5 = true;
else if (algorithm == "sha1")
hashes_to_calculate.sha1 = true;
else if (algorithm == "rchash")
rc_hash_calculate = true;
}
if (!hashes_to_calculate.crc32 && !hashes_to_calculate.md5 && !hashes_to_calculate.sha1 &&
!rc_hash_calculate)
{
// optparse should protect from this
2023-06-16 23:59:30 +00:00
fmt::print(std::cerr, "Error: No algorithms selected for the operation\n");
return EXIT_FAILURE;
}
// Open the volume
2024-05-28 03:15:00 +00:00
const std::unique_ptr<DiscIO::Volume> volume = DiscIO::CreateVolume(input_file_path);
if (!volume)
{
2024-05-28 03:15:00 +00:00
fmt::print(std::cerr, "Error: Unable to open input file\n");
return EXIT_FAILURE;
}
// Verify the volume
2023-06-14 20:14:03 +00:00
DiscIO::VolumeVerifier verifier(*volume, false, hashes_to_calculate);
verifier.Start();
while (verifier.GetBytesProcessed() != verifier.GetTotalBytes())
{
2023-06-14 20:14:03 +00:00
verifier.Process();
}
2023-06-14 20:14:03 +00:00
verifier.Finish();
const DiscIO::VolumeVerifier::Result& result = verifier.GetResult();
// Calculate rcheevos hash
if (rc_hash_calculate)
{
rc_hash_result = AchievementManager::CalculateHash(input_file_path);
}
2023-06-14 20:14:03 +00:00
// Print the report
if (!algorithm_is_set)
{
PrintFullReport(result);
}
else
{
2023-06-14 20:14:03 +00:00
if (hashes_to_calculate.crc32 && !result.hashes.crc32.empty())
2023-06-16 23:59:30 +00:00
fmt::print(std::cout, "{}\n", HashToHexString(result.hashes.crc32));
2023-06-14 20:14:03 +00:00
else if (hashes_to_calculate.md5 && !result.hashes.md5.empty())
2023-06-16 23:59:30 +00:00
fmt::print(std::cout, "{}\n", HashToHexString(result.hashes.md5));
2023-06-14 20:14:03 +00:00
else if (hashes_to_calculate.sha1 && !result.hashes.sha1.empty())
2023-06-16 23:59:30 +00:00
fmt::print(std::cout, "{}\n", HashToHexString(result.hashes.sha1));
else if (rc_hash_calculate)
fmt::print(std::cout, "{}\n", rc_hash_result);
else
{
2023-06-16 23:59:30 +00:00
fmt::print(std::cerr, "Error: No hash computed\n");
return EXIT_FAILURE;
}
}
return EXIT_SUCCESS;
}
} // namespace DolphinTool