diff --git a/src/xenia/kernel/user_module.cc b/src/xenia/kernel/user_module.cc index 58ab9f36c..1851250ad 100644 --- a/src/xenia/kernel/user_module.cc +++ b/src/xenia/kernel/user_module.cc @@ -19,6 +19,7 @@ #include "xenia/emulator.h" #include "xenia/kernel/xfile.h" #include "xenia/kernel/xthread.h" +#include "xenia/vfs/devices/stfs_container_device.h" DEFINE_bool(xex_apply_patches, true, "Apply XEX patches.", "Kernel"); @@ -51,9 +52,11 @@ uint32_t UserModule::title_id() const { X_STATUS UserModule::LoadFromFile(std::string path) { X_STATUS result = X_STATUS_UNSUCCESSFUL; + auto file_system = kernel_state()->file_system(); + // Resolve the file to open. // TODO(benvanik): make this code shared? - auto fs_entry = kernel_state()->file_system()->ResolvePath(path); + auto fs_entry = file_system->ResolvePath(path); if (!fs_entry) { XELOGE("File not found: %s", path.c_str()); return X_STATUS_NO_SUCH_FILE; @@ -105,26 +108,66 @@ X_STATUS UserModule::LoadFromFile(std::string path) { if (cvars::xex_apply_patches) { // Search for xexp patch file auto patch_entry = kernel_state()->file_system()->ResolvePath(path_ + "p"); + } + + auto module_path = fs_entry->path(); - if (patch_entry) { - auto patch_path = patch_entry->absolute_path(); + auto content_manager = kernel_state()->content_manager(); - XELOGI("Loading XEX patch from %s", patch_path.c_str()); + if (!file_system->IsSymbolicLink("update:")) { + // update:\\ path isn't symlinked, try searching for an update package - auto patch_module = object_ref(new UserModule(kernel_state_)); - result = patch_module->LoadFromFile(patch_path); - if (!result) { - result = patch_module->xex_module()->ApplyPatch(xex_module()); - if (result) { - XELOGE("Failed to apply XEX patch, code: %d", result); + xex2_opt_execution_info* exec_info = 0; + xex_module()->GetOptHeader(XEX_HEADER_EXECUTION_INFO, &exec_info); + + if (exec_info) { + content_manager->SetTitleIdOverride(exec_info->title_id); + + auto update_packages = content_manager->ListContent( + 0, (uint32_t)vfs::StfsContentType::kInstaller); + + for (auto& update : update_packages) { + auto result = content_manager->OpenContent("update", update); + + if (!file_system->ResolvePath("update:\\" + module_path + "p")) { + // XEXP/DLLP doesn't exist in this package, lets just close it + content_manager->CloseContent("update"); + continue; + } else { + // XEXP/DLLP found, break out of package loop + // TODO: verify XEXP/DLLP works first? + break; } - } else { - XELOGE("Failed to load XEX patch, code: %d", result); } + } + } + // Unset content_manager title ID override + content_manager->SetTitleIdOverride(0); + + // First try checking update:\ root for patch, otherwise try same path as XEX + auto patch_entry = file_system->ResolvePath("update:\\" + module_path + "p"); + if (!patch_entry) { + patch_entry = file_system->ResolvePath(path_ + "p"); + } + if (patch_entry) { + auto patch_path = patch_entry->absolute_path(); + + XELOGI("Loading XEX patch from %s", patch_path.c_str()); + + auto patch_module = object_ref(new UserModule(kernel_state_)); + result = patch_module->LoadFromFile(patch_path); + if (!result) { + result = patch_module->xex_module()->ApplyPatch(xex_module()); if (result) { - return X_STATUS_UNSUCCESSFUL; + XELOGE("Failed to apply XEX patch, code: %d", result); } + } else { + XELOGE("Failed to load XEX patch, code: %d", result); + } + + if (result) { + return X_STATUS_UNSUCCESSFUL; } } diff --git a/src/xenia/kernel/xam/content_manager.cc b/src/xenia/kernel/xam/content_manager.cc index 2ca34f716..c2ee72858 100644 --- a/src/xenia/kernel/xam/content_manager.cc +++ b/src/xenia/kernel/xam/content_manager.cc @@ -14,77 +14,71 @@ #include "xenia/base/filesystem.h" #include "xenia/base/string.h" #include "xenia/kernel/kernel_state.h" +#include "xenia/kernel/user_module.h" +#include "xenia/kernel/xam/content_package.h" #include "xenia/kernel/xobject.h" #include "xenia/vfs/devices/host_path_device.h" +#include "xenia/vfs/devices/stfs_container_device.h" namespace xe { namespace kernel { namespace xam { -static const wchar_t* kThumbnailFileName = L"__thumbnail.png"; +constexpr const wchar_t* const ContentManager::kStfsHeadersExtension; static const wchar_t* kGameUserContentDirName = L"profile"; -static int content_device_id_ = 0; - -ContentPackage::ContentPackage(KernelState* kernel_state, std::string root_name, - const XCONTENT_DATA& data, - std::wstring package_path) - : kernel_state_(kernel_state), root_name_(std::move(root_name)) { - device_path_ = std::string("\\Device\\Content\\") + - std::to_string(++content_device_id_) + "\\"; - - auto fs = kernel_state_->file_system(); - auto device = - std::make_unique(device_path_, package_path, false); - device->Initialize(); - fs->RegisterDevice(std::move(device)); - fs->RegisterSymbolicLink(root_name_ + ":", device_path_); -} - -ContentPackage::~ContentPackage() { - auto fs = kernel_state_->file_system(); - fs->UnregisterSymbolicLink(root_name_ + ":"); - fs->UnregisterDevice(device_path_); -} - ContentManager::ContentManager(KernelState* kernel_state, std::wstring root_path) : kernel_state_(kernel_state), root_path_(std::move(root_path)) {} ContentManager::~ContentManager() = default; -std::wstring ContentManager::ResolvePackageRoot(uint32_t content_type) { - wchar_t title_id[9] = L"00000000"; - std::swprintf(title_id, 9, L"%.8X", kernel_state_->title_id()); - - std::wstring type_name; - switch (content_type) { - case 1: - // Save games. - type_name = L"00000001"; - break; - case 2: - // DLC from the marketplace. - type_name = L"00000002"; - break; - case 3: - // Publisher content? - type_name = L"00000003"; - break; - case 0x000D0000: - // ??? - type_name = L"000D0000"; - break; - default: - assert_unhandled_case(data.content_type); - return nullptr; +uint32_t ContentManager::title_id() { + if (title_id_override_) { + return title_id_override_; } + if (!kernel_state_->GetExecutableModule()) { + return -1; + } + return kernel_state_->title_id(); +} + +std::wstring ContentManager::ResolvePackageRoot(uint32_t content_type) { + wchar_t title_id_str[9] = L"00000000"; + std::swprintf(title_id_str, 9, L"%.8X", title_id()); + + wchar_t content_type_str[9] = L"00000000"; + std::swprintf(content_type_str, 9, L"%.8X", content_type); + + std::wstring type_name; + switch (content_type) { + case 1: + // Save games. + type_name = L"00000001"; + break; + case 2: + // DLC from the marketplace. + type_name = L"00000002"; + break; + case 3: + // Publisher content? + type_name = L"00000003"; + break; + case 0x000D0000: + // ??? + type_name = L"000D0000"; + break; + default: + type_name = L"00000000"; + //assert_unhandled_case(data.content_type); + //return nullptr; + } // Package root path: // content_root/title_id/type_name/ - auto package_root = - xe::join_paths(root_path_, xe::join_paths(title_id, type_name)); + auto package_root = xe::join_paths( + root_path_, xe::join_paths(title_id_str, content_type_str)); return package_root + xe::kWPathSeparator; } @@ -94,7 +88,13 @@ std::wstring ContentManager::ResolvePackagePath(const XCONTENT_DATA& data) { auto package_root = ResolvePackageRoot(data.content_type); auto package_path = xe::join_paths(package_root, xe::to_wstring(data.file_name)); - package_path += xe::kPathSeparator; + + // Add slash to end of path if this is a folder + // (or package doesn't exist, meaning we're creating a new folder) + if (!xe::filesystem::PathExists(package_path) || + xe::filesystem::IsFolder(package_path)) { + package_path += xe::kPathSeparator; + } return package_path; } @@ -102,28 +102,58 @@ std::vector ContentManager::ListContent(uint32_t device_id, uint32_t content_type) { std::vector result; + // StfsHeader is a huge class - alloc on heap instead of stack + vfs::StfsHeader* header = new vfs::StfsHeader(); + // Search path: // content_root/title_id/type_name/* auto package_root = ResolvePackageRoot(content_type); auto file_infos = xe::filesystem::ListFiles(package_root); for (const auto& file_info : file_infos) { - if (file_info.type != xe::filesystem::FileInfo::Type::kDirectory) { - // Directories only. - continue; - } XCONTENT_DATA content_data; content_data.device_id = device_id; content_data.content_type = content_type; content_data.display_name = file_info.name; content_data.file_name = xe::to_string(file_info.name); + + auto headers_path = file_info.path + file_info.name; + if (file_info.type == xe::filesystem::FileInfo::Type::kDirectory) { + headers_path = headers_path + ContentManager::kStfsHeadersExtension; + } + + if (xe::filesystem::PathExists(headers_path)) { + // File is either package or directory that has .headers file + + if (file_info.type != xe::filesystem::FileInfo::Type::kDirectory) { + // Not a directory so must be a package, verify size to make sure + if (file_info.total_size <= vfs::StfsHeader::kHeaderLength) { + continue; // Invalid package (maybe .headers.bin) + } + } + + auto map = MappedMemory::Open(headers_path, MappedMemory::Mode::kRead, 0, + vfs::StfsHeader::kHeaderLength); + if (map) { + if (header->Read(map->data())) { + content_data.content_type = + static_cast(header->content_type); + content_data.display_name = header->display_names; + // TODO: select localized display name + // some games may expect different ones depending on language setting. + } + map->Close(); + } + } + result.emplace_back(std::move(content_data)); } + delete header; + return result; } -std::unique_ptr ContentManager::ResolvePackage( - std::string root_name, const XCONTENT_DATA& data) { +ContentPackage* ContentManager::ResolvePackage(const XCONTENT_DATA& data) { auto package_path = ResolvePackagePath(data); if (!xe::filesystem::PathExists(package_path)) { return nullptr; @@ -131,9 +161,25 @@ std::unique_ptr ContentManager::ResolvePackage( auto global_lock = global_critical_region_.Acquire(); - auto package = std::make_unique(kernel_state_, root_name, - data, package_path); - return package; + for (auto package : open_packages_) { + if (package->package_path() == package_path) { + return package; + } + } + + std::unique_ptr package; + + // Open as FolderContentPackage if the package is a folder or doesn't exist + if (xe::filesystem::IsFolder(package_path) || + !xe::filesystem::PathExists(package_path)) { + package = std::make_unique(kernel_state_, data, + package_path); + } else { + package = + std::make_unique(kernel_state_, data, package_path); + } + + return package.release(); } bool ContentManager::ContentExists(const XCONTENT_DATA& data) { @@ -145,25 +191,32 @@ X_RESULT ContentManager::CreateContent(std::string root_name, const XCONTENT_DATA& data) { auto global_lock = global_critical_region_.Acquire(); - if (open_packages_.count(root_name)) { - // Already content open with this root name. - return X_ERROR_ALREADY_EXISTS; - } - auto package_path = ResolvePackagePath(data); if (xe::filesystem::PathExists(package_path)) { // Exists, must not! return X_ERROR_ALREADY_EXISTS; } + for (auto package : open_packages_) { + if (package->package_path() == package_path) { + return X_ERROR_ALREADY_EXISTS; + } + } + if (!xe::filesystem::CreateFolder(package_path)) { return X_ERROR_ACCESS_DENIED; } - auto package = ResolvePackage(root_name, data); - assert_not_null(package); + auto package = ResolvePackage(data); + if (!package) { + return X_ERROR_FUNCTION_FAILED; // Failed to create directory? + } - open_packages_.insert({root_name, package.release()}); + if (!package->Mount(root_name)) { + return X_ERROR_DEVICE_NOT_CONNECTED; + } + + open_packages_.push_back(package); return X_ERROR_SUCCESS; } @@ -172,22 +225,16 @@ X_RESULT ContentManager::OpenContent(std::string root_name, const XCONTENT_DATA& data) { auto global_lock = global_critical_region_.Acquire(); - if (open_packages_.count(root_name)) { - // Already content open with this root name. - return X_ERROR_ALREADY_EXISTS; - } - - auto package_path = ResolvePackagePath(data); - if (!xe::filesystem::PathExists(package_path)) { - // Does not exist, must be created. + auto package = ResolvePackage(data); + if (!package) { return X_ERROR_FILE_NOT_FOUND; } - // Open package. - auto package = ResolvePackage(root_name, data); - assert_not_null(package); + if (!package->Mount(root_name)) { + return X_ERROR_DEVICE_NOT_CONNECTED; + } - open_packages_.insert({root_name, package.release()}); + open_packages_.push_back(package); return X_ERROR_SUCCESS; } @@ -195,63 +242,65 @@ X_RESULT ContentManager::OpenContent(std::string root_name, X_RESULT ContentManager::CloseContent(std::string root_name) { auto global_lock = global_critical_region_.Acquire(); - auto it = open_packages_.find(root_name); - if (it == open_packages_.end()) { - return X_ERROR_FILE_NOT_FOUND; + for (auto it = open_packages_.begin(); it != open_packages_.end(); ++it) { + auto& root_names = (*it)->root_names(); + auto root = std::find(root_names.begin(), root_names.end(), root_name); + if (root != root_names.end()) { + if ((*it)->Unmount(root_name)) { + delete *it; + open_packages_.erase(it); + } + + return X_ERROR_SUCCESS; + } } - auto package = it->second; - open_packages_.erase(it); - delete package; - - return X_ERROR_SUCCESS; + return X_ERROR_FILE_NOT_FOUND; } X_RESULT ContentManager::GetContentThumbnail(const XCONTENT_DATA& data, std::vector* buffer) { auto global_lock = global_critical_region_.Acquire(); - auto package_path = ResolvePackagePath(data); - auto thumb_path = xe::join_paths(package_path, kThumbnailFileName); - if (xe::filesystem::PathExists(thumb_path)) { - auto file = xe::filesystem::OpenFile(thumb_path, "rb"); - fseek(file, 0, SEEK_END); - size_t file_len = ftell(file); - fseek(file, 0, SEEK_SET); - buffer->resize(file_len); - fread(const_cast(buffer->data()), 1, buffer->size(), file); - fclose(file); - return X_ERROR_SUCCESS; - } else { + + auto package = ResolvePackage(data); + if (!package) { return X_ERROR_FILE_NOT_FOUND; } + + return package->GetThumbnail(buffer); } X_RESULT ContentManager::SetContentThumbnail(const XCONTENT_DATA& data, std::vector buffer) { auto global_lock = global_critical_region_.Acquire(); - auto package_path = ResolvePackagePath(data); - xe::filesystem::CreateFolder(package_path); - if (xe::filesystem::PathExists(package_path)) { - auto thumb_path = xe::join_paths(package_path, kThumbnailFileName); - auto file = xe::filesystem::OpenFile(thumb_path, "wb"); - fwrite(buffer.data(), 1, buffer.size(), file); - fclose(file); - return X_ERROR_SUCCESS; - } else { + + auto package = ResolvePackage(data); + if (!package) { return X_ERROR_FILE_NOT_FOUND; } + + return package->SetThumbnail(buffer); } X_RESULT ContentManager::DeleteContent(const XCONTENT_DATA& data) { auto global_lock = global_critical_region_.Acquire(); - auto package_path = ResolvePackagePath(data); - if (xe::filesystem::PathExists(package_path)) { - xe::filesystem::DeleteFolder(package_path); - return X_ERROR_SUCCESS; - } else { + auto package = ResolvePackage(data); + if (!package) { return X_ERROR_FILE_NOT_FOUND; } + + auto result = package->Delete(); + if (XSUCCEEDED(result)) { + auto it = std::find(open_packages_.begin(), open_packages_.end(), package); + if (it != open_packages_.end()) { + open_packages_.erase(it); + } + + delete package; + } + + return result; } std::wstring ContentManager::ResolveGameUserContentPath() { diff --git a/src/xenia/kernel/xam/content_manager.h b/src/xenia/kernel/xam/content_manager.h index 179939b80..1099314e2 100644 --- a/src/xenia/kernel/xam/content_manager.h +++ b/src/xenia/kernel/xam/content_manager.h @@ -17,6 +17,7 @@ #include "xenia/base/memory.h" #include "xenia/base/mutex.h" +#include "xenia/kernel/xam/content_package.h" #include "xenia/xbox.h" namespace xe { @@ -52,28 +53,18 @@ struct XCONTENT_DATA { } }; -class ContentPackage { - public: - ContentPackage(KernelState* kernel_state, std::string root_name, - const XCONTENT_DATA& data, std::wstring package_path); - ~ContentPackage(); - - private: - KernelState* kernel_state_; - std::string root_name_; - std::string device_path_; -}; - class ContentManager { public: + // Extension to append to folder path when searching for STFS headers + static constexpr const wchar_t* const kStfsHeadersExtension = L".headers.bin"; + ContentManager(KernelState* kernel_state, std::wstring root_path); ~ContentManager(); std::vector ListContent(uint32_t device_id, uint32_t content_type); - std::unique_ptr ResolvePackage(std::string root_name, - const XCONTENT_DATA& data); + ContentPackage* ResolvePackage(const XCONTENT_DATA& data); bool ContentExists(const XCONTENT_DATA& data); X_RESULT CreateContent(std::string root_name, const XCONTENT_DATA& data); @@ -86,7 +77,11 @@ class ContentManager { X_RESULT DeleteContent(const XCONTENT_DATA& data); std::wstring ResolveGameUserContentPath(); + void SetTitleIdOverride(uint32_t title_id) { title_id_override_ = title_id; } + private: + uint32_t title_id(); + std::wstring ResolvePackageRoot(uint32_t content_type); std::wstring ResolvePackagePath(const XCONTENT_DATA& data); @@ -95,7 +90,10 @@ class ContentManager { // TODO(benvanik): remove use of global lock, it's bad here! xe::global_critical_region global_critical_region_; - std::unordered_map open_packages_; + std::vector open_packages_; + + uint32_t title_id_override_ = + 0; // can be used for games/apps that request content for other IDs }; } // namespace xam diff --git a/src/xenia/kernel/xam/content_package.cc b/src/xenia/kernel/xam/content_package.cc new file mode 100644 index 000000000..3fa04efd9 --- /dev/null +++ b/src/xenia/kernel/xam/content_package.cc @@ -0,0 +1,200 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2015 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/kernel/xam/content_package.h" + +#include + +#include "xenia/base/filesystem.h" +#include "xenia/base/string.h" +#include "xenia/kernel/kernel_state.h" +#include "xenia/kernel/xobject.h" +#include "xenia/vfs/devices/host_path_device.h" +#include "xenia/vfs/devices/stfs_container_device.h" + +namespace xe { +namespace kernel { +namespace xam { + +static const wchar_t* kThumbnailFileName = L"__thumbnail.png"; + +static int content_device_id_ = 0; + +ContentPackage::ContentPackage(KernelState* kernel_state, + const XCONTENT_DATA& data, + std::wstring package_path) + : kernel_state_(kernel_state), package_path_(std::move(package_path)) { + device_path_ = std::string("\\Device\\Content\\") + + std::to_string(++content_device_id_) + "\\"; +} + +ContentPackage::~ContentPackage() { Unmount(); } + +bool ContentPackage::Mount(std::string root_name, + std::unique_ptr device) { + auto fs = kernel_state_->file_system(); + + if (device) { + if (!fs->RegisterDevice(std::move(device))) { + return false; + } + } + + if (!fs->RegisterSymbolicLink(root_name + ":", device_path_)) { + return false; + } + + root_names_.push_back(root_name); + + return true; +} + +bool ContentPackage::Unmount() { + auto fs = kernel_state_->file_system(); + for (auto root_name : root_names_) { + fs->UnregisterSymbolicLink(root_name + ":"); + } + root_names_.clear(); + fs->UnregisterDevice(device_path_); + device_inited_ = false; + + return true; +} + +bool ContentPackage::Unmount(std::string root_name) { + auto itr = std::find(root_names_.begin(), root_names_.end(), root_name); + if (itr != root_names_.end()) { + root_names_.erase(itr); + kernel_state_->file_system()->UnregisterSymbolicLink(root_name + ":"); + } + + // Do a complete device unmount if no root names are left + if (!root_names_.size()) { + return Unmount(); + } + return false; // still mounted under a different root_name, so return false +} + +bool FolderContentPackage::Mount(std::string root_name) { + std::unique_ptr device = nullptr; + + if (!device_inited_) { + device = std::make_unique(device_path_, package_path_, + false); + if (!device->Initialize()) { + return false; + } + device_inited_ = true; + } + + return ContentPackage::Mount(root_name, std::move(device)); +} + +X_RESULT FolderContentPackage::GetThumbnail(std::vector* buffer) { + // Grab thumb from kThumbnailFileName if it exists, otherwise try STFS headers + auto thumb_path = xe::join_paths(package_path_, kThumbnailFileName); + if (xe::filesystem::PathExists(thumb_path)) { + auto file = xe::filesystem::OpenFile(thumb_path, "rb"); + fseek(file, 0, SEEK_END); + size_t file_len = ftell(file); + fseek(file, 0, SEEK_SET); + buffer->resize(file_len); + fread(const_cast(buffer->data()), 1, buffer->size(), file); + fclose(file); + return X_ERROR_SUCCESS; + } + auto result = X_ERROR_FILE_NOT_FOUND; + + // Try reading thumbnail from kStfsHeadersExtension file + auto headers_path = package_path_ + ContentManager::kStfsHeadersExtension; + if (xe::filesystem::PathExists(headers_path)) { + vfs::StfsHeader* header = + new vfs::StfsHeader(); // huge class, alloc on heap + auto map = MappedMemory::Open(headers_path, MappedMemory::Mode::kRead, 0, + vfs::StfsHeader::kHeaderLength); + if (map) { + if (header->Read(map->data())) { + buffer->resize(header->thumbnail_image_size); + memcpy(buffer->data(), header->thumbnail_image, + header->thumbnail_image_size); + result = X_ERROR_SUCCESS; + } + } + delete header; + } + return result; +} + +X_RESULT FolderContentPackage::SetThumbnail(std::vector buffer) { + xe::filesystem::CreateFolder(package_path_); + if (xe::filesystem::PathExists(package_path_)) { + auto thumb_path = xe::join_paths(package_path_, kThumbnailFileName); + auto file = xe::filesystem::OpenFile(thumb_path, "wb"); + fwrite(buffer.data(), 1, buffer.size(), file); + fclose(file); + return X_ERROR_SUCCESS; + } + return X_ERROR_FILE_NOT_FOUND; +} + +X_RESULT FolderContentPackage::Delete() { + // Make sure package isn't in use + Unmount(); + + if (xe::filesystem::PathExists(package_path_)) { + xe::filesystem::DeleteFolder(package_path_); + return X_ERROR_SUCCESS; + } + return X_ERROR_FILE_NOT_FOUND; +} + +bool StfsContentPackage::Mount(std::string root_name) { + std::unique_ptr device = nullptr; + + if (!device_inited_) { + device = + std::make_unique(device_path_, package_path_); + if (!device->Initialize()) { + return false; + } + + header_ = device->header(); + device_inited_ = true; + } + + return ContentPackage::Mount(root_name, std::move(device)); +} + +X_RESULT StfsContentPackage::GetThumbnail(std::vector* buffer) { + if (!device_inited_) { + return X_ERROR_DEVICE_NOT_CONNECTED; + } + buffer->resize(header_.thumbnail_image_size); + memcpy(buffer->data(), header_.thumbnail_image, header_.thumbnail_image_size); + return X_ERROR_SUCCESS; +} + +X_RESULT StfsContentPackage::SetThumbnail(std::vector buffer) { + return X_ERROR_FUNCTION_FAILED; // can't write to STFS headers right now +} + +X_RESULT StfsContentPackage::Delete() { + // Make sure package isn't in use + Unmount(); + + if (xe::filesystem::PathExists(package_path_)) { + return xe::filesystem::DeleteFile(package_path_) ? X_ERROR_SUCCESS + : X_ERROR_FUNCTION_FAILED; + } + return X_ERROR_FILE_NOT_FOUND; +} + +} // namespace xam +} // namespace kernel +} // namespace xe diff --git a/src/xenia/kernel/xam/content_package.h b/src/xenia/kernel/xam/content_package.h new file mode 100644 index 000000000..92fa10b4d --- /dev/null +++ b/src/xenia/kernel/xam/content_package.h @@ -0,0 +1,91 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2015 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_KERNEL_XAM_CONTENT_PACKAGE_H_ +#define XENIA_KERNEL_XAM_CONTENT_PACKAGE_H_ + +#include +#include +#include +#include + +#include "xenia/base/memory.h" +#include "xenia/base/mutex.h" +#include "xenia/vfs/devices/stfs_container_device.h" +#include "xenia/xbox.h" + +namespace xe { +namespace kernel { +namespace xam { + +struct XCONTENT_DATA; + +class ContentPackage { + public: + ContentPackage(KernelState* kernel_state, const XCONTENT_DATA& data, + std::wstring package_path); + virtual ~ContentPackage(); + + virtual bool Mount(std::string root_name) = 0; + virtual X_RESULT GetThumbnail(std::vector* buffer) = 0; + virtual X_RESULT SetThumbnail(std::vector buffer) = 0; + virtual X_RESULT Delete() = 0; + + bool Unmount(); // Unmounts device & all root names - returns true if device + // is unmounted + bool Unmount( + std::string root_name); // Unmounts a single root name, if no root names + // are left mounted the device will be unmounted + // - returns true if device is unmounted + + std::wstring package_path() { return package_path_; } + const std::vector& root_names() { return root_names_; } + + protected: + bool Mount(std::string root_name, std::unique_ptr device); + + bool device_inited_ = false; + KernelState* kernel_state_; + std::string device_path_; + std::wstring package_path_; + std::vector root_names_; +}; + +class StfsContentPackage : public ContentPackage { + public: + StfsContentPackage(KernelState* kernel_state, const XCONTENT_DATA& data, + std::wstring package_path) + : ContentPackage(kernel_state, data, package_path) {} + + bool Mount(std::string root_name); + X_RESULT GetThumbnail(std::vector* buffer); + X_RESULT SetThumbnail(std::vector buffer); + X_RESULT Delete(); + + private: + vfs::StfsHeader header_; +}; + +class FolderContentPackage : public ContentPackage { + public: + FolderContentPackage(KernelState* kernel_state, const XCONTENT_DATA& data, + std::wstring package_path) + : ContentPackage(kernel_state, data, package_path) {} + + bool Mount(std::string root_name); + X_RESULT GetThumbnail(std::vector* buffer); + X_RESULT SetThumbnail(std::vector buffer); + X_RESULT Delete(); +}; + +} // namespace xam +} // namespace kernel +} // namespace xe + +#endif // XENIA_KERNEL_XAM_CONTENT_PACKAGE_H_ diff --git a/src/xenia/vfs/devices/stfs_container_device.h b/src/xenia/vfs/devices/stfs_container_device.h index 624393e01..89e76c231 100644 --- a/src/xenia/vfs/devices/stfs_container_device.h +++ b/src/xenia/vfs/devices/stfs_container_device.h @@ -117,6 +117,8 @@ struct SvodVolumeDescriptor { class StfsHeader { public: + static const uint32_t kHeaderLength = 0xA000; + bool Read(const uint8_t* p); uint8_t license_entries[0x100]; @@ -181,6 +183,8 @@ class StfsContainerDevice : public Device { uint32_t sectors_per_allocation_unit() const override { return 1; } uint32_t bytes_per_sector() const override { return 4 * 1024; } + StfsHeader& header() { return header_; } + private: enum class Error { kSuccess = 0, diff --git a/src/xenia/vfs/virtual_file_system.cc b/src/xenia/vfs/virtual_file_system.cc index 15f37702b..d6b3a250a 100644 --- a/src/xenia/vfs/virtual_file_system.cc +++ b/src/xenia/vfs/virtual_file_system.cc @@ -25,6 +25,26 @@ VirtualFileSystem::~VirtualFileSystem() { devices_.clear(); symlinks_.clear(); } +Entry* VirtualFileSystem::ResolveDevice(const std::string& devicepath) { + auto global_lock = global_critical_region_.Acquire(); + + // Resolve relative paths + std::string normalized_path(xe::filesystem::CanonicalizePath(devicepath)); + + // Find the device. + auto it = + std::find_if(devices_.cbegin(), devices_.cend(), [&](const auto& d) { + return xe::find_first_of_case(normalized_path, d->mount_path()) == 0; + }); + if (it == devices_.cend()) { + XELOGE("ResolveDevice(%s) device not initialized", devicepath.c_str()); + return nullptr; + } + + const auto& device = *it; + auto relative_path = normalized_path.substr(device->mount_path().size()); + return device->ResolvePath(relative_path); +} bool VirtualFileSystem::RegisterDevice(std::unique_ptr device) { auto global_lock = global_critical_region_.Acquire(); @@ -65,7 +85,6 @@ bool VirtualFileSystem::UnregisterSymbolicLink(const std::string& path) { symlinks_.erase(it); return true; } - bool VirtualFileSystem::IsSymbolicLink(const std::string& path) { auto global_lock = global_critical_region_.Acquire(); auto it = symlinks_.find(path); diff --git a/src/xenia/vfs/virtual_file_system.h b/src/xenia/vfs/virtual_file_system.h index 2d84525ac..6c20a3d08 100644 --- a/src/xenia/vfs/virtual_file_system.h +++ b/src/xenia/vfs/virtual_file_system.h @@ -27,13 +27,14 @@ class VirtualFileSystem { public: VirtualFileSystem(); ~VirtualFileSystem(); + Entry* ResolveDevice(const std::string& path); bool RegisterDevice(std::unique_ptr device); bool UnregisterDevice(const std::string& path); bool RegisterSymbolicLink(const std::string& path, const std::string& target); bool UnregisterSymbolicLink(const std::string& path); - bool VirtualFileSystem::IsSymbolicLink(const std::string& path); + bool IsSymbolicLink(const std::string& path); bool FindSymbolicLink(const std::string& path, std::string& target); Entry* ResolvePath(const std::string& path);