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:
Stenzek
2025-11-22 22:04:40 +10:00
parent 48ec261043
commit 548887449e
16 changed files with 153 additions and 115 deletions

View File

@@ -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."));

View File

@@ -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();

View File

@@ -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);

View File

@@ -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())

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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();

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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.

View File

@@ -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;

View File

@@ -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);

View File

@@ -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

View File

@@ -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;

View File

@@ -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;