/** ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** * Copyright 2013 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ #include "xenia/base/logging.h" #include "xenia/base/memory.h" #include "xenia/cpu/processor.h" #include "xenia/kernel/async_request.h" #include "xenia/kernel/kernel_state.h" #include "xenia/kernel/objects/xevent.h" #include "xenia/kernel/objects/xfile.h" #include "xenia/kernel/objects/xthread.h" #include "xenia/kernel/util/shim_utils.h" #include "xenia/kernel/xboxkrnl_private.h" #include "xenia/vfs/device.h" #include "xenia/xbox.h" namespace xe { namespace kernel { using namespace xe::vfs; // TODO(benvanik): replace X_OBJECT_ATTRIBUTES with new style and remove this. class X_ANSI_STRING_OLD { private: uint16_t length; uint16_t maximum_length; const char* buffer; public: X_ANSI_STRING_OLD() { Zero(); } X_ANSI_STRING_OLD(const uint8_t* base, uint32_t p) { Read(base, p); } void Read(const uint8_t* base, uint32_t p) { length = xe::load_and_swap(base + p); maximum_length = xe::load_and_swap(base + p + 2); if (maximum_length) { buffer = (const char*)(base + xe::load_and_swap(base + p + 4)); } else { buffer = 0; } } void Zero() { length = maximum_length = 0; buffer = 0; } char* Duplicate() { if (!buffer || !length) { return nullptr; } auto copy = (char*)calloc(length + 1, sizeof(char)); std::strncpy(copy, buffer, length); return copy; } std::string to_string() { if (!buffer || !length) { return ""; } std::string result(buffer, length); return result; } }; // static_assert_size(X_ANSI_STRING, 8); class X_OBJECT_ATTRIBUTES { public: uint32_t root_directory; uint32_t object_name_ptr; X_ANSI_STRING_OLD object_name; uint32_t attributes; X_OBJECT_ATTRIBUTES() { Zero(); } X_OBJECT_ATTRIBUTES(const uint8_t* base, uint32_t p) { Read(base, p); } void Read(const uint8_t* base, uint32_t p) { root_directory = xe::load_and_swap(base + p); object_name_ptr = xe::load_and_swap(base + p + 4); if (object_name_ptr) { object_name.Read(base, object_name_ptr); } else { object_name.Zero(); } attributes = xe::load_and_swap(base + p + 8); } void Zero() { root_directory = 0; object_name_ptr = 0; object_name.Zero(); attributes = 0; } }; // http://msdn.microsoft.com/en-us/library/windows/hardware/ff540287.aspx class X_FILE_FS_VOLUME_INFORMATION { public: // FILE_FS_VOLUME_INFORMATION uint64_t creation_time; uint32_t serial_number; uint32_t label_length; uint32_t supports_objects; char label[1]; void Write(uint8_t* base, uint32_t p) { uint8_t* dst = base + p; xe::store_and_swap(dst + 0, this->creation_time); xe::store_and_swap(dst + 8, this->serial_number); xe::store_and_swap(dst + 12, this->label_length); xe::store_and_swap(dst + 16, this->supports_objects); memcpy(dst + 20, this->label, this->label_length); } }; static_assert_size(X_FILE_FS_VOLUME_INFORMATION, 24); // https://msdn.microsoft.com/en-us/library/windows/hardware/ff540282.aspx class X_FILE_FS_SIZE_INFORMATION { public: // FILE_FS_SIZE_INFORMATION uint64_t total_allocation_units; uint64_t available_allocation_units; uint32_t sectors_per_allocation_unit; uint32_t bytes_per_sector; void Write(uint8_t* base, uint32_t p) { uint8_t* dst = base + p; xe::store_and_swap(dst + 0, this->total_allocation_units); xe::store_and_swap(dst + 8, this->available_allocation_units); xe::store_and_swap(dst + 16, this->sectors_per_allocation_unit); xe::store_and_swap(dst + 20, this->bytes_per_sector); } }; static_assert_size(X_FILE_FS_SIZE_INFORMATION, 24); // http://msdn.microsoft.com/en-us/library/windows/hardware/ff540251(v=vs.85).aspx class X_FILE_FS_ATTRIBUTE_INFORMATION { public: // FILE_FS_ATTRIBUTE_INFORMATION uint32_t attributes; int32_t maximum_component_name_length; uint32_t fs_name_length; char fs_name[1]; void Write(uint8_t* base, uint32_t p) { uint8_t* dst = base + p; xe::store_and_swap(dst + 0, this->attributes); xe::store_and_swap(dst + 4, this->maximum_component_name_length); xe::store_and_swap(dst + 8, this->fs_name_length); memcpy(dst + 12, this->fs_name, this->fs_name_length); } }; static_assert_size(X_FILE_FS_ATTRIBUTE_INFORMATION, 16); X_STATUS NtCreateFile(PPCContext* ppc_context, KernelState* kernel_state, uint32_t handle_ptr, uint32_t desired_access, X_OBJECT_ATTRIBUTES* object_attrs, const char* object_name, uint32_t io_status_block_ptr, uint32_t allocation_size_ptr, uint32_t file_attributes, uint32_t share_access, FileDisposition creation_disposition) { uint64_t allocation_size = 0; // is this correct??? if (allocation_size_ptr != 0) { allocation_size = SHIM_MEM_64(allocation_size_ptr); } // Compute path, possibly attrs relative. std::string target_path = object_name; if (object_attrs->root_directory != 0xFFFFFFFD && // ObDosDevices object_attrs->root_directory != 0) { auto root_file = kernel_state->object_table()->LookupObject( object_attrs->root_directory); assert_not_null(root_file); assert_true(root_file->type() == XObject::Type::kTypeFile); // Resolve the file using the device the root directory is part of. auto device = root_file->device(); target_path = xe::join_paths( device->mount_path(), xe::join_paths(root_file->path(), object_name)); } // Attempt open (or create). object_ref file; FileAction file_action; X_STATUS result = kernel_state->file_system()->OpenFile( kernel_state, target_path, creation_disposition, desired_access, &file, &file_action); uint32_t info = uint32_t(file_action); X_HANDLE handle = X_INVALID_HANDLE_VALUE; if (XSUCCEEDED(result)) { // Handle ref is incremented, so return that. handle = file->handle(); } if (io_status_block_ptr) { SHIM_SET_MEM_32(io_status_block_ptr, result); // Status SHIM_SET_MEM_32(io_status_block_ptr + 4, info); // Information } if (XSUCCEEDED(result)) { if (handle_ptr) { SHIM_SET_MEM_32(handle_ptr, handle); } } return result; } SHIM_CALL NtCreateFile_shim(PPCContext* ppc_context, KernelState* kernel_state) { uint32_t handle_ptr = SHIM_GET_ARG_32(0); uint32_t desired_access = SHIM_GET_ARG_32(1); uint32_t object_attributes_ptr = SHIM_GET_ARG_32(2); uint32_t io_status_block_ptr = SHIM_GET_ARG_32(3); uint32_t allocation_size_ptr = SHIM_GET_ARG_32(4); uint32_t file_attributes = SHIM_GET_ARG_32(5); uint32_t share_access = SHIM_GET_ARG_32(6); uint32_t creation_disposition = SHIM_GET_ARG_32(7); X_OBJECT_ATTRIBUTES object_attrs(SHIM_MEM_BASE, object_attributes_ptr); char* object_name = object_attrs.object_name.Duplicate(); XELOGD("NtCreateFile(%.8X, %.8X, %.8X(%s), %.8X, %.8X, %.8X, %d, %d)", handle_ptr, desired_access, object_attributes_ptr, !object_name ? "(null)" : object_name, io_status_block_ptr, allocation_size_ptr, file_attributes, share_access, creation_disposition); auto result = NtCreateFile( ppc_context, kernel_state, handle_ptr, desired_access, &object_attrs, object_name, io_status_block_ptr, allocation_size_ptr, file_attributes, share_access, FileDisposition(creation_disposition)); free(object_name); SHIM_SET_RETURN_32(result); } SHIM_CALL NtOpenFile_shim(PPCContext* ppc_context, KernelState* kernel_state) { uint32_t handle_ptr = SHIM_GET_ARG_32(0); uint32_t desired_access = SHIM_GET_ARG_32(1); uint32_t object_attributes_ptr = SHIM_GET_ARG_32(2); uint32_t io_status_block_ptr = SHIM_GET_ARG_32(3); uint32_t open_options = SHIM_GET_ARG_32(4); X_OBJECT_ATTRIBUTES object_attrs(SHIM_MEM_BASE, object_attributes_ptr); char* object_name = object_attrs.object_name.Duplicate(); XELOGD("NtOpenFile(%.8X, %.8X, %.8X(%s), %.8X, %d)", handle_ptr, desired_access, object_attributes_ptr, !object_name ? "(null)" : object_name, io_status_block_ptr, open_options); auto result = NtCreateFile( ppc_context, kernel_state, handle_ptr, desired_access, &object_attrs, object_name, io_status_block_ptr, 0, 0, 0, FileDisposition::kOpen); free(object_name); SHIM_SET_RETURN_32(result); } class xeNtReadFileState { public: uint32_t x; }; void xeNtReadFileCompleted(XAsyncRequest* request, xeNtReadFileState* state) { // TODO(benvanik): set io_status_block_ptr delete request; delete state; } SHIM_CALL NtReadFile_shim(PPCContext* ppc_context, KernelState* kernel_state) { uint32_t file_handle = SHIM_GET_ARG_32(0); uint32_t event_handle = SHIM_GET_ARG_32(1); uint32_t apc_routine_ptr = SHIM_GET_ARG_32(2); uint32_t apc_context = SHIM_GET_ARG_32(3); uint32_t io_status_block_ptr = SHIM_GET_ARG_32(4); uint32_t buffer = SHIM_GET_ARG_32(5); uint32_t buffer_length = SHIM_GET_ARG_32(6); uint32_t byte_offset_ptr = SHIM_GET_ARG_32(7); size_t byte_offset = byte_offset_ptr ? SHIM_MEM_64(byte_offset_ptr) : 0; XELOGD("NtReadFile(%.8X, %.8X, %.8X, %.8X, %.8X, %.8X, %d, %.8X(%llu))", file_handle, event_handle, apc_routine_ptr, apc_context, io_status_block_ptr, buffer, buffer_length, byte_offset_ptr, byte_offset); X_STATUS result = X_STATUS_SUCCESS; uint32_t info = 0; // Grab event to signal. bool signal_event = false; auto ev = event_handle ? kernel_state->object_table()->LookupObject(event_handle) : object_ref(); if (event_handle && !ev) { result = X_STATUS_INVALID_HANDLE; } // Grab file. auto file = kernel_state->object_table()->LookupObject(file_handle); if (!file) { result = X_STATUS_INVALID_HANDLE; } // Execute read. if (XSUCCEEDED(result)) { // Reset event before we begin. if (ev) { ev->Reset(); } // TODO(benvanik): async path. if (true) { // Synchronous request. if (!byte_offset_ptr || byte_offset == 0xFFFFFFFFfffffffe) { // FILE_USE_FILE_POINTER_POSITION byte_offset = -1; } // Read now. size_t bytes_read = 0; result = file->Read(SHIM_MEM_ADDR(buffer), buffer_length, byte_offset, &bytes_read); if (XSUCCEEDED(result)) { info = (int32_t)bytes_read; } // Queue the APC callback. It must be delivered via the APC mechanism even // though were are completing immediately. if (apc_routine_ptr & ~1) { auto thread = XThread::GetCurrentThread(); thread->EnqueueApc(apc_routine_ptr & ~1, apc_context, io_status_block_ptr, 0); } // Mark that we should signal the event now. We do this after // we have written the info out. signal_event = true; } else { // X_STATUS_PENDING if not returning immediately. // XFile is waitable and signalled after each async req completes. // reset the input event (->Reset()) /*xeNtReadFileState* call_state = new xeNtReadFileState(); XAsyncRequest* request = new XAsyncRequest( state, file, (XAsyncRequest::CompletionCallback)xeNtReadFileCompleted, call_state);*/ // result = file->Read(buffer, buffer_length, byte_offset, request); result = X_STATUS_PENDING; info = 0; } } if (io_status_block_ptr) { SHIM_SET_MEM_32(io_status_block_ptr, result); // Status SHIM_SET_MEM_32(io_status_block_ptr + 4, info); // Information } if (ev && signal_event) { ev->Set(0, false); } SHIM_SET_RETURN_32(result); } SHIM_CALL NtWriteFile_shim(PPCContext* ppc_context, KernelState* kernel_state) { uint32_t file_handle = SHIM_GET_ARG_32(0); uint32_t event_handle = SHIM_GET_ARG_32(1); uint32_t apc_routine_ptr = SHIM_GET_ARG_32(2); uint32_t apc_context = SHIM_GET_ARG_32(3); uint32_t io_status_block_ptr = SHIM_GET_ARG_32(4); uint32_t buffer = SHIM_GET_ARG_32(5); uint32_t buffer_length = SHIM_GET_ARG_32(6); uint32_t byte_offset_ptr = SHIM_GET_ARG_32(7); size_t byte_offset = byte_offset_ptr ? SHIM_MEM_64(byte_offset_ptr) : 0; XELOGD("NtWriteFile(%.8X, %.8X, %.8X, %.8X, %.8X, %.8X, %d, %.8X(%d))", file_handle, event_handle, apc_routine_ptr, apc_context, io_status_block_ptr, buffer, buffer_length, byte_offset_ptr, byte_offset); // Async not supported yet. assert_zero(apc_routine_ptr); X_STATUS result = X_STATUS_SUCCESS; uint32_t info = 0; // Grab event to signal. bool signal_event = false; auto ev = event_handle ? kernel_state->object_table()->LookupObject(event_handle) : object_ref(); if (event_handle && !ev) { result = X_STATUS_INVALID_HANDLE; } // Grab file. auto file = kernel_state->object_table()->LookupObject(file_handle); if (!file) { result = X_STATUS_INVALID_HANDLE; } // Execute write. if (XSUCCEEDED(result)) { // Reset event before we begin. if (ev) { ev->Reset(); } // TODO(benvanik): async path. if (true) { // Synchronous request. if (!byte_offset_ptr || byte_offset == 0xFFFFFFFFfffffffe) { // FILE_USE_FILE_POINTER_POSITION byte_offset = -1; } // Write now. size_t bytes_written = 0; result = file->Write(SHIM_MEM_ADDR(buffer), buffer_length, byte_offset, &bytes_written); if (XSUCCEEDED(result)) { info = (int32_t)bytes_written; } // Mark that we should signal the event now. We do this after // we have written the info out. signal_event = true; } else { // X_STATUS_PENDING if not returning immediately. result = X_STATUS_PENDING; info = 0; } } if (io_status_block_ptr) { SHIM_SET_MEM_32(io_status_block_ptr, result); // Status SHIM_SET_MEM_32(io_status_block_ptr + 4, info); // Information } if (ev && signal_event) { ev->Set(0, false); } SHIM_SET_RETURN_32(result); } dword_result_t NtCreateIoCompletion(lpvoid_t out_handle, dword_t desired_access, lpvoid_t object_attribs, dword_t num_concurrent_threads) { return X_STATUS_UNSUCCESSFUL; } DECLARE_XBOXKRNL_EXPORT(NtCreateIoCompletion, ExportTag::kStub); SHIM_CALL NtSetInformationFile_shim(PPCContext* ppc_context, KernelState* kernel_state) { uint32_t file_handle = SHIM_GET_ARG_32(0); uint32_t io_status_block_ptr = SHIM_GET_ARG_32(1); uint32_t file_info_ptr = SHIM_GET_ARG_32(2); uint32_t length = SHIM_GET_ARG_32(3); uint32_t file_info_class = SHIM_GET_ARG_32(4); XELOGD("NtSetInformationFile(%.8X, %.8X, %.8X, %.8X, %.8X)", file_handle, io_status_block_ptr, file_info_ptr, length, file_info_class); X_STATUS result = X_STATUS_SUCCESS; uint32_t info = 0; // Grab file. auto file = kernel_state->object_table()->LookupObject(file_handle); if (file) { switch (file_info_class) { case XFileDispositionInformation: { // Used to set deletion flag. Which we don't support. Probably? info = 0; bool delete_on_close = SHIM_MEM_8(file_info_ptr) ? true : false; XELOGW("NtSetInformationFile ignoring delete on close: %d", delete_on_close); break; } case XFilePositionInformation: // struct FILE_POSITION_INFORMATION { // LARGE_INTEGER CurrentByteOffset; // }; assert_true(length == 8); info = 8; file->set_position(SHIM_MEM_64(file_info_ptr)); break; case XFileAllocationInformation: case XFileEndOfFileInformation: assert_true(length == 8); info = 8; XELOGW("NtSetInformationFile ignoring alloc/eof"); break; case XFileCompletionInformation: // Games appear to call NtCreateIoCompletion right before this break; default: // Unsupported, for now. assert_always(); info = 0; break; } } else { result = X_STATUS_INVALID_HANDLE; } if (io_status_block_ptr) { SHIM_SET_MEM_32(io_status_block_ptr, result); // Status SHIM_SET_MEM_32(io_status_block_ptr + 4, info); // Information } SHIM_SET_RETURN_32(result); } struct X_IO_STATUS_BLOCK { union { xe::be status; xe::be pointer; }; xe::be information; }; dword_result_t NtQueryInformationFile( dword_t file_handle, pointer_t io_status_block_ptr, lpvoid_t file_info_ptr, dword_t length, dword_t file_info_class) { X_STATUS result = X_STATUS_SUCCESS; uint32_t info = 0; // Grab file. auto file = kernel_state()->object_table()->LookupObject(file_handle); if (file) { switch (file_info_class) { case XFileInternalInformation: // Internal unique file pointer. Not sure why anyone would want this. assert_true(length == 8); info = 8; // TODO(benvanik): use pointer to fs:: entry? xe::store_and_swap(file_info_ptr, xe::memory::hash_combine(0, file->path())); break; case XFilePositionInformation: // struct FILE_POSITION_INFORMATION { // LARGE_INTEGER CurrentByteOffset; // }; assert_true(length == 8); info = 8; xe::store_and_swap(file_info_ptr, file->position()); break; case XFileNetworkOpenInformation: { // struct FILE_NETWORK_OPEN_INFORMATION { // LARGE_INTEGER CreationTime; // LARGE_INTEGER LastAccessTime; // LARGE_INTEGER LastWriteTime; // LARGE_INTEGER ChangeTime; // LARGE_INTEGER AllocationSize; // LARGE_INTEGER EndOfFile; // ULONG FileAttributes; // ULONG Unknown; // }; assert_true(length == 56); auto file_info = file_info_ptr.as(); file_info->creation_time = file->entry()->create_timestamp(); file_info->last_access_time = file->entry()->access_timestamp(); file_info->last_write_time = file->entry()->write_timestamp(); file_info->change_time = file->entry()->write_timestamp(); file_info->allocation_size = file->entry()->allocation_size(); file_info->end_of_file = file->entry()->size(); file_info->attributes = file->entry()->attributes(); info = 56; break; } case XFileXctdCompressionInformation: assert_true(length == 4); /* // This is wrong and puts files into wrong states for games that use // XctdDecompression. uint32_t magic; size_t bytes_read; result = file->Read(&magic, sizeof(magic), 0, &bytes_read); if (XSUCCEEDED(result)) { if (bytes_read == sizeof(magic)) { info = 4; SHIM_SET_MEM_32(file_info_ptr, magic == xe::byte_swap(0x0FF512ED) ? 1 : 0); } else { result = X_STATUS_UNSUCCESSFUL; } } */ result = X_STATUS_UNSUCCESSFUL; break; case XFileSectorInformation: // TODO: Return sector this file's on. assert_true(length == 4); result = X_STATUS_UNSUCCESSFUL; info = 0; break; default: // Unsupported, for now. assert_always(); info = 0; result = X_STATUS_UNSUCCESSFUL; break; } } else { result = X_STATUS_INVALID_HANDLE; } if (io_status_block_ptr) { io_status_block_ptr->status = result; io_status_block_ptr->information = info; // # bytes written } return result; } DECLARE_XBOXKRNL_EXPORT(NtQueryInformationFile, ExportTag::kImplemented | ExportTag::kFileSystem); SHIM_CALL NtQueryFullAttributesFile_shim(PPCContext* ppc_context, KernelState* kernel_state) { uint32_t object_attributes_ptr = SHIM_GET_ARG_32(0); uint32_t file_info_ptr = SHIM_GET_ARG_32(1); X_OBJECT_ATTRIBUTES attrs(SHIM_MEM_BASE, object_attributes_ptr); char* object_name = attrs.object_name.Duplicate(); XELOGD("NtQueryFullAttributesFile(%.8X(%s), %.8X)", object_attributes_ptr, !object_name ? "(null)" : object_name, file_info_ptr); X_STATUS result = X_STATUS_NO_SUCH_FILE; object_ref root_file; if (attrs.root_directory != 0xFFFFFFFD && // ObDosDevices attrs.root_directory != 0) { root_file = kernel_state->object_table()->LookupObject(attrs.root_directory); assert_not_null(root_file); assert_true(root_file->type() == XObject::Type::kTypeFile); assert_always(); } // Resolve the file using the virtual file system. auto fs = kernel_state->file_system(); auto entry = fs->ResolvePath(object_name); if (entry) { // Found. result = X_STATUS_SUCCESS; auto file_info = kernel_memory()->TranslateVirtual( file_info_ptr); file_info->creation_time = entry->create_timestamp(); file_info->last_access_time = entry->access_timestamp(); file_info->last_write_time = entry->write_timestamp(); file_info->change_time = entry->write_timestamp(); file_info->allocation_size = entry->allocation_size(); file_info->end_of_file = entry->size(); file_info->attributes = entry->attributes(); } free(object_name); SHIM_SET_RETURN_32(result); } SHIM_CALL NtQueryVolumeInformationFile_shim(PPCContext* ppc_context, KernelState* kernel_state) { uint32_t file_handle = SHIM_GET_ARG_32(0); uint32_t io_status_block_ptr = SHIM_GET_ARG_32(1); uint32_t fs_info_ptr = SHIM_GET_ARG_32(2); uint32_t length = SHIM_GET_ARG_32(3); uint32_t fs_info_class = SHIM_GET_ARG_32(4); XELOGD("NtQueryVolumeInformationFile(%.8X, %.8X, %.8X, %.8X, %.8X)", file_handle, io_status_block_ptr, fs_info_ptr, length, fs_info_class); X_STATUS result = X_STATUS_SUCCESS; uint32_t info = 0; // Grab file. auto file = kernel_state->object_table()->LookupObject(file_handle); if (file) { switch (fs_info_class) { case 1: { // FileFsVolumeInformation // TODO(gibbed): actual value std::string name = "test"; X_FILE_FS_VOLUME_INFORMATION volume_info = {0}; volume_info.creation_time = 0; volume_info.serial_number = 12345678; volume_info.supports_objects = 0; volume_info.label_length = uint32_t(name.size()); std::memcpy(volume_info.label, name.data(), name.size()); volume_info.Write(SHIM_MEM_BASE, fs_info_ptr); info = length; break; } case 3: { // FileFsSizeInformation X_FILE_FS_SIZE_INFORMATION fs_size_info = {0}; fs_size_info.total_allocation_units = file->device()->total_allocation_units(); fs_size_info.available_allocation_units = file->device()->available_allocation_units(); fs_size_info.sectors_per_allocation_unit = file->device()->sectors_per_allocation_unit(); fs_size_info.bytes_per_sector = file->device()->bytes_per_sector(); fs_size_info.Write(SHIM_MEM_BASE, fs_info_ptr); info = length; break; } case 5: { // FileFsAttributeInformation // TODO(gibbed): actual value std::string name = "test"; X_FILE_FS_ATTRIBUTE_INFORMATION fs_attribute_info = {0}; fs_attribute_info.attributes = 0; fs_attribute_info.maximum_component_name_length = 255; fs_attribute_info.fs_name_length = uint32_t(name.size()); std::memcpy(fs_attribute_info.fs_name, name.data(), name.size()); fs_attribute_info.Write(SHIM_MEM_BASE, fs_info_ptr); info = length; break; } case 2: // FileFsLabelInformation case 4: // FileFsDeviceInformation case 6: // FileFsControlInformation case 7: // FileFsFullSizeInformation case 8: // FileFsObjectIdInformation default: // Unsupported, for now. assert_always(); info = 0; break; } } else { result = X_STATUS_NO_SUCH_FILE; } if (XFAILED(result)) { info = 0; } if (io_status_block_ptr) { SHIM_SET_MEM_32(io_status_block_ptr, result); // Status SHIM_SET_MEM_32(io_status_block_ptr + 4, info); // Information } SHIM_SET_RETURN_32(result); } SHIM_CALL NtQueryDirectoryFile_shim(PPCContext* ppc_context, KernelState* kernel_state) { uint32_t file_handle = SHIM_GET_ARG_32(0); uint32_t event_handle = SHIM_GET_ARG_32(1); uint32_t apc_routine = SHIM_GET_ARG_32(2); uint32_t apc_context = SHIM_GET_ARG_32(3); uint32_t io_status_block_ptr = SHIM_GET_ARG_32(4); uint32_t file_info_ptr = SHIM_GET_ARG_32(5); uint32_t length = SHIM_GET_ARG_32(6); uint32_t file_name_ptr = SHIM_GET_ARG_32(7); uint32_t restart_scan = SHIM_GET_ARG_32(8); auto file_name = X_ANSI_STRING::to_string(SHIM_MEM_BASE, file_name_ptr); XELOGD( "NtQueryDirectoryFile(%.8X, %.8X, %.8X, %.8X, %.8X, %.8X, %d, %.8X(%s), " "%d)", file_handle, event_handle, apc_routine, apc_context, io_status_block_ptr, file_info_ptr, length, file_name_ptr, !file_name.empty() ? file_name.c_str() : "(null)", restart_scan); if (length < 72) { SHIM_SET_RETURN_32(X_STATUS_INFO_LENGTH_MISMATCH); return; } X_STATUS result = X_STATUS_UNSUCCESSFUL; uint32_t info = 0; auto file = kernel_state->object_table()->LookupObject(file_handle); if (file) { X_FILE_DIRECTORY_INFORMATION dir_info = {0}; result = file->QueryDirectory( &dir_info, length, !file_name.empty() ? file_name.c_str() : nullptr, restart_scan != 0); if (XSUCCEEDED(result)) { dir_info.Write(SHIM_MEM_BASE, file_info_ptr); info = length; } } else { result = X_STATUS_NO_SUCH_FILE; } if (XFAILED(result)) { info = 0; } if (io_status_block_ptr) { SHIM_SET_MEM_32(io_status_block_ptr, result); // Status SHIM_SET_MEM_32(io_status_block_ptr + 4, info); // Information } SHIM_SET_RETURN_32(result); } SHIM_CALL NtFlushBuffersFile_shim(PPCContext* ppc_context, KernelState* kernel_state) { uint32_t file_handle = SHIM_GET_ARG_32(0); uint32_t io_status_block_ptr = SHIM_GET_ARG_32(1); XELOGD("NtFlushBuffersFile(%.8X, %.8X)", file_handle, io_status_block_ptr); auto result = X_STATUS_SUCCESS; if (io_status_block_ptr) { SHIM_SET_MEM_32(io_status_block_ptr, result); // Status SHIM_SET_MEM_32(io_status_block_ptr + 4, 0); // Information } SHIM_SET_RETURN_32(result); } dword_result_t FscGetCacheElementCount(dword_t r3) { return 0; } DECLARE_XBOXKRNL_EXPORT(FscGetCacheElementCount, ExportTag::kStub); SHIM_CALL FscSetCacheElementCount_shim(PPCContext* ppc_context, KernelState* kernel_state) { uint32_t unk_0 = SHIM_GET_ARG_32(0); uint32_t unk_1 = SHIM_GET_ARG_32(1); // unk_0 = 0 // unk_1 looks like a count? in what units? 256 is a common value XELOGD("FscSetCacheElementCount(%.8X, %.8X)", unk_0, unk_1); SHIM_SET_RETURN_32(X_STATUS_SUCCESS); } } // namespace kernel } // namespace xe void xe::kernel::xboxkrnl::RegisterIoExports( xe::cpu::ExportResolver* export_resolver, KernelState* kernel_state) { SHIM_SET_MAPPING("xboxkrnl.exe", NtCreateFile, state); SHIM_SET_MAPPING("xboxkrnl.exe", NtOpenFile, state); SHIM_SET_MAPPING("xboxkrnl.exe", NtReadFile, state); SHIM_SET_MAPPING("xboxkrnl.exe", NtWriteFile, state); SHIM_SET_MAPPING("xboxkrnl.exe", NtSetInformationFile, state); SHIM_SET_MAPPING("xboxkrnl.exe", NtQueryFullAttributesFile, state); SHIM_SET_MAPPING("xboxkrnl.exe", NtQueryVolumeInformationFile, state); SHIM_SET_MAPPING("xboxkrnl.exe", NtQueryDirectoryFile, state); SHIM_SET_MAPPING("xboxkrnl.exe", NtFlushBuffersFile, state); SHIM_SET_MAPPING("xboxkrnl.exe", FscSetCacheElementCount, state); }