diff --git a/FEXCore/include/FEXCore/Utils/File.h b/FEXCore/include/FEXCore/Utils/File.h index a288c166e..140e40a1c 100644 --- a/FEXCore/include/FEXCore/Utils/File.h +++ b/FEXCore/include/FEXCore/Utils/File.h @@ -3,6 +3,7 @@ #include #include #include +#include "FEXCore/Utils/LogManager.h" #include #include @@ -11,6 +12,7 @@ #include #include #include +#include #else #define WIN32_LEAN_AND_MEAN #include @@ -43,7 +45,8 @@ public: File() = default; - File(const char* Filepath, FileModes Modes) { + File(const char* Filepath, FileModes Modes, bool Seekable = true) + : Seekable {Seekable} { #ifndef _WIN32 auto Disp = TranslateModes(Modes); Handle = open(Filepath, Disp, DEFAULT_USER_PERMS); @@ -75,6 +78,10 @@ public: * @return The number of bytes actually written or -1 on error. */ ssize_t Write(const void* Buffer, size_t Bytes) { + if (!Seekable) { + LOGMAN_THROW_A_FMT(false, "Can't use non-positioned ops on a non-seekable file!"); + return -1; + } #ifndef _WIN32 return write(Handle, Buffer, Bytes); #else @@ -101,6 +108,10 @@ public: * @return The number of bytes read or -1 on error. */ ssize_t Read(void* Buffer, size_t Bytes) { + if (!Seekable) { + LOGMAN_THROW_A_FMT(false, "Can't use non-positioned ops on a non-seekable file!"); + return -1; + } #ifndef _WIN32 return read(Handle, Buffer, Bytes); #else @@ -114,6 +125,48 @@ public: #endif } + ssize_t PRead(void* Buffer, size_t Bytes, uint64_t Offset) { + if (Seekable) { + LOGMAN_THROW_A_FMT(false, "Can't use positioned ops on a seekable file!"); + return -1; + } +#ifndef _WIN32 + return pread(Handle, Buffer, Bytes, Offset); +#else + DWORD BytesRead {}; + OVERLAPPED Overlapped {}; + Overlapped.Offset = static_cast(Offset); + Overlapped.OffsetHigh = static_cast(Offset >> 32); + auto Result = ReadFile(Handle, Buffer, Bytes, &BytesRead, &Overlapped); + if (Result) { + return BytesRead; + } + // Some error, match Linux side. + return -1; +#endif + } + + ssize_t PWrite(const void* Buffer, size_t Bytes, uint64_t Offset) { + if (Seekable) { + LOGMAN_THROW_A_FMT(false, "Can't use positioned ops on a seekable file!"); + return -1; + } +#ifndef _WIN32 + return pwrite(Handle, Buffer, Bytes, Offset); +#else + DWORD BytesWritten {}; + OVERLAPPED Overlapped {}; + Overlapped.Offset = static_cast(Offset); + Overlapped.OffsetHigh = static_cast(Offset >> 32); + auto Result = WriteFile(Handle, Buffer, Bytes, &BytesWritten, &Overlapped); + if (Result) { + return BytesWritten; + } + // Some error, match Linux side. + return -1; +#endif + } + bool Lock(uint32_t TimeoutMS) { for (uint32_t i = 0;; ++i) { if (TryLock()) { @@ -202,6 +255,22 @@ public: #endif } + ssize_t Size() { +#ifndef _WIN32 + struct stat st; + if (fstat(Handle, &st) != 0) { + return -1; + } + return st.st_size; +#else + LARGE_INTEGER FileSize; + if (!GetFileSizeEx(Handle, &FileSize)) { + return -1; + } + return FileSize.QuadPart; +#endif + } + /** * @brief Seek the file pointer location. * @@ -211,6 +280,10 @@ public: * @return The current file pointer location or -1. */ ssize_t Seek(ssize_t Distance, SeekOp Op) { + if (!Seekable) { + LOGMAN_THROW_A_FMT(false, "Can't use non-positioned ops on a non-seekable file!"); + return -1; + } #ifndef _WIN32 return lseek(Handle, Distance, TranslateSeek(Op)); #else @@ -227,10 +300,11 @@ public: protected: - File(FileHandleType Handle, bool ShouldClose) + File(FileHandleType Handle, bool ShouldClose, bool Seekable = true) : ShouldClose {ShouldClose} , IsValidHandle {true} - , Handle {Handle} {} + , Handle {Handle} + , Seekable {Seekable} {} private: bool TryLock() { if (Locked) { @@ -257,6 +331,7 @@ private: bool IsValidHandle {}; FileHandleType Handle {}; + bool Seekable = true; bool Locked = false; #ifndef _WIN32 static constexpr int DEFAULT_USER_PERMS = S_IRWXU | S_IRWXG | S_IRWXO; diff --git a/Source/Windows/Common/WinAPI/IO.cpp b/Source/Windows/Common/WinAPI/IO.cpp index b9e3359bf..ac960554a 100644 --- a/Source/Windows/Common/WinAPI/IO.cpp +++ b/Source/Windows/Common/WinAPI/IO.cpp @@ -111,10 +111,16 @@ DLLEXPORT_FUNC(HANDLE, CreateFileW, DLLEXPORT_FUNC(WINBOOL, WriteFile, (HANDLE hFile, const void* lpBuffer, DWORD nNumberOfBytesToWrite, LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped)) { IO_STATUS_BLOCK IOSB; + LARGE_INTEGER ByteOffset; + PLARGE_INTEGER ByteOffsetPtr = nullptr; if (lpOverlapped) { - UNIMPLEMENTED(); + if (lpOverlapped->hEvent) { + UNIMPLEMENTED(); + } + ByteOffset.QuadPart = (static_cast(lpOverlapped->OffsetHigh) << 32) | lpOverlapped->Offset; + ByteOffsetPtr = &ByteOffset; } - NTSTATUS Status = NtWriteFile(hFile, nullptr, nullptr, nullptr, &IOSB, lpBuffer, nNumberOfBytesToWrite, nullptr, nullptr); + NTSTATUS Status = NtWriteFile(hFile, nullptr, nullptr, nullptr, &IOSB, lpBuffer, nNumberOfBytesToWrite, ByteOffsetPtr, nullptr); if (lpNumberOfBytesWritten) { *lpNumberOfBytesWritten = static_cast(IOSB.Information); } @@ -135,6 +141,18 @@ DLLEXPORT_FUNC(WINBOOL, WriteConsoleW, UNIMPLEMENTED(); } +DLLEXPORT_FUNC(WINBOOL, GetFileSizeEx, (HANDLE hFile, PLARGE_INTEGER lpFileSize)) { + IO_STATUS_BLOCK IOSB; + FILE_STANDARD_INFORMATION StandardInfo; + if (NTSTATUS Status = NtQueryInformationFile(hFile, &IOSB, &StandardInfo, sizeof(StandardInfo), FileStandardInformation); Status) { + return WinAPIReturn(Status); + } + if (lpFileSize) { + *lpFileSize = StandardInfo.EndOfFile; + } + return true; +} + DLLEXPORT_FUNC(WINBOOL, SetFilePointerEx, (HANDLE hFile, LARGE_INTEGER liDistanceToMove, PLARGE_INTEGER lpNewFilePointer, DWORD dwMoveMethod)) { IO_STATUS_BLOCK IOSB; FILE_POSITION_INFORMATION PositionInfo; @@ -173,10 +191,17 @@ DLLEXPORT_FUNC(WINBOOL, SetFilePointerEx, (HANDLE hFile, LARGE_INTEGER liDistanc DLLEXPORT_FUNC(WINBOOL, ReadFile, (HANDLE hFile, void* lpBuffer, DWORD nNumberOfBytesToRead, LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped)) { IO_STATUS_BLOCK IOSB; + LARGE_INTEGER ByteOffset; + PLARGE_INTEGER ByteOffsetPtr = nullptr; if (lpOverlapped) { - UNIMPLEMENTED(); + if (lpOverlapped->hEvent) { + // no actual async-overlapped, just enough for PRead + UNIMPLEMENTED(); + } + ByteOffset.QuadPart = (static_cast(lpOverlapped->OffsetHigh) << 32) | lpOverlapped->Offset; + ByteOffsetPtr = &ByteOffset; } - NTSTATUS Status = NtReadFile(hFile, nullptr, nullptr, nullptr, &IOSB, lpBuffer, nNumberOfBytesToRead, nullptr, nullptr); + NTSTATUS Status = NtReadFile(hFile, nullptr, nullptr, nullptr, &IOSB, lpBuffer, nNumberOfBytesToRead, ByteOffsetPtr, nullptr); if (lpNumberOfBytesRead) { *lpNumberOfBytesRead = static_cast(IOSB.Information); }