From 8b04b74f270560803b13ba773fec722a31e33927 Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Mon, 8 Jun 2020 01:19:35 +1000 Subject: [PATCH] Common: Add a CD image hasher class --- src/common/CMakeLists.txt | 2 + src/common/cd_image.cpp | 22 ++++++ src/common/cd_image.h | 2 + src/common/cd_image_hasher.cpp | 116 ++++++++++++++++++++++++++++++ src/common/cd_image_hasher.h | 19 +++++ src/common/common.vcxproj | 2 + src/common/common.vcxproj.filters | 2 + 7 files changed, 165 insertions(+) create mode 100644 src/common/cd_image_hasher.cpp create mode 100644 src/common/cd_image_hasher.h diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 791a47e86..336dff1b4 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -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 diff --git a/src/common/cd_image.cpp b/src/common/cd_image.cpp index 3c924fa2f..9e1cdf0dd 100644 --- a/src/common/cd_image.cpp +++ b/src/common/cd_image.cpp @@ -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; diff --git a/src/common/cd_image.h b/src/common/cd_image.h index 6ad15e8d8..9847f6725 100644 --- a/src/common/cd_image.h +++ b/src/common/cd_image.h @@ -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; } diff --git a/src/common/cd_image_hasher.cpp b/src/common/cd_image_hasher.cpp new file mode 100644 index 000000000..44ff81746 --- /dev/null +++ b/src/common/cd_image_hasher.cpp @@ -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(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 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(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 \ No newline at end of file diff --git a/src/common/cd_image_hasher.h b/src/common/cd_image_hasher.h new file mode 100644 index 000000000..38d5f32b9 --- /dev/null +++ b/src/common/cd_image_hasher.h @@ -0,0 +1,19 @@ +#pragma once +#include "progress_callback.h" +#include "types.h" +#include +#include + +class CDImage; + +namespace CDImageHasher { + +using Hash = std::array; +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 \ No newline at end of file diff --git a/src/common/common.vcxproj b/src/common/common.vcxproj index 4174eb0f9..9848168ab 100644 --- a/src/common/common.vcxproj +++ b/src/common/common.vcxproj @@ -42,6 +42,7 @@ + @@ -86,6 +87,7 @@ + diff --git a/src/common/common.vcxproj.filters b/src/common/common.vcxproj.filters index b9f894ac8..b7d3b0b47 100644 --- a/src/common/common.vcxproj.filters +++ b/src/common/common.vcxproj.filters @@ -65,6 +65,7 @@ gl + @@ -124,6 +125,7 @@ gl +