3 changed files with 213 additions and 0 deletions
-
2src/core/CMakeLists.txt
-
124src/core/file_sys/partition_filesystem.cpp
-
87src/core/file_sys/partition_filesystem.h
@ -0,0 +1,124 @@ |
|||
// Copyright 2018 yuzu emulator team
|
|||
// Licensed under GPLv2 or any later version
|
|||
// Refer to the license.txt file included.
|
|||
|
|||
#include <cinttypes>
|
|||
#include "common/file_util.h"
|
|||
#include "common/logging/log.h"
|
|||
#include "core/file_sys/partition_filesystem.h"
|
|||
#include "core/loader/loader.h"
|
|||
|
|||
namespace FileSys { |
|||
|
|||
Loader::ResultStatus PartitionFilesystem::Load(const std::string& file_path, size_t offset) { |
|||
FileUtil::IOFile file(file_path, "rb"); |
|||
if (!file.IsOpen()) |
|||
return Loader::ResultStatus::Error; |
|||
|
|||
// At least be as large as the header
|
|||
if (file.GetSize() < sizeof(Header)) |
|||
return Loader::ResultStatus::Error; |
|||
|
|||
// For cartridges, HFSs can get very large, so we need to calculate the size up to
|
|||
// the actual content itself instead of just blindly reading in the entire file.
|
|||
Header pfs_header; |
|||
if (!file.ReadBytes(&pfs_header, sizeof(Header))) |
|||
return Loader::ResultStatus::Error; |
|||
|
|||
bool is_hfs = (memcmp(pfs_header.magic.data(), "HFS", 3) == 0); |
|||
size_t entry_size = is_hfs ? sizeof(HFSEntry) : sizeof(PFSEntry); |
|||
size_t metadata_size = |
|||
sizeof(Header) + (pfs_header.num_entries * entry_size) + pfs_header.strtab_size; |
|||
|
|||
// Actually read in now...
|
|||
file.Seek(offset, SEEK_SET); |
|||
std::vector<u8> file_data(metadata_size); |
|||
|
|||
if (!file.ReadBytes(file_data.data(), metadata_size)) |
|||
return Loader::ResultStatus::Error; |
|||
|
|||
Loader::ResultStatus result = Load(file_data); |
|||
if (result != Loader::ResultStatus::Success) |
|||
LOG_ERROR(Service_FS, "Failed to load PFS from file %s!", file_path.c_str()); |
|||
|
|||
return result; |
|||
} |
|||
|
|||
Loader::ResultStatus PartitionFilesystem::Load(const std::vector<u8> file_data, size_t offset) { |
|||
size_t total_size = static_cast<size_t>(file_data.size() - offset); |
|||
if (total_size < sizeof(Header)) |
|||
return Loader::ResultStatus::Error; |
|||
|
|||
memcpy(&pfs_header, &file_data[offset], sizeof(Header)); |
|||
is_hfs = (memcmp(pfs_header.magic.data(), "HFS", 3) == 0); |
|||
|
|||
size_t entries_offset = offset + sizeof(Header); |
|||
size_t entry_size = is_hfs ? sizeof(HFSEntry) : sizeof(PFSEntry); |
|||
size_t strtab_offset = entries_offset + (pfs_header.num_entries * entry_size); |
|||
for (u16 i = 0; i < pfs_header.num_entries; i++) { |
|||
FileEntry entry; |
|||
|
|||
memcpy(&entry.fs_entry, &file_data[entries_offset + (i * entry_size)], sizeof(FSEntry)); |
|||
entry.name = std::string(reinterpret_cast<const char*>( |
|||
&file_data[strtab_offset + entry.fs_entry.strtab_offset])); |
|||
pfs_entries.push_back(entry); |
|||
} |
|||
|
|||
content_offset = strtab_offset + pfs_header.strtab_size; |
|||
|
|||
return Loader::ResultStatus::Success; |
|||
} |
|||
|
|||
u32 PartitionFilesystem::GetNumEntries(void) const { |
|||
return pfs_header.num_entries; |
|||
} |
|||
|
|||
u64 PartitionFilesystem::GetEntryOffset(int index) const { |
|||
if (index > GetNumEntries()) |
|||
return 0; |
|||
|
|||
return content_offset + pfs_entries[index].fs_entry.offset; |
|||
} |
|||
|
|||
u64 PartitionFilesystem::GetEntrySize(int index) const { |
|||
if (index > GetNumEntries()) |
|||
return 0; |
|||
|
|||
return pfs_entries[index].fs_entry.size; |
|||
} |
|||
|
|||
std::string PartitionFilesystem::GetEntryName(int index) const { |
|||
if (index > GetNumEntries()) |
|||
return ""; |
|||
|
|||
return pfs_entries[index].name; |
|||
} |
|||
|
|||
u64 PartitionFilesystem::GetFileOffset(const std::string& name) const { |
|||
for (u32 i = 0; i < pfs_header.num_entries; i++) { |
|||
if (pfs_entries[i].name == name) |
|||
return content_offset + pfs_entries[i].fs_entry.offset; |
|||
} |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
u64 PartitionFilesystem::GetFileSize(const std::string& name) const { |
|||
for (u32 i = 0; i < pfs_header.num_entries; i++) { |
|||
if (pfs_entries[i].name == name) |
|||
return pfs_entries[i].fs_entry.size; |
|||
} |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
void PartitionFilesystem::Print() const { |
|||
LOG_DEBUG(Service_FS, "Magic: %.4s", pfs_header.magic.data()); |
|||
LOG_DEBUG(Service_FS, "Files: %u", pfs_header.num_entries); |
|||
for (u32 i = 0; i < pfs_header.num_entries; i++) { |
|||
LOG_DEBUG(Service_FS, " > File %u: %s (0x%" PRIX64 " bytes, at 0x%" PRIX64 ")", |
|||
i, pfs_entries[i].name.c_str(), pfs_entries[i].fs_entry.size, |
|||
GetFileOffset(pfs_entries[i].name)); |
|||
} |
|||
} |
|||
} // namespace FileSys
|
|||
@ -0,0 +1,87 @@ |
|||
// Copyright 2018 yuzu emulator team |
|||
// Licensed under GPLv2 or any later version |
|||
// Refer to the license.txt file included. |
|||
|
|||
#pragma once |
|||
|
|||
#include <array> |
|||
#include <string> |
|||
#include <vector> |
|||
#include "common/common_funcs.h" |
|||
#include "common/common_types.h" |
|||
#include "common/swap.h" |
|||
|
|||
namespace Loader { |
|||
enum class ResultStatus; |
|||
} |
|||
|
|||
namespace FileSys { |
|||
|
|||
/** |
|||
* Helper which implements an interface to parse PFS/HFS filesystems. |
|||
* Data can either be loaded from a file path or data with an offset into it. |
|||
*/ |
|||
class PartitionFilesystem { |
|||
public: |
|||
Loader::ResultStatus Load(const std::string& file_path, size_t offset = 0); |
|||
Loader::ResultStatus Load(const std::vector<u8> file_data, size_t offset = 0); |
|||
|
|||
u32 GetNumEntries(void) const; |
|||
u64 GetEntryOffset(int index) const; |
|||
u64 GetEntrySize(int index) const; |
|||
std::string GetEntryName(int index) const; |
|||
u64 GetFileOffset(const std::string& name) const; |
|||
u64 GetFileSize(const std::string& name) const; |
|||
|
|||
void Print() const; |
|||
|
|||
private: |
|||
struct Header { |
|||
std::array<char, 4> magic; |
|||
u32_le num_entries; |
|||
u32_le strtab_size; |
|||
INSERT_PADDING_BYTES(0x4); |
|||
}; |
|||
|
|||
static_assert(sizeof(Header) == 0x10, "PFS/HFS header structure size is wrong"); |
|||
|
|||
#pragma pack(push, 1) |
|||
struct FSEntry { |
|||
u64_le offset; |
|||
u64_le size; |
|||
u32_le strtab_offset; |
|||
}; |
|||
|
|||
static_assert(sizeof(FSEntry) == 0x14, "FS entry structure size is wrong"); |
|||
|
|||
struct PFSEntry { |
|||
FSEntry fs_entry; |
|||
INSERT_PADDING_BYTES(0x4); |
|||
}; |
|||
|
|||
static_assert(sizeof(PFSEntry) == 0x18, "PFS entry structure size is wrong"); |
|||
|
|||
struct HFSEntry { |
|||
FSEntry fs_entry; |
|||
u32_le hash_region_size; |
|||
INSERT_PADDING_BYTES(0x8); |
|||
std::array<char, 0x20> hash; |
|||
}; |
|||
|
|||
static_assert(sizeof(HFSEntry) == 0x40, "HFS entry structure size is wrong"); |
|||
|
|||
#pragma pack(pop) |
|||
|
|||
struct FileEntry { |
|||
FSEntry fs_entry; |
|||
std::string name; |
|||
}; |
|||
|
|||
Header pfs_header; |
|||
bool is_hfs; |
|||
size_t content_offset; |
|||
|
|||
std::vector<FileEntry> pfs_entries; |
|||
}; |
|||
|
|||
} // namespace FileSys |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue