#include "common/file.h" #include "common/archive.h" #include "common/assert.h" #include "common/common.h" #include "common/platform/sysFileIO.h" #include "common/platform/sysTimer.h" #include "common/stringUtils.h" #include #include #include #include // IWYU pragma: no_include // IWYU pragma: no_include // IWYU pragma: no_include #if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS #ifdef CreateDirectory #undef CreateDirectory #endif #ifdef DeleteFile #undef DeleteFile #endif #ifdef CopyFile #undef CopyFile #endif #endif namespace Common { namespace { SysFileTimeStruct DateTimeToFileTimeUtc(const DateTime& date_time) { SysTimeStruct time {}; time.is_invalid = false; int year = 0; int month = 0; int day = 0; date_time.GetDate().Get(&year, &month, &day); time.Year = year; time.Month = month; time.Day = day; int hour = 0; int minute = 0; int second = 0; int milliseconds = 0; date_time.GetTime().Get(&hour, &minute, &second, &milliseconds); time.Hour = hour; time.Minute = minute; time.Second = second; time.Milliseconds = milliseconds; SysFileTimeStruct file_time {}; SysSystemToFileTimeUtc(time, file_time); return file_time; } } // namespace struct File::FilePrivate { sys_file_t* f; std::unique_ptr archive; }; File::File(): m_p(std::make_unique()) { m_p->f = nullptr; } File::~File() { Close(); } File::File(const std::filesystem::path& name): m_p(std::make_unique()) { m_p->f = nullptr; Create(name); } File::File(const std::filesystem::path& name, Mode mode): m_p(std::make_unique()) { m_p->f = nullptr; Open(name, mode); } bool File::IsInvalid() const { return m_p->f == nullptr && m_p->archive == nullptr; } bool File::Create(const std::filesystem::path& name) { EXIT_IF(!IsInvalid()); if (IsArchivePath(name)) { return false; } m_file_name = name; m_p->f = SysFileCreate(name); if (SysFileIsError(*m_p->f)) { Close(); return false; } return true; } bool File::Open(const std::filesystem::path& name, Mode mode) { EXIT_IF(!IsInvalid()); m_file_name = name; if (IsArchivePath(name)) { if (mode != Mode::Read) { return false; } m_p->archive = OpenArchiveFile(name); return m_p->archive != nullptr; } switch (mode) { case Mode::Read: m_p->f = SysFileOpenR(name); break; case Mode::Write: m_p->f = SysFileOpenW(name); break; case Mode::ReadWrite: case Mode::WriteRead: m_p->f = SysFileOpenRw(name); break; } if (SysFileIsError(*m_p->f)) { Close(); return false; } return true; } bool File::OpenInMem(void* buf, uint32_t buf_size) { EXIT_IF(!IsInvalid()); m_p->f = SysFileOpen(static_cast(buf), buf_size); if (SysFileIsError(*m_p->f)) { Close(); return false; } return true; } bool File::CreateInMem() { EXIT_IF(!IsInvalid()); m_p->f = SysFileCreate(); if (SysFileIsError(*m_p->f)) { Close(); return false; } return true; } void File::Close() { m_p->archive.reset(); if (m_p->f != nullptr) { SysFileClose(m_p->f); m_p->f = nullptr; } } uint64_t File::Size() const { EXIT_IF(IsInvalid()); if (m_p->archive != nullptr) { return m_p->archive->Size(); } if (m_p->f == nullptr) { return 0; } return SysFileSize(*m_p->f); } uint64_t File::Remaining() const { EXIT_IF(IsInvalid()); const auto size = Size(); const auto position = Tell(); return position < size ? size - position : 0; } uint64_t File::Size(const std::filesystem::path& name) { if (IsArchivePath(name)) { const auto info = GetArchiveInfo(name); return info ? info->size : 0; } return SysFileSize(name); } std::optional File::GetInfo(const std::filesystem::path& name) { if (IsArchivePath(name)) { return GetArchiveInfo(name); } Info info {}; return SysFileGetInfo(name, &info.is_file, &info.size) ? std::optional(info) : std::nullopt; } bool File::Seek(uint64_t offset) { EXIT_IF(IsInvalid()); if (m_p->archive != nullptr) { return m_p->archive->Seek(offset); } return SysFileSeek(*m_p->f, offset); } bool File::Truncate(uint64_t size) { EXIT_IF(IsInvalid()); if (m_p->archive != nullptr) { return false; } return SysFileTruncate(*m_p->f, size); } bool File::Unlink() { EXIT_IF(IsInvalid()); if (m_p->archive != nullptr) { return false; } return SysFileUnlink(*m_p->f, m_file_name); } uint64_t File::Tell() const { EXIT_IF(IsInvalid()); if (m_p->archive != nullptr) { return m_p->archive->Tell(); } return SysFileTell(*m_p->f); } void File::Read(void* data, uint32_t size, uint32_t* bytes_read) { EXIT_IF(IsInvalid()); if (m_p->archive != nullptr) { m_p->archive->Read(data, size, bytes_read); return; } if (m_p->f != nullptr) { SysFileRead(data, size, *m_p->f, bytes_read); } } void File::Write(const void* data, uint32_t size, uint32_t* bytes_written) { EXIT_IF(IsInvalid()); if (m_p->archive != nullptr) { if (bytes_written != nullptr) { *bytes_written = 0; } return; } SysFileWrite(data, size, *m_p->f, bytes_written); } static std::string FileVformat(const char* format, va_list args) { va_list copy {}; va_copy(copy, args); const int size = std::vsnprintf(nullptr, 0, format, copy); va_end(copy); if (size <= 0) { return {}; } std::vector buffer(static_cast(size) + 1u); va_list copy2 {}; va_copy(copy2, args); std::vsnprintf(buffer.data(), buffer.size(), format, copy2); va_end(copy2); return {buffer.data(), static_cast(size)}; } void File::Printf(const char* format, ...) { va_list args {}; va_start(args, format); auto s = FileVformat(format, args); va_end(args); s = Common::ReplaceStr(s, "\n", "\r\n"); Write(s.data(), static_cast(s.size())); } static std::filesystem::path WithoutTrailingSeparator( const std::filesystem::path& path) { if (!path.empty() && !path.has_filename() && path != path.root_path()) { return path.parent_path(); } return path; } bool File::IsDirectoryExisting(const std::filesystem::path& path) { if (IsArchivePath(path)) { const auto info = GetArchiveInfo(path); return info && !info->is_file; } return SysFileIsDirectoryExisting(WithoutTrailingSeparator(path)); } bool File::IsFileExisting(const std::filesystem::path& name) { if (IsArchivePath(name)) { const auto info = GetArchiveInfo(name); return info && info->is_file; } return SysFileIsFileExisting(name); } bool File::CreateDirectory(const std::filesystem::path& path) { if (IsArchivePath(path)) { return false; } return SysFileCreateDirectory( WithoutTrailingSeparator(path)); } bool File::DeleteDirectory(const std::filesystem::path& path) { if (IsArchivePath(path)) { return false; } return SysFileDeleteDirectory( WithoutTrailingSeparator(path)); } bool File::CreateDirectories(const std::filesystem::path& path) { if (IsArchivePath(path)) { return false; } const auto normalized_path = WithoutTrailingSeparator(path); auto current_path = normalized_path.root_path(); for (const auto& component: normalized_path.relative_path()) { current_path /= component; if (IsDirectoryExisting(current_path)) { continue; } if (!CreateDirectory(current_path)) { return false; } } return true; } bool File::DeleteDirectories(const std::filesystem::path& path) { if (IsArchivePath(path)) { return false; } const auto normalized_path = WithoutTrailingSeparator(path); std::vector directories; auto current_path = normalized_path.root_path(); for (const auto& component: normalized_path.relative_path()) { current_path /= component; directories.push_back(current_path); } for (auto it = directories.rbegin(); it != directories.rend(); ++it) { if (!DeleteDirectory(*it)) { return false; } } return true; } bool File::DeleteFile( const std::filesystem::path& name) // @suppress("Member declaration not found") { if (IsArchivePath(name)) { return false; } return SysFileDeleteFile(name); } bool File::Flush() { EXIT_IF(IsInvalid()); if (m_p->archive != nullptr) { return true; } return SysFileFlush(*m_p->f); } std::vector File::ReadWholeBuffer() { EXIT_IF(IsInvalid()); EXIT_IF(Tell() != 0); uint64_t s = Size(); EXIT_IF((s >> 32u) != 0); const auto read_size = static_cast(s); std::vector buf(read_size); Read(buf.data(), read_size); return buf; } DateTime File::GetLastAccessTimeUTC(const std::filesystem::path& name) { if (IsArchivePath(name)) { return GetLastAccessTimeUTC(GetArchiveHostPath(name)); } SysTimeStruct t {}; SysFileToSystemTimeUtc(SysFileGetLastAccessTimeUtc(name), t); if (t.is_invalid) { return {}; } return DateTime(Date(t.Year, t.Month, t.Day), Time(t.Hour, t.Minute, t.Second, t.Milliseconds)); } DateTime File::GetLastWriteTimeUTC(const std::filesystem::path& name) { if (IsArchivePath(name)) { return GetLastWriteTimeUTC(GetArchiveHostPath(name)); } SysTimeStruct t {}; SysFileToSystemTimeUtc(SysFileGetLastWriteTimeUtc(name), t); if (t.is_invalid) { return {}; } return DateTime(Date(t.Year, t.Month, t.Day), Time(t.Hour, t.Minute, t.Second, t.Milliseconds)); } void File::GetLastAccessAndWriteTimeUTC(const std::filesystem::path& name, DateTime* access, DateTime* write) { EXIT_IF(access == nullptr); EXIT_IF(write == nullptr); if (IsArchivePath(name)) { GetLastAccessAndWriteTimeUTC(GetArchiveHostPath(name), access, write); return; } SysTimeStruct at {}; SysTimeStruct wt {}; SysFileTimeStruct af {}; SysFileTimeStruct wf {}; SysFileGetLastAccessAndWriteTimeUtc(name, af, wf); SysFileToSystemTimeUtc(af, at); SysFileToSystemTimeUtc(wf, wt); if (at.is_invalid || wt.is_invalid) { *access = DateTime(); *write = DateTime(); } else { *access = DateTime(Date(at.Year, at.Month, at.Day), Time(at.Hour, at.Minute, at.Second, at.Milliseconds)); *write = DateTime(Date(wt.Year, wt.Month, wt.Day), Time(wt.Hour, wt.Minute, wt.Second, wt.Milliseconds)); } } void File::GetLastAccessAndWriteTimeUTC(DateTime* access, DateTime* write) { EXIT_IF(access == nullptr); EXIT_IF(write == nullptr); EXIT_IF(IsInvalid()); if (m_p->archive != nullptr) { GetLastAccessAndWriteTimeUTC(m_file_name, access, write); return; } if (m_p->f == nullptr) { *access = DateTime(); *write = DateTime(); return; } SysTimeStruct at {}; SysTimeStruct wt {}; SysFileTimeStruct af {}; SysFileTimeStruct wf {}; SysFileGetLastAccessAndWriteTimeUtc(*m_p->f, af, wf); SysFileToSystemTimeUtc(af, at); SysFileToSystemTimeUtc(wf, wt); if (at.is_invalid || wt.is_invalid) { *access = DateTime(); *write = DateTime(); } else { *access = DateTime(Date(at.Year, at.Month, at.Day), Time(at.Hour, at.Minute, at.Second, at.Milliseconds)); *write = DateTime(Date(wt.Year, wt.Month, wt.Day), Time(wt.Hour, wt.Minute, wt.Second, wt.Milliseconds)); } } bool File::SetLastAccessTimeUTC(const std::filesystem::path& name, const DateTime& dt) { if (IsArchivePath(name)) { return false; } auto f = DateTimeToFileTimeUtc(dt); return SysFileSetLastAccessTimeUtc(name, f); } bool File::SetLastWriteTimeUTC(const std::filesystem::path& name, const DateTime& dt) { if (IsArchivePath(name)) { return false; } auto f = DateTimeToFileTimeUtc(dt); return SysFileSetLastWriteTimeUtc(name, f); } bool File::SetLastAccessAndWriteTimeUTC(const std::filesystem::path& name, const DateTime& access, const DateTime& write) { if (IsArchivePath(name)) { return false; } auto af = DateTimeToFileTimeUtc(access); auto wf = DateTimeToFileTimeUtc(write); return SysFileSetLastAccessAndWriteTimeUtc(name, af, wf); } std::vector File::GetDirEntries(const std::filesystem::path& path) { if (IsArchivePath(path)) { return GetArchiveDirEntries(path); } std::vector files; SysFileGetDents(path, files); std::vector ret; ret.reserve(files.size()); for (auto& f: files) { ret.emplace_back(std::move(f.name), f.is_file); } return ret; } bool File::CopyFile(const std::filesystem::path& src, const std::filesystem::path& dst) // @suppress("Member declaration not found") { if (IsArchivePath(src) || IsArchivePath(dst)) { return false; } return SysFileCopyFile(src, dst); } bool File::RenameFile(const std::filesystem::path& src, const std::filesystem::path& dst) // @suppress("Member declaration not found") { if (IsArchivePath(src) || IsArchivePath(dst)) { return false; } if (IsFileExisting(dst)) { DeleteFile(dst); // @suppress("Invalid arguments") } return SysFileRenameFile(src, dst); } void File::RemoveReadonly(const std::filesystem::path& name) { if (IsArchivePath(name)) { return; } SysFileRemoveReadonly(name); } } // namespace Common