diff options
Diffstat (limited to 'src/core/file_sys')
22 files changed, 793 insertions, 946 deletions
diff --git a/src/core/file_sys/archive.h b/src/core/file_sys/archive.h deleted file mode 100644 index 2e79bb883..000000000 --- a/src/core/file_sys/archive.h +++ /dev/null @@ -1,246 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 -// Refer to the license.txt file included. - -#pragma once - -#include <memory> - -#include "common/common_types.h" -#include "common/string_util.h" -#include "common/bit_field.h" - -#include "core/file_sys/file.h" -#include "core/file_sys/directory.h" - -#include "core/mem_map.h" -#include "core/hle/kernel/kernel.h" - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// FileSys namespace - -namespace FileSys { - -// Path string type -enum LowPathType : u32 { - Invalid = 0, - Empty = 1, - Binary = 2, - Char = 3, - Wchar = 4 -}; - -union Mode { - u32 hex; - BitField<0, 1, u32> read_flag; - BitField<1, 1, u32> write_flag; - BitField<2, 1, u32> create_flag; -}; - -class Path { -public: - - Path(): - type(Invalid) - { - } - - Path(LowPathType type, u32 size, u32 pointer): - type(type) - { - switch (type) { - case Binary: - { - u8* data = Memory::GetPointer(pointer); - binary = std::vector<u8>(data, data + size); - break; - } - case Char: - { - const char* data = reinterpret_cast<const char*>(Memory::GetPointer(pointer)); - string = std::string(data, size - 1); // Data is always null-terminated. - break; - } - case Wchar: - { - const char16_t* data = reinterpret_cast<const char16_t*>(Memory::GetPointer(pointer)); - u16str = std::u16string(data, size/2 - 1); // Data is always null-terminated. - break; - } - } - } - - LowPathType GetType() const { - return type; - } - - /** - * Gets the string representation of the path for debugging - * @return String representation of the path for debugging - */ - const std::string DebugStr() const { - switch (GetType()) { - case Invalid: - return "[Invalid]"; - case Empty: - return "[Empty]"; - case Binary: - { - std::stringstream res; - res << "[Binary: "; - for (unsigned byte : binary) - res << std::hex << std::setw(2) << std::setfill('0') << byte; - res << ']'; - return res.str(); - } - case Char: - return "[Char: " + AsString() + ']'; - case Wchar: - return "[Wchar: " + AsString() + ']'; - default: - ERROR_LOG(KERNEL, "LowPathType cannot be converted to string!"); - return {}; - } - } - - const std::string AsString() const { - switch (GetType()) { - case Char: - return string; - case Wchar: - return Common::UTF16ToUTF8(u16str); - case Empty: - return {}; - default: - ERROR_LOG(KERNEL, "LowPathType cannot be converted to string!"); - return {}; - } - } - - const std::u16string AsU16Str() const { - switch (GetType()) { - case Char: - return Common::UTF8ToUTF16(string); - case Wchar: - return u16str; - case Empty: - return {}; - default: - ERROR_LOG(KERNEL, "LowPathType cannot be converted to u16string!"); - return {}; - } - } - - const std::vector<u8> AsBinary() const { - switch (GetType()) { - case Binary: - return binary; - case Char: - return std::vector<u8>(string.begin(), string.end()); - case Wchar: - return std::vector<u8>(u16str.begin(), u16str.end()); - case Empty: - return {}; - default: - ERROR_LOG(KERNEL, "LowPathType cannot be converted to binary!"); - return {}; - } - } - -private: - LowPathType type; - std::vector<u8> binary; - std::string string; - std::u16string u16str; -}; - -class Archive : NonCopyable { -public: - /// Supported archive types - enum class IdCode : u32 { - RomFS = 0x00000003, - SaveData = 0x00000004, - ExtSaveData = 0x00000006, - SharedExtSaveData = 0x00000007, - SystemSaveData = 0x00000008, - SDMC = 0x00000009, - SDMCWriteOnly = 0x0000000A, - }; - - Archive() { } - virtual ~Archive() { } - - /** - * Get the IdCode of the archive (e.g. RomFS, SaveData, etc.) - * @return IdCode of the archive - */ - virtual IdCode GetIdCode() const = 0; - - /** - * 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 - */ - virtual std::unique_ptr<File> OpenFile(const Path& path, const Mode mode) const = 0; - - /** - * Delete a file specified by its path - * @param path Path relative to the archive - * @return Whether the file could be deleted - */ - virtual bool DeleteFile(const FileSys::Path& path) const = 0; - - /** - * Delete a directory specified by its path - * @param path Path relative to the archive - * @return Whether the directory could be deleted - */ - virtual bool DeleteDirectory(const FileSys::Path& path) const = 0; - - /** - * Create a directory specified by its path - * @param path Path relative to the archive - * @return Whether the directory could be created - */ - virtual bool CreateDirectory(const Path& path) const = 0; - - /** - * Open a directory specified by its path - * @param path Path relative to the archive - * @return Opened directory, or nullptr - */ - virtual std::unique_ptr<Directory> OpenDirectory(const Path& path) const = 0; - - /** - * Read data from the archive - * @param offset Offset in bytes to start reading data from - * @param length Length in bytes of data to read from archive - * @param buffer Buffer to read data into - * @return Number of bytes read - */ - virtual size_t Read(const u64 offset, const u32 length, u8* buffer) const = 0; - - /** - * 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 - */ - virtual size_t Write(const u64 offset, const u32 length, const u32 flush, u8* buffer) = 0; - - /** - * Get the size of the archive in bytes - * @return Size of the archive in bytes - */ - virtual size_t GetSize() const = 0; - - /** - * Set the size of the archive in bytes - */ - virtual void SetSize(const u64 size) = 0; -}; - -} // namespace FileSys diff --git a/src/core/file_sys/archive_backend.h b/src/core/file_sys/archive_backend.h new file mode 100644 index 000000000..e153917ea --- /dev/null +++ b/src/core/file_sys/archive_backend.h @@ -0,0 +1,243 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> + +#include "common/common_types.h" +#include "common/string_util.h" +#include "common/bit_field.h" + +#include "core/file_sys/file_backend.h" +#include "core/file_sys/directory_backend.h" + +#include "core/mem_map.h" +#include "core/hle/kernel/kernel.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// FileSys namespace + +namespace FileSys { + +// Path string type +enum LowPathType : u32 { + Invalid = 0, + Empty = 1, + Binary = 2, + Char = 3, + Wchar = 4 +}; + +union Mode { + u32 hex; + BitField<0, 1, u32> read_flag; + BitField<1, 1, u32> write_flag; + BitField<2, 1, u32> create_flag; +}; + +class Path { +public: + + Path() : type(Invalid) { + } + + Path(const char* path) : type(Char), string(path) { + } + + Path(LowPathType type, u32 size, u32 pointer) : type(type) { + switch (type) { + case Binary: + { + u8* data = Memory::GetPointer(pointer); + binary = std::vector<u8>(data, data + size); + break; + } + + case Char: + { + const char* data = reinterpret_cast<const char*>(Memory::GetPointer(pointer)); + string = std::string(data, size - 1); // Data is always null-terminated. + break; + } + + case Wchar: + { + const char16_t* data = reinterpret_cast<const char16_t*>(Memory::GetPointer(pointer)); + u16str = std::u16string(data, size/2 - 1); // Data is always null-terminated. + break; + } + + default: + break; + } + } + + LowPathType GetType() const { + return type; + } + + /** + * Gets the string representation of the path for debugging + * @return String representation of the path for debugging + */ + const std::string DebugStr() const { + switch (GetType()) { + case Invalid: + return "[Invalid]"; + case Empty: + return "[Empty]"; + case Binary: + { + std::stringstream res; + res << "[Binary: "; + for (unsigned byte : binary) + res << std::hex << std::setw(2) << std::setfill('0') << byte; + res << ']'; + return res.str(); + } + case Char: + return "[Char: " + AsString() + ']'; + case Wchar: + return "[Wchar: " + AsString() + ']'; + } + } + + const std::string AsString() const { + switch (GetType()) { + case Char: + return string; + case Wchar: + return Common::UTF16ToUTF8(u16str); + case Empty: + return {}; + case Invalid: + case Binary: + // TODO(yuriks): Add assert + LOG_ERROR(Service_FS, "LowPathType cannot be converted to string!"); + return {}; + } + } + + const std::u16string AsU16Str() const { + switch (GetType()) { + case Char: + return Common::UTF8ToUTF16(string); + case Wchar: + return u16str; + case Empty: + return {}; + case Invalid: + case Binary: + // TODO(yuriks): Add assert + LOG_ERROR(Service_FS, "LowPathType cannot be converted to u16string!"); + return {}; + } + } + + const std::vector<u8> AsBinary() const { + switch (GetType()) { + case Binary: + return binary; + case Char: + return std::vector<u8>(string.begin(), string.end()); + case Wchar: + { + // use two u8 for each character of u16str + std::vector<u8> to_return(u16str.size() * 2); + for (size_t i = 0; i < u16str.size(); ++i) { + u16 tmp_char = u16str.at(i); + to_return[i*2] = (tmp_char & 0xFF00) >> 8; + to_return[i*2 + 1] = (tmp_char & 0x00FF); + } + return to_return; + } + case Empty: + return {}; + case Invalid: + // TODO(yuriks): Add assert + LOG_ERROR(Service_FS, "LowPathType cannot be converted to binary!"); + return {}; + } + } + +private: + LowPathType type; + std::vector<u8> binary; + std::string string; + std::u16string u16str; +}; + +class ArchiveBackend : NonCopyable { +public: + virtual ~ArchiveBackend() { + } + + /** + * Get a descriptive name for the archive (e.g. "RomFS", "SaveData", etc.) + */ + virtual std::string GetName() const = 0; + + /** + * 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 + */ + virtual std::unique_ptr<FileBackend> OpenFile(const Path& path, const Mode mode) const = 0; + + /** + * Delete a file specified by its path + * @param path Path relative to the archive + * @return Whether the file could be deleted + */ + virtual bool DeleteFile(const Path& path) const = 0; + + /** + * Rename a File specified by its path + * @param src_path Source path relative to the archive + * @param dest_path Destination path relative to the archive + * @return Whether rename succeeded + */ + virtual bool RenameFile(const Path& src_path, const Path& dest_path) const = 0; + + /** + * Delete a directory specified by its path + * @param path Path relative to the archive + * @return Whether the directory could be deleted + */ + virtual bool DeleteDirectory(const Path& path) const = 0; + + /** + * Create a file specified by its path + * @param path Path relative to the Archive + * @param size The size of the new file, filled with zeroes + * @return File creation result code + */ + virtual ResultCode CreateFile(const Path& path, u32 size) const = 0; + + /** + * Create a directory specified by its path + * @param path Path relative to the archive + * @return Whether the directory could be created + */ + virtual bool CreateDirectory(const Path& path) const = 0; + + /** + * Rename a Directory specified by its path + * @param src_path Source path relative to the archive + * @param dest_path Destination path relative to the archive + * @return Whether rename succeeded + */ + virtual bool RenameDirectory(const Path& src_path, const Path& dest_path) const = 0; + + /** + * Open a directory specified by its path + * @param path Path relative to the archive + * @return Opened directory, or nullptr + */ + virtual std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const = 0; +}; + +} // namespace FileSys diff --git a/src/core/file_sys/archive_romfs.cpp b/src/core/file_sys/archive_romfs.cpp index 53dc57954..fdaf73179 100644 --- a/src/core/file_sys/archive_romfs.cpp +++ b/src/core/file_sys/archive_romfs.cpp @@ -1,8 +1,11 @@ // Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 +// Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <memory> + #include "common/common_types.h" +#include "common/make_unique.h" #include "core/file_sys/archive_romfs.h" #include "core/file_sys/directory_romfs.h" @@ -16,101 +19,47 @@ namespace FileSys { Archive_RomFS::Archive_RomFS(const Loader::AppLoader& app_loader) { // Load the RomFS from the app if (Loader::ResultStatus::Success != app_loader.ReadRomFS(raw_data)) { - WARN_LOG(FILESYS, "Unable to read RomFS!"); + LOG_ERROR(Service_FS, "Unable to read RomFS!"); } } -Archive_RomFS::~Archive_RomFS() { -} - -/** - * 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_RomFS::OpenFile(const Path& path, const Mode mode) const { - return std::unique_ptr<File>(new File_RomFS); +std::unique_ptr<FileBackend> Archive_RomFS::OpenFile(const Path& path, const Mode mode) const { + return Common::make_unique<File_RomFS>(this); } -/** - * Delete a file specified by its path - * @param path Path relative to the archive - * @return Whether the file could be deleted - */ -bool Archive_RomFS::DeleteFile(const FileSys::Path& path) const { - ERROR_LOG(FILESYS, "Attempted to delete a file from ROMFS."); +bool Archive_RomFS::DeleteFile(const Path& path) const { + LOG_WARNING(Service_FS, "Attempted to delete a file from ROMFS."); return false; } -/** - * Delete a directory specified by its path - * @param path Path relative to the archive - * @return Whether the directory could be deleted - */ -bool Archive_RomFS::DeleteDirectory(const FileSys::Path& path) const { - ERROR_LOG(FILESYS, "Attempted to delete a directory from ROMFS."); +bool Archive_RomFS::RenameFile(const Path& src_path, const Path& dest_path) const { + LOG_WARNING(Service_FS, "Attempted to rename a file within ROMFS."); return false; } -/** - * Create a directory specified by its path - * @param path Path relative to the archive - * @return Whether the directory could be created - */ -bool Archive_RomFS::CreateDirectory(const Path& path) const { - ERROR_LOG(FILESYS, "Attempted to create a directory in ROMFS."); +bool Archive_RomFS::DeleteDirectory(const Path& path) const { + LOG_WARNING(Service_FS, "Attempted to delete a directory from ROMFS."); return false; } -/** - * Open a directory specified by its path - * @param path Path relative to the archive - * @return Opened directory, or nullptr - */ -std::unique_ptr<Directory> Archive_RomFS::OpenDirectory(const Path& path) const { - return std::unique_ptr<Directory>(new Directory_RomFS); +ResultCode Archive_RomFS::CreateFile(const Path& path, u32 size) const { + LOG_WARNING(Service_FS, "Attempted to create a file in ROMFS."); + // TODO: Verify error code + return ResultCode(ErrorDescription::NotAuthorized, ErrorModule::FS, ErrorSummary::NotSupported, ErrorLevel::Permanent); } -/** - * Read data from the archive - * @param offset Offset in bytes to start reading data from - * @param length Length in bytes of data to read from archive - * @param buffer Buffer to read data into - * @return Number of bytes read - */ -size_t Archive_RomFS::Read(const u64 offset, const u32 length, u8* buffer) const { - DEBUG_LOG(FILESYS, "called offset=%llu, length=%d", offset, length); - memcpy(buffer, &raw_data[(u32)offset], length); - return length; -} - -/** - * 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_RomFS::Write(const u64 offset, const u32 length, const u32 flush, u8* buffer) { - ERROR_LOG(FILESYS, "Attempted to write to ROMFS."); - return 0; +bool Archive_RomFS::CreateDirectory(const Path& path) const { + LOG_WARNING(Service_FS, "Attempted to create a directory in ROMFS."); + return false; } -/** - * Get the size of the archive in bytes - * @return Size of the archive in bytes - */ -size_t Archive_RomFS::GetSize() const { - return sizeof(u8) * raw_data.size(); +bool Archive_RomFS::RenameDirectory(const Path& src_path, const Path& dest_path) const { + LOG_WARNING(Service_FS, "Attempted to rename a file within ROMFS."); + return false; } -/** - * Set the size of the archive in bytes - */ -void Archive_RomFS::SetSize(const u64 size) { - ERROR_LOG(FILESYS, "Attempted to set the size of ROMFS"); +std::unique_ptr<DirectoryBackend> Archive_RomFS::OpenDirectory(const Path& path) const { + return Common::make_unique<Directory_RomFS>(); } } // namespace FileSys diff --git a/src/core/file_sys/archive_romfs.h b/src/core/file_sys/archive_romfs.h index 0649dde99..5e918f92d 100644 --- a/src/core/file_sys/archive_romfs.h +++ b/src/core/file_sys/archive_romfs.h @@ -1,5 +1,5 @@ // Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 +// Licensed under GPLv2 or any later version // Refer to the license.txt file included. #pragma once @@ -8,7 +8,7 @@ #include "common/common_types.h" -#include "core/file_sys/archive.h" +#include "core/file_sys/archive_backend.h" #include "core/loader/loader.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -17,16 +17,11 @@ namespace FileSys { /// File system interface to the RomFS archive -class Archive_RomFS final : public Archive { +class Archive_RomFS final : public ArchiveBackend { public: Archive_RomFS(const Loader::AppLoader& app_loader); - ~Archive_RomFS() override; - /** - * Get the IdCode of the archive (e.g. RomFS, SaveData, etc.) - * @return IdCode of the archive - */ - IdCode GetIdCode() const override { return IdCode::RomFS; } + std::string GetName() const override { return "RomFS"; } /** * Open a file specified by its path, using the specified mode @@ -34,67 +29,63 @@ public: * @param mode Mode to open the file with * @return Opened file, or nullptr */ - std::unique_ptr<File> OpenFile(const Path& path, const Mode mode) const override; + std::unique_ptr<FileBackend> OpenFile(const Path& path, const Mode mode) const override; /** * Delete a file specified by its path * @param path Path relative to the archive * @return Whether the file could be deleted */ - bool DeleteFile(const FileSys::Path& path) const override; - - /** - * Delete a directory specified by its path - * @param path Path relative to the archive - * @return Whether the directory could be deleted - */ - bool DeleteDirectory(const FileSys::Path& path) const override; + bool DeleteFile(const Path& path) const override; /** - * Create a directory specified by its path - * @param path Path relative to the archive - * @return Whether the directory could be created + * Rename a File specified by its path + * @param src_path Source path relative to the archive + * @param dest_path Destination path relative to the archive + * @return Whether rename succeeded */ - bool CreateDirectory(const Path& path) const override; + bool RenameFile(const Path& src_path, const Path& dest_path) const override; /** - * Open a directory specified by its path + * Delete a directory specified by its path * @param path Path relative to the archive - * @return Opened directory, or nullptr + * @return Whether the directory could be deleted */ - std::unique_ptr<Directory> OpenDirectory(const Path& path) const override; + bool DeleteDirectory(const Path& path) const override; /** - * Read data from the archive - * @param offset Offset in bytes to start reading data from - * @param length Length in bytes of data to read from archive - * @param buffer Buffer to read data into - * @return Number of bytes read + * Create a file specified by its path + * @param path Path relative to the Archive + * @param size The size of the new file, filled with zeroes + * @return File creation result code */ - size_t Read(const u64 offset, const u32 length, u8* buffer) const override; + ResultCode CreateFile(const Path& path, u32 size) 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 + * Create a directory specified by its path + * @param path Path relative to the archive + * @return Whether the directory could be created */ - size_t Write(const u64 offset, const u32 length, const u32 flush, u8* buffer) override; + bool CreateDirectory(const Path& path) const override; /** - * Get the size of the archive in bytes - * @return Size of the archive in bytes + * Rename a Directory specified by its path + * @param src_path Source path relative to the archive + * @param dest_path Destination path relative to the archive + * @return Whether rename succeeded */ - size_t GetSize() const override; + bool RenameDirectory(const Path& src_path, const Path& dest_path) const override; /** - * Set the size of the archive in bytes + * Open a directory specified by its path + * @param path Path relative to the archive + * @return Opened directory, or nullptr */ - void SetSize(const u64 size) override; + std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const override; private: + friend class File_RomFS; + std::vector<u8> raw_data; }; diff --git a/src/core/file_sys/archive_savedata.cpp b/src/core/file_sys/archive_savedata.cpp new file mode 100644 index 000000000..97853567c --- /dev/null +++ b/src/core/file_sys/archive_savedata.cpp @@ -0,0 +1,33 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2 or any later version +// 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_savedata.h" +#include "core/file_sys/disk_archive.h" +#include "core/settings.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// FileSys namespace + +namespace FileSys { + +Archive_SaveData::Archive_SaveData(const std::string& mount_point, u64 program_id) + : DiskArchive(mount_point + Common::StringFromFormat("%016X", program_id) + DIR_SEP) { + LOG_INFO(Service_FS, "Directory %s set as SaveData.", this->mount_point.c_str()); +} + +bool Archive_SaveData::Initialize() { + if (!FileUtil::CreateFullPath(mount_point)) { + LOG_ERROR(Service_FS, "Unable to create SaveData path."); + return false; + } + + return true; +} + +} // namespace FileSys diff --git a/src/core/file_sys/archive_savedata.h b/src/core/file_sys/archive_savedata.h new file mode 100644 index 000000000..5b0ce29e6 --- /dev/null +++ b/src/core/file_sys/archive_savedata.h @@ -0,0 +1,31 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "common/common_types.h" + +#include "core/file_sys/disk_archive.h" +#include "core/loader/loader.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// FileSys namespace + +namespace FileSys { + +/// File system interface to the SaveData archive +class Archive_SaveData final : public DiskArchive { +public: + Archive_SaveData(const std::string& mount_point, u64 program_id); + + /** + * Initialize the archive. + * @return true if it initialized successfully + */ + bool Initialize(); + + std::string GetName() const override { return "SaveData"; } +}; + +} // namespace FileSys diff --git a/src/core/file_sys/archive_sdmc.cpp b/src/core/file_sys/archive_sdmc.cpp index c2ffcd40d..1c1c170b6 100644 --- a/src/core/file_sys/archive_sdmc.cpp +++ b/src/core/file_sys/archive_sdmc.cpp @@ -1,5 +1,5 @@ // Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 +// Licensed under GPLv2 or any later version // Refer to the license.txt file included. #include <sys/stat.h> @@ -8,8 +8,7 @@ #include "common/file_util.h" #include "core/file_sys/archive_sdmc.h" -#include "core/file_sys/directory_sdmc.h" -#include "core/file_sys/file_sdmc.h" +#include "core/file_sys/disk_archive.h" #include "core/settings.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -17,131 +16,22 @@ 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(const std::string& mount_point) : DiskArchive(mount_point) { + LOG_INFO(Service_FS, "Directory %s set as SDMC.", mount_point.c_str()); } -Archive_SDMC::~Archive_SDMC() { -} - -/** - * Initialize the archive. - * @return true if it initialized successfully - */ bool Archive_SDMC::Initialize() { if (!Settings::values.use_virtual_sd) { - WARN_LOG(FILESYS, "SDMC disabled by config."); + LOG_WARNING(Service_FS, "SDMC disabled by config."); return false; } if (!FileUtil::CreateFullPath(mount_point)) { - WARN_LOG(FILESYS, "Unable to create SDMC path."); + LOG_ERROR(Service_FS, "Unable to create SDMC path."); 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 Path& path, const Mode mode) const { - DEBUG_LOG(FILESYS, "called path=%s mode=%d", path.DebugStr().c_str(), mode); - File_SDMC* file = new File_SDMC(this, path, mode); - if (!file->Open()) - return nullptr; - return std::unique_ptr<File>(file); -} - -/** - * Delete a file specified by its path - * @param path Path relative to the archive - * @return Whether the file could be deleted - */ -bool Archive_SDMC::DeleteFile(const FileSys::Path& path) const { - return FileUtil::Delete(GetMountPoint() + path.AsString()); -} - -/** - * Delete a directory specified by its path - * @param path Path relative to the archive - * @return Whether the directory could be deleted - */ -bool Archive_SDMC::DeleteDirectory(const FileSys::Path& path) const { - return FileUtil::DeleteDir(GetMountPoint() + path.AsString()); -} - -/** - * Create a directory specified by its path - * @param path Path relative to the archive - * @return Whether the directory could be created - */ -bool Archive_SDMC::CreateDirectory(const Path& path) const { - return FileUtil::CreateDir(GetMountPoint() + path.AsString()); -} - -/** - * Open a directory specified by its path - * @param path Path relative to the archive - * @return Opened directory, or nullptr - */ -std::unique_ptr<Directory> Archive_SDMC::OpenDirectory(const Path& path) const { - DEBUG_LOG(FILESYS, "called path=%s", path.DebugStr().c_str()); - Directory_SDMC* directory = new Directory_SDMC(this, path); - return std::unique_ptr<Directory>(directory); -} - -/** - * 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 index 74ce29c0d..1b801f217 100644 --- a/src/core/file_sys/archive_sdmc.h +++ b/src/core/file_sys/archive_sdmc.h @@ -1,12 +1,12 @@ // Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 +// Licensed under GPLv2 or any later version // Refer to the license.txt file included. #pragma once #include "common/common_types.h" -#include "core/file_sys/archive.h" +#include "core/file_sys/disk_archive.h" #include "core/loader/loader.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -15,10 +15,9 @@ namespace FileSys { /// File system interface to the SDMC archive -class Archive_SDMC final : public Archive { +class Archive_SDMC final : public DiskArchive { public: Archive_SDMC(const std::string& mount_point); - ~Archive_SDMC() override; /** * Initialize the archive. @@ -26,86 +25,7 @@ public: */ 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 Path& path, const Mode mode) const override; - - /** - * Delete a file specified by its path - * @param path Path relative to the archive - * @return Whether the file could be deleted - */ - bool DeleteFile(const FileSys::Path& path) const override; - - /** - * Delete a directory specified by its path - * @param path Path relative to the archive - * @return Whether the directory could be deleted - */ - bool DeleteDirectory(const FileSys::Path& path) const override; - - /** - * Create a directory specified by its path - * @param path Path relative to the archive - * @return Whether the directory could be created - */ - bool CreateDirectory(const Path& path) const override; - - /** - * Open a directory specified by its path - * @param path Path relative to the archive - * @return Opened directory, or nullptr - */ - std::unique_ptr<Directory> OpenDirectory(const Path& path) 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; + std::string GetName() const override { return "SDMC"; } }; } // namespace FileSys diff --git a/src/core/file_sys/archive_systemsavedata.cpp b/src/core/file_sys/archive_systemsavedata.cpp new file mode 100644 index 000000000..0da32d510 --- /dev/null +++ b/src/core/file_sys/archive_systemsavedata.cpp @@ -0,0 +1,39 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2 or any later version +// 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_systemsavedata.h" +#include "core/file_sys/disk_archive.h" +#include "core/settings.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// FileSys namespace + +namespace FileSys { + +static std::string GetSystemSaveDataPath(const std::string& mount_point, u64 save_id) { + u32 save_high = static_cast<u32>((save_id >> 32) & 0xFFFFFFFF); + u32 save_low = static_cast<u32>(save_id & 0xFFFFFFFF); + return Common::StringFromFormat("%s%08X/%08X/", mount_point.c_str(), save_low, save_high); +} + +Archive_SystemSaveData::Archive_SystemSaveData(const std::string& mount_point, u64 save_id) + : DiskArchive(GetSystemSaveDataPath(mount_point, save_id)) { + LOG_INFO(Service_FS, "Directory %s set as SystemSaveData.", this->mount_point.c_str()); +} + +bool Archive_SystemSaveData::Initialize() { + if (!FileUtil::CreateFullPath(mount_point)) { + LOG_ERROR(Service_FS, "Unable to create SystemSaveData path."); + return false; + } + + return true; +} + +} // namespace FileSys diff --git a/src/core/file_sys/archive_systemsavedata.h b/src/core/file_sys/archive_systemsavedata.h new file mode 100644 index 000000000..55d85193c --- /dev/null +++ b/src/core/file_sys/archive_systemsavedata.h @@ -0,0 +1,33 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "common/common_types.h" + +#include "core/file_sys/disk_archive.h" +#include "core/loader/loader.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// FileSys namespace + +namespace FileSys { + +/// File system interface to the SystemSaveData archive +/// TODO(Subv): This archive should point to a location in the NAND, +/// specifically nand:/data/<ID0>/sysdata/<SaveID-Low>/<SaveID-High> +class Archive_SystemSaveData final : public DiskArchive { +public: + Archive_SystemSaveData(const std::string& mount_point, u64 save_id); + + /** + * Initialize the archive. + * @return true if it initialized successfully + */ + bool Initialize(); + + std::string GetName() const override { return "SystemSaveData"; } +}; + +} // namespace FileSys diff --git a/src/core/file_sys/directory.h b/src/core/file_sys/directory_backend.h index e10431337..7f327dc42 100644 --- a/src/core/file_sys/directory.h +++ b/src/core/file_sys/directory_backend.h @@ -1,5 +1,5 @@ // Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 +// Licensed under GPLv2 or any later version // Refer to the license.txt file included. #pragma once @@ -36,10 +36,16 @@ static_assert(offsetof(Entry, extension) == 0x216, "Wrong offset for extension i static_assert(offsetof(Entry, is_archive) == 0x21E, "Wrong offset for is_archive in Entry."); static_assert(offsetof(Entry, file_size) == 0x220, "Wrong offset for file_size in Entry."); -class Directory : NonCopyable { +class DirectoryBackend : NonCopyable { public: - Directory() { } - virtual ~Directory() { } + DirectoryBackend() { } + virtual ~DirectoryBackend() { } + + /** + * Open the directory + * @return true if the directory opened correctly + */ + virtual bool Open() = 0; /** * List files contained in the directory diff --git a/src/core/file_sys/directory_romfs.cpp b/src/core/file_sys/directory_romfs.cpp index 4e8f4c04d..e130aca17 100644 --- a/src/core/file_sys/directory_romfs.cpp +++ b/src/core/file_sys/directory_romfs.cpp @@ -1,5 +1,5 @@ // Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 +// Licensed under GPLv2 or any later version // Refer to the license.txt file included. #include "common/common_types.h" @@ -17,20 +17,14 @@ Directory_RomFS::Directory_RomFS() { Directory_RomFS::~Directory_RomFS() { } -/** - * List files contained in the directory - * @param count Number of entries to return at once in entries - * @param entries Buffer to read data into - * @return Number of entries listed - */ +bool Directory_RomFS::Open() { + return false; +} + u32 Directory_RomFS::Read(const u32 count, Entry* entries) { return 0; } -/** - * Close the directory - * @return true if the directory closed correctly - */ bool Directory_RomFS::Close() const { return false; } diff --git a/src/core/file_sys/directory_romfs.h b/src/core/file_sys/directory_romfs.h index 4b71c4b13..2297f1645 100644 --- a/src/core/file_sys/directory_romfs.h +++ b/src/core/file_sys/directory_romfs.h @@ -1,12 +1,12 @@ // Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 +// Licensed under GPLv2 or any later version // Refer to the license.txt file included. #pragma once #include "common/common_types.h" -#include "core/file_sys/directory.h" +#include "core/file_sys/directory_backend.h" #include "core/loader/loader.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -14,12 +14,18 @@ namespace FileSys { -class Directory_RomFS final : public Directory { +class Directory_RomFS final : public DirectoryBackend { public: Directory_RomFS(); ~Directory_RomFS() override; /** + * Open the directory + * @return true if the directory opened correctly + */ + bool Open() override; + + /** * List files contained in the directory * @param count Number of entries to return at once in entries * @param entries Buffer to read data into diff --git a/src/core/file_sys/directory_sdmc.cpp b/src/core/file_sys/directory_sdmc.cpp deleted file mode 100644 index 60a197ce9..000000000 --- a/src/core/file_sys/directory_sdmc.cpp +++ /dev/null @@ -1,81 +0,0 @@ -// 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/directory_sdmc.h" -#include "core/file_sys/archive_sdmc.h" - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// FileSys namespace - -namespace FileSys { - -Directory_SDMC::Directory_SDMC(const Archive_SDMC* archive, const Path& path) { - // 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 /../../usr/bin can give the emulated program your installed programs. - std::string absolute_path = archive->GetMountPoint() + path.AsString(); - FileUtil::ScanDirectoryTree(absolute_path, directory); - children_iterator = directory.children.begin(); -} - -Directory_SDMC::~Directory_SDMC() { - Close(); -} - -/** - * List files contained in the directory - * @param count Number of entries to return at once in entries - * @param entries Buffer to read data into - * @return Number of entries listed - */ -u32 Directory_SDMC::Read(const u32 count, Entry* entries) { - u32 entries_read = 0; - - while (entries_read < count && children_iterator != directory.children.cend()) { - const FileUtil::FSTEntry& file = *children_iterator; - const std::string& filename = file.virtualName; - Entry& entry = entries[entries_read]; - - WARN_LOG(FILESYS, "File %s: size=%llu dir=%d", filename.c_str(), file.size, file.isDirectory); - - // TODO(Link Mauve): use a proper conversion to UTF-16. - for (size_t j = 0; j < FILENAME_LENGTH; ++j) { - entry.filename[j] = filename[j]; - if (!filename[j]) - break; - } - - FileUtil::SplitFilename83(filename, entry.short_name, entry.extension); - - entry.is_directory = file.isDirectory; - entry.is_hidden = (filename[0] == '.'); - entry.is_read_only = 0; - entry.file_size = file.size; - - // We emulate a SD card where the archive bit has never been cleared, as it would be on - // most user SD cards. - // Some homebrews (blargSNES for instance) are known to mistakenly use the archive bit as a - // file bit. - entry.is_archive = !file.isDirectory; - - ++entries_read; - ++children_iterator; - } - return entries_read; -} - -/** - * Close the directory - * @return true if the directory closed correctly - */ -bool Directory_SDMC::Close() const { - return true; -} - -} // namespace FileSys diff --git a/src/core/file_sys/directory_sdmc.h b/src/core/file_sys/directory_sdmc.h deleted file mode 100644 index 4520d0401..000000000 --- a/src/core/file_sys/directory_sdmc.h +++ /dev/null @@ -1,48 +0,0 @@ -// 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/directory.h" -#include "core/file_sys/archive_sdmc.h" -#include "core/loader/loader.h" - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// FileSys namespace - -namespace FileSys { - -class Directory_SDMC final : public Directory { -public: - Directory_SDMC(); - Directory_SDMC(const Archive_SDMC* archive, const Path& path); - ~Directory_SDMC() override; - - /** - * List files contained in the directory - * @param count Number of entries to return at once in entries - * @param entries Buffer to read data into - * @return Number of entries listed - */ - u32 Read(const u32 count, Entry* entries) override; - - /** - * Close the directory - * @return true if the directory closed correctly - */ - bool Close() const override; - -private: - u32 total_entries_in_directory; - FileUtil::FSTEntry directory; - - // We need to remember the last entry we returned, so a subsequent call to Read will continue - // from the next one. This iterator will always point to the next unread entry. - std::vector<FileUtil::FSTEntry>::iterator children_iterator; -}; - -} // namespace FileSys diff --git a/src/core/file_sys/disk_archive.cpp b/src/core/file_sys/disk_archive.cpp new file mode 100644 index 000000000..0197f727d --- /dev/null +++ b/src/core/file_sys/disk_archive.cpp @@ -0,0 +1,188 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2 or any later version +// 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/disk_archive.h" +#include "core/settings.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// FileSys namespace + +namespace FileSys { + +std::unique_ptr<FileBackend> DiskArchive::OpenFile(const Path& path, const Mode mode) const { + LOG_DEBUG(Service_FS, "called path=%s mode=%01X", path.DebugStr().c_str(), mode.hex); + DiskFile* file = new DiskFile(this, path, mode); + if (!file->Open()) + return nullptr; + return std::unique_ptr<FileBackend>(file); +} + +bool DiskArchive::DeleteFile(const Path& path) const { + return FileUtil::Delete(GetMountPoint() + path.AsString()); +} + +bool DiskArchive::RenameFile(const Path& src_path, const Path& dest_path) const { + return FileUtil::Rename(GetMountPoint() + src_path.AsString(), GetMountPoint() + dest_path.AsString()); +} + +bool DiskArchive::DeleteDirectory(const Path& path) const { + return FileUtil::DeleteDir(GetMountPoint() + path.AsString()); +} + +ResultCode DiskArchive::CreateFile(const FileSys::Path& path, u32 size) const { + std::string full_path = GetMountPoint() + path.AsString(); + + if (FileUtil::Exists(full_path)) + return ResultCode(ErrorDescription::AlreadyExists, ErrorModule::FS, ErrorSummary::NothingHappened, ErrorLevel::Info); + + if (size == 0) { + FileUtil::CreateEmptyFile(full_path); + return RESULT_SUCCESS; + } + + FileUtil::IOFile file(full_path, "wb"); + // Creates a sparse file (or a normal file on filesystems without the concept of sparse files) + // We do this by seeking to the right size, then writing a single null byte. + if (file.Seek(size - 1, SEEK_SET) && file.WriteBytes("", 1) == 1) + return RESULT_SUCCESS; + + return ResultCode(ErrorDescription::TooLarge, ErrorModule::FS, ErrorSummary::OutOfResource, ErrorLevel::Info); +} + + +bool DiskArchive::CreateDirectory(const Path& path) const { + return FileUtil::CreateDir(GetMountPoint() + path.AsString()); +} + +bool DiskArchive::RenameDirectory(const Path& src_path, const Path& dest_path) const { + return FileUtil::Rename(GetMountPoint() + src_path.AsString(), GetMountPoint() + dest_path.AsString()); +} + +std::unique_ptr<DirectoryBackend> DiskArchive::OpenDirectory(const Path& path) const { + LOG_DEBUG(Service_FS, "called path=%s", path.DebugStr().c_str()); + DiskDirectory* directory = new DiskDirectory(this, path); + if (!directory->Open()) + return nullptr; + return std::unique_ptr<DirectoryBackend>(directory); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +DiskFile::DiskFile(const DiskArchive* archive, const Path& 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. + this->path = archive->GetMountPoint() + path.AsString(); + this->mode.hex = mode.hex; + this->archive = archive; +} + +bool DiskFile::Open() { + if (!mode.create_flag && !FileUtil::Exists(path)) { + LOG_ERROR(Service_FS, "Non-existing file %s can't be open without mode create.", path.c_str()); + return false; + } + + std::string mode_string; + if (mode.create_flag) + mode_string = "w+"; + else if (mode.write_flag) + mode_string = "r+"; // Files opened with Write access can be read from + else if (mode.read_flag) + mode_string = "r"; + + // Open the file in binary mode, to avoid problems with CR/LF on Windows systems + mode_string += "b"; + + file = new FileUtil::IOFile(path, mode_string.c_str()); + return true; +} + +size_t DiskFile::Read(const u64 offset, const u32 length, u8* buffer) const { + file->Seek(offset, SEEK_SET); + return file->ReadBytes(buffer, length); +} + +size_t DiskFile::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 DiskFile::GetSize() const { + return static_cast<size_t>(file->GetSize()); +} + +bool DiskFile::SetSize(const u64 size) const { + file->Resize(size); + file->Flush(); + return true; +} + +bool DiskFile::Close() const { + return file->Close(); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +DiskDirectory::DiskDirectory(const DiskArchive* archive, const Path& path) { + // 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 /../../usr/bin can give the emulated program your installed programs. + this->path = archive->GetMountPoint() + path.AsString(); + this->archive = archive; +} + +bool DiskDirectory::Open() { + if (!FileUtil::IsDirectory(path)) + return false; + FileUtil::ScanDirectoryTree(path, directory); + children_iterator = directory.children.begin(); + return true; +} + +u32 DiskDirectory::Read(const u32 count, Entry* entries) { + u32 entries_read = 0; + + while (entries_read < count && children_iterator != directory.children.cend()) { + const FileUtil::FSTEntry& file = *children_iterator; + const std::string& filename = file.virtualName; + Entry& entry = entries[entries_read]; + + LOG_TRACE(Service_FS, "File %s: size=%llu dir=%d", filename.c_str(), file.size, file.isDirectory); + + // TODO(Link Mauve): use a proper conversion to UTF-16. + for (size_t j = 0; j < FILENAME_LENGTH; ++j) { + entry.filename[j] = filename[j]; + if (!filename[j]) + break; + } + + FileUtil::SplitFilename83(filename, entry.short_name, entry.extension); + + entry.is_directory = file.isDirectory; + entry.is_hidden = (filename[0] == '.'); + entry.is_read_only = 0; + entry.file_size = file.size; + + // We emulate a SD card where the archive bit has never been cleared, as it would be on + // most user SD cards. + // Some homebrews (blargSNES for instance) are known to mistakenly use the archive bit as a + // file bit. + entry.is_archive = !file.isDirectory; + + ++entries_read; + ++children_iterator; + } + return entries_read; +} + +} // namespace FileSys diff --git a/src/core/file_sys/disk_archive.h b/src/core/file_sys/disk_archive.h new file mode 100644 index 000000000..018ebd2ed --- /dev/null +++ b/src/core/file_sys/disk_archive.h @@ -0,0 +1,103 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "common/common_types.h" +#include "common/file_util.h" + +#include "core/file_sys/archive_backend.h" +#include "core/loader/loader.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// FileSys namespace + +namespace FileSys { + +/** + * Helper which implements a backend accessing the host machine's filesystem. + * This should be subclassed by concrete archive types, which will provide the + * base directory on the host filesystem and override any required functionality. + */ +class DiskArchive : public ArchiveBackend { +public: + DiskArchive(const std::string& mount_point_) : mount_point(mount_point_) {} + + virtual std::string GetName() const = 0; + std::unique_ptr<FileBackend> OpenFile(const Path& path, const Mode mode) const override; + bool DeleteFile(const Path& path) const override; + bool RenameFile(const Path& src_path, const Path& dest_path) const override; + bool DeleteDirectory(const Path& path) const override; + ResultCode CreateFile(const Path& path, u32 size) const override; + bool CreateDirectory(const Path& path) const override; + bool RenameDirectory(const Path& src_path, const Path& dest_path) const override; + std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const override; + + /** + * Getter for the path used for this Archive + * @return Mount point of that passthrough archive + */ + const std::string& GetMountPoint() const { + return mount_point; + } + +protected: + std::string mount_point; +}; + +class DiskFile : public FileBackend { +public: + DiskFile(); + DiskFile(const DiskArchive* archive, const Path& path, const Mode mode); + + ~DiskFile() override { + Close(); + } + + bool Open() override; + size_t Read(const u64 offset, const u32 length, u8* buffer) const override; + size_t Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const override; + size_t GetSize() const override; + bool SetSize(const u64 size) const override; + bool Close() const override; + + void Flush() const override { + file->Flush(); + } + +protected: + const DiskArchive* archive; + std::string path; + Mode mode; + FileUtil::IOFile* file; +}; + +class DiskDirectory : public DirectoryBackend { +public: + DiskDirectory(); + DiskDirectory(const DiskArchive* archive, const Path& path); + + ~DiskDirectory() override { + Close(); + } + + bool Open() override; + u32 Read(const u32 count, Entry* entries) override; + + bool Close() const override { + return true; + } + +protected: + const DiskArchive* archive; + std::string path; + u32 total_entries_in_directory; + FileUtil::FSTEntry directory; + + // We need to remember the last entry we returned, so a subsequent call to Read will continue + // from the next one. This iterator will always point to the next unread entry. + std::vector<FileUtil::FSTEntry>::iterator children_iterator; +}; + +} // namespace FileSys diff --git a/src/core/file_sys/file.h b/src/core/file_sys/file_backend.h index 4013b6c3e..35890af1f 100644 --- a/src/core/file_sys/file.h +++ b/src/core/file_sys/file_backend.h @@ -1,5 +1,5 @@ // Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 +// Licensed under GPLv2 or any later version // Refer to the license.txt file included. #pragma once @@ -13,10 +13,10 @@ namespace FileSys { -class File : NonCopyable { +class FileBackend : NonCopyable { public: - File() { } - virtual ~File() { } + FileBackend() { } + virtual ~FileBackend() { } /** * Open the file @@ -61,6 +61,11 @@ public: * @return true if the file closed correctly */ virtual bool Close() const = 0; + + /** + * Flushes the file + */ + virtual void Flush() const = 0; }; } // namespace FileSys diff --git a/src/core/file_sys/file_romfs.cpp b/src/core/file_sys/file_romfs.cpp index b55708df4..7467d6d31 100644 --- a/src/core/file_sys/file_romfs.cpp +++ b/src/core/file_sys/file_romfs.cpp @@ -1,74 +1,41 @@ // Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 +// Licensed under GPLv2 or any later version // Refer to the license.txt file included. #include "common/common_types.h" #include "core/file_sys/file_romfs.h" +#include "core/file_sys/archive_romfs.h" //////////////////////////////////////////////////////////////////////////////////////////////////// // FileSys namespace namespace FileSys { -File_RomFS::File_RomFS() { -} - -File_RomFS::~File_RomFS() { -} - -/** - * Open the file - * @return true if the file opened correctly - */ bool File_RomFS::Open() { - return false; + return true; } -/** - * 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 File_RomFS::Read(const u64 offset, const u32 length, u8* buffer) const { - return -1; + LOG_TRACE(Service_FS, "called offset=%llu, length=%d", offset, length); + memcpy(buffer, &archive->raw_data[(u32)offset], length); + return length; } -/** - * 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 flush The flush parameters (0 == do not flush) - * @param buffer Buffer to read data from - * @return Number of bytes written - */ size_t File_RomFS::Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const { - return -1; + LOG_WARNING(Service_FS, "Attempted to write to ROMFS."); + return 0; } -/** - * Get the size of the file in bytes - * @return Size of the file in bytes - */ size_t File_RomFS::GetSize() const { - return -1; + return sizeof(u8) * archive->raw_data.size(); } -/** - * Set the size of the file in bytes - * @param size New size of the file - * @return true if successful - */ bool File_RomFS::SetSize(const u64 size) const { + LOG_WARNING(Service_FS, "Attempted to set the size of ROMFS"); return false; } -/** - * Close the file - * @return true if the file closed correctly - */ bool File_RomFS::Close() const { return false; } diff --git a/src/core/file_sys/file_romfs.h b/src/core/file_sys/file_romfs.h index 5196701d3..04d8a16a2 100644 --- a/src/core/file_sys/file_romfs.h +++ b/src/core/file_sys/file_romfs.h @@ -1,12 +1,12 @@ // Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 +// Licensed under GPLv2 or any later version // Refer to the license.txt file included. #pragma once #include "common/common_types.h" -#include "core/file_sys/file.h" +#include "core/file_sys/file_backend.h" #include "core/loader/loader.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -14,10 +14,11 @@ namespace FileSys { -class File_RomFS final : public File { +class Archive_RomFS; + +class File_RomFS final : public FileBackend { public: - File_RomFS(); - ~File_RomFS() override; + File_RomFS(const Archive_RomFS* archive) : archive(archive) {} /** * Open the file @@ -62,6 +63,11 @@ public: * @return true if the file closed correctly */ bool Close() const override; + + void Flush() const override { } + +private: + const Archive_RomFS* archive; }; } // namespace FileSys diff --git a/src/core/file_sys/file_sdmc.cpp b/src/core/file_sys/file_sdmc.cpp deleted file mode 100644 index a4b90670a..000000000 --- a/src/core/file_sys/file_sdmc.cpp +++ /dev/null @@ -1,107 +0,0 @@ -// 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 Path& 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. - this->path = archive->GetMountPoint() + path.AsString(); - this->mode.hex = mode.hex; -} - -File_SDMC::~File_SDMC() { - Close(); -} - -/** - * Open the file - * @return true if the file opened correctly - */ -bool File_SDMC::Open() { - if (!mode.create_flag && !FileUtil::Exists(path)) { - ERROR_LOG(FILESYS, "Non-existing file %s can’t be open without mode create.", path.c_str()); - return false; - } - - std::string mode_string; - if (mode.read_flag && mode.write_flag) - mode_string = "w+"; - else if (mode.read_flag) - mode_string = "r"; - else if (mode.write_flag) - mode_string = "w"; - - file = new FileUtil::IOFile(path, mode_string.c_str()); - return true; -} - -/** - * 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 File_SDMC::Read(const u64 offset, const u32 length, u8* buffer) const { - file->Seek(offset, SEEK_SET); - return file->ReadBytes(buffer, length); -} - -/** - * 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 flush The flush parameters (0 == do not flush) - * @param buffer Buffer to read data from - * @return Number of bytes written - */ -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; -} - -/** - * Get the size of the file in bytes - * @return Size of the file in bytes - */ -size_t File_SDMC::GetSize() const { - return static_cast<size_t>(file->GetSize()); -} - -/** - * Set the size of the file in bytes - * @param size New size of the file - * @return true if successful - */ -bool File_SDMC::SetSize(const u64 size) const { - file->Resize(size); - file->Flush(); - return true; -} - -/** - * Close the file - * @return true if the file closed correctly - */ -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 deleted file mode 100644 index 80b445968..000000000 --- a/src/core/file_sys/file_sdmc.h +++ /dev/null @@ -1,75 +0,0 @@ -// 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 Path& path, const Mode mode); - ~File_SDMC() override; - - /** - * Open the file - * @return true if the file opened correctly - */ - bool Open() 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 flush The flush parameters (0 == do not flush) - * @param buffer Buffer to read data from - * @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; - - /** - * Set the size of the file in bytes - * @param size New size of the file - * @return true if successful - */ - bool SetSize(const u64 size) const override; - - /** - * Close the file - * @return true if the file closed correctly - */ - bool Close() const override; - -private: - std::string path; - Mode mode; - FileUtil::IOFile* file; -}; - -} // namespace FileSys |