143 lines
4.6 KiB
C++
143 lines
4.6 KiB
C++
/**
|
|
******************************************************************************
|
|
* Xenia : Xbox 360 Emulator Research Project *
|
|
******************************************************************************
|
|
* Copyright 2020 Ben Vanik. All rights reserved. *
|
|
* Released under the BSD license - see LICENSE in the root for more details. *
|
|
******************************************************************************
|
|
*
|
|
* @modified Tom Clay, 2026 - Adapted for ReXGlue runtime
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <rex/filesystem/devices/stfs_xbox.h>
|
|
|
|
#include <map>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <unordered_map>
|
|
|
|
#include <rex/filesystem/device.h>
|
|
#include <rex/math.h>
|
|
#include <rex/string/util.h>
|
|
|
|
namespace rex::filesystem {
|
|
|
|
// https://free60project.github.io/wiki/STFS.html
|
|
|
|
class StfsContainerEntry;
|
|
|
|
class StfsContainerDevice : public Device {
|
|
public:
|
|
const static uint32_t kBlockSize = 0x1000;
|
|
|
|
StfsContainerDevice(const std::string_view mount_path, const std::filesystem::path& host_path);
|
|
~StfsContainerDevice() override;
|
|
|
|
bool Initialize() override;
|
|
|
|
bool is_read_only() const override {
|
|
return header_.metadata.volume_type != XContentVolumeType::kStfs ||
|
|
header_.metadata.volume_descriptor.stfs.flags.bits.read_only_format;
|
|
}
|
|
|
|
void Dump(string::StringBuffer* string_buffer) override;
|
|
Entry* ResolvePath(const std::string_view path) override;
|
|
|
|
const std::string& name() const override { return name_; }
|
|
uint32_t attributes() const override { return 0; }
|
|
uint32_t component_name_max_length() const override { return 40; }
|
|
const StfsHeader& header() const { return header_; }
|
|
|
|
// Reads and validates the StfsHeader from an STFS package file without
|
|
// mounting the device. Returns nullptr if the file is missing, too small,
|
|
// or has an invalid magic.
|
|
static std::unique_ptr<StfsHeader> ReadPackageHeader(const std::filesystem::path& file_path);
|
|
|
|
uint32_t total_allocation_units() const override {
|
|
if (header_.metadata.volume_type == XContentVolumeType::kStfs) {
|
|
return header_.metadata.volume_descriptor.stfs.total_block_count;
|
|
}
|
|
|
|
return uint32_t(data_size() / sectors_per_allocation_unit() / bytes_per_sector());
|
|
}
|
|
uint32_t available_allocation_units() const override {
|
|
if (!is_read_only()) {
|
|
auto& descriptor = header_.metadata.volume_descriptor.stfs;
|
|
return kBlocksPerHashLevel[2] - (descriptor.total_block_count - descriptor.free_block_count);
|
|
}
|
|
return 0;
|
|
}
|
|
uint32_t sectors_per_allocation_unit() const override { return 8; }
|
|
uint32_t bytes_per_sector() const override { return 0x200; }
|
|
|
|
size_t data_size() const {
|
|
if (header_.header.header_size) {
|
|
if (header_.metadata.volume_type == XContentVolumeType::kStfs) {
|
|
return header_.metadata.volume_descriptor.stfs.total_block_count * kBlockSize;
|
|
}
|
|
return files_total_size_ - StfsBaseOffset();
|
|
}
|
|
return files_total_size_ - sizeof(StfsHeader);
|
|
}
|
|
|
|
private:
|
|
const uint32_t kBlocksPerHashLevel[3] = {170, 28900, 4913000};
|
|
const uint32_t kEndOfChain = 0xFFFFFF;
|
|
const uint32_t kEntriesPerDirectoryBlock = kBlockSize / sizeof(StfsDirectoryEntry);
|
|
|
|
enum class Error {
|
|
kSuccess = 0,
|
|
kErrorOutOfMemory = -1,
|
|
kErrorReadError = -10,
|
|
kErrorFileMismatch = -30,
|
|
kErrorDamagedFile = -31,
|
|
kErrorTooSmall = -32,
|
|
};
|
|
|
|
enum class SvodLayoutType {
|
|
kUnknown = 0x0,
|
|
kEnhancedGDF = 0x1,
|
|
kXSF = 0x2,
|
|
kSingleFile = 0x4,
|
|
};
|
|
|
|
XContentPackageType ReadMagic(const std::filesystem::path& path);
|
|
bool ResolveFromFolder(const std::filesystem::path& path);
|
|
|
|
Error OpenFiles();
|
|
void CloseFiles();
|
|
|
|
Error ReadHeaderAndVerify(FILE* header_file);
|
|
|
|
Error ReadSVOD();
|
|
Error ReadEntrySVOD(uint32_t sector, uint32_t ordinal, StfsContainerEntry* parent);
|
|
void BlockToOffsetSVOD(size_t sector, size_t* address, size_t* file_index);
|
|
|
|
Error ReadSTFS();
|
|
size_t StfsBaseOffset() const;
|
|
size_t BlockToOffsetSTFS(uint64_t block_index) const;
|
|
uint32_t BlockToHashBlockNumberSTFS(uint32_t block_index, uint32_t hash_level) const;
|
|
size_t BlockToHashBlockOffsetSTFS(uint32_t block_index, uint32_t hash_level) const;
|
|
|
|
const StfsHashEntry* GetBlockHash(uint32_t block_index);
|
|
|
|
std::string name_;
|
|
std::filesystem::path host_path_;
|
|
|
|
std::map<size_t, FILE*> files_;
|
|
size_t files_total_size_;
|
|
|
|
size_t svod_base_offset_;
|
|
|
|
std::unique_ptr<Entry> root_entry_;
|
|
StfsHeader header_;
|
|
SvodLayoutType svod_layout_;
|
|
uint32_t blocks_per_hash_table_;
|
|
uint32_t block_step[2];
|
|
|
|
std::unordered_map<size_t, StfsHashTable> cached_hash_tables_;
|
|
};
|
|
|
|
} // namespace rex::filesystem
|