diff --git a/src/xenia/base/filesystem.h b/src/xenia/base/filesystem.h index fbdfe7c1d..f2f2684cf 100644 --- a/src/xenia/base/filesystem.h +++ b/src/xenia/base/filesystem.h @@ -88,6 +88,9 @@ class FileHandle { virtual bool Write(size_t file_offset, const void* buffer, size_t buffer_length, size_t* out_bytes_written) = 0; + // Set length of the file in bytes. + virtual bool SetLength(size_t length) = 0; + // Flushes any pending write buffers to the underlying filesystem. virtual void Flush() = 0; diff --git a/src/xenia/base/filesystem_posix.cc b/src/xenia/base/filesystem_posix.cc index a948717cc..c3c2eaf80 100644 --- a/src/xenia/base/filesystem_posix.cc +++ b/src/xenia/base/filesystem_posix.cc @@ -99,6 +99,9 @@ class PosixFileHandle : public FileHandle { *out_bytes_written = out; return out >= 0 ? true : false; } + bool SetLength(size_t length) override { + return ftruncate(handle_, length) >= 0 ? true : false; + } void Flush() override { fsync(handle_); } private: diff --git a/src/xenia/base/filesystem_win.cc b/src/xenia/base/filesystem_win.cc index 52ae2eb36..c72ebea07 100644 --- a/src/xenia/base/filesystem_win.cc +++ b/src/xenia/base/filesystem_win.cc @@ -115,6 +115,17 @@ class Win32FileHandle : public FileHandle { return false; } } + bool SetLength(size_t length) { + LARGE_INTEGER position; + position.QuadPart = length; + if (!SetFilePointerEx(handle_, position, nullptr, SEEK_SET)) { + return false; + } + if (!SetEndOfFile(handle_)) { + return false; + } + return true; + } void Flush() override { FlushFileBuffers(handle_); } private: diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_io.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_io.cc index c01a7c5f6..c3bbb5e7e 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_io.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_io.cc @@ -393,11 +393,16 @@ dword_result_t NtSetInformationFile( file->set_position(xe::load_and_swap(file_info)); break; case XFileAllocationInformation: - case XFileEndOfFileInformation: assert_true(length == 8); info = 8; - XELOGW("NtSetInformationFile ignoring alloc/eof"); + XELOGW("NtSetInformationFile ignoring alloc"); break; + case XFileEndOfFileInformation: { + assert_true(length == 8); + auto eof = xe::load_and_swap(file_info); + result = file->SetLength(eof); + break; + } case XFileCompletionInformation: { // Info contains IO Completion handle and completion key assert_true(length == 8); diff --git a/src/xenia/kernel/xfile.cc b/src/xenia/kernel/xfile.cc index b369d8357..8f0dc8f51 100644 --- a/src/xenia/kernel/xfile.cc +++ b/src/xenia/kernel/xfile.cc @@ -147,6 +147,8 @@ X_STATUS XFile::Write(const void* buffer, size_t buffer_length, return result; } +X_STATUS XFile::SetLength(size_t length) { return file_->SetLength(length); } + void XFile::RegisterIOCompletionPort(uint32_t key, object_ref port) { std::lock_guard lock(completion_port_lock_); diff --git a/src/xenia/kernel/xfile.h b/src/xenia/kernel/xfile.h index 19fbc2052..fda7abb47 100644 --- a/src/xenia/kernel/xfile.h +++ b/src/xenia/kernel/xfile.h @@ -104,6 +104,8 @@ class XFile : public XObject { X_STATUS Write(const void* buffer, size_t buffer_length, size_t byte_offset, size_t* out_bytes_written, uint32_t apc_context); + X_STATUS SetLength(size_t length); + void RegisterIOCompletionPort(uint32_t key, object_ref port); void RemoveIOCompletionPort(uint32_t key); diff --git a/src/xenia/vfs/devices/disc_image_file.h b/src/xenia/vfs/devices/disc_image_file.h index 4e904a248..d4b996c3e 100644 --- a/src/xenia/vfs/devices/disc_image_file.h +++ b/src/xenia/vfs/devices/disc_image_file.h @@ -30,6 +30,7 @@ class DiscImageFile : public File { size_t byte_offset, size_t* out_bytes_written) override { return X_STATUS_ACCESS_DENIED; } + X_STATUS SetLength(size_t length) override { return X_STATUS_ACCESS_DENIED; } private: DiscImageEntry* entry_; diff --git a/src/xenia/vfs/devices/host_path_file.cc b/src/xenia/vfs/devices/host_path_file.cc index 9d4718044..366273ae6 100644 --- a/src/xenia/vfs/devices/host_path_file.cc +++ b/src/xenia/vfs/devices/host_path_file.cc @@ -52,5 +52,17 @@ X_STATUS HostPathFile::WriteSync(const void* buffer, size_t buffer_length, } } +X_STATUS HostPathFile::SetLength(size_t length) { + if (!(file_access_ & FileAccess::kFileWriteData)) { + return X_STATUS_ACCESS_DENIED; + } + + if (file_handle_->SetLength(length)) { + return X_STATUS_SUCCESS; + } else { + return X_STATUS_END_OF_FILE; + } +} + } // namespace vfs } // namespace xe diff --git a/src/xenia/vfs/devices/host_path_file.h b/src/xenia/vfs/devices/host_path_file.h index 7785da2c5..2d896e11a 100644 --- a/src/xenia/vfs/devices/host_path_file.h +++ b/src/xenia/vfs/devices/host_path_file.h @@ -32,6 +32,7 @@ class HostPathFile : public File { 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; + X_STATUS SetLength(size_t length) override; private: std::unique_ptr file_handle_; diff --git a/src/xenia/vfs/devices/stfs_container_file.h b/src/xenia/vfs/devices/stfs_container_file.h index 3525d4bbe..d680dffe5 100644 --- a/src/xenia/vfs/devices/stfs_container_file.h +++ b/src/xenia/vfs/devices/stfs_container_file.h @@ -32,6 +32,7 @@ class StfsContainerFile : public File { size_t byte_offset, size_t* out_bytes_written) override { return X_STATUS_ACCESS_DENIED; } + X_STATUS SetLength(size_t length) override { return X_STATUS_ACCESS_DENIED; } private: StfsContainerEntry* entry_; diff --git a/src/xenia/vfs/file.h b/src/xenia/vfs/file.h index e1905f2c9..af06c1b45 100644 --- a/src/xenia/vfs/file.h +++ b/src/xenia/vfs/file.h @@ -44,6 +44,8 @@ class File { return X_STATUS_NOT_IMPLEMENTED; } + virtual X_STATUS SetLength(size_t length) { return X_STATUS_NOT_IMPLEMENTED; } + // xe::filesystem::FileAccess uint32_t file_access() const { return file_access_; } const Entry* entry() const { return entry_; }