summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Subv2014-12-16 00:33:41 -0500
committerGravatar Subv2014-12-17 19:21:38 -0500
commitea9ce0fba776eef8f9e4f3a86e71256091732a14 (patch)
tree9e698ba00993a4aff1df4d60f9fcf3cf135ee76c
parentMerge pull request #293 from lioncash/sops (diff)
downloadyuzu-ea9ce0fba776eef8f9e4f3a86e71256091732a14.tar.gz
yuzu-ea9ce0fba776eef8f9e4f3a86e71256091732a14.tar.xz
yuzu-ea9ce0fba776eef8f9e4f3a86e71256091732a14.zip
Filesystem/Archives: Implemented the SaveData archive
The savedata for each game is stored in /savedata/<ProgramID> for NCCH files. ELF files and 3DSX files use the folder 0 because they have no ID information Got rid of the code duplication in File and Directory Files that deal with the host machine's file system now live in DiskFile, similarly for directories and DiskDirectory and archives with DiskArchive. FS_U: Use the correct error code when a file wasn't found
Diffstat (limited to '')
-rw-r--r--src/common/common_paths.h1
-rw-r--r--src/common/file_util.cpp2
-rw-r--r--src/common/file_util.h1
-rw-r--r--src/core/CMakeLists.txt8
-rw-r--r--src/core/file_sys/archive_savedata.cpp33
-rw-r--r--src/core/file_sys/archive_savedata.h32
-rw-r--r--src/core/file_sys/archive_sdmc.cpp83
-rw-r--r--src/core/file_sys/archive_sdmc.h66
-rw-r--r--src/core/file_sys/directory_sdmc.cpp88
-rw-r--r--src/core/file_sys/directory_sdmc.h55
-rw-r--r--src/core/file_sys/disk_archive.cpp167
-rw-r--r--src/core/file_sys/disk_archive.h101
-rw-r--r--src/core/file_sys/file_backend.h5
-rw-r--r--src/core/file_sys/file_romfs.h2
-rw-r--r--src/core/file_sys/file_sdmc.cpp110
-rw-r--r--src/core/file_sys/file_sdmc.h75
-rw-r--r--src/core/hle/kernel/kernel.cpp1
-rw-r--r--src/core/hle/kernel/kernel.h6
-rw-r--r--src/core/hle/result.h2
-rw-r--r--src/core/hle/service/fs/archive.cpp51
-rw-r--r--src/core/hle/service/fs/archive.h6
-rw-r--r--src/core/hle/service/fs/fs_user.cpp42
-rw-r--r--src/core/loader/loader.cpp1
-rw-r--r--src/core/loader/ncch.cpp4
-rw-r--r--src/core/loader/ncch.h6
25 files changed, 458 insertions, 490 deletions
diff --git a/src/common/common_paths.h b/src/common/common_paths.h
index 42e1a29c1..a86889756 100644
--- a/src/common/common_paths.h
+++ b/src/common/common_paths.h
@@ -40,6 +40,7 @@
40#define MAPS_DIR "maps" 40#define MAPS_DIR "maps"
41#define CACHE_DIR "cache" 41#define CACHE_DIR "cache"
42#define SDMC_DIR "sdmc" 42#define SDMC_DIR "sdmc"
43#define SAVEDATA_DIR "savedata"
43#define SYSDATA_DIR "sysdata" 44#define SYSDATA_DIR "sysdata"
44#define SHADERCACHE_DIR "shader_cache" 45#define SHADERCACHE_DIR "shader_cache"
45#define STATESAVES_DIR "state_saves" 46#define STATESAVES_DIR "state_saves"
diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp
index 88c46c117..42cdf3262 100644
--- a/src/common/file_util.cpp
+++ b/src/common/file_util.cpp
@@ -676,6 +676,7 @@ const std::string& GetUserPath(const unsigned int DirIDX, const std::string &new
676 paths[D_MAPS_IDX] = paths[D_USER_IDX] + MAPS_DIR DIR_SEP; 676 paths[D_MAPS_IDX] = paths[D_USER_IDX] + MAPS_DIR DIR_SEP;
677 paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP; 677 paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP;
678 paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP; 678 paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP;
679 paths[D_SAVEDATA_IDX] = paths[D_USER_IDX] + SAVEDATA_DIR DIR_SEP;
679 paths[D_SYSDATA_IDX] = paths[D_USER_IDX] + SYSDATA_DIR DIR_SEP; 680 paths[D_SYSDATA_IDX] = paths[D_USER_IDX] + SYSDATA_DIR DIR_SEP;
680 paths[D_SHADERCACHE_IDX] = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP; 681 paths[D_SHADERCACHE_IDX] = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP;
681 paths[D_SHADERS_IDX] = paths[D_USER_IDX] + SHADERS_DIR DIR_SEP; 682 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
718 paths[D_MAPS_IDX] = paths[D_USER_IDX] + MAPS_DIR DIR_SEP; 719 paths[D_MAPS_IDX] = paths[D_USER_IDX] + MAPS_DIR DIR_SEP;
719 paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP; 720 paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP;
720 paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP; 721 paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP;
722 paths[D_SAVEDATA_IDX] = paths[D_USER_IDX] + SAVEDATA_DIR DIR_SEP;
721 paths[D_SHADERCACHE_IDX] = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP; 723 paths[D_SHADERCACHE_IDX] = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP;
722 paths[D_SHADERS_IDX] = paths[D_USER_IDX] + SHADERS_DIR DIR_SEP; 724 paths[D_SHADERS_IDX] = paths[D_USER_IDX] + SHADERS_DIR DIR_SEP;
723 paths[D_STATESAVES_IDX] = paths[D_USER_IDX] + STATESAVES_DIR DIR_SEP; 725 paths[D_STATESAVES_IDX] = paths[D_USER_IDX] + STATESAVES_DIR DIR_SEP;
diff --git a/src/common/file_util.h b/src/common/file_util.h
index a9d48cfe8..e691b6139 100644
--- a/src/common/file_util.h
+++ b/src/common/file_util.h
@@ -27,6 +27,7 @@ enum {
27 D_STATESAVES_IDX, 27 D_STATESAVES_IDX,
28 D_SCREENSHOTS_IDX, 28 D_SCREENSHOTS_IDX,
29 D_SDMC_IDX, 29 D_SDMC_IDX,
30 D_SAVEDATA_IDX,
30 D_SYSDATA_IDX, 31 D_SYSDATA_IDX,
31 D_HIRESTEXTURES_IDX, 32 D_HIRESTEXTURES_IDX,
32 D_DUMP_IDX, 33 D_DUMP_IDX,
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 198e4afd3..f71232c1a 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -18,11 +18,11 @@ set(SRCS
18 arm/skyeye_common/vfp/vfpinstr.cpp 18 arm/skyeye_common/vfp/vfpinstr.cpp
19 arm/skyeye_common/vfp/vfpsingle.cpp 19 arm/skyeye_common/vfp/vfpsingle.cpp
20 file_sys/archive_romfs.cpp 20 file_sys/archive_romfs.cpp
21 file_sys/archive_savedata.cpp
21 file_sys/archive_sdmc.cpp 22 file_sys/archive_sdmc.cpp
23 file_sys/disk_archive.cpp
22 file_sys/file_romfs.cpp 24 file_sys/file_romfs.cpp
23 file_sys/file_sdmc.cpp
24 file_sys/directory_romfs.cpp 25 file_sys/directory_romfs.cpp
25 file_sys/directory_sdmc.cpp
26 hle/kernel/address_arbiter.cpp 26 hle/kernel/address_arbiter.cpp
27 hle/kernel/event.cpp 27 hle/kernel/event.cpp
28 hle/kernel/kernel.cpp 28 hle/kernel/kernel.cpp
@@ -99,13 +99,13 @@ set(HEADERS
99 arm/arm_interface.h 99 arm/arm_interface.h
100 file_sys/archive_backend.h 100 file_sys/archive_backend.h
101 file_sys/archive_romfs.h 101 file_sys/archive_romfs.h
102 file_sys/archive_savedata.h
102 file_sys/archive_sdmc.h 103 file_sys/archive_sdmc.h
104 file_sys/disk_archive.h
103 file_sys/file_backend.h 105 file_sys/file_backend.h
104 file_sys/file_romfs.h 106 file_sys/file_romfs.h
105 file_sys/file_sdmc.h
106 file_sys/directory_backend.h 107 file_sys/directory_backend.h
107 file_sys/directory_romfs.h 108 file_sys/directory_romfs.h
108 file_sys/directory_sdmc.h
109 hle/kernel/address_arbiter.h 109 hle/kernel/address_arbiter.h
110 hle/kernel/event.h 110 hle/kernel/event.h
111 hle/kernel/kernel.h 111 hle/kernel/kernel.h
diff --git a/src/core/file_sys/archive_savedata.cpp b/src/core/file_sys/archive_savedata.cpp
new file mode 100644
index 000000000..2414564e4
--- /dev/null
+++ b/src/core/file_sys/archive_savedata.cpp
@@ -0,0 +1,33 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2+
3// Refer to the license.txt file included.
4
5#include <sys/stat.h>
6
7#include "common/common_types.h"
8#include "common/file_util.h"
9
10#include "core/file_sys/archive_savedata.h"
11#include "core/file_sys/disk_archive.h"
12#include "core/settings.h"
13
14////////////////////////////////////////////////////////////////////////////////////////////////////
15// FileSys namespace
16
17namespace FileSys {
18
19Archive_SaveData::Archive_SaveData(const std::string& mount_point, u64 program_id)
20 : DiskArchive(mount_point + Common::StringFromFormat("%016X", program_id) + DIR_SEP) {
21 LOG_INFO(Service_FS, "Directory %s set as SaveData.", this->mount_point.c_str());
22}
23
24bool Archive_SaveData::Initialize() {
25 if (!FileUtil::CreateFullPath(mount_point)) {
26 LOG_ERROR(Service_FS, "Unable to create SaveData path.");
27 return false;
28 }
29
30 return true;
31}
32
33} // 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..b3e561130
--- /dev/null
+++ b/src/core/file_sys/archive_savedata.h
@@ -0,0 +1,32 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2+
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8
9#include "core/file_sys/disk_archive.h"
10#include "core/loader/loader.h"
11
12////////////////////////////////////////////////////////////////////////////////////////////////////
13// FileSys namespace
14
15namespace FileSys {
16
17/// File system interface to the SaveData archive
18class Archive_SaveData final : public DiskArchive {
19public:
20 Archive_SaveData(const std::string& mount_point, u64 program_id);
21
22 /**
23 * Initialize the archive.
24 * @return CreateSaveDataResult AlreadyExists if the SaveData folder already exists,
25 * Success if it was created properly and Failure if there was any error
26 */
27 bool Initialize();
28
29 std::string GetName() const override { return "SaveData"; }
30};
31
32} // namespace FileSys
diff --git a/src/core/file_sys/archive_sdmc.cpp b/src/core/file_sys/archive_sdmc.cpp
index 9d58668e0..dccdf7f67 100644
--- a/src/core/file_sys/archive_sdmc.cpp
+++ b/src/core/file_sys/archive_sdmc.cpp
@@ -8,8 +8,7 @@
8#include "common/file_util.h" 8#include "common/file_util.h"
9 9
10#include "core/file_sys/archive_sdmc.h" 10#include "core/file_sys/archive_sdmc.h"
11#include "core/file_sys/directory_sdmc.h" 11#include "core/file_sys/disk_archive.h"
12#include "core/file_sys/file_sdmc.h"
13#include "core/settings.h" 12#include "core/settings.h"
14 13
15//////////////////////////////////////////////////////////////////////////////////////////////////// 14////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -17,18 +16,10 @@
17 16
18namespace FileSys { 17namespace FileSys {
19 18
20Archive_SDMC::Archive_SDMC(const std::string& mount_point) { 19Archive_SDMC::Archive_SDMC(const std::string& mount_point) : DiskArchive(mount_point) {
21 this->mount_point = mount_point;
22 LOG_INFO(Service_FS, "Directory %s set as SDMC.", mount_point.c_str()); 20 LOG_INFO(Service_FS, "Directory %s set as SDMC.", mount_point.c_str());
23} 21}
24 22
25Archive_SDMC::~Archive_SDMC() {
26}
27
28/**
29 * Initialize the archive.
30 * @return true if it initialized successfully
31 */
32bool Archive_SDMC::Initialize() { 23bool Archive_SDMC::Initialize() {
33 if (!Settings::values.use_virtual_sd) { 24 if (!Settings::values.use_virtual_sd) {
34 LOG_WARNING(Service_FS, "SDMC disabled by config."); 25 LOG_WARNING(Service_FS, "SDMC disabled by config.");
@@ -43,74 +34,4 @@ bool Archive_SDMC::Initialize() {
43 return true; 34 return true;
44} 35}
45 36
46/**
47 * Open a file specified by its path, using the specified mode
48 * @param path Path relative to the archive
49 * @param mode Mode to open the file with
50 * @return Opened file, or nullptr
51 */
52std::unique_ptr<FileBackend> Archive_SDMC::OpenFile(const Path& path, const Mode mode) const {
53 LOG_DEBUG(Service_FS, "called path=%s mode=%u", path.DebugStr().c_str(), mode.hex);
54 File_SDMC* file = new File_SDMC(this, path, mode);
55 if (!file->Open())
56 return nullptr;
57 return std::unique_ptr<FileBackend>(file);
58}
59
60/**
61 * Delete a file specified by its path
62 * @param path Path relative to the archive
63 * @return Whether the file could be deleted
64 */
65bool Archive_SDMC::DeleteFile(const FileSys::Path& path) const {
66 return FileUtil::Delete(GetMountPoint() + path.AsString());
67}
68
69bool Archive_SDMC::RenameFile(const FileSys::Path& src_path, const FileSys::Path& dest_path) const {
70 return FileUtil::Rename(GetMountPoint() + src_path.AsString(), GetMountPoint() + dest_path.AsString());
71}
72
73/**
74 * Delete a directory specified by its path
75 * @param path Path relative to the archive
76 * @return Whether the directory could be deleted
77 */
78bool Archive_SDMC::DeleteDirectory(const FileSys::Path& path) const {
79 return FileUtil::DeleteDir(GetMountPoint() + path.AsString());
80}
81
82/**
83 * Create a directory specified by its path
84 * @param path Path relative to the archive
85 * @return Whether the directory could be created
86 */
87bool Archive_SDMC::CreateDirectory(const Path& path) const {
88 return FileUtil::CreateDir(GetMountPoint() + path.AsString());
89}
90
91bool Archive_SDMC::RenameDirectory(const FileSys::Path& src_path, const FileSys::Path& dest_path) const {
92 return FileUtil::Rename(GetMountPoint() + src_path.AsString(), GetMountPoint() + dest_path.AsString());
93}
94
95/**
96 * Open a directory specified by its path
97 * @param path Path relative to the archive
98 * @return Opened directory, or nullptr
99 */
100std::unique_ptr<DirectoryBackend> Archive_SDMC::OpenDirectory(const Path& path) const {
101 LOG_DEBUG(Service_FS, "called path=%s", path.DebugStr().c_str());
102 Directory_SDMC* directory = new Directory_SDMC(this, path);
103 if (!directory->Open())
104 return nullptr;
105 return std::unique_ptr<DirectoryBackend>(directory);
106}
107
108/**
109 * Getter for the path used for this Archive
110 * @return Mount point of that passthrough archive
111 */
112std::string Archive_SDMC::GetMountPoint() const {
113 return mount_point;
114}
115
116} // namespace FileSys 37} // namespace FileSys
diff --git a/src/core/file_sys/archive_sdmc.h b/src/core/file_sys/archive_sdmc.h
index 059045245..c84c6948e 100644
--- a/src/core/file_sys/archive_sdmc.h
+++ b/src/core/file_sys/archive_sdmc.h
@@ -6,7 +6,7 @@
6 6
7#include "common/common_types.h" 7#include "common/common_types.h"
8 8
9#include "core/file_sys/archive_backend.h" 9#include "core/file_sys/disk_archive.h"
10#include "core/loader/loader.h" 10#include "core/loader/loader.h"
11 11
12//////////////////////////////////////////////////////////////////////////////////////////////////// 12////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -15,10 +15,9 @@
15namespace FileSys { 15namespace FileSys {
16 16
17/// File system interface to the SDMC archive 17/// File system interface to the SDMC archive
18class Archive_SDMC final : public ArchiveBackend { 18class Archive_SDMC final : public DiskArchive {
19public: 19public:
20 Archive_SDMC(const std::string& mount_point); 20 Archive_SDMC(const std::string& mount_point);
21 ~Archive_SDMC() override;
22 21
23 /** 22 /**
24 * Initialize the archive. 23 * Initialize the archive.
@@ -27,67 +26,6 @@ public:
27 bool Initialize(); 26 bool Initialize();
28 27
29 std::string GetName() const override { return "SDMC"; } 28 std::string GetName() const override { return "SDMC"; }
30
31 /**
32 * Open a file specified by its path, using the specified mode
33 * @param path Path relative to the archive
34 * @param mode Mode to open the file with
35 * @return Opened file, or nullptr
36 */
37 std::unique_ptr<FileBackend> OpenFile(const Path& path, const Mode mode) const override;
38
39 /**
40 * Delete a file specified by its path
41 * @param path Path relative to the archive
42 * @return Whether the file could be deleted
43 */
44 bool DeleteFile(const FileSys::Path& path) const override;
45
46 /**
47 * Rename a File specified by its path
48 * @param src_path Source path relative to the archive
49 * @param dest_path Destination path relative to the archive
50 * @return Whether rename succeeded
51 */
52 bool RenameFile(const FileSys::Path& src_path, const FileSys::Path& dest_path) const override;
53
54 /**
55 * Delete a directory specified by its path
56 * @param path Path relative to the archive
57 * @return Whether the directory could be deleted
58 */
59 bool DeleteDirectory(const FileSys::Path& path) const override;
60
61 /**
62 * Create a directory specified by its path
63 * @param path Path relative to the archive
64 * @return Whether the directory could be created
65 */
66 bool CreateDirectory(const Path& path) const override;
67
68 /**
69 * Rename a Directory specified by its path
70 * @param src_path Source path relative to the archive
71 * @param dest_path Destination path relative to the archive
72 * @return Whether rename succeeded
73 */
74 bool RenameDirectory(const FileSys::Path& src_path, const FileSys::Path& dest_path) const override;
75
76 /**
77 * Open a directory specified by its path
78 * @param path Path relative to the archive
79 * @return Opened directory, or nullptr
80 */
81 std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const override;
82
83 /**
84 * Getter for the path used for this Archive
85 * @return Mount point of that passthrough archive
86 */
87 std::string GetMountPoint() const;
88
89private:
90 std::string mount_point;
91}; 29};
92 30
93} // namespace FileSys 31} // namespace FileSys
diff --git a/src/core/file_sys/directory_sdmc.cpp b/src/core/file_sys/directory_sdmc.cpp
deleted file mode 100644
index 519787641..000000000
--- a/src/core/file_sys/directory_sdmc.cpp
+++ /dev/null
@@ -1,88 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#include <sys/stat.h>
6
7#include "common/common_types.h"
8#include "common/file_util.h"
9
10#include "core/file_sys/directory_sdmc.h"
11#include "core/file_sys/archive_sdmc.h"
12
13////////////////////////////////////////////////////////////////////////////////////////////////////
14// FileSys namespace
15
16namespace FileSys {
17
18Directory_SDMC::Directory_SDMC(const Archive_SDMC* archive, const Path& path) {
19 // TODO(Link Mauve): normalize path into an absolute path without "..", it can currently bypass
20 // the root directory we set while opening the archive.
21 // For example, opening /../../usr/bin can give the emulated program your installed programs.
22 this->path = archive->GetMountPoint() + path.AsString();
23
24}
25
26Directory_SDMC::~Directory_SDMC() {
27 Close();
28}
29
30bool Directory_SDMC::Open() {
31 if (!FileUtil::IsDirectory(path))
32 return false;
33 FileUtil::ScanDirectoryTree(path, directory);
34 children_iterator = directory.children.begin();
35 return true;
36}
37
38/**
39 * List files contained in the directory
40 * @param count Number of entries to return at once in entries
41 * @param entries Buffer to read data into
42 * @return Number of entries listed
43 */
44u32 Directory_SDMC::Read(const u32 count, Entry* entries) {
45 u32 entries_read = 0;
46
47 while (entries_read < count && children_iterator != directory.children.cend()) {
48 const FileUtil::FSTEntry& file = *children_iterator;
49 const std::string& filename = file.virtualName;
50 Entry& entry = entries[entries_read];
51
52 LOG_TRACE(Service_FS, "File %s: size=%llu dir=%d", filename.c_str(), file.size, file.isDirectory);
53
54 // TODO(Link Mauve): use a proper conversion to UTF-16.
55 for (size_t j = 0; j < FILENAME_LENGTH; ++j) {
56 entry.filename[j] = filename[j];
57 if (!filename[j])
58 break;
59 }
60
61 FileUtil::SplitFilename83(filename, entry.short_name, entry.extension);
62
63 entry.is_directory = file.isDirectory;
64 entry.is_hidden = (filename[0] == '.');
65 entry.is_read_only = 0;
66 entry.file_size = file.size;
67
68 // We emulate a SD card where the archive bit has never been cleared, as it would be on
69 // most user SD cards.
70 // Some homebrews (blargSNES for instance) are known to mistakenly use the archive bit as a
71 // file bit.
72 entry.is_archive = !file.isDirectory;
73
74 ++entries_read;
75 ++children_iterator;
76 }
77 return entries_read;
78}
79
80/**
81 * Close the directory
82 * @return true if the directory closed correctly
83 */
84bool Directory_SDMC::Close() const {
85 return true;
86}
87
88} // 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 407a256ef..000000000
--- a/src/core/file_sys/directory_sdmc.h
+++ /dev/null
@@ -1,55 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8#include "common/file_util.h"
9
10#include "core/file_sys/directory_backend.h"
11#include "core/file_sys/archive_sdmc.h"
12#include "core/loader/loader.h"
13
14////////////////////////////////////////////////////////////////////////////////////////////////////
15// FileSys namespace
16
17namespace FileSys {
18
19class Directory_SDMC final : public DirectoryBackend {
20public:
21 Directory_SDMC();
22 Directory_SDMC(const Archive_SDMC* archive, const Path& path);
23 ~Directory_SDMC() override;
24
25 /**
26 * Open the directory
27 * @return true if the directory opened correctly
28 */
29 bool Open() override;
30
31 /**
32 * List files contained in the directory
33 * @param count Number of entries to return at once in entries
34 * @param entries Buffer to read data into
35 * @return Number of entries listed
36 */
37 u32 Read(const u32 count, Entry* entries) override;
38
39 /**
40 * Close the directory
41 * @return true if the directory closed correctly
42 */
43 bool Close() const override;
44
45private:
46 std::string path;
47 u32 total_entries_in_directory;
48 FileUtil::FSTEntry directory;
49
50 // We need to remember the last entry we returned, so a subsequent call to Read will continue
51 // from the next one. This iterator will always point to the next unread entry.
52 std::vector<FileUtil::FSTEntry>::iterator children_iterator;
53};
54
55} // 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..eabf58057
--- /dev/null
+++ b/src/core/file_sys/disk_archive.cpp
@@ -0,0 +1,167 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#include <sys/stat.h>
6
7#include "common/common_types.h"
8#include "common/file_util.h"
9
10#include "core/file_sys/disk_archive.h"
11#include "core/settings.h"
12
13////////////////////////////////////////////////////////////////////////////////////////////////////
14// FileSys namespace
15
16namespace FileSys {
17
18std::unique_ptr<FileBackend> DiskArchive::OpenFile(const Path& path, const Mode mode) const {
19 LOG_DEBUG(Service_FS, "called path=%s mode=%01X", path.DebugStr().c_str(), mode.hex);
20 DiskFile* file = new DiskFile(this, path, mode);
21 if (!file->Open())
22 return nullptr;
23 return std::unique_ptr<FileBackend>(file);
24}
25
26bool DiskArchive::DeleteFile(const FileSys::Path& path) const {
27 return FileUtil::Delete(GetMountPoint() + path.AsString());
28}
29
30bool DiskArchive::RenameFile(const FileSys::Path& src_path, const FileSys::Path& dest_path) const {
31 return FileUtil::Rename(GetMountPoint() + src_path.AsString(), GetMountPoint() + dest_path.AsString());
32}
33
34bool DiskArchive::DeleteDirectory(const FileSys::Path& path) const {
35 return FileUtil::DeleteDir(GetMountPoint() + path.AsString());
36}
37
38bool DiskArchive::CreateDirectory(const Path& path) const {
39 return FileUtil::CreateDir(GetMountPoint() + path.AsString());
40}
41
42bool DiskArchive::RenameDirectory(const FileSys::Path& src_path, const FileSys::Path& dest_path) const {
43 return FileUtil::Rename(GetMountPoint() + src_path.AsString(), GetMountPoint() + dest_path.AsString());
44}
45
46std::unique_ptr<DirectoryBackend> DiskArchive::OpenDirectory(const Path& path) const {
47 LOG_DEBUG(Service_FS, "called path=%s", path.DebugStr().c_str());
48 DiskDirectory* directory = new DiskDirectory(this, path);
49 if (!directory->Open())
50 return nullptr;
51 return std::unique_ptr<DirectoryBackend>(directory);
52}
53
54////////////////////////////////////////////////////////////////////////////////////////////////////
55
56DiskFile::DiskFile(const DiskArchive* archive, const Path& path, const Mode mode) {
57 // TODO(Link Mauve): normalize path into an absolute path without "..", it can currently bypass
58 // the root directory we set while opening the archive.
59 // For example, opening /../../etc/passwd can give the emulated program your users list.
60 this->path = archive->GetMountPoint() + path.AsString();
61 this->mode.hex = mode.hex;
62 this->archive = archive;
63}
64
65bool DiskFile::Open() {
66 if (!mode.create_flag && !FileUtil::Exists(path)) {
67 LOG_ERROR(Service_FS, "Non-existing file %s can’t be open without mode create.", path.c_str());
68 return false;
69 }
70
71 std::string mode_string;
72 if (mode.create_flag)
73 mode_string = "w+";
74 else if (mode.write_flag)
75 mode_string = "r+"; // Files opened with Write access can be read from
76 else if (mode.read_flag)
77 mode_string = "r";
78
79 // Open the file in binary mode, to avoid problems with CR/LF on Windows systems
80 mode_string += "b";
81
82 file = new FileUtil::IOFile(path, mode_string.c_str());
83 return true;
84}
85
86size_t DiskFile::Read(const u64 offset, const u32 length, u8* buffer) const {
87 file->Seek(offset, SEEK_SET);
88 return file->ReadBytes(buffer, length);
89}
90
91size_t DiskFile::Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const {
92 file->Seek(offset, SEEK_SET);
93 size_t written = file->WriteBytes(buffer, length);
94 if (flush)
95 file->Flush();
96 return written;
97}
98
99size_t DiskFile::GetSize() const {
100 return static_cast<size_t>(file->GetSize());
101}
102
103bool DiskFile::SetSize(const u64 size) const {
104 file->Resize(size);
105 file->Flush();
106 return true;
107}
108
109bool DiskFile::Close() const {
110 return file->Close();
111}
112
113////////////////////////////////////////////////////////////////////////////////////////////////////
114
115DiskDirectory::DiskDirectory(const DiskArchive* archive, const Path& path) {
116 // TODO(Link Mauve): normalize path into an absolute path without "..", it can currently bypass
117 // the root directory we set while opening the archive.
118 // For example, opening /../../usr/bin can give the emulated program your installed programs.
119 this->path = archive->GetMountPoint() + path.AsString();
120 this->archive = archive;
121}
122
123bool DiskDirectory::Open() {
124 if (!FileUtil::IsDirectory(path))
125 return false;
126 FileUtil::ScanDirectoryTree(path, directory);
127 children_iterator = directory.children.begin();
128 return true;
129}
130
131u32 DiskDirectory::Read(const u32 count, Entry* entries) {
132 u32 entries_read = 0;
133
134 while (entries_read < count && children_iterator != directory.children.cend()) {
135 const FileUtil::FSTEntry& file = *children_iterator;
136 const std::string& filename = file.virtualName;
137 Entry& entry = entries[entries_read];
138
139 LOG_TRACE(Service_FS, "File %s: size=%llu dir=%d", filename.c_str(), file.size, file.isDirectory);
140
141 // TODO(Link Mauve): use a proper conversion to UTF-16.
142 for (size_t j = 0; j < FILENAME_LENGTH; ++j) {
143 entry.filename[j] = filename[j];
144 if (!filename[j])
145 break;
146 }
147
148 FileUtil::SplitFilename83(filename, entry.short_name, entry.extension);
149
150 entry.is_directory = file.isDirectory;
151 entry.is_hidden = (filename[0] == '.');
152 entry.is_read_only = 0;
153 entry.file_size = file.size;
154
155 // We emulate a SD card where the archive bit has never been cleared, as it would be on
156 // most user SD cards.
157 // Some homebrews (blargSNES for instance) are known to mistakenly use the archive bit as a
158 // file bit.
159 entry.is_archive = !file.isDirectory;
160
161 ++entries_read;
162 ++children_iterator;
163 }
164 return entries_read;
165}
166
167} // 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..778c83953
--- /dev/null
+++ b/src/core/file_sys/disk_archive.h
@@ -0,0 +1,101 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8
9#include "core/file_sys/archive_backend.h"
10#include "core/loader/loader.h"
11
12////////////////////////////////////////////////////////////////////////////////////////////////////
13// FileSys namespace
14
15namespace FileSys {
16
17/**
18 * Helper which implements a backend accessing the host machine's filesystem.
19 * This should be subclassed by concrete archive types, which will provide the
20 * base directory on the host filesystem and override any required functionality.
21 */
22class DiskArchive : public ArchiveBackend {
23public:
24 DiskArchive(const std::string& mount_point_) : mount_point(mount_point_) {}
25
26 virtual std::string GetName() const = 0;
27 std::unique_ptr<FileBackend> OpenFile(const Path& path, const Mode mode) const override;
28 bool DeleteFile(const FileSys::Path& path) const override;
29 bool RenameFile(const FileSys::Path& src_path, const FileSys::Path& dest_path) const override;
30 bool DeleteDirectory(const FileSys::Path& path) const override;
31 bool CreateDirectory(const Path& path) const override;
32 bool RenameDirectory(const FileSys::Path& src_path, const FileSys::Path& dest_path) const override;
33 std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const override;
34
35 /**
36 * Getter for the path used for this Archive
37 * @return Mount point of that passthrough archive
38 */
39 const std::string& GetMountPoint() const {
40 return mount_point;
41 }
42
43protected:
44 std::string mount_point;
45};
46
47class DiskFile : public FileBackend {
48public:
49 DiskFile();
50 DiskFile(const DiskArchive* archive, const Path& path, const Mode mode);
51
52 ~DiskFile() override {
53 Close();
54 }
55
56 bool Open() override;
57 size_t Read(const u64 offset, const u32 length, u8* buffer) const override;
58 size_t Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const override;
59 size_t GetSize() const override;
60 bool SetSize(const u64 size) const override;
61 bool Close() const override;
62
63 void Flush() const override {
64 file->Flush();
65 }
66
67protected:
68 const DiskArchive* archive;
69 std::string path;
70 Mode mode;
71 FileUtil::IOFile* file;
72};
73
74class DiskDirectory : public DirectoryBackend {
75public:
76 DiskDirectory();
77 DiskDirectory(const DiskArchive* archive, const Path& path);
78
79 ~DiskDirectory() override {
80 Close();
81 }
82
83 bool Open() override;
84 u32 Read(const u32 count, Entry* entries) override;
85
86 bool Close() const override {
87 return true;
88 }
89
90protected:
91 const DiskArchive* archive;
92 std::string path;
93 u32 total_entries_in_directory;
94 FileUtil::FSTEntry directory;
95
96 // We need to remember the last entry we returned, so a subsequent call to Read will continue
97 // from the next one. This iterator will always point to the next unread entry.
98 std::vector<FileUtil::FSTEntry>::iterator children_iterator;
99};
100
101} // namespace FileSys
diff --git a/src/core/file_sys/file_backend.h b/src/core/file_sys/file_backend.h
index 1b81d5fe9..539ec7314 100644
--- a/src/core/file_sys/file_backend.h
+++ b/src/core/file_sys/file_backend.h
@@ -61,6 +61,11 @@ public:
61 * @return true if the file closed correctly 61 * @return true if the file closed correctly
62 */ 62 */
63 virtual bool Close() const = 0; 63 virtual bool Close() const = 0;
64
65 /**
66 * Flushes the file
67 */
68 virtual void Flush() const = 0;
64}; 69};
65 70
66} // namespace FileSys 71} // namespace FileSys
diff --git a/src/core/file_sys/file_romfs.h b/src/core/file_sys/file_romfs.h
index 09fa2e7e3..32fa6b6d3 100644
--- a/src/core/file_sys/file_romfs.h
+++ b/src/core/file_sys/file_romfs.h
@@ -64,6 +64,8 @@ public:
64 */ 64 */
65 bool Close() const override; 65 bool Close() const override;
66 66
67 void Flush() const override { }
68
67private: 69private:
68 const Archive_RomFS* archive; 70 const Archive_RomFS* archive;
69}; 71};
diff --git a/src/core/file_sys/file_sdmc.cpp b/src/core/file_sys/file_sdmc.cpp
deleted file mode 100644
index 46c29900b..000000000
--- a/src/core/file_sys/file_sdmc.cpp
+++ /dev/null
@@ -1,110 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#include <sys/stat.h>
6
7#include "common/common_types.h"
8#include "common/file_util.h"
9
10#include "core/file_sys/file_sdmc.h"
11#include "core/file_sys/archive_sdmc.h"
12
13////////////////////////////////////////////////////////////////////////////////////////////////////
14// FileSys namespace
15
16namespace FileSys {
17
18File_SDMC::File_SDMC(const Archive_SDMC* archive, const Path& path, const Mode mode) {
19 // TODO(Link Mauve): normalize path into an absolute path without "..", it can currently bypass
20 // the root directory we set while opening the archive.
21 // For example, opening /../../etc/passwd can give the emulated program your users list.
22 this->path = archive->GetMountPoint() + path.AsString();
23 this->mode.hex = mode.hex;
24}
25
26File_SDMC::~File_SDMC() {
27 Close();
28}
29
30/**
31 * Open the file
32 * @return true if the file opened correctly
33 */
34bool File_SDMC::Open() {
35 if (!mode.create_flag && !FileUtil::Exists(path)) {
36 LOG_ERROR(Service_FS, "Non-existing file %s can’t be open without mode create.", path.c_str());
37 return false;
38 }
39
40 std::string mode_string;
41 if (mode.create_flag)
42 mode_string = "w+";
43 else if (mode.write_flag)
44 mode_string = "r+"; // Files opened with Write access can be read from
45 else if (mode.read_flag)
46 mode_string = "r";
47
48 // Open the file in binary mode, to avoid problems with CR/LF on Windows systems
49 mode_string += "b";
50
51 file = new FileUtil::IOFile(path, mode_string.c_str());
52 return true;
53}
54
55/**
56 * Read data from the file
57 * @param offset Offset in bytes to start reading data from
58 * @param length Length in bytes of data to read from file
59 * @param buffer Buffer to read data into
60 * @return Number of bytes read
61 */
62size_t File_SDMC::Read(const u64 offset, const u32 length, u8* buffer) const {
63 file->Seek(offset, SEEK_SET);
64 return file->ReadBytes(buffer, length);
65}
66
67/**
68 * Write data to the file
69 * @param offset Offset in bytes to start writing data to
70 * @param length Length in bytes of data to write to file
71 * @param flush The flush parameters (0 == do not flush)
72 * @param buffer Buffer to read data from
73 * @return Number of bytes written
74 */
75size_t File_SDMC::Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const {
76 file->Seek(offset, SEEK_SET);
77 size_t written = file->WriteBytes(buffer, length);
78 if (flush)
79 file->Flush();
80 return written;
81}
82
83/**
84 * Get the size of the file in bytes
85 * @return Size of the file in bytes
86 */
87size_t File_SDMC::GetSize() const {
88 return static_cast<size_t>(file->GetSize());
89}
90
91/**
92 * Set the size of the file in bytes
93 * @param size New size of the file
94 * @return true if successful
95 */
96bool File_SDMC::SetSize(const u64 size) const {
97 file->Resize(size);
98 file->Flush();
99 return true;
100}
101
102/**
103 * Close the file
104 * @return true if the file closed correctly
105 */
106bool File_SDMC::Close() const {
107 return file->Close();
108}
109
110} // 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 e01548598..000000000
--- a/src/core/file_sys/file_sdmc.h
+++ /dev/null
@@ -1,75 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8#include "common/file_util.h"
9
10#include "core/file_sys/file_backend.h"
11#include "core/file_sys/archive_sdmc.h"
12#include "core/loader/loader.h"
13
14////////////////////////////////////////////////////////////////////////////////////////////////////
15// FileSys namespace
16
17namespace FileSys {
18
19class File_SDMC final : public FileBackend {
20public:
21 File_SDMC();
22 File_SDMC(const Archive_SDMC* archive, const Path& path, const Mode mode);
23 ~File_SDMC() override;
24
25 /**
26 * Open the file
27 * @return true if the file opened correctly
28 */
29 bool Open() override;
30
31 /**
32 * Read data from the file
33 * @param offset Offset in bytes to start reading data from
34 * @param length Length in bytes of data to read from file
35 * @param buffer Buffer to read data into
36 * @return Number of bytes read
37 */
38 size_t Read(const u64 offset, const u32 length, u8* buffer) const override;
39
40 /**
41 * Write data to the file
42 * @param offset Offset in bytes to start writing data to
43 * @param length Length in bytes of data to write to file
44 * @param flush The flush parameters (0 == do not flush)
45 * @param buffer Buffer to read data from
46 * @return Number of bytes written
47 */
48 size_t Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const override;
49
50 /**
51 * Get the size of the file in bytes
52 * @return Size of the file in bytes
53 */
54 size_t GetSize() const override;
55
56 /**
57 * Set the size of the file in bytes
58 * @param size New size of the file
59 * @return true if successful
60 */
61 bool SetSize(const u64 size) const override;
62
63 /**
64 * Close the file
65 * @return true if the file closed correctly
66 */
67 bool Close() const override;
68
69private:
70 std::string path;
71 Mode mode;
72 FileUtil::IOFile* file;
73};
74
75} // namespace FileSys
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 929422b36..6a690e915 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -14,6 +14,7 @@ namespace Kernel {
14 14
15Handle g_main_thread = 0; 15Handle g_main_thread = 0;
16ObjectPool g_object_pool; 16ObjectPool g_object_pool;
17u64 g_program_id = 0;
17 18
18ObjectPool::ObjectPool() { 19ObjectPool::ObjectPool() {
19 next_id = INITIAL_NEXT_ID; 20 next_id = INITIAL_NEXT_ID;
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 7e0f15c84..7123485be 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -151,6 +151,12 @@ private:
151extern ObjectPool g_object_pool; 151extern ObjectPool g_object_pool;
152extern Handle g_main_thread; 152extern Handle g_main_thread;
153 153
154/// The ID code of the currently running game
155/// TODO(Subv): This variable should not be here,
156/// we need a way to store information about the currently loaded application
157/// for later query during runtime, maybe using the LDR service?
158extern u64 g_program_id;
159
154/// Initialize the kernel 160/// Initialize the kernel
155void Init(); 161void Init();
156 162
diff --git a/src/core/hle/result.h b/src/core/hle/result.h
index 15c4a2677..14d2be4a2 100644
--- a/src/core/hle/result.h
+++ b/src/core/hle/result.h
@@ -17,6 +17,8 @@
17/// Detailed description of the error. This listing is likely incomplete. 17/// Detailed description of the error. This listing is likely incomplete.
18enum class ErrorDescription : u32 { 18enum class ErrorDescription : u32 {
19 Success = 0, 19 Success = 0,
20 FS_NotFound = 100,
21 FS_NotFormatted = 340, ///< This is used by the FS service when creating a SaveData archive
20 InvalidSection = 1000, 22 InvalidSection = 1000,
21 TooLarge = 1001, 23 TooLarge = 1001,
22 NotAuthorized = 1002, 24 NotAuthorized = 1002,
diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp
index caf82d556..9c3834733 100644
--- a/src/core/hle/service/fs/archive.cpp
+++ b/src/core/hle/service/fs/archive.cpp
@@ -9,6 +9,7 @@
9#include "common/file_util.h" 9#include "common/file_util.h"
10#include "common/math_util.h" 10#include "common/math_util.h"
11 11
12#include "core/file_sys/archive_savedata.h"
12#include "core/file_sys/archive_backend.h" 13#include "core/file_sys/archive_backend.h"
13#include "core/file_sys/archive_sdmc.h" 14#include "core/file_sys/archive_sdmc.h"
14#include "core/file_sys/directory_backend.h" 15#include "core/file_sys/directory_backend.h"
@@ -135,6 +136,13 @@ public:
135 break; 136 break;
136 } 137 }
137 138
139 case FileCommand::Flush:
140 {
141 LOG_TRACE(Service_FS, "Flush");
142 backend->Flush();
143 break;
144 }
145
138 // Unknown command... 146 // Unknown command...
139 default: 147 default:
140 LOG_ERROR(Service_FS, "Unknown command=0x%08X!", cmd); 148 LOG_ERROR(Service_FS, "Unknown command=0x%08X!", cmd);
@@ -220,9 +228,18 @@ ResultVal<ArchiveHandle> OpenArchive(ArchiveIdCode id_code) {
220 228
221 auto itr = id_code_map.find(id_code); 229 auto itr = id_code_map.find(id_code);
222 if (itr == id_code_map.end()) { 230 if (itr == id_code_map.end()) {
231 if (id_code == ArchiveIdCode::SaveData) {
232 // When a SaveData archive is created for the first time, it is not yet formatted
233 // and the save file/directory structure expected by the game has not yet been initialized.
234 // Returning the NotFormatted error code will signal the game to provision the SaveData archive
235 // with the files and folders that it expects.
236 // The FormatSaveData service call will create the SaveData archive when it is called.
237 return ResultCode(ErrorDescription::FS_NotFormatted, ErrorModule::FS,
238 ErrorSummary::InvalidState, ErrorLevel::Status);
239 }
223 // TODO: Verify error against hardware 240 // TODO: Verify error against hardware
224 return ResultCode(ErrorDescription::NotFound, ErrorModule::FS, 241 return ResultCode(ErrorDescription::NotFound, ErrorModule::FS,
225 ErrorSummary::NotFound, ErrorLevel::Permanent); 242 ErrorSummary::NotFound, ErrorLevel::Permanent);
226 } 243 }
227 244
228 // This should never even happen in the first place with 64-bit handles, 245 // This should never even happen in the first place with 64-bit handles,
@@ -260,8 +277,8 @@ ResultVal<Handle> OpenFileFromArchive(ArchiveHandle archive_handle, const FileSy
260 277
261 std::unique_ptr<FileSys::FileBackend> backend = archive->backend->OpenFile(path, mode); 278 std::unique_ptr<FileSys::FileBackend> backend = archive->backend->OpenFile(path, mode);
262 if (backend == nullptr) { 279 if (backend == nullptr) {
263 return ResultCode(ErrorDescription::NotFound, ErrorModule::FS, 280 return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS,
264 ErrorSummary::NotFound, ErrorLevel::Permanent); 281 ErrorSummary::NotFound, ErrorLevel::Status);
265 } 282 }
266 283
267 auto file = std::make_unique<File>(std::move(backend), path); 284 auto file = std::make_unique<File>(std::move(backend), path);
@@ -366,6 +383,28 @@ ResultVal<Handle> OpenDirectoryFromArchive(ArchiveHandle archive_handle, const F
366 return MakeResult<Handle>(handle); 383 return MakeResult<Handle>(handle);
367} 384}
368 385
386ResultCode FormatSaveData() {
387 // TODO(Subv): Actually wipe the savedata folder after creating or opening it
388
389 // Do not create the archive again if it already exists
390 if (id_code_map.find(ArchiveIdCode::SaveData) != id_code_map.end())
391 return UnimplementedFunction(ErrorModule::FS); // TODO(Subv): Find the correct error code
392
393 // Create the SaveData archive
394 std::string savedata_directory = FileUtil::GetUserPath(D_SAVEDATA_IDX);
395 auto savedata_archive = std::make_unique<FileSys::Archive_SaveData>(savedata_directory,
396 Kernel::g_program_id);
397
398 if (savedata_archive->Initialize()) {
399 CreateArchive(std::move(savedata_archive), ArchiveIdCode::SaveData);
400 return RESULT_SUCCESS;
401 } else {
402 LOG_ERROR(Service_FS, "Can't instantiate SaveData archive with path %s",
403 savedata_archive->GetMountPoint().c_str());
404 return UnimplementedFunction(ErrorModule::FS); // TODO(Subv): Find the proper error code
405 }
406}
407
369/// Initialize archives 408/// Initialize archives
370void ArchiveInit() { 409void ArchiveInit() {
371 next_handle = 1; 410 next_handle = 1;
@@ -375,9 +414,9 @@ void ArchiveInit() {
375 // archive type is SDMC, so it is the only one getting exposed. 414 // archive type is SDMC, so it is the only one getting exposed.
376 415
377 std::string sdmc_directory = FileUtil::GetUserPath(D_SDMC_IDX); 416 std::string sdmc_directory = FileUtil::GetUserPath(D_SDMC_IDX);
378 auto archive = std::make_unique<FileSys::Archive_SDMC>(sdmc_directory); 417 auto sdmc_archive = std::make_unique<FileSys::Archive_SDMC>(sdmc_directory);
379 if (archive->Initialize()) 418 if (sdmc_archive->Initialize())
380 CreateArchive(std::move(archive), ArchiveIdCode::SDMC); 419 CreateArchive(std::move(sdmc_archive), ArchiveIdCode::SDMC);
381 else 420 else
382 LOG_ERROR(Service_FS, "Can't instantiate SDMC archive with path %s", sdmc_directory.c_str()); 421 LOG_ERROR(Service_FS, "Can't instantiate SDMC archive with path %s", sdmc_directory.c_str());
383} 422}
diff --git a/src/core/hle/service/fs/archive.h b/src/core/hle/service/fs/archive.h
index a38de92e3..a128276b6 100644
--- a/src/core/hle/service/fs/archive.h
+++ b/src/core/hle/service/fs/archive.h
@@ -109,6 +109,12 @@ ResultCode RenameDirectoryBetweenArchives(ArchiveHandle src_archive_handle, cons
109 */ 109 */
110ResultVal<Handle> OpenDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path); 110ResultVal<Handle> OpenDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path);
111 111
112/**
113 * Creates a blank SaveData archive.
114 * @return ResultCode 0 on success or the corresponding code on error
115 */
116ResultCode FormatSaveData();
117
112/// Initialize archives 118/// Initialize archives
113void ArchiveInit(); 119void ArchiveInit();
114 120
diff --git a/src/core/hle/service/fs/fs_user.cpp b/src/core/hle/service/fs/fs_user.cpp
index 0f75d5e3a..f99d84b2f 100644
--- a/src/core/hle/service/fs/fs_user.cpp
+++ b/src/core/hle/service/fs/fs_user.cpp
@@ -3,11 +3,11 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/common.h" 5#include "common/common.h"
6#include "common/file_util.h"
6#include "common/scope_exit.h" 7#include "common/scope_exit.h"
7
8#include "common/string_util.h" 8#include "common/string_util.h"
9#include "core/hle/service/fs/archive.h"
10#include "core/hle/result.h" 9#include "core/hle/result.h"
10#include "core/hle/service/fs/archive.h"
11#include "core/hle/service/fs/fs_user.h" 11#include "core/hle/service/fs/fs_user.h"
12#include "core/settings.h" 12#include "core/settings.h"
13 13
@@ -50,9 +50,7 @@ static void Initialize(Service::Interface* self) {
50static void OpenFile(Service::Interface* self) { 50static void OpenFile(Service::Interface* self) {
51 u32* cmd_buff = Kernel::GetCommandBuffer(); 51 u32* cmd_buff = Kernel::GetCommandBuffer();
52 52
53 // TODO(Link Mauve): cmd_buff[2], aka archive handle lower word, isn't used according to 53 ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]);
54 // 3dmoo's or ctrulib's implementations. Triple check if it's really the case.
55 Handle archive_handle = static_cast<Handle>(cmd_buff[3]);
56 auto filename_type = static_cast<FileSys::LowPathType>(cmd_buff[4]); 54 auto filename_type = static_cast<FileSys::LowPathType>(cmd_buff[4]);
57 u32 filename_size = cmd_buff[5]; 55 u32 filename_size = cmd_buff[5];
58 FileSys::Mode mode; mode.hex = cmd_buff[6]; 56 FileSys::Mode mode; mode.hex = cmd_buff[6];
@@ -398,6 +396,36 @@ static void IsSdmcDetected(Service::Interface* self) {
398 LOG_DEBUG(Service_FS, "called"); 396 LOG_DEBUG(Service_FS, "called");
399} 397}
400 398
399/**
400 * FS_User::FormatSaveData service function
401 * Inputs:
402 * Outputs:
403 * 1 : Result of function, 0 on success, otherwise error code
404 */
405static void FormatSaveData(Service::Interface* self) {
406 u32* cmd_buff = Kernel::GetCommandBuffer();
407 LOG_DEBUG(Service_FS, "(STUBBED)");
408
409 // TODO(Subv): Find out what the inputs and outputs of this function are
410
411 cmd_buff[1] = FormatSaveData().raw;
412}
413
414/**
415 * FS_User::FormatThisUserSaveData service function
416 * Inputs:
417 * Outputs:
418 * 1 : Result of function, 0 on success, otherwise error code
419 */
420static void FormatThisUserSaveData(Service::Interface* self) {
421 u32* cmd_buff = Kernel::GetCommandBuffer();
422 LOG_DEBUG(Service_FS, "(STUBBED)");
423
424 // TODO(Subv): Find out what the inputs and outputs of this function are
425
426 cmd_buff[1] = FormatSaveData().raw;
427}
428
401const FSUserInterface::FunctionInfo FunctionTable[] = { 429const FSUserInterface::FunctionInfo FunctionTable[] = {
402 {0x000100C6, nullptr, "Dummy1"}, 430 {0x000100C6, nullptr, "Dummy1"},
403 {0x040100C4, nullptr, "Control"}, 431 {0x040100C4, nullptr, "Control"},
@@ -415,7 +443,7 @@ const FSUserInterface::FunctionInfo FunctionTable[] = {
415 {0x080C00C2, OpenArchive, "OpenArchive"}, 443 {0x080C00C2, OpenArchive, "OpenArchive"},
416 {0x080D0144, nullptr, "ControlArchive"}, 444 {0x080D0144, nullptr, "ControlArchive"},
417 {0x080E0080, CloseArchive, "CloseArchive"}, 445 {0x080E0080, CloseArchive, "CloseArchive"},
418 {0x080F0180, nullptr, "FormatThisUserSaveData"}, 446 {0x080F0180, FormatThisUserSaveData,"FormatThisUserSaveData"},
419 {0x08100200, nullptr, "CreateSystemSaveData"}, 447 {0x08100200, nullptr, "CreateSystemSaveData"},
420 {0x08110040, nullptr, "DeleteSystemSaveData"}, 448 {0x08110040, nullptr, "DeleteSystemSaveData"},
421 {0x08120080, nullptr, "GetFreeBytes"}, 449 {0x08120080, nullptr, "GetFreeBytes"},
@@ -476,7 +504,7 @@ const FSUserInterface::FunctionInfo FunctionTable[] = {
476 {0x08490040, nullptr, "GetArchiveResource"}, 504 {0x08490040, nullptr, "GetArchiveResource"},
477 {0x084A0002, nullptr, "ExportIntegrityVerificationSeed"}, 505 {0x084A0002, nullptr, "ExportIntegrityVerificationSeed"},
478 {0x084B0002, nullptr, "ImportIntegrityVerificationSeed"}, 506 {0x084B0002, nullptr, "ImportIntegrityVerificationSeed"},
479 {0x084C0242, nullptr, "FormatSaveData"}, 507 {0x084C0242, FormatSaveData, "FormatSaveData"},
480 {0x084D0102, nullptr, "GetLegacySubBannerData"}, 508 {0x084D0102, nullptr, "GetLegacySubBannerData"},
481 {0x084E0342, nullptr, "UpdateSha256Context"}, 509 {0x084E0342, nullptr, "UpdateSha256Context"},
482 {0x084F0102, nullptr, "ReadSpecialFile"}, 510 {0x084F0102, nullptr, "ReadSpecialFile"},
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp
index 463dacca3..480274d23 100644
--- a/src/core/loader/loader.cpp
+++ b/src/core/loader/loader.cpp
@@ -74,6 +74,7 @@ ResultStatus LoadFile(const std::string& filename) {
74 74
75 // Load application and RomFS 75 // Load application and RomFS
76 if (ResultStatus::Success == app_loader.Load()) { 76 if (ResultStatus::Success == app_loader.Load()) {
77 Kernel::g_program_id = app_loader.GetProgramId();
77 Service::FS::CreateArchive(std::make_unique<FileSys::Archive_RomFS>(app_loader), Service::FS::ArchiveIdCode::RomFS); 78 Service::FS::CreateArchive(std::make_unique<FileSys::Archive_RomFS>(app_loader), Service::FS::ArchiveIdCode::RomFS);
78 return ResultStatus::Success; 79 return ResultStatus::Success;
79 } 80 }
diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp
index ba9ba00c0..4d23656ec 100644
--- a/src/core/loader/ncch.cpp
+++ b/src/core/loader/ncch.cpp
@@ -315,4 +315,8 @@ ResultStatus AppLoader_NCCH::ReadRomFS(std::vector<u8>& buffer) const {
315 return ResultStatus::Error; 315 return ResultStatus::Error;
316} 316}
317 317
318u64 AppLoader_NCCH::GetProgramId() const {
319 return *reinterpret_cast<u64 const*>(&ncch_header.program_id[0]);
320}
321
318} // namespace Loader 322} // namespace Loader
diff --git a/src/core/loader/ncch.h b/src/core/loader/ncch.h
index 03116add8..2fe2a7d82 100644
--- a/src/core/loader/ncch.h
+++ b/src/core/loader/ncch.h
@@ -191,6 +191,12 @@ public:
191 */ 191 */
192 ResultStatus ReadRomFS(std::vector<u8>& buffer) const override; 192 ResultStatus ReadRomFS(std::vector<u8>& buffer) const override;
193 193
194 /*
195 * Gets the program id from the NCCH header
196 * @return u64 Program id
197 */
198 u64 GetProgramId() const;
199
194private: 200private:
195 201
196 /** 202 /**