Shuffling filesystem to xboxkrnl.
This doesn't really change anything yet, just moves things. Also fixed some bad over-including.
This commit is contained in:
@@ -10,6 +10,7 @@
|
||||
#include <xenia/kernel/modules/xam/xam_info.h>
|
||||
|
||||
#include <xenia/kernel/shim_utils.h>
|
||||
#include <xenia/kernel/xex2.h>
|
||||
#include <xenia/kernel/modules/xam/xam_private.h>
|
||||
#include <xenia/kernel/modules/xam/xam_state.h>
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ namespace kernel {
|
||||
namespace xboxkrnl {
|
||||
|
||||
class XEvent;
|
||||
class XObject;
|
||||
|
||||
|
||||
class XAsyncRequest {
|
||||
|
||||
28
src/xenia/kernel/modules/xboxkrnl/fs/device.cc
Normal file
28
src/xenia/kernel/modules/xboxkrnl/fs/device.cc
Normal file
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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/modules/xboxkrnl/fs/device.h>
|
||||
|
||||
|
||||
using namespace xe;
|
||||
using namespace xe::kernel;
|
||||
using namespace xe::kernel::xboxkrnl::fs;
|
||||
|
||||
|
||||
Device::Device(const char* path) {
|
||||
path_ = xestrdupa(path);
|
||||
}
|
||||
|
||||
Device::~Device() {
|
||||
xe_free(path_);
|
||||
}
|
||||
|
||||
const char* Device::path() {
|
||||
return path_;
|
||||
}
|
||||
45
src/xenia/kernel/modules/xboxkrnl/fs/device.h
Normal file
45
src/xenia/kernel/modules/xboxkrnl/fs/device.h
Normal file
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_KERNEL_MODULES_XBOXKRNL_FS_DEVICE_H_
|
||||
#define XENIA_KERNEL_MODULES_XBOXKRNL_FS_DEVICE_H_
|
||||
|
||||
#include <xenia/common.h>
|
||||
#include <xenia/core.h>
|
||||
|
||||
#include <xenia/kernel/modules/xboxkrnl/fs/entry.h>
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace kernel {
|
||||
namespace xboxkrnl {
|
||||
namespace fs {
|
||||
|
||||
|
||||
class Device {
|
||||
public:
|
||||
Device(const char* path);
|
||||
virtual ~Device();
|
||||
|
||||
const char* path();
|
||||
|
||||
virtual Entry* ResolvePath(const char* path) = 0;
|
||||
|
||||
protected:
|
||||
char* path_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace fs
|
||||
} // namespace xboxkrnl
|
||||
} // namespace kernel
|
||||
} // namespace xe
|
||||
|
||||
|
||||
#endif // XENIA_KERNEL_MODULES_XBOXKRNL_FS_DEVICE_H_
|
||||
@@ -0,0 +1,156 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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/modules/xboxkrnl/fs/devices/disc_image_device.h>
|
||||
|
||||
#include <xenia/kernel/modules/xboxkrnl/fs/gdfx.h>
|
||||
|
||||
|
||||
using namespace xe;
|
||||
using namespace xe::kernel;
|
||||
using namespace xe::kernel::xboxkrnl::fs;
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
|
||||
class DiscImageMemoryMapping : public MemoryMapping {
|
||||
public:
|
||||
DiscImageMemoryMapping(uint8_t* address, size_t length, xe_mmap_ref mmap) :
|
||||
MemoryMapping(address, length) {
|
||||
mmap_ = xe_mmap_retain(mmap);
|
||||
}
|
||||
|
||||
virtual ~DiscImageMemoryMapping() {
|
||||
xe_mmap_release(mmap_);
|
||||
}
|
||||
|
||||
private:
|
||||
xe_mmap_ref mmap_;
|
||||
};
|
||||
|
||||
|
||||
class DiscImageFileEntry : public FileEntry {
|
||||
public:
|
||||
DiscImageFileEntry(Device* device, const char* path,
|
||||
xe_mmap_ref mmap, GDFXEntry* gdfx_entry) :
|
||||
FileEntry(device, path),
|
||||
gdfx_entry_(gdfx_entry) {
|
||||
mmap_ = xe_mmap_retain(mmap);
|
||||
}
|
||||
|
||||
virtual ~DiscImageFileEntry() {
|
||||
xe_mmap_release(mmap_);
|
||||
}
|
||||
|
||||
virtual MemoryMapping* CreateMemoryMapping(
|
||||
xe_file_mode file_mode, const size_t offset, const size_t length) {
|
||||
if (file_mode & kXEFileModeWrite) {
|
||||
// Only allow reads.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t real_offset = gdfx_entry_->offset + offset;
|
||||
size_t real_length = length ?
|
||||
MIN(length, gdfx_entry_->size) : gdfx_entry_->size;
|
||||
return new DiscImageMemoryMapping(
|
||||
xe_mmap_get_addr(mmap_) + real_offset,
|
||||
real_length,
|
||||
mmap_);
|
||||
}
|
||||
|
||||
private:
|
||||
xe_mmap_ref mmap_;
|
||||
GDFXEntry* gdfx_entry_;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
DiscImageDevice::DiscImageDevice(const char* path, const xechar_t* local_path) :
|
||||
Device(path) {
|
||||
local_path_ = xestrdup(local_path);
|
||||
mmap_ = NULL;
|
||||
gdfx_ = NULL;
|
||||
}
|
||||
|
||||
DiscImageDevice::~DiscImageDevice() {
|
||||
delete gdfx_;
|
||||
xe_mmap_release(mmap_);
|
||||
xe_free(local_path_);
|
||||
}
|
||||
|
||||
int DiscImageDevice::Init() {
|
||||
mmap_ = xe_mmap_open(kXEFileModeRead, local_path_, 0, 0);
|
||||
if (!mmap_) {
|
||||
XELOGE("Disc image could not be mapped");
|
||||
return 1;
|
||||
}
|
||||
|
||||
gdfx_ = new GDFX(mmap_);
|
||||
GDFX::Error error = gdfx_->Load();
|
||||
if (error != GDFX::kSuccess) {
|
||||
XELOGE("GDFX init failed: %d", error);
|
||||
return 1;
|
||||
}
|
||||
|
||||
//gdfx_->Dump();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Entry* DiscImageDevice::ResolvePath(const char* path) {
|
||||
// The filesystem will have stripped our prefix off already, so the path will
|
||||
// be in the form:
|
||||
// some\PATH.foo
|
||||
|
||||
XELOGFS("DiscImageDevice::ResolvePath(%s)", path);
|
||||
|
||||
GDFXEntry* gdfx_entry = gdfx_->root_entry();
|
||||
|
||||
// Walk the path, one separator at a time.
|
||||
// We copy it into the buffer and shift it left over and over.
|
||||
char remaining[XE_MAX_PATH];
|
||||
XEIGNORE(xestrcpya(remaining, XECOUNT(remaining), path));
|
||||
while (remaining[0]) {
|
||||
char* next_slash = xestrchra(remaining, '\\');
|
||||
if (next_slash == remaining) {
|
||||
// Leading slash - shift
|
||||
XEIGNORE(xestrcpya(remaining, XECOUNT(remaining), remaining + 1));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Make the buffer just the name.
|
||||
if (next_slash) {
|
||||
*next_slash = 0;
|
||||
}
|
||||
|
||||
// Look up in the entry.
|
||||
gdfx_entry = gdfx_entry->GetChild(remaining);
|
||||
if (!gdfx_entry) {
|
||||
// Not found.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Shift the buffer down, unless we are at the end.
|
||||
if (!next_slash) {
|
||||
break;
|
||||
}
|
||||
XEIGNORE(xestrcpya(remaining, XECOUNT(remaining), next_slash + 1));
|
||||
}
|
||||
|
||||
if (gdfx_entry->attributes & GDFXEntry::kAttrFolder) {
|
||||
//return new DiscImageDirectoryEntry(mmap_, gdfx_entry);
|
||||
XEASSERTALWAYS();
|
||||
return NULL;
|
||||
} else {
|
||||
return new DiscImageFileEntry(this, path, mmap_, gdfx_entry);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_KERNEL_MODULES_XBOXKRNL_FS_DEVICES_DISC_IMAGE_DEVICE_H_
|
||||
#define XENIA_KERNEL_MODULES_XBOXKRNL_FS_DEVICES_DISC_IMAGE_DEVICE_H_
|
||||
|
||||
#include <xenia/common.h>
|
||||
#include <xenia/core.h>
|
||||
|
||||
#include <xenia/kernel/modules/xboxkrnl/fs/device.h>
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace kernel {
|
||||
namespace xboxkrnl {
|
||||
namespace fs {
|
||||
|
||||
|
||||
class GDFX;
|
||||
|
||||
|
||||
class DiscImageDevice : public Device {
|
||||
public:
|
||||
DiscImageDevice(const char* path, const xechar_t* local_path);
|
||||
virtual ~DiscImageDevice();
|
||||
|
||||
int Init();
|
||||
|
||||
virtual Entry* ResolvePath(const char* path);
|
||||
|
||||
private:
|
||||
xechar_t* local_path_;
|
||||
xe_mmap_ref mmap_;
|
||||
GDFX* gdfx_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace fs
|
||||
} // namespace xboxkrnl
|
||||
} // namespace kernel
|
||||
} // namespace xe
|
||||
|
||||
|
||||
#endif // XENIA_KERNEL_MODULES_XBOXKRNL_FS_DEVICES_DISC_IMAGE_DEVICE_H_
|
||||
@@ -0,0 +1,115 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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/modules/xboxkrnl/fs/devices/local_directory_device.h>
|
||||
|
||||
|
||||
using namespace xe;
|
||||
using namespace xe::kernel;
|
||||
using namespace xe::kernel::xboxkrnl::fs;
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
|
||||
class LocalFileMemoryMapping : public MemoryMapping {
|
||||
public:
|
||||
LocalFileMemoryMapping(uint8_t* address, size_t length, xe_mmap_ref mmap) :
|
||||
MemoryMapping(address, length) {
|
||||
mmap_ = xe_mmap_retain(mmap);
|
||||
}
|
||||
virtual ~LocalFileMemoryMapping() {
|
||||
xe_mmap_release(mmap_);
|
||||
}
|
||||
private:
|
||||
xe_mmap_ref mmap_;
|
||||
};
|
||||
|
||||
|
||||
class LocalFileEntry : public FileEntry {
|
||||
public:
|
||||
LocalFileEntry(Device* device, const char* path, const xechar_t* local_path) :
|
||||
FileEntry(device, path) {
|
||||
local_path_ = xestrdup(local_path);
|
||||
}
|
||||
virtual ~LocalFileEntry() {
|
||||
xe_free(local_path_);
|
||||
}
|
||||
|
||||
const xechar_t* local_path() { return local_path_; }
|
||||
|
||||
virtual MemoryMapping* CreateMemoryMapping(
|
||||
xe_file_mode file_mode, const size_t offset, const size_t length) {
|
||||
xe_mmap_ref mmap = xe_mmap_open(file_mode, local_path_, offset, length);
|
||||
if (!mmap) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
LocalFileMemoryMapping* lfmm = new LocalFileMemoryMapping(
|
||||
(uint8_t*)xe_mmap_get_addr(mmap), xe_mmap_get_length(mmap),
|
||||
mmap);
|
||||
xe_mmap_release(mmap);
|
||||
|
||||
return lfmm;
|
||||
}
|
||||
|
||||
private:
|
||||
xechar_t* local_path_;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
LocalDirectoryDevice::LocalDirectoryDevice(const char* path,
|
||||
const xechar_t* local_path) :
|
||||
Device(path) {
|
||||
local_path_ = xestrdup(local_path);
|
||||
}
|
||||
|
||||
LocalDirectoryDevice::~LocalDirectoryDevice() {
|
||||
xe_free(local_path_);
|
||||
}
|
||||
|
||||
Entry* LocalDirectoryDevice::ResolvePath(const char* path) {
|
||||
// The filesystem will have stripped our prefix off already, so the path will
|
||||
// be in the form:
|
||||
// some\PATH.foo
|
||||
|
||||
XELOGFS("LocalDirectoryDevice::ResolvePath(%s)", path);
|
||||
|
||||
#if XE_WCHAR
|
||||
xechar_t rel_path[XE_MAX_PATH];
|
||||
XEIGNORE(xestrwiden(rel_path, XECOUNT(rel_path), path));
|
||||
#else
|
||||
const xechar_t* rel_path = path;
|
||||
#endif
|
||||
|
||||
xechar_t full_path[XE_MAX_PATH];
|
||||
xe_path_join(local_path_, rel_path, full_path, XECOUNT(full_path));
|
||||
|
||||
// Swap around path separators.
|
||||
if (XE_PATH_SEPARATOR != '\\') {
|
||||
for (size_t n = 0; n < XECOUNT(full_path); n++) {
|
||||
if (full_path[n] == 0) {
|
||||
break;
|
||||
}
|
||||
if (full_path[n] == '\\') {
|
||||
full_path[n] = XE_PATH_SEPARATOR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(benvanik): get file info
|
||||
// TODO(benvanik): fail if does not exit
|
||||
// TODO(benvanik): switch based on type
|
||||
|
||||
LocalFileEntry* file_entry = new LocalFileEntry(this, path, full_path);
|
||||
return file_entry;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_KERNEL_MODULES_XBOXKRNL_FS_DEVICES_LOCAL_DIRECTORY_DEVICE_H_
|
||||
#define XENIA_KERNEL_MODULES_XBOXKRNL_FS_DEVICES_LOCAL_DIRECTORY_DEVICE_H_
|
||||
|
||||
#include <xenia/common.h>
|
||||
#include <xenia/core.h>
|
||||
|
||||
#include <xenia/kernel/modules/xboxkrnl/fs/device.h>
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace kernel {
|
||||
namespace xboxkrnl {
|
||||
namespace fs {
|
||||
|
||||
|
||||
class LocalDirectoryDevice : public Device {
|
||||
public:
|
||||
LocalDirectoryDevice(const char* path, const xechar_t* local_path);
|
||||
virtual ~LocalDirectoryDevice();
|
||||
|
||||
virtual Entry* ResolvePath(const char* path);
|
||||
|
||||
private:
|
||||
xechar_t* local_path_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace fs
|
||||
} // namespace xboxkrnl
|
||||
} // namespace kernel
|
||||
} // namespace xe
|
||||
|
||||
|
||||
#endif // XENIA_KERNEL_MODULES_XBOXKRNL_FS_DEVICES_LOCAL_DIRECTORY_DEVICE_H_
|
||||
@@ -0,0 +1,9 @@
|
||||
# Copyright 2013 Ben Vanik. All Rights Reserved.
|
||||
{
|
||||
'sources': [
|
||||
'disc_image_device.cc',
|
||||
'disc_image_device.h',
|
||||
'local_directory_device.cc',
|
||||
'local_directory_device.h',
|
||||
],
|
||||
}
|
||||
77
src/xenia/kernel/modules/xboxkrnl/fs/entry.cc
Normal file
77
src/xenia/kernel/modules/xboxkrnl/fs/entry.cc
Normal file
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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/modules/xboxkrnl/fs/entry.h>
|
||||
|
||||
|
||||
using namespace xe;
|
||||
using namespace xe::kernel;
|
||||
using namespace xe::kernel::xboxkrnl::fs;
|
||||
|
||||
|
||||
Entry::Entry(Type type, Device* device, const char* path) :
|
||||
type_(type),
|
||||
device_(device) {
|
||||
path_ = xestrdupa(path);
|
||||
// TODO(benvanik): last index of \, unless \ at end, then before that
|
||||
name_ = NULL;
|
||||
}
|
||||
|
||||
Entry::~Entry() {
|
||||
xe_free(path_);
|
||||
xe_free(name_);
|
||||
}
|
||||
|
||||
Entry::Type Entry::type() {
|
||||
return type_;
|
||||
}
|
||||
|
||||
Device* Entry::device() {
|
||||
return device_;
|
||||
}
|
||||
|
||||
const char* Entry::path() {
|
||||
return path_;
|
||||
}
|
||||
|
||||
const char* Entry::name() {
|
||||
return name_;
|
||||
}
|
||||
|
||||
|
||||
MemoryMapping::MemoryMapping(uint8_t* address, size_t length) :
|
||||
address_(address), length_(length) {
|
||||
}
|
||||
|
||||
MemoryMapping::~MemoryMapping() {
|
||||
}
|
||||
|
||||
uint8_t* MemoryMapping::address() {
|
||||
return address_;
|
||||
}
|
||||
|
||||
size_t MemoryMapping::length() {
|
||||
return length_;
|
||||
}
|
||||
|
||||
|
||||
FileEntry::FileEntry(Device* device, const char* path) :
|
||||
Entry(kTypeFile, device, path) {
|
||||
}
|
||||
|
||||
FileEntry::~FileEntry() {
|
||||
}
|
||||
|
||||
|
||||
DirectoryEntry::DirectoryEntry(Device* device, const char* path) :
|
||||
Entry(kTypeDirectory, device, path) {
|
||||
}
|
||||
|
||||
DirectoryEntry::~DirectoryEntry() {
|
||||
}
|
||||
90
src/xenia/kernel/modules/xboxkrnl/fs/entry.h
Normal file
90
src/xenia/kernel/modules/xboxkrnl/fs/entry.h
Normal file
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_KERNEL_MODULES_XBOXKRNL_FS_ENTRY_H_
|
||||
#define XENIA_KERNEL_MODULES_XBOXKRNL_FS_ENTRY_H_
|
||||
|
||||
#include <xenia/common.h>
|
||||
#include <xenia/core.h>
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace kernel {
|
||||
namespace xboxkrnl {
|
||||
namespace fs {
|
||||
|
||||
|
||||
class Device;
|
||||
|
||||
|
||||
class Entry {
|
||||
public:
|
||||
enum Type {
|
||||
kTypeFile,
|
||||
kTypeDirectory,
|
||||
};
|
||||
|
||||
Entry(Type type, Device* device, const char* path);
|
||||
virtual ~Entry();
|
||||
|
||||
Type type();
|
||||
Device* device();
|
||||
const char* path();
|
||||
const char* name();
|
||||
|
||||
private:
|
||||
Type type_;
|
||||
Device* device_;
|
||||
char* path_;
|
||||
char* name_;
|
||||
};
|
||||
|
||||
|
||||
class MemoryMapping {
|
||||
public:
|
||||
MemoryMapping(uint8_t* address, size_t length);
|
||||
virtual ~MemoryMapping();
|
||||
|
||||
uint8_t* address();
|
||||
size_t length();
|
||||
|
||||
private:
|
||||
uint8_t* address_;
|
||||
size_t length_;
|
||||
};
|
||||
|
||||
|
||||
class FileEntry : public Entry {
|
||||
public:
|
||||
FileEntry(Device* device, const char* path);
|
||||
virtual ~FileEntry();
|
||||
|
||||
//virtual void Query() = 0;
|
||||
|
||||
virtual MemoryMapping* CreateMemoryMapping(
|
||||
xe_file_mode file_mode, const size_t offset, const size_t length) = 0;
|
||||
};
|
||||
|
||||
|
||||
class DirectoryEntry : public Entry {
|
||||
public:
|
||||
DirectoryEntry(Device* device, const char* path);
|
||||
virtual ~DirectoryEntry();
|
||||
|
||||
//virtual void Query() = 0;
|
||||
};
|
||||
|
||||
|
||||
} // namespace fs
|
||||
} // namespace xboxkrnl
|
||||
} // namespace kernel
|
||||
} // namespace xe
|
||||
|
||||
|
||||
#endif // XENIA_KERNEL_MODULES_XBOXKRNL_FS_ENTRY_H_
|
||||
113
src/xenia/kernel/modules/xboxkrnl/fs/filesystem.cc
Normal file
113
src/xenia/kernel/modules/xboxkrnl/fs/filesystem.cc
Normal file
@@ -0,0 +1,113 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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/modules/xboxkrnl/fs/filesystem.h>
|
||||
|
||||
#include <xenia/kernel/modules/xboxkrnl/fs/devices/disc_image_device.h>
|
||||
#include <xenia/kernel/modules/xboxkrnl/fs/devices/local_directory_device.h>
|
||||
|
||||
|
||||
using namespace xe;
|
||||
using namespace xe::kernel;
|
||||
using namespace xe::kernel::xboxkrnl::fs;
|
||||
|
||||
|
||||
FileSystem::FileSystem() {
|
||||
}
|
||||
|
||||
FileSystem::~FileSystem() {
|
||||
// Delete all devices.
|
||||
// This will explode if anyone is still using data from them.
|
||||
for (std::vector<Device*>::iterator it = devices_.begin();
|
||||
it != devices_.end(); ++it) {
|
||||
delete *it;
|
||||
}
|
||||
devices_.clear();
|
||||
symlinks_.clear();
|
||||
}
|
||||
|
||||
int FileSystem::RegisterDevice(const char* path, Device* device) {
|
||||
devices_.push_back(device);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int FileSystem::RegisterLocalDirectoryDevice(
|
||||
const char* path, const xechar_t* local_path) {
|
||||
Device* device = new LocalDirectoryDevice(path, local_path);
|
||||
return RegisterDevice(path, device);
|
||||
}
|
||||
|
||||
int FileSystem::RegisterDiscImageDevice(
|
||||
const char* path, const xechar_t* local_path) {
|
||||
DiscImageDevice* device = new DiscImageDevice(path, local_path);
|
||||
if (device->Init()) {
|
||||
return 1;
|
||||
}
|
||||
return RegisterDevice(path, device);
|
||||
}
|
||||
|
||||
int FileSystem::CreateSymbolicLink(const char* path, const char* target) {
|
||||
symlinks_.insert(std::pair<const char*, const char*>(
|
||||
xestrdupa(path),
|
||||
xestrdupa(target)));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int FileSystem::DeleteSymbolicLink(const char* path) {
|
||||
std::tr1::unordered_map<std::string, std::string>::iterator it =
|
||||
symlinks_.find(std::string(path));
|
||||
if (it != symlinks_.end()) {
|
||||
symlinks_.erase(it);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
Entry* FileSystem::ResolvePath(const char* path) {
|
||||
// Strip off prefix and pass to device.
|
||||
// e.g., d:\some\PATH.foo -> some\PATH.foo
|
||||
// Support both symlinks and device specifiers, like:
|
||||
// \\Device\Foo\some\PATH.foo, d:\some\PATH.foo, etc.
|
||||
|
||||
// TODO(benvanik): normalize path/etc
|
||||
// e.g., remove ..'s and such
|
||||
|
||||
// Resolve symlinks.
|
||||
// TODO(benvanik): more robust symlink handling - right now we assume simple
|
||||
// drive path -> device mappings with nothing nested.
|
||||
char full_path[XE_MAX_PATH];
|
||||
XEIGNORE(xestrcpya(full_path, XECOUNT(full_path), path));
|
||||
for (std::tr1::unordered_map<std::string, std::string>::iterator it =
|
||||
symlinks_.begin(); it != symlinks_.end(); ++it) {
|
||||
if (xestrcasestra(path, it->first.c_str()) == path) {
|
||||
// Found symlink, fixup.
|
||||
const char* after_path = path + it->first.size();
|
||||
XEIGNORE(xesnprintfa(full_path, XECOUNT(full_path), "%s%s",
|
||||
it->second.c_str(), after_path));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Scan all devices.
|
||||
for (std::vector<Device*>::iterator it = devices_.begin();
|
||||
it != devices_.end(); ++it) {
|
||||
Device* device = *it;
|
||||
if (xestrcasestra(full_path, device->path()) == full_path) {
|
||||
// Found!
|
||||
// Trim the device prefix off and pass down.
|
||||
char device_path[XE_MAX_PATH];
|
||||
XEIGNORE(xestrcpya(device_path, XECOUNT(device_path),
|
||||
full_path + xestrlena(device->path())));
|
||||
return device->ResolvePath(device_path);
|
||||
}
|
||||
}
|
||||
|
||||
XELOGE("ResolvePath(%s) failed - no root found", path);
|
||||
return NULL;
|
||||
}
|
||||
57
src/xenia/kernel/modules/xboxkrnl/fs/filesystem.h
Normal file
57
src/xenia/kernel/modules/xboxkrnl/fs/filesystem.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_KERNEL_MODULES_XBOXKRNL_FS_FILESYSTEM_H_
|
||||
#define XENIA_KERNEL_MODULES_XBOXKRNL_FS_FILESYSTEM_H_
|
||||
|
||||
#include <xenia/common.h>
|
||||
#include <xenia/core.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <xenia/kernel/modules/xboxkrnl/fs/entry.h>
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace kernel {
|
||||
namespace xboxkrnl {
|
||||
namespace fs {
|
||||
|
||||
|
||||
class Device;
|
||||
|
||||
|
||||
class FileSystem {
|
||||
public:
|
||||
FileSystem();
|
||||
~FileSystem();
|
||||
|
||||
int RegisterDevice(const char* path, Device* device);
|
||||
int RegisterLocalDirectoryDevice(const char* path,
|
||||
const xechar_t* local_path);
|
||||
int RegisterDiscImageDevice(const char* path, const xechar_t* local_path);
|
||||
|
||||
int CreateSymbolicLink(const char* path, const char* target);
|
||||
int DeleteSymbolicLink(const char* path);
|
||||
|
||||
Entry* ResolvePath(const char* path);
|
||||
|
||||
private:
|
||||
std::vector<Device*> devices_;
|
||||
std::tr1::unordered_map<std::string, std::string> symlinks_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace fs
|
||||
} // namespace xboxkrnl
|
||||
} // namespace kernel
|
||||
} // namespace xe
|
||||
|
||||
|
||||
#endif // XENIA_KERNEL_MODULES_XBOXKRNL_FS_FILESYSTEM_H_
|
||||
209
src/xenia/kernel/modules/xboxkrnl/fs/gdfx.cc
Normal file
209
src/xenia/kernel/modules/xboxkrnl/fs/gdfx.cc
Normal file
@@ -0,0 +1,209 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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. *
|
||||
******************************************************************************
|
||||
* Major contributions to this file from:
|
||||
* - abgx360
|
||||
*/
|
||||
|
||||
#include <xenia/kernel/modules/xboxkrnl/fs/gdfx.h>
|
||||
|
||||
|
||||
using namespace xe;
|
||||
using namespace xe::kernel;
|
||||
using namespace xe::kernel::xboxkrnl::fs;
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
#define kXESectorSize 2048
|
||||
|
||||
}
|
||||
|
||||
|
||||
GDFXEntry::GDFXEntry() :
|
||||
attributes(0), offset(0), size(0) {
|
||||
}
|
||||
|
||||
GDFXEntry::~GDFXEntry() {
|
||||
for (std::vector<GDFXEntry*>::iterator it = children.begin();
|
||||
it != children.end(); ++it) {
|
||||
delete *it;
|
||||
}
|
||||
}
|
||||
|
||||
GDFXEntry* GDFXEntry::GetChild(const char* name) {
|
||||
// TODO(benvanik): a faster search
|
||||
for (std::vector<GDFXEntry*>::iterator it = children.begin();
|
||||
it != children.end(); ++it) {
|
||||
GDFXEntry* entry = *it;
|
||||
if (xestrcasecmpa(entry->name.c_str(), name) == 0) {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void GDFXEntry::Dump(int indent) {
|
||||
printf("%s%s\n", std::string(indent, ' ').c_str(), name.c_str());
|
||||
for (std::vector<GDFXEntry*>::iterator it = children.begin();
|
||||
it != children.end(); ++it) {
|
||||
GDFXEntry* entry = *it;
|
||||
entry->Dump(indent + 2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
GDFX::GDFX(xe_mmap_ref mmap) {
|
||||
mmap_ = xe_mmap_retain(mmap);
|
||||
|
||||
root_entry_ = NULL;
|
||||
}
|
||||
|
||||
GDFX::~GDFX() {
|
||||
delete root_entry_;
|
||||
|
||||
xe_mmap_release(mmap_);
|
||||
}
|
||||
|
||||
GDFXEntry* GDFX::root_entry() {
|
||||
return root_entry_;
|
||||
}
|
||||
|
||||
GDFX::Error GDFX::Load() {
|
||||
Error result = kErrorOutOfMemory;
|
||||
|
||||
ParseState state;
|
||||
xe_zero_struct(&state, sizeof(state));
|
||||
|
||||
state.ptr = (uint8_t*)xe_mmap_get_addr(mmap_);
|
||||
state.size = xe_mmap_get_length(mmap_);
|
||||
|
||||
result = Verify(state);
|
||||
XEEXPECTZERO(result);
|
||||
|
||||
result = ReadAllEntries(state, state.ptr + state.root_offset);
|
||||
XEEXPECTZERO(result);
|
||||
|
||||
result = kSuccess;
|
||||
XECLEANUP:
|
||||
return result;
|
||||
}
|
||||
|
||||
void GDFX::Dump() {
|
||||
if (root_entry_) {
|
||||
root_entry_->Dump(0);
|
||||
}
|
||||
}
|
||||
|
||||
GDFX::Error GDFX::Verify(ParseState& state) {
|
||||
// Find sector 32 of the game partition - try at a few points.
|
||||
const static size_t likely_offsets[] = {
|
||||
0x00000000, 0x0000FB20, 0x00020600, 0x0FD90000,
|
||||
};
|
||||
bool magic_found = false;
|
||||
for (size_t n = 0; n < XECOUNT(likely_offsets); n++) {
|
||||
state.game_offset = likely_offsets[n];
|
||||
if (VerifyMagic(state, state.game_offset + (32 * kXESectorSize))) {
|
||||
magic_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!magic_found) {
|
||||
// File doesn't have the magic values - likely not a real GDFX source.
|
||||
return kErrorFileMismatch;
|
||||
}
|
||||
|
||||
// Read sector 32 to get FS state.
|
||||
if (state.size < state.game_offset + (32 * kXESectorSize)) {
|
||||
return kErrorReadError;
|
||||
}
|
||||
uint8_t* fs_ptr = state.ptr + state.game_offset + (32 * kXESectorSize);
|
||||
state.root_sector = XEGETUINT32LE(fs_ptr + 20);
|
||||
state.root_size = XEGETUINT32LE(fs_ptr + 24);
|
||||
state.root_offset = state.game_offset + (state.root_sector * kXESectorSize);
|
||||
if (state.root_size < 13 ||
|
||||
state.root_size > 32 * 1024 * 1024) {
|
||||
return kErrorDamagedFile;
|
||||
}
|
||||
|
||||
return kSuccess;
|
||||
}
|
||||
|
||||
bool GDFX::VerifyMagic(ParseState& state, size_t offset) {
|
||||
// Simple check to see if the given offset contains the magic value.
|
||||
return memcmp(state.ptr + offset, "MICROSOFT*XBOX*MEDIA", 20) == 0;
|
||||
}
|
||||
|
||||
GDFX::Error GDFX::ReadAllEntries(ParseState& state,
|
||||
const uint8_t* root_buffer) {
|
||||
root_entry_ = new GDFXEntry();
|
||||
root_entry_->offset = 0;
|
||||
root_entry_->size = 0;
|
||||
root_entry_->name = "";
|
||||
root_entry_->attributes = GDFXEntry::kAttrFolder;
|
||||
|
||||
if (!ReadEntry(state, root_buffer, 0, root_entry_)) {
|
||||
return kErrorOutOfMemory;
|
||||
}
|
||||
|
||||
return kSuccess;
|
||||
}
|
||||
|
||||
bool GDFX::ReadEntry(ParseState& state, const uint8_t* buffer,
|
||||
uint16_t entry_ordinal, GDFXEntry* parent) {
|
||||
const uint8_t* p = buffer + (entry_ordinal * 4);
|
||||
|
||||
uint16_t node_l = XEGETUINT16LE(p + 0);
|
||||
uint16_t node_r = XEGETUINT16LE(p + 2);
|
||||
size_t sector = XEGETUINT32LE(p + 4);
|
||||
size_t length = XEGETUINT32LE(p + 8);
|
||||
uint8_t attributes = XEGETUINT8LE(p + 12);
|
||||
uint8_t name_length = XEGETUINT8LE(p + 13);
|
||||
char* name = (char*)(p + 14);
|
||||
|
||||
if (node_l && !ReadEntry(state, buffer, node_l, parent)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
GDFXEntry* entry = new GDFXEntry();
|
||||
entry->name = std::string(name, name_length);
|
||||
entry->name.append(1, '\0');
|
||||
entry->attributes = attributes;
|
||||
|
||||
// Add to parent.
|
||||
parent->children.push_back(entry);
|
||||
|
||||
if (attributes & GDFXEntry::kAttrFolder) {
|
||||
// Folder.
|
||||
entry->offset = 0;
|
||||
entry->size = 0;
|
||||
if (length) {
|
||||
// Not a leaf - read in children.
|
||||
if (state.size < state.game_offset + (sector * kXESectorSize)) {
|
||||
// Out of bounds read.
|
||||
return false;
|
||||
}
|
||||
// Read child list.
|
||||
uint8_t* folder_ptr =
|
||||
state.ptr + state.game_offset + (sector * kXESectorSize);
|
||||
if (!ReadEntry(state, folder_ptr, 0, entry)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// File.
|
||||
entry->offset = state.game_offset + (sector * kXESectorSize);
|
||||
entry->size = length;
|
||||
}
|
||||
|
||||
// Read next file in the list.
|
||||
if (node_r && !ReadEntry(state, buffer, node_r, parent)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
107
src/xenia/kernel/modules/xboxkrnl/fs/gdfx.h
Normal file
107
src/xenia/kernel/modules/xboxkrnl/fs/gdfx.h
Normal file
@@ -0,0 +1,107 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_KERNEL_MODULES_XBOXKRNL_FS_GDFX_H_
|
||||
#define XENIA_KERNEL_MODULES_XBOXKRNL_FS_GDFX_H_
|
||||
|
||||
#include <xenia/common.h>
|
||||
#include <xenia/core.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <xenia/kernel/modules/xboxkrnl/fs/entry.h>
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace kernel {
|
||||
namespace xboxkrnl {
|
||||
namespace fs {
|
||||
|
||||
|
||||
class GDFX;
|
||||
|
||||
|
||||
class GDFXEntry {
|
||||
public:
|
||||
enum Attributes {
|
||||
kAttrNone = 0x00000000,
|
||||
kAttrReadOnly = 0x00000001,
|
||||
kAttrHidden = 0x00000002,
|
||||
kAttrSystem = 0x00000004,
|
||||
kAttrFolder = 0x00000010,
|
||||
kAttrArchived = 0x00000020,
|
||||
kAttrNormal = 0x00000080,
|
||||
};
|
||||
|
||||
GDFXEntry();
|
||||
~GDFXEntry();
|
||||
|
||||
GDFXEntry* GetChild(const char* name);
|
||||
|
||||
void Dump(int indent);
|
||||
|
||||
std::string name;
|
||||
uint32_t attributes;
|
||||
size_t offset;
|
||||
size_t size;
|
||||
|
||||
std::vector<GDFXEntry*> children;
|
||||
};
|
||||
|
||||
|
||||
class GDFX {
|
||||
public:
|
||||
enum Error {
|
||||
kSuccess = 0,
|
||||
kErrorOutOfMemory = -1,
|
||||
kErrorReadError = -10,
|
||||
kErrorFileMismatch = -30,
|
||||
kErrorDamagedFile = -31,
|
||||
};
|
||||
|
||||
GDFX(xe_mmap_ref mmap);
|
||||
virtual ~GDFX();
|
||||
|
||||
GDFXEntry* root_entry();
|
||||
|
||||
Error Load();
|
||||
void Dump();
|
||||
|
||||
private:
|
||||
typedef struct {
|
||||
uint8_t* ptr;
|
||||
|
||||
size_t size; // Size (bytes) of total image
|
||||
|
||||
size_t game_offset; // Offset (bytes) of game partition
|
||||
|
||||
size_t root_sector; // Offset (sector) of root
|
||||
size_t root_offset; // Offset (bytes) of root
|
||||
size_t root_size; // Size (bytes) of root
|
||||
} ParseState;
|
||||
|
||||
Error Verify(ParseState& state);
|
||||
bool VerifyMagic(ParseState& state, size_t offset);
|
||||
Error ReadAllEntries(ParseState& state, const uint8_t* root_buffer);
|
||||
bool ReadEntry(ParseState& state, const uint8_t* buffer,
|
||||
uint16_t entry_ordinal, GDFXEntry* parent);
|
||||
|
||||
xe_mmap_ref mmap_;
|
||||
|
||||
GDFXEntry* root_entry_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace fs
|
||||
} // namespace xboxkrnl
|
||||
} // namespace kernel
|
||||
} // namespace xe
|
||||
|
||||
|
||||
#endif // XENIA_KERNEL_MODULES_XBOXKRNL_FS_GDFX_H_
|
||||
17
src/xenia/kernel/modules/xboxkrnl/fs/sources.gypi
Normal file
17
src/xenia/kernel/modules/xboxkrnl/fs/sources.gypi
Normal file
@@ -0,0 +1,17 @@
|
||||
# Copyright 2013 Ben Vanik. All Rights Reserved.
|
||||
{
|
||||
'sources': [
|
||||
'device.cc',
|
||||
'device.h',
|
||||
'entry.cc',
|
||||
'entry.h',
|
||||
'filesystem.cc',
|
||||
'filesystem.h',
|
||||
'gdfx.cc',
|
||||
'gdfx.h',
|
||||
],
|
||||
|
||||
'includes': [
|
||||
'devices/sources.gypi',
|
||||
],
|
||||
}
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include <xenia/kernel/modules/xboxkrnl/kernel_state.h>
|
||||
|
||||
#include <xenia/kernel/runtime.h>
|
||||
#include <xenia/kernel/modules/xboxkrnl/xobject.h>
|
||||
#include <xenia/kernel/modules/xboxkrnl/objects/xmodule.h>
|
||||
#include <xenia/kernel/modules/xboxkrnl/objects/xthread.h>
|
||||
|
||||
@@ -16,16 +16,25 @@
|
||||
#include <xenia/kernel/export.h>
|
||||
#include <xenia/kernel/kernel_module.h>
|
||||
#include <xenia/kernel/xbox.h>
|
||||
#include <xenia/kernel/fs/filesystem.h>
|
||||
#include <xenia/kernel/modules/xboxkrnl/object_table.h>
|
||||
#include <xenia/kernel/modules/xboxkrnl/fs/filesystem.h>
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
class Processor;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace kernel {
|
||||
namespace xboxkrnl {
|
||||
|
||||
|
||||
class XModule;
|
||||
namespace fs {
|
||||
class FileSystem;
|
||||
}
|
||||
|
||||
|
||||
class KernelState {
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
|
||||
using namespace xe;
|
||||
using namespace xe::kernel;
|
||||
using namespace xe::kernel::fs;
|
||||
using namespace xe::kernel::xboxkrnl;
|
||||
using namespace xe::kernel::xboxkrnl::fs;
|
||||
|
||||
|
||||
XFile::XFile(KernelState* kernel_state, FileEntry* entry) :
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
#include <xenia/kernel/modules/xboxkrnl/xobject.h>
|
||||
|
||||
#include <xenia/kernel/xbox.h>
|
||||
#include <xenia/kernel/fs/entry.h>
|
||||
#include <xenia/kernel/modules/xboxkrnl/async_request.h>
|
||||
#include <xenia/kernel/modules/xboxkrnl/fs/entry.h>
|
||||
|
||||
|
||||
namespace xe {
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
|
||||
#include <xenia/kernel/modules/xboxkrnl/objects/xmodule.h>
|
||||
|
||||
#include <xenia/cpu/cpu.h>
|
||||
#include <xenia/kernel/runtime.h>
|
||||
#include <xenia/kernel/modules/xboxkrnl/objects/xthread.h>
|
||||
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include <xenia/kernel/modules/xboxkrnl/objects/xthread.h>
|
||||
|
||||
#include <xenia/cpu/cpu.h>
|
||||
#include <xenia/kernel/modules/xboxkrnl/objects/xevent.h>
|
||||
#include <xenia/kernel/modules/xboxkrnl/objects/xmodule.h>
|
||||
|
||||
|
||||
@@ -15,6 +15,13 @@
|
||||
#include <xenia/kernel/xbox.h>
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
class ThreadState;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace kernel {
|
||||
namespace xboxkrnl {
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
],
|
||||
|
||||
'includes': [
|
||||
'fs/sources.gypi',
|
||||
'objects/sources.gypi',
|
||||
],
|
||||
}
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
|
||||
using namespace xe;
|
||||
using namespace xe::kernel;
|
||||
using namespace xe::kernel::fs;
|
||||
using namespace xe::kernel::xboxkrnl;
|
||||
using namespace xe::kernel::xboxkrnl::fs;
|
||||
|
||||
|
||||
namespace xe {
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include <xenia/kernel/modules/xboxkrnl/xboxkrnl_video.h>
|
||||
|
||||
#include <xenia/cpu/cpu.h>
|
||||
#include <xenia/gpu/gpu.h>
|
||||
#include <xenia/kernel/shim_utils.h>
|
||||
#include <xenia/kernel/modules/xboxkrnl/kernel_state.h>
|
||||
|
||||
Reference in New Issue
Block a user