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