cd_image_hasher.cpp (4416B)
1 // SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com> 2 // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) 3 4 #include "cd_image_hasher.h" 5 #include "cd_image.h" 6 7 #include "util/host.h" 8 9 #include "common/md5_digest.h" 10 #include "common/string_util.h" 11 12 namespace CDImageHasher { 13 14 static bool ReadIndex(CDImage* image, u8 track, u8 index, MD5Digest* digest, ProgressCallback* progress_callback); 15 static bool ReadTrack(CDImage* image, u8 track, MD5Digest* digest, ProgressCallback* progress_callback); 16 17 } // namespace CDImageHasher 18 19 bool CDImageHasher::ReadIndex(CDImage* image, u8 track, u8 index, MD5Digest* digest, 20 ProgressCallback* progress_callback) 21 { 22 const CDImage::LBA index_start = image->GetTrackIndexPosition(track, index); 23 const u32 index_length = image->GetTrackIndexLength(track, index); 24 const u32 update_interval = std::max<u32>(index_length / 100u, 1u); 25 26 progress_callback->SetStatusText( 27 fmt::format(TRANSLATE_FS("CDImageHasher", "Computing hash for Track {}/Index {}..."), track, index).c_str()); 28 progress_callback->SetProgressRange(index_length); 29 30 if (!image->Seek(index_start)) 31 { 32 progress_callback->FormatModalError("Failed to seek to sector {} for track {} index {}", index_start, track, index); 33 return false; 34 } 35 36 std::array<u8, CDImage::RAW_SECTOR_SIZE> sector; 37 for (u32 lba = 0; lba < index_length; lba++) 38 { 39 if ((lba % update_interval) == 0) 40 progress_callback->SetProgressValue(lba); 41 42 if (!image->ReadRawSector(sector.data(), nullptr)) 43 { 44 progress_callback->FormatModalError("Failed to read sector {} from image", image->GetPositionOnDisc()); 45 return false; 46 } 47 48 digest->Update(sector); 49 } 50 51 progress_callback->SetProgressValue(index_length); 52 return true; 53 } 54 55 bool CDImageHasher::ReadTrack(CDImage* image, u8 track, MD5Digest* digest, ProgressCallback* progress_callback) 56 { 57 static constexpr u8 INDICES_TO_READ = 2; 58 59 progress_callback->PushState(); 60 61 const bool dataTrack = track == 1; 62 progress_callback->SetProgressRange(dataTrack ? 1 : 2); 63 64 u8 progress = 0; 65 for (u8 index = 0; index < INDICES_TO_READ; index++) 66 { 67 progress_callback->SetProgressValue(progress); 68 69 // skip index 0 if data track 70 if (dataTrack && index == 0) 71 continue; 72 73 progress++; 74 progress_callback->PushState(); 75 if (!ReadIndex(image, track, index, digest, progress_callback)) 76 { 77 progress_callback->PopState(); 78 progress_callback->PopState(); 79 return false; 80 } 81 82 progress_callback->PopState(); 83 } 84 85 progress_callback->SetProgressValue(progress); 86 progress_callback->PopState(); 87 return true; 88 } 89 90 std::string CDImageHasher::HashToString(const Hash& hash) 91 { 92 return fmt::format("{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}", 93 hash[0], hash[1], hash[2], hash[3], hash[4], hash[5], hash[6], hash[7], hash[8], hash[9], hash[10], 94 hash[11], hash[12], hash[13], hash[14], hash[15]); 95 } 96 97 std::optional<CDImageHasher::Hash> CDImageHasher::HashFromString(std::string_view str) 98 { 99 auto decoded = StringUtil::DecodeHex(str); 100 if (decoded && decoded->size() == std::tuple_size_v<Hash>) 101 { 102 Hash result; 103 std::copy(decoded->begin(), decoded->end(), result.begin()); 104 return result; 105 } 106 return std::nullopt; 107 } 108 109 bool CDImageHasher::GetImageHash(CDImage* image, Hash* out_hash, 110 ProgressCallback* progress_callback /*= ProgressCallback::NullProgressCallback*/) 111 { 112 MD5Digest digest; 113 114 progress_callback->SetProgressRange(image->GetTrackCount()); 115 progress_callback->SetProgressValue(0); 116 progress_callback->PushState(); 117 118 for (u32 i = 1; i <= image->GetTrackCount(); i++) 119 { 120 progress_callback->SetProgressValue(i - 1); 121 if (!ReadTrack(image, static_cast<u8>(i), &digest, progress_callback)) 122 { 123 progress_callback->PopState(); 124 return false; 125 } 126 } 127 128 progress_callback->SetProgressValue(image->GetTrackCount()); 129 digest.Final(*out_hash); 130 return true; 131 } 132 133 bool CDImageHasher::GetTrackHash(CDImage* image, u8 track, Hash* out_hash, 134 ProgressCallback* progress_callback /*= ProgressCallback::NullProgressCallback*/) 135 { 136 MD5Digest digest; 137 if (!ReadTrack(image, track, &digest, progress_callback)) 138 return false; 139 140 digest.Final(*out_hash); 141 return true; 142 }