From 8d63ebcb645476d8a1efca7957d8308e32b0353c Mon Sep 17 00:00:00 2001
From: Morph <39850852+Morph1984@users.noreply.github.com>
Date: Mon, 13 Sep 2021 23:02:53 -0400
Subject: [PATCH] vfs: Partially implement GetFileTimeStampRaw

Gets rid of homebrew warnings using this func
---
 src/core/file_sys/vfs.cpp                     |  4 +++
 src/core/file_sys/vfs.h                       |  3 ++
 src/core/file_sys/vfs_real.cpp                | 29 +++++++++++++++++++
 src/core/file_sys/vfs_real.h                  |  1 +
 src/core/file_sys/vfs_types.h                 |  9 ++++++
 .../hle/service/filesystem/filesystem.cpp     | 12 ++++++++
 src/core/hle/service/filesystem/filesystem.h  |  6 ++++
 src/core/hle/service/filesystem/fsp_srv.cpp   | 20 ++++++++++++-
 8 files changed, 83 insertions(+), 1 deletion(-)

diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs.cpp
index 368419ecaf..f5ad10b15f 100644
--- a/src/core/file_sys/vfs.cpp
+++ b/src/core/file_sys/vfs.cpp
@@ -273,6 +273,10 @@ VirtualFile VfsDirectory::GetFile(std::string_view name) const {
     return iter == files.end() ? nullptr : *iter;
 }
 
+FileTimeStampRaw VfsDirectory::GetFileTimeStamp([[maybe_unused]] std::string_view path) const {
+    return {};
+}
+
 VirtualDir VfsDirectory::GetSubdirectory(std::string_view name) const {
     const auto& subs = GetSubdirectories();
     const auto iter = std::find_if(subs.begin(), subs.end(),
diff --git a/src/core/file_sys/vfs.h b/src/core/file_sys/vfs.h
index afd64e95c5..ff6935da67 100644
--- a/src/core/file_sys/vfs.h
+++ b/src/core/file_sys/vfs.h
@@ -199,6 +199,9 @@ public:
     // file with name.
     virtual VirtualFile GetFile(std::string_view name) const;
 
+    // Returns a struct containing the file's timestamp.
+    virtual FileTimeStampRaw GetFileTimeStamp(std::string_view path) const;
+
     // Returns a vector containing all of the subdirectories in this directory.
     virtual std::vector<VirtualDir> GetSubdirectories() const = 0;
     // Returns the directory with name matching name. Returns nullptr if directory dosen't have a
diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs_real.cpp
index 3dad54f493..f4073b76ab 100644
--- a/src/core/file_sys/vfs_real.cpp
+++ b/src/core/file_sys/vfs_real.cpp
@@ -13,6 +13,13 @@
 #include "common/logging/log.h"
 #include "core/file_sys/vfs_real.h"
 
+// For FileTimeStampRaw
+#include <sys/stat.h>
+
+#ifdef _MSC_VER
+#define stat _stat64
+#endif
+
 namespace FileSys {
 
 namespace FS = Common::FS;
@@ -392,6 +399,28 @@ std::vector<VirtualFile> RealVfsDirectory::GetFiles() const {
     return IterateEntries<RealVfsFile, VfsFile>();
 }
 
+FileTimeStampRaw RealVfsDirectory::GetFileTimeStamp(std::string_view path_) const {
+    const auto full_path = FS::SanitizePath(path + '/' + std::string(path_));
+    const auto fs_path = std::filesystem::path{FS::ToU8String(full_path)};
+    struct stat file_status;
+
+#ifdef _WIN32
+    const auto stat_result = _wstat64(fs_path.c_str(), &file_status);
+#else
+    const auto stat_result = stat(fs_path.c_str(), &file_status);
+#endif
+
+    if (stat_result != 0) {
+        return {};
+    }
+
+    return {
+        .created{static_cast<u64>(file_status.st_ctime)},
+        .accessed{static_cast<u64>(file_status.st_atime)},
+        .modified{static_cast<u64>(file_status.st_mtime)},
+    };
+}
+
 std::vector<VirtualDir> RealVfsDirectory::GetSubdirectories() const {
     return IterateEntries<RealVfsDirectory, VfsDirectory>();
 }
diff --git a/src/core/file_sys/vfs_real.h b/src/core/file_sys/vfs_real.h
index e4d1bba79a..746e624cbf 100644
--- a/src/core/file_sys/vfs_real.h
+++ b/src/core/file_sys/vfs_real.h
@@ -86,6 +86,7 @@ public:
     VirtualDir CreateDirectoryRelative(std::string_view relative_path) override;
     bool DeleteSubdirectoryRecursive(std::string_view name) override;
     std::vector<VirtualFile> GetFiles() const override;
+    FileTimeStampRaw GetFileTimeStamp(std::string_view path) const override;
     std::vector<VirtualDir> GetSubdirectories() const override;
     bool IsWritable() const override;
     bool IsReadable() const override;
diff --git a/src/core/file_sys/vfs_types.h b/src/core/file_sys/vfs_types.h
index 6215ed7af2..ed07247178 100644
--- a/src/core/file_sys/vfs_types.h
+++ b/src/core/file_sys/vfs_types.h
@@ -6,6 +6,8 @@
 
 #include <memory>
 
+#include "common/common_types.h"
+
 namespace FileSys {
 
 class VfsDirectory;
@@ -18,4 +20,11 @@ using VirtualDir = std::shared_ptr<VfsDirectory>;
 using VirtualFile = std::shared_ptr<VfsFile>;
 using VirtualFilesystem = std::shared_ptr<VfsFilesystem>;
 
+struct FileTimeStampRaw {
+    u64 created{};
+    u64 accessed{};
+    u64 modified{};
+    u64 padding{};
+};
+
 } // namespace FileSys
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index c8d65f3282..f8f9e32f7d 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -261,6 +261,18 @@ ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType(
     return FileSys::ERROR_PATH_NOT_FOUND;
 }
 
+ResultVal<FileSys::FileTimeStampRaw> VfsDirectoryServiceWrapper::GetFileTimeStampRaw(
+    const std::string& path) const {
+    auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
+    if (dir == nullptr) {
+        return FileSys::ERROR_PATH_NOT_FOUND;
+    }
+    if (GetEntryType(path).Failed()) {
+        return FileSys::ERROR_PATH_NOT_FOUND;
+    }
+    return MakeResult(dir->GetFileTimeStamp(Common::FS::GetFilename(path)));
+}
+
 FileSystemController::FileSystemController(Core::System& system_) : system{system_} {}
 
 FileSystemController::~FileSystemController() = default;
diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h
index d387af3cbe..b155e08113 100644
--- a/src/core/hle/service/filesystem/filesystem.h
+++ b/src/core/hle/service/filesystem/filesystem.h
@@ -240,6 +240,12 @@ public:
      */
     ResultVal<FileSys::EntryType> GetEntryType(const std::string& path) const;
 
+    /**
+     * Get the timestamp of the specified path
+     * @return The timestamp of the specified path or error code
+     */
+    ResultVal<FileSys::FileTimeStampRaw> GetFileTimeStampRaw(const std::string& path) const;
+
 private:
     FileSys::VirtualDir backing;
 };
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index db4d44c128..50c788dd60 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -326,7 +326,7 @@ public:
             {11, &IFileSystem::GetFreeSpaceSize, "GetFreeSpaceSize"},
             {12, &IFileSystem::GetTotalSpaceSize, "GetTotalSpaceSize"},
             {13, &IFileSystem::CleanDirectoryRecursively, "CleanDirectoryRecursively"},
-            {14, nullptr, "GetFileTimeStampRaw"},
+            {14, &IFileSystem::GetFileTimeStampRaw, "GetFileTimeStampRaw"},
             {15, nullptr, "QueryEntry"},
         };
         RegisterHandlers(functions);
@@ -501,6 +501,24 @@ public:
         rb.Push(size.get_total_size());
     }
 
+    void GetFileTimeStampRaw(Kernel::HLERequestContext& ctx) {
+        const auto file_buffer = ctx.ReadBuffer();
+        const std::string name = Common::StringFromBuffer(file_buffer);
+
+        LOG_WARNING(Service_FS, "(Partial Implementation) called. file={}", name);
+
+        auto result = backend.GetFileTimeStampRaw(name);
+        if (result.Failed()) {
+            IPC::ResponseBuilder rb{ctx, 2};
+            rb.Push(result.Code());
+            return;
+        }
+
+        IPC::ResponseBuilder rb{ctx, 10};
+        rb.Push(ResultSuccess);
+        rb.PushRaw(*result);
+    }
+
 private:
     VfsDirectoryServiceWrapper backend;
     SizeGetter size;