diff --git a/libxenia.vcxproj b/libxenia.vcxproj
index 38887e395..ddb29f09f 100644
--- a/libxenia.vcxproj
+++ b/libxenia.vcxproj
@@ -205,8 +205,6 @@
-
-
CompileAsCpp
@@ -460,8 +458,6 @@
-
-
diff --git a/libxenia.vcxproj.filters b/libxenia.vcxproj.filters
index f9b82b911..cd19ffd7e 100644
--- a/libxenia.vcxproj.filters
+++ b/libxenia.vcxproj.filters
@@ -745,12 +745,6 @@
src\xenia\vfs
-
- src\xenia\vfs
-
-
- src\xenia\vfs
-
src\xenia\vfs\devices
@@ -1452,12 +1446,6 @@
src\xenia\vfs
-
- src\xenia\vfs
-
-
- src\xenia\vfs
-
src\xenia\vfs\devices
diff --git a/src/xenia/base/filesystem.h b/src/xenia/base/filesystem.h
index 11b83cf9a..b4b0b23bf 100644
--- a/src/xenia/base/filesystem.h
+++ b/src/xenia/base/filesystem.h
@@ -33,6 +33,9 @@ struct FileInfo {
Type type;
std::wstring name;
size_t total_size;
+ uint64_t create_timestamp;
+ uint64_t access_timestamp;
+ uint64_t write_timestamp;
};
std::vector ListFiles(const std::wstring& path);
diff --git a/src/xenia/base/filesystem_win.cc b/src/xenia/base/filesystem_win.cc
index 2ad7346ec..05e99fe8d 100644
--- a/src/xenia/base/filesystem_win.cc
+++ b/src/xenia/base/filesystem_win.cc
@@ -43,6 +43,8 @@ bool DeleteFolder(const std::wstring& path) {
return SHFileOperation(&op) == 0;
}
+#define COMBINE_TIME(t) (((uint64_t)t.dwHighDateTime << 32) | t.dwLowDateTime)
+
std::vector ListFiles(const std::wstring& path) {
std::vector result;
@@ -66,6 +68,9 @@ std::vector ListFiles(const std::wstring& path) {
(ffd.nFileSizeHigh * (size_t(MAXDWORD) + 1)) + ffd.nFileSizeLow;
}
info.name = ffd.cFileName;
+ info.create_timestamp = COMBINE_TIME(ffd.ftCreationTime);
+ info.access_timestamp = COMBINE_TIME(ffd.ftLastAccessTime);
+ info.write_timestamp = COMBINE_TIME(ffd.ftLastWriteTime);
result.push_back(info);
} while (FindNextFile(handle, &ffd) != 0);
FindClose(handle);
diff --git a/src/xenia/emulator.cc b/src/xenia/emulator.cc
index 07cb8ecc3..466b54319 100644
--- a/src/xenia/emulator.cc
+++ b/src/xenia/emulator.cc
@@ -209,6 +209,10 @@ X_STATUS Emulator::LaunchXexFile(std::wstring path) {
auto parent_path = xe::find_base_path(path);
auto device =
std::make_unique(mount_path, parent_path, true);
+ if (!device->Initialize()) {
+ XELOGE("Unable to scan host path");
+ return X_STATUS_NO_SUCH_FILE;
+ }
if (!file_system_->RegisterDevice(std::move(device))) {
XELOGE("Unable to register host path");
return X_STATUS_NO_SUCH_FILE;
diff --git a/src/xenia/kernel/objects/xfile.cc b/src/xenia/kernel/objects/xfile.cc
index 99c496c28..565632918 100644
--- a/src/xenia/kernel/objects/xfile.cc
+++ b/src/xenia/kernel/objects/xfile.cc
@@ -9,14 +9,19 @@
#include "xenia/kernel/objects/xfile.h"
+#include "xenia/base/math.h"
#include "xenia/kernel/async_request.h"
#include "xenia/kernel/objects/xevent.h"
namespace xe {
namespace kernel {
-XFile::XFile(KernelState* kernel_state, vfs::Mode mode)
- : mode_(mode), position_(0), XObject(kernel_state, kTypeFile) {
+XFile::XFile(KernelState* kernel_state, vfs::Mode mode, vfs::Entry* entry)
+ : XObject(kernel_state, kTypeFile),
+ entry_(entry),
+ mode_(mode),
+ position_(0),
+ find_index_(0) {
async_event_ = new XEvent(kernel_state);
async_event_->Initialize(false, false);
}
@@ -29,6 +34,58 @@ XFile::~XFile() {
void* XFile::GetWaitHandle() { return async_event_->GetWaitHandle(); }
+X_STATUS XFile::QueryDirectory(X_FILE_DIRECTORY_INFORMATION* out_info,
+ size_t length, const char* file_name,
+ bool restart) {
+ assert_not_null(out_info);
+
+ vfs::Entry* entry = nullptr;
+
+ if (file_name != nullptr) {
+ // Only queries in the current directory are supported for now.
+ assert_true(std::strchr(file_name, '\\') == nullptr);
+
+ find_engine_.SetRule(file_name);
+
+ // Always restart the search?
+ find_index_ = 0;
+ entry = entry_->IterateChildren(find_engine_, &find_index_);
+ if (!entry) {
+ return X_STATUS_NO_SUCH_FILE;
+ }
+ } else {
+ if (restart) {
+ find_index_ = 0;
+ }
+
+ entry = entry_->IterateChildren(find_engine_, &find_index_);
+ if (!entry) {
+ return X_STATUS_NO_SUCH_FILE;
+ }
+ }
+
+ auto end = (uint8_t*)out_info + length;
+ const auto& entry_name = entry->name();
+ if (((uint8_t*)&out_info->file_name[0]) + entry_name.size() > end) {
+ assert_always("Buffer overflow?");
+ return X_STATUS_NO_SUCH_FILE;
+ }
+
+ out_info->next_entry_offset = 0;
+ out_info->file_index = static_cast(find_index_);
+ out_info->creation_time = entry->create_timestamp();
+ out_info->last_access_time = entry->access_timestamp();
+ out_info->last_write_time = entry->write_timestamp();
+ out_info->change_time = entry->write_timestamp();
+ out_info->end_of_file = entry->size();
+ out_info->allocation_size = entry->allocation_size();
+ out_info->attributes = entry->attributes();
+ out_info->file_name_length = static_cast(entry_name.size());
+ std::memcpy(out_info->file_name, entry_name.data(), entry_name.size());
+
+ return X_STATUS_SUCCESS;
+}
+
X_STATUS XFile::Read(void* buffer, size_t buffer_length, size_t byte_offset,
size_t* out_bytes_read) {
if (byte_offset == -1) {
diff --git a/src/xenia/kernel/objects/xfile.h b/src/xenia/kernel/objects/xfile.h
index 967f49a2c..f449294c1 100644
--- a/src/xenia/kernel/objects/xfile.h
+++ b/src/xenia/kernel/objects/xfile.h
@@ -10,7 +10,9 @@
#ifndef XENIA_KERNEL_XBOXKRNL_XFILE_H_
#define XENIA_KERNEL_XBOXKRNL_XFILE_H_
+#include "xenia/base/filesystem.h"
#include "xenia/kernel/xobject.h"
+#include "xenia/vfs/device.h"
#include "xenia/vfs/entry.h"
#include "xenia/xbox.h"
@@ -29,7 +31,7 @@ struct X_FILE_NETWORK_OPEN_INFORMATION {
xe::be change_time;
xe::be allocation_size;
xe::be end_of_file; // size in bytes
- xe::be attributes;
+ xe::be attributes;
xe::be pad;
};
@@ -45,7 +47,7 @@ class X_FILE_DIRECTORY_INFORMATION {
uint64_t change_time;
uint64_t end_of_file; // size in bytes
uint64_t allocation_size;
- X_FILE_ATTRIBUTES attributes;
+ uint32_t attributes; // X_FILE_ATTRIBUTES
uint32_t file_name_length;
char file_name[1];
@@ -75,20 +77,19 @@ static_assert_size(X_FILE_DIRECTORY_INFORMATION, 72);
class XFile : public XObject {
public:
- virtual ~XFile();
+ ~XFile() override;
- virtual const std::string& path() const = 0;
- virtual const std::string& name() const = 0;
+ vfs::Device* device() const { return entry_->device(); }
+ vfs::Entry* entry() const { return entry_; }
- virtual vfs::Device* device() const = 0;
+ const std::string& path() const { return entry_->path(); }
+ const std::string& name() const { return entry_->name(); }
size_t position() const { return position_; }
void set_position(size_t value) { position_ = value; }
- virtual X_STATUS QueryInfo(X_FILE_NETWORK_OPEN_INFORMATION* out_info) = 0;
- virtual X_STATUS QueryDirectory(X_FILE_DIRECTORY_INFORMATION* out_info,
- size_t length, const char* file_name,
- bool restart) = 0;
+ X_STATUS QueryDirectory(X_FILE_DIRECTORY_INFORMATION* out_info, size_t length,
+ const char* file_name, bool restart);
X_STATUS Read(void* buffer, size_t buffer_length, size_t byte_offset,
size_t* out_bytes_read);
@@ -101,7 +102,7 @@ class XFile : public XObject {
virtual void* GetWaitHandle();
protected:
- XFile(KernelState* kernel_state, vfs::Mode mode);
+ XFile(KernelState* kernel_state, vfs::Mode mode, vfs::Entry* entry);
virtual X_STATUS ReadSync(void* buffer, size_t buffer_length,
size_t byte_offset, size_t* out_bytes_read) = 0;
virtual X_STATUS WriteSync(const void* buffer, size_t buffer_length,
@@ -110,12 +111,16 @@ class XFile : public XObject {
}
private:
+ vfs::Entry* entry_;
vfs::Mode mode_;
XEvent* async_event_;
// TODO(benvanik): create flags, open state, etc.
size_t position_;
+
+ xe::filesystem::WildcardEngine find_engine_;
+ size_t find_index_;
};
} // namespace kernel
diff --git a/src/xenia/kernel/objects/xuser_module.cc b/src/xenia/kernel/objects/xuser_module.cc
index 8e82db820..bb50d8cac 100644
--- a/src/xenia/kernel/objects/xuser_module.cc
+++ b/src/xenia/kernel/objects/xuser_module.cc
@@ -59,18 +59,11 @@ X_STATUS XUserModule::LoadFromFile(std::string path) {
// Load the module.
result = LoadFromMemory(mmap->data(), mmap->size());
} else {
- X_FILE_NETWORK_OPEN_INFORMATION file_info = {0};
- result = fs_entry->QueryInfo(&file_info);
- if (result) {
- return result;
- }
-
- std::vector buffer(file_info.end_of_file);
+ std::vector buffer(fs_entry->size());
// Open file for reading.
XFile* file_ptr = nullptr;
- result = kernel_state()->file_system()->Open(
- std::move(fs_entry), kernel_state(), vfs::Mode::READ, false, &file_ptr);
+ result = fs_entry->Open(kernel_state(), vfs::Mode::READ, false, &file_ptr);
object_ref file(file_ptr);
if (result) {
return result;
diff --git a/src/xenia/kernel/xboxkrnl_io.cc b/src/xenia/kernel/xboxkrnl_io.cc
index 5101912b1..84aa246b0 100644
--- a/src/xenia/kernel/xboxkrnl_io.cc
+++ b/src/xenia/kernel/xboxkrnl_io.cc
@@ -187,7 +187,7 @@ X_STATUS NtCreateFile(PPCContext* ppc_context, KernelState* kernel_state,
uint32_t handle;
auto fs = kernel_state->file_system();
- std::unique_ptr entry;
+ Entry* entry;
object_ref root_file;
if (object_attrs->root_directory != 0xFFFFFFFD && // ObDosDevices
@@ -233,9 +233,9 @@ X_STATUS NtCreateFile(PPCContext* ppc_context, KernelState* kernel_state,
mode = vfs::Mode::READ;
}
XFile* file_ptr = nullptr;
- result = fs->Open(std::move(entry), kernel_state, mode,
- false, // TODO(benvanik): pick async mode, if needed.
- &file_ptr);
+ result = entry->Open(kernel_state, mode,
+ false, // TODO(benvanik): pick async mode, if needed.
+ &file_ptr);
file = object_ref(file_ptr);
}
@@ -618,10 +618,14 @@ dword_result_t NtQueryInformationFile(
assert_true(length == 56);
auto file_info = file_info_ptr.as();
- result = file->QueryInfo(file_info);
- if (XSUCCEEDED(result)) {
- info = 56;
- }
+ file_info->creation_time = file->entry()->create_timestamp();
+ file_info->last_access_time = file->entry()->access_timestamp();
+ file_info->last_write_time = file->entry()->write_timestamp();
+ file_info->change_time = file->entry()->write_timestamp();
+ file_info->allocation_size = file->entry()->allocation_size();
+ file_info->end_of_file = file->entry()->size();
+ file_info->attributes = file->entry()->attributes();
+ info = 56;
break;
}
case XFileXctdCompressionInformation:
@@ -701,10 +705,19 @@ SHIM_CALL NtQueryFullAttributesFile_shim(PPCContext* ppc_context,
auto entry = fs->ResolvePath(object_name);
if (entry) {
// Found.
+ result = X_STATUS_SUCCESS;
+
auto file_info =
kernel_memory()->TranslateVirtual(
file_info_ptr);
- result = entry->QueryInfo(file_info);
+
+ file_info->creation_time = entry->create_timestamp();
+ file_info->last_access_time = entry->access_timestamp();
+ file_info->last_write_time = entry->write_timestamp();
+ file_info->change_time = entry->write_timestamp();
+ file_info->allocation_size = entry->allocation_size();
+ file_info->end_of_file = entry->size();
+ file_info->attributes = entry->attributes();
}
free(object_name);
diff --git a/src/xenia/vfs/device.cc b/src/xenia/vfs/device.cc
index 18f4d1ea8..cb60d44e6 100644
--- a/src/xenia/vfs/device.cc
+++ b/src/xenia/vfs/device.cc
@@ -9,6 +9,8 @@
#include "xenia/vfs/device.h"
+#include "xenia/base/logging.h"
+
namespace xe {
namespace vfs {
@@ -16,5 +18,31 @@ Device::Device(const std::string& mount_path) : mount_path_(mount_path) {}
Device::~Device() = default;
+void Device::Dump(StringBuffer* string_buffer) {
+ if (root_entry_) {
+ root_entry_->Dump(string_buffer, 0);
+ }
+}
+
+Entry* Device::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("Device::ResolvePath(%s)", path);
+
+ // Walk the path, one separator at a time.
+ auto entry = root_entry_.get();
+ auto path_parts = xe::split_path(path);
+ for (auto& part : path_parts) {
+ entry = entry->GetChild(part);
+ if (!entry) {
+ // Not found.
+ return nullptr;
+ }
+ }
+ return entry;
+}
+
} // namespace vfs
} // namespace xe
diff --git a/src/xenia/vfs/device.h b/src/xenia/vfs/device.h
index 6c9ca8e17..2d378af25 100644
--- a/src/xenia/vfs/device.h
+++ b/src/xenia/vfs/device.h
@@ -13,6 +13,7 @@
#include
#include
+#include "xenia/base/string_buffer.h"
#include "xenia/vfs/entry.h"
namespace xe {
@@ -23,11 +24,14 @@ class Device {
Device(const std::string& path);
virtual ~Device();
+ virtual bool Initialize() = 0;
+ void Dump(StringBuffer* string_buffer);
+
const std::string& mount_path() const { return mount_path_; }
virtual bool is_read_only() const { return true; }
- virtual std::unique_ptr ResolvePath(const char* path) = 0;
+ Entry* ResolvePath(const char* path);
virtual uint32_t total_allocation_units() const = 0;
virtual uint32_t available_allocation_units() const = 0;
@@ -36,6 +40,7 @@ class Device {
protected:
std::string mount_path_;
+ std::unique_ptr root_entry_;
};
} // namespace vfs
diff --git a/src/xenia/vfs/devices/disc_image_device.cc b/src/xenia/vfs/devices/disc_image_device.cc
index 1e57b2baa..5d0768c43 100644
--- a/src/xenia/vfs/devices/disc_image_device.cc
+++ b/src/xenia/vfs/devices/disc_image_device.cc
@@ -11,17 +11,18 @@
#include "xenia/base/logging.h"
#include "xenia/base/math.h"
-#include "xenia/vfs/gdfx.h"
#include "xenia/vfs/devices/disc_image_entry.h"
namespace xe {
namespace vfs {
+const size_t kXESectorSize = 2048;
+
DiscImageDevice::DiscImageDevice(const std::string& mount_path,
const std::wstring& local_path)
- : Device(mount_path), local_path_(local_path), gdfx_(nullptr) {}
+ : Device(mount_path), local_path_(local_path) {}
-DiscImageDevice::~DiscImageDevice() { delete gdfx_; }
+DiscImageDevice::~DiscImageDevice() = default;
bool DiscImageDevice::Initialize() {
mmap_ = MappedMemory::Open(local_path_, MappedMemory::Mode::kRead);
@@ -30,38 +31,133 @@ bool DiscImageDevice::Initialize() {
return false;
}
- gdfx_ = new GDFX(mmap_.get());
- GDFX::Error error = gdfx_->Load();
- if (error != GDFX::kSuccess) {
- XELOGE("GDFX init failed: %d", error);
+ ParseState state = {0};
+ state.ptr = mmap_->data();
+ state.size = mmap_->size();
+ auto result = Verify(state);
+ if (result != Error::kSuccess) {
+ XELOGE("Failed to verify disc image header: %d", result);
return false;
}
- // gdfx_->Dump();
+ result = ReadAllEntries(state, state.ptr + state.root_offset);
+ if (result != Error::kSuccess) {
+ XELOGE("Failed to read all GDFX entries: %d", result);
+ return false;
+ }
return true;
}
-std::unique_ptr 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.
- auto path_parts = xe::split_path(path);
- for (auto& part : path_parts) {
- gdfx_entry = gdfx_entry->GetChild(part.c_str());
- if (!gdfx_entry) {
- // Not found.
- return nullptr;
+DiscImageDevice::Error DiscImageDevice::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 < xe::countof(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 Error::kErrorFileMismatch;
+ }
- return std::make_unique(this, path, mmap_.get(), gdfx_entry);
+ // Read sector 32 to get FS state.
+ if (state.size < state.game_offset + (32 * kXESectorSize)) {
+ return Error::kErrorReadError;
+ }
+ uint8_t* fs_ptr = state.ptr + state.game_offset + (32 * kXESectorSize);
+ state.root_sector = xe::load(fs_ptr + 20);
+ state.root_size = xe::load(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 Error::kErrorDamagedFile;
+ }
+
+ return Error::kSuccess;
+}
+
+bool DiscImageDevice::VerifyMagic(ParseState& state, size_t offset) {
+ // Simple check to see if the given offset contains the magic value.
+ return std::memcmp(state.ptr + offset, "MICROSOFT*XBOX*MEDIA", 20) == 0;
+}
+
+DiscImageDevice::Error DiscImageDevice::ReadAllEntries(
+ ParseState& state, const uint8_t* root_buffer) {
+ auto root_entry = new DiscImageEntry(this, "", mmap_.get());
+ root_entry->attributes_ = kFileAttributeDirectory;
+ root_entry_ = std::unique_ptr(root_entry);
+
+ if (!ReadEntry(state, root_buffer, 0, root_entry)) {
+ return Error::kErrorOutOfMemory;
+ }
+
+ return Error::kSuccess;
+}
+
+bool DiscImageDevice::ReadEntry(ParseState& state, const uint8_t* buffer,
+ uint16_t entry_ordinal,
+ DiscImageEntry* parent) {
+ const uint8_t* p = buffer + (entry_ordinal * 4);
+
+ uint16_t node_l = xe::load(p + 0);
+ uint16_t node_r = xe::load(p + 2);
+ size_t sector = xe::load(p + 4);
+ size_t length = xe::load(p + 8);
+ uint8_t attributes = xe::load(p + 12);
+ uint8_t name_length = xe::load(p + 13);
+ char* name = (char*)(p + 14);
+
+ if (node_l && !ReadEntry(state, buffer, node_l, parent)) {
+ return false;
+ }
+
+ auto entry =
+ new DiscImageEntry(this, std::string(name, name_length), mmap_.get());
+ entry->attributes_ = attributes | kFileAttributeReadOnly;
+ entry->size_ = length;
+ entry->allocation_size_ = xe::round_up(length, bytes_per_sector());
+ entry->create_timestamp_ = 0;
+ entry->access_timestamp_ = 0;
+ entry->write_timestamp_ = 0;
+
+ // Add to parent.
+ parent->children_.emplace_back(std::unique_ptr(entry));
+
+ if (attributes & kFileAttributeDirectory) {
+ // Folder.
+ entry->data_offset_ = 0;
+ entry->data_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->data_offset_ = state.game_offset + (sector * kXESectorSize);
+ entry->data_size_ = length;
+ }
+
+ // Read next file in the list.
+ if (node_r && !ReadEntry(state, buffer, node_r, parent)) {
+ return false;
+ }
+
+ return true;
}
} // namespace vfs
diff --git a/src/xenia/vfs/devices/disc_image_device.h b/src/xenia/vfs/devices/disc_image_device.h
index 1c1151c96..c0ce2ed38 100644
--- a/src/xenia/vfs/devices/disc_image_device.h
+++ b/src/xenia/vfs/devices/disc_image_device.h
@@ -19,7 +19,7 @@
namespace xe {
namespace vfs {
-class GDFX;
+class DiscImageEntry;
class DiscImageDevice : public Device {
public:
@@ -27,9 +27,7 @@ class DiscImageDevice : public Device {
const std::wstring& local_path);
~DiscImageDevice() override;
- bool Initialize();
-
- std::unique_ptr ResolvePath(const char* path) override;
+ bool Initialize() override;
uint32_t total_allocation_units() const override {
return uint32_t(mmap_->size() / sectors_per_allocation_unit() /
@@ -40,9 +38,31 @@ class DiscImageDevice : public Device {
uint32_t bytes_per_sector() const override { return 2 * 1024; }
private:
+ enum class Error {
+ kSuccess = 0,
+ kErrorOutOfMemory = -1,
+ kErrorReadError = -10,
+ kErrorFileMismatch = -30,
+ kErrorDamagedFile = -31,
+ };
+
std::wstring local_path_;
std::unique_ptr mmap_;
- GDFX* gdfx_;
+
+ 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, DiscImageEntry* parent);
};
} // namespace vfs
diff --git a/src/xenia/vfs/devices/disc_image_entry.cc b/src/xenia/vfs/devices/disc_image_entry.cc
index 36238dbc5..b05457b96 100644
--- a/src/xenia/vfs/devices/disc_image_entry.cc
+++ b/src/xenia/vfs/devices/disc_image_entry.cc
@@ -17,77 +17,11 @@
namespace xe {
namespace vfs {
-DiscImageEntry::DiscImageEntry(Device* device, const char* path,
- MappedMemory* mmap, GDFXEntry* gdfx_entry)
- : Entry(device, path),
- mmap_(mmap),
- gdfx_entry_(gdfx_entry),
- it_(gdfx_entry->children.begin()) {}
+DiscImageEntry::DiscImageEntry(Device* device, std::string path,
+ MappedMemory* mmap)
+ : Entry(device, path), mmap_(mmap), data_offset_(0), data_size_(0) {}
-DiscImageEntry::~DiscImageEntry() {}
-
-X_STATUS DiscImageEntry::QueryInfo(X_FILE_NETWORK_OPEN_INFORMATION* out_info) {
- assert_not_null(out_info);
- out_info->creation_time = 0;
- out_info->last_access_time = 0;
- out_info->last_write_time = 0;
- out_info->change_time = 0;
- out_info->allocation_size = xe::round_up(gdfx_entry_->size, 2048);
- out_info->end_of_file = gdfx_entry_->size;
- out_info->attributes = gdfx_entry_->attributes;
- return X_STATUS_SUCCESS;
-}
-
-X_STATUS DiscImageEntry::QueryDirectory(X_FILE_DIRECTORY_INFORMATION* out_info,
- size_t length, const char* file_name,
- bool restart) {
- assert_not_null(out_info);
-
- GDFXEntry* entry(nullptr);
-
- if (file_name != nullptr) {
- // Only queries in the current directory are supported for now
- assert_true(std::strchr(file_name, '\\') == nullptr);
-
- find_engine_.SetRule(file_name);
-
- // Always restart the search?
- it_ = gdfx_entry_->children.begin();
- entry = gdfx_entry_->GetChild(find_engine_, it_);
- if (!entry) {
- return X_STATUS_NO_SUCH_FILE;
- }
- } else {
- if (restart) {
- it_ = gdfx_entry_->children.begin();
- }
-
- entry = gdfx_entry_->GetChild(find_engine_, it_);
- if (!entry) {
- return X_STATUS_NO_SUCH_FILE;
- }
-
- auto end = (uint8_t*)out_info + length;
- auto entry_name = entry->name;
- if (((uint8_t*)&out_info->file_name[0]) + entry_name.size() > end) {
- return X_STATUS_NO_SUCH_FILE;
- }
- }
-
- out_info->next_entry_offset = 0;
- out_info->file_index = 0xCDCDCDCD;
- out_info->creation_time = 0;
- out_info->last_access_time = 0;
- out_info->last_write_time = 0;
- out_info->change_time = 0;
- out_info->end_of_file = entry->size;
- out_info->allocation_size = xe::round_up(entry->size, 2048);
- out_info->attributes = entry->attributes;
- out_info->file_name_length = static_cast(entry->name.size());
- memcpy(out_info->file_name, entry->name.c_str(), entry->name.size());
-
- return X_STATUS_SUCCESS;
-}
+DiscImageEntry::~DiscImageEntry() = default;
X_STATUS DiscImageEntry::Open(KernelState* kernel_state, Mode mode, bool async,
XFile** out_file) {
@@ -102,9 +36,8 @@ std::unique_ptr DiscImageEntry::OpenMapped(
return nullptr;
}
- size_t real_offset = gdfx_entry_->offset + offset;
- size_t real_length =
- length ? std::min(length, gdfx_entry_->size) : gdfx_entry_->size;
+ size_t real_offset = data_offset_ + offset;
+ size_t real_length = length ? std::min(length, data_size_) : data_size_;
return mmap_->Slice(mode, real_offset, real_length);
}
diff --git a/src/xenia/vfs/devices/disc_image_entry.h b/src/xenia/vfs/devices/disc_image_entry.h
index e7f94c2c3..a47612696 100644
--- a/src/xenia/vfs/devices/disc_image_entry.h
+++ b/src/xenia/vfs/devices/disc_image_entry.h
@@ -15,23 +15,20 @@
#include "xenia/base/filesystem.h"
#include "xenia/base/mapped_memory.h"
#include "xenia/vfs/entry.h"
-#include "xenia/vfs/gdfx.h"
namespace xe {
namespace vfs {
+class DiscImageDevice;
+
class DiscImageEntry : public Entry {
public:
- DiscImageEntry(Device* device, const char* path, MappedMemory* mmap,
- GDFXEntry* gdfx_entry);
+ DiscImageEntry(Device* device, std::string path, MappedMemory* mmap);
~DiscImageEntry() override;
MappedMemory* mmap() const { return mmap_; }
- GDFXEntry* gdfx_entry() const { return gdfx_entry_; }
-
- X_STATUS QueryInfo(X_FILE_NETWORK_OPEN_INFORMATION* out_info) override;
- X_STATUS QueryDirectory(X_FILE_DIRECTORY_INFORMATION* out_info, size_t length,
- const char* file_name, bool restart) override;
+ size_t data_offset() const { return data_offset_; }
+ size_t data_size() const { return data_size_; }
X_STATUS Open(KernelState* kernel_state, Mode mode, bool async,
XFile** out_file) override;
@@ -42,11 +39,11 @@ class DiscImageEntry : public Entry {
size_t length) override;
private:
- MappedMemory* mmap_;
- GDFXEntry* gdfx_entry_;
+ friend class DiscImageDevice;
- xe::filesystem::WildcardEngine find_engine_;
- GDFXEntry::child_it_t it_;
+ MappedMemory* mmap_;
+ size_t data_offset_;
+ size_t data_size_;
};
} // namespace vfs
diff --git a/src/xenia/vfs/devices/disc_image_file.cc b/src/xenia/vfs/devices/disc_image_file.cc
index 0629ea475..8a2e706f9 100644
--- a/src/xenia/vfs/devices/disc_image_file.cc
+++ b/src/xenia/vfs/devices/disc_image_file.cc
@@ -11,43 +11,25 @@
#include
-#include "xenia/vfs/device.h"
#include "xenia/vfs/devices/disc_image_entry.h"
-#include "xenia/vfs/gdfx.h"
namespace xe {
namespace vfs {
DiscImageFile::DiscImageFile(KernelState* kernel_state, Mode mode,
DiscImageEntry* entry)
- : XFile(kernel_state, mode), entry_(entry) {}
+ : XFile(kernel_state, mode, entry), entry_(entry) {}
-DiscImageFile::~DiscImageFile() { delete entry_; }
-
-const std::string& DiscImageFile::path() const { return entry_->path(); }
-
-const std::string& DiscImageFile::name() const { return entry_->name(); }
-
-Device* DiscImageFile::device() const { return entry_->device(); }
-
-X_STATUS DiscImageFile::QueryInfo(X_FILE_NETWORK_OPEN_INFORMATION* out_info) {
- return entry_->QueryInfo(out_info);
-}
-
-X_STATUS DiscImageFile::QueryDirectory(X_FILE_DIRECTORY_INFORMATION* out_info,
- size_t length, const char* file_name,
- bool restart) {
- return entry_->QueryDirectory(out_info, length, file_name, restart);
-}
+DiscImageFile::~DiscImageFile() = default;
X_STATUS DiscImageFile::ReadSync(void* buffer, size_t buffer_length,
size_t byte_offset, size_t* out_bytes_read) {
- GDFXEntry* gdfx_entry = entry_->gdfx_entry();
- if (byte_offset >= gdfx_entry->size) {
+ if (byte_offset >= entry_->size()) {
return X_STATUS_END_OF_FILE;
}
- size_t real_offset = gdfx_entry->offset + byte_offset;
- size_t real_length = std::min(buffer_length, gdfx_entry->size - byte_offset);
+ size_t real_offset = entry_->data_offset() + byte_offset;
+ size_t real_length =
+ std::min(buffer_length, entry_->data_size() - byte_offset);
std::memcpy(buffer, entry_->mmap()->data() + real_offset, real_length);
*out_bytes_read = real_length;
return X_STATUS_SUCCESS;
diff --git a/src/xenia/vfs/devices/disc_image_file.h b/src/xenia/vfs/devices/disc_image_file.h
index 647c05de3..d01125b67 100644
--- a/src/xenia/vfs/devices/disc_image_file.h
+++ b/src/xenia/vfs/devices/disc_image_file.h
@@ -23,15 +23,6 @@ class DiscImageFile : public XFile {
DiscImageEntry* entry);
~DiscImageFile() override;
- const std::string& path() const override;
- const std::string& name() const override;
-
- Device* device() const override;
-
- X_STATUS QueryInfo(X_FILE_NETWORK_OPEN_INFORMATION* out_info) override;
- X_STATUS QueryDirectory(X_FILE_DIRECTORY_INFORMATION* out_info, size_t length,
- const char* file_name, bool restart) override;
-
protected:
X_STATUS ReadSync(void* buffer, size_t buffer_length, size_t byte_offset,
size_t* out_bytes_read) override;
diff --git a/src/xenia/vfs/devices/host_path_device.cc b/src/xenia/vfs/devices/host_path_device.cc
index a51d3232f..e7d81e741 100644
--- a/src/xenia/vfs/devices/host_path_device.cc
+++ b/src/xenia/vfs/devices/host_path_device.cc
@@ -11,6 +11,7 @@
#include "xenia/base/filesystem.h"
#include "xenia/base/logging.h"
+#include "xenia/base/math.h"
#include "xenia/vfs/devices/host_path_entry.h"
#include "xenia/kernel/objects/xfile.h"
@@ -21,30 +22,50 @@ HostPathDevice::HostPathDevice(const std::string& mount_path,
const std::wstring& local_path, bool read_only)
: Device(mount_path), local_path_(local_path), read_only_(read_only) {}
-HostPathDevice::~HostPathDevice() {}
+HostPathDevice::~HostPathDevice() = default;
-std::unique_ptr HostPathDevice::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("HostPathDevice::ResolvePath(%s)", path);
-
- auto rel_path = xe::to_wstring(path);
- auto full_path = xe::join_paths(local_path_, rel_path);
- full_path = xe::fix_path_separators(full_path);
-
- // Only check the file exists when the device is read-only
- if (read_only_) {
- if (!xe::filesystem::PathExists(full_path)) {
- return nullptr;
- }
+bool HostPathDevice::Initialize() {
+ if (!filesystem::PathExists(local_path_)) {
+ XELOGE("Host path does not exist");
+ return false;
}
- // TODO(benvanik): get file info
- // TODO(benvanik): switch based on type
+ auto root_entry = new HostPathEntry(this, "", local_path_);
+ root_entry->attributes_ = kFileAttributeDirectory;
+ root_entry_ = std::unique_ptr(root_entry);
+ PopulateEntry(root_entry);
- return std::make_unique(this, path, full_path);
+ return true;
+}
+
+void HostPathDevice::PopulateEntry(HostPathEntry* entry) {
+ auto child_infos = filesystem::ListFiles(entry->local_path());
+ for (auto& child_info : child_infos) {
+ auto child =
+ new HostPathEntry(this, xe::to_string(child_info.name),
+ xe::join_paths(entry->local_path(), child_info.name));
+ child->create_timestamp_ = child_info.create_timestamp;
+ child->access_timestamp_ = child_info.access_timestamp;
+ child->write_timestamp_ = child_info.write_timestamp;
+
+ if (child_info.type == filesystem::FileInfo::Type::kDirectory) {
+ child->attributes_ = kFileAttributeDirectory;
+ } else {
+ child->attributes_ = kFileAttributeNormal;
+ if (read_only_) {
+ child->attributes_ |= kFileAttributeReadOnly;
+ }
+ child->size_ = child_info.total_size;
+ child->allocation_size_ =
+ xe::round_up(child_info.total_size, bytes_per_sector());
+ }
+
+ entry->children_.push_back(std::unique_ptr(child));
+
+ if (child_info.type == filesystem::FileInfo::Type::kDirectory) {
+ PopulateEntry(child);
+ }
+ }
}
} // namespace vfs
diff --git a/src/xenia/vfs/devices/host_path_device.h b/src/xenia/vfs/devices/host_path_device.h
index d5d569d8b..5922bb8a6 100644
--- a/src/xenia/vfs/devices/host_path_device.h
+++ b/src/xenia/vfs/devices/host_path_device.h
@@ -17,15 +17,17 @@
namespace xe {
namespace vfs {
+class HostPathEntry;
+
class HostPathDevice : public Device {
public:
HostPathDevice(const std::string& mount_path, const std::wstring& local_path,
bool read_only);
~HostPathDevice() override;
- bool is_read_only() const { return read_only_; }
+ bool Initialize() override;
- std::unique_ptr ResolvePath(const char* path) override;
+ bool is_read_only() const { return read_only_; }
uint32_t total_allocation_units() const override { return 128 * 1024; }
uint32_t available_allocation_units() const override { return 128 * 1024; }
@@ -33,6 +35,8 @@ class HostPathDevice : public Device {
uint32_t bytes_per_sector() const override { return 2 * 1024; }
private:
+ void PopulateEntry(HostPathEntry* entry);
+
std::wstring local_path_;
bool read_only_;
};
diff --git a/src/xenia/vfs/devices/host_path_entry.cc b/src/xenia/vfs/devices/host_path_entry.cc
index 8660c9f63..2d18df824 100644
--- a/src/xenia/vfs/devices/host_path_entry.cc
+++ b/src/xenia/vfs/devices/host_path_entry.cc
@@ -17,104 +17,11 @@
namespace xe {
namespace vfs {
-HostPathEntry::HostPathEntry(Device* device, const char* path,
+HostPathEntry::HostPathEntry(Device* device, std::string path,
const std::wstring& local_path)
- : Entry(device, path),
- local_path_(local_path),
- find_file_(INVALID_HANDLE_VALUE) {}
+ : Entry(device, path), local_path_(local_path) {}
-HostPathEntry::~HostPathEntry() {
- if (find_file_ != INVALID_HANDLE_VALUE) {
- FindClose(find_file_);
- }
-}
-
-#define COMBINE_TIME(t) (((uint64_t)t.dwHighDateTime << 32) | t.dwLowDateTime)
-
-X_STATUS HostPathEntry::QueryInfo(X_FILE_NETWORK_OPEN_INFORMATION* out_info) {
- assert_not_null(out_info);
-
- WIN32_FILE_ATTRIBUTE_DATA data;
- if (!GetFileAttributesEx(local_path_.c_str(), GetFileExInfoStandard, &data)) {
- return X_STATUS_ACCESS_DENIED;
- }
-
- uint64_t file_size = ((uint64_t)data.nFileSizeHigh << 32) | data.nFileSizeLow;
-
- out_info->creation_time = COMBINE_TIME(data.ftCreationTime);
- out_info->last_access_time = COMBINE_TIME(data.ftLastAccessTime);
- out_info->last_write_time = COMBINE_TIME(data.ftLastWriteTime);
- out_info->change_time = COMBINE_TIME(data.ftLastWriteTime);
- out_info->allocation_size = xe::round_up(file_size, 4096);
- out_info->end_of_file = file_size;
- out_info->attributes = (X_FILE_ATTRIBUTES)data.dwFileAttributes;
- return X_STATUS_SUCCESS;
-}
-
-X_STATUS HostPathEntry::QueryDirectory(X_FILE_DIRECTORY_INFORMATION* out_info,
- size_t length, const char* file_name,
- bool restart) {
- assert_not_null(out_info);
-
- WIN32_FIND_DATA ffd;
-
- HANDLE handle = find_file_;
-
- if (restart == true && handle != INVALID_HANDLE_VALUE) {
- FindClose(find_file_);
- handle = find_file_ = INVALID_HANDLE_VALUE;
- }
-
- if (handle == INVALID_HANDLE_VALUE) {
- std::wstring target_path = local_path_;
- if (!file_name) {
- target_path = xe::join_paths(target_path, L"*");
- } else {
- target_path = xe::join_paths(target_path, xe::to_wstring(file_name));
- }
- handle = find_file_ = FindFirstFile(target_path.c_str(), &ffd);
- if (handle == INVALID_HANDLE_VALUE) {
- if (GetLastError() == ERROR_FILE_NOT_FOUND) {
- return X_STATUS_NO_SUCH_FILE;
- }
- return X_STATUS_UNSUCCESSFUL;
- }
- } else {
- if (FindNextFile(handle, &ffd) == FALSE) {
- FindClose(handle);
- find_file_ = INVALID_HANDLE_VALUE;
- return X_STATUS_NO_SUCH_FILE;
- }
- }
-
- auto end = (uint8_t*)out_info + length;
- size_t entry_name_length = wcslen(ffd.cFileName);
- if (((uint8_t*)&out_info->file_name[0]) + entry_name_length > end) {
- FindClose(handle);
- find_file_ = INVALID_HANDLE_VALUE;
- return X_STATUS_BUFFER_OVERFLOW;
- }
-
- uint64_t file_size = ((uint64_t)ffd.nFileSizeHigh << 32) | ffd.nFileSizeLow;
-
- out_info->next_entry_offset = 0;
- out_info->file_index = 0xCDCDCDCD;
- out_info->creation_time = COMBINE_TIME(ffd.ftCreationTime);
- out_info->last_access_time = COMBINE_TIME(ffd.ftLastAccessTime);
- out_info->last_write_time = COMBINE_TIME(ffd.ftLastWriteTime);
- out_info->change_time = COMBINE_TIME(ffd.ftLastWriteTime);
- out_info->end_of_file = file_size;
- out_info->allocation_size = xe::round_up(file_size, 4096);
- out_info->attributes = (X_FILE_ATTRIBUTES)ffd.dwFileAttributes;
-
- out_info->file_name_length = (uint32_t)entry_name_length;
- for (size_t i = 0; i < entry_name_length; ++i) {
- out_info->file_name[i] =
- ffd.cFileName[i] < 256 ? (char)ffd.cFileName[i] : '?';
- }
-
- return X_STATUS_SUCCESS;
-}
+HostPathEntry::~HostPathEntry() = default;
X_STATUS HostPathEntry::Open(KernelState* kernel_state, Mode mode, bool async,
XFile** out_file) {
diff --git a/src/xenia/vfs/devices/host_path_entry.h b/src/xenia/vfs/devices/host_path_entry.h
index fb38074e1..8cf507a2d 100644
--- a/src/xenia/vfs/devices/host_path_entry.h
+++ b/src/xenia/vfs/devices/host_path_entry.h
@@ -10,23 +10,23 @@
#ifndef XENIA_VFS_DEVICES_HOST_PATH_ENTRY_H_
#define XENIA_VFS_DEVICES_HOST_PATH_ENTRY_H_
+#include
+
#include "xenia/vfs/entry.h"
namespace xe {
namespace vfs {
+class HostPathDevice;
+
class HostPathEntry : public Entry {
public:
- HostPathEntry(Device* device, const char* path,
+ HostPathEntry(Device* device, std::string path,
const std::wstring& local_path);
~HostPathEntry() override;
const std::wstring& local_path() { return local_path_; }
- X_STATUS QueryInfo(X_FILE_NETWORK_OPEN_INFORMATION* out_info) override;
- X_STATUS QueryDirectory(X_FILE_DIRECTORY_INFORMATION* out_info, size_t length,
- const char* file_name, bool restart) override;
-
X_STATUS Open(KernelState* kernel_state, Mode mode, bool async,
XFile** out_file) override;
@@ -36,8 +36,9 @@ class HostPathEntry : public Entry {
size_t length) override;
private:
+ friend class HostPathDevice;
+
std::wstring local_path_;
- HANDLE find_file_;
};
} // namespace vfs
diff --git a/src/xenia/vfs/devices/host_path_file.cc b/src/xenia/vfs/devices/host_path_file.cc
index e20b7522c..87fafd634 100644
--- a/src/xenia/vfs/devices/host_path_file.cc
+++ b/src/xenia/vfs/devices/host_path_file.cc
@@ -9,7 +9,6 @@
#include "xenia/vfs/devices/host_path_file.h"
-#include "xenia/vfs/device.h"
#include "xenia/vfs/devices/host_path_entry.h"
namespace xe {
@@ -17,28 +16,11 @@ namespace vfs {
HostPathFile::HostPathFile(KernelState* kernel_state, Mode mode,
HostPathEntry* entry, HANDLE file_handle)
- : entry_(entry), file_handle_(file_handle), XFile(kernel_state, mode) {}
+ : XFile(kernel_state, mode, entry),
+ entry_(entry),
+ file_handle_(file_handle) {}
-HostPathFile::~HostPathFile() {
- CloseHandle(file_handle_);
- delete entry_;
-}
-
-const std::string& HostPathFile::path() const { return entry_->path(); }
-
-const std::string& HostPathFile::name() const { return entry_->name(); }
-
-Device* HostPathFile::device() const { return entry_->device(); }
-
-X_STATUS HostPathFile::QueryInfo(X_FILE_NETWORK_OPEN_INFORMATION* out_info) {
- return entry_->QueryInfo(out_info);
-}
-
-X_STATUS HostPathFile::QueryDirectory(X_FILE_DIRECTORY_INFORMATION* out_info,
- size_t length, const char* file_name,
- bool restart) {
- return entry_->QueryDirectory(out_info, length, file_name, restart);
-}
+HostPathFile::~HostPathFile() { CloseHandle(file_handle_); }
X_STATUS HostPathFile::ReadSync(void* buffer, size_t buffer_length,
size_t byte_offset, size_t* out_bytes_read) {
diff --git a/src/xenia/vfs/devices/host_path_file.h b/src/xenia/vfs/devices/host_path_file.h
index cb58c2b1f..7e683d8d9 100644
--- a/src/xenia/vfs/devices/host_path_file.h
+++ b/src/xenia/vfs/devices/host_path_file.h
@@ -25,15 +25,6 @@ class HostPathFile : public XFile {
HANDLE file_handle);
~HostPathFile() override;
- const std::string& path() const override;
- const std::string& name() const override;
-
- Device* device() const override;
-
- X_STATUS QueryInfo(X_FILE_NETWORK_OPEN_INFORMATION* out_info) override;
- X_STATUS QueryDirectory(X_FILE_DIRECTORY_INFORMATION* out_info, size_t length,
- const char* file_name, bool restart) override;
-
protected:
X_STATUS ReadSync(void* buffer, size_t buffer_length, size_t byte_offset,
size_t* out_bytes_read) override;
diff --git a/src/xenia/vfs/devices/stfs_container_device.cc b/src/xenia/vfs/devices/stfs_container_device.cc
index f2c4a33be..3c6cd3f8b 100644
--- a/src/xenia/vfs/devices/stfs_container_device.cc
+++ b/src/xenia/vfs/devices/stfs_container_device.cc
@@ -11,18 +11,25 @@
#include "xenia/base/logging.h"
#include "xenia/base/math.h"
-#include "xenia/vfs/stfs.h"
#include "xenia/vfs/devices/stfs_container_entry.h"
-#include "xenia/kernel/objects/xfile.h"
namespace xe {
namespace vfs {
+#define XEGETUINT24BE(p) \
+ (((uint32_t)xe::load_and_swap((p) + 0) << 16) | \
+ ((uint32_t)xe::load_and_swap((p) + 1) << 8) | \
+ (uint32_t)xe::load_and_swap((p) + 2))
+#define XEGETUINT24LE(p) \
+ (((uint32_t)xe::load((p) + 2) << 16) | \
+ ((uint32_t)xe::load((p) + 1) << 8) | \
+ (uint32_t)xe::load((p) + 0))
+
STFSContainerDevice::STFSContainerDevice(const std::string& mount_path,
const std::wstring& local_path)
- : Device(mount_path), local_path_(local_path), stfs_(nullptr) {}
+ : Device(mount_path), local_path_(local_path) {}
-STFSContainerDevice::~STFSContainerDevice() { delete stfs_; }
+STFSContainerDevice::~STFSContainerDevice() = default;
bool STFSContainerDevice::Initialize() {
mmap_ = MappedMemory::Open(local_path_, MappedMemory::Mode::kRead);
@@ -31,39 +38,277 @@ bool STFSContainerDevice::Initialize() {
return false;
}
- stfs_ = new STFS(mmap_.get());
- STFS::Error error = stfs_->Load();
- if (error != STFS::kSuccess) {
- XELOGE("STFS init failed: %d", error);
+ uint8_t* map_ptr = mmap_->data();
+
+ auto result = ReadHeaderAndVerify(map_ptr);
+ if (result != Error::kSuccess) {
+ XELOGE("STFS header read/verification failed: %d", result);
return false;
}
- // stfs_->Dump();
+ result = ReadAllEntries(map_ptr);
+ if (result != Error::kSuccess) {
+ XELOGE("STFS entry reading failed: %d", result);
+ return false;
+ }
return true;
}
-std::unique_ptr STFSContainerDevice::ResolvePath(const char* path) {
- // The filesystem will have stripped our prefix off already, so the path will
- // be in the form:
- // some\PATH.foo
+STFSContainerDevice::Error STFSContainerDevice::ReadHeaderAndVerify(
+ const uint8_t* map_ptr) {
+ // Check signature.
+ if (memcmp(map_ptr, "LIVE", 4) == 0) {
+ package_type_ = StfsPackageType::STFS_PACKAGE_LIVE;
+ } else if (memcmp(map_ptr, "PIRS", 4) == 0) {
+ package_type_ = StfsPackageType::STFS_PACKAGE_PIRS;
+ } else if (memcmp(map_ptr, "CON", 3) == 0) {
+ package_type_ = StfsPackageType::STFS_PACKAGE_CON;
+ } else {
+ // Unexpected format.
+ return Error::kErrorFileMismatch;
+ }
- XELOGFS("STFSContainerDevice::ResolvePath(%s)", path);
+ // Read header.
+ if (!header_.Read(map_ptr)) {
+ return Error::kErrorDamagedFile;
+ }
- STFSEntry* stfs_entry = stfs_->root_entry();
+ if (((header_.header_size + 0x0FFF) & 0xB000) == 0xB000) {
+ table_size_shift_ = 0;
+ } else {
+ table_size_shift_ = 1;
+ }
- // Walk the path, one separator at a time.
- auto path_parts = xe::split_path(path);
- for (auto& part : path_parts) {
- stfs_entry = stfs_entry->GetChild(part.c_str());
- if (!stfs_entry) {
- // Not found.
- return nullptr;
+ return Error::kSuccess;
+}
+
+STFSContainerDevice::Error STFSContainerDevice::ReadAllEntries(
+ const uint8_t* map_ptr) {
+ auto root_entry = new STFSContainerEntry(this, "", mmap_.get());
+ root_entry->attributes_ = kFileAttributeDirectory;
+
+ root_entry_ = std::unique_ptr(root_entry);
+
+ std::vector all_entries;
+
+ // Load all listings.
+ auto& volume_descriptor = header_.volume_descriptor;
+ uint32_t table_block_index = volume_descriptor.file_table_block_number;
+ for (size_t n = 0; n < volume_descriptor.file_table_block_count; n++) {
+ const uint8_t* p =
+ map_ptr + BlockToOffset(ComputeBlockNumber(table_block_index));
+ for (size_t m = 0; m < 0x1000 / 0x40; m++) {
+ const uint8_t* filename = p; // 0x28b
+ if (filename[0] == 0) {
+ // Done.
+ break;
+ }
+ uint8_t filename_length_flags = xe::load_and_swap(p + 0x28);
+ uint32_t allocated_block_count = XEGETUINT24LE(p + 0x29);
+ uint32_t start_block_index = XEGETUINT24LE(p + 0x2F);
+ uint16_t path_indicator = xe::load_and_swap(p + 0x32);
+ uint32_t file_size = xe::load_and_swap(p + 0x34);
+ uint32_t update_timestamp = xe::load_and_swap(p + 0x38);
+ uint32_t access_timestamp = xe::load_and_swap(p + 0x3C);
+ p += 0x40;
+
+ auto entry = new STFSContainerEntry(
+ this, std::string((char*)filename, filename_length_flags & 0x3F),
+ mmap_.get());
+ // bit 0x40 = consecutive blocks (not fragmented?)
+ if (filename_length_flags & 0x80) {
+ entry->attributes_ = kFileAttributeDirectory;
+ } else {
+ entry->attributes_ = kFileAttributeNormal | kFileAttributeReadOnly;
+ entry->data_offset_ =
+ BlockToOffset(ComputeBlockNumber(start_block_index));
+ entry->data_size_ = file_size;
+ }
+ entry->size_ = file_size;
+ entry->allocation_size_ = xe::round_up(file_size, bytes_per_sector());
+ entry->create_timestamp_ = update_timestamp;
+ entry->access_timestamp_ = access_timestamp;
+ entry->write_timestamp_ = update_timestamp;
+ all_entries.push_back(entry);
+
+ // Fill in all block records.
+ // It's easier to do this now and just look them up later, at the cost
+ // of some memory. Nasty chain walk.
+ // TODO(benvanik): optimize if flag 0x40 (consecutive) is set.
+ if (entry->attributes() & X_FILE_ATTRIBUTE_NORMAL) {
+ uint32_t block_index = start_block_index;
+ size_t remaining_size = file_size;
+ uint32_t info = 0x80;
+ while (remaining_size && block_index && info >= 0x80) {
+ size_t block_size = std::min(0x1000ull, remaining_size);
+ size_t offset = BlockToOffset(ComputeBlockNumber(block_index));
+ entry->block_list_.push_back({offset, block_size});
+ remaining_size -= block_size;
+ auto block_hash = GetBlockHash(map_ptr, block_index, 0);
+ if (table_size_shift_ && block_hash.info < 0x80) {
+ block_hash = GetBlockHash(map_ptr, block_index, 1);
+ }
+ block_index = block_hash.next_block_index;
+ info = block_hash.info;
+ }
+ }
+
+ if (path_indicator == 0xFFFF) {
+ // Root entry.
+ root_entry->children_.emplace_back(std::unique_ptr(entry));
+ } else {
+ // Lookup and add.
+ auto parent = all_entries[path_indicator];
+ parent->children_.emplace_back(std::unique_ptr(entry));
+ }
+ }
+
+ auto block_hash = GetBlockHash(map_ptr, table_block_index, 0);
+ if (table_size_shift_ && block_hash.info < 0x80) {
+ block_hash = GetBlockHash(map_ptr, table_block_index, 1);
+ }
+ table_block_index = block_hash.next_block_index;
+ if (table_block_index == 0xFFFFFF) {
+ break;
}
}
- return std::make_unique(this, path, mmap_.get(),
- stfs_entry);
+ return Error::kSuccess;
+}
+
+size_t STFSContainerDevice::BlockToOffset(uint32_t block) {
+ if (block >= 0xFFFFFF) {
+ return -1;
+ } else {
+ return ((header_.header_size + 0x0FFF) & 0xF000) + (block << 12);
+ }
+}
+
+uint32_t STFSContainerDevice::ComputeBlockNumber(uint32_t block_index) {
+ uint32_t block_shift = 0;
+ if (((header_.header_size + 0x0FFF) & 0xB000) == 0xB000) {
+ block_shift = 1;
+ } else {
+ if ((header_.volume_descriptor.block_separation & 0x1) == 0x1) {
+ block_shift = 0;
+ } else {
+ block_shift = 1;
+ }
+ }
+
+ uint32_t base = (block_index + 0xAA) / 0xAA;
+ if (package_type_ == StfsPackageType::STFS_PACKAGE_CON) {
+ base <<= block_shift;
+ }
+ uint32_t block = base + block_index;
+ if (block_index >= 0xAA) {
+ base = (block_index + 0x70E4) / 0x70E4;
+ if (package_type_ == StfsPackageType::STFS_PACKAGE_CON) {
+ base <<= block_shift;
+ }
+ block += base;
+ if (block_index >= 0x70E4) {
+ base = (block_index + 0x4AF768) / 0x4AF768;
+ if (package_type_ == StfsPackageType::STFS_PACKAGE_CON) {
+ base <<= block_shift;
+ }
+ block += base;
+ }
+ }
+ return block;
+}
+
+STFSContainerDevice::BlockHash STFSContainerDevice::GetBlockHash(
+ const uint8_t* map_ptr, uint32_t block_index, uint32_t table_offset) {
+ static const uint32_t table_spacing[] = {
+ 0xAB, 0x718F,
+ 0xFE7DA, // The distance in blocks between tables
+ 0xAC, 0x723A,
+ 0xFD00B, // For when tables are 1 block and when they are 2 blocks
+ };
+ uint32_t record = block_index % 0xAA;
+ uint32_t table_index =
+ (block_index / 0xAA) * table_spacing[table_size_shift_ * 3 + 0];
+ if (block_index >= 0xAA) {
+ table_index += ((block_index / 0x70E4) + 1) << table_size_shift_;
+ if (block_index >= 0x70E4) {
+ table_index += 1 << table_size_shift_;
+ }
+ }
+ // table_index += table_offset - (1 << table_size_shift_);
+ const uint8_t* hash_data = map_ptr + BlockToOffset(table_index);
+ const uint8_t* record_data = hash_data + record * 0x18;
+ uint32_t info = xe::load_and_swap(record_data + 0x14);
+ uint32_t next_block_index = XEGETUINT24BE(record_data + 0x15);
+ return {next_block_index, info};
+}
+
+bool StfsVolumeDescriptor::Read(const uint8_t* p) {
+ descriptor_size = xe::load_and_swap(p + 0x00);
+ if (descriptor_size != 0x24) {
+ XELOGE("STFS volume descriptor size mismatch, expected 0x24 but got 0x%X",
+ descriptor_size);
+ return false;
+ }
+ reserved = xe::load_and_swap(p + 0x01);
+ block_separation = xe::load_and_swap(p + 0x02);
+ file_table_block_count = xe::load_and_swap(p + 0x03);
+ file_table_block_number = XEGETUINT24BE(p + 0x05);
+ std::memcpy(top_hash_table_hash, p + 0x08, 0x14);
+ total_allocated_block_count = xe::load_and_swap(p + 0x1C);
+ total_unallocated_block_count = xe::load_and_swap(p + 0x20);
+ return true;
+};
+
+bool StfsHeader::Read(const uint8_t* p) {
+ std::memcpy(license_entries, p + 0x22C, 0x100);
+ std::memcpy(header_hash, p + 0x32C, 0x14);
+ header_size = xe::load_and_swap(p + 0x340);
+ content_type = (STFSContentType)xe::load_and_swap(p + 0x344);
+ metadata_version = xe::load_and_swap(p + 0x348);
+ if (metadata_version > 1) {
+ // This is a variant of thumbnail data/etc.
+ // Can just ignore it for now (until we parse thumbnails).
+ XELOGW("STFSContainer doesn't support version %d yet", metadata_version);
+ }
+ content_size = xe::load_and_swap(p + 0x34C);
+ media_id = xe::load_and_swap(p + 0x354);
+ version = xe::load_and_swap(p + 0x358);
+ base_version = xe::load_and_swap(p + 0x35C);
+ title_id = xe::load_and_swap(p + 0x360);
+ platform = (StfsPlatform)xe::load_and_swap(p + 0x364);
+ executable_type = xe::load_and_swap(p + 0x365);
+ disc_number = xe::load_and_swap(p + 0x366);
+ disc_in_set = xe::load_and_swap(p + 0x367);
+ save_game_id = xe::load_and_swap(p + 0x368);
+ std::memcpy(console_id, p + 0x36C, 0x5);
+ std::memcpy(profile_id, p + 0x371, 0x8);
+ data_file_count = xe::load_and_swap(p + 0x39D);
+ data_file_combined_size = xe::load_and_swap(p + 0x3A1);
+ descriptor_type = (StfsDescriptorType)xe::load_and_swap(p + 0x3A9);
+ if (descriptor_type != StfsDescriptorType::STFS_DESCRIPTOR_STFS) {
+ XELOGE("STFS descriptor format not supported: %d", descriptor_type);
+ return false;
+ }
+ if (!volume_descriptor.Read(p + 0x379)) {
+ return false;
+ }
+ memcpy(device_id, p + 0x3FD, 0x14);
+ for (size_t n = 0; n < 0x900 / 2; n++) {
+ display_names[n] = xe::load_and_swap(p + 0x411 + n * 2);
+ display_descs[n] = xe::load_and_swap(p + 0xD11 + n * 2);
+ }
+ for (size_t n = 0; n < 0x80 / 2; n++) {
+ publisher_name[n] = xe::load_and_swap(p + 0x1611 + n * 2);
+ title_name[n] = xe::load_and_swap(p + 0x1691 + n * 2);
+ }
+ transfer_flags = xe::load_and_swap(p + 0x1711);
+ thumbnail_image_size = xe::load_and_swap(p + 0x1712);
+ title_thumbnail_image_size = xe::load_and_swap(p + 0x1716);
+ std::memcpy(thumbnail_image, p + 0x171A, 0x4000);
+ std::memcpy(title_thumbnail_image, p + 0x571A, 0x4000);
+ return true;
}
} // namespace vfs
diff --git a/src/xenia/vfs/devices/stfs_container_device.h b/src/xenia/vfs/devices/stfs_container_device.h
index 13d1a9402..0a0567147 100644
--- a/src/xenia/vfs/devices/stfs_container_device.h
+++ b/src/xenia/vfs/devices/stfs_container_device.h
@@ -19,7 +19,107 @@
namespace xe {
namespace vfs {
-class STFS;
+// http://www.free60.org/STFS
+
+enum class StfsPackageType {
+ STFS_PACKAGE_CON,
+ STFS_PACKAGE_PIRS,
+ STFS_PACKAGE_LIVE,
+};
+
+enum STFSContentType : uint32_t {
+ STFS_CONTENT_ARCADE_TITLE = 0x000D0000,
+ STFS_CONTENT_AVATAR_ITEM = 0x00009000,
+ STFS_CONTENT_CACHE_FILE = 0x00040000,
+ STFS_CONTENT_COMMUNITY_GAME = 0x02000000,
+ STFS_CONTENT_GAME_DEMO = 0x00080000,
+ STFS_CONTENT_GAMER_PICTURE = 0x00020000,
+ STFS_CONTENT_GAME_TITLE = 0x000A0000,
+ STFS_CONTENT_GAME_TRAILER = 0x000C0000,
+ STFS_CONTENT_GAME_VIDEO = 0x00400000,
+ STFS_CONTENT_INSTALLED_GAME = 0x00004000,
+ STFS_CONTENT_INSTALLER = 0x000B0000,
+ STFS_CONTENT_IPTV_PAUSE_BUFFER = 0x00002000,
+ STFS_CONTENT_LICENSE_STORE = 0x000F0000,
+ STFS_CONTENT_MARKETPLACE_CONTENT = 0x00000002,
+ STFS_CONTENT_MOVIE = 0x00100000,
+ STFS_CONTENT_MUSIC_VIDEO = 0x00300000,
+ STFS_CONTENT_PODCAST_VIDEO = 0x00500000,
+ STFS_CONTENT_PROFILE = 0x00010000,
+ STFS_CONTENT_PUBLISHER = 0x00000003,
+ STFS_CONTENT_SAVED_GAME = 0x00000001,
+ STFS_CONTENT_STORAGE_DOWNLOAD = 0x00050000,
+ STFS_CONTENT_THEME = 0x00030000,
+ STFS_CONTENT_TV = 0x00200000,
+ STFS_CONTENT_VIDEO = 0x00090000,
+ STFS_CONTENT_VIRAL_VIDEO = 0x00600000,
+ STFS_CONTENT_XBOX_DOWNLOAD = 0x00070000,
+ STFS_CONTENT_XBOX_ORIGINAL_GAME = 0x00005000,
+ STFS_CONTENT_XBOX_SAVED_GAME = 0x00060000,
+ STFS_CONTENT_XBOX_360_TITLE = 0x00001000,
+ STFS_CONTENT_XBOX_TITLE = 0x00005000,
+ STFS_CONTENT_XNA = 0x000E0000,
+};
+
+enum class StfsPlatform : uint8_t {
+ STFS_PLATFORM_XBOX_360 = 0x02,
+ STFS_PLATFORM_PC = 0x04,
+};
+
+enum class StfsDescriptorType : uint32_t {
+ STFS_DESCRIPTOR_STFS = 0,
+ STFS_DESCRIPTOR_SVOD = 1,
+};
+
+struct StfsVolumeDescriptor {
+ bool Read(const uint8_t* p);
+
+ uint8_t descriptor_size;
+ uint8_t reserved;
+ uint8_t block_separation;
+ uint16_t file_table_block_count;
+ uint32_t file_table_block_number;
+ uint8_t top_hash_table_hash[0x14];
+ uint32_t total_allocated_block_count;
+ uint32_t total_unallocated_block_count;
+};
+
+class StfsHeader {
+ public:
+ bool Read(const uint8_t* p);
+
+ uint8_t license_entries[0x100];
+ uint8_t header_hash[0x14];
+ uint32_t header_size;
+ STFSContentType content_type;
+ uint32_t metadata_version;
+ uint64_t content_size;
+ uint32_t media_id;
+ uint32_t version;
+ uint32_t base_version;
+ uint32_t title_id;
+ StfsPlatform platform;
+ uint8_t executable_type;
+ uint8_t disc_number;
+ uint8_t disc_in_set;
+ uint32_t save_game_id;
+ uint8_t console_id[0x5];
+ uint8_t profile_id[0x8];
+ StfsVolumeDescriptor volume_descriptor;
+ uint32_t data_file_count;
+ uint64_t data_file_combined_size;
+ StfsDescriptorType descriptor_type;
+ uint8_t device_id[0x14];
+ wchar_t display_names[0x900 / 2];
+ wchar_t display_descs[0x900 / 2];
+ wchar_t publisher_name[0x80 / 2];
+ wchar_t title_name[0x80 / 2];
+ uint8_t transfer_flags;
+ uint32_t thumbnail_image_size;
+ uint32_t title_thumbnail_image_size;
+ uint8_t thumbnail_image[0x4000];
+ uint8_t title_thumbnail_image[0x4000];
+};
class STFSContainerDevice : public Device {
public:
@@ -27,9 +127,7 @@ class STFSContainerDevice : public Device {
const std::wstring& local_path);
~STFSContainerDevice() override;
- bool Initialize();
-
- std::unique_ptr ResolvePath(const char* path) override;
+ bool Initialize() override;
uint32_t total_allocation_units() const override {
return uint32_t(mmap_->size() / sectors_per_allocation_unit() /
@@ -40,9 +138,33 @@ class STFSContainerDevice : public Device {
uint32_t bytes_per_sector() const override { return 4 * 1024; }
private:
+ enum class Error {
+ kSuccess = 0,
+ kErrorOutOfMemory = -1,
+ kErrorReadError = -10,
+ kErrorFileMismatch = -30,
+ kErrorDamagedFile = -31,
+ };
+
+ struct BlockHash {
+ uint32_t next_block_index;
+ uint32_t info;
+ };
+
+ Error ReadHeaderAndVerify(const uint8_t* map_ptr);
+ Error ReadAllEntries(const uint8_t* map_ptr);
+ size_t BlockToOffset(uint32_t block);
+ uint32_t ComputeBlockNumber(uint32_t block_index);
+
+ BlockHash GetBlockHash(const uint8_t* map_ptr, uint32_t block_index,
+ uint32_t table_offset);
+
std::wstring local_path_;
std::unique_ptr mmap_;
- STFS* stfs_;
+
+ StfsPackageType package_type_;
+ StfsHeader header_;
+ uint32_t table_size_shift_;
};
} // namespace vfs
diff --git a/src/xenia/vfs/devices/stfs_container_entry.cc b/src/xenia/vfs/devices/stfs_container_entry.cc
index 2a963f3fd..61d35c43f 100644
--- a/src/xenia/vfs/devices/stfs_container_entry.cc
+++ b/src/xenia/vfs/devices/stfs_container_entry.cc
@@ -15,79 +15,12 @@
namespace xe {
namespace vfs {
-STFSContainerEntry::STFSContainerEntry(Device* device, const char* path,
- MappedMemory* mmap,
- STFSEntry* stfs_entry)
- : Entry(device, path),
- mmap_(mmap),
- stfs_entry_(stfs_entry),
- it_(stfs_entry_->children.begin()) {}
+STFSContainerEntry::STFSContainerEntry(Device* device, std::string path,
+ MappedMemory* mmap)
+ : Entry(device, path), mmap_(mmap), data_offset_(0), data_size_(0) {}
STFSContainerEntry::~STFSContainerEntry() = default;
-X_STATUS STFSContainerEntry::QueryInfo(
- X_FILE_NETWORK_OPEN_INFORMATION* out_info) {
- assert_not_null(out_info);
- out_info->creation_time = stfs_entry_->update_timestamp;
- out_info->last_access_time = stfs_entry_->access_timestamp;
- out_info->last_write_time = stfs_entry_->update_timestamp;
- out_info->change_time = stfs_entry_->update_timestamp;
- out_info->allocation_size = xe::round_up(stfs_entry_->size, 4096);
- out_info->end_of_file = stfs_entry_->size;
- out_info->attributes = stfs_entry_->attributes;
- return X_STATUS_SUCCESS;
-}
-
-X_STATUS STFSContainerEntry::QueryDirectory(
- X_FILE_DIRECTORY_INFORMATION* out_info, size_t length,
- const char* file_name, bool restart) {
- assert_not_null(out_info);
-
- STFSEntry* entry(nullptr);
-
- if (file_name != nullptr) {
- // Only queries in the current directory are supported for now
- assert_true(std::strchr(file_name, '\\') == nullptr);
-
- find_engine_.SetRule(file_name);
-
- // Always restart the search?
- it_ = stfs_entry_->children.begin();
- entry = stfs_entry_->GetChild(find_engine_, it_);
- if (!entry) {
- return X_STATUS_NO_SUCH_FILE;
- }
- } else {
- if (restart) {
- it_ = stfs_entry_->children.begin();
- }
-
- entry = stfs_entry_->GetChild(find_engine_, it_);
- if (!entry) {
- return X_STATUS_NO_SUCH_FILE;
- }
-
- auto end = (uint8_t*)out_info + length;
- auto entry_name = entry->name;
- if (((uint8_t*)&out_info->file_name[0]) + entry_name.size() > end) {
- return X_STATUS_NO_SUCH_FILE;
- }
- }
-
- out_info->file_index = 0xCDCDCDCD;
- out_info->creation_time = entry->update_timestamp;
- out_info->last_access_time = entry->access_timestamp;
- out_info->last_write_time = entry->update_timestamp;
- out_info->change_time = entry->update_timestamp;
- out_info->end_of_file = entry->size;
- out_info->allocation_size = xe::round_up(entry->size, 4096);
- out_info->attributes = entry->attributes;
- out_info->file_name_length = static_cast(entry->name.size());
- memcpy(out_info->file_name, entry->name.c_str(), entry->name.size());
-
- return X_STATUS_SUCCESS;
-}
-
X_STATUS STFSContainerEntry::Open(KernelState* kernel_state, Mode mode,
bool async, XFile** out_file) {
*out_file = new STFSContainerFile(kernel_state, mode, this);
diff --git a/src/xenia/vfs/devices/stfs_container_entry.h b/src/xenia/vfs/devices/stfs_container_entry.h
index 7a4660403..55321957b 100644
--- a/src/xenia/vfs/devices/stfs_container_entry.h
+++ b/src/xenia/vfs/devices/stfs_container_entry.h
@@ -11,38 +11,41 @@
#define XENIA_VFS_DEVICES_STFS_CONTAINER_ENTRY_H_
#include
-#include
#include "xenia/base/filesystem.h"
#include "xenia/base/mapped_memory.h"
#include "xenia/vfs/entry.h"
-#include "xenia/vfs/stfs.h"
namespace xe {
namespace vfs {
+class STFSContainerDevice;
+
class STFSContainerEntry : public Entry {
public:
- STFSContainerEntry(Device* device, const char* path, MappedMemory* mmap,
- STFSEntry* stfs_entry);
+ STFSContainerEntry(Device* device, std::string path, MappedMemory* mmap);
~STFSContainerEntry() override;
MappedMemory* mmap() const { return mmap_; }
- STFSEntry* stfs_entry() const { return stfs_entry_; }
-
- X_STATUS QueryInfo(X_FILE_NETWORK_OPEN_INFORMATION* out_info) override;
- X_STATUS QueryDirectory(X_FILE_DIRECTORY_INFORMATION* out_info, size_t length,
- const char* file_name, bool restart) override;
+ size_t data_offset() const { return data_offset_; }
+ size_t data_size() const { return data_size_; }
X_STATUS Open(KernelState* kernel_state, Mode desired_access, bool async,
XFile** out_file) override;
- private:
- MappedMemory* mmap_;
- STFSEntry* stfs_entry_;
+ struct BlockRecord {
+ size_t offset;
+ size_t length;
+ };
+ const std::vector& block_list() const { return block_list_; }
- xe::filesystem::WildcardEngine find_engine_;
- STFSEntry::child_it_t it_;
+ private:
+ friend class STFSContainerDevice;
+
+ MappedMemory* mmap_;
+ size_t data_offset_;
+ size_t data_size_;
+ std::vector block_list_;
};
} // namespace vfs
diff --git a/src/xenia/vfs/devices/stfs_container_file.cc b/src/xenia/vfs/devices/stfs_container_file.cc
index 9fe9b878d..cdbabc887 100644
--- a/src/xenia/vfs/devices/stfs_container_file.cc
+++ b/src/xenia/vfs/devices/stfs_container_file.cc
@@ -11,56 +11,36 @@
#include
-#include "xenia/vfs/device.h"
#include "xenia/vfs/devices/stfs_container_entry.h"
-#include "xenia/vfs/stfs.h"
namespace xe {
namespace vfs {
STFSContainerFile::STFSContainerFile(KernelState* kernel_state, Mode mode,
STFSContainerEntry* entry)
- : XFile(kernel_state, mode), entry_(entry) {}
+ : XFile(kernel_state, mode, entry), entry_(entry) {}
-STFSContainerFile::~STFSContainerFile() { delete entry_; }
-
-const std::string& STFSContainerFile::path() const { return entry_->path(); }
-
-const std::string& STFSContainerFile::name() const { return entry_->name(); }
-
-Device* STFSContainerFile::device() const { return entry_->device(); }
-
-X_STATUS STFSContainerFile::QueryInfo(
- X_FILE_NETWORK_OPEN_INFORMATION* out_info) {
- return entry_->QueryInfo(out_info);
-}
-
-X_STATUS STFSContainerFile::QueryDirectory(
- X_FILE_DIRECTORY_INFORMATION* out_info, size_t length,
- const char* file_name, bool restart) {
- return entry_->QueryDirectory(out_info, length, file_name, restart);
-}
+STFSContainerFile::~STFSContainerFile() = default;
X_STATUS STFSContainerFile::ReadSync(void* buffer, size_t buffer_length,
size_t byte_offset,
size_t* out_bytes_read) {
- STFSEntry* stfs_entry = entry_->stfs_entry();
- if (byte_offset >= stfs_entry->size) {
+ if (byte_offset >= entry_->size()) {
return X_STATUS_END_OF_FILE;
}
// Each block is 4096.
// Blocks may not be sequential, so we need to read by blocks and handle the
// offsets.
- size_t real_length = std::min(buffer_length, stfs_entry->size - byte_offset);
+ size_t real_length = std::min(buffer_length, entry_->size() - byte_offset);
size_t start_block = byte_offset / 4096;
size_t end_block =
- std::min(stfs_entry->block_list.size(),
+ std::min(entry_->block_list().size(),
(size_t)ceil((byte_offset + real_length) / 4096.0));
uint8_t* dest_ptr = (uint8_t*)buffer;
size_t remaining_length = real_length;
for (size_t n = start_block; n < end_block; n++) {
- auto& record = stfs_entry->block_list[n];
+ auto& record = entry_->block_list()[n];
size_t offset = record.offset;
size_t read_length = std::min(remaining_length, record.length);
if (n == start_block) {
diff --git a/src/xenia/vfs/devices/stfs_container_file.h b/src/xenia/vfs/devices/stfs_container_file.h
index c273d4e30..a39f1107c 100644
--- a/src/xenia/vfs/devices/stfs_container_file.h
+++ b/src/xenia/vfs/devices/stfs_container_file.h
@@ -23,15 +23,6 @@ class STFSContainerFile : public XFile {
STFSContainerEntry* entry);
~STFSContainerFile() override;
- const std::string& path() const override;
- const std::string& name() const override;
-
- Device* device() const override;
-
- X_STATUS QueryInfo(X_FILE_NETWORK_OPEN_INFORMATION* out_info) override;
- X_STATUS QueryDirectory(X_FILE_DIRECTORY_INFORMATION* out_info, size_t length,
- const char* file_name, bool restart) override;
-
protected:
X_STATUS ReadSync(void* buffer, size_t buffer_length, size_t byte_offset,
size_t* out_bytes_read) override;
diff --git a/src/xenia/vfs/entry.cc b/src/xenia/vfs/entry.cc
index e03f5b24e..61895e90d 100644
--- a/src/xenia/vfs/entry.cc
+++ b/src/xenia/vfs/entry.cc
@@ -16,7 +16,14 @@ namespace xe {
namespace vfs {
Entry::Entry(Device* device, const std::string& path)
- : device_(device), path_(path) {
+ : device_(device),
+ path_(path),
+ attributes_(0),
+ size_(0),
+ allocation_size_(0),
+ create_timestamp_(0),
+ access_timestamp_(0),
+ write_timestamp_(0) {
assert_not_null(device);
absolute_path_ = xe::join_paths(device->mount_path(), path);
name_ = xe::find_name_from_path(path);
@@ -24,7 +31,40 @@ Entry::Entry(Device* device, const std::string& path)
Entry::~Entry() = default;
+void Entry::Dump(xe::StringBuffer* string_buffer, int indent) {
+ for (int i = 0; i < indent; ++i) {
+ string_buffer->Append(' ');
+ }
+ string_buffer->Append(name());
+ string_buffer->Append('\n');
+ for (auto& child : children_) {
+ child->Dump(string_buffer, indent + 2);
+ }
+}
+
bool Entry::is_read_only() const { return device_->is_read_only(); }
+Entry* Entry::GetChild(std::string name) {
+ // TODO(benvanik): a faster search
+ for (auto& child : children_) {
+ if (strcasecmp(child->name().c_str(), name.c_str()) == 0) {
+ return child.get();
+ }
+ }
+ return nullptr;
+}
+
+Entry* Entry::IterateChildren(const xe::filesystem::WildcardEngine& engine,
+ size_t* current_index) {
+ while (*current_index < children_.size()) {
+ auto& child = children_[*current_index];
+ *current_index = *current_index + 1;
+ if (engine.Match(child->name())) {
+ return child.get();
+ }
+ }
+ return nullptr;
+}
+
} // namespace vfs
} // namespace xe
diff --git a/src/xenia/vfs/entry.h b/src/xenia/vfs/entry.h
index e04a9f3c2..1eaf03d75 100644
--- a/src/xenia/vfs/entry.h
+++ b/src/xenia/vfs/entry.h
@@ -13,15 +13,15 @@
#include
#include
+#include "xenia/base/filesystem.h"
#include "xenia/base/mapped_memory.h"
+#include "xenia/base/string_buffer.h"
#include "xenia/xbox.h"
namespace xe {
namespace kernel {
class KernelState;
class XFile;
-struct X_FILE_NETWORK_OPEN_INFORMATION;
-class X_FILE_DIRECTORY_INFORMATION;
} // namespace kernel
} // namespace xe
@@ -38,22 +38,44 @@ enum class Mode {
READ_APPEND,
};
+enum FileAttributeFlags : uint32_t {
+ kFileAttributeNone = 0x0000,
+ kFileAttributeReadOnly = 0x0001,
+ kFileAttributeHidden = 0x0002,
+ kFileAttributeSystem = 0x0004,
+ kFileAttributeDirectory = 0x0010,
+ kFileAttributeArchive = 0x0020,
+ kFileAttributeDevice = 0x0040,
+ kFileAttributeNormal = 0x0080,
+ kFileAttributeTemporary = 0x0100,
+ kFileAttributeCompressed = 0x0800,
+ kFileAttributeEncrypted = 0x4000,
+};
+
class Entry {
public:
- Entry(Device* device, const std::string& path);
virtual ~Entry();
+ void Dump(xe::StringBuffer* string_buffer, int indent);
+
Device* device() const { return device_; }
const std::string& path() const { return path_; }
const std::string& absolute_path() const { return absolute_path_; }
const std::string& name() const { return name_; }
+ uint32_t attributes() const { return attributes_; }
+ size_t size() const { return size_; }
+ size_t allocation_size() const { return allocation_size_; }
+ uint64_t create_timestamp() const { return create_timestamp_; }
+ uint64_t access_timestamp() const { return access_timestamp_; }
+ uint64_t write_timestamp() const { return write_timestamp_; }
bool is_read_only() const;
- virtual X_STATUS QueryInfo(X_FILE_NETWORK_OPEN_INFORMATION* out_info) = 0;
- virtual X_STATUS QueryDirectory(X_FILE_DIRECTORY_INFORMATION* out_info,
- size_t length, const char* file_name,
- bool restart) = 0;
+ Entry* GetChild(std::string name);
+
+ size_t child_count() const { return children_.size(); }
+ Entry* IterateChildren(const xe::filesystem::WildcardEngine& engine,
+ size_t* current_index);
virtual X_STATUS Open(KernelState* kernel_state, Mode mode, bool async,
XFile** out_file) = 0;
@@ -65,11 +87,20 @@ class Entry {
return nullptr;
}
- private:
+ protected:
+ Entry(Device* device, const std::string& path);
+
Device* device_;
std::string path_;
std::string absolute_path_;
std::string name_;
+ uint32_t attributes_; // FileAttributeFlags
+ size_t size_;
+ size_t allocation_size_;
+ uint64_t create_timestamp_;
+ uint64_t access_timestamp_;
+ uint64_t write_timestamp_;
+ std::vector> children_;
};
} // namespace vfs
diff --git a/src/xenia/vfs/gdfx.cc b/src/xenia/vfs/gdfx.cc
deleted file mode 100644
index c612d3eca..000000000
--- a/src/xenia/vfs/gdfx.cc
+++ /dev/null
@@ -1,205 +0,0 @@
-/**
- ******************************************************************************
- * 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/vfs/gdfx.h"
-
-#include "xenia/base/math.h"
-
-namespace xe {
-namespace vfs {
-
-const size_t kXESectorSize = 2048;
-
-GDFXEntry::GDFXEntry()
- : attributes(X_FILE_ATTRIBUTE_NONE), offset(0), size(0) {}
-
-GDFXEntry::~GDFXEntry() {
- for (std::vector::iterator it = children.begin();
- it != children.end(); ++it) {
- delete *it;
- }
-}
-
-GDFXEntry* GDFXEntry::GetChild(const xe::filesystem::WildcardEngine& engine,
- child_it_t& ref_it) {
- GDFXEntry* child_entry(nullptr);
- while (ref_it != children.end()) {
- if (engine.Match((*ref_it)->name)) {
- child_entry = (*ref_it);
- ++ref_it;
- break;
- }
- ++ref_it;
- }
- return child_entry;
-}
-
-GDFXEntry* GDFXEntry::GetChild(const char* name) {
- // TODO(benvanik): a faster search
- for (std::vector::iterator it = children.begin();
- it != children.end(); ++it) {
- GDFXEntry* entry = *it;
- if (strcasecmp(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::iterator it = children.begin();
- it != children.end(); ++it) {
- GDFXEntry* entry = *it;
- entry->Dump(indent + 2);
- }
-}
-
-GDFX::GDFX(MappedMemory* mmap) : mmap_(mmap) { root_entry_ = nullptr; }
-
-GDFX::~GDFX() { delete root_entry_; }
-
-GDFXEntry* GDFX::root_entry() { return root_entry_; }
-
-GDFX::Error GDFX::Load() {
- ParseState state = {0};
-
- state.ptr = mmap_->data();
- state.size = mmap_->size();
-
- auto result = Verify(state);
- if (result != kSuccess) {
- return result;
- }
-
- result = ReadAllEntries(state, state.ptr + state.root_offset);
- if (result != kSuccess) {
- return result;
- }
-
- return kSuccess;
-}
-
-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 < xe::countof(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 = xe::load(fs_ptr + 20);
- state.root_size = xe::load(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 = X_FILE_ATTRIBUTE_DIRECTORY;
-
- 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 = xe::load(p + 0);
- uint16_t node_r = xe::load(p + 2);
- size_t sector = xe::load(p + 4);
- size_t length = xe::load(p + 8);
- uint8_t attributes = xe::load(p + 12);
- uint8_t name_length = xe::load(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->attributes = (X_FILE_ATTRIBUTES)attributes;
-
- // Add to parent.
- parent->children.push_back(entry);
-
- if (attributes & X_FILE_ATTRIBUTE_DIRECTORY) {
- // 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;
-}
-
-} // namespace vfs
-} // namespace xe
diff --git a/src/xenia/vfs/gdfx.h b/src/xenia/vfs/gdfx.h
deleted file mode 100644
index 8cbc8cd27..000000000
--- a/src/xenia/vfs/gdfx.h
+++ /dev/null
@@ -1,96 +0,0 @@
-/**
- ******************************************************************************
- * 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_VFS_GDFX_H_
-#define XENIA_VFS_GDFX_H_
-
-#include
-
-#include "xenia/base/filesystem.h"
-#include "xenia/base/mapped_memory.h"
-#include "xenia/vfs/entry.h"
-#include "xenia/xbox.h"
-
-namespace xe {
-namespace vfs {
-
-class GDFX;
-
-class GDFXEntry {
- public:
- GDFXEntry();
- ~GDFXEntry();
-
- typedef std::vector child_t;
- typedef child_t::iterator child_it_t;
-
- GDFXEntry* GetChild(const xe::filesystem::WildcardEngine& engine,
- child_it_t& ref_it);
- GDFXEntry* GetChild(const char* name);
-
- void Dump(int indent);
-
- std::string name;
- X_FILE_ATTRIBUTES attributes;
- size_t offset;
- size_t size;
- child_t children;
-};
-
-class GDFX {
- public:
- enum Error {
- kSuccess = 0,
- kErrorOutOfMemory = -1,
- kErrorReadError = -10,
- kErrorFileMismatch = -30,
- kErrorDamagedFile = -31,
- };
-
- GDFX(MappedMemory* mmap);
- virtual ~GDFX();
-
- GDFXEntry* root_entry();
-
- Error Load();
- void Dump();
-
- private:
- typedef struct {
- uint8_t* ptr;
-
- // Size (bytes) of total image.
- size_t size;
-
- // Offset (bytes) of game partition.
- size_t game_offset;
-
- // Offset (sector) of root.
- size_t root_sector;
- // Offset (bytes) of root.
- size_t root_offset;
- // Size (bytes) of root.
- size_t root_size;
- } 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);
-
- MappedMemory* mmap_;
-
- GDFXEntry* root_entry_;
-};
-
-} // namespace vfs
-} // namespace xe
-
-#endif // XENIA_VFS_GDFX_H_
diff --git a/src/xenia/vfs/stfs.cc b/src/xenia/vfs/stfs.cc
deleted file mode 100644
index 074b003fc..000000000
--- a/src/xenia/vfs/stfs.cc
+++ /dev/null
@@ -1,343 +0,0 @@
-/**
- ******************************************************************************
- * Xenia : Xbox 360 Emulator Research Project *
- ******************************************************************************
- * Copyright 2014 Ben Vanik. All rights reserved. *
- * Released under the BSD license - see LICENSE in the root for more details. *
- ******************************************************************************
- * Major contributions to this file from:
- * - free60
- */
-
-#include "xenia/vfs/stfs.h"
-
-#include
-
-#include "xenia/base/logging.h"
-
-namespace xe {
-namespace vfs {
-
-#define XEGETUINT24BE(p) \
- (((uint32_t)xe::load_and_swap((p) + 0) << 16) | \
- ((uint32_t)xe::load_and_swap((p) + 1) << 8) | \
- (uint32_t)xe::load_and_swap((p) + 2))
-#define XEGETUINT24LE(p) \
- (((uint32_t)xe::load((p) + 2) << 16) | \
- ((uint32_t)xe::load((p) + 1) << 8) | \
- (uint32_t)xe::load((p) + 0))
-
-bool STFSVolumeDescriptor::Read(const uint8_t* p) {
- descriptor_size = xe::load_and_swap(p + 0x00);
- if (descriptor_size != 0x24) {
- XELOGE("STFS volume descriptor size mismatch, expected 0x24 but got 0x%X",
- descriptor_size);
- return false;
- }
- reserved = xe::load_and_swap(p + 0x01);
- block_separation = xe::load_and_swap(p + 0x02);
- file_table_block_count = xe::load_and_swap(p + 0x03);
- file_table_block_number = XEGETUINT24BE(p + 0x05);
- memcpy(top_hash_table_hash, p + 0x08, 0x14);
- total_allocated_block_count = xe::load_and_swap(p + 0x1C);
- total_unallocated_block_count = xe::load_and_swap(p + 0x20);
- return true;
-};
-
-bool STFSHeader::Read(const uint8_t* p) {
- memcpy(license_entries, p + 0x22C, 0x100);
- memcpy(header_hash, p + 0x32C, 0x14);
- header_size = xe::load_and_swap(p + 0x340);
- content_type = (STFSContentType)xe::load_and_swap(p + 0x344);
- metadata_version = xe::load_and_swap(p + 0x348);
- if (metadata_version > 1) {
- // This is a variant of thumbnail data/etc.
- // Can just ignore it for now (until we parse thumbnails).
- XELOGW("STFSContainer doesn't support version %d yet", metadata_version);
- }
- content_size = xe::load_and_swap(p + 0x34C);
- media_id = xe::load_and_swap(p + 0x354);
- version = xe::load_and_swap(p + 0x358);
- base_version = xe::load_and_swap(p + 0x35C);
- title_id = xe::load_and_swap(p + 0x360);
- platform = (STFSPlatform)xe::load_and_swap(p + 0x364);
- executable_type = xe::load_and_swap(p + 0x365);
- disc_number = xe::load_and_swap(p + 0x366);
- disc_in_set = xe::load_and_swap(p + 0x367);
- save_game_id = xe::load_and_swap(p + 0x368);
- memcpy(console_id, p + 0x36C, 0x5);
- memcpy(profile_id, p + 0x371, 0x8);
- data_file_count = xe::load_and_swap(p + 0x39D);
- data_file_combined_size = xe::load_and_swap(p + 0x3A1);
- descriptor_type = (STFSDescriptorType)xe::load_and_swap(p + 0x3A9);
- if (descriptor_type != STFS_DESCRIPTOR_STFS) {
- XELOGE("STFS descriptor format not supported: %d", descriptor_type);
- return false;
- }
- if (!volume_descriptor.Read(p + 0x379)) {
- return false;
- }
- memcpy(device_id, p + 0x3FD, 0x14);
- for (size_t n = 0; n < 0x900 / 2; n++) {
- display_names[n] = xe::load_and_swap(p + 0x411 + n * 2);
- display_descs[n] = xe::load_and_swap(p + 0xD11 + n * 2);
- }
- for (size_t n = 0; n < 0x80 / 2; n++) {
- publisher_name[n] = xe::load_and_swap(p + 0x1611 + n * 2);
- title_name[n] = xe::load_and_swap(p + 0x1691 + n * 2);
- }
- transfer_flags = xe::load_and_swap(p + 0x1711);
- thumbnail_image_size = xe::load_and_swap(p + 0x1712);
- title_thumbnail_image_size = xe::load_and_swap(p + 0x1716);
- memcpy(thumbnail_image, p + 0x171A, 0x4000);
- memcpy(title_thumbnail_image, p + 0x571A, 0x4000);
- return true;
-}
-
-STFSEntry::STFSEntry()
- : attributes(X_FILE_ATTRIBUTE_NONE),
- offset(0),
- size(0),
- update_timestamp(0),
- access_timestamp(0) {}
-
-STFSEntry* STFSEntry::GetChild(const xe::filesystem::WildcardEngine& engine,
- child_it_t& ref_it) {
- STFSEntry* child_entry(nullptr);
- while (ref_it != children.end()) {
- if (engine.Match((*ref_it)->name)) {
- child_entry = (*ref_it).get();
- ++ref_it;
- break;
- }
- ++ref_it;
- }
- return child_entry;
-}
-
-STFSEntry* STFSEntry::GetChild(const char* name) {
- // TODO(benvanik): a faster search
- for (const auto& entry : children) {
- if (strcasecmp(entry->name.c_str(), name) == 0) {
- return entry.get();
- }
- }
- return nullptr;
-}
-
-void STFSEntry::Dump(int indent) {
- printf("%s%s\n", std::string(indent, ' ').c_str(), name.c_str());
- for (const auto& entry : children) {
- entry->Dump(indent + 2);
- }
-}
-
-STFS::STFS(MappedMemory* mmap) : mmap_(mmap) {}
-
-STFS::~STFS() {}
-
-STFS::Error STFS::Load() {
- uint8_t* map_ptr = mmap_->data();
-
- auto result = ReadHeaderAndVerify(map_ptr);
- if (result != kSuccess) {
- return result;
- }
-
- result = ReadAllEntries(map_ptr);
- if (result != kSuccess) {
- return result;
- }
-
- return kSuccess;
-}
-
-void STFS::Dump() {
- if (root_entry_) {
- root_entry_->Dump(0);
- }
-}
-
-STFS::Error STFS::ReadHeaderAndVerify(const uint8_t* map_ptr) {
- // Check signature.
- if (memcmp(map_ptr, "LIVE", 4) == 0) {
- package_type_ = STFS_PACKAGE_LIVE;
- } else if (memcmp(map_ptr, "PIRS", 4) == 0) {
- package_type_ = STFS_PACKAGE_PIRS;
- } else if (memcmp(map_ptr, "CON", 3) == 0) {
- package_type_ = STFS_PACKAGE_CON;
- } else {
- // Unexpected format.
- return STFS::Error::kErrorFileMismatch;
- }
-
- // Read header.
- if (!header_.Read(map_ptr)) {
- return STFS::Error::kErrorDamagedFile;
- }
-
- if (((header_.header_size + 0x0FFF) & 0xB000) == 0xB000) {
- table_size_shift_ = 0;
- } else {
- table_size_shift_ = 1;
- }
-
- return kSuccess;
-}
-
-STFS::Error STFS::ReadAllEntries(const uint8_t* map_ptr) {
- root_entry_.reset(new STFSEntry());
- root_entry_->attributes = X_FILE_ATTRIBUTE_DIRECTORY;
-
- std::vector entries;
-
- // Load all listings.
- auto& volume_descriptor = header_.volume_descriptor;
- uint32_t table_block_index = volume_descriptor.file_table_block_number;
- for (size_t n = 0; n < volume_descriptor.file_table_block_count; n++) {
- const uint8_t* p =
- map_ptr + BlockToOffset(ComputeBlockNumber(table_block_index));
- for (size_t m = 0; m < 0x1000 / 0x40; m++) {
- const uint8_t* filename = p; // 0x28b
- if (filename[0] == 0) {
- // Done.
- break;
- }
- uint8_t filename_length_flags = xe::load_and_swap(p + 0x28);
- uint32_t allocated_block_count = XEGETUINT24LE(p + 0x29);
- uint32_t start_block_index = XEGETUINT24LE(p + 0x2F);
- uint16_t path_indicator = xe::load_and_swap(p + 0x32);
- uint32_t file_size = xe::load_and_swap(p + 0x34);
- uint32_t update_timestamp = xe::load_and_swap(p + 0x38);
- uint32_t access_timestamp = xe::load_and_swap(p + 0x3C);
- p += 0x40;
-
- auto entry = std::make_unique();
- entry->name = std::string((char*)filename, filename_length_flags & 0x3F);
- // bit 0x40 = consecutive blocks (not fragmented?)
- if (filename_length_flags & 0x80) {
- entry->attributes = X_FILE_ATTRIBUTE_DIRECTORY;
- } else {
- entry->attributes = X_FILE_ATTRIBUTE_NORMAL;
- entry->offset = BlockToOffset(ComputeBlockNumber(start_block_index));
- entry->size = file_size;
- }
- entry->update_timestamp = update_timestamp;
- entry->access_timestamp = access_timestamp;
- entries.push_back(entry.get());
-
- // Fill in all block records.
- // It's easier to do this now and just look them up later, at the cost
- // of some memory. Nasty chain walk.
- // TODO(benvanik): optimize if flag 0x40 (consecutive) is set.
- if (entry->attributes & X_FILE_ATTRIBUTE_NORMAL) {
- uint32_t block_index = start_block_index;
- size_t remaining_size = file_size;
- uint32_t info = 0x80;
- while (remaining_size && block_index && info >= 0x80) {
- size_t block_size = std::min(0x1000ull, remaining_size);
- size_t offset = BlockToOffset(ComputeBlockNumber(block_index));
- entry->block_list.push_back({offset, block_size});
- remaining_size -= block_size;
- auto block_hash = GetBlockHash(map_ptr, block_index, 0);
- if (table_size_shift_ && block_hash.info < 0x80) {
- block_hash = GetBlockHash(map_ptr, block_index, 1);
- }
- block_index = block_hash.next_block_index;
- info = block_hash.info;
- }
- }
-
- if (path_indicator == 0xFFFF) {
- // Root entry.
- root_entry_->children.push_back(std::move(entry));
- } else {
- // Lookup and add.
- auto parent = entries[path_indicator];
- parent->children.push_back(std::move(entry));
- }
- }
-
- auto block_hash = GetBlockHash(map_ptr, table_block_index, 0);
- if (table_size_shift_ && block_hash.info < 0x80) {
- block_hash = GetBlockHash(map_ptr, table_block_index, 1);
- }
- table_block_index = block_hash.next_block_index;
- if (table_block_index == 0xFFFFFF) {
- break;
- }
- }
-
- return kSuccess;
-}
-
-size_t STFS::BlockToOffset(uint32_t block) {
- if (block >= 0xFFFFFF) {
- return -1;
- } else {
- return ((header_.header_size + 0x0FFF) & 0xF000) + (block << 12);
- }
-}
-
-uint32_t STFS::ComputeBlockNumber(uint32_t block_index) {
- uint32_t block_shift = 0;
- if (((header_.header_size + 0x0FFF) & 0xB000) == 0xB000) {
- block_shift = 1;
- } else {
- if ((header_.volume_descriptor.block_separation & 0x1) == 0x1) {
- block_shift = 0;
- } else {
- block_shift = 1;
- }
- }
-
- uint32_t base = (block_index + 0xAA) / 0xAA;
- if (package_type_ == STFS_PACKAGE_CON) {
- base <<= block_shift;
- }
- uint32_t block = base + block_index;
- if (block_index >= 0xAA) {
- base = (block_index + 0x70E4) / 0x70E4;
- if (package_type_ == STFS_PACKAGE_CON) {
- base <<= block_shift;
- }
- block += base;
- if (block_index >= 0x70E4) {
- base = (block_index + 0x4AF768) / 0x4AF768;
- if (package_type_ == STFS_PACKAGE_CON) {
- base <<= block_shift;
- }
- block += base;
- }
- }
- return block;
-}
-
-STFS::BlockHash_t STFS::GetBlockHash(const uint8_t* map_ptr,
- uint32_t block_index,
- uint32_t table_offset) {
- static const uint32_t table_spacing[] = {
- 0xAB, 0x718F,
- 0xFE7DA, // The distance in blocks between tables
- 0xAC, 0x723A,
- 0xFD00B, // For when tables are 1 block and when they are 2 blocks
- };
- uint32_t record = block_index % 0xAA;
- uint32_t table_index =
- (block_index / 0xAA) * table_spacing[table_size_shift_ * 3 + 0];
- if (block_index >= 0xAA) {
- table_index += ((block_index / 0x70E4) + 1) << table_size_shift_;
- if (block_index >= 0x70E4) {
- table_index += 1 << table_size_shift_;
- }
- }
- // table_index += table_offset - (1 << table_size_shift_);
- const uint8_t* hash_data = map_ptr + BlockToOffset(table_index);
- const uint8_t* record_data = hash_data + record * 0x18;
- uint32_t info = xe::load_and_swap(record_data + 0x14);
- uint32_t next_block_index = XEGETUINT24BE(record_data + 0x15);
- return {next_block_index, info};
-}
-
-} // namespace vfs
-} // namespace xe
diff --git a/src/xenia/vfs/stfs.h b/src/xenia/vfs/stfs.h
deleted file mode 100644
index e41151c88..000000000
--- a/src/xenia/vfs/stfs.h
+++ /dev/null
@@ -1,200 +0,0 @@
-/**
- ******************************************************************************
- * Xenia : Xbox 360 Emulator Research Project *
- ******************************************************************************
- * Copyright 2014 Ben Vanik. All rights reserved. *
- * Released under the BSD license - see LICENSE in the root for more details. *
- ******************************************************************************
- */
-
-#ifndef XENIA_VFS_STFS_H_
-#define XENIA_VFS_STFS_H_
-
-#include
-#include
-
-#include "xenia/base/filesystem.h"
-#include "xenia/base/mapped_memory.h"
-#include "xenia/vfs/entry.h"
-#include "xenia/xbox.h"
-
-namespace xe {
-namespace vfs {
-
-class STFS;
-
-// http://www.free60.org/STFS
-
-enum STFSPackageType {
- STFS_PACKAGE_CON,
- STFS_PACKAGE_PIRS,
- STFS_PACKAGE_LIVE,
-};
-
-enum STFSContentType : uint32_t {
- STFS_CONTENT_ARCADE_TITLE = 0x000D0000,
- STFS_CONTENT_AVATAR_ITEM = 0x00009000,
- STFS_CONTENT_CACHE_FILE = 0x00040000,
- STFS_CONTENT_COMMUNITY_GAME = 0x02000000,
- STFS_CONTENT_GAME_DEMO = 0x00080000,
- STFS_CONTENT_GAMER_PICTURE = 0x00020000,
- STFS_CONTENT_GAME_TITLE = 0x000A0000,
- STFS_CONTENT_GAME_TRAILER = 0x000C0000,
- STFS_CONTENT_GAME_VIDEO = 0x00400000,
- STFS_CONTENT_INSTALLED_GAME = 0x00004000,
- STFS_CONTENT_INSTALLER = 0x000B0000,
- STFS_CONTENT_IPTV_PAUSE_BUFFER = 0x00002000,
- STFS_CONTENT_LICENSE_STORE = 0x000F0000,
- STFS_CONTENT_MARKETPLACE_CONTENT = 0x00000002,
- STFS_CONTENT_MOVIE = 0x00100000,
- STFS_CONTENT_MUSIC_VIDEO = 0x00300000,
- STFS_CONTENT_PODCAST_VIDEO = 0x00500000,
- STFS_CONTENT_PROFILE = 0x00010000,
- STFS_CONTENT_PUBLISHER = 0x00000003,
- STFS_CONTENT_SAVED_GAME = 0x00000001,
- STFS_CONTENT_STORAGE_DOWNLOAD = 0x00050000,
- STFS_CONTENT_THEME = 0x00030000,
- STFS_CONTENT_TV = 0x00200000,
- STFS_CONTENT_VIDEO = 0x00090000,
- STFS_CONTENT_VIRAL_VIDEO = 0x00600000,
- STFS_CONTENT_XBOX_DOWNLOAD = 0x00070000,
- STFS_CONTENT_XBOX_ORIGINAL_GAME = 0x00005000,
- STFS_CONTENT_XBOX_SAVED_GAME = 0x00060000,
- STFS_CONTENT_XBOX_360_TITLE = 0x00001000,
- STFS_CONTENT_XBOX_TITLE = 0x00005000,
- STFS_CONTENT_XNA = 0x000E0000,
-};
-
-enum STFSPlatform : uint8_t {
- STFS_PLATFORM_XBOX_360 = 0x02,
- STFS_PLATFORM_PC = 0x04,
-};
-
-enum STFSDescriptorType : uint32_t {
- STFS_DESCRIPTOR_STFS = 0,
- STFS_DESCRIPTOR_SVOD = 1,
-};
-
-class STFSVolumeDescriptor {
- public:
- bool Read(const uint8_t* p);
-
- uint8_t descriptor_size;
- uint8_t reserved;
- uint8_t block_separation;
- uint16_t file_table_block_count;
- uint32_t file_table_block_number;
- uint8_t top_hash_table_hash[0x14];
- uint32_t total_allocated_block_count;
- uint32_t total_unallocated_block_count;
-};
-
-class STFSHeader {
- public:
- bool Read(const uint8_t* p);
-
- uint8_t license_entries[0x100];
- uint8_t header_hash[0x14];
- uint32_t header_size;
- STFSContentType content_type;
- uint32_t metadata_version;
- uint64_t content_size;
- uint32_t media_id;
- uint32_t version;
- uint32_t base_version;
- uint32_t title_id;
- STFSPlatform platform;
- uint8_t executable_type;
- uint8_t disc_number;
- uint8_t disc_in_set;
- uint32_t save_game_id;
- uint8_t console_id[0x5];
- uint8_t profile_id[0x8];
- STFSVolumeDescriptor volume_descriptor;
- uint32_t data_file_count;
- uint64_t data_file_combined_size;
- STFSDescriptorType descriptor_type;
- uint8_t device_id[0x14];
- wchar_t display_names[0x900 / 2];
- wchar_t display_descs[0x900 / 2];
- wchar_t publisher_name[0x80 / 2];
- wchar_t title_name[0x80 / 2];
- uint8_t transfer_flags;
- uint32_t thumbnail_image_size;
- uint32_t title_thumbnail_image_size;
- uint8_t thumbnail_image[0x4000];
- uint8_t title_thumbnail_image[0x4000];
-};
-
-class STFSEntry {
- public:
- STFSEntry();
-
- typedef std::vector> child_t;
- typedef child_t::iterator child_it_t;
-
- STFSEntry* GetChild(const xe::filesystem::WildcardEngine& engine,
- child_it_t& ref_it);
- STFSEntry* GetChild(const char* name);
-
- void Dump(int indent);
-
- std::string name;
- X_FILE_ATTRIBUTES attributes;
- size_t offset;
- size_t size;
- uint32_t update_timestamp;
- uint32_t access_timestamp;
- child_t children;
-
- typedef struct {
- size_t offset;
- size_t length;
- } BlockRecord_t;
- std::vector block_list;
-};
-
-class STFS {
- public:
- enum Error {
- kSuccess = 0,
- kErrorOutOfMemory = -1,
- kErrorReadError = -10,
- kErrorFileMismatch = -30,
- kErrorDamagedFile = -31,
- };
-
- STFS(MappedMemory* mmap);
- virtual ~STFS();
-
- const STFSHeader* header() const { return &header_; }
- STFSEntry* root_entry() const { return root_entry_.get(); }
-
- Error Load();
- void Dump();
-
- private:
- Error ReadHeaderAndVerify(const uint8_t* map_ptr);
- Error ReadAllEntries(const uint8_t* map_ptr);
- size_t BlockToOffset(uint32_t block);
- uint32_t ComputeBlockNumber(uint32_t block_index);
-
- typedef struct {
- uint32_t next_block_index;
- uint32_t info;
- } BlockHash_t;
- BlockHash_t GetBlockHash(const uint8_t* map_ptr, uint32_t block_index,
- uint32_t table_offset);
-
- MappedMemory* mmap_;
-
- STFSPackageType package_type_;
- STFSHeader header_;
- uint32_t table_size_shift_;
- std::unique_ptr root_entry_;
-};
-
-} // namespace vfs
-} // namespace xe
-
-#endif // XENIA_VFS_STFS_H_
diff --git a/src/xenia/vfs/virtual_file_system.cc b/src/xenia/vfs/virtual_file_system.cc
index c5ab0c39f..600f5aaca 100644
--- a/src/xenia/vfs/virtual_file_system.cc
+++ b/src/xenia/vfs/virtual_file_system.cc
@@ -45,7 +45,7 @@ bool VirtualFileSystem::UnregisterSymbolicLink(std::string path) {
return true;
}
-std::unique_ptr VirtualFileSystem::ResolvePath(const std::string& path) {
+Entry* VirtualFileSystem::ResolvePath(std::string path) {
// Resolve relative paths
std::string normalized_path(xe::filesystem::CanonicalizePath(path));
@@ -88,15 +88,5 @@ std::unique_ptr VirtualFileSystem::ResolvePath(const std::string& path) {
return nullptr;
}
-X_STATUS VirtualFileSystem::Open(std::unique_ptr entry,
- KernelState* kernel_state, Mode mode,
- bool async, XFile** out_file) {
- auto result = entry->Open(kernel_state, mode, async, out_file);
- if (XSUCCEEDED(result)) {
- entry.release();
- }
- return result;
-}
-
} // namespace vfs
} // namespace xe
diff --git a/src/xenia/vfs/virtual_file_system.h b/src/xenia/vfs/virtual_file_system.h
index e8c50af1c..b44b374e1 100644
--- a/src/xenia/vfs/virtual_file_system.h
+++ b/src/xenia/vfs/virtual_file_system.h
@@ -31,9 +31,7 @@ class VirtualFileSystem {
bool RegisterSymbolicLink(std::string path, std::string target);
bool UnregisterSymbolicLink(std::string path);
- std::unique_ptr ResolvePath(const std::string& path);
- X_STATUS Open(std::unique_ptr entry, KernelState* kernel_state,
- Mode mode, bool async, XFile** out_file);
+ Entry* ResolvePath(std::string path);
private:
std::vector> devices_;