Separation of STFS, SVOD into different entities
This commit is contained in:
192
src/xenia/vfs/devices/xcontent_container_device.cc
Normal file
192
src/xenia/vfs/devices/xcontent_container_device.cc
Normal file
@@ -0,0 +1,192 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2023 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/vfs/devices/xcontent_container_device.h"
|
||||
#include "xenia/vfs/devices/xcontent_devices/stfs_container_device.h"
|
||||
#include "xenia/vfs/devices/xcontent_devices/svod_container_device.h"
|
||||
|
||||
namespace xe {
|
||||
namespace vfs {
|
||||
|
||||
std::unique_ptr<Device> XContentContainerDevice::CreateContentDevice(
|
||||
const std::string_view mount_path, const std::filesystem::path& host_path) {
|
||||
if (!std::filesystem::exists(host_path)) {
|
||||
XELOGE("Path to XContent container does not exist: {}",
|
||||
xe::path_to_utf8(host_path));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (std::filesystem::is_directory(host_path)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FILE* host_file = xe::filesystem::OpenFile(host_path, "rb");
|
||||
if (!host_file) {
|
||||
XELOGE("Error opening XContent file.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const uint64_t package_size = std::filesystem::file_size(host_path);
|
||||
if (package_size < sizeof(XContentContainerHeader)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const auto header = XContentContainerDevice::ReadContainerHeader(host_file);
|
||||
if (header == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
fclose(host_file);
|
||||
|
||||
if (!header->content_header.is_magic_valid()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
switch (header->content_metadata.volume_type) {
|
||||
case XContentVolumeType::kStfs:
|
||||
return std::make_unique<StfsContainerDevice>(mount_path, host_path);
|
||||
break;
|
||||
case XContentVolumeType::kSvod:
|
||||
return std::make_unique<SvodContainerDevice>(mount_path, host_path);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
XContentContainerDevice::XContentContainerDevice(
|
||||
const std::string_view mount_path, const std::filesystem::path& host_path)
|
||||
: Device(mount_path),
|
||||
name_("XContent"),
|
||||
host_path_(host_path),
|
||||
files_total_size_(0),
|
||||
header_(std::make_unique<XContentContainerHeader>()) {}
|
||||
|
||||
XContentContainerDevice::~XContentContainerDevice() {}
|
||||
|
||||
bool XContentContainerDevice::Initialize() {
|
||||
if (!std::filesystem::exists(host_path_)) {
|
||||
XELOGE("Path to XContent container does not exist: {}",
|
||||
xe::path_to_utf8(host_path_));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (std::filesystem::is_directory(host_path_)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
XELOGI("Loading XContent header file: {}", xe::path_to_utf8(host_path_));
|
||||
auto header_file = xe::filesystem::OpenFile(host_path_, "rb");
|
||||
if (!header_file) {
|
||||
XELOGE("Error opening XContent header file.");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto header_loading_result = ReadHeaderAndVerify(header_file);
|
||||
if (header_loading_result != Result::kSuccess) {
|
||||
XELOGE("Error reading XContent header: {}", header_loading_result);
|
||||
fclose(header_file);
|
||||
return false;
|
||||
}
|
||||
|
||||
SetupContainer();
|
||||
|
||||
if (LoadHostFiles(header_file) != Result::kSuccess) {
|
||||
XELOGE("Error loading XContent host files.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return Read() == Result::kSuccess;
|
||||
}
|
||||
|
||||
XContentContainerHeader* XContentContainerDevice::ReadContainerHeader(
|
||||
FILE* host_file) {
|
||||
XContentContainerHeader* header = new XContentContainerHeader();
|
||||
// Read header & check signature
|
||||
if (fread(header, sizeof(XContentContainerHeader), 1, host_file) != 1) {
|
||||
return nullptr;
|
||||
}
|
||||
return header;
|
||||
}
|
||||
|
||||
Entry* XContentContainerDevice::ResolvePath(const std::string_view path) {
|
||||
// The filesystem will have stripped our prefix off already, so the path will
|
||||
// be in the form:
|
||||
// some\PATH.foo
|
||||
XELOGFS("StfsContainerDevice::ResolvePath({})", path);
|
||||
return root_entry_->ResolvePath(path);
|
||||
}
|
||||
|
||||
void XContentContainerDevice::Dump(StringBuffer* string_buffer) {
|
||||
auto global_lock = global_critical_region_.Acquire();
|
||||
root_entry_->Dump(string_buffer, 0);
|
||||
}
|
||||
|
||||
void XContentContainerDevice::CloseFiles() {
|
||||
for (auto& file : files_) {
|
||||
fclose(file.second);
|
||||
}
|
||||
files_.clear();
|
||||
files_total_size_ = 0;
|
||||
}
|
||||
|
||||
kernel::xam::XCONTENT_AGGREGATE_DATA XContentContainerDevice::content_header()
|
||||
const {
|
||||
kernel::xam::XCONTENT_AGGREGATE_DATA data;
|
||||
|
||||
std::memset(&data, 0, sizeof(kernel::xam::XCONTENT_AGGREGATE_DATA));
|
||||
|
||||
data.device_id = 1;
|
||||
data.title_id = header_->content_metadata.execution_info.title_id;
|
||||
data.content_type = header_->content_metadata.content_type;
|
||||
|
||||
auto name = header_->content_metadata.display_name(XLanguage::kEnglish);
|
||||
if (name.empty()) {
|
||||
// Find first filled language and use it. It might be incorrect, but meh
|
||||
// until stfs support is done.
|
||||
for (uint8_t i = 0; i < header_->content_metadata.kNumLanguagesV2; i++) {
|
||||
name = header_->content_metadata.display_name((XLanguage)i);
|
||||
if (!name.empty()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data.set_display_name(name);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
XContentContainerDevice::Result XContentContainerDevice::ReadHeaderAndVerify(
|
||||
FILE* header_file) {
|
||||
files_total_size_ = std::filesystem::file_size(host_path_);
|
||||
if (files_total_size_ < sizeof(XContentContainerHeader)) {
|
||||
return Result::kTooSmall;
|
||||
}
|
||||
|
||||
const XContentContainerHeader* header = ReadContainerHeader(header_file);
|
||||
if (header == nullptr) {
|
||||
return Result::kReadError;
|
||||
}
|
||||
|
||||
std::memcpy(header_.get(), header, sizeof(XContentContainerHeader));
|
||||
|
||||
if (!header_->content_header.is_magic_valid()) {
|
||||
// Unexpected format.
|
||||
return Result::kFileMismatch;
|
||||
}
|
||||
|
||||
return Result::kSuccess;
|
||||
}
|
||||
|
||||
} // namespace vfs
|
||||
} // namespace xe
|
||||
Reference in New Issue
Block a user