summaryrefslogtreecommitdiff
path: root/Source/Core/DiscIO/FileSystemGCWii.cpp
diff options
context:
space:
mode:
authorJosJuice <josjuice@gmail.com>2015-08-08 19:59:33 +0200
committerJosJuice <josjuice@gmail.com>2017-06-14 15:23:48 +0200
commit87916fe09979eea276438deef83ba85fcace91e0 (patch)
treeb8b20059d8c1d7e8cdd310d8d54a37ad2d4ed819 /Source/Core/DiscIO/FileSystemGCWii.cpp
parentee2b88ebb627869a6153fe3135df232793f02e87 (diff)
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.
Diffstat (limited to 'Source/Core/DiscIO/FileSystemGCWii.cpp')
-rw-r--r--Source/Core/DiscIO/FileSystemGCWii.cpp250
1 files changed, 146 insertions, 104 deletions
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 <cinttypes>
#include <cstddef>
#include <cstring>
+#include <memory>
#include <optional>
#include <string>
#include <vector>
@@ -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<uintptr_t>(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<FileInfo> FileInfoGCWii::clone() const
+{
+ return std::make_unique<FileInfoGCWii>(*this);
+}
+
+FileInfo::const_iterator FileInfoGCWii::begin() const
+{
+ return const_iterator(std::make_unique<FileInfoGCWii>(*this, m_index + 1));
+}
+
+FileInfo::const_iterator FileInfoGCWii::end() const
+{
+ return const_iterator(std::make_unique<FileInfoGCWii>(*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<u64>(Get(EntryProperty::FILE_OFFSET)) << (IsDirectory() ? 0 : m_offset_shift);
+ return static_cast<u64>(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<const char*>(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<u32>(0x18, m_partition) == u32(0x5D1C9EA3))
@@ -81,7 +183,10 @@ FileSystemGCWii::FileSystemGCWii(const Volume* _rVolume, const Partition& partit
const u64 fst_offset = static_cast<u64>(*fst_offset_unshifted) << m_offset_shift;
const u64 fst_size = static_cast<u64>(*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<FileInfoGCWii>& 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<FileInfo> 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<FileInfo> 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,113 +255,50 @@ 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<FileInfo> 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<FileInfo> FileSystemGCWii::FindFileInfo(u64 disc_offset) const
{
- for (auto& file_info : m_FileInfoVector)
- {
- if ((file_info.GetOffset() <= disc_offset) &&
- ((file_info.GetOffset() + file_info.GetSize()) > disc_offset))
- {
- return &file_info;
- }
- }
+ if (!IsValid())
+ return nullptr;
- return nullptr;
+ return FindFileInfo(disc_offset, m_root);
}
-std::string FileSystemGCWii::GetPath(u64 _Address) const
+std::unique_ptr<FileInfo> FileSystemGCWii::FindFileInfo(u64 disc_offset,
+ const FileInfo& file_info) const
{
- for (size_t i = 0; i < m_FileInfoVector.size(); ++i)
+ for (const FileInfo& child : file_info)
{
- const FileInfoGCWii& file_info = m_FileInfoVector[i];
- if ((file_info.GetOffset() <= _Address) &&
- ((file_info.GetOffset() + file_info.GetSize()) > _Address))
+ if (child.IsDirectory())
{
- return GetPathFromFSTOffset(i);
+ std::unique_ptr<FileInfo> result = FindFileInfo(disc_offset, child);
+ if (result)
+ return result;
}
- }
-
- 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)
+ else if ((file_info.GetOffset() <= disc_offset) &&
+ ((file_info.GetOffset() + file_info.GetSize()) > disc_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 file_info.clone();
}
- 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();
- }
+ return nullptr;
}
u64 FileSystemGCWii::ReadFile(const FileInfo* file_info, u8* _pBuffer, u64 _MaxBufferSize,
@@ -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;