mirror of
https://github.com/stenzek/duckstation.git
synced 2026-02-13 18:04:32 +00:00
Misc: Eliminate usage of modal progress callback functions
Too error-prone, nested event loops are nasty. Error pattern is better.
This commit is contained in:
@@ -21,6 +21,7 @@
|
||||
|
||||
#include "common/align.h"
|
||||
#include "common/bitfield.h"
|
||||
#include "common/error.h"
|
||||
#include "common/fifo_queue.h"
|
||||
#include "common/file_system.h"
|
||||
#include "common/gsvector.h"
|
||||
@@ -1023,8 +1024,9 @@ bool CDROM::PrecacheMedia()
|
||||
return false;
|
||||
}
|
||||
|
||||
Error error;
|
||||
FullscreenUI::LoadingScreenProgressCallback callback;
|
||||
if (!s_reader.Precache(&callback))
|
||||
if (!s_reader.Precache(&callback, &error))
|
||||
{
|
||||
Host::AddOSDMessage(OSDMessageType::Error,
|
||||
TRANSLATE_STR("OSDMessage", "Precaching CD image failed, it may be unreliable."));
|
||||
|
||||
@@ -60,7 +60,7 @@ std::unique_ptr<CDImage> CDROMAsyncReader::RemoveMedia()
|
||||
return std::move(m_media);
|
||||
}
|
||||
|
||||
bool CDROMAsyncReader::Precache(ProgressCallback* callback)
|
||||
bool CDROMAsyncReader::Precache(ProgressCallback* callback, Error* error)
|
||||
{
|
||||
WaitForIdle();
|
||||
|
||||
@@ -70,11 +70,11 @@ bool CDROMAsyncReader::Precache(ProgressCallback* callback)
|
||||
else if (m_media->IsPrecached())
|
||||
return true;
|
||||
|
||||
const CDImage::PrecacheResult res = m_media->Precache(callback);
|
||||
const CDImage::PrecacheResult res = m_media->Precache(callback, error);
|
||||
if (res == CDImage::PrecacheResult::Unsupported)
|
||||
{
|
||||
// fall back to copy precaching
|
||||
std::unique_ptr<CDImage> memory_image = CDImage::CreateMemoryImage(m_media.get(), callback);
|
||||
std::unique_ptr<CDImage> memory_image = CDImage::CreateMemoryImage(m_media.get(), callback, error);
|
||||
if (memory_image)
|
||||
{
|
||||
const CDImage::LBA lba = m_media->GetPositionOnDisc();
|
||||
|
||||
@@ -47,7 +47,7 @@ public:
|
||||
std::unique_ptr<CDImage> RemoveMedia();
|
||||
|
||||
/// Precaches image, either to memory, or using the underlying image precache.
|
||||
bool Precache(ProgressCallback* callback);
|
||||
bool Precache(ProgressCallback* callback, Error* error);
|
||||
|
||||
void QueueReadSector(CDImage::LBA lba);
|
||||
|
||||
|
||||
@@ -2492,15 +2492,19 @@ void FullscreenUI::DrawCoverDownloaderWindow()
|
||||
std::unique_ptr<ProgressCallback> progress = OpenModalProgressDialog(FSUI_STR("Cover Downloader"), 1000.0f);
|
||||
System::QueueAsyncTask([progress = progress.release(), urls = StringUtil::SplitNewString(template_urls, '\n'),
|
||||
use_serial_names = use_serial_names]() {
|
||||
GameList::DownloadCovers(
|
||||
urls, use_serial_names, progress, [](const GameList::Entry* entry, std::string save_path) {
|
||||
// cache the cover path on our side once it's saved
|
||||
Host::RunOnCPUThread([path = entry->path, save_path = std::move(save_path)]() mutable {
|
||||
GPUThread::RunOnThread([path = std::move(path), save_path = std::move(save_path)]() mutable {
|
||||
FullscreenUI::SetCoverCacheEntry(std::move(path), std::move(save_path));
|
||||
});
|
||||
});
|
||||
});
|
||||
Error error;
|
||||
if (!GameList::DownloadCovers(
|
||||
urls, use_serial_names, progress, &error, [](const GameList::Entry* entry, std::string save_path) {
|
||||
// cache the cover path on our side once it's saved
|
||||
Host::RunOnCPUThread([path = entry->path, save_path = std::move(save_path)]() mutable {
|
||||
GPUThread::RunOnThread([path = std::move(path), save_path = std::move(save_path)]() mutable {
|
||||
FullscreenUI::SetCoverCacheEntry(std::move(path), std::move(save_path));
|
||||
});
|
||||
});
|
||||
}))
|
||||
{
|
||||
OpenInfoMessageDialog(FSUI_STR("Cover Download Error"), error.TakeDescription());
|
||||
}
|
||||
|
||||
// close the parent window if we weren't cancelled
|
||||
if (!progress->IsCancelled())
|
||||
|
||||
@@ -1688,7 +1688,8 @@ GameList::GetEntriesInDiscSet(const GameDatabase::DiscSetEntry* dsentry, bool lo
|
||||
}
|
||||
|
||||
bool GameList::DownloadCovers(const std::vector<std::string>& url_templates, bool use_serial,
|
||||
ProgressCallback* progress, std::function<void(const Entry*, std::string)> save_callback)
|
||||
ProgressCallback* progress, Error* error,
|
||||
std::function<void(const Entry*, std::string)> save_callback)
|
||||
{
|
||||
if (!progress)
|
||||
progress = ProgressCallback::NullProgressCallback;
|
||||
@@ -1713,8 +1714,10 @@ bool GameList::DownloadCovers(const std::vector<std::string>& url_templates, boo
|
||||
}
|
||||
if (!has_title && !has_save_title && !has_file_title && !has_serial)
|
||||
{
|
||||
progress->DisplayError(TRANSLATE_SV(
|
||||
"GameList", "URL template must contain at least one of ${title}, ${savetitle}, ${filetitle}, or ${serial}."));
|
||||
Error::SetStringView(
|
||||
error,
|
||||
TRANSLATE_SV("GameList",
|
||||
"URL template must contain at least one of ${title}, ${savetitle}, ${filetitle}, or ${serial}."));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1750,16 +1753,14 @@ bool GameList::DownloadCovers(const std::vector<std::string>& url_templates, boo
|
||||
}
|
||||
if (download_urls.empty())
|
||||
{
|
||||
progress->DisplayError(TRANSLATE_SV("GameList", "No URLs to download enumerated."));
|
||||
Error::SetStringView(error, TRANSLATE_SV("GameList", "No URLs to download enumerated."));
|
||||
return false;
|
||||
}
|
||||
|
||||
Error error;
|
||||
std::unique_ptr<HTTPDownloader> downloader(HTTPDownloader::Create(Host::GetHTTPUserAgent(), &error));
|
||||
std::unique_ptr<HTTPDownloader> downloader(HTTPDownloader::Create(Host::GetHTTPUserAgent(), error));
|
||||
if (!downloader)
|
||||
{
|
||||
progress->DisplayError(
|
||||
fmt::format(TRANSLATE_FS("GameList", "Failed to create HTTP downloader:\n{}"), error.GetDescription()));
|
||||
Error::AddPrefix(error, "Failed to create HTTP downloader: ");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -154,9 +154,8 @@ std::vector<std::pair<std::string_view, const Entry*>> GetEntriesInDiscSet(const
|
||||
|
||||
/// Downloads covers using the specified URL templates. By default, covers are saved by title, but this can be changed
|
||||
/// with the use_serial parameter. save_callback optionall takes the entry and the path the new cover is saved to.
|
||||
bool DownloadCovers(const std::vector<std::string>& url_templates, bool use_serial = false,
|
||||
ProgressCallback* progress = nullptr,
|
||||
std::function<void(const Entry*, std::string)> save_callback = {});
|
||||
bool DownloadCovers(const std::vector<std::string>& url_templates, bool use_serial, ProgressCallback* progress,
|
||||
Error* error, std::function<void(const Entry*, std::string)> save_callback = {});
|
||||
|
||||
// Custom properties support
|
||||
bool SaveCustomTitleForPath(const std::string& path, const std::string& custom_title);
|
||||
|
||||
@@ -58,15 +58,22 @@ void CoverDownloadWindow::onDownloadComplete()
|
||||
{
|
||||
emit coverRefreshRequested();
|
||||
|
||||
m_ui.status->setText(tr("Download complete."));
|
||||
|
||||
QString error;
|
||||
if (m_thread)
|
||||
{
|
||||
m_thread->join();
|
||||
if (!m_thread->getResult())
|
||||
{
|
||||
if (const std::string& err_str = m_thread->getError().GetDescription(); !err_str.empty())
|
||||
m_ui.status->setText(QString::fromStdString(err_str));
|
||||
}
|
||||
|
||||
m_thread.reset();
|
||||
}
|
||||
|
||||
updateEnabled();
|
||||
|
||||
m_ui.status->setText(tr("Download complete."));
|
||||
}
|
||||
|
||||
void CoverDownloadWindow::onStartClicked()
|
||||
@@ -127,5 +134,5 @@ CoverDownloadWindow::CoverDownloadThread::~CoverDownloadThread() = default;
|
||||
|
||||
void CoverDownloadWindow::CoverDownloadThread::runAsync()
|
||||
{
|
||||
GameList::DownloadCovers(m_urls, m_use_serials, this);
|
||||
m_result = GameList::DownloadCovers(m_urls, m_use_serials, this, &m_error);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "qtprogresscallback.h"
|
||||
#include "ui_coverdownloadwindow.h"
|
||||
|
||||
#include "common/error.h"
|
||||
#include "common/timer.h"
|
||||
#include "common/types.h"
|
||||
|
||||
@@ -37,12 +38,17 @@ private:
|
||||
CoverDownloadThread(QWidget* parent, const QString& urls, bool use_serials);
|
||||
~CoverDownloadThread();
|
||||
|
||||
ALWAYS_INLINE const Error& getError() const { return m_error; }
|
||||
ALWAYS_INLINE bool getResult() const { return m_result; }
|
||||
|
||||
protected:
|
||||
void runAsync() override;
|
||||
|
||||
private:
|
||||
std::vector<std::string> m_urls;
|
||||
bool m_use_serials;
|
||||
Error m_error;
|
||||
bool m_use_serials = false;
|
||||
bool m_result = false;
|
||||
};
|
||||
|
||||
void startThread();
|
||||
|
||||
@@ -445,10 +445,12 @@ void GameSummaryWidget::onComputeHashClicked()
|
||||
return;
|
||||
}
|
||||
|
||||
std::unique_ptr<CDImage> image = CDImage::Open(m_path.c_str(), false, nullptr);
|
||||
Error error;
|
||||
std::unique_ptr<CDImage> image = CDImage::Open(m_path.c_str(), false, &error);
|
||||
if (!image)
|
||||
{
|
||||
QtUtils::MessageBoxCritical(QtUtils::GetRootWidget(this), tr("Error"), tr("Failed to open CD image for hashing."));
|
||||
QtUtils::MessageBoxCritical(QtUtils::GetRootWidget(this), tr("Image Open Failed"),
|
||||
QString::fromStdString(error.GetDescription()));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -468,13 +470,15 @@ void GameSummaryWidget::onComputeHashClicked()
|
||||
progress_callback.PushState();
|
||||
|
||||
CDImageHasher::Hash hash;
|
||||
if (!CDImageHasher::GetTrackHash(image.get(), track, &hash, &progress_callback))
|
||||
if (!CDImageHasher::GetTrackHash(image.get(), track, &hash, &progress_callback, &error))
|
||||
{
|
||||
progress_callback.PopState();
|
||||
|
||||
if (progress_callback.IsCancelled())
|
||||
return;
|
||||
|
||||
QtUtils::MessageBoxCritical(QtUtils::GetRootWidget(this), tr("Hash Calculation Failed"),
|
||||
QString::fromStdString(error.GetDescription()));
|
||||
calculate_hash_success = false;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -120,9 +120,9 @@ std::unique_ptr<CDImage> CDImage::Open(const char* path, bool allow_patches, Err
|
||||
#endif
|
||||
if (FileSystem::FileExists(ppf_path.c_str()))
|
||||
{
|
||||
image = CDImage::OverlayPPFPatch(ppf_path.c_str(), std::move(image));
|
||||
image = CDImage::OverlayPPFPatch(ppf_path.c_str(), std::move(image), error);
|
||||
if (!image)
|
||||
Error::SetStringFmt(error, "Failed to apply ppf patch from '{}'.", ppf_path);
|
||||
Error::AddPrefixFmt(error, "Failed to apply ppf patch from '{}':\n", ppf_path);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -342,7 +342,7 @@ std::string CDImage::GetSubImageTitle(u32 index) const
|
||||
return {};
|
||||
}
|
||||
|
||||
CDImage::PrecacheResult CDImage::Precache(ProgressCallback* progress /*= ProgressCallback::NullProgressCallback*/)
|
||||
CDImage::PrecacheResult CDImage::Precache(ProgressCallback* progress, Error* error)
|
||||
{
|
||||
return PrecacheResult::Unsupported;
|
||||
}
|
||||
|
||||
@@ -233,10 +233,9 @@ public:
|
||||
static std::unique_ptr<CDImage> OpenPBPImage(const char* path, Error* error);
|
||||
static std::unique_ptr<CDImage> OpenM3uImage(const char* path, bool apply_patches, Error* error);
|
||||
static std::unique_ptr<CDImage> OpenDeviceImage(const char* path, Error* error);
|
||||
static std::unique_ptr<CDImage>
|
||||
CreateMemoryImage(CDImage* image, ProgressCallback* progress = ProgressCallback::NullProgressCallback);
|
||||
static std::unique_ptr<CDImage> CreateMemoryImage(CDImage* image, ProgressCallback* progress, Error* error);
|
||||
static std::unique_ptr<CDImage> OverlayPPFPatch(const char* path, std::unique_ptr<CDImage> parent_image,
|
||||
ProgressCallback* progress = ProgressCallback::NullProgressCallback);
|
||||
Error* error);
|
||||
|
||||
// Accessors.
|
||||
const std::string& GetPath() const { return m_filename; }
|
||||
@@ -309,7 +308,7 @@ public:
|
||||
virtual std::string GetSubImageTitle(u32 index) const;
|
||||
|
||||
// Returns true if the source supports precaching, which may be more optimal than an in-memory copy.
|
||||
virtual PrecacheResult Precache(ProgressCallback* progress = ProgressCallback::NullProgressCallback);
|
||||
virtual PrecacheResult Precache(ProgressCallback* progress, Error* error);
|
||||
virtual bool IsPrecached() const;
|
||||
|
||||
// Returns the size on disk of the image. This could be multiple files.
|
||||
|
||||
@@ -65,7 +65,7 @@ public:
|
||||
|
||||
bool ReadSubChannelQ(SubChannelQ* subq, const Index& index, LBA lba_in_index) override;
|
||||
bool HasSubchannelData() const override;
|
||||
PrecacheResult Precache(ProgressCallback* progress) override;
|
||||
PrecacheResult Precache(ProgressCallback* progress, Error* error) override;
|
||||
bool IsPrecached() const override;
|
||||
s64 GetSizeOnDisk() const override;
|
||||
|
||||
@@ -444,7 +444,7 @@ bool CDImageCHD::HasSubchannelData() const
|
||||
return (m_tracks.front().submode != CDImage::SubchannelMode::None);
|
||||
}
|
||||
|
||||
CDImage::PrecacheResult CDImageCHD::Precache(ProgressCallback* progress)
|
||||
CDImage::PrecacheResult CDImageCHD::Precache(ProgressCallback* progress, Error* error)
|
||||
{
|
||||
if (m_precached)
|
||||
return CDImage::PrecacheResult::Success;
|
||||
@@ -461,8 +461,11 @@ CDImage::PrecacheResult CDImageCHD::Precache(ProgressCallback* progress)
|
||||
static_cast<ProgressCallback*>(param)->SetStatusText(TinyString::from_format("{}MB of {}MB", pos_mb, total_mb));
|
||||
};
|
||||
|
||||
if (chd_precache_progress(m_chd, callback, progress) != CHDERR_NONE)
|
||||
if (const chd_error err = chd_precache_progress(m_chd, callback, progress); err != CHDERR_NONE)
|
||||
{
|
||||
Error::SetStringFmt(error, "chd_precache_progress() failed: {}", chd_error_string(err));
|
||||
return CDImage::PrecacheResult::ReadError;
|
||||
}
|
||||
|
||||
m_precached = true;
|
||||
return CDImage::PrecacheResult::Success;
|
||||
|
||||
@@ -1,36 +1,39 @@
|
||||
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
|
||||
|
||||
#include "cd_image_hasher.h"
|
||||
#include "cd_image.h"
|
||||
#include "host.h"
|
||||
|
||||
#include "common/error.h"
|
||||
#include "common/md5_digest.h"
|
||||
#include "common/progress_callback.h"
|
||||
#include "common/string_util.h"
|
||||
|
||||
#include "fmt/format.h"
|
||||
|
||||
namespace CDImageHasher {
|
||||
|
||||
static bool ReadIndex(CDImage* image, u8 track, u8 index, MD5Digest* digest, ProgressCallback* progress_callback);
|
||||
static bool ReadTrack(CDImage* image, u8 track, MD5Digest* digest, ProgressCallback* progress_callback);
|
||||
static bool ReadIndex(CDImage* image, u8 track, u8 index, MD5Digest* digest, ProgressCallback* progress_callback,
|
||||
Error* error);
|
||||
static bool ReadTrack(CDImage* image, u8 track, MD5Digest* digest, ProgressCallback* progress_callback, Error* error);
|
||||
|
||||
} // namespace CDImageHasher
|
||||
|
||||
bool CDImageHasher::ReadIndex(CDImage* image, u8 track, u8 index, MD5Digest* digest,
|
||||
ProgressCallback* progress_callback)
|
||||
ProgressCallback* progress_callback, Error* error)
|
||||
{
|
||||
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->SetStatusText(
|
||||
fmt::format(TRANSLATE_FS("CDImageHasher", "Computing hash for Track {}/Index {}..."), track, index).c_str());
|
||||
progress_callback->FormatStatusText(TRANSLATE_FS("CDImageHasher", "Computing hash for Track {}/Index {}..."), track,
|
||||
index);
|
||||
progress_callback->SetProgressRange(index_length);
|
||||
|
||||
if (!image->Seek(index_start))
|
||||
{
|
||||
progress_callback->FormatModalError("Failed to seek to sector {} for track {} index {}", index_start, track, index);
|
||||
Error::SetStringFmt(error, "Failed to seek to sector {} for track {} index {}", index_start, track, index);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -45,7 +48,7 @@ bool CDImageHasher::ReadIndex(CDImage* image, u8 track, u8 index, MD5Digest* dig
|
||||
|
||||
if (!image->ReadRawSector(sector.data(), nullptr))
|
||||
{
|
||||
progress_callback->FormatModalError("Failed to read sector {} from image", image->GetPositionOnDisc());
|
||||
Error::SetStringFmt(error, "Failed to read sector {} from image", image->GetPositionOnDisc());
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -56,7 +59,8 @@ bool CDImageHasher::ReadIndex(CDImage* image, u8 track, u8 index, MD5Digest* dig
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDImageHasher::ReadTrack(CDImage* image, u8 track, MD5Digest* digest, ProgressCallback* progress_callback)
|
||||
bool CDImageHasher::ReadTrack(CDImage* image, u8 track, MD5Digest* digest, ProgressCallback* progress_callback,
|
||||
Error* error)
|
||||
{
|
||||
static constexpr u8 INDICES_TO_READ = 2;
|
||||
|
||||
@@ -76,7 +80,7 @@ bool CDImageHasher::ReadTrack(CDImage* image, u8 track, MD5Digest* digest, Progr
|
||||
|
||||
progress++;
|
||||
progress_callback->PushState();
|
||||
if (!ReadIndex(image, track, index, digest, progress_callback))
|
||||
if (!ReadIndex(image, track, index, digest, progress_callback, error))
|
||||
{
|
||||
progress_callback->PopState();
|
||||
progress_callback->PopState();
|
||||
@@ -110,8 +114,7 @@ std::optional<CDImageHasher::Hash> CDImageHasher::HashFromString(std::string_vie
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
bool CDImageHasher::GetImageHash(CDImage* image, Hash* out_hash,
|
||||
ProgressCallback* progress_callback /*= ProgressCallback::NullProgressCallback*/)
|
||||
bool CDImageHasher::GetImageHash(CDImage* image, Hash* out_hash, ProgressCallback* progress_callback, Error* error)
|
||||
{
|
||||
MD5Digest digest;
|
||||
|
||||
@@ -123,7 +126,7 @@ bool CDImageHasher::GetImageHash(CDImage* image, Hash* out_hash,
|
||||
for (u32 i = 1; i <= image->GetTrackCount(); i++)
|
||||
{
|
||||
progress_callback->SetProgressValue(i - 1);
|
||||
if (!ReadTrack(image, static_cast<u8>(i), &digest, progress_callback))
|
||||
if (!ReadTrack(image, static_cast<u8>(i), &digest, progress_callback, error))
|
||||
{
|
||||
progress_callback->PopState();
|
||||
return false;
|
||||
@@ -135,11 +138,11 @@ bool CDImageHasher::GetImageHash(CDImage* image, Hash* out_hash,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDImageHasher::GetTrackHash(CDImage* image, u8 track, Hash* out_hash,
|
||||
ProgressCallback* progress_callback /*= ProgressCallback::NullProgressCallback*/)
|
||||
bool CDImageHasher::GetTrackHash(CDImage* image, u8 track, Hash* out_hash, ProgressCallback* progress_callback,
|
||||
Error* error)
|
||||
{
|
||||
MD5Digest digest;
|
||||
if (!ReadTrack(image, track, &digest, progress_callback))
|
||||
if (!ReadTrack(image, track, &digest, progress_callback, error))
|
||||
return false;
|
||||
|
||||
digest.Final(*out_hash);
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
|
||||
|
||||
#pragma once
|
||||
#include "common/progress_callback.h"
|
||||
|
||||
#include "common/types.h"
|
||||
|
||||
#include <array>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
class CDImage;
|
||||
class Error;
|
||||
class ProgressCallback;
|
||||
|
||||
namespace CDImageHasher {
|
||||
|
||||
@@ -16,9 +19,7 @@ using Hash = std::array<u8, 16>;
|
||||
std::string HashToString(const Hash& hash);
|
||||
std::optional<Hash> HashFromString(std::string_view str);
|
||||
|
||||
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);
|
||||
bool GetImageHash(CDImage* image, Hash* out_hash, ProgressCallback* progress_callback, Error* error);
|
||||
bool GetTrackHash(CDImage* image, u8 track, Hash* out_hash, ProgressCallback* progress_callback, Error* error);
|
||||
|
||||
} // namespace CDImageHasher
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "cd_image.h"
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/error.h"
|
||||
#include "common/file_system.h"
|
||||
#include "common/log.h"
|
||||
#include "common/path.h"
|
||||
@@ -21,7 +22,7 @@ public:
|
||||
CDImageMemory();
|
||||
~CDImageMemory() override;
|
||||
|
||||
bool CopyImage(CDImage* image, ProgressCallback* progress);
|
||||
bool CopyImage(CDImage* image, ProgressCallback* progress, Error* error);
|
||||
|
||||
bool IsPrecached() const override;
|
||||
|
||||
@@ -43,7 +44,7 @@ CDImageMemory::~CDImageMemory()
|
||||
std::free(m_memory);
|
||||
}
|
||||
|
||||
bool CDImageMemory::CopyImage(CDImage* image, ProgressCallback* progress)
|
||||
bool CDImageMemory::CopyImage(CDImage* image, ProgressCallback* progress, Error* error)
|
||||
{
|
||||
// figure out the total number of sectors (not including blank pregaps)
|
||||
m_memory_sectors = 0;
|
||||
@@ -57,7 +58,7 @@ bool CDImageMemory::CopyImage(CDImage* image, ProgressCallback* progress)
|
||||
if (m_memory_sectors == 0 || (static_cast<u64>(RAW_SECTOR_SIZE) * static_cast<u64>(m_memory_sectors)) >=
|
||||
static_cast<u64>(std::numeric_limits<size_t>::max()))
|
||||
{
|
||||
progress->ModalError("Insufficient address space");
|
||||
Error::SetStringView(error, "Insufficient address space");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -67,7 +68,7 @@ bool CDImageMemory::CopyImage(CDImage* image, ProgressCallback* progress)
|
||||
static_cast<u8*>(std::malloc(static_cast<size_t>(RAW_SECTOR_SIZE) * static_cast<size_t>(m_memory_sectors)));
|
||||
if (!m_memory)
|
||||
{
|
||||
progress->FormatModalError("Failed to allocate memory for {} sectors", m_memory_sectors);
|
||||
Error::SetStringFmt(error, "Failed to allocate memory for {} sectors", m_memory_sectors);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -138,11 +139,10 @@ bool CDImageMemory::ReadSectorFromIndex(void* buffer, const Index& index, LBA lb
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<CDImage>
|
||||
CDImage::CreateMemoryImage(CDImage* image, ProgressCallback* progress /* = ProgressCallback::NullProgressCallback */)
|
||||
std::unique_ptr<CDImage> CDImage::CreateMemoryImage(CDImage* image, ProgressCallback* progress, Error* error)
|
||||
{
|
||||
std::unique_ptr<CDImageMemory> memory_image = std::make_unique<CDImageMemory>();
|
||||
if (!memory_image->CopyImage(image, progress))
|
||||
if (!memory_image->CopyImage(image, progress, error))
|
||||
return {};
|
||||
|
||||
return memory_image;
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
|
||||
|
||||
#include "cd_image.h"
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/error.h"
|
||||
#include "common/file_system.h"
|
||||
#include "common/log.h"
|
||||
#include "common/path.h"
|
||||
@@ -29,7 +30,7 @@ public:
|
||||
CDImagePPF();
|
||||
~CDImagePPF() override;
|
||||
|
||||
bool Open(const char* filename, std::unique_ptr<CDImage> parent_image);
|
||||
bool Open(const char* filename, std::unique_ptr<CDImage> parent_image, Error* error);
|
||||
|
||||
bool ReadSubChannelQ(SubChannelQ* subq, const Index& index, LBA lba_in_index) override;
|
||||
bool HasSubchannelData() const override;
|
||||
@@ -37,18 +38,18 @@ public:
|
||||
|
||||
std::string GetSubImageTitle(u32 index) const override;
|
||||
|
||||
PrecacheResult Precache(ProgressCallback* progress = ProgressCallback::NullProgressCallback) override;
|
||||
PrecacheResult Precache(ProgressCallback* progress, Error* error) override;
|
||||
|
||||
protected:
|
||||
bool ReadSectorFromIndex(void* buffer, const Index& index, LBA lba_in_index) override;
|
||||
|
||||
private:
|
||||
bool ReadV1Patch(std::FILE* fp);
|
||||
bool ReadV2Patch(std::FILE* fp);
|
||||
bool ReadV3Patch(std::FILE* fp);
|
||||
bool ReadV1Patch(std::FILE* fp, Error* error);
|
||||
bool ReadV2Patch(std::FILE* fp, Error* error);
|
||||
bool ReadV3Patch(std::FILE* fp, Error* error);
|
||||
u32 ReadFileIDDiz(std::FILE* fp, u32 version);
|
||||
|
||||
bool AddPatch(u64 offset, const u8* patch, u32 patch_size);
|
||||
bool AddPatch(u64 offset, const u8* patch, u32 patch_size, Error* error);
|
||||
|
||||
std::unique_ptr<CDImage> m_parent_image;
|
||||
std::vector<u8> m_replacement_data;
|
||||
@@ -63,12 +64,12 @@ CDImagePPF::CDImagePPF() = default;
|
||||
|
||||
CDImagePPF::~CDImagePPF() = default;
|
||||
|
||||
bool CDImagePPF::Open(const char* filename, std::unique_ptr<CDImage> parent_image)
|
||||
bool CDImagePPF::Open(const char* filename, std::unique_ptr<CDImage> parent_image, Error* error)
|
||||
{
|
||||
auto fp = FileSystem::OpenManagedSharedCFile(filename, "rb", FileSystem::FileShareMode::DenyWrite);
|
||||
auto fp = FileSystem::OpenManagedSharedCFile(filename, "rb", FileSystem::FileShareMode::DenyWrite, error);
|
||||
if (!fp)
|
||||
{
|
||||
ERROR_LOG("Failed to open '{}'", Path::GetFileName(filename));
|
||||
Error::AddPrefixFmt(error, "Failed to open '{}'", Path::GetFileName(filename));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -77,7 +78,7 @@ bool CDImagePPF::Open(const char* filename, std::unique_ptr<CDImage> parent_imag
|
||||
u32 magic;
|
||||
if (std::fread(&magic, sizeof(magic), 1, fp.get()) != 1)
|
||||
{
|
||||
ERROR_LOG("Failed to read magic from '{}'", Path::GetFileName(filename));
|
||||
Error::SetErrno(error, "Failed to read PPF magic: ", errno);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -93,13 +94,13 @@ bool CDImagePPF::Open(const char* filename, std::unique_ptr<CDImage> parent_imag
|
||||
m_parent_image = std::move(parent_image);
|
||||
|
||||
if (magic == 0x33465050) // PPF3
|
||||
return ReadV3Patch(fp.get());
|
||||
return ReadV3Patch(fp.get(), error);
|
||||
else if (magic == 0x32465050) // PPF2
|
||||
return ReadV2Patch(fp.get());
|
||||
return ReadV2Patch(fp.get(), error);
|
||||
else if (magic == 0x31465050) // PPF1
|
||||
return ReadV1Patch(fp.get());
|
||||
return ReadV1Patch(fp.get(), error);
|
||||
|
||||
ERROR_LOG("Unknown PPF magic {:08X}", magic);
|
||||
Error::SetStringFmt(error, "Unknown PPF magic {:08X}", magic);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -143,12 +144,12 @@ u32 CDImagePPF::ReadFileIDDiz(std::FILE* fp, u32 version)
|
||||
return dlen;
|
||||
}
|
||||
|
||||
bool CDImagePPF::ReadV1Patch(std::FILE* fp)
|
||||
bool CDImagePPF::ReadV1Patch(std::FILE* fp, Error* error)
|
||||
{
|
||||
char desc[DESC_SIZE + 1] = {};
|
||||
if (std::fseek(fp, 6, SEEK_SET) != 0 || std::fread(desc, sizeof(char), DESC_SIZE, fp) != DESC_SIZE) [[unlikely]]
|
||||
{
|
||||
ERROR_LOG("Failed to read description");
|
||||
Error::SetErrno(error, "Failed to read description: ", errno);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -156,16 +157,22 @@ bool CDImagePPF::ReadV1Patch(std::FILE* fp)
|
||||
if (std::fseek(fp, 0, SEEK_END) != 0 || (filelen = static_cast<u32>(std::ftell(fp))) == 0 || filelen < 56)
|
||||
[[unlikely]]
|
||||
{
|
||||
ERROR_LOG("Invalid ppf file");
|
||||
Error::SetErrno(error, "Invalid ppf file: ", errno);
|
||||
return false;
|
||||
}
|
||||
|
||||
u32 count = filelen - 56;
|
||||
if (count <= 0)
|
||||
{
|
||||
Error::SetStringView(error, "Invalid count/filelen");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (std::fseek(fp, 56, SEEK_SET) != 0)
|
||||
{
|
||||
Error::SetErrno(error, "Failed to seek to patch data: ", errno);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<u8> temp;
|
||||
while (count > 0)
|
||||
@@ -175,18 +182,18 @@ bool CDImagePPF::ReadV1Patch(std::FILE* fp)
|
||||
if (std::fread(&offset, sizeof(offset), 1, fp) != 1 || std::fread(&chunk_size, sizeof(chunk_size), 1, fp) != 1)
|
||||
[[unlikely]]
|
||||
{
|
||||
ERROR_LOG("Incomplete ppf");
|
||||
Error::SetErrno(error, "Incomplete ppf: ", errno);
|
||||
return false;
|
||||
}
|
||||
|
||||
temp.resize(chunk_size);
|
||||
if (std::fread(temp.data(), 1, chunk_size, fp) != chunk_size) [[unlikely]]
|
||||
{
|
||||
ERROR_LOG("Failed to read patch data");
|
||||
Error::SetErrno(error, "Failed to read patch data: ", errno);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!AddPatch(offset, temp.data(), chunk_size)) [[unlikely]]
|
||||
if (!AddPatch(offset, temp.data(), chunk_size, error)) [[unlikely]]
|
||||
return false;
|
||||
|
||||
count -= sizeof(offset) + sizeof(chunk_size) + chunk_size;
|
||||
@@ -196,12 +203,12 @@ bool CDImagePPF::ReadV1Patch(std::FILE* fp)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDImagePPF::ReadV2Patch(std::FILE* fp)
|
||||
bool CDImagePPF::ReadV2Patch(std::FILE* fp, Error* error)
|
||||
{
|
||||
char desc[DESC_SIZE + 1] = {};
|
||||
if (std::fseek(fp, 6, SEEK_SET) != 0 || std::fread(desc, sizeof(char), DESC_SIZE, fp) != DESC_SIZE) [[unlikely]]
|
||||
{
|
||||
ERROR_LOG("Failed to read description");
|
||||
Error::SetErrno(error, "Failed to read description: ", errno);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -212,7 +219,7 @@ bool CDImagePPF::ReadV2Patch(std::FILE* fp)
|
||||
u32 origlen;
|
||||
if (std::fseek(fp, 56, SEEK_SET) != 0 || std::fread(&origlen, sizeof(origlen), 1, fp) != 1) [[unlikely]]
|
||||
{
|
||||
ERROR_LOG("Failed to read size");
|
||||
Error::SetErrno(error, "Failed to read size: ", errno);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -220,7 +227,7 @@ bool CDImagePPF::ReadV2Patch(std::FILE* fp)
|
||||
temp.resize(BLOCKCHECK_SIZE);
|
||||
if (std::fread(temp.data(), 1, BLOCKCHECK_SIZE, fp) != BLOCKCHECK_SIZE) [[unlikely]]
|
||||
{
|
||||
ERROR_LOG("Failed to read blockcheck data");
|
||||
Error::SetErrno(error, "Failed to read blockcheck data: ", errno);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -245,7 +252,7 @@ bool CDImagePPF::ReadV2Patch(std::FILE* fp)
|
||||
if (std::fseek(fp, 0, SEEK_END) != 0 || (filelen = static_cast<u32>(std::ftell(fp))) == 0 || filelen < 1084)
|
||||
[[unlikely]]
|
||||
{
|
||||
ERROR_LOG("Invalid ppf file");
|
||||
Error::SetErrno(error, "Invalid ppf file: ", errno);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -266,18 +273,18 @@ bool CDImagePPF::ReadV2Patch(std::FILE* fp)
|
||||
if (std::fread(&offset, sizeof(offset), 1, fp) != 1 || std::fread(&chunk_size, sizeof(chunk_size), 1, fp) != 1)
|
||||
[[unlikely]]
|
||||
{
|
||||
ERROR_LOG("Incomplete ppf");
|
||||
Error::SetErrno(error, "Incomplete ppf: ", errno);
|
||||
return false;
|
||||
}
|
||||
|
||||
temp.resize(chunk_size);
|
||||
if (std::fread(temp.data(), 1, chunk_size, fp) != chunk_size) [[unlikely]]
|
||||
{
|
||||
ERROR_LOG("Failed to read patch data");
|
||||
Error::SetErrno(error, "Failed to read patch data: ", errno);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!AddPatch(offset, temp.data(), chunk_size))
|
||||
if (!AddPatch(offset, temp.data(), chunk_size, error))
|
||||
return false;
|
||||
|
||||
count -= sizeof(offset) + sizeof(chunk_size) + chunk_size;
|
||||
@@ -287,12 +294,12 @@ bool CDImagePPF::ReadV2Patch(std::FILE* fp)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDImagePPF::ReadV3Patch(std::FILE* fp)
|
||||
bool CDImagePPF::ReadV3Patch(std::FILE* fp, Error* error)
|
||||
{
|
||||
char desc[DESC_SIZE + 1] = {};
|
||||
if (std::fseek(fp, 6, SEEK_SET) != 0 || std::fread(desc, sizeof(char), DESC_SIZE, fp) != DESC_SIZE)
|
||||
{
|
||||
ERROR_LOG("Failed to read description");
|
||||
Error::SetErrno(error, "Failed to read description: ", errno);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -306,7 +313,7 @@ bool CDImagePPF::ReadV3Patch(std::FILE* fp)
|
||||
if (std::fseek(fp, 56, SEEK_SET) != 0 || std::fread(&image_type, sizeof(image_type), 1, fp) != 1 ||
|
||||
std::fread(&block_check, sizeof(block_check), 1, fp) != 1 || std::fread(&undo, sizeof(undo), 1, fp) != 1)
|
||||
{
|
||||
ERROR_LOG("Failed to read headers");
|
||||
Error::SetErrno(error, "Failed to read headers: ", errno);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -318,7 +325,7 @@ bool CDImagePPF::ReadV3Patch(std::FILE* fp)
|
||||
u32 seekpos = (block_check) ? 1084 : 60;
|
||||
if (seekpos >= count)
|
||||
{
|
||||
ERROR_LOG("File is too short");
|
||||
Error::SetStringView(error, "File is too short");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -328,7 +335,7 @@ bool CDImagePPF::ReadV3Patch(std::FILE* fp)
|
||||
const u32 extralen = idlen + 18 + 16 + 2;
|
||||
if (count < extralen)
|
||||
{
|
||||
ERROR_LOG("File is too short (diz)");
|
||||
Error::SetStringView(error, "File is too short (diz)");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -336,7 +343,10 @@ bool CDImagePPF::ReadV3Patch(std::FILE* fp)
|
||||
}
|
||||
|
||||
if (std::fseek(fp, seekpos, SEEK_SET) != 0)
|
||||
{
|
||||
Error::SetErrno(error, "Failed to seek to patch data: ", errno);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<u8> temp;
|
||||
|
||||
@@ -346,18 +356,18 @@ bool CDImagePPF::ReadV3Patch(std::FILE* fp)
|
||||
u8 chunk_size;
|
||||
if (std::fread(&offset, sizeof(offset), 1, fp) != 1 || std::fread(&chunk_size, sizeof(chunk_size), 1, fp) != 1)
|
||||
{
|
||||
ERROR_LOG("Incomplete ppf");
|
||||
Error::SetErrno(error, "Incomplete ppf: ", errno);
|
||||
return false;
|
||||
}
|
||||
|
||||
temp.resize(chunk_size);
|
||||
if (std::fread(temp.data(), 1, chunk_size, fp) != chunk_size)
|
||||
{
|
||||
ERROR_LOG("Failed to read patch data");
|
||||
Error::SetErrno(error, "Failed to read patch data: ", errno);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!AddPatch(offset, temp.data(), chunk_size))
|
||||
if (!AddPatch(offset, temp.data(), chunk_size, error))
|
||||
return false;
|
||||
|
||||
count -= sizeof(offset) + sizeof(chunk_size) + chunk_size;
|
||||
@@ -367,7 +377,7 @@ bool CDImagePPF::ReadV3Patch(std::FILE* fp)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDImagePPF::AddPatch(u64 offset, const u8* patch, u32 patch_size)
|
||||
bool CDImagePPF::AddPatch(u64 offset, const u8* patch, u32 patch_size, Error* error)
|
||||
{
|
||||
DEBUG_LOG("Starting applying patch of {} bytes at at offset {}", patch_size, offset);
|
||||
|
||||
@@ -391,7 +401,7 @@ bool CDImagePPF::AddPatch(u64 offset, const u8* patch, u32 patch_size)
|
||||
if (!m_parent_image->Seek(sector_index) ||
|
||||
!m_parent_image->ReadRawSector(&m_replacement_data[replacement_buffer_start], nullptr))
|
||||
{
|
||||
ERROR_LOG("Failed to read sector {} from parent image", sector_index);
|
||||
Error::SetStringFmt(error, "Failed to read sector {} from parent image", sector_index);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -425,9 +435,9 @@ std::string CDImagePPF::GetSubImageTitle(u32 index) const
|
||||
return (index == 0) ? m_parent_image->GetSubImageTitle(index) : std::string();
|
||||
}
|
||||
|
||||
CDImage::PrecacheResult CDImagePPF::Precache(ProgressCallback* progress /*= ProgressCallback::NullProgressCallback*/)
|
||||
CDImage::PrecacheResult CDImagePPF::Precache(ProgressCallback* progress, Error* error)
|
||||
{
|
||||
return m_parent_image->Precache(progress);
|
||||
return m_parent_image->Precache(progress, error);
|
||||
}
|
||||
|
||||
bool CDImagePPF::ReadSectorFromIndex(void* buffer, const Index& index, LBA lba_in_index)
|
||||
@@ -448,11 +458,10 @@ s64 CDImagePPF::GetSizeOnDisk() const
|
||||
return m_patch_size + m_parent_image->GetSizeOnDisk();
|
||||
}
|
||||
|
||||
std::unique_ptr<CDImage> CDImage::OverlayPPFPatch(const char* path, std::unique_ptr<CDImage> parent_image,
|
||||
ProgressCallback* progress)
|
||||
std::unique_ptr<CDImage> CDImage::OverlayPPFPatch(const char* path, std::unique_ptr<CDImage> parent_image, Error* error)
|
||||
{
|
||||
std::unique_ptr<CDImagePPF> ppf_image = std::make_unique<CDImagePPF>();
|
||||
if (!ppf_image->Open(path, std::move(parent_image)))
|
||||
if (!ppf_image->Open(path, std::move(parent_image), error))
|
||||
return {};
|
||||
|
||||
return ppf_image;
|
||||
|
||||
Reference in New Issue
Block a user