Compare commits
1 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5987044918 |
@@ -7,7 +7,7 @@ if grep -nrI '\s$' src *.yml *.txt *.md Doxyfile .gitignore .gitmodules .ci* dis
|
||||
fi
|
||||
|
||||
# Default clang-format points to default 3.5 version one
|
||||
CLANG_FORMAT=clang-format-12
|
||||
CLANG_FORMAT=clang-format-10
|
||||
$CLANG_FORMAT --version
|
||||
|
||||
if [ "$TRAVIS_EVENT_TYPE" = "pull_request" ]; then
|
||||
|
||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -28,6 +28,9 @@
|
||||
[submodule "mbedtls"]
|
||||
path = externals/mbedtls
|
||||
url = https://github.com/yuzu-emu/mbedtls
|
||||
[submodule "libzip"]
|
||||
path = externals/libzip/libzip
|
||||
url = https://github.com/nih-at/libzip.git
|
||||
[submodule "xbyak"]
|
||||
path = externals/xbyak
|
||||
url = https://github.com/herumi/xbyak.git
|
||||
|
||||
@@ -29,10 +29,16 @@ option(YUZU_USE_BUNDLED_FFMPEG "Download/Build bundled FFmpeg" "${WIN32}")
|
||||
|
||||
option(YUZU_USE_QT_WEB_ENGINE "Use QtWebEngine for web applet implementation" OFF)
|
||||
|
||||
option(YUZU_ENABLE_BOXCAT "Enable the Boxcat service, a yuzu high-level implementation of BCAT" ON)
|
||||
|
||||
option(ENABLE_CUBEB "Enables the cubeb audio backend" ON)
|
||||
|
||||
option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF)
|
||||
|
||||
if (NOT ENABLE_WEB_SERVICE)
|
||||
set(YUZU_ENABLE_BOXCAT OFF)
|
||||
endif()
|
||||
|
||||
# Default to a Release build
|
||||
get_property(IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
|
||||
if (NOT IS_MULTI_CONFIG AND NOT CMAKE_BUILD_TYPE)
|
||||
@@ -129,7 +135,7 @@ endif()
|
||||
# boost asio's concept usage doesn't play nicely with some compilers yet.
|
||||
add_definitions(-DBOOST_ASIO_DISABLE_CONCEPTS)
|
||||
if (MSVC)
|
||||
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:/std:c++latest>)
|
||||
add_compile_options(/std:c++latest)
|
||||
|
||||
# cubeb and boost still make use of deprecated result_of.
|
||||
add_definitions(-D_HAS_DEPRECATED_RESULT_OF)
|
||||
@@ -416,6 +422,11 @@ if (CONAN_REQUIRED_LIBS)
|
||||
endif()
|
||||
include(${CMAKE_BINARY_DIR}/conan.cmake)
|
||||
|
||||
set(CONAN_LIB_OPTIONS
|
||||
libzip:with_openssl=False
|
||||
libzip:enable_windows_crypto=False
|
||||
)
|
||||
|
||||
conan_check(VERSION 1.24.0 REQUIRED)
|
||||
|
||||
# Manually add iconv to fix a dep conflict between qt and sdl2
|
||||
@@ -769,7 +780,7 @@ endif()
|
||||
# against all the src files. This should be used before making a pull request.
|
||||
# =======================================================================
|
||||
|
||||
set(CLANG_FORMAT_POSTFIX "-12")
|
||||
set(CLANG_FORMAT_POSTFIX "-10")
|
||||
find_program(CLANG_FORMAT
|
||||
NAMES clang-format${CLANG_FORMAT_POSTFIX}
|
||||
clang-format
|
||||
|
||||
7
externals/CMakeLists.txt
vendored
7
externals/CMakeLists.txt
vendored
@@ -93,6 +93,13 @@ endif()
|
||||
# Sirit
|
||||
add_subdirectory(sirit)
|
||||
|
||||
# libzip
|
||||
find_package(libzip 1.5)
|
||||
if (NOT libzip_FOUND)
|
||||
message(STATUS "libzip 1.5 or newer not found, falling back to externals")
|
||||
add_subdirectory(libzip EXCLUDE_FROM_ALL)
|
||||
endif()
|
||||
|
||||
if (ENABLE_WEB_SERVICE)
|
||||
find_package(OpenSSL 1.1)
|
||||
if (OPENSSL_FOUND)
|
||||
|
||||
72
externals/find-modules/Findlibzip.cmake
vendored
Normal file
72
externals/find-modules/Findlibzip.cmake
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
|
||||
find_package(PkgConfig QUIET)
|
||||
pkg_check_modules(PC_libzip QUIET libzip)
|
||||
|
||||
find_path(libzip_INCLUDE_DIR
|
||||
NAMES zip.h
|
||||
PATHS ${PC_libzip_INCLUDE_DIRS}
|
||||
"$ENV{LIB_DIR}/include"
|
||||
"$ENV{INCLUDE}"
|
||||
/usr/local/include
|
||||
/usr/include
|
||||
)
|
||||
find_path(libzip_INCLUDE_DIR_ZIPCONF
|
||||
NAMES zipconf.h
|
||||
HINTS ${PC_libzip_INCLUDE_DIRS}
|
||||
"$ENV{LIB_DIR}/include"
|
||||
"$ENV{LIB_DIR}/lib/libzip/include"
|
||||
"$ENV{LIB}/lib/libzip/include"
|
||||
/usr/local/lib/libzip/include
|
||||
/usr/lib/libzip/include
|
||||
/usr/local/include
|
||||
/usr/include
|
||||
"$ENV{INCLUDE}"
|
||||
)
|
||||
find_library(libzip_LIBRARY
|
||||
NAMES zip
|
||||
PATHS ${PC_libzip_LIBRARY_DIRS}
|
||||
"$ENV{LIB_DIR}/lib" "$ENV{LIB}" /usr/local/lib /usr/lib
|
||||
)
|
||||
|
||||
if (libzip_INCLUDE_DIR_ZIPCONF)
|
||||
FILE(READ "${libzip_INCLUDE_DIR_ZIPCONF}/zipconf.h" _libzip_VERSION_CONTENTS)
|
||||
if (_libzip_VERSION_CONTENTS)
|
||||
STRING(REGEX REPLACE ".*#define LIBZIP_VERSION \"([0-9.]+)\".*" "\\1" libzip_VERSION "${_libzip_VERSION_CONTENTS}")
|
||||
endif()
|
||||
unset(_libzip_VERSION_CONTENTS)
|
||||
endif()
|
||||
|
||||
set(libzip_VERSION ${libzip_VERSION} CACHE STRING "Version number of libzip")
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(libzip
|
||||
FOUND_VAR libzip_FOUND
|
||||
REQUIRED_VARS
|
||||
libzip_LIBRARY
|
||||
libzip_INCLUDE_DIR
|
||||
libzip_INCLUDE_DIR_ZIPCONF
|
||||
libzip_VERSION
|
||||
VERSION_VAR libzip_VERSION
|
||||
)
|
||||
|
||||
if(libzip_FOUND)
|
||||
set(libzip_LIBRARIES ${libzip_LIBRARY})
|
||||
set(libzip_INCLUDE_DIRS ${libzip_INCLUDE_DIR})
|
||||
set(libzip_DEFINITIONS ${PC_libzip_CFLAGS_OTHER})
|
||||
endif()
|
||||
|
||||
if(libzip_FOUND AND NOT TARGET libzip::libzip)
|
||||
add_library(libzip::libzip UNKNOWN IMPORTED)
|
||||
set_target_properties(libzip::libzip PROPERTIES
|
||||
IMPORTED_LOCATION "${libzip_LIBRARY}"
|
||||
INTERFACE_COMPILE_OPTIONS "${PC_libzip_CFLAGS_OTHER}"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${libzip_INCLUDE_DIR}"
|
||||
)
|
||||
endif()
|
||||
|
||||
mark_as_advanced(
|
||||
libzip_INCLUDE_DIR
|
||||
libzip_INCLUDE_DIR_ZIPCONF
|
||||
libzip_LIBRARY
|
||||
libzip_VERSION
|
||||
)
|
||||
564
externals/libzip/CMakeLists.txt
vendored
Normal file
564
externals/libzip/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,564 @@
|
||||
# TODO:
|
||||
# create usable libtool .la file
|
||||
|
||||
CMAKE_MINIMUM_REQUIRED(VERSION 3.0.2)
|
||||
|
||||
LIST(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/libzip")
|
||||
|
||||
PROJECT(libzip C)
|
||||
|
||||
OPTION(ENABLE_COMMONCRYPTO "Enable use of CommonCrypto" ON)
|
||||
OPTION(ENABLE_GNUTLS "Enable use of GnuTLS" ON)
|
||||
OPTION(ENABLE_MBEDTLS "Enable use of mbed TLS" ON)
|
||||
OPTION(ENABLE_OPENSSL "Enable use of OpenSSL" ON)
|
||||
OPTION(ENABLE_WINDOWS_CRYPTO "Enable use of Windows cryptography libraries" ON)
|
||||
|
||||
OPTION(ENABLE_BZIP2 "Enable use of BZip2" OFF)
|
||||
OPTION(ENABLE_LZMA "Enable use of LZMA" OFF)
|
||||
|
||||
INCLUDE(CheckFunctionExists)
|
||||
INCLUDE(CheckIncludeFiles)
|
||||
INCLUDE(CheckSymbolExists)
|
||||
INCLUDE(CheckTypeSize)
|
||||
INCLUDE(CheckCSourceRuns)
|
||||
INCLUDE(CheckCSourceCompiles)
|
||||
INCLUDE(CheckStructHasMember)
|
||||
INCLUDE(TestBigEndian)
|
||||
INCLUDE(GNUInstallDirs)
|
||||
IF(ENABLE_COMMONCRYPTO)
|
||||
CHECK_INCLUDE_FILES(CommonCrypto/CommonCrypto.h COMMONCRYPTO_FOUND)
|
||||
ELSE()
|
||||
SET(COMMONCRYPTO_FOUND FALSE)
|
||||
ENDIF()
|
||||
IF(ENABLE_GNUTLS)
|
||||
INCLUDE(FindNettle)
|
||||
INCLUDE(FindGnuTLS)
|
||||
ELSE()
|
||||
SET(GNUTLS_FOUND FALSE)
|
||||
ENDIF()
|
||||
IF(ENABLE_MBEDTLS)
|
||||
FIND_PATH(MBEDTLS_INCLUDE_DIR mbedtls/aes.h)
|
||||
FIND_LIBRARY(MBEDTLS_LIBRARIES NAMES mbedcrypto)
|
||||
ELSE()
|
||||
SET(MBEDTLS_LIBRARIES FALSE)
|
||||
ENDIF()
|
||||
IF(ENABLE_OPENSSL)
|
||||
INCLUDE(FindOpenSSL)
|
||||
ELSE()
|
||||
SET(OPENSSL_FOUND FALSE)
|
||||
ENDIF()
|
||||
IF(WIN32)
|
||||
IF(ENABLE_WINDOWS_CRYPTO)
|
||||
SET(WINDOWS_CRYPTO_FOUND TRUE)
|
||||
ENDIF()
|
||||
ELSE()
|
||||
SET(WINDOWS_CRYPTO_FOUND FALSE)
|
||||
ENDIF()
|
||||
|
||||
OPTION(BUILD_SHARED_LIBS "Build shared libraries" ON)
|
||||
OPTION(SHARED_LIB_VERSIONNING "Add SO version in .so build" ON)
|
||||
|
||||
SET(PACKAGE "libzip")
|
||||
SET(PACKAGE_NAME ${PACKAGE})
|
||||
SET(PACKAGE_VERSION_MAJOR "1")
|
||||
SET(PACKAGE_VERSION_MINOR "5")
|
||||
SET(PACKAGE_VERSION_MICRO "2a")
|
||||
#SET(VERSION "${PACKAGE_VERSION_MAJOR}.${PACKAGE_VERSION_MINOR}")
|
||||
SET(VERSION "${PACKAGE_VERSION_MAJOR}.${PACKAGE_VERSION_MINOR}.${PACKAGE_VERSION_MICRO}")
|
||||
SET(PACKAGE_VERSION ${VERSION})
|
||||
SET(LIBZIP_VERSION ${PACKAGE_VERSION})
|
||||
SET(LIBZIP_VERSION_MAJOR ${PACKAGE_VERSION_MAJOR})
|
||||
SET(LIBZIP_VERSION_MINOR ${PACKAGE_VERSION_MINOR})
|
||||
SET(LIBZIP_VERSION_MICRO ${PACKAGE_VERSION_MICRO})
|
||||
SET(PACKAGE_STRING "${PACKAGE_NAME} ${PACKAGE_VERSION}")
|
||||
|
||||
SET(ARCHIVE_NAME ${PACKAGE_NAME}-${PACKAGE_VERSION})
|
||||
IF(NOT TARGET dist)
|
||||
ADD_CUSTOM_TARGET(dist
|
||||
COMMAND git config tar.tar.xz.command "xz -c"
|
||||
COMMAND git archive --prefix=${ARCHIVE_NAME}/ -o ${ARCHIVE_NAME}.tar.gz HEAD
|
||||
COMMAND git archive --prefix=${ARCHIVE_NAME}/ -o ${ARCHIVE_NAME}.tar.xz HEAD
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
)
|
||||
ADD_CUSTOM_TARGET(distcheck
|
||||
COMMAND chmod -R u+w ${ARCHIVE_NAME} ${ARCHIVE_NAME}-build ${ARCHIVE_NAME}-dest 2>/dev/null || true
|
||||
COMMAND rm -rf ${ARCHIVE_NAME} ${ARCHIVE_NAME}-build ${ARCHIVE_NAME}-dest
|
||||
COMMAND cmake -E tar xf ${ARCHIVE_NAME}.tar.gz
|
||||
COMMAND chmod -R u-w ${ARCHIVE_NAME}
|
||||
COMMAND mkdir ${ARCHIVE_NAME}-build
|
||||
COMMAND mkdir ${ARCHIVE_NAME}-dest
|
||||
COMMAND cd ${ARCHIVE_NAME}-build && cmake -DCMAKE_INSTALL_PREFIX=../${ARCHIVE_NAME}-dest ../${ARCHIVE_NAME}
|
||||
COMMAND cd ${ARCHIVE_NAME}-build && make -j4
|
||||
COMMAND cd ${ARCHIVE_NAME}-build && make test
|
||||
COMMAND cd ${ARCHIVE_NAME}-build && make install
|
||||
# COMMAND cd ${ARCHIVE_NAME}-build && make uninstall
|
||||
# COMMAND if [ `find ${ARCHIVE_NAME}-dest ! -type d | wc -l` -ne 0 ]; then echo leftover files in ${ARCHIVE_NAME}-dest; false; fi
|
||||
COMMAND cd ${ARCHIVE_NAME}-build && make clean
|
||||
COMMAND chmod -R u+w ${ARCHIVE_NAME} ${ARCHIVE_NAME}-build ${ARCHIVE_NAME}-dest
|
||||
COMMAND rm -rf ${ARCHIVE_NAME} ${ARCHIVE_NAME}-build ${ARCHIVE_NAME}-dest
|
||||
COMMAND echo "${ARCHIVE_NAME}.tar.gz is ready for distribution."
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
)
|
||||
ADD_DEPENDENCIES(distcheck dist)
|
||||
ENDIF(NOT TARGET dist)
|
||||
|
||||
IF(BUILD_SHARED_LIBS)
|
||||
SET(HAVE_SHARED TRUE)
|
||||
ELSE()
|
||||
SET(ZIP_STATIC TRUE)
|
||||
ENDIF()
|
||||
|
||||
# Checks
|
||||
|
||||
CHECK_FUNCTION_EXISTS(_chmod HAVE__CHMOD)
|
||||
CHECK_FUNCTION_EXISTS(_close HAVE__CLOSE)
|
||||
CHECK_FUNCTION_EXISTS(_dup HAVE__DUP)
|
||||
CHECK_FUNCTION_EXISTS(_fdopen HAVE__FDOPEN)
|
||||
CHECK_FUNCTION_EXISTS(_fileno HAVE__FILENO)
|
||||
CHECK_FUNCTION_EXISTS(_open HAVE__OPEN)
|
||||
CHECK_FUNCTION_EXISTS(_setmode HAVE__SETMODE)
|
||||
CHECK_FUNCTION_EXISTS(_snprintf HAVE__SNPRINTF)
|
||||
CHECK_FUNCTION_EXISTS(_strdup HAVE__STRDUP)
|
||||
CHECK_FUNCTION_EXISTS(_stricmp HAVE__STRICMP)
|
||||
CHECK_FUNCTION_EXISTS(_strtoi64 HAVE__STRTOI64)
|
||||
CHECK_FUNCTION_EXISTS(_strtoui64 HAVE__STRTOUI64)
|
||||
CHECK_FUNCTION_EXISTS(_unlink HAVE__UNLINK)
|
||||
CHECK_FUNCTION_EXISTS(arc4random HAVE_ARC4RANDOM)
|
||||
CHECK_FUNCTION_EXISTS(clonefile HAVE_CLONEFILE)
|
||||
CHECK_FUNCTION_EXISTS(explicit_bzero HAVE_EXPLICIT_BZERO)
|
||||
CHECK_FUNCTION_EXISTS(explicit_memset HAVE_EXPLICIT_MEMSET)
|
||||
CHECK_FUNCTION_EXISTS(fileno HAVE_FILENO)
|
||||
CHECK_FUNCTION_EXISTS(fseeko HAVE_FSEEKO)
|
||||
CHECK_FUNCTION_EXISTS(ftello HAVE_FTELLO)
|
||||
CHECK_FUNCTION_EXISTS(getprogname HAVE_GETPROGNAME)
|
||||
CHECK_FUNCTION_EXISTS(localtime_r HAVE_LOCALTIME_R)
|
||||
CHECK_FUNCTION_EXISTS(open HAVE_OPEN)
|
||||
CHECK_FUNCTION_EXISTS(setmode HAVE_SETMODE)
|
||||
CHECK_FUNCTION_EXISTS(snprintf HAVE_SNPRINTF)
|
||||
CHECK_FUNCTION_EXISTS(strcasecmp HAVE_STRCASECMP)
|
||||
CHECK_FUNCTION_EXISTS(strdup HAVE_STRDUP)
|
||||
CHECK_FUNCTION_EXISTS(stricmp HAVE_STRICMP)
|
||||
CHECK_FUNCTION_EXISTS(strtoll HAVE_STRTOLL)
|
||||
CHECK_FUNCTION_EXISTS(strtoull HAVE_STRTOULL)
|
||||
|
||||
CHECK_INCLUDE_FILES("sys/types.h;sys/stat.h;fts.h" HAVE_FTS_H)
|
||||
CHECK_INCLUDE_FILES(stdbool.h HAVE_STDBOOL_H)
|
||||
CHECK_INCLUDE_FILES(strings.h HAVE_STRINGS_H)
|
||||
CHECK_INCLUDE_FILES(unistd.h HAVE_UNISTD_H)
|
||||
|
||||
CHECK_INCLUDE_FILES(inttypes.h HAVE_INTTYPES_H_LIBZIP)
|
||||
CHECK_INCLUDE_FILES(stdint.h HAVE_STDINT_H_LIBZIP)
|
||||
CHECK_INCLUDE_FILES(sys/types.h HAVE_SYS_TYPES_H_LIBZIP)
|
||||
|
||||
# TODO: fix test
|
||||
# this test does not find __progname even when it exists
|
||||
#CHECK_SYMBOL_EXISTS(__progname stdlib.h HAVE___PROGNAME)
|
||||
|
||||
CHECK_TYPE_SIZE(__int8 __INT8_LIBZIP)
|
||||
CHECK_TYPE_SIZE(int8_t INT8_T_LIBZIP)
|
||||
CHECK_TYPE_SIZE(uint8_t UINT8_T_LIBZIP)
|
||||
CHECK_TYPE_SIZE(__int16 __INT16_LIBZIP)
|
||||
CHECK_TYPE_SIZE(int16_t INT16_T_LIBZIP)
|
||||
CHECK_TYPE_SIZE(uint16_t UINT16_T_LIBZIP)
|
||||
CHECK_TYPE_SIZE(__int32 __INT32_LIBZIP)
|
||||
CHECK_TYPE_SIZE(int32_t INT32_T_LIBZIP)
|
||||
CHECK_TYPE_SIZE(uint32_t UINT32_T_LIBZIP)
|
||||
CHECK_TYPE_SIZE(__int64 __INT64_LIBZIP)
|
||||
CHECK_TYPE_SIZE(int64_t INT64_T_LIBZIP)
|
||||
CHECK_TYPE_SIZE(uint64_t UINT64_T_LIBZIP)
|
||||
CHECK_TYPE_SIZE("short" SHORT_LIBZIP)
|
||||
CHECK_TYPE_SIZE("int" INT_LIBZIP)
|
||||
CHECK_TYPE_SIZE("long" LONG_LIBZIP)
|
||||
CHECK_TYPE_SIZE("long long" LONG_LONG_LIBZIP)
|
||||
CHECK_TYPE_SIZE("off_t" SIZEOF_OFF_T)
|
||||
CHECK_TYPE_SIZE("size_t" SIZE_T_LIBZIP)
|
||||
CHECK_TYPE_SIZE("ssize_t" SSIZE_T_LIBZIP)
|
||||
|
||||
CHECK_C_SOURCE_COMPILES("#include <sys/ioctl.h>
|
||||
#include <linux/fs.h>
|
||||
int main(int argc, char *argv[]) { unsigned long x = FICLONERANGE; }" HAVE_FICLONERANGE)
|
||||
|
||||
CHECK_C_SOURCE_COMPILES("
|
||||
int foo(char * _Nullable bar);
|
||||
int main(int argc, char *argv[]) { }" HAVE_NULLABLE)
|
||||
|
||||
TEST_BIG_ENDIAN(WORDS_BIGENDIAN)
|
||||
|
||||
#FIND_PACKAGE(ZLIB 1.1.2 REQUIRED)
|
||||
INCLUDE_DIRECTORIES(../zlib/zlib)
|
||||
SET(CMAKE_REQUIRED_INCLUDES ../zlib/zlib)
|
||||
|
||||
IF(ENABLE_BZIP2)
|
||||
FIND_PACKAGE(BZip2)
|
||||
IF(BZIP2_FOUND)
|
||||
SET (HAVE_LIBBZ2 1)
|
||||
|
||||
INCLUDE_DIRECTORIES(${BZIP2_INCLUDE_DIR})
|
||||
SET (OPTIONAL_LIBRARY ${OPTIONAL_LIBRARY} ${BZIP2_LIBRARIES})
|
||||
ELSE()
|
||||
MESSAGE(WARNING "-- bzip2 library not found; bzip2 support disabled")
|
||||
ENDIF(BZIP2_FOUND)
|
||||
ENDIF(ENABLE_BZIP2)
|
||||
|
||||
IF(ENABLE_LZMA)
|
||||
FIND_PACKAGE(LibLZMA)
|
||||
IF(LIBLZMA_FOUND)
|
||||
SET (HAVE_LIBLZMA 1)
|
||||
|
||||
INCLUDE_DIRECTORIES(${LIBLZMA_INCLUDE_DIR})
|
||||
SET (OPTIONAL_LIBRARY ${OPTIONAL_LIBRARY} ${LIBLZMA_LIBRARY})
|
||||
ELSE()
|
||||
MESSAGE(WARNING "-- lzma library not found; lzma support disabled")
|
||||
ENDIF(LIBLZMA_FOUND)
|
||||
ENDIF(ENABLE_LZMA)
|
||||
|
||||
|
||||
IF (COMMONCRYPTO_FOUND)
|
||||
SET (HAVE_CRYPTO 1)
|
||||
SET (HAVE_COMMONCRYPTO 1)
|
||||
ELSEIF (WINDOWS_CRYPTO_FOUND)
|
||||
SET (HAVE_CRYPTO 1)
|
||||
SET (HAVE_WINDOWS_CRYPTO 1)
|
||||
ELSEIF (GNUTLS_FOUND AND NETTLE_FOUND)
|
||||
SET (HAVE_CRYPTO 1)
|
||||
SET (HAVE_GNUTLS 1)
|
||||
INCLUDE_DIRECTORIES(${GNUTLS_INCLUDE_DIR} ${NETTLE_INCLUDE_DIR})
|
||||
SET (OPTIONAL_LIBRARY ${OPTIONAL_LIBRARY} ${GNUTLS_LIBRARY} ${NETTLE_LIBRARY})
|
||||
ELSEIF (OPENSSL_FOUND)
|
||||
SET (HAVE_CRYPTO 1)
|
||||
SET (HAVE_OPENSSL 1)
|
||||
INCLUDE_DIRECTORIES(${OPENSSL_INCLUDE_DIR})
|
||||
SET (OPTIONAL_LIBRARY ${OPTIONAL_LIBRARY} ${OPENSSL_LIBRARIES})
|
||||
ELSEIF (MBEDTLS_LIBRARIES)
|
||||
SET (HAVE_CRYPTO 1)
|
||||
SET (HAVE_MBEDTLS 1)
|
||||
INCLUDE_DIRECTORIES(${MBEDTLS_INCLUDE_DIR})
|
||||
SET (OPTIONAL_LIBRARY ${OPTIONAL_LIBRARY} ${MBEDTLS_LIBRARIES})
|
||||
ENDIF()
|
||||
|
||||
IF (NOT HAVE_CRYPTO)
|
||||
MESSAGE(WARNING "-- neither Common Crypto, GnuTLS, mbed TLS, OpenSSL, nor Windows Cryptography found; AES support disabled")
|
||||
ENDIF()
|
||||
|
||||
IF(MSVC)
|
||||
ADD_DEFINITIONS("-D_CRT_SECURE_NO_WARNINGS")
|
||||
ADD_DEFINITIONS("-D_CRT_NONSTDC_NO_DEPRECATE")
|
||||
ENDIF(MSVC)
|
||||
|
||||
if(WIN32)
|
||||
if(HAVE_WINDOWS_CRYPTO)
|
||||
SET (OPTIONAL_LIBRARY ${OPTIONAL_LIBRARY} bcrypt)
|
||||
endif()
|
||||
if(CMAKE_SYSTEM_NAME MATCHES WindowsPhone OR CMAKE_SYSTEM_NAME MATCHES WindowsStore)
|
||||
ADD_DEFINITIONS(-DMS_UWP)
|
||||
else(CMAKE_SYSTEM_NAME MATCHES WindowsPhone OR CMAKE_SYSTEM_NAME MATCHES WindowsStore)
|
||||
SET (OPTIONAL_LIBRARY ${OPTIONAL_LIBRARY} advapi32)
|
||||
endif(CMAKE_SYSTEM_NAME MATCHES WindowsPhone OR CMAKE_SYSTEM_NAME MATCHES WindowsStore)
|
||||
endif(WIN32)
|
||||
|
||||
ADD_DEFINITIONS("-DHAVE_CONFIG_H")
|
||||
|
||||
# rpath handling: use rpath in installed binaries
|
||||
IF(NOT CMAKE_SYSTEM_NAME MATCHES Linux)
|
||||
SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}")
|
||||
SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
|
||||
ENDIF()
|
||||
|
||||
# fixed size integral types
|
||||
|
||||
IF(HAVE_INTTYPES_H_LIBZIP)
|
||||
SET(LIBZIP_TYPES_INCLUDE "#define __STDC_FORMAT_MACROS 1
|
||||
#include <inttypes.h>")
|
||||
ELSEIF(HAVE_STDINT_H_LIBZIP)
|
||||
SET(LIBZIP_TYPES_INCLUDE "#include <stdint.h>")
|
||||
ELSEIF(HAVE_SYS_TYPES_H_LIBZIP)
|
||||
SET(LIBZIP_TYPES_INCLUDE "#include <sys/types.h>")
|
||||
ENDIF()
|
||||
|
||||
IF(HAVE_INT8_T_LIBZIP)
|
||||
SET(ZIP_INT8_T int8_t)
|
||||
ELSEIF(HAVE___INT8_LIBZIP)
|
||||
SET(ZIP_INT8_T __int8)
|
||||
ELSE()
|
||||
SET(ZIP_INT8_T "signed char")
|
||||
ENDIF()
|
||||
|
||||
IF(HAVE_UINT8_T_LIBZIP)
|
||||
SET(ZIP_UINT8_T uint8_t)
|
||||
ELSEIF(HAVE___INT8_LIBZIP)
|
||||
SET(ZIP_UINT8_T "unsigned __int8")
|
||||
ELSE()
|
||||
SET(ZIP_UINT8_T "unsigned char")
|
||||
ENDIF()
|
||||
|
||||
IF(HAVE_INT16_T_LIBZIP)
|
||||
SET(ZIP_INT16_T int16_t)
|
||||
ELSEIF(HAVE___INT16_LIBZIP)
|
||||
SET(INT16_T_LIBZIP __int16)
|
||||
ELSEIF(SHORT_LIBZIP EQUAL 2)
|
||||
SET(INT16_T_LIBZIP short)
|
||||
ENDIF()
|
||||
|
||||
IF(HAVE_UINT16_T_LIBZIP)
|
||||
SET(ZIP_UINT16_T uint16_t)
|
||||
ELSEIF(HAVE___INT16_LIBZIP)
|
||||
SET(UINT16_T_LIBZIP "unsigned __int16")
|
||||
ELSEIF(SHORT_LIBZIP EQUAL 2)
|
||||
SET(UINT16_T_LIBZIP "unsigned short")
|
||||
ENDIF()
|
||||
|
||||
IF(HAVE_INT32_T_LIBZIP)
|
||||
SET(ZIP_INT32_T int32_t)
|
||||
ELSEIF(HAVE___INT32_LIBZIP)
|
||||
SET(ZIP_INT32_T __int32)
|
||||
ELSEIF(INT_LIBZIP EQUAL 4)
|
||||
SET(ZIP_INT32_T int)
|
||||
ELSEIF(LONG_LIBZIP EQUAL 4)
|
||||
SET(ZIP_INT32_T long)
|
||||
ENDIF()
|
||||
|
||||
IF(HAVE_UINT32_T_LIBZIP)
|
||||
SET(ZIP_UINT32_T uint32_t)
|
||||
ELSEIF(HAVE___INT32_LIBZIP)
|
||||
SET(ZIP_UINT32_T "unsigned __int32")
|
||||
ELSEIF(INT_LIBZIP EQUAL 4)
|
||||
SET(ZIP_UINT32_T "unsigned int")
|
||||
ELSEIF(LONG_LIBZIP EQUAL 4)
|
||||
SET(ZIP_UINT32_T "unsigned long")
|
||||
ENDIF()
|
||||
|
||||
IF(HAVE_INT64_T_LIBZIP)
|
||||
SET(ZIP_INT64_T int64_t)
|
||||
ELSEIF(HAVE___INT64_LIBZIP)
|
||||
SET(ZIP_INT64_T __int64)
|
||||
ELSEIF(LONG_LIBZIP EQUAL 8)
|
||||
SET(ZIP_INT64_T long)
|
||||
ELSEIF(LONG_LONG_LIBZIP EQUAL 8)
|
||||
SET(ZIP_INT64_T "long long")
|
||||
ENDIF()
|
||||
|
||||
IF(HAVE_UINT64_T_LIBZIP)
|
||||
SET(ZIP_UINT64_T uint64_t)
|
||||
ELSEIF(HAVE___INT64_LIBZIP)
|
||||
SET(ZIP_UINT64_T "unsigned __int64")
|
||||
ELSEIF(LONG_LIBZIP EQUAL 8)
|
||||
SET(ZIP_UINT64_T "unsigned long")
|
||||
ELSEIF(LONG_LONG_LIBZIP EQUAL 8)
|
||||
SET(ZIP_UINT64_T "unsigned long long")
|
||||
ENDIF()
|
||||
|
||||
IF(HAVE_NULLABLE)
|
||||
SET(ZIP_NULLABLE_DEFINES)
|
||||
ELSE()
|
||||
SET(ZIP_NULLABLE_DEFINES "#define _Nullable
|
||||
#define _Nonnull")
|
||||
ENDIF()
|
||||
|
||||
# write out config file
|
||||
CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/libzip/cmake-config.h.in ${CMAKE_CURRENT_BINARY_DIR}/libzip/config.h)
|
||||
CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/libzip/cmake-zipconf.h.in ${CMAKE_CURRENT_BINARY_DIR}/libzip/zipconf.h)
|
||||
|
||||
# installation
|
||||
INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/libzip/zipconf.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
|
||||
INSTALL(FILES libzip/lib/zip.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
|
||||
|
||||
SET(CMAKE_C_VISIBILITY_PRESET hidden)
|
||||
|
||||
ADD_LIBRARY(zip
|
||||
libzip/lib/zip_add.c
|
||||
libzip/lib/zip_add_dir.c
|
||||
libzip/lib/zip_add_entry.c
|
||||
libzip/lib/zip_algorithm_deflate.c
|
||||
libzip/lib/zip_buffer.c
|
||||
libzip/lib/zip_close.c
|
||||
libzip/lib/zip_delete.c
|
||||
libzip/lib/zip_dir_add.c
|
||||
libzip/lib/zip_dirent.c
|
||||
libzip/lib/zip_discard.c
|
||||
libzip/lib/zip_entry.c
|
||||
libzip/lib/zip_err_str.c
|
||||
libzip/lib/zip_error.c
|
||||
libzip/lib/zip_error_clear.c
|
||||
libzip/lib/zip_error_get.c
|
||||
libzip/lib/zip_error_get_sys_type.c
|
||||
libzip/lib/zip_error_strerror.c
|
||||
libzip/lib/zip_error_to_str.c
|
||||
libzip/lib/zip_extra_field.c
|
||||
libzip/lib/zip_extra_field_api.c
|
||||
libzip/lib/zip_fclose.c
|
||||
libzip/lib/zip_fdopen.c
|
||||
libzip/lib/zip_file_add.c
|
||||
libzip/lib/zip_file_error_clear.c
|
||||
libzip/lib/zip_file_error_get.c
|
||||
libzip/lib/zip_file_get_comment.c
|
||||
libzip/lib/zip_file_get_external_attributes.c
|
||||
libzip/lib/zip_file_get_offset.c
|
||||
libzip/lib/zip_file_rename.c
|
||||
libzip/lib/zip_file_replace.c
|
||||
libzip/lib/zip_file_set_comment.c
|
||||
libzip/lib/zip_file_set_encryption.c
|
||||
libzip/lib/zip_file_set_external_attributes.c
|
||||
libzip/lib/zip_file_set_mtime.c
|
||||
libzip/lib/zip_file_strerror.c
|
||||
libzip/lib/zip_filerange_crc.c
|
||||
libzip/lib/zip_fopen.c
|
||||
libzip/lib/zip_fopen_encrypted.c
|
||||
libzip/lib/zip_fopen_index.c
|
||||
libzip/lib/zip_fopen_index_encrypted.c
|
||||
libzip/lib/zip_fread.c
|
||||
libzip/lib/zip_fseek.c
|
||||
libzip/lib/zip_ftell.c
|
||||
libzip/lib/zip_get_archive_comment.c
|
||||
libzip/lib/zip_get_archive_flag.c
|
||||
libzip/lib/zip_get_encryption_implementation.c
|
||||
libzip/lib/zip_get_file_comment.c
|
||||
libzip/lib/zip_get_name.c
|
||||
libzip/lib/zip_get_num_entries.c
|
||||
libzip/lib/zip_get_num_files.c
|
||||
libzip/lib/zip_hash.c
|
||||
libzip/lib/zip_io_util.c
|
||||
libzip/lib/zip_libzip_version.c
|
||||
libzip/lib/zip_memdup.c
|
||||
libzip/lib/zip_name_locate.c
|
||||
libzip/lib/zip_new.c
|
||||
libzip/lib/zip_open.c
|
||||
libzip/lib/zip_progress.c
|
||||
libzip/lib/zip_rename.c
|
||||
libzip/lib/zip_replace.c
|
||||
libzip/lib/zip_set_archive_comment.c
|
||||
libzip/lib/zip_set_archive_flag.c
|
||||
libzip/lib/zip_set_default_password.c
|
||||
libzip/lib/zip_set_file_comment.c
|
||||
libzip/lib/zip_set_file_compression.c
|
||||
libzip/lib/zip_set_name.c
|
||||
libzip/lib/zip_source_accept_empty.c
|
||||
libzip/lib/zip_source_begin_write.c
|
||||
libzip/lib/zip_source_begin_write_cloning.c
|
||||
libzip/lib/zip_source_buffer.c
|
||||
libzip/lib/zip_source_call.c
|
||||
libzip/lib/zip_source_close.c
|
||||
libzip/lib/zip_source_commit_write.c
|
||||
libzip/lib/zip_source_compress.c
|
||||
libzip/lib/zip_source_crc.c
|
||||
libzip/lib/zip_source_error.c
|
||||
libzip/lib/zip_source_filep.c
|
||||
libzip/lib/zip_source_free.c
|
||||
libzip/lib/zip_source_function.c
|
||||
libzip/lib/zip_source_get_compression_flags.c
|
||||
libzip/lib/zip_source_is_deleted.c
|
||||
libzip/lib/zip_source_layered.c
|
||||
libzip/lib/zip_source_open.c
|
||||
libzip/lib/zip_source_pkware.c
|
||||
libzip/lib/zip_source_read.c
|
||||
libzip/lib/zip_source_remove.c
|
||||
libzip/lib/zip_source_rollback_write.c
|
||||
libzip/lib/zip_source_seek.c
|
||||
libzip/lib/zip_source_seek_write.c
|
||||
libzip/lib/zip_source_stat.c
|
||||
libzip/lib/zip_source_supports.c
|
||||
libzip/lib/zip_source_tell.c
|
||||
libzip/lib/zip_source_tell_write.c
|
||||
libzip/lib/zip_source_window.c
|
||||
libzip/lib/zip_source_write.c
|
||||
libzip/lib/zip_source_zip.c
|
||||
libzip/lib/zip_source_zip_new.c
|
||||
libzip/lib/zip_stat.c
|
||||
libzip/lib/zip_stat_index.c
|
||||
libzip/lib/zip_stat_init.c
|
||||
libzip/lib/zip_strerror.c
|
||||
libzip/lib/zip_string.c
|
||||
libzip/lib/zip_unchange.c
|
||||
libzip/lib/zip_unchange_all.c
|
||||
libzip/lib/zip_unchange_archive.c
|
||||
libzip/lib/zip_unchange_data.c
|
||||
libzip/lib/zip_utf-8.c
|
||||
)
|
||||
|
||||
IF(WIN32)
|
||||
target_sources(zip PRIVATE
|
||||
libzip/lib/zip_source_win32handle.c
|
||||
libzip/lib/zip_source_win32utf8.c
|
||||
libzip/lib/zip_source_win32w.c
|
||||
)
|
||||
IF(CMAKE_SYSTEM_NAME MATCHES WindowsPhone OR CMAKE_SYSTEM_NAME MATCHES WindowsStore)
|
||||
ELSE()
|
||||
target_sources(zip PRIVATE libzip/lib/zip_source_win32a.c)
|
||||
ENDIF()
|
||||
ELSE()
|
||||
target_sources(zip PRIVATE
|
||||
libzip/lib/zip_mkstempm.c
|
||||
libzip/lib/zip_source_file.c
|
||||
libzip/lib/zip_random_unix.c
|
||||
)
|
||||
ENDIF()
|
||||
|
||||
IF(HAVE_LIBBZ2)
|
||||
target_sources(zip PRIVATE libzip/lib/zip_algorithm_bzip2.c)
|
||||
ENDIF()
|
||||
|
||||
IF(HAVE_LIBLZMA)
|
||||
target_sources(zip PRIVATE libzip/lib/zip_algorithm_xz.c)
|
||||
ENDIF()
|
||||
|
||||
IF(HAVE_COMMONCRYPTO)
|
||||
target_sources(zip PRIVATE libzip/lib/zip_crypto_commoncrypto.c)
|
||||
ELSEIF(HAVE_WINDOWS_CRYPTO)
|
||||
target_sources(zip PRIVATE libzip/lib/zip_crypto_win.c)
|
||||
ELSEIF(HAVE_GNUTLS)
|
||||
target_sources(zip PRIVATE libzip/lib/zip_crypto_gnutls.c)
|
||||
ELSEIF(HAVE_OPENSSL)
|
||||
target_sources(zip PRIVATE libzip/lib/zip_crypto_openssl.c)
|
||||
ELSEIF(HAVE_MBEDTLS)
|
||||
target_sources(zip PRIVATE libzip/lib/zip_crypto_mbedtls.c)
|
||||
ENDIF()
|
||||
|
||||
IF(HAVE_CRYPTO)
|
||||
target_sources(zip PRIVATE
|
||||
libzip/lib/zip_winzip_aes.c
|
||||
libzip/lib/zip_source_winzip_aes_decode.c
|
||||
libzip/lib/zip_source_winzip_aes_encode.c
|
||||
)
|
||||
ENDIF()
|
||||
|
||||
target_include_directories(zip
|
||||
PUBLIC
|
||||
libzip/lib
|
||||
${CMAKE_CURRENT_BINARY_DIR}/libzip
|
||||
)
|
||||
|
||||
# pkgconfig file
|
||||
SET(prefix ${CMAKE_INSTALL_PREFIX})
|
||||
SET(exec_prefix \${prefix})
|
||||
SET(bindir \${exec_prefix}/${CMAKE_INSTALL_BINDIR})
|
||||
SET(libdir \${exec_prefix}/${CMAKE_INSTALL_LIBDIR})
|
||||
SET(includedir \${prefix}/${CMAKE_INSTALL_INCLUDEDIR})
|
||||
IF(CMAKE_SYSTEM_NAME MATCHES BSD)
|
||||
SET(PKG_CONFIG_RPATH "-Wl,-R\${libdir}")
|
||||
ENDIF(CMAKE_SYSTEM_NAME MATCHES BSD)
|
||||
get_target_property(LIBS_PRIVATE zip LINK_LIBRARIES)
|
||||
foreach(LIB ${LIBS_PRIVATE})
|
||||
if(LIB MATCHES "^/")
|
||||
get_filename_component(LIB ${LIB} NAME_WE)
|
||||
string(REGEX REPLACE "^lib" "" LIB ${LIB})
|
||||
endif()
|
||||
set(LIBS "${LIBS} -l${LIB}")
|
||||
endforeach()
|
||||
CONFIGURE_FILE(libzip/libzip.pc.in libzip/libzip.pc @ONLY)
|
||||
INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/libzip.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
|
||||
|
||||
ADD_CUSTOM_TARGET(update_zip_err_str
|
||||
COMMAND sh ${CMAKE_CURRENT_SOURCE_DIR}/libzip/lib/make_zip_err_str.sh ${CMAKE_CURRENT_SOURCE_DIR}/libzip/lib/zip.h ${CMAKE_CURRENT_SOURCE_DIR}/libzip/lib/zip_err_str.c
|
||||
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/libzip/lib/zip.h ${CMAKE_CURRENT_SOURCE_DIR}/libzip/lib/make_zip_err_str.sh
|
||||
)
|
||||
|
||||
IF(SHARED_LIB_VERSIONNING)
|
||||
SET_TARGET_PROPERTIES(zip PROPERTIES VERSION 5.0 SOVERSION 5)
|
||||
ENDIF()
|
||||
|
||||
TARGET_LINK_LIBRARIES(zip ${ZLIB_LIBRARIES} ${OPTIONAL_LIBRARY})
|
||||
INSTALL(TARGETS zip
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
)
|
||||
|
||||
1
externals/libzip/libzip
vendored
Submodule
1
externals/libzip/libzip
vendored
Submodule
Submodule externals/libzip/libzip added at 89bd6d63bd
@@ -9,48 +9,41 @@
|
||||
namespace Common {
|
||||
|
||||
template <typename T>
|
||||
requires std::is_unsigned_v<T>
|
||||
[[nodiscard]] constexpr T AlignUp(T value, size_t size) {
|
||||
requires std::is_unsigned_v<T>[[nodiscard]] constexpr T AlignUp(T value, size_t size) {
|
||||
auto mod{static_cast<T>(value % size)};
|
||||
value -= mod;
|
||||
return static_cast<T>(mod == T{0} ? value : value + size);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
requires std::is_unsigned_v<T>
|
||||
[[nodiscard]] constexpr T AlignUpLog2(T value, size_t align_log2) {
|
||||
requires std::is_unsigned_v<T>[[nodiscard]] constexpr T AlignUpLog2(T value, size_t align_log2) {
|
||||
return static_cast<T>((value + ((1ULL << align_log2) - 1)) >> align_log2 << align_log2);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
requires std::is_unsigned_v<T>
|
||||
[[nodiscard]] constexpr T AlignDown(T value, size_t size) {
|
||||
requires std::is_unsigned_v<T>[[nodiscard]] constexpr T AlignDown(T value, size_t size) {
|
||||
return static_cast<T>(value - value % size);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
requires std::is_unsigned_v<T>
|
||||
[[nodiscard]] constexpr bool Is4KBAligned(T value) {
|
||||
requires std::is_unsigned_v<T>[[nodiscard]] constexpr bool Is4KBAligned(T value) {
|
||||
return (value & 0xFFF) == 0;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
requires std::is_unsigned_v<T>
|
||||
[[nodiscard]] constexpr bool IsWordAligned(T value) {
|
||||
requires std::is_unsigned_v<T>[[nodiscard]] constexpr bool IsWordAligned(T value) {
|
||||
return (value & 0b11) == 0;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
requires std::is_integral_v<T>
|
||||
[[nodiscard]] constexpr bool IsAligned(T value, size_t alignment) {
|
||||
requires std::is_integral_v<T>[[nodiscard]] constexpr bool IsAligned(T value, size_t alignment) {
|
||||
using U = typename std::make_unsigned_t<T>;
|
||||
const U mask = static_cast<U>(alignment - 1);
|
||||
return (value & mask) == 0;
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
requires std::is_integral_v<T>
|
||||
[[nodiscard]] constexpr T DivideUp(T x, U y) {
|
||||
requires std::is_integral_v<T>[[nodiscard]] constexpr T DivideUp(T x, U y) {
|
||||
return (x + (y - 1)) / y;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,15 +11,15 @@ namespace Common {
|
||||
|
||||
/// Ceiled integer division.
|
||||
template <typename N, typename D>
|
||||
requires std::is_integral_v<N> && std::is_unsigned_v<D>
|
||||
[[nodiscard]] constexpr N DivCeil(N number, D divisor) {
|
||||
requires std::is_integral_v<N>&& std::is_unsigned_v<D>[[nodiscard]] constexpr N DivCeil(N number,
|
||||
D divisor) {
|
||||
return static_cast<N>((static_cast<D>(number) + divisor - 1) / divisor);
|
||||
}
|
||||
|
||||
/// Ceiled integer division with logarithmic divisor in base 2
|
||||
template <typename N, typename D>
|
||||
requires std::is_integral_v<N> && std::is_unsigned_v<D>
|
||||
[[nodiscard]] constexpr N DivCeilLog2(N value, D alignment_log2) {
|
||||
requires std::is_integral_v<N>&& std::is_unsigned_v<D>[[nodiscard]] constexpr N DivCeilLog2(
|
||||
N value, D alignment_log2) {
|
||||
return static_cast<N>((static_cast<D>(value) + (D(1) << alignment_log2) - 1) >> alignment_log2);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
#define SCREENSHOTS_DIR "screenshots"
|
||||
#define SDMC_DIR "sdmc"
|
||||
#define SHADER_DIR "shader"
|
||||
#define TAS_DIR "tas"
|
||||
|
||||
// yuzu-specific files
|
||||
|
||||
|
||||
@@ -116,7 +116,6 @@ private:
|
||||
GenerateYuzuPath(YuzuPath::ScreenshotsDir, yuzu_path / SCREENSHOTS_DIR);
|
||||
GenerateYuzuPath(YuzuPath::SDMCDir, yuzu_path / SDMC_DIR);
|
||||
GenerateYuzuPath(YuzuPath::ShaderDir, yuzu_path / SHADER_DIR);
|
||||
GenerateYuzuPath(YuzuPath::TASDir, yuzu_path / TAS_DIR);
|
||||
}
|
||||
|
||||
~PathManagerImpl() = default;
|
||||
|
||||
@@ -23,7 +23,6 @@ enum class YuzuPath {
|
||||
ScreenshotsDir, // Where yuzu screenshots are stored.
|
||||
SDMCDir, // Where the emulated SDMC is stored.
|
||||
ShaderDir, // Where shaders are stored.
|
||||
TASDir, // Where TAS scripts are stored.
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -235,19 +235,20 @@ public:
|
||||
|
||||
template <typename T>
|
||||
concept HasLightCompareType = requires {
|
||||
{ std::is_same<typename T::LightCompareType, void>::value } -> std::convertible_to<bool>;
|
||||
{ std::is_same<typename T::LightCompareType, void>::value }
|
||||
->std::convertible_to<bool>;
|
||||
};
|
||||
|
||||
namespace impl {
|
||||
|
||||
template <typename T, typename Default>
|
||||
consteval auto* GetLightCompareType() {
|
||||
if constexpr (HasLightCompareType<T>) {
|
||||
return static_cast<typename T::LightCompareType*>(nullptr);
|
||||
} else {
|
||||
return static_cast<Default*>(nullptr);
|
||||
}
|
||||
template <typename T, typename Default>
|
||||
consteval auto* GetLightCompareType() {
|
||||
if constexpr (HasLightCompareType<T>) {
|
||||
return static_cast<typename T::LightCompareType*>(nullptr);
|
||||
} else {
|
||||
return static_cast<Default*>(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace impl
|
||||
|
||||
|
||||
@@ -61,6 +61,7 @@ void LogSettings() {
|
||||
log_setting("Renderer_UseAsynchronousShaders", values.use_asynchronous_shaders.GetValue());
|
||||
log_setting("Renderer_AnisotropicFilteringLevel", values.max_anisotropy.GetValue());
|
||||
log_setting("Audio_OutputEngine", values.sink_id.GetValue());
|
||||
log_setting("Audio_EnableAudioStretching", values.enable_audio_stretching.GetValue());
|
||||
log_setting("Audio_OutputDevice", values.audio_device_id.GetValue());
|
||||
log_setting("DataStorage_UseVirtualSd", values.use_virtual_sd.GetValue());
|
||||
log_path("DataStorage_CacheDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir));
|
||||
@@ -69,9 +70,8 @@ void LogSettings() {
|
||||
log_path("DataStorage_NANDDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir));
|
||||
log_path("DataStorage_SDMCDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::SDMCDir));
|
||||
log_setting("Debugging_ProgramArgs", values.program_args.GetValue());
|
||||
log_setting("Input_EnableMotion", values.motion_enabled.GetValue());
|
||||
log_setting("Input_EnableVibration", values.vibration_enabled.GetValue());
|
||||
log_setting("Input_EnableRawInput", values.enable_raw_input.GetValue());
|
||||
log_setting("Services_BCATBackend", values.bcat_backend.GetValue());
|
||||
log_setting("Services_BCATBoxcatLocal", values.bcat_boxcat_local.GetValue());
|
||||
}
|
||||
|
||||
bool IsConfiguringGlobal() {
|
||||
@@ -112,6 +112,7 @@ void RestoreGlobalState(bool is_powered_on) {
|
||||
}
|
||||
|
||||
// Audio
|
||||
values.enable_audio_stretching.SetGlobal(true);
|
||||
values.volume.SetGlobal(true);
|
||||
|
||||
// Core
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/settings_input.h"
|
||||
#include "input_common/udp/client.h"
|
||||
|
||||
namespace Settings {
|
||||
|
||||
@@ -414,6 +415,7 @@ struct Values {
|
||||
BasicSetting<std::string> audio_device_id{"auto", "output_device"};
|
||||
BasicSetting<std::string> sink_id{"auto", "output_engine"};
|
||||
BasicSetting<bool> audio_muted{false, "audio_muted"};
|
||||
Setting<bool> enable_audio_stretching{true, "enable_audio_stretching"};
|
||||
RangedSetting<u8> volume{100, 0, 100, "volume"};
|
||||
|
||||
// Core
|
||||
@@ -502,20 +504,14 @@ struct Values {
|
||||
|
||||
Setting<bool> use_docked_mode{true, "use_docked_mode"};
|
||||
|
||||
BasicSetting<bool> enable_raw_input{false, "enable_raw_input"};
|
||||
|
||||
Setting<bool> vibration_enabled{true, "vibration_enabled"};
|
||||
Setting<bool> enable_accurate_vibrations{false, "enable_accurate_vibrations"};
|
||||
|
||||
Setting<bool> motion_enabled{true, "motion_enabled"};
|
||||
BasicSetting<std::string> motion_device{"engine:motion_emu,update_period:100,sensitivity:0.01",
|
||||
"motion_device"};
|
||||
BasicSetting<std::string> udp_input_servers{"127.0.0.1:26760", "udp_input_servers"};
|
||||
|
||||
BasicSetting<bool> pause_tas_on_load{true, "pause_tas_on_load"};
|
||||
BasicSetting<bool> tas_enable{false, "tas_enable"};
|
||||
BasicSetting<bool> tas_loop{false, "tas_loop"};
|
||||
BasicSetting<bool> tas_swap_controllers{true, "tas_swap_controllers"};
|
||||
BasicSetting<std::string> udp_input_servers{InputCommon::CemuhookUDP::DEFAULT_SRV,
|
||||
"udp_input_servers"};
|
||||
|
||||
BasicSetting<bool> mouse_panning{false, "mouse_panning"};
|
||||
BasicRangedSetting<u8> mouse_panning_sensitivity{10, 1, 100, "mouse_panning_sensitivity"};
|
||||
@@ -568,6 +564,8 @@ struct Values {
|
||||
BasicSetting<bool> use_dev_keys{false, "use_dev_keys"};
|
||||
|
||||
// Network
|
||||
BasicSetting<std::string> bcat_backend{"none", "bcat_backend"};
|
||||
BasicSetting<bool> bcat_boxcat_local{false, "bcat_boxcat_local"};
|
||||
BasicSetting<std::string> network_interface{std::string(), "network_interface"};
|
||||
|
||||
// WebService
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
#include <utility>
|
||||
|
||||
namespace Common {
|
||||
template <typename T, bool with_stop_token = false>
|
||||
template <typename T>
|
||||
class SPSCQueue {
|
||||
public:
|
||||
SPSCQueue() {
|
||||
@@ -84,7 +84,7 @@ public:
|
||||
void Wait() {
|
||||
if (Empty()) {
|
||||
std::unique_lock lock{cv_mutex};
|
||||
cv.wait(lock, [this] { return !Empty(); });
|
||||
cv.wait(lock, [this]() { return !Empty(); });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,19 +95,6 @@ public:
|
||||
return t;
|
||||
}
|
||||
|
||||
T PopWait(std::stop_token stop_token) {
|
||||
if (Empty()) {
|
||||
std::unique_lock lock{cv_mutex};
|
||||
cv.wait(lock, stop_token, [this] { return !Empty(); });
|
||||
}
|
||||
if (stop_token.stop_requested()) {
|
||||
return T{};
|
||||
}
|
||||
T t;
|
||||
Pop(t);
|
||||
return t;
|
||||
}
|
||||
|
||||
// not thread-safe
|
||||
void Clear() {
|
||||
size.store(0);
|
||||
@@ -136,13 +123,13 @@ private:
|
||||
ElementPtr* read_ptr;
|
||||
std::atomic_size_t size{0};
|
||||
std::mutex cv_mutex;
|
||||
std::conditional_t<with_stop_token, std::condition_variable_any, std::condition_variable> cv;
|
||||
std::condition_variable cv;
|
||||
};
|
||||
|
||||
// a simple thread-safe,
|
||||
// single reader, multiple writer queue
|
||||
|
||||
template <typename T, bool with_stop_token = false>
|
||||
template <typename T>
|
||||
class MPSCQueue {
|
||||
public:
|
||||
[[nodiscard]] std::size_t Size() const {
|
||||
@@ -179,17 +166,13 @@ public:
|
||||
return spsc_queue.PopWait();
|
||||
}
|
||||
|
||||
T PopWait(std::stop_token stop_token) {
|
||||
return spsc_queue.PopWait(stop_token);
|
||||
}
|
||||
|
||||
// not thread-safe
|
||||
void Clear() {
|
||||
spsc_queue.Clear();
|
||||
}
|
||||
|
||||
private:
|
||||
SPSCQueue<T, with_stop_token> spsc_queue;
|
||||
SPSCQueue<T> spsc_queue;
|
||||
std::mutex write_lock;
|
||||
};
|
||||
} // namespace Common
|
||||
|
||||
@@ -58,13 +58,6 @@ struct UUID {
|
||||
uuid = INVALID_UUID;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool IsInvalid() const {
|
||||
return uuid == INVALID_UUID;
|
||||
}
|
||||
[[nodiscard]] constexpr bool IsValid() const {
|
||||
return !IsInvalid();
|
||||
}
|
||||
|
||||
// TODO(ogniK): Properly generate a Nintendo ID
|
||||
[[nodiscard]] constexpr u64 GetNintendoID() const {
|
||||
return uuid[0];
|
||||
|
||||
@@ -667,8 +667,8 @@ template <typename T>
|
||||
|
||||
// linear interpolation via float: 0.0=begin, 1.0=end
|
||||
template <typename X>
|
||||
[[nodiscard]] constexpr decltype(X{} * float{} + X{} * float{})
|
||||
Lerp(const X& begin, const X& end, const float t) {
|
||||
[[nodiscard]] constexpr decltype(X{} * float{} + X{} * float{}) Lerp(const X& begin, const X& end,
|
||||
const float t) {
|
||||
return begin * (1.f - t) + end * t;
|
||||
}
|
||||
|
||||
|
||||
@@ -106,6 +106,8 @@ add_library(core STATIC
|
||||
file_sys/vfs_concat.h
|
||||
file_sys/vfs_layered.cpp
|
||||
file_sys/vfs_layered.h
|
||||
file_sys/vfs_libzip.cpp
|
||||
file_sys/vfs_libzip.h
|
||||
file_sys/vfs_offset.cpp
|
||||
file_sys/vfs_offset.h
|
||||
file_sys/vfs_real.cpp
|
||||
@@ -651,6 +653,13 @@ add_library(core STATIC
|
||||
tools/freezer.h
|
||||
)
|
||||
|
||||
if (YUZU_ENABLE_BOXCAT)
|
||||
target_sources(core PRIVATE
|
||||
hle/service/bcat/backend/boxcat.cpp
|
||||
hle/service/bcat/backend/boxcat.h
|
||||
)
|
||||
endif()
|
||||
|
||||
if (MSVC)
|
||||
target_compile_options(core PRIVATE
|
||||
/we4242 # 'identifier': conversion from 'type1' to 'type2', possible loss of data
|
||||
@@ -681,7 +690,12 @@ endif()
|
||||
create_target_directory_groups(core)
|
||||
|
||||
target_link_libraries(core PUBLIC common PRIVATE audio_core video_core)
|
||||
target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls Opus::Opus)
|
||||
target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls Opus::Opus zip)
|
||||
|
||||
if (YUZU_ENABLE_BOXCAT)
|
||||
target_compile_definitions(core PRIVATE -DYUZU_ENABLE_BOXCAT)
|
||||
target_link_libraries(core PRIVATE httplib nlohmann_json::nlohmann_json)
|
||||
endif()
|
||||
|
||||
if (ENABLE_WEB_SERVICE)
|
||||
target_compile_definitions(core PRIVATE -DENABLE_WEB_SERVICE)
|
||||
|
||||
@@ -305,6 +305,10 @@ struct System::Impl {
|
||||
is_powered_on = false;
|
||||
exit_lock = false;
|
||||
|
||||
if (gpu_core) {
|
||||
gpu_core->ShutDown();
|
||||
}
|
||||
|
||||
services.reset();
|
||||
service_manager.reset();
|
||||
cheat_engine.reset();
|
||||
@@ -313,8 +317,8 @@ struct System::Impl {
|
||||
time_manager.Shutdown();
|
||||
core_timing.Shutdown();
|
||||
app_loader.reset();
|
||||
perf_stats.reset();
|
||||
gpu_core.reset();
|
||||
perf_stats.reset();
|
||||
kernel.Shutdown();
|
||||
memory.Reset();
|
||||
applet_manager.ClearAll();
|
||||
|
||||
@@ -273,10 +273,6 @@ VirtualFile VfsDirectory::GetFile(std::string_view name) const {
|
||||
return iter == files.end() ? nullptr : *iter;
|
||||
}
|
||||
|
||||
FileTimeStampRaw VfsDirectory::GetFileTimeStamp([[maybe_unused]] std::string_view path) const {
|
||||
return {};
|
||||
}
|
||||
|
||||
VirtualDir VfsDirectory::GetSubdirectory(std::string_view name) const {
|
||||
const auto& subs = GetSubdirectories();
|
||||
const auto iter = std::find_if(subs.begin(), subs.end(),
|
||||
|
||||
@@ -199,9 +199,6 @@ public:
|
||||
// file with name.
|
||||
virtual VirtualFile GetFile(std::string_view name) const;
|
||||
|
||||
// Returns a struct containing the file's timestamp.
|
||||
virtual FileTimeStampRaw GetFileTimeStamp(std::string_view path) const;
|
||||
|
||||
// Returns a vector containing all of the subdirectories in this directory.
|
||||
virtual std::vector<VirtualDir> GetSubdirectories() const = 0;
|
||||
// Returns the directory with name matching name. Returns nullptr if directory dosen't have a
|
||||
|
||||
88
src/core/file_sys/vfs_libzip.cpp
Normal file
88
src/core/file_sys/vfs_libzip.cpp
Normal file
@@ -0,0 +1,88 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <string>
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wshadow"
|
||||
#endif
|
||||
#include <zip.h>
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
#include "common/fs/path_util.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
#include "core/file_sys/vfs_libzip.h"
|
||||
#include "core/file_sys/vfs_vector.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
VirtualDir ExtractZIP(VirtualFile file) {
|
||||
zip_error_t error{};
|
||||
|
||||
const auto data = file->ReadAllBytes();
|
||||
std::unique_ptr<zip_source_t, decltype(&zip_source_close)> src{
|
||||
zip_source_buffer_create(data.data(), data.size(), 0, &error), zip_source_close};
|
||||
if (src == nullptr)
|
||||
return nullptr;
|
||||
|
||||
std::unique_ptr<zip_t, decltype(&zip_close)> zip{zip_open_from_source(src.get(), 0, &error),
|
||||
zip_close};
|
||||
if (zip == nullptr)
|
||||
return nullptr;
|
||||
|
||||
std::shared_ptr<VectorVfsDirectory> out = std::make_shared<VectorVfsDirectory>();
|
||||
|
||||
const auto num_entries = static_cast<std::size_t>(zip_get_num_entries(zip.get(), 0));
|
||||
|
||||
zip_stat_t stat{};
|
||||
zip_stat_init(&stat);
|
||||
|
||||
for (std::size_t i = 0; i < num_entries; ++i) {
|
||||
const auto stat_res = zip_stat_index(zip.get(), i, 0, &stat);
|
||||
if (stat_res == -1)
|
||||
return nullptr;
|
||||
|
||||
const std::string name(stat.name);
|
||||
if (name.empty())
|
||||
continue;
|
||||
|
||||
if (name.back() != '/') {
|
||||
std::unique_ptr<zip_file_t, decltype(&zip_fclose)> file2{
|
||||
zip_fopen_index(zip.get(), i, 0), zip_fclose};
|
||||
|
||||
std::vector<u8> buf(stat.size);
|
||||
if (zip_fread(file2.get(), buf.data(), buf.size()) != s64(buf.size()))
|
||||
return nullptr;
|
||||
|
||||
const auto parts = Common::FS::SplitPathComponents(stat.name);
|
||||
const auto new_file = std::make_shared<VectorVfsFile>(buf, parts.back());
|
||||
|
||||
std::shared_ptr<VectorVfsDirectory> dtrv = out;
|
||||
for (std::size_t j = 0; j < parts.size() - 1; ++j) {
|
||||
if (dtrv == nullptr)
|
||||
return nullptr;
|
||||
const auto subdir = dtrv->GetSubdirectory(parts[j]);
|
||||
if (subdir == nullptr) {
|
||||
const auto temp = std::make_shared<VectorVfsDirectory>(
|
||||
std::vector<VirtualFile>{}, std::vector<VirtualDir>{}, parts[j]);
|
||||
dtrv->AddDirectory(temp);
|
||||
dtrv = temp;
|
||||
} else {
|
||||
dtrv = std::dynamic_pointer_cast<VectorVfsDirectory>(subdir);
|
||||
}
|
||||
}
|
||||
|
||||
if (dtrv == nullptr)
|
||||
return nullptr;
|
||||
dtrv->AddFile(new_file);
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace FileSys
|
||||
13
src/core/file_sys/vfs_libzip.h
Normal file
13
src/core/file_sys/vfs_libzip.h
Normal file
@@ -0,0 +1,13 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/file_sys/vfs_types.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
VirtualDir ExtractZIP(VirtualFile zip);
|
||||
|
||||
} // namespace FileSys
|
||||
@@ -13,13 +13,6 @@
|
||||
#include "common/logging/log.h"
|
||||
#include "core/file_sys/vfs_real.h"
|
||||
|
||||
// For FileTimeStampRaw
|
||||
#include <sys/stat.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define stat _stat64
|
||||
#endif
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
namespace FS = Common::FS;
|
||||
@@ -399,28 +392,6 @@ std::vector<VirtualFile> RealVfsDirectory::GetFiles() const {
|
||||
return IterateEntries<RealVfsFile, VfsFile>();
|
||||
}
|
||||
|
||||
FileTimeStampRaw RealVfsDirectory::GetFileTimeStamp(std::string_view path_) const {
|
||||
const auto full_path = FS::SanitizePath(path + '/' + std::string(path_));
|
||||
const auto fs_path = std::filesystem::path{FS::ToU8String(full_path)};
|
||||
struct stat file_status;
|
||||
|
||||
#ifdef _WIN32
|
||||
const auto stat_result = _wstat64(fs_path.c_str(), &file_status);
|
||||
#else
|
||||
const auto stat_result = stat(fs_path.c_str(), &file_status);
|
||||
#endif
|
||||
|
||||
if (stat_result != 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return {
|
||||
.created{static_cast<u64>(file_status.st_ctime)},
|
||||
.accessed{static_cast<u64>(file_status.st_atime)},
|
||||
.modified{static_cast<u64>(file_status.st_mtime)},
|
||||
};
|
||||
}
|
||||
|
||||
std::vector<VirtualDir> RealVfsDirectory::GetSubdirectories() const {
|
||||
return IterateEntries<RealVfsDirectory, VfsDirectory>();
|
||||
}
|
||||
|
||||
@@ -86,7 +86,6 @@ public:
|
||||
VirtualDir CreateDirectoryRelative(std::string_view relative_path) override;
|
||||
bool DeleteSubdirectoryRecursive(std::string_view name) override;
|
||||
std::vector<VirtualFile> GetFiles() const override;
|
||||
FileTimeStampRaw GetFileTimeStamp(std::string_view path) const override;
|
||||
std::vector<VirtualDir> GetSubdirectories() const override;
|
||||
bool IsWritable() const override;
|
||||
bool IsReadable() const override;
|
||||
|
||||
@@ -6,8 +6,6 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
class VfsDirectory;
|
||||
@@ -20,11 +18,4 @@ using VirtualDir = std::shared_ptr<VfsDirectory>;
|
||||
using VirtualFile = std::shared_ptr<VfsFile>;
|
||||
using VirtualFilesystem = std::shared_ptr<VfsFilesystem>;
|
||||
|
||||
struct FileTimeStampRaw {
|
||||
u64 created{};
|
||||
u64 accessed{};
|
||||
u64 modified{};
|
||||
u64 padding{};
|
||||
};
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -13,8 +13,7 @@ ProfileSelectApplet::~ProfileSelectApplet() = default;
|
||||
void DefaultProfileSelectApplet::SelectProfile(
|
||||
std::function<void(std::optional<Common::UUID>)> callback) const {
|
||||
Service::Account::ProfileManager manager;
|
||||
callback(manager.GetUser(Settings::values.current_user.GetValue())
|
||||
.value_or(Common::UUID{Common::INVALID_UUID}));
|
||||
callback(manager.GetUser(Settings::values.current_user.GetValue()).value_or(Common::UUID{}));
|
||||
LOG_INFO(Service_ACC, "called, selecting current user instead of prompting...");
|
||||
}
|
||||
|
||||
|
||||
@@ -22,10 +22,12 @@ class KThread;
|
||||
|
||||
template <typename T>
|
||||
concept KPriorityQueueAffinityMask = !std::is_reference_v<T> && requires(T & t) {
|
||||
{ t.GetAffinityMask() } -> Common::ConvertibleTo<u64>;
|
||||
{ t.GetAffinityMask() }
|
||||
->Common::ConvertibleTo<u64>;
|
||||
{t.SetAffinityMask(0)};
|
||||
|
||||
{ t.GetAffinity(0) } -> std::same_as<bool>;
|
||||
{ t.GetAffinity(0) }
|
||||
->std::same_as<bool>;
|
||||
{t.SetAffinity(0, false)};
|
||||
{t.SetAll()};
|
||||
};
|
||||
@@ -36,20 +38,25 @@ concept KPriorityQueueMember = !std::is_reference_v<T> && requires(T & t) {
|
||||
{(typename T::QueueEntry()).Initialize()};
|
||||
{(typename T::QueueEntry()).SetPrev(std::addressof(t))};
|
||||
{(typename T::QueueEntry()).SetNext(std::addressof(t))};
|
||||
{ (typename T::QueueEntry()).GetNext() } -> std::same_as<T*>;
|
||||
{ (typename T::QueueEntry()).GetPrev() } -> std::same_as<T*>;
|
||||
{ t.GetPriorityQueueEntry(0) } -> std::same_as<typename T::QueueEntry&>;
|
||||
{ (typename T::QueueEntry()).GetNext() }
|
||||
->std::same_as<T*>;
|
||||
{ (typename T::QueueEntry()).GetPrev() }
|
||||
->std::same_as<T*>;
|
||||
{ t.GetPriorityQueueEntry(0) }
|
||||
->std::same_as<typename T::QueueEntry&>;
|
||||
|
||||
{t.GetAffinityMask()};
|
||||
{ std::remove_cvref_t<decltype(t.GetAffinityMask())>() } -> KPriorityQueueAffinityMask;
|
||||
{ std::remove_cvref_t<decltype(t.GetAffinityMask())>() }
|
||||
->KPriorityQueueAffinityMask;
|
||||
|
||||
{ t.GetActiveCore() } -> Common::ConvertibleTo<s32>;
|
||||
{ t.GetPriority() } -> Common::ConvertibleTo<s32>;
|
||||
{ t.GetActiveCore() }
|
||||
->Common::ConvertibleTo<s32>;
|
||||
{ t.GetPriority() }
|
||||
->Common::ConvertibleTo<s32>;
|
||||
};
|
||||
|
||||
template <typename Member, size_t NumCores_, int LowestPriority, int HighestPriority>
|
||||
requires KPriorityQueueMember<Member>
|
||||
class KPriorityQueue {
|
||||
requires KPriorityQueueMember<Member> class KPriorityQueue {
|
||||
public:
|
||||
using AffinityMaskType = std::remove_cv_t<
|
||||
std::remove_reference_t<decltype(std::declval<Member>().GetAffinityMask())>>;
|
||||
|
||||
@@ -197,7 +197,7 @@ private:
|
||||
|
||||
class [[nodiscard]] KScopedSchedulerLock : KScopedLock<GlobalSchedulerContext::LockType> {
|
||||
public:
|
||||
explicit KScopedSchedulerLock(KernelCore& kernel);
|
||||
explicit KScopedSchedulerLock(KernelCore & kernel);
|
||||
~KScopedSchedulerLock();
|
||||
};
|
||||
|
||||
|
||||
@@ -13,18 +13,19 @@ namespace Kernel {
|
||||
|
||||
template <typename T>
|
||||
concept KLockable = !std::is_reference_v<T> && requires(T & t) {
|
||||
{ t.Lock() } -> std::same_as<void>;
|
||||
{ t.Unlock() } -> std::same_as<void>;
|
||||
{ t.Lock() }
|
||||
->std::same_as<void>;
|
||||
{ t.Unlock() }
|
||||
->std::same_as<void>;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
requires KLockable<T>
|
||||
class [[nodiscard]] KScopedLock {
|
||||
requires KLockable<T> class [[nodiscard]] KScopedLock {
|
||||
public:
|
||||
explicit KScopedLock(T* l) : lock_ptr(l) {
|
||||
explicit KScopedLock(T * l) : lock_ptr(l) {
|
||||
this->lock_ptr->Lock();
|
||||
}
|
||||
explicit KScopedLock(T& l) : KScopedLock(std::addressof(l)) {}
|
||||
explicit KScopedLock(T & l) : KScopedLock(std::addressof(l)) {}
|
||||
|
||||
~KScopedLock() {
|
||||
this->lock_ptr->Unlock();
|
||||
@@ -33,7 +34,7 @@ public:
|
||||
KScopedLock(const KScopedLock&) = delete;
|
||||
KScopedLock& operator=(const KScopedLock&) = delete;
|
||||
|
||||
KScopedLock(KScopedLock&&) = delete;
|
||||
KScopedLock(KScopedLock &&) = delete;
|
||||
KScopedLock& operator=(KScopedLock&&) = delete;
|
||||
|
||||
private:
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace Kernel {
|
||||
|
||||
class [[nodiscard]] KScopedSchedulerLockAndSleep {
|
||||
public:
|
||||
explicit KScopedSchedulerLockAndSleep(KernelCore& kernel_, KThread* t, s64 timeout)
|
||||
explicit KScopedSchedulerLockAndSleep(KernelCore & kernel_, KThread * t, s64 timeout)
|
||||
: kernel(kernel_), thread(t), timeout_tick(timeout) {
|
||||
// Lock the scheduler.
|
||||
kernel.GlobalSchedulerContext().scheduler_lock.Lock();
|
||||
|
||||
@@ -929,7 +929,8 @@ void Module::Interface::TrySelectUserWithoutInteraction(Kernel::HLERequestContex
|
||||
}
|
||||
|
||||
const auto user_list = profile_manager->GetAllUsers();
|
||||
if (std::ranges::all_of(user_list, [](const auto& user) { return user.IsInvalid(); })) {
|
||||
if (std::all_of(user_list.begin(), user_list.end(),
|
||||
[](const auto& user) { return user.uuid == Common::INVALID_UUID; })) {
|
||||
rb.Push(ResultUnknown); // TODO(ogniK): Find the correct error code
|
||||
rb.PushRaw<u128>(Common::INVALID_UUID);
|
||||
return;
|
||||
|
||||
@@ -208,10 +208,9 @@ bool ProfileManager::UserExists(UUID uuid) const {
|
||||
}
|
||||
|
||||
bool ProfileManager::UserExistsIndex(std::size_t index) const {
|
||||
if (index >= MAX_USERS) {
|
||||
if (index >= MAX_USERS)
|
||||
return false;
|
||||
}
|
||||
return profiles[index].user_uuid.IsValid();
|
||||
return profiles[index].user_uuid.uuid != Common::INVALID_UUID;
|
||||
}
|
||||
|
||||
/// Opens a specific user
|
||||
@@ -305,7 +304,7 @@ bool ProfileManager::RemoveUser(UUID uuid) {
|
||||
|
||||
bool ProfileManager::SetProfileBase(UUID uuid, const ProfileBase& profile_new) {
|
||||
const auto index = GetUserIndex(uuid);
|
||||
if (!index || profile_new.user_uuid.IsInvalid()) {
|
||||
if (!index || profile_new.user_uuid == UUID(Common::INVALID_UUID)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -347,7 +346,7 @@ void ProfileManager::ParseUserSaveFile() {
|
||||
}
|
||||
|
||||
for (const auto& user : data.users) {
|
||||
if (user.uuid.IsInvalid()) {
|
||||
if (user.uuid == UUID(Common::INVALID_UUID)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -275,14 +275,12 @@ ISelfController::ISelfController(Core::System& system_, NVFlinger::NVFlinger& nv
|
||||
{18, nullptr, "SetRequiresCaptureButtonShortPressedMessage"},
|
||||
{19, &ISelfController::SetAlbumImageOrientation, "SetAlbumImageOrientation"},
|
||||
{20, nullptr, "SetDesirableKeyboardLayout"},
|
||||
{21, nullptr, "GetScreenShotProgramId"},
|
||||
{40, &ISelfController::CreateManagedDisplayLayer, "CreateManagedDisplayLayer"},
|
||||
{41, nullptr, "IsSystemBufferSharingEnabled"},
|
||||
{42, nullptr, "GetSystemSharedLayerHandle"},
|
||||
{43, nullptr, "GetSystemSharedBufferHandle"},
|
||||
{44, &ISelfController::CreateManagedDisplaySeparableLayer, "CreateManagedDisplaySeparableLayer"},
|
||||
{45, nullptr, "SetManagedDisplayLayerSeparationMode"},
|
||||
{46, nullptr, "SetRecordingLayerCompositionEnabled"},
|
||||
{50, &ISelfController::SetHandlesRequestToDisplay, "SetHandlesRequestToDisplay"},
|
||||
{51, nullptr, "ApproveToDisplay"},
|
||||
{60, nullptr, "OverrideAutoSleepTimeAndDimmingTime"},
|
||||
@@ -304,7 +302,6 @@ ISelfController::ISelfController(Core::System& system_, NVFlinger::NVFlinger& nv
|
||||
{100, &ISelfController::SetAlbumImageTakenNotificationEnabled, "SetAlbumImageTakenNotificationEnabled"},
|
||||
{110, nullptr, "SetApplicationAlbumUserData"},
|
||||
{120, nullptr, "SaveCurrentScreenshot"},
|
||||
{130, nullptr, "SetRecordVolumeMuted"},
|
||||
{1000, nullptr, "GetDebugStorageChannel"},
|
||||
};
|
||||
// clang-format on
|
||||
@@ -686,7 +683,6 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system_,
|
||||
{91, nullptr, "GetCurrentPerformanceConfiguration"},
|
||||
{100, nullptr, "SetHandlingHomeButtonShortPressedEnabled"},
|
||||
{110, nullptr, "OpenMyGpuErrorHandler"},
|
||||
{120, nullptr, "GetAppletLaunchedHistory"},
|
||||
{200, nullptr, "GetOperationModeSystemInfo"},
|
||||
{300, nullptr, "GetSettingsPlatformRegion"},
|
||||
{400, nullptr, "ActivateMigrationService"},
|
||||
|
||||
@@ -60,7 +60,7 @@ void ProfileSelect::Execute() {
|
||||
void ProfileSelect::SelectionComplete(std::optional<Common::UUID> uuid) {
|
||||
UserSelectionOutput output{};
|
||||
|
||||
if (uuid.has_value() && uuid->IsValid()) {
|
||||
if (uuid.has_value() && uuid->uuid != Common::INVALID_UUID) {
|
||||
output.result = 0;
|
||||
output.uuid_selected = uuid->uuid;
|
||||
} else {
|
||||
|
||||
@@ -41,14 +41,6 @@ AudCtl::AudCtl(Core::System& system_) : ServiceFramework{system_, "audctl"} {
|
||||
{27, nullptr, "SetVolumeMappingTableForDev"},
|
||||
{28, nullptr, "GetAudioOutputChannelCountForPlayReport"},
|
||||
{29, nullptr, "BindAudioOutputChannelCountUpdateEventForPlayReport"},
|
||||
{30, nullptr, "Unknown30"},
|
||||
{31, nullptr, "Unknown31"},
|
||||
{32, nullptr, "Unknown32"},
|
||||
{33, nullptr, "Unknown33"},
|
||||
{34, nullptr, "Unknown34"},
|
||||
{10000, nullptr, "Unknown10000"},
|
||||
{10001, nullptr, "Unknown10001"},
|
||||
{10002, nullptr, "Unknown10002"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
||||
@@ -3,65 +3,38 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/hle_ipc.h"
|
||||
#include "core/hle/kernel/k_event.h"
|
||||
#include "core/hle/service/audio/audin_u.h"
|
||||
|
||||
namespace Service::Audio {
|
||||
|
||||
IAudioIn::IAudioIn(Core::System& system_)
|
||||
: ServiceFramework{system_, "IAudioIn"}, buffer_event{system_.Kernel()} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "GetAudioInState"},
|
||||
{1, &IAudioIn::Start, "Start"},
|
||||
{2, nullptr, "Stop"},
|
||||
{3, nullptr, "AppendAudioInBuffer"},
|
||||
{4, &IAudioIn::RegisterBufferEvent, "RegisterBufferEvent"},
|
||||
{5, nullptr, "GetReleasedAudioInBuffer"},
|
||||
{6, nullptr, "ContainsAudioInBuffer"},
|
||||
{7, nullptr, "AppendUacInBuffer"},
|
||||
{8, &IAudioIn::AppendAudioInBufferAuto, "AppendAudioInBufferAuto"},
|
||||
{9, nullptr, "GetReleasedAudioInBuffersAuto"},
|
||||
{10, nullptr, "AppendUacInBufferAuto"},
|
||||
{11, nullptr, "GetAudioInBufferCount"},
|
||||
{12, nullptr, "SetDeviceGain"},
|
||||
{13, nullptr, "GetDeviceGain"},
|
||||
{14, nullptr, "FlushAudioInBuffers"},
|
||||
};
|
||||
// clang-format on
|
||||
class IAudioIn final : public ServiceFramework<IAudioIn> {
|
||||
public:
|
||||
explicit IAudioIn(Core::System& system_) : ServiceFramework{system_, "IAudioIn"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "GetAudioInState"},
|
||||
{1, nullptr, "Start"},
|
||||
{2, nullptr, "Stop"},
|
||||
{3, nullptr, "AppendAudioInBuffer"},
|
||||
{4, nullptr, "RegisterBufferEvent"},
|
||||
{5, nullptr, "GetReleasedAudioInBuffer"},
|
||||
{6, nullptr, "ContainsAudioInBuffer"},
|
||||
{7, nullptr, "AppendUacInBuffer"},
|
||||
{8, nullptr, "AppendAudioInBufferAuto"},
|
||||
{9, nullptr, "GetReleasedAudioInBuffersAuto"},
|
||||
{10, nullptr, "AppendUacInBufferAuto"},
|
||||
{11, nullptr, "GetAudioInBufferCount"},
|
||||
{12, nullptr, "SetDeviceGain"},
|
||||
{13, nullptr, "GetDeviceGain"},
|
||||
{14, nullptr, "FlushAudioInBuffers"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
|
||||
Kernel::KAutoObject::Create(std::addressof(buffer_event));
|
||||
buffer_event.Initialize("IAudioIn:BufferEvent");
|
||||
}
|
||||
|
||||
IAudioIn::~IAudioIn() = default;
|
||||
|
||||
void IAudioIn::Start(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_Audio, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void IAudioIn::RegisterBufferEvent(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_Audio, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(buffer_event.GetReadableEvent());
|
||||
}
|
||||
|
||||
void IAudioIn::AppendAudioInBufferAuto(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_Audio, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
};
|
||||
|
||||
AudInU::AudInU(Core::System& system_) : ServiceFramework{system_, "audin:u"} {
|
||||
// clang-format off
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/kernel/k_event.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Core {
|
||||
@@ -17,19 +16,6 @@ class HLERequestContext;
|
||||
|
||||
namespace Service::Audio {
|
||||
|
||||
class IAudioIn final : public ServiceFramework<IAudioIn> {
|
||||
public:
|
||||
explicit IAudioIn(Core::System& system_);
|
||||
~IAudioIn() override;
|
||||
|
||||
private:
|
||||
void Start(Kernel::HLERequestContext& ctx);
|
||||
void RegisterBufferEvent(Kernel::HLERequestContext& ctx);
|
||||
void AppendAudioInBufferAuto(Kernel::HLERequestContext& ctx);
|
||||
|
||||
Kernel::KEvent buffer_event;
|
||||
};
|
||||
|
||||
class AudInU final : public ServiceFramework<AudInU> {
|
||||
public:
|
||||
explicit AudInU(Core::System& system_);
|
||||
|
||||
@@ -187,8 +187,7 @@ public:
|
||||
{10, &IAudioDevice::GetActiveAudioDeviceName, "GetActiveAudioDeviceNameAuto"},
|
||||
{11, &IAudioDevice::QueryAudioDeviceInputEvent, "QueryAudioDeviceInputEvent"},
|
||||
{12, &IAudioDevice::QueryAudioDeviceOutputEvent, "QueryAudioDeviceOutputEvent"},
|
||||
{13, nullptr, "GetActiveAudioOutputDeviceName"},
|
||||
{14, nullptr, "ListAudioOutputDeviceName"},
|
||||
{13, nullptr, "GetAudioSystemMasterVolumeSetting"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
548
src/core/hle/service/bcat/backend/boxcat.cpp
Normal file
548
src/core/hle/service/bcat/backend/boxcat.cpp
Normal file
@@ -0,0 +1,548 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <fmt/ostream.h>
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wshadow"
|
||||
#ifndef __clang__
|
||||
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
|
||||
#endif
|
||||
#endif
|
||||
#include <httplib.h>
|
||||
#include <mbedtls/sha256.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
#include "common/fs/file.h"
|
||||
#include "common/fs/fs.h"
|
||||
#include "common/fs/path_util.h"
|
||||
#include "common/hex_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
#include "core/file_sys/vfs_libzip.h"
|
||||
#include "core/file_sys/vfs_vector.h"
|
||||
#include "core/frontend/applets/error.h"
|
||||
#include "core/hle/service/am/applets/applets.h"
|
||||
#include "core/hle/service/bcat/backend/boxcat.h"
|
||||
|
||||
namespace Service::BCAT {
|
||||
namespace {
|
||||
|
||||
// Prevents conflicts with windows macro called CreateFile
|
||||
FileSys::VirtualFile VfsCreateFileWrap(FileSys::VirtualDir dir, std::string_view name) {
|
||||
return dir->CreateFile(name);
|
||||
}
|
||||
|
||||
// Prevents conflicts with windows macro called DeleteFile
|
||||
bool VfsDeleteFileWrap(FileSys::VirtualDir dir, std::string_view name) {
|
||||
return dir->DeleteFile(name);
|
||||
}
|
||||
|
||||
constexpr ResultCode ERROR_GENERAL_BCAT_FAILURE{ErrorModule::BCAT, 1};
|
||||
|
||||
constexpr char BOXCAT_HOSTNAME[] = "api.yuzu-emu.org";
|
||||
|
||||
// Formatted using fmt with arg[0] = hex title id
|
||||
constexpr char BOXCAT_PATHNAME_DATA[] = "/game-assets/{:016X}/boxcat";
|
||||
constexpr char BOXCAT_PATHNAME_LAUNCHPARAM[] = "/game-assets/{:016X}/launchparam";
|
||||
|
||||
constexpr char BOXCAT_PATHNAME_EVENTS[] = "/game-assets/boxcat/events";
|
||||
|
||||
constexpr char BOXCAT_API_VERSION[] = "1";
|
||||
constexpr char BOXCAT_CLIENT_TYPE[] = "yuzu";
|
||||
|
||||
// HTTP status codes for Boxcat
|
||||
enum class ResponseStatus {
|
||||
Ok = 200, ///< Operation completed successfully.
|
||||
BadClientVersion = 301, ///< The Boxcat-Client-Version doesn't match the server.
|
||||
NoUpdate = 304, ///< The digest provided would match the new data, no need to update.
|
||||
NoMatchTitleId = 404, ///< The title ID provided doesn't have a boxcat implementation.
|
||||
NoMatchBuildId = 406, ///< The build ID provided is blacklisted (potentially because of format
|
||||
///< issues or whatnot) and has no data.
|
||||
};
|
||||
|
||||
enum class DownloadResult {
|
||||
Success = 0,
|
||||
NoResponse,
|
||||
GeneralWebError,
|
||||
NoMatchTitleId,
|
||||
NoMatchBuildId,
|
||||
InvalidContentType,
|
||||
GeneralFSError,
|
||||
BadClientVersion,
|
||||
};
|
||||
|
||||
constexpr std::array<const char*, 8> DOWNLOAD_RESULT_LOG_MESSAGES{
|
||||
"Success",
|
||||
"There was no response from the server.",
|
||||
"There was a general web error code returned from the server.",
|
||||
"The title ID of the current game doesn't have a boxcat implementation. If you believe an "
|
||||
"implementation should be added, contact yuzu support.",
|
||||
"The build ID of the current version of the game is marked as incompatible with the current "
|
||||
"BCAT distribution. Try upgrading or downgrading your game version or contacting yuzu support.",
|
||||
"The content type of the web response was invalid.",
|
||||
"There was a general filesystem error while saving the zip file.",
|
||||
"The server is either too new or too old to serve the request. Try using the latest version of "
|
||||
"an official release of yuzu.",
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, DownloadResult result) {
|
||||
return os << DOWNLOAD_RESULT_LOG_MESSAGES.at(static_cast<std::size_t>(result));
|
||||
}
|
||||
|
||||
constexpr u32 PORT = 443;
|
||||
constexpr u32 TIMEOUT_SECONDS = 30;
|
||||
[[maybe_unused]] constexpr u64 VFS_COPY_BLOCK_SIZE = 1ULL << 24; // 4MB
|
||||
|
||||
std::filesystem::path GetBINFilePath(u64 title_id) {
|
||||
return Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) / "bcat" /
|
||||
fmt::format("{:016X}/launchparam.bin", title_id);
|
||||
}
|
||||
|
||||
std::filesystem::path GetZIPFilePath(u64 title_id) {
|
||||
return Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) / "bcat" /
|
||||
fmt::format("{:016X}/data.zip", title_id);
|
||||
}
|
||||
|
||||
// If the error is something the user should know about (build ID mismatch, bad client version),
|
||||
// display an error.
|
||||
void HandleDownloadDisplayResult(const AM::Applets::AppletManager& applet_manager,
|
||||
DownloadResult res) {
|
||||
if (res == DownloadResult::Success || res == DownloadResult::NoResponse ||
|
||||
res == DownloadResult::GeneralWebError || res == DownloadResult::GeneralFSError ||
|
||||
res == DownloadResult::NoMatchTitleId || res == DownloadResult::InvalidContentType) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& frontend{applet_manager.GetAppletFrontendSet()};
|
||||
frontend.error->ShowCustomErrorText(
|
||||
ResultUnknown, "There was an error while attempting to use Boxcat.",
|
||||
DOWNLOAD_RESULT_LOG_MESSAGES[static_cast<std::size_t>(res)], [] {});
|
||||
}
|
||||
|
||||
bool VfsRawCopyProgress(FileSys::VirtualFile src, FileSys::VirtualFile dest,
|
||||
std::string_view dir_name, ProgressServiceBackend& progress,
|
||||
std::size_t block_size = 0x1000) {
|
||||
if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable())
|
||||
return false;
|
||||
if (!dest->Resize(src->GetSize()))
|
||||
return false;
|
||||
|
||||
progress.StartDownloadingFile(dir_name, src->GetName(), src->GetSize());
|
||||
|
||||
std::vector<u8> temp(std::min(block_size, src->GetSize()));
|
||||
for (std::size_t i = 0; i < src->GetSize(); i += block_size) {
|
||||
const auto read = std::min(block_size, src->GetSize() - i);
|
||||
|
||||
if (src->Read(temp.data(), read, i) != read) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dest->Write(temp.data(), read, i) != read) {
|
||||
return false;
|
||||
}
|
||||
|
||||
progress.UpdateFileProgress(i);
|
||||
}
|
||||
|
||||
progress.FinishDownloadingFile();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VfsRawCopyDProgressSingle(FileSys::VirtualDir src, FileSys::VirtualDir dest,
|
||||
ProgressServiceBackend& progress, std::size_t block_size = 0x1000) {
|
||||
if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable())
|
||||
return false;
|
||||
|
||||
for (const auto& file : src->GetFiles()) {
|
||||
const auto out_file = VfsCreateFileWrap(dest, file->GetName());
|
||||
if (!VfsRawCopyProgress(file, out_file, src->GetName(), progress, block_size)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
progress.CommitDirectory(src->GetName());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VfsRawCopyDProgress(FileSys::VirtualDir src, FileSys::VirtualDir dest,
|
||||
ProgressServiceBackend& progress, std::size_t block_size = 0x1000) {
|
||||
if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable())
|
||||
return false;
|
||||
|
||||
for (const auto& dir : src->GetSubdirectories()) {
|
||||
const auto out = dest->CreateSubdirectory(dir->GetName());
|
||||
if (!VfsRawCopyDProgressSingle(dir, out, progress, block_size)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
class Boxcat::Client {
|
||||
public:
|
||||
Client(std::filesystem::path path_, u64 title_id_, u64 build_id_)
|
||||
: path(std::move(path_)), title_id(title_id_), build_id(build_id_) {}
|
||||
|
||||
DownloadResult DownloadDataZip() {
|
||||
return DownloadInternal(fmt::format(BOXCAT_PATHNAME_DATA, title_id), TIMEOUT_SECONDS,
|
||||
"application/zip");
|
||||
}
|
||||
|
||||
DownloadResult DownloadLaunchParam() {
|
||||
return DownloadInternal(fmt::format(BOXCAT_PATHNAME_LAUNCHPARAM, title_id),
|
||||
TIMEOUT_SECONDS / 3, "application/octet-stream");
|
||||
}
|
||||
|
||||
private:
|
||||
DownloadResult DownloadInternal(const std::string& resolved_path, u32 timeout_seconds,
|
||||
const std::string& content_type_name) {
|
||||
if (client == nullptr) {
|
||||
client = std::make_unique<httplib::SSLClient>(BOXCAT_HOSTNAME, PORT);
|
||||
client->set_connection_timeout(timeout_seconds);
|
||||
client->set_read_timeout(timeout_seconds);
|
||||
client->set_write_timeout(timeout_seconds);
|
||||
}
|
||||
|
||||
httplib::Headers headers{
|
||||
{std::string("Game-Assets-API-Version"), std::string(BOXCAT_API_VERSION)},
|
||||
{std::string("Boxcat-Client-Type"), std::string(BOXCAT_CLIENT_TYPE)},
|
||||
{std::string("Game-Build-Id"), fmt::format("{:016X}", build_id)},
|
||||
};
|
||||
|
||||
if (Common::FS::Exists(path)) {
|
||||
Common::FS::IOFile file{path, Common::FS::FileAccessMode::Read,
|
||||
Common::FS::FileType::BinaryFile};
|
||||
if (file.IsOpen()) {
|
||||
std::vector<u8> bytes(file.GetSize());
|
||||
void(file.Read(bytes));
|
||||
const auto digest = DigestFile(bytes);
|
||||
headers.insert({std::string("If-None-Match"), Common::HexToString(digest, false)});
|
||||
}
|
||||
}
|
||||
|
||||
const auto response = client->Get(resolved_path.c_str(), headers);
|
||||
if (response == nullptr)
|
||||
return DownloadResult::NoResponse;
|
||||
|
||||
if (response->status == static_cast<int>(ResponseStatus::NoUpdate))
|
||||
return DownloadResult::Success;
|
||||
if (response->status == static_cast<int>(ResponseStatus::BadClientVersion))
|
||||
return DownloadResult::BadClientVersion;
|
||||
if (response->status == static_cast<int>(ResponseStatus::NoMatchTitleId))
|
||||
return DownloadResult::NoMatchTitleId;
|
||||
if (response->status == static_cast<int>(ResponseStatus::NoMatchBuildId))
|
||||
return DownloadResult::NoMatchBuildId;
|
||||
if (response->status != static_cast<int>(ResponseStatus::Ok))
|
||||
return DownloadResult::GeneralWebError;
|
||||
|
||||
const auto content_type = response->headers.find("content-type");
|
||||
if (content_type == response->headers.end() ||
|
||||
content_type->second.find(content_type_name) == std::string::npos) {
|
||||
return DownloadResult::InvalidContentType;
|
||||
}
|
||||
|
||||
if (!Common::FS::CreateDirs(path)) {
|
||||
return DownloadResult::GeneralFSError;
|
||||
}
|
||||
|
||||
Common::FS::IOFile file{path, Common::FS::FileAccessMode::Append,
|
||||
Common::FS::FileType::BinaryFile};
|
||||
if (!file.IsOpen()) {
|
||||
return DownloadResult::GeneralFSError;
|
||||
}
|
||||
|
||||
if (!file.SetSize(response->body.size())) {
|
||||
return DownloadResult::GeneralFSError;
|
||||
}
|
||||
|
||||
if (file.Write(response->body) != response->body.size()) {
|
||||
return DownloadResult::GeneralFSError;
|
||||
}
|
||||
|
||||
return DownloadResult::Success;
|
||||
}
|
||||
|
||||
using Digest = std::array<u8, 0x20>;
|
||||
static Digest DigestFile(std::vector<u8> bytes) {
|
||||
Digest out{};
|
||||
mbedtls_sha256_ret(bytes.data(), bytes.size(), out.data(), 0);
|
||||
return out;
|
||||
}
|
||||
|
||||
std::unique_ptr<httplib::SSLClient> client;
|
||||
std::filesystem::path path;
|
||||
u64 title_id;
|
||||
u64 build_id;
|
||||
};
|
||||
|
||||
Boxcat::Boxcat(AM::Applets::AppletManager& applet_manager_, DirectoryGetter getter)
|
||||
: Backend(std::move(getter)), applet_manager{applet_manager_} {}
|
||||
|
||||
Boxcat::~Boxcat() = default;
|
||||
|
||||
void SynchronizeInternal(AM::Applets::AppletManager& applet_manager, DirectoryGetter dir_getter,
|
||||
TitleIDVersion title, ProgressServiceBackend& progress,
|
||||
std::optional<std::string> dir_name = {}) {
|
||||
progress.SetNeedHLELock(true);
|
||||
|
||||
if (Settings::values.bcat_boxcat_local) {
|
||||
LOG_INFO(Service_BCAT, "Boxcat using local data by override, skipping download.");
|
||||
const auto dir = dir_getter(title.title_id);
|
||||
if (dir)
|
||||
progress.SetTotalSize(dir->GetSize());
|
||||
progress.FinishDownload(ResultSuccess);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto zip_path = GetZIPFilePath(title.title_id);
|
||||
Boxcat::Client client{zip_path, title.title_id, title.build_id};
|
||||
|
||||
progress.StartConnecting();
|
||||
|
||||
const auto res = client.DownloadDataZip();
|
||||
if (res != DownloadResult::Success) {
|
||||
LOG_ERROR(Service_BCAT, "Boxcat synchronization failed with error '{}'!", res);
|
||||
|
||||
if (res == DownloadResult::NoMatchBuildId || res == DownloadResult::NoMatchTitleId) {
|
||||
Common::FS::RemoveFile(zip_path);
|
||||
}
|
||||
|
||||
HandleDownloadDisplayResult(applet_manager, res);
|
||||
progress.FinishDownload(ERROR_GENERAL_BCAT_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
progress.StartProcessingDataList();
|
||||
|
||||
Common::FS::IOFile zip{zip_path, Common::FS::FileAccessMode::Read,
|
||||
Common::FS::FileType::BinaryFile};
|
||||
const auto size = zip.GetSize();
|
||||
std::vector<u8> bytes(size);
|
||||
if (!zip.IsOpen() || size == 0 || zip.Read(bytes) != bytes.size()) {
|
||||
LOG_ERROR(Service_BCAT, "Boxcat failed to read ZIP file at path '{}'!",
|
||||
Common::FS::PathToUTF8String(zip_path));
|
||||
progress.FinishDownload(ERROR_GENERAL_BCAT_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto extracted = FileSys::ExtractZIP(std::make_shared<FileSys::VectorVfsFile>(bytes));
|
||||
if (extracted == nullptr) {
|
||||
LOG_ERROR(Service_BCAT, "Boxcat failed to extract ZIP file!");
|
||||
progress.FinishDownload(ERROR_GENERAL_BCAT_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (dir_name == std::nullopt) {
|
||||
progress.SetTotalSize(extracted->GetSize());
|
||||
|
||||
const auto target_dir = dir_getter(title.title_id);
|
||||
if (target_dir == nullptr || !VfsRawCopyDProgress(extracted, target_dir, progress)) {
|
||||
LOG_ERROR(Service_BCAT, "Boxcat failed to copy extracted ZIP to target directory!");
|
||||
progress.FinishDownload(ERROR_GENERAL_BCAT_FAILURE);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
const auto target_dir = dir_getter(title.title_id);
|
||||
if (target_dir == nullptr) {
|
||||
LOG_ERROR(Service_BCAT, "Boxcat failed to get directory for title ID!");
|
||||
progress.FinishDownload(ERROR_GENERAL_BCAT_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto target_sub = target_dir->GetSubdirectory(*dir_name);
|
||||
const auto source_sub = extracted->GetSubdirectory(*dir_name);
|
||||
|
||||
progress.SetTotalSize(source_sub->GetSize());
|
||||
|
||||
std::vector<std::string> filenames;
|
||||
{
|
||||
const auto files = target_sub->GetFiles();
|
||||
std::transform(files.begin(), files.end(), std::back_inserter(filenames),
|
||||
[](const auto& vfile) { return vfile->GetName(); });
|
||||
}
|
||||
|
||||
for (const auto& filename : filenames) {
|
||||
VfsDeleteFileWrap(target_sub, filename);
|
||||
}
|
||||
|
||||
if (target_sub == nullptr || source_sub == nullptr ||
|
||||
!VfsRawCopyDProgressSingle(source_sub, target_sub, progress)) {
|
||||
LOG_ERROR(Service_BCAT, "Boxcat failed to copy extracted ZIP to target directory!");
|
||||
progress.FinishDownload(ERROR_GENERAL_BCAT_FAILURE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
progress.FinishDownload(ResultSuccess);
|
||||
}
|
||||
|
||||
bool Boxcat::Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) {
|
||||
is_syncing.exchange(true);
|
||||
|
||||
std::thread([this, title, &progress] {
|
||||
SynchronizeInternal(applet_manager, dir_getter, title, progress);
|
||||
}).detach();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Boxcat::SynchronizeDirectory(TitleIDVersion title, std::string name,
|
||||
ProgressServiceBackend& progress) {
|
||||
is_syncing.exchange(true);
|
||||
|
||||
std::thread([this, title, name, &progress] {
|
||||
SynchronizeInternal(applet_manager, dir_getter, title, progress, name);
|
||||
}).detach();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Boxcat::Clear(u64 title_id) {
|
||||
if (Settings::values.bcat_boxcat_local) {
|
||||
LOG_INFO(Service_BCAT, "Boxcat using local data by override, skipping clear.");
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto dir = dir_getter(title_id);
|
||||
|
||||
std::vector<std::string> dirnames;
|
||||
|
||||
for (const auto& subdir : dir->GetSubdirectories())
|
||||
dirnames.push_back(subdir->GetName());
|
||||
|
||||
for (const auto& subdir : dirnames) {
|
||||
if (!dir->DeleteSubdirectoryRecursive(subdir))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Boxcat::SetPassphrase(u64 title_id, const Passphrase& passphrase) {
|
||||
LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, passphrase={}", title_id,
|
||||
Common::HexToString(passphrase));
|
||||
}
|
||||
|
||||
std::optional<std::vector<u8>> Boxcat::GetLaunchParameter(TitleIDVersion title) {
|
||||
const auto bin_file_path = GetBINFilePath(title.title_id);
|
||||
|
||||
if (Settings::values.bcat_boxcat_local) {
|
||||
LOG_INFO(Service_BCAT, "Boxcat using local data by override, skipping download.");
|
||||
} else {
|
||||
Client launch_client{bin_file_path, title.title_id, title.build_id};
|
||||
|
||||
const auto res = launch_client.DownloadLaunchParam();
|
||||
if (res != DownloadResult::Success) {
|
||||
LOG_ERROR(Service_BCAT, "Boxcat synchronization failed with error '{}'!", res);
|
||||
|
||||
if (res == DownloadResult::NoMatchBuildId || res == DownloadResult::NoMatchTitleId) {
|
||||
Common::FS::RemoveFile(bin_file_path);
|
||||
}
|
||||
|
||||
HandleDownloadDisplayResult(applet_manager, res);
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
Common::FS::IOFile bin{bin_file_path, Common::FS::FileAccessMode::Read,
|
||||
Common::FS::FileType::BinaryFile};
|
||||
const auto size = bin.GetSize();
|
||||
std::vector<u8> bytes(size);
|
||||
if (!bin.IsOpen() || size == 0 || bin.Read(bytes) != bytes.size()) {
|
||||
LOG_ERROR(Service_BCAT, "Boxcat failed to read launch parameter binary at path '{}'!",
|
||||
Common::FS::PathToUTF8String(bin_file_path));
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
Boxcat::StatusResult Boxcat::GetStatus(std::optional<std::string>& global,
|
||||
std::map<std::string, EventStatus>& games) {
|
||||
httplib::SSLClient client{BOXCAT_HOSTNAME, static_cast<int>(PORT)};
|
||||
client.set_connection_timeout(static_cast<int>(TIMEOUT_SECONDS));
|
||||
client.set_read_timeout(static_cast<int>(TIMEOUT_SECONDS));
|
||||
client.set_write_timeout(static_cast<int>(TIMEOUT_SECONDS));
|
||||
|
||||
httplib::Headers headers{
|
||||
{std::string("Game-Assets-API-Version"), std::string(BOXCAT_API_VERSION)},
|
||||
{std::string("Boxcat-Client-Type"), std::string(BOXCAT_CLIENT_TYPE)},
|
||||
};
|
||||
|
||||
if (!client.is_valid()) {
|
||||
LOG_ERROR(Service_BCAT, "Client is invalid, going offline!");
|
||||
return StatusResult::Offline;
|
||||
}
|
||||
|
||||
if (!client.is_socket_open()) {
|
||||
LOG_ERROR(Service_BCAT, "Failed to open socket, going offline!");
|
||||
return StatusResult::Offline;
|
||||
}
|
||||
|
||||
const auto response = client.Get(BOXCAT_PATHNAME_EVENTS, headers);
|
||||
if (response == nullptr)
|
||||
return StatusResult::Offline;
|
||||
|
||||
if (response->status == static_cast<int>(ResponseStatus::BadClientVersion))
|
||||
return StatusResult::BadClientVersion;
|
||||
|
||||
try {
|
||||
nlohmann::json json = nlohmann::json::parse(response->body);
|
||||
|
||||
if (!json["online"].get<bool>())
|
||||
return StatusResult::Offline;
|
||||
|
||||
if (json["global"].is_null())
|
||||
global = std::nullopt;
|
||||
else
|
||||
global = json["global"].get<std::string>();
|
||||
|
||||
if (json["games"].is_array()) {
|
||||
for (const auto& object : json["games"]) {
|
||||
if (object.is_object() && object.find("name") != object.end()) {
|
||||
EventStatus detail{};
|
||||
if (object["header"].is_string()) {
|
||||
detail.header = object["header"].get<std::string>();
|
||||
} else {
|
||||
detail.header = std::nullopt;
|
||||
}
|
||||
|
||||
if (object["footer"].is_string()) {
|
||||
detail.footer = object["footer"].get<std::string>();
|
||||
} else {
|
||||
detail.footer = std::nullopt;
|
||||
}
|
||||
|
||||
if (object["events"].is_array()) {
|
||||
for (const auto& event : object["events"]) {
|
||||
if (!event.is_string())
|
||||
continue;
|
||||
detail.events.push_back(event.get<std::string>());
|
||||
}
|
||||
}
|
||||
|
||||
games.insert_or_assign(object["name"], std::move(detail));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return StatusResult::Success;
|
||||
} catch (const nlohmann::json::parse_error& error) {
|
||||
LOG_ERROR(Service_BCAT, "{}", error.what());
|
||||
return StatusResult::ParseError;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Service::BCAT
|
||||
64
src/core/hle/service/bcat/backend/boxcat.h
Normal file
64
src/core/hle/service/bcat/backend/boxcat.h
Normal file
@@ -0,0 +1,64 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include "core/hle/service/bcat/backend/backend.h"
|
||||
|
||||
namespace Service::AM::Applets {
|
||||
class AppletManager;
|
||||
}
|
||||
|
||||
namespace Service::BCAT {
|
||||
|
||||
struct EventStatus {
|
||||
std::optional<std::string> header;
|
||||
std::optional<std::string> footer;
|
||||
std::vector<std::string> events;
|
||||
};
|
||||
|
||||
/// Boxcat is yuzu's custom backend implementation of Nintendo's BCAT service. It is free to use and
|
||||
/// doesn't require a switch or nintendo account. The content is controlled by the yuzu team.
|
||||
class Boxcat final : public Backend {
|
||||
friend void SynchronizeInternal(AM::Applets::AppletManager& applet_manager,
|
||||
DirectoryGetter dir_getter, TitleIDVersion title,
|
||||
ProgressServiceBackend& progress,
|
||||
std::optional<std::string> dir_name);
|
||||
|
||||
public:
|
||||
explicit Boxcat(AM::Applets::AppletManager& applet_manager_, DirectoryGetter getter);
|
||||
~Boxcat() override;
|
||||
|
||||
bool Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) override;
|
||||
bool SynchronizeDirectory(TitleIDVersion title, std::string name,
|
||||
ProgressServiceBackend& progress) override;
|
||||
|
||||
bool Clear(u64 title_id) override;
|
||||
|
||||
void SetPassphrase(u64 title_id, const Passphrase& passphrase) override;
|
||||
|
||||
std::optional<std::vector<u8>> GetLaunchParameter(TitleIDVersion title) override;
|
||||
|
||||
enum class StatusResult {
|
||||
Success,
|
||||
Offline,
|
||||
ParseError,
|
||||
BadClientVersion,
|
||||
};
|
||||
|
||||
static StatusResult GetStatus(std::optional<std::string>& global,
|
||||
std::map<std::string, EventStatus>& games);
|
||||
|
||||
private:
|
||||
std::atomic_bool is_syncing{false};
|
||||
|
||||
class Client;
|
||||
std::unique_ptr<Client> client;
|
||||
AM::Applets::AppletManager& applet_manager;
|
||||
};
|
||||
|
||||
} // namespace Service::BCAT
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include <cctype>
|
||||
#include <mbedtls/md5.h>
|
||||
#include "backend/boxcat.h"
|
||||
#include "common/hex_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/settings.h"
|
||||
@@ -577,6 +578,12 @@ void Module::Interface::CreateDeliveryCacheStorageServiceWithApplicationId(
|
||||
|
||||
std::unique_ptr<Backend> CreateBackendFromSettings([[maybe_unused]] Core::System& system,
|
||||
DirectoryGetter getter) {
|
||||
#ifdef YUZU_ENABLE_BOXCAT
|
||||
if (Settings::values.bcat_backend.GetValue() == "boxcat") {
|
||||
return std::make_unique<Boxcat>(system.GetAppletManager(), std::move(getter));
|
||||
}
|
||||
#endif
|
||||
|
||||
return std::make_unique<NullBackend>(std::move(getter));
|
||||
}
|
||||
|
||||
|
||||
@@ -175,10 +175,6 @@ public:
|
||||
{143, nullptr, "GetAudioControlInputState"},
|
||||
{144, nullptr, "AcquireAudioConnectionStateChangedEvent"},
|
||||
{145, nullptr, "GetConnectedAudioDevice"},
|
||||
{146, nullptr, "CloseAudioControlInput"},
|
||||
{147, nullptr, "RegisterAudioControlNotification"},
|
||||
{148, nullptr, "SendAudioControlPassthroughCommand"},
|
||||
{149, nullptr, "SendAudioControlSetAbsoluteVolumeCommand"},
|
||||
{256, nullptr, "IsManufacturingMode"},
|
||||
{257, nullptr, "EmulateBluetoothCrash"},
|
||||
{258, nullptr, "GetBleChannelMap"},
|
||||
|
||||
@@ -15,7 +15,6 @@ CAPS_SS::CAPS_SS(Core::System& system_) : ServiceFramework{system_, "caps:ss"} {
|
||||
{204, nullptr, "SaveEditedScreenShotEx0"},
|
||||
{206, nullptr, "Unknown206"},
|
||||
{208, nullptr, "SaveScreenShotOfMovieEx1"},
|
||||
{1000, nullptr, "Unknown1000"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
||||
@@ -55,8 +55,6 @@ public:
|
||||
{36, nullptr, "DeleteAllInactiveELicenseRequiredPersonalizedTicket"},
|
||||
{37, nullptr, "OwnTicket2"},
|
||||
{38, nullptr, "OwnTicket3"},
|
||||
{39, nullptr, "DeleteAllInactivePersonalizedTicket"},
|
||||
{40, nullptr, "DeletePrepurchaseRecordByNintendoAccountId"},
|
||||
{501, nullptr, "Unknown501"},
|
||||
{502, nullptr, "Unknown502"},
|
||||
{503, nullptr, "GetTitleKey"},
|
||||
@@ -90,15 +88,11 @@ public:
|
||||
{1503, nullptr, "Unknown1503"},
|
||||
{1504, nullptr, "Unknown1504"},
|
||||
{1505, nullptr, "Unknown1505"},
|
||||
{1506, nullptr, "Unknown1506"},
|
||||
{2000, nullptr, "Unknown2000"},
|
||||
{2001, nullptr, "Unknown2001"},
|
||||
{2002, nullptr, "Unknown2002"},
|
||||
{2003, nullptr, "Unknown2003"},
|
||||
{2100, nullptr, "Unknown2100"},
|
||||
{2501, nullptr, "Unknown2501"},
|
||||
{2502, nullptr, "Unknown2502"},
|
||||
{2601, nullptr, "Unknown2601"},
|
||||
{3001, nullptr, "Unknown3001"},
|
||||
{3002, nullptr, "Unknown3002"},
|
||||
};
|
||||
|
||||
@@ -261,18 +261,6 @@ ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType(
|
||||
return FileSys::ERROR_PATH_NOT_FOUND;
|
||||
}
|
||||
|
||||
ResultVal<FileSys::FileTimeStampRaw> VfsDirectoryServiceWrapper::GetFileTimeStampRaw(
|
||||
const std::string& path) const {
|
||||
auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
|
||||
if (dir == nullptr) {
|
||||
return FileSys::ERROR_PATH_NOT_FOUND;
|
||||
}
|
||||
if (GetEntryType(path).Failed()) {
|
||||
return FileSys::ERROR_PATH_NOT_FOUND;
|
||||
}
|
||||
return MakeResult(dir->GetFileTimeStamp(Common::FS::GetFilename(path)));
|
||||
}
|
||||
|
||||
FileSystemController::FileSystemController(Core::System& system_) : system{system_} {}
|
||||
|
||||
FileSystemController::~FileSystemController() = default;
|
||||
|
||||
@@ -240,12 +240,6 @@ public:
|
||||
*/
|
||||
ResultVal<FileSys::EntryType> GetEntryType(const std::string& path) const;
|
||||
|
||||
/**
|
||||
* Get the timestamp of the specified path
|
||||
* @return The timestamp of the specified path or error code
|
||||
*/
|
||||
ResultVal<FileSys::FileTimeStampRaw> GetFileTimeStampRaw(const std::string& path) const;
|
||||
|
||||
private:
|
||||
FileSys::VirtualDir backing;
|
||||
};
|
||||
|
||||
@@ -326,7 +326,7 @@ public:
|
||||
{11, &IFileSystem::GetFreeSpaceSize, "GetFreeSpaceSize"},
|
||||
{12, &IFileSystem::GetTotalSpaceSize, "GetTotalSpaceSize"},
|
||||
{13, &IFileSystem::CleanDirectoryRecursively, "CleanDirectoryRecursively"},
|
||||
{14, &IFileSystem::GetFileTimeStampRaw, "GetFileTimeStampRaw"},
|
||||
{14, nullptr, "GetFileTimeStampRaw"},
|
||||
{15, nullptr, "QueryEntry"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
@@ -501,24 +501,6 @@ public:
|
||||
rb.Push(size.get_total_size());
|
||||
}
|
||||
|
||||
void GetFileTimeStampRaw(Kernel::HLERequestContext& ctx) {
|
||||
const auto file_buffer = ctx.ReadBuffer();
|
||||
const std::string name = Common::StringFromBuffer(file_buffer);
|
||||
|
||||
LOG_WARNING(Service_FS, "(Partial Implementation) called. file={}", name);
|
||||
|
||||
auto result = backend.GetFileTimeStampRaw(name);
|
||||
if (result.Failed()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result.Code());
|
||||
return;
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 10};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushRaw(*result);
|
||||
}
|
||||
|
||||
private:
|
||||
VfsDirectoryServiceWrapper backend;
|
||||
SizeGetter size;
|
||||
|
||||
@@ -507,7 +507,6 @@ private:
|
||||
LarkNesRight = 18,
|
||||
Lucia = 19,
|
||||
Verification = 20,
|
||||
Lagon = 21,
|
||||
};
|
||||
|
||||
struct NPadEntry {
|
||||
|
||||
@@ -239,12 +239,6 @@ Hid::Hid(Core::System& system_)
|
||||
{81, &Hid::ResetGyroscopeZeroDriftMode, "ResetGyroscopeZeroDriftMode"},
|
||||
{82, &Hid::IsSixAxisSensorAtRest, "IsSixAxisSensorAtRest"},
|
||||
{83, &Hid::IsFirmwareUpdateAvailableForSixAxisSensor, "IsFirmwareUpdateAvailableForSixAxisSensor"},
|
||||
{84, nullptr, "EnableSixAxisSensorUnalteredPassthrough"},
|
||||
{85, nullptr, "IsSixAxisSensorUnalteredPassthroughEnabled"},
|
||||
{86, nullptr, "StoreSixAxisSensorCalibrationParameter"},
|
||||
{87, nullptr, "LoadSixAxisSensorCalibrationParameter"},
|
||||
{88, nullptr, "GetSixAxisSensorIcInformation"},
|
||||
{89, nullptr, "ResetIsSixAxisSensorDeviceNewlyAssigned"},
|
||||
{91, &Hid::ActivateGesture, "ActivateGesture"},
|
||||
{100, &Hid::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"},
|
||||
{101, &Hid::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"},
|
||||
@@ -1662,9 +1656,6 @@ public:
|
||||
{12, nullptr, "UnsetTouchScreenAutoPilotState"},
|
||||
{13, nullptr, "GetTouchScreenConfiguration"},
|
||||
{14, nullptr, "ProcessTouchScreenAutoTune"},
|
||||
{15, nullptr, "ForceStopTouchScreenManagement"},
|
||||
{16, nullptr, "ForceRestartTouchScreenManagement"},
|
||||
{17, nullptr, "IsTouchScreenManaged"},
|
||||
{20, nullptr, "DeactivateMouse"},
|
||||
{21, nullptr, "SetMouseAutoPilotState"},
|
||||
{22, nullptr, "UnsetMouseAutoPilotState"},
|
||||
|
||||
@@ -15,7 +15,7 @@ public:
|
||||
explicit IService(Core::System& system_) : ServiceFramework{system_, "ngct:u"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IService::Match, "Match"},
|
||||
{0, nullptr, "Match"},
|
||||
{1, &IService::Filter, "Filter"},
|
||||
};
|
||||
// clang-format on
|
||||
@@ -24,19 +24,6 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
void Match(Kernel::HLERequestContext& ctx) {
|
||||
const auto buffer = ctx.ReadBuffer();
|
||||
const auto text = Common::StringFromFixedZeroTerminatedBuffer(
|
||||
reinterpret_cast<const char*>(buffer.data()), buffer.size());
|
||||
|
||||
LOG_WARNING(Service_NGCT, "(STUBBED) called, text={}", text);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
// Return false since we don't censor anything
|
||||
rb.Push(false);
|
||||
}
|
||||
|
||||
void Filter(Kernel::HLERequestContext& ctx) {
|
||||
const auto buffer = ctx.ReadBuffer();
|
||||
const auto text = Common::StringFromFixedZeroTerminatedBuffer(
|
||||
|
||||
@@ -31,7 +31,6 @@ public:
|
||||
{24, nullptr, "DestroyTokenWithApplicationId"},
|
||||
{25, nullptr, "QueryIsTokenValid"},
|
||||
{26, nullptr, "ListenToMyApplicationId"},
|
||||
{27, nullptr, "DestroyTokenAll"},
|
||||
{31, nullptr, "UploadTokenToBaaS"},
|
||||
{32, nullptr, "DestroyTokenForBaaS"},
|
||||
{33, nullptr, "CreateTokenForBaaS"},
|
||||
|
||||
@@ -415,18 +415,6 @@ void BSD::Write(Kernel::HLERequestContext& ctx) {
|
||||
});
|
||||
}
|
||||
|
||||
void BSD::Read(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const s32 fd = rp.Pop<s32>();
|
||||
|
||||
LOG_WARNING(Service, "(STUBBED) called. fd={} len={}", fd, ctx.GetWriteBufferSize());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push<u32>(0); // ret
|
||||
rb.Push<u32>(0); // bsd errno
|
||||
}
|
||||
|
||||
void BSD::Close(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const s32 fd = rp.Pop<s32>();
|
||||
@@ -867,7 +855,7 @@ BSD::BSD(Core::System& system_, const char* name) : ServiceFramework{system_, na
|
||||
{22, &BSD::Shutdown, "Shutdown"},
|
||||
{23, nullptr, "ShutdownAllSockets"},
|
||||
{24, &BSD::Write, "Write"},
|
||||
{25, &BSD::Read, "Read"},
|
||||
{25, nullptr, "Read"},
|
||||
{26, &BSD::Close, "Close"},
|
||||
{27, nullptr, "DuplicateSocket"},
|
||||
{28, nullptr, "GetResourceStatistics"},
|
||||
|
||||
@@ -135,7 +135,6 @@ private:
|
||||
void Send(Kernel::HLERequestContext& ctx);
|
||||
void SendTo(Kernel::HLERequestContext& ctx);
|
||||
void Write(Kernel::HLERequestContext& ctx);
|
||||
void Read(Kernel::HLERequestContext& ctx);
|
||||
void Close(Kernel::HLERequestContext& ctx);
|
||||
void EventFd(Kernel::HLERequestContext& ctx);
|
||||
|
||||
|
||||
@@ -97,7 +97,7 @@ public:
|
||||
{3, nullptr, "GetAlternateInterface"},
|
||||
{4, nullptr, "GetCurrentFrame"},
|
||||
{5, nullptr, "CtrlXferAsync"},
|
||||
{6, nullptr, "GetCtrlXferCompletionEvent"},
|
||||
{6, nullptr, "Unknown6"},
|
||||
{7, nullptr, "GetCtrlXferReport"},
|
||||
{8, nullptr, "ResetDevice"},
|
||||
{9, nullptr, "OpenUsbEp"},
|
||||
@@ -183,8 +183,8 @@ public:
|
||||
{4, nullptr, "GetHostPdcFirmwareRevision"},
|
||||
{5, nullptr, "GetHostPdcManufactureId"},
|
||||
{6, nullptr, "GetHostPdcDeviceId"},
|
||||
{7, nullptr, "EnableCradleRecovery"},
|
||||
{8, nullptr, "DisableCradleRecovery"},
|
||||
{7, nullptr, "AwakeCradle"},
|
||||
{8, nullptr, "SleepCradle"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
||||
@@ -831,7 +831,6 @@ public:
|
||||
{6010, nullptr, "GetLayerPresentationAllFencesExpiredEvent"},
|
||||
{6011, nullptr, "EnableLayerAutoClearTransitionBuffer"},
|
||||
{6012, nullptr, "DisableLayerAutoClearTransitionBuffer"},
|
||||
{6013, nullptr, "SetLayerOpacity"},
|
||||
{7000, nullptr, "SetContentVisibility"},
|
||||
{8000, nullptr, "SetConductorLayer"},
|
||||
{8001, nullptr, "SetTimestampTracking"},
|
||||
|
||||
@@ -226,6 +226,8 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader,
|
||||
// Log user configuration information
|
||||
constexpr auto field_type = Telemetry::FieldType::UserConfig;
|
||||
AddField(field_type, "Audio_SinkId", Settings::values.sink_id.GetValue());
|
||||
AddField(field_type, "Audio_EnableAudioStretching",
|
||||
Settings::values.enable_audio_stretching.GetValue());
|
||||
AddField(field_type, "Core_UseMultiCore", Settings::values.use_multi_core.GetValue());
|
||||
AddField(field_type, "Renderer_Backend",
|
||||
TranslateRenderer(Settings::values.renderer_backend.GetValue()));
|
||||
|
||||
@@ -21,10 +21,6 @@ add_library(input_common STATIC
|
||||
mouse/mouse_poller.h
|
||||
sdl/sdl.cpp
|
||||
sdl/sdl.h
|
||||
tas/tas_input.cpp
|
||||
tas/tas_input.h
|
||||
tas/tas_poller.cpp
|
||||
tas/tas_poller.h
|
||||
udp/client.cpp
|
||||
udp/client.h
|
||||
udp/protocol.cpp
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include "common/param_package.h"
|
||||
#include "common/settings.h"
|
||||
#include "input_common/analog_from_button.h"
|
||||
#include "input_common/gcadapter/gc_adapter.h"
|
||||
#include "input_common/gcadapter/gc_poller.h"
|
||||
@@ -14,8 +13,6 @@
|
||||
#include "input_common/motion_from_button.h"
|
||||
#include "input_common/mouse/mouse_input.h"
|
||||
#include "input_common/mouse/mouse_poller.h"
|
||||
#include "input_common/tas/tas_input.h"
|
||||
#include "input_common/tas/tas_poller.h"
|
||||
#include "input_common/touch_from_button.h"
|
||||
#include "input_common/udp/client.h"
|
||||
#include "input_common/udp/udp.h"
|
||||
@@ -63,12 +60,6 @@ struct InputSubsystem::Impl {
|
||||
Input::RegisterFactory<Input::MotionDevice>("mouse", mousemotion);
|
||||
mousetouch = std::make_shared<MouseTouchFactory>(mouse);
|
||||
Input::RegisterFactory<Input::TouchDevice>("mouse", mousetouch);
|
||||
|
||||
tas = std::make_shared<TasInput::Tas>();
|
||||
tasbuttons = std::make_shared<TasButtonFactory>(tas);
|
||||
Input::RegisterFactory<Input::ButtonDevice>("tas", tasbuttons);
|
||||
tasanalog = std::make_shared<TasAnalogFactory>(tas);
|
||||
Input::RegisterFactory<Input::AnalogDevice>("tas", tasanalog);
|
||||
}
|
||||
|
||||
void Shutdown() {
|
||||
@@ -103,12 +94,6 @@ struct InputSubsystem::Impl {
|
||||
mouseanalog.reset();
|
||||
mousemotion.reset();
|
||||
mousetouch.reset();
|
||||
|
||||
Input::UnregisterFactory<Input::ButtonDevice>("tas");
|
||||
Input::UnregisterFactory<Input::AnalogDevice>("tas");
|
||||
|
||||
tasbuttons.reset();
|
||||
tasanalog.reset();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<Common::ParamPackage> GetInputDevices() const {
|
||||
@@ -116,10 +101,6 @@ struct InputSubsystem::Impl {
|
||||
Common::ParamPackage{{"display", "Any"}, {"class", "any"}},
|
||||
Common::ParamPackage{{"display", "Keyboard/Mouse"}, {"class", "keyboard"}},
|
||||
};
|
||||
if (Settings::values.tas_enable) {
|
||||
devices.emplace_back(
|
||||
Common::ParamPackage{{"display", "TAS Controller"}, {"class", "tas"}});
|
||||
}
|
||||
#ifdef HAVE_SDL2
|
||||
auto sdl_devices = sdl->GetInputDevices();
|
||||
devices.insert(devices.end(), sdl_devices.begin(), sdl_devices.end());
|
||||
@@ -139,9 +120,6 @@ struct InputSubsystem::Impl {
|
||||
if (params.Get("class", "") == "gcpad") {
|
||||
return gcadapter->GetAnalogMappingForDevice(params);
|
||||
}
|
||||
if (params.Get("class", "") == "tas") {
|
||||
return tas->GetAnalogMappingForDevice(params);
|
||||
}
|
||||
#ifdef HAVE_SDL2
|
||||
if (params.Get("class", "") == "sdl") {
|
||||
return sdl->GetAnalogMappingForDevice(params);
|
||||
@@ -158,9 +136,6 @@ struct InputSubsystem::Impl {
|
||||
if (params.Get("class", "") == "gcpad") {
|
||||
return gcadapter->GetButtonMappingForDevice(params);
|
||||
}
|
||||
if (params.Get("class", "") == "tas") {
|
||||
return tas->GetButtonMappingForDevice(params);
|
||||
}
|
||||
#ifdef HAVE_SDL2
|
||||
if (params.Get("class", "") == "sdl") {
|
||||
return sdl->GetButtonMappingForDevice(params);
|
||||
@@ -199,12 +174,9 @@ struct InputSubsystem::Impl {
|
||||
std::shared_ptr<MouseAnalogFactory> mouseanalog;
|
||||
std::shared_ptr<MouseMotionFactory> mousemotion;
|
||||
std::shared_ptr<MouseTouchFactory> mousetouch;
|
||||
std::shared_ptr<TasButtonFactory> tasbuttons;
|
||||
std::shared_ptr<TasAnalogFactory> tasanalog;
|
||||
std::shared_ptr<CemuhookUDP::Client> udp;
|
||||
std::shared_ptr<GCAdapter::Adapter> gcadapter;
|
||||
std::shared_ptr<MouseInput::Mouse> mouse;
|
||||
std::shared_ptr<TasInput::Tas> tas;
|
||||
};
|
||||
|
||||
InputSubsystem::InputSubsystem() : impl{std::make_unique<Impl>()} {}
|
||||
@@ -235,14 +207,6 @@ const MouseInput::Mouse* InputSubsystem::GetMouse() const {
|
||||
return impl->mouse.get();
|
||||
}
|
||||
|
||||
TasInput::Tas* InputSubsystem::GetTas() {
|
||||
return impl->tas.get();
|
||||
}
|
||||
|
||||
const TasInput::Tas* InputSubsystem::GetTas() const {
|
||||
return impl->tas.get();
|
||||
}
|
||||
|
||||
std::vector<Common::ParamPackage> InputSubsystem::GetInputDevices() const {
|
||||
return impl->GetInputDevices();
|
||||
}
|
||||
@@ -323,22 +287,6 @@ const MouseTouchFactory* InputSubsystem::GetMouseTouch() const {
|
||||
return impl->mousetouch.get();
|
||||
}
|
||||
|
||||
TasButtonFactory* InputSubsystem::GetTasButtons() {
|
||||
return impl->tasbuttons.get();
|
||||
}
|
||||
|
||||
const TasButtonFactory* InputSubsystem::GetTasButtons() const {
|
||||
return impl->tasbuttons.get();
|
||||
}
|
||||
|
||||
TasAnalogFactory* InputSubsystem::GetTasAnalogs() {
|
||||
return impl->tasanalog.get();
|
||||
}
|
||||
|
||||
const TasAnalogFactory* InputSubsystem::GetTasAnalogs() const {
|
||||
return impl->tasanalog.get();
|
||||
}
|
||||
|
||||
void InputSubsystem::ReloadInputDevices() {
|
||||
if (!impl->udp) {
|
||||
return;
|
||||
@@ -346,8 +294,8 @@ void InputSubsystem::ReloadInputDevices() {
|
||||
impl->udp->ReloadSockets();
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<Polling::DevicePoller>> InputSubsystem::GetPollers(
|
||||
[[maybe_unused]] Polling::DeviceType type) const {
|
||||
std::vector<std::unique_ptr<Polling::DevicePoller>> InputSubsystem::GetPollers([
|
||||
[maybe_unused]] Polling::DeviceType type) const {
|
||||
#ifdef HAVE_SDL2
|
||||
return impl->sdl->GetPollers(type);
|
||||
#else
|
||||
|
||||
@@ -29,10 +29,6 @@ namespace MouseInput {
|
||||
class Mouse;
|
||||
}
|
||||
|
||||
namespace TasInput {
|
||||
class Tas;
|
||||
}
|
||||
|
||||
namespace InputCommon {
|
||||
namespace Polling {
|
||||
|
||||
@@ -68,8 +64,6 @@ class MouseButtonFactory;
|
||||
class MouseAnalogFactory;
|
||||
class MouseMotionFactory;
|
||||
class MouseTouchFactory;
|
||||
class TasButtonFactory;
|
||||
class TasAnalogFactory;
|
||||
class Keyboard;
|
||||
|
||||
/**
|
||||
@@ -109,11 +103,6 @@ public:
|
||||
/// Retrieves the underlying mouse device.
|
||||
[[nodiscard]] const MouseInput::Mouse* GetMouse() const;
|
||||
|
||||
/// Retrieves the underlying tas device.
|
||||
[[nodiscard]] TasInput::Tas* GetTas();
|
||||
|
||||
/// Retrieves the underlying tas device.
|
||||
[[nodiscard]] const TasInput::Tas* GetTas() const;
|
||||
/**
|
||||
* Returns all available input devices that this Factory can create a new device with.
|
||||
* Each returned ParamPackage should have a `display` field used for display, a class field for
|
||||
@@ -155,42 +144,30 @@ public:
|
||||
/// Retrieves the underlying udp touch handler.
|
||||
[[nodiscard]] const UDPTouchFactory* GetUDPTouch() const;
|
||||
|
||||
/// Retrieves the underlying mouse button handler.
|
||||
/// Retrieves the underlying GameCube button handler.
|
||||
[[nodiscard]] MouseButtonFactory* GetMouseButtons();
|
||||
|
||||
/// Retrieves the underlying mouse button handler.
|
||||
/// Retrieves the underlying GameCube button handler.
|
||||
[[nodiscard]] const MouseButtonFactory* GetMouseButtons() const;
|
||||
|
||||
/// Retrieves the underlying mouse analog handler.
|
||||
/// Retrieves the underlying udp touch handler.
|
||||
[[nodiscard]] MouseAnalogFactory* GetMouseAnalogs();
|
||||
|
||||
/// Retrieves the underlying mouse analog handler.
|
||||
/// Retrieves the underlying udp touch handler.
|
||||
[[nodiscard]] const MouseAnalogFactory* GetMouseAnalogs() const;
|
||||
|
||||
/// Retrieves the underlying mouse motion handler.
|
||||
/// Retrieves the underlying udp motion handler.
|
||||
[[nodiscard]] MouseMotionFactory* GetMouseMotions();
|
||||
|
||||
/// Retrieves the underlying mouse motion handler.
|
||||
/// Retrieves the underlying udp motion handler.
|
||||
[[nodiscard]] const MouseMotionFactory* GetMouseMotions() const;
|
||||
|
||||
/// Retrieves the underlying mouse touch handler.
|
||||
/// Retrieves the underlying udp touch handler.
|
||||
[[nodiscard]] MouseTouchFactory* GetMouseTouch();
|
||||
|
||||
/// Retrieves the underlying mouse touch handler.
|
||||
/// Retrieves the underlying udp touch handler.
|
||||
[[nodiscard]] const MouseTouchFactory* GetMouseTouch() const;
|
||||
|
||||
/// Retrieves the underlying tas button handler.
|
||||
[[nodiscard]] TasButtonFactory* GetTasButtons();
|
||||
|
||||
/// Retrieves the underlying tas button handler.
|
||||
[[nodiscard]] const TasButtonFactory* GetTasButtons() const;
|
||||
|
||||
/// Retrieves the underlying tas analogs handler.
|
||||
[[nodiscard]] TasAnalogFactory* GetTasAnalogs();
|
||||
|
||||
/// Retrieves the underlying tas analogs handler.
|
||||
[[nodiscard]] const TasAnalogFactory* GetTasAnalogs() const;
|
||||
|
||||
/// Reloads the input devices
|
||||
void ReloadInputDevices();
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
#include "common/logging/log.h"
|
||||
#include "common/math_util.h"
|
||||
#include "common/param_package.h"
|
||||
#include "common/settings.h"
|
||||
#include "common/settings_input.h"
|
||||
#include "common/threadsafe_queue.h"
|
||||
#include "core/frontend/input.h"
|
||||
#include "input_common/motion_input.h"
|
||||
@@ -889,10 +889,8 @@ SDLState::SDLState() {
|
||||
RegisterFactory<VibrationDevice>("sdl", vibration_factory);
|
||||
RegisterFactory<MotionDevice>("sdl", motion_factory);
|
||||
|
||||
if (!Settings::values.enable_raw_input) {
|
||||
// Disable raw input. When enabled this setting causes SDL to die when a web applet opens
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_RAWINPUT, "0");
|
||||
}
|
||||
// Disable raw input. When enabled this setting causes SDL to die when a web applet opens
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_RAWINPUT, "0");
|
||||
|
||||
// Enable HIDAPI rumble. This prevents SDL from disabling motion on PS4 and PS5 controllers
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, "1");
|
||||
@@ -900,10 +898,10 @@ SDLState::SDLState() {
|
||||
|
||||
// Tell SDL2 to use the hidapi driver. This will allow joycons to be detected as a
|
||||
// GameController and not a generic one
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1");
|
||||
SDL_SetHint("SDL_JOYSTICK_HIDAPI_JOY_CONS", "1");
|
||||
|
||||
// Turn off Pro controller home led
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_HOME_LED, "0");
|
||||
SDL_SetHint("SDL_JOYSTICK_HIDAPI_SWITCH_HOME_LED", "0");
|
||||
|
||||
// If the frontend is going to manage the event loop, then we don't start one here
|
||||
start_thread = SDL_WasInit(SDL_INIT_JOYSTICK) == 0;
|
||||
|
||||
@@ -1,455 +0,0 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cstring>
|
||||
#include <regex>
|
||||
|
||||
#include "common/fs/file.h"
|
||||
#include "common/fs/fs_types.h"
|
||||
#include "common/fs/path_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/settings.h"
|
||||
#include "input_common/tas/tas_input.h"
|
||||
|
||||
namespace TasInput {
|
||||
|
||||
// Supported keywords and buttons from a TAS file
|
||||
constexpr std::array<std::pair<std::string_view, TasButton>, 20> text_to_tas_button = {
|
||||
std::pair{"KEY_A", TasButton::BUTTON_A},
|
||||
{"KEY_B", TasButton::BUTTON_B},
|
||||
{"KEY_X", TasButton::BUTTON_X},
|
||||
{"KEY_Y", TasButton::BUTTON_Y},
|
||||
{"KEY_LSTICK", TasButton::STICK_L},
|
||||
{"KEY_RSTICK", TasButton::STICK_R},
|
||||
{"KEY_L", TasButton::TRIGGER_L},
|
||||
{"KEY_R", TasButton::TRIGGER_R},
|
||||
{"KEY_PLUS", TasButton::BUTTON_PLUS},
|
||||
{"KEY_MINUS", TasButton::BUTTON_MINUS},
|
||||
{"KEY_DLEFT", TasButton::BUTTON_LEFT},
|
||||
{"KEY_DUP", TasButton::BUTTON_UP},
|
||||
{"KEY_DRIGHT", TasButton::BUTTON_RIGHT},
|
||||
{"KEY_DDOWN", TasButton::BUTTON_DOWN},
|
||||
{"KEY_SL", TasButton::BUTTON_SL},
|
||||
{"KEY_SR", TasButton::BUTTON_SR},
|
||||
{"KEY_CAPTURE", TasButton::BUTTON_CAPTURE},
|
||||
{"KEY_HOME", TasButton::BUTTON_HOME},
|
||||
{"KEY_ZL", TasButton::TRIGGER_ZL},
|
||||
{"KEY_ZR", TasButton::TRIGGER_ZR},
|
||||
};
|
||||
|
||||
Tas::Tas() {
|
||||
if (!Settings::values.tas_enable) {
|
||||
needs_reset = true;
|
||||
return;
|
||||
}
|
||||
LoadTasFiles();
|
||||
}
|
||||
|
||||
Tas::~Tas() {
|
||||
Stop();
|
||||
};
|
||||
|
||||
void Tas::LoadTasFiles() {
|
||||
script_length = 0;
|
||||
for (size_t i = 0; i < commands.size(); i++) {
|
||||
LoadTasFile(i);
|
||||
if (commands[i].size() > script_length) {
|
||||
script_length = commands[i].size();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Tas::LoadTasFile(size_t player_index) {
|
||||
if (!commands[player_index].empty()) {
|
||||
commands[player_index].clear();
|
||||
}
|
||||
std::string file =
|
||||
Common::FS::ReadStringFromFile(Common::FS::GetYuzuPath(Common::FS::YuzuPath::TASDir) /
|
||||
fmt::format("script0-{}.txt", player_index + 1),
|
||||
Common::FS::FileType::BinaryFile);
|
||||
std::stringstream command_line(file);
|
||||
std::string line;
|
||||
int frame_no = 0;
|
||||
while (std::getline(command_line, line, '\n')) {
|
||||
if (line.empty()) {
|
||||
continue;
|
||||
}
|
||||
LOG_DEBUG(Input, "Loading line: {}", line);
|
||||
std::smatch m;
|
||||
|
||||
std::stringstream linestream(line);
|
||||
std::string segment;
|
||||
std::vector<std::string> seglist;
|
||||
|
||||
while (std::getline(linestream, segment, ' ')) {
|
||||
seglist.push_back(segment);
|
||||
}
|
||||
|
||||
if (seglist.size() < 4) {
|
||||
continue;
|
||||
}
|
||||
|
||||
while (frame_no < std::stoi(seglist.at(0))) {
|
||||
commands[player_index].push_back({});
|
||||
frame_no++;
|
||||
}
|
||||
|
||||
TASCommand command = {
|
||||
.buttons = ReadCommandButtons(seglist.at(1)),
|
||||
.l_axis = ReadCommandAxis(seglist.at(2)),
|
||||
.r_axis = ReadCommandAxis(seglist.at(3)),
|
||||
};
|
||||
commands[player_index].push_back(command);
|
||||
frame_no++;
|
||||
}
|
||||
LOG_INFO(Input, "TAS file loaded! {} frames", frame_no);
|
||||
}
|
||||
|
||||
void Tas::WriteTasFile(std::u8string file_name) {
|
||||
std::string output_text;
|
||||
for (size_t frame = 0; frame < record_commands.size(); frame++) {
|
||||
if (!output_text.empty()) {
|
||||
output_text += "\n";
|
||||
}
|
||||
const TASCommand& line = record_commands[frame];
|
||||
output_text += std::to_string(frame) + " " + WriteCommandButtons(line.buttons) + " " +
|
||||
WriteCommandAxis(line.l_axis) + " " + WriteCommandAxis(line.r_axis);
|
||||
}
|
||||
const auto bytes_written = Common::FS::WriteStringToFile(
|
||||
Common::FS::GetYuzuPath(Common::FS::YuzuPath::TASDir) / file_name,
|
||||
Common::FS::FileType::TextFile, output_text);
|
||||
if (bytes_written == output_text.size()) {
|
||||
LOG_INFO(Input, "TAS file written to file!");
|
||||
} else {
|
||||
LOG_ERROR(Input, "Writing the TAS-file has failed! {} / {} bytes written", bytes_written,
|
||||
output_text.size());
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<float, float> Tas::FlipAxisY(std::pair<float, float> old) {
|
||||
auto [x, y] = old;
|
||||
return {x, -y};
|
||||
}
|
||||
|
||||
void Tas::RecordInput(u32 buttons, const std::array<std::pair<float, float>, 2>& axes) {
|
||||
last_input = {buttons, FlipAxisY(axes[0]), FlipAxisY(axes[1])};
|
||||
}
|
||||
|
||||
std::tuple<TasState, size_t, size_t> Tas::GetStatus() const {
|
||||
TasState state;
|
||||
if (is_recording) {
|
||||
return {TasState::Recording, 0, record_commands.size()};
|
||||
}
|
||||
|
||||
if (is_running) {
|
||||
state = TasState::Running;
|
||||
} else {
|
||||
state = TasState::Stopped;
|
||||
}
|
||||
|
||||
return {state, current_command, script_length};
|
||||
}
|
||||
|
||||
std::string Tas::DebugButtons(u32 buttons) const {
|
||||
return fmt::format("{{ {} }}", TasInput::Tas::ButtonsToString(buttons));
|
||||
}
|
||||
|
||||
std::string Tas::DebugJoystick(float x, float y) const {
|
||||
return fmt::format("[ {} , {} ]", std::to_string(x), std::to_string(y));
|
||||
}
|
||||
|
||||
std::string Tas::DebugInput(const TasData& data) const {
|
||||
return fmt::format("{{ {} , {} , {} }}", DebugButtons(data.buttons),
|
||||
DebugJoystick(data.axis[0], data.axis[1]),
|
||||
DebugJoystick(data.axis[2], data.axis[3]));
|
||||
}
|
||||
|
||||
std::string Tas::DebugInputs(const std::array<TasData, PLAYER_NUMBER>& arr) const {
|
||||
std::string returns = "[ ";
|
||||
for (size_t i = 0; i < arr.size(); i++) {
|
||||
returns += DebugInput(arr[i]);
|
||||
if (i != arr.size() - 1) {
|
||||
returns += " , ";
|
||||
}
|
||||
}
|
||||
return returns + "]";
|
||||
}
|
||||
|
||||
std::string Tas::ButtonsToString(u32 button) const {
|
||||
std::string returns;
|
||||
for (auto [text_button, tas_button] : text_to_tas_button) {
|
||||
if ((button & static_cast<u32>(tas_button)) != 0)
|
||||
returns += fmt::format(", {}", text_button.substr(4));
|
||||
}
|
||||
return returns.empty() ? "" : returns.substr(2);
|
||||
}
|
||||
|
||||
void Tas::UpdateThread() {
|
||||
if (!Settings::values.tas_enable) {
|
||||
if (is_running) {
|
||||
Stop();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_recording) {
|
||||
record_commands.push_back(last_input);
|
||||
}
|
||||
if (needs_reset) {
|
||||
current_command = 0;
|
||||
needs_reset = false;
|
||||
LoadTasFiles();
|
||||
LOG_DEBUG(Input, "tas_reset done");
|
||||
}
|
||||
|
||||
if (!is_running) {
|
||||
tas_data.fill({});
|
||||
return;
|
||||
}
|
||||
if (current_command < script_length) {
|
||||
LOG_DEBUG(Input, "Playing TAS {}/{}", current_command, script_length);
|
||||
size_t frame = current_command++;
|
||||
for (size_t i = 0; i < commands.size(); i++) {
|
||||
if (frame < commands[i].size()) {
|
||||
TASCommand command = commands[i][frame];
|
||||
tas_data[i].buttons = command.buttons;
|
||||
auto [l_axis_x, l_axis_y] = command.l_axis;
|
||||
tas_data[i].axis[0] = l_axis_x;
|
||||
tas_data[i].axis[1] = l_axis_y;
|
||||
auto [r_axis_x, r_axis_y] = command.r_axis;
|
||||
tas_data[i].axis[2] = r_axis_x;
|
||||
tas_data[i].axis[3] = r_axis_y;
|
||||
} else {
|
||||
tas_data[i] = {};
|
||||
}
|
||||
}
|
||||
} else {
|
||||
is_running = Settings::values.tas_loop.GetValue();
|
||||
current_command = 0;
|
||||
tas_data.fill({});
|
||||
if (!is_running) {
|
||||
SwapToStoredController();
|
||||
}
|
||||
}
|
||||
LOG_DEBUG(Input, "TAS inputs: {}", DebugInputs(tas_data));
|
||||
}
|
||||
|
||||
TasAnalog Tas::ReadCommandAxis(const std::string& line) const {
|
||||
std::stringstream linestream(line);
|
||||
std::string segment;
|
||||
std::vector<std::string> seglist;
|
||||
|
||||
while (std::getline(linestream, segment, ';')) {
|
||||
seglist.push_back(segment);
|
||||
}
|
||||
|
||||
const float x = std::stof(seglist.at(0)) / 32767.0f;
|
||||
const float y = std::stof(seglist.at(1)) / 32767.0f;
|
||||
|
||||
return {x, y};
|
||||
}
|
||||
|
||||
u32 Tas::ReadCommandButtons(const std::string& data) const {
|
||||
std::stringstream button_text(data);
|
||||
std::string line;
|
||||
u32 buttons = 0;
|
||||
while (std::getline(button_text, line, ';')) {
|
||||
for (auto [text, tas_button] : text_to_tas_button) {
|
||||
if (text == line) {
|
||||
buttons |= static_cast<u32>(tas_button);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return buttons;
|
||||
}
|
||||
|
||||
std::string Tas::WriteCommandAxis(TasAnalog data) const {
|
||||
auto [x, y] = data;
|
||||
std::string line;
|
||||
line += std::to_string(static_cast<int>(x * 32767));
|
||||
line += ";";
|
||||
line += std::to_string(static_cast<int>(y * 32767));
|
||||
return line;
|
||||
}
|
||||
|
||||
std::string Tas::WriteCommandButtons(u32 data) const {
|
||||
if (data == 0) {
|
||||
return "NONE";
|
||||
}
|
||||
|
||||
std::string line;
|
||||
u32 index = 0;
|
||||
while (data > 0) {
|
||||
if ((data & 1) == 1) {
|
||||
for (auto [text, tas_button] : text_to_tas_button) {
|
||||
if (tas_button == static_cast<TasButton>(1 << index)) {
|
||||
if (line.size() > 0) {
|
||||
line += ";";
|
||||
}
|
||||
line += text;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
index++;
|
||||
data >>= 1;
|
||||
}
|
||||
return line;
|
||||
}
|
||||
|
||||
void Tas::StartStop() {
|
||||
if (!Settings::values.tas_enable) {
|
||||
return;
|
||||
}
|
||||
if (is_running) {
|
||||
Stop();
|
||||
} else {
|
||||
is_running = true;
|
||||
SwapToTasController();
|
||||
}
|
||||
}
|
||||
|
||||
void Tas::Stop() {
|
||||
is_running = false;
|
||||
SwapToStoredController();
|
||||
}
|
||||
|
||||
void Tas::SwapToTasController() {
|
||||
if (!Settings::values.tas_swap_controllers) {
|
||||
return;
|
||||
}
|
||||
auto& players = Settings::values.players.GetValue();
|
||||
for (std::size_t index = 0; index < players.size(); index++) {
|
||||
auto& player = players[index];
|
||||
player_mappings[index] = player;
|
||||
|
||||
// Only swap active controllers
|
||||
if (!player.connected) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Common::ParamPackage tas_param;
|
||||
tas_param.Set("pad", static_cast<u8>(index));
|
||||
auto button_mapping = GetButtonMappingForDevice(tas_param);
|
||||
auto analog_mapping = GetAnalogMappingForDevice(tas_param);
|
||||
auto& buttons = player.buttons;
|
||||
auto& analogs = player.analogs;
|
||||
|
||||
for (std::size_t i = 0; i < buttons.size(); ++i) {
|
||||
buttons[i] = button_mapping[static_cast<Settings::NativeButton::Values>(i)].Serialize();
|
||||
}
|
||||
for (std::size_t i = 0; i < analogs.size(); ++i) {
|
||||
analogs[i] = analog_mapping[static_cast<Settings::NativeAnalog::Values>(i)].Serialize();
|
||||
}
|
||||
}
|
||||
is_old_input_saved = true;
|
||||
Settings::values.is_device_reload_pending.store(true);
|
||||
}
|
||||
|
||||
void Tas::SwapToStoredController() {
|
||||
if (!is_old_input_saved) {
|
||||
return;
|
||||
}
|
||||
auto& players = Settings::values.players.GetValue();
|
||||
for (std::size_t index = 0; index < players.size(); index++) {
|
||||
players[index] = player_mappings[index];
|
||||
}
|
||||
is_old_input_saved = false;
|
||||
Settings::values.is_device_reload_pending.store(true);
|
||||
}
|
||||
|
||||
void Tas::Reset() {
|
||||
if (!Settings::values.tas_enable) {
|
||||
return;
|
||||
}
|
||||
needs_reset = true;
|
||||
}
|
||||
|
||||
bool Tas::Record() {
|
||||
if (!Settings::values.tas_enable) {
|
||||
return true;
|
||||
}
|
||||
is_recording = !is_recording;
|
||||
return is_recording;
|
||||
}
|
||||
|
||||
void Tas::SaveRecording(bool overwrite_file) {
|
||||
if (is_recording) {
|
||||
return;
|
||||
}
|
||||
if (record_commands.empty()) {
|
||||
return;
|
||||
}
|
||||
WriteTasFile(u8"record.txt");
|
||||
if (overwrite_file) {
|
||||
WriteTasFile(u8"script0-1.txt");
|
||||
}
|
||||
needs_reset = true;
|
||||
record_commands.clear();
|
||||
}
|
||||
|
||||
InputCommon::ButtonMapping Tas::GetButtonMappingForDevice(
|
||||
const Common::ParamPackage& params) const {
|
||||
// This list is missing ZL/ZR since those are not considered buttons.
|
||||
// We will add those afterwards
|
||||
// This list also excludes any button that can't be really mapped
|
||||
static constexpr std::array<std::pair<Settings::NativeButton::Values, TasButton>, 20>
|
||||
switch_to_tas_button = {
|
||||
std::pair{Settings::NativeButton::A, TasButton::BUTTON_A},
|
||||
{Settings::NativeButton::B, TasButton::BUTTON_B},
|
||||
{Settings::NativeButton::X, TasButton::BUTTON_X},
|
||||
{Settings::NativeButton::Y, TasButton::BUTTON_Y},
|
||||
{Settings::NativeButton::LStick, TasButton::STICK_L},
|
||||
{Settings::NativeButton::RStick, TasButton::STICK_R},
|
||||
{Settings::NativeButton::L, TasButton::TRIGGER_L},
|
||||
{Settings::NativeButton::R, TasButton::TRIGGER_R},
|
||||
{Settings::NativeButton::Plus, TasButton::BUTTON_PLUS},
|
||||
{Settings::NativeButton::Minus, TasButton::BUTTON_MINUS},
|
||||
{Settings::NativeButton::DLeft, TasButton::BUTTON_LEFT},
|
||||
{Settings::NativeButton::DUp, TasButton::BUTTON_UP},
|
||||
{Settings::NativeButton::DRight, TasButton::BUTTON_RIGHT},
|
||||
{Settings::NativeButton::DDown, TasButton::BUTTON_DOWN},
|
||||
{Settings::NativeButton::SL, TasButton::BUTTON_SL},
|
||||
{Settings::NativeButton::SR, TasButton::BUTTON_SR},
|
||||
{Settings::NativeButton::Screenshot, TasButton::BUTTON_CAPTURE},
|
||||
{Settings::NativeButton::Home, TasButton::BUTTON_HOME},
|
||||
{Settings::NativeButton::ZL, TasButton::TRIGGER_ZL},
|
||||
{Settings::NativeButton::ZR, TasButton::TRIGGER_ZR},
|
||||
};
|
||||
|
||||
InputCommon::ButtonMapping mapping{};
|
||||
for (const auto& [switch_button, tas_button] : switch_to_tas_button) {
|
||||
Common::ParamPackage button_params({{"engine", "tas"}});
|
||||
button_params.Set("pad", params.Get("pad", 0));
|
||||
button_params.Set("button", static_cast<int>(tas_button));
|
||||
mapping.insert_or_assign(switch_button, std::move(button_params));
|
||||
}
|
||||
|
||||
return mapping;
|
||||
}
|
||||
|
||||
InputCommon::AnalogMapping Tas::GetAnalogMappingForDevice(
|
||||
const Common::ParamPackage& params) const {
|
||||
|
||||
InputCommon::AnalogMapping mapping = {};
|
||||
Common::ParamPackage left_analog_params;
|
||||
left_analog_params.Set("engine", "tas");
|
||||
left_analog_params.Set("pad", params.Get("pad", 0));
|
||||
left_analog_params.Set("axis_x", static_cast<int>(TasAxes::StickX));
|
||||
left_analog_params.Set("axis_y", static_cast<int>(TasAxes::StickY));
|
||||
mapping.insert_or_assign(Settings::NativeAnalog::LStick, std::move(left_analog_params));
|
||||
Common::ParamPackage right_analog_params;
|
||||
right_analog_params.Set("engine", "tas");
|
||||
right_analog_params.Set("pad", params.Get("pad", 0));
|
||||
right_analog_params.Set("axis_x", static_cast<int>(TasAxes::SubstickX));
|
||||
right_analog_params.Set("axis_y", static_cast<int>(TasAxes::SubstickY));
|
||||
mapping.insert_or_assign(Settings::NativeAnalog::RStick, std::move(right_analog_params));
|
||||
return mapping;
|
||||
}
|
||||
|
||||
const TasData& Tas::GetTasState(std::size_t pad) const {
|
||||
return tas_data[pad];
|
||||
}
|
||||
} // namespace TasInput
|
||||
@@ -1,237 +0,0 @@
|
||||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/settings_input.h"
|
||||
#include "core/frontend/input.h"
|
||||
#include "input_common/main.h"
|
||||
|
||||
/*
|
||||
To play back TAS scripts on Yuzu, select the folder with scripts in the configuration menu below
|
||||
Tools -> Configure TAS. The file itself has normal text format and has to be called script0-1.txt
|
||||
for controller 1, script0-2.txt for controller 2 and so forth (with max. 8 players).
|
||||
|
||||
A script file has the same format as TAS-nx uses, so final files will look like this:
|
||||
|
||||
1 KEY_B 0;0 0;0
|
||||
6 KEY_ZL 0;0 0;0
|
||||
41 KEY_ZL;KEY_Y 0;0 0;0
|
||||
43 KEY_X;KEY_A 32767;0 0;0
|
||||
44 KEY_A 32767;0 0;0
|
||||
45 KEY_A 32767;0 0;0
|
||||
46 KEY_A 32767;0 0;0
|
||||
47 KEY_A 32767;0 0;0
|
||||
|
||||
After placing the file at the correct location, it can be read into Yuzu with the (default) hotkey
|
||||
CTRL+F6 (refresh). In the bottom left corner, it will display the amount of frames the script file
|
||||
has. Playback can be started or stopped using CTRL+F5.
|
||||
|
||||
However, for playback to actually work, the correct input device has to be selected: In the Controls
|
||||
menu, select TAS from the device list for the controller that the script should be played on.
|
||||
|
||||
Recording a new script file is really simple: Just make sure that the proper device (not TAS) is
|
||||
connected on P1, and press CTRL+F7 to start recording. When done, just press the same keystroke
|
||||
again (CTRL+F7). The new script will be saved at the location previously selected, as the filename
|
||||
record.txt.
|
||||
|
||||
For debugging purposes, the common controller debugger can be used (View -> Debugging -> Controller
|
||||
P1).
|
||||
*/
|
||||
|
||||
namespace TasInput {
|
||||
|
||||
constexpr size_t PLAYER_NUMBER = 8;
|
||||
|
||||
using TasAnalog = std::pair<float, float>;
|
||||
|
||||
enum class TasState {
|
||||
Running,
|
||||
Recording,
|
||||
Stopped,
|
||||
};
|
||||
|
||||
enum class TasButton : u32 {
|
||||
BUTTON_A = 1U << 0,
|
||||
BUTTON_B = 1U << 1,
|
||||
BUTTON_X = 1U << 2,
|
||||
BUTTON_Y = 1U << 3,
|
||||
STICK_L = 1U << 4,
|
||||
STICK_R = 1U << 5,
|
||||
TRIGGER_L = 1U << 6,
|
||||
TRIGGER_R = 1U << 7,
|
||||
TRIGGER_ZL = 1U << 8,
|
||||
TRIGGER_ZR = 1U << 9,
|
||||
BUTTON_PLUS = 1U << 10,
|
||||
BUTTON_MINUS = 1U << 11,
|
||||
BUTTON_LEFT = 1U << 12,
|
||||
BUTTON_UP = 1U << 13,
|
||||
BUTTON_RIGHT = 1U << 14,
|
||||
BUTTON_DOWN = 1U << 15,
|
||||
BUTTON_SL = 1U << 16,
|
||||
BUTTON_SR = 1U << 17,
|
||||
BUTTON_HOME = 1U << 18,
|
||||
BUTTON_CAPTURE = 1U << 19,
|
||||
};
|
||||
|
||||
enum class TasAxes : u8 {
|
||||
StickX,
|
||||
StickY,
|
||||
SubstickX,
|
||||
SubstickY,
|
||||
Undefined,
|
||||
};
|
||||
|
||||
struct TasData {
|
||||
u32 buttons{};
|
||||
std::array<float, 4> axis{};
|
||||
};
|
||||
|
||||
class Tas {
|
||||
public:
|
||||
Tas();
|
||||
~Tas();
|
||||
|
||||
// Changes the input status that will be stored in each frame
|
||||
void RecordInput(u32 buttons, const std::array<std::pair<float, float>, 2>& axes);
|
||||
|
||||
// Main loop that records or executes input
|
||||
void UpdateThread();
|
||||
|
||||
// Sets the flag to start or stop the TAS command excecution and swaps controllers profiles
|
||||
void StartStop();
|
||||
|
||||
// Stop the TAS and reverts any controller profile
|
||||
void Stop();
|
||||
|
||||
// Sets the flag to reload the file and start from the begining in the next update
|
||||
void Reset();
|
||||
|
||||
/**
|
||||
* Sets the flag to enable or disable recording of inputs
|
||||
* @return Returns true if the current recording status is enabled
|
||||
*/
|
||||
bool Record();
|
||||
|
||||
// Saves contents of record_commands on a file if overwrite is enabled player 1 will be
|
||||
// overwritten with the recorded commands
|
||||
void SaveRecording(bool overwrite_file);
|
||||
|
||||
/**
|
||||
* Returns the current status values of TAS playback/recording
|
||||
* @return Tuple of
|
||||
* TasState indicating the current state out of Running, Recording or Stopped ;
|
||||
* Current playback progress or amount of frames (so far) for Recording ;
|
||||
* Total length of script file currently loaded or amount of frames (so far) for Recording
|
||||
*/
|
||||
std::tuple<TasState, size_t, size_t> GetStatus() const;
|
||||
|
||||
// Retuns an array of the default button mappings
|
||||
InputCommon::ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) const;
|
||||
|
||||
// Retuns an array of the default analog mappings
|
||||
InputCommon::AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) const;
|
||||
[[nodiscard]] const TasData& GetTasState(std::size_t pad) const;
|
||||
|
||||
private:
|
||||
struct TASCommand {
|
||||
u32 buttons{};
|
||||
TasAnalog l_axis{};
|
||||
TasAnalog r_axis{};
|
||||
};
|
||||
|
||||
// Loads TAS files from all players
|
||||
void LoadTasFiles();
|
||||
|
||||
// Loads TAS file from the specified player
|
||||
void LoadTasFile(size_t player_index);
|
||||
|
||||
// Writes a TAS file from the recorded commands
|
||||
void WriteTasFile(std::u8string file_name);
|
||||
|
||||
/**
|
||||
* Parses a string containing the axis values with the following format "x;y"
|
||||
* X and Y have a range from -32767 to 32767
|
||||
* @return Returns a TAS analog object with axis values with range from -1.0 to 1.0
|
||||
*/
|
||||
TasAnalog ReadCommandAxis(const std::string& line) const;
|
||||
|
||||
/**
|
||||
* Parses a string containing the button values with the following format "a;b;c;d..."
|
||||
* Each button is represented by it's text format specified in text_to_tas_button array
|
||||
* @return Returns a u32 with each bit representing the status of a button
|
||||
*/
|
||||
u32 ReadCommandButtons(const std::string& line) const;
|
||||
|
||||
/**
|
||||
* Converts an u32 containing the button status into the text equivalent
|
||||
* @return Returns a string with the name of the buttons to be written to the file
|
||||
*/
|
||||
std::string WriteCommandButtons(u32 data) const;
|
||||
|
||||
/**
|
||||
* Converts an TAS analog object containing the axis status into the text equivalent
|
||||
* @return Returns a string with the value of the axis to be written to the file
|
||||
*/
|
||||
std::string WriteCommandAxis(TasAnalog data) const;
|
||||
|
||||
// Inverts the Y axis polarity
|
||||
std::pair<float, float> FlipAxisY(std::pair<float, float> old);
|
||||
|
||||
/**
|
||||
* Converts an u32 containing the button status into the text equivalent
|
||||
* @return Returns a string with the name of the buttons to be printed on console
|
||||
*/
|
||||
std::string DebugButtons(u32 buttons) const;
|
||||
|
||||
/**
|
||||
* Converts an TAS analog object containing the axis status into the text equivalent
|
||||
* @return Returns a string with the value of the axis to be printed on console
|
||||
*/
|
||||
std::string DebugJoystick(float x, float y) const;
|
||||
|
||||
/**
|
||||
* Converts the given TAS status into the text equivalent
|
||||
* @return Returns a string with the value of the TAS status to be printed on console
|
||||
*/
|
||||
std::string DebugInput(const TasData& data) const;
|
||||
|
||||
/**
|
||||
* Converts the given TAS status of multiple players into the text equivalent
|
||||
* @return Returns a string with the value of the status of all TAS players to be printed on
|
||||
* console
|
||||
*/
|
||||
std::string DebugInputs(const std::array<TasData, PLAYER_NUMBER>& arr) const;
|
||||
|
||||
/**
|
||||
* Converts an u32 containing the button status into the text equivalent
|
||||
* @return Returns a string with the name of the buttons
|
||||
*/
|
||||
std::string ButtonsToString(u32 button) const;
|
||||
|
||||
// Stores current controller configuration and sets a TAS controller for every active controller
|
||||
// to the current config
|
||||
void SwapToTasController();
|
||||
|
||||
// Sets the stored controller configuration to the current config
|
||||
void SwapToStoredController();
|
||||
|
||||
size_t script_length{0};
|
||||
std::array<TasData, PLAYER_NUMBER> tas_data;
|
||||
bool is_old_input_saved{false};
|
||||
bool is_recording{false};
|
||||
bool is_running{false};
|
||||
bool needs_reset{false};
|
||||
std::array<std::vector<TASCommand>, PLAYER_NUMBER> commands{};
|
||||
std::vector<TASCommand> record_commands{};
|
||||
size_t current_command{0};
|
||||
TASCommand last_input{}; // only used for recording
|
||||
|
||||
// Old settings for swapping controllers
|
||||
std::array<Settings::PlayerInput, 10> player_mappings;
|
||||
};
|
||||
} // namespace TasInput
|
||||
@@ -1,101 +0,0 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <mutex>
|
||||
#include <utility>
|
||||
|
||||
#include "common/settings.h"
|
||||
#include "common/threadsafe_queue.h"
|
||||
#include "input_common/tas/tas_input.h"
|
||||
#include "input_common/tas/tas_poller.h"
|
||||
|
||||
namespace InputCommon {
|
||||
|
||||
class TasButton final : public Input::ButtonDevice {
|
||||
public:
|
||||
explicit TasButton(u32 button_, u32 pad_, const TasInput::Tas* tas_input_)
|
||||
: button(button_), pad(pad_), tas_input(tas_input_) {}
|
||||
|
||||
bool GetStatus() const override {
|
||||
return (tas_input->GetTasState(pad).buttons & button) != 0;
|
||||
}
|
||||
|
||||
private:
|
||||
const u32 button;
|
||||
const u32 pad;
|
||||
const TasInput::Tas* tas_input;
|
||||
};
|
||||
|
||||
TasButtonFactory::TasButtonFactory(std::shared_ptr<TasInput::Tas> tas_input_)
|
||||
: tas_input(std::move(tas_input_)) {}
|
||||
|
||||
std::unique_ptr<Input::ButtonDevice> TasButtonFactory::Create(const Common::ParamPackage& params) {
|
||||
const auto button_id = params.Get("button", 0);
|
||||
const auto pad = params.Get("pad", 0);
|
||||
|
||||
return std::make_unique<TasButton>(button_id, pad, tas_input.get());
|
||||
}
|
||||
|
||||
class TasAnalog final : public Input::AnalogDevice {
|
||||
public:
|
||||
explicit TasAnalog(u32 pad_, u32 axis_x_, u32 axis_y_, const TasInput::Tas* tas_input_)
|
||||
: pad(pad_), axis_x(axis_x_), axis_y(axis_y_), tas_input(tas_input_) {}
|
||||
|
||||
float GetAxis(u32 axis) const {
|
||||
std::lock_guard lock{mutex};
|
||||
return tas_input->GetTasState(pad).axis.at(axis);
|
||||
}
|
||||
|
||||
std::pair<float, float> GetAnalog(u32 analog_axis_x, u32 analog_axis_y) const {
|
||||
float x = GetAxis(analog_axis_x);
|
||||
float y = GetAxis(analog_axis_y);
|
||||
|
||||
// Make sure the coordinates are in the unit circle,
|
||||
// otherwise normalize it.
|
||||
float r = x * x + y * y;
|
||||
if (r > 1.0f) {
|
||||
r = std::sqrt(r);
|
||||
x /= r;
|
||||
y /= r;
|
||||
}
|
||||
|
||||
return {x, y};
|
||||
}
|
||||
|
||||
std::tuple<float, float> GetStatus() const override {
|
||||
return GetAnalog(axis_x, axis_y);
|
||||
}
|
||||
|
||||
Input::AnalogProperties GetAnalogProperties() const override {
|
||||
return {0.0f, 1.0f, 0.5f};
|
||||
}
|
||||
|
||||
private:
|
||||
const u32 pad;
|
||||
const u32 axis_x;
|
||||
const u32 axis_y;
|
||||
const TasInput::Tas* tas_input;
|
||||
mutable std::mutex mutex;
|
||||
};
|
||||
|
||||
/// An analog device factory that creates analog devices from GC Adapter
|
||||
TasAnalogFactory::TasAnalogFactory(std::shared_ptr<TasInput::Tas> tas_input_)
|
||||
: tas_input(std::move(tas_input_)) {}
|
||||
|
||||
/**
|
||||
* Creates analog device from joystick axes
|
||||
* @param params contains parameters for creating the device:
|
||||
* - "port": the nth gcpad on the adapter
|
||||
* - "axis_x": the index of the axis to be bind as x-axis
|
||||
* - "axis_y": the index of the axis to be bind as y-axis
|
||||
*/
|
||||
std::unique_ptr<Input::AnalogDevice> TasAnalogFactory::Create(const Common::ParamPackage& params) {
|
||||
const auto pad = static_cast<u32>(params.Get("pad", 0));
|
||||
const auto axis_x = static_cast<u32>(params.Get("axis_x", 0));
|
||||
const auto axis_y = static_cast<u32>(params.Get("axis_y", 1));
|
||||
|
||||
return std::make_unique<TasAnalog>(pad, axis_x, axis_y, tas_input.get());
|
||||
}
|
||||
|
||||
} // namespace InputCommon
|
||||
@@ -1,43 +0,0 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include "core/frontend/input.h"
|
||||
#include "input_common/tas/tas_input.h"
|
||||
|
||||
namespace InputCommon {
|
||||
|
||||
/**
|
||||
* A button device factory representing a tas bot. It receives tas events and forward them
|
||||
* to all button devices it created.
|
||||
*/
|
||||
class TasButtonFactory final : public Input::Factory<Input::ButtonDevice> {
|
||||
public:
|
||||
explicit TasButtonFactory(std::shared_ptr<TasInput::Tas> tas_input_);
|
||||
|
||||
/**
|
||||
* Creates a button device from a button press
|
||||
* @param params contains parameters for creating the device:
|
||||
* - "code": the code of the key to bind with the button
|
||||
*/
|
||||
std::unique_ptr<Input::ButtonDevice> Create(const Common::ParamPackage& params) override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<TasInput::Tas> tas_input;
|
||||
};
|
||||
|
||||
/// An analog device factory that creates analog devices from tas
|
||||
class TasAnalogFactory final : public Input::Factory<Input::AnalogDevice> {
|
||||
public:
|
||||
explicit TasAnalogFactory(std::shared_ptr<TasInput::Tas> tas_input_);
|
||||
|
||||
std::unique_ptr<Input::AnalogDevice> Create(const Common::ParamPackage& params) override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<TasInput::Tas> tas_input;
|
||||
};
|
||||
|
||||
} // namespace InputCommon
|
||||
@@ -21,6 +21,8 @@
|
||||
|
||||
namespace InputCommon::CemuhookUDP {
|
||||
|
||||
constexpr char DEFAULT_SRV[] = "127.0.0.1:26760";
|
||||
|
||||
class Socket;
|
||||
|
||||
namespace Response {
|
||||
|
||||
@@ -477,13 +477,7 @@ void EmitSetSampleMask(EmitContext& ctx, Id value) {
|
||||
}
|
||||
|
||||
void EmitSetFragDepth(EmitContext& ctx, Id value) {
|
||||
if (!ctx.runtime_info.convert_depth_mode) {
|
||||
ctx.OpStore(ctx.frag_depth, value);
|
||||
return;
|
||||
}
|
||||
const Id unit{ctx.Const(0.5f)};
|
||||
const Id new_depth{ctx.OpFma(ctx.F32[1], value, unit, unit)};
|
||||
ctx.OpStore(ctx.frag_depth, new_depth);
|
||||
ctx.OpStore(ctx.frag_depth, value);
|
||||
}
|
||||
|
||||
void EmitGetZFlag(EmitContext&) {
|
||||
|
||||
@@ -11,16 +11,14 @@
|
||||
namespace Shader {
|
||||
|
||||
template <typename T>
|
||||
requires std::is_destructible_v<T>
|
||||
class ObjectPool {
|
||||
requires std::is_destructible_v<T> class ObjectPool {
|
||||
public:
|
||||
explicit ObjectPool(size_t chunk_size = 8192) : new_chunk_size{chunk_size} {
|
||||
node = &chunks.emplace_back(new_chunk_size);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
requires std::is_constructible_v<T, Args...>
|
||||
[[nodiscard]] T* Create(Args&&... args) {
|
||||
requires std::is_constructible_v<T, Args...>[[nodiscard]] T* Create(Args&&... args) {
|
||||
return std::construct_at(Memory(), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
|
||||
@@ -531,6 +531,14 @@ void GPU::TriggerCpuInterrupt(const u32 syncpoint_id, const u32 value) const {
|
||||
interrupt_manager.GPUInterruptSyncpt(syncpoint_id, value);
|
||||
}
|
||||
|
||||
void GPU::ShutDown() {
|
||||
// Signal that threads should no longer block on syncpoint fences
|
||||
shutting_down.store(true, std::memory_order_relaxed);
|
||||
sync_cv.notify_all();
|
||||
|
||||
gpu_thread.ShutDown();
|
||||
}
|
||||
|
||||
void GPU::OnCommandListEnd() {
|
||||
if (is_async) {
|
||||
// This command only applies to asynchronous GPU mode
|
||||
|
||||
@@ -219,6 +219,9 @@ public:
|
||||
return *shader_notify;
|
||||
}
|
||||
|
||||
// Stops the GPU execution and waits for the GPU to finish working
|
||||
void ShutDown();
|
||||
|
||||
/// Allows the CPU/NvFlinger to wait on the GPU before presenting a frame.
|
||||
void WaitFence(u32 syncpoint_id, u32 value);
|
||||
|
||||
|
||||
@@ -17,9 +17,9 @@
|
||||
namespace VideoCommon::GPUThread {
|
||||
|
||||
/// Runs the GPU thread
|
||||
static void RunThread(std::stop_token stop_token, Core::System& system,
|
||||
VideoCore::RendererBase& renderer, Core::Frontend::GraphicsContext& context,
|
||||
Tegra::DmaPusher& dma_pusher, SynchState& state) {
|
||||
static void RunThread(Core::System& system, VideoCore::RendererBase& renderer,
|
||||
Core::Frontend::GraphicsContext& context, Tegra::DmaPusher& dma_pusher,
|
||||
SynchState& state) {
|
||||
std::string name = "yuzu:GPU";
|
||||
MicroProfileOnThreadCreate(name.c_str());
|
||||
SCOPE_EXIT({ MicroProfileOnThreadExit(); });
|
||||
@@ -28,14 +28,20 @@ static void RunThread(std::stop_token stop_token, Core::System& system,
|
||||
Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
|
||||
system.RegisterHostThread();
|
||||
|
||||
// Wait for first GPU command before acquiring the window context
|
||||
state.queue.Wait();
|
||||
|
||||
// If emulation was stopped during disk shader loading, abort before trying to acquire context
|
||||
if (!state.is_running) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto current_context = context.Acquire();
|
||||
VideoCore::RasterizerInterface* const rasterizer = renderer.ReadRasterizer();
|
||||
|
||||
while (!stop_token.stop_requested()) {
|
||||
CommandDataContainer next = state.queue.PopWait(stop_token);
|
||||
if (stop_token.stop_requested()) {
|
||||
break;
|
||||
}
|
||||
CommandDataContainer next;
|
||||
while (state.is_running) {
|
||||
next = state.queue.PopWait();
|
||||
if (auto* submit_list = std::get_if<SubmitListCommand>(&next.data)) {
|
||||
dma_pusher.Push(std::move(submit_list->entries));
|
||||
dma_pusher.DispatchCalls();
|
||||
@@ -49,6 +55,8 @@ static void RunThread(std::stop_token stop_token, Core::System& system,
|
||||
rasterizer->FlushRegion(flush->addr, flush->size);
|
||||
} else if (const auto* invalidate = std::get_if<InvalidateRegionCommand>(&next.data)) {
|
||||
rasterizer->OnCPUWrite(invalidate->addr, invalidate->size);
|
||||
} else if (std::holds_alternative<EndProcessingCommand>(next.data)) {
|
||||
ASSERT(state.is_running == false);
|
||||
} else {
|
||||
UNREACHABLE();
|
||||
}
|
||||
@@ -65,14 +73,16 @@ static void RunThread(std::stop_token stop_token, Core::System& system,
|
||||
ThreadManager::ThreadManager(Core::System& system_, bool is_async_)
|
||||
: system{system_}, is_async{is_async_} {}
|
||||
|
||||
ThreadManager::~ThreadManager() = default;
|
||||
ThreadManager::~ThreadManager() {
|
||||
ShutDown();
|
||||
}
|
||||
|
||||
void ThreadManager::StartThread(VideoCore::RendererBase& renderer,
|
||||
Core::Frontend::GraphicsContext& context,
|
||||
Tegra::DmaPusher& dma_pusher) {
|
||||
rasterizer = renderer.ReadRasterizer();
|
||||
thread = std::jthread(RunThread, std::ref(system), std::ref(renderer), std::ref(context),
|
||||
std::ref(dma_pusher), std::ref(state));
|
||||
thread = std::thread(RunThread, std::ref(system), std::ref(renderer), std::ref(context),
|
||||
std::ref(dma_pusher), std::ref(state));
|
||||
}
|
||||
|
||||
void ThreadManager::SubmitList(Tegra::CommandList&& entries) {
|
||||
@@ -107,6 +117,26 @@ void ThreadManager::FlushAndInvalidateRegion(VAddr addr, u64 size) {
|
||||
rasterizer->OnCPUWrite(addr, size);
|
||||
}
|
||||
|
||||
void ThreadManager::ShutDown() {
|
||||
if (!state.is_running) {
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard lk(state.write_lock);
|
||||
state.is_running = false;
|
||||
state.cv.notify_all();
|
||||
}
|
||||
|
||||
if (!thread.joinable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Notify GPU thread that a shutdown is pending
|
||||
PushCommand(EndProcessingCommand());
|
||||
thread.join();
|
||||
}
|
||||
|
||||
void ThreadManager::OnCommandListEnd() {
|
||||
PushCommand(OnCommandListEndCommand());
|
||||
}
|
||||
@@ -122,8 +152,9 @@ u64 ThreadManager::PushCommand(CommandData&& command_data, bool block) {
|
||||
state.queue.Push(CommandDataContainer(std::move(command_data), fence, block));
|
||||
|
||||
if (block) {
|
||||
state.cv.wait(lk, thread.get_stop_token(), [this, fence] {
|
||||
return fence <= state.signaled_fence.load(std::memory_order_relaxed);
|
||||
state.cv.wait(lk, [this, fence] {
|
||||
return fence <= state.signaled_fence.load(std::memory_order_relaxed) ||
|
||||
!state.is_running;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,9 @@ class RendererBase;
|
||||
|
||||
namespace VideoCommon::GPUThread {
|
||||
|
||||
/// Command to signal to the GPU thread that processing has ended
|
||||
struct EndProcessingCommand final {};
|
||||
|
||||
/// Command to signal to the GPU thread that a command list is ready for processing
|
||||
struct SubmitListCommand final {
|
||||
explicit SubmitListCommand(Tegra::CommandList&& entries_) : entries{std::move(entries_)} {}
|
||||
@@ -80,7 +83,7 @@ struct OnCommandListEndCommand final {};
|
||||
struct GPUTickCommand final {};
|
||||
|
||||
using CommandData =
|
||||
std::variant<std::monostate, SubmitListCommand, SwapBuffersCommand, FlushRegionCommand,
|
||||
std::variant<EndProcessingCommand, SubmitListCommand, SwapBuffersCommand, FlushRegionCommand,
|
||||
InvalidateRegionCommand, FlushAndInvalidateRegionCommand, OnCommandListEndCommand,
|
||||
GPUTickCommand>;
|
||||
|
||||
@@ -97,12 +100,14 @@ struct CommandDataContainer {
|
||||
|
||||
/// Struct used to synchronize the GPU thread
|
||||
struct SynchState final {
|
||||
using CommandQueue = Common::SPSCQueue<CommandDataContainer, true>;
|
||||
std::atomic_bool is_running{true};
|
||||
|
||||
using CommandQueue = Common::SPSCQueue<CommandDataContainer>;
|
||||
std::mutex write_lock;
|
||||
CommandQueue queue;
|
||||
u64 last_fence{};
|
||||
std::atomic<u64> signaled_fence{};
|
||||
std::condition_variable_any cv;
|
||||
std::condition_variable cv;
|
||||
};
|
||||
|
||||
/// Class used to manage the GPU thread
|
||||
@@ -144,7 +149,7 @@ private:
|
||||
VideoCore::RasterizerInterface* rasterizer = nullptr;
|
||||
|
||||
SynchState state;
|
||||
std::jthread thread;
|
||||
std::thread thread;
|
||||
};
|
||||
|
||||
} // namespace VideoCommon::GPUThread
|
||||
|
||||
@@ -566,10 +566,8 @@ void RasterizerOpenGL::SyncViewport() {
|
||||
if (regs.screen_y_control.y_negate != 0) {
|
||||
flip_y = !flip_y;
|
||||
}
|
||||
const bool is_zero_to_one = regs.depth_mode == Maxwell::DepthMode::ZeroToOne;
|
||||
const GLenum origin = flip_y ? GL_UPPER_LEFT : GL_LOWER_LEFT;
|
||||
const GLenum depth = is_zero_to_one ? GL_ZERO_TO_ONE : GL_NEGATIVE_ONE_TO_ONE;
|
||||
state_tracker.ClipControl(origin, depth);
|
||||
state_tracker.SetOrigin(origin);
|
||||
state_tracker.SetYNegate(regs.screen_y_control.y_negate != 0);
|
||||
}
|
||||
|
||||
@@ -593,6 +591,15 @@ void RasterizerOpenGL::SyncViewport() {
|
||||
const GLdouble reduce_z = regs.depth_mode == Maxwell::DepthMode::MinusOneToOne;
|
||||
const GLdouble near_depth = src.translate_z - src.scale_z * reduce_z;
|
||||
const GLdouble far_depth = src.translate_z + src.scale_z;
|
||||
if (i == 0) {
|
||||
const auto& viewports = regs.viewports[i];
|
||||
const GLenum depth = viewports.depth_range_near != src.translate_z &&
|
||||
viewports.depth_range_far != src.translate_z
|
||||
? GL_NEGATIVE_ONE_TO_ONE
|
||||
: GL_ZERO_TO_ONE;
|
||||
state_tracker.SetDepthMode(depth);
|
||||
}
|
||||
|
||||
if (device.HasDepthBufferFloat()) {
|
||||
glDepthRangeIndexeddNV(static_cast<GLuint>(i), near_depth, far_depth);
|
||||
} else {
|
||||
|
||||
@@ -293,8 +293,6 @@ void ShaderCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading,
|
||||
}};
|
||||
LoadPipelines(stop_loading, shader_cache_filename, CACHE_VERSION, load_compute, load_graphics);
|
||||
|
||||
LOG_INFO(Render_OpenGL, "Total Pipeline Count: {}", state.total);
|
||||
|
||||
std::unique_lock lock{state.mutex};
|
||||
callback(VideoCore::LoadCallbackStage::Build, 0, state.total);
|
||||
state.has_loaded = true;
|
||||
|
||||
@@ -112,6 +112,22 @@ public:
|
||||
glClipControl(origin, depth);
|
||||
}
|
||||
|
||||
void SetOrigin(GLenum new_origin) {
|
||||
if (new_origin == origin) {
|
||||
return;
|
||||
}
|
||||
origin = new_origin;
|
||||
glClipControl(origin, depth);
|
||||
}
|
||||
|
||||
void SetDepthMode(GLenum new_depth) {
|
||||
if (new_depth == depth) {
|
||||
return;
|
||||
}
|
||||
depth = new_depth;
|
||||
glClipControl(origin, depth);
|
||||
}
|
||||
|
||||
void SetYNegate(bool new_y_negate) {
|
||||
if (new_y_negate == y_negate) {
|
||||
return;
|
||||
|
||||
@@ -97,14 +97,19 @@ RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_,
|
||||
Core::Frontend::EmuWindow& emu_window,
|
||||
Core::Memory::Memory& cpu_memory_, Tegra::GPU& gpu_,
|
||||
std::unique_ptr<Core::Frontend::GraphicsContext> context_) try
|
||||
: RendererBase(emu_window, std::move(context_)), telemetry_session(telemetry_session_),
|
||||
cpu_memory(cpu_memory_), gpu(gpu_), library(OpenLibrary()),
|
||||
: RendererBase(emu_window, std::move(context_)),
|
||||
telemetry_session(telemetry_session_),
|
||||
cpu_memory(cpu_memory_),
|
||||
gpu(gpu_),
|
||||
library(OpenLibrary()),
|
||||
instance(CreateInstance(library, dld, VK_API_VERSION_1_1, render_window.GetWindowInfo().type,
|
||||
true, Settings::values.renderer_debug.GetValue())),
|
||||
debug_callback(Settings::values.renderer_debug ? CreateDebugCallback(instance) : nullptr),
|
||||
surface(CreateSurface(instance, render_window)),
|
||||
device(CreateDevice(instance, dld, *surface)), memory_allocator(device, false),
|
||||
state_tracker(gpu), scheduler(device, state_tracker),
|
||||
device(CreateDevice(instance, dld, *surface)),
|
||||
memory_allocator(device, false),
|
||||
state_tracker(gpu),
|
||||
scheduler(device, state_tracker),
|
||||
swapchain(*surface, device, scheduler, render_window.GetFramebufferLayout().width,
|
||||
render_window.GetFramebufferLayout().height, false),
|
||||
blit_screen(cpu_memory, render_window, device, memory_allocator, swapchain, scheduler,
|
||||
@@ -144,7 +149,7 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
|
||||
const Layout::FramebufferLayout layout = render_window.GetFramebufferLayout();
|
||||
swapchain.Create(layout.width, layout.height, is_srgb);
|
||||
};
|
||||
if (swapchain.NeedsRecreation(is_srgb)) {
|
||||
if (swapchain.IsSubOptimal() || swapchain.HasColorSpaceChanged(is_srgb)) {
|
||||
recreate_swapchain();
|
||||
}
|
||||
bool is_outdated;
|
||||
|
||||
@@ -507,9 +507,8 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
|
||||
vertex_attributes.push_back({
|
||||
.location = static_cast<u32>(index),
|
||||
.binding = 0,
|
||||
.format = type == 1 ? VK_FORMAT_R32_SFLOAT
|
||||
: type == 2 ? VK_FORMAT_R32_SINT
|
||||
: VK_FORMAT_R32_UINT,
|
||||
.format = type == 1 ? VK_FORMAT_R32_SFLOAT
|
||||
: type == 2 ? VK_FORMAT_R32_SINT : VK_FORMAT_R32_UINT,
|
||||
.offset = 0,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -447,8 +447,6 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
|
||||
VideoCommon::LoadPipelines(stop_loading, pipeline_cache_filename, CACHE_VERSION, load_compute,
|
||||
load_graphics);
|
||||
|
||||
LOG_INFO(Render_Vulkan, "Total Pipeline Count: {}", state.total);
|
||||
|
||||
std::unique_lock lock{state.mutex};
|
||||
callback(VideoCore::LoadCallbackStage::Build, 0, state.total);
|
||||
state.has_loaded = true;
|
||||
|
||||
@@ -43,10 +43,17 @@ VKScheduler::VKScheduler(const Device& device_, StateTracker& state_tracker_)
|
||||
command_pool{std::make_unique<CommandPool>(*master_semaphore, device)} {
|
||||
AcquireNewChunk();
|
||||
AllocateWorkerCommandBuffer();
|
||||
worker_thread = std::jthread([this](std::stop_token token) { WorkerThread(token); });
|
||||
worker_thread = std::thread(&VKScheduler::WorkerThread, this);
|
||||
}
|
||||
|
||||
VKScheduler::~VKScheduler() = default;
|
||||
VKScheduler::~VKScheduler() {
|
||||
{
|
||||
std::lock_guard lock{work_mutex};
|
||||
quit = true;
|
||||
}
|
||||
work_cv.notify_all();
|
||||
worker_thread.join();
|
||||
}
|
||||
|
||||
void VKScheduler::Flush(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) {
|
||||
SubmitExecution(signal_semaphore, wait_semaphore);
|
||||
@@ -128,7 +135,7 @@ bool VKScheduler::UpdateGraphicsPipeline(GraphicsPipeline* pipeline) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void VKScheduler::WorkerThread(std::stop_token stop_token) {
|
||||
void VKScheduler::WorkerThread() {
|
||||
Common::SetCurrentThreadName("yuzu:VulkanWorker");
|
||||
do {
|
||||
if (work_queue.empty()) {
|
||||
@@ -137,8 +144,8 @@ void VKScheduler::WorkerThread(std::stop_token stop_token) {
|
||||
std::unique_ptr<CommandChunk> work;
|
||||
{
|
||||
std::unique_lock lock{work_mutex};
|
||||
work_cv.wait(lock, stop_token, [this] { return !work_queue.empty(); });
|
||||
if (stop_token.stop_requested()) {
|
||||
work_cv.wait(lock, [this] { return !work_queue.empty() || quit; });
|
||||
if (quit) {
|
||||
continue;
|
||||
}
|
||||
work = std::move(work_queue.front());
|
||||
@@ -151,7 +158,7 @@ void VKScheduler::WorkerThread(std::stop_token stop_token) {
|
||||
}
|
||||
std::lock_guard reserve_lock{reserve_mutex};
|
||||
chunk_reserve.push_back(std::move(work));
|
||||
} while (!stop_token.stop_requested());
|
||||
} while (!quit);
|
||||
}
|
||||
|
||||
void VKScheduler::AllocateWorkerCommandBuffer() {
|
||||
|
||||
@@ -187,7 +187,7 @@ private:
|
||||
GraphicsPipeline* graphics_pipeline = nullptr;
|
||||
};
|
||||
|
||||
void WorkerThread(std::stop_token stop_token);
|
||||
void WorkerThread();
|
||||
|
||||
void AllocateWorkerCommandBuffer();
|
||||
|
||||
@@ -212,6 +212,7 @@ private:
|
||||
vk::CommandBuffer current_cmdbuf;
|
||||
|
||||
std::unique_ptr<CommandChunk> chunk;
|
||||
std::thread worker_thread;
|
||||
|
||||
State state;
|
||||
|
||||
@@ -223,9 +224,9 @@ private:
|
||||
std::vector<std::unique_ptr<CommandChunk>> chunk_reserve;
|
||||
std::mutex reserve_mutex;
|
||||
std::mutex work_mutex;
|
||||
std::condition_variable_any work_cv;
|
||||
std::condition_variable work_cv;
|
||||
std::condition_variable wait_cv;
|
||||
std::jthread worker_thread;
|
||||
std::atomic_bool quit{};
|
||||
};
|
||||
|
||||
} // namespace Vulkan
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/core.h"
|
||||
#include "core/frontend/framebuffer_layout.h"
|
||||
#include "video_core/renderer_vulkan/vk_scheduler.h"
|
||||
@@ -37,19 +36,8 @@ VkSurfaceFormatKHR ChooseSwapSurfaceFormat(vk::Span<VkSurfaceFormatKHR> formats)
|
||||
|
||||
VkPresentModeKHR ChooseSwapPresentMode(vk::Span<VkPresentModeKHR> modes) {
|
||||
// Mailbox doesn't lock the application like fifo (vsync), prefer it
|
||||
const auto found_mailbox = std::find(modes.begin(), modes.end(), VK_PRESENT_MODE_MAILBOX_KHR);
|
||||
if (found_mailbox != modes.end()) {
|
||||
return VK_PRESENT_MODE_MAILBOX_KHR;
|
||||
}
|
||||
if (Settings::values.disable_fps_limit.GetValue()) {
|
||||
// FIFO present mode locks the framerate to the monitor's refresh rate,
|
||||
// Find an alternative to surpass this limitation if FPS is unlocked.
|
||||
const auto found_imm = std::find(modes.begin(), modes.end(), VK_PRESENT_MODE_IMMEDIATE_KHR);
|
||||
if (found_imm != modes.end()) {
|
||||
return VK_PRESENT_MODE_IMMEDIATE_KHR;
|
||||
}
|
||||
}
|
||||
return VK_PRESENT_MODE_FIFO_KHR;
|
||||
const auto found = std::find(modes.begin(), modes.end(), VK_PRESENT_MODE_MAILBOX_KHR);
|
||||
return found != modes.end() ? *found : VK_PRESENT_MODE_FIFO_KHR;
|
||||
}
|
||||
|
||||
VkExtent2D ChooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities, u32 width, u32 height) {
|
||||
@@ -155,7 +143,7 @@ void VKSwapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities,
|
||||
const auto present_modes{physical_device.GetSurfacePresentModesKHR(surface)};
|
||||
|
||||
const VkSurfaceFormatKHR surface_format{ChooseSwapSurfaceFormat(formats)};
|
||||
present_mode = ChooseSwapPresentMode(present_modes);
|
||||
const VkPresentModeKHR present_mode{ChooseSwapPresentMode(present_modes)};
|
||||
|
||||
u32 requested_image_count{capabilities.minImageCount + 1};
|
||||
if (capabilities.maxImageCount > 0 && requested_image_count > capabilities.maxImageCount) {
|
||||
@@ -208,7 +196,6 @@ void VKSwapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities,
|
||||
|
||||
extent = swapchain_ci.imageExtent;
|
||||
current_srgb = srgb;
|
||||
current_fps_unlocked = Settings::values.disable_fps_limit.GetValue();
|
||||
|
||||
images = swapchain.GetImages();
|
||||
image_count = static_cast<u32>(images.size());
|
||||
@@ -261,14 +248,4 @@ void VKSwapchain::Destroy() {
|
||||
swapchain.reset();
|
||||
}
|
||||
|
||||
bool VKSwapchain::HasFpsUnlockChanged() const {
|
||||
return current_fps_unlocked != Settings::values.disable_fps_limit.GetValue();
|
||||
}
|
||||
|
||||
bool VKSwapchain::NeedsPresentModeUpdate() const {
|
||||
// Mailbox present mode is the ideal for all scenarios. If it is not available,
|
||||
// A different present mode is needed to support unlocked FPS above the monitor's refresh rate.
|
||||
return present_mode != VK_PRESENT_MODE_MAILBOX_KHR && HasFpsUnlockChanged();
|
||||
}
|
||||
|
||||
} // namespace Vulkan
|
||||
|
||||
@@ -33,11 +33,6 @@ public:
|
||||
/// Presents the rendered image to the swapchain.
|
||||
void Present(VkSemaphore render_semaphore);
|
||||
|
||||
/// Returns true when the swapchain needs to be recreated.
|
||||
bool NeedsRecreation(bool is_srgb) const {
|
||||
return HasColorSpaceChanged(is_srgb) || IsSubOptimal() || NeedsPresentModeUpdate();
|
||||
}
|
||||
|
||||
/// Returns true when the color space has changed.
|
||||
bool HasColorSpaceChanged(bool is_srgb) const {
|
||||
return current_srgb != is_srgb;
|
||||
@@ -89,10 +84,6 @@ private:
|
||||
|
||||
void Destroy();
|
||||
|
||||
bool HasFpsUnlockChanged() const;
|
||||
|
||||
bool NeedsPresentModeUpdate() const;
|
||||
|
||||
const VkSurfaceKHR surface;
|
||||
const Device& device;
|
||||
VKScheduler& scheduler;
|
||||
@@ -111,10 +102,8 @@ private:
|
||||
|
||||
VkFormat image_view_format{};
|
||||
VkExtent2D extent{};
|
||||
VkPresentModeKHR present_mode{};
|
||||
|
||||
bool current_srgb{};
|
||||
bool current_fps_unlocked{};
|
||||
bool is_outdated{};
|
||||
bool is_suboptimal{};
|
||||
};
|
||||
|
||||
@@ -127,7 +127,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
|
||||
const auto format_info = MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, false, format);
|
||||
VkImageCreateFlags flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
|
||||
if (info.type == ImageType::e2D && info.resources.layers >= 6 &&
|
||||
info.size.width == info.size.height && !device.HasBrokenCubeImageCompability()) {
|
||||
info.size.width == info.size.height) {
|
||||
flags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
|
||||
}
|
||||
if (info.type == ImageType::e3D) {
|
||||
|
||||
@@ -31,8 +31,8 @@ struct SlotId {
|
||||
};
|
||||
|
||||
template <class T>
|
||||
requires std::is_nothrow_move_assignable_v<T> && std::is_nothrow_move_constructible_v<T>
|
||||
class SlotVector {
|
||||
requires std::is_nothrow_move_assignable_v<T>&&
|
||||
std::is_nothrow_move_constructible_v<T> class SlotVector {
|
||||
public:
|
||||
class Iterator {
|
||||
friend SlotVector<T>;
|
||||
|
||||
@@ -16,7 +16,6 @@ VkBool32 Callback(VkDebugUtilsMessageSeverityFlagBitsEXT severity,
|
||||
switch (static_cast<u32>(data->messageIdNumber)) {
|
||||
case 0x682a878au: // VUID-vkCmdBindVertexBuffers2EXT-pBuffers-parameter
|
||||
case 0x99fb7dfdu: // UNASSIGNED-RequiredParameter (vkCmdBindVertexBuffers2EXT pBuffers[0])
|
||||
case 0xe8616bf2u: // Bound VkDescriptorSet 0x0[] was destroyed. Likely push_descriptor related
|
||||
return VK_FALSE;
|
||||
default:
|
||||
break;
|
||||
|
||||
@@ -243,6 +243,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
||||
SetupFamilies(surface);
|
||||
SetupFeatures();
|
||||
SetupProperties();
|
||||
CollectTelemetryParameters();
|
||||
|
||||
const auto queue_cis = GetDeviceQueueCreateInfos();
|
||||
const std::vector extensions = LoadExtensions(surface != nullptr);
|
||||
@@ -368,9 +369,20 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
||||
};
|
||||
SetNext(next, demote);
|
||||
|
||||
VkPhysicalDeviceFloat16Int8FeaturesKHR float16_int8;
|
||||
if (driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE) {
|
||||
const u32 version = properties.driverVersion;
|
||||
// Broken in this driver
|
||||
if (version > VK_MAKE_API_VERSION(0, 2, 0, 193)) {
|
||||
LOG_WARNING(Render_Vulkan, "AMD proprietary driver versions newer than 21.9.1 "
|
||||
"(windows) / 0.2.0.194 (amdvlk) have "
|
||||
"broken VkPhysicalDeviceFloat16Int8FeaturesKHR");
|
||||
is_int8_supported = false;
|
||||
is_float16_supported = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_int8_supported || is_float16_supported) {
|
||||
float16_int8 = {
|
||||
VkPhysicalDeviceFloat16Int8FeaturesKHR float16_int8{
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT16_INT8_FEATURES_KHR,
|
||||
.pNext = nullptr,
|
||||
.shaderFloat16 = is_float16_supported,
|
||||
@@ -561,7 +573,6 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
||||
logical = vk::Device::Create(physical, queue_cis, extensions, first_next, dld);
|
||||
|
||||
CollectPhysicalMemoryInfo();
|
||||
CollectTelemetryParameters();
|
||||
CollectToolingInfo();
|
||||
|
||||
if (driver_id == VK_DRIVER_ID_NVIDIA_PROPRIETARY_KHR) {
|
||||
@@ -588,27 +599,22 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
||||
ext_extended_dynamic_state = false;
|
||||
}
|
||||
}
|
||||
sets_per_pool = 64;
|
||||
|
||||
const bool is_amd =
|
||||
driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE;
|
||||
if (is_amd) {
|
||||
sets_per_pool = 64;
|
||||
if (driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE) {
|
||||
// AMD drivers need a higher amount of Sets per Pool in certain circunstances like in XC2.
|
||||
sets_per_pool = 96;
|
||||
// Disable VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT on AMD GCN4 and lower as it is broken.
|
||||
}
|
||||
|
||||
const bool is_amd = driver_id == VK_DRIVER_ID_AMD_PROPRIETARY ||
|
||||
driver_id == VK_DRIVER_ID_MESA_RADV ||
|
||||
driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE;
|
||||
if (ext_sampler_filter_minmax && is_amd) {
|
||||
// Disable ext_sampler_filter_minmax on AMD GCN4 and lower as it is broken.
|
||||
if (!is_float16_supported) {
|
||||
LOG_WARNING(
|
||||
Render_Vulkan,
|
||||
"AMD GCN4 and earlier do not properly support VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT");
|
||||
has_broken_cube_compatibility = true;
|
||||
}
|
||||
}
|
||||
const bool is_amd_or_radv = is_amd || driver_id == VK_DRIVER_ID_MESA_RADV;
|
||||
if (ext_sampler_filter_minmax && is_amd_or_radv) {
|
||||
// Disable ext_sampler_filter_minmax on AMD GCN4 and lower as it is broken.
|
||||
if (!is_float16_supported) {
|
||||
LOG_WARNING(Render_Vulkan,
|
||||
"Blacklisting AMD GCN4 and earlier for VK_EXT_sampler_filter_minmax");
|
||||
"Blacklisting AMD GCN4 and lower for VK_EXT_SAMPLER_FILTER_MINMAX_EXTENSION_NAME");
|
||||
ext_sampler_filter_minmax = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -309,11 +309,6 @@ public:
|
||||
return has_renderdoc || has_nsight_graphics;
|
||||
}
|
||||
|
||||
/// Returns true when the device does not properly support cube compatibility.
|
||||
bool HasBrokenCubeImageCompability() const {
|
||||
return has_broken_cube_compatibility;
|
||||
}
|
||||
|
||||
/// Returns the vendor name reported from Vulkan.
|
||||
std::string_view GetVendorName() const {
|
||||
return vendor_name;
|
||||
@@ -422,7 +417,6 @@ private:
|
||||
bool ext_conservative_rasterization{}; ///< Support for VK_EXT_conservative_rasterization.
|
||||
bool ext_provoking_vertex{}; ///< Support for VK_EXT_provoking_vertex.
|
||||
bool nv_device_diagnostics_config{}; ///< Support for VK_NV_device_diagnostics_config.
|
||||
bool has_broken_cube_compatibility{}; ///< Has broken cube compatiblity bit
|
||||
bool has_renderdoc{}; ///< Has RenderDoc attached
|
||||
bool has_nsight_graphics{}; ///< Has Nsight Graphics attached
|
||||
|
||||
|
||||
@@ -108,9 +108,6 @@ add_executable(yuzu
|
||||
configuration/configure_system.cpp
|
||||
configuration/configure_system.h
|
||||
configuration/configure_system.ui
|
||||
configuration/configure_tas.cpp
|
||||
configuration/configure_tas.h
|
||||
configuration/configure_tas.ui
|
||||
configuration/configure_touch_from_button.cpp
|
||||
configuration/configure_touch_from_button.h
|
||||
configuration/configure_touch_from_button.ui
|
||||
@@ -290,6 +287,10 @@ if (YUZU_USE_QT_WEB_ENGINE)
|
||||
target_compile_definitions(yuzu PRIVATE -DYUZU_USE_QT_WEB_ENGINE)
|
||||
endif ()
|
||||
|
||||
if (YUZU_ENABLE_BOXCAT)
|
||||
target_compile_definitions(yuzu PRIVATE -DYUZU_ENABLE_BOXCAT)
|
||||
endif ()
|
||||
|
||||
if(UNIX AND NOT APPLE)
|
||||
install(TARGETS yuzu RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin")
|
||||
endif()
|
||||
|
||||
@@ -36,7 +36,6 @@
|
||||
#include "input_common/keyboard.h"
|
||||
#include "input_common/main.h"
|
||||
#include "input_common/mouse/mouse_input.h"
|
||||
#include "input_common/tas/tas_input.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
#include "video_core/video_core.h"
|
||||
#include "yuzu/bootmanager.h"
|
||||
@@ -313,7 +312,6 @@ GRenderWindow::~GRenderWindow() {
|
||||
}
|
||||
|
||||
void GRenderWindow::OnFrameDisplayed() {
|
||||
input_subsystem->GetTas()->UpdateThread();
|
||||
if (!first_frame) {
|
||||
first_frame = true;
|
||||
emit FirstFrameDisplayed();
|
||||
|
||||
@@ -221,7 +221,7 @@ const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> Config::default
|
||||
// This must be in alphabetical order according to action name as it must have the same order as
|
||||
// UISetting::values.shortcuts, which is alphabetically ordered.
|
||||
// clang-format off
|
||||
const std::array<UISettings::Shortcut, 21> Config::default_hotkeys{{
|
||||
const std::array<UISettings::Shortcut, 18> Config::default_hotkeys{{
|
||||
{QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), Qt::WidgetWithChildrenShortcut}},
|
||||
{QStringLiteral("Change Docked Mode"), QStringLiteral("Main Window"), {QStringLiteral("F10"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), Qt::WindowShortcut}},
|
||||
@@ -235,9 +235,6 @@ const std::array<UISettings::Shortcut, 21> Config::default_hotkeys{{
|
||||
{QStringLiteral("Mute Audio"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+M"), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), Qt::WindowShortcut}},
|
||||
{QStringLiteral("TAS Start/Stop"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F5"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("TAS Reset"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F6"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("TAS Record"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F7"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Toggle Framerate Limit"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+U"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Toggle Mouse Panning"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F9"), Qt::ApplicationShortcut}},
|
||||
@@ -545,6 +542,7 @@ void Config::ReadAudioValues() {
|
||||
ReadBasicSetting(Settings::values.audio_device_id);
|
||||
ReadBasicSetting(Settings::values.sink_id);
|
||||
}
|
||||
ReadGlobalSetting(Settings::values.enable_audio_stretching);
|
||||
ReadGlobalSetting(Settings::values.volume);
|
||||
|
||||
qt_config->endGroup();
|
||||
@@ -562,20 +560,10 @@ void Config::ReadControlValues() {
|
||||
ReadTouchscreenValues();
|
||||
ReadMotionTouchValues();
|
||||
|
||||
#ifdef _WIN32
|
||||
ReadBasicSetting(Settings::values.enable_raw_input);
|
||||
#else
|
||||
Settings::values.enable_raw_input = false;
|
||||
#endif
|
||||
ReadBasicSetting(Settings::values.emulate_analog_keyboard);
|
||||
Settings::values.mouse_panning = false;
|
||||
ReadBasicSetting(Settings::values.mouse_panning_sensitivity);
|
||||
|
||||
ReadBasicSetting(Settings::values.tas_enable);
|
||||
ReadBasicSetting(Settings::values.tas_loop);
|
||||
ReadBasicSetting(Settings::values.tas_swap_controllers);
|
||||
ReadBasicSetting(Settings::values.pause_tas_on_load);
|
||||
|
||||
ReadGlobalSetting(Settings::values.use_docked_mode);
|
||||
|
||||
// Disable docked mode if handheld is selected
|
||||
@@ -673,13 +661,6 @@ void Config::ReadDataStorageValues() {
|
||||
QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::DumpDir)))
|
||||
.toString()
|
||||
.toStdString());
|
||||
FS::SetYuzuPath(FS::YuzuPath::TASDir,
|
||||
qt_config
|
||||
->value(QStringLiteral("tas_directory"),
|
||||
QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::TASDir)))
|
||||
.toString()
|
||||
.toStdString());
|
||||
|
||||
ReadBasicSetting(Settings::values.gamecard_inserted);
|
||||
ReadBasicSetting(Settings::values.gamecard_current_game);
|
||||
ReadBasicSetting(Settings::values.gamecard_path);
|
||||
@@ -709,6 +690,8 @@ void Config::ReadDebuggingValues() {
|
||||
|
||||
void Config::ReadServiceValues() {
|
||||
qt_config->beginGroup(QStringLiteral("Services"));
|
||||
ReadBasicSetting(Settings::values.bcat_backend);
|
||||
ReadBasicSetting(Settings::values.bcat_boxcat_local);
|
||||
ReadBasicSetting(Settings::values.network_interface);
|
||||
qt_config->endGroup();
|
||||
}
|
||||
@@ -1180,6 +1163,7 @@ void Config::SaveAudioValues() {
|
||||
WriteBasicSetting(Settings::values.sink_id);
|
||||
WriteBasicSetting(Settings::values.audio_device_id);
|
||||
}
|
||||
WriteGlobalSetting(Settings::values.enable_audio_stretching);
|
||||
WriteGlobalSetting(Settings::values.volume);
|
||||
|
||||
qt_config->endGroup();
|
||||
@@ -1200,16 +1184,10 @@ void Config::SaveControlValues() {
|
||||
WriteGlobalSetting(Settings::values.vibration_enabled);
|
||||
WriteGlobalSetting(Settings::values.enable_accurate_vibrations);
|
||||
WriteGlobalSetting(Settings::values.motion_enabled);
|
||||
WriteBasicSetting(Settings::values.enable_raw_input);
|
||||
WriteBasicSetting(Settings::values.keyboard_enabled);
|
||||
WriteBasicSetting(Settings::values.emulate_analog_keyboard);
|
||||
WriteBasicSetting(Settings::values.mouse_panning_sensitivity);
|
||||
|
||||
WriteBasicSetting(Settings::values.tas_enable);
|
||||
WriteBasicSetting(Settings::values.tas_loop);
|
||||
WriteBasicSetting(Settings::values.tas_swap_controllers);
|
||||
WriteBasicSetting(Settings::values.pause_tas_on_load);
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
|
||||
@@ -1237,10 +1215,6 @@ void Config::SaveDataStorageValues() {
|
||||
WriteSetting(QStringLiteral("dump_directory"),
|
||||
QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::DumpDir)),
|
||||
QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::DumpDir)));
|
||||
WriteSetting(QStringLiteral("tas_directory"),
|
||||
QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::TASDir)),
|
||||
QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::TASDir)));
|
||||
|
||||
WriteBasicSetting(Settings::values.gamecard_inserted);
|
||||
WriteBasicSetting(Settings::values.gamecard_current_game);
|
||||
WriteBasicSetting(Settings::values.gamecard_path);
|
||||
@@ -1267,6 +1241,8 @@ void Config::SaveDebuggingValues() {
|
||||
void Config::SaveNetworkValues() {
|
||||
qt_config->beginGroup(QStringLiteral("Services"));
|
||||
|
||||
WriteBasicSetting(Settings::values.bcat_backend);
|
||||
WriteBasicSetting(Settings::values.bcat_boxcat_local);
|
||||
WriteBasicSetting(Settings::values.network_interface);
|
||||
|
||||
qt_config->endGroup();
|
||||
|
||||
@@ -42,7 +42,7 @@ public:
|
||||
default_mouse_buttons;
|
||||
static const std::array<int, Settings::NativeKeyboard::NumKeyboardKeys> default_keyboard_keys;
|
||||
static const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> default_keyboard_mods;
|
||||
static const std::array<UISettings::Shortcut, 21> default_hotkeys;
|
||||
static const std::array<UISettings::Shortcut, 18> default_hotkeys;
|
||||
|
||||
private:
|
||||
void Initialize(const std::string& config_name);
|
||||
|
||||
@@ -50,6 +50,8 @@ void ConfigureAudio::SetConfiguration() {
|
||||
const auto volume_value = static_cast<int>(Settings::values.volume.GetValue());
|
||||
ui->volume_slider->setValue(volume_value);
|
||||
|
||||
ui->toggle_audio_stretching->setChecked(Settings::values.enable_audio_stretching.GetValue());
|
||||
|
||||
if (!Settings::IsConfiguringGlobal()) {
|
||||
if (Settings::values.volume.UsingGlobal()) {
|
||||
ui->volume_combo_box->setCurrentIndex(0);
|
||||
@@ -98,6 +100,8 @@ void ConfigureAudio::SetVolumeIndicatorText(int percentage) {
|
||||
}
|
||||
|
||||
void ConfigureAudio::ApplyConfiguration() {
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.enable_audio_stretching,
|
||||
ui->toggle_audio_stretching, enable_audio_stretching);
|
||||
|
||||
if (Settings::IsConfiguringGlobal()) {
|
||||
Settings::values.sink_id =
|
||||
@@ -158,10 +162,15 @@ void ConfigureAudio::RetranslateUI() {
|
||||
void ConfigureAudio::SetupPerGameUI() {
|
||||
if (Settings::IsConfiguringGlobal()) {
|
||||
ui->volume_slider->setEnabled(Settings::values.volume.UsingGlobal());
|
||||
ui->toggle_audio_stretching->setEnabled(
|
||||
Settings::values.enable_audio_stretching.UsingGlobal());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
ConfigurationShared::SetColoredTristate(ui->toggle_audio_stretching,
|
||||
Settings::values.enable_audio_stretching,
|
||||
enable_audio_stretching);
|
||||
connect(ui->volume_combo_box, qOverload<int>(&QComboBox::activated), this, [this](int index) {
|
||||
ui->volume_slider->setEnabled(index == 1);
|
||||
ConfigurationShared::SetHighlight(ui->volume_layout, index == 1);
|
||||
|
||||
@@ -41,4 +41,6 @@ private:
|
||||
void SetupPerGameUI();
|
||||
|
||||
std::unique_ptr<Ui::ConfigureAudio> ui;
|
||||
|
||||
ConfigurationShared::CheckState enable_audio_stretching;
|
||||
};
|
||||
|
||||
@@ -31,6 +31,16 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="toggle_audio_stretching">
|
||||
<property name="toolTip">
|
||||
<string>This post-processing effect adjusts audio speed to match emulation speed and helps prevent audio stutter. This however increases audio latency.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Enable audio stretching</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="_2">
|
||||
<item>
|
||||
|
||||
@@ -2,55 +2,85 @@
|
||||
<ui version="4.0">
|
||||
<class>ConfigureDebug</class>
|
||||
<widget class="QWidget" name="ConfigureDebug">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>777</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_1">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_2">
|
||||
<property name="title">
|
||||
<string>Logging</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_1">
|
||||
<item row="0" column="0" colspan="2">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_1">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_1">
|
||||
<property name="text">
|
||||
<string>Global Log Filter</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="log_filter_edit"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="toggle_console">
|
||||
<property name="text">
|
||||
<string>Show Log in Console</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QPushButton" name="open_log_button">
|
||||
<property name="text">
|
||||
<string>Open Log Location</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QCheckBox" name="extended_logging">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>When checked, the max size of the log increases from 100 MB to 1 GB</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_1">
|
||||
<property name="text">
|
||||
<string>Enable Extended Logging**</string>
|
||||
<string>Global Log Filter</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="log_filter_edit"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="toggle_console">
|
||||
<property name="text">
|
||||
<string>Show Log in Console</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="open_log_button">
|
||||
<property name="text">
|
||||
<string>Open Log Location</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="extended_logging">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>When checked, the max size of the log increases from 100 MB to 1 GB</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Enable Extended Logging</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="font">
|
||||
<font>
|
||||
<italic>true</italic>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>This will be reset automatically when yuzu closes.</string>
|
||||
</property>
|
||||
<property name="indent">
|
||||
<number>20</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
@@ -81,7 +111,7 @@
|
||||
<property name="title">
|
||||
<string>Graphics</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="enable_graphics_debugging">
|
||||
<property name="enabled">
|
||||
@@ -146,18 +176,33 @@
|
||||
<property name="title">
|
||||
<string>Debugging</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<item row="0" column="0">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_7">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="fs_access_log">
|
||||
<property name="text">
|
||||
<string>Enable FS Access Log</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="reporting_services">
|
||||
<property name="text">
|
||||
<string>Enable Verbose Reporting Services**</string>
|
||||
<string>Enable Verbose Reporting Services</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="font">
|
||||
<font>
|
||||
<italic>true</italic>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>This will be reset automatically when yuzu closes.</string>
|
||||
</property>
|
||||
<property name="indent">
|
||||
<number>20</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -169,32 +214,47 @@
|
||||
<property name="title">
|
||||
<string>Advanced</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_4">
|
||||
<item> row="0" column="0">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_8">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="quest_flag">
|
||||
<property name="text">
|
||||
<string>Kiosk (Quest) Mode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="enable_cpu_debugging">
|
||||
<property name="text">
|
||||
<string>Enable CPU Debugging</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="use_debug_asserts">
|
||||
<property name="text">
|
||||
<string>Enable Debug Asserts</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="use_auto_stub">
|
||||
<property name="text">
|
||||
<string>Enable Auto-Stub**</string>
|
||||
<string>Enable Auto-Stub</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="font">
|
||||
<font>
|
||||
<italic>true</italic>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>This will be reset automatically when yuzu closes.</string>
|
||||
</property>
|
||||
<property name="indent">
|
||||
<number>20</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -202,19 +262,20 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="font">
|
||||
<font>
|
||||
<italic>true</italic>
|
||||
</font>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>**This will be reset automatically when yuzu closes.</string>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Expanding</enum>
|
||||
</property>
|
||||
<property name="indent">
|
||||
<number>20</number>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
|
||||
@@ -88,10 +88,6 @@ ConfigureInputAdvanced::ConfigureInputAdvanced(QWidget* parent)
|
||||
connect(ui->buttonMotionTouch, &QPushButton::clicked, this,
|
||||
&ConfigureInputAdvanced::CallMotionTouchConfigDialog);
|
||||
|
||||
#ifndef _WIN32
|
||||
ui->enable_raw_input->setVisible(false);
|
||||
#endif
|
||||
|
||||
LoadConfiguration();
|
||||
}
|
||||
|
||||
@@ -130,7 +126,6 @@ void ConfigureInputAdvanced::ApplyConfiguration() {
|
||||
Settings::values.mouse_panning_sensitivity =
|
||||
static_cast<float>(ui->mouse_panning_sensitivity->value());
|
||||
Settings::values.touchscreen.enabled = ui->touchscreen_enabled->isChecked();
|
||||
Settings::values.enable_raw_input = ui->enable_raw_input->isChecked();
|
||||
}
|
||||
|
||||
void ConfigureInputAdvanced::LoadConfiguration() {
|
||||
@@ -160,7 +155,6 @@ void ConfigureInputAdvanced::LoadConfiguration() {
|
||||
ui->mouse_panning->setChecked(Settings::values.mouse_panning.GetValue());
|
||||
ui->mouse_panning_sensitivity->setValue(Settings::values.mouse_panning_sensitivity.GetValue());
|
||||
ui->touchscreen_enabled->setChecked(Settings::values.touchscreen.enabled);
|
||||
ui->enable_raw_input->setChecked(Settings::values.enable_raw_input.GetValue());
|
||||
|
||||
UpdateUIEnabled();
|
||||
}
|
||||
|
||||
@@ -2672,22 +2672,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="0">
|
||||
<widget class="QCheckBox" name="enable_raw_input">
|
||||
<property name="toolTip">
|
||||
<string>Requires restarting yuzu</string>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>23</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Enable XInput 8 player support (disables web applet)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@@ -124,19 +124,6 @@ QString ButtonToText(const Common::ParamPackage& param) {
|
||||
return GetKeyName(param.Get("code", 0));
|
||||
}
|
||||
|
||||
if (param.Get("engine", "") == "tas") {
|
||||
if (param.Has("axis")) {
|
||||
const QString axis_str = QString::fromStdString(param.Get("axis", ""));
|
||||
|
||||
return QObject::tr("TAS Axis %1").arg(axis_str);
|
||||
}
|
||||
if (param.Has("button")) {
|
||||
const QString button_str = QString::number(int(std::log2(param.Get("button", 0))));
|
||||
return QObject::tr("TAS Btn %1").arg(button_str);
|
||||
}
|
||||
return GetKeyName(param.Get("code", 0));
|
||||
}
|
||||
|
||||
if (param.Get("engine", "") == "cemuhookudp") {
|
||||
if (param.Has("pad_index")) {
|
||||
const QString motion_str = QString::fromStdString(param.Get("pad_index", ""));
|
||||
@@ -200,8 +187,7 @@ QString AnalogToText(const Common::ParamPackage& param, const std::string& dir)
|
||||
const QString axis_y_str = QString::fromStdString(param.Get("axis_y", ""));
|
||||
const bool invert_x = param.Get("invert_x", "+") == "-";
|
||||
const bool invert_y = param.Get("invert_y", "+") == "-";
|
||||
if (engine_str == "sdl" || engine_str == "gcpad" || engine_str == "mouse" ||
|
||||
engine_str == "tas") {
|
||||
if (engine_str == "sdl" || engine_str == "gcpad" || engine_str == "mouse") {
|
||||
if (dir == "modifier") {
|
||||
return QObject::tr("[unused]");
|
||||
}
|
||||
@@ -940,9 +926,9 @@ void ConfigureInputPlayer::UpdateUI() {
|
||||
|
||||
int slider_value;
|
||||
auto& param = analogs_param[analog_id];
|
||||
const bool is_controller =
|
||||
param.Get("engine", "") == "sdl" || param.Get("engine", "") == "gcpad" ||
|
||||
param.Get("engine", "") == "mouse" || param.Get("engine", "") == "tas";
|
||||
const bool is_controller = param.Get("engine", "") == "sdl" ||
|
||||
param.Get("engine", "") == "gcpad" ||
|
||||
param.Get("engine", "") == "mouse";
|
||||
|
||||
if (is_controller) {
|
||||
if (!param.Has("deadzone")) {
|
||||
@@ -1059,12 +1045,8 @@ int ConfigureInputPlayer::GetIndexFromControllerType(Settings::ControllerType ty
|
||||
void ConfigureInputPlayer::UpdateInputDevices() {
|
||||
input_devices = input_subsystem->GetInputDevices();
|
||||
ui->comboDevices->clear();
|
||||
for (auto& device : input_devices) {
|
||||
const std::string display = device.Get("display", "Unknown");
|
||||
ui->comboDevices->addItem(QString::fromStdString(display), {});
|
||||
if (display == "TAS") {
|
||||
device.Set("pad", static_cast<u8>(player_index));
|
||||
}
|
||||
for (auto device : input_devices) {
|
||||
ui->comboDevices->addItem(QString::fromStdString(device.Get("display", "Unknown")), {});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -175,7 +175,7 @@ void PlayerControlPreview::ResetInputs() {
|
||||
}
|
||||
|
||||
void PlayerControlPreview::UpdateInput() {
|
||||
if (!is_enabled && !mapping_active && !Settings::values.tas_enable) {
|
||||
if (!is_enabled && !mapping_active) {
|
||||
return;
|
||||
}
|
||||
bool input_changed = false;
|
||||
@@ -222,19 +222,6 @@ void PlayerControlPreview::UpdateInput() {
|
||||
|
||||
if (input_changed) {
|
||||
update();
|
||||
if (controller_callback.input != nullptr) {
|
||||
ControllerInput input{
|
||||
.axis_values = {std::pair<float, float>{
|
||||
axis_values[Settings::NativeAnalog::LStick].value.x(),
|
||||
axis_values[Settings::NativeAnalog::LStick].value.y()},
|
||||
std::pair<float, float>{
|
||||
axis_values[Settings::NativeAnalog::RStick].value.x(),
|
||||
axis_values[Settings::NativeAnalog::RStick].value.y()}},
|
||||
.button_values = button_values,
|
||||
.changed = true,
|
||||
};
|
||||
controller_callback.input(std::move(input));
|
||||
}
|
||||
}
|
||||
|
||||
if (mapping_active) {
|
||||
@@ -242,10 +229,6 @@ void PlayerControlPreview::UpdateInput() {
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerControlPreview::SetCallBack(ControllerCallback callback_) {
|
||||
controller_callback = std::move(callback_);
|
||||
}
|
||||
|
||||
void PlayerControlPreview::paintEvent(QPaintEvent* event) {
|
||||
QFrame::paintEvent(event);
|
||||
QPainter p(this);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user