diff --git a/Source/Core/Core/IOS/FS/FileSystem.h b/Source/Core/Core/IOS/FS/FileSystem.h index 94b943d92a..8d9f5ebf64 100644 --- a/Source/Core/Core/IOS/FS/FileSystem.h +++ b/Source/Core/Core/IOS/FS/FileSystem.h @@ -16,6 +16,7 @@ #endif #include "Common/CommonTypes.h" +#include "Common/EnumFormatter.h" class PointerWrap; @@ -281,6 +282,10 @@ public: virtual Result GetExtendedDirectoryStats(const std::string& path) = 0; virtual void SetNandRedirects(std::vector nand_redirects) = 0; + +protected: + void DoStateWriteOrMeasure(PointerWrap& p, const std::string& directory_path); + void DoStateRead(PointerWrap& p, const std::string& directory_path); }; template @@ -319,3 +324,16 @@ IOS::HLE::ReturnCode ConvertResult(ResultCode code); } // namespace FS } // namespace IOS::HLE + +template <> +struct fmt::formatter : EnumFormatter +{ + constexpr formatter() + : EnumFormatter({"Success", "Invalid", "AccessDenied", "SuperblockWriteFailed", + "SuperblockInitFailed", "AlreadyExists", "NotFound", "FstFull", + "NoFreeSpace", "NoFreeHandle", "TooManyPathComponents", "InUse", "BadBlock", + "EccError", "CriticalEccError", "FileNotEmpty", "CheckFailed", + "UnknownError", "ShortRead"}) + { + } +}; diff --git a/Source/Core/Core/IOS/FS/FileSystemCommon.cpp b/Source/Core/Core/IOS/FS/FileSystemCommon.cpp index 471f7cd828..aa99f46da3 100644 --- a/Source/Core/Core/IOS/FS/FileSystemCommon.cpp +++ b/Source/Core/Core/IOS/FS/FileSystemCommon.cpp @@ -4,6 +4,7 @@ #include "Core/IOS/FS/FileSystem.h" #include +#include #include #include "Common/Assert.h" @@ -13,6 +14,8 @@ namespace IOS::HLE::FS { +constexpr u32 BUFFER_CHUNK_SIZE = 65536; + bool IsValidPath(std::string_view path) { return path == "/" || IsValidNonRootPath(path); @@ -133,4 +136,194 @@ ResultCode FileSystem::CreateFullPath(Uid uid, Gid gid, const std::string& path, ++position; } } + +void FileSystem::DoStateRead(PointerWrap& p, const std::string& directory_path) +{ + const ResultCode delete_result = Delete(0, 0, directory_path); + if (delete_result != ResultCode::Success && delete_result != ResultCode::NotFound) + { + ERROR_LOG_FMT(IOS_FS, "DoStateRead failed to call Delete: {}", delete_result); + p.SetVerifyMode(); + return; + } + + Metadata metadata; + p.Do(metadata.uid); + p.Do(metadata.gid); + p.Do(metadata.attribute); + p.Do(metadata.modes); + p.Do(metadata.is_file); + p.Do(metadata.size); + p.Do(metadata.fst_index); + + const ResultCode create_directory_result = CreateDirectory( + metadata.uid, metadata.gid, directory_path, metadata.attribute, metadata.modes); + if (create_directory_result != ResultCode::Success) + { + ERROR_LOG_FMT(IOS_FS, "DoStateRead failed to call CreateDirectory: {}", + create_directory_result); + p.SetVerifyMode(); + return; + } + + // Now restore from the stream + std::vector children; + p.DoEachElement(children, [this, &directory_path](PointerWrap& p_, std::string& child_name) { + Metadata child_metadata; + p_.Do(child_metadata); + p_.Do(child_name); + + std::string child_path; + child_path.reserve(directory_path.size() + child_name.size() + 1); + child_path.append(directory_path); + if (directory_path.back() != '/') + child_path.push_back('/'); + child_path.append(child_name); + + if (child_metadata.is_file) + { + const ResultCode create_file_result = + CreateFile(child_metadata.uid, child_metadata.gid, child_path, child_metadata.attribute, + child_metadata.modes); + if (create_file_result != ResultCode::Success) + { + ERROR_LOG_FMT(IOS_FS, "DoStateRead failed to call CreateFile for {}: {}", child_name, + create_file_result); + p_.SetVerifyMode(); + return; + } + + std::array buffer; + Result handle = OpenFile(0, 0, child_path, Mode::Write); + if (!handle) + { + ERROR_LOG_FMT(IOS_FS, "DoStateRead failed to call OpenFile for {}: {}", child_name, + handle.error()); + p_.SetVerifyMode(); + return; + } + + u32 i = 0; + while (i < child_metadata.size) + { + const u32 bytes_to_write = + std::min(child_metadata.size - i, static_cast(buffer.size())); + p_.DoArray(buffer.data(), bytes_to_write); + + Result write_result = handle->Write(buffer.data(), bytes_to_write); + if (!write_result) + { + ERROR_LOG_FMT(IOS_FS, "DoStateRead failed to call Write for {}: {}", child_name, + write_result.error()); + p_.SetVerifyMode(); + return; + } + if (*write_result != bytes_to_write) + { + ERROR_LOG_FMT(IOS_FS, "DoStateRead tried to write {} bytes to {} but wrote {} bytes", + child_name, bytes_to_write, *write_result); + p_.SetVerifyMode(); + return; + } + + i += bytes_to_write; + } + } + else + { + DoStateRead(p_, child_path); + } + }); +} + +void FileSystem::DoStateWriteOrMeasure(PointerWrap& p, const std::string& directory_path) +{ + const Result metadata = GetMetadata(0, 0, directory_path); + if (!metadata) + { + ERROR_LOG_FMT(IOS_FS, "DoStateWriteOrMeasure failed to call GetMetadata: {}", metadata.error()); + p.SetVerifyMode(); + return; + } + p.Do(metadata->uid); + p.Do(metadata->gid); + p.Do(metadata->attribute); + p.Do(metadata->modes); + p.Do(metadata->is_file); + p.Do(metadata->size); + p.Do(metadata->fst_index); + + auto children = ReadDirectory(0, 0, directory_path); + if (!children) + { + ERROR_LOG_FMT(IOS_FS, "DoStateWriteOrMeasure failed to call ReadDirectory: {}", + children.error()); + p.SetVerifyMode(); + return; + } + + p.DoEachElement(*children, [this, &directory_path](PointerWrap& p_, std::string& child_name) { + std::string child_path; + child_path.reserve(directory_path.size() + child_name.size() + 1); + child_path.append(directory_path); + if (directory_path.back() != '/') + child_path.push_back('/'); + child_path.append(child_name); + + Result child_metadata = GetMetadata(0, 0, child_path); + if (!child_metadata) + { + ERROR_LOG_FMT(IOS_FS, "DoStateWriteOrMeasure failed to call GetMetadata for {}: {}", + child_name, child_metadata.error()); + p_.SetVerifyMode(); + return; + } + + p_.Do(*child_metadata); + p_.Do(child_name); + + if (child_metadata->is_file) + { + std::array buffer; + Result handle = OpenFile(0, 0, child_path, Mode::Read); + if (!handle) + { + ERROR_LOG_FMT(IOS_FS, "DoStateWriteOrMeasure failed to call OpenFile for {}: {}", + child_name, handle.error()); + p_.SetVerifyMode(); + return; + } + + u32 i = 0; + while (i < child_metadata->size) + { + const u32 bytes_to_read = + std::min(child_metadata->size - i, static_cast(buffer.size())); + Result read_result = handle->Read(buffer.data(), bytes_to_read); + if (!read_result) + { + ERROR_LOG_FMT(IOS_FS, "DoStateWriteOrMeasure failed to call Read for {}: {}", child_name, + read_result.error()); + p_.SetVerifyMode(); + return; + } + if (*read_result != bytes_to_read) + { + ERROR_LOG_FMT(IOS_FS, + "DoStateWriteOrMeasure tried to read {} bytes from {} but got {} bytes", + child_name, bytes_to_read, *read_result); + p_.SetVerifyMode(); + return; + } + + p_.DoArray(buffer.data(), bytes_to_read); + i += bytes_to_read; + } + } + else + { + DoStateWriteOrMeasure(p_, child_path); + } + }); +} } // namespace IOS::HLE::FS diff --git a/Source/Core/Core/IOS/FS/HostBackend/FS.cpp b/Source/Core/Core/IOS/FS/HostBackend/FS.cpp index 03bc48103a..8a058abf7e 100644 --- a/Source/Core/Core/IOS/FS/HostBackend/FS.cpp +++ b/Source/Core/Core/IOS/FS/HostBackend/FS.cpp @@ -29,8 +29,6 @@ namespace IOS::HLE::FS { -constexpr u32 BUFFER_CHUNK_SIZE = 65536; - HostFileSystem::HostFilename HostFileSystem::BuildFilename(const std::string& wii_path) const { for (const auto& redirect : m_nand_redirects) @@ -279,94 +277,6 @@ HostFileSystem::FstEntry* HostFileSystem::GetFstEntryForPath(const std::string& return entry; } -void HostFileSystem::DoStateRead(PointerWrap& p, const std::string& start_directory_path) -{ - std::string path = BuildFilename(start_directory_path).host_path; - File::DeleteDirRecursively(path); - File::CreateDir(path); - - // now restore from the stream - while (true) - { - char type = 0; - p.Do(type); - if (!type) - break; - std::string file_name; - p.Do(file_name); - std::string name = path + "/" + file_name; - switch (type) - { - case 'd': - { - File::CreateDir(name); - break; - } - case 'f': - { - u32 size = 0; - p.Do(size); - - File::IOFile handle(name, "wb"); - char buf[BUFFER_CHUNK_SIZE]; - u32 count = size; - while (count > BUFFER_CHUNK_SIZE) - { - p.DoArray(buf); - handle.WriteArray(&buf[0], BUFFER_CHUNK_SIZE); - count -= BUFFER_CHUNK_SIZE; - } - p.DoArray(&buf[0], count); - handle.WriteArray(&buf[0], count); - break; - } - } - } -} - -void HostFileSystem::DoStateWriteOrMeasure(PointerWrap& p, const std::string& start_directory_path) -{ - std::string path = BuildFilename(start_directory_path).host_path; - File::FSTEntry parent_entry = File::ScanDirectoryTree(path, true); - std::deque todo; - todo.insert(todo.end(), parent_entry.children.begin(), parent_entry.children.end()); - - while (!todo.empty()) - { - File::FSTEntry& entry = todo.front(); - std::string name = entry.physicalName; - name.erase(0, path.length() + 1); - char type = entry.isDirectory ? 'd' : 'f'; - p.Do(type); - p.Do(name); - if (entry.isDirectory) - { - todo.insert(todo.end(), entry.children.begin(), entry.children.end()); - } - else - { - u32 size = (u32)entry.size; - p.Do(size); - - File::IOFile handle(entry.physicalName, "rb"); - char buf[BUFFER_CHUNK_SIZE]; - u32 count = size; - while (count > BUFFER_CHUNK_SIZE) - { - handle.ReadArray(&buf[0], BUFFER_CHUNK_SIZE); - p.DoArray(buf); - count -= BUFFER_CHUNK_SIZE; - } - handle.ReadArray(&buf[0], count); - p.DoArray(&buf[0], count); - } - todo.pop_front(); - } - - char type = 0; - p.Do(type); -} - void HostFileSystem::DoState(PointerWrap& p) { // Temporarily close the file, to prevent any issues with the savestating of files/folders. diff --git a/Source/Core/Core/IOS/FS/HostBackend/FS.h b/Source/Core/Core/IOS/FS/HostBackend/FS.h index d178117770..c54c17bcb4 100644 --- a/Source/Core/Core/IOS/FS/HostBackend/FS.h +++ b/Source/Core/Core/IOS/FS/HostBackend/FS.h @@ -60,9 +60,6 @@ public: void SetNandRedirects(std::vector nand_redirects) override; private: - void DoStateWriteOrMeasure(PointerWrap& p, const std::string& start_directory_path); - void DoStateRead(PointerWrap& p, const std::string& start_directory_path); - struct FstEntry { bool CheckPermission(Uid uid, Gid gid, Mode requested_mode) const; diff --git a/Source/Core/Core/State.cpp b/Source/Core/Core/State.cpp index 5724240ffd..c20dc8c91e 100644 --- a/Source/Core/Core/State.cpp +++ b/Source/Core/Core/State.cpp @@ -95,7 +95,7 @@ struct CompressAndDumpStateArgs static Common::WorkQueueThreadSP s_compress_and_dump_thread; // Don't forget to increase this after doing changes on the savestate system -constexpr u32 STATE_VERSION = 190; // Last changed in PR 14448 +constexpr u32 STATE_VERSION = 191; // Last changed in PR 14668 // Increase this if the StateExtendedHeader definition changes constexpr u32 EXTENDED_HEADER_VERSION = 1; // Last changed in PR 12217