From 95bc57cff3f81f98b172d20a20b503a332fa2603 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Mon, 20 Jul 2015 21:21:24 +0200 Subject: [PATCH 01/15] DiscScrubber: Use FileInfo::IsDirectory instead of bit magic --- Source/Core/DiscIO/DiscScrubber.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Core/DiscIO/DiscScrubber.cpp b/Source/Core/DiscIO/DiscScrubber.cpp index fff2db9ab6..6b56d8b607 100644 --- a/Source/Core/DiscIO/DiscScrubber.cpp +++ b/Source/Core/DiscIO/DiscScrubber.cpp @@ -222,7 +222,7 @@ bool DiscScrubber::ParsePartitionData(const Partition& partition, PartitionHeade for (const FileInfo& file : filesystem->GetFileList()) { DEBUG_LOG(DISCIO, "%s", file.m_FullPath.empty() ? "/" : file.m_FullPath.c_str()); - if ((file.m_NameOffset & 0x1000000) == 0) + if (!file.IsDirectory()) MarkAsUsedE(partition_data_offset, file.m_Offset, file.m_FileSize); } From 5021b4a567fad9a258f2b0fc397912f499e4cb1d Mon Sep 17 00:00:00 2001 From: JosJuice Date: Tue, 28 Jul 2015 16:56:25 +0200 Subject: [PATCH 02/15] Filesystem: Replace FileInfo struct with interface GC/Wii filesystem internals shouldn't be exposed to other classes. This change isn't especially useful by itself, but it opens up the way for some neat stuff in the following commits. --- Source/Core/DiscIO/DiscScrubber.cpp | 6 ++- Source/Core/DiscIO/FileSystemGCWii.cpp | 53 +++++++++++-------- Source/Core/DiscIO/FileSystemGCWii.h | 27 ++++++++-- Source/Core/DiscIO/Filesystem.cpp | 4 ++ Source/Core/DiscIO/Filesystem.h | 27 +++++----- .../ISOProperties/FilesystemPanel.cpp | 16 +++--- 6 files changed, 84 insertions(+), 49 deletions(-) diff --git a/Source/Core/DiscIO/DiscScrubber.cpp b/Source/Core/DiscIO/DiscScrubber.cpp index 6b56d8b607..70f9bed79a 100644 --- a/Source/Core/DiscIO/DiscScrubber.cpp +++ b/Source/Core/DiscIO/DiscScrubber.cpp @@ -16,6 +16,8 @@ #include "Common/Logging/Log.h" #include "DiscIO/DiscScrubber.h" #include "DiscIO/Filesystem.h" +// TODO: eww +#include "DiscIO/FileSystemGCWii.h" #include "DiscIO/Volume.h" namespace DiscIO @@ -219,11 +221,11 @@ bool DiscScrubber::ParsePartitionData(const Partition& partition, PartitionHeade MarkAsUsedE(partition_data_offset, header->fst_offset, header->fst_size); // Go through the filesystem and mark entries as used - for (const FileInfo& file : filesystem->GetFileList()) + for (const FileInfoGCWii& file : filesystem->GetFileList()) { DEBUG_LOG(DISCIO, "%s", file.m_FullPath.empty() ? "/" : file.m_FullPath.c_str()); if (!file.IsDirectory()) - MarkAsUsedE(partition_data_offset, file.m_Offset, file.m_FileSize); + MarkAsUsedE(partition_data_offset, file.GetOffset(), file.GetSize()); } return true; diff --git a/Source/Core/DiscIO/FileSystemGCWii.cpp b/Source/Core/DiscIO/FileSystemGCWii.cpp index b809047679..32f93f5eb6 100644 --- a/Source/Core/DiscIO/FileSystemGCWii.cpp +++ b/Source/Core/DiscIO/FileSystemGCWii.cpp @@ -22,8 +22,17 @@ namespace DiscIO { +FileInfoGCWii::FileInfoGCWii(u64 name_offset, u64 offset, u64 file_size) + : m_NameOffset(name_offset), m_Offset(offset), m_FileSize(file_size) +{ +} + +FileInfoGCWii::~FileInfoGCWii() +{ +} + FileSystemGCWii::FileSystemGCWii(const Volume* _rVolume, const Partition& partition) - : FileSystem(_rVolume, partition), m_Initialized(false), m_Valid(false), m_offset_shift(0) + : FileSystem(_rVolume, partition), m_Initialized(false), m_Valid(false), m_offset_shift(0) { m_Valid = DetectFileSystem(); } @@ -38,10 +47,10 @@ u64 FileSystemGCWii::GetFileSize(const std::string& _rFullPath) if (!m_Initialized) InitFileSystem(); - const FileInfo* pFileInfo = FindFileInfo(_rFullPath); + const FileInfoGCWii* pFileInfo = FindFileInfo(_rFullPath); if (pFileInfo != nullptr && !pFileInfo->IsDirectory()) - return pFileInfo->m_FileSize; + return pFileInfo->GetSize(); return 0; } @@ -53,7 +62,8 @@ std::string FileSystemGCWii::GetFileName(u64 _Address) for (auto& fileInfo : m_FileInfoVector) { - if ((fileInfo.m_Offset <= _Address) && ((fileInfo.m_Offset + fileInfo.m_FileSize) > _Address)) + if ((fileInfo.GetOffset() <= _Address) && + ((fileInfo.GetOffset() + fileInfo.GetSize()) > _Address)) { return fileInfo.m_FullPath; } @@ -68,21 +78,21 @@ u64 FileSystemGCWii::ReadFile(const std::string& _rFullPath, u8* _pBuffer, u64 _ if (!m_Initialized) InitFileSystem(); - const FileInfo* pFileInfo = FindFileInfo(_rFullPath); + const FileInfoGCWii* pFileInfo = FindFileInfo(_rFullPath); if (pFileInfo == nullptr) return 0; - if (_OffsetInFile >= pFileInfo->m_FileSize) + if (_OffsetInFile >= pFileInfo->GetSize()) return 0; - u64 read_length = std::min(_MaxBufferSize, pFileInfo->m_FileSize - _OffsetInFile); + u64 read_length = std::min(_MaxBufferSize, pFileInfo->GetSize() - _OffsetInFile); DEBUG_LOG(DISCIO, "Reading %" PRIx64 " bytes at %" PRIx64 " from file %s. Offset: %" PRIx64 " Size: %" PRIx64, - read_length, _OffsetInFile, _rFullPath.c_str(), pFileInfo->m_Offset, - pFileInfo->m_FileSize); + read_length, _OffsetInFile, _rFullPath.c_str(), pFileInfo->GetOffset(), + pFileInfo->GetSize()); - m_rVolume->Read(pFileInfo->m_Offset + _OffsetInFile, read_length, _pBuffer, m_partition); + m_rVolume->Read(pFileInfo->GetOffset() + _OffsetInFile, read_length, _pBuffer, m_partition); return read_length; } @@ -91,13 +101,13 @@ bool FileSystemGCWii::ExportFile(const std::string& _rFullPath, const std::strin if (!m_Initialized) InitFileSystem(); - const FileInfo* pFileInfo = FindFileInfo(_rFullPath); + const FileInfoGCWii* pFileInfo = FindFileInfo(_rFullPath); if (!pFileInfo) return false; - u64 remainingSize = pFileInfo->m_FileSize; - u64 fileOffset = pFileInfo->m_Offset; + u64 remainingSize = pFileInfo->GetSize(); + u64 fileOffset = pFileInfo->GetOffset(); File::IOFile f(_rExportFilename, "wb"); if (!f) @@ -225,7 +235,7 @@ std::string FileSystemGCWii::GetStringFromOffset(u64 _Offset) const return SHIFTJISToUTF8(data); } -const std::vector& FileSystemGCWii::GetFileList() +const std::vector& FileSystemGCWii::GetFileList() { if (!m_Initialized) InitFileSystem(); @@ -233,7 +243,7 @@ const std::vector& FileSystemGCWii::GetFileList() return m_FileInfoVector; } -const FileInfo* FileSystemGCWii::FindFileInfo(const std::string& _rFullPath) +const FileInfoGCWii* FileSystemGCWii::FindFileInfo(const std::string& _rFullPath) { if (!m_Initialized) InitFileSystem(); @@ -279,7 +289,8 @@ void FileSystemGCWii::InitFileSystem() const std::optional root_size = m_rVolume->ReadSwapped(FSTOffset + 0x8, m_partition); if (!root_name_offset || !root_offset || !root_size) return; - FileInfo root = {*root_name_offset, static_cast(*root_offset) << m_offset_shift, *root_size}; + FileInfoGCWii root(*root_name_offset, static_cast(*root_offset) << m_offset_shift, + *root_size); if (!root.IsDirectory()) return; @@ -287,7 +298,7 @@ void FileSystemGCWii::InitFileSystem() // 12 bytes (the size of a file entry) times 10 * 1024 * 1024 is 120 MiB, // more than total RAM in a Wii. No file system should use anywhere near that much. static const u32 ARBITRARY_FILE_SYSTEM_SIZE_LIMIT = 10 * 1024 * 1024; - if (root.m_FileSize > ARBITRARY_FILE_SYSTEM_SIZE_LIMIT) + if (root.GetSize() > ARBITRARY_FILE_SYSTEM_SIZE_LIMIT) { // Without this check, Dolphin can crash by trying to allocate too much // memory when loading the file systems of certain malformed disc images. @@ -300,8 +311,8 @@ void FileSystemGCWii::InitFileSystem() PanicAlert("Wtf?"); u64 NameTableOffset = FSTOffset; - m_FileInfoVector.reserve((size_t)root.m_FileSize); - for (u32 i = 0; i < root.m_FileSize; i++) + m_FileInfoVector.reserve((size_t)root.GetSize()); + for (u32 i = 0; i < root.GetSize(); i++) { const u64 read_offset = FSTOffset + (i * 0xC); const std::optional name_offset = m_rVolume->ReadSwapped(read_offset, m_partition); @@ -323,7 +334,7 @@ size_t FileSystemGCWii::BuildFilenames(const size_t _FirstIndex, const size_t _L while (CurrentIndex < _LastIndex) { - FileInfo& rFileInfo = m_FileInfoVector[CurrentIndex]; + FileInfoGCWii& rFileInfo = m_FileInfoVector[CurrentIndex]; u64 const uOffset = _NameTableOffset + (rFileInfo.m_NameOffset & 0xFFFFFF); std::string const offset_str{GetStringFromOffset(uOffset)}; bool const is_dir = rFileInfo.IsDirectory(); @@ -340,7 +351,7 @@ size_t FileSystemGCWii::BuildFilenames(const size_t _FirstIndex, const size_t _L } // check next index - CurrentIndex = BuildFilenames(CurrentIndex + 1, (size_t)rFileInfo.m_FileSize, + CurrentIndex = BuildFilenames(CurrentIndex + 1, (size_t)rFileInfo.GetSize(), rFileInfo.m_FullPath, _NameTableOffset); } diff --git a/Source/Core/DiscIO/FileSystemGCWii.h b/Source/Core/DiscIO/FileSystemGCWii.h index 3655c5fcb2..d328880636 100644 --- a/Source/Core/DiscIO/FileSystemGCWii.h +++ b/Source/Core/DiscIO/FileSystemGCWii.h @@ -17,15 +17,32 @@ namespace DiscIO class Volume; struct Partition; +class FileInfoGCWii : public FileInfo +{ +public: + FileInfoGCWii(u64 name_offset, u64 offset, u64 file_size); + ~FileInfoGCWii() override; + + u64 GetOffset() const override { return m_Offset; } + u64 GetSize() const override { return m_FileSize; } + bool IsDirectory() const override { return (m_NameOffset & 0xFF000000) != 0; } + // TODO: These shouldn't be public + std::string m_FullPath; + const u64 m_NameOffset = 0u; + +private: + const u64 m_Offset = 0u; + const u64 m_FileSize = 0u; +}; + class FileSystemGCWii : public FileSystem { public: FileSystemGCWii(const Volume* _rVolume, const Partition& partition); - virtual ~FileSystemGCWii(); - + ~FileSystemGCWii() override; bool IsValid() const override { return m_Valid; } u64 GetFileSize(const std::string& _rFullPath) override; - const std::vector& GetFileList() override; + const std::vector& GetFileList() override; std::string GetFileName(u64 _Address) override; u64 ReadFile(const std::string& _rFullPath, u8* _pBuffer, u64 _MaxBufferSize, u64 _OffsetInFile) override; @@ -39,10 +56,10 @@ private: bool m_Initialized; bool m_Valid; u32 m_offset_shift; - std::vector m_FileInfoVector; + std::vector m_FileInfoVector; std::string GetStringFromOffset(u64 _Offset) const; - const FileInfo* FindFileInfo(const std::string& _rFullPath); + const FileInfoGCWii* FindFileInfo(const std::string& _rFullPath); bool DetectFileSystem(); void InitFileSystem(); size_t BuildFilenames(const size_t _FirstIndex, const size_t _LastIndex, diff --git a/Source/Core/DiscIO/Filesystem.cpp b/Source/Core/DiscIO/Filesystem.cpp index a955c1f402..94127e5e90 100644 --- a/Source/Core/DiscIO/Filesystem.cpp +++ b/Source/Core/DiscIO/Filesystem.cpp @@ -9,6 +9,10 @@ namespace DiscIO { +FileInfo::~FileInfo() +{ +} + FileSystem::FileSystem(const Volume* _rVolume, const Partition& partition) : m_rVolume(_rVolume), m_partition(partition) { diff --git a/Source/Core/DiscIO/Filesystem.h b/Source/Core/DiscIO/Filesystem.h index 7502db52f0..c4b89dc908 100644 --- a/Source/Core/DiscIO/Filesystem.h +++ b/Source/Core/DiscIO/Filesystem.h @@ -14,22 +14,20 @@ namespace DiscIO { +// TODO: eww +class FileInfoGCWii; + // file info of an FST entry -struct FileInfo +class FileInfo { - u64 m_NameOffset = 0u; - u64 m_Offset = 0u; - u64 m_FileSize = 0u; - std::string m_FullPath; +public: + virtual ~FileInfo(); - bool IsDirectory() const { return (m_NameOffset & 0xFF000000) != 0; } - FileInfo(u64 name_offset, u64 offset, u64 filesize) - : m_NameOffset(name_offset), m_Offset(offset), m_FileSize(filesize) - { - } - - FileInfo(FileInfo const&) = default; - FileInfo() = default; + // Not guaranteed to return a meaningful value for directories + virtual u64 GetOffset() const = 0; + // Not guaranteed to return a meaningful value for directories + virtual u64 GetSize() const = 0; + virtual bool IsDirectory() const = 0; }; class FileSystem @@ -39,7 +37,8 @@ public: virtual ~FileSystem(); virtual bool IsValid() const = 0; - virtual const std::vector& GetFileList() = 0; + // TODO: Should only return FileInfo, not FileInfoGCWii + virtual const std::vector& GetFileList() = 0; virtual u64 GetFileSize(const std::string& _rFullPath) = 0; virtual u64 ReadFile(const std::string& _rFullPath, u8* _pBuffer, u64 _MaxBufferSize, u64 _OffsetInFile = 0) = 0; diff --git a/Source/Core/DolphinWX/ISOProperties/FilesystemPanel.cpp b/Source/Core/DolphinWX/ISOProperties/FilesystemPanel.cpp index 955c5bae50..4b1f5d330a 100644 --- a/Source/Core/DolphinWX/ISOProperties/FilesystemPanel.cpp +++ b/Source/Core/DolphinWX/ISOProperties/FilesystemPanel.cpp @@ -23,6 +23,8 @@ #include "Common/Logging/Log.h" #include "DiscIO/Enums.h" #include "DiscIO/Filesystem.h" +// TODO: eww +#include "DiscIO/FileSystemGCWii.h" #include "DiscIO/Volume.h" #include "DolphinWX/ISOFile.h" #include "DolphinWX/WxUtils.h" @@ -84,14 +86,14 @@ wxImageList* LoadIconBitmaps(const wxWindow* context) } size_t CreateDirectoryTree(wxTreeCtrl* tree_ctrl, wxTreeItemId parent, - const std::vector& file_infos, + const std::vector& file_infos, const size_t first_index, const size_t last_index) { size_t current_index = first_index; while (current_index < last_index) { - const DiscIO::FileInfo& file_info = file_infos[current_index]; + const DiscIO::FileInfoGCWii& file_info = file_infos[current_index]; std::string file_path = file_info.m_FullPath; // Trim the trailing '/' if it exists. @@ -113,7 +115,7 @@ size_t CreateDirectoryTree(wxTreeCtrl* tree_ctrl, wxTreeItemId parent, { const wxTreeItemId item = tree_ctrl->AppendItem(parent, StrToWxStr(file_path), ICON_FOLDER); current_index = CreateDirectoryTree(tree_ctrl, item, file_infos, current_index + 1, - static_cast(file_info.m_FileSize)); + static_cast(file_info.GetSize())); } else { @@ -126,12 +128,12 @@ size_t CreateDirectoryTree(wxTreeCtrl* tree_ctrl, wxTreeItemId parent, } size_t CreateDirectoryTree(wxTreeCtrl* tree_ctrl, wxTreeItemId parent, - const std::vector& file_infos) + const std::vector& file_infos) { if (file_infos.empty()) return 0; - return CreateDirectoryTree(tree_ctrl, parent, file_infos, 1, file_infos.at(0).m_FileSize); + return CreateDirectoryTree(tree_ctrl, parent, file_infos, 1, file_infos.at(0).GetSize()); } WiiPartition* FindWiiPartition(wxTreeCtrl* tree_ctrl, const wxString& label) @@ -452,7 +454,7 @@ void FilesystemPanel::ExtractDirectories(const std::string& full_path, const std::string& output_folder, DiscIO::FileSystem* filesystem) { - const std::vector& fst = filesystem->GetFileList(); + const std::vector& fst = filesystem->GetFileList(); u32 index = 0; u32 size = 0; @@ -473,7 +475,7 @@ void FilesystemPanel::ExtractDirectories(const std::string& full_path, if (fst[index].m_FullPath == full_path) { INFO_LOG(DISCIO, "Found the directory at %u", index); - size = static_cast(fst[index].m_FileSize); + size = static_cast(fst[index].GetSize()); break; } } From 07d3a39aeb06204e13cfdf1d5f7dbe4f3aa66f89 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Wed, 29 Jul 2015 16:42:29 +0200 Subject: [PATCH 03/15] Filesystem: Replace file info's full path with name Some callers (i.e. ISOProperties) don't want the full path, so giving them it is unnecessary. Those that do want it can use GetPathFromFSTOffset. Not storing full paths everywhere also saves a small bit of RAM and is necessary for a later commit. The code isn't especially pretty right now (callers need to use FST offsets...) but it'll become better later. --- Source/Core/Core/HW/DVD/FileMonitor.cpp | 15 ++- Source/Core/DiscIO/DiscScrubber.cpp | 7 +- Source/Core/DiscIO/FileSystemGCWii.cpp | 112 ++++++++++-------- Source/Core/DiscIO/FileSystemGCWii.h | 10 +- Source/Core/DiscIO/Filesystem.h | 4 +- .../ISOProperties/FilesystemPanel.cpp | 31 ++--- 6 files changed, 89 insertions(+), 90 deletions(-) diff --git a/Source/Core/Core/HW/DVD/FileMonitor.cpp b/Source/Core/Core/HW/DVD/FileMonitor.cpp index cc888b09c2..f6c984c4fc 100644 --- a/Source/Core/Core/HW/DVD/FileMonitor.cpp +++ b/Source/Core/Core/HW/DVD/FileMonitor.cpp @@ -88,28 +88,27 @@ void Log(u64 offset, const DiscIO::Partition& partition) if (!s_filesystem) return; - const std::string filename = s_filesystem->GetFileName(offset); + const std::string path = s_filesystem->GetPath(offset); // Do nothing if no file was found at that offset - if (filename.empty()) + if (path.empty()) return; // Do nothing if we found the same file again - if (s_previous_file == filename) + if (s_previous_file == path) return; - const u64 size = s_filesystem->GetFileSize(filename); + const u64 size = s_filesystem->GetFileSize(path); const std::string size_string = ThousandSeparate(size / 1000, 7); - const std::string log_string = - StringFromFormat("%s kB %s", size_string.c_str(), filename.c_str()); - if (IsSoundFile(filename)) + const std::string log_string = StringFromFormat("%s kB %s", size_string.c_str(), path.c_str()); + if (IsSoundFile(path)) INFO_LOG(FILEMON, "%s", log_string.c_str()); else WARN_LOG(FILEMON, "%s", log_string.c_str()); // Update the last accessed file - s_previous_file = filename; + s_previous_file = path; } } // namespace FileMonitor diff --git a/Source/Core/DiscIO/DiscScrubber.cpp b/Source/Core/DiscIO/DiscScrubber.cpp index 70f9bed79a..71d1fa16bc 100644 --- a/Source/Core/DiscIO/DiscScrubber.cpp +++ b/Source/Core/DiscIO/DiscScrubber.cpp @@ -221,9 +221,12 @@ bool DiscScrubber::ParsePartitionData(const Partition& partition, PartitionHeade MarkAsUsedE(partition_data_offset, header->fst_offset, header->fst_size); // Go through the filesystem and mark entries as used - for (const FileInfoGCWii& file : filesystem->GetFileList()) + auto& file_list = filesystem->GetFileList(); + for (size_t i = 0; i < file_list.size(); ++i) { - DEBUG_LOG(DISCIO, "%s", file.m_FullPath.empty() ? "/" : file.m_FullPath.c_str()); + const std::string path = filesystem->GetPathFromFSTOffset(i); + DEBUG_LOG(DISCIO, "%s", path.empty() ? "/" : path.c_str()); + auto& file = file_list[i]; if (!file.IsDirectory()) MarkAsUsedE(partition_data_offset, file.GetOffset(), file.GetSize()); } diff --git a/Source/Core/DiscIO/FileSystemGCWii.cpp b/Source/Core/DiscIO/FileSystemGCWii.cpp index 32f93f5eb6..fc0e40f055 100644 --- a/Source/Core/DiscIO/FileSystemGCWii.cpp +++ b/Source/Core/DiscIO/FileSystemGCWii.cpp @@ -22,8 +22,8 @@ namespace DiscIO { -FileInfoGCWii::FileInfoGCWii(u64 name_offset, u64 offset, u64 file_size) - : m_NameOffset(name_offset), m_Offset(offset), m_FileSize(file_size) +FileInfoGCWii::FileInfoGCWii(u64 name_offset, u64 offset, u64 file_size, std::string name) + : m_NameOffset(name_offset), m_Offset(offset), m_FileSize(file_size), m_Name(name) { } @@ -55,23 +55,64 @@ u64 FileSystemGCWii::GetFileSize(const std::string& _rFullPath) return 0; } -std::string FileSystemGCWii::GetFileName(u64 _Address) +std::string FileSystemGCWii::GetPath(u64 _Address) { if (!m_Initialized) InitFileSystem(); - for (auto& fileInfo : m_FileInfoVector) + for (size_t i = 0; i < m_FileInfoVector.size(); ++i) { - if ((fileInfo.GetOffset() <= _Address) && - ((fileInfo.GetOffset() + fileInfo.GetSize()) > _Address)) + const FileInfoGCWii& file_info = m_FileInfoVector[i]; + if ((file_info.GetOffset() <= _Address) && + ((file_info.GetOffset() + file_info.GetSize()) > _Address)) { - return fileInfo.m_FullPath; + return GetPathFromFSTOffset(i); } } return ""; } +std::string FileSystemGCWii::GetPathFromFSTOffset(size_t file_info_offset) +{ + if (!m_Initialized) + InitFileSystem(); + + // Root entry doesn't have a name + if (file_info_offset == 0) + return ""; + + const FileInfoGCWii& file_info = m_FileInfoVector[file_info_offset]; + if (file_info.IsDirectory()) + { + // The offset of the parent directory is stored in the current directory. + + if (file_info.GetOffset() >= file_info_offset) + { + // The offset of the parent directory is supposed to be smaller than + // the current offset. If an FST is malformed and breaks that rule, + // there's a risk that parent directory pointers form a loop. + // To avoid stack overflows, this method returns. + ERROR_LOG(DISCIO, "Invalid parent offset in file system"); + return ""; + } + return GetPathFromFSTOffset(file_info.GetOffset()) + file_info.GetName() + "/"; + } + else + { + // The parent directory can be found by searching backwards + // for a directory that contains this file. + + size_t parent_offset = file_info_offset - 1; + while (!(m_FileInfoVector[parent_offset].IsDirectory() && + m_FileInfoVector[parent_offset].GetSize() > file_info_offset)) + { + parent_offset--; + } + return GetPathFromFSTOffset(parent_offset) + file_info.GetName(); + } +} + u64 FileSystemGCWii::ReadFile(const std::string& _rFullPath, u8* _pBuffer, u64 _MaxBufferSize, u64 _OffsetInFile) { @@ -248,10 +289,10 @@ const FileInfoGCWii* FileSystemGCWii::FindFileInfo(const std::string& _rFullPath if (!m_Initialized) InitFileSystem(); - for (auto& fileInfo : m_FileInfoVector) + for (size_t i = 0; i < m_FileInfoVector.size(); ++i) { - if (!strcasecmp(fileInfo.m_FullPath.c_str(), _rFullPath.c_str())) - return &fileInfo; + if (!strcasecmp(GetPathFromFSTOffset(i).c_str(), _rFullPath.c_str())) + return &m_FileInfoVector[i]; } return nullptr; @@ -290,7 +331,7 @@ void FileSystemGCWii::InitFileSystem() if (!root_name_offset || !root_offset || !root_size) return; FileInfoGCWii root(*root_name_offset, static_cast(*root_offset) << m_offset_shift, - *root_size); + *root_size, ""); if (!root.IsDirectory()) return; @@ -309,53 +350,20 @@ void FileSystemGCWii::InitFileSystem() if (m_FileInfoVector.size()) PanicAlert("Wtf?"); - u64 NameTableOffset = FSTOffset; + u64 NameTableOffset = FSTOffset + (root.GetSize() * 0xC); m_FileInfoVector.reserve((size_t)root.GetSize()); for (u32 i = 0; i < root.GetSize(); i++) { const u64 read_offset = FSTOffset + (i * 0xC); - const std::optional name_offset = m_rVolume->ReadSwapped(read_offset, m_partition); - const std::optional offset = m_rVolume->ReadSwapped(read_offset + 0x4, m_partition); - const std::optional size = m_rVolume->ReadSwapped(read_offset + 0x8, m_partition); - m_FileInfoVector.emplace_back(name_offset.value_or(0), - static_cast(offset.value_or(0)) << m_offset_shift, - size.value_or(0)); - NameTableOffset += 0xC; + const u32 name_offset = m_rVolume->ReadSwapped(read_offset, m_partition).value_or(0); + const u32 offset = m_rVolume->ReadSwapped(read_offset + 0x4, m_partition).value_or(0); + const u32 size = m_rVolume->ReadSwapped(read_offset + 0x8, m_partition).value_or(0); + const std::string name = GetStringFromOffset(NameTableOffset + (name_offset & 0xFFFFFF)); + m_FileInfoVector.emplace_back( + name_offset, static_cast(offset) << (m_offset_shift * !(name_offset & 0xFF000000)), + size, name); } - - BuildFilenames(1, m_FileInfoVector.size(), "", NameTableOffset); -} - -size_t FileSystemGCWii::BuildFilenames(const size_t _FirstIndex, const size_t _LastIndex, - const std::string& _szDirectory, u64 _NameTableOffset) -{ - size_t CurrentIndex = _FirstIndex; - - while (CurrentIndex < _LastIndex) - { - FileInfoGCWii& rFileInfo = m_FileInfoVector[CurrentIndex]; - u64 const uOffset = _NameTableOffset + (rFileInfo.m_NameOffset & 0xFFFFFF); - std::string const offset_str{GetStringFromOffset(uOffset)}; - bool const is_dir = rFileInfo.IsDirectory(); - rFileInfo.m_FullPath.reserve(_szDirectory.size() + offset_str.size()); - - rFileInfo.m_FullPath.append(_szDirectory.data(), _szDirectory.size()) - .append(offset_str.data(), offset_str.size()) - .append("/", size_t(is_dir)); - - if (!is_dir) - { - ++CurrentIndex; - continue; - } - - // check next index - CurrentIndex = BuildFilenames(CurrentIndex + 1, (size_t)rFileInfo.GetSize(), - rFileInfo.m_FullPath, _NameTableOffset); - } - - return CurrentIndex; } } // namespace diff --git a/Source/Core/DiscIO/FileSystemGCWii.h b/Source/Core/DiscIO/FileSystemGCWii.h index d328880636..1f442e4d40 100644 --- a/Source/Core/DiscIO/FileSystemGCWii.h +++ b/Source/Core/DiscIO/FileSystemGCWii.h @@ -20,14 +20,15 @@ struct Partition; class FileInfoGCWii : public FileInfo { public: - FileInfoGCWii(u64 name_offset, u64 offset, u64 file_size); + FileInfoGCWii(u64 name_offset, u64 offset, u64 file_size, std::string name); ~FileInfoGCWii() override; u64 GetOffset() const override { return m_Offset; } u64 GetSize() const override { return m_FileSize; } bool IsDirectory() const override { return (m_NameOffset & 0xFF000000) != 0; } + const std::string& GetName() const override { return m_Name; } // TODO: These shouldn't be public - std::string m_FullPath; + std::string m_Name; const u64 m_NameOffset = 0u; private: @@ -43,7 +44,8 @@ public: bool IsValid() const override { return m_Valid; } u64 GetFileSize(const std::string& _rFullPath) override; const std::vector& GetFileList() override; - std::string GetFileName(u64 _Address) override; + std::string GetPath(u64 _Address) override; + std::string GetPathFromFSTOffset(size_t file_info_offset) override; u64 ReadFile(const std::string& _rFullPath, u8* _pBuffer, u64 _MaxBufferSize, u64 _OffsetInFile) override; bool ExportFile(const std::string& _rFullPath, const std::string& _rExportFilename) override; @@ -62,8 +64,6 @@ private: const FileInfoGCWii* FindFileInfo(const std::string& _rFullPath); bool DetectFileSystem(); void InitFileSystem(); - size_t BuildFilenames(const size_t _FirstIndex, const size_t _LastIndex, - const std::string& _szDirectory, u64 _NameTableOffset); }; } // namespace diff --git a/Source/Core/DiscIO/Filesystem.h b/Source/Core/DiscIO/Filesystem.h index c4b89dc908..798da5ba07 100644 --- a/Source/Core/DiscIO/Filesystem.h +++ b/Source/Core/DiscIO/Filesystem.h @@ -28,6 +28,7 @@ public: // Not guaranteed to return a meaningful value for directories virtual u64 GetSize() const = 0; virtual bool IsDirectory() const = 0; + virtual const std::string& GetName() const = 0; }; class FileSystem @@ -45,7 +46,8 @@ public: virtual bool ExportFile(const std::string& _rFullPath, const std::string& _rExportFilename) = 0; virtual bool ExportApploader(const std::string& _rExportFolder) const = 0; virtual bool ExportDOL(const std::string& _rExportFolder) const = 0; - virtual std::string GetFileName(u64 _Address) = 0; + virtual std::string GetPath(u64 _Address) = 0; + virtual std::string GetPathFromFSTOffset(size_t file_info_offset) = 0; virtual std::optional GetBootDOLOffset() const = 0; virtual std::optional GetBootDOLSize(u64 dol_offset) const = 0; diff --git a/Source/Core/DolphinWX/ISOProperties/FilesystemPanel.cpp b/Source/Core/DolphinWX/ISOProperties/FilesystemPanel.cpp index 4b1f5d330a..ebad7364ef 100644 --- a/Source/Core/DolphinWX/ISOProperties/FilesystemPanel.cpp +++ b/Source/Core/DolphinWX/ISOProperties/FilesystemPanel.cpp @@ -94,32 +94,18 @@ size_t CreateDirectoryTree(wxTreeCtrl* tree_ctrl, wxTreeItemId parent, while (current_index < last_index) { const DiscIO::FileInfoGCWii& file_info = file_infos[current_index]; - std::string file_path = file_info.m_FullPath; - - // Trim the trailing '/' if it exists. - if (file_path.back() == DIR_SEP_CHR) - { - file_path.pop_back(); - } - - // Cut off the path up to the actual filename or folder. - // Say we have "/music/stream/stream1.strm", the result will be "stream1.strm". - const size_t dir_sep_index = file_path.rfind(DIR_SEP_CHR); - if (dir_sep_index != std::string::npos) - { - file_path = file_path.substr(dir_sep_index + 1); - } // check next index if (file_info.IsDirectory()) { - const wxTreeItemId item = tree_ctrl->AppendItem(parent, StrToWxStr(file_path), ICON_FOLDER); + const wxTreeItemId item = + tree_ctrl->AppendItem(parent, StrToWxStr(file_info.GetName()), ICON_FOLDER); current_index = CreateDirectoryTree(tree_ctrl, item, file_infos, current_index + 1, static_cast(file_info.GetSize())); } else { - tree_ctrl->AppendItem(parent, StrToWxStr(file_path), ICON_FILE); + tree_ctrl->AppendItem(parent, StrToWxStr(file_info.GetName()), ICON_FILE); current_index++; } } @@ -472,7 +458,7 @@ void FilesystemPanel::ExtractDirectories(const std::string& full_path, // Look for the dir we are going to extract for (index = 0; index < fst.size(); ++index) { - if (fst[index].m_FullPath == full_path) + if (filesystem->GetPathFromFSTOffset(index) == full_path) { INFO_LOG(DISCIO, "Found the directory at %u", index); size = static_cast(fst[index].GetSize()); @@ -492,12 +478,13 @@ void FilesystemPanel::ExtractDirectories(const std::string& full_path, // Extraction for (u32 i = index; i < size; i++) { + const std::string path = filesystem->GetPathFromFSTOffset(i); dialog.SetTitle(wxString::Format( "%s : %u%%", dialog_title.c_str(), static_cast((static_cast(i - index) / static_cast(size - index)) * 100))); - dialog.Update(i, wxString::Format(_("Extracting %s"), StrToWxStr(fst[i].m_FullPath))); + dialog.Update(i, wxString::Format(_("Extracting %s"), StrToWxStr(path))); if (dialog.WasCancelled()) break; @@ -505,7 +492,7 @@ void FilesystemPanel::ExtractDirectories(const std::string& full_path, if (fst[i].IsDirectory()) { const std::string export_name = - StringFromFormat("%s/%s/", output_folder.c_str(), fst[i].m_FullPath.c_str()); + StringFromFormat("%s/%s/", output_folder.c_str(), path.c_str()); INFO_LOG(DISCIO, "%s", export_name.c_str()); if (!File::Exists(export_name) && !File::CreateFullPath(export_name)) @@ -523,10 +510,10 @@ void FilesystemPanel::ExtractDirectories(const std::string& full_path, else { const std::string export_name = - StringFromFormat("%s/%s", output_folder.c_str(), fst[i].m_FullPath.c_str()); + StringFromFormat("%s/%s", output_folder.c_str(), path.c_str()); INFO_LOG(DISCIO, "%s", export_name.c_str()); - if (!File::Exists(export_name) && !filesystem->ExportFile(fst[i].m_FullPath, export_name)) + if (!File::Exists(export_name) && !filesystem->ExportFile(path, export_name)) { ERROR_LOG(DISCIO, "Could not export %s", export_name.c_str()); } From 3d5ef948d0d3984e4ef7c26fe56acf469868e0f3 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Wed, 29 Jul 2015 17:27:43 +0200 Subject: [PATCH 04/15] Filesystem: Make FindFileInfo public --- Source/Core/DiscIO/FileSystemGCWii.cpp | 50 +++++++++++++------------- Source/Core/DiscIO/FileSystemGCWii.h | 4 +-- Source/Core/DiscIO/Filesystem.h | 1 + 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/Source/Core/DiscIO/FileSystemGCWii.cpp b/Source/Core/DiscIO/FileSystemGCWii.cpp index fc0e40f055..f8ddab28fe 100644 --- a/Source/Core/DiscIO/FileSystemGCWii.cpp +++ b/Source/Core/DiscIO/FileSystemGCWii.cpp @@ -42,12 +42,34 @@ FileSystemGCWii::~FileSystemGCWii() m_FileInfoVector.clear(); } +const std::vector& FileSystemGCWii::GetFileList() +{ + if (!m_Initialized) + InitFileSystem(); + + return m_FileInfoVector; +} + +const FileInfo* FileSystemGCWii::FindFileInfo(const std::string& _rFullPath) +{ + if (!m_Initialized) + InitFileSystem(); + + for (size_t i = 0; i < m_FileInfoVector.size(); ++i) + { + if (!strcasecmp(GetPathFromFSTOffset(i).c_str(), _rFullPath.c_str())) + return &m_FileInfoVector[i]; + } + + return nullptr; +} + u64 FileSystemGCWii::GetFileSize(const std::string& _rFullPath) { if (!m_Initialized) InitFileSystem(); - const FileInfoGCWii* pFileInfo = FindFileInfo(_rFullPath); + const FileInfo* pFileInfo = FindFileInfo(_rFullPath); if (pFileInfo != nullptr && !pFileInfo->IsDirectory()) return pFileInfo->GetSize(); @@ -119,7 +141,7 @@ u64 FileSystemGCWii::ReadFile(const std::string& _rFullPath, u8* _pBuffer, u64 _ if (!m_Initialized) InitFileSystem(); - const FileInfoGCWii* pFileInfo = FindFileInfo(_rFullPath); + const FileInfo* pFileInfo = FindFileInfo(_rFullPath); if (pFileInfo == nullptr) return 0; @@ -142,7 +164,7 @@ bool FileSystemGCWii::ExportFile(const std::string& _rFullPath, const std::strin if (!m_Initialized) InitFileSystem(); - const FileInfoGCWii* pFileInfo = FindFileInfo(_rFullPath); + const FileInfo* pFileInfo = FindFileInfo(_rFullPath); if (!pFileInfo) return false; @@ -276,28 +298,6 @@ std::string FileSystemGCWii::GetStringFromOffset(u64 _Offset) const return SHIFTJISToUTF8(data); } -const std::vector& FileSystemGCWii::GetFileList() -{ - if (!m_Initialized) - InitFileSystem(); - - return m_FileInfoVector; -} - -const FileInfoGCWii* FileSystemGCWii::FindFileInfo(const std::string& _rFullPath) -{ - if (!m_Initialized) - InitFileSystem(); - - for (size_t i = 0; i < m_FileInfoVector.size(); ++i) - { - if (!strcasecmp(GetPathFromFSTOffset(i).c_str(), _rFullPath.c_str())) - return &m_FileInfoVector[i]; - } - - return nullptr; -} - bool FileSystemGCWii::DetectFileSystem() { if (m_rVolume->ReadSwapped(0x18, m_partition) == u32(0x5D1C9EA3)) diff --git a/Source/Core/DiscIO/FileSystemGCWii.h b/Source/Core/DiscIO/FileSystemGCWii.h index 1f442e4d40..37e972e542 100644 --- a/Source/Core/DiscIO/FileSystemGCWii.h +++ b/Source/Core/DiscIO/FileSystemGCWii.h @@ -42,8 +42,9 @@ public: FileSystemGCWii(const Volume* _rVolume, const Partition& partition); ~FileSystemGCWii() override; bool IsValid() const override { return m_Valid; } - u64 GetFileSize(const std::string& _rFullPath) override; const std::vector& GetFileList() override; + const FileInfo* FindFileInfo(const std::string& path) override; + u64 GetFileSize(const std::string& _rFullPath) override; std::string GetPath(u64 _Address) override; std::string GetPathFromFSTOffset(size_t file_info_offset) override; u64 ReadFile(const std::string& _rFullPath, u8* _pBuffer, u64 _MaxBufferSize, @@ -61,7 +62,6 @@ private: std::vector m_FileInfoVector; std::string GetStringFromOffset(u64 _Offset) const; - const FileInfoGCWii* FindFileInfo(const std::string& _rFullPath); bool DetectFileSystem(); void InitFileSystem(); }; diff --git a/Source/Core/DiscIO/Filesystem.h b/Source/Core/DiscIO/Filesystem.h index 798da5ba07..c27d6e9636 100644 --- a/Source/Core/DiscIO/Filesystem.h +++ b/Source/Core/DiscIO/Filesystem.h @@ -41,6 +41,7 @@ public: // TODO: Should only return FileInfo, not FileInfoGCWii virtual const std::vector& GetFileList() = 0; virtual u64 GetFileSize(const std::string& _rFullPath) = 0; + virtual const FileInfo* FindFileInfo(const std::string& path) = 0; virtual u64 ReadFile(const std::string& _rFullPath, u8* _pBuffer, u64 _MaxBufferSize, u64 _OffsetInFile = 0) = 0; virtual bool ExportFile(const std::string& _rFullPath, const std::string& _rExportFilename) = 0; From 7c45afecb2a157088591a94f8c008d74419243d1 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Thu, 30 Jul 2015 15:06:23 +0200 Subject: [PATCH 05/15] Filesystem: Use file info in arguments instead of path Some callers already have the file info, making the relatively slow FindFileInfo calls unnecessary. Callers that didn't have the file info will now need to call FindFileInfo on their own. --- Source/Core/Core/HW/DVD/FileMonitor.cpp | 9 ++-- Source/Core/DiscIO/FileSystemGCWii.cpp | 45 ++++++++++--------- Source/Core/DiscIO/FileSystemGCWii.h | 6 +-- Source/Core/DiscIO/Filesystem.h | 6 +-- Source/Core/DiscIO/VolumeGC.cpp | 12 ++++- Source/Core/DiscIO/VolumeWii.cpp | 3 +- .../ISOProperties/FilesystemPanel.cpp | 11 +++-- 7 files changed, 53 insertions(+), 39 deletions(-) diff --git a/Source/Core/Core/HW/DVD/FileMonitor.cpp b/Source/Core/Core/HW/DVD/FileMonitor.cpp index f6c984c4fc..980bc5454f 100644 --- a/Source/Core/Core/HW/DVD/FileMonitor.cpp +++ b/Source/Core/Core/HW/DVD/FileMonitor.cpp @@ -88,18 +88,19 @@ void Log(u64 offset, const DiscIO::Partition& partition) if (!s_filesystem) return; - const std::string path = s_filesystem->GetPath(offset); + const DiscIO::FileInfo* file_info = s_filesystem->FindFileInfo(offset); // Do nothing if no file was found at that offset - if (path.empty()) + if (!file_info) return; + const std::string path = s_filesystem->GetPath(file_info->GetOffset()); + // Do nothing if we found the same file again if (s_previous_file == path) return; - const u64 size = s_filesystem->GetFileSize(path); - const std::string size_string = ThousandSeparate(size / 1000, 7); + const std::string size_string = ThousandSeparate(file_info->GetSize() / 1000, 7); const std::string log_string = StringFromFormat("%s kB %s", size_string.c_str(), path.c_str()); if (IsSoundFile(path)) diff --git a/Source/Core/DiscIO/FileSystemGCWii.cpp b/Source/Core/DiscIO/FileSystemGCWii.cpp index f8ddab28fe..d799caf435 100644 --- a/Source/Core/DiscIO/FileSystemGCWii.cpp +++ b/Source/Core/DiscIO/FileSystemGCWii.cpp @@ -50,31 +50,35 @@ const std::vector& FileSystemGCWii::GetFileList() return m_FileInfoVector; } -const FileInfo* FileSystemGCWii::FindFileInfo(const std::string& _rFullPath) +const FileInfo* FileSystemGCWii::FindFileInfo(const std::string& path) { if (!m_Initialized) InitFileSystem(); for (size_t i = 0; i < m_FileInfoVector.size(); ++i) { - if (!strcasecmp(GetPathFromFSTOffset(i).c_str(), _rFullPath.c_str())) + if (!strcasecmp(GetPathFromFSTOffset(i).c_str(), path.c_str())) return &m_FileInfoVector[i]; } return nullptr; } -u64 FileSystemGCWii::GetFileSize(const std::string& _rFullPath) +const FileInfo* FileSystemGCWii::FindFileInfo(u64 disc_offset) { if (!m_Initialized) InitFileSystem(); - const FileInfo* pFileInfo = FindFileInfo(_rFullPath); + for (auto& file_info : m_FileInfoVector) + { + if ((file_info.GetOffset() <= disc_offset) && + ((file_info.GetOffset() + file_info.GetSize()) > disc_offset)) + { + return &file_info; + } + } - if (pFileInfo != nullptr && !pFileInfo->IsDirectory()) - return pFileInfo->GetSize(); - - return 0; + return nullptr; } std::string FileSystemGCWii::GetPath(u64 _Address) @@ -135,42 +139,39 @@ std::string FileSystemGCWii::GetPathFromFSTOffset(size_t file_info_offset) } } -u64 FileSystemGCWii::ReadFile(const std::string& _rFullPath, u8* _pBuffer, u64 _MaxBufferSize, +u64 FileSystemGCWii::ReadFile(const FileInfo* file_info, u8* _pBuffer, u64 _MaxBufferSize, u64 _OffsetInFile) { if (!m_Initialized) InitFileSystem(); - const FileInfo* pFileInfo = FindFileInfo(_rFullPath); - if (pFileInfo == nullptr) + if (!file_info || file_info->IsDirectory()) return 0; - if (_OffsetInFile >= pFileInfo->GetSize()) + if (_OffsetInFile >= file_info->GetSize()) return 0; - u64 read_length = std::min(_MaxBufferSize, pFileInfo->GetSize() - _OffsetInFile); + u64 read_length = std::min(_MaxBufferSize, file_info->GetSize() - _OffsetInFile); DEBUG_LOG(DISCIO, "Reading %" PRIx64 " bytes at %" PRIx64 " from file %s. Offset: %" PRIx64 " Size: %" PRIx64, - read_length, _OffsetInFile, _rFullPath.c_str(), pFileInfo->GetOffset(), - pFileInfo->GetSize()); + read_length, _OffsetInFile, GetPath(file_info->GetOffset()).c_str(), + file_info->GetOffset(), file_info->GetSize()); - m_rVolume->Read(pFileInfo->GetOffset() + _OffsetInFile, read_length, _pBuffer, m_partition); + m_rVolume->Read(file_info->GetOffset() + _OffsetInFile, read_length, _pBuffer, m_partition); return read_length; } -bool FileSystemGCWii::ExportFile(const std::string& _rFullPath, const std::string& _rExportFilename) +bool FileSystemGCWii::ExportFile(const FileInfo* file_info, const std::string& _rExportFilename) { if (!m_Initialized) InitFileSystem(); - const FileInfo* pFileInfo = FindFileInfo(_rFullPath); - - if (!pFileInfo) + if (!file_info || file_info->IsDirectory()) return false; - u64 remainingSize = pFileInfo->GetSize(); - u64 fileOffset = pFileInfo->GetOffset(); + u64 remainingSize = file_info->GetSize(); + u64 fileOffset = file_info->GetOffset(); File::IOFile f(_rExportFilename, "wb"); if (!f) diff --git a/Source/Core/DiscIO/FileSystemGCWii.h b/Source/Core/DiscIO/FileSystemGCWii.h index 37e972e542..9c31c1bcc8 100644 --- a/Source/Core/DiscIO/FileSystemGCWii.h +++ b/Source/Core/DiscIO/FileSystemGCWii.h @@ -44,12 +44,12 @@ public: bool IsValid() const override { return m_Valid; } const std::vector& GetFileList() override; const FileInfo* FindFileInfo(const std::string& path) override; - u64 GetFileSize(const std::string& _rFullPath) override; + const FileInfo* FindFileInfo(u64 disc_offset) override; std::string GetPath(u64 _Address) override; std::string GetPathFromFSTOffset(size_t file_info_offset) override; - u64 ReadFile(const std::string& _rFullPath, u8* _pBuffer, u64 _MaxBufferSize, + u64 ReadFile(const FileInfo* file_info, u8* _pBuffer, u64 _MaxBufferSize, u64 _OffsetInFile) override; - bool ExportFile(const std::string& _rFullPath, const std::string& _rExportFilename) override; + bool ExportFile(const FileInfo* file_info, const std::string& _rExportFilename) override; bool ExportApploader(const std::string& _rExportFolder) const override; bool ExportDOL(const std::string& _rExportFolder) const override; std::optional GetBootDOLOffset() const override; diff --git a/Source/Core/DiscIO/Filesystem.h b/Source/Core/DiscIO/Filesystem.h index c27d6e9636..22cc9bc47a 100644 --- a/Source/Core/DiscIO/Filesystem.h +++ b/Source/Core/DiscIO/Filesystem.h @@ -40,11 +40,11 @@ public: virtual bool IsValid() const = 0; // TODO: Should only return FileInfo, not FileInfoGCWii virtual const std::vector& GetFileList() = 0; - virtual u64 GetFileSize(const std::string& _rFullPath) = 0; virtual const FileInfo* FindFileInfo(const std::string& path) = 0; - virtual u64 ReadFile(const std::string& _rFullPath, u8* _pBuffer, u64 _MaxBufferSize, + virtual const FileInfo* FindFileInfo(u64 disc_offset) = 0; + virtual u64 ReadFile(const FileInfo* file_info, u8* _pBuffer, u64 _MaxBufferSize, u64 _OffsetInFile = 0) = 0; - virtual bool ExportFile(const std::string& _rFullPath, const std::string& _rExportFilename) = 0; + virtual bool ExportFile(const FileInfo* file_info, const std::string& _rExportFilename) = 0; virtual bool ExportApploader(const std::string& _rExportFolder) const = 0; virtual bool ExportDOL(const std::string& _rExportFolder) const = 0; virtual std::string GetPath(u64 _Address) = 0; diff --git a/Source/Core/DiscIO/VolumeGC.cpp b/Source/Core/DiscIO/VolumeGC.cpp index 39bc029ec2..09f073d075 100644 --- a/Source/Core/DiscIO/VolumeGC.cpp +++ b/Source/Core/DiscIO/VolumeGC.cpp @@ -177,7 +177,11 @@ void VolumeGC::LoadBannerFile() const if (!file_system) return; - size_t file_size = static_cast(file_system->GetFileSize("opening.bnr")); + const FileInfo* file_info = file_system->FindFileInfo("opening.bnr"); + if (!file_info) + return; + + size_t file_size = static_cast(file_info->GetSize()); constexpr int BNR1_MAGIC = 0x31524e42; constexpr int BNR2_MAGIC = 0x32524e42; if (file_size != BNR1_SIZE && file_size != BNR2_SIZE) @@ -186,7 +190,11 @@ void VolumeGC::LoadBannerFile() const return; } - file_system->ReadFile("opening.bnr", reinterpret_cast(&banner_file), file_size); + if (file_size != file_system->ReadFile(file_info, reinterpret_cast(&banner_file), file_size)) + { + WARN_LOG(DISCIO, "Could not read opening.bnr."); + return; + } bool is_bnr1; if (banner_file.id == BNR1_MAGIC && file_size == BNR1_SIZE) diff --git a/Source/Core/DiscIO/VolumeWii.cpp b/Source/Core/DiscIO/VolumeWii.cpp index ceb1fa5788..6b1cf6dafa 100644 --- a/Source/Core/DiscIO/VolumeWii.cpp +++ b/Source/Core/DiscIO/VolumeWii.cpp @@ -274,7 +274,8 @@ std::map VolumeWii::GetLongNames() const return {}; std::vector opening_bnr(NAMES_TOTAL_BYTES); - size_t size = file_system->ReadFile("opening.bnr", opening_bnr.data(), opening_bnr.size(), 0x5C); + const FileInfo* file_info = file_system->FindFileInfo("opening.bnr"); + size_t size = file_system->ReadFile(file_info, opening_bnr.data(), opening_bnr.size(), 0x5C); opening_bnr.resize(size); return ReadWiiNames(opening_bnr); } diff --git a/Source/Core/DolphinWX/ISOProperties/FilesystemPanel.cpp b/Source/Core/DolphinWX/ISOProperties/FilesystemPanel.cpp index ebad7364ef..cb4e3fd059 100644 --- a/Source/Core/DolphinWX/ISOProperties/FilesystemPanel.cpp +++ b/Source/Core/DolphinWX/ISOProperties/FilesystemPanel.cpp @@ -405,12 +405,14 @@ void FilesystemPanel::ExtractSingleFile(const wxString& output_file_path) const // Remove "Partition x/" selection_file_path.erase(0, slash_index + 1); - partition->filesystem->ExportFile(WxStrToStr(selection_file_path), - WxStrToStr(output_file_path)); + partition->filesystem->ExportFile( + partition->filesystem->FindFileInfo(WxStrToStr(selection_file_path)), + WxStrToStr(output_file_path)); } else { - m_filesystem->ExportFile(WxStrToStr(selection_file_path), WxStrToStr(output_file_path)); + m_filesystem->ExportFile(m_filesystem->FindFileInfo(WxStrToStr(selection_file_path)), + WxStrToStr(output_file_path)); } } @@ -456,6 +458,7 @@ void FilesystemPanel::ExtractDirectories(const std::string& full_path, else { // Look for the dir we are going to extract + // TODO: Make this more efficient for (index = 0; index < fst.size(); ++index) { if (filesystem->GetPathFromFSTOffset(index) == full_path) @@ -513,7 +516,7 @@ void FilesystemPanel::ExtractDirectories(const std::string& full_path, StringFromFormat("%s/%s", output_folder.c_str(), path.c_str()); INFO_LOG(DISCIO, "%s", export_name.c_str()); - if (!File::Exists(export_name) && !filesystem->ExportFile(path, export_name)) + if (!File::Exists(export_name) && !filesystem->ExportFile(&fst[index], export_name)) { ERROR_LOG(DISCIO, "Could not export %s", export_name.c_str()); } From f49b64caff2ebfeeaa80bc97d85de8b22f56e588 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Thu, 30 Jul 2015 17:12:44 +0200 Subject: [PATCH 06/15] Filesystem: Rewrite finding file info by path for performance Instead of calling GetPathFromFSTOffset for every file info, FindFileInfo now only looks at names in directories that are included in the path. For the common case of searching for "opening.bnr", this means that only root-level files and directories have to be searched through. --- Source/Core/DiscIO/FileSystemGCWii.cpp | 61 ++++++++++++++++++++++++-- Source/Core/DiscIO/FileSystemGCWii.h | 1 + 2 files changed, 59 insertions(+), 3 deletions(-) diff --git a/Source/Core/DiscIO/FileSystemGCWii.cpp b/Source/Core/DiscIO/FileSystemGCWii.cpp index d799caf435..5f3dd425ab 100644 --- a/Source/Core/DiscIO/FileSystemGCWii.cpp +++ b/Source/Core/DiscIO/FileSystemGCWii.cpp @@ -55,10 +55,65 @@ const FileInfo* FileSystemGCWii::FindFileInfo(const std::string& path) if (!m_Initialized) InitFileSystem(); - for (size_t i = 0; i < m_FileInfoVector.size(); ++i) + return FindFileInfo(path, 0); +} + +const FileInfo* FileSystemGCWii::FindFileInfo(const std::string& path, + size_t search_start_offset) const +{ + // Given a path like "directory1/directory2/fileA.bin", this function will + // find directory1 and then call itself to search for "directory2/fileA.bin". + + if (path.empty() || path == "/") + return &m_FileInfoVector[search_start_offset]; + + // It's only possible to search in directories. Searching in a file is an error + if (!m_FileInfoVector[search_start_offset].IsDirectory()) + return nullptr; + + size_t first_dir_separator = path.find('/'); + const std::string searching_for = path.substr(0, first_dir_separator); + const std::string rest_of_path = + (first_dir_separator != std::string::npos) ? path.substr(first_dir_separator + 1) : ""; + + size_t search_end_offset = m_FileInfoVector[search_start_offset].GetSize(); + search_start_offset++; + while (search_start_offset < search_end_offset) { - if (!strcasecmp(GetPathFromFSTOffset(i).c_str(), path.c_str())) - return &m_FileInfoVector[i]; + const FileInfoGCWii& file_info = m_FileInfoVector[search_start_offset]; + + if (file_info.GetName() == searching_for) + { + // A match is found. The rest of the path is passed on to finish the search. + const FileInfo* result = FindFileInfo(rest_of_path, search_start_offset); + + // If the search wasn't successful, the loop continues, just in case there's a second + // file info that matches searching_for (which probably won't happen in practice) + if (result) + return result; + } + + if (file_info.IsDirectory()) + { + // Skip a directory and everything that it contains + + if (file_info.GetSize() <= search_start_offset) + { + // The next offset (obtained by GetSize) is supposed to be larger than + // the current offset. If an FST is malformed and breaks that rule, + // there's a risk that next offset pointers form a loop. + // To avoid infinite loops, this method returns. + ERROR_LOG(DISCIO, "Invalid next offset in file system"); + return nullptr; + } + + search_start_offset = file_info.GetSize(); + } + else + { + // Skip a single file + search_start_offset++; + } } return nullptr; diff --git a/Source/Core/DiscIO/FileSystemGCWii.h b/Source/Core/DiscIO/FileSystemGCWii.h index 9c31c1bcc8..9380a68061 100644 --- a/Source/Core/DiscIO/FileSystemGCWii.h +++ b/Source/Core/DiscIO/FileSystemGCWii.h @@ -61,6 +61,7 @@ private: u32 m_offset_shift; std::vector m_FileInfoVector; + const FileInfo* FindFileInfo(const std::string& path, size_t search_start_offset) const; std::string GetStringFromOffset(u64 _Offset) const; bool DetectFileSystem(); void InitFileSystem(); From d6ee7ec32c22871ea05a3fc383ceb8139c9227f9 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Thu, 30 Jul 2015 22:18:20 +0200 Subject: [PATCH 07/15] Filesystem: Read the entire FST in one go Instead of using lots of small scattered reads to read the FST, only one big read is used, which is more efficient. This also means that the FST only allocates memory once and stores all strings close to each other - good for the CPU cache. The file info objects use pointers to this FST memory of containing data themselves. Keeping around the big m_FileInfoVector containing objects with only pointers is a bit unnecessary, but that will be fixed soon. --- Source/Core/DiscIO/FileSystemGCWii.cpp | 107 ++++++++++++++----------- Source/Core/DiscIO/FileSystemGCWii.h | 31 ++++--- Source/Core/DiscIO/Filesystem.h | 4 +- 3 files changed, 80 insertions(+), 62 deletions(-) diff --git a/Source/Core/DiscIO/FileSystemGCWii.cpp b/Source/Core/DiscIO/FileSystemGCWii.cpp index 5f3dd425ab..a6e7ee0cd1 100644 --- a/Source/Core/DiscIO/FileSystemGCWii.cpp +++ b/Source/Core/DiscIO/FileSystemGCWii.cpp @@ -22,8 +22,8 @@ namespace DiscIO { -FileInfoGCWii::FileInfoGCWii(u64 name_offset, u64 offset, u64 file_size, std::string name) - : m_NameOffset(name_offset), m_Offset(offset), m_FileSize(file_size), m_Name(name) +FileInfoGCWii::FileInfoGCWii(u8 offset_shift, const u8* fst_entry, const u8* name_table_start) + : m_offset_shift(offset_shift), m_fst_entry(fst_entry), m_name_table_start(name_table_start) { } @@ -31,8 +31,36 @@ FileInfoGCWii::~FileInfoGCWii() { } +u32 FileInfoGCWii::Get(EntryProperty entry_property) const +{ + return Common::swap32(m_fst_entry + sizeof(u32) * static_cast(entry_property)); +} + +u32 FileInfoGCWii::GetSize() const +{ + return Get(EntryProperty::FILE_SIZE); +} + +u64 FileInfoGCWii::GetOffset() const +{ + return static_cast(Get(EntryProperty::FILE_OFFSET)) << (IsDirectory() ? 0 : m_offset_shift); +} + +bool FileInfoGCWii::IsDirectory() const +{ + return (Get(EntryProperty::NAME_OFFSET) & 0xFF000000) != 0; +} + +std::string FileInfoGCWii::GetName() const +{ + // TODO: Should we really always use SHIFT-JIS? + // Some names in Pikmin (NTSC-U) don't make sense without it, but is it correct? + const u8* name = m_name_table_start + (Get(EntryProperty::NAME_OFFSET) & 0xFFFFFF); + return SHIFTJISToUTF8(reinterpret_cast(name)); +} + FileSystemGCWii::FileSystemGCWii(const Volume* _rVolume, const Partition& partition) - : FileSystem(_rVolume, partition), m_Initialized(false), m_Valid(false), m_offset_shift(0) + : FileSystem(_rVolume, partition), m_Initialized(false), m_Valid(false), m_offset_shift(0) { m_Valid = DetectFileSystem(); } @@ -55,6 +83,9 @@ const FileInfo* FileSystemGCWii::FindFileInfo(const std::string& path) if (!m_Initialized) InitFileSystem(); + if (m_FileInfoVector.empty()) + return nullptr; + return FindFileInfo(path, 0); } @@ -209,7 +240,7 @@ u64 FileSystemGCWii::ReadFile(const FileInfo* file_info, u8* _pBuffer, u64 _MaxB u64 read_length = std::min(_MaxBufferSize, file_info->GetSize() - _OffsetInFile); DEBUG_LOG(DISCIO, "Reading %" PRIx64 " bytes at %" PRIx64 " from file %s. Offset: %" PRIx64 - " Size: %" PRIx64, + " Size: %" PRIx32, read_length, _OffsetInFile, GetPath(file_info->GetOffset()).c_str(), file_info->GetOffset(), file_info->GetSize()); @@ -343,17 +374,6 @@ bool FileSystemGCWii::ExportDOL(const std::string& _rExportFolder) const return false; } -std::string FileSystemGCWii::GetStringFromOffset(u64 _Offset) const -{ - std::string data(255, 0x00); - m_rVolume->Read(_Offset, data.size(), (u8*)&data[0], m_partition); - data.erase(std::find(data.begin(), data.end(), 0x00), data.end()); - - // TODO: Should we really always use SHIFT-JIS? - // It makes some filenames in Pikmin (NTSC-U) sane, but is it correct? - return SHIFTJISToUTF8(data); -} - bool FileSystemGCWii::DetectFileSystem() { if (m_rVolume->ReadSwapped(0x18, m_partition) == u32(0x5D1C9EA3)) @@ -374,52 +394,41 @@ void FileSystemGCWii::InitFileSystem() { m_Initialized = true; - // read the whole FST const std::optional fst_offset_unshifted = m_rVolume->ReadSwapped(0x424, m_partition); - if (!fst_offset_unshifted) + const std::optional fst_size_unshifted = m_rVolume->ReadSwapped(0x428, m_partition); + if (!fst_offset_unshifted || !fst_size_unshifted) return; - const u64 FSTOffset = static_cast(*fst_offset_unshifted) << m_offset_shift; - - // read all fileinfos - const std::optional root_name_offset = m_rVolume->ReadSwapped(FSTOffset, m_partition); - const std::optional root_offset = m_rVolume->ReadSwapped(FSTOffset + 0x4, m_partition); - const std::optional root_size = m_rVolume->ReadSwapped(FSTOffset + 0x8, m_partition); - if (!root_name_offset || !root_offset || !root_size) - return; - FileInfoGCWii root(*root_name_offset, static_cast(*root_offset) << m_offset_shift, - *root_size, ""); - - if (!root.IsDirectory()) + const u64 fst_offset = static_cast(*fst_offset_unshifted) << m_offset_shift; + const u64 fst_size = static_cast(*fst_size_unshifted) << m_offset_shift; + if (fst_size < 0xC) return; - // 12 bytes (the size of a file entry) times 10 * 1024 * 1024 is 120 MiB, - // more than total RAM in a Wii. No file system should use anywhere near that much. - static const u32 ARBITRARY_FILE_SYSTEM_SIZE_LIMIT = 10 * 1024 * 1024; - if (root.GetSize() > ARBITRARY_FILE_SYSTEM_SIZE_LIMIT) + // 128 MiB is more than the total amount of RAM in a Wii. + // No file system should use anywhere near that much. + static const u32 ARBITRARY_FILE_SYSTEM_SIZE_LIMIT = 128 * 1024 * 1024; + if (fst_size > ARBITRARY_FILE_SYSTEM_SIZE_LIMIT) { // Without this check, Dolphin can crash by trying to allocate too much - // memory when loading the file systems of certain malformed disc images. + // memory when loading a disc image with an incorrect FST size. ERROR_LOG(DISCIO, "File system is abnormally large! Aborting loading"); return; } - if (m_FileInfoVector.size()) - PanicAlert("Wtf?"); - u64 NameTableOffset = FSTOffset + (root.GetSize() * 0xC); + // Read the whole FST + m_file_system_table.resize(fst_size); + if (!m_rVolume->Read(fst_offset, fst_size, m_file_system_table.data(), m_partition)) + return; - m_FileInfoVector.reserve((size_t)root.GetSize()); - for (u32 i = 0; i < root.GetSize(); i++) - { - const u64 read_offset = FSTOffset + (i * 0xC); - const u32 name_offset = m_rVolume->ReadSwapped(read_offset, m_partition).value_or(0); - const u32 offset = m_rVolume->ReadSwapped(read_offset + 0x4, m_partition).value_or(0); - const u32 size = m_rVolume->ReadSwapped(read_offset + 0x8, m_partition).value_or(0); - const std::string name = GetStringFromOffset(NameTableOffset + (name_offset & 0xFFFFFF)); - m_FileInfoVector.emplace_back( - name_offset, static_cast(offset) << (m_offset_shift * !(name_offset & 0xFF000000)), - size, name); - } + // Create all file info objects + u32 number_of_file_infos = Common::swap32(*((u32*)m_file_system_table.data() + 2)); + const u8* fst_start = m_file_system_table.data(); + const u8* name_table_start = fst_start + (number_of_file_infos * 0xC); + const u8* name_table_end = fst_start + fst_size; + if (name_table_end < name_table_start) + return; + for (u32 i = 0; i < number_of_file_infos; i++) + m_FileInfoVector.emplace_back(m_offset_shift, fst_start + (i * 0xC), name_table_start); } } // namespace diff --git a/Source/Core/DiscIO/FileSystemGCWii.h b/Source/Core/DiscIO/FileSystemGCWii.h index 9380a68061..b4b9fd49c2 100644 --- a/Source/Core/DiscIO/FileSystemGCWii.h +++ b/Source/Core/DiscIO/FileSystemGCWii.h @@ -20,20 +20,29 @@ struct Partition; class FileInfoGCWii : public FileInfo { public: - FileInfoGCWii(u64 name_offset, u64 offset, u64 file_size, std::string name); + // Does not take ownership of pointers + FileInfoGCWii(u8 offset_shift, const u8* fst_entry, const u8* name_table_start); + ~FileInfoGCWii() override; - u64 GetOffset() const override { return m_Offset; } - u64 GetSize() const override { return m_FileSize; } - bool IsDirectory() const override { return (m_NameOffset & 0xFF000000) != 0; } - const std::string& GetName() const override { return m_Name; } - // TODO: These shouldn't be public - std::string m_Name; - const u64 m_NameOffset = 0u; + u64 GetOffset() const override; + u32 GetSize() const override; + bool IsDirectory() const override; + std::string GetName() const override; private: - const u64 m_Offset = 0u; - const u64 m_FileSize = 0u; + enum class EntryProperty + { + NAME_OFFSET = 0, + FILE_OFFSET = 1, + FILE_SIZE = 2 + }; + + u32 Get(EntryProperty entry_property) const; + + const u8 m_offset_shift; + const u8* const m_fst_entry; + const u8* const m_name_table_start; }; class FileSystemGCWii : public FileSystem @@ -60,9 +69,9 @@ private: bool m_Valid; u32 m_offset_shift; std::vector m_FileInfoVector; + std::vector m_file_system_table; const FileInfo* FindFileInfo(const std::string& path, size_t search_start_offset) const; - std::string GetStringFromOffset(u64 _Offset) const; bool DetectFileSystem(); void InitFileSystem(); }; diff --git a/Source/Core/DiscIO/Filesystem.h b/Source/Core/DiscIO/Filesystem.h index 22cc9bc47a..99d89f110a 100644 --- a/Source/Core/DiscIO/Filesystem.h +++ b/Source/Core/DiscIO/Filesystem.h @@ -26,9 +26,9 @@ public: // Not guaranteed to return a meaningful value for directories virtual u64 GetOffset() const = 0; // Not guaranteed to return a meaningful value for directories - virtual u64 GetSize() const = 0; + virtual u32 GetSize() const = 0; virtual bool IsDirectory() const = 0; - virtual const std::string& GetName() const = 0; + virtual std::string GetName() const = 0; }; class FileSystem From afe2bc60f634cf1d417680cefc51b3caeb3cfbf2 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Fri, 31 Jul 2015 13:36:28 +0200 Subject: [PATCH 08/15] Filesystem: Initialize everything in constructor Not initializing until the filesystem is used is good when a filesystem is constructed and then never used, but nobody does that. This simplifies the code a little and lets all methods be const. --- Source/Core/DiscIO/FileSystemGCWii.cpp | 146 ++++++++++--------------- Source/Core/DiscIO/FileSystemGCWii.h | 17 ++- Source/Core/DiscIO/Filesystem.h | 15 +-- 3 files changed, 74 insertions(+), 104 deletions(-) diff --git a/Source/Core/DiscIO/FileSystemGCWii.cpp b/Source/Core/DiscIO/FileSystemGCWii.cpp index a6e7ee0cd1..813201b7a0 100644 --- a/Source/Core/DiscIO/FileSystemGCWii.cpp +++ b/Source/Core/DiscIO/FileSystemGCWii.cpp @@ -60,9 +60,58 @@ std::string FileInfoGCWii::GetName() const } FileSystemGCWii::FileSystemGCWii(const Volume* _rVolume, const Partition& partition) - : FileSystem(_rVolume, partition), m_Initialized(false), m_Valid(false), m_offset_shift(0) + : FileSystem(_rVolume, partition), m_Valid(false), m_offset_shift(0) { - m_Valid = DetectFileSystem(); + // Check if this is a GameCube or Wii disc + if (m_rVolume->ReadSwapped(0x18, m_partition) == u32(0x5D1C9EA3)) + { + m_offset_shift = 2; // Wii file system + m_Valid = true; + } + else if (m_rVolume->ReadSwapped(0x1c, m_partition) == u32(0xC2339F3D)) + { + m_offset_shift = 0; // GameCube file system + m_Valid = true; + } + + if (!m_Valid) + return; + + const std::optional fst_offset_unshifted = m_rVolume->ReadSwapped(0x424, m_partition); + const std::optional fst_size_unshifted = m_rVolume->ReadSwapped(0x428, m_partition); + if (!fst_offset_unshifted || !fst_size_unshifted) + return; + const u64 fst_offset = static_cast(*fst_offset_unshifted) << m_offset_shift; + const u64 fst_size = static_cast(*fst_size_unshifted) << m_offset_shift; + if (fst_size < 0xC) + return; + + // 128 MiB is more than the total amount of RAM in a Wii. + // No file system should use anywhere near that much. + static const u32 ARBITRARY_FILE_SYSTEM_SIZE_LIMIT = 128 * 1024 * 1024; + if (fst_size > ARBITRARY_FILE_SYSTEM_SIZE_LIMIT) + { + // Without this check, Dolphin can crash by trying to allocate too much + // memory when loading a disc image with an incorrect FST size. + + ERROR_LOG(DISCIO, "File system is abnormally large! Aborting loading"); + return; + } + + // Read the whole FST + m_file_system_table.resize(fst_size); + if (!m_rVolume->Read(fst_offset, fst_size, m_file_system_table.data(), m_partition)) + return; + + // Create all file info objects + u32 number_of_file_infos = Common::swap32(*((u32*)m_file_system_table.data() + 2)); + const u8* fst_start = m_file_system_table.data(); + const u8* name_table_start = fst_start + (number_of_file_infos * 0xC); + const u8* name_table_end = fst_start + fst_size; + if (name_table_end < name_table_start) + return; + for (u32 i = 0; i < number_of_file_infos; i++) + m_FileInfoVector.emplace_back(m_offset_shift, fst_start + (i * 0xC), name_table_start); } FileSystemGCWii::~FileSystemGCWii() @@ -70,19 +119,13 @@ FileSystemGCWii::~FileSystemGCWii() m_FileInfoVector.clear(); } -const std::vector& FileSystemGCWii::GetFileList() +const std::vector& FileSystemGCWii::GetFileList() const { - if (!m_Initialized) - InitFileSystem(); - return m_FileInfoVector; } -const FileInfo* FileSystemGCWii::FindFileInfo(const std::string& path) +const FileInfo* FileSystemGCWii::FindFileInfo(const std::string& path) const { - if (!m_Initialized) - InitFileSystem(); - if (m_FileInfoVector.empty()) return nullptr; @@ -150,11 +193,8 @@ const FileInfo* FileSystemGCWii::FindFileInfo(const std::string& path, return nullptr; } -const FileInfo* FileSystemGCWii::FindFileInfo(u64 disc_offset) +const FileInfo* FileSystemGCWii::FindFileInfo(u64 disc_offset) const { - if (!m_Initialized) - InitFileSystem(); - for (auto& file_info : m_FileInfoVector) { if ((file_info.GetOffset() <= disc_offset) && @@ -167,11 +207,8 @@ const FileInfo* FileSystemGCWii::FindFileInfo(u64 disc_offset) return nullptr; } -std::string FileSystemGCWii::GetPath(u64 _Address) +std::string FileSystemGCWii::GetPath(u64 _Address) const { - if (!m_Initialized) - InitFileSystem(); - for (size_t i = 0; i < m_FileInfoVector.size(); ++i) { const FileInfoGCWii& file_info = m_FileInfoVector[i]; @@ -185,11 +222,8 @@ std::string FileSystemGCWii::GetPath(u64 _Address) return ""; } -std::string FileSystemGCWii::GetPathFromFSTOffset(size_t file_info_offset) +std::string FileSystemGCWii::GetPathFromFSTOffset(size_t file_info_offset) const { - if (!m_Initialized) - InitFileSystem(); - // Root entry doesn't have a name if (file_info_offset == 0) return ""; @@ -226,11 +260,8 @@ std::string FileSystemGCWii::GetPathFromFSTOffset(size_t file_info_offset) } u64 FileSystemGCWii::ReadFile(const FileInfo* file_info, u8* _pBuffer, u64 _MaxBufferSize, - u64 _OffsetInFile) + u64 _OffsetInFile) const { - if (!m_Initialized) - InitFileSystem(); - if (!file_info || file_info->IsDirectory()) return 0; @@ -248,11 +279,9 @@ u64 FileSystemGCWii::ReadFile(const FileInfo* file_info, u8* _pBuffer, u64 _MaxB return read_length; } -bool FileSystemGCWii::ExportFile(const FileInfo* file_info, const std::string& _rExportFilename) +bool FileSystemGCWii::ExportFile(const FileInfo* file_info, + const std::string& _rExportFilename) const { - if (!m_Initialized) - InitFileSystem(); - if (!file_info || file_info->IsDirectory()) return false; @@ -374,61 +403,4 @@ bool FileSystemGCWii::ExportDOL(const std::string& _rExportFolder) const return false; } -bool FileSystemGCWii::DetectFileSystem() -{ - if (m_rVolume->ReadSwapped(0x18, m_partition) == u32(0x5D1C9EA3)) - { - m_offset_shift = 2; // Wii file system - return true; - } - else if (m_rVolume->ReadSwapped(0x1c, m_partition) == u32(0xC2339F3D)) - { - m_offset_shift = 0; // GameCube file system - return true; - } - - return false; -} - -void FileSystemGCWii::InitFileSystem() -{ - m_Initialized = true; - - const std::optional fst_offset_unshifted = m_rVolume->ReadSwapped(0x424, m_partition); - const std::optional fst_size_unshifted = m_rVolume->ReadSwapped(0x428, m_partition); - if (!fst_offset_unshifted || !fst_size_unshifted) - return; - const u64 fst_offset = static_cast(*fst_offset_unshifted) << m_offset_shift; - const u64 fst_size = static_cast(*fst_size_unshifted) << m_offset_shift; - if (fst_size < 0xC) - return; - - // 128 MiB is more than the total amount of RAM in a Wii. - // No file system should use anywhere near that much. - static const u32 ARBITRARY_FILE_SYSTEM_SIZE_LIMIT = 128 * 1024 * 1024; - if (fst_size > ARBITRARY_FILE_SYSTEM_SIZE_LIMIT) - { - // Without this check, Dolphin can crash by trying to allocate too much - // memory when loading a disc image with an incorrect FST size. - - ERROR_LOG(DISCIO, "File system is abnormally large! Aborting loading"); - return; - } - - // Read the whole FST - m_file_system_table.resize(fst_size); - if (!m_rVolume->Read(fst_offset, fst_size, m_file_system_table.data(), m_partition)) - return; - - // Create all file info objects - u32 number_of_file_infos = Common::swap32(*((u32*)m_file_system_table.data() + 2)); - const u8* fst_start = m_file_system_table.data(); - const u8* name_table_start = fst_start + (number_of_file_infos * 0xC); - const u8* name_table_end = fst_start + fst_size; - if (name_table_end < name_table_start) - return; - for (u32 i = 0; i < number_of_file_infos; i++) - m_FileInfoVector.emplace_back(m_offset_shift, fst_start + (i * 0xC), name_table_start); -} - } // namespace diff --git a/Source/Core/DiscIO/FileSystemGCWii.h b/Source/Core/DiscIO/FileSystemGCWii.h index b4b9fd49c2..ab4af8d0f0 100644 --- a/Source/Core/DiscIO/FileSystemGCWii.h +++ b/Source/Core/DiscIO/FileSystemGCWii.h @@ -51,29 +51,26 @@ public: FileSystemGCWii(const Volume* _rVolume, const Partition& partition); ~FileSystemGCWii() override; bool IsValid() const override { return m_Valid; } - const std::vector& GetFileList() override; - const FileInfo* FindFileInfo(const std::string& path) override; - const FileInfo* FindFileInfo(u64 disc_offset) override; - std::string GetPath(u64 _Address) override; - std::string GetPathFromFSTOffset(size_t file_info_offset) override; + const std::vector& GetFileList() const override; + const FileInfo* FindFileInfo(const std::string& path) const override; + const FileInfo* FindFileInfo(u64 disc_offset) const override; + std::string GetPath(u64 _Address) const override; + std::string GetPathFromFSTOffset(size_t file_info_offset) const override; u64 ReadFile(const FileInfo* file_info, u8* _pBuffer, u64 _MaxBufferSize, - u64 _OffsetInFile) override; - bool ExportFile(const FileInfo* file_info, const std::string& _rExportFilename) override; + u64 _OffsetInFile) const override; + bool ExportFile(const FileInfo* file_info, const std::string& _rExportFilename) const override; bool ExportApploader(const std::string& _rExportFolder) const override; bool ExportDOL(const std::string& _rExportFolder) const override; std::optional GetBootDOLOffset() const override; std::optional GetBootDOLSize(u64 dol_offset) const override; private: - bool m_Initialized; bool m_Valid; u32 m_offset_shift; std::vector m_FileInfoVector; std::vector m_file_system_table; const FileInfo* FindFileInfo(const std::string& path, size_t search_start_offset) const; - bool DetectFileSystem(); - void InitFileSystem(); }; } // namespace diff --git a/Source/Core/DiscIO/Filesystem.h b/Source/Core/DiscIO/Filesystem.h index 99d89f110a..2f1fe2c82d 100644 --- a/Source/Core/DiscIO/Filesystem.h +++ b/Source/Core/DiscIO/Filesystem.h @@ -39,16 +39,17 @@ public: virtual ~FileSystem(); virtual bool IsValid() const = 0; // TODO: Should only return FileInfo, not FileInfoGCWii - virtual const std::vector& GetFileList() = 0; - virtual const FileInfo* FindFileInfo(const std::string& path) = 0; - virtual const FileInfo* FindFileInfo(u64 disc_offset) = 0; + virtual const std::vector& GetFileList() const = 0; + virtual const FileInfo* FindFileInfo(const std::string& path) const = 0; + virtual const FileInfo* FindFileInfo(u64 disc_offset) const = 0; virtual u64 ReadFile(const FileInfo* file_info, u8* _pBuffer, u64 _MaxBufferSize, - u64 _OffsetInFile = 0) = 0; - virtual bool ExportFile(const FileInfo* file_info, const std::string& _rExportFilename) = 0; + u64 _OffsetInFile = 0) const = 0; + virtual bool ExportFile(const FileInfo* file_info, + const std::string& _rExportFilename) const = 0; virtual bool ExportApploader(const std::string& _rExportFolder) const = 0; virtual bool ExportDOL(const std::string& _rExportFolder) const = 0; - virtual std::string GetPath(u64 _Address) = 0; - virtual std::string GetPathFromFSTOffset(size_t file_info_offset) = 0; + virtual std::string GetPath(u64 _Address) const = 0; + virtual std::string GetPathFromFSTOffset(size_t file_info_offset) const = 0; virtual std::optional GetBootDOLOffset() const = 0; virtual std::optional GetBootDOLSize(u64 dol_offset) const = 0; From 1262f08ac119e5d12ec48688f66e8b12e52b6d29 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Fri, 31 Jul 2015 13:56:29 +0200 Subject: [PATCH 09/15] Filesystem: Better validity checking Now that the FST in read in the constructor, m_Valid can be set to false when there are errors in the FST. --- Source/Core/DiscIO/FileSystemGCWii.cpp | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/Source/Core/DiscIO/FileSystemGCWii.cpp b/Source/Core/DiscIO/FileSystemGCWii.cpp index 813201b7a0..8e8254e21c 100644 --- a/Source/Core/DiscIO/FileSystemGCWii.cpp +++ b/Source/Core/DiscIO/FileSystemGCWii.cpp @@ -64,17 +64,10 @@ FileSystemGCWii::FileSystemGCWii(const Volume* _rVolume, const Partition& partit { // Check if this is a GameCube or Wii disc if (m_rVolume->ReadSwapped(0x18, m_partition) == u32(0x5D1C9EA3)) - { m_offset_shift = 2; // Wii file system - m_Valid = true; - } else if (m_rVolume->ReadSwapped(0x1c, m_partition) == u32(0xC2339F3D)) - { m_offset_shift = 0; // GameCube file system - m_Valid = true; - } - - if (!m_Valid) + else return; const std::optional fst_offset_unshifted = m_rVolume->ReadSwapped(0x424, m_partition); @@ -112,6 +105,9 @@ FileSystemGCWii::FileSystemGCWii(const Volume* _rVolume, const Partition& partit return; for (u32 i = 0; i < number_of_file_infos; i++) m_FileInfoVector.emplace_back(m_offset_shift, fst_start + (i * 0xC), name_table_start); + + // If we haven't returned yet, everything succeeded + m_Valid = true; } FileSystemGCWii::~FileSystemGCWii() From ee2b88ebb627869a6153fe3135df232793f02e87 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Sun, 2 Aug 2015 17:39:03 +0200 Subject: [PATCH 10/15] Filesystem: Store pointer to beginning of FST in file infos Needed for the next commit. --- Source/Core/DiscIO/FileSystemGCWii.cpp | 18 +++++++++++------- Source/Core/DiscIO/FileSystemGCWii.h | 7 ++++--- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/Source/Core/DiscIO/FileSystemGCWii.cpp b/Source/Core/DiscIO/FileSystemGCWii.cpp index 8e8254e21c..9280dbab16 100644 --- a/Source/Core/DiscIO/FileSystemGCWii.cpp +++ b/Source/Core/DiscIO/FileSystemGCWii.cpp @@ -22,8 +22,10 @@ namespace DiscIO { -FileInfoGCWii::FileInfoGCWii(u8 offset_shift, const u8* fst_entry, const u8* name_table_start) - : m_offset_shift(offset_shift), m_fst_entry(fst_entry), m_name_table_start(name_table_start) +constexpr u32 FST_ENTRY_SIZE = 4 * 3; // An FST entry consists of three 32-bit integers + +FileInfoGCWii::FileInfoGCWii(const u8* fst, u8 offset_shift, u32 index, u32 total_file_infos) + : m_fst(fst), m_offset_shift(offset_shift), m_index(index), m_total_file_infos(total_file_infos) { } @@ -33,7 +35,8 @@ FileInfoGCWii::~FileInfoGCWii() u32 FileInfoGCWii::Get(EntryProperty entry_property) const { - return Common::swap32(m_fst_entry + sizeof(u32) * static_cast(entry_property)); + return Common::swap32(m_fst + FST_ENTRY_SIZE * m_index + + sizeof(u32) * static_cast(entry_property)); } u32 FileInfoGCWii::GetSize() const @@ -55,7 +58,8 @@ std::string FileInfoGCWii::GetName() const { // TODO: Should we really always use SHIFT-JIS? // Some names in Pikmin (NTSC-U) don't make sense without it, but is it correct? - const u8* name = m_name_table_start + (Get(EntryProperty::NAME_OFFSET) & 0xFFFFFF); + u32 name_offset = Get(EntryProperty::NAME_OFFSET) & 0xFFFFFF; + const u8* name = m_fst + FST_ENTRY_SIZE * m_total_file_infos + name_offset; return SHIFTJISToUTF8(reinterpret_cast(name)); } @@ -76,7 +80,7 @@ FileSystemGCWii::FileSystemGCWii(const Volume* _rVolume, const Partition& partit return; const u64 fst_offset = static_cast(*fst_offset_unshifted) << m_offset_shift; const u64 fst_size = static_cast(*fst_size_unshifted) << m_offset_shift; - if (fst_size < 0xC) + if (fst_size < FST_ENTRY_SIZE) return; // 128 MiB is more than the total amount of RAM in a Wii. @@ -99,12 +103,12 @@ FileSystemGCWii::FileSystemGCWii(const Volume* _rVolume, const Partition& partit // Create all file info objects u32 number_of_file_infos = Common::swap32(*((u32*)m_file_system_table.data() + 2)); const u8* fst_start = m_file_system_table.data(); - const u8* name_table_start = fst_start + (number_of_file_infos * 0xC); + const u8* name_table_start = fst_start + (FST_ENTRY_SIZE * number_of_file_infos); const u8* name_table_end = fst_start + fst_size; if (name_table_end < name_table_start) return; for (u32 i = 0; i < number_of_file_infos; i++) - m_FileInfoVector.emplace_back(m_offset_shift, fst_start + (i * 0xC), name_table_start); + m_FileInfoVector.emplace_back(fst_start, m_offset_shift, i, number_of_file_infos); // If we haven't returned yet, everything succeeded m_Valid = true; diff --git a/Source/Core/DiscIO/FileSystemGCWii.h b/Source/Core/DiscIO/FileSystemGCWii.h index ab4af8d0f0..d4fb9dd90e 100644 --- a/Source/Core/DiscIO/FileSystemGCWii.h +++ b/Source/Core/DiscIO/FileSystemGCWii.h @@ -21,7 +21,7 @@ class FileInfoGCWii : public FileInfo { public: // Does not take ownership of pointers - FileInfoGCWii(u8 offset_shift, const u8* fst_entry, const u8* name_table_start); + FileInfoGCWii(const u8* fst, u8 offset_shift, u32 index, u32 total_file_infos); ~FileInfoGCWii() override; @@ -40,9 +40,10 @@ private: u32 Get(EntryProperty entry_property) const; + const u8* const m_fst; const u8 m_offset_shift; - const u8* const m_fst_entry; - const u8* const m_name_table_start; + const u32 m_index; + const u32 m_total_file_infos; }; class FileSystemGCWii : public FileSystem From 87916fe09979eea276438deef83ba85fcace91e0 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Sat, 8 Aug 2015 19:59:33 +0200 Subject: [PATCH 11/15] Filesystem: Replace GetFileList() Instead of expecting callers to know how the size of directory file infos relates to which files are in which directories, filesystems now offer a GetRoot() method, and file infos offer a way to get their children. As a bonus, m_FileInfoVector no longer has to be created and kept around in RAM. Only the file info objects that actually are used are created. --- Source/Core/Core/HW/DVD/FileMonitor.cpp | 4 +- Source/Core/DiscIO/DiscScrubber.cpp | 24 +- Source/Core/DiscIO/DiscScrubber.h | 2 + Source/Core/DiscIO/FileSystemGCWii.cpp | 260 ++++++++++-------- Source/Core/DiscIO/FileSystemGCWii.h | 56 +++- Source/Core/DiscIO/Filesystem.h | 104 ++++++- Source/Core/DiscIO/VolumeGC.cpp | 5 +- Source/Core/DiscIO/VolumeWii.cpp | 6 +- .../ISOProperties/FilesystemPanel.cpp | 178 +++++------- .../DolphinWX/ISOProperties/FilesystemPanel.h | 2 +- .../DolphinWX/ISOProperties/ISOProperties.h | 1 + 11 files changed, 374 insertions(+), 268 deletions(-) diff --git a/Source/Core/Core/HW/DVD/FileMonitor.cpp b/Source/Core/Core/HW/DVD/FileMonitor.cpp index 980bc5454f..cc50d0ef61 100644 --- a/Source/Core/Core/HW/DVD/FileMonitor.cpp +++ b/Source/Core/Core/HW/DVD/FileMonitor.cpp @@ -88,13 +88,13 @@ void Log(u64 offset, const DiscIO::Partition& partition) if (!s_filesystem) return; - const DiscIO::FileInfo* file_info = s_filesystem->FindFileInfo(offset); + const std::unique_ptr file_info = s_filesystem->FindFileInfo(offset); // Do nothing if no file was found at that offset if (!file_info) return; - const std::string path = s_filesystem->GetPath(file_info->GetOffset()); + const std::string path = file_info->GetPath(); // Do nothing if we found the same file again if (s_previous_file == path) diff --git a/Source/Core/DiscIO/DiscScrubber.cpp b/Source/Core/DiscIO/DiscScrubber.cpp index 71d1fa16bc..009081cbb6 100644 --- a/Source/Core/DiscIO/DiscScrubber.cpp +++ b/Source/Core/DiscIO/DiscScrubber.cpp @@ -16,8 +16,6 @@ #include "Common/Logging/Log.h" #include "DiscIO/DiscScrubber.h" #include "DiscIO/Filesystem.h" -// TODO: eww -#include "DiscIO/FileSystemGCWii.h" #include "DiscIO/Volume.h" namespace DiscIO @@ -221,17 +219,21 @@ bool DiscScrubber::ParsePartitionData(const Partition& partition, PartitionHeade MarkAsUsedE(partition_data_offset, header->fst_offset, header->fst_size); // Go through the filesystem and mark entries as used - auto& file_list = filesystem->GetFileList(); - for (size_t i = 0; i < file_list.size(); ++i) - { - const std::string path = filesystem->GetPathFromFSTOffset(i); - DEBUG_LOG(DISCIO, "%s", path.empty() ? "/" : path.c_str()); - auto& file = file_list[i]; - if (!file.IsDirectory()) - MarkAsUsedE(partition_data_offset, file.GetOffset(), file.GetSize()); - } + ParseFileSystemData(partition_data_offset, filesystem->GetRoot()); return true; } +void DiscScrubber::ParseFileSystemData(u64 partition_data_offset, const FileInfo& directory) +{ + for (const DiscIO::FileInfo& file_info : directory) + { + DEBUG_LOG(DISCIO, "Scrubbing %s", file_info.GetPath().c_str()); + if (file_info.IsDirectory()) + ParseFileSystemData(partition_data_offset, file_info); + else + MarkAsUsedE(partition_data_offset, file_info.GetOffset(), file_info.GetSize()); + } +} + } // namespace DiscIO diff --git a/Source/Core/DiscIO/DiscScrubber.h b/Source/Core/DiscIO/DiscScrubber.h index 0eda66da48..d61b8422da 100644 --- a/Source/Core/DiscIO/DiscScrubber.h +++ b/Source/Core/DiscIO/DiscScrubber.h @@ -25,6 +25,7 @@ class IOFile; namespace DiscIO { +class FileInfo; class Volume; struct Partition; @@ -64,6 +65,7 @@ private: bool ReadFromVolume(u64 offset, u64& buffer, const Partition& partition); bool ParseDisc(); bool ParsePartitionData(const Partition& partition, PartitionHeader* header); + void ParseFileSystemData(u64 partition_data_offset, const FileInfo& directory); std::string m_filename; std::unique_ptr m_disc; diff --git a/Source/Core/DiscIO/FileSystemGCWii.cpp b/Source/Core/DiscIO/FileSystemGCWii.cpp index 9280dbab16..54b7f6a0e2 100644 --- a/Source/Core/DiscIO/FileSystemGCWii.cpp +++ b/Source/Core/DiscIO/FileSystemGCWii.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -24,15 +25,60 @@ namespace DiscIO { constexpr u32 FST_ENTRY_SIZE = 4 * 3; // An FST entry consists of three 32-bit integers +// Set everything manually. FileInfoGCWii::FileInfoGCWii(const u8* fst, u8 offset_shift, u32 index, u32 total_file_infos) : m_fst(fst), m_offset_shift(offset_shift), m_index(index), m_total_file_infos(total_file_infos) { } +// For the root object only. +// m_fst and m_index must be correctly set before GetSize() is called! +FileInfoGCWii::FileInfoGCWii(const u8* fst, u8 offset_shift) + : m_fst(fst), m_offset_shift(offset_shift), m_index(0), m_total_file_infos(GetSize()) +{ +} + +// Copy data that is common to the whole file system. +FileInfoGCWii::FileInfoGCWii(const FileInfoGCWii& file_info, u32 index) + : FileInfoGCWii(file_info.m_fst, file_info.m_offset_shift, index, file_info.m_total_file_infos) +{ +} + FileInfoGCWii::~FileInfoGCWii() { } +uintptr_t FileInfoGCWii::GetAddress() const +{ + return reinterpret_cast(m_fst + FST_ENTRY_SIZE * m_index); +} + +u32 FileInfoGCWii::GetNextIndex() const +{ + return IsDirectory() ? GetSize() : m_index + 1; +} + +FileInfo& FileInfoGCWii::operator++() +{ + m_index = GetNextIndex(); + return *this; +} + +std::unique_ptr FileInfoGCWii::clone() const +{ + return std::make_unique(*this); +} + +FileInfo::const_iterator FileInfoGCWii::begin() const +{ + return const_iterator(std::make_unique(*this, m_index + 1)); +} + +FileInfo::const_iterator FileInfoGCWii::end() const +{ + return const_iterator(std::make_unique(*this, GetNextIndex())); +} + u32 FileInfoGCWii::Get(EntryProperty entry_property) const { return Common::swap32(m_fst + FST_ENTRY_SIZE * m_index + @@ -41,12 +87,23 @@ u32 FileInfoGCWii::Get(EntryProperty entry_property) const u32 FileInfoGCWii::GetSize() const { - return Get(EntryProperty::FILE_SIZE); + u32 result = Get(EntryProperty::FILE_SIZE); + + if (IsDirectory() && result <= m_index) + { + // For directories, GetSize is supposed to return the index of the next entry. + // If a file system is malformed and instead has an index that isn't after this one, + // we act as if the directory is empty to avoid strange behavior. + ERROR_LOG(DISCIO, "Invalid folder end in file system"); + return m_index + 1; + } + + return result; } u64 FileInfoGCWii::GetOffset() const { - return static_cast(Get(EntryProperty::FILE_OFFSET)) << (IsDirectory() ? 0 : m_offset_shift); + return static_cast(Get(EntryProperty::FILE_OFFSET)) << m_offset_shift; } bool FileInfoGCWii::IsDirectory() const @@ -54,6 +111,11 @@ bool FileInfoGCWii::IsDirectory() const return (Get(EntryProperty::NAME_OFFSET) & 0xFF000000) != 0; } +u32 FileInfoGCWii::GetTotalChildren() const +{ + return GetSize() - (m_index + 1); +} + std::string FileInfoGCWii::GetName() const { // TODO: Should we really always use SHIFT-JIS? @@ -63,8 +125,48 @@ std::string FileInfoGCWii::GetName() const return SHIFTJISToUTF8(reinterpret_cast(name)); } +std::string FileInfoGCWii::GetPath() const +{ + // The root entry doesn't have a name + if (m_index == 0) + return ""; + + if (IsDirectory()) + { + u32 parent_directory_index = Get(EntryProperty::FILE_OFFSET); + if (parent_directory_index >= m_index) + { + // The index of the parent directory is supposed to be smaller than + // the current index. If an FST is malformed and breaks that rule, + // there's a risk that parent directory pointers form a loop. + // To avoid stack overflows, this method returns. + ERROR_LOG(DISCIO, "Invalid parent offset in file system"); + return ""; + } + return FileInfoGCWii(*this, parent_directory_index).GetPath() + GetName() + "/"; + } + else + { + // The parent directory can be found by searching backwards + // for a directory that contains this file. + + FileInfoGCWii potential_parent(*this, m_index - 1); + while (!(potential_parent.IsDirectory() && potential_parent.GetSize() > m_index)) + { + if (potential_parent.m_index == 0) + { + // This can happen if an FST has a root with a size that's too small + ERROR_LOG(DISCIO, "The parent of %s couldn't be found", GetName().c_str()); + return ""; + } + potential_parent = FileInfoGCWii(*this, potential_parent.m_index - 1); + } + return potential_parent.GetPath() + GetName(); + } +} + FileSystemGCWii::FileSystemGCWii(const Volume* _rVolume, const Partition& partition) - : FileSystem(_rVolume, partition), m_Valid(false), m_offset_shift(0) + : FileSystem(_rVolume, partition), m_Valid(false), m_offset_shift(0), m_root(nullptr, 0, 0, 0) { // Check if this is a GameCube or Wii disc if (m_rVolume->ReadSwapped(0x18, m_partition) == u32(0x5D1C9EA3)) @@ -81,7 +183,10 @@ FileSystemGCWii::FileSystemGCWii(const Volume* _rVolume, const Partition& partit const u64 fst_offset = static_cast(*fst_offset_unshifted) << m_offset_shift; const u64 fst_size = static_cast(*fst_size_unshifted) << m_offset_shift; if (fst_size < FST_ENTRY_SIZE) + { + ERROR_LOG(DISCIO, "File system is too small"); return; + } // 128 MiB is more than the total amount of RAM in a Wii. // No file system should use anywhere near that much. @@ -98,17 +203,18 @@ FileSystemGCWii::FileSystemGCWii(const Volume* _rVolume, const Partition& partit // Read the whole FST m_file_system_table.resize(fst_size); if (!m_rVolume->Read(fst_offset, fst_size, m_file_system_table.data(), m_partition)) + { + ERROR_LOG(DISCIO, "Couldn't read file system table"); return; + } - // Create all file info objects - u32 number_of_file_infos = Common::swap32(*((u32*)m_file_system_table.data() + 2)); - const u8* fst_start = m_file_system_table.data(); - const u8* name_table_start = fst_start + (FST_ENTRY_SIZE * number_of_file_infos); - const u8* name_table_end = fst_start + fst_size; - if (name_table_end < name_table_start) + // Create the root object + m_root = FileInfoGCWii(m_file_system_table.data(), m_offset_shift); + if (!m_root.IsDirectory()) + { + ERROR_LOG(DISCIO, "File system root is not a directory"); return; - for (u32 i = 0; i < number_of_file_infos; i++) - m_FileInfoVector.emplace_back(fst_start, m_offset_shift, i, number_of_file_infos); + } // If we haven't returned yet, everything succeeded m_Valid = true; @@ -116,33 +222,32 @@ FileSystemGCWii::FileSystemGCWii(const Volume* _rVolume, const Partition& partit FileSystemGCWii::~FileSystemGCWii() { - m_FileInfoVector.clear(); } -const std::vector& FileSystemGCWii::GetFileList() const +const FileInfo& FileSystemGCWii::GetRoot() const { - return m_FileInfoVector; + return m_root; } -const FileInfo* FileSystemGCWii::FindFileInfo(const std::string& path) const +std::unique_ptr FileSystemGCWii::FindFileInfo(const std::string& path) const { - if (m_FileInfoVector.empty()) + if (!IsValid()) return nullptr; - return FindFileInfo(path, 0); + return FindFileInfo(path, m_root); } -const FileInfo* FileSystemGCWii::FindFileInfo(const std::string& path, - size_t search_start_offset) const +std::unique_ptr FileSystemGCWii::FindFileInfo(const std::string& path, + const FileInfo& file_info) const { // Given a path like "directory1/directory2/fileA.bin", this function will // find directory1 and then call itself to search for "directory2/fileA.bin". if (path.empty() || path == "/") - return &m_FileInfoVector[search_start_offset]; + return file_info.clone(); // It's only possible to search in directories. Searching in a file is an error - if (!m_FileInfoVector[search_start_offset].IsDirectory()) + if (!file_info.IsDirectory()) return nullptr; size_t first_dir_separator = path.find('/'); @@ -150,115 +255,52 @@ const FileInfo* FileSystemGCWii::FindFileInfo(const std::string& path, const std::string rest_of_path = (first_dir_separator != std::string::npos) ? path.substr(first_dir_separator + 1) : ""; - size_t search_end_offset = m_FileInfoVector[search_start_offset].GetSize(); - search_start_offset++; - while (search_start_offset < search_end_offset) + for (const FileInfo& child : file_info) { - const FileInfoGCWii& file_info = m_FileInfoVector[search_start_offset]; - - if (file_info.GetName() == searching_for) + if (child.GetName() == searching_for) { // A match is found. The rest of the path is passed on to finish the search. - const FileInfo* result = FindFileInfo(rest_of_path, search_start_offset); + std::unique_ptr result = FindFileInfo(rest_of_path, child); // If the search wasn't successful, the loop continues, just in case there's a second // file info that matches searching_for (which probably won't happen in practice) if (result) return result; } - - if (file_info.IsDirectory()) - { - // Skip a directory and everything that it contains - - if (file_info.GetSize() <= search_start_offset) - { - // The next offset (obtained by GetSize) is supposed to be larger than - // the current offset. If an FST is malformed and breaks that rule, - // there's a risk that next offset pointers form a loop. - // To avoid infinite loops, this method returns. - ERROR_LOG(DISCIO, "Invalid next offset in file system"); - return nullptr; - } - - search_start_offset = file_info.GetSize(); - } - else - { - // Skip a single file - search_start_offset++; - } } return nullptr; } -const FileInfo* FileSystemGCWii::FindFileInfo(u64 disc_offset) const +std::unique_ptr FileSystemGCWii::FindFileInfo(u64 disc_offset) const { - for (auto& file_info : m_FileInfoVector) + if (!IsValid()) + return nullptr; + + return FindFileInfo(disc_offset, m_root); +} + +std::unique_ptr FileSystemGCWii::FindFileInfo(u64 disc_offset, + const FileInfo& file_info) const +{ + for (const FileInfo& child : file_info) { - if ((file_info.GetOffset() <= disc_offset) && - ((file_info.GetOffset() + file_info.GetSize()) > disc_offset)) + if (child.IsDirectory()) { - return &file_info; + std::unique_ptr result = FindFileInfo(disc_offset, child); + if (result) + return result; + } + else if ((file_info.GetOffset() <= disc_offset) && + ((file_info.GetOffset() + file_info.GetSize()) > disc_offset)) + { + return file_info.clone(); } } return nullptr; } -std::string FileSystemGCWii::GetPath(u64 _Address) const -{ - for (size_t i = 0; i < m_FileInfoVector.size(); ++i) - { - const FileInfoGCWii& file_info = m_FileInfoVector[i]; - if ((file_info.GetOffset() <= _Address) && - ((file_info.GetOffset() + file_info.GetSize()) > _Address)) - { - return GetPathFromFSTOffset(i); - } - } - - return ""; -} - -std::string FileSystemGCWii::GetPathFromFSTOffset(size_t file_info_offset) const -{ - // Root entry doesn't have a name - if (file_info_offset == 0) - return ""; - - const FileInfoGCWii& file_info = m_FileInfoVector[file_info_offset]; - if (file_info.IsDirectory()) - { - // The offset of the parent directory is stored in the current directory. - - if (file_info.GetOffset() >= file_info_offset) - { - // The offset of the parent directory is supposed to be smaller than - // the current offset. If an FST is malformed and breaks that rule, - // there's a risk that parent directory pointers form a loop. - // To avoid stack overflows, this method returns. - ERROR_LOG(DISCIO, "Invalid parent offset in file system"); - return ""; - } - return GetPathFromFSTOffset(file_info.GetOffset()) + file_info.GetName() + "/"; - } - else - { - // The parent directory can be found by searching backwards - // for a directory that contains this file. - - size_t parent_offset = file_info_offset - 1; - while (!(m_FileInfoVector[parent_offset].IsDirectory() && - m_FileInfoVector[parent_offset].GetSize() > file_info_offset)) - { - parent_offset--; - } - return GetPathFromFSTOffset(parent_offset) + file_info.GetName(); - } -} - u64 FileSystemGCWii::ReadFile(const FileInfo* file_info, u8* _pBuffer, u64 _MaxBufferSize, u64 _OffsetInFile) const { @@ -272,8 +314,8 @@ u64 FileSystemGCWii::ReadFile(const FileInfo* file_info, u8* _pBuffer, u64 _MaxB DEBUG_LOG(DISCIO, "Reading %" PRIx64 " bytes at %" PRIx64 " from file %s. Offset: %" PRIx64 " Size: %" PRIx32, - read_length, _OffsetInFile, GetPath(file_info->GetOffset()).c_str(), - file_info->GetOffset(), file_info->GetSize()); + read_length, _OffsetInFile, file_info->GetPath().c_str(), file_info->GetOffset(), + file_info->GetSize()); m_rVolume->Read(file_info->GetOffset() + _OffsetInFile, read_length, _pBuffer, m_partition); return read_length; diff --git a/Source/Core/DiscIO/FileSystemGCWii.h b/Source/Core/DiscIO/FileSystemGCWii.h index d4fb9dd90e..50782dacbf 100644 --- a/Source/Core/DiscIO/FileSystemGCWii.h +++ b/Source/Core/DiscIO/FileSystemGCWii.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include #include #include @@ -20,30 +21,60 @@ struct Partition; class FileInfoGCWii : public FileInfo { public: - // Does not take ownership of pointers + // None of the constructors take ownership of FST pointers + + // Set everything manually FileInfoGCWii(const u8* fst, u8 offset_shift, u32 index, u32 total_file_infos); + // For the root object only + FileInfoGCWii(const u8* fst, u8 offset_shift); + // Copy another object + FileInfoGCWii(const FileInfoGCWii& file_info) = default; + // Copy data that is common to the whole file system + FileInfoGCWii(const FileInfoGCWii& file_info, u32 index); ~FileInfoGCWii() override; + std::unique_ptr clone() const override; + const_iterator begin() const override; + const_iterator end() const override; + u64 GetOffset() const override; u32 GetSize() const override; bool IsDirectory() const override; + u32 GetTotalChildren() const override; std::string GetName() const override; + std::string GetPath() const override; + +protected: + uintptr_t GetAddress() const override; + FileInfo& operator++() override; private: enum class EntryProperty { + // NAME_OFFSET's lower 3 bytes are the name's offset within the name table. + // NAME_OFFSET's upper 1 byte is 1 for directories and 0 for files. NAME_OFFSET = 0, + // For files, FILE_OFFSET is the file offset in the partition, + // and for directories, it's the FST index of the parent directory. + // The root directory has its parent directory index set to 0. FILE_OFFSET = 1, + // For files, FILE_SIZE is the file size, and for directories, + // it's the FST index of the next entry that isn't in the directory. FILE_SIZE = 2 }; + // For files, returns the index of the next item. For directories, + // returns the index of the next item that isn't inside it. + u32 GetNextIndex() const; + // Returns one of the three properties of this FST entry. + // Read the comments in EntryProperty for details. u32 Get(EntryProperty entry_property) const; - const u8* const m_fst; - const u8 m_offset_shift; - const u32 m_index; - const u32 m_total_file_infos; + const u8* m_fst; + u8 m_offset_shift; + u32 m_index; + u32 m_total_file_infos; }; class FileSystemGCWii : public FileSystem @@ -51,12 +82,12 @@ class FileSystemGCWii : public FileSystem public: FileSystemGCWii(const Volume* _rVolume, const Partition& partition); ~FileSystemGCWii() override; + bool IsValid() const override { return m_Valid; } - const std::vector& GetFileList() const override; - const FileInfo* FindFileInfo(const std::string& path) const override; - const FileInfo* FindFileInfo(u64 disc_offset) const override; - std::string GetPath(u64 _Address) const override; - std::string GetPathFromFSTOffset(size_t file_info_offset) const override; + const FileInfo& GetRoot() const override; + std::unique_ptr FindFileInfo(const std::string& path) const override; + std::unique_ptr FindFileInfo(u64 disc_offset) const override; + u64 ReadFile(const FileInfo* file_info, u8* _pBuffer, u64 _MaxBufferSize, u64 _OffsetInFile) const override; bool ExportFile(const FileInfo* file_info, const std::string& _rExportFilename) const override; @@ -68,10 +99,11 @@ public: private: bool m_Valid; u32 m_offset_shift; - std::vector m_FileInfoVector; std::vector m_file_system_table; + FileInfoGCWii m_root; - const FileInfo* FindFileInfo(const std::string& path, size_t search_start_offset) const; + std::unique_ptr FindFileInfo(const std::string& path, const FileInfo& file_info) const; + std::unique_ptr FindFileInfo(u64 disc_offset, const FileInfo& file_info) const; }; } // namespace diff --git a/Source/Core/DiscIO/Filesystem.h b/Source/Core/DiscIO/Filesystem.h index 2f1fe2c82d..363a57b385 100644 --- a/Source/Core/DiscIO/Filesystem.h +++ b/Source/Core/DiscIO/Filesystem.h @@ -4,6 +4,7 @@ #pragma once +#include #include #include #include @@ -14,42 +15,116 @@ namespace DiscIO { -// TODO: eww -class FileInfoGCWii; - // file info of an FST entry class FileInfo { + friend class const_iterator; + public: + class const_iterator final + { + public: + using iterator_category = std::forward_iterator_tag; + using value_type = const FileInfo; + using difference_type = std::ptrdiff_t; + using pointer = value_type*; + using reference = value_type&; + + const_iterator() : m_file_info(nullptr) {} + const_iterator(std::unique_ptr file_info) : m_file_info(std::move(file_info)) {} + const_iterator(const const_iterator& it) : m_file_info(it.m_file_info->clone()) {} + const_iterator(const_iterator&& it) : m_file_info(std::move(it.m_file_info)) {} + ~const_iterator() {} + const_iterator& operator=(const const_iterator& it) + { + m_file_info = it.m_file_info ? it.m_file_info->clone() : nullptr; + return *this; + } + const_iterator& operator=(const_iterator&& it) + { + m_file_info = std::move(it.m_file_info); + return *this; + } + const_iterator& operator++() + { + ++*m_file_info; + return *this; + } + const_iterator operator++(int) + { + const_iterator old = *this; + ++*m_file_info; + return old; + } + bool operator==(const const_iterator& it) const + { + return m_file_info ? (it.m_file_info && *m_file_info == *it.m_file_info) : (!it.m_file_info); + } + bool operator!=(const const_iterator& it) const { return !operator==(it); } + // Incrementing or destroying an iterator will invalidate its returned references and + // pointers, but will not invalidate copies of the iterator or file info object. + const FileInfo& operator*() const { return *m_file_info.get(); } + const FileInfo* operator->() const { return m_file_info.get(); } + private: + std::unique_ptr m_file_info; + }; + virtual ~FileInfo(); - // Not guaranteed to return a meaningful value for directories + bool operator==(const FileInfo& other) const { return GetAddress() == other.GetAddress(); } + bool operator!=(const FileInfo& other) const { return !operator==(other); } + virtual std::unique_ptr clone() const = 0; + virtual const_iterator cbegin() const { return begin(); } + virtual const_iterator cend() const { return end(); } + virtual const_iterator begin() const = 0; + virtual const_iterator end() const = 0; + + // The offset of a file on the disc (inside the partition, if there is one). + // Not guaranteed to return a meaningful value for directories. virtual u64 GetOffset() const = 0; - // Not guaranteed to return a meaningful value for directories + // The size of a file. + // Not guaranteed to return a meaningful value for directories. virtual u32 GetSize() const = 0; virtual bool IsDirectory() const = 0; + // The number of files and directories in a directory, including those in subdirectories. + // Not guaranteed to return a meaningful value for files. + virtual u32 GetTotalChildren() const = 0; virtual std::string GetName() const = 0; + // GetPath will find the parents of the current object and call GetName on them, + // so it's slower than other functions. If you're traversing through folders + // to get a file and its path, building the path while traversing is faster. + virtual std::string GetPath() const = 0; + +protected: + // Only used for comparisons with other file info objects + virtual uintptr_t GetAddress() const = 0; + + // Called by iterators + virtual FileInfo& operator++() = 0; }; class FileSystem { public: FileSystem(const Volume* _rVolume, const Partition& partition); - virtual ~FileSystem(); + + // If IsValid is false, GetRoot must not be called. CreateFileSystem + // takes care of this automatically, so other code is recommended to use it. virtual bool IsValid() const = 0; - // TODO: Should only return FileInfo, not FileInfoGCWii - virtual const std::vector& GetFileList() const = 0; - virtual const FileInfo* FindFileInfo(const std::string& path) const = 0; - virtual const FileInfo* FindFileInfo(u64 disc_offset) const = 0; + // The object returned by GetRoot and all objects created from it + // are only valid for as long as the file system object is valid. + virtual const FileInfo& GetRoot() const = 0; + // Returns nullptr if not found + virtual std::unique_ptr FindFileInfo(const std::string& path) const = 0; + // Returns nullptr if not found + virtual std::unique_ptr FindFileInfo(u64 disc_offset) const = 0; + virtual u64 ReadFile(const FileInfo* file_info, u8* _pBuffer, u64 _MaxBufferSize, u64 _OffsetInFile = 0) const = 0; - virtual bool ExportFile(const FileInfo* file_info, - const std::string& _rExportFilename) const = 0; + virtual bool ExportFile(const FileInfo* file_info, const std::string& _rExportFilename) const = 0; virtual bool ExportApploader(const std::string& _rExportFolder) const = 0; virtual bool ExportDOL(const std::string& _rExportFolder) const = 0; - virtual std::string GetPath(u64 _Address) const = 0; - virtual std::string GetPathFromFSTOffset(size_t file_info_offset) const = 0; virtual std::optional GetBootDOLOffset() const = 0; virtual std::optional GetBootDOLSize(u64 dol_offset) const = 0; @@ -59,6 +134,7 @@ protected: const Partition m_partition; }; +// Returns nullptr if a valid file system could not be created std::unique_ptr CreateFileSystem(const Volume* volume, const Partition& partition); } // namespace diff --git a/Source/Core/DiscIO/VolumeGC.cpp b/Source/Core/DiscIO/VolumeGC.cpp index 09f073d075..af4178682f 100644 --- a/Source/Core/DiscIO/VolumeGC.cpp +++ b/Source/Core/DiscIO/VolumeGC.cpp @@ -177,7 +177,7 @@ void VolumeGC::LoadBannerFile() const if (!file_system) return; - const FileInfo* file_info = file_system->FindFileInfo("opening.bnr"); + std::unique_ptr file_info = file_system->FindFileInfo("opening.bnr"); if (!file_info) return; @@ -190,7 +190,8 @@ void VolumeGC::LoadBannerFile() const return; } - if (file_size != file_system->ReadFile(file_info, reinterpret_cast(&banner_file), file_size)) + if (file_size != + file_system->ReadFile(file_info.get(), reinterpret_cast(&banner_file), file_size)) { WARN_LOG(DISCIO, "Could not read opening.bnr."); return; diff --git a/Source/Core/DiscIO/VolumeWii.cpp b/Source/Core/DiscIO/VolumeWii.cpp index 6b1cf6dafa..b8215527a6 100644 --- a/Source/Core/DiscIO/VolumeWii.cpp +++ b/Source/Core/DiscIO/VolumeWii.cpp @@ -274,9 +274,9 @@ std::map VolumeWii::GetLongNames() const return {}; std::vector opening_bnr(NAMES_TOTAL_BYTES); - const FileInfo* file_info = file_system->FindFileInfo("opening.bnr"); - size_t size = file_system->ReadFile(file_info, opening_bnr.data(), opening_bnr.size(), 0x5C); - opening_bnr.resize(size); + std::unique_ptr file_info = file_system->FindFileInfo("opening.bnr"); + opening_bnr.resize( + file_system->ReadFile(file_info.get(), opening_bnr.data(), opening_bnr.size(), 0x5C)); return ReadWiiNames(opening_bnr); } diff --git a/Source/Core/DolphinWX/ISOProperties/FilesystemPanel.cpp b/Source/Core/DolphinWX/ISOProperties/FilesystemPanel.cpp index cb4e3fd059..e13b96db07 100644 --- a/Source/Core/DolphinWX/ISOProperties/FilesystemPanel.cpp +++ b/Source/Core/DolphinWX/ISOProperties/FilesystemPanel.cpp @@ -5,6 +5,8 @@ #include "DolphinWX/ISOProperties/FilesystemPanel.h" #include +#include +#include #include #include @@ -23,8 +25,6 @@ #include "Common/Logging/Log.h" #include "DiscIO/Enums.h" #include "DiscIO/Filesystem.h" -// TODO: eww -#include "DiscIO/FileSystemGCWii.h" #include "DiscIO/Volume.h" #include "DolphinWX/ISOFile.h" #include "DolphinWX/WxUtils.h" @@ -85,41 +85,22 @@ wxImageList* LoadIconBitmaps(const wxWindow* context) return icon_list; } -size_t CreateDirectoryTree(wxTreeCtrl* tree_ctrl, wxTreeItemId parent, - const std::vector& file_infos, - const size_t first_index, const size_t last_index) +void CreateDirectoryTree(wxTreeCtrl* tree_ctrl, wxTreeItemId parent, + const DiscIO::FileInfo& directory) { - size_t current_index = first_index; - - while (current_index < last_index) + for (const DiscIO::FileInfo& file_info : directory) { - const DiscIO::FileInfoGCWii& file_info = file_infos[current_index]; - - // check next index + const wxString name = StrToWxStr(file_info.GetName()); if (file_info.IsDirectory()) { - const wxTreeItemId item = - tree_ctrl->AppendItem(parent, StrToWxStr(file_info.GetName()), ICON_FOLDER); - current_index = CreateDirectoryTree(tree_ctrl, item, file_infos, current_index + 1, - static_cast(file_info.GetSize())); + wxTreeItemId item = tree_ctrl->AppendItem(parent, name, ICON_FOLDER); + CreateDirectoryTree(tree_ctrl, item, file_info); } else { - tree_ctrl->AppendItem(parent, StrToWxStr(file_info.GetName()), ICON_FILE); - current_index++; + tree_ctrl->AppendItem(parent, name, ICON_FILE); } } - - return current_index; -} - -size_t CreateDirectoryTree(wxTreeCtrl* tree_ctrl, wxTreeItemId parent, - const std::vector& file_infos) -{ - if (file_infos.empty()) - return 0; - - return CreateDirectoryTree(tree_ctrl, parent, file_infos, 1, file_infos.at(0).GetSize()); } WiiPartition* FindWiiPartition(wxTreeCtrl* tree_ctrl, const wxString& label) @@ -201,7 +182,7 @@ bool FilesystemPanel::PopulateFileSystemTree() WiiPartition* const partition = new WiiPartition(std::move(file_system)); m_tree_ctrl->SetItemData(partition_root, partition); - CreateDirectoryTree(m_tree_ctrl, partition_root, partition->filesystem->GetFileList()); + CreateDirectoryTree(m_tree_ctrl, partition_root, partition->filesystem->GetRoot()); if (partitions[i] == m_opened_iso->GetGamePartition()) m_tree_ctrl->Expand(partition_root); @@ -214,7 +195,7 @@ bool FilesystemPanel::PopulateFileSystemTree() if (!m_filesystem) return false; - CreateDirectoryTree(m_tree_ctrl, m_tree_ctrl->GetRootItem(), m_filesystem->GetFileList()); + CreateDirectoryTree(m_tree_ctrl, m_tree_ctrl->GetRootItem(), m_filesystem->GetRoot()); } return true; @@ -382,13 +363,13 @@ void FilesystemPanel::ExtractAllFiles(const wxString& output_folder) while (item.IsOk()) { const auto* const partition = static_cast(m_tree_ctrl->GetItemData(item)); - ExtractDirectories("", WxStrToStr(output_folder), partition->filesystem.get()); + ExtractDirectories("", WxStrToStr(output_folder), *partition->filesystem); item = m_tree_ctrl->GetNextChild(root, cookie); } } else { - ExtractDirectories("", WxStrToStr(output_folder), m_filesystem.get()); + ExtractDirectories("", WxStrToStr(output_folder), *m_filesystem); } } @@ -406,12 +387,12 @@ void FilesystemPanel::ExtractSingleFile(const wxString& output_file_path) const selection_file_path.erase(0, slash_index + 1); partition->filesystem->ExportFile( - partition->filesystem->FindFileInfo(WxStrToStr(selection_file_path)), + partition->filesystem->FindFileInfo(WxStrToStr(selection_file_path)).get(), WxStrToStr(output_file_path)); } else { - m_filesystem->ExportFile(m_filesystem->FindFileInfo(WxStrToStr(selection_file_path)), + m_filesystem->ExportFile(m_filesystem->FindFileInfo(WxStrToStr(selection_file_path)).get(), WxStrToStr(output_file_path)); } } @@ -430,102 +411,71 @@ void FilesystemPanel::ExtractSingleDirectory(const wxString& output_folder) directory_path.erase(0, slash_index + 1); ExtractDirectories(WxStrToStr(directory_path), WxStrToStr(output_folder), - partition->filesystem.get()); + *partition->filesystem); } else { - ExtractDirectories(WxStrToStr(directory_path), WxStrToStr(output_folder), m_filesystem.get()); + ExtractDirectories(WxStrToStr(directory_path), WxStrToStr(output_folder), *m_filesystem); + } +} + +void ExtractDir(const std::string& full_path, const std::string& output_folder, + const DiscIO::FileSystem& file_system, const DiscIO::FileInfo& directory, + const std::function& update_progress) +{ + for (const DiscIO::FileInfo& file_info : directory) + { + const std::string path = full_path + file_info.GetName() + (file_info.IsDirectory() ? "/" : ""); + const std::string output_path = output_folder + DIR_SEP_CHR + path; + + if (update_progress(path)) + return; + + DEBUG_LOG(DISCIO, "%s", output_path.c_str()); + + if (file_info.IsDirectory()) + { + File::CreateFullPath(output_path); + ExtractDir(path, output_folder, file_system, file_info, update_progress); + } + else + { + if (File::Exists(output_path)) + NOTICE_LOG(DISCIO, "%s already exists", output_path.c_str()); + else if (!file_system.ExportFile(&file_info, output_path)) + ERROR_LOG(DISCIO, "Could not export %s", output_path.c_str()); + } } } void FilesystemPanel::ExtractDirectories(const std::string& full_path, const std::string& output_folder, - DiscIO::FileSystem* filesystem) + const DiscIO::FileSystem& filesystem) { - const std::vector& fst = filesystem->GetFileList(); - - u32 index = 0; - u32 size = 0; - - // Extract all - if (full_path.empty()) + if (full_path.empty()) // Root { - size = static_cast(fst.size()); - - filesystem->ExportApploader(output_folder); - filesystem->ExportDOL(output_folder); - } - else - { - // Look for the dir we are going to extract - // TODO: Make this more efficient - for (index = 0; index < fst.size(); ++index) - { - if (filesystem->GetPathFromFSTOffset(index) == full_path) - { - INFO_LOG(DISCIO, "Found the directory at %u", index); - size = static_cast(fst[index].GetSize()); - break; - } - } - - INFO_LOG(DISCIO, "Directory found from %u to %u\nextracting to: %s", index, size, - output_folder.c_str()); + filesystem.ExportApploader(output_folder); + filesystem.ExportDOL(output_folder); } - const auto dialog_title = (index != 0) ? _("Extracting Directory") : _("Extracting All Files"); - wxProgressDialog dialog(dialog_title, _("Extracting..."), static_cast(size - 1), this, + std::unique_ptr file_info = filesystem.FindFileInfo(full_path); + u32 size = file_info->GetTotalChildren(); + u32 progress = 0; + + wxString dialog_title = full_path.empty() ? _("Extracting All Files") : _("Extracting Directory"); + wxProgressDialog dialog(dialog_title, _("Extracting..."), size, this, wxPD_APP_MODAL | wxPD_AUTO_HIDE | wxPD_CAN_ABORT | wxPD_ELAPSED_TIME | wxPD_ESTIMATED_TIME | wxPD_REMAINING_TIME | wxPD_SMOOTH); - // Extraction - for (u32 i = index; i < size; i++) - { - const std::string path = filesystem->GetPathFromFSTOffset(i); + File::CreateFullPath(output_folder + "/" + full_path); + ExtractDir(full_path, output_folder, filesystem, *file_info, [&](const std::string& path) { dialog.SetTitle(wxString::Format( - "%s : %u%%", dialog_title.c_str(), - static_cast((static_cast(i - index) / static_cast(size - index)) * - 100))); - - dialog.Update(i, wxString::Format(_("Extracting %s"), StrToWxStr(path))); - - if (dialog.WasCancelled()) - break; - - if (fst[i].IsDirectory()) - { - const std::string export_name = - StringFromFormat("%s/%s/", output_folder.c_str(), path.c_str()); - INFO_LOG(DISCIO, "%s", export_name.c_str()); - - if (!File::Exists(export_name) && !File::CreateFullPath(export_name)) - { - ERROR_LOG(DISCIO, "Could not create the path %s", export_name.c_str()); - } - else - { - if (!File::IsDirectory(export_name)) - ERROR_LOG(DISCIO, "%s already exists and is not a directory", export_name.c_str()); - - ERROR_LOG(DISCIO, "Folder %s already exists", export_name.c_str()); - } - } - else - { - const std::string export_name = - StringFromFormat("%s/%s", output_folder.c_str(), path.c_str()); - INFO_LOG(DISCIO, "%s", export_name.c_str()); - - if (!File::Exists(export_name) && !filesystem->ExportFile(&fst[index], export_name)) - { - ERROR_LOG(DISCIO, "Could not export %s", export_name.c_str()); - } - else - { - ERROR_LOG(DISCIO, "%s already exists", export_name.c_str()); - } - } - } + "%s : %d%%", dialog_title.c_str(), + static_cast((static_cast(progress) / static_cast(size)) * 100))); + dialog.Update(progress, wxString::Format(_("Extracting %s"), StrToWxStr(path))); + ++progress; + return dialog.WasCancelled(); + }); } wxString FilesystemPanel::BuildFilePathFromSelection() const diff --git a/Source/Core/DolphinWX/ISOProperties/FilesystemPanel.h b/Source/Core/DolphinWX/ISOProperties/FilesystemPanel.h index 969dde1dc4..ae898b8fd5 100644 --- a/Source/Core/DolphinWX/ISOProperties/FilesystemPanel.h +++ b/Source/Core/DolphinWX/ISOProperties/FilesystemPanel.h @@ -51,7 +51,7 @@ private: void ExtractSingleFile(const wxString& output_file_path) const; void ExtractSingleDirectory(const wxString& output_folder); void ExtractDirectories(const std::string& full_path, const std::string& output_folder, - DiscIO::FileSystem* filesystem); + const DiscIO::FileSystem& filesystem); wxString BuildFilePathFromSelection() const; wxString BuildDirectoryPathFromSelection() const; diff --git a/Source/Core/DolphinWX/ISOProperties/ISOProperties.h b/Source/Core/DolphinWX/ISOProperties/ISOProperties.h index 8041b54adb..b3c6f15b00 100644 --- a/Source/Core/DolphinWX/ISOProperties/ISOProperties.h +++ b/Source/Core/DolphinWX/ISOProperties/ISOProperties.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include #include #include From ee27be06d79a88f4e4344966002211a47a9eafeb Mon Sep 17 00:00:00 2001 From: JosJuice Date: Sun, 9 Aug 2015 13:41:41 +0200 Subject: [PATCH 12/15] Filesystem: Add a cache for finding file info by disc offset FileMonitor calls this every time a read happens, and there's no code that only calls this a small number of times, so having a cache is worthwhile. --- Source/Core/DiscIO/FileSystemGCWii.cpp | 34 ++++++++++++++------------ Source/Core/DiscIO/FileSystemGCWii.h | 4 ++- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/Source/Core/DiscIO/FileSystemGCWii.cpp b/Source/Core/DiscIO/FileSystemGCWii.cpp index 54b7f6a0e2..8546e4d4d1 100644 --- a/Source/Core/DiscIO/FileSystemGCWii.cpp +++ b/Source/Core/DiscIO/FileSystemGCWii.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -277,27 +278,28 @@ std::unique_ptr FileSystemGCWii::FindFileInfo(u64 disc_offset) const if (!IsValid()) return nullptr; - return FindFileInfo(disc_offset, m_root); -} - -std::unique_ptr FileSystemGCWii::FindFileInfo(u64 disc_offset, - const FileInfo& file_info) const -{ - for (const FileInfo& child : file_info) + // Build a cache (unless there already is one) + if (m_offset_file_info_cache.empty()) { - if (child.IsDirectory()) + u32 fst_entries = m_root.GetSize(); + for (u32 i = 0; i < fst_entries; i++) { - std::unique_ptr result = FindFileInfo(disc_offset, child); - if (result) - return result; - } - else if ((file_info.GetOffset() <= disc_offset) && - ((file_info.GetOffset() + file_info.GetSize()) > disc_offset)) - { - return file_info.clone(); + FileInfoGCWii file_info(m_root, i); + if (!file_info.IsDirectory()) + m_offset_file_info_cache.emplace(file_info.GetOffset() + file_info.GetSize(), i); } } + // Get the first file that ends after disc_offset + const auto it = m_offset_file_info_cache.upper_bound(disc_offset); + if (it == m_offset_file_info_cache.end()) + return nullptr; + std::unique_ptr result(std::make_unique(m_root, it->second)); + + // If the file's start isn't after disc_offset, success + if (result->GetOffset() <= disc_offset) + return result; + return nullptr; } diff --git a/Source/Core/DiscIO/FileSystemGCWii.h b/Source/Core/DiscIO/FileSystemGCWii.h index 50782dacbf..158aa3e420 100644 --- a/Source/Core/DiscIO/FileSystemGCWii.h +++ b/Source/Core/DiscIO/FileSystemGCWii.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include #include #include @@ -101,9 +102,10 @@ private: u32 m_offset_shift; std::vector m_file_system_table; FileInfoGCWii m_root; + // Maps the end offset of files to FST indexes + mutable std::map m_offset_file_info_cache; std::unique_ptr FindFileInfo(const std::string& path, const FileInfo& file_info) const; - std::unique_ptr FindFileInfo(u64 disc_offset, const FileInfo& file_info) const; }; } // namespace From e3a2cd827a750505d99e83d1eafa98dc0274d9f5 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Tue, 20 Oct 2015 14:32:04 +0200 Subject: [PATCH 13/15] Filesystem: Do more validity checking in the constructor This makes it possible to catch errors earlier so that file systems simply fail to load instead of behaving oddly. It also makes it possible to check for errors that weren't checkable before, like the end of a directory being after the end of the parent directory. --- Source/Core/DiscIO/FileSystemGCWii.cpp | 103 ++++++++++++++++--------- Source/Core/DiscIO/FileSystemGCWii.h | 4 + 2 files changed, 71 insertions(+), 36 deletions(-) diff --git a/Source/Core/DiscIO/FileSystemGCWii.cpp b/Source/Core/DiscIO/FileSystemGCWii.cpp index 8546e4d4d1..8c3ce41b5b 100644 --- a/Source/Core/DiscIO/FileSystemGCWii.cpp +++ b/Source/Core/DiscIO/FileSystemGCWii.cpp @@ -88,18 +88,7 @@ u32 FileInfoGCWii::Get(EntryProperty entry_property) const u32 FileInfoGCWii::GetSize() const { - u32 result = Get(EntryProperty::FILE_SIZE); - - if (IsDirectory() && result <= m_index) - { - // For directories, GetSize is supposed to return the index of the next entry. - // If a file system is malformed and instead has an index that isn't after this one, - // we act as if the directory is empty to avoid strange behavior. - ERROR_LOG(DISCIO, "Invalid folder end in file system"); - return m_index + 1; - } - - return result; + return Get(EntryProperty::FILE_SIZE); } u64 FileInfoGCWii::GetOffset() const @@ -114,16 +103,20 @@ bool FileInfoGCWii::IsDirectory() const u32 FileInfoGCWii::GetTotalChildren() const { - return GetSize() - (m_index + 1); + return Get(EntryProperty::FILE_SIZE) - (m_index + 1); +} + +u64 FileInfoGCWii::GetNameOffset() const +{ + return static_cast(FST_ENTRY_SIZE) * m_total_file_infos + + (Get(EntryProperty::NAME_OFFSET) & 0xFFFFFF); } std::string FileInfoGCWii::GetName() const { // TODO: Should we really always use SHIFT-JIS? // Some names in Pikmin (NTSC-U) don't make sense without it, but is it correct? - u32 name_offset = Get(EntryProperty::NAME_OFFSET) & 0xFFFFFF; - const u8* name = m_fst + FST_ENTRY_SIZE * m_total_file_infos + name_offset; - return SHIFTJISToUTF8(reinterpret_cast(name)); + return SHIFTJISToUTF8(reinterpret_cast(m_fst + GetNameOffset())); } std::string FileInfoGCWii::GetPath() const @@ -135,37 +128,63 @@ std::string FileInfoGCWii::GetPath() const if (IsDirectory()) { u32 parent_directory_index = Get(EntryProperty::FILE_OFFSET); - if (parent_directory_index >= m_index) - { - // The index of the parent directory is supposed to be smaller than - // the current index. If an FST is malformed and breaks that rule, - // there's a risk that parent directory pointers form a loop. - // To avoid stack overflows, this method returns. - ERROR_LOG(DISCIO, "Invalid parent offset in file system"); - return ""; - } return FileInfoGCWii(*this, parent_directory_index).GetPath() + GetName() + "/"; } else { // The parent directory can be found by searching backwards - // for a directory that contains this file. - + // for a directory that contains this file. The search cannot fail, + // because the root directory at index 0 contains all files. FileInfoGCWii potential_parent(*this, m_index - 1); - while (!(potential_parent.IsDirectory() && potential_parent.GetSize() > m_index)) + while (!(potential_parent.IsDirectory() && + potential_parent.Get(EntryProperty::FILE_SIZE) > m_index)) { - if (potential_parent.m_index == 0) - { - // This can happen if an FST has a root with a size that's too small - ERROR_LOG(DISCIO, "The parent of %s couldn't be found", GetName().c_str()); - return ""; - } potential_parent = FileInfoGCWii(*this, potential_parent.m_index - 1); } return potential_parent.GetPath() + GetName(); } } +bool FileInfoGCWii::IsValid(u64 fst_size, const FileInfoGCWii& parent_directory) const +{ + if (GetNameOffset() >= fst_size) + { + ERROR_LOG(DISCIO, "Impossibly large name offset in file system"); + return false; + } + + if (IsDirectory()) + { + if (Get(EntryProperty::FILE_OFFSET) != parent_directory.m_index) + { + ERROR_LOG(DISCIO, "Incorrect parent offset in file system"); + return false; + } + + u32 size = Get(EntryProperty::FILE_SIZE); + + if (size <= m_index) + { + ERROR_LOG(DISCIO, "Impossibly small directory size in file system"); + return false; + } + + if (size > parent_directory.Get(EntryProperty::FILE_SIZE)) + { + ERROR_LOG(DISCIO, "Impossibly large directory size in file system"); + return false; + } + + for (const FileInfo& child : *this) + { + if (!static_cast(child).IsValid(fst_size, *this)) + return false; + } + } + + return true; +} + FileSystemGCWii::FileSystemGCWii(const Volume* _rVolume, const Partition& partition) : FileSystem(_rVolume, partition), m_Valid(false), m_offset_shift(0), m_root(nullptr, 0, 0, 0) { @@ -217,8 +236,20 @@ FileSystemGCWii::FileSystemGCWii(const Volume* _rVolume, const Partition& partit return; } - // If we haven't returned yet, everything succeeded - m_Valid = true; + if (FST_ENTRY_SIZE * m_root.GetSize() > fst_size) + { + ERROR_LOG(DISCIO, "File system has too many entries for its size"); + return; + } + + // If the FST's final byte isn't 0, CFileInfoGCWii::GetName() can read past the end + if (m_file_system_table[fst_size - 1] != 0) + { + ERROR_LOG(DISCIO, "File system does not end with a null byte"); + return; + } + + m_Valid = m_root.IsValid(fst_size, m_root); } FileSystemGCWii::~FileSystemGCWii() diff --git a/Source/Core/DiscIO/FileSystemGCWii.h b/Source/Core/DiscIO/FileSystemGCWii.h index 158aa3e420..52013277b7 100644 --- a/Source/Core/DiscIO/FileSystemGCWii.h +++ b/Source/Core/DiscIO/FileSystemGCWii.h @@ -46,6 +46,8 @@ public: std::string GetName() const override; std::string GetPath() const override; + bool IsValid(u64 fst_size, const FileInfoGCWii& parent_directory) const; + protected: uintptr_t GetAddress() const override; FileInfo& operator++() override; @@ -71,6 +73,8 @@ private: // Returns one of the three properties of this FST entry. // Read the comments in EntryProperty for details. u32 Get(EntryProperty entry_property) const; + // Returns the name offset, excluding the directory identification byte + u64 GetNameOffset() const; const u8* m_fst; u8 m_offset_shift; From 0b7d2e7c688a1e5e45c6d53fdcc89e4663e6d299 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Sun, 9 Aug 2015 20:53:38 +0200 Subject: [PATCH 14/15] Filesystem: Modernize variable names --- Source/Core/DiscIO/FileSystemGCWii.cpp | 88 +++++++++++++------------- Source/Core/DiscIO/FileSystemGCWii.h | 16 ++--- Source/Core/DiscIO/Filesystem.cpp | 4 +- Source/Core/DiscIO/Filesystem.h | 14 ++-- 4 files changed, 61 insertions(+), 61 deletions(-) diff --git a/Source/Core/DiscIO/FileSystemGCWii.cpp b/Source/Core/DiscIO/FileSystemGCWii.cpp index 8c3ce41b5b..62e29b2b5c 100644 --- a/Source/Core/DiscIO/FileSystemGCWii.cpp +++ b/Source/Core/DiscIO/FileSystemGCWii.cpp @@ -185,19 +185,19 @@ bool FileInfoGCWii::IsValid(u64 fst_size, const FileInfoGCWii& parent_directory) return true; } -FileSystemGCWii::FileSystemGCWii(const Volume* _rVolume, const Partition& partition) - : FileSystem(_rVolume, partition), m_Valid(false), m_offset_shift(0), m_root(nullptr, 0, 0, 0) +FileSystemGCWii::FileSystemGCWii(const Volume* volume, const Partition& partition) + : FileSystem(volume, partition), m_valid(false), m_offset_shift(0), m_root(nullptr, 0, 0, 0) { // Check if this is a GameCube or Wii disc - if (m_rVolume->ReadSwapped(0x18, m_partition) == u32(0x5D1C9EA3)) + if (m_volume->ReadSwapped(0x18, m_partition) == u32(0x5D1C9EA3)) m_offset_shift = 2; // Wii file system - else if (m_rVolume->ReadSwapped(0x1c, m_partition) == u32(0xC2339F3D)) + else if (m_volume->ReadSwapped(0x1c, m_partition) == u32(0xC2339F3D)) m_offset_shift = 0; // GameCube file system else return; - const std::optional fst_offset_unshifted = m_rVolume->ReadSwapped(0x424, m_partition); - const std::optional fst_size_unshifted = m_rVolume->ReadSwapped(0x428, m_partition); + const std::optional fst_offset_unshifted = m_volume->ReadSwapped(0x424, m_partition); + const std::optional fst_size_unshifted = m_volume->ReadSwapped(0x428, m_partition); if (!fst_offset_unshifted || !fst_size_unshifted) return; const u64 fst_offset = static_cast(*fst_offset_unshifted) << m_offset_shift; @@ -222,7 +222,7 @@ FileSystemGCWii::FileSystemGCWii(const Volume* _rVolume, const Partition& partit // Read the whole FST m_file_system_table.resize(fst_size); - if (!m_rVolume->Read(fst_offset, fst_size, m_file_system_table.data(), m_partition)) + if (!m_volume->Read(fst_offset, fst_size, m_file_system_table.data(), m_partition)) { ERROR_LOG(DISCIO, "Couldn't read file system table"); return; @@ -249,7 +249,7 @@ FileSystemGCWii::FileSystemGCWii(const Volume* _rVolume, const Partition& partit return; } - m_Valid = m_root.IsValid(fst_size, m_root); + m_valid = m_root.IsValid(fst_size, m_root); } FileSystemGCWii::~FileSystemGCWii() @@ -334,66 +334,66 @@ std::unique_ptr FileSystemGCWii::FindFileInfo(u64 disc_offset) const return nullptr; } -u64 FileSystemGCWii::ReadFile(const FileInfo* file_info, u8* _pBuffer, u64 _MaxBufferSize, - u64 _OffsetInFile) const +u64 FileSystemGCWii::ReadFile(const FileInfo* file_info, u8* buffer, u64 max_buffer_size, + u64 offset_in_file) const { if (!file_info || file_info->IsDirectory()) return 0; - if (_OffsetInFile >= file_info->GetSize()) + if (offset_in_file >= file_info->GetSize()) return 0; - u64 read_length = std::min(_MaxBufferSize, file_info->GetSize() - _OffsetInFile); + u64 read_length = std::min(max_buffer_size, file_info->GetSize() - offset_in_file); DEBUG_LOG(DISCIO, "Reading %" PRIx64 " bytes at %" PRIx64 " from file %s. Offset: %" PRIx64 " Size: %" PRIx32, - read_length, _OffsetInFile, file_info->GetPath().c_str(), file_info->GetOffset(), + read_length, offset_in_file, file_info->GetPath().c_str(), file_info->GetOffset(), file_info->GetSize()); - m_rVolume->Read(file_info->GetOffset() + _OffsetInFile, read_length, _pBuffer, m_partition); + m_volume->Read(file_info->GetOffset() + offset_in_file, read_length, buffer, m_partition); return read_length; } bool FileSystemGCWii::ExportFile(const FileInfo* file_info, - const std::string& _rExportFilename) const + const std::string& export_filename) const { if (!file_info || file_info->IsDirectory()) return false; - u64 remainingSize = file_info->GetSize(); - u64 fileOffset = file_info->GetOffset(); + u64 remaining_size = file_info->GetSize(); + u64 file_offset = file_info->GetOffset(); - File::IOFile f(_rExportFilename, "wb"); + File::IOFile f(export_filename, "wb"); if (!f) return false; bool result = true; - while (remainingSize) + while (remaining_size) { // Limit read size to 128 MB - size_t readSize = (size_t)std::min(remainingSize, (u64)0x08000000); + size_t read_size = (size_t)std::min(remaining_size, (u64)0x08000000); - std::vector buffer(readSize); + std::vector buffer(read_size); - result = m_rVolume->Read(fileOffset, readSize, &buffer[0], m_partition); + result = m_volume->Read(file_offset, read_size, &buffer[0], m_partition); if (!result) break; - f.WriteBytes(&buffer[0], readSize); + f.WriteBytes(&buffer[0], read_size); - remainingSize -= readSize; - fileOffset += readSize; + remaining_size -= read_size; + file_offset += read_size; } return result; } -bool FileSystemGCWii::ExportApploader(const std::string& _rExportFolder) const +bool FileSystemGCWii::ExportApploader(const std::string& export_folder) const { - std::optional apploader_size = m_rVolume->ReadSwapped(0x2440 + 0x14, m_partition); - const std::optional trailer_size = m_rVolume->ReadSwapped(0x2440 + 0x18, m_partition); + std::optional apploader_size = m_volume->ReadSwapped(0x2440 + 0x14, m_partition); + const std::optional trailer_size = m_volume->ReadSwapped(0x2440 + 0x18, m_partition); constexpr u32 header_size = 0x20; if (!apploader_size || !trailer_size) return false; @@ -401,14 +401,14 @@ bool FileSystemGCWii::ExportApploader(const std::string& _rExportFolder) const DEBUG_LOG(DISCIO, "Apploader size -> %x", *apploader_size); std::vector buffer(*apploader_size); - if (m_rVolume->Read(0x2440, *apploader_size, buffer.data(), m_partition)) + if (m_volume->Read(0x2440, *apploader_size, buffer.data(), m_partition)) { - std::string exportName(_rExportFolder + "/apploader.img"); + std::string export_name(export_folder + "/apploader.img"); - File::IOFile AppFile(exportName, "wb"); - if (AppFile) + File::IOFile apploader_file(export_name, "wb"); + if (apploader_file) { - AppFile.WriteBytes(buffer.data(), *apploader_size); + apploader_file.WriteBytes(buffer.data(), *apploader_size); return true; } } @@ -418,7 +418,7 @@ bool FileSystemGCWii::ExportApploader(const std::string& _rExportFolder) const std::optional FileSystemGCWii::GetBootDOLOffset() const { - std::optional offset = m_rVolume->ReadSwapped(0x420, m_partition); + std::optional offset = m_volume->ReadSwapped(0x420, m_partition); return offset ? static_cast(*offset) << m_offset_shift : std::optional(); } @@ -430,9 +430,9 @@ std::optional FileSystemGCWii::GetBootDOLSize(u64 dol_offset) const for (u8 i = 0; i < 7; i++) { const std::optional offset = - m_rVolume->ReadSwapped(dol_offset + 0x00 + i * 4, m_partition); + m_volume->ReadSwapped(dol_offset + 0x00 + i * 4, m_partition); const std::optional size = - m_rVolume->ReadSwapped(dol_offset + 0x90 + i * 4, m_partition); + m_volume->ReadSwapped(dol_offset + 0x90 + i * 4, m_partition); if (!offset || !size) return {}; dol_size = std::max(*offset + *size, dol_size); @@ -442,9 +442,9 @@ std::optional FileSystemGCWii::GetBootDOLSize(u64 dol_offset) const for (u8 i = 0; i < 11; i++) { const std::optional offset = - m_rVolume->ReadSwapped(dol_offset + 0x1c + i * 4, m_partition); + m_volume->ReadSwapped(dol_offset + 0x1c + i * 4, m_partition); const std::optional size = - m_rVolume->ReadSwapped(dol_offset + 0xac + i * 4, m_partition); + m_volume->ReadSwapped(dol_offset + 0xac + i * 4, m_partition); if (!offset || !size) return {}; dol_size = std::max(*offset + *size, dol_size); @@ -453,7 +453,7 @@ std::optional FileSystemGCWii::GetBootDOLSize(u64 dol_offset) const return dol_size; } -bool FileSystemGCWii::ExportDOL(const std::string& _rExportFolder) const +bool FileSystemGCWii::ExportDOL(const std::string& export_folder) const { std::optional dol_offset = GetBootDOLOffset(); if (!dol_offset) @@ -463,14 +463,14 @@ bool FileSystemGCWii::ExportDOL(const std::string& _rExportFolder) const return false; std::vector buffer(*dol_size); - if (m_rVolume->Read(*dol_offset, *dol_size, &buffer[0], m_partition)) + if (m_volume->Read(*dol_offset, *dol_size, buffer.data(), m_partition)) { - std::string exportName(_rExportFolder + "/boot.dol"); + std::string export_name(export_folder + "/boot.dol"); - File::IOFile DolFile(exportName, "wb"); - if (DolFile) + File::IOFile dol_file(export_name, "wb"); + if (dol_file) { - DolFile.WriteBytes(&buffer[0], *dol_size); + dol_file.WriteBytes(&buffer[0], *dol_size); return true; } } diff --git a/Source/Core/DiscIO/FileSystemGCWii.h b/Source/Core/DiscIO/FileSystemGCWii.h index 52013277b7..ee69bfed94 100644 --- a/Source/Core/DiscIO/FileSystemGCWii.h +++ b/Source/Core/DiscIO/FileSystemGCWii.h @@ -85,24 +85,24 @@ private: class FileSystemGCWii : public FileSystem { public: - FileSystemGCWii(const Volume* _rVolume, const Partition& partition); + FileSystemGCWii(const Volume* volume, const Partition& partition); ~FileSystemGCWii() override; - bool IsValid() const override { return m_Valid; } + bool IsValid() const override { return m_valid; } const FileInfo& GetRoot() const override; std::unique_ptr FindFileInfo(const std::string& path) const override; std::unique_ptr FindFileInfo(u64 disc_offset) const override; - u64 ReadFile(const FileInfo* file_info, u8* _pBuffer, u64 _MaxBufferSize, - u64 _OffsetInFile) const override; - bool ExportFile(const FileInfo* file_info, const std::string& _rExportFilename) const override; - bool ExportApploader(const std::string& _rExportFolder) const override; - bool ExportDOL(const std::string& _rExportFolder) const override; + u64 ReadFile(const FileInfo* file_info, u8* buffer, u64 max_buffer_size, + u64 offset_in_file) const override; + bool ExportFile(const FileInfo* file_info, const std::string& export_filename) const override; + bool ExportApploader(const std::string& export_folder) const override; + bool ExportDOL(const std::string& export_folder) const override; std::optional GetBootDOLOffset() const override; std::optional GetBootDOLSize(u64 dol_offset) const override; private: - bool m_Valid; + bool m_valid; u32 m_offset_shift; std::vector m_file_system_table; FileInfoGCWii m_root; diff --git a/Source/Core/DiscIO/Filesystem.cpp b/Source/Core/DiscIO/Filesystem.cpp index 94127e5e90..807fb6c3eb 100644 --- a/Source/Core/DiscIO/Filesystem.cpp +++ b/Source/Core/DiscIO/Filesystem.cpp @@ -13,8 +13,8 @@ FileInfo::~FileInfo() { } -FileSystem::FileSystem(const Volume* _rVolume, const Partition& partition) - : m_rVolume(_rVolume), m_partition(partition) +FileSystem::FileSystem(const Volume* volume, const Partition& partition) + : m_volume(volume), m_partition(partition) { } diff --git a/Source/Core/DiscIO/Filesystem.h b/Source/Core/DiscIO/Filesystem.h index 363a57b385..30f524281d 100644 --- a/Source/Core/DiscIO/Filesystem.h +++ b/Source/Core/DiscIO/Filesystem.h @@ -106,7 +106,7 @@ protected: class FileSystem { public: - FileSystem(const Volume* _rVolume, const Partition& partition); + FileSystem(const Volume* volume, const Partition& partition); virtual ~FileSystem(); // If IsValid is false, GetRoot must not be called. CreateFileSystem @@ -120,17 +120,17 @@ public: // Returns nullptr if not found virtual std::unique_ptr FindFileInfo(u64 disc_offset) const = 0; - virtual u64 ReadFile(const FileInfo* file_info, u8* _pBuffer, u64 _MaxBufferSize, - u64 _OffsetInFile = 0) const = 0; - virtual bool ExportFile(const FileInfo* file_info, const std::string& _rExportFilename) const = 0; - virtual bool ExportApploader(const std::string& _rExportFolder) const = 0; - virtual bool ExportDOL(const std::string& _rExportFolder) const = 0; + virtual u64 ReadFile(const FileInfo* file_info, u8* buffer, u64 max_buffer_size, + u64 offset_in_file = 0) const = 0; + virtual bool ExportFile(const FileInfo* file_info, const std::string& export_filename) const = 0; + virtual bool ExportApploader(const std::string& export_folder) const = 0; + virtual bool ExportDOL(const std::string& export_folder) const = 0; virtual std::optional GetBootDOLOffset() const = 0; virtual std::optional GetBootDOLSize(u64 dol_offset) const = 0; virtual const Partition GetPartition() const { return m_partition; } protected: - const Volume* const m_rVolume; + const Volume* const m_volume; const Partition m_partition; }; From 583406d900bd0ca9111135c6a9e526aed67e8322 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Tue, 13 Jun 2017 23:05:30 +0200 Subject: [PATCH 15/15] Filesystem: Make destructors = default --- Source/Core/DiscIO/FileSystemGCWii.cpp | 8 ++------ Source/Core/DiscIO/Filesystem.cpp | 8 ++------ Source/Core/DiscIO/Filesystem.h | 2 +- 3 files changed, 5 insertions(+), 13 deletions(-) diff --git a/Source/Core/DiscIO/FileSystemGCWii.cpp b/Source/Core/DiscIO/FileSystemGCWii.cpp index 62e29b2b5c..09a171677d 100644 --- a/Source/Core/DiscIO/FileSystemGCWii.cpp +++ b/Source/Core/DiscIO/FileSystemGCWii.cpp @@ -45,9 +45,7 @@ FileInfoGCWii::FileInfoGCWii(const FileInfoGCWii& file_info, u32 index) { } -FileInfoGCWii::~FileInfoGCWii() -{ -} +FileInfoGCWii::~FileInfoGCWii() = default; uintptr_t FileInfoGCWii::GetAddress() const { @@ -252,9 +250,7 @@ FileSystemGCWii::FileSystemGCWii(const Volume* volume, const Partition& partitio m_valid = m_root.IsValid(fst_size, m_root); } -FileSystemGCWii::~FileSystemGCWii() -{ -} +FileSystemGCWii::~FileSystemGCWii() = default; const FileInfo& FileSystemGCWii::GetRoot() const { diff --git a/Source/Core/DiscIO/Filesystem.cpp b/Source/Core/DiscIO/Filesystem.cpp index 807fb6c3eb..6171c3004f 100644 --- a/Source/Core/DiscIO/Filesystem.cpp +++ b/Source/Core/DiscIO/Filesystem.cpp @@ -9,18 +9,14 @@ namespace DiscIO { -FileInfo::~FileInfo() -{ -} +FileInfo::~FileInfo() = default; FileSystem::FileSystem(const Volume* volume, const Partition& partition) : m_volume(volume), m_partition(partition) { } -FileSystem::~FileSystem() -{ -} +FileSystem::~FileSystem() = default; std::unique_ptr CreateFileSystem(const Volume* volume, const Partition& partition) { diff --git a/Source/Core/DiscIO/Filesystem.h b/Source/Core/DiscIO/Filesystem.h index 30f524281d..879c6fbbdd 100644 --- a/Source/Core/DiscIO/Filesystem.h +++ b/Source/Core/DiscIO/Filesystem.h @@ -34,7 +34,7 @@ public: const_iterator(std::unique_ptr file_info) : m_file_info(std::move(file_info)) {} const_iterator(const const_iterator& it) : m_file_info(it.m_file_info->clone()) {} const_iterator(const_iterator&& it) : m_file_info(std::move(it.m_file_info)) {} - ~const_iterator() {} + ~const_iterator() = default; const_iterator& operator=(const const_iterator& it) { m_file_info = it.m_file_info ? it.m_file_info->clone() : nullptr;