From bcacb9b12765b68545bb67344dec85bd8d574ad2 Mon Sep 17 00:00:00 2001 From: "Dr. Chat" Date: Tue, 15 Dec 2015 16:52:41 -0600 Subject: [PATCH] Remove vfs dependency on kernel, implement I/O completion ports --- src/xenia/kernel/xboxkrnl/xboxkrnl_io.cc | 83 ++++++++++++++++--- src/xenia/kernel/xfile.cc | 82 +++++++++++++++--- src/xenia/kernel/xfile.h | 32 +++---- src/xenia/kernel/xiocompletion.cc | 50 +++++++++++ src/xenia/kernel/xiocompletion.h | 54 ++++++++++++ src/xenia/vfs/devices/disc_image_entry.cc | 7 +- src/xenia/vfs/devices/disc_image_entry.h | 3 +- src/xenia/vfs/devices/disc_image_file.cc | 7 +- src/xenia/vfs/devices/disc_image_file.h | 14 ++-- src/xenia/vfs/devices/host_path_entry.cc | 8 +- src/xenia/vfs/devices/host_path_entry.h | 3 +- src/xenia/vfs/devices/host_path_file.cc | 12 +-- src/xenia/vfs/devices/host_path_file.h | 10 +-- src/xenia/vfs/devices/stfs_container_entry.cc | 7 +- src/xenia/vfs/devices/stfs_container_entry.h | 4 +- src/xenia/vfs/devices/stfs_container_file.cc | 7 +- src/xenia/vfs/devices/stfs_container_file.h | 16 ++-- src/xenia/vfs/entry.h | 8 +- src/xenia/vfs/file.h | 61 ++++++++++++++ src/xenia/vfs/virtual_file_system.cc | 10 +-- src/xenia/vfs/virtual_file_system.h | 8 +- 21 files changed, 385 insertions(+), 101 deletions(-) create mode 100644 src/xenia/kernel/xiocompletion.cc create mode 100644 src/xenia/kernel/xiocompletion.h create mode 100644 src/xenia/vfs/file.h diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_io.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_io.cc index 33731e0e8..ff3bdeebb 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_io.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_io.cc @@ -14,6 +14,7 @@ #include "xenia/kernel/util/shim_utils.h" #include "xenia/kernel/xboxkrnl/xboxkrnl_private.h" #include "xenia/kernel/xevent.h" +#include "xenia/kernel/xiocompletion.h" #include "xenia/kernel/xfile.h" #include "xenia/kernel/xthread.h" #include "xenia/vfs/device.h" @@ -120,15 +121,17 @@ dword_result_t NtCreateFile(lpdword_t handle_out, dword_t desired_access, } // Attempt open (or create). - object_ref file; - xe::vfs::FileAction file_action; + vfs::File* vfs_file; + vfs::FileAction file_action; X_STATUS result = kernel_state()->file_system()->OpenFile( - kernel_state(), target_path, - xe::vfs::FileDisposition((uint32_t)creation_disposition), desired_access, - &file, &file_action); + target_path, vfs::FileDisposition((uint32_t)creation_disposition), + desired_access, &vfs_file, &file_action); + object_ref file = nullptr; X_HANDLE handle = X_INVALID_HANDLE_VALUE; if (XSUCCEEDED(result)) { + file = object_ref(new XFile(kernel_state(), vfs_file)); + // Handle ref is incremented, so return that. handle = file->handle(); } @@ -178,7 +181,8 @@ dword_result_t NtReadFile(dword_t file_handle, dword_t event_handle, // Synchronous. size_t bytes_read = 0; result = file->Read(buffer, buffer_length, - byte_offset_ptr ? *byte_offset_ptr : -1, &bytes_read); + byte_offset_ptr ? *byte_offset_ptr : -1, &bytes_read, + apc_context); if (io_status_block) { io_status_block->status = result; io_status_block->information = (uint32_t)bytes_read; @@ -261,9 +265,9 @@ dword_result_t NtWriteFile(dword_t file_handle, dword_t event_handle, if (true) { // Synchronous request. size_t bytes_written = 0; - result = - file->Write(buffer, buffer_length, - byte_offset_ptr ? *byte_offset_ptr : -1, &bytes_written); + result = file->Write(buffer, buffer_length, + byte_offset_ptr ? *byte_offset_ptr : -1, + &bytes_written, apc_context); if (XSUCCEEDED(result)) { info = (int32_t)bytes_written; } @@ -291,13 +295,54 @@ dword_result_t NtWriteFile(dword_t file_handle, dword_t event_handle, } DECLARE_XBOXKRNL_EXPORT(NtWriteFile, ExportTag::kImplemented); -dword_result_t NtCreateIoCompletion(lpvoid_t out_handle, dword_t desired_access, +dword_result_t NtCreateIoCompletion(lpdword_t out_handle, + dword_t desired_access, lpvoid_t object_attribs, dword_t num_concurrent_threads) { - return X_STATUS_UNSUCCESSFUL; + auto completion = new XIOCompletion(kernel_state()); + if (out_handle) { + *out_handle = completion->handle(); + } + + return X_STATUS_SUCCESS; } DECLARE_XBOXKRNL_EXPORT(NtCreateIoCompletion, ExportTag::kStub); +// Dequeues a packet from the completion port. +dword_result_t NtRemoveIoCompletion( + dword_t handle, lpdword_t key_context, lpdword_t apc_context, + pointer_t io_status_block, lpqword_t timeout) { + X_STATUS status = X_STATUS_SUCCESS; + uint32_t info = 0; + + auto port = + kernel_state()->object_table()->LookupObject(handle); + if (!port) { + status = X_STATUS_INVALID_HANDLE; + } + + uint64_t timeout_ticks = timeout ? *timeout : 0; + if (port->WaitForNotification(timeout_ticks)) { + auto notification = port->DequeueNotification(); + if (key_context) { + *key_context = notification.key_context; + } + if (apc_context) { + *apc_context = notification.apc_context; + } + + if (io_status_block) { + io_status_block->status = notification.status; + io_status_block->information = notification.num_bytes; + } + } else { + status = X_STATUS_TIMEOUT; + } + + return status; +} +DECLARE_XBOXKRNL_EXPORT(NtRemoveIoCompletion, ExportTag::kStub); + dword_result_t NtSetInformationFile( dword_t file_handle, pointer_t io_status_block, lpvoid_t file_info, dword_t length, dword_t file_info_class) { @@ -335,9 +380,21 @@ dword_result_t NtSetInformationFile( info = 8; XELOGW("NtSetInformationFile ignoring alloc/eof"); break; - case XFileCompletionInformation: - // Games appear to call NtCreateIoCompletion right before this + case XFileCompletionInformation: { + // Info contains IO Completion handle and completion key + assert_true(length == 8); + + auto handle = xe::load_and_swap(file_info + 0x0); + auto key = xe::load_and_swap(file_info + 0x4); + auto port = + kernel_state()->object_table()->LookupObject(handle); + if (!port) { + return X_STATUS_INVALID_HANDLE; + } + + file->RegisterIOCompletionPort(key, port); break; + } default: // Unsupported, for now. assert_always(); diff --git a/src/xenia/kernel/xfile.cc b/src/xenia/kernel/xfile.cc index 3b85372fc..c2e00e9a5 100644 --- a/src/xenia/kernel/xfile.cc +++ b/src/xenia/kernel/xfile.cc @@ -15,10 +15,8 @@ namespace xe { namespace kernel { -XFile::XFile(KernelState* kernel_state, uint32_t file_access, vfs::Entry* entry) - : XObject(kernel_state, kTypeFile), - entry_(entry), - file_access_(file_access) { +XFile::XFile(KernelState* kernel_state, vfs::File* file) + : XObject(kernel_state, kTypeFile), file_(file) { async_event_ = new XEvent(kernel_state); async_event_->Initialize(false, false); } @@ -27,6 +25,8 @@ XFile::~XFile() { // TODO(benvanik): signal that the file is closing? async_event_->Set(0, false); async_event_->Delete(); + + file_->Destroy(); } X_STATUS XFile::QueryDirectory(X_FILE_DIRECTORY_INFORMATION* out_info, @@ -44,7 +44,7 @@ X_STATUS XFile::QueryDirectory(X_FILE_DIRECTORY_INFORMATION* out_info, // Always restart the search? find_index_ = 0; - entry = entry_->IterateChildren(find_engine_, &find_index_); + entry = file_->entry()->IterateChildren(find_engine_, &find_index_); if (!entry) { return X_STATUS_NO_SUCH_FILE; } @@ -53,7 +53,7 @@ X_STATUS XFile::QueryDirectory(X_FILE_DIRECTORY_INFORMATION* out_info, find_index_ = 0; } - entry = entry_->IterateChildren(find_engine_, &find_index_); + entry = file_->entry()->IterateChildren(find_engine_, &find_index_); if (!entry) { return X_STATUS_NO_SUCH_FILE; } @@ -83,34 +83,92 @@ X_STATUS XFile::QueryDirectory(X_FILE_DIRECTORY_INFORMATION* out_info, } X_STATUS XFile::Read(void* buffer, size_t buffer_length, size_t byte_offset, - size_t* out_bytes_read) { + size_t* out_bytes_read, uint32_t apc_context) { if (byte_offset == -1) { // Read from current position. byte_offset = position_; } + + size_t bytes_read = 0; X_STATUS result = - ReadSync(buffer, buffer_length, byte_offset, out_bytes_read); + file_->ReadSync(buffer, buffer_length, byte_offset, &bytes_read); if (XSUCCEEDED(result)) { - position_ += *out_bytes_read; + position_ += bytes_read; } + + XIOCompletion::IONotification notify; + notify.apc_context = apc_context; + notify.num_bytes = uint32_t(bytes_read); + notify.status = result; + + NotifyIOCompletionPorts(notify); + + if (out_bytes_read) { + *out_bytes_read = bytes_read; + } + async_event_->Set(0, false); return result; } X_STATUS XFile::Write(const void* buffer, size_t buffer_length, - size_t byte_offset, size_t* out_bytes_written) { + size_t byte_offset, size_t* out_bytes_written, + uint32_t apc_context) { if (byte_offset == -1) { // Write from current position. byte_offset = position_; } + + size_t bytes_written = 0; X_STATUS result = - WriteSync(buffer, buffer_length, byte_offset, out_bytes_written); + file_->WriteSync(buffer, buffer_length, byte_offset, &bytes_written); if (XSUCCEEDED(result)) { - position_ += *out_bytes_written; + position_ += bytes_written; } + + XIOCompletion::IONotification notify; + notify.apc_context = apc_context; + notify.num_bytes = uint32_t(bytes_written); + notify.status = result; + + NotifyIOCompletionPorts(notify); + + if (out_bytes_written) { + *out_bytes_written = bytes_written; + } + async_event_->Set(0, false); return result; } +void XFile::RegisterIOCompletionPort(uint32_t key, + object_ref port) { + std::lock_guard lock(completion_port_lock_); + + completion_ports_.push_back({key, port}); +} + +void XFile::RemoveIOCompletionPort(uint32_t key) { + std::lock_guard lock(completion_port_lock_); + + for (auto it = completion_ports_.begin(); it != completion_ports_.end(); + it++) { + if (it->first == key) { + completion_ports_.erase(it); + break; + } + } +} + +void XFile::NotifyIOCompletionPorts( + XIOCompletion::IONotification& notification) { + std::lock_guard lock(completion_port_lock_); + + for (auto port : completion_ports_) { + notification.key_context = port.first; + port.second->QueueNotification(notification); + } +} + } // namespace kernel } // namespace xe diff --git a/src/xenia/kernel/xfile.h b/src/xenia/kernel/xfile.h index e3e4dd3da..851ad6e05 100644 --- a/src/xenia/kernel/xfile.h +++ b/src/xenia/kernel/xfile.h @@ -14,9 +14,11 @@ #include "xenia/base/filesystem.h" #include "xenia/kernel/xevent.h" +#include "xenia/kernel/xiocompletion.h" #include "xenia/kernel/xobject.h" #include "xenia/vfs/device.h" #include "xenia/vfs/entry.h" +#include "xenia/vfs/file.h" #include "xenia/xbox.h" namespace xe { @@ -79,14 +81,16 @@ class XFile : public XObject { public: static const Type kType = kTypeFile; + XFile(KernelState* kernel_state, vfs::File* file); ~XFile() override; - vfs::Device* device() const { return entry_->device(); } - vfs::Entry* entry() const { return entry_; } + vfs::Device* device() const { return file_->entry()->device(); } + vfs::Entry* entry() const { return file_->entry(); } + vfs::File* file() const { return file_; } uint32_t file_access() const { return file_access_; } - const std::string& path() const { return entry_->path(); } - const std::string& name() const { return entry_->name(); } + const std::string& path() const { return file_->entry()->path(); } + const std::string& name() const { return file_->entry()->name(); } size_t position() const { return position_; } void set_position(size_t value) { position_ = value; } @@ -95,29 +99,29 @@ class XFile : public XObject { const char* file_name, bool restart); X_STATUS Read(void* buffer, size_t buffer_length, size_t byte_offset, - size_t* out_bytes_read); + size_t* out_bytes_read, uint32_t apc_context); X_STATUS Write(const void* buffer, size_t buffer_length, size_t byte_offset, - size_t* out_bytes_written); + size_t* out_bytes_written, uint32_t apc_context); xe::threading::WaitHandle* GetWaitHandle() override { return async_event_->GetWaitHandle(); } + void RegisterIOCompletionPort(uint32_t key, object_ref port); + void RemoveIOCompletionPort(uint32_t key); + protected: - XFile(KernelState* kernel_state, uint32_t file_access, vfs::Entry* entry); - virtual X_STATUS ReadSync(void* buffer, size_t buffer_length, - size_t byte_offset, size_t* out_bytes_read) = 0; - virtual X_STATUS WriteSync(const void* buffer, size_t buffer_length, - size_t byte_offset, size_t* out_bytes_written) { - return X_STATUS_ACCESS_DENIED; - } + void NotifyIOCompletionPorts(XIOCompletion::IONotification& notification); private: - vfs::Entry* entry_ = nullptr; + vfs::File* file_ = nullptr; uint32_t file_access_ = 0; XEvent* async_event_ = nullptr; + std::mutex completion_port_lock_; + std::vector>> completion_ports_; + // TODO(benvanik): create flags, open state, etc. size_t position_ = 0; diff --git a/src/xenia/kernel/xiocompletion.cc b/src/xenia/kernel/xiocompletion.cc new file mode 100644 index 000000000..fd525edc4 --- /dev/null +++ b/src/xenia/kernel/xiocompletion.cc @@ -0,0 +1,50 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2015 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/kernel/xiocompletion.h" + +namespace xe { +namespace kernel { + +XIOCompletion::XIOCompletion(KernelState* kernel_state) + : XObject(kernel_state, kTypeIOCompletion) { + notification_semaphore_ = threading::Semaphore::Create(0, kMaxNotifications); +} + +XIOCompletion::~XIOCompletion() = default; + +void XIOCompletion::QueueNotification(IONotification& notification) { + std::unique_lock lock(notification_lock_); + + notifications_.push(notification); + notification_semaphore_->Release(1, nullptr); +} + +XIOCompletion::IONotification XIOCompletion::DequeueNotification() { + std::unique_lock lock(notification_lock_); + assert_false(notifications_.empty()); + + auto notification = notifications_.front(); + notifications_.pop(); + + return notification; +} + +bool XIOCompletion::WaitForNotification(uint64_t wait_ticks) { + auto ms = std::chrono::milliseconds(TimeoutTicksToMs(wait_ticks)); + auto res = threading::Wait(notification_semaphore_.get(), false, ms); + if (res == threading::WaitResult::kSuccess) { + return true; + } + + return false; +} + +} // namespace kernel +} // namespace xe \ No newline at end of file diff --git a/src/xenia/kernel/xiocompletion.h b/src/xenia/kernel/xiocompletion.h new file mode 100644 index 000000000..fd608e418 --- /dev/null +++ b/src/xenia/kernel/xiocompletion.h @@ -0,0 +1,54 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2015 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_KERNEL_XIOCOMPLETION_H_ +#define XENIA_KERNEL_XIOCOMPLETION_H_ + +#include + +#include "xenia/base/threading.h" +#include "xenia/kernel/xobject.h" +#include "xenia/xbox.h" + +namespace xe { +namespace kernel { + +class XIOCompletion : public XObject { + public: + static const Type kType = kTypeIOCompletion; + + explicit XIOCompletion(KernelState* kernel_state); + ~XIOCompletion() override; + + struct IONotification { + uint32_t key_context; + uint32_t apc_context; + uint32_t status; + uint32_t num_bytes; + }; + + void QueueNotification(IONotification& notification); + IONotification DequeueNotification(); + + // If you call this and it returns true, you MUST dequeue a notification! + // Returns true if the wait was cancelled because a notification was queued. + bool WaitForNotification(uint64_t wait_ticks); + + private: + static const uint32_t kMaxNotifications = 1024; + + std::mutex notification_lock_; + std::queue notifications_; + std::unique_ptr notification_semaphore_ = nullptr; +}; + +} // namespace kernel +} // namespace xe + +#endif // XENIA_KERNEL_XIOCOMPLETION_H_ \ No newline at end of file diff --git a/src/xenia/vfs/devices/disc_image_entry.cc b/src/xenia/vfs/devices/disc_image_entry.cc index d6748c1c7..552a34f7d 100644 --- a/src/xenia/vfs/devices/disc_image_entry.cc +++ b/src/xenia/vfs/devices/disc_image_entry.cc @@ -26,11 +26,8 @@ DiscImageEntry::DiscImageEntry(Device* device, Entry* parent, std::string path, DiscImageEntry::~DiscImageEntry() = default; -X_STATUS DiscImageEntry::Open(kernel::KernelState* kernel_state, - uint32_t desired_access, - kernel::object_ref* out_file) { - *out_file = kernel::object_ref( - new DiscImageFile(kernel_state, desired_access, this)); +X_STATUS DiscImageEntry::Open(uint32_t desired_access, File** out_file) { + *out_file = new DiscImageFile(desired_access, this); return X_STATUS_SUCCESS; } diff --git a/src/xenia/vfs/devices/disc_image_entry.h b/src/xenia/vfs/devices/disc_image_entry.h index a9bb5f089..c2e83e43f 100644 --- a/src/xenia/vfs/devices/disc_image_entry.h +++ b/src/xenia/vfs/devices/disc_image_entry.h @@ -32,8 +32,7 @@ class DiscImageEntry : public Entry { size_t data_offset() const { return data_offset_; } size_t data_size() const { return data_size_; } - X_STATUS Open(kernel::KernelState* kernel_state, uint32_t desired_access, - kernel::object_ref* out_file) override; + X_STATUS Open(uint32_t desired_access, File** out_file) override; bool can_map() const override { return true; } std::unique_ptr OpenMapped(MappedMemory::Mode mode, diff --git a/src/xenia/vfs/devices/disc_image_file.cc b/src/xenia/vfs/devices/disc_image_file.cc index bcbe56b3b..821189630 100644 --- a/src/xenia/vfs/devices/disc_image_file.cc +++ b/src/xenia/vfs/devices/disc_image_file.cc @@ -16,12 +16,13 @@ namespace xe { namespace vfs { -DiscImageFile::DiscImageFile(kernel::KernelState* kernel_state, - uint32_t file_access, DiscImageEntry* entry) - : XFile(kernel_state, file_access, entry), entry_(entry) {} +DiscImageFile::DiscImageFile(uint32_t file_access, DiscImageEntry* entry) + : File(file_access, entry), entry_(entry) {} DiscImageFile::~DiscImageFile() = default; +void DiscImageFile::Destroy() { delete this; } + X_STATUS DiscImageFile::ReadSync(void* buffer, size_t buffer_length, size_t byte_offset, size_t* out_bytes_read) { if (byte_offset >= entry_->size()) { diff --git a/src/xenia/vfs/devices/disc_image_file.h b/src/xenia/vfs/devices/disc_image_file.h index e477286e1..4e904a248 100644 --- a/src/xenia/vfs/devices/disc_image_file.h +++ b/src/xenia/vfs/devices/disc_image_file.h @@ -10,22 +10,26 @@ #ifndef XENIA_VFS_DEVICES_DISC_IMAGE_FILE_H_ #define XENIA_VFS_DEVICES_DISC_IMAGE_FILE_H_ -#include "xenia/kernel/xfile.h" +#include "xenia/vfs/file.h" namespace xe { namespace vfs { class DiscImageEntry; -class DiscImageFile : public kernel::XFile { +class DiscImageFile : public File { public: - DiscImageFile(kernel::KernelState* kernel_state, uint32_t file_access, - DiscImageEntry* entry); + DiscImageFile(uint32_t file_access, DiscImageEntry* entry); ~DiscImageFile() override; - protected: + void Destroy() override; + X_STATUS ReadSync(void* buffer, size_t buffer_length, size_t byte_offset, size_t* out_bytes_read) override; + X_STATUS WriteSync(const void* buffer, size_t buffer_length, + size_t byte_offset, size_t* out_bytes_written) override { + return X_STATUS_ACCESS_DENIED; + } private: DiscImageEntry* entry_; diff --git a/src/xenia/vfs/devices/host_path_entry.cc b/src/xenia/vfs/devices/host_path_entry.cc index ddcfde485..d704a3be2 100644 --- a/src/xenia/vfs/devices/host_path_entry.cc +++ b/src/xenia/vfs/devices/host_path_entry.cc @@ -14,6 +14,7 @@ #include "xenia/base/mapped_memory.h" #include "xenia/base/math.h" #include "xenia/base/string.h" +#include "xenia/vfs/device.h" #include "xenia/vfs/devices/host_path_file.h" namespace xe { @@ -47,9 +48,7 @@ HostPathEntry* HostPathEntry::Create(Device* device, Entry* parent, return entry; } -X_STATUS HostPathEntry::Open(kernel::KernelState* kernel_state, - uint32_t desired_access, - kernel::object_ref* out_file) { +X_STATUS HostPathEntry::Open(uint32_t desired_access, File** out_file) { if (is_read_only() && (desired_access & (FileAccess::kFileWriteData | FileAccess::kFileAppendData))) { XELOGE("Attempting to open file for write access on read-only device"); @@ -61,8 +60,7 @@ X_STATUS HostPathEntry::Open(kernel::KernelState* kernel_state, // TODO(benvanik): pick correct response. return X_STATUS_NO_SUCH_FILE; } - *out_file = kernel::object_ref(new HostPathFile( - kernel_state, desired_access, this, std::move(file_handle))); + *out_file = new HostPathFile(desired_access, this, std::move(file_handle)); return X_STATUS_SUCCESS; } diff --git a/src/xenia/vfs/devices/host_path_entry.h b/src/xenia/vfs/devices/host_path_entry.h index ba7970d13..50e0aea82 100644 --- a/src/xenia/vfs/devices/host_path_entry.h +++ b/src/xenia/vfs/devices/host_path_entry.h @@ -32,8 +32,7 @@ class HostPathEntry : public Entry { const std::wstring& local_path() { return local_path_; } - X_STATUS Open(kernel::KernelState* kernel_state, uint32_t desired_access, - kernel::object_ref* out_file) override; + X_STATUS Open(uint32_t desired_access, File** out_file) override; bool can_map() const override { return true; } std::unique_ptr OpenMapped(MappedMemory::Mode mode, diff --git a/src/xenia/vfs/devices/host_path_file.cc b/src/xenia/vfs/devices/host_path_file.cc index 851c78d77..9d4718044 100644 --- a/src/xenia/vfs/devices/host_path_file.cc +++ b/src/xenia/vfs/devices/host_path_file.cc @@ -15,17 +15,17 @@ namespace xe { namespace vfs { HostPathFile::HostPathFile( - kernel::KernelState* kernel_state, uint32_t file_access, - HostPathEntry* entry, + uint32_t file_access, HostPathEntry* entry, std::unique_ptr file_handle) - : XFile(kernel_state, file_access, entry), - file_handle_(std::move(file_handle)) {} + : File(file_access, entry), file_handle_(std::move(file_handle)) {} HostPathFile::~HostPathFile() = default; +void HostPathFile::Destroy() { delete this; } + X_STATUS HostPathFile::ReadSync(void* buffer, size_t buffer_length, size_t byte_offset, size_t* out_bytes_read) { - if (!(file_access() & FileAccess::kFileReadData)) { + if (!(file_access_ & FileAccess::kFileReadData)) { return X_STATUS_ACCESS_DENIED; } @@ -39,7 +39,7 @@ X_STATUS HostPathFile::ReadSync(void* buffer, size_t buffer_length, X_STATUS HostPathFile::WriteSync(const void* buffer, size_t buffer_length, size_t byte_offset, size_t* out_bytes_written) { - if (!(file_access() & + if (!(file_access_ & (FileAccess::kFileWriteData | FileAccess::kFileAppendData))) { return X_STATUS_ACCESS_DENIED; } diff --git a/src/xenia/vfs/devices/host_path_file.h b/src/xenia/vfs/devices/host_path_file.h index 325c131d4..7785da2c5 100644 --- a/src/xenia/vfs/devices/host_path_file.h +++ b/src/xenia/vfs/devices/host_path_file.h @@ -13,21 +13,21 @@ #include #include "xenia/base/filesystem.h" -#include "xenia/kernel/xfile.h" +#include "xenia/vfs/file.h" namespace xe { namespace vfs { class HostPathEntry; -class HostPathFile : public kernel::XFile { +class HostPathFile : public File { public: - HostPathFile(kernel::KernelState* kernel_state, uint32_t file_access, - HostPathEntry* entry, + HostPathFile(uint32_t file_access, HostPathEntry* entry, std::unique_ptr file_handle); ~HostPathFile() override; - protected: + void Destroy() override; + X_STATUS ReadSync(void* buffer, size_t buffer_length, size_t byte_offset, size_t* out_bytes_read) override; X_STATUS WriteSync(const void* buffer, size_t buffer_length, diff --git a/src/xenia/vfs/devices/stfs_container_entry.cc b/src/xenia/vfs/devices/stfs_container_entry.cc index 3df1f8d5f..79b8756da 100644 --- a/src/xenia/vfs/devices/stfs_container_entry.cc +++ b/src/xenia/vfs/devices/stfs_container_entry.cc @@ -24,11 +24,8 @@ StfsContainerEntry::StfsContainerEntry(Device* device, Entry* parent, StfsContainerEntry::~StfsContainerEntry() = default; -X_STATUS StfsContainerEntry::Open(kernel::KernelState* kernel_state, - uint32_t desired_access, - kernel::object_ref* out_file) { - *out_file = kernel::object_ref( - new StfsContainerFile(kernel_state, desired_access, this)); +X_STATUS StfsContainerEntry::Open(uint32_t desired_access, File** out_file) { + *out_file = new StfsContainerFile(desired_access, this); return X_STATUS_SUCCESS; } diff --git a/src/xenia/vfs/devices/stfs_container_entry.h b/src/xenia/vfs/devices/stfs_container_entry.h index 11a7138dc..2ab2d9dae 100644 --- a/src/xenia/vfs/devices/stfs_container_entry.h +++ b/src/xenia/vfs/devices/stfs_container_entry.h @@ -16,6 +16,7 @@ #include "xenia/base/filesystem.h" #include "xenia/base/mapped_memory.h" #include "xenia/vfs/entry.h" +#include "xenia/vfs/file.h" namespace xe { namespace vfs { @@ -32,8 +33,7 @@ class StfsContainerEntry : public Entry { size_t data_offset() const { return data_offset_; } size_t data_size() const { return data_size_; } - X_STATUS Open(kernel::KernelState* kernel_state, uint32_t desired_access, - kernel::object_ref* out_file) override; + X_STATUS Open(uint32_t desired_access, File** out_file) override; struct BlockRecord { size_t offset; diff --git a/src/xenia/vfs/devices/stfs_container_file.cc b/src/xenia/vfs/devices/stfs_container_file.cc index 0de27b61d..aab01da83 100644 --- a/src/xenia/vfs/devices/stfs_container_file.cc +++ b/src/xenia/vfs/devices/stfs_container_file.cc @@ -16,13 +16,14 @@ namespace xe { namespace vfs { -StfsContainerFile::StfsContainerFile(kernel::KernelState* kernel_state, - uint32_t file_access, +StfsContainerFile::StfsContainerFile(uint32_t file_access, StfsContainerEntry* entry) - : XFile(kernel_state, file_access, entry), entry_(entry) {} + : File(file_access, entry) {} StfsContainerFile::~StfsContainerFile() = default; +void StfsContainerFile::Destroy() { delete this; } + X_STATUS StfsContainerFile::ReadSync(void* buffer, size_t buffer_length, size_t byte_offset, size_t* out_bytes_read) { diff --git a/src/xenia/vfs/devices/stfs_container_file.h b/src/xenia/vfs/devices/stfs_container_file.h index 7f6874ebf..3525d4bbe 100644 --- a/src/xenia/vfs/devices/stfs_container_file.h +++ b/src/xenia/vfs/devices/stfs_container_file.h @@ -10,22 +10,28 @@ #ifndef XENIA_VFS_DEVICES_STFS_CONTAINER_FILE_H_ #define XENIA_VFS_DEVICES_STFS_CONTAINER_FILE_H_ -#include "xenia/kernel/xfile.h" +#include "xenia/vfs/file.h" + +#include "xenia/xbox.h" namespace xe { namespace vfs { class StfsContainerEntry; -class StfsContainerFile : public kernel::XFile { +class StfsContainerFile : public File { public: - StfsContainerFile(kernel::KernelState* kernel_state, uint32_t file_access, - StfsContainerEntry* entry); + StfsContainerFile(uint32_t file_access, StfsContainerEntry* entry); ~StfsContainerFile() override; - protected: + void Destroy() override; + X_STATUS ReadSync(void* buffer, size_t buffer_length, size_t byte_offset, size_t* out_bytes_read) override; + X_STATUS WriteSync(const void* buffer, size_t buffer_length, + size_t byte_offset, size_t* out_bytes_written) override { + return X_STATUS_ACCESS_DENIED; + } private: StfsContainerEntry* entry_; diff --git a/src/xenia/vfs/entry.h b/src/xenia/vfs/entry.h index bc7852a86..1799da0cf 100644 --- a/src/xenia/vfs/entry.h +++ b/src/xenia/vfs/entry.h @@ -18,7 +18,6 @@ #include "xenia/base/mapped_memory.h" #include "xenia/base/mutex.h" #include "xenia/base/string_buffer.h" -#include "xenia/kernel/xobject.h" #include "xenia/xbox.h" namespace xe { @@ -32,6 +31,7 @@ namespace xe { namespace vfs { class Device; +class File; // Matches http://source.winehq.org/source/include/winternl.h#1591. enum class FileAction { @@ -106,9 +106,9 @@ class Entry { bool Delete(); void Touch(); - virtual X_STATUS Open(kernel::KernelState* kernel_state, - uint32_t desired_access, - kernel::object_ref* out_file) = 0; + // If successful, out_file points to a new file. When finished, call + // file->Destroy() + virtual X_STATUS Open(uint32_t desired_access, File** out_file) = 0; virtual bool can_map() const { return false; } virtual std::unique_ptr OpenMapped(MappedMemory::Mode mode, diff --git a/src/xenia/vfs/file.h b/src/xenia/vfs/file.h new file mode 100644 index 000000000..e1905f2c9 --- /dev/null +++ b/src/xenia/vfs/file.h @@ -0,0 +1,61 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2015 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_VFS_FILE_H_ +#define XENIA_VFS_FILE_H_ + +#include + +#include "xenia/xbox.h" + +namespace xe { +namespace vfs { + +class Entry; + +class File { + public: + File(uint32_t file_access, Entry* entry) + : file_access_(file_access), entry_(entry) {} + virtual ~File() = default; + + virtual void Destroy() = 0; + + virtual X_STATUS ReadSync(void* buffer, size_t buffer_length, + size_t byte_offset, size_t* out_bytes_read) = 0; + virtual X_STATUS WriteSync(const void* buffer, size_t buffer_length, + size_t byte_offset, size_t* out_bytes_written) = 0; + + // TODO: Parameters + virtual X_STATUS ReadAsync(void* buffer, size_t buffer_length, + size_t byte_offset, size_t* out_bytes_read) { + return X_STATUS_NOT_IMPLEMENTED; + } + + // TODO: Parameters + virtual X_STATUS WriteAsync(const void* buffer, size_t buffer_length, + size_t byte_offset, size_t* out_bytes_written) { + return X_STATUS_NOT_IMPLEMENTED; + } + + // xe::filesystem::FileAccess + uint32_t file_access() const { return file_access_; } + const Entry* entry() const { return entry_; } + Entry* entry() { return entry_; } + + protected: + // xe::filesystem::FileAccess + uint32_t file_access_ = 0; + Entry* entry_ = nullptr; +}; + +} // namespace vfs +} // namespace xe + +#endif // XENIA_VFS_FILE_H_ \ No newline at end of file diff --git a/src/xenia/vfs/virtual_file_system.cc b/src/xenia/vfs/virtual_file_system.cc index 90694442f..5806c0824 100644 --- a/src/xenia/vfs/virtual_file_system.cc +++ b/src/xenia/vfs/virtual_file_system.cc @@ -140,10 +140,10 @@ bool VirtualFileSystem::DeletePath(std::string path) { return parent->Delete(entry); } -X_STATUS VirtualFileSystem::OpenFile( - kernel::KernelState* kernel_state, std::string path, - FileDisposition creation_disposition, uint32_t desired_access, - kernel::object_ref* out_file, FileAction* out_action) { +X_STATUS VirtualFileSystem::OpenFile(std::string path, + FileDisposition creation_disposition, + uint32_t desired_access, File** out_file, + FileAction* out_action) { // Cleanup access. if (desired_access & FileAccess::kGenericRead) { desired_access |= FileAccess::kFileReadData; @@ -253,7 +253,7 @@ X_STATUS VirtualFileSystem::OpenFile( } // Open. - auto result = entry->Open(kernel_state, desired_access, out_file); + auto result = entry->Open(desired_access, out_file); if (XFAILED(result)) { *out_action = FileAction::kDoesNotExist; } diff --git a/src/xenia/vfs/virtual_file_system.h b/src/xenia/vfs/virtual_file_system.h index cd854870b..f9f3eff62 100644 --- a/src/xenia/vfs/virtual_file_system.h +++ b/src/xenia/vfs/virtual_file_system.h @@ -16,9 +16,9 @@ #include #include "xenia/base/mutex.h" -#include "xenia/kernel/xobject.h" #include "xenia/vfs/device.h" #include "xenia/vfs/entry.h" +#include "xenia/vfs/file.h" namespace xe { namespace vfs { @@ -39,10 +39,8 @@ class VirtualFileSystem { Entry* CreatePath(std::string path, uint32_t attributes); bool DeletePath(std::string path); - X_STATUS OpenFile(kernel::KernelState* kernel_state, std::string path, - FileDisposition creation_disposition, - uint32_t desired_access, - kernel::object_ref* out_file, + X_STATUS OpenFile(std::string path, FileDisposition creation_disposition, + uint32_t desired_access, File** out_file, FileAction* out_action); private: