From f49b64caff2ebfeeaa80bc97d85de8b22f56e588 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Thu, 30 Jul 2015 17:12:44 +0200 Subject: [PATCH] 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();