| 
									
										
										
										
											2015-05-24 06:55:12 +02:00
										 |  |  | // Copyright 2008 Dolphin Emulator Project
 | 
					
						
							| 
									
										
										
										
											2015-05-18 01:08:10 +02:00
										 |  |  | // Licensed under GPLv2+
 | 
					
						
							| 
									
										
										
										
											2013-04-17 23:09:55 -04:00
										 |  |  | // Refer to the license.txt file included.
 | 
					
						
							| 
									
										
										
										
											2008-12-08 05:30:24 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-03 16:51:26 -06:00
										 |  |  | #include <algorithm>
 | 
					
						
							| 
									
										
										
										
											2013-10-26 11:55:41 +02:00
										 |  |  | #include <cinttypes>
 | 
					
						
							| 
									
										
										
										
											2014-02-21 01:47:53 +01:00
										 |  |  | #include <cstddef>
 | 
					
						
							|  |  |  | #include <cstring>
 | 
					
						
							| 
									
										
										
										
											2015-08-09 13:41:41 +02:00
										 |  |  | #include <map>
 | 
					
						
							| 
									
										
										
										
											2015-08-08 19:59:33 +02:00
										 |  |  | #include <memory>
 | 
					
						
							| 
									
										
										
										
											2017-06-04 10:33:14 +02:00
										 |  |  | #include <optional>
 | 
					
						
							| 
									
										
										
										
											2014-02-17 05:18:15 -05:00
										 |  |  | #include <string>
 | 
					
						
							|  |  |  | #include <vector>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-22 10:39:36 +02:00
										 |  |  | #include "Common/CommonFuncs.h"
 | 
					
						
							| 
									
										
										
										
											2014-09-07 20:06:58 -05:00
										 |  |  | #include "Common/CommonTypes.h"
 | 
					
						
							| 
									
										
										
										
											2017-01-15 21:46:32 +01:00
										 |  |  | #include "Common/File.h"
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  | #include "Common/Logging/Log.h"
 | 
					
						
							| 
									
										
										
										
											2015-09-26 17:13:07 -04:00
										 |  |  | #include "Common/MsgHandler.h"
 | 
					
						
							| 
									
										
										
										
											2014-02-17 05:18:15 -05:00
										 |  |  | #include "Common/StringUtil.h"
 | 
					
						
							| 
									
										
										
										
											2017-06-20 17:44:37 +02:00
										 |  |  | #include "DiscIO/DiscExtractor.h"
 | 
					
						
							| 
									
										
										
										
											2014-02-17 05:18:15 -05:00
										 |  |  | #include "DiscIO/FileSystemGCWii.h"
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  | #include "DiscIO/Filesystem.h"
 | 
					
						
							| 
									
										
										
										
											2014-02-21 01:47:53 +01:00
										 |  |  | #include "DiscIO/Volume.h"
 | 
					
						
							| 
									
										
										
										
											2008-12-08 05:30:24 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | namespace DiscIO | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2015-08-02 17:39:03 +02:00
										 |  |  | constexpr u32 FST_ENTRY_SIZE = 4 * 3;  // An FST entry consists of three 32-bit integers
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-08 19:59:33 +02:00
										 |  |  | // Set everything manually.
 | 
					
						
							| 
									
										
										
										
											2015-08-02 17:39:03 +02:00
										 |  |  | 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) | 
					
						
							| 
									
										
										
										
											2015-07-28 16:56:25 +02:00
										 |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-08 19:59:33 +02:00
										 |  |  | // 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) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-13 23:05:30 +02:00
										 |  |  | FileInfoGCWii::~FileInfoGCWii() = default; | 
					
						
							| 
									
										
										
										
											2015-07-28 16:56:25 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-08 19:59:33 +02:00
										 |  |  | 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())); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-07-30 22:18:20 +02:00
										 |  |  | u32 FileInfoGCWii::Get(EntryProperty entry_property) const | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2015-08-02 17:39:03 +02:00
										 |  |  |   return Common::swap32(m_fst + FST_ENTRY_SIZE * m_index + | 
					
						
							|  |  |  |                         sizeof(u32) * static_cast<int>(entry_property)); | 
					
						
							| 
									
										
										
										
											2015-07-30 22:18:20 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | u32 FileInfoGCWii::GetSize() const | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2015-10-20 14:32:04 +02:00
										 |  |  |   return Get(EntryProperty::FILE_SIZE); | 
					
						
							| 
									
										
										
										
											2015-07-30 22:18:20 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | u64 FileInfoGCWii::GetOffset() const | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2015-08-08 19:59:33 +02:00
										 |  |  |   return static_cast<u64>(Get(EntryProperty::FILE_OFFSET)) << m_offset_shift; | 
					
						
							| 
									
										
										
										
											2015-07-30 22:18:20 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool FileInfoGCWii::IsDirectory() const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   return (Get(EntryProperty::NAME_OFFSET) & 0xFF000000) != 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-08 19:59:33 +02:00
										 |  |  | u32 FileInfoGCWii::GetTotalChildren() const | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2015-10-20 14:32:04 +02:00
										 |  |  |   return Get(EntryProperty::FILE_SIZE) - (m_index + 1); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | u64 FileInfoGCWii::GetNameOffset() const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   return static_cast<u64>(FST_ENTRY_SIZE) * m_total_file_infos + | 
					
						
							|  |  |  |          (Get(EntryProperty::NAME_OFFSET) & 0xFFFFFF); | 
					
						
							| 
									
										
										
										
											2015-08-08 19:59:33 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-07-30 22:18:20 +02:00
										 |  |  | 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?
 | 
					
						
							| 
									
										
										
										
											2015-10-20 14:32:04 +02:00
										 |  |  |   return SHIFTJISToUTF8(reinterpret_cast<const char*>(m_fst + GetNameOffset())); | 
					
						
							| 
									
										
										
										
											2015-07-30 22:18:20 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-08 19:59:33 +02:00
										 |  |  | 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); | 
					
						
							|  |  |  |     return FileInfoGCWii(*this, parent_directory_index).GetPath() + GetName() + "/"; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   else | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     // The parent directory can be found by searching backwards
 | 
					
						
							| 
									
										
										
										
											2015-10-20 14:32:04 +02:00
										 |  |  |     // for a directory that contains this file. The search cannot fail,
 | 
					
						
							|  |  |  |     // because the root directory at index 0 contains all files.
 | 
					
						
							| 
									
										
										
										
											2015-08-08 19:59:33 +02:00
										 |  |  |     FileInfoGCWii potential_parent(*this, m_index - 1); | 
					
						
							| 
									
										
										
										
											2015-10-20 14:32:04 +02:00
										 |  |  |     while (!(potential_parent.IsDirectory() && | 
					
						
							|  |  |  |              potential_parent.Get(EntryProperty::FILE_SIZE) > m_index)) | 
					
						
							| 
									
										
										
										
											2015-08-08 19:59:33 +02:00
										 |  |  |     { | 
					
						
							|  |  |  |       potential_parent = FileInfoGCWii(*this, potential_parent.m_index - 1); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return potential_parent.GetPath() + GetName(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-20 14:32:04 +02:00
										 |  |  | 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<const FileInfoGCWii&>(child).IsValid(fst_size, *this)) | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-09 20:53:38 +02:00
										 |  |  | FileSystemGCWii::FileSystemGCWii(const Volume* volume, const Partition& partition) | 
					
						
							| 
									
										
										
										
											2017-08-02 18:34:44 +02:00
										 |  |  |     : m_valid(false), m_root(nullptr, 0, 0, 0) | 
					
						
							| 
									
										
										
										
											2008-12-08 05:30:24 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2017-06-07 11:49:34 +02:00
										 |  |  |   u8 offset_shift; | 
					
						
							| 
									
										
										
										
											2015-07-31 13:36:28 +02:00
										 |  |  |   // Check if this is a GameCube or Wii disc
 | 
					
						
							| 
									
										
										
										
											2017-08-02 18:34:44 +02:00
										 |  |  |   if (volume->ReadSwapped<u32>(0x18, partition) == u32(0x5D1C9EA3)) | 
					
						
							| 
									
										
										
										
											2017-06-07 11:49:34 +02:00
										 |  |  |     offset_shift = 2;  // Wii file system
 | 
					
						
							| 
									
										
										
										
											2017-08-02 18:34:44 +02:00
										 |  |  |   else if (volume->ReadSwapped<u32>(0x1c, partition) == u32(0xC2339F3D)) | 
					
						
							| 
									
										
										
										
											2017-06-07 11:49:34 +02:00
										 |  |  |     offset_shift = 0;  // GameCube file system
 | 
					
						
							| 
									
										
										
										
											2015-07-31 13:56:29 +02:00
										 |  |  |   else | 
					
						
							| 
									
										
										
										
											2017-06-07 11:49:34 +02:00
										 |  |  |     return;  // Invalid partition (maybe someone removed its data but not its partition table entry)
 | 
					
						
							| 
									
										
										
										
											2015-07-31 13:36:28 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-02 18:34:44 +02:00
										 |  |  |   const std::optional<u64> fst_offset = GetFSTOffset(*volume, partition); | 
					
						
							|  |  |  |   const std::optional<u64> fst_size = GetFSTSize(*volume, partition); | 
					
						
							| 
									
										
										
										
											2017-06-20 17:44:37 +02:00
										 |  |  |   if (!fst_offset || !fst_size) | 
					
						
							| 
									
										
										
										
											2015-07-31 13:36:28 +02:00
										 |  |  |     return; | 
					
						
							| 
									
										
										
										
											2017-06-20 17:44:37 +02:00
										 |  |  |   if (*fst_size < FST_ENTRY_SIZE) | 
					
						
							| 
									
										
										
										
											2015-08-08 19:59:33 +02:00
										 |  |  |   { | 
					
						
							|  |  |  |     ERROR_LOG(DISCIO, "File system is too small"); | 
					
						
							| 
									
										
										
										
											2015-07-31 13:36:28 +02:00
										 |  |  |     return; | 
					
						
							| 
									
										
										
										
											2015-08-08 19:59:33 +02:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2015-07-31 13:36:28 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // 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; | 
					
						
							| 
									
										
										
										
											2017-06-20 17:44:37 +02:00
										 |  |  |   if (*fst_size > ARBITRARY_FILE_SYSTEM_SIZE_LIMIT) | 
					
						
							| 
									
										
										
										
											2015-07-31 13:36:28 +02:00
										 |  |  |   { | 
					
						
							|  |  |  |     // 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
 | 
					
						
							| 
									
										
										
										
											2017-06-20 17:44:37 +02:00
										 |  |  |   m_file_system_table.resize(*fst_size); | 
					
						
							| 
									
										
										
										
											2017-08-02 18:34:44 +02:00
										 |  |  |   if (!volume->Read(*fst_offset, *fst_size, m_file_system_table.data(), partition)) | 
					
						
							| 
									
										
										
										
											2015-08-08 19:59:33 +02:00
										 |  |  |   { | 
					
						
							|  |  |  |     ERROR_LOG(DISCIO, "Couldn't read file system table"); | 
					
						
							| 
									
										
										
										
											2015-07-31 13:36:28 +02:00
										 |  |  |     return; | 
					
						
							| 
									
										
										
										
											2015-08-08 19:59:33 +02:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2015-07-31 13:36:28 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-08 19:59:33 +02:00
										 |  |  |   // Create the root object
 | 
					
						
							| 
									
										
										
										
											2017-06-07 11:49:34 +02:00
										 |  |  |   m_root = FileInfoGCWii(m_file_system_table.data(), offset_shift); | 
					
						
							| 
									
										
										
										
											2015-08-08 19:59:33 +02:00
										 |  |  |   if (!m_root.IsDirectory()) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     ERROR_LOG(DISCIO, "File system root is not a directory"); | 
					
						
							| 
									
										
										
										
											2015-07-31 13:36:28 +02:00
										 |  |  |     return; | 
					
						
							| 
									
										
										
										
											2015-08-08 19:59:33 +02:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2015-07-31 13:56:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-20 17:44:37 +02:00
										 |  |  |   if (FST_ENTRY_SIZE * m_root.GetSize() > *fst_size) | 
					
						
							| 
									
										
										
										
											2015-10-20 14:32:04 +02:00
										 |  |  |   { | 
					
						
							|  |  |  |     ERROR_LOG(DISCIO, "File system has too many entries for its size"); | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-20 17:44:37 +02:00
										 |  |  |   // If the FST's final byte isn't 0, FileInfoGCWii::GetName() can read past the end
 | 
					
						
							|  |  |  |   if (m_file_system_table[*fst_size - 1] != 0) | 
					
						
							| 
									
										
										
										
											2015-10-20 14:32:04 +02:00
										 |  |  |   { | 
					
						
							|  |  |  |     ERROR_LOG(DISCIO, "File system does not end with a null byte"); | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-20 17:44:37 +02:00
										 |  |  |   m_valid = m_root.IsValid(*fst_size, m_root); | 
					
						
							| 
									
										
										
										
											2008-12-08 05:30:24 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-13 23:05:30 +02:00
										 |  |  | FileSystemGCWii::~FileSystemGCWii() = default; | 
					
						
							| 
									
										
										
										
											2008-12-08 05:30:24 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-08 19:59:33 +02:00
										 |  |  | const FileInfo& FileSystemGCWii::GetRoot() const | 
					
						
							| 
									
										
										
										
											2015-07-29 17:27:43 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2015-08-08 19:59:33 +02:00
										 |  |  |   return m_root; | 
					
						
							| 
									
										
										
										
											2015-07-29 17:27:43 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-08 19:59:33 +02:00
										 |  |  | std::unique_ptr<FileInfo> FileSystemGCWii::FindFileInfo(const std::string& path) const | 
					
						
							| 
									
										
										
										
											2015-07-29 17:27:43 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2015-08-08 19:59:33 +02:00
										 |  |  |   if (!IsValid()) | 
					
						
							| 
									
										
										
										
											2015-07-30 22:18:20 +02:00
										 |  |  |     return nullptr; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-08 19:59:33 +02:00
										 |  |  |   return FindFileInfo(path, m_root); | 
					
						
							| 
									
										
										
										
											2015-07-30 17:12:44 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-08 19:59:33 +02:00
										 |  |  | std::unique_ptr<FileInfo> FileSystemGCWii::FindFileInfo(const std::string& path, | 
					
						
							|  |  |  |                                                         const FileInfo& file_info) const | 
					
						
							| 
									
										
										
										
											2015-07-30 17:12:44 +02:00
										 |  |  | { | 
					
						
							|  |  |  |   // Given a path like "directory1/directory2/fileA.bin", this function will
 | 
					
						
							|  |  |  |   // find directory1 and then call itself to search for "directory2/fileA.bin".
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-16 13:19:18 +02:00
										 |  |  |   const size_t name_start = path.find_first_not_of('/'); | 
					
						
							|  |  |  |   if (name_start == std::string::npos) | 
					
						
							| 
									
										
										
										
											2017-07-04 20:25:53 +02:00
										 |  |  |     return file_info.clone();  // We're done
 | 
					
						
							| 
									
										
										
										
											2015-07-30 17:12:44 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-04 20:25:53 +02:00
										 |  |  |   const size_t name_end = path.find('/', name_start); | 
					
						
							|  |  |  |   const std::string name = path.substr(name_start, name_end - name_start); | 
					
						
							|  |  |  |   const std::string rest_of_path = (name_end != std::string::npos) ? path.substr(name_end + 1) : ""; | 
					
						
							| 
									
										
										
										
											2015-07-30 17:12:44 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-08 19:59:33 +02:00
										 |  |  |   for (const FileInfo& child : file_info) | 
					
						
							| 
									
										
										
										
											2015-07-29 17:27:43 +02:00
										 |  |  |   { | 
					
						
							| 
									
										
										
										
											2017-07-04 20:25:53 +02:00
										 |  |  |     if (!strcasecmp(child.GetName().c_str(), name.c_str())) | 
					
						
							| 
									
										
										
										
											2015-07-30 17:12:44 +02:00
										 |  |  |     { | 
					
						
							|  |  |  |       // A match is found. The rest of the path is passed on to finish the search.
 | 
					
						
							| 
									
										
										
										
											2015-08-08 19:59:33 +02:00
										 |  |  |       std::unique_ptr<FileInfo> result = FindFileInfo(rest_of_path, child); | 
					
						
							| 
									
										
										
										
											2015-07-30 17:12:44 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |       // 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; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2015-07-29 17:27:43 +02:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return nullptr; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-08 19:59:33 +02:00
										 |  |  | std::unique_ptr<FileInfo> FileSystemGCWii::FindFileInfo(u64 disc_offset) const | 
					
						
							| 
									
										
										
										
											2008-12-08 05:30:24 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2015-08-08 19:59:33 +02:00
										 |  |  |   if (!IsValid()) | 
					
						
							|  |  |  |     return nullptr; | 
					
						
							| 
									
										
										
										
											2008-12-08 05:30:24 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-09 13:41:41 +02:00
										 |  |  |   // Build a cache (unless there already is one)
 | 
					
						
							|  |  |  |   if (m_offset_file_info_cache.empty()) | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   { | 
					
						
							| 
									
										
										
										
											2015-08-09 13:41:41 +02:00
										 |  |  |     u32 fst_entries = m_root.GetSize(); | 
					
						
							|  |  |  |     for (u32 i = 0; i < fst_entries; i++) | 
					
						
							| 
									
										
										
										
											2015-07-29 16:42:29 +02:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2015-08-09 13:41:41 +02:00
										 |  |  |       FileInfoGCWii file_info(m_root, i); | 
					
						
							|  |  |  |       if (!file_info.IsDirectory()) | 
					
						
							| 
									
										
										
										
											2017-08-01 19:57:06 +02:00
										 |  |  |       { | 
					
						
							|  |  |  |         const u32 size = file_info.GetSize(); | 
					
						
							|  |  |  |         if (size != 0) | 
					
						
							|  |  |  |           m_offset_file_info_cache.emplace(file_info.GetOffset() + size, i); | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2015-07-29 16:42:29 +02:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-09 13:41:41 +02:00
										 |  |  |   // 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<FileInfo> result(std::make_unique<FileInfoGCWii>(m_root, it->second)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // If the file's start isn't after disc_offset, success
 | 
					
						
							|  |  |  |   if (result->GetOffset() <= disc_offset) | 
					
						
							|  |  |  |     return result; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-08 19:59:33 +02:00
										 |  |  |   return nullptr; | 
					
						
							| 
									
										
										
										
											2015-07-29 16:42:29 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-12-08 05:30:24 +00:00
										 |  |  | }  // namespace
 |