[VFS] Rewrote STFS to use memory mapping
- Replaced old-style buffer, buffer_length with std::span - Added STFS and SVOD specific Entry and File classes - Other smaller improvements
This commit is contained in:
parent
e85c2392ba
commit
fe739208b6
|
@ -412,8 +412,9 @@ bool AudioMediaPlayer::LoadSongToMemory(std::vector<uint8_t>* buffer) {
|
|||
|
||||
buffer->resize(vfs_file->entry()->size());
|
||||
size_t bytes_read = 0;
|
||||
result = vfs_file->ReadSync(buffer->data(), vfs_file->entry()->size(), 0,
|
||||
&bytes_read);
|
||||
result = vfs_file->ReadSync(
|
||||
std::span<uint8_t>(buffer->data(), vfs_file->entry()->size()), 0,
|
||||
&bytes_read);
|
||||
|
||||
return !result;
|
||||
}
|
||||
|
|
|
@ -157,8 +157,8 @@ bool KernelState::UpdateSpaData(vfs::Entry* spa_file_update) {
|
|||
std::vector<uint8_t> data(spa_file_update->size());
|
||||
|
||||
size_t read_bytes = 0;
|
||||
if (file->ReadSync(data.data(), spa_file_update->size(), 0, &read_bytes) !=
|
||||
X_STATUS_SUCCESS) {
|
||||
if (file->ReadSync(std::span<uint8_t>(data.data(), spa_file_update->size()),
|
||||
0, &read_bytes) != X_STATUS_SUCCESS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -102,7 +102,8 @@ X_STATUS UserModule::LoadFromFile(const std::string_view path) {
|
|||
// Read entire file into memory.
|
||||
// Ugh.
|
||||
size_t bytes_read = 0;
|
||||
result = file->ReadSync(buffer.data(), buffer.size(), 0, &bytes_read);
|
||||
result = file->ReadSync(std::span<uint8_t>(buffer.data(), buffer.size()), 0,
|
||||
&bytes_read);
|
||||
if (XFAILED(result)) {
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#define XENIA_KERNEL_UTIL_XEX2_INFO_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "xenia/base/byte_order.h"
|
||||
|
|
|
@ -202,8 +202,9 @@ bool ProfileManager::LoadAccount(const uint64_t xuid) {
|
|||
file_data.resize(output_file->entry()->size());
|
||||
|
||||
size_t bytes_read = 0;
|
||||
output_file->ReadSync(file_data.data(), output_file->entry()->size(), 0,
|
||||
&bytes_read);
|
||||
output_file->ReadSync(
|
||||
std::span<uint8_t>(file_data.data(), output_file->entry()->size()), 0,
|
||||
&bytes_read);
|
||||
output_file->Destroy();
|
||||
|
||||
if (bytes_read < sizeof(X_XAMACCOUNTINFO)) {
|
||||
|
@ -562,8 +563,9 @@ bool ProfileManager::UpdateAccount(const uint64_t xuid,
|
|||
EncryptAccountFile(account, encrypted_data.data());
|
||||
|
||||
size_t written_bytes = 0;
|
||||
output_file->WriteSync(encrypted_data.data(), encrypted_data.size(), 0,
|
||||
&written_bytes);
|
||||
output_file->WriteSync(
|
||||
std::span<uint8_t>(encrypted_data.data(), encrypted_data.size()), 0,
|
||||
&written_bytes);
|
||||
output_file->Destroy();
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -89,7 +89,8 @@ void UserProfile::LoadProfileIcon(XTileType tile_type) {
|
|||
|
||||
std::vector<uint8_t> data(file->entry()->size());
|
||||
size_t written_bytes = 0;
|
||||
file->ReadSync(data.data(), file->entry()->size(), 0, &written_bytes);
|
||||
file->ReadSync(std::span<uint8_t>(data.data(), file->entry()->size()), 0,
|
||||
&written_bytes);
|
||||
file->Destroy();
|
||||
|
||||
profile_images_.insert({tile_type, data});
|
||||
|
@ -114,7 +115,8 @@ std::vector<uint8_t> UserProfile::LoadGpd(const uint32_t title_id) {
|
|||
std::vector<uint8_t> data(entry->size());
|
||||
|
||||
size_t read_size = 0;
|
||||
result = file->ReadSync(data.data(), entry->size(), 0, &read_size);
|
||||
result = file->ReadSync(std::span<uint8_t>(data.data(), entry->size()), 0,
|
||||
&read_size);
|
||||
if (result != X_STATUS_SUCCESS || read_size != entry->size()) {
|
||||
XELOGW(
|
||||
"User {} (XUID: {:016X}) cannot read profile GPD! Status: {:08X} read: "
|
||||
|
@ -150,7 +152,8 @@ bool UserProfile::WriteGpd(const uint32_t title_id) {
|
|||
}
|
||||
|
||||
size_t written_bytes = 0;
|
||||
file->WriteSync(data.data(), data.size(), 0, &written_bytes);
|
||||
file->WriteSync(std::span<uint8_t>(data.data(), data.size()), 0,
|
||||
&written_bytes);
|
||||
file->Destroy();
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -277,7 +277,9 @@ dword_result_t XexLoadImageHeaders_entry(pointer_t<X_ANSI_STRING> path,
|
|||
size_t bytes_read = 0;
|
||||
|
||||
X_STATUS result_status = vfs_file->ReadSync(
|
||||
reinterpret_cast<void*>(header.host_address()), 2048, 0, &bytes_read);
|
||||
std::span<uint8_t>(reinterpret_cast<uint8_t*>(header.host_address()),
|
||||
2048),
|
||||
0, &bytes_read);
|
||||
|
||||
if (result_status < 0) {
|
||||
vfs_file->Destroy();
|
||||
|
@ -298,8 +300,10 @@ dword_result_t XexLoadImageHeaders_entry(pointer_t<X_ANSI_STRING> path,
|
|||
result_status = X_STATUS_SUCCESS;
|
||||
} else {
|
||||
result_status = vfs_file->ReadSync(
|
||||
reinterpret_cast<void*>(header.host_address() + 2048),
|
||||
header_size - 2048, 2048, &bytes_read);
|
||||
std::span<uint8_t>(
|
||||
reinterpret_cast<uint8_t*>(header.host_address() + 2048),
|
||||
header_size - 2048),
|
||||
2048, &bytes_read);
|
||||
if (result_status >= X_STATUS_SUCCESS) {
|
||||
result_status = X_STATUS_SUCCESS;
|
||||
}
|
||||
|
|
|
@ -143,12 +143,14 @@ X_STATUS XFile::Read(uint32_t buffer_guest_address, uint32_t buffer_length,
|
|||
result = X_STATUS_ACCESS_VIOLATION;
|
||||
} else {
|
||||
result = file_->ReadSync(
|
||||
buffer_physical_heap
|
||||
? memory()->TranslatePhysical(
|
||||
buffer_physical_heap->GetPhysicalAddress(
|
||||
buffer_guest_address))
|
||||
: memory()->TranslateVirtual(buffer_guest_address),
|
||||
buffer_length, size_t(byte_offset), &bytes_read);
|
||||
std::span<uint8_t>(
|
||||
buffer_physical_heap
|
||||
? memory()->TranslatePhysical(
|
||||
buffer_physical_heap->GetPhysicalAddress(
|
||||
buffer_guest_address))
|
||||
: memory()->TranslateVirtual(buffer_guest_address),
|
||||
buffer_length),
|
||||
size_t(byte_offset), &bytes_read);
|
||||
if (XSUCCEEDED(result)) {
|
||||
if (buffer_physical_heap) {
|
||||
buffer_physical_heap->TriggerCallbacks(
|
||||
|
@ -245,9 +247,10 @@ X_STATUS XFile::Write(uint32_t buffer_guest_address, uint32_t buffer_length,
|
|||
}
|
||||
|
||||
size_t bytes_written = 0;
|
||||
X_STATUS result =
|
||||
file_->WriteSync(memory()->TranslateVirtual(buffer_guest_address),
|
||||
buffer_length, size_t(byte_offset), &bytes_written);
|
||||
X_STATUS result = file_->WriteSync(
|
||||
std::span<uint8_t>(memory()->TranslateVirtual(buffer_guest_address),
|
||||
buffer_length),
|
||||
size_t(byte_offset), &bytes_written);
|
||||
if (XSUCCEEDED(result)) {
|
||||
position_ += bytes_written;
|
||||
}
|
||||
|
|
|
@ -23,8 +23,8 @@ DiscImageFile::~DiscImageFile() = default;
|
|||
|
||||
void DiscImageFile::Destroy() { delete this; }
|
||||
|
||||
X_STATUS DiscImageFile::ReadSync(void* buffer, size_t buffer_length,
|
||||
size_t byte_offset, size_t* out_bytes_read) {
|
||||
X_STATUS DiscImageFile::ReadSync(std::span<uint8_t> buffer, size_t byte_offset,
|
||||
size_t* out_bytes_read) {
|
||||
if (byte_offset >= entry_->size()) {
|
||||
return X_STATUS_END_OF_FILE;
|
||||
}
|
||||
|
@ -36,8 +36,8 @@ X_STATUS DiscImageFile::ReadSync(void* buffer, size_t buffer_length,
|
|||
|
||||
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);
|
||||
std::min(buffer.size(), entry_->data_size() - byte_offset);
|
||||
std::memcpy(buffer.data(), entry_->mmap()->data() + real_offset, real_length);
|
||||
*out_bytes_read = real_length;
|
||||
return X_STATUS_SUCCESS;
|
||||
}
|
||||
|
|
|
@ -24,10 +24,10 @@ class DiscImageFile : public File {
|
|||
|
||||
void Destroy() override;
|
||||
|
||||
X_STATUS ReadSync(void* buffer, size_t buffer_length, size_t byte_offset,
|
||||
X_STATUS ReadSync(std::span<uint8_t> buffer, size_t byte_offset,
|
||||
size_t* out_bytes_read) override;
|
||||
X_STATUS WriteSync(const void* buffer, size_t buffer_length,
|
||||
size_t byte_offset, size_t* out_bytes_written) override {
|
||||
X_STATUS WriteSync(std::span<const uint8_t> buffer, size_t byte_offset,
|
||||
size_t* out_bytes_written) override {
|
||||
return X_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
X_STATUS SetLength(size_t length) override { return X_STATUS_ACCESS_DENIED; }
|
||||
|
|
|
@ -25,19 +25,23 @@ DiscZarchiveFile::~DiscZarchiveFile() = default;
|
|||
|
||||
void DiscZarchiveFile::Destroy() { delete this; }
|
||||
|
||||
X_STATUS DiscZarchiveFile::ReadSync(void* buffer, size_t buffer_length,
|
||||
X_STATUS DiscZarchiveFile::ReadSync(std::span<uint8_t> buffer,
|
||||
size_t byte_offset,
|
||||
size_t* out_bytes_read) {
|
||||
if (byte_offset >= entry_->size()) {
|
||||
return X_STATUS_END_OF_FILE;
|
||||
}
|
||||
const uint64_t bytes_read =
|
||||
((DiscZarchiveDevice*)entry_->device_)
|
||||
->reader()
|
||||
->ReadFromFile(entry_->handle_, byte_offset, buffer_length, buffer);
|
||||
const size_t real_length =
|
||||
std::min(buffer_length, entry_->data_size() - byte_offset);
|
||||
*out_bytes_read = real_length;
|
||||
|
||||
DiscZarchiveDevice* zArchDev =
|
||||
dynamic_cast<DiscZarchiveDevice*>(entry_->device_);
|
||||
|
||||
if (!zArchDev) {
|
||||
return X_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
const uint64_t bytes_read = zArchDev->reader()->ReadFromFile(
|
||||
entry_->handle_, byte_offset, buffer.size(), buffer.data());
|
||||
*out_bytes_read = bytes_read;
|
||||
return X_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
|
|
@ -24,10 +24,10 @@ class DiscZarchiveFile : public File {
|
|||
|
||||
void Destroy() override;
|
||||
|
||||
X_STATUS ReadSync(void* buffer, size_t buffer_length, size_t byte_offset,
|
||||
X_STATUS ReadSync(std::span<uint8_t> buffer, size_t byte_offset,
|
||||
size_t* out_bytes_read) override;
|
||||
X_STATUS WriteSync(const void* buffer, size_t buffer_length,
|
||||
size_t byte_offset, size_t* out_bytes_written) override {
|
||||
X_STATUS WriteSync(std::span<const uint8_t> buffer, size_t byte_offset,
|
||||
size_t* out_bytes_written) override {
|
||||
return X_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
X_STATUS SetLength(size_t length) override { return X_STATUS_ACCESS_DENIED; }
|
||||
|
|
|
@ -28,21 +28,22 @@ void HostPathFile::Destroy() {
|
|||
delete this;
|
||||
}
|
||||
|
||||
X_STATUS HostPathFile::ReadSync(void* buffer, size_t buffer_length,
|
||||
size_t byte_offset, size_t* out_bytes_read) {
|
||||
X_STATUS HostPathFile::ReadSync(std::span<uint8_t> buffer, size_t byte_offset,
|
||||
size_t* out_bytes_read) {
|
||||
if (!(file_access_ &
|
||||
(FileAccess::kGenericRead | FileAccess::kFileReadData))) {
|
||||
return X_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
if (file_handle_->Read(byte_offset, buffer, buffer_length, out_bytes_read)) {
|
||||
if (file_handle_->Read(byte_offset, buffer.data(), buffer.size(),
|
||||
out_bytes_read)) {
|
||||
return X_STATUS_SUCCESS;
|
||||
} else {
|
||||
return X_STATUS_END_OF_FILE;
|
||||
}
|
||||
}
|
||||
|
||||
X_STATUS HostPathFile::WriteSync(const void* buffer, size_t buffer_length,
|
||||
X_STATUS HostPathFile::WriteSync(std::span<const uint8_t> buffer,
|
||||
size_t byte_offset,
|
||||
size_t* out_bytes_written) {
|
||||
if (!(file_access_ & (FileAccess::kGenericWrite | FileAccess::kFileWriteData |
|
||||
|
@ -50,7 +51,7 @@ X_STATUS HostPathFile::WriteSync(const void* buffer, size_t buffer_length,
|
|||
return X_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
if (file_handle_->Write(byte_offset, buffer, buffer_length,
|
||||
if (file_handle_->Write(byte_offset, buffer.data(), buffer.size(),
|
||||
out_bytes_written)) {
|
||||
return X_STATUS_SUCCESS;
|
||||
} else {
|
||||
|
|
|
@ -28,10 +28,10 @@ class HostPathFile : public File {
|
|||
|
||||
void Destroy() override;
|
||||
|
||||
X_STATUS ReadSync(void* buffer, size_t buffer_length, size_t byte_offset,
|
||||
X_STATUS ReadSync(std::span<uint8_t> buffer, size_t byte_offset,
|
||||
size_t* out_bytes_read) override;
|
||||
X_STATUS WriteSync(const void* buffer, size_t buffer_length,
|
||||
size_t byte_offset, size_t* out_bytes_written) override;
|
||||
X_STATUS WriteSync(std::span<const uint8_t> buffer, size_t byte_offset,
|
||||
size_t* out_bytes_written) override;
|
||||
X_STATUS SetLength(size_t length) override;
|
||||
|
||||
private:
|
||||
|
|
|
@ -21,8 +21,8 @@ NullFile::~NullFile() = default;
|
|||
|
||||
void NullFile::Destroy() { delete this; }
|
||||
|
||||
X_STATUS NullFile::ReadSync(void* buffer, size_t buffer_length,
|
||||
size_t byte_offset, size_t* out_bytes_read) {
|
||||
X_STATUS NullFile::ReadSync(std::span<uint8_t> buffer, size_t byte_offset,
|
||||
size_t* out_bytes_read) {
|
||||
if (!(file_access_ & FileAccess::kFileReadData)) {
|
||||
return X_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ X_STATUS NullFile::ReadSync(void* buffer, size_t buffer_length,
|
|||
return X_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
X_STATUS NullFile::WriteSync(const void* buffer, size_t buffer_length,
|
||||
X_STATUS NullFile::WriteSync(std::span<const uint8_t> buffer,
|
||||
size_t byte_offset, size_t* out_bytes_written) {
|
||||
if (!(file_access_ &
|
||||
(FileAccess::kFileWriteData | FileAccess::kFileAppendData))) {
|
||||
|
|
|
@ -27,10 +27,10 @@ class NullFile : public File {
|
|||
|
||||
void Destroy() override;
|
||||
|
||||
X_STATUS ReadSync(void* buffer, size_t buffer_length, size_t byte_offset,
|
||||
X_STATUS ReadSync(std::span<uint8_t> buffer, size_t byte_offset,
|
||||
size_t* out_bytes_read) override;
|
||||
X_STATUS WriteSync(const void* buffer, size_t buffer_length,
|
||||
size_t byte_offset, size_t* out_bytes_written) override;
|
||||
X_STATUS WriteSync(std::span<const uint8_t> buffer, size_t byte_offset,
|
||||
size_t* out_bytes_written) override;
|
||||
X_STATUS SetLength(size_t length) override;
|
||||
};
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2023 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2025 Xenia Canary. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
@ -100,7 +100,7 @@ bool XContentContainerDevice::Initialize() {
|
|||
|
||||
SetupContainer();
|
||||
|
||||
if (LoadHostFiles(header_file) != Result::kSuccess) {
|
||||
if (LoadHostFiles() != Result::kSuccess) {
|
||||
XELOGE("Error loading XContent host files.");
|
||||
return false;
|
||||
}
|
||||
|
@ -152,14 +152,6 @@ void XContentContainerDevice::Dump(StringBuffer* string_buffer) {
|
|||
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;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2023 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2025 Xenia Canary. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
@ -14,8 +14,6 @@
|
|||
#include <map>
|
||||
#include <string_view>
|
||||
|
||||
#include "xenia/base/math.h"
|
||||
#include "xenia/kernel/util/xex2_info.h"
|
||||
#include "xenia/kernel/xam/content_manager.h"
|
||||
#include "xenia/vfs/device.h"
|
||||
#include "xenia/vfs/devices/stfs_xbox.h"
|
||||
|
@ -66,7 +64,7 @@ class XContentContainerDevice : public Device {
|
|||
}
|
||||
|
||||
uint32_t content_type() const {
|
||||
return (uint32_t)header_->content_metadata.content_type.get();
|
||||
return static_cast<uint32_t>(header_->content_metadata.content_type.get());
|
||||
}
|
||||
|
||||
kernel::xam::XCONTENT_AGGREGATE_DATA content_header() const;
|
||||
|
@ -99,12 +97,11 @@ class XContentContainerDevice : public Device {
|
|||
virtual Result Read() = 0;
|
||||
// Load all host files. Usually STFS is only 1 file, meanwhile SVOD is usually
|
||||
// multiple file.
|
||||
virtual Result LoadHostFiles(FILE* header_file) = 0;
|
||||
// Initialize any container specific fields.
|
||||
virtual Result LoadHostFiles() = 0;
|
||||
// Initialize container specific fields.
|
||||
virtual void SetupContainer() {};
|
||||
|
||||
Entry* ResolvePath(const std::string_view path) override;
|
||||
void CloseFiles();
|
||||
void Dump(StringBuffer* string_buffer) override;
|
||||
Result ReadHeaderAndVerify(FILE* header_file);
|
||||
|
||||
|
@ -119,7 +116,6 @@ class XContentContainerDevice : public Device {
|
|||
std::string name_;
|
||||
std::filesystem::path host_path_;
|
||||
|
||||
std::map<size_t, FILE*> files_;
|
||||
size_t files_total_size_;
|
||||
std::unique_ptr<Entry> root_entry_;
|
||||
std::unique_ptr<XContentContainerHeader> header_;
|
||||
|
|
|
@ -2,46 +2,22 @@
|
|||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2023 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2025 Xenia Canary. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "xenia/vfs/devices/xcontent_container_entry.h"
|
||||
#include "xenia/vfs/devices/xcontent_container_file.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
namespace xe {
|
||||
namespace vfs {
|
||||
|
||||
XContentContainerEntry::XContentContainerEntry(Device* device, Entry* parent,
|
||||
const std::string_view path,
|
||||
MultiFileHandles* files)
|
||||
: Entry(device, parent, path),
|
||||
files_(files),
|
||||
data_offset_(0),
|
||||
data_size_(0),
|
||||
block_(0) {}
|
||||
const std::string_view path)
|
||||
: Entry(device, parent, path), data_offset_(0), data_size_(0), block_(0) {}
|
||||
|
||||
XContentContainerEntry::~XContentContainerEntry() = default;
|
||||
|
||||
std::unique_ptr<XContentContainerEntry> XContentContainerEntry::Create(
|
||||
Device* device, Entry* parent, const std::string_view name,
|
||||
MultiFileHandles* files) {
|
||||
auto path = xe::utf8::join_guest_paths(parent->path(), name);
|
||||
auto entry =
|
||||
std::make_unique<XContentContainerEntry>(device, parent, path, files);
|
||||
|
||||
return std::move(entry);
|
||||
}
|
||||
|
||||
X_STATUS XContentContainerEntry::Open(uint32_t desired_access,
|
||||
File** out_file) {
|
||||
*out_file = new XContentContainerFile(desired_access, this);
|
||||
return X_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
bool XContentContainerEntry::DeleteEntryInternal(Entry* entry) { return false; }
|
||||
|
||||
} // namespace vfs
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2023 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2025 Xenia Canary. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
@ -10,8 +10,6 @@
|
|||
#ifndef XENIA_VFS_DEVICES_XCONTENT_CONTAINER_ENTRY_H_
|
||||
#define XENIA_VFS_DEVICES_XCONTENT_CONTAINER_ENTRY_H_
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "xenia/vfs/entry.h"
|
||||
|
@ -19,26 +17,18 @@
|
|||
|
||||
namespace xe {
|
||||
namespace vfs {
|
||||
typedef std::map<size_t, FILE*> MultiFileHandles;
|
||||
|
||||
class XContentContainerDevice;
|
||||
|
||||
class XContentContainerEntry : public Entry {
|
||||
public:
|
||||
XContentContainerEntry(Device* device, Entry* parent,
|
||||
const std::string_view path, MultiFileHandles* files);
|
||||
const std::string_view path);
|
||||
~XContentContainerEntry() override;
|
||||
|
||||
static std::unique_ptr<XContentContainerEntry> Create(
|
||||
Device* device, Entry* parent, const std::string_view name,
|
||||
MultiFileHandles* files);
|
||||
|
||||
MultiFileHandles* files() const { return files_; }
|
||||
size_t data_offset() const { return data_offset_; }
|
||||
size_t data_size() const { return data_size_; }
|
||||
size_t block() const { return block_; }
|
||||
|
||||
X_STATUS Open(uint32_t desired_access, File** out_file) override;
|
||||
X_STATUS Open(uint32_t desired_access, File** out_file) override = 0;
|
||||
|
||||
struct BlockRecord {
|
||||
size_t file;
|
||||
|
@ -53,7 +43,6 @@ class XContentContainerEntry : public Entry {
|
|||
|
||||
bool DeleteEntryInternal(Entry* entry) override;
|
||||
|
||||
MultiFileHandles* files_;
|
||||
size_t data_offset_;
|
||||
size_t data_size_;
|
||||
size_t block_;
|
||||
|
|
|
@ -2,17 +2,13 @@
|
|||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2023 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2025 Xenia Canary. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
#include "xenia/base/math.h"
|
||||
#include "xenia/vfs/devices/xcontent_container_entry.h"
|
||||
#include "xenia/vfs/devices/xcontent_container_file.h"
|
||||
#include "xenia/vfs/devices/xcontent_container_entry.h"
|
||||
|
||||
namespace xe {
|
||||
namespace vfs {
|
||||
|
@ -25,7 +21,7 @@ XContentContainerFile::~XContentContainerFile() = default;
|
|||
|
||||
void XContentContainerFile::Destroy() { delete this; }
|
||||
|
||||
X_STATUS XContentContainerFile::ReadSync(void* buffer, size_t buffer_length,
|
||||
X_STATUS XContentContainerFile::ReadSync(std::span<uint8_t> buffer,
|
||||
size_t byte_offset,
|
||||
size_t* out_bytes_read) {
|
||||
if (byte_offset >= entry_->size()) {
|
||||
|
@ -33,9 +29,9 @@ X_STATUS XContentContainerFile::ReadSync(void* buffer, size_t buffer_length,
|
|||
}
|
||||
|
||||
size_t src_offset = 0;
|
||||
uint8_t* p = reinterpret_cast<uint8_t*>(buffer);
|
||||
uint8_t* p = buffer.data();
|
||||
size_t remaining_length =
|
||||
std::min(buffer_length, entry_->size() - byte_offset);
|
||||
std::min(buffer.size(), entry_->size() - byte_offset);
|
||||
|
||||
*out_bytes_read = 0;
|
||||
for (size_t i = 0; i < entry_->block_list().size(); i++) {
|
||||
|
@ -51,9 +47,8 @@ X_STATUS XContentContainerFile::ReadSync(void* buffer, size_t buffer_length,
|
|||
size_t read_length =
|
||||
std::min(record.length - read_offset, remaining_length);
|
||||
|
||||
auto& file = entry_->files()->at(record.file);
|
||||
xe::filesystem::Seek(file, record.offset + read_offset, SEEK_SET);
|
||||
auto num_read = fread(p, 1, read_length, file);
|
||||
auto num_read = Read(std::span<uint8_t>(p, read_length),
|
||||
record.offset + read_offset, record.file);
|
||||
|
||||
*out_bytes_read += num_read;
|
||||
p += num_read;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2023 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2025 Xenia Canary. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
@ -26,15 +26,18 @@ class XContentContainerFile : public File {
|
|||
|
||||
void Destroy() override;
|
||||
|
||||
X_STATUS ReadSync(void* buffer, size_t buffer_length, size_t byte_offset,
|
||||
X_STATUS ReadSync(std::span<uint8_t> buffer, size_t byte_offset,
|
||||
size_t* out_bytes_read) override;
|
||||
X_STATUS WriteSync(const void* buffer, size_t buffer_length,
|
||||
size_t byte_offset, size_t* out_bytes_written) override {
|
||||
X_STATUS WriteSync(std::span<const uint8_t> buffer, size_t byte_offset,
|
||||
size_t* out_bytes_written) override {
|
||||
return X_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
X_STATUS SetLength(size_t length) override { return X_STATUS_ACCESS_DENIED; }
|
||||
|
||||
private:
|
||||
virtual size_t Read(std::span<uint8_t> buffer, size_t offset,
|
||||
size_t record_file) = 0;
|
||||
|
||||
XContentContainerEntry* entry_;
|
||||
};
|
||||
|
||||
|
|
|
@ -7,13 +7,10 @@
|
|||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
#include "xenia/vfs/devices/xcontent_devices/stfs_container_device.h"
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/kernel/xam/content_manager.h"
|
||||
#include "xenia/vfs/devices/xcontent_container_entry.h"
|
||||
#include "xenia/vfs/devices/xcontent_devices/stfs_container_device.h"
|
||||
#include "xenia/vfs/devices/xcontent_devices/stfs_container_entry.h"
|
||||
|
||||
namespace xe {
|
||||
namespace vfs {
|
||||
|
@ -26,7 +23,7 @@ StfsContainerDevice::StfsContainerDevice(const std::string_view mount_path,
|
|||
SetName("STFS");
|
||||
}
|
||||
|
||||
StfsContainerDevice::~StfsContainerDevice() { CloseFiles(); }
|
||||
StfsContainerDevice::~StfsContainerDevice() {}
|
||||
|
||||
void StfsContainerDevice::SetupContainer() {
|
||||
// Additional part specific to STFS container.
|
||||
|
@ -38,26 +35,27 @@ void StfsContainerDevice::SetupContainer() {
|
|||
((kBlocksPerHashLevel[0] + 1) * blocks_per_hash_table_);
|
||||
}
|
||||
|
||||
XContentContainerDevice::Result StfsContainerDevice::LoadHostFiles(
|
||||
FILE* header_file) {
|
||||
XContentContainerDevice::Result StfsContainerDevice::LoadHostFiles() {
|
||||
const XContentContainerHeader* header = GetContainerHeader();
|
||||
|
||||
if (header->content_metadata.data_file_count > 0) {
|
||||
XELOGW("STFS container is not a single file. Loading might fail!");
|
||||
}
|
||||
|
||||
files_.emplace(std::make_pair(0, header_file));
|
||||
data_ = MappedMemory::Open(host_path_, MappedMemory::Mode::kRead);
|
||||
if (!data_) {
|
||||
return Result::kOutOfMemory;
|
||||
}
|
||||
|
||||
return Result::kSuccess;
|
||||
}
|
||||
|
||||
StfsContainerDevice::Result StfsContainerDevice::Read() {
|
||||
auto& file = files_.at(0);
|
||||
|
||||
auto root_entry = new XContentContainerEntry(this, nullptr, "", &files_);
|
||||
auto root_entry = new StfsContainerEntry(this, nullptr, "", data_.get());
|
||||
root_entry->attributes_ = kFileAttributeDirectory;
|
||||
root_entry_ = std::unique_ptr<Entry>(root_entry);
|
||||
|
||||
std::vector<XContentContainerEntry*> all_entries;
|
||||
std::vector<StfsContainerEntry*> all_entries;
|
||||
|
||||
// Load all listings.
|
||||
StfsDirectoryBlock directory;
|
||||
|
@ -68,12 +66,7 @@ StfsContainerDevice::Result StfsContainerDevice::Read() {
|
|||
size_t n = 0;
|
||||
for (n = 0; n < descriptor.file_table_block_count; n++) {
|
||||
const size_t offset = BlockToOffset(table_block_index);
|
||||
xe::filesystem::Seek(file, offset, SEEK_SET);
|
||||
|
||||
if (fread(&directory, sizeof(StfsDirectoryBlock), 1, file) != 1) {
|
||||
XELOGE("ReadSTFS failed to read directory block at 0x{X}", offset);
|
||||
return Result::kReadError;
|
||||
}
|
||||
directory = *reinterpret_cast<StfsDirectoryBlock*>(data_->data() + offset);
|
||||
|
||||
for (size_t m = 0; m < kEntriesPerDirectoryBlock; m++) {
|
||||
const StfsDirectoryEntry& dir_entry = directory.entries[m];
|
||||
|
@ -83,13 +76,13 @@ StfsContainerDevice::Result StfsContainerDevice::Read() {
|
|||
break;
|
||||
}
|
||||
|
||||
XContentContainerEntry* parent_entry =
|
||||
StfsContainerEntry* parent_entry =
|
||||
dir_entry.directory_index == 0xFFFF
|
||||
? root_entry
|
||||
: all_entries[dir_entry.directory_index];
|
||||
|
||||
std::unique_ptr<XContentContainerEntry> entry =
|
||||
ReadEntry(parent_entry, &files_, &dir_entry);
|
||||
std::unique_ptr<StfsContainerEntry> entry =
|
||||
ReadEntry(parent_entry, &dir_entry);
|
||||
all_entries.push_back(entry.get());
|
||||
parent_entry->children_.emplace_back(std::move(entry));
|
||||
}
|
||||
|
@ -110,9 +103,8 @@ StfsContainerDevice::Result StfsContainerDevice::Read() {
|
|||
return Result::kSuccess;
|
||||
}
|
||||
|
||||
std::unique_ptr<XContentContainerEntry> StfsContainerDevice::ReadEntry(
|
||||
Entry* parent, MultiFileHandles* files,
|
||||
const StfsDirectoryEntry* dir_entry) {
|
||||
std::unique_ptr<StfsContainerEntry> StfsContainerDevice::ReadEntry(
|
||||
Entry* parent, const StfsDirectoryEntry* dir_entry) {
|
||||
// Filename is stored as Windows-1252, convert it to UTF-8.
|
||||
std::string ansi_name(reinterpret_cast<const char*>(dir_entry->name),
|
||||
dir_entry->flags.name_length & 0x3F);
|
||||
|
@ -123,7 +115,7 @@ std::unique_ptr<XContentContainerEntry> StfsContainerDevice::ReadEntry(
|
|||
name = ansi_name;
|
||||
}
|
||||
|
||||
auto entry = XContentContainerEntry::Create(this, parent, name, &files_);
|
||||
auto entry = StfsContainerEntry::Create(this, parent, name, data_.get());
|
||||
|
||||
if (dir_entry->flags.directory) {
|
||||
entry->attributes_ = kFileAttributeDirectory;
|
||||
|
@ -253,15 +245,8 @@ void StfsContainerDevice::UpdateCachedHashTable(
|
|||
const size_t hash_offset = BlockToHashBlockOffset(block_index, hash_level);
|
||||
// Do nothing. It's already there.
|
||||
if (!cached_hash_tables_.count(hash_offset)) {
|
||||
auto& file = files_.at(0);
|
||||
xe::filesystem::Seek(file, hash_offset + secondary_table_offset, SEEK_SET);
|
||||
StfsHashTable table;
|
||||
if (fread(&table, sizeof(StfsHashTable), 1, file) != 1) {
|
||||
XELOGE("GetBlockHash failed to read level{} hash table at 0x{:08X}",
|
||||
hash_level, hash_offset + secondary_table_offset);
|
||||
return;
|
||||
}
|
||||
cached_hash_tables_[hash_offset] = table;
|
||||
cached_hash_tables_[hash_offset] = *reinterpret_cast<StfsHashTable*>(
|
||||
data_->data() + hash_offset + secondary_table_offset);
|
||||
}
|
||||
|
||||
uint32_t record = block_index % kBlocksPerHashLevel[0];
|
||||
|
@ -283,8 +268,6 @@ void StfsContainerDevice::UpdateCachedHashTables(
|
|||
}
|
||||
|
||||
const StfsHashEntry* StfsContainerDevice::GetBlockHash(uint32_t block_index) {
|
||||
auto& file = files_.at(0);
|
||||
|
||||
const StfsVolumeDescriptor& descriptor =
|
||||
header_->content_metadata.volume_descriptor.stfs;
|
||||
|
||||
|
|
|
@ -10,15 +10,12 @@
|
|||
#ifndef XENIA_VFS_DEVICES_XCONTENT_DEVICES_STFS_CONTAINER_DEVICE_H_
|
||||
#define XENIA_VFS_DEVICES_XCONTENT_DEVICES_STFS_CONTAINER_DEVICE_H_
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "xenia/base/string_util.h"
|
||||
#include "xenia/kernel/util/xex2_info.h"
|
||||
#include "xenia/vfs/device.h"
|
||||
#include "xenia/vfs/devices/stfs_xbox.h"
|
||||
#include "xenia/vfs/devices/xcontent_container_device.h"
|
||||
#include "xenia/vfs/devices/xcontent_container_entry.h"
|
||||
#include "xenia/vfs/devices/xcontent_devices/stfs_container_entry.h"
|
||||
|
||||
namespace xe {
|
||||
namespace vfs {
|
||||
|
@ -61,12 +58,11 @@ class StfsContainerDevice : public XContentContainerDevice {
|
|||
kBlockSize / sizeof(StfsDirectoryEntry);
|
||||
|
||||
void SetupContainer() override;
|
||||
Result LoadHostFiles(FILE* header_file) override;
|
||||
Result LoadHostFiles() override;
|
||||
|
||||
Result Read() override;
|
||||
std::unique_ptr<XContentContainerEntry> ReadEntry(
|
||||
Entry* parent, MultiFileHandles* files,
|
||||
const StfsDirectoryEntry* dir_entry);
|
||||
std::unique_ptr<StfsContainerEntry> ReadEntry(
|
||||
Entry* parent, const StfsDirectoryEntry* dir_entry);
|
||||
|
||||
size_t BlockToOffset(uint64_t block_index) const;
|
||||
uint32_t BlockToHashBlockNumber(uint32_t block_index,
|
||||
|
@ -87,6 +83,7 @@ class StfsContainerDevice : public XContentContainerDevice {
|
|||
uint32_t block_step_[2];
|
||||
|
||||
std::unordered_map<size_t, StfsHashTable> cached_hash_tables_;
|
||||
std::unique_ptr<MappedMemory> data_;
|
||||
};
|
||||
|
||||
} // namespace vfs
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2025 Xenia Canary. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "xenia/vfs/devices/xcontent_devices/stfs_container_entry.h"
|
||||
#include "xenia/vfs/devices/xcontent_devices/stfs_container_file.h"
|
||||
|
||||
namespace xe {
|
||||
namespace vfs {
|
||||
|
||||
StfsContainerEntry::StfsContainerEntry(Device* device, Entry* parent,
|
||||
const std::string_view path,
|
||||
MappedMemory* data)
|
||||
: XContentContainerEntry(device, parent, path), data_(data) {}
|
||||
|
||||
StfsContainerEntry::~StfsContainerEntry() = default;
|
||||
|
||||
std::unique_ptr<StfsContainerEntry> StfsContainerEntry::Create(
|
||||
Device* device, Entry* parent, const std::string_view name,
|
||||
MappedMemory* data) {
|
||||
auto path = xe::utf8::join_guest_paths(parent->path(), name);
|
||||
auto entry = std::make_unique<StfsContainerEntry>(device, parent, path, data);
|
||||
|
||||
return std::move(entry);
|
||||
}
|
||||
|
||||
X_STATUS StfsContainerEntry::Open(uint32_t desired_access, File** out_file) {
|
||||
*out_file = new StfsContainerFile(desired_access, this);
|
||||
return X_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
bool StfsContainerEntry::DeleteEntryInternal(Entry* entry) { return false; }
|
||||
|
||||
} // namespace vfs
|
||||
} // namespace xe
|
|
@ -0,0 +1,42 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2025 Xenia Canary. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_VFS_DEVICES_XCONTENT_STFS_CONTAINER_ENTRY_H_
|
||||
#define XENIA_VFS_DEVICES_XCONTENT_STFS_CONTAINER_ENTRY_H_
|
||||
|
||||
#include "xenia/vfs/devices/xcontent_container_entry.h"
|
||||
|
||||
namespace xe {
|
||||
namespace vfs {
|
||||
|
||||
class StfsContainerEntry : public XContentContainerEntry {
|
||||
public:
|
||||
StfsContainerEntry(Device* device, Entry* parent, const std::string_view path,
|
||||
MappedMemory* data);
|
||||
~StfsContainerEntry() override;
|
||||
|
||||
static std::unique_ptr<StfsContainerEntry> Create(Device* device,
|
||||
Entry* parent,
|
||||
const std::string_view name,
|
||||
MappedMemory* data);
|
||||
|
||||
MappedMemory* data() const { return data_; }
|
||||
|
||||
X_STATUS Open(uint32_t desired_access, File** out_file) override;
|
||||
|
||||
private:
|
||||
bool DeleteEntryInternal(Entry* entry) override;
|
||||
|
||||
MappedMemory* data_;
|
||||
};
|
||||
|
||||
} // namespace vfs
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_VFS_DEVICES_XCONTENT_CONTAINER_ENTRY_H_
|
|
@ -0,0 +1,31 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2025 Xenia Canary. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "xenia/vfs/devices/xcontent_devices/stfs_container_file.h"
|
||||
#include "xenia/vfs/devices/xcontent_devices/stfs_container_entry.h"
|
||||
|
||||
namespace xe {
|
||||
namespace vfs {
|
||||
|
||||
StfsContainerFile::StfsContainerFile(uint32_t file_access,
|
||||
StfsContainerEntry* entry)
|
||||
: XContentContainerFile(file_access, entry), entry_(entry) {}
|
||||
|
||||
StfsContainerFile::~StfsContainerFile() = default;
|
||||
|
||||
void StfsContainerFile::Destroy() { delete this; }
|
||||
|
||||
size_t StfsContainerFile::Read(std::span<uint8_t> buffer, size_t offset,
|
||||
size_t record_file) {
|
||||
std::memcpy(buffer.data(), entry_->data()->data() + offset, buffer.size());
|
||||
return buffer.size();
|
||||
}
|
||||
|
||||
} // namespace vfs
|
||||
} // namespace xe
|
|
@ -0,0 +1,48 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2025 Xenia Canary. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_VFS_DEVICES_XCONTENT_STFS_CONTAINER_FILE_H_
|
||||
#define XENIA_VFS_DEVICES_XCONTENT_STFS_CONTAINER_FILE_H_
|
||||
|
||||
#include <span>
|
||||
|
||||
#include "xenia/vfs/devices/xcontent_container_file.h"
|
||||
#include "xenia/vfs/file.h"
|
||||
|
||||
#include "xenia/xbox.h"
|
||||
|
||||
namespace xe {
|
||||
namespace vfs {
|
||||
|
||||
class StfsContainerEntry;
|
||||
|
||||
class StfsContainerFile : public XContentContainerFile {
|
||||
public:
|
||||
StfsContainerFile(uint32_t file_access, StfsContainerEntry* entry);
|
||||
~StfsContainerFile() override;
|
||||
|
||||
void Destroy() override;
|
||||
|
||||
X_STATUS WriteSync(std::span<const uint8_t> buffer, size_t byte_offset,
|
||||
size_t* out_bytes_written) override {
|
||||
return X_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
X_STATUS SetLength(size_t length) override { return X_STATUS_ACCESS_DENIED; }
|
||||
|
||||
private:
|
||||
size_t Read(std::span<uint8_t> buffer, size_t offset,
|
||||
size_t record_file) override;
|
||||
|
||||
StfsContainerEntry* entry_;
|
||||
};
|
||||
|
||||
} // namespace vfs
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_VFS_DEVICES_XCONTENT_CONTAINER_FILE_H_
|
|
@ -7,13 +7,9 @@
|
|||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/vfs/devices/xcontent_container_device.h"
|
||||
#include "xenia/vfs/devices/xcontent_container_entry.h"
|
||||
#include "xenia/vfs/devices/xcontent_devices/svod_container_device.h"
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/vfs/devices/xcontent_devices/svod_container_entry.h"
|
||||
|
||||
namespace xe {
|
||||
namespace vfs {
|
||||
|
@ -26,10 +22,15 @@ SvodContainerDevice::SvodContainerDevice(const std::string_view mount_path,
|
|||
SetName("FATX");
|
||||
}
|
||||
|
||||
SvodContainerDevice::~SvodContainerDevice() { CloseFiles(); }
|
||||
SvodContainerDevice::~SvodContainerDevice() {
|
||||
for (auto& file : files_) {
|
||||
fclose(file.second);
|
||||
}
|
||||
files_.clear();
|
||||
files_total_size_ = 0;
|
||||
}
|
||||
|
||||
SvodContainerDevice::Result SvodContainerDevice::LoadHostFiles(
|
||||
FILE* header_file) {
|
||||
SvodContainerDevice::Result SvodContainerDevice::LoadHostFiles() {
|
||||
std::filesystem::path data_fragment_path = host_path_;
|
||||
data_fragment_path += ".data";
|
||||
if (!std::filesystem::exists(data_fragment_path)) {
|
||||
|
@ -58,7 +59,7 @@ SvodContainerDevice::Result SvodContainerDevice::LoadHostFiles(
|
|||
auto file = xe::filesystem::OpenFile(path, "rb");
|
||||
if (!file) {
|
||||
XELOGI("Failed to map SVOD file {}.", path);
|
||||
CloseFiles();
|
||||
// CloseFiles();
|
||||
return Result::kReadError;
|
||||
}
|
||||
|
||||
|
@ -100,7 +101,7 @@ XContentContainerDevice::Result SvodContainerDevice::Read() {
|
|||
const uint64_t root_creation_timestamp =
|
||||
decode_fat_timestamp(root_data.creation_date, root_data.creation_time);
|
||||
|
||||
auto root_entry = new XContentContainerEntry(this, nullptr, "", &files_);
|
||||
auto root_entry = new SvodContainerEntry(this, nullptr, "", &files_);
|
||||
root_entry->attributes_ = kFileAttributeDirectory;
|
||||
root_entry->access_timestamp_ = root_creation_timestamp;
|
||||
root_entry->create_timestamp_ = root_creation_timestamp;
|
||||
|
@ -112,7 +113,7 @@ XContentContainerDevice::Result SvodContainerDevice::Read() {
|
|||
}
|
||||
|
||||
SvodContainerDevice::Result SvodContainerDevice::ReadEntry(
|
||||
uint32_t block, uint32_t ordinal, XContentContainerEntry* parent) {
|
||||
uint32_t block, uint32_t ordinal, SvodContainerEntry* parent) {
|
||||
// For games with a large amount of files, the ordinal offset can overrun
|
||||
// the current block and potentially hit a hash block.
|
||||
size_t ordinal_offset = ordinal * 0x4;
|
||||
|
@ -184,7 +185,7 @@ SvodContainerDevice::Result SvodContainerDevice::ReadEntry(
|
|||
// NOTE: SVOD entries don't have timestamps for individual files, which can
|
||||
// cause issues when decrypting games. Using the root entry's timestamp
|
||||
// solves this issues.
|
||||
auto entry = XContentContainerEntry::Create(this, parent, name, &files_);
|
||||
auto entry = SvodContainerEntry::Create(this, parent, name, &files_);
|
||||
if (dir_entry.attributes & kFileAttributeDirectory) {
|
||||
// Entry is a directory
|
||||
entry->attributes_ = kFileAttributeDirectory | kFileAttributeReadOnly;
|
||||
|
|
|
@ -11,19 +11,14 @@
|
|||
#define XENIA_VFS_DEVICES_XCONTENT_DEVICES_SVOD_CONTAINER_DEVICE_H_
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "xenia/base/string_util.h"
|
||||
#include "xenia/kernel/util/xex2_info.h"
|
||||
#include "xenia/vfs/device.h"
|
||||
#include "xenia/vfs/devices/stfs_xbox.h"
|
||||
#include "xenia/vfs/devices/xcontent_container_device.h"
|
||||
#include "xenia/vfs/devices/xcontent_container_entry.h"
|
||||
#include "xenia/vfs/devices/xcontent_devices/svod_container_entry.h"
|
||||
|
||||
namespace xe {
|
||||
namespace vfs {
|
||||
|
||||
class SvodContainerDevice : public XContentContainerDevice {
|
||||
public:
|
||||
SvodContainerDevice(const std::string_view mount_path,
|
||||
|
@ -49,11 +44,11 @@ class SvodContainerDevice : public XContentContainerDevice {
|
|||
};
|
||||
const char* MEDIA_MAGIC = "MICROSOFT*XBOX*MEDIA";
|
||||
|
||||
Result LoadHostFiles(FILE* header_file) override;
|
||||
Result LoadHostFiles() override;
|
||||
|
||||
Result Read() override;
|
||||
Result ReadEntry(uint32_t sector, uint32_t ordinal,
|
||||
XContentContainerEntry* parent);
|
||||
SvodContainerEntry* parent);
|
||||
void BlockToOffset(size_t sector, size_t* address, size_t* file_index) const;
|
||||
|
||||
Result SetLayout(FILE* header, size_t& magic_offset);
|
||||
|
@ -69,6 +64,7 @@ class SvodContainerDevice : public XContentContainerDevice {
|
|||
|
||||
size_t svod_base_offset_;
|
||||
SvodLayoutType svod_layout_;
|
||||
MultiFileHandles files_;
|
||||
};
|
||||
|
||||
} // namespace vfs
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2025 Xenia Canary. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "xenia/vfs/devices/xcontent_devices/svod_container_entry.h"
|
||||
#include "xenia/vfs/devices/xcontent_devices/svod_container_file.h"
|
||||
|
||||
namespace xe {
|
||||
namespace vfs {
|
||||
|
||||
SvodContainerEntry::SvodContainerEntry(Device* device, Entry* parent,
|
||||
const std::string_view path,
|
||||
MultiFileHandles* files)
|
||||
: XContentContainerEntry(device, parent, path), files_(files) {}
|
||||
|
||||
SvodContainerEntry::~SvodContainerEntry() = default;
|
||||
|
||||
std::unique_ptr<SvodContainerEntry> SvodContainerEntry::Create(
|
||||
Device* device, Entry* parent, const std::string_view name,
|
||||
MultiFileHandles* files) {
|
||||
auto path = xe::utf8::join_guest_paths(parent->path(), name);
|
||||
auto entry =
|
||||
std::make_unique<SvodContainerEntry>(device, parent, path, files);
|
||||
|
||||
return std::move(entry);
|
||||
}
|
||||
|
||||
X_STATUS SvodContainerEntry::Open(uint32_t desired_access, File** out_file) {
|
||||
*out_file = new SvodContainerFile(desired_access, this);
|
||||
return X_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
bool SvodContainerEntry::DeleteEntryInternal(Entry* entry) { return false; }
|
||||
|
||||
} // namespace vfs
|
||||
} // namespace xe
|
|
@ -0,0 +1,48 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2025 Xenia Canary. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_VFS_DEVICES_XCONTENT_SVOD_CONTAINER_ENTRY_H_
|
||||
#define XENIA_VFS_DEVICES_XCONTENT_SVOD_CONTAINER_ENTRY_H_
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "xenia/vfs/devices/xcontent_container_entry.h"
|
||||
#include "xenia/vfs/file.h"
|
||||
|
||||
namespace xe {
|
||||
namespace vfs {
|
||||
typedef std::map<size_t, FILE*> MultiFileHandles;
|
||||
|
||||
class XContentContainerDevice;
|
||||
|
||||
class SvodContainerEntry : public XContentContainerEntry {
|
||||
public:
|
||||
SvodContainerEntry(Device* device, Entry* parent, const std::string_view path,
|
||||
MultiFileHandles* files);
|
||||
~SvodContainerEntry() override;
|
||||
|
||||
static std::unique_ptr<SvodContainerEntry> Create(Device* device,
|
||||
Entry* parent,
|
||||
const std::string_view name,
|
||||
MultiFileHandles* files);
|
||||
|
||||
MultiFileHandles* files() const { return files_; }
|
||||
|
||||
X_STATUS Open(uint32_t desired_access, File** out_file) override;
|
||||
|
||||
private:
|
||||
bool DeleteEntryInternal(Entry* entry) override;
|
||||
|
||||
MultiFileHandles* files_;
|
||||
};
|
||||
|
||||
} // namespace vfs
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_VFS_DEVICES_XCONTENT_SVOD_CONTAINER_ENTRY_H_
|
|
@ -0,0 +1,32 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2025 Xenia Canary. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "xenia/vfs/devices/xcontent_devices/svod_container_file.h"
|
||||
#include "xenia/vfs/devices/xcontent_devices/svod_container_entry.h"
|
||||
|
||||
namespace xe {
|
||||
namespace vfs {
|
||||
|
||||
SvodContainerFile::SvodContainerFile(uint32_t file_access,
|
||||
SvodContainerEntry* entry)
|
||||
: XContentContainerFile(file_access, entry), entry_(entry) {}
|
||||
|
||||
SvodContainerFile::~SvodContainerFile() = default;
|
||||
|
||||
void SvodContainerFile::Destroy() { delete this; }
|
||||
|
||||
size_t SvodContainerFile::Read(std::span<uint8_t> buffer, size_t offset,
|
||||
size_t record_file) {
|
||||
auto& file = entry_->files()->at(record_file);
|
||||
xe::filesystem::Seek(file, offset, SEEK_SET);
|
||||
return fread(buffer.data(), 1, buffer.size(), file);
|
||||
}
|
||||
|
||||
} // namespace vfs
|
||||
} // namespace xe
|
|
@ -0,0 +1,43 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2025 Xenia Canary. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_VFS_DEVICES_XCONTENT_SVOD_CONTAINER_FILE_H_
|
||||
#define XENIA_VFS_DEVICES_XCONTENT_SVOD_CONTAINER_FILE_H_
|
||||
|
||||
#include "xenia/vfs/devices/xcontent_container_file.h"
|
||||
|
||||
namespace xe {
|
||||
namespace vfs {
|
||||
|
||||
class SvodContainerEntry;
|
||||
|
||||
class SvodContainerFile : public XContentContainerFile {
|
||||
public:
|
||||
SvodContainerFile(uint32_t file_access, SvodContainerEntry* entry);
|
||||
~SvodContainerFile() override;
|
||||
|
||||
void Destroy() override;
|
||||
|
||||
X_STATUS WriteSync(std::span<const uint8_t> buffer, size_t byte_offset,
|
||||
size_t* out_bytes_written) override {
|
||||
return X_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
X_STATUS SetLength(size_t length) override { return X_STATUS_ACCESS_DENIED; }
|
||||
|
||||
private:
|
||||
size_t Read(std::span<uint8_t> buffer, size_t offset,
|
||||
size_t record_file) override;
|
||||
|
||||
SvodContainerEntry* entry_;
|
||||
};
|
||||
|
||||
} // namespace vfs
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_VFS_DEVICES_XCONTENT_SVOD_CONTAINER_FILE_H_
|
|
@ -11,6 +11,7 @@
|
|||
#define XENIA_VFS_FILE_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <span>
|
||||
|
||||
#include "xenia/xbox.h"
|
||||
|
||||
|
@ -27,20 +28,20 @@ class File {
|
|||
|
||||
virtual void Destroy() = 0;
|
||||
|
||||
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,
|
||||
virtual X_STATUS ReadSync(std::span<uint8_t> buffer, size_t byte_offset,
|
||||
size_t* out_bytes_read) = 0;
|
||||
virtual X_STATUS WriteSync(std::span<const uint8_t> buffer,
|
||||
size_t byte_offset, size_t* out_bytes_written) = 0;
|
||||
|
||||
// TODO: Parameters
|
||||
virtual X_STATUS ReadAsync(void* buffer, size_t buffer_length,
|
||||
size_t byte_offset, size_t* out_bytes_read) {
|
||||
virtual X_STATUS ReadAsync(std::span<uint8_t> buffer, size_t byte_offset,
|
||||
size_t* out_bytes_read) {
|
||||
return X_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
// TODO: Parameters
|
||||
virtual X_STATUS WriteAsync(const void* buffer, size_t buffer_length,
|
||||
size_t byte_offset, size_t* out_bytes_written) {
|
||||
virtual X_STATUS WriteAsync(std::span<uint8_t> buffer, size_t byte_offset,
|
||||
size_t* out_bytes_written) {
|
||||
return X_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
|
|
|
@ -391,7 +391,8 @@ X_STATUS VirtualFileSystem::ExtractContentFile(Entry* entry,
|
|||
|
||||
while (remaining_size > 0) {
|
||||
size_t bytes_read = 0;
|
||||
in_file->ReadSync(buffer, write_buffer_size, offset, &bytes_read);
|
||||
in_file->ReadSync(std::span<uint8_t>(buffer, write_buffer_size), offset,
|
||||
&bytes_read);
|
||||
fwrite(buffer, bytes_read, 1, file);
|
||||
offset += bytes_read;
|
||||
remaining_size -= bytes_read;
|
||||
|
|
Loading…
Reference in New Issue