diff options
48 files changed, 1418 insertions, 568 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 6e98b557e..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 |
| @@ -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/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/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 c9161155a..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 |
| @@ -96,6 +97,7 @@ enum class Class : ClassType { | |||
| 96 | Service_SM, ///< The SM (Service manager) service | 97 | Service_SM, ///< The SM (Service manager) service |
| 97 | Service_SPL, ///< The SPL service | 98 | Service_SPL, ///< The SPL service |
| 98 | Service_SSL, ///< The SSL service | 99 | Service_SSL, ///< The SSL service |
| 100 | Service_TCAP, ///< The TCAP service. | ||
| 99 | Service_Time, ///< The time service | 101 | Service_Time, ///< The time service |
| 100 | Service_USB, ///< The USB (Universal Serial Bus) service | 102 | Service_USB, ///< The USB (Universal Serial Bus) service |
| 101 | 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/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/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/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.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_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/svc.cpp b/src/core/hle/kernel/svc.cpp index 690b84930..9a783d524 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,14 +593,15 @@ 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); | 597 | SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle); |
| 551 | if (!thread) | 598 | if (!thread) { |
| 552 | return ERR_INVALID_HANDLE; | 599 | return ERR_INVALID_HANDLE; |
| 600 | } | ||
| 553 | 601 | ||
| 554 | // Note: The kernel uses the current process's resource limit instead of | 602 | // Note: The kernel uses the current process's resource limit instead of |
| 555 | // the one from the thread owner's resource limit. | 603 | // the one from the thread owner's resource limit. |
| 556 | const ResourceLimit& resource_limit = Core::CurrentProcess()->GetResourceLimit(); | 604 | const ResourceLimit& resource_limit = current_process->GetResourceLimit(); |
| 557 | if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) { | 605 | if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) { |
| 558 | return ERR_NOT_AUTHORIZED; | 606 | return ERR_NOT_AUTHORIZED; |
| 559 | } | 607 | } |
| @@ -595,15 +643,13 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s | |||
| 595 | return ERR_INVALID_MEMORY_PERMISSIONS; | 643 | return ERR_INVALID_MEMORY_PERMISSIONS; |
| 596 | } | 644 | } |
| 597 | 645 | ||
| 598 | auto& kernel = Core::System::GetInstance().Kernel(); | 646 | auto* const current_process = Core::CurrentProcess(); |
| 599 | auto shared_memory = kernel.HandleTable().Get<SharedMemory>(shared_memory_handle); | 647 | auto shared_memory = current_process->GetHandleTable().Get<SharedMemory>(shared_memory_handle); |
| 600 | if (!shared_memory) { | 648 | if (!shared_memory) { |
| 601 | return ERR_INVALID_HANDLE; | 649 | return ERR_INVALID_HANDLE; |
| 602 | } | 650 | } |
| 603 | 651 | ||
| 604 | auto* const current_process = Core::CurrentProcess(); | ||
| 605 | const auto& vm_manager = current_process->VMManager(); | 652 | const auto& vm_manager = current_process->VMManager(); |
| 606 | |||
| 607 | if (!vm_manager.IsWithinASLRRegion(addr, size)) { | 653 | if (!vm_manager.IsWithinASLRRegion(addr, size)) { |
| 608 | return ERR_INVALID_MEMORY_RANGE; | 654 | return ERR_INVALID_MEMORY_RANGE; |
| 609 | } | 655 | } |
| @@ -627,15 +673,13 @@ static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 | |||
| 627 | return ERR_INVALID_ADDRESS_STATE; | 673 | return ERR_INVALID_ADDRESS_STATE; |
| 628 | } | 674 | } |
| 629 | 675 | ||
| 630 | auto& kernel = Core::System::GetInstance().Kernel(); | 676 | auto* const current_process = Core::CurrentProcess(); |
| 631 | auto shared_memory = kernel.HandleTable().Get<SharedMemory>(shared_memory_handle); | 677 | auto shared_memory = current_process->GetHandleTable().Get<SharedMemory>(shared_memory_handle); |
| 632 | if (!shared_memory) { | 678 | if (!shared_memory) { |
| 633 | return ERR_INVALID_HANDLE; | 679 | return ERR_INVALID_HANDLE; |
| 634 | } | 680 | } |
| 635 | 681 | ||
| 636 | auto* const current_process = Core::CurrentProcess(); | ||
| 637 | const auto& vm_manager = current_process->VMManager(); | 682 | const auto& vm_manager = current_process->VMManager(); |
| 638 | |||
| 639 | if (!vm_manager.IsWithinASLRRegion(addr, size)) { | 683 | if (!vm_manager.IsWithinASLRRegion(addr, size)) { |
| 640 | return ERR_INVALID_MEMORY_RANGE; | 684 | return ERR_INVALID_MEMORY_RANGE; |
| 641 | } | 685 | } |
| @@ -646,9 +690,8 @@ static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 | |||
| 646 | /// Query process memory | 690 | /// Query process memory |
| 647 | static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* /*page_info*/, | 691 | static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* /*page_info*/, |
| 648 | Handle process_handle, u64 addr) { | 692 | Handle process_handle, u64 addr) { |
| 649 | 693 | const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); | |
| 650 | auto& kernel = Core::System::GetInstance().Kernel(); | 694 | SharedPtr<Process> process = handle_table.Get<Process>(process_handle); |
| 651 | SharedPtr<Process> process = kernel.HandleTable().Get<Process>(process_handle); | ||
| 652 | if (!process) { | 695 | if (!process) { |
| 653 | return ERR_INVALID_HANDLE; | 696 | return ERR_INVALID_HANDLE; |
| 654 | } | 697 | } |
| @@ -695,20 +738,19 @@ static void ExitProcess() { | |||
| 695 | /// Creates a new thread | 738 | /// Creates a new thread |
| 696 | static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, VAddr stack_top, | 739 | static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, VAddr stack_top, |
| 697 | u32 priority, s32 processor_id) { | 740 | u32 priority, s32 processor_id) { |
| 698 | std::string name = fmt::format("thread-{:X}", entry_point); | ||
| 699 | |||
| 700 | if (priority > THREADPRIO_LOWEST) { | 741 | if (priority > THREADPRIO_LOWEST) { |
| 701 | return ERR_INVALID_THREAD_PRIORITY; | 742 | return ERR_INVALID_THREAD_PRIORITY; |
| 702 | } | 743 | } |
| 703 | 744 | ||
| 704 | const ResourceLimit& resource_limit = Core::CurrentProcess()->GetResourceLimit(); | 745 | auto* const current_process = Core::CurrentProcess(); |
| 746 | const ResourceLimit& resource_limit = current_process->GetResourceLimit(); | ||
| 705 | if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) { | 747 | if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) { |
| 706 | return ERR_NOT_AUTHORIZED; | 748 | return ERR_NOT_AUTHORIZED; |
| 707 | } | 749 | } |
| 708 | 750 | ||
| 709 | if (processor_id == THREADPROCESSORID_DEFAULT) { | 751 | if (processor_id == THREADPROCESSORID_DEFAULT) { |
| 710 | // Set the target CPU to the one specified in the process' exheader. | 752 | // Set the target CPU to the one specified in the process' exheader. |
| 711 | processor_id = Core::CurrentProcess()->GetDefaultProcessorID(); | 753 | processor_id = current_process->GetDefaultProcessorID(); |
| 712 | ASSERT(processor_id != THREADPROCESSORID_DEFAULT); | 754 | ASSERT(processor_id != THREADPROCESSORID_DEFAULT); |
| 713 | } | 755 | } |
| 714 | 756 | ||
| @@ -723,11 +765,13 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V | |||
| 723 | return ERR_INVALID_PROCESSOR_ID; | 765 | return ERR_INVALID_PROCESSOR_ID; |
| 724 | } | 766 | } |
| 725 | 767 | ||
| 768 | const std::string name = fmt::format("thread-{:X}", entry_point); | ||
| 726 | auto& kernel = Core::System::GetInstance().Kernel(); | 769 | auto& kernel = Core::System::GetInstance().Kernel(); |
| 727 | CASCADE_RESULT(SharedPtr<Thread> thread, | 770 | CASCADE_RESULT(SharedPtr<Thread> thread, |
| 728 | Thread::Create(kernel, name, entry_point, priority, arg, processor_id, stack_top, | 771 | Thread::Create(kernel, name, entry_point, priority, arg, processor_id, stack_top, |
| 729 | *Core::CurrentProcess())); | 772 | *current_process)); |
| 730 | const auto new_guest_handle = kernel.HandleTable().Create(thread); | 773 | |
| 774 | const auto new_guest_handle = current_process->GetHandleTable().Create(thread); | ||
| 731 | if (new_guest_handle.Failed()) { | 775 | if (new_guest_handle.Failed()) { |
| 732 | return new_guest_handle.Code(); | 776 | return new_guest_handle.Code(); |
| 733 | } | 777 | } |
| @@ -748,8 +792,8 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V | |||
| 748 | static ResultCode StartThread(Handle thread_handle) { | 792 | static ResultCode StartThread(Handle thread_handle) { |
| 749 | LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle); | 793 | LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle); |
| 750 | 794 | ||
| 751 | auto& kernel = Core::System::GetInstance().Kernel(); | 795 | const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); |
| 752 | const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle); | 796 | const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); |
| 753 | if (!thread) { | 797 | if (!thread) { |
| 754 | return ERR_INVALID_HANDLE; | 798 | return ERR_INVALID_HANDLE; |
| 755 | } | 799 | } |
| @@ -796,8 +840,8 @@ static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_var | |||
| 796 | "called mutex_addr={:X}, condition_variable_addr={:X}, thread_handle=0x{:08X}, timeout={}", | 840 | "called mutex_addr={:X}, condition_variable_addr={:X}, thread_handle=0x{:08X}, timeout={}", |
| 797 | mutex_addr, condition_variable_addr, thread_handle, nano_seconds); | 841 | mutex_addr, condition_variable_addr, thread_handle, nano_seconds); |
| 798 | 842 | ||
| 799 | auto& kernel = Core::System::GetInstance().Kernel(); | 843 | const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); |
| 800 | SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle); | 844 | SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); |
| 801 | ASSERT(thread); | 845 | ASSERT(thread); |
| 802 | 846 | ||
| 803 | CASCADE_CODE(Mutex::Release(mutex_addr)); | 847 | CASCADE_CODE(Mutex::Release(mutex_addr)); |
| @@ -908,9 +952,9 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target | |||
| 908 | mutex_val | Mutex::MutexHasWaitersFlag)); | 952 | mutex_val | Mutex::MutexHasWaitersFlag)); |
| 909 | 953 | ||
| 910 | // The mutex is already owned by some other thread, make this thread wait on it. | 954 | // The mutex is already owned by some other thread, make this thread wait on it. |
| 911 | auto& kernel = Core::System::GetInstance().Kernel(); | 955 | const Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask); |
| 912 | Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask); | 956 | const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); |
| 913 | auto owner = kernel.HandleTable().Get<Thread>(owner_handle); | 957 | auto owner = handle_table.Get<Thread>(owner_handle); |
| 914 | ASSERT(owner); | 958 | ASSERT(owner); |
| 915 | ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex); | 959 | ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex); |
| 916 | thread->InvalidateWakeupCallback(); | 960 | thread->InvalidateWakeupCallback(); |
| @@ -989,16 +1033,16 @@ static u64 GetSystemTick() { | |||
| 989 | static ResultCode CloseHandle(Handle handle) { | 1033 | static ResultCode CloseHandle(Handle handle) { |
| 990 | LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle); | 1034 | LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle); |
| 991 | 1035 | ||
| 992 | auto& kernel = Core::System::GetInstance().Kernel(); | 1036 | auto& handle_table = Core::CurrentProcess()->GetHandleTable(); |
| 993 | return kernel.HandleTable().Close(handle); | 1037 | return handle_table.Close(handle); |
| 994 | } | 1038 | } |
| 995 | 1039 | ||
| 996 | /// Reset an event | 1040 | /// Reset an event |
| 997 | static ResultCode ResetSignal(Handle handle) { | 1041 | static ResultCode ResetSignal(Handle handle) { |
| 998 | LOG_WARNING(Kernel_SVC, "(STUBBED) called handle 0x{:08X}", handle); | 1042 | LOG_WARNING(Kernel_SVC, "(STUBBED) called handle 0x{:08X}", handle); |
| 999 | 1043 | ||
| 1000 | auto& kernel = Core::System::GetInstance().Kernel(); | 1044 | const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); |
| 1001 | auto event = kernel.HandleTable().Get<Event>(handle); | 1045 | auto event = handle_table.Get<Event>(handle); |
| 1002 | 1046 | ||
| 1003 | ASSERT(event != nullptr); | 1047 | ASSERT(event != nullptr); |
| 1004 | 1048 | ||
| @@ -1017,8 +1061,8 @@ static ResultCode CreateTransferMemory(Handle* handle, VAddr addr, u64 size, u32 | |||
| 1017 | static ResultCode GetThreadCoreMask(Handle thread_handle, u32* core, u64* mask) { | 1061 | static ResultCode GetThreadCoreMask(Handle thread_handle, u32* core, u64* mask) { |
| 1018 | LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle); | 1062 | LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle); |
| 1019 | 1063 | ||
| 1020 | auto& kernel = Core::System::GetInstance().Kernel(); | 1064 | const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); |
| 1021 | const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle); | 1065 | const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); |
| 1022 | if (!thread) { | 1066 | if (!thread) { |
| 1023 | return ERR_INVALID_HANDLE; | 1067 | return ERR_INVALID_HANDLE; |
| 1024 | } | 1068 | } |
| @@ -1033,8 +1077,8 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) { | |||
| 1033 | LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, mask=0x{:16X}, core=0x{:X}", thread_handle, | 1077 | LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, mask=0x{:16X}, core=0x{:X}", thread_handle, |
| 1034 | mask, core); | 1078 | mask, core); |
| 1035 | 1079 | ||
| 1036 | auto& kernel = Core::System::GetInstance().Kernel(); | 1080 | const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); |
| 1037 | const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle); | 1081 | const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); |
| 1038 | if (!thread) { | 1082 | if (!thread) { |
| 1039 | return ERR_INVALID_HANDLE; | 1083 | return ERR_INVALID_HANDLE; |
| 1040 | } | 1084 | } |
| @@ -1095,7 +1139,7 @@ static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permiss | |||
| 1095 | } | 1139 | } |
| 1096 | 1140 | ||
| 1097 | auto& kernel = Core::System::GetInstance().Kernel(); | 1141 | auto& kernel = Core::System::GetInstance().Kernel(); |
| 1098 | auto& handle_table = kernel.HandleTable(); | 1142 | auto& handle_table = Core::CurrentProcess()->GetHandleTable(); |
| 1099 | auto shared_mem_handle = | 1143 | auto shared_mem_handle = |
| 1100 | SharedMemory::Create(kernel, handle_table.Get<Process>(KernelHandle::CurrentProcess), size, | 1144 | SharedMemory::Create(kernel, handle_table.Get<Process>(KernelHandle::CurrentProcess), size, |
| 1101 | local_perms, remote_perms); | 1145 | local_perms, remote_perms); |
| @@ -1107,10 +1151,12 @@ static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permiss | |||
| 1107 | static ResultCode ClearEvent(Handle handle) { | 1151 | static ResultCode ClearEvent(Handle handle) { |
| 1108 | LOG_TRACE(Kernel_SVC, "called, event=0x{:08X}", handle); | 1152 | LOG_TRACE(Kernel_SVC, "called, event=0x{:08X}", handle); |
| 1109 | 1153 | ||
| 1110 | auto& kernel = Core::System::GetInstance().Kernel(); | 1154 | const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); |
| 1111 | SharedPtr<Event> evt = kernel.HandleTable().Get<Event>(handle); | 1155 | SharedPtr<Event> evt = handle_table.Get<Event>(handle); |
| 1112 | if (evt == nullptr) | 1156 | if (evt == nullptr) { |
| 1113 | return ERR_INVALID_HANDLE; | 1157 | return ERR_INVALID_HANDLE; |
| 1158 | } | ||
| 1159 | |||
| 1114 | evt->Clear(); | 1160 | evt->Clear(); |
| 1115 | return RESULT_SUCCESS; | 1161 | return RESULT_SUCCESS; |
| 1116 | } | 1162 | } |
| @@ -1123,8 +1169,8 @@ static ResultCode GetProcessInfo(u64* out, Handle process_handle, u32 type) { | |||
| 1123 | Status, | 1169 | Status, |
| 1124 | }; | 1170 | }; |
| 1125 | 1171 | ||
| 1126 | const auto& kernel = Core::System::GetInstance().Kernel(); | 1172 | const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); |
| 1127 | const auto process = kernel.HandleTable().Get<Process>(process_handle); | 1173 | const auto process = handle_table.Get<Process>(process_handle); |
| 1128 | if (!process) { | 1174 | if (!process) { |
| 1129 | return ERR_INVALID_HANDLE; | 1175 | return ERR_INVALID_HANDLE; |
| 1130 | } | 1176 | } |
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/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp index e61748ca3..cf065c2e0 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,9 @@ | |||
| 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 | |||
| 24 | constexpr u32 MAX_JPEG_IMAGE_SIZE = 0x20000; | ||
| 25 | |||
| 19 | // TODO: RE this structure | 26 | // TODO: RE this structure |
| 20 | struct UserData { | 27 | struct UserData { |
| 21 | INSERT_PADDING_WORDS(1); | 28 | INSERT_PADDING_WORDS(1); |
| @@ -27,6 +34,11 @@ struct UserData { | |||
| 27 | }; | 34 | }; |
| 28 | static_assert(sizeof(UserData) == 0x80, "UserData structure has incorrect size"); | 35 | static_assert(sizeof(UserData) == 0x80, "UserData structure has incorrect size"); |
| 29 | 36 | ||
| 37 | static std::string GetImagePath(UUID uuid) { | ||
| 38 | return FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + | ||
| 39 | "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg"; | ||
| 40 | } | ||
| 41 | |||
| 30 | class IProfile final : public ServiceFramework<IProfile> { | 42 | class IProfile final : public ServiceFramework<IProfile> { |
| 31 | public: | 43 | public: |
| 32 | explicit IProfile(UUID user_id, ProfileManager& profile_manager) | 44 | explicit IProfile(UUID user_id, ProfileManager& profile_manager) |
| @@ -73,11 +85,11 @@ private: | |||
| 73 | } | 85 | } |
| 74 | 86 | ||
| 75 | void LoadImage(Kernel::HLERequestContext& ctx) { | 87 | void LoadImage(Kernel::HLERequestContext& ctx) { |
| 76 | LOG_WARNING(Service_ACC, "(STUBBED) called"); | 88 | LOG_DEBUG(Service_ACC, "called"); |
| 77 | // smallest jpeg https://github.com/mathiasbynens/small/blob/master/jpeg.jpg | 89 | // smallest jpeg https://github.com/mathiasbynens/small/blob/master/jpeg.jpg |
| 78 | // TODO(mailwl): load actual profile image from disk, width 256px, max size 0x20000 | 90 | // used as a backup should the one on disk not exist |
| 79 | constexpr u32 jpeg_size = 107; | 91 | constexpr u32 backup_jpeg_size = 107; |
| 80 | static constexpr std::array<u8, jpeg_size> jpeg{ | 92 | static constexpr std::array<u8, backup_jpeg_size> backup_jpeg{ |
| 81 | 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, | 93 | 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, | 94 | 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, | 95 | 0x08, 0x06, 0x06, 0x05, 0x06, 0x09, 0x08, 0x0a, 0x0a, 0x09, 0x08, 0x09, 0x09, 0x0a, |
| @@ -87,18 +99,42 @@ private: | |||
| 87 | 0xff, 0xcc, 0x00, 0x06, 0x00, 0x10, 0x10, 0x05, 0xff, 0xda, 0x00, 0x08, 0x01, 0x01, | 99 | 0xff, 0xcc, 0x00, 0x06, 0x00, 0x10, 0x10, 0x05, 0xff, 0xda, 0x00, 0x08, 0x01, 0x01, |
| 88 | 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9, | 100 | 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9, |
| 89 | }; | 101 | }; |
| 90 | ctx.WriteBuffer(jpeg); | 102 | |
| 91 | IPC::ResponseBuilder rb{ctx, 3}; | 103 | IPC::ResponseBuilder rb{ctx, 3}; |
| 92 | rb.Push(RESULT_SUCCESS); | 104 | rb.Push(RESULT_SUCCESS); |
| 93 | rb.Push<u32>(jpeg_size); | 105 | |
| 106 | const FileUtil::IOFile image(GetImagePath(user_id), "rb"); | ||
| 107 | |||
| 108 | if (!image.IsOpen()) { | ||
| 109 | LOG_WARNING(Service_ACC, | ||
| 110 | "Failed to load user provided image! Falling back to built-in backup..."); | ||
| 111 | ctx.WriteBuffer(backup_jpeg); | ||
| 112 | rb.Push<u32>(backup_jpeg_size); | ||
| 113 | } else { | ||
| 114 | const auto size = std::min<u32>(image.GetSize(), MAX_JPEG_IMAGE_SIZE); | ||
| 115 | std::vector<u8> buffer(size); | ||
| 116 | image.ReadBytes(buffer.data(), buffer.size()); | ||
| 117 | |||
| 118 | ctx.WriteBuffer(buffer.data(), buffer.size()); | ||
| 119 | rb.Push<u32>(buffer.size()); | ||
| 120 | } | ||
| 94 | } | 121 | } |
| 95 | 122 | ||
| 96 | void GetImageSize(Kernel::HLERequestContext& ctx) { | 123 | void GetImageSize(Kernel::HLERequestContext& ctx) { |
| 97 | LOG_WARNING(Service_ACC, "(STUBBED) called"); | 124 | LOG_DEBUG(Service_ACC, "called"); |
| 98 | constexpr u32 jpeg_size = 107; | 125 | constexpr u32 backup_jpeg_size = 107; |
| 99 | IPC::ResponseBuilder rb{ctx, 3}; | 126 | IPC::ResponseBuilder rb{ctx, 3}; |
| 100 | rb.Push(RESULT_SUCCESS); | 127 | rb.Push(RESULT_SUCCESS); |
| 101 | rb.Push<u32>(jpeg_size); | 128 | |
| 129 | const FileUtil::IOFile image(GetImagePath(user_id), "rb"); | ||
| 130 | |||
| 131 | if (!image.IsOpen()) { | ||
| 132 | LOG_WARNING(Service_ACC, | ||
| 133 | "Failed to load user provided image! Falling back to built-in backup..."); | ||
| 134 | rb.Push<u32>(backup_jpeg_size); | ||
| 135 | } else { | ||
| 136 | rb.Push<u32>(std::min<u32>(image.GetSize(), MAX_JPEG_IMAGE_SIZE)); | ||
| 137 | } | ||
| 102 | } | 138 | } |
| 103 | 139 | ||
| 104 | const ProfileManager& profile_manager; | 140 | 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..06f7d1b15 100644 --- a/src/core/hle/service/acc/profile_manager.cpp +++ b/src/core/hle/service/acc/profile_manager.cpp | |||
| @@ -4,32 +4,57 @@ | |||
| 4 | 4 | ||
| 5 | #include <random> | 5 | #include <random> |
| 6 | #include <boost/optional.hpp> | 6 | #include <boost/optional.hpp> |
| 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 |
| @@ -101,6 +126,12 @@ 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 | boost::optional<UUID> ProfileManager::GetUser(std::size_t index) const { | ||
| 130 | if (index >= MAX_USERS) | ||
| 131 | return boost::none; | ||
| 132 | return profiles[index].user_uuid; | ||
| 133 | } | ||
| 134 | |||
| 104 | /// Returns a users profile index based on their user id. | 135 | /// Returns a users profile index based on their user id. |
| 105 | boost::optional<std::size_t> ProfileManager::GetUserIndex(const UUID& uuid) const { | 136 | boost::optional<std::size_t> ProfileManager::GetUserIndex(const UUID& uuid) const { |
| 106 | if (!uuid) { | 137 | if (!uuid) { |
| @@ -164,6 +195,12 @@ bool ProfileManager::UserExists(UUID uuid) const { | |||
| 164 | return (GetUserIndex(uuid) != boost::none); | 195 | return (GetUserIndex(uuid) != boost::none); |
| 165 | } | 196 | } |
| 166 | 197 | ||
| 198 | bool ProfileManager::UserExistsIndex(std::size_t index) const { | ||
| 199 | if (index >= MAX_USERS) | ||
| 200 | return false; | ||
| 201 | return profiles[index].user_uuid.uuid != INVALID_UUID; | ||
| 202 | } | ||
| 203 | |||
| 167 | /// Opens a specific user | 204 | /// Opens a specific user |
| 168 | void ProfileManager::OpenUser(UUID uuid) { | 205 | void ProfileManager::OpenUser(UUID uuid) { |
| 169 | auto idx = GetUserIndex(uuid); | 206 | auto idx = GetUserIndex(uuid); |
| @@ -239,4 +276,96 @@ bool ProfileManager::CanSystemRegisterUser() const { | |||
| 239 | // emulate qlaunch. Update this to dynamically change. | 276 | // emulate qlaunch. Update this to dynamically change. |
| 240 | } | 277 | } |
| 241 | 278 | ||
| 279 | bool ProfileManager::RemoveUser(UUID uuid) { | ||
| 280 | auto index = GetUserIndex(uuid); | ||
| 281 | if (index == boost::none) { | ||
| 282 | return false; | ||
| 283 | } | ||
| 284 | |||
| 285 | profiles[*index] = ProfileInfo{}; | ||
| 286 | std::stable_partition(profiles.begin(), profiles.end(), | ||
| 287 | [](const ProfileInfo& profile) { return profile.user_uuid; }); | ||
| 288 | return true; | ||
| 289 | } | ||
| 290 | |||
| 291 | bool ProfileManager::SetProfileBase(UUID uuid, const ProfileBase& profile_new) { | ||
| 292 | auto index = GetUserIndex(uuid); | ||
| 293 | if (profile_new.user_uuid == UUID(INVALID_UUID) || index == boost::none) { | ||
| 294 | return false; | ||
| 295 | } | ||
| 296 | |||
| 297 | auto& profile = profiles[*index]; | ||
| 298 | profile.user_uuid = profile_new.user_uuid; | ||
| 299 | profile.username = profile_new.username; | ||
| 300 | profile.creation_time = profile_new.timestamp; | ||
| 301 | |||
| 302 | return true; | ||
| 303 | } | ||
| 304 | |||
| 305 | void ProfileManager::ParseUserSaveFile() { | ||
| 306 | FileUtil::IOFile save(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + | ||
| 307 | ACC_SAVE_AVATORS_BASE_PATH + "profiles.dat", | ||
| 308 | "rb"); | ||
| 309 | |||
| 310 | if (!save.IsOpen()) { | ||
| 311 | LOG_WARNING(Service_ACC, "Failed to load profile data from save data... Generating new " | ||
| 312 | "user 'yuzu' with random UUID."); | ||
| 313 | return; | ||
| 314 | } | ||
| 315 | |||
| 316 | ProfileDataRaw data; | ||
| 317 | if (save.ReadBytes(&data, sizeof(ProfileDataRaw)) != sizeof(ProfileDataRaw)) { | ||
| 318 | LOG_WARNING(Service_ACC, "profiles.dat is smaller than expected... Generating new user " | ||
| 319 | "'yuzu' with random UUID."); | ||
| 320 | return; | ||
| 321 | } | ||
| 322 | |||
| 323 | for (std::size_t i = 0; i < MAX_USERS; ++i) { | ||
| 324 | const auto& user = data.users[i]; | ||
| 325 | |||
| 326 | if (user.uuid != UUID(INVALID_UUID)) | ||
| 327 | AddUser({user.uuid, user.username, user.timestamp, {}, false}); | ||
| 328 | } | ||
| 329 | |||
| 330 | std::stable_partition(profiles.begin(), profiles.end(), | ||
| 331 | [](const ProfileInfo& profile) { return profile.user_uuid; }); | ||
| 332 | } | ||
| 333 | |||
| 334 | void ProfileManager::WriteUserSaveFile() { | ||
| 335 | ProfileDataRaw raw{}; | ||
| 336 | |||
| 337 | for (std::size_t i = 0; i < MAX_USERS; ++i) { | ||
| 338 | raw.users[i].username = profiles[i].username; | ||
| 339 | raw.users[i].uuid2 = profiles[i].user_uuid; | ||
| 340 | raw.users[i].uuid = profiles[i].user_uuid; | ||
| 341 | raw.users[i].timestamp = profiles[i].creation_time; | ||
| 342 | } | ||
| 343 | |||
| 344 | const auto raw_path = | ||
| 345 | FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "/system/save/8000000000000010"; | ||
| 346 | if (FileUtil::Exists(raw_path) && !FileUtil::IsDirectory(raw_path)) | ||
| 347 | FileUtil::Delete(raw_path); | ||
| 348 | |||
| 349 | const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + | ||
| 350 | ACC_SAVE_AVATORS_BASE_PATH + "profiles.dat"; | ||
| 351 | |||
| 352 | if (!FileUtil::CreateFullPath(path)) { | ||
| 353 | LOG_WARNING(Service_ACC, "Failed to create full path of profiles.dat. Create the directory " | ||
| 354 | "nand/system/save/8000000000000010/su/avators to mitigate this " | ||
| 355 | "issue."); | ||
| 356 | return; | ||
| 357 | } | ||
| 358 | |||
| 359 | FileUtil::IOFile save(path, "wb"); | ||
| 360 | |||
| 361 | if (!save.IsOpen()) { | ||
| 362 | LOG_WARNING(Service_ACC, "Failed to write save data to file... No changes to user data " | ||
| 363 | "made in current session will be saved."); | ||
| 364 | return; | ||
| 365 | } | ||
| 366 | |||
| 367 | save.Resize(sizeof(ProfileDataRaw)); | ||
| 368 | save.WriteBytes(&raw, sizeof(ProfileDataRaw)); | ||
| 369 | } | ||
| 370 | |||
| 242 | }; // namespace Service::Account | 371 | }; // 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..235208d56 100644 --- a/src/core/hle/service/acc/profile_manager.h +++ b/src/core/hle/service/acc/profile_manager.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,12 +90,13 @@ 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); |
| 99 | boost::optional<UUID> GetUser(std::size_t index) const; | ||
| 90 | boost::optional<std::size_t> GetUserIndex(const UUID& uuid) const; | 100 | boost::optional<std::size_t> GetUserIndex(const UUID& uuid) const; |
| 91 | boost::optional<std::size_t> GetUserIndex(const ProfileInfo& user) const; | 101 | boost::optional<std::size_t> GetUserIndex(const ProfileInfo& user) const; |
| 92 | bool GetProfileBase(boost::optional<std::size_t> index, ProfileBase& profile) const; | 102 | bool GetProfileBase(boost::optional<std::size_t> index, ProfileBase& profile) const; |
| @@ -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,7 +119,13 @@ 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); | ||
| 124 | |||
| 111 | private: | 125 | private: |
| 126 | void ParseUserSaveFile(); | ||
| 127 | void WriteUserSaveFile(); | ||
| 128 | |||
| 112 | std::array<ProfileInfo, MAX_USERS> profiles{}; | 129 | std::array<ProfileInfo, MAX_USERS> profiles{}; |
| 113 | std::size_t user_count = 0; | 130 | std::size_t user_count = 0; |
| 114 | boost::optional<std::size_t> AddToProfiles(const ProfileInfo& profile); | 131 | boost::optional<std::size_t> AddToProfiles(const ProfileInfo& profile); |
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index ecf72ae24..4ed66d817 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp | |||
| @@ -4,11 +4,13 @@ | |||
| 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" |
| @@ -26,6 +28,16 @@ | |||
| 26 | 28 | ||
| 27 | namespace Service::AM { | 29 | namespace Service::AM { |
| 28 | 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 | |||
| 29 | IWindowController::IWindowController() : ServiceFramework("IWindowController") { | 41 | IWindowController::IWindowController() : ServiceFramework("IWindowController") { |
| 30 | // clang-format off | 42 | // clang-format off |
| 31 | static const FunctionInfo functions[] = { | 43 | static const FunctionInfo functions[] = { |
| @@ -724,20 +736,23 @@ void IApplicationFunctions::EndBlockingHomeButton(Kernel::HLERequestContext& ctx | |||
| 724 | } | 736 | } |
| 725 | 737 | ||
| 726 | void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) { | 738 | void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) { |
| 727 | constexpr std::array<u8, 0x88> data{{ | 739 | LaunchParameters params{}; |
| 728 | 0xca, 0x97, 0x94, 0xc7, // Magic | ||
| 729 | 1, 0, 0, 0, // IsAccountSelected (bool) | ||
| 730 | 1, 0, 0, 0, // User Id (word 0) | ||
| 731 | 0, 0, 0, 0, // User Id (word 1) | ||
| 732 | 0, 0, 0, 0, // User Id (word 2) | ||
| 733 | 0, 0, 0, 0 // User Id (word 3) | ||
| 734 | }}; | ||
| 735 | 740 | ||
| 736 | std::vector<u8> buffer(data.begin(), data.end()); | 741 | params.magic = POP_LAUNCH_PARAMETER_MAGIC; |
| 742 | params.is_account_selected = 1; | ||
| 743 | |||
| 744 | Account::ProfileManager profile_manager{}; | ||
| 745 | const auto uuid = profile_manager.GetUser(Settings::values.current_user); | ||
| 746 | ASSERT(uuid != boost::none); | ||
| 747 | params.current_user = uuid->uuid; | ||
| 737 | 748 | ||
| 738 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 749 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 739 | 750 | ||
| 740 | 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 | |||
| 741 | rb.PushIpcInterface<AM::IStorage>(buffer); | 756 | rb.PushIpcInterface<AM::IStorage>(buffer); |
| 742 | 757 | ||
| 743 | LOG_DEBUG(Service_AM, "called"); | 758 | LOG_DEBUG(Service_AM, "called"); |
diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp index 428069df2..54305cf05 100644 --- a/src/core/hle/service/aoc/aoc_u.cpp +++ b/src/core/hle/service/aoc/aoc_u.cpp | |||
| @@ -24,8 +24,8 @@ namespace Service::AOC { | |||
| 24 | constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000; | 24 | constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000; |
| 25 | constexpr u64 DLC_BASE_TO_AOC_ID = 0x1000; | 25 | constexpr u64 DLC_BASE_TO_AOC_ID = 0x1000; |
| 26 | 26 | ||
| 27 | static bool CheckAOCTitleIDMatchesBase(u64 base, u64 aoc) { | 27 | static bool CheckAOCTitleIDMatchesBase(u64 title_id, u64 base) { |
| 28 | return (aoc & DLC_BASE_TITLE_ID_MASK) == base; | 28 | return (title_id & DLC_BASE_TITLE_ID_MASK) == base; |
| 29 | } | 29 | } |
| 30 | 30 | ||
| 31 | static std::vector<u64> AccumulateAOCTitleIDs() { | 31 | static std::vector<u64> AccumulateAOCTitleIDs() { |
| @@ -74,7 +74,7 @@ void AOC_U::CountAddOnContent(Kernel::HLERequestContext& ctx) { | |||
| 74 | const auto current = Core::System::GetInstance().CurrentProcess()->GetTitleID(); | 74 | const auto current = Core::System::GetInstance().CurrentProcess()->GetTitleID(); |
| 75 | rb.Push<u32>(static_cast<u32>( | 75 | rb.Push<u32>(static_cast<u32>( |
| 76 | std::count_if(add_on_content.begin(), add_on_content.end(), | 76 | std::count_if(add_on_content.begin(), add_on_content.end(), |
| 77 | [¤t](u64 tid) { return (tid & DLC_BASE_TITLE_ID_MASK) == current; }))); | 77 | [current](u64 tid) { return CheckAOCTitleIDMatchesBase(tid, current); }))); |
| 78 | } | 78 | } |
| 79 | 79 | ||
| 80 | void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) { | 80 | void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) { |
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/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..9a4eb9301 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 | ||
| 69 | }; | 97 | }; |
| 70 | 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); | ||
| 107 | }; | ||
| 108 | static_assert(sizeof(CommonInfo) == 0x40, "CommonInfo is an invalid size"); | ||
| 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,217 @@ 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 | void 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; // Failed to load file | ||
| 335 | } | ||
| 336 | std::memcpy(&amiibo, buffer.data(), sizeof(amiibo)); | ||
| 337 | nfc_tag_load->Signal(); | ||
| 338 | } | ||
| 339 | const Kernel::SharedPtr<Kernel::Event>& Module::Interface::GetNFCEvent() const { | ||
| 340 | return nfc_tag_load; | ||
| 341 | } | ||
| 342 | const Module::Interface::AmiiboFile& Module::Interface::GetAmiiboBuffer() const { | ||
| 343 | return amiibo; | ||
| 160 | } | 344 | } |
| 161 | 345 | ||
| 162 | void InstallInterfaces(SM::ServiceManager& service_manager) { | 346 | 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..46370dedd 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 | void 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/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/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index bca014a4a..78ba29fc1 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp | |||
| @@ -155,7 +155,6 @@ void Maxwell3D::ProcessQueryGet() { | |||
| 155 | ASSERT_MSG(regs.query.query_get.unit == Regs::QueryUnit::Crop, | 155 | ASSERT_MSG(regs.query.query_get.unit == Regs::QueryUnit::Crop, |
| 156 | "Units other than CROP are unimplemented"); | 156 | "Units other than CROP are unimplemented"); |
| 157 | 157 | ||
| 158 | u32 value = Memory::Read32(*address); | ||
| 159 | u64 result = 0; | 158 | u64 result = 0; |
| 160 | 159 | ||
| 161 | // TODO(Subv): Support the other query variables | 160 | // TODO(Subv): Support the other query variables |
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp index f1b40e7f5..da7989db9 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; |
diff --git a/src/web_service/CMakeLists.txt b/src/web_service/CMakeLists.txt index 9ad75e74a..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 PRIVATE -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 b901c29d2..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 | ||
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..1fe9a7edd 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" |
| @@ -122,7 +123,11 @@ void Config::ReadValues() { | |||
| 122 | 123 | ||
| 123 | qt_config->beginGroup("System"); | 124 | qt_config->beginGroup("System"); |
| 124 | Settings::values.use_docked_mode = qt_config->value("use_docked_mode", false).toBool(); | 125 | Settings::values.use_docked_mode = qt_config->value("use_docked_mode", false).toBool(); |
| 125 | Settings::values.username = qt_config->value("username", "yuzu").toString().toStdString(); | 126 | Settings::values.enable_nfc = qt_config->value("enable_nfc", true).toBool(); |
| 127 | |||
| 128 | Settings::values.current_user = std::clamp<int>(qt_config->value("current_user", 0).toInt(), 0, | ||
| 129 | Service::Account::MAX_USERS - 1); | ||
| 130 | |||
| 126 | Settings::values.language_index = qt_config->value("language_index", 1).toInt(); | 131 | Settings::values.language_index = qt_config->value("language_index", 1).toInt(); |
| 127 | qt_config->endGroup(); | 132 | qt_config->endGroup(); |
| 128 | 133 | ||
| @@ -258,7 +263,9 @@ void Config::SaveValues() { | |||
| 258 | 263 | ||
| 259 | qt_config->beginGroup("System"); | 264 | qt_config->beginGroup("System"); |
| 260 | qt_config->setValue("use_docked_mode", Settings::values.use_docked_mode); | 265 | qt_config->setValue("use_docked_mode", Settings::values.use_docked_mode); |
| 261 | qt_config->setValue("username", QString::fromStdString(Settings::values.username)); | 266 | qt_config->setValue("enable_nfc", Settings::values.enable_nfc); |
| 267 | qt_config->setValue("current_user", Settings::values.current_user); | ||
| 268 | |||
| 262 | qt_config->setValue("language_index", Settings::values.language_index); | 269 | qt_config->setValue("language_index", Settings::values.language_index); |
| 263 | qt_config->endGroup(); | 270 | qt_config->endGroup(); |
| 264 | 271 | ||
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..83cc49dfc 100644 --- a/src/yuzu/configuration/configure_system.cpp +++ b/src/yuzu/configuration/configure_system.cpp | |||
| @@ -2,13 +2,30 @@ | |||
| 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 | ||
| 24 | static std::string GetImagePath(Service::Account::UUID uuid) { | ||
| 25 | return FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + | ||
| 26 | "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg"; | ||
| 27 | } | ||
| 28 | |||
| 12 | static const std::array<int, 12> days_in_month = {{ | 29 | static const std::array<int, 12> days_in_month = {{ |
| 13 | 31, | 30 | 31, |
| 14 | 29, | 31 | 29, |
| @@ -24,7 +41,20 @@ static const std::array<int, 12> days_in_month = {{ | |||
| 24 | 31, | 41 | 31, |
| 25 | }}; | 42 | }}; |
| 26 | 43 | ||
| 27 | ConfigureSystem::ConfigureSystem(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureSystem) { | 44 | // Same backup JPEG used by acc IProfile::GetImage if no jpeg found |
| 45 | static constexpr std::array<u8, 107> backup_jpeg{ | ||
| 46 | 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x02, 0x02, | ||
| 47 | 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, 0x08, 0x06, 0x06, 0x05, | ||
| 48 | 0x06, 0x09, 0x08, 0x0a, 0x0a, 0x09, 0x08, 0x09, 0x09, 0x0a, 0x0c, 0x0f, 0x0c, 0x0a, 0x0b, 0x0e, | ||
| 49 | 0x0b, 0x09, 0x09, 0x0d, 0x11, 0x0d, 0x0e, 0x0f, 0x10, 0x10, 0x11, 0x10, 0x0a, 0x0c, 0x12, 0x13, | ||
| 50 | 0x12, 0x10, 0x13, 0x0f, 0x10, 0x10, 0x10, 0xff, 0xc9, 0x00, 0x0b, 0x08, 0x00, 0x01, 0x00, 0x01, | ||
| 51 | 0x01, 0x01, 0x11, 0x00, 0xff, 0xcc, 0x00, 0x06, 0x00, 0x10, 0x10, 0x05, 0xff, 0xda, 0x00, 0x08, | ||
| 52 | 0x01, 0x01, 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9, | ||
| 53 | }; | ||
| 54 | |||
| 55 | ConfigureSystem::ConfigureSystem(QWidget* parent) | ||
| 56 | : QWidget(parent), ui(new Ui::ConfigureSystem), | ||
| 57 | profile_manager(std::make_unique<Service::Account::ProfileManager>()) { | ||
| 28 | ui->setupUi(this); | 58 | ui->setupUi(this); |
| 29 | connect(ui->combo_birthmonth, | 59 | connect(ui->combo_birthmonth, |
| 30 | static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, | 60 | static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, |
| @@ -32,6 +62,45 @@ ConfigureSystem::ConfigureSystem(QWidget* parent) : QWidget(parent), ui(new Ui:: | |||
| 32 | connect(ui->button_regenerate_console_id, &QPushButton::clicked, this, | 62 | connect(ui->button_regenerate_console_id, &QPushButton::clicked, this, |
| 33 | &ConfigureSystem::refreshConsoleID); | 63 | &ConfigureSystem::refreshConsoleID); |
| 34 | 64 | ||
| 65 | layout = new QVBoxLayout; | ||
| 66 | tree_view = new QTreeView; | ||
| 67 | item_model = new QStandardItemModel(tree_view); | ||
| 68 | tree_view->setModel(item_model); | ||
| 69 | |||
| 70 | tree_view->setAlternatingRowColors(true); | ||
| 71 | tree_view->setSelectionMode(QHeaderView::SingleSelection); | ||
| 72 | tree_view->setSelectionBehavior(QHeaderView::SelectRows); | ||
| 73 | tree_view->setVerticalScrollMode(QHeaderView::ScrollPerPixel); | ||
| 74 | tree_view->setHorizontalScrollMode(QHeaderView::ScrollPerPixel); | ||
| 75 | tree_view->setSortingEnabled(true); | ||
| 76 | tree_view->setEditTriggers(QHeaderView::NoEditTriggers); | ||
| 77 | tree_view->setUniformRowHeights(true); | ||
| 78 | tree_view->setIconSize({64, 64}); | ||
| 79 | tree_view->setContextMenuPolicy(Qt::NoContextMenu); | ||
| 80 | |||
| 81 | item_model->insertColumns(0, 1); | ||
| 82 | item_model->setHeaderData(0, Qt::Horizontal, "Users"); | ||
| 83 | |||
| 84 | // We must register all custom types with the Qt Automoc system so that we are able to use it | ||
| 85 | // with signals/slots. In this case, QList falls under the umbrells of custom types. | ||
| 86 | qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>"); | ||
| 87 | |||
| 88 | layout->setContentsMargins(0, 0, 0, 0); | ||
| 89 | layout->setSpacing(0); | ||
| 90 | layout->addWidget(tree_view); | ||
| 91 | |||
| 92 | ui->scrollArea->setLayout(layout); | ||
| 93 | |||
| 94 | connect(tree_view, &QTreeView::clicked, this, &ConfigureSystem::SelectUser); | ||
| 95 | |||
| 96 | connect(ui->pm_add, &QPushButton::pressed, this, &ConfigureSystem::AddUser); | ||
| 97 | connect(ui->pm_rename, &QPushButton::pressed, this, &ConfigureSystem::RenameUser); | ||
| 98 | connect(ui->pm_remove, &QPushButton::pressed, this, &ConfigureSystem::DeleteUser); | ||
| 99 | connect(ui->pm_set_image, &QPushButton::pressed, this, &ConfigureSystem::SetUserImage); | ||
| 100 | |||
| 101 | scene = new QGraphicsScene; | ||
| 102 | ui->current_user_icon->setScene(scene); | ||
| 103 | |||
| 35 | this->setConfiguration(); | 104 | this->setConfiguration(); |
| 36 | } | 105 | } |
| 37 | 106 | ||
| @@ -39,16 +108,74 @@ ConfigureSystem::~ConfigureSystem() = default; | |||
| 39 | 108 | ||
| 40 | void ConfigureSystem::setConfiguration() { | 109 | void ConfigureSystem::setConfiguration() { |
| 41 | enabled = !Core::System::GetInstance().IsPoweredOn(); | 110 | enabled = !Core::System::GetInstance().IsPoweredOn(); |
| 42 | ui->edit_username->setText(QString::fromStdString(Settings::values.username)); | 111 | |
| 43 | ui->combo_language->setCurrentIndex(Settings::values.language_index); | 112 | ui->combo_language->setCurrentIndex(Settings::values.language_index); |
| 113 | |||
| 114 | item_model->removeRows(0, item_model->rowCount()); | ||
| 115 | list_items.clear(); | ||
| 116 | |||
| 117 | PopulateUserList(); | ||
| 118 | UpdateCurrentUser(); | ||
| 119 | } | ||
| 120 | |||
| 121 | static QPixmap GetIcon(Service::Account::UUID uuid) { | ||
| 122 | const auto icon_url = QString::fromStdString(GetImagePath(uuid)); | ||
| 123 | QPixmap icon{icon_url}; | ||
| 124 | |||
| 125 | if (!icon) { | ||
| 126 | icon.fill(Qt::black); | ||
| 127 | icon.loadFromData(backup_jpeg.data(), backup_jpeg.size()); | ||
| 128 | } | ||
| 129 | |||
| 130 | return icon; | ||
| 131 | } | ||
| 132 | |||
| 133 | void ConfigureSystem::PopulateUserList() { | ||
| 134 | const auto& profiles = profile_manager->GetAllUsers(); | ||
| 135 | for (const auto& user : profiles) { | ||
| 136 | Service::Account::ProfileBase profile; | ||
| 137 | if (!profile_manager->GetProfileBase(user, profile)) | ||
| 138 | continue; | ||
| 139 | |||
| 140 | const auto username = Common::StringFromFixedZeroTerminatedBuffer( | ||
| 141 | reinterpret_cast<const char*>(profile.username.data()), profile.username.size()); | ||
| 142 | |||
| 143 | list_items.push_back(QList<QStandardItem*>{new QStandardItem{ | ||
| 144 | GetIcon(user).scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), | ||
| 145 | QString::fromStdString(username + '\n' + user.FormatSwitch())}}); | ||
| 146 | } | ||
| 147 | |||
| 148 | for (const auto& item : list_items) | ||
| 149 | item_model->appendRow(item); | ||
| 150 | } | ||
| 151 | |||
| 152 | void ConfigureSystem::UpdateCurrentUser() { | ||
| 153 | ui->pm_add->setEnabled(profile_manager->GetUserCount() < Service::Account::MAX_USERS); | ||
| 154 | |||
| 155 | const auto& current_user = profile_manager->GetUser(Settings::values.current_user); | ||
| 156 | ASSERT(current_user != boost::none); | ||
| 157 | const auto username = GetAccountUsername(*current_user); | ||
| 158 | |||
| 159 | scene->clear(); | ||
| 160 | scene->addPixmap( | ||
| 161 | GetIcon(*current_user).scaled(48, 48, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); | ||
| 162 | ui->current_user_username->setText(QString::fromStdString(username)); | ||
| 44 | } | 163 | } |
| 45 | 164 | ||
| 46 | void ConfigureSystem::ReadSystemSettings() {} | 165 | void ConfigureSystem::ReadSystemSettings() {} |
| 47 | 166 | ||
| 167 | std::string ConfigureSystem::GetAccountUsername(Service::Account::UUID uuid) const { | ||
| 168 | Service::Account::ProfileBase profile; | ||
| 169 | if (!profile_manager->GetProfileBase(uuid, profile)) | ||
| 170 | return ""; | ||
| 171 | return Common::StringFromFixedZeroTerminatedBuffer( | ||
| 172 | reinterpret_cast<const char*>(profile.username.data()), profile.username.size()); | ||
| 173 | } | ||
| 174 | |||
| 48 | void ConfigureSystem::applyConfiguration() { | 175 | void ConfigureSystem::applyConfiguration() { |
| 49 | if (!enabled) | 176 | if (!enabled) |
| 50 | return; | 177 | return; |
| 51 | Settings::values.username = ui->edit_username->text().toStdString(); | 178 | |
| 52 | Settings::values.language_index = ui->combo_language->currentIndex(); | 179 | Settings::values.language_index = ui->combo_language->currentIndex(); |
| 53 | Settings::Apply(); | 180 | Settings::Apply(); |
| 54 | } | 181 | } |
| @@ -92,3 +219,130 @@ void ConfigureSystem::refreshConsoleID() { | |||
| 92 | ui->label_console_id->setText( | 219 | ui->label_console_id->setText( |
| 93 | tr("Console ID: 0x%1").arg(QString::number(console_id, 16).toUpper())); | 220 | tr("Console ID: 0x%1").arg(QString::number(console_id, 16).toUpper())); |
| 94 | } | 221 | } |
| 222 | |||
| 223 | void ConfigureSystem::SelectUser(const QModelIndex& index) { | ||
| 224 | Settings::values.current_user = | ||
| 225 | std::clamp<std::size_t>(index.row(), 0, profile_manager->GetUserCount() - 1); | ||
| 226 | |||
| 227 | UpdateCurrentUser(); | ||
| 228 | |||
| 229 | ui->pm_remove->setEnabled(profile_manager->GetUserCount() >= 2); | ||
| 230 | ui->pm_rename->setEnabled(true); | ||
| 231 | ui->pm_set_image->setEnabled(true); | ||
| 232 | } | ||
| 233 | |||
| 234 | void ConfigureSystem::AddUser() { | ||
| 235 | Service::Account::UUID uuid; | ||
| 236 | uuid.Generate(); | ||
| 237 | |||
| 238 | bool ok = false; | ||
| 239 | const auto username = | ||
| 240 | QInputDialog::getText(this, tr("Enter Username"), tr("Enter a username for the new user:"), | ||
| 241 | QLineEdit::Normal, QString(), &ok); | ||
| 242 | if (!ok) | ||
| 243 | return; | ||
| 244 | |||
| 245 | profile_manager->CreateNewUser(uuid, username.toStdString()); | ||
| 246 | |||
| 247 | item_model->appendRow(new QStandardItem{ | ||
| 248 | GetIcon(uuid).scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), | ||
| 249 | QString::fromStdString(username.toStdString() + '\n' + uuid.FormatSwitch())}); | ||
| 250 | } | ||
| 251 | |||
| 252 | void ConfigureSystem::RenameUser() { | ||
| 253 | const auto user = tree_view->currentIndex().row(); | ||
| 254 | const auto uuid = profile_manager->GetUser(user); | ||
| 255 | ASSERT(uuid != boost::none); | ||
| 256 | const auto username = GetAccountUsername(*uuid); | ||
| 257 | |||
| 258 | Service::Account::ProfileBase profile; | ||
| 259 | if (!profile_manager->GetProfileBase(*uuid, profile)) | ||
| 260 | return; | ||
| 261 | |||
| 262 | bool ok = false; | ||
| 263 | const auto new_username = | ||
| 264 | QInputDialog::getText(this, tr("Enter Username"), tr("Enter a new username:"), | ||
| 265 | QLineEdit::Normal, QString::fromStdString(username), &ok); | ||
| 266 | |||
| 267 | if (!ok) | ||
| 268 | return; | ||
| 269 | |||
| 270 | std::fill(profile.username.begin(), profile.username.end(), '\0'); | ||
| 271 | const auto username_std = new_username.toStdString(); | ||
| 272 | if (username_std.size() > profile.username.size()) { | ||
| 273 | std::copy_n(username_std.begin(), std::min(profile.username.size(), username_std.size()), | ||
| 274 | profile.username.begin()); | ||
| 275 | } else { | ||
| 276 | std::copy(username_std.begin(), username_std.end(), profile.username.begin()); | ||
| 277 | } | ||
| 278 | |||
| 279 | profile_manager->SetProfileBase(*uuid, profile); | ||
| 280 | |||
| 281 | item_model->setItem( | ||
| 282 | user, 0, | ||
| 283 | new QStandardItem{ | ||
| 284 | GetIcon(*uuid).scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), | ||
| 285 | tr("%1\n%2", "%1 is the profile username, %2 is the formatted UUID (e.g. " | ||
| 286 | "00112233-4455-6677-8899-AABBCCDDEEFF))") | ||
| 287 | .arg(QString::fromStdString(username_std), | ||
| 288 | QString::fromStdString(uuid->FormatSwitch()))}); | ||
| 289 | UpdateCurrentUser(); | ||
| 290 | } | ||
| 291 | |||
| 292 | void ConfigureSystem::DeleteUser() { | ||
| 293 | const auto index = tree_view->currentIndex().row(); | ||
| 294 | const auto uuid = profile_manager->GetUser(index); | ||
| 295 | ASSERT(uuid != boost::none); | ||
| 296 | const auto username = GetAccountUsername(*uuid); | ||
| 297 | |||
| 298 | const auto confirm = | ||
| 299 | QMessageBox::question(this, tr("Confirm Delete"), | ||
| 300 | tr("You are about to delete user with name %1. Are you sure?") | ||
| 301 | .arg(QString::fromStdString(username))); | ||
| 302 | |||
| 303 | if (confirm == QMessageBox::No) | ||
| 304 | return; | ||
| 305 | |||
| 306 | if (Settings::values.current_user == tree_view->currentIndex().row()) | ||
| 307 | Settings::values.current_user = 0; | ||
| 308 | UpdateCurrentUser(); | ||
| 309 | |||
| 310 | if (!profile_manager->RemoveUser(*uuid)) | ||
| 311 | return; | ||
| 312 | |||
| 313 | item_model->removeRows(tree_view->currentIndex().row(), 1); | ||
| 314 | tree_view->clearSelection(); | ||
| 315 | |||
| 316 | ui->pm_remove->setEnabled(false); | ||
| 317 | ui->pm_rename->setEnabled(false); | ||
| 318 | } | ||
| 319 | |||
| 320 | void ConfigureSystem::SetUserImage() { | ||
| 321 | const auto index = tree_view->currentIndex().row(); | ||
| 322 | const auto uuid = profile_manager->GetUser(index); | ||
| 323 | ASSERT(uuid != boost::none); | ||
| 324 | const auto username = GetAccountUsername(*uuid); | ||
| 325 | |||
| 326 | const auto file = QFileDialog::getOpenFileName(this, tr("Select User Image"), QString(), | ||
| 327 | "JPEG Images (*.jpg *.jpeg)"); | ||
| 328 | |||
| 329 | if (file.isEmpty()) | ||
| 330 | return; | ||
| 331 | |||
| 332 | FileUtil::Delete(GetImagePath(*uuid)); | ||
| 333 | |||
| 334 | const auto raw_path = | ||
| 335 | FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "/system/save/8000000000000010"; | ||
| 336 | if (FileUtil::Exists(raw_path) && !FileUtil::IsDirectory(raw_path)) | ||
| 337 | FileUtil::Delete(raw_path); | ||
| 338 | |||
| 339 | FileUtil::CreateFullPath(GetImagePath(*uuid)); | ||
| 340 | FileUtil::Copy(file.toStdString(), GetImagePath(*uuid)); | ||
| 341 | |||
| 342 | item_model->setItem( | ||
| 343 | index, 0, | ||
| 344 | new QStandardItem{ | ||
| 345 | GetIcon(*uuid).scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), | ||
| 346 | QString::fromStdString(username + '\n' + uuid->FormatSwitch())}); | ||
| 347 | UpdateCurrentUser(); | ||
| 348 | } | ||
diff --git a/src/yuzu/configuration/configure_system.h b/src/yuzu/configuration/configure_system.h index f13de17d4..b73e0719c 100644 --- a/src/yuzu/configuration/configure_system.h +++ b/src/yuzu/configuration/configure_system.h | |||
| @@ -5,8 +5,21 @@ | |||
| 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 | namespace Service::Account { | ||
| 13 | class ProfileManager; | ||
| 14 | struct UUID; | ||
| 15 | } // namespace Service::Account | ||
| 16 | |||
| 17 | class QGraphicsScene; | ||
| 18 | class QStandardItem; | ||
| 19 | class QStandardItemModel; | ||
| 20 | class QTreeView; | ||
| 21 | class QVBoxLayout; | ||
| 22 | |||
| 10 | namespace Ui { | 23 | namespace Ui { |
| 11 | class ConfigureSystem; | 24 | class ConfigureSystem; |
| 12 | } | 25 | } |
| @@ -21,18 +34,36 @@ public: | |||
| 21 | void applyConfiguration(); | 34 | void applyConfiguration(); |
| 22 | void setConfiguration(); | 35 | void setConfiguration(); |
| 23 | 36 | ||
| 37 | void PopulateUserList(); | ||
| 38 | void UpdateCurrentUser(); | ||
| 39 | |||
| 24 | public slots: | 40 | public slots: |
| 25 | void updateBirthdayComboBox(int birthmonth_index); | 41 | void updateBirthdayComboBox(int birthmonth_index); |
| 26 | void refreshConsoleID(); | 42 | void refreshConsoleID(); |
| 27 | 43 | ||
| 44 | void SelectUser(const QModelIndex& index); | ||
| 45 | void AddUser(); | ||
| 46 | void RenameUser(); | ||
| 47 | void DeleteUser(); | ||
| 48 | void SetUserImage(); | ||
| 49 | |||
| 28 | private: | 50 | private: |
| 29 | void ReadSystemSettings(); | 51 | void ReadSystemSettings(); |
| 52 | std::string GetAccountUsername(Service::Account::UUID uuid) const; | ||
| 53 | |||
| 54 | QVBoxLayout* layout; | ||
| 55 | QTreeView* tree_view; | ||
| 56 | QStandardItemModel* item_model; | ||
| 57 | QGraphicsScene* scene; | ||
| 58 | |||
| 59 | std::vector<QList<QStandardItem*>> list_items; | ||
| 30 | 60 | ||
| 31 | std::unique_ptr<Ui::ConfigureSystem> ui; | 61 | std::unique_ptr<Ui::ConfigureSystem> ui; |
| 32 | bool enabled; | 62 | bool enabled; |
| 33 | 63 | ||
| 34 | std::u16string username; | ||
| 35 | int birthmonth, birthday; | 64 | int birthmonth, birthday; |
| 36 | int language_index; | 65 | int language_index; |
| 37 | int sound_index; | 66 | int sound_index; |
| 67 | |||
| 68 | std::unique_ptr<Service::Account::ProfileManager> profile_manager; | ||
| 38 | }; | 69 | }; |
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/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 3a2685e24..a5a4aa432 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp | |||
| @@ -216,11 +216,11 @@ GameList::GameList(FileSys::VirtualFilesystem vfs, GMainWindow* parent) | |||
| 216 | tree_view->setContextMenuPolicy(Qt::CustomContextMenu); | 216 | tree_view->setContextMenuPolicy(Qt::CustomContextMenu); |
| 217 | 217 | ||
| 218 | item_model->insertColumns(0, COLUMN_COUNT); | 218 | item_model->insertColumns(0, COLUMN_COUNT); |
| 219 | item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, "Name"); | 219 | item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, tr("Name")); |
| 220 | item_model->setHeaderData(COLUMN_COMPATIBILITY, Qt::Horizontal, "Compatibility"); | 220 | item_model->setHeaderData(COLUMN_COMPATIBILITY, Qt::Horizontal, tr("Compatibility")); |
| 221 | item_model->setHeaderData(COLUMN_ADD_ONS, Qt::Horizontal, "Add-ons"); | 221 | item_model->setHeaderData(COLUMN_ADD_ONS, Qt::Horizontal, tr("Add-ons")); |
| 222 | item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, "File type"); | 222 | item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, tr("File type")); |
| 223 | item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, "Size"); | 223 | item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, tr("Size")); |
| 224 | 224 | ||
| 225 | connect(tree_view, &QTreeView::activated, this, &GameList::ValidateEntry); | 225 | connect(tree_view, &QTreeView::activated, this, &GameList::ValidateEntry); |
| 226 | connect(tree_view, &QTreeView::customContextMenuRequested, this, &GameList::PopupContextMenu); | 226 | connect(tree_view, &QTreeView::customContextMenuRequested, this, &GameList::PopupContextMenu); |
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index bef9df00d..47f494841 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. |
| @@ -60,6 +61,8 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual | |||
| 60 | #include "core/hle/kernel/process.h" | 61 | #include "core/hle/kernel/process.h" |
| 61 | #include "core/hle/service/filesystem/filesystem.h" | 62 | #include "core/hle/service/filesystem/filesystem.h" |
| 62 | #include "core/hle/service/filesystem/fsp_ldr.h" | 63 | #include "core/hle/service/filesystem/fsp_ldr.h" |
| 64 | #include "core/hle/service/nfp/nfp.h" | ||
| 65 | #include "core/hle/service/sm/sm.h" | ||
| 63 | #include "core/loader/loader.h" | 66 | #include "core/loader/loader.h" |
| 64 | #include "core/perf_stats.h" | 67 | #include "core/perf_stats.h" |
| 65 | #include "core/settings.h" | 68 | #include "core/settings.h" |
| @@ -100,6 +103,8 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; | |||
| 100 | } | 103 | } |
| 101 | #endif | 104 | #endif |
| 102 | 105 | ||
| 106 | constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000; | ||
| 107 | |||
| 103 | /** | 108 | /** |
| 104 | * "Callouts" are one-time instructional messages shown to the user. In the config settings, there | 109 | * "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 | 110 | * is a bitfield "callout_flags" options, used to track if a message has already been shown to the |
| @@ -422,6 +427,7 @@ void GMainWindow::ConnectMenuEvents() { | |||
| 422 | connect(ui.action_Select_SDMC_Directory, &QAction::triggered, this, | 427 | connect(ui.action_Select_SDMC_Directory, &QAction::triggered, this, |
| 423 | [this] { OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget::SDMC); }); | 428 | [this] { OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget::SDMC); }); |
| 424 | connect(ui.action_Exit, &QAction::triggered, this, &QMainWindow::close); | 429 | connect(ui.action_Exit, &QAction::triggered, this, &QMainWindow::close); |
| 430 | connect(ui.action_Load_Amiibo, &QAction::triggered, this, &GMainWindow::OnLoadAmiibo); | ||
| 425 | 431 | ||
| 426 | // Emulation | 432 | // Emulation |
| 427 | connect(ui.action_Start, &QAction::triggered, this, &GMainWindow::OnStartGame); | 433 | connect(ui.action_Start, &QAction::triggered, this, &GMainWindow::OnStartGame); |
| @@ -690,6 +696,7 @@ void GMainWindow::ShutdownGame() { | |||
| 690 | ui.action_Stop->setEnabled(false); | 696 | ui.action_Stop->setEnabled(false); |
| 691 | ui.action_Restart->setEnabled(false); | 697 | ui.action_Restart->setEnabled(false); |
| 692 | ui.action_Report_Compatibility->setEnabled(false); | 698 | ui.action_Report_Compatibility->setEnabled(false); |
| 699 | ui.action_Load_Amiibo->setEnabled(false); | ||
| 693 | render_window->hide(); | 700 | render_window->hide(); |
| 694 | game_list->show(); | 701 | game_list->show(); |
| 695 | game_list->setFilterFocus(); | 702 | game_list->setFilterFocus(); |
| @@ -751,12 +758,43 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target | |||
| 751 | open_target = "Save Data"; | 758 | open_target = "Save Data"; |
| 752 | const std::string nand_dir = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir); | 759 | const std::string nand_dir = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir); |
| 753 | ASSERT(program_id != 0); | 760 | ASSERT(program_id != 0); |
| 754 | // TODO(tech4me): Update this to work with arbitrary user profile | 761 | |
| 755 | // Refer to core/hle/service/acc/profile_manager.cpp ProfileManager constructor | 762 | Service::Account::ProfileManager manager{}; |
| 756 | constexpr u128 user_id = {1, 0}; | 763 | const auto user_ids = manager.GetAllUsers(); |
| 764 | QStringList list; | ||
| 765 | for (const auto& user_id : user_ids) { | ||
| 766 | if (user_id == Service::Account::UUID{}) | ||
| 767 | continue; | ||
| 768 | Service::Account::ProfileBase base; | ||
| 769 | if (!manager.GetProfileBase(user_id, base)) | ||
| 770 | continue; | ||
| 771 | |||
| 772 | list.push_back(QString::fromStdString(Common::StringFromFixedZeroTerminatedBuffer( | ||
| 773 | reinterpret_cast<const char*>(base.username.data()), base.username.size()))); | ||
| 774 | } | ||
| 775 | |||
| 776 | bool ok = false; | ||
| 777 | const auto index_string = | ||
| 778 | QInputDialog::getItem(this, tr("Select User"), | ||
| 779 | tr("Please select the user's save data you would like to open."), | ||
| 780 | list, Settings::values.current_user, false, &ok); | ||
| 781 | if (!ok) | ||
| 782 | return; | ||
| 783 | |||
| 784 | const auto index = list.indexOf(index_string); | ||
| 785 | ASSERT(index != -1 && index < 8); | ||
| 786 | |||
| 787 | const auto user_id = manager.GetUser(index); | ||
| 788 | ASSERT(user_id != boost::none); | ||
| 757 | path = nand_dir + FileSys::SaveDataFactory::GetFullPath(FileSys::SaveDataSpaceId::NandUser, | 789 | path = nand_dir + FileSys::SaveDataFactory::GetFullPath(FileSys::SaveDataSpaceId::NandUser, |
| 758 | FileSys::SaveDataType::SaveData, | 790 | FileSys::SaveDataType::SaveData, |
| 759 | program_id, user_id, 0); | 791 | program_id, user_id->uuid, 0); |
| 792 | |||
| 793 | if (!FileUtil::Exists(path)) { | ||
| 794 | FileUtil::CreateFullPath(path); | ||
| 795 | FileUtil::CreateDir(path); | ||
| 796 | } | ||
| 797 | |||
| 760 | break; | 798 | break; |
| 761 | } | 799 | } |
| 762 | case GameListOpenTarget::ModData: { | 800 | case GameListOpenTarget::ModData: { |
| @@ -823,14 +861,10 @@ static bool RomFSRawCopy(QProgressDialog& dialog, const FileSys::VirtualDir& src | |||
| 823 | } | 861 | } |
| 824 | 862 | ||
| 825 | void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_path) { | 863 | void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_path) { |
| 826 | const auto path = fmt::format("{}{:016X}/romfs", | 864 | 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!"), | 865 | QMessageBox::warning(this, tr("RomFS Extraction Failed!"), |
| 831 | tr("There was an error copying the RomFS files or the user " | 866 | tr("There was an error copying the RomFS files or the user " |
| 832 | "cancelled the operation.")); | 867 | "cancelled the operation.")); |
| 833 | vfs->DeleteDirectory(path); | ||
| 834 | }; | 868 | }; |
| 835 | 869 | ||
| 836 | const auto loader = Loader::GetLoader(vfs->OpenFile(game_path, FileSys::Mode::Read)); | 870 | const auto loader = Loader::GetLoader(vfs->OpenFile(game_path, FileSys::Mode::Read)); |
| @@ -845,10 +879,24 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa | |||
| 845 | return; | 879 | return; |
| 846 | } | 880 | } |
| 847 | 881 | ||
| 848 | const auto romfs = | 882 | const auto installed = Service::FileSystem::GetUnionContents(); |
| 849 | loader->IsRomFSUpdatable() | 883 | auto romfs_title_id = SelectRomFSDumpTarget(*installed, program_id); |
| 850 | ? FileSys::PatchManager(program_id).PatchRomFS(file, loader->ReadRomFSIVFCOffset()) | 884 | |
| 851 | : file; | 885 | if (!romfs_title_id) { |
| 886 | failed(); | ||
| 887 | return; | ||
| 888 | } | ||
| 889 | |||
| 890 | const auto path = fmt::format( | ||
| 891 | "{}{:016X}/romfs", FileUtil::GetUserPath(FileUtil::UserPath::DumpDir), *romfs_title_id); | ||
| 892 | |||
| 893 | FileSys::VirtualFile romfs; | ||
| 894 | |||
| 895 | if (*romfs_title_id == program_id) { | ||
| 896 | romfs = file; | ||
| 897 | } else { | ||
| 898 | romfs = installed->GetEntry(*romfs_title_id, FileSys::ContentRecordType::Data)->GetRomFS(); | ||
| 899 | } | ||
| 852 | 900 | ||
| 853 | const auto extracted = FileSys::ExtractRomFS(romfs, FileSys::RomFSExtractionType::Full); | 901 | const auto extracted = FileSys::ExtractRomFS(romfs, FileSys::RomFSExtractionType::Full); |
| 854 | if (extracted == nullptr) { | 902 | if (extracted == nullptr) { |
| @@ -860,6 +908,7 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa | |||
| 860 | 908 | ||
| 861 | if (out == nullptr) { | 909 | if (out == nullptr) { |
| 862 | failed(); | 910 | failed(); |
| 911 | vfs->DeleteDirectory(path); | ||
| 863 | return; | 912 | return; |
| 864 | } | 913 | } |
| 865 | 914 | ||
| @@ -870,8 +919,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 " | 919 | "files into the new directory while <br>skeleton will only create the directory " |
| 871 | "structure."), | 920 | "structure."), |
| 872 | {"Full", "Skeleton"}, 0, false, &ok); | 921 | {"Full", "Skeleton"}, 0, false, &ok); |
| 873 | if (!ok) | 922 | if (!ok) { |
| 874 | failed(); | 923 | failed(); |
| 924 | vfs->DeleteDirectory(path); | ||
| 925 | return; | ||
| 926 | } | ||
| 875 | 927 | ||
| 876 | const auto full = res == "Full"; | 928 | const auto full = res == "Full"; |
| 877 | const auto entry_size = CalculateRomFSEntrySize(extracted, full); | 929 | const auto entry_size = CalculateRomFSEntrySize(extracted, full); |
| @@ -888,6 +940,7 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa | |||
| 888 | } else { | 940 | } else { |
| 889 | progress.close(); | 941 | progress.close(); |
| 890 | failed(); | 942 | failed(); |
| 943 | vfs->DeleteDirectory(path); | ||
| 891 | } | 944 | } |
| 892 | } | 945 | } |
| 893 | 946 | ||
| @@ -1174,6 +1227,7 @@ void GMainWindow::OnStartGame() { | |||
| 1174 | ui.action_Report_Compatibility->setEnabled(true); | 1227 | ui.action_Report_Compatibility->setEnabled(true); |
| 1175 | 1228 | ||
| 1176 | discord_rpc->Update(); | 1229 | discord_rpc->Update(); |
| 1230 | ui.action_Load_Amiibo->setEnabled(true); | ||
| 1177 | } | 1231 | } |
| 1178 | 1232 | ||
| 1179 | void GMainWindow::OnPauseGame() { | 1233 | void GMainWindow::OnPauseGame() { |
| @@ -1278,6 +1332,27 @@ void GMainWindow::OnConfigure() { | |||
| 1278 | } | 1332 | } |
| 1279 | } | 1333 | } |
| 1280 | 1334 | ||
| 1335 | void GMainWindow::OnLoadAmiibo() { | ||
| 1336 | const QString extensions{"*.bin"}; | ||
| 1337 | const QString file_filter = tr("Amiibo File (%1);; All Files (*.*)").arg(extensions); | ||
| 1338 | const QString filename = QFileDialog::getOpenFileName(this, tr("Load Amiibo"), "", file_filter); | ||
| 1339 | if (!filename.isEmpty()) { | ||
| 1340 | Core::System& system{Core::System::GetInstance()}; | ||
| 1341 | Service::SM::ServiceManager& sm = system.ServiceManager(); | ||
| 1342 | auto nfc = sm.GetService<Service::NFP::Module::Interface>("nfp:user"); | ||
| 1343 | if (nfc != nullptr) { | ||
| 1344 | auto nfc_file = FileUtil::IOFile(filename.toStdString(), "rb"); | ||
| 1345 | if (!nfc_file.IsOpen()) { | ||
| 1346 | return; | ||
| 1347 | } | ||
| 1348 | std::vector<u8> amiibo_buffer(nfc_file.GetSize()); | ||
| 1349 | nfc_file.ReadBytes(amiibo_buffer.data(), amiibo_buffer.size()); | ||
| 1350 | nfc_file.Close(); | ||
| 1351 | nfc->LoadAmiibo(amiibo_buffer); | ||
| 1352 | } | ||
| 1353 | } | ||
| 1354 | } | ||
| 1355 | |||
| 1281 | void GMainWindow::OnAbout() { | 1356 | void GMainWindow::OnAbout() { |
| 1282 | AboutDialog aboutDialog(this); | 1357 | AboutDialog aboutDialog(this); |
| 1283 | aboutDialog.exec(); | 1358 | aboutDialog.exec(); |
| @@ -1318,15 +1393,17 @@ void GMainWindow::UpdateStatusBar() { | |||
| 1318 | void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string details) { | 1393 | void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string details) { |
| 1319 | QMessageBox::StandardButton answer; | 1394 | QMessageBox::StandardButton answer; |
| 1320 | QString status_message; | 1395 | QString status_message; |
| 1321 | const QString common_message = tr( | 1396 | const QString common_message = |
| 1322 | "The game you are trying to load requires additional files from your Switch to be dumped " | 1397 | 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 " | 1398 | "dumped " |
| 1324 | "following wiki page: <a " | 1399 | "before playing.<br/><br/>For more information on dumping these files, please see the " |
| 1325 | "href='https://yuzu-emu.org/wiki/" | 1400 | "following wiki page: <a " |
| 1326 | "dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System " | 1401 | "href='https://yuzu-emu.org/wiki/" |
| 1327 | "Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to quit " | 1402 | "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 " | 1403 | "Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to " |
| 1329 | "data, or other bugs."); | 1404 | "quit " |
| 1405 | "back to the game list? Continuing emulation may result in crashes, corrupted save " | ||
| 1406 | "data, or other bugs."); | ||
| 1330 | switch (result) { | 1407 | switch (result) { |
| 1331 | case Core::System::ResultStatus::ErrorSystemFiles: { | 1408 | case Core::System::ResultStatus::ErrorSystemFiles: { |
| 1332 | QString message = "yuzu was unable to locate a Switch system archive"; | 1409 | QString message = "yuzu was unable to locate a Switch system archive"; |
| @@ -1357,9 +1434,12 @@ void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string det | |||
| 1357 | this, tr("Fatal Error"), | 1434 | this, tr("Fatal Error"), |
| 1358 | tr("yuzu has encountered a fatal error, please see the log for more details. " | 1435 | 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: " | 1436 | "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 " | 1437 | "<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? " | 1438 | "to " |
| 1362 | "Continuing emulation may result in crashes, corrupted save data, or other bugs."), | 1439 | "Upload the Log File</a>.<br/><br/>Would you like to quit back to the game " |
| 1440 | "list? " | ||
| 1441 | "Continuing emulation may result in crashes, corrupted save data, or other " | ||
| 1442 | "bugs."), | ||
| 1363 | QMessageBox::Yes | QMessageBox::No, QMessageBox::No); | 1443 | QMessageBox::Yes | QMessageBox::No, QMessageBox::No); |
| 1364 | status_message = "Fatal Error encountered"; | 1444 | status_message = "Fatal Error encountered"; |
| 1365 | break; | 1445 | break; |
| @@ -1459,6 +1539,42 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) { | |||
| 1459 | } | 1539 | } |
| 1460 | } | 1540 | } |
| 1461 | 1541 | ||
| 1542 | boost::optional<u64> GMainWindow::SelectRomFSDumpTarget( | ||
| 1543 | const FileSys::RegisteredCacheUnion& installed, u64 program_id) { | ||
| 1544 | const auto dlc_entries = | ||
| 1545 | installed.ListEntriesFilter(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data); | ||
| 1546 | std::vector<FileSys::RegisteredCacheEntry> dlc_match; | ||
| 1547 | dlc_match.reserve(dlc_entries.size()); | ||
| 1548 | std::copy_if(dlc_entries.begin(), dlc_entries.end(), std::back_inserter(dlc_match), | ||
| 1549 | [&program_id, &installed](const FileSys::RegisteredCacheEntry& entry) { | ||
| 1550 | return (entry.title_id & DLC_BASE_TITLE_ID_MASK) == program_id && | ||
| 1551 | installed.GetEntry(entry)->GetStatus() == Loader::ResultStatus::Success; | ||
| 1552 | }); | ||
| 1553 | |||
| 1554 | std::vector<u64> romfs_tids; | ||
| 1555 | romfs_tids.push_back(program_id); | ||
| 1556 | for (const auto& entry : dlc_match) | ||
| 1557 | romfs_tids.push_back(entry.title_id); | ||
| 1558 | |||
| 1559 | if (romfs_tids.size() > 1) { | ||
| 1560 | QStringList list{"Base"}; | ||
| 1561 | for (std::size_t i = 1; i < romfs_tids.size(); ++i) | ||
| 1562 | list.push_back(QStringLiteral("DLC %1").arg(romfs_tids[i] & 0x7FF)); | ||
| 1563 | |||
| 1564 | bool ok; | ||
| 1565 | const auto res = QInputDialog::getItem( | ||
| 1566 | this, tr("Select RomFS Dump Target"), | ||
| 1567 | tr("Please select which RomFS you would like to dump."), list, 0, false, &ok); | ||
| 1568 | if (!ok) { | ||
| 1569 | return boost::none; | ||
| 1570 | } | ||
| 1571 | |||
| 1572 | return romfs_tids[list.indexOf(res)]; | ||
| 1573 | } | ||
| 1574 | |||
| 1575 | return program_id; | ||
| 1576 | } | ||
| 1577 | |||
| 1462 | bool GMainWindow::ConfirmClose() { | 1578 | bool GMainWindow::ConfirmClose() { |
| 1463 | if (emu_thread == nullptr || !UISettings::values.confirm_before_closing) | 1579 | if (emu_thread == nullptr || !UISettings::values.confirm_before_closing) |
| 1464 | return true; | 1580 | 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 dffd9c788..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"> |
| @@ -117,11 +119,14 @@ | |||
| 117 | <addaction name="menu_Tools" /> | 119 | <addaction name="menu_Tools" /> |
| 118 | <addaction name="menu_Help"/> | 120 | <addaction name="menu_Help"/> |
| 119 | </widget> | 121 | </widget> |
| 120 | <action name="action_Install_File_NAND"> | 122 | <action name="action_Install_File_NAND"> |
| 121 | <property name="text"> | 123 | <property name="enabled"> |
| 122 | <string>Install File to NAND...</string> | 124 | <bool>true</bool> |
| 123 | </property> | 125 | </property> |
| 124 | </action> | 126 | <property name="text"> |
| 127 | <string>Install File to NAND...</string> | ||
| 128 | </property> | ||
| 129 | </action> | ||
| 125 | <action name="action_Load_File"> | 130 | <action name="action_Load_File"> |
| 126 | <property name="text"> | 131 | <property name="text"> |
| 127 | <string>Load File...</string> | 132 | <string>Load File...</string> |
| @@ -253,6 +258,14 @@ | |||
| 253 | <string>Restart</string> | 258 | <string>Restart</string> |
| 254 | </property> | 259 | </property> |
| 255 | </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> | ||
| 256 | <action name="action_Report_Compatibility"> | 269 | <action name="action_Report_Compatibility"> |
| 257 | <property name="enabled"> | 270 | <property name="enabled"> |
| 258 | <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 |