Wiring up xex loading via the new virtual filesystem.

File resolution is hacked, but works well enough for testing.
This commit is contained in:
Ben Vanik
2013-01-31 16:52:50 -08:00
parent 59189f12ab
commit f78fdba9c3
26 changed files with 392 additions and 60 deletions

View File

@@ -15,10 +15,20 @@ using namespace xe::kernel;
using namespace xe::kernel::fs;
Device::Device(xe_pal_ref pal) {
Device::Device(xe_pal_ref pal, const char* path) {
pal_ = xe_pal_retain(pal);
path_ = xestrdupa(path);
}
Device::~Device() {
xe_free(path_);
xe_pal_release(pal_);
}
xe_pal_ref Device::pal() {
return xe_pal_retain(pal_);
}
const char* Device::path() {
return path_;
}

View File

@@ -15,8 +15,9 @@ using namespace xe::kernel;
using namespace xe::kernel::fs;
DiscImageDevice::DiscImageDevice(xe_pal_ref pal, const xechar_t* local_path) :
Device(pal) {
DiscImageDevice::DiscImageDevice(xe_pal_ref pal, const char* path,
const xechar_t* local_path) :
Device(pal, path) {
local_path_ = xestrdup(local_path);
}
@@ -25,5 +26,11 @@ DiscImageDevice::~DiscImageDevice() {
}
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(XT("DiscImageDevice::ResolvePath(%s)"), path);
return NULL;
}

View File

@@ -23,7 +23,7 @@ namespace fs {
class DiscImageDevice : public Device {
public:
DiscImageDevice(xe_pal_ref pal, const xechar_t* local_path);
DiscImageDevice(xe_pal_ref pal, const char* path, const xechar_t* local_path);
virtual ~DiscImageDevice();
virtual Entry* ResolvePath(const char* path);

View File

@@ -15,9 +15,64 @@ using namespace xe::kernel;
using namespace xe::kernel::fs;
LocalDirectoryDevice::LocalDirectoryDevice(xe_pal_ref pal,
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_pal_ref pal = device()->pal();
xe_mmap_ref mmap = xe_mmap_open(pal, file_mode, local_path_,
offset, length);
xe_pal_release(pal);
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(xe_pal_ref pal, const char* path,
const xechar_t* local_path) :
Device(pal) {
Device(pal, path) {
local_path_ = xestrdup(local_path);
}
@@ -26,5 +81,32 @@ LocalDirectoryDevice::~LocalDirectoryDevice() {
}
Entry* LocalDirectoryDevice::ResolvePath(const char* path) {
return NULL;
// The filesystem will have stripped our prefix off already, so the path will
// be in the form:
// some\PATH.foo
XELOGFS(XT("LocalDirectoryDevice::ResolvePath(%s)"), path);
xechar_t full_path[XE_MAX_PATH];
xesnprintf(full_path, XECOUNT(full_path), XT("%s%c%s"),
local_path_, XE_PATH_SEPARATOR, 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;
}

View File

@@ -23,7 +23,8 @@ namespace fs {
class LocalDirectoryDevice : public Device {
public:
LocalDirectoryDevice(xe_pal_ref pal, const xechar_t* local_path);
LocalDirectoryDevice(xe_pal_ref pal, const char* path,
const xechar_t* local_path);
virtual ~LocalDirectoryDevice();
virtual Entry* ResolvePath(const char* path);

View File

@@ -45,6 +45,22 @@ const char* Entry::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) {
}

View File

@@ -23,33 +23,91 @@ FileSystem::FileSystem(xe_pal_ref pal) {
}
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();
xe_pal_release(pal_);
}
int FileSystem::RegisterDevice(const char* path, Device* device) {
return 1;
devices_.push_back(device);
return 0;
}
int FileSystem::RegisterLocalDirectoryDevice(
const char* path, const xechar_t* local_path) {
Device* device = new LocalDirectoryDevice(pal_, local_path);
Device* device = new LocalDirectoryDevice(pal_, path, local_path);
return RegisterDevice(path, device);
}
int FileSystem::RegisterDiscImageDevice(
const char* path, const xechar_t* local_path) {
Device* device = new DiscImageDevice(pal_, local_path);
Device* device = new DiscImageDevice(pal_, path, local_path);
return RegisterDevice(path, device);
}
int FileSystem::CreateSymbolicLink(const char* path, const char* target) {
return 1;
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(xesnprintf(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(XT("ResolvePath(%s) failed - no root found"), path);
return NULL;
}

View File

@@ -31,6 +31,7 @@ KernelState::KernelState(Runtime* runtime) :
pal_ = runtime->pal();
memory_ = runtime->memory();
processor_ = runtime->processor();
filesystem_ = runtime->filesystem();
objects_mutex_ = xe_mutex_alloc(0);
XEASSERTNOTNULL(objects_mutex_);
@@ -65,6 +66,7 @@ KernelState::~KernelState() {
xe_mutex_free(objects_mutex_);
objects_mutex_ = NULL;
filesystem_.reset();
processor_.reset();
xe_memory_release(memory_);
xe_pal_release(pal_);
@@ -86,6 +88,10 @@ cpu::Processor* KernelState::processor() {
return processor_.get();
}
fs::FileSystem* KernelState::filesystem() {
return filesystem_.get();
}
// TODO(benvanik): invesitgate better handle storage/structure.
// A much better way of doing handles, if performance becomes an issue, would
// be to try to make the pointers 32bit. Then we could round-trip them through

View File

@@ -16,6 +16,7 @@
#include <xenia/kernel/export.h>
#include <xenia/kernel/kernel_module.h>
#include <xenia/kernel/xbox.h>
#include <xenia/kernel/fs/filesystem.h>
namespace xe {
@@ -37,6 +38,7 @@ public:
xe_pal_ref pal();
xe_memory_ref memory();
cpu::Processor* processor();
fs::FileSystem* filesystem();
XObject* GetObject(X_HANDLE handle);
@@ -52,6 +54,7 @@ private:
xe_pal_ref pal_;
xe_memory_ref memory_;
shared_ptr<cpu::Processor> processor_;
shared_ptr<fs::FileSystem> filesystem_;
XModule* executable_module_;

View File

@@ -104,36 +104,24 @@ XboxkrnlModule::XboxkrnlModule(Runtime* runtime) :
XboxkrnlModule::~XboxkrnlModule() {
}
int XboxkrnlModule::LaunchModule(const xechar_t* path) {
// TODO(benvanik): setup the virtual filesystem map and use LoadFromFile.
int XboxkrnlModule::LaunchModule(const char* path) {
// Create and register the module. We keep it local to this function and
// dispose it on exit.
XModule* module = new XModule(kernel_state_.get(), path);
xe_mmap_ref mmap = xe_mmap_open(pal_, kXEFileModeRead, path, 0, 0);
if (!mmap) {
return NULL;
}
void* addr = xe_mmap_get_addr(mmap);
size_t length = xe_mmap_get_length(mmap);
char path_a[2048];
XEIGNORE(xestrnarrow(path_a, XECOUNT(path_a), path));
// Create and load the module.
XModule* module = new XModule(kernel_state_.get(), path_a);
X_STATUS status = module->LoadFromMemory(addr, length);
// TODO(benvanik): retain memory somehow? is it needed?
xe_mmap_release(mmap);
if (XFAILED(status)) {
XELOGE(XT("Failed to load module"));
// Load the module into memory from the filesystem.
X_STATUS result_code = module->LoadFromFile(path);
if (XFAILED(result_code)) {
XELOGE(XT("Failed to load module %s: %.8X"), path, result_code);
module->Release();
return 1;
}
// Launch the module.
status = module->Launch(0);
if (XFAILED(status)) {
XELOGE(XT("Failed to launch module"));
// NOTE: this won't return until the module exits.
result_code = module->Launch(0);
if (XFAILED(result_code)) {
XELOGE(XT("Failed to launch module %s: %.8X"), path, result_code);
module->Release();
return 2;
}

View File

@@ -29,7 +29,7 @@ public:
XboxkrnlModule(Runtime* runtime);
virtual ~XboxkrnlModule();
int LaunchModule(const xechar_t* path);
int LaunchModule(const char* path);
private:
auto_ptr<KernelState> kernel_state_;

View File

@@ -24,10 +24,13 @@ namespace {
XModule::XModule(KernelState* kernel_state, const char* path) :
XObject(kernel_state, kTypeModule),
xex_(NULL) {
XEIGNORE(xestrcpy(path_, XECOUNT(path_), path));
XEIGNORE(xestrcpya(path_, XECOUNT(path_), path));
const xechar_t *slash = xestrrchr(path, '/');
if (!slash) {
slash = xestrrchr(path, '\\');
}
if (slash) {
XEIGNORE(xestrcpy(name_, XECOUNT(name_), slash + 1));
XEIGNORE(xestrcpya(name_, XECOUNT(name_), slash + 1));
}
}
@@ -52,9 +55,33 @@ const xe_xex2_header_t* XModule::xex_header() {
}
X_STATUS XModule::LoadFromFile(const char* path) {
// TODO(benvanik): load from virtual filesystem.
XEASSERTALWAYS();
return X_STATUS_UNSUCCESSFUL;
// Resolve the file to open.
// TODO(benvanik): make this code shared?
fs::Entry* fs_entry = kernel_state()->filesystem()->ResolvePath(path);
if (!fs_entry) {
XELOGE(XT("File not found: %s"), path);
return X_STATUS_NO_SUCH_FILE;
}
if (fs_entry->type() != fs::Entry::kTypeFile) {
XELOGE(XT("Invalid file type: %s"), path);
return X_STATUS_NO_SUCH_FILE;
}
fs::FileEntry* fs_file = static_cast<fs::FileEntry*>(fs_entry);
// Map into memory.
fs::MemoryMapping* mmap = fs_file->CreateMemoryMapping(kXEFileModeRead, 0, 0);
if (!mmap) {
return X_STATUS_UNSUCCESSFUL;
}
// Load the module.
X_STATUS return_code = LoadFromMemory(mmap->address(), mmap->length());
// Unmap memory and cleanup.
delete mmap;
delete fs_entry;
return return_code;
}
X_STATUS XModule::LoadFromMemory(const void* addr, const size_t length) {

View File

@@ -48,7 +48,7 @@ private:
int LoadPE();
char name_[256];
char path_[2048];
char path_[XE_MAX_PATH];
xe_xex2_ref xex_;
};

View File

@@ -63,6 +63,79 @@ shared_ptr<FileSystem> Runtime::filesystem() {
return filesystem_;
}
int Runtime::LaunchModule(const xechar_t* path) {
return xboxkrnl_->LaunchModule(path);
int Runtime::LaunchXexFile(const xechar_t* path) {
// We create a virtual filesystem pointing to its directory and symlink
// that to the game filesystem.
// e.g., /my/files/foo.xex will get a local fs at:
// \\Device\\Harddisk0\\Partition1
// and then get that symlinked to game:\, so
// -> game:\foo.xex
int result_code = 0;
// Get just the filename (foo.xex).
const xechar_t* file_name = xestrrchr(path, XE_PATH_SEPARATOR);
if (file_name) {
// Skip slash.
file_name++;
} else {
// No slash found, whole thing is a file.
file_name = path;
}
// Get the parent path of the file.
xechar_t parent_path[XE_MAX_PATH];
XEIGNORE(xestrcpy(parent_path, XECOUNT(parent_path), path));
parent_path[file_name - path] = 0;
// Register the local directory in the virtual filesystem.
result_code = filesystem_->RegisterLocalDirectoryDevice(
"\\Device\\Harddisk1\\Partition0", parent_path);
if (result_code) {
XELOGE(XT("Unable to mount local directory %s"), parent_path);
return result_code;
}
// Create symlinks to the device.
filesystem_->CreateSymbolicLink(
"game:", "\\Device\\Harddisk1\\Partition0");
filesystem_->CreateSymbolicLink(
"d:", "\\Device\\Harddisk1\\Partition0");
// Get the file name of the module to load from the filesystem.
char fs_path[XE_MAX_PATH];
XEIGNORE(xestrcpya(fs_path, XECOUNT(fs_path), "game:\\"));
char* fs_path_ptr = fs_path + xestrlena(fs_path);
*fs_path_ptr = 0;
#if XE_WCHAR
XEIGNORE(xestrnarrow(fs_path_ptr, XECOUNT(fs_path), file_name));
#else
XEIGNORE(xestrcpya(fs_path_ptr, XECOUNT(fs_path), file_name));
#endif
// Launch the game.
return xboxkrnl_->LaunchModule(fs_path);
}
int Runtime::LaunchDiscImage(const xechar_t* path) {
int result_code = 0;
// Register the disc image in the virtual filesystem.
result_code = filesystem_->RegisterDiscImageDevice(
"\\Device\\Cdrom0", path);
if (result_code) {
XELOGE(XT("Unable to mount disc image %s"), path);
return result_code;
}
// Create symlinks to the device.
filesystem_->CreateSymbolicLink(
"game:",
"\\Device\\Cdrom0");
filesystem_->CreateSymbolicLink(
"d:",
"\\Device\\Cdrom0");
// Launch the game.
return xboxkrnl_->LaunchModule("game:\\default.xex");
}