diff options
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'" | ||
| 126 | fi | 124 | fi |
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 | ||
| 31 | cache: | 30 | cache: |
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() |
| @@ -5,7 +5,7 @@ Citra Emulator | |||
| 5 | [](https://travis-ci.org/citra-emu/citra) | 5 | [](https://travis-ci.org/citra-emu/citra) |
| 6 | [](https://ci.appveyor.com/project/bunnei/citra) | 6 | [](https://ci.appveyor.com/project/bunnei/citra) |
| 7 | 7 | ||
| 8 | Citra 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. | 8 | Citra 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 | ||
| 10 | Citra 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. | 10 | Citra 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 | ||
| 26 | SDL2Sink::SDL2Sink() : impl(std::make_unique<Impl>()) { | 26 | SDL2Sink::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) |
| 43 | use_cpu_jit = | 43 | use_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. | ||
| 47 | frame_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 | |||
| 601 | std::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 |
| 156 | std::string& GetExeDirectory(); | 156 | std::string& GetExeDirectory(); |
| 157 | std::string AppDataRoamingDirectory(); | ||
| 157 | #endif | 158 | #endif |
| 158 | 159 | ||
| 159 | size_t WriteStringToFile(bool text_file, const std::string& str, const char* filename); | 160 | size_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)); | |||
| 130 | void ARM_Dynarmic::ExecuteInstructions(int num_instructions) { | 131 | void 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 | ||
| 138 | void ARM_Dynarmic::SaveContext(Core::ThreadContext& ctx) { | 139 | void 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 | ||
| 19 | namespace FileSys { | 22 | namespace 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 | */ | ||
| 28 | class FixSizeDiskFile : public DiskFile { | ||
| 29 | public: | ||
| 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 | |||
| 54 | private: | ||
| 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 | */ | ||
| 65 | class ExtSaveDataArchive : public SaveDataArchive { | ||
| 66 | public: | ||
| 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 | |||
| 21 | std::string GetExtSaveDataPath(const std::string& mount_point, const Path& path) { | 134 | std::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 | ||
| 19 | namespace FileSys { | 19 | namespace FileSys { |
| 20 | 20 | ||
| 21 | static std::string GetSaveDataCheckContainerPath(const std::string& nand_directory) { | 21 | static 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 | ||
| 25 | static std::string GetSaveDataCheckPath(const std::string& mount_point, u32 high, u32 low) { | 25 | static 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 | ||
| 30 | ArchiveFactory_SaveDataCheck::ArchiveFactory_SaveDataCheck(const std::string& nand_directory) | 30 | ArchiveFactory_NCCH::ArchiveFactory_NCCH(const std::string& nand_directory) |
| 31 | : mount_point(GetSaveDataCheckContainerPath(nand_directory)) {} | 31 | : mount_point(GetNCCHContainerPath(nand_directory)) {} |
| 32 | 32 | ||
| 33 | ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SaveDataCheck::Open(const Path& path) { | 33 | ResultVal<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 | ||
| 48 | ResultCode ArchiveFactory_SaveDataCheck::Format(const Path& path, | 48 | ResultCode 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 | ||
| 56 | ResultVal<ArchiveFormatInfo> ArchiveFactory_SaveDataCheck::GetFormatInfo(const Path& path) const { | 56 | ResultVal<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 | ||
| 15 | namespace FileSys { | 15 | namespace FileSys { |
| 16 | 16 | ||
| 17 | /// File system interface to the SaveDataCheck archive | 17 | /// File system interface to the NCCH archive |
| 18 | class ArchiveFactory_SaveDataCheck final : public ArchiveFactory { | 18 | class ArchiveFactory_NCCH final : public ArchiveFactory { |
| 19 | public: | 19 | public: |
| 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 | ||
| 16 | namespace FileSys { | 18 | namespace FileSys { |
| 17 | 19 | ||
| 20 | ResultVal<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 | |||
| 31 | ResultVal<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 | |||
| 87 | ResultCode 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 | |||
| 119 | ResultCode 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 | |||
| 130 | template <typename T> | ||
| 131 | static 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 | |||
| 167 | ResultCode SDMCArchive::DeleteDirectory(const Path& path) const { | ||
| 168 | return DeleteDirectoryHelper(path, mount_point, FileUtil::DeleteDir); | ||
| 169 | } | ||
| 170 | |||
| 171 | ResultCode SDMCArchive::DeleteDirectoryRecursively(const Path& path) const { | ||
| 172 | return DeleteDirectoryHelper( | ||
| 173 | path, mount_point, [](const std::string& p) { return FileUtil::DeleteDirRecursively(p); }); | ||
| 174 | } | ||
| 175 | |||
| 176 | ResultCode 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 | |||
| 219 | ResultCode 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 | |||
| 252 | ResultCode 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 | |||
| 262 | ResultVal<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 | |||
| 290 | u64 SDMCArchive::GetFreeBytes() const { | ||
| 291 | // TODO: Stubbed to return 1GiB | ||
| 292 | return 1024 * 1024 * 1024; | ||
| 293 | } | ||
| 294 | |||
| 18 | ArchiveFactory_SDMC::ArchiveFactory_SDMC(const std::string& sdmc_directory) | 295 | ArchiveFactory_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 | ||
| 37 | ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SDMC::Open(const Path& path) { | 314 | ResultVal<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 | ||
| 15 | namespace FileSys { | 15 | namespace FileSys { |
| 16 | 16 | ||
| 17 | /// Archive backend for SDMC archive | ||
| 18 | class SDMCArchive : public ArchiveBackend { | ||
| 19 | public: | ||
| 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 | |||
| 38 | protected: | ||
| 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 |
| 18 | class ArchiveFactory_SDMC final : public ArchiveFactory { | 44 | class ArchiveFactory_SDMC final : public ArchiveFactory { |
| 19 | public: | 45 | public: |
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 | |||
| 16 | namespace FileSys { | ||
| 17 | |||
| 18 | ResultVal<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 | |||
| 27 | ResultVal<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 | |||
| 33 | ArchiveFactory_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 | |||
| 38 | bool 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 | |||
| 52 | ResultVal<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 | |||
| 57 | ResultCode 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 | |||
| 64 | ResultVal<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 | |||
| 12 | namespace 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 | */ | ||
| 20 | class SDMCWriteOnlyArchive : public SDMCArchive { | ||
| 21 | public: | ||
| 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 | ||
| 35 | class ArchiveFactory_SDMCWriteOnly final : public ArchiveFactory { | ||
| 36 | public: | ||
| 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 | |||
| 53 | private: | ||
| 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 | ||
| 16 | namespace FileSys { | 16 | namespace FileSys { |
| 17 | 17 | ||
| 18 | ResultVal<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 | |||
| 28 | ResultCode 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 | |||
| 46 | bool 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 | |||
| 50 | bool DiskArchive::DeleteDirectory(const Path& path) const { | ||
| 51 | return FileUtil::DeleteDir(mount_point + path.AsString()); | ||
| 52 | } | ||
| 53 | |||
| 54 | bool DiskArchive::DeleteDirectoryRecursively(const Path& path) const { | ||
| 55 | return FileUtil::DeleteDirRecursively(mount_point + path.AsString()); | ||
| 56 | } | ||
| 57 | |||
| 58 | ResultCode 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 | |||
| 84 | bool DiskArchive::CreateDirectory(const Path& path) const { | ||
| 85 | return FileUtil::CreateDir(mount_point + path.AsString()); | ||
| 86 | } | ||
| 87 | |||
| 88 | bool 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 | |||
| 92 | std::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 | |||
| 100 | u64 DiskArchive::GetFreeBytes() const { | ||
| 101 | // TODO: Stubbed to return 1GiB | ||
| 102 | return 1024 * 1024 * 1024; | ||
| 103 | } | ||
| 104 | |||
| 105 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 106 | |||
| 107 | DiskFile::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 | |||
| 115 | ResultCode 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 | |||
| 154 | ResultVal<size_t> DiskFile::Read(const u64 offset, const size_t length, u8* buffer) const { | 18 | ResultVal<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 | ||
| 192 | DiskDirectory::DiskDirectory(const DiskArchive& archive, const Path& path) : directory() { | 56 | DiskDirectory::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 | |||
| 199 | bool 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 | ||
| 209 | u32 DiskDirectory::Read(const u32 count, Entry* entries) { | 63 | u32 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 | ||
| 21 | namespace FileSys { | 21 | namespace 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 | */ | ||
| 28 | class DiskArchive : public ArchiveBackend { | ||
| 29 | public: | ||
| 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 | |||
| 48 | protected: | ||
| 49 | friend class DiskFile; | ||
| 50 | friend class DiskDirectory; | ||
| 51 | |||
| 52 | std::string mount_point; | ||
| 53 | }; | ||
| 54 | |||
| 55 | class DiskFile : public FileBackend { | 23 | class DiskFile : public FileBackend { |
| 56 | public: | 24 | public: |
| 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 | ||
| 70 | protected: | 40 | protected: |
| 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 | ||
| 76 | class DiskDirectory : public DirectoryBackend { | 45 | class DiskDirectory : public DirectoryBackend { |
| 77 | public: | 46 | public: |
| 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 | ||
| 91 | protected: | 59 | protected: |
| 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 | |||
| 7 | namespace FileSys { | ||
| 8 | |||
| 9 | const ResultCode ERROR_INVALID_PATH(ErrorDescription::FS_InvalidPath, ErrorModule::FS, | ||
| 10 | ErrorSummary::InvalidArgument, ErrorLevel::Usage); | ||
| 11 | const ResultCode ERROR_UNSUPPORTED_OPEN_FLAGS(ErrorDescription::FS_UnsupportedOpenFlags, | ||
| 12 | ErrorModule::FS, ErrorSummary::NotSupported, | ||
| 13 | ErrorLevel::Usage); | ||
| 14 | const ResultCode ERROR_INVALID_OPEN_FLAGS(ErrorDescription::FS_InvalidOpenFlags, ErrorModule::FS, | ||
| 15 | ErrorSummary::Canceled, ErrorLevel::Status); | ||
| 16 | const ResultCode ERROR_INVALID_READ_FLAG(ErrorDescription::FS_InvalidReadFlag, ErrorModule::FS, | ||
| 17 | ErrorSummary::InvalidArgument, ErrorLevel::Usage); | ||
| 18 | const ResultCode ERROR_FILE_NOT_FOUND(ErrorDescription::FS_FileNotFound, ErrorModule::FS, | ||
| 19 | ErrorSummary::NotFound, ErrorLevel::Status); | ||
| 20 | const ResultCode ERROR_PATH_NOT_FOUND(ErrorDescription::FS_PathNotFound, ErrorModule::FS, | ||
| 21 | ErrorSummary::NotFound, ErrorLevel::Status); | ||
| 22 | const ResultCode ERROR_NOT_FOUND(ErrorDescription::FS_NotFound, ErrorModule::FS, | ||
| 23 | ErrorSummary::NotFound, ErrorLevel::Status); | ||
| 24 | const ResultCode ERROR_UNEXPECTED_FILE_OR_DIRECTORY(ErrorDescription::FS_UnexpectedFileOrDirectory, | ||
| 25 | ErrorModule::FS, ErrorSummary::NotSupported, | ||
| 26 | ErrorLevel::Usage); | ||
| 27 | const ResultCode ERROR_UNEXPECTED_FILE_OR_DIRECTORY_SDMC(ErrorDescription::FS_NotAFile, | ||
| 28 | ErrorModule::FS, ErrorSummary::Canceled, | ||
| 29 | ErrorLevel::Status); | ||
| 30 | const ResultCode ERROR_DIRECTORY_ALREADY_EXISTS(ErrorDescription::FS_DirectoryAlreadyExists, | ||
| 31 | ErrorModule::FS, ErrorSummary::NothingHappened, | ||
| 32 | ErrorLevel::Status); | ||
| 33 | const ResultCode ERROR_FILE_ALREADY_EXISTS(ErrorDescription::FS_FileAlreadyExists, ErrorModule::FS, | ||
| 34 | ErrorSummary::NothingHappened, ErrorLevel::Status); | ||
| 35 | const ResultCode ERROR_ALREADY_EXISTS(ErrorDescription::FS_AlreadyExists, ErrorModule::FS, | ||
| 36 | ErrorSummary::NothingHappened, ErrorLevel::Status); | ||
| 37 | const 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 | ||
| 20 | ResultVal<std::unique_ptr<FileBackend>> IVFCArchive::OpenFile(const Path& path, | 20 | ResultVal<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 | ||
| 34 | bool IVFCArchive::RenameFile(const Path& src_path, const Path& dest_path) const { | 34 | ResultCode 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 | ||
| 40 | bool IVFCArchive::DeleteDirectory(const Path& path) const { | 41 | ResultCode 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 | ||
| 46 | bool IVFCArchive::DeleteDirectoryRecursively(const Path& path) const { | 48 | ResultCode 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 | ||
| 52 | ResultCode IVFCArchive::CreateFile(const Path& path, u64 size) const { | 55 | ResultCode 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 | ||
| 60 | bool IVFCArchive::CreateDirectory(const Path& path) const { | 63 | ResultCode 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 | ||
| 66 | bool IVFCArchive::RenameDirectory(const Path& src_path, const Path& dest_path) const { | 70 | ResultCode 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 | ||
| 72 | std::unique_ptr<DirectoryBackend> IVFCArchive::OpenDirectory(const Path& path) const { | 77 | ResultVal<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 | ||
| 76 | u64 IVFCArchive::GetFreeBytes() const { | 81 | u64 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 | ||
| 47 | protected: | 47 | protected: |
| @@ -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 | ||
| 76 | class IVFCDirectory : public DirectoryBackend { | 73 | class IVFCDirectory : public DirectoryBackend { |
| 77 | public: | 74 | public: |
| 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 | |||
| 11 | namespace FileSys { | ||
| 12 | |||
| 13 | PathParser::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 | |||
| 60 | PathParser::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 | |||
| 88 | std::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 | |||
| 11 | namespace 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 | */ | ||
| 18 | class PathParser { | ||
| 19 | public: | ||
| 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 | |||
| 55 | private: | ||
| 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 | |||
| 14 | namespace FileSys { | ||
| 15 | |||
| 16 | ResultVal<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 | |||
| 72 | ResultCode 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 | |||
| 104 | ResultCode 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 | |||
| 115 | template <typename T> | ||
| 116 | static 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 | |||
| 152 | ResultCode SaveDataArchive::DeleteDirectory(const Path& path) const { | ||
| 153 | return DeleteDirectoryHelper(path, mount_point, FileUtil::DeleteDir); | ||
| 154 | } | ||
| 155 | |||
| 156 | ResultCode SaveDataArchive::DeleteDirectoryRecursively(const Path& path) const { | ||
| 157 | return DeleteDirectoryHelper( | ||
| 158 | path, mount_point, [](const std::string& p) { return FileUtil::DeleteDirRecursively(p); }); | ||
| 159 | } | ||
| 160 | |||
| 161 | ResultCode 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 | |||
| 204 | ResultCode 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 | |||
| 239 | ResultCode 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 | |||
| 249 | ResultVal<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 | |||
| 278 | u64 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 | |||
| 16 | namespace FileSys { | ||
| 17 | |||
| 18 | /// Archive backend for general save data archive type (SaveData and SystemSaveData) | ||
| 19 | class SaveDataArchive : public ArchiveBackend { | ||
| 20 | public: | ||
| 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 | |||
| 39 | protected: | ||
| 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 { | |||
| 10 | namespace Applets { | 10 | namespace Applets { |
| 11 | 11 | ||
| 12 | ResultCode ErrEula::ReceiveParameter(const Service::APT::MessageParameter& parameter) { | 12 | ResultCode 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 { | |||
| 19 | namespace Applets { | 19 | namespace Applets { |
| 20 | 20 | ||
| 21 | ResultCode MiiSelector::ReceiveParameter(const Service::APT::MessageParameter& parameter) { | 21 | ResultCode 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 { | |||
| 22 | namespace Applets { | 22 | namespace Applets { |
| 23 | 23 | ||
| 24 | ResultCode SoftwareKeyboard::ReceiveParameter(Service::APT::MessageParameter const& parameter) { | 24 | ResultCode 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 | ||
| 399 | void 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 | |||
| 399 | void SetScreenCapPostPermission(Service::Interface* self) { | 408 | void 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 |
| 47 | enum class SignalType : u32 { | 47 | enum 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); | |||
| 381 | void StartLibraryApplet(Service::Interface* self); | 392 | void 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 | */ | ||
| 403 | void 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, | |||
| 45 | enum ConfigBlockID { | 45 | enum 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 | ||
| 361 | ResultCode DeleteConfigNANDSaveFile() { | 362 | ResultCode 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() { | |||
| 382 | ResultCode FormatConfig() { | 383 | ResultCode 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 | ||
| 13 | namespace ERR_F { | 19 | namespace ERR_F { |
| 14 | 20 | ||
| 15 | enum { | 21 | enum 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 | |||
| 30 | enum class ExceptionType : u32 { | ||
| 31 | PrefetchAbort = 0, | ||
| 32 | DataAbort = 1, | ||
| 33 | Undefined = 2, | ||
| 34 | VectorFP = 3, | ||
| 35 | }; | ||
| 36 | |||
| 37 | struct 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 | }; | ||
| 46 | static_assert(sizeof(ExceptionInfo) == 0x18, "ExceptionInfo struct has incorrect size"); | ||
| 47 | |||
| 48 | struct ExceptionContext final { | ||
| 49 | std::array<u32, 16> arm_regs; | ||
| 50 | u32 cpsr; | ||
| 20 | }; | 51 | }; |
| 52 | static_assert(sizeof(ExceptionContext) == 0x44, "ExceptionContext struct has incorrect size"); | ||
| 53 | |||
| 54 | struct ExceptionData { | ||
| 55 | ExceptionInfo exception_info; | ||
| 56 | ExceptionContext exception_context; | ||
| 57 | INSERT_PADDING_WORDS(1); | ||
| 58 | }; | ||
| 59 | static_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 | ||
| 33 | union ErrInfo { | 72 | struct 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 | ||
| 87 | enum { PrefetchAbort = 0, DataAbort = 1, UndefInstr = 2, VectorFP = 3 }; | 102 | static 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 | ||
| 89 | static std::string GetErrInfo3Type(u8 type_code) { | 121 | static 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 | ||
| 136 | static 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 | |||
| 145 | static 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 | */ | ||
| 104 | static void ThrowFatalError(Service::Interface* self) { | 170 | static 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 | ||
| 175 | const Interface::FunctionInfo FunctionTable[] = { | 252 | const 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 | ||
| 354 | ResultCode DeleteDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) { | 349 | ResultCode 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 | ||
| 365 | ResultCode DeleteDirectoryRecursivelyFromArchive(ArchiveHandle archive_handle, | 357 | ResultCode 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 | ||
| 377 | ResultCode CreateFileInArchive(ArchiveHandle archive_handle, const FileSys::Path& path, | 366 | ResultCode 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 | ||
| 397 | ResultCode RenameDirectoryBetweenArchives(ArchiveHandle src_archive_handle, | 383 | ResultCode 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 | ||
| 420 | ResultVal<Kernel::SharedPtr<Directory>> OpenDirectoryFromArchive(ArchiveHandle archive_handle, | 400 | ResultVal<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 | ||
| 10 | namespace MIC_U { | 13 | namespace MIC_U { |
| 11 | 14 | ||
| 15 | enum class Encoding : u8 { | ||
| 16 | PCM8 = 0, | ||
| 17 | PCM16 = 1, | ||
| 18 | PCM8Signed = 2, | ||
| 19 | PCM16Signed = 3, | ||
| 20 | }; | ||
| 21 | |||
| 22 | enum class SampleRate : u8 { | ||
| 23 | SampleRate32730 = 0, | ||
| 24 | SampleRate16360 = 1, | ||
| 25 | SampleRate10910 = 2, | ||
| 26 | SampleRate8180 = 3 | ||
| 27 | }; | ||
| 28 | |||
| 29 | static Kernel::SharedPtr<Kernel::Event> buffer_full_event; | ||
| 30 | static Kernel::SharedPtr<Kernel::SharedMemory> shared_memory; | ||
| 31 | static u8 mic_gain = 0; | ||
| 32 | static bool mic_power = false; | ||
| 33 | static bool is_sampling = false; | ||
| 34 | static bool allow_shell_closed; | ||
| 35 | static bool clamp = false; | ||
| 36 | static Encoding encoding; | ||
| 37 | static SampleRate sample_rate; | ||
| 38 | static s32 audio_buffer_offset; | ||
| 39 | static u32 audio_buffer_size; | ||
| 40 | static 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 | */ | ||
| 52 | static 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 | */ | ||
| 71 | static 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 | */ | ||
| 90 | static 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 | */ | ||
| 114 | static 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 | */ | ||
| 128 | static 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 | */ | ||
| 143 | static 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 | */ | ||
| 158 | static 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 | */ | ||
| 173 | static 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 | */ | ||
| 188 | static 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 | */ | ||
| 203 | static 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 | */ | ||
| 218 | static 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 | */ | ||
| 235 | static 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 | */ | ||
| 253 | static 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 | */ | ||
| 268 | static 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 | */ | ||
| 283 | static 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 | |||
| 12 | const Interface::FunctionInfo FunctionTable[] = { | 290 | const 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 | ||
| 34 | Interface::Interface() { | 312 | Interface::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 | |||
| 323 | Interface::~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 { | |||
| 16 | class Interface : public Service::Interface { | 16 | class Interface : public Service::Interface { |
| 17 | public: | 17 | public: |
| 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 | ||
| 30 | Regs g_regs; | 30 | Regs g_regs; |
| 31 | 31 | ||
| 32 | /// True if the current frame was skipped | ||
| 33 | bool g_skip_frame; | ||
| 34 | /// 268MHz CPU clocks / 60Hz frames per second | 32 | /// 268MHz CPU clocks / 60Hz frames per second |
| 35 | const u64 frame_ticks = 268123480ull / 60; | 33 | const u64 frame_ticks = 268123480ull / 60; |
| 36 | /// Event id for CoreTiming | 34 | /// Event id for CoreTiming |
| 37 | static int vblank_event; | 35 | static int vblank_event; |
| 38 | /// Total number of frames drawn | 36 | /// Total number of frames drawn |
| 39 | static u64 frame_count; | 37 | static u64 frame_count; |
| 40 | /// True if the last frame was skipped | ||
| 41 | static bool last_skip_frame; | ||
| 42 | 38 | ||
| 43 | template <typename T> | 39 | template <typename T> |
| 44 | inline void Read(T& var, const u32 raw_addr) { | 40 | inline 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 |
| 520 | static void VBlankCallback(u64 userdata, int cycles_late) { | 516 | static 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); | |||
| 316 | static_assert(sizeof(Regs) == 0x1000 * sizeof(u32), "Invalid total size of register set"); | 316 | static_assert(sizeof(Regs) == 0x1000 * sizeof(u32), "Invalid total size of register set"); |
| 317 | 317 | ||
| 318 | extern Regs g_regs; | 318 | extern Regs g_regs; |
| 319 | extern bool g_skip_frame; | ||
| 320 | 319 | ||
| 321 | template <typename T> | 320 | template <typename T> |
| 322 | void Read(T& var, const u32 addr); | 321 | void 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 | */ |
| 47 | struct PageTable { | 47 | struct 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 |
| 78 | static PageTable* current_page_table = &main_page_table; | 76 | static PageTable* current_page_table = &main_page_table; |
| 79 | 77 | ||
| 78 | std::array<u8*, PAGE_TABLE_NUM_ENTRIES>* GetCurrentPageTablePointers() { | ||
| 79 | return ¤t_page_table->pointers; | ||
| 80 | } | ||
| 81 | |||
| 80 | static void MapPages(u32 base, u32 size, u8* memory, PageType type) { | 82 | static 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 { | |||
| 17 | const u32 PAGE_SIZE = 0x1000; | 18 | const u32 PAGE_SIZE = 0x1000; |
| 18 | const u32 PAGE_MASK = PAGE_SIZE - 1; | 19 | const u32 PAGE_MASK = PAGE_SIZE - 1; |
| 19 | const int PAGE_BITS = 12; | 20 | const int PAGE_BITS = 12; |
| 21 | const 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 |
| 22 | enum : PAddr { | 24 | enum : 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 | */ |
| 168 | void RasterizerFlushAndInvalidateRegion(PAddr start, u32 size); | 170 | void 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 | */ | ||
| 177 | std::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 @@ | |||
| 1 | set(SRCS | 1 | set(SRCS |
| 2 | glad.cpp | ||
| 2 | tests.cpp | 3 | tests.cpp |
| 4 | core/file_sys/path_parser.cpp | ||
| 3 | ) | 5 | ) |
| 4 | 6 | ||
| 5 | set(HEADERS | 7 | set(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 | |||
| 9 | namespace FileSys { | ||
| 10 | |||
| 11 | TEST_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 | |||
| 23 | TEST_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. | ||
| 12 | TEST_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 | ||
| 46 | struct Regs { | 46 | struct 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; |