Wiring up xex loading via the new virtual filesystem.
File resolution is hacked, but works well enough for testing.
This commit is contained in:
parent
59189f12ab
commit
f78fdba9c3
|
@ -26,6 +26,7 @@
|
|||
#define XE_OPTION_LOG_SDB 1
|
||||
#define XE_OPTION_LOG_GPU 1
|
||||
#define XE_OPTION_LOG_KERNEL 1
|
||||
#define XE_OPTION_LOG_FS 1
|
||||
|
||||
|
||||
// TODO(benvanik): make this a runtime option
|
||||
|
|
|
@ -23,13 +23,17 @@ namespace fs {
|
|||
|
||||
class Device {
|
||||
public:
|
||||
Device(xe_pal_ref pal);
|
||||
Device(xe_pal_ref pal, const char* path);
|
||||
virtual ~Device();
|
||||
|
||||
xe_pal_ref pal();
|
||||
const char* path();
|
||||
|
||||
virtual Entry* ResolvePath(const char* path) = 0;
|
||||
|
||||
protected:
|
||||
xe_pal_ref pal_;
|
||||
char* path_;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -45,12 +45,29 @@ private:
|
|||
};
|
||||
|
||||
|
||||
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();
|
||||
//virtual void Query() = 0;
|
||||
|
||||
virtual MemoryMapping* CreateMemoryMapping(
|
||||
xe_file_mode file_mode, const size_t offset, const size_t length) = 0;
|
||||
};
|
||||
|
||||
|
||||
|
@ -59,7 +76,7 @@ public:
|
|||
DirectoryEntry(Device* device, const char* path);
|
||||
virtual ~DirectoryEntry();
|
||||
|
||||
//virtual void Query();
|
||||
//virtual void Query() = 0;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
#include <xenia/common.h>
|
||||
#include <xenia/core.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <xenia/kernel/fs/entry.h>
|
||||
|
||||
|
||||
|
@ -41,6 +43,8 @@ public:
|
|||
|
||||
private:
|
||||
xe_pal_ref pal_;
|
||||
std::vector<Device*> devices_;
|
||||
std::tr1::unordered_map<std::string, std::string> symlinks_;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -54,10 +54,11 @@ public:
|
|||
shared_ptr<ExportResolver> export_resolver();
|
||||
shared_ptr<fs::FileSystem> filesystem();
|
||||
|
||||
int LaunchModule(const xechar_t* path);
|
||||
int LaunchXexFile(const xechar_t* path);
|
||||
int LaunchDiscImage(const xechar_t* path);
|
||||
|
||||
private:
|
||||
xechar_t command_line_[2048];
|
||||
xechar_t command_line_[XE_MAX_PATH];
|
||||
|
||||
xe_pal_ref pal_;
|
||||
xe_memory_ref memory_;
|
||||
|
|
|
@ -34,6 +34,7 @@ typedef uint32_t X_STATUS;
|
|||
#define X_STATUS_ACCESS_VIOLATION ((uint32_t)0xC0000005L)
|
||||
#define X_STATUS_INVALID_HANDLE ((uint32_t)0xC0000008L)
|
||||
#define X_STATUS_INVALID_PARAMETER ((uint32_t)0xC000000DL)
|
||||
#define X_STATUS_NO_SUCH_FILE ((uint32_t)0xC000000FL)
|
||||
#define X_STATUS_NO_MEMORY ((uint32_t)0xC0000017L)
|
||||
#define X_STATUS_ALREADY_COMMITTED ((uint32_t)0xC0000021L)
|
||||
#define X_STATUS_ACCESS_DENIED ((uint32_t)0xC0000022L)
|
||||
|
|
|
@ -74,6 +74,11 @@ void xe_log_line(const xechar_t* file_path, const uint32_t line_number,
|
|||
#else
|
||||
#define XELOGKERNEL(fmt, ...) XE_EMPTY_MACRO
|
||||
#endif
|
||||
#if XE_OPTION(LOG_FS)
|
||||
#define XELOGFS(fmt, ...) XELOGCORE('F', fmt, ##__VA_ARGS__)
|
||||
#else
|
||||
#define XELOGFS(fmt, ...) XE_EMPTY_MACRO
|
||||
#endif
|
||||
|
||||
|
||||
#endif // XENIA_LOGGING_H_
|
||||
|
|
|
@ -31,7 +31,7 @@ int strncpy_s(char* dest, size_t destLength, const char* source, size_t count);
|
|||
#define xestrdupa _strdup
|
||||
#define xestrtoullw _wcstoui64
|
||||
#define xestrtoulla _strtoui64
|
||||
#endif
|
||||
#endif // !WIN32
|
||||
|
||||
#define xestrlenw wcslen
|
||||
#define xestrcmpw wcscmp
|
||||
|
@ -40,6 +40,7 @@ int strncpy_s(char* dest, size_t destLength, const char* source, size_t count);
|
|||
#define xestrchrw wcschr
|
||||
#define xestrrchrw wcsrchr
|
||||
#define xestrstrw wcsstr
|
||||
#define xestrcasestrw ??
|
||||
#define xestrcpyw(dest, destLength, source) (wcscpy_s(dest, destLength, source) == 0)
|
||||
#define xestrncpyw(dest, destLength, source, count) (wcsncpy_s(dest, destLength, source, count) == 0)
|
||||
#define xestrcatw(dest, destLength, source) (wcscat_s(dest, destLength, source) == 0)
|
||||
|
@ -53,6 +54,7 @@ int strncpy_s(char* dest, size_t destLength, const char* source, size_t count);
|
|||
#define xestrchra strchr
|
||||
#define xestrrchra strrchr
|
||||
#define xestrstra strstr
|
||||
#define xestrcasestra strcasestr
|
||||
#define xestrcpya(dest, destLength, source) (strcpy_s(dest, destLength, source) == 0)
|
||||
#define xestrncpya(dest, destLength, source, count) (strncpy_s(dest, destLength, source, count) == 0)
|
||||
#define xestrcata(dest, destLength, source) (strcat_s(dest, destLength, source) == 0)
|
||||
|
@ -72,6 +74,7 @@ typedef wchar_t xechar_t;
|
|||
#define xestrchr xestrchrw
|
||||
#define xestrrchr xestrrchrw
|
||||
#define xestrstr xestrstrw
|
||||
#define xestrcasestr xestrcasestrw
|
||||
#define xestrtoull xestrtoullw
|
||||
#define xestrcpy xestrcpyw
|
||||
#define xestrncpy xestrncpyw
|
||||
|
@ -94,6 +97,7 @@ typedef char xechar_t;
|
|||
#define xestrchr xestrchra
|
||||
#define xestrrchr xestrrchra
|
||||
#define xestrstr xestrstra
|
||||
#define xestrcasestr xestrcasestra
|
||||
#define xestrtoull xestrtoulla
|
||||
#define xestrcpy xestrcpya
|
||||
#define xestrncpy xestrncpya
|
||||
|
@ -109,8 +113,10 @@ typedef char xechar_t;
|
|||
|
||||
#if XE_LIKE(WIN32)
|
||||
#define XE_PATH_SEPARATOR ((xechar_t)'\\')
|
||||
#define XE_MAX_PATH _MAX_PATH
|
||||
#else
|
||||
#define XE_PATH_SEPARATOR ((xechar_t)'/')
|
||||
#define XE_MAX_PATH PATH_MAX
|
||||
#endif // WIN32
|
||||
|
||||
#endif // XENIA_STRING_H_
|
||||
|
|
|
@ -71,7 +71,7 @@ int ModuleGenerator::Generate() {
|
|||
// Setup a debug info builder.
|
||||
// This is used when creating any debug info. We may want to go more
|
||||
// fine grained than this, but for now it's something.
|
||||
char dir[2048];
|
||||
char dir[XE_MAX_PATH];
|
||||
XEIGNORE(xestrcpya(dir, XECOUNT(dir), module_path_));
|
||||
char* slash = xestrrchra(dir, '/');
|
||||
if (slash) {
|
||||
|
|
|
@ -97,7 +97,7 @@ int ExecModule::Prepare() {
|
|||
int result_code = 1;
|
||||
std::string error_message;
|
||||
|
||||
char file_name[2048];
|
||||
char file_name[XE_MAX_PATH];
|
||||
|
||||
OwningPtr<MemoryBuffer> shared_module_buffer;
|
||||
auto_ptr<Module> shared_module;
|
||||
|
|
|
@ -133,9 +133,9 @@ int Processor::LoadBinary(const xechar_t* path, uint32_t start_address,
|
|||
addr, length));
|
||||
|
||||
// Prepare the module.
|
||||
char name_a[2048];
|
||||
char name_a[XE_MAX_PATH];
|
||||
XEEXPECTTRUE(xestrnarrow(name_a, XECOUNT(name_a), name));
|
||||
char path_a[2048];
|
||||
char path_a[XE_MAX_PATH];
|
||||
XEEXPECTTRUE(xestrnarrow(path_a, XECOUNT(path_a), path));
|
||||
|
||||
exec_module = new ExecModule(
|
||||
|
|
|
@ -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_;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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_;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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_;
|
||||
|
|
|
@ -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();
|
||||
// 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) {
|
||||
|
|
|
@ -48,7 +48,7 @@ private:
|
|||
int LoadPE();
|
||||
|
||||
char name_[256];
|
||||
char path_[2048];
|
||||
char path_[XE_MAX_PATH];
|
||||
|
||||
xe_xex2_ref xex_;
|
||||
};
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -70,10 +70,32 @@ int Run::Launch(const xechar_t* path) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
// TODO(benvanik): wait until the module thread exits
|
||||
runtime_->LaunchModule(path);
|
||||
// Normalize the path and make absolute.
|
||||
// TODO(benvanik): move this someplace common.
|
||||
xechar_t abs_path[XE_MAX_PATH];
|
||||
#if XE_PLATFORM(WIN32)
|
||||
_wfullpath(abs_path, path, XECOUNT(abs_path));
|
||||
#else
|
||||
realpath(path, abs_path);
|
||||
#endif // WIN32
|
||||
|
||||
return 0;
|
||||
// Grab file extension.
|
||||
const xechar_t* dot = xestrrchr(abs_path, '.');
|
||||
if (!dot) {
|
||||
XELOGE(XT("Invalid input path; no extension found"));
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Launch based on file type.
|
||||
// This is a silly guess based on file extension.
|
||||
// NOTE: the runtime launch routine will wait until the module exits.
|
||||
if (xestrcmp(dot, XT(".xex")) == 0) {
|
||||
// Treat as a naked xex file.
|
||||
return runtime_->LaunchXexFile(abs_path);
|
||||
} else {
|
||||
// Assume a disc image.
|
||||
return runtime_->LaunchDiscImage(abs_path);
|
||||
}
|
||||
}
|
||||
|
||||
int xenia_run(int argc, xechar_t **argv) {
|
||||
|
|
Loading…
Reference in New Issue