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_SDB 1
|
||||||
#define XE_OPTION_LOG_GPU 1
|
#define XE_OPTION_LOG_GPU 1
|
||||||
#define XE_OPTION_LOG_KERNEL 1
|
#define XE_OPTION_LOG_KERNEL 1
|
||||||
|
#define XE_OPTION_LOG_FS 1
|
||||||
|
|
||||||
|
|
||||||
// TODO(benvanik): make this a runtime option
|
// TODO(benvanik): make this a runtime option
|
||||||
|
|
|
@ -23,13 +23,17 @@ namespace fs {
|
||||||
|
|
||||||
class Device {
|
class Device {
|
||||||
public:
|
public:
|
||||||
Device(xe_pal_ref pal);
|
Device(xe_pal_ref pal, const char* path);
|
||||||
virtual ~Device();
|
virtual ~Device();
|
||||||
|
|
||||||
|
xe_pal_ref pal();
|
||||||
|
const char* path();
|
||||||
|
|
||||||
virtual Entry* ResolvePath(const char* path) = 0;
|
virtual Entry* ResolvePath(const char* path) = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
xe_pal_ref pal_;
|
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 {
|
class FileEntry : public Entry {
|
||||||
public:
|
public:
|
||||||
FileEntry(Device* device, const char* path);
|
FileEntry(Device* device, const char* path);
|
||||||
virtual ~FileEntry();
|
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);
|
DirectoryEntry(Device* device, const char* path);
|
||||||
virtual ~DirectoryEntry();
|
virtual ~DirectoryEntry();
|
||||||
|
|
||||||
//virtual void Query();
|
//virtual void Query() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,8 @@
|
||||||
#include <xenia/common.h>
|
#include <xenia/common.h>
|
||||||
#include <xenia/core.h>
|
#include <xenia/core.h>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include <xenia/kernel/fs/entry.h>
|
#include <xenia/kernel/fs/entry.h>
|
||||||
|
|
||||||
|
|
||||||
|
@ -40,7 +42,9 @@ public:
|
||||||
Entry* ResolvePath(const char* path);
|
Entry* ResolvePath(const char* path);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
xe_pal_ref pal_;
|
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<ExportResolver> export_resolver();
|
||||||
shared_ptr<fs::FileSystem> filesystem();
|
shared_ptr<fs::FileSystem> filesystem();
|
||||||
|
|
||||||
int LaunchModule(const xechar_t* path);
|
int LaunchXexFile(const xechar_t* path);
|
||||||
|
int LaunchDiscImage(const xechar_t* path);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
xechar_t command_line_[2048];
|
xechar_t command_line_[XE_MAX_PATH];
|
||||||
|
|
||||||
xe_pal_ref pal_;
|
xe_pal_ref pal_;
|
||||||
xe_memory_ref memory_;
|
xe_memory_ref memory_;
|
||||||
|
|
|
@ -34,6 +34,7 @@ typedef uint32_t X_STATUS;
|
||||||
#define X_STATUS_ACCESS_VIOLATION ((uint32_t)0xC0000005L)
|
#define X_STATUS_ACCESS_VIOLATION ((uint32_t)0xC0000005L)
|
||||||
#define X_STATUS_INVALID_HANDLE ((uint32_t)0xC0000008L)
|
#define X_STATUS_INVALID_HANDLE ((uint32_t)0xC0000008L)
|
||||||
#define X_STATUS_INVALID_PARAMETER ((uint32_t)0xC000000DL)
|
#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_NO_MEMORY ((uint32_t)0xC0000017L)
|
||||||
#define X_STATUS_ALREADY_COMMITTED ((uint32_t)0xC0000021L)
|
#define X_STATUS_ALREADY_COMMITTED ((uint32_t)0xC0000021L)
|
||||||
#define X_STATUS_ACCESS_DENIED ((uint32_t)0xC0000022L)
|
#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
|
#else
|
||||||
#define XELOGKERNEL(fmt, ...) XE_EMPTY_MACRO
|
#define XELOGKERNEL(fmt, ...) XE_EMPTY_MACRO
|
||||||
#endif
|
#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_
|
#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 xestrdupa _strdup
|
||||||
#define xestrtoullw _wcstoui64
|
#define xestrtoullw _wcstoui64
|
||||||
#define xestrtoulla _strtoui64
|
#define xestrtoulla _strtoui64
|
||||||
#endif
|
#endif // !WIN32
|
||||||
|
|
||||||
#define xestrlenw wcslen
|
#define xestrlenw wcslen
|
||||||
#define xestrcmpw wcscmp
|
#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 xestrchrw wcschr
|
||||||
#define xestrrchrw wcsrchr
|
#define xestrrchrw wcsrchr
|
||||||
#define xestrstrw wcsstr
|
#define xestrstrw wcsstr
|
||||||
|
#define xestrcasestrw ??
|
||||||
#define xestrcpyw(dest, destLength, source) (wcscpy_s(dest, destLength, source) == 0)
|
#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 xestrncpyw(dest, destLength, source, count) (wcsncpy_s(dest, destLength, source, count) == 0)
|
||||||
#define xestrcatw(dest, destLength, source) (wcscat_s(dest, destLength, source) == 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 xestrchra strchr
|
||||||
#define xestrrchra strrchr
|
#define xestrrchra strrchr
|
||||||
#define xestrstra strstr
|
#define xestrstra strstr
|
||||||
|
#define xestrcasestra strcasestr
|
||||||
#define xestrcpya(dest, destLength, source) (strcpy_s(dest, destLength, source) == 0)
|
#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 xestrncpya(dest, destLength, source, count) (strncpy_s(dest, destLength, source, count) == 0)
|
||||||
#define xestrcata(dest, destLength, source) (strcat_s(dest, destLength, source) == 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 xestrchr xestrchrw
|
||||||
#define xestrrchr xestrrchrw
|
#define xestrrchr xestrrchrw
|
||||||
#define xestrstr xestrstrw
|
#define xestrstr xestrstrw
|
||||||
|
#define xestrcasestr xestrcasestrw
|
||||||
#define xestrtoull xestrtoullw
|
#define xestrtoull xestrtoullw
|
||||||
#define xestrcpy xestrcpyw
|
#define xestrcpy xestrcpyw
|
||||||
#define xestrncpy xestrncpyw
|
#define xestrncpy xestrncpyw
|
||||||
|
@ -94,6 +97,7 @@ typedef char xechar_t;
|
||||||
#define xestrchr xestrchra
|
#define xestrchr xestrchra
|
||||||
#define xestrrchr xestrrchra
|
#define xestrrchr xestrrchra
|
||||||
#define xestrstr xestrstra
|
#define xestrstr xestrstra
|
||||||
|
#define xestrcasestr xestrcasestra
|
||||||
#define xestrtoull xestrtoulla
|
#define xestrtoull xestrtoulla
|
||||||
#define xestrcpy xestrcpya
|
#define xestrcpy xestrcpya
|
||||||
#define xestrncpy xestrncpya
|
#define xestrncpy xestrncpya
|
||||||
|
@ -109,8 +113,10 @@ typedef char xechar_t;
|
||||||
|
|
||||||
#if XE_LIKE(WIN32)
|
#if XE_LIKE(WIN32)
|
||||||
#define XE_PATH_SEPARATOR ((xechar_t)'\\')
|
#define XE_PATH_SEPARATOR ((xechar_t)'\\')
|
||||||
|
#define XE_MAX_PATH _MAX_PATH
|
||||||
#else
|
#else
|
||||||
#define XE_PATH_SEPARATOR ((xechar_t)'/')
|
#define XE_PATH_SEPARATOR ((xechar_t)'/')
|
||||||
|
#define XE_MAX_PATH PATH_MAX
|
||||||
#endif // WIN32
|
#endif // WIN32
|
||||||
|
|
||||||
#endif // XENIA_STRING_H_
|
#endif // XENIA_STRING_H_
|
||||||
|
|
|
@ -71,7 +71,7 @@ int ModuleGenerator::Generate() {
|
||||||
// Setup a debug info builder.
|
// Setup a debug info builder.
|
||||||
// This is used when creating any debug info. We may want to go more
|
// This is used when creating any debug info. We may want to go more
|
||||||
// fine grained than this, but for now it's something.
|
// 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_));
|
XEIGNORE(xestrcpya(dir, XECOUNT(dir), module_path_));
|
||||||
char* slash = xestrrchra(dir, '/');
|
char* slash = xestrrchra(dir, '/');
|
||||||
if (slash) {
|
if (slash) {
|
||||||
|
|
|
@ -97,7 +97,7 @@ int ExecModule::Prepare() {
|
||||||
int result_code = 1;
|
int result_code = 1;
|
||||||
std::string error_message;
|
std::string error_message;
|
||||||
|
|
||||||
char file_name[2048];
|
char file_name[XE_MAX_PATH];
|
||||||
|
|
||||||
OwningPtr<MemoryBuffer> shared_module_buffer;
|
OwningPtr<MemoryBuffer> shared_module_buffer;
|
||||||
auto_ptr<Module> shared_module;
|
auto_ptr<Module> shared_module;
|
||||||
|
|
|
@ -133,9 +133,9 @@ int Processor::LoadBinary(const xechar_t* path, uint32_t start_address,
|
||||||
addr, length));
|
addr, length));
|
||||||
|
|
||||||
// Prepare the module.
|
// Prepare the module.
|
||||||
char name_a[2048];
|
char name_a[XE_MAX_PATH];
|
||||||
XEEXPECTTRUE(xestrnarrow(name_a, XECOUNT(name_a), name));
|
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));
|
XEEXPECTTRUE(xestrnarrow(path_a, XECOUNT(path_a), path));
|
||||||
|
|
||||||
exec_module = new ExecModule(
|
exec_module = new ExecModule(
|
||||||
|
|
|
@ -15,10 +15,20 @@ using namespace xe::kernel;
|
||||||
using namespace xe::kernel::fs;
|
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);
|
pal_ = xe_pal_retain(pal);
|
||||||
|
path_ = xestrdupa(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
Device::~Device() {
|
Device::~Device() {
|
||||||
|
xe_free(path_);
|
||||||
xe_pal_release(pal_);
|
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;
|
using namespace xe::kernel::fs;
|
||||||
|
|
||||||
|
|
||||||
DiscImageDevice::DiscImageDevice(xe_pal_ref pal, const xechar_t* local_path) :
|
DiscImageDevice::DiscImageDevice(xe_pal_ref pal, const char* path,
|
||||||
Device(pal) {
|
const xechar_t* local_path) :
|
||||||
|
Device(pal, path) {
|
||||||
local_path_ = xestrdup(local_path);
|
local_path_ = xestrdup(local_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,5 +26,11 @@ DiscImageDevice::~DiscImageDevice() {
|
||||||
}
|
}
|
||||||
|
|
||||||
Entry* DiscImageDevice::ResolvePath(const char* path) {
|
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;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ namespace fs {
|
||||||
|
|
||||||
class DiscImageDevice : public Device {
|
class DiscImageDevice : public Device {
|
||||||
public:
|
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 ~DiscImageDevice();
|
||||||
|
|
||||||
virtual Entry* ResolvePath(const char* path);
|
virtual Entry* ResolvePath(const char* path);
|
||||||
|
|
|
@ -15,9 +15,64 @@ using namespace xe::kernel;
|
||||||
using namespace xe::kernel::fs;
|
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) :
|
const xechar_t* local_path) :
|
||||||
Device(pal) {
|
Device(pal, path) {
|
||||||
local_path_ = xestrdup(local_path);
|
local_path_ = xestrdup(local_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,5 +81,32 @@ LocalDirectoryDevice::~LocalDirectoryDevice() {
|
||||||
}
|
}
|
||||||
|
|
||||||
Entry* LocalDirectoryDevice::ResolvePath(const char* path) {
|
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 {
|
class LocalDirectoryDevice : public Device {
|
||||||
public:
|
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 ~LocalDirectoryDevice();
|
||||||
|
|
||||||
virtual Entry* ResolvePath(const char* path);
|
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) :
|
FileEntry::FileEntry(Device* device, const char* path) :
|
||||||
Entry(kTypeFile, device, path) {
|
Entry(kTypeFile, device, path) {
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,33 +23,91 @@ FileSystem::FileSystem(xe_pal_ref pal) {
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
||||||
|
|
||||||
xe_pal_release(pal_);
|
xe_pal_release(pal_);
|
||||||
}
|
}
|
||||||
|
|
||||||
int FileSystem::RegisterDevice(const char* path, Device* device) {
|
int FileSystem::RegisterDevice(const char* path, Device* device) {
|
||||||
return 1;
|
devices_.push_back(device);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int FileSystem::RegisterLocalDirectoryDevice(
|
int FileSystem::RegisterLocalDirectoryDevice(
|
||||||
const char* path, const xechar_t* local_path) {
|
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);
|
return RegisterDevice(path, device);
|
||||||
}
|
}
|
||||||
|
|
||||||
int FileSystem::RegisterDiscImageDevice(
|
int FileSystem::RegisterDiscImageDevice(
|
||||||
const char* path, const xechar_t* local_path) {
|
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);
|
return RegisterDevice(path, device);
|
||||||
}
|
}
|
||||||
|
|
||||||
int FileSystem::CreateSymbolicLink(const char* path, const char* target) {
|
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) {
|
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;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Entry* FileSystem::ResolvePath(const char* path) {
|
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;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@ KernelState::KernelState(Runtime* runtime) :
|
||||||
pal_ = runtime->pal();
|
pal_ = runtime->pal();
|
||||||
memory_ = runtime->memory();
|
memory_ = runtime->memory();
|
||||||
processor_ = runtime->processor();
|
processor_ = runtime->processor();
|
||||||
|
filesystem_ = runtime->filesystem();
|
||||||
|
|
||||||
objects_mutex_ = xe_mutex_alloc(0);
|
objects_mutex_ = xe_mutex_alloc(0);
|
||||||
XEASSERTNOTNULL(objects_mutex_);
|
XEASSERTNOTNULL(objects_mutex_);
|
||||||
|
@ -65,6 +66,7 @@ KernelState::~KernelState() {
|
||||||
xe_mutex_free(objects_mutex_);
|
xe_mutex_free(objects_mutex_);
|
||||||
objects_mutex_ = NULL;
|
objects_mutex_ = NULL;
|
||||||
|
|
||||||
|
filesystem_.reset();
|
||||||
processor_.reset();
|
processor_.reset();
|
||||||
xe_memory_release(memory_);
|
xe_memory_release(memory_);
|
||||||
xe_pal_release(pal_);
|
xe_pal_release(pal_);
|
||||||
|
@ -86,6 +88,10 @@ cpu::Processor* KernelState::processor() {
|
||||||
return processor_.get();
|
return processor_.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fs::FileSystem* KernelState::filesystem() {
|
||||||
|
return filesystem_.get();
|
||||||
|
}
|
||||||
|
|
||||||
// TODO(benvanik): invesitgate better handle storage/structure.
|
// TODO(benvanik): invesitgate better handle storage/structure.
|
||||||
// A much better way of doing handles, if performance becomes an issue, would
|
// 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
|
// 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/export.h>
|
||||||
#include <xenia/kernel/kernel_module.h>
|
#include <xenia/kernel/kernel_module.h>
|
||||||
#include <xenia/kernel/xbox.h>
|
#include <xenia/kernel/xbox.h>
|
||||||
|
#include <xenia/kernel/fs/filesystem.h>
|
||||||
|
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
|
@ -37,6 +38,7 @@ public:
|
||||||
xe_pal_ref pal();
|
xe_pal_ref pal();
|
||||||
xe_memory_ref memory();
|
xe_memory_ref memory();
|
||||||
cpu::Processor* processor();
|
cpu::Processor* processor();
|
||||||
|
fs::FileSystem* filesystem();
|
||||||
|
|
||||||
XObject* GetObject(X_HANDLE handle);
|
XObject* GetObject(X_HANDLE handle);
|
||||||
|
|
||||||
|
@ -52,6 +54,7 @@ private:
|
||||||
xe_pal_ref pal_;
|
xe_pal_ref pal_;
|
||||||
xe_memory_ref memory_;
|
xe_memory_ref memory_;
|
||||||
shared_ptr<cpu::Processor> processor_;
|
shared_ptr<cpu::Processor> processor_;
|
||||||
|
shared_ptr<fs::FileSystem> filesystem_;
|
||||||
|
|
||||||
XModule* executable_module_;
|
XModule* executable_module_;
|
||||||
|
|
||||||
|
|
|
@ -104,36 +104,24 @@ XboxkrnlModule::XboxkrnlModule(Runtime* runtime) :
|
||||||
XboxkrnlModule::~XboxkrnlModule() {
|
XboxkrnlModule::~XboxkrnlModule() {
|
||||||
}
|
}
|
||||||
|
|
||||||
int XboxkrnlModule::LaunchModule(const xechar_t* path) {
|
int XboxkrnlModule::LaunchModule(const char* path) {
|
||||||
// TODO(benvanik): setup the virtual filesystem map and use LoadFromFile.
|
// 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);
|
// Load the module into memory from the filesystem.
|
||||||
if (!mmap) {
|
X_STATUS result_code = module->LoadFromFile(path);
|
||||||
return NULL;
|
if (XFAILED(result_code)) {
|
||||||
}
|
XELOGE(XT("Failed to load module %s: %.8X"), path, result_code);
|
||||||
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"));
|
|
||||||
module->Release();
|
module->Release();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Launch the module.
|
// Launch the module.
|
||||||
status = module->Launch(0);
|
// NOTE: this won't return until the module exits.
|
||||||
if (XFAILED(status)) {
|
result_code = module->Launch(0);
|
||||||
XELOGE(XT("Failed to launch module"));
|
if (XFAILED(result_code)) {
|
||||||
|
XELOGE(XT("Failed to launch module %s: %.8X"), path, result_code);
|
||||||
module->Release();
|
module->Release();
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ public:
|
||||||
XboxkrnlModule(Runtime* runtime);
|
XboxkrnlModule(Runtime* runtime);
|
||||||
virtual ~XboxkrnlModule();
|
virtual ~XboxkrnlModule();
|
||||||
|
|
||||||
int LaunchModule(const xechar_t* path);
|
int LaunchModule(const char* path);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
auto_ptr<KernelState> kernel_state_;
|
auto_ptr<KernelState> kernel_state_;
|
||||||
|
|
|
@ -24,10 +24,13 @@ namespace {
|
||||||
XModule::XModule(KernelState* kernel_state, const char* path) :
|
XModule::XModule(KernelState* kernel_state, const char* path) :
|
||||||
XObject(kernel_state, kTypeModule),
|
XObject(kernel_state, kTypeModule),
|
||||||
xex_(NULL) {
|
xex_(NULL) {
|
||||||
XEIGNORE(xestrcpy(path_, XECOUNT(path_), path));
|
XEIGNORE(xestrcpya(path_, XECOUNT(path_), path));
|
||||||
const xechar_t *slash = xestrrchr(path, '/');
|
const xechar_t *slash = xestrrchr(path, '/');
|
||||||
|
if (!slash) {
|
||||||
|
slash = xestrrchr(path, '\\');
|
||||||
|
}
|
||||||
if (slash) {
|
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) {
|
X_STATUS XModule::LoadFromFile(const char* path) {
|
||||||
// TODO(benvanik): load from virtual filesystem.
|
// Resolve the file to open.
|
||||||
XEASSERTALWAYS();
|
// TODO(benvanik): make this code shared?
|
||||||
return X_STATUS_UNSUCCESSFUL;
|
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) {
|
X_STATUS XModule::LoadFromMemory(const void* addr, const size_t length) {
|
||||||
|
|
|
@ -48,7 +48,7 @@ private:
|
||||||
int LoadPE();
|
int LoadPE();
|
||||||
|
|
||||||
char name_[256];
|
char name_[256];
|
||||||
char path_[2048];
|
char path_[XE_MAX_PATH];
|
||||||
|
|
||||||
xe_xex2_ref xex_;
|
xe_xex2_ref xex_;
|
||||||
};
|
};
|
||||||
|
|
|
@ -63,6 +63,79 @@ shared_ptr<FileSystem> Runtime::filesystem() {
|
||||||
return filesystem_;
|
return filesystem_;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Runtime::LaunchModule(const xechar_t* path) {
|
int Runtime::LaunchXexFile(const xechar_t* path) {
|
||||||
return xboxkrnl_->LaunchModule(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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(benvanik): wait until the module thread exits
|
// Normalize the path and make absolute.
|
||||||
runtime_->LaunchModule(path);
|
// 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) {
|
int xenia_run(int argc, xechar_t **argv) {
|
||||||
|
|
Loading…
Reference in New Issue