romfs: fix extraction of single-directory root

This commit is contained in:
Liam 2023-10-31 20:11:14 -04:00
parent c60204e255
commit b0c6bf497a
4 changed files with 18 additions and 40 deletions

View file

@ -35,13 +35,14 @@ struct RomFSHeader {
static_assert(sizeof(RomFSHeader) == 0x50, "RomFSHeader has incorrect size."); static_assert(sizeof(RomFSHeader) == 0x50, "RomFSHeader has incorrect size.");
struct DirectoryEntry { struct DirectoryEntry {
u32_le parent;
u32_le sibling; u32_le sibling;
u32_le child_dir; u32_le child_dir;
u32_le child_file; u32_le child_file;
u32_le hash; u32_le hash;
u32_le name_length; u32_le name_length;
}; };
static_assert(sizeof(DirectoryEntry) == 0x14, "DirectoryEntry has incorrect size."); static_assert(sizeof(DirectoryEntry) == 0x18, "DirectoryEntry has incorrect size.");
struct FileEntry { struct FileEntry {
u32_le parent; u32_le parent;
@ -64,25 +65,22 @@ std::pair<Entry, std::string> GetEntry(const VirtualFile& file, std::size_t offs
return {entry, string}; return {entry, string};
} }
void ProcessFile(VirtualFile file, std::size_t file_offset, std::size_t data_offset, void ProcessFile(const VirtualFile& file, std::size_t file_offset, std::size_t data_offset,
u32 this_file_offset, std::shared_ptr<VectorVfsDirectory> parent) { u32 this_file_offset, std::shared_ptr<VectorVfsDirectory>& parent) {
while (true) { while (this_file_offset != ROMFS_ENTRY_EMPTY) {
auto entry = GetEntry<FileEntry>(file, file_offset + this_file_offset); auto entry = GetEntry<FileEntry>(file, file_offset + this_file_offset);
parent->AddFile(std::make_shared<OffsetVfsFile>( parent->AddFile(std::make_shared<OffsetVfsFile>(
file, entry.first.size, entry.first.offset + data_offset, entry.second)); file, entry.first.size, entry.first.offset + data_offset, entry.second));
if (entry.first.sibling == ROMFS_ENTRY_EMPTY)
break;
this_file_offset = entry.first.sibling; this_file_offset = entry.first.sibling;
} }
} }
void ProcessDirectory(VirtualFile file, std::size_t dir_offset, std::size_t file_offset, void ProcessDirectory(const VirtualFile& file, std::size_t dir_offset, std::size_t file_offset,
std::size_t data_offset, u32 this_dir_offset, std::size_t data_offset, u32 this_dir_offset,
std::shared_ptr<VectorVfsDirectory> parent) { std::shared_ptr<VectorVfsDirectory>& parent) {
while (true) { while (this_dir_offset != ROMFS_ENTRY_EMPTY) {
auto entry = GetEntry<DirectoryEntry>(file, dir_offset + this_dir_offset); auto entry = GetEntry<DirectoryEntry>(file, dir_offset + this_dir_offset);
auto current = std::make_shared<VectorVfsDirectory>( auto current = std::make_shared<VectorVfsDirectory>(
std::vector<VirtualFile>{}, std::vector<VirtualDir>{}, entry.second); std::vector<VirtualFile>{}, std::vector<VirtualDir>{}, entry.second);
@ -97,14 +95,12 @@ void ProcessDirectory(VirtualFile file, std::size_t dir_offset, std::size_t file
} }
parent->AddDirectory(current); parent->AddDirectory(current);
if (entry.first.sibling == ROMFS_ENTRY_EMPTY)
break;
this_dir_offset = entry.first.sibling; this_dir_offset = entry.first.sibling;
} }
} }
} // Anonymous namespace } // Anonymous namespace
VirtualDir ExtractRomFS(VirtualFile file, RomFSExtractionType type) { VirtualDir ExtractRomFS(VirtualFile file) {
RomFSHeader header{}; RomFSHeader header{};
if (file->ReadObject(&header) != sizeof(RomFSHeader)) if (file->ReadObject(&header) != sizeof(RomFSHeader))
return nullptr; return nullptr;
@ -113,27 +109,17 @@ VirtualDir ExtractRomFS(VirtualFile file, RomFSExtractionType type) {
return nullptr; return nullptr;
const u64 file_offset = header.file_meta.offset; const u64 file_offset = header.file_meta.offset;
const u64 dir_offset = header.directory_meta.offset + 4; const u64 dir_offset = header.directory_meta.offset;
auto root = auto root_container = std::make_shared<VectorVfsDirectory>();
std::make_shared<VectorVfsDirectory>(std::vector<VirtualFile>{}, std::vector<VirtualDir>{},
file->GetName(), file->GetContainingDirectory());
ProcessDirectory(file, dir_offset, file_offset, header.data_offset, 0, root); ProcessDirectory(file, dir_offset, file_offset, header.data_offset, 0, root_container);
VirtualDir out = std::move(root); if (auto root = root_container->GetSubdirectory(""); root) {
return std::make_shared<CachedVfsDirectory>(std::move(root));
if (type == RomFSExtractionType::SingleDiscard)
return out->GetSubdirectories().front();
while (out->GetSubdirectories().size() == 1 && out->GetFiles().empty()) {
if (Common::ToLower(out->GetSubdirectories().front()->GetName()) == "data" &&
type == RomFSExtractionType::Truncated)
break;
out = out->GetSubdirectories().front();
} }
return std::make_shared<CachedVfsDirectory>(std::move(out)); return nullptr;
} }
VirtualFile CreateRomFS(VirtualDir dir, VirtualDir ext) { VirtualFile CreateRomFS(VirtualDir dir, VirtualDir ext) {

View file

@ -7,16 +7,9 @@
namespace FileSys { namespace FileSys {
enum class RomFSExtractionType {
Full, // Includes data directory
Truncated, // Traverses into data directory
SingleDiscard, // Traverses into the first subdirectory of root
};
// Converts a RomFS binary blob to VFS Filesystem // Converts a RomFS binary blob to VFS Filesystem
// Returns nullptr on failure // Returns nullptr on failure
VirtualDir ExtractRomFS(VirtualFile file, VirtualDir ExtractRomFS(VirtualFile file);
RomFSExtractionType type = RomFSExtractionType::Truncated);
// Converts a VFS filesystem into a RomFS binary // Converts a VFS filesystem into a RomFS binary
// Returns nullptr on failure // Returns nullptr on failure

View file

@ -330,8 +330,7 @@ void WebBrowser::ExtractOfflineRomFS() {
LOG_DEBUG(Service_AM, "Extracting RomFS to {}", LOG_DEBUG(Service_AM, "Extracting RomFS to {}",
Common::FS::PathToUTF8String(offline_cache_dir)); Common::FS::PathToUTF8String(offline_cache_dir));
const auto extracted_romfs_dir = const auto extracted_romfs_dir = FileSys::ExtractRomFS(offline_romfs);
FileSys::ExtractRomFS(offline_romfs, FileSys::RomFSExtractionType::SingleDiscard);
const auto temp_dir = system.GetFilesystem()->CreateDirectory( const auto temp_dir = system.GetFilesystem()->CreateDirectory(
Common::FS::PathToUTF8String(offline_cache_dir), FileSys::Mode::ReadWrite); Common::FS::PathToUTF8String(offline_cache_dir), FileSys::Mode::ReadWrite);

View file

@ -2737,7 +2737,7 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
return; return;
} }
const auto extracted = FileSys::ExtractRomFS(romfs, FileSys::RomFSExtractionType::Full); const auto extracted = FileSys::ExtractRomFS(romfs);
if (extracted == nullptr) { if (extracted == nullptr) {
failed(); failed();
return; return;