Compare commits
225 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b7cd5d742e | ||
|
|
56ecafc204 | ||
|
|
92344da20c | ||
|
|
abda366362 | ||
|
|
0ca91ced2d | ||
|
|
b3c8997829 | ||
|
|
3cfd962ef4 | ||
|
|
0890451c55 | ||
|
|
2dc9dbb809 | ||
|
|
70812ec57b | ||
|
|
a78021580d | ||
|
|
b928fca114 | ||
|
|
8ace3959a5 | ||
|
|
e7e8a87927 | ||
|
|
b254d528bc | ||
|
|
ad50209383 | ||
|
|
d8ad2f3484 | ||
|
|
6f5b942897 | ||
|
|
97b2220a82 | ||
|
|
117bdc71e0 | ||
|
|
760a9e8693 | ||
|
|
30e0d1c973 | ||
|
|
91c06dae1a | ||
|
|
978ca65f59 | ||
|
|
e9e1876e82 | ||
|
|
38110dd485 | ||
|
|
d6a41cfc21 | ||
|
|
92fa5257c7 | ||
|
|
373408ae8c | ||
|
|
70f16f1722 | ||
|
|
9b501af8e3 | ||
|
|
652d6766d5 | ||
|
|
e02ef3c3be | ||
|
|
07b81f57ba | ||
|
|
31de52513e | ||
|
|
e3c2749986 | ||
|
|
b92bf51ae1 | ||
|
|
16e2e1c45f | ||
|
|
428ce8ec29 | ||
|
|
0a966e2cac | ||
|
|
ceb7b11f16 | ||
|
|
8f2959f680 | ||
|
|
8ead176639 | ||
|
|
64e174237e | ||
|
|
c0c4ed0d3b | ||
|
|
5cafa70d3b | ||
|
|
484623cd61 | ||
|
|
57d89e291d | ||
|
|
75eaab2e0f | ||
|
|
9d4edd4e88 | ||
|
|
0a50ba3bd1 | ||
|
|
cb826bcee7 | ||
|
|
ce718522bc | ||
|
|
87f220efff | ||
|
|
c22d0d9945 | ||
|
|
c433c0a746 | ||
|
|
945cfe234b | ||
|
|
9b24197ca0 | ||
|
|
8008b5ddc9 | ||
|
|
da7be67daf | ||
|
|
0aad914527 | ||
|
|
a6ecdf42bc | ||
|
|
9efbf5309f | ||
|
|
af1183a993 | ||
|
|
88192af8ac | ||
|
|
7bf9f9ae49 | ||
|
|
9f5facc3aa | ||
|
|
0785796372 | ||
|
|
e829973742 | ||
|
|
1e149dc18b | ||
|
|
dc5396a466 | ||
|
|
af477fb8c5 | ||
|
|
a0d7a2732d | ||
|
|
f6a89edb67 | ||
|
|
00fb79b2f3 | ||
|
|
91a45834fd | ||
|
|
0b75ec5316 | ||
|
|
c0ab5b79dc | ||
|
|
a111a9ae2c | ||
|
|
6f006d051e | ||
|
|
d62d28522b | ||
|
|
087f52e872 | ||
|
|
7aae6d6d2b | ||
|
|
6bbbbe8f85 | ||
|
|
fc6db97a09 | ||
|
|
4bfa411ddc | ||
|
|
46fdc94586 | ||
|
|
ee21b5378b | ||
|
|
222fe75401 | ||
|
|
448e4d5c2a | ||
|
|
4a4b685a04 | ||
|
|
4f0f481f63 | ||
|
|
1089d76736 | ||
|
|
848bdf8a40 | ||
|
|
7d2839d7a3 | ||
|
|
e67b8678f8 | ||
|
|
c6e1c46ac7 | ||
|
|
c64545d07a | ||
|
|
1d4cbb92f2 | ||
|
|
6053b95552 | ||
|
|
66edfd61c6 | ||
|
|
4a3fd97e48 | ||
|
|
d567b7e841 | ||
|
|
bca9591660 | ||
|
|
98f68d06f1 | ||
|
|
a0e5cccb92 | ||
|
|
6db0c0d8d9 | ||
|
|
14a97d082e | ||
|
|
50e52ade85 | ||
|
|
8aa9ae5ba5 | ||
|
|
131a75b65d | ||
|
|
11d0a6e7b8 | ||
|
|
26547d3e3b | ||
|
|
8049b8beb6 | ||
|
|
12eeffcb7c | ||
|
|
0d713cf8eb | ||
|
|
badea3b301 | ||
|
|
f8543249f0 | ||
|
|
5553bd3ba2 | ||
|
|
7dcf4c0018 | ||
|
|
ef29bf4515 | ||
|
|
3620206136 | ||
|
|
2dbb144fc6 | ||
|
|
89199ca215 | ||
|
|
9cfc5fee2f | ||
|
|
1a6b1bf1d7 | ||
|
|
c5134cbf3a | ||
|
|
c6d001c94f | ||
|
|
cf63eacc1a | ||
|
|
5333db91c1 | ||
|
|
c20569ebdf | ||
|
|
156556ddd2 | ||
|
|
475d46bb64 | ||
|
|
94eca09cf6 | ||
|
|
7af2cb4318 | ||
|
|
657771bdcb | ||
|
|
44b552be71 | ||
|
|
663e221f99 | ||
|
|
725fcbb368 | ||
|
|
a1f176ce52 | ||
|
|
1fd22823bc | ||
|
|
978e7897a3 | ||
|
|
55ac6f7a2b | ||
|
|
79da90cea8 | ||
|
|
4a451e5849 | ||
|
|
cdb2480d39 | ||
|
|
3fdb42e0b4 | ||
|
|
020519def8 | ||
|
|
9a44c1ea27 | ||
|
|
65e697de59 | ||
|
|
7d27a7a511 | ||
|
|
eb84e0f63a | ||
|
|
8e673cbb08 | ||
|
|
047e77e2f0 | ||
|
|
cce14b4cd7 | ||
|
|
6291975731 | ||
|
|
00decfbb07 | ||
|
|
111802bbbb | ||
|
|
3b5d5fa86f | ||
|
|
dcc26c54a5 | ||
|
|
c04203b786 | ||
|
|
cd92a94965 | ||
|
|
941563f981 | ||
|
|
d33399e1f4 | ||
|
|
ce69ff2890 | ||
|
|
c7f32931ee | ||
|
|
1828f82000 | ||
|
|
eb67a45ca8 | ||
|
|
9f08cea2c4 | ||
|
|
8bd246032a | ||
|
|
6b5f565324 | ||
|
|
3984bb6def | ||
|
|
54aabb00b0 | ||
|
|
1dd4132eb1 | ||
|
|
2f6ba54483 | ||
|
|
ae3a755d13 | ||
|
|
98f4c5e7b8 | ||
|
|
061a63547f | ||
|
|
fe53ee26ce | ||
|
|
9afbcd9e8a | ||
|
|
ab052cf684 | ||
|
|
6f6d83befa | ||
|
|
3e46934442 | ||
|
|
e7042163c8 | ||
|
|
85b5b816cf | ||
|
|
ea20b5c970 | ||
|
|
2f852f182a | ||
|
|
1fc61d09d3 | ||
|
|
e408bd3b7c | ||
|
|
2e74b79e89 | ||
|
|
3d592972dc | ||
|
|
678d012c2c | ||
|
|
fdd9154069 | ||
|
|
536c51912d | ||
|
|
88d5140cf2 | ||
|
|
940c3bf68d | ||
|
|
ea8345cdcd | ||
|
|
e03dc4d569 | ||
|
|
ff82f3894a | ||
|
|
f21a189148 | ||
|
|
298b50e220 | ||
|
|
acd35e1b60 | ||
|
|
60bd54776a | ||
|
|
e7a26ecec5 | ||
|
|
f1ead11df7 | ||
|
|
598ef6b0b3 | ||
|
|
54b977acaa | ||
|
|
0ab7bfdfce | ||
|
|
2190f1a2b7 | ||
|
|
743fe1aea3 | ||
|
|
be1954e04c | ||
|
|
c1577f3448 | ||
|
|
1eb908bc88 | ||
|
|
cb708631b6 | ||
|
|
30b1e71066 | ||
|
|
36cfb234d5 | ||
|
|
7b3f5845d2 | ||
|
|
b9a9b83bee | ||
|
|
e4e0abc418 | ||
|
|
8db3feae19 | ||
|
|
62c6c9f6a6 | ||
|
|
b260847218 | ||
|
|
67af0323f0 | ||
|
|
c5a78f4480 | ||
|
|
29a0ca2391 |
5
.gitmodules
vendored
5
.gitmodules
vendored
@@ -1,15 +1,12 @@
|
||||
[submodule "inih"]
|
||||
path = externals/inih/inih
|
||||
url = https://github.com/svn2github/inih
|
||||
url = https://github.com/benhoyt/inih.git
|
||||
[submodule "cubeb"]
|
||||
path = externals/cubeb
|
||||
url = https://github.com/kinetiknz/cubeb.git
|
||||
[submodule "dynarmic"]
|
||||
path = externals/dynarmic
|
||||
url = https://github.com/MerryMage/dynarmic.git
|
||||
[submodule "unicorn"]
|
||||
path = externals/unicorn
|
||||
url = https://github.com/yuzu-emu/unicorn
|
||||
[submodule "soundtouch"]
|
||||
path = externals/soundtouch
|
||||
url = https://github.com/citra-emu/ext-soundtouch.git
|
||||
|
||||
@@ -4,16 +4,8 @@ cd /yuzu
|
||||
# override Travis CI unreasonable ccache size
|
||||
echo 'max_size = 3.0G' > "$HOME/.ccache/ccache.conf"
|
||||
|
||||
# Dirty hack to trick unicorn makefile into believing we are in a MINGW system
|
||||
mv /bin/uname /bin/uname1 && echo -e '#!/bin/sh\necho MINGW64' >> /bin/uname
|
||||
chmod +x /bin/uname
|
||||
|
||||
# Dirty hack to trick unicorn makefile into believing we have cmd
|
||||
echo '' >> /bin/cmd
|
||||
chmod +x /bin/cmd
|
||||
|
||||
mkdir build && cd build
|
||||
cmake .. -G Ninja -DCMAKE_TOOLCHAIN_FILE="$(pwd)/../CMakeModules/MinGWCross.cmake" -DUSE_CCACHE=ON -DYUZU_USE_BUNDLED_UNICORN=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DCMAKE_BUILD_TYPE=Release
|
||||
cmake .. -G Ninja -DCMAKE_TOOLCHAIN_FILE="$(pwd)/../CMakeModules/MinGWCross.cmake" -DUSE_CCACHE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DCMAKE_BUILD_TYPE=Release
|
||||
ninja
|
||||
|
||||
# Clean up the dirty hacks
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
cd /yuzu
|
||||
|
||||
mkdir build && cd build
|
||||
cmake .. -G Ninja -DYUZU_USE_BUNDLED_UNICORN=ON -DYUZU_USE_QT_WEB_ENGINE=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON
|
||||
cmake .. -G Ninja -DYUZU_USE_QT_WEB_ENGINE=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON
|
||||
ninja
|
||||
|
||||
ccache -s
|
||||
|
||||
@@ -4,13 +4,12 @@ set -o pipefail
|
||||
|
||||
export MACOSX_DEPLOYMENT_TARGET=10.14
|
||||
export Qt5_DIR=$(brew --prefix)/opt/qt5
|
||||
export UNICORNDIR=$(pwd)/externals/unicorn
|
||||
export PATH="/usr/local/opt/ccache/libexec:$PATH"
|
||||
|
||||
# TODO: Build using ninja instead of make
|
||||
mkdir build && cd build
|
||||
cmake --version
|
||||
cmake .. -DYUZU_USE_BUNDLED_UNICORN=ON -DYUZU_USE_QT_WEB_ENGINE=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DUSE_DISCORD_PRESENCE=ON
|
||||
cmake .. -DYUZU_USE_QT_WEB_ENGINE=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DUSE_DISCORD_PRESENCE=ON
|
||||
make -j4
|
||||
|
||||
ccache -s
|
||||
|
||||
@@ -18,8 +18,6 @@ CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_QT "Download bundled Qt binaries" ON "EN
|
||||
|
||||
option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON)
|
||||
|
||||
option(YUZU_USE_BUNDLED_UNICORN "Build/Download bundled Unicorn" ON)
|
||||
|
||||
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)
|
||||
@@ -161,7 +159,7 @@ macro(yuzu_find_packages)
|
||||
# Cmake Pkg Prefix Version Conan Pkg
|
||||
"Boost 1.73 boost/1.73.0"
|
||||
"Catch2 2.13 catch2/2.13.0"
|
||||
"fmt 7.0 fmt/7.0.3"
|
||||
"fmt 7.1 fmt/7.1.2"
|
||||
# can't use until https://github.com/bincrafters/community/issues/1173
|
||||
#"libzip 1.5 libzip/1.5.2@bincrafters/stable"
|
||||
"lz4 1.8 lz4/1.9.2"
|
||||
@@ -263,6 +261,7 @@ if (CONAN_REQUIRED_LIBS)
|
||||
libzip:with_openssl=False
|
||||
libzip:enable_windows_crypto=False
|
||||
)
|
||||
|
||||
conan_check(VERSION 1.24.0 REQUIRED)
|
||||
# Add the bincrafters remote
|
||||
conan_add_remote(NAME bincrafters
|
||||
@@ -354,85 +353,23 @@ if (NOT LIBUSB_FOUND)
|
||||
set(LIBUSB_LIBRARIES usb)
|
||||
endif()
|
||||
|
||||
# Use system installed ffmpeg.
|
||||
if (NOT MSVC)
|
||||
find_package(FFmpeg REQUIRED)
|
||||
else()
|
||||
set(FFMPEG_EXT_NAME "ffmpeg-4.2.1")
|
||||
set(FFMPEG_PATH "${CMAKE_BINARY_DIR}/externals/${FFMPEG_EXT_NAME}")
|
||||
download_bundled_external("ffmpeg/" ${FFMPEG_EXT_NAME} "")
|
||||
set(FFMPEG_FOUND YES)
|
||||
set(FFMPEG_INCLUDE_DIR "${FFMPEG_PATH}/include" CACHE PATH "Path to FFmpeg headers" FORCE)
|
||||
set(FFMPEG_LIBRARY_DIR "${FFMPEG_PATH}/bin" CACHE PATH "Path to FFmpeg library" FORCE)
|
||||
set(FFMPEG_DLL_DIR "${FFMPEG_PATH}/bin" CACHE PATH "Path to FFmpeg dll's" FORCE)
|
||||
endif()
|
||||
|
||||
# Prefer the -pthread flag on Linux.
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
# If unicorn isn't found, msvc -> download bundled unicorn; everyone else -> build external
|
||||
if (YUZU_USE_BUNDLED_UNICORN)
|
||||
if (MSVC)
|
||||
message(STATUS "unicorn not found, falling back to bundled")
|
||||
# Detect toolchain and platform
|
||||
if ((MSVC_VERSION GREATER_EQUAL 1910 AND MSVC_VERSION LESS 1930) AND ARCHITECTURE_x86_64)
|
||||
set(UNICORN_VER "unicorn-yuzu")
|
||||
else()
|
||||
message(FATAL_ERROR "No bundled Unicorn binaries for your toolchain. Disable YUZU_USE_BUNDLED_UNICORN and provide your own.")
|
||||
endif()
|
||||
|
||||
if (DEFINED UNICORN_VER)
|
||||
download_bundled_external("unicorn/" ${UNICORN_VER} UNICORN_PREFIX)
|
||||
endif()
|
||||
|
||||
if (DEFINED UNICORN_VER)
|
||||
download_bundled_external("unicorn/" ${UNICORN_VER} UNICORN_PREFIX)
|
||||
endif()
|
||||
|
||||
set(UNICORN_FOUND YES)
|
||||
set(LIBUNICORN_INCLUDE_DIR "${UNICORN_PREFIX}/include" CACHE PATH "Path to Unicorn headers" FORCE)
|
||||
set(LIBUNICORN_LIBRARY "${UNICORN_PREFIX}/lib/x64/unicorn_dynload.lib" CACHE PATH "Path to Unicorn library" FORCE)
|
||||
set(UNICORN_DLL_DIR "${UNICORN_PREFIX}/lib/x64/" CACHE PATH "Path to unicorn.dll" FORCE)
|
||||
else()
|
||||
message(STATUS "unicorn not found, falling back to externals")
|
||||
if (MINGW)
|
||||
set(UNICORN_LIB_NAME "unicorn.a")
|
||||
else()
|
||||
set(UNICORN_LIB_NAME "libunicorn.a")
|
||||
endif()
|
||||
|
||||
set(UNICORN_FOUND YES)
|
||||
set(UNICORN_PREFIX ${PROJECT_SOURCE_DIR}/externals/unicorn)
|
||||
set(LIBUNICORN_LIBRARY "${UNICORN_PREFIX}/${UNICORN_LIB_NAME}" CACHE PATH "Path to Unicorn library" FORCE)
|
||||
set(LIBUNICORN_INCLUDE_DIR "${UNICORN_PREFIX}/include" CACHE PATH "Path to Unicorn headers" FORCE)
|
||||
set(UNICORN_DLL_DIR "${UNICORN_PREFIX}/" CACHE PATH "Path to unicorn dynamic library" FORCE)
|
||||
|
||||
find_package(PythonInterp 2.7 REQUIRED)
|
||||
|
||||
if (MINGW)
|
||||
# Intentionally call the unicorn makefile directly instead of using make.sh so that we can override the
|
||||
# UNAME_S makefile variable to MINGW. This way we don't have to hack at the uname binary to build
|
||||
# Additionally, overriding DO_WINDOWS_EXPORT prevents unicorn from patching the static unicorn.a by using msvc and cmd,
|
||||
# which are both things we don't have in a mingw cross compiling environment.
|
||||
add_custom_command(OUTPUT ${LIBUNICORN_LIBRARY}
|
||||
COMMAND ${CMAKE_COMMAND} -E env UNICORN_ARCHS="aarch64" PYTHON="${PYTHON_EXECUTABLE}" CC=x86_64-w64-mingw32-gcc AR=x86_64-w64-mingw32-gcc-ar RANLIB=x86_64-w64-mingw32-gcc-ranlib make UNAME_S=MINGW DO_WINDOWS_EXPORT=0
|
||||
WORKING_DIRECTORY ${UNICORN_PREFIX}
|
||||
)
|
||||
else()
|
||||
add_custom_command(OUTPUT ${LIBUNICORN_LIBRARY}
|
||||
COMMAND ${CMAKE_COMMAND} -E env UNICORN_ARCHS="aarch64" PYTHON="${PYTHON_EXECUTABLE}" /bin/sh make.sh macos-universal-no
|
||||
WORKING_DIRECTORY ${UNICORN_PREFIX}
|
||||
)
|
||||
endif()
|
||||
|
||||
# ALL makes this custom target build every time
|
||||
# but it won't actually build if LIBUNICORN_LIBRARY is up to date
|
||||
add_custom_target(unicorn-build ALL
|
||||
DEPENDS ${LIBUNICORN_LIBRARY}
|
||||
)
|
||||
unset(UNICORN_LIB_NAME)
|
||||
endif()
|
||||
else()
|
||||
find_package(Unicorn REQUIRED)
|
||||
endif()
|
||||
|
||||
if (UNICORN_FOUND)
|
||||
add_library(unicorn INTERFACE)
|
||||
add_dependencies(unicorn unicorn-build)
|
||||
target_link_libraries(unicorn INTERFACE "${LIBUNICORN_LIBRARY}")
|
||||
target_include_directories(unicorn INTERFACE "${LIBUNICORN_INCLUDE_DIR}")
|
||||
else()
|
||||
message(FATAL_ERROR "Could not find or build unicorn which is required.")
|
||||
endif()
|
||||
|
||||
# Platform-specific library requirements
|
||||
# ======================================
|
||||
|
||||
|
||||
10
CMakeModules/CopyYuzuFFmpegDeps.cmake
Normal file
10
CMakeModules/CopyYuzuFFmpegDeps.cmake
Normal file
@@ -0,0 +1,10 @@
|
||||
function(copy_yuzu_FFmpeg_deps target_dir)
|
||||
include(WindowsCopyFiles)
|
||||
set(DLL_DEST "${CMAKE_BINARY_DIR}/bin/$<CONFIG>/")
|
||||
windows_copy_files(${target_dir} ${FFMPEG_DLL_DIR} ${DLL_DEST}
|
||||
avcodec-58.dll
|
||||
avutil-56.dll
|
||||
swresample-3.dll
|
||||
swscale-5.dll
|
||||
)
|
||||
endfunction(copy_yuzu_FFmpeg_deps)
|
||||
@@ -1,9 +0,0 @@
|
||||
function(copy_yuzu_unicorn_deps target_dir)
|
||||
include(WindowsCopyFiles)
|
||||
set(DLL_DEST "${CMAKE_BINARY_DIR}/bin/$<CONFIG>/")
|
||||
windows_copy_files(${target_dir} ${UNICORN_DLL_DIR} ${DLL_DEST}
|
||||
libgcc_s_seh-1.dll
|
||||
libwinpthread-1.dll
|
||||
unicorn.dll
|
||||
)
|
||||
endfunction(copy_yuzu_unicorn_deps)
|
||||
14
dist/qt_themes/default/style.qss
vendored
14
dist/qt_themes/default/style.qss
vendored
@@ -1,3 +1,7 @@
|
||||
QAbstractSpinBox {
|
||||
min-height: 19px;
|
||||
}
|
||||
|
||||
QPushButton#TogglableStatusBarButton {
|
||||
color: #959595;
|
||||
border: 1px solid transparent;
|
||||
@@ -35,10 +39,10 @@ QPushButton#RendererStatusBarButton:!checked {
|
||||
}
|
||||
|
||||
QPushButton#buttonRefreshDevices {
|
||||
min-width: 20px;
|
||||
min-height: 20px;
|
||||
max-width: 20px;
|
||||
max-height: 20px;
|
||||
min-width: 21px;
|
||||
min-height: 21px;
|
||||
max-width: 21px;
|
||||
max-height: 21px;
|
||||
}
|
||||
|
||||
QWidget#bottomPerGameInput,
|
||||
@@ -71,7 +75,7 @@ QWidget#middleControllerApplet {
|
||||
|
||||
QWidget#topPerGameInput QComboBox,
|
||||
QWidget#middleControllerApplet QComboBox {
|
||||
width: 123px;
|
||||
width: 120px;
|
||||
}
|
||||
|
||||
QWidget#connectedControllers {
|
||||
|
||||
82
dist/qt_themes/qdarkstyle/style.qss
vendored
82
dist/qt_themes/qdarkstyle/style.qss
vendored
@@ -99,12 +99,19 @@ QGroupBox::indicator:unchecked:disabled {
|
||||
}
|
||||
|
||||
QRadioButton {
|
||||
spacing: 5px;
|
||||
outline: none;
|
||||
color: #eff0f1;
|
||||
spacing: 3px;
|
||||
padding: 0px;
|
||||
border: none;
|
||||
outline: none;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
QGroupBox QRadioButton {
|
||||
padding-left: 0px;
|
||||
padding-right: 7px;
|
||||
}
|
||||
|
||||
QRadioButton:disabled {
|
||||
color: #76797C;
|
||||
}
|
||||
@@ -522,13 +529,12 @@ QToolButton#qt_toolbar_ext_button {
|
||||
|
||||
QPushButton {
|
||||
color: #eff0f1;
|
||||
border-width: 1px;
|
||||
border-color: #54575B;
|
||||
border-style: solid;
|
||||
padding: 6px 4px;
|
||||
border: 1px solid #54575B;
|
||||
border-radius: 2px;
|
||||
padding: 5px 0px 5px 0px;
|
||||
outline: none;
|
||||
min-width: 100px;
|
||||
min-height: 13px;
|
||||
background-color: #232629;
|
||||
}
|
||||
|
||||
@@ -553,8 +559,9 @@ QComboBox {
|
||||
selection-background-color: #3daee9;
|
||||
border: 1px solid #54575B;
|
||||
border-radius: 2px;
|
||||
padding: 4px 6px;
|
||||
min-width: 75px;
|
||||
padding: 0px 4px 0px 4px;
|
||||
min-width: 60px;
|
||||
min-height: 23px;
|
||||
background-color: #232629;
|
||||
}
|
||||
|
||||
@@ -608,26 +615,26 @@ QComboBox::down-arrow:focus {
|
||||
}
|
||||
|
||||
QAbstractSpinBox {
|
||||
padding: 4px 6px;
|
||||
border: 1px solid #54575B;
|
||||
background-color: #232629;
|
||||
color: #eff0f1;
|
||||
border-radius: 2px;
|
||||
min-width: 75px;
|
||||
min-width: 52px;
|
||||
min-height: 23px;
|
||||
}
|
||||
|
||||
QAbstractSpinBox:up-button {
|
||||
background-color: transparent;
|
||||
subcontrol-origin: border;
|
||||
subcontrol-position: center right;
|
||||
left: -6px;
|
||||
left: -2px;
|
||||
}
|
||||
|
||||
QAbstractSpinBox:down-button {
|
||||
background-color: transparent;
|
||||
subcontrol-origin: border;
|
||||
subcontrol-position: center left;
|
||||
right: -6px;
|
||||
right: -2px;
|
||||
}
|
||||
|
||||
QAbstractSpinBox::up-arrow,
|
||||
@@ -1277,41 +1284,33 @@ QPushButton#RendererStatusBarButton:!checked {
|
||||
}
|
||||
|
||||
QPushButton#buttonRefreshDevices {
|
||||
min-width: 24px;
|
||||
min-height: 24px;
|
||||
max-width: 24px;
|
||||
max-height: 24px;
|
||||
min-width: 23px;
|
||||
min-height: 23px;
|
||||
max-width: 23px;
|
||||
max-height: 23px;
|
||||
padding: 0px 0px;
|
||||
}
|
||||
|
||||
QSpinBox#spinboxLStickRange,
|
||||
QSpinBox#spinboxRStickRange {
|
||||
padding: 4px 0px 5px 0px;
|
||||
min-width: 63px;
|
||||
}
|
||||
|
||||
QSpinBox#vibrationSpin {
|
||||
padding: 4px 0px 5px 0px;
|
||||
min-width: 63px;
|
||||
}
|
||||
|
||||
QSpinBox#spinboxLStickRange:up-button,
|
||||
QSpinBox#spinboxRStickRange:up-button,
|
||||
QSpinBox#vibrationSpin:up-button {
|
||||
left: -2px;
|
||||
}
|
||||
|
||||
QSpinBox#spinboxLStickRange:down-button,
|
||||
QSpinBox#spinboxRStickRange:down-button,
|
||||
QSpinBox#vibrationSpin:down-button {
|
||||
right: -1px;
|
||||
QSpinBox#spinboxRStickRange,
|
||||
QSpinBox#vibrationSpinPlayer1,
|
||||
QSpinBox#vibrationSpinPlayer2,
|
||||
QSpinBox#vibrationSpinPlayer3,
|
||||
QSpinBox#vibrationSpinPlayer4,
|
||||
QSpinBox#vibrationSpinPlayer5,
|
||||
QSpinBox#vibrationSpinPlayer6,
|
||||
QSpinBox#vibrationSpinPlayer7,
|
||||
QSpinBox#vibrationSpinPlayer8 {
|
||||
min-width: 68px;
|
||||
}
|
||||
|
||||
QDialog#ConfigureVibration QGroupBox::indicator,
|
||||
QGroupBox#motionGroup::indicator,
|
||||
QGroupBox#vibrationGroup::indicator {
|
||||
margin-left: 0px;
|
||||
}
|
||||
|
||||
QDialog#ConfigureVibration QGroupBox::title,
|
||||
QGroupBox#motionGroup::title,
|
||||
QGroupBox#vibrationGroup::title {
|
||||
spacing: 2px;
|
||||
@@ -1340,16 +1339,7 @@ QWidget#middleControllerApplet {
|
||||
|
||||
QWidget#topPerGameInput QComboBox,
|
||||
QWidget#middleControllerApplet QComboBox {
|
||||
width: 119px;
|
||||
}
|
||||
|
||||
QRadioButton#radioDocked {
|
||||
margin-left: -3px;
|
||||
}
|
||||
|
||||
|
||||
QRadioButton#radioUndocked {
|
||||
margin-right: 5px;
|
||||
width: 120px;
|
||||
}
|
||||
|
||||
QWidget#connectedControllers {
|
||||
|
||||
@@ -172,8 +172,8 @@ QCheckBox {
|
||||
color: #F0F0F0;
|
||||
spacing: 4px;
|
||||
outline: none;
|
||||
padding-top: 4px;
|
||||
padding-bottom: 4px;
|
||||
padding-top: 2px;
|
||||
padding-bottom: 2px;
|
||||
}
|
||||
|
||||
QCheckBox:focus {
|
||||
@@ -239,7 +239,7 @@ QGroupBox {
|
||||
border: 1px solid #32414B;
|
||||
border-radius: 4px;
|
||||
margin-top: 12px;
|
||||
padding: 4px;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
QGroupBox::title {
|
||||
@@ -247,7 +247,7 @@ QGroupBox::title {
|
||||
subcontrol-position: top left;
|
||||
padding-left: 3px;
|
||||
padding-right: 5px;
|
||||
padding-top: 4px;
|
||||
padding-top: 2px;
|
||||
}
|
||||
|
||||
QGroupBox::indicator {
|
||||
@@ -298,6 +298,11 @@ QRadioButton {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
QGroupBox QRadioButton {
|
||||
padding-left: 0px;
|
||||
padding-right: 7px;
|
||||
}
|
||||
|
||||
QRadioButton:focus {
|
||||
border: none;
|
||||
}
|
||||
@@ -321,7 +326,6 @@ QRadioButton QWidget {
|
||||
QRadioButton::indicator {
|
||||
border: none;
|
||||
outline: none;
|
||||
margin-left: 4px;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
}
|
||||
@@ -785,14 +789,8 @@ QAbstractSpinBox {
|
||||
background-color: #19232D;
|
||||
border: 1px solid #32414B;
|
||||
color: #F0F0F0;
|
||||
/* This fixes 103, 111 */
|
||||
padding-top: 2px;
|
||||
/* This fixes 103, 111 */
|
||||
padding-bottom: 2px;
|
||||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
border-radius: 4px;
|
||||
/* min-width: 5px; removed to fix 109 */
|
||||
min-height: 19px;
|
||||
}
|
||||
|
||||
QAbstractSpinBox:up-button {
|
||||
@@ -997,10 +995,11 @@ QPushButton {
|
||||
border: 1px solid #32414B;
|
||||
color: #F0F0F0;
|
||||
border-radius: 4px;
|
||||
padding: 3px;
|
||||
padding: 3px 0px 3px 0px;
|
||||
outline: none;
|
||||
/* Issue #194 - Special case of QPushButton inside dialogs, for better UI */
|
||||
min-width: 80px;
|
||||
min-height: 13px;
|
||||
}
|
||||
|
||||
QPushButton:disabled {
|
||||
@@ -1008,14 +1007,14 @@ QPushButton:disabled {
|
||||
border: 1px solid #32414B;
|
||||
color: #787878;
|
||||
border-radius: 4px;
|
||||
padding: 3px;
|
||||
padding: 3px 0px 3px 0px;
|
||||
}
|
||||
|
||||
QPushButton:checked {
|
||||
background-color: #32414B;
|
||||
border: 1px solid #32414B;
|
||||
border-radius: 4px;
|
||||
padding: 3px;
|
||||
padding: 3px 0px 3px 0px;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
@@ -1024,7 +1023,7 @@ QPushButton:checked:disabled {
|
||||
border: 1px solid #32414B;
|
||||
color: #787878;
|
||||
border-radius: 4px;
|
||||
padding: 3px;
|
||||
padding: 3px 0px 3px 0px;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
@@ -1197,15 +1196,9 @@ QComboBox {
|
||||
border: 1px solid #32414B;
|
||||
border-radius: 4px;
|
||||
selection-background-color: #1464A0;
|
||||
padding-left: 4px;
|
||||
padding-right: 36px;
|
||||
/* 4 + 16*2 See scrollbar size */
|
||||
/* Fixes #103, #111 */
|
||||
min-height: 1.5em;
|
||||
/* padding-top: 2px; removed to fix #132 */
|
||||
/* padding-bottom: 2px; removed to fix #132 */
|
||||
/* min-width: 75px; removed to fix #109 */
|
||||
/* Needed to remove indicator - fix #132 */
|
||||
padding: 0px 4px 0px 4px;
|
||||
min-width: 60px;
|
||||
min-height: 19px;
|
||||
}
|
||||
|
||||
QComboBox QAbstractItemView {
|
||||
@@ -2198,29 +2191,40 @@ QPushButton#RendererStatusBarButton:!checked {
|
||||
}
|
||||
|
||||
QPushButton#buttonRefreshDevices {
|
||||
min-width: 20px;
|
||||
min-height: 20px;
|
||||
max-width: 20px;
|
||||
max-height: 20px;
|
||||
min-width: 19px;
|
||||
min-height: 19px;
|
||||
max-width: 19px;
|
||||
max-height: 19px;
|
||||
padding: 0px 0px;
|
||||
}
|
||||
|
||||
QSpinBox#spinboxLStickRange,
|
||||
QSpinBox#spinboxRStickRange {
|
||||
min-width: 38px;
|
||||
QSpinBox#spinboxRStickRange,
|
||||
QSpinBox#vibrationSpinPlayer1,
|
||||
QSpinBox#vibrationSpinPlayer2,
|
||||
QSpinBox#vibrationSpinPlayer3,
|
||||
QSpinBox#vibrationSpinPlayer4,
|
||||
QSpinBox#vibrationSpinPlayer5,
|
||||
QSpinBox#vibrationSpinPlayer6,
|
||||
QSpinBox#vibrationSpinPlayer7,
|
||||
QSpinBox#vibrationSpinPlayer8 {
|
||||
min-width: 68px;
|
||||
}
|
||||
|
||||
QDialog#ConfigureVibration QGroupBox::indicator,
|
||||
QGroupBox#motionGroup::indicator,
|
||||
QGroupBox#vibrationGroup::indicator {
|
||||
margin-left: 0px;
|
||||
}
|
||||
|
||||
QDialog#ConfigureVibration QGroupBox,
|
||||
QWidget#bottomPerGameInput QGroupBox#motionGroup,
|
||||
QWidget#bottomPerGameInput QGroupBox#vibrationGroup,
|
||||
QWidget#bottomPerGameInput QGroupBox#inputConfigGroup {
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
QDialog#ConfigureVibration QGroupBox::title,
|
||||
QGroupBox#motionGroup::title,
|
||||
QGroupBox#vibrationGroup::title {
|
||||
spacing: 2px;
|
||||
@@ -2260,26 +2264,7 @@ QWidget#middleControllerApplet {
|
||||
|
||||
QWidget#topPerGameInput QComboBox,
|
||||
QWidget#middleControllerApplet QComboBox {
|
||||
padding-right: 2px;
|
||||
width: 127px;
|
||||
}
|
||||
|
||||
QGroupBox#handheldGroup {
|
||||
padding-left: 0px;
|
||||
}
|
||||
|
||||
QRadioButton#radioDocked {
|
||||
margin-left: -1px;
|
||||
padding-left: 0px;
|
||||
}
|
||||
|
||||
QRadioButton#radioDocked::indicator {
|
||||
margin-left: 0px;
|
||||
}
|
||||
|
||||
|
||||
QRadioButton#radioUndocked {
|
||||
margin-right: 2px;
|
||||
width: 120px;
|
||||
}
|
||||
|
||||
QWidget#connectedControllers {
|
||||
@@ -2352,7 +2337,7 @@ QCheckBox#checkboxPlayer5Connected,
|
||||
QCheckBox#checkboxPlayer6Connected,
|
||||
QCheckBox#checkboxPlayer7Connected,
|
||||
QCheckBox#checkboxPlayer8Connected {
|
||||
spacing: 0px;
|
||||
spacing: 0px;
|
||||
}
|
||||
|
||||
QWidget#connectedControllers QLabel {
|
||||
@@ -2427,7 +2412,7 @@ QCheckBox#checkboxPlayer7Connected::indicator,
|
||||
QCheckBox#checkboxPlayer8Connected::indicator {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
margin-left: 2px;
|
||||
margin-left: 0px;
|
||||
}
|
||||
|
||||
QWidget#Player1LEDs QCheckBox::indicator:checked,
|
||||
|
||||
25
externals/CMakeLists.txt
vendored
25
externals/CMakeLists.txt
vendored
@@ -73,17 +73,20 @@ if (NOT LIBZIP_FOUND)
|
||||
endif()
|
||||
|
||||
if (ENABLE_WEB_SERVICE)
|
||||
# LibreSSL
|
||||
set(LIBRESSL_SKIP_INSTALL ON CACHE BOOL "")
|
||||
add_subdirectory(libressl EXCLUDE_FROM_ALL)
|
||||
target_include_directories(ssl INTERFACE ./libressl/include)
|
||||
target_compile_definitions(ssl PRIVATE -DHAVE_INET_NTOP)
|
||||
get_directory_property(OPENSSL_LIBRARIES
|
||||
DIRECTORY libressl
|
||||
DEFINITION OPENSSL_LIBS)
|
||||
|
||||
# lurlparser
|
||||
add_subdirectory(lurlparser EXCLUDE_FROM_ALL)
|
||||
find_package(OpenSSL 1.1)
|
||||
if (OPENSSL_FOUND)
|
||||
set(OPENSSL_LIBRARIES OpenSSL::SSL OpenSSL::Crypto)
|
||||
else()
|
||||
# LibreSSL
|
||||
set(LIBRESSL_SKIP_INSTALL ON CACHE BOOL "")
|
||||
set(OPENSSLDIR "/etc/ssl/")
|
||||
add_subdirectory(libressl EXCLUDE_FROM_ALL)
|
||||
target_include_directories(ssl INTERFACE ./libressl/include)
|
||||
target_compile_definitions(ssl PRIVATE -DHAVE_INET_NTOP)
|
||||
get_directory_property(OPENSSL_LIBRARIES
|
||||
DIRECTORY libressl
|
||||
DEFINITION OPENSSL_LIBS)
|
||||
endif()
|
||||
|
||||
# httplib
|
||||
add_library(httplib INTERFACE)
|
||||
|
||||
100
externals/find-modules/FindFFmpeg.cmake
vendored
Normal file
100
externals/find-modules/FindFFmpeg.cmake
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
# - Try to find ffmpeg libraries (libavcodec, libavformat and libavutil)
|
||||
# Once done this will define
|
||||
#
|
||||
# FFMPEG_FOUND - system has ffmpeg or libav
|
||||
# FFMPEG_INCLUDE_DIR - the ffmpeg include directory
|
||||
# FFMPEG_LIBRARIES - Link these to use ffmpeg
|
||||
# FFMPEG_LIBAVCODEC
|
||||
# FFMPEG_LIBAVFORMAT
|
||||
# FFMPEG_LIBAVUTIL
|
||||
#
|
||||
# Copyright (c) 2008 Andreas Schneider <mail@cynapses.org>
|
||||
# Modified for other libraries by Lasse Kärkkäinen <tronic>
|
||||
# Modified for Hedgewars by Stepik777
|
||||
# Modified for FFmpeg-example Tuukka Pasanen 2018
|
||||
# Modified for yuzu toastUnlimted 2020
|
||||
#
|
||||
# Redistribution and use is allowed according to the terms of the New
|
||||
# BSD license.
|
||||
#
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
|
||||
find_package_handle_standard_args(FFMPEG
|
||||
FOUND_VAR FFMPEG_FOUND
|
||||
REQUIRED_VARS
|
||||
FFMPEG_LIBRARY
|
||||
FFMPEG_INCLUDE_DIR
|
||||
VERSION_VAR FFMPEG_VERSION
|
||||
)
|
||||
|
||||
if(FFMPEG_LIBRARIES AND FFMPEG_INCLUDE_DIR)
|
||||
# in cache already
|
||||
set(FFMPEG_FOUND TRUE)
|
||||
else()
|
||||
# use pkg-config to get the directories and then use these values
|
||||
# in the FIND_PATH() and FIND_LIBRARY() calls
|
||||
find_package(PkgConfig)
|
||||
if(PKG_CONFIG_FOUND)
|
||||
pkg_check_modules(_FFMPEG_AVCODEC libavcodec)
|
||||
pkg_check_modules(_FFMPEG_AVUTIL libavutil)
|
||||
pkg_check_modules(_FFMPEG_SWSCALE libswscale)
|
||||
endif()
|
||||
|
||||
find_path(FFMPEG_AVCODEC_INCLUDE_DIR
|
||||
NAMES libavcodec/avcodec.h
|
||||
PATHS ${_FFMPEG_AVCODEC_INCLUDE_DIRS}
|
||||
/usr/include
|
||||
/usr/local/include
|
||||
/opt/local/include
|
||||
/sw/include
|
||||
PATH_SUFFIXES ffmpeg libav)
|
||||
|
||||
find_library(FFMPEG_LIBAVCODEC
|
||||
NAMES avcodec
|
||||
PATHS ${_FFMPEG_AVCODEC_LIBRARY_DIRS}
|
||||
/usr/lib
|
||||
/usr/local/lib
|
||||
/opt/local/lib
|
||||
/sw/lib)
|
||||
|
||||
find_library(FFMPEG_LIBAVUTIL
|
||||
NAMES avutil
|
||||
PATHS ${_FFMPEG_AVUTIL_LIBRARY_DIRS}
|
||||
/usr/lib
|
||||
/usr/local/lib
|
||||
/opt/local/lib
|
||||
/sw/lib)
|
||||
|
||||
find_library(FFMPEG_LIBSWSCALE
|
||||
NAMES swscale
|
||||
PATHS ${_FFMPEG_SWSCALE_LIBRARY_DIRS}
|
||||
/usr/lib
|
||||
/usr/local/lib
|
||||
/opt/local/lib
|
||||
/sw/lib)
|
||||
|
||||
if(FFMPEG_LIBAVCODEC AND FFMPEG_LIBAVUTIL AND FFMPEG_LIBSWSCALE)
|
||||
set(FFMPEG_FOUND TRUE)
|
||||
endif()
|
||||
|
||||
if(FFMPEG_FOUND)
|
||||
set(FFMPEG_INCLUDE_DIR ${FFMPEG_AVCODEC_INCLUDE_DIR})
|
||||
set(FFMPEG_LIBRARIES
|
||||
${FFMPEG_LIBAVCODEC}
|
||||
${FFMPEG_LIBAVUTIL}
|
||||
${FFMPEG_LIBSWSCALE})
|
||||
endif()
|
||||
|
||||
if(FFMPEG_FOUND)
|
||||
if(NOT FFMPEG_FIND_QUIETLY)
|
||||
message(STATUS
|
||||
"Found FFMPEG or Libav: ${FFMPEG_LIBRARIES}, ${FFMPEG_INCLUDE_DIR}")
|
||||
endif()
|
||||
else()
|
||||
if(FFMPEG_FIND_REQUIRED)
|
||||
message(FATAL_ERROR
|
||||
"Could not find libavcodec or libavutil or libswscale")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
2
externals/httplib/README.md
vendored
2
externals/httplib/README.md
vendored
@@ -1,4 +1,4 @@
|
||||
From https://github.com/yhirose/cpp-httplib/tree/fce8e6fefdab4ad48bc5b25c98e5ebfda4f3cf53
|
||||
From https://github.com/yhirose/cpp-httplib/tree/ff5677ad197947177c158fe857caff4f0e242045 with https://github.com/yhirose/cpp-httplib/pull/701
|
||||
|
||||
MIT License
|
||||
|
||||
|
||||
657
externals/httplib/httplib.h
vendored
657
externals/httplib/httplib.h
vendored
File diff suppressed because it is too large
Load Diff
2
externals/inih/inih
vendored
2
externals/inih/inih
vendored
Submodule externals/inih/inih updated: 603729dec8...1e80a47dff
2
externals/libressl
vendored
2
externals/libressl
vendored
Submodule externals/libressl updated: 7d01cb01cb...8289d0d07d
8
externals/lurlparser/CMakeLists.txt
vendored
8
externals/lurlparser/CMakeLists.txt
vendored
@@ -1,8 +0,0 @@
|
||||
add_library(lurlparser
|
||||
LUrlParser.cpp
|
||||
LUrlParser.h
|
||||
)
|
||||
|
||||
create_target_directory_groups(lurlparser)
|
||||
|
||||
target_include_directories(lurlparser INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
265
externals/lurlparser/LUrlParser.cpp
vendored
265
externals/lurlparser/LUrlParser.cpp
vendored
@@ -1,265 +0,0 @@
|
||||
/*
|
||||
* Lightweight URL & URI parser (RFC 1738, RFC 3986)
|
||||
* https://github.com/corporateshark/LUrlParser
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (C) 2015 Sergey Kosarevsky (sk@linderdaum.com)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "LUrlParser.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <stdlib.h>
|
||||
|
||||
// check if the scheme name is valid
|
||||
static bool IsSchemeValid( const std::string& SchemeName )
|
||||
{
|
||||
for ( auto c : SchemeName )
|
||||
{
|
||||
if ( !isalpha( c ) && c != '+' && c != '-' && c != '.' ) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LUrlParser::clParseURL::GetPort( int* OutPort ) const
|
||||
{
|
||||
if ( !IsValid() ) { return false; }
|
||||
|
||||
int Port = atoi( m_Port.c_str() );
|
||||
|
||||
if ( Port <= 0 || Port > 65535 ) { return false; }
|
||||
|
||||
if ( OutPort ) { *OutPort = Port; }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// based on RFC 1738 and RFC 3986
|
||||
LUrlParser::clParseURL LUrlParser::clParseURL::ParseURL( const std::string& URL )
|
||||
{
|
||||
LUrlParser::clParseURL Result;
|
||||
|
||||
const char* CurrentString = URL.c_str();
|
||||
|
||||
/*
|
||||
* <scheme>:<scheme-specific-part>
|
||||
* <scheme> := [a-z\+\-\.]+
|
||||
* For resiliency, programs interpreting URLs should treat upper case letters as equivalent to lower case in scheme names
|
||||
*/
|
||||
|
||||
// try to read scheme
|
||||
{
|
||||
const char* LocalString = strchr( CurrentString, ':' );
|
||||
|
||||
if ( !LocalString )
|
||||
{
|
||||
return clParseURL( LUrlParserError_NoUrlCharacter );
|
||||
}
|
||||
|
||||
// save the scheme name
|
||||
Result.m_Scheme = std::string( CurrentString, LocalString - CurrentString );
|
||||
|
||||
if ( !IsSchemeValid( Result.m_Scheme ) )
|
||||
{
|
||||
return clParseURL( LUrlParserError_InvalidSchemeName );
|
||||
}
|
||||
|
||||
// scheme should be lowercase
|
||||
std::transform( Result.m_Scheme.begin(), Result.m_Scheme.end(), Result.m_Scheme.begin(), ::tolower );
|
||||
|
||||
// skip ':'
|
||||
CurrentString = LocalString+1;
|
||||
}
|
||||
|
||||
/*
|
||||
* //<user>:<password>@<host>:<port>/<url-path>
|
||||
* any ":", "@" and "/" must be normalized
|
||||
*/
|
||||
|
||||
// skip "//"
|
||||
if ( *CurrentString++ != '/' ) return clParseURL( LUrlParserError_NoDoubleSlash );
|
||||
if ( *CurrentString++ != '/' ) return clParseURL( LUrlParserError_NoDoubleSlash );
|
||||
|
||||
// check if the user name and password are specified
|
||||
bool bHasUserName = false;
|
||||
|
||||
const char* LocalString = CurrentString;
|
||||
|
||||
while ( *LocalString )
|
||||
{
|
||||
if ( *LocalString == '@' )
|
||||
{
|
||||
// user name and password are specified
|
||||
bHasUserName = true;
|
||||
break;
|
||||
}
|
||||
else if ( *LocalString == '/' )
|
||||
{
|
||||
// end of <host>:<port> specification
|
||||
bHasUserName = false;
|
||||
break;
|
||||
}
|
||||
|
||||
LocalString++;
|
||||
}
|
||||
|
||||
// user name and password
|
||||
LocalString = CurrentString;
|
||||
|
||||
if ( bHasUserName )
|
||||
{
|
||||
// read user name
|
||||
while ( *LocalString && *LocalString != ':' && *LocalString != '@' ) LocalString++;
|
||||
|
||||
Result.m_UserName = std::string( CurrentString, LocalString - CurrentString );
|
||||
|
||||
// proceed with the current pointer
|
||||
CurrentString = LocalString;
|
||||
|
||||
if ( *CurrentString == ':' )
|
||||
{
|
||||
// skip ':'
|
||||
CurrentString++;
|
||||
|
||||
// read password
|
||||
LocalString = CurrentString;
|
||||
|
||||
while ( *LocalString && *LocalString != '@' ) LocalString++;
|
||||
|
||||
Result.m_Password = std::string( CurrentString, LocalString - CurrentString );
|
||||
|
||||
CurrentString = LocalString;
|
||||
}
|
||||
|
||||
// skip '@'
|
||||
if ( *CurrentString != '@' )
|
||||
{
|
||||
return clParseURL( LUrlParserError_NoAtSign );
|
||||
}
|
||||
|
||||
CurrentString++;
|
||||
}
|
||||
|
||||
bool bHasBracket = ( *CurrentString == '[' );
|
||||
|
||||
// go ahead, read the host name
|
||||
LocalString = CurrentString;
|
||||
|
||||
while ( *LocalString )
|
||||
{
|
||||
if ( bHasBracket && *LocalString == ']' )
|
||||
{
|
||||
// end of IPv6 address
|
||||
LocalString++;
|
||||
break;
|
||||
}
|
||||
else if ( !bHasBracket && ( *LocalString == ':' || *LocalString == '/' ) )
|
||||
{
|
||||
// port number is specified
|
||||
break;
|
||||
}
|
||||
|
||||
LocalString++;
|
||||
}
|
||||
|
||||
Result.m_Host = std::string( CurrentString, LocalString - CurrentString );
|
||||
|
||||
CurrentString = LocalString;
|
||||
|
||||
// is port number specified?
|
||||
if ( *CurrentString == ':' )
|
||||
{
|
||||
CurrentString++;
|
||||
|
||||
// read port number
|
||||
LocalString = CurrentString;
|
||||
|
||||
while ( *LocalString && *LocalString != '/' ) LocalString++;
|
||||
|
||||
Result.m_Port = std::string( CurrentString, LocalString - CurrentString );
|
||||
|
||||
CurrentString = LocalString;
|
||||
}
|
||||
|
||||
// end of string
|
||||
if ( !*CurrentString )
|
||||
{
|
||||
Result.m_ErrorCode = LUrlParserError_Ok;
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
// skip '/'
|
||||
if ( *CurrentString != '/' )
|
||||
{
|
||||
return clParseURL( LUrlParserError_NoSlash );
|
||||
}
|
||||
|
||||
CurrentString++;
|
||||
|
||||
// parse the path
|
||||
LocalString = CurrentString;
|
||||
|
||||
while ( *LocalString && *LocalString != '#' && *LocalString != '?' ) LocalString++;
|
||||
|
||||
Result.m_Path = std::string( CurrentString, LocalString - CurrentString );
|
||||
|
||||
CurrentString = LocalString;
|
||||
|
||||
// check for query
|
||||
if ( *CurrentString == '?' )
|
||||
{
|
||||
// skip '?'
|
||||
CurrentString++;
|
||||
|
||||
// read query
|
||||
LocalString = CurrentString;
|
||||
|
||||
while ( *LocalString && *LocalString != '#' ) LocalString++;
|
||||
|
||||
Result.m_Query = std::string( CurrentString, LocalString - CurrentString );
|
||||
|
||||
CurrentString = LocalString;
|
||||
}
|
||||
|
||||
// check for fragment
|
||||
if ( *CurrentString == '#' )
|
||||
{
|
||||
// skip '#'
|
||||
CurrentString++;
|
||||
|
||||
// read fragment
|
||||
LocalString = CurrentString;
|
||||
|
||||
while ( *LocalString ) LocalString++;
|
||||
|
||||
Result.m_Fragment = std::string( CurrentString, LocalString - CurrentString );
|
||||
|
||||
CurrentString = LocalString;
|
||||
}
|
||||
|
||||
Result.m_ErrorCode = LUrlParserError_Ok;
|
||||
|
||||
return Result;
|
||||
}
|
||||
78
externals/lurlparser/LUrlParser.h
vendored
78
externals/lurlparser/LUrlParser.h
vendored
@@ -1,78 +0,0 @@
|
||||
/*
|
||||
* Lightweight URL & URI parser (RFC 1738, RFC 3986)
|
||||
* https://github.com/corporateshark/LUrlParser
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (C) 2015 Sergey Kosarevsky (sk@linderdaum.com)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace LUrlParser
|
||||
{
|
||||
enum LUrlParserError
|
||||
{
|
||||
LUrlParserError_Ok = 0,
|
||||
LUrlParserError_Uninitialized = 1,
|
||||
LUrlParserError_NoUrlCharacter = 2,
|
||||
LUrlParserError_InvalidSchemeName = 3,
|
||||
LUrlParserError_NoDoubleSlash = 4,
|
||||
LUrlParserError_NoAtSign = 5,
|
||||
LUrlParserError_UnexpectedEndOfLine = 6,
|
||||
LUrlParserError_NoSlash = 7,
|
||||
};
|
||||
|
||||
class clParseURL
|
||||
{
|
||||
public:
|
||||
LUrlParserError m_ErrorCode;
|
||||
std::string m_Scheme;
|
||||
std::string m_Host;
|
||||
std::string m_Port;
|
||||
std::string m_Path;
|
||||
std::string m_Query;
|
||||
std::string m_Fragment;
|
||||
std::string m_UserName;
|
||||
std::string m_Password;
|
||||
|
||||
clParseURL()
|
||||
: m_ErrorCode( LUrlParserError_Uninitialized )
|
||||
{}
|
||||
|
||||
/// return 'true' if the parsing was successful
|
||||
bool IsValid() const { return m_ErrorCode == LUrlParserError_Ok; }
|
||||
|
||||
/// helper to convert the port number to int, return 'true' if the port is valid (within the 0..65535 range)
|
||||
bool GetPort( int* OutPort ) const;
|
||||
|
||||
/// parse the URL
|
||||
static clParseURL ParseURL( const std::string& URL );
|
||||
|
||||
private:
|
||||
explicit clParseURL( LUrlParserError ErrorCode )
|
||||
: m_ErrorCode( ErrorCode )
|
||||
{}
|
||||
};
|
||||
|
||||
} // namespace LUrlParser
|
||||
19
externals/lurlparser/README.md
vendored
19
externals/lurlparser/README.md
vendored
@@ -1,19 +0,0 @@
|
||||
From https://github.com/corporateshark/LUrlParser/commit/455d5e2d27e3946f11ad0328fee9ee2628e6a8e2
|
||||
|
||||
MIT License
|
||||
|
||||
===
|
||||
|
||||
Lightweight URL & URI parser (RFC 1738, RFC 3986)
|
||||
|
||||
(C) Sergey Kosarevsky, 2015
|
||||
|
||||
@corporateshark sk@linderdaum.com
|
||||
|
||||
http://www.linderdaum.com
|
||||
|
||||
http://blog.linderdaum.com
|
||||
|
||||
=============================
|
||||
|
||||
A tiny and lightweight URL & URI parser (RFC 1738, RFC 3986) written in C++.
|
||||
18
externals/microprofile/microprofile.h
vendored
18
externals/microprofile/microprofile.h
vendored
@@ -902,8 +902,10 @@ inline uint16_t MicroProfileGetGroupIndex(MicroProfileToken t)
|
||||
#include <windows.h>
|
||||
#define snprintf _snprintf
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4244)
|
||||
#endif
|
||||
int64_t MicroProfileTicksPerSecondCpu()
|
||||
{
|
||||
static int64_t nTicksPerSecond = 0;
|
||||
@@ -946,7 +948,11 @@ typedef HANDLE MicroProfileThread;
|
||||
DWORD _stdcall ThreadTrampoline(void* pFunc)
|
||||
{
|
||||
MicroProfileThreadFunc F = (MicroProfileThreadFunc)pFunc;
|
||||
return (uint32_t)F(0);
|
||||
|
||||
// The return value of F will always return a void*, however, this is for
|
||||
// compatibility with pthreads. The underlying "address" of the pointer
|
||||
// is always a 32-bit value, so this cast is safe to perform.
|
||||
return static_cast<DWORD>(reinterpret_cast<uint64_t>(F(0)));
|
||||
}
|
||||
|
||||
inline void MicroProfileThreadStart(MicroProfileThread* pThread, MicroProfileThreadFunc Func)
|
||||
@@ -1742,10 +1748,10 @@ void MicroProfileFlip()
|
||||
}
|
||||
}
|
||||
}
|
||||
for(uint32_t i = 0; i < MICROPROFILE_MAX_GROUPS; ++i)
|
||||
for(uint32_t j = 0; j < MICROPROFILE_MAX_GROUPS; ++j)
|
||||
{
|
||||
pLog->nGroupTicks[i] += nGroupTicks[i];
|
||||
pFrameGroup[i] += nGroupTicks[i];
|
||||
pLog->nGroupTicks[j] += nGroupTicks[j];
|
||||
pFrameGroup[j] += nGroupTicks[j];
|
||||
}
|
||||
pLog->nStackPos = nStackPos;
|
||||
}
|
||||
@@ -3328,7 +3334,7 @@ bool MicroProfileIsLocalThread(uint32_t nThreadId)
|
||||
#endif
|
||||
#else
|
||||
|
||||
bool MicroProfileIsLocalThread(uint32_t nThreadId){return false;}
|
||||
bool MicroProfileIsLocalThread([[maybe_unused]] uint32_t nThreadId) { return false; }
|
||||
void MicroProfileStopContextSwitchTrace(){}
|
||||
void MicroProfileStartContextSwitchTrace(){}
|
||||
|
||||
@@ -3576,7 +3582,7 @@ int MicroProfileGetGpuTickReference(int64_t* pOutCpu, int64_t* pOutGpu)
|
||||
|
||||
#undef S
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
|
||||
1
externals/unicorn
vendored
1
externals/unicorn
vendored
Submodule externals/unicorn deleted from 73f4573535
@@ -32,7 +32,6 @@ if (MSVC)
|
||||
# /Zc:inline - Let codegen omit inline functions in object files
|
||||
# /Zc:throwingNew - Let codegen assume `operator new` (without std::nothrow) will never return null
|
||||
add_compile_options(
|
||||
/W3
|
||||
/MP
|
||||
/Zi
|
||||
/Zo
|
||||
@@ -43,6 +42,13 @@ if (MSVC)
|
||||
/Zc:externConstexpr
|
||||
/Zc:inline
|
||||
/Zc:throwingNew
|
||||
|
||||
# Warnings
|
||||
/W3
|
||||
/we4547 # 'operator' : operator before comma has no effect; expected operator with side-effect
|
||||
/we4549 # 'operator1': operator before comma has no effect; did you intend 'operator2'?
|
||||
/we4555 # Expression has no effect; expected expression with side-effect
|
||||
/we4834 # Discarding return value of function with 'nodiscard' attribute
|
||||
)
|
||||
|
||||
# /GS- - No stack buffer overflow checks
|
||||
@@ -56,6 +62,7 @@ else()
|
||||
-Werror=implicit-fallthrough
|
||||
-Werror=missing-declarations
|
||||
-Werror=reorder
|
||||
-Werror=unused-result
|
||||
-Wextra
|
||||
-Wmissing-declarations
|
||||
-Wno-attributes
|
||||
|
||||
@@ -51,9 +51,12 @@ if (NOT MSVC)
|
||||
-Werror=implicit-fallthrough
|
||||
-Werror=reorder
|
||||
-Werror=sign-compare
|
||||
-Werror=unused-but-set-parameter
|
||||
-Werror=unused-but-set-variable
|
||||
-Werror=unused-variable
|
||||
|
||||
$<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter>
|
||||
$<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable>
|
||||
|
||||
-Wno-sign-conversion
|
||||
)
|
||||
endif()
|
||||
|
||||
|
||||
@@ -150,6 +150,8 @@ add_library(common STATIC
|
||||
scope_exit.h
|
||||
spin_lock.cpp
|
||||
spin_lock.h
|
||||
stream.cpp
|
||||
stream.h
|
||||
string_util.cpp
|
||||
string_util.h
|
||||
swap.h
|
||||
@@ -188,6 +190,22 @@ if(ARCHITECTURE_x86_64)
|
||||
)
|
||||
endif()
|
||||
|
||||
if (MSVC)
|
||||
target_compile_definitions(common PRIVATE
|
||||
# The standard library doesn't provide any replacement for codecvt yet
|
||||
# so we can disable this deprecation warning for the time being.
|
||||
_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING
|
||||
)
|
||||
target_compile_options(common PRIVATE
|
||||
/W4
|
||||
/WX
|
||||
)
|
||||
else()
|
||||
target_compile_options(common PRIVATE
|
||||
-Werror
|
||||
)
|
||||
endif()
|
||||
|
||||
create_target_directory_groups(common)
|
||||
find_package(Boost 1.71 COMPONENTS context headers REQUIRED)
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/fiber.h"
|
||||
#include "common/spin_lock.h"
|
||||
|
||||
#if defined(_WIN32) || defined(WIN32)
|
||||
#include <windows.h>
|
||||
#else
|
||||
@@ -14,18 +16,45 @@ namespace Common {
|
||||
|
||||
constexpr std::size_t default_stack_size = 256 * 1024; // 256kb
|
||||
|
||||
#if defined(_WIN32) || defined(WIN32)
|
||||
|
||||
struct Fiber::FiberImpl {
|
||||
SpinLock guard{};
|
||||
std::function<void(void*)> entry_point;
|
||||
std::function<void(void*)> rewind_point;
|
||||
void* rewind_parameter{};
|
||||
void* start_parameter{};
|
||||
std::shared_ptr<Fiber> previous_fiber;
|
||||
bool is_thread_fiber{};
|
||||
bool released{};
|
||||
|
||||
#if defined(_WIN32) || defined(WIN32)
|
||||
LPVOID handle = nullptr;
|
||||
LPVOID rewind_handle = nullptr;
|
||||
#else
|
||||
alignas(64) std::array<u8, default_stack_size> stack;
|
||||
alignas(64) std::array<u8, default_stack_size> rewind_stack;
|
||||
u8* stack_limit;
|
||||
u8* rewind_stack_limit;
|
||||
boost::context::detail::fcontext_t context;
|
||||
boost::context::detail::fcontext_t rewind_context;
|
||||
#endif
|
||||
};
|
||||
|
||||
void Fiber::SetStartParameter(void* new_parameter) {
|
||||
impl->start_parameter = new_parameter;
|
||||
}
|
||||
|
||||
void Fiber::SetRewindPoint(std::function<void(void*)>&& rewind_func, void* rewind_param) {
|
||||
impl->rewind_point = std::move(rewind_func);
|
||||
impl->rewind_parameter = rewind_param;
|
||||
}
|
||||
|
||||
#if defined(_WIN32) || defined(WIN32)
|
||||
|
||||
void Fiber::Start() {
|
||||
ASSERT(previous_fiber != nullptr);
|
||||
previous_fiber->guard.unlock();
|
||||
previous_fiber.reset();
|
||||
entry_point(start_parameter);
|
||||
ASSERT(impl->previous_fiber != nullptr);
|
||||
impl->previous_fiber->impl->guard.unlock();
|
||||
impl->previous_fiber.reset();
|
||||
impl->entry_point(impl->start_parameter);
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
@@ -34,99 +63,86 @@ void Fiber::OnRewind() {
|
||||
DeleteFiber(impl->handle);
|
||||
impl->handle = impl->rewind_handle;
|
||||
impl->rewind_handle = nullptr;
|
||||
rewind_point(rewind_parameter);
|
||||
impl->rewind_point(impl->rewind_parameter);
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
void Fiber::FiberStartFunc(void* fiber_parameter) {
|
||||
auto fiber = static_cast<Fiber*>(fiber_parameter);
|
||||
auto* fiber = static_cast<Fiber*>(fiber_parameter);
|
||||
fiber->Start();
|
||||
}
|
||||
|
||||
void Fiber::RewindStartFunc(void* fiber_parameter) {
|
||||
auto fiber = static_cast<Fiber*>(fiber_parameter);
|
||||
auto* fiber = static_cast<Fiber*>(fiber_parameter);
|
||||
fiber->OnRewind();
|
||||
}
|
||||
|
||||
Fiber::Fiber(std::function<void(void*)>&& entry_point_func, void* start_parameter)
|
||||
: entry_point{std::move(entry_point_func)}, start_parameter{start_parameter} {
|
||||
impl = std::make_unique<FiberImpl>();
|
||||
: impl{std::make_unique<FiberImpl>()} {
|
||||
impl->entry_point = std::move(entry_point_func);
|
||||
impl->start_parameter = start_parameter;
|
||||
impl->handle = CreateFiber(default_stack_size, &FiberStartFunc, this);
|
||||
}
|
||||
|
||||
Fiber::Fiber() : impl{std::make_unique<FiberImpl>()} {}
|
||||
|
||||
Fiber::~Fiber() {
|
||||
if (released) {
|
||||
if (impl->released) {
|
||||
return;
|
||||
}
|
||||
// Make sure the Fiber is not being used
|
||||
const bool locked = guard.try_lock();
|
||||
const bool locked = impl->guard.try_lock();
|
||||
ASSERT_MSG(locked, "Destroying a fiber that's still running");
|
||||
if (locked) {
|
||||
guard.unlock();
|
||||
impl->guard.unlock();
|
||||
}
|
||||
DeleteFiber(impl->handle);
|
||||
}
|
||||
|
||||
void Fiber::Exit() {
|
||||
ASSERT_MSG(is_thread_fiber, "Exitting non main thread fiber");
|
||||
if (!is_thread_fiber) {
|
||||
ASSERT_MSG(impl->is_thread_fiber, "Exitting non main thread fiber");
|
||||
if (!impl->is_thread_fiber) {
|
||||
return;
|
||||
}
|
||||
ConvertFiberToThread();
|
||||
guard.unlock();
|
||||
released = true;
|
||||
}
|
||||
|
||||
void Fiber::SetRewindPoint(std::function<void(void*)>&& rewind_func, void* start_parameter) {
|
||||
rewind_point = std::move(rewind_func);
|
||||
rewind_parameter = start_parameter;
|
||||
impl->guard.unlock();
|
||||
impl->released = true;
|
||||
}
|
||||
|
||||
void Fiber::Rewind() {
|
||||
ASSERT(rewind_point);
|
||||
ASSERT(impl->rewind_point);
|
||||
ASSERT(impl->rewind_handle == nullptr);
|
||||
impl->rewind_handle = CreateFiber(default_stack_size, &RewindStartFunc, this);
|
||||
SwitchToFiber(impl->rewind_handle);
|
||||
}
|
||||
|
||||
void Fiber::YieldTo(std::shared_ptr<Fiber>& from, std::shared_ptr<Fiber>& to) {
|
||||
void Fiber::YieldTo(std::shared_ptr<Fiber> from, std::shared_ptr<Fiber> to) {
|
||||
ASSERT_MSG(from != nullptr, "Yielding fiber is null!");
|
||||
ASSERT_MSG(to != nullptr, "Next fiber is null!");
|
||||
to->guard.lock();
|
||||
to->previous_fiber = from;
|
||||
to->impl->guard.lock();
|
||||
to->impl->previous_fiber = from;
|
||||
SwitchToFiber(to->impl->handle);
|
||||
ASSERT(from->previous_fiber != nullptr);
|
||||
from->previous_fiber->guard.unlock();
|
||||
from->previous_fiber.reset();
|
||||
ASSERT(from->impl->previous_fiber != nullptr);
|
||||
from->impl->previous_fiber->impl->guard.unlock();
|
||||
from->impl->previous_fiber.reset();
|
||||
}
|
||||
|
||||
std::shared_ptr<Fiber> Fiber::ThreadToFiber() {
|
||||
std::shared_ptr<Fiber> fiber = std::shared_ptr<Fiber>{new Fiber()};
|
||||
fiber->guard.lock();
|
||||
fiber->impl->guard.lock();
|
||||
fiber->impl->handle = ConvertThreadToFiber(nullptr);
|
||||
fiber->is_thread_fiber = true;
|
||||
fiber->impl->is_thread_fiber = true;
|
||||
return fiber;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
struct Fiber::FiberImpl {
|
||||
alignas(64) std::array<u8, default_stack_size> stack;
|
||||
alignas(64) std::array<u8, default_stack_size> rewind_stack;
|
||||
u8* stack_limit;
|
||||
u8* rewind_stack_limit;
|
||||
boost::context::detail::fcontext_t context;
|
||||
boost::context::detail::fcontext_t rewind_context;
|
||||
};
|
||||
|
||||
void Fiber::Start(boost::context::detail::transfer_t& transfer) {
|
||||
ASSERT(previous_fiber != nullptr);
|
||||
previous_fiber->impl->context = transfer.fctx;
|
||||
previous_fiber->guard.unlock();
|
||||
previous_fiber.reset();
|
||||
entry_point(start_parameter);
|
||||
ASSERT(impl->previous_fiber != nullptr);
|
||||
impl->previous_fiber->impl->context = transfer.fctx;
|
||||
impl->previous_fiber->impl->guard.unlock();
|
||||
impl->previous_fiber.reset();
|
||||
impl->entry_point(impl->start_parameter);
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
@@ -137,23 +153,24 @@ void Fiber::OnRewind([[maybe_unused]] boost::context::detail::transfer_t& transf
|
||||
u8* tmp = impl->stack_limit;
|
||||
impl->stack_limit = impl->rewind_stack_limit;
|
||||
impl->rewind_stack_limit = tmp;
|
||||
rewind_point(rewind_parameter);
|
||||
impl->rewind_point(impl->rewind_parameter);
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
void Fiber::FiberStartFunc(boost::context::detail::transfer_t transfer) {
|
||||
auto fiber = static_cast<Fiber*>(transfer.data);
|
||||
auto* fiber = static_cast<Fiber*>(transfer.data);
|
||||
fiber->Start(transfer);
|
||||
}
|
||||
|
||||
void Fiber::RewindStartFunc(boost::context::detail::transfer_t transfer) {
|
||||
auto fiber = static_cast<Fiber*>(transfer.data);
|
||||
auto* fiber = static_cast<Fiber*>(transfer.data);
|
||||
fiber->OnRewind(transfer);
|
||||
}
|
||||
|
||||
Fiber::Fiber(std::function<void(void*)>&& entry_point_func, void* start_parameter)
|
||||
: entry_point{std::move(entry_point_func)}, start_parameter{start_parameter} {
|
||||
impl = std::make_unique<FiberImpl>();
|
||||
: impl{std::make_unique<FiberImpl>()} {
|
||||
impl->entry_point = std::move(entry_point_func);
|
||||
impl->start_parameter = start_parameter;
|
||||
impl->stack_limit = impl->stack.data();
|
||||
impl->rewind_stack_limit = impl->rewind_stack.data();
|
||||
u8* stack_base = impl->stack_limit + default_stack_size;
|
||||
@@ -161,37 +178,31 @@ Fiber::Fiber(std::function<void(void*)>&& entry_point_func, void* start_paramete
|
||||
boost::context::detail::make_fcontext(stack_base, impl->stack.size(), FiberStartFunc);
|
||||
}
|
||||
|
||||
void Fiber::SetRewindPoint(std::function<void(void*)>&& rewind_func, void* start_parameter) {
|
||||
rewind_point = std::move(rewind_func);
|
||||
rewind_parameter = start_parameter;
|
||||
}
|
||||
|
||||
Fiber::Fiber() : impl{std::make_unique<FiberImpl>()} {}
|
||||
|
||||
Fiber::~Fiber() {
|
||||
if (released) {
|
||||
if (impl->released) {
|
||||
return;
|
||||
}
|
||||
// Make sure the Fiber is not being used
|
||||
const bool locked = guard.try_lock();
|
||||
const bool locked = impl->guard.try_lock();
|
||||
ASSERT_MSG(locked, "Destroying a fiber that's still running");
|
||||
if (locked) {
|
||||
guard.unlock();
|
||||
impl->guard.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void Fiber::Exit() {
|
||||
|
||||
ASSERT_MSG(is_thread_fiber, "Exitting non main thread fiber");
|
||||
if (!is_thread_fiber) {
|
||||
ASSERT_MSG(impl->is_thread_fiber, "Exitting non main thread fiber");
|
||||
if (!impl->is_thread_fiber) {
|
||||
return;
|
||||
}
|
||||
guard.unlock();
|
||||
released = true;
|
||||
impl->guard.unlock();
|
||||
impl->released = true;
|
||||
}
|
||||
|
||||
void Fiber::Rewind() {
|
||||
ASSERT(rewind_point);
|
||||
ASSERT(impl->rewind_point);
|
||||
ASSERT(impl->rewind_context == nullptr);
|
||||
u8* stack_base = impl->rewind_stack_limit + default_stack_size;
|
||||
impl->rewind_context =
|
||||
@@ -199,22 +210,22 @@ void Fiber::Rewind() {
|
||||
boost::context::detail::jump_fcontext(impl->rewind_context, this);
|
||||
}
|
||||
|
||||
void Fiber::YieldTo(std::shared_ptr<Fiber>& from, std::shared_ptr<Fiber>& to) {
|
||||
void Fiber::YieldTo(std::shared_ptr<Fiber> from, std::shared_ptr<Fiber> to) {
|
||||
ASSERT_MSG(from != nullptr, "Yielding fiber is null!");
|
||||
ASSERT_MSG(to != nullptr, "Next fiber is null!");
|
||||
to->guard.lock();
|
||||
to->previous_fiber = from;
|
||||
to->impl->guard.lock();
|
||||
to->impl->previous_fiber = from;
|
||||
auto transfer = boost::context::detail::jump_fcontext(to->impl->context, to.get());
|
||||
ASSERT(from->previous_fiber != nullptr);
|
||||
from->previous_fiber->impl->context = transfer.fctx;
|
||||
from->previous_fiber->guard.unlock();
|
||||
from->previous_fiber.reset();
|
||||
ASSERT(from->impl->previous_fiber != nullptr);
|
||||
from->impl->previous_fiber->impl->context = transfer.fctx;
|
||||
from->impl->previous_fiber->impl->guard.unlock();
|
||||
from->impl->previous_fiber.reset();
|
||||
}
|
||||
|
||||
std::shared_ptr<Fiber> Fiber::ThreadToFiber() {
|
||||
std::shared_ptr<Fiber> fiber = std::shared_ptr<Fiber>{new Fiber()};
|
||||
fiber->guard.lock();
|
||||
fiber->is_thread_fiber = true;
|
||||
fiber->impl->guard.lock();
|
||||
fiber->impl->is_thread_fiber = true;
|
||||
return fiber;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,9 +7,6 @@
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/spin_lock.h"
|
||||
|
||||
#if !defined(_WIN32) && !defined(WIN32)
|
||||
namespace boost::context::detail {
|
||||
struct transfer_t;
|
||||
@@ -46,10 +43,10 @@ public:
|
||||
|
||||
/// Yields control from Fiber 'from' to Fiber 'to'
|
||||
/// Fiber 'from' must be the currently running fiber.
|
||||
static void YieldTo(std::shared_ptr<Fiber>& from, std::shared_ptr<Fiber>& to);
|
||||
static void YieldTo(std::shared_ptr<Fiber> from, std::shared_ptr<Fiber> to);
|
||||
[[nodiscard]] static std::shared_ptr<Fiber> ThreadToFiber();
|
||||
|
||||
void SetRewindPoint(std::function<void(void*)>&& rewind_func, void* start_parameter);
|
||||
void SetRewindPoint(std::function<void(void*)>&& rewind_func, void* rewind_param);
|
||||
|
||||
void Rewind();
|
||||
|
||||
@@ -57,9 +54,7 @@ public:
|
||||
void Exit();
|
||||
|
||||
/// Changes the start parameter of the fiber. Has no effect if the fiber already started
|
||||
void SetStartParameter(void* new_parameter) {
|
||||
start_parameter = new_parameter;
|
||||
}
|
||||
void SetStartParameter(void* new_parameter);
|
||||
|
||||
private:
|
||||
Fiber();
|
||||
@@ -77,16 +72,7 @@ private:
|
||||
#endif
|
||||
|
||||
struct FiberImpl;
|
||||
|
||||
SpinLock guard{};
|
||||
std::function<void(void*)> entry_point;
|
||||
std::function<void(void*)> rewind_point;
|
||||
void* rewind_parameter{};
|
||||
void* start_parameter{};
|
||||
std::shared_ptr<Fiber> previous_fiber;
|
||||
std::unique_ptr<FiberImpl> impl;
|
||||
bool is_thread_fiber{};
|
||||
bool released{};
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -472,13 +472,14 @@ u64 ScanDirectoryTree(const std::string& directory, FSTEntry& parent_entry,
|
||||
}
|
||||
|
||||
bool DeleteDirRecursively(const std::string& directory, unsigned int recursion) {
|
||||
const auto callback = [recursion](u64* num_entries_out, const std::string& directory,
|
||||
const std::string& virtual_name) -> bool {
|
||||
std::string new_path = directory + DIR_SEP_CHR + virtual_name;
|
||||
const auto callback = [recursion](u64*, const std::string& directory,
|
||||
const std::string& virtual_name) {
|
||||
const std::string new_path = directory + DIR_SEP_CHR + virtual_name;
|
||||
|
||||
if (IsDirectory(new_path)) {
|
||||
if (recursion == 0)
|
||||
if (recursion == 0) {
|
||||
return false;
|
||||
}
|
||||
return DeleteDirRecursively(new_path, recursion - 1);
|
||||
}
|
||||
return Delete(new_path);
|
||||
@@ -492,7 +493,8 @@ bool DeleteDirRecursively(const std::string& directory, unsigned int recursion)
|
||||
return true;
|
||||
}
|
||||
|
||||
void CopyDir(const std::string& source_path, const std::string& dest_path) {
|
||||
void CopyDir([[maybe_unused]] const std::string& source_path,
|
||||
[[maybe_unused]] const std::string& dest_path) {
|
||||
#ifndef _WIN32
|
||||
if (source_path == dest_path) {
|
||||
return;
|
||||
@@ -553,7 +555,7 @@ std::optional<std::string> GetCurrentDir() {
|
||||
std::string strDir = dir;
|
||||
#endif
|
||||
free(dir);
|
||||
return std::move(strDir);
|
||||
return strDir;
|
||||
}
|
||||
|
||||
bool SetCurrentDir(const std::string& directory) {
|
||||
@@ -772,21 +774,23 @@ std::size_t ReadFileToString(bool text_file, const std::string& filename, std::s
|
||||
|
||||
void SplitFilename83(const std::string& filename, std::array<char, 9>& short_name,
|
||||
std::array<char, 4>& extension) {
|
||||
const std::string forbidden_characters = ".\"/\\[]:;=, ";
|
||||
static constexpr std::string_view forbidden_characters = ".\"/\\[]:;=, ";
|
||||
|
||||
// On a FAT32 partition, 8.3 names are stored as a 11 bytes array, filled with spaces.
|
||||
short_name = {{' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '\0'}};
|
||||
extension = {{' ', ' ', ' ', '\0'}};
|
||||
|
||||
std::string::size_type point = filename.rfind('.');
|
||||
if (point == filename.size() - 1)
|
||||
auto point = filename.rfind('.');
|
||||
if (point == filename.size() - 1) {
|
||||
point = filename.rfind('.', point);
|
||||
}
|
||||
|
||||
// Get short name.
|
||||
int j = 0;
|
||||
for (char letter : filename.substr(0, point)) {
|
||||
if (forbidden_characters.find(letter, 0) != std::string::npos)
|
||||
if (forbidden_characters.find(letter, 0) != std::string::npos) {
|
||||
continue;
|
||||
}
|
||||
if (j == 8) {
|
||||
// TODO(Link Mauve): also do that for filenames containing a space.
|
||||
// TODO(Link Mauve): handle multiple files having the same short name.
|
||||
@@ -794,14 +798,15 @@ void SplitFilename83(const std::string& filename, std::array<char, 9>& short_nam
|
||||
short_name[7] = '1';
|
||||
break;
|
||||
}
|
||||
short_name[j++] = toupper(letter);
|
||||
short_name[j++] = static_cast<char>(std::toupper(letter));
|
||||
}
|
||||
|
||||
// Get extension.
|
||||
if (point != std::string::npos) {
|
||||
j = 0;
|
||||
for (char letter : filename.substr(point + 1, 3))
|
||||
extension[j++] = toupper(letter);
|
||||
for (char letter : filename.substr(point + 1, 3)) {
|
||||
extension[j++] = static_cast<char>(std::toupper(letter));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -232,7 +232,7 @@ public:
|
||||
|
||||
void Swap(IOFile& other) noexcept;
|
||||
|
||||
[[nodiscard]] bool Open(const std::string& filename, const char openmode[], int flags = 0);
|
||||
bool Open(const std::string& filename, const char openmode[], int flags = 0);
|
||||
bool Close();
|
||||
|
||||
template <typename T>
|
||||
|
||||
@@ -274,7 +274,6 @@ const char* GetLogClassName(Class log_class) {
|
||||
case Class::Count:
|
||||
break;
|
||||
}
|
||||
UNREACHABLE();
|
||||
return "Invalid";
|
||||
}
|
||||
|
||||
@@ -293,7 +292,6 @@ const char* GetLevelName(Level log_level) {
|
||||
break;
|
||||
}
|
||||
#undef LVL
|
||||
UNREACHABLE();
|
||||
return "Invalid";
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ struct Rectangle {
|
||||
if constexpr (std::is_floating_point_v<T>) {
|
||||
return std::abs(right - left);
|
||||
} else {
|
||||
return std::abs(static_cast<std::make_signed_t<T>>(right - left));
|
||||
return static_cast<T>(std::abs(static_cast<std::make_signed_t<T>>(right - left)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ struct Rectangle {
|
||||
if constexpr (std::is_floating_point_v<T>) {
|
||||
return std::abs(bottom - top);
|
||||
} else {
|
||||
return std::abs(static_cast<std::make_signed_t<T>>(bottom - top));
|
||||
return static_cast<T>(std::abs(static_cast<std::make_signed_t<T>>(bottom - top)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,16 +16,23 @@
|
||||
// Call directly after the command or use the error num.
|
||||
// This function might change the error code.
|
||||
std::string GetLastErrorMsg() {
|
||||
static const std::size_t buff_size = 255;
|
||||
static constexpr std::size_t buff_size = 255;
|
||||
char err_str[buff_size];
|
||||
|
||||
#ifdef _WIN32
|
||||
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, GetLastError(),
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), err_str, buff_size, nullptr);
|
||||
return std::string(err_str, buff_size);
|
||||
#elif defined(__GLIBC__) && (_GNU_SOURCE || (_POSIX_C_SOURCE < 200112L && _XOPEN_SOURCE < 600))
|
||||
// Thread safe (GNU-specific)
|
||||
const char* str = strerror_r(errno, err_str, buff_size);
|
||||
return std::string(str);
|
||||
#else
|
||||
// Thread safe (XSI-compliant)
|
||||
strerror_r(errno, err_str, buff_size);
|
||||
const int success = strerror_r(errno, err_str, buff_size);
|
||||
if (success != 0) {
|
||||
return {};
|
||||
}
|
||||
return std::string(err_str);
|
||||
#endif
|
||||
|
||||
return std::string(err_str, buff_size);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace Common {
|
||||
|
||||
PageTable::PageTable() = default;
|
||||
|
||||
PageTable::~PageTable() = default;
|
||||
PageTable::~PageTable() noexcept = default;
|
||||
|
||||
void PageTable::Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits,
|
||||
bool has_attribute) {
|
||||
|
||||
@@ -4,9 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <boost/icl/interval_map.hpp>
|
||||
#include <tuple>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/memory_hook.h"
|
||||
@@ -51,13 +49,21 @@ struct SpecialRegion {
|
||||
*/
|
||||
struct PageTable {
|
||||
PageTable();
|
||||
~PageTable();
|
||||
~PageTable() noexcept;
|
||||
|
||||
PageTable(const PageTable&) = delete;
|
||||
PageTable& operator=(const PageTable&) = delete;
|
||||
|
||||
PageTable(PageTable&&) noexcept = default;
|
||||
PageTable& operator=(PageTable&&) noexcept = default;
|
||||
|
||||
/**
|
||||
* Resizes the page table to be able to accomodate enough pages within
|
||||
* a given address space.
|
||||
*
|
||||
* @param address_space_width_in_bits The address size width in bits.
|
||||
* @param page_size_in_bits The page size in bits.
|
||||
* @param has_attribute Whether or not this page has any backing attributes.
|
||||
*/
|
||||
void Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits,
|
||||
bool has_attribute);
|
||||
|
||||
@@ -15,6 +15,14 @@ namespace Common {
|
||||
*/
|
||||
class SpinLock {
|
||||
public:
|
||||
SpinLock() = default;
|
||||
|
||||
SpinLock(const SpinLock&) = delete;
|
||||
SpinLock& operator=(const SpinLock&) = delete;
|
||||
|
||||
SpinLock(SpinLock&&) = delete;
|
||||
SpinLock& operator=(SpinLock&&) = delete;
|
||||
|
||||
void lock();
|
||||
void unlock();
|
||||
[[nodiscard]] bool try_lock();
|
||||
|
||||
47
src/common/stream.cpp
Normal file
47
src/common/stream.cpp
Normal file
@@ -0,0 +1,47 @@
|
||||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <stdexcept>
|
||||
#include "common/common_types.h"
|
||||
#include "common/stream.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
Stream::Stream() = default;
|
||||
Stream::~Stream() = default;
|
||||
|
||||
void Stream::Seek(s32 offset, SeekOrigin origin) {
|
||||
if (origin == SeekOrigin::SetOrigin) {
|
||||
if (offset < 0) {
|
||||
position = 0;
|
||||
} else if (position >= buffer.size()) {
|
||||
position = buffer.size();
|
||||
} else {
|
||||
position = offset;
|
||||
}
|
||||
} else if (origin == SeekOrigin::FromCurrentPos) {
|
||||
Seek(static_cast<s32>(position) + offset, SeekOrigin::SetOrigin);
|
||||
} else if (origin == SeekOrigin::FromEnd) {
|
||||
Seek(static_cast<s32>(buffer.size()) - offset, SeekOrigin::SetOrigin);
|
||||
}
|
||||
}
|
||||
|
||||
u8 Stream::ReadByte() {
|
||||
if (position < buffer.size()) {
|
||||
return buffer[position++];
|
||||
} else {
|
||||
throw std::out_of_range("Attempting to read a byte not within the buffer range");
|
||||
}
|
||||
}
|
||||
|
||||
void Stream::WriteByte(u8 byte) {
|
||||
if (position == buffer.size()) {
|
||||
buffer.push_back(byte);
|
||||
position++;
|
||||
} else {
|
||||
buffer.insert(buffer.begin() + position, byte);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
56
src/common/stream.h
Normal file
56
src/common/stream.h
Normal file
@@ -0,0 +1,56 @@
|
||||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
enum class SeekOrigin {
|
||||
SetOrigin,
|
||||
FromCurrentPos,
|
||||
FromEnd,
|
||||
};
|
||||
|
||||
class Stream {
|
||||
public:
|
||||
/// Stream creates a bitstream and provides common functionality on the stream.
|
||||
explicit Stream();
|
||||
~Stream();
|
||||
|
||||
Stream(const Stream&) = delete;
|
||||
Stream& operator=(const Stream&) = delete;
|
||||
|
||||
Stream(Stream&&) = default;
|
||||
Stream& operator=(Stream&&) = default;
|
||||
|
||||
/// Reposition bitstream "cursor" to the specified offset from origin
|
||||
void Seek(s32 offset, SeekOrigin origin);
|
||||
|
||||
/// Reads next byte in the stream buffer and increments position
|
||||
u8 ReadByte();
|
||||
|
||||
/// Writes byte at current position
|
||||
void WriteByte(u8 byte);
|
||||
|
||||
[[nodiscard]] std::size_t GetPosition() const {
|
||||
return position;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<u8>& GetBuffer() {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::vector<u8>& GetBuffer() const {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<u8> buffer;
|
||||
std::size_t position{0};
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <cstdlib>
|
||||
#include <locale>
|
||||
#include <sstream>
|
||||
|
||||
#include "common/common_paths.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/string_util.h"
|
||||
@@ -21,14 +22,14 @@ namespace Common {
|
||||
/// Make a string lowercase
|
||||
std::string ToLower(std::string str) {
|
||||
std::transform(str.begin(), str.end(), str.begin(),
|
||||
[](unsigned char c) { return std::tolower(c); });
|
||||
[](unsigned char c) { return static_cast<char>(std::tolower(c)); });
|
||||
return str;
|
||||
}
|
||||
|
||||
/// Make a string uppercase
|
||||
std::string ToUpper(std::string str) {
|
||||
std::transform(str.begin(), str.end(), str.begin(),
|
||||
[](unsigned char c) { return std::toupper(c); });
|
||||
[](unsigned char c) { return static_cast<char>(std::toupper(c)); });
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
@@ -142,20 +142,18 @@ std::string Timer::GetTimeFormatted() {
|
||||
// ----------------
|
||||
double Timer::GetDoubleTime() {
|
||||
// Get continuous timestamp
|
||||
u64 TmpSeconds = static_cast<u64>(Common::Timer::GetTimeSinceJan1970().count());
|
||||
double ms = static_cast<u64>(GetTimeMs().count()) % 1000;
|
||||
auto tmp_seconds = static_cast<u64>(GetTimeSinceJan1970().count());
|
||||
const auto ms = static_cast<double>(static_cast<u64>(GetTimeMs().count()) % 1000);
|
||||
|
||||
// Remove a few years. We only really want enough seconds to make
|
||||
// sure that we are detecting actual actions, perhaps 60 seconds is
|
||||
// enough really, but I leave a year of seconds anyway, in case the
|
||||
// user's clock is incorrect or something like that.
|
||||
TmpSeconds = TmpSeconds - (38 * 365 * 24 * 60 * 60);
|
||||
tmp_seconds = tmp_seconds - (38 * 365 * 24 * 60 * 60);
|
||||
|
||||
// Make a smaller integer that fits in the double
|
||||
u32 Seconds = static_cast<u32>(TmpSeconds);
|
||||
double TmpTime = Seconds + ms;
|
||||
|
||||
return TmpTime;
|
||||
const auto seconds = static_cast<u32>(tmp_seconds);
|
||||
return seconds + ms;
|
||||
}
|
||||
|
||||
} // Namespace Common
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
namespace Common {
|
||||
|
||||
void* AllocateMemoryPages(std::size_t size) {
|
||||
void* AllocateMemoryPages(std::size_t size) noexcept {
|
||||
#ifdef _WIN32
|
||||
void* base{VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE)};
|
||||
#else
|
||||
@@ -29,7 +29,7 @@ void* AllocateMemoryPages(std::size_t size) {
|
||||
return base;
|
||||
}
|
||||
|
||||
void FreeMemoryPages(void* base, [[maybe_unused]] std::size_t size) {
|
||||
void FreeMemoryPages(void* base, [[maybe_unused]] std::size_t size) noexcept {
|
||||
if (!base) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -4,25 +4,44 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace Common {
|
||||
|
||||
void* AllocateMemoryPages(std::size_t size);
|
||||
void FreeMemoryPages(void* base, std::size_t size);
|
||||
void* AllocateMemoryPages(std::size_t size) noexcept;
|
||||
void FreeMemoryPages(void* base, std::size_t size) noexcept;
|
||||
|
||||
template <typename T>
|
||||
class VirtualBuffer final : NonCopyable {
|
||||
class VirtualBuffer final {
|
||||
public:
|
||||
static_assert(
|
||||
std::is_trivially_constructible_v<T>,
|
||||
"T must be trivially constructible, as non-trivial constructors will not be executed "
|
||||
"with the current allocator");
|
||||
|
||||
constexpr VirtualBuffer() = default;
|
||||
explicit VirtualBuffer(std::size_t count) : alloc_size{count * sizeof(T)} {
|
||||
base_ptr = reinterpret_cast<T*>(AllocateMemoryPages(alloc_size));
|
||||
}
|
||||
|
||||
~VirtualBuffer() {
|
||||
~VirtualBuffer() noexcept {
|
||||
FreeMemoryPages(base_ptr, alloc_size);
|
||||
}
|
||||
|
||||
VirtualBuffer(const VirtualBuffer&) = delete;
|
||||
VirtualBuffer& operator=(const VirtualBuffer&) = delete;
|
||||
|
||||
VirtualBuffer(VirtualBuffer&& other) noexcept
|
||||
: alloc_size{std::exchange(other.alloc_size, 0)}, base_ptr{std::exchange(other.base_ptr),
|
||||
nullptr} {}
|
||||
|
||||
VirtualBuffer& operator=(VirtualBuffer&& other) noexcept {
|
||||
alloc_size = std::exchange(other.alloc_size, 0);
|
||||
base_ptr = std::exchange(other.base_ptr, nullptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void resize(std::size_t count) {
|
||||
FreeMemoryPages(base_ptr, alloc_size);
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ public:
|
||||
return Common::Divide128On32(temporary, 1000000000).first;
|
||||
}
|
||||
|
||||
void Pause(bool is_paused) override {
|
||||
void Pause([[maybe_unused]] bool is_paused) override {
|
||||
// Do nothing in this clock type.
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ private:
|
||||
/// value used to reduce the native clocks accuracy as some apss rely on
|
||||
/// undefined behavior where the level of accuracy in the clock shouldn't
|
||||
/// be higher.
|
||||
static constexpr u64 inaccuracy_mask = ~(0x400 - 1);
|
||||
static constexpr u64 inaccuracy_mask = ~(UINT64_C(0x400) - 1);
|
||||
|
||||
SpinLock rtsc_serialize{};
|
||||
u64 last_measure{};
|
||||
|
||||
@@ -1,9 +1,3 @@
|
||||
if (YUZU_ENABLE_BOXCAT)
|
||||
set(BCAT_BOXCAT_ADDITIONAL_SOURCES hle/service/bcat/backend/boxcat.cpp hle/service/bcat/backend/boxcat.h)
|
||||
else()
|
||||
set(BCAT_BOXCAT_ADDITIONAL_SOURCES)
|
||||
endif()
|
||||
|
||||
add_library(core STATIC
|
||||
arm/arm_interface.h
|
||||
arm/arm_interface.cpp
|
||||
@@ -19,8 +13,6 @@ add_library(core STATIC
|
||||
arm/dynarmic/arm_exclusive_monitor.h
|
||||
arm/exclusive_monitor.cpp
|
||||
arm/exclusive_monitor.h
|
||||
arm/unicorn/arm_unicorn.cpp
|
||||
arm/unicorn/arm_unicorn.h
|
||||
constants.cpp
|
||||
constants.h
|
||||
core.cpp
|
||||
@@ -303,7 +295,6 @@ add_library(core STATIC
|
||||
hle/service/audio/hwopus.h
|
||||
hle/service/bcat/backend/backend.cpp
|
||||
hle/service/bcat/backend/backend.h
|
||||
${BCAT_BOXCAT_ADDITIONAL_SOURCES}
|
||||
hle/service/bcat/bcat.cpp
|
||||
hle/service/bcat/bcat.h
|
||||
hle/service/bcat/module.cpp
|
||||
@@ -446,6 +437,8 @@ add_library(core STATIC
|
||||
hle/service/nvdrv/devices/nvhost_gpu.h
|
||||
hle/service/nvdrv/devices/nvhost_nvdec.cpp
|
||||
hle/service/nvdrv/devices/nvhost_nvdec.h
|
||||
hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
|
||||
hle/service/nvdrv/devices/nvhost_nvdec_common.h
|
||||
hle/service/nvdrv/devices/nvhost_nvjpg.cpp
|
||||
hle/service/nvdrv/devices/nvhost_nvjpg.h
|
||||
hle/service/nvdrv/devices/nvhost_vic.cpp
|
||||
@@ -459,6 +452,8 @@ add_library(core STATIC
|
||||
hle/service/nvdrv/nvdrv.h
|
||||
hle/service/nvdrv/nvmemp.cpp
|
||||
hle/service/nvdrv/nvmemp.h
|
||||
hle/service/nvdrv/syncpoint_manager.cpp
|
||||
hle/service/nvdrv/syncpoint_manager.h
|
||||
hle/service/nvflinger/buffer_queue.cpp
|
||||
hle/service/nvflinger/buffer_queue.h
|
||||
hle/service/nvflinger/nvflinger.cpp
|
||||
@@ -608,6 +603,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
|
||||
# 'expression' : signed/unsigned mismatch
|
||||
@@ -630,16 +632,19 @@ else()
|
||||
-Werror=implicit-fallthrough
|
||||
-Werror=reorder
|
||||
-Werror=sign-compare
|
||||
-Werror=unused-but-set-parameter
|
||||
-Werror=unused-but-set-variable
|
||||
-Werror=unused-variable
|
||||
|
||||
$<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter>
|
||||
$<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable>
|
||||
|
||||
-Wno-sign-conversion
|
||||
)
|
||||
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 unicorn zip)
|
||||
target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls opus zip)
|
||||
|
||||
if (YUZU_ENABLE_BOXCAT)
|
||||
target_compile_definitions(core PRIVATE -DYUZU_ENABLE_BOXCAT)
|
||||
|
||||
@@ -147,10 +147,18 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktraceFromContex
|
||||
auto fp = ctx.cpu_registers[29];
|
||||
auto lr = ctx.cpu_registers[30];
|
||||
while (true) {
|
||||
out.push_back({"", 0, lr, 0});
|
||||
if (!fp) {
|
||||
out.push_back({
|
||||
.module = "",
|
||||
.address = 0,
|
||||
.original_address = lr,
|
||||
.offset = 0,
|
||||
.name = {},
|
||||
});
|
||||
|
||||
if (fp == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
lr = memory.Read64(fp + 8) - 4;
|
||||
fp = memory.Read64(fp);
|
||||
}
|
||||
|
||||
@@ -21,8 +21,8 @@ public:
|
||||
CPUInterruptHandler(const CPUInterruptHandler&) = delete;
|
||||
CPUInterruptHandler& operator=(const CPUInterruptHandler&) = delete;
|
||||
|
||||
CPUInterruptHandler(CPUInterruptHandler&&) = default;
|
||||
CPUInterruptHandler& operator=(CPUInterruptHandler&&) = default;
|
||||
CPUInterruptHandler(CPUInterruptHandler&&) = delete;
|
||||
CPUInterruptHandler& operator=(CPUInterruptHandler&&) = delete;
|
||||
|
||||
bool IsInterrupted() const {
|
||||
return is_interrupted;
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <dynarmic/A32/a32.h>
|
||||
#include <dynarmic/A32/config.h>
|
||||
#include <dynarmic/A32/context.h>
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/page_table.h"
|
||||
#include "core/arm/cpu_interrupt_handler.h"
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <memory>
|
||||
#include <dynarmic/A64/a64.h>
|
||||
#include <dynarmic/A64/config.h>
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/page_table.h"
|
||||
#include "core/arm/cpu_interrupt_handler.h"
|
||||
@@ -13,7 +14,6 @@
|
||||
#include "core/arm/dynarmic/arm_exclusive_monitor.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/core_timing_util.h"
|
||||
#include "core/gdbstub/gdbstub.h"
|
||||
#include "core/hardware_properties.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
@@ -82,16 +82,9 @@ public:
|
||||
}
|
||||
|
||||
void InterpreterFallback(u64 pc, std::size_t num_instructions) override {
|
||||
LOG_INFO(Core_ARM, "Unicorn fallback @ 0x{:X} for {} instructions (instr = {:08X})", pc,
|
||||
num_instructions, MemoryReadCode(pc));
|
||||
|
||||
ARM_Interface::ThreadContext64 ctx;
|
||||
parent.SaveContext(ctx);
|
||||
parent.inner_unicorn.LoadContext(ctx);
|
||||
parent.inner_unicorn.ExecuteInstructions(num_instructions);
|
||||
parent.inner_unicorn.SaveContext(ctx);
|
||||
parent.LoadContext(ctx);
|
||||
num_interpreted_instructions += num_instructions;
|
||||
LOG_ERROR(Core_ARM,
|
||||
"Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc,
|
||||
num_instructions, MemoryReadCode(pc));
|
||||
}
|
||||
|
||||
void ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) override {
|
||||
@@ -127,18 +120,17 @@ public:
|
||||
if (parent.uses_wall_clock) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a
|
||||
// rough approximation of the amount of executed ticks in the system, it may be thrown off
|
||||
// if not all cores are doing a similar amount of work. Instead of doing this, we should
|
||||
// device a way so that timing is consistent across all cores without increasing the ticks 4
|
||||
// times.
|
||||
u64 amortized_ticks =
|
||||
(ticks - num_interpreted_instructions) / Core::Hardware::NUM_CPU_CORES;
|
||||
u64 amortized_ticks = ticks / Core::Hardware::NUM_CPU_CORES;
|
||||
// Always execute at least one tick.
|
||||
amortized_ticks = std::max<u64>(amortized_ticks, 1);
|
||||
|
||||
parent.system.CoreTiming().AddTicks(amortized_ticks);
|
||||
num_interpreted_instructions = 0;
|
||||
}
|
||||
|
||||
u64 GetTicksRemaining() override {
|
||||
@@ -156,7 +148,6 @@ public:
|
||||
}
|
||||
|
||||
ARM_Dynarmic_64& parent;
|
||||
std::size_t num_interpreted_instructions = 0;
|
||||
u64 tpidrro_el0 = 0;
|
||||
u64 tpidr_el0 = 0;
|
||||
static constexpr u64 minimum_run_cycles = 1000U;
|
||||
@@ -248,12 +239,8 @@ ARM_Dynarmic_64::ARM_Dynarmic_64(System& system, CPUInterrupts& interrupt_handle
|
||||
bool uses_wall_clock, ExclusiveMonitor& exclusive_monitor,
|
||||
std::size_t core_index)
|
||||
: ARM_Interface{system, interrupt_handlers, uses_wall_clock},
|
||||
cb(std::make_unique<DynarmicCallbacks64>(*this)), inner_unicorn{system, interrupt_handlers,
|
||||
uses_wall_clock,
|
||||
ARM_Unicorn::Arch::AArch64,
|
||||
core_index},
|
||||
core_index{core_index}, exclusive_monitor{
|
||||
dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {}
|
||||
cb(std::make_unique<DynarmicCallbacks64>(*this)), core_index{core_index},
|
||||
exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {}
|
||||
|
||||
ARM_Dynarmic_64::~ARM_Dynarmic_64() = default;
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#include "common/hash.h"
|
||||
#include "core/arm/arm_interface.h"
|
||||
#include "core/arm/exclusive_monitor.h"
|
||||
#include "core/arm/unicorn/arm_unicorn.h"
|
||||
|
||||
namespace Core::Memory {
|
||||
class Memory;
|
||||
@@ -71,7 +70,6 @@ private:
|
||||
std::unique_ptr<DynarmicCallbacks64> cb;
|
||||
JitCacheType jit_cache;
|
||||
std::shared_ptr<Dynarmic::A64::Jit> jit;
|
||||
ARM_Unicorn inner_unicorn;
|
||||
|
||||
std::size_t core_index;
|
||||
DynarmicExclusiveMonitor& exclusive_monitor;
|
||||
|
||||
@@ -1,295 +0,0 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <unicorn/arm64.h>
|
||||
#include "common/assert.h"
|
||||
#include "common/microprofile.h"
|
||||
#include "core/arm/cpu_interrupt_handler.h"
|
||||
#include "core/arm/unicorn/arm_unicorn.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/kernel/scheduler.h"
|
||||
#include "core/hle/kernel/svc.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
// Load Unicorn DLL once on Windows using RAII
|
||||
#ifdef _MSC_VER
|
||||
#include <unicorn_dynload.h>
|
||||
struct LoadDll {
|
||||
private:
|
||||
LoadDll() {
|
||||
ASSERT(uc_dyn_load(NULL, 0));
|
||||
}
|
||||
~LoadDll() {
|
||||
ASSERT(uc_dyn_free());
|
||||
}
|
||||
static LoadDll g_load_dll;
|
||||
};
|
||||
LoadDll LoadDll::g_load_dll;
|
||||
#endif
|
||||
|
||||
#define CHECKED(expr) \
|
||||
do { \
|
||||
if (auto _cerr = (expr)) { \
|
||||
ASSERT_MSG(false, "Call " #expr " failed with error: {} ({})\n", _cerr, \
|
||||
uc_strerror(_cerr)); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static void CodeHook(uc_engine* uc, uint64_t address, uint32_t size, void* user_data) {
|
||||
GDBStub::BreakpointAddress bkpt =
|
||||
GDBStub::GetNextBreakpointFromAddress(address, GDBStub::BreakpointType::Execute);
|
||||
if (GDBStub::IsMemoryBreak() ||
|
||||
(bkpt.type != GDBStub::BreakpointType::None && address == bkpt.address)) {
|
||||
auto core = static_cast<ARM_Unicorn*>(user_data);
|
||||
core->RecordBreak(bkpt);
|
||||
uc_emu_stop(uc);
|
||||
}
|
||||
}
|
||||
|
||||
static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int size, u64 value,
|
||||
void* user_data) {
|
||||
auto* const system = static_cast<System*>(user_data);
|
||||
|
||||
ARM_Interface::ThreadContext64 ctx{};
|
||||
system->CurrentArmInterface().SaveContext(ctx);
|
||||
ASSERT_MSG(false, "Attempted to read from unmapped memory: 0x{:X}, pc=0x{:X}, lr=0x{:X}", addr,
|
||||
ctx.pc, ctx.cpu_registers[30]);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
ARM_Unicorn::ARM_Unicorn(System& system, CPUInterrupts& interrupt_handlers, bool uses_wall_clock,
|
||||
Arch architecture, std::size_t core_index)
|
||||
: ARM_Interface{system, interrupt_handlers, uses_wall_clock}, core_index{core_index} {
|
||||
const auto arch = architecture == Arch::AArch32 ? UC_ARCH_ARM : UC_ARCH_ARM64;
|
||||
CHECKED(uc_open(arch, UC_MODE_ARM, &uc));
|
||||
|
||||
auto fpv = 3 << 20;
|
||||
CHECKED(uc_reg_write(uc, UC_ARM64_REG_CPACR_EL1, &fpv));
|
||||
|
||||
uc_hook hook{};
|
||||
CHECKED(uc_hook_add(uc, &hook, UC_HOOK_INTR, (void*)InterruptHook, this, 0, UINT64_MAX));
|
||||
CHECKED(uc_hook_add(uc, &hook, UC_HOOK_MEM_INVALID, (void*)UnmappedMemoryHook, &system, 0,
|
||||
UINT64_MAX));
|
||||
if (GDBStub::IsServerEnabled()) {
|
||||
CHECKED(uc_hook_add(uc, &hook, UC_HOOK_CODE, (void*)CodeHook, this, 0, UINT64_MAX));
|
||||
last_bkpt_hit = false;
|
||||
}
|
||||
}
|
||||
|
||||
ARM_Unicorn::~ARM_Unicorn() {
|
||||
CHECKED(uc_close(uc));
|
||||
}
|
||||
|
||||
void ARM_Unicorn::SetPC(u64 pc) {
|
||||
CHECKED(uc_reg_write(uc, UC_ARM64_REG_PC, &pc));
|
||||
}
|
||||
|
||||
u64 ARM_Unicorn::GetPC() const {
|
||||
u64 val{};
|
||||
CHECKED(uc_reg_read(uc, UC_ARM64_REG_PC, &val));
|
||||
return val;
|
||||
}
|
||||
|
||||
u64 ARM_Unicorn::GetReg(int regn) const {
|
||||
u64 val{};
|
||||
auto treg = UC_ARM64_REG_SP;
|
||||
if (regn <= 28) {
|
||||
treg = (uc_arm64_reg)(UC_ARM64_REG_X0 + regn);
|
||||
} else if (regn < 31) {
|
||||
treg = (uc_arm64_reg)(UC_ARM64_REG_X29 + regn - 29);
|
||||
}
|
||||
CHECKED(uc_reg_read(uc, treg, &val));
|
||||
return val;
|
||||
}
|
||||
|
||||
void ARM_Unicorn::SetReg(int regn, u64 val) {
|
||||
auto treg = UC_ARM64_REG_SP;
|
||||
if (regn <= 28) {
|
||||
treg = (uc_arm64_reg)(UC_ARM64_REG_X0 + regn);
|
||||
} else if (regn < 31) {
|
||||
treg = (uc_arm64_reg)(UC_ARM64_REG_X29 + regn - 29);
|
||||
}
|
||||
CHECKED(uc_reg_write(uc, treg, &val));
|
||||
}
|
||||
|
||||
u128 ARM_Unicorn::GetVectorReg(int /*index*/) const {
|
||||
UNIMPLEMENTED();
|
||||
static constexpr u128 res{};
|
||||
return res;
|
||||
}
|
||||
|
||||
void ARM_Unicorn::SetVectorReg(int /*index*/, u128 /*value*/) {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
u32 ARM_Unicorn::GetPSTATE() const {
|
||||
u64 nzcv{};
|
||||
CHECKED(uc_reg_read(uc, UC_ARM64_REG_NZCV, &nzcv));
|
||||
return static_cast<u32>(nzcv);
|
||||
}
|
||||
|
||||
void ARM_Unicorn::SetPSTATE(u32 pstate) {
|
||||
u64 nzcv = pstate;
|
||||
CHECKED(uc_reg_write(uc, UC_ARM64_REG_NZCV, &nzcv));
|
||||
}
|
||||
|
||||
VAddr ARM_Unicorn::GetTlsAddress() const {
|
||||
u64 base{};
|
||||
CHECKED(uc_reg_read(uc, UC_ARM64_REG_TPIDRRO_EL0, &base));
|
||||
return base;
|
||||
}
|
||||
|
||||
void ARM_Unicorn::SetTlsAddress(VAddr base) {
|
||||
CHECKED(uc_reg_write(uc, UC_ARM64_REG_TPIDRRO_EL0, &base));
|
||||
}
|
||||
|
||||
u64 ARM_Unicorn::GetTPIDR_EL0() const {
|
||||
u64 value{};
|
||||
CHECKED(uc_reg_read(uc, UC_ARM64_REG_TPIDR_EL0, &value));
|
||||
return value;
|
||||
}
|
||||
|
||||
void ARM_Unicorn::SetTPIDR_EL0(u64 value) {
|
||||
CHECKED(uc_reg_write(uc, UC_ARM64_REG_TPIDR_EL0, &value));
|
||||
}
|
||||
|
||||
void ARM_Unicorn::ChangeProcessorID(std::size_t new_core_id) {
|
||||
core_index = new_core_id;
|
||||
}
|
||||
|
||||
void ARM_Unicorn::Run() {
|
||||
if (GDBStub::IsServerEnabled()) {
|
||||
ExecuteInstructions(std::max(4000000U, 0U));
|
||||
} else {
|
||||
while (true) {
|
||||
if (interrupt_handlers[core_index].IsInterrupted()) {
|
||||
return;
|
||||
}
|
||||
ExecuteInstructions(10);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ARM_Unicorn::Step() {
|
||||
ExecuteInstructions(1);
|
||||
}
|
||||
|
||||
MICROPROFILE_DEFINE(ARM_Jit_Unicorn, "ARM JIT", "Unicorn", MP_RGB(255, 64, 64));
|
||||
|
||||
void ARM_Unicorn::ExecuteInstructions(std::size_t num_instructions) {
|
||||
MICROPROFILE_SCOPE(ARM_Jit_Unicorn);
|
||||
|
||||
// Temporarily map the code page for Unicorn
|
||||
u64 map_addr{GetPC() & ~Memory::PAGE_MASK};
|
||||
std::vector<u8> page_buffer(Memory::PAGE_SIZE);
|
||||
system.Memory().ReadBlock(map_addr, page_buffer.data(), page_buffer.size());
|
||||
|
||||
CHECKED(uc_mem_map_ptr(uc, map_addr, page_buffer.size(),
|
||||
UC_PROT_READ | UC_PROT_WRITE | UC_PROT_EXEC, page_buffer.data()));
|
||||
CHECKED(uc_emu_start(uc, GetPC(), 1ULL << 63, 0, num_instructions));
|
||||
CHECKED(uc_mem_unmap(uc, map_addr, page_buffer.size()));
|
||||
if (GDBStub::IsServerEnabled()) {
|
||||
if (last_bkpt_hit && last_bkpt.type == GDBStub::BreakpointType::Execute) {
|
||||
uc_reg_write(uc, UC_ARM64_REG_PC, &last_bkpt.address);
|
||||
}
|
||||
|
||||
Kernel::Thread* const thread = system.CurrentScheduler().GetCurrentThread();
|
||||
SaveContext(thread->GetContext64());
|
||||
if (last_bkpt_hit || GDBStub::IsMemoryBreak() || GDBStub::GetCpuStepFlag()) {
|
||||
last_bkpt_hit = false;
|
||||
GDBStub::Break();
|
||||
GDBStub::SendTrap(thread, 5);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ARM_Unicorn::SaveContext(ThreadContext64& ctx) {
|
||||
int uregs[32];
|
||||
void* tregs[32];
|
||||
|
||||
CHECKED(uc_reg_read(uc, UC_ARM64_REG_SP, &ctx.sp));
|
||||
CHECKED(uc_reg_read(uc, UC_ARM64_REG_PC, &ctx.pc));
|
||||
CHECKED(uc_reg_read(uc, UC_ARM64_REG_NZCV, &ctx.pstate));
|
||||
|
||||
for (auto i = 0; i < 29; ++i) {
|
||||
uregs[i] = UC_ARM64_REG_X0 + i;
|
||||
tregs[i] = &ctx.cpu_registers[i];
|
||||
}
|
||||
uregs[29] = UC_ARM64_REG_X29;
|
||||
tregs[29] = (void*)&ctx.cpu_registers[29];
|
||||
uregs[30] = UC_ARM64_REG_X30;
|
||||
tregs[30] = (void*)&ctx.cpu_registers[30];
|
||||
|
||||
CHECKED(uc_reg_read_batch(uc, uregs, tregs, 31));
|
||||
|
||||
for (int i = 0; i < 32; ++i) {
|
||||
uregs[i] = UC_ARM64_REG_Q0 + i;
|
||||
tregs[i] = &ctx.vector_registers[i];
|
||||
}
|
||||
|
||||
CHECKED(uc_reg_read_batch(uc, uregs, tregs, 32));
|
||||
}
|
||||
|
||||
void ARM_Unicorn::LoadContext(const ThreadContext64& ctx) {
|
||||
int uregs[32];
|
||||
void* tregs[32];
|
||||
|
||||
CHECKED(uc_reg_write(uc, UC_ARM64_REG_SP, &ctx.sp));
|
||||
CHECKED(uc_reg_write(uc, UC_ARM64_REG_PC, &ctx.pc));
|
||||
CHECKED(uc_reg_write(uc, UC_ARM64_REG_NZCV, &ctx.pstate));
|
||||
|
||||
for (int i = 0; i < 29; ++i) {
|
||||
uregs[i] = UC_ARM64_REG_X0 + i;
|
||||
tregs[i] = (void*)&ctx.cpu_registers[i];
|
||||
}
|
||||
uregs[29] = UC_ARM64_REG_X29;
|
||||
tregs[29] = (void*)&ctx.cpu_registers[29];
|
||||
uregs[30] = UC_ARM64_REG_X30;
|
||||
tregs[30] = (void*)&ctx.cpu_registers[30];
|
||||
|
||||
CHECKED(uc_reg_write_batch(uc, uregs, tregs, 31));
|
||||
|
||||
for (auto i = 0; i < 32; ++i) {
|
||||
uregs[i] = UC_ARM64_REG_Q0 + i;
|
||||
tregs[i] = (void*)&ctx.vector_registers[i];
|
||||
}
|
||||
|
||||
CHECKED(uc_reg_write_batch(uc, uregs, tregs, 32));
|
||||
}
|
||||
|
||||
void ARM_Unicorn::PrepareReschedule() {
|
||||
CHECKED(uc_emu_stop(uc));
|
||||
}
|
||||
|
||||
void ARM_Unicorn::ClearExclusiveState() {}
|
||||
|
||||
void ARM_Unicorn::ClearInstructionCache() {}
|
||||
|
||||
void ARM_Unicorn::RecordBreak(GDBStub::BreakpointAddress bkpt) {
|
||||
last_bkpt = bkpt;
|
||||
last_bkpt_hit = true;
|
||||
}
|
||||
|
||||
void ARM_Unicorn::InterruptHook(uc_engine* uc, u32 int_no, void* user_data) {
|
||||
u32 esr{};
|
||||
CHECKED(uc_reg_read(uc, UC_ARM64_REG_ESR, &esr));
|
||||
|
||||
const auto ec = esr >> 26;
|
||||
const auto iss = esr & 0xFFFFFF;
|
||||
|
||||
auto* const arm_instance = static_cast<ARM_Unicorn*>(user_data);
|
||||
|
||||
switch (ec) {
|
||||
case 0x15: // SVC
|
||||
Kernel::Svc::Call(arm_instance->system, iss);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Core
|
||||
@@ -1,63 +0,0 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <unicorn/unicorn.h>
|
||||
#include "common/common_types.h"
|
||||
#include "core/arm/arm_interface.h"
|
||||
#include "core/gdbstub/gdbstub.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
class System;
|
||||
|
||||
class ARM_Unicorn final : public ARM_Interface {
|
||||
public:
|
||||
enum class Arch {
|
||||
AArch32, // 32-bit ARM
|
||||
AArch64, // 64-bit ARM
|
||||
};
|
||||
|
||||
explicit ARM_Unicorn(System& system, CPUInterrupts& interrupt_handlers, bool uses_wall_clock,
|
||||
Arch architecture, std::size_t core_index);
|
||||
~ARM_Unicorn() override;
|
||||
|
||||
void SetPC(u64 pc) override;
|
||||
u64 GetPC() const override;
|
||||
u64 GetReg(int index) const override;
|
||||
void SetReg(int index, u64 value) override;
|
||||
u128 GetVectorReg(int index) const override;
|
||||
void SetVectorReg(int index, u128 value) override;
|
||||
u32 GetPSTATE() const override;
|
||||
void SetPSTATE(u32 pstate) override;
|
||||
VAddr GetTlsAddress() const override;
|
||||
void SetTlsAddress(VAddr address) override;
|
||||
void SetTPIDR_EL0(u64 value) override;
|
||||
u64 GetTPIDR_EL0() const override;
|
||||
void ChangeProcessorID(std::size_t new_core_id) override;
|
||||
void PrepareReschedule() override;
|
||||
void ClearExclusiveState() override;
|
||||
void ExecuteInstructions(std::size_t num_instructions);
|
||||
void Run() override;
|
||||
void Step() override;
|
||||
void ClearInstructionCache() override;
|
||||
void PageTableChanged(Common::PageTable&, std::size_t) override {}
|
||||
void RecordBreak(GDBStub::BreakpointAddress bkpt);
|
||||
|
||||
void SaveContext(ThreadContext32& ctx) override {}
|
||||
void SaveContext(ThreadContext64& ctx) override;
|
||||
void LoadContext(const ThreadContext32& ctx) override {}
|
||||
void LoadContext(const ThreadContext64& ctx) override;
|
||||
|
||||
private:
|
||||
static void InterruptHook(uc_engine* uc, u32 int_no, void* user_data);
|
||||
|
||||
uc_engine* uc{};
|
||||
GDBStub::BreakpointAddress last_bkpt{};
|
||||
bool last_bkpt_hit = false;
|
||||
std::size_t core_index;
|
||||
};
|
||||
|
||||
} // namespace Core
|
||||
@@ -40,6 +40,7 @@
|
||||
#include "core/hle/service/lm/manager.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
#include "core/hle/service/time/time_manager.h"
|
||||
#include "core/loader/loader.h"
|
||||
#include "core/memory.h"
|
||||
#include "core/memory/cheat_engine.h"
|
||||
@@ -121,7 +122,7 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
|
||||
struct System::Impl {
|
||||
explicit Impl(System& system)
|
||||
: kernel{system}, fs_controller{system}, memory{system},
|
||||
cpu_manager{system}, reporter{system}, applet_manager{system} {}
|
||||
cpu_manager{system}, reporter{system}, applet_manager{system}, time_manager{system} {}
|
||||
|
||||
ResultStatus Run() {
|
||||
status = ResultStatus::Success;
|
||||
@@ -178,16 +179,21 @@ struct System::Impl {
|
||||
arp_manager.ResetAll();
|
||||
|
||||
telemetry_session = std::make_unique<Core::TelemetrySession>();
|
||||
|
||||
gpu_core = VideoCore::CreateGPU(emu_window, system);
|
||||
if (!gpu_core) {
|
||||
return ResultStatus::ErrorVideoCore;
|
||||
}
|
||||
|
||||
service_manager = std::make_shared<Service::SM::ServiceManager>(kernel);
|
||||
|
||||
Service::Init(service_manager, system);
|
||||
GDBStub::DeferStart();
|
||||
|
||||
interrupt_manager = std::make_unique<Core::Hardware::InterruptManager>(system);
|
||||
gpu_core = VideoCore::CreateGPU(emu_window, system);
|
||||
if (!gpu_core) {
|
||||
return ResultStatus::ErrorVideoCore;
|
||||
}
|
||||
|
||||
// Initialize time manager, which must happen after kernel is created
|
||||
time_manager.Initialize();
|
||||
|
||||
is_powered_on = true;
|
||||
exit_lock = false;
|
||||
@@ -387,6 +393,7 @@ struct System::Impl {
|
||||
/// Service State
|
||||
Service::Glue::ARPManager arp_manager;
|
||||
Service::LM::Manager lm_manager{reporter};
|
||||
Service::Time::TimeManager time_manager;
|
||||
|
||||
/// Service manager
|
||||
std::shared_ptr<Service::SM::ServiceManager> service_manager;
|
||||
@@ -717,6 +724,14 @@ const Service::LM::Manager& System::GetLogManager() const {
|
||||
return impl->lm_manager;
|
||||
}
|
||||
|
||||
Service::Time::TimeManager& System::GetTimeManager() {
|
||||
return impl->time_manager;
|
||||
}
|
||||
|
||||
const Service::Time::TimeManager& System::GetTimeManager() const {
|
||||
return impl->time_manager;
|
||||
}
|
||||
|
||||
void System::SetExitLock(bool locked) {
|
||||
impl->exit_lock = locked;
|
||||
}
|
||||
|
||||
@@ -69,6 +69,10 @@ namespace SM {
|
||||
class ServiceManager;
|
||||
} // namespace SM
|
||||
|
||||
namespace Time {
|
||||
class TimeManager;
|
||||
} // namespace Time
|
||||
|
||||
} // namespace Service
|
||||
|
||||
namespace Tegra {
|
||||
@@ -361,6 +365,10 @@ public:
|
||||
|
||||
const Service::LM::Manager& GetLogManager() const;
|
||||
|
||||
Service::Time::TimeManager& GetTimeManager();
|
||||
|
||||
const Service::Time::TimeManager& GetTimeManager() const;
|
||||
|
||||
void SetExitLock(bool locked);
|
||||
|
||||
bool GetExitLock() const;
|
||||
|
||||
@@ -365,6 +365,8 @@ void CpuManager::RunThread(std::size_t core) {
|
||||
data.enter_barrier.reset();
|
||||
data.exit_barrier.reset();
|
||||
data.initialized = false;
|
||||
|
||||
MicroProfileOnThreadExit();
|
||||
}
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -19,7 +19,7 @@ DefaultControllerApplet::DefaultControllerApplet(Service::SM::ServiceManager& se
|
||||
DefaultControllerApplet::~DefaultControllerApplet() = default;
|
||||
|
||||
void DefaultControllerApplet::ReconfigureControllers(std::function<void()> callback,
|
||||
ControllerParameters parameters) const {
|
||||
const ControllerParameters& parameters) const {
|
||||
LOG_INFO(Service_HID, "called, deducing the best configuration based on the given parameters!");
|
||||
|
||||
auto& npad =
|
||||
@@ -27,19 +27,19 @@ void DefaultControllerApplet::ReconfigureControllers(std::function<void()> callb
|
||||
->GetAppletResource()
|
||||
->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad);
|
||||
|
||||
auto& players = Settings::values.players;
|
||||
auto& players = Settings::values.players.GetValue();
|
||||
|
||||
const std::size_t min_supported_players =
|
||||
parameters.enable_single_mode ? 1 : parameters.min_players;
|
||||
|
||||
// Disconnect Handheld first.
|
||||
npad.DisconnectNPadAtIndex(8);
|
||||
npad.DisconnectNpadAtIndex(8);
|
||||
|
||||
// Deduce the best configuration based on the input parameters.
|
||||
for (std::size_t index = 0; index < players.size() - 2; ++index) {
|
||||
// First, disconnect all controllers regardless of the value of keep_controllers_connected.
|
||||
// This makes it easy to connect the desired controllers.
|
||||
npad.DisconnectNPadAtIndex(index);
|
||||
npad.DisconnectNpadAtIndex(index);
|
||||
|
||||
// Only connect the minimum number of required players.
|
||||
if (index >= min_supported_players) {
|
||||
@@ -66,7 +66,7 @@ void DefaultControllerApplet::ReconfigureControllers(std::function<void()> callb
|
||||
npad.MapSettingsTypeToNPad(Settings::ControllerType::RightJoycon), index);
|
||||
}
|
||||
} else if (index == 0 && parameters.enable_single_mode && parameters.allow_handheld &&
|
||||
!Settings::values.use_docked_mode) {
|
||||
!Settings::values.use_docked_mode.GetValue()) {
|
||||
// We should *never* reach here under any normal circumstances.
|
||||
npad.AddNewControllerAt(npad.MapSettingsTypeToNPad(Settings::ControllerType::Handheld),
|
||||
index);
|
||||
|
||||
@@ -38,7 +38,7 @@ public:
|
||||
virtual ~ControllerApplet();
|
||||
|
||||
virtual void ReconfigureControllers(std::function<void()> callback,
|
||||
ControllerParameters parameters) const = 0;
|
||||
const ControllerParameters& parameters) const = 0;
|
||||
};
|
||||
|
||||
class DefaultControllerApplet final : public ControllerApplet {
|
||||
@@ -47,7 +47,7 @@ public:
|
||||
~DefaultControllerApplet() override;
|
||||
|
||||
void ReconfigureControllers(std::function<void()> callback,
|
||||
ControllerParameters parameters) const override;
|
||||
const ControllerParameters& parameters) const override;
|
||||
|
||||
private:
|
||||
Service::SM::ServiceManager& service_manager;
|
||||
|
||||
@@ -47,7 +47,7 @@ FramebufferLayout DefaultFrameLayout(u32 width, u32 height) {
|
||||
FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale) {
|
||||
u32 width, height;
|
||||
|
||||
if (Settings::values.use_docked_mode) {
|
||||
if (Settings::values.use_docked_mode.GetValue()) {
|
||||
width = ScreenDocked::Width * res_scale;
|
||||
height = ScreenDocked::Height * res_scale;
|
||||
} else {
|
||||
|
||||
@@ -33,7 +33,7 @@ public:
|
||||
virtual bool GetAnalogDirectionStatus(AnalogDirection direction) const {
|
||||
return {};
|
||||
}
|
||||
virtual bool SetRumblePlay(f32 amp_high, f32 amp_low, f32 freq_high, f32 freq_low) const {
|
||||
virtual bool SetRumblePlay(f32 amp_low, f32 freq_low, f32 amp_high, f32 freq_high) const {
|
||||
return {};
|
||||
}
|
||||
};
|
||||
@@ -121,6 +121,13 @@ using ButtonDevice = InputDevice<bool>;
|
||||
*/
|
||||
using AnalogDevice = InputDevice<std::tuple<float, float>>;
|
||||
|
||||
/**
|
||||
* A vibration device is an input device that returns an unsigned byte as status.
|
||||
* It represents whether the vibration device supports vibration or not.
|
||||
* If the status returns 1, it supports vibration. Otherwise, it does not support vibration.
|
||||
*/
|
||||
using VibrationDevice = InputDevice<u8>;
|
||||
|
||||
/**
|
||||
* A motion status is an object that returns a tuple of accelerometer state vector,
|
||||
* gyroscope state vector, rotation state vector and orientation state matrix.
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#include <utility>
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/ipc.h"
|
||||
#include "core/hle/kernel/client_port.h"
|
||||
#include "core/hle/kernel/client_session.h"
|
||||
@@ -73,14 +72,12 @@ public:
|
||||
AlwaysMoveHandles = 1,
|
||||
};
|
||||
|
||||
explicit ResponseBuilder(u32* command_buffer) : RequestHelperBase(command_buffer) {}
|
||||
|
||||
explicit ResponseBuilder(Kernel::HLERequestContext& context, u32 normal_params_size,
|
||||
u32 num_handles_to_copy = 0, u32 num_objects_to_move = 0,
|
||||
Flags flags = Flags::None)
|
||||
|
||||
: RequestHelperBase(context), normal_params_size(normal_params_size),
|
||||
num_handles_to_copy(num_handles_to_copy), num_objects_to_move(num_objects_to_move) {
|
||||
num_handles_to_copy(num_handles_to_copy),
|
||||
num_objects_to_move(num_objects_to_move), kernel{context.kernel} {
|
||||
|
||||
memset(cmdbuf, 0, sizeof(u32) * IPC::COMMAND_BUFFER_LENGTH);
|
||||
|
||||
@@ -140,7 +137,6 @@ public:
|
||||
if (context->Session()->IsDomain()) {
|
||||
context->AddDomainObject(std::move(iface));
|
||||
} else {
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
auto [client, server] = Kernel::Session::Create(kernel, iface->GetServiceName());
|
||||
context->AddMoveObject(std::move(client));
|
||||
iface->ClientConnected(std::move(server));
|
||||
@@ -214,6 +210,7 @@ private:
|
||||
u32 num_handles_to_copy{};
|
||||
u32 num_objects_to_move{}; ///< Domain objects or move handles, context dependent
|
||||
std::ptrdiff_t datapayload_index{};
|
||||
Kernel::KernelCore& kernel;
|
||||
};
|
||||
|
||||
/// Push ///
|
||||
|
||||
@@ -24,6 +24,10 @@ namespace Core::Memory {
|
||||
class Memory;
|
||||
}
|
||||
|
||||
namespace IPC {
|
||||
class ResponseBuilder;
|
||||
}
|
||||
|
||||
namespace Service {
|
||||
class ServiceFrameworkBase;
|
||||
}
|
||||
@@ -287,6 +291,8 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
friend class IPC::ResponseBuilder;
|
||||
|
||||
void ParseCommandBuffer(const HandleTable& handle_table, u32_le* src_cmdbuf, bool incoming);
|
||||
|
||||
std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf;
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#include <bitset>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
@@ -87,8 +86,6 @@ struct KernelCore::Impl {
|
||||
}
|
||||
cores.clear();
|
||||
|
||||
registered_core_threads.reset();
|
||||
|
||||
process_list.clear();
|
||||
current_process = nullptr;
|
||||
|
||||
@@ -107,7 +104,11 @@ struct KernelCore::Impl {
|
||||
cores.clear();
|
||||
|
||||
exclusive_monitor.reset();
|
||||
host_thread_ids.clear();
|
||||
|
||||
num_host_threads = 0;
|
||||
std::fill(register_host_thread_keys.begin(), register_host_thread_keys.end(),
|
||||
std::thread::id{});
|
||||
std::fill(register_host_thread_values.begin(), register_host_thread_values.end(), 0);
|
||||
}
|
||||
|
||||
void InitializePhysicalCores() {
|
||||
@@ -177,54 +178,58 @@ struct KernelCore::Impl {
|
||||
|
||||
void MakeCurrentProcess(Process* process) {
|
||||
current_process = process;
|
||||
|
||||
if (process == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
u32 core_id = GetCurrentHostThreadID();
|
||||
const u32 core_id = GetCurrentHostThreadID();
|
||||
if (core_id < Core::Hardware::NUM_CPU_CORES) {
|
||||
system.Memory().SetCurrentPageTable(*process, core_id);
|
||||
}
|
||||
}
|
||||
|
||||
void RegisterCoreThread(std::size_t core_id) {
|
||||
std::unique_lock lock{register_thread_mutex};
|
||||
if (!is_multicore) {
|
||||
single_core_thread_id = std::this_thread::get_id();
|
||||
}
|
||||
const std::thread::id this_id = std::this_thread::get_id();
|
||||
const auto it = host_thread_ids.find(this_id);
|
||||
if (!is_multicore) {
|
||||
single_core_thread_id = this_id;
|
||||
}
|
||||
const auto end =
|
||||
register_host_thread_keys.begin() + static_cast<ptrdiff_t>(num_host_threads);
|
||||
const auto it = std::find(register_host_thread_keys.begin(), end, this_id);
|
||||
ASSERT(core_id < Core::Hardware::NUM_CPU_CORES);
|
||||
ASSERT(it == host_thread_ids.end());
|
||||
ASSERT(!registered_core_threads[core_id]);
|
||||
host_thread_ids[this_id] = static_cast<u32>(core_id);
|
||||
registered_core_threads.set(core_id);
|
||||
ASSERT(it == end);
|
||||
InsertHostThread(static_cast<u32>(core_id));
|
||||
}
|
||||
|
||||
void RegisterHostThread() {
|
||||
std::unique_lock lock{register_thread_mutex};
|
||||
const std::thread::id this_id = std::this_thread::get_id();
|
||||
const auto it = host_thread_ids.find(this_id);
|
||||
if (it != host_thread_ids.end()) {
|
||||
return;
|
||||
const auto end =
|
||||
register_host_thread_keys.begin() + static_cast<ptrdiff_t>(num_host_threads);
|
||||
const auto it = std::find(register_host_thread_keys.begin(), end, this_id);
|
||||
if (it == end) {
|
||||
InsertHostThread(registered_thread_ids++);
|
||||
}
|
||||
host_thread_ids[this_id] = registered_thread_ids++;
|
||||
}
|
||||
|
||||
u32 GetCurrentHostThreadID() const {
|
||||
void InsertHostThread(u32 value) {
|
||||
const size_t index = num_host_threads++;
|
||||
ASSERT_MSG(index < NUM_REGISTRABLE_HOST_THREADS, "Too many host threads");
|
||||
register_host_thread_values[index] = value;
|
||||
register_host_thread_keys[index] = std::this_thread::get_id();
|
||||
}
|
||||
|
||||
[[nodiscard]] u32 GetCurrentHostThreadID() const {
|
||||
const std::thread::id this_id = std::this_thread::get_id();
|
||||
if (!is_multicore) {
|
||||
if (single_core_thread_id == this_id) {
|
||||
return static_cast<u32>(system.GetCpuManager().CurrentCore());
|
||||
}
|
||||
if (!is_multicore && single_core_thread_id == this_id) {
|
||||
return static_cast<u32>(system.GetCpuManager().CurrentCore());
|
||||
}
|
||||
std::unique_lock lock{register_thread_mutex};
|
||||
const auto it = host_thread_ids.find(this_id);
|
||||
if (it == host_thread_ids.end()) {
|
||||
const auto end =
|
||||
register_host_thread_keys.begin() + static_cast<ptrdiff_t>(num_host_threads);
|
||||
const auto it = std::find(register_host_thread_keys.begin(), end, this_id);
|
||||
if (it == end) {
|
||||
return Core::INVALID_HOST_THREAD_ID;
|
||||
}
|
||||
return it->second;
|
||||
return register_host_thread_values[static_cast<size_t>(
|
||||
std::distance(register_host_thread_keys.begin(), it))];
|
||||
}
|
||||
|
||||
Core::EmuThreadHandle GetCurrentEmuThreadID() const {
|
||||
@@ -322,10 +327,14 @@ struct KernelCore::Impl {
|
||||
std::vector<Kernel::PhysicalCore> cores;
|
||||
|
||||
// 0-3 IDs represent core threads, >3 represent others
|
||||
std::unordered_map<std::thread::id, u32> host_thread_ids;
|
||||
u32 registered_thread_ids{Core::Hardware::NUM_CPU_CORES};
|
||||
std::bitset<Core::Hardware::NUM_CPU_CORES> registered_core_threads;
|
||||
mutable std::mutex register_thread_mutex;
|
||||
std::atomic<u32> registered_thread_ids{Core::Hardware::NUM_CPU_CORES};
|
||||
|
||||
// Number of host threads is a relatively high number to avoid overflowing
|
||||
static constexpr size_t NUM_REGISTRABLE_HOST_THREADS = 64;
|
||||
std::atomic<size_t> num_host_threads{0};
|
||||
std::array<std::atomic<std::thread::id>, NUM_REGISTRABLE_HOST_THREADS>
|
||||
register_host_thread_keys{};
|
||||
std::array<std::atomic<u32>, NUM_REGISTRABLE_HOST_THREADS> register_host_thread_values{};
|
||||
|
||||
// Kernel memory management
|
||||
std::unique_ptr<Memory::MemoryManager> memory_manager;
|
||||
|
||||
@@ -2,30 +2,18 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/spin_lock.h"
|
||||
#include "core/arm/arm_interface.h"
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
#include "core/arm/dynarmic/arm_dynarmic_32.h"
|
||||
#include "core/arm/dynarmic/arm_dynarmic_64.h"
|
||||
#endif
|
||||
#include "core/arm/cpu_interrupt_handler.h"
|
||||
#include "core/arm/exclusive_monitor.h"
|
||||
#include "core/arm/unicorn/arm_unicorn.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/physical_core.h"
|
||||
#include "core/hle/kernel/scheduler.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
PhysicalCore::PhysicalCore(Core::System& system, std::size_t id, Kernel::Scheduler& scheduler,
|
||||
Core::CPUInterruptHandler& interrupt_handler)
|
||||
: interrupt_handler{interrupt_handler}, core_index{id}, scheduler{scheduler} {
|
||||
|
||||
guard = std::make_unique<Common::SpinLock>();
|
||||
}
|
||||
: interrupt_handler{interrupt_handler},
|
||||
core_index{id}, scheduler{scheduler}, guard{std::make_unique<Common::SpinLock>()} {}
|
||||
|
||||
PhysicalCore::~PhysicalCore() = default;
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <bitset>
|
||||
#include <ctime>
|
||||
#include <memory>
|
||||
#include <random>
|
||||
#include "common/alignment.h"
|
||||
@@ -123,7 +124,7 @@ std::shared_ptr<Process> Process::Create(Core::System& system, std::string name,
|
||||
: kernel.CreateNewUserProcessID();
|
||||
process->capabilities.InitializeForMetadatalessProcess();
|
||||
|
||||
std::mt19937 rng(Settings::values.rng_seed.GetValue().value_or(0));
|
||||
std::mt19937 rng(Settings::values.rng_seed.GetValue().value_or(std::time(nullptr)));
|
||||
std::uniform_int_distribution<u64> distribution;
|
||||
std::generate(process->random_entropy.begin(), process->random_entropy.end(),
|
||||
[&] { return distribution(rng); });
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
#include "common/logging/log.h"
|
||||
#include "common/thread_queue_list.h"
|
||||
#include "core/arm/arm_interface.h"
|
||||
#include "core/arm/unicorn/arm_unicorn.h"
|
||||
#include "core/core.h"
|
||||
#include "core/cpu_manager.h"
|
||||
#include "core/hardware_properties.h"
|
||||
@@ -217,8 +216,7 @@ ResultVal<std::shared_ptr<Thread>> Thread::Create(Core::System& system, ThreadTy
|
||||
} else {
|
||||
thread->tls_address = 0;
|
||||
}
|
||||
// TODO(peachum): move to ScheduleThread() when scheduler is added so selected core is used
|
||||
// to initialize the context
|
||||
|
||||
thread->arm_interface.reset();
|
||||
if ((type_flags & THREADTYPE_HLE) == 0) {
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
@@ -231,19 +229,10 @@ ResultVal<std::shared_ptr<Thread>> Thread::Create(Core::System& system, ThreadTy
|
||||
system, kernel.Interrupts(), kernel.IsMulticore(), kernel.GetExclusiveMonitor(),
|
||||
processor_id);
|
||||
}
|
||||
|
||||
#else
|
||||
if (owner_process && !owner_process->Is64BitProcess()) {
|
||||
thread->arm_interface = std::make_shared<Core::ARM_Unicorn>(
|
||||
system, kernel.Interrupts(), kernel.IsMulticore(), ARM_Unicorn::Arch::AArch32,
|
||||
processor_id);
|
||||
} else {
|
||||
thread->arm_interface = std::make_shared<Core::ARM_Unicorn>(
|
||||
system, kernel.Interrupts(), kernel.IsMulticore(), ARM_Unicorn::Arch::AArch64,
|
||||
processor_id);
|
||||
}
|
||||
LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available");
|
||||
#error Platform not supported yet.
|
||||
#endif
|
||||
|
||||
ResetThreadContext32(thread->context_32, static_cast<u32>(stack_top),
|
||||
static_cast<u32>(entry_point), static_cast<u32>(arg));
|
||||
ResetThreadContext64(thread->context_64, stack_top, entry_point, arg);
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "common/string_util.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/constants.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
|
||||
@@ -751,7 +751,7 @@ void ICommonStateGetter::GetDefaultDisplayResolution(Kernel::HLERequestContext&
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
if (Settings::values.use_docked_mode) {
|
||||
if (Settings::values.use_docked_mode.GetValue()) {
|
||||
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth) *
|
||||
static_cast<u32>(Settings::values.resolution_factor.GetValue()));
|
||||
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight) *
|
||||
@@ -824,7 +824,7 @@ void IStorage::Open(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
void ICommonStateGetter::GetOperationMode(Kernel::HLERequestContext& ctx) {
|
||||
const bool use_docked_mode{Settings::values.use_docked_mode};
|
||||
const bool use_docked_mode{Settings::values.use_docked_mode.GetValue()};
|
||||
LOG_DEBUG(Service_AM, "called, use_docked_mode={}", use_docked_mode);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
@@ -1201,6 +1201,8 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_)
|
||||
{151, nullptr, "TryPopFromNotificationStorageChannel"},
|
||||
{160, nullptr, "GetHealthWarningDisappearedSystemEvent"},
|
||||
{170, nullptr, "SetHdcpAuthenticationActivated"},
|
||||
{180, nullptr, "GetLaunchRequiredVersion"},
|
||||
{181, nullptr, "UpgradeLaunchRequiredVersion"},
|
||||
{500, nullptr, "StartContinuousRecordingFlushForDebug"},
|
||||
{1000, nullptr, "CreateMovieMaker"},
|
||||
{1001, nullptr, "PrepareForJit"},
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
// 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/process.h"
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/hle/service/am/applet_ae.h"
|
||||
#include "core/hle/service/nvflinger/nvflinger.h"
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace Service::AM::Applets {
|
||||
static Core::Frontend::ControllerParameters ConvertToFrontendParameters(
|
||||
ControllerSupportArgPrivate private_arg, ControllerSupportArgHeader header, bool enable_text,
|
||||
std::vector<IdentificationColor> identification_colors, std::vector<ExplainText> text) {
|
||||
HID::Controller_NPad::NPadType npad_style_set;
|
||||
HID::Controller_NPad::NpadStyleSet npad_style_set;
|
||||
npad_style_set.raw = private_arg.style_set;
|
||||
|
||||
return {
|
||||
@@ -62,7 +62,7 @@ void Controller::Initialize() {
|
||||
common_args.play_startup_sound, common_args.size, common_args.system_tick,
|
||||
common_args.theme_color);
|
||||
|
||||
library_applet_version = LibraryAppletVersion{common_args.library_version};
|
||||
controller_applet_version = ControllerAppletVersion{common_args.library_version};
|
||||
|
||||
const auto private_arg_storage = broker.PopNormalDataToApplet();
|
||||
ASSERT(private_arg_storage != nullptr);
|
||||
@@ -70,39 +70,78 @@ void Controller::Initialize() {
|
||||
const auto& private_arg = private_arg_storage->GetData();
|
||||
ASSERT(private_arg.size() == sizeof(ControllerSupportArgPrivate));
|
||||
|
||||
std::memcpy(&controller_private_arg, private_arg.data(), sizeof(ControllerSupportArgPrivate));
|
||||
std::memcpy(&controller_private_arg, private_arg.data(), private_arg.size());
|
||||
ASSERT_MSG(controller_private_arg.arg_private_size == sizeof(ControllerSupportArgPrivate),
|
||||
"Unknown ControllerSupportArgPrivate revision={} with size={}",
|
||||
library_applet_version, controller_private_arg.arg_private_size);
|
||||
controller_applet_version, controller_private_arg.arg_private_size);
|
||||
|
||||
// Some games such as Cave Story+ set invalid values for the ControllerSupportMode.
|
||||
// Defer to arg_size to set the ControllerSupportMode.
|
||||
if (controller_private_arg.mode >= ControllerSupportMode::MaxControllerSupportMode) {
|
||||
switch (controller_private_arg.arg_size) {
|
||||
case sizeof(ControllerSupportArgOld):
|
||||
case sizeof(ControllerSupportArgNew):
|
||||
controller_private_arg.mode = ControllerSupportMode::ShowControllerSupport;
|
||||
break;
|
||||
case sizeof(ControllerUpdateFirmwareArg):
|
||||
controller_private_arg.mode = ControllerSupportMode::ShowControllerFirmwareUpdate;
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unknown ControllerPrivateArg mode={} with arg_size={}",
|
||||
controller_private_arg.mode, controller_private_arg.arg_size);
|
||||
controller_private_arg.mode = ControllerSupportMode::ShowControllerSupport;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Some games such as Cave Story+ set invalid values for the ControllerSupportCaller.
|
||||
// This is always 0 (Application) except with ShowControllerFirmwareUpdateForSystem.
|
||||
if (controller_private_arg.caller >= ControllerSupportCaller::MaxControllerSupportCaller) {
|
||||
if (controller_private_arg.flag_1 &&
|
||||
controller_private_arg.mode == ControllerSupportMode::ShowControllerFirmwareUpdate) {
|
||||
controller_private_arg.caller = ControllerSupportCaller::System;
|
||||
} else {
|
||||
controller_private_arg.caller = ControllerSupportCaller::Application;
|
||||
}
|
||||
}
|
||||
|
||||
switch (controller_private_arg.mode) {
|
||||
case ControllerSupportMode::ShowControllerSupport: {
|
||||
case ControllerSupportMode::ShowControllerSupport:
|
||||
case ControllerSupportMode::ShowControllerStrapGuide: {
|
||||
const auto user_arg_storage = broker.PopNormalDataToApplet();
|
||||
ASSERT(user_arg_storage != nullptr);
|
||||
|
||||
const auto& user_arg = user_arg_storage->GetData();
|
||||
switch (library_applet_version) {
|
||||
case LibraryAppletVersion::Version3:
|
||||
case LibraryAppletVersion::Version4:
|
||||
case LibraryAppletVersion::Version5:
|
||||
switch (controller_applet_version) {
|
||||
case ControllerAppletVersion::Version3:
|
||||
case ControllerAppletVersion::Version4:
|
||||
case ControllerAppletVersion::Version5:
|
||||
ASSERT(user_arg.size() == sizeof(ControllerSupportArgOld));
|
||||
std::memcpy(&controller_user_arg_old, user_arg.data(), sizeof(ControllerSupportArgOld));
|
||||
std::memcpy(&controller_user_arg_old, user_arg.data(), user_arg.size());
|
||||
break;
|
||||
case LibraryAppletVersion::Version7:
|
||||
case ControllerAppletVersion::Version7:
|
||||
ASSERT(user_arg.size() == sizeof(ControllerSupportArgNew));
|
||||
std::memcpy(&controller_user_arg_new, user_arg.data(), sizeof(ControllerSupportArgNew));
|
||||
std::memcpy(&controller_user_arg_new, user_arg.data(), user_arg.size());
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unknown ControllerSupportArg revision={} with size={}",
|
||||
library_applet_version, controller_private_arg.arg_size);
|
||||
controller_applet_version, controller_private_arg.arg_size);
|
||||
ASSERT(user_arg.size() >= sizeof(ControllerSupportArgNew));
|
||||
std::memcpy(&controller_user_arg_new, user_arg.data(), sizeof(ControllerSupportArgNew));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ControllerSupportMode::ShowControllerStrapGuide:
|
||||
case ControllerSupportMode::ShowControllerFirmwareUpdate:
|
||||
case ControllerSupportMode::ShowControllerFirmwareUpdate: {
|
||||
const auto update_arg_storage = broker.PopNormalDataToApplet();
|
||||
ASSERT(update_arg_storage != nullptr);
|
||||
|
||||
const auto& update_arg = update_arg_storage->GetData();
|
||||
ASSERT(update_arg.size() == sizeof(ControllerUpdateFirmwareArg));
|
||||
|
||||
std::memcpy(&controller_update_arg, update_arg.data(), update_arg.size());
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
UNIMPLEMENTED_MSG("Unimplemented ControllerSupportMode={}", controller_private_arg.mode);
|
||||
break;
|
||||
@@ -126,10 +165,10 @@ void Controller::Execute() {
|
||||
switch (controller_private_arg.mode) {
|
||||
case ControllerSupportMode::ShowControllerSupport: {
|
||||
const auto parameters = [this] {
|
||||
switch (library_applet_version) {
|
||||
case LibraryAppletVersion::Version3:
|
||||
case LibraryAppletVersion::Version4:
|
||||
case LibraryAppletVersion::Version5:
|
||||
switch (controller_applet_version) {
|
||||
case ControllerAppletVersion::Version3:
|
||||
case ControllerAppletVersion::Version4:
|
||||
case ControllerAppletVersion::Version5:
|
||||
return ConvertToFrontendParameters(
|
||||
controller_private_arg, controller_user_arg_old.header,
|
||||
controller_user_arg_old.enable_explain_text,
|
||||
@@ -138,7 +177,7 @@ void Controller::Execute() {
|
||||
controller_user_arg_old.identification_colors.end()),
|
||||
std::vector<ExplainText>(controller_user_arg_old.explain_text.begin(),
|
||||
controller_user_arg_old.explain_text.end()));
|
||||
case LibraryAppletVersion::Version7:
|
||||
case ControllerAppletVersion::Version7:
|
||||
default:
|
||||
return ConvertToFrontendParameters(
|
||||
controller_private_arg, controller_user_arg_new.header,
|
||||
@@ -170,6 +209,9 @@ void Controller::Execute() {
|
||||
}
|
||||
case ControllerSupportMode::ShowControllerStrapGuide:
|
||||
case ControllerSupportMode::ShowControllerFirmwareUpdate:
|
||||
UNIMPLEMENTED_MSG("ControllerSupportMode={} is not implemented",
|
||||
controller_private_arg.mode);
|
||||
[[fallthrough]];
|
||||
default: {
|
||||
ConfigurationComplete();
|
||||
break;
|
||||
@@ -180,7 +222,7 @@ void Controller::Execute() {
|
||||
void Controller::ConfigurationComplete() {
|
||||
ControllerSupportResultInfo result_info{};
|
||||
|
||||
const auto& players = Settings::values.players;
|
||||
const auto& players = Settings::values.players.GetValue();
|
||||
|
||||
// If enable_single_mode is enabled, player_count is 1 regardless of any other parameters.
|
||||
// Otherwise, only count connected players from P1-P8.
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace Service::AM::Applets {
|
||||
using IdentificationColor = std::array<u8, 4>;
|
||||
using ExplainText = std::array<char, 0x81>;
|
||||
|
||||
enum class LibraryAppletVersion : u32_le {
|
||||
enum class ControllerAppletVersion : u32_le {
|
||||
Version3 = 0x3, // 1.0.0 - 2.3.0
|
||||
Version4 = 0x4, // 3.0.0 - 5.1.0
|
||||
Version5 = 0x5, // 6.0.0 - 7.0.1
|
||||
@@ -29,14 +29,18 @@ enum class LibraryAppletVersion : u32_le {
|
||||
};
|
||||
|
||||
enum class ControllerSupportMode : u8 {
|
||||
ShowControllerSupport = 0,
|
||||
ShowControllerStrapGuide = 1,
|
||||
ShowControllerFirmwareUpdate = 2,
|
||||
ShowControllerSupport,
|
||||
ShowControllerStrapGuide,
|
||||
ShowControllerFirmwareUpdate,
|
||||
|
||||
MaxControllerSupportMode,
|
||||
};
|
||||
|
||||
enum class ControllerSupportCaller : u8 {
|
||||
Application = 0,
|
||||
System = 1,
|
||||
Application,
|
||||
System,
|
||||
|
||||
MaxControllerSupportCaller,
|
||||
};
|
||||
|
||||
struct ControllerSupportArgPrivate {
|
||||
@@ -84,6 +88,13 @@ struct ControllerSupportArgNew {
|
||||
static_assert(sizeof(ControllerSupportArgNew) == 0x430,
|
||||
"ControllerSupportArgNew has incorrect size.");
|
||||
|
||||
struct ControllerUpdateFirmwareArg {
|
||||
bool enable_force_update{};
|
||||
INSERT_PADDING_BYTES(3);
|
||||
};
|
||||
static_assert(sizeof(ControllerUpdateFirmwareArg) == 0x4,
|
||||
"ControllerUpdateFirmwareArg has incorrect size.");
|
||||
|
||||
struct ControllerSupportResultInfo {
|
||||
s8 player_count{};
|
||||
INSERT_PADDING_BYTES(3);
|
||||
@@ -110,10 +121,11 @@ public:
|
||||
private:
|
||||
const Core::Frontend::ControllerApplet& frontend;
|
||||
|
||||
LibraryAppletVersion library_applet_version;
|
||||
ControllerAppletVersion controller_applet_version;
|
||||
ControllerSupportArgPrivate controller_private_arg;
|
||||
ControllerSupportArgOld controller_user_arg_old;
|
||||
ControllerSupportArgNew controller_user_arg_new;
|
||||
ControllerUpdateFirmwareArg controller_update_arg;
|
||||
bool complete{false};
|
||||
ResultCode status{RESULT_SUCCESS};
|
||||
bool is_single_mode{false};
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <numeric>
|
||||
#include <vector>
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/content_archive.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/nca_metadata.h"
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/service/apm/apm.h"
|
||||
#include "core/hle/service/apm/interface.h"
|
||||
|
||||
@@ -69,7 +69,8 @@ void Controller::SetFromCpuBoostMode(CpuBoostMode mode) {
|
||||
}
|
||||
|
||||
PerformanceMode Controller::GetCurrentPerformanceMode() const {
|
||||
return Settings::values.use_docked_mode ? PerformanceMode::Docked : PerformanceMode::Handheld;
|
||||
return Settings::values.use_docked_mode.GetValue() ? PerformanceMode::Docked
|
||||
: PerformanceMode::Handheld;
|
||||
}
|
||||
|
||||
PerformanceConfiguration Controller::GetCurrentPerformanceConfiguration(PerformanceMode mode) {
|
||||
|
||||
@@ -84,7 +84,7 @@ void ProgressServiceBackend::FinishDownload(ResultCode result) {
|
||||
|
||||
void ProgressServiceBackend::SignalUpdate() const {
|
||||
if (need_hle_lock) {
|
||||
std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
|
||||
std::lock_guard lock(HLE::g_hle_lock);
|
||||
event.writable->Signal();
|
||||
} else {
|
||||
event.writable->Signal();
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "common/hex_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// 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/kernel.h"
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <memory>
|
||||
|
||||
#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/kernel.h"
|
||||
|
||||
@@ -41,7 +41,7 @@ CAPS_U::CAPS_U() : ServiceFramework("caps:u") {
|
||||
{130, nullptr, "PrecheckToCreateContentsForApplication"},
|
||||
{140, nullptr, "GetAlbumFileList1AafeAruidDeprecated"},
|
||||
{141, nullptr, "GetAlbumFileList2AafeUidAruidDeprecated"},
|
||||
{142, nullptr, "GetAlbumFileList3AaeAruid"},
|
||||
{142, &CAPS_U::GetAlbumFileList3AaeAruid, "GetAlbumFileList3AaeAruid"},
|
||||
{143, nullptr, "GetAlbumFileList4AaeUidAruid"},
|
||||
{60002, nullptr, "OpenAccessorSessionForApplication"},
|
||||
};
|
||||
@@ -77,17 +77,24 @@ void CAPS_U::GetAlbumContentsFileListForApplication(Kernel::HLERequestContext& c
|
||||
|
||||
// TODO: Update this when we implement the album.
|
||||
// Currently we do not have a method of accessing album entries, set this to 0 for now.
|
||||
constexpr s32 total_entries{0};
|
||||
constexpr u32 total_entries_1{};
|
||||
constexpr u32 total_entries_2{};
|
||||
|
||||
LOG_WARNING(Service_Capture,
|
||||
"(STUBBED) called. pid={}, content_type={}, start_posix_time={}, "
|
||||
"end_posix_time={}, applet_resource_user_id={}, total_entries={}",
|
||||
pid, content_type, start_posix_time, end_posix_time, applet_resource_user_id,
|
||||
total_entries);
|
||||
LOG_WARNING(
|
||||
Service_Capture,
|
||||
"(STUBBED) called. pid={}, content_type={}, start_posix_time={}, "
|
||||
"end_posix_time={}, applet_resource_user_id={}, total_entries_1={}, total_entries_2={}",
|
||||
pid, content_type, start_posix_time, end_posix_time, applet_resource_user_id,
|
||||
total_entries_1, total_entries_2);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(total_entries);
|
||||
rb.Push(total_entries_1);
|
||||
rb.Push(total_entries_2);
|
||||
}
|
||||
|
||||
void CAPS_U::GetAlbumFileList3AaeAruid(Kernel::HLERequestContext& ctx) {
|
||||
GetAlbumContentsFileListForApplication(ctx);
|
||||
}
|
||||
|
||||
} // namespace Service::Capture
|
||||
|
||||
@@ -20,6 +20,7 @@ public:
|
||||
private:
|
||||
void SetShimLibraryVersion(Kernel::HLERequestContext& ctx);
|
||||
void GetAlbumContentsFileListForApplication(Kernel::HLERequestContext& ctx);
|
||||
void GetAlbumFileList3AaeAruid(Kernel::HLERequestContext& ctx);
|
||||
};
|
||||
|
||||
} // namespace Service::Capture
|
||||
|
||||
@@ -79,7 +79,7 @@ ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) cons
|
||||
}
|
||||
|
||||
auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
|
||||
if (dir->GetFile(Common::FS::GetFilename(path)) == nullptr) {
|
||||
if (dir == nullptr || dir->GetFile(Common::FS::GetFilename(path)) == nullptr) {
|
||||
return FileSys::ERROR_PATH_NOT_FOUND;
|
||||
}
|
||||
if (!dir->DeleteFile(Common::FS::GetFilename(path))) {
|
||||
@@ -93,8 +93,9 @@ ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) cons
|
||||
ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path_) const {
|
||||
std::string path(Common::FS::SanitizePath(path_));
|
||||
auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
|
||||
if (dir == nullptr && Common::FS::GetFilename(Common::FS::GetParentPath(path)).empty())
|
||||
if (dir == nullptr || Common::FS::GetFilename(Common::FS::GetParentPath(path)).empty()) {
|
||||
dir = backing;
|
||||
}
|
||||
auto new_dir = dir->CreateSubdirectory(Common::FS::GetFilename(path));
|
||||
if (new_dir == nullptr) {
|
||||
// TODO(DarkLordZach): Find a better error code for this
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <queue>
|
||||
#include "common/logging/log.h"
|
||||
#include "common/uuid.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/readable_event.h"
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <memory>
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/hle_ipc.h"
|
||||
|
||||
@@ -117,7 +117,10 @@ u32 Controller_NPad::IndexToNPad(std::size_t index) {
|
||||
}
|
||||
|
||||
Controller_NPad::Controller_NPad(Core::System& system) : ControllerBase(system), system(system) {}
|
||||
Controller_NPad::~Controller_NPad() = default;
|
||||
|
||||
Controller_NPad::~Controller_NPad() {
|
||||
OnRelease();
|
||||
}
|
||||
|
||||
void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) {
|
||||
const auto controller_type = connected_controllers[controller_idx].type;
|
||||
@@ -139,7 +142,7 @@ void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) {
|
||||
controller.properties.is_vertical.Assign(1);
|
||||
controller.properties.use_plus.Assign(1);
|
||||
controller.properties.use_minus.Assign(1);
|
||||
controller.pad_assignment = NPadAssignments::Single;
|
||||
controller.pad_assignment = NpadAssignments::Single;
|
||||
break;
|
||||
case NPadControllerType::Handheld:
|
||||
controller.joy_styles.handheld.Assign(1);
|
||||
@@ -147,7 +150,7 @@ void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) {
|
||||
controller.properties.is_vertical.Assign(1);
|
||||
controller.properties.use_plus.Assign(1);
|
||||
controller.properties.use_minus.Assign(1);
|
||||
controller.pad_assignment = NPadAssignments::Dual;
|
||||
controller.pad_assignment = NpadAssignments::Dual;
|
||||
break;
|
||||
case NPadControllerType::JoyDual:
|
||||
controller.joy_styles.joycon_dual.Assign(1);
|
||||
@@ -156,26 +159,26 @@ void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) {
|
||||
controller.properties.is_vertical.Assign(1);
|
||||
controller.properties.use_plus.Assign(1);
|
||||
controller.properties.use_minus.Assign(1);
|
||||
controller.pad_assignment = NPadAssignments::Dual;
|
||||
controller.pad_assignment = NpadAssignments::Dual;
|
||||
break;
|
||||
case NPadControllerType::JoyLeft:
|
||||
controller.joy_styles.joycon_left.Assign(1);
|
||||
controller.device_type.joycon_left.Assign(1);
|
||||
controller.properties.is_horizontal.Assign(1);
|
||||
controller.properties.use_minus.Assign(1);
|
||||
controller.pad_assignment = NPadAssignments::Single;
|
||||
controller.pad_assignment = NpadAssignments::Single;
|
||||
break;
|
||||
case NPadControllerType::JoyRight:
|
||||
controller.joy_styles.joycon_right.Assign(1);
|
||||
controller.device_type.joycon_right.Assign(1);
|
||||
controller.properties.is_horizontal.Assign(1);
|
||||
controller.properties.use_plus.Assign(1);
|
||||
controller.pad_assignment = NPadAssignments::Single;
|
||||
controller.pad_assignment = NpadAssignments::Single;
|
||||
break;
|
||||
case NPadControllerType::Pokeball:
|
||||
controller.joy_styles.pokeball.Assign(1);
|
||||
controller.device_type.pokeball.Assign(1);
|
||||
controller.pad_assignment = NPadAssignments::Single;
|
||||
controller.pad_assignment = NpadAssignments::Single;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -184,11 +187,14 @@ void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) {
|
||||
controller.single_color.button_color = 0;
|
||||
|
||||
controller.dual_color_error = ColorReadError::ReadOk;
|
||||
controller.left_color.body_color = Settings::values.players[controller_idx].body_color_left;
|
||||
controller.left_color.button_color = Settings::values.players[controller_idx].button_color_left;
|
||||
controller.right_color.body_color = Settings::values.players[controller_idx].body_color_right;
|
||||
controller.left_color.body_color =
|
||||
Settings::values.players.GetValue()[controller_idx].body_color_left;
|
||||
controller.left_color.button_color =
|
||||
Settings::values.players.GetValue()[controller_idx].button_color_left;
|
||||
controller.right_color.body_color =
|
||||
Settings::values.players.GetValue()[controller_idx].body_color_right;
|
||||
controller.right_color.button_color =
|
||||
Settings::values.players[controller_idx].button_color_right;
|
||||
Settings::values.players.GetValue()[controller_idx].button_color_right;
|
||||
|
||||
controller.battery_level[0] = BATTERY_FULL;
|
||||
controller.battery_level[1] = BATTERY_FULL;
|
||||
@@ -199,7 +205,7 @@ void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) {
|
||||
|
||||
void Controller_NPad::OnInit() {
|
||||
auto& kernel = system.Kernel();
|
||||
for (std::size_t i = 0; i < styleset_changed_events.size(); i++) {
|
||||
for (std::size_t i = 0; i < styleset_changed_events.size(); ++i) {
|
||||
styleset_changed_events[i] = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, fmt::format("npad:NpadStyleSetChanged_{}", i));
|
||||
}
|
||||
@@ -208,6 +214,8 @@ void Controller_NPad::OnInit() {
|
||||
return;
|
||||
}
|
||||
|
||||
OnLoadInputDevices();
|
||||
|
||||
if (style.raw == 0) {
|
||||
// We want to support all controllers
|
||||
style.handheld.Assign(1);
|
||||
@@ -218,12 +226,27 @@ void Controller_NPad::OnInit() {
|
||||
style.pokeball.Assign(1);
|
||||
}
|
||||
|
||||
std::transform(Settings::values.players.begin(), Settings::values.players.end(),
|
||||
connected_controllers.begin(), [](const Settings::PlayerInput& player) {
|
||||
std::transform(Settings::values.players.GetValue().begin(),
|
||||
Settings::values.players.GetValue().end(), connected_controllers.begin(),
|
||||
[](const Settings::PlayerInput& player) {
|
||||
return ControllerHolder{MapSettingsTypeToNPad(player.controller_type),
|
||||
player.connected};
|
||||
});
|
||||
|
||||
// Connect the Player 1 or Handheld controller if none are connected.
|
||||
if (std::none_of(connected_controllers.begin(), connected_controllers.end(),
|
||||
[](const ControllerHolder& controller) { return controller.is_connected; })) {
|
||||
const auto controller =
|
||||
MapSettingsTypeToNPad(Settings::values.players.GetValue()[0].controller_type);
|
||||
if (controller == NPadControllerType::Handheld) {
|
||||
Settings::values.players.GetValue()[HANDHELD_INDEX].connected = true;
|
||||
connected_controllers[HANDHELD_INDEX] = {controller, true};
|
||||
} else {
|
||||
Settings::values.players.GetValue()[0].connected = true;
|
||||
connected_controllers[0] = {controller, true};
|
||||
}
|
||||
}
|
||||
|
||||
// Account for handheld
|
||||
if (connected_controllers[HANDHELD_INDEX].is_connected) {
|
||||
connected_controllers[HANDHELD_INDEX].type = NPadControllerType::Handheld;
|
||||
@@ -242,7 +265,7 @@ void Controller_NPad::OnInit() {
|
||||
}
|
||||
|
||||
void Controller_NPad::OnLoadInputDevices() {
|
||||
const auto& players = Settings::values.players;
|
||||
const auto& players = Settings::values.players.GetValue();
|
||||
for (std::size_t i = 0; i < players.size(); ++i) {
|
||||
std::transform(players[i].buttons.begin() + Settings::NativeButton::BUTTON_HID_BEGIN,
|
||||
players[i].buttons.begin() + Settings::NativeButton::BUTTON_HID_END,
|
||||
@@ -250,13 +273,26 @@ void Controller_NPad::OnLoadInputDevices() {
|
||||
std::transform(players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN,
|
||||
players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_END,
|
||||
sticks[i].begin(), Input::CreateDevice<Input::AnalogDevice>);
|
||||
std::transform(players[i].vibrations.begin() +
|
||||
Settings::NativeVibration::VIBRATION_HID_BEGIN,
|
||||
players[i].vibrations.begin() + Settings::NativeVibration::VIBRATION_HID_END,
|
||||
vibrations[i].begin(), Input::CreateDevice<Input::VibrationDevice>);
|
||||
std::transform(players[i].motions.begin() + Settings::NativeMotion::MOTION_HID_BEGIN,
|
||||
players[i].motions.begin() + Settings::NativeMotion::MOTION_HID_END,
|
||||
motions[i].begin(), Input::CreateDevice<Input::MotionDevice>);
|
||||
for (std::size_t device_idx = 0; device_idx < vibrations[i].size(); ++device_idx) {
|
||||
InitializeVibrationDeviceAtIndex(i, device_idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Controller_NPad::OnRelease() {}
|
||||
void Controller_NPad::OnRelease() {
|
||||
for (std::size_t npad_idx = 0; npad_idx < vibrations.size(); ++npad_idx) {
|
||||
for (std::size_t device_idx = 0; device_idx < vibrations[npad_idx].size(); ++device_idx) {
|
||||
VibrateControllerAtIndex(npad_idx, device_idx, {});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Controller_NPad::RequestPadStateUpdate(u32 npad_id) {
|
||||
const auto controller_idx = NPadIdToIndex(npad_id);
|
||||
@@ -339,7 +375,7 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
|
||||
if (!IsControllerActivated()) {
|
||||
return;
|
||||
}
|
||||
for (std::size_t i = 0; i < shared_memory_entries.size(); i++) {
|
||||
for (std::size_t i = 0; i < shared_memory_entries.size(); ++i) {
|
||||
auto& npad = shared_memory_entries[i];
|
||||
const std::array<NPadGeneric*, 7> controller_npads{&npad.main_controller_states,
|
||||
&npad.handheld_states,
|
||||
@@ -481,7 +517,7 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing
|
||||
if (!IsControllerActivated()) {
|
||||
return;
|
||||
}
|
||||
for (std::size_t i = 0; i < shared_memory_entries.size(); i++) {
|
||||
for (std::size_t i = 0; i < shared_memory_entries.size(); ++i) {
|
||||
auto& npad = shared_memory_entries[i];
|
||||
|
||||
const auto& controller_type = connected_controllers[i].type;
|
||||
@@ -515,7 +551,7 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing
|
||||
// Try to read sixaxis sensor states
|
||||
std::array<MotionDevice, 2> motion_devices;
|
||||
|
||||
if (sixaxis_sensors_enabled && Settings::values.motion_enabled) {
|
||||
if (sixaxis_sensors_enabled && Settings::values.motion_enabled.GetValue()) {
|
||||
sixaxis_at_rest = true;
|
||||
for (std::size_t e = 0; e < motion_devices.size(); ++e) {
|
||||
const auto& device = motions[i][e];
|
||||
@@ -601,15 +637,15 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing
|
||||
shared_memory_entries.size() * sizeof(NPadEntry));
|
||||
}
|
||||
|
||||
void Controller_NPad::SetSupportedStyleSet(NPadType style_set) {
|
||||
void Controller_NPad::SetSupportedStyleSet(NpadStyleSet style_set) {
|
||||
style.raw = style_set.raw;
|
||||
}
|
||||
|
||||
Controller_NPad::NPadType Controller_NPad::GetSupportedStyleSet() const {
|
||||
Controller_NPad::NpadStyleSet Controller_NPad::GetSupportedStyleSet() const {
|
||||
return style;
|
||||
}
|
||||
|
||||
void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) {
|
||||
void Controller_NPad::SetSupportedNpadIdTypes(u8* data, std::size_t length) {
|
||||
ASSERT(length > 0 && (length % sizeof(u32)) == 0);
|
||||
supported_npad_id_types.clear();
|
||||
supported_npad_id_types.resize(length / sizeof(u32));
|
||||
@@ -621,7 +657,7 @@ void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length)
|
||||
std::memcpy(data, supported_npad_id_types.data(), supported_npad_id_types.size());
|
||||
}
|
||||
|
||||
std::size_t Controller_NPad::GetSupportedNPadIdTypesSize() const {
|
||||
std::size_t Controller_NPad::GetSupportedNpadIdTypesSize() const {
|
||||
return supported_npad_id_types.size();
|
||||
}
|
||||
|
||||
@@ -641,7 +677,7 @@ Controller_NPad::NpadHandheldActivationMode Controller_NPad::GetNpadHandheldActi
|
||||
return handheld_activation_mode;
|
||||
}
|
||||
|
||||
void Controller_NPad::SetNpadMode(u32 npad_id, NPadAssignments assignment_mode) {
|
||||
void Controller_NPad::SetNpadMode(u32 npad_id, NpadAssignments assignment_mode) {
|
||||
const std::size_t npad_index = NPadIdToIndex(npad_id);
|
||||
ASSERT(npad_index < shared_memory_entries.size());
|
||||
if (shared_memory_entries[npad_index].pad_assignment != assignment_mode) {
|
||||
@@ -649,35 +685,140 @@ void Controller_NPad::SetNpadMode(u32 npad_id, NPadAssignments assignment_mode)
|
||||
}
|
||||
}
|
||||
|
||||
void Controller_NPad::VibrateController(const std::vector<u32>& controllers,
|
||||
const std::vector<Vibration>& vibrations) {
|
||||
LOG_TRACE(Service_HID, "called");
|
||||
bool Controller_NPad::VibrateControllerAtIndex(std::size_t npad_index, std::size_t device_index,
|
||||
const VibrationValue& vibration_value) {
|
||||
if (!connected_controllers[npad_index].is_connected || !vibrations[npad_index][device_index]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Settings::values.vibration_enabled || !can_controllers_vibrate) {
|
||||
const auto& player = Settings::values.players.GetValue()[npad_index];
|
||||
|
||||
if (!player.vibration_enabled) {
|
||||
if (latest_vibration_values[npad_index][device_index].amp_low != 0.0f ||
|
||||
latest_vibration_values[npad_index][device_index].amp_high != 0.0f) {
|
||||
// Send an empty vibration to stop any vibrations.
|
||||
vibrations[npad_index][device_index]->SetRumblePlay(0.0f, 160.0f, 0.0f, 320.0f);
|
||||
// Then reset the vibration value to its default value.
|
||||
latest_vibration_values[npad_index][device_index] = {};
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Settings::values.enable_accurate_vibrations.GetValue()) {
|
||||
using std::chrono::duration_cast;
|
||||
using std::chrono::milliseconds;
|
||||
using std::chrono::steady_clock;
|
||||
|
||||
const auto now = steady_clock::now();
|
||||
|
||||
// Filter out non-zero vibrations that are within 10ms of each other.
|
||||
if ((vibration_value.amp_low != 0.0f || vibration_value.amp_high != 0.0f) &&
|
||||
duration_cast<milliseconds>(now - last_vibration_timepoints[npad_index][device_index]) <
|
||||
milliseconds(10)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
last_vibration_timepoints[npad_index][device_index] = now;
|
||||
}
|
||||
|
||||
auto& vibration = vibrations[npad_index][device_index];
|
||||
const auto player_vibration_strength = static_cast<f32>(player.vibration_strength);
|
||||
const auto amp_low =
|
||||
std::min(vibration_value.amp_low * player_vibration_strength / 100.0f, 1.0f);
|
||||
const auto amp_high =
|
||||
std::min(vibration_value.amp_high * player_vibration_strength / 100.0f, 1.0f);
|
||||
return vibration->SetRumblePlay(amp_low, vibration_value.freq_low, amp_high,
|
||||
vibration_value.freq_high);
|
||||
}
|
||||
|
||||
void Controller_NPad::VibrateController(const DeviceHandle& vibration_device_handle,
|
||||
const VibrationValue& vibration_value) {
|
||||
if (!Settings::values.vibration_enabled.GetValue() && !permit_vibration_session_enabled) {
|
||||
return;
|
||||
}
|
||||
bool success = true;
|
||||
for (std::size_t i = 0; i < controllers.size(); ++i) {
|
||||
if (!connected_controllers[i].is_connected) {
|
||||
continue;
|
||||
}
|
||||
using namespace Settings::NativeButton;
|
||||
const auto& button_state = buttons[i];
|
||||
if (button_state[A - BUTTON_HID_BEGIN]) {
|
||||
if (button_state[A - BUTTON_HID_BEGIN]->SetRumblePlay(
|
||||
vibrations[0].amp_high, vibrations[0].amp_low, vibrations[0].freq_high,
|
||||
vibrations[0].freq_low)) {
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
|
||||
const auto npad_index = NPadIdToIndex(vibration_device_handle.npad_id);
|
||||
const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index);
|
||||
|
||||
if (!vibration_devices_mounted[npad_index][device_index] ||
|
||||
!connected_controllers[npad_index].is_connected) {
|
||||
return;
|
||||
}
|
||||
if (success) {
|
||||
last_processed_vibration = vibrations.back();
|
||||
|
||||
if (vibration_device_handle.device_index == DeviceIndex::None) {
|
||||
UNREACHABLE_MSG("DeviceIndex should never be None!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Some games try to send mismatched parameters in the device handle, block these.
|
||||
if ((connected_controllers[npad_index].type == NPadControllerType::JoyLeft &&
|
||||
(vibration_device_handle.npad_type == NpadType::JoyconRight ||
|
||||
vibration_device_handle.device_index == DeviceIndex::Right)) ||
|
||||
(connected_controllers[npad_index].type == NPadControllerType::JoyRight &&
|
||||
(vibration_device_handle.npad_type == NpadType::JoyconLeft ||
|
||||
vibration_device_handle.device_index == DeviceIndex::Left))) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Filter out vibrations with equivalent values to reduce unnecessary state changes.
|
||||
if (vibration_value.amp_low == latest_vibration_values[npad_index][device_index].amp_low &&
|
||||
vibration_value.amp_high == latest_vibration_values[npad_index][device_index].amp_high) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (VibrateControllerAtIndex(npad_index, device_index, vibration_value)) {
|
||||
latest_vibration_values[npad_index][device_index] = vibration_value;
|
||||
}
|
||||
}
|
||||
|
||||
Controller_NPad::Vibration Controller_NPad::GetLastVibration() const {
|
||||
return last_processed_vibration;
|
||||
void Controller_NPad::VibrateControllers(const std::vector<DeviceHandle>& vibration_device_handles,
|
||||
const std::vector<VibrationValue>& vibration_values) {
|
||||
if (!Settings::values.vibration_enabled.GetValue() && !permit_vibration_session_enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
ASSERT_OR_EXECUTE_MSG(
|
||||
vibration_device_handles.size() == vibration_values.size(), { return; },
|
||||
"The amount of device handles does not match with the amount of vibration values,"
|
||||
"this is undefined behavior!");
|
||||
|
||||
for (std::size_t i = 0; i < vibration_device_handles.size(); ++i) {
|
||||
VibrateController(vibration_device_handles[i], vibration_values[i]);
|
||||
}
|
||||
}
|
||||
|
||||
Controller_NPad::VibrationValue Controller_NPad::GetLastVibration(
|
||||
const DeviceHandle& vibration_device_handle) const {
|
||||
const auto npad_index = NPadIdToIndex(vibration_device_handle.npad_id);
|
||||
const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index);
|
||||
return latest_vibration_values[npad_index][device_index];
|
||||
}
|
||||
|
||||
void Controller_NPad::InitializeVibrationDevice(const DeviceHandle& vibration_device_handle) {
|
||||
const auto npad_index = NPadIdToIndex(vibration_device_handle.npad_id);
|
||||
const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index);
|
||||
InitializeVibrationDeviceAtIndex(npad_index, device_index);
|
||||
}
|
||||
|
||||
void Controller_NPad::InitializeVibrationDeviceAtIndex(std::size_t npad_index,
|
||||
std::size_t device_index) {
|
||||
if (vibrations[npad_index][device_index]) {
|
||||
vibration_devices_mounted[npad_index][device_index] =
|
||||
vibrations[npad_index][device_index]->GetStatus() == 1;
|
||||
} else {
|
||||
vibration_devices_mounted[npad_index][device_index] = false;
|
||||
}
|
||||
}
|
||||
|
||||
void Controller_NPad::SetPermitVibrationSession(bool permit_vibration_session) {
|
||||
permit_vibration_session_enabled = permit_vibration_session;
|
||||
}
|
||||
|
||||
bool Controller_NPad::IsVibrationDeviceMounted(const DeviceHandle& vibration_device_handle) const {
|
||||
const auto npad_index = NPadIdToIndex(vibration_device_handle.npad_id);
|
||||
const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index);
|
||||
return vibration_devices_mounted[npad_index][device_index];
|
||||
}
|
||||
|
||||
std::shared_ptr<Kernel::ReadableEvent> Controller_NPad::GetStyleSetChangedEvent(u32 npad_id) const {
|
||||
@@ -696,31 +837,38 @@ void Controller_NPad::AddNewControllerAt(NPadControllerType controller, std::siz
|
||||
void Controller_NPad::UpdateControllerAt(NPadControllerType controller, std::size_t npad_index,
|
||||
bool connected) {
|
||||
if (!connected) {
|
||||
DisconnectNPadAtIndex(npad_index);
|
||||
DisconnectNpadAtIndex(npad_index);
|
||||
return;
|
||||
}
|
||||
|
||||
if (controller == NPadControllerType::Handheld) {
|
||||
Settings::values.players[HANDHELD_INDEX].controller_type =
|
||||
Settings::values.players.GetValue()[HANDHELD_INDEX].controller_type =
|
||||
MapNPadToSettingsType(controller);
|
||||
Settings::values.players[HANDHELD_INDEX].connected = true;
|
||||
Settings::values.players.GetValue()[HANDHELD_INDEX].connected = true;
|
||||
connected_controllers[HANDHELD_INDEX] = {controller, true};
|
||||
InitNewlyAddedController(HANDHELD_INDEX);
|
||||
return;
|
||||
}
|
||||
|
||||
Settings::values.players[npad_index].controller_type = MapNPadToSettingsType(controller);
|
||||
Settings::values.players[npad_index].connected = true;
|
||||
Settings::values.players.GetValue()[npad_index].controller_type =
|
||||
MapNPadToSettingsType(controller);
|
||||
Settings::values.players.GetValue()[npad_index].connected = true;
|
||||
connected_controllers[npad_index] = {controller, true};
|
||||
InitNewlyAddedController(npad_index);
|
||||
}
|
||||
|
||||
void Controller_NPad::DisconnectNPad(u32 npad_id) {
|
||||
DisconnectNPadAtIndex(NPadIdToIndex(npad_id));
|
||||
void Controller_NPad::DisconnectNpad(u32 npad_id) {
|
||||
DisconnectNpadAtIndex(NPadIdToIndex(npad_id));
|
||||
}
|
||||
|
||||
void Controller_NPad::DisconnectNPadAtIndex(std::size_t npad_index) {
|
||||
Settings::values.players[npad_index].connected = false;
|
||||
void Controller_NPad::DisconnectNpadAtIndex(std::size_t npad_index) {
|
||||
for (std::size_t device_idx = 0; device_idx < vibrations[npad_index].size(); ++device_idx) {
|
||||
// Send an empty vibration to stop any vibrations.
|
||||
VibrateControllerAtIndex(npad_index, device_idx, {});
|
||||
vibration_devices_mounted[npad_index][device_idx] = false;
|
||||
}
|
||||
|
||||
Settings::values.players.GetValue()[npad_index].connected = false;
|
||||
connected_controllers[npad_index].is_connected = false;
|
||||
|
||||
auto& controller = shared_memory_entries[npad_index];
|
||||
@@ -758,7 +906,7 @@ void Controller_NPad::MergeSingleJoyAsDualJoy(u32 npad_id_1, u32 npad_id_2) {
|
||||
(connected_controllers[npad_index_2].type == NPadControllerType::JoyLeft &&
|
||||
connected_controllers[npad_index_1].type == NPadControllerType::JoyRight)) {
|
||||
// Disconnect the joycon at the second id and connect the dual joycon at the first index.
|
||||
DisconnectNPad(npad_id_2);
|
||||
DisconnectNpad(npad_id_2);
|
||||
AddNewControllerAt(NPadControllerType::JoyDual, npad_index_1);
|
||||
}
|
||||
}
|
||||
@@ -830,14 +978,6 @@ void Controller_NPad::SetUnintendedHomeButtonInputProtectionEnabled(bool is_prot
|
||||
unintended_home_button_input_protection[NPadIdToIndex(npad_id)] = is_protection_enabled;
|
||||
}
|
||||
|
||||
void Controller_NPad::SetVibrationEnabled(bool can_vibrate) {
|
||||
can_controllers_vibrate = can_vibrate;
|
||||
}
|
||||
|
||||
bool Controller_NPad::IsVibrationEnabled() const {
|
||||
return can_controllers_vibrate;
|
||||
}
|
||||
|
||||
void Controller_NPad::ClearAllConnectedControllers() {
|
||||
for (auto& controller : connected_controllers) {
|
||||
if (controller.is_connected && controller.type != NPadControllerType::None) {
|
||||
@@ -882,7 +1022,7 @@ bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const
|
||||
return false;
|
||||
}
|
||||
// Handheld should not be supported in docked mode
|
||||
if (Settings::values.use_docked_mode) {
|
||||
if (Settings::values.use_docked_mode.GetValue()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -39,28 +39,30 @@ public:
|
||||
// Called when input devices should be loaded
|
||||
void OnLoadInputDevices() override;
|
||||
|
||||
struct NPadType {
|
||||
union {
|
||||
u32_le raw{};
|
||||
|
||||
BitField<0, 1, u32> pro_controller;
|
||||
BitField<1, 1, u32> handheld;
|
||||
BitField<2, 1, u32> joycon_dual;
|
||||
BitField<3, 1, u32> joycon_left;
|
||||
BitField<4, 1, u32> joycon_right;
|
||||
|
||||
BitField<6, 1, u32> pokeball; // TODO(ogniK): Confirm when possible
|
||||
};
|
||||
enum class NPadControllerType {
|
||||
None,
|
||||
ProController,
|
||||
Handheld,
|
||||
JoyDual,
|
||||
JoyLeft,
|
||||
JoyRight,
|
||||
Pokeball,
|
||||
};
|
||||
static_assert(sizeof(NPadType) == 4, "NPadType is an invalid size");
|
||||
|
||||
struct Vibration {
|
||||
f32 amp_low;
|
||||
f32 freq_low;
|
||||
f32 amp_high;
|
||||
f32 freq_high;
|
||||
enum class NpadType : u8 {
|
||||
ProController = 3,
|
||||
Handheld = 4,
|
||||
JoyconDual = 5,
|
||||
JoyconLeft = 6,
|
||||
JoyconRight = 7,
|
||||
Pokeball = 9,
|
||||
};
|
||||
|
||||
enum class DeviceIndex : u8 {
|
||||
Left = 0,
|
||||
Right = 1,
|
||||
None = 2,
|
||||
};
|
||||
static_assert(sizeof(Vibration) == 0x10, "Vibration is an invalid size");
|
||||
|
||||
enum class GyroscopeZeroDriftMode : u32 {
|
||||
Loose = 0,
|
||||
@@ -73,7 +75,7 @@ public:
|
||||
Horizontal = 1,
|
||||
};
|
||||
|
||||
enum class NPadAssignments : u32_le {
|
||||
enum class NpadAssignments : u32 {
|
||||
Dual = 0,
|
||||
Single = 1,
|
||||
};
|
||||
@@ -84,15 +86,36 @@ public:
|
||||
None = 2,
|
||||
};
|
||||
|
||||
enum class NPadControllerType {
|
||||
None,
|
||||
ProController,
|
||||
Handheld,
|
||||
JoyDual,
|
||||
JoyLeft,
|
||||
JoyRight,
|
||||
Pokeball,
|
||||
struct DeviceHandle {
|
||||
NpadType npad_type{};
|
||||
u8 npad_id{};
|
||||
DeviceIndex device_index{};
|
||||
INSERT_PADDING_BYTES(1);
|
||||
};
|
||||
static_assert(sizeof(DeviceHandle) == 4, "DeviceHandle is an invalid size");
|
||||
|
||||
struct NpadStyleSet {
|
||||
union {
|
||||
u32_le raw{};
|
||||
|
||||
BitField<0, 1, u32> pro_controller;
|
||||
BitField<1, 1, u32> handheld;
|
||||
BitField<2, 1, u32> joycon_dual;
|
||||
BitField<3, 1, u32> joycon_left;
|
||||
BitField<4, 1, u32> joycon_right;
|
||||
|
||||
BitField<6, 1, u32> pokeball; // TODO(ogniK): Confirm when possible
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(NpadStyleSet) == 4, "NpadStyleSet is an invalid size");
|
||||
|
||||
struct VibrationValue {
|
||||
f32 amp_low{0.0f};
|
||||
f32 freq_low{160.0f};
|
||||
f32 amp_high{0.0f};
|
||||
f32 freq_high{320.0f};
|
||||
};
|
||||
static_assert(sizeof(VibrationValue) == 0x10, "Vibration is an invalid size");
|
||||
|
||||
struct LedPattern {
|
||||
explicit LedPattern(u64 light1, u64 light2, u64 light3, u64 light4) {
|
||||
@@ -110,12 +133,12 @@ public:
|
||||
};
|
||||
};
|
||||
|
||||
void SetSupportedStyleSet(NPadType style_set);
|
||||
NPadType GetSupportedStyleSet() const;
|
||||
void SetSupportedStyleSet(NpadStyleSet style_set);
|
||||
NpadStyleSet GetSupportedStyleSet() const;
|
||||
|
||||
void SetSupportedNPadIdTypes(u8* data, std::size_t length);
|
||||
void SetSupportedNpadIdTypes(u8* data, std::size_t length);
|
||||
void GetSupportedNpadIdTypes(u32* data, std::size_t max_length);
|
||||
std::size_t GetSupportedNPadIdTypesSize() const;
|
||||
std::size_t GetSupportedNpadIdTypesSize() const;
|
||||
|
||||
void SetHoldType(NpadHoldType joy_hold_type);
|
||||
NpadHoldType GetHoldType() const;
|
||||
@@ -123,12 +146,26 @@ public:
|
||||
void SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode);
|
||||
NpadHandheldActivationMode GetNpadHandheldActivationMode() const;
|
||||
|
||||
void SetNpadMode(u32 npad_id, NPadAssignments assignment_mode);
|
||||
void SetNpadMode(u32 npad_id, NpadAssignments assignment_mode);
|
||||
|
||||
void VibrateController(const std::vector<u32>& controllers,
|
||||
const std::vector<Vibration>& vibrations);
|
||||
bool VibrateControllerAtIndex(std::size_t npad_index, std::size_t device_index,
|
||||
const VibrationValue& vibration_value);
|
||||
|
||||
Vibration GetLastVibration() const;
|
||||
void VibrateController(const DeviceHandle& vibration_device_handle,
|
||||
const VibrationValue& vibration_value);
|
||||
|
||||
void VibrateControllers(const std::vector<DeviceHandle>& vibration_device_handles,
|
||||
const std::vector<VibrationValue>& vibration_values);
|
||||
|
||||
VibrationValue GetLastVibration(const DeviceHandle& vibration_device_handle) const;
|
||||
|
||||
void InitializeVibrationDevice(const DeviceHandle& vibration_device_handle);
|
||||
|
||||
void InitializeVibrationDeviceAtIndex(std::size_t npad_index, std::size_t device_index);
|
||||
|
||||
void SetPermitVibrationSession(bool permit_vibration_session);
|
||||
|
||||
bool IsVibrationDeviceMounted(const DeviceHandle& vibration_device_handle) const;
|
||||
|
||||
std::shared_ptr<Kernel::ReadableEvent> GetStyleSetChangedEvent(u32 npad_id) const;
|
||||
void SignalStyleSetChangedEvent(u32 npad_id) const;
|
||||
@@ -138,8 +175,8 @@ public:
|
||||
// Adds a new controller at an index with connection status.
|
||||
void UpdateControllerAt(NPadControllerType controller, std::size_t npad_index, bool connected);
|
||||
|
||||
void DisconnectNPad(u32 npad_id);
|
||||
void DisconnectNPadAtIndex(std::size_t index);
|
||||
void DisconnectNpad(u32 npad_id);
|
||||
void DisconnectNpadAtIndex(std::size_t index);
|
||||
|
||||
void SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode);
|
||||
GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode() const;
|
||||
@@ -148,8 +185,6 @@ public:
|
||||
LedPattern GetLedPattern(u32 npad_id);
|
||||
bool IsUnintendedHomeButtonInputProtectionEnabled(u32 npad_id) const;
|
||||
void SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled, u32 npad_id);
|
||||
void SetVibrationEnabled(bool can_vibrate);
|
||||
bool IsVibrationEnabled() const;
|
||||
void ClearAllConnectedControllers();
|
||||
void DisconnectAllConnectedControllers();
|
||||
void ConnectAllDisconnectedControllers();
|
||||
@@ -324,8 +359,8 @@ private:
|
||||
};
|
||||
|
||||
struct NPadEntry {
|
||||
NPadType joy_styles;
|
||||
NPadAssignments pad_assignment;
|
||||
NpadStyleSet joy_styles;
|
||||
NpadAssignments pad_assignment;
|
||||
|
||||
ColorReadError single_color_error;
|
||||
ControllerColor single_color;
|
||||
@@ -368,7 +403,7 @@ private:
|
||||
|
||||
u32 press_state{};
|
||||
|
||||
NPadType style{};
|
||||
NpadStyleSet style{};
|
||||
std::array<NPadEntry, 10> shared_memory_entries{};
|
||||
using ButtonArray = std::array<
|
||||
std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>,
|
||||
@@ -376,22 +411,28 @@ private:
|
||||
using StickArray = std::array<
|
||||
std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>,
|
||||
10>;
|
||||
using VibrationArray = std::array<std::array<std::unique_ptr<Input::VibrationDevice>,
|
||||
Settings::NativeVibration::NUM_VIBRATIONS_HID>,
|
||||
10>;
|
||||
using MotionArray = std::array<
|
||||
std::array<std::unique_ptr<Input::MotionDevice>, Settings::NativeMotion::NUM_MOTION_HID>,
|
||||
std::array<std::unique_ptr<Input::MotionDevice>, Settings::NativeMotion::NUM_MOTIONS_HID>,
|
||||
10>;
|
||||
ButtonArray buttons;
|
||||
StickArray sticks;
|
||||
VibrationArray vibrations;
|
||||
MotionArray motions;
|
||||
std::vector<u32> supported_npad_id_types{};
|
||||
NpadHoldType hold_type{NpadHoldType::Vertical};
|
||||
NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual};
|
||||
// Each controller should have their own styleset changed event
|
||||
std::array<Kernel::EventPair, 10> styleset_changed_events;
|
||||
Vibration last_processed_vibration{};
|
||||
std::array<std::array<std::chrono::steady_clock::time_point, 2>, 10> last_vibration_timepoints;
|
||||
std::array<std::array<VibrationValue, 2>, 10> latest_vibration_values{};
|
||||
bool permit_vibration_session_enabled{false};
|
||||
std::array<std::array<bool, 2>, 10> vibration_devices_mounted{};
|
||||
std::array<ControllerHolder, 10> connected_controllers{};
|
||||
std::array<bool, 10> unintended_home_button_input_protection{};
|
||||
GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard};
|
||||
bool can_controllers_vibrate{true};
|
||||
bool sixaxis_sensors_enabled{true};
|
||||
bool sixaxis_at_rest{true};
|
||||
std::array<ControllerPad, 10> npad_pad_states{};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -86,17 +86,15 @@ public:
|
||||
|
||||
private:
|
||||
void CreateAppletResource(Kernel::HLERequestContext& ctx);
|
||||
void ActivateXpad(Kernel::HLERequestContext& ctx);
|
||||
void GetXpadIDs(Kernel::HLERequestContext& ctx);
|
||||
void ActivateSixAxisSensor(Kernel::HLERequestContext& ctx);
|
||||
void DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx);
|
||||
void ActivateDebugPad(Kernel::HLERequestContext& ctx);
|
||||
void ActivateTouchScreen(Kernel::HLERequestContext& ctx);
|
||||
void ActivateMouse(Kernel::HLERequestContext& ctx);
|
||||
void ActivateKeyboard(Kernel::HLERequestContext& ctx);
|
||||
void SendKeyboardLockKeyEvent(Kernel::HLERequestContext& ctx);
|
||||
void ActivateGesture(Kernel::HLERequestContext& ctx);
|
||||
void ActivateNpadWithRevision(Kernel::HLERequestContext& ctx);
|
||||
void ActivateXpad(Kernel::HLERequestContext& ctx);
|
||||
void GetXpadIDs(Kernel::HLERequestContext& ctx);
|
||||
void ActivateSixAxisSensor(Kernel::HLERequestContext& ctx);
|
||||
void DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx);
|
||||
void StartSixAxisSensor(Kernel::HLERequestContext& ctx);
|
||||
void StopSixAxisSensor(Kernel::HLERequestContext& ctx);
|
||||
void EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx);
|
||||
@@ -104,6 +102,7 @@ private:
|
||||
void GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx);
|
||||
void ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx);
|
||||
void IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx);
|
||||
void ActivateGesture(Kernel::HLERequestContext& ctx);
|
||||
void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx);
|
||||
void GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx);
|
||||
void SetSupportedNpadIdType(Kernel::HLERequestContext& ctx);
|
||||
@@ -112,6 +111,7 @@ private:
|
||||
void AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx);
|
||||
void DisconnectNpad(Kernel::HLERequestContext& ctx);
|
||||
void GetPlayerLedPattern(Kernel::HLERequestContext& ctx);
|
||||
void ActivateNpadWithRevision(Kernel::HLERequestContext& ctx);
|
||||
void SetNpadJoyHoldType(Kernel::HLERequestContext& ctx);
|
||||
void GetNpadJoyHoldType(Kernel::HLERequestContext& ctx);
|
||||
void SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx);
|
||||
@@ -125,15 +125,16 @@ private:
|
||||
void SwapNpadAssignment(Kernel::HLERequestContext& ctx);
|
||||
void IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext& ctx);
|
||||
void EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& ctx);
|
||||
void BeginPermitVibrationSession(Kernel::HLERequestContext& ctx);
|
||||
void EndPermitVibrationSession(Kernel::HLERequestContext& ctx);
|
||||
void SendVibrationValue(Kernel::HLERequestContext& ctx);
|
||||
void SendVibrationValues(Kernel::HLERequestContext& ctx);
|
||||
void GetActualVibrationValue(Kernel::HLERequestContext& ctx);
|
||||
void GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx);
|
||||
void SendVibrationValue(Kernel::HLERequestContext& ctx);
|
||||
void GetActualVibrationValue(Kernel::HLERequestContext& ctx);
|
||||
void CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx);
|
||||
void PermitVibration(Kernel::HLERequestContext& ctx);
|
||||
void IsVibrationPermitted(Kernel::HLERequestContext& ctx);
|
||||
void SendVibrationValues(Kernel::HLERequestContext& ctx);
|
||||
void BeginPermitVibrationSession(Kernel::HLERequestContext& ctx);
|
||||
void EndPermitVibrationSession(Kernel::HLERequestContext& ctx);
|
||||
void IsVibrationDeviceMounted(Kernel::HLERequestContext& ctx);
|
||||
void ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx);
|
||||
void StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx);
|
||||
void StopConsoleSixAxisSensor(Kernel::HLERequestContext& ctx);
|
||||
@@ -146,6 +147,22 @@ private:
|
||||
void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx);
|
||||
void SetPalmaBoostMode(Kernel::HLERequestContext& ctx);
|
||||
|
||||
enum class VibrationDeviceType : u32 {
|
||||
LinearResonantActuator = 1,
|
||||
};
|
||||
|
||||
enum class VibrationDevicePosition : u32 {
|
||||
None = 0,
|
||||
Left = 1,
|
||||
Right = 2,
|
||||
};
|
||||
|
||||
struct VibrationDeviceInfo {
|
||||
VibrationDeviceType type{};
|
||||
VibrationDevicePosition position{};
|
||||
};
|
||||
static_assert(sizeof(VibrationDeviceInfo) == 0x8, "VibrationDeviceInfo has incorrect size.");
|
||||
|
||||
std::shared_ptr<IAppletResource> applet_resource;
|
||||
Core::System& system;
|
||||
};
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "common/alignment.h"
|
||||
#include "common/hex_util.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/errors.h"
|
||||
#include "core/hle/kernel/memory/page_table.h"
|
||||
@@ -23,7 +24,7 @@ namespace Service::LDR {
|
||||
|
||||
constexpr ResultCode ERROR_INSUFFICIENT_ADDRESS_SPACE{ErrorModule::RO, 2};
|
||||
|
||||
constexpr ResultCode ERROR_INVALID_MEMORY_STATE{ErrorModule::Loader, 51};
|
||||
[[maybe_unused]] constexpr ResultCode ERROR_INVALID_MEMORY_STATE{ErrorModule::Loader, 51};
|
||||
constexpr ResultCode ERROR_INVALID_NRO{ErrorModule::Loader, 52};
|
||||
constexpr ResultCode ERROR_INVALID_NRR{ErrorModule::Loader, 53};
|
||||
constexpr ResultCode ERROR_MISSING_NRR_HASH{ErrorModule::Loader, 54};
|
||||
@@ -33,7 +34,7 @@ constexpr ResultCode ERROR_ALREADY_LOADED{ErrorModule::Loader, 57};
|
||||
constexpr ResultCode ERROR_INVALID_ALIGNMENT{ErrorModule::Loader, 81};
|
||||
constexpr ResultCode ERROR_INVALID_SIZE{ErrorModule::Loader, 82};
|
||||
constexpr ResultCode ERROR_INVALID_NRO_ADDRESS{ErrorModule::Loader, 84};
|
||||
constexpr ResultCode ERROR_INVALID_NRR_ADDRESS{ErrorModule::Loader, 85};
|
||||
[[maybe_unused]] constexpr ResultCode ERROR_INVALID_NRR_ADDRESS{ErrorModule::Loader, 85};
|
||||
constexpr ResultCode ERROR_NOT_INITIALIZED{ErrorModule::Loader, 87};
|
||||
|
||||
constexpr std::size_t MAXIMUM_LOADED_RO{0x40};
|
||||
@@ -166,7 +167,7 @@ public:
|
||||
{0, &RelocatableObject::LoadNro, "LoadNro"},
|
||||
{1, &RelocatableObject::UnloadNro, "UnloadNro"},
|
||||
{2, &RelocatableObject::LoadNrr, "LoadNrr"},
|
||||
{3, nullptr, "UnloadNrr"},
|
||||
{3, &RelocatableObject::UnloadNrr, "UnloadNrr"},
|
||||
{4, &RelocatableObject::Initialize, "Initialize"},
|
||||
{10, nullptr, "LoadNrrEx"},
|
||||
};
|
||||
@@ -272,6 +273,20 @@ public:
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void UnloadNrr(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto pid = rp.Pop<u64>();
|
||||
const auto nrr_address = rp.Pop<VAddr>();
|
||||
|
||||
LOG_DEBUG(Service_LDR, "called with pid={}, nrr_address={:016X}", pid, nrr_address);
|
||||
|
||||
nrr.erase(nrr_address);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
bool ValidateRegionForMap(Kernel::Memory::PageTable& page_table, VAddr start,
|
||||
std::size_t size) const {
|
||||
constexpr std::size_t padding_size{4 * Kernel::Memory::PageSize};
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/service/lm/lm.h"
|
||||
#include "core/hle/service/lm/manager.h"
|
||||
|
||||
@@ -131,7 +131,7 @@ template <typename T>
|
||||
T GetRandomValue(T min, T max) {
|
||||
std::random_device device;
|
||||
std::mt19937 gen(device());
|
||||
std::uniform_int_distribution<u64> distribution(0, static_cast<u64>(max));
|
||||
std::uniform_int_distribution<u64> distribution(static_cast<u64>(min), static_cast<u64>(max));
|
||||
return static_cast<T>(distribution(gen));
|
||||
}
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ public:
|
||||
{23, nullptr, "Convert"},
|
||||
{24, nullptr, "ConvertCoreDataToCharInfo"},
|
||||
{25, nullptr, "ConvertCharInfoToCoreData"},
|
||||
{26, nullptr, "Append"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ public:
|
||||
{18, nullptr, "SetRequirementByRevision"},
|
||||
{19, nullptr, "GetRequirement"},
|
||||
{20, nullptr, "GetRevision"},
|
||||
{21, nullptr, "GetAppletInfo"},
|
||||
{21, &IRequest::GetAppletInfo, "GetAppletInfo"},
|
||||
{22, nullptr, "GetAdditionalInfo"},
|
||||
{23, nullptr, "SetKeptInSleep"},
|
||||
{24, nullptr, "RegisterSocketDescriptor"},
|
||||
@@ -125,6 +125,16 @@ private:
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void GetAppletInfo(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_NIFM, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 8};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(0);
|
||||
rb.Push<u32>(0);
|
||||
rb.Push<u32>(0);
|
||||
}
|
||||
|
||||
Kernel::EventPair event1, event2;
|
||||
};
|
||||
|
||||
|
||||
@@ -50,19 +50,9 @@ constexpr std::array<std::pair<FontArchives, const char*>, 7> SHARED_FONTS{
|
||||
std::make_pair(FontArchives::Extension, "nintendo_ext2_003.bfttf"),
|
||||
};
|
||||
|
||||
constexpr std::array<const char*, 7> SHARED_FONTS_TTF{
|
||||
"FontStandard.ttf",
|
||||
"FontChineseSimplified.ttf",
|
||||
"FontExtendedChineseSimplified.ttf",
|
||||
"FontChineseTraditional.ttf",
|
||||
"FontKorean.ttf",
|
||||
"FontNintendoExtended.ttf",
|
||||
"FontNintendoExtended2.ttf",
|
||||
};
|
||||
|
||||
// The below data is specific to shared font data dumped from Switch on f/w 2.2
|
||||
// Virtual address and offsets/sizes likely will vary by dump
|
||||
constexpr VAddr SHARED_FONT_MEM_VADDR{0x00000009d3016000ULL};
|
||||
[[maybe_unused]] constexpr VAddr SHARED_FONT_MEM_VADDR{0x00000009d3016000ULL};
|
||||
constexpr u32 EXPECTED_RESULT{0x7f9a0218}; // What we expect the decrypted bfttf first 4 bytes to be
|
||||
constexpr u32 EXPECTED_MAGIC{0x36f81a1e}; // What we expect the encrypted bfttf first 4 bytes to be
|
||||
constexpr u64 SHARED_FONT_MEM_SIZE{0x1100000};
|
||||
|
||||
@@ -46,6 +46,8 @@ u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, const std:
|
||||
return GetVARegions(input, output);
|
||||
case IoctlCommand::IocUnmapBufferCommand:
|
||||
return UnmapBuffer(input, output);
|
||||
case IoctlCommand::IocFreeSpaceCommand:
|
||||
return FreeSpace(input, output);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -91,6 +93,20 @@ u32 nvhost_as_gpu::AllocateSpace(const std::vector<u8>& input, std::vector<u8>&
|
||||
return result;
|
||||
}
|
||||
|
||||
u32 nvhost_as_gpu::FreeSpace(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
IoctlFreeSpace params{};
|
||||
std::memcpy(¶ms, input.data(), input.size());
|
||||
|
||||
LOG_DEBUG(Service_NVDRV, "called, offset={:X}, pages={:X}, page_size={:X}", params.offset,
|
||||
params.pages, params.page_size);
|
||||
|
||||
system.GPU().MemoryManager().Unmap(params.offset,
|
||||
static_cast<std::size_t>(params.pages) * params.page_size);
|
||||
|
||||
std::memcpy(output.data(), ¶ms, output.size());
|
||||
return NvErrCodes::Success;
|
||||
}
|
||||
|
||||
u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
const auto num_entries = input.size() / sizeof(IoctlRemapEntry);
|
||||
|
||||
|
||||
@@ -82,6 +82,7 @@ private:
|
||||
IocBindChannelCommand = 0x40044101,
|
||||
IocGetVaRegionsCommand = 0xC0404108,
|
||||
IocUnmapBufferCommand = 0xC0084105,
|
||||
IocFreeSpaceCommand = 0xC0104103,
|
||||
};
|
||||
|
||||
struct IoctlInitalizeEx {
|
||||
@@ -107,6 +108,13 @@ private:
|
||||
};
|
||||
static_assert(sizeof(IoctlAllocSpace) == 24, "IoctlInitalizeEx is incorrect size");
|
||||
|
||||
struct IoctlFreeSpace {
|
||||
u64_le offset;
|
||||
u32_le pages;
|
||||
u32_le page_size;
|
||||
};
|
||||
static_assert(sizeof(IoctlFreeSpace) == 16, "IoctlFreeSpace is incorrect size");
|
||||
|
||||
struct IoctlRemapEntry {
|
||||
u16_le flags;
|
||||
u16_le kind;
|
||||
@@ -162,6 +170,7 @@ private:
|
||||
u32 Remap(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 FreeSpace(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 BindChannel(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 GetVARegions(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
|
||||
|
||||
@@ -15,8 +15,9 @@
|
||||
|
||||
namespace Service::Nvidia::Devices {
|
||||
|
||||
nvhost_ctrl::nvhost_ctrl(Core::System& system, EventInterface& events_interface)
|
||||
: nvdevice(system), events_interface{events_interface} {}
|
||||
nvhost_ctrl::nvhost_ctrl(Core::System& system, EventInterface& events_interface,
|
||||
SyncpointManager& syncpoint_manager)
|
||||
: nvdevice(system), events_interface{events_interface}, syncpoint_manager{syncpoint_manager} {}
|
||||
nvhost_ctrl::~nvhost_ctrl() = default;
|
||||
|
||||
u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
|
||||
@@ -36,8 +37,8 @@ u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, const std::v
|
||||
return IocCtrlEventRegister(input, output);
|
||||
case IoctlCommand::IocCtrlEventUnregisterCommand:
|
||||
return IocCtrlEventUnregister(input, output);
|
||||
case IoctlCommand::IocCtrlEventSignalCommand:
|
||||
return IocCtrlEventSignal(input, output);
|
||||
case IoctlCommand::IocCtrlClearEventWaitCommand:
|
||||
return IocCtrlClearEventWait(input, output);
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented ioctl");
|
||||
return 0;
|
||||
@@ -70,19 +71,33 @@ u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>&
|
||||
return NvResult::BadParameter;
|
||||
}
|
||||
|
||||
if (syncpoint_manager.IsSyncpointExpired(params.syncpt_id, params.threshold)) {
|
||||
params.value = syncpoint_manager.GetSyncpointMin(params.syncpt_id);
|
||||
std::memcpy(output.data(), ¶ms, sizeof(params));
|
||||
return NvResult::Success;
|
||||
}
|
||||
|
||||
if (const auto new_value = syncpoint_manager.RefreshSyncpoint(params.syncpt_id);
|
||||
syncpoint_manager.IsSyncpointExpired(params.syncpt_id, params.threshold)) {
|
||||
params.value = new_value;
|
||||
std::memcpy(output.data(), ¶ms, sizeof(params));
|
||||
return NvResult::Success;
|
||||
}
|
||||
|
||||
auto event = events_interface.events[event_id];
|
||||
auto& gpu = system.GPU();
|
||||
|
||||
// This is mostly to take into account unimplemented features. As synced
|
||||
// gpu is always synced.
|
||||
if (!gpu.IsAsync()) {
|
||||
event.writable->Signal();
|
||||
event.event.writable->Signal();
|
||||
return NvResult::Success;
|
||||
}
|
||||
auto lock = gpu.LockSync();
|
||||
const u32 current_syncpoint_value = gpu.GetSyncpointValue(params.syncpt_id);
|
||||
const u32 current_syncpoint_value = event.fence.value;
|
||||
const s32 diff = current_syncpoint_value - params.threshold;
|
||||
if (diff >= 0) {
|
||||
event.writable->Signal();
|
||||
event.event.writable->Signal();
|
||||
params.value = current_syncpoint_value;
|
||||
std::memcpy(output.data(), ¶ms, sizeof(params));
|
||||
return NvResult::Success;
|
||||
@@ -109,7 +124,7 @@ u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>&
|
||||
params.value = ((params.syncpt_id & 0xfff) << 16) | 0x10000000;
|
||||
}
|
||||
params.value |= event_id;
|
||||
event.writable->Clear();
|
||||
event.event.writable->Clear();
|
||||
gpu.RegisterSyncptInterrupt(params.syncpt_id, target_value);
|
||||
if (!is_async && ctrl.fresh_call) {
|
||||
ctrl.must_delay = true;
|
||||
@@ -154,24 +169,22 @@ u32 nvhost_ctrl::IocCtrlEventUnregister(const std::vector<u8>& input, std::vecto
|
||||
return NvResult::Success;
|
||||
}
|
||||
|
||||
u32 nvhost_ctrl::IocCtrlEventSignal(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
u32 nvhost_ctrl::IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
IocCtrlEventSignalParams params{};
|
||||
std::memcpy(¶ms, input.data(), sizeof(params));
|
||||
// TODO(Blinkhawk): This is normally called when an NvEvents timeout on WaitSynchronization
|
||||
// It is believed from RE to cancel the GPU Event. However, better research is required
|
||||
u32 event_id = params.user_event_id & 0x00FF;
|
||||
LOG_WARNING(Service_NVDRV, "(STUBBED) called, user_event_id: {:X}", event_id);
|
||||
|
||||
u32 event_id = params.event_id & 0x00FF;
|
||||
LOG_WARNING(Service_NVDRV, "cleared event wait on, event_id: {:X}", event_id);
|
||||
|
||||
if (event_id >= MaxNvEvents) {
|
||||
return NvResult::BadParameter;
|
||||
}
|
||||
if (events_interface.status[event_id] == EventState::Waiting) {
|
||||
auto& gpu = system.GPU();
|
||||
if (gpu.CancelSyncptInterrupt(events_interface.assigned_syncpt[event_id],
|
||||
events_interface.assigned_value[event_id])) {
|
||||
events_interface.LiberateEvent(event_id);
|
||||
events_interface.events[event_id].writable->Signal();
|
||||
}
|
||||
events_interface.LiberateEvent(event_id);
|
||||
}
|
||||
|
||||
syncpoint_manager.RefreshSyncpoint(events_interface.events[event_id].fence.id);
|
||||
|
||||
return NvResult::Success;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,8 @@ namespace Service::Nvidia::Devices {
|
||||
|
||||
class nvhost_ctrl final : public nvdevice {
|
||||
public:
|
||||
explicit nvhost_ctrl(Core::System& system, EventInterface& events_interface);
|
||||
explicit nvhost_ctrl(Core::System& system, EventInterface& events_interface,
|
||||
SyncpointManager& syncpoint_manager);
|
||||
~nvhost_ctrl() override;
|
||||
|
||||
u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
|
||||
@@ -31,7 +32,7 @@ private:
|
||||
IocSyncptWaitexCommand = 0xC0100019,
|
||||
IocSyncptReadMaxCommand = 0xC008001A,
|
||||
IocGetConfigCommand = 0xC183001B,
|
||||
IocCtrlEventSignalCommand = 0xC004001C,
|
||||
IocCtrlClearEventWaitCommand = 0xC004001C,
|
||||
IocCtrlEventWaitCommand = 0xC010001D,
|
||||
IocCtrlEventWaitAsyncCommand = 0xC010001E,
|
||||
IocCtrlEventRegisterCommand = 0xC004001F,
|
||||
@@ -94,7 +95,7 @@ private:
|
||||
static_assert(sizeof(IocGetConfigParams) == 387, "IocGetConfigParams is incorrect size");
|
||||
|
||||
struct IocCtrlEventSignalParams {
|
||||
u32_le user_event_id;
|
||||
u32_le event_id;
|
||||
};
|
||||
static_assert(sizeof(IocCtrlEventSignalParams) == 4,
|
||||
"IocCtrlEventSignalParams is incorrect size");
|
||||
@@ -142,9 +143,10 @@ private:
|
||||
|
||||
u32 IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
|
||||
u32 IocCtrlEventSignal(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
|
||||
EventInterface& events_interface;
|
||||
SyncpointManager& syncpoint_manager;
|
||||
};
|
||||
|
||||
} // namespace Service::Nvidia::Devices
|
||||
|
||||
@@ -7,14 +7,20 @@
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/nvdrv/devices/nvhost_gpu.h"
|
||||
#include "core/hle/service/nvdrv/syncpoint_manager.h"
|
||||
#include "core/memory.h"
|
||||
#include "video_core/gpu.h"
|
||||
#include "video_core/memory_manager.h"
|
||||
|
||||
namespace Service::Nvidia::Devices {
|
||||
|
||||
nvhost_gpu::nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev)
|
||||
: nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
|
||||
nvhost_gpu::nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev,
|
||||
SyncpointManager& syncpoint_manager)
|
||||
: nvdevice(system), nvmap_dev(std::move(nvmap_dev)), syncpoint_manager{syncpoint_manager} {
|
||||
channel_fence.id = syncpoint_manager.AllocateSyncpoint();
|
||||
channel_fence.value = system.GPU().GetSyncpointValue(channel_fence.id);
|
||||
}
|
||||
|
||||
nvhost_gpu::~nvhost_gpu() = default;
|
||||
|
||||
u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
|
||||
@@ -126,10 +132,10 @@ u32 nvhost_gpu::AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& ou
|
||||
params.num_entries, params.flags, params.unk0, params.unk1, params.unk2,
|
||||
params.unk3);
|
||||
|
||||
auto& gpu = system.GPU();
|
||||
params.fence_out.id = assigned_syncpoints;
|
||||
params.fence_out.value = gpu.GetSyncpointValue(assigned_syncpoints);
|
||||
assigned_syncpoints++;
|
||||
channel_fence.value = system.GPU().GetSyncpointValue(channel_fence.id);
|
||||
|
||||
params.fence_out = channel_fence;
|
||||
|
||||
std::memcpy(output.data(), ¶ms, output.size());
|
||||
return 0;
|
||||
}
|
||||
@@ -145,37 +151,97 @@ u32 nvhost_gpu::AllocateObjectContext(const std::vector<u8>& input, std::vector<
|
||||
return 0;
|
||||
}
|
||||
|
||||
static std::vector<Tegra::CommandHeader> BuildWaitCommandList(Fence fence) {
|
||||
return {
|
||||
Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceValue, 1,
|
||||
Tegra::SubmissionMode::Increasing),
|
||||
{fence.value},
|
||||
Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceAction, 1,
|
||||
Tegra::SubmissionMode::Increasing),
|
||||
Tegra::GPU::FenceAction::Build(Tegra::GPU::FenceOperation::Acquire, fence.id),
|
||||
};
|
||||
}
|
||||
|
||||
static std::vector<Tegra::CommandHeader> BuildIncrementCommandList(Fence fence, u32 add_increment) {
|
||||
std::vector<Tegra::CommandHeader> result{
|
||||
Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceValue, 1,
|
||||
Tegra::SubmissionMode::Increasing),
|
||||
{}};
|
||||
|
||||
for (u32 count = 0; count < add_increment; ++count) {
|
||||
result.emplace_back(Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceAction, 1,
|
||||
Tegra::SubmissionMode::Increasing));
|
||||
result.emplace_back(
|
||||
Tegra::GPU::FenceAction::Build(Tegra::GPU::FenceOperation::Increment, fence.id));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static std::vector<Tegra::CommandHeader> BuildIncrementWithWfiCommandList(Fence fence,
|
||||
u32 add_increment) {
|
||||
std::vector<Tegra::CommandHeader> result{
|
||||
Tegra::BuildCommandHeader(Tegra::BufferMethods::WaitForInterrupt, 1,
|
||||
Tegra::SubmissionMode::Increasing),
|
||||
{}};
|
||||
const std::vector<Tegra::CommandHeader> increment{
|
||||
BuildIncrementCommandList(fence, add_increment)};
|
||||
|
||||
result.insert(result.end(), increment.begin(), increment.end());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
u32 nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8>& output,
|
||||
Tegra::CommandList&& entries) {
|
||||
LOG_TRACE(Service_NVDRV, "called, gpfifo={:X}, num_entries={:X}, flags={:X}", params.address,
|
||||
params.num_entries, params.flags.raw);
|
||||
|
||||
auto& gpu = system.GPU();
|
||||
|
||||
params.fence_out.id = channel_fence.id;
|
||||
|
||||
if (params.flags.add_wait.Value() &&
|
||||
!syncpoint_manager.IsSyncpointExpired(params.fence_out.id, params.fence_out.value)) {
|
||||
gpu.PushGPUEntries(Tegra::CommandList{BuildWaitCommandList(params.fence_out)});
|
||||
}
|
||||
|
||||
if (params.flags.add_increment.Value() || params.flags.increment.Value()) {
|
||||
const u32 increment_value = params.flags.increment.Value() ? params.fence_out.value : 0;
|
||||
params.fence_out.value = syncpoint_manager.IncreaseSyncpoint(
|
||||
params.fence_out.id, params.AddIncrementValue() + increment_value);
|
||||
} else {
|
||||
params.fence_out.value = syncpoint_manager.GetSyncpointMax(params.fence_out.id);
|
||||
}
|
||||
|
||||
gpu.PushGPUEntries(std::move(entries));
|
||||
|
||||
if (params.flags.add_increment.Value()) {
|
||||
if (params.flags.suppress_wfi) {
|
||||
gpu.PushGPUEntries(Tegra::CommandList{
|
||||
BuildIncrementCommandList(params.fence_out, params.AddIncrementValue())});
|
||||
} else {
|
||||
gpu.PushGPUEntries(Tegra::CommandList{
|
||||
BuildIncrementWithWfiCommandList(params.fence_out, params.AddIncrementValue())});
|
||||
}
|
||||
}
|
||||
|
||||
std::memcpy(output.data(), ¶ms, sizeof(IoctlSubmitGpfifo));
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
if (input.size() < sizeof(IoctlSubmitGpfifo)) {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
IoctlSubmitGpfifo params{};
|
||||
std::memcpy(¶ms, input.data(), sizeof(IoctlSubmitGpfifo));
|
||||
LOG_TRACE(Service_NVDRV, "called, gpfifo={:X}, num_entries={:X}, flags={:X}", params.address,
|
||||
params.num_entries, params.flags.raw);
|
||||
|
||||
ASSERT_MSG(input.size() == sizeof(IoctlSubmitGpfifo) +
|
||||
params.num_entries * sizeof(Tegra::CommandListHeader),
|
||||
"Incorrect input size");
|
||||
|
||||
Tegra::CommandList entries(params.num_entries);
|
||||
std::memcpy(entries.data(), &input[sizeof(IoctlSubmitGpfifo)],
|
||||
std::memcpy(entries.command_lists.data(), &input[sizeof(IoctlSubmitGpfifo)],
|
||||
params.num_entries * sizeof(Tegra::CommandListHeader));
|
||||
|
||||
UNIMPLEMENTED_IF(params.flags.add_wait.Value() != 0);
|
||||
UNIMPLEMENTED_IF(params.flags.add_increment.Value() != 0);
|
||||
|
||||
auto& gpu = system.GPU();
|
||||
u32 current_syncpoint_value = gpu.GetSyncpointValue(params.fence_out.id);
|
||||
if (params.flags.increment.Value()) {
|
||||
params.fence_out.value += current_syncpoint_value;
|
||||
} else {
|
||||
params.fence_out.value = current_syncpoint_value;
|
||||
}
|
||||
gpu.PushGPUEntries(std::move(entries));
|
||||
|
||||
std::memcpy(output.data(), ¶ms, sizeof(IoctlSubmitGpfifo));
|
||||
return 0;
|
||||
return SubmitGPFIFOImpl(params, output, std::move(entries));
|
||||
}
|
||||
|
||||
u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output,
|
||||
@@ -185,31 +251,17 @@ u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output,
|
||||
}
|
||||
IoctlSubmitGpfifo params{};
|
||||
std::memcpy(¶ms, input.data(), sizeof(IoctlSubmitGpfifo));
|
||||
LOG_TRACE(Service_NVDRV, "called, gpfifo={:X}, num_entries={:X}, flags={:X}", params.address,
|
||||
params.num_entries, params.flags.raw);
|
||||
|
||||
Tegra::CommandList entries(params.num_entries);
|
||||
if (version == IoctlVersion::Version2) {
|
||||
std::memcpy(entries.data(), input2.data(),
|
||||
std::memcpy(entries.command_lists.data(), input2.data(),
|
||||
params.num_entries * sizeof(Tegra::CommandListHeader));
|
||||
} else {
|
||||
system.Memory().ReadBlock(params.address, entries.data(),
|
||||
system.Memory().ReadBlock(params.address, entries.command_lists.data(),
|
||||
params.num_entries * sizeof(Tegra::CommandListHeader));
|
||||
}
|
||||
UNIMPLEMENTED_IF(params.flags.add_wait.Value() != 0);
|
||||
UNIMPLEMENTED_IF(params.flags.add_increment.Value() != 0);
|
||||
|
||||
auto& gpu = system.GPU();
|
||||
u32 current_syncpoint_value = gpu.GetSyncpointValue(params.fence_out.id);
|
||||
if (params.flags.increment.Value()) {
|
||||
params.fence_out.value += current_syncpoint_value;
|
||||
} else {
|
||||
params.fence_out.value = current_syncpoint_value;
|
||||
}
|
||||
gpu.PushGPUEntries(std::move(entries));
|
||||
|
||||
std::memcpy(output.data(), ¶ms, output.size());
|
||||
return 0;
|
||||
return SubmitGPFIFOImpl(params, output, std::move(entries));
|
||||
}
|
||||
|
||||
u32 nvhost_gpu::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
|
||||
@@ -11,6 +11,11 @@
|
||||
#include "common/swap.h"
|
||||
#include "core/hle/service/nvdrv/devices/nvdevice.h"
|
||||
#include "core/hle/service/nvdrv/nvdata.h"
|
||||
#include "video_core/dma_pusher.h"
|
||||
|
||||
namespace Service::Nvidia {
|
||||
class SyncpointManager;
|
||||
}
|
||||
|
||||
namespace Service::Nvidia::Devices {
|
||||
|
||||
@@ -21,7 +26,8 @@ constexpr u32 NVGPU_IOCTL_CHANNEL_KICKOFF_PB(0x1b);
|
||||
|
||||
class nvhost_gpu final : public nvdevice {
|
||||
public:
|
||||
explicit nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
|
||||
explicit nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev,
|
||||
SyncpointManager& syncpoint_manager);
|
||||
~nvhost_gpu() override;
|
||||
|
||||
u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
|
||||
@@ -162,10 +168,15 @@ private:
|
||||
u32_le raw;
|
||||
BitField<0, 1, u32_le> add_wait; // append a wait sync_point to the list
|
||||
BitField<1, 1, u32_le> add_increment; // append an increment to the list
|
||||
BitField<2, 1, u32_le> new_hw_format; // Mostly ignored
|
||||
BitField<2, 1, u32_le> new_hw_format; // mostly ignored
|
||||
BitField<4, 1, u32_le> suppress_wfi; // suppress wait for interrupt
|
||||
BitField<8, 1, u32_le> increment; // increment the returned fence
|
||||
} flags;
|
||||
Fence fence_out; // returned new fence object for others to wait on
|
||||
|
||||
u32 AddIncrementValue() const {
|
||||
return flags.add_increment.Value() << 1;
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(IoctlSubmitGpfifo) == 16 + sizeof(Fence),
|
||||
"IoctlSubmitGpfifo is incorrect size");
|
||||
@@ -190,6 +201,8 @@ private:
|
||||
u32 SetChannelPriority(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8>& output,
|
||||
Tegra::CommandList&& entries);
|
||||
u32 SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 KickoffPB(const std::vector<u8>& input, std::vector<u8>& output,
|
||||
const std::vector<u8>& input2, IoctlVersion version);
|
||||
@@ -198,7 +211,8 @@ private:
|
||||
u32 ChannelSetTimeslice(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
|
||||
std::shared_ptr<nvmap> nvmap_dev;
|
||||
u32 assigned_syncpoints{};
|
||||
SyncpointManager& syncpoint_manager;
|
||||
Fence channel_fence;
|
||||
};
|
||||
|
||||
} // namespace Service::Nvidia::Devices
|
||||
|
||||
@@ -2,15 +2,17 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/nvdrv/devices/nvhost_nvdec.h"
|
||||
#include "video_core/memory_manager.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
|
||||
namespace Service::Nvidia::Devices {
|
||||
|
||||
nvhost_nvdec::nvhost_nvdec(Core::System& system) : nvdevice(system) {}
|
||||
nvhost_nvdec::nvhost_nvdec(Core::System& system, std::shared_ptr<nvmap> nvmap_dev)
|
||||
: nvhost_nvdec_common(system, std::move(nvmap_dev)) {}
|
||||
nvhost_nvdec::~nvhost_nvdec() = default;
|
||||
|
||||
u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
|
||||
@@ -21,7 +23,7 @@ u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, const std::
|
||||
|
||||
switch (static_cast<IoctlCommand>(command.raw)) {
|
||||
case IoctlCommand::IocSetNVMAPfdCommand:
|
||||
return SetNVMAPfd(input, output);
|
||||
return SetNVMAPfd(input);
|
||||
case IoctlCommand::IocSubmit:
|
||||
return Submit(input, output);
|
||||
case IoctlCommand::IocGetSyncpoint:
|
||||
@@ -29,79 +31,29 @@ u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, const std::
|
||||
case IoctlCommand::IocGetWaitbase:
|
||||
return GetWaitbase(input, output);
|
||||
case IoctlCommand::IocMapBuffer:
|
||||
return MapBuffer(input, output);
|
||||
case IoctlCommand::IocMapBuffer2:
|
||||
case IoctlCommand::IocMapBuffer3:
|
||||
case IoctlCommand::IocMapBufferEx:
|
||||
return MapBufferEx(input, output);
|
||||
case IoctlCommand::IocUnmapBufferEx:
|
||||
return UnmapBufferEx(input, output);
|
||||
return MapBuffer(input, output);
|
||||
case IoctlCommand::IocUnmapBufferEx: {
|
||||
// This command is sent when the video stream has ended, flush all video contexts
|
||||
// This is usually sent in the folowing order: vic, nvdec, vic.
|
||||
// Inform the GPU to clear any remaining nvdec buffers when this is detected.
|
||||
LOG_INFO(Service_NVDRV, "NVDEC video stream ended");
|
||||
Tegra::ChCommandHeaderList cmdlist(1);
|
||||
cmdlist[0] = Tegra::ChCommandHeader{0xDEADB33F};
|
||||
system.GPU().PushCommandBuffer(cmdlist);
|
||||
[[fallthrough]]; // fallthrough to unmap buffers
|
||||
};
|
||||
case IoctlCommand::IocUnmapBuffer:
|
||||
case IoctlCommand::IocUnmapBuffer2:
|
||||
case IoctlCommand::IocUnmapBuffer3:
|
||||
return UnmapBuffer(input, output);
|
||||
case IoctlCommand::IocSetSubmitTimeout:
|
||||
return SetSubmitTimeout(input, output);
|
||||
}
|
||||
|
||||
UNIMPLEMENTED_MSG("Unimplemented ioctl");
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 nvhost_nvdec::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
IoctlSetNvmapFD params{};
|
||||
std::memcpy(¶ms, input.data(), sizeof(IoctlSetNvmapFD));
|
||||
LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd);
|
||||
|
||||
nvmap_fd = params.nvmap_fd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 nvhost_nvdec::Submit(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
IoctlSubmit params{};
|
||||
std::memcpy(¶ms, input.data(), sizeof(IoctlSubmit));
|
||||
LOG_WARNING(Service_NVDRV, "(STUBBED) called");
|
||||
std::memcpy(output.data(), ¶ms, sizeof(IoctlSubmit));
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 nvhost_nvdec::GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
IoctlGetSyncpoint params{};
|
||||
std::memcpy(¶ms, input.data(), sizeof(IoctlGetSyncpoint));
|
||||
LOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown);
|
||||
params.value = 0; // Seems to be hard coded at 0
|
||||
std::memcpy(output.data(), ¶ms, sizeof(IoctlGetSyncpoint));
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 nvhost_nvdec::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
IoctlGetWaitbase params{};
|
||||
std::memcpy(¶ms, input.data(), sizeof(IoctlGetWaitbase));
|
||||
LOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown);
|
||||
params.value = 0; // Seems to be hard coded at 0
|
||||
std::memcpy(output.data(), ¶ms, sizeof(IoctlGetWaitbase));
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 nvhost_nvdec::MapBuffer(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
IoctlMapBuffer params{};
|
||||
std::memcpy(¶ms, input.data(), sizeof(IoctlMapBuffer));
|
||||
LOG_WARNING(Service_NVDRV, "(STUBBED) called with address={:08X}{:08X}", params.address_2,
|
||||
params.address_1);
|
||||
params.address_1 = 0;
|
||||
params.address_2 = 0;
|
||||
std::memcpy(output.data(), ¶ms, sizeof(IoctlMapBuffer));
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 nvhost_nvdec::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
IoctlMapBufferEx params{};
|
||||
std::memcpy(¶ms, input.data(), sizeof(IoctlMapBufferEx));
|
||||
LOG_WARNING(Service_NVDRV, "(STUBBED) called with address={:08X}{:08X}", params.address_2,
|
||||
params.address_1);
|
||||
params.address_1 = 0;
|
||||
params.address_2 = 0;
|
||||
std::memcpy(output.data(), ¶ms, sizeof(IoctlMapBufferEx));
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 nvhost_nvdec::UnmapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
IoctlUnmapBufferEx params{};
|
||||
std::memcpy(¶ms, input.data(), sizeof(IoctlUnmapBufferEx));
|
||||
LOG_WARNING(Service_NVDRV, "(STUBBED) called");
|
||||
std::memcpy(output.data(), ¶ms, sizeof(IoctlUnmapBufferEx));
|
||||
UNIMPLEMENTED_MSG("Unimplemented ioctl 0x{:X}", command.raw);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,16 +4,14 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/hle/service/nvdrv/devices/nvdevice.h"
|
||||
#include <memory>
|
||||
#include "core/hle/service/nvdrv/devices/nvhost_nvdec_common.h"
|
||||
|
||||
namespace Service::Nvidia::Devices {
|
||||
|
||||
class nvhost_nvdec final : public nvdevice {
|
||||
class nvhost_nvdec final : public nvhost_nvdec_common {
|
||||
public:
|
||||
explicit nvhost_nvdec(Core::System& system);
|
||||
explicit nvhost_nvdec(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
|
||||
~nvhost_nvdec() override;
|
||||
|
||||
u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
|
||||
@@ -27,62 +25,15 @@ private:
|
||||
IocGetSyncpoint = 0xC0080002,
|
||||
IocGetWaitbase = 0xC0080003,
|
||||
IocMapBuffer = 0xC01C0009,
|
||||
IocMapBuffer2 = 0xC16C0009,
|
||||
IocMapBuffer3 = 0xC15C0009,
|
||||
IocMapBufferEx = 0xC0A40009,
|
||||
IocUnmapBufferEx = 0xC0A4000A,
|
||||
IocUnmapBuffer = 0xC0A4000A,
|
||||
IocUnmapBuffer2 = 0xC16C000A,
|
||||
IocUnmapBufferEx = 0xC01C000A,
|
||||
IocUnmapBuffer3 = 0xC15C000A,
|
||||
IocSetSubmitTimeout = 0x40040007,
|
||||
};
|
||||
|
||||
struct IoctlSetNvmapFD {
|
||||
u32_le nvmap_fd;
|
||||
};
|
||||
static_assert(sizeof(IoctlSetNvmapFD) == 0x4, "IoctlSetNvmapFD is incorrect size");
|
||||
|
||||
struct IoctlSubmit {
|
||||
INSERT_PADDING_BYTES(0x40); // TODO(DarkLordZach): RE this structure
|
||||
};
|
||||
static_assert(sizeof(IoctlSubmit) == 0x40, "IoctlSubmit has incorrect size");
|
||||
|
||||
struct IoctlGetSyncpoint {
|
||||
u32 unknown; // seems to be ignored? Nintendo added this
|
||||
u32 value;
|
||||
};
|
||||
static_assert(sizeof(IoctlGetSyncpoint) == 0x08, "IoctlGetSyncpoint has incorrect size");
|
||||
|
||||
struct IoctlGetWaitbase {
|
||||
u32 unknown; // seems to be ignored? Nintendo added this
|
||||
u32 value;
|
||||
};
|
||||
static_assert(sizeof(IoctlGetWaitbase) == 0x08, "IoctlGetWaitbase has incorrect size");
|
||||
|
||||
struct IoctlMapBuffer {
|
||||
u32 unknown;
|
||||
u32 address_1;
|
||||
u32 address_2;
|
||||
INSERT_PADDING_BYTES(0x10); // TODO(DarkLordZach): RE this structure
|
||||
};
|
||||
static_assert(sizeof(IoctlMapBuffer) == 0x1C, "IoctlMapBuffer is incorrect size");
|
||||
|
||||
struct IoctlMapBufferEx {
|
||||
u32 unknown;
|
||||
u32 address_1;
|
||||
u32 address_2;
|
||||
INSERT_PADDING_BYTES(0x98); // TODO(DarkLordZach): RE this structure
|
||||
};
|
||||
static_assert(sizeof(IoctlMapBufferEx) == 0xA4, "IoctlMapBufferEx has incorrect size");
|
||||
|
||||
struct IoctlUnmapBufferEx {
|
||||
INSERT_PADDING_BYTES(0xA4); // TODO(DarkLordZach): RE this structure
|
||||
};
|
||||
static_assert(sizeof(IoctlUnmapBufferEx) == 0xA4, "IoctlUnmapBufferEx has incorrect size");
|
||||
|
||||
u32_le nvmap_fd{};
|
||||
|
||||
u32 SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 Submit(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 MapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 UnmapBufferEx(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
};
|
||||
|
||||
} // namespace Service::Nvidia::Devices
|
||||
|
||||
234
src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
Normal file
234
src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
Normal file
@@ -0,0 +1,234 @@
|
||||
// Copyright 2020 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/nvdrv/devices/nvhost_nvdec_common.h"
|
||||
#include "core/hle/service/nvdrv/devices/nvmap.h"
|
||||
#include "core/memory.h"
|
||||
#include "video_core/memory_manager.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
|
||||
namespace Service::Nvidia::Devices {
|
||||
|
||||
namespace {
|
||||
// Splice vectors will copy count amount of type T from the input vector into the dst vector.
|
||||
template <typename T>
|
||||
std::size_t SpliceVectors(const std::vector<u8>& input, std::vector<T>& dst, std::size_t count,
|
||||
std::size_t offset) {
|
||||
std::memcpy(dst.data(), input.data() + offset, count * sizeof(T));
|
||||
offset += count * sizeof(T);
|
||||
return offset;
|
||||
}
|
||||
|
||||
// Write vectors will write data to the output buffer
|
||||
template <typename T>
|
||||
std::size_t WriteVectors(std::vector<u8>& dst, const std::vector<T>& src, std::size_t offset) {
|
||||
std::memcpy(dst.data() + offset, src.data(), src.size() * sizeof(T));
|
||||
offset += src.size() * sizeof(T);
|
||||
return offset;
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
namespace NvErrCodes {
|
||||
constexpr u32 Success{};
|
||||
[[maybe_unused]] constexpr u32 OutOfMemory{static_cast<u32>(-12)};
|
||||
constexpr u32 InvalidInput{static_cast<u32>(-22)};
|
||||
} // namespace NvErrCodes
|
||||
|
||||
nvhost_nvdec_common::nvhost_nvdec_common(Core::System& system, std::shared_ptr<nvmap> nvmap_dev)
|
||||
: nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
|
||||
nvhost_nvdec_common::~nvhost_nvdec_common() = default;
|
||||
|
||||
u32 nvhost_nvdec_common::SetNVMAPfd(const std::vector<u8>& input) {
|
||||
IoctlSetNvmapFD params{};
|
||||
std::memcpy(¶ms, input.data(), sizeof(IoctlSetNvmapFD));
|
||||
LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd);
|
||||
|
||||
nvmap_fd = params.nvmap_fd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 nvhost_nvdec_common::Submit(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
IoctlSubmit params{};
|
||||
std::memcpy(¶ms, input.data(), sizeof(IoctlSubmit));
|
||||
LOG_DEBUG(Service_NVDRV, "called NVDEC Submit, cmd_buffer_count={}", params.cmd_buffer_count);
|
||||
|
||||
// Instantiate param buffers
|
||||
std::size_t offset = sizeof(IoctlSubmit);
|
||||
std::vector<CommandBuffer> command_buffers(params.cmd_buffer_count);
|
||||
std::vector<Reloc> relocs(params.relocation_count);
|
||||
std::vector<u32> reloc_shifts(params.relocation_count);
|
||||
std::vector<SyncptIncr> syncpt_increments(params.syncpoint_count);
|
||||
std::vector<SyncptIncr> wait_checks(params.syncpoint_count);
|
||||
std::vector<Fence> fences(params.fence_count);
|
||||
|
||||
// Splice input into their respective buffers
|
||||
offset = SpliceVectors(input, command_buffers, params.cmd_buffer_count, offset);
|
||||
offset = SpliceVectors(input, relocs, params.relocation_count, offset);
|
||||
offset = SpliceVectors(input, reloc_shifts, params.relocation_count, offset);
|
||||
offset = SpliceVectors(input, syncpt_increments, params.syncpoint_count, offset);
|
||||
offset = SpliceVectors(input, wait_checks, params.syncpoint_count, offset);
|
||||
offset = SpliceVectors(input, fences, params.fence_count, offset);
|
||||
|
||||
// TODO(ameerj): For async gpu, utilize fences for syncpoint 'max' increment
|
||||
|
||||
auto& gpu = system.GPU();
|
||||
|
||||
for (const auto& cmd_buffer : command_buffers) {
|
||||
auto object = nvmap_dev->GetObject(cmd_buffer.memory_id);
|
||||
ASSERT_OR_EXECUTE(object, return NvErrCodes::InvalidInput;);
|
||||
const auto map = FindBufferMap(object->dma_map_addr);
|
||||
if (!map) {
|
||||
LOG_ERROR(Service_NVDRV, "Tried to submit an invalid offset 0x{:X} dma 0x{:X}",
|
||||
object->addr, object->dma_map_addr);
|
||||
return 0;
|
||||
}
|
||||
Tegra::ChCommandHeaderList cmdlist(cmd_buffer.word_count);
|
||||
gpu.MemoryManager().ReadBlock(map->StartAddr() + cmd_buffer.offset, cmdlist.data(),
|
||||
cmdlist.size() * sizeof(u32));
|
||||
gpu.PushCommandBuffer(cmdlist);
|
||||
}
|
||||
|
||||
std::memcpy(output.data(), ¶ms, sizeof(IoctlSubmit));
|
||||
// Some games expect command_buffers to be written back
|
||||
offset = sizeof(IoctlSubmit);
|
||||
offset = WriteVectors(output, command_buffers, offset);
|
||||
offset = WriteVectors(output, relocs, offset);
|
||||
offset = WriteVectors(output, reloc_shifts, offset);
|
||||
offset = WriteVectors(output, syncpt_increments, offset);
|
||||
offset = WriteVectors(output, wait_checks, offset);
|
||||
|
||||
return NvErrCodes::Success;
|
||||
}
|
||||
|
||||
u32 nvhost_nvdec_common::GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
IoctlGetSyncpoint params{};
|
||||
std::memcpy(¶ms, input.data(), sizeof(IoctlGetSyncpoint));
|
||||
LOG_DEBUG(Service_NVDRV, "called GetSyncpoint, id={}", params.param);
|
||||
|
||||
// We found that implementing this causes deadlocks with async gpu, along with degraded
|
||||
// performance. TODO: RE the nvdec async implementation
|
||||
params.value = 0;
|
||||
std::memcpy(output.data(), ¶ms, sizeof(IoctlGetSyncpoint));
|
||||
|
||||
return NvErrCodes::Success;
|
||||
}
|
||||
|
||||
u32 nvhost_nvdec_common::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
IoctlGetWaitbase params{};
|
||||
std::memcpy(¶ms, input.data(), sizeof(IoctlGetWaitbase));
|
||||
params.value = 0; // Seems to be hard coded at 0
|
||||
std::memcpy(output.data(), ¶ms, sizeof(IoctlGetWaitbase));
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 nvhost_nvdec_common::MapBuffer(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
IoctlMapBuffer params{};
|
||||
std::memcpy(¶ms, input.data(), sizeof(IoctlMapBuffer));
|
||||
std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries);
|
||||
|
||||
SpliceVectors(input, cmd_buffer_handles, params.num_entries, sizeof(IoctlMapBuffer));
|
||||
|
||||
auto& gpu = system.GPU();
|
||||
|
||||
for (auto& cmf_buff : cmd_buffer_handles) {
|
||||
auto object{nvmap_dev->GetObject(cmf_buff.map_handle)};
|
||||
if (!object) {
|
||||
LOG_ERROR(Service_NVDRV, "invalid cmd_buffer nvmap_handle={:X}", cmf_buff.map_handle);
|
||||
std::memcpy(output.data(), ¶ms, output.size());
|
||||
return NvErrCodes::InvalidInput;
|
||||
}
|
||||
if (object->dma_map_addr == 0) {
|
||||
// NVDEC and VIC memory is in the 32-bit address space
|
||||
// MapAllocate32 will attempt to map a lower 32-bit value in the shared gpu memory space
|
||||
const GPUVAddr low_addr = gpu.MemoryManager().MapAllocate32(object->addr, object->size);
|
||||
object->dma_map_addr = static_cast<u32>(low_addr);
|
||||
// Ensure that the dma_map_addr is indeed in the lower 32-bit address space.
|
||||
ASSERT(object->dma_map_addr == low_addr);
|
||||
}
|
||||
if (!object->dma_map_addr) {
|
||||
LOG_ERROR(Service_NVDRV, "failed to map size={}", object->size);
|
||||
} else {
|
||||
cmf_buff.map_address = object->dma_map_addr;
|
||||
AddBufferMap(object->dma_map_addr, object->size, object->addr,
|
||||
object->status == nvmap::Object::Status::Allocated);
|
||||
}
|
||||
}
|
||||
std::memcpy(output.data(), ¶ms, sizeof(IoctlMapBuffer));
|
||||
std::memcpy(output.data() + sizeof(IoctlMapBuffer), cmd_buffer_handles.data(),
|
||||
cmd_buffer_handles.size() * sizeof(MapBufferEntry));
|
||||
|
||||
return NvErrCodes::Success;
|
||||
}
|
||||
|
||||
u32 nvhost_nvdec_common::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
IoctlMapBuffer params{};
|
||||
std::memcpy(¶ms, input.data(), sizeof(IoctlMapBuffer));
|
||||
std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries);
|
||||
SpliceVectors(input, cmd_buffer_handles, params.num_entries, sizeof(IoctlMapBuffer));
|
||||
|
||||
auto& gpu = system.GPU();
|
||||
|
||||
for (auto& cmf_buff : cmd_buffer_handles) {
|
||||
const auto object{nvmap_dev->GetObject(cmf_buff.map_handle)};
|
||||
if (!object) {
|
||||
LOG_ERROR(Service_NVDRV, "invalid cmd_buffer nvmap_handle={:X}", cmf_buff.map_handle);
|
||||
std::memcpy(output.data(), ¶ms, output.size());
|
||||
return NvErrCodes::InvalidInput;
|
||||
}
|
||||
if (const auto size{RemoveBufferMap(object->dma_map_addr)}; size) {
|
||||
gpu.MemoryManager().Unmap(object->dma_map_addr, *size);
|
||||
} else {
|
||||
// This occurs quite frequently, however does not seem to impact functionality
|
||||
LOG_DEBUG(Service_NVDRV, "invalid offset=0x{:X} dma=0x{:X}", object->addr,
|
||||
object->dma_map_addr);
|
||||
}
|
||||
object->dma_map_addr = 0;
|
||||
}
|
||||
std::memset(output.data(), 0, output.size());
|
||||
return NvErrCodes::Success;
|
||||
}
|
||||
|
||||
u32 nvhost_nvdec_common::SetSubmitTimeout(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
std::memcpy(&submit_timeout, input.data(), input.size());
|
||||
LOG_WARNING(Service_NVDRV, "(STUBBED) called");
|
||||
return NvErrCodes::Success;
|
||||
}
|
||||
|
||||
std::optional<nvhost_nvdec_common::BufferMap> nvhost_nvdec_common::FindBufferMap(
|
||||
GPUVAddr gpu_addr) const {
|
||||
const auto it = std::find_if(
|
||||
buffer_mappings.begin(), buffer_mappings.upper_bound(gpu_addr), [&](const auto& entry) {
|
||||
return (gpu_addr >= entry.second.StartAddr() && gpu_addr < entry.second.EndAddr());
|
||||
});
|
||||
|
||||
ASSERT(it != buffer_mappings.end());
|
||||
return it->second;
|
||||
}
|
||||
|
||||
void nvhost_nvdec_common::AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr,
|
||||
bool is_allocated) {
|
||||
buffer_mappings.insert_or_assign(gpu_addr, BufferMap{gpu_addr, size, cpu_addr, is_allocated});
|
||||
}
|
||||
|
||||
std::optional<std::size_t> nvhost_nvdec_common::RemoveBufferMap(GPUVAddr gpu_addr) {
|
||||
const auto iter{buffer_mappings.find(gpu_addr)};
|
||||
if (iter == buffer_mappings.end()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
std::size_t size = 0;
|
||||
if (iter->second.IsAllocated()) {
|
||||
size = iter->second.Size();
|
||||
}
|
||||
buffer_mappings.erase(iter);
|
||||
return size;
|
||||
}
|
||||
|
||||
} // namespace Service::Nvidia::Devices
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user