summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-x.travis-deps.sh4
-rwxr-xr-x.travis-upload.sh2
-rw-r--r--.travis.yml1
-rw-r--r--CMakeLists.txt2
-rw-r--r--README.md4
-rw-r--r--dist/citra.xml24
-rw-r--r--src/audio_core/sdl2_sink.cpp4
-rw-r--r--src/citra/config.cpp1
-rw-r--r--src/citra/default_ini.h4
-rw-r--r--src/citra_qt/config.cpp2
-rw-r--r--src/common/common_paths.h2
-rw-r--r--src/common/file_util.cpp16
-rw-r--r--src/common/file_util.h1
-rw-r--r--src/common/logging/backend.cpp1
-rw-r--r--src/common/logging/log.h1
-rw-r--r--src/core/CMakeLists.txt10
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.cpp5
-rw-r--r--src/core/file_sys/archive_backend.h28
-rw-r--r--src/core/file_sys/archive_extsavedata.cpp115
-rw-r--r--src/core/file_sys/archive_ncch.cpp (renamed from src/core/file_sys/archive_savedatacheck.cpp)22
-rw-r--r--src/core/file_sys/archive_ncch.h (renamed from src/core/file_sys/archive_savedatacheck.h)8
-rw-r--r--src/core/file_sys/archive_savedata.cpp4
-rw-r--r--src/core/file_sys/archive_sdmc.cpp279
-rw-r--r--src/core/file_sys/archive_sdmc.h26
-rw-r--r--src/core/file_sys/archive_sdmcwriteonly.cpp70
-rw-r--r--src/core/file_sys/archive_sdmcwriteonly.h57
-rw-r--r--src/core/file_sys/archive_systemsavedata.cpp4
-rw-r--r--src/core/file_sys/directory_backend.h6
-rw-r--r--src/core/file_sys/disk_archive.cpp150
-rw-r--r--src/core/file_sys/disk_archive.h43
-rw-r--r--src/core/file_sys/errors.h40
-rw-r--r--src/core/file_sys/file_backend.h6
-rw-r--r--src/core/file_sys/ivfc_archive.cpp31
-rw-r--r--src/core/file_sys/ivfc_archive.h20
-rw-r--r--src/core/file_sys/path_parser.cpp98
-rw-r--r--src/core/file_sys/path_parser.h61
-rw-r--r--src/core/file_sys/savedata_archive.cpp283
-rw-r--r--src/core/file_sys/savedata_archive.h43
-rw-r--r--src/core/hle/applets/erreula.cpp6
-rw-r--r--src/core/hle/applets/mii_selector.cpp6
-rw-r--r--src/core/hle/applets/swkbd.cpp6
-rw-r--r--src/core/hle/kernel/event.cpp5
-rw-r--r--src/core/hle/kernel/timer.cpp5
-rw-r--r--src/core/hle/result.h9
-rw-r--r--src/core/hle/service/apt/apt.cpp11
-rw-r--r--src/core/hle/service/apt/apt.h34
-rw-r--r--src/core/hle/service/apt/apt_a.cpp2
-rw-r--r--src/core/hle/service/apt/apt_u.cpp2
-rw-r--r--src/core/hle/service/cfg/cfg.cpp19
-rw-r--r--src/core/hle/service/err_f.cpp316
-rw-r--r--src/core/hle/service/fs/archive.cpp58
-rw-r--r--src/core/hle/service/fs/archive.h2
-rw-r--r--src/core/hle/service/mic_u.cpp322
-rw-r--r--src/core/hle/service/mic_u.h1
-rw-r--r--src/core/hle/service/ptm/ptm.cpp2
-rw-r--r--src/core/hw/gpu.cpp21
-rw-r--r--src/core/hw/gpu.h1
-rw-r--r--src/core/memory.cpp14
-rw-r--r--src/core/memory.h9
-rw-r--r--src/core/settings.h1
-rw-r--r--src/tests/CMakeLists.txt2
-rw-r--r--src/tests/core/file_sys/path_parser.cpp38
-rw-r--r--src/tests/glad.cpp14
-rw-r--r--src/video_core/command_processor.cpp28
-rw-r--r--src/video_core/pica.h2
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp6
-rw-r--r--src/video_core/renderer_opengl/gl_state.cpp4
67 files changed, 1884 insertions, 540 deletions
diff --git a/.travis-deps.sh b/.travis-deps.sh
index ab3a32382..9fd21cc57 100755
--- a/.travis-deps.sh
+++ b/.travis-deps.sh
@@ -19,8 +19,8 @@ if [ "$TRAVIS_OS_NAME" = "linux" -o -z "$TRAVIS_OS_NAME" ]; then
19 19
20 if [ ! -e $HOME/.local/lib/libSDL2.la ]; then 20 if [ ! -e $HOME/.local/lib/libSDL2.la ]; then
21 echo "SDL2 not found in cache, get and build it..." 21 echo "SDL2 not found in cache, get and build it..."
22 wget http://libsdl.org/release/SDL2-2.0.4.tar.gz -O - | tar xz 22 wget http://libsdl.org/release/SDL2-2.0.5.tar.gz -O - | tar xz
23 cd SDL2-2.0.4 23 cd SDL2-2.0.5
24 ./configure --prefix=$HOME/.local 24 ./configure --prefix=$HOME/.local
25 make -j4 && make install 25 make -j4 && make install
26 else 26 else
diff --git a/.travis-upload.sh b/.travis-upload.sh
index 1bec74b3d..2eeda4c50 100755
--- a/.travis-upload.sh
+++ b/.travis-upload.sh
@@ -14,7 +14,6 @@ if [ "$TRAVIS_EVENT_TYPE" = "push" ]&&[ "$TRAVIS_BRANCH" = "master" ]; then
14 UPLOAD_DIR="/citra/nightly/osx-amd64" 14 UPLOAD_DIR="/citra/nightly/osx-amd64"
15 mkdir "$REV_NAME" 15 mkdir "$REV_NAME"
16 16
17 brew install lftp
18 cp build/src/citra/Release/citra "$REV_NAME" 17 cp build/src/citra/Release/citra "$REV_NAME"
19 cp -r build/src/citra_qt/Release/citra-qt.app "$REV_NAME" 18 cp -r build/src/citra_qt/Release/citra-qt.app "$REV_NAME"
20 19
@@ -122,5 +121,4 @@ EOL
122 121
123 ARCHIVE_NAME="${REV_NAME}.tar.xz" 122 ARCHIVE_NAME="${REV_NAME}.tar.xz"
124 tar -cJvf "$ARCHIVE_NAME" "$REV_NAME" 123 tar -cJvf "$ARCHIVE_NAME" "$REV_NAME"
125 lftp -c "open -u citra-builds,$BUILD_PASSWORD sftp://builds.citra-emu.org; set sftp:auto-confirm yes; put -O '$UPLOAD_DIR' '$ARCHIVE_NAME'"
126fi 124fi
diff --git a/.travis.yml b/.travis.yml
index ea99be2c4..a9e7aadd2 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -25,7 +25,6 @@ addons:
25 - libqt5opengl5-dev 25 - libqt5opengl5-dev
26 - xorg-dev 26 - xorg-dev
27 - lib32stdc++6 # For CMake 27 - lib32stdc++6 # For CMake
28 - lftp # To upload builds
29 - clang-format-4.0 28 - clang-format-4.0
30 29
31cache: 30cache:
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 56503f1ad..5c9b7f86a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -156,7 +156,7 @@ if (ENABLE_SDL2)
156 if (CITRA_USE_BUNDLED_SDL2) 156 if (CITRA_USE_BUNDLED_SDL2)
157 # Detect toolchain and platform 157 # Detect toolchain and platform
158 if (MSVC14 AND ARCHITECTURE_x86_64) 158 if (MSVC14 AND ARCHITECTURE_x86_64)
159 set(SDL2_VER "SDL2-2.0.4") 159 set(SDL2_VER "SDL2-2.0.5")
160 else() 160 else()
161 message(FATAL_ERROR "No bundled SDL2 binaries for your toolchain. Disable CITRA_USE_BUNDLED_SDL2 and provide your own.") 161 message(FATAL_ERROR "No bundled SDL2 binaries for your toolchain. Disable CITRA_USE_BUNDLED_SDL2 and provide your own.")
162 endif() 162 endif()
diff --git a/README.md b/README.md
index 92e2d04a5..7d1e1de0d 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@ Citra Emulator
5[![Travis CI Build Status](https://travis-ci.org/citra-emu/citra.svg?branch=master)](https://travis-ci.org/citra-emu/citra) 5[![Travis CI Build Status](https://travis-ci.org/citra-emu/citra.svg?branch=master)](https://travis-ci.org/citra-emu/citra)
6[![AppVeyor CI Build Status](https://ci.appveyor.com/api/projects/status/sdf1o4kh3g1e68m9?svg=true)](https://ci.appveyor.com/project/bunnei/citra) 6[![AppVeyor CI Build Status](https://ci.appveyor.com/api/projects/status/sdf1o4kh3g1e68m9?svg=true)](https://ci.appveyor.com/project/bunnei/citra)
7 7
8Citra is an experimental open-source Nintendo 3DS emulator/debugger written in C++. It is written with portability in mind, with builds actively maintained for Windows, Linux and OS X. Citra only emulates a subset of 3DS hardware, and therefore is generally only useful for running/debugging homebrew applications. At this time, Citra is even able to boot several commercial games! Most of these do not run to a playable state, but we are working every day to advance the project forward. 8Citra is an experimental open-source Nintendo 3DS emulator/debugger written in C++. It is written with portability in mind, with builds actively maintained for Windows, Linux and macOS. Citra only emulates a subset of 3DS hardware, and therefore is generally only useful for running/debugging homebrew applications. At this time, Citra is even able to boot several commercial games! Most of these do not run to a playable state, but we are working every day to advance the project forward.
9 9
10Citra is licensed under the GPLv2 (or any later version). Refer to the license.txt file included. Please read the [FAQ](https://github.com/citra-emu/citra/wiki/FAQ) before getting started with the project. 10Citra is licensed under the GPLv2 (or any later version). Refer to the license.txt file included. Please read the [FAQ](https://github.com/citra-emu/citra/wiki/FAQ) before getting started with the project.
11 11
@@ -23,7 +23,7 @@ If you want to contribute please take a look at the [Contributor's Guide](CONTRI
23 23
24* __Windows__: [Windows Build](https://github.com/citra-emu/citra/wiki/Building-For-Windows) 24* __Windows__: [Windows Build](https://github.com/citra-emu/citra/wiki/Building-For-Windows)
25* __Linux__: [Linux Build](https://github.com/citra-emu/citra/wiki/Building-For-Linux) 25* __Linux__: [Linux Build](https://github.com/citra-emu/citra/wiki/Building-For-Linux)
26* __OSX__: [OS X Build](https://github.com/citra-emu/citra/wiki/Building-For-OS-X) 26* __macOS__: [macOS Build](https://github.com/citra-emu/citra/wiki/Building-for-macOS)
27 27
28 28
29### Support 29### Support
diff --git a/dist/citra.xml b/dist/citra.xml
index bcb9acd87..6d47c8760 100644
--- a/dist/citra.xml
+++ b/dist/citra.xml
@@ -1,8 +1,8 @@
1<?xml version="1.0" encoding="UTF-8"?> 1<?xml version="1.0" encoding="UTF-8"?>
2<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info"> 2<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
3 <mime-type type="application/x-ctr-3dsx"> 3 <mime-type type="application/x-ctr-3dsx">
4 <comment>3DS homebrew executable</comment> 4 <comment>Nintendo 3DS homebrew executable</comment>
5 <comment xml:lang="fr">Exécutable 3DS homebrew</comment> 5 <comment xml:lang="fr">Exécutable non-officiel pour Nintendo 3DS </comment>
6 <acronym>3DSX</acronym> 6 <acronym>3DSX</acronym>
7 <icon name="citra"/> 7 <icon name="citra"/>
8 <glob pattern="*.3dsx"/> 8 <glob pattern="*.3dsx"/>
@@ -10,8 +10,8 @@
10 </mime-type> 10 </mime-type>
11 11
12 <mime-type type="application/x-ctr-cci"> 12 <mime-type type="application/x-ctr-cci">
13 <comment>3DS cartridge image</comment> 13 <comment>Nintendo 3DS cartridge image</comment>
14 <comment xml:lang="fr">Image de cartouche 3DS</comment> 14 <comment xml:lang="fr">Image de cartouche Nintendo 3DS</comment>
15 <acronym>CCI</acronym> 15 <acronym>CCI</acronym>
16 <expanded-acronym>CTR Cart Image</expanded-acronym> 16 <expanded-acronym>CTR Cart Image</expanded-acronym>
17 <icon name="citra"/> 17 <icon name="citra"/>
@@ -21,8 +21,8 @@
21 </mime-type> 21 </mime-type>
22 22
23 <mime-type type="application/x-ctr-cxi"> 23 <mime-type type="application/x-ctr-cxi">
24 <comment>3DS executable</comment> 24 <comment>Nintendo 3DS executable</comment>
25 <comment xml:lang="fr">Exécutable 3DS</comment> 25 <comment xml:lang="fr">Exécutable Nintendo 3DS</comment>
26 <acronym>CXI</acronym> 26 <acronym>CXI</acronym>
27 <expanded-acronym>CTR eXecutable Image</expanded-acronym> 27 <expanded-acronym>CTR eXecutable Image</expanded-acronym>
28 <icon name="citra"/> 28 <icon name="citra"/>
@@ -31,8 +31,8 @@
31 </mime-type> 31 </mime-type>
32 32
33 <mime-type type="application/x-ctr-cia"> 33 <mime-type type="application/x-ctr-cia">
34 <comment>3DS importable archive</comment> 34 <comment>Nintendo 3DS importable archive</comment>
35 <comment xml:lang="fr">Archive importable 3DS</comment> 35 <comment xml:lang="fr">Archive installable Nintendo 3DS</comment>
36 <acronym>CIA</acronym> 36 <acronym>CIA</acronym>
37 <expanded-acronym>CTR Importable Archive</expanded-acronym> 37 <expanded-acronym>CTR Importable Archive</expanded-acronym>
38 <icon name="citra"/> 38 <icon name="citra"/>
@@ -40,8 +40,8 @@
40 </mime-type> 40 </mime-type>
41 41
42 <mime-type type="application/x-ctr-smdh"> 42 <mime-type type="application/x-ctr-smdh">
43 <comment>3DS icon</comment> 43 <comment>Nintendo 3DS icon and metadata</comment>
44 <comment xml:lang="fr">Icône 3DS</comment> 44 <comment xml:lang="fr">Icône et métadonnées Nintendo 3DS</comment>
45 <acronym>SMDH</acronym> 45 <acronym>SMDH</acronym>
46 <expanded-acronym>System Menu Data Header</expanded-acronym> 46 <expanded-acronym>System Menu Data Header</expanded-acronym>
47 <glob pattern="*.smdh"/> 47 <glob pattern="*.smdh"/>
@@ -49,8 +49,8 @@
49 </mime-type> 49 </mime-type>
50 50
51 <mime-type type="application/x-ctr-cbmd"> 51 <mime-type type="application/x-ctr-cbmd">
52 <comment>3DS banner</comment> 52 <comment>Nintendo 3DS banner</comment>
53 <comment xml:lang="fr">Bannière 3DS</comment> 53 <comment xml:lang="fr">Bannière Nintendo 3DS</comment>
54 <acronym>CBMD</acronym> 54 <acronym>CBMD</acronym>
55 <expanded-acronym>CTR Banner Model Data</expanded-acronym> 55 <expanded-acronym>CTR Banner Model Data</expanded-acronym>
56 <glob pattern="*.cbmd"/> 56 <glob pattern="*.cbmd"/>
diff --git a/src/audio_core/sdl2_sink.cpp b/src/audio_core/sdl2_sink.cpp
index 75cc0d6dd..4b66cd826 100644
--- a/src/audio_core/sdl2_sink.cpp
+++ b/src/audio_core/sdl2_sink.cpp
@@ -25,7 +25,7 @@ struct SDL2Sink::Impl {
25 25
26SDL2Sink::SDL2Sink() : impl(std::make_unique<Impl>()) { 26SDL2Sink::SDL2Sink() : impl(std::make_unique<Impl>()) {
27 if (SDL_Init(SDL_INIT_AUDIO) < 0) { 27 if (SDL_Init(SDL_INIT_AUDIO) < 0) {
28 LOG_CRITICAL(Audio_Sink, "SDL_Init(SDL_INIT_AUDIO) failed"); 28 LOG_CRITICAL(Audio_Sink, "SDL_Init(SDL_INIT_AUDIO) failed with: %s", SDL_GetError());
29 impl->audio_device_id = 0; 29 impl->audio_device_id = 0;
30 return; 30 return;
31 } 31 }
@@ -45,7 +45,7 @@ SDL2Sink::SDL2Sink() : impl(std::make_unique<Impl>()) {
45 impl->audio_device_id = 45 impl->audio_device_id =
46 SDL_OpenAudioDevice(nullptr, false, &desired_audiospec, &obtained_audiospec, 0); 46 SDL_OpenAudioDevice(nullptr, false, &desired_audiospec, &obtained_audiospec, 0);
47 if (impl->audio_device_id <= 0) { 47 if (impl->audio_device_id <= 0) {
48 LOG_CRITICAL(Audio_Sink, "SDL_OpenAudioDevice failed"); 48 LOG_CRITICAL(Audio_Sink, "SDL_OpenAudioDevice failed with: %s", SDL_GetError());
49 return; 49 return;
50 } 50 }
51 51
diff --git a/src/citra/config.cpp b/src/citra/config.cpp
index fd30bfc85..fe74fcd72 100644
--- a/src/citra/config.cpp
+++ b/src/citra/config.cpp
@@ -59,7 +59,6 @@ void Config::ReadValues() {
59 59
60 // Core 60 // Core
61 Settings::values.use_cpu_jit = sdl2_config->GetBoolean("Core", "use_cpu_jit", true); 61 Settings::values.use_cpu_jit = sdl2_config->GetBoolean("Core", "use_cpu_jit", true);
62 Settings::values.frame_skip = sdl2_config->GetInteger("Core", "frame_skip", 0);
63 62
64 // Renderer 63 // Renderer
65 Settings::values.use_hw_renderer = sdl2_config->GetBoolean("Renderer", "use_hw_renderer", true); 64 Settings::values.use_hw_renderer = sdl2_config->GetBoolean("Renderer", "use_hw_renderer", true);
diff --git a/src/citra/default_ini.h b/src/citra/default_ini.h
index b22627a2f..b98dc4d83 100644
--- a/src/citra/default_ini.h
+++ b/src/citra/default_ini.h
@@ -42,10 +42,6 @@ pad_circle_modifier_scale =
42# 0: Interpreter (slow), 1 (default): JIT (fast) 42# 0: Interpreter (slow), 1 (default): JIT (fast)
43use_cpu_jit = 43use_cpu_jit =
44 44
45# The applied frameskip amount. Must be a power of two.
46# 0 (default): No frameskip, 1: x2 frameskip, 2: x4 frameskip, 3: x8 frameskip, etc.
47frame_skip =
48
49[Renderer] 45[Renderer]
50# Whether to use software or hardware rendering. 46# Whether to use software or hardware rendering.
51# 0: Software, 1 (default): Hardware 47# 0: Software, 1 (default): Hardware
diff --git a/src/citra_qt/config.cpp b/src/citra_qt/config.cpp
index 3d2312619..3cdfe6443 100644
--- a/src/citra_qt/config.cpp
+++ b/src/citra_qt/config.cpp
@@ -39,7 +39,6 @@ void Config::ReadValues() {
39 39
40 qt_config->beginGroup("Core"); 40 qt_config->beginGroup("Core");
41 Settings::values.use_cpu_jit = qt_config->value("use_cpu_jit", true).toBool(); 41 Settings::values.use_cpu_jit = qt_config->value("use_cpu_jit", true).toBool();
42 Settings::values.frame_skip = qt_config->value("frame_skip", 0).toInt();
43 qt_config->endGroup(); 42 qt_config->endGroup();
44 43
45 qt_config->beginGroup("Renderer"); 44 qt_config->beginGroup("Renderer");
@@ -146,7 +145,6 @@ void Config::SaveValues() {
146 145
147 qt_config->beginGroup("Core"); 146 qt_config->beginGroup("Core");
148 qt_config->setValue("use_cpu_jit", Settings::values.use_cpu_jit); 147 qt_config->setValue("use_cpu_jit", Settings::values.use_cpu_jit);
149 qt_config->setValue("frame_skip", Settings::values.frame_skip);
150 qt_config->endGroup(); 148 qt_config->endGroup();
151 149
152 qt_config->beginGroup("Renderer"); 150 qt_config->beginGroup("Renderer");
diff --git a/src/common/common_paths.h b/src/common/common_paths.h
index a5342a610..37304d236 100644
--- a/src/common/common_paths.h
+++ b/src/common/common_paths.h
@@ -19,7 +19,7 @@
19#define EMU_DATA_DIR USER_DIR 19#define EMU_DATA_DIR USER_DIR
20#else 20#else
21#ifdef _WIN32 21#ifdef _WIN32
22#define EMU_DATA_DIR "Citra Emulator" 22#define EMU_DATA_DIR "Citra"
23#else 23#else
24#define EMU_DATA_DIR "citra-emu" 24#define EMU_DATA_DIR "citra-emu"
25#endif 25#endif
diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp
index 407ed047a..413a8e7e5 100644
--- a/src/common/file_util.cpp
+++ b/src/common/file_util.cpp
@@ -26,6 +26,9 @@
26#define stat _stat64 26#define stat _stat64
27#define fstat _fstat64 27#define fstat _fstat64
28#define fileno _fileno 28#define fileno _fileno
29// Windows version, at least Vista is required to obtain AppData Path
30#define WINVER 0x0600
31#define _WIN32_WINNT 0x0600
29#else 32#else
30#ifdef __APPLE__ 33#ifdef __APPLE__
31#include <sys/param.h> 34#include <sys/param.h>
@@ -594,6 +597,15 @@ std::string& GetExeDirectory() {
594 } 597 }
595 return exe_path; 598 return exe_path;
596} 599}
600
601std::string AppDataRoamingDirectory() {
602 PWSTR pw_local_path = nullptr;
603 // Only supported by Windows Vista or later
604 SHGetKnownFolderPath(FOLDERID_RoamingAppData, 0, nullptr, &pw_local_path);
605 std::string local_path = Common::UTF16ToUTF8(pw_local_path);
606 CoTaskMemFree(pw_local_path);
607 return local_path;
608}
597#else 609#else
598/** 610/**
599 * @return The user’s home directory on POSIX systems 611 * @return The user’s home directory on POSIX systems
@@ -671,6 +683,10 @@ const std::string& GetUserPath(const unsigned int DirIDX, const std::string& new
671 if (paths[D_USER_IDX].empty()) { 683 if (paths[D_USER_IDX].empty()) {
672#ifdef _WIN32 684#ifdef _WIN32
673 paths[D_USER_IDX] = GetExeDirectory() + DIR_SEP USERDATA_DIR DIR_SEP; 685 paths[D_USER_IDX] = GetExeDirectory() + DIR_SEP USERDATA_DIR DIR_SEP;
686 if (!FileUtil::IsDirectory(paths[D_USER_IDX])) {
687 paths[D_USER_IDX] = AppDataRoamingDirectory() + DIR_SEP EMU_DATA_DIR DIR_SEP;
688 }
689
674 paths[D_CONFIG_IDX] = paths[D_USER_IDX] + CONFIG_DIR DIR_SEP; 690 paths[D_CONFIG_IDX] = paths[D_USER_IDX] + CONFIG_DIR DIR_SEP;
675 paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP; 691 paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP;
676#else 692#else
diff --git a/src/common/file_util.h b/src/common/file_util.h
index 204b06f14..ac58607c5 100644
--- a/src/common/file_util.h
+++ b/src/common/file_util.h
@@ -154,6 +154,7 @@ std::string GetBundleDirectory();
154 154
155#ifdef _WIN32 155#ifdef _WIN32
156std::string& GetExeDirectory(); 156std::string& GetExeDirectory();
157std::string AppDataRoamingDirectory();
157#endif 158#endif
158 159
159size_t WriteStringToFile(bool text_file, const std::string& str, const char* filename); 160size_t WriteStringToFile(bool text_file, const std::string& str, const char* filename);
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index 88209081d..7fd397fe5 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -43,6 +43,7 @@ namespace Log {
43 SUB(Service, AM) \ 43 SUB(Service, AM) \
44 SUB(Service, PTM) \ 44 SUB(Service, PTM) \
45 SUB(Service, LDR) \ 45 SUB(Service, LDR) \
46 SUB(Service, MIC) \
46 SUB(Service, NDM) \ 47 SUB(Service, NDM) \
47 SUB(Service, NIM) \ 48 SUB(Service, NIM) \
48 SUB(Service, NWM) \ 49 SUB(Service, NWM) \
diff --git a/src/common/logging/log.h b/src/common/logging/log.h
index 8d3a2d03e..8011534b8 100644
--- a/src/common/logging/log.h
+++ b/src/common/logging/log.h
@@ -60,6 +60,7 @@ enum class Class : ClassType {
60 Service_AM, ///< The AM (Application manager) service 60 Service_AM, ///< The AM (Application manager) service
61 Service_PTM, ///< The PTM (Power status & misc.) service 61 Service_PTM, ///< The PTM (Power status & misc.) service
62 Service_LDR, ///< The LDR (3ds dll loader) service 62 Service_LDR, ///< The LDR (3ds dll loader) service
63 Service_MIC, ///< The MIC (microphone) service
63 Service_NDM, ///< The NDM (Network daemon manager) service 64 Service_NDM, ///< The NDM (Network daemon manager) service
64 Service_NIM, ///< The NIM (Network interface manager) service 65 Service_NIM, ///< The NIM (Network interface manager) service
65 Service_NWM, ///< The NWM (Network wlan manager) service 66 Service_NWM, ///< The NWM (Network wlan manager) service
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 4a9c6fd2f..299f1f261 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -17,13 +17,16 @@ set(SRCS
17 core_timing.cpp 17 core_timing.cpp
18 file_sys/archive_backend.cpp 18 file_sys/archive_backend.cpp
19 file_sys/archive_extsavedata.cpp 19 file_sys/archive_extsavedata.cpp
20 file_sys/archive_ncch.cpp
20 file_sys/archive_romfs.cpp 21 file_sys/archive_romfs.cpp
21 file_sys/archive_savedata.cpp 22 file_sys/archive_savedata.cpp
22 file_sys/archive_savedatacheck.cpp
23 file_sys/archive_sdmc.cpp 23 file_sys/archive_sdmc.cpp
24 file_sys/archive_sdmcwriteonly.cpp
24 file_sys/archive_systemsavedata.cpp 25 file_sys/archive_systemsavedata.cpp
25 file_sys/disk_archive.cpp 26 file_sys/disk_archive.cpp
26 file_sys/ivfc_archive.cpp 27 file_sys/ivfc_archive.cpp
28 file_sys/path_parser.cpp
29 file_sys/savedata_archive.cpp
27 gdbstub/gdbstub.cpp 30 gdbstub/gdbstub.cpp
28 hle/config_mem.cpp 31 hle/config_mem.cpp
29 hle/hle.cpp 32 hle/hle.cpp
@@ -159,15 +162,18 @@ set(HEADERS
159 core_timing.h 162 core_timing.h
160 file_sys/archive_backend.h 163 file_sys/archive_backend.h
161 file_sys/archive_extsavedata.h 164 file_sys/archive_extsavedata.h
165 file_sys/archive_ncch.h
162 file_sys/archive_romfs.h 166 file_sys/archive_romfs.h
163 file_sys/archive_savedata.h 167 file_sys/archive_savedata.h
164 file_sys/archive_savedatacheck.h
165 file_sys/archive_sdmc.h 168 file_sys/archive_sdmc.h
169 file_sys/archive_sdmcwriteonly.h
166 file_sys/archive_systemsavedata.h 170 file_sys/archive_systemsavedata.h
167 file_sys/directory_backend.h 171 file_sys/directory_backend.h
168 file_sys/disk_archive.h 172 file_sys/disk_archive.h
169 file_sys/file_backend.h 173 file_sys/file_backend.h
170 file_sys/ivfc_archive.h 174 file_sys/ivfc_archive.h
175 file_sys/path_parser.h
176 file_sys/savedata_archive.h
171 gdbstub/gdbstub.h 177 gdbstub/gdbstub.h
172 hle/config_mem.h 178 hle/config_mem.h
173 hle/function_wrappers.h 179 hle/function_wrappers.h
diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp
index b4444c869..ca8a94ee9 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic.cpp
@@ -52,6 +52,7 @@ static Dynarmic::UserCallbacks GetUserCallbacks(ARMul_State* interpeter_state) {
52 user_callbacks.MemoryWrite16 = &Memory::Write16; 52 user_callbacks.MemoryWrite16 = &Memory::Write16;
53 user_callbacks.MemoryWrite32 = &Memory::Write32; 53 user_callbacks.MemoryWrite32 = &Memory::Write32;
54 user_callbacks.MemoryWrite64 = &Memory::Write64; 54 user_callbacks.MemoryWrite64 = &Memory::Write64;
55 user_callbacks.page_table = Memory::GetCurrentPageTablePointers();
55 return user_callbacks; 56 return user_callbacks;
56} 57}
57 58
@@ -130,9 +131,9 @@ MICROPROFILE_DEFINE(ARM_Jit, "ARM JIT", "ARM JIT", MP_RGB(255, 64, 64));
130void ARM_Dynarmic::ExecuteInstructions(int num_instructions) { 131void ARM_Dynarmic::ExecuteInstructions(int num_instructions) {
131 MICROPROFILE_SCOPE(ARM_Jit); 132 MICROPROFILE_SCOPE(ARM_Jit);
132 133
133 jit->Run(static_cast<unsigned>(num_instructions)); 134 unsigned ticks_executed = jit->Run(static_cast<unsigned>(num_instructions));
134 135
135 AddTicks(num_instructions); 136 AddTicks(ticks_executed);
136} 137}
137 138
138void ARM_Dynarmic::SaveContext(Core::ThreadContext& ctx) { 139void ARM_Dynarmic::SaveContext(Core::ThreadContext& ctx) {
diff --git a/src/core/file_sys/archive_backend.h b/src/core/file_sys/archive_backend.h
index 06b8f2ed7..58f6c150c 100644
--- a/src/core/file_sys/archive_backend.h
+++ b/src/core/file_sys/archive_backend.h
@@ -87,7 +87,7 @@ public:
87 * @return Opened file, or error code 87 * @return Opened file, or error code
88 */ 88 */
89 virtual ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path, 89 virtual ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path,
90 const Mode mode) const = 0; 90 const Mode& mode) const = 0;
91 91
92 /** 92 /**
93 * Delete a file specified by its path 93 * Delete a file specified by its path
@@ -100,53 +100,53 @@ public:
100 * Rename a File specified by its path 100 * Rename a File specified by its path
101 * @param src_path Source path relative to the archive 101 * @param src_path Source path relative to the archive
102 * @param dest_path Destination path relative to the archive 102 * @param dest_path Destination path relative to the archive
103 * @return Whether rename succeeded 103 * @return Result of the operation
104 */ 104 */
105 virtual bool RenameFile(const Path& src_path, const Path& dest_path) const = 0; 105 virtual ResultCode RenameFile(const Path& src_path, const Path& dest_path) const = 0;
106 106
107 /** 107 /**
108 * Delete a directory specified by its path 108 * Delete a directory specified by its path
109 * @param path Path relative to the archive 109 * @param path Path relative to the archive
110 * @return Whether the directory could be deleted 110 * @return Result of the operation
111 */ 111 */
112 virtual bool DeleteDirectory(const Path& path) const = 0; 112 virtual ResultCode DeleteDirectory(const Path& path) const = 0;
113 113
114 /** 114 /**
115 * Delete a directory specified by its path and anything under it 115 * Delete a directory specified by its path and anything under it
116 * @param path Path relative to the archive 116 * @param path Path relative to the archive
117 * @return Whether the directory could be deleted 117 * @return Result of the operation
118 */ 118 */
119 virtual bool DeleteDirectoryRecursively(const Path& path) const = 0; 119 virtual ResultCode DeleteDirectoryRecursively(const Path& path) const = 0;
120 120
121 /** 121 /**
122 * Create a file specified by its path 122 * Create a file specified by its path
123 * @param path Path relative to the Archive 123 * @param path Path relative to the Archive
124 * @param size The size of the new file, filled with zeroes 124 * @param size The size of the new file, filled with zeroes
125 * @return File creation result code 125 * @return Result of the operation
126 */ 126 */
127 virtual ResultCode CreateFile(const Path& path, u64 size) const = 0; 127 virtual ResultCode CreateFile(const Path& path, u64 size) const = 0;
128 128
129 /** 129 /**
130 * Create a directory specified by its path 130 * Create a directory specified by its path
131 * @param path Path relative to the archive 131 * @param path Path relative to the archive
132 * @return Whether the directory could be created 132 * @return Result of the operation
133 */ 133 */
134 virtual bool CreateDirectory(const Path& path) const = 0; 134 virtual ResultCode CreateDirectory(const Path& path) const = 0;
135 135
136 /** 136 /**
137 * Rename a Directory specified by its path 137 * Rename a Directory specified by its path
138 * @param src_path Source path relative to the archive 138 * @param src_path Source path relative to the archive
139 * @param dest_path Destination path relative to the archive 139 * @param dest_path Destination path relative to the archive
140 * @return Whether rename succeeded 140 * @return Result of the operation
141 */ 141 */
142 virtual bool RenameDirectory(const Path& src_path, const Path& dest_path) const = 0; 142 virtual ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const = 0;
143 143
144 /** 144 /**
145 * Open a directory specified by its path 145 * Open a directory specified by its path
146 * @param path Path relative to the archive 146 * @param path Path relative to the archive
147 * @return Opened directory, or nullptr 147 * @return Opened directory, or error code
148 */ 148 */
149 virtual std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const = 0; 149 virtual ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const = 0;
150 150
151 /** 151 /**
152 * Get the free space 152 * Get the free space
diff --git a/src/core/file_sys/archive_extsavedata.cpp b/src/core/file_sys/archive_extsavedata.cpp
index e1d29efd3..e1c4931ec 100644
--- a/src/core/file_sys/archive_extsavedata.cpp
+++ b/src/core/file_sys/archive_extsavedata.cpp
@@ -11,6 +11,9 @@
11#include "common/string_util.h" 11#include "common/string_util.h"
12#include "core/file_sys/archive_extsavedata.h" 12#include "core/file_sys/archive_extsavedata.h"
13#include "core/file_sys/disk_archive.h" 13#include "core/file_sys/disk_archive.h"
14#include "core/file_sys/errors.h"
15#include "core/file_sys/path_parser.h"
16#include "core/file_sys/savedata_archive.h"
14#include "core/hle/service/fs/archive.h" 17#include "core/hle/service/fs/archive.h"
15 18
16//////////////////////////////////////////////////////////////////////////////////////////////////// 19////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -18,6 +21,116 @@
18 21
19namespace FileSys { 22namespace FileSys {
20 23
24/**
25 * A modified version of DiskFile for fixed-size file used by ExtSaveData
26 * The file size can't be changed by SetSize or Write.
27 */
28class FixSizeDiskFile : public DiskFile {
29public:
30 FixSizeDiskFile(FileUtil::IOFile&& file, const Mode& mode) : DiskFile(std::move(file), mode) {
31 size = GetSize();
32 }
33
34 bool SetSize(u64 size) const override {
35 return false;
36 }
37
38 ResultVal<size_t> Write(u64 offset, size_t length, bool flush,
39 const u8* buffer) const override {
40 if (offset > size) {
41 return ResultCode(ErrorDescription::FS_WriteBeyondEnd, ErrorModule::FS,
42 ErrorSummary::InvalidArgument, ErrorLevel::Usage);
43 } else if (offset == size) {
44 return MakeResult<size_t>(0);
45 }
46
47 if (offset + length > size) {
48 length = size - offset;
49 }
50
51 return DiskFile::Write(offset, length, flush, buffer);
52 }
53
54private:
55 u64 size{};
56};
57
58/**
59 * Archive backend for general extsave data archive type.
60 * The behaviour of ExtSaveDataArchive is almost the same as SaveDataArchive, except for
61 * - file size can't be changed once created (thus creating zero-size file and openning with create
62 * flag are prohibited);
63 * - always open a file with read+write permission.
64 */
65class ExtSaveDataArchive : public SaveDataArchive {
66public:
67 ExtSaveDataArchive(const std::string& mount_point) : SaveDataArchive(mount_point) {}
68
69 std::string GetName() const override {
70 return "ExtSaveDataArchive: " + mount_point;
71 }
72
73 ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path,
74 const Mode& mode) const override {
75 LOG_DEBUG(Service_FS, "called path=%s mode=%01X", path.DebugStr().c_str(), mode.hex);
76
77 const PathParser path_parser(path);
78
79 if (!path_parser.IsValid()) {
80 LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
81 return ERROR_INVALID_PATH;
82 }
83
84 if (mode.hex == 0) {
85 LOG_ERROR(Service_FS, "Empty open mode");
86 return ERROR_UNSUPPORTED_OPEN_FLAGS;
87 }
88
89 if (mode.create_flag) {
90 LOG_ERROR(Service_FS, "Create flag is not supported");
91 return ERROR_UNSUPPORTED_OPEN_FLAGS;
92 }
93
94 const auto full_path = path_parser.BuildHostPath(mount_point);
95
96 switch (path_parser.GetHostStatus(mount_point)) {
97 case PathParser::InvalidMountPoint:
98 LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
99 return ERROR_FILE_NOT_FOUND;
100 case PathParser::PathNotFound:
101 LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str());
102 return ERROR_PATH_NOT_FOUND;
103 case PathParser::FileInPath:
104 case PathParser::DirectoryFound:
105 LOG_ERROR(Service_FS, "Unexpected file or directory in %s", full_path.c_str());
106 return ERROR_UNEXPECTED_FILE_OR_DIRECTORY;
107 case PathParser::NotFound:
108 LOG_ERROR(Service_FS, "%s not found", full_path.c_str());
109 return ERROR_FILE_NOT_FOUND;
110 }
111
112 FileUtil::IOFile file(full_path, "r+b");
113 if (!file.IsOpen()) {
114 LOG_CRITICAL(Service_FS, "(unreachable) Unknown error opening %s", full_path.c_str());
115 return ERROR_FILE_NOT_FOUND;
116 }
117
118 Mode rwmode;
119 rwmode.write_flag.Assign(1);
120 rwmode.read_flag.Assign(1);
121 auto disk_file = std::make_unique<FixSizeDiskFile>(std::move(file), rwmode);
122 return MakeResult<std::unique_ptr<FileBackend>>(std::move(disk_file));
123 }
124
125 ResultCode CreateFile(const Path& path, u64 size) const override {
126 if (size == 0) {
127 LOG_ERROR(Service_FS, "Zero-size file is not supported");
128 return ERROR_UNSUPPORTED_OPEN_FLAGS;
129 }
130 return SaveDataArchive::CreateFile(path, size);
131 }
132};
133
21std::string GetExtSaveDataPath(const std::string& mount_point, const Path& path) { 134std::string GetExtSaveDataPath(const std::string& mount_point, const Path& path) {
22 std::vector<u8> vec_data = path.AsBinary(); 135 std::vector<u8> vec_data = path.AsBinary();
23 const u32* data = reinterpret_cast<const u32*>(vec_data.data()); 136 const u32* data = reinterpret_cast<const u32*>(vec_data.data());
@@ -84,7 +197,7 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_ExtSaveData::Open(cons
84 ErrorSummary::InvalidState, ErrorLevel::Status); 197 ErrorSummary::InvalidState, ErrorLevel::Status);
85 } 198 }
86 } 199 }
87 auto archive = std::make_unique<DiskArchive>(fullpath); 200 auto archive = std::make_unique<ExtSaveDataArchive>(fullpath);
88 return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); 201 return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
89} 202}
90 203
diff --git a/src/core/file_sys/archive_savedatacheck.cpp b/src/core/file_sys/archive_ncch.cpp
index 6c4542b7d..6f1aadfc3 100644
--- a/src/core/file_sys/archive_savedatacheck.cpp
+++ b/src/core/file_sys/archive_ncch.cpp
@@ -9,7 +9,7 @@
9#include "common/file_util.h" 9#include "common/file_util.h"
10#include "common/logging/log.h" 10#include "common/logging/log.h"
11#include "common/string_util.h" 11#include "common/string_util.h"
12#include "core/file_sys/archive_savedatacheck.h" 12#include "core/file_sys/archive_ncch.h"
13#include "core/file_sys/ivfc_archive.h" 13#include "core/file_sys/ivfc_archive.h"
14#include "core/hle/service/fs/archive.h" 14#include "core/hle/service/fs/archive.h"
15 15
@@ -18,22 +18,22 @@
18 18
19namespace FileSys { 19namespace FileSys {
20 20
21static std::string GetSaveDataCheckContainerPath(const std::string& nand_directory) { 21static std::string GetNCCHContainerPath(const std::string& nand_directory) {
22 return Common::StringFromFormat("%s%s/title/", nand_directory.c_str(), SYSTEM_ID.c_str()); 22 return Common::StringFromFormat("%s%s/title/", nand_directory.c_str(), SYSTEM_ID.c_str());
23} 23}
24 24
25static std::string GetSaveDataCheckPath(const std::string& mount_point, u32 high, u32 low) { 25static std::string GetNCCHPath(const std::string& mount_point, u32 high, u32 low) {
26 return Common::StringFromFormat("%s%08x/%08x/content/00000000.app.romfs", mount_point.c_str(), 26 return Common::StringFromFormat("%s%08x/%08x/content/00000000.app.romfs", mount_point.c_str(),
27 high, low); 27 high, low);
28} 28}
29 29
30ArchiveFactory_SaveDataCheck::ArchiveFactory_SaveDataCheck(const std::string& nand_directory) 30ArchiveFactory_NCCH::ArchiveFactory_NCCH(const std::string& nand_directory)
31 : mount_point(GetSaveDataCheckContainerPath(nand_directory)) {} 31 : mount_point(GetNCCHContainerPath(nand_directory)) {}
32 32
33ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SaveDataCheck::Open(const Path& path) { 33ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_NCCH::Open(const Path& path) {
34 auto vec = path.AsBinary(); 34 auto vec = path.AsBinary();
35 const u32* data = reinterpret_cast<u32*>(vec.data()); 35 const u32* data = reinterpret_cast<u32*>(vec.data());
36 std::string file_path = GetSaveDataCheckPath(mount_point, data[1], data[0]); 36 std::string file_path = GetNCCHPath(mount_point, data[1], data[0]);
37 auto file = std::make_shared<FileUtil::IOFile>(file_path, "rb"); 37 auto file = std::make_shared<FileUtil::IOFile>(file_path, "rb");
38 38
39 if (!file->IsOpen()) { 39 if (!file->IsOpen()) {
@@ -45,15 +45,15 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SaveDataCheck::Open(co
45 return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); 45 return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
46} 46}
47 47
48ResultCode ArchiveFactory_SaveDataCheck::Format(const Path& path, 48ResultCode ArchiveFactory_NCCH::Format(const Path& path,
49 const FileSys::ArchiveFormatInfo& format_info) { 49 const FileSys::ArchiveFormatInfo& format_info) {
50 LOG_ERROR(Service_FS, "Attempted to format a SaveDataCheck archive."); 50 LOG_ERROR(Service_FS, "Attempted to format a NCCH archive.");
51 // TODO: Verify error code 51 // TODO: Verify error code
52 return ResultCode(ErrorDescription::NotAuthorized, ErrorModule::FS, ErrorSummary::NotSupported, 52 return ResultCode(ErrorDescription::NotAuthorized, ErrorModule::FS, ErrorSummary::NotSupported,
53 ErrorLevel::Permanent); 53 ErrorLevel::Permanent);
54} 54}
55 55
56ResultVal<ArchiveFormatInfo> ArchiveFactory_SaveDataCheck::GetFormatInfo(const Path& path) const { 56ResultVal<ArchiveFormatInfo> ArchiveFactory_NCCH::GetFormatInfo(const Path& path) const {
57 // TODO(Subv): Implement 57 // TODO(Subv): Implement
58 LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive %s", GetName().c_str()); 58 LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive %s", GetName().c_str());
59 return ResultCode(-1); 59 return ResultCode(-1);
diff --git a/src/core/file_sys/archive_savedatacheck.h b/src/core/file_sys/archive_ncch.h
index e9cafbed9..66b8ce75d 100644
--- a/src/core/file_sys/archive_savedatacheck.h
+++ b/src/core/file_sys/archive_ncch.h
@@ -14,13 +14,13 @@
14 14
15namespace FileSys { 15namespace FileSys {
16 16
17/// File system interface to the SaveDataCheck archive 17/// File system interface to the NCCH archive
18class ArchiveFactory_SaveDataCheck final : public ArchiveFactory { 18class ArchiveFactory_NCCH final : public ArchiveFactory {
19public: 19public:
20 ArchiveFactory_SaveDataCheck(const std::string& mount_point); 20 ArchiveFactory_NCCH(const std::string& mount_point);
21 21
22 std::string GetName() const override { 22 std::string GetName() const override {
23 return "SaveDataCheck"; 23 return "NCCH";
24 } 24 }
25 25
26 ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override; 26 ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override;
diff --git a/src/core/file_sys/archive_savedata.cpp b/src/core/file_sys/archive_savedata.cpp
index 6711035ec..ecb44a215 100644
--- a/src/core/file_sys/archive_savedata.cpp
+++ b/src/core/file_sys/archive_savedata.cpp
@@ -9,7 +9,7 @@
9#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "common/string_util.h" 10#include "common/string_util.h"
11#include "core/file_sys/archive_savedata.h" 11#include "core/file_sys/archive_savedata.h"
12#include "core/file_sys/disk_archive.h" 12#include "core/file_sys/savedata_archive.h"
13#include "core/hle/kernel/process.h" 13#include "core/hle/kernel/process.h"
14#include "core/hle/service/fs/archive.h" 14#include "core/hle/service/fs/archive.h"
15 15
@@ -54,7 +54,7 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SaveData::Open(const P
54 ErrorSummary::InvalidState, ErrorLevel::Status); 54 ErrorSummary::InvalidState, ErrorLevel::Status);
55 } 55 }
56 56
57 auto archive = std::make_unique<DiskArchive>(std::move(concrete_mount_point)); 57 auto archive = std::make_unique<SaveDataArchive>(std::move(concrete_mount_point));
58 return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); 58 return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
59} 59}
60 60
diff --git a/src/core/file_sys/archive_sdmc.cpp b/src/core/file_sys/archive_sdmc.cpp
index bcb03ed36..333dfb92e 100644
--- a/src/core/file_sys/archive_sdmc.cpp
+++ b/src/core/file_sys/archive_sdmc.cpp
@@ -8,6 +8,8 @@
8#include "common/logging/log.h" 8#include "common/logging/log.h"
9#include "core/file_sys/archive_sdmc.h" 9#include "core/file_sys/archive_sdmc.h"
10#include "core/file_sys/disk_archive.h" 10#include "core/file_sys/disk_archive.h"
11#include "core/file_sys/errors.h"
12#include "core/file_sys/path_parser.h"
11#include "core/settings.h" 13#include "core/settings.h"
12 14
13//////////////////////////////////////////////////////////////////////////////////////////////////// 15////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -15,6 +17,281 @@
15 17
16namespace FileSys { 18namespace FileSys {
17 19
20ResultVal<std::unique_ptr<FileBackend>> SDMCArchive::OpenFile(const Path& path,
21 const Mode& mode) const {
22 Mode modified_mode;
23 modified_mode.hex = mode.hex;
24
25 // SDMC archive always opens a file with at least read permission
26 modified_mode.read_flag.Assign(1);
27
28 return OpenFileBase(path, modified_mode);
29}
30
31ResultVal<std::unique_ptr<FileBackend>> SDMCArchive::OpenFileBase(const Path& path,
32 const Mode& mode) const {
33 LOG_DEBUG(Service_FS, "called path=%s mode=%01X", path.DebugStr().c_str(), mode.hex);
34
35 const PathParser path_parser(path);
36
37 if (!path_parser.IsValid()) {
38 LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
39 return ERROR_INVALID_PATH;
40 }
41
42 if (mode.hex == 0) {
43 LOG_ERROR(Service_FS, "Empty open mode");
44 return ERROR_INVALID_OPEN_FLAGS;
45 }
46
47 if (mode.create_flag && !mode.write_flag) {
48 LOG_ERROR(Service_FS, "Create flag set but write flag not set");
49 return ERROR_INVALID_OPEN_FLAGS;
50 }
51
52 const auto full_path = path_parser.BuildHostPath(mount_point);
53
54 switch (path_parser.GetHostStatus(mount_point)) {
55 case PathParser::InvalidMountPoint:
56 LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
57 return ERROR_NOT_FOUND;
58 case PathParser::PathNotFound:
59 case PathParser::FileInPath:
60 LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str());
61 return ERROR_NOT_FOUND;
62 case PathParser::DirectoryFound:
63 LOG_ERROR(Service_FS, "%s is not a file", full_path.c_str());
64 return ERROR_UNEXPECTED_FILE_OR_DIRECTORY_SDMC;
65 case PathParser::NotFound:
66 if (!mode.create_flag) {
67 LOG_ERROR(Service_FS, "Non-existing file %s can't be open without mode create.",
68 full_path.c_str());
69 return ERROR_NOT_FOUND;
70 } else {
71 // Create the file
72 FileUtil::CreateEmptyFile(full_path);
73 }
74 break;
75 }
76
77 FileUtil::IOFile file(full_path, mode.write_flag ? "r+b" : "rb");
78 if (!file.IsOpen()) {
79 LOG_CRITICAL(Service_FS, "(unreachable) Unknown error opening %s", full_path.c_str());
80 return ERROR_NOT_FOUND;
81 }
82
83 auto disk_file = std::make_unique<DiskFile>(std::move(file), mode);
84 return MakeResult<std::unique_ptr<FileBackend>>(std::move(disk_file));
85}
86
87ResultCode SDMCArchive::DeleteFile(const Path& path) const {
88 const PathParser path_parser(path);
89
90 if (!path_parser.IsValid()) {
91 LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
92 return ERROR_INVALID_PATH;
93 }
94
95 const auto full_path = path_parser.BuildHostPath(mount_point);
96
97 switch (path_parser.GetHostStatus(mount_point)) {
98 case PathParser::InvalidMountPoint:
99 LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
100 return ERROR_NOT_FOUND;
101 case PathParser::PathNotFound:
102 case PathParser::FileInPath:
103 case PathParser::NotFound:
104 LOG_ERROR(Service_FS, "%s not found", full_path.c_str());
105 return ERROR_NOT_FOUND;
106 case PathParser::DirectoryFound:
107 LOG_ERROR(Service_FS, "%s is not a file", full_path.c_str());
108 return ERROR_UNEXPECTED_FILE_OR_DIRECTORY_SDMC;
109 }
110
111 if (FileUtil::Delete(full_path)) {
112 return RESULT_SUCCESS;
113 }
114
115 LOG_CRITICAL(Service_FS, "(unreachable) Unknown error deleting %s", full_path.c_str());
116 return ERROR_NOT_FOUND;
117}
118
119ResultCode SDMCArchive::RenameFile(const Path& src_path, const Path& dest_path) const {
120 if (FileUtil::Rename(mount_point + src_path.AsString(), mount_point + dest_path.AsString())) {
121 return RESULT_SUCCESS;
122 }
123
124 // TODO(yuriks): This code probably isn't right, it'll return a Status even if the file didn't
125 // exist or similar. Verify.
126 return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
127 ErrorSummary::NothingHappened, ErrorLevel::Status);
128}
129
130template <typename T>
131static ResultCode DeleteDirectoryHelper(const Path& path, const std::string& mount_point,
132 T deleter) {
133 const PathParser path_parser(path);
134
135 if (!path_parser.IsValid()) {
136 LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
137 return ERROR_INVALID_PATH;
138 }
139
140 if (path_parser.IsRootDirectory())
141 return ERROR_NOT_FOUND;
142
143 const auto full_path = path_parser.BuildHostPath(mount_point);
144
145 switch (path_parser.GetHostStatus(mount_point)) {
146 case PathParser::InvalidMountPoint:
147 LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
148 return ERROR_NOT_FOUND;
149 case PathParser::PathNotFound:
150 case PathParser::NotFound:
151 LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str());
152 return ERROR_NOT_FOUND;
153 case PathParser::FileInPath:
154 case PathParser::FileFound:
155 LOG_ERROR(Service_FS, "Unexpected file in path %s", full_path.c_str());
156 return ERROR_UNEXPECTED_FILE_OR_DIRECTORY_SDMC;
157 }
158
159 if (deleter(full_path)) {
160 return RESULT_SUCCESS;
161 }
162
163 LOG_ERROR(Service_FS, "Directory not empty %s", full_path.c_str());
164 return ERROR_UNEXPECTED_FILE_OR_DIRECTORY_SDMC;
165}
166
167ResultCode SDMCArchive::DeleteDirectory(const Path& path) const {
168 return DeleteDirectoryHelper(path, mount_point, FileUtil::DeleteDir);
169}
170
171ResultCode SDMCArchive::DeleteDirectoryRecursively(const Path& path) const {
172 return DeleteDirectoryHelper(
173 path, mount_point, [](const std::string& p) { return FileUtil::DeleteDirRecursively(p); });
174}
175
176ResultCode SDMCArchive::CreateFile(const FileSys::Path& path, u64 size) const {
177 const PathParser path_parser(path);
178
179 if (!path_parser.IsValid()) {
180 LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
181 return ERROR_INVALID_PATH;
182 }
183
184 const auto full_path = path_parser.BuildHostPath(mount_point);
185
186 switch (path_parser.GetHostStatus(mount_point)) {
187 case PathParser::InvalidMountPoint:
188 LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
189 return ERROR_NOT_FOUND;
190 case PathParser::PathNotFound:
191 case PathParser::FileInPath:
192 LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str());
193 return ERROR_NOT_FOUND;
194 case PathParser::DirectoryFound:
195 LOG_ERROR(Service_FS, "%s already exists", full_path.c_str());
196 return ERROR_UNEXPECTED_FILE_OR_DIRECTORY_SDMC;
197 case PathParser::FileFound:
198 LOG_ERROR(Service_FS, "%s already exists", full_path.c_str());
199 return ERROR_ALREADY_EXISTS;
200 }
201
202 if (size == 0) {
203 FileUtil::CreateEmptyFile(full_path);
204 return RESULT_SUCCESS;
205 }
206
207 FileUtil::IOFile file(full_path, "wb");
208 // Creates a sparse file (or a normal file on filesystems without the concept of sparse files)
209 // We do this by seeking to the right size, then writing a single null byte.
210 if (file.Seek(size - 1, SEEK_SET) && file.WriteBytes("", 1) == 1) {
211 return RESULT_SUCCESS;
212 }
213
214 LOG_ERROR(Service_FS, "Too large file");
215 return ResultCode(ErrorDescription::TooLarge, ErrorModule::FS, ErrorSummary::OutOfResource,
216 ErrorLevel::Info);
217}
218
219ResultCode SDMCArchive::CreateDirectory(const Path& path) const {
220 const PathParser path_parser(path);
221
222 if (!path_parser.IsValid()) {
223 LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
224 return ERROR_INVALID_PATH;
225 }
226
227 const auto full_path = path_parser.BuildHostPath(mount_point);
228
229 switch (path_parser.GetHostStatus(mount_point)) {
230 case PathParser::InvalidMountPoint:
231 LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
232 return ERROR_NOT_FOUND;
233 case PathParser::PathNotFound:
234 case PathParser::FileInPath:
235 LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str());
236 return ERROR_NOT_FOUND;
237 case PathParser::DirectoryFound:
238 case PathParser::FileFound:
239 LOG_ERROR(Service_FS, "%s already exists", full_path.c_str());
240 return ERROR_ALREADY_EXISTS;
241 }
242
243 if (FileUtil::CreateDir(mount_point + path.AsString())) {
244 return RESULT_SUCCESS;
245 }
246
247 LOG_CRITICAL(Service_FS, "(unreachable) Unknown error creating %s", mount_point.c_str());
248 return ResultCode(ErrorDescription::NoData, ErrorModule::FS, ErrorSummary::Canceled,
249 ErrorLevel::Status);
250}
251
252ResultCode SDMCArchive::RenameDirectory(const Path& src_path, const Path& dest_path) const {
253 if (FileUtil::Rename(mount_point + src_path.AsString(), mount_point + dest_path.AsString()))
254 return RESULT_SUCCESS;
255
256 // TODO(yuriks): This code probably isn't right, it'll return a Status even if the file didn't
257 // exist or similar. Verify.
258 return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
259 ErrorSummary::NothingHappened, ErrorLevel::Status);
260}
261
262ResultVal<std::unique_ptr<DirectoryBackend>> SDMCArchive::OpenDirectory(const Path& path) const {
263 const PathParser path_parser(path);
264
265 if (!path_parser.IsValid()) {
266 LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
267 return ERROR_INVALID_PATH;
268 }
269
270 const auto full_path = path_parser.BuildHostPath(mount_point);
271
272 switch (path_parser.GetHostStatus(mount_point)) {
273 case PathParser::InvalidMountPoint:
274 LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
275 return ERROR_NOT_FOUND;
276 case PathParser::PathNotFound:
277 case PathParser::NotFound:
278 case PathParser::FileFound:
279 LOG_ERROR(Service_FS, "%s not found", full_path.c_str());
280 return ERROR_NOT_FOUND;
281 case PathParser::FileInPath:
282 LOG_ERROR(Service_FS, "Unexpected file in path %s", full_path.c_str());
283 return ERROR_UNEXPECTED_FILE_OR_DIRECTORY_SDMC;
284 }
285
286 auto directory = std::make_unique<DiskDirectory>(full_path);
287 return MakeResult<std::unique_ptr<DirectoryBackend>>(std::move(directory));
288}
289
290u64 SDMCArchive::GetFreeBytes() const {
291 // TODO: Stubbed to return 1GiB
292 return 1024 * 1024 * 1024;
293}
294
18ArchiveFactory_SDMC::ArchiveFactory_SDMC(const std::string& sdmc_directory) 295ArchiveFactory_SDMC::ArchiveFactory_SDMC(const std::string& sdmc_directory)
19 : sdmc_directory(sdmc_directory) { 296 : sdmc_directory(sdmc_directory) {
20 LOG_INFO(Service_FS, "Directory %s set as SDMC.", sdmc_directory.c_str()); 297 LOG_INFO(Service_FS, "Directory %s set as SDMC.", sdmc_directory.c_str());
@@ -35,7 +312,7 @@ bool ArchiveFactory_SDMC::Initialize() {
35} 312}
36 313
37ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SDMC::Open(const Path& path) { 314ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SDMC::Open(const Path& path) {
38 auto archive = std::make_unique<DiskArchive>(sdmc_directory); 315 auto archive = std::make_unique<SDMCArchive>(sdmc_directory);
39 return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); 316 return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
40} 317}
41 318
diff --git a/src/core/file_sys/archive_sdmc.h b/src/core/file_sys/archive_sdmc.h
index 88e855351..9d99b110c 100644
--- a/src/core/file_sys/archive_sdmc.h
+++ b/src/core/file_sys/archive_sdmc.h
@@ -14,6 +14,32 @@
14 14
15namespace FileSys { 15namespace FileSys {
16 16
17/// Archive backend for SDMC archive
18class SDMCArchive : public ArchiveBackend {
19public:
20 SDMCArchive(const std::string& mount_point_) : mount_point(mount_point_) {}
21
22 std::string GetName() const override {
23 return "SDMCArchive: " + mount_point;
24 }
25
26 ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path,
27 const Mode& mode) const override;
28 ResultCode DeleteFile(const Path& path) const override;
29 ResultCode RenameFile(const Path& src_path, const Path& dest_path) const override;
30 ResultCode DeleteDirectory(const Path& path) const override;
31 ResultCode DeleteDirectoryRecursively(const Path& path) const override;
32 ResultCode CreateFile(const Path& path, u64 size) const override;
33 ResultCode CreateDirectory(const Path& path) const override;
34 ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const override;
35 ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const override;
36 u64 GetFreeBytes() const override;
37
38protected:
39 ResultVal<std::unique_ptr<FileBackend>> OpenFileBase(const Path& path, const Mode& mode) const;
40 std::string mount_point;
41};
42
17/// File system interface to the SDMC archive 43/// File system interface to the SDMC archive
18class ArchiveFactory_SDMC final : public ArchiveFactory { 44class ArchiveFactory_SDMC final : public ArchiveFactory {
19public: 45public:
diff --git a/src/core/file_sys/archive_sdmcwriteonly.cpp b/src/core/file_sys/archive_sdmcwriteonly.cpp
new file mode 100644
index 000000000..2aafc9b1d
--- /dev/null
+++ b/src/core/file_sys/archive_sdmcwriteonly.cpp
@@ -0,0 +1,70 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <memory>
6#include "common/file_util.h"
7#include "core/file_sys/archive_sdmcwriteonly.h"
8#include "core/file_sys/directory_backend.h"
9#include "core/file_sys/errors.h"
10#include "core/file_sys/file_backend.h"
11#include "core/settings.h"
12
13////////////////////////////////////////////////////////////////////////////////////////////////////
14// FileSys namespace
15
16namespace FileSys {
17
18ResultVal<std::unique_ptr<FileBackend>> SDMCWriteOnlyArchive::OpenFile(const Path& path,
19 const Mode& mode) const {
20 if (mode.read_flag) {
21 LOG_ERROR(Service_FS, "Read flag is not supported");
22 return ERROR_INVALID_READ_FLAG;
23 }
24 return SDMCArchive::OpenFileBase(path, mode);
25}
26
27ResultVal<std::unique_ptr<DirectoryBackend>> SDMCWriteOnlyArchive::OpenDirectory(
28 const Path& path) const {
29 LOG_ERROR(Service_FS, "Not supported");
30 return ERROR_UNSUPPORTED_OPEN_FLAGS;
31}
32
33ArchiveFactory_SDMCWriteOnly::ArchiveFactory_SDMCWriteOnly(const std::string& mount_point)
34 : sdmc_directory(mount_point) {
35 LOG_INFO(Service_FS, "Directory %s set as SDMCWriteOnly.", sdmc_directory.c_str());
36}
37
38bool ArchiveFactory_SDMCWriteOnly::Initialize() {
39 if (!Settings::values.use_virtual_sd) {
40 LOG_WARNING(Service_FS, "SDMC disabled by config.");
41 return false;
42 }
43
44 if (!FileUtil::CreateFullPath(sdmc_directory)) {
45 LOG_ERROR(Service_FS, "Unable to create SDMC path.");
46 return false;
47 }
48
49 return true;
50}
51
52ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SDMCWriteOnly::Open(const Path& path) {
53 auto archive = std::make_unique<SDMCWriteOnlyArchive>(sdmc_directory);
54 return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
55}
56
57ResultCode ArchiveFactory_SDMCWriteOnly::Format(const Path& path,
58 const FileSys::ArchiveFormatInfo& format_info) {
59 // TODO(wwylele): hwtest this
60 LOG_ERROR(Service_FS, "Attempted to format a SDMC write-only archive.");
61 return ResultCode(-1);
62}
63
64ResultVal<ArchiveFormatInfo> ArchiveFactory_SDMCWriteOnly::GetFormatInfo(const Path& path) const {
65 // TODO(Subv): Implement
66 LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive %s", GetName().c_str());
67 return ResultCode(-1);
68}
69
70} // namespace FileSys
diff --git a/src/core/file_sys/archive_sdmcwriteonly.h b/src/core/file_sys/archive_sdmcwriteonly.h
new file mode 100644
index 000000000..ed977485a
--- /dev/null
+++ b/src/core/file_sys/archive_sdmcwriteonly.h
@@ -0,0 +1,57 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/file_sys/archive_sdmc.h"
8
9////////////////////////////////////////////////////////////////////////////////////////////////////
10// FileSys namespace
11
12namespace FileSys {
13
14/**
15 * Archive backend for SDMC write-only archive.
16 * The behaviour of SDMCWriteOnlyArchive is almost the same as SDMCArchive, except for
17 * - OpenDirectory is unsupported;
18 * - OpenFile with read flag is unsupported.
19 */
20class SDMCWriteOnlyArchive : public SDMCArchive {
21public:
22 SDMCWriteOnlyArchive(const std::string& mount_point) : SDMCArchive(mount_point) {}
23
24 std::string GetName() const override {
25 return "SDMCWriteOnlyArchive: " + mount_point;
26 }
27
28 ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path,
29 const Mode& mode) const override;
30
31 ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const override;
32};
33
34/// File system interface to the SDMC write-only archive
35class ArchiveFactory_SDMCWriteOnly final : public ArchiveFactory {
36public:
37 ArchiveFactory_SDMCWriteOnly(const std::string& mount_point);
38
39 /**
40 * Initialize the archive.
41 * @return true if it initialized successfully
42 */
43 bool Initialize();
44
45 std::string GetName() const override {
46 return "SDMCWriteOnly";
47 }
48
49 ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override;
50 ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) override;
51 ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
52
53private:
54 std::string sdmc_directory;
55};
56
57} // namespace FileSys
diff --git a/src/core/file_sys/archive_systemsavedata.cpp b/src/core/file_sys/archive_systemsavedata.cpp
index 48ebc0ed4..54e7793e0 100644
--- a/src/core/file_sys/archive_systemsavedata.cpp
+++ b/src/core/file_sys/archive_systemsavedata.cpp
@@ -9,7 +9,7 @@
9#include "common/file_util.h" 9#include "common/file_util.h"
10#include "common/string_util.h" 10#include "common/string_util.h"
11#include "core/file_sys/archive_systemsavedata.h" 11#include "core/file_sys/archive_systemsavedata.h"
12#include "core/file_sys/disk_archive.h" 12#include "core/file_sys/savedata_archive.h"
13#include "core/hle/service/fs/archive.h" 13#include "core/hle/service/fs/archive.h"
14 14
15//////////////////////////////////////////////////////////////////////////////////////////////////// 15////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -56,7 +56,7 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SystemSaveData::Open(c
56 return ResultCode(ErrorDescription::FS_NotFormatted, ErrorModule::FS, 56 return ResultCode(ErrorDescription::FS_NotFormatted, ErrorModule::FS,
57 ErrorSummary::InvalidState, ErrorLevel::Status); 57 ErrorSummary::InvalidState, ErrorLevel::Status);
58 } 58 }
59 auto archive = std::make_unique<DiskArchive>(fullpath); 59 auto archive = std::make_unique<SaveDataArchive>(fullpath);
60 return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); 60 return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
61} 61}
62 62
diff --git a/src/core/file_sys/directory_backend.h b/src/core/file_sys/directory_backend.h
index b55e382ef..0c93f2074 100644
--- a/src/core/file_sys/directory_backend.h
+++ b/src/core/file_sys/directory_backend.h
@@ -41,12 +41,6 @@ public:
41 virtual ~DirectoryBackend() {} 41 virtual ~DirectoryBackend() {}
42 42
43 /** 43 /**
44 * Open the directory
45 * @return true if the directory opened correctly
46 */
47 virtual bool Open() = 0;
48
49 /**
50 * List files contained in the directory 44 * List files contained in the directory
51 * @param count Number of entries to return at once in entries 45 * @param count Number of entries to return at once in entries
52 * @param entries Buffer to read data into 46 * @param entries Buffer to read data into
diff --git a/src/core/file_sys/disk_archive.cpp b/src/core/file_sys/disk_archive.cpp
index 2f05af361..a243d9a13 100644
--- a/src/core/file_sys/disk_archive.cpp
+++ b/src/core/file_sys/disk_archive.cpp
@@ -15,144 +15,8 @@
15 15
16namespace FileSys { 16namespace FileSys {
17 17
18ResultVal<std::unique_ptr<FileBackend>> DiskArchive::OpenFile(const Path& path,
19 const Mode mode) const {
20 LOG_DEBUG(Service_FS, "called path=%s mode=%01X", path.DebugStr().c_str(), mode.hex);
21 auto file = std::make_unique<DiskFile>(*this, path, mode);
22 ResultCode result = file->Open();
23 if (result.IsError())
24 return result;
25 return MakeResult<std::unique_ptr<FileBackend>>(std::move(file));
26}
27
28ResultCode DiskArchive::DeleteFile(const Path& path) const {
29 std::string file_path = mount_point + path.AsString();
30
31 if (FileUtil::IsDirectory(file_path))
32 return ResultCode(ErrorDescription::FS_NotAFile, ErrorModule::FS, ErrorSummary::Canceled,
33 ErrorLevel::Status);
34
35 if (!FileUtil::Exists(file_path))
36 return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS, ErrorSummary::NotFound,
37 ErrorLevel::Status);
38
39 if (FileUtil::Delete(file_path))
40 return RESULT_SUCCESS;
41
42 return ResultCode(ErrorDescription::FS_NotAFile, ErrorModule::FS, ErrorSummary::Canceled,
43 ErrorLevel::Status);
44}
45
46bool DiskArchive::RenameFile(const Path& src_path, const Path& dest_path) const {
47 return FileUtil::Rename(mount_point + src_path.AsString(), mount_point + dest_path.AsString());
48}
49
50bool DiskArchive::DeleteDirectory(const Path& path) const {
51 return FileUtil::DeleteDir(mount_point + path.AsString());
52}
53
54bool DiskArchive::DeleteDirectoryRecursively(const Path& path) const {
55 return FileUtil::DeleteDirRecursively(mount_point + path.AsString());
56}
57
58ResultCode DiskArchive::CreateFile(const FileSys::Path& path, u64 size) const {
59 std::string full_path = mount_point + path.AsString();
60
61 if (FileUtil::IsDirectory(full_path))
62 return ResultCode(ErrorDescription::FS_NotAFile, ErrorModule::FS, ErrorSummary::Canceled,
63 ErrorLevel::Status);
64
65 if (FileUtil::Exists(full_path))
66 return ResultCode(ErrorDescription::FS_AlreadyExists, ErrorModule::FS,
67 ErrorSummary::NothingHappened, ErrorLevel::Status);
68
69 if (size == 0) {
70 FileUtil::CreateEmptyFile(full_path);
71 return RESULT_SUCCESS;
72 }
73
74 FileUtil::IOFile file(full_path, "wb");
75 // Creates a sparse file (or a normal file on filesystems without the concept of sparse files)
76 // We do this by seeking to the right size, then writing a single null byte.
77 if (file.Seek(size - 1, SEEK_SET) && file.WriteBytes("", 1) == 1)
78 return RESULT_SUCCESS;
79
80 return ResultCode(ErrorDescription::TooLarge, ErrorModule::FS, ErrorSummary::OutOfResource,
81 ErrorLevel::Info);
82}
83
84bool DiskArchive::CreateDirectory(const Path& path) const {
85 return FileUtil::CreateDir(mount_point + path.AsString());
86}
87
88bool DiskArchive::RenameDirectory(const Path& src_path, const Path& dest_path) const {
89 return FileUtil::Rename(mount_point + src_path.AsString(), mount_point + dest_path.AsString());
90}
91
92std::unique_ptr<DirectoryBackend> DiskArchive::OpenDirectory(const Path& path) const {
93 LOG_DEBUG(Service_FS, "called path=%s", path.DebugStr().c_str());
94 auto directory = std::make_unique<DiskDirectory>(*this, path);
95 if (!directory->Open())
96 return nullptr;
97 return std::move(directory);
98}
99
100u64 DiskArchive::GetFreeBytes() const {
101 // TODO: Stubbed to return 1GiB
102 return 1024 * 1024 * 1024;
103}
104
105////////////////////////////////////////////////////////////////////////////////////////////////////
106
107DiskFile::DiskFile(const DiskArchive& archive, const Path& path, const Mode mode) {
108 // TODO(Link Mauve): normalize path into an absolute path without "..", it can currently bypass
109 // the root directory we set while opening the archive.
110 // For example, opening /../../etc/passwd can give the emulated program your users list.
111 this->path = archive.mount_point + path.AsString();
112 this->mode.hex = mode.hex;
113}
114
115ResultCode DiskFile::Open() {
116 if (FileUtil::IsDirectory(path))
117 return ResultCode(ErrorDescription::FS_NotAFile, ErrorModule::FS, ErrorSummary::Canceled,
118 ErrorLevel::Status);
119
120 // Specifying only the Create flag is invalid
121 if (mode.create_flag && !mode.read_flag && !mode.write_flag) {
122 return ResultCode(ErrorDescription::FS_InvalidOpenFlags, ErrorModule::FS,
123 ErrorSummary::Canceled, ErrorLevel::Status);
124 }
125
126 if (!FileUtil::Exists(path)) {
127 if (!mode.create_flag) {
128 LOG_ERROR(Service_FS, "Non-existing file %s can't be open without mode create.",
129 path.c_str());
130 return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS,
131 ErrorSummary::NotFound, ErrorLevel::Status);
132 } else {
133 // Create the file
134 FileUtil::CreateEmptyFile(path);
135 }
136 }
137
138 std::string mode_string = "";
139 if (mode.write_flag)
140 mode_string += "r+"; // Files opened with Write access can be read from
141 else if (mode.read_flag)
142 mode_string += "r";
143
144 // Open the file in binary mode, to avoid problems with CR/LF on Windows systems
145 mode_string += "b";
146
147 file = std::make_unique<FileUtil::IOFile>(path, mode_string.c_str());
148 if (file->IsOpen())
149 return RESULT_SUCCESS;
150 return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS, ErrorSummary::NotFound,
151 ErrorLevel::Status);
152}
153
154ResultVal<size_t> DiskFile::Read(const u64 offset, const size_t length, u8* buffer) const { 18ResultVal<size_t> DiskFile::Read(const u64 offset, const size_t length, u8* buffer) const {
155 if (!mode.read_flag && !mode.write_flag) 19 if (!mode.read_flag)
156 return ResultCode(ErrorDescription::FS_InvalidOpenFlags, ErrorModule::FS, 20 return ResultCode(ErrorDescription::FS_InvalidOpenFlags, ErrorModule::FS,
157 ErrorSummary::Canceled, ErrorLevel::Status); 21 ErrorSummary::Canceled, ErrorLevel::Status);
158 22
@@ -189,21 +53,11 @@ bool DiskFile::Close() const {
189 53
190//////////////////////////////////////////////////////////////////////////////////////////////////// 54////////////////////////////////////////////////////////////////////////////////////////////////////
191 55
192DiskDirectory::DiskDirectory(const DiskArchive& archive, const Path& path) : directory() { 56DiskDirectory::DiskDirectory(const std::string& path) : directory() {
193 // TODO(Link Mauve): normalize path into an absolute path without "..", it can currently bypass
194 // the root directory we set while opening the archive.
195 // For example, opening /../../usr/bin can give the emulated program your installed programs.
196 this->path = archive.mount_point + path.AsString();
197}
198
199bool DiskDirectory::Open() {
200 if (!FileUtil::IsDirectory(path))
201 return false;
202 unsigned size = FileUtil::ScanDirectoryTree(path, directory); 57 unsigned size = FileUtil::ScanDirectoryTree(path, directory);
203 directory.size = size; 58 directory.size = size;
204 directory.isDirectory = true; 59 directory.isDirectory = true;
205 children_iterator = directory.children.begin(); 60 children_iterator = directory.children.begin();
206 return true;
207} 61}
208 62
209u32 DiskDirectory::Read(const u32 count, Entry* entries) { 63u32 DiskDirectory::Read(const u32 count, Entry* entries) {
diff --git a/src/core/file_sys/disk_archive.h b/src/core/file_sys/disk_archive.h
index 59ebb2002..eb9166df6 100644
--- a/src/core/file_sys/disk_archive.h
+++ b/src/core/file_sys/disk_archive.h
@@ -20,43 +20,13 @@
20 20
21namespace FileSys { 21namespace FileSys {
22 22
23/**
24 * Helper which implements a backend accessing the host machine's filesystem.
25 * This should be subclassed by concrete archive types, which will provide the
26 * base directory on the host filesystem and override any required functionality.
27 */
28class DiskArchive : public ArchiveBackend {
29public:
30 DiskArchive(const std::string& mount_point_) : mount_point(mount_point_) {}
31
32 virtual std::string GetName() const override {
33 return "DiskArchive: " + mount_point;
34 }
35
36 ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path,
37 const Mode mode) const override;
38 ResultCode DeleteFile(const Path& path) const override;
39 bool RenameFile(const Path& src_path, const Path& dest_path) const override;
40 bool DeleteDirectory(const Path& path) const override;
41 bool DeleteDirectoryRecursively(const Path& path) const override;
42 ResultCode CreateFile(const Path& path, u64 size) const override;
43 bool CreateDirectory(const Path& path) const override;
44 bool RenameDirectory(const Path& src_path, const Path& dest_path) const override;
45 std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const override;
46 u64 GetFreeBytes() const override;
47
48protected:
49 friend class DiskFile;
50 friend class DiskDirectory;
51
52 std::string mount_point;
53};
54
55class DiskFile : public FileBackend { 23class DiskFile : public FileBackend {
56public: 24public:
57 DiskFile(const DiskArchive& archive, const Path& path, const Mode mode); 25 DiskFile(FileUtil::IOFile&& file_, const Mode& mode_)
26 : file(new FileUtil::IOFile(std::move(file_))) {
27 mode.hex = mode_.hex;
28 }
58 29
59 ResultCode Open() override;
60 ResultVal<size_t> Read(u64 offset, size_t length, u8* buffer) const override; 30 ResultVal<size_t> Read(u64 offset, size_t length, u8* buffer) const override;
61 ResultVal<size_t> Write(u64 offset, size_t length, bool flush, const u8* buffer) const override; 31 ResultVal<size_t> Write(u64 offset, size_t length, bool flush, const u8* buffer) const override;
62 u64 GetSize() const override; 32 u64 GetSize() const override;
@@ -68,20 +38,18 @@ public:
68 } 38 }
69 39
70protected: 40protected:
71 std::string path;
72 Mode mode; 41 Mode mode;
73 std::unique_ptr<FileUtil::IOFile> file; 42 std::unique_ptr<FileUtil::IOFile> file;
74}; 43};
75 44
76class DiskDirectory : public DirectoryBackend { 45class DiskDirectory : public DirectoryBackend {
77public: 46public:
78 DiskDirectory(const DiskArchive& archive, const Path& path); 47 DiskDirectory(const std::string& path);
79 48
80 ~DiskDirectory() override { 49 ~DiskDirectory() override {
81 Close(); 50 Close();
82 } 51 }
83 52
84 bool Open() override;
85 u32 Read(const u32 count, Entry* entries) override; 53 u32 Read(const u32 count, Entry* entries) override;
86 54
87 bool Close() const override { 55 bool Close() const override {
@@ -89,7 +57,6 @@ public:
89 } 57 }
90 58
91protected: 59protected:
92 std::string path;
93 u32 total_entries_in_directory; 60 u32 total_entries_in_directory;
94 FileUtil::FSTEntry directory; 61 FileUtil::FSTEntry directory;
95 62
diff --git a/src/core/file_sys/errors.h b/src/core/file_sys/errors.h
new file mode 100644
index 000000000..fd1b07df0
--- /dev/null
+++ b/src/core/file_sys/errors.h
@@ -0,0 +1,40 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/result.h"
6
7namespace FileSys {
8
9const ResultCode ERROR_INVALID_PATH(ErrorDescription::FS_InvalidPath, ErrorModule::FS,
10 ErrorSummary::InvalidArgument, ErrorLevel::Usage);
11const ResultCode ERROR_UNSUPPORTED_OPEN_FLAGS(ErrorDescription::FS_UnsupportedOpenFlags,
12 ErrorModule::FS, ErrorSummary::NotSupported,
13 ErrorLevel::Usage);
14const ResultCode ERROR_INVALID_OPEN_FLAGS(ErrorDescription::FS_InvalidOpenFlags, ErrorModule::FS,
15 ErrorSummary::Canceled, ErrorLevel::Status);
16const ResultCode ERROR_INVALID_READ_FLAG(ErrorDescription::FS_InvalidReadFlag, ErrorModule::FS,
17 ErrorSummary::InvalidArgument, ErrorLevel::Usage);
18const ResultCode ERROR_FILE_NOT_FOUND(ErrorDescription::FS_FileNotFound, ErrorModule::FS,
19 ErrorSummary::NotFound, ErrorLevel::Status);
20const ResultCode ERROR_PATH_NOT_FOUND(ErrorDescription::FS_PathNotFound, ErrorModule::FS,
21 ErrorSummary::NotFound, ErrorLevel::Status);
22const ResultCode ERROR_NOT_FOUND(ErrorDescription::FS_NotFound, ErrorModule::FS,
23 ErrorSummary::NotFound, ErrorLevel::Status);
24const ResultCode ERROR_UNEXPECTED_FILE_OR_DIRECTORY(ErrorDescription::FS_UnexpectedFileOrDirectory,
25 ErrorModule::FS, ErrorSummary::NotSupported,
26 ErrorLevel::Usage);
27const ResultCode ERROR_UNEXPECTED_FILE_OR_DIRECTORY_SDMC(ErrorDescription::FS_NotAFile,
28 ErrorModule::FS, ErrorSummary::Canceled,
29 ErrorLevel::Status);
30const ResultCode ERROR_DIRECTORY_ALREADY_EXISTS(ErrorDescription::FS_DirectoryAlreadyExists,
31 ErrorModule::FS, ErrorSummary::NothingHappened,
32 ErrorLevel::Status);
33const ResultCode ERROR_FILE_ALREADY_EXISTS(ErrorDescription::FS_FileAlreadyExists, ErrorModule::FS,
34 ErrorSummary::NothingHappened, ErrorLevel::Status);
35const ResultCode ERROR_ALREADY_EXISTS(ErrorDescription::FS_AlreadyExists, ErrorModule::FS,
36 ErrorSummary::NothingHappened, ErrorLevel::Status);
37const ResultCode ERROR_DIRECTORY_NOT_EMPTY(ErrorDescription::FS_DirectoryNotEmpty, ErrorModule::FS,
38 ErrorSummary::Canceled, ErrorLevel::Status);
39
40} // namespace FileSys
diff --git a/src/core/file_sys/file_backend.h b/src/core/file_sys/file_backend.h
index ed997537f..5e7c2bab4 100644
--- a/src/core/file_sys/file_backend.h
+++ b/src/core/file_sys/file_backend.h
@@ -19,12 +19,6 @@ public:
19 virtual ~FileBackend() {} 19 virtual ~FileBackend() {}
20 20
21 /** 21 /**
22 * Open the file
23 * @return Result of the file operation
24 */
25 virtual ResultCode Open() = 0;
26
27 /**
28 * Read data from the file 22 * Read data from the file
29 * @param offset Offset in bytes to start reading data from 23 * @param offset Offset in bytes to start reading data from
30 * @param length Length in bytes of data to read from file 24 * @param length Length in bytes of data to read from file
diff --git a/src/core/file_sys/ivfc_archive.cpp b/src/core/file_sys/ivfc_archive.cpp
index af59d296d..2735d2e3c 100644
--- a/src/core/file_sys/ivfc_archive.cpp
+++ b/src/core/file_sys/ivfc_archive.cpp
@@ -18,7 +18,7 @@ std::string IVFCArchive::GetName() const {
18} 18}
19 19
20ResultVal<std::unique_ptr<FileBackend>> IVFCArchive::OpenFile(const Path& path, 20ResultVal<std::unique_ptr<FileBackend>> IVFCArchive::OpenFile(const Path& path,
21 const Mode mode) const { 21 const Mode& mode) const {
22 return MakeResult<std::unique_ptr<FileBackend>>( 22 return MakeResult<std::unique_ptr<FileBackend>>(
23 std::make_unique<IVFCFile>(romfs_file, data_offset, data_size)); 23 std::make_unique<IVFCFile>(romfs_file, data_offset, data_size));
24} 24}
@@ -31,22 +31,25 @@ ResultCode IVFCArchive::DeleteFile(const Path& path) const {
31 ErrorLevel::Status); 31 ErrorLevel::Status);
32} 32}
33 33
34bool IVFCArchive::RenameFile(const Path& src_path, const Path& dest_path) const { 34ResultCode IVFCArchive::RenameFile(const Path& src_path, const Path& dest_path) const {
35 LOG_CRITICAL(Service_FS, "Attempted to rename a file within an IVFC archive (%s).", 35 LOG_CRITICAL(Service_FS, "Attempted to rename a file within an IVFC archive (%s).",
36 GetName().c_str()); 36 GetName().c_str());
37 return false; 37 // TODO(wwylele): Use correct error code
38 return ResultCode(-1);
38} 39}
39 40
40bool IVFCArchive::DeleteDirectory(const Path& path) const { 41ResultCode IVFCArchive::DeleteDirectory(const Path& path) const {
41 LOG_CRITICAL(Service_FS, "Attempted to delete a directory from an IVFC archive (%s).", 42 LOG_CRITICAL(Service_FS, "Attempted to delete a directory from an IVFC archive (%s).",
42 GetName().c_str()); 43 GetName().c_str());
43 return false; 44 // TODO(wwylele): Use correct error code
45 return ResultCode(-1);
44} 46}
45 47
46bool IVFCArchive::DeleteDirectoryRecursively(const Path& path) const { 48ResultCode IVFCArchive::DeleteDirectoryRecursively(const Path& path) const {
47 LOG_CRITICAL(Service_FS, "Attempted to delete a directory from an IVFC archive (%s).", 49 LOG_CRITICAL(Service_FS, "Attempted to delete a directory from an IVFC archive (%s).",
48 GetName().c_str()); 50 GetName().c_str());
49 return false; 51 // TODO(wwylele): Use correct error code
52 return ResultCode(-1);
50} 53}
51 54
52ResultCode IVFCArchive::CreateFile(const Path& path, u64 size) const { 55ResultCode IVFCArchive::CreateFile(const Path& path, u64 size) const {
@@ -57,20 +60,22 @@ ResultCode IVFCArchive::CreateFile(const Path& path, u64 size) const {
57 ErrorLevel::Permanent); 60 ErrorLevel::Permanent);
58} 61}
59 62
60bool IVFCArchive::CreateDirectory(const Path& path) const { 63ResultCode IVFCArchive::CreateDirectory(const Path& path) const {
61 LOG_CRITICAL(Service_FS, "Attempted to create a directory in an IVFC archive (%s).", 64 LOG_CRITICAL(Service_FS, "Attempted to create a directory in an IVFC archive (%s).",
62 GetName().c_str()); 65 GetName().c_str());
63 return false; 66 // TODO(wwylele): Use correct error code
67 return ResultCode(-1);
64} 68}
65 69
66bool IVFCArchive::RenameDirectory(const Path& src_path, const Path& dest_path) const { 70ResultCode IVFCArchive::RenameDirectory(const Path& src_path, const Path& dest_path) const {
67 LOG_CRITICAL(Service_FS, "Attempted to rename a file within an IVFC archive (%s).", 71 LOG_CRITICAL(Service_FS, "Attempted to rename a file within an IVFC archive (%s).",
68 GetName().c_str()); 72 GetName().c_str());
69 return false; 73 // TODO(wwylele): Use correct error code
74 return ResultCode(-1);
70} 75}
71 76
72std::unique_ptr<DirectoryBackend> IVFCArchive::OpenDirectory(const Path& path) const { 77ResultVal<std::unique_ptr<DirectoryBackend>> IVFCArchive::OpenDirectory(const Path& path) const {
73 return std::make_unique<IVFCDirectory>(); 78 return MakeResult<std::unique_ptr<DirectoryBackend>>(std::make_unique<IVFCDirectory>());
74} 79}
75 80
76u64 IVFCArchive::GetFreeBytes() const { 81u64 IVFCArchive::GetFreeBytes() const {
diff --git a/src/core/file_sys/ivfc_archive.h b/src/core/file_sys/ivfc_archive.h
index 2fbb3a568..e6fbdfb1f 100644
--- a/src/core/file_sys/ivfc_archive.h
+++ b/src/core/file_sys/ivfc_archive.h
@@ -33,15 +33,15 @@ public:
33 std::string GetName() const override; 33 std::string GetName() const override;
34 34
35 ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path, 35 ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path,
36 const Mode mode) const override; 36 const Mode& mode) const override;
37 ResultCode DeleteFile(const Path& path) const override; 37 ResultCode DeleteFile(const Path& path) const override;
38 bool RenameFile(const Path& src_path, const Path& dest_path) const override; 38 ResultCode RenameFile(const Path& src_path, const Path& dest_path) const override;
39 bool DeleteDirectory(const Path& path) const override; 39 ResultCode DeleteDirectory(const Path& path) const override;
40 bool DeleteDirectoryRecursively(const Path& path) const override; 40 ResultCode DeleteDirectoryRecursively(const Path& path) const override;
41 ResultCode CreateFile(const Path& path, u64 size) const override; 41 ResultCode CreateFile(const Path& path, u64 size) const override;
42 bool CreateDirectory(const Path& path) const override; 42 ResultCode CreateDirectory(const Path& path) const override;
43 bool RenameDirectory(const Path& src_path, const Path& dest_path) const override; 43 ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const override;
44 std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const override; 44 ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const override;
45 u64 GetFreeBytes() const override; 45 u64 GetFreeBytes() const override;
46 46
47protected: 47protected:
@@ -55,9 +55,6 @@ public:
55 IVFCFile(std::shared_ptr<FileUtil::IOFile> file, u64 offset, u64 size) 55 IVFCFile(std::shared_ptr<FileUtil::IOFile> file, u64 offset, u64 size)
56 : romfs_file(file), data_offset(offset), data_size(size) {} 56 : romfs_file(file), data_offset(offset), data_size(size) {}
57 57
58 ResultCode Open() override {
59 return RESULT_SUCCESS;
60 }
61 ResultVal<size_t> Read(u64 offset, size_t length, u8* buffer) const override; 58 ResultVal<size_t> Read(u64 offset, size_t length, u8* buffer) const override;
62 ResultVal<size_t> Write(u64 offset, size_t length, bool flush, const u8* buffer) const override; 59 ResultVal<size_t> Write(u64 offset, size_t length, bool flush, const u8* buffer) const override;
63 u64 GetSize() const override; 60 u64 GetSize() const override;
@@ -75,9 +72,6 @@ private:
75 72
76class IVFCDirectory : public DirectoryBackend { 73class IVFCDirectory : public DirectoryBackend {
77public: 74public:
78 bool Open() override {
79 return false;
80 }
81 u32 Read(const u32 count, Entry* entries) override { 75 u32 Read(const u32 count, Entry* entries) override {
82 return 0; 76 return 0;
83 } 77 }
diff --git a/src/core/file_sys/path_parser.cpp b/src/core/file_sys/path_parser.cpp
new file mode 100644
index 000000000..5a89b02b8
--- /dev/null
+++ b/src/core/file_sys/path_parser.cpp
@@ -0,0 +1,98 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <set>
7#include "common/file_util.h"
8#include "common/string_util.h"
9#include "core/file_sys/path_parser.h"
10
11namespace FileSys {
12
13PathParser::PathParser(const Path& path) {
14 if (path.GetType() != LowPathType::Char && path.GetType() != LowPathType::Wchar) {
15 is_valid = false;
16 return;
17 }
18
19 auto path_string = path.AsString();
20 if (path_string.size() == 0 || path_string[0] != '/') {
21 is_valid = false;
22 return;
23 }
24
25 // Filter out invalid characters for the host system.
26 // Although some of these characters are valid on 3DS, they are unlikely to be used by games.
27 if (std::find_if(path_string.begin(), path_string.end(), [](char c) {
28 static const std::set<char> invalid_chars{'<', '>', '\\', '|', ':', '\"', '*', '?'};
29 return invalid_chars.find(c) != invalid_chars.end();
30 }) != path_string.end()) {
31 is_valid = false;
32 return;
33 }
34
35 Common::SplitString(path_string, '/', path_sequence);
36
37 auto begin = path_sequence.begin();
38 auto end = path_sequence.end();
39 end = std::remove_if(begin, end, [](std::string& str) { return str == "" || str == "."; });
40 path_sequence = std::vector<std::string>(begin, end);
41
42 // checks if the path is out of bounds.
43 int level = 0;
44 for (auto& node : path_sequence) {
45 if (node == "..") {
46 --level;
47 if (level < 0) {
48 is_valid = false;
49 return;
50 }
51 } else {
52 ++level;
53 }
54 }
55
56 is_valid = true;
57 is_root = level == 0;
58}
59
60PathParser::HostStatus PathParser::GetHostStatus(const std::string& mount_point) const {
61 auto path = mount_point;
62 if (!FileUtil::IsDirectory(path))
63 return InvalidMountPoint;
64 if (path_sequence.empty()) {
65 return DirectoryFound;
66 }
67
68 for (auto iter = path_sequence.begin(); iter != path_sequence.end() - 1; iter++) {
69 if (path.back() != '/')
70 path += '/';
71 path += *iter;
72
73 if (!FileUtil::Exists(path))
74 return PathNotFound;
75 if (FileUtil::IsDirectory(path))
76 continue;
77 return FileInPath;
78 }
79
80 path += "/" + path_sequence.back();
81 if (!FileUtil::Exists(path))
82 return NotFound;
83 if (FileUtil::IsDirectory(path))
84 return DirectoryFound;
85 return FileFound;
86}
87
88std::string PathParser::BuildHostPath(const std::string& mount_point) const {
89 std::string path = mount_point;
90 for (auto& node : path_sequence) {
91 if (path.back() != '/')
92 path += '/';
93 path += node;
94 }
95 return path;
96}
97
98} // namespace FileSys
diff --git a/src/core/file_sys/path_parser.h b/src/core/file_sys/path_parser.h
new file mode 100644
index 000000000..990802579
--- /dev/null
+++ b/src/core/file_sys/path_parser.h
@@ -0,0 +1,61 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <string>
8#include <vector>
9#include "core/file_sys/archive_backend.h"
10
11namespace FileSys {
12
13/**
14 * A helper class parsing and verifying a string-type Path.
15 * Every archives with a sub file system should use this class to parse the path argument and check
16 * the status of the file / directory in question on the host file system.
17 */
18class PathParser {
19public:
20 PathParser(const Path& path);
21
22 /**
23 * Checks if the Path is valid.
24 * This function should be called once a PathParser is constructed.
25 * A Path is valid if:
26 * - it is a string path (with type LowPathType::Char or LowPathType::Wchar),
27 * - it starts with "/" (this seems a hard requirement in real 3DS),
28 * - it doesn't contain invalid characters, and
29 * - it doesn't go out of the root directory using "..".
30 */
31 bool IsValid() const {
32 return is_valid;
33 }
34
35 /// Checks if the Path represents the root directory.
36 bool IsRootDirectory() const {
37 return is_root;
38 }
39
40 enum HostStatus {
41 InvalidMountPoint,
42 PathNotFound, // "/a/b/c" when "a" doesn't exist
43 FileInPath, // "/a/b/c" when "a" is a file
44 FileFound, // "/a/b/c" when "c" is a file
45 DirectoryFound, // "/a/b/c" when "c" is a directory
46 NotFound // "/a/b/c" when "a/b/" exists but "c" doesn't exist
47 };
48
49 /// Checks the status of the specified file / directory by the Path on the host file system.
50 HostStatus GetHostStatus(const std::string& mount_point) const;
51
52 /// Builds a full path on the host file system.
53 std::string BuildHostPath(const std::string& mount_point) const;
54
55private:
56 std::vector<std::string> path_sequence;
57 bool is_valid{};
58 bool is_root{};
59};
60
61} // namespace FileSys
diff --git a/src/core/file_sys/savedata_archive.cpp b/src/core/file_sys/savedata_archive.cpp
new file mode 100644
index 000000000..f2e6a06bc
--- /dev/null
+++ b/src/core/file_sys/savedata_archive.cpp
@@ -0,0 +1,283 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/file_util.h"
6#include "core/file_sys/disk_archive.h"
7#include "core/file_sys/errors.h"
8#include "core/file_sys/path_parser.h"
9#include "core/file_sys/savedata_archive.h"
10
11////////////////////////////////////////////////////////////////////////////////////////////////////
12// FileSys namespace
13
14namespace FileSys {
15
16ResultVal<std::unique_ptr<FileBackend>> SaveDataArchive::OpenFile(const Path& path,
17 const Mode& mode) const {
18 LOG_DEBUG(Service_FS, "called path=%s mode=%01X", path.DebugStr().c_str(), mode.hex);
19
20 const PathParser path_parser(path);
21
22 if (!path_parser.IsValid()) {
23 LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
24 return ERROR_INVALID_PATH;
25 }
26
27 if (mode.hex == 0) {
28 LOG_ERROR(Service_FS, "Empty open mode");
29 return ERROR_UNSUPPORTED_OPEN_FLAGS;
30 }
31
32 if (mode.create_flag && !mode.write_flag) {
33 LOG_ERROR(Service_FS, "Create flag set but write flag not set");
34 return ERROR_UNSUPPORTED_OPEN_FLAGS;
35 }
36
37 const auto full_path = path_parser.BuildHostPath(mount_point);
38
39 switch (path_parser.GetHostStatus(mount_point)) {
40 case PathParser::InvalidMountPoint:
41 LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
42 return ERROR_FILE_NOT_FOUND;
43 case PathParser::PathNotFound:
44 LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str());
45 return ERROR_PATH_NOT_FOUND;
46 case PathParser::FileInPath:
47 case PathParser::DirectoryFound:
48 LOG_ERROR(Service_FS, "Unexpected file or directory in %s", full_path.c_str());
49 return ERROR_UNEXPECTED_FILE_OR_DIRECTORY;
50 case PathParser::NotFound:
51 if (!mode.create_flag) {
52 LOG_ERROR(Service_FS, "Non-existing file %s can't be open without mode create.",
53 full_path.c_str());
54 return ERROR_FILE_NOT_FOUND;
55 } else {
56 // Create the file
57 FileUtil::CreateEmptyFile(full_path);
58 }
59 break;
60 }
61
62 FileUtil::IOFile file(full_path, mode.write_flag ? "r+b" : "rb");
63 if (!file.IsOpen()) {
64 LOG_CRITICAL(Service_FS, "(unreachable) Unknown error opening %s", full_path.c_str());
65 return ERROR_FILE_NOT_FOUND;
66 }
67
68 auto disk_file = std::make_unique<DiskFile>(std::move(file), mode);
69 return MakeResult<std::unique_ptr<FileBackend>>(std::move(disk_file));
70}
71
72ResultCode SaveDataArchive::DeleteFile(const Path& path) const {
73 const PathParser path_parser(path);
74
75 if (!path_parser.IsValid()) {
76 LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
77 return ERROR_INVALID_PATH;
78 }
79
80 const auto full_path = path_parser.BuildHostPath(mount_point);
81
82 switch (path_parser.GetHostStatus(mount_point)) {
83 case PathParser::InvalidMountPoint:
84 LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
85 return ERROR_FILE_NOT_FOUND;
86 case PathParser::PathNotFound:
87 LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str());
88 return ERROR_PATH_NOT_FOUND;
89 case PathParser::FileInPath:
90 case PathParser::DirectoryFound:
91 case PathParser::NotFound:
92 LOG_ERROR(Service_FS, "File not found %s", full_path.c_str());
93 return ERROR_FILE_NOT_FOUND;
94 }
95
96 if (FileUtil::Delete(full_path)) {
97 return RESULT_SUCCESS;
98 }
99
100 LOG_CRITICAL(Service_FS, "(unreachable) Unknown error deleting %s", full_path.c_str());
101 return ERROR_FILE_NOT_FOUND;
102}
103
104ResultCode SaveDataArchive::RenameFile(const Path& src_path, const Path& dest_path) const {
105 if (FileUtil::Rename(mount_point + src_path.AsString(), mount_point + dest_path.AsString())) {
106 return RESULT_SUCCESS;
107 }
108
109 // TODO(yuriks): This code probably isn't right, it'll return a Status even if the file didn't
110 // exist or similar. Verify.
111 return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
112 ErrorSummary::NothingHappened, ErrorLevel::Status);
113}
114
115template <typename T>
116static ResultCode DeleteDirectoryHelper(const Path& path, const std::string& mount_point,
117 T deleter) {
118 const PathParser path_parser(path);
119
120 if (!path_parser.IsValid()) {
121 LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
122 return ERROR_INVALID_PATH;
123 }
124
125 if (path_parser.IsRootDirectory())
126 return ERROR_DIRECTORY_NOT_EMPTY;
127
128 const auto full_path = path_parser.BuildHostPath(mount_point);
129
130 switch (path_parser.GetHostStatus(mount_point)) {
131 case PathParser::InvalidMountPoint:
132 LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
133 return ERROR_PATH_NOT_FOUND;
134 case PathParser::PathNotFound:
135 case PathParser::NotFound:
136 LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str());
137 return ERROR_PATH_NOT_FOUND;
138 case PathParser::FileInPath:
139 case PathParser::FileFound:
140 LOG_ERROR(Service_FS, "Unexpected file or directory %s", full_path.c_str());
141 return ERROR_UNEXPECTED_FILE_OR_DIRECTORY;
142 }
143
144 if (deleter(full_path)) {
145 return RESULT_SUCCESS;
146 }
147
148 LOG_ERROR(Service_FS, "Directory not empty %s", full_path.c_str());
149 return ERROR_DIRECTORY_NOT_EMPTY;
150}
151
152ResultCode SaveDataArchive::DeleteDirectory(const Path& path) const {
153 return DeleteDirectoryHelper(path, mount_point, FileUtil::DeleteDir);
154}
155
156ResultCode SaveDataArchive::DeleteDirectoryRecursively(const Path& path) const {
157 return DeleteDirectoryHelper(
158 path, mount_point, [](const std::string& p) { return FileUtil::DeleteDirRecursively(p); });
159}
160
161ResultCode SaveDataArchive::CreateFile(const FileSys::Path& path, u64 size) const {
162 const PathParser path_parser(path);
163
164 if (!path_parser.IsValid()) {
165 LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
166 return ERROR_INVALID_PATH;
167 }
168
169 const auto full_path = path_parser.BuildHostPath(mount_point);
170
171 switch (path_parser.GetHostStatus(mount_point)) {
172 case PathParser::InvalidMountPoint:
173 LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
174 return ERROR_FILE_NOT_FOUND;
175 case PathParser::PathNotFound:
176 LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str());
177 return ERROR_PATH_NOT_FOUND;
178 case PathParser::FileInPath:
179 LOG_ERROR(Service_FS, "Unexpected file in path %s", full_path.c_str());
180 return ERROR_UNEXPECTED_FILE_OR_DIRECTORY;
181 case PathParser::DirectoryFound:
182 case PathParser::FileFound:
183 LOG_ERROR(Service_FS, "%s already exists", full_path.c_str());
184 return ERROR_FILE_ALREADY_EXISTS;
185 }
186
187 if (size == 0) {
188 FileUtil::CreateEmptyFile(full_path);
189 return RESULT_SUCCESS;
190 }
191
192 FileUtil::IOFile file(full_path, "wb");
193 // Creates a sparse file (or a normal file on filesystems without the concept of sparse files)
194 // We do this by seeking to the right size, then writing a single null byte.
195 if (file.Seek(size - 1, SEEK_SET) && file.WriteBytes("", 1) == 1) {
196 return RESULT_SUCCESS;
197 }
198
199 LOG_ERROR(Service_FS, "Too large file");
200 return ResultCode(ErrorDescription::TooLarge, ErrorModule::FS, ErrorSummary::OutOfResource,
201 ErrorLevel::Info);
202}
203
204ResultCode SaveDataArchive::CreateDirectory(const Path& path) const {
205 const PathParser path_parser(path);
206
207 if (!path_parser.IsValid()) {
208 LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
209 return ERROR_INVALID_PATH;
210 }
211
212 const auto full_path = path_parser.BuildHostPath(mount_point);
213
214 switch (path_parser.GetHostStatus(mount_point)) {
215 case PathParser::InvalidMountPoint:
216 LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
217 return ERROR_FILE_NOT_FOUND;
218 case PathParser::PathNotFound:
219 LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str());
220 return ERROR_PATH_NOT_FOUND;
221 case PathParser::FileInPath:
222 LOG_ERROR(Service_FS, "Unexpected file in path %s", full_path.c_str());
223 return ERROR_UNEXPECTED_FILE_OR_DIRECTORY;
224 case PathParser::DirectoryFound:
225 case PathParser::FileFound:
226 LOG_ERROR(Service_FS, "%s already exists", full_path.c_str());
227 return ERROR_DIRECTORY_ALREADY_EXISTS;
228 }
229
230 if (FileUtil::CreateDir(mount_point + path.AsString())) {
231 return RESULT_SUCCESS;
232 }
233
234 LOG_CRITICAL(Service_FS, "(unreachable) Unknown error creating %s", mount_point.c_str());
235 return ResultCode(ErrorDescription::NoData, ErrorModule::FS, ErrorSummary::Canceled,
236 ErrorLevel::Status);
237}
238
239ResultCode SaveDataArchive::RenameDirectory(const Path& src_path, const Path& dest_path) const {
240 if (FileUtil::Rename(mount_point + src_path.AsString(), mount_point + dest_path.AsString()))
241 return RESULT_SUCCESS;
242
243 // TODO(yuriks): This code probably isn't right, it'll return a Status even if the file didn't
244 // exist or similar. Verify.
245 return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
246 ErrorSummary::NothingHappened, ErrorLevel::Status);
247}
248
249ResultVal<std::unique_ptr<DirectoryBackend>> SaveDataArchive::OpenDirectory(
250 const Path& path) const {
251 const PathParser path_parser(path);
252
253 if (!path_parser.IsValid()) {
254 LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
255 return ERROR_INVALID_PATH;
256 }
257
258 const auto full_path = path_parser.BuildHostPath(mount_point);
259
260 switch (path_parser.GetHostStatus(mount_point)) {
261 case PathParser::InvalidMountPoint:
262 LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
263 return ERROR_FILE_NOT_FOUND;
264 case PathParser::PathNotFound:
265 case PathParser::NotFound:
266 LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str());
267 return ERROR_PATH_NOT_FOUND;
268 case PathParser::FileInPath:
269 case PathParser::FileFound:
270 LOG_ERROR(Service_FS, "Unexpected file in path %s", full_path.c_str());
271 return ERROR_UNEXPECTED_FILE_OR_DIRECTORY;
272 }
273
274 auto directory = std::make_unique<DiskDirectory>(full_path);
275 return MakeResult<std::unique_ptr<DirectoryBackend>>(std::move(directory));
276}
277
278u64 SaveDataArchive::GetFreeBytes() const {
279 // TODO: Stubbed to return 1GiB
280 return 1024 * 1024 * 1024;
281}
282
283} // namespace FileSys
diff --git a/src/core/file_sys/savedata_archive.h b/src/core/file_sys/savedata_archive.h
new file mode 100644
index 000000000..2fb6c452a
--- /dev/null
+++ b/src/core/file_sys/savedata_archive.h
@@ -0,0 +1,43 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <string>
8#include "core/file_sys/archive_backend.h"
9#include "core/file_sys/directory_backend.h"
10#include "core/file_sys/file_backend.h"
11#include "core/hle/result.h"
12
13////////////////////////////////////////////////////////////////////////////////////////////////////
14// FileSys namespace
15
16namespace FileSys {
17
18/// Archive backend for general save data archive type (SaveData and SystemSaveData)
19class SaveDataArchive : public ArchiveBackend {
20public:
21 SaveDataArchive(const std::string& mount_point_) : mount_point(mount_point_) {}
22
23 std::string GetName() const override {
24 return "SaveDataArchive: " + mount_point;
25 }
26
27 ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path,
28 const Mode& mode) const override;
29 ResultCode DeleteFile(const Path& path) const override;
30 ResultCode RenameFile(const Path& src_path, const Path& dest_path) const override;
31 ResultCode DeleteDirectory(const Path& path) const override;
32 ResultCode DeleteDirectoryRecursively(const Path& path) const override;
33 ResultCode CreateFile(const Path& path, u64 size) const override;
34 ResultCode CreateDirectory(const Path& path) const override;
35 ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const override;
36 ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const override;
37 u64 GetFreeBytes() const override;
38
39protected:
40 std::string mount_point;
41};
42
43} // namespace FileSys
diff --git a/src/core/hle/applets/erreula.cpp b/src/core/hle/applets/erreula.cpp
index 14964427b..e1379ac4d 100644
--- a/src/core/hle/applets/erreula.cpp
+++ b/src/core/hle/applets/erreula.cpp
@@ -10,7 +10,7 @@ namespace HLE {
10namespace Applets { 10namespace Applets {
11 11
12ResultCode ErrEula::ReceiveParameter(const Service::APT::MessageParameter& parameter) { 12ResultCode ErrEula::ReceiveParameter(const Service::APT::MessageParameter& parameter) {
13 if (parameter.signal != static_cast<u32>(Service::APT::SignalType::LibAppJustStarted)) { 13 if (parameter.signal != static_cast<u32>(Service::APT::SignalType::Request)) {
14 LOG_ERROR(Service_APT, "unsupported signal %u", parameter.signal); 14 LOG_ERROR(Service_APT, "unsupported signal %u", parameter.signal);
15 UNIMPLEMENTED(); 15 UNIMPLEMENTED();
16 // TODO(Subv): Find the right error code 16 // TODO(Subv): Find the right error code
@@ -36,7 +36,7 @@ ResultCode ErrEula::ReceiveParameter(const Service::APT::MessageParameter& param
36 36
37 // Send the response message with the newly created SharedMemory 37 // Send the response message with the newly created SharedMemory
38 Service::APT::MessageParameter result; 38 Service::APT::MessageParameter result;
39 result.signal = static_cast<u32>(Service::APT::SignalType::LibAppFinished); 39 result.signal = static_cast<u32>(Service::APT::SignalType::Response);
40 result.buffer.clear(); 40 result.buffer.clear();
41 result.destination_id = static_cast<u32>(Service::APT::AppletId::Application); 41 result.destination_id = static_cast<u32>(Service::APT::AppletId::Application);
42 result.sender_id = static_cast<u32>(id); 42 result.sender_id = static_cast<u32>(id);
@@ -57,7 +57,7 @@ ResultCode ErrEula::StartImpl(const Service::APT::AppletStartupParameter& parame
57 Service::APT::MessageParameter message; 57 Service::APT::MessageParameter message;
58 message.buffer.resize(parameter.buffer.size()); 58 message.buffer.resize(parameter.buffer.size());
59 std::fill(message.buffer.begin(), message.buffer.end(), 0); 59 std::fill(message.buffer.begin(), message.buffer.end(), 0);
60 message.signal = static_cast<u32>(Service::APT::SignalType::LibAppClosed); 60 message.signal = static_cast<u32>(Service::APT::SignalType::WakeupByExit);
61 message.destination_id = static_cast<u32>(Service::APT::AppletId::Application); 61 message.destination_id = static_cast<u32>(Service::APT::AppletId::Application);
62 message.sender_id = static_cast<u32>(id); 62 message.sender_id = static_cast<u32>(id);
63 Service::APT::SendParameter(message); 63 Service::APT::SendParameter(message);
diff --git a/src/core/hle/applets/mii_selector.cpp b/src/core/hle/applets/mii_selector.cpp
index 53a8683a4..3455b9201 100644
--- a/src/core/hle/applets/mii_selector.cpp
+++ b/src/core/hle/applets/mii_selector.cpp
@@ -19,7 +19,7 @@ namespace HLE {
19namespace Applets { 19namespace Applets {
20 20
21ResultCode MiiSelector::ReceiveParameter(const Service::APT::MessageParameter& parameter) { 21ResultCode MiiSelector::ReceiveParameter(const Service::APT::MessageParameter& parameter) {
22 if (parameter.signal != static_cast<u32>(Service::APT::SignalType::LibAppJustStarted)) { 22 if (parameter.signal != static_cast<u32>(Service::APT::SignalType::Request)) {
23 LOG_ERROR(Service_APT, "unsupported signal %u", parameter.signal); 23 LOG_ERROR(Service_APT, "unsupported signal %u", parameter.signal);
24 UNIMPLEMENTED(); 24 UNIMPLEMENTED();
25 // TODO(Subv): Find the right error code 25 // TODO(Subv): Find the right error code
@@ -44,7 +44,7 @@ ResultCode MiiSelector::ReceiveParameter(const Service::APT::MessageParameter& p
44 44
45 // Send the response message with the newly created SharedMemory 45 // Send the response message with the newly created SharedMemory
46 Service::APT::MessageParameter result; 46 Service::APT::MessageParameter result;
47 result.signal = static_cast<u32>(Service::APT::SignalType::LibAppFinished); 47 result.signal = static_cast<u32>(Service::APT::SignalType::Response);
48 result.buffer.clear(); 48 result.buffer.clear();
49 result.destination_id = static_cast<u32>(Service::APT::AppletId::Application); 49 result.destination_id = static_cast<u32>(Service::APT::AppletId::Application);
50 result.sender_id = static_cast<u32>(id); 50 result.sender_id = static_cast<u32>(id);
@@ -73,7 +73,7 @@ ResultCode MiiSelector::StartImpl(const Service::APT::AppletStartupParameter& pa
73 Service::APT::MessageParameter message; 73 Service::APT::MessageParameter message;
74 message.buffer.resize(sizeof(MiiResult)); 74 message.buffer.resize(sizeof(MiiResult));
75 std::memcpy(message.buffer.data(), &result, message.buffer.size()); 75 std::memcpy(message.buffer.data(), &result, message.buffer.size());
76 message.signal = static_cast<u32>(Service::APT::SignalType::LibAppClosed); 76 message.signal = static_cast<u32>(Service::APT::SignalType::WakeupByExit);
77 message.destination_id = static_cast<u32>(Service::APT::AppletId::Application); 77 message.destination_id = static_cast<u32>(Service::APT::AppletId::Application);
78 message.sender_id = static_cast<u32>(id); 78 message.sender_id = static_cast<u32>(id);
79 Service::APT::SendParameter(message); 79 Service::APT::SendParameter(message);
diff --git a/src/core/hle/applets/swkbd.cpp b/src/core/hle/applets/swkbd.cpp
index 06ddf538b..1e21337f5 100644
--- a/src/core/hle/applets/swkbd.cpp
+++ b/src/core/hle/applets/swkbd.cpp
@@ -22,7 +22,7 @@ namespace HLE {
22namespace Applets { 22namespace Applets {
23 23
24ResultCode SoftwareKeyboard::ReceiveParameter(Service::APT::MessageParameter const& parameter) { 24ResultCode SoftwareKeyboard::ReceiveParameter(Service::APT::MessageParameter const& parameter) {
25 if (parameter.signal != static_cast<u32>(Service::APT::SignalType::LibAppJustStarted)) { 25 if (parameter.signal != static_cast<u32>(Service::APT::SignalType::Request)) {
26 LOG_ERROR(Service_APT, "unsupported signal %u", parameter.signal); 26 LOG_ERROR(Service_APT, "unsupported signal %u", parameter.signal);
27 UNIMPLEMENTED(); 27 UNIMPLEMENTED();
28 // TODO(Subv): Find the right error code 28 // TODO(Subv): Find the right error code
@@ -47,7 +47,7 @@ ResultCode SoftwareKeyboard::ReceiveParameter(Service::APT::MessageParameter con
47 47
48 // Send the response message with the newly created SharedMemory 48 // Send the response message with the newly created SharedMemory
49 Service::APT::MessageParameter result; 49 Service::APT::MessageParameter result;
50 result.signal = static_cast<u32>(Service::APT::SignalType::LibAppFinished); 50 result.signal = static_cast<u32>(Service::APT::SignalType::Response);
51 result.buffer.clear(); 51 result.buffer.clear();
52 result.destination_id = static_cast<u32>(Service::APT::AppletId::Application); 52 result.destination_id = static_cast<u32>(Service::APT::AppletId::Application);
53 result.sender_id = static_cast<u32>(id); 53 result.sender_id = static_cast<u32>(id);
@@ -108,7 +108,7 @@ void SoftwareKeyboard::Finalize() {
108 Service::APT::MessageParameter message; 108 Service::APT::MessageParameter message;
109 message.buffer.resize(sizeof(SoftwareKeyboardConfig)); 109 message.buffer.resize(sizeof(SoftwareKeyboardConfig));
110 std::memcpy(message.buffer.data(), &config, message.buffer.size()); 110 std::memcpy(message.buffer.data(), &config, message.buffer.size());
111 message.signal = static_cast<u32>(Service::APT::SignalType::LibAppClosed); 111 message.signal = static_cast<u32>(Service::APT::SignalType::WakeupByExit);
112 message.destination_id = static_cast<u32>(Service::APT::AppletId::Application); 112 message.destination_id = static_cast<u32>(Service::APT::AppletId::Application);
113 message.sender_id = static_cast<u32>(id); 113 message.sender_id = static_cast<u32>(id);
114 Service::APT::SendParameter(message); 114 Service::APT::SendParameter(message);
diff --git a/src/core/hle/kernel/event.cpp b/src/core/hle/kernel/event.cpp
index 1489c7002..3e116e3df 100644
--- a/src/core/hle/kernel/event.cpp
+++ b/src/core/hle/kernel/event.cpp
@@ -22,6 +22,11 @@ SharedPtr<Event> Event::Create(ResetType reset_type, std::string name) {
22 evt->reset_type = reset_type; 22 evt->reset_type = reset_type;
23 evt->name = std::move(name); 23 evt->name = std::move(name);
24 24
25 if (reset_type == ResetType::Pulse) {
26 LOG_ERROR(Kernel, "Unimplemented event reset type Pulse");
27 UNIMPLEMENTED();
28 }
29
25 return evt; 30 return evt;
26} 31}
27 32
diff --git a/src/core/hle/kernel/timer.cpp b/src/core/hle/kernel/timer.cpp
index a9f98223c..eac181f4e 100644
--- a/src/core/hle/kernel/timer.cpp
+++ b/src/core/hle/kernel/timer.cpp
@@ -31,6 +31,11 @@ SharedPtr<Timer> Timer::Create(ResetType reset_type, std::string name) {
31 timer->interval_delay = 0; 31 timer->interval_delay = 0;
32 timer->callback_handle = timer_callback_handle_table.Create(timer).MoveFrom(); 32 timer->callback_handle = timer_callback_handle_table.Create(timer).MoveFrom();
33 33
34 if (reset_type == ResetType::Pulse) {
35 LOG_ERROR(Kernel, "Unimplemented timer reset type Pulse");
36 UNIMPLEMENTED();
37 }
38
34 return timer; 39 return timer;
35} 40}
36 41
diff --git a/src/core/hle/result.h b/src/core/hle/result.h
index 7f8d8e00d..f7356f9d8 100644
--- a/src/core/hle/result.h
+++ b/src/core/hle/result.h
@@ -20,15 +20,24 @@ enum class ErrorDescription : u32 {
20 OS_InvalidBufferDescriptor = 48, 20 OS_InvalidBufferDescriptor = 48,
21 WrongAddress = 53, 21 WrongAddress = 53,
22 FS_ArchiveNotMounted = 101, 22 FS_ArchiveNotMounted = 101,
23 FS_FileNotFound = 112,
24 FS_PathNotFound = 113,
23 FS_NotFound = 120, 25 FS_NotFound = 120,
26 FS_FileAlreadyExists = 180,
27 FS_DirectoryAlreadyExists = 185,
24 FS_AlreadyExists = 190, 28 FS_AlreadyExists = 190,
25 FS_InvalidOpenFlags = 230, 29 FS_InvalidOpenFlags = 230,
30 FS_DirectoryNotEmpty = 240,
26 FS_NotAFile = 250, 31 FS_NotAFile = 250,
27 FS_NotFormatted = 340, ///< This is used by the FS service when creating a SaveData archive 32 FS_NotFormatted = 340, ///< This is used by the FS service when creating a SaveData archive
28 OutofRangeOrMisalignedAddress = 33 OutofRangeOrMisalignedAddress =
29 513, // TODO(purpasmart): Check if this name fits its actual usage 34 513, // TODO(purpasmart): Check if this name fits its actual usage
30 GPU_FirstInitialization = 519, 35 GPU_FirstInitialization = 519,
36 FS_InvalidReadFlag = 700,
31 FS_InvalidPath = 702, 37 FS_InvalidPath = 702,
38 FS_WriteBeyondEnd = 705,
39 FS_UnsupportedOpenFlags = 760,
40 FS_UnexpectedFileOrDirectory = 770,
32 InvalidSection = 1000, 41 InvalidSection = 1000,
33 TooLarge = 1001, 42 TooLarge = 1001,
34 NotAuthorized = 1002, 43 NotAuthorized = 1002,
diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp
index c4bd65986..31e5e07b2 100644
--- a/src/core/hle/service/apt/apt.cpp
+++ b/src/core/hle/service/apt/apt.cpp
@@ -396,6 +396,15 @@ void StartLibraryApplet(Service::Interface* self) {
396 cmd_buff[1] = applet->Start(parameter).raw; 396 cmd_buff[1] = applet->Start(parameter).raw;
397} 397}
398 398
399void CancelLibraryApplet(Service::Interface* self) {
400 u32* cmd_buff = Kernel::GetCommandBuffer();
401 u32 exiting = cmd_buff[1] & 0xFF;
402
403 cmd_buff[1] = 1; // TODO: Find the return code meaning
404
405 LOG_WARNING(Service_APT, "(STUBBED) called exiting=%u", exiting);
406}
407
399void SetScreenCapPostPermission(Service::Interface* self) { 408void SetScreenCapPostPermission(Service::Interface* self) {
400 u32* cmd_buff = Kernel::GetCommandBuffer(); 409 u32* cmd_buff = Kernel::GetCommandBuffer();
401 410
@@ -523,7 +532,7 @@ void Init() {
523 notification_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "APT_U:Notification"); 532 notification_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "APT_U:Notification");
524 parameter_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "APT_U:Start"); 533 parameter_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "APT_U:Start");
525 534
526 next_parameter.signal = static_cast<u32>(SignalType::AppJustStarted); 535 next_parameter.signal = static_cast<u32>(SignalType::Wakeup);
527 next_parameter.destination_id = 0x300; 536 next_parameter.destination_id = 0x300;
528} 537}
529 538
diff --git a/src/core/hle/service/apt/apt.h b/src/core/hle/service/apt/apt.h
index a118cda1f..44dbd8757 100644
--- a/src/core/hle/service/apt/apt.h
+++ b/src/core/hle/service/apt/apt.h
@@ -46,12 +46,23 @@ static_assert(sizeof(CaptureBufferInfo) == 0x20, "CaptureBufferInfo struct has i
46/// Signals used by APT functions 46/// Signals used by APT functions
47enum class SignalType : u32 { 47enum class SignalType : u32 {
48 None = 0x0, 48 None = 0x0,
49 AppJustStarted = 0x1, 49 Wakeup = 0x1,
50 LibAppJustStarted = 0x2, 50 Request = 0x2,
51 LibAppFinished = 0x3, 51 Response = 0x3,
52 LibAppClosed = 0xA, 52 Exit = 0x4,
53 ReturningToApp = 0xB, 53 Message = 0x5,
54 ExitingApp = 0xC, 54 HomeButtonSingle = 0x6,
55 HomeButtonDouble = 0x7,
56 DspSleep = 0x8,
57 DspWakeup = 0x9,
58 WakeupByExit = 0xA,
59 WakeupByPause = 0xB,
60 WakeupByCancel = 0xC,
61 WakeupByCancelAll = 0xD,
62 WakeupByPowerButtonClick = 0xE,
63 WakeupToJumpHome = 0xF,
64 RequestForSysApplet = 0x10,
65 WakeupToLaunchApplication = 0x11,
55}; 66};
56 67
57/// App Id's used by APT functions 68/// App Id's used by APT functions
@@ -381,6 +392,17 @@ void PreloadLibraryApplet(Service::Interface* self);
381void StartLibraryApplet(Service::Interface* self); 392void StartLibraryApplet(Service::Interface* self);
382 393
383/** 394/**
395 * APT::CancelLibraryApplet service function
396 * Inputs:
397 * 0 : Command header [0x003B0040]
398 * 1 : u8, Application exiting (0 = not exiting, 1 = exiting)
399 * Outputs:
400 * 0 : Header code
401 * 1 : Result code
402 */
403void CancelLibraryApplet(Service::Interface* self);
404
405/**
384 * APT::GetStartupArgument service function 406 * APT::GetStartupArgument service function
385 * Inputs: 407 * Inputs:
386 * 1 : Parameter Size (capped to 0x300) 408 * 1 : Parameter Size (capped to 0x300)
diff --git a/src/core/hle/service/apt/apt_a.cpp b/src/core/hle/service/apt/apt_a.cpp
index f27ad91b7..a7a0c8a41 100644
--- a/src/core/hle/service/apt/apt_a.cpp
+++ b/src/core/hle/service/apt/apt_a.cpp
@@ -25,7 +25,7 @@ const Interface::FunctionInfo FunctionTable[] = {
25 {0x00160040, PreloadLibraryApplet, "PreloadLibraryApplet"}, 25 {0x00160040, PreloadLibraryApplet, "PreloadLibraryApplet"},
26 {0x00180040, PrepareToStartLibraryApplet, "PrepareToStartLibraryApplet"}, 26 {0x00180040, PrepareToStartLibraryApplet, "PrepareToStartLibraryApplet"},
27 {0x001E0084, StartLibraryApplet, "StartLibraryApplet"}, 27 {0x001E0084, StartLibraryApplet, "StartLibraryApplet"},
28 {0x003B0040, nullptr, "CancelLibraryApplet?"}, 28 {0x003B0040, CancelLibraryApplet, "CancelLibraryApplet"},
29 {0x003E0080, nullptr, "ReplySleepQuery"}, 29 {0x003E0080, nullptr, "ReplySleepQuery"},
30 {0x00430040, NotifyToWait, "NotifyToWait?"}, 30 {0x00430040, NotifyToWait, "NotifyToWait?"},
31 {0x00440000, GetSharedFont, "GetSharedFont?"}, 31 {0x00440000, GetSharedFont, "GetSharedFont?"},
diff --git a/src/core/hle/service/apt/apt_u.cpp b/src/core/hle/service/apt/apt_u.cpp
index d6ad42e21..a731c39f6 100644
--- a/src/core/hle/service/apt/apt_u.cpp
+++ b/src/core/hle/service/apt/apt_u.cpp
@@ -67,7 +67,7 @@ const Interface::FunctionInfo FunctionTable[] = {
67 {0x00380040, nullptr, "PreloadResidentApplet"}, 67 {0x00380040, nullptr, "PreloadResidentApplet"},
68 {0x00390040, nullptr, "PrepareToStartResidentApplet"}, 68 {0x00390040, nullptr, "PrepareToStartResidentApplet"},
69 {0x003A0044, nullptr, "StartResidentApplet"}, 69 {0x003A0044, nullptr, "StartResidentApplet"},
70 {0x003B0040, nullptr, "CancelLibraryApplet"}, 70 {0x003B0040, CancelLibraryApplet, "CancelLibraryApplet"},
71 {0x003C0042, nullptr, "SendDspSleep"}, 71 {0x003C0042, nullptr, "SendDspSleep"},
72 {0x003D0042, nullptr, "SendDspWakeUp"}, 72 {0x003D0042, nullptr, "SendDspWakeUp"},
73 {0x003E0080, nullptr, "ReplySleepQuery"}, 73 {0x003E0080, nullptr, "ReplySleepQuery"},
diff --git a/src/core/hle/service/cfg/cfg.cpp b/src/core/hle/service/cfg/cfg.cpp
index 24eee6903..d554c3f54 100644
--- a/src/core/hle/service/cfg/cfg.cpp
+++ b/src/core/hle/service/cfg/cfg.cpp
@@ -45,7 +45,8 @@ static_assert(sizeof(SaveFileConfig) == 0x455C,
45enum ConfigBlockID { 45enum ConfigBlockID {
46 StereoCameraSettingsBlockID = 0x00050005, 46 StereoCameraSettingsBlockID = 0x00050005,
47 SoundOutputModeBlockID = 0x00070001, 47 SoundOutputModeBlockID = 0x00070001,
48 ConsoleUniqueIDBlockID = 0x00090001, 48 ConsoleUniqueID1BlockID = 0x00090000,
49 ConsoleUniqueID2BlockID = 0x00090001,
49 UsernameBlockID = 0x000A0000, 50 UsernameBlockID = 0x000A0000,
50 BirthdayBlockID = 0x000A0001, 51 BirthdayBlockID = 0x000A0001,
51 LanguageBlockID = 0x000A0002, 52 LanguageBlockID = 0x000A0002,
@@ -359,7 +360,7 @@ ResultCode CreateConfigInfoBlk(u32 block_id, u16 size, u16 flags, const void* da
359} 360}
360 361
361ResultCode DeleteConfigNANDSaveFile() { 362ResultCode DeleteConfigNANDSaveFile() {
362 FileSys::Path path("config"); 363 FileSys::Path path("/config");
363 return Service::FS::DeleteFileFromArchive(cfg_system_save_data_archive, path); 364 return Service::FS::DeleteFileFromArchive(cfg_system_save_data_archive, path);
364} 365}
365 366
@@ -368,7 +369,7 @@ ResultCode UpdateConfigNANDSavegame() {
368 mode.write_flag.Assign(1); 369 mode.write_flag.Assign(1);
369 mode.create_flag.Assign(1); 370 mode.create_flag.Assign(1);
370 371
371 FileSys::Path path("config"); 372 FileSys::Path path("/config");
372 373
373 auto config_result = Service::FS::OpenFileFromArchive(cfg_system_save_data_archive, path, mode); 374 auto config_result = Service::FS::OpenFileFromArchive(cfg_system_save_data_archive, path, mode);
374 ASSERT_MSG(config_result.Succeeded(), "could not open file"); 375 ASSERT_MSG(config_result.Succeeded(), "could not open file");
@@ -382,8 +383,9 @@ ResultCode UpdateConfigNANDSavegame() {
382ResultCode FormatConfig() { 383ResultCode FormatConfig() {
383 ResultCode res = DeleteConfigNANDSaveFile(); 384 ResultCode res = DeleteConfigNANDSaveFile();
384 // The delete command fails if the file doesn't exist, so we have to check that too 385 // The delete command fails if the file doesn't exist, so we have to check that too
385 if (!res.IsSuccess() && res.description != ErrorDescription::FS_NotFound) 386 if (!res.IsSuccess() && res.description != ErrorDescription::FS_FileNotFound) {
386 return res; 387 return res;
388 }
387 // Delete the old data 389 // Delete the old data
388 cfg_config_file_buffer.fill(0); 390 cfg_config_file_buffer.fill(0);
389 // Create the header 391 // Create the header
@@ -409,7 +411,12 @@ ResultCode FormatConfig() {
409 if (!res.IsSuccess()) 411 if (!res.IsSuccess())
410 return res; 412 return res;
411 413
412 res = CreateConfigInfoBlk(ConsoleUniqueIDBlockID, sizeof(CONSOLE_UNIQUE_ID), 0xE, 414 res = CreateConfigInfoBlk(ConsoleUniqueID1BlockID, sizeof(CONSOLE_UNIQUE_ID), 0xE,
415 &CONSOLE_UNIQUE_ID);
416 if (!res.IsSuccess())
417 return res;
418
419 res = CreateConfigInfoBlk(ConsoleUniqueID2BlockID, sizeof(CONSOLE_UNIQUE_ID), 0xE,
413 &CONSOLE_UNIQUE_ID); 420 &CONSOLE_UNIQUE_ID);
414 if (!res.IsSuccess()) 421 if (!res.IsSuccess())
415 return res; 422 return res;
@@ -504,7 +511,7 @@ ResultCode LoadConfigNANDSaveFile() {
504 511
505 cfg_system_save_data_archive = *archive_result; 512 cfg_system_save_data_archive = *archive_result;
506 513
507 FileSys::Path config_path("config"); 514 FileSys::Path config_path("/config");
508 FileSys::Mode open_mode = {}; 515 FileSys::Mode open_mode = {};
509 open_mode.read_flag.Assign(1); 516 open_mode.read_flag.Assign(1);
510 517
diff --git a/src/core/hle/service/err_f.cpp b/src/core/hle/service/err_f.cpp
index 3ca4f98de..9905757c7 100644
--- a/src/core/hle/service/err_f.cpp
+++ b/src/core/hle/service/err_f.cpp
@@ -2,9 +2,15 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <array>
6#include <chrono>
7#include <iomanip>
8#include <sstream>
9
5#include "common/bit_field.h" 10#include "common/bit_field.h"
6#include "common/common_types.h" 11#include "common/common_types.h"
7#include "common/logging/log.h" 12#include "common/logging/log.h"
13#include "core/hle/result.h"
8#include "core/hle/service/err_f.h" 14#include "core/hle/service/err_f.h"
9 15
10//////////////////////////////////////////////////////////////////////////////////////////////////// 16////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -12,12 +18,45 @@
12 18
13namespace ERR_F { 19namespace ERR_F {
14 20
15enum { 21enum class FatalErrType : u32 {
16 ErrSpecifier0 = 0, 22 Generic = 0,
17 ErrSpecifier1 = 1, 23 Corrupted = 1,
18 ErrSpecifier3 = 3, 24 CardRemoved = 2,
19 ErrSpecifier4 = 4, 25 Exception = 3,
26 ResultFailure = 4,
27 Logged = 5,
28};
29
30enum class ExceptionType : u32 {
31 PrefetchAbort = 0,
32 DataAbort = 1,
33 Undefined = 2,
34 VectorFP = 3,
35};
36
37struct ExceptionInfo {
38 u8 exception_type;
39 INSERT_PADDING_BYTES(3);
40 u32 sr;
41 u32 ar;
42 u32 fpexc;
43 u32 fpinst;
44 u32 fpinst2;
45};
46static_assert(sizeof(ExceptionInfo) == 0x18, "ExceptionInfo struct has incorrect size");
47
48struct ExceptionContext final {
49 std::array<u32, 16> arm_regs;
50 u32 cpsr;
20}; 51};
52static_assert(sizeof(ExceptionContext) == 0x44, "ExceptionContext struct has incorrect size");
53
54struct ExceptionData {
55 ExceptionInfo exception_info;
56 ExceptionContext exception_context;
57 INSERT_PADDING_WORDS(1);
58};
59static_assert(sizeof(ExceptionData) == 0x60, "ExceptionData struct has incorrect size");
21 60
22// This is used instead of ResultCode from result.h 61// This is used instead of ResultCode from result.h
23// because we can't have non-trivial data members in unions. 62// because we can't have non-trivial data members in unions.
@@ -30,150 +69,191 @@ union RSL {
30 BitField<27, 5, u32> level; 69 BitField<27, 5, u32> level;
31}; 70};
32 71
33union ErrInfo { 72struct ErrInfo {
34 u8 specifier; 73 struct ErrInfoCommon {
35 74 u8 specifier; // 0x0
36 struct { 75 u8 rev_high; // 0x1
37 u8 specifier; // 0x0 76 u16 rev_low; // 0x2
38 u8 rev_high; // 0x1 77 RSL result_code; // 0x4
39 u16 rev_low; // 0x2 78 u32 pc_address; // 0x8
40 RSL result_code; // 0x4 79 u32 pid; // 0xC
41 u32 address; // 0x8 80 u32 title_id_low; // 0x10
42 INSERT_PADDING_BYTES(4); // 0xC 81 u32 title_id_high; // 0x14
43 u32 pid_low; // 0x10 82 u32 app_title_id_low; // 0x18
44 u32 pid_high; // 0x14 83 u32 app_title_id_high; // 0x1C
45 u32 aid_low; // 0x18 84 } errinfo_common;
46 u32 aid_high; // 0x1C 85 static_assert(sizeof(ErrInfoCommon) == 0x20, "ErrInfoCommon struct has incorrect size");
47 } errtype1; 86
48 87 union {
49 struct { 88 struct {
50 u8 specifier; // 0x0 89 char data[0x60]; // 0x20
51 u8 rev_high; // 0x1 90 } generic;
52 u16 rev_low; // 0x2 91
53 INSERT_PADDING_BYTES(0xC); // 0x4 92 struct {
54 u32 pid_low; // 0x10 93 ExceptionData exception_data; // 0x20
55 u32 pid_high; // 0x14 94 } exception;
56 u32 aid_low; // 0x18 95
57 u32 aid_high; // 0x1C 96 struct {
58 u8 error_type; // 0x20 97 char message[0x60]; // 0x20
59 INSERT_PADDING_BYTES(3); // 0x21 98 } result_failure;
60 u32 fault_status_reg; // 0x24 99 };
61 u32 fault_addr; // 0x28
62 u32 fpexc; // 0x2C
63 u32 finst; // 0x30
64 u32 finst2; // 0x34
65 INSERT_PADDING_BYTES(0x34); // 0x38
66 u32 sp; // 0x6C
67 u32 pc; // 0x70
68 u32 lr; // 0x74
69 u32 cpsr; // 0x78
70 } errtype3;
71
72 struct {
73 u8 specifier; // 0x0
74 u8 rev_high; // 0x1
75 u16 rev_low; // 0x2
76 RSL result_code; // 0x4
77 INSERT_PADDING_BYTES(8); // 0x8
78 u32 pid_low; // 0x10
79 u32 pid_high; // 0x14
80 u32 aid_low; // 0x18
81 u32 aid_high; // 0x1C
82 char debug_string1[0x2E]; // 0x20
83 char debug_string2[0x2E]; // 0x4E
84 } errtype4;
85}; 100};
86 101
87enum { PrefetchAbort = 0, DataAbort = 1, UndefInstr = 2, VectorFP = 3 }; 102static std::string GetErrType(u8 type_code) {
103 switch (static_cast<FatalErrType>(type_code)) {
104 case FatalErrType::Generic:
105 return "Generic";
106 case FatalErrType::Corrupted:
107 return "Corrupted";
108 case FatalErrType::CardRemoved:
109 return "CardRemoved";
110 case FatalErrType::Exception:
111 return "Exception";
112 case FatalErrType::ResultFailure:
113 return "ResultFailure";
114 case FatalErrType::Logged:
115 return "Logged";
116 default:
117 return "Unknown Error Type";
118 }
119}
88 120
89static std::string GetErrInfo3Type(u8 type_code) { 121static std::string GetExceptionType(u8 type_code) {
90 switch (type_code) { 122 switch (static_cast<ExceptionType>(type_code)) {
91 case PrefetchAbort: 123 case ExceptionType::PrefetchAbort:
92 return "Prefetch Abort"; 124 return "Prefetch Abort";
93 case DataAbort: 125 case ExceptionType::DataAbort:
94 return "Data Abort"; 126 return "Data Abort";
95 case UndefInstr: 127 case ExceptionType::Undefined:
96 return "Undefined Instruction"; 128 return "Undefined Exception";
97 case VectorFP: 129 case ExceptionType::VectorFP:
98 return "Vector Floating Point"; 130 return "Vector Floating Point Exception";
99 default: 131 default:
100 return "unknown"; 132 return "Unknown Exception Type";
101 } 133 }
102} 134}
103 135
136static std::string GetCurrentSystemTime() {
137 auto now = std::chrono::system_clock::now();
138 auto time = std::chrono::system_clock::to_time_t(now);
139
140 std::stringstream time_stream;
141 time_stream << std::put_time(std::localtime(&time), "%Y/%m/%d %H:%M:%S");
142 return time_stream.str();
143}
144
145static void LogGenericInfo(const ErrInfo::ErrInfoCommon& errinfo_common) {
146 LOG_CRITICAL(Service_ERR, "PID: 0x%08X", errinfo_common.pid);
147 LOG_CRITICAL(Service_ERR, "REV: 0x%08X_0x%08X", errinfo_common.rev_high,
148 errinfo_common.rev_low);
149 LOG_CRITICAL(Service_ERR, "TID: 0x%08X_0x%08X", errinfo_common.title_id_high,
150 errinfo_common.title_id_low);
151 LOG_CRITICAL(Service_ERR, "AID: 0x%08X_0x%08X", errinfo_common.app_title_id_high,
152 errinfo_common.app_title_id_low);
153 LOG_CRITICAL(Service_ERR, "ADR: 0x%08X", errinfo_common.pc_address);
154
155 LOG_CRITICAL(Service_ERR, "RSL: 0x%08X", errinfo_common.result_code.raw);
156 LOG_CRITICAL(Service_ERR, " Level: %u", errinfo_common.result_code.level.Value());
157 LOG_CRITICAL(Service_ERR, " Summary: %u", errinfo_common.result_code.summary.Value());
158 LOG_CRITICAL(Service_ERR, " Module: %u", errinfo_common.result_code.module.Value());
159 LOG_CRITICAL(Service_ERR, " Desc: %u", errinfo_common.result_code.description.Value());
160}
161
162/* ThrowFatalError function
163 * Inputs:
164 * 0 : Header code [0x00010800]
165 * 1-32 : FatalErrInfo
166 * Outputs:
167 * 0 : Header code
168 * 1 : Result code
169 */
104static void ThrowFatalError(Service::Interface* self) { 170static void ThrowFatalError(Service::Interface* self) {
105 u32* cmd_buff = Kernel::GetCommandBuffer(); 171 u32* cmd_buff = Kernel::GetCommandBuffer();
106 172
107 LOG_CRITICAL(Service_ERR, "Fatal error!"); 173 LOG_CRITICAL(Service_ERR, "Fatal error");
108 const ErrInfo* errinfo = reinterpret_cast<ErrInfo*>(&cmd_buff[1]); 174 const ErrInfo* errinfo = reinterpret_cast<ErrInfo*>(&cmd_buff[1]);
175 LOG_CRITICAL(Service_ERR, "Fatal error type: %s",
176 GetErrType(errinfo->errinfo_common.specifier).c_str());
109 177
110 switch (errinfo->specifier) { 178 // Generic Info
111 case ErrSpecifier0: 179 LogGenericInfo(errinfo->errinfo_common);
112 case ErrSpecifier1: { 180
113 const auto& errtype = errinfo->errtype1; 181 switch (static_cast<FatalErrType>(errinfo->errinfo_common.specifier)) {
114 LOG_CRITICAL(Service_ERR, "PID: 0x%08X_0x%08X", errtype.pid_low, errtype.pid_high); 182 case FatalErrType::Generic:
115 LOG_CRITICAL(Service_ERR, "REV: %d", errtype.rev_low | (errtype.rev_high << 16)); 183 case FatalErrType::Corrupted:
116 LOG_CRITICAL(Service_ERR, "AID: 0x%08X_0x%08X", errtype.aid_low, errtype.aid_high); 184 case FatalErrType::CardRemoved:
117 LOG_CRITICAL(Service_ERR, "ADR: 0x%08X", errtype.address); 185 case FatalErrType::Logged: {
118 186 LOG_CRITICAL(Service_ERR, "Datetime: %s", GetCurrentSystemTime().c_str());
119 LOG_CRITICAL(Service_ERR, "RSL: 0x%08X", errtype.result_code.raw);
120 LOG_CRITICAL(Service_ERR, " Level: %u", errtype.result_code.level.Value());
121 LOG_CRITICAL(Service_ERR, " Summary: %u", errtype.result_code.summary.Value());
122 LOG_CRITICAL(Service_ERR, " Module: %u", errtype.result_code.module.Value());
123 LOG_CRITICAL(Service_ERR, " Desc: %u", errtype.result_code.description.Value());
124 break; 187 break;
125 } 188 }
189 case FatalErrType::Exception: {
190 const auto& errtype = errinfo->exception;
191
192 // Register Info
193 LOG_CRITICAL(Service_ERR, "ARM Registers:");
194 for (u32 index = 0; index < errtype.exception_data.exception_context.arm_regs.size();
195 ++index) {
196 if (index < 13) {
197 LOG_DEBUG(Service_ERR, "r%u=0x%08X", index,
198 errtype.exception_data.exception_context.arm_regs.at(index));
199 } else if (index == 13) {
200 LOG_CRITICAL(Service_ERR, "SP=0x%08X",
201 errtype.exception_data.exception_context.arm_regs.at(index));
202 } else if (index == 14) {
203 LOG_CRITICAL(Service_ERR, "LR=0x%08X",
204 errtype.exception_data.exception_context.arm_regs.at(index));
205 } else if (index == 15) {
206 LOG_CRITICAL(Service_ERR, "PC=0x%08X",
207 errtype.exception_data.exception_context.arm_regs.at(index));
208 }
209 }
210 LOG_CRITICAL(Service_ERR, "CPSR=0x%08X", errtype.exception_data.exception_context.cpsr);
126 211
127 case ErrSpecifier3: { 212 // Exception Info
128 const auto& errtype = errinfo->errtype3; 213 LOG_CRITICAL(
129 LOG_CRITICAL(Service_ERR, "PID: 0x%08X_0x%08X", errtype.pid_low, errtype.pid_high); 214 Service_ERR, "EXCEPTION TYPE: %s",
130 LOG_CRITICAL(Service_ERR, "REV: %d", errtype.rev_low | (errtype.rev_high << 16)); 215 GetExceptionType(errtype.exception_data.exception_info.exception_type).c_str());
131 LOG_CRITICAL(Service_ERR, "AID: 0x%08X_0x%08X", errtype.aid_low, errtype.aid_high); 216 switch (static_cast<ExceptionType>(errtype.exception_data.exception_info.exception_type)) {
132 LOG_CRITICAL(Service_ERR, "TYPE: %s", GetErrInfo3Type(errtype.error_type).c_str()); 217 case ExceptionType::PrefetchAbort:
133 218 LOG_CRITICAL(Service_ERR, "IFSR: 0x%08X", errtype.exception_data.exception_info.sr);
134 LOG_CRITICAL(Service_ERR, "PC: 0x%08X", errtype.pc); 219 LOG_CRITICAL(Service_ERR, "r15: 0x%08X", errtype.exception_data.exception_info.ar);
135 LOG_CRITICAL(Service_ERR, "LR: 0x%08X", errtype.lr); 220 case ExceptionType::DataAbort:
136 LOG_CRITICAL(Service_ERR, "SP: 0x%08X", errtype.sp); 221 LOG_CRITICAL(Service_ERR, "DFSR: 0x%08X", errtype.exception_data.exception_info.sr);
137 LOG_CRITICAL(Service_ERR, "CPSR: 0x%08X", errtype.cpsr); 222 LOG_CRITICAL(Service_ERR, "DFAR: 0x%08X", errtype.exception_data.exception_info.ar);
138
139 switch (errtype.error_type) {
140 case PrefetchAbort:
141 case DataAbort:
142 LOG_CRITICAL(Service_ERR, "Fault Address: 0x%08X", errtype.fault_addr);
143 LOG_CRITICAL(Service_ERR, "Fault Status Register: 0x%08X", errtype.fault_status_reg);
144 break; 223 break;
145 case VectorFP: 224 case ExceptionType::VectorFP:
146 LOG_CRITICAL(Service_ERR, "FPEXC: 0x%08X", errtype.fpexc); 225 LOG_CRITICAL(Service_ERR, "FPEXC: 0x%08X",
147 LOG_CRITICAL(Service_ERR, "FINST: 0x%08X", errtype.finst); 226 errtype.exception_data.exception_info.fpinst);
148 LOG_CRITICAL(Service_ERR, "FINST2: 0x%08X", errtype.finst2); 227 LOG_CRITICAL(Service_ERR, "FINST: 0x%08X",
228 errtype.exception_data.exception_info.fpinst);
229 LOG_CRITICAL(Service_ERR, "FINST2: 0x%08X",
230 errtype.exception_data.exception_info.fpinst2);
149 break; 231 break;
150 } 232 }
233 LOG_CRITICAL(Service_ERR, "Datetime: %s", GetCurrentSystemTime().c_str());
151 break; 234 break;
152 } 235 }
153 236
154 case ErrSpecifier4: { 237 case FatalErrType::ResultFailure: {
155 const auto& errtype = errinfo->errtype4; 238 const auto& errtype = errinfo->result_failure;
156 LOG_CRITICAL(Service_ERR, "PID: 0x%08X_0x%08X", errtype.pid_low, errtype.pid_high);
157 LOG_CRITICAL(Service_ERR, "REV: %d", errtype.rev_low | (errtype.rev_high << 16));
158 LOG_CRITICAL(Service_ERR, "AID: 0x%08X_0x%08X", errtype.aid_low, errtype.aid_high);
159 239
160 LOG_CRITICAL(Service_ERR, "RSL: 0x%08X", errtype.result_code.raw); 240 // Failure Message
161 LOG_CRITICAL(Service_ERR, " Level: %u", errtype.result_code.level.Value()); 241 LOG_CRITICAL(Service_ERR, "Failure Message: %s", errtype.message);
162 LOG_CRITICAL(Service_ERR, " Summary: %u", errtype.result_code.summary.Value()); 242 LOG_CRITICAL(Service_ERR, "Datetime: %s", GetCurrentSystemTime().c_str());
163 LOG_CRITICAL(Service_ERR, " Module: %u", errtype.result_code.module.Value());
164 LOG_CRITICAL(Service_ERR, " Desc: %u", errtype.result_code.description.Value());
165
166 LOG_CRITICAL(Service_ERR, "%s", errtype.debug_string1);
167 LOG_CRITICAL(Service_ERR, "%s", errtype.debug_string2);
168 break; 243 break;
169 } 244 }
170 }
171 245
172 cmd_buff[1] = 0; // No error 246 } // switch FatalErrType
247
248 cmd_buff[0] = IPC::MakeHeader(0x1, 1, 0);
249 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
173} 250}
174 251
175const Interface::FunctionInfo FunctionTable[] = { 252const Interface::FunctionInfo FunctionTable[] = {
253 // clang-format off
176 {0x00010800, ThrowFatalError, "ThrowFatalError"}, 254 {0x00010800, ThrowFatalError, "ThrowFatalError"},
255 {0x00020042, nullptr, "SetUserString"},
256 // clang-format on
177}; 257};
178 258
179//////////////////////////////////////////////////////////////////////////////////////////////////// 259////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp
index 7f9696bfb..4c29784e8 100644
--- a/src/core/hle/service/fs/archive.cpp
+++ b/src/core/hle/service/fs/archive.cpp
@@ -15,9 +15,10 @@
15#include "common/logging/log.h" 15#include "common/logging/log.h"
16#include "core/file_sys/archive_backend.h" 16#include "core/file_sys/archive_backend.h"
17#include "core/file_sys/archive_extsavedata.h" 17#include "core/file_sys/archive_extsavedata.h"
18#include "core/file_sys/archive_ncch.h"
18#include "core/file_sys/archive_savedata.h" 19#include "core/file_sys/archive_savedata.h"
19#include "core/file_sys/archive_savedatacheck.h"
20#include "core/file_sys/archive_sdmc.h" 20#include "core/file_sys/archive_sdmc.h"
21#include "core/file_sys/archive_sdmcwriteonly.h"
21#include "core/file_sys/archive_systemsavedata.h" 22#include "core/file_sys/archive_systemsavedata.h"
22#include "core/file_sys/directory_backend.h" 23#include "core/file_sys/directory_backend.h"
23#include "core/file_sys/file_backend.h" 24#include "core/file_sys/file_backend.h"
@@ -338,17 +339,11 @@ ResultCode RenameFileBetweenArchives(ArchiveHandle src_archive_handle,
338 return ERR_INVALID_ARCHIVE_HANDLE; 339 return ERR_INVALID_ARCHIVE_HANDLE;
339 340
340 if (src_archive == dest_archive) { 341 if (src_archive == dest_archive) {
341 if (src_archive->RenameFile(src_path, dest_path)) 342 return src_archive->RenameFile(src_path, dest_path);
342 return RESULT_SUCCESS;
343 } else { 343 } else {
344 // TODO: Implement renaming across archives 344 // TODO: Implement renaming across archives
345 return UnimplementedFunction(ErrorModule::FS); 345 return UnimplementedFunction(ErrorModule::FS);
346 } 346 }
347
348 // TODO(yuriks): This code probably isn't right, it'll return a Status even if the file didn't
349 // exist or similar. Verify.
350 return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
351 ErrorSummary::NothingHappened, ErrorLevel::Status);
352} 347}
353 348
354ResultCode DeleteDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) { 349ResultCode DeleteDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) {
@@ -356,10 +351,7 @@ ResultCode DeleteDirectoryFromArchive(ArchiveHandle archive_handle, const FileSy
356 if (archive == nullptr) 351 if (archive == nullptr)
357 return ERR_INVALID_ARCHIVE_HANDLE; 352 return ERR_INVALID_ARCHIVE_HANDLE;
358 353
359 if (archive->DeleteDirectory(path)) 354 return archive->DeleteDirectory(path);
360 return RESULT_SUCCESS;
361 return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
362 ErrorSummary::Canceled, ErrorLevel::Status);
363} 355}
364 356
365ResultCode DeleteDirectoryRecursivelyFromArchive(ArchiveHandle archive_handle, 357ResultCode DeleteDirectoryRecursivelyFromArchive(ArchiveHandle archive_handle,
@@ -368,10 +360,7 @@ ResultCode DeleteDirectoryRecursivelyFromArchive(ArchiveHandle archive_handle,
368 if (archive == nullptr) 360 if (archive == nullptr)
369 return ERR_INVALID_ARCHIVE_HANDLE; 361 return ERR_INVALID_ARCHIVE_HANDLE;
370 362
371 if (archive->DeleteDirectoryRecursively(path)) 363 return archive->DeleteDirectoryRecursively(path);
372 return RESULT_SUCCESS;
373 return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
374 ErrorSummary::Canceled, ErrorLevel::Status);
375} 364}
376 365
377ResultCode CreateFileInArchive(ArchiveHandle archive_handle, const FileSys::Path& path, 366ResultCode CreateFileInArchive(ArchiveHandle archive_handle, const FileSys::Path& path,
@@ -388,10 +377,7 @@ ResultCode CreateDirectoryFromArchive(ArchiveHandle archive_handle, const FileSy
388 if (archive == nullptr) 377 if (archive == nullptr)
389 return ERR_INVALID_ARCHIVE_HANDLE; 378 return ERR_INVALID_ARCHIVE_HANDLE;
390 379
391 if (archive->CreateDirectory(path)) 380 return archive->CreateDirectory(path);
392 return RESULT_SUCCESS;
393 return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
394 ErrorSummary::Canceled, ErrorLevel::Status);
395} 381}
396 382
397ResultCode RenameDirectoryBetweenArchives(ArchiveHandle src_archive_handle, 383ResultCode RenameDirectoryBetweenArchives(ArchiveHandle src_archive_handle,
@@ -404,17 +390,11 @@ ResultCode RenameDirectoryBetweenArchives(ArchiveHandle src_archive_handle,
404 return ERR_INVALID_ARCHIVE_HANDLE; 390 return ERR_INVALID_ARCHIVE_HANDLE;
405 391
406 if (src_archive == dest_archive) { 392 if (src_archive == dest_archive) {
407 if (src_archive->RenameDirectory(src_path, dest_path)) 393 return src_archive->RenameDirectory(src_path, dest_path);
408 return RESULT_SUCCESS;
409 } else { 394 } else {
410 // TODO: Implement renaming across archives 395 // TODO: Implement renaming across archives
411 return UnimplementedFunction(ErrorModule::FS); 396 return UnimplementedFunction(ErrorModule::FS);
412 } 397 }
413
414 // TODO(yuriks): This code probably isn't right, it'll return a Status even if the file didn't
415 // exist or similar. Verify.
416 return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
417 ErrorSummary::NothingHappened, ErrorLevel::Status);
418} 398}
419 399
420ResultVal<Kernel::SharedPtr<Directory>> OpenDirectoryFromArchive(ArchiveHandle archive_handle, 400ResultVal<Kernel::SharedPtr<Directory>> OpenDirectoryFromArchive(ArchiveHandle archive_handle,
@@ -423,13 +403,11 @@ ResultVal<Kernel::SharedPtr<Directory>> OpenDirectoryFromArchive(ArchiveHandle a
423 if (archive == nullptr) 403 if (archive == nullptr)
424 return ERR_INVALID_ARCHIVE_HANDLE; 404 return ERR_INVALID_ARCHIVE_HANDLE;
425 405
426 std::unique_ptr<FileSys::DirectoryBackend> backend = archive->OpenDirectory(path); 406 auto backend = archive->OpenDirectory(path);
427 if (backend == nullptr) { 407 if (backend.Failed())
428 return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS, ErrorSummary::NotFound, 408 return backend.Code();
429 ErrorLevel::Permanent);
430 }
431 409
432 auto directory = Kernel::SharedPtr<Directory>(new Directory(std::move(backend), path)); 410 auto directory = Kernel::SharedPtr<Directory>(new Directory(backend.MoveFrom(), path));
433 return MakeResult<Kernel::SharedPtr<Directory>>(std::move(directory)); 411 return MakeResult<Kernel::SharedPtr<Directory>>(std::move(directory));
434} 412}
435 413
@@ -549,6 +527,13 @@ void RegisterArchiveTypes() {
549 LOG_ERROR(Service_FS, "Can't instantiate SDMC archive with path %s", 527 LOG_ERROR(Service_FS, "Can't instantiate SDMC archive with path %s",
550 sdmc_directory.c_str()); 528 sdmc_directory.c_str());
551 529
530 auto sdmcwo_factory = std::make_unique<FileSys::ArchiveFactory_SDMCWriteOnly>(sdmc_directory);
531 if (sdmcwo_factory->Initialize())
532 RegisterArchiveType(std::move(sdmcwo_factory), ArchiveIdCode::SDMCWriteOnly);
533 else
534 LOG_ERROR(Service_FS, "Can't instantiate SDMCWriteOnly archive with path %s",
535 sdmc_directory.c_str());
536
552 // Create the SaveData archive 537 // Create the SaveData archive
553 auto savedata_factory = std::make_unique<FileSys::ArchiveFactory_SaveData>(sdmc_directory); 538 auto savedata_factory = std::make_unique<FileSys::ArchiveFactory_SaveData>(sdmc_directory);
554 RegisterArchiveType(std::move(savedata_factory), ArchiveIdCode::SaveData); 539 RegisterArchiveType(std::move(savedata_factory), ArchiveIdCode::SaveData);
@@ -569,10 +554,9 @@ void RegisterArchiveTypes() {
569 LOG_ERROR(Service_FS, "Can't instantiate SharedExtSaveData archive with path %s", 554 LOG_ERROR(Service_FS, "Can't instantiate SharedExtSaveData archive with path %s",
570 sharedextsavedata_factory->GetMountPoint().c_str()); 555 sharedextsavedata_factory->GetMountPoint().c_str());
571 556
572 // Create the SaveDataCheck archive, basically a small variation of the RomFS archive 557 // Create the NCCH archive, basically a small variation of the RomFS archive
573 auto savedatacheck_factory = 558 auto savedatacheck_factory = std::make_unique<FileSys::ArchiveFactory_NCCH>(nand_directory);
574 std::make_unique<FileSys::ArchiveFactory_SaveDataCheck>(nand_directory); 559 RegisterArchiveType(std::move(savedatacheck_factory), ArchiveIdCode::NCCH);
575 RegisterArchiveType(std::move(savedatacheck_factory), ArchiveIdCode::SaveDataCheck);
576 560
577 auto systemsavedata_factory = 561 auto systemsavedata_factory =
578 std::make_unique<FileSys::ArchiveFactory_SystemSaveData>(nand_directory); 562 std::make_unique<FileSys::ArchiveFactory_SystemSaveData>(nand_directory);
diff --git a/src/core/hle/service/fs/archive.h b/src/core/hle/service/fs/archive.h
index 41a76285c..21ed9717b 100644
--- a/src/core/hle/service/fs/archive.h
+++ b/src/core/hle/service/fs/archive.h
@@ -33,7 +33,7 @@ enum class ArchiveIdCode : u32 {
33 SystemSaveData = 0x00000008, 33 SystemSaveData = 0x00000008,
34 SDMC = 0x00000009, 34 SDMC = 0x00000009,
35 SDMCWriteOnly = 0x0000000A, 35 SDMCWriteOnly = 0x0000000A,
36 SaveDataCheck = 0x2345678A, 36 NCCH = 0x2345678A,
37}; 37};
38 38
39/// Media types for the archives 39/// Media types for the archives
diff --git a/src/core/hle/service/mic_u.cpp b/src/core/hle/service/mic_u.cpp
index edd1ea97b..563341504 100644
--- a/src/core/hle/service/mic_u.cpp
+++ b/src/core/hle/service/mic_u.cpp
@@ -2,6 +2,9 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/logging/log.h"
6#include "core/hle/kernel/event.h"
7#include "core/hle/kernel/shared_memory.h"
5#include "core/hle/service/mic_u.h" 8#include "core/hle/service/mic_u.h"
6 9
7//////////////////////////////////////////////////////////////////////////////////////////////////// 10////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -9,23 +12,298 @@
9 12
10namespace MIC_U { 13namespace MIC_U {
11 14
15enum class Encoding : u8 {
16 PCM8 = 0,
17 PCM16 = 1,
18 PCM8Signed = 2,
19 PCM16Signed = 3,
20};
21
22enum class SampleRate : u8 {
23 SampleRate32730 = 0,
24 SampleRate16360 = 1,
25 SampleRate10910 = 2,
26 SampleRate8180 = 3
27};
28
29static Kernel::SharedPtr<Kernel::Event> buffer_full_event;
30static Kernel::SharedPtr<Kernel::SharedMemory> shared_memory;
31static u8 mic_gain = 0;
32static bool mic_power = false;
33static bool is_sampling = false;
34static bool allow_shell_closed;
35static bool clamp = false;
36static Encoding encoding;
37static SampleRate sample_rate;
38static s32 audio_buffer_offset;
39static u32 audio_buffer_size;
40static bool audio_buffer_loop;
41
42/**
43 * MIC::MapSharedMem service function
44 * Inputs:
45 * 0 : Header Code[0x00010042]
46 * 1 : Shared-mem size
47 * 2 : CopyHandleDesc
48 * 3 : Shared-mem handle
49 * Outputs:
50 * 1 : Result of function, 0 on success, otherwise error code
51 */
52static void MapSharedMem(Service::Interface* self) {
53 u32* cmd_buff = Kernel::GetCommandBuffer();
54 u32 size = cmd_buff[1];
55 Handle mem_handle = cmd_buff[3];
56 shared_memory = Kernel::g_handle_table.Get<Kernel::SharedMemory>(mem_handle);
57 if (shared_memory) {
58 shared_memory->name = "MIC_U:shared_memory";
59 }
60 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
61 LOG_WARNING(Service_MIC, "called, size=0x%X, mem_handle=0x%08X", size, mem_handle);
62}
63
64/**
65 * MIC::UnmapSharedMem service function
66 * Inputs:
67 * 0 : Header Code[0x00020000]
68 * Outputs:
69 * 1 : Result of function, 0 on success, otherwise error code
70 */
71static void UnmapSharedMem(Service::Interface* self) {
72 u32* cmd_buff = Kernel::GetCommandBuffer();
73
74 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
75 LOG_WARNING(Service_MIC, "called");
76}
77
78/**
79 * MIC::StartSampling service function
80 * Inputs:
81 * 0 : Header Code[0x00030140]
82 * 1 : Encoding
83 * 2 : SampleRate
84 * 3 : Base offset for audio data in sharedmem
85 * 4 : Size of the audio data in sharedmem
86 * 5 : Loop at end of buffer
87 * Outputs:
88 * 1 : Result of function, 0 on success, otherwise error code
89 */
90static void StartSampling(Service::Interface* self) {
91 u32* cmd_buff = Kernel::GetCommandBuffer();
92
93 encoding = static_cast<Encoding>(cmd_buff[1] & 0xFF);
94 sample_rate = static_cast<SampleRate>(cmd_buff[2] & 0xFF);
95 audio_buffer_offset = cmd_buff[3];
96 audio_buffer_size = cmd_buff[4];
97 audio_buffer_loop = static_cast<bool>(cmd_buff[5] & 0xFF);
98
99 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
100 is_sampling = true;
101 LOG_WARNING(Service_MIC, "(STUBBED) called, encoding=%u, sample_rate=%u, "
102 "audio_buffer_offset=%d, audio_buffer_size=%u, audio_buffer_loop=%u",
103 encoding, sample_rate, audio_buffer_offset, audio_buffer_size, audio_buffer_loop);
104}
105
106/**
107 * MIC::AdjustSampling service function
108 * Inputs:
109 * 0 : Header Code[0x00040040]
110 * 1 : SampleRate
111 * Outputs:
112 * 1 : Result of function, 0 on success, otherwise error code
113 */
114static void AdjustSampling(Service::Interface* self) {
115 u32* cmd_buff = Kernel::GetCommandBuffer();
116 sample_rate = static_cast<SampleRate>(cmd_buff[1] & 0xFF);
117 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
118 LOG_WARNING(Service_MIC, "(STUBBED) called, sample_rate=%u", sample_rate);
119}
120
121/**
122 * MIC::StopSampling service function
123 * Inputs:
124 * 0 : Header Code[0x00050000]
125 * Outputs:
126 * 1 : Result of function, 0 on success, otherwise error code
127 */
128static void StopSampling(Service::Interface* self) {
129 u32* cmd_buff = Kernel::GetCommandBuffer();
130 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
131 is_sampling = false;
132 LOG_WARNING(Service_MIC, "(STUBBED) called");
133}
134
135/**
136 * MIC::IsSampling service function
137 * Inputs:
138 * 0 : Header Code[0x00060000]
139 * Outputs:
140 * 1 : Result of function, 0 on success, otherwise error code
141 * 2 : 0 = sampling, non-zero = sampling
142 */
143static void IsSampling(Service::Interface* self) {
144 u32* cmd_buff = Kernel::GetCommandBuffer();
145 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
146 cmd_buff[2] = is_sampling;
147 LOG_WARNING(Service_MIC, "(STUBBED) called");
148}
149
150/**
151 * MIC::GetBufferFullEvent service function
152 * Inputs:
153 * 0 : Header Code[0x00070000]
154 * Outputs:
155 * 1 : Result of function, 0 on success, otherwise error code
156 * 3 : Event handle
157 */
158static void GetBufferFullEvent(Service::Interface* self) {
159 u32* cmd_buff = Kernel::GetCommandBuffer();
160 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
161 cmd_buff[3] = Kernel::g_handle_table.Create(buffer_full_event).MoveFrom();
162 LOG_WARNING(Service_MIC, "(STUBBED) called");
163}
164
165/**
166 * MIC::SetGain service function
167 * Inputs:
168 * 0 : Header Code[0x00080040]
169 * 1 : Gain
170 * Outputs:
171 * 1 : Result of function, 0 on success, otherwise error code
172 */
173static void SetGain(Service::Interface* self) {
174 u32* cmd_buff = Kernel::GetCommandBuffer();
175 mic_gain = cmd_buff[1] & 0xFF;
176 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
177 LOG_WARNING(Service_MIC, "(STUBBED) called, mic_gain=%u", mic_gain);
178}
179
180/**
181 * MIC::GetGain service function
182 * Inputs:
183 * 0 : Header Code[0x00090000]
184 * Outputs:
185 * 1 : Result of function, 0 on success, otherwise error code
186 * 2 : Gain
187 */
188static void GetGain(Service::Interface* self) {
189 u32* cmd_buff = Kernel::GetCommandBuffer();
190 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
191 cmd_buff[2] = mic_gain;
192 LOG_WARNING(Service_MIC, "(STUBBED) called");
193}
194
195/**
196 * MIC::SetPower service function
197 * Inputs:
198 * 0 : Header Code[0x000A0040]
199 * 1 : Power (0 = off, 1 = on)
200 * Outputs:
201 * 1 : Result of function, 0 on success, otherwise error code
202 */
203static void SetPower(Service::Interface* self) {
204 u32* cmd_buff = Kernel::GetCommandBuffer();
205 mic_power = static_cast<bool>(cmd_buff[1] & 0xFF);
206 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
207 LOG_WARNING(Service_MIC, "(STUBBED) called, mic_power=%u", mic_power);
208}
209
210/**
211 * MIC::GetPower service function
212 * Inputs:
213 * 0 : Header Code[0x000B0000]
214 * Outputs:
215 * 1 : Result of function, 0 on success, otherwise error code
216 * 2 : Power
217 */
218static void GetPower(Service::Interface* self) {
219 u32* cmd_buff = Kernel::GetCommandBuffer();
220 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
221 cmd_buff[2] = mic_power;
222 LOG_WARNING(Service_MIC, "(STUBBED) called");
223}
224
225/**
226 * MIC::SetIirFilterMic service function
227 * Inputs:
228 * 0 : Header Code[0x000C0042]
229 * 1 : Size
230 * 2 : (Size << 4) | 0xA
231 * 3 : Pointer to IIR Filter Data
232 * Outputs:
233 * 1 : Result of function, 0 on success, otherwise error code
234 */
235static void SetIirFilterMic(Service::Interface* self) {
236 u32* cmd_buff = Kernel::GetCommandBuffer();
237
238 u32 size = cmd_buff[1];
239 VAddr buffer = cmd_buff[3];
240
241 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
242 LOG_WARNING(Service_MIC, "(STUBBED) called, size=0x%X, buffer=0x%08X", size, buffer);
243}
244
245/**
246 * MIC::SetClamp service function
247 * Inputs:
248 * 0 : Header Code[0x000D0040]
249 * 1 : Clamp (0 = don't clamp, non-zero = clamp)
250 * Outputs:
251 * 1 : Result of function, 0 on success, otherwise error code
252 */
253static void SetClamp(Service::Interface* self) {
254 u32* cmd_buff = Kernel::GetCommandBuffer();
255 clamp = static_cast<bool>(cmd_buff[1] & 0xFF);
256 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
257 LOG_WARNING(Service_MIC, "(STUBBED) called, clamp=%u", clamp);
258}
259
260/**
261 * MIC::GetClamp service function
262 * Inputs:
263 * 0 : Header Code[0x000E0000]
264 * Outputs:
265 * 1 : Result of function, 0 on success, otherwise error code
266 * 2 : Clamp (0 = don't clamp, non-zero = clamp)
267 */
268static void GetClamp(Service::Interface* self) {
269 u32* cmd_buff = Kernel::GetCommandBuffer();
270 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
271 cmd_buff[2] = clamp;
272 LOG_WARNING(Service_MIC, "(STUBBED) called");
273}
274
275/**
276 * MIC::SetAllowShellClosed service function
277 * Inputs:
278 * 0 : Header Code[0x000D0040]
279 * 1 : Sampling allowed while shell closed (0 = disallow, non-zero = allow)
280 * Outputs:
281 * 1 : Result of function, 0 on success, otherwise error code
282 */
283static void SetAllowShellClosed(Service::Interface* self) {
284 u32* cmd_buff = Kernel::GetCommandBuffer();
285 allow_shell_closed = static_cast<bool>(cmd_buff[1] & 0xFF);
286 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
287 LOG_WARNING(Service_MIC, "(STUBBED) called, allow_shell_closed=%u", allow_shell_closed);
288}
289
12const Interface::FunctionInfo FunctionTable[] = { 290const Interface::FunctionInfo FunctionTable[] = {
13 {0x00010042, nullptr, "MapSharedMem"}, 291 {0x00010042, MapSharedMem, "MapSharedMem"},
14 {0x00020000, nullptr, "UnmapSharedMem"}, 292 {0x00020000, UnmapSharedMem, "UnmapSharedMem"},
15 {0x00030140, nullptr, "Initialize"}, 293 {0x00030140, StartSampling, "StartSampling"},
16 {0x00040040, nullptr, "AdjustSampling"}, 294 {0x00040040, AdjustSampling, "AdjustSampling"},
17 {0x00050000, nullptr, "StopSampling"}, 295 {0x00050000, StopSampling, "StopSampling"},
18 {0x00060000, nullptr, "IsSampling"}, 296 {0x00060000, IsSampling, "IsSampling"},
19 {0x00070000, nullptr, "GetEventHandle"}, 297 {0x00070000, GetBufferFullEvent, "GetBufferFullEvent"},
20 {0x00080040, nullptr, "SetGain"}, 298 {0x00080040, SetGain, "SetGain"},
21 {0x00090000, nullptr, "GetGain"}, 299 {0x00090000, GetGain, "GetGain"},
22 {0x000A0040, nullptr, "SetPower"}, 300 {0x000A0040, SetPower, "SetPower"},
23 {0x000B0000, nullptr, "GetPower"}, 301 {0x000B0000, GetPower, "GetPower"},
24 {0x000C0042, nullptr, "size"}, 302 {0x000C0042, SetIirFilterMic, "SetIirFilterMic"},
25 {0x000D0040, nullptr, "SetClamp"}, 303 {0x000D0040, SetClamp, "SetClamp"},
26 {0x000E0000, nullptr, "GetClamp"}, 304 {0x000E0000, GetClamp, "GetClamp"},
27 {0x000F0040, nullptr, "SetAllowShellClosed"}, 305 {0x000F0040, SetAllowShellClosed, "SetAllowShellClosed"},
28 {0x00100040, nullptr, "unknown_input2"}, 306 {0x00100040, nullptr, "SetClientSDKVersion"},
29}; 307};
30 308
31//////////////////////////////////////////////////////////////////////////////////////////////////// 309////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -33,6 +311,18 @@ const Interface::FunctionInfo FunctionTable[] = {
33 311
34Interface::Interface() { 312Interface::Interface() {
35 Register(FunctionTable); 313 Register(FunctionTable);
314 shared_memory = nullptr;
315 buffer_full_event =
316 Kernel::Event::Create(Kernel::ResetType::OneShot, "MIC_U::buffer_full_event");
317 mic_gain = 0;
318 mic_power = false;
319 is_sampling = false;
320 clamp = false;
321}
322
323Interface::~Interface() {
324 shared_memory = nullptr;
325 buffer_full_event = nullptr;
36} 326}
37 327
38} // namespace 328} // namespace
diff --git a/src/core/hle/service/mic_u.h b/src/core/hle/service/mic_u.h
index dc795d14c..1cff7390e 100644
--- a/src/core/hle/service/mic_u.h
+++ b/src/core/hle/service/mic_u.h
@@ -16,6 +16,7 @@ namespace MIC_U {
16class Interface : public Service::Interface { 16class Interface : public Service::Interface {
17public: 17public:
18 Interface(); 18 Interface();
19 ~Interface();
19 20
20 std::string GetPortName() const override { 21 std::string GetPortName() const override {
21 return "mic:u"; 22 return "mic:u";
diff --git a/src/core/hle/service/ptm/ptm.cpp b/src/core/hle/service/ptm/ptm.cpp
index 6e6b63329..cc859c14c 100644
--- a/src/core/hle/service/ptm/ptm.cpp
+++ b/src/core/hle/service/ptm/ptm.cpp
@@ -128,7 +128,7 @@ void Init() {
128 Service::FS::OpenArchive(Service::FS::ArchiveIdCode::SharedExtSaveData, archive_path); 128 Service::FS::OpenArchive(Service::FS::ArchiveIdCode::SharedExtSaveData, archive_path);
129 ASSERT_MSG(archive_result.Succeeded(), "Could not open the PTM SharedExtSaveData archive!"); 129 ASSERT_MSG(archive_result.Succeeded(), "Could not open the PTM SharedExtSaveData archive!");
130 130
131 FileSys::Path gamecoin_path("gamecoin.dat"); 131 FileSys::Path gamecoin_path("/gamecoin.dat");
132 FileSys::Mode open_mode = {}; 132 FileSys::Mode open_mode = {};
133 open_mode.write_flag.Assign(1); 133 open_mode.write_flag.Assign(1);
134 open_mode.create_flag.Assign(1); 134 open_mode.create_flag.Assign(1);
diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp
index 28cb97d8e..45dedea68 100644
--- a/src/core/hw/gpu.cpp
+++ b/src/core/hw/gpu.cpp
@@ -29,16 +29,12 @@ namespace GPU {
29 29
30Regs g_regs; 30Regs g_regs;
31 31
32/// True if the current frame was skipped
33bool g_skip_frame;
34/// 268MHz CPU clocks / 60Hz frames per second 32/// 268MHz CPU clocks / 60Hz frames per second
35const u64 frame_ticks = 268123480ull / 60; 33const u64 frame_ticks = 268123480ull / 60;
36/// Event id for CoreTiming 34/// Event id for CoreTiming
37static int vblank_event; 35static int vblank_event;
38/// Total number of frames drawn 36/// Total number of frames drawn
39static u64 frame_count; 37static u64 frame_count;
40/// True if the last frame was skipped
41static bool last_skip_frame;
42 38
43template <typename T> 39template <typename T>
44inline void Read(T& var, const u32 raw_addr) { 40inline void Read(T& var, const u32 raw_addr) {
@@ -519,20 +515,7 @@ template void Write<u8>(u32 addr, const u8 data);
519/// Update hardware 515/// Update hardware
520static void VBlankCallback(u64 userdata, int cycles_late) { 516static void VBlankCallback(u64 userdata, int cycles_late) {
521 frame_count++; 517 frame_count++;
522 last_skip_frame = g_skip_frame; 518 VideoCore::g_renderer->SwapBuffers();
523 g_skip_frame = (frame_count & Settings::values.frame_skip) != 0;
524
525 // Swap buffers based on the frameskip mode, which is a little bit tricky. When
526 // a frame is being skipped, nothing is being rendered to the internal framebuffer(s).
527 // So, we should only swap frames if the last frame was rendered. The rules are:
528 // - If frameskip == 0 (disabled), always swap buffers
529 // - If frameskip == 1, swap buffers every other frame (starting from the first frame)
530 // - If frameskip > 1, swap buffers every frameskip^n frames (starting from the second frame)
531 if ((((Settings::values.frame_skip != 1) ^ last_skip_frame) &&
532 last_skip_frame != g_skip_frame) ||
533 Settings::values.frame_skip == 0) {
534 VideoCore::g_renderer->SwapBuffers();
535 }
536 519
537 // Signal to GSP that GPU interrupt has occurred 520 // Signal to GSP that GPU interrupt has occurred
538 // TODO(yuriks): hwtest to determine if PDC0 is for the Top screen and PDC1 for the Sub 521 // TODO(yuriks): hwtest to determine if PDC0 is for the Top screen and PDC1 for the Sub
@@ -579,8 +562,6 @@ void Init() {
579 framebuffer_sub.color_format.Assign(Regs::PixelFormat::RGB8); 562 framebuffer_sub.color_format.Assign(Regs::PixelFormat::RGB8);
580 framebuffer_sub.active_fb = 0; 563 framebuffer_sub.active_fb = 0;
581 564
582 last_skip_frame = false;
583 g_skip_frame = false;
584 frame_count = 0; 565 frame_count = 0;
585 566
586 vblank_event = CoreTiming::RegisterEvent("GPU::VBlankCallback", VBlankCallback); 567 vblank_event = CoreTiming::RegisterEvent("GPU::VBlankCallback", VBlankCallback);
diff --git a/src/core/hw/gpu.h b/src/core/hw/gpu.h
index 32ddc5697..d53381216 100644
--- a/src/core/hw/gpu.h
+++ b/src/core/hw/gpu.h
@@ -316,7 +316,6 @@ ASSERT_REG_POSITION(command_processor_config, 0x00638);
316static_assert(sizeof(Regs) == 0x1000 * sizeof(u32), "Invalid total size of register set"); 316static_assert(sizeof(Regs) == 0x1000 * sizeof(u32), "Invalid total size of register set");
317 317
318extern Regs g_regs; 318extern Regs g_regs;
319extern bool g_skip_frame;
320 319
321template <typename T> 320template <typename T>
322void Read(T& var, const u32 addr); 321void Read(T& var, const u32 addr);
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 64c388374..65e4bba85 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -45,13 +45,11 @@ struct SpecialRegion {
45 * requires an indexed fetch and a check for NULL. 45 * requires an indexed fetch and a check for NULL.
46 */ 46 */
47struct PageTable { 47struct PageTable {
48 static const size_t NUM_ENTRIES = 1 << (32 - PAGE_BITS);
49
50 /** 48 /**
51 * Array of memory pointers backing each page. An entry can only be non-null if the 49 * Array of memory pointers backing each page. An entry can only be non-null if the
52 * corresponding entry in the `attributes` array is of type `Memory`. 50 * corresponding entry in the `attributes` array is of type `Memory`.
53 */ 51 */
54 std::array<u8*, NUM_ENTRIES> pointers; 52 std::array<u8*, PAGE_TABLE_NUM_ENTRIES> pointers;
55 53
56 /** 54 /**
57 * Contains MMIO handlers that back memory regions whose entries in the `attribute` array is of 55 * Contains MMIO handlers that back memory regions whose entries in the `attribute` array is of
@@ -63,13 +61,13 @@ struct PageTable {
63 * Array of fine grained page attributes. If it is set to any value other than `Memory`, then 61 * Array of fine grained page attributes. If it is set to any value other than `Memory`, then
64 * the corresponding entry in `pointers` MUST be set to null. 62 * the corresponding entry in `pointers` MUST be set to null.
65 */ 63 */
66 std::array<PageType, NUM_ENTRIES> attributes; 64 std::array<PageType, PAGE_TABLE_NUM_ENTRIES> attributes;
67 65
68 /** 66 /**
69 * Indicates the number of externally cached resources touching a page that should be 67 * Indicates the number of externally cached resources touching a page that should be
70 * flushed before the memory is accessed 68 * flushed before the memory is accessed
71 */ 69 */
72 std::array<u8, NUM_ENTRIES> cached_res_count; 70 std::array<u8, PAGE_TABLE_NUM_ENTRIES> cached_res_count;
73}; 71};
74 72
75/// Singular page table used for the singleton process 73/// Singular page table used for the singleton process
@@ -77,6 +75,10 @@ static PageTable main_page_table;
77/// Currently active page table 75/// Currently active page table
78static PageTable* current_page_table = &main_page_table; 76static PageTable* current_page_table = &main_page_table;
79 77
78std::array<u8*, PAGE_TABLE_NUM_ENTRIES>* GetCurrentPageTablePointers() {
79 return &current_page_table->pointers;
80}
81
80static void MapPages(u32 base, u32 size, u8* memory, PageType type) { 82static void MapPages(u32 base, u32 size, u8* memory, PageType type) {
81 LOG_DEBUG(HW_Memory, "Mapping %p onto %08X-%08X", memory, base * PAGE_SIZE, 83 LOG_DEBUG(HW_Memory, "Mapping %p onto %08X-%08X", memory, base * PAGE_SIZE,
82 (base + size) * PAGE_SIZE); 84 (base + size) * PAGE_SIZE);
@@ -84,7 +86,7 @@ static void MapPages(u32 base, u32 size, u8* memory, PageType type) {
84 u32 end = base + size; 86 u32 end = base + size;
85 87
86 while (base != end) { 88 while (base != end) {
87 ASSERT_MSG(base < PageTable::NUM_ENTRIES, "out of range mapping at %08X", base); 89 ASSERT_MSG(base < PAGE_TABLE_NUM_ENTRIES, "out of range mapping at %08X", base);
88 90
89 // Since pages are unmapped on shutdown after video core is shutdown, the renderer may be 91 // Since pages are unmapped on shutdown after video core is shutdown, the renderer may be
90 // null here 92 // null here
diff --git a/src/core/memory.h b/src/core/memory.h
index 8fd3080ff..903b58a22 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array>
7#include <cstddef> 8#include <cstddef>
8#include <string> 9#include <string>
9#include "common/common_types.h" 10#include "common/common_types.h"
@@ -17,6 +18,7 @@ namespace Memory {
17const u32 PAGE_SIZE = 0x1000; 18const u32 PAGE_SIZE = 0x1000;
18const u32 PAGE_MASK = PAGE_SIZE - 1; 19const u32 PAGE_MASK = PAGE_SIZE - 1;
19const int PAGE_BITS = 12; 20const int PAGE_BITS = 12;
21const size_t PAGE_TABLE_NUM_ENTRIES = 1 << (32 - PAGE_BITS);
20 22
21/// Physical memory regions as seen from the ARM11 23/// Physical memory regions as seen from the ARM11
22enum : PAddr { 24enum : PAddr {
@@ -166,4 +168,11 @@ void RasterizerFlushRegion(PAddr start, u32 size);
166 * Flushes and invalidates any externally cached rasterizer resources touching the given region. 168 * Flushes and invalidates any externally cached rasterizer resources touching the given region.
167 */ 169 */
168void RasterizerFlushAndInvalidateRegion(PAddr start, u32 size); 170void RasterizerFlushAndInvalidateRegion(PAddr start, u32 size);
171
172/**
173 * Dynarmic has an optimization to memory accesses when the pointer to the page exists that
174 * can be used by setting up the current page table as a callback. This function is used to
175 * retrieve the current page table for that purpose.
176 */
177std::array<u8*, PAGE_TABLE_NUM_ENTRIES>* GetCurrentPageTablePointers();
169} 178}
diff --git a/src/core/settings.h b/src/core/settings.h
index e931953d7..7470fdbeb 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -78,7 +78,6 @@ struct Values {
78 78
79 // Core 79 // Core
80 bool use_cpu_jit; 80 bool use_cpu_jit;
81 int frame_skip;
82 81
83 // Data Storage 82 // Data Storage
84 bool use_virtual_sd; 83 bool use_virtual_sd;
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
index 457c55571..89237e477 100644
--- a/src/tests/CMakeLists.txt
+++ b/src/tests/CMakeLists.txt
@@ -1,5 +1,7 @@
1set(SRCS 1set(SRCS
2 glad.cpp
2 tests.cpp 3 tests.cpp
4 core/file_sys/path_parser.cpp
3 ) 5 )
4 6
5set(HEADERS 7set(HEADERS
diff --git a/src/tests/core/file_sys/path_parser.cpp b/src/tests/core/file_sys/path_parser.cpp
new file mode 100644
index 000000000..2b543e438
--- /dev/null
+++ b/src/tests/core/file_sys/path_parser.cpp
@@ -0,0 +1,38 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <catch.hpp>
6#include "common/file_util.h"
7#include "core/file_sys/path_parser.h"
8
9namespace FileSys {
10
11TEST_CASE("PathParser", "[core][file_sys]") {
12 REQUIRE(!PathParser(Path(std::vector<u8>{})).IsValid());
13 REQUIRE(!PathParser(Path("a")).IsValid());
14 REQUIRE(!PathParser(Path("/|")).IsValid());
15 REQUIRE(PathParser(Path("/a")).IsValid());
16 REQUIRE(!PathParser(Path("/a/b/../../c/../../d")).IsValid());
17 REQUIRE(PathParser(Path("/a/b/../c/../../d")).IsValid());
18 REQUIRE(PathParser(Path("/")).IsRootDirectory());
19 REQUIRE(!PathParser(Path("/a")).IsRootDirectory());
20 REQUIRE(PathParser(Path("/a/..")).IsRootDirectory());
21}
22
23TEST_CASE("PathParser - Host file system", "[core][file_sys]") {
24 std::string test_dir = "./test";
25 FileUtil::CreateDir(test_dir);
26 FileUtil::CreateDir(test_dir + "/z");
27 FileUtil::CreateEmptyFile(test_dir + "/a");
28
29 REQUIRE(PathParser(Path("/a")).GetHostStatus(test_dir) == PathParser::FileFound);
30 REQUIRE(PathParser(Path("/b")).GetHostStatus(test_dir) == PathParser::NotFound);
31 REQUIRE(PathParser(Path("/z")).GetHostStatus(test_dir) == PathParser::DirectoryFound);
32 REQUIRE(PathParser(Path("/a/c")).GetHostStatus(test_dir) == PathParser::FileInPath);
33 REQUIRE(PathParser(Path("/b/c")).GetHostStatus(test_dir) == PathParser::PathNotFound);
34
35 FileUtil::DeleteDirRecursively(test_dir);
36}
37
38} // namespace FileSys
diff --git a/src/tests/glad.cpp b/src/tests/glad.cpp
new file mode 100644
index 000000000..b0b016440
--- /dev/null
+++ b/src/tests/glad.cpp
@@ -0,0 +1,14 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <catch.hpp>
6#include <glad/glad.h>
7
8// This is not an actual test, but a work-around for issue #2183.
9// If tests uses functions in core but doesn't explicitly use functions in glad, the linker of macOS
10// will error about undefined references from video_core to glad. So we explicitly use a glad
11// function here to shut up the linker.
12TEST_CASE("glad fake test", "[dummy]") {
13 REQUIRE(&gladLoadGL != nullptr);
14}
diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp
index fda91e29c..c80c96762 100644
--- a/src/video_core/command_processor.cpp
+++ b/src/video_core/command_processor.cpp
@@ -52,10 +52,6 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
52 if (id >= regs.NumIds()) 52 if (id >= regs.NumIds())
53 return; 53 return;
54 54
55 // If we're skipping this frame, only allow trigger IRQ
56 if (GPU::g_skip_frame && id != PICA_REG_INDEX(trigger_irq))
57 return;
58
59 // TODO: Figure out how register masking acts on e.g. vs.uniform_setup.set_value 55 // TODO: Figure out how register masking acts on e.g. vs.uniform_setup.set_value
60 u32 old_value = regs[id]; 56 u32 old_value = regs[id];
61 57
@@ -215,18 +211,17 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
215 211
216 PrimitiveAssembler<Shader::OutputVertex>& primitive_assembler = g_state.primitive_assembler; 212 PrimitiveAssembler<Shader::OutputVertex>& primitive_assembler = g_state.primitive_assembler;
217 213
218 if (g_debug_context) { 214 if (g_debug_context && g_debug_context->recorder) {
219 for (int i = 0; i < 3; ++i) { 215 for (int i = 0; i < 3; ++i) {
220 const auto texture = regs.GetTextures()[i]; 216 const auto texture = regs.GetTextures()[i];
221 if (!texture.enabled) 217 if (!texture.enabled)
222 continue; 218 continue;
223 219
224 u8* texture_data = Memory::GetPhysicalPointer(texture.config.GetPhysicalAddress()); 220 u8* texture_data = Memory::GetPhysicalPointer(texture.config.GetPhysicalAddress());
225 if (g_debug_context && Pica::g_debug_context->recorder) 221 g_debug_context->recorder->MemoryAccessed(
226 g_debug_context->recorder->MemoryAccessed( 222 texture_data, Pica::Regs::NibblesPerPixel(texture.format) *
227 texture_data, Pica::Regs::NibblesPerPixel(texture.format) * 223 texture.config.width / 2 * texture.config.height,
228 texture.config.width / 2 * texture.config.height, 224 texture.config.GetPhysicalAddress());
229 texture.config.GetPhysicalAddress());
230 } 225 }
231 } 226 }
232 227
@@ -236,7 +231,8 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
236 // The size has been tuned for optimal balance between hit-rate and the cost of lookup 231 // The size has been tuned for optimal balance between hit-rate and the cost of lookup
237 const size_t VERTEX_CACHE_SIZE = 32; 232 const size_t VERTEX_CACHE_SIZE = 32;
238 std::array<u16, VERTEX_CACHE_SIZE> vertex_cache_ids; 233 std::array<u16, VERTEX_CACHE_SIZE> vertex_cache_ids;
239 std::array<Shader::OutputRegisters, VERTEX_CACHE_SIZE> vertex_cache; 234 std::array<Shader::OutputVertex, VERTEX_CACHE_SIZE> vertex_cache;
235 Shader::OutputVertex output_vertex;
240 236
241 unsigned int vertex_cache_pos = 0; 237 unsigned int vertex_cache_pos = 0;
242 vertex_cache_ids.fill(-1); 238 vertex_cache_ids.fill(-1);
@@ -266,7 +262,7 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
266 262
267 for (unsigned int i = 0; i < VERTEX_CACHE_SIZE; ++i) { 263 for (unsigned int i = 0; i < VERTEX_CACHE_SIZE; ++i) {
268 if (vertex == vertex_cache_ids[i]) { 264 if (vertex == vertex_cache_ids[i]) {
269 output_registers = vertex_cache[i]; 265 output_vertex = vertex_cache[i];
270 vertex_cache_hit = true; 266 vertex_cache_hit = true;
271 break; 267 break;
272 } 268 }
@@ -285,16 +281,16 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
285 g_state.vs.Run(shader_unit, input, loader.GetNumTotalAttributes()); 281 g_state.vs.Run(shader_unit, input, loader.GetNumTotalAttributes());
286 output_registers = shader_unit.output_registers; 282 output_registers = shader_unit.output_registers;
287 283
284 // Retrieve vertex from register data
285 output_vertex = output_registers.ToVertex(regs.vs);
286
288 if (is_indexed) { 287 if (is_indexed) {
289 vertex_cache[vertex_cache_pos] = output_registers; 288 vertex_cache[vertex_cache_pos] = output_vertex;
290 vertex_cache_ids[vertex_cache_pos] = vertex; 289 vertex_cache_ids[vertex_cache_pos] = vertex;
291 vertex_cache_pos = (vertex_cache_pos + 1) % VERTEX_CACHE_SIZE; 290 vertex_cache_pos = (vertex_cache_pos + 1) % VERTEX_CACHE_SIZE;
292 } 291 }
293 } 292 }
294 293
295 // Retrieve vertex from register data
296 Shader::OutputVertex output_vertex = output_registers.ToVertex(regs.vs);
297
298 // Send to renderer 294 // Send to renderer
299 using Pica::Shader::OutputVertex; 295 using Pica::Shader::OutputVertex;
300 auto AddTriangle = [](const OutputVertex& v0, const OutputVertex& v1, 296 auto AddTriangle = [](const OutputVertex& v0, const OutputVertex& v1,
diff --git a/src/video_core/pica.h b/src/video_core/pica.h
index b2db609ec..99bd59a69 100644
--- a/src/video_core/pica.h
+++ b/src/video_core/pica.h
@@ -40,7 +40,7 @@ namespace Pica {
40// field offset. Otherwise, the compiler will fail to compile this code. 40// field offset. Otherwise, the compiler will fail to compile this code.
41#define PICA_REG_INDEX_WORKAROUND(field_name, backup_workaround_index) \ 41#define PICA_REG_INDEX_WORKAROUND(field_name, backup_workaround_index) \
42 ((typename std::enable_if<backup_workaround_index == PICA_REG_INDEX(field_name), \ 42 ((typename std::enable_if<backup_workaround_index == PICA_REG_INDEX(field_name), \
43 size_t>::type)PICA_REG_INDEX(field_name)) 43 size_t>::type) PICA_REG_INDEX(field_name))
44#endif // _MSC_VER 44#endif // _MSC_VER
45 45
46struct Regs { 46struct Regs {
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 7cc3b407a..d4d5903ce 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -893,7 +893,7 @@ bool RasterizerOpenGL::AccelerateFill(const GPU::Regs::MemoryFillConfig& config)
893 value_float = config.value_32bit / 16777215.0f; // 2^24 - 1 893 value_float = config.value_32bit / 16777215.0f; // 2^24 - 1
894 } 894 }
895 895
896 cur_state.depth.write_mask = true; 896 cur_state.depth.write_mask = GL_TRUE;
897 cur_state.Apply(); 897 cur_state.Apply();
898 glClearBufferfv(GL_DEPTH, 0, &value_float); 898 glClearBufferfv(GL_DEPTH, 0, &value_float);
899 } else if (dst_type == SurfaceType::DepthStencil) { 899 } else if (dst_type == SurfaceType::DepthStencil) {
@@ -908,8 +908,8 @@ bool RasterizerOpenGL::AccelerateFill(const GPU::Regs::MemoryFillConfig& config)
908 GLfloat value_float = (config.value_32bit & 0xFFFFFF) / 16777215.0f; // 2^24 - 1 908 GLfloat value_float = (config.value_32bit & 0xFFFFFF) / 16777215.0f; // 2^24 - 1
909 GLint value_int = (config.value_32bit >> 24); 909 GLint value_int = (config.value_32bit >> 24);
910 910
911 cur_state.depth.write_mask = true; 911 cur_state.depth.write_mask = GL_TRUE;
912 cur_state.stencil.write_mask = true; 912 cur_state.stencil.write_mask = 0xFF;
913 cur_state.Apply(); 913 cur_state.Apply();
914 glClearBufferfi(GL_DEPTH_STENCIL, 0, value_float, value_int); 914 glClearBufferfi(GL_DEPTH_STENCIL, 0, value_float, value_int);
915 } 915 }
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index ed84cadea..2a731f483 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -27,8 +27,8 @@ OpenGLState::OpenGLState() {
27 stencil.test_enabled = false; 27 stencil.test_enabled = false;
28 stencil.test_func = GL_ALWAYS; 28 stencil.test_func = GL_ALWAYS;
29 stencil.test_ref = 0; 29 stencil.test_ref = 0;
30 stencil.test_mask = -1; 30 stencil.test_mask = 0xFF;
31 stencil.write_mask = -1; 31 stencil.write_mask = 0xFF;
32 stencil.action_depth_fail = GL_KEEP; 32 stencil.action_depth_fail = GL_KEEP;
33 stencil.action_depth_pass = GL_KEEP; 33 stencil.action_depth_pass = GL_KEEP;
34 stencil.action_stencil_fail = GL_KEEP; 34 stencil.action_stencil_fail = GL_KEEP;