diff --git a/src/util/iso_reader.cpp b/src/util/iso_reader.cpp index e3f676680..ef9d8870b 100644 --- a/src/util/iso_reader.cpp +++ b/src/util/iso_reader.cpp @@ -5,7 +5,9 @@ #include "cd_image.h" #include "common/error.h" +#include "common/file_system.h" #include "common/log.h" +#include "common/progress_callback.h" #include "common/string_util.h" #include "fmt/format.h" @@ -385,3 +387,97 @@ bool IsoReader::ReadFile(const ISODirectoryEntry& de, std::vector* data, Err data->resize(de.length_le); return true; } + +bool IsoReader::WriteFileToStream(std::string_view path, std::FILE* fp, Error* error /* = nullptr */, + ProgressCallback* progress /* = nullptr */) +{ + auto de = LocateFile(path, error); + if (!de) + return false; + + return WriteFileToStream(de.value(), fp, error, progress); +} + +bool IsoReader::WriteFileToStream(const ISODirectoryEntry& de, std::FILE* fp, Error* error /* = nullptr */, + ProgressCallback* progress /* = nullptr */) +{ + if (de.flags & ISODirectoryEntryFlag_Directory) + { + Error::SetString(error, "File is a directory"); + return false; + } + + if (!FileSystem::FSeek64(fp, 0, SEEK_SET, error)) + return false; + + if (de.length_le == 0) + return FileSystem::FTruncate64(fp, 0, error); + + const u32 num_sectors = (de.length_le + (SECTOR_SIZE - 1)) / SECTOR_SIZE; + u32 file_pos = 0; + u8 sector_buffer[SECTOR_SIZE]; + + if (progress) + { + progress->SetProgressRange(de.length_le); + progress->SetProgressValue(0); + } + + for (u32 i = 0, lsn = de.location_le; i < num_sectors; i++, lsn++) + { + if (!ReadSector(sector_buffer, lsn, error)) + return false; + + const u32 write_size = std::min(de.length_le - file_pos, SECTOR_SIZE); + if (std::fwrite(sector_buffer, write_size, 1, fp) != 1) + { + Error::SetErrno(error, "fwrite() failed: ", errno); + return false; + } + + file_pos += write_size; + if (progress) + { + progress->SetProgressValue(file_pos); + if (progress->IsCancelled()) + { + Error::SetStringView(error, "Operation was cancelled."); + return false; + } + } + } + + if (std::fflush(fp) != 0) + { + Error::SetErrno(error, "fflush() failed: ", errno); + return false; + } + + return true; +} + +std::string IsoReader::ISODirectoryEntryDateTime::GetFormattedTime() const +{ + // need to apply the UTC offset, so first convert to unix time + struct tm utime; + utime.tm_year = years_since_1900; + utime.tm_mon = (month > 0) ? (month - 1) : 0; + utime.tm_mday = (day > 0) ? (day - 1) : 0; + utime.tm_hour = hour; + utime.tm_min = minute; + utime.tm_sec = second; + + const s32 uts_offset = static_cast(gmt_offset) * 3600; + const time_t uts = std::mktime(&utime) + uts_offset; + + struct tm ltime; +#ifdef _MSC_VER + localtime_s(<ime, &uts); +#else + localtime_r(&uts, <ime); +#endif + + char buf[128]; + const size_t len = std::strftime(buf, std::size(buf), "%c", <ime); + return std::string(buf, len); +} diff --git a/src/util/iso_reader.h b/src/util/iso_reader.h index 0eb8049f6..54fab6d15 100644 --- a/src/util/iso_reader.h +++ b/src/util/iso_reader.h @@ -5,6 +5,7 @@ #include "common/types.h" +#include #include #include #include @@ -13,6 +14,7 @@ class CDImage; class Error; +class ProgressCallback; class IsoReader { @@ -104,6 +106,8 @@ public: u8 minute; u8 second; s8 gmt_offset; + + std::string GetFormattedTime() const; }; enum ISODirectoryEntryFlags : u8 @@ -161,6 +165,11 @@ public: bool ReadFile(std::string_view path, std::vector* data, Error* error = nullptr); bool ReadFile(const ISODirectoryEntry& de, std::vector* data, Error* error = nullptr); + bool WriteFileToStream(std::string_view path, std::FILE* fp, Error* error = nullptr, + ProgressCallback* progress = nullptr); + bool WriteFileToStream(const ISODirectoryEntry& de, std::FILE* fp, Error* error = nullptr, + ProgressCallback* progress = nullptr); + private: static std::string_view GetDirectoryEntryFileName(const u8* sector, u32 de_sector_offset);