Merge remote-tracking branch 'emoose/title-updates'
This commit is contained in:
commit
3edc563bac
|
@ -19,6 +19,7 @@
|
||||||
#include "xenia/emulator.h"
|
#include "xenia/emulator.h"
|
||||||
#include "xenia/kernel/xfile.h"
|
#include "xenia/kernel/xfile.h"
|
||||||
#include "xenia/kernel/xthread.h"
|
#include "xenia/kernel/xthread.h"
|
||||||
|
#include "xenia/vfs/devices/stfs_container_device.h"
|
||||||
|
|
||||||
DEFINE_bool(xex_apply_patches, true, "Apply XEX patches.", "Kernel");
|
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 UserModule::LoadFromFile(std::string path) {
|
||||||
X_STATUS result = X_STATUS_UNSUCCESSFUL;
|
X_STATUS result = X_STATUS_UNSUCCESSFUL;
|
||||||
|
|
||||||
|
auto file_system = kernel_state()->file_system();
|
||||||
|
|
||||||
// Resolve the file to open.
|
// Resolve the file to open.
|
||||||
// TODO(benvanik): make this code shared?
|
// 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) {
|
if (!fs_entry) {
|
||||||
XELOGE("File not found: %s", path.c_str());
|
XELOGE("File not found: %s", path.c_str());
|
||||||
return X_STATUS_NO_SUCH_FILE;
|
return X_STATUS_NO_SUCH_FILE;
|
||||||
|
@ -105,7 +108,48 @@ X_STATUS UserModule::LoadFromFile(std::string path) {
|
||||||
if (cvars::xex_apply_patches) {
|
if (cvars::xex_apply_patches) {
|
||||||
// Search for xexp patch file
|
// Search for xexp patch file
|
||||||
auto patch_entry = kernel_state()->file_system()->ResolvePath(path_ + "p");
|
auto patch_entry = kernel_state()->file_system()->ResolvePath(path_ + "p");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto module_path = fs_entry->path();
|
||||||
|
|
||||||
|
auto content_manager = kernel_state()->content_manager();
|
||||||
|
|
||||||
|
if (!file_system->IsSymbolicLink("update:")) {
|
||||||
|
// update:\\ path isn't symlinked, try searching for an update package
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
if (patch_entry) {
|
||||||
auto patch_path = patch_entry->absolute_path();
|
auto patch_path = patch_entry->absolute_path();
|
||||||
|
|
||||||
|
@ -126,7 +170,6 @@ X_STATUS UserModule::LoadFromFile(std::string path) {
|
||||||
return X_STATUS_UNSUCCESSFUL;
|
return X_STATUS_UNSUCCESSFUL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return LoadXexContinue();
|
return LoadXexContinue();
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,8 +14,10 @@
|
||||||
#include "xenia/base/filesystem.h"
|
#include "xenia/base/filesystem.h"
|
||||||
#include "xenia/base/string.h"
|
#include "xenia/base/string.h"
|
||||||
#include "xenia/kernel/kernel_state.h"
|
#include "xenia/kernel/kernel_state.h"
|
||||||
|
#include "xenia/kernel/user_module.h"
|
||||||
#include "xenia/kernel/xobject.h"
|
#include "xenia/kernel/xobject.h"
|
||||||
#include "xenia/vfs/devices/host_path_device.h"
|
#include "xenia/vfs/devices/host_path_device.h"
|
||||||
|
#include "xenia/vfs/devices/stfs_container_device.h"
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace kernel {
|
namespace kernel {
|
||||||
|
@ -35,8 +37,20 @@ ContentPackage::ContentPackage(KernelState* kernel_state, std::string root_name,
|
||||||
std::to_string(++content_device_id_) + "\\";
|
std::to_string(++content_device_id_) + "\\";
|
||||||
|
|
||||||
auto fs = kernel_state_->file_system();
|
auto fs = kernel_state_->file_system();
|
||||||
auto device =
|
|
||||||
std::make_unique<vfs::HostPathDevice>(device_path_, package_path, false);
|
std::unique_ptr<vfs::Device> device;
|
||||||
|
|
||||||
|
// If this isn't a folder try mounting as STFS package
|
||||||
|
// Otherwise mount as a local host path
|
||||||
|
if (filesystem::PathExists(package_path) &&
|
||||||
|
!filesystem::IsFolder(package_path)) {
|
||||||
|
device =
|
||||||
|
std::make_unique<vfs::StfsContainerDevice>(device_path_, package_path);
|
||||||
|
} else {
|
||||||
|
device = std::make_unique<vfs::HostPathDevice>(device_path_, package_path,
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
|
||||||
device->Initialize();
|
device->Initialize();
|
||||||
fs->RegisterDevice(std::move(device));
|
fs->RegisterDevice(std::move(device));
|
||||||
fs->RegisterSymbolicLink(root_name_ + ":", device_path_);
|
fs->RegisterSymbolicLink(root_name_ + ":", device_path_);
|
||||||
|
@ -54,37 +68,27 @@ ContentManager::ContentManager(KernelState* kernel_state,
|
||||||
|
|
||||||
ContentManager::~ContentManager() = default;
|
ContentManager::~ContentManager() = default;
|
||||||
|
|
||||||
std::wstring ContentManager::ResolvePackageRoot(uint32_t content_type) {
|
uint32_t ContentManager::title_id() {
|
||||||
wchar_t title_id[9] = L"00000000";
|
if (title_id_override_) {
|
||||||
std::swprintf(title_id, 9, L"%.8X", kernel_state_->title_id());
|
return title_id_override_;
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
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);
|
||||||
|
|
||||||
// Package root path:
|
// Package root path:
|
||||||
// content_root/title_id/type_name/
|
// content_root/title_id/type_name/
|
||||||
auto package_root =
|
auto package_root = xe::join_paths(
|
||||||
xe::join_paths(root_path_, xe::join_paths(title_id, type_name));
|
root_path_, xe::join_paths(title_id_str, content_type_str));
|
||||||
return package_root + xe::kWPathSeparator;
|
return package_root + xe::kWPathSeparator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,7 +98,13 @@ std::wstring ContentManager::ResolvePackagePath(const XCONTENT_DATA& data) {
|
||||||
auto package_root = ResolvePackageRoot(data.content_type);
|
auto package_root = ResolvePackageRoot(data.content_type);
|
||||||
auto package_path =
|
auto package_path =
|
||||||
xe::join_paths(package_root, xe::to_wstring(data.file_name));
|
xe::join_paths(package_root, xe::to_wstring(data.file_name));
|
||||||
|
|
||||||
|
// 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;
|
package_path += xe::kPathSeparator;
|
||||||
|
}
|
||||||
return package_path;
|
return package_path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,10 +117,6 @@ std::vector<XCONTENT_DATA> ContentManager::ListContent(uint32_t device_id,
|
||||||
auto package_root = ResolvePackageRoot(content_type);
|
auto package_root = ResolvePackageRoot(content_type);
|
||||||
auto file_infos = xe::filesystem::ListFiles(package_root);
|
auto file_infos = xe::filesystem::ListFiles(package_root);
|
||||||
for (const auto& file_info : file_infos) {
|
for (const auto& file_info : file_infos) {
|
||||||
if (file_info.type != xe::filesystem::FileInfo::Type::kDirectory) {
|
|
||||||
// Directories only.
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
XCONTENT_DATA content_data;
|
XCONTENT_DATA content_data;
|
||||||
content_data.device_id = device_id;
|
content_data.device_id = device_id;
|
||||||
content_data.content_type = content_type;
|
content_data.content_type = content_type;
|
||||||
|
@ -247,7 +253,11 @@ X_RESULT ContentManager::DeleteContent(const XCONTENT_DATA& data) {
|
||||||
|
|
||||||
auto package_path = ResolvePackagePath(data);
|
auto package_path = ResolvePackagePath(data);
|
||||||
if (xe::filesystem::PathExists(package_path)) {
|
if (xe::filesystem::PathExists(package_path)) {
|
||||||
|
if (xe::filesystem::IsFolder(package_path)) {
|
||||||
xe::filesystem::DeleteFolder(package_path);
|
xe::filesystem::DeleteFolder(package_path);
|
||||||
|
} else {
|
||||||
|
// TODO: delete STFS package?
|
||||||
|
}
|
||||||
return X_ERROR_SUCCESS;
|
return X_ERROR_SUCCESS;
|
||||||
} else {
|
} else {
|
||||||
return X_ERROR_FILE_NOT_FOUND;
|
return X_ERROR_FILE_NOT_FOUND;
|
||||||
|
|
|
@ -86,7 +86,11 @@ class ContentManager {
|
||||||
X_RESULT DeleteContent(const XCONTENT_DATA& data);
|
X_RESULT DeleteContent(const XCONTENT_DATA& data);
|
||||||
std::wstring ResolveGameUserContentPath();
|
std::wstring ResolveGameUserContentPath();
|
||||||
|
|
||||||
|
void SetTitleIdOverride(uint32_t title_id) { title_id_override_ = title_id; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
uint32_t title_id();
|
||||||
|
|
||||||
std::wstring ResolvePackageRoot(uint32_t content_type);
|
std::wstring ResolvePackageRoot(uint32_t content_type);
|
||||||
std::wstring ResolvePackagePath(const XCONTENT_DATA& data);
|
std::wstring ResolvePackagePath(const XCONTENT_DATA& data);
|
||||||
|
|
||||||
|
@ -96,6 +100,9 @@ class ContentManager {
|
||||||
// TODO(benvanik): remove use of global lock, it's bad here!
|
// TODO(benvanik): remove use of global lock, it's bad here!
|
||||||
xe::global_critical_region global_critical_region_;
|
xe::global_critical_region global_critical_region_;
|
||||||
std::unordered_map<std::string, ContentPackage*> open_packages_;
|
std::unordered_map<std::string, ContentPackage*> open_packages_;
|
||||||
|
|
||||||
|
uint32_t title_id_override_ =
|
||||||
|
0; // can be used for games/apps that request content for other IDs
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace xam
|
} // namespace xam
|
||||||
|
|
Loading…
Reference in New Issue