559 lines
15 KiB
C++
559 lines
15 KiB
C++
/**
|
|
******************************************************************************
|
|
* 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/kernel/xboxkrnl_io.h>
|
|
|
|
#include <xenia/kernel/async_request.h>
|
|
#include <xenia/kernel/kernel_state.h>
|
|
#include <xenia/kernel/xboxkrnl_private.h>
|
|
#include <xenia/kernel/objects/xevent.h>
|
|
#include <xenia/kernel/objects/xfile.h>
|
|
#include <xenia/kernel/util/shim_utils.h>
|
|
|
|
|
|
using namespace xe;
|
|
using namespace xe::kernel;
|
|
using namespace xe::kernel::fs;
|
|
|
|
|
|
namespace xe {
|
|
namespace kernel {
|
|
|
|
|
|
SHIM_CALL NtCreateFile_shim(
|
|
PPCContext* ppc_state, KernelState* 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 attrs(SHIM_MEM_BASE, object_attributes_ptr);
|
|
|
|
XELOGD(
|
|
"NtCreateFile(%.8X, %.8X, %.8X(%s), %.8X, %.8X, %.8X, %d, %d)",
|
|
handle_ptr,
|
|
desired_access,
|
|
object_attributes_ptr,
|
|
attrs.object_name.buffer,
|
|
io_status_block_ptr,
|
|
allocation_size_ptr,
|
|
file_attributes,
|
|
share_access,
|
|
creation_disposition);
|
|
|
|
uint64_t allocation_size = 0; // is this correct???
|
|
if (allocation_size_ptr != 0) {
|
|
allocation_size = SHIM_MEM_64(allocation_size_ptr);
|
|
}
|
|
|
|
X_STATUS result = X_STATUS_NO_SUCH_FILE;
|
|
uint32_t info = X_FILE_DOES_NOT_EXIST;
|
|
uint32_t handle;
|
|
|
|
// Resolve the file using the virtual file system.
|
|
FileSystem* fs = state->file_system();
|
|
Entry* entry = fs->ResolvePath(attrs.object_name.buffer);
|
|
XFile* file = NULL;
|
|
if (entry && entry->type() == Entry::kTypeFile) {
|
|
// Open the file.
|
|
result = entry->Open(
|
|
state,
|
|
desired_access,
|
|
false, // TODO(benvanik): pick async mode, if needed.
|
|
&file);
|
|
} else {
|
|
result = X_STATUS_NO_SUCH_FILE;
|
|
info = X_FILE_DOES_NOT_EXIST;
|
|
}
|
|
|
|
if (XSUCCEEDED(result)) {
|
|
// Handle ref is incremented, so return that.
|
|
handle = file->handle();
|
|
file->Release();
|
|
result = X_STATUS_SUCCESS;
|
|
info = X_FILE_OPENED;
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
SHIM_SET_RETURN(result);
|
|
}
|
|
|
|
SHIM_CALL NtOpenFile_shim(
|
|
PPCContext* ppc_state, KernelState* 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 attrs(SHIM_MEM_BASE, object_attributes_ptr);
|
|
|
|
XELOGD(
|
|
"NtOpenFile(%.8X, %.8X, %.8X(%s), %.8X, %d)",
|
|
handle_ptr,
|
|
desired_access,
|
|
object_attributes_ptr,
|
|
attrs.object_name.buffer,
|
|
io_status_block_ptr,
|
|
open_options);
|
|
|
|
X_STATUS result = X_STATUS_NO_SUCH_FILE;
|
|
uint32_t info = X_FILE_DOES_NOT_EXIST;
|
|
uint32_t handle;
|
|
|
|
// Resolve the file using the virtual file system.
|
|
FileSystem* fs = state->file_system();
|
|
Entry* entry = fs->ResolvePath(attrs.object_name.buffer);
|
|
XFile* file = NULL;
|
|
if (entry && entry->type() == Entry::kTypeFile) {
|
|
// Open the file.
|
|
result = entry->Open(
|
|
state,
|
|
desired_access,
|
|
false, // TODO(benvanik): pick async mode, if needed.
|
|
&file);
|
|
}
|
|
else {
|
|
result = X_STATUS_NO_SUCH_FILE;
|
|
info = X_FILE_DOES_NOT_EXIST;
|
|
}
|
|
|
|
if (XSUCCEEDED(result)) {
|
|
// Handle ref is incremented, so return that.
|
|
handle = file->handle();
|
|
file->Release();
|
|
result = X_STATUS_SUCCESS;
|
|
info = X_FILE_OPENED;
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
SHIM_SET_RETURN(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_state, KernelState* 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(%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.
|
|
XEASSERTNULL(apc_routine_ptr);
|
|
|
|
X_STATUS result = X_STATUS_SUCCESS;
|
|
uint32_t info = 0;
|
|
|
|
// Grab event to signal.
|
|
XEvent* ev = NULL;
|
|
bool signal_event = false;
|
|
if (event_handle) {
|
|
result = state->object_table()->GetObject(
|
|
event_handle, (XObject**)&ev);
|
|
}
|
|
|
|
// Grab file.
|
|
XFile* file = NULL;
|
|
if (XSUCCEEDED(result)) {
|
|
result = state->object_table()->GetObject(
|
|
file_handle, (XObject**)&file);
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
// 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 (file) {
|
|
file->Release();
|
|
}
|
|
if (ev) {
|
|
if (signal_event) {
|
|
ev->Set(0, false);
|
|
}
|
|
ev->Release();
|
|
}
|
|
|
|
SHIM_SET_RETURN(result);
|
|
}
|
|
|
|
SHIM_CALL NtSetInformationFile_shim(
|
|
PPCContext* ppc_state, KernelState* 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.
|
|
XFile* file = NULL;
|
|
result = state->object_table()->GetObject(
|
|
file_handle, (XObject**)&file);
|
|
|
|
if (XSUCCEEDED(result)) {
|
|
result = X_STATUS_SUCCESS;
|
|
switch (file_info_class) {
|
|
case XFilePositionInformation:
|
|
// struct FILE_POSITION_INFORMATION {
|
|
// LARGE_INTEGER CurrentByteOffset;
|
|
// };
|
|
XEASSERT(length == 8);
|
|
info = 8;
|
|
file->set_position(SHIM_MEM_64(file_info_ptr));
|
|
break;
|
|
default:
|
|
// Unsupported, for now.
|
|
XEASSERTALWAYS();
|
|
info = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
if (file) {
|
|
file->Release();
|
|
}
|
|
|
|
SHIM_SET_RETURN(result);
|
|
}
|
|
|
|
SHIM_CALL NtQueryInformationFile_shim(
|
|
PPCContext* ppc_state, KernelState* 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(
|
|
"NtQueryInformationFile(%.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.
|
|
XFile* file = NULL;
|
|
result = state->object_table()->GetObject(
|
|
file_handle, (XObject**)&file);
|
|
|
|
if (XSUCCEEDED(result)) {
|
|
result = X_STATUS_SUCCESS;
|
|
switch (file_info_class) {
|
|
case XFilePositionInformation:
|
|
// struct FILE_POSITION_INFORMATION {
|
|
// LARGE_INTEGER CurrentByteOffset;
|
|
// };
|
|
XEASSERT(length == 8);
|
|
info = 8;
|
|
SHIM_SET_MEM_64(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;
|
|
// };
|
|
XEASSERT(length == 56);
|
|
XFileInfo file_info;
|
|
result = file->QueryInfo(&file_info);
|
|
if (XSUCCEEDED(result)) {
|
|
info = 56;
|
|
file_info.Write(SHIM_MEM_BASE, file_info_ptr);
|
|
}
|
|
break;
|
|
case XFileMailslotSetInformation:
|
|
// Read timeout.
|
|
if (length == 4) {
|
|
info = 4; // ? it's not a read.
|
|
uint32_t read_timeout = SHIM_MEM_32(file_info_ptr);
|
|
// May be INFINITE.
|
|
// Do something with this?
|
|
} else {
|
|
result = X_STATUS_INFO_LENGTH_MISMATCH;
|
|
}
|
|
break;
|
|
default:
|
|
// Unsupported, for now.
|
|
XEASSERTALWAYS();
|
|
info = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
if (file) {
|
|
file->Release();
|
|
}
|
|
|
|
SHIM_SET_RETURN(result);
|
|
}
|
|
|
|
SHIM_CALL NtQueryFullAttributesFile_shim(
|
|
PPCContext* ppc_state, KernelState* 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);
|
|
|
|
XELOGD(
|
|
"NtQueryFullAttributesFile(%.8X(%s), %.8X)",
|
|
object_attributes_ptr,
|
|
attrs.object_name.buffer,
|
|
file_info_ptr);
|
|
|
|
X_STATUS result = X_STATUS_NO_SUCH_FILE;
|
|
|
|
// Resolve the file using the virtual file system.
|
|
FileSystem* fs = state->file_system();
|
|
Entry* entry = fs->ResolvePath(attrs.object_name.buffer);
|
|
if (entry && entry->type() == Entry::kTypeFile) {
|
|
// Found.
|
|
XFileInfo file_info;
|
|
result = entry->QueryInfo(&file_info);
|
|
if (XSUCCEEDED(result)) {
|
|
file_info.Write(SHIM_MEM_BASE, file_info_ptr);
|
|
}
|
|
}
|
|
|
|
SHIM_SET_RETURN(result);
|
|
}
|
|
|
|
SHIM_CALL NtQueryVolumeInformationFile_shim(
|
|
PPCContext* ppc_state, KernelState* state) {
|
|
// TODO(benvanik): see if this is used.
|
|
XELOGD(
|
|
"NtQueryVolumeInformationFile(?)");
|
|
|
|
XEASSERTALWAYS();
|
|
SHIM_SET_RETURN(X_STATUS_NO_SUCH_FILE);
|
|
}
|
|
|
|
SHIM_CALL NtQueryDirectoryFile_shim(
|
|
PPCContext* ppc_state, KernelState* 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);
|
|
|
|
XELOGD(
|
|
"NtQueryDirectoryFile(%.8X, %.8X, %.8X, %.8X, %.8X, %.8X, %d, %.8X)",
|
|
file_handle,
|
|
event_handle,
|
|
apc_routine,
|
|
apc_context,
|
|
io_status_block_ptr,
|
|
file_info_ptr,
|
|
length,
|
|
file_name_ptr);
|
|
|
|
if (length < 72) {
|
|
SHIM_SET_RETURN(X_STATUS_INFO_LENGTH_MISMATCH);
|
|
return;
|
|
}
|
|
|
|
if (file_name_ptr != 0) {
|
|
if (SHIM_MEM_16(file_name_ptr + 0) != 0 ||
|
|
SHIM_MEM_16(file_name_ptr + 2) != 0) {
|
|
const char* file_name = (const char *)SHIM_MEM_ADDR(SHIM_MEM_32(file_name_ptr + 4));
|
|
XEASSERT(strcmp(file_name, "*.*") == 0);
|
|
}
|
|
}
|
|
|
|
X_STATUS result = X_STATUS_UNSUCCESSFUL;
|
|
uint32_t info = 0;
|
|
|
|
XFile* file = NULL;
|
|
result = state->object_table()->GetObject(
|
|
file_handle, (XObject**)&file);
|
|
if (XSUCCEEDED(result)) {
|
|
XDirectoryInfo* dirInfo = (XDirectoryInfo*)xe_malloc(length);
|
|
result = file->QueryDirectory(dirInfo, length, restart_scan != 0);
|
|
if (XSUCCEEDED(result)) {
|
|
dirInfo->Write(SHIM_MEM_BASE, file_info_ptr);
|
|
info = length;
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
if (file) {
|
|
file->Release();
|
|
}
|
|
|
|
SHIM_SET_RETURN(result);
|
|
}
|
|
|
|
SHIM_CALL FscSetCacheElementCount_shim(
|
|
PPCContext* ppc_state, KernelState* 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(X_STATUS_SUCCESS);
|
|
}
|
|
|
|
|
|
} // namespace kernel
|
|
} // namespace xe
|
|
|
|
|
|
void xe::kernel::xboxkrnl::RegisterIoExports(
|
|
ExportResolver* export_resolver, KernelState* 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", NtQueryInformationFile, 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", FscSetCacheElementCount, state);
|
|
}
|