Some fs/ cleanup.

This commit is contained in:
Ben Vanik 2014-08-16 21:36:01 -07:00
parent fdab788017
commit 383d3acbb0
11 changed files with 301 additions and 337 deletions

View File

@ -9,10 +9,14 @@
#include <xenia/kernel/fs/device.h> #include <xenia/kernel/fs/device.h>
using namespace xe; namespace xe {
using namespace xe::kernel; namespace kernel {
using namespace xe::kernel::fs; namespace fs {
Device::Device(const std::string& path) : path_(path) {} Device::Device(const std::string& path) : path_(path) {}
Device::~Device() = default; Device::~Device() = default;
} // namespace fs
} // namespace kernel
} // namespace xe

View File

@ -12,9 +12,7 @@
#include <string> #include <string>
#include <xenia/common.h>
#include <xenia/core.h> #include <xenia/core.h>
#include <xenia/kernel/fs/entry.h> #include <xenia/kernel/fs/entry.h>
namespace xe { namespace xe {

View File

@ -65,7 +65,7 @@ X_STATUS STFSContainerEntry::QueryDirectory(
auto end = (uint8_t*)out_info + length; auto end = (uint8_t*)out_info + length;
auto entry = *stfs_entry_iterator_; auto entry = stfs_entry_iterator_->get();
auto entry_name = entry->name; auto entry_name = entry->name;
if (((uint8_t*)&out_info->file_name[0]) + entry_name.size() > end) { if (((uint8_t*)&out_info->file_name[0]) + entry_name.size() > end) {

View File

@ -46,7 +46,7 @@ public:
private: private:
xe_mmap_ref mmap_; xe_mmap_ref mmap_;
STFSEntry* stfs_entry_; STFSEntry* stfs_entry_;
std::vector<STFSEntry*>::iterator stfs_entry_iterator_; std::vector<std::unique_ptr<STFSEntry>>::iterator stfs_entry_iterator_;
}; };

View File

@ -10,9 +10,9 @@
#include <xenia/kernel/fs/entry.h> #include <xenia/kernel/fs/entry.h>
#include <xenia/kernel/fs/device.h> #include <xenia/kernel/fs/device.h>
using namespace xe; namespace xe {
using namespace xe::kernel; namespace kernel {
using namespace xe::kernel::fs; namespace fs {
MemoryMapping::MemoryMapping(uint8_t* address, size_t length) MemoryMapping::MemoryMapping(uint8_t* address, size_t length)
: address_(address), length_(length) {} : address_(address), length_(length) {}
@ -28,3 +28,7 @@ Entry::Entry(Type type, Device* device, const std::string& path)
} }
Entry::~Entry() = default; Entry::~Entry() = default;
} // namespace fs
} // namespace kernel
} // namespace xe

View File

@ -12,19 +12,19 @@
#include <string> #include <string>
#include <xenia/common.h>
#include <xenia/core.h> #include <xenia/core.h>
#include <xenia/xbox.h> #include <xenia/xbox.h>
namespace xe {
XEDECLARECLASS2(xe, kernel, KernelState); namespace kernel {
XEDECLARECLASS2(xe, kernel, XFile); class KernelState;
XEDECLARECLASS2(xe, kernel, XFileInfo); class XFile;
XEDECLARECLASS2(xe, kernel, XDirectoryInfo); class XFileInfo;
XEDECLARECLASS2(xe, kernel, XVolumeInfo); class XFileSystemAttributeInfo;
XEDECLARECLASS2(xe, kernel, XFileSystemAttributeInfo); class XDirectoryInfo;
class XVolumeInfo;
} // namespace kernel
} // namespace xe
namespace xe { namespace xe {
namespace kernel { namespace kernel {
@ -32,23 +32,21 @@ namespace fs {
class Device; class Device;
class MemoryMapping { class MemoryMapping {
public: public:
MemoryMapping(uint8_t* address, size_t length); MemoryMapping(uint8_t* address, size_t length);
virtual ~MemoryMapping(); virtual ~MemoryMapping();
uint8_t* address() const { return address_; } uint8_t* address() const { return address_; }
size_t length() const { return length_; } size_t length() const { return length_; }
private: private:
uint8_t* address_; uint8_t* address_;
size_t length_; size_t length_;
}; };
class Entry { class Entry {
public: public:
enum Type { enum Type {
kTypeFile, kTypeFile,
kTypeDirectory, kTypeDirectory,
@ -64,32 +62,29 @@ public:
const std::string& name() const { return name_; } const std::string& name() const { return name_; }
virtual X_STATUS QueryInfo(XFileInfo* out_info) = 0; virtual X_STATUS QueryInfo(XFileInfo* out_info) = 0;
virtual X_STATUS QueryDirectory(XDirectoryInfo* out_info, virtual X_STATUS QueryDirectory(XDirectoryInfo* out_info, size_t length,
size_t length, const char* file_name, bool restart) = 0; const char* file_name, bool restart) = 0;
virtual bool can_map() { return false; } virtual bool can_map() { return false; }
virtual MemoryMapping* CreateMemoryMapping( virtual MemoryMapping* CreateMemoryMapping(xe_file_mode file_mode,
xe_file_mode file_mode, const size_t offset, const size_t length) { const size_t offset,
const size_t length) {
return NULL; return NULL;
} }
virtual X_STATUS Open( virtual X_STATUS Open(KernelState* kernel_state, uint32_t desired_access,
KernelState* kernel_state, bool async, XFile** out_file) = 0;
uint32_t desired_access, bool async,
XFile** out_file) = 0;
private: private:
Type type_; Type type_;
Device* device_; Device* device_;
std::string path_; std::string path_;
std::string absolute_path_; std::string absolute_path_;
std::string name_; std::string name_;
}; };
} // namespace fs } // namespace fs
} // namespace kernel } // namespace kernel
} // namespace xe } // namespace xe
#endif // XENIA_KERNEL_FS_ENTRY_H_ #endif // XENIA_KERNEL_FS_ENTRY_H_

View File

@ -14,14 +14,11 @@
#include <xenia/kernel/fs/devices/host_path_device.h> #include <xenia/kernel/fs/devices/host_path_device.h>
#include <xenia/kernel/fs/devices/stfs_container_device.h> #include <xenia/kernel/fs/devices/stfs_container_device.h>
namespace xe {
namespace kernel {
namespace fs {
using namespace xe; FileSystem::FileSystem() {}
using namespace xe::kernel;
using namespace xe::kernel::fs;
FileSystem::FileSystem() {
}
FileSystem::~FileSystem() { FileSystem::~FileSystem() {
// Delete all devices. // Delete all devices.
@ -106,14 +103,14 @@ int FileSystem::RegisterDevice(const std::string& path, Device* device) {
return 0; return 0;
} }
int FileSystem::RegisterHostPathDevice( int FileSystem::RegisterHostPathDevice(const std::string& path,
const std::string& path, const std::wstring& local_path) { const std::wstring& local_path) {
Device* device = new HostPathDevice(path, local_path); Device* device = new HostPathDevice(path, local_path);
return RegisterDevice(path, device); return RegisterDevice(path, device);
} }
int FileSystem::RegisterDiscImageDevice( int FileSystem::RegisterDiscImageDevice(const std::string& path,
const std::string& path, const std::wstring& local_path) { const std::wstring& local_path) {
DiscImageDevice* device = new DiscImageDevice(path, local_path); DiscImageDevice* device = new DiscImageDevice(path, local_path);
if (device->Init()) { if (device->Init()) {
return 1; return 1;
@ -121,8 +118,8 @@ int FileSystem::RegisterDiscImageDevice(
return RegisterDevice(path, device); return RegisterDevice(path, device);
} }
int FileSystem::RegisterSTFSContainerDevice( int FileSystem::RegisterSTFSContainerDevice(const std::string& path,
const std::string& path, const std::wstring& local_path) { const std::wstring& local_path) {
STFSContainerDevice* device = new STFSContainerDevice(path, local_path); STFSContainerDevice* device = new STFSContainerDevice(path, local_path);
if (device->Init()) { if (device->Init()) {
return 1; return 1;
@ -178,3 +175,7 @@ Entry* FileSystem::ResolvePath(const std::string& path) {
XELOGE("ResolvePath(%s) failed - no root found", path.c_str()); XELOGE("ResolvePath(%s) failed - no root found", path.c_str());
return NULL; return NULL;
} }
} // namespace fs
} // namespace kernel
} // namespace xe

View File

@ -13,21 +13,14 @@
#include <poly/math.h> #include <poly/math.h>
using namespace xe; namespace xe {
using namespace xe::kernel; namespace kernel {
using namespace xe::kernel::fs; namespace fs {
const size_t kXESectorSize = 2048;
namespace { GDFXEntry::GDFXEntry()
: attributes(X_FILE_ATTRIBUTE_NONE), offset(0), size(0) {}
#define kXESectorSize 2048
}
GDFXEntry::GDFXEntry() :
attributes(X_FILE_ATTRIBUTE_NONE), offset(0), size(0) {
}
GDFXEntry::~GDFXEntry() { GDFXEntry::~GDFXEntry() {
for (std::vector<GDFXEntry*>::iterator it = children.begin(); for (std::vector<GDFXEntry*>::iterator it = children.begin();
@ -57,7 +50,6 @@ void GDFXEntry::Dump(int indent) {
} }
} }
GDFX::GDFX(xe_mmap_ref mmap) { GDFX::GDFX(xe_mmap_ref mmap) {
mmap_ = xe_mmap_retain(mmap); mmap_ = xe_mmap_retain(mmap);
@ -70,28 +62,26 @@ GDFX::~GDFX() {
xe_mmap_release(mmap_); xe_mmap_release(mmap_);
} }
GDFXEntry* GDFX::root_entry() { GDFXEntry* GDFX::root_entry() { return root_entry_; }
return root_entry_;
}
GDFX::Error GDFX::Load() { GDFX::Error GDFX::Load() {
Error result = kErrorOutOfMemory;
ParseState state; ParseState state;
xe_zero_struct(&state, sizeof(state)); xe_zero_struct(&state, sizeof(state));
state.ptr = (uint8_t*)xe_mmap_get_addr(mmap_); state.ptr = (uint8_t*)xe_mmap_get_addr(mmap_);
state.size = xe_mmap_get_length(mmap_); state.size = xe_mmap_get_length(mmap_);
result = Verify(state); auto result = Verify(state);
XEEXPECTZERO(result); if (result != kSuccess) {
return result;
}
result = ReadAllEntries(state, state.ptr + state.root_offset); result = ReadAllEntries(state, state.ptr + state.root_offset);
XEEXPECTZERO(result); if (result != kSuccess) {
return result;
}
result = kSuccess; return kSuccess;
XECLEANUP:
return result;
} }
void GDFX::Dump() { void GDFX::Dump() {
@ -103,7 +93,7 @@ void GDFX::Dump() {
GDFX::Error GDFX::Verify(ParseState& state) { GDFX::Error GDFX::Verify(ParseState& state) {
// Find sector 32 of the game partition - try at a few points. // Find sector 32 of the game partition - try at a few points.
const static size_t likely_offsets[] = { const static size_t likely_offsets[] = {
0x00000000, 0x0000FB20, 0x00020600, 0x0FD90000, 0x00000000, 0x0000FB20, 0x00020600, 0x0FD90000,
}; };
bool magic_found = false; bool magic_found = false;
for (size_t n = 0; n < poly::countof(likely_offsets); n++) { for (size_t n = 0; n < poly::countof(likely_offsets); n++) {
@ -124,10 +114,9 @@ GDFX::Error GDFX::Verify(ParseState& state) {
} }
uint8_t* fs_ptr = state.ptr + state.game_offset + (32 * kXESectorSize); uint8_t* fs_ptr = state.ptr + state.game_offset + (32 * kXESectorSize);
state.root_sector = poly::load<uint32_t>(fs_ptr + 20); state.root_sector = poly::load<uint32_t>(fs_ptr + 20);
state.root_size = poly::load<uint32_t>(fs_ptr + 24); state.root_size = poly::load<uint32_t>(fs_ptr + 24);
state.root_offset = state.game_offset + (state.root_sector * kXESectorSize); state.root_offset = state.game_offset + (state.root_sector * kXESectorSize);
if (state.root_size < 13 || if (state.root_size < 13 || state.root_size > 32 * 1024 * 1024) {
state.root_size > 32 * 1024 * 1024) {
return kErrorDamagedFile; return kErrorDamagedFile;
} }
@ -143,8 +132,8 @@ GDFX::Error GDFX::ReadAllEntries(ParseState& state,
const uint8_t* root_buffer) { const uint8_t* root_buffer) {
root_entry_ = new GDFXEntry(); root_entry_ = new GDFXEntry();
root_entry_->offset = 0; root_entry_->offset = 0;
root_entry_->size = 0; root_entry_->size = 0;
root_entry_->name = ""; root_entry_->name = "";
root_entry_->attributes = X_FILE_ATTRIBUTE_DIRECTORY; root_entry_->attributes = X_FILE_ATTRIBUTE_DIRECTORY;
if (!ReadEntry(state, root_buffer, 0, root_entry_)) { if (!ReadEntry(state, root_buffer, 0, root_entry_)) {
@ -158,13 +147,13 @@ bool GDFX::ReadEntry(ParseState& state, const uint8_t* buffer,
uint16_t entry_ordinal, GDFXEntry* parent) { uint16_t entry_ordinal, GDFXEntry* parent) {
const uint8_t* p = buffer + (entry_ordinal * 4); const uint8_t* p = buffer + (entry_ordinal * 4);
uint16_t node_l = poly::load<uint16_t>(p + 0); uint16_t node_l = poly::load<uint16_t>(p + 0);
uint16_t node_r = poly::load<uint16_t>(p + 2); uint16_t node_r = poly::load<uint16_t>(p + 2);
size_t sector = poly::load<uint32_t>(p + 4); size_t sector = poly::load<uint32_t>(p + 4);
size_t length = poly::load<uint32_t>(p + 8); size_t length = poly::load<uint32_t>(p + 8);
uint8_t attributes = poly::load<uint8_t>(p + 12); uint8_t attributes = poly::load<uint8_t>(p + 12);
uint8_t name_length = poly::load<uint8_t>(p + 13); uint8_t name_length = poly::load<uint8_t>(p + 13);
char* name = (char*)(p + 14); char* name = (char*)(p + 14);
if (node_l && !ReadEntry(state, buffer, node_l, parent)) { if (node_l && !ReadEntry(state, buffer, node_l, parent)) {
return false; return false;
@ -181,7 +170,7 @@ bool GDFX::ReadEntry(ParseState& state, const uint8_t* buffer,
if (attributes & X_FILE_ATTRIBUTE_DIRECTORY) { if (attributes & X_FILE_ATTRIBUTE_DIRECTORY) {
// Folder. // Folder.
entry->offset = 0; entry->offset = 0;
entry->size = 0; entry->size = 0;
if (length) { if (length) {
// Not a leaf - read in children. // Not a leaf - read in children.
if (state.size < state.game_offset + (sector * kXESectorSize)) { if (state.size < state.game_offset + (sector * kXESectorSize)) {
@ -198,7 +187,7 @@ bool GDFX::ReadEntry(ParseState& state, const uint8_t* buffer,
} else { } else {
// File. // File.
entry->offset = state.game_offset + (sector * kXESectorSize); entry->offset = state.game_offset + (sector * kXESectorSize);
entry->size = length; entry->size = length;
} }
// Read next file in the list. // Read next file in the list.
@ -208,3 +197,7 @@ bool GDFX::ReadEntry(ParseState& state, const uint8_t* buffer,
return true; return true;
} }
} // namespace fs
} // namespace kernel
} // namespace xe

View File

@ -10,25 +10,20 @@
#ifndef XENIA_KERNEL_FS_GDFX_H_ #ifndef XENIA_KERNEL_FS_GDFX_H_
#define XENIA_KERNEL_FS_GDFX_H_ #define XENIA_KERNEL_FS_GDFX_H_
#include <xenia/common.h>
#include <xenia/core.h>
#include <vector> #include <vector>
#include <xenia/core.h>
#include <xenia/xbox.h> #include <xenia/xbox.h>
#include <xenia/kernel/fs/entry.h> #include <xenia/kernel/fs/entry.h>
namespace xe { namespace xe {
namespace kernel { namespace kernel {
namespace fs { namespace fs {
class GDFX; class GDFX;
class GDFXEntry { class GDFXEntry {
public: public:
GDFXEntry(); GDFXEntry();
~GDFXEntry(); ~GDFXEntry();
@ -36,23 +31,22 @@ public:
void Dump(int indent); void Dump(int indent);
std::string name; std::string name;
X_FILE_ATTRIBUTES attributes; X_FILE_ATTRIBUTES attributes;
size_t offset; size_t offset;
size_t size; size_t size;
std::vector<GDFXEntry*> children; std::vector<GDFXEntry*> children;
}; };
class GDFX { class GDFX {
public: public:
enum Error { enum Error {
kSuccess = 0, kSuccess = 0,
kErrorOutOfMemory = -1, kErrorOutOfMemory = -1,
kErrorReadError = -10, kErrorReadError = -10,
kErrorFileMismatch = -30, kErrorFileMismatch = -30,
kErrorDamagedFile = -31, kErrorDamagedFile = -31,
}; };
GDFX(xe_mmap_ref mmap); GDFX(xe_mmap_ref mmap);
@ -63,17 +57,22 @@ public:
Error Load(); Error Load();
void Dump(); void Dump();
private: private:
typedef struct { typedef struct {
uint8_t* ptr; uint8_t* ptr;
size_t size; // Size (bytes) of total image // Size (bytes) of total image.
size_t size;
size_t game_offset; // Offset (bytes) of game partition // Offset (bytes) of game partition.
size_t game_offset;
size_t root_sector; // Offset (sector) of root // Offset (sector) of root.
size_t root_offset; // Offset (bytes) of root size_t root_sector;
size_t root_size; // Size (bytes) of root // Offset (bytes) of root.
size_t root_offset;
// Size (bytes) of root.
size_t root_size;
} ParseState; } ParseState;
Error Verify(ParseState& state); Error Verify(ParseState& state);
@ -84,13 +83,11 @@ private:
xe_mmap_ref mmap_; xe_mmap_ref mmap_;
GDFXEntry* root_entry_; GDFXEntry* root_entry_;
}; };
} // namespace fs } // namespace fs
} // namespace kernel } // namespace kernel
} // namespace xe } // namespace xe
#endif // XENIA_KERNEL_FS_GDFX_H_ #endif // XENIA_KERNEL_FS_GDFX_H_

View File

@ -13,63 +13,62 @@
#include <algorithm> #include <algorithm>
using namespace xe; namespace xe {
using namespace xe::kernel; namespace kernel {
using namespace xe::kernel::fs; namespace fs {
#define XEGETUINT24BE(p) \ #define XEGETUINT24BE(p) \
(((uint32_t)poly::load_and_swap<uint8_t>((p) + 0) << 16) | \ (((uint32_t)poly::load_and_swap<uint8_t>((p)+0) << 16) | \
((uint32_t)poly::load_and_swap<uint8_t>((p) + 1) << 8) | \ ((uint32_t)poly::load_and_swap<uint8_t>((p)+1) << 8) | \
(uint32_t)poly::load_and_swap<uint8_t>((p) + 2)) (uint32_t)poly::load_and_swap<uint8_t>((p)+2))
#define XEGETUINT24LE(p) \ #define XEGETUINT24LE(p) \
(((uint32_t)poly::load<uint8_t>((p) + 2) << 16) | \ (((uint32_t)poly::load<uint8_t>((p)+2) << 16) | \
((uint32_t)poly::load<uint8_t>((p) + 1) << 8) | \ ((uint32_t)poly::load<uint8_t>((p)+1) << 8) | \
(uint32_t)poly::load<uint8_t>((p) + 0)) (uint32_t)poly::load<uint8_t>((p)+0))
bool STFSVolumeDescriptor::Read(const uint8_t* p) { bool STFSVolumeDescriptor::Read(const uint8_t* p) {
descriptor_size = poly::load_and_swap<uint8_t>(p + 0x00); descriptor_size = poly::load_and_swap<uint8_t>(p + 0x00);
if (descriptor_size != 0x24) { if (descriptor_size != 0x24) {
XELOGE("STFS volume descriptor size mismatch, expected 0x24 but got 0x%X", XELOGE("STFS volume descriptor size mismatch, expected 0x24 but got 0x%X",
descriptor_size); descriptor_size);
return false; return false;
} }
reserved = poly::load_and_swap<uint8_t>(p + 0x01); reserved = poly::load_and_swap<uint8_t>(p + 0x01);
block_separation = poly::load_and_swap<uint8_t>(p + 0x02); block_separation = poly::load_and_swap<uint8_t>(p + 0x02);
file_table_block_count = poly::load_and_swap<uint16_t>(p + 0x03); file_table_block_count = poly::load_and_swap<uint16_t>(p + 0x03);
file_table_block_number = XEGETUINT24BE(p + 0x05); file_table_block_number = XEGETUINT24BE(p + 0x05);
xe_copy_struct(top_hash_table_hash, p + 0x08, 0x14); xe_copy_struct(top_hash_table_hash, p + 0x08, 0x14);
total_allocated_block_count = poly::load_and_swap<uint32_t>(p + 0x1C); total_allocated_block_count = poly::load_and_swap<uint32_t>(p + 0x1C);
total_unallocated_block_count = poly::load_and_swap<uint32_t>(p + 0x20); total_unallocated_block_count = poly::load_and_swap<uint32_t>(p + 0x20);
return true; return true;
}; };
bool STFSHeader::Read(const uint8_t* p) { bool STFSHeader::Read(const uint8_t* p) {
xe_copy_struct(license_entries, p + 0x22C, 0x100); xe_copy_struct(license_entries, p + 0x22C, 0x100);
xe_copy_struct(header_hash, p + 0x32C, 0x14); xe_copy_struct(header_hash, p + 0x32C, 0x14);
header_size = poly::load_and_swap<uint32_t>(p + 0x340); header_size = poly::load_and_swap<uint32_t>(p + 0x340);
content_type = (STFSContentType)poly::load_and_swap<uint32_t>(p + 0x344); content_type = (STFSContentType)poly::load_and_swap<uint32_t>(p + 0x344);
metadata_version = poly::load_and_swap<uint32_t>(p + 0x348); metadata_version = poly::load_and_swap<uint32_t>(p + 0x348);
if (metadata_version > 1) { if (metadata_version > 1) {
// This is a variant of thumbnail data/etc. // This is a variant of thumbnail data/etc.
// Can just ignore it for now (until we parse thumbnails). // Can just ignore it for now (until we parse thumbnails).
XELOGW("STFSContainer doesn't support version %d yet", metadata_version); XELOGW("STFSContainer doesn't support version %d yet", metadata_version);
} }
content_size = poly::load_and_swap<uint32_t>(p + 0x34C); content_size = poly::load_and_swap<uint32_t>(p + 0x34C);
media_id = poly::load_and_swap<uint32_t>(p + 0x354); media_id = poly::load_and_swap<uint32_t>(p + 0x354);
version = poly::load_and_swap<uint32_t>(p + 0x358); version = poly::load_and_swap<uint32_t>(p + 0x358);
base_version = poly::load_and_swap<uint32_t>(p + 0x35C); base_version = poly::load_and_swap<uint32_t>(p + 0x35C);
title_id = poly::load_and_swap<uint32_t>(p + 0x360); title_id = poly::load_and_swap<uint32_t>(p + 0x360);
platform = (STFSPlatform)poly::load_and_swap<uint8_t>(p + 0x364); platform = (STFSPlatform)poly::load_and_swap<uint8_t>(p + 0x364);
executable_type = poly::load_and_swap<uint8_t>(p + 0x365); executable_type = poly::load_and_swap<uint8_t>(p + 0x365);
disc_number = poly::load_and_swap<uint8_t>(p + 0x366); disc_number = poly::load_and_swap<uint8_t>(p + 0x366);
disc_in_set = poly::load_and_swap<uint8_t>(p + 0x367); disc_in_set = poly::load_and_swap<uint8_t>(p + 0x367);
save_game_id = poly::load_and_swap<uint32_t>(p + 0x368); save_game_id = poly::load_and_swap<uint32_t>(p + 0x368);
xe_copy_struct(console_id, p + 0x36C, 0x5); xe_copy_struct(console_id, p + 0x36C, 0x5);
xe_copy_struct(profile_id, p + 0x371, 0x8); xe_copy_struct(profile_id, p + 0x371, 0x8);
data_file_count = poly::load_and_swap<uint32_t>(p + 0x39D); data_file_count = poly::load_and_swap<uint32_t>(p + 0x39D);
data_file_combined_size = poly::load_and_swap<uint64_t>(p + 0x3A1); data_file_combined_size = poly::load_and_swap<uint64_t>(p + 0x3A1);
descriptor_type = (STFSDescriptorType)poly::load_and_swap<uint8_t>(p + 0x3A9); descriptor_type = (STFSDescriptorType)poly::load_and_swap<uint8_t>(p + 0x3A9);
if (descriptor_type != STFS_DESCRIPTOR_STFS) { if (descriptor_type != STFS_DESCRIPTOR_STFS) {
XELOGE("STFS descriptor format not supported: %d", descriptor_type); XELOGE("STFS descriptor format not supported: %d", descriptor_type);
return false; return false;
@ -86,76 +85,61 @@ bool STFSHeader::Read(const uint8_t* p) {
publisher_name[n] = poly::load_and_swap<uint16_t>(p + 0x1611 + n * 2); publisher_name[n] = poly::load_and_swap<uint16_t>(p + 0x1611 + n * 2);
title_name[n] = poly::load_and_swap<uint16_t>(p + 0x1691 + n * 2); title_name[n] = poly::load_and_swap<uint16_t>(p + 0x1691 + n * 2);
} }
transfer_flags = poly::load_and_swap<uint8_t>(p + 0x1711); transfer_flags = poly::load_and_swap<uint8_t>(p + 0x1711);
thumbnail_image_size = poly::load_and_swap<uint32_t>(p + 0x1712); thumbnail_image_size = poly::load_and_swap<uint32_t>(p + 0x1712);
title_thumbnail_image_size = poly::load_and_swap<uint32_t>(p + 0x1716); title_thumbnail_image_size = poly::load_and_swap<uint32_t>(p + 0x1716);
xe_copy_struct(thumbnail_image, p + 0x171A, 0x4000); xe_copy_struct(thumbnail_image, p + 0x171A, 0x4000);
xe_copy_struct(title_thumbnail_image, p + 0x571A, 0x4000); xe_copy_struct(title_thumbnail_image, p + 0x571A, 0x4000);
return true; return true;
} }
STFSEntry::STFSEntry() : STFSEntry::STFSEntry()
attributes(X_FILE_ATTRIBUTE_NONE), offset(0), size(0), : attributes(X_FILE_ATTRIBUTE_NONE),
update_timestamp(0), access_timestamp(0) { offset(0),
} size(0),
update_timestamp(0),
STFSEntry::~STFSEntry() { access_timestamp(0) {}
for (auto it = children.begin(); it != children.end(); ++it) {
delete *it;
}
}
STFSEntry* STFSEntry::GetChild(const char* name) { STFSEntry* STFSEntry::GetChild(const char* name) {
// TODO(benvanik): a faster search // TODO(benvanik): a faster search
for (auto it = children.begin(); it != children.end(); ++it) { for (const auto& entry : children) {
STFSEntry* entry = *it;
if (strcasecmp(entry->name.c_str(), name) == 0) { if (strcasecmp(entry->name.c_str(), name) == 0) {
return entry; return entry.get();
} }
} }
return NULL; return nullptr;
} }
void STFSEntry::Dump(int indent) { void STFSEntry::Dump(int indent) {
printf("%s%s\n", std::string(indent, ' ').c_str(), name.c_str()); printf("%s%s\n", std::string(indent, ' ').c_str(), name.c_str());
for (auto it = children.begin(); it != children.end(); ++it) { for (const auto& entry : children) {
STFSEntry* entry = *it;
entry->Dump(indent + 2); entry->Dump(indent + 2);
} }
} }
STFS::STFS(xe_mmap_ref mmap) { STFS::STFS(xe_mmap_ref mmap) {
mmap_ = xe_mmap_retain(mmap); mmap_ = xe_mmap_retain(mmap);
root_entry_ = NULL;
} }
STFS::~STFS() { STFS::~STFS() {
delete root_entry_;
xe_mmap_release(mmap_); xe_mmap_release(mmap_);
} }
STFSEntry* STFS::root_entry() {
return root_entry_;
}
STFS::Error STFS::Load() { STFS::Error STFS::Load() {
Error result = kErrorOutOfMemory;
uint8_t* map_ptr = (uint8_t*)xe_mmap_get_addr(mmap_); uint8_t* map_ptr = (uint8_t*)xe_mmap_get_addr(mmap_);
size_t map_size = xe_mmap_get_length(mmap_); size_t map_size = xe_mmap_get_length(mmap_);
result = ReadHeaderAndVerify(map_ptr); auto result = ReadHeaderAndVerify(map_ptr);
XEEXPECTZERO(result); if (result != kSuccess) {
return result;
}
result = ReadAllEntries(map_ptr); result = ReadAllEntries(map_ptr);
XEEXPECTZERO(result); if (result != kSuccess) {
return result;
}
result = kSuccess; return kSuccess;
XECLEANUP:
return result;
} }
void STFS::Dump() { void STFS::Dump() {
@ -192,10 +176,7 @@ STFS::Error STFS::ReadHeaderAndVerify(const uint8_t* map_ptr) {
} }
STFS::Error STFS::ReadAllEntries(const uint8_t* map_ptr) { STFS::Error STFS::ReadAllEntries(const uint8_t* map_ptr) {
root_entry_ = new STFSEntry(); root_entry_.reset(new STFSEntry());
root_entry_->offset = 0;
root_entry_->size = 0;
root_entry_->name = "";
root_entry_->attributes = X_FILE_ATTRIBUTE_DIRECTORY; root_entry_->attributes = X_FILE_ATTRIBUTE_DIRECTORY;
std::vector<STFSEntry*> entries; std::vector<STFSEntry*> entries;
@ -207,21 +188,21 @@ STFS::Error STFS::ReadAllEntries(const uint8_t* map_ptr) {
const uint8_t* p = const uint8_t* p =
map_ptr + BlockToOffset(ComputeBlockNumber(table_block_index)); map_ptr + BlockToOffset(ComputeBlockNumber(table_block_index));
for (size_t m = 0; m < 0x1000 / 0x40; m++) { for (size_t m = 0; m < 0x1000 / 0x40; m++) {
const uint8_t* filename = p; // 0x28b const uint8_t* filename = p; // 0x28b
if (filename[0] == 0) { if (filename[0] == 0) {
// Done. // Done.
break; break;
} }
uint8_t filename_length_flags = poly::load_and_swap<uint8_t>(p + 0x28); uint8_t filename_length_flags = poly::load_and_swap<uint8_t>(p + 0x28);
uint32_t allocated_block_count = XEGETUINT24LE(p + 0x29); uint32_t allocated_block_count = XEGETUINT24LE(p + 0x29);
uint32_t start_block_index = XEGETUINT24LE(p + 0x2F); uint32_t start_block_index = XEGETUINT24LE(p + 0x2F);
uint16_t path_indicator = poly::load_and_swap<uint16_t>(p + 0x32); uint16_t path_indicator = poly::load_and_swap<uint16_t>(p + 0x32);
uint32_t file_size = poly::load_and_swap<uint32_t>(p + 0x34); uint32_t file_size = poly::load_and_swap<uint32_t>(p + 0x34);
uint32_t update_timestamp = poly::load_and_swap<uint32_t>(p + 0x38); uint32_t update_timestamp = poly::load_and_swap<uint32_t>(p + 0x38);
uint32_t access_timestamp = poly::load_and_swap<uint32_t>(p + 0x3C); uint32_t access_timestamp = poly::load_and_swap<uint32_t>(p + 0x3C);
p += 0x40; p += 0x40;
STFSEntry* entry = new STFSEntry(); auto entry = std::make_unique<STFSEntry>();
entry->name = std::string((char*)filename, filename_length_flags & 0x3F); entry->name = std::string((char*)filename, filename_length_flags & 0x3F);
entry->name.append(1, '\0'); entry->name.append(1, '\0');
// bit 0x40 = consecutive blocks (not fragmented?) // bit 0x40 = consecutive blocks (not fragmented?)
@ -229,23 +210,12 @@ STFS::Error STFS::ReadAllEntries(const uint8_t* map_ptr) {
entry->attributes = X_FILE_ATTRIBUTE_DIRECTORY; entry->attributes = X_FILE_ATTRIBUTE_DIRECTORY;
} else { } else {
entry->attributes = X_FILE_ATTRIBUTE_NORMAL; entry->attributes = X_FILE_ATTRIBUTE_NORMAL;
entry->offset = BlockToOffset(ComputeBlockNumber(start_block_index)); entry->offset = BlockToOffset(ComputeBlockNumber(start_block_index));
entry->size = file_size; entry->size = file_size;
} }
entry->update_timestamp = update_timestamp; entry->update_timestamp = update_timestamp;
entry->access_timestamp = access_timestamp; entry->access_timestamp = access_timestamp;
entries.push_back(entry); entries.push_back(entry.get());
const uint8_t* p = map_ptr + entry->offset;
if (path_indicator == 0xFFFF) {
// Root entry.
root_entry_->children.push_back(entry);
} else {
// Lookup and add.
auto parent = entries[path_indicator];
parent->children.push_back(entry);
}
// Fill in all block records. // Fill in all block records.
// It's easier to do this now and just look them up later, at the cost // It's easier to do this now and just look them up later, at the cost
@ -255,12 +225,10 @@ STFS::Error STFS::ReadAllEntries(const uint8_t* map_ptr) {
uint32_t block_index = start_block_index; uint32_t block_index = start_block_index;
size_t remaining_size = file_size; size_t remaining_size = file_size;
uint32_t info = 0x80; uint32_t info = 0x80;
while (remaining_size && while (remaining_size && block_index && info >= 0x80) {
block_index &&
info >= 0x80) {
size_t block_size = std::min(0x1000ull, remaining_size); size_t block_size = std::min(0x1000ull, remaining_size);
size_t offset = BlockToOffset(ComputeBlockNumber(block_index)); size_t offset = BlockToOffset(ComputeBlockNumber(block_index));
entry->block_list.push_back({ offset, block_size }); entry->block_list.push_back({offset, block_size});
remaining_size -= block_size; remaining_size -= block_size;
auto block_hash = GetBlockHash(map_ptr, block_index, 0); auto block_hash = GetBlockHash(map_ptr, block_index, 0);
if (table_size_shift_ && block_hash.info < 0x80) { if (table_size_shift_ && block_hash.info < 0x80) {
@ -270,6 +238,15 @@ STFS::Error STFS::ReadAllEntries(const uint8_t* map_ptr) {
info = block_hash.info; 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); auto block_hash = GetBlockHash(map_ptr, table_block_index, 0);
@ -327,12 +304,14 @@ uint32_t STFS::ComputeBlockNumber(uint32_t block_index) {
return block; return block;
} }
STFS::BlockHash_t STFS::GetBlockHash( STFS::BlockHash_t STFS::GetBlockHash(const uint8_t* map_ptr,
const uint8_t* map_ptr, uint32_t block_index,
uint32_t block_index, uint32_t table_offset) { uint32_t table_offset) {
static const uint32_t table_spacing[] = { static const uint32_t table_spacing[] = {
0xAB, 0x718F, 0xFE7DA, // The distance in blocks between tables 0xAB, 0x718F,
0xAC, 0x723A, 0xFD00B, // For when tables are 1 block and when they are 2 blocks 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 record = block_index % 0xAA;
uint32_t table_index = uint32_t table_index =
@ -343,10 +322,14 @@ STFS::BlockHash_t STFS::GetBlockHash(
table_index += 1 << table_size_shift_; table_index += 1 << table_size_shift_;
} }
} }
//table_index += table_offset - (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* hash_data = map_ptr + BlockToOffset(table_index);
const uint8_t* record_data = hash_data + record * 0x18; const uint8_t* record_data = hash_data + record * 0x18;
uint32_t info = poly::load_and_swap<uint8_t>(record_data + 0x14); uint32_t info = poly::load_and_swap<uint8_t>(record_data + 0x14);
uint32_t next_block_index = XEGETUINT24BE(record_data + 0x15); uint32_t next_block_index = XEGETUINT24BE(record_data + 0x15);
return{ next_block_index, info }; return {next_block_index, info};
} }
} // namespace fs
} // namespace kernel
} // namespace xe

View File

@ -10,23 +10,19 @@
#ifndef XENIA_KERNEL_FS_STFS_H_ #ifndef XENIA_KERNEL_FS_STFS_H_
#define XENIA_KERNEL_FS_STFS_H_ #define XENIA_KERNEL_FS_STFS_H_
#include <xenia/common.h> #include <memory>
#include <xenia/core.h>
#include <vector> #include <vector>
#include <xenia/core.h>
#include <xenia/xbox.h> #include <xenia/xbox.h>
#include <xenia/kernel/fs/entry.h> #include <xenia/kernel/fs/entry.h>
namespace xe { namespace xe {
namespace kernel { namespace kernel {
namespace fs { namespace fs {
class STFS; class STFS;
// http://www.free60.org/STFS // http://www.free60.org/STFS
enum STFSPackageType { enum STFSPackageType {
@ -36,120 +32,116 @@ enum STFSPackageType {
}; };
enum STFSContentType : uint32_t { enum STFSContentType : uint32_t {
STFS_CONTENT_ARCADE_TITLE = 0x000D0000, STFS_CONTENT_ARCADE_TITLE = 0x000D0000,
STFS_CONTENT_AVATAR_ITEM = 0x00009000, STFS_CONTENT_AVATAR_ITEM = 0x00009000,
STFS_CONTENT_CACHE_FILE = 0x00040000, STFS_CONTENT_CACHE_FILE = 0x00040000,
STFS_CONTENT_COMMUNITY_GAME = 0x02000000, STFS_CONTENT_COMMUNITY_GAME = 0x02000000,
STFS_CONTENT_GAME_DEMO = 0x00080000, STFS_CONTENT_GAME_DEMO = 0x00080000,
STFS_CONTENT_GAMER_PICTURE = 0x00020000, STFS_CONTENT_GAMER_PICTURE = 0x00020000,
STFS_CONTENT_GAME_TITLE = 0x000A0000, STFS_CONTENT_GAME_TITLE = 0x000A0000,
STFS_CONTENT_GAME_TRAILER = 0x000C0000, STFS_CONTENT_GAME_TRAILER = 0x000C0000,
STFS_CONTENT_GAME_VIDEO = 0x00400000, STFS_CONTENT_GAME_VIDEO = 0x00400000,
STFS_CONTENT_INSTALLED_GAME = 0x00004000, STFS_CONTENT_INSTALLED_GAME = 0x00004000,
STFS_CONTENT_INSTALLER = 0x000B0000, STFS_CONTENT_INSTALLER = 0x000B0000,
STFS_CONTENT_IPTV_PAUSE_BUFFER = 0x00002000, STFS_CONTENT_IPTV_PAUSE_BUFFER = 0x00002000,
STFS_CONTENT_LICENSE_STORE = 0x000F0000, STFS_CONTENT_LICENSE_STORE = 0x000F0000,
STFS_CONTENT_MARKETPLACE_CONTENT = 0x00000002, STFS_CONTENT_MARKETPLACE_CONTENT = 0x00000002,
STFS_CONTENT_MOVIE = 0x00100000, STFS_CONTENT_MOVIE = 0x00100000,
STFS_CONTENT_MUSIC_VIDEO = 0x00300000, STFS_CONTENT_MUSIC_VIDEO = 0x00300000,
STFS_CONTENT_PODCAST_VIDEO = 0x00500000, STFS_CONTENT_PODCAST_VIDEO = 0x00500000,
STFS_CONTENT_PROFILE = 0x00010000, STFS_CONTENT_PROFILE = 0x00010000,
STFS_CONTENT_PUBLISHER = 0x00000003, STFS_CONTENT_PUBLISHER = 0x00000003,
STFS_CONTENT_SAVED_GAME = 0x00000001, STFS_CONTENT_SAVED_GAME = 0x00000001,
STFS_CONTENT_STORAGE_DOWNLOAD = 0x00050000, STFS_CONTENT_STORAGE_DOWNLOAD = 0x00050000,
STFS_CONTENT_THEME = 0x00030000, STFS_CONTENT_THEME = 0x00030000,
STFS_CONTENT_TV = 0x00200000, STFS_CONTENT_TV = 0x00200000,
STFS_CONTENT_VIDEO = 0x00090000, STFS_CONTENT_VIDEO = 0x00090000,
STFS_CONTENT_VIRAL_VIDEO = 0x00600000, STFS_CONTENT_VIRAL_VIDEO = 0x00600000,
STFS_CONTENT_XBOX_DOWNLOAD = 0x00070000, STFS_CONTENT_XBOX_DOWNLOAD = 0x00070000,
STFS_CONTENT_XBOX_ORIGINAL_GAME = 0x00005000, STFS_CONTENT_XBOX_ORIGINAL_GAME = 0x00005000,
STFS_CONTENT_XBOX_SAVED_GAME = 0x00060000, STFS_CONTENT_XBOX_SAVED_GAME = 0x00060000,
STFS_CONTENT_XBOX_360_TITLE = 0x00001000, STFS_CONTENT_XBOX_360_TITLE = 0x00001000,
STFS_CONTENT_XBOX_TITLE = 0x00005000, STFS_CONTENT_XBOX_TITLE = 0x00005000,
STFS_CONTENT_XNA = 0x000E0000, STFS_CONTENT_XNA = 0x000E0000,
}; };
enum STFSPlatform : uint8_t { enum STFSPlatform : uint8_t {
STFS_PLATFORM_XBOX_360 = 0x02, STFS_PLATFORM_XBOX_360 = 0x02,
STFS_PLATFORM_PC = 0x04, STFS_PLATFORM_PC = 0x04,
}; };
enum STFSDescriptorType : uint32_t { enum STFSDescriptorType : uint32_t {
STFS_DESCRIPTOR_STFS = 0, STFS_DESCRIPTOR_STFS = 0,
STFS_DESCRIPTOR_SVOD = 1, STFS_DESCRIPTOR_SVOD = 1,
}; };
class STFSVolumeDescriptor { class STFSVolumeDescriptor {
public: public:
bool Read(const uint8_t* p); bool Read(const uint8_t* p);
uint8_t descriptor_size; uint8_t descriptor_size;
uint8_t reserved; uint8_t reserved;
uint8_t block_separation; uint8_t block_separation;
uint16_t file_table_block_count; uint16_t file_table_block_count;
uint32_t file_table_block_number; uint32_t file_table_block_number;
uint8_t top_hash_table_hash[0x14]; uint8_t top_hash_table_hash[0x14];
uint32_t total_allocated_block_count; uint32_t total_allocated_block_count;
uint32_t total_unallocated_block_count; uint32_t total_unallocated_block_count;
}; };
class STFSHeader { class STFSHeader {
public: public:
bool Read(const uint8_t* p); bool Read(const uint8_t* p);
uint8_t license_entries[0x100]; uint8_t license_entries[0x100];
uint8_t header_hash[0x14]; uint8_t header_hash[0x14];
uint32_t header_size; uint32_t header_size;
STFSContentType content_type; STFSContentType content_type;
uint32_t metadata_version; uint32_t metadata_version;
uint64_t content_size; uint64_t content_size;
uint32_t media_id; uint32_t media_id;
uint32_t version; uint32_t version;
uint32_t base_version; uint32_t base_version;
uint32_t title_id; uint32_t title_id;
STFSPlatform platform; STFSPlatform platform;
uint8_t executable_type; uint8_t executable_type;
uint8_t disc_number; uint8_t disc_number;
uint8_t disc_in_set; uint8_t disc_in_set;
uint32_t save_game_id; uint32_t save_game_id;
uint8_t console_id[0x5]; uint8_t console_id[0x5];
uint8_t profile_id[0x8]; uint8_t profile_id[0x8];
STFSVolumeDescriptor volume_descriptor; STFSVolumeDescriptor volume_descriptor;
uint32_t data_file_count; uint32_t data_file_count;
uint64_t data_file_combined_size; uint64_t data_file_combined_size;
STFSDescriptorType descriptor_type; STFSDescriptorType descriptor_type;
uint8_t device_id[0x14]; uint8_t device_id[0x14];
wchar_t display_names[0x900 / 2]; wchar_t display_names[0x900 / 2];
wchar_t display_descs[0x900 / 2]; wchar_t display_descs[0x900 / 2];
wchar_t publisher_name[0x80 / 2]; wchar_t publisher_name[0x80 / 2];
wchar_t title_name[0x80 / 2]; wchar_t title_name[0x80 / 2];
uint8_t transfer_flags; uint8_t transfer_flags;
uint32_t thumbnail_image_size; uint32_t thumbnail_image_size;
uint32_t title_thumbnail_image_size; uint32_t title_thumbnail_image_size;
uint8_t thumbnail_image[0x4000]; uint8_t thumbnail_image[0x4000];
uint8_t title_thumbnail_image[0x4000]; uint8_t title_thumbnail_image[0x4000];
}; };
class STFSEntry { class STFSEntry {
public: public:
STFSEntry(); STFSEntry();
~STFSEntry();
STFSEntry* GetChild(const char* name); STFSEntry* GetChild(const char* name);
void Dump(int indent); void Dump(int indent);
std::string name; std::string name;
X_FILE_ATTRIBUTES attributes; X_FILE_ATTRIBUTES attributes;
size_t offset; size_t offset;
size_t size; size_t size;
uint32_t update_timestamp; uint32_t update_timestamp;
uint32_t access_timestamp; uint32_t access_timestamp;
std::vector<STFSEntry*> children; std::vector<std::unique_ptr<STFSEntry>> children;
typedef struct { typedef struct {
size_t offset; size_t offset;
@ -158,27 +150,26 @@ public:
std::vector<BlockRecord_t> block_list; std::vector<BlockRecord_t> block_list;
}; };
class STFS { class STFS {
public: public:
enum Error { enum Error {
kSuccess = 0, kSuccess = 0,
kErrorOutOfMemory = -1, kErrorOutOfMemory = -1,
kErrorReadError = -10, kErrorReadError = -10,
kErrorFileMismatch = -30, kErrorFileMismatch = -30,
kErrorDamagedFile = -31, kErrorDamagedFile = -31,
}; };
STFS(xe_mmap_ref mmap); STFS(xe_mmap_ref mmap);
virtual ~STFS(); virtual ~STFS();
const STFSHeader* header() const { return &header_; } const STFSHeader* header() const { return &header_; }
STFSEntry* root_entry(); STFSEntry* root_entry() const { return root_entry_.get(); }
Error Load(); Error Load();
void Dump(); void Dump();
private: private:
Error ReadHeaderAndVerify(const uint8_t* map_ptr); Error ReadHeaderAndVerify(const uint8_t* map_ptr);
Error ReadAllEntries(const uint8_t* map_ptr); Error ReadAllEntries(const uint8_t* map_ptr);
size_t BlockToOffset(uint32_t block); size_t BlockToOffset(uint32_t block);
@ -188,21 +179,19 @@ private:
uint32_t next_block_index; uint32_t next_block_index;
uint32_t info; uint32_t info;
} BlockHash_t; } BlockHash_t;
BlockHash_t GetBlockHash(const uint8_t* map_ptr, BlockHash_t GetBlockHash(const uint8_t* map_ptr, uint32_t block_index,
uint32_t block_index, uint32_t table_offset); uint32_t table_offset);
xe_mmap_ref mmap_; xe_mmap_ref mmap_;
STFSPackageType package_type_; STFSPackageType package_type_;
STFSHeader header_; STFSHeader header_;
uint32_t table_size_shift_; uint32_t table_size_shift_;
STFSEntry* root_entry_; std::unique_ptr<STFSEntry> root_entry_;
}; };
} // namespace fs } // namespace fs
} // namespace kernel } // namespace kernel
} // namespace xe } // namespace xe
#endif // XENIA_KERNEL_FS_STFS_H_ #endif // XENIA_KERNEL_FS_STFS_H_