diff options
131 files changed, 3574 insertions, 1161 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 6021a8054..918cf5372 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt | |||
| @@ -23,21 +23,21 @@ option(ENABLE_CUBEB "Enables the cubeb audio backend" ON) | |||
| 23 | 23 | ||
| 24 | option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF) | 24 | option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF) |
| 25 | 25 | ||
| 26 | if(NOT EXISTS ${CMAKE_SOURCE_DIR}/.git/hooks/pre-commit) | 26 | if(NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/hooks/pre-commit) |
| 27 | message(STATUS "Copying pre-commit hook") | 27 | message(STATUS "Copying pre-commit hook") |
| 28 | file(COPY hooks/pre-commit | 28 | file(COPY hooks/pre-commit |
| 29 | DESTINATION ${CMAKE_SOURCE_DIR}/.git/hooks) | 29 | DESTINATION ${PROJECT_SOURCE_DIR}/.git/hooks) |
| 30 | endif() | 30 | endif() |
| 31 | 31 | ||
| 32 | # Sanity check : Check that all submodules are present | 32 | # Sanity check : Check that all submodules are present |
| 33 | # ======================================================================= | 33 | # ======================================================================= |
| 34 | 34 | ||
| 35 | function(check_submodules_present) | 35 | function(check_submodules_present) |
| 36 | file(READ "${CMAKE_SOURCE_DIR}/.gitmodules" gitmodules) | 36 | file(READ "${PROJECT_SOURCE_DIR}/.gitmodules" gitmodules) |
| 37 | string(REGEX MATCHALL "path *= *[^ \t\r\n]*" gitmodules ${gitmodules}) | 37 | string(REGEX MATCHALL "path *= *[^ \t\r\n]*" gitmodules ${gitmodules}) |
| 38 | foreach(module ${gitmodules}) | 38 | foreach(module ${gitmodules}) |
| 39 | string(REGEX REPLACE "path *= *" "" module ${module}) | 39 | string(REGEX REPLACE "path *= *" "" module ${module}) |
| 40 | if (NOT EXISTS "${CMAKE_SOURCE_DIR}/${module}/.git") | 40 | if (NOT EXISTS "${PROJECT_SOURCE_DIR}/${module}/.git") |
| 41 | message(FATAL_ERROR "Git submodule ${module} not found. " | 41 | message(FATAL_ERROR "Git submodule ${module} not found. " |
| 42 | "Please run: git submodule update --init --recursive") | 42 | "Please run: git submodule update --init --recursive") |
| 43 | endif() | 43 | endif() |
| @@ -45,17 +45,17 @@ function(check_submodules_present) | |||
| 45 | endfunction() | 45 | endfunction() |
| 46 | check_submodules_present() | 46 | check_submodules_present() |
| 47 | 47 | ||
| 48 | configure_file(${CMAKE_SOURCE_DIR}/dist/compatibility_list/compatibility_list.qrc | 48 | configure_file(${PROJECT_SOURCE_DIR}/dist/compatibility_list/compatibility_list.qrc |
| 49 | ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc | 49 | ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc |
| 50 | COPYONLY) | 50 | COPYONLY) |
| 51 | if (ENABLE_COMPATIBILITY_LIST_DOWNLOAD AND NOT EXISTS ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json) | 51 | if (ENABLE_COMPATIBILITY_LIST_DOWNLOAD AND NOT EXISTS ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json) |
| 52 | message(STATUS "Downloading compatibility list for yuzu...") | 52 | message(STATUS "Downloading compatibility list for yuzu...") |
| 53 | file(DOWNLOAD | 53 | file(DOWNLOAD |
| 54 | https://api.yuzu-emu.org/gamedb/ | 54 | https://api.yuzu-emu.org/gamedb/ |
| 55 | "${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json" SHOW_PROGRESS) | 55 | "${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json" SHOW_PROGRESS) |
| 56 | endif() | 56 | endif() |
| 57 | if (NOT EXISTS ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json) | 57 | if (NOT EXISTS ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json) |
| 58 | file(WRITE ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json "") | 58 | file(WRITE ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json "") |
| 59 | endif() | 59 | endif() |
| 60 | 60 | ||
| 61 | # Detect current compilation architecture and create standard definitions | 61 | # Detect current compilation architecture and create standard definitions |
| @@ -170,7 +170,7 @@ endif() | |||
| 170 | # On modern Unixes, this is typically already the case. The lone exception is | 170 | # On modern Unixes, this is typically already the case. The lone exception is |
| 171 | # glibc, which may default to 32 bits. glibc allows this to be configured | 171 | # glibc, which may default to 32 bits. glibc allows this to be configured |
| 172 | # by setting _FILE_OFFSET_BITS. | 172 | # by setting _FILE_OFFSET_BITS. |
| 173 | if(CMAKE_SYSTEM_NAME STREQUAL "Linux") | 173 | if(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR MINGW) |
| 174 | add_definitions(-D_FILE_OFFSET_BITS=64) | 174 | add_definitions(-D_FILE_OFFSET_BITS=64) |
| 175 | endif() | 175 | endif() |
| 176 | 176 | ||
| @@ -178,10 +178,6 @@ endif() | |||
| 178 | set_property(DIRECTORY APPEND PROPERTY | 178 | set_property(DIRECTORY APPEND PROPERTY |
| 179 | COMPILE_DEFINITIONS $<$<CONFIG:Debug>:_DEBUG> $<$<NOT:$<CONFIG:Debug>>:NDEBUG>) | 179 | COMPILE_DEFINITIONS $<$<CONFIG:Debug>:_DEBUG> $<$<NOT:$<CONFIG:Debug>>:NDEBUG>) |
| 180 | 180 | ||
| 181 | |||
| 182 | math(EXPR EMU_ARCH_BITS ${CMAKE_SIZEOF_VOID_P}*8) | ||
| 183 | add_definitions(-DEMU_ARCH_BITS=${EMU_ARCH_BITS}) | ||
| 184 | |||
| 185 | # System imported libraries | 181 | # System imported libraries |
| 186 | # ====================== | 182 | # ====================== |
| 187 | 183 | ||
| @@ -189,13 +185,13 @@ find_package(Boost 1.63.0 QUIET) | |||
| 189 | if (NOT Boost_FOUND) | 185 | if (NOT Boost_FOUND) |
| 190 | message(STATUS "Boost 1.63.0 or newer not found, falling back to externals") | 186 | message(STATUS "Boost 1.63.0 or newer not found, falling back to externals") |
| 191 | 187 | ||
| 192 | set(BOOST_ROOT "${CMAKE_SOURCE_DIR}/externals/boost") | 188 | set(BOOST_ROOT "${PROJECT_SOURCE_DIR}/externals/boost") |
| 193 | set(Boost_NO_SYSTEM_PATHS OFF) | 189 | set(Boost_NO_SYSTEM_PATHS OFF) |
| 194 | find_package(Boost QUIET REQUIRED) | 190 | find_package(Boost QUIET REQUIRED) |
| 195 | endif() | 191 | endif() |
| 196 | 192 | ||
| 197 | # Output binaries to bin/ | 193 | # Output binaries to bin/ |
| 198 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) | 194 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin) |
| 199 | 195 | ||
| 200 | # Prefer the -pthread flag on Linux. | 196 | # Prefer the -pthread flag on Linux. |
| 201 | set(THREADS_PREFER_PTHREAD_FLAG ON) | 197 | set(THREADS_PREFER_PTHREAD_FLAG ON) |
| @@ -264,7 +260,7 @@ if (YUZU_USE_BUNDLED_UNICORN) | |||
| 264 | endif() | 260 | endif() |
| 265 | 261 | ||
| 266 | set(UNICORN_FOUND YES) | 262 | set(UNICORN_FOUND YES) |
| 267 | set(UNICORN_PREFIX ${CMAKE_SOURCE_DIR}/externals/unicorn) | 263 | set(UNICORN_PREFIX ${PROJECT_SOURCE_DIR}/externals/unicorn) |
| 268 | set(LIBUNICORN_LIBRARY "${UNICORN_PREFIX}/${UNICORN_LIB_NAME}" CACHE PATH "Path to Unicorn library" FORCE) | 264 | set(LIBUNICORN_LIBRARY "${UNICORN_PREFIX}/${UNICORN_LIB_NAME}" CACHE PATH "Path to Unicorn library" FORCE) |
| 269 | set(LIBUNICORN_INCLUDE_DIR "${UNICORN_PREFIX}/include" CACHE PATH "Path to Unicorn headers" FORCE) | 265 | set(LIBUNICORN_INCLUDE_DIR "${UNICORN_PREFIX}/include" CACHE PATH "Path to Unicorn headers" FORCE) |
| 270 | set(UNICORN_DLL_DIR "${UNICORN_PREFIX}/" CACHE PATH "Path to unicorn dynamic library" FORCE) | 266 | set(UNICORN_DLL_DIR "${UNICORN_PREFIX}/" CACHE PATH "Path to unicorn dynamic library" FORCE) |
| @@ -356,12 +352,12 @@ set(CLANG_FORMAT_POSTFIX "-6.0") | |||
| 356 | find_program(CLANG_FORMAT | 352 | find_program(CLANG_FORMAT |
| 357 | NAMES clang-format${CLANG_FORMAT_POSTFIX} | 353 | NAMES clang-format${CLANG_FORMAT_POSTFIX} |
| 358 | clang-format | 354 | clang-format |
| 359 | PATHS ${CMAKE_BINARY_DIR}/externals) | 355 | PATHS ${PROJECT_BINARY_DIR}/externals) |
| 360 | # if find_program doesn't find it, try to download from externals | 356 | # if find_program doesn't find it, try to download from externals |
| 361 | if (NOT CLANG_FORMAT) | 357 | if (NOT CLANG_FORMAT) |
| 362 | if (WIN32) | 358 | if (WIN32) |
| 363 | message(STATUS "Clang format not found! Downloading...") | 359 | message(STATUS "Clang format not found! Downloading...") |
| 364 | set(CLANG_FORMAT "${CMAKE_BINARY_DIR}/externals/clang-format${CLANG_FORMAT_POSTFIX}.exe") | 360 | set(CLANG_FORMAT "${PROJECT_BINARY_DIR}/externals/clang-format${CLANG_FORMAT_POSTFIX}.exe") |
| 365 | file(DOWNLOAD | 361 | file(DOWNLOAD |
| 366 | https://github.com/yuzu-emu/ext-windows-bin/raw/master/clang-format${CLANG_FORMAT_POSTFIX}.exe | 362 | https://github.com/yuzu-emu/ext-windows-bin/raw/master/clang-format${CLANG_FORMAT_POSTFIX}.exe |
| 367 | "${CLANG_FORMAT}" SHOW_PROGRESS | 363 | "${CLANG_FORMAT}" SHOW_PROGRESS |
| @@ -377,7 +373,7 @@ if (NOT CLANG_FORMAT) | |||
| 377 | endif() | 373 | endif() |
| 378 | 374 | ||
| 379 | if (CLANG_FORMAT) | 375 | if (CLANG_FORMAT) |
| 380 | set(SRCS ${CMAKE_SOURCE_DIR}/src) | 376 | set(SRCS ${PROJECT_SOURCE_DIR}/src) |
| 381 | set(CCOMMENT "Running clang format against all the .h and .cpp files in src/") | 377 | set(CCOMMENT "Running clang format against all the .h and .cpp files in src/") |
| 382 | if (WIN32) | 378 | if (WIN32) |
| 383 | add_custom_target(clang-format | 379 | add_custom_target(clang-format |
| @@ -450,10 +446,10 @@ endif() | |||
| 450 | # http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html | 446 | # http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html |
| 451 | # http://standards.freedesktop.org/shared-mime-info-spec/shared-mime-info-spec-latest.html | 447 | # http://standards.freedesktop.org/shared-mime-info-spec/shared-mime-info-spec-latest.html |
| 452 | if(ENABLE_QT AND UNIX AND NOT APPLE) | 448 | if(ENABLE_QT AND UNIX AND NOT APPLE) |
| 453 | install(FILES "${CMAKE_SOURCE_DIR}/dist/yuzu.desktop" | 449 | install(FILES "${PROJECT_SOURCE_DIR}/dist/yuzu.desktop" |
| 454 | DESTINATION "${CMAKE_INSTALL_PREFIX}/share/applications") | 450 | DESTINATION "${CMAKE_INSTALL_PREFIX}/share/applications") |
| 455 | install(FILES "${CMAKE_SOURCE_DIR}/dist/yuzu.svg" | 451 | install(FILES "${PROJECT_SOURCE_DIR}/dist/yuzu.svg" |
| 456 | DESTINATION "${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/scalable/apps") | 452 | DESTINATION "${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/scalable/apps") |
| 457 | install(FILES "${CMAKE_SOURCE_DIR}/dist/yuzu.xml" | 453 | install(FILES "${PROJECT_SOURCE_DIR}/dist/yuzu.xml" |
| 458 | DESTINATION "${CMAKE_INSTALL_PREFIX}/share/mime/packages") | 454 | DESTINATION "${CMAKE_INSTALL_PREFIX}/share/mime/packages") |
| 459 | endif() | 455 | endif() |
diff --git a/src/audio_core/time_stretch.cpp b/src/audio_core/time_stretch.cpp index d72d67994..cee8b12dd 100644 --- a/src/audio_core/time_stretch.cpp +++ b/src/audio_core/time_stretch.cpp | |||
| @@ -10,8 +10,7 @@ | |||
| 10 | 10 | ||
| 11 | namespace AudioCore { | 11 | namespace AudioCore { |
| 12 | 12 | ||
| 13 | TimeStretcher::TimeStretcher(u32 sample_rate, u32 channel_count) | 13 | TimeStretcher::TimeStretcher(u32 sample_rate, u32 channel_count) : m_sample_rate{sample_rate} { |
| 14 | : m_sample_rate(sample_rate), m_channel_count(channel_count) { | ||
| 15 | m_sound_touch.setChannels(channel_count); | 14 | m_sound_touch.setChannels(channel_count); |
| 16 | m_sound_touch.setSampleRate(sample_rate); | 15 | m_sound_touch.setSampleRate(sample_rate); |
| 17 | m_sound_touch.setPitch(1.0); | 16 | m_sound_touch.setPitch(1.0); |
diff --git a/src/audio_core/time_stretch.h b/src/audio_core/time_stretch.h index decd760f1..bb2270b96 100644 --- a/src/audio_core/time_stretch.h +++ b/src/audio_core/time_stretch.h | |||
| @@ -27,7 +27,6 @@ public: | |||
| 27 | 27 | ||
| 28 | private: | 28 | private: |
| 29 | u32 m_sample_rate; | 29 | u32 m_sample_rate; |
| 30 | u32 m_channel_count; | ||
| 31 | soundtouch::SoundTouch m_sound_touch; | 30 | soundtouch::SoundTouch m_sound_touch; |
| 32 | double m_stretch_ratio = 1.0; | 31 | double m_stretch_ratio = 1.0; |
| 33 | }; | 32 | }; |
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index d0e506689..eccd8f64a 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt | |||
| @@ -64,8 +64,6 @@ add_library(common STATIC | |||
| 64 | logging/text_formatter.cpp | 64 | logging/text_formatter.cpp |
| 65 | logging/text_formatter.h | 65 | logging/text_formatter.h |
| 66 | math_util.h | 66 | math_util.h |
| 67 | memory_util.cpp | ||
| 68 | memory_util.h | ||
| 69 | microprofile.cpp | 67 | microprofile.cpp |
| 70 | microprofile.h | 68 | microprofile.h |
| 71 | microprofileui.h | 69 | microprofileui.h |
diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp index 548463787..b52492da6 100644 --- a/src/common/file_util.cpp +++ b/src/common/file_util.cpp | |||
| @@ -15,21 +15,24 @@ | |||
| 15 | #ifdef _WIN32 | 15 | #ifdef _WIN32 |
| 16 | #include <windows.h> | 16 | #include <windows.h> |
| 17 | // windows.h needs to be included before other windows headers | 17 | // windows.h needs to be included before other windows headers |
| 18 | #include <commdlg.h> // for GetSaveFileName | 18 | #include <direct.h> // getcwd |
| 19 | #include <direct.h> // getcwd | ||
| 20 | #include <io.h> | 19 | #include <io.h> |
| 21 | #include <shellapi.h> | 20 | #include <shellapi.h> |
| 22 | #include <shlobj.h> // for SHGetFolderPath | 21 | #include <shlobj.h> // for SHGetFolderPath |
| 23 | #include <tchar.h> | 22 | #include <tchar.h> |
| 24 | #include "common/string_util.h" | 23 | #include "common/string_util.h" |
| 25 | 24 | ||
| 26 | // 64 bit offsets for windows | 25 | #ifdef _MSC_VER |
| 26 | // 64 bit offsets for MSVC | ||
| 27 | #define fseeko _fseeki64 | 27 | #define fseeko _fseeki64 |
| 28 | #define ftello _ftelli64 | 28 | #define ftello _ftelli64 |
| 29 | #define atoll _atoi64 | 29 | #define fileno _fileno |
| 30 | #endif | ||
| 31 | |||
| 32 | // 64 bit offsets for MSVC and MinGW. MinGW also needs this for using _wstat64 | ||
| 30 | #define stat _stat64 | 33 | #define stat _stat64 |
| 31 | #define fstat _fstat64 | 34 | #define fstat _fstat64 |
| 32 | #define fileno _fileno | 35 | |
| 33 | #else | 36 | #else |
| 34 | #ifdef __APPLE__ | 37 | #ifdef __APPLE__ |
| 35 | #include <sys/param.h> | 38 | #include <sys/param.h> |
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index 9f5918851..6d5218465 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp | |||
| @@ -196,6 +196,7 @@ void FileBackend::Write(const Entry& entry) { | |||
| 196 | SUB(Service, NFP) \ | 196 | SUB(Service, NFP) \ |
| 197 | SUB(Service, NIFM) \ | 197 | SUB(Service, NIFM) \ |
| 198 | SUB(Service, NIM) \ | 198 | SUB(Service, NIM) \ |
| 199 | SUB(Service, NPNS) \ | ||
| 199 | SUB(Service, NS) \ | 200 | SUB(Service, NS) \ |
| 200 | SUB(Service, NVDRV) \ | 201 | SUB(Service, NVDRV) \ |
| 201 | SUB(Service, PCIE) \ | 202 | SUB(Service, PCIE) \ |
| @@ -204,10 +205,12 @@ void FileBackend::Write(const Entry& entry) { | |||
| 204 | SUB(Service, PM) \ | 205 | SUB(Service, PM) \ |
| 205 | SUB(Service, PREPO) \ | 206 | SUB(Service, PREPO) \ |
| 206 | SUB(Service, PSC) \ | 207 | SUB(Service, PSC) \ |
| 208 | SUB(Service, PSM) \ | ||
| 207 | SUB(Service, SET) \ | 209 | SUB(Service, SET) \ |
| 208 | SUB(Service, SM) \ | 210 | SUB(Service, SM) \ |
| 209 | SUB(Service, SPL) \ | 211 | SUB(Service, SPL) \ |
| 210 | SUB(Service, SSL) \ | 212 | SUB(Service, SSL) \ |
| 213 | SUB(Service, TCAP) \ | ||
| 211 | SUB(Service, Time) \ | 214 | SUB(Service, Time) \ |
| 212 | SUB(Service, USB) \ | 215 | SUB(Service, USB) \ |
| 213 | SUB(Service, VI) \ | 216 | SUB(Service, VI) \ |
diff --git a/src/common/logging/log.h b/src/common/logging/log.h index abbd056ee..d4ec31ec3 100644 --- a/src/common/logging/log.h +++ b/src/common/logging/log.h | |||
| @@ -83,6 +83,7 @@ enum class Class : ClassType { | |||
| 83 | Service_NFP, ///< The NFP service | 83 | Service_NFP, ///< The NFP service |
| 84 | Service_NIFM, ///< The NIFM (Network interface) service | 84 | Service_NIFM, ///< The NIFM (Network interface) service |
| 85 | Service_NIM, ///< The NIM service | 85 | Service_NIM, ///< The NIM service |
| 86 | Service_NPNS, ///< The NPNS service | ||
| 86 | Service_NS, ///< The NS services | 87 | Service_NS, ///< The NS services |
| 87 | Service_NVDRV, ///< The NVDRV (Nvidia driver) service | 88 | Service_NVDRV, ///< The NVDRV (Nvidia driver) service |
| 88 | Service_PCIE, ///< The PCIe service | 89 | Service_PCIE, ///< The PCIe service |
| @@ -91,10 +92,12 @@ enum class Class : ClassType { | |||
| 91 | Service_PM, ///< The PM service | 92 | Service_PM, ///< The PM service |
| 92 | Service_PREPO, ///< The PREPO (Play report) service | 93 | Service_PREPO, ///< The PREPO (Play report) service |
| 93 | Service_PSC, ///< The PSC service | 94 | Service_PSC, ///< The PSC service |
| 95 | Service_PSM, ///< The PSM service | ||
| 94 | Service_SET, ///< The SET (Settings) service | 96 | Service_SET, ///< The SET (Settings) service |
| 95 | Service_SM, ///< The SM (Service manager) service | 97 | Service_SM, ///< The SM (Service manager) service |
| 96 | Service_SPL, ///< The SPL service | 98 | Service_SPL, ///< The SPL service |
| 97 | Service_SSL, ///< The SSL service | 99 | Service_SSL, ///< The SSL service |
| 100 | Service_TCAP, ///< The TCAP service. | ||
| 98 | Service_Time, ///< The time service | 101 | Service_Time, ///< The time service |
| 99 | Service_USB, ///< The USB (Universal Serial Bus) service | 102 | Service_USB, ///< The USB (Universal Serial Bus) service |
| 100 | Service_VI, ///< The VI (Video interface) service | 103 | Service_VI, ///< The VI (Video interface) service |
diff --git a/src/common/memory_util.cpp b/src/common/memory_util.cpp deleted file mode 100644 index 9736fb12a..000000000 --- a/src/common/memory_util.cpp +++ /dev/null | |||
| @@ -1,177 +0,0 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/logging/log.h" | ||
| 6 | #include "common/memory_util.h" | ||
| 7 | |||
| 8 | #ifdef _WIN32 | ||
| 9 | #include <windows.h> | ||
| 10 | // Windows.h needs to be included before psapi.h | ||
| 11 | #include <psapi.h> | ||
| 12 | #include "common/common_funcs.h" | ||
| 13 | #include "common/string_util.h" | ||
| 14 | #else | ||
| 15 | #include <cstdlib> | ||
| 16 | #include <sys/mman.h> | ||
| 17 | #endif | ||
| 18 | |||
| 19 | #if !defined(_WIN32) && defined(ARCHITECTURE_x86_64) && !defined(MAP_32BIT) | ||
| 20 | #include <unistd.h> | ||
| 21 | #define PAGE_MASK (getpagesize() - 1) | ||
| 22 | #define round_page(x) ((((unsigned long)(x)) + PAGE_MASK) & ~(PAGE_MASK)) | ||
| 23 | #endif | ||
| 24 | |||
| 25 | // This is purposely not a full wrapper for virtualalloc/mmap, but it | ||
| 26 | // provides exactly the primitive operations that Dolphin needs. | ||
| 27 | |||
| 28 | void* AllocateExecutableMemory(std::size_t size, bool low) { | ||
| 29 | #if defined(_WIN32) | ||
| 30 | void* ptr = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE); | ||
| 31 | #else | ||
| 32 | static char* map_hint = nullptr; | ||
| 33 | #if defined(ARCHITECTURE_x86_64) && !defined(MAP_32BIT) | ||
| 34 | // This OS has no flag to enforce allocation below the 4 GB boundary, | ||
| 35 | // but if we hint that we want a low address it is very likely we will | ||
| 36 | // get one. | ||
| 37 | // An older version of this code used MAP_FIXED, but that has the side | ||
| 38 | // effect of discarding already mapped pages that happen to be in the | ||
| 39 | // requested virtual memory range (such as the emulated RAM, sometimes). | ||
| 40 | if (low && (!map_hint)) | ||
| 41 | map_hint = (char*)round_page(512 * 1024 * 1024); /* 0.5 GB rounded up to the next page */ | ||
| 42 | #endif | ||
| 43 | void* ptr = mmap(map_hint, size, PROT_READ | PROT_WRITE | PROT_EXEC, | ||
| 44 | MAP_ANON | MAP_PRIVATE | ||
| 45 | #if defined(ARCHITECTURE_x86_64) && defined(MAP_32BIT) | ||
| 46 | | (low ? MAP_32BIT : 0) | ||
| 47 | #endif | ||
| 48 | , | ||
| 49 | -1, 0); | ||
| 50 | #endif /* defined(_WIN32) */ | ||
| 51 | |||
| 52 | #ifdef _WIN32 | ||
| 53 | if (ptr == nullptr) { | ||
| 54 | #else | ||
| 55 | if (ptr == MAP_FAILED) { | ||
| 56 | ptr = nullptr; | ||
| 57 | #endif | ||
| 58 | LOG_ERROR(Common_Memory, "Failed to allocate executable memory"); | ||
| 59 | } | ||
| 60 | #if !defined(_WIN32) && defined(ARCHITECTURE_x86_64) && !defined(MAP_32BIT) | ||
| 61 | else { | ||
| 62 | if (low) { | ||
| 63 | map_hint += size; | ||
| 64 | map_hint = (char*)round_page(map_hint); /* round up to the next page */ | ||
| 65 | } | ||
| 66 | } | ||
| 67 | #endif | ||
| 68 | |||
| 69 | #if EMU_ARCH_BITS == 64 | ||
| 70 | if ((u64)ptr >= 0x80000000 && low == true) | ||
| 71 | LOG_ERROR(Common_Memory, "Executable memory ended up above 2GB!"); | ||
| 72 | #endif | ||
| 73 | |||
| 74 | return ptr; | ||
| 75 | } | ||
| 76 | |||
| 77 | void* AllocateMemoryPages(std::size_t size) { | ||
| 78 | #ifdef _WIN32 | ||
| 79 | void* ptr = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE); | ||
| 80 | #else | ||
| 81 | void* ptr = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); | ||
| 82 | |||
| 83 | if (ptr == MAP_FAILED) | ||
| 84 | ptr = nullptr; | ||
| 85 | #endif | ||
| 86 | |||
| 87 | if (ptr == nullptr) | ||
| 88 | LOG_ERROR(Common_Memory, "Failed to allocate raw memory"); | ||
| 89 | |||
| 90 | return ptr; | ||
| 91 | } | ||
| 92 | |||
| 93 | void* AllocateAlignedMemory(std::size_t size, std::size_t alignment) { | ||
| 94 | #ifdef _WIN32 | ||
| 95 | void* ptr = _aligned_malloc(size, alignment); | ||
| 96 | #else | ||
| 97 | void* ptr = nullptr; | ||
| 98 | #ifdef ANDROID | ||
| 99 | ptr = memalign(alignment, size); | ||
| 100 | #else | ||
| 101 | if (posix_memalign(&ptr, alignment, size) != 0) | ||
| 102 | LOG_ERROR(Common_Memory, "Failed to allocate aligned memory"); | ||
| 103 | #endif | ||
| 104 | #endif | ||
| 105 | |||
| 106 | if (ptr == nullptr) | ||
| 107 | LOG_ERROR(Common_Memory, "Failed to allocate aligned memory"); | ||
| 108 | |||
| 109 | return ptr; | ||
| 110 | } | ||
| 111 | |||
| 112 | void FreeMemoryPages(void* ptr, std::size_t size) { | ||
| 113 | if (ptr) { | ||
| 114 | #ifdef _WIN32 | ||
| 115 | if (!VirtualFree(ptr, 0, MEM_RELEASE)) | ||
| 116 | LOG_ERROR(Common_Memory, "FreeMemoryPages failed!\n{}", GetLastErrorMsg()); | ||
| 117 | #else | ||
| 118 | munmap(ptr, size); | ||
| 119 | #endif | ||
| 120 | } | ||
| 121 | } | ||
| 122 | |||
| 123 | void FreeAlignedMemory(void* ptr) { | ||
| 124 | if (ptr) { | ||
| 125 | #ifdef _WIN32 | ||
| 126 | _aligned_free(ptr); | ||
| 127 | #else | ||
| 128 | free(ptr); | ||
| 129 | #endif | ||
| 130 | } | ||
| 131 | } | ||
| 132 | |||
| 133 | void WriteProtectMemory(void* ptr, std::size_t size, bool allowExecute) { | ||
| 134 | #ifdef _WIN32 | ||
| 135 | DWORD oldValue; | ||
| 136 | if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READ : PAGE_READONLY, &oldValue)) | ||
| 137 | LOG_ERROR(Common_Memory, "WriteProtectMemory failed!\n{}", GetLastErrorMsg()); | ||
| 138 | #else | ||
| 139 | mprotect(ptr, size, allowExecute ? (PROT_READ | PROT_EXEC) : PROT_READ); | ||
| 140 | #endif | ||
| 141 | } | ||
| 142 | |||
| 143 | void UnWriteProtectMemory(void* ptr, std::size_t size, bool allowExecute) { | ||
| 144 | #ifdef _WIN32 | ||
| 145 | DWORD oldValue; | ||
| 146 | if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE, | ||
| 147 | &oldValue)) | ||
| 148 | LOG_ERROR(Common_Memory, "UnWriteProtectMemory failed!\n{}", GetLastErrorMsg()); | ||
| 149 | #else | ||
| 150 | mprotect(ptr, size, | ||
| 151 | allowExecute ? (PROT_READ | PROT_WRITE | PROT_EXEC) : PROT_WRITE | PROT_READ); | ||
| 152 | #endif | ||
| 153 | } | ||
| 154 | |||
| 155 | std::string MemUsage() { | ||
| 156 | #ifdef _WIN32 | ||
| 157 | #pragma comment(lib, "psapi") | ||
| 158 | DWORD processID = GetCurrentProcessId(); | ||
| 159 | HANDLE hProcess; | ||
| 160 | PROCESS_MEMORY_COUNTERS pmc; | ||
| 161 | std::string Ret; | ||
| 162 | |||
| 163 | // Print information about the memory usage of the process. | ||
| 164 | |||
| 165 | hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processID); | ||
| 166 | if (nullptr == hProcess) | ||
| 167 | return "MemUsage Error"; | ||
| 168 | |||
| 169 | if (GetProcessMemoryInfo(hProcess, &pmc, sizeof(pmc))) | ||
| 170 | Ret = fmt::format("{} K", Common::ThousandSeparate(pmc.WorkingSetSize / 1024, 7)); | ||
| 171 | |||
| 172 | CloseHandle(hProcess); | ||
| 173 | return Ret; | ||
| 174 | #else | ||
| 175 | return ""; | ||
| 176 | #endif | ||
| 177 | } | ||
diff --git a/src/common/memory_util.h b/src/common/memory_util.h deleted file mode 100644 index aad071979..000000000 --- a/src/common/memory_util.h +++ /dev/null | |||
| @@ -1,21 +0,0 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project / 2014 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 <cstddef> | ||
| 8 | #include <string> | ||
| 9 | |||
| 10 | void* AllocateExecutableMemory(std::size_t size, bool low = true); | ||
| 11 | void* AllocateMemoryPages(std::size_t size); | ||
| 12 | void FreeMemoryPages(void* ptr, std::size_t size); | ||
| 13 | void* AllocateAlignedMemory(std::size_t size, std::size_t alignment); | ||
| 14 | void FreeAlignedMemory(void* ptr); | ||
| 15 | void WriteProtectMemory(void* ptr, std::size_t size, bool executable = false); | ||
| 16 | void UnWriteProtectMemory(void* ptr, std::size_t size, bool allowExecute = false); | ||
| 17 | std::string MemUsage(); | ||
| 18 | |||
| 19 | inline int GetPageSize() { | ||
| 20 | return 4096; | ||
| 21 | } | ||
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 4755ec822..64fdf38cd 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -156,6 +156,8 @@ add_library(core STATIC | |||
| 156 | hle/service/am/omm.h | 156 | hle/service/am/omm.h |
| 157 | hle/service/am/spsm.cpp | 157 | hle/service/am/spsm.cpp |
| 158 | hle/service/am/spsm.h | 158 | hle/service/am/spsm.h |
| 159 | hle/service/am/tcap.cpp | ||
| 160 | hle/service/am/tcap.h | ||
| 159 | hle/service/aoc/aoc_u.cpp | 161 | hle/service/aoc/aoc_u.cpp |
| 160 | hle/service/aoc/aoc_u.h | 162 | hle/service/aoc/aoc_u.h |
| 161 | hle/service/apm/apm.cpp | 163 | hle/service/apm/apm.cpp |
| @@ -280,6 +282,8 @@ add_library(core STATIC | |||
| 280 | hle/service/nifm/nifm.h | 282 | hle/service/nifm/nifm.h |
| 281 | hle/service/nim/nim.cpp | 283 | hle/service/nim/nim.cpp |
| 282 | hle/service/nim/nim.h | 284 | hle/service/nim/nim.h |
| 285 | hle/service/npns/npns.cpp | ||
| 286 | hle/service/npns/npns.h | ||
| 283 | hle/service/ns/ns.cpp | 287 | hle/service/ns/ns.cpp |
| 284 | hle/service/ns/ns.h | 288 | hle/service/ns/ns.h |
| 285 | hle/service/ns/pl_u.cpp | 289 | hle/service/ns/pl_u.cpp |
| @@ -327,6 +331,8 @@ add_library(core STATIC | |||
| 327 | hle/service/prepo/prepo.h | 331 | hle/service/prepo/prepo.h |
| 328 | hle/service/psc/psc.cpp | 332 | hle/service/psc/psc.cpp |
| 329 | hle/service/psc/psc.h | 333 | hle/service/psc/psc.h |
| 334 | hle/service/ptm/psm.cpp | ||
| 335 | hle/service/ptm/psm.h | ||
| 330 | hle/service/service.cpp | 336 | hle/service/service.cpp |
| 331 | hle/service/service.h | 337 | hle/service/service.h |
| 332 | hle/service/set/set.cpp | 338 | hle/service/set/set.cpp |
diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp index fd0786068..fefc3c747 100644 --- a/src/core/crypto/key_manager.cpp +++ b/src/core/crypto/key_manager.cpp | |||
| @@ -713,7 +713,6 @@ void KeyManager::DeriveBase() { | |||
| 713 | 713 | ||
| 714 | const auto sbk = GetKey(S128KeyType::SecureBoot); | 714 | const auto sbk = GetKey(S128KeyType::SecureBoot); |
| 715 | const auto tsec = GetKey(S128KeyType::TSEC); | 715 | const auto tsec = GetKey(S128KeyType::TSEC); |
| 716 | const auto master_source = GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::Master)); | ||
| 717 | 716 | ||
| 718 | for (size_t i = 0; i < revisions.size(); ++i) { | 717 | for (size_t i = 0; i < revisions.size(); ++i) { |
| 719 | if (!revisions[i]) | 718 | if (!revisions[i]) |
diff --git a/src/core/crypto/partition_data_manager.cpp b/src/core/crypto/partition_data_manager.cpp index 25cee1f3a..ed0775444 100644 --- a/src/core/crypto/partition_data_manager.cpp +++ b/src/core/crypto/partition_data_manager.cpp | |||
| @@ -516,7 +516,8 @@ void PartitionDataManager::DecryptPackage2(const std::array<Key128, 0x20>& packa | |||
| 516 | out.insert(out.end(), rodata.begin(), rodata.end()); | 516 | out.insert(out.end(), rodata.begin(), rodata.end()); |
| 517 | out.insert(out.end(), data.begin(), data.end()); | 517 | out.insert(out.end(), data.begin(), data.end()); |
| 518 | 518 | ||
| 519 | offset += sizeof(KIPHeader) + out.size(); | 519 | offset += sizeof(KIPHeader) + kip.sections[0].size_compressed + |
| 520 | kip.sections[1].size_compressed + kip.sections[2].size_compressed; | ||
| 520 | 521 | ||
| 521 | if (name == "FS") | 522 | if (name == "FS") |
| 522 | package2_fs[static_cast<size_t>(type)] = std::move(out); | 523 | package2_fs[static_cast<size_t>(type)] = std::move(out); |
diff --git a/src/core/file_sys/ips_layer.cpp b/src/core/file_sys/ips_layer.cpp index 554eae9bc..999939d5a 100644 --- a/src/core/file_sys/ips_layer.cpp +++ b/src/core/file_sys/ips_layer.cpp | |||
| @@ -99,7 +99,7 @@ VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) { | |||
| 99 | u16 rle_size{}; | 99 | u16 rle_size{}; |
| 100 | if (ips->ReadObject(&rle_size, offset) != sizeof(u16)) | 100 | if (ips->ReadObject(&rle_size, offset) != sizeof(u16)) |
| 101 | return nullptr; | 101 | return nullptr; |
| 102 | rle_size = Common::swap16(data_size); | 102 | rle_size = Common::swap16(rle_size); |
| 103 | offset += sizeof(u16); | 103 | offset += sizeof(u16); |
| 104 | 104 | ||
| 105 | const auto data = ips->ReadByte(offset++); | 105 | const auto data = ips->ReadByte(offset++); |
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index 0117cb0bf..1f4928562 100644 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp | |||
| @@ -168,7 +168,8 @@ bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const { | |||
| 168 | 168 | ||
| 169 | static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) { | 169 | static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) { |
| 170 | const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); | 170 | const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); |
| 171 | if (type != ContentRecordType::Program || load_dir == nullptr || load_dir->GetSize() <= 0) { | 171 | if ((type != ContentRecordType::Program && type != ContentRecordType::Data) || |
| 172 | load_dir == nullptr || load_dir->GetSize() <= 0) { | ||
| 172 | return; | 173 | return; |
| 173 | } | 174 | } |
| 174 | 175 | ||
| @@ -218,7 +219,7 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, Content | |||
| 218 | title_id, static_cast<u8>(type)) | 219 | title_id, static_cast<u8>(type)) |
| 219 | .c_str(); | 220 | .c_str(); |
| 220 | 221 | ||
| 221 | if (type == ContentRecordType::Program) | 222 | if (type == ContentRecordType::Program || type == ContentRecordType::Data) |
| 222 | LOG_INFO(Loader, log_string); | 223 | LOG_INFO(Loader, log_string); |
| 223 | else | 224 | else |
| 224 | LOG_DEBUG(Loader, log_string); | 225 | LOG_DEBUG(Loader, log_string); |
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp index 1febb398e..29b100414 100644 --- a/src/core/file_sys/registered_cache.cpp +++ b/src/core/file_sys/registered_cache.cpp | |||
| @@ -2,6 +2,7 @@ | |||
| 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 <algorithm> | ||
| 5 | #include <regex> | 6 | #include <regex> |
| 6 | #include <mbedtls/sha256.h> | 7 | #include <mbedtls/sha256.h> |
| 7 | #include "common/assert.h" | 8 | #include "common/assert.h" |
| @@ -30,6 +31,14 @@ bool operator<(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs) | |||
| 30 | return (lhs.title_id < rhs.title_id) || (lhs.title_id == rhs.title_id && lhs.type < rhs.type); | 31 | return (lhs.title_id < rhs.title_id) || (lhs.title_id == rhs.title_id && lhs.type < rhs.type); |
| 31 | } | 32 | } |
| 32 | 33 | ||
| 34 | bool operator==(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs) { | ||
| 35 | return std::tie(lhs.title_id, lhs.type) == std::tie(rhs.title_id, rhs.type); | ||
| 36 | } | ||
| 37 | |||
| 38 | bool operator!=(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs) { | ||
| 39 | return !operator==(lhs, rhs); | ||
| 40 | } | ||
| 41 | |||
| 33 | static bool FollowsTwoDigitDirFormat(std::string_view name) { | 42 | static bool FollowsTwoDigitDirFormat(std::string_view name) { |
| 34 | static const std::regex two_digit_regex("000000[0-9A-F]{2}", std::regex_constants::ECMAScript | | 43 | static const std::regex two_digit_regex("000000[0-9A-F]{2}", std::regex_constants::ECMAScript | |
| 35 | std::regex_constants::icase); | 44 | std::regex_constants::icase); |
| @@ -593,6 +602,9 @@ std::vector<RegisteredCacheEntry> RegisteredCacheUnion::ListEntries() const { | |||
| 593 | }, | 602 | }, |
| 594 | [](const CNMT& c, const ContentRecord& r) { return true; }); | 603 | [](const CNMT& c, const ContentRecord& r) { return true; }); |
| 595 | } | 604 | } |
| 605 | |||
| 606 | std::sort(out.begin(), out.end()); | ||
| 607 | out.erase(std::unique(out.begin(), out.end()), out.end()); | ||
| 596 | return out; | 608 | return out; |
| 597 | } | 609 | } |
| 598 | 610 | ||
| @@ -616,6 +628,9 @@ std::vector<RegisteredCacheEntry> RegisteredCacheUnion::ListEntriesFilter( | |||
| 616 | return true; | 628 | return true; |
| 617 | }); | 629 | }); |
| 618 | } | 630 | } |
| 631 | |||
| 632 | std::sort(out.begin(), out.end()); | ||
| 633 | out.erase(std::unique(out.begin(), out.end()), out.end()); | ||
| 619 | return out; | 634 | return out; |
| 620 | } | 635 | } |
| 621 | } // namespace FileSys | 636 | } // namespace FileSys |
diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h index 5ddacba47..5beceffb3 100644 --- a/src/core/file_sys/registered_cache.h +++ b/src/core/file_sys/registered_cache.h | |||
| @@ -50,6 +50,10 @@ constexpr u64 GetUpdateTitleID(u64 base_title_id) { | |||
| 50 | // boost flat_map requires operator< for O(log(n)) lookups. | 50 | // boost flat_map requires operator< for O(log(n)) lookups. |
| 51 | bool operator<(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs); | 51 | bool operator<(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs); |
| 52 | 52 | ||
| 53 | // std unique requires operator== to identify duplicates. | ||
| 54 | bool operator==(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs); | ||
| 55 | bool operator!=(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs); | ||
| 56 | |||
| 53 | /* | 57 | /* |
| 54 | * A class that catalogues NCAs in the registered directory structure. | 58 | * A class that catalogues NCAs in the registered directory structure. |
| 55 | * Nintendo's registered format follows this structure: | 59 | * Nintendo's registered format follows this structure: |
| @@ -60,8 +64,8 @@ bool operator<(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs) | |||
| 60 | * | 00 | 64 | * | 00 |
| 61 | * | 01 <- Actual content split along 4GB boundaries. (optional) | 65 | * | 01 <- Actual content split along 4GB boundaries. (optional) |
| 62 | * | 66 | * |
| 63 | * (This impl also supports substituting the nca dir for an nca file, as that's more convenient when | 67 | * (This impl also supports substituting the nca dir for an nca file, as that's more convenient |
| 64 | * 4GB splitting can be ignored.) | 68 | * when 4GB splitting can be ignored.) |
| 65 | */ | 69 | */ |
| 66 | class RegisteredCache { | 70 | class RegisteredCache { |
| 67 | friend class RegisteredCacheUnion; | 71 | friend class RegisteredCacheUnion; |
diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs.cpp index bfe50da73..3824c74e0 100644 --- a/src/core/file_sys/vfs.cpp +++ b/src/core/file_sys/vfs.cpp | |||
| @@ -472,10 +472,14 @@ bool VfsRawCopy(const VirtualFile& src, const VirtualFile& dest, std::size_t blo | |||
| 472 | std::vector<u8> temp(std::min(block_size, src->GetSize())); | 472 | std::vector<u8> temp(std::min(block_size, src->GetSize())); |
| 473 | for (std::size_t i = 0; i < src->GetSize(); i += block_size) { | 473 | for (std::size_t i = 0; i < src->GetSize(); i += block_size) { |
| 474 | const auto read = std::min(block_size, src->GetSize() - i); | 474 | const auto read = std::min(block_size, src->GetSize() - i); |
| 475 | const auto block = src->Read(temp.data(), read, i); | ||
| 476 | 475 | ||
| 477 | if (dest->Write(temp.data(), read, i) != read) | 476 | if (src->Read(temp.data(), read, i) != read) { |
| 478 | return false; | 477 | return false; |
| 478 | } | ||
| 479 | |||
| 480 | if (dest->Write(temp.data(), read, i) != read) { | ||
| 481 | return false; | ||
| 482 | } | ||
| 479 | } | 483 | } |
| 480 | 484 | ||
| 481 | return true; | 485 | return true; |
diff --git a/src/core/hle/ipc.h b/src/core/hle/ipc.h index 419f45896..ed84197b3 100644 --- a/src/core/hle/ipc.h +++ b/src/core/hle/ipc.h | |||
| @@ -14,11 +14,6 @@ namespace IPC { | |||
| 14 | /// Size of the command buffer area, in 32-bit words. | 14 | /// Size of the command buffer area, in 32-bit words. |
| 15 | constexpr std::size_t COMMAND_BUFFER_LENGTH = 0x100 / sizeof(u32); | 15 | constexpr std::size_t COMMAND_BUFFER_LENGTH = 0x100 / sizeof(u32); |
| 16 | 16 | ||
| 17 | // These errors are commonly returned by invalid IPC translations, so alias them here for | ||
| 18 | // convenience. | ||
| 19 | // TODO(yuriks): These will probably go away once translation is implemented inside the kernel. | ||
| 20 | constexpr auto ERR_INVALID_HANDLE = Kernel::ERR_INVALID_HANDLE_OS; | ||
| 21 | |||
| 22 | enum class ControlCommand : u32 { | 17 | enum class ControlCommand : u32 { |
| 23 | ConvertSessionToDomain = 0, | 18 | ConvertSessionToDomain = 0, |
| 24 | ConvertDomainToSession = 1, | 19 | ConvertDomainToSession = 1, |
diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h index 885259618..ee698c8a7 100644 --- a/src/core/hle/kernel/errors.h +++ b/src/core/hle/kernel/errors.h | |||
| @@ -10,11 +10,6 @@ namespace Kernel { | |||
| 10 | 10 | ||
| 11 | namespace ErrCodes { | 11 | namespace ErrCodes { |
| 12 | enum { | 12 | enum { |
| 13 | // TODO(Subv): Remove these 3DS OS error codes. | ||
| 14 | SessionClosedByRemote = 26, | ||
| 15 | NoPendingSessions = 35, | ||
| 16 | InvalidBufferDescriptor = 48, | ||
| 17 | |||
| 18 | // Confirmed Switch OS error codes | 13 | // Confirmed Switch OS error codes |
| 19 | MaxConnectionsReached = 7, | 14 | MaxConnectionsReached = 7, |
| 20 | InvalidSize = 101, | 15 | InvalidSize = 101, |
| @@ -26,6 +21,7 @@ enum { | |||
| 26 | InvalidThreadPriority = 112, | 21 | InvalidThreadPriority = 112, |
| 27 | InvalidProcessorId = 113, | 22 | InvalidProcessorId = 113, |
| 28 | InvalidHandle = 114, | 23 | InvalidHandle = 114, |
| 24 | InvalidPointer = 115, | ||
| 29 | InvalidCombination = 116, | 25 | InvalidCombination = 116, |
| 30 | Timeout = 117, | 26 | Timeout = 117, |
| 31 | SynchronizationCanceled = 118, | 27 | SynchronizationCanceled = 118, |
| @@ -33,6 +29,7 @@ enum { | |||
| 33 | InvalidEnumValue = 120, | 29 | InvalidEnumValue = 120, |
| 34 | NoSuchEntry = 121, | 30 | NoSuchEntry = 121, |
| 35 | AlreadyRegistered = 122, | 31 | AlreadyRegistered = 122, |
| 32 | SessionClosed = 123, | ||
| 36 | InvalidState = 125, | 33 | InvalidState = 125, |
| 37 | ResourceLimitExceeded = 132, | 34 | ResourceLimitExceeded = 132, |
| 38 | }; | 35 | }; |
| @@ -41,18 +38,14 @@ enum { | |||
| 41 | // WARNING: The kernel is quite inconsistent in it's usage of errors code. Make sure to always | 38 | // WARNING: The kernel is quite inconsistent in it's usage of errors code. Make sure to always |
| 42 | // double check that the code matches before re-using the constant. | 39 | // double check that the code matches before re-using the constant. |
| 43 | 40 | ||
| 44 | // TODO(bunnei): Replace -1 with correct errors for Switch OS | ||
| 45 | constexpr ResultCode ERR_HANDLE_TABLE_FULL(ErrorModule::Kernel, ErrCodes::HandleTableFull); | 41 | constexpr ResultCode ERR_HANDLE_TABLE_FULL(ErrorModule::Kernel, ErrCodes::HandleTableFull); |
| 46 | constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE(-1); | 42 | constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE(ErrorModule::Kernel, ErrCodes::SessionClosed); |
| 47 | constexpr ResultCode ERR_PORT_NAME_TOO_LONG(ErrorModule::Kernel, ErrCodes::TooLarge); | 43 | constexpr ResultCode ERR_PORT_NAME_TOO_LONG(ErrorModule::Kernel, ErrCodes::TooLarge); |
| 48 | constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED(ErrorModule::Kernel, | 44 | constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED(ErrorModule::Kernel, |
| 49 | ErrCodes::MaxConnectionsReached); | 45 | ErrCodes::MaxConnectionsReached); |
| 50 | constexpr ResultCode ERR_INVALID_ENUM_VALUE(ErrorModule::Kernel, ErrCodes::InvalidEnumValue); | 46 | constexpr ResultCode ERR_INVALID_ENUM_VALUE(ErrorModule::Kernel, ErrCodes::InvalidEnumValue); |
| 51 | constexpr ResultCode ERR_INVALID_ENUM_VALUE_FND(-1); | ||
| 52 | constexpr ResultCode ERR_INVALID_COMBINATION(-1); | ||
| 53 | constexpr ResultCode ERR_INVALID_COMBINATION_KERNEL(ErrorModule::Kernel, | 47 | constexpr ResultCode ERR_INVALID_COMBINATION_KERNEL(ErrorModule::Kernel, |
| 54 | ErrCodes::InvalidCombination); | 48 | ErrCodes::InvalidCombination); |
| 55 | constexpr ResultCode ERR_OUT_OF_MEMORY(-1); | ||
| 56 | constexpr ResultCode ERR_INVALID_ADDRESS(ErrorModule::Kernel, ErrCodes::InvalidAddress); | 49 | constexpr ResultCode ERR_INVALID_ADDRESS(ErrorModule::Kernel, ErrCodes::InvalidAddress); |
| 57 | constexpr ResultCode ERR_INVALID_ADDRESS_STATE(ErrorModule::Kernel, ErrCodes::InvalidMemoryState); | 50 | constexpr ResultCode ERR_INVALID_ADDRESS_STATE(ErrorModule::Kernel, ErrCodes::InvalidMemoryState); |
| 58 | constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS(ErrorModule::Kernel, | 51 | constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS(ErrorModule::Kernel, |
| @@ -65,14 +58,8 @@ constexpr ResultCode ERR_ALREADY_REGISTERED(ErrorModule::Kernel, ErrCodes::Alrea | |||
| 65 | constexpr ResultCode ERR_INVALID_STATE(ErrorModule::Kernel, ErrCodes::InvalidState); | 58 | constexpr ResultCode ERR_INVALID_STATE(ErrorModule::Kernel, ErrCodes::InvalidState); |
| 66 | constexpr ResultCode ERR_INVALID_THREAD_PRIORITY(ErrorModule::Kernel, | 59 | constexpr ResultCode ERR_INVALID_THREAD_PRIORITY(ErrorModule::Kernel, |
| 67 | ErrCodes::InvalidThreadPriority); | 60 | ErrCodes::InvalidThreadPriority); |
| 68 | constexpr ResultCode ERR_INVALID_POINTER(-1); | 61 | constexpr ResultCode ERR_INVALID_POINTER(ErrorModule::Kernel, ErrCodes::InvalidPointer); |
| 69 | constexpr ResultCode ERR_INVALID_OBJECT_ADDR(-1); | ||
| 70 | constexpr ResultCode ERR_NOT_AUTHORIZED(-1); | ||
| 71 | /// Alternate code returned instead of ERR_INVALID_HANDLE in some code paths. | ||
| 72 | constexpr ResultCode ERR_INVALID_HANDLE_OS(-1); | ||
| 73 | constexpr ResultCode ERR_NOT_FOUND(ErrorModule::Kernel, ErrCodes::NoSuchEntry); | 62 | constexpr ResultCode ERR_NOT_FOUND(ErrorModule::Kernel, ErrCodes::NoSuchEntry); |
| 74 | constexpr ResultCode RESULT_TIMEOUT(ErrorModule::Kernel, ErrCodes::Timeout); | 63 | constexpr ResultCode RESULT_TIMEOUT(ErrorModule::Kernel, ErrCodes::Timeout); |
| 75 | /// Returned when Accept() is called on a port with no sessions to be accepted. | ||
| 76 | constexpr ResultCode ERR_NO_PENDING_SESSIONS(-1); | ||
| 77 | 64 | ||
| 78 | } // namespace Kernel | 65 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index edad5f1b1..68d5376cb 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp | |||
| @@ -77,7 +77,8 @@ HLERequestContext::HLERequestContext(SharedPtr<Kernel::ServerSession> server_ses | |||
| 77 | 77 | ||
| 78 | HLERequestContext::~HLERequestContext() = default; | 78 | HLERequestContext::~HLERequestContext() = default; |
| 79 | 79 | ||
| 80 | void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) { | 80 | void HLERequestContext::ParseCommandBuffer(const HandleTable& handle_table, u32_le* src_cmdbuf, |
| 81 | bool incoming) { | ||
| 81 | IPC::RequestParser rp(src_cmdbuf); | 82 | IPC::RequestParser rp(src_cmdbuf); |
| 82 | command_header = std::make_shared<IPC::CommandHeader>(rp.PopRaw<IPC::CommandHeader>()); | 83 | command_header = std::make_shared<IPC::CommandHeader>(rp.PopRaw<IPC::CommandHeader>()); |
| 83 | 84 | ||
| @@ -94,8 +95,6 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) { | |||
| 94 | rp.Skip(2, false); | 95 | rp.Skip(2, false); |
| 95 | } | 96 | } |
| 96 | if (incoming) { | 97 | if (incoming) { |
| 97 | auto& handle_table = Core::System::GetInstance().Kernel().HandleTable(); | ||
| 98 | |||
| 99 | // Populate the object lists with the data in the IPC request. | 98 | // Populate the object lists with the data in the IPC request. |
| 100 | for (u32 handle = 0; handle < handle_descriptor_header->num_handles_to_copy; ++handle) { | 99 | for (u32 handle = 0; handle < handle_descriptor_header->num_handles_to_copy; ++handle) { |
| 101 | copy_objects.push_back(handle_table.GetGeneric(rp.Pop<Handle>())); | 100 | copy_objects.push_back(handle_table.GetGeneric(rp.Pop<Handle>())); |
| @@ -189,10 +188,9 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) { | |||
| 189 | rp.Skip(1, false); // The command is actually an u64, but we don't use the high part. | 188 | rp.Skip(1, false); // The command is actually an u64, but we don't use the high part. |
| 190 | } | 189 | } |
| 191 | 190 | ||
| 192 | ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(u32_le* src_cmdbuf, | 191 | ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const HandleTable& handle_table, |
| 193 | Process& src_process, | 192 | u32_le* src_cmdbuf) { |
| 194 | HandleTable& src_table) { | 193 | ParseCommandBuffer(handle_table, src_cmdbuf, true); |
| 195 | ParseCommandBuffer(src_cmdbuf, true); | ||
| 196 | if (command_header->type == IPC::CommandType::Close) { | 194 | if (command_header->type == IPC::CommandType::Close) { |
| 197 | // Close does not populate the rest of the IPC header | 195 | // Close does not populate the rest of the IPC header |
| 198 | return RESULT_SUCCESS; | 196 | return RESULT_SUCCESS; |
| @@ -207,14 +205,17 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(u32_le* src_cmdb | |||
| 207 | return RESULT_SUCCESS; | 205 | return RESULT_SUCCESS; |
| 208 | } | 206 | } |
| 209 | 207 | ||
| 210 | ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(const Thread& thread) { | 208 | ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) { |
| 209 | auto& owner_process = *thread.GetOwnerProcess(); | ||
| 210 | auto& handle_table = owner_process.GetHandleTable(); | ||
| 211 | |||
| 211 | std::array<u32, IPC::COMMAND_BUFFER_LENGTH> dst_cmdbuf; | 212 | std::array<u32, IPC::COMMAND_BUFFER_LENGTH> dst_cmdbuf; |
| 212 | Memory::ReadBlock(*thread.GetOwnerProcess(), thread.GetTLSAddress(), dst_cmdbuf.data(), | 213 | Memory::ReadBlock(owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(), |
| 213 | dst_cmdbuf.size() * sizeof(u32)); | 214 | dst_cmdbuf.size() * sizeof(u32)); |
| 214 | 215 | ||
| 215 | // The header was already built in the internal command buffer. Attempt to parse it to verify | 216 | // The header was already built in the internal command buffer. Attempt to parse it to verify |
| 216 | // the integrity and then copy it over to the target command buffer. | 217 | // the integrity and then copy it over to the target command buffer. |
| 217 | ParseCommandBuffer(cmd_buf.data(), false); | 218 | ParseCommandBuffer(handle_table, cmd_buf.data(), false); |
| 218 | 219 | ||
| 219 | // The data_size already includes the payload header, the padding and the domain header. | 220 | // The data_size already includes the payload header, the padding and the domain header. |
| 220 | std::size_t size = data_payload_offset + command_header->data_size - | 221 | std::size_t size = data_payload_offset + command_header->data_size - |
| @@ -236,8 +237,6 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(const Thread& thread) | |||
| 236 | ASSERT(copy_objects.size() == handle_descriptor_header->num_handles_to_copy); | 237 | ASSERT(copy_objects.size() == handle_descriptor_header->num_handles_to_copy); |
| 237 | ASSERT(move_objects.size() == handle_descriptor_header->num_handles_to_move); | 238 | ASSERT(move_objects.size() == handle_descriptor_header->num_handles_to_move); |
| 238 | 239 | ||
| 239 | auto& handle_table = Core::System::GetInstance().Kernel().HandleTable(); | ||
| 240 | |||
| 241 | // We don't make a distinction between copy and move handles when translating since HLE | 240 | // We don't make a distinction between copy and move handles when translating since HLE |
| 242 | // services don't deal with handles directly. However, the guest applications might check | 241 | // services don't deal with handles directly. However, the guest applications might check |
| 243 | // for specific values in each of these descriptors. | 242 | // for specific values in each of these descriptors. |
| @@ -268,7 +267,7 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(const Thread& thread) | |||
| 268 | } | 267 | } |
| 269 | 268 | ||
| 270 | // Copy the translated command buffer back into the thread's command buffer area. | 269 | // Copy the translated command buffer back into the thread's command buffer area. |
| 271 | Memory::WriteBlock(*thread.GetOwnerProcess(), thread.GetTLSAddress(), dst_cmdbuf.data(), | 270 | Memory::WriteBlock(owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(), |
| 272 | dst_cmdbuf.size() * sizeof(u32)); | 271 | dst_cmdbuf.size() * sizeof(u32)); |
| 273 | 272 | ||
| 274 | return RESULT_SUCCESS; | 273 | return RESULT_SUCCESS; |
diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index 894479ee0..f01491daa 100644 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h | |||
| @@ -24,10 +24,10 @@ class ServiceFrameworkBase; | |||
| 24 | namespace Kernel { | 24 | namespace Kernel { |
| 25 | 25 | ||
| 26 | class Domain; | 26 | class Domain; |
| 27 | class Event; | ||
| 27 | class HandleTable; | 28 | class HandleTable; |
| 28 | class HLERequestContext; | 29 | class HLERequestContext; |
| 29 | class Process; | 30 | class Process; |
| 30 | class Event; | ||
| 31 | 31 | ||
| 32 | /** | 32 | /** |
| 33 | * Interface implemented by HLE Session handlers. | 33 | * Interface implemented by HLE Session handlers. |
| @@ -126,13 +126,12 @@ public: | |||
| 126 | u64 timeout, WakeupCallback&& callback, | 126 | u64 timeout, WakeupCallback&& callback, |
| 127 | Kernel::SharedPtr<Kernel::Event> event = nullptr); | 127 | Kernel::SharedPtr<Kernel::Event> event = nullptr); |
| 128 | 128 | ||
| 129 | void ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming); | ||
| 130 | |||
| 131 | /// Populates this context with data from the requesting process/thread. | 129 | /// Populates this context with data from the requesting process/thread. |
| 132 | ResultCode PopulateFromIncomingCommandBuffer(u32_le* src_cmdbuf, Process& src_process, | 130 | ResultCode PopulateFromIncomingCommandBuffer(const HandleTable& handle_table, |
| 133 | HandleTable& src_table); | 131 | u32_le* src_cmdbuf); |
| 132 | |||
| 134 | /// Writes data from this context back to the requesting process/thread. | 133 | /// Writes data from this context back to the requesting process/thread. |
| 135 | ResultCode WriteToOutgoingCommandBuffer(const Thread& thread); | 134 | ResultCode WriteToOutgoingCommandBuffer(Thread& thread); |
| 136 | 135 | ||
| 137 | u32_le GetCommand() const { | 136 | u32_le GetCommand() const { |
| 138 | return command; | 137 | return command; |
| @@ -255,6 +254,8 @@ public: | |||
| 255 | std::string Description() const; | 254 | std::string Description() const; |
| 256 | 255 | ||
| 257 | private: | 256 | private: |
| 257 | void ParseCommandBuffer(const HandleTable& handle_table, u32_le* src_cmdbuf, bool incoming); | ||
| 258 | |||
| 258 | std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf; | 259 | std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf; |
| 259 | SharedPtr<Kernel::ServerSession> server_session; | 260 | SharedPtr<Kernel::ServerSession> server_session; |
| 260 | // TODO(yuriks): Check common usage of this and optimize size accordingly | 261 | // TODO(yuriks): Check common usage of this and optimize size accordingly |
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index bd680adfe..4b6b32dd5 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp | |||
| @@ -118,7 +118,6 @@ struct KernelCore::Impl { | |||
| 118 | process_list.clear(); | 118 | process_list.clear(); |
| 119 | current_process = nullptr; | 119 | current_process = nullptr; |
| 120 | 120 | ||
| 121 | handle_table.Clear(); | ||
| 122 | resource_limits.fill(nullptr); | 121 | resource_limits.fill(nullptr); |
| 123 | 122 | ||
| 124 | thread_wakeup_callback_handle_table.Clear(); | 123 | thread_wakeup_callback_handle_table.Clear(); |
| @@ -209,7 +208,6 @@ struct KernelCore::Impl { | |||
| 209 | std::vector<SharedPtr<Process>> process_list; | 208 | std::vector<SharedPtr<Process>> process_list; |
| 210 | Process* current_process = nullptr; | 209 | Process* current_process = nullptr; |
| 211 | 210 | ||
| 212 | Kernel::HandleTable handle_table; | ||
| 213 | std::array<SharedPtr<ResourceLimit>, 4> resource_limits; | 211 | std::array<SharedPtr<ResourceLimit>, 4> resource_limits; |
| 214 | 212 | ||
| 215 | /// The event type of the generic timer callback event | 213 | /// The event type of the generic timer callback event |
| @@ -241,14 +239,6 @@ void KernelCore::Shutdown() { | |||
| 241 | impl->Shutdown(); | 239 | impl->Shutdown(); |
| 242 | } | 240 | } |
| 243 | 241 | ||
| 244 | Kernel::HandleTable& KernelCore::HandleTable() { | ||
| 245 | return impl->handle_table; | ||
| 246 | } | ||
| 247 | |||
| 248 | const Kernel::HandleTable& KernelCore::HandleTable() const { | ||
| 249 | return impl->handle_table; | ||
| 250 | } | ||
| 251 | |||
| 252 | SharedPtr<ResourceLimit> KernelCore::ResourceLimitForCategory( | 242 | SharedPtr<ResourceLimit> KernelCore::ResourceLimitForCategory( |
| 253 | ResourceLimitCategory category) const { | 243 | ResourceLimitCategory category) const { |
| 254 | return impl->resource_limits.at(static_cast<std::size_t>(category)); | 244 | return impl->resource_limits.at(static_cast<std::size_t>(category)); |
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 41554821f..7f822d524 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h | |||
| @@ -47,12 +47,6 @@ public: | |||
| 47 | /// Clears all resources in use by the kernel instance. | 47 | /// Clears all resources in use by the kernel instance. |
| 48 | void Shutdown(); | 48 | void Shutdown(); |
| 49 | 49 | ||
| 50 | /// Provides a reference to the handle table. | ||
| 51 | Kernel::HandleTable& HandleTable(); | ||
| 52 | |||
| 53 | /// Provides a const reference to the handle table. | ||
| 54 | const Kernel::HandleTable& HandleTable() const; | ||
| 55 | |||
| 56 | /// Retrieves a shared pointer to a ResourceLimit identified by the given category. | 50 | /// Retrieves a shared pointer to a ResourceLimit identified by the given category. |
| 57 | SharedPtr<ResourceLimit> ResourceLimitForCategory(ResourceLimitCategory category) const; | 51 | SharedPtr<ResourceLimit> ResourceLimitForCategory(ResourceLimitCategory category) const; |
| 58 | 52 | ||
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index 073dd5a7d..420218d59 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp | |||
| @@ -232,6 +232,12 @@ void Process::LoadModule(CodeSet module_, VAddr base_addr) { | |||
| 232 | MapSegment(module_.CodeSegment(), VMAPermission::ReadExecute, MemoryState::CodeStatic); | 232 | MapSegment(module_.CodeSegment(), VMAPermission::ReadExecute, MemoryState::CodeStatic); |
| 233 | MapSegment(module_.RODataSegment(), VMAPermission::Read, MemoryState::CodeMutable); | 233 | MapSegment(module_.RODataSegment(), VMAPermission::Read, MemoryState::CodeMutable); |
| 234 | MapSegment(module_.DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeMutable); | 234 | MapSegment(module_.DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeMutable); |
| 235 | |||
| 236 | // Clear instruction cache in CPU JIT | ||
| 237 | Core::System::GetInstance().ArmInterface(0).ClearInstructionCache(); | ||
| 238 | Core::System::GetInstance().ArmInterface(1).ClearInstructionCache(); | ||
| 239 | Core::System::GetInstance().ArmInterface(2).ClearInstructionCache(); | ||
| 240 | Core::System::GetInstance().ArmInterface(3).ClearInstructionCache(); | ||
| 235 | } | 241 | } |
| 236 | 242 | ||
| 237 | ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission perms) { | 243 | ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission perms) { |
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h index f2816943a..148478488 100644 --- a/src/core/hle/kernel/process.h +++ b/src/core/hle/kernel/process.h | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | #include <boost/container/static_vector.hpp> | 13 | #include <boost/container/static_vector.hpp> |
| 14 | #include "common/bit_field.h" | 14 | #include "common/bit_field.h" |
| 15 | #include "common/common_types.h" | 15 | #include "common/common_types.h" |
| 16 | #include "core/hle/kernel/handle_table.h" | ||
| 16 | #include "core/hle/kernel/object.h" | 17 | #include "core/hle/kernel/object.h" |
| 17 | #include "core/hle/kernel/thread.h" | 18 | #include "core/hle/kernel/thread.h" |
| 18 | #include "core/hle/kernel/vm_manager.h" | 19 | #include "core/hle/kernel/vm_manager.h" |
| @@ -142,6 +143,16 @@ public: | |||
| 142 | return vm_manager; | 143 | return vm_manager; |
| 143 | } | 144 | } |
| 144 | 145 | ||
| 146 | /// Gets a reference to the process' handle table. | ||
| 147 | HandleTable& GetHandleTable() { | ||
| 148 | return handle_table; | ||
| 149 | } | ||
| 150 | |||
| 151 | /// Gets a const reference to the process' handle table. | ||
| 152 | const HandleTable& GetHandleTable() const { | ||
| 153 | return handle_table; | ||
| 154 | } | ||
| 155 | |||
| 145 | /// Gets the current status of the process | 156 | /// Gets the current status of the process |
| 146 | ProcessStatus GetStatus() const { | 157 | ProcessStatus GetStatus() const { |
| 147 | return status; | 158 | return status; |
| @@ -294,6 +305,9 @@ private: | |||
| 294 | /// specified by metadata provided to the process during loading. | 305 | /// specified by metadata provided to the process during loading. |
| 295 | bool is_64bit_process = true; | 306 | bool is_64bit_process = true; |
| 296 | 307 | ||
| 308 | /// Per-process handle table for storing created object handles in. | ||
| 309 | HandleTable handle_table; | ||
| 310 | |||
| 297 | std::string name; | 311 | std::string name; |
| 298 | }; | 312 | }; |
| 299 | 313 | ||
diff --git a/src/core/hle/kernel/server_port.cpp b/src/core/hle/kernel/server_port.cpp index 3792e3e18..d6ceeb2da 100644 --- a/src/core/hle/kernel/server_port.cpp +++ b/src/core/hle/kernel/server_port.cpp | |||
| @@ -18,7 +18,7 @@ ServerPort::~ServerPort() = default; | |||
| 18 | 18 | ||
| 19 | ResultVal<SharedPtr<ServerSession>> ServerPort::Accept() { | 19 | ResultVal<SharedPtr<ServerSession>> ServerPort::Accept() { |
| 20 | if (pending_sessions.empty()) { | 20 | if (pending_sessions.empty()) { |
| 21 | return ERR_NO_PENDING_SESSIONS; | 21 | return ERR_NOT_FOUND; |
| 22 | } | 22 | } |
| 23 | 23 | ||
| 24 | auto session = std::move(pending_sessions.back()); | 24 | auto session = std::move(pending_sessions.back()); |
| @@ -28,7 +28,7 @@ ResultVal<SharedPtr<ServerSession>> ServerPort::Accept() { | |||
| 28 | 28 | ||
| 29 | bool ServerPort::ShouldWait(Thread* thread) const { | 29 | bool ServerPort::ShouldWait(Thread* thread) const { |
| 30 | // If there are no pending sessions, we wait until a new one is added. | 30 | // If there are no pending sessions, we wait until a new one is added. |
| 31 | return pending_sessions.size() == 0; | 31 | return pending_sessions.empty(); |
| 32 | } | 32 | } |
| 33 | 33 | ||
| 34 | void ServerPort::Acquire(Thread* thread) { | 34 | void ServerPort::Acquire(Thread* thread) { |
diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp index 1ece691c7..5fc320403 100644 --- a/src/core/hle/kernel/server_session.cpp +++ b/src/core/hle/kernel/server_session.cpp | |||
| @@ -107,8 +107,7 @@ ResultCode ServerSession::HandleSyncRequest(SharedPtr<Thread> thread) { | |||
| 107 | // similar. | 107 | // similar. |
| 108 | Kernel::HLERequestContext context(this); | 108 | Kernel::HLERequestContext context(this); |
| 109 | u32* cmd_buf = (u32*)Memory::GetPointer(thread->GetTLSAddress()); | 109 | u32* cmd_buf = (u32*)Memory::GetPointer(thread->GetTLSAddress()); |
| 110 | context.PopulateFromIncomingCommandBuffer(cmd_buf, *Core::CurrentProcess(), | 110 | context.PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf); |
| 111 | kernel.HandleTable()); | ||
| 112 | 111 | ||
| 113 | ResultCode result = RESULT_SUCCESS; | 112 | ResultCode result = RESULT_SUCCESS; |
| 114 | // If the session has been converted to a domain, handle the domain request | 113 | // If the session has been converted to a domain, handle the domain request |
diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp index d061e6155..a016a86b6 100644 --- a/src/core/hle/kernel/shared_memory.cpp +++ b/src/core/hle/kernel/shared_memory.cpp | |||
| @@ -80,20 +80,19 @@ SharedPtr<SharedMemory> SharedMemory::CreateForApplet( | |||
| 80 | 80 | ||
| 81 | ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermission permissions, | 81 | ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermission permissions, |
| 82 | MemoryPermission other_permissions) { | 82 | MemoryPermission other_permissions) { |
| 83 | 83 | const MemoryPermission own_other_permissions = | |
| 84 | MemoryPermission own_other_permissions = | ||
| 85 | target_process == owner_process ? this->permissions : this->other_permissions; | 84 | target_process == owner_process ? this->permissions : this->other_permissions; |
| 86 | 85 | ||
| 87 | // Automatically allocated memory blocks can only be mapped with other_permissions = DontCare | 86 | // Automatically allocated memory blocks can only be mapped with other_permissions = DontCare |
| 88 | if (base_address == 0 && other_permissions != MemoryPermission::DontCare) { | 87 | if (base_address == 0 && other_permissions != MemoryPermission::DontCare) { |
| 89 | return ERR_INVALID_COMBINATION; | 88 | return ERR_INVALID_MEMORY_PERMISSIONS; |
| 90 | } | 89 | } |
| 91 | 90 | ||
| 92 | // Error out if the requested permissions don't match what the creator process allows. | 91 | // Error out if the requested permissions don't match what the creator process allows. |
| 93 | if (static_cast<u32>(permissions) & ~static_cast<u32>(own_other_permissions)) { | 92 | if (static_cast<u32>(permissions) & ~static_cast<u32>(own_other_permissions)) { |
| 94 | LOG_ERROR(Kernel, "cannot map id={}, address=0x{:X} name={}, permissions don't match", | 93 | LOG_ERROR(Kernel, "cannot map id={}, address=0x{:X} name={}, permissions don't match", |
| 95 | GetObjectId(), address, name); | 94 | GetObjectId(), address, name); |
| 96 | return ERR_INVALID_COMBINATION; | 95 | return ERR_INVALID_MEMORY_PERMISSIONS; |
| 97 | } | 96 | } |
| 98 | 97 | ||
| 99 | // Error out if the provided permissions are not compatible with what the creator process needs. | 98 | // Error out if the provided permissions are not compatible with what the creator process needs. |
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index d3c9d50b5..a5302d924 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp | |||
| @@ -189,14 +189,15 @@ static ResultCode ConnectToNamedPort(Handle* out_handle, VAddr port_name_address | |||
| 189 | CASCADE_RESULT(client_session, client_port->Connect()); | 189 | CASCADE_RESULT(client_session, client_port->Connect()); |
| 190 | 190 | ||
| 191 | // Return the client session | 191 | // Return the client session |
| 192 | CASCADE_RESULT(*out_handle, kernel.HandleTable().Create(client_session)); | 192 | auto& handle_table = Core::CurrentProcess()->GetHandleTable(); |
| 193 | CASCADE_RESULT(*out_handle, handle_table.Create(client_session)); | ||
| 193 | return RESULT_SUCCESS; | 194 | return RESULT_SUCCESS; |
| 194 | } | 195 | } |
| 195 | 196 | ||
| 196 | /// Makes a blocking IPC call to an OS service. | 197 | /// Makes a blocking IPC call to an OS service. |
| 197 | static ResultCode SendSyncRequest(Handle handle) { | 198 | static ResultCode SendSyncRequest(Handle handle) { |
| 198 | auto& kernel = Core::System::GetInstance().Kernel(); | 199 | const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); |
| 199 | SharedPtr<ClientSession> session = kernel.HandleTable().Get<ClientSession>(handle); | 200 | SharedPtr<ClientSession> session = handle_table.Get<ClientSession>(handle); |
| 200 | if (!session) { | 201 | if (!session) { |
| 201 | LOG_ERROR(Kernel_SVC, "called with invalid handle=0x{:08X}", handle); | 202 | LOG_ERROR(Kernel_SVC, "called with invalid handle=0x{:08X}", handle); |
| 202 | return ERR_INVALID_HANDLE; | 203 | return ERR_INVALID_HANDLE; |
| @@ -215,8 +216,8 @@ static ResultCode SendSyncRequest(Handle handle) { | |||
| 215 | static ResultCode GetThreadId(u32* thread_id, Handle thread_handle) { | 216 | static ResultCode GetThreadId(u32* thread_id, Handle thread_handle) { |
| 216 | LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle); | 217 | LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle); |
| 217 | 218 | ||
| 218 | auto& kernel = Core::System::GetInstance().Kernel(); | 219 | const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); |
| 219 | const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle); | 220 | const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); |
| 220 | if (!thread) { | 221 | if (!thread) { |
| 221 | return ERR_INVALID_HANDLE; | 222 | return ERR_INVALID_HANDLE; |
| 222 | } | 223 | } |
| @@ -229,8 +230,8 @@ static ResultCode GetThreadId(u32* thread_id, Handle thread_handle) { | |||
| 229 | static ResultCode GetProcessId(u32* process_id, Handle process_handle) { | 230 | static ResultCode GetProcessId(u32* process_id, Handle process_handle) { |
| 230 | LOG_TRACE(Kernel_SVC, "called process=0x{:08X}", process_handle); | 231 | LOG_TRACE(Kernel_SVC, "called process=0x{:08X}", process_handle); |
| 231 | 232 | ||
| 232 | auto& kernel = Core::System::GetInstance().Kernel(); | 233 | const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); |
| 233 | const SharedPtr<Process> process = kernel.HandleTable().Get<Process>(process_handle); | 234 | const SharedPtr<Process> process = handle_table.Get<Process>(process_handle); |
| 234 | if (!process) { | 235 | if (!process) { |
| 235 | return ERR_INVALID_HANDLE; | 236 | return ERR_INVALID_HANDLE; |
| 236 | } | 237 | } |
| @@ -273,11 +274,11 @@ static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64 | |||
| 273 | 274 | ||
| 274 | using ObjectPtr = Thread::ThreadWaitObjects::value_type; | 275 | using ObjectPtr = Thread::ThreadWaitObjects::value_type; |
| 275 | Thread::ThreadWaitObjects objects(handle_count); | 276 | Thread::ThreadWaitObjects objects(handle_count); |
| 276 | auto& kernel = Core::System::GetInstance().Kernel(); | 277 | const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); |
| 277 | 278 | ||
| 278 | for (u64 i = 0; i < handle_count; ++i) { | 279 | for (u64 i = 0; i < handle_count; ++i) { |
| 279 | const Handle handle = Memory::Read32(handles_address + i * sizeof(Handle)); | 280 | const Handle handle = Memory::Read32(handles_address + i * sizeof(Handle)); |
| 280 | const auto object = kernel.HandleTable().Get<WaitObject>(handle); | 281 | const auto object = handle_table.Get<WaitObject>(handle); |
| 281 | 282 | ||
| 282 | if (object == nullptr) { | 283 | if (object == nullptr) { |
| 283 | return ERR_INVALID_HANDLE; | 284 | return ERR_INVALID_HANDLE; |
| @@ -325,8 +326,8 @@ static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64 | |||
| 325 | static ResultCode CancelSynchronization(Handle thread_handle) { | 326 | static ResultCode CancelSynchronization(Handle thread_handle) { |
| 326 | LOG_TRACE(Kernel_SVC, "called thread=0x{:X}", thread_handle); | 327 | LOG_TRACE(Kernel_SVC, "called thread=0x{:X}", thread_handle); |
| 327 | 328 | ||
| 328 | auto& kernel = Core::System::GetInstance().Kernel(); | 329 | const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); |
| 329 | const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle); | 330 | const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); |
| 330 | if (!thread) { | 331 | if (!thread) { |
| 331 | return ERR_INVALID_HANDLE; | 332 | return ERR_INVALID_HANDLE; |
| 332 | } | 333 | } |
| @@ -354,7 +355,7 @@ static ResultCode ArbitrateLock(Handle holding_thread_handle, VAddr mutex_addr, | |||
| 354 | return ERR_INVALID_ADDRESS; | 355 | return ERR_INVALID_ADDRESS; |
| 355 | } | 356 | } |
| 356 | 357 | ||
| 357 | auto& handle_table = Core::System::GetInstance().Kernel().HandleTable(); | 358 | auto& handle_table = Core::CurrentProcess()->GetHandleTable(); |
| 358 | return Mutex::TryAcquire(handle_table, mutex_addr, holding_thread_handle, | 359 | return Mutex::TryAcquire(handle_table, mutex_addr, holding_thread_handle, |
| 359 | requesting_thread_handle); | 360 | requesting_thread_handle); |
| 360 | } | 361 | } |
| @@ -374,9 +375,19 @@ static ResultCode ArbitrateUnlock(VAddr mutex_addr) { | |||
| 374 | return Mutex::Release(mutex_addr); | 375 | return Mutex::Release(mutex_addr); |
| 375 | } | 376 | } |
| 376 | 377 | ||
| 378 | enum class BreakType : u32 { | ||
| 379 | Panic = 0, | ||
| 380 | AssertionFailed = 1, | ||
| 381 | PreNROLoad = 3, | ||
| 382 | PostNROLoad = 4, | ||
| 383 | PreNROUnload = 5, | ||
| 384 | PostNROUnload = 6, | ||
| 385 | }; | ||
| 386 | |||
| 377 | struct BreakReason { | 387 | struct BreakReason { |
| 378 | union { | 388 | union { |
| 379 | u32 raw; | 389 | u32 raw; |
| 390 | BitField<0, 30, BreakType> break_type; | ||
| 380 | BitField<31, 1, u32> signal_debugger; | 391 | BitField<31, 1, u32> signal_debugger; |
| 381 | }; | 392 | }; |
| 382 | }; | 393 | }; |
| @@ -384,12 +395,48 @@ struct BreakReason { | |||
| 384 | /// Break program execution | 395 | /// Break program execution |
| 385 | static void Break(u32 reason, u64 info1, u64 info2) { | 396 | static void Break(u32 reason, u64 info1, u64 info2) { |
| 386 | BreakReason break_reason{reason}; | 397 | BreakReason break_reason{reason}; |
| 387 | if (break_reason.signal_debugger) { | 398 | |
| 388 | LOG_ERROR( | 399 | switch (break_reason.break_type) { |
| 400 | case BreakType::Panic: | ||
| 401 | LOG_CRITICAL(Debug_Emulated, "Signalling debugger, PANIC! info1=0x{:016X}, info2=0x{:016X}", | ||
| 402 | info1, info2); | ||
| 403 | break; | ||
| 404 | case BreakType::AssertionFailed: | ||
| 405 | LOG_CRITICAL(Debug_Emulated, | ||
| 406 | "Signalling debugger, Assertion failed! info1=0x{:016X}, info2=0x{:016X}", | ||
| 407 | info1, info2); | ||
| 408 | break; | ||
| 409 | case BreakType::PreNROLoad: | ||
| 410 | LOG_WARNING( | ||
| 389 | Debug_Emulated, | 411 | Debug_Emulated, |
| 390 | "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}", | 412 | "Signalling debugger, Attempting to load an NRO at 0x{:016X} with size 0x{:016X}", |
| 391 | reason, info1, info2); | 413 | info1, info2); |
| 392 | } else { | 414 | break; |
| 415 | case BreakType::PostNROLoad: | ||
| 416 | LOG_WARNING(Debug_Emulated, | ||
| 417 | "Signalling debugger, Loaded an NRO at 0x{:016X} with size 0x{:016X}", info1, | ||
| 418 | info2); | ||
| 419 | break; | ||
| 420 | case BreakType::PreNROUnload: | ||
| 421 | LOG_WARNING( | ||
| 422 | Debug_Emulated, | ||
| 423 | "Signalling debugger, Attempting to unload an NRO at 0x{:016X} with size 0x{:016X}", | ||
| 424 | info1, info2); | ||
| 425 | break; | ||
| 426 | case BreakType::PostNROUnload: | ||
| 427 | LOG_WARNING(Debug_Emulated, | ||
| 428 | "Signalling debugger, Unloaded an NRO at 0x{:016X} with size 0x{:016X}", info1, | ||
| 429 | info2); | ||
| 430 | break; | ||
| 431 | default: | ||
| 432 | LOG_WARNING( | ||
| 433 | Debug_Emulated, | ||
| 434 | "Signalling debugger, Unknown break reason {}, info1=0x{:016X}, info2=0x{:016X}", | ||
| 435 | static_cast<u32>(break_reason.break_type.Value()), info1, info2); | ||
| 436 | break; | ||
| 437 | } | ||
| 438 | |||
| 439 | if (!break_reason.signal_debugger) { | ||
| 393 | LOG_CRITICAL( | 440 | LOG_CRITICAL( |
| 394 | Debug_Emulated, | 441 | Debug_Emulated, |
| 395 | "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}", | 442 | "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}", |
| @@ -499,13 +546,12 @@ static ResultCode SetThreadActivity(Handle handle, u32 unknown) { | |||
| 499 | static ResultCode GetThreadContext(VAddr thread_context, Handle handle) { | 546 | static ResultCode GetThreadContext(VAddr thread_context, Handle handle) { |
| 500 | LOG_DEBUG(Kernel_SVC, "called, context=0x{:08X}, thread=0x{:X}", thread_context, handle); | 547 | LOG_DEBUG(Kernel_SVC, "called, context=0x{:08X}, thread=0x{:X}", thread_context, handle); |
| 501 | 548 | ||
| 502 | auto& kernel = Core::System::GetInstance().Kernel(); | 549 | const auto* current_process = Core::CurrentProcess(); |
| 503 | const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(handle); | 550 | const SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle); |
| 504 | if (!thread) { | 551 | if (!thread) { |
| 505 | return ERR_INVALID_HANDLE; | 552 | return ERR_INVALID_HANDLE; |
| 506 | } | 553 | } |
| 507 | 554 | ||
| 508 | const auto* current_process = Core::CurrentProcess(); | ||
| 509 | if (thread->GetOwnerProcess() != current_process) { | 555 | if (thread->GetOwnerProcess() != current_process) { |
| 510 | return ERR_INVALID_HANDLE; | 556 | return ERR_INVALID_HANDLE; |
| 511 | } | 557 | } |
| @@ -531,10 +577,11 @@ static ResultCode GetThreadContext(VAddr thread_context, Handle handle) { | |||
| 531 | 577 | ||
| 532 | /// Gets the priority for the specified thread | 578 | /// Gets the priority for the specified thread |
| 533 | static ResultCode GetThreadPriority(u32* priority, Handle handle) { | 579 | static ResultCode GetThreadPriority(u32* priority, Handle handle) { |
| 534 | auto& kernel = Core::System::GetInstance().Kernel(); | 580 | const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); |
| 535 | const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(handle); | 581 | const SharedPtr<Thread> thread = handle_table.Get<Thread>(handle); |
| 536 | if (!thread) | 582 | if (!thread) { |
| 537 | return ERR_INVALID_HANDLE; | 583 | return ERR_INVALID_HANDLE; |
| 584 | } | ||
| 538 | 585 | ||
| 539 | *priority = thread->GetPriority(); | 586 | *priority = thread->GetPriority(); |
| 540 | return RESULT_SUCCESS; | 587 | return RESULT_SUCCESS; |
| @@ -546,16 +593,18 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) { | |||
| 546 | return ERR_INVALID_THREAD_PRIORITY; | 593 | return ERR_INVALID_THREAD_PRIORITY; |
| 547 | } | 594 | } |
| 548 | 595 | ||
| 549 | auto& kernel = Core::System::GetInstance().Kernel(); | 596 | const auto* const current_process = Core::CurrentProcess(); |
| 550 | SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(handle); | ||
| 551 | if (!thread) | ||
| 552 | return ERR_INVALID_HANDLE; | ||
| 553 | 597 | ||
| 554 | // Note: The kernel uses the current process's resource limit instead of | 598 | // Note: The kernel uses the current process's resource limit instead of |
| 555 | // the one from the thread owner's resource limit. | 599 | // the one from the thread owner's resource limit. |
| 556 | const ResourceLimit& resource_limit = Core::CurrentProcess()->GetResourceLimit(); | 600 | const ResourceLimit& resource_limit = current_process->GetResourceLimit(); |
| 557 | if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) { | 601 | if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) { |
| 558 | return ERR_NOT_AUTHORIZED; | 602 | return ERR_INVALID_THREAD_PRIORITY; |
| 603 | } | ||
| 604 | |||
| 605 | SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle); | ||
| 606 | if (!thread) { | ||
| 607 | return ERR_INVALID_HANDLE; | ||
| 559 | } | 608 | } |
| 560 | 609 | ||
| 561 | thread->SetPriority(priority); | 610 | thread->SetPriority(priority); |
| @@ -584,6 +633,10 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s | |||
| 584 | return ERR_INVALID_SIZE; | 633 | return ERR_INVALID_SIZE; |
| 585 | } | 634 | } |
| 586 | 635 | ||
| 636 | if (!IsValidAddressRange(addr, size)) { | ||
| 637 | return ERR_INVALID_ADDRESS_STATE; | ||
| 638 | } | ||
| 639 | |||
| 587 | const auto permissions_type = static_cast<MemoryPermission>(permissions); | 640 | const auto permissions_type = static_cast<MemoryPermission>(permissions); |
| 588 | if (permissions_type != MemoryPermission::Read && | 641 | if (permissions_type != MemoryPermission::Read && |
| 589 | permissions_type != MemoryPermission::ReadWrite) { | 642 | permissions_type != MemoryPermission::ReadWrite) { |
| @@ -591,14 +644,18 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s | |||
| 591 | return ERR_INVALID_MEMORY_PERMISSIONS; | 644 | return ERR_INVALID_MEMORY_PERMISSIONS; |
| 592 | } | 645 | } |
| 593 | 646 | ||
| 594 | auto& kernel = Core::System::GetInstance().Kernel(); | 647 | auto* const current_process = Core::CurrentProcess(); |
| 595 | auto shared_memory = kernel.HandleTable().Get<SharedMemory>(shared_memory_handle); | 648 | auto shared_memory = current_process->GetHandleTable().Get<SharedMemory>(shared_memory_handle); |
| 596 | if (!shared_memory) { | 649 | if (!shared_memory) { |
| 597 | return ERR_INVALID_HANDLE; | 650 | return ERR_INVALID_HANDLE; |
| 598 | } | 651 | } |
| 599 | 652 | ||
| 600 | return shared_memory->Map(Core::CurrentProcess(), addr, permissions_type, | 653 | const auto& vm_manager = current_process->VMManager(); |
| 601 | MemoryPermission::DontCare); | 654 | if (!vm_manager.IsWithinASLRRegion(addr, size)) { |
| 655 | return ERR_INVALID_MEMORY_RANGE; | ||
| 656 | } | ||
| 657 | |||
| 658 | return shared_memory->Map(current_process, addr, permissions_type, MemoryPermission::DontCare); | ||
| 602 | } | 659 | } |
| 603 | 660 | ||
| 604 | static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size) { | 661 | static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size) { |
| @@ -613,24 +670,35 @@ static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 | |||
| 613 | return ERR_INVALID_SIZE; | 670 | return ERR_INVALID_SIZE; |
| 614 | } | 671 | } |
| 615 | 672 | ||
| 616 | auto& kernel = Core::System::GetInstance().Kernel(); | 673 | if (!IsValidAddressRange(addr, size)) { |
| 617 | auto shared_memory = kernel.HandleTable().Get<SharedMemory>(shared_memory_handle); | 674 | return ERR_INVALID_ADDRESS_STATE; |
| 675 | } | ||
| 676 | |||
| 677 | auto* const current_process = Core::CurrentProcess(); | ||
| 678 | auto shared_memory = current_process->GetHandleTable().Get<SharedMemory>(shared_memory_handle); | ||
| 679 | if (!shared_memory) { | ||
| 680 | return ERR_INVALID_HANDLE; | ||
| 681 | } | ||
| 682 | |||
| 683 | const auto& vm_manager = current_process->VMManager(); | ||
| 684 | if (!vm_manager.IsWithinASLRRegion(addr, size)) { | ||
| 685 | return ERR_INVALID_MEMORY_RANGE; | ||
| 686 | } | ||
| 618 | 687 | ||
| 619 | return shared_memory->Unmap(Core::CurrentProcess(), addr); | 688 | return shared_memory->Unmap(current_process, addr); |
| 620 | } | 689 | } |
| 621 | 690 | ||
| 622 | /// Query process memory | 691 | /// Query process memory |
| 623 | static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* /*page_info*/, | 692 | static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* /*page_info*/, |
| 624 | Handle process_handle, u64 addr) { | 693 | Handle process_handle, u64 addr) { |
| 625 | 694 | const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); | |
| 626 | auto& kernel = Core::System::GetInstance().Kernel(); | 695 | SharedPtr<Process> process = handle_table.Get<Process>(process_handle); |
| 627 | SharedPtr<Process> process = kernel.HandleTable().Get<Process>(process_handle); | ||
| 628 | if (!process) { | 696 | if (!process) { |
| 629 | return ERR_INVALID_HANDLE; | 697 | return ERR_INVALID_HANDLE; |
| 630 | } | 698 | } |
| 631 | auto vma = process->VMManager().FindVMA(addr); | 699 | auto vma = process->VMManager().FindVMA(addr); |
| 632 | memory_info->attributes = 0; | 700 | memory_info->attributes = 0; |
| 633 | if (vma == Core::CurrentProcess()->VMManager().vma_map.end()) { | 701 | if (vma == process->VMManager().vma_map.end()) { |
| 634 | memory_info->base_address = 0; | 702 | memory_info->base_address = 0; |
| 635 | memory_info->permission = static_cast<u32>(VMAPermission::None); | 703 | memory_info->permission = static_cast<u32>(VMAPermission::None); |
| 636 | memory_info->size = 0; | 704 | memory_info->size = 0; |
| @@ -671,20 +739,19 @@ static void ExitProcess() { | |||
| 671 | /// Creates a new thread | 739 | /// Creates a new thread |
| 672 | static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, VAddr stack_top, | 740 | static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, VAddr stack_top, |
| 673 | u32 priority, s32 processor_id) { | 741 | u32 priority, s32 processor_id) { |
| 674 | std::string name = fmt::format("thread-{:X}", entry_point); | ||
| 675 | |||
| 676 | if (priority > THREADPRIO_LOWEST) { | 742 | if (priority > THREADPRIO_LOWEST) { |
| 677 | return ERR_INVALID_THREAD_PRIORITY; | 743 | return ERR_INVALID_THREAD_PRIORITY; |
| 678 | } | 744 | } |
| 679 | 745 | ||
| 680 | const ResourceLimit& resource_limit = Core::CurrentProcess()->GetResourceLimit(); | 746 | auto* const current_process = Core::CurrentProcess(); |
| 747 | const ResourceLimit& resource_limit = current_process->GetResourceLimit(); | ||
| 681 | if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) { | 748 | if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) { |
| 682 | return ERR_NOT_AUTHORIZED; | 749 | return ERR_INVALID_THREAD_PRIORITY; |
| 683 | } | 750 | } |
| 684 | 751 | ||
| 685 | if (processor_id == THREADPROCESSORID_DEFAULT) { | 752 | if (processor_id == THREADPROCESSORID_DEFAULT) { |
| 686 | // Set the target CPU to the one specified in the process' exheader. | 753 | // Set the target CPU to the one specified in the process' exheader. |
| 687 | processor_id = Core::CurrentProcess()->GetDefaultProcessorID(); | 754 | processor_id = current_process->GetDefaultProcessorID(); |
| 688 | ASSERT(processor_id != THREADPROCESSORID_DEFAULT); | 755 | ASSERT(processor_id != THREADPROCESSORID_DEFAULT); |
| 689 | } | 756 | } |
| 690 | 757 | ||
| @@ -699,11 +766,13 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V | |||
| 699 | return ERR_INVALID_PROCESSOR_ID; | 766 | return ERR_INVALID_PROCESSOR_ID; |
| 700 | } | 767 | } |
| 701 | 768 | ||
| 769 | const std::string name = fmt::format("thread-{:X}", entry_point); | ||
| 702 | auto& kernel = Core::System::GetInstance().Kernel(); | 770 | auto& kernel = Core::System::GetInstance().Kernel(); |
| 703 | CASCADE_RESULT(SharedPtr<Thread> thread, | 771 | CASCADE_RESULT(SharedPtr<Thread> thread, |
| 704 | Thread::Create(kernel, name, entry_point, priority, arg, processor_id, stack_top, | 772 | Thread::Create(kernel, name, entry_point, priority, arg, processor_id, stack_top, |
| 705 | *Core::CurrentProcess())); | 773 | *current_process)); |
| 706 | const auto new_guest_handle = kernel.HandleTable().Create(thread); | 774 | |
| 775 | const auto new_guest_handle = current_process->GetHandleTable().Create(thread); | ||
| 707 | if (new_guest_handle.Failed()) { | 776 | if (new_guest_handle.Failed()) { |
| 708 | return new_guest_handle.Code(); | 777 | return new_guest_handle.Code(); |
| 709 | } | 778 | } |
| @@ -724,8 +793,8 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V | |||
| 724 | static ResultCode StartThread(Handle thread_handle) { | 793 | static ResultCode StartThread(Handle thread_handle) { |
| 725 | LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle); | 794 | LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle); |
| 726 | 795 | ||
| 727 | auto& kernel = Core::System::GetInstance().Kernel(); | 796 | const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); |
| 728 | const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle); | 797 | const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); |
| 729 | if (!thread) { | 798 | if (!thread) { |
| 730 | return ERR_INVALID_HANDLE; | 799 | return ERR_INVALID_HANDLE; |
| 731 | } | 800 | } |
| @@ -772,8 +841,8 @@ static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_var | |||
| 772 | "called mutex_addr={:X}, condition_variable_addr={:X}, thread_handle=0x{:08X}, timeout={}", | 841 | "called mutex_addr={:X}, condition_variable_addr={:X}, thread_handle=0x{:08X}, timeout={}", |
| 773 | mutex_addr, condition_variable_addr, thread_handle, nano_seconds); | 842 | mutex_addr, condition_variable_addr, thread_handle, nano_seconds); |
| 774 | 843 | ||
| 775 | auto& kernel = Core::System::GetInstance().Kernel(); | 844 | const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); |
| 776 | SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle); | 845 | SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); |
| 777 | ASSERT(thread); | 846 | ASSERT(thread); |
| 778 | 847 | ||
| 779 | CASCADE_CODE(Mutex::Release(mutex_addr)); | 848 | CASCADE_CODE(Mutex::Release(mutex_addr)); |
| @@ -884,9 +953,9 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target | |||
| 884 | mutex_val | Mutex::MutexHasWaitersFlag)); | 953 | mutex_val | Mutex::MutexHasWaitersFlag)); |
| 885 | 954 | ||
| 886 | // The mutex is already owned by some other thread, make this thread wait on it. | 955 | // The mutex is already owned by some other thread, make this thread wait on it. |
| 887 | auto& kernel = Core::System::GetInstance().Kernel(); | 956 | const Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask); |
| 888 | Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask); | 957 | const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); |
| 889 | auto owner = kernel.HandleTable().Get<Thread>(owner_handle); | 958 | auto owner = handle_table.Get<Thread>(owner_handle); |
| 890 | ASSERT(owner); | 959 | ASSERT(owner); |
| 891 | ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex); | 960 | ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex); |
| 892 | thread->InvalidateWakeupCallback(); | 961 | thread->InvalidateWakeupCallback(); |
| @@ -965,16 +1034,16 @@ static u64 GetSystemTick() { | |||
| 965 | static ResultCode CloseHandle(Handle handle) { | 1034 | static ResultCode CloseHandle(Handle handle) { |
| 966 | LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle); | 1035 | LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle); |
| 967 | 1036 | ||
| 968 | auto& kernel = Core::System::GetInstance().Kernel(); | 1037 | auto& handle_table = Core::CurrentProcess()->GetHandleTable(); |
| 969 | return kernel.HandleTable().Close(handle); | 1038 | return handle_table.Close(handle); |
| 970 | } | 1039 | } |
| 971 | 1040 | ||
| 972 | /// Reset an event | 1041 | /// Reset an event |
| 973 | static ResultCode ResetSignal(Handle handle) { | 1042 | static ResultCode ResetSignal(Handle handle) { |
| 974 | LOG_WARNING(Kernel_SVC, "(STUBBED) called handle 0x{:08X}", handle); | 1043 | LOG_WARNING(Kernel_SVC, "(STUBBED) called handle 0x{:08X}", handle); |
| 975 | 1044 | ||
| 976 | auto& kernel = Core::System::GetInstance().Kernel(); | 1045 | const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); |
| 977 | auto event = kernel.HandleTable().Get<Event>(handle); | 1046 | auto event = handle_table.Get<Event>(handle); |
| 978 | 1047 | ||
| 979 | ASSERT(event != nullptr); | 1048 | ASSERT(event != nullptr); |
| 980 | 1049 | ||
| @@ -993,8 +1062,8 @@ static ResultCode CreateTransferMemory(Handle* handle, VAddr addr, u64 size, u32 | |||
| 993 | static ResultCode GetThreadCoreMask(Handle thread_handle, u32* core, u64* mask) { | 1062 | static ResultCode GetThreadCoreMask(Handle thread_handle, u32* core, u64* mask) { |
| 994 | LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle); | 1063 | LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle); |
| 995 | 1064 | ||
| 996 | auto& kernel = Core::System::GetInstance().Kernel(); | 1065 | const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); |
| 997 | const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle); | 1066 | const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); |
| 998 | if (!thread) { | 1067 | if (!thread) { |
| 999 | return ERR_INVALID_HANDLE; | 1068 | return ERR_INVALID_HANDLE; |
| 1000 | } | 1069 | } |
| @@ -1009,8 +1078,8 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) { | |||
| 1009 | LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, mask=0x{:16X}, core=0x{:X}", thread_handle, | 1078 | LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, mask=0x{:16X}, core=0x{:X}", thread_handle, |
| 1010 | mask, core); | 1079 | mask, core); |
| 1011 | 1080 | ||
| 1012 | auto& kernel = Core::System::GetInstance().Kernel(); | 1081 | const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); |
| 1013 | const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle); | 1082 | const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); |
| 1014 | if (!thread) { | 1083 | if (!thread) { |
| 1015 | return ERR_INVALID_HANDLE; | 1084 | return ERR_INVALID_HANDLE; |
| 1016 | } | 1085 | } |
| @@ -1071,7 +1140,7 @@ static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permiss | |||
| 1071 | } | 1140 | } |
| 1072 | 1141 | ||
| 1073 | auto& kernel = Core::System::GetInstance().Kernel(); | 1142 | auto& kernel = Core::System::GetInstance().Kernel(); |
| 1074 | auto& handle_table = kernel.HandleTable(); | 1143 | auto& handle_table = Core::CurrentProcess()->GetHandleTable(); |
| 1075 | auto shared_mem_handle = | 1144 | auto shared_mem_handle = |
| 1076 | SharedMemory::Create(kernel, handle_table.Get<Process>(KernelHandle::CurrentProcess), size, | 1145 | SharedMemory::Create(kernel, handle_table.Get<Process>(KernelHandle::CurrentProcess), size, |
| 1077 | local_perms, remote_perms); | 1146 | local_perms, remote_perms); |
| @@ -1083,10 +1152,12 @@ static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permiss | |||
| 1083 | static ResultCode ClearEvent(Handle handle) { | 1152 | static ResultCode ClearEvent(Handle handle) { |
| 1084 | LOG_TRACE(Kernel_SVC, "called, event=0x{:08X}", handle); | 1153 | LOG_TRACE(Kernel_SVC, "called, event=0x{:08X}", handle); |
| 1085 | 1154 | ||
| 1086 | auto& kernel = Core::System::GetInstance().Kernel(); | 1155 | const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); |
| 1087 | SharedPtr<Event> evt = kernel.HandleTable().Get<Event>(handle); | 1156 | SharedPtr<Event> evt = handle_table.Get<Event>(handle); |
| 1088 | if (evt == nullptr) | 1157 | if (evt == nullptr) { |
| 1089 | return ERR_INVALID_HANDLE; | 1158 | return ERR_INVALID_HANDLE; |
| 1159 | } | ||
| 1160 | |||
| 1090 | evt->Clear(); | 1161 | evt->Clear(); |
| 1091 | return RESULT_SUCCESS; | 1162 | return RESULT_SUCCESS; |
| 1092 | } | 1163 | } |
| @@ -1099,8 +1170,8 @@ static ResultCode GetProcessInfo(u64* out, Handle process_handle, u32 type) { | |||
| 1099 | Status, | 1170 | Status, |
| 1100 | }; | 1171 | }; |
| 1101 | 1172 | ||
| 1102 | const auto& kernel = Core::System::GetInstance().Kernel(); | 1173 | const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); |
| 1103 | const auto process = kernel.HandleTable().Get<Process>(process_handle); | 1174 | const auto process = handle_table.Get<Process>(process_handle); |
| 1104 | if (!process) { | 1175 | if (!process) { |
| 1105 | return ERR_INVALID_HANDLE; | 1176 | return ERR_INVALID_HANDLE; |
| 1106 | } | 1177 | } |
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 35ec98c1a..59bc9e0af 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp | |||
| @@ -266,7 +266,7 @@ SharedPtr<Thread> SetupMainThread(KernelCore& kernel, VAddr entry_point, u32 pri | |||
| 266 | SharedPtr<Thread> thread = std::move(thread_res).Unwrap(); | 266 | SharedPtr<Thread> thread = std::move(thread_res).Unwrap(); |
| 267 | 267 | ||
| 268 | // Register 1 must be a handle to the main thread | 268 | // Register 1 must be a handle to the main thread |
| 269 | const Handle guest_handle = kernel.HandleTable().Create(thread).Unwrap(); | 269 | const Handle guest_handle = owner_process.GetHandleTable().Create(thread).Unwrap(); |
| 270 | thread->SetGuestHandle(guest_handle); | 270 | thread->SetGuestHandle(guest_handle); |
| 271 | thread->GetContext().cpu_registers[1] = guest_handle; | 271 | thread->GetContext().cpu_registers[1] = guest_handle; |
| 272 | 272 | ||
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index 1e28ccbda..1a92c8f70 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp | |||
| @@ -143,6 +143,26 @@ ResultVal<VMManager::VMAHandle> VMManager::MapBackingMemory(VAddr target, u8* me | |||
| 143 | return MakeResult<VMAHandle>(MergeAdjacent(vma_handle)); | 143 | return MakeResult<VMAHandle>(MergeAdjacent(vma_handle)); |
| 144 | } | 144 | } |
| 145 | 145 | ||
| 146 | ResultVal<VAddr> VMManager::FindFreeRegion(u64 size) const { | ||
| 147 | // Find the first Free VMA. | ||
| 148 | const VAddr base = GetASLRRegionBaseAddress(); | ||
| 149 | const VMAHandle vma_handle = std::find_if(vma_map.begin(), vma_map.end(), [&](const auto& vma) { | ||
| 150 | if (vma.second.type != VMAType::Free) | ||
| 151 | return false; | ||
| 152 | |||
| 153 | const VAddr vma_end = vma.second.base + vma.second.size; | ||
| 154 | return vma_end > base && vma_end >= base + size; | ||
| 155 | }); | ||
| 156 | |||
| 157 | if (vma_handle == vma_map.end()) { | ||
| 158 | // TODO(Subv): Find the correct error code here. | ||
| 159 | return ResultCode(-1); | ||
| 160 | } | ||
| 161 | |||
| 162 | const VAddr target = std::max(base, vma_handle->second.base); | ||
| 163 | return MakeResult<VAddr>(target); | ||
| 164 | } | ||
| 165 | |||
| 146 | ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u64 size, | 166 | ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u64 size, |
| 147 | MemoryState state, | 167 | MemoryState state, |
| 148 | Memory::MemoryHookPointer mmio_handler) { | 168 | Memory::MemoryHookPointer mmio_handler) { |
| @@ -507,6 +527,26 @@ u64 VMManager::GetASLRRegionSize() const { | |||
| 507 | return aslr_region_end - aslr_region_base; | 527 | return aslr_region_end - aslr_region_base; |
| 508 | } | 528 | } |
| 509 | 529 | ||
| 530 | bool VMManager::IsWithinASLRRegion(VAddr begin, u64 size) const { | ||
| 531 | const VAddr range_end = begin + size; | ||
| 532 | const VAddr aslr_start = GetASLRRegionBaseAddress(); | ||
| 533 | const VAddr aslr_end = GetASLRRegionEndAddress(); | ||
| 534 | |||
| 535 | if (aslr_start > begin || begin > range_end || range_end - 1 > aslr_end - 1) { | ||
| 536 | return false; | ||
| 537 | } | ||
| 538 | |||
| 539 | if (range_end > heap_region_base && heap_region_end > begin) { | ||
| 540 | return false; | ||
| 541 | } | ||
| 542 | |||
| 543 | if (range_end > map_region_base && map_region_end > begin) { | ||
| 544 | return false; | ||
| 545 | } | ||
| 546 | |||
| 547 | return true; | ||
| 548 | } | ||
| 549 | |||
| 510 | VAddr VMManager::GetCodeRegionBaseAddress() const { | 550 | VAddr VMManager::GetCodeRegionBaseAddress() const { |
| 511 | return code_region_base; | 551 | return code_region_base; |
| 512 | } | 552 | } |
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h index 4accde6b3..2447cbb8f 100644 --- a/src/core/hle/kernel/vm_manager.h +++ b/src/core/hle/kernel/vm_manager.h | |||
| @@ -158,6 +158,14 @@ public: | |||
| 158 | ResultVal<VMAHandle> MapBackingMemory(VAddr target, u8* memory, u64 size, MemoryState state); | 158 | ResultVal<VMAHandle> MapBackingMemory(VAddr target, u8* memory, u64 size, MemoryState state); |
| 159 | 159 | ||
| 160 | /** | 160 | /** |
| 161 | * Finds the first free address that can hold a region of the desired size. | ||
| 162 | * | ||
| 163 | * @param size Size of the desired region. | ||
| 164 | * @return The found free address. | ||
| 165 | */ | ||
| 166 | ResultVal<VAddr> FindFreeRegion(u64 size) const; | ||
| 167 | |||
| 168 | /** | ||
| 161 | * Maps a memory-mapped IO region at a given address. | 169 | * Maps a memory-mapped IO region at a given address. |
| 162 | * | 170 | * |
| 163 | * @param target The guest address to start the mapping at. | 171 | * @param target The guest address to start the mapping at. |
| @@ -211,6 +219,9 @@ public: | |||
| 211 | /// Gets the end address of the ASLR region. | 219 | /// Gets the end address of the ASLR region. |
| 212 | VAddr GetASLRRegionEndAddress() const; | 220 | VAddr GetASLRRegionEndAddress() const; |
| 213 | 221 | ||
| 222 | /// Determines whether or not the specified address range is within the ASLR region. | ||
| 223 | bool IsWithinASLRRegion(VAddr address, u64 size) const; | ||
| 224 | |||
| 214 | /// Gets the size of the ASLR region | 225 | /// Gets the size of the ASLR region |
| 215 | u64 GetASLRRegionSize() const; | 226 | u64 GetASLRRegionSize() const; |
| 216 | 227 | ||
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp index e61748ca3..c6437a671 100644 --- a/src/core/hle/service/acc/acc.cpp +++ b/src/core/hle/service/acc/acc.cpp | |||
| @@ -2,9 +2,13 @@ | |||
| 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 <algorithm> | ||
| 5 | #include <array> | 6 | #include <array> |
| 7 | #include "common/common_paths.h" | ||
| 6 | #include "common/common_types.h" | 8 | #include "common/common_types.h" |
| 9 | #include "common/file_util.h" | ||
| 7 | #include "common/logging/log.h" | 10 | #include "common/logging/log.h" |
| 11 | #include "common/string_util.h" | ||
| 8 | #include "common/swap.h" | 12 | #include "common/swap.h" |
| 9 | #include "core/core_timing.h" | 13 | #include "core/core_timing.h" |
| 10 | #include "core/hle/ipc_helpers.h" | 14 | #include "core/hle/ipc_helpers.h" |
| @@ -16,6 +20,7 @@ | |||
| 16 | #include "core/hle/service/acc/profile_manager.h" | 20 | #include "core/hle/service/acc/profile_manager.h" |
| 17 | 21 | ||
| 18 | namespace Service::Account { | 22 | namespace Service::Account { |
| 23 | |||
| 19 | // TODO: RE this structure | 24 | // TODO: RE this structure |
| 20 | struct UserData { | 25 | struct UserData { |
| 21 | INSERT_PADDING_WORDS(1); | 26 | INSERT_PADDING_WORDS(1); |
| @@ -27,6 +32,29 @@ struct UserData { | |||
| 27 | }; | 32 | }; |
| 28 | static_assert(sizeof(UserData) == 0x80, "UserData structure has incorrect size"); | 33 | static_assert(sizeof(UserData) == 0x80, "UserData structure has incorrect size"); |
| 29 | 34 | ||
| 35 | // Smallest JPEG https://github.com/mathiasbynens/small/blob/master/jpeg.jpg | ||
| 36 | // used as a backup should the one on disk not exist | ||
| 37 | constexpr u32 backup_jpeg_size = 107; | ||
| 38 | constexpr std::array<u8, backup_jpeg_size> backup_jpeg{{ | ||
| 39 | 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x02, 0x02, | ||
| 40 | 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, 0x08, 0x06, 0x06, 0x05, | ||
| 41 | 0x06, 0x09, 0x08, 0x0a, 0x0a, 0x09, 0x08, 0x09, 0x09, 0x0a, 0x0c, 0x0f, 0x0c, 0x0a, 0x0b, 0x0e, | ||
| 42 | 0x0b, 0x09, 0x09, 0x0d, 0x11, 0x0d, 0x0e, 0x0f, 0x10, 0x10, 0x11, 0x10, 0x0a, 0x0c, 0x12, 0x13, | ||
| 43 | 0x12, 0x10, 0x13, 0x0f, 0x10, 0x10, 0x10, 0xff, 0xc9, 0x00, 0x0b, 0x08, 0x00, 0x01, 0x00, 0x01, | ||
| 44 | 0x01, 0x01, 0x11, 0x00, 0xff, 0xcc, 0x00, 0x06, 0x00, 0x10, 0x10, 0x05, 0xff, 0xda, 0x00, 0x08, | ||
| 45 | 0x01, 0x01, 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9, | ||
| 46 | }}; | ||
| 47 | |||
| 48 | static std::string GetImagePath(UUID uuid) { | ||
| 49 | return FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + | ||
| 50 | "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg"; | ||
| 51 | } | ||
| 52 | |||
| 53 | static constexpr u32 SanitizeJPEGSize(std::size_t size) { | ||
| 54 | constexpr std::size_t max_jpeg_image_size = 0x20000; | ||
| 55 | return static_cast<u32>(std::min(size, max_jpeg_image_size)); | ||
| 56 | } | ||
| 57 | |||
| 30 | class IProfile final : public ServiceFramework<IProfile> { | 58 | class IProfile final : public ServiceFramework<IProfile> { |
| 31 | public: | 59 | public: |
| 32 | explicit IProfile(UUID user_id, ProfileManager& profile_manager) | 60 | explicit IProfile(UUID user_id, ProfileManager& profile_manager) |
| @@ -73,32 +101,42 @@ private: | |||
| 73 | } | 101 | } |
| 74 | 102 | ||
| 75 | void LoadImage(Kernel::HLERequestContext& ctx) { | 103 | void LoadImage(Kernel::HLERequestContext& ctx) { |
| 76 | LOG_WARNING(Service_ACC, "(STUBBED) called"); | 104 | LOG_DEBUG(Service_ACC, "called"); |
| 77 | // smallest jpeg https://github.com/mathiasbynens/small/blob/master/jpeg.jpg | 105 | |
| 78 | // TODO(mailwl): load actual profile image from disk, width 256px, max size 0x20000 | ||
| 79 | constexpr u32 jpeg_size = 107; | ||
| 80 | static constexpr std::array<u8, jpeg_size> jpeg{ | ||
| 81 | 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, | ||
| 82 | 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, | ||
| 83 | 0x08, 0x06, 0x06, 0x05, 0x06, 0x09, 0x08, 0x0a, 0x0a, 0x09, 0x08, 0x09, 0x09, 0x0a, | ||
| 84 | 0x0c, 0x0f, 0x0c, 0x0a, 0x0b, 0x0e, 0x0b, 0x09, 0x09, 0x0d, 0x11, 0x0d, 0x0e, 0x0f, | ||
| 85 | 0x10, 0x10, 0x11, 0x10, 0x0a, 0x0c, 0x12, 0x13, 0x12, 0x10, 0x13, 0x0f, 0x10, 0x10, | ||
| 86 | 0x10, 0xff, 0xc9, 0x00, 0x0b, 0x08, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x11, 0x00, | ||
| 87 | 0xff, 0xcc, 0x00, 0x06, 0x00, 0x10, 0x10, 0x05, 0xff, 0xda, 0x00, 0x08, 0x01, 0x01, | ||
| 88 | 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9, | ||
| 89 | }; | ||
| 90 | ctx.WriteBuffer(jpeg); | ||
| 91 | IPC::ResponseBuilder rb{ctx, 3}; | 106 | IPC::ResponseBuilder rb{ctx, 3}; |
| 92 | rb.Push(RESULT_SUCCESS); | 107 | rb.Push(RESULT_SUCCESS); |
| 93 | rb.Push<u32>(jpeg_size); | 108 | |
| 109 | const FileUtil::IOFile image(GetImagePath(user_id), "rb"); | ||
| 110 | if (!image.IsOpen()) { | ||
| 111 | LOG_WARNING(Service_ACC, | ||
| 112 | "Failed to load user provided image! Falling back to built-in backup..."); | ||
| 113 | ctx.WriteBuffer(backup_jpeg); | ||
| 114 | rb.Push<u32>(backup_jpeg_size); | ||
| 115 | return; | ||
| 116 | } | ||
| 117 | |||
| 118 | const u32 size = SanitizeJPEGSize(image.GetSize()); | ||
| 119 | std::vector<u8> buffer(size); | ||
| 120 | image.ReadBytes(buffer.data(), buffer.size()); | ||
| 121 | |||
| 122 | ctx.WriteBuffer(buffer.data(), buffer.size()); | ||
| 123 | rb.Push<u32>(size); | ||
| 94 | } | 124 | } |
| 95 | 125 | ||
| 96 | void GetImageSize(Kernel::HLERequestContext& ctx) { | 126 | void GetImageSize(Kernel::HLERequestContext& ctx) { |
| 97 | LOG_WARNING(Service_ACC, "(STUBBED) called"); | 127 | LOG_DEBUG(Service_ACC, "called"); |
| 98 | constexpr u32 jpeg_size = 107; | ||
| 99 | IPC::ResponseBuilder rb{ctx, 3}; | 128 | IPC::ResponseBuilder rb{ctx, 3}; |
| 100 | rb.Push(RESULT_SUCCESS); | 129 | rb.Push(RESULT_SUCCESS); |
| 101 | rb.Push<u32>(jpeg_size); | 130 | |
| 131 | const FileUtil::IOFile image(GetImagePath(user_id), "rb"); | ||
| 132 | |||
| 133 | if (!image.IsOpen()) { | ||
| 134 | LOG_WARNING(Service_ACC, | ||
| 135 | "Failed to load user provided image! Falling back to built-in backup..."); | ||
| 136 | rb.Push<u32>(backup_jpeg_size); | ||
| 137 | } else { | ||
| 138 | rb.Push<u32>(SanitizeJPEGSize(image.GetSize())); | ||
| 139 | } | ||
| 102 | } | 140 | } |
| 103 | 141 | ||
| 104 | const ProfileManager& profile_manager; | 142 | const ProfileManager& profile_manager; |
diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp index bcb3475db..3cac1b4ff 100644 --- a/src/core/hle/service/acc/profile_manager.cpp +++ b/src/core/hle/service/acc/profile_manager.cpp | |||
| @@ -3,41 +3,66 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <random> | 5 | #include <random> |
| 6 | #include <boost/optional.hpp> | 6 | |
| 7 | #include "common/file_util.h" | ||
| 7 | #include "core/hle/service/acc/profile_manager.h" | 8 | #include "core/hle/service/acc/profile_manager.h" |
| 8 | #include "core/settings.h" | 9 | #include "core/settings.h" |
| 9 | 10 | ||
| 10 | namespace Service::Account { | 11 | namespace Service::Account { |
| 12 | |||
| 13 | struct UserRaw { | ||
| 14 | UUID uuid; | ||
| 15 | UUID uuid2; | ||
| 16 | u64 timestamp; | ||
| 17 | ProfileUsername username; | ||
| 18 | INSERT_PADDING_BYTES(0x80); | ||
| 19 | }; | ||
| 20 | static_assert(sizeof(UserRaw) == 0xC8, "UserRaw has incorrect size."); | ||
| 21 | |||
| 22 | struct ProfileDataRaw { | ||
| 23 | INSERT_PADDING_BYTES(0x10); | ||
| 24 | std::array<UserRaw, MAX_USERS> users; | ||
| 25 | }; | ||
| 26 | static_assert(sizeof(ProfileDataRaw) == 0x650, "ProfileDataRaw has incorrect size."); | ||
| 27 | |||
| 11 | // TODO(ogniK): Get actual error codes | 28 | // TODO(ogniK): Get actual error codes |
| 12 | constexpr ResultCode ERROR_TOO_MANY_USERS(ErrorModule::Account, -1); | 29 | constexpr ResultCode ERROR_TOO_MANY_USERS(ErrorModule::Account, -1); |
| 13 | constexpr ResultCode ERROR_USER_ALREADY_EXISTS(ErrorModule::Account, -2); | 30 | constexpr ResultCode ERROR_USER_ALREADY_EXISTS(ErrorModule::Account, -2); |
| 14 | constexpr ResultCode ERROR_ARGUMENT_IS_NULL(ErrorModule::Account, 20); | 31 | constexpr ResultCode ERROR_ARGUMENT_IS_NULL(ErrorModule::Account, 20); |
| 15 | 32 | ||
| 16 | const UUID& UUID::Generate() { | 33 | constexpr char ACC_SAVE_AVATORS_BASE_PATH[] = "/system/save/8000000000000010/su/avators/"; |
| 34 | |||
| 35 | UUID UUID::Generate() { | ||
| 17 | std::random_device device; | 36 | std::random_device device; |
| 18 | std::mt19937 gen(device()); | 37 | std::mt19937 gen(device()); |
| 19 | std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max()); | 38 | std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max()); |
| 20 | uuid[0] = distribution(gen); | 39 | return UUID{distribution(gen), distribution(gen)}; |
| 21 | uuid[1] = distribution(gen); | ||
| 22 | return *this; | ||
| 23 | } | 40 | } |
| 24 | 41 | ||
| 25 | ProfileManager::ProfileManager() { | 42 | ProfileManager::ProfileManager() { |
| 26 | // TODO(ogniK): Create the default user we have for now until loading/saving users is added | 43 | ParseUserSaveFile(); |
| 27 | auto user_uuid = UUID{1, 0}; | 44 | |
| 28 | ASSERT(CreateNewUser(user_uuid, Settings::values.username).IsSuccess()); | 45 | if (user_count == 0) |
| 29 | OpenUser(user_uuid); | 46 | CreateNewUser(UUID::Generate(), "yuzu"); |
| 47 | |||
| 48 | auto current = std::clamp<int>(Settings::values.current_user, 0, MAX_USERS - 1); | ||
| 49 | if (UserExistsIndex(current)) | ||
| 50 | current = 0; | ||
| 51 | |||
| 52 | OpenUser(*GetUser(current)); | ||
| 30 | } | 53 | } |
| 31 | 54 | ||
| 32 | ProfileManager::~ProfileManager() = default; | 55 | ProfileManager::~ProfileManager() { |
| 56 | WriteUserSaveFile(); | ||
| 57 | } | ||
| 33 | 58 | ||
| 34 | /// After a users creation it needs to be "registered" to the system. AddToProfiles handles the | 59 | /// After a users creation it needs to be "registered" to the system. AddToProfiles handles the |
| 35 | /// internal management of the users profiles | 60 | /// internal management of the users profiles |
| 36 | boost::optional<std::size_t> ProfileManager::AddToProfiles(const ProfileInfo& user) { | 61 | std::optional<std::size_t> ProfileManager::AddToProfiles(const ProfileInfo& profile) { |
| 37 | if (user_count >= MAX_USERS) { | 62 | if (user_count >= MAX_USERS) { |
| 38 | return boost::none; | 63 | return {}; |
| 39 | } | 64 | } |
| 40 | profiles[user_count] = user; | 65 | profiles[user_count] = profile; |
| 41 | return user_count++; | 66 | return user_count++; |
| 42 | } | 67 | } |
| 43 | 68 | ||
| @@ -56,7 +81,7 @@ bool ProfileManager::RemoveProfileAtIndex(std::size_t index) { | |||
| 56 | 81 | ||
| 57 | /// Helper function to register a user to the system | 82 | /// Helper function to register a user to the system |
| 58 | ResultCode ProfileManager::AddUser(const ProfileInfo& user) { | 83 | ResultCode ProfileManager::AddUser(const ProfileInfo& user) { |
| 59 | if (AddToProfiles(user) == boost::none) { | 84 | if (!AddToProfiles(user)) { |
| 60 | return ERROR_TOO_MANY_USERS; | 85 | return ERROR_TOO_MANY_USERS; |
| 61 | } | 86 | } |
| 62 | return RESULT_SUCCESS; | 87 | return RESULT_SUCCESS; |
| @@ -101,31 +126,40 @@ ResultCode ProfileManager::CreateNewUser(UUID uuid, const std::string& username) | |||
| 101 | return CreateNewUser(uuid, username_output); | 126 | return CreateNewUser(uuid, username_output); |
| 102 | } | 127 | } |
| 103 | 128 | ||
| 129 | std::optional<UUID> ProfileManager::GetUser(std::size_t index) const { | ||
| 130 | if (index >= MAX_USERS) { | ||
| 131 | return {}; | ||
| 132 | } | ||
| 133 | |||
| 134 | return profiles[index].user_uuid; | ||
| 135 | } | ||
| 136 | |||
| 104 | /// Returns a users profile index based on their user id. | 137 | /// Returns a users profile index based on their user id. |
| 105 | boost::optional<std::size_t> ProfileManager::GetUserIndex(const UUID& uuid) const { | 138 | std::optional<std::size_t> ProfileManager::GetUserIndex(const UUID& uuid) const { |
| 106 | if (!uuid) { | 139 | if (!uuid) { |
| 107 | return boost::none; | 140 | return {}; |
| 108 | } | 141 | } |
| 109 | auto iter = std::find_if(profiles.begin(), profiles.end(), | 142 | |
| 110 | [&uuid](const ProfileInfo& p) { return p.user_uuid == uuid; }); | 143 | const auto iter = std::find_if(profiles.begin(), profiles.end(), |
| 144 | [&uuid](const ProfileInfo& p) { return p.user_uuid == uuid; }); | ||
| 111 | if (iter == profiles.end()) { | 145 | if (iter == profiles.end()) { |
| 112 | return boost::none; | 146 | return {}; |
| 113 | } | 147 | } |
| 148 | |||
| 114 | return static_cast<std::size_t>(std::distance(profiles.begin(), iter)); | 149 | return static_cast<std::size_t>(std::distance(profiles.begin(), iter)); |
| 115 | } | 150 | } |
| 116 | 151 | ||
| 117 | /// Returns a users profile index based on their profile | 152 | /// Returns a users profile index based on their profile |
| 118 | boost::optional<std::size_t> ProfileManager::GetUserIndex(const ProfileInfo& user) const { | 153 | std::optional<std::size_t> ProfileManager::GetUserIndex(const ProfileInfo& user) const { |
| 119 | return GetUserIndex(user.user_uuid); | 154 | return GetUserIndex(user.user_uuid); |
| 120 | } | 155 | } |
| 121 | 156 | ||
| 122 | /// Returns the data structure used by the switch when GetProfileBase is called on acc:* | 157 | /// Returns the data structure used by the switch when GetProfileBase is called on acc:* |
| 123 | bool ProfileManager::GetProfileBase(boost::optional<std::size_t> index, | 158 | bool ProfileManager::GetProfileBase(std::optional<std::size_t> index, ProfileBase& profile) const { |
| 124 | ProfileBase& profile) const { | 159 | if (!index || index >= MAX_USERS) { |
| 125 | if (index == boost::none || index >= MAX_USERS) { | ||
| 126 | return false; | 160 | return false; |
| 127 | } | 161 | } |
| 128 | const auto& prof_info = profiles[index.get()]; | 162 | const auto& prof_info = profiles[*index]; |
| 129 | profile.user_uuid = prof_info.user_uuid; | 163 | profile.user_uuid = prof_info.user_uuid; |
| 130 | profile.username = prof_info.username; | 164 | profile.username = prof_info.username; |
| 131 | profile.timestamp = prof_info.creation_time; | 165 | profile.timestamp = prof_info.creation_time; |
| @@ -134,7 +168,7 @@ bool ProfileManager::GetProfileBase(boost::optional<std::size_t> index, | |||
| 134 | 168 | ||
| 135 | /// Returns the data structure used by the switch when GetProfileBase is called on acc:* | 169 | /// Returns the data structure used by the switch when GetProfileBase is called on acc:* |
| 136 | bool ProfileManager::GetProfileBase(UUID uuid, ProfileBase& profile) const { | 170 | bool ProfileManager::GetProfileBase(UUID uuid, ProfileBase& profile) const { |
| 137 | auto idx = GetUserIndex(uuid); | 171 | const auto idx = GetUserIndex(uuid); |
| 138 | return GetProfileBase(idx, profile); | 172 | return GetProfileBase(idx, profile); |
| 139 | } | 173 | } |
| 140 | 174 | ||
| @@ -161,26 +195,34 @@ std::size_t ProfileManager::GetOpenUserCount() const { | |||
| 161 | 195 | ||
| 162 | /// Checks if a user id exists in our profile manager | 196 | /// Checks if a user id exists in our profile manager |
| 163 | bool ProfileManager::UserExists(UUID uuid) const { | 197 | bool ProfileManager::UserExists(UUID uuid) const { |
| 164 | return (GetUserIndex(uuid) != boost::none); | 198 | return GetUserIndex(uuid) != std::nullopt; |
| 199 | } | ||
| 200 | |||
| 201 | bool ProfileManager::UserExistsIndex(std::size_t index) const { | ||
| 202 | if (index >= MAX_USERS) | ||
| 203 | return false; | ||
| 204 | return profiles[index].user_uuid.uuid != INVALID_UUID; | ||
| 165 | } | 205 | } |
| 166 | 206 | ||
| 167 | /// Opens a specific user | 207 | /// Opens a specific user |
| 168 | void ProfileManager::OpenUser(UUID uuid) { | 208 | void ProfileManager::OpenUser(UUID uuid) { |
| 169 | auto idx = GetUserIndex(uuid); | 209 | const auto idx = GetUserIndex(uuid); |
| 170 | if (idx == boost::none) { | 210 | if (!idx) { |
| 171 | return; | 211 | return; |
| 172 | } | 212 | } |
| 173 | profiles[idx.get()].is_open = true; | 213 | |
| 214 | profiles[*idx].is_open = true; | ||
| 174 | last_opened_user = uuid; | 215 | last_opened_user = uuid; |
| 175 | } | 216 | } |
| 176 | 217 | ||
| 177 | /// Closes a specific user | 218 | /// Closes a specific user |
| 178 | void ProfileManager::CloseUser(UUID uuid) { | 219 | void ProfileManager::CloseUser(UUID uuid) { |
| 179 | auto idx = GetUserIndex(uuid); | 220 | const auto idx = GetUserIndex(uuid); |
| 180 | if (idx == boost::none) { | 221 | if (!idx) { |
| 181 | return; | 222 | return; |
| 182 | } | 223 | } |
| 183 | profiles[idx.get()].is_open = false; | 224 | |
| 225 | profiles[*idx].is_open = false; | ||
| 184 | } | 226 | } |
| 185 | 227 | ||
| 186 | /// Gets all valid user ids on the system | 228 | /// Gets all valid user ids on the system |
| @@ -210,10 +252,10 @@ UUID ProfileManager::GetLastOpenedUser() const { | |||
| 210 | } | 252 | } |
| 211 | 253 | ||
| 212 | /// Return the users profile base and the unknown arbitary data. | 254 | /// Return the users profile base and the unknown arbitary data. |
| 213 | bool ProfileManager::GetProfileBaseAndData(boost::optional<std::size_t> index, ProfileBase& profile, | 255 | bool ProfileManager::GetProfileBaseAndData(std::optional<std::size_t> index, ProfileBase& profile, |
| 214 | ProfileData& data) const { | 256 | ProfileData& data) const { |
| 215 | if (GetProfileBase(index, profile)) { | 257 | if (GetProfileBase(index, profile)) { |
| 216 | data = profiles[index.get()].data; | 258 | data = profiles[*index].data; |
| 217 | return true; | 259 | return true; |
| 218 | } | 260 | } |
| 219 | return false; | 261 | return false; |
| @@ -222,7 +264,7 @@ bool ProfileManager::GetProfileBaseAndData(boost::optional<std::size_t> index, P | |||
| 222 | /// Return the users profile base and the unknown arbitary data. | 264 | /// Return the users profile base and the unknown arbitary data. |
| 223 | bool ProfileManager::GetProfileBaseAndData(UUID uuid, ProfileBase& profile, | 265 | bool ProfileManager::GetProfileBaseAndData(UUID uuid, ProfileBase& profile, |
| 224 | ProfileData& data) const { | 266 | ProfileData& data) const { |
| 225 | auto idx = GetUserIndex(uuid); | 267 | const auto idx = GetUserIndex(uuid); |
| 226 | return GetProfileBaseAndData(idx, profile, data); | 268 | return GetProfileBaseAndData(idx, profile, data); |
| 227 | } | 269 | } |
| 228 | 270 | ||
| @@ -239,4 +281,96 @@ bool ProfileManager::CanSystemRegisterUser() const { | |||
| 239 | // emulate qlaunch. Update this to dynamically change. | 281 | // emulate qlaunch. Update this to dynamically change. |
| 240 | } | 282 | } |
| 241 | 283 | ||
| 284 | bool ProfileManager::RemoveUser(UUID uuid) { | ||
| 285 | const auto index = GetUserIndex(uuid); | ||
| 286 | if (!index) { | ||
| 287 | return false; | ||
| 288 | } | ||
| 289 | |||
| 290 | profiles[*index] = ProfileInfo{}; | ||
| 291 | std::stable_partition(profiles.begin(), profiles.end(), | ||
| 292 | [](const ProfileInfo& profile) { return profile.user_uuid; }); | ||
| 293 | return true; | ||
| 294 | } | ||
| 295 | |||
| 296 | bool ProfileManager::SetProfileBase(UUID uuid, const ProfileBase& profile_new) { | ||
| 297 | const auto index = GetUserIndex(uuid); | ||
| 298 | if (!index || profile_new.user_uuid == UUID(INVALID_UUID)) { | ||
| 299 | return false; | ||
| 300 | } | ||
| 301 | |||
| 302 | auto& profile = profiles[*index]; | ||
| 303 | profile.user_uuid = profile_new.user_uuid; | ||
| 304 | profile.username = profile_new.username; | ||
| 305 | profile.creation_time = profile_new.timestamp; | ||
| 306 | |||
| 307 | return true; | ||
| 308 | } | ||
| 309 | |||
| 310 | void ProfileManager::ParseUserSaveFile() { | ||
| 311 | FileUtil::IOFile save(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + | ||
| 312 | ACC_SAVE_AVATORS_BASE_PATH + "profiles.dat", | ||
| 313 | "rb"); | ||
| 314 | |||
| 315 | if (!save.IsOpen()) { | ||
| 316 | LOG_WARNING(Service_ACC, "Failed to load profile data from save data... Generating new " | ||
| 317 | "user 'yuzu' with random UUID."); | ||
| 318 | return; | ||
| 319 | } | ||
| 320 | |||
| 321 | ProfileDataRaw data; | ||
| 322 | if (save.ReadBytes(&data, sizeof(ProfileDataRaw)) != sizeof(ProfileDataRaw)) { | ||
| 323 | LOG_WARNING(Service_ACC, "profiles.dat is smaller than expected... Generating new user " | ||
| 324 | "'yuzu' with random UUID."); | ||
| 325 | return; | ||
| 326 | } | ||
| 327 | |||
| 328 | for (std::size_t i = 0; i < MAX_USERS; ++i) { | ||
| 329 | const auto& user = data.users[i]; | ||
| 330 | |||
| 331 | if (user.uuid != UUID(INVALID_UUID)) | ||
| 332 | AddUser({user.uuid, user.username, user.timestamp, {}, false}); | ||
| 333 | } | ||
| 334 | |||
| 335 | std::stable_partition(profiles.begin(), profiles.end(), | ||
| 336 | [](const ProfileInfo& profile) { return profile.user_uuid; }); | ||
| 337 | } | ||
| 338 | |||
| 339 | void ProfileManager::WriteUserSaveFile() { | ||
| 340 | ProfileDataRaw raw{}; | ||
| 341 | |||
| 342 | for (std::size_t i = 0; i < MAX_USERS; ++i) { | ||
| 343 | raw.users[i].username = profiles[i].username; | ||
| 344 | raw.users[i].uuid2 = profiles[i].user_uuid; | ||
| 345 | raw.users[i].uuid = profiles[i].user_uuid; | ||
| 346 | raw.users[i].timestamp = profiles[i].creation_time; | ||
| 347 | } | ||
| 348 | |||
| 349 | const auto raw_path = | ||
| 350 | FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "/system/save/8000000000000010"; | ||
| 351 | if (FileUtil::Exists(raw_path) && !FileUtil::IsDirectory(raw_path)) | ||
| 352 | FileUtil::Delete(raw_path); | ||
| 353 | |||
| 354 | const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + | ||
| 355 | ACC_SAVE_AVATORS_BASE_PATH + "profiles.dat"; | ||
| 356 | |||
| 357 | if (!FileUtil::CreateFullPath(path)) { | ||
| 358 | LOG_WARNING(Service_ACC, "Failed to create full path of profiles.dat. Create the directory " | ||
| 359 | "nand/system/save/8000000000000010/su/avators to mitigate this " | ||
| 360 | "issue."); | ||
| 361 | return; | ||
| 362 | } | ||
| 363 | |||
| 364 | FileUtil::IOFile save(path, "wb"); | ||
| 365 | |||
| 366 | if (!save.IsOpen()) { | ||
| 367 | LOG_WARNING(Service_ACC, "Failed to write save data to file... No changes to user data " | ||
| 368 | "made in current session will be saved."); | ||
| 369 | return; | ||
| 370 | } | ||
| 371 | |||
| 372 | save.Resize(sizeof(ProfileDataRaw)); | ||
| 373 | save.WriteBytes(&raw, sizeof(ProfileDataRaw)); | ||
| 374 | } | ||
| 375 | |||
| 242 | }; // namespace Service::Account | 376 | }; // namespace Service::Account |
diff --git a/src/core/hle/service/acc/profile_manager.h b/src/core/hle/service/acc/profile_manager.h index bffd4cf4d..1cd2e51b2 100644 --- a/src/core/hle/service/acc/profile_manager.h +++ b/src/core/hle/service/acc/profile_manager.h | |||
| @@ -5,8 +5,8 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <optional> | ||
| 8 | 9 | ||
| 9 | #include "boost/optional.hpp" | ||
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | #include "common/swap.h" | 11 | #include "common/swap.h" |
| 12 | #include "core/hle/result.h" | 12 | #include "core/hle/result.h" |
| @@ -36,7 +36,7 @@ struct UUID { | |||
| 36 | } | 36 | } |
| 37 | 37 | ||
| 38 | // TODO(ogniK): Properly generate uuids based on RFC-4122 | 38 | // TODO(ogniK): Properly generate uuids based on RFC-4122 |
| 39 | const UUID& Generate(); | 39 | static UUID Generate(); |
| 40 | 40 | ||
| 41 | // Set the UUID to {0,0} to be considered an invalid user | 41 | // Set the UUID to {0,0} to be considered an invalid user |
| 42 | void Invalidate() { | 42 | void Invalidate() { |
| @@ -45,6 +45,15 @@ struct UUID { | |||
| 45 | std::string Format() const { | 45 | std::string Format() const { |
| 46 | return fmt::format("0x{:016X}{:016X}", uuid[1], uuid[0]); | 46 | return fmt::format("0x{:016X}{:016X}", uuid[1], uuid[0]); |
| 47 | } | 47 | } |
| 48 | |||
| 49 | std::string FormatSwitch() const { | ||
| 50 | std::array<u8, 16> s{}; | ||
| 51 | std::memcpy(s.data(), uuid.data(), sizeof(u128)); | ||
| 52 | return fmt::format("{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{" | ||
| 53 | ":02x}{:02x}{:02x}{:02x}{:02x}", | ||
| 54 | s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9], s[10], s[11], | ||
| 55 | s[12], s[13], s[14], s[15]); | ||
| 56 | } | ||
| 48 | }; | 57 | }; |
| 49 | static_assert(sizeof(UUID) == 16, "UUID is an invalid size!"); | 58 | static_assert(sizeof(UUID) == 16, "UUID is an invalid size!"); |
| 50 | 59 | ||
| @@ -81,18 +90,19 @@ static_assert(sizeof(ProfileBase) == 0x38, "ProfileBase is an invalid size"); | |||
| 81 | /// objects | 90 | /// objects |
| 82 | class ProfileManager { | 91 | class ProfileManager { |
| 83 | public: | 92 | public: |
| 84 | ProfileManager(); // TODO(ogniK): Load from system save | 93 | ProfileManager(); |
| 85 | ~ProfileManager(); | 94 | ~ProfileManager(); |
| 86 | 95 | ||
| 87 | ResultCode AddUser(const ProfileInfo& user); | 96 | ResultCode AddUser(const ProfileInfo& user); |
| 88 | ResultCode CreateNewUser(UUID uuid, const ProfileUsername& username); | 97 | ResultCode CreateNewUser(UUID uuid, const ProfileUsername& username); |
| 89 | ResultCode CreateNewUser(UUID uuid, const std::string& username); | 98 | ResultCode CreateNewUser(UUID uuid, const std::string& username); |
| 90 | boost::optional<std::size_t> GetUserIndex(const UUID& uuid) const; | 99 | std::optional<UUID> GetUser(std::size_t index) const; |
| 91 | boost::optional<std::size_t> GetUserIndex(const ProfileInfo& user) const; | 100 | std::optional<std::size_t> GetUserIndex(const UUID& uuid) const; |
| 92 | bool GetProfileBase(boost::optional<std::size_t> index, ProfileBase& profile) const; | 101 | std::optional<std::size_t> GetUserIndex(const ProfileInfo& user) const; |
| 102 | bool GetProfileBase(std::optional<std::size_t> index, ProfileBase& profile) const; | ||
| 93 | bool GetProfileBase(UUID uuid, ProfileBase& profile) const; | 103 | bool GetProfileBase(UUID uuid, ProfileBase& profile) const; |
| 94 | bool GetProfileBase(const ProfileInfo& user, ProfileBase& profile) const; | 104 | bool GetProfileBase(const ProfileInfo& user, ProfileBase& profile) const; |
| 95 | bool GetProfileBaseAndData(boost::optional<std::size_t> index, ProfileBase& profile, | 105 | bool GetProfileBaseAndData(std::optional<std::size_t> index, ProfileBase& profile, |
| 96 | ProfileData& data) const; | 106 | ProfileData& data) const; |
| 97 | bool GetProfileBaseAndData(UUID uuid, ProfileBase& profile, ProfileData& data) const; | 107 | bool GetProfileBaseAndData(UUID uuid, ProfileBase& profile, ProfileData& data) const; |
| 98 | bool GetProfileBaseAndData(const ProfileInfo& user, ProfileBase& profile, | 108 | bool GetProfileBaseAndData(const ProfileInfo& user, ProfileBase& profile, |
| @@ -100,6 +110,7 @@ public: | |||
| 100 | std::size_t GetUserCount() const; | 110 | std::size_t GetUserCount() const; |
| 101 | std::size_t GetOpenUserCount() const; | 111 | std::size_t GetOpenUserCount() const; |
| 102 | bool UserExists(UUID uuid) const; | 112 | bool UserExists(UUID uuid) const; |
| 113 | bool UserExistsIndex(std::size_t index) const; | ||
| 103 | void OpenUser(UUID uuid); | 114 | void OpenUser(UUID uuid); |
| 104 | void CloseUser(UUID uuid); | 115 | void CloseUser(UUID uuid); |
| 105 | UserIDArray GetOpenUsers() const; | 116 | UserIDArray GetOpenUsers() const; |
| @@ -108,11 +119,17 @@ public: | |||
| 108 | 119 | ||
| 109 | bool CanSystemRegisterUser() const; | 120 | bool CanSystemRegisterUser() const; |
| 110 | 121 | ||
| 122 | bool RemoveUser(UUID uuid); | ||
| 123 | bool SetProfileBase(UUID uuid, const ProfileBase& profile_new); | ||
| 124 | |||
| 111 | private: | 125 | private: |
| 126 | void ParseUserSaveFile(); | ||
| 127 | void WriteUserSaveFile(); | ||
| 128 | std::optional<std::size_t> AddToProfiles(const ProfileInfo& profile); | ||
| 129 | bool RemoveProfileAtIndex(std::size_t index); | ||
| 130 | |||
| 112 | std::array<ProfileInfo, MAX_USERS> profiles{}; | 131 | std::array<ProfileInfo, MAX_USERS> profiles{}; |
| 113 | std::size_t user_count = 0; | 132 | std::size_t user_count = 0; |
| 114 | boost::optional<std::size_t> AddToProfiles(const ProfileInfo& profile); | ||
| 115 | bool RemoveProfileAtIndex(std::size_t index); | ||
| 116 | UUID last_opened_user{INVALID_UUID}; | 133 | UUID last_opened_user{INVALID_UUID}; |
| 117 | }; | 134 | }; |
| 118 | 135 | ||
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 4d1f83170..59aafd616 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp | |||
| @@ -4,17 +4,20 @@ | |||
| 4 | 4 | ||
| 5 | #include <array> | 5 | #include <array> |
| 6 | #include <cinttypes> | 6 | #include <cinttypes> |
| 7 | #include <cstring> | ||
| 7 | #include <stack> | 8 | #include <stack> |
| 8 | #include "core/core.h" | 9 | #include "core/core.h" |
| 9 | #include "core/hle/ipc_helpers.h" | 10 | #include "core/hle/ipc_helpers.h" |
| 10 | #include "core/hle/kernel/event.h" | 11 | #include "core/hle/kernel/event.h" |
| 11 | #include "core/hle/kernel/process.h" | 12 | #include "core/hle/kernel/process.h" |
| 13 | #include "core/hle/service/acc/profile_manager.h" | ||
| 12 | #include "core/hle/service/am/am.h" | 14 | #include "core/hle/service/am/am.h" |
| 13 | #include "core/hle/service/am/applet_ae.h" | 15 | #include "core/hle/service/am/applet_ae.h" |
| 14 | #include "core/hle/service/am/applet_oe.h" | 16 | #include "core/hle/service/am/applet_oe.h" |
| 15 | #include "core/hle/service/am/idle.h" | 17 | #include "core/hle/service/am/idle.h" |
| 16 | #include "core/hle/service/am/omm.h" | 18 | #include "core/hle/service/am/omm.h" |
| 17 | #include "core/hle/service/am/spsm.h" | 19 | #include "core/hle/service/am/spsm.h" |
| 20 | #include "core/hle/service/am/tcap.h" | ||
| 18 | #include "core/hle/service/apm/apm.h" | 21 | #include "core/hle/service/apm/apm.h" |
| 19 | #include "core/hle/service/filesystem/filesystem.h" | 22 | #include "core/hle/service/filesystem/filesystem.h" |
| 20 | #include "core/hle/service/nvflinger/nvflinger.h" | 23 | #include "core/hle/service/nvflinger/nvflinger.h" |
| @@ -25,14 +28,29 @@ | |||
| 25 | 28 | ||
| 26 | namespace Service::AM { | 29 | namespace Service::AM { |
| 27 | 30 | ||
| 31 | constexpr u32 POP_LAUNCH_PARAMETER_MAGIC = 0xC79497CA; | ||
| 32 | |||
| 33 | struct LaunchParameters { | ||
| 34 | u32_le magic; | ||
| 35 | u32_le is_account_selected; | ||
| 36 | u128 current_user; | ||
| 37 | INSERT_PADDING_BYTES(0x70); | ||
| 38 | }; | ||
| 39 | static_assert(sizeof(LaunchParameters) == 0x88); | ||
| 40 | |||
| 28 | IWindowController::IWindowController() : ServiceFramework("IWindowController") { | 41 | IWindowController::IWindowController() : ServiceFramework("IWindowController") { |
| 42 | // clang-format off | ||
| 29 | static const FunctionInfo functions[] = { | 43 | static const FunctionInfo functions[] = { |
| 30 | {0, nullptr, "CreateWindow"}, | 44 | {0, nullptr, "CreateWindow"}, |
| 31 | {1, &IWindowController::GetAppletResourceUserId, "GetAppletResourceUserId"}, | 45 | {1, &IWindowController::GetAppletResourceUserId, "GetAppletResourceUserId"}, |
| 32 | {10, &IWindowController::AcquireForegroundRights, "AcquireForegroundRights"}, | 46 | {10, &IWindowController::AcquireForegroundRights, "AcquireForegroundRights"}, |
| 33 | {11, nullptr, "ReleaseForegroundRights"}, | 47 | {11, nullptr, "ReleaseForegroundRights"}, |
| 34 | {12, nullptr, "RejectToChangeIntoBackground"}, | 48 | {12, nullptr, "RejectToChangeIntoBackground"}, |
| 49 | {20, nullptr, "SetAppletWindowVisibility"}, | ||
| 50 | {21, nullptr, "SetAppletGpuTimeSlice"}, | ||
| 35 | }; | 51 | }; |
| 52 | // clang-format on | ||
| 53 | |||
| 36 | RegisterHandlers(functions); | 54 | RegisterHandlers(functions); |
| 37 | } | 55 | } |
| 38 | 56 | ||
| @@ -87,6 +105,7 @@ void IAudioController::GetLibraryAppletExpectedMasterVolume(Kernel::HLERequestCo | |||
| 87 | } | 105 | } |
| 88 | 106 | ||
| 89 | IDisplayController::IDisplayController() : ServiceFramework("IDisplayController") { | 107 | IDisplayController::IDisplayController() : ServiceFramework("IDisplayController") { |
| 108 | // clang-format off | ||
| 90 | static const FunctionInfo functions[] = { | 109 | static const FunctionInfo functions[] = { |
| 91 | {0, nullptr, "GetLastForegroundCaptureImage"}, | 110 | {0, nullptr, "GetLastForegroundCaptureImage"}, |
| 92 | {1, nullptr, "UpdateLastForegroundCaptureImage"}, | 111 | {1, nullptr, "UpdateLastForegroundCaptureImage"}, |
| @@ -117,7 +136,11 @@ IDisplayController::IDisplayController() : ServiceFramework("IDisplayController" | |||
| 117 | {25, nullptr, "ReleaseLastForegroundCaptureSharedBuffer"}, | 136 | {25, nullptr, "ReleaseLastForegroundCaptureSharedBuffer"}, |
| 118 | {26, nullptr, "AcquireCallerAppletCaptureSharedBuffer"}, | 137 | {26, nullptr, "AcquireCallerAppletCaptureSharedBuffer"}, |
| 119 | {27, nullptr, "ReleaseCallerAppletCaptureSharedBuffer"}, | 138 | {27, nullptr, "ReleaseCallerAppletCaptureSharedBuffer"}, |
| 139 | // 6.0.0+ | ||
| 140 | {28, nullptr, "TakeScreenShotOfOwnLayerEx"}, | ||
| 120 | }; | 141 | }; |
| 142 | // clang-format on | ||
| 143 | |||
| 121 | RegisterHandlers(functions); | 144 | RegisterHandlers(functions); |
| 122 | } | 145 | } |
| 123 | 146 | ||
| @@ -128,6 +151,7 @@ IDebugFunctions::~IDebugFunctions() = default; | |||
| 128 | 151 | ||
| 129 | ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) | 152 | ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) |
| 130 | : ServiceFramework("ISelfController"), nvflinger(std::move(nvflinger)) { | 153 | : ServiceFramework("ISelfController"), nvflinger(std::move(nvflinger)) { |
| 154 | // clang-format off | ||
| 131 | static const FunctionInfo functions[] = { | 155 | static const FunctionInfo functions[] = { |
| 132 | {0, nullptr, "Exit"}, | 156 | {0, nullptr, "Exit"}, |
| 133 | {1, &ISelfController::LockExit, "LockExit"}, | 157 | {1, &ISelfController::LockExit, "LockExit"}, |
| @@ -136,10 +160,8 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger | |||
| 136 | {4, nullptr, "LeaveFatalSection"}, | 160 | {4, nullptr, "LeaveFatalSection"}, |
| 137 | {9, &ISelfController::GetLibraryAppletLaunchableEvent, "GetLibraryAppletLaunchableEvent"}, | 161 | {9, &ISelfController::GetLibraryAppletLaunchableEvent, "GetLibraryAppletLaunchableEvent"}, |
| 138 | {10, &ISelfController::SetScreenShotPermission, "SetScreenShotPermission"}, | 162 | {10, &ISelfController::SetScreenShotPermission, "SetScreenShotPermission"}, |
| 139 | {11, &ISelfController::SetOperationModeChangedNotification, | 163 | {11, &ISelfController::SetOperationModeChangedNotification, "SetOperationModeChangedNotification"}, |
| 140 | "SetOperationModeChangedNotification"}, | 164 | {12, &ISelfController::SetPerformanceModeChangedNotification, "SetPerformanceModeChangedNotification"}, |
| 141 | {12, &ISelfController::SetPerformanceModeChangedNotification, | ||
| 142 | "SetPerformanceModeChangedNotification"}, | ||
| 143 | {13, &ISelfController::SetFocusHandlingMode, "SetFocusHandlingMode"}, | 165 | {13, &ISelfController::SetFocusHandlingMode, "SetFocusHandlingMode"}, |
| 144 | {14, &ISelfController::SetRestartMessageEnabled, "SetRestartMessageEnabled"}, | 166 | {14, &ISelfController::SetRestartMessageEnabled, "SetRestartMessageEnabled"}, |
| 145 | {15, nullptr, "SetScreenShotAppletIdentityInfo"}, | 167 | {15, nullptr, "SetScreenShotAppletIdentityInfo"}, |
| @@ -165,7 +187,12 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger | |||
| 165 | {69, nullptr, "IsAutoSleepDisabled"}, | 187 | {69, nullptr, "IsAutoSleepDisabled"}, |
| 166 | {70, nullptr, "ReportMultimediaError"}, | 188 | {70, nullptr, "ReportMultimediaError"}, |
| 167 | {80, nullptr, "SetWirelessPriorityMode"}, | 189 | {80, nullptr, "SetWirelessPriorityMode"}, |
| 190 | {90, nullptr, "GetAccumulatedSuspendedTickValue"}, | ||
| 191 | {91, nullptr, "GetAccumulatedSuspendedTickChangedEvent"}, | ||
| 192 | {1000, nullptr, "GetDebugStorageChannel"}, | ||
| 168 | }; | 193 | }; |
| 194 | // clang-format on | ||
| 195 | |||
| 169 | RegisterHandlers(functions); | 196 | RegisterHandlers(functions); |
| 170 | 197 | ||
| 171 | auto& kernel = Core::System::GetInstance().Kernel(); | 198 | auto& kernel = Core::System::GetInstance().Kernel(); |
| @@ -312,6 +339,7 @@ void ISelfController::GetIdleTimeDetectionExtension(Kernel::HLERequestContext& c | |||
| 312 | } | 339 | } |
| 313 | 340 | ||
| 314 | ICommonStateGetter::ICommonStateGetter() : ServiceFramework("ICommonStateGetter") { | 341 | ICommonStateGetter::ICommonStateGetter() : ServiceFramework("ICommonStateGetter") { |
| 342 | // clang-format off | ||
| 315 | static const FunctionInfo functions[] = { | 343 | static const FunctionInfo functions[] = { |
| 316 | {0, &ICommonStateGetter::GetEventHandle, "GetEventHandle"}, | 344 | {0, &ICommonStateGetter::GetEventHandle, "GetEventHandle"}, |
| 317 | {1, &ICommonStateGetter::ReceiveMessage, "ReceiveMessage"}, | 345 | {1, &ICommonStateGetter::ReceiveMessage, "ReceiveMessage"}, |
| @@ -336,11 +364,12 @@ ICommonStateGetter::ICommonStateGetter() : ServiceFramework("ICommonStateGetter" | |||
| 336 | {52, nullptr, "SwitchLcdBacklight"}, | 364 | {52, nullptr, "SwitchLcdBacklight"}, |
| 337 | {55, nullptr, "IsInControllerFirmwareUpdateSection"}, | 365 | {55, nullptr, "IsInControllerFirmwareUpdateSection"}, |
| 338 | {60, &ICommonStateGetter::GetDefaultDisplayResolution, "GetDefaultDisplayResolution"}, | 366 | {60, &ICommonStateGetter::GetDefaultDisplayResolution, "GetDefaultDisplayResolution"}, |
| 339 | {61, &ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent, | 367 | {61, &ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent, "GetDefaultDisplayResolutionChangeEvent"}, |
| 340 | "GetDefaultDisplayResolutionChangeEvent"}, | ||
| 341 | {62, nullptr, "GetHdcpAuthenticationState"}, | 368 | {62, nullptr, "GetHdcpAuthenticationState"}, |
| 342 | {63, nullptr, "GetHdcpAuthenticationStateChangeEvent"}, | 369 | {63, nullptr, "GetHdcpAuthenticationStateChangeEvent"}, |
| 343 | }; | 370 | }; |
| 371 | // clang-format on | ||
| 372 | |||
| 344 | RegisterHandlers(functions); | 373 | RegisterHandlers(functions); |
| 345 | 374 | ||
| 346 | auto& kernel = Core::System::GetInstance().Kernel(); | 375 | auto& kernel = Core::System::GetInstance().Kernel(); |
| @@ -432,11 +461,14 @@ class IStorageAccessor final : public ServiceFramework<IStorageAccessor> { | |||
| 432 | public: | 461 | public: |
| 433 | explicit IStorageAccessor(std::vector<u8> buffer) | 462 | explicit IStorageAccessor(std::vector<u8> buffer) |
| 434 | : ServiceFramework("IStorageAccessor"), buffer(std::move(buffer)) { | 463 | : ServiceFramework("IStorageAccessor"), buffer(std::move(buffer)) { |
| 464 | // clang-format off | ||
| 435 | static const FunctionInfo functions[] = { | 465 | static const FunctionInfo functions[] = { |
| 436 | {0, &IStorageAccessor::GetSize, "GetSize"}, | 466 | {0, &IStorageAccessor::GetSize, "GetSize"}, |
| 437 | {10, &IStorageAccessor::Write, "Write"}, | 467 | {10, &IStorageAccessor::Write, "Write"}, |
| 438 | {11, &IStorageAccessor::Read, "Read"}, | 468 | {11, &IStorageAccessor::Read, "Read"}, |
| 439 | }; | 469 | }; |
| 470 | // clang-format on | ||
| 471 | |||
| 440 | RegisterHandlers(functions); | 472 | RegisterHandlers(functions); |
| 441 | } | 473 | } |
| 442 | 474 | ||
| @@ -489,10 +521,13 @@ class IStorage final : public ServiceFramework<IStorage> { | |||
| 489 | public: | 521 | public: |
| 490 | explicit IStorage(std::vector<u8> buffer) | 522 | explicit IStorage(std::vector<u8> buffer) |
| 491 | : ServiceFramework("IStorage"), buffer(std::move(buffer)) { | 523 | : ServiceFramework("IStorage"), buffer(std::move(buffer)) { |
| 524 | // clang-format off | ||
| 492 | static const FunctionInfo functions[] = { | 525 | static const FunctionInfo functions[] = { |
| 493 | {0, &IStorage::Open, "Open"}, | 526 | {0, &IStorage::Open, "Open"}, |
| 494 | {1, nullptr, "OpenTransferStorage"}, | 527 | {1, nullptr, "OpenTransferStorage"}, |
| 495 | }; | 528 | }; |
| 529 | // clang-format on | ||
| 530 | |||
| 496 | RegisterHandlers(functions); | 531 | RegisterHandlers(functions); |
| 497 | } | 532 | } |
| 498 | 533 | ||
| @@ -512,6 +547,7 @@ private: | |||
| 512 | class ILibraryAppletAccessor final : public ServiceFramework<ILibraryAppletAccessor> { | 547 | class ILibraryAppletAccessor final : public ServiceFramework<ILibraryAppletAccessor> { |
| 513 | public: | 548 | public: |
| 514 | explicit ILibraryAppletAccessor() : ServiceFramework("ILibraryAppletAccessor") { | 549 | explicit ILibraryAppletAccessor() : ServiceFramework("ILibraryAppletAccessor") { |
| 550 | // clang-format off | ||
| 515 | static const FunctionInfo functions[] = { | 551 | static const FunctionInfo functions[] = { |
| 516 | {0, &ILibraryAppletAccessor::GetAppletStateChangedEvent, "GetAppletStateChangedEvent"}, | 552 | {0, &ILibraryAppletAccessor::GetAppletStateChangedEvent, "GetAppletStateChangedEvent"}, |
| 517 | {1, nullptr, "IsCompleted"}, | 553 | {1, nullptr, "IsCompleted"}, |
| @@ -532,6 +568,8 @@ public: | |||
| 532 | {150, nullptr, "RequestForAppletToGetForeground"}, | 568 | {150, nullptr, "RequestForAppletToGetForeground"}, |
| 533 | {160, nullptr, "GetIndirectLayerConsumerHandle"}, | 569 | {160, nullptr, "GetIndirectLayerConsumerHandle"}, |
| 534 | }; | 570 | }; |
| 571 | // clang-format on | ||
| 572 | |||
| 535 | RegisterHandlers(functions); | 573 | RegisterHandlers(functions); |
| 536 | 574 | ||
| 537 | auto& kernel = Core::System::GetInstance().Kernel(); | 575 | auto& kernel = Core::System::GetInstance().Kernel(); |
| @@ -624,13 +662,13 @@ void ILibraryAppletCreator::CreateStorage(Kernel::HLERequestContext& ctx) { | |||
| 624 | } | 662 | } |
| 625 | 663 | ||
| 626 | IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationFunctions") { | 664 | IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationFunctions") { |
| 665 | // clang-format off | ||
| 627 | static const FunctionInfo functions[] = { | 666 | static const FunctionInfo functions[] = { |
| 628 | {1, &IApplicationFunctions::PopLaunchParameter, "PopLaunchParameter"}, | 667 | {1, &IApplicationFunctions::PopLaunchParameter, "PopLaunchParameter"}, |
| 629 | {10, nullptr, "CreateApplicationAndPushAndRequestToStart"}, | 668 | {10, nullptr, "CreateApplicationAndPushAndRequestToStart"}, |
| 630 | {11, nullptr, "CreateApplicationAndPushAndRequestToStartForQuest"}, | 669 | {11, nullptr, "CreateApplicationAndPushAndRequestToStartForQuest"}, |
| 631 | {12, nullptr, "CreateApplicationAndRequestToStart"}, | 670 | {12, nullptr, "CreateApplicationAndRequestToStart"}, |
| 632 | {13, &IApplicationFunctions::CreateApplicationAndRequestToStartForQuest, | 671 | {13, &IApplicationFunctions::CreateApplicationAndRequestToStartForQuest, "CreateApplicationAndRequestToStartForQuest"}, |
| 633 | "CreateApplicationAndRequestToStartForQuest"}, | ||
| 634 | {20, &IApplicationFunctions::EnsureSaveData, "EnsureSaveData"}, | 672 | {20, &IApplicationFunctions::EnsureSaveData, "EnsureSaveData"}, |
| 635 | {21, &IApplicationFunctions::GetDesiredLanguage, "GetDesiredLanguage"}, | 673 | {21, &IApplicationFunctions::GetDesiredLanguage, "GetDesiredLanguage"}, |
| 636 | {22, &IApplicationFunctions::SetTerminateResult, "SetTerminateResult"}, | 674 | {22, &IApplicationFunctions::SetTerminateResult, "SetTerminateResult"}, |
| @@ -638,10 +676,8 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF | |||
| 638 | {24, nullptr, "GetLaunchStorageInfoForDebug"}, | 676 | {24, nullptr, "GetLaunchStorageInfoForDebug"}, |
| 639 | {25, nullptr, "ExtendSaveData"}, | 677 | {25, nullptr, "ExtendSaveData"}, |
| 640 | {26, nullptr, "GetSaveDataSize"}, | 678 | {26, nullptr, "GetSaveDataSize"}, |
| 641 | {30, &IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed, | 679 | {30, &IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed, "BeginBlockingHomeButtonShortAndLongPressed"}, |
| 642 | "BeginBlockingHomeButtonShortAndLongPressed"}, | 680 | {31, &IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed, "EndBlockingHomeButtonShortAndLongPressed"}, |
| 643 | {31, &IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed, | ||
| 644 | "EndBlockingHomeButtonShortAndLongPressed"}, | ||
| 645 | {32, &IApplicationFunctions::BeginBlockingHomeButton, "BeginBlockingHomeButton"}, | 681 | {32, &IApplicationFunctions::BeginBlockingHomeButton, "BeginBlockingHomeButton"}, |
| 646 | {33, &IApplicationFunctions::EndBlockingHomeButton, "EndBlockingHomeButton"}, | 682 | {33, &IApplicationFunctions::EndBlockingHomeButton, "EndBlockingHomeButton"}, |
| 647 | {40, &IApplicationFunctions::NotifyRunning, "NotifyRunning"}, | 683 | {40, &IApplicationFunctions::NotifyRunning, "NotifyRunning"}, |
| @@ -666,6 +702,8 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF | |||
| 666 | {1000, nullptr, "CreateMovieMaker"}, | 702 | {1000, nullptr, "CreateMovieMaker"}, |
| 667 | {1001, nullptr, "PrepareForJit"}, | 703 | {1001, nullptr, "PrepareForJit"}, |
| 668 | }; | 704 | }; |
| 705 | // clang-format on | ||
| 706 | |||
| 669 | RegisterHandlers(functions); | 707 | RegisterHandlers(functions); |
| 670 | } | 708 | } |
| 671 | 709 | ||
| @@ -698,20 +736,23 @@ void IApplicationFunctions::EndBlockingHomeButton(Kernel::HLERequestContext& ctx | |||
| 698 | } | 736 | } |
| 699 | 737 | ||
| 700 | void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) { | 738 | void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) { |
| 701 | constexpr std::array<u8, 0x88> data{{ | 739 | LaunchParameters params{}; |
| 702 | 0xca, 0x97, 0x94, 0xc7, // Magic | 740 | |
| 703 | 1, 0, 0, 0, // IsAccountSelected (bool) | 741 | params.magic = POP_LAUNCH_PARAMETER_MAGIC; |
| 704 | 1, 0, 0, 0, // User Id (word 0) | 742 | params.is_account_selected = 1; |
| 705 | 0, 0, 0, 0, // User Id (word 1) | ||
| 706 | 0, 0, 0, 0, // User Id (word 2) | ||
| 707 | 0, 0, 0, 0 // User Id (word 3) | ||
| 708 | }}; | ||
| 709 | 743 | ||
| 710 | std::vector<u8> buffer(data.begin(), data.end()); | 744 | Account::ProfileManager profile_manager{}; |
| 745 | const auto uuid = profile_manager.GetUser(Settings::values.current_user); | ||
| 746 | ASSERT(uuid != std::nullopt); | ||
| 747 | params.current_user = uuid->uuid; | ||
| 711 | 748 | ||
| 712 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 749 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 713 | 750 | ||
| 714 | rb.Push(RESULT_SUCCESS); | 751 | rb.Push(RESULT_SUCCESS); |
| 752 | |||
| 753 | std::vector<u8> buffer(sizeof(LaunchParameters)); | ||
| 754 | std::memcpy(buffer.data(), ¶ms, buffer.size()); | ||
| 755 | |||
| 715 | rb.PushIpcInterface<AM::IStorage>(buffer); | 756 | rb.PushIpcInterface<AM::IStorage>(buffer); |
| 716 | 757 | ||
| 717 | LOG_DEBUG(Service_AM, "called"); | 758 | LOG_DEBUG(Service_AM, "called"); |
| @@ -804,9 +845,11 @@ void InstallInterfaces(SM::ServiceManager& service_manager, | |||
| 804 | std::make_shared<IdleSys>()->InstallAsService(service_manager); | 845 | std::make_shared<IdleSys>()->InstallAsService(service_manager); |
| 805 | std::make_shared<OMM>()->InstallAsService(service_manager); | 846 | std::make_shared<OMM>()->InstallAsService(service_manager); |
| 806 | std::make_shared<SPSM>()->InstallAsService(service_manager); | 847 | std::make_shared<SPSM>()->InstallAsService(service_manager); |
| 848 | std::make_shared<TCAP>()->InstallAsService(service_manager); | ||
| 807 | } | 849 | } |
| 808 | 850 | ||
| 809 | IHomeMenuFunctions::IHomeMenuFunctions() : ServiceFramework("IHomeMenuFunctions") { | 851 | IHomeMenuFunctions::IHomeMenuFunctions() : ServiceFramework("IHomeMenuFunctions") { |
| 852 | // clang-format off | ||
| 810 | static const FunctionInfo functions[] = { | 853 | static const FunctionInfo functions[] = { |
| 811 | {10, &IHomeMenuFunctions::RequestToGetForeground, "RequestToGetForeground"}, | 854 | {10, &IHomeMenuFunctions::RequestToGetForeground, "RequestToGetForeground"}, |
| 812 | {11, nullptr, "LockForeground"}, | 855 | {11, nullptr, "LockForeground"}, |
| @@ -815,7 +858,10 @@ IHomeMenuFunctions::IHomeMenuFunctions() : ServiceFramework("IHomeMenuFunctions" | |||
| 815 | {21, nullptr, "GetPopFromGeneralChannelEvent"}, | 858 | {21, nullptr, "GetPopFromGeneralChannelEvent"}, |
| 816 | {30, nullptr, "GetHomeButtonWriterLockAccessor"}, | 859 | {30, nullptr, "GetHomeButtonWriterLockAccessor"}, |
| 817 | {31, nullptr, "GetWriterLockAccessorEx"}, | 860 | {31, nullptr, "GetWriterLockAccessorEx"}, |
| 861 | {100, nullptr, "PopRequestLaunchApplicationForDebug"}, | ||
| 818 | }; | 862 | }; |
| 863 | // clang-format on | ||
| 864 | |||
| 819 | RegisterHandlers(functions); | 865 | RegisterHandlers(functions); |
| 820 | } | 866 | } |
| 821 | 867 | ||
| @@ -828,6 +874,7 @@ void IHomeMenuFunctions::RequestToGetForeground(Kernel::HLERequestContext& ctx) | |||
| 828 | } | 874 | } |
| 829 | 875 | ||
| 830 | IGlobalStateController::IGlobalStateController() : ServiceFramework("IGlobalStateController") { | 876 | IGlobalStateController::IGlobalStateController() : ServiceFramework("IGlobalStateController") { |
| 877 | // clang-format off | ||
| 831 | static const FunctionInfo functions[] = { | 878 | static const FunctionInfo functions[] = { |
| 832 | {0, nullptr, "RequestToEnterSleep"}, | 879 | {0, nullptr, "RequestToEnterSleep"}, |
| 833 | {1, nullptr, "EnterSleep"}, | 880 | {1, nullptr, "EnterSleep"}, |
| @@ -841,18 +888,23 @@ IGlobalStateController::IGlobalStateController() : ServiceFramework("IGlobalStat | |||
| 841 | {14, nullptr, "ShouldSleepOnBoot"}, | 888 | {14, nullptr, "ShouldSleepOnBoot"}, |
| 842 | {15, nullptr, "GetHdcpAuthenticationFailedEvent"}, | 889 | {15, nullptr, "GetHdcpAuthenticationFailedEvent"}, |
| 843 | }; | 890 | }; |
| 891 | // clang-format on | ||
| 892 | |||
| 844 | RegisterHandlers(functions); | 893 | RegisterHandlers(functions); |
| 845 | } | 894 | } |
| 846 | 895 | ||
| 847 | IGlobalStateController::~IGlobalStateController() = default; | 896 | IGlobalStateController::~IGlobalStateController() = default; |
| 848 | 897 | ||
| 849 | IApplicationCreator::IApplicationCreator() : ServiceFramework("IApplicationCreator") { | 898 | IApplicationCreator::IApplicationCreator() : ServiceFramework("IApplicationCreator") { |
| 899 | // clang-format off | ||
| 850 | static const FunctionInfo functions[] = { | 900 | static const FunctionInfo functions[] = { |
| 851 | {0, nullptr, "CreateApplication"}, | 901 | {0, nullptr, "CreateApplication"}, |
| 852 | {1, nullptr, "PopLaunchRequestedApplication"}, | 902 | {1, nullptr, "PopLaunchRequestedApplication"}, |
| 853 | {10, nullptr, "CreateSystemApplication"}, | 903 | {10, nullptr, "CreateSystemApplication"}, |
| 854 | {100, nullptr, "PopFloatingApplicationForDevelopment"}, | 904 | {100, nullptr, "PopFloatingApplicationForDevelopment"}, |
| 855 | }; | 905 | }; |
| 906 | // clang-format on | ||
| 907 | |||
| 856 | RegisterHandlers(functions); | 908 | RegisterHandlers(functions); |
| 857 | } | 909 | } |
| 858 | 910 | ||
| @@ -860,6 +912,7 @@ IApplicationCreator::~IApplicationCreator() = default; | |||
| 860 | 912 | ||
| 861 | IProcessWindingController::IProcessWindingController() | 913 | IProcessWindingController::IProcessWindingController() |
| 862 | : ServiceFramework("IProcessWindingController") { | 914 | : ServiceFramework("IProcessWindingController") { |
| 915 | // clang-format off | ||
| 863 | static const FunctionInfo functions[] = { | 916 | static const FunctionInfo functions[] = { |
| 864 | {0, nullptr, "GetLaunchReason"}, | 917 | {0, nullptr, "GetLaunchReason"}, |
| 865 | {11, nullptr, "OpenCallingLibraryApplet"}, | 918 | {11, nullptr, "OpenCallingLibraryApplet"}, |
| @@ -870,6 +923,8 @@ IProcessWindingController::IProcessWindingController() | |||
| 870 | {40, nullptr, "ReserveToStartAndWaitAndUnwindThis"}, | 923 | {40, nullptr, "ReserveToStartAndWaitAndUnwindThis"}, |
| 871 | {41, nullptr, "ReserveToStartAndWait"}, | 924 | {41, nullptr, "ReserveToStartAndWait"}, |
| 872 | }; | 925 | }; |
| 926 | // clang-format on | ||
| 927 | |||
| 873 | RegisterHandlers(functions); | 928 | RegisterHandlers(functions); |
| 874 | } | 929 | } |
| 875 | 930 | ||
diff --git a/src/core/hle/service/am/applet_ae.cpp b/src/core/hle/service/am/applet_ae.cpp index 4296c255e..68ea778e8 100644 --- a/src/core/hle/service/am/applet_ae.cpp +++ b/src/core/hle/service/am/applet_ae.cpp | |||
| @@ -211,6 +211,7 @@ void AppletAE::OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx) { | |||
| 211 | 211 | ||
| 212 | AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) | 212 | AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) |
| 213 | : ServiceFramework("appletAE"), nvflinger(std::move(nvflinger)) { | 213 | : ServiceFramework("appletAE"), nvflinger(std::move(nvflinger)) { |
| 214 | // clang-format off | ||
| 214 | static const FunctionInfo functions[] = { | 215 | static const FunctionInfo functions[] = { |
| 215 | {100, &AppletAE::OpenSystemAppletProxy, "OpenSystemAppletProxy"}, | 216 | {100, &AppletAE::OpenSystemAppletProxy, "OpenSystemAppletProxy"}, |
| 216 | {200, &AppletAE::OpenLibraryAppletProxyOld, "OpenLibraryAppletProxyOld"}, | 217 | {200, &AppletAE::OpenLibraryAppletProxyOld, "OpenLibraryAppletProxyOld"}, |
| @@ -218,7 +219,10 @@ AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) | |||
| 218 | {300, nullptr, "OpenOverlayAppletProxy"}, | 219 | {300, nullptr, "OpenOverlayAppletProxy"}, |
| 219 | {350, nullptr, "OpenSystemApplicationProxy"}, | 220 | {350, nullptr, "OpenSystemApplicationProxy"}, |
| 220 | {400, nullptr, "CreateSelfLibraryAppletCreatorForDevelop"}, | 221 | {400, nullptr, "CreateSelfLibraryAppletCreatorForDevelop"}, |
| 222 | {401, nullptr, "GetSystemAppletControllerForDebug"}, | ||
| 221 | }; | 223 | }; |
| 224 | // clang-format on | ||
| 225 | |||
| 222 | RegisterHandlers(functions); | 226 | RegisterHandlers(functions); |
| 223 | } | 227 | } |
| 224 | 228 | ||
diff --git a/src/core/hle/service/am/applet_oe.cpp b/src/core/hle/service/am/applet_oe.cpp index e45cf6e20..60717afd9 100644 --- a/src/core/hle/service/am/applet_oe.cpp +++ b/src/core/hle/service/am/applet_oe.cpp | |||
| @@ -14,6 +14,7 @@ class IApplicationProxy final : public ServiceFramework<IApplicationProxy> { | |||
| 14 | public: | 14 | public: |
| 15 | explicit IApplicationProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) | 15 | explicit IApplicationProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) |
| 16 | : ServiceFramework("IApplicationProxy"), nvflinger(std::move(nvflinger)) { | 16 | : ServiceFramework("IApplicationProxy"), nvflinger(std::move(nvflinger)) { |
| 17 | // clang-format off | ||
| 17 | static const FunctionInfo functions[] = { | 18 | static const FunctionInfo functions[] = { |
| 18 | {0, &IApplicationProxy::GetCommonStateGetter, "GetCommonStateGetter"}, | 19 | {0, &IApplicationProxy::GetCommonStateGetter, "GetCommonStateGetter"}, |
| 19 | {1, &IApplicationProxy::GetSelfController, "GetSelfController"}, | 20 | {1, &IApplicationProxy::GetSelfController, "GetSelfController"}, |
| @@ -25,6 +26,8 @@ public: | |||
| 25 | {20, &IApplicationProxy::GetApplicationFunctions, "GetApplicationFunctions"}, | 26 | {20, &IApplicationProxy::GetApplicationFunctions, "GetApplicationFunctions"}, |
| 26 | {1000, &IApplicationProxy::GetDebugFunctions, "GetDebugFunctions"}, | 27 | {1000, &IApplicationProxy::GetDebugFunctions, "GetDebugFunctions"}, |
| 27 | }; | 28 | }; |
| 29 | // clang-format on | ||
| 30 | |||
| 28 | RegisterHandlers(functions); | 31 | RegisterHandlers(functions); |
| 29 | } | 32 | } |
| 30 | 33 | ||
diff --git a/src/core/hle/service/am/idle.cpp b/src/core/hle/service/am/idle.cpp index 0e3088bc8..f814fe2c0 100644 --- a/src/core/hle/service/am/idle.cpp +++ b/src/core/hle/service/am/idle.cpp | |||
| @@ -12,9 +12,9 @@ IdleSys::IdleSys() : ServiceFramework{"idle:sys"} { | |||
| 12 | {0, nullptr, "GetAutoPowerDownEvent"}, | 12 | {0, nullptr, "GetAutoPowerDownEvent"}, |
| 13 | {1, nullptr, "Unknown1"}, | 13 | {1, nullptr, "Unknown1"}, |
| 14 | {2, nullptr, "Unknown2"}, | 14 | {2, nullptr, "Unknown2"}, |
| 15 | {3, nullptr, "Unknown3"}, | 15 | {3, nullptr, "SetHandlingContext"}, |
| 16 | {4, nullptr, "Unknown4"}, | 16 | {4, nullptr, "LoadAndApplySettings"}, |
| 17 | {5, nullptr, "Unknown5"}, | 17 | {5, nullptr, "ReportUserIsActive"}, |
| 18 | }; | 18 | }; |
| 19 | // clang-format on | 19 | // clang-format on |
| 20 | 20 | ||
diff --git a/src/core/hle/service/am/omm.cpp b/src/core/hle/service/am/omm.cpp index 1c37f849f..6ab3fb906 100644 --- a/src/core/hle/service/am/omm.cpp +++ b/src/core/hle/service/am/omm.cpp | |||
| @@ -17,22 +17,24 @@ OMM::OMM() : ServiceFramework{"omm"} { | |||
| 17 | {5, nullptr, "GetCradleStatus"}, | 17 | {5, nullptr, "GetCradleStatus"}, |
| 18 | {6, nullptr, "FadeInDisplay"}, | 18 | {6, nullptr, "FadeInDisplay"}, |
| 19 | {7, nullptr, "FadeOutDisplay"}, | 19 | {7, nullptr, "FadeOutDisplay"}, |
| 20 | {8, nullptr, "Unknown1"}, | 20 | {8, nullptr, "GetCradleFwVersion"}, |
| 21 | {9, nullptr, "Unknown2"}, | 21 | {9, nullptr, "NotifyCecSettingsChanged"}, |
| 22 | {10, nullptr, "Unknown3"}, | 22 | {10, nullptr, "SetOperationModePolicy"}, |
| 23 | {11, nullptr, "Unknown4"}, | 23 | {11, nullptr, "GetDefaultDisplayResolution"}, |
| 24 | {12, nullptr, "Unknown5"}, | 24 | {12, nullptr, "GetDefaultDisplayResolutionChangeEvent"}, |
| 25 | {13, nullptr, "Unknown6"}, | 25 | {13, nullptr, "UpdateDefaultDisplayResolution"}, |
| 26 | {14, nullptr, "Unknown7"}, | 26 | {14, nullptr, "ShouldSleepOnBoot"}, |
| 27 | {15, nullptr, "Unknown8"}, | 27 | {15, nullptr, "NotifyHdcpApplicationExecutionStarted"}, |
| 28 | {16, nullptr, "Unknown9"}, | 28 | {16, nullptr, "NotifyHdcpApplicationExecutionFinished"}, |
| 29 | {17, nullptr, "Unknown10"}, | 29 | {17, nullptr, "NotifyHdcpApplicationDrawingStarted"}, |
| 30 | {18, nullptr, "Unknown11"}, | 30 | {18, nullptr, "NotifyHdcpApplicationDrawingFinished"}, |
| 31 | {19, nullptr, "Unknown12"}, | 31 | {19, nullptr, "GetHdcpAuthenticationFailedEvent"}, |
| 32 | {20, nullptr, "Unknown13"}, | 32 | {20, nullptr, "GetHdcpAuthenticationFailedEmulationEnabled"}, |
| 33 | {21, nullptr, "Unknown14"}, | 33 | {21, nullptr, "SetHdcpAuthenticationFailedEmulation"}, |
| 34 | {22, nullptr, "Unknown15"}, | 34 | {22, nullptr, "GetHdcpStateChangeEvent"}, |
| 35 | {23, nullptr, "Unknown16"}, | 35 | {23, nullptr, "GetHdcpState"}, |
| 36 | {24, nullptr, "ShowCardUpdateProcessing"}, | ||
| 37 | {25, nullptr, "SetApplicationCecSettingsAndNotifyChanged"}, | ||
| 36 | }; | 38 | }; |
| 37 | // clang-format on | 39 | // clang-format on |
| 38 | 40 | ||
diff --git a/src/core/hle/service/am/tcap.cpp b/src/core/hle/service/am/tcap.cpp new file mode 100644 index 000000000..a75cbdda8 --- /dev/null +++ b/src/core/hle/service/am/tcap.cpp | |||
| @@ -0,0 +1,23 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "core/hle/service/am/tcap.h" | ||
| 6 | |||
| 7 | namespace Service::AM { | ||
| 8 | |||
| 9 | TCAP::TCAP() : ServiceFramework{"tcap"} { | ||
| 10 | // clang-format off | ||
| 11 | static const FunctionInfo functions[] = { | ||
| 12 | {0, nullptr, "GetContinuousHighSkinTemperatureEvent"}, | ||
| 13 | {1, nullptr, "SetOperationMode"}, | ||
| 14 | {2, nullptr, "LoadAndApplySettings"}, | ||
| 15 | }; | ||
| 16 | // clang-format on | ||
| 17 | |||
| 18 | RegisterHandlers(functions); | ||
| 19 | } | ||
| 20 | |||
| 21 | TCAP::~TCAP() = default; | ||
| 22 | |||
| 23 | } // namespace Service::AM | ||
diff --git a/src/core/hle/service/am/tcap.h b/src/core/hle/service/am/tcap.h new file mode 100644 index 000000000..2021b55d1 --- /dev/null +++ b/src/core/hle/service/am/tcap.h | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "core/hle/service/service.h" | ||
| 8 | |||
| 9 | namespace Service::AM { | ||
| 10 | |||
| 11 | class TCAP final : public ServiceFramework<TCAP> { | ||
| 12 | public: | ||
| 13 | explicit TCAP(); | ||
| 14 | ~TCAP() override; | ||
| 15 | }; | ||
| 16 | |||
| 17 | } // namespace Service::AM | ||
diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp index 518161bf7..54305cf05 100644 --- a/src/core/hle/service/aoc/aoc_u.cpp +++ b/src/core/hle/service/aoc/aoc_u.cpp | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | #include "core/file_sys/patch_manager.h" | 13 | #include "core/file_sys/patch_manager.h" |
| 14 | #include "core/file_sys/registered_cache.h" | 14 | #include "core/file_sys/registered_cache.h" |
| 15 | #include "core/hle/ipc_helpers.h" | 15 | #include "core/hle/ipc_helpers.h" |
| 16 | #include "core/hle/kernel/event.h" | ||
| 16 | #include "core/hle/kernel/process.h" | 17 | #include "core/hle/kernel/process.h" |
| 17 | #include "core/hle/service/aoc/aoc_u.h" | 18 | #include "core/hle/service/aoc/aoc_u.h" |
| 18 | #include "core/hle/service/filesystem/filesystem.h" | 19 | #include "core/hle/service/filesystem/filesystem.h" |
| @@ -23,8 +24,8 @@ namespace Service::AOC { | |||
| 23 | constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000; | 24 | constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000; |
| 24 | constexpr u64 DLC_BASE_TO_AOC_ID = 0x1000; | 25 | constexpr u64 DLC_BASE_TO_AOC_ID = 0x1000; |
| 25 | 26 | ||
| 26 | static bool CheckAOCTitleIDMatchesBase(u64 base, u64 aoc) { | 27 | static bool CheckAOCTitleIDMatchesBase(u64 title_id, u64 base) { |
| 27 | return (aoc & DLC_BASE_TITLE_ID_MASK) == base; | 28 | return (title_id & DLC_BASE_TITLE_ID_MASK) == base; |
| 28 | } | 29 | } |
| 29 | 30 | ||
| 30 | static std::vector<u64> AccumulateAOCTitleIDs() { | 31 | static std::vector<u64> AccumulateAOCTitleIDs() { |
| @@ -55,9 +56,13 @@ AOC_U::AOC_U() : ServiceFramework("aoc:u"), add_on_content(AccumulateAOCTitleIDs | |||
| 55 | {5, &AOC_U::GetAddOnContentBaseId, "GetAddOnContentBaseId"}, | 56 | {5, &AOC_U::GetAddOnContentBaseId, "GetAddOnContentBaseId"}, |
| 56 | {6, nullptr, "PrepareAddOnContentByApplicationId"}, | 57 | {6, nullptr, "PrepareAddOnContentByApplicationId"}, |
| 57 | {7, &AOC_U::PrepareAddOnContent, "PrepareAddOnContent"}, | 58 | {7, &AOC_U::PrepareAddOnContent, "PrepareAddOnContent"}, |
| 58 | {8, nullptr, "GetAddOnContentListChangedEvent"}, | 59 | {8, &AOC_U::GetAddOnContentListChangedEvent, "GetAddOnContentListChangedEvent"}, |
| 59 | }; | 60 | }; |
| 60 | RegisterHandlers(functions); | 61 | RegisterHandlers(functions); |
| 62 | |||
| 63 | auto& kernel = Core::System::GetInstance().Kernel(); | ||
| 64 | aoc_change_event = Kernel::Event::Create(kernel, Kernel::ResetType::Sticky, | ||
| 65 | "GetAddOnContentListChanged:Event"); | ||
| 61 | } | 66 | } |
| 62 | 67 | ||
| 63 | AOC_U::~AOC_U() = default; | 68 | AOC_U::~AOC_U() = default; |
| @@ -69,7 +74,7 @@ void AOC_U::CountAddOnContent(Kernel::HLERequestContext& ctx) { | |||
| 69 | const auto current = Core::System::GetInstance().CurrentProcess()->GetTitleID(); | 74 | const auto current = Core::System::GetInstance().CurrentProcess()->GetTitleID(); |
| 70 | rb.Push<u32>(static_cast<u32>( | 75 | rb.Push<u32>(static_cast<u32>( |
| 71 | std::count_if(add_on_content.begin(), add_on_content.end(), | 76 | std::count_if(add_on_content.begin(), add_on_content.end(), |
| 72 | [¤t](u64 tid) { return (tid & DLC_BASE_TITLE_ID_MASK) == current; }))); | 77 | [current](u64 tid) { return CheckAOCTitleIDMatchesBase(tid, current); }))); |
| 73 | } | 78 | } |
| 74 | 79 | ||
| 75 | void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) { | 80 | void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) { |
| @@ -130,6 +135,14 @@ void AOC_U::PrepareAddOnContent(Kernel::HLERequestContext& ctx) { | |||
| 130 | rb.Push(RESULT_SUCCESS); | 135 | rb.Push(RESULT_SUCCESS); |
| 131 | } | 136 | } |
| 132 | 137 | ||
| 138 | void AOC_U::GetAddOnContentListChangedEvent(Kernel::HLERequestContext& ctx) { | ||
| 139 | LOG_WARNING(Service_AOC, "(STUBBED) called"); | ||
| 140 | |||
| 141 | IPC::ResponseBuilder rb{ctx, 2, 1}; | ||
| 142 | rb.Push(RESULT_SUCCESS); | ||
| 143 | rb.PushCopyObjects(aoc_change_event); | ||
| 144 | } | ||
| 145 | |||
| 133 | void InstallInterfaces(SM::ServiceManager& service_manager) { | 146 | void InstallInterfaces(SM::ServiceManager& service_manager) { |
| 134 | std::make_shared<AOC_U>()->InstallAsService(service_manager); | 147 | std::make_shared<AOC_U>()->InstallAsService(service_manager); |
| 135 | } | 148 | } |
diff --git a/src/core/hle/service/aoc/aoc_u.h b/src/core/hle/service/aoc/aoc_u.h index b3c7cab7a..68d94fdaa 100644 --- a/src/core/hle/service/aoc/aoc_u.h +++ b/src/core/hle/service/aoc/aoc_u.h | |||
| @@ -18,8 +18,10 @@ private: | |||
| 18 | void ListAddOnContent(Kernel::HLERequestContext& ctx); | 18 | void ListAddOnContent(Kernel::HLERequestContext& ctx); |
| 19 | void GetAddOnContentBaseId(Kernel::HLERequestContext& ctx); | 19 | void GetAddOnContentBaseId(Kernel::HLERequestContext& ctx); |
| 20 | void PrepareAddOnContent(Kernel::HLERequestContext& ctx); | 20 | void PrepareAddOnContent(Kernel::HLERequestContext& ctx); |
| 21 | void GetAddOnContentListChangedEvent(Kernel::HLERequestContext& ctx); | ||
| 21 | 22 | ||
| 22 | std::vector<u64> add_on_content; | 23 | std::vector<u64> add_on_content; |
| 24 | Kernel::SharedPtr<Kernel::Event> aoc_change_event; | ||
| 23 | }; | 25 | }; |
| 24 | 26 | ||
| 25 | /// Registers all AOC services with the specified service manager. | 27 | /// Registers all AOC services with the specified service manager. |
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp index 6073f4ecd..fac6785a5 100644 --- a/src/core/hle/service/audio/audren_u.cpp +++ b/src/core/hle/service/audio/audren_u.cpp | |||
| @@ -22,20 +22,22 @@ class IAudioRenderer final : public ServiceFramework<IAudioRenderer> { | |||
| 22 | public: | 22 | public: |
| 23 | explicit IAudioRenderer(AudioCore::AudioRendererParameter audren_params) | 23 | explicit IAudioRenderer(AudioCore::AudioRendererParameter audren_params) |
| 24 | : ServiceFramework("IAudioRenderer") { | 24 | : ServiceFramework("IAudioRenderer") { |
| 25 | // clang-format off | ||
| 25 | static const FunctionInfo functions[] = { | 26 | static const FunctionInfo functions[] = { |
| 26 | {0, &IAudioRenderer::GetAudioRendererSampleRate, "GetAudioRendererSampleRate"}, | 27 | {0, &IAudioRenderer::GetSampleRate, "GetSampleRate"}, |
| 27 | {1, &IAudioRenderer::GetAudioRendererSampleCount, "GetAudioRendererSampleCount"}, | 28 | {1, &IAudioRenderer::GetSampleCount, "GetSampleCount"}, |
| 28 | {2, &IAudioRenderer::GetAudioRendererMixBufferCount, "GetAudioRendererMixBufferCount"}, | 29 | {2, &IAudioRenderer::GetMixBufferCount, "GetMixBufferCount"}, |
| 29 | {3, &IAudioRenderer::GetAudioRendererState, "GetAudioRendererState"}, | 30 | {3, &IAudioRenderer::GetState, "GetState"}, |
| 30 | {4, &IAudioRenderer::RequestUpdateAudioRenderer, "RequestUpdateAudioRenderer"}, | 31 | {4, &IAudioRenderer::RequestUpdate, "RequestUpdate"}, |
| 31 | {5, &IAudioRenderer::StartAudioRenderer, "StartAudioRenderer"}, | 32 | {5, &IAudioRenderer::Start, "Start"}, |
| 32 | {6, &IAudioRenderer::StopAudioRenderer, "StopAudioRenderer"}, | 33 | {6, &IAudioRenderer::Stop, "Stop"}, |
| 33 | {7, &IAudioRenderer::QuerySystemEvent, "QuerySystemEvent"}, | 34 | {7, &IAudioRenderer::QuerySystemEvent, "QuerySystemEvent"}, |
| 34 | {8, nullptr, "SetAudioRendererRenderingTimeLimit"}, | 35 | {8, nullptr, "SetRenderingTimeLimit"}, |
| 35 | {9, nullptr, "GetAudioRendererRenderingTimeLimit"}, | 36 | {9, nullptr, "GetRenderingTimeLimit"}, |
| 36 | {10, nullptr, "RequestUpdateAudioRendererAuto"}, | 37 | {10, nullptr, "RequestUpdateAuto"}, |
| 37 | {11, nullptr, "ExecuteAudioRendererRendering"}, | 38 | {11, nullptr, "ExecuteAudioRendererRendering"}, |
| 38 | }; | 39 | }; |
| 40 | // clang-format on | ||
| 39 | RegisterHandlers(functions); | 41 | RegisterHandlers(functions); |
| 40 | 42 | ||
| 41 | auto& kernel = Core::System::GetInstance().Kernel(); | 43 | auto& kernel = Core::System::GetInstance().Kernel(); |
| @@ -49,42 +51,42 @@ private: | |||
| 49 | system_event->Signal(); | 51 | system_event->Signal(); |
| 50 | } | 52 | } |
| 51 | 53 | ||
| 52 | void GetAudioRendererSampleRate(Kernel::HLERequestContext& ctx) { | 54 | void GetSampleRate(Kernel::HLERequestContext& ctx) { |
| 53 | IPC::ResponseBuilder rb{ctx, 3}; | 55 | IPC::ResponseBuilder rb{ctx, 3}; |
| 54 | rb.Push(RESULT_SUCCESS); | 56 | rb.Push(RESULT_SUCCESS); |
| 55 | rb.Push<u32>(renderer->GetSampleRate()); | 57 | rb.Push<u32>(renderer->GetSampleRate()); |
| 56 | LOG_DEBUG(Service_Audio, "called"); | 58 | LOG_DEBUG(Service_Audio, "called"); |
| 57 | } | 59 | } |
| 58 | 60 | ||
| 59 | void GetAudioRendererSampleCount(Kernel::HLERequestContext& ctx) { | 61 | void GetSampleCount(Kernel::HLERequestContext& ctx) { |
| 60 | IPC::ResponseBuilder rb{ctx, 3}; | 62 | IPC::ResponseBuilder rb{ctx, 3}; |
| 61 | rb.Push(RESULT_SUCCESS); | 63 | rb.Push(RESULT_SUCCESS); |
| 62 | rb.Push<u32>(renderer->GetSampleCount()); | 64 | rb.Push<u32>(renderer->GetSampleCount()); |
| 63 | LOG_DEBUG(Service_Audio, "called"); | 65 | LOG_DEBUG(Service_Audio, "called"); |
| 64 | } | 66 | } |
| 65 | 67 | ||
| 66 | void GetAudioRendererState(Kernel::HLERequestContext& ctx) { | 68 | void GetState(Kernel::HLERequestContext& ctx) { |
| 67 | IPC::ResponseBuilder rb{ctx, 3}; | 69 | IPC::ResponseBuilder rb{ctx, 3}; |
| 68 | rb.Push(RESULT_SUCCESS); | 70 | rb.Push(RESULT_SUCCESS); |
| 69 | rb.Push<u32>(static_cast<u32>(renderer->GetStreamState())); | 71 | rb.Push<u32>(static_cast<u32>(renderer->GetStreamState())); |
| 70 | LOG_DEBUG(Service_Audio, "called"); | 72 | LOG_DEBUG(Service_Audio, "called"); |
| 71 | } | 73 | } |
| 72 | 74 | ||
| 73 | void GetAudioRendererMixBufferCount(Kernel::HLERequestContext& ctx) { | 75 | void GetMixBufferCount(Kernel::HLERequestContext& ctx) { |
| 74 | IPC::ResponseBuilder rb{ctx, 3}; | 76 | IPC::ResponseBuilder rb{ctx, 3}; |
| 75 | rb.Push(RESULT_SUCCESS); | 77 | rb.Push(RESULT_SUCCESS); |
| 76 | rb.Push<u32>(renderer->GetMixBufferCount()); | 78 | rb.Push<u32>(renderer->GetMixBufferCount()); |
| 77 | LOG_DEBUG(Service_Audio, "called"); | 79 | LOG_DEBUG(Service_Audio, "called"); |
| 78 | } | 80 | } |
| 79 | 81 | ||
| 80 | void RequestUpdateAudioRenderer(Kernel::HLERequestContext& ctx) { | 82 | void RequestUpdate(Kernel::HLERequestContext& ctx) { |
| 81 | ctx.WriteBuffer(renderer->UpdateAudioRenderer(ctx.ReadBuffer())); | 83 | ctx.WriteBuffer(renderer->UpdateAudioRenderer(ctx.ReadBuffer())); |
| 82 | IPC::ResponseBuilder rb{ctx, 2}; | 84 | IPC::ResponseBuilder rb{ctx, 2}; |
| 83 | rb.Push(RESULT_SUCCESS); | 85 | rb.Push(RESULT_SUCCESS); |
| 84 | LOG_WARNING(Service_Audio, "(STUBBED) called"); | 86 | LOG_WARNING(Service_Audio, "(STUBBED) called"); |
| 85 | } | 87 | } |
| 86 | 88 | ||
| 87 | void StartAudioRenderer(Kernel::HLERequestContext& ctx) { | 89 | void Start(Kernel::HLERequestContext& ctx) { |
| 88 | IPC::ResponseBuilder rb{ctx, 2}; | 90 | IPC::ResponseBuilder rb{ctx, 2}; |
| 89 | 91 | ||
| 90 | rb.Push(RESULT_SUCCESS); | 92 | rb.Push(RESULT_SUCCESS); |
| @@ -92,7 +94,7 @@ private: | |||
| 92 | LOG_WARNING(Service_Audio, "(STUBBED) called"); | 94 | LOG_WARNING(Service_Audio, "(STUBBED) called"); |
| 93 | } | 95 | } |
| 94 | 96 | ||
| 95 | void StopAudioRenderer(Kernel::HLERequestContext& ctx) { | 97 | void Stop(Kernel::HLERequestContext& ctx) { |
| 96 | IPC::ResponseBuilder rb{ctx, 2}; | 98 | IPC::ResponseBuilder rb{ctx, 2}; |
| 97 | 99 | ||
| 98 | rb.Push(RESULT_SUCCESS); | 100 | rb.Push(RESULT_SUCCESS); |
| @@ -129,6 +131,7 @@ public: | |||
| 129 | {10, &IAudioDevice::GetActiveAudioDeviceName, "GetActiveAudioDeviceNameAuto"}, | 131 | {10, &IAudioDevice::GetActiveAudioDeviceName, "GetActiveAudioDeviceNameAuto"}, |
| 130 | {11, nullptr, "QueryAudioDeviceInputEvent"}, | 132 | {11, nullptr, "QueryAudioDeviceInputEvent"}, |
| 131 | {12, nullptr, "QueryAudioDeviceOutputEvent"}, | 133 | {12, nullptr, "QueryAudioDeviceOutputEvent"}, |
| 134 | {13, nullptr, "GetAudioSystemMasterVolumeSetting"}, | ||
| 132 | }; | 135 | }; |
| 133 | RegisterHandlers(functions); | 136 | RegisterHandlers(functions); |
| 134 | 137 | ||
diff --git a/src/core/hle/service/es/es.cpp b/src/core/hle/service/es/es.cpp index d40f18565..6701cb913 100644 --- a/src/core/hle/service/es/es.cpp +++ b/src/core/hle/service/es/es.cpp | |||
| @@ -9,6 +9,7 @@ namespace Service::ES { | |||
| 9 | class ETicket final : public ServiceFramework<ETicket> { | 9 | class ETicket final : public ServiceFramework<ETicket> { |
| 10 | public: | 10 | public: |
| 11 | explicit ETicket() : ServiceFramework{"es"} { | 11 | explicit ETicket() : ServiceFramework{"es"} { |
| 12 | // clang-format off | ||
| 12 | static const FunctionInfo functions[] = { | 13 | static const FunctionInfo functions[] = { |
| 13 | {1, nullptr, "ImportTicket"}, | 14 | {1, nullptr, "ImportTicket"}, |
| 14 | {2, nullptr, "ImportTicketCertificateSet"}, | 15 | {2, nullptr, "ImportTicketCertificateSet"}, |
| @@ -37,15 +38,18 @@ public: | |||
| 37 | {25, nullptr, "DeletePrepurchaseRecord"}, | 38 | {25, nullptr, "DeletePrepurchaseRecord"}, |
| 38 | {26, nullptr, "DeleteAllPrepurchaseRecord"}, | 39 | {26, nullptr, "DeleteAllPrepurchaseRecord"}, |
| 39 | {27, nullptr, "CountPrepurchaseRecord"}, | 40 | {27, nullptr, "CountPrepurchaseRecord"}, |
| 40 | {28, nullptr, "ListPrepurchaseRecord"}, | 41 | {28, nullptr, "ListPrepurchaseRecordRightsIds"}, |
| 41 | {29, nullptr, "ListPrepurchaseRecordInfo"}, | 42 | {29, nullptr, "ListPrepurchaseRecordInfo"}, |
| 42 | {30, nullptr, "Unknown1"}, | 43 | {30, nullptr, "CountTicket"}, |
| 43 | {31, nullptr, "Unknown2"}, | 44 | {31, nullptr, "ListTicketRightsIds"}, |
| 44 | {32, nullptr, "Unknown3"}, | 45 | {32, nullptr, "CountPrepurchaseRecordEx"}, |
| 45 | {33, nullptr, "Unknown4"}, | 46 | {33, nullptr, "ListPrepurchaseRecordRightsIdsEx"}, |
| 46 | {34, nullptr, "Unknown5"}, | 47 | {34, nullptr, "GetEncryptedTicketSize"}, |
| 47 | {35, nullptr, "Unknown6"}, | 48 | {35, nullptr, "GetEncryptedTicketData"}, |
| 49 | {36, nullptr, "DeleteAllInactiveELicenseRequiredPersonalizedTicket"}, | ||
| 50 | {503, nullptr, "GetTitleKey"}, | ||
| 48 | }; | 51 | }; |
| 52 | // clang-format on | ||
| 49 | RegisterHandlers(functions); | 53 | RegisterHandlers(functions); |
| 50 | } | 54 | } |
| 51 | }; | 55 | }; |
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp index d5dced429..c87721c39 100644 --- a/src/core/hle/service/filesystem/fsp_srv.cpp +++ b/src/core/hle/service/filesystem/fsp_srv.cpp | |||
| @@ -17,6 +17,7 @@ | |||
| 17 | #include "core/file_sys/errors.h" | 17 | #include "core/file_sys/errors.h" |
| 18 | #include "core/file_sys/mode.h" | 18 | #include "core/file_sys/mode.h" |
| 19 | #include "core/file_sys/nca_metadata.h" | 19 | #include "core/file_sys/nca_metadata.h" |
| 20 | #include "core/file_sys/patch_manager.h" | ||
| 20 | #include "core/file_sys/savedata_factory.h" | 21 | #include "core/file_sys/savedata_factory.h" |
| 21 | #include "core/file_sys/vfs.h" | 22 | #include "core/file_sys/vfs.h" |
| 22 | #include "core/hle/ipc_helpers.h" | 23 | #include "core/hle/ipc_helpers.h" |
| @@ -630,6 +631,7 @@ void FSP_SRV::OpenDataStorageByDataId(Kernel::HLERequestContext& ctx) { | |||
| 630 | static_cast<u8>(storage_id), unknown, title_id); | 631 | static_cast<u8>(storage_id), unknown, title_id); |
| 631 | 632 | ||
| 632 | auto data = OpenRomFS(title_id, storage_id, FileSys::ContentRecordType::Data); | 633 | auto data = OpenRomFS(title_id, storage_id, FileSys::ContentRecordType::Data); |
| 634 | |||
| 633 | if (data.Failed()) { | 635 | if (data.Failed()) { |
| 634 | // TODO(DarkLordZach): Find the right error code to use here | 636 | // TODO(DarkLordZach): Find the right error code to use here |
| 635 | LOG_ERROR(Service_FS, | 637 | LOG_ERROR(Service_FS, |
| @@ -640,7 +642,9 @@ void FSP_SRV::OpenDataStorageByDataId(Kernel::HLERequestContext& ctx) { | |||
| 640 | return; | 642 | return; |
| 641 | } | 643 | } |
| 642 | 644 | ||
| 643 | IStorage storage(std::move(data.Unwrap())); | 645 | FileSys::PatchManager pm{title_id}; |
| 646 | |||
| 647 | IStorage storage(pm.PatchRomFS(std::move(data.Unwrap()), 0, FileSys::ContentRecordType::Data)); | ||
| 644 | 648 | ||
| 645 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 649 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 646 | rb.Push(RESULT_SUCCESS); | 650 | rb.Push(RESULT_SUCCESS); |
diff --git a/src/core/hle/service/hid/controllers/controller_base.cpp b/src/core/hle/service/hid/controllers/controller_base.cpp index 1625e9c3d..0993a7815 100644 --- a/src/core/hle/service/hid/controllers/controller_base.cpp +++ b/src/core/hle/service/hid/controllers/controller_base.cpp | |||
| @@ -5,6 +5,8 @@ | |||
| 5 | #include "core/hle/service/hid/controllers/controller_base.h" | 5 | #include "core/hle/service/hid/controllers/controller_base.h" |
| 6 | 6 | ||
| 7 | namespace Service::HID { | 7 | namespace Service::HID { |
| 8 | |||
| 9 | ControllerBase::ControllerBase() = default; | ||
| 8 | ControllerBase::~ControllerBase() = default; | 10 | ControllerBase::~ControllerBase() = default; |
| 9 | 11 | ||
| 10 | void ControllerBase::ActivateController() { | 12 | void ControllerBase::ActivateController() { |
diff --git a/src/core/hle/service/hid/controllers/controller_base.h b/src/core/hle/service/hid/controllers/controller_base.h index fa98e2354..f0e092b1b 100644 --- a/src/core/hle/service/hid/controllers/controller_base.h +++ b/src/core/hle/service/hid/controllers/controller_base.h | |||
| @@ -10,8 +10,8 @@ | |||
| 10 | namespace Service::HID { | 10 | namespace Service::HID { |
| 11 | class ControllerBase { | 11 | class ControllerBase { |
| 12 | public: | 12 | public: |
| 13 | ControllerBase() = default; | 13 | ControllerBase(); |
| 14 | virtual ~ControllerBase() = 0; | 14 | virtual ~ControllerBase(); |
| 15 | 15 | ||
| 16 | // Called when the controller is initialized | 16 | // Called when the controller is initialized |
| 17 | virtual void OnInit() = 0; | 17 | virtual void OnInit() = 0; |
diff --git a/src/core/hle/service/hid/controllers/debug_pad.cpp b/src/core/hle/service/hid/controllers/debug_pad.cpp index 6f8ef6e3f..3d100763f 100644 --- a/src/core/hle/service/hid/controllers/debug_pad.cpp +++ b/src/core/hle/service/hid/controllers/debug_pad.cpp | |||
| @@ -4,13 +4,13 @@ | |||
| 4 | 4 | ||
| 5 | #include <cstring> | 5 | #include <cstring> |
| 6 | #include "common/common_types.h" | 6 | #include "common/common_types.h" |
| 7 | #include "common/swap.h" | ||
| 8 | #include "core/core_timing.h" | 7 | #include "core/core_timing.h" |
| 9 | #include "core/hle/service/hid/controllers/debug_pad.h" | 8 | #include "core/hle/service/hid/controllers/debug_pad.h" |
| 10 | 9 | ||
| 11 | namespace Service::HID { | 10 | namespace Service::HID { |
| 12 | 11 | ||
| 13 | Controller_DebugPad::Controller_DebugPad() = default; | 12 | Controller_DebugPad::Controller_DebugPad() = default; |
| 13 | Controller_DebugPad::~Controller_DebugPad() = default; | ||
| 14 | 14 | ||
| 15 | void Controller_DebugPad::OnInit() {} | 15 | void Controller_DebugPad::OnInit() {} |
| 16 | 16 | ||
diff --git a/src/core/hle/service/hid/controllers/debug_pad.h b/src/core/hle/service/hid/controllers/debug_pad.h index e35675fa1..62b4f2682 100644 --- a/src/core/hle/service/hid/controllers/debug_pad.h +++ b/src/core/hle/service/hid/controllers/debug_pad.h | |||
| @@ -14,6 +14,7 @@ namespace Service::HID { | |||
| 14 | class Controller_DebugPad final : public ControllerBase { | 14 | class Controller_DebugPad final : public ControllerBase { |
| 15 | public: | 15 | public: |
| 16 | Controller_DebugPad(); | 16 | Controller_DebugPad(); |
| 17 | ~Controller_DebugPad() override; | ||
| 17 | 18 | ||
| 18 | // Called when the controller is initialized | 19 | // Called when the controller is initialized |
| 19 | void OnInit() override; | 20 | void OnInit() override; |
diff --git a/src/core/hle/service/hid/controllers/gesture.cpp b/src/core/hle/service/hid/controllers/gesture.cpp index b473b9e2b..898572277 100644 --- a/src/core/hle/service/hid/controllers/gesture.cpp +++ b/src/core/hle/service/hid/controllers/gesture.cpp | |||
| @@ -4,7 +4,6 @@ | |||
| 4 | 4 | ||
| 5 | #include <cstring> | 5 | #include <cstring> |
| 6 | #include "common/common_types.h" | 6 | #include "common/common_types.h" |
| 7 | #include "common/swap.h" | ||
| 8 | #include "core/core_timing.h" | 7 | #include "core/core_timing.h" |
| 9 | #include "core/hle/service/hid/controllers/gesture.h" | 8 | #include "core/hle/service/hid/controllers/gesture.h" |
| 10 | 9 | ||
| @@ -12,6 +11,7 @@ namespace Service::HID { | |||
| 12 | constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3BA00; | 11 | constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3BA00; |
| 13 | 12 | ||
| 14 | Controller_Gesture::Controller_Gesture() = default; | 13 | Controller_Gesture::Controller_Gesture() = default; |
| 14 | Controller_Gesture::~Controller_Gesture() = default; | ||
| 15 | 15 | ||
| 16 | void Controller_Gesture::OnInit() {} | 16 | void Controller_Gesture::OnInit() {} |
| 17 | 17 | ||
diff --git a/src/core/hle/service/hid/controllers/gesture.h b/src/core/hle/service/hid/controllers/gesture.h index 0ced50dfd..1056ffbcd 100644 --- a/src/core/hle/service/hid/controllers/gesture.h +++ b/src/core/hle/service/hid/controllers/gesture.h | |||
| @@ -13,6 +13,7 @@ namespace Service::HID { | |||
| 13 | class Controller_Gesture final : public ControllerBase { | 13 | class Controller_Gesture final : public ControllerBase { |
| 14 | public: | 14 | public: |
| 15 | Controller_Gesture(); | 15 | Controller_Gesture(); |
| 16 | ~Controller_Gesture() override; | ||
| 16 | 17 | ||
| 17 | // Called when the controller is initialized | 18 | // Called when the controller is initialized |
| 18 | void OnInit() override; | 19 | void OnInit() override; |
diff --git a/src/core/hle/service/hid/controllers/keyboard.cpp b/src/core/hle/service/hid/controllers/keyboard.cpp index 089c02ac4..ccfbce9ac 100644 --- a/src/core/hle/service/hid/controllers/keyboard.cpp +++ b/src/core/hle/service/hid/controllers/keyboard.cpp | |||
| @@ -4,7 +4,6 @@ | |||
| 4 | 4 | ||
| 5 | #include <cstring> | 5 | #include <cstring> |
| 6 | #include "common/common_types.h" | 6 | #include "common/common_types.h" |
| 7 | #include "common/swap.h" | ||
| 8 | #include "core/core_timing.h" | 7 | #include "core/core_timing.h" |
| 9 | #include "core/hle/service/hid/controllers/keyboard.h" | 8 | #include "core/hle/service/hid/controllers/keyboard.h" |
| 10 | 9 | ||
| @@ -12,6 +11,7 @@ namespace Service::HID { | |||
| 12 | constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800; | 11 | constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800; |
| 13 | 12 | ||
| 14 | Controller_Keyboard::Controller_Keyboard() = default; | 13 | Controller_Keyboard::Controller_Keyboard() = default; |
| 14 | Controller_Keyboard::~Controller_Keyboard() = default; | ||
| 15 | 15 | ||
| 16 | void Controller_Keyboard::OnInit() {} | 16 | void Controller_Keyboard::OnInit() {} |
| 17 | 17 | ||
diff --git a/src/core/hle/service/hid/controllers/keyboard.h b/src/core/hle/service/hid/controllers/keyboard.h index 778e14f7e..493e68fce 100644 --- a/src/core/hle/service/hid/controllers/keyboard.h +++ b/src/core/hle/service/hid/controllers/keyboard.h | |||
| @@ -14,6 +14,7 @@ namespace Service::HID { | |||
| 14 | class Controller_Keyboard final : public ControllerBase { | 14 | class Controller_Keyboard final : public ControllerBase { |
| 15 | public: | 15 | public: |
| 16 | Controller_Keyboard(); | 16 | Controller_Keyboard(); |
| 17 | ~Controller_Keyboard() override; | ||
| 17 | 18 | ||
| 18 | // Called when the controller is initialized | 19 | // Called when the controller is initialized |
| 19 | void OnInit() override; | 20 | void OnInit() override; |
diff --git a/src/core/hle/service/hid/controllers/mouse.cpp b/src/core/hle/service/hid/controllers/mouse.cpp index 78e9b5e9e..4e246a57d 100644 --- a/src/core/hle/service/hid/controllers/mouse.cpp +++ b/src/core/hle/service/hid/controllers/mouse.cpp | |||
| @@ -4,7 +4,6 @@ | |||
| 4 | 4 | ||
| 5 | #include <cstring> | 5 | #include <cstring> |
| 6 | #include "common/common_types.h" | 6 | #include "common/common_types.h" |
| 7 | #include "common/swap.h" | ||
| 8 | #include "core/core_timing.h" | 7 | #include "core/core_timing.h" |
| 9 | #include "core/hle/service/hid/controllers/mouse.h" | 8 | #include "core/hle/service/hid/controllers/mouse.h" |
| 10 | 9 | ||
| @@ -12,6 +11,7 @@ namespace Service::HID { | |||
| 12 | constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3400; | 11 | constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3400; |
| 13 | 12 | ||
| 14 | Controller_Mouse::Controller_Mouse() = default; | 13 | Controller_Mouse::Controller_Mouse() = default; |
| 14 | Controller_Mouse::~Controller_Mouse() = default; | ||
| 15 | 15 | ||
| 16 | void Controller_Mouse::OnInit() {} | 16 | void Controller_Mouse::OnInit() {} |
| 17 | 17 | ||
diff --git a/src/core/hle/service/hid/controllers/mouse.h b/src/core/hle/service/hid/controllers/mouse.h index 05358a4f5..543b0b71f 100644 --- a/src/core/hle/service/hid/controllers/mouse.h +++ b/src/core/hle/service/hid/controllers/mouse.h | |||
| @@ -13,6 +13,7 @@ namespace Service::HID { | |||
| 13 | class Controller_Mouse final : public ControllerBase { | 13 | class Controller_Mouse final : public ControllerBase { |
| 14 | public: | 14 | public: |
| 15 | Controller_Mouse(); | 15 | Controller_Mouse(); |
| 16 | ~Controller_Mouse() override; | ||
| 16 | 17 | ||
| 17 | // Called when the controller is initialized | 18 | // Called when the controller is initialized |
| 18 | void OnInit() override; | 19 | void OnInit() override; |
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index d17e64b2a..4b4d1324f 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp | |||
| @@ -2,8 +2,6 @@ | |||
| 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 | #pragma once | ||
| 6 | |||
| 7 | #include <algorithm> | 5 | #include <algorithm> |
| 8 | #include <array> | 6 | #include <array> |
| 9 | #include <cstring> | 7 | #include <cstring> |
| @@ -11,7 +9,6 @@ | |||
| 11 | #include "common/bit_field.h" | 9 | #include "common/bit_field.h" |
| 12 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 13 | #include "common/logging/log.h" | 11 | #include "common/logging/log.h" |
| 14 | #include "common/swap.h" | ||
| 15 | #include "core/core.h" | 12 | #include "core/core.h" |
| 16 | #include "core/core_timing.h" | 13 | #include "core/core_timing.h" |
| 17 | #include "core/frontend/input.h" | 14 | #include "core/frontend/input.h" |
| @@ -20,6 +17,7 @@ | |||
| 20 | #include "core/settings.h" | 17 | #include "core/settings.h" |
| 21 | 18 | ||
| 22 | namespace Service::HID { | 19 | namespace Service::HID { |
| 20 | |||
| 23 | constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28; | 21 | constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28; |
| 24 | constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A; | 22 | constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A; |
| 25 | constexpr u32 JOYCON_BODY_NEON_BLUE = 0x0AB9E6; | 23 | constexpr u32 JOYCON_BODY_NEON_BLUE = 0x0AB9E6; |
| @@ -28,9 +26,22 @@ constexpr s32 HID_JOYSTICK_MAX = 0x7fff; | |||
| 28 | constexpr s32 HID_JOYSTICK_MIN = -0x7fff; | 26 | constexpr s32 HID_JOYSTICK_MIN = -0x7fff; |
| 29 | constexpr std::size_t NPAD_OFFSET = 0x9A00; | 27 | constexpr std::size_t NPAD_OFFSET = 0x9A00; |
| 30 | constexpr u32 BATTERY_FULL = 2; | 28 | constexpr u32 BATTERY_FULL = 2; |
| 31 | enum class JoystickId : std::size_t { Joystick_Left, Joystick_Right }; | 29 | constexpr u32 NPAD_HANDHELD = 32; |
| 30 | constexpr u32 NPAD_UNKNOWN = 16; // TODO(ogniK): What is this? | ||
| 31 | constexpr u32 MAX_NPAD_ID = 7; | ||
| 32 | constexpr Controller_NPad::NPadControllerType PREFERRED_CONTROLLER = | ||
| 33 | Controller_NPad::NPadControllerType::JoyDual; | ||
| 34 | constexpr std::array<u32, 10> npad_id_list{ | ||
| 35 | 0, 1, 2, 3, 4, 5, 6, 7, 32, 16, | ||
| 36 | }; | ||
| 37 | |||
| 38 | enum class JoystickId : std::size_t { | ||
| 39 | Joystick_Left, | ||
| 40 | Joystick_Right, | ||
| 41 | }; | ||
| 32 | 42 | ||
| 33 | Controller_NPad::Controller_NPad() = default; | 43 | Controller_NPad::Controller_NPad() = default; |
| 44 | Controller_NPad::~Controller_NPad() = default; | ||
| 34 | 45 | ||
| 35 | void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) { | 46 | void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) { |
| 36 | const auto controller_type = connected_controllers[controller_idx].type; | 47 | const auto controller_type = connected_controllers[controller_idx].type; |
| @@ -97,9 +108,10 @@ void Controller_NPad::OnInit() { | |||
| 97 | styleset_changed_event = | 108 | styleset_changed_event = |
| 98 | Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "npad:NpadStyleSetChanged"); | 109 | Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "npad:NpadStyleSetChanged"); |
| 99 | 110 | ||
| 100 | if (!IsControllerActivated()) | 111 | if (!IsControllerActivated()) { |
| 101 | return; | 112 | return; |
| 102 | std::size_t controller{}; | 113 | } |
| 114 | |||
| 103 | if (style.raw == 0) { | 115 | if (style.raw == 0) { |
| 104 | // We want to support all controllers | 116 | // We want to support all controllers |
| 105 | style.handheld.Assign(1); | 117 | style.handheld.Assign(1); |
| @@ -114,7 +126,7 @@ void Controller_NPad::OnInit() { | |||
| 114 | supported_npad_id_types.resize(npad_id_list.size()); | 126 | supported_npad_id_types.resize(npad_id_list.size()); |
| 115 | std::memcpy(supported_npad_id_types.data(), npad_id_list.data(), | 127 | std::memcpy(supported_npad_id_types.data(), npad_id_list.data(), |
| 116 | npad_id_list.size() * sizeof(u32)); | 128 | npad_id_list.size() * sizeof(u32)); |
| 117 | AddNewController(NPadControllerType::JoyDual); | 129 | AddNewController(PREFERRED_CONTROLLER); |
| 118 | } | 130 | } |
| 119 | } | 131 | } |
| 120 | 132 | ||
| @@ -211,6 +223,51 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) { | |||
| 211 | rstick_entry.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX); | 223 | rstick_entry.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX); |
| 212 | rstick_entry.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX); | 224 | rstick_entry.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX); |
| 213 | 225 | ||
| 226 | if (controller_type == NPadControllerType::JoyLeft || | ||
| 227 | controller_type == NPadControllerType::JoyRight) { | ||
| 228 | if (npad.properties.is_horizontal) { | ||
| 229 | ControllerPadState state{}; | ||
| 230 | AnalogPosition temp_lstick_entry{}; | ||
| 231 | AnalogPosition temp_rstick_entry{}; | ||
| 232 | if (controller_type == NPadControllerType::JoyLeft) { | ||
| 233 | state.d_down.Assign(pad_state.d_left.Value()); | ||
| 234 | state.d_left.Assign(pad_state.d_up.Value()); | ||
| 235 | state.d_right.Assign(pad_state.d_down.Value()); | ||
| 236 | state.d_up.Assign(pad_state.d_right.Value()); | ||
| 237 | state.l.Assign(pad_state.l.Value() | pad_state.sl.Value()); | ||
| 238 | state.r.Assign(pad_state.r.Value() | pad_state.sr.Value()); | ||
| 239 | |||
| 240 | state.zl.Assign(pad_state.zl.Value()); | ||
| 241 | state.plus.Assign(pad_state.minus.Value()); | ||
| 242 | |||
| 243 | temp_lstick_entry = lstick_entry; | ||
| 244 | temp_rstick_entry = rstick_entry; | ||
| 245 | std::swap(temp_lstick_entry.x, temp_lstick_entry.y); | ||
| 246 | std::swap(temp_rstick_entry.x, temp_rstick_entry.y); | ||
| 247 | temp_lstick_entry.y *= -1; | ||
| 248 | } else if (controller_type == NPadControllerType::JoyRight) { | ||
| 249 | state.x.Assign(pad_state.a.Value()); | ||
| 250 | state.a.Assign(pad_state.b.Value()); | ||
| 251 | state.b.Assign(pad_state.y.Value()); | ||
| 252 | state.y.Assign(pad_state.b.Value()); | ||
| 253 | |||
| 254 | state.l.Assign(pad_state.l.Value() | pad_state.sl.Value()); | ||
| 255 | state.r.Assign(pad_state.r.Value() | pad_state.sr.Value()); | ||
| 256 | state.zr.Assign(pad_state.zr.Value()); | ||
| 257 | state.plus.Assign(pad_state.plus.Value()); | ||
| 258 | |||
| 259 | temp_lstick_entry = lstick_entry; | ||
| 260 | temp_rstick_entry = rstick_entry; | ||
| 261 | std::swap(temp_lstick_entry.x, temp_lstick_entry.y); | ||
| 262 | std::swap(temp_rstick_entry.x, temp_rstick_entry.y); | ||
| 263 | temp_rstick_entry.x *= -1; | ||
| 264 | } | ||
| 265 | pad_state.raw = state.raw; | ||
| 266 | lstick_entry = temp_lstick_entry; | ||
| 267 | rstick_entry = temp_rstick_entry; | ||
| 268 | } | ||
| 269 | } | ||
| 270 | |||
| 214 | auto& main_controller = | 271 | auto& main_controller = |
| 215 | npad.main_controller_states.npad[npad.main_controller_states.common.last_entry_index]; | 272 | npad.main_controller_states.npad[npad.main_controller_states.common.last_entry_index]; |
| 216 | auto& handheld_entry = | 273 | auto& handheld_entry = |
| @@ -313,9 +370,19 @@ void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) { | |||
| 313 | supported_npad_id_types.clear(); | 370 | supported_npad_id_types.clear(); |
| 314 | supported_npad_id_types.resize(length / sizeof(u32)); | 371 | supported_npad_id_types.resize(length / sizeof(u32)); |
| 315 | std::memcpy(supported_npad_id_types.data(), data, length); | 372 | std::memcpy(supported_npad_id_types.data(), data, length); |
| 373 | for (std::size_t i = 0; i < connected_controllers.size(); i++) { | ||
| 374 | auto& controller = connected_controllers[i]; | ||
| 375 | if (!controller.is_connected) { | ||
| 376 | continue; | ||
| 377 | } | ||
| 378 | if (!IsControllerSupported(PREFERRED_CONTROLLER)) { | ||
| 379 | controller.type = DecideBestController(PREFERRED_CONTROLLER); | ||
| 380 | InitNewlyAddedControler(i); | ||
| 381 | } | ||
| 382 | } | ||
| 316 | } | 383 | } |
| 317 | 384 | ||
| 318 | const void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) { | 385 | void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) { |
| 319 | ASSERT(max_length < supported_npad_id_types.size()); | 386 | ASSERT(max_length < supported_npad_id_types.size()); |
| 320 | std::memcpy(data, supported_npad_id_types.data(), supported_npad_id_types.size()); | 387 | std::memcpy(data, supported_npad_id_types.data(), supported_npad_id_types.size()); |
| 321 | } | 388 | } |
| @@ -344,11 +411,11 @@ void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids, | |||
| 344 | for (std::size_t i = 0; i < controller_ids.size(); i++) { | 411 | for (std::size_t i = 0; i < controller_ids.size(); i++) { |
| 345 | std::size_t controller_pos = i; | 412 | std::size_t controller_pos = i; |
| 346 | // Handheld controller conversion | 413 | // Handheld controller conversion |
| 347 | if (controller_pos == 32) { | 414 | if (controller_pos == NPAD_HANDHELD) { |
| 348 | controller_pos = 8; | 415 | controller_pos = 8; |
| 349 | } | 416 | } |
| 350 | // Unknown controller conversion | 417 | // Unknown controller conversion |
| 351 | if (controller_pos == 16) { | 418 | if (controller_pos == NPAD_UNKNOWN) { |
| 352 | controller_pos = 9; | 419 | controller_pos = 9; |
| 353 | } | 420 | } |
| 354 | if (connected_controllers[controller_pos].is_connected) { | 421 | if (connected_controllers[controller_pos].is_connected) { |
| @@ -426,4 +493,128 @@ Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) { | |||
| 426 | void Controller_NPad::SetVibrationEnabled(bool can_vibrate) { | 493 | void Controller_NPad::SetVibrationEnabled(bool can_vibrate) { |
| 427 | can_controllers_vibrate = can_vibrate; | 494 | can_controllers_vibrate = can_vibrate; |
| 428 | } | 495 | } |
| 496 | |||
| 497 | bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const { | ||
| 498 | const bool support_handheld = | ||
| 499 | std::find(supported_npad_id_types.begin(), supported_npad_id_types.end(), NPAD_HANDHELD) != | ||
| 500 | supported_npad_id_types.end(); | ||
| 501 | if (controller == NPadControllerType::Handheld) { | ||
| 502 | // Handheld is not even a supported type, lets stop here | ||
| 503 | if (!support_handheld) { | ||
| 504 | return false; | ||
| 505 | } | ||
| 506 | // Handheld should not be supported in docked mode | ||
| 507 | if (Settings::values.use_docked_mode) { | ||
| 508 | return false; | ||
| 509 | } | ||
| 510 | |||
| 511 | return true; | ||
| 512 | } | ||
| 513 | if (std::any_of(supported_npad_id_types.begin(), supported_npad_id_types.end(), | ||
| 514 | [](u32 npad_id) { return npad_id <= MAX_NPAD_ID; })) { | ||
| 515 | switch (controller) { | ||
| 516 | case NPadControllerType::ProController: | ||
| 517 | return style.pro_controller; | ||
| 518 | case NPadControllerType::JoyDual: | ||
| 519 | return style.joycon_dual; | ||
| 520 | case NPadControllerType::JoyLeft: | ||
| 521 | return style.joycon_left; | ||
| 522 | case NPadControllerType::JoyRight: | ||
| 523 | return style.joycon_right; | ||
| 524 | case NPadControllerType::Pokeball: | ||
| 525 | return style.pokeball; | ||
| 526 | default: | ||
| 527 | return false; | ||
| 528 | } | ||
| 529 | } | ||
| 530 | return false; | ||
| 531 | } | ||
| 532 | |||
| 533 | Controller_NPad::NPadControllerType Controller_NPad::DecideBestController( | ||
| 534 | NPadControllerType priority) const { | ||
| 535 | if (IsControllerSupported(priority)) { | ||
| 536 | return priority; | ||
| 537 | } | ||
| 538 | const auto is_docked = Settings::values.use_docked_mode; | ||
| 539 | if (is_docked && priority == NPadControllerType::Handheld) { | ||
| 540 | priority = NPadControllerType::JoyDual; | ||
| 541 | if (IsControllerSupported(priority)) { | ||
| 542 | return priority; | ||
| 543 | } | ||
| 544 | } | ||
| 545 | std::vector<NPadControllerType> priority_list; | ||
| 546 | switch (priority) { | ||
| 547 | case NPadControllerType::ProController: | ||
| 548 | priority_list.push_back(NPadControllerType::JoyDual); | ||
| 549 | if (!is_docked) { | ||
| 550 | priority_list.push_back(NPadControllerType::Handheld); | ||
| 551 | } | ||
| 552 | priority_list.push_back(NPadControllerType::JoyLeft); | ||
| 553 | priority_list.push_back(NPadControllerType::JoyRight); | ||
| 554 | priority_list.push_back(NPadControllerType::Pokeball); | ||
| 555 | break; | ||
| 556 | case NPadControllerType::Handheld: | ||
| 557 | priority_list.push_back(NPadControllerType::JoyDual); | ||
| 558 | priority_list.push_back(NPadControllerType::ProController); | ||
| 559 | priority_list.push_back(NPadControllerType::JoyLeft); | ||
| 560 | priority_list.push_back(NPadControllerType::JoyRight); | ||
| 561 | priority_list.push_back(NPadControllerType::Pokeball); | ||
| 562 | break; | ||
| 563 | case NPadControllerType::JoyDual: | ||
| 564 | if (!is_docked) { | ||
| 565 | priority_list.push_back(NPadControllerType::Handheld); | ||
| 566 | } | ||
| 567 | priority_list.push_back(NPadControllerType::ProController); | ||
| 568 | priority_list.push_back(NPadControllerType::JoyLeft); | ||
| 569 | priority_list.push_back(NPadControllerType::JoyRight); | ||
| 570 | priority_list.push_back(NPadControllerType::Pokeball); | ||
| 571 | break; | ||
| 572 | case NPadControllerType::JoyLeft: | ||
| 573 | priority_list.push_back(NPadControllerType::JoyRight); | ||
| 574 | priority_list.push_back(NPadControllerType::JoyDual); | ||
| 575 | if (!is_docked) { | ||
| 576 | priority_list.push_back(NPadControllerType::Handheld); | ||
| 577 | } | ||
| 578 | priority_list.push_back(NPadControllerType::ProController); | ||
| 579 | priority_list.push_back(NPadControllerType::Pokeball); | ||
| 580 | break; | ||
| 581 | case NPadControllerType::JoyRight: | ||
| 582 | priority_list.push_back(NPadControllerType::JoyLeft); | ||
| 583 | priority_list.push_back(NPadControllerType::JoyDual); | ||
| 584 | if (!is_docked) { | ||
| 585 | priority_list.push_back(NPadControllerType::Handheld); | ||
| 586 | } | ||
| 587 | priority_list.push_back(NPadControllerType::ProController); | ||
| 588 | priority_list.push_back(NPadControllerType::Pokeball); | ||
| 589 | break; | ||
| 590 | case NPadControllerType::Pokeball: | ||
| 591 | priority_list.push_back(NPadControllerType::JoyLeft); | ||
| 592 | priority_list.push_back(NPadControllerType::JoyRight); | ||
| 593 | priority_list.push_back(NPadControllerType::JoyDual); | ||
| 594 | if (!is_docked) { | ||
| 595 | priority_list.push_back(NPadControllerType::Handheld); | ||
| 596 | } | ||
| 597 | priority_list.push_back(NPadControllerType::ProController); | ||
| 598 | break; | ||
| 599 | default: | ||
| 600 | priority_list.push_back(NPadControllerType::JoyDual); | ||
| 601 | if (!is_docked) { | ||
| 602 | priority_list.push_back(NPadControllerType::Handheld); | ||
| 603 | } | ||
| 604 | priority_list.push_back(NPadControllerType::ProController); | ||
| 605 | priority_list.push_back(NPadControllerType::JoyLeft); | ||
| 606 | priority_list.push_back(NPadControllerType::JoyRight); | ||
| 607 | priority_list.push_back(NPadControllerType::JoyDual); | ||
| 608 | } | ||
| 609 | |||
| 610 | const auto iter = std::find_if(priority_list.begin(), priority_list.end(), | ||
| 611 | [this](auto type) { return IsControllerSupported(type); }); | ||
| 612 | if (iter == priority_list.end()) { | ||
| 613 | UNIMPLEMENTED_MSG("Could not find supported controller!"); | ||
| 614 | return priority; | ||
| 615 | } | ||
| 616 | |||
| 617 | return *iter; | ||
| 618 | } | ||
| 619 | |||
| 429 | } // namespace Service::HID | 620 | } // namespace Service::HID |
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h index 9d07d258d..ac86985ff 100644 --- a/src/core/hle/service/hid/controllers/npad.h +++ b/src/core/hle/service/hid/controllers/npad.h | |||
| @@ -15,6 +15,7 @@ namespace Service::HID { | |||
| 15 | class Controller_NPad final : public ControllerBase { | 15 | class Controller_NPad final : public ControllerBase { |
| 16 | public: | 16 | public: |
| 17 | Controller_NPad(); | 17 | Controller_NPad(); |
| 18 | ~Controller_NPad() override; | ||
| 18 | 19 | ||
| 19 | // Called when the controller is initialized | 20 | // Called when the controller is initialized |
| 20 | void OnInit() override; | 21 | void OnInit() override; |
| @@ -77,7 +78,7 @@ public: | |||
| 77 | position1.Assign(light2); | 78 | position1.Assign(light2); |
| 78 | position1.Assign(light3); | 79 | position1.Assign(light3); |
| 79 | position1.Assign(light4); | 80 | position1.Assign(light4); |
| 80 | }; | 81 | } |
| 81 | union { | 82 | union { |
| 82 | u64 raw{}; | 83 | u64 raw{}; |
| 83 | BitField<0, 1, u64> position1; | 84 | BitField<0, 1, u64> position1; |
| @@ -91,7 +92,7 @@ public: | |||
| 91 | NPadType GetSupportedStyleSet() const; | 92 | NPadType GetSupportedStyleSet() const; |
| 92 | 93 | ||
| 93 | void SetSupportedNPadIdTypes(u8* data, std::size_t length); | 94 | void SetSupportedNPadIdTypes(u8* data, std::size_t length); |
| 94 | const void GetSupportedNpadIdTypes(u32* data, std::size_t max_length); | 95 | void GetSupportedNpadIdTypes(u32* data, std::size_t max_length); |
| 95 | std::size_t GetSupportedNPadIdTypesSize() const; | 96 | std::size_t GetSupportedNPadIdTypesSize() const; |
| 96 | 97 | ||
| 97 | void SetHoldType(NpadHoldType joy_hold_type); | 98 | void SetHoldType(NpadHoldType joy_hold_type); |
| @@ -277,12 +278,12 @@ private: | |||
| 277 | std::vector<u32> supported_npad_id_types{}; | 278 | std::vector<u32> supported_npad_id_types{}; |
| 278 | NpadHoldType hold_type{NpadHoldType::Vertical}; | 279 | NpadHoldType hold_type{NpadHoldType::Vertical}; |
| 279 | Kernel::SharedPtr<Kernel::Event> styleset_changed_event; | 280 | Kernel::SharedPtr<Kernel::Event> styleset_changed_event; |
| 280 | std::size_t dump_idx{}; | ||
| 281 | Vibration last_processed_vibration{}; | 281 | Vibration last_processed_vibration{}; |
| 282 | static constexpr std::array<u32, 10> npad_id_list{0, 1, 2, 3, 4, 5, 6, 7, 32, 16}; | ||
| 283 | std::array<ControllerHolder, 10> connected_controllers{}; | 282 | std::array<ControllerHolder, 10> connected_controllers{}; |
| 284 | bool can_controllers_vibrate{true}; | 283 | bool can_controllers_vibrate{true}; |
| 285 | 284 | ||
| 286 | void InitNewlyAddedControler(std::size_t controller_idx); | 285 | void InitNewlyAddedControler(std::size_t controller_idx); |
| 286 | bool IsControllerSupported(NPadControllerType controller) const; | ||
| 287 | NPadControllerType DecideBestController(NPadControllerType priority) const; | ||
| 287 | }; | 288 | }; |
| 288 | } // namespace Service::HID | 289 | } // namespace Service::HID |
diff --git a/src/core/hle/service/hid/controllers/stubbed.cpp b/src/core/hle/service/hid/controllers/stubbed.cpp index 3a13d5991..02fcfadd9 100644 --- a/src/core/hle/service/hid/controllers/stubbed.cpp +++ b/src/core/hle/service/hid/controllers/stubbed.cpp | |||
| @@ -4,13 +4,13 @@ | |||
| 4 | 4 | ||
| 5 | #include <cstring> | 5 | #include <cstring> |
| 6 | #include "common/common_types.h" | 6 | #include "common/common_types.h" |
| 7 | #include "common/swap.h" | ||
| 8 | #include "core/core_timing.h" | 7 | #include "core/core_timing.h" |
| 9 | #include "core/hle/service/hid/controllers/stubbed.h" | 8 | #include "core/hle/service/hid/controllers/stubbed.h" |
| 10 | 9 | ||
| 11 | namespace Service::HID { | 10 | namespace Service::HID { |
| 12 | 11 | ||
| 13 | Controller_Stubbed::Controller_Stubbed() = default; | 12 | Controller_Stubbed::Controller_Stubbed() = default; |
| 13 | Controller_Stubbed::~Controller_Stubbed() = default; | ||
| 14 | 14 | ||
| 15 | void Controller_Stubbed::OnInit() {} | 15 | void Controller_Stubbed::OnInit() {} |
| 16 | 16 | ||
diff --git a/src/core/hle/service/hid/controllers/stubbed.h b/src/core/hle/service/hid/controllers/stubbed.h index 9c1b57f83..4a21c643e 100644 --- a/src/core/hle/service/hid/controllers/stubbed.h +++ b/src/core/hle/service/hid/controllers/stubbed.h | |||
| @@ -11,6 +11,7 @@ namespace Service::HID { | |||
| 11 | class Controller_Stubbed final : public ControllerBase { | 11 | class Controller_Stubbed final : public ControllerBase { |
| 12 | public: | 12 | public: |
| 13 | Controller_Stubbed(); | 13 | Controller_Stubbed(); |
| 14 | ~Controller_Stubbed() override; | ||
| 14 | 15 | ||
| 15 | // Called when the controller is initialized | 16 | // Called when the controller is initialized |
| 16 | void OnInit() override; | 17 | void OnInit() override; |
diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp index e97f84ea1..43efef803 100644 --- a/src/core/hle/service/hid/controllers/touchscreen.cpp +++ b/src/core/hle/service/hid/controllers/touchscreen.cpp | |||
| @@ -4,7 +4,6 @@ | |||
| 4 | 4 | ||
| 5 | #include <cstring> | 5 | #include <cstring> |
| 6 | #include "common/common_types.h" | 6 | #include "common/common_types.h" |
| 7 | #include "common/swap.h" | ||
| 8 | #include "core/core_timing.h" | 7 | #include "core/core_timing.h" |
| 9 | #include "core/frontend/emu_window.h" | 8 | #include "core/frontend/emu_window.h" |
| 10 | #include "core/frontend/input.h" | 9 | #include "core/frontend/input.h" |
| @@ -15,6 +14,7 @@ namespace Service::HID { | |||
| 15 | constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400; | 14 | constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400; |
| 16 | 15 | ||
| 17 | Controller_Touchscreen::Controller_Touchscreen() = default; | 16 | Controller_Touchscreen::Controller_Touchscreen() = default; |
| 17 | Controller_Touchscreen::~Controller_Touchscreen() = default; | ||
| 18 | 18 | ||
| 19 | void Controller_Touchscreen::OnInit() {} | 19 | void Controller_Touchscreen::OnInit() {} |
| 20 | 20 | ||
diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h index 1d97b6c2a..e5db6e6ba 100644 --- a/src/core/hle/service/hid/controllers/touchscreen.h +++ b/src/core/hle/service/hid/controllers/touchscreen.h | |||
| @@ -14,6 +14,7 @@ namespace Service::HID { | |||
| 14 | class Controller_Touchscreen final : public ControllerBase { | 14 | class Controller_Touchscreen final : public ControllerBase { |
| 15 | public: | 15 | public: |
| 16 | Controller_Touchscreen(); | 16 | Controller_Touchscreen(); |
| 17 | ~Controller_Touchscreen() override; | ||
| 17 | 18 | ||
| 18 | // Called when the controller is initialized | 19 | // Called when the controller is initialized |
| 19 | void OnInit() override; | 20 | void OnInit() override; |
diff --git a/src/core/hle/service/hid/controllers/xpad.cpp b/src/core/hle/service/hid/controllers/xpad.cpp index df0b48451..cd397c70b 100644 --- a/src/core/hle/service/hid/controllers/xpad.cpp +++ b/src/core/hle/service/hid/controllers/xpad.cpp | |||
| @@ -4,7 +4,6 @@ | |||
| 4 | 4 | ||
| 5 | #include <cstring> | 5 | #include <cstring> |
| 6 | #include "common/common_types.h" | 6 | #include "common/common_types.h" |
| 7 | #include "common/swap.h" | ||
| 8 | #include "core/core_timing.h" | 7 | #include "core/core_timing.h" |
| 9 | #include "core/hle/service/hid/controllers/xpad.h" | 8 | #include "core/hle/service/hid/controllers/xpad.h" |
| 10 | 9 | ||
| @@ -12,6 +11,7 @@ namespace Service::HID { | |||
| 12 | constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C00; | 11 | constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C00; |
| 13 | 12 | ||
| 14 | Controller_XPad::Controller_XPad() = default; | 13 | Controller_XPad::Controller_XPad() = default; |
| 14 | Controller_XPad::~Controller_XPad() = default; | ||
| 15 | 15 | ||
| 16 | void Controller_XPad::OnInit() {} | 16 | void Controller_XPad::OnInit() {} |
| 17 | 17 | ||
diff --git a/src/core/hle/service/hid/controllers/xpad.h b/src/core/hle/service/hid/controllers/xpad.h index e2007183d..ff836989f 100644 --- a/src/core/hle/service/hid/controllers/xpad.h +++ b/src/core/hle/service/hid/controllers/xpad.h | |||
| @@ -7,13 +7,13 @@ | |||
| 7 | #include "common/common_funcs.h" | 7 | #include "common/common_funcs.h" |
| 8 | #include "common/common_types.h" | 8 | #include "common/common_types.h" |
| 9 | #include "common/swap.h" | 9 | #include "common/swap.h" |
| 10 | #include "core/frontend/input.h" | ||
| 11 | #include "core/hle/service/hid/controllers/controller_base.h" | 10 | #include "core/hle/service/hid/controllers/controller_base.h" |
| 12 | 11 | ||
| 13 | namespace Service::HID { | 12 | namespace Service::HID { |
| 14 | class Controller_XPad final : public ControllerBase { | 13 | class Controller_XPad final : public ControllerBase { |
| 15 | public: | 14 | public: |
| 16 | Controller_XPad(); | 15 | Controller_XPad(); |
| 16 | ~Controller_XPad() override; | ||
| 17 | 17 | ||
| 18 | // Called when the controller is initialized | 18 | // Called when the controller is initialized |
| 19 | void OnInit() override; | 19 | void OnInit() override; |
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 8aca0f197..a9aa9ec78 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp | |||
| @@ -177,6 +177,7 @@ public: | |||
| 177 | {11, &Hid::ActivateTouchScreen, "ActivateTouchScreen"}, | 177 | {11, &Hid::ActivateTouchScreen, "ActivateTouchScreen"}, |
| 178 | {21, &Hid::ActivateMouse, "ActivateMouse"}, | 178 | {21, &Hid::ActivateMouse, "ActivateMouse"}, |
| 179 | {31, &Hid::ActivateKeyboard, "ActivateKeyboard"}, | 179 | {31, &Hid::ActivateKeyboard, "ActivateKeyboard"}, |
| 180 | {32, nullptr, "SendKeyboardLockKeyEvent"}, | ||
| 180 | {40, nullptr, "AcquireXpadIdEventHandle"}, | 181 | {40, nullptr, "AcquireXpadIdEventHandle"}, |
| 181 | {41, nullptr, "ReleaseXpadIdEventHandle"}, | 182 | {41, nullptr, "ReleaseXpadIdEventHandle"}, |
| 182 | {51, &Hid::ActivateXpad, "ActivateXpad"}, | 183 | {51, &Hid::ActivateXpad, "ActivateXpad"}, |
| @@ -207,6 +208,7 @@ public: | |||
| 207 | {80, nullptr, "GetGyroscopeZeroDriftMode"}, | 208 | {80, nullptr, "GetGyroscopeZeroDriftMode"}, |
| 208 | {81, nullptr, "ResetGyroscopeZeroDriftMode"}, | 209 | {81, nullptr, "ResetGyroscopeZeroDriftMode"}, |
| 209 | {82, &Hid::IsSixAxisSensorAtRest, "IsSixAxisSensorAtRest"}, | 210 | {82, &Hid::IsSixAxisSensorAtRest, "IsSixAxisSensorAtRest"}, |
| 211 | {83, nullptr, "IsFirmwareUpdateAvailableForSixAxisSensor"}, | ||
| 210 | {91, &Hid::ActivateGesture, "ActivateGesture"}, | 212 | {91, &Hid::ActivateGesture, "ActivateGesture"}, |
| 211 | {100, &Hid::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"}, | 213 | {100, &Hid::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"}, |
| 212 | {101, &Hid::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"}, | 214 | {101, &Hid::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"}, |
| @@ -252,6 +254,7 @@ public: | |||
| 252 | {307, nullptr, "FinalizeSevenSixAxisSensor"}, | 254 | {307, nullptr, "FinalizeSevenSixAxisSensor"}, |
| 253 | {308, nullptr, "SetSevenSixAxisSensorFusionStrength"}, | 255 | {308, nullptr, "SetSevenSixAxisSensorFusionStrength"}, |
| 254 | {309, nullptr, "GetSevenSixAxisSensorFusionStrength"}, | 256 | {309, nullptr, "GetSevenSixAxisSensorFusionStrength"}, |
| 257 | {310, nullptr, "ResetSevenSixAxisSensorTimestamp"}, | ||
| 255 | {400, nullptr, "IsUsbFullKeyControllerEnabled"}, | 258 | {400, nullptr, "IsUsbFullKeyControllerEnabled"}, |
| 256 | {401, nullptr, "EnableUsbFullKeyController"}, | 259 | {401, nullptr, "EnableUsbFullKeyController"}, |
| 257 | {402, nullptr, "IsUsbFullKeyControllerConnected"}, | 260 | {402, nullptr, "IsUsbFullKeyControllerConnected"}, |
| @@ -267,12 +270,24 @@ public: | |||
| 267 | {505, nullptr, "SetPalmaFrModeType"}, | 270 | {505, nullptr, "SetPalmaFrModeType"}, |
| 268 | {506, nullptr, "ReadPalmaStep"}, | 271 | {506, nullptr, "ReadPalmaStep"}, |
| 269 | {507, nullptr, "EnablePalmaStep"}, | 272 | {507, nullptr, "EnablePalmaStep"}, |
| 270 | {508, nullptr, "SuspendPalmaStep"}, | 273 | {508, nullptr, "ResetPalmaStep"}, |
| 271 | {509, nullptr, "ResetPalmaStep"}, | 274 | {509, nullptr, "ReadPalmaApplicationSection"}, |
| 272 | {510, nullptr, "ReadPalmaApplicationSection"}, | 275 | {510, nullptr, "WritePalmaApplicationSection"}, |
| 273 | {511, nullptr, "WritePalmaApplicationSection"}, | 276 | {511, nullptr, "ReadPalmaUniqueCode"}, |
| 274 | {512, nullptr, "ReadPalmaUniqueCode"}, | 277 | {512, nullptr, "SetPalmaUniqueCodeInvalid"}, |
| 275 | {513, nullptr, "SetPalmaUniqueCodeInvalid"}, | 278 | {513, nullptr, "WritePalmaActivityEntry"}, |
| 279 | {514, nullptr, "WritePalmaRgbLedPatternEntry"}, | ||
| 280 | {515, nullptr, "WritePalmaWaveEntry"}, | ||
| 281 | {516, nullptr, "SetPalmaDataBaseIdentificationVersion"}, | ||
| 282 | {517, nullptr, "GetPalmaDataBaseIdentificationVersion"}, | ||
| 283 | {518, nullptr, "SuspendPalmaFeature"}, | ||
| 284 | {519, nullptr, "GetPalmaOperationResult"}, | ||
| 285 | {520, nullptr, "ReadPalmaPlayLog"}, | ||
| 286 | {521, nullptr, "ResetPalmaPlayLog"}, | ||
| 287 | {522, nullptr, "SetIsPalmaAllConnectable"}, | ||
| 288 | {523, nullptr, "SetIsPalmaPairedConnectable"}, | ||
| 289 | {524, nullptr, "PairPalma"}, | ||
| 290 | {525, nullptr, "SetPalmaBoostMode"}, | ||
| 276 | {1000, nullptr, "SetNpadCommunicationMode"}, | 291 | {1000, nullptr, "SetNpadCommunicationMode"}, |
| 277 | {1001, nullptr, "GetNpadCommunicationMode"}, | 292 | {1001, nullptr, "GetNpadCommunicationMode"}, |
| 278 | }; | 293 | }; |
| @@ -620,6 +635,7 @@ public: | |||
| 620 | {140, nullptr, "DeactivateConsoleSixAxisSensor"}, | 635 | {140, nullptr, "DeactivateConsoleSixAxisSensor"}, |
| 621 | {141, nullptr, "GetConsoleSixAxisSensorSamplingFrequency"}, | 636 | {141, nullptr, "GetConsoleSixAxisSensorSamplingFrequency"}, |
| 622 | {142, nullptr, "DeactivateSevenSixAxisSensor"}, | 637 | {142, nullptr, "DeactivateSevenSixAxisSensor"}, |
| 638 | {143, nullptr, "GetConsoleSixAxisSensorCountStates"}, | ||
| 623 | {201, nullptr, "ActivateFirmwareUpdate"}, | 639 | {201, nullptr, "ActivateFirmwareUpdate"}, |
| 624 | {202, nullptr, "DeactivateFirmwareUpdate"}, | 640 | {202, nullptr, "DeactivateFirmwareUpdate"}, |
| 625 | {203, nullptr, "StartFirmwareUpdate"}, | 641 | {203, nullptr, "StartFirmwareUpdate"}, |
| @@ -630,12 +646,23 @@ public: | |||
| 630 | {208, nullptr, "StartFirmwareUpdateForRevert"}, | 646 | {208, nullptr, "StartFirmwareUpdateForRevert"}, |
| 631 | {209, nullptr, "GetAvailableFirmwareVersionForRevert"}, | 647 | {209, nullptr, "GetAvailableFirmwareVersionForRevert"}, |
| 632 | {210, nullptr, "IsFirmwareUpdatingDevice"}, | 648 | {210, nullptr, "IsFirmwareUpdatingDevice"}, |
| 649 | {211, nullptr, "StartFirmwareUpdateIndividual"}, | ||
| 650 | {215, nullptr, "SetUsbFirmwareForceUpdateEnabled"}, | ||
| 651 | {216, nullptr, "SetAllKuinaDevicesToFirmwareUpdateMode"}, | ||
| 633 | {221, nullptr, "UpdateControllerColor"}, | 652 | {221, nullptr, "UpdateControllerColor"}, |
| 634 | {222, nullptr, "ConnectUsbPadsAsync"}, | 653 | {222, nullptr, "ConnectUsbPadsAsync"}, |
| 635 | {223, nullptr, "DisconnectUsbPadsAsync"}, | 654 | {223, nullptr, "DisconnectUsbPadsAsync"}, |
| 636 | {224, nullptr, "UpdateDesignInfo"}, | 655 | {224, nullptr, "UpdateDesignInfo"}, |
| 637 | {225, nullptr, "GetUniquePadDriverState"}, | 656 | {225, nullptr, "GetUniquePadDriverState"}, |
| 638 | {226, nullptr, "GetSixAxisSensorDriverStates"}, | 657 | {226, nullptr, "GetSixAxisSensorDriverStates"}, |
| 658 | {227, nullptr, "GetRxPacketHistory"}, | ||
| 659 | {228, nullptr, "AcquireOperationEventHandle"}, | ||
| 660 | {229, nullptr, "ReadSerialFlash"}, | ||
| 661 | {230, nullptr, "WriteSerialFlash"}, | ||
| 662 | {231, nullptr, "GetOperationResult"}, | ||
| 663 | {232, nullptr, "EnableShipmentMode"}, | ||
| 664 | {233, nullptr, "ClearPairingInfo"}, | ||
| 665 | {234, nullptr, "GetUniquePadDeviceTypeSetInternal"}, | ||
| 639 | {301, nullptr, "GetAbstractedPadHandles"}, | 666 | {301, nullptr, "GetAbstractedPadHandles"}, |
| 640 | {302, nullptr, "GetAbstractedPadState"}, | 667 | {302, nullptr, "GetAbstractedPadState"}, |
| 641 | {303, nullptr, "GetAbstractedPadsState"}, | 668 | {303, nullptr, "GetAbstractedPadsState"}, |
| @@ -643,6 +670,8 @@ public: | |||
| 643 | {322, nullptr, "UnsetAutoPilotVirtualPadState"}, | 670 | {322, nullptr, "UnsetAutoPilotVirtualPadState"}, |
| 644 | {323, nullptr, "UnsetAllAutoPilotVirtualPadState"}, | 671 | {323, nullptr, "UnsetAllAutoPilotVirtualPadState"}, |
| 645 | {350, nullptr, "AddRegisteredDevice"}, | 672 | {350, nullptr, "AddRegisteredDevice"}, |
| 673 | {400, nullptr, "DisableExternalMcuOnNxDevice"}, | ||
| 674 | {401, nullptr, "DisableRailDeviceFiltering"}, | ||
| 646 | }; | 675 | }; |
| 647 | // clang-format on | 676 | // clang-format on |
| 648 | 677 | ||
| @@ -678,7 +707,9 @@ public: | |||
| 678 | {307, nullptr, "GetNpadSystemExtStyle"}, | 707 | {307, nullptr, "GetNpadSystemExtStyle"}, |
| 679 | {308, nullptr, "ApplyNpadSystemCommonPolicyFull"}, | 708 | {308, nullptr, "ApplyNpadSystemCommonPolicyFull"}, |
| 680 | {309, nullptr, "GetNpadFullKeyGripColor"}, | 709 | {309, nullptr, "GetNpadFullKeyGripColor"}, |
| 710 | {310, nullptr, "GetMaskedSupportedNpadStyleSet"}, | ||
| 681 | {311, nullptr, "SetNpadPlayerLedBlinkingDevice"}, | 711 | {311, nullptr, "SetNpadPlayerLedBlinkingDevice"}, |
| 712 | {312, nullptr, "SetSupportedNpadStyleSetAll"}, | ||
| 682 | {321, nullptr, "GetUniquePadsFromNpad"}, | 713 | {321, nullptr, "GetUniquePadsFromNpad"}, |
| 683 | {322, nullptr, "GetIrSensorState"}, | 714 | {322, nullptr, "GetIrSensorState"}, |
| 684 | {323, nullptr, "GetXcdHandleForNpadWithIrSensor"}, | 715 | {323, nullptr, "GetXcdHandleForNpadWithIrSensor"}, |
| @@ -703,6 +734,7 @@ public: | |||
| 703 | {546, nullptr, "AcquireDeviceRegisteredEventForControllerSupport"}, | 734 | {546, nullptr, "AcquireDeviceRegisteredEventForControllerSupport"}, |
| 704 | {547, nullptr, "GetAllowedBluetoothLinksCount"}, | 735 | {547, nullptr, "GetAllowedBluetoothLinksCount"}, |
| 705 | {548, nullptr, "GetRegisteredDevices"}, | 736 | {548, nullptr, "GetRegisteredDevices"}, |
| 737 | {549, nullptr, "GetConnectableRegisteredDevices"}, | ||
| 706 | {700, nullptr, "ActivateUniquePad"}, | 738 | {700, nullptr, "ActivateUniquePad"}, |
| 707 | {702, nullptr, "AcquireUniquePadConnectionEventHandle"}, | 739 | {702, nullptr, "AcquireUniquePadConnectionEventHandle"}, |
| 708 | {703, nullptr, "GetUniquePadIds"}, | 740 | {703, nullptr, "GetUniquePadIds"}, |
| @@ -731,6 +763,7 @@ public: | |||
| 731 | {850, nullptr, "IsUsbFullKeyControllerEnabled"}, | 763 | {850, nullptr, "IsUsbFullKeyControllerEnabled"}, |
| 732 | {851, nullptr, "EnableUsbFullKeyController"}, | 764 | {851, nullptr, "EnableUsbFullKeyController"}, |
| 733 | {852, nullptr, "IsUsbConnected"}, | 765 | {852, nullptr, "IsUsbConnected"}, |
| 766 | {870, nullptr, "IsHandheldButtonPressedOnConsoleMode"}, | ||
| 734 | {900, nullptr, "ActivateInputDetector"}, | 767 | {900, nullptr, "ActivateInputDetector"}, |
| 735 | {901, nullptr, "NotifyInputDetector"}, | 768 | {901, nullptr, "NotifyInputDetector"}, |
| 736 | {1000, nullptr, "InitializeFirmwareUpdate"}, | 769 | {1000, nullptr, "InitializeFirmwareUpdate"}, |
| @@ -750,6 +783,12 @@ public: | |||
| 750 | {1052, nullptr, "CancelSixAxisSensorAccurateUserCalibration"}, | 783 | {1052, nullptr, "CancelSixAxisSensorAccurateUserCalibration"}, |
| 751 | {1053, nullptr, "GetSixAxisSensorAccurateUserCalibrationState"}, | 784 | {1053, nullptr, "GetSixAxisSensorAccurateUserCalibrationState"}, |
| 752 | {1100, nullptr, "GetHidbusSystemServiceObject"}, | 785 | {1100, nullptr, "GetHidbusSystemServiceObject"}, |
| 786 | {1120, nullptr, "SetFirmwareHotfixUpdateSkipEnabled"}, | ||
| 787 | {1130, nullptr, "InitializeUsbFirmwareUpdate"}, | ||
| 788 | {1131, nullptr, "FinalizeUsbFirmwareUpdate"}, | ||
| 789 | {1132, nullptr, "CheckUsbFirmwareUpdateRequired"}, | ||
| 790 | {1133, nullptr, "StartUsbFirmwareUpdate"}, | ||
| 791 | {1134, nullptr, "GetUsbFirmwareUpdateState"}, | ||
| 753 | }; | 792 | }; |
| 754 | // clang-format on | 793 | // clang-format on |
| 755 | 794 | ||
| @@ -788,6 +827,7 @@ public: | |||
| 788 | {11, nullptr, "EnableJoyPollingReceiveMode"}, | 827 | {11, nullptr, "EnableJoyPollingReceiveMode"}, |
| 789 | {12, nullptr, "DisableJoyPollingReceiveMode"}, | 828 | {12, nullptr, "DisableJoyPollingReceiveMode"}, |
| 790 | {13, nullptr, "GetPollingData"}, | 829 | {13, nullptr, "GetPollingData"}, |
| 830 | {14, nullptr, "SetStatusManagerType"}, | ||
| 791 | }; | 831 | }; |
| 792 | // clang-format on | 832 | // clang-format on |
| 793 | 833 | ||
diff --git a/src/core/hle/service/lbl/lbl.cpp b/src/core/hle/service/lbl/lbl.cpp index 7321584e1..164c57e18 100644 --- a/src/core/hle/service/lbl/lbl.cpp +++ b/src/core/hle/service/lbl/lbl.cpp | |||
| @@ -18,35 +18,35 @@ public: | |||
| 18 | explicit LBL() : ServiceFramework{"lbl"} { | 18 | explicit LBL() : ServiceFramework{"lbl"} { |
| 19 | // clang-format off | 19 | // clang-format off |
| 20 | static const FunctionInfo functions[] = { | 20 | static const FunctionInfo functions[] = { |
| 21 | {0, nullptr, "Unknown1"}, | 21 | {0, nullptr, "SaveCurrentSetting"}, |
| 22 | {1, nullptr, "Unknown2"}, | 22 | {1, nullptr, "LoadCurrentSetting"}, |
| 23 | {2, nullptr, "Unknown3"}, | 23 | {2, nullptr, "SetCurrentBrightnessSetting"}, |
| 24 | {3, nullptr, "GetCurrentBacklightLevel"}, | 24 | {3, nullptr, "GetCurrentBrightnessSetting"}, |
| 25 | {4, nullptr, "Unknown4"}, | 25 | {4, nullptr, "ApplyCurrentBrightnessSettingToBacklight"}, |
| 26 | {5, nullptr, "GetAlsComputedBacklightLevel"}, | 26 | {5, nullptr, "GetBrightnessSettingAppliedToBacklight"}, |
| 27 | {6, nullptr, "TurnOffBacklight"}, | 27 | {6, nullptr, "SwitchBacklightOn"}, |
| 28 | {7, nullptr, "TurnOnBacklight"}, | 28 | {7, nullptr, "SwitchBacklightOff"}, |
| 29 | {8, nullptr, "GetBacklightStatus"}, | 29 | {8, nullptr, "GetBacklightSwitchStatus"}, |
| 30 | {9, nullptr, "Unknown5"}, | 30 | {9, nullptr, "EnableDimming"}, |
| 31 | {10, nullptr, "Unknown6"}, | 31 | {10, nullptr, "DisableDimming"}, |
| 32 | {11, nullptr, "Unknown7"}, | 32 | {11, nullptr, "IsDimmingEnabled"}, |
| 33 | {12, nullptr, "Unknown8"}, | 33 | {12, nullptr, "EnableAutoBrightnessControl"}, |
| 34 | {13, nullptr, "Unknown9"}, | 34 | {13, nullptr, "DisableAutoBrightnessControl"}, |
| 35 | {14, nullptr, "Unknown10"}, | 35 | {14, nullptr, "IsAutoBrightnessControlEnabled"}, |
| 36 | {15, nullptr, "GetAutoBrightnessSetting"}, | 36 | {15, nullptr, "SetAmbientLightSensorValue"}, |
| 37 | {16, nullptr, "ReadRawLightSensor"}, | 37 | {16, nullptr, "GetAmbientLightSensorValue"}, |
| 38 | {17, nullptr, "Unknown11"}, | 38 | {17, nullptr, "SetBrightnessReflectionDelayLevel"}, |
| 39 | {18, nullptr, "Unknown12"}, | 39 | {18, nullptr, "GetBrightnessReflectionDelayLevel"}, |
| 40 | {19, nullptr, "Unknown13"}, | 40 | {19, nullptr, "SetCurrentBrightnessMapping"}, |
| 41 | {20, nullptr, "Unknown14"}, | 41 | {20, nullptr, "GetCurrentBrightnessMapping"}, |
| 42 | {21, nullptr, "Unknown15"}, | 42 | {21, nullptr, "SetCurrentAmbientLightSensorMapping"}, |
| 43 | {22, nullptr, "Unknown16"}, | 43 | {22, nullptr, "GetCurrentAmbientLightSensorMapping"}, |
| 44 | {23, nullptr, "Unknown17"}, | 44 | {23, nullptr, "IsAmbientLightSensorAvailable"}, |
| 45 | {24, nullptr, "Unknown18"}, | 45 | {24, nullptr, "SetCurrentBrightnessSettingForVrMode"}, |
| 46 | {25, nullptr, "Unknown19"}, | 46 | {25, nullptr, "GetCurrentBrightnessSettingForVrMode"}, |
| 47 | {26, &LBL::EnableVrMode, "EnableVrMode"}, | 47 | {26, &LBL::EnableVrMode, "EnableVrMode"}, |
| 48 | {27, &LBL::DisableVrMode, "DisableVrMode"}, | 48 | {27, &LBL::DisableVrMode, "DisableVrMode"}, |
| 49 | {28, &LBL::GetVrMode, "GetVrMode"}, | 49 | {28, &LBL::IsVrModeEnabled, "IsVrModeEnabled"}, |
| 50 | }; | 50 | }; |
| 51 | // clang-format on | 51 | // clang-format on |
| 52 | 52 | ||
| @@ -72,7 +72,7 @@ private: | |||
| 72 | LOG_DEBUG(Service_LBL, "called"); | 72 | LOG_DEBUG(Service_LBL, "called"); |
| 73 | } | 73 | } |
| 74 | 74 | ||
| 75 | void GetVrMode(Kernel::HLERequestContext& ctx) { | 75 | void IsVrModeEnabled(Kernel::HLERequestContext& ctx) { |
| 76 | IPC::ResponseBuilder rb{ctx, 3}; | 76 | IPC::ResponseBuilder rb{ctx, 3}; |
| 77 | rb.Push(RESULT_SUCCESS); | 77 | rb.Push(RESULT_SUCCESS); |
| 78 | rb.Push(vr_mode_enabled); | 78 | rb.Push(vr_mode_enabled); |
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp index ec32faf15..d607d985e 100644 --- a/src/core/hle/service/ldr/ldr.cpp +++ b/src/core/hle/service/ldr/ldr.cpp | |||
| @@ -3,9 +3,13 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <memory> | 5 | #include <memory> |
| 6 | #include <fmt/format.h> | ||
| 6 | 7 | ||
| 8 | #include "core/hle/ipc_helpers.h" | ||
| 9 | #include "core/hle/kernel/process.h" | ||
| 7 | #include "core/hle/service/ldr/ldr.h" | 10 | #include "core/hle/service/ldr/ldr.h" |
| 8 | #include "core/hle/service/service.h" | 11 | #include "core/hle/service/service.h" |
| 12 | #include "core/loader/nro.h" | ||
| 9 | 13 | ||
| 10 | namespace Service::LDR { | 14 | namespace Service::LDR { |
| 11 | 15 | ||
| @@ -59,16 +63,58 @@ public: | |||
| 59 | explicit RelocatableObject() : ServiceFramework{"ldr:ro"} { | 63 | explicit RelocatableObject() : ServiceFramework{"ldr:ro"} { |
| 60 | // clang-format off | 64 | // clang-format off |
| 61 | static const FunctionInfo functions[] = { | 65 | static const FunctionInfo functions[] = { |
| 62 | {0, nullptr, "LoadNro"}, | 66 | {0, &RelocatableObject::LoadNro, "LoadNro"}, |
| 63 | {1, nullptr, "UnloadNro"}, | 67 | {1, nullptr, "UnloadNro"}, |
| 64 | {2, nullptr, "LoadNrr"}, | 68 | {2, &RelocatableObject::LoadNrr, "LoadNrr"}, |
| 65 | {3, nullptr, "UnloadNrr"}, | 69 | {3, nullptr, "UnloadNrr"}, |
| 66 | {4, nullptr, "Initialize"}, | 70 | {4, &RelocatableObject::Initialize, "Initialize"}, |
| 67 | }; | 71 | }; |
| 68 | // clang-format on | 72 | // clang-format on |
| 69 | 73 | ||
| 70 | RegisterHandlers(functions); | 74 | RegisterHandlers(functions); |
| 71 | } | 75 | } |
| 76 | |||
| 77 | void LoadNrr(Kernel::HLERequestContext& ctx) { | ||
| 78 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 79 | rb.Push(RESULT_SUCCESS); | ||
| 80 | LOG_WARNING(Service_LDR, "(STUBBED) called"); | ||
| 81 | } | ||
| 82 | |||
| 83 | void LoadNro(Kernel::HLERequestContext& ctx) { | ||
| 84 | IPC::RequestParser rp{ctx}; | ||
| 85 | rp.Skip(2, false); | ||
| 86 | const VAddr nro_addr{rp.Pop<VAddr>()}; | ||
| 87 | const u64 nro_size{rp.Pop<u64>()}; | ||
| 88 | const VAddr bss_addr{rp.Pop<VAddr>()}; | ||
| 89 | const u64 bss_size{rp.Pop<u64>()}; | ||
| 90 | |||
| 91 | // Read NRO data from memory | ||
| 92 | std::vector<u8> nro_data(nro_size); | ||
| 93 | Memory::ReadBlock(nro_addr, nro_data.data(), nro_size); | ||
| 94 | |||
| 95 | // Load NRO as new executable module | ||
| 96 | const VAddr addr{*Core::CurrentProcess()->VMManager().FindFreeRegion(nro_size + bss_size)}; | ||
| 97 | Loader::AppLoader_NRO::LoadNro(nro_data, fmt::format("nro-{:08x}", addr), addr); | ||
| 98 | |||
| 99 | // TODO(bunnei): This is an incomplete implementation. It was tested with Super Mario Party. | ||
| 100 | // It is currently missing: | ||
| 101 | // - Signature checks with LoadNRR | ||
| 102 | // - Checking if a module has already been loaded | ||
| 103 | // - Using/validating BSS, etc. params (these are used from NRO header instead) | ||
| 104 | // - Error checking | ||
| 105 | // - ...Probably other things | ||
| 106 | |||
| 107 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 108 | rb.Push(RESULT_SUCCESS); | ||
| 109 | rb.Push(addr); | ||
| 110 | LOG_WARNING(Service_LDR, "(STUBBED) called"); | ||
| 111 | } | ||
| 112 | |||
| 113 | void Initialize(Kernel::HLERequestContext& ctx) { | ||
| 114 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 115 | rb.Push(RESULT_SUCCESS); | ||
| 116 | LOG_WARNING(Service_LDR, "(STUBBED) called"); | ||
| 117 | } | ||
| 72 | }; | 118 | }; |
| 73 | 119 | ||
| 74 | void InstallInterfaces(SM::ServiceManager& sm) { | 120 | void InstallInterfaces(SM::ServiceManager& sm) { |
diff --git a/src/core/hle/service/nfc/nfc.cpp b/src/core/hle/service/nfc/nfc.cpp index 8fec97db8..30e542542 100644 --- a/src/core/hle/service/nfc/nfc.cpp +++ b/src/core/hle/service/nfc/nfc.cpp | |||
| @@ -10,12 +10,13 @@ | |||
| 10 | #include "core/hle/service/nfc/nfc.h" | 10 | #include "core/hle/service/nfc/nfc.h" |
| 11 | #include "core/hle/service/service.h" | 11 | #include "core/hle/service/service.h" |
| 12 | #include "core/hle/service/sm/sm.h" | 12 | #include "core/hle/service/sm/sm.h" |
| 13 | #include "core/settings.h" | ||
| 13 | 14 | ||
| 14 | namespace Service::NFC { | 15 | namespace Service::NFC { |
| 15 | 16 | ||
| 16 | class IAm final : public ServiceFramework<IAm> { | 17 | class IAm final : public ServiceFramework<IAm> { |
| 17 | public: | 18 | public: |
| 18 | explicit IAm() : ServiceFramework{"IAm"} { | 19 | explicit IAm() : ServiceFramework{"NFC::IAm"} { |
| 19 | // clang-format off | 20 | // clang-format off |
| 20 | static const FunctionInfo functions[] = { | 21 | static const FunctionInfo functions[] = { |
| 21 | {0, nullptr, "Initialize"}, | 22 | {0, nullptr, "Initialize"}, |
| @@ -52,7 +53,7 @@ private: | |||
| 52 | 53 | ||
| 53 | class MFIUser final : public ServiceFramework<MFIUser> { | 54 | class MFIUser final : public ServiceFramework<MFIUser> { |
| 54 | public: | 55 | public: |
| 55 | explicit MFIUser() : ServiceFramework{"IUser"} { | 56 | explicit MFIUser() : ServiceFramework{"NFC::MFIUser"} { |
| 56 | // clang-format off | 57 | // clang-format off |
| 57 | static const FunctionInfo functions[] = { | 58 | static const FunctionInfo functions[] = { |
| 58 | {0, nullptr, "Initialize"}, | 59 | {0, nullptr, "Initialize"}, |
| @@ -100,13 +101,13 @@ private: | |||
| 100 | 101 | ||
| 101 | class IUser final : public ServiceFramework<IUser> { | 102 | class IUser final : public ServiceFramework<IUser> { |
| 102 | public: | 103 | public: |
| 103 | explicit IUser() : ServiceFramework{"IUser"} { | 104 | explicit IUser() : ServiceFramework{"NFC::IUser"} { |
| 104 | // clang-format off | 105 | // clang-format off |
| 105 | static const FunctionInfo functions[] = { | 106 | static const FunctionInfo functions[] = { |
| 106 | {0, nullptr, "Initialize"}, | 107 | {0, &IUser::InitializeOld, "InitializeOld"}, |
| 107 | {1, nullptr, "Finalize"}, | 108 | {1, &IUser::FinalizeOld, "FinalizeOld"}, |
| 108 | {2, nullptr, "GetState"}, | 109 | {2, &IUser::GetStateOld, "GetStateOld"}, |
| 109 | {3, nullptr, "IsNfcEnabled"}, | 110 | {3, &IUser::IsNfcEnabledOld, "IsNfcEnabledOld"}, |
| 110 | {400, nullptr, "Initialize"}, | 111 | {400, nullptr, "Initialize"}, |
| 111 | {401, nullptr, "Finalize"}, | 112 | {401, nullptr, "Finalize"}, |
| 112 | {402, nullptr, "GetState"}, | 113 | {402, nullptr, "GetState"}, |
| @@ -130,11 +131,47 @@ public: | |||
| 130 | 131 | ||
| 131 | RegisterHandlers(functions); | 132 | RegisterHandlers(functions); |
| 132 | } | 133 | } |
| 134 | |||
| 135 | private: | ||
| 136 | enum class NfcStates : u32 { | ||
| 137 | Finalized = 6, | ||
| 138 | }; | ||
| 139 | |||
| 140 | void InitializeOld(Kernel::HLERequestContext& ctx) { | ||
| 141 | IPC::ResponseBuilder rb{ctx, 2, 0}; | ||
| 142 | rb.Push(RESULT_SUCCESS); | ||
| 143 | |||
| 144 | // We don't deal with hardware initialization so we can just stub this. | ||
| 145 | LOG_DEBUG(Service_NFC, "called"); | ||
| 146 | } | ||
| 147 | |||
| 148 | void IsNfcEnabledOld(Kernel::HLERequestContext& ctx) { | ||
| 149 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 150 | rb.Push(RESULT_SUCCESS); | ||
| 151 | rb.PushRaw<u8>(Settings::values.enable_nfc); | ||
| 152 | |||
| 153 | LOG_DEBUG(Service_NFC, "IsNfcEnabledOld"); | ||
| 154 | } | ||
| 155 | |||
| 156 | void GetStateOld(Kernel::HLERequestContext& ctx) { | ||
| 157 | LOG_WARNING(Service_NFC, "(STUBBED) called"); | ||
| 158 | |||
| 159 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 160 | rb.Push(RESULT_SUCCESS); | ||
| 161 | rb.PushEnum(NfcStates::Finalized); // TODO(ogniK): Figure out if this matches nfp | ||
| 162 | } | ||
| 163 | |||
| 164 | void FinalizeOld(Kernel::HLERequestContext& ctx) { | ||
| 165 | LOG_WARNING(Service_NFC, "(STUBBED) called"); | ||
| 166 | |||
| 167 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 168 | rb.Push(RESULT_SUCCESS); | ||
| 169 | } | ||
| 133 | }; | 170 | }; |
| 134 | 171 | ||
| 135 | class NFC_U final : public ServiceFramework<NFC_U> { | 172 | class NFC_U final : public ServiceFramework<NFC_U> { |
| 136 | public: | 173 | public: |
| 137 | explicit NFC_U() : ServiceFramework{"nfc:u"} { | 174 | explicit NFC_U() : ServiceFramework{"nfc:user"} { |
| 138 | // clang-format off | 175 | // clang-format off |
| 139 | static const FunctionInfo functions[] = { | 176 | static const FunctionInfo functions[] = { |
| 140 | {0, &NFC_U::CreateUserInterface, "CreateUserInterface"}, | 177 | {0, &NFC_U::CreateUserInterface, "CreateUserInterface"}, |
diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp index 39c0c1e63..c1af878fe 100644 --- a/src/core/hle/service/nfp/nfp.cpp +++ b/src/core/hle/service/nfp/nfp.cpp | |||
| @@ -2,56 +2,67 @@ | |||
| 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 <atomic> | ||
| 6 | |||
| 5 | #include "common/logging/log.h" | 7 | #include "common/logging/log.h" |
| 6 | #include "core/core.h" | 8 | #include "core/core.h" |
| 7 | #include "core/hle/ipc_helpers.h" | 9 | #include "core/hle/ipc_helpers.h" |
| 8 | #include "core/hle/kernel/event.h" | 10 | #include "core/hle/kernel/event.h" |
| 11 | #include "core/hle/lock.h" | ||
| 9 | #include "core/hle/service/hid/hid.h" | 12 | #include "core/hle/service/hid/hid.h" |
| 10 | #include "core/hle/service/nfp/nfp.h" | 13 | #include "core/hle/service/nfp/nfp.h" |
| 11 | #include "core/hle/service/nfp/nfp_user.h" | 14 | #include "core/hle/service/nfp/nfp_user.h" |
| 12 | 15 | ||
| 13 | namespace Service::NFP { | 16 | namespace Service::NFP { |
| 14 | 17 | ||
| 18 | namespace ErrCodes { | ||
| 19 | constexpr ResultCode ERR_TAG_FAILED(ErrorModule::NFP, | ||
| 20 | -1); // TODO(ogniK): Find the actual error code | ||
| 21 | } | ||
| 22 | |||
| 15 | Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) | 23 | Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) |
| 16 | : ServiceFramework(name), module(std::move(module)) {} | 24 | : ServiceFramework(name), module(std::move(module)) { |
| 25 | auto& kernel = Core::System::GetInstance().Kernel(); | ||
| 26 | nfc_tag_load = | ||
| 27 | Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IUser:NFCTagDetected"); | ||
| 28 | } | ||
| 17 | 29 | ||
| 18 | Module::Interface::~Interface() = default; | 30 | Module::Interface::~Interface() = default; |
| 19 | 31 | ||
| 20 | class IUser final : public ServiceFramework<IUser> { | 32 | class IUser final : public ServiceFramework<IUser> { |
| 21 | public: | 33 | public: |
| 22 | IUser() : ServiceFramework("IUser") { | 34 | IUser(Module::Interface& nfp_interface) |
| 35 | : ServiceFramework("NFP::IUser"), nfp_interface(nfp_interface) { | ||
| 23 | static const FunctionInfo functions[] = { | 36 | static const FunctionInfo functions[] = { |
| 24 | {0, &IUser::Initialize, "Initialize"}, | 37 | {0, &IUser::Initialize, "Initialize"}, |
| 25 | {1, nullptr, "Finalize"}, | 38 | {1, &IUser::Finalize, "Finalize"}, |
| 26 | {2, &IUser::ListDevices, "ListDevices"}, | 39 | {2, &IUser::ListDevices, "ListDevices"}, |
| 27 | {3, nullptr, "StartDetection"}, | 40 | {3, &IUser::StartDetection, "StartDetection"}, |
| 28 | {4, nullptr, "StopDetection"}, | 41 | {4, &IUser::StopDetection, "StopDetection"}, |
| 29 | {5, nullptr, "Mount"}, | 42 | {5, &IUser::Mount, "Mount"}, |
| 30 | {6, nullptr, "Unmount"}, | 43 | {6, &IUser::Unmount, "Unmount"}, |
| 31 | {7, nullptr, "OpenApplicationArea"}, | 44 | {7, &IUser::OpenApplicationArea, "OpenApplicationArea"}, |
| 32 | {8, nullptr, "GetApplicationArea"}, | 45 | {8, &IUser::GetApplicationArea, "GetApplicationArea"}, |
| 33 | {9, nullptr, "SetApplicationArea"}, | 46 | {9, nullptr, "SetApplicationArea"}, |
| 34 | {10, nullptr, "Flush"}, | 47 | {10, nullptr, "Flush"}, |
| 35 | {11, nullptr, "Restore"}, | 48 | {11, nullptr, "Restore"}, |
| 36 | {12, nullptr, "CreateApplicationArea"}, | 49 | {12, nullptr, "CreateApplicationArea"}, |
| 37 | {13, nullptr, "GetTagInfo"}, | 50 | {13, &IUser::GetTagInfo, "GetTagInfo"}, |
| 38 | {14, nullptr, "GetRegisterInfo"}, | 51 | {14, &IUser::GetRegisterInfo, "GetRegisterInfo"}, |
| 39 | {15, nullptr, "GetCommonInfo"}, | 52 | {15, &IUser::GetCommonInfo, "GetCommonInfo"}, |
| 40 | {16, nullptr, "GetModelInfo"}, | 53 | {16, &IUser::GetModelInfo, "GetModelInfo"}, |
| 41 | {17, &IUser::AttachActivateEvent, "AttachActivateEvent"}, | 54 | {17, &IUser::AttachActivateEvent, "AttachActivateEvent"}, |
| 42 | {18, &IUser::AttachDeactivateEvent, "AttachDeactivateEvent"}, | 55 | {18, &IUser::AttachDeactivateEvent, "AttachDeactivateEvent"}, |
| 43 | {19, &IUser::GetState, "GetState"}, | 56 | {19, &IUser::GetState, "GetState"}, |
| 44 | {20, &IUser::GetDeviceState, "GetDeviceState"}, | 57 | {20, &IUser::GetDeviceState, "GetDeviceState"}, |
| 45 | {21, &IUser::GetNpadId, "GetNpadId"}, | 58 | {21, &IUser::GetNpadId, "GetNpadId"}, |
| 46 | {22, nullptr, "GetApplicationArea2"}, | 59 | {22, &IUser::GetApplicationAreaSize, "GetApplicationAreaSize"}, |
| 47 | {23, &IUser::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"}, | 60 | {23, &IUser::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"}, |
| 48 | {24, nullptr, "RecreateApplicationArea"}, | 61 | {24, nullptr, "RecreateApplicationArea"}, |
| 49 | }; | 62 | }; |
| 50 | RegisterHandlers(functions); | 63 | RegisterHandlers(functions); |
| 51 | 64 | ||
| 52 | auto& kernel = Core::System::GetInstance().Kernel(); | 65 | auto& kernel = Core::System::GetInstance().Kernel(); |
| 53 | activate_event = | ||
| 54 | Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IUser:ActivateEvent"); | ||
| 55 | deactivate_event = | 66 | deactivate_event = |
| 56 | Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IUser:DeactivateEvent"); | 67 | Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IUser:DeactivateEvent"); |
| 57 | availability_change_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, | 68 | availability_change_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, |
| @@ -59,6 +70,17 @@ public: | |||
| 59 | } | 70 | } |
| 60 | 71 | ||
| 61 | private: | 72 | private: |
| 73 | struct TagInfo { | ||
| 74 | std::array<u8, 10> uuid; | ||
| 75 | u8 uuid_length; // TODO(ogniK): Figure out if this is actual the uuid length or does it | ||
| 76 | // mean something else | ||
| 77 | INSERT_PADDING_BYTES(0x15); | ||
| 78 | u32_le protocol; | ||
| 79 | u32_le tag_type; | ||
| 80 | INSERT_PADDING_BYTES(0x2c); | ||
| 81 | }; | ||
| 82 | static_assert(sizeof(TagInfo) == 0x54, "TagInfo is an invalid size"); | ||
| 83 | |||
| 62 | enum class State : u32 { | 84 | enum class State : u32 { |
| 63 | NonInitialized = 0, | 85 | NonInitialized = 0, |
| 64 | Initialized = 1, | 86 | Initialized = 1, |
| @@ -66,15 +88,40 @@ private: | |||
| 66 | 88 | ||
| 67 | enum class DeviceState : u32 { | 89 | enum class DeviceState : u32 { |
| 68 | Initialized = 0, | 90 | Initialized = 0, |
| 91 | SearchingForTag = 1, | ||
| 92 | TagFound = 2, | ||
| 93 | TagRemoved = 3, | ||
| 94 | TagNearby = 4, | ||
| 95 | Unknown5 = 5, | ||
| 96 | Finalized = 6 | ||
| 97 | }; | ||
| 98 | |||
| 99 | struct CommonInfo { | ||
| 100 | u16_be last_write_year; | ||
| 101 | u8 last_write_month; | ||
| 102 | u8 last_write_day; | ||
| 103 | u16_be write_counter; | ||
| 104 | u16_be version; | ||
| 105 | u32_be application_area_size; | ||
| 106 | INSERT_PADDING_BYTES(0x34); | ||
| 69 | }; | 107 | }; |
| 108 | static_assert(sizeof(CommonInfo) == 0x40, "CommonInfo is an invalid size"); | ||
| 70 | 109 | ||
| 71 | void Initialize(Kernel::HLERequestContext& ctx) { | 110 | void Initialize(Kernel::HLERequestContext& ctx) { |
| 72 | LOG_WARNING(Service_NFP, "(STUBBED) called"); | 111 | IPC::ResponseBuilder rb{ctx, 2, 0}; |
| 112 | rb.Push(RESULT_SUCCESS); | ||
| 73 | 113 | ||
| 74 | state = State::Initialized; | 114 | state = State::Initialized; |
| 75 | 115 | ||
| 76 | IPC::ResponseBuilder rb{ctx, 2}; | 116 | LOG_DEBUG(Service_NFC, "called"); |
| 117 | } | ||
| 118 | |||
| 119 | void GetState(Kernel::HLERequestContext& ctx) { | ||
| 120 | IPC::ResponseBuilder rb{ctx, 3, 0}; | ||
| 77 | rb.Push(RESULT_SUCCESS); | 121 | rb.Push(RESULT_SUCCESS); |
| 122 | rb.PushRaw<u32>(static_cast<u32>(state)); | ||
| 123 | |||
| 124 | LOG_DEBUG(Service_NFC, "called"); | ||
| 78 | } | 125 | } |
| 79 | 126 | ||
| 80 | void ListDevices(Kernel::HLERequestContext& ctx) { | 127 | void ListDevices(Kernel::HLERequestContext& ctx) { |
| @@ -83,80 +130,219 @@ private: | |||
| 83 | 130 | ||
| 84 | ctx.WriteBuffer(&device_handle, sizeof(device_handle)); | 131 | ctx.WriteBuffer(&device_handle, sizeof(device_handle)); |
| 85 | 132 | ||
| 86 | LOG_WARNING(Service_NFP, "(STUBBED) called, array_size={}", array_size); | 133 | LOG_DEBUG(Service_NFP, "called, array_size={}", array_size); |
| 87 | 134 | ||
| 88 | IPC::ResponseBuilder rb{ctx, 3}; | 135 | IPC::ResponseBuilder rb{ctx, 3}; |
| 89 | rb.Push(RESULT_SUCCESS); | 136 | rb.Push(RESULT_SUCCESS); |
| 90 | rb.Push<u32>(0); | 137 | rb.Push<u32>(1); |
| 91 | } | 138 | } |
| 92 | 139 | ||
| 93 | void AttachActivateEvent(Kernel::HLERequestContext& ctx) { | 140 | void GetNpadId(Kernel::HLERequestContext& ctx) { |
| 94 | IPC::RequestParser rp{ctx}; | 141 | IPC::RequestParser rp{ctx}; |
| 95 | const u64 dev_handle = rp.Pop<u64>(); | 142 | const u64 dev_handle = rp.Pop<u64>(); |
| 96 | LOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle); | 143 | LOG_DEBUG(Service_NFP, "called, dev_handle=0x{:X}", dev_handle); |
| 144 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 145 | rb.Push(RESULT_SUCCESS); | ||
| 146 | rb.Push<u32>(npad_id); | ||
| 147 | } | ||
| 97 | 148 | ||
| 149 | void AttachActivateEvent(Kernel::HLERequestContext& ctx) { | ||
| 150 | IPC::RequestParser rp{ctx}; | ||
| 151 | const u64 dev_handle = rp.Pop<u64>(); | ||
| 152 | LOG_DEBUG(Service_NFP, "called, dev_handle=0x{:X}", dev_handle); | ||
| 98 | IPC::ResponseBuilder rb{ctx, 2, 1}; | 153 | IPC::ResponseBuilder rb{ctx, 2, 1}; |
| 99 | rb.Push(RESULT_SUCCESS); | 154 | rb.Push(RESULT_SUCCESS); |
| 100 | rb.PushCopyObjects(activate_event); | 155 | rb.PushCopyObjects(nfp_interface.GetNFCEvent()); |
| 156 | has_attached_handle = true; | ||
| 101 | } | 157 | } |
| 102 | 158 | ||
| 103 | void AttachDeactivateEvent(Kernel::HLERequestContext& ctx) { | 159 | void AttachDeactivateEvent(Kernel::HLERequestContext& ctx) { |
| 104 | IPC::RequestParser rp{ctx}; | 160 | IPC::RequestParser rp{ctx}; |
| 105 | const u64 dev_handle = rp.Pop<u64>(); | 161 | const u64 dev_handle = rp.Pop<u64>(); |
| 106 | LOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle); | 162 | LOG_DEBUG(Service_NFP, "called, dev_handle=0x{:X}", dev_handle); |
| 107 | 163 | ||
| 108 | IPC::ResponseBuilder rb{ctx, 2, 1}; | 164 | IPC::ResponseBuilder rb{ctx, 2, 1}; |
| 109 | rb.Push(RESULT_SUCCESS); | 165 | rb.Push(RESULT_SUCCESS); |
| 110 | rb.PushCopyObjects(deactivate_event); | 166 | rb.PushCopyObjects(deactivate_event); |
| 111 | } | 167 | } |
| 112 | 168 | ||
| 113 | void GetState(Kernel::HLERequestContext& ctx) { | 169 | void StopDetection(Kernel::HLERequestContext& ctx) { |
| 114 | LOG_WARNING(Service_NFP, "(STUBBED) called"); | 170 | LOG_DEBUG(Service_NFP, "called"); |
| 115 | IPC::ResponseBuilder rb{ctx, 3}; | 171 | switch (device_state) { |
| 172 | case DeviceState::TagFound: | ||
| 173 | case DeviceState::TagNearby: | ||
| 174 | deactivate_event->Signal(); | ||
| 175 | device_state = DeviceState::Initialized; | ||
| 176 | break; | ||
| 177 | case DeviceState::SearchingForTag: | ||
| 178 | case DeviceState::TagRemoved: | ||
| 179 | device_state = DeviceState::Initialized; | ||
| 180 | break; | ||
| 181 | } | ||
| 182 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 116 | rb.Push(RESULT_SUCCESS); | 183 | rb.Push(RESULT_SUCCESS); |
| 117 | rb.Push<u32>(static_cast<u32>(state)); | ||
| 118 | } | 184 | } |
| 119 | 185 | ||
| 120 | void GetDeviceState(Kernel::HLERequestContext& ctx) { | 186 | void GetDeviceState(Kernel::HLERequestContext& ctx) { |
| 121 | LOG_WARNING(Service_NFP, "(STUBBED) called"); | 187 | LOG_DEBUG(Service_NFP, "called"); |
| 188 | auto nfc_event = nfp_interface.GetNFCEvent(); | ||
| 189 | if (!nfc_event->ShouldWait(Kernel::GetCurrentThread()) && !has_attached_handle) { | ||
| 190 | device_state = DeviceState::TagFound; | ||
| 191 | nfc_event->Clear(); | ||
| 192 | } | ||
| 193 | |||
| 122 | IPC::ResponseBuilder rb{ctx, 3}; | 194 | IPC::ResponseBuilder rb{ctx, 3}; |
| 123 | rb.Push(RESULT_SUCCESS); | 195 | rb.Push(RESULT_SUCCESS); |
| 124 | rb.Push<u32>(static_cast<u32>(device_state)); | 196 | rb.Push<u32>(static_cast<u32>(device_state)); |
| 125 | } | 197 | } |
| 126 | 198 | ||
| 127 | void GetNpadId(Kernel::HLERequestContext& ctx) { | 199 | void StartDetection(Kernel::HLERequestContext& ctx) { |
| 128 | IPC::RequestParser rp{ctx}; | 200 | LOG_DEBUG(Service_NFP, "called"); |
| 129 | const u64 dev_handle = rp.Pop<u64>(); | 201 | |
| 130 | LOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle); | 202 | if (device_state == DeviceState::Initialized || device_state == DeviceState::TagRemoved) { |
| 131 | IPC::ResponseBuilder rb{ctx, 3}; | 203 | device_state = DeviceState::SearchingForTag; |
| 204 | } | ||
| 205 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 206 | rb.Push(RESULT_SUCCESS); | ||
| 207 | } | ||
| 208 | |||
| 209 | void GetTagInfo(Kernel::HLERequestContext& ctx) { | ||
| 210 | LOG_DEBUG(Service_NFP, "called"); | ||
| 211 | |||
| 212 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 213 | auto amiibo = nfp_interface.GetAmiiboBuffer(); | ||
| 214 | TagInfo tag_info{}; | ||
| 215 | std::memcpy(tag_info.uuid.data(), amiibo.uuid.data(), sizeof(tag_info.uuid.size())); | ||
| 216 | tag_info.uuid_length = static_cast<u8>(tag_info.uuid.size()); | ||
| 217 | |||
| 218 | tag_info.protocol = 1; // TODO(ogniK): Figure out actual values | ||
| 219 | tag_info.tag_type = 2; | ||
| 220 | ctx.WriteBuffer(&tag_info, sizeof(TagInfo)); | ||
| 221 | rb.Push(RESULT_SUCCESS); | ||
| 222 | } | ||
| 223 | |||
| 224 | void Mount(Kernel::HLERequestContext& ctx) { | ||
| 225 | LOG_DEBUG(Service_NFP, "called"); | ||
| 226 | |||
| 227 | device_state = DeviceState::TagNearby; | ||
| 228 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 229 | rb.Push(RESULT_SUCCESS); | ||
| 230 | } | ||
| 231 | |||
| 232 | void GetModelInfo(Kernel::HLERequestContext& ctx) { | ||
| 233 | LOG_DEBUG(Service_NFP, "called"); | ||
| 234 | |||
| 235 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 236 | auto amiibo = nfp_interface.GetAmiiboBuffer(); | ||
| 237 | ctx.WriteBuffer(&amiibo.model_info, sizeof(amiibo.model_info)); | ||
| 238 | rb.Push(RESULT_SUCCESS); | ||
| 239 | } | ||
| 240 | |||
| 241 | void Unmount(Kernel::HLERequestContext& ctx) { | ||
| 242 | LOG_DEBUG(Service_NFP, "called"); | ||
| 243 | |||
| 244 | device_state = DeviceState::TagFound; | ||
| 245 | |||
| 246 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 247 | rb.Push(RESULT_SUCCESS); | ||
| 248 | } | ||
| 249 | |||
| 250 | void Finalize(Kernel::HLERequestContext& ctx) { | ||
| 251 | LOG_DEBUG(Service_NFP, "called"); | ||
| 252 | |||
| 253 | device_state = DeviceState::Finalized; | ||
| 254 | |||
| 255 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 132 | rb.Push(RESULT_SUCCESS); | 256 | rb.Push(RESULT_SUCCESS); |
| 133 | rb.Push<u32>(npad_id); | ||
| 134 | } | 257 | } |
| 135 | 258 | ||
| 136 | void AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx) { | 259 | void AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx) { |
| 137 | IPC::RequestParser rp{ctx}; | 260 | LOG_WARNING(Service_NFP, "(STUBBED) called"); |
| 138 | const u64 dev_handle = rp.Pop<u64>(); | ||
| 139 | LOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle); | ||
| 140 | 261 | ||
| 141 | IPC::ResponseBuilder rb{ctx, 2, 1}; | 262 | IPC::ResponseBuilder rb{ctx, 2, 1}; |
| 142 | rb.Push(RESULT_SUCCESS); | 263 | rb.Push(RESULT_SUCCESS); |
| 143 | rb.PushCopyObjects(availability_change_event); | 264 | rb.PushCopyObjects(availability_change_event); |
| 144 | } | 265 | } |
| 145 | 266 | ||
| 146 | const u64 device_handle{0xDEAD}; | 267 | void GetRegisterInfo(Kernel::HLERequestContext& ctx) { |
| 147 | const u32 npad_id{0}; // This is the first player controller id | 268 | LOG_WARNING(Service_NFP, "(STUBBED) called"); |
| 269 | |||
| 270 | // TODO(ogniK): Pull Mii and owner data from amiibo | ||
| 271 | |||
| 272 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 273 | rb.Push(RESULT_SUCCESS); | ||
| 274 | } | ||
| 275 | |||
| 276 | void GetCommonInfo(Kernel::HLERequestContext& ctx) { | ||
| 277 | LOG_WARNING(Service_NFP, "(STUBBED) called"); | ||
| 278 | |||
| 279 | // TODO(ogniK): Pull common information from amiibo | ||
| 280 | |||
| 281 | CommonInfo common_info{}; | ||
| 282 | common_info.application_area_size = 0; | ||
| 283 | ctx.WriteBuffer(&common_info, sizeof(CommonInfo)); | ||
| 284 | |||
| 285 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 286 | rb.Push(RESULT_SUCCESS); | ||
| 287 | } | ||
| 288 | |||
| 289 | void OpenApplicationArea(Kernel::HLERequestContext& ctx) { | ||
| 290 | LOG_DEBUG(Service_NFP, "called"); | ||
| 291 | // We don't need to worry about this since we can just open the file | ||
| 292 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 293 | rb.Push(RESULT_SUCCESS); | ||
| 294 | } | ||
| 295 | |||
| 296 | void GetApplicationAreaSize(Kernel::HLERequestContext& ctx) { | ||
| 297 | LOG_WARNING(Service_NFP, "(STUBBED) called"); | ||
| 298 | // We don't need to worry about this since we can just open the file | ||
| 299 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 300 | rb.Push(RESULT_SUCCESS); | ||
| 301 | rb.PushRaw<u32>(0); // This is from the GetCommonInfo stub | ||
| 302 | } | ||
| 303 | |||
| 304 | void GetApplicationArea(Kernel::HLERequestContext& ctx) { | ||
| 305 | LOG_WARNING(Service_NFP, "(STUBBED) called"); | ||
| 306 | |||
| 307 | // TODO(ogniK): Pull application area from amiibo | ||
| 308 | |||
| 309 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 310 | rb.Push(RESULT_SUCCESS); | ||
| 311 | rb.PushRaw<u32>(0); // This is from the GetCommonInfo stub | ||
| 312 | } | ||
| 313 | |||
| 314 | bool has_attached_handle{}; | ||
| 315 | const u64 device_handle{Common::MakeMagic('Y', 'U', 'Z', 'U')}; | ||
| 316 | const u32 npad_id{0}; // Player 1 controller | ||
| 148 | State state{State::NonInitialized}; | 317 | State state{State::NonInitialized}; |
| 149 | DeviceState device_state{DeviceState::Initialized}; | 318 | DeviceState device_state{DeviceState::Initialized}; |
| 150 | Kernel::SharedPtr<Kernel::Event> activate_event; | ||
| 151 | Kernel::SharedPtr<Kernel::Event> deactivate_event; | 319 | Kernel::SharedPtr<Kernel::Event> deactivate_event; |
| 152 | Kernel::SharedPtr<Kernel::Event> availability_change_event; | 320 | Kernel::SharedPtr<Kernel::Event> availability_change_event; |
| 321 | const Module::Interface& nfp_interface; | ||
| 153 | }; | 322 | }; |
| 154 | 323 | ||
| 155 | void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) { | 324 | void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) { |
| 156 | LOG_DEBUG(Service_NFP, "called"); | 325 | LOG_DEBUG(Service_NFP, "called"); |
| 157 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 326 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 158 | rb.Push(RESULT_SUCCESS); | 327 | rb.Push(RESULT_SUCCESS); |
| 159 | rb.PushIpcInterface<IUser>(); | 328 | rb.PushIpcInterface<IUser>(*this); |
| 329 | } | ||
| 330 | |||
| 331 | bool Module::Interface::LoadAmiibo(const std::vector<u8>& buffer) { | ||
| 332 | std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); | ||
| 333 | if (buffer.size() < sizeof(AmiiboFile)) { | ||
| 334 | return false; | ||
| 335 | } | ||
| 336 | |||
| 337 | std::memcpy(&amiibo, buffer.data(), sizeof(amiibo)); | ||
| 338 | nfc_tag_load->Signal(); | ||
| 339 | return true; | ||
| 340 | } | ||
| 341 | const Kernel::SharedPtr<Kernel::Event>& Module::Interface::GetNFCEvent() const { | ||
| 342 | return nfc_tag_load; | ||
| 343 | } | ||
| 344 | const Module::Interface::AmiiboFile& Module::Interface::GetAmiiboBuffer() const { | ||
| 345 | return amiibo; | ||
| 160 | } | 346 | } |
| 161 | 347 | ||
| 162 | void InstallInterfaces(SM::ServiceManager& service_manager) { | 348 | void InstallInterfaces(SM::ServiceManager& service_manager) { |
diff --git a/src/core/hle/service/nfp/nfp.h b/src/core/hle/service/nfp/nfp.h index 77df343c4..5c0ae8a54 100644 --- a/src/core/hle/service/nfp/nfp.h +++ b/src/core/hle/service/nfp/nfp.h | |||
| @@ -4,6 +4,9 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | ||
| 8 | #include <vector> | ||
| 9 | #include "core/hle/kernel/event.h" | ||
| 7 | #include "core/hle/service/service.h" | 10 | #include "core/hle/service/service.h" |
| 8 | 11 | ||
| 9 | namespace Service::NFP { | 12 | namespace Service::NFP { |
| @@ -15,7 +18,27 @@ public: | |||
| 15 | explicit Interface(std::shared_ptr<Module> module, const char* name); | 18 | explicit Interface(std::shared_ptr<Module> module, const char* name); |
| 16 | ~Interface() override; | 19 | ~Interface() override; |
| 17 | 20 | ||
| 21 | struct ModelInfo { | ||
| 22 | std::array<u8, 0x8> amiibo_identification_block; | ||
| 23 | INSERT_PADDING_BYTES(0x38); | ||
| 24 | }; | ||
| 25 | static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size"); | ||
| 26 | |||
| 27 | struct AmiiboFile { | ||
| 28 | std::array<u8, 10> uuid; | ||
| 29 | INSERT_PADDING_BYTES(0x4a); | ||
| 30 | ModelInfo model_info; | ||
| 31 | }; | ||
| 32 | static_assert(sizeof(AmiiboFile) == 0x94, "AmiiboFile is an invalid size"); | ||
| 33 | |||
| 18 | void CreateUserInterface(Kernel::HLERequestContext& ctx); | 34 | void CreateUserInterface(Kernel::HLERequestContext& ctx); |
| 35 | bool LoadAmiibo(const std::vector<u8>& buffer); | ||
| 36 | const Kernel::SharedPtr<Kernel::Event>& GetNFCEvent() const; | ||
| 37 | const AmiiboFile& GetAmiiboBuffer() const; | ||
| 38 | |||
| 39 | private: | ||
| 40 | Kernel::SharedPtr<Kernel::Event> nfc_tag_load{}; | ||
| 41 | AmiiboFile amiibo{}; | ||
| 19 | 42 | ||
| 20 | protected: | 43 | protected: |
| 21 | std::shared_ptr<Module> module; | 44 | std::shared_ptr<Module> module; |
diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp index 10611ed6a..75dcd94a3 100644 --- a/src/core/hle/service/nifm/nifm.cpp +++ b/src/core/hle/service/nifm/nifm.cpp | |||
| @@ -219,6 +219,7 @@ IGeneralService::IGeneralService() : ServiceFramework("IGeneralService") { | |||
| 219 | {35, nullptr, "GetScanData"}, | 219 | {35, nullptr, "GetScanData"}, |
| 220 | {36, nullptr, "GetCurrentAccessPoint"}, | 220 | {36, nullptr, "GetCurrentAccessPoint"}, |
| 221 | {37, nullptr, "Shutdown"}, | 221 | {37, nullptr, "Shutdown"}, |
| 222 | {38, nullptr, "GetAllowedChannels"}, | ||
| 222 | }; | 223 | }; |
| 223 | RegisterHandlers(functions); | 224 | RegisterHandlers(functions); |
| 224 | } | 225 | } |
diff --git a/src/core/hle/service/nim/nim.cpp b/src/core/hle/service/nim/nim.cpp index 261ad539c..18091c9bb 100644 --- a/src/core/hle/service/nim/nim.cpp +++ b/src/core/hle/service/nim/nim.cpp | |||
| @@ -71,6 +71,22 @@ public: | |||
| 71 | } | 71 | } |
| 72 | }; | 72 | }; |
| 73 | 73 | ||
| 74 | class NIM_ECA final : public ServiceFramework<NIM_ECA> { | ||
| 75 | public: | ||
| 76 | explicit NIM_ECA() : ServiceFramework{"nim:eca"} { | ||
| 77 | // clang-format off | ||
| 78 | static const FunctionInfo functions[] = { | ||
| 79 | {0, nullptr, "CreateServerInterface"}, | ||
| 80 | {1, nullptr, "RefreshDebugAvailability"}, | ||
| 81 | {2, nullptr, "ClearDebugResponse"}, | ||
| 82 | {3, nullptr, "RegisterDebugResponse"}, | ||
| 83 | }; | ||
| 84 | // clang-format on | ||
| 85 | |||
| 86 | RegisterHandlers(functions); | ||
| 87 | } | ||
| 88 | }; | ||
| 89 | |||
| 74 | class NIM_SHP final : public ServiceFramework<NIM_SHP> { | 90 | class NIM_SHP final : public ServiceFramework<NIM_SHP> { |
| 75 | public: | 91 | public: |
| 76 | explicit NIM_SHP() : ServiceFramework{"nim:shp"} { | 92 | explicit NIM_SHP() : ServiceFramework{"nim:shp"} { |
| @@ -214,6 +230,7 @@ private: | |||
| 214 | 230 | ||
| 215 | void InstallInterfaces(SM::ServiceManager& sm) { | 231 | void InstallInterfaces(SM::ServiceManager& sm) { |
| 216 | std::make_shared<NIM>()->InstallAsService(sm); | 232 | std::make_shared<NIM>()->InstallAsService(sm); |
| 233 | std::make_shared<NIM_ECA>()->InstallAsService(sm); | ||
| 217 | std::make_shared<NIM_SHP>()->InstallAsService(sm); | 234 | std::make_shared<NIM_SHP>()->InstallAsService(sm); |
| 218 | std::make_shared<NTC>()->InstallAsService(sm); | 235 | std::make_shared<NTC>()->InstallAsService(sm); |
| 219 | } | 236 | } |
diff --git a/src/core/hle/service/npns/npns.cpp b/src/core/hle/service/npns/npns.cpp new file mode 100644 index 000000000..ccb6f9da9 --- /dev/null +++ b/src/core/hle/service/npns/npns.cpp | |||
| @@ -0,0 +1,88 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <memory> | ||
| 6 | |||
| 7 | #include "core/hle/service/npns/npns.h" | ||
| 8 | #include "core/hle/service/service.h" | ||
| 9 | #include "core/hle/service/sm/sm.h" | ||
| 10 | |||
| 11 | namespace Service::NPNS { | ||
| 12 | |||
| 13 | class NPNS_S final : public ServiceFramework<NPNS_S> { | ||
| 14 | public: | ||
| 15 | explicit NPNS_S() : ServiceFramework{"npns:s"} { | ||
| 16 | // clang-format off | ||
| 17 | static const FunctionInfo functions[] = { | ||
| 18 | {1, nullptr, "ListenAll"}, | ||
| 19 | {2, nullptr, "ListenTo"}, | ||
| 20 | {3, nullptr, "Receive"}, | ||
| 21 | {4, nullptr, "ReceiveRaw"}, | ||
| 22 | {5, nullptr, "GetReceiveEvent"}, | ||
| 23 | {6, nullptr, "ListenUndelivered"}, | ||
| 24 | {7, nullptr, "GetStateChangeEVent"}, | ||
| 25 | {11, nullptr, "SubscribeTopic"}, | ||
| 26 | {12, nullptr, "UnsubscribeTopic"}, | ||
| 27 | {13, nullptr, "QueryIsTopicExist"}, | ||
| 28 | {21, nullptr, "CreateToken"}, | ||
| 29 | {22, nullptr, "CreateTokenWithApplicationId"}, | ||
| 30 | {23, nullptr, "DestroyToken"}, | ||
| 31 | {24, nullptr, "DestroyTokenWithApplicationId"}, | ||
| 32 | {25, nullptr, "QueryIsTokenValid"}, | ||
| 33 | {31, nullptr, "UploadTokenToBaaS"}, | ||
| 34 | {32, nullptr, "DestroyTokenForBaaS"}, | ||
| 35 | {33, nullptr, "CreateTokenForBaaS"}, | ||
| 36 | {34, nullptr, "SetBaaSDeviceAccountIdList"}, | ||
| 37 | {101, nullptr, "Suspend"}, | ||
| 38 | {102, nullptr, "Resume"}, | ||
| 39 | {103, nullptr, "GetState"}, | ||
| 40 | {104, nullptr, "GetStatistics"}, | ||
| 41 | {105, nullptr, "GetPlayReportRequestEvent"}, | ||
| 42 | {111, nullptr, "GetJid"}, | ||
| 43 | {112, nullptr, "CreateJid"}, | ||
| 44 | {113, nullptr, "DestroyJid"}, | ||
| 45 | {114, nullptr, "AttachJid"}, | ||
| 46 | {115, nullptr, "DetachJid"}, | ||
| 47 | {201, nullptr, "RequestChangeStateForceTimed"}, | ||
| 48 | {102, nullptr, "RequestChangeStateForceAsync"}, | ||
| 49 | }; | ||
| 50 | // clang-format on | ||
| 51 | |||
| 52 | RegisterHandlers(functions); | ||
| 53 | } | ||
| 54 | }; | ||
| 55 | |||
| 56 | class NPNS_U final : public ServiceFramework<NPNS_U> { | ||
| 57 | public: | ||
| 58 | explicit NPNS_U() : ServiceFramework{"npns:u"} { | ||
| 59 | // clang-format off | ||
| 60 | static const FunctionInfo functions[] = { | ||
| 61 | {1, nullptr, "ListenAll"}, | ||
| 62 | {2, nullptr, "ListenTo"}, | ||
| 63 | {3, nullptr, "Receive"}, | ||
| 64 | {4, nullptr, "ReceiveRaw"}, | ||
| 65 | {5, nullptr, "GetReceiveEvent"}, | ||
| 66 | {7, nullptr, "GetStateChangeEVent"}, | ||
| 67 | {21, nullptr, "CreateToken"}, | ||
| 68 | {23, nullptr, "DestroyToken"}, | ||
| 69 | {25, nullptr, "QueryIsTokenValid"}, | ||
| 70 | {26, nullptr, "ListenToMyApplicationId"}, | ||
| 71 | {101, nullptr, "Suspend"}, | ||
| 72 | {102, nullptr, "Resume"}, | ||
| 73 | {103, nullptr, "GetState"}, | ||
| 74 | {104, nullptr, "GetStatistics"}, | ||
| 75 | {111, nullptr, "GetJid"}, | ||
| 76 | }; | ||
| 77 | // clang-format on | ||
| 78 | |||
| 79 | RegisterHandlers(functions); | ||
| 80 | } | ||
| 81 | }; | ||
| 82 | |||
| 83 | void InstallInterfaces(SM::ServiceManager& sm) { | ||
| 84 | std::make_shared<NPNS_S>()->InstallAsService(sm); | ||
| 85 | std::make_shared<NPNS_U>()->InstallAsService(sm); | ||
| 86 | } | ||
| 87 | |||
| 88 | } // namespace Service::NPNS | ||
diff --git a/src/core/hle/service/npns/npns.h b/src/core/hle/service/npns/npns.h new file mode 100644 index 000000000..861cd3e48 --- /dev/null +++ b/src/core/hle/service/npns/npns.h | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | namespace Service::SM { | ||
| 8 | class ServiceManager; | ||
| 9 | } | ||
| 10 | |||
| 11 | namespace Service::NPNS { | ||
| 12 | |||
| 13 | void InstallInterfaces(SM::ServiceManager& sm); | ||
| 14 | |||
| 15 | } // namespace Service::NPNS | ||
diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp index 98017267c..07c1381fe 100644 --- a/src/core/hle/service/ns/ns.cpp +++ b/src/core/hle/service/ns/ns.cpp | |||
| @@ -93,13 +93,23 @@ public: | |||
| 93 | {86, nullptr, "EnableApplicationCrashReport"}, | 93 | {86, nullptr, "EnableApplicationCrashReport"}, |
| 94 | {87, nullptr, "IsApplicationCrashReportEnabled"}, | 94 | {87, nullptr, "IsApplicationCrashReportEnabled"}, |
| 95 | {90, nullptr, "BoostSystemMemoryResourceLimit"}, | 95 | {90, nullptr, "BoostSystemMemoryResourceLimit"}, |
| 96 | {91, nullptr, "Unknown1"}, | ||
| 97 | {92, nullptr, "Unknown2"}, | ||
| 98 | {93, nullptr, "GetMainApplicationProgramIndex"}, | ||
| 99 | {94, nullptr, "LaunchApplication2"}, | ||
| 100 | {95, nullptr, "GetApplicationLaunchInfo"}, | ||
| 101 | {96, nullptr, "AcquireApplicationLaunchInfo"}, | ||
| 102 | {97, nullptr, "GetMainApplicationProgramIndex2"}, | ||
| 103 | {98, nullptr, "EnableApplicationAllThreadDumpOnCrash"}, | ||
| 96 | {100, nullptr, "ResetToFactorySettings"}, | 104 | {100, nullptr, "ResetToFactorySettings"}, |
| 97 | {101, nullptr, "ResetToFactorySettingsWithoutUserSaveData"}, | 105 | {101, nullptr, "ResetToFactorySettingsWithoutUserSaveData"}, |
| 98 | {102, nullptr, "ResetToFactorySettingsForRefurbishment"}, | 106 | {102, nullptr, "ResetToFactorySettingsForRefurbishment"}, |
| 99 | {200, nullptr, "CalculateUserSaveDataStatistics"}, | 107 | {200, nullptr, "CalculateUserSaveDataStatistics"}, |
| 100 | {201, nullptr, "DeleteUserSaveDataAll"}, | 108 | {201, nullptr, "DeleteUserSaveDataAll"}, |
| 101 | {210, nullptr, "DeleteUserSystemSaveData"}, | 109 | {210, nullptr, "DeleteUserSystemSaveData"}, |
| 110 | {211, nullptr, "DeleteSaveData"}, | ||
| 102 | {220, nullptr, "UnregisterNetworkServiceAccount"}, | 111 | {220, nullptr, "UnregisterNetworkServiceAccount"}, |
| 112 | {221, nullptr, "UnregisterNetworkServiceAccountWithUserSaveDataDeletion"}, | ||
| 103 | {300, nullptr, "GetApplicationShellEvent"}, | 113 | {300, nullptr, "GetApplicationShellEvent"}, |
| 104 | {301, nullptr, "PopApplicationShellEventInfo"}, | 114 | {301, nullptr, "PopApplicationShellEventInfo"}, |
| 105 | {302, nullptr, "LaunchLibraryApplet"}, | 115 | {302, nullptr, "LaunchLibraryApplet"}, |
| @@ -114,6 +124,7 @@ public: | |||
| 114 | {403, nullptr, "GetMaxApplicationControlCacheCount"}, | 124 | {403, nullptr, "GetMaxApplicationControlCacheCount"}, |
| 115 | {404, nullptr, "InvalidateApplicationControlCache"}, | 125 | {404, nullptr, "InvalidateApplicationControlCache"}, |
| 116 | {405, nullptr, "ListApplicationControlCacheEntryInfo"}, | 126 | {405, nullptr, "ListApplicationControlCacheEntryInfo"}, |
| 127 | {406, nullptr, "GetApplicationControlProperty"}, | ||
| 117 | {502, nullptr, "RequestCheckGameCardRegistration"}, | 128 | {502, nullptr, "RequestCheckGameCardRegistration"}, |
| 118 | {503, nullptr, "RequestGameCardRegistrationGoldPoint"}, | 129 | {503, nullptr, "RequestGameCardRegistrationGoldPoint"}, |
| 119 | {504, nullptr, "RequestRegisterGameCard"}, | 130 | {504, nullptr, "RequestRegisterGameCard"}, |
| @@ -129,6 +140,7 @@ public: | |||
| 129 | {604, nullptr, "RegisterContentsExternalKey"}, | 140 | {604, nullptr, "RegisterContentsExternalKey"}, |
| 130 | {605, nullptr, "ListApplicationContentMetaStatusWithRightsCheck"}, | 141 | {605, nullptr, "ListApplicationContentMetaStatusWithRightsCheck"}, |
| 131 | {606, nullptr, "GetContentMetaStorage"}, | 142 | {606, nullptr, "GetContentMetaStorage"}, |
| 143 | {607, nullptr, "ListAvailableAddOnContent"}, | ||
| 132 | {700, nullptr, "PushDownloadTaskList"}, | 144 | {700, nullptr, "PushDownloadTaskList"}, |
| 133 | {701, nullptr, "ClearTaskStatusList"}, | 145 | {701, nullptr, "ClearTaskStatusList"}, |
| 134 | {702, nullptr, "RequestDownloadTaskList"}, | 146 | {702, nullptr, "RequestDownloadTaskList"}, |
| @@ -148,6 +160,9 @@ public: | |||
| 148 | {907, nullptr, "WithdrawApplicationUpdateRequest"}, | 160 | {907, nullptr, "WithdrawApplicationUpdateRequest"}, |
| 149 | {908, nullptr, "ListApplicationRecordInstalledContentMeta"}, | 161 | {908, nullptr, "ListApplicationRecordInstalledContentMeta"}, |
| 150 | {909, nullptr, "WithdrawCleanupAddOnContentsWithNoRightsRecommendation"}, | 162 | {909, nullptr, "WithdrawCleanupAddOnContentsWithNoRightsRecommendation"}, |
| 163 | {910, nullptr, "Unknown3"}, | ||
| 164 | {911, nullptr, "SetPreInstalledApplication"}, | ||
| 165 | {912, nullptr, "ClearPreInstalledApplicationFlag"}, | ||
| 151 | {1000, nullptr, "RequestVerifyApplicationDeprecated"}, | 166 | {1000, nullptr, "RequestVerifyApplicationDeprecated"}, |
| 152 | {1001, nullptr, "CorruptApplicationForDebug"}, | 167 | {1001, nullptr, "CorruptApplicationForDebug"}, |
| 153 | {1002, nullptr, "RequestVerifyAddOnContentsRights"}, | 168 | {1002, nullptr, "RequestVerifyAddOnContentsRights"}, |
| @@ -162,6 +177,8 @@ public: | |||
| 162 | {1305, nullptr, "TryDeleteRunningApplicationEntity"}, | 177 | {1305, nullptr, "TryDeleteRunningApplicationEntity"}, |
| 163 | {1306, nullptr, "TryDeleteRunningApplicationCompletely"}, | 178 | {1306, nullptr, "TryDeleteRunningApplicationCompletely"}, |
| 164 | {1307, nullptr, "TryDeleteRunningApplicationContentEntities"}, | 179 | {1307, nullptr, "TryDeleteRunningApplicationContentEntities"}, |
| 180 | {1308, nullptr, "DeleteApplicationCompletelyForDebug"}, | ||
| 181 | {1309, nullptr, "CleanupUnavailableAddOnContents"}, | ||
| 165 | {1400, nullptr, "PrepareShutdown"}, | 182 | {1400, nullptr, "PrepareShutdown"}, |
| 166 | {1500, nullptr, "FormatSdCard"}, | 183 | {1500, nullptr, "FormatSdCard"}, |
| 167 | {1501, nullptr, "NeedsSystemUpdateToFormatSdCard"}, | 184 | {1501, nullptr, "NeedsSystemUpdateToFormatSdCard"}, |
| @@ -199,6 +216,28 @@ public: | |||
| 199 | {2015, nullptr, "CompareSystemDeliveryInfo"}, | 216 | {2015, nullptr, "CompareSystemDeliveryInfo"}, |
| 200 | {2016, nullptr, "ListNotCommittedContentMeta"}, | 217 | {2016, nullptr, "ListNotCommittedContentMeta"}, |
| 201 | {2017, nullptr, "CreateDownloadTask"}, | 218 | {2017, nullptr, "CreateDownloadTask"}, |
| 219 | {2018, nullptr, "Unknown4"}, | ||
| 220 | {2050, nullptr, "Unknown5"}, | ||
| 221 | {2100, nullptr, "Unknown6"}, | ||
| 222 | {2101, nullptr, "Unknown7"}, | ||
| 223 | {2150, nullptr, "CreateRightsEnvironment"}, | ||
| 224 | {2151, nullptr, "DestroyRightsEnvironment"}, | ||
| 225 | {2152, nullptr, "ActivateRightsEnvironment"}, | ||
| 226 | {2153, nullptr, "DeactivateRightsEnvironment"}, | ||
| 227 | {2154, nullptr, "ForceActivateRightsContextForExit"}, | ||
| 228 | {2160, nullptr, "AddTargetApplicationToRightsEnvironment"}, | ||
| 229 | {2161, nullptr, "SetUsersToRightsEnvironment"}, | ||
| 230 | {2170, nullptr, "GetRightsEnvironmentStatus"}, | ||
| 231 | {2171, nullptr, "GetRightsEnvironmentStatusChangedEvent"}, | ||
| 232 | {2180, nullptr, "RequestExtendRightsInRightsEnvironment"}, | ||
| 233 | {2181, nullptr, "GetLastResultOfExtendRightsInRightsEnvironment"}, | ||
| 234 | {2182, nullptr, "SetActiveRightsContextUsingStateToRightsEnvironment"}, | ||
| 235 | {2190, nullptr, "GetRightsEnvironmentHandleForApplication"}, | ||
| 236 | {2199, nullptr, "GetRightsEnvironmentCountForDebug"}, | ||
| 237 | {2200, nullptr, "Unknown8"}, | ||
| 238 | {2201, nullptr, "Unknown9"}, | ||
| 239 | {2250, nullptr, "Unknown10"}, | ||
| 240 | {2300, nullptr, "Unknown11"}, | ||
| 202 | }; | 241 | }; |
| 203 | // clang-format on | 242 | // clang-format on |
| 204 | 243 | ||
| @@ -348,12 +387,15 @@ public: | |||
| 348 | {0, nullptr, "LaunchProgram"}, | 387 | {0, nullptr, "LaunchProgram"}, |
| 349 | {1, nullptr, "TerminateProcess"}, | 388 | {1, nullptr, "TerminateProcess"}, |
| 350 | {2, nullptr, "TerminateProgram"}, | 389 | {2, nullptr, "TerminateProgram"}, |
| 351 | {3, nullptr, "GetShellEventHandle"}, | 390 | {4, nullptr, "GetShellEventHandle"}, |
| 352 | {4, nullptr, "GetShellEventInfo"}, | 391 | {5, nullptr, "GetShellEventInfo"}, |
| 353 | {5, nullptr, "TerminateApplication"}, | 392 | {6, nullptr, "TerminateApplication"}, |
| 354 | {6, nullptr, "PrepareLaunchProgramFromHost"}, | 393 | {7, nullptr, "PrepareLaunchProgramFromHost"}, |
| 355 | {7, nullptr, "LaunchApplication"}, | 394 | {8, nullptr, "LaunchApplication"}, |
| 356 | {8, nullptr, "LaunchApplicationWithStorageId"}, | 395 | {9, nullptr, "LaunchApplicationWithStorageId"}, |
| 396 | {10, nullptr, "TerminateApplication2"}, | ||
| 397 | {11, nullptr, "GetRunningApplicationProcessId"}, | ||
| 398 | {12, nullptr, "SetCurrentApplicationRightsEnvironmentCanBeActive"}, | ||
| 357 | }; | 399 | }; |
| 358 | // clang-format on | 400 | // clang-format on |
| 359 | 401 | ||
| @@ -388,6 +430,7 @@ public: | |||
| 388 | {19, nullptr, "GetReceivedEulaDataSize"}, | 430 | {19, nullptr, "GetReceivedEulaDataSize"}, |
| 389 | {20, nullptr, "GetReceivedEulaData"}, | 431 | {20, nullptr, "GetReceivedEulaData"}, |
| 390 | {21, nullptr, "SetupToReceiveSystemUpdate"}, | 432 | {21, nullptr, "SetupToReceiveSystemUpdate"}, |
| 433 | {22, nullptr, "RequestCheckLatestUpdateIncludesRebootlessUpdate"}, | ||
| 391 | }; | 434 | }; |
| 392 | // clang-format on | 435 | // clang-format on |
| 393 | 436 | ||
diff --git a/src/core/hle/service/prepo/prepo.cpp b/src/core/hle/service/prepo/prepo.cpp index 6a9eccfb5..e4fcee9f8 100644 --- a/src/core/hle/service/prepo/prepo.cpp +++ b/src/core/hle/service/prepo/prepo.cpp | |||
| @@ -14,20 +14,24 @@ public: | |||
| 14 | explicit PlayReport(const char* name) : ServiceFramework{name} { | 14 | explicit PlayReport(const char* name) : ServiceFramework{name} { |
| 15 | // clang-format off | 15 | // clang-format off |
| 16 | static const FunctionInfo functions[] = { | 16 | static const FunctionInfo functions[] = { |
| 17 | {10100, nullptr, "SaveReport"}, | 17 | {10100, nullptr, "SaveReportOld"}, |
| 18 | {10101, &PlayReport::SaveReportWithUser, "SaveReportWithUser"}, | 18 | {10101, &PlayReport::SaveReportWithUserOld, "SaveReportWithUserOld"}, |
| 19 | {10102, nullptr, "SaveReport"}, | ||
| 20 | {10103, nullptr, "SaveReportWithUser"}, | ||
| 19 | {10200, nullptr, "RequestImmediateTransmission"}, | 21 | {10200, nullptr, "RequestImmediateTransmission"}, |
| 20 | {10300, nullptr, "GetTransmissionStatus"}, | 22 | {10300, nullptr, "GetTransmissionStatus"}, |
| 21 | {20100, nullptr, "SaveSystemReport"}, | 23 | {20100, nullptr, "SaveSystemReport"}, |
| 22 | {20200, nullptr, "SetOperationMode"}, | ||
| 23 | {20101, nullptr, "SaveSystemReportWithUser"}, | 24 | {20101, nullptr, "SaveSystemReportWithUser"}, |
| 25 | {20200, nullptr, "SetOperationMode"}, | ||
| 24 | {30100, nullptr, "ClearStorage"}, | 26 | {30100, nullptr, "ClearStorage"}, |
| 27 | {30200, nullptr, "ClearStatistics"}, | ||
| 28 | {30300, nullptr, "GetStorageUsage"}, | ||
| 29 | {30400, nullptr, "GetStatistics"}, | ||
| 30 | {30401, nullptr, "GetThroughputHistory"}, | ||
| 31 | {30500, nullptr, "GetLastUploadError"}, | ||
| 25 | {40100, nullptr, "IsUserAgreementCheckEnabled"}, | 32 | {40100, nullptr, "IsUserAgreementCheckEnabled"}, |
| 26 | {40101, nullptr, "SetUserAgreementCheckEnabled"}, | 33 | {40101, nullptr, "SetUserAgreementCheckEnabled"}, |
| 27 | {90100, nullptr, "GetStorageUsage"}, | 34 | {90100, nullptr, "ReadAllReportFiles"}, |
| 28 | {90200, nullptr, "GetStatistics"}, | ||
| 29 | {90201, nullptr, "GetThroughputHistory"}, | ||
| 30 | {90300, nullptr, "GetLastUploadError"}, | ||
| 31 | }; | 35 | }; |
| 32 | // clang-format on | 36 | // clang-format on |
| 33 | 37 | ||
| @@ -35,7 +39,7 @@ public: | |||
| 35 | } | 39 | } |
| 36 | 40 | ||
| 37 | private: | 41 | private: |
| 38 | void SaveReportWithUser(Kernel::HLERequestContext& ctx) { | 42 | void SaveReportWithUserOld(Kernel::HLERequestContext& ctx) { |
| 39 | // TODO(ogniK): Do we want to add play report? | 43 | // TODO(ogniK): Do we want to add play report? |
| 40 | LOG_WARNING(Service_PREPO, "(STUBBED) called"); | 44 | LOG_WARNING(Service_PREPO, "(STUBBED) called"); |
| 41 | 45 | ||
| @@ -46,6 +50,7 @@ private: | |||
| 46 | 50 | ||
| 47 | void InstallInterfaces(SM::ServiceManager& service_manager) { | 51 | void InstallInterfaces(SM::ServiceManager& service_manager) { |
| 48 | std::make_shared<PlayReport>("prepo:a")->InstallAsService(service_manager); | 52 | std::make_shared<PlayReport>("prepo:a")->InstallAsService(service_manager); |
| 53 | std::make_shared<PlayReport>("prepo:a2")->InstallAsService(service_manager); | ||
| 49 | std::make_shared<PlayReport>("prepo:m")->InstallAsService(service_manager); | 54 | std::make_shared<PlayReport>("prepo:m")->InstallAsService(service_manager); |
| 50 | std::make_shared<PlayReport>("prepo:s")->InstallAsService(service_manager); | 55 | std::make_shared<PlayReport>("prepo:s")->InstallAsService(service_manager); |
| 51 | std::make_shared<PlayReport>("prepo:u")->InstallAsService(service_manager); | 56 | std::make_shared<PlayReport>("prepo:u")->InstallAsService(service_manager); |
diff --git a/src/core/hle/service/ptm/psm.cpp b/src/core/hle/service/ptm/psm.cpp new file mode 100644 index 000000000..c2d5fda94 --- /dev/null +++ b/src/core/hle/service/ptm/psm.cpp | |||
| @@ -0,0 +1,71 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <memory> | ||
| 6 | |||
| 7 | #include "common/logging/log.h" | ||
| 8 | #include "core/hle/ipc_helpers.h" | ||
| 9 | #include "core/hle/service/ptm/psm.h" | ||
| 10 | #include "core/hle/service/service.h" | ||
| 11 | #include "core/hle/service/sm/sm.h" | ||
| 12 | |||
| 13 | namespace Service::PSM { | ||
| 14 | |||
| 15 | constexpr u32 BATTERY_FULLY_CHARGED = 100; // 100% Full | ||
| 16 | constexpr u32 BATTERY_CURRENTLY_CHARGING = 1; // Plugged into an official dock | ||
| 17 | |||
| 18 | class PSM final : public ServiceFramework<PSM> { | ||
| 19 | public: | ||
| 20 | explicit PSM() : ServiceFramework{"psm"} { | ||
| 21 | // clang-format off | ||
| 22 | static const FunctionInfo functions[] = { | ||
| 23 | {0, &PSM::GetBatteryChargePercentage, "GetBatteryChargePercentage"}, | ||
| 24 | {1, &PSM::GetChargerType, "GetChargerType"}, | ||
| 25 | {2, nullptr, "EnableBatteryCharging"}, | ||
| 26 | {3, nullptr, "DisableBatteryCharging"}, | ||
| 27 | {4, nullptr, "IsBatteryChargingEnabled"}, | ||
| 28 | {5, nullptr, "AcquireControllerPowerSupply"}, | ||
| 29 | {6, nullptr, "ReleaseControllerPowerSupply"}, | ||
| 30 | {7, nullptr, "OpenSession"}, | ||
| 31 | {8, nullptr, "EnableEnoughPowerChargeEmulation"}, | ||
| 32 | {9, nullptr, "DisableEnoughPowerChargeEmulation"}, | ||
| 33 | {10, nullptr, "EnableFastBatteryCharging"}, | ||
| 34 | {11, nullptr, "DisableFastBatteryCharging"}, | ||
| 35 | {12, nullptr, "GetBatteryVoltageState"}, | ||
| 36 | {13, nullptr, "GetRawBatteryChargePercentage"}, | ||
| 37 | {14, nullptr, "IsEnoughPowerSupplied"}, | ||
| 38 | {15, nullptr, "GetBatteryAgePercentage"}, | ||
| 39 | {16, nullptr, "GetBatteryChargeInfoEvent"}, | ||
| 40 | {17, nullptr, "GetBatteryChargeInfoFields"}, | ||
| 41 | }; | ||
| 42 | // clang-format on | ||
| 43 | |||
| 44 | RegisterHandlers(functions); | ||
| 45 | } | ||
| 46 | |||
| 47 | ~PSM() override = default; | ||
| 48 | |||
| 49 | private: | ||
| 50 | void GetBatteryChargePercentage(Kernel::HLERequestContext& ctx) { | ||
| 51 | LOG_WARNING(Service_PSM, "(STUBBED) called"); | ||
| 52 | |||
| 53 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 54 | rb.Push(RESULT_SUCCESS); | ||
| 55 | rb.Push<u32>(BATTERY_FULLY_CHARGED); | ||
| 56 | } | ||
| 57 | |||
| 58 | void GetChargerType(Kernel::HLERequestContext& ctx) { | ||
| 59 | LOG_WARNING(Service_PSM, "(STUBBED) called"); | ||
| 60 | |||
| 61 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 62 | rb.Push(RESULT_SUCCESS); | ||
| 63 | rb.Push<u32>(BATTERY_CURRENTLY_CHARGING); | ||
| 64 | } | ||
| 65 | }; | ||
| 66 | |||
| 67 | void InstallInterfaces(SM::ServiceManager& sm) { | ||
| 68 | std::make_shared<PSM>()->InstallAsService(sm); | ||
| 69 | } | ||
| 70 | |||
| 71 | } // namespace Service::PSM | ||
diff --git a/src/core/hle/service/ptm/psm.h b/src/core/hle/service/ptm/psm.h new file mode 100644 index 000000000..a286793ae --- /dev/null +++ b/src/core/hle/service/ptm/psm.h | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | namespace Service::SM { | ||
| 8 | class ServiceManager; | ||
| 9 | } | ||
| 10 | |||
| 11 | namespace Service::PSM { | ||
| 12 | |||
| 13 | void InstallInterfaces(SM::ServiceManager& sm); | ||
| 14 | |||
| 15 | } // namespace Service::PSM | ||
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index a225cb4cb..a4cf45267 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp | |||
| @@ -22,7 +22,7 @@ | |||
| 22 | #include "core/hle/service/apm/apm.h" | 22 | #include "core/hle/service/apm/apm.h" |
| 23 | #include "core/hle/service/arp/arp.h" | 23 | #include "core/hle/service/arp/arp.h" |
| 24 | #include "core/hle/service/audio/audio.h" | 24 | #include "core/hle/service/audio/audio.h" |
| 25 | #include "core/hle/service/bcat/bcat.h" | 25 | #include "core/hle/service/bcat/module.h" |
| 26 | #include "core/hle/service/bpc/bpc.h" | 26 | #include "core/hle/service/bpc/bpc.h" |
| 27 | #include "core/hle/service/btdrv/btdrv.h" | 27 | #include "core/hle/service/btdrv/btdrv.h" |
| 28 | #include "core/hle/service/btm/btm.h" | 28 | #include "core/hle/service/btm/btm.h" |
| @@ -48,15 +48,17 @@ | |||
| 48 | #include "core/hle/service/nfp/nfp.h" | 48 | #include "core/hle/service/nfp/nfp.h" |
| 49 | #include "core/hle/service/nifm/nifm.h" | 49 | #include "core/hle/service/nifm/nifm.h" |
| 50 | #include "core/hle/service/nim/nim.h" | 50 | #include "core/hle/service/nim/nim.h" |
| 51 | #include "core/hle/service/npns/npns.h" | ||
| 51 | #include "core/hle/service/ns/ns.h" | 52 | #include "core/hle/service/ns/ns.h" |
| 52 | #include "core/hle/service/nvdrv/nvdrv.h" | 53 | #include "core/hle/service/nvdrv/nvdrv.h" |
| 53 | #include "core/hle/service/nvflinger/nvflinger.h" | 54 | #include "core/hle/service/nvflinger/nvflinger.h" |
| 54 | #include "core/hle/service/pcie/pcie.h" | 55 | #include "core/hle/service/pcie/pcie.h" |
| 55 | #include "core/hle/service/pctl/pctl.h" | 56 | #include "core/hle/service/pctl/module.h" |
| 56 | #include "core/hle/service/pcv/pcv.h" | 57 | #include "core/hle/service/pcv/pcv.h" |
| 57 | #include "core/hle/service/pm/pm.h" | 58 | #include "core/hle/service/pm/pm.h" |
| 58 | #include "core/hle/service/prepo/prepo.h" | 59 | #include "core/hle/service/prepo/prepo.h" |
| 59 | #include "core/hle/service/psc/psc.h" | 60 | #include "core/hle/service/psc/psc.h" |
| 61 | #include "core/hle/service/ptm/psm.h" | ||
| 60 | #include "core/hle/service/service.h" | 62 | #include "core/hle/service/service.h" |
| 61 | #include "core/hle/service/set/settings.h" | 63 | #include "core/hle/service/set/settings.h" |
| 62 | #include "core/hle/service/sm/sm.h" | 64 | #include "core/hle/service/sm/sm.h" |
| @@ -236,6 +238,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, FileSys::VfsFilesystem& vfs) | |||
| 236 | NFP::InstallInterfaces(*sm); | 238 | NFP::InstallInterfaces(*sm); |
| 237 | NIFM::InstallInterfaces(*sm); | 239 | NIFM::InstallInterfaces(*sm); |
| 238 | NIM::InstallInterfaces(*sm); | 240 | NIM::InstallInterfaces(*sm); |
| 241 | NPNS::InstallInterfaces(*sm); | ||
| 239 | NS::InstallInterfaces(*sm); | 242 | NS::InstallInterfaces(*sm); |
| 240 | Nvidia::InstallInterfaces(*sm, *nv_flinger); | 243 | Nvidia::InstallInterfaces(*sm, *nv_flinger); |
| 241 | PCIe::InstallInterfaces(*sm); | 244 | PCIe::InstallInterfaces(*sm); |
| @@ -244,6 +247,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, FileSys::VfsFilesystem& vfs) | |||
| 244 | PlayReport::InstallInterfaces(*sm); | 247 | PlayReport::InstallInterfaces(*sm); |
| 245 | PM::InstallInterfaces(*sm); | 248 | PM::InstallInterfaces(*sm); |
| 246 | PSC::InstallInterfaces(*sm); | 249 | PSC::InstallInterfaces(*sm); |
| 250 | PSM::InstallInterfaces(*sm); | ||
| 247 | Set::InstallInterfaces(*sm); | 251 | Set::InstallInterfaces(*sm); |
| 248 | Sockets::InstallInterfaces(*sm); | 252 | Sockets::InstallInterfaces(*sm); |
| 249 | SPL::InstallInterfaces(*sm); | 253 | SPL::InstallInterfaces(*sm); |
diff --git a/src/core/hle/service/set/set_cal.cpp b/src/core/hle/service/set/set_cal.cpp index 5af356d10..34654bb07 100644 --- a/src/core/hle/service/set/set_cal.cpp +++ b/src/core/hle/service/set/set_cal.cpp | |||
| @@ -39,7 +39,8 @@ SET_CAL::SET_CAL() : ServiceFramework("set:cal") { | |||
| 39 | {29, nullptr, "GetAmiiboEcqvBlsKey"}, | 39 | {29, nullptr, "GetAmiiboEcqvBlsKey"}, |
| 40 | {30, nullptr, "GetAmiiboEcqvBlsCertificate"}, | 40 | {30, nullptr, "GetAmiiboEcqvBlsCertificate"}, |
| 41 | {31, nullptr, "GetAmiiboEcqvBlsRootCertificate"}, | 41 | {31, nullptr, "GetAmiiboEcqvBlsRootCertificate"}, |
| 42 | {32, nullptr, "GetUnknownId"}, | 42 | {32, nullptr, "GetUsbTypeCPowerSourceCircuitVersion"}, |
| 43 | {33, nullptr, "GetBatteryVersion"}, | ||
| 43 | }; | 44 | }; |
| 44 | RegisterHandlers(functions); | 45 | RegisterHandlers(functions); |
| 45 | } | 46 | } |
diff --git a/src/core/hle/service/usb/usb.cpp b/src/core/hle/service/usb/usb.cpp index e7fb5a419..c489da071 100644 --- a/src/core/hle/service/usb/usb.cpp +++ b/src/core/hle/service/usb/usb.cpp | |||
| @@ -67,15 +67,15 @@ public: | |||
| 67 | explicit IClientEpSession() : ServiceFramework{"IClientEpSession"} { | 67 | explicit IClientEpSession() : ServiceFramework{"IClientEpSession"} { |
| 68 | // clang-format off | 68 | // clang-format off |
| 69 | static const FunctionInfo functions[] = { | 69 | static const FunctionInfo functions[] = { |
| 70 | {0, nullptr, "Unknown1"}, | 70 | {0, nullptr, "Open"}, |
| 71 | {1, nullptr, "Unknown2"}, | 71 | {1, nullptr, "Close"}, |
| 72 | {2, nullptr, "Unknown3"}, | 72 | {2, nullptr, "Unknown1"}, |
| 73 | {3, nullptr, "Unknown4"}, | 73 | {3, nullptr, "Populate"}, |
| 74 | {4, nullptr, "PostBufferAsync"}, | 74 | {4, nullptr, "PostBufferAsync"}, |
| 75 | {5, nullptr, "Unknown5"}, | 75 | {5, nullptr, "GetXferReport"}, |
| 76 | {6, nullptr, "Unknown6"}, | 76 | {6, nullptr, "Unknown2"}, |
| 77 | {7, nullptr, "Unknown7"}, | 77 | {7, nullptr, "Unknown3"}, |
| 78 | {8, nullptr, "Unknown8"}, | 78 | {8, nullptr, "Unknown4"}, |
| 79 | }; | 79 | }; |
| 80 | // clang-format on | 80 | // clang-format on |
| 81 | 81 | ||
| @@ -89,15 +89,15 @@ public: | |||
| 89 | // clang-format off | 89 | // clang-format off |
| 90 | static const FunctionInfo functions[] = { | 90 | static const FunctionInfo functions[] = { |
| 91 | {0, nullptr, "Unknown1"}, | 91 | {0, nullptr, "Unknown1"}, |
| 92 | {1, nullptr, "Unknown2"}, | 92 | {1, nullptr, "SetInterface"}, |
| 93 | {2, nullptr, "Unknown3"}, | 93 | {2, nullptr, "GetInterface"}, |
| 94 | {3, nullptr, "Unknown4"}, | 94 | {3, nullptr, "GetAlternateInterface"}, |
| 95 | {4, nullptr, "Unknown5"}, | 95 | {4, nullptr, "GetCurrentFrame"}, |
| 96 | {5, nullptr, "CtrlXferAsync"}, | 96 | {5, nullptr, "CtrlXferAsync"}, |
| 97 | {6, nullptr, "Unknown6"}, | 97 | {6, nullptr, "Unknown2"}, |
| 98 | {7, nullptr, "GetCtrlXferReport"}, | 98 | {7, nullptr, "GetCtrlXferReport"}, |
| 99 | {8, nullptr, "Unknown7"}, | 99 | {8, nullptr, "ResetDevice"}, |
| 100 | {9, nullptr, "GetClientEpSession"}, | 100 | {9, nullptr, "OpenUsbEp"}, |
| 101 | }; | 101 | }; |
| 102 | // clang-format on | 102 | // clang-format on |
| 103 | 103 | ||
| @@ -111,13 +111,14 @@ public: | |||
| 111 | // clang-format off | 111 | // clang-format off |
| 112 | static const FunctionInfo functions[] = { | 112 | static const FunctionInfo functions[] = { |
| 113 | {0, nullptr, "BindClientProcess"}, | 113 | {0, nullptr, "BindClientProcess"}, |
| 114 | {1, nullptr, "Unknown1"}, | 114 | {1, nullptr, "QueryAllInterfaces"}, |
| 115 | {2, nullptr, "Unknown2"}, | 115 | {2, nullptr, "QueryAvailableInterfaces"}, |
| 116 | {3, nullptr, "Unknown3"}, | 116 | {3, nullptr, "QueryAcquiredInterfaces"}, |
| 117 | {4, nullptr, "Unknown4"}, | 117 | {4, nullptr, "CreateInterfaceAvailableEvent"}, |
| 118 | {5, nullptr, "Unknown5"}, | 118 | {5, nullptr, "DestroyInterfaceAvailableEvent"}, |
| 119 | {6, nullptr, "GetInterfaceStateChangeEvent"}, | 119 | {6, nullptr, "GetInterfaceStateChangeEvent"}, |
| 120 | {7, nullptr, "GetClientIfSession"}, | 120 | {7, nullptr, "AcquireUsbIf"}, |
| 121 | {8, nullptr, "Unknown1"}, | ||
| 121 | }; | 122 | }; |
| 122 | // clang-format on | 123 | // clang-format on |
| 123 | 124 | ||
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp index 243b499f2..bc8e402a8 100644 --- a/src/core/loader/nro.cpp +++ b/src/core/loader/nro.cpp | |||
| @@ -127,18 +127,23 @@ static constexpr u32 PageAlignSize(u32 size) { | |||
| 127 | return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; | 127 | return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; |
| 128 | } | 128 | } |
| 129 | 129 | ||
| 130 | bool AppLoader_NRO::LoadNro(const FileSys::VfsFile& file, VAddr load_base) { | 130 | /*static*/ bool AppLoader_NRO::LoadNro(const std::vector<u8>& data, const std::string& name, |
| 131 | // Read NSO header | 131 | VAddr load_base) { |
| 132 | NroHeader nro_header{}; | 132 | |
| 133 | if (sizeof(NroHeader) != file.ReadObject(&nro_header)) { | 133 | if (data.size() < sizeof(NroHeader)) { |
| 134 | return {}; | 134 | return {}; |
| 135 | } | 135 | } |
| 136 | |||
| 137 | // Read NSO header | ||
| 138 | NroHeader nro_header{}; | ||
| 139 | std::memcpy(&nro_header, data.data(), sizeof(NroHeader)); | ||
| 136 | if (nro_header.magic != Common::MakeMagic('N', 'R', 'O', '0')) { | 140 | if (nro_header.magic != Common::MakeMagic('N', 'R', 'O', '0')) { |
| 137 | return {}; | 141 | return {}; |
| 138 | } | 142 | } |
| 139 | 143 | ||
| 140 | // Build program image | 144 | // Build program image |
| 141 | std::vector<u8> program_image = file.ReadBytes(PageAlignSize(nro_header.file_size)); | 145 | std::vector<u8> program_image(PageAlignSize(nro_header.file_size)); |
| 146 | std::memcpy(program_image.data(), data.data(), program_image.size()); | ||
| 142 | if (program_image.size() != PageAlignSize(nro_header.file_size)) { | 147 | if (program_image.size() != PageAlignSize(nro_header.file_size)) { |
| 143 | return {}; | 148 | return {}; |
| 144 | } | 149 | } |
| @@ -182,11 +187,15 @@ bool AppLoader_NRO::LoadNro(const FileSys::VfsFile& file, VAddr load_base) { | |||
| 182 | Core::CurrentProcess()->LoadModule(std::move(codeset), load_base); | 187 | Core::CurrentProcess()->LoadModule(std::move(codeset), load_base); |
| 183 | 188 | ||
| 184 | // Register module with GDBStub | 189 | // Register module with GDBStub |
| 185 | GDBStub::RegisterModule(file.GetName(), load_base, load_base); | 190 | GDBStub::RegisterModule(name, load_base, load_base); |
| 186 | 191 | ||
| 187 | return true; | 192 | return true; |
| 188 | } | 193 | } |
| 189 | 194 | ||
| 195 | bool AppLoader_NRO::LoadNro(const FileSys::VfsFile& file, VAddr load_base) { | ||
| 196 | return AppLoader_NRO::LoadNro(file.ReadAllBytes(), file.GetName(), load_base); | ||
| 197 | } | ||
| 198 | |||
| 190 | ResultStatus AppLoader_NRO::Load(Kernel::Process& process) { | 199 | ResultStatus AppLoader_NRO::Load(Kernel::Process& process) { |
| 191 | if (is_loaded) { | 200 | if (is_loaded) { |
| 192 | return ResultStatus::ErrorAlreadyLoaded; | 201 | return ResultStatus::ErrorAlreadyLoaded; |
diff --git a/src/core/loader/nro.h b/src/core/loader/nro.h index 50ee5a78a..3e6959302 100644 --- a/src/core/loader/nro.h +++ b/src/core/loader/nro.h | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <string> | 7 | #include <string> |
| 8 | #include <vector> | ||
| 8 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 9 | #include "core/loader/linker.h" | 10 | #include "core/loader/linker.h" |
| 10 | #include "core/loader/loader.h" | 11 | #include "core/loader/loader.h" |
| @@ -40,6 +41,8 @@ public: | |||
| 40 | ResultStatus ReadTitle(std::string& title) override; | 41 | ResultStatus ReadTitle(std::string& title) override; |
| 41 | bool IsRomFSUpdatable() const override; | 42 | bool IsRomFSUpdatable() const override; |
| 42 | 43 | ||
| 44 | static bool LoadNro(const std::vector<u8>& data, const std::string& name, VAddr load_base); | ||
| 45 | |||
| 43 | private: | 46 | private: |
| 44 | bool LoadNro(const FileSys::VfsFile& file, VAddr load_base); | 47 | bool LoadNro(const FileSys::VfsFile& file, VAddr load_base); |
| 45 | 48 | ||
diff --git a/src/core/perf_stats.cpp b/src/core/perf_stats.cpp index 7d95816fe..c716a462b 100644 --- a/src/core/perf_stats.cpp +++ b/src/core/perf_stats.cpp | |||
| @@ -74,10 +74,6 @@ double PerfStats::GetLastFrameTimeScale() { | |||
| 74 | } | 74 | } |
| 75 | 75 | ||
| 76 | void FrameLimiter::DoFrameLimiting(microseconds current_system_time_us) { | 76 | void FrameLimiter::DoFrameLimiting(microseconds current_system_time_us) { |
| 77 | // Max lag caused by slow frames. Can be adjusted to compensate for too many slow frames. Higher | ||
| 78 | // values increase the time needed to recover and limit framerate again after spikes. | ||
| 79 | constexpr microseconds MAX_LAG_TIME_US = 25000us; | ||
| 80 | |||
| 81 | if (!Settings::values.use_frame_limit) { | 77 | if (!Settings::values.use_frame_limit) { |
| 82 | return; | 78 | return; |
| 83 | } | 79 | } |
diff --git a/src/core/settings.h b/src/core/settings.h index 8f2da01c8..b5aeff29b 100644 --- a/src/core/settings.h +++ b/src/core/settings.h | |||
| @@ -113,7 +113,8 @@ static const std::array<const char*, NumAnalogs> mapping = {{ | |||
| 113 | struct Values { | 113 | struct Values { |
| 114 | // System | 114 | // System |
| 115 | bool use_docked_mode; | 115 | bool use_docked_mode; |
| 116 | std::string username; | 116 | bool enable_nfc; |
| 117 | int current_user; | ||
| 117 | int language_index; | 118 | int language_index; |
| 118 | 119 | ||
| 119 | // Controls | 120 | // Controls |
diff --git a/src/video_core/engines/fermi_2d.cpp b/src/video_core/engines/fermi_2d.cpp index 597b279b9..74e44c7fe 100644 --- a/src/video_core/engines/fermi_2d.cpp +++ b/src/video_core/engines/fermi_2d.cpp | |||
| @@ -47,9 +47,12 @@ void Fermi2D::HandleSurfaceCopy() { | |||
| 47 | u32 dst_bytes_per_pixel = RenderTargetBytesPerPixel(regs.dst.format); | 47 | u32 dst_bytes_per_pixel = RenderTargetBytesPerPixel(regs.dst.format); |
| 48 | 48 | ||
| 49 | if (!rasterizer.AccelerateSurfaceCopy(regs.src, regs.dst)) { | 49 | if (!rasterizer.AccelerateSurfaceCopy(regs.src, regs.dst)) { |
| 50 | // TODO(bunnei): The below implementation currently will not get hit, as | 50 | rasterizer.FlushRegion(source_cpu, src_bytes_per_pixel * regs.src.width * regs.src.height); |
| 51 | // AccelerateSurfaceCopy tries to always copy and will always return success. This should be | 51 | // We have to invalidate the destination region to evict any outdated surfaces from the |
| 52 | // changed once we properly support flushing. | 52 | // cache. We do this before actually writing the new data because the destination address |
| 53 | // might contain a dirty surface that will have to be written back to memory. | ||
| 54 | rasterizer.InvalidateRegion(dest_cpu, | ||
| 55 | dst_bytes_per_pixel * regs.dst.width * regs.dst.height); | ||
| 53 | 56 | ||
| 54 | if (regs.src.linear == regs.dst.linear) { | 57 | if (regs.src.linear == regs.dst.linear) { |
| 55 | // If the input layout and the output layout are the same, just perform a raw copy. | 58 | // If the input layout and the output layout are the same, just perform a raw copy. |
diff --git a/src/video_core/engines/kepler_memory.cpp b/src/video_core/engines/kepler_memory.cpp index 66ae6332d..585290d9f 100644 --- a/src/video_core/engines/kepler_memory.cpp +++ b/src/video_core/engines/kepler_memory.cpp | |||
| @@ -5,10 +5,14 @@ | |||
| 5 | #include "common/logging/log.h" | 5 | #include "common/logging/log.h" |
| 6 | #include "core/memory.h" | 6 | #include "core/memory.h" |
| 7 | #include "video_core/engines/kepler_memory.h" | 7 | #include "video_core/engines/kepler_memory.h" |
| 8 | #include "video_core/rasterizer_interface.h" | ||
| 8 | 9 | ||
| 9 | namespace Tegra::Engines { | 10 | namespace Tegra::Engines { |
| 10 | 11 | ||
| 11 | KeplerMemory::KeplerMemory(MemoryManager& memory_manager) : memory_manager(memory_manager) {} | 12 | KeplerMemory::KeplerMemory(VideoCore::RasterizerInterface& rasterizer, |
| 13 | MemoryManager& memory_manager) | ||
| 14 | : memory_manager(memory_manager), rasterizer{rasterizer} {} | ||
| 15 | |||
| 12 | KeplerMemory::~KeplerMemory() = default; | 16 | KeplerMemory::~KeplerMemory() = default; |
| 13 | 17 | ||
| 14 | void KeplerMemory::WriteReg(u32 method, u32 value) { | 18 | void KeplerMemory::WriteReg(u32 method, u32 value) { |
| @@ -37,6 +41,11 @@ void KeplerMemory::ProcessData(u32 data) { | |||
| 37 | VAddr dest_address = | 41 | VAddr dest_address = |
| 38 | *memory_manager.GpuToCpuAddress(address + state.write_offset * sizeof(u32)); | 42 | *memory_manager.GpuToCpuAddress(address + state.write_offset * sizeof(u32)); |
| 39 | 43 | ||
| 44 | // We have to invalidate the destination region to evict any outdated surfaces from the cache. | ||
| 45 | // We do this before actually writing the new data because the destination address might contain | ||
| 46 | // a dirty surface that will have to be written back to memory. | ||
| 47 | rasterizer.InvalidateRegion(dest_address, sizeof(u32)); | ||
| 48 | |||
| 40 | Memory::Write32(dest_address, data); | 49 | Memory::Write32(dest_address, data); |
| 41 | 50 | ||
| 42 | state.write_offset++; | 51 | state.write_offset++; |
diff --git a/src/video_core/engines/kepler_memory.h b/src/video_core/engines/kepler_memory.h index b0d0078cf..bf4a13cff 100644 --- a/src/video_core/engines/kepler_memory.h +++ b/src/video_core/engines/kepler_memory.h | |||
| @@ -11,6 +11,10 @@ | |||
| 11 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | #include "video_core/memory_manager.h" | 12 | #include "video_core/memory_manager.h" |
| 13 | 13 | ||
| 14 | namespace VideoCore { | ||
| 15 | class RasterizerInterface; | ||
| 16 | } | ||
| 17 | |||
| 14 | namespace Tegra::Engines { | 18 | namespace Tegra::Engines { |
| 15 | 19 | ||
| 16 | #define KEPLERMEMORY_REG_INDEX(field_name) \ | 20 | #define KEPLERMEMORY_REG_INDEX(field_name) \ |
| @@ -18,7 +22,7 @@ namespace Tegra::Engines { | |||
| 18 | 22 | ||
| 19 | class KeplerMemory final { | 23 | class KeplerMemory final { |
| 20 | public: | 24 | public: |
| 21 | KeplerMemory(MemoryManager& memory_manager); | 25 | KeplerMemory(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager); |
| 22 | ~KeplerMemory(); | 26 | ~KeplerMemory(); |
| 23 | 27 | ||
| 24 | /// Write the value to the register identified by method. | 28 | /// Write the value to the register identified by method. |
| @@ -72,6 +76,7 @@ public: | |||
| 72 | 76 | ||
| 73 | private: | 77 | private: |
| 74 | MemoryManager& memory_manager; | 78 | MemoryManager& memory_manager; |
| 79 | VideoCore::RasterizerInterface& rasterizer; | ||
| 75 | 80 | ||
| 76 | void ProcessData(u32 data); | 81 | void ProcessData(u32 data); |
| 77 | }; | 82 | }; |
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index 8afd26fe9..27ef865a2 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <cinttypes> | 5 | #include <cinttypes> |
| 6 | #include <cstring> | ||
| 6 | #include "common/assert.h" | 7 | #include "common/assert.h" |
| 7 | #include "core/core.h" | 8 | #include "core/core.h" |
| 8 | #include "core/core_timing.h" | 9 | #include "core/core_timing.h" |
| @@ -13,14 +14,30 @@ | |||
| 13 | #include "video_core/renderer_base.h" | 14 | #include "video_core/renderer_base.h" |
| 14 | #include "video_core/textures/texture.h" | 15 | #include "video_core/textures/texture.h" |
| 15 | 16 | ||
| 16 | namespace Tegra { | 17 | namespace Tegra::Engines { |
| 17 | namespace Engines { | ||
| 18 | 18 | ||
| 19 | /// First register id that is actually a Macro call. | 19 | /// First register id that is actually a Macro call. |
| 20 | constexpr u32 MacroRegistersStart = 0xE00; | 20 | constexpr u32 MacroRegistersStart = 0xE00; |
| 21 | 21 | ||
| 22 | Maxwell3D::Maxwell3D(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager) | 22 | Maxwell3D::Maxwell3D(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager) |
| 23 | : memory_manager(memory_manager), rasterizer{rasterizer}, macro_interpreter(*this) {} | 23 | : memory_manager(memory_manager), rasterizer{rasterizer}, macro_interpreter(*this) { |
| 24 | InitializeRegisterDefaults(); | ||
| 25 | } | ||
| 26 | |||
| 27 | void Maxwell3D::InitializeRegisterDefaults() { | ||
| 28 | // Initializes registers to their default values - what games expect them to be at boot. This is | ||
| 29 | // for certain registers that may not be explicitly set by games. | ||
| 30 | |||
| 31 | // Reset all registers to zero | ||
| 32 | std::memset(®s, 0, sizeof(regs)); | ||
| 33 | |||
| 34 | // Depth range near/far is not always set, but is expected to be the default 0.0f, 1.0f. This is | ||
| 35 | // needed for ARMS. | ||
| 36 | for (std::size_t viewport{}; viewport < Regs::NumViewports; ++viewport) { | ||
| 37 | regs.viewport[viewport].depth_range_near = 0.0f; | ||
| 38 | regs.viewport[viewport].depth_range_far = 1.0f; | ||
| 39 | } | ||
| 40 | } | ||
| 24 | 41 | ||
| 25 | void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) { | 42 | void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) { |
| 26 | // Reset the current macro. | 43 | // Reset the current macro. |
| @@ -156,7 +173,6 @@ void Maxwell3D::ProcessQueryGet() { | |||
| 156 | ASSERT_MSG(regs.query.query_get.unit == Regs::QueryUnit::Crop, | 173 | ASSERT_MSG(regs.query.query_get.unit == Regs::QueryUnit::Crop, |
| 157 | "Units other than CROP are unimplemented"); | 174 | "Units other than CROP are unimplemented"); |
| 158 | 175 | ||
| 159 | u32 value = Memory::Read32(*address); | ||
| 160 | u64 result = 0; | 176 | u64 result = 0; |
| 161 | 177 | ||
| 162 | // TODO(Subv): Support the other query variables | 178 | // TODO(Subv): Support the other query variables |
| @@ -408,5 +424,4 @@ void Maxwell3D::ProcessClearBuffers() { | |||
| 408 | rasterizer.Clear(); | 424 | rasterizer.Clear(); |
| 409 | } | 425 | } |
| 410 | 426 | ||
| 411 | } // namespace Engines | 427 | } // namespace Tegra::Engines |
| 412 | } // namespace Tegra | ||
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index c8af1c6b6..754a149fa 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h | |||
| @@ -643,8 +643,10 @@ public: | |||
| 643 | u32 d3d_cull_mode; | 643 | u32 d3d_cull_mode; |
| 644 | 644 | ||
| 645 | ComparisonOp depth_test_func; | 645 | ComparisonOp depth_test_func; |
| 646 | float alpha_test_ref; | ||
| 647 | ComparisonOp alpha_test_func; | ||
| 646 | 648 | ||
| 647 | INSERT_PADDING_WORDS(0xB); | 649 | INSERT_PADDING_WORDS(0x9); |
| 648 | 650 | ||
| 649 | struct { | 651 | struct { |
| 650 | u32 separate_alpha; | 652 | u32 separate_alpha; |
| @@ -982,6 +984,8 @@ public: | |||
| 982 | Texture::FullTextureInfo GetStageTexture(Regs::ShaderStage stage, std::size_t offset) const; | 984 | Texture::FullTextureInfo GetStageTexture(Regs::ShaderStage stage, std::size_t offset) const; |
| 983 | 985 | ||
| 984 | private: | 986 | private: |
| 987 | void InitializeRegisterDefaults(); | ||
| 988 | |||
| 985 | VideoCore::RasterizerInterface& rasterizer; | 989 | VideoCore::RasterizerInterface& rasterizer; |
| 986 | 990 | ||
| 987 | std::unordered_map<u32, std::vector<u32>> uploaded_macros; | 991 | std::unordered_map<u32, std::vector<u32>> uploaded_macros; |
diff --git a/src/video_core/engines/maxwell_compute.cpp b/src/video_core/engines/maxwell_compute.cpp index 59e28b22d..8b5f08351 100644 --- a/src/video_core/engines/maxwell_compute.cpp +++ b/src/video_core/engines/maxwell_compute.cpp | |||
| @@ -6,8 +6,7 @@ | |||
| 6 | #include "core/core.h" | 6 | #include "core/core.h" |
| 7 | #include "video_core/engines/maxwell_compute.h" | 7 | #include "video_core/engines/maxwell_compute.h" |
| 8 | 8 | ||
| 9 | namespace Tegra { | 9 | namespace Tegra::Engines { |
| 10 | namespace Engines { | ||
| 11 | 10 | ||
| 12 | void MaxwellCompute::WriteReg(u32 method, u32 value) { | 11 | void MaxwellCompute::WriteReg(u32 method, u32 value) { |
| 13 | ASSERT_MSG(method < Regs::NUM_REGS, | 12 | ASSERT_MSG(method < Regs::NUM_REGS, |
| @@ -26,5 +25,4 @@ void MaxwellCompute::WriteReg(u32 method, u32 value) { | |||
| 26 | } | 25 | } |
| 27 | } | 26 | } |
| 28 | 27 | ||
| 29 | } // namespace Engines | 28 | } // namespace Tegra::Engines |
| 30 | } // namespace Tegra | ||
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp index bf2a21bb6..b8a78cf82 100644 --- a/src/video_core/engines/maxwell_dma.cpp +++ b/src/video_core/engines/maxwell_dma.cpp | |||
| @@ -4,12 +4,13 @@ | |||
| 4 | 4 | ||
| 5 | #include "core/memory.h" | 5 | #include "core/memory.h" |
| 6 | #include "video_core/engines/maxwell_dma.h" | 6 | #include "video_core/engines/maxwell_dma.h" |
| 7 | #include "video_core/rasterizer_interface.h" | ||
| 7 | #include "video_core/textures/decoders.h" | 8 | #include "video_core/textures/decoders.h" |
| 8 | 9 | ||
| 9 | namespace Tegra { | 10 | namespace Tegra::Engines { |
| 10 | namespace Engines { | ||
| 11 | 11 | ||
| 12 | MaxwellDMA::MaxwellDMA(MemoryManager& memory_manager) : memory_manager(memory_manager) {} | 12 | MaxwellDMA::MaxwellDMA(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager) |
| 13 | : memory_manager(memory_manager), rasterizer{rasterizer} {} | ||
| 13 | 14 | ||
| 14 | void MaxwellDMA::WriteReg(u32 method, u32 value) { | 15 | void MaxwellDMA::WriteReg(u32 method, u32 value) { |
| 15 | ASSERT_MSG(method < Regs::NUM_REGS, | 16 | ASSERT_MSG(method < Regs::NUM_REGS, |
| @@ -44,40 +45,77 @@ void MaxwellDMA::HandleCopy() { | |||
| 44 | ASSERT(regs.exec.query_mode == Regs::QueryMode::None); | 45 | ASSERT(regs.exec.query_mode == Regs::QueryMode::None); |
| 45 | ASSERT(regs.exec.query_intr == Regs::QueryIntr::None); | 46 | ASSERT(regs.exec.query_intr == Regs::QueryIntr::None); |
| 46 | ASSERT(regs.exec.copy_mode == Regs::CopyMode::Unk2); | 47 | ASSERT(regs.exec.copy_mode == Regs::CopyMode::Unk2); |
| 47 | ASSERT(regs.src_params.pos_x == 0); | ||
| 48 | ASSERT(regs.src_params.pos_y == 0); | ||
| 49 | ASSERT(regs.dst_params.pos_x == 0); | 48 | ASSERT(regs.dst_params.pos_x == 0); |
| 50 | ASSERT(regs.dst_params.pos_y == 0); | 49 | ASSERT(regs.dst_params.pos_y == 0); |
| 51 | 50 | ||
| 52 | if (regs.exec.is_dst_linear == regs.exec.is_src_linear) { | 51 | if (!regs.exec.is_dst_linear && !regs.exec.is_src_linear) { |
| 53 | std::size_t copy_size = regs.x_count; | 52 | // If both the source and the destination are in block layout, assert. |
| 53 | UNREACHABLE_MSG("Tiled->Tiled DMA transfers are not yet implemented"); | ||
| 54 | return; | ||
| 55 | } | ||
| 54 | 56 | ||
| 57 | if (regs.exec.is_dst_linear && regs.exec.is_src_linear) { | ||
| 55 | // When the enable_2d bit is disabled, the copy is performed as if we were copying a 1D | 58 | // When the enable_2d bit is disabled, the copy is performed as if we were copying a 1D |
| 56 | // buffer of length `x_count`, otherwise we copy a 2D buffer of size (x_count, y_count). | 59 | // buffer of length `x_count`, otherwise we copy a 2D image of dimensions (x_count, |
| 57 | if (regs.exec.enable_2d) { | 60 | // y_count). |
| 58 | copy_size = copy_size * regs.y_count; | 61 | if (!regs.exec.enable_2d) { |
| 62 | Memory::CopyBlock(dest_cpu, source_cpu, regs.x_count); | ||
| 63 | return; | ||
| 59 | } | 64 | } |
| 60 | 65 | ||
| 61 | Memory::CopyBlock(dest_cpu, source_cpu, copy_size); | 66 | // If both the source and the destination are in linear layout, perform a line-by-line |
| 67 | // copy. We're going to take a subrect of size (x_count, y_count) from the source | ||
| 68 | // rectangle. There is no need to manually flush/invalidate the regions because | ||
| 69 | // CopyBlock does that for us. | ||
| 70 | for (u32 line = 0; line < regs.y_count; ++line) { | ||
| 71 | const VAddr source_line = source_cpu + line * regs.src_pitch; | ||
| 72 | const VAddr dest_line = dest_cpu + line * regs.dst_pitch; | ||
| 73 | Memory::CopyBlock(dest_line, source_line, regs.x_count); | ||
| 74 | } | ||
| 62 | return; | 75 | return; |
| 63 | } | 76 | } |
| 64 | 77 | ||
| 65 | ASSERT(regs.exec.enable_2d == 1); | 78 | ASSERT(regs.exec.enable_2d == 1); |
| 66 | u8* src_buffer = Memory::GetPointer(source_cpu); | 79 | |
| 67 | u8* dst_buffer = Memory::GetPointer(dest_cpu); | 80 | const std::size_t copy_size = regs.x_count * regs.y_count; |
| 81 | |||
| 82 | const auto FlushAndInvalidate = [&](u32 src_size, u64 dst_size) { | ||
| 83 | // TODO(Subv): For now, manually flush the regions until we implement GPU-accelerated | ||
| 84 | // copying. | ||
| 85 | rasterizer.FlushRegion(source_cpu, src_size); | ||
| 86 | |||
| 87 | // We have to invalidate the destination region to evict any outdated surfaces from the | ||
| 88 | // cache. We do this before actually writing the new data because the destination address | ||
| 89 | // might contain a dirty surface that will have to be written back to memory. | ||
| 90 | rasterizer.InvalidateRegion(dest_cpu, dst_size); | ||
| 91 | }; | ||
| 68 | 92 | ||
| 69 | if (regs.exec.is_dst_linear && !regs.exec.is_src_linear) { | 93 | if (regs.exec.is_dst_linear && !regs.exec.is_src_linear) { |
| 94 | ASSERT(regs.src_params.size_z == 1); | ||
| 70 | // If the input is tiled and the output is linear, deswizzle the input and copy it over. | 95 | // If the input is tiled and the output is linear, deswizzle the input and copy it over. |
| 71 | Texture::CopySwizzledData(regs.src_params.size_x, regs.src_params.size_y, | 96 | |
| 72 | regs.src_params.size_z, 1, 1, src_buffer, dst_buffer, true, | 97 | const u32 src_bytes_per_pixel = regs.src_pitch / regs.src_params.size_x; |
| 73 | regs.src_params.BlockHeight(), regs.src_params.BlockDepth()); | 98 | |
| 99 | FlushAndInvalidate(regs.src_pitch * regs.src_params.size_y, | ||
| 100 | copy_size * src_bytes_per_pixel); | ||
| 101 | |||
| 102 | Texture::UnswizzleSubrect(regs.x_count, regs.y_count, regs.dst_pitch, | ||
| 103 | regs.src_params.size_x, src_bytes_per_pixel, source_cpu, dest_cpu, | ||
| 104 | regs.src_params.BlockHeight(), regs.src_params.pos_x, | ||
| 105 | regs.src_params.pos_y); | ||
| 74 | } else { | 106 | } else { |
| 107 | ASSERT(regs.dst_params.size_z == 1); | ||
| 108 | ASSERT(regs.src_pitch == regs.x_count); | ||
| 109 | |||
| 110 | const u32 src_bpp = regs.src_pitch / regs.x_count; | ||
| 111 | |||
| 112 | FlushAndInvalidate(regs.src_pitch * regs.y_count, | ||
| 113 | regs.dst_params.size_x * regs.dst_params.size_y * src_bpp); | ||
| 114 | |||
| 75 | // If the input is linear and the output is tiled, swizzle the input and copy it over. | 115 | // If the input is linear and the output is tiled, swizzle the input and copy it over. |
| 76 | Texture::CopySwizzledData(regs.dst_params.size_x, regs.dst_params.size_y, | 116 | Texture::SwizzleSubrect(regs.x_count, regs.y_count, regs.src_pitch, regs.dst_params.size_x, |
| 77 | regs.dst_params.size_z, 1, 1, dst_buffer, src_buffer, false, | 117 | src_bpp, dest_cpu, source_cpu, regs.dst_params.BlockHeight()); |
| 78 | regs.dst_params.BlockHeight(), regs.dst_params.BlockDepth()); | ||
| 79 | } | 118 | } |
| 80 | } | 119 | } |
| 81 | 120 | ||
| 82 | } // namespace Engines | 121 | } // namespace Tegra::Engines |
| 83 | } // namespace Tegra | ||
diff --git a/src/video_core/engines/maxwell_dma.h b/src/video_core/engines/maxwell_dma.h index df19e02e2..5f3704f05 100644 --- a/src/video_core/engines/maxwell_dma.h +++ b/src/video_core/engines/maxwell_dma.h | |||
| @@ -12,11 +12,15 @@ | |||
| 12 | #include "video_core/gpu.h" | 12 | #include "video_core/gpu.h" |
| 13 | #include "video_core/memory_manager.h" | 13 | #include "video_core/memory_manager.h" |
| 14 | 14 | ||
| 15 | namespace VideoCore { | ||
| 16 | class RasterizerInterface; | ||
| 17 | } | ||
| 18 | |||
| 15 | namespace Tegra::Engines { | 19 | namespace Tegra::Engines { |
| 16 | 20 | ||
| 17 | class MaxwellDMA final { | 21 | class MaxwellDMA final { |
| 18 | public: | 22 | public: |
| 19 | explicit MaxwellDMA(MemoryManager& memory_manager); | 23 | explicit MaxwellDMA(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager); |
| 20 | ~MaxwellDMA() = default; | 24 | ~MaxwellDMA() = default; |
| 21 | 25 | ||
| 22 | /// Write the value to the register identified by method. | 26 | /// Write the value to the register identified by method. |
| @@ -133,6 +137,8 @@ public: | |||
| 133 | MemoryManager& memory_manager; | 137 | MemoryManager& memory_manager; |
| 134 | 138 | ||
| 135 | private: | 139 | private: |
| 140 | VideoCore::RasterizerInterface& rasterizer; | ||
| 141 | |||
| 136 | /// Performs the copy from the source buffer to the destination buffer as configured in the | 142 | /// Performs the copy from the source buffer to the destination buffer as configured in the |
| 137 | /// registers. | 143 | /// registers. |
| 138 | void HandleCopy(); | 144 | void HandleCopy(); |
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index f356f9a03..af7756266 100644 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h | |||
| @@ -79,6 +79,7 @@ union Attribute { | |||
| 79 | constexpr explicit Attribute(u64 value) : value(value) {} | 79 | constexpr explicit Attribute(u64 value) : value(value) {} |
| 80 | 80 | ||
| 81 | enum class Index : u64 { | 81 | enum class Index : u64 { |
| 82 | PointSize = 6, | ||
| 82 | Position = 7, | 83 | Position = 7, |
| 83 | Attribute_0 = 8, | 84 | Attribute_0 = 8, |
| 84 | Attribute_31 = 39, | 85 | Attribute_31 = 39, |
| @@ -214,7 +215,7 @@ enum class IMinMaxExchange : u64 { | |||
| 214 | XHi = 3, | 215 | XHi = 3, |
| 215 | }; | 216 | }; |
| 216 | 217 | ||
| 217 | enum class VmadType : u64 { | 218 | enum class VideoType : u64 { |
| 218 | Size16_Low = 0, | 219 | Size16_Low = 0, |
| 219 | Size16_High = 1, | 220 | Size16_High = 1, |
| 220 | Size32 = 2, | 221 | Size32 = 2, |
| @@ -335,6 +336,26 @@ enum class IsberdMode : u64 { | |||
| 335 | 336 | ||
| 336 | enum class IsberdShift : u64 { None = 0, U16 = 1, B32 = 2 }; | 337 | enum class IsberdShift : u64 { None = 0, U16 = 1, B32 = 2 }; |
| 337 | 338 | ||
| 339 | enum class HalfType : u64 { | ||
| 340 | H0_H1 = 0, | ||
| 341 | F32 = 1, | ||
| 342 | H0_H0 = 2, | ||
| 343 | H1_H1 = 3, | ||
| 344 | }; | ||
| 345 | |||
| 346 | enum class HalfMerge : u64 { | ||
| 347 | H0_H1 = 0, | ||
| 348 | F32 = 1, | ||
| 349 | Mrg_H0 = 2, | ||
| 350 | Mrg_H1 = 3, | ||
| 351 | }; | ||
| 352 | |||
| 353 | enum class HalfPrecision : u64 { | ||
| 354 | None = 0, | ||
| 355 | FTZ = 1, | ||
| 356 | FMZ = 2, | ||
| 357 | }; | ||
| 358 | |||
| 338 | enum class IpaInterpMode : u64 { | 359 | enum class IpaInterpMode : u64 { |
| 339 | Linear = 0, | 360 | Linear = 0, |
| 340 | Perspective = 1, | 361 | Perspective = 1, |
| @@ -544,6 +565,10 @@ union Instruction { | |||
| 544 | } fmul; | 565 | } fmul; |
| 545 | 566 | ||
| 546 | union { | 567 | union { |
| 568 | BitField<55, 1, u64> saturate; | ||
| 569 | } fmul32; | ||
| 570 | |||
| 571 | union { | ||
| 547 | BitField<48, 1, u64> is_signed; | 572 | BitField<48, 1, u64> is_signed; |
| 548 | } shift; | 573 | } shift; |
| 549 | 574 | ||
| @@ -554,6 +579,70 @@ union Instruction { | |||
| 554 | } alu_integer; | 579 | } alu_integer; |
| 555 | 580 | ||
| 556 | union { | 581 | union { |
| 582 | BitField<39, 1, u64> ftz; | ||
| 583 | BitField<32, 1, u64> saturate; | ||
| 584 | BitField<49, 2, HalfMerge> merge; | ||
| 585 | |||
| 586 | BitField<43, 1, u64> negate_a; | ||
| 587 | BitField<44, 1, u64> abs_a; | ||
| 588 | BitField<47, 2, HalfType> type_a; | ||
| 589 | |||
| 590 | BitField<31, 1, u64> negate_b; | ||
| 591 | BitField<30, 1, u64> abs_b; | ||
| 592 | BitField<47, 2, HalfType> type_b; | ||
| 593 | |||
| 594 | BitField<35, 2, HalfType> type_c; | ||
| 595 | } alu_half; | ||
| 596 | |||
| 597 | union { | ||
| 598 | BitField<39, 2, HalfPrecision> precision; | ||
| 599 | BitField<39, 1, u64> ftz; | ||
| 600 | BitField<52, 1, u64> saturate; | ||
| 601 | BitField<49, 2, HalfMerge> merge; | ||
| 602 | |||
| 603 | BitField<43, 1, u64> negate_a; | ||
| 604 | BitField<44, 1, u64> abs_a; | ||
| 605 | BitField<47, 2, HalfType> type_a; | ||
| 606 | } alu_half_imm; | ||
| 607 | |||
| 608 | union { | ||
| 609 | BitField<29, 1, u64> first_negate; | ||
| 610 | BitField<20, 9, u64> first; | ||
| 611 | |||
| 612 | BitField<56, 1, u64> second_negate; | ||
| 613 | BitField<30, 9, u64> second; | ||
| 614 | |||
| 615 | u32 PackImmediates() const { | ||
| 616 | // Immediates are half floats shifted. | ||
| 617 | constexpr u32 imm_shift = 6; | ||
| 618 | return static_cast<u32>((first << imm_shift) | (second << (16 + imm_shift))); | ||
| 619 | } | ||
| 620 | } half_imm; | ||
| 621 | |||
| 622 | union { | ||
| 623 | union { | ||
| 624 | BitField<37, 2, HalfPrecision> precision; | ||
| 625 | BitField<32, 1, u64> saturate; | ||
| 626 | |||
| 627 | BitField<30, 1, u64> negate_c; | ||
| 628 | BitField<35, 2, HalfType> type_c; | ||
| 629 | } rr; | ||
| 630 | |||
| 631 | BitField<57, 2, HalfPrecision> precision; | ||
| 632 | BitField<52, 1, u64> saturate; | ||
| 633 | |||
| 634 | BitField<49, 2, HalfMerge> merge; | ||
| 635 | |||
| 636 | BitField<47, 2, HalfType> type_a; | ||
| 637 | |||
| 638 | BitField<56, 1, u64> negate_b; | ||
| 639 | BitField<28, 2, HalfType> type_b; | ||
| 640 | |||
| 641 | BitField<51, 1, u64> negate_c; | ||
| 642 | BitField<53, 2, HalfType> type_reg39; | ||
| 643 | } hfma2; | ||
| 644 | |||
| 645 | union { | ||
| 557 | BitField<40, 1, u64> invert; | 646 | BitField<40, 1, u64> invert; |
| 558 | } popc; | 647 | } popc; |
| 559 | 648 | ||
| @@ -669,7 +758,6 @@ union Instruction { | |||
| 669 | BitField<45, 2, PredOperation> op; | 758 | BitField<45, 2, PredOperation> op; |
| 670 | BitField<47, 1, u64> ftz; | 759 | BitField<47, 1, u64> ftz; |
| 671 | BitField<48, 4, PredCondition> cond; | 760 | BitField<48, 4, PredCondition> cond; |
| 672 | BitField<56, 1, u64> neg_b; | ||
| 673 | } fsetp; | 761 | } fsetp; |
| 674 | 762 | ||
| 675 | union { | 763 | union { |
| @@ -696,6 +784,14 @@ union Instruction { | |||
| 696 | } psetp; | 784 | } psetp; |
| 697 | 785 | ||
| 698 | union { | 786 | union { |
| 787 | BitField<43, 4, PredCondition> cond; | ||
| 788 | BitField<45, 2, PredOperation> op; | ||
| 789 | BitField<3, 3, u64> pred3; | ||
| 790 | BitField<0, 3, u64> pred0; | ||
| 791 | BitField<39, 3, u64> pred39; | ||
| 792 | } vsetp; | ||
| 793 | |||
| 794 | union { | ||
| 699 | BitField<12, 3, u64> pred12; | 795 | BitField<12, 3, u64> pred12; |
| 700 | BitField<15, 1, u64> neg_pred12; | 796 | BitField<15, 1, u64> neg_pred12; |
| 701 | BitField<24, 2, PredOperation> cond; | 797 | BitField<24, 2, PredOperation> cond; |
| @@ -717,6 +813,23 @@ union Instruction { | |||
| 717 | } csetp; | 813 | } csetp; |
| 718 | 814 | ||
| 719 | union { | 815 | union { |
| 816 | BitField<35, 4, PredCondition> cond; | ||
| 817 | BitField<49, 1, u64> h_and; | ||
| 818 | BitField<6, 1, u64> ftz; | ||
| 819 | BitField<45, 2, PredOperation> op; | ||
| 820 | BitField<3, 3, u64> pred3; | ||
| 821 | BitField<0, 3, u64> pred0; | ||
| 822 | BitField<43, 1, u64> negate_a; | ||
| 823 | BitField<44, 1, u64> abs_a; | ||
| 824 | BitField<47, 2, HalfType> type_a; | ||
| 825 | BitField<31, 1, u64> negate_b; | ||
| 826 | BitField<30, 1, u64> abs_b; | ||
| 827 | BitField<28, 2, HalfType> type_b; | ||
| 828 | BitField<42, 1, u64> neg_pred; | ||
| 829 | BitField<39, 3, u64> pred39; | ||
| 830 | } hsetp2; | ||
| 831 | |||
| 832 | union { | ||
| 720 | BitField<39, 3, u64> pred39; | 833 | BitField<39, 3, u64> pred39; |
| 721 | BitField<42, 1, u64> neg_pred; | 834 | BitField<42, 1, u64> neg_pred; |
| 722 | BitField<43, 1, u64> neg_a; | 835 | BitField<43, 1, u64> neg_a; |
| @@ -727,10 +840,24 @@ union Instruction { | |||
| 727 | BitField<53, 1, u64> neg_b; | 840 | BitField<53, 1, u64> neg_b; |
| 728 | BitField<54, 1, u64> abs_a; | 841 | BitField<54, 1, u64> abs_a; |
| 729 | BitField<55, 1, u64> ftz; | 842 | BitField<55, 1, u64> ftz; |
| 730 | BitField<56, 1, u64> neg_imm; | ||
| 731 | } fset; | 843 | } fset; |
| 732 | 844 | ||
| 733 | union { | 845 | union { |
| 846 | BitField<49, 1, u64> bf; | ||
| 847 | BitField<35, 3, PredCondition> cond; | ||
| 848 | BitField<50, 1, u64> ftz; | ||
| 849 | BitField<45, 2, PredOperation> op; | ||
| 850 | BitField<43, 1, u64> negate_a; | ||
| 851 | BitField<44, 1, u64> abs_a; | ||
| 852 | BitField<47, 2, HalfType> type_a; | ||
| 853 | BitField<31, 1, u64> negate_b; | ||
| 854 | BitField<30, 1, u64> abs_b; | ||
| 855 | BitField<28, 2, HalfType> type_b; | ||
| 856 | BitField<42, 1, u64> neg_pred; | ||
| 857 | BitField<39, 3, u64> pred39; | ||
| 858 | } hset2; | ||
| 859 | |||
| 860 | union { | ||
| 734 | BitField<39, 3, u64> pred39; | 861 | BitField<39, 3, u64> pred39; |
| 735 | BitField<42, 1, u64> neg_pred; | 862 | BitField<42, 1, u64> neg_pred; |
| 736 | BitField<44, 1, u64> bf; | 863 | BitField<44, 1, u64> bf; |
| @@ -1036,15 +1163,17 @@ union Instruction { | |||
| 1036 | union { | 1163 | union { |
| 1037 | BitField<48, 1, u64> signed_a; | 1164 | BitField<48, 1, u64> signed_a; |
| 1038 | BitField<38, 1, u64> is_byte_chunk_a; | 1165 | BitField<38, 1, u64> is_byte_chunk_a; |
| 1039 | BitField<36, 2, VmadType> type_a; | 1166 | BitField<36, 2, VideoType> type_a; |
| 1040 | BitField<36, 2, u64> byte_height_a; | 1167 | BitField<36, 2, u64> byte_height_a; |
| 1041 | 1168 | ||
| 1042 | BitField<49, 1, u64> signed_b; | 1169 | BitField<49, 1, u64> signed_b; |
| 1043 | BitField<50, 1, u64> use_register_b; | 1170 | BitField<50, 1, u64> use_register_b; |
| 1044 | BitField<30, 1, u64> is_byte_chunk_b; | 1171 | BitField<30, 1, u64> is_byte_chunk_b; |
| 1045 | BitField<28, 2, VmadType> type_b; | 1172 | BitField<28, 2, VideoType> type_b; |
| 1046 | BitField<28, 2, u64> byte_height_b; | 1173 | BitField<28, 2, u64> byte_height_b; |
| 1174 | } video; | ||
| 1047 | 1175 | ||
| 1176 | union { | ||
| 1048 | BitField<51, 2, VmadShr> shr; | 1177 | BitField<51, 2, VmadShr> shr; |
| 1049 | BitField<55, 1, u64> saturate; // Saturates the result (a * b + c) | 1178 | BitField<55, 1, u64> saturate; // Saturates the result (a * b + c) |
| 1050 | BitField<47, 1, u64> cc; | 1179 | BitField<47, 1, u64> cc; |
| @@ -1095,11 +1224,13 @@ public: | |||
| 1095 | KIL, | 1224 | KIL, |
| 1096 | SSY, | 1225 | SSY, |
| 1097 | SYNC, | 1226 | SYNC, |
| 1227 | BRK, | ||
| 1098 | DEPBAR, | 1228 | DEPBAR, |
| 1099 | BFE_C, | 1229 | BFE_C, |
| 1100 | BFE_R, | 1230 | BFE_R, |
| 1101 | BFE_IMM, | 1231 | BFE_IMM, |
| 1102 | BRA, | 1232 | BRA, |
| 1233 | PBK, | ||
| 1103 | LD_A, | 1234 | LD_A, |
| 1104 | LD_C, | 1235 | LD_C, |
| 1105 | ST_A, | 1236 | ST_A, |
| @@ -1118,6 +1249,7 @@ public: | |||
| 1118 | OUT_R, // Emit vertex/primitive | 1249 | OUT_R, // Emit vertex/primitive |
| 1119 | ISBERD, | 1250 | ISBERD, |
| 1120 | VMAD, | 1251 | VMAD, |
| 1252 | VSETP, | ||
| 1121 | FFMA_IMM, // Fused Multiply and Add | 1253 | FFMA_IMM, // Fused Multiply and Add |
| 1122 | FFMA_CR, | 1254 | FFMA_CR, |
| 1123 | FFMA_RC, | 1255 | FFMA_RC, |
| @@ -1145,6 +1277,18 @@ public: | |||
| 1145 | LEA_RZ, | 1277 | LEA_RZ, |
| 1146 | LEA_IMM, | 1278 | LEA_IMM, |
| 1147 | LEA_HI, | 1279 | LEA_HI, |
| 1280 | HADD2_C, | ||
| 1281 | HADD2_R, | ||
| 1282 | HADD2_IMM, | ||
| 1283 | HMUL2_C, | ||
| 1284 | HMUL2_R, | ||
| 1285 | HMUL2_IMM, | ||
| 1286 | HFMA2_CR, | ||
| 1287 | HFMA2_RC, | ||
| 1288 | HFMA2_RR, | ||
| 1289 | HFMA2_IMM_R, | ||
| 1290 | HSETP2_R, | ||
| 1291 | HSET2_R, | ||
| 1148 | POPC_C, | 1292 | POPC_C, |
| 1149 | POPC_R, | 1293 | POPC_R, |
| 1150 | POPC_IMM, | 1294 | POPC_IMM, |
| @@ -1218,9 +1362,12 @@ public: | |||
| 1218 | ArithmeticImmediate, | 1362 | ArithmeticImmediate, |
| 1219 | ArithmeticInteger, | 1363 | ArithmeticInteger, |
| 1220 | ArithmeticIntegerImmediate, | 1364 | ArithmeticIntegerImmediate, |
| 1365 | ArithmeticHalf, | ||
| 1366 | ArithmeticHalfImmediate, | ||
| 1221 | Bfe, | 1367 | Bfe, |
| 1222 | Shift, | 1368 | Shift, |
| 1223 | Ffma, | 1369 | Ffma, |
| 1370 | Hfma2, | ||
| 1224 | Flow, | 1371 | Flow, |
| 1225 | Synch, | 1372 | Synch, |
| 1226 | Memory, | 1373 | Memory, |
| @@ -1228,6 +1375,8 @@ public: | |||
| 1228 | FloatSetPredicate, | 1375 | FloatSetPredicate, |
| 1229 | IntegerSet, | 1376 | IntegerSet, |
| 1230 | IntegerSetPredicate, | 1377 | IntegerSetPredicate, |
| 1378 | HalfSet, | ||
| 1379 | HalfSetPredicate, | ||
| 1231 | PredicateSetPredicate, | 1380 | PredicateSetPredicate, |
| 1232 | PredicateSetRegister, | 1381 | PredicateSetRegister, |
| 1233 | Conversion, | 1382 | Conversion, |
| @@ -1239,7 +1388,7 @@ public: | |||
| 1239 | /// conditionally executed). | 1388 | /// conditionally executed). |
| 1240 | static bool IsPredicatedInstruction(Id opcode) { | 1389 | static bool IsPredicatedInstruction(Id opcode) { |
| 1241 | // TODO(Subv): Add the rest of unpredicated instructions. | 1390 | // TODO(Subv): Add the rest of unpredicated instructions. |
| 1242 | return opcode != Id::SSY; | 1391 | return opcode != Id::SSY && opcode != Id::PBK; |
| 1243 | } | 1392 | } |
| 1244 | 1393 | ||
| 1245 | class Matcher { | 1394 | class Matcher { |
| @@ -1335,9 +1484,11 @@ private: | |||
| 1335 | #define INST(bitstring, op, type, name) Detail::GetMatcher(bitstring, op, type, name) | 1484 | #define INST(bitstring, op, type, name) Detail::GetMatcher(bitstring, op, type, name) |
| 1336 | INST("111000110011----", Id::KIL, Type::Flow, "KIL"), | 1485 | INST("111000110011----", Id::KIL, Type::Flow, "KIL"), |
| 1337 | INST("111000101001----", Id::SSY, Type::Flow, "SSY"), | 1486 | INST("111000101001----", Id::SSY, Type::Flow, "SSY"), |
| 1487 | INST("111000101010----", Id::PBK, Type::Flow, "PBK"), | ||
| 1338 | INST("111000100100----", Id::BRA, Type::Flow, "BRA"), | 1488 | INST("111000100100----", Id::BRA, Type::Flow, "BRA"), |
| 1489 | INST("1111000011111---", Id::SYNC, Type::Flow, "SYNC"), | ||
| 1490 | INST("111000110100---", Id::BRK, Type::Flow, "BRK"), | ||
| 1339 | INST("1111000011110---", Id::DEPBAR, Type::Synch, "DEPBAR"), | 1491 | INST("1111000011110---", Id::DEPBAR, Type::Synch, "DEPBAR"), |
| 1340 | INST("1111000011111---", Id::SYNC, Type::Synch, "SYNC"), | ||
| 1341 | INST("1110111111011---", Id::LD_A, Type::Memory, "LD_A"), | 1492 | INST("1110111111011---", Id::LD_A, Type::Memory, "LD_A"), |
| 1342 | INST("1110111110010---", Id::LD_C, Type::Memory, "LD_C"), | 1493 | INST("1110111110010---", Id::LD_C, Type::Memory, "LD_C"), |
| 1343 | INST("1110111111110---", Id::ST_A, Type::Memory, "ST_A"), | 1494 | INST("1110111111110---", Id::ST_A, Type::Memory, "ST_A"), |
| @@ -1356,6 +1507,7 @@ private: | |||
| 1356 | INST("1111101111100---", Id::OUT_R, Type::Trivial, "OUT_R"), | 1507 | INST("1111101111100---", Id::OUT_R, Type::Trivial, "OUT_R"), |
| 1357 | INST("1110111111010---", Id::ISBERD, Type::Trivial, "ISBERD"), | 1508 | INST("1110111111010---", Id::ISBERD, Type::Trivial, "ISBERD"), |
| 1358 | INST("01011111--------", Id::VMAD, Type::Trivial, "VMAD"), | 1509 | INST("01011111--------", Id::VMAD, Type::Trivial, "VMAD"), |
| 1510 | INST("0101000011110---", Id::VSETP, Type::Trivial, "VSETP"), | ||
| 1359 | INST("0011001-1-------", Id::FFMA_IMM, Type::Ffma, "FFMA_IMM"), | 1511 | INST("0011001-1-------", Id::FFMA_IMM, Type::Ffma, "FFMA_IMM"), |
| 1360 | INST("010010011-------", Id::FFMA_CR, Type::Ffma, "FFMA_CR"), | 1512 | INST("010010011-------", Id::FFMA_CR, Type::Ffma, "FFMA_CR"), |
| 1361 | INST("010100011-------", Id::FFMA_RC, Type::Ffma, "FFMA_RC"), | 1513 | INST("010100011-------", Id::FFMA_RC, Type::Ffma, "FFMA_RC"), |
| @@ -1389,6 +1541,18 @@ private: | |||
| 1389 | INST("001101101101----", Id::LEA_IMM, Type::ArithmeticInteger, "LEA_IMM"), | 1541 | INST("001101101101----", Id::LEA_IMM, Type::ArithmeticInteger, "LEA_IMM"), |
| 1390 | INST("010010111101----", Id::LEA_RZ, Type::ArithmeticInteger, "LEA_RZ"), | 1542 | INST("010010111101----", Id::LEA_RZ, Type::ArithmeticInteger, "LEA_RZ"), |
| 1391 | INST("00011000--------", Id::LEA_HI, Type::ArithmeticInteger, "LEA_HI"), | 1543 | INST("00011000--------", Id::LEA_HI, Type::ArithmeticInteger, "LEA_HI"), |
| 1544 | INST("0111101-1-------", Id::HADD2_C, Type::ArithmeticHalf, "HADD2_C"), | ||
| 1545 | INST("0101110100010---", Id::HADD2_R, Type::ArithmeticHalf, "HADD2_R"), | ||
| 1546 | INST("0111101-0-------", Id::HADD2_IMM, Type::ArithmeticHalfImmediate, "HADD2_IMM"), | ||
| 1547 | INST("0111100-1-------", Id::HMUL2_C, Type::ArithmeticHalf, "HMUL2_C"), | ||
| 1548 | INST("0101110100001---", Id::HMUL2_R, Type::ArithmeticHalf, "HMUL2_R"), | ||
| 1549 | INST("0111100-0-------", Id::HMUL2_IMM, Type::ArithmeticHalfImmediate, "HMUL2_IMM"), | ||
| 1550 | INST("01110---1-------", Id::HFMA2_CR, Type::Hfma2, "HFMA2_CR"), | ||
| 1551 | INST("01100---1-------", Id::HFMA2_RC, Type::Hfma2, "HFMA2_RC"), | ||
| 1552 | INST("0101110100000---", Id::HFMA2_RR, Type::Hfma2, "HFMA2_RR"), | ||
| 1553 | INST("01110---0-------", Id::HFMA2_IMM_R, Type::Hfma2, "HFMA2_R_IMM"), | ||
| 1554 | INST("0101110100100---", Id::HSETP2_R, Type::HalfSetPredicate, "HSETP_R"), | ||
| 1555 | INST("0101110100011---", Id::HSET2_R, Type::HalfSet, "HSET2_R"), | ||
| 1392 | INST("0101000010000---", Id::MUFU, Type::Arithmetic, "MUFU"), | 1556 | INST("0101000010000---", Id::MUFU, Type::Arithmetic, "MUFU"), |
| 1393 | INST("0100110010010---", Id::RRO_C, Type::Arithmetic, "RRO_C"), | 1557 | INST("0100110010010---", Id::RRO_C, Type::Arithmetic, "RRO_C"), |
| 1394 | INST("0101110010010---", Id::RRO_R, Type::Arithmetic, "RRO_R"), | 1558 | INST("0101110010010---", Id::RRO_R, Type::Arithmetic, "RRO_R"), |
| @@ -1463,4 +1627,4 @@ private: | |||
| 1463 | } | 1627 | } |
| 1464 | }; | 1628 | }; |
| 1465 | 1629 | ||
| 1466 | } // namespace Tegra::Shader | 1630 | } // namespace Tegra::Shader \ No newline at end of file |
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index 9ba7e3533..83c7e5b0b 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp | |||
| @@ -27,8 +27,8 @@ GPU::GPU(VideoCore::RasterizerInterface& rasterizer) { | |||
| 27 | maxwell_3d = std::make_unique<Engines::Maxwell3D>(rasterizer, *memory_manager); | 27 | maxwell_3d = std::make_unique<Engines::Maxwell3D>(rasterizer, *memory_manager); |
| 28 | fermi_2d = std::make_unique<Engines::Fermi2D>(rasterizer, *memory_manager); | 28 | fermi_2d = std::make_unique<Engines::Fermi2D>(rasterizer, *memory_manager); |
| 29 | maxwell_compute = std::make_unique<Engines::MaxwellCompute>(); | 29 | maxwell_compute = std::make_unique<Engines::MaxwellCompute>(); |
| 30 | maxwell_dma = std::make_unique<Engines::MaxwellDMA>(*memory_manager); | 30 | maxwell_dma = std::make_unique<Engines::MaxwellDMA>(rasterizer, *memory_manager); |
| 31 | kepler_memory = std::make_unique<Engines::KeplerMemory>(*memory_manager); | 31 | kepler_memory = std::make_unique<Engines::KeplerMemory>(rasterizer, *memory_manager); |
| 32 | } | 32 | } |
| 33 | 33 | ||
| 34 | GPU::~GPU() = default; | 34 | GPU::~GPU() = default; |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 468253033..b472f421f 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp | |||
| @@ -570,10 +570,12 @@ void RasterizerOpenGL::DrawArrays() { | |||
| 570 | SyncBlendState(); | 570 | SyncBlendState(); |
| 571 | SyncLogicOpState(); | 571 | SyncLogicOpState(); |
| 572 | SyncCullMode(); | 572 | SyncCullMode(); |
| 573 | SyncAlphaTest(); | 573 | SyncDepthRange(); |
| 574 | SyncScissorTest(); | 574 | SyncScissorTest(); |
| 575 | // Alpha Testing is synced on shaders. | ||
| 575 | SyncTransformFeedback(); | 576 | SyncTransformFeedback(); |
| 576 | SyncPointState(); | 577 | SyncPointState(); |
| 578 | CheckAlphaTests(); | ||
| 577 | 579 | ||
| 578 | // TODO(bunnei): Sync framebuffer_scale uniform here | 580 | // TODO(bunnei): Sync framebuffer_scale uniform here |
| 579 | // TODO(bunnei): Sync scissorbox uniform(s) here | 581 | // TODO(bunnei): Sync scissorbox uniform(s) here |
| @@ -659,6 +661,12 @@ void RasterizerOpenGL::FlushAndInvalidateRegion(VAddr addr, u64 size) { | |||
| 659 | bool RasterizerOpenGL::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Regs::Surface& src, | 661 | bool RasterizerOpenGL::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Regs::Surface& src, |
| 660 | const Tegra::Engines::Fermi2D::Regs::Surface& dst) { | 662 | const Tegra::Engines::Fermi2D::Regs::Surface& dst) { |
| 661 | MICROPROFILE_SCOPE(OpenGL_Blits); | 663 | MICROPROFILE_SCOPE(OpenGL_Blits); |
| 664 | |||
| 665 | if (Settings::values.use_accurate_gpu_emulation) { | ||
| 666 | // Skip the accelerated copy and perform a slow but more accurate copy | ||
| 667 | return false; | ||
| 668 | } | ||
| 669 | |||
| 662 | res_cache.FermiCopySurface(src, dst); | 670 | res_cache.FermiCopySurface(src, dst); |
| 663 | return true; | 671 | return true; |
| 664 | } | 672 | } |
| @@ -916,12 +924,11 @@ void RasterizerOpenGL::SyncCullMode() { | |||
| 916 | } | 924 | } |
| 917 | } | 925 | } |
| 918 | 926 | ||
| 919 | void RasterizerOpenGL::SyncDepthScale() { | 927 | void RasterizerOpenGL::SyncDepthRange() { |
| 920 | UNREACHABLE(); | 928 | const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; |
| 921 | } | ||
| 922 | 929 | ||
| 923 | void RasterizerOpenGL::SyncDepthOffset() { | 930 | state.depth.depth_range_near = regs.viewport->depth_range_near; |
| 924 | UNREACHABLE(); | 931 | state.depth.depth_range_far = regs.viewport->depth_range_far; |
| 925 | } | 932 | } |
| 926 | 933 | ||
| 927 | void RasterizerOpenGL::SyncDepthTestState() { | 934 | void RasterizerOpenGL::SyncDepthTestState() { |
| @@ -1001,17 +1008,6 @@ void RasterizerOpenGL::SyncLogicOpState() { | |||
| 1001 | state.logic_op.operation = MaxwellToGL::LogicOp(regs.logic_op.operation); | 1008 | state.logic_op.operation = MaxwellToGL::LogicOp(regs.logic_op.operation); |
| 1002 | } | 1009 | } |
| 1003 | 1010 | ||
| 1004 | void RasterizerOpenGL::SyncAlphaTest() { | ||
| 1005 | const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; | ||
| 1006 | |||
| 1007 | // TODO(Rodrigo): Alpha testing is a legacy OpenGL feature, but it can be | ||
| 1008 | // implemented with a test+discard in fragment shaders. | ||
| 1009 | if (regs.alpha_test_enabled != 0) { | ||
| 1010 | LOG_CRITICAL(Render_OpenGL, "Alpha testing is not implemented"); | ||
| 1011 | UNREACHABLE(); | ||
| 1012 | } | ||
| 1013 | } | ||
| 1014 | |||
| 1015 | void RasterizerOpenGL::SyncScissorTest() { | 1011 | void RasterizerOpenGL::SyncScissorTest() { |
| 1016 | const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; | 1012 | const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; |
| 1017 | 1013 | ||
| @@ -1046,4 +1042,15 @@ void RasterizerOpenGL::SyncPointState() { | |||
| 1046 | state.point.size = regs.point_size == 0 ? 1 : regs.point_size; | 1042 | state.point.size = regs.point_size == 0 ? 1 : regs.point_size; |
| 1047 | } | 1043 | } |
| 1048 | 1044 | ||
| 1045 | void RasterizerOpenGL::CheckAlphaTests() { | ||
| 1046 | const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; | ||
| 1047 | |||
| 1048 | if (regs.alpha_test_enabled != 0 && regs.rt_control.count > 1) { | ||
| 1049 | LOG_CRITICAL( | ||
| 1050 | Render_OpenGL, | ||
| 1051 | "Alpha Testing is enabled with Multiple Render Targets, this behavior is undefined."); | ||
| 1052 | UNREACHABLE(); | ||
| 1053 | } | ||
| 1054 | } | ||
| 1055 | |||
| 1049 | } // namespace OpenGL | 1056 | } // namespace OpenGL |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index b1f7ccc7e..731a336d5 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h | |||
| @@ -144,11 +144,8 @@ private: | |||
| 144 | /// Syncs the cull mode to match the guest state | 144 | /// Syncs the cull mode to match the guest state |
| 145 | void SyncCullMode(); | 145 | void SyncCullMode(); |
| 146 | 146 | ||
| 147 | /// Syncs the depth scale to match the guest state | 147 | /// Syncs the depth range to match the guest state |
| 148 | void SyncDepthScale(); | 148 | void SyncDepthRange(); |
| 149 | |||
| 150 | /// Syncs the depth offset to match the guest state | ||
| 151 | void SyncDepthOffset(); | ||
| 152 | 149 | ||
| 153 | /// Syncs the depth test state to match the guest state | 150 | /// Syncs the depth test state to match the guest state |
| 154 | void SyncDepthTestState(); | 151 | void SyncDepthTestState(); |
| @@ -162,9 +159,6 @@ private: | |||
| 162 | /// Syncs the LogicOp state to match the guest state | 159 | /// Syncs the LogicOp state to match the guest state |
| 163 | void SyncLogicOpState(); | 160 | void SyncLogicOpState(); |
| 164 | 161 | ||
| 165 | /// Syncs the alpha test state to match the guest state | ||
| 166 | void SyncAlphaTest(); | ||
| 167 | |||
| 168 | /// Syncs the scissor test state to match the guest state | 162 | /// Syncs the scissor test state to match the guest state |
| 169 | void SyncScissorTest(); | 163 | void SyncScissorTest(); |
| 170 | 164 | ||
| @@ -174,6 +168,9 @@ private: | |||
| 174 | /// Syncs the point state to match the guest state | 168 | /// Syncs the point state to match the guest state |
| 175 | void SyncPointState(); | 169 | void SyncPointState(); |
| 176 | 170 | ||
| 171 | /// Check asserts for alpha testing. | ||
| 172 | void CheckAlphaTests(); | ||
| 173 | |||
| 177 | bool has_ARB_direct_state_access = false; | 174 | bool has_ARB_direct_state_access = false; |
| 178 | bool has_ARB_multi_bind = false; | 175 | bool has_ARB_multi_bind = false; |
| 179 | bool has_ARB_separate_shader_objects = false; | 176 | bool has_ARB_separate_shader_objects = false; |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index 9c8925383..591ec7998 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp | |||
| @@ -78,6 +78,29 @@ void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) { | |||
| 78 | } | 78 | } |
| 79 | } | 79 | } |
| 80 | 80 | ||
| 81 | std::size_t SurfaceParams::InnerMemorySize(bool layer_only) const { | ||
| 82 | const u32 compression_factor{GetCompressionFactor(pixel_format)}; | ||
| 83 | const u32 bytes_per_pixel{GetBytesPerPixel(pixel_format)}; | ||
| 84 | u32 m_depth = (layer_only ? 1U : depth); | ||
| 85 | u32 m_width = std::max(1U, width / compression_factor); | ||
| 86 | u32 m_height = std::max(1U, height / compression_factor); | ||
| 87 | std::size_t size = Tegra::Texture::CalculateSize(is_tiled, bytes_per_pixel, m_width, m_height, | ||
| 88 | m_depth, block_height, block_depth); | ||
| 89 | u32 m_block_height = block_height; | ||
| 90 | u32 m_block_depth = block_depth; | ||
| 91 | std::size_t block_size_bytes = 512 * block_height * block_depth; // 512 is GOB size | ||
| 92 | for (u32 i = 1; i < max_mip_level; i++) { | ||
| 93 | m_width = std::max(1U, m_width / 2); | ||
| 94 | m_height = std::max(1U, m_height / 2); | ||
| 95 | m_depth = std::max(1U, m_depth / 2); | ||
| 96 | m_block_height = std::max(1U, m_block_height / 2); | ||
| 97 | m_block_depth = std::max(1U, m_block_depth / 2); | ||
| 98 | size += Tegra::Texture::CalculateSize(is_tiled, bytes_per_pixel, m_width, m_height, m_depth, | ||
| 99 | m_block_height, m_block_depth); | ||
| 100 | } | ||
| 101 | return is_tiled ? Common::AlignUp(size, block_size_bytes) : size; | ||
| 102 | } | ||
| 103 | |||
| 81 | /*static*/ SurfaceParams SurfaceParams::CreateForTexture( | 104 | /*static*/ SurfaceParams SurfaceParams::CreateForTexture( |
| 82 | const Tegra::Texture::FullTextureInfo& config, const GLShader::SamplerEntry& entry) { | 105 | const Tegra::Texture::FullTextureInfo& config, const GLShader::SamplerEntry& entry) { |
| 83 | SurfaceParams params{}; | 106 | SurfaceParams params{}; |
| @@ -124,6 +147,7 @@ void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) { | |||
| 124 | break; | 147 | break; |
| 125 | } | 148 | } |
| 126 | 149 | ||
| 150 | params.is_layered = SurfaceTargetIsLayered(params.target); | ||
| 127 | params.max_mip_level = config.tic.max_mip_level + 1; | 151 | params.max_mip_level = config.tic.max_mip_level + 1; |
| 128 | params.rt = {}; | 152 | params.rt = {}; |
| 129 | 153 | ||
| @@ -150,6 +174,7 @@ void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) { | |||
| 150 | params.target = SurfaceTarget::Texture2D; | 174 | params.target = SurfaceTarget::Texture2D; |
| 151 | params.depth = 1; | 175 | params.depth = 1; |
| 152 | params.max_mip_level = 0; | 176 | params.max_mip_level = 0; |
| 177 | params.is_layered = false; | ||
| 153 | 178 | ||
| 154 | // Render target specific parameters, not used for caching | 179 | // Render target specific parameters, not used for caching |
| 155 | params.rt.index = static_cast<u32>(index); | 180 | params.rt.index = static_cast<u32>(index); |
| @@ -182,6 +207,7 @@ void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) { | |||
| 182 | params.target = SurfaceTarget::Texture2D; | 207 | params.target = SurfaceTarget::Texture2D; |
| 183 | params.depth = 1; | 208 | params.depth = 1; |
| 184 | params.max_mip_level = 0; | 209 | params.max_mip_level = 0; |
| 210 | params.is_layered = false; | ||
| 185 | params.rt = {}; | 211 | params.rt = {}; |
| 186 | 212 | ||
| 187 | params.InitCacheParameters(zeta_address); | 213 | params.InitCacheParameters(zeta_address); |
| @@ -361,10 +387,11 @@ void MortonCopy(u32 stride, u32 block_height, u32 height, u32 block_depth, u32 d | |||
| 361 | } | 387 | } |
| 362 | } | 388 | } |
| 363 | 389 | ||
| 364 | static constexpr std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t, VAddr), | 390 | using GLConversionArray = std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t, VAddr), |
| 365 | SurfaceParams::MaxPixelFormat> | 391 | SurfaceParams::MaxPixelFormat>; |
| 366 | morton_to_gl_fns = { | 392 | |
| 367 | // clang-format off | 393 | static constexpr GLConversionArray morton_to_gl_fns = { |
| 394 | // clang-format off | ||
| 368 | MortonCopy<true, PixelFormat::ABGR8U>, | 395 | MortonCopy<true, PixelFormat::ABGR8U>, |
| 369 | MortonCopy<true, PixelFormat::ABGR8S>, | 396 | MortonCopy<true, PixelFormat::ABGR8S>, |
| 370 | MortonCopy<true, PixelFormat::ABGR8UI>, | 397 | MortonCopy<true, PixelFormat::ABGR8UI>, |
| @@ -418,13 +445,11 @@ static constexpr std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t, | |||
| 418 | MortonCopy<true, PixelFormat::Z24S8>, | 445 | MortonCopy<true, PixelFormat::Z24S8>, |
| 419 | MortonCopy<true, PixelFormat::S8Z24>, | 446 | MortonCopy<true, PixelFormat::S8Z24>, |
| 420 | MortonCopy<true, PixelFormat::Z32FS8>, | 447 | MortonCopy<true, PixelFormat::Z32FS8>, |
| 421 | // clang-format on | 448 | // clang-format on |
| 422 | }; | 449 | }; |
| 423 | 450 | ||
| 424 | static constexpr std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t, VAddr), | 451 | static constexpr GLConversionArray gl_to_morton_fns = { |
| 425 | SurfaceParams::MaxPixelFormat> | 452 | // clang-format off |
| 426 | gl_to_morton_fns = { | ||
| 427 | // clang-format off | ||
| 428 | MortonCopy<false, PixelFormat::ABGR8U>, | 453 | MortonCopy<false, PixelFormat::ABGR8U>, |
| 429 | MortonCopy<false, PixelFormat::ABGR8S>, | 454 | MortonCopy<false, PixelFormat::ABGR8S>, |
| 430 | MortonCopy<false, PixelFormat::ABGR8UI>, | 455 | MortonCopy<false, PixelFormat::ABGR8UI>, |
| @@ -479,9 +504,35 @@ static constexpr std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t, | |||
| 479 | MortonCopy<false, PixelFormat::Z24S8>, | 504 | MortonCopy<false, PixelFormat::Z24S8>, |
| 480 | MortonCopy<false, PixelFormat::S8Z24>, | 505 | MortonCopy<false, PixelFormat::S8Z24>, |
| 481 | MortonCopy<false, PixelFormat::Z32FS8>, | 506 | MortonCopy<false, PixelFormat::Z32FS8>, |
| 482 | // clang-format on | 507 | // clang-format on |
| 483 | }; | 508 | }; |
| 484 | 509 | ||
| 510 | void SwizzleFunc(const GLConversionArray& functions, const SurfaceParams& params, | ||
| 511 | std::vector<u8>& gl_buffer) { | ||
| 512 | u32 depth = params.depth; | ||
| 513 | if (params.target == SurfaceParams::SurfaceTarget::Texture2D) { | ||
| 514 | // TODO(Blinkhawk): Eliminate this condition once all texture types are implemented. | ||
| 515 | depth = 1U; | ||
| 516 | } | ||
| 517 | if (params.is_layered) { | ||
| 518 | u64 offset = 0; | ||
| 519 | u64 offset_gl = 0; | ||
| 520 | u64 layer_size = params.LayerMemorySize(); | ||
| 521 | u64 gl_size = params.LayerSizeGL(); | ||
| 522 | for (u32 i = 0; i < depth; i++) { | ||
| 523 | functions[static_cast<std::size_t>(params.pixel_format)]( | ||
| 524 | params.width, params.block_height, params.height, params.block_depth, 1, | ||
| 525 | gl_buffer.data() + offset_gl, gl_size, params.addr + offset); | ||
| 526 | offset += layer_size; | ||
| 527 | offset_gl += gl_size; | ||
| 528 | } | ||
| 529 | } else { | ||
| 530 | functions[static_cast<std::size_t>(params.pixel_format)]( | ||
| 531 | params.width, params.block_height, params.height, params.block_depth, depth, | ||
| 532 | gl_buffer.data(), gl_buffer.size(), params.addr); | ||
| 533 | } | ||
| 534 | } | ||
| 535 | |||
| 485 | static bool BlitSurface(const Surface& src_surface, const Surface& dst_surface, | 536 | static bool BlitSurface(const Surface& src_surface, const Surface& dst_surface, |
| 486 | GLuint read_fb_handle, GLuint draw_fb_handle, GLenum src_attachment = 0, | 537 | GLuint read_fb_handle, GLuint draw_fb_handle, GLenum src_attachment = 0, |
| 487 | GLenum dst_attachment = 0, std::size_t cubemap_face = 0) { | 538 | GLenum dst_attachment = 0, std::size_t cubemap_face = 0) { |
| @@ -881,21 +932,10 @@ void CachedSurface::LoadGLBuffer() { | |||
| 881 | 932 | ||
| 882 | gl_buffer.resize(params.size_in_bytes_gl); | 933 | gl_buffer.resize(params.size_in_bytes_gl); |
| 883 | if (params.is_tiled) { | 934 | if (params.is_tiled) { |
| 884 | u32 depth = params.depth; | ||
| 885 | u32 block_depth = params.block_depth; | ||
| 886 | |||
| 887 | ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}", | 935 | ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}", |
| 888 | params.block_width, static_cast<u32>(params.target)); | 936 | params.block_width, static_cast<u32>(params.target)); |
| 889 | 937 | ||
| 890 | if (params.target == SurfaceParams::SurfaceTarget::Texture2D) { | 938 | SwizzleFunc(morton_to_gl_fns, params, gl_buffer); |
| 891 | // TODO(Blinkhawk): Eliminate this condition once all texture types are implemented. | ||
| 892 | depth = 1U; | ||
| 893 | block_depth = 1U; | ||
| 894 | } | ||
| 895 | |||
| 896 | morton_to_gl_fns[static_cast<std::size_t>(params.pixel_format)]( | ||
| 897 | params.width, params.block_height, params.height, block_depth, depth, gl_buffer.data(), | ||
| 898 | gl_buffer.size(), params.addr); | ||
| 899 | } else { | 939 | } else { |
| 900 | const auto texture_src_data{Memory::GetPointer(params.addr)}; | 940 | const auto texture_src_data{Memory::GetPointer(params.addr)}; |
| 901 | const auto texture_src_data_end{texture_src_data + params.size_in_bytes_gl}; | 941 | const auto texture_src_data_end{texture_src_data + params.size_in_bytes_gl}; |
| @@ -929,19 +969,10 @@ void CachedSurface::FlushGLBuffer() { | |||
| 929 | const u8* const texture_src_data = Memory::GetPointer(params.addr); | 969 | const u8* const texture_src_data = Memory::GetPointer(params.addr); |
| 930 | ASSERT(texture_src_data); | 970 | ASSERT(texture_src_data); |
| 931 | if (params.is_tiled) { | 971 | if (params.is_tiled) { |
| 932 | u32 depth = params.depth; | ||
| 933 | u32 block_depth = params.block_depth; | ||
| 934 | |||
| 935 | ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}", | 972 | ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}", |
| 936 | params.block_width, static_cast<u32>(params.target)); | 973 | params.block_width, static_cast<u32>(params.target)); |
| 937 | 974 | ||
| 938 | if (params.target == SurfaceParams::SurfaceTarget::Texture2D) { | 975 | SwizzleFunc(gl_to_morton_fns, params, gl_buffer); |
| 939 | // TODO(Blinkhawk): Eliminate this condition once all texture types are implemented. | ||
| 940 | depth = 1U; | ||
| 941 | } | ||
| 942 | gl_to_morton_fns[static_cast<size_t>(params.pixel_format)]( | ||
| 943 | params.width, params.block_height, params.height, block_depth, depth, gl_buffer.data(), | ||
| 944 | gl_buffer.size(), GetAddr()); | ||
| 945 | } else { | 976 | } else { |
| 946 | std::memcpy(Memory::GetPointer(GetAddr()), gl_buffer.data(), GetSizeInBytes()); | 977 | std::memcpy(Memory::GetPointer(GetAddr()), gl_buffer.data(), GetSizeInBytes()); |
| 947 | } | 978 | } |
| @@ -1179,7 +1210,7 @@ void RasterizerCacheOpenGL::AccurateCopySurface(const Surface& src_surface, | |||
| 1179 | const Surface& dst_surface) { | 1210 | const Surface& dst_surface) { |
| 1180 | const auto& src_params{src_surface->GetSurfaceParams()}; | 1211 | const auto& src_params{src_surface->GetSurfaceParams()}; |
| 1181 | const auto& dst_params{dst_surface->GetSurfaceParams()}; | 1212 | const auto& dst_params{dst_surface->GetSurfaceParams()}; |
| 1182 | FlushRegion(src_params.addr, dst_params.size_in_bytes); | 1213 | FlushRegion(src_params.addr, dst_params.MemorySize()); |
| 1183 | LoadSurface(dst_surface); | 1214 | LoadSurface(dst_surface); |
| 1184 | } | 1215 | } |
| 1185 | 1216 | ||
| @@ -1221,44 +1252,10 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface, | |||
| 1221 | CopySurface(old_surface, new_surface, copy_pbo.handle); | 1252 | CopySurface(old_surface, new_surface, copy_pbo.handle); |
| 1222 | } | 1253 | } |
| 1223 | break; | 1254 | break; |
| 1255 | case SurfaceParams::SurfaceTarget::TextureCubemap: | ||
| 1224 | case SurfaceParams::SurfaceTarget::Texture3D: | 1256 | case SurfaceParams::SurfaceTarget::Texture3D: |
| 1225 | AccurateCopySurface(old_surface, new_surface); | 1257 | AccurateCopySurface(old_surface, new_surface); |
| 1226 | break; | 1258 | break; |
| 1227 | case SurfaceParams::SurfaceTarget::TextureCubemap: { | ||
| 1228 | if (old_params.rt.array_mode != 1) { | ||
| 1229 | // TODO(bunnei): This is used by Breath of the Wild, I'm not sure how to implement this | ||
| 1230 | // yet (array rendering used as a cubemap texture). | ||
| 1231 | LOG_CRITICAL(HW_GPU, "Unhandled rendertarget array_mode {}", old_params.rt.array_mode); | ||
| 1232 | UNREACHABLE(); | ||
| 1233 | return new_surface; | ||
| 1234 | } | ||
| 1235 | |||
| 1236 | // This seems to be used for render-to-cubemap texture | ||
| 1237 | ASSERT_MSG(old_params.target == SurfaceParams::SurfaceTarget::Texture2D, "Unexpected"); | ||
| 1238 | ASSERT_MSG(old_params.pixel_format == new_params.pixel_format, "Unexpected"); | ||
| 1239 | ASSERT_MSG(old_params.rt.base_layer == 0, "Unimplemented"); | ||
| 1240 | |||
| 1241 | // TODO(bunnei): Verify the below - this stride seems to be in 32-bit words, not pixels. | ||
| 1242 | // Tested with Splatoon 2, Super Mario Odyssey, and Breath of the Wild. | ||
| 1243 | const std::size_t byte_stride{old_params.rt.layer_stride * sizeof(u32)}; | ||
| 1244 | |||
| 1245 | for (std::size_t index = 0; index < new_params.depth; ++index) { | ||
| 1246 | Surface face_surface{TryGetReservedSurface(old_params)}; | ||
| 1247 | ASSERT_MSG(face_surface, "Unexpected"); | ||
| 1248 | |||
| 1249 | if (is_blit) { | ||
| 1250 | BlitSurface(face_surface, new_surface, read_framebuffer.handle, | ||
| 1251 | draw_framebuffer.handle, face_surface->GetSurfaceParams().rt.index, | ||
| 1252 | new_params.rt.index, index); | ||
| 1253 | } else { | ||
| 1254 | CopySurface(face_surface, new_surface, copy_pbo.handle, | ||
| 1255 | face_surface->GetSurfaceParams().rt.index, new_params.rt.index, index); | ||
| 1256 | } | ||
| 1257 | |||
| 1258 | old_params.addr += byte_stride; | ||
| 1259 | } | ||
| 1260 | break; | ||
| 1261 | } | ||
| 1262 | default: | 1259 | default: |
| 1263 | LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}", | 1260 | LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}", |
| 1264 | static_cast<u32>(new_params.target)); | 1261 | static_cast<u32>(new_params.target)); |
| @@ -1266,7 +1263,7 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface, | |||
| 1266 | } | 1263 | } |
| 1267 | 1264 | ||
| 1268 | return new_surface; | 1265 | return new_surface; |
| 1269 | } | 1266 | } // namespace OpenGL |
| 1270 | 1267 | ||
| 1271 | Surface RasterizerCacheOpenGL::TryFindFramebufferSurface(VAddr addr) const { | 1268 | Surface RasterizerCacheOpenGL::TryFindFramebufferSurface(VAddr addr) const { |
| 1272 | return TryGet(addr); | 1269 | return TryGet(addr); |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h index 0dd0d90a3..50a7ab47d 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h | |||
| @@ -168,6 +168,23 @@ struct SurfaceParams { | |||
| 168 | } | 168 | } |
| 169 | } | 169 | } |
| 170 | 170 | ||
| 171 | static bool SurfaceTargetIsLayered(SurfaceTarget target) { | ||
| 172 | switch (target) { | ||
| 173 | case SurfaceTarget::Texture1D: | ||
| 174 | case SurfaceTarget::Texture2D: | ||
| 175 | case SurfaceTarget::Texture3D: | ||
| 176 | return false; | ||
| 177 | case SurfaceTarget::Texture1DArray: | ||
| 178 | case SurfaceTarget::Texture2DArray: | ||
| 179 | case SurfaceTarget::TextureCubemap: | ||
| 180 | return true; | ||
| 181 | default: | ||
| 182 | LOG_CRITICAL(HW_GPU, "Unimplemented surface_target={}", static_cast<u32>(target)); | ||
| 183 | UNREACHABLE(); | ||
| 184 | return false; | ||
| 185 | } | ||
| 186 | } | ||
| 187 | |||
| 171 | /** | 188 | /** |
| 172 | * Gets the compression factor for the specified PixelFormat. This applies to just the | 189 | * Gets the compression factor for the specified PixelFormat. This applies to just the |
| 173 | * "compressed width" and "compressed height", not the overall compression factor of a | 190 | * "compressed width" and "compressed height", not the overall compression factor of a |
| @@ -742,6 +759,25 @@ struct SurfaceParams { | |||
| 742 | return size_in_bytes_gl / 6; | 759 | return size_in_bytes_gl / 6; |
| 743 | } | 760 | } |
| 744 | 761 | ||
| 762 | /// Returns the exact size of memory occupied by the texture in VRAM, including mipmaps. | ||
| 763 | std::size_t MemorySize() const { | ||
| 764 | std::size_t size = InnerMemorySize(is_layered); | ||
| 765 | if (is_layered) | ||
| 766 | return size * depth; | ||
| 767 | return size; | ||
| 768 | } | ||
| 769 | |||
| 770 | /// Returns the exact size of the memory occupied by a layer in a texture in VRAM, including | ||
| 771 | /// mipmaps. | ||
| 772 | std::size_t LayerMemorySize() const { | ||
| 773 | return InnerMemorySize(true); | ||
| 774 | } | ||
| 775 | |||
| 776 | /// Returns the size of a layer of this surface in OpenGL. | ||
| 777 | std::size_t LayerSizeGL() const { | ||
| 778 | return SizeInBytesRaw(true) / depth; | ||
| 779 | } | ||
| 780 | |||
| 745 | /// Creates SurfaceParams from a texture configuration | 781 | /// Creates SurfaceParams from a texture configuration |
| 746 | static SurfaceParams CreateForTexture(const Tegra::Texture::FullTextureInfo& config, | 782 | static SurfaceParams CreateForTexture(const Tegra::Texture::FullTextureInfo& config, |
| 747 | const GLShader::SamplerEntry& entry); | 783 | const GLShader::SamplerEntry& entry); |
| @@ -782,6 +818,7 @@ struct SurfaceParams { | |||
| 782 | u32 unaligned_height; | 818 | u32 unaligned_height; |
| 783 | SurfaceTarget target; | 819 | SurfaceTarget target; |
| 784 | u32 max_mip_level; | 820 | u32 max_mip_level; |
| 821 | bool is_layered; | ||
| 785 | 822 | ||
| 786 | // Parameters used for caching | 823 | // Parameters used for caching |
| 787 | VAddr addr; | 824 | VAddr addr; |
| @@ -797,6 +834,9 @@ struct SurfaceParams { | |||
| 797 | u32 layer_stride; | 834 | u32 layer_stride; |
| 798 | u32 base_layer; | 835 | u32 base_layer; |
| 799 | } rt; | 836 | } rt; |
| 837 | |||
| 838 | private: | ||
| 839 | std::size_t InnerMemorySize(bool layer_only = false) const; | ||
| 800 | }; | 840 | }; |
| 801 | 841 | ||
| 802 | }; // namespace OpenGL | 842 | }; // namespace OpenGL |
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index f4340a017..81ffb24e4 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | #include <set> | 6 | #include <set> |
| 7 | #include <string> | 7 | #include <string> |
| 8 | #include <string_view> | 8 | #include <string_view> |
| 9 | #include <unordered_set> | ||
| 9 | 10 | ||
| 10 | #include <boost/optional.hpp> | 11 | #include <boost/optional.hpp> |
| 11 | #include <fmt/format.h> | 12 | #include <fmt/format.h> |
| @@ -30,8 +31,6 @@ using Tegra::Shader::SubOp; | |||
| 30 | constexpr u32 PROGRAM_END = MAX_PROGRAM_CODE_LENGTH; | 31 | constexpr u32 PROGRAM_END = MAX_PROGRAM_CODE_LENGTH; |
| 31 | constexpr u32 PROGRAM_HEADER_SIZE = sizeof(Tegra::Shader::Header); | 32 | constexpr u32 PROGRAM_HEADER_SIZE = sizeof(Tegra::Shader::Header); |
| 32 | 33 | ||
| 33 | enum : u32 { POSITION_VARYING_LOCATION = 0, GENERIC_VARYING_START_LOCATION = 1 }; | ||
| 34 | |||
| 35 | constexpr u32 MAX_GEOMETRY_BUFFERS = 6; | 34 | constexpr u32 MAX_GEOMETRY_BUFFERS = 6; |
| 36 | constexpr u32 MAX_ATTRIBUTES = 0x100; // Size in vec4s, this value is untested | 35 | constexpr u32 MAX_ATTRIBUTES = 0x100; // Size in vec4s, this value is untested |
| 37 | 36 | ||
| @@ -165,10 +164,11 @@ private: | |||
| 165 | const ExitMethod jmp = Scan(target, end, labels); | 164 | const ExitMethod jmp = Scan(target, end, labels); |
| 166 | return exit_method = ParallelExit(no_jmp, jmp); | 165 | return exit_method = ParallelExit(no_jmp, jmp); |
| 167 | } | 166 | } |
| 168 | case OpCode::Id::SSY: { | 167 | case OpCode::Id::SSY: |
| 169 | // The SSY instruction uses a similar encoding as the BRA instruction. | 168 | case OpCode::Id::PBK: { |
| 169 | // The SSY and PBK use a similar encoding as the BRA instruction. | ||
| 170 | ASSERT_MSG(instr.bra.constant_buffer == 0, | 170 | ASSERT_MSG(instr.bra.constant_buffer == 0, |
| 171 | "Constant buffer SSY is not supported"); | 171 | "Constant buffer branching is not supported"); |
| 172 | const u32 target = offset + instr.bra.GetBranchTarget(); | 172 | const u32 target = offset + instr.bra.GetBranchTarget(); |
| 173 | labels.insert(target); | 173 | labels.insert(target); |
| 174 | // Continue scanning for an exit method. | 174 | // Continue scanning for an exit method. |
| @@ -277,7 +277,8 @@ public: | |||
| 277 | GLSLRegisterManager(ShaderWriter& shader, ShaderWriter& declarations, | 277 | GLSLRegisterManager(ShaderWriter& shader, ShaderWriter& declarations, |
| 278 | const Maxwell3D::Regs::ShaderStage& stage, const std::string& suffix, | 278 | const Maxwell3D::Regs::ShaderStage& stage, const std::string& suffix, |
| 279 | const Tegra::Shader::Header& header) | 279 | const Tegra::Shader::Header& header) |
| 280 | : shader{shader}, declarations{declarations}, stage{stage}, suffix{suffix}, header{header} { | 280 | : shader{shader}, declarations{declarations}, stage{stage}, suffix{suffix}, header{header}, |
| 281 | fixed_pipeline_output_attributes_used{} { | ||
| 281 | BuildRegisterList(); | 282 | BuildRegisterList(); |
| 282 | BuildInputList(); | 283 | BuildInputList(); |
| 283 | } | 284 | } |
| @@ -376,11 +377,55 @@ public: | |||
| 376 | } | 377 | } |
| 377 | 378 | ||
| 378 | /** | 379 | /** |
| 380 | * Writes code that does a register assignment to a half float value operation. | ||
| 381 | * @param reg The destination register to use. | ||
| 382 | * @param elem The element to use for the operation. | ||
| 383 | * @param value The code representing the value to assign. Type has to be half float. | ||
| 384 | * @param merge Half float kind of assignment. | ||
| 385 | * @param dest_num_components Number of components in the destination. | ||
| 386 | * @param value_num_components Number of components in the value. | ||
| 387 | * @param is_saturated Optional, when True, saturates the provided value. | ||
| 388 | * @param dest_elem Optional, the destination element to use for the operation. | ||
| 389 | */ | ||
| 390 | void SetRegisterToHalfFloat(const Register& reg, u64 elem, const std::string& value, | ||
| 391 | Tegra::Shader::HalfMerge merge, u64 dest_num_components, | ||
| 392 | u64 value_num_components, bool is_saturated = false, | ||
| 393 | u64 dest_elem = 0) { | ||
| 394 | ASSERT_MSG(!is_saturated, "Unimplemented"); | ||
| 395 | |||
| 396 | const std::string result = [&]() { | ||
| 397 | switch (merge) { | ||
| 398 | case Tegra::Shader::HalfMerge::H0_H1: | ||
| 399 | return "uintBitsToFloat(packHalf2x16(" + value + "))"; | ||
| 400 | case Tegra::Shader::HalfMerge::F32: | ||
| 401 | // Half float instructions take the first component when doing a float cast. | ||
| 402 | return "float(" + value + ".x)"; | ||
| 403 | case Tegra::Shader::HalfMerge::Mrg_H0: | ||
| 404 | // TODO(Rodrigo): I guess Mrg_H0 and Mrg_H1 take their respective component from the | ||
| 405 | // pack. I couldn't test this on hardware but it shouldn't really matter since most | ||
| 406 | // of the time when a Mrg_* flag is used both components will be mirrored. That | ||
| 407 | // being said, it deserves a test. | ||
| 408 | return "((" + GetRegisterAsInteger(reg, 0, false) + | ||
| 409 | " & 0xffff0000) | (packHalf2x16(" + value + ") & 0x0000ffff))"; | ||
| 410 | case Tegra::Shader::HalfMerge::Mrg_H1: | ||
| 411 | return "((" + GetRegisterAsInteger(reg, 0, false) + | ||
| 412 | " & 0x0000ffff) | (packHalf2x16(" + value + ") & 0xffff0000))"; | ||
| 413 | default: | ||
| 414 | UNREACHABLE(); | ||
| 415 | return std::string("0"); | ||
| 416 | } | ||
| 417 | }(); | ||
| 418 | |||
| 419 | SetRegister(reg, elem, result, dest_num_components, value_num_components, dest_elem); | ||
| 420 | } | ||
| 421 | |||
| 422 | /** | ||
| 379 | * Writes code that does a register assignment to input attribute operation. Input attributes | 423 | * Writes code that does a register assignment to input attribute operation. Input attributes |
| 380 | * are stored as floats, so this may require conversion. | 424 | * are stored as floats, so this may require conversion. |
| 381 | * @param reg The destination register to use. | 425 | * @param reg The destination register to use. |
| 382 | * @param elem The element to use for the operation. | 426 | * @param elem The element to use for the operation. |
| 383 | * @param attribute The input attribute to use as the source value. | 427 | * @param attribute The input attribute to use as the source value. |
| 428 | * @param input_mode The input mode. | ||
| 384 | * @param vertex The register that decides which vertex to read from (used in GS). | 429 | * @param vertex The register that decides which vertex to read from (used in GS). |
| 385 | */ | 430 | */ |
| 386 | void SetRegisterToInputAttibute(const Register& reg, u64 elem, Attribute::Index attribute, | 431 | void SetRegisterToInputAttibute(const Register& reg, u64 elem, Attribute::Index attribute, |
| @@ -437,7 +482,12 @@ public: | |||
| 437 | std::to_string(static_cast<u32>(attribute)) + ']' + | 482 | std::to_string(static_cast<u32>(attribute)) + ']' + |
| 438 | GetSwizzle(elem) + " = " + src + ';'); | 483 | GetSwizzle(elem) + " = " + src + ';'); |
| 439 | } else { | 484 | } else { |
| 440 | shader.AddLine(dest + GetSwizzle(elem) + " = " + src + ';'); | 485 | if (attribute == Attribute::Index::PointSize) { |
| 486 | fixed_pipeline_output_attributes_used.insert(attribute); | ||
| 487 | shader.AddLine(dest + " = " + src + ';'); | ||
| 488 | } else { | ||
| 489 | shader.AddLine(dest + GetSwizzle(elem) + " = " + src + ';'); | ||
| 490 | } | ||
| 441 | } | 491 | } |
| 442 | } | 492 | } |
| 443 | } | 493 | } |
| @@ -481,6 +531,7 @@ public: | |||
| 481 | 531 | ||
| 482 | /// Add declarations. | 532 | /// Add declarations. |
| 483 | void GenerateDeclarations(const std::string& suffix) { | 533 | void GenerateDeclarations(const std::string& suffix) { |
| 534 | GenerateVertex(); | ||
| 484 | GenerateRegisters(suffix); | 535 | GenerateRegisters(suffix); |
| 485 | GenerateInternalFlags(); | 536 | GenerateInternalFlags(); |
| 486 | GenerateInputAttrs(); | 537 | GenerateInputAttrs(); |
| @@ -548,13 +599,6 @@ private: | |||
| 548 | 599 | ||
| 549 | /// Generates declarations for input attributes. | 600 | /// Generates declarations for input attributes. |
| 550 | void GenerateInputAttrs() { | 601 | void GenerateInputAttrs() { |
| 551 | if (stage != Maxwell3D::Regs::ShaderStage::Vertex) { | ||
| 552 | const std::string attr = | ||
| 553 | stage == Maxwell3D::Regs::ShaderStage::Geometry ? "gs_position[]" : "position"; | ||
| 554 | declarations.AddLine("layout (location = " + std::to_string(POSITION_VARYING_LOCATION) + | ||
| 555 | ") in vec4 " + attr + ';'); | ||
| 556 | } | ||
| 557 | |||
| 558 | for (const auto element : declr_input_attribute) { | 602 | for (const auto element : declr_input_attribute) { |
| 559 | // TODO(bunnei): Use proper number of elements for these | 603 | // TODO(bunnei): Use proper number of elements for these |
| 560 | u32 idx = | 604 | u32 idx = |
| @@ -577,10 +621,6 @@ private: | |||
| 577 | 621 | ||
| 578 | /// Generates declarations for output attributes. | 622 | /// Generates declarations for output attributes. |
| 579 | void GenerateOutputAttrs() { | 623 | void GenerateOutputAttrs() { |
| 580 | if (stage != Maxwell3D::Regs::ShaderStage::Fragment) { | ||
| 581 | declarations.AddLine("layout (location = " + std::to_string(POSITION_VARYING_LOCATION) + | ||
| 582 | ") out vec4 position;"); | ||
| 583 | } | ||
| 584 | for (const auto& index : declr_output_attribute) { | 624 | for (const auto& index : declr_output_attribute) { |
| 585 | // TODO(bunnei): Use proper number of elements for these | 625 | // TODO(bunnei): Use proper number of elements for these |
| 586 | const u32 idx = static_cast<u32>(index) - | 626 | const u32 idx = static_cast<u32>(index) - |
| @@ -651,6 +691,20 @@ private: | |||
| 651 | declarations.AddNewLine(); | 691 | declarations.AddNewLine(); |
| 652 | } | 692 | } |
| 653 | 693 | ||
| 694 | void GenerateVertex() { | ||
| 695 | if (stage != Maxwell3D::Regs::ShaderStage::Vertex) | ||
| 696 | return; | ||
| 697 | declarations.AddLine("out gl_PerVertex {"); | ||
| 698 | ++declarations.scope; | ||
| 699 | declarations.AddLine("vec4 gl_Position;"); | ||
| 700 | for (auto& o : fixed_pipeline_output_attributes_used) { | ||
| 701 | if (o == Attribute::Index::PointSize) | ||
| 702 | declarations.AddLine("float gl_PointSize;"); | ||
| 703 | } | ||
| 704 | --declarations.scope; | ||
| 705 | declarations.AddLine("};"); | ||
| 706 | } | ||
| 707 | |||
| 654 | /// Generates code representing a temporary (GPR) register. | 708 | /// Generates code representing a temporary (GPR) register. |
| 655 | std::string GetRegister(const Register& reg, unsigned elem) { | 709 | std::string GetRegister(const Register& reg, unsigned elem) { |
| 656 | if (reg == Register::ZeroIndex) { | 710 | if (reg == Register::ZeroIndex) { |
| @@ -804,6 +858,8 @@ private: | |||
| 804 | /// Generates code representing the declaration name of an output attribute register. | 858 | /// Generates code representing the declaration name of an output attribute register. |
| 805 | std::string GetOutputAttribute(Attribute::Index attribute) { | 859 | std::string GetOutputAttribute(Attribute::Index attribute) { |
| 806 | switch (attribute) { | 860 | switch (attribute) { |
| 861 | case Attribute::Index::PointSize: | ||
| 862 | return "gl_PointSize"; | ||
| 807 | case Attribute::Index::Position: | 863 | case Attribute::Index::Position: |
| 808 | return "position"; | 864 | return "position"; |
| 809 | default: | 865 | default: |
| @@ -838,6 +894,7 @@ private: | |||
| 838 | const Maxwell3D::Regs::ShaderStage& stage; | 894 | const Maxwell3D::Regs::ShaderStage& stage; |
| 839 | const std::string& suffix; | 895 | const std::string& suffix; |
| 840 | const Tegra::Shader::Header& header; | 896 | const Tegra::Shader::Header& header; |
| 897 | std::unordered_set<Attribute::Index> fixed_pipeline_output_attributes_used; | ||
| 841 | }; | 898 | }; |
| 842 | 899 | ||
| 843 | class GLSLGenerator { | 900 | class GLSLGenerator { |
| @@ -877,6 +934,19 @@ private: | |||
| 877 | return fmt::format("uintBitsToFloat({})", instr.alu.GetImm20_32()); | 934 | return fmt::format("uintBitsToFloat({})", instr.alu.GetImm20_32()); |
| 878 | } | 935 | } |
| 879 | 936 | ||
| 937 | /// Generates code representing a vec2 pair unpacked from a half float immediate | ||
| 938 | static std::string UnpackHalfImmediate(const Instruction& instr, bool negate) { | ||
| 939 | const std::string immediate = GetHalfFloat(std::to_string(instr.half_imm.PackImmediates())); | ||
| 940 | if (!negate) { | ||
| 941 | return immediate; | ||
| 942 | } | ||
| 943 | const std::string negate_first = instr.half_imm.first_negate != 0 ? "-" : ""; | ||
| 944 | const std::string negate_second = instr.half_imm.second_negate != 0 ? "-" : ""; | ||
| 945 | const std::string negate_vec = "vec2(" + negate_first + "1, " + negate_second + "1)"; | ||
| 946 | |||
| 947 | return '(' + immediate + " * " + negate_vec + ')'; | ||
| 948 | } | ||
| 949 | |||
| 880 | /// Generates code representing a texture sampler. | 950 | /// Generates code representing a texture sampler. |
| 881 | std::string GetSampler(const Sampler& sampler, Tegra::Shader::TextureType type, bool is_array, | 951 | std::string GetSampler(const Sampler& sampler, Tegra::Shader::TextureType type, bool is_array, |
| 882 | bool is_shadow) { | 952 | bool is_shadow) { |
| @@ -908,7 +978,7 @@ private: | |||
| 908 | // Can't assign to the constant predicate. | 978 | // Can't assign to the constant predicate. |
| 909 | ASSERT(pred != static_cast<u64>(Pred::UnusedIndex)); | 979 | ASSERT(pred != static_cast<u64>(Pred::UnusedIndex)); |
| 910 | 980 | ||
| 911 | const std::string variable = 'p' + std::to_string(pred) + '_' + suffix; | 981 | std::string variable = 'p' + std::to_string(pred) + '_' + suffix; |
| 912 | shader.AddLine(variable + " = " + value + ';'); | 982 | shader.AddLine(variable + " = " + value + ';'); |
| 913 | declr_predicates.insert(std::move(variable)); | 983 | declr_predicates.insert(std::move(variable)); |
| 914 | } | 984 | } |
| @@ -1013,6 +1083,41 @@ private: | |||
| 1013 | } | 1083 | } |
| 1014 | 1084 | ||
| 1015 | /* | 1085 | /* |
| 1086 | * Transforms the input string GLSL operand into an unpacked half float pair. | ||
| 1087 | * @note This function returns a float type pair instead of a half float pair. This is because | ||
| 1088 | * real half floats are not standardized in GLSL but unpackHalf2x16 (which returns a vec2) is. | ||
| 1089 | * @param operand Input operand. It has to be an unsigned integer. | ||
| 1090 | * @param type How to unpack the unsigned integer to a half float pair. | ||
| 1091 | * @param abs Get the absolute value of unpacked half floats. | ||
| 1092 | * @param neg Get the negative value of unpacked half floats. | ||
| 1093 | * @returns String corresponding to a half float pair. | ||
| 1094 | */ | ||
| 1095 | static std::string GetHalfFloat(const std::string& operand, | ||
| 1096 | Tegra::Shader::HalfType type = Tegra::Shader::HalfType::H0_H1, | ||
| 1097 | bool abs = false, bool neg = false) { | ||
| 1098 | // "vec2" calls emitted in this function are intended to alias components. | ||
| 1099 | const std::string value = [&]() { | ||
| 1100 | switch (type) { | ||
| 1101 | case Tegra::Shader::HalfType::H0_H1: | ||
| 1102 | return "unpackHalf2x16(" + operand + ')'; | ||
| 1103 | case Tegra::Shader::HalfType::F32: | ||
| 1104 | return "vec2(uintBitsToFloat(" + operand + "))"; | ||
| 1105 | case Tegra::Shader::HalfType::H0_H0: | ||
| 1106 | case Tegra::Shader::HalfType::H1_H1: { | ||
| 1107 | const bool high = type == Tegra::Shader::HalfType::H1_H1; | ||
| 1108 | const char unpack_index = "xy"[high ? 1 : 0]; | ||
| 1109 | return "vec2(unpackHalf2x16(" + operand + ")." + unpack_index + ')'; | ||
| 1110 | } | ||
| 1111 | default: | ||
| 1112 | UNREACHABLE(); | ||
| 1113 | return std::string("vec2(0)"); | ||
| 1114 | } | ||
| 1115 | }(); | ||
| 1116 | |||
| 1117 | return GetOperandAbsNeg(value, abs, neg); | ||
| 1118 | } | ||
| 1119 | |||
| 1120 | /* | ||
| 1016 | * Returns whether the instruction at the specified offset is a 'sched' instruction. | 1121 | * Returns whether the instruction at the specified offset is a 'sched' instruction. |
| 1017 | * Sched instructions always appear before a sequence of 3 instructions. | 1122 | * Sched instructions always appear before a sequence of 3 instructions. |
| 1018 | */ | 1123 | */ |
| @@ -1154,27 +1259,27 @@ private: | |||
| 1154 | } | 1259 | } |
| 1155 | 1260 | ||
| 1156 | /* | 1261 | /* |
| 1157 | * Emits code to push the input target address to the SSY address stack, incrementing the stack | 1262 | * Emits code to push the input target address to the flow address stack, incrementing the stack |
| 1158 | * top. | 1263 | * top. |
| 1159 | */ | 1264 | */ |
| 1160 | void EmitPushToSSYStack(u32 target) { | 1265 | void EmitPushToFlowStack(u32 target) { |
| 1161 | shader.AddLine('{'); | 1266 | shader.AddLine('{'); |
| 1162 | ++shader.scope; | 1267 | ++shader.scope; |
| 1163 | shader.AddLine("ssy_stack[ssy_stack_top] = " + std::to_string(target) + "u;"); | 1268 | shader.AddLine("flow_stack[flow_stack_top] = " + std::to_string(target) + "u;"); |
| 1164 | shader.AddLine("ssy_stack_top++;"); | 1269 | shader.AddLine("flow_stack_top++;"); |
| 1165 | --shader.scope; | 1270 | --shader.scope; |
| 1166 | shader.AddLine('}'); | 1271 | shader.AddLine('}'); |
| 1167 | } | 1272 | } |
| 1168 | 1273 | ||
| 1169 | /* | 1274 | /* |
| 1170 | * Emits code to pop an address from the SSY address stack, setting the jump address to the | 1275 | * Emits code to pop an address from the flow address stack, setting the jump address to the |
| 1171 | * popped address and decrementing the stack top. | 1276 | * popped address and decrementing the stack top. |
| 1172 | */ | 1277 | */ |
| 1173 | void EmitPopFromSSYStack() { | 1278 | void EmitPopFromFlowStack() { |
| 1174 | shader.AddLine('{'); | 1279 | shader.AddLine('{'); |
| 1175 | ++shader.scope; | 1280 | ++shader.scope; |
| 1176 | shader.AddLine("ssy_stack_top--;"); | 1281 | shader.AddLine("flow_stack_top--;"); |
| 1177 | shader.AddLine("jmp_to = ssy_stack[ssy_stack_top];"); | 1282 | shader.AddLine("jmp_to = flow_stack[flow_stack_top];"); |
| 1178 | shader.AddLine("break;"); | 1283 | shader.AddLine("break;"); |
| 1179 | --shader.scope; | 1284 | --shader.scope; |
| 1180 | shader.AddLine('}'); | 1285 | shader.AddLine('}'); |
| @@ -1186,9 +1291,29 @@ private: | |||
| 1186 | 1291 | ||
| 1187 | ASSERT_MSG(header.ps.omap.sample_mask == 0, "Samplemask write is unimplemented"); | 1292 | ASSERT_MSG(header.ps.omap.sample_mask == 0, "Samplemask write is unimplemented"); |
| 1188 | 1293 | ||
| 1294 | shader.AddLine("if (alpha_test[0] != 0) {"); | ||
| 1295 | ++shader.scope; | ||
| 1296 | // We start on the register containing the alpha value in the first RT. | ||
| 1297 | u32 current_reg = 3; | ||
| 1298 | for (u32 render_target = 0; render_target < Maxwell3D::Regs::NumRenderTargets; | ||
| 1299 | ++render_target) { | ||
| 1300 | // TODO(Blinkhawk): verify the behavior of alpha testing on hardware when | ||
| 1301 | // multiple render targets are used. | ||
| 1302 | if (header.ps.IsColorComponentOutputEnabled(render_target, 0) || | ||
| 1303 | header.ps.IsColorComponentOutputEnabled(render_target, 1) || | ||
| 1304 | header.ps.IsColorComponentOutputEnabled(render_target, 2) || | ||
| 1305 | header.ps.IsColorComponentOutputEnabled(render_target, 3)) { | ||
| 1306 | shader.AddLine(fmt::format("if (!AlphaFunc({})) discard;", | ||
| 1307 | regs.GetRegisterAsFloat(current_reg))); | ||
| 1308 | current_reg += 4; | ||
| 1309 | } | ||
| 1310 | } | ||
| 1311 | --shader.scope; | ||
| 1312 | shader.AddLine('}'); | ||
| 1313 | |||
| 1189 | // Write the color outputs using the data in the shader registers, disabled | 1314 | // Write the color outputs using the data in the shader registers, disabled |
| 1190 | // rendertargets/components are skipped in the register assignment. | 1315 | // rendertargets/components are skipped in the register assignment. |
| 1191 | u32 current_reg = 0; | 1316 | current_reg = 0; |
| 1192 | for (u32 render_target = 0; render_target < Maxwell3D::Regs::NumRenderTargets; | 1317 | for (u32 render_target = 0; render_target < Maxwell3D::Regs::NumRenderTargets; |
| 1193 | ++render_target) { | 1318 | ++render_target) { |
| 1194 | // TODO(Subv): Figure out how dual-source blending is configured in the Switch. | 1319 | // TODO(Subv): Figure out how dual-source blending is configured in the Switch. |
| @@ -1212,6 +1337,63 @@ private: | |||
| 1212 | } | 1337 | } |
| 1213 | } | 1338 | } |
| 1214 | 1339 | ||
| 1340 | /// Unpacks a video instruction operand (e.g. VMAD). | ||
| 1341 | std::string GetVideoOperand(const std::string& op, bool is_chunk, bool is_signed, | ||
| 1342 | Tegra::Shader::VideoType type, u64 byte_height) { | ||
| 1343 | const std::string value = [&]() { | ||
| 1344 | if (!is_chunk) { | ||
| 1345 | const auto offset = static_cast<u32>(byte_height * 8); | ||
| 1346 | return "((" + op + " >> " + std::to_string(offset) + ") & 0xff)"; | ||
| 1347 | } | ||
| 1348 | const std::string zero = "0"; | ||
| 1349 | |||
| 1350 | switch (type) { | ||
| 1351 | case Tegra::Shader::VideoType::Size16_Low: | ||
| 1352 | return '(' + op + " & 0xffff)"; | ||
| 1353 | case Tegra::Shader::VideoType::Size16_High: | ||
| 1354 | return '(' + op + " >> 16)"; | ||
| 1355 | case Tegra::Shader::VideoType::Size32: | ||
| 1356 | // TODO(Rodrigo): From my hardware tests it becomes a bit "mad" when | ||
| 1357 | // this type is used (1 * 1 + 0 == 0x5b800000). Until a better | ||
| 1358 | // explanation is found: assert. | ||
| 1359 | UNIMPLEMENTED(); | ||
| 1360 | return zero; | ||
| 1361 | case Tegra::Shader::VideoType::Invalid: | ||
| 1362 | UNREACHABLE_MSG("Invalid instruction encoding"); | ||
| 1363 | return zero; | ||
| 1364 | default: | ||
| 1365 | UNREACHABLE(); | ||
| 1366 | return zero; | ||
| 1367 | } | ||
| 1368 | }(); | ||
| 1369 | |||
| 1370 | if (is_signed) { | ||
| 1371 | return "int(" + value + ')'; | ||
| 1372 | } | ||
| 1373 | return value; | ||
| 1374 | }; | ||
| 1375 | |||
| 1376 | /// Gets the A operand for a video instruction. | ||
| 1377 | std::string GetVideoOperandA(Instruction instr) { | ||
| 1378 | return GetVideoOperand(regs.GetRegisterAsInteger(instr.gpr8, 0, false), | ||
| 1379 | instr.video.is_byte_chunk_a != 0, instr.video.signed_a, | ||
| 1380 | instr.video.type_a, instr.video.byte_height_a); | ||
| 1381 | } | ||
| 1382 | |||
| 1383 | /// Gets the B operand for a video instruction. | ||
| 1384 | std::string GetVideoOperandB(Instruction instr) { | ||
| 1385 | if (instr.video.use_register_b) { | ||
| 1386 | return GetVideoOperand(regs.GetRegisterAsInteger(instr.gpr20, 0, false), | ||
| 1387 | instr.video.is_byte_chunk_b != 0, instr.video.signed_b, | ||
| 1388 | instr.video.type_b, instr.video.byte_height_b); | ||
| 1389 | } else { | ||
| 1390 | return '(' + | ||
| 1391 | std::to_string(instr.video.signed_b ? static_cast<s16>(instr.alu.GetImm20_16()) | ||
| 1392 | : instr.alu.GetImm20_16()) + | ||
| 1393 | ')'; | ||
| 1394 | } | ||
| 1395 | } | ||
| 1396 | |||
| 1215 | /** | 1397 | /** |
| 1216 | * Compiles a single instruction from Tegra to GLSL. | 1398 | * Compiles a single instruction from Tegra to GLSL. |
| 1217 | * @param offset the offset of the Tegra shader instruction. | 1399 | * @param offset the offset of the Tegra shader instruction. |
| @@ -1381,9 +1563,10 @@ private: | |||
| 1381 | break; | 1563 | break; |
| 1382 | } | 1564 | } |
| 1383 | case OpCode::Id::FMUL32_IMM: { | 1565 | case OpCode::Id::FMUL32_IMM: { |
| 1384 | regs.SetRegisterToFloat( | 1566 | regs.SetRegisterToFloat(instr.gpr0, 0, |
| 1385 | instr.gpr0, 0, | 1567 | regs.GetRegisterAsFloat(instr.gpr8) + " * " + |
| 1386 | regs.GetRegisterAsFloat(instr.gpr8) + " * " + GetImmediate32(instr), 1, 1); | 1568 | GetImmediate32(instr), |
| 1569 | 1, 1, instr.fmul32.saturate); | ||
| 1387 | break; | 1570 | break; |
| 1388 | } | 1571 | } |
| 1389 | case OpCode::Id::FADD32I: { | 1572 | case OpCode::Id::FADD32I: { |
| @@ -1748,6 +1931,86 @@ private: | |||
| 1748 | 1931 | ||
| 1749 | break; | 1932 | break; |
| 1750 | } | 1933 | } |
| 1934 | case OpCode::Type::ArithmeticHalf: { | ||
| 1935 | if (opcode->GetId() == OpCode::Id::HADD2_C || opcode->GetId() == OpCode::Id::HADD2_R) { | ||
| 1936 | ASSERT_MSG(instr.alu_half.ftz == 0, "Unimplemented"); | ||
| 1937 | } | ||
| 1938 | const bool negate_a = | ||
| 1939 | opcode->GetId() != OpCode::Id::HMUL2_R && instr.alu_half.negate_a != 0; | ||
| 1940 | const bool negate_b = | ||
| 1941 | opcode->GetId() != OpCode::Id::HMUL2_C && instr.alu_half.negate_b != 0; | ||
| 1942 | |||
| 1943 | const std::string op_a = | ||
| 1944 | GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr8, 0, false), instr.alu_half.type_a, | ||
| 1945 | instr.alu_half.abs_a != 0, negate_a); | ||
| 1946 | |||
| 1947 | std::string op_b; | ||
| 1948 | switch (opcode->GetId()) { | ||
| 1949 | case OpCode::Id::HADD2_C: | ||
| 1950 | case OpCode::Id::HMUL2_C: | ||
| 1951 | op_b = regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset, | ||
| 1952 | GLSLRegister::Type::UnsignedInteger); | ||
| 1953 | break; | ||
| 1954 | case OpCode::Id::HADD2_R: | ||
| 1955 | case OpCode::Id::HMUL2_R: | ||
| 1956 | op_b = regs.GetRegisterAsInteger(instr.gpr20, 0, false); | ||
| 1957 | break; | ||
| 1958 | default: | ||
| 1959 | UNREACHABLE(); | ||
| 1960 | op_b = "0"; | ||
| 1961 | break; | ||
| 1962 | } | ||
| 1963 | op_b = GetHalfFloat(op_b, instr.alu_half.type_b, instr.alu_half.abs_b != 0, negate_b); | ||
| 1964 | |||
| 1965 | const std::string result = [&]() { | ||
| 1966 | switch (opcode->GetId()) { | ||
| 1967 | case OpCode::Id::HADD2_C: | ||
| 1968 | case OpCode::Id::HADD2_R: | ||
| 1969 | return '(' + op_a + " + " + op_b + ')'; | ||
| 1970 | case OpCode::Id::HMUL2_C: | ||
| 1971 | case OpCode::Id::HMUL2_R: | ||
| 1972 | return '(' + op_a + " * " + op_b + ')'; | ||
| 1973 | default: | ||
| 1974 | LOG_CRITICAL(HW_GPU, "Unhandled half float instruction: {}", opcode->GetName()); | ||
| 1975 | UNREACHABLE(); | ||
| 1976 | return std::string("0"); | ||
| 1977 | } | ||
| 1978 | }(); | ||
| 1979 | |||
| 1980 | regs.SetRegisterToHalfFloat(instr.gpr0, 0, result, instr.alu_half.merge, 1, 1, | ||
| 1981 | instr.alu_half.saturate != 0); | ||
| 1982 | break; | ||
| 1983 | } | ||
| 1984 | case OpCode::Type::ArithmeticHalfImmediate: { | ||
| 1985 | if (opcode->GetId() == OpCode::Id::HADD2_IMM) { | ||
| 1986 | ASSERT_MSG(instr.alu_half_imm.ftz == 0, "Unimplemented"); | ||
| 1987 | } else { | ||
| 1988 | ASSERT_MSG(instr.alu_half_imm.precision == Tegra::Shader::HalfPrecision::None, | ||
| 1989 | "Unimplemented"); | ||
| 1990 | } | ||
| 1991 | |||
| 1992 | const std::string op_a = GetHalfFloat( | ||
| 1993 | regs.GetRegisterAsInteger(instr.gpr8, 0, false), instr.alu_half_imm.type_a, | ||
| 1994 | instr.alu_half_imm.abs_a != 0, instr.alu_half_imm.negate_a != 0); | ||
| 1995 | |||
| 1996 | const std::string op_b = UnpackHalfImmediate(instr, true); | ||
| 1997 | |||
| 1998 | const std::string result = [&]() { | ||
| 1999 | switch (opcode->GetId()) { | ||
| 2000 | case OpCode::Id::HADD2_IMM: | ||
| 2001 | return op_a + " + " + op_b; | ||
| 2002 | case OpCode::Id::HMUL2_IMM: | ||
| 2003 | return op_a + " * " + op_b; | ||
| 2004 | default: | ||
| 2005 | UNREACHABLE(); | ||
| 2006 | return std::string("0"); | ||
| 2007 | } | ||
| 2008 | }(); | ||
| 2009 | |||
| 2010 | regs.SetRegisterToHalfFloat(instr.gpr0, 0, result, instr.alu_half_imm.merge, 1, 1, | ||
| 2011 | instr.alu_half_imm.saturate != 0); | ||
| 2012 | break; | ||
| 2013 | } | ||
| 1751 | case OpCode::Type::Ffma: { | 2014 | case OpCode::Type::Ffma: { |
| 1752 | const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); | 2015 | const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); |
| 1753 | std::string op_b = instr.ffma.negate_b ? "-" : ""; | 2016 | std::string op_b = instr.ffma.negate_b ? "-" : ""; |
| @@ -1792,6 +2055,59 @@ private: | |||
| 1792 | instr.alu.saturate_d); | 2055 | instr.alu.saturate_d); |
| 1793 | break; | 2056 | break; |
| 1794 | } | 2057 | } |
| 2058 | case OpCode::Type::Hfma2: { | ||
| 2059 | if (opcode->GetId() == OpCode::Id::HFMA2_RR) { | ||
| 2060 | ASSERT_MSG(instr.hfma2.rr.precision == Tegra::Shader::HalfPrecision::None, | ||
| 2061 | "Unimplemented"); | ||
| 2062 | } else { | ||
| 2063 | ASSERT_MSG(instr.hfma2.precision == Tegra::Shader::HalfPrecision::None, | ||
| 2064 | "Unimplemented"); | ||
| 2065 | } | ||
| 2066 | const bool saturate = opcode->GetId() == OpCode::Id::HFMA2_RR | ||
| 2067 | ? instr.hfma2.rr.saturate != 0 | ||
| 2068 | : instr.hfma2.saturate != 0; | ||
| 2069 | |||
| 2070 | const std::string op_a = | ||
| 2071 | GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr8, 0, false), instr.hfma2.type_a); | ||
| 2072 | std::string op_b, op_c; | ||
| 2073 | |||
| 2074 | switch (opcode->GetId()) { | ||
| 2075 | case OpCode::Id::HFMA2_CR: | ||
| 2076 | op_b = GetHalfFloat(regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset, | ||
| 2077 | GLSLRegister::Type::UnsignedInteger), | ||
| 2078 | instr.hfma2.type_b, false, instr.hfma2.negate_b); | ||
| 2079 | op_c = GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr39, 0, false), | ||
| 2080 | instr.hfma2.type_reg39, false, instr.hfma2.negate_c); | ||
| 2081 | break; | ||
| 2082 | case OpCode::Id::HFMA2_RC: | ||
| 2083 | op_b = GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr39, 0, false), | ||
| 2084 | instr.hfma2.type_reg39, false, instr.hfma2.negate_b); | ||
| 2085 | op_c = GetHalfFloat(regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset, | ||
| 2086 | GLSLRegister::Type::UnsignedInteger), | ||
| 2087 | instr.hfma2.type_b, false, instr.hfma2.negate_c); | ||
| 2088 | break; | ||
| 2089 | case OpCode::Id::HFMA2_RR: | ||
| 2090 | op_b = GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr20, 0, false), | ||
| 2091 | instr.hfma2.type_b, false, instr.hfma2.negate_b); | ||
| 2092 | op_c = GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr39, 0, false), | ||
| 2093 | instr.hfma2.rr.type_c, false, instr.hfma2.rr.negate_c); | ||
| 2094 | break; | ||
| 2095 | case OpCode::Id::HFMA2_IMM_R: | ||
| 2096 | op_b = UnpackHalfImmediate(instr, true); | ||
| 2097 | op_c = GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr39, 0, false), | ||
| 2098 | instr.hfma2.type_reg39, false, instr.hfma2.negate_c); | ||
| 2099 | break; | ||
| 2100 | default: | ||
| 2101 | UNREACHABLE(); | ||
| 2102 | op_c = op_b = "vec2(0)"; | ||
| 2103 | break; | ||
| 2104 | } | ||
| 2105 | |||
| 2106 | const std::string result = '(' + op_a + " * " + op_b + " + " + op_c + ')'; | ||
| 2107 | |||
| 2108 | regs.SetRegisterToHalfFloat(instr.gpr0, 0, result, instr.hfma2.merge, 1, 1, saturate); | ||
| 2109 | break; | ||
| 2110 | } | ||
| 1795 | case OpCode::Type::Conversion: { | 2111 | case OpCode::Type::Conversion: { |
| 1796 | switch (opcode->GetId()) { | 2112 | switch (opcode->GetId()) { |
| 1797 | case OpCode::Id::I2I_R: { | 2113 | case OpCode::Id::I2I_R: { |
| @@ -2525,20 +2841,13 @@ private: | |||
| 2525 | break; | 2841 | break; |
| 2526 | } | 2842 | } |
| 2527 | case OpCode::Type::FloatSetPredicate: { | 2843 | case OpCode::Type::FloatSetPredicate: { |
| 2528 | std::string op_a = instr.fsetp.neg_a ? "-" : ""; | 2844 | const std::string op_a = |
| 2529 | op_a += regs.GetRegisterAsFloat(instr.gpr8); | 2845 | GetOperandAbsNeg(regs.GetRegisterAsFloat(instr.gpr8), instr.fsetp.abs_a != 0, |
| 2530 | 2846 | instr.fsetp.neg_a != 0); | |
| 2531 | if (instr.fsetp.abs_a) { | ||
| 2532 | op_a = "abs(" + op_a + ')'; | ||
| 2533 | } | ||
| 2534 | 2847 | ||
| 2535 | std::string op_b{}; | 2848 | std::string op_b; |
| 2536 | 2849 | ||
| 2537 | if (instr.is_b_imm) { | 2850 | if (instr.is_b_imm) { |
| 2538 | if (instr.fsetp.neg_b) { | ||
| 2539 | // Only the immediate version of fsetp has a neg_b bit. | ||
| 2540 | op_b += '-'; | ||
| 2541 | } | ||
| 2542 | op_b += '(' + GetImmediate19(instr) + ')'; | 2851 | op_b += '(' + GetImmediate19(instr) + ')'; |
| 2543 | } else { | 2852 | } else { |
| 2544 | if (instr.is_b_gpr) { | 2853 | if (instr.is_b_gpr) { |
| @@ -2611,6 +2920,51 @@ private: | |||
| 2611 | } | 2920 | } |
| 2612 | break; | 2921 | break; |
| 2613 | } | 2922 | } |
| 2923 | case OpCode::Type::HalfSetPredicate: { | ||
| 2924 | ASSERT_MSG(instr.hsetp2.ftz == 0, "Unimplemented"); | ||
| 2925 | |||
| 2926 | const std::string op_a = | ||
| 2927 | GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr8, 0, false), instr.hsetp2.type_a, | ||
| 2928 | instr.hsetp2.abs_a, instr.hsetp2.negate_a); | ||
| 2929 | |||
| 2930 | const std::string op_b = [&]() { | ||
| 2931 | switch (opcode->GetId()) { | ||
| 2932 | case OpCode::Id::HSETP2_R: | ||
| 2933 | return GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr20, 0, false), | ||
| 2934 | instr.hsetp2.type_b, instr.hsetp2.abs_a, | ||
| 2935 | instr.hsetp2.negate_b); | ||
| 2936 | default: | ||
| 2937 | UNREACHABLE(); | ||
| 2938 | return std::string("vec2(0)"); | ||
| 2939 | } | ||
| 2940 | }(); | ||
| 2941 | |||
| 2942 | // We can't use the constant predicate as destination. | ||
| 2943 | ASSERT(instr.hsetp2.pred3 != static_cast<u64>(Pred::UnusedIndex)); | ||
| 2944 | |||
| 2945 | const std::string second_pred = | ||
| 2946 | GetPredicateCondition(instr.hsetp2.pred39, instr.hsetp2.neg_pred != 0); | ||
| 2947 | |||
| 2948 | const std::string combiner = GetPredicateCombiner(instr.hsetp2.op); | ||
| 2949 | |||
| 2950 | const std::string component_combiner = instr.hsetp2.h_and ? "&&" : "||"; | ||
| 2951 | const std::string predicate = | ||
| 2952 | '(' + GetPredicateComparison(instr.hsetp2.cond, op_a + ".x", op_b + ".x") + ' ' + | ||
| 2953 | component_combiner + ' ' + | ||
| 2954 | GetPredicateComparison(instr.hsetp2.cond, op_a + ".y", op_b + ".y") + ')'; | ||
| 2955 | |||
| 2956 | // Set the primary predicate to the result of Predicate OP SecondPredicate | ||
| 2957 | SetPredicate(instr.hsetp2.pred3, | ||
| 2958 | '(' + predicate + ") " + combiner + " (" + second_pred + ')'); | ||
| 2959 | |||
| 2960 | if (instr.hsetp2.pred0 != static_cast<u64>(Pred::UnusedIndex)) { | ||
| 2961 | // Set the secondary predicate to the result of !Predicate OP SecondPredicate, | ||
| 2962 | // if enabled | ||
| 2963 | SetPredicate(instr.hsetp2.pred0, | ||
| 2964 | "!(" + predicate + ") " + combiner + " (" + second_pred + ')'); | ||
| 2965 | } | ||
| 2966 | break; | ||
| 2967 | } | ||
| 2614 | case OpCode::Type::PredicateSetRegister: { | 2968 | case OpCode::Type::PredicateSetRegister: { |
| 2615 | const std::string op_a = | 2969 | const std::string op_a = |
| 2616 | GetPredicateCondition(instr.pset.pred12, instr.pset.neg_pred12 != 0); | 2970 | GetPredicateCondition(instr.pset.pred12, instr.pset.neg_pred12 != 0); |
| @@ -2689,33 +3043,24 @@ private: | |||
| 2689 | break; | 3043 | break; |
| 2690 | } | 3044 | } |
| 2691 | case OpCode::Type::FloatSet: { | 3045 | case OpCode::Type::FloatSet: { |
| 2692 | std::string op_a = instr.fset.neg_a ? "-" : ""; | 3046 | const std::string op_a = GetOperandAbsNeg(regs.GetRegisterAsFloat(instr.gpr8), |
| 2693 | op_a += regs.GetRegisterAsFloat(instr.gpr8); | 3047 | instr.fset.abs_a != 0, instr.fset.neg_a != 0); |
| 2694 | |||
| 2695 | if (instr.fset.abs_a) { | ||
| 2696 | op_a = "abs(" + op_a + ')'; | ||
| 2697 | } | ||
| 2698 | 3048 | ||
| 2699 | std::string op_b = instr.fset.neg_b ? "-" : ""; | 3049 | std::string op_b; |
| 2700 | 3050 | ||
| 2701 | if (instr.is_b_imm) { | 3051 | if (instr.is_b_imm) { |
| 2702 | const std::string imm = GetImmediate19(instr); | 3052 | const std::string imm = GetImmediate19(instr); |
| 2703 | if (instr.fset.neg_imm) | 3053 | op_b = imm; |
| 2704 | op_b += "(-" + imm + ')'; | ||
| 2705 | else | ||
| 2706 | op_b += imm; | ||
| 2707 | } else { | 3054 | } else { |
| 2708 | if (instr.is_b_gpr) { | 3055 | if (instr.is_b_gpr) { |
| 2709 | op_b += regs.GetRegisterAsFloat(instr.gpr20); | 3056 | op_b = regs.GetRegisterAsFloat(instr.gpr20); |
| 2710 | } else { | 3057 | } else { |
| 2711 | op_b += regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset, | 3058 | op_b = regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset, |
| 2712 | GLSLRegister::Type::Float); | 3059 | GLSLRegister::Type::Float); |
| 2713 | } | 3060 | } |
| 2714 | } | 3061 | } |
| 2715 | 3062 | ||
| 2716 | if (instr.fset.abs_b) { | 3063 | op_b = GetOperandAbsNeg(op_b, instr.fset.abs_b != 0, instr.fset.neg_b != 0); |
| 2717 | op_b = "abs(" + op_b + ')'; | ||
| 2718 | } | ||
| 2719 | 3064 | ||
| 2720 | // The fset instruction sets a register to 1.0 or -1 (depending on the bf bit) if the | 3065 | // The fset instruction sets a register to 1.0 or -1 (depending on the bf bit) if the |
| 2721 | // condition is true, and to 0 otherwise. | 3066 | // condition is true, and to 0 otherwise. |
| @@ -2771,6 +3116,50 @@ private: | |||
| 2771 | } | 3116 | } |
| 2772 | break; | 3117 | break; |
| 2773 | } | 3118 | } |
| 3119 | case OpCode::Type::HalfSet: { | ||
| 3120 | ASSERT_MSG(instr.hset2.ftz == 0, "Unimplemented"); | ||
| 3121 | |||
| 3122 | const std::string op_a = | ||
| 3123 | GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr8, 0, false), instr.hset2.type_a, | ||
| 3124 | instr.hset2.abs_a != 0, instr.hset2.negate_a != 0); | ||
| 3125 | |||
| 3126 | const std::string op_b = [&]() { | ||
| 3127 | switch (opcode->GetId()) { | ||
| 3128 | case OpCode::Id::HSET2_R: | ||
| 3129 | return GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr20, 0, false), | ||
| 3130 | instr.hset2.type_b, instr.hset2.abs_b != 0, | ||
| 3131 | instr.hset2.negate_b != 0); | ||
| 3132 | default: | ||
| 3133 | UNREACHABLE(); | ||
| 3134 | return std::string("vec2(0)"); | ||
| 3135 | } | ||
| 3136 | }(); | ||
| 3137 | |||
| 3138 | const std::string second_pred = | ||
| 3139 | GetPredicateCondition(instr.hset2.pred39, instr.hset2.neg_pred != 0); | ||
| 3140 | |||
| 3141 | const std::string combiner = GetPredicateCombiner(instr.hset2.op); | ||
| 3142 | |||
| 3143 | // HSET2 operates on each half float in the pack. | ||
| 3144 | std::string result; | ||
| 3145 | for (int i = 0; i < 2; ++i) { | ||
| 3146 | const std::string float_value = i == 0 ? "0x00003c00" : "0x3c000000"; | ||
| 3147 | const std::string integer_value = i == 0 ? "0x0000ffff" : "0xffff0000"; | ||
| 3148 | const std::string value = instr.hset2.bf == 1 ? float_value : integer_value; | ||
| 3149 | |||
| 3150 | const std::string comp = std::string(".") + "xy"[i]; | ||
| 3151 | const std::string predicate = | ||
| 3152 | "((" + GetPredicateComparison(instr.hset2.cond, op_a + comp, op_b + comp) + | ||
| 3153 | ") " + combiner + " (" + second_pred + "))"; | ||
| 3154 | |||
| 3155 | result += '(' + predicate + " ? " + value + " : 0)"; | ||
| 3156 | if (i == 0) { | ||
| 3157 | result += " | "; | ||
| 3158 | } | ||
| 3159 | } | ||
| 3160 | regs.SetRegisterToInteger(instr.gpr0, false, 0, '(' + result + ')', 1, 1); | ||
| 3161 | break; | ||
| 3162 | } | ||
| 2774 | case OpCode::Type::Xmad: { | 3163 | case OpCode::Type::Xmad: { |
| 2775 | ASSERT_MSG(!instr.xmad.sign_a, "Unimplemented"); | 3164 | ASSERT_MSG(!instr.xmad.sign_a, "Unimplemented"); |
| 2776 | ASSERT_MSG(!instr.xmad.sign_b, "Unimplemented"); | 3165 | ASSERT_MSG(!instr.xmad.sign_b, "Unimplemented"); |
| @@ -2979,16 +3368,32 @@ private: | |||
| 2979 | // The SSY opcode tells the GPU where to re-converge divergent execution paths, it | 3368 | // The SSY opcode tells the GPU where to re-converge divergent execution paths, it |
| 2980 | // sets the target of the jump that the SYNC instruction will make. The SSY opcode | 3369 | // sets the target of the jump that the SYNC instruction will make. The SSY opcode |
| 2981 | // has a similar structure to the BRA opcode. | 3370 | // has a similar structure to the BRA opcode. |
| 2982 | ASSERT_MSG(instr.bra.constant_buffer == 0, "Constant buffer SSY is not supported"); | 3371 | ASSERT_MSG(instr.bra.constant_buffer == 0, "Constant buffer flow is not supported"); |
| 3372 | |||
| 3373 | const u32 target = offset + instr.bra.GetBranchTarget(); | ||
| 3374 | EmitPushToFlowStack(target); | ||
| 3375 | break; | ||
| 3376 | } | ||
| 3377 | case OpCode::Id::PBK: { | ||
| 3378 | // PBK pushes to a stack the address where BRK will jump to. This shares stack with | ||
| 3379 | // SSY but using SYNC on a PBK address will kill the shader execution. We don't | ||
| 3380 | // emulate this because it's very unlikely a driver will emit such invalid shader. | ||
| 3381 | ASSERT_MSG(instr.bra.constant_buffer == 0, "Constant buffer PBK is not supported"); | ||
| 2983 | 3382 | ||
| 2984 | const u32 target = offset + instr.bra.GetBranchTarget(); | 3383 | const u32 target = offset + instr.bra.GetBranchTarget(); |
| 2985 | EmitPushToSSYStack(target); | 3384 | EmitPushToFlowStack(target); |
| 2986 | break; | 3385 | break; |
| 2987 | } | 3386 | } |
| 2988 | case OpCode::Id::SYNC: { | 3387 | case OpCode::Id::SYNC: { |
| 2989 | // The SYNC opcode jumps to the address previously set by the SSY opcode | 3388 | // The SYNC opcode jumps to the address previously set by the SSY opcode |
| 2990 | ASSERT(instr.flow.cond == Tegra::Shader::FlowCondition::Always); | 3389 | ASSERT(instr.flow.cond == Tegra::Shader::FlowCondition::Always); |
| 2991 | EmitPopFromSSYStack(); | 3390 | EmitPopFromFlowStack(); |
| 3391 | break; | ||
| 3392 | } | ||
| 3393 | case OpCode::Id::BRK: { | ||
| 3394 | // The BRK opcode jumps to the address previously set by the PBK opcode | ||
| 3395 | ASSERT(instr.flow.cond == Tegra::Shader::FlowCondition::Always); | ||
| 3396 | EmitPopFromFlowStack(); | ||
| 2992 | break; | 3397 | break; |
| 2993 | } | 3398 | } |
| 2994 | case OpCode::Id::DEPBAR: { | 3399 | case OpCode::Id::DEPBAR: { |
| @@ -2998,87 +3403,51 @@ private: | |||
| 2998 | break; | 3403 | break; |
| 2999 | } | 3404 | } |
| 3000 | case OpCode::Id::VMAD: { | 3405 | case OpCode::Id::VMAD: { |
| 3001 | const bool signed_a = instr.vmad.signed_a == 1; | 3406 | const bool result_signed = instr.video.signed_a == 1 || instr.video.signed_b == 1; |
| 3002 | const bool signed_b = instr.vmad.signed_b == 1; | 3407 | const std::string op_a = GetVideoOperandA(instr); |
| 3003 | const bool result_signed = signed_a || signed_b; | 3408 | const std::string op_b = GetVideoOperandB(instr); |
| 3004 | boost::optional<std::string> forced_result; | ||
| 3005 | |||
| 3006 | auto Unpack = [&](const std::string& op, bool is_chunk, bool is_signed, | ||
| 3007 | Tegra::Shader::VmadType type, u64 byte_height) { | ||
| 3008 | const std::string value = [&]() { | ||
| 3009 | if (!is_chunk) { | ||
| 3010 | const auto offset = static_cast<u32>(byte_height * 8); | ||
| 3011 | return "((" + op + " >> " + std::to_string(offset) + ") & 0xff)"; | ||
| 3012 | } | ||
| 3013 | const std::string zero = "0"; | ||
| 3014 | |||
| 3015 | switch (type) { | ||
| 3016 | case Tegra::Shader::VmadType::Size16_Low: | ||
| 3017 | return '(' + op + " & 0xffff)"; | ||
| 3018 | case Tegra::Shader::VmadType::Size16_High: | ||
| 3019 | return '(' + op + " >> 16)"; | ||
| 3020 | case Tegra::Shader::VmadType::Size32: | ||
| 3021 | // TODO(Rodrigo): From my hardware tests it becomes a bit "mad" when | ||
| 3022 | // this type is used (1 * 1 + 0 == 0x5b800000). Until a better | ||
| 3023 | // explanation is found: assert. | ||
| 3024 | UNREACHABLE_MSG("Unimplemented"); | ||
| 3025 | return zero; | ||
| 3026 | case Tegra::Shader::VmadType::Invalid: | ||
| 3027 | // Note(Rodrigo): This flag is invalid according to nvdisasm. From my | ||
| 3028 | // testing (even though it's invalid) this makes the whole instruction | ||
| 3029 | // assign zero to target register. | ||
| 3030 | forced_result = boost::make_optional(zero); | ||
| 3031 | return zero; | ||
| 3032 | default: | ||
| 3033 | UNREACHABLE(); | ||
| 3034 | return zero; | ||
| 3035 | } | ||
| 3036 | }(); | ||
| 3037 | |||
| 3038 | if (is_signed) { | ||
| 3039 | return "int(" + value + ')'; | ||
| 3040 | } | ||
| 3041 | return value; | ||
| 3042 | }; | ||
| 3043 | |||
| 3044 | const std::string op_a = Unpack(regs.GetRegisterAsInteger(instr.gpr8, 0, false), | ||
| 3045 | instr.vmad.is_byte_chunk_a != 0, signed_a, | ||
| 3046 | instr.vmad.type_a, instr.vmad.byte_height_a); | ||
| 3047 | |||
| 3048 | std::string op_b; | ||
| 3049 | if (instr.vmad.use_register_b) { | ||
| 3050 | op_b = Unpack(regs.GetRegisterAsInteger(instr.gpr20, 0, false), | ||
| 3051 | instr.vmad.is_byte_chunk_b != 0, signed_b, instr.vmad.type_b, | ||
| 3052 | instr.vmad.byte_height_b); | ||
| 3053 | } else { | ||
| 3054 | op_b = '(' + | ||
| 3055 | std::to_string(signed_b ? static_cast<s16>(instr.alu.GetImm20_16()) | ||
| 3056 | : instr.alu.GetImm20_16()) + | ||
| 3057 | ')'; | ||
| 3058 | } | ||
| 3059 | |||
| 3060 | const std::string op_c = regs.GetRegisterAsInteger(instr.gpr39, 0, result_signed); | 3409 | const std::string op_c = regs.GetRegisterAsInteger(instr.gpr39, 0, result_signed); |
| 3061 | 3410 | ||
| 3062 | std::string result; | 3411 | std::string result = '(' + op_a + " * " + op_b + " + " + op_c + ')'; |
| 3063 | if (forced_result) { | ||
| 3064 | result = *forced_result; | ||
| 3065 | } else { | ||
| 3066 | result = '(' + op_a + " * " + op_b + " + " + op_c + ')'; | ||
| 3067 | 3412 | ||
| 3068 | switch (instr.vmad.shr) { | 3413 | switch (instr.vmad.shr) { |
| 3069 | case Tegra::Shader::VmadShr::Shr7: | 3414 | case Tegra::Shader::VmadShr::Shr7: |
| 3070 | result = '(' + result + " >> 7)"; | 3415 | result = '(' + result + " >> 7)"; |
| 3071 | break; | 3416 | break; |
| 3072 | case Tegra::Shader::VmadShr::Shr15: | 3417 | case Tegra::Shader::VmadShr::Shr15: |
| 3073 | result = '(' + result + " >> 15)"; | 3418 | result = '(' + result + " >> 15)"; |
| 3074 | break; | 3419 | break; |
| 3075 | } | ||
| 3076 | } | 3420 | } |
| 3421 | |||
| 3077 | regs.SetRegisterToInteger(instr.gpr0, result_signed, 1, result, 1, 1, | 3422 | regs.SetRegisterToInteger(instr.gpr0, result_signed, 1, result, 1, 1, |
| 3078 | instr.vmad.saturate == 1, 0, Register::Size::Word, | 3423 | instr.vmad.saturate == 1, 0, Register::Size::Word, |
| 3079 | instr.vmad.cc); | 3424 | instr.vmad.cc); |
| 3080 | break; | 3425 | break; |
| 3081 | } | 3426 | } |
| 3427 | case OpCode::Id::VSETP: { | ||
| 3428 | const std::string op_a = GetVideoOperandA(instr); | ||
| 3429 | const std::string op_b = GetVideoOperandB(instr); | ||
| 3430 | |||
| 3431 | // We can't use the constant predicate as destination. | ||
| 3432 | ASSERT(instr.vsetp.pred3 != static_cast<u64>(Pred::UnusedIndex)); | ||
| 3433 | |||
| 3434 | const std::string second_pred = GetPredicateCondition(instr.vsetp.pred39, false); | ||
| 3435 | |||
| 3436 | const std::string combiner = GetPredicateCombiner(instr.vsetp.op); | ||
| 3437 | |||
| 3438 | const std::string predicate = GetPredicateComparison(instr.vsetp.cond, op_a, op_b); | ||
| 3439 | // Set the primary predicate to the result of Predicate OP SecondPredicate | ||
| 3440 | SetPredicate(instr.vsetp.pred3, | ||
| 3441 | '(' + predicate + ") " + combiner + " (" + second_pred + ')'); | ||
| 3442 | |||
| 3443 | if (instr.vsetp.pred0 != static_cast<u64>(Pred::UnusedIndex)) { | ||
| 3444 | // Set the secondary predicate to the result of !Predicate OP SecondPredicate, | ||
| 3445 | // if enabled | ||
| 3446 | SetPredicate(instr.vsetp.pred0, | ||
| 3447 | "!(" + predicate + ") " + combiner + " (" + second_pred + ')'); | ||
| 3448 | } | ||
| 3449 | break; | ||
| 3450 | } | ||
| 3082 | default: { | 3451 | default: { |
| 3083 | LOG_CRITICAL(HW_GPU, "Unhandled instruction: {}", opcode->GetName()); | 3452 | LOG_CRITICAL(HW_GPU, "Unhandled instruction: {}", opcode->GetName()); |
| 3084 | UNREACHABLE(); | 3453 | UNREACHABLE(); |
| @@ -3142,11 +3511,11 @@ private: | |||
| 3142 | labels.insert(subroutine.begin); | 3511 | labels.insert(subroutine.begin); |
| 3143 | shader.AddLine("uint jmp_to = " + std::to_string(subroutine.begin) + "u;"); | 3512 | shader.AddLine("uint jmp_to = " + std::to_string(subroutine.begin) + "u;"); |
| 3144 | 3513 | ||
| 3145 | // TODO(Subv): Figure out the actual depth of the SSY stack, for now it seems | 3514 | // TODO(Subv): Figure out the actual depth of the flow stack, for now it seems |
| 3146 | // unlikely that shaders will use 20 nested SSYs. | 3515 | // unlikely that shaders will use 20 nested SSYs and PBKs. |
| 3147 | constexpr u32 SSY_STACK_SIZE = 20; | 3516 | constexpr u32 FLOW_STACK_SIZE = 20; |
| 3148 | shader.AddLine("uint ssy_stack[" + std::to_string(SSY_STACK_SIZE) + "];"); | 3517 | shader.AddLine("uint flow_stack[" + std::to_string(FLOW_STACK_SIZE) + "];"); |
| 3149 | shader.AddLine("uint ssy_stack_top = 0u;"); | 3518 | shader.AddLine("uint flow_stack_top = 0u;"); |
| 3150 | 3519 | ||
| 3151 | shader.AddLine("while (true) {"); | 3520 | shader.AddLine("while (true) {"); |
| 3152 | ++shader.scope; | 3521 | ++shader.scope; |
| @@ -3213,7 +3582,7 @@ private: | |||
| 3213 | 3582 | ||
| 3214 | // Declarations | 3583 | // Declarations |
| 3215 | std::set<std::string> declr_predicates; | 3584 | std::set<std::string> declr_predicates; |
| 3216 | }; // namespace Decompiler | 3585 | }; // namespace OpenGL::GLShader::Decompiler |
| 3217 | 3586 | ||
| 3218 | std::string GetCommonDeclarations() { | 3587 | std::string GetCommonDeclarations() { |
| 3219 | return fmt::format("#define MAX_CONSTBUFFER_ELEMENTS {}\n", | 3588 | return fmt::format("#define MAX_CONSTBUFFER_ELEMENTS {}\n", |
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index 1e5eb32df..dfb562706 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp | |||
| @@ -19,14 +19,14 @@ ProgramResult GenerateVertexShader(const ShaderSetup& setup) { | |||
| 19 | out += Decompiler::GetCommonDeclarations(); | 19 | out += Decompiler::GetCommonDeclarations(); |
| 20 | 20 | ||
| 21 | out += R"( | 21 | out += R"( |
| 22 | out gl_PerVertex { | 22 | |
| 23 | vec4 gl_Position; | 23 | layout (location = 0) out vec4 position; |
| 24 | }; | ||
| 25 | 24 | ||
| 26 | layout(std140) uniform vs_config { | 25 | layout(std140) uniform vs_config { |
| 27 | vec4 viewport_flip; | 26 | vec4 viewport_flip; |
| 28 | uvec4 instance_id; | 27 | uvec4 instance_id; |
| 29 | uvec4 flip_stage; | 28 | uvec4 flip_stage; |
| 29 | uvec4 alpha_test; | ||
| 30 | }; | 30 | }; |
| 31 | )"; | 31 | )"; |
| 32 | 32 | ||
| @@ -96,10 +96,14 @@ out gl_PerVertex { | |||
| 96 | vec4 gl_Position; | 96 | vec4 gl_Position; |
| 97 | }; | 97 | }; |
| 98 | 98 | ||
| 99 | layout (location = 0) in vec4 gs_position[]; | ||
| 100 | layout (location = 0) out vec4 position; | ||
| 101 | |||
| 99 | layout (std140) uniform gs_config { | 102 | layout (std140) uniform gs_config { |
| 100 | vec4 viewport_flip; | 103 | vec4 viewport_flip; |
| 101 | uvec4 instance_id; | 104 | uvec4 instance_id; |
| 102 | uvec4 flip_stage; | 105 | uvec4 flip_stage; |
| 106 | uvec4 alpha_test; | ||
| 103 | }; | 107 | }; |
| 104 | 108 | ||
| 105 | void main() { | 109 | void main() { |
| @@ -131,12 +135,39 @@ layout(location = 5) out vec4 FragColor5; | |||
| 131 | layout(location = 6) out vec4 FragColor6; | 135 | layout(location = 6) out vec4 FragColor6; |
| 132 | layout(location = 7) out vec4 FragColor7; | 136 | layout(location = 7) out vec4 FragColor7; |
| 133 | 137 | ||
| 138 | layout (location = 0) in vec4 position; | ||
| 139 | |||
| 134 | layout (std140) uniform fs_config { | 140 | layout (std140) uniform fs_config { |
| 135 | vec4 viewport_flip; | 141 | vec4 viewport_flip; |
| 136 | uvec4 instance_id; | 142 | uvec4 instance_id; |
| 137 | uvec4 flip_stage; | 143 | uvec4 flip_stage; |
| 144 | uvec4 alpha_test; | ||
| 138 | }; | 145 | }; |
| 139 | 146 | ||
| 147 | bool AlphaFunc(in float value) { | ||
| 148 | float ref = uintBitsToFloat(alpha_test[2]); | ||
| 149 | switch (alpha_test[1]) { | ||
| 150 | case 1: | ||
| 151 | return false; | ||
| 152 | case 2: | ||
| 153 | return value < ref; | ||
| 154 | case 3: | ||
| 155 | return value == ref; | ||
| 156 | case 4: | ||
| 157 | return value <= ref; | ||
| 158 | case 5: | ||
| 159 | return value > ref; | ||
| 160 | case 6: | ||
| 161 | return value != ref; | ||
| 162 | case 7: | ||
| 163 | return value >= ref; | ||
| 164 | case 8: | ||
| 165 | return true; | ||
| 166 | default: | ||
| 167 | return false; | ||
| 168 | } | ||
| 169 | } | ||
| 170 | |||
| 140 | void main() { | 171 | void main() { |
| 141 | exec_fragment(); | 172 | exec_fragment(); |
| 142 | } | 173 | } |
| @@ -145,4 +176,4 @@ void main() { | |||
| 145 | out += program.first; | 176 | out += program.first; |
| 146 | return {out, program.second}; | 177 | return {out, program.second}; |
| 147 | } | 178 | } |
| 148 | } // namespace OpenGL::GLShader \ No newline at end of file | 179 | } // namespace OpenGL::GLShader |
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.h b/src/video_core/renderer_opengl/gl_shader_gen.h index 79596087a..520b9d4e3 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.h +++ b/src/video_core/renderer_opengl/gl_shader_gen.h | |||
| @@ -16,6 +16,8 @@ namespace OpenGL::GLShader { | |||
| 16 | constexpr std::size_t MAX_PROGRAM_CODE_LENGTH{0x1000}; | 16 | constexpr std::size_t MAX_PROGRAM_CODE_LENGTH{0x1000}; |
| 17 | using ProgramCode = std::vector<u64>; | 17 | using ProgramCode = std::vector<u64>; |
| 18 | 18 | ||
| 19 | enum : u32 { POSITION_VARYING_LOCATION = 0, GENERIC_VARYING_START_LOCATION = 1 }; | ||
| 20 | |||
| 19 | class ConstBufferEntry { | 21 | class ConstBufferEntry { |
| 20 | using Maxwell = Tegra::Engines::Maxwell3D::Regs; | 22 | using Maxwell = Tegra::Engines::Maxwell3D::Regs; |
| 21 | 23 | ||
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.cpp b/src/video_core/renderer_opengl/gl_shader_manager.cpp index 010857ec6..8b8869ecb 100644 --- a/src/video_core/renderer_opengl/gl_shader_manager.cpp +++ b/src/video_core/renderer_opengl/gl_shader_manager.cpp | |||
| @@ -16,6 +16,17 @@ void MaxwellUniformData::SetFromRegs(const Maxwell3D::State::ShaderStageInfo& sh | |||
| 16 | viewport_flip[0] = regs.viewport_transform[0].scale_x < 0.0 ? -1.0f : 1.0f; | 16 | viewport_flip[0] = regs.viewport_transform[0].scale_x < 0.0 ? -1.0f : 1.0f; |
| 17 | viewport_flip[1] = regs.viewport_transform[0].scale_y < 0.0 ? -1.0f : 1.0f; | 17 | viewport_flip[1] = regs.viewport_transform[0].scale_y < 0.0 ? -1.0f : 1.0f; |
| 18 | 18 | ||
| 19 | u32 func = static_cast<u32>(regs.alpha_test_func); | ||
| 20 | // Normalize the gl variants of opCompare to be the same as the normal variants | ||
| 21 | u32 op_gl_variant_base = static_cast<u32>(Tegra::Engines::Maxwell3D::Regs::ComparisonOp::Never); | ||
| 22 | if (func >= op_gl_variant_base) { | ||
| 23 | func = func - op_gl_variant_base + 1U; | ||
| 24 | } | ||
| 25 | |||
| 26 | alpha_test.enabled = regs.alpha_test_enabled; | ||
| 27 | alpha_test.func = func; | ||
| 28 | alpha_test.ref = regs.alpha_test_ref; | ||
| 29 | |||
| 19 | // We only assign the instance to the first component of the vector, the rest is just padding. | 30 | // We only assign the instance to the first component of the vector, the rest is just padding. |
| 20 | instance_id[0] = state.current_instance; | 31 | instance_id[0] = state.current_instance; |
| 21 | 32 | ||
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.h b/src/video_core/renderer_opengl/gl_shader_manager.h index b3a191cf2..36fe1f04c 100644 --- a/src/video_core/renderer_opengl/gl_shader_manager.h +++ b/src/video_core/renderer_opengl/gl_shader_manager.h | |||
| @@ -22,8 +22,14 @@ struct MaxwellUniformData { | |||
| 22 | alignas(16) GLvec4 viewport_flip; | 22 | alignas(16) GLvec4 viewport_flip; |
| 23 | alignas(16) GLuvec4 instance_id; | 23 | alignas(16) GLuvec4 instance_id; |
| 24 | alignas(16) GLuvec4 flip_stage; | 24 | alignas(16) GLuvec4 flip_stage; |
| 25 | struct alignas(16) { | ||
| 26 | GLuint enabled; | ||
| 27 | GLuint func; | ||
| 28 | GLfloat ref; | ||
| 29 | GLuint padding; | ||
| 30 | } alpha_test; | ||
| 25 | }; | 31 | }; |
| 26 | static_assert(sizeof(MaxwellUniformData) == 48, "MaxwellUniformData structure size is incorrect"); | 32 | static_assert(sizeof(MaxwellUniformData) == 64, "MaxwellUniformData structure size is incorrect"); |
| 27 | static_assert(sizeof(MaxwellUniformData) < 16384, | 33 | static_assert(sizeof(MaxwellUniformData) < 16384, |
| 28 | "MaxwellUniformData structure must be less than 16kb as per the OpenGL spec"); | 34 | "MaxwellUniformData structure must be less than 16kb as per the OpenGL spec"); |
| 29 | 35 | ||
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp index 1fe26a2a9..ba6c6919a 100644 --- a/src/video_core/renderer_opengl/gl_state.cpp +++ b/src/video_core/renderer_opengl/gl_state.cpp | |||
| @@ -21,6 +21,8 @@ OpenGLState::OpenGLState() { | |||
| 21 | depth.test_enabled = false; | 21 | depth.test_enabled = false; |
| 22 | depth.test_func = GL_LESS; | 22 | depth.test_func = GL_LESS; |
| 23 | depth.write_mask = GL_TRUE; | 23 | depth.write_mask = GL_TRUE; |
| 24 | depth.depth_range_near = 0.0f; | ||
| 25 | depth.depth_range_far = 1.0f; | ||
| 24 | 26 | ||
| 25 | color_mask.red_enabled = GL_TRUE; | 27 | color_mask.red_enabled = GL_TRUE; |
| 26 | color_mask.green_enabled = GL_TRUE; | 28 | color_mask.green_enabled = GL_TRUE; |
| @@ -119,6 +121,12 @@ void OpenGLState::Apply() const { | |||
| 119 | glDepthMask(depth.write_mask); | 121 | glDepthMask(depth.write_mask); |
| 120 | } | 122 | } |
| 121 | 123 | ||
| 124 | // Depth range | ||
| 125 | if (depth.depth_range_near != cur_state.depth.depth_range_near || | ||
| 126 | depth.depth_range_far != cur_state.depth.depth_range_far) { | ||
| 127 | glDepthRange(depth.depth_range_near, depth.depth_range_far); | ||
| 128 | } | ||
| 129 | |||
| 122 | // Color mask | 130 | // Color mask |
| 123 | if (color_mask.red_enabled != cur_state.color_mask.red_enabled || | 131 | if (color_mask.red_enabled != cur_state.color_mask.red_enabled || |
| 124 | color_mask.green_enabled != cur_state.color_mask.green_enabled || | 132 | color_mask.green_enabled != cur_state.color_mask.green_enabled || |
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h index dc21a2ee3..daf7eb533 100644 --- a/src/video_core/renderer_opengl/gl_state.h +++ b/src/video_core/renderer_opengl/gl_state.h | |||
| @@ -42,9 +42,11 @@ public: | |||
| 42 | } cull; | 42 | } cull; |
| 43 | 43 | ||
| 44 | struct { | 44 | struct { |
| 45 | bool test_enabled; // GL_DEPTH_TEST | 45 | bool test_enabled; // GL_DEPTH_TEST |
| 46 | GLenum test_func; // GL_DEPTH_FUNC | 46 | GLenum test_func; // GL_DEPTH_FUNC |
| 47 | GLboolean write_mask; // GL_DEPTH_WRITEMASK | 47 | GLboolean write_mask; // GL_DEPTH_WRITEMASK |
| 48 | GLfloat depth_range_near; // GL_DEPTH_RANGE | ||
| 49 | GLfloat depth_range_far; // GL_DEPTH_RANGE | ||
| 48 | } depth; | 50 | } depth; |
| 49 | 51 | ||
| 50 | struct { | 52 | struct { |
diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h index 3c3bcaae4..0f6dcab2b 100644 --- a/src/video_core/renderer_opengl/maxwell_to_gl.h +++ b/src/video_core/renderer_opengl/maxwell_to_gl.h | |||
| @@ -82,8 +82,20 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) { | |||
| 82 | return {}; | 82 | return {}; |
| 83 | } | 83 | } |
| 84 | 84 | ||
| 85 | case Maxwell::VertexAttribute::Type::Float: | 85 | case Maxwell::VertexAttribute::Type::Float: { |
| 86 | return GL_FLOAT; | 86 | switch (attrib.size) { |
| 87 | case Maxwell::VertexAttribute::Size::Size_16: | ||
| 88 | case Maxwell::VertexAttribute::Size::Size_16_16: | ||
| 89 | case Maxwell::VertexAttribute::Size::Size_16_16_16: | ||
| 90 | case Maxwell::VertexAttribute::Size::Size_16_16_16_16: | ||
| 91 | return GL_HALF_FLOAT; | ||
| 92 | case Maxwell::VertexAttribute::Size::Size_32: | ||
| 93 | case Maxwell::VertexAttribute::Size::Size_32_32: | ||
| 94 | case Maxwell::VertexAttribute::Size::Size_32_32_32: | ||
| 95 | case Maxwell::VertexAttribute::Size::Size_32_32_32_32: | ||
| 96 | return GL_FLOAT; | ||
| 97 | } | ||
| 98 | } | ||
| 87 | } | 99 | } |
| 88 | 100 | ||
| 89 | LOG_CRITICAL(Render_OpenGL, "Unimplemented vertex type={}", attrib.TypeString()); | 101 | LOG_CRITICAL(Render_OpenGL, "Unimplemented vertex type={}", attrib.TypeString()); |
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp index 18ab723f7..550ca856c 100644 --- a/src/video_core/textures/decoders.cpp +++ b/src/video_core/textures/decoders.cpp | |||
| @@ -142,7 +142,6 @@ void SwizzledData(u8* swizzled_data, u8* unswizzled_data, const bool unswizzle, | |||
| 142 | const u32 blocks_on_x = div_ceil(width, block_x_elements); | 142 | const u32 blocks_on_x = div_ceil(width, block_x_elements); |
| 143 | const u32 blocks_on_y = div_ceil(height, block_y_elements); | 143 | const u32 blocks_on_y = div_ceil(height, block_y_elements); |
| 144 | const u32 blocks_on_z = div_ceil(depth, block_z_elements); | 144 | const u32 blocks_on_z = div_ceil(depth, block_z_elements); |
| 145 | const u32 blocks = blocks_on_x * blocks_on_y * blocks_on_z; | ||
| 146 | const u32 gob_size = gob_x_bytes * gob_elements_y * gob_elements_z; | 145 | const u32 gob_size = gob_x_bytes * gob_elements_y * gob_elements_z; |
| 147 | const u32 xy_block_size = gob_size * block_height; | 146 | const u32 xy_block_size = gob_size * block_height; |
| 148 | const u32 block_size = xy_block_size * block_depth; | 147 | const u32 block_size = xy_block_size * block_depth; |
| @@ -237,6 +236,46 @@ std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size, u32 bytes_per_pix | |||
| 237 | return unswizzled_data; | 236 | return unswizzled_data; |
| 238 | } | 237 | } |
| 239 | 238 | ||
| 239 | void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 swizzled_width, | ||
| 240 | u32 bytes_per_pixel, VAddr swizzled_data, VAddr unswizzled_data, | ||
| 241 | u32 block_height) { | ||
| 242 | const u32 image_width_in_gobs{(swizzled_width * bytes_per_pixel + 63) / 64}; | ||
| 243 | for (u32 line = 0; line < subrect_height; ++line) { | ||
| 244 | const u32 gob_address_y = | ||
| 245 | (line / (8 * block_height)) * 512 * block_height * image_width_in_gobs + | ||
| 246 | (line % (8 * block_height) / 8) * 512; | ||
| 247 | const auto& table = legacy_swizzle_table[line % 8]; | ||
| 248 | for (u32 x = 0; x < subrect_width; ++x) { | ||
| 249 | const u32 gob_address = gob_address_y + (x * bytes_per_pixel / 64) * 512 * block_height; | ||
| 250 | const u32 swizzled_offset = gob_address + table[(x * bytes_per_pixel) % 64]; | ||
| 251 | const VAddr source_line = unswizzled_data + line * source_pitch + x * bytes_per_pixel; | ||
| 252 | const VAddr dest_addr = swizzled_data + swizzled_offset; | ||
| 253 | |||
| 254 | Memory::CopyBlock(dest_addr, source_line, bytes_per_pixel); | ||
| 255 | } | ||
| 256 | } | ||
| 257 | } | ||
| 258 | |||
| 259 | void UnswizzleSubrect(u32 subrect_width, u32 subrect_height, u32 dest_pitch, u32 swizzled_width, | ||
| 260 | u32 bytes_per_pixel, VAddr swizzled_data, VAddr unswizzled_data, | ||
| 261 | u32 block_height, u32 offset_x, u32 offset_y) { | ||
| 262 | for (u32 line = 0; line < subrect_height; ++line) { | ||
| 263 | const u32 y2 = line + offset_y; | ||
| 264 | const u32 gob_address_y = | ||
| 265 | (y2 / (8 * block_height)) * 512 * block_height + (y2 % (8 * block_height) / 8) * 512; | ||
| 266 | const auto& table = legacy_swizzle_table[y2 % 8]; | ||
| 267 | for (u32 x = 0; x < subrect_width; ++x) { | ||
| 268 | const u32 x2 = (x + offset_x) * bytes_per_pixel; | ||
| 269 | const u32 gob_address = gob_address_y + (x2 / 64) * 512 * block_height; | ||
| 270 | const u32 swizzled_offset = gob_address + table[x2 % 64]; | ||
| 271 | const VAddr dest_line = unswizzled_data + line * dest_pitch + x * bytes_per_pixel; | ||
| 272 | const VAddr source_addr = swizzled_data + swizzled_offset; | ||
| 273 | |||
| 274 | Memory::CopyBlock(dest_line, source_addr, bytes_per_pixel); | ||
| 275 | } | ||
| 276 | } | ||
| 277 | } | ||
| 278 | |||
| 240 | std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat format, u32 width, | 279 | std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat format, u32 width, |
| 241 | u32 height) { | 280 | u32 height) { |
| 242 | std::vector<u8> rgba_data; | 281 | std::vector<u8> rgba_data; |
| @@ -280,13 +319,13 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat | |||
| 280 | std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth, | 319 | std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth, |
| 281 | u32 block_height, u32 block_depth) { | 320 | u32 block_height, u32 block_depth) { |
| 282 | if (tiled) { | 321 | if (tiled) { |
| 283 | const u32 gobs_in_x = 64 / bytes_per_pixel; | 322 | const u32 gobs_in_x = 64; |
| 284 | const u32 gobs_in_y = 8; | 323 | const u32 gobs_in_y = 8; |
| 285 | const u32 gobs_in_z = 1; | 324 | const u32 gobs_in_z = 1; |
| 286 | const u32 aligned_width = Common::AlignUp(width, gobs_in_x); | 325 | const u32 aligned_width = Common::AlignUp(width * bytes_per_pixel, gobs_in_x); |
| 287 | const u32 aligned_height = Common::AlignUp(height, gobs_in_y * block_height); | 326 | const u32 aligned_height = Common::AlignUp(height, gobs_in_y * block_height); |
| 288 | const u32 aligned_depth = Common::AlignUp(depth, gobs_in_z * block_depth); | 327 | const u32 aligned_depth = Common::AlignUp(depth, gobs_in_z * block_depth); |
| 289 | return aligned_width * aligned_height * aligned_depth * bytes_per_pixel; | 328 | return aligned_width * aligned_height * aligned_depth; |
| 290 | } else { | 329 | } else { |
| 291 | return width * height * depth * bytes_per_pixel; | 330 | return width * height * depth * bytes_per_pixel; |
| 292 | } | 331 | } |
diff --git a/src/video_core/textures/decoders.h b/src/video_core/textures/decoders.h index aaf316947..4726f54a5 100644 --- a/src/video_core/textures/decoders.h +++ b/src/video_core/textures/decoders.h | |||
| @@ -35,4 +35,13 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat | |||
| 35 | std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth, | 35 | std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth, |
| 36 | u32 block_height, u32 block_depth); | 36 | u32 block_height, u32 block_depth); |
| 37 | 37 | ||
| 38 | /// Copies an untiled subrectangle into a tiled surface. | ||
| 39 | void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 swizzled_width, | ||
| 40 | u32 bytes_per_pixel, VAddr swizzled_data, VAddr unswizzled_data, | ||
| 41 | u32 block_height); | ||
| 42 | /// Copies a tiled subrectangle into a linear surface. | ||
| 43 | void UnswizzleSubrect(u32 subrect_width, u32 subrect_height, u32 dest_pitch, u32 swizzled_width, | ||
| 44 | u32 bytes_per_pixel, VAddr swizzled_data, VAddr unswizzled_data, | ||
| 45 | u32 block_height, u32 offset_x, u32 offset_y); | ||
| 46 | |||
| 38 | } // namespace Tegra::Texture | 47 | } // namespace Tegra::Texture |
diff --git a/src/web_service/CMakeLists.txt b/src/web_service/CMakeLists.txt index 1c83e9c34..01f2d129d 100644 --- a/src/web_service/CMakeLists.txt +++ b/src/web_service/CMakeLists.txt | |||
| @@ -10,7 +10,7 @@ add_library(web_service STATIC | |||
| 10 | create_target_directory_groups(web_service) | 10 | create_target_directory_groups(web_service) |
| 11 | 11 | ||
| 12 | get_directory_property(OPENSSL_LIBS | 12 | get_directory_property(OPENSSL_LIBS |
| 13 | DIRECTORY ${CMAKE_SOURCE_DIR}/externals/libressl | 13 | DIRECTORY ${PROJECT_SOURCE_DIR}/externals/libressl |
| 14 | DEFINITION OPENSSL_LIBS) | 14 | DEFINITION OPENSSL_LIBS) |
| 15 | target_compile_definitions(web_service PUBLIC -DCPPHTTPLIB_OPENSSL_SUPPORT) | 15 | target_compile_definitions(web_service PRIVATE -DCPPHTTPLIB_OPENSSL_SUPPORT) |
| 16 | target_link_libraries(web_service PRIVATE common json-headers ${OPENSSL_LIBS} httplib lurlparser) | 16 | target_link_libraries(web_service PRIVATE common json-headers ${OPENSSL_LIBS} httplib lurlparser) |
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 04464ad5e..9379d9110 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt | |||
| @@ -82,10 +82,10 @@ set(UIS | |||
| 82 | ) | 82 | ) |
| 83 | 83 | ||
| 84 | file(GLOB COMPAT_LIST | 84 | file(GLOB COMPAT_LIST |
| 85 | ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc | 85 | ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc |
| 86 | ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json) | 86 | ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json) |
| 87 | file(GLOB_RECURSE ICONS ${CMAKE_SOURCE_DIR}/dist/icons/*) | 87 | file(GLOB_RECURSE ICONS ${PROJECT_SOURCE_DIR}/dist/icons/*) |
| 88 | file(GLOB_RECURSE THEMES ${CMAKE_SOURCE_DIR}/dist/qt_themes/*) | 88 | file(GLOB_RECURSE THEMES ${PROJECT_SOURCE_DIR}/dist/qt_themes/*) |
| 89 | 89 | ||
| 90 | qt5_wrap_ui(UI_HDRS ${UIS}) | 90 | qt5_wrap_ui(UI_HDRS ${UIS}) |
| 91 | 91 | ||
| @@ -121,7 +121,7 @@ target_link_libraries(yuzu PRIVATE Boost::boost glad Qt5::OpenGL Qt5::Widgets) | |||
| 121 | target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) | 121 | target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) |
| 122 | 122 | ||
| 123 | if (YUZU_ENABLE_COMPATIBILITY_REPORTING) | 123 | if (YUZU_ENABLE_COMPATIBILITY_REPORTING) |
| 124 | add_definitions(-DYUZU_ENABLE_COMPATIBILITY_REPORTING) | 124 | target_compile_definitions(yuzu PRIVATE -DYUZU_ENABLE_COMPATIBILITY_REPORTING) |
| 125 | endif() | 125 | endif() |
| 126 | 126 | ||
| 127 | if (USE_DISCORD_PRESENCE) | 127 | if (USE_DISCORD_PRESENCE) |
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index e8ab23326..39eef8858 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp | |||
| @@ -8,7 +8,6 @@ | |||
| 8 | 8 | ||
| 9 | #include "common/microprofile.h" | 9 | #include "common/microprofile.h" |
| 10 | #include "common/scm_rev.h" | 10 | #include "common/scm_rev.h" |
| 11 | #include "common/string_util.h" | ||
| 12 | #include "core/core.h" | 11 | #include "core/core.h" |
| 13 | #include "core/frontend/framebuffer_layout.h" | 12 | #include "core/frontend/framebuffer_layout.h" |
| 14 | #include "core/settings.h" | 13 | #include "core/settings.h" |
| @@ -107,9 +106,8 @@ private: | |||
| 107 | GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) | 106 | GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) |
| 108 | : QWidget(parent), child(nullptr), emu_thread(emu_thread) { | 107 | : QWidget(parent), child(nullptr), emu_thread(emu_thread) { |
| 109 | 108 | ||
| 110 | std::string window_title = fmt::format("yuzu {} | {}-{}", Common::g_build_name, | 109 | setWindowTitle(QStringLiteral("yuzu %1 | %2-%3") |
| 111 | Common::g_scm_branch, Common::g_scm_desc); | 110 | .arg(Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc)); |
| 112 | setWindowTitle(QString::fromStdString(window_title)); | ||
| 113 | setAttribute(Qt::WA_AcceptTouchEvents); | 111 | setAttribute(Qt::WA_AcceptTouchEvents); |
| 114 | 112 | ||
| 115 | InputCommon::Init(); | 113 | InputCommon::Init(); |
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 71c6ebb41..d4fd60a73 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #include <QSettings> | 5 | #include <QSettings> |
| 6 | #include "common/file_util.h" | 6 | #include "common/file_util.h" |
| 7 | #include "core/hle/service/acc/profile_manager.h" | ||
| 7 | #include "input_common/main.h" | 8 | #include "input_common/main.h" |
| 8 | #include "yuzu/configuration/config.h" | 9 | #include "yuzu/configuration/config.h" |
| 9 | #include "yuzu/ui_settings.h" | 10 | #include "yuzu/ui_settings.h" |
| @@ -12,11 +13,16 @@ Config::Config() { | |||
| 12 | // TODO: Don't hardcode the path; let the frontend decide where to put the config files. | 13 | // TODO: Don't hardcode the path; let the frontend decide where to put the config files. |
| 13 | qt_config_loc = FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + "qt-config.ini"; | 14 | qt_config_loc = FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + "qt-config.ini"; |
| 14 | FileUtil::CreateFullPath(qt_config_loc); | 15 | FileUtil::CreateFullPath(qt_config_loc); |
| 15 | qt_config = new QSettings(QString::fromStdString(qt_config_loc), QSettings::IniFormat); | 16 | qt_config = |
| 17 | std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), QSettings::IniFormat); | ||
| 16 | 18 | ||
| 17 | Reload(); | 19 | Reload(); |
| 18 | } | 20 | } |
| 19 | 21 | ||
| 22 | Config::~Config() { | ||
| 23 | Save(); | ||
| 24 | } | ||
| 25 | |||
| 20 | const std::array<int, Settings::NativeButton::NumButtons> Config::default_buttons = { | 26 | const std::array<int, Settings::NativeButton::NumButtons> Config::default_buttons = { |
| 21 | Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_3, Qt::Key_4, Qt::Key_Q, | 27 | Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_3, Qt::Key_4, Qt::Key_Q, |
| 22 | Qt::Key_W, Qt::Key_1, Qt::Key_2, Qt::Key_N, Qt::Key_M, Qt::Key_F, Qt::Key_T, | 28 | Qt::Key_W, Qt::Key_1, Qt::Key_2, Qt::Key_N, Qt::Key_M, Qt::Key_F, Qt::Key_T, |
| @@ -122,7 +128,11 @@ void Config::ReadValues() { | |||
| 122 | 128 | ||
| 123 | qt_config->beginGroup("System"); | 129 | qt_config->beginGroup("System"); |
| 124 | Settings::values.use_docked_mode = qt_config->value("use_docked_mode", false).toBool(); | 130 | Settings::values.use_docked_mode = qt_config->value("use_docked_mode", false).toBool(); |
| 125 | Settings::values.username = qt_config->value("username", "yuzu").toString().toStdString(); | 131 | Settings::values.enable_nfc = qt_config->value("enable_nfc", true).toBool(); |
| 132 | |||
| 133 | Settings::values.current_user = std::clamp<int>(qt_config->value("current_user", 0).toInt(), 0, | ||
| 134 | Service::Account::MAX_USERS - 1); | ||
| 135 | |||
| 126 | Settings::values.language_index = qt_config->value("language_index", 1).toInt(); | 136 | Settings::values.language_index = qt_config->value("language_index", 1).toInt(); |
| 127 | qt_config->endGroup(); | 137 | qt_config->endGroup(); |
| 128 | 138 | ||
| @@ -258,7 +268,9 @@ void Config::SaveValues() { | |||
| 258 | 268 | ||
| 259 | qt_config->beginGroup("System"); | 269 | qt_config->beginGroup("System"); |
| 260 | qt_config->setValue("use_docked_mode", Settings::values.use_docked_mode); | 270 | qt_config->setValue("use_docked_mode", Settings::values.use_docked_mode); |
| 261 | qt_config->setValue("username", QString::fromStdString(Settings::values.username)); | 271 | qt_config->setValue("enable_nfc", Settings::values.enable_nfc); |
| 272 | qt_config->setValue("current_user", Settings::values.current_user); | ||
| 273 | |||
| 262 | qt_config->setValue("language_index", Settings::values.language_index); | 274 | qt_config->setValue("language_index", Settings::values.language_index); |
| 263 | qt_config->endGroup(); | 275 | qt_config->endGroup(); |
| 264 | 276 | ||
| @@ -335,9 +347,3 @@ void Config::Reload() { | |||
| 335 | void Config::Save() { | 347 | void Config::Save() { |
| 336 | SaveValues(); | 348 | SaveValues(); |
| 337 | } | 349 | } |
| 338 | |||
| 339 | Config::~Config() { | ||
| 340 | Save(); | ||
| 341 | |||
| 342 | delete qt_config; | ||
| 343 | } | ||
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h index cbf745ea2..9c99c1b75 100644 --- a/src/yuzu/configuration/config.h +++ b/src/yuzu/configuration/config.h | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <memory> | ||
| 8 | #include <string> | 9 | #include <string> |
| 9 | #include <QVariant> | 10 | #include <QVariant> |
| 10 | #include "core/settings.h" | 11 | #include "core/settings.h" |
| @@ -12,12 +13,6 @@ | |||
| 12 | class QSettings; | 13 | class QSettings; |
| 13 | 14 | ||
| 14 | class Config { | 15 | class Config { |
| 15 | QSettings* qt_config; | ||
| 16 | std::string qt_config_loc; | ||
| 17 | |||
| 18 | void ReadValues(); | ||
| 19 | void SaveValues(); | ||
| 20 | |||
| 21 | public: | 16 | public: |
| 22 | Config(); | 17 | Config(); |
| 23 | ~Config(); | 18 | ~Config(); |
| @@ -27,4 +22,11 @@ public: | |||
| 27 | 22 | ||
| 28 | static const std::array<int, Settings::NativeButton::NumButtons> default_buttons; | 23 | static const std::array<int, Settings::NativeButton::NumButtons> default_buttons; |
| 29 | static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs; | 24 | static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs; |
| 25 | |||
| 26 | private: | ||
| 27 | void ReadValues(); | ||
| 28 | void SaveValues(); | ||
| 29 | |||
| 30 | std::unique_ptr<QSettings> qt_config; | ||
| 31 | std::string qt_config_loc; | ||
| 30 | }; | 32 | }; |
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp index f5db9e55b..537d6e576 100644 --- a/src/yuzu/configuration/configure_general.cpp +++ b/src/yuzu/configuration/configure_general.cpp | |||
| @@ -31,6 +31,7 @@ void ConfigureGeneral::setConfiguration() { | |||
| 31 | ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme)); | 31 | ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme)); |
| 32 | ui->use_cpu_jit->setChecked(Settings::values.use_cpu_jit); | 32 | ui->use_cpu_jit->setChecked(Settings::values.use_cpu_jit); |
| 33 | ui->use_docked_mode->setChecked(Settings::values.use_docked_mode); | 33 | ui->use_docked_mode->setChecked(Settings::values.use_docked_mode); |
| 34 | ui->enable_nfc->setChecked(Settings::values.enable_nfc); | ||
| 34 | } | 35 | } |
| 35 | 36 | ||
| 36 | void ConfigureGeneral::PopulateHotkeyList(const HotkeyRegistry& registry) { | 37 | void ConfigureGeneral::PopulateHotkeyList(const HotkeyRegistry& registry) { |
| @@ -45,4 +46,5 @@ void ConfigureGeneral::applyConfiguration() { | |||
| 45 | 46 | ||
| 46 | Settings::values.use_cpu_jit = ui->use_cpu_jit->isChecked(); | 47 | Settings::values.use_cpu_jit = ui->use_cpu_jit->isChecked(); |
| 47 | Settings::values.use_docked_mode = ui->use_docked_mode->isChecked(); | 48 | Settings::values.use_docked_mode = ui->use_docked_mode->isChecked(); |
| 49 | Settings::values.enable_nfc = ui->enable_nfc->isChecked(); | ||
| 48 | } | 50 | } |
diff --git a/src/yuzu/configuration/configure_general.ui b/src/yuzu/configuration/configure_general.ui index 1775c4d40..b82fffde8 100644 --- a/src/yuzu/configuration/configure_general.ui +++ b/src/yuzu/configuration/configure_general.ui | |||
| @@ -68,19 +68,26 @@ | |||
| 68 | <property name="title"> | 68 | <property name="title"> |
| 69 | <string>Emulation</string> | 69 | <string>Emulation</string> |
| 70 | </property> | 70 | </property> |
| 71 | <layout class="QHBoxLayout" name="EmulationHorizontalLayout"> | 71 | <layout class="QHBoxLayout" name="EmulationHorizontalLayout"> |
| 72 | <item> | ||
| 73 | <layout class="QVBoxLayout" name="EmulationVerticalLayout"> | ||
| 74 | <item> | ||
| 75 | <widget class="QCheckBox" name="use_docked_mode"> | ||
| 76 | <property name="text"> | ||
| 77 | <string>Enable docked mode</string> | ||
| 78 | </property> | ||
| 79 | </widget> | ||
| 80 | </item> | ||
| 72 | <item> | 81 | <item> |
| 73 | <layout class="QVBoxLayout" name="EmulationVerticalLayout"> | 82 | <widget class="QCheckBox" name="enable_nfc"> |
| 74 | <item> | 83 | <property name="text"> |
| 75 | <widget class="QCheckBox" name="use_docked_mode"> | 84 | <string>Enable NFC</string> |
| 76 | <property name="text"> | 85 | </property> |
| 77 | <string>Enable docked mode</string> | 86 | </widget> |
| 78 | </property> | ||
| 79 | </widget> | ||
| 80 | </item> | ||
| 81 | </layout> | ||
| 82 | </item> | 87 | </item> |
| 83 | </layout> | 88 | </layout> |
| 89 | </item> | ||
| 90 | </layout> | ||
| 84 | </widget> | 91 | </widget> |
| 85 | </item> | 92 | </item> |
| 86 | <item> | 93 | <item> |
diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp index e9ed9c38f..20ffb0a9a 100644 --- a/src/yuzu/configuration/configure_system.cpp +++ b/src/yuzu/configuration/configure_system.cpp | |||
| @@ -2,14 +2,27 @@ | |||
| 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 <algorithm> | ||
| 6 | #include <QFileDialog> | ||
| 7 | #include <QGraphicsItem> | ||
| 8 | #include <QGraphicsScene> | ||
| 9 | #include <QInputDialog> | ||
| 5 | #include <QMessageBox> | 10 | #include <QMessageBox> |
| 11 | #include <QStandardItemModel> | ||
| 12 | #include <QTreeView> | ||
| 13 | #include <QVBoxLayout> | ||
| 14 | #include "common/common_paths.h" | ||
| 15 | #include "common/logging/backend.h" | ||
| 16 | #include "common/string_util.h" | ||
| 6 | #include "core/core.h" | 17 | #include "core/core.h" |
| 18 | #include "core/hle/service/acc/profile_manager.h" | ||
| 7 | #include "core/settings.h" | 19 | #include "core/settings.h" |
| 8 | #include "ui_configure_system.h" | 20 | #include "ui_configure_system.h" |
| 9 | #include "yuzu/configuration/configure_system.h" | 21 | #include "yuzu/configuration/configure_system.h" |
| 10 | #include "yuzu/main.h" | 22 | #include "yuzu/main.h" |
| 11 | 23 | ||
| 12 | static const std::array<int, 12> days_in_month = {{ | 24 | namespace { |
| 25 | constexpr std::array<int, 12> days_in_month = {{ | ||
| 13 | 31, | 26 | 31, |
| 14 | 29, | 27 | 29, |
| 15 | 31, | 28 | 31, |
| @@ -24,13 +37,82 @@ static const std::array<int, 12> days_in_month = {{ | |||
| 24 | 31, | 37 | 31, |
| 25 | }}; | 38 | }}; |
| 26 | 39 | ||
| 27 | ConfigureSystem::ConfigureSystem(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureSystem) { | 40 | // Same backup JPEG used by acc IProfile::GetImage if no jpeg found |
| 41 | constexpr std::array<u8, 107> backup_jpeg{ | ||
| 42 | 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x02, 0x02, | ||
| 43 | 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, 0x08, 0x06, 0x06, 0x05, | ||
| 44 | 0x06, 0x09, 0x08, 0x0a, 0x0a, 0x09, 0x08, 0x09, 0x09, 0x0a, 0x0c, 0x0f, 0x0c, 0x0a, 0x0b, 0x0e, | ||
| 45 | 0x0b, 0x09, 0x09, 0x0d, 0x11, 0x0d, 0x0e, 0x0f, 0x10, 0x10, 0x11, 0x10, 0x0a, 0x0c, 0x12, 0x13, | ||
| 46 | 0x12, 0x10, 0x13, 0x0f, 0x10, 0x10, 0x10, 0xff, 0xc9, 0x00, 0x0b, 0x08, 0x00, 0x01, 0x00, 0x01, | ||
| 47 | 0x01, 0x01, 0x11, 0x00, 0xff, 0xcc, 0x00, 0x06, 0x00, 0x10, 0x10, 0x05, 0xff, 0xda, 0x00, 0x08, | ||
| 48 | 0x01, 0x01, 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9, | ||
| 49 | }; | ||
| 50 | |||
| 51 | std::string GetImagePath(Service::Account::UUID uuid) { | ||
| 52 | return FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + | ||
| 53 | "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg"; | ||
| 54 | } | ||
| 55 | |||
| 56 | std::string GetAccountUsername(const Service::Account::ProfileManager& manager, | ||
| 57 | Service::Account::UUID uuid) { | ||
| 58 | Service::Account::ProfileBase profile; | ||
| 59 | if (!manager.GetProfileBase(uuid, profile)) { | ||
| 60 | return ""; | ||
| 61 | } | ||
| 62 | |||
| 63 | return Common::StringFromFixedZeroTerminatedBuffer( | ||
| 64 | reinterpret_cast<const char*>(profile.username.data()), profile.username.size()); | ||
| 65 | } | ||
| 66 | } // Anonymous namespace | ||
| 67 | |||
| 68 | ConfigureSystem::ConfigureSystem(QWidget* parent) | ||
| 69 | : QWidget(parent), ui(new Ui::ConfigureSystem), | ||
| 70 | profile_manager(std::make_unique<Service::Account::ProfileManager>()) { | ||
| 28 | ui->setupUi(this); | 71 | ui->setupUi(this); |
| 29 | connect(ui->combo_birthmonth, | 72 | connect(ui->combo_birthmonth, |
| 30 | static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, | 73 | static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, |
| 31 | &ConfigureSystem::updateBirthdayComboBox); | 74 | &ConfigureSystem::UpdateBirthdayComboBox); |
| 32 | connect(ui->button_regenerate_console_id, &QPushButton::clicked, this, | 75 | connect(ui->button_regenerate_console_id, &QPushButton::clicked, this, |
| 33 | &ConfigureSystem::refreshConsoleID); | 76 | &ConfigureSystem::RefreshConsoleID); |
| 77 | |||
| 78 | layout = new QVBoxLayout; | ||
| 79 | tree_view = new QTreeView; | ||
| 80 | item_model = new QStandardItemModel(tree_view); | ||
| 81 | tree_view->setModel(item_model); | ||
| 82 | |||
| 83 | tree_view->setAlternatingRowColors(true); | ||
| 84 | tree_view->setSelectionMode(QHeaderView::SingleSelection); | ||
| 85 | tree_view->setSelectionBehavior(QHeaderView::SelectRows); | ||
| 86 | tree_view->setVerticalScrollMode(QHeaderView::ScrollPerPixel); | ||
| 87 | tree_view->setHorizontalScrollMode(QHeaderView::ScrollPerPixel); | ||
| 88 | tree_view->setSortingEnabled(true); | ||
| 89 | tree_view->setEditTriggers(QHeaderView::NoEditTriggers); | ||
| 90 | tree_view->setUniformRowHeights(true); | ||
| 91 | tree_view->setIconSize({64, 64}); | ||
| 92 | tree_view->setContextMenuPolicy(Qt::NoContextMenu); | ||
| 93 | |||
| 94 | item_model->insertColumns(0, 1); | ||
| 95 | item_model->setHeaderData(0, Qt::Horizontal, "Users"); | ||
| 96 | |||
| 97 | // We must register all custom types with the Qt Automoc system so that we are able to use it | ||
| 98 | // with signals/slots. In this case, QList falls under the umbrells of custom types. | ||
| 99 | qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>"); | ||
| 100 | |||
| 101 | layout->setContentsMargins(0, 0, 0, 0); | ||
| 102 | layout->setSpacing(0); | ||
| 103 | layout->addWidget(tree_view); | ||
| 104 | |||
| 105 | ui->scrollArea->setLayout(layout); | ||
| 106 | |||
| 107 | connect(tree_view, &QTreeView::clicked, this, &ConfigureSystem::SelectUser); | ||
| 108 | |||
| 109 | connect(ui->pm_add, &QPushButton::pressed, this, &ConfigureSystem::AddUser); | ||
| 110 | connect(ui->pm_rename, &QPushButton::pressed, this, &ConfigureSystem::RenameUser); | ||
| 111 | connect(ui->pm_remove, &QPushButton::pressed, this, &ConfigureSystem::DeleteUser); | ||
| 112 | connect(ui->pm_set_image, &QPushButton::pressed, this, &ConfigureSystem::SetUserImage); | ||
| 113 | |||
| 114 | scene = new QGraphicsScene; | ||
| 115 | ui->current_user_icon->setScene(scene); | ||
| 34 | 116 | ||
| 35 | this->setConfiguration(); | 117 | this->setConfiguration(); |
| 36 | } | 118 | } |
| @@ -39,8 +121,58 @@ ConfigureSystem::~ConfigureSystem() = default; | |||
| 39 | 121 | ||
| 40 | void ConfigureSystem::setConfiguration() { | 122 | void ConfigureSystem::setConfiguration() { |
| 41 | enabled = !Core::System::GetInstance().IsPoweredOn(); | 123 | enabled = !Core::System::GetInstance().IsPoweredOn(); |
| 42 | ui->edit_username->setText(QString::fromStdString(Settings::values.username)); | 124 | |
| 43 | ui->combo_language->setCurrentIndex(Settings::values.language_index); | 125 | ui->combo_language->setCurrentIndex(Settings::values.language_index); |
| 126 | |||
| 127 | item_model->removeRows(0, item_model->rowCount()); | ||
| 128 | list_items.clear(); | ||
| 129 | |||
| 130 | PopulateUserList(); | ||
| 131 | UpdateCurrentUser(); | ||
| 132 | } | ||
| 133 | |||
| 134 | static QPixmap GetIcon(Service::Account::UUID uuid) { | ||
| 135 | const auto icon_url = QString::fromStdString(GetImagePath(uuid)); | ||
| 136 | QPixmap icon{icon_url}; | ||
| 137 | |||
| 138 | if (!icon) { | ||
| 139 | icon.fill(Qt::black); | ||
| 140 | icon.loadFromData(backup_jpeg.data(), backup_jpeg.size()); | ||
| 141 | } | ||
| 142 | |||
| 143 | return icon; | ||
| 144 | } | ||
| 145 | |||
| 146 | void ConfigureSystem::PopulateUserList() { | ||
| 147 | const auto& profiles = profile_manager->GetAllUsers(); | ||
| 148 | for (const auto& user : profiles) { | ||
| 149 | Service::Account::ProfileBase profile; | ||
| 150 | if (!profile_manager->GetProfileBase(user, profile)) | ||
| 151 | continue; | ||
| 152 | |||
| 153 | const auto username = Common::StringFromFixedZeroTerminatedBuffer( | ||
| 154 | reinterpret_cast<const char*>(profile.username.data()), profile.username.size()); | ||
| 155 | |||
| 156 | list_items.push_back(QList<QStandardItem*>{new QStandardItem{ | ||
| 157 | GetIcon(user).scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), | ||
| 158 | QString::fromStdString(username + '\n' + user.FormatSwitch())}}); | ||
| 159 | } | ||
| 160 | |||
| 161 | for (const auto& item : list_items) | ||
| 162 | item_model->appendRow(item); | ||
| 163 | } | ||
| 164 | |||
| 165 | void ConfigureSystem::UpdateCurrentUser() { | ||
| 166 | ui->pm_add->setEnabled(profile_manager->GetUserCount() < Service::Account::MAX_USERS); | ||
| 167 | |||
| 168 | const auto& current_user = profile_manager->GetUser(Settings::values.current_user); | ||
| 169 | ASSERT(current_user != std::nullopt); | ||
| 170 | const auto username = GetAccountUsername(*profile_manager, *current_user); | ||
| 171 | |||
| 172 | scene->clear(); | ||
| 173 | scene->addPixmap( | ||
| 174 | GetIcon(*current_user).scaled(48, 48, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); | ||
| 175 | ui->current_user_username->setText(QString::fromStdString(username)); | ||
| 44 | } | 176 | } |
| 45 | 177 | ||
| 46 | void ConfigureSystem::ReadSystemSettings() {} | 178 | void ConfigureSystem::ReadSystemSettings() {} |
| @@ -48,12 +180,12 @@ void ConfigureSystem::ReadSystemSettings() {} | |||
| 48 | void ConfigureSystem::applyConfiguration() { | 180 | void ConfigureSystem::applyConfiguration() { |
| 49 | if (!enabled) | 181 | if (!enabled) |
| 50 | return; | 182 | return; |
| 51 | Settings::values.username = ui->edit_username->text().toStdString(); | 183 | |
| 52 | Settings::values.language_index = ui->combo_language->currentIndex(); | 184 | Settings::values.language_index = ui->combo_language->currentIndex(); |
| 53 | Settings::Apply(); | 185 | Settings::Apply(); |
| 54 | } | 186 | } |
| 55 | 187 | ||
| 56 | void ConfigureSystem::updateBirthdayComboBox(int birthmonth_index) { | 188 | void ConfigureSystem::UpdateBirthdayComboBox(int birthmonth_index) { |
| 57 | if (birthmonth_index < 0 || birthmonth_index >= 12) | 189 | if (birthmonth_index < 0 || birthmonth_index >= 12) |
| 58 | return; | 190 | return; |
| 59 | 191 | ||
| @@ -78,7 +210,7 @@ void ConfigureSystem::updateBirthdayComboBox(int birthmonth_index) { | |||
| 78 | ui->combo_birthday->setCurrentIndex(birthday_index); | 210 | ui->combo_birthday->setCurrentIndex(birthday_index); |
| 79 | } | 211 | } |
| 80 | 212 | ||
| 81 | void ConfigureSystem::refreshConsoleID() { | 213 | void ConfigureSystem::RefreshConsoleID() { |
| 82 | QMessageBox::StandardButton reply; | 214 | QMessageBox::StandardButton reply; |
| 83 | QString warning_text = tr("This will replace your current virtual Switch with a new one. " | 215 | QString warning_text = tr("This will replace your current virtual Switch with a new one. " |
| 84 | "Your current virtual Switch will not be recoverable. " | 216 | "Your current virtual Switch will not be recoverable. " |
| @@ -92,3 +224,129 @@ void ConfigureSystem::refreshConsoleID() { | |||
| 92 | ui->label_console_id->setText( | 224 | ui->label_console_id->setText( |
| 93 | tr("Console ID: 0x%1").arg(QString::number(console_id, 16).toUpper())); | 225 | tr("Console ID: 0x%1").arg(QString::number(console_id, 16).toUpper())); |
| 94 | } | 226 | } |
| 227 | |||
| 228 | void ConfigureSystem::SelectUser(const QModelIndex& index) { | ||
| 229 | Settings::values.current_user = | ||
| 230 | std::clamp<std::size_t>(index.row(), 0, profile_manager->GetUserCount() - 1); | ||
| 231 | |||
| 232 | UpdateCurrentUser(); | ||
| 233 | |||
| 234 | ui->pm_remove->setEnabled(profile_manager->GetUserCount() >= 2); | ||
| 235 | ui->pm_rename->setEnabled(true); | ||
| 236 | ui->pm_set_image->setEnabled(true); | ||
| 237 | } | ||
| 238 | |||
| 239 | void ConfigureSystem::AddUser() { | ||
| 240 | const auto uuid = Service::Account::UUID::Generate(); | ||
| 241 | |||
| 242 | bool ok = false; | ||
| 243 | const auto username = | ||
| 244 | QInputDialog::getText(this, tr("Enter Username"), tr("Enter a username for the new user:"), | ||
| 245 | QLineEdit::Normal, QString(), &ok); | ||
| 246 | if (!ok) | ||
| 247 | return; | ||
| 248 | |||
| 249 | profile_manager->CreateNewUser(uuid, username.toStdString()); | ||
| 250 | |||
| 251 | item_model->appendRow(new QStandardItem{ | ||
| 252 | GetIcon(uuid).scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), | ||
| 253 | QString::fromStdString(username.toStdString() + '\n' + uuid.FormatSwitch())}); | ||
| 254 | } | ||
| 255 | |||
| 256 | void ConfigureSystem::RenameUser() { | ||
| 257 | const auto user = tree_view->currentIndex().row(); | ||
| 258 | const auto uuid = profile_manager->GetUser(user); | ||
| 259 | ASSERT(uuid != std::nullopt); | ||
| 260 | const auto username = GetAccountUsername(*profile_manager, *uuid); | ||
| 261 | |||
| 262 | Service::Account::ProfileBase profile; | ||
| 263 | if (!profile_manager->GetProfileBase(*uuid, profile)) | ||
| 264 | return; | ||
| 265 | |||
| 266 | bool ok = false; | ||
| 267 | const auto new_username = | ||
| 268 | QInputDialog::getText(this, tr("Enter Username"), tr("Enter a new username:"), | ||
| 269 | QLineEdit::Normal, QString::fromStdString(username), &ok); | ||
| 270 | |||
| 271 | if (!ok) | ||
| 272 | return; | ||
| 273 | |||
| 274 | std::fill(profile.username.begin(), profile.username.end(), '\0'); | ||
| 275 | const auto username_std = new_username.toStdString(); | ||
| 276 | if (username_std.size() > profile.username.size()) { | ||
| 277 | std::copy_n(username_std.begin(), std::min(profile.username.size(), username_std.size()), | ||
| 278 | profile.username.begin()); | ||
| 279 | } else { | ||
| 280 | std::copy(username_std.begin(), username_std.end(), profile.username.begin()); | ||
| 281 | } | ||
| 282 | |||
| 283 | profile_manager->SetProfileBase(*uuid, profile); | ||
| 284 | |||
| 285 | item_model->setItem( | ||
| 286 | user, 0, | ||
| 287 | new QStandardItem{ | ||
| 288 | GetIcon(*uuid).scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), | ||
| 289 | tr("%1\n%2", "%1 is the profile username, %2 is the formatted UUID (e.g. " | ||
| 290 | "00112233-4455-6677-8899-AABBCCDDEEFF))") | ||
| 291 | .arg(QString::fromStdString(username_std), | ||
| 292 | QString::fromStdString(uuid->FormatSwitch()))}); | ||
| 293 | UpdateCurrentUser(); | ||
| 294 | } | ||
| 295 | |||
| 296 | void ConfigureSystem::DeleteUser() { | ||
| 297 | const auto index = tree_view->currentIndex().row(); | ||
| 298 | const auto uuid = profile_manager->GetUser(index); | ||
| 299 | ASSERT(uuid != std::nullopt); | ||
| 300 | const auto username = GetAccountUsername(*profile_manager, *uuid); | ||
| 301 | |||
| 302 | const auto confirm = | ||
| 303 | QMessageBox::question(this, tr("Confirm Delete"), | ||
| 304 | tr("You are about to delete user with name %1. Are you sure?") | ||
| 305 | .arg(QString::fromStdString(username))); | ||
| 306 | |||
| 307 | if (confirm == QMessageBox::No) | ||
| 308 | return; | ||
| 309 | |||
| 310 | if (Settings::values.current_user == tree_view->currentIndex().row()) | ||
| 311 | Settings::values.current_user = 0; | ||
| 312 | UpdateCurrentUser(); | ||
| 313 | |||
| 314 | if (!profile_manager->RemoveUser(*uuid)) | ||
| 315 | return; | ||
| 316 | |||
| 317 | item_model->removeRows(tree_view->currentIndex().row(), 1); | ||
| 318 | tree_view->clearSelection(); | ||
| 319 | |||
| 320 | ui->pm_remove->setEnabled(false); | ||
| 321 | ui->pm_rename->setEnabled(false); | ||
| 322 | } | ||
| 323 | |||
| 324 | void ConfigureSystem::SetUserImage() { | ||
| 325 | const auto index = tree_view->currentIndex().row(); | ||
| 326 | const auto uuid = profile_manager->GetUser(index); | ||
| 327 | ASSERT(uuid != std::nullopt); | ||
| 328 | const auto username = GetAccountUsername(*profile_manager, *uuid); | ||
| 329 | |||
| 330 | const auto file = QFileDialog::getOpenFileName(this, tr("Select User Image"), QString(), | ||
| 331 | tr("JPEG Images (*.jpg *.jpeg)")); | ||
| 332 | |||
| 333 | if (file.isEmpty()) | ||
| 334 | return; | ||
| 335 | |||
| 336 | FileUtil::Delete(GetImagePath(*uuid)); | ||
| 337 | |||
| 338 | const auto raw_path = | ||
| 339 | FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "/system/save/8000000000000010"; | ||
| 340 | if (FileUtil::Exists(raw_path) && !FileUtil::IsDirectory(raw_path)) | ||
| 341 | FileUtil::Delete(raw_path); | ||
| 342 | |||
| 343 | FileUtil::CreateFullPath(GetImagePath(*uuid)); | ||
| 344 | FileUtil::Copy(file.toStdString(), GetImagePath(*uuid)); | ||
| 345 | |||
| 346 | item_model->setItem( | ||
| 347 | index, 0, | ||
| 348 | new QStandardItem{ | ||
| 349 | GetIcon(*uuid).scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), | ||
| 350 | QString::fromStdString(username + '\n' + uuid->FormatSwitch())}); | ||
| 351 | UpdateCurrentUser(); | ||
| 352 | } | ||
diff --git a/src/yuzu/configuration/configure_system.h b/src/yuzu/configuration/configure_system.h index f13de17d4..07764e1f7 100644 --- a/src/yuzu/configuration/configure_system.h +++ b/src/yuzu/configuration/configure_system.h | |||
| @@ -5,8 +5,20 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <memory> | 7 | #include <memory> |
| 8 | |||
| 9 | #include <QList> | ||
| 8 | #include <QWidget> | 10 | #include <QWidget> |
| 9 | 11 | ||
| 12 | class QGraphicsScene; | ||
| 13 | class QStandardItem; | ||
| 14 | class QStandardItemModel; | ||
| 15 | class QTreeView; | ||
| 16 | class QVBoxLayout; | ||
| 17 | |||
| 18 | namespace Service::Account { | ||
| 19 | class ProfileManager; | ||
| 20 | } | ||
| 21 | |||
| 10 | namespace Ui { | 22 | namespace Ui { |
| 11 | class ConfigureSystem; | 23 | class ConfigureSystem; |
| 12 | } | 24 | } |
| @@ -16,23 +28,39 @@ class ConfigureSystem : public QWidget { | |||
| 16 | 28 | ||
| 17 | public: | 29 | public: |
| 18 | explicit ConfigureSystem(QWidget* parent = nullptr); | 30 | explicit ConfigureSystem(QWidget* parent = nullptr); |
| 19 | ~ConfigureSystem(); | 31 | ~ConfigureSystem() override; |
| 20 | 32 | ||
| 21 | void applyConfiguration(); | 33 | void applyConfiguration(); |
| 22 | void setConfiguration(); | 34 | void setConfiguration(); |
| 23 | 35 | ||
| 24 | public slots: | ||
| 25 | void updateBirthdayComboBox(int birthmonth_index); | ||
| 26 | void refreshConsoleID(); | ||
| 27 | |||
| 28 | private: | 36 | private: |
| 29 | void ReadSystemSettings(); | 37 | void ReadSystemSettings(); |
| 30 | 38 | ||
| 39 | void UpdateBirthdayComboBox(int birthmonth_index); | ||
| 40 | void RefreshConsoleID(); | ||
| 41 | |||
| 42 | void PopulateUserList(); | ||
| 43 | void UpdateCurrentUser(); | ||
| 44 | void SelectUser(const QModelIndex& index); | ||
| 45 | void AddUser(); | ||
| 46 | void RenameUser(); | ||
| 47 | void DeleteUser(); | ||
| 48 | void SetUserImage(); | ||
| 49 | |||
| 50 | QVBoxLayout* layout; | ||
| 51 | QTreeView* tree_view; | ||
| 52 | QStandardItemModel* item_model; | ||
| 53 | QGraphicsScene* scene; | ||
| 54 | |||
| 55 | std::vector<QList<QStandardItem*>> list_items; | ||
| 56 | |||
| 31 | std::unique_ptr<Ui::ConfigureSystem> ui; | 57 | std::unique_ptr<Ui::ConfigureSystem> ui; |
| 32 | bool enabled; | 58 | bool enabled = false; |
| 59 | |||
| 60 | int birthmonth = 0; | ||
| 61 | int birthday = 0; | ||
| 62 | int language_index = 0; | ||
| 63 | int sound_index = 0; | ||
| 33 | 64 | ||
| 34 | std::u16string username; | 65 | std::unique_ptr<Service::Account::ProfileManager> profile_manager; |
| 35 | int birthmonth, birthday; | ||
| 36 | int language_index; | ||
| 37 | int sound_index; | ||
| 38 | }; | 66 | }; |
diff --git a/src/yuzu/configuration/configure_system.ui b/src/yuzu/configuration/configure_system.ui index f3f8db038..020b32a37 100644 --- a/src/yuzu/configuration/configure_system.ui +++ b/src/yuzu/configuration/configure_system.ui | |||
| @@ -7,7 +7,7 @@ | |||
| 7 | <x>0</x> | 7 | <x>0</x> |
| 8 | <y>0</y> | 8 | <y>0</y> |
| 9 | <width>360</width> | 9 | <width>360</width> |
| 10 | <height>377</height> | 10 | <height>483</height> |
| 11 | </rect> | 11 | </rect> |
| 12 | </property> | 12 | </property> |
| 13 | <property name="windowTitle"> | 13 | <property name="windowTitle"> |
| @@ -22,34 +22,28 @@ | |||
| 22 | <string>System Settings</string> | 22 | <string>System Settings</string> |
| 23 | </property> | 23 | </property> |
| 24 | <layout class="QGridLayout" name="gridLayout"> | 24 | <layout class="QGridLayout" name="gridLayout"> |
| 25 | <item row="0" column="0"> | 25 | <item row="1" column="0"> |
| 26 | <widget class="QLabel" name="label_username"> | 26 | <widget class="QLabel" name="label_language"> |
| 27 | <property name="text"> | 27 | <property name="text"> |
| 28 | <string>Username</string> | 28 | <string>Language</string> |
| 29 | </property> | 29 | </property> |
| 30 | </widget> | 30 | </widget> |
| 31 | </item> | 31 | </item> |
| 32 | <item row="0" column="1"> | 32 | <item row="0" column="0"> |
| 33 | <widget class="QLineEdit" name="edit_username"> | 33 | <widget class="QLabel" name="label_birthday"> |
| 34 | <property name="sizePolicy"> | 34 | <property name="text"> |
| 35 | <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> | 35 | <string>Birthday</string> |
| 36 | <horstretch>0</horstretch> | ||
| 37 | <verstretch>0</verstretch> | ||
| 38 | </sizepolicy> | ||
| 39 | </property> | ||
| 40 | <property name="maxLength"> | ||
| 41 | <number>32</number> | ||
| 42 | </property> | 36 | </property> |
| 43 | </widget> | 37 | </widget> |
| 44 | </item> | 38 | </item> |
| 45 | <item row="1" column="0"> | 39 | <item row="3" column="0"> |
| 46 | <widget class="QLabel" name="label_birthday"> | 40 | <widget class="QLabel" name="label_console_id"> |
| 47 | <property name="text"> | 41 | <property name="text"> |
| 48 | <string>Birthday</string> | 42 | <string>Console ID:</string> |
| 49 | </property> | 43 | </property> |
| 50 | </widget> | 44 | </widget> |
| 51 | </item> | 45 | </item> |
| 52 | <item row="1" column="1"> | 46 | <item row="0" column="1"> |
| 53 | <layout class="QHBoxLayout" name="horizontalLayout_birthday2"> | 47 | <layout class="QHBoxLayout" name="horizontalLayout_birthday2"> |
| 54 | <item> | 48 | <item> |
| 55 | <widget class="QComboBox" name="combo_birthmonth"> | 49 | <widget class="QComboBox" name="combo_birthmonth"> |
| @@ -120,14 +114,7 @@ | |||
| 120 | </item> | 114 | </item> |
| 121 | </layout> | 115 | </layout> |
| 122 | </item> | 116 | </item> |
| 123 | <item row="2" column="0"> | 117 | <item row="1" column="1"> |
| 124 | <widget class="QLabel" name="label_language"> | ||
| 125 | <property name="text"> | ||
| 126 | <string>Language</string> | ||
| 127 | </property> | ||
| 128 | </widget> | ||
| 129 | </item> | ||
| 130 | <item row="2" column="1"> | ||
| 131 | <widget class="QComboBox" name="combo_language"> | 118 | <widget class="QComboBox" name="combo_language"> |
| 132 | <property name="toolTip"> | 119 | <property name="toolTip"> |
| 133 | <string>Note: this can be overridden when region setting is auto-select</string> | 120 | <string>Note: this can be overridden when region setting is auto-select</string> |
| @@ -187,31 +174,31 @@ | |||
| 187 | <string>Russian (Русский)</string> | 174 | <string>Russian (Русский)</string> |
| 188 | </property> | 175 | </property> |
| 189 | </item> | 176 | </item> |
| 190 | <item> | 177 | <item> |
| 191 | <property name="text"> | 178 | <property name="text"> |
| 192 | <string>Taiwanese</string> | 179 | <string>Taiwanese</string> |
| 193 | </property> | 180 | </property> |
| 194 | </item> | 181 | </item> |
| 195 | <item> | 182 | <item> |
| 196 | <property name="text"> | 183 | <property name="text"> |
| 197 | <string>British English</string> | 184 | <string>British English</string> |
| 198 | </property> | 185 | </property> |
| 199 | </item> | 186 | </item> |
| 200 | <item> | 187 | <item> |
| 201 | <property name="text"> | 188 | <property name="text"> |
| 202 | <string>Canadian French</string> | 189 | <string>Canadian French</string> |
| 203 | </property> | 190 | </property> |
| 204 | </item> | 191 | </item> |
| 205 | <item> | 192 | <item> |
| 206 | <property name="text"> | 193 | <property name="text"> |
| 207 | <string>Latin American Spanish</string> | 194 | <string>Latin American Spanish</string> |
| 208 | </property> | 195 | </property> |
| 209 | </item> | 196 | </item> |
| 210 | <item> | 197 | <item> |
| 211 | <property name="text"> | 198 | <property name="text"> |
| 212 | <string>Simplified Chinese</string> | 199 | <string>Simplified Chinese</string> |
| 213 | </property> | 200 | </property> |
| 214 | </item> | 201 | </item> |
| 215 | <item> | 202 | <item> |
| 216 | <property name="text"> | 203 | <property name="text"> |
| 217 | <string>Traditional Chinese (正體中文)</string> | 204 | <string>Traditional Chinese (正體中文)</string> |
| @@ -219,14 +206,14 @@ | |||
| 219 | </item> | 206 | </item> |
| 220 | </widget> | 207 | </widget> |
| 221 | </item> | 208 | </item> |
| 222 | <item row="3" column="0"> | 209 | <item row="2" column="0"> |
| 223 | <widget class="QLabel" name="label_sound"> | 210 | <widget class="QLabel" name="label_sound"> |
| 224 | <property name="text"> | 211 | <property name="text"> |
| 225 | <string>Sound output mode</string> | 212 | <string>Sound output mode</string> |
| 226 | </property> | 213 | </property> |
| 227 | </widget> | 214 | </widget> |
| 228 | </item> | 215 | </item> |
| 229 | <item row="3" column="1"> | 216 | <item row="2" column="1"> |
| 230 | <widget class="QComboBox" name="combo_sound"> | 217 | <widget class="QComboBox" name="combo_sound"> |
| 231 | <item> | 218 | <item> |
| 232 | <property name="text"> | 219 | <property name="text"> |
| @@ -245,14 +232,7 @@ | |||
| 245 | </item> | 232 | </item> |
| 246 | </widget> | 233 | </widget> |
| 247 | </item> | 234 | </item> |
| 248 | <item row="4" column="0"> | 235 | <item row="3" column="1"> |
| 249 | <widget class="QLabel" name="label_console_id"> | ||
| 250 | <property name="text"> | ||
| 251 | <string>Console ID:</string> | ||
| 252 | </property> | ||
| 253 | </widget> | ||
| 254 | </item> | ||
| 255 | <item row="4" column="1"> | ||
| 256 | <widget class="QPushButton" name="button_regenerate_console_id"> | 236 | <widget class="QPushButton" name="button_regenerate_console_id"> |
| 257 | <property name="sizePolicy"> | 237 | <property name="sizePolicy"> |
| 258 | <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> | 238 | <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> |
| @@ -272,6 +252,143 @@ | |||
| 272 | </widget> | 252 | </widget> |
| 273 | </item> | 253 | </item> |
| 274 | <item> | 254 | <item> |
| 255 | <widget class="QGroupBox" name="gridGroupBox"> | ||
| 256 | <property name="title"> | ||
| 257 | <string>Profile Manager</string> | ||
| 258 | </property> | ||
| 259 | <layout class="QGridLayout" name="gridLayout_2"> | ||
| 260 | <property name="sizeConstraint"> | ||
| 261 | <enum>QLayout::SetNoConstraint</enum> | ||
| 262 | </property> | ||
| 263 | <item row="0" column="0"> | ||
| 264 | <layout class="QHBoxLayout" name="horizontalLayout_2"> | ||
| 265 | <item> | ||
| 266 | <widget class="QLabel" name="label"> | ||
| 267 | <property name="sizePolicy"> | ||
| 268 | <sizepolicy hsizetype="Preferred" vsizetype="Minimum"> | ||
| 269 | <horstretch>0</horstretch> | ||
| 270 | <verstretch>0</verstretch> | ||
| 271 | </sizepolicy> | ||
| 272 | </property> | ||
| 273 | <property name="text"> | ||
| 274 | <string>Current User</string> | ||
| 275 | </property> | ||
| 276 | </widget> | ||
| 277 | </item> | ||
| 278 | <item> | ||
| 279 | <widget class="QGraphicsView" name="current_user_icon"> | ||
| 280 | <property name="minimumSize"> | ||
| 281 | <size> | ||
| 282 | <width>48</width> | ||
| 283 | <height>48</height> | ||
| 284 | </size> | ||
| 285 | </property> | ||
| 286 | <property name="maximumSize"> | ||
| 287 | <size> | ||
| 288 | <width>48</width> | ||
| 289 | <height>48</height> | ||
| 290 | </size> | ||
| 291 | </property> | ||
| 292 | <property name="verticalScrollBarPolicy"> | ||
| 293 | <enum>Qt::ScrollBarAlwaysOff</enum> | ||
| 294 | </property> | ||
| 295 | <property name="horizontalScrollBarPolicy"> | ||
| 296 | <enum>Qt::ScrollBarAlwaysOff</enum> | ||
| 297 | </property> | ||
| 298 | <property name="interactive"> | ||
| 299 | <bool>false</bool> | ||
| 300 | </property> | ||
| 301 | </widget> | ||
| 302 | </item> | ||
| 303 | <item> | ||
| 304 | <widget class="QLabel" name="current_user_username"> | ||
| 305 | <property name="sizePolicy"> | ||
| 306 | <sizepolicy hsizetype="Preferred" vsizetype="Minimum"> | ||
| 307 | <horstretch>0</horstretch> | ||
| 308 | <verstretch>0</verstretch> | ||
| 309 | </sizepolicy> | ||
| 310 | </property> | ||
| 311 | <property name="text"> | ||
| 312 | <string>Username</string> | ||
| 313 | </property> | ||
| 314 | </widget> | ||
| 315 | </item> | ||
| 316 | </layout> | ||
| 317 | </item> | ||
| 318 | <item row="1" column="0"> | ||
| 319 | <widget class="QScrollArea" name="scrollArea"> | ||
| 320 | <property name="sizePolicy"> | ||
| 321 | <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> | ||
| 322 | <horstretch>0</horstretch> | ||
| 323 | <verstretch>0</verstretch> | ||
| 324 | </sizepolicy> | ||
| 325 | </property> | ||
| 326 | <property name="frameShape"> | ||
| 327 | <enum>QFrame::StyledPanel</enum> | ||
| 328 | </property> | ||
| 329 | <property name="widgetResizable"> | ||
| 330 | <bool>false</bool> | ||
| 331 | </property> | ||
| 332 | </widget> | ||
| 333 | </item> | ||
| 334 | <item row="2" column="0"> | ||
| 335 | <layout class="QHBoxLayout" name="horizontalLayout_3"> | ||
| 336 | <item> | ||
| 337 | <widget class="QPushButton" name="pm_set_image"> | ||
| 338 | <property name="enabled"> | ||
| 339 | <bool>false</bool> | ||
| 340 | </property> | ||
| 341 | <property name="text"> | ||
| 342 | <string>Set Image</string> | ||
| 343 | </property> | ||
| 344 | </widget> | ||
| 345 | </item> | ||
| 346 | <item> | ||
| 347 | <spacer name="horizontalSpacer"> | ||
| 348 | <property name="orientation"> | ||
| 349 | <enum>Qt::Horizontal</enum> | ||
| 350 | </property> | ||
| 351 | <property name="sizeHint" stdset="0"> | ||
| 352 | <size> | ||
| 353 | <width>40</width> | ||
| 354 | <height>20</height> | ||
| 355 | </size> | ||
| 356 | </property> | ||
| 357 | </spacer> | ||
| 358 | </item> | ||
| 359 | <item> | ||
| 360 | <widget class="QPushButton" name="pm_add"> | ||
| 361 | <property name="text"> | ||
| 362 | <string>Add</string> | ||
| 363 | </property> | ||
| 364 | </widget> | ||
| 365 | </item> | ||
| 366 | <item> | ||
| 367 | <widget class="QPushButton" name="pm_rename"> | ||
| 368 | <property name="enabled"> | ||
| 369 | <bool>false</bool> | ||
| 370 | </property> | ||
| 371 | <property name="text"> | ||
| 372 | <string>Rename</string> | ||
| 373 | </property> | ||
| 374 | </widget> | ||
| 375 | </item> | ||
| 376 | <item> | ||
| 377 | <widget class="QPushButton" name="pm_remove"> | ||
| 378 | <property name="enabled"> | ||
| 379 | <bool>false</bool> | ||
| 380 | </property> | ||
| 381 | <property name="text"> | ||
| 382 | <string>Remove</string> | ||
| 383 | </property> | ||
| 384 | </widget> | ||
| 385 | </item> | ||
| 386 | </layout> | ||
| 387 | </item> | ||
| 388 | </layout> | ||
| 389 | </widget> | ||
| 390 | </item> | ||
| 391 | <item> | ||
| 275 | <widget class="QLabel" name="label_disable_info"> | 392 | <widget class="QLabel" name="label_disable_info"> |
| 276 | <property name="text"> | 393 | <property name="text"> |
| 277 | <string>System settings are available only when game is not running.</string> | 394 | <string>System settings are available only when game is not running.</string> |
| @@ -281,19 +398,6 @@ | |||
| 281 | </property> | 398 | </property> |
| 282 | </widget> | 399 | </widget> |
| 283 | </item> | 400 | </item> |
| 284 | <item> | ||
| 285 | <spacer name="verticalSpacer"> | ||
| 286 | <property name="orientation"> | ||
| 287 | <enum>Qt::Vertical</enum> | ||
| 288 | </property> | ||
| 289 | <property name="sizeHint" stdset="0"> | ||
| 290 | <size> | ||
| 291 | <width>20</width> | ||
| 292 | <height>40</height> | ||
| 293 | </size> | ||
| 294 | </property> | ||
| 295 | </spacer> | ||
| 296 | </item> | ||
| 297 | </layout> | 401 | </layout> |
| 298 | </item> | 402 | </item> |
| 299 | </layout> | 403 | </layout> |
diff --git a/src/yuzu/debugger/graphics/graphics_breakpoints.cpp b/src/yuzu/debugger/graphics/graphics_breakpoints.cpp index b5c88f944..67ed0ba6d 100644 --- a/src/yuzu/debugger/graphics/graphics_breakpoints.cpp +++ b/src/yuzu/debugger/graphics/graphics_breakpoints.cpp | |||
| @@ -2,7 +2,6 @@ | |||
| 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 <map> | ||
| 6 | #include <QLabel> | 5 | #include <QLabel> |
| 7 | #include <QMetaType> | 6 | #include <QMetaType> |
| 8 | #include <QPushButton> | 7 | #include <QPushButton> |
| @@ -32,21 +31,8 @@ QVariant BreakPointModel::data(const QModelIndex& index, int role) const { | |||
| 32 | switch (role) { | 31 | switch (role) { |
| 33 | case Qt::DisplayRole: { | 32 | case Qt::DisplayRole: { |
| 34 | if (index.column() == 0) { | 33 | if (index.column() == 0) { |
| 35 | static const std::map<Tegra::DebugContext::Event, QString> map = { | 34 | return DebugContextEventToString(event); |
| 36 | {Tegra::DebugContext::Event::MaxwellCommandLoaded, tr("Maxwell command loaded")}, | ||
| 37 | {Tegra::DebugContext::Event::MaxwellCommandProcessed, | ||
| 38 | tr("Maxwell command processed")}, | ||
| 39 | {Tegra::DebugContext::Event::IncomingPrimitiveBatch, | ||
| 40 | tr("Incoming primitive batch")}, | ||
| 41 | {Tegra::DebugContext::Event::FinishedPrimitiveBatch, | ||
| 42 | tr("Finished primitive batch")}, | ||
| 43 | }; | ||
| 44 | |||
| 45 | DEBUG_ASSERT(map.size() == | ||
| 46 | static_cast<std::size_t>(Tegra::DebugContext::Event::NumEvents)); | ||
| 47 | return (map.find(event) != map.end()) ? map.at(event) : QString(); | ||
| 48 | } | 35 | } |
| 49 | |||
| 50 | break; | 36 | break; |
| 51 | } | 37 | } |
| 52 | 38 | ||
| @@ -128,6 +114,23 @@ void BreakPointModel::OnResumed() { | |||
| 128 | active_breakpoint = context->active_breakpoint; | 114 | active_breakpoint = context->active_breakpoint; |
| 129 | } | 115 | } |
| 130 | 116 | ||
| 117 | QString BreakPointModel::DebugContextEventToString(Tegra::DebugContext::Event event) { | ||
| 118 | switch (event) { | ||
| 119 | case Tegra::DebugContext::Event::MaxwellCommandLoaded: | ||
| 120 | return tr("Maxwell command loaded"); | ||
| 121 | case Tegra::DebugContext::Event::MaxwellCommandProcessed: | ||
| 122 | return tr("Maxwell command processed"); | ||
| 123 | case Tegra::DebugContext::Event::IncomingPrimitiveBatch: | ||
| 124 | return tr("Incoming primitive batch"); | ||
| 125 | case Tegra::DebugContext::Event::FinishedPrimitiveBatch: | ||
| 126 | return tr("Finished primitive batch"); | ||
| 127 | case Tegra::DebugContext::Event::NumEvents: | ||
| 128 | break; | ||
| 129 | } | ||
| 130 | |||
| 131 | return tr("Unknown debug context event"); | ||
| 132 | } | ||
| 133 | |||
| 131 | GraphicsBreakPointsWidget::GraphicsBreakPointsWidget( | 134 | GraphicsBreakPointsWidget::GraphicsBreakPointsWidget( |
| 132 | std::shared_ptr<Tegra::DebugContext> debug_context, QWidget* parent) | 135 | std::shared_ptr<Tegra::DebugContext> debug_context, QWidget* parent) |
| 133 | : QDockWidget(tr("Maxwell Breakpoints"), parent), Tegra::DebugContext::BreakPointObserver( | 136 | : QDockWidget(tr("Maxwell Breakpoints"), parent), Tegra::DebugContext::BreakPointObserver( |
diff --git a/src/yuzu/debugger/graphics/graphics_breakpoints_p.h b/src/yuzu/debugger/graphics/graphics_breakpoints_p.h index 7112b87e6..fb488e38f 100644 --- a/src/yuzu/debugger/graphics/graphics_breakpoints_p.h +++ b/src/yuzu/debugger/graphics/graphics_breakpoints_p.h | |||
| @@ -29,6 +29,8 @@ public: | |||
| 29 | void OnResumed(); | 29 | void OnResumed(); |
| 30 | 30 | ||
| 31 | private: | 31 | private: |
| 32 | static QString DebugContextEventToString(Tegra::DebugContext::Event event); | ||
| 33 | |||
| 32 | std::weak_ptr<Tegra::DebugContext> context_weak; | 34 | std::weak_ptr<Tegra::DebugContext> context_weak; |
| 33 | bool at_breakpoint; | 35 | bool at_breakpoint; |
| 34 | Tegra::DebugContext::Event active_breakpoint; | 36 | Tegra::DebugContext::Event active_breakpoint; |
diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp index 7403e9ccd..0c831c9f4 100644 --- a/src/yuzu/debugger/wait_tree.cpp +++ b/src/yuzu/debugger/wait_tree.cpp | |||
| @@ -9,8 +9,8 @@ | |||
| 9 | #include "core/core.h" | 9 | #include "core/core.h" |
| 10 | #include "core/hle/kernel/event.h" | 10 | #include "core/hle/kernel/event.h" |
| 11 | #include "core/hle/kernel/handle_table.h" | 11 | #include "core/hle/kernel/handle_table.h" |
| 12 | #include "core/hle/kernel/kernel.h" | ||
| 13 | #include "core/hle/kernel/mutex.h" | 12 | #include "core/hle/kernel/mutex.h" |
| 13 | #include "core/hle/kernel/process.h" | ||
| 14 | #include "core/hle/kernel/scheduler.h" | 14 | #include "core/hle/kernel/scheduler.h" |
| 15 | #include "core/hle/kernel/thread.h" | 15 | #include "core/hle/kernel/thread.h" |
| 16 | #include "core/hle/kernel/timer.h" | 16 | #include "core/hle/kernel/timer.h" |
| @@ -83,7 +83,7 @@ QString WaitTreeText::GetText() const { | |||
| 83 | } | 83 | } |
| 84 | 84 | ||
| 85 | WaitTreeMutexInfo::WaitTreeMutexInfo(VAddr mutex_address) : mutex_address(mutex_address) { | 85 | WaitTreeMutexInfo::WaitTreeMutexInfo(VAddr mutex_address) : mutex_address(mutex_address) { |
| 86 | auto& handle_table = Core::System::GetInstance().Kernel().HandleTable(); | 86 | const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); |
| 87 | 87 | ||
| 88 | mutex_value = Memory::Read32(mutex_address); | 88 | mutex_value = Memory::Read32(mutex_address); |
| 89 | owner_handle = static_cast<Kernel::Handle>(mutex_value & Kernel::Mutex::MutexOwnerMask); | 89 | owner_handle = static_cast<Kernel::Handle>(mutex_value & Kernel::Mutex::MutexOwnerMask); |
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index 67890455a..a5a4aa432 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp | |||
| @@ -16,7 +16,6 @@ | |||
| 16 | #include <fmt/format.h> | 16 | #include <fmt/format.h> |
| 17 | #include "common/common_paths.h" | 17 | #include "common/common_paths.h" |
| 18 | #include "common/common_types.h" | 18 | #include "common/common_types.h" |
| 19 | #include "common/file_util.h" | ||
| 20 | #include "common/logging/log.h" | 19 | #include "common/logging/log.h" |
| 21 | #include "core/file_sys/patch_manager.h" | 20 | #include "core/file_sys/patch_manager.h" |
| 22 | #include "yuzu/compatibility_list.h" | 21 | #include "yuzu/compatibility_list.h" |
| @@ -217,11 +216,11 @@ GameList::GameList(FileSys::VirtualFilesystem vfs, GMainWindow* parent) | |||
| 217 | tree_view->setContextMenuPolicy(Qt::CustomContextMenu); | 216 | tree_view->setContextMenuPolicy(Qt::CustomContextMenu); |
| 218 | 217 | ||
| 219 | item_model->insertColumns(0, COLUMN_COUNT); | 218 | item_model->insertColumns(0, COLUMN_COUNT); |
| 220 | item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, "Name"); | 219 | item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, tr("Name")); |
| 221 | item_model->setHeaderData(COLUMN_COMPATIBILITY, Qt::Horizontal, "Compatibility"); | 220 | item_model->setHeaderData(COLUMN_COMPATIBILITY, Qt::Horizontal, tr("Compatibility")); |
| 222 | item_model->setHeaderData(COLUMN_ADD_ONS, Qt::Horizontal, "Add-ons"); | 221 | item_model->setHeaderData(COLUMN_ADD_ONS, Qt::Horizontal, tr("Add-ons")); |
| 223 | item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, "File type"); | 222 | item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, tr("File type")); |
| 224 | item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, "Size"); | 223 | item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, tr("Size")); |
| 225 | 224 | ||
| 226 | connect(tree_view, &QTreeView::activated, this, &GameList::ValidateEntry); | 225 | connect(tree_view, &QTreeView::activated, this, &GameList::ValidateEntry); |
| 227 | connect(tree_view, &QTreeView::customContextMenuRequested, this, &GameList::PopupContextMenu); | 226 | connect(tree_view, &QTreeView::customContextMenuRequested, this, &GameList::PopupContextMenu); |
| @@ -387,9 +386,9 @@ void GameList::LoadCompatibilityList() { | |||
| 387 | } | 386 | } |
| 388 | 387 | ||
| 389 | void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) { | 388 | void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) { |
| 390 | if (!FileUtil::Exists(dir_path.toStdString()) || | 389 | const QFileInfo dir_info{dir_path}; |
| 391 | !FileUtil::IsDirectory(dir_path.toStdString())) { | 390 | if (!dir_info.exists() || !dir_info.isDir()) { |
| 392 | LOG_ERROR(Frontend, "Could not find game list folder at {}", dir_path.toLocal8Bit().data()); | 391 | LOG_ERROR(Frontend, "Could not find game list folder at {}", dir_path.toStdString()); |
| 393 | search_field->setFilterResult(0, 0); | 392 | search_field->setFilterResult(0, 0); |
| 394 | return; | 393 | return; |
| 395 | } | 394 | } |
diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp index 3881aba5f..3d865a12d 100644 --- a/src/yuzu/game_list_worker.cpp +++ b/src/yuzu/game_list_worker.cpp | |||
| @@ -62,19 +62,24 @@ QString FormatPatchNameVersions(const FileSys::PatchManager& patch_manager, | |||
| 62 | FileSys::VirtualFile update_raw; | 62 | FileSys::VirtualFile update_raw; |
| 63 | loader.ReadUpdateRaw(update_raw); | 63 | loader.ReadUpdateRaw(update_raw); |
| 64 | for (const auto& kv : patch_manager.GetPatchVersionNames(update_raw)) { | 64 | for (const auto& kv : patch_manager.GetPatchVersionNames(update_raw)) { |
| 65 | if (!updatable && kv.first == "Update") | 65 | const bool is_update = kv.first == "Update"; |
| 66 | if (!updatable && is_update) { | ||
| 66 | continue; | 67 | continue; |
| 68 | } | ||
| 69 | |||
| 70 | const QString type = QString::fromStdString(kv.first); | ||
| 67 | 71 | ||
| 68 | if (kv.second.empty()) { | 72 | if (kv.second.empty()) { |
| 69 | out.append(fmt::format("{}\n", kv.first).c_str()); | 73 | out.append(QStringLiteral("%1\n").arg(type)); |
| 70 | } else { | 74 | } else { |
| 71 | auto ver = kv.second; | 75 | auto ver = kv.second; |
| 72 | 76 | ||
| 73 | // Display container name for packed updates | 77 | // Display container name for packed updates |
| 74 | if (ver == "PACKED" && kv.first == "Update") | 78 | if (is_update && ver == "PACKED") { |
| 75 | ver = Loader::GetFileTypeString(loader.GetFileType()); | 79 | ver = Loader::GetFileTypeString(loader.GetFileType()); |
| 80 | } | ||
| 76 | 81 | ||
| 77 | out.append(fmt::format("{} ({})\n", kv.first, ver).c_str()); | 82 | out.append(QStringLiteral("%1 (%2)\n").arg(type, QString::fromStdString(ver))); |
| 78 | } | 83 | } |
| 79 | } | 84 | } |
| 80 | 85 | ||
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index bef9df00d..b5bfa6741 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -10,6 +10,7 @@ | |||
| 10 | // VFS includes must be before glad as they will conflict with Windows file api, which uses defines. | 10 | // VFS includes must be before glad as they will conflict with Windows file api, which uses defines. |
| 11 | #include "core/file_sys/vfs.h" | 11 | #include "core/file_sys/vfs.h" |
| 12 | #include "core/file_sys/vfs_real.h" | 12 | #include "core/file_sys/vfs_real.h" |
| 13 | #include "core/hle/service/acc/profile_manager.h" | ||
| 13 | 14 | ||
| 14 | // These are wrappers to avoid the calls to CreateDirectory and CreateFile becuase of the Windows | 15 | // These are wrappers to avoid the calls to CreateDirectory and CreateFile becuase of the Windows |
| 15 | // defines. | 16 | // defines. |
| @@ -29,6 +30,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual | |||
| 29 | #define QT_NO_OPENGL | 30 | #define QT_NO_OPENGL |
| 30 | #include <QDesktopWidget> | 31 | #include <QDesktopWidget> |
| 31 | #include <QDialogButtonBox> | 32 | #include <QDialogButtonBox> |
| 33 | #include <QFile> | ||
| 32 | #include <QFileDialog> | 34 | #include <QFileDialog> |
| 33 | #include <QMessageBox> | 35 | #include <QMessageBox> |
| 34 | #include <QtConcurrent/QtConcurrent> | 36 | #include <QtConcurrent/QtConcurrent> |
| @@ -60,6 +62,8 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual | |||
| 60 | #include "core/hle/kernel/process.h" | 62 | #include "core/hle/kernel/process.h" |
| 61 | #include "core/hle/service/filesystem/filesystem.h" | 63 | #include "core/hle/service/filesystem/filesystem.h" |
| 62 | #include "core/hle/service/filesystem/fsp_ldr.h" | 64 | #include "core/hle/service/filesystem/fsp_ldr.h" |
| 65 | #include "core/hle/service/nfp/nfp.h" | ||
| 66 | #include "core/hle/service/sm/sm.h" | ||
| 63 | #include "core/loader/loader.h" | 67 | #include "core/loader/loader.h" |
| 64 | #include "core/perf_stats.h" | 68 | #include "core/perf_stats.h" |
| 65 | #include "core/settings.h" | 69 | #include "core/settings.h" |
| @@ -100,6 +104,8 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; | |||
| 100 | } | 104 | } |
| 101 | #endif | 105 | #endif |
| 102 | 106 | ||
| 107 | constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000; | ||
| 108 | |||
| 103 | /** | 109 | /** |
| 104 | * "Callouts" are one-time instructional messages shown to the user. In the config settings, there | 110 | * "Callouts" are one-time instructional messages shown to the user. In the config settings, there |
| 105 | * is a bitfield "callout_flags" options, used to track if a message has already been shown to the | 111 | * is a bitfield "callout_flags" options, used to track if a message has already been shown to the |
| @@ -422,6 +428,7 @@ void GMainWindow::ConnectMenuEvents() { | |||
| 422 | connect(ui.action_Select_SDMC_Directory, &QAction::triggered, this, | 428 | connect(ui.action_Select_SDMC_Directory, &QAction::triggered, this, |
| 423 | [this] { OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget::SDMC); }); | 429 | [this] { OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget::SDMC); }); |
| 424 | connect(ui.action_Exit, &QAction::triggered, this, &QMainWindow::close); | 430 | connect(ui.action_Exit, &QAction::triggered, this, &QMainWindow::close); |
| 431 | connect(ui.action_Load_Amiibo, &QAction::triggered, this, &GMainWindow::OnLoadAmiibo); | ||
| 425 | 432 | ||
| 426 | // Emulation | 433 | // Emulation |
| 427 | connect(ui.action_Start, &QAction::triggered, this, &GMainWindow::OnStartGame); | 434 | connect(ui.action_Start, &QAction::triggered, this, &GMainWindow::OnStartGame); |
| @@ -690,6 +697,7 @@ void GMainWindow::ShutdownGame() { | |||
| 690 | ui.action_Stop->setEnabled(false); | 697 | ui.action_Stop->setEnabled(false); |
| 691 | ui.action_Restart->setEnabled(false); | 698 | ui.action_Restart->setEnabled(false); |
| 692 | ui.action_Report_Compatibility->setEnabled(false); | 699 | ui.action_Report_Compatibility->setEnabled(false); |
| 700 | ui.action_Load_Amiibo->setEnabled(false); | ||
| 693 | render_window->hide(); | 701 | render_window->hide(); |
| 694 | game_list->show(); | 702 | game_list->show(); |
| 695 | game_list->setFilterFocus(); | 703 | game_list->setFilterFocus(); |
| @@ -751,12 +759,43 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target | |||
| 751 | open_target = "Save Data"; | 759 | open_target = "Save Data"; |
| 752 | const std::string nand_dir = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir); | 760 | const std::string nand_dir = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir); |
| 753 | ASSERT(program_id != 0); | 761 | ASSERT(program_id != 0); |
| 754 | // TODO(tech4me): Update this to work with arbitrary user profile | 762 | |
| 755 | // Refer to core/hle/service/acc/profile_manager.cpp ProfileManager constructor | 763 | Service::Account::ProfileManager manager{}; |
| 756 | constexpr u128 user_id = {1, 0}; | 764 | const auto user_ids = manager.GetAllUsers(); |
| 765 | QStringList list; | ||
| 766 | for (const auto& user_id : user_ids) { | ||
| 767 | if (user_id == Service::Account::UUID{}) | ||
| 768 | continue; | ||
| 769 | Service::Account::ProfileBase base; | ||
| 770 | if (!manager.GetProfileBase(user_id, base)) | ||
| 771 | continue; | ||
| 772 | |||
| 773 | list.push_back(QString::fromStdString(Common::StringFromFixedZeroTerminatedBuffer( | ||
| 774 | reinterpret_cast<const char*>(base.username.data()), base.username.size()))); | ||
| 775 | } | ||
| 776 | |||
| 777 | bool ok = false; | ||
| 778 | const auto index_string = | ||
| 779 | QInputDialog::getItem(this, tr("Select User"), | ||
| 780 | tr("Please select the user's save data you would like to open."), | ||
| 781 | list, Settings::values.current_user, false, &ok); | ||
| 782 | if (!ok) | ||
| 783 | return; | ||
| 784 | |||
| 785 | const auto index = list.indexOf(index_string); | ||
| 786 | ASSERT(index != -1 && index < 8); | ||
| 787 | |||
| 788 | const auto user_id = manager.GetUser(index); | ||
| 789 | ASSERT(user_id != std::nullopt); | ||
| 757 | path = nand_dir + FileSys::SaveDataFactory::GetFullPath(FileSys::SaveDataSpaceId::NandUser, | 790 | path = nand_dir + FileSys::SaveDataFactory::GetFullPath(FileSys::SaveDataSpaceId::NandUser, |
| 758 | FileSys::SaveDataType::SaveData, | 791 | FileSys::SaveDataType::SaveData, |
| 759 | program_id, user_id, 0); | 792 | program_id, user_id->uuid, 0); |
| 793 | |||
| 794 | if (!FileUtil::Exists(path)) { | ||
| 795 | FileUtil::CreateFullPath(path); | ||
| 796 | FileUtil::CreateDir(path); | ||
| 797 | } | ||
| 798 | |||
| 760 | break; | 799 | break; |
| 761 | } | 800 | } |
| 762 | case GameListOpenTarget::ModData: { | 801 | case GameListOpenTarget::ModData: { |
| @@ -823,14 +862,10 @@ static bool RomFSRawCopy(QProgressDialog& dialog, const FileSys::VirtualDir& src | |||
| 823 | } | 862 | } |
| 824 | 863 | ||
| 825 | void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_path) { | 864 | void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_path) { |
| 826 | const auto path = fmt::format("{}{:016X}/romfs", | 865 | const auto failed = [this] { |
| 827 | FileUtil::GetUserPath(FileUtil::UserPath::DumpDir), program_id); | ||
| 828 | |||
| 829 | const auto failed = [this, &path] { | ||
| 830 | QMessageBox::warning(this, tr("RomFS Extraction Failed!"), | 866 | QMessageBox::warning(this, tr("RomFS Extraction Failed!"), |
| 831 | tr("There was an error copying the RomFS files or the user " | 867 | tr("There was an error copying the RomFS files or the user " |
| 832 | "cancelled the operation.")); | 868 | "cancelled the operation.")); |
| 833 | vfs->DeleteDirectory(path); | ||
| 834 | }; | 869 | }; |
| 835 | 870 | ||
| 836 | const auto loader = Loader::GetLoader(vfs->OpenFile(game_path, FileSys::Mode::Read)); | 871 | const auto loader = Loader::GetLoader(vfs->OpenFile(game_path, FileSys::Mode::Read)); |
| @@ -845,10 +880,24 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa | |||
| 845 | return; | 880 | return; |
| 846 | } | 881 | } |
| 847 | 882 | ||
| 848 | const auto romfs = | 883 | const auto installed = Service::FileSystem::GetUnionContents(); |
| 849 | loader->IsRomFSUpdatable() | 884 | auto romfs_title_id = SelectRomFSDumpTarget(*installed, program_id); |
| 850 | ? FileSys::PatchManager(program_id).PatchRomFS(file, loader->ReadRomFSIVFCOffset()) | 885 | |
| 851 | : file; | 886 | if (!romfs_title_id) { |
| 887 | failed(); | ||
| 888 | return; | ||
| 889 | } | ||
| 890 | |||
| 891 | const auto path = fmt::format( | ||
| 892 | "{}{:016X}/romfs", FileUtil::GetUserPath(FileUtil::UserPath::DumpDir), *romfs_title_id); | ||
| 893 | |||
| 894 | FileSys::VirtualFile romfs; | ||
| 895 | |||
| 896 | if (*romfs_title_id == program_id) { | ||
| 897 | romfs = file; | ||
| 898 | } else { | ||
| 899 | romfs = installed->GetEntry(*romfs_title_id, FileSys::ContentRecordType::Data)->GetRomFS(); | ||
| 900 | } | ||
| 852 | 901 | ||
| 853 | const auto extracted = FileSys::ExtractRomFS(romfs, FileSys::RomFSExtractionType::Full); | 902 | const auto extracted = FileSys::ExtractRomFS(romfs, FileSys::RomFSExtractionType::Full); |
| 854 | if (extracted == nullptr) { | 903 | if (extracted == nullptr) { |
| @@ -860,6 +909,7 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa | |||
| 860 | 909 | ||
| 861 | if (out == nullptr) { | 910 | if (out == nullptr) { |
| 862 | failed(); | 911 | failed(); |
| 912 | vfs->DeleteDirectory(path); | ||
| 863 | return; | 913 | return; |
| 864 | } | 914 | } |
| 865 | 915 | ||
| @@ -870,8 +920,11 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa | |||
| 870 | "files into the new directory while <br>skeleton will only create the directory " | 920 | "files into the new directory while <br>skeleton will only create the directory " |
| 871 | "structure."), | 921 | "structure."), |
| 872 | {"Full", "Skeleton"}, 0, false, &ok); | 922 | {"Full", "Skeleton"}, 0, false, &ok); |
| 873 | if (!ok) | 923 | if (!ok) { |
| 874 | failed(); | 924 | failed(); |
| 925 | vfs->DeleteDirectory(path); | ||
| 926 | return; | ||
| 927 | } | ||
| 875 | 928 | ||
| 876 | const auto full = res == "Full"; | 929 | const auto full = res == "Full"; |
| 877 | const auto entry_size = CalculateRomFSEntrySize(extracted, full); | 930 | const auto entry_size = CalculateRomFSEntrySize(extracted, full); |
| @@ -888,6 +941,7 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa | |||
| 888 | } else { | 941 | } else { |
| 889 | progress.close(); | 942 | progress.close(); |
| 890 | failed(); | 943 | failed(); |
| 944 | vfs->DeleteDirectory(path); | ||
| 891 | } | 945 | } |
| 892 | } | 946 | } |
| 893 | 947 | ||
| @@ -1174,6 +1228,7 @@ void GMainWindow::OnStartGame() { | |||
| 1174 | ui.action_Report_Compatibility->setEnabled(true); | 1228 | ui.action_Report_Compatibility->setEnabled(true); |
| 1175 | 1229 | ||
| 1176 | discord_rpc->Update(); | 1230 | discord_rpc->Update(); |
| 1231 | ui.action_Load_Amiibo->setEnabled(true); | ||
| 1177 | } | 1232 | } |
| 1178 | 1233 | ||
| 1179 | void GMainWindow::OnPauseGame() { | 1234 | void GMainWindow::OnPauseGame() { |
| @@ -1278,6 +1333,47 @@ void GMainWindow::OnConfigure() { | |||
| 1278 | } | 1333 | } |
| 1279 | } | 1334 | } |
| 1280 | 1335 | ||
| 1336 | void GMainWindow::OnLoadAmiibo() { | ||
| 1337 | const QString extensions{"*.bin"}; | ||
| 1338 | const QString file_filter = tr("Amiibo File (%1);; All Files (*.*)").arg(extensions); | ||
| 1339 | const QString filename = QFileDialog::getOpenFileName(this, tr("Load Amiibo"), "", file_filter); | ||
| 1340 | |||
| 1341 | if (filename.isEmpty()) { | ||
| 1342 | return; | ||
| 1343 | } | ||
| 1344 | |||
| 1345 | Core::System& system{Core::System::GetInstance()}; | ||
| 1346 | Service::SM::ServiceManager& sm = system.ServiceManager(); | ||
| 1347 | auto nfc = sm.GetService<Service::NFP::Module::Interface>("nfp:user"); | ||
| 1348 | if (nfc == nullptr) { | ||
| 1349 | return; | ||
| 1350 | } | ||
| 1351 | |||
| 1352 | QFile nfc_file{filename}; | ||
| 1353 | if (!nfc_file.open(QIODevice::ReadOnly)) { | ||
| 1354 | QMessageBox::warning(this, tr("Error opening Amiibo data file"), | ||
| 1355 | tr("Unable to open Amiibo file \"%1\" for reading.").arg(filename)); | ||
| 1356 | return; | ||
| 1357 | } | ||
| 1358 | |||
| 1359 | const u64 nfc_file_size = nfc_file.size(); | ||
| 1360 | std::vector<u8> buffer(nfc_file_size); | ||
| 1361 | const u64 read_size = nfc_file.read(reinterpret_cast<char*>(buffer.data()), nfc_file_size); | ||
| 1362 | if (nfc_file_size != read_size) { | ||
| 1363 | QMessageBox::warning(this, tr("Error reading Amiibo data file"), | ||
| 1364 | tr("Unable to fully read Amiibo data. Expected to read %1 bytes, but " | ||
| 1365 | "was only able to read %2 bytes.") | ||
| 1366 | .arg(nfc_file_size) | ||
| 1367 | .arg(read_size)); | ||
| 1368 | return; | ||
| 1369 | } | ||
| 1370 | |||
| 1371 | if (!nfc->LoadAmiibo(buffer)) { | ||
| 1372 | QMessageBox::warning(this, tr("Error loading Amiibo data"), | ||
| 1373 | tr("Unable to load Amiibo data.")); | ||
| 1374 | } | ||
| 1375 | } | ||
| 1376 | |||
| 1281 | void GMainWindow::OnAbout() { | 1377 | void GMainWindow::OnAbout() { |
| 1282 | AboutDialog aboutDialog(this); | 1378 | AboutDialog aboutDialog(this); |
| 1283 | aboutDialog.exec(); | 1379 | aboutDialog.exec(); |
| @@ -1318,15 +1414,17 @@ void GMainWindow::UpdateStatusBar() { | |||
| 1318 | void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string details) { | 1414 | void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string details) { |
| 1319 | QMessageBox::StandardButton answer; | 1415 | QMessageBox::StandardButton answer; |
| 1320 | QString status_message; | 1416 | QString status_message; |
| 1321 | const QString common_message = tr( | 1417 | const QString common_message = |
| 1322 | "The game you are trying to load requires additional files from your Switch to be dumped " | 1418 | tr("The game you are trying to load requires additional files from your Switch to be " |
| 1323 | "before playing.<br/><br/>For more information on dumping these files, please see the " | 1419 | "dumped " |
| 1324 | "following wiki page: <a " | 1420 | "before playing.<br/><br/>For more information on dumping these files, please see the " |
| 1325 | "href='https://yuzu-emu.org/wiki/" | 1421 | "following wiki page: <a " |
| 1326 | "dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System " | 1422 | "href='https://yuzu-emu.org/wiki/" |
| 1327 | "Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to quit " | 1423 | "dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System " |
| 1328 | "back to the game list? Continuing emulation may result in crashes, corrupted save " | 1424 | "Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to " |
| 1329 | "data, or other bugs."); | 1425 | "quit " |
| 1426 | "back to the game list? Continuing emulation may result in crashes, corrupted save " | ||
| 1427 | "data, or other bugs."); | ||
| 1330 | switch (result) { | 1428 | switch (result) { |
| 1331 | case Core::System::ResultStatus::ErrorSystemFiles: { | 1429 | case Core::System::ResultStatus::ErrorSystemFiles: { |
| 1332 | QString message = "yuzu was unable to locate a Switch system archive"; | 1430 | QString message = "yuzu was unable to locate a Switch system archive"; |
| @@ -1357,9 +1455,12 @@ void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string det | |||
| 1357 | this, tr("Fatal Error"), | 1455 | this, tr("Fatal Error"), |
| 1358 | tr("yuzu has encountered a fatal error, please see the log for more details. " | 1456 | tr("yuzu has encountered a fatal error, please see the log for more details. " |
| 1359 | "For more information on accessing the log, please see the following page: " | 1457 | "For more information on accessing the log, please see the following page: " |
| 1360 | "<a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to " | 1458 | "<a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How " |
| 1361 | "Upload the Log File</a>.<br/><br/>Would you like to quit back to the game list? " | 1459 | "to " |
| 1362 | "Continuing emulation may result in crashes, corrupted save data, or other bugs."), | 1460 | "Upload the Log File</a>.<br/><br/>Would you like to quit back to the game " |
| 1461 | "list? " | ||
| 1462 | "Continuing emulation may result in crashes, corrupted save data, or other " | ||
| 1463 | "bugs."), | ||
| 1363 | QMessageBox::Yes | QMessageBox::No, QMessageBox::No); | 1464 | QMessageBox::Yes | QMessageBox::No, QMessageBox::No); |
| 1364 | status_message = "Fatal Error encountered"; | 1465 | status_message = "Fatal Error encountered"; |
| 1365 | break; | 1466 | break; |
| @@ -1459,6 +1560,42 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) { | |||
| 1459 | } | 1560 | } |
| 1460 | } | 1561 | } |
| 1461 | 1562 | ||
| 1563 | boost::optional<u64> GMainWindow::SelectRomFSDumpTarget( | ||
| 1564 | const FileSys::RegisteredCacheUnion& installed, u64 program_id) { | ||
| 1565 | const auto dlc_entries = | ||
| 1566 | installed.ListEntriesFilter(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data); | ||
| 1567 | std::vector<FileSys::RegisteredCacheEntry> dlc_match; | ||
| 1568 | dlc_match.reserve(dlc_entries.size()); | ||
| 1569 | std::copy_if(dlc_entries.begin(), dlc_entries.end(), std::back_inserter(dlc_match), | ||
| 1570 | [&program_id, &installed](const FileSys::RegisteredCacheEntry& entry) { | ||
| 1571 | return (entry.title_id & DLC_BASE_TITLE_ID_MASK) == program_id && | ||
| 1572 | installed.GetEntry(entry)->GetStatus() == Loader::ResultStatus::Success; | ||
| 1573 | }); | ||
| 1574 | |||
| 1575 | std::vector<u64> romfs_tids; | ||
| 1576 | romfs_tids.push_back(program_id); | ||
| 1577 | for (const auto& entry : dlc_match) | ||
| 1578 | romfs_tids.push_back(entry.title_id); | ||
| 1579 | |||
| 1580 | if (romfs_tids.size() > 1) { | ||
| 1581 | QStringList list{"Base"}; | ||
| 1582 | for (std::size_t i = 1; i < romfs_tids.size(); ++i) | ||
| 1583 | list.push_back(QStringLiteral("DLC %1").arg(romfs_tids[i] & 0x7FF)); | ||
| 1584 | |||
| 1585 | bool ok; | ||
| 1586 | const auto res = QInputDialog::getItem( | ||
| 1587 | this, tr("Select RomFS Dump Target"), | ||
| 1588 | tr("Please select which RomFS you would like to dump."), list, 0, false, &ok); | ||
| 1589 | if (!ok) { | ||
| 1590 | return boost::none; | ||
| 1591 | } | ||
| 1592 | |||
| 1593 | return romfs_tids[list.indexOf(res)]; | ||
| 1594 | } | ||
| 1595 | |||
| 1596 | return program_id; | ||
| 1597 | } | ||
| 1598 | |||
| 1462 | bool GMainWindow::ConfirmClose() { | 1599 | bool GMainWindow::ConfirmClose() { |
| 1463 | if (emu_thread == nullptr || !UISettings::values.confirm_before_closing) | 1600 | if (emu_thread == nullptr || !UISettings::values.confirm_before_closing) |
| 1464 | return true; | 1601 | return true; |
diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 3663d6aed..7c7c223e1 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h | |||
| @@ -10,6 +10,7 @@ | |||
| 10 | #include <QMainWindow> | 10 | #include <QMainWindow> |
| 11 | #include <QTimer> | 11 | #include <QTimer> |
| 12 | 12 | ||
| 13 | #include <boost/optional.hpp> | ||
| 13 | #include "common/common_types.h" | 14 | #include "common/common_types.h" |
| 14 | #include "core/core.h" | 15 | #include "core/core.h" |
| 15 | #include "ui_main.h" | 16 | #include "ui_main.h" |
| @@ -29,8 +30,9 @@ class WaitTreeWidget; | |||
| 29 | enum class GameListOpenTarget; | 30 | enum class GameListOpenTarget; |
| 30 | 31 | ||
| 31 | namespace FileSys { | 32 | namespace FileSys { |
| 33 | class RegisteredCacheUnion; | ||
| 32 | class VfsFilesystem; | 34 | class VfsFilesystem; |
| 33 | } | 35 | } // namespace FileSys |
| 34 | 36 | ||
| 35 | namespace Tegra { | 37 | namespace Tegra { |
| 36 | class DebugContext; | 38 | class DebugContext; |
| @@ -164,6 +166,7 @@ private slots: | |||
| 164 | void OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget target); | 166 | void OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget target); |
| 165 | void OnMenuRecentFile(); | 167 | void OnMenuRecentFile(); |
| 166 | void OnConfigure(); | 168 | void OnConfigure(); |
| 169 | void OnLoadAmiibo(); | ||
| 167 | void OnAbout(); | 170 | void OnAbout(); |
| 168 | void OnToggleFilterBar(); | 171 | void OnToggleFilterBar(); |
| 169 | void OnDisplayTitleBars(bool); | 172 | void OnDisplayTitleBars(bool); |
| @@ -175,6 +178,8 @@ private slots: | |||
| 175 | void OnReinitializeKeys(ReinitializeKeyBehavior behavior); | 178 | void OnReinitializeKeys(ReinitializeKeyBehavior behavior); |
| 176 | 179 | ||
| 177 | private: | 180 | private: |
| 181 | boost::optional<u64> SelectRomFSDumpTarget(const FileSys::RegisteredCacheUnion&, | ||
| 182 | u64 program_id); | ||
| 178 | void UpdateStatusBar(); | 183 | void UpdateStatusBar(); |
| 179 | 184 | ||
| 180 | Ui::MainWindow ui; | 185 | Ui::MainWindow ui; |
diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui index 9851f507d..48d099591 100644 --- a/src/yuzu/main.ui +++ b/src/yuzu/main.ui | |||
| @@ -57,8 +57,8 @@ | |||
| 57 | <string>Recent Files</string> | 57 | <string>Recent Files</string> |
| 58 | </property> | 58 | </property> |
| 59 | </widget> | 59 | </widget> |
| 60 | <addaction name="action_Install_File_NAND" /> | 60 | <addaction name="action_Install_File_NAND"/> |
| 61 | <addaction name="separator"/> | 61 | <addaction name="separator"/> |
| 62 | <addaction name="action_Load_File"/> | 62 | <addaction name="action_Load_File"/> |
| 63 | <addaction name="action_Load_Folder"/> | 63 | <addaction name="action_Load_Folder"/> |
| 64 | <addaction name="separator"/> | 64 | <addaction name="separator"/> |
| @@ -68,6 +68,8 @@ | |||
| 68 | <addaction name="action_Select_NAND_Directory"/> | 68 | <addaction name="action_Select_NAND_Directory"/> |
| 69 | <addaction name="action_Select_SDMC_Directory"/> | 69 | <addaction name="action_Select_SDMC_Directory"/> |
| 70 | <addaction name="separator"/> | 70 | <addaction name="separator"/> |
| 71 | <addaction name="action_Load_Amiibo"/> | ||
| 72 | <addaction name="separator"/> | ||
| 71 | <addaction name="action_Exit"/> | 73 | <addaction name="action_Exit"/> |
| 72 | </widget> | 74 | </widget> |
| 73 | <widget class="QMenu" name="menu_Emulation"> | 75 | <widget class="QMenu" name="menu_Emulation"> |
| @@ -97,25 +99,34 @@ | |||
| 97 | <addaction name="action_Show_Status_Bar"/> | 99 | <addaction name="action_Show_Status_Bar"/> |
| 98 | <addaction name="menu_View_Debugging"/> | 100 | <addaction name="menu_View_Debugging"/> |
| 99 | </widget> | 101 | </widget> |
| 102 | <widget class ="QMenu" name="menu_Tools"> | ||
| 103 | <property name="title"> | ||
| 104 | <string>Tools</string> | ||
| 105 | </property> | ||
| 106 | <addaction name="action_Rederive" /> | ||
| 107 | </widget> | ||
| 100 | <widget class="QMenu" name="menu_Help"> | 108 | <widget class="QMenu" name="menu_Help"> |
| 101 | <property name="title"> | 109 | <property name="title"> |
| 102 | <string>&Help</string> | 110 | <string>&Help</string> |
| 103 | </property> | 111 | </property> |
| 104 | <addaction name="action_Report_Compatibility"/> | 112 | <addaction name="action_Report_Compatibility"/> |
| 105 | <addaction name="separator"/> | 113 | <addaction name="separator"/> |
| 106 | <addaction name="action_Rederive"/> | ||
| 107 | <addaction name="action_About"/> | 114 | <addaction name="action_About"/> |
| 108 | </widget> | 115 | </widget> |
| 109 | <addaction name="menu_File"/> | 116 | <addaction name="menu_File"/> |
| 110 | <addaction name="menu_Emulation"/> | 117 | <addaction name="menu_Emulation"/> |
| 111 | <addaction name="menu_View"/> | 118 | <addaction name="menu_View"/> |
| 119 | <addaction name="menu_Tools" /> | ||
| 112 | <addaction name="menu_Help"/> | 120 | <addaction name="menu_Help"/> |
| 113 | </widget> | 121 | </widget> |
| 114 | <action name="action_Install_File_NAND"> | 122 | <action name="action_Install_File_NAND"> |
| 115 | <property name="text"> | 123 | <property name="enabled"> |
| 116 | <string>Install File to NAND...</string> | 124 | <bool>true</bool> |
| 117 | </property> | 125 | </property> |
| 118 | </action> | 126 | <property name="text"> |
| 127 | <string>Install File to NAND...</string> | ||
| 128 | </property> | ||
| 129 | </action> | ||
| 119 | <action name="action_Load_File"> | 130 | <action name="action_Load_File"> |
| 120 | <property name="text"> | 131 | <property name="text"> |
| 121 | <string>Load File...</string> | 132 | <string>Load File...</string> |
| @@ -247,6 +258,14 @@ | |||
| 247 | <string>Restart</string> | 258 | <string>Restart</string> |
| 248 | </property> | 259 | </property> |
| 249 | </action> | 260 | </action> |
| 261 | <action name="action_Load_Amiibo"> | ||
| 262 | <property name="enabled"> | ||
| 263 | <bool>false</bool> | ||
| 264 | </property> | ||
| 265 | <property name="text"> | ||
| 266 | <string>Load Amiibo...</string> | ||
| 267 | </property> | ||
| 268 | </action> | ||
| 250 | <action name="action_Report_Compatibility"> | 269 | <action name="action_Report_Compatibility"> |
| 251 | <property name="enabled"> | 270 | <property name="enabled"> |
| 252 | <bool>false</bool> | 271 | <bool>false</bool> |
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index 5e42e48b2..b456266a6 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | #include "common/file_util.h" | 8 | #include "common/file_util.h" |
| 9 | #include "common/logging/log.h" | 9 | #include "common/logging/log.h" |
| 10 | #include "common/param_package.h" | 10 | #include "common/param_package.h" |
| 11 | #include "core/hle/service/acc/profile_manager.h" | ||
| 11 | #include "core/settings.h" | 12 | #include "core/settings.h" |
| 12 | #include "input_common/main.h" | 13 | #include "input_common/main.h" |
| 13 | #include "yuzu_cmd/config.h" | 14 | #include "yuzu_cmd/config.h" |
| @@ -125,10 +126,11 @@ void Config::ReadValues() { | |||
| 125 | 126 | ||
| 126 | // System | 127 | // System |
| 127 | Settings::values.use_docked_mode = sdl2_config->GetBoolean("System", "use_docked_mode", false); | 128 | Settings::values.use_docked_mode = sdl2_config->GetBoolean("System", "use_docked_mode", false); |
| 128 | Settings::values.username = sdl2_config->Get("System", "username", "yuzu"); | 129 | Settings::values.enable_nfc = sdl2_config->GetBoolean("System", "enable_nfc", true); |
| 129 | if (Settings::values.username.empty()) { | 130 | const auto size = sdl2_config->GetInteger("System", "users_size", 0); |
| 130 | Settings::values.username = "yuzu"; | 131 | |
| 131 | } | 132 | Settings::values.current_user = std::clamp<int>( |
| 133 | sdl2_config->GetInteger("System", "current_user", 0), 0, Service::Account::MAX_USERS - 1); | ||
| 132 | 134 | ||
| 133 | // Miscellaneous | 135 | // Miscellaneous |
| 134 | Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace"); | 136 | Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace"); |
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h index a97b75f7b..e0b223cd6 100644 --- a/src/yuzu_cmd/default_ini.h +++ b/src/yuzu_cmd/default_ini.h | |||
| @@ -174,6 +174,10 @@ use_virtual_sd = | |||
| 174 | # 1: Yes, 0 (default): No | 174 | # 1: Yes, 0 (default): No |
| 175 | use_docked_mode = | 175 | use_docked_mode = |
| 176 | 176 | ||
| 177 | # Allow the use of NFC in games | ||
| 178 | # 1 (default): Yes, 0 : No | ||
| 179 | enable_nfc = | ||
| 180 | |||
| 177 | # Sets the account username, max length is 32 characters | 181 | # Sets the account username, max length is 32 characters |
| 178 | # yuzu (default) | 182 | # yuzu (default) |
| 179 | username = yuzu | 183 | username = yuzu |