123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197 |
- // Copyright 2021 Dolphin Emulator Project
- // SPDX-License-Identifier: GPL-2.0-or-later
- #include "DolphinTool/VerifyCommand.h"
- #include <cstdlib>
- #include <string>
- #include <vector>
- #include <OptionParser.h>
- #include <fmt/format.h>
- #include <fmt/ostream.h>
- #include "Common/StringUtil.h"
- #include "Core/AchievementManager.h"
- #include "DiscIO/Volume.h"
- #include "DiscIO/VolumeVerifier.h"
- #include "UICommon/UICommon.h"
- namespace DolphinTool
- {
- 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())
- fmt::print(std::cout, "CRC32: {}\n", HashToHexString(result.hashes.crc32));
- else
- fmt::print(std::cout, "CRC32 not computed\n");
- if (!result.hashes.md5.empty())
- fmt::print(std::cout, "MD5: {}\n", HashToHexString(result.hashes.md5));
- else
- fmt::print(std::cout, "MD5 not computed\n");
- if (!result.hashes.sha1.empty())
- fmt::print(std::cout, "SHA1: {}\n", HashToHexString(result.hashes.sha1));
- else
- fmt::print(std::cout, "SHA1 not computed\n");
- fmt::print(std::cout, "Problems Found: {}\n", result.problems.empty() ? "No" : "Yes");
- for (const auto& problem : result.problems)
- {
- fmt::print(std::cout, "\nSeverity: ");
- switch (problem.severity)
- {
- case DiscIO::VolumeVerifier::Severity::Low:
- fmt::print(std::cout, "Low");
- break;
- case DiscIO::VolumeVerifier::Severity::Medium:
- fmt::print(std::cout, "Medium");
- break;
- case DiscIO::VolumeVerifier::Severity::High:
- fmt::print(std::cout, "High");
- break;
- case DiscIO::VolumeVerifier::Severity::None:
- fmt::print(std::cout, "None");
- break;
- default:
- ASSERT(false);
- break;
- }
- fmt::print(std::cout, "\nSummary: {}\n\n", problem.text);
- }
- }
- int VerifyCommand(const std::vector<std::string>& args)
- {
- optparse::OptionParser parser;
- parser.usage("usage: verify [options]...");
- parser.add_option("-u", "--user")
- .type("string")
- .action("store")
- .help("User folder path, required for temporary processing files. "
- "Will be automatically created if this option is not set.")
- .set_default("");
- parser.add_option("-i", "--input")
- .type("string")
- .action("store")
- .help("Path to input file.")
- .metavar("FILE");
- 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"});
- 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
- UICommon::SetUserDirectory(options["user"]);
- UICommon::Init();
- // Validate options
- if (!options.is_set("input"))
- {
- fmt::print(std::cerr, "Error: No input set\n");
- return EXIT_FAILURE;
- }
- 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{};
- const bool algorithm_is_set = options.is_set("algorithm");
- if (!algorithm_is_set)
- {
- hashes_to_calculate = DiscIO::VolumeVerifier::GetDefaultHashesToCalculate();
- }
- else
- {
- 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
- fmt::print(std::cerr, "Error: No algorithms selected for the operation\n");
- return EXIT_FAILURE;
- }
- // Open the volume
- const std::unique_ptr<DiscIO::Volume> volume = DiscIO::CreateVolume(input_file_path);
- if (!volume)
- {
- fmt::print(std::cerr, "Error: Unable to open input file\n");
- return EXIT_FAILURE;
- }
- // Verify the volume
- DiscIO::VolumeVerifier verifier(*volume, false, hashes_to_calculate);
- verifier.Start();
- while (verifier.GetBytesProcessed() != verifier.GetTotalBytes())
- {
- verifier.Process();
- }
- verifier.Finish();
- const DiscIO::VolumeVerifier::Result& result = verifier.GetResult();
- // Calculate rcheevos hash
- if (rc_hash_calculate)
- {
- rc_hash_result = AchievementManager::CalculateHash(input_file_path);
- }
- // Print the report
- if (!algorithm_is_set)
- {
- PrintFullReport(result);
- }
- else
- {
- if (hashes_to_calculate.crc32 && !result.hashes.crc32.empty())
- fmt::print(std::cout, "{}\n", HashToHexString(result.hashes.crc32));
- else if (hashes_to_calculate.md5 && !result.hashes.md5.empty())
- fmt::print(std::cout, "{}\n", HashToHexString(result.hashes.md5));
- else if (hashes_to_calculate.sha1 && !result.hashes.sha1.empty())
- fmt::print(std::cout, "{}\n", HashToHexString(result.hashes.sha1));
- else if (rc_hash_calculate)
- fmt::print(std::cout, "{}\n", rc_hash_result);
- else
- {
- fmt::print(std::cerr, "Error: No hash computed\n");
- return EXIT_FAILURE;
- }
- }
- return EXIT_SUCCESS;
- }
- } // namespace DolphinTool
|