From e73caaefe58503f7d1d79a08e71bf66b5fe6edba Mon Sep 17 00:00:00 2001
From: Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
Date: Fri, 12 Sep 2014 00:44:16 +0200
Subject: [PATCH] Core: Add a passthrough backend for the filesystem, exposed
 as SDMC.

---
 src/core/CMakeLists.txt            |  4 ++
 src/core/file_sys/archive_sdmc.cpp | 96 ++++++++++++++++++++++++++++++
 src/core/file_sys/archive_sdmc.h   | 79 ++++++++++++++++++++++++
 src/core/file_sys/file_sdmc.cpp    | 63 ++++++++++++++++++++
 src/core/file_sys/file_sdmc.h      | 60 +++++++++++++++++++
 5 files changed, 302 insertions(+)
 create mode 100644 src/core/file_sys/archive_sdmc.cpp
 create mode 100644 src/core/file_sys/archive_sdmc.h
 create mode 100644 src/core/file_sys/file_sdmc.cpp
 create mode 100644 src/core/file_sys/file_sdmc.h

diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 14c114b63..46e11d779 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -23,7 +23,9 @@ set(SRCS
             arm/interpreter/armvirt.cpp
             arm/interpreter/thumbemu.cpp
             file_sys/archive_romfs.cpp
+            file_sys/archive_sdmc.cpp
             file_sys/file_romfs.cpp
+            file_sys/file_sdmc.cpp
             hle/kernel/address_arbiter.cpp
             hle/kernel/archive.cpp
             hle/kernel/event.cpp
@@ -78,8 +80,10 @@ set(HEADERS
             arm/arm_interface.h
             file_sys/archive.h
             file_sys/archive_romfs.h
+            file_sys/archive_sdmc.h
             file_sys/file.h
             file_sys/file_romfs.h
+            file_sys/file_sdmc.h
             hle/kernel/address_arbiter.h
             hle/kernel/archive.h
             hle/kernel/event.h
diff --git a/src/core/file_sys/archive_sdmc.cpp b/src/core/file_sys/archive_sdmc.cpp
new file mode 100644
index 000000000..fb155430d
--- /dev/null
+++ b/src/core/file_sys/archive_sdmc.cpp
@@ -0,0 +1,96 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include <sys/stat.h>
+
+#include "common/common_types.h"
+#include "common/file_util.h"
+
+#include "core/file_sys/archive_sdmc.h"
+#include "core/file_sys/file_sdmc.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// FileSys namespace
+
+namespace FileSys {
+
+Archive_SDMC::Archive_SDMC(const std::string& mount_point) {
+    this->mount_point = mount_point;
+    DEBUG_LOG(FILESYS, "Directory %s set as SDMC.", mount_point.c_str());
+}
+
+Archive_SDMC::~Archive_SDMC() {
+}
+
+bool Archive_SDMC::Initialize() {
+    if (!FileUtil::IsDirectory(mount_point)) {
+        WARN_LOG(FILESYS, "Directory %s not found, disabling SDMC.", mount_point.c_str());
+        return false;
+    }
+
+    return true;
+}
+
+/**
+ * Open a file specified by its path, using the specified mode
+ * @param path Path relative to the archive
+ * @param mode Mode to open the file with
+ * @return Opened file, or nullptr
+ */
+std::unique_ptr<File> Archive_SDMC::OpenFile(const std::string& path, const Mode mode) const {
+    DEBUG_LOG(FILESYS, "called path=%s mode=%d", path.c_str(), mode);
+    File_SDMC* file = new File_SDMC(this, path, mode);
+    return std::unique_ptr<File>(file);
+}
+
+/**
+ * Read data from the archive
+ * @param offset Offset in bytes to start reading archive from
+ * @param length Length in bytes to read data from archive
+ * @param buffer Buffer to read data into
+ * @return Number of bytes read
+ */
+size_t Archive_SDMC::Read(const u64 offset, const u32 length, u8* buffer) const {
+    ERROR_LOG(FILESYS, "(UNIMPLEMENTED)");
+    return -1;
+}
+
+/**
+ * Write data to the archive
+ * @param offset Offset in bytes to start writing data to
+ * @param length Length in bytes of data to write to archive
+ * @param buffer Buffer to write data from
+ * @param flush  The flush parameters (0 == do not flush)
+ * @return Number of bytes written
+ */
+size_t Archive_SDMC::Write(const u64 offset, const u32 length, const u32 flush, u8* buffer) {
+    ERROR_LOG(FILESYS, "(UNIMPLEMENTED)");
+    return -1;
+}
+
+/**
+ * Get the size of the archive in bytes
+ * @return Size of the archive in bytes
+ */
+size_t Archive_SDMC::GetSize() const {
+    ERROR_LOG(FILESYS, "(UNIMPLEMENTED)");
+    return 0;
+}
+
+/**
+ * Set the size of the archive in bytes
+ */
+void Archive_SDMC::SetSize(const u64 size) {
+    ERROR_LOG(FILESYS, "(UNIMPLEMENTED)");
+}
+
+/**
+ * Getter for the path used for this Archive
+ * @return Mount point of that passthrough archive
+ */
+std::string Archive_SDMC::GetMountPoint() const {
+    return mount_point;
+}
+
+} // namespace FileSys
diff --git a/src/core/file_sys/archive_sdmc.h b/src/core/file_sys/archive_sdmc.h
new file mode 100644
index 000000000..931817e5b
--- /dev/null
+++ b/src/core/file_sys/archive_sdmc.h
@@ -0,0 +1,79 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_types.h"
+
+#include "core/file_sys/archive.h"
+#include "core/loader/loader.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// FileSys namespace
+
+namespace FileSys {
+
+/// File system interface to the SDMC archive
+class Archive_SDMC final : public Archive {
+public:
+    Archive_SDMC(const std::string& mount_point);
+    ~Archive_SDMC() override;
+
+    bool Initialize();
+
+    /**
+     * Get the IdCode of the archive (e.g. RomFS, SaveData, etc.)
+     * @return IdCode of the archive
+     */
+    IdCode GetIdCode() const override { return IdCode::SDMC; };
+
+    /**
+     * Open a file specified by its path, using the specified mode
+     * @param path Path relative to the archive
+     * @param mode Mode to open the file with
+     * @return Opened file, or nullptr
+     */
+    std::unique_ptr<File> OpenFile(const std::string& path, const Mode mode) const override;
+
+    /**
+     * Read data from the archive
+     * @param offset Offset in bytes to start reading archive from
+     * @param length Length in bytes to read data from archive
+     * @param buffer Buffer to read data into
+     * @return Number of bytes read
+     */
+    size_t Read(const u64 offset, const u32 length, u8* buffer) const override;
+
+    /**
+     * Write data to the archive
+     * @param offset Offset in bytes to start writing data to
+     * @param length Length in bytes of data to write to archive
+     * @param buffer Buffer to write data from
+     * @param flush  The flush parameters (0 == do not flush)
+     * @return Number of bytes written
+     */
+    size_t Write(const u64 offset, const u32 length, const u32 flush, u8* buffer) override;
+
+    /**
+     * Get the size of the archive in bytes
+     * @return Size of the archive in bytes
+     */
+    size_t GetSize() const override;
+
+    /**
+     * Set the size of the archive in bytes
+     */
+    void SetSize(const u64 size) override;
+
+    /**
+     * Getter for the path used for this Archive
+     * @return Mount point of that passthrough archive
+     */
+    std::string GetMountPoint() const;
+
+private:
+    std::string mount_point;
+};
+
+} // namespace FileSys
diff --git a/src/core/file_sys/file_sdmc.cpp b/src/core/file_sys/file_sdmc.cpp
new file mode 100644
index 000000000..76adc6403
--- /dev/null
+++ b/src/core/file_sys/file_sdmc.cpp
@@ -0,0 +1,63 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include <sys/stat.h>
+
+#include "common/common_types.h"
+#include "common/file_util.h"
+
+#include "core/file_sys/file_sdmc.h"
+#include "core/file_sys/archive_sdmc.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// FileSys namespace
+
+namespace FileSys {
+
+File_SDMC::File_SDMC(const Archive_SDMC* archive, const std::string& path, const Mode mode) {
+    // TODO(Link Mauve): normalize path into an absolute path without "..", it can currently bypass
+    // the root directory we set while opening the archive.
+    // For example, opening /../../etc/passwd can give the emulated program your users list.
+    std::string real_path = archive->GetMountPoint() + path;
+
+    if (!mode.create_flag && !FileUtil::Exists(real_path)) {
+        file = nullptr;
+        return;
+    }
+
+    std::string mode_string;
+    if (mode.read_flag)
+        mode_string += "r";
+    if (mode.write_flag)
+        mode_string += "w";
+
+    file = new FileUtil::IOFile(real_path, mode_string.c_str());
+}
+
+File_SDMC::~File_SDMC() {
+    Close();
+}
+
+size_t File_SDMC::Read(const u64 offset, const u32 length, u8* buffer) const {
+    file->Seek(offset, SEEK_SET);
+    return file->ReadBytes(buffer, length);
+}
+
+size_t File_SDMC::Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const {
+    file->Seek(offset, SEEK_SET);
+    size_t written = file->WriteBytes(buffer, length);
+    if (flush)
+        file->Flush();
+    return written;
+}
+
+size_t File_SDMC::GetSize() const {
+    return file->GetSize();
+}
+
+bool File_SDMC::Close() const {
+    return file->Close();
+}
+
+} // namespace FileSys
diff --git a/src/core/file_sys/file_sdmc.h b/src/core/file_sys/file_sdmc.h
new file mode 100644
index 000000000..b2e46f449
--- /dev/null
+++ b/src/core/file_sys/file_sdmc.h
@@ -0,0 +1,60 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_types.h"
+#include "common/file_util.h"
+
+#include "core/file_sys/file.h"
+#include "core/file_sys/archive_sdmc.h"
+#include "core/loader/loader.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// FileSys namespace
+
+namespace FileSys {
+
+class File_SDMC final : public File {
+public:
+    File_SDMC();
+    File_SDMC(const Archive_SDMC* archive, const std::string& path, const Mode mode);
+    ~File_SDMC() override;
+
+    /**
+     * Read data from the file
+     * @param offset Offset in bytes to start reading data from
+     * @param length Length in bytes of data to read from file
+     * @param buffer Buffer to read data into
+     * @return Number of bytes read
+     */
+    size_t Read(const u64 offset, const u32 length, u8* buffer) const override;
+
+    /**
+     * Write data to the file
+     * @param offset Offset in bytes to start writing data to
+     * @param length Length in bytes of data to write to file
+     * @param buffer Buffer to write data from
+     * @param flush The flush parameters (0 == do not flush)
+     * @return Number of bytes written
+     */
+    size_t Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const override;
+
+    /**
+     * Get the size of the file in bytes
+     * @return Size of the file in bytes
+     */
+    size_t GetSize() const override;
+
+    /**
+     * Close the file
+     * @return true if the file closed correctly
+     */
+    bool Close() const override;
+
+private:
+    FileUtil::IOFile* file;
+};
+
+} // namespace FileSys