summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Source/Core/Core/IOS/FS/FileSystem.h18
-rw-r--r--Source/Core/Core/IOS/FS/FileSystemCommon.cpp193
-rw-r--r--Source/Core/Core/IOS/FS/HostBackend/FS.cpp90
-rw-r--r--Source/Core/Core/IOS/FS/HostBackend/FS.h3
-rw-r--r--Source/Core/Core/State.cpp10
5 files changed, 216 insertions, 98 deletions
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<ExtendedDirectoryStats> GetExtendedDirectoryStats(const std::string& path) = 0;
virtual void SetNandRedirects(std::vector<NandRedirect> nand_redirects) = 0;
+
+protected:
+ void DoStateWriteOrMeasure(PointerWrap& p, const std::string& directory_path);
+ void DoStateRead(PointerWrap& p, const std::string& directory_path);
};
template <typename T>
@@ -319,3 +324,16 @@ IOS::HLE::ReturnCode ConvertResult(ResultCode code);
} // namespace FS
} // namespace IOS::HLE
+
+template <>
+struct fmt::formatter<IOS::HLE::FS::ResultCode> : EnumFormatter<IOS::HLE::FS::ResultCode::ShortRead>
+{
+ 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 <algorithm>
+#include <array>
#include <expected>
#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<std::string> 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<u8, BUFFER_CHUNK_SIZE> buffer;
+ Result<FileHandle> 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<u32>(buffer.size()));
+ p_.DoArray(buffer.data(), bytes_to_write);
+
+ Result<size_t> 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> 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<Metadata> 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<u8, BUFFER_CHUNK_SIZE> buffer;
+ Result<FileHandle> 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<u32>(buffer.size()));
+ Result<size_t> 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<File::FSTEntry> 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<NandRedirect> 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..040c60f6ab 100644
--- a/Source/Core/Core/State.cpp
+++ b/Source/Core/Core/State.cpp
@@ -15,6 +15,7 @@
#include <fmt/chrono.h>
#include <fmt/format.h>
+#include <fmt/std.h>
#include <lz4.h>
#include <lzo/lzo1x.h>
@@ -95,7 +96,7 @@ struct CompressAndDumpStateArgs
static Common::WorkQueueThreadSP<CompressAndDumpStateArgs> 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
@@ -463,7 +464,7 @@ static void CompressAndDumpState(Core::System& system, const CompressAndDumpStat
else
{
const std::filesystem::path temp_path(filename);
- Core::DisplayMessage(fmt::format("Saved State to {}", temp_path.filename().string()), 2000);
+ Core::DisplayMessage(fmt::format("Saved State to {}", temp_path.filename()), 2000);
}
}
@@ -840,9 +841,8 @@ static void LoadAsFromCore(Core::System& system, std::string filename)
{
if (loaded_successfully)
{
- std::filesystem::path temp_filename(std::move(filename));
- Core::DisplayMessage(fmt::format("Loaded State from {}", temp_filename.filename().string()),
- 2000);
+ const std::filesystem::path temp_filename(filename);
+ Core::DisplayMessage(fmt::format("Loaded State from {}", temp_filename.filename()), 2000);
if (File::Exists(filename + ".dtm"))
{
movie.LoadInput(filename + ".dtm");