duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

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 }