[VFS] Device now exposes name, attributes, component name max length.
[VFS] Fix STFS device to return 0x200 sector size. XCTD compression
userland code appears to always expect a sector size of 0x200.
[Kernel] Move X_FILE_*_INFORMATION structs to new files.
[Kernel] Move NtQueryInformationFile, NtSetInformationFile,
NtQueryVolumeInformationFile to new file.
[Kernel] Cleanup implementation of NtQueryInformationFile,
NetSetInformationFile, NtQueryVolumeInformationFile.
[Kernel] Properly validate arguments to NtQueryInformationFile,
NetSetInformationFile, NtQueryVolumeInformationFile.
[Kernel] Properly implement query of XFileFsVolumeInformation.
[Kernel] Properly implement query of XFileFsSizeInformation.
[Kernel] Properly implement query of XFileFsAttributeInformation.
241 lines
6.3 KiB
C++
241 lines
6.3 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. *
|
|
******************************************************************************
|
|
*/
|
|
|
|
#ifndef XENIA_VFS_DEVICES_STFS_CONTAINER_DEVICE_H_
|
|
#define XENIA_VFS_DEVICES_STFS_CONTAINER_DEVICE_H_
|
|
|
|
#include <map>
|
|
#include <memory>
|
|
#include <string>
|
|
|
|
#include "xenia/base/mapped_memory.h"
|
|
#include "xenia/vfs/device.h"
|
|
|
|
namespace xe {
|
|
namespace vfs {
|
|
|
|
// https://free60project.github.io/wiki/STFS.html
|
|
|
|
class StfsContainerEntry;
|
|
|
|
enum class StfsPackageType {
|
|
kCon,
|
|
kPirs,
|
|
kLive,
|
|
};
|
|
|
|
enum class StfsContentType : uint32_t {
|
|
kArcadeTitle = 0x000D0000,
|
|
kAvatarItem = 0x00009000,
|
|
kCacheFile = 0x00040000,
|
|
kCommunityGame = 0x02000000,
|
|
kGamesOnDemand = 0x00007000,
|
|
kGameDemo = 0x00080000,
|
|
kGamerPicture = 0x00020000,
|
|
kGameTitle = 0x000A0000,
|
|
kGameTrailer = 0x000C0000,
|
|
kGameVideo = 0x00400000,
|
|
kInstalledGame = 0x00004000,
|
|
kInstaller = 0x000B0000,
|
|
kIptvPauseBuffer = 0x00002000,
|
|
kLicenseStore = 0x000F0000,
|
|
kMarketplaceContent = 0x00000002,
|
|
kMovie = 0x00100000,
|
|
kMusicVideo = 0x00300000,
|
|
kPodcastVideo = 0x00500000,
|
|
kProfile = 0x00010000,
|
|
kPublisher = 0x00000003,
|
|
kSavedGame = 0x00000001,
|
|
kStorageDownload = 0x00050000,
|
|
kTheme = 0x00030000,
|
|
kTV = 0x00200000,
|
|
kVideo = 0x00090000,
|
|
kViralVideo = 0x00600000,
|
|
kXboxDownload = 0x00070000,
|
|
kXboxOriginalGame = 0x00005000,
|
|
kXboxSavedGame = 0x00060000,
|
|
kXbox360Title = 0x00001000,
|
|
kXboxTitle = 0x00005000,
|
|
kXNA = 0x000E0000,
|
|
};
|
|
|
|
enum class StfsPlatform : uint8_t {
|
|
kXbox360 = 0x02,
|
|
kPc = 0x04,
|
|
};
|
|
|
|
enum class StfsDescriptorType : uint32_t {
|
|
kStfs = 0,
|
|
kSvod = 1,
|
|
};
|
|
|
|
struct StfsVolumeDescriptor {
|
|
bool Read(const uint8_t* p);
|
|
|
|
uint8_t descriptor_size;
|
|
uint8_t version;
|
|
uint8_t flags;
|
|
uint16_t file_table_block_count;
|
|
uint32_t file_table_block_number;
|
|
uint8_t top_hash_table_hash[0x14];
|
|
uint32_t total_allocated_block_count;
|
|
uint32_t total_unallocated_block_count;
|
|
};
|
|
|
|
enum SvodDeviceFeatures {
|
|
kFeatureHasEnhancedGDFLayout = 0x40,
|
|
};
|
|
|
|
enum SvodLayoutType {
|
|
kUnknownLayout = 0x0,
|
|
kEnhancedGDFLayout = 0x1,
|
|
kXSFLayout = 0x2,
|
|
kSingleFileLayout = 0x4,
|
|
};
|
|
|
|
struct SvodVolumeDescriptor {
|
|
bool Read(const uint8_t* p);
|
|
|
|
uint8_t descriptor_size;
|
|
uint8_t block_cache_element_count;
|
|
uint8_t worker_thread_processor;
|
|
uint8_t worker_thread_priority;
|
|
uint8_t hash[0x14];
|
|
uint8_t device_features;
|
|
uint32_t data_block_count;
|
|
uint32_t data_block_offset;
|
|
// 0x5 padding bytes...
|
|
|
|
SvodLayoutType layout_type;
|
|
};
|
|
|
|
class StfsHeader {
|
|
public:
|
|
bool Read(const uint8_t* p);
|
|
|
|
uint8_t license_entries[0x100];
|
|
uint8_t header_hash[0x14];
|
|
uint32_t header_size;
|
|
StfsContentType content_type;
|
|
uint32_t metadata_version;
|
|
uint64_t content_size;
|
|
uint32_t media_id;
|
|
uint32_t version;
|
|
uint32_t base_version;
|
|
uint32_t title_id;
|
|
StfsPlatform platform;
|
|
uint8_t executable_type;
|
|
uint8_t disc_number;
|
|
uint8_t disc_in_set;
|
|
uint32_t save_game_id;
|
|
uint8_t console_id[0x5];
|
|
uint8_t profile_id[0x8];
|
|
union {
|
|
StfsVolumeDescriptor stfs_volume_descriptor;
|
|
SvodVolumeDescriptor svod_volume_descriptor;
|
|
};
|
|
uint32_t data_file_count;
|
|
uint64_t data_file_combined_size;
|
|
StfsDescriptorType descriptor_type;
|
|
uint8_t device_id[0x14];
|
|
char16_t display_names[0x900 / 2];
|
|
char16_t display_descs[0x900 / 2];
|
|
char16_t publisher_name[0x80 / 2];
|
|
char16_t title_name[0x80 / 2];
|
|
uint8_t transfer_flags;
|
|
uint32_t thumbnail_image_size;
|
|
uint32_t title_thumbnail_image_size;
|
|
uint8_t thumbnail_image[0x4000];
|
|
uint8_t title_thumbnail_image[0x4000];
|
|
|
|
// Metadata v2 Fields
|
|
uint8_t series_id[0x10];
|
|
uint8_t season_id[0x10];
|
|
int16_t season_number;
|
|
int16_t episode_number;
|
|
char16_t additonal_display_names[0x300 / 2];
|
|
char16_t additional_display_descriptions[0x300 / 2];
|
|
};
|
|
|
|
class StfsContainerDevice : public Device {
|
|
public:
|
|
StfsContainerDevice(const std::string_view mount_path,
|
|
const std::filesystem::path& host_path);
|
|
~StfsContainerDevice() override;
|
|
|
|
bool Initialize() override;
|
|
void Dump(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; }
|
|
|
|
uint32_t total_allocation_units() const override {
|
|
return uint32_t(mmap_total_size_ / sectors_per_allocation_unit() /
|
|
bytes_per_sector());
|
|
}
|
|
uint32_t available_allocation_units() const override { return 0; }
|
|
uint32_t sectors_per_allocation_unit() const override { return 8; }
|
|
uint32_t bytes_per_sector() const override { return 0x200; }
|
|
|
|
private:
|
|
const uint32_t kSectorSize = 0x1000;
|
|
|
|
enum class Error {
|
|
kSuccess = 0,
|
|
kErrorOutOfMemory = -1,
|
|
kErrorReadError = -10,
|
|
kErrorFileMismatch = -30,
|
|
kErrorDamagedFile = -31,
|
|
};
|
|
|
|
struct BlockHash {
|
|
uint32_t next_block_index;
|
|
uint32_t info;
|
|
};
|
|
|
|
const uint32_t kSTFSHashSpacing = 170;
|
|
|
|
bool ResolveFromFolder(const std::filesystem::path& path);
|
|
|
|
Error MapFiles();
|
|
static Error ReadPackageType(const uint8_t* map_ptr, size_t map_size,
|
|
StfsPackageType* package_type_out);
|
|
Error ReadHeaderAndVerify(const uint8_t* map_ptr, size_t map_size);
|
|
|
|
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 BlockToOffsetSTFS(uint64_t block);
|
|
|
|
BlockHash GetBlockHash(const uint8_t* map_ptr, uint32_t block_index,
|
|
uint32_t table_offset);
|
|
|
|
std::string name_;
|
|
std::filesystem::path host_path_;
|
|
std::map<size_t, std::unique_ptr<MappedMemory>> mmap_;
|
|
size_t mmap_total_size_;
|
|
|
|
size_t base_offset_;
|
|
size_t magic_offset_;
|
|
std::unique_ptr<Entry> root_entry_;
|
|
StfsPackageType package_type_;
|
|
StfsHeader header_;
|
|
uint32_t table_size_shift_;
|
|
};
|
|
|
|
} // namespace vfs
|
|
} // namespace xe
|
|
|
|
#endif // XENIA_VFS_DEVICES_STFS_CONTAINER_DEVICE_H_
|