From 94c48a4c2dfbad6ffa81c964f65a3a2d69ebae3b Mon Sep 17 00:00:00 2001 From: Stenzek Date: Tue, 27 Sep 2022 22:56:46 +1000 Subject: [PATCH] CDVD: Add IsoHasher --- pcsx2/CDVD/IsoHasher.cpp | 200 ++++++++++++++++++++++++++++++++++++ pcsx2/CDVD/IsoHasher.h | 61 +++++++++++ pcsx2/CMakeLists.txt | 2 + pcsx2/pcsx2.vcxproj | 2 + pcsx2/pcsx2.vcxproj.filters | 17 ++- 5 files changed, 277 insertions(+), 5 deletions(-) create mode 100644 pcsx2/CDVD/IsoHasher.cpp create mode 100644 pcsx2/CDVD/IsoHasher.h diff --git a/pcsx2/CDVD/IsoHasher.cpp b/pcsx2/CDVD/IsoHasher.cpp new file mode 100644 index 0000000000..44cb9cb024 --- /dev/null +++ b/pcsx2/CDVD/IsoHasher.cpp @@ -0,0 +1,200 @@ +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2023 PCSX2 Dev Team + * + * PCSX2 is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with PCSX2. + * If not, see . + */ + +#include "PrecompiledHeader.h" + +#include "CDVD/CDVDcommon.h" +#include "CDVD/IsoHasher.h" +#include "Host.h" + +#include "common/Error.h" +#include "common/MD5Digest.h" +#include "common/StringUtil.h" + +#include "fmt/core.h" + +#include + +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(); + + CDVDsys_SetFile(CDVD_SourceType::Iso, std::move(iso_path)); + CDVDsys_ChangeSource(CDVD_SourceType::Iso); + + m_is_open = DoCDVDopen(); + if (!m_is_open) + { + Error::SetString(error, "Failed to open CDVD."); + 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(track))); + return false; + } + + // sanity check.. + if (next_td.lsn < td.lsn) + { + Error::SetString(error, + fmt::format("Invalid track range for {} ({},{})", static_cast(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(strack.sectors) * (m_is_cd ? 2352 : 2048); + m_tracks.push_back(std::move(strack)); + } + + return true; +} + +void IsoHasher::Close() +{ + 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 sector_buffer(sector_size); + + const u32 update_interval = std::max(track.sectors / 100u, 1u); + callback->SetFormattedStatusText("Computing hash for track %u...", track.number); + 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; +} diff --git a/pcsx2/CDVD/IsoHasher.h b/pcsx2/CDVD/IsoHasher.h new file mode 100644 index 0000000000..1b56b82bed --- /dev/null +++ b/pcsx2/CDVD/IsoHasher.h @@ -0,0 +1,61 @@ +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2023 PCSX2 Dev Team + * + * PCSX2 is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with PCSX2. + * If not, see . + */ + +#pragma once + +#include "common/Pcsx2Defs.h" +#include "common/ProgressCallback.h" + +#include +#include + +class Error; + +class IsoHasher +{ +public: + struct Track + { + u32 number; + u32 type; + u32 start_lsn; + u32 sectors; + u64 size; + std::string hash; + }; + +public: + IsoHasher(); + ~IsoHasher(); + + static std::string_view GetTrackTypeString(u32 type); + + const u32 GetTrackCount() const { return static_cast(m_tracks.size()); } + const Track& GetTrack(u32 n) const { return m_tracks.at(n); } + const std::vector& GetTracks() const { return m_tracks; } + bool IsCD() const { return m_is_cd; } + + bool Open(std::string iso_path, Error* error = nullptr); + void Close(); + + void ComputeHashes(ProgressCallback* callback = ProgressCallback::NullProgressCallback); + +private: + bool ComputeTrackHash(Track& track, ProgressCallback* callback); + + std::vector m_tracks; + bool m_is_open = false; + bool m_is_cd = false; +}; diff --git a/pcsx2/CMakeLists.txt b/pcsx2/CMakeLists.txt index 30295c89f3..e71c9b0d73 100644 --- a/pcsx2/CMakeLists.txt +++ b/pcsx2/CMakeLists.txt @@ -247,6 +247,7 @@ set(pcsx2CDVDSources CDVD/CDVDisoReader.cpp CDVD/CDVDdiscThread.cpp CDVD/InputIsoFile.cpp + CDVD/IsoHasher.cpp CDVD/IsoReader.cpp CDVD/OutputIsoFile.cpp CDVD/ChunksCache.cpp @@ -271,6 +272,7 @@ set(pcsx2CDVDHeaders CDVD/GzippedFileReader.h CDVD/ThreadedFileReader.h CDVD/IsoFileFormats.h + CDVD/IsoHasher.h CDVD/IsoReader.h CDVD/zlib_indexed.h ) diff --git a/pcsx2/pcsx2.vcxproj b/pcsx2/pcsx2.vcxproj index a7123198a0..989728c95f 100644 --- a/pcsx2/pcsx2.vcxproj +++ b/pcsx2/pcsx2.vcxproj @@ -122,6 +122,7 @@ + @@ -483,6 +484,7 @@ + diff --git a/pcsx2/pcsx2.vcxproj.filters b/pcsx2/pcsx2.vcxproj.filters index 5994ad37ad..99aaa5b53a 100644 --- a/pcsx2/pcsx2.vcxproj.filters +++ b/pcsx2/pcsx2.vcxproj.filters @@ -109,9 +109,6 @@ {5e741f2d-9e0b-4e43-b2a2-44a8370e1546} - - {c5237754-2509-4291-b869-c5dacc1a8aba} - {be861049-a142-4650-85a5-a2fdd4eef011} @@ -1409,7 +1406,12 @@ Misc\ImGui - + + System\ISO + + + System\ISO + @@ -2328,7 +2330,12 @@ Misc\ImGui - + + System\ISO + + + System\ISO +