VerifyCommand.cpp 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. // Copyright 2021 Dolphin Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include "DolphinTool/VerifyCommand.h"
  4. #include <cstdlib>
  5. #include <string>
  6. #include <vector>
  7. #include <OptionParser.h>
  8. #include <fmt/format.h>
  9. #include <fmt/ostream.h>
  10. #include "Common/StringUtil.h"
  11. #include "Core/AchievementManager.h"
  12. #include "DiscIO/Volume.h"
  13. #include "DiscIO/VolumeVerifier.h"
  14. #include "UICommon/UICommon.h"
  15. namespace DolphinTool
  16. {
  17. static std::string HashToHexString(const std::vector<u8>& hash)
  18. {
  19. std::stringstream ss;
  20. ss << std::hex;
  21. for (int i = 0; i < static_cast<int>(hash.size()); ++i)
  22. {
  23. ss << std::setw(2) << std::setfill('0') << static_cast<int>(hash[i]);
  24. }
  25. return ss.str();
  26. }
  27. static void PrintFullReport(const DiscIO::VolumeVerifier::Result& result)
  28. {
  29. if (!result.hashes.crc32.empty())
  30. fmt::print(std::cout, "CRC32: {}\n", HashToHexString(result.hashes.crc32));
  31. else
  32. fmt::print(std::cout, "CRC32 not computed\n");
  33. if (!result.hashes.md5.empty())
  34. fmt::print(std::cout, "MD5: {}\n", HashToHexString(result.hashes.md5));
  35. else
  36. fmt::print(std::cout, "MD5 not computed\n");
  37. if (!result.hashes.sha1.empty())
  38. fmt::print(std::cout, "SHA1: {}\n", HashToHexString(result.hashes.sha1));
  39. else
  40. fmt::print(std::cout, "SHA1 not computed\n");
  41. fmt::print(std::cout, "Problems Found: {}\n", result.problems.empty() ? "No" : "Yes");
  42. for (const auto& problem : result.problems)
  43. {
  44. fmt::print(std::cout, "\nSeverity: ");
  45. switch (problem.severity)
  46. {
  47. case DiscIO::VolumeVerifier::Severity::Low:
  48. fmt::print(std::cout, "Low");
  49. break;
  50. case DiscIO::VolumeVerifier::Severity::Medium:
  51. fmt::print(std::cout, "Medium");
  52. break;
  53. case DiscIO::VolumeVerifier::Severity::High:
  54. fmt::print(std::cout, "High");
  55. break;
  56. case DiscIO::VolumeVerifier::Severity::None:
  57. fmt::print(std::cout, "None");
  58. break;
  59. default:
  60. ASSERT(false);
  61. break;
  62. }
  63. fmt::print(std::cout, "\nSummary: {}\n\n", problem.text);
  64. }
  65. }
  66. int VerifyCommand(const std::vector<std::string>& args)
  67. {
  68. optparse::OptionParser parser;
  69. parser.usage("usage: verify [options]...");
  70. parser.add_option("-u", "--user")
  71. .type("string")
  72. .action("store")
  73. .help("User folder path, required for temporary processing files. "
  74. "Will be automatically created if this option is not set.")
  75. .set_default("");
  76. parser.add_option("-i", "--input")
  77. .type("string")
  78. .action("store")
  79. .help("Path to input file.")
  80. .metavar("FILE");
  81. parser.add_option("-a", "--algorithm")
  82. .type("string")
  83. .action("store")
  84. .help("Optional. Compute and print the digest using the selected algorithm, then exit. "
  85. "[%choices]")
  86. .choices({"crc32", "md5", "sha1", "rchash"});
  87. const optparse::Values& options = parser.parse_args(args);
  88. // Initialize the dolphin user directory, required for temporary processing files
  89. // If this is not set, destructive file operations could occur due to path confusion
  90. UICommon::SetUserDirectory(options["user"]);
  91. UICommon::Init();
  92. // Validate options
  93. if (!options.is_set("input"))
  94. {
  95. fmt::print(std::cerr, "Error: No input set\n");
  96. return EXIT_FAILURE;
  97. }
  98. const std::string& input_file_path = options["input"];
  99. bool rc_hash_calculate = false;
  100. std::string rc_hash_result = "0";
  101. DiscIO::Hashes<bool> hashes_to_calculate{};
  102. const bool algorithm_is_set = options.is_set("algorithm");
  103. if (!algorithm_is_set)
  104. {
  105. hashes_to_calculate = DiscIO::VolumeVerifier::GetDefaultHashesToCalculate();
  106. }
  107. else
  108. {
  109. const std::string& algorithm = options["algorithm"];
  110. if (algorithm == "crc32")
  111. hashes_to_calculate.crc32 = true;
  112. else if (algorithm == "md5")
  113. hashes_to_calculate.md5 = true;
  114. else if (algorithm == "sha1")
  115. hashes_to_calculate.sha1 = true;
  116. else if (algorithm == "rchash")
  117. rc_hash_calculate = true;
  118. }
  119. if (!hashes_to_calculate.crc32 && !hashes_to_calculate.md5 && !hashes_to_calculate.sha1 &&
  120. !rc_hash_calculate)
  121. {
  122. // optparse should protect from this
  123. fmt::print(std::cerr, "Error: No algorithms selected for the operation\n");
  124. return EXIT_FAILURE;
  125. }
  126. // Open the volume
  127. const std::unique_ptr<DiscIO::Volume> volume = DiscIO::CreateVolume(input_file_path);
  128. if (!volume)
  129. {
  130. fmt::print(std::cerr, "Error: Unable to open input file\n");
  131. return EXIT_FAILURE;
  132. }
  133. // Verify the volume
  134. DiscIO::VolumeVerifier verifier(*volume, false, hashes_to_calculate);
  135. verifier.Start();
  136. while (verifier.GetBytesProcessed() != verifier.GetTotalBytes())
  137. {
  138. verifier.Process();
  139. }
  140. verifier.Finish();
  141. const DiscIO::VolumeVerifier::Result& result = verifier.GetResult();
  142. // Calculate rcheevos hash
  143. if (rc_hash_calculate)
  144. {
  145. rc_hash_result = AchievementManager::CalculateHash(input_file_path);
  146. }
  147. // Print the report
  148. if (!algorithm_is_set)
  149. {
  150. PrintFullReport(result);
  151. }
  152. else
  153. {
  154. if (hashes_to_calculate.crc32 && !result.hashes.crc32.empty())
  155. fmt::print(std::cout, "{}\n", HashToHexString(result.hashes.crc32));
  156. else if (hashes_to_calculate.md5 && !result.hashes.md5.empty())
  157. fmt::print(std::cout, "{}\n", HashToHexString(result.hashes.md5));
  158. else if (hashes_to_calculate.sha1 && !result.hashes.sha1.empty())
  159. fmt::print(std::cout, "{}\n", HashToHexString(result.hashes.sha1));
  160. else if (rc_hash_calculate)
  161. fmt::print(std::cout, "{}\n", rc_hash_result);
  162. else
  163. {
  164. fmt::print(std::cerr, "Error: No hash computed\n");
  165. return EXIT_FAILURE;
  166. }
  167. }
  168. return EXIT_SUCCESS;
  169. }
  170. } // namespace DolphinTool