Common: Add a CD image hasher class

This commit is contained in:
Connor McLaughlin 2020-06-08 01:19:35 +10:00
parent 06f5c3710d
commit 8b04b74f27
7 changed files with 165 additions and 0 deletions

View File

@ -13,6 +13,8 @@ add_library(common
cd_image_bin.cpp
cd_image_cue.cpp
cd_image_chd.cpp
cd_image_hasher.cpp
cd_image_hasher.h
cd_subchannel_replacement.cpp
cd_subchannel_replacement.h
cd_xa.cpp

View File

@ -72,6 +72,28 @@ CDImage::TrackMode CDImage::GetTrackMode(u8 track) const
return m_tracks[track - 1].mode;
}
CDImage::LBA CDImage::GetTrackIndexPosition(u8 track, u8 index) const
{
for (const Index& current_index : m_indices)
{
if (current_index.track_number == track && current_index.index_number == index)
return current_index.start_lba_on_disc;
}
return m_lba_count;
}
CDImage::LBA CDImage::GetTrackIndexLength(u8 track, u8 index) const
{
for (const Index& current_index : m_indices)
{
if (current_index.track_number == track && current_index.index_number == index)
return current_index.length;
}
return 0;
}
bool CDImage::Seek(LBA lba)
{
const Index* new_index;

View File

@ -182,6 +182,8 @@ public:
LBA GetTrackLength(u8 track) const;
Position GetTrackMSFLength(u8 track) const;
TrackMode GetTrackMode(u8 track) const;
LBA GetTrackIndexPosition(u8 track, u8 index) const;
LBA GetTrackIndexLength(u8 track, u8 index) const;
u32 GetFirstTrackNumber() const { return m_tracks.front().track_number; }
u32 GetLastTrackNumber() const { return m_tracks.back().track_number; }

View File

@ -0,0 +1,116 @@
#include "cd_image_hasher.h"
#include "cd_image.h"
#include "md5_digest.h"
#include "string_util.h"
namespace CDImageHasher {
static bool ReadIndex(CDImage* image, u8 track, u8 index, MD5Digest* digest, ProgressCallback* progress_callback)
{
const CDImage::LBA index_start = image->GetTrackIndexPosition(track, index);
const u32 index_length = image->GetTrackIndexLength(track, index);
const u32 update_interval = std::max<u32>(index_length / 100u, 1u);
progress_callback->SetFormattedStatusText("Computing hash for track %u/index %u...", track, index);
progress_callback->SetProgressRange(index_length);
if (!image->Seek(index_start))
{
progress_callback->DisplayFormattedModalError("Failed to seek to sector %u for track %u index %u", index_start,
track, index);
return false;
}
std::array<u8, CDImage::RAW_SECTOR_SIZE> sector;
for (u32 lba = 0; lba < index_length; lba++)
{
if ((lba % update_interval) == 0)
progress_callback->SetProgressValue(lba);
if (!image->ReadRawSector(sector.data()))
{
progress_callback->DisplayFormattedModalError("Failed to read sector %u from image", image->GetPositionOnDisc());
return false;
}
digest->Update(sector.data(), static_cast<u32>(sector.size()));
}
progress_callback->SetProgressValue(index_length);
return true;
}
static bool ReadTrack(CDImage* image, u8 track, MD5Digest* digest, ProgressCallback* progress_callback)
{
static constexpr u8 INDICES_TO_READ = 2;
progress_callback->PushState();
progress_callback->SetProgressRange(2);
for (u8 index = 0; index < INDICES_TO_READ; index++)
{
progress_callback->SetProgressValue(index);
// skip index 0 if data track
if (index == 0 && image->GetTrackMode(track) != CDImage::TrackMode::Audio)
continue;
progress_callback->PushState();
if (!ReadIndex(image, track, index, digest, progress_callback))
{
progress_callback->PopState();
progress_callback->PopState();
return false;
}
progress_callback->PopState();
}
progress_callback->SetProgressValue(INDICES_TO_READ);
progress_callback->PopState();
return true;
}
std::string HashToString(const Hash& hash)
{
return StringUtil::StdStringFromFormat("%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", hash[0],
hash[1], hash[2], hash[3], hash[4], hash[5], hash[6], hash[7], hash[8],
hash[9], hash[10], hash[11], hash[12], hash[13], hash[14], hash[15]);
}
bool GetImageHash(CDImage* image, Hash* out_hash,
ProgressCallback* progress_callback /*= ProgressCallback::NullProgressCallback*/)
{
MD5Digest digest;
progress_callback->SetProgressRange(image->GetTrackCount());
progress_callback->SetProgressValue(0);
progress_callback->PushState();
for (u32 i = 1; i <= image->GetTrackCount(); i++)
{
progress_callback->SetProgressValue(i - 1);
if (!ReadTrack(image, i, &digest, progress_callback))
{
progress_callback->PopState();
return false;
}
}
progress_callback->SetProgressValue(image->GetTrackCount());
digest.Final(out_hash->data());
return true;
}
bool GetTrackHash(CDImage* image, u8 track, Hash* out_hash,
ProgressCallback* progress_callback /*= ProgressCallback::NullProgressCallback*/)
{
MD5Digest digest;
if (!ReadTrack(image, track, &digest, progress_callback))
return false;
digest.Final(out_hash->data());
return true;
}
} // namespace CDImageHasher

View File

@ -0,0 +1,19 @@
#pragma once
#include "progress_callback.h"
#include "types.h"
#include <array>
#include <string>
class CDImage;
namespace CDImageHasher {
using Hash = std::array<u8, 16>;
std::string HashToString(const Hash& hash);
bool GetImageHash(CDImage* image, Hash* out_hash,
ProgressCallback* progress_callback = ProgressCallback::NullProgressCallback);
bool GetTrackHash(CDImage* image, u8 track, Hash* out_hash,
ProgressCallback* progress_callback = ProgressCallback::NullProgressCallback);
} // namespace CDImageHasher

View File

@ -42,6 +42,7 @@
<ClInclude Include="bitutils.h" />
<ClInclude Include="byte_stream.h" />
<ClInclude Include="cd_image.h" />
<ClInclude Include="cd_image_hasher.h" />
<ClInclude Include="cpu_detect.h" />
<ClInclude Include="cubeb_audio_stream.h" />
<ClInclude Include="d3d11\shader_cache.h" />
@ -86,6 +87,7 @@
<ClCompile Include="cd_image_bin.cpp" />
<ClCompile Include="cd_image_chd.cpp" />
<ClCompile Include="cd_image_cue.cpp" />
<ClCompile Include="cd_image_hasher.cpp" />
<ClCompile Include="cubeb_audio_stream.cpp" />
<ClCompile Include="d3d11\shader_cache.cpp" />
<ClCompile Include="d3d11\shader_compiler.cpp" />

View File

@ -65,6 +65,7 @@
<Filter>gl</Filter>
</ClInclude>
<ClInclude Include="window_info.h" />
<ClInclude Include="cd_image_hasher.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="jit_code_buffer.cpp" />
@ -124,6 +125,7 @@
<ClCompile Include="gl\context.cpp">
<Filter>gl</Filter>
</ClCompile>
<ClCompile Include="cd_image_hasher.cpp" />
</ItemGroup>
<ItemGroup>
<Natvis Include="bitfield.natvis" />