diff --git a/src/core/game_list.cpp b/src/core/game_list.cpp index 3950446eb..37ef86839 100644 --- a/src/core/game_list.cpp +++ b/src/core/game_list.cpp @@ -1735,8 +1735,9 @@ std::string GameList::GetGameIconPath(std::string_view serial, std::string_view StringUtil::Strlcpy(serial_entry->serial, index_serial.view(), sizeof(serial_entry->serial)); // Try extracting an icon. + Error error; MemoryCardImage::DataArray data; - if (MemoryCardImage::LoadFromFile(&data, memcard_path.c_str())) + if (MemoryCardImage::LoadFromFile(&data, memcard_path.c_str(), &error)) { std::vector files = MemoryCardImage::EnumerateFiles(data, false); if (!files.empty()) @@ -1760,6 +1761,10 @@ std::string GameList::GetGameIconPath(std::string_view serial, std::string_view } } } + else + { + ERROR_LOG("Failed to load memory card '{}': {}", Path::GetFileName(memcard_path), error.GetDescription()); + } UpdateMemcardTimestampCache(*serial_entry); return ret; diff --git a/src/core/memory_card.cpp b/src/core/memory_card.cpp index e6dd2b007..85757f5db 100644 --- a/src/core/memory_card.cpp +++ b/src/core/memory_card.cpp @@ -10,6 +10,7 @@ #include "common/bitutils.h" #include "common/byte_stream.h" +#include "common/error.h" #include "common/file_system.h" #include "common/log.h" #include "common/path.h" @@ -298,11 +299,20 @@ std::unique_ptr MemoryCard::Open(std::string_view filename) { std::unique_ptr mc = std::make_unique(); mc->m_filename = filename; - if (!mc->LoadFromFile()) [[unlikely]] + + Error error; + if (!FileSystem::FileExists(mc->m_filename.c_str())) [[unlikely]] { - INFO_LOG("Memory card at '{}' could not be read, formatting.", mc->m_filename); Host::AddIconOSDMessage(fmt::format("memory_card_{}", filename), ICON_FA_SD_CARD, - fmt::format(TRANSLATE_FS("OSDMessage", "Memory card '{}' could not be read, formatting."), + fmt::format(TRANSLATE_FS("OSDMessage", "Memory card '{}' does not exist, creating."), + Path::GetFileName(filename), Host::OSD_INFO_DURATION)); + mc->Format(); + mc->SaveIfChanged(true); + } + else if (!MemoryCardImage::LoadFromFile(&mc->m_data, mc->m_filename.c_str(), &error)) [[unlikely]] + { + Host::AddIconOSDMessage(fmt::format("memory_card_{}", filename), ICON_FA_SD_CARD, + fmt::format(TRANSLATE_FS("OSDMessage", "Memory card '{}' could not be read: {}"), Path::GetFileName(filename), Host::OSD_INFO_DURATION)); mc->Format(); } @@ -316,11 +326,6 @@ void MemoryCard::Format() m_changed = true; } -bool MemoryCard::LoadFromFile() -{ - return MemoryCardImage::LoadFromFile(&m_data, m_filename.c_str()); -} - bool MemoryCard::SaveIfChanged(bool display_osd_message) { m_save_event.Deactivate(); @@ -341,14 +346,17 @@ bool MemoryCard::SaveIfChanged(bool display_osd_message) display_name = FileSystem::GetDisplayNameFromPath(m_filename); } - if (!MemoryCardImage::SaveToFile(m_data, m_filename.c_str())) + INFO_LOG("Saving memory card to {}...", Path::GetFileTitle(m_filename)); + + Error error; + if (!MemoryCardImage::SaveToFile(m_data, m_filename.c_str(), &error)) { if (display_osd_message) { - Host::AddIconOSDMessage( - std::move(osd_key), ICON_FA_SD_CARD, - fmt::format(TRANSLATE_FS("OSDMessage", "Failed to save memory card to '{}'."), Path::GetFileName(display_name)), - Host::OSD_ERROR_DURATION); + Host::AddIconOSDMessage(std::move(osd_key), ICON_FA_SD_CARD, + fmt::format(TRANSLATE_FS("OSDMessage", "Failed to save memory card to '{}': {}"), + Path::GetFileName(display_name), error.GetDescription()), + Host::OSD_ERROR_DURATION); } return false; diff --git a/src/core/memory_card.h b/src/core/memory_card.h index 80faf3085..56ee7771d 100644 --- a/src/core/memory_card.h +++ b/src/core/memory_card.h @@ -95,7 +95,6 @@ private: static TickCount GetSaveDelayInTicks(); - bool LoadFromFile(); bool SaveIfChanged(bool display_osd_message); void QueueFileSave(); diff --git a/src/core/memory_card_image.cpp b/src/core/memory_card_image.cpp index 8044235dc..2c44082e9 100644 --- a/src/core/memory_card_image.cpp +++ b/src/core/memory_card_image.cpp @@ -9,7 +9,6 @@ #include "util/state_wrapper.h" #include "common/bitutils.h" -#include "common/byte_stream.h" #include "common/error.h" #include "common/file_system.h" #include "common/log.h" @@ -97,17 +96,21 @@ static bool ImportSaveWithDirectoryFrame(DataArray* data, const char* filename, static bool ImportRawSave(DataArray* data, const char* filename, const FILESYSTEM_STAT_DATA& sd, Error* error); } // namespace MemoryCardImage -bool MemoryCardImage::LoadFromFile(DataArray* data, const char* filename) +bool MemoryCardImage::LoadFromFile(DataArray* data, const char* filename, Error* error) { - FILESYSTEM_STAT_DATA sd; - if (!FileSystem::StatFile(filename, &sd) || sd.Size != DATA_SIZE) + FileSystem::ManagedCFilePtr fp = FileSystem::OpenManagedCFile(filename, "rb", error); + if (!fp) return false; - std::unique_ptr stream = ByteStream::OpenFile(filename, BYTESTREAM_OPEN_READ | BYTESTREAM_OPEN_STREAMED); - if (!stream || stream->GetSize() != DATA_SIZE) + const s64 size = FileSystem::FSize64(fp.get()); + if (size != static_cast(DATA_SIZE)) + { + ERROR_LOG("Memory card {} is incorrect size (expected {} got {})", Path::GetFileName(filename), + static_cast(DATA_SIZE), size); return false; + } - const size_t num_read = stream->Read(data->data(), DATA_SIZE); + const size_t num_read = std::fread(data->data(), 1, DATA_SIZE, fp.get()); if (num_read != DATA_SIZE) { ERROR_LOG("Only read {} of {} sectors from '{}'", num_read / FRAME_SIZE, static_cast(NUM_FRAMES), filename); @@ -118,25 +121,17 @@ bool MemoryCardImage::LoadFromFile(DataArray* data, const char* filename) return true; } -bool MemoryCardImage::SaveToFile(const DataArray& data, const char* filename) +bool MemoryCardImage::SaveToFile(const DataArray& data, const char* filename, Error* error) { - std::unique_ptr stream = - ByteStream::OpenFile(filename, BYTESTREAM_OPEN_CREATE | BYTESTREAM_OPEN_TRUNCATE | BYTESTREAM_OPEN_WRITE | - BYTESTREAM_OPEN_ATOMIC_UPDATE | BYTESTREAM_OPEN_STREAMED); - if (!stream) + Error local_error; + if (!FileSystem::WriteAtomicRenamedFile(filename, data.data(), data.size(), error ? error : &local_error)) + [[unlikely]] { - ERROR_LOG("Failed to open '{}' for writing.", filename); + ERROR_LOG("Failed to save memory card '{}': {}", Path::GetFileName(filename), + (error ? error : &local_error)->GetDescription()); return false; } - if (!stream->Write2(data.data(), DATA_SIZE) || !stream->Commit()) - { - ERROR_LOG("Failed to write sectors to '{}'", filename); - stream->Discard(); - return false; - } - - VERBOSE_LOG("Saved memory card to '{}'", filename); return true; } @@ -634,27 +629,21 @@ bool MemoryCardImage::ImportCard(DataArray* data, const char* filename, Error* e bool MemoryCardImage::ExportSave(DataArray* data, const FileInfo& fi, const char* filename, Error* error) { - std::unique_ptr stream = - ByteStream::OpenFile(filename, - BYTESTREAM_OPEN_CREATE | BYTESTREAM_OPEN_TRUNCATE | BYTESTREAM_OPEN_WRITE | - BYTESTREAM_OPEN_ATOMIC_UPDATE | BYTESTREAM_OPEN_STREAMED, - error); - if (!stream) + // TODO: This could be span... + std::vector file_data; + if (!ReadFile(*data, fi, &file_data, error)) + return false; + + auto fp = FileSystem::CreateAtomicRenamedFile(filename, "wb", error); + if (!fp) return false; DirectoryFrame* df_ptr = GetFramePtr(data, 0, fi.first_block); - std::vector header = std::vector(static_cast(FRAME_SIZE)); - std::memcpy(header.data(), df_ptr, sizeof(*df_ptr)); - - std::vector blocks; - if (!ReadFile(*data, fi, &blocks, error)) - return false; - - if (!stream->Write(header.data(), static_cast(header.size())) || - !stream->Write(blocks.data(), static_cast(blocks.size())) || !stream->Commit()) + if (std::fwrite(df_ptr, sizeof(DirectoryFrame), 1, fp.get()) != 1 || + std::fwrite(file_data.data(), file_data.size(), 1, fp.get()) != 1) { - Error::SetStringView(error, "Failed to write exported save."); - stream->Discard(); + Error::SetErrno(error, "fwrite() failed: ", errno); + FileSystem::DiscardAtomicRenamedFile(fp); return false; } @@ -671,15 +660,14 @@ bool MemoryCardImage::ImportSaveWithDirectoryFrame(DataArray* data, const char* return false; } - std::unique_ptr stream = - ByteStream::OpenFile(filename, BYTESTREAM_OPEN_READ | BYTESTREAM_OPEN_STREAMED, error); - if (!stream) + auto fp = FileSystem::OpenManagedCFile(filename, "rb", error); + if (!fp) return false; DirectoryFrame df; - if (stream->Read(&df, FRAME_SIZE) != FRAME_SIZE) + if (std::fread(&df, sizeof(df), 1, fp.get()) != 1) { - Error::SetStringView(error, "Failed to read directory frame."); + Error::SetErrno(error, "Failed to read directory frame: ", errno); return false; } @@ -691,9 +679,9 @@ bool MemoryCardImage::ImportSaveWithDirectoryFrame(DataArray* data, const char* } std::vector blocks = std::vector(static_cast(df.file_size)); - if (stream->Read(blocks.data(), df.file_size) != df.file_size) + if (std::fread(blocks.data(), df.file_size, 1, fp.get()) != 1) { - Error::SetStringView(error, "Failed to read block bytes."); + Error::SetErrno(error, "Failed to read block bytes: ", errno); return false; } diff --git a/src/core/memory_card_image.h b/src/core/memory_card_image.h index e2ff4f679..0f254c1b0 100644 --- a/src/core/memory_card_image.h +++ b/src/core/memory_card_image.h @@ -25,8 +25,8 @@ enum : u32 using DataArray = std::array; -bool LoadFromFile(DataArray* data, const char* filename); -bool SaveToFile(const DataArray& data, const char* filename); +bool LoadFromFile(DataArray* data, const char* filename, Error* error); +bool SaveToFile(const DataArray& data, const char* filename, Error* error); void Format(DataArray* data); diff --git a/src/duckstation-qt/mainwindow.cpp b/src/duckstation-qt/mainwindow.cpp index 9ed44c590..9ebb3aa69 100644 --- a/src/duckstation-qt/mainwindow.cpp +++ b/src/duckstation-qt/mainwindow.cpp @@ -33,6 +33,7 @@ #include "util/gpu_device.h" #include "common/assert.h" +#include "common/error.h" #include "common/file_system.h" #include "common/log.h" @@ -2965,9 +2966,14 @@ void MainWindow::openMemoryCardEditor(const QString& card_a_path, const QString& tr("Memory card '%1' does not exist. Do you want to create an empty memory card?").arg(card_path), QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) { - if (!MemoryCardEditorWindow::createMemoryCard(card_path)) + Error error; + if (!MemoryCardEditorWindow::createMemoryCard(card_path, &error)) + { QMessageBox::critical(this, tr("Memory Card Not Found"), - tr("Failed to create memory card '%1'").arg(card_path)); + tr("Failed to create memory card '%1': %2") + .arg(card_path) + .arg(QString::fromStdString(error.GetDescription()))); + } } } } diff --git a/src/duckstation-qt/memorycardeditorwindow.cpp b/src/duckstation-qt/memorycardeditorwindow.cpp index 83b04971a..6908489ee 100644 --- a/src/duckstation-qt/memorycardeditorwindow.cpp +++ b/src/duckstation-qt/memorycardeditorwindow.cpp @@ -96,12 +96,12 @@ bool MemoryCardEditorWindow::setCardB(const QString& path) return true; } -bool MemoryCardEditorWindow::createMemoryCard(const QString& path) +bool MemoryCardEditorWindow::createMemoryCard(const QString& path, Error* error) { MemoryCardImage::DataArray data; MemoryCardImage::Format(&data); - return MemoryCardImage::SaveToFile(data, path.toUtf8().constData()); + return MemoryCardImage::SaveToFile(data, path.toUtf8().constData(), error); } void MemoryCardEditorWindow::resizeEvent(QResizeEvent* ev) @@ -232,10 +232,12 @@ bool MemoryCardEditorWindow::loadCard(const QString& filename, Card* card) return false; } + Error error; std::string filename_str = filename.toStdString(); - if (!MemoryCardImage::LoadFromFile(&card->data, filename_str.c_str())) + if (!MemoryCardImage::LoadFromFile(&card->data, filename_str.c_str(), &error)) { - QMessageBox::critical(this, tr("Error"), tr("Failed to load memory card image.")); + QMessageBox::critical(this, tr("Error"), + tr("Failed to load memory card: %1").arg(QString::fromStdString(error.GetDescription()))); return false; } @@ -343,9 +345,11 @@ void MemoryCardEditorWindow::openCard(Card* card) if (filename.isEmpty()) return; - if (!MemoryCardImage::LoadFromFile(&card->data, filename.toUtf8().constData())) + Error error; + if (!MemoryCardImage::LoadFromFile(&card->data, filename.toUtf8().constData(), &error)) { - QMessageBox::critical(this, tr("Error"), tr("Failed to load memory card image.")); + QMessageBox::critical(this, tr("Error"), + tr("Failed to load memory card: %1").arg(QString::fromStdString(error.GetDescription()))); return; } @@ -368,10 +372,11 @@ void MemoryCardEditorWindow::saveCard(Card* card) if (card->filename.empty()) return; - if (!MemoryCardImage::SaveToFile(card->data, card->filename.c_str())) + Error error; + if (!MemoryCardImage::SaveToFile(card->data, card->filename.c_str(), &error)) { QMessageBox::critical(this, tr("Error"), - tr("Failed to write card to '%1'").arg(QString::fromStdString(card->filename))); + tr("Failed to save memory card: %1").arg(QString::fromStdString(error.GetDescription()))); return; } diff --git a/src/duckstation-qt/memorycardeditorwindow.h b/src/duckstation-qt/memorycardeditorwindow.h index e204565f3..eeacf63e9 100644 --- a/src/duckstation-qt/memorycardeditorwindow.h +++ b/src/duckstation-qt/memorycardeditorwindow.h @@ -14,6 +14,8 @@ #include #include +class Error; + class MemoryCardEditorWindow : public QWidget { Q_OBJECT @@ -25,7 +27,7 @@ public: bool setCardA(const QString& path); bool setCardB(const QString& path); - static bool createMemoryCard(const QString& path); + static bool createMemoryCard(const QString& path, Error* error); protected: void resizeEvent(QResizeEvent* ev);