diff --git a/src/xenia/base/filesystem.h b/src/xenia/base/filesystem.h index 0c7246196..ff9403a9c 100644 --- a/src/xenia/base/filesystem.h +++ b/src/xenia/base/filesystem.h @@ -11,14 +11,12 @@ #define XENIA_BASE_FILESYSTEM_H_ #include +#include #include #include #include "xenia/base/string.h" -// TOODO(benvanik): remove windows headers. -#undef DeleteFile - namespace xe { namespace filesystem { @@ -33,6 +31,50 @@ bool IsFolder(const std::wstring& path); FILE* OpenFile(const std::wstring& path, const char* mode); bool DeleteFile(const std::wstring& path); +struct FileAccess { + // Implies kFileReadData. + static const uint32_t kGenericRead = 0x80000000; + // Implies kFileWriteData. + static const uint32_t kGenericWrite = 0x40000000; + static const uint32_t kGenericExecute = 0x20000000; + static const uint32_t kGenericAll = 0x10000000; + static const uint32_t kFileReadData = 0x00000001; + static const uint32_t kFileWriteData = 0x00000002; + static const uint32_t kFileAppendData = 0x00000004; +}; + +class FileHandle { + public: + // Opens the file, failing if it doesn't exist. + // The desired_access bitmask denotes the permissions on the file. + static std::unique_ptr OpenExisting(std::wstring path, + uint32_t desired_access); + + virtual ~FileHandle() = default; + + std::wstring path() const { return path_; } + + // Reads the requested number of bytes from the file starting at the given + // offset. The total number of bytes read is returned only if the complete + // read succeeds. + virtual bool Read(size_t file_offset, void* buffer, size_t buffer_length, + size_t* out_bytes_read) = 0; + + // Writes the given buffer to the file starting at the given offset. + // The total number of bytes written is returned only if the complete + // write succeeds. + virtual bool Write(size_t file_offset, const void* buffer, + size_t buffer_length, size_t* out_bytes_written) = 0; + + // Flushes any pending write buffers to the underlying filesystem. + virtual void Flush() = 0; + + protected: + FileHandle(std::wstring path) : path_(std::move(path)) {} + + std::wstring path_; +}; + struct FileInfo { enum class Type { kFile, diff --git a/src/xenia/base/filesystem_win.cc b/src/xenia/base/filesystem_win.cc index afa010bc2..02b5c3e64 100644 --- a/src/xenia/base/filesystem_win.cc +++ b/src/xenia/base/filesystem_win.cc @@ -56,6 +56,89 @@ bool DeleteFile(const std::wstring& path) { return DeleteFileW(path.c_str()) ? true : false; } +class Win32FileHandle : public FileHandle { + public: + Win32FileHandle(std::wstring path, HANDLE handle) + : FileHandle(std::move(path)), handle_(handle) {} + ~Win32FileHandle() override { + CloseHandle(handle_); + handle_ = nullptr; + } + bool Read(size_t file_offset, void* buffer, size_t buffer_length, + size_t* out_bytes_read) override { + *out_bytes_read = 0; + OVERLAPPED overlapped; + overlapped.Pointer = (PVOID)file_offset; + overlapped.hEvent = NULL; + DWORD bytes_read = 0; + BOOL read = ReadFile(handle_, buffer, (DWORD)buffer_length, &bytes_read, + &overlapped); + if (read) { + *out_bytes_read = bytes_read; + return true; + } else { + return false; + } + } + bool Write(size_t file_offset, const void* buffer, size_t buffer_length, + size_t* out_bytes_written) override { + *out_bytes_written = 0; + OVERLAPPED overlapped; + overlapped.Pointer = (PVOID)file_offset; + overlapped.hEvent = NULL; + DWORD bytes_written = 0; + BOOL wrote = WriteFile(handle_, buffer, (DWORD)buffer_length, + &bytes_written, &overlapped); + if (wrote) { + *out_bytes_written = bytes_written; + return true; + } else { + return false; + } + } + void Flush() override { FlushFileBuffers(handle_); } + + private: + HANDLE handle_ = nullptr; +}; + +std::unique_ptr FileHandle::OpenExisting(std::wstring path, + uint32_t desired_access) { + DWORD open_access = 0; + if (desired_access & FileAccess::kGenericRead) { + open_access |= GENERIC_READ; + } + if (desired_access & FileAccess::kGenericWrite) { + open_access |= GENERIC_WRITE; + } + if (desired_access & FileAccess::kGenericExecute) { + open_access |= GENERIC_EXECUTE; + } + if (desired_access & FileAccess::kGenericAll) { + open_access |= GENERIC_ALL; + } + if (desired_access & FileAccess::kFileReadData) { + open_access |= FILE_READ_DATA; + } + if (desired_access & FileAccess::kFileWriteData) { + open_access |= FILE_WRITE_DATA; + } + if (desired_access & FileAccess::kFileAppendData) { + open_access |= FILE_APPEND_DATA; + } + DWORD share_mode = FILE_SHARE_READ; + // We assume we've already created the file in the caller. + DWORD creation_disposition = OPEN_EXISTING; + HANDLE handle = + CreateFileW(path.c_str(), open_access, share_mode, NULL, + creation_disposition, FILE_FLAG_BACKUP_SEMANTICS, NULL); + if (handle == INVALID_HANDLE_VALUE) { + // TODO(benvanik): pick correct response. + return nullptr; + } + return std::make_unique(path, handle); +} + #define COMBINE_TIME(t) (((uint64_t)t.dwHighDateTime << 32) | t.dwLowDateTime) bool GetInfo(const std::wstring& path, FileInfo* out_info) { diff --git a/src/xenia/vfs/devices/host_path_entry.cc b/src/xenia/vfs/devices/host_path_entry.cc index 1d238187f..4e1fcc0c7 100644 --- a/src/xenia/vfs/devices/host_path_entry.cc +++ b/src/xenia/vfs/devices/host_path_entry.cc @@ -9,10 +9,10 @@ #include "xenia/vfs/devices/host_path_entry.h" +#include "xenia/base/filesystem.h" #include "xenia/base/logging.h" #include "xenia/base/mapped_memory.h" #include "xenia/base/math.h" -#include "xenia/base/platform_win.h" #include "xenia/base/string.h" #include "xenia/vfs/devices/host_path_file.h" @@ -54,41 +54,14 @@ X_STATUS HostPathEntry::Open(KernelState* kernel_state, uint32_t desired_access, XELOGE("Attempting to open file for write access on read-only device"); return X_STATUS_ACCESS_DENIED; } - DWORD open_access = 0; - if (desired_access & FileAccess::kGenericRead) { - open_access |= GENERIC_READ; - } - if (desired_access & FileAccess::kGenericWrite) { - open_access |= GENERIC_WRITE; - } - if (desired_access & FileAccess::kGenericExecute) { - open_access |= GENERIC_EXECUTE; - } - if (desired_access & FileAccess::kGenericAll) { - open_access |= GENERIC_ALL; - } - if (desired_access & FileAccess::kFileReadData) { - open_access |= FILE_READ_DATA; - } - if (desired_access & FileAccess::kFileWriteData) { - open_access |= FILE_WRITE_DATA; - } - if (desired_access & FileAccess::kFileAppendData) { - open_access |= FILE_APPEND_DATA; - } - DWORD share_mode = FILE_SHARE_READ; - // We assume we've already created the file in the caller. - DWORD creation_disposition = OPEN_EXISTING; - HANDLE file = - CreateFileW(local_path_.c_str(), desired_access, share_mode, NULL, - creation_disposition, FILE_FLAG_BACKUP_SEMANTICS, NULL); - if (file == INVALID_HANDLE_VALUE) { + auto file_handle = + xe::filesystem::FileHandle::OpenExisting(local_path_, desired_access); + if (!file_handle) { // TODO(benvanik): pick correct response. return X_STATUS_NO_SUCH_FILE; } - - *out_file = object_ref( - new HostPathFile(kernel_state, desired_access, this, file)); + *out_file = object_ref(new HostPathFile(kernel_state, desired_access, + this, std::move(file_handle))); return X_STATUS_SUCCESS; } diff --git a/src/xenia/vfs/devices/host_path_file.cc b/src/xenia/vfs/devices/host_path_file.cc index 9eef179da..0982f9a3c 100644 --- a/src/xenia/vfs/devices/host_path_file.cc +++ b/src/xenia/vfs/devices/host_path_file.cc @@ -9,19 +9,19 @@ #include "xenia/vfs/devices/host_path_file.h" -#include "xenia/base/platform_win.h" #include "xenia/vfs/devices/host_path_entry.h" namespace xe { namespace vfs { -HostPathFile::HostPathFile(KernelState* kernel_state, uint32_t file_access, - HostPathEntry* entry, HANDLE file_handle) +HostPathFile::HostPathFile( + KernelState* kernel_state, uint32_t file_access, HostPathEntry* entry, + std::unique_ptr file_handle) : XFile(kernel_state, file_access, entry), entry_(entry), - file_handle_(file_handle) {} + file_handle_(std::move(file_handle)) {} -HostPathFile::~HostPathFile() { CloseHandle(file_handle_); } +HostPathFile::~HostPathFile() = default; X_STATUS HostPathFile::ReadSync(void* buffer, size_t buffer_length, size_t byte_offset, size_t* out_bytes_read) { @@ -29,14 +29,7 @@ X_STATUS HostPathFile::ReadSync(void* buffer, size_t buffer_length, return X_STATUS_ACCESS_DENIED; } - OVERLAPPED overlapped; - overlapped.Pointer = (PVOID)byte_offset; - overlapped.hEvent = NULL; - DWORD bytes_read = 0; - BOOL read = ReadFile(file_handle_, buffer, (DWORD)buffer_length, &bytes_read, - &overlapped); - if (read) { - *out_bytes_read = bytes_read; + if (file_handle_->Read(byte_offset, buffer, buffer_length, out_bytes_read)) { return X_STATUS_SUCCESS; } else { return X_STATUS_END_OF_FILE; @@ -51,14 +44,8 @@ X_STATUS HostPathFile::WriteSync(const void* buffer, size_t buffer_length, return X_STATUS_ACCESS_DENIED; } - OVERLAPPED overlapped; - overlapped.Pointer = (PVOID)byte_offset; - overlapped.hEvent = NULL; - DWORD bytes_written = 0; - BOOL wrote = WriteFile(file_handle_, buffer, (DWORD)buffer_length, - &bytes_written, &overlapped); - if (wrote) { - *out_bytes_written = bytes_written; + if (file_handle_->Write(byte_offset, buffer, buffer_length, + out_bytes_written)) { return X_STATUS_SUCCESS; } else { return X_STATUS_END_OF_FILE; diff --git a/src/xenia/vfs/devices/host_path_file.h b/src/xenia/vfs/devices/host_path_file.h index 5b3374d1c..8a13169f8 100644 --- a/src/xenia/vfs/devices/host_path_file.h +++ b/src/xenia/vfs/devices/host_path_file.h @@ -12,6 +12,7 @@ #include +#include "xenia/base/filesystem.h" #include "xenia/kernel/objects/xfile.h" typedef void* HANDLE; @@ -24,7 +25,8 @@ class HostPathEntry; class HostPathFile : public XFile { public: HostPathFile(KernelState* kernel_state, uint32_t file_access, - HostPathEntry* entry, HANDLE file_handle); + HostPathEntry* entry, + std::unique_ptr file_handle); ~HostPathFile() override; protected: @@ -35,7 +37,7 @@ class HostPathFile : public XFile { private: HostPathEntry* entry_; - HANDLE file_handle_; + std::unique_ptr file_handle_; }; } // namespace vfs diff --git a/src/xenia/vfs/entry.h b/src/xenia/vfs/entry.h index e7799f127..e88b07286 100644 --- a/src/xenia/vfs/entry.h +++ b/src/xenia/vfs/entry.h @@ -14,6 +14,7 @@ #include #include +#include "xenia/base/filesystem.h" #include "xenia/base/mapped_memory.h" #include "xenia/base/string_buffer.h" #include "xenia/kernel/xobject.h" @@ -61,17 +62,8 @@ enum class FileDisposition { kOverwriteIf = 5, }; -struct FileAccess { - // Implies kFileReadData. - static const uint32_t kGenericRead = 0x80000000; - // Implies kFileWriteData. - static const uint32_t kGenericWrite = 0x40000000; - static const uint32_t kGenericExecute = 0x20000000; - static const uint32_t kGenericAll = 0x10000000; - static const uint32_t kFileReadData = 0x00000001; - static const uint32_t kFileWriteData = 0x00000002; - static const uint32_t kFileAppendData = 0x00000004; -}; +// Reuse xe::filesystem definition. +using FileAccess = xe::filesystem::FileAccess; enum FileAttributeFlags : uint32_t { kFileAttributeNone = 0x0000,