diff options
Diffstat (limited to '')
| -rw-r--r-- | src/core/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/core/file_sys/partition_filesystem.cpp | 125 | ||||
| -rw-r--r-- | src/core/file_sys/partition_filesystem.h | 87 |
3 files changed, 214 insertions, 0 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 9877b83fe..c1a645460 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -12,6 +12,8 @@ add_library(core STATIC | |||
| 12 | file_sys/errors.h | 12 | file_sys/errors.h |
| 13 | file_sys/filesystem.cpp | 13 | file_sys/filesystem.cpp |
| 14 | file_sys/filesystem.h | 14 | file_sys/filesystem.h |
| 15 | file_sys/partition_filesystem.cpp | ||
| 16 | file_sys/partition_filesystem.h | ||
| 15 | file_sys/path_parser.cpp | 17 | file_sys/path_parser.cpp |
| 16 | file_sys/path_parser.h | 18 | file_sys/path_parser.h |
| 17 | file_sys/program_metadata.cpp | 19 | file_sys/program_metadata.cpp |
diff --git a/src/core/file_sys/partition_filesystem.cpp b/src/core/file_sys/partition_filesystem.cpp new file mode 100644 index 000000000..4a58a9291 --- /dev/null +++ b/src/core/file_sys/partition_filesystem.cpp | |||
| @@ -0,0 +1,125 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <cinttypes> | ||
| 6 | #include <utility> | ||
| 7 | #include "common/file_util.h" | ||
| 8 | #include "common/logging/log.h" | ||
| 9 | #include "core/file_sys/partition_filesystem.h" | ||
| 10 | #include "core/loader/loader.h" | ||
| 11 | |||
| 12 | namespace FileSys { | ||
| 13 | |||
| 14 | Loader::ResultStatus PartitionFilesystem::Load(const std::string& file_path, size_t offset) { | ||
| 15 | FileUtil::IOFile file(file_path, "rb"); | ||
| 16 | if (!file.IsOpen()) | ||
| 17 | return Loader::ResultStatus::Error; | ||
| 18 | |||
| 19 | // At least be as large as the header | ||
| 20 | if (file.GetSize() < sizeof(Header)) | ||
| 21 | return Loader::ResultStatus::Error; | ||
| 22 | |||
| 23 | // For cartridges, HFSs can get very large, so we need to calculate the size up to | ||
| 24 | // the actual content itself instead of just blindly reading in the entire file. | ||
| 25 | Header pfs_header; | ||
| 26 | if (!file.ReadBytes(&pfs_header, sizeof(Header))) | ||
| 27 | return Loader::ResultStatus::Error; | ||
| 28 | |||
| 29 | bool is_hfs = (memcmp(pfs_header.magic.data(), "HFS", 3) == 0); | ||
| 30 | size_t entry_size = is_hfs ? sizeof(HFSEntry) : sizeof(PFSEntry); | ||
| 31 | size_t metadata_size = | ||
| 32 | sizeof(Header) + (pfs_header.num_entries * entry_size) + pfs_header.strtab_size; | ||
| 33 | |||
| 34 | // Actually read in now... | ||
| 35 | file.Seek(offset, SEEK_SET); | ||
| 36 | std::vector<u8> file_data(metadata_size); | ||
| 37 | |||
| 38 | if (!file.ReadBytes(file_data.data(), metadata_size)) | ||
| 39 | return Loader::ResultStatus::Error; | ||
| 40 | |||
| 41 | Loader::ResultStatus result = Load(file_data); | ||
| 42 | if (result != Loader::ResultStatus::Success) | ||
| 43 | LOG_ERROR(Service_FS, "Failed to load PFS from file %s!", file_path.c_str()); | ||
| 44 | |||
| 45 | return result; | ||
| 46 | } | ||
| 47 | |||
| 48 | Loader::ResultStatus PartitionFilesystem::Load(const std::vector<u8>& file_data, size_t offset) { | ||
| 49 | size_t total_size = file_data.size() - offset; | ||
| 50 | if (total_size < sizeof(Header)) | ||
| 51 | return Loader::ResultStatus::Error; | ||
| 52 | |||
| 53 | memcpy(&pfs_header, &file_data[offset], sizeof(Header)); | ||
| 54 | is_hfs = (memcmp(pfs_header.magic.data(), "HFS", 3) == 0); | ||
| 55 | |||
| 56 | size_t entries_offset = offset + sizeof(Header); | ||
| 57 | size_t entry_size = is_hfs ? sizeof(HFSEntry) : sizeof(PFSEntry); | ||
| 58 | size_t strtab_offset = entries_offset + (pfs_header.num_entries * entry_size); | ||
| 59 | for (u16 i = 0; i < pfs_header.num_entries; i++) { | ||
| 60 | FileEntry entry; | ||
| 61 | |||
| 62 | memcpy(&entry.fs_entry, &file_data[entries_offset + (i * entry_size)], sizeof(FSEntry)); | ||
| 63 | entry.name = std::string(reinterpret_cast<const char*>( | ||
| 64 | &file_data[strtab_offset + entry.fs_entry.strtab_offset])); | ||
| 65 | pfs_entries.push_back(std::move(entry)); | ||
| 66 | } | ||
| 67 | |||
| 68 | content_offset = strtab_offset + pfs_header.strtab_size; | ||
| 69 | |||
| 70 | return Loader::ResultStatus::Success; | ||
| 71 | } | ||
| 72 | |||
| 73 | u32 PartitionFilesystem::GetNumEntries() const { | ||
| 74 | return pfs_header.num_entries; | ||
| 75 | } | ||
| 76 | |||
| 77 | u64 PartitionFilesystem::GetEntryOffset(int index) const { | ||
| 78 | if (index > GetNumEntries()) | ||
| 79 | return 0; | ||
| 80 | |||
| 81 | return content_offset + pfs_entries[index].fs_entry.offset; | ||
| 82 | } | ||
| 83 | |||
| 84 | u64 PartitionFilesystem::GetEntrySize(int index) const { | ||
| 85 | if (index > GetNumEntries()) | ||
| 86 | return 0; | ||
| 87 | |||
| 88 | return pfs_entries[index].fs_entry.size; | ||
| 89 | } | ||
| 90 | |||
| 91 | std::string PartitionFilesystem::GetEntryName(int index) const { | ||
| 92 | if (index > GetNumEntries()) | ||
| 93 | return ""; | ||
| 94 | |||
| 95 | return pfs_entries[index].name; | ||
| 96 | } | ||
| 97 | |||
| 98 | u64 PartitionFilesystem::GetFileOffset(const std::string& name) const { | ||
| 99 | for (u32 i = 0; i < pfs_header.num_entries; i++) { | ||
| 100 | if (pfs_entries[i].name == name) | ||
| 101 | return content_offset + pfs_entries[i].fs_entry.offset; | ||
| 102 | } | ||
| 103 | |||
| 104 | return 0; | ||
| 105 | } | ||
| 106 | |||
| 107 | u64 PartitionFilesystem::GetFileSize(const std::string& name) const { | ||
| 108 | for (u32 i = 0; i < pfs_header.num_entries; i++) { | ||
| 109 | if (pfs_entries[i].name == name) | ||
| 110 | return pfs_entries[i].fs_entry.size; | ||
| 111 | } | ||
| 112 | |||
| 113 | return 0; | ||
| 114 | } | ||
| 115 | |||
| 116 | void PartitionFilesystem::Print() const { | ||
| 117 | NGLOG_DEBUG(Service_FS, "Magic: {:.4}", pfs_header.magic.data()); | ||
| 118 | NGLOG_DEBUG(Service_FS, "Files: {}", pfs_header.num_entries); | ||
| 119 | for (u32 i = 0; i < pfs_header.num_entries; i++) { | ||
| 120 | NGLOG_DEBUG(Service_FS, " > File {}: {} (0x{:X} bytes, at 0x{:X})", i, | ||
| 121 | pfs_entries[i].name.c_str(), pfs_entries[i].fs_entry.size, | ||
| 122 | GetFileOffset(pfs_entries[i].name)); | ||
| 123 | } | ||
| 124 | } | ||
| 125 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/partition_filesystem.h b/src/core/file_sys/partition_filesystem.h new file mode 100644 index 000000000..573c90057 --- /dev/null +++ b/src/core/file_sys/partition_filesystem.h | |||
| @@ -0,0 +1,87 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <array> | ||
| 8 | #include <string> | ||
| 9 | #include <vector> | ||
| 10 | #include "common/common_funcs.h" | ||
| 11 | #include "common/common_types.h" | ||
| 12 | #include "common/swap.h" | ||
| 13 | |||
| 14 | namespace Loader { | ||
| 15 | enum class ResultStatus; | ||
| 16 | } | ||
| 17 | |||
| 18 | namespace FileSys { | ||
| 19 | |||
| 20 | /** | ||
| 21 | * Helper which implements an interface to parse PFS/HFS filesystems. | ||
| 22 | * Data can either be loaded from a file path or data with an offset into it. | ||
| 23 | */ | ||
| 24 | class PartitionFilesystem { | ||
| 25 | public: | ||
| 26 | Loader::ResultStatus Load(const std::string& file_path, size_t offset = 0); | ||
| 27 | Loader::ResultStatus Load(const std::vector<u8>& file_data, size_t offset = 0); | ||
| 28 | |||
| 29 | u32 GetNumEntries() const; | ||
| 30 | u64 GetEntryOffset(int index) const; | ||
| 31 | u64 GetEntrySize(int index) const; | ||
| 32 | std::string GetEntryName(int index) const; | ||
| 33 | u64 GetFileOffset(const std::string& name) const; | ||
| 34 | u64 GetFileSize(const std::string& name) const; | ||
| 35 | |||
| 36 | void Print() const; | ||
| 37 | |||
| 38 | private: | ||
| 39 | struct Header { | ||
| 40 | std::array<char, 4> magic; | ||
| 41 | u32_le num_entries; | ||
| 42 | u32_le strtab_size; | ||
| 43 | INSERT_PADDING_BYTES(0x4); | ||
| 44 | }; | ||
| 45 | |||
| 46 | static_assert(sizeof(Header) == 0x10, "PFS/HFS header structure size is wrong"); | ||
| 47 | |||
| 48 | #pragma pack(push, 1) | ||
| 49 | struct FSEntry { | ||
| 50 | u64_le offset; | ||
| 51 | u64_le size; | ||
| 52 | u32_le strtab_offset; | ||
| 53 | }; | ||
| 54 | |||
| 55 | static_assert(sizeof(FSEntry) == 0x14, "FS entry structure size is wrong"); | ||
| 56 | |||
| 57 | struct PFSEntry { | ||
| 58 | FSEntry fs_entry; | ||
| 59 | INSERT_PADDING_BYTES(0x4); | ||
| 60 | }; | ||
| 61 | |||
| 62 | static_assert(sizeof(PFSEntry) == 0x18, "PFS entry structure size is wrong"); | ||
| 63 | |||
| 64 | struct HFSEntry { | ||
| 65 | FSEntry fs_entry; | ||
| 66 | u32_le hash_region_size; | ||
| 67 | INSERT_PADDING_BYTES(0x8); | ||
| 68 | std::array<char, 0x20> hash; | ||
| 69 | }; | ||
| 70 | |||
| 71 | static_assert(sizeof(HFSEntry) == 0x40, "HFS entry structure size is wrong"); | ||
| 72 | |||
| 73 | #pragma pack(pop) | ||
| 74 | |||
| 75 | struct FileEntry { | ||
| 76 | FSEntry fs_entry; | ||
| 77 | std::string name; | ||
| 78 | }; | ||
| 79 | |||
| 80 | Header pfs_header; | ||
| 81 | bool is_hfs; | ||
| 82 | size_t content_offset; | ||
| 83 | |||
| 84 | std::vector<FileEntry> pfs_entries; | ||
| 85 | }; | ||
| 86 | |||
| 87 | } // namespace FileSys | ||