Files
archived-pcsx2/pcsx2/CDVD/IsoHasher.cpp

194 lines
4.4 KiB
C++

// SPDX-FileCopyrightText: 2002-2026 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#include "CDVD/CDVDcommon.h"
#include "CDVD/IsoHasher.h"
#include "Host.h"
#include "common/Error.h"
#include "common/MD5Digest.h"
#include "fmt/format.h"
#include <algorithm>
IsoHasher::IsoHasher() = default;
IsoHasher::~IsoHasher()
{
Close();
}
std::string_view IsoHasher::GetTrackTypeString(u32 type)
{
switch (type)
{
case CDVD_AUDIO_TRACK:
return TRANSLATE_SV("CDVD", "Audio");
case CDVD_MODE1_TRACK:
return TRANSLATE_SV("CDVD", "Mode 1");
case CDVD_MODE2_TRACK:
return TRANSLATE_SV("CDVD", "Mode 2");
default:
return TRANSLATE_SV("CDVD", "Unknown");
}
}
bool IsoHasher::Open(std::string iso_path, Error* error)
{
Close();
m_is_locked = cdvdLock(error);
if (!m_is_locked)
return false;
CDVDsys_SetFile(CDVD_SourceType::Iso, std::move(iso_path));
CDVDsys_ChangeSource(CDVD_SourceType::Iso);
m_is_open = DoCDVDopen(error);
if (!m_is_open)
return false;
const s32 type = DoCDVDdetectDiskType();
switch (type)
{
case CDVD_TYPE_PSCD:
case CDVD_TYPE_PSCDDA:
case CDVD_TYPE_PS2CD:
case CDVD_TYPE_PS2CDDA:
m_is_cd = true;
break;
case CDVD_TYPE_PS2DVD:
m_is_cd = false;
break;
default:
Error::SetString(error, fmt::format("Unknown CDVD disk type {}", type));
return false;
}
cdvdTN tn;
if (CDVD->getTN(&tn) < 0)
{
Error::SetString(error, "Failed to get track count.");
return false;
}
for (u8 track = tn.strack; track <= tn.etrack; track++)
{
cdvdTD td, next_td;
if (CDVD->getTD(track, &td) < 0 || CDVD->getTD((track == tn.etrack) ? 0 : (track + 1), &next_td) < 0)
{
Error::SetString(error, fmt::format("Failed to get track range for {}", static_cast<unsigned>(track)));
return false;
}
// sanity check..
if (next_td.lsn < td.lsn)
{
Error::SetString(error,
fmt::format("Invalid track range for {} ({},{})", static_cast<unsigned>(track), td.lsn, next_td.lsn));
return false;
}
Track strack;
strack.number = track;
strack.type = td.type;
strack.start_lsn = td.lsn;
strack.sectors = next_td.lsn - td.lsn;
strack.size = static_cast<u64>(strack.sectors) * (m_is_cd ? 2352 : 2048);
m_tracks.push_back(std::move(strack));
}
return true;
}
void IsoHasher::Close()
{
if (!m_is_locked)
return;
cdvdUnlock();
m_is_locked = false;
if (!m_is_open)
return;
DoCDVDclose();
m_tracks.clear();
m_is_cd = false;
m_is_open = false;
}
void IsoHasher::ComputeHashes(ProgressCallback* callback)
{
callback->SetProgressRange(GetTrackCount());
callback->SetProgressValue(0);
callback->SetCancellable(true);
for (u32 index = 0; index < GetTrackCount(); index++)
{
Track& track = m_tracks[index];
if (!track.hash.empty())
{
callback->SetProgressValue(index + 1);
continue;
}
callback->PushState();
const bool result = ComputeTrackHash(track, callback);
callback->PopState();
if (!result)
break;
callback->SetProgressValue(index + 1);
callback->IncrementProgressValue();
}
callback->SetProgressValue(GetTrackCount());
}
bool IsoHasher::ComputeTrackHash(Track& track, ProgressCallback* callback)
{
// use 2048 byte reads for DVDs, otherwise 2352 raw.
const int read_mode = m_is_cd ? CDVD_MODE_2352 : CDVD_MODE_2048;
const u32 sector_size = m_is_cd ? 2352 : 2048;
std::vector<u8> sector_buffer(sector_size);
const u32 update_interval = std::max<u32>(track.sectors / 100u, 1u);
callback->SetStatusText(
fmt::format(TRANSLATE_FS("CDVD", "Calculating checksum for track {}..."), track.number).c_str());
callback->SetProgressRange(track.sectors);
MD5Digest md5;
for (u32 i = 0; i < track.sectors; i++)
{
if (callback->IsCancelled())
return false;
const u32 lsn = track.start_lsn + i;
if (DoCDVDreadSector(sector_buffer.data(), lsn, read_mode) != 0)
{
callback->DisplayFormattedModalError("Read error at LSN %u", lsn);
return false;
}
md5.Update(sector_buffer.data(), sector_size);
if ((i % update_interval) == 0)
callback->SetProgressValue(i);
}
u8 digest[16];
md5.Final(digest);
track.hash =
fmt::format("{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
digest[0], digest[1], digest[2], digest[3], digest[4], digest[5], digest[6], digest[7], digest[8],
digest[9], digest[10], digest[11], digest[12], digest[13], digest[14], digest[15]);
callback->SetProgressValue(track.sectors);
return true;
}