Merge pull request #288 from Subv/savedata_stuff
FS_U: Implemented the SaveData archive
This commit is contained in:
commit
94a103a000
|
@ -40,6 +40,7 @@
|
||||||
#define MAPS_DIR "maps"
|
#define MAPS_DIR "maps"
|
||||||
#define CACHE_DIR "cache"
|
#define CACHE_DIR "cache"
|
||||||
#define SDMC_DIR "sdmc"
|
#define SDMC_DIR "sdmc"
|
||||||
|
#define SAVEDATA_DIR "savedata"
|
||||||
#define SYSDATA_DIR "sysdata"
|
#define SYSDATA_DIR "sysdata"
|
||||||
#define SHADERCACHE_DIR "shader_cache"
|
#define SHADERCACHE_DIR "shader_cache"
|
||||||
#define STATESAVES_DIR "state_saves"
|
#define STATESAVES_DIR "state_saves"
|
||||||
|
|
|
@ -676,6 +676,7 @@ const std::string& GetUserPath(const unsigned int DirIDX, const std::string &new
|
||||||
paths[D_MAPS_IDX] = paths[D_USER_IDX] + MAPS_DIR DIR_SEP;
|
paths[D_MAPS_IDX] = paths[D_USER_IDX] + MAPS_DIR DIR_SEP;
|
||||||
paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP;
|
paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP;
|
||||||
paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP;
|
paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP;
|
||||||
|
paths[D_SAVEDATA_IDX] = paths[D_USER_IDX] + SAVEDATA_DIR DIR_SEP;
|
||||||
paths[D_SYSDATA_IDX] = paths[D_USER_IDX] + SYSDATA_DIR DIR_SEP;
|
paths[D_SYSDATA_IDX] = paths[D_USER_IDX] + SYSDATA_DIR DIR_SEP;
|
||||||
paths[D_SHADERCACHE_IDX] = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP;
|
paths[D_SHADERCACHE_IDX] = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP;
|
||||||
paths[D_SHADERS_IDX] = paths[D_USER_IDX] + SHADERS_DIR DIR_SEP;
|
paths[D_SHADERS_IDX] = paths[D_USER_IDX] + SHADERS_DIR DIR_SEP;
|
||||||
|
@ -718,6 +719,7 @@ const std::string& GetUserPath(const unsigned int DirIDX, const std::string &new
|
||||||
paths[D_MAPS_IDX] = paths[D_USER_IDX] + MAPS_DIR DIR_SEP;
|
paths[D_MAPS_IDX] = paths[D_USER_IDX] + MAPS_DIR DIR_SEP;
|
||||||
paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP;
|
paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP;
|
||||||
paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP;
|
paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP;
|
||||||
|
paths[D_SAVEDATA_IDX] = paths[D_USER_IDX] + SAVEDATA_DIR DIR_SEP;
|
||||||
paths[D_SHADERCACHE_IDX] = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP;
|
paths[D_SHADERCACHE_IDX] = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP;
|
||||||
paths[D_SHADERS_IDX] = paths[D_USER_IDX] + SHADERS_DIR DIR_SEP;
|
paths[D_SHADERS_IDX] = paths[D_USER_IDX] + SHADERS_DIR DIR_SEP;
|
||||||
paths[D_STATESAVES_IDX] = paths[D_USER_IDX] + STATESAVES_DIR DIR_SEP;
|
paths[D_STATESAVES_IDX] = paths[D_USER_IDX] + STATESAVES_DIR DIR_SEP;
|
||||||
|
|
|
@ -27,6 +27,7 @@ enum {
|
||||||
D_STATESAVES_IDX,
|
D_STATESAVES_IDX,
|
||||||
D_SCREENSHOTS_IDX,
|
D_SCREENSHOTS_IDX,
|
||||||
D_SDMC_IDX,
|
D_SDMC_IDX,
|
||||||
|
D_SAVEDATA_IDX,
|
||||||
D_SYSDATA_IDX,
|
D_SYSDATA_IDX,
|
||||||
D_HIRESTEXTURES_IDX,
|
D_HIRESTEXTURES_IDX,
|
||||||
D_DUMP_IDX,
|
D_DUMP_IDX,
|
||||||
|
|
|
@ -18,11 +18,11 @@ set(SRCS
|
||||||
arm/skyeye_common/vfp/vfpinstr.cpp
|
arm/skyeye_common/vfp/vfpinstr.cpp
|
||||||
arm/skyeye_common/vfp/vfpsingle.cpp
|
arm/skyeye_common/vfp/vfpsingle.cpp
|
||||||
file_sys/archive_romfs.cpp
|
file_sys/archive_romfs.cpp
|
||||||
|
file_sys/archive_savedata.cpp
|
||||||
file_sys/archive_sdmc.cpp
|
file_sys/archive_sdmc.cpp
|
||||||
|
file_sys/disk_archive.cpp
|
||||||
file_sys/file_romfs.cpp
|
file_sys/file_romfs.cpp
|
||||||
file_sys/file_sdmc.cpp
|
|
||||||
file_sys/directory_romfs.cpp
|
file_sys/directory_romfs.cpp
|
||||||
file_sys/directory_sdmc.cpp
|
|
||||||
hle/kernel/address_arbiter.cpp
|
hle/kernel/address_arbiter.cpp
|
||||||
hle/kernel/event.cpp
|
hle/kernel/event.cpp
|
||||||
hle/kernel/kernel.cpp
|
hle/kernel/kernel.cpp
|
||||||
|
@ -99,13 +99,13 @@ set(HEADERS
|
||||||
arm/arm_interface.h
|
arm/arm_interface.h
|
||||||
file_sys/archive_backend.h
|
file_sys/archive_backend.h
|
||||||
file_sys/archive_romfs.h
|
file_sys/archive_romfs.h
|
||||||
|
file_sys/archive_savedata.h
|
||||||
file_sys/archive_sdmc.h
|
file_sys/archive_sdmc.h
|
||||||
|
file_sys/disk_archive.h
|
||||||
file_sys/file_backend.h
|
file_sys/file_backend.h
|
||||||
file_sys/file_romfs.h
|
file_sys/file_romfs.h
|
||||||
file_sys/file_sdmc.h
|
|
||||||
file_sys/directory_backend.h
|
file_sys/directory_backend.h
|
||||||
file_sys/directory_romfs.h
|
file_sys/directory_romfs.h
|
||||||
file_sys/directory_sdmc.h
|
|
||||||
hle/kernel/address_arbiter.h
|
hle/kernel/address_arbiter.h
|
||||||
hle/kernel/event.h
|
hle/kernel/event.h
|
||||||
hle/kernel/kernel.h
|
hle/kernel/kernel.h
|
||||||
|
|
33
src/core/file_sys/archive_savedata.cpp
Normal file
33
src/core/file_sys/archive_savedata.cpp
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
// 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_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
|
32
src/core/file_sys/archive_savedata.h
Normal file
32
src/core/file_sys/archive_savedata.h
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
// 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/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 CreateSaveDataResult AlreadyExists if the SaveData folder already exists,
|
||||||
|
* Success if it was created properly and Failure if there was any error
|
||||||
|
*/
|
||||||
|
bool Initialize();
|
||||||
|
|
||||||
|
std::string GetName() const override { return "SaveData"; }
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace FileSys
|
|
@ -8,8 +8,7 @@
|
||||||
#include "common/file_util.h"
|
#include "common/file_util.h"
|
||||||
|
|
||||||
#include "core/file_sys/archive_sdmc.h"
|
#include "core/file_sys/archive_sdmc.h"
|
||||||
#include "core/file_sys/directory_sdmc.h"
|
#include "core/file_sys/disk_archive.h"
|
||||||
#include "core/file_sys/file_sdmc.h"
|
|
||||||
#include "core/settings.h"
|
#include "core/settings.h"
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -17,18 +16,10 @@
|
||||||
|
|
||||||
namespace FileSys {
|
namespace FileSys {
|
||||||
|
|
||||||
Archive_SDMC::Archive_SDMC(const std::string& mount_point) {
|
Archive_SDMC::Archive_SDMC(const std::string& mount_point) : DiskArchive(mount_point) {
|
||||||
this->mount_point = mount_point;
|
|
||||||
LOG_INFO(Service_FS, "Directory %s set as SDMC.", mount_point.c_str());
|
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() {
|
bool Archive_SDMC::Initialize() {
|
||||||
if (!Settings::values.use_virtual_sd) {
|
if (!Settings::values.use_virtual_sd) {
|
||||||
LOG_WARNING(Service_FS, "SDMC disabled by config.");
|
LOG_WARNING(Service_FS, "SDMC disabled by config.");
|
||||||
|
@ -43,74 +34,4 @@ bool Archive_SDMC::Initialize() {
|
||||||
return true;
|
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<FileBackend> Archive_SDMC::OpenFile(const Path& path, const Mode mode) const {
|
|
||||||
LOG_DEBUG(Service_FS, "called path=%s mode=%u", path.DebugStr().c_str(), mode.hex);
|
|
||||||
File_SDMC* file = new File_SDMC(this, path, mode);
|
|
||||||
if (!file->Open())
|
|
||||||
return nullptr;
|
|
||||||
return std::unique_ptr<FileBackend>(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());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Archive_SDMC::RenameFile(const FileSys::Path& src_path, const FileSys::Path& dest_path) const {
|
|
||||||
return FileUtil::Rename(GetMountPoint() + src_path.AsString(), GetMountPoint() + dest_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());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Archive_SDMC::RenameDirectory(const FileSys::Path& src_path, const FileSys::Path& dest_path) const {
|
|
||||||
return FileUtil::Rename(GetMountPoint() + src_path.AsString(), GetMountPoint() + dest_path.AsString());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Open a directory specified by its path
|
|
||||||
* @param path Path relative to the archive
|
|
||||||
* @return Opened directory, or nullptr
|
|
||||||
*/
|
|
||||||
std::unique_ptr<DirectoryBackend> Archive_SDMC::OpenDirectory(const Path& path) const {
|
|
||||||
LOG_DEBUG(Service_FS, "called path=%s", path.DebugStr().c_str());
|
|
||||||
Directory_SDMC* directory = new Directory_SDMC(this, path);
|
|
||||||
if (!directory->Open())
|
|
||||||
return nullptr;
|
|
||||||
return std::unique_ptr<DirectoryBackend>(directory);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
} // namespace FileSys
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
|
||||||
#include "core/file_sys/archive_backend.h"
|
#include "core/file_sys/disk_archive.h"
|
||||||
#include "core/loader/loader.h"
|
#include "core/loader/loader.h"
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -15,10 +15,9 @@
|
||||||
namespace FileSys {
|
namespace FileSys {
|
||||||
|
|
||||||
/// File system interface to the SDMC archive
|
/// File system interface to the SDMC archive
|
||||||
class Archive_SDMC final : public ArchiveBackend {
|
class Archive_SDMC final : public DiskArchive {
|
||||||
public:
|
public:
|
||||||
Archive_SDMC(const std::string& mount_point);
|
Archive_SDMC(const std::string& mount_point);
|
||||||
~Archive_SDMC() override;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the archive.
|
* Initialize the archive.
|
||||||
|
@ -27,67 +26,6 @@ public:
|
||||||
bool Initialize();
|
bool Initialize();
|
||||||
|
|
||||||
std::string GetName() const override { return "SDMC"; }
|
std::string GetName() const override { return "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<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;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 RenameFile(const FileSys::Path& src_path, const FileSys::Path& dest_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;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
|
||||||
*/
|
|
||||||
bool RenameDirectory(const FileSys::Path& src_path, const FileSys::Path& dest_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<DirectoryBackend> OpenDirectory(const Path& path) const 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
|
} // namespace FileSys
|
||||||
|
|
|
@ -1,88 +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.
|
|
||||||
this->path = archive->GetMountPoint() + path.AsString();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Directory_SDMC::~Directory_SDMC() {
|
|
||||||
Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Directory_SDMC::Open() {
|
|
||||||
if (!FileUtil::IsDirectory(path))
|
|
||||||
return false;
|
|
||||||
FileUtil::ScanDirectoryTree(path, directory);
|
|
||||||
children_iterator = directory.children.begin();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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];
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Close the directory
|
|
||||||
* @return true if the directory closed correctly
|
|
||||||
*/
|
|
||||||
bool Directory_SDMC::Close() const {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace FileSys
|
|
|
@ -1,55 +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_backend.h"
|
|
||||||
#include "core/file_sys/archive_sdmc.h"
|
|
||||||
#include "core/loader/loader.h"
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// FileSys namespace
|
|
||||||
|
|
||||||
namespace FileSys {
|
|
||||||
|
|
||||||
class Directory_SDMC final : public DirectoryBackend {
|
|
||||||
public:
|
|
||||||
Directory_SDMC();
|
|
||||||
Directory_SDMC(const Archive_SDMC* archive, const Path& path);
|
|
||||||
~Directory_SDMC() 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
|
|
||||||
* @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:
|
|
||||||
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
|
|
167
src/core/file_sys/disk_archive.cpp
Normal file
167
src/core/file_sys/disk_archive.cpp
Normal file
|
@ -0,0 +1,167 @@
|
||||||
|
// 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/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 FileSys::Path& path) const {
|
||||||
|
return FileUtil::Delete(GetMountPoint() + path.AsString());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DiskArchive::RenameFile(const FileSys::Path& src_path, const FileSys::Path& dest_path) const {
|
||||||
|
return FileUtil::Rename(GetMountPoint() + src_path.AsString(), GetMountPoint() + dest_path.AsString());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DiskArchive::DeleteDirectory(const FileSys::Path& path) const {
|
||||||
|
return FileUtil::DeleteDir(GetMountPoint() + path.AsString());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DiskArchive::CreateDirectory(const Path& path) const {
|
||||||
|
return FileUtil::CreateDir(GetMountPoint() + path.AsString());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DiskArchive::RenameDirectory(const FileSys::Path& src_path, const FileSys::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
|
101
src/core/file_sys/disk_archive.h
Normal file
101
src/core/file_sys/disk_archive.h
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
// 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_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 FileSys::Path& path) const override;
|
||||||
|
bool RenameFile(const FileSys::Path& src_path, const FileSys::Path& dest_path) const override;
|
||||||
|
bool DeleteDirectory(const FileSys::Path& path) const override;
|
||||||
|
bool CreateDirectory(const Path& path) const override;
|
||||||
|
bool RenameDirectory(const FileSys::Path& src_path, const FileSys::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
|
|
@ -61,6 +61,11 @@ public:
|
||||||
* @return true if the file closed correctly
|
* @return true if the file closed correctly
|
||||||
*/
|
*/
|
||||||
virtual bool Close() const = 0;
|
virtual bool Close() const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flushes the file
|
||||||
|
*/
|
||||||
|
virtual void Flush() const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace FileSys
|
} // namespace FileSys
|
||||||
|
|
|
@ -64,6 +64,8 @@ public:
|
||||||
*/
|
*/
|
||||||
bool Close() const override;
|
bool Close() const override;
|
||||||
|
|
||||||
|
void Flush() const override { }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const Archive_RomFS* archive;
|
const Archive_RomFS* archive;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,110 +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)) {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
|
|
@ -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_backend.h"
|
|
||||||
#include "core/file_sys/archive_sdmc.h"
|
|
||||||
#include "core/loader/loader.h"
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// FileSys namespace
|
|
||||||
|
|
||||||
namespace FileSys {
|
|
||||||
|
|
||||||
class File_SDMC final : public FileBackend {
|
|
||||||
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
|
|
|
@ -14,6 +14,7 @@ namespace Kernel {
|
||||||
|
|
||||||
Handle g_main_thread = 0;
|
Handle g_main_thread = 0;
|
||||||
ObjectPool g_object_pool;
|
ObjectPool g_object_pool;
|
||||||
|
u64 g_program_id = 0;
|
||||||
|
|
||||||
ObjectPool::ObjectPool() {
|
ObjectPool::ObjectPool() {
|
||||||
next_id = INITIAL_NEXT_ID;
|
next_id = INITIAL_NEXT_ID;
|
||||||
|
|
|
@ -151,6 +151,12 @@ private:
|
||||||
extern ObjectPool g_object_pool;
|
extern ObjectPool g_object_pool;
|
||||||
extern Handle g_main_thread;
|
extern Handle g_main_thread;
|
||||||
|
|
||||||
|
/// The ID code of the currently running game
|
||||||
|
/// TODO(Subv): This variable should not be here,
|
||||||
|
/// we need a way to store information about the currently loaded application
|
||||||
|
/// for later query during runtime, maybe using the LDR service?
|
||||||
|
extern u64 g_program_id;
|
||||||
|
|
||||||
/// Initialize the kernel
|
/// Initialize the kernel
|
||||||
void Init();
|
void Init();
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
/// Detailed description of the error. This listing is likely incomplete.
|
/// Detailed description of the error. This listing is likely incomplete.
|
||||||
enum class ErrorDescription : u32 {
|
enum class ErrorDescription : u32 {
|
||||||
Success = 0,
|
Success = 0,
|
||||||
|
FS_NotFound = 100,
|
||||||
|
FS_NotFormatted = 340, ///< This is used by the FS service when creating a SaveData archive
|
||||||
InvalidSection = 1000,
|
InvalidSection = 1000,
|
||||||
TooLarge = 1001,
|
TooLarge = 1001,
|
||||||
NotAuthorized = 1002,
|
NotAuthorized = 1002,
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include "common/file_util.h"
|
#include "common/file_util.h"
|
||||||
#include "common/math_util.h"
|
#include "common/math_util.h"
|
||||||
|
|
||||||
|
#include "core/file_sys/archive_savedata.h"
|
||||||
#include "core/file_sys/archive_backend.h"
|
#include "core/file_sys/archive_backend.h"
|
||||||
#include "core/file_sys/archive_sdmc.h"
|
#include "core/file_sys/archive_sdmc.h"
|
||||||
#include "core/file_sys/directory_backend.h"
|
#include "core/file_sys/directory_backend.h"
|
||||||
|
@ -135,6 +136,13 @@ public:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case FileCommand::Flush:
|
||||||
|
{
|
||||||
|
LOG_TRACE(Service_FS, "Flush");
|
||||||
|
backend->Flush();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// Unknown command...
|
// Unknown command...
|
||||||
default:
|
default:
|
||||||
LOG_ERROR(Service_FS, "Unknown command=0x%08X!", cmd);
|
LOG_ERROR(Service_FS, "Unknown command=0x%08X!", cmd);
|
||||||
|
@ -220,6 +228,15 @@ ResultVal<ArchiveHandle> OpenArchive(ArchiveIdCode id_code) {
|
||||||
|
|
||||||
auto itr = id_code_map.find(id_code);
|
auto itr = id_code_map.find(id_code);
|
||||||
if (itr == id_code_map.end()) {
|
if (itr == id_code_map.end()) {
|
||||||
|
if (id_code == ArchiveIdCode::SaveData) {
|
||||||
|
// When a SaveData archive is created for the first time, it is not yet formatted
|
||||||
|
// and the save file/directory structure expected by the game has not yet been initialized.
|
||||||
|
// Returning the NotFormatted error code will signal the game to provision the SaveData archive
|
||||||
|
// with the files and folders that it expects.
|
||||||
|
// The FormatSaveData service call will create the SaveData archive when it is called.
|
||||||
|
return ResultCode(ErrorDescription::FS_NotFormatted, ErrorModule::FS,
|
||||||
|
ErrorSummary::InvalidState, ErrorLevel::Status);
|
||||||
|
}
|
||||||
// TODO: Verify error against hardware
|
// TODO: Verify error against hardware
|
||||||
return ResultCode(ErrorDescription::NotFound, ErrorModule::FS,
|
return ResultCode(ErrorDescription::NotFound, ErrorModule::FS,
|
||||||
ErrorSummary::NotFound, ErrorLevel::Permanent);
|
ErrorSummary::NotFound, ErrorLevel::Permanent);
|
||||||
|
@ -260,8 +277,8 @@ ResultVal<Handle> OpenFileFromArchive(ArchiveHandle archive_handle, const FileSy
|
||||||
|
|
||||||
std::unique_ptr<FileSys::FileBackend> backend = archive->backend->OpenFile(path, mode);
|
std::unique_ptr<FileSys::FileBackend> backend = archive->backend->OpenFile(path, mode);
|
||||||
if (backend == nullptr) {
|
if (backend == nullptr) {
|
||||||
return ResultCode(ErrorDescription::NotFound, ErrorModule::FS,
|
return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS,
|
||||||
ErrorSummary::NotFound, ErrorLevel::Permanent);
|
ErrorSummary::NotFound, ErrorLevel::Status);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto file = std::make_unique<File>(std::move(backend), path);
|
auto file = std::make_unique<File>(std::move(backend), path);
|
||||||
|
@ -366,6 +383,28 @@ ResultVal<Handle> OpenDirectoryFromArchive(ArchiveHandle archive_handle, const F
|
||||||
return MakeResult<Handle>(handle);
|
return MakeResult<Handle>(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ResultCode FormatSaveData() {
|
||||||
|
// TODO(Subv): Actually wipe the savedata folder after creating or opening it
|
||||||
|
|
||||||
|
// Do not create the archive again if it already exists
|
||||||
|
if (id_code_map.find(ArchiveIdCode::SaveData) != id_code_map.end())
|
||||||
|
return UnimplementedFunction(ErrorModule::FS); // TODO(Subv): Find the correct error code
|
||||||
|
|
||||||
|
// Create the SaveData archive
|
||||||
|
std::string savedata_directory = FileUtil::GetUserPath(D_SAVEDATA_IDX);
|
||||||
|
auto savedata_archive = std::make_unique<FileSys::Archive_SaveData>(savedata_directory,
|
||||||
|
Kernel::g_program_id);
|
||||||
|
|
||||||
|
if (savedata_archive->Initialize()) {
|
||||||
|
CreateArchive(std::move(savedata_archive), ArchiveIdCode::SaveData);
|
||||||
|
return RESULT_SUCCESS;
|
||||||
|
} else {
|
||||||
|
LOG_ERROR(Service_FS, "Can't instantiate SaveData archive with path %s",
|
||||||
|
savedata_archive->GetMountPoint().c_str());
|
||||||
|
return UnimplementedFunction(ErrorModule::FS); // TODO(Subv): Find the proper error code
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Initialize archives
|
/// Initialize archives
|
||||||
void ArchiveInit() {
|
void ArchiveInit() {
|
||||||
next_handle = 1;
|
next_handle = 1;
|
||||||
|
@ -375,9 +414,9 @@ void ArchiveInit() {
|
||||||
// archive type is SDMC, so it is the only one getting exposed.
|
// archive type is SDMC, so it is the only one getting exposed.
|
||||||
|
|
||||||
std::string sdmc_directory = FileUtil::GetUserPath(D_SDMC_IDX);
|
std::string sdmc_directory = FileUtil::GetUserPath(D_SDMC_IDX);
|
||||||
auto archive = std::make_unique<FileSys::Archive_SDMC>(sdmc_directory);
|
auto sdmc_archive = std::make_unique<FileSys::Archive_SDMC>(sdmc_directory);
|
||||||
if (archive->Initialize())
|
if (sdmc_archive->Initialize())
|
||||||
CreateArchive(std::move(archive), ArchiveIdCode::SDMC);
|
CreateArchive(std::move(sdmc_archive), ArchiveIdCode::SDMC);
|
||||||
else
|
else
|
||||||
LOG_ERROR(Service_FS, "Can't instantiate SDMC archive with path %s", sdmc_directory.c_str());
|
LOG_ERROR(Service_FS, "Can't instantiate SDMC archive with path %s", sdmc_directory.c_str());
|
||||||
}
|
}
|
||||||
|
|
|
@ -109,6 +109,12 @@ ResultCode RenameDirectoryBetweenArchives(ArchiveHandle src_archive_handle, cons
|
||||||
*/
|
*/
|
||||||
ResultVal<Handle> OpenDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path);
|
ResultVal<Handle> OpenDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a blank SaveData archive.
|
||||||
|
* @return ResultCode 0 on success or the corresponding code on error
|
||||||
|
*/
|
||||||
|
ResultCode FormatSaveData();
|
||||||
|
|
||||||
/// Initialize archives
|
/// Initialize archives
|
||||||
void ArchiveInit();
|
void ArchiveInit();
|
||||||
|
|
||||||
|
|
|
@ -3,11 +3,11 @@
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#include "common/common.h"
|
#include "common/common.h"
|
||||||
|
#include "common/file_util.h"
|
||||||
#include "common/scope_exit.h"
|
#include "common/scope_exit.h"
|
||||||
|
|
||||||
#include "common/string_util.h"
|
#include "common/string_util.h"
|
||||||
#include "core/hle/service/fs/archive.h"
|
|
||||||
#include "core/hle/result.h"
|
#include "core/hle/result.h"
|
||||||
|
#include "core/hle/service/fs/archive.h"
|
||||||
#include "core/hle/service/fs/fs_user.h"
|
#include "core/hle/service/fs/fs_user.h"
|
||||||
#include "core/settings.h"
|
#include "core/settings.h"
|
||||||
|
|
||||||
|
@ -50,9 +50,7 @@ static void Initialize(Service::Interface* self) {
|
||||||
static void OpenFile(Service::Interface* self) {
|
static void OpenFile(Service::Interface* self) {
|
||||||
u32* cmd_buff = Kernel::GetCommandBuffer();
|
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||||
|
|
||||||
// TODO(Link Mauve): cmd_buff[2], aka archive handle lower word, isn't used according to
|
ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]);
|
||||||
// 3dmoo's or ctrulib's implementations. Triple check if it's really the case.
|
|
||||||
Handle archive_handle = static_cast<Handle>(cmd_buff[3]);
|
|
||||||
auto filename_type = static_cast<FileSys::LowPathType>(cmd_buff[4]);
|
auto filename_type = static_cast<FileSys::LowPathType>(cmd_buff[4]);
|
||||||
u32 filename_size = cmd_buff[5];
|
u32 filename_size = cmd_buff[5];
|
||||||
FileSys::Mode mode; mode.hex = cmd_buff[6];
|
FileSys::Mode mode; mode.hex = cmd_buff[6];
|
||||||
|
@ -398,6 +396,36 @@ static void IsSdmcDetected(Service::Interface* self) {
|
||||||
LOG_DEBUG(Service_FS, "called");
|
LOG_DEBUG(Service_FS, "called");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FS_User::FormatSaveData service function
|
||||||
|
* Inputs:
|
||||||
|
* Outputs:
|
||||||
|
* 1 : Result of function, 0 on success, otherwise error code
|
||||||
|
*/
|
||||||
|
static void FormatSaveData(Service::Interface* self) {
|
||||||
|
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||||
|
LOG_DEBUG(Service_FS, "(STUBBED)");
|
||||||
|
|
||||||
|
// TODO(Subv): Find out what the inputs and outputs of this function are
|
||||||
|
|
||||||
|
cmd_buff[1] = FormatSaveData().raw;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FS_User::FormatThisUserSaveData service function
|
||||||
|
* Inputs:
|
||||||
|
* Outputs:
|
||||||
|
* 1 : Result of function, 0 on success, otherwise error code
|
||||||
|
*/
|
||||||
|
static void FormatThisUserSaveData(Service::Interface* self) {
|
||||||
|
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||||
|
LOG_DEBUG(Service_FS, "(STUBBED)");
|
||||||
|
|
||||||
|
// TODO(Subv): Find out what the inputs and outputs of this function are
|
||||||
|
|
||||||
|
cmd_buff[1] = FormatSaveData().raw;
|
||||||
|
}
|
||||||
|
|
||||||
const FSUserInterface::FunctionInfo FunctionTable[] = {
|
const FSUserInterface::FunctionInfo FunctionTable[] = {
|
||||||
{0x000100C6, nullptr, "Dummy1"},
|
{0x000100C6, nullptr, "Dummy1"},
|
||||||
{0x040100C4, nullptr, "Control"},
|
{0x040100C4, nullptr, "Control"},
|
||||||
|
@ -415,7 +443,7 @@ const FSUserInterface::FunctionInfo FunctionTable[] = {
|
||||||
{0x080C00C2, OpenArchive, "OpenArchive"},
|
{0x080C00C2, OpenArchive, "OpenArchive"},
|
||||||
{0x080D0144, nullptr, "ControlArchive"},
|
{0x080D0144, nullptr, "ControlArchive"},
|
||||||
{0x080E0080, CloseArchive, "CloseArchive"},
|
{0x080E0080, CloseArchive, "CloseArchive"},
|
||||||
{0x080F0180, nullptr, "FormatThisUserSaveData"},
|
{0x080F0180, FormatThisUserSaveData,"FormatThisUserSaveData"},
|
||||||
{0x08100200, nullptr, "CreateSystemSaveData"},
|
{0x08100200, nullptr, "CreateSystemSaveData"},
|
||||||
{0x08110040, nullptr, "DeleteSystemSaveData"},
|
{0x08110040, nullptr, "DeleteSystemSaveData"},
|
||||||
{0x08120080, nullptr, "GetFreeBytes"},
|
{0x08120080, nullptr, "GetFreeBytes"},
|
||||||
|
@ -476,7 +504,7 @@ const FSUserInterface::FunctionInfo FunctionTable[] = {
|
||||||
{0x08490040, nullptr, "GetArchiveResource"},
|
{0x08490040, nullptr, "GetArchiveResource"},
|
||||||
{0x084A0002, nullptr, "ExportIntegrityVerificationSeed"},
|
{0x084A0002, nullptr, "ExportIntegrityVerificationSeed"},
|
||||||
{0x084B0002, nullptr, "ImportIntegrityVerificationSeed"},
|
{0x084B0002, nullptr, "ImportIntegrityVerificationSeed"},
|
||||||
{0x084C0242, nullptr, "FormatSaveData"},
|
{0x084C0242, FormatSaveData, "FormatSaveData"},
|
||||||
{0x084D0102, nullptr, "GetLegacySubBannerData"},
|
{0x084D0102, nullptr, "GetLegacySubBannerData"},
|
||||||
{0x084E0342, nullptr, "UpdateSha256Context"},
|
{0x084E0342, nullptr, "UpdateSha256Context"},
|
||||||
{0x084F0102, nullptr, "ReadSpecialFile"},
|
{0x084F0102, nullptr, "ReadSpecialFile"},
|
||||||
|
|
|
@ -74,6 +74,7 @@ ResultStatus LoadFile(const std::string& filename) {
|
||||||
|
|
||||||
// Load application and RomFS
|
// Load application and RomFS
|
||||||
if (ResultStatus::Success == app_loader.Load()) {
|
if (ResultStatus::Success == app_loader.Load()) {
|
||||||
|
Kernel::g_program_id = app_loader.GetProgramId();
|
||||||
Service::FS::CreateArchive(std::make_unique<FileSys::Archive_RomFS>(app_loader), Service::FS::ArchiveIdCode::RomFS);
|
Service::FS::CreateArchive(std::make_unique<FileSys::Archive_RomFS>(app_loader), Service::FS::ArchiveIdCode::RomFS);
|
||||||
return ResultStatus::Success;
|
return ResultStatus::Success;
|
||||||
}
|
}
|
||||||
|
|
|
@ -315,4 +315,8 @@ ResultStatus AppLoader_NCCH::ReadRomFS(std::vector<u8>& buffer) const {
|
||||||
return ResultStatus::Error;
|
return ResultStatus::Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u64 AppLoader_NCCH::GetProgramId() const {
|
||||||
|
return *reinterpret_cast<u64 const*>(&ncch_header.program_id[0]);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Loader
|
} // namespace Loader
|
||||||
|
|
|
@ -191,6 +191,12 @@ public:
|
||||||
*/
|
*/
|
||||||
ResultStatus ReadRomFS(std::vector<u8>& buffer) const override;
|
ResultStatus ReadRomFS(std::vector<u8>& buffer) const override;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Gets the program id from the NCCH header
|
||||||
|
* @return u64 Program id
|
||||||
|
*/
|
||||||
|
u64 GetProgramId() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in a new issue