From 1b70ff775d2f3fc8a1db1e4ff26426464648abee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CE=B6eh=20Matt?= <3397065-ZehMatt@users.noreply.gitlab.com> Date: Sat, 16 Jul 2022 20:37:19 +0300 Subject: [PATCH] Move platform specific file code into platform and cleanup LowLevelFile --- components/CMakeLists.txt | 4 + components/files/lowlevelfile.cpp | 327 ++-------------------- components/files/lowlevelfile.hpp | 46 +-- components/platform/file.hpp | 35 +++ components/platform/file.posix.cpp | 105 +++++++ components/platform/file.stdio.cpp | 93 ++++++ components/platform/file.win32.cpp | 102 +++++++ components/platform/platform.internal.hpp | 19 ++ 8 files changed, 390 insertions(+), 341 deletions(-) create mode 100644 components/platform/file.hpp create mode 100644 components/platform/file.posix.cpp create mode 100644 components/platform/file.stdio.cpp create mode 100644 components/platform/file.win32.cpp create mode 100644 components/platform/platform.internal.hpp diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 9a2295671e..39c20ab127 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -337,6 +337,10 @@ add_component_dir(navmeshtool add_component_dir(platform platform + file.posix + file.win32 + file.stdio + file ) set (ESM_UI ${CMAKE_SOURCE_DIR}/files/ui/contentselector.ui diff --git a/components/files/lowlevelfile.cpp b/components/files/lowlevelfile.cpp index 9192630236..de17d67cc2 100644 --- a/components/files/lowlevelfile.cpp +++ b/components/files/lowlevelfile.cpp @@ -4,333 +4,50 @@ #include #include -#if FILE_API == FILE_API_STDIO -#include -#include -#endif +namespace File = Platform::File; -#if FILE_API == FILE_API_POSIX -#include -#include -#include -#include -#include -#endif - -#if FILE_API == FILE_API_STDIO -/* - * - * Implementation of LowLevelFile methods using c stdio - * - */ - -LowLevelFile::LowLevelFile () +LowLevelFile::~LowLevelFile() { - mHandle = nullptr; + if (mHandle != File::Handle::Invalid) + File::close(mHandle); } -LowLevelFile::~LowLevelFile () +void LowLevelFile::open(char const* filename) { - if (mHandle != nullptr) - fclose (mHandle); + mHandle = File::open(filename); } -void LowLevelFile::open (char const * filename) +void LowLevelFile::close() { - assert (mHandle == nullptr); - - mHandle = fopen (filename, "rb"); - - if (mHandle == nullptr) - { - std::ostringstream os; - os << "Failed to open '" << filename << "' for reading: " << strerror(errno); - throw std::runtime_error (os.str ()); - } + if (mHandle != File::Handle::Invalid) + File::close(mHandle); + mHandle = File::Handle::Invalid; } -void LowLevelFile::close () +size_t LowLevelFile::size() { - assert (mHandle != nullptr); + assert(mHandle != File::Handle::Invalid); - fclose (mHandle); - - mHandle = nullptr; + return File::size(mHandle); } -size_t LowLevelFile::size () +void LowLevelFile::seek(size_t position) { - assert (mHandle != nullptr); + assert(mHandle != File::Handle::Invalid); - long oldPosition = ftell (mHandle); - if (oldPosition == -1) - { - throw std::runtime_error ("An ftell() call failed: " + std::string(strerror(errno))); - } - - if (fseek (mHandle, 0, SEEK_END) != 0) - { - throw std::runtime_error ("An fseek() call failed: " + std::string(strerror(errno))); - } - - long size = ftell (mHandle); - if (size == -1) - { - throw std::runtime_error ("An ftell() call failed: " + std::string(strerror(errno))); - } - - if (fseek (mHandle, oldPosition, SEEK_SET) != 0) - { - throw std::runtime_error ("An fseek() call failed: " + std::string(strerror(errno))); - } - - return size_t (size); + return File::seek(mHandle, position); } -void LowLevelFile::seek (size_t position) +size_t LowLevelFile::tell() { - assert (mHandle != nullptr); + assert(mHandle != File::Handle::Invalid); - if (fseek (mHandle, position, SEEK_SET) != 0) - { - throw std::runtime_error ("An fseek() call failed: " + std::string(strerror(errno))); - } + return File::tell(mHandle); } -size_t LowLevelFile::tell () +size_t LowLevelFile::read(void* data, size_t size) { - assert (mHandle != nullptr); + assert(mHandle != File::Handle::Invalid); - long position = ftell (mHandle); - if (position == -1) - { - throw std::runtime_error ("An ftell() call failed: " + std::string(strerror(errno))); - } - - return size_t (position); + return File::read(mHandle, data, size); } - -size_t LowLevelFile::read (void * data, size_t size) -{ - assert (mHandle != nullptr); - - int amount = fread (data, 1, size, mHandle); - - if (amount == 0 && ferror (mHandle)) - { - std::ostringstream os; - os << "An attempt to read " << size << " bytes failed: " << strerror(errno); - throw std::runtime_error (os.str ()); - } - - return amount; -} - -#elif FILE_API == FILE_API_POSIX -/* - * - * Implementation of LowLevelFile methods using posix IO calls - * - */ - -LowLevelFile::LowLevelFile () -{ - mHandle = -1; -} - -LowLevelFile::~LowLevelFile () -{ - if (mHandle != -1) - ::close (mHandle); -} - -void LowLevelFile::open (char const * filename) -{ - assert (mHandle == -1); - -#ifdef O_BINARY - static const int openFlags = O_RDONLY | O_BINARY; -#else - static const int openFlags = O_RDONLY; -#endif - - mHandle = ::open (filename, openFlags, 0); - - if (mHandle == -1) - { - std::ostringstream os; - os << "Failed to open '" << filename << "' for reading: " << strerror(errno); - throw std::runtime_error (os.str ()); - } -} - -void LowLevelFile::close () -{ - assert (mHandle != -1); - - ::close (mHandle); - - mHandle = -1; -} - -size_t LowLevelFile::size () -{ - assert (mHandle != -1); - - size_t oldPosition = ::lseek (mHandle, 0, SEEK_CUR); - - if (oldPosition == size_t (-1)) - { - throw std::runtime_error ("An lseek() call failed: " + std::string(strerror(errno))); - } - - size_t size = ::lseek (mHandle, 0, SEEK_END); - - if (size == size_t (-1)) - { - throw std::runtime_error ("An lseek() call failed: " + std::string(strerror(errno))); - } - - if (lseek (mHandle, oldPosition, SEEK_SET) == -1) - { - throw std::runtime_error ("An lseek() call failed: " + std::string(strerror(errno))); - } - - return size; -} - -void LowLevelFile::seek (size_t position) -{ - assert (mHandle != -1); - - if (::lseek (mHandle, position, SEEK_SET) == -1) - { - throw std::runtime_error ("An lseek() call failed: " + std::string(strerror(errno))); - } -} - -size_t LowLevelFile::tell () -{ - assert (mHandle != -1); - - size_t position = ::lseek (mHandle, 0, SEEK_CUR); - - if (position == size_t (-1)) - { - throw std::runtime_error("An lseek() call failed: " + std::string(strerror(errno))); - } - - return position; -} - -size_t LowLevelFile::read (void * data, size_t size) -{ - assert (mHandle != -1); - - int amount = ::read (mHandle, data, size); - - if (amount == -1) - { - std::ostringstream os; - os << "An attempt to read " << size << " bytes failed: " << strerror(errno); - throw std::runtime_error (os.str ()); - } - - return amount; -} - -#elif FILE_API == FILE_API_WIN32 - -#include -/* - * - * Implementation of LowLevelFile methods using Win32 API calls - * - */ - -LowLevelFile::LowLevelFile () -{ - mHandle = INVALID_HANDLE_VALUE; -} - -LowLevelFile::~LowLevelFile () -{ - if (mHandle != INVALID_HANDLE_VALUE) - CloseHandle (mHandle); -} - -void LowLevelFile::open (char const * filename) -{ - assert (mHandle == INVALID_HANDLE_VALUE); - - std::wstring wname = boost::locale::conv::utf_to_utf(filename); - HANDLE handle = CreateFileW (wname.c_str(), GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); - - if (handle == INVALID_HANDLE_VALUE) - { - std::ostringstream os; - os << "Failed to open '" << filename << "' for reading: " << GetLastError(); - throw std::runtime_error (os.str ()); - } - - mHandle = handle; -} - -void LowLevelFile::close () -{ - assert (mHandle != INVALID_HANDLE_VALUE); - - CloseHandle (mHandle); - - mHandle = INVALID_HANDLE_VALUE; -} - -size_t LowLevelFile::size () -{ - assert (mHandle != INVALID_HANDLE_VALUE); - - BY_HANDLE_FILE_INFORMATION info; - - if (!GetFileInformationByHandle (mHandle, &info)) - throw std::runtime_error ("A query operation on a file failed."); - - if (info.nFileSizeHigh != 0) - throw std::runtime_error ("Files greater that 4GB are not supported."); - - return info.nFileSizeLow; -} - -void LowLevelFile::seek (size_t position) -{ - assert (mHandle != INVALID_HANDLE_VALUE); - - if (SetFilePointer (mHandle, static_cast(position), nullptr, SEEK_SET) == INVALID_SET_FILE_POINTER) - if (GetLastError () != NO_ERROR) - throw std::runtime_error ("A seek operation on a file failed."); -} - -size_t LowLevelFile::tell () -{ - assert (mHandle != INVALID_HANDLE_VALUE); - - DWORD value = SetFilePointer (mHandle, 0, nullptr, SEEK_CUR); - - if (value == INVALID_SET_FILE_POINTER && GetLastError () != NO_ERROR) - throw std::runtime_error ("A query operation on a file failed."); - - return value; -} - -size_t LowLevelFile::read (void * data, size_t size) -{ - assert (mHandle != INVALID_HANDLE_VALUE); - - DWORD read; - - if (!ReadFile (mHandle, data, static_cast(size), &read, nullptr)) - throw std::runtime_error ("A read operation on a file failed."); - - return read; -} - -#endif diff --git a/components/files/lowlevelfile.hpp b/components/files/lowlevelfile.hpp index 47c2e44f15..1bd182d0e4 100644 --- a/components/files/lowlevelfile.hpp +++ b/components/files/lowlevelfile.hpp @@ -2,53 +2,27 @@ #define COMPONENTS_FILES_LOWLEVELFILE_HPP #include +#include -#define FILE_API_STDIO 0 -#define FILE_API_POSIX 1 -#define FILE_API_WIN32 2 - -#if defined(__linux) || defined(__unix) || defined(__posix) -#define FILE_API FILE_API_POSIX -#elif defined(_WIN32) -#define FILE_API FILE_API_WIN32 -#else -#define FILE_API FILE_API_STDIO -#endif - -#if FILE_API == FILE_API_STDIO -#include -#elif FILE_API == FILE_API_POSIX -#elif FILE_API == FILE_API_WIN32 -#include -#else -#error Unsupported File API -#endif +#include class LowLevelFile { public: + ~LowLevelFile(); - LowLevelFile (); - ~LowLevelFile (); + void open(char const* filename); + void close(); - void open (char const * filename); - void close (); + size_t size(); - size_t size (); + void seek(size_t Position); + size_t tell(); - void seek (size_t Position); - size_t tell (); - - size_t read (void * data, size_t size); + size_t read(void* data, size_t size); private: -#if FILE_API == FILE_API_STDIO - FILE* mHandle; -#elif FILE_API == FILE_API_POSIX - int mHandle; -#elif FILE_API == FILE_API_WIN32 - HANDLE mHandle; -#endif + Platform::File::Handle mHandle{ Platform::File::Handle::Invalid }; }; #endif diff --git a/components/platform/file.hpp b/components/platform/file.hpp new file mode 100644 index 0000000000..d0d5eb0399 --- /dev/null +++ b/components/platform/file.hpp @@ -0,0 +1,35 @@ +#ifndef OPENMW_COMPONENTS_PLATFORM_FILE_HPP +#define OPENMW_COMPONENTS_PLATFORM_FILE_HPP + +#include +#include + +namespace Platform::File { + + enum class Handle : intptr_t + { + Invalid = -1 + }; + + enum class SeekType + { + Begin, + Current, + End + }; + + Handle open(const char* filename); + + void close(Handle handle); + + size_t size(Handle handle); + + void seek(Handle handle, size_t Position, SeekType type = SeekType::Begin); + size_t tell(Handle handle); + + size_t read(Handle handle, void* data, size_t size); + + +} + +#endif // OPENMW_COMPONENTS_PLATFORM_FILE_HPP diff --git a/components/platform/file.posix.cpp b/components/platform/file.posix.cpp new file mode 100644 index 0000000000..b6c322de11 --- /dev/null +++ b/components/platform/file.posix.cpp @@ -0,0 +1,105 @@ +#include "platform.internal.hpp" +#include "file.hpp" + +#if PLATFORM_TYPE == PLATFORM_TYPE_POSIX + +#include +#include +#include +#include +#include + +namespace Platform::File { + + static auto getNativeHandle(Handle handle) + { + return static_cast(handle); + } + + static int getNativeSeekType(SeekType seek) + { + if (seek == SeekType::Begin) + return SEEK_SET; + if (seek == SeekType::Current) + return SEEK_CUR; + if (seek == SeekType::End) + return SEEK_END; + return -1; + } + + Handle open(const char* filename) + { + // Posix +#ifdef O_BINARY + static const int openFlags = O_RDONLY | O_BINARY; +#else + static const int openFlags = O_RDONLY; +#endif + + auto handle = ::open(filename, openFlags, 0); + if (handle == -1) + { + std::ostringstream os; + os << "Failed to open '" << filename << "' for reading: " << strerror(errno); + throw std::runtime_error(os.str()); + } + return static_cast(handle); + } + + void close(Handle handle) + { + auto nativeHandle = getNativeHandle(handle); + + ::close(nativeHandle); + } + + void seek(Handle handle, size_t position, SeekType type /*= SeekType::Begin*/) + { + const auto nativeHandle = getNativeHandle(handle); + const auto nativeSeekType = getNativeSeekType(type); + + if (::lseek(toNativeHandle(mHandle), position, SEEK_SET) == -1) + { + throw std::runtime_error("An lseek() call failed: " + std::string(strerror(errno))); + } + } + + size_t size(Handle handle) + { + auto nativeHandle = getNativeHandle(handle); + + auto oldPos = tell(handle); + + seek(handle, 0, SeekType::End); + auto size = tell(handle); + + return static_cast(size); + } + + size_t tell(Handle handle) + { + auto nativeHandle = getNativeHandle(handle); + + size_t position = ::lseek(nativeHandle, 0, SEEK_CUR); + if (position == size_t(-1)) + { + throw std::runtime_error("An lseek() call failed: " + std::string(strerror(errno))); + } + return position; + } + + size_t read(Handle handle, void* data, size_t size) + { + auto nativeHandle = getNativeHandle(handle); + + int amount = ::read(toNativeHandle(mHandle), data, size); + if (amount == -1) + { + throw std::runtime_error("An attempt to read " + std::to_string(size) + " bytes failed: " + strerror(errno)); + } + return amount; + } + +} + +#endif diff --git a/components/platform/file.stdio.cpp b/components/platform/file.stdio.cpp new file mode 100644 index 0000000000..dd2660cd32 --- /dev/null +++ b/components/platform/file.stdio.cpp @@ -0,0 +1,93 @@ +#include "platform.internal.hpp" +#include "file.hpp" + +#if PLATFORM_TYPE == PLATFORM_TYPE_STDIO + +#include +#include + +namespace Platform::File { + + static auto getNativeHandle(Handle handle) + { + return reinterpret_cast(static_cast(handle)); + } + + static int getNativeSeekType(SeekType seek) + { + if (seek == SeekType::Begin) + return SEEK_SET; + if (seek == SeekType::Current) + return SEEK_CUR; + if (seek == SeekType::End) + return SEEK_END; + return -1; + } + + Handle open(const char* filename) + { + // Stdio + FILE* handle = fopen(filename, "rb"); + if (handle == nullptr) + { + std::ostringstream os; + os << "Failed to open '" << filename << "' for reading: " << strerror(errno); + throw std::runtime_error(os.str()); + } + return static_cast(reinterpret_cast(handle)); + } + + void close(Handle handle) + { + auto nativeHandle = getNativeHandle(handle); + fclose(nativeHandle); + } + + void seek(Handle handle, size_t position, SeekType type /*= SeekType::Begin*/) + { + const auto nativeHandle = getNativeHandle(handle); + const auto nativeSeekType = getNativeSeekType(type); + if (fseek(nativeHandle, position, nativeSeekType) != 0) + { + throw std::runtime_error("An fseek() call failed: " + std::string(strerror(errno))); + } + } + + size_t size(Handle handle) + { + auto nativeHandle = getNativeHandle(handle); + auto oldPos = tell(handle); + + seek(handle, 0, SeekType::End); + auto size = tell(handle); + + return static_cast(size); + } + + size_t tell(Handle handle) + { + auto nativeHandle = getNativeHandle(handle); + + long position = ftell(nativeHandle); + if (position == -1) + { + throw std::runtime_error("An ftell() call failed: " + std::string(strerror(errno))); + } + return static_cast(position); + } + + size_t read(Handle handle, void* data, size_t size) + { + auto nativeHandle = getNativeHandle(handle); + + int amount = fread(data, 1, size, nativeHandle); + if (amount == 0 && ferror(nativeHandle)) + { + throw std::runtime_error("An attempt to read " + std::to_string(size) + " bytes failed: " + strerror(errno)); + } + return static_cast(amount); + } + +} + +#endif diff --git a/components/platform/file.win32.cpp b/components/platform/file.win32.cpp new file mode 100644 index 0000000000..59601caa80 --- /dev/null +++ b/components/platform/file.win32.cpp @@ -0,0 +1,102 @@ +#include "platform.internal.hpp" +#include "file.hpp" + +#if PLATFORM_TYPE == PLATFORM_TYPE_WIN32 + +#include +#include +#include + +namespace Platform::File { + + static auto getNativeHandle(Handle handle) + { + return reinterpret_cast(static_cast(handle)); + } + + static int getNativeSeekType(SeekType seek) + { + if (seek == SeekType::Begin) + return FILE_BEGIN; + if (seek == SeekType::Current) + return FILE_CURRENT; + if (seek == SeekType::End) + return FILE_END; + return -1; + } + + Handle open(const char* filename) + { + std::wstring wname = boost::locale::conv::utf_to_utf(filename); + HANDLE handle = CreateFileW(wname.c_str(), GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); + if (handle == INVALID_HANDLE_VALUE) + { + std::ostringstream os; + os << "Failed to open '" << filename << "' for reading: " << GetLastError(); + throw std::runtime_error(os.str()); + } + return static_cast(reinterpret_cast(handle)); + } + + void close(Handle handle) + { + auto nativeHandle = getNativeHandle(handle); + CloseHandle(nativeHandle); + } + + void seek(Handle handle, size_t position, SeekType type /*= SeekType::Begin*/) + { + const auto nativeHandle = getNativeHandle(handle); + const auto nativeSeekType = getNativeSeekType(type); + + if (SetFilePointer(nativeHandle, static_cast(position), nullptr, nativeSeekType) == INVALID_SET_FILE_POINTER) + { + if (auto errCode = GetLastError(); errCode != ERROR_SUCCESS) + { + throw std::runtime_error("An fseek() call failed: " + std::to_string(errCode)); + } + } + } + + size_t size(Handle handle) + { + auto nativeHandle = getNativeHandle(handle); + + assert(isValidHandle(mHandle)); + + BY_HANDLE_FILE_INFORMATION info; + + if (!GetFileInformationByHandle(nativeHandle, &info)) + throw std::runtime_error("A query operation on a file failed."); + + if (info.nFileSizeHigh != 0) + throw std::runtime_error("Files greater that 4GB are not supported."); + + return info.nFileSizeLow; + } + + size_t tell(Handle handle) + { + auto nativeHandle = getNativeHandle(handle); + + DWORD value = SetFilePointer(nativeHandle, 0, nullptr, SEEK_CUR); + if (value == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) + throw std::runtime_error("A query operation on a file failed."); + + return value; + } + + size_t read(Handle handle, void* data, size_t size) + { + auto nativeHandle = getNativeHandle(handle); + + DWORD bytesRead{}; + + if (!ReadFile(nativeHandle, data, static_cast(size), &bytesRead, nullptr)) + throw std::runtime_error("A read operation on a file failed: " + std::to_string(GetLastError())); + + return bytesRead; + } +} + +#endif diff --git a/components/platform/platform.internal.hpp b/components/platform/platform.internal.hpp new file mode 100644 index 0000000000..255fe7cb7a --- /dev/null +++ b/components/platform/platform.internal.hpp @@ -0,0 +1,19 @@ +#ifndef COMPONENT_PLATFORM_FILE_HPP +#define COMPONENT_PLATFORM_FILE_HPP + +namespace Platform +{ +#define PLATFORM_TYPE_STDIO 0 +#define PLATFORM_TYPE_WIN32 1 +#define PLATFORM_TYPE_POSIX 2 + +#if defined(__linux) || defined(__unix) || defined(__posix) +#define PLATFORM_TYPE PLATFORM_TYPE_POSIX +#elif defined(_WIN32) +#define PLATFORM_TYPE PLATFORM_TYPE_WIN32 +#else +#define PLATFORM_TYPE PLATFORM_TYPE_STDIO +#endif +} + +#endif