Merge pull request #255 from Subv/sd_card
FS: Implemented access to the SD card
This commit is contained in:
commit
a397a9e9a4
|
@ -22,6 +22,8 @@ add_library(core STATIC
|
||||||
file_sys/romfs_filesystem.h
|
file_sys/romfs_filesystem.h
|
||||||
file_sys/savedata_factory.cpp
|
file_sys/savedata_factory.cpp
|
||||||
file_sys/savedata_factory.h
|
file_sys/savedata_factory.h
|
||||||
|
file_sys/sdmc_factory.cpp
|
||||||
|
file_sys/sdmc_factory.h
|
||||||
file_sys/storage.h
|
file_sys/storage.h
|
||||||
frontend/emu_window.cpp
|
frontend/emu_window.cpp
|
||||||
frontend/emu_window.h
|
frontend/emu_window.h
|
||||||
|
|
|
@ -6,34 +6,28 @@
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
#include "common/common_funcs.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
#include "core/file_sys/filesystem.h"
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// FileSys namespace
|
// FileSys namespace
|
||||||
|
|
||||||
namespace FileSys {
|
namespace FileSys {
|
||||||
|
|
||||||
// Structure of a directory entry, from http://3dbrew.org/wiki/FSDir:Read#Entry_format
|
// Structure of a directory entry, from
|
||||||
const size_t FILENAME_LENGTH = 0x20C / 2;
|
// http://switchbrew.org/index.php?title=Filesystem_services#DirectoryEntry
|
||||||
|
const size_t FILENAME_LENGTH = 0x300;
|
||||||
struct Entry {
|
struct Entry {
|
||||||
char16_t filename[FILENAME_LENGTH]; // Entry name (UTF-16, null-terminated)
|
char filename[FILENAME_LENGTH];
|
||||||
std::array<char, 9> short_name; // 8.3 file name ('longfilename' -> 'LONGFI~1', null-terminated)
|
INSERT_PADDING_BYTES(4);
|
||||||
char unknown1; // unknown (observed values: 0x0A, 0x70, 0xFD)
|
EntryType type;
|
||||||
std::array<char, 4>
|
INSERT_PADDING_BYTES(3);
|
||||||
extension; // 8.3 file extension (set to spaces for directories, null-terminated)
|
u64 file_size;
|
||||||
char unknown2; // unknown (always 0x01)
|
|
||||||
char unknown3; // unknown (0x00 or 0x08)
|
|
||||||
char is_directory; // directory flag
|
|
||||||
char is_hidden; // hidden flag
|
|
||||||
char is_archive; // archive flag
|
|
||||||
char is_read_only; // read-only flag
|
|
||||||
u64 file_size; // file size (for files only)
|
|
||||||
};
|
};
|
||||||
static_assert(sizeof(Entry) == 0x228, "Directory Entry struct isn't exactly 0x228 bytes long!");
|
static_assert(sizeof(Entry) == 0x310, "Directory Entry struct isn't exactly 0x310 bytes long!");
|
||||||
static_assert(offsetof(Entry, short_name) == 0x20C, "Wrong offset for short_name in Entry.");
|
static_assert(offsetof(Entry, type) == 0x304, "Wrong offset for type in Entry.");
|
||||||
static_assert(offsetof(Entry, extension) == 0x216, "Wrong offset for extension in Entry.");
|
static_assert(offsetof(Entry, file_size) == 0x308, "Wrong offset for file_size in Entry.");
|
||||||
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 DirectoryBackend : NonCopyable {
|
class DirectoryBackend : NonCopyable {
|
||||||
public:
|
public:
|
||||||
|
@ -46,7 +40,10 @@ public:
|
||||||
* @param entries Buffer to read data into
|
* @param entries Buffer to read data into
|
||||||
* @return Number of entries listed
|
* @return Number of entries listed
|
||||||
*/
|
*/
|
||||||
virtual u32 Read(const u32 count, Entry* entries) = 0;
|
virtual u64 Read(const u64 count, Entry* entries) = 0;
|
||||||
|
|
||||||
|
/// Returns the number of entries still left to read.
|
||||||
|
virtual u64 GetEntryCount() const = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Close the directory
|
* Close the directory
|
||||||
|
|
|
@ -11,16 +11,43 @@
|
||||||
|
|
||||||
namespace FileSys {
|
namespace FileSys {
|
||||||
|
|
||||||
|
static std::string ModeFlagsToString(Mode mode) {
|
||||||
|
std::string mode_str;
|
||||||
|
u32 mode_flags = static_cast<u32>(mode);
|
||||||
|
|
||||||
|
// Calculate the correct open mode for the file.
|
||||||
|
if ((mode_flags & static_cast<u32>(Mode::Read)) &&
|
||||||
|
(mode_flags & static_cast<u32>(Mode::Write))) {
|
||||||
|
if (mode_flags & static_cast<u32>(Mode::Append))
|
||||||
|
mode_str = "a+";
|
||||||
|
else
|
||||||
|
mode_str = "r+";
|
||||||
|
} else {
|
||||||
|
if (mode_flags & static_cast<u32>(Mode::Read))
|
||||||
|
mode_str = "r";
|
||||||
|
else if (mode_flags & static_cast<u32>(Mode::Append))
|
||||||
|
mode_str = "a";
|
||||||
|
else if (mode_flags & static_cast<u32>(Mode::Write))
|
||||||
|
mode_str = "w";
|
||||||
|
}
|
||||||
|
|
||||||
|
mode_str += "b";
|
||||||
|
|
||||||
|
return mode_str;
|
||||||
|
}
|
||||||
|
|
||||||
std::string Disk_FileSystem::GetName() const {
|
std::string Disk_FileSystem::GetName() const {
|
||||||
return "Disk";
|
return "Disk";
|
||||||
}
|
}
|
||||||
|
|
||||||
ResultVal<std::unique_ptr<StorageBackend>> Disk_FileSystem::OpenFile(const std::string& path,
|
ResultVal<std::unique_ptr<StorageBackend>> Disk_FileSystem::OpenFile(const std::string& path,
|
||||||
Mode mode) const {
|
Mode mode) const {
|
||||||
ASSERT_MSG(mode == Mode::Read || mode == Mode::Write, "Other file modes are not supported");
|
|
||||||
|
// Calculate the correct open mode for the file.
|
||||||
|
std::string mode_str = ModeFlagsToString(mode);
|
||||||
|
|
||||||
std::string full_path = base_directory + path;
|
std::string full_path = base_directory + path;
|
||||||
auto file = std::make_shared<FileUtil::IOFile>(full_path, mode == Mode::Read ? "rb" : "wb");
|
auto file = std::make_shared<FileUtil::IOFile>(full_path, mode_str.c_str());
|
||||||
|
|
||||||
if (!file->IsOpen()) {
|
if (!file->IsOpen()) {
|
||||||
return ERROR_PATH_NOT_FOUND;
|
return ERROR_PATH_NOT_FOUND;
|
||||||
|
@ -75,8 +102,15 @@ ResultCode Disk_FileSystem::CreateFile(const std::string& path, u64 size) const
|
||||||
return ResultCode(-1);
|
return ResultCode(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
ResultCode Disk_FileSystem::CreateDirectory(const Path& path) const {
|
ResultCode Disk_FileSystem::CreateDirectory(const std::string& path) const {
|
||||||
LOG_WARNING(Service_FS, "(STUBBED) called");
|
// TODO(Subv): Perform path validation to prevent escaping the emulator sandbox.
|
||||||
|
std::string full_path = base_directory + path;
|
||||||
|
|
||||||
|
if (FileUtil::CreateDir(full_path)) {
|
||||||
|
return RESULT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_CRITICAL(Service_FS, "(unreachable) Unknown error creating %s", full_path.c_str());
|
||||||
// TODO(wwylele): Use correct error code
|
// TODO(wwylele): Use correct error code
|
||||||
return ResultCode(-1);
|
return ResultCode(-1);
|
||||||
}
|
}
|
||||||
|
@ -88,8 +122,17 @@ ResultCode Disk_FileSystem::RenameDirectory(const Path& src_path, const Path& de
|
||||||
}
|
}
|
||||||
|
|
||||||
ResultVal<std::unique_ptr<DirectoryBackend>> Disk_FileSystem::OpenDirectory(
|
ResultVal<std::unique_ptr<DirectoryBackend>> Disk_FileSystem::OpenDirectory(
|
||||||
const Path& path) const {
|
const std::string& path) const {
|
||||||
return MakeResult<std::unique_ptr<DirectoryBackend>>(std::make_unique<Disk_Directory>());
|
|
||||||
|
std::string full_path = base_directory + path;
|
||||||
|
|
||||||
|
if (!FileUtil::IsDirectory(full_path)) {
|
||||||
|
// TODO(Subv): Find the correct error code for this.
|
||||||
|
return ResultCode(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto directory = std::make_unique<Disk_Directory>(full_path);
|
||||||
|
return MakeResult<std::unique_ptr<DirectoryBackend>>(std::move(directory));
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 Disk_FileSystem::GetFreeSpaceSize() const {
|
u64 Disk_FileSystem::GetFreeSpaceSize() const {
|
||||||
|
@ -103,8 +146,10 @@ ResultVal<FileSys::EntryType> Disk_FileSystem::GetEntryType(const std::string& p
|
||||||
return ERROR_PATH_NOT_FOUND;
|
return ERROR_PATH_NOT_FOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(Subv): Find out the EntryType values
|
if (FileUtil::IsDirectory(full_path))
|
||||||
UNIMPLEMENTED_MSG("Unimplemented GetEntryType");
|
return MakeResult(EntryType::Directory);
|
||||||
|
|
||||||
|
return MakeResult(EntryType::File);
|
||||||
}
|
}
|
||||||
|
|
||||||
ResultVal<size_t> Disk_Storage::Read(const u64 offset, const size_t length, u8* buffer) const {
|
ResultVal<size_t> Disk_Storage::Read(const u64 offset, const size_t length, u8* buffer) const {
|
||||||
|
@ -133,14 +178,50 @@ bool Disk_Storage::SetSize(const u64 size) const {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 Disk_Directory::Read(const u32 count, Entry* entries) {
|
Disk_Directory::Disk_Directory(const std::string& path) : directory() {
|
||||||
LOG_WARNING(Service_FS, "(STUBBED) called");
|
unsigned size = FileUtil::ScanDirectoryTree(path, directory);
|
||||||
return 0;
|
directory.size = size;
|
||||||
|
directory.isDirectory = true;
|
||||||
|
children_iterator = directory.children.begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Disk_Directory::Close() const {
|
u64 Disk_Directory::Read(const u64 count, Entry* entries) {
|
||||||
LOG_WARNING(Service_FS, "(STUBBED) called");
|
u64 entries_read = 0;
|
||||||
return true;
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file.isDirectory) {
|
||||||
|
entry.file_size = 0;
|
||||||
|
entry.type = EntryType::Directory;
|
||||||
|
} else {
|
||||||
|
entry.file_size = file.size;
|
||||||
|
entry.type = EntryType::File;
|
||||||
|
}
|
||||||
|
|
||||||
|
++entries_read;
|
||||||
|
++children_iterator;
|
||||||
|
}
|
||||||
|
return entries_read;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 Disk_Directory::GetEntryCount() const {
|
||||||
|
// We convert the children iterator into a const_iterator to allow template argument deduction
|
||||||
|
// in std::distance.
|
||||||
|
std::vector<FileUtil::FSTEntry>::const_iterator current = children_iterator;
|
||||||
|
return std::distance(current, directory.children.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace FileSys
|
} // namespace FileSys
|
||||||
|
|
|
@ -30,9 +30,10 @@ public:
|
||||||
ResultCode DeleteDirectory(const Path& path) const override;
|
ResultCode DeleteDirectory(const Path& path) const override;
|
||||||
ResultCode DeleteDirectoryRecursively(const Path& path) const override;
|
ResultCode DeleteDirectoryRecursively(const Path& path) const override;
|
||||||
ResultCode CreateFile(const std::string& path, u64 size) const override;
|
ResultCode CreateFile(const std::string& path, u64 size) const override;
|
||||||
ResultCode CreateDirectory(const Path& path) const override;
|
ResultCode CreateDirectory(const std::string& path) const override;
|
||||||
ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const override;
|
ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const override;
|
||||||
ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const override;
|
ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(
|
||||||
|
const std::string& path) const override;
|
||||||
u64 GetFreeSpaceSize() const override;
|
u64 GetFreeSpaceSize() const override;
|
||||||
ResultVal<EntryType> GetEntryType(const std::string& path) const override;
|
ResultVal<EntryType> GetEntryType(const std::string& path) const override;
|
||||||
|
|
||||||
|
@ -59,8 +60,26 @@ private:
|
||||||
|
|
||||||
class Disk_Directory : public DirectoryBackend {
|
class Disk_Directory : public DirectoryBackend {
|
||||||
public:
|
public:
|
||||||
u32 Read(const u32 count, Entry* entries) override;
|
Disk_Directory(const std::string& path);
|
||||||
bool Close() const override;
|
|
||||||
|
~Disk_Directory() override {
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 Read(const u64 count, Entry* entries) override;
|
||||||
|
u64 GetEntryCount() const override;
|
||||||
|
|
||||||
|
bool Close() const override {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
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
|
} // namespace FileSys
|
||||||
|
|
|
@ -27,7 +27,7 @@ enum LowPathType : u32 {
|
||||||
Wchar = 4,
|
Wchar = 4,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum EntryType : u32 {
|
enum EntryType : u8 {
|
||||||
Directory = 0,
|
Directory = 0,
|
||||||
File = 1,
|
File = 1,
|
||||||
};
|
};
|
||||||
|
@ -35,6 +35,7 @@ enum EntryType : u32 {
|
||||||
enum class Mode : u32 {
|
enum class Mode : u32 {
|
||||||
Read = 1,
|
Read = 1,
|
||||||
Write = 2,
|
Write = 2,
|
||||||
|
Append = 4,
|
||||||
};
|
};
|
||||||
|
|
||||||
class Path {
|
class Path {
|
||||||
|
@ -103,7 +104,7 @@ public:
|
||||||
* @param path Path relative to the archive
|
* @param path Path relative to the archive
|
||||||
* @return Result of the operation
|
* @return Result of the operation
|
||||||
*/
|
*/
|
||||||
virtual ResultCode CreateDirectory(const Path& path) const = 0;
|
virtual ResultCode CreateDirectory(const std::string& path) const = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete a directory specified by its path
|
* Delete a directory specified by its path
|
||||||
|
@ -149,7 +150,8 @@ public:
|
||||||
* @param path Path relative to the archive
|
* @param path Path relative to the archive
|
||||||
* @return Opened directory, or error code
|
* @return Opened directory, or error code
|
||||||
*/
|
*/
|
||||||
virtual ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const = 0;
|
virtual ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(
|
||||||
|
const std::string& path) const = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the free space
|
* Get the free space
|
||||||
|
|
|
@ -55,7 +55,7 @@ ResultCode RomFS_FileSystem::CreateFile(const std::string& path, u64 size) const
|
||||||
return ResultCode(-1);
|
return ResultCode(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
ResultCode RomFS_FileSystem::CreateDirectory(const Path& path) const {
|
ResultCode RomFS_FileSystem::CreateDirectory(const std::string& path) const {
|
||||||
LOG_CRITICAL(Service_FS, "Attempted to create a directory in an ROMFS archive (%s).",
|
LOG_CRITICAL(Service_FS, "Attempted to create a directory in an ROMFS archive (%s).",
|
||||||
GetName().c_str());
|
GetName().c_str());
|
||||||
// TODO(wwylele): Use correct error code
|
// TODO(wwylele): Use correct error code
|
||||||
|
@ -70,7 +70,8 @@ ResultCode RomFS_FileSystem::RenameDirectory(const Path& src_path, const Path& d
|
||||||
}
|
}
|
||||||
|
|
||||||
ResultVal<std::unique_ptr<DirectoryBackend>> RomFS_FileSystem::OpenDirectory(
|
ResultVal<std::unique_ptr<DirectoryBackend>> RomFS_FileSystem::OpenDirectory(
|
||||||
const Path& path) const {
|
const std::string& path) const {
|
||||||
|
LOG_WARNING(Service_FS, "Opening Directory in a ROMFS archive");
|
||||||
return MakeResult<std::unique_ptr<DirectoryBackend>>(std::make_unique<ROMFSDirectory>());
|
return MakeResult<std::unique_ptr<DirectoryBackend>>(std::make_unique<ROMFSDirectory>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,9 +36,10 @@ public:
|
||||||
ResultCode DeleteDirectory(const Path& path) const override;
|
ResultCode DeleteDirectory(const Path& path) const override;
|
||||||
ResultCode DeleteDirectoryRecursively(const Path& path) const override;
|
ResultCode DeleteDirectoryRecursively(const Path& path) const override;
|
||||||
ResultCode CreateFile(const std::string& path, u64 size) const override;
|
ResultCode CreateFile(const std::string& path, u64 size) const override;
|
||||||
ResultCode CreateDirectory(const Path& path) const override;
|
ResultCode CreateDirectory(const std::string& path) const override;
|
||||||
ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const override;
|
ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const override;
|
||||||
ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const override;
|
ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(
|
||||||
|
const std::string& path) const override;
|
||||||
u64 GetFreeSpaceSize() const override;
|
u64 GetFreeSpaceSize() const override;
|
||||||
ResultVal<EntryType> GetEntryType(const std::string& path) const override;
|
ResultVal<EntryType> GetEntryType(const std::string& path) const override;
|
||||||
|
|
||||||
|
@ -70,7 +71,10 @@ private:
|
||||||
|
|
||||||
class ROMFSDirectory : public DirectoryBackend {
|
class ROMFSDirectory : public DirectoryBackend {
|
||||||
public:
|
public:
|
||||||
u32 Read(const u32 count, Entry* entries) override {
|
u64 Read(const u64 count, Entry* entries) override {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
u64 GetEntryCount() const override {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
bool Close() const override {
|
bool Close() const override {
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
// Copyright 2018 yuzu emulator team
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <cinttypes>
|
||||||
|
#include <memory>
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "common/logging/log.h"
|
||||||
|
#include "common/string_util.h"
|
||||||
|
#include "core/core.h"
|
||||||
|
#include "core/file_sys/disk_filesystem.h"
|
||||||
|
#include "core/file_sys/sdmc_factory.h"
|
||||||
|
|
||||||
|
namespace FileSys {
|
||||||
|
|
||||||
|
SDMC_Factory::SDMC_Factory(std::string sd_directory) : sd_directory(std::move(sd_directory)) {}
|
||||||
|
|
||||||
|
ResultVal<std::unique_ptr<FileSystemBackend>> SDMC_Factory::Open(const Path& path) {
|
||||||
|
// Create the SD Card directory if it doesn't already exist.
|
||||||
|
if (!FileUtil::IsDirectory(sd_directory)) {
|
||||||
|
FileUtil::CreateFullPath(sd_directory);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto archive = std::make_unique<Disk_FileSystem>(sd_directory);
|
||||||
|
return MakeResult<std::unique_ptr<FileSystemBackend>>(std::move(archive));
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultCode SDMC_Factory::Format(const Path& path) {
|
||||||
|
LOG_ERROR(Service_FS, "Unimplemented Format archive %s", GetName().c_str());
|
||||||
|
// TODO(Subv): Find the right error code for this
|
||||||
|
return ResultCode(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultVal<ArchiveFormatInfo> SDMC_Factory::GetFormatInfo(const Path& path) const {
|
||||||
|
LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive %s", GetName().c_str());
|
||||||
|
// TODO(bunnei): Find the right error code for this
|
||||||
|
return ResultCode(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace FileSys
|
|
@ -0,0 +1,31 @@
|
||||||
|
// Copyright 2018 yuzu emulator team
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "core/file_sys/filesystem.h"
|
||||||
|
#include "core/hle/result.h"
|
||||||
|
|
||||||
|
namespace FileSys {
|
||||||
|
|
||||||
|
/// File system interface to the SDCard archive
|
||||||
|
class SDMC_Factory final : public FileSystemFactory {
|
||||||
|
public:
|
||||||
|
explicit SDMC_Factory(std::string sd_directory);
|
||||||
|
|
||||||
|
std::string GetName() const override {
|
||||||
|
return "SDMC_Factory";
|
||||||
|
}
|
||||||
|
ResultVal<std::unique_ptr<FileSystemBackend>> Open(const Path& path) override;
|
||||||
|
ResultCode Format(const Path& path) override;
|
||||||
|
ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string sd_directory;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace FileSys
|
|
@ -6,6 +6,7 @@
|
||||||
#include "common/file_util.h"
|
#include "common/file_util.h"
|
||||||
#include "core/file_sys/filesystem.h"
|
#include "core/file_sys/filesystem.h"
|
||||||
#include "core/file_sys/savedata_factory.h"
|
#include "core/file_sys/savedata_factory.h"
|
||||||
|
#include "core/file_sys/sdmc_factory.h"
|
||||||
#include "core/hle/service/filesystem/filesystem.h"
|
#include "core/hle/service/filesystem/filesystem.h"
|
||||||
#include "core/hle/service/filesystem/fsp_srv.h"
|
#include "core/hle/service/filesystem/fsp_srv.h"
|
||||||
|
|
||||||
|
@ -60,9 +61,13 @@ void RegisterFileSystems() {
|
||||||
filesystem_map.clear();
|
filesystem_map.clear();
|
||||||
|
|
||||||
std::string nand_directory = FileUtil::GetUserPath(D_NAND_IDX);
|
std::string nand_directory = FileUtil::GetUserPath(D_NAND_IDX);
|
||||||
|
std::string sd_directory = FileUtil::GetUserPath(D_SDMC_IDX);
|
||||||
|
|
||||||
auto savedata = std::make_unique<FileSys::SaveData_Factory>(std::move(nand_directory));
|
auto savedata = std::make_unique<FileSys::SaveData_Factory>(std::move(nand_directory));
|
||||||
RegisterFileSystem(std::move(savedata), Type::SaveData);
|
RegisterFileSystem(std::move(savedata), Type::SaveData);
|
||||||
|
|
||||||
|
auto sdcard = std::make_unique<FileSys::SDMC_Factory>(std::move(sd_directory));
|
||||||
|
RegisterFileSystem(std::move(sdcard), Type::SDMC);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstallInterfaces(SM::ServiceManager& service_manager) {
|
void InstallInterfaces(SM::ServiceManager& service_manager) {
|
||||||
|
|
|
@ -26,6 +26,7 @@ namespace FileSystem {
|
||||||
enum class Type {
|
enum class Type {
|
||||||
RomFS = 1,
|
RomFS = 1,
|
||||||
SaveData = 2,
|
SaveData = 2,
|
||||||
|
SDMC = 3,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include <cinttypes>
|
#include <cinttypes>
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
|
#include "core/file_sys/directory.h"
|
||||||
#include "core/file_sys/filesystem.h"
|
#include "core/file_sys/filesystem.h"
|
||||||
#include "core/file_sys/storage.h"
|
#include "core/file_sys/storage.h"
|
||||||
#include "core/hle/ipc_helpers.h"
|
#include "core/hle/ipc_helpers.h"
|
||||||
|
@ -151,14 +152,66 @@ private:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class IDirectory final : public ServiceFramework<IDirectory> {
|
||||||
|
public:
|
||||||
|
explicit IDirectory(std::unique_ptr<FileSys::DirectoryBackend>&& backend)
|
||||||
|
: ServiceFramework("IDirectory"), backend(std::move(backend)) {
|
||||||
|
static const FunctionInfo functions[] = {
|
||||||
|
{0, &IDirectory::Read, "Read"},
|
||||||
|
{1, &IDirectory::GetEntryCount, "GetEntryCount"},
|
||||||
|
};
|
||||||
|
RegisterHandlers(functions);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<FileSys::DirectoryBackend> backend;
|
||||||
|
|
||||||
|
void Read(Kernel::HLERequestContext& ctx) {
|
||||||
|
IPC::RequestParser rp{ctx};
|
||||||
|
const u64 unk = rp.Pop<u64>();
|
||||||
|
|
||||||
|
LOG_DEBUG(Service_FS, "called, unk=0x%llx", unk);
|
||||||
|
|
||||||
|
// Calculate how many entries we can fit in the output buffer
|
||||||
|
u64 count_entries = ctx.GetWriteBufferSize() / sizeof(FileSys::Entry);
|
||||||
|
|
||||||
|
// Read the data from the Directory backend
|
||||||
|
std::vector<FileSys::Entry> entries(count_entries);
|
||||||
|
u64 read_entries = backend->Read(count_entries, entries.data());
|
||||||
|
|
||||||
|
// Convert the data into a byte array
|
||||||
|
std::vector<u8> output(entries.size() * sizeof(FileSys::Entry));
|
||||||
|
std::memcpy(output.data(), entries.data(), output.size());
|
||||||
|
|
||||||
|
// Write the data to memory
|
||||||
|
ctx.WriteBuffer(output);
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 4};
|
||||||
|
rb.Push(RESULT_SUCCESS);
|
||||||
|
rb.Push(read_entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetEntryCount(Kernel::HLERequestContext& ctx) {
|
||||||
|
LOG_DEBUG(Service_FS, "called");
|
||||||
|
|
||||||
|
u64 count = backend->GetEntryCount();
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 4};
|
||||||
|
rb.Push(RESULT_SUCCESS);
|
||||||
|
rb.Push(count);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class IFileSystem final : public ServiceFramework<IFileSystem> {
|
class IFileSystem final : public ServiceFramework<IFileSystem> {
|
||||||
public:
|
public:
|
||||||
explicit IFileSystem(std::unique_ptr<FileSys::FileSystemBackend>&& backend)
|
explicit IFileSystem(std::unique_ptr<FileSys::FileSystemBackend>&& backend)
|
||||||
: ServiceFramework("IFileSystem"), backend(std::move(backend)) {
|
: ServiceFramework("IFileSystem"), backend(std::move(backend)) {
|
||||||
static const FunctionInfo functions[] = {
|
static const FunctionInfo functions[] = {
|
||||||
{0, &IFileSystem::CreateFile, "CreateFile"},
|
{0, &IFileSystem::CreateFile, "CreateFile"},
|
||||||
|
{2, &IFileSystem::CreateDirectory, "CreateDirectory"},
|
||||||
{7, &IFileSystem::GetEntryType, "GetEntryType"},
|
{7, &IFileSystem::GetEntryType, "GetEntryType"},
|
||||||
{8, &IFileSystem::OpenFile, "OpenFile"},
|
{8, &IFileSystem::OpenFile, "OpenFile"},
|
||||||
|
{9, &IFileSystem::OpenDirectory, "OpenDirectory"},
|
||||||
{10, &IFileSystem::Commit, "Commit"},
|
{10, &IFileSystem::Commit, "Commit"},
|
||||||
};
|
};
|
||||||
RegisterHandlers(functions);
|
RegisterHandlers(functions);
|
||||||
|
@ -182,6 +235,20 @@ public:
|
||||||
rb.Push(backend->CreateFile(name, size));
|
rb.Push(backend->CreateFile(name, size));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CreateDirectory(Kernel::HLERequestContext& ctx) {
|
||||||
|
IPC::RequestParser rp{ctx};
|
||||||
|
|
||||||
|
auto file_buffer = ctx.ReadBuffer();
|
||||||
|
auto end = std::find(file_buffer.begin(), file_buffer.end(), '\0');
|
||||||
|
|
||||||
|
std::string name(file_buffer.begin(), end);
|
||||||
|
|
||||||
|
LOG_DEBUG(Service_FS, "called directory %s", name.c_str());
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
rb.Push(backend->CreateDirectory(name));
|
||||||
|
}
|
||||||
|
|
||||||
void OpenFile(Kernel::HLERequestContext& ctx) {
|
void OpenFile(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
|
|
||||||
|
@ -208,6 +275,33 @@ public:
|
||||||
rb.PushIpcInterface<IFile>(std::move(file));
|
rb.PushIpcInterface<IFile>(std::move(file));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OpenDirectory(Kernel::HLERequestContext& ctx) {
|
||||||
|
IPC::RequestParser rp{ctx};
|
||||||
|
|
||||||
|
auto file_buffer = ctx.ReadBuffer();
|
||||||
|
auto end = std::find(file_buffer.begin(), file_buffer.end(), '\0');
|
||||||
|
|
||||||
|
std::string name(file_buffer.begin(), end);
|
||||||
|
|
||||||
|
// TODO(Subv): Implement this filter.
|
||||||
|
u32 filter_flags = rp.Pop<u32>();
|
||||||
|
|
||||||
|
LOG_DEBUG(Service_FS, "called directory %s filter %u", name.c_str(), filter_flags);
|
||||||
|
|
||||||
|
auto result = backend->OpenDirectory(name);
|
||||||
|
if (result.Failed()) {
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
rb.Push(result.Code());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto directory = std::move(result.Unwrap());
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||||
|
rb.Push(RESULT_SUCCESS);
|
||||||
|
rb.PushIpcInterface<IDirectory>(std::move(directory));
|
||||||
|
}
|
||||||
|
|
||||||
void GetEntryType(Kernel::HLERequestContext& ctx) {
|
void GetEntryType(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
|
|
||||||
|
@ -274,10 +368,14 @@ void FSP_SRV::Initalize(Kernel::HLERequestContext& ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void FSP_SRV::MountSdCard(Kernel::HLERequestContext& ctx) {
|
void FSP_SRV::MountSdCard(Kernel::HLERequestContext& ctx) {
|
||||||
LOG_WARNING(Service_FS, "(STUBBED) called");
|
LOG_DEBUG(Service_FS, "called");
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
FileSys::Path unused;
|
||||||
|
auto filesystem = OpenFileSystem(Type::SDMC, unused).Unwrap();
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(RESULT_SUCCESS);
|
||||||
|
rb.PushIpcInterface<IFileSystem>(std::move(filesystem));
|
||||||
}
|
}
|
||||||
|
|
||||||
void FSP_SRV::CreateSaveData(Kernel::HLERequestContext& ctx) {
|
void FSP_SRV::CreateSaveData(Kernel::HLERequestContext& ctx) {
|
||||||
|
|
Loading…
Reference in New Issue