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