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()); }