Compare commits
245 Commits
android-92
...
android-11
Author | SHA1 | Date | |
---|---|---|---|
7684f37daa | |||
c60204e255 | |||
5e69769356 | |||
22cac3a5e3 | |||
e867768316 | |||
07276cf62a | |||
f04bc172ae | |||
585b6e9d46 | |||
a9e29a3972 | |||
1e61c3e1e7 | |||
79d3cef8db | |||
3e0da4f698 | |||
789c16305d | |||
1836e62d33 | |||
0bbbe80f75 | |||
eec3d356b6 | |||
2c1d850b46 | |||
2581590023 | |||
adb0900906 | |||
2d608cd625 | |||
29955de767 | |||
b0f62d8f24 | |||
ed2d77ddbc | |||
6e883a26da | |||
8427b9d49d | |||
0bb1c7c804 | |||
a5aa5876b4 | |||
911d2216be | |||
4da2105a32 | |||
1f9684eaf9 | |||
40c97c0549 | |||
6aee148b17 | |||
b5b93e6741 | |||
18a4529851 | |||
9e4d606c4c | |||
64f60f0acb | |||
21c631b33b | |||
43be2bfe33 | |||
79ba5d9c26 | |||
008d7e8c5f | |||
19e9bde9e0 | |||
6eb3a583cb | |||
e0834ee50b | |||
79894152a8 | |||
9274eaecd0 | |||
c733620024 | |||
897b411ae7 | |||
94836ba3b1 | |||
b1909b0435 | |||
1cc764988f | |||
da5c49f22d | |||
6b93b0b08c | |||
68f25217b8 | |||
0604b14263 | |||
a065dcdcd9 | |||
3d4a064674 | |||
e4dfd51337 | |||
cfe73af6f2 | |||
d6bd16b2c0 | |||
a49b146ccc | |||
fd9e157184 | |||
6cbd4020e8 | |||
3558b236cd | |||
48e82c4138 | |||
9eb70aea1d | |||
0460fbacc9 | |||
c73297e840 | |||
633d869ff4 | |||
e03f86cc54 | |||
a0a3566977 | |||
77fb9d415b | |||
bbdaa62175 | |||
31bffc7299 | |||
5f8f09d750 | |||
dcfe674ed4 | |||
bb195c2c2b | |||
8c59543ee3 | |||
db37e583ff | |||
d28e826e47 | |||
13beb85514 | |||
4b06bcc82c | |||
12ebc8d9d1 | |||
2b85e9e997 | |||
59b62c6507 | |||
2e760a9833 | |||
bab4a13a41 | |||
b56c7397ad | |||
689f346e97 | |||
249db0a59b | |||
9526ce95dd | |||
687158fe00 | |||
d8507332c1 | |||
67e983a354 | |||
f21058a6c0 | |||
b456af31e6 | |||
0441853d0f | |||
60a1c6b95b | |||
794e6c7a96 | |||
22afa2c7a3 | |||
85a89ca3e3 | |||
26776c0e60 | |||
e02ee8e59d | |||
134ecca9b0 | |||
c5f1ec8040 | |||
765ea9b79d | |||
c5bdc0054c | |||
0b7593d352 | |||
bd05ace08d | |||
fa56518f20 | |||
b577d7a55f | |||
d9dde7e6f3 | |||
2244b613cf | |||
c73bb33ff1 | |||
bcce184e60 | |||
326ebbb2fa | |||
07143ce15c | |||
dbc73c6c6c | |||
8becf13e8b | |||
9e2ebb24df | |||
90c56f5dc1 | |||
4b0291172e | |||
12e4757cf3 | |||
144c0734f5 | |||
f40f65f5d2 | |||
03c3f936cf | |||
66f41da365 | |||
7a986d731b | |||
eae0570a1c | |||
b57d98f847 | |||
762ac5aa9f | |||
1a4abd184f | |||
9524d7034c | |||
36d18e457b | |||
db562bc08d | |||
18672e6a78 | |||
32ad99701d | |||
63c5340cc4 | |||
a94371f67b | |||
22e4add562 | |||
b1a7bbd458 | |||
27ab2a6e13 | |||
68ea0a2b72 | |||
a8bd02acd8 | |||
3e4edbe007 | |||
4a9240599a | |||
053a16799e | |||
82c845dc2f | |||
3aa6d4d8ce | |||
9b961dddb4 | |||
224b6036a4 | |||
2c3281c66b | |||
1591923f91 | |||
56e5d99684 | |||
ca75c9125d | |||
1a4874e178 | |||
c00b63b9e1 | |||
c8602e1b1f | |||
faa6c35e78 | |||
8b64878258 | |||
519c12da15 | |||
d74fd9e2fe | |||
d011d3ff0e | |||
a5fb9de6fa | |||
c4ec76edba | |||
3f05b8facd | |||
65d3300875 | |||
7b2ac196d2 | |||
d9456f0a11 | |||
2212c9653d | |||
fe04a7523a | |||
45a76637f5 | |||
bf7c45e560 | |||
48b67fc4a0 | |||
6c246f2ac5 | |||
a34565727b | |||
6f4a080b98 | |||
da6824d9fd | |||
b36f45b239 | |||
07ae6659e7 | |||
880b004321 | |||
21ebe3e462 | |||
98cac9410c | |||
7b5d234558 | |||
84b0e29b56 | |||
5ecdcfa334 | |||
5f4857691e | |||
b50ce645ac | |||
b6d19329ac | |||
8c769b71a1 | |||
9512992fe2 | |||
6b10f04322 | |||
c206a04747 | |||
ec6ddaf766 | |||
36ea7565fa | |||
00b0938f10 | |||
ed58445111 | |||
b28b05e2aa | |||
8151a4d301 | |||
8ac8d703b9 | |||
1c1959eaeb | |||
c0d152affa | |||
85d99f873f | |||
21bc2c14bc | |||
d3997bad9b | |||
54fa1115a6 | |||
8347e5cdb9 | |||
dac53b4ba0 | |||
0bb7990c49 | |||
9ef9ca0927 | |||
bd42bba71c | |||
a27f94830a | |||
bd6f9f1d91 | |||
bf15aa093c | |||
0e9b839b6f | |||
15a5bdd979 | |||
fc4cde7513 | |||
ff3859d482 | |||
10de8f2c60 | |||
51b89fddd0 | |||
f585dec48d | |||
ad1a9f3d3a | |||
e797a917a9 | |||
71044f6def | |||
a764f49910 | |||
a17cde7b2c | |||
a84c928827 | |||
9568d3bc60 | |||
0fe935a5de | |||
c84c35ac74 | |||
b32940d3ea | |||
c334959440 | |||
2fa53ec1d9 | |||
e37ad99f22 | |||
38394f36d7 | |||
26f9d1f122 | |||
2f0db2708c | |||
1a246bf135 | |||
02b897ce27 | |||
79f0202045 | |||
e69eebb14a | |||
0145c89879 | |||
667ec28697 | |||
5464423667 | |||
cc4736fa58 | |||
b881949b6d |
@ -19,6 +19,7 @@ cmake .. \
|
|||||||
-DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON \
|
-DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON \
|
||||||
-DENABLE_QT_TRANSLATION=ON \
|
-DENABLE_QT_TRANSLATION=ON \
|
||||||
-DUSE_DISCORD_PRESENCE=ON \
|
-DUSE_DISCORD_PRESENCE=ON \
|
||||||
|
-DYUZU_CRASH_DUMPS=ON \
|
||||||
-DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} \
|
-DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} \
|
||||||
-DYUZU_USE_BUNDLED_FFMPEG=ON \
|
-DYUZU_USE_BUNDLED_FFMPEG=ON \
|
||||||
-GNinja
|
-GNinja
|
||||||
|
@ -5,6 +5,6 @@
|
|||||||
|
|
||||||
GITDATE="`git show -s --date=short --format='%ad' | sed 's/-//g'`"
|
GITDATE="`git show -s --date=short --format='%ad' | sed 's/-//g'`"
|
||||||
GITREV="`git show -s --format='%h'`"
|
GITREV="`git show -s --format='%h'`"
|
||||||
ARTIFACTS_DIR="artifacts"
|
ARTIFACTS_DIR="$PWD/artifacts"
|
||||||
|
|
||||||
mkdir -p "${ARTIFACTS_DIR}/"
|
mkdir -p "${ARTIFACTS_DIR}/"
|
||||||
|
@ -11,7 +11,7 @@ ccache -s
|
|||||||
mkdir build || true && cd build
|
mkdir build || true && cd build
|
||||||
cmake .. \
|
cmake .. \
|
||||||
-DBoost_USE_STATIC_LIBS=ON \
|
-DBoost_USE_STATIC_LIBS=ON \
|
||||||
-DCMAKE_BUILD_TYPE=Release \
|
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||||
-DCMAKE_CXX_FLAGS="-march=x86-64-v2" \
|
-DCMAKE_CXX_FLAGS="-march=x86-64-v2" \
|
||||||
-DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ \
|
-DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ \
|
||||||
-DCMAKE_C_COMPILER=/usr/lib/ccache/gcc \
|
-DCMAKE_C_COMPILER=/usr/lib/ccache/gcc \
|
||||||
@ -23,6 +23,7 @@ cmake .. \
|
|||||||
-DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} \
|
-DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} \
|
||||||
-DYUZU_USE_BUNDLED_FFMPEG=ON \
|
-DYUZU_USE_BUNDLED_FFMPEG=ON \
|
||||||
-DYUZU_ENABLE_LTO=ON \
|
-DYUZU_ENABLE_LTO=ON \
|
||||||
|
-DYUZU_CRASH_DUMPS=ON \
|
||||||
-GNinja
|
-GNinja
|
||||||
|
|
||||||
ninja
|
ninja
|
||||||
@ -31,6 +32,19 @@ ccache -s
|
|||||||
|
|
||||||
ctest -VV -C Release
|
ctest -VV -C Release
|
||||||
|
|
||||||
|
# Separate debug symbols from specified executables
|
||||||
|
for EXE in yuzu; do
|
||||||
|
EXE_PATH="bin/$EXE"
|
||||||
|
# Copy debug symbols out
|
||||||
|
objcopy --only-keep-debug $EXE_PATH $EXE_PATH.debug
|
||||||
|
# Add debug link and strip debug symbols
|
||||||
|
objcopy -g --add-gnu-debuglink=$EXE_PATH.debug $EXE_PATH $EXE_PATH.out
|
||||||
|
# Overwrite original with stripped copy
|
||||||
|
mv $EXE_PATH.out $EXE_PATH
|
||||||
|
done
|
||||||
|
# Strip debug symbols from all executables
|
||||||
|
find bin/ -type f -not -regex '.*.debug' -exec strip -g {} ';'
|
||||||
|
|
||||||
DESTDIR="$PWD/AppDir" ninja install
|
DESTDIR="$PWD/AppDir" ninja install
|
||||||
rm -vf AppDir/usr/bin/yuzu-cmd AppDir/usr/bin/yuzu-tester
|
rm -vf AppDir/usr/bin/yuzu-cmd AppDir/usr/bin/yuzu-tester
|
||||||
|
|
||||||
|
@ -59,4 +59,9 @@ if [ "${RELEASE_NAME}" = "mainline" ] || [ "${RELEASE_NAME}" = "early-access" ];
|
|||||||
cp "build/${APPIMAGE_NAME}" "${DIR_NAME}/yuzu-${RELEASE_NAME}.AppImage"
|
cp "build/${APPIMAGE_NAME}" "${DIR_NAME}/yuzu-${RELEASE_NAME}.AppImage"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Copy debug symbols to artifacts
|
||||||
|
cd build/bin
|
||||||
|
tar $COMPRESSION_FLAGS "${ARTIFACTS_DIR}/${REV_NAME}-debug.tar.xz" *.debug
|
||||||
|
cd -
|
||||||
|
|
||||||
. .ci/scripts/common/post-upload.sh
|
. .ci/scripts/common/post-upload.sh
|
||||||
|
@ -17,7 +17,6 @@ cmake .. \
|
|||||||
-DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON \
|
-DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON \
|
||||||
-DENABLE_QT_TRANSLATION=ON \
|
-DENABLE_QT_TRANSLATION=ON \
|
||||||
-DUSE_CCACHE=ON \
|
-DUSE_CCACHE=ON \
|
||||||
-DYUZU_CRASH_DUMPS=ON \
|
|
||||||
-DYUZU_USE_BUNDLED_SDL2=OFF \
|
-DYUZU_USE_BUNDLED_SDL2=OFF \
|
||||||
-DYUZU_USE_EXTERNAL_SDL2=OFF \
|
-DYUZU_USE_EXTERNAL_SDL2=OFF \
|
||||||
-DYUZU_TESTS=OFF \
|
-DYUZU_TESTS=OFF \
|
||||||
|
@ -3,4 +3,4 @@
|
|||||||
|
|
||||||
[codespell]
|
[codespell]
|
||||||
skip = ./.git,./build,./dist,./Doxyfile,./externals,./LICENSES,./src/android/app/src/main/res
|
skip = ./.git,./build,./dist,./Doxyfile,./externals,./LICENSES,./src/android/app/src/main/res
|
||||||
ignore-words-list = aci,allright,ba,canonicalizations,deques,froms,hda,inout,lod,masia,nam,nax,nd,optin,pullrequests,pullrequest,te,transfered,unstall,uscaled,zink
|
ignore-words-list = aci,allright,ba,canonicalizations,deques,froms,hda,inout,lod,masia,nam,nax,nd,optin,pullrequests,pullrequest,te,transfered,unstall,uscaled,vas,zink
|
||||||
|
5
.gitmodules
vendored
5
.gitmodules
vendored
@ -32,7 +32,7 @@
|
|||||||
path = externals/xbyak
|
path = externals/xbyak
|
||||||
url = https://github.com/herumi/xbyak.git
|
url = https://github.com/herumi/xbyak.git
|
||||||
[submodule "opus"]
|
[submodule "opus"]
|
||||||
path = externals/opus/opus
|
path = externals/opus
|
||||||
url = https://github.com/xiph/opus.git
|
url = https://github.com/xiph/opus.git
|
||||||
[submodule "SDL"]
|
[submodule "SDL"]
|
||||||
path = externals/SDL
|
path = externals/SDL
|
||||||
@ -58,3 +58,6 @@
|
|||||||
[submodule "VulkanMemoryAllocator"]
|
[submodule "VulkanMemoryAllocator"]
|
||||||
path = externals/VulkanMemoryAllocator
|
path = externals/VulkanMemoryAllocator
|
||||||
url = https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git
|
url = https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git
|
||||||
|
[submodule "breakpad"]
|
||||||
|
path = externals/breakpad
|
||||||
|
url = https://github.com/yuzu-emu/breakpad.git
|
||||||
|
@ -147,3 +147,7 @@ License: GPL-3.0-or-later
|
|||||||
Files: src/android/gradle/wrapper/*
|
Files: src/android/gradle/wrapper/*
|
||||||
Copyright: 2023 yuzu Emulator Project
|
Copyright: 2023 yuzu Emulator Project
|
||||||
License: GPL-3.0-or-later
|
License: GPL-3.0-or-later
|
||||||
|
|
||||||
|
Files: externals/stb/*
|
||||||
|
Copyright: Sean Barrett
|
||||||
|
License: MIT
|
||||||
|
@ -11,7 +11,6 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/externals/cmake-modul
|
|||||||
include(DownloadExternals)
|
include(DownloadExternals)
|
||||||
include(CMakeDependentOption)
|
include(CMakeDependentOption)
|
||||||
include(CTest)
|
include(CTest)
|
||||||
include(FetchContent)
|
|
||||||
|
|
||||||
# Set bundled sdl2/qt as dependent options.
|
# Set bundled sdl2/qt as dependent options.
|
||||||
# OFF by default, but if ENABLE_SDL2 and MSVC are true then ON
|
# OFF by default, but if ENABLE_SDL2 and MSVC are true then ON
|
||||||
@ -53,7 +52,7 @@ option(YUZU_DOWNLOAD_ANDROID_VVL "Download validation layer binary for android"
|
|||||||
|
|
||||||
CMAKE_DEPENDENT_OPTION(YUZU_ROOM "Compile LDN room server" ON "NOT ANDROID" OFF)
|
CMAKE_DEPENDENT_OPTION(YUZU_ROOM "Compile LDN room server" ON "NOT ANDROID" OFF)
|
||||||
|
|
||||||
CMAKE_DEPENDENT_OPTION(YUZU_CRASH_DUMPS "Compile Windows crash dump (Minidump) support" OFF "WIN32" OFF)
|
CMAKE_DEPENDENT_OPTION(YUZU_CRASH_DUMPS "Compile crash dump (Minidump) support" OFF "WIN32 OR LINUX" OFF)
|
||||||
|
|
||||||
option(YUZU_USE_BUNDLED_VCPKG "Use vcpkg for yuzu dependencies" "${MSVC}")
|
option(YUZU_USE_BUNDLED_VCPKG "Use vcpkg for yuzu dependencies" "${MSVC}")
|
||||||
|
|
||||||
@ -99,47 +98,8 @@ if (ANDROID AND YUZU_DOWNLOAD_ANDROID_VVL)
|
|||||||
DESTINATION "${vvl_lib_path}")
|
DESTINATION "${vvl_lib_path}")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# On Android, fetch and compile libcxx before doing anything else
|
|
||||||
if (ANDROID)
|
if (ANDROID)
|
||||||
set(CMAKE_SKIP_INSTALL_RULES ON)
|
set(CMAKE_SKIP_INSTALL_RULES ON)
|
||||||
set(LLVM_VERSION "15.0.6")
|
|
||||||
|
|
||||||
# Note: even though libcxx and libcxxabi have separate releases on the project page,
|
|
||||||
# the separated releases cannot be compiled. Only in-tree builds work. Therefore we
|
|
||||||
# must fetch the source release for the entire llvm tree.
|
|
||||||
FetchContent_Declare(llvm
|
|
||||||
URL "https://github.com/llvm/llvm-project/releases/download/llvmorg-${LLVM_VERSION}/llvm-project-${LLVM_VERSION}.src.tar.xz"
|
|
||||||
URL_HASH SHA256=9d53ad04dc60cb7b30e810faf64c5ab8157dadef46c8766f67f286238256ff92
|
|
||||||
TLS_VERIFY TRUE
|
|
||||||
)
|
|
||||||
FetchContent_MakeAvailable(llvm)
|
|
||||||
|
|
||||||
# libcxx has support for most of the range library, but it's gated behind a flag:
|
|
||||||
add_compile_definitions(_LIBCPP_ENABLE_EXPERIMENTAL)
|
|
||||||
|
|
||||||
# Disable standard header inclusion
|
|
||||||
set(ANDROID_STL "none")
|
|
||||||
|
|
||||||
# libcxxabi
|
|
||||||
set(LIBCXXABI_INCLUDE_TESTS OFF)
|
|
||||||
set(LIBCXXABI_ENABLE_SHARED FALSE)
|
|
||||||
set(LIBCXXABI_ENABLE_STATIC TRUE)
|
|
||||||
set(LIBCXXABI_LIBCXX_INCLUDES "${LIBCXX_TARGET_INCLUDE_DIRECTORY}" CACHE STRING "" FORCE)
|
|
||||||
add_subdirectory("${llvm_SOURCE_DIR}/libcxxabi" "${llvm_BINARY_DIR}/libcxxabi")
|
|
||||||
link_libraries(cxxabi_static)
|
|
||||||
|
|
||||||
# libcxx
|
|
||||||
set(LIBCXX_ABI_NAMESPACE "__ndk1" CACHE STRING "" FORCE)
|
|
||||||
set(LIBCXX_CXX_ABI "libcxxabi")
|
|
||||||
set(LIBCXX_INCLUDE_TESTS OFF)
|
|
||||||
set(LIBCXX_INCLUDE_BENCHMARKS OFF)
|
|
||||||
set(LIBCXX_INCLUDE_DOCS OFF)
|
|
||||||
set(LIBCXX_ENABLE_SHARED FALSE)
|
|
||||||
set(LIBCXX_ENABLE_STATIC TRUE)
|
|
||||||
set(LIBCXX_ENABLE_ASSERTIONS FALSE)
|
|
||||||
add_subdirectory("${llvm_SOURCE_DIR}/libcxx" "${llvm_BINARY_DIR}/libcxx")
|
|
||||||
set_target_properties(cxx-headers PROPERTIES INTERFACE_COMPILE_OPTIONS "-isystem${CMAKE_BINARY_DIR}/${LIBCXX_INSTALL_INCLUDE_DIR}")
|
|
||||||
link_libraries(cxx_static cxx-headers)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (YUZU_USE_BUNDLED_VCPKG)
|
if (YUZU_USE_BUNDLED_VCPKG)
|
||||||
@ -179,9 +139,6 @@ if (YUZU_USE_BUNDLED_VCPKG)
|
|||||||
if (YUZU_TESTS)
|
if (YUZU_TESTS)
|
||||||
list(APPEND VCPKG_MANIFEST_FEATURES "yuzu-tests")
|
list(APPEND VCPKG_MANIFEST_FEATURES "yuzu-tests")
|
||||||
endif()
|
endif()
|
||||||
if (YUZU_CRASH_DUMPS)
|
|
||||||
list(APPEND VCPKG_MANIFEST_FEATURES "dbghelp")
|
|
||||||
endif()
|
|
||||||
if (ENABLE_WEB_SERVICE)
|
if (ENABLE_WEB_SERVICE)
|
||||||
list(APPEND VCPKG_MANIFEST_FEATURES "web-service")
|
list(APPEND VCPKG_MANIFEST_FEATURES "web-service")
|
||||||
endif()
|
endif()
|
||||||
@ -329,11 +286,12 @@ find_package(Boost 1.79.0 REQUIRED context)
|
|||||||
find_package(enet 1.3 MODULE)
|
find_package(enet 1.3 MODULE)
|
||||||
find_package(fmt 9 REQUIRED)
|
find_package(fmt 9 REQUIRED)
|
||||||
find_package(inih 52 MODULE COMPONENTS INIReader)
|
find_package(inih 52 MODULE COMPONENTS INIReader)
|
||||||
find_package(LLVM 17 MODULE COMPONENTS Demangle)
|
find_package(LLVM 17.0.2 MODULE COMPONENTS Demangle)
|
||||||
find_package(lz4 REQUIRED)
|
find_package(lz4 REQUIRED)
|
||||||
find_package(nlohmann_json 3.8 REQUIRED)
|
find_package(nlohmann_json 3.8 REQUIRED)
|
||||||
find_package(Opus 1.3 MODULE)
|
find_package(Opus 1.3 MODULE)
|
||||||
find_package(RenderDoc MODULE)
|
find_package(RenderDoc MODULE)
|
||||||
|
find_package(stb MODULE)
|
||||||
find_package(VulkanMemoryAllocator CONFIG)
|
find_package(VulkanMemoryAllocator CONFIG)
|
||||||
find_package(ZLIB 1.2 REQUIRED)
|
find_package(ZLIB 1.2 REQUIRED)
|
||||||
find_package(zstd 1.5 REQUIRED)
|
find_package(zstd 1.5 REQUIRED)
|
||||||
@ -400,6 +358,9 @@ function(set_yuzu_qt_components)
|
|||||||
if (ENABLE_QT_TRANSLATION)
|
if (ENABLE_QT_TRANSLATION)
|
||||||
list(APPEND YUZU_QT_COMPONENTS2 LinguistTools)
|
list(APPEND YUZU_QT_COMPONENTS2 LinguistTools)
|
||||||
endif()
|
endif()
|
||||||
|
if (USE_DISCORD_PRESENCE)
|
||||||
|
list(APPEND YUZU_QT_COMPONENTS2 Network)
|
||||||
|
endif()
|
||||||
set(YUZU_QT_COMPONENTS ${YUZU_QT_COMPONENTS2} PARENT_SCOPE)
|
set(YUZU_QT_COMPONENTS ${YUZU_QT_COMPONENTS2} PARENT_SCOPE)
|
||||||
endfunction(set_yuzu_qt_components)
|
endfunction(set_yuzu_qt_components)
|
||||||
|
|
||||||
@ -587,6 +548,18 @@ if (NOT YUZU_USE_BUNDLED_FFMPEG)
|
|||||||
find_package(FFmpeg 4.3 REQUIRED QUIET COMPONENTS ${FFmpeg_COMPONENTS})
|
find_package(FFmpeg 4.3 REQUIRED QUIET COMPONENTS ${FFmpeg_COMPONENTS})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if (WIN32 AND YUZU_CRASH_DUMPS)
|
||||||
|
set(BREAKPAD_VER "breakpad-c89f9dd")
|
||||||
|
download_bundled_external("breakpad/" ${BREAKPAD_VER} BREAKPAD_PREFIX)
|
||||||
|
|
||||||
|
set(BREAKPAD_CLIENT_INCLUDE_DIR "${BREAKPAD_PREFIX}/include")
|
||||||
|
set(BREAKPAD_CLIENT_LIBRARY "${BREAKPAD_PREFIX}/lib/libbreakpad_client.lib")
|
||||||
|
|
||||||
|
add_library(libbreakpad_client INTERFACE IMPORTED)
|
||||||
|
target_link_libraries(libbreakpad_client INTERFACE "${BREAKPAD_CLIENT_LIBRARY}")
|
||||||
|
target_include_directories(libbreakpad_client INTERFACE "${BREAKPAD_CLIENT_INCLUDE_DIR}")
|
||||||
|
endif()
|
||||||
|
|
||||||
# Prefer the -pthread flag on Linux.
|
# Prefer the -pthread flag on Linux.
|
||||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||||
find_package(Threads REQUIRED)
|
find_package(Threads REQUIRED)
|
||||||
@ -606,13 +579,6 @@ elseif (WIN32)
|
|||||||
# PSAPI is the Process Status API
|
# PSAPI is the Process Status API
|
||||||
set(PLATFORM_LIBRARIES ${PLATFORM_LIBRARIES} psapi imm32 version)
|
set(PLATFORM_LIBRARIES ${PLATFORM_LIBRARIES} psapi imm32 version)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (YUZU_CRASH_DUMPS)
|
|
||||||
find_library(DBGHELP_LIBRARY dbghelp)
|
|
||||||
if ("${DBGHELP_LIBRARY}" STREQUAL "DBGHELP_LIBRARY-NOTFOUND")
|
|
||||||
message(FATAL_ERROR "YUZU_CRASH_DUMPS enabled but dbghelp library not found")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
elseif (CMAKE_SYSTEM_NAME MATCHES "^(Linux|kFreeBSD|GNU|SunOS)$")
|
elseif (CMAKE_SYSTEM_NAME MATCHES "^(Linux|kFreeBSD|GNU|SunOS)$")
|
||||||
set(PLATFORM_LIBRARIES rt)
|
set(PLATFORM_LIBRARIES rt)
|
||||||
endif()
|
endif()
|
||||||
|
31
CMakeModules/Findstb.cmake
Normal file
31
CMakeModules/Findstb.cmake
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
# SPDX-FileCopyrightText: 2023 Alexandre Bouvier <contact@amb.tf>
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
find_path(stb_image_INCLUDE_DIR stb_image.h PATH_SUFFIXES stb)
|
||||||
|
find_path(stb_image_resize_INCLUDE_DIR stb_image_resize.h PATH_SUFFIXES stb)
|
||||||
|
find_path(stb_image_write_INCLUDE_DIR stb_image_write.h PATH_SUFFIXES stb)
|
||||||
|
|
||||||
|
include(FindPackageHandleStandardArgs)
|
||||||
|
find_package_handle_standard_args(stb
|
||||||
|
REQUIRED_VARS
|
||||||
|
stb_image_INCLUDE_DIR
|
||||||
|
stb_image_resize_INCLUDE_DIR
|
||||||
|
stb_image_write_INCLUDE_DIR
|
||||||
|
)
|
||||||
|
|
||||||
|
if (stb_FOUND AND NOT TARGET stb::headers)
|
||||||
|
add_library(stb::headers INTERFACE IMPORTED)
|
||||||
|
set_property(TARGET stb::headers PROPERTY
|
||||||
|
INTERFACE_INCLUDE_DIRECTORIES
|
||||||
|
"${stb_image_INCLUDE_DIR}"
|
||||||
|
"${stb_image_resize_INCLUDE_DIR}"
|
||||||
|
"${stb_image_write_INCLUDE_DIR}"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
mark_as_advanced(
|
||||||
|
stb_image_INCLUDE_DIR
|
||||||
|
stb_image_resize_INCLUDE_DIR
|
||||||
|
stb_image_write_INCLUDE_DIR
|
||||||
|
)
|
@ -1,3 +1,11 @@
|
|||||||
|
| Pull Request | Commit | Title | Author | Merged? |
|
||||||
|
|----|----|----|----|----|
|
||||||
|
|
||||||
|
|
||||||
|
End of merge log. You can find the original README.md below the break.
|
||||||
|
|
||||||
|
-----
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
SPDX-FileCopyrightText: 2018 yuzu Emulator Project
|
SPDX-FileCopyrightText: 2018 yuzu Emulator Project
|
||||||
SPDX-License-Identifier: GPL-2.0-or-later
|
SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
1
dist/org.yuzu_emu.yuzu.desktop
vendored
1
dist/org.yuzu_emu.yuzu.desktop
vendored
@ -13,3 +13,4 @@ Exec=yuzu %f
|
|||||||
Categories=Game;Emulator;Qt;
|
Categories=Game;Emulator;Qt;
|
||||||
MimeType=application/x-nx-nro;application/x-nx-nso;application/x-nx-nsp;application/x-nx-xci;
|
MimeType=application/x-nx-nro;application/x-nx-nso;application/x-nx-nsp;application/x-nx-xci;
|
||||||
Keywords=Nintendo;Switch;
|
Keywords=Nintendo;Switch;
|
||||||
|
StartupWMClass=yuzu
|
||||||
|
4
dist/qt_themes/default/style.qss
vendored
4
dist/qt_themes/default/style.qss
vendored
@ -120,6 +120,10 @@ QWidget#connectedControllers {
|
|||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QWidget#closeButtons {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
QWidget#playersSupported,
|
QWidget#playersSupported,
|
||||||
QWidget#controllersSupported,
|
QWidget#controllersSupported,
|
||||||
QWidget#controllerSupported1,
|
QWidget#controllerSupported1,
|
||||||
|
4
dist/qt_themes/qdarkstyle/style.qss
vendored
4
dist/qt_themes/qdarkstyle/style.qss
vendored
@ -1380,6 +1380,10 @@ QWidget#connectedControllers {
|
|||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QWidget#closeButtons {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
QWidget#playersSupported,
|
QWidget#playersSupported,
|
||||||
QWidget#controllersSupported,
|
QWidget#controllersSupported,
|
||||||
QWidget#controllerSupported1,
|
QWidget#controllerSupported1,
|
||||||
|
@ -2305,6 +2305,10 @@ QWidget#connectedControllers {
|
|||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QWidget#closeButtons {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
QWidget#playersSupported,
|
QWidget#playersSupported,
|
||||||
QWidget#controllersSupported,
|
QWidget#controllersSupported,
|
||||||
QWidget#controllerSupported1,
|
QWidget#controllerSupported1,
|
||||||
|
110
externals/CMakeLists.txt
vendored
110
externals/CMakeLists.txt
vendored
@ -134,6 +134,10 @@ endif()
|
|||||||
|
|
||||||
# Opus
|
# Opus
|
||||||
if (NOT TARGET Opus::opus)
|
if (NOT TARGET Opus::opus)
|
||||||
|
set(OPUS_BUILD_TESTING OFF)
|
||||||
|
set(OPUS_BUILD_PROGRAMS OFF)
|
||||||
|
set(OPUS_INSTALL_PKG_CONFIG_MODULE OFF)
|
||||||
|
set(OPUS_INSTALL_CMAKE_CONFIG_MODULE OFF)
|
||||||
add_subdirectory(opus)
|
add_subdirectory(opus)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@ -171,6 +175,10 @@ endif()
|
|||||||
add_library(stb stb/stb_dxt.cpp)
|
add_library(stb stb/stb_dxt.cpp)
|
||||||
target_include_directories(stb PUBLIC ./stb)
|
target_include_directories(stb PUBLIC ./stb)
|
||||||
|
|
||||||
|
if (NOT TARGET stb::headers)
|
||||||
|
add_library(stb::headers ALIAS stb)
|
||||||
|
endif()
|
||||||
|
|
||||||
add_library(bc_decoder bc_decoder/bc_decoder.cpp)
|
add_library(bc_decoder bc_decoder/bc_decoder.cpp)
|
||||||
target_include_directories(bc_decoder PUBLIC ./bc_decoder)
|
target_include_directories(bc_decoder PUBLIC ./bc_decoder)
|
||||||
|
|
||||||
@ -185,3 +193,105 @@ if (ANDROID)
|
|||||||
add_subdirectory(libadrenotools)
|
add_subdirectory(libadrenotools)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# Breakpad
|
||||||
|
# https://github.com/microsoft/vcpkg/blob/master/ports/breakpad/CMakeLists.txt
|
||||||
|
if (YUZU_CRASH_DUMPS AND NOT TARGET libbreakpad_client)
|
||||||
|
set(BREAKPAD_WIN32_DEFINES
|
||||||
|
NOMINMAX
|
||||||
|
UNICODE
|
||||||
|
WIN32_LEAN_AND_MEAN
|
||||||
|
_CRT_SECURE_NO_WARNINGS
|
||||||
|
_CRT_SECURE_NO_DEPRECATE
|
||||||
|
_CRT_NONSTDC_NO_DEPRECATE
|
||||||
|
)
|
||||||
|
|
||||||
|
# libbreakpad
|
||||||
|
add_library(libbreakpad STATIC)
|
||||||
|
file(GLOB_RECURSE LIBBREAKPAD_SOURCES breakpad/src/processor/*.cc)
|
||||||
|
file(GLOB_RECURSE LIBDISASM_SOURCES breakpad/src/third_party/libdisasm/*.c)
|
||||||
|
list(FILTER LIBBREAKPAD_SOURCES EXCLUDE REGEX "_unittest|_selftest|synth_minidump|/tests|/testdata|/solaris|microdump_stackwalk|minidump_dump|minidump_stackwalk")
|
||||||
|
if (WIN32)
|
||||||
|
list(FILTER LIBBREAKPAD_SOURCES EXCLUDE REGEX "/linux|/mac|/android")
|
||||||
|
target_compile_definitions(libbreakpad PRIVATE ${BREAKPAD_WIN32_DEFINES})
|
||||||
|
target_include_directories(libbreakpad PRIVATE "${CMAKE_GENERATOR_INSTANCE}/DIA SDK/include")
|
||||||
|
elseif (APPLE)
|
||||||
|
list(FILTER LIBBREAKPAD_SOURCES EXCLUDE REGEX "/linux|/windows|/android")
|
||||||
|
else()
|
||||||
|
list(FILTER LIBBREAKPAD_SOURCES EXCLUDE REGEX "/mac|/windows|/android")
|
||||||
|
endif()
|
||||||
|
target_sources(libbreakpad PRIVATE ${LIBBREAKPAD_SOURCES} ${LIBDISASM_SOURCES})
|
||||||
|
target_include_directories(libbreakpad
|
||||||
|
PUBLIC
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/breakpad/src
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/breakpad/src/third_party/libdisasm
|
||||||
|
)
|
||||||
|
|
||||||
|
# libbreakpad_client
|
||||||
|
add_library(libbreakpad_client STATIC)
|
||||||
|
file(GLOB LIBBREAKPAD_COMMON_SOURCES breakpad/src/common/*.cc breakpad/src/common/*.c breakpad/src/client/*.cc)
|
||||||
|
|
||||||
|
if (WIN32)
|
||||||
|
file(GLOB_RECURSE LIBBREAKPAD_CLIENT_SOURCES breakpad/src/client/windows/*.cc breakpad/src/common/windows/*.cc)
|
||||||
|
list(FILTER LIBBREAKPAD_COMMON_SOURCES EXCLUDE REGEX "language.cc|path_helper.cc|stabs_to_module.cc|stabs_reader.cc|minidump_file_writer.cc")
|
||||||
|
target_include_directories(libbreakpad_client PRIVATE "${CMAKE_GENERATOR_INSTANCE}/DIA SDK/include")
|
||||||
|
target_compile_definitions(libbreakpad_client PRIVATE ${BREAKPAD_WIN32_DEFINES})
|
||||||
|
elseif (APPLE)
|
||||||
|
target_compile_definitions(libbreakpad_client PRIVATE HAVE_MACH_O_NLIST_H)
|
||||||
|
file(GLOB_RECURSE LIBBREAKPAD_CLIENT_SOURCES breakpad/src/client/mac/*.cc breakpad/src/common/mac/*.cc)
|
||||||
|
list(APPEND LIBBREAKPAD_CLIENT_SOURCES breakpad/src/common/mac/MachIPC.mm)
|
||||||
|
else()
|
||||||
|
target_compile_definitions(libbreakpad_client PUBLIC -DHAVE_A_OUT_H)
|
||||||
|
file(GLOB_RECURSE LIBBREAKPAD_CLIENT_SOURCES breakpad/src/client/linux/*.cc breakpad/src/common/linux/*.cc)
|
||||||
|
endif()
|
||||||
|
list(APPEND LIBBREAKPAD_CLIENT_SOURCES ${LIBBREAKPAD_COMMON_SOURCES})
|
||||||
|
list(FILTER LIBBREAKPAD_CLIENT_SOURCES EXCLUDE REGEX "/sender|/tests|/unittests|/testcases|_unittest|_test")
|
||||||
|
target_sources(libbreakpad_client PRIVATE ${LIBBREAKPAD_CLIENT_SOURCES})
|
||||||
|
target_include_directories(libbreakpad_client PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/breakpad/src)
|
||||||
|
|
||||||
|
if (WIN32)
|
||||||
|
target_link_libraries(libbreakpad_client PRIVATE wininet.lib)
|
||||||
|
elseif (APPLE)
|
||||||
|
find_library(CoreFoundation_FRAMEWORK CoreFoundation)
|
||||||
|
target_link_libraries(libbreakpad_client PRIVATE ${CoreFoundation_FRAMEWORK})
|
||||||
|
else()
|
||||||
|
find_library(PTHREAD_LIBRARIES pthread)
|
||||||
|
target_compile_definitions(libbreakpad_client PRIVATE HAVE_GETCONTEXT=1)
|
||||||
|
if (PTHREAD_LIBRARIES)
|
||||||
|
target_link_libraries(libbreakpad_client PRIVATE ${PTHREAD_LIBRARIES})
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Host tools for symbol processing
|
||||||
|
if (LINUX)
|
||||||
|
find_package(ZLIB REQUIRED)
|
||||||
|
|
||||||
|
add_executable(minidump_stackwalk breakpad/src/processor/minidump_stackwalk.cc)
|
||||||
|
target_link_libraries(minidump_stackwalk PRIVATE libbreakpad libbreakpad_client)
|
||||||
|
|
||||||
|
add_executable(dump_syms
|
||||||
|
breakpad/src/common/dwarf_cfi_to_module.cc
|
||||||
|
breakpad/src/common/dwarf_cu_to_module.cc
|
||||||
|
breakpad/src/common/dwarf_line_to_module.cc
|
||||||
|
breakpad/src/common/dwarf_range_list_handler.cc
|
||||||
|
breakpad/src/common/language.cc
|
||||||
|
breakpad/src/common/module.cc
|
||||||
|
breakpad/src/common/path_helper.cc
|
||||||
|
breakpad/src/common/stabs_reader.cc
|
||||||
|
breakpad/src/common/stabs_to_module.cc
|
||||||
|
breakpad/src/common/dwarf/bytereader.cc
|
||||||
|
breakpad/src/common/dwarf/dwarf2diehandler.cc
|
||||||
|
breakpad/src/common/dwarf/dwarf2reader.cc
|
||||||
|
breakpad/src/common/dwarf/elf_reader.cc
|
||||||
|
breakpad/src/common/linux/crc32.cc
|
||||||
|
breakpad/src/common/linux/dump_symbols.cc
|
||||||
|
breakpad/src/common/linux/elf_symbols_to_module.cc
|
||||||
|
breakpad/src/common/linux/elfutils.cc
|
||||||
|
breakpad/src/common/linux/file_id.cc
|
||||||
|
breakpad/src/common/linux/linux_libc_support.cc
|
||||||
|
breakpad/src/common/linux/memory_mapped_file.cc
|
||||||
|
breakpad/src/common/linux/safe_readlink.cc
|
||||||
|
breakpad/src/tools/linux/dump_syms/dump_syms.cc)
|
||||||
|
target_link_libraries(dump_syms PRIVATE libbreakpad_client ZLIB::ZLIB)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
2
externals/SDL
vendored
2
externals/SDL
vendored
Submodule externals/SDL updated: 031912c4b6...cc016b0046
2
externals/Vulkan-Headers
vendored
2
externals/Vulkan-Headers
vendored
Submodule externals/Vulkan-Headers updated: ed857118e2...df60f03168
2
externals/VulkanMemoryAllocator
vendored
2
externals/VulkanMemoryAllocator
vendored
Submodule externals/VulkanMemoryAllocator updated: 9b0fc3e7b0...2f382df218
1
externals/breakpad
vendored
Submodule
1
externals/breakpad
vendored
Submodule
Submodule externals/breakpad added at c89f9dddc7
2
externals/cpp-httplib
vendored
2
externals/cpp-httplib
vendored
Submodule externals/cpp-httplib updated: 6d963fbe8d...a609330e4c
2
externals/cpp-jwt
vendored
2
externals/cpp-jwt
vendored
Submodule externals/cpp-jwt updated: e12ef06218...10ef5735d8
2
externals/dynarmic
vendored
2
externals/dynarmic
vendored
Submodule externals/dynarmic updated: 7da378033a...0df09e2f6b
2
externals/ffmpeg/ffmpeg
vendored
2
externals/ffmpeg/ffmpeg
vendored
Submodule externals/ffmpeg/ffmpeg updated: 6b6b9e593d...9c1294eadd
2
externals/inih/inih
vendored
2
externals/inih/inih
vendored
Submodule externals/inih/inih updated: 1e80a47dff...9cecf0643d
7
externals/libusb/CMakeLists.txt
vendored
7
externals/libusb/CMakeLists.txt
vendored
@ -49,11 +49,6 @@ if (MINGW OR (${CMAKE_SYSTEM_NAME} MATCHES "Linux") OR APPLE)
|
|||||||
|
|
||||||
set(LIBUSB_INCLUDE_DIRS "${LIBUSB_SRC_DIR}/libusb" CACHE PATH "libusb headers path" FORCE)
|
set(LIBUSB_INCLUDE_DIRS "${LIBUSB_SRC_DIR}/libusb" CACHE PATH "libusb headers path" FORCE)
|
||||||
|
|
||||||
# MINGW: causes "externals/libusb/libusb/libusb/os/windows_winusb.c:1427:2: error: conversion to non-scalar type requested", so cannot statically link it for now.
|
|
||||||
if (NOT MINGW)
|
|
||||||
set(LIBUSB_CFLAGS "-DGUID_DEVINTERFACE_USB_DEVICE=\\(GUID\\){0xA5DCBF10,0x6530,0x11D2,{0x90,0x1F,0x00,0xC0,0x4F,0xB9,0x51,0xED}}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
make_directory("${LIBUSB_PREFIX}")
|
make_directory("${LIBUSB_PREFIX}")
|
||||||
|
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
@ -146,8 +141,6 @@ else() # MINGW OR (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
|||||||
target_include_directories(usb BEFORE PRIVATE libusb/msvc)
|
target_include_directories(usb BEFORE PRIVATE libusb/msvc)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Works around other libraries providing their own definition of USB GUIDs (e.g. SDL2)
|
|
||||||
target_compile_definitions(usb PRIVATE "-DGUID_DEVINTERFACE_USB_DEVICE=(GUID){ 0xA5DCBF10, 0x6530, 0x11D2, {0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED}}")
|
|
||||||
else()
|
else()
|
||||||
target_include_directories(usb
|
target_include_directories(usb
|
||||||
# turns out other projects also have "config.h", so make sure the
|
# turns out other projects also have "config.h", so make sure the
|
||||||
|
2
externals/libusb/libusb
vendored
2
externals/libusb/libusb
vendored
Submodule externals/libusb/libusb updated: c6a35c5601...c060e9ce30
2
externals/nx_tzdb/CMakeLists.txt
vendored
2
externals/nx_tzdb/CMakeLists.txt
vendored
@ -27,7 +27,7 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Windows" OR ANDROID)
|
|||||||
set(CAN_BUILD_NX_TZDB false)
|
set(CAN_BUILD_NX_TZDB false)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(NX_TZDB_VERSION "220816")
|
set(NX_TZDB_VERSION "221202")
|
||||||
set(NX_TZDB_ARCHIVE "${CMAKE_CURRENT_BINARY_DIR}/${NX_TZDB_VERSION}.zip")
|
set(NX_TZDB_ARCHIVE "${CMAKE_CURRENT_BINARY_DIR}/${NX_TZDB_VERSION}.zip")
|
||||||
|
|
||||||
set(NX_TZDB_ROMFS_DIR "${CMAKE_CURRENT_BINARY_DIR}/nx_tzdb")
|
set(NX_TZDB_ROMFS_DIR "${CMAKE_CURRENT_BINARY_DIR}/nx_tzdb")
|
||||||
|
2
externals/nx_tzdb/tzdb_to_nx
vendored
2
externals/nx_tzdb/tzdb_to_nx
vendored
Submodule externals/nx_tzdb/tzdb_to_nx updated: 212afa2394...0d17dd066d
1
externals/opus
vendored
Submodule
1
externals/opus
vendored
Submodule
Submodule externals/opus added at 101a71e03b
259
externals/opus/CMakeLists.txt
vendored
259
externals/opus/CMakeLists.txt
vendored
@ -1,259 +0,0 @@
|
|||||||
# SPDX-FileCopyrightText: 2019 yuzu Emulator Project
|
|
||||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.8)
|
|
||||||
|
|
||||||
project(opus)
|
|
||||||
|
|
||||||
option(OPUS_STACK_PROTECTOR "Use stack protection" OFF)
|
|
||||||
option(OPUS_USE_ALLOCA "Use alloca for stack arrays (on non-C99 compilers)" OFF)
|
|
||||||
option(OPUS_CUSTOM_MODES "Enable non-Opus modes, e.g. 44.1 kHz & 2^n frames" OFF)
|
|
||||||
option(OPUS_FIXED_POINT "Compile as fixed-point (for machines without a fast enough FPU)" OFF)
|
|
||||||
option(OPUS_ENABLE_FLOAT_API "Compile with the floating point API (for machines with float library" ON)
|
|
||||||
|
|
||||||
include(opus/opus_functions.cmake)
|
|
||||||
|
|
||||||
if(OPUS_STACK_PROTECTOR)
|
|
||||||
if(NOT MSVC) # GC on by default on MSVC
|
|
||||||
check_and_set_flag(STACK_PROTECTION_STRONG -fstack-protector-strong)
|
|
||||||
endif()
|
|
||||||
else()
|
|
||||||
if(MSVC)
|
|
||||||
check_and_set_flag(BUFFER_SECURITY_CHECK /GS-)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
add_library(opus
|
|
||||||
# CELT sources
|
|
||||||
opus/celt/bands.c
|
|
||||||
opus/celt/celt.c
|
|
||||||
opus/celt/celt_decoder.c
|
|
||||||
opus/celt/celt_encoder.c
|
|
||||||
opus/celt/celt_lpc.c
|
|
||||||
opus/celt/cwrs.c
|
|
||||||
opus/celt/entcode.c
|
|
||||||
opus/celt/entdec.c
|
|
||||||
opus/celt/entenc.c
|
|
||||||
opus/celt/kiss_fft.c
|
|
||||||
opus/celt/laplace.c
|
|
||||||
opus/celt/mathops.c
|
|
||||||
opus/celt/mdct.c
|
|
||||||
opus/celt/modes.c
|
|
||||||
opus/celt/pitch.c
|
|
||||||
opus/celt/quant_bands.c
|
|
||||||
opus/celt/rate.c
|
|
||||||
opus/celt/vq.c
|
|
||||||
|
|
||||||
# SILK sources
|
|
||||||
opus/silk/A2NLSF.c
|
|
||||||
opus/silk/CNG.c
|
|
||||||
opus/silk/HP_variable_cutoff.c
|
|
||||||
opus/silk/LPC_analysis_filter.c
|
|
||||||
opus/silk/LPC_fit.c
|
|
||||||
opus/silk/LPC_inv_pred_gain.c
|
|
||||||
opus/silk/LP_variable_cutoff.c
|
|
||||||
opus/silk/NLSF2A.c
|
|
||||||
opus/silk/NLSF_VQ.c
|
|
||||||
opus/silk/NLSF_VQ_weights_laroia.c
|
|
||||||
opus/silk/NLSF_decode.c
|
|
||||||
opus/silk/NLSF_del_dec_quant.c
|
|
||||||
opus/silk/NLSF_encode.c
|
|
||||||
opus/silk/NLSF_stabilize.c
|
|
||||||
opus/silk/NLSF_unpack.c
|
|
||||||
opus/silk/NSQ.c
|
|
||||||
opus/silk/NSQ_del_dec.c
|
|
||||||
opus/silk/PLC.c
|
|
||||||
opus/silk/VAD.c
|
|
||||||
opus/silk/VQ_WMat_EC.c
|
|
||||||
opus/silk/ana_filt_bank_1.c
|
|
||||||
opus/silk/biquad_alt.c
|
|
||||||
opus/silk/bwexpander.c
|
|
||||||
opus/silk/bwexpander_32.c
|
|
||||||
opus/silk/check_control_input.c
|
|
||||||
opus/silk/code_signs.c
|
|
||||||
opus/silk/control_SNR.c
|
|
||||||
opus/silk/control_audio_bandwidth.c
|
|
||||||
opus/silk/control_codec.c
|
|
||||||
opus/silk/dec_API.c
|
|
||||||
opus/silk/decode_core.c
|
|
||||||
opus/silk/decode_frame.c
|
|
||||||
opus/silk/decode_indices.c
|
|
||||||
opus/silk/decode_parameters.c
|
|
||||||
opus/silk/decode_pitch.c
|
|
||||||
opus/silk/decode_pulses.c
|
|
||||||
opus/silk/decoder_set_fs.c
|
|
||||||
opus/silk/enc_API.c
|
|
||||||
opus/silk/encode_indices.c
|
|
||||||
opus/silk/encode_pulses.c
|
|
||||||
opus/silk/gain_quant.c
|
|
||||||
opus/silk/init_decoder.c
|
|
||||||
opus/silk/init_encoder.c
|
|
||||||
opus/silk/inner_prod_aligned.c
|
|
||||||
opus/silk/interpolate.c
|
|
||||||
opus/silk/lin2log.c
|
|
||||||
opus/silk/log2lin.c
|
|
||||||
opus/silk/pitch_est_tables.c
|
|
||||||
opus/silk/process_NLSFs.c
|
|
||||||
opus/silk/quant_LTP_gains.c
|
|
||||||
opus/silk/resampler.c
|
|
||||||
opus/silk/resampler_down2.c
|
|
||||||
opus/silk/resampler_down2_3.c
|
|
||||||
opus/silk/resampler_private_AR2.c
|
|
||||||
opus/silk/resampler_private_IIR_FIR.c
|
|
||||||
opus/silk/resampler_private_down_FIR.c
|
|
||||||
opus/silk/resampler_private_up2_HQ.c
|
|
||||||
opus/silk/resampler_rom.c
|
|
||||||
opus/silk/shell_coder.c
|
|
||||||
opus/silk/sigm_Q15.c
|
|
||||||
opus/silk/sort.c
|
|
||||||
opus/silk/stereo_LR_to_MS.c
|
|
||||||
opus/silk/stereo_MS_to_LR.c
|
|
||||||
opus/silk/stereo_decode_pred.c
|
|
||||||
opus/silk/stereo_encode_pred.c
|
|
||||||
opus/silk/stereo_find_predictor.c
|
|
||||||
opus/silk/stereo_quant_pred.c
|
|
||||||
opus/silk/sum_sqr_shift.c
|
|
||||||
opus/silk/table_LSF_cos.c
|
|
||||||
opus/silk/tables_LTP.c
|
|
||||||
opus/silk/tables_NLSF_CB_NB_MB.c
|
|
||||||
opus/silk/tables_NLSF_CB_WB.c
|
|
||||||
opus/silk/tables_gain.c
|
|
||||||
opus/silk/tables_other.c
|
|
||||||
opus/silk/tables_pitch_lag.c
|
|
||||||
opus/silk/tables_pulses_per_block.c
|
|
||||||
|
|
||||||
# Opus sources
|
|
||||||
opus/src/analysis.c
|
|
||||||
opus/src/mapping_matrix.c
|
|
||||||
opus/src/mlp.c
|
|
||||||
opus/src/mlp_data.c
|
|
||||||
opus/src/opus.c
|
|
||||||
opus/src/opus_decoder.c
|
|
||||||
opus/src/opus_encoder.c
|
|
||||||
opus/src/opus_multistream.c
|
|
||||||
opus/src/opus_multistream_decoder.c
|
|
||||||
opus/src/opus_multistream_encoder.c
|
|
||||||
opus/src/opus_projection_decoder.c
|
|
||||||
opus/src/opus_projection_encoder.c
|
|
||||||
opus/src/repacketizer.c
|
|
||||||
)
|
|
||||||
|
|
||||||
if (DEBUG)
|
|
||||||
target_sources(opus PRIVATE opus/silk/debug.c)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (OPUS_FIXED_POINT)
|
|
||||||
target_sources(opus PRIVATE
|
|
||||||
opus/silk/fixed/LTP_analysis_filter_FIX.c
|
|
||||||
opus/silk/fixed/LTP_scale_ctrl_FIX.c
|
|
||||||
opus/silk/fixed/apply_sine_window_FIX.c
|
|
||||||
opus/silk/fixed/autocorr_FIX.c
|
|
||||||
opus/silk/fixed/burg_modified_FIX.c
|
|
||||||
opus/silk/fixed/corrMatrix_FIX.c
|
|
||||||
opus/silk/fixed/encode_frame_FIX.c
|
|
||||||
opus/silk/fixed/find_LPC_FIX.c
|
|
||||||
opus/silk/fixed/find_LTP_FIX.c
|
|
||||||
opus/silk/fixed/find_pitch_lags_FIX.c
|
|
||||||
opus/silk/fixed/find_pred_coefs_FIX.c
|
|
||||||
opus/silk/fixed/k2a_FIX.c
|
|
||||||
opus/silk/fixed/k2a_Q16_FIX.c
|
|
||||||
opus/silk/fixed/noise_shape_analysis_FIX.c
|
|
||||||
opus/silk/fixed/pitch_analysis_core_FIX.c
|
|
||||||
opus/silk/fixed/prefilter_FIX.c
|
|
||||||
opus/silk/fixed/process_gains_FIX.c
|
|
||||||
opus/silk/fixed/regularize_correlations_FIX.c
|
|
||||||
opus/silk/fixed/residual_energy16_FIX.c
|
|
||||||
opus/silk/fixed/residual_energy_FIX.c
|
|
||||||
opus/silk/fixed/schur64_FIX.c
|
|
||||||
opus/silk/fixed/schur_FIX.c
|
|
||||||
opus/silk/fixed/solve_LS_FIX.c
|
|
||||||
opus/silk/fixed/vector_ops_FIX.c
|
|
||||||
opus/silk/fixed/warped_autocorrelation_FIX.c
|
|
||||||
)
|
|
||||||
else()
|
|
||||||
target_sources(opus PRIVATE
|
|
||||||
opus/silk/float/LPC_analysis_filter_FLP.c
|
|
||||||
opus/silk/float/LPC_inv_pred_gain_FLP.c
|
|
||||||
opus/silk/float/LTP_analysis_filter_FLP.c
|
|
||||||
opus/silk/float/LTP_scale_ctrl_FLP.c
|
|
||||||
opus/silk/float/apply_sine_window_FLP.c
|
|
||||||
opus/silk/float/autocorrelation_FLP.c
|
|
||||||
opus/silk/float/burg_modified_FLP.c
|
|
||||||
opus/silk/float/bwexpander_FLP.c
|
|
||||||
opus/silk/float/corrMatrix_FLP.c
|
|
||||||
opus/silk/float/encode_frame_FLP.c
|
|
||||||
opus/silk/float/energy_FLP.c
|
|
||||||
opus/silk/float/find_LPC_FLP.c
|
|
||||||
opus/silk/float/find_LTP_FLP.c
|
|
||||||
opus/silk/float/find_pitch_lags_FLP.c
|
|
||||||
opus/silk/float/find_pred_coefs_FLP.c
|
|
||||||
opus/silk/float/inner_product_FLP.c
|
|
||||||
opus/silk/float/k2a_FLP.c
|
|
||||||
opus/silk/float/noise_shape_analysis_FLP.c
|
|
||||||
opus/silk/float/pitch_analysis_core_FLP.c
|
|
||||||
opus/silk/float/process_gains_FLP.c
|
|
||||||
opus/silk/float/regularize_correlations_FLP.c
|
|
||||||
opus/silk/float/residual_energy_FLP.c
|
|
||||||
opus/silk/float/scale_copy_vector_FLP.c
|
|
||||||
opus/silk/float/scale_vector_FLP.c
|
|
||||||
opus/silk/float/schur_FLP.c
|
|
||||||
opus/silk/float/sort_FLP.c
|
|
||||||
opus/silk/float/warped_autocorrelation_FLP.c
|
|
||||||
opus/silk/float/wrappers_FLP.c
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
target_compile_definitions(opus PRIVATE OPUS_BUILD ENABLE_HARDENING)
|
|
||||||
|
|
||||||
if(NOT MSVC)
|
|
||||||
if(MINGW)
|
|
||||||
target_compile_definitions(opus PRIVATE _FORTIFY_SOURCE=0)
|
|
||||||
else()
|
|
||||||
target_compile_definitions(opus PRIVATE _FORTIFY_SOURCE=2)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# It is strongly recommended to uncomment one of these VAR_ARRAYS: Use C99
|
|
||||||
# variable-length arrays for stack allocation USE_ALLOCA: Use alloca() for stack
|
|
||||||
# allocation If none is defined, then the fallback is a non-threadsafe global
|
|
||||||
# array
|
|
||||||
if(OPUS_USE_ALLOCA OR MSVC)
|
|
||||||
target_compile_definitions(opus PRIVATE USE_ALLOCA)
|
|
||||||
else()
|
|
||||||
target_compile_definitions(opus PRIVATE VAR_ARRAYS)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(OPUS_CUSTOM_MODES)
|
|
||||||
target_compile_definitions(opus PRIVATE CUSTOM_MODES)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(NOT OPUS_ENABLE_FLOAT_API)
|
|
||||||
target_compile_definitions(opus PRIVATE DISABLE_FLOAT_API)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
target_compile_definitions(opus
|
|
||||||
PUBLIC
|
|
||||||
-DOPUS_VERSION="\\"1.3.1\\""
|
|
||||||
|
|
||||||
PRIVATE
|
|
||||||
# Use C99 intrinsics to speed up float-to-int conversion
|
|
||||||
HAVE_LRINTF
|
|
||||||
)
|
|
||||||
|
|
||||||
if (FIXED_POINT)
|
|
||||||
target_compile_definitions(opus PRIVATE -DFIXED_POINT=1 -DDISABLE_FLOAT_API)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
target_include_directories(opus
|
|
||||||
PUBLIC
|
|
||||||
opus/include
|
|
||||||
|
|
||||||
PRIVATE
|
|
||||||
opus/celt
|
|
||||||
opus/silk
|
|
||||||
opus/silk/fixed
|
|
||||||
opus/silk/float
|
|
||||||
opus/src
|
|
||||||
)
|
|
||||||
|
|
||||||
add_library(Opus::opus ALIAS opus)
|
|
1
externals/opus/opus
vendored
1
externals/opus/opus
vendored
Submodule externals/opus/opus deleted from ad8fe90db7
7987
externals/stb/stb_image.h
vendored
Normal file
7987
externals/stb/stb_image.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
2634
externals/stb/stb_image_resize.h
vendored
Normal file
2634
externals/stb/stb_image_resize.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1724
externals/stb/stb_image_write.h
vendored
Normal file
1724
externals/stb/stb_image_write.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
2
externals/vcpkg
vendored
2
externals/vcpkg
vendored
Submodule externals/vcpkg updated: cbf56573a9...ef2eef1734
@ -27,7 +27,7 @@ android {
|
|||||||
namespace = "org.yuzu.yuzu_emu"
|
namespace = "org.yuzu.yuzu_emu"
|
||||||
|
|
||||||
compileSdkVersion = "android-34"
|
compileSdkVersion = "android-34"
|
||||||
ndkVersion = "25.2.9519653"
|
ndkVersion = "26.1.10909125"
|
||||||
|
|
||||||
buildFeatures {
|
buildFeatures {
|
||||||
viewBinding = true
|
viewBinding = true
|
||||||
@ -203,23 +203,23 @@ ktlint {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("androidx.core:core-ktx:1.10.1")
|
implementation("androidx.core:core-ktx:1.12.0")
|
||||||
implementation("androidx.appcompat:appcompat:1.6.1")
|
implementation("androidx.appcompat:appcompat:1.6.1")
|
||||||
implementation("androidx.recyclerview:recyclerview:1.3.0")
|
implementation("androidx.recyclerview:recyclerview:1.3.1")
|
||||||
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
|
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
|
||||||
implementation("androidx.fragment:fragment-ktx:1.6.0")
|
implementation("androidx.fragment:fragment-ktx:1.6.1")
|
||||||
implementation("androidx.documentfile:documentfile:1.0.1")
|
implementation("androidx.documentfile:documentfile:1.0.1")
|
||||||
implementation("com.google.android.material:material:1.9.0")
|
implementation("com.google.android.material:material:1.9.0")
|
||||||
implementation("androidx.preference:preference:1.2.0")
|
implementation("androidx.preference:preference-ktx:1.2.1")
|
||||||
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1")
|
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2")
|
||||||
implementation("io.coil-kt:coil:2.2.2")
|
implementation("io.coil-kt:coil:2.2.2")
|
||||||
implementation("androidx.core:core-splashscreen:1.0.1")
|
implementation("androidx.core:core-splashscreen:1.0.1")
|
||||||
implementation("androidx.window:window:1.2.0-beta03")
|
implementation("androidx.window:window:1.2.0-beta03")
|
||||||
implementation("org.ini4j:ini4j:0.5.4")
|
implementation("org.ini4j:ini4j:0.5.4")
|
||||||
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
|
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
|
||||||
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
|
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
|
||||||
implementation("androidx.navigation:navigation-fragment-ktx:2.6.0")
|
implementation("androidx.navigation:navigation-fragment-ktx:2.7.4")
|
||||||
implementation("androidx.navigation:navigation-ui-ktx:2.6.0")
|
implementation("androidx.navigation:navigation-ui-ktx:2.7.4")
|
||||||
implementation("info.debatty:java-string-similarity:2.0.0")
|
implementation("info.debatty:java-string-similarity:2.0.0")
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0")
|
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0")
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,6 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
|||||||
android:appCategory="game"
|
android:appCategory="game"
|
||||||
android:localeConfig="@xml/locales_config"
|
android:localeConfig="@xml/locales_config"
|
||||||
android:banner="@drawable/tv_banner"
|
android:banner="@drawable/tv_banner"
|
||||||
android:extractNativeLibs="true"
|
|
||||||
android:fullBackupContent="@xml/data_extraction_rules"
|
android:fullBackupContent="@xml/data_extraction_rules"
|
||||||
android:dataExtractionRules="@xml/data_extraction_rules_api_31"
|
android:dataExtractionRules="@xml/data_extraction_rules_api_31"
|
||||||
android:enableOnBackInvokedCallback="true">
|
android:enableOnBackInvokedCallback="true">
|
||||||
|
@ -5,6 +5,7 @@ package org.yuzu.yuzu_emu
|
|||||||
|
|
||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
import android.content.DialogInterface
|
import android.content.DialogInterface
|
||||||
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.text.Html
|
import android.text.Html
|
||||||
import android.text.method.LinkMovementMethod
|
import android.text.method.LinkMovementMethod
|
||||||
@ -15,13 +16,9 @@ import androidx.annotation.Keep
|
|||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
import org.yuzu.yuzu_emu.YuzuApplication.Companion.appContext
|
|
||||||
import org.yuzu.yuzu_emu.activities.EmulationActivity
|
import org.yuzu.yuzu_emu.activities.EmulationActivity
|
||||||
import org.yuzu.yuzu_emu.utils.DocumentsTree.Companion.isNativePath
|
import org.yuzu.yuzu_emu.utils.DocumentsTree
|
||||||
import org.yuzu.yuzu_emu.utils.FileUtil.exists
|
import org.yuzu.yuzu_emu.utils.FileUtil
|
||||||
import org.yuzu.yuzu_emu.utils.FileUtil.getFileSize
|
|
||||||
import org.yuzu.yuzu_emu.utils.FileUtil.isDirectory
|
|
||||||
import org.yuzu.yuzu_emu.utils.FileUtil.openContentUri
|
|
||||||
import org.yuzu.yuzu_emu.utils.Log
|
import org.yuzu.yuzu_emu.utils.Log
|
||||||
import org.yuzu.yuzu_emu.utils.SerializableHelper.serializable
|
import org.yuzu.yuzu_emu.utils.SerializableHelper.serializable
|
||||||
|
|
||||||
@ -72,43 +69,61 @@ object NativeLibrary {
|
|||||||
@Keep
|
@Keep
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun openContentUri(path: String?, openmode: String?): Int {
|
fun openContentUri(path: String?, openmode: String?): Int {
|
||||||
return if (isNativePath(path!!)) {
|
return if (DocumentsTree.isNativePath(path!!)) {
|
||||||
YuzuApplication.documentsTree!!.openContentUri(path, openmode)
|
YuzuApplication.documentsTree!!.openContentUri(path, openmode)
|
||||||
} else {
|
} else {
|
||||||
openContentUri(appContext, path, openmode)
|
FileUtil.openContentUri(path, openmode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Keep
|
@Keep
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun getSize(path: String?): Long {
|
fun getSize(path: String?): Long {
|
||||||
return if (isNativePath(path!!)) {
|
return if (DocumentsTree.isNativePath(path!!)) {
|
||||||
YuzuApplication.documentsTree!!.getFileSize(path)
|
YuzuApplication.documentsTree!!.getFileSize(path)
|
||||||
} else {
|
} else {
|
||||||
getFileSize(appContext, path)
|
FileUtil.getFileSize(path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Keep
|
@Keep
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun exists(path: String?): Boolean {
|
fun exists(path: String?): Boolean {
|
||||||
return if (isNativePath(path!!)) {
|
return if (DocumentsTree.isNativePath(path!!)) {
|
||||||
YuzuApplication.documentsTree!!.exists(path)
|
YuzuApplication.documentsTree!!.exists(path)
|
||||||
} else {
|
} else {
|
||||||
exists(appContext, path)
|
FileUtil.exists(path, suppressLog = true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Keep
|
@Keep
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun isDirectory(path: String?): Boolean {
|
fun isDirectory(path: String?): Boolean {
|
||||||
return if (isNativePath(path!!)) {
|
return if (DocumentsTree.isNativePath(path!!)) {
|
||||||
YuzuApplication.documentsTree!!.isDirectory(path)
|
YuzuApplication.documentsTree!!.isDirectory(path)
|
||||||
} else {
|
} else {
|
||||||
isDirectory(appContext, path)
|
FileUtil.isDirectory(path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Keep
|
||||||
|
@JvmStatic
|
||||||
|
fun getParentDirectory(path: String): String =
|
||||||
|
if (DocumentsTree.isNativePath(path)) {
|
||||||
|
YuzuApplication.documentsTree!!.getParentDirectory(path)
|
||||||
|
} else {
|
||||||
|
path
|
||||||
|
}
|
||||||
|
|
||||||
|
@Keep
|
||||||
|
@JvmStatic
|
||||||
|
fun getFilename(path: String): String =
|
||||||
|
if (DocumentsTree.isNativePath(path)) {
|
||||||
|
YuzuApplication.documentsTree!!.getFilename(path)
|
||||||
|
} else {
|
||||||
|
FileUtil.getFilename(Uri.parse(path))
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if pro controller isn't available and handheld is
|
* Returns true if pro controller isn't available and handheld is
|
||||||
*/
|
*/
|
||||||
@ -219,32 +234,6 @@ object NativeLibrary {
|
|||||||
|
|
||||||
external fun initGameIni(gameID: String?)
|
external fun initGameIni(gameID: String?)
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the embedded icon within the given ROM.
|
|
||||||
*
|
|
||||||
* @param filename the file path to the ROM.
|
|
||||||
* @return a byte array containing the JPEG data for the icon.
|
|
||||||
*/
|
|
||||||
external fun getIcon(filename: String): ByteArray
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the embedded title of the given ISO/ROM.
|
|
||||||
*
|
|
||||||
* @param filename The file path to the ISO/ROM.
|
|
||||||
* @return the embedded title of the ISO/ROM.
|
|
||||||
*/
|
|
||||||
external fun getTitle(filename: String): String
|
|
||||||
|
|
||||||
external fun getDescription(filename: String): String
|
|
||||||
|
|
||||||
external fun getGameId(filename: String): String
|
|
||||||
|
|
||||||
external fun getRegions(filename: String): String
|
|
||||||
|
|
||||||
external fun getCompany(filename: String): String
|
|
||||||
|
|
||||||
external fun isHomebrew(filename: String): Boolean
|
|
||||||
|
|
||||||
external fun setAppDirectory(directory: String)
|
external fun setAppDirectory(directory: String)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -297,11 +286,6 @@ object NativeLibrary {
|
|||||||
*/
|
*/
|
||||||
external fun stopEmulation()
|
external fun stopEmulation()
|
||||||
|
|
||||||
/**
|
|
||||||
* Resets the in-memory ROM metadata cache.
|
|
||||||
*/
|
|
||||||
external fun resetRomMetadata()
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if emulation is running (or is paused).
|
* Returns true if emulation is running (or is paused).
|
||||||
*/
|
*/
|
||||||
|
@ -47,7 +47,7 @@ class YuzuApplication : Application() {
|
|||||||
application = this
|
application = this
|
||||||
documentsTree = DocumentsTree()
|
documentsTree = DocumentsTree()
|
||||||
DirectoryInitialization.start()
|
DirectoryInitialization.start()
|
||||||
GpuDriverHelper.initializeDriverParameters(applicationContext)
|
GpuDriverHelper.initializeDriverParameters()
|
||||||
NativeLibrary.logDeviceInfo()
|
NativeLibrary.logDeviceInfo()
|
||||||
|
|
||||||
createNotificationChannels()
|
createNotificationChannels()
|
||||||
|
@ -0,0 +1,117 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
package org.yuzu.yuzu_emu.adapters
|
||||||
|
|
||||||
|
import android.text.TextUtils
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.recyclerview.widget.AsyncDifferConfig
|
||||||
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
|
import androidx.recyclerview.widget.ListAdapter
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import org.yuzu.yuzu_emu.R
|
||||||
|
import org.yuzu.yuzu_emu.databinding.CardDriverOptionBinding
|
||||||
|
import org.yuzu.yuzu_emu.model.DriverViewModel
|
||||||
|
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
|
||||||
|
import org.yuzu.yuzu_emu.utils.GpuDriverMetadata
|
||||||
|
|
||||||
|
class DriverAdapter(private val driverViewModel: DriverViewModel) :
|
||||||
|
ListAdapter<Pair<String, GpuDriverMetadata>, DriverAdapter.DriverViewHolder>(
|
||||||
|
AsyncDifferConfig.Builder(DiffCallback()).build()
|
||||||
|
) {
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DriverViewHolder {
|
||||||
|
val binding =
|
||||||
|
CardDriverOptionBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||||
|
return DriverViewHolder(binding)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount(): Int = currentList.size
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: DriverViewHolder, position: Int) =
|
||||||
|
holder.bind(currentList[position])
|
||||||
|
|
||||||
|
private fun onSelectDriver(position: Int) {
|
||||||
|
driverViewModel.setSelectedDriverIndex(position)
|
||||||
|
notifyItemChanged(driverViewModel.previouslySelectedDriver)
|
||||||
|
notifyItemChanged(driverViewModel.selectedDriver)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onDeleteDriver(driverData: Pair<String, GpuDriverMetadata>, position: Int) {
|
||||||
|
if (driverViewModel.selectedDriver > position) {
|
||||||
|
driverViewModel.setSelectedDriverIndex(driverViewModel.selectedDriver - 1)
|
||||||
|
}
|
||||||
|
if (GpuDriverHelper.customDriverData == driverData.second) {
|
||||||
|
driverViewModel.setSelectedDriverIndex(0)
|
||||||
|
}
|
||||||
|
driverViewModel.driversToDelete.add(driverData.first)
|
||||||
|
driverViewModel.removeDriver(driverData)
|
||||||
|
notifyItemRemoved(position)
|
||||||
|
notifyItemChanged(driverViewModel.selectedDriver)
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class DriverViewHolder(val binding: CardDriverOptionBinding) :
|
||||||
|
RecyclerView.ViewHolder(binding.root) {
|
||||||
|
private lateinit var driverData: Pair<String, GpuDriverMetadata>
|
||||||
|
|
||||||
|
fun bind(driverData: Pair<String, GpuDriverMetadata>) {
|
||||||
|
this.driverData = driverData
|
||||||
|
val driver = driverData.second
|
||||||
|
|
||||||
|
binding.apply {
|
||||||
|
radioButton.isChecked = driverViewModel.selectedDriver == bindingAdapterPosition
|
||||||
|
root.setOnClickListener {
|
||||||
|
onSelectDriver(bindingAdapterPosition)
|
||||||
|
}
|
||||||
|
buttonDelete.setOnClickListener {
|
||||||
|
onDeleteDriver(driverData, bindingAdapterPosition)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delay marquee by 3s
|
||||||
|
title.postDelayed(
|
||||||
|
{
|
||||||
|
title.isSelected = true
|
||||||
|
title.ellipsize = TextUtils.TruncateAt.MARQUEE
|
||||||
|
version.isSelected = true
|
||||||
|
version.ellipsize = TextUtils.TruncateAt.MARQUEE
|
||||||
|
description.isSelected = true
|
||||||
|
description.ellipsize = TextUtils.TruncateAt.MARQUEE
|
||||||
|
},
|
||||||
|
3000
|
||||||
|
)
|
||||||
|
if (driver.name == null) {
|
||||||
|
title.setText(R.string.system_gpu_driver)
|
||||||
|
description.text = ""
|
||||||
|
version.text = ""
|
||||||
|
version.visibility = View.GONE
|
||||||
|
description.visibility = View.GONE
|
||||||
|
buttonDelete.visibility = View.GONE
|
||||||
|
} else {
|
||||||
|
title.text = driver.name
|
||||||
|
version.text = driver.version
|
||||||
|
description.text = driver.description
|
||||||
|
version.visibility = View.VISIBLE
|
||||||
|
description.visibility = View.VISIBLE
|
||||||
|
buttonDelete.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DiffCallback : DiffUtil.ItemCallback<Pair<String, GpuDriverMetadata>>() {
|
||||||
|
override fun areItemsTheSame(
|
||||||
|
oldItem: Pair<String, GpuDriverMetadata>,
|
||||||
|
newItem: Pair<String, GpuDriverMetadata>
|
||||||
|
): Boolean {
|
||||||
|
return oldItem.first == newItem.first
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun areContentsTheSame(
|
||||||
|
oldItem: Pair<String, GpuDriverMetadata>,
|
||||||
|
newItem: Pair<String, GpuDriverMetadata>
|
||||||
|
): Boolean {
|
||||||
|
return oldItem.second == newItem.second
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -147,7 +147,7 @@ class GameAdapter(private val activity: AppCompatActivity) :
|
|||||||
|
|
||||||
private class DiffCallback : DiffUtil.ItemCallback<Game>() {
|
private class DiffCallback : DiffUtil.ItemCallback<Game>() {
|
||||||
override fun areItemsTheSame(oldItem: Game, newItem: Game): Boolean {
|
override fun areItemsTheSame(oldItem: Game, newItem: Game): Boolean {
|
||||||
return oldItem.gameId == newItem.gameId
|
return oldItem.programId == newItem.programId
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun areContentsTheSame(oldItem: Game, newItem: Game): Boolean {
|
override fun areContentsTheSame(oldItem: Game, newItem: Game): Boolean {
|
||||||
|
@ -0,0 +1,186 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
package org.yuzu.yuzu_emu.fragments
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
|
import androidx.core.view.ViewCompat
|
||||||
|
import androidx.core.view.WindowInsetsCompat
|
||||||
|
import androidx.core.view.updatePadding
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.fragment.app.activityViewModels
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.navigation.findNavController
|
||||||
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
|
import com.google.android.material.transition.MaterialSharedAxis
|
||||||
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import org.yuzu.yuzu_emu.R
|
||||||
|
import org.yuzu.yuzu_emu.adapters.DriverAdapter
|
||||||
|
import org.yuzu.yuzu_emu.databinding.FragmentDriverManagerBinding
|
||||||
|
import org.yuzu.yuzu_emu.model.DriverViewModel
|
||||||
|
import org.yuzu.yuzu_emu.model.HomeViewModel
|
||||||
|
import org.yuzu.yuzu_emu.utils.FileUtil
|
||||||
|
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
|
||||||
|
import java.io.File
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
|
class DriverManagerFragment : Fragment() {
|
||||||
|
private var _binding: FragmentDriverManagerBinding? = null
|
||||||
|
private val binding get() = _binding!!
|
||||||
|
|
||||||
|
private val homeViewModel: HomeViewModel by activityViewModels()
|
||||||
|
private val driverViewModel: DriverViewModel by activityViewModels()
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
|
||||||
|
returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
|
||||||
|
reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View {
|
||||||
|
_binding = FragmentDriverManagerBinding.inflate(inflater)
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
homeViewModel.setNavigationVisibility(visible = false, animated = true)
|
||||||
|
homeViewModel.setStatusBarShadeVisibility(visible = false)
|
||||||
|
|
||||||
|
if (!driverViewModel.isInteractionAllowed) {
|
||||||
|
DriversLoadingDialogFragment().show(
|
||||||
|
childFragmentManager,
|
||||||
|
DriversLoadingDialogFragment.TAG
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.toolbarDrivers.setNavigationOnClickListener {
|
||||||
|
binding.root.findNavController().popBackStack()
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.buttonInstall.setOnClickListener {
|
||||||
|
getDriver.launch(arrayOf("application/zip"))
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.listDrivers.apply {
|
||||||
|
layoutManager = GridLayoutManager(
|
||||||
|
requireContext(),
|
||||||
|
resources.getInteger(R.integer.grid_columns)
|
||||||
|
)
|
||||||
|
adapter = DriverAdapter(driverViewModel)
|
||||||
|
}
|
||||||
|
|
||||||
|
viewLifecycleOwner.lifecycleScope.apply {
|
||||||
|
launch {
|
||||||
|
driverViewModel.driverList.collectLatest {
|
||||||
|
(binding.listDrivers.adapter as DriverAdapter).submitList(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
launch {
|
||||||
|
driverViewModel.newDriverInstalled.collect {
|
||||||
|
if (_binding != null && it) {
|
||||||
|
(binding.listDrivers.adapter as DriverAdapter).apply {
|
||||||
|
notifyItemChanged(driverViewModel.previouslySelectedDriver)
|
||||||
|
notifyItemChanged(driverViewModel.selectedDriver)
|
||||||
|
driverViewModel.setNewDriverInstalled(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setInsets()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start installing requested driver
|
||||||
|
override fun onStop() {
|
||||||
|
super.onStop()
|
||||||
|
driverViewModel.onCloseDriverManager()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setInsets() =
|
||||||
|
ViewCompat.setOnApplyWindowInsetsListener(
|
||||||
|
binding.root
|
||||||
|
) { _: View, windowInsets: WindowInsetsCompat ->
|
||||||
|
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||||
|
val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
|
||||||
|
|
||||||
|
val leftInsets = barInsets.left + cutoutInsets.left
|
||||||
|
val rightInsets = barInsets.right + cutoutInsets.right
|
||||||
|
|
||||||
|
val mlpAppBar = binding.toolbarDrivers.layoutParams as ViewGroup.MarginLayoutParams
|
||||||
|
mlpAppBar.leftMargin = leftInsets
|
||||||
|
mlpAppBar.rightMargin = rightInsets
|
||||||
|
binding.toolbarDrivers.layoutParams = mlpAppBar
|
||||||
|
|
||||||
|
val mlplistDrivers = binding.listDrivers.layoutParams as ViewGroup.MarginLayoutParams
|
||||||
|
mlplistDrivers.leftMargin = leftInsets
|
||||||
|
mlplistDrivers.rightMargin = rightInsets
|
||||||
|
binding.listDrivers.layoutParams = mlplistDrivers
|
||||||
|
|
||||||
|
val fabSpacing = resources.getDimensionPixelSize(R.dimen.spacing_fab)
|
||||||
|
val mlpFab =
|
||||||
|
binding.buttonInstall.layoutParams as ViewGroup.MarginLayoutParams
|
||||||
|
mlpFab.leftMargin = leftInsets + fabSpacing
|
||||||
|
mlpFab.rightMargin = rightInsets + fabSpacing
|
||||||
|
mlpFab.bottomMargin = barInsets.bottom + fabSpacing
|
||||||
|
binding.buttonInstall.layoutParams = mlpFab
|
||||||
|
|
||||||
|
binding.listDrivers.updatePadding(
|
||||||
|
bottom = barInsets.bottom +
|
||||||
|
resources.getDimensionPixelSize(R.dimen.spacing_bottom_list_fab)
|
||||||
|
)
|
||||||
|
|
||||||
|
windowInsets
|
||||||
|
}
|
||||||
|
|
||||||
|
private val getDriver =
|
||||||
|
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
|
||||||
|
if (result == null) {
|
||||||
|
return@registerForActivityResult
|
||||||
|
}
|
||||||
|
|
||||||
|
IndeterminateProgressDialogFragment.newInstance(
|
||||||
|
requireActivity(),
|
||||||
|
R.string.installing_driver,
|
||||||
|
false
|
||||||
|
) {
|
||||||
|
val driverPath =
|
||||||
|
"${GpuDriverHelper.driverStoragePath}/${FileUtil.getFilename(result)}"
|
||||||
|
val driverFile = File(driverPath)
|
||||||
|
|
||||||
|
// Ignore file exceptions when a user selects an invalid zip
|
||||||
|
try {
|
||||||
|
if (!GpuDriverHelper.copyDriverToInternalStorage(result)) {
|
||||||
|
throw IOException("Driver failed validation!")
|
||||||
|
}
|
||||||
|
} catch (_: IOException) {
|
||||||
|
if (driverFile.exists()) {
|
||||||
|
driverFile.delete()
|
||||||
|
}
|
||||||
|
return@newInstance getString(R.string.select_gpu_driver_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
val driverData = GpuDriverHelper.getMetadataFromZip(driverFile)
|
||||||
|
val driverInList =
|
||||||
|
driverViewModel.driverList.value.firstOrNull { it.second == driverData }
|
||||||
|
if (driverInList != null) {
|
||||||
|
return@newInstance getString(R.string.driver_already_installed)
|
||||||
|
} else {
|
||||||
|
driverViewModel.addDriver(Pair(driverPath, driverData))
|
||||||
|
driverViewModel.setNewDriverInstalled(true)
|
||||||
|
}
|
||||||
|
return@newInstance Any()
|
||||||
|
}.show(childFragmentManager, IndeterminateProgressDialogFragment.TAG)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,75 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
package org.yuzu.yuzu_emu.fragments
|
||||||
|
|
||||||
|
import android.app.Dialog
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.fragment.app.DialogFragment
|
||||||
|
import androidx.fragment.app.activityViewModels
|
||||||
|
import androidx.lifecycle.Lifecycle
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.lifecycle.repeatOnLifecycle
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import org.yuzu.yuzu_emu.R
|
||||||
|
import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
|
||||||
|
import org.yuzu.yuzu_emu.model.DriverViewModel
|
||||||
|
|
||||||
|
class DriversLoadingDialogFragment : DialogFragment() {
|
||||||
|
private val driverViewModel: DriverViewModel by activityViewModels()
|
||||||
|
|
||||||
|
private lateinit var binding: DialogProgressBarBinding
|
||||||
|
|
||||||
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
|
binding = DialogProgressBarBinding.inflate(layoutInflater)
|
||||||
|
binding.progressBar.isIndeterminate = true
|
||||||
|
|
||||||
|
isCancelable = false
|
||||||
|
|
||||||
|
return MaterialAlertDialogBuilder(requireContext())
|
||||||
|
.setTitle(R.string.loading)
|
||||||
|
.setView(binding.root)
|
||||||
|
.create()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View = binding.root
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
viewLifecycleOwner.lifecycleScope.apply {
|
||||||
|
launch {
|
||||||
|
repeatOnLifecycle(Lifecycle.State.RESUMED) {
|
||||||
|
driverViewModel.areDriversLoading.collect { checkForDismiss() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
launch {
|
||||||
|
repeatOnLifecycle(Lifecycle.State.RESUMED) {
|
||||||
|
driverViewModel.isDriverReady.collect { checkForDismiss() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
launch {
|
||||||
|
repeatOnLifecycle(Lifecycle.State.RESUMED) {
|
||||||
|
driverViewModel.isDeletingDrivers.collect { checkForDismiss() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkForDismiss() {
|
||||||
|
if (driverViewModel.isInteractionAllowed) {
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val TAG = "DriversLoadingDialogFragment"
|
||||||
|
}
|
||||||
|
}
|
@ -15,6 +15,7 @@ import android.net.Uri
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
|
import android.os.SystemClock
|
||||||
import android.view.*
|
import android.view.*
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
@ -25,6 +26,7 @@ import androidx.core.graphics.Insets
|
|||||||
import androidx.core.view.ViewCompat
|
import androidx.core.view.ViewCompat
|
||||||
import androidx.core.view.WindowInsetsCompat
|
import androidx.core.view.WindowInsetsCompat
|
||||||
import androidx.drawerlayout.widget.DrawerLayout
|
import androidx.drawerlayout.widget.DrawerLayout
|
||||||
|
import androidx.drawerlayout.widget.DrawerLayout.DrawerListener
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
@ -39,6 +41,7 @@ import androidx.window.layout.WindowLayoutInfo
|
|||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import com.google.android.material.slider.Slider
|
import com.google.android.material.slider.Slider
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.collect
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.yuzu.yuzu_emu.HomeNavigationDirections
|
import org.yuzu.yuzu_emu.HomeNavigationDirections
|
||||||
@ -50,6 +53,7 @@ import org.yuzu.yuzu_emu.databinding.DialogOverlayAdjustBinding
|
|||||||
import org.yuzu.yuzu_emu.databinding.FragmentEmulationBinding
|
import org.yuzu.yuzu_emu.databinding.FragmentEmulationBinding
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
|
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.Settings
|
import org.yuzu.yuzu_emu.features.settings.model.Settings
|
||||||
|
import org.yuzu.yuzu_emu.model.DriverViewModel
|
||||||
import org.yuzu.yuzu_emu.model.Game
|
import org.yuzu.yuzu_emu.model.Game
|
||||||
import org.yuzu.yuzu_emu.model.EmulationViewModel
|
import org.yuzu.yuzu_emu.model.EmulationViewModel
|
||||||
import org.yuzu.yuzu_emu.overlay.InputOverlay
|
import org.yuzu.yuzu_emu.overlay.InputOverlay
|
||||||
@ -70,6 +74,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
|||||||
private lateinit var game: Game
|
private lateinit var game: Game
|
||||||
|
|
||||||
private val emulationViewModel: EmulationViewModel by activityViewModels()
|
private val emulationViewModel: EmulationViewModel by activityViewModels()
|
||||||
|
private val driverViewModel: DriverViewModel by activityViewModels()
|
||||||
|
|
||||||
private var isInFoldableLayout = false
|
private var isInFoldableLayout = false
|
||||||
|
|
||||||
@ -153,6 +158,32 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
|||||||
binding.showFpsText.setTextColor(Color.YELLOW)
|
binding.showFpsText.setTextColor(Color.YELLOW)
|
||||||
binding.doneControlConfig.setOnClickListener { stopConfiguringControls() }
|
binding.doneControlConfig.setOnClickListener { stopConfiguringControls() }
|
||||||
|
|
||||||
|
binding.drawerLayout.addDrawerListener(object : DrawerListener {
|
||||||
|
override fun onDrawerSlide(drawerView: View, slideOffset: Float) {
|
||||||
|
binding.surfaceInputOverlay.dispatchTouchEvent(
|
||||||
|
MotionEvent.obtain(
|
||||||
|
SystemClock.uptimeMillis(),
|
||||||
|
SystemClock.uptimeMillis() + 100,
|
||||||
|
MotionEvent.ACTION_UP,
|
||||||
|
0f,
|
||||||
|
0f,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDrawerOpened(drawerView: View) {
|
||||||
|
// No op
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDrawerClosed(drawerView: View) {
|
||||||
|
// No op
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDrawerStateChanged(newState: Int) {
|
||||||
|
// No op
|
||||||
|
}
|
||||||
|
})
|
||||||
binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
|
binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
|
||||||
binding.inGameMenu.getHeaderView(0).findViewById<TextView>(R.id.text_game_title).text =
|
binding.inGameMenu.getHeaderView(0).findViewById<TextView>(R.id.text_game_title).text =
|
||||||
game.title
|
game.title
|
||||||
@ -299,6 +330,21 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
launch {
|
||||||
|
repeatOnLifecycle(Lifecycle.State.RESUMED) {
|
||||||
|
driverViewModel.isDriverReady.collect {
|
||||||
|
if (it && !emulationState.isRunning) {
|
||||||
|
if (!DirectoryInitialization.areDirectoriesReady) {
|
||||||
|
DirectoryInitialization.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
updateScreenLayout()
|
||||||
|
|
||||||
|
emulationState.run(emulationActivity!!.isActivityRecreated)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -332,17 +378,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
|
||||||
super.onResume()
|
|
||||||
if (!DirectoryInitialization.areDirectoriesReady) {
|
|
||||||
DirectoryInitialization.start()
|
|
||||||
}
|
|
||||||
|
|
||||||
updateScreenLayout()
|
|
||||||
|
|
||||||
emulationState.run(emulationActivity!!.isActivityRecreated)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onPause() {
|
override fun onPause() {
|
||||||
if (emulationState.isRunning && emulationActivity?.isInPictureInPictureMode != true) {
|
if (emulationState.isRunning && emulationActivity?.isInPictureInPictureMode != true) {
|
||||||
emulationState.pause()
|
emulationState.pause()
|
||||||
|
@ -5,7 +5,6 @@ package org.yuzu.yuzu_emu.fragments
|
|||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.content.ActivityNotFoundException
|
import android.content.ActivityNotFoundException
|
||||||
import android.content.DialogInterface
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
@ -27,8 +26,7 @@ import androidx.fragment.app.Fragment
|
|||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
import androidx.navigation.findNavController
|
import androidx.navigation.findNavController
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
|
||||||
import com.google.android.material.transition.MaterialSharedAxis
|
import com.google.android.material.transition.MaterialSharedAxis
|
||||||
import org.yuzu.yuzu_emu.BuildConfig
|
import org.yuzu.yuzu_emu.BuildConfig
|
||||||
import org.yuzu.yuzu_emu.HomeNavigationDirections
|
import org.yuzu.yuzu_emu.HomeNavigationDirections
|
||||||
@ -37,6 +35,7 @@ import org.yuzu.yuzu_emu.adapters.HomeSettingAdapter
|
|||||||
import org.yuzu.yuzu_emu.databinding.FragmentHomeSettingsBinding
|
import org.yuzu.yuzu_emu.databinding.FragmentHomeSettingsBinding
|
||||||
import org.yuzu.yuzu_emu.features.DocumentProvider
|
import org.yuzu.yuzu_emu.features.DocumentProvider
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.Settings
|
import org.yuzu.yuzu_emu.features.settings.model.Settings
|
||||||
|
import org.yuzu.yuzu_emu.model.DriverViewModel
|
||||||
import org.yuzu.yuzu_emu.model.HomeSetting
|
import org.yuzu.yuzu_emu.model.HomeSetting
|
||||||
import org.yuzu.yuzu_emu.model.HomeViewModel
|
import org.yuzu.yuzu_emu.model.HomeViewModel
|
||||||
import org.yuzu.yuzu_emu.ui.main.MainActivity
|
import org.yuzu.yuzu_emu.ui.main.MainActivity
|
||||||
@ -50,6 +49,7 @@ class HomeSettingsFragment : Fragment() {
|
|||||||
private lateinit var mainActivity: MainActivity
|
private lateinit var mainActivity: MainActivity
|
||||||
|
|
||||||
private val homeViewModel: HomeViewModel by activityViewModels()
|
private val homeViewModel: HomeViewModel by activityViewModels()
|
||||||
|
private val driverViewModel: DriverViewModel by activityViewModels()
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
@ -107,13 +107,17 @@ class HomeSettingsFragment : Fragment() {
|
|||||||
)
|
)
|
||||||
add(
|
add(
|
||||||
HomeSetting(
|
HomeSetting(
|
||||||
R.string.install_gpu_driver,
|
R.string.gpu_driver_manager,
|
||||||
R.string.install_gpu_driver_description,
|
R.string.install_gpu_driver_description,
|
||||||
R.drawable.ic_exit,
|
R.drawable.ic_build,
|
||||||
{ driverInstaller() },
|
{
|
||||||
|
binding.root.findNavController()
|
||||||
|
.navigate(R.id.action_homeSettingsFragment_to_driverManagerFragment)
|
||||||
|
},
|
||||||
{ GpuDriverHelper.supportsCustomDriverLoading() },
|
{ GpuDriverHelper.supportsCustomDriverLoading() },
|
||||||
R.string.custom_driver_not_supported,
|
R.string.custom_driver_not_supported,
|
||||||
R.string.custom_driver_not_supported_description
|
R.string.custom_driver_not_supported_description,
|
||||||
|
driverViewModel.selectedDriverMetadata
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
add(
|
add(
|
||||||
@ -182,7 +186,8 @@ class HomeSettingsFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
binding.homeSettingsList.apply {
|
binding.homeSettingsList.apply {
|
||||||
layoutManager = LinearLayoutManager(requireContext())
|
layoutManager =
|
||||||
|
GridLayoutManager(requireContext(), resources.getInteger(R.integer.grid_columns))
|
||||||
adapter = HomeSettingAdapter(
|
adapter = HomeSettingAdapter(
|
||||||
requireActivity() as AppCompatActivity,
|
requireActivity() as AppCompatActivity,
|
||||||
viewLifecycleOwner,
|
viewLifecycleOwner,
|
||||||
@ -292,31 +297,6 @@ class HomeSettingsFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun driverInstaller() {
|
|
||||||
// Get the driver name for the dialog message.
|
|
||||||
var driverName = GpuDriverHelper.customDriverName
|
|
||||||
if (driverName == null) {
|
|
||||||
driverName = getString(R.string.system_gpu_driver)
|
|
||||||
}
|
|
||||||
|
|
||||||
MaterialAlertDialogBuilder(requireContext())
|
|
||||||
.setTitle(getString(R.string.select_gpu_driver_title))
|
|
||||||
.setMessage(driverName)
|
|
||||||
.setNegativeButton(android.R.string.cancel, null)
|
|
||||||
.setNeutralButton(R.string.select_gpu_driver_default) { _: DialogInterface?, _: Int ->
|
|
||||||
GpuDriverHelper.installDefaultDriver(requireContext())
|
|
||||||
Toast.makeText(
|
|
||||||
requireContext(),
|
|
||||||
R.string.select_gpu_driver_use_default,
|
|
||||||
Toast.LENGTH_SHORT
|
|
||||||
).show()
|
|
||||||
}
|
|
||||||
.setPositiveButton(R.string.select_gpu_driver_install) { _: DialogInterface?, _: Int ->
|
|
||||||
mainActivity.getDriver.launch(arrayOf("application/zip"))
|
|
||||||
}
|
|
||||||
.show()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun shareLog() {
|
private fun shareLog() {
|
||||||
val file = DocumentFile.fromSingleUri(
|
val file = DocumentFile.fromSingleUri(
|
||||||
mainActivity,
|
mainActivity,
|
||||||
|
@ -10,8 +10,8 @@ import android.view.View
|
|||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
|
||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
|
import androidx.fragment.app.FragmentActivity
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
@ -78,6 +78,10 @@ class IndeterminateProgressDialogFragment : DialogFragment() {
|
|||||||
requireActivity().supportFragmentManager,
|
requireActivity().supportFragmentManager,
|
||||||
MessageDialogFragment.TAG
|
MessageDialogFragment.TAG
|
||||||
)
|
)
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
}
|
}
|
||||||
taskViewModel.clear()
|
taskViewModel.clear()
|
||||||
}
|
}
|
||||||
@ -115,7 +119,7 @@ class IndeterminateProgressDialogFragment : DialogFragment() {
|
|||||||
private const val CANCELLABLE = "Cancellable"
|
private const val CANCELLABLE = "Cancellable"
|
||||||
|
|
||||||
fun newInstance(
|
fun newInstance(
|
||||||
activity: AppCompatActivity,
|
activity: FragmentActivity,
|
||||||
titleId: Int,
|
titleId: Int,
|
||||||
cancellable: Boolean = false,
|
cancellable: Boolean = false,
|
||||||
task: () -> Any
|
task: () -> Any
|
||||||
|
@ -0,0 +1,158 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
package org.yuzu.yuzu_emu.model
|
||||||
|
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import org.yuzu.yuzu_emu.R
|
||||||
|
import org.yuzu.yuzu_emu.YuzuApplication
|
||||||
|
import org.yuzu.yuzu_emu.utils.FileUtil
|
||||||
|
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
|
||||||
|
import org.yuzu.yuzu_emu.utils.GpuDriverMetadata
|
||||||
|
import java.io.BufferedOutputStream
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
class DriverViewModel : ViewModel() {
|
||||||
|
private val _areDriversLoading = MutableStateFlow(false)
|
||||||
|
val areDriversLoading: StateFlow<Boolean> get() = _areDriversLoading
|
||||||
|
|
||||||
|
private val _isDriverReady = MutableStateFlow(true)
|
||||||
|
val isDriverReady: StateFlow<Boolean> get() = _isDriverReady
|
||||||
|
|
||||||
|
private val _isDeletingDrivers = MutableStateFlow(false)
|
||||||
|
val isDeletingDrivers: StateFlow<Boolean> get() = _isDeletingDrivers
|
||||||
|
|
||||||
|
private val _driverList = MutableStateFlow(mutableListOf<Pair<String, GpuDriverMetadata>>())
|
||||||
|
val driverList: StateFlow<MutableList<Pair<String, GpuDriverMetadata>>> get() = _driverList
|
||||||
|
|
||||||
|
var previouslySelectedDriver = 0
|
||||||
|
var selectedDriver = -1
|
||||||
|
|
||||||
|
private val _selectedDriverMetadata =
|
||||||
|
MutableStateFlow(
|
||||||
|
GpuDriverHelper.customDriverData.name
|
||||||
|
?: YuzuApplication.appContext.getString(R.string.system_gpu_driver)
|
||||||
|
)
|
||||||
|
val selectedDriverMetadata: StateFlow<String> get() = _selectedDriverMetadata
|
||||||
|
|
||||||
|
private val _newDriverInstalled = MutableStateFlow(false)
|
||||||
|
val newDriverInstalled: StateFlow<Boolean> get() = _newDriverInstalled
|
||||||
|
|
||||||
|
val driversToDelete = mutableListOf<String>()
|
||||||
|
|
||||||
|
val isInteractionAllowed
|
||||||
|
get() = !areDriversLoading.value && isDriverReady.value && !isDeletingDrivers.value
|
||||||
|
|
||||||
|
init {
|
||||||
|
_areDriversLoading.value = true
|
||||||
|
viewModelScope.launch {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
val drivers = GpuDriverHelper.getDrivers()
|
||||||
|
val currentDriverMetadata = GpuDriverHelper.customDriverData
|
||||||
|
for (i in drivers.indices) {
|
||||||
|
if (drivers[i].second == currentDriverMetadata) {
|
||||||
|
setSelectedDriverIndex(i)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If a user had installed a driver before the manager was implemented, this zips
|
||||||
|
// the installed driver to UserData/gpu_drivers/CustomDriver.zip so that it can
|
||||||
|
// be indexed and exported as expected.
|
||||||
|
if (selectedDriver == -1) {
|
||||||
|
val driverToSave =
|
||||||
|
File(GpuDriverHelper.driverStoragePath, "CustomDriver.zip")
|
||||||
|
driverToSave.createNewFile()
|
||||||
|
FileUtil.zipFromInternalStorage(
|
||||||
|
File(GpuDriverHelper.driverInstallationPath!!),
|
||||||
|
GpuDriverHelper.driverInstallationPath!!,
|
||||||
|
BufferedOutputStream(driverToSave.outputStream())
|
||||||
|
)
|
||||||
|
drivers.add(Pair(driverToSave.path, currentDriverMetadata))
|
||||||
|
setSelectedDriverIndex(drivers.size - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
_driverList.value = drivers
|
||||||
|
_areDriversLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setSelectedDriverIndex(value: Int) {
|
||||||
|
if (selectedDriver != -1) {
|
||||||
|
previouslySelectedDriver = selectedDriver
|
||||||
|
}
|
||||||
|
selectedDriver = value
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setNewDriverInstalled(value: Boolean) {
|
||||||
|
_newDriverInstalled.value = value
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addDriver(driverData: Pair<String, GpuDriverMetadata>) {
|
||||||
|
val driverIndex = _driverList.value.indexOfFirst { it == driverData }
|
||||||
|
if (driverIndex == -1) {
|
||||||
|
setSelectedDriverIndex(_driverList.value.size)
|
||||||
|
_driverList.value.add(driverData)
|
||||||
|
_selectedDriverMetadata.value = driverData.second.name
|
||||||
|
?: YuzuApplication.appContext.getString(R.string.system_gpu_driver)
|
||||||
|
} else {
|
||||||
|
setSelectedDriverIndex(driverIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeDriver(driverData: Pair<String, GpuDriverMetadata>) {
|
||||||
|
_driverList.value.remove(driverData)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onCloseDriverManager() {
|
||||||
|
_isDeletingDrivers.value = true
|
||||||
|
viewModelScope.launch {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
driversToDelete.forEach {
|
||||||
|
val driver = File(it)
|
||||||
|
if (driver.exists()) {
|
||||||
|
driver.delete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
driversToDelete.clear()
|
||||||
|
_isDeletingDrivers.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GpuDriverHelper.customDriverData == driverList.value[selectedDriver].second) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_isDriverReady.value = false
|
||||||
|
viewModelScope.launch {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
if (selectedDriver == 0) {
|
||||||
|
GpuDriverHelper.installDefaultDriver()
|
||||||
|
setDriverReady()
|
||||||
|
return@withContext
|
||||||
|
}
|
||||||
|
|
||||||
|
val driverToInstall = File(driverList.value[selectedDriver].first)
|
||||||
|
if (driverToInstall.exists()) {
|
||||||
|
GpuDriverHelper.installCustomDriver(driverToInstall)
|
||||||
|
} else {
|
||||||
|
GpuDriverHelper.installDefaultDriver()
|
||||||
|
}
|
||||||
|
setDriverReady()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setDriverReady() {
|
||||||
|
_isDriverReady.value = true
|
||||||
|
_selectedDriverMetadata.value = GpuDriverHelper.customDriverData.name
|
||||||
|
?: YuzuApplication.appContext.getString(R.string.system_gpu_driver)
|
||||||
|
}
|
||||||
|
}
|
@ -12,15 +12,14 @@ import kotlinx.serialization.Serializable
|
|||||||
@Serializable
|
@Serializable
|
||||||
class Game(
|
class Game(
|
||||||
val title: String,
|
val title: String,
|
||||||
val description: String,
|
|
||||||
val regions: String,
|
|
||||||
val path: String,
|
val path: String,
|
||||||
val gameId: String,
|
val programId: String,
|
||||||
val company: String,
|
val developer: String,
|
||||||
|
val version: String,
|
||||||
val isHomebrew: Boolean
|
val isHomebrew: Boolean
|
||||||
) : Parcelable {
|
) : Parcelable {
|
||||||
val keyAddedToLibraryTime get() = "${gameId}_AddedToLibraryTime"
|
val keyAddedToLibraryTime get() = "${programId}_AddedToLibraryTime"
|
||||||
val keyLastPlayedTime get() = "${gameId}_LastPlayed"
|
val keyLastPlayedTime get() = "${programId}_LastPlayed"
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
if (other !is Game) {
|
if (other !is Game) {
|
||||||
@ -32,11 +31,9 @@ class Game(
|
|||||||
|
|
||||||
override fun hashCode(): Int {
|
override fun hashCode(): Int {
|
||||||
var result = title.hashCode()
|
var result = title.hashCode()
|
||||||
result = 31 * result + description.hashCode()
|
|
||||||
result = 31 * result + regions.hashCode()
|
|
||||||
result = 31 * result + path.hashCode()
|
result = 31 * result + path.hashCode()
|
||||||
result = 31 * result + gameId.hashCode()
|
result = 31 * result + programId.hashCode()
|
||||||
result = 31 * result + company.hashCode()
|
result = 31 * result + developer.hashCode()
|
||||||
result = 31 * result + isHomebrew.hashCode()
|
result = 31 * result + isHomebrew.hashCode()
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
@ -14,15 +14,13 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
|||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import kotlinx.serialization.ExperimentalSerializationApi
|
|
||||||
import kotlinx.serialization.MissingFieldException
|
|
||||||
import kotlinx.serialization.decodeFromString
|
import kotlinx.serialization.decodeFromString
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import org.yuzu.yuzu_emu.NativeLibrary
|
import org.yuzu.yuzu_emu.NativeLibrary
|
||||||
import org.yuzu.yuzu_emu.YuzuApplication
|
import org.yuzu.yuzu_emu.YuzuApplication
|
||||||
import org.yuzu.yuzu_emu.utils.GameHelper
|
import org.yuzu.yuzu_emu.utils.GameHelper
|
||||||
|
import org.yuzu.yuzu_emu.utils.GameMetadata
|
||||||
|
|
||||||
@OptIn(ExperimentalSerializationApi::class)
|
|
||||||
class GamesViewModel : ViewModel() {
|
class GamesViewModel : ViewModel() {
|
||||||
val games: StateFlow<List<Game>> get() = _games
|
val games: StateFlow<List<Game>> get() = _games
|
||||||
private val _games = MutableStateFlow(emptyList<Game>())
|
private val _games = MutableStateFlow(emptyList<Game>())
|
||||||
@ -49,26 +47,34 @@ class GamesViewModel : ViewModel() {
|
|||||||
// Retrieve list of cached games
|
// Retrieve list of cached games
|
||||||
val storedGames = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
|
val storedGames = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
|
||||||
.getStringSet(GameHelper.KEY_GAMES, emptySet())
|
.getStringSet(GameHelper.KEY_GAMES, emptySet())
|
||||||
if (storedGames!!.isNotEmpty()) {
|
|
||||||
val deserializedGames = mutableSetOf<Game>()
|
|
||||||
storedGames.forEach {
|
|
||||||
val game: Game
|
|
||||||
try {
|
|
||||||
game = Json.decodeFromString(it)
|
|
||||||
} catch (e: MissingFieldException) {
|
|
||||||
return@forEach
|
|
||||||
}
|
|
||||||
|
|
||||||
val gameExists =
|
viewModelScope.launch {
|
||||||
DocumentFile.fromSingleUri(YuzuApplication.appContext, Uri.parse(game.path))
|
withContext(Dispatchers.IO) {
|
||||||
?.exists()
|
if (storedGames!!.isNotEmpty()) {
|
||||||
if (gameExists == true) {
|
val deserializedGames = mutableSetOf<Game>()
|
||||||
deserializedGames.add(game)
|
storedGames.forEach {
|
||||||
|
val game: Game
|
||||||
|
try {
|
||||||
|
game = Json.decodeFromString(it)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
// We don't care about any errors related to parsing the game cache
|
||||||
|
return@forEach
|
||||||
|
}
|
||||||
|
|
||||||
|
val gameExists =
|
||||||
|
DocumentFile.fromSingleUri(
|
||||||
|
YuzuApplication.appContext,
|
||||||
|
Uri.parse(game.path)
|
||||||
|
)?.exists()
|
||||||
|
if (gameExists == true) {
|
||||||
|
deserializedGames.add(game)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setGames(deserializedGames.toList())
|
||||||
}
|
}
|
||||||
|
reloadGames(false)
|
||||||
}
|
}
|
||||||
setGames(deserializedGames.toList())
|
|
||||||
}
|
}
|
||||||
reloadGames(false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setGames(games: List<Game>) {
|
fun setGames(games: List<Game>) {
|
||||||
@ -106,7 +112,7 @@ class GamesViewModel : ViewModel() {
|
|||||||
|
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
NativeLibrary.resetRomMetadata()
|
GameMetadata.resetMetadata()
|
||||||
setGames(GameHelper.getGames())
|
setGames(GameHelper.getGames())
|
||||||
_isReloading.value = false
|
_isReloading.value = false
|
||||||
|
|
||||||
|
@ -29,12 +29,10 @@ import androidx.navigation.fragment.NavHostFragment
|
|||||||
import androidx.navigation.ui.setupWithNavController
|
import androidx.navigation.ui.setupWithNavController
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import com.google.android.material.color.MaterialColors
|
import com.google.android.material.color.MaterialColors
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
|
||||||
import com.google.android.material.navigation.NavigationBarView
|
import com.google.android.material.navigation.NavigationBarView
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FilenameFilter
|
import java.io.FilenameFilter
|
||||||
import java.io.IOException
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
@ -43,7 +41,6 @@ import org.yuzu.yuzu_emu.NativeLibrary
|
|||||||
import org.yuzu.yuzu_emu.R
|
import org.yuzu.yuzu_emu.R
|
||||||
import org.yuzu.yuzu_emu.activities.EmulationActivity
|
import org.yuzu.yuzu_emu.activities.EmulationActivity
|
||||||
import org.yuzu.yuzu_emu.databinding.ActivityMainBinding
|
import org.yuzu.yuzu_emu.databinding.ActivityMainBinding
|
||||||
import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
|
|
||||||
import org.yuzu.yuzu_emu.features.DocumentProvider
|
import org.yuzu.yuzu_emu.features.DocumentProvider
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.Settings
|
import org.yuzu.yuzu_emu.features.settings.model.Settings
|
||||||
import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment
|
import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment
|
||||||
@ -343,11 +340,10 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
|||||||
|
|
||||||
val dstPath = DirectoryInitialization.userDirectory + "/keys/"
|
val dstPath = DirectoryInitialization.userDirectory + "/keys/"
|
||||||
if (FileUtil.copyUriToInternalStorage(
|
if (FileUtil.copyUriToInternalStorage(
|
||||||
applicationContext,
|
|
||||||
result,
|
result,
|
||||||
dstPath,
|
dstPath,
|
||||||
"prod.keys"
|
"prod.keys"
|
||||||
)
|
) != null
|
||||||
) {
|
) {
|
||||||
if (NativeLibrary.reloadKeys()) {
|
if (NativeLibrary.reloadKeys()) {
|
||||||
Toast.makeText(
|
Toast.makeText(
|
||||||
@ -446,11 +442,10 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
|||||||
|
|
||||||
val dstPath = DirectoryInitialization.userDirectory + "/keys/"
|
val dstPath = DirectoryInitialization.userDirectory + "/keys/"
|
||||||
if (FileUtil.copyUriToInternalStorage(
|
if (FileUtil.copyUriToInternalStorage(
|
||||||
applicationContext,
|
|
||||||
result,
|
result,
|
||||||
dstPath,
|
dstPath,
|
||||||
"key_retail.bin"
|
"key_retail.bin"
|
||||||
)
|
) != null
|
||||||
) {
|
) {
|
||||||
if (NativeLibrary.reloadKeys()) {
|
if (NativeLibrary.reloadKeys()) {
|
||||||
Toast.makeText(
|
Toast.makeText(
|
||||||
@ -469,59 +464,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val getDriver =
|
|
||||||
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
|
|
||||||
if (result == null) {
|
|
||||||
return@registerForActivityResult
|
|
||||||
}
|
|
||||||
|
|
||||||
val takeFlags =
|
|
||||||
Intent.FLAG_GRANT_WRITE_URI_PERMISSION or Intent.FLAG_GRANT_READ_URI_PERMISSION
|
|
||||||
contentResolver.takePersistableUriPermission(
|
|
||||||
result,
|
|
||||||
takeFlags
|
|
||||||
)
|
|
||||||
|
|
||||||
val progressBinding = DialogProgressBarBinding.inflate(layoutInflater)
|
|
||||||
progressBinding.progressBar.isIndeterminate = true
|
|
||||||
val installationDialog = MaterialAlertDialogBuilder(this)
|
|
||||||
.setTitle(R.string.installing_driver)
|
|
||||||
.setView(progressBinding.root)
|
|
||||||
.show()
|
|
||||||
|
|
||||||
lifecycleScope.launch {
|
|
||||||
withContext(Dispatchers.IO) {
|
|
||||||
// Ignore file exceptions when a user selects an invalid zip
|
|
||||||
try {
|
|
||||||
GpuDriverHelper.installCustomDriver(applicationContext, result)
|
|
||||||
} catch (_: IOException) {
|
|
||||||
}
|
|
||||||
|
|
||||||
withContext(Dispatchers.Main) {
|
|
||||||
installationDialog.dismiss()
|
|
||||||
|
|
||||||
val driverName = GpuDriverHelper.customDriverName
|
|
||||||
if (driverName != null) {
|
|
||||||
Toast.makeText(
|
|
||||||
applicationContext,
|
|
||||||
getString(
|
|
||||||
R.string.select_gpu_driver_install_success,
|
|
||||||
driverName
|
|
||||||
),
|
|
||||||
Toast.LENGTH_SHORT
|
|
||||||
).show()
|
|
||||||
} else {
|
|
||||||
Toast.makeText(
|
|
||||||
applicationContext,
|
|
||||||
R.string.select_gpu_driver_error,
|
|
||||||
Toast.LENGTH_LONG
|
|
||||||
).show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val installGameUpdate = registerForActivityResult(
|
val installGameUpdate = registerForActivityResult(
|
||||||
ActivityResultContracts.OpenMultipleDocuments()
|
ActivityResultContracts.OpenMultipleDocuments()
|
||||||
) { documents: List<Uri> ->
|
) { documents: List<Uri> ->
|
||||||
|
@ -7,7 +7,6 @@ import android.net.Uri
|
|||||||
import androidx.documentfile.provider.DocumentFile
|
import androidx.documentfile.provider.DocumentFile
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import org.yuzu.yuzu_emu.YuzuApplication
|
|
||||||
import org.yuzu.yuzu_emu.model.MinimalDocumentFile
|
import org.yuzu.yuzu_emu.model.MinimalDocumentFile
|
||||||
|
|
||||||
class DocumentsTree {
|
class DocumentsTree {
|
||||||
@ -22,7 +21,7 @@ class DocumentsTree {
|
|||||||
|
|
||||||
fun openContentUri(filepath: String, openMode: String?): Int {
|
fun openContentUri(filepath: String, openMode: String?): Int {
|
||||||
val node = resolvePath(filepath) ?: return -1
|
val node = resolvePath(filepath) ?: return -1
|
||||||
return FileUtil.openContentUri(YuzuApplication.appContext, node.uri.toString(), openMode)
|
return FileUtil.openContentUri(node.uri.toString(), openMode)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getFileSize(filepath: String): Long {
|
fun getFileSize(filepath: String): Long {
|
||||||
@ -30,7 +29,7 @@ class DocumentsTree {
|
|||||||
return if (node == null || node.isDirectory) {
|
return if (node == null || node.isDirectory) {
|
||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
FileUtil.getFileSize(YuzuApplication.appContext, node.uri.toString())
|
FileUtil.getFileSize(node.uri.toString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,6 +42,23 @@ class DocumentsTree {
|
|||||||
return node != null && node.isDirectory
|
return node != null && node.isDirectory
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getParentDirectory(filepath: String): String {
|
||||||
|
val node = resolvePath(filepath)!!
|
||||||
|
val parentNode = node.parent
|
||||||
|
if (parentNode != null && parentNode.isDirectory) {
|
||||||
|
return parentNode.uri!!.toString()
|
||||||
|
}
|
||||||
|
return node.uri!!.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getFilename(filepath: String): String {
|
||||||
|
val node = resolvePath(filepath)
|
||||||
|
if (node != null) {
|
||||||
|
return node.name!!
|
||||||
|
}
|
||||||
|
return filepath
|
||||||
|
}
|
||||||
|
|
||||||
private fun resolvePath(filepath: String): DocumentsNode? {
|
private fun resolvePath(filepath: String): DocumentsNode? {
|
||||||
val tokens = StringTokenizer(filepath, File.separator, false)
|
val tokens = StringTokenizer(filepath, File.separator, false)
|
||||||
var iterator = root
|
var iterator = root
|
||||||
@ -67,7 +83,7 @@ class DocumentsTree {
|
|||||||
* @param parent parent node of this level
|
* @param parent parent node of this level
|
||||||
*/
|
*/
|
||||||
private fun structTree(parent: DocumentsNode) {
|
private fun structTree(parent: DocumentsNode) {
|
||||||
val documents = FileUtil.listFiles(YuzuApplication.appContext, parent.uri!!)
|
val documents = FileUtil.listFiles(parent.uri!!)
|
||||||
for (document in documents) {
|
for (document in documents) {
|
||||||
val node = DocumentsNode(document)
|
val node = DocumentsNode(document)
|
||||||
node.parent = parent
|
node.parent = parent
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
package org.yuzu.yuzu_emu.utils
|
package org.yuzu.yuzu_emu.utils
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.database.Cursor
|
import android.database.Cursor
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.provider.DocumentsContract
|
import android.provider.DocumentsContract
|
||||||
@ -11,7 +10,6 @@ import androidx.documentfile.provider.DocumentFile
|
|||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import java.io.BufferedInputStream
|
import java.io.BufferedInputStream
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileOutputStream
|
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.net.URLDecoder
|
import java.net.URLDecoder
|
||||||
@ -21,6 +19,8 @@ import org.yuzu.yuzu_emu.YuzuApplication
|
|||||||
import org.yuzu.yuzu_emu.model.MinimalDocumentFile
|
import org.yuzu.yuzu_emu.model.MinimalDocumentFile
|
||||||
import org.yuzu.yuzu_emu.model.TaskState
|
import org.yuzu.yuzu_emu.model.TaskState
|
||||||
import java.io.BufferedOutputStream
|
import java.io.BufferedOutputStream
|
||||||
|
import java.lang.NullPointerException
|
||||||
|
import java.nio.charset.StandardCharsets
|
||||||
import java.util.zip.ZipOutputStream
|
import java.util.zip.ZipOutputStream
|
||||||
|
|
||||||
object FileUtil {
|
object FileUtil {
|
||||||
@ -29,6 +29,8 @@ object FileUtil {
|
|||||||
const val APPLICATION_OCTET_STREAM = "application/octet-stream"
|
const val APPLICATION_OCTET_STREAM = "application/octet-stream"
|
||||||
const val TEXT_PLAIN = "text/plain"
|
const val TEXT_PLAIN = "text/plain"
|
||||||
|
|
||||||
|
private val context get() = YuzuApplication.appContext
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a file from directory with filename.
|
* Create a file from directory with filename.
|
||||||
* @param context Application context
|
* @param context Application context
|
||||||
@ -36,11 +38,11 @@ object FileUtil {
|
|||||||
* @param filename file display name.
|
* @param filename file display name.
|
||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
fun createFile(context: Context?, directory: String?, filename: String): DocumentFile? {
|
fun createFile(directory: String?, filename: String): DocumentFile? {
|
||||||
var decodedFilename = filename
|
var decodedFilename = filename
|
||||||
try {
|
try {
|
||||||
val directoryUri = Uri.parse(directory)
|
val directoryUri = Uri.parse(directory)
|
||||||
val parent = DocumentFile.fromTreeUri(context!!, directoryUri) ?: return null
|
val parent = DocumentFile.fromTreeUri(context, directoryUri) ?: return null
|
||||||
decodedFilename = URLDecoder.decode(decodedFilename, DECODE_METHOD)
|
decodedFilename = URLDecoder.decode(decodedFilename, DECODE_METHOD)
|
||||||
var mimeType = APPLICATION_OCTET_STREAM
|
var mimeType = APPLICATION_OCTET_STREAM
|
||||||
if (decodedFilename.endsWith(".txt")) {
|
if (decodedFilename.endsWith(".txt")) {
|
||||||
@ -56,16 +58,15 @@ object FileUtil {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a directory from directory with filename.
|
* Create a directory from directory with filename.
|
||||||
* @param context Application context
|
|
||||||
* @param directory parent path for directory.
|
* @param directory parent path for directory.
|
||||||
* @param directoryName directory display name.
|
* @param directoryName directory display name.
|
||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
fun createDir(context: Context?, directory: String?, directoryName: String?): DocumentFile? {
|
fun createDir(directory: String?, directoryName: String?): DocumentFile? {
|
||||||
var decodedDirectoryName = directoryName
|
var decodedDirectoryName = directoryName
|
||||||
try {
|
try {
|
||||||
val directoryUri = Uri.parse(directory)
|
val directoryUri = Uri.parse(directory)
|
||||||
val parent = DocumentFile.fromTreeUri(context!!, directoryUri) ?: return null
|
val parent = DocumentFile.fromTreeUri(context, directoryUri) ?: return null
|
||||||
decodedDirectoryName = URLDecoder.decode(decodedDirectoryName, DECODE_METHOD)
|
decodedDirectoryName = URLDecoder.decode(decodedDirectoryName, DECODE_METHOD)
|
||||||
val isExist = parent.findFile(decodedDirectoryName)
|
val isExist = parent.findFile(decodedDirectoryName)
|
||||||
return isExist ?: parent.createDirectory(decodedDirectoryName)
|
return isExist ?: parent.createDirectory(decodedDirectoryName)
|
||||||
@ -77,13 +78,12 @@ object FileUtil {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Open content uri and return file descriptor to JNI.
|
* Open content uri and return file descriptor to JNI.
|
||||||
* @param context Application context
|
|
||||||
* @param path Native content uri path
|
* @param path Native content uri path
|
||||||
* @param openMode will be one of "r", "r", "rw", "wa", "rwa"
|
* @param openMode will be one of "r", "r", "rw", "wa", "rwa"
|
||||||
* @return file descriptor
|
* @return file descriptor
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun openContentUri(context: Context, path: String, openMode: String?): Int {
|
fun openContentUri(path: String, openMode: String?): Int {
|
||||||
try {
|
try {
|
||||||
val uri = Uri.parse(path)
|
val uri = Uri.parse(path)
|
||||||
val parcelFileDescriptor = context.contentResolver.openFileDescriptor(uri, openMode!!)
|
val parcelFileDescriptor = context.contentResolver.openFileDescriptor(uri, openMode!!)
|
||||||
@ -103,11 +103,10 @@ object FileUtil {
|
|||||||
/**
|
/**
|
||||||
* Reference: https://stackoverflow.com/questions/42186820/documentfile-is-very-slow
|
* Reference: https://stackoverflow.com/questions/42186820/documentfile-is-very-slow
|
||||||
* This function will be faster than DoucmentFile.listFiles
|
* This function will be faster than DoucmentFile.listFiles
|
||||||
* @param context Application context
|
|
||||||
* @param uri Directory uri.
|
* @param uri Directory uri.
|
||||||
* @return CheapDocument lists.
|
* @return CheapDocument lists.
|
||||||
*/
|
*/
|
||||||
fun listFiles(context: Context, uri: Uri): Array<MinimalDocumentFile> {
|
fun listFiles(uri: Uri): Array<MinimalDocumentFile> {
|
||||||
val resolver = context.contentResolver
|
val resolver = context.contentResolver
|
||||||
val columns = arrayOf(
|
val columns = arrayOf(
|
||||||
DocumentsContract.Document.COLUMN_DOCUMENT_ID,
|
DocumentsContract.Document.COLUMN_DOCUMENT_ID,
|
||||||
@ -145,7 +144,7 @@ object FileUtil {
|
|||||||
* @param path Native content uri path
|
* @param path Native content uri path
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
fun exists(context: Context, path: String?): Boolean {
|
fun exists(path: String?, suppressLog: Boolean = false): Boolean {
|
||||||
var c: Cursor? = null
|
var c: Cursor? = null
|
||||||
try {
|
try {
|
||||||
val mUri = Uri.parse(path)
|
val mUri = Uri.parse(path)
|
||||||
@ -153,7 +152,9 @@ object FileUtil {
|
|||||||
c = context.contentResolver.query(mUri, columns, null, null, null)
|
c = context.contentResolver.query(mUri, columns, null, null, null)
|
||||||
return c!!.count > 0
|
return c!!.count > 0
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.info("[FileUtil] Cannot find file from given path, error: " + e.message)
|
if (!suppressLog) {
|
||||||
|
Log.info("[FileUtil] Cannot find file from given path, error: " + e.message)
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
closeQuietly(c)
|
closeQuietly(c)
|
||||||
}
|
}
|
||||||
@ -165,7 +166,7 @@ object FileUtil {
|
|||||||
* @param path content uri path
|
* @param path content uri path
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
fun isDirectory(context: Context, path: String): Boolean {
|
fun isDirectory(path: String): Boolean {
|
||||||
val resolver = context.contentResolver
|
val resolver = context.contentResolver
|
||||||
val columns = arrayOf(
|
val columns = arrayOf(
|
||||||
DocumentsContract.Document.COLUMN_MIME_TYPE
|
DocumentsContract.Document.COLUMN_MIME_TYPE
|
||||||
@ -210,10 +211,10 @@ object FileUtil {
|
|||||||
return filename
|
return filename
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getFilesName(context: Context, path: String): Array<String> {
|
fun getFilesName(path: String): Array<String> {
|
||||||
val uri = Uri.parse(path)
|
val uri = Uri.parse(path)
|
||||||
val files: MutableList<String> = ArrayList()
|
val files: MutableList<String> = ArrayList()
|
||||||
for (file in listFiles(context, uri)) {
|
for (file in listFiles(uri)) {
|
||||||
files.add(file.filename)
|
files.add(file.filename)
|
||||||
}
|
}
|
||||||
return files.toTypedArray()
|
return files.toTypedArray()
|
||||||
@ -225,7 +226,7 @@ object FileUtil {
|
|||||||
* @return long file size
|
* @return long file size
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun getFileSize(context: Context, path: String): Long {
|
fun getFileSize(path: String): Long {
|
||||||
val resolver = context.contentResolver
|
val resolver = context.contentResolver
|
||||||
val columns = arrayOf(
|
val columns = arrayOf(
|
||||||
DocumentsContract.Document.COLUMN_SIZE
|
DocumentsContract.Document.COLUMN_SIZE
|
||||||
@ -245,44 +246,38 @@ object FileUtil {
|
|||||||
return size
|
return size
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an input stream with a given [Uri] and copies its data to the given path. This will
|
||||||
|
* overwrite any pre-existing files.
|
||||||
|
*
|
||||||
|
* @param sourceUri The [Uri] to copy data from
|
||||||
|
* @param destinationParentPath Destination directory
|
||||||
|
* @param destinationFilename Optionally renames the file once copied
|
||||||
|
*/
|
||||||
fun copyUriToInternalStorage(
|
fun copyUriToInternalStorage(
|
||||||
context: Context,
|
sourceUri: Uri,
|
||||||
sourceUri: Uri?,
|
|
||||||
destinationParentPath: String,
|
destinationParentPath: String,
|
||||||
destinationFilename: String
|
destinationFilename: String = ""
|
||||||
): Boolean {
|
): File? =
|
||||||
var input: InputStream? = null
|
|
||||||
var output: FileOutputStream? = null
|
|
||||||
try {
|
try {
|
||||||
input = context.contentResolver.openInputStream(sourceUri!!)
|
val fileName =
|
||||||
output = FileOutputStream("$destinationParentPath/$destinationFilename")
|
if (destinationFilename == "") getFilename(sourceUri) else "/$destinationFilename"
|
||||||
val buffer = ByteArray(1024)
|
val inputStream = context.contentResolver.openInputStream(sourceUri)!!
|
||||||
var len: Int
|
|
||||||
while (input!!.read(buffer).also { len = it } != -1) {
|
val destinationFile = File("$destinationParentPath$fileName")
|
||||||
output.write(buffer, 0, len)
|
if (destinationFile.exists()) {
|
||||||
|
destinationFile.delete()
|
||||||
}
|
}
|
||||||
output.flush()
|
|
||||||
return true
|
destinationFile.outputStream().use { fos ->
|
||||||
} catch (e: Exception) {
|
inputStream.use { it.copyTo(fos) }
|
||||||
Log.error("[FileUtil]: Cannot copy file, error: " + e.message)
|
|
||||||
} finally {
|
|
||||||
if (input != null) {
|
|
||||||
try {
|
|
||||||
input.close()
|
|
||||||
} catch (e: IOException) {
|
|
||||||
Log.error("[FileUtil]: Cannot close input file, error: " + e.message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (output != null) {
|
|
||||||
try {
|
|
||||||
output.close()
|
|
||||||
} catch (e: IOException) {
|
|
||||||
Log.error("[FileUtil]: Cannot close output file, error: " + e.message)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
destinationFile
|
||||||
|
} catch (e: IOException) {
|
||||||
|
null
|
||||||
|
} catch (e: NullPointerException) {
|
||||||
|
null
|
||||||
}
|
}
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extracts the given zip file into the given directory.
|
* Extracts the given zip file into the given directory.
|
||||||
@ -368,4 +363,12 @@ object FileUtil {
|
|||||||
return fileName.substring(fileName.lastIndexOf(".") + 1)
|
return fileName.substring(fileName.lastIndexOf(".") + 1)
|
||||||
.lowercase()
|
.lowercase()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Throws(IOException::class)
|
||||||
|
fun getStringFromFile(file: File): String =
|
||||||
|
String(file.readBytes(), StandardCharsets.UTF_8)
|
||||||
|
|
||||||
|
@Throws(IOException::class)
|
||||||
|
fun getStringFromInputStream(stream: InputStream): String =
|
||||||
|
String(stream.readBytes(), StandardCharsets.UTF_8)
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ object GameHelper {
|
|||||||
// Ensure keys are loaded so that ROM metadata can be decrypted.
|
// Ensure keys are loaded so that ROM metadata can be decrypted.
|
||||||
NativeLibrary.reloadKeys()
|
NativeLibrary.reloadKeys()
|
||||||
|
|
||||||
addGamesRecursive(games, FileUtil.listFiles(context, gamesUri), 3)
|
addGamesRecursive(games, FileUtil.listFiles(gamesUri), 3)
|
||||||
|
|
||||||
// Cache list of games found on disk
|
// Cache list of games found on disk
|
||||||
val serializedGames = mutableSetOf<String>()
|
val serializedGames = mutableSetOf<String>()
|
||||||
@ -58,7 +58,7 @@ object GameHelper {
|
|||||||
if (it.isDirectory) {
|
if (it.isDirectory) {
|
||||||
addGamesRecursive(
|
addGamesRecursive(
|
||||||
games,
|
games,
|
||||||
FileUtil.listFiles(YuzuApplication.appContext, it.uri),
|
FileUtil.listFiles(it.uri),
|
||||||
depth - 1
|
depth - 1
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
@ -71,27 +71,26 @@ object GameHelper {
|
|||||||
|
|
||||||
fun getGame(uri: Uri, addedToLibrary: Boolean): Game {
|
fun getGame(uri: Uri, addedToLibrary: Boolean): Game {
|
||||||
val filePath = uri.toString()
|
val filePath = uri.toString()
|
||||||
var name = NativeLibrary.getTitle(filePath)
|
var name = GameMetadata.getTitle(filePath)
|
||||||
|
|
||||||
// If the game's title field is empty, use the filename.
|
// If the game's title field is empty, use the filename.
|
||||||
if (name.isEmpty()) {
|
if (name.isEmpty()) {
|
||||||
name = FileUtil.getFilename(uri)
|
name = FileUtil.getFilename(uri)
|
||||||
}
|
}
|
||||||
var gameId = NativeLibrary.getGameId(filePath)
|
var programId = GameMetadata.getProgramId(filePath)
|
||||||
|
|
||||||
// If the game's ID field is empty, use the filename without extension.
|
// If the game's ID field is empty, use the filename without extension.
|
||||||
if (gameId.isEmpty()) {
|
if (programId.isEmpty()) {
|
||||||
gameId = name.substring(0, name.lastIndexOf("."))
|
programId = name.substring(0, name.lastIndexOf("."))
|
||||||
}
|
}
|
||||||
|
|
||||||
val newGame = Game(
|
val newGame = Game(
|
||||||
name,
|
name,
|
||||||
NativeLibrary.getDescription(filePath).replace("\n", " "),
|
|
||||||
NativeLibrary.getRegions(filePath),
|
|
||||||
filePath,
|
filePath,
|
||||||
gameId,
|
programId,
|
||||||
NativeLibrary.getCompany(filePath),
|
GameMetadata.getDeveloper(filePath),
|
||||||
NativeLibrary.isHomebrew(filePath)
|
GameMetadata.getVersion(filePath),
|
||||||
|
GameMetadata.getIsHomebrew(filePath)
|
||||||
)
|
)
|
||||||
|
|
||||||
if (addedToLibrary) {
|
if (addedToLibrary) {
|
||||||
|
@ -18,7 +18,6 @@ import coil.key.Keyer
|
|||||||
import coil.memory.MemoryCache
|
import coil.memory.MemoryCache
|
||||||
import coil.request.ImageRequest
|
import coil.request.ImageRequest
|
||||||
import coil.request.Options
|
import coil.request.Options
|
||||||
import org.yuzu.yuzu_emu.NativeLibrary
|
|
||||||
import org.yuzu.yuzu_emu.R
|
import org.yuzu.yuzu_emu.R
|
||||||
import org.yuzu.yuzu_emu.YuzuApplication
|
import org.yuzu.yuzu_emu.YuzuApplication
|
||||||
import org.yuzu.yuzu_emu.model.Game
|
import org.yuzu.yuzu_emu.model.Game
|
||||||
@ -36,7 +35,7 @@ class GameIconFetcher(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun decodeGameIcon(uri: String): Bitmap? {
|
private fun decodeGameIcon(uri: String): Bitmap? {
|
||||||
val data = NativeLibrary.getIcon(uri)
|
val data = GameMetadata.getIcon(uri)
|
||||||
return BitmapFactory.decodeByteArray(
|
return BitmapFactory.decodeByteArray(
|
||||||
data,
|
data,
|
||||||
0,
|
0,
|
||||||
|
@ -0,0 +1,20 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
package org.yuzu.yuzu_emu.utils
|
||||||
|
|
||||||
|
object GameMetadata {
|
||||||
|
external fun getTitle(path: String): String
|
||||||
|
|
||||||
|
external fun getProgramId(path: String): String
|
||||||
|
|
||||||
|
external fun getDeveloper(path: String): String
|
||||||
|
|
||||||
|
external fun getVersion(path: String): String
|
||||||
|
|
||||||
|
external fun getIcon(path: String): ByteArray
|
||||||
|
|
||||||
|
external fun getIsHomebrew(path: String): Boolean
|
||||||
|
|
||||||
|
external fun resetMetadata()
|
||||||
|
}
|
@ -3,64 +3,33 @@
|
|||||||
|
|
||||||
package org.yuzu.yuzu_emu.utils
|
package org.yuzu.yuzu_emu.utils
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import android.os.Build
|
||||||
import java.io.BufferedInputStream
|
import java.io.BufferedInputStream
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileInputStream
|
|
||||||
import java.io.FileOutputStream
|
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.util.zip.ZipInputStream
|
|
||||||
import org.yuzu.yuzu_emu.NativeLibrary
|
import org.yuzu.yuzu_emu.NativeLibrary
|
||||||
import org.yuzu.yuzu_emu.utils.FileUtil.copyUriToInternalStorage
|
import org.yuzu.yuzu_emu.YuzuApplication
|
||||||
|
import java.util.zip.ZipException
|
||||||
|
import java.util.zip.ZipFile
|
||||||
|
|
||||||
object GpuDriverHelper {
|
object GpuDriverHelper {
|
||||||
private const val META_JSON_FILENAME = "meta.json"
|
private const val META_JSON_FILENAME = "meta.json"
|
||||||
private const val DRIVER_INTERNAL_FILENAME = "gpu_driver.zip"
|
|
||||||
private var fileRedirectionPath: String? = null
|
private var fileRedirectionPath: String? = null
|
||||||
private var driverInstallationPath: String? = null
|
var driverInstallationPath: String? = null
|
||||||
private var hookLibPath: String? = null
|
private var hookLibPath: String? = null
|
||||||
|
|
||||||
@Throws(IOException::class)
|
val driverStoragePath get() = DirectoryInitialization.userDirectory!! + "/gpu_drivers/"
|
||||||
private fun unzip(zipFilePath: String, destDir: String) {
|
|
||||||
val dir = File(destDir)
|
|
||||||
|
|
||||||
// Create output directory if it doesn't exist
|
fun initializeDriverParameters() {
|
||||||
if (!dir.exists()) dir.mkdirs()
|
|
||||||
|
|
||||||
// Unpack the files.
|
|
||||||
val inputStream = FileInputStream(zipFilePath)
|
|
||||||
val zis = ZipInputStream(BufferedInputStream(inputStream))
|
|
||||||
val buffer = ByteArray(1024)
|
|
||||||
var ze = zis.nextEntry
|
|
||||||
while (ze != null) {
|
|
||||||
val newFile = File(destDir, ze.name)
|
|
||||||
val canonicalPath = newFile.canonicalPath
|
|
||||||
if (!canonicalPath.startsWith(destDir + ze.name)) {
|
|
||||||
throw SecurityException("Zip file attempted path traversal! " + ze.name)
|
|
||||||
}
|
|
||||||
|
|
||||||
newFile.parentFile!!.mkdirs()
|
|
||||||
val fos = FileOutputStream(newFile)
|
|
||||||
var len: Int
|
|
||||||
while (zis.read(buffer).also { len = it } > 0) {
|
|
||||||
fos.write(buffer, 0, len)
|
|
||||||
}
|
|
||||||
fos.close()
|
|
||||||
zis.closeEntry()
|
|
||||||
ze = zis.nextEntry
|
|
||||||
}
|
|
||||||
zis.closeEntry()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun initializeDriverParameters(context: Context) {
|
|
||||||
try {
|
try {
|
||||||
// Initialize the file redirection directory.
|
// Initialize the file redirection directory.
|
||||||
fileRedirectionPath =
|
fileRedirectionPath = YuzuApplication.appContext
|
||||||
context.getExternalFilesDir(null)!!.canonicalPath + "/gpu/vk_file_redirect/"
|
.getExternalFilesDir(null)!!.canonicalPath + "/gpu/vk_file_redirect/"
|
||||||
|
|
||||||
// Initialize the driver installation directory.
|
// Initialize the driver installation directory.
|
||||||
driverInstallationPath = context.filesDir.canonicalPath + "/gpu_driver/"
|
driverInstallationPath = YuzuApplication.appContext
|
||||||
|
.filesDir.canonicalPath + "/gpu_driver/"
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
throw RuntimeException(e)
|
throw RuntimeException(e)
|
||||||
}
|
}
|
||||||
@ -69,68 +38,169 @@ object GpuDriverHelper {
|
|||||||
initializeDirectories()
|
initializeDirectories()
|
||||||
|
|
||||||
// Initialize hook libraries directory.
|
// Initialize hook libraries directory.
|
||||||
hookLibPath = context.applicationInfo.nativeLibraryDir + "/"
|
hookLibPath = YuzuApplication.appContext.applicationInfo.nativeLibraryDir + "/"
|
||||||
|
|
||||||
// Initialize GPU driver.
|
// Initialize GPU driver.
|
||||||
NativeLibrary.initializeGpuDriver(
|
NativeLibrary.initializeGpuDriver(
|
||||||
hookLibPath,
|
hookLibPath,
|
||||||
driverInstallationPath,
|
driverInstallationPath,
|
||||||
customDriverLibraryName,
|
customDriverData.libraryName,
|
||||||
fileRedirectionPath
|
fileRedirectionPath
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun installDefaultDriver(context: Context) {
|
fun getDrivers(): MutableList<Pair<String, GpuDriverMetadata>> {
|
||||||
// Removing the installed driver will result in the backend using the default system driver.
|
val driverZips = File(driverStoragePath).listFiles()
|
||||||
val driverInstallationDir = File(driverInstallationPath!!)
|
val drivers: MutableList<Pair<String, GpuDriverMetadata>> =
|
||||||
deleteRecursive(driverInstallationDir)
|
driverZips
|
||||||
initializeDriverParameters(context)
|
?.mapNotNull {
|
||||||
|
val metadata = getMetadataFromZip(it)
|
||||||
|
metadata.name?.let { _ -> Pair(it.path, metadata) }
|
||||||
|
}
|
||||||
|
?.sortedByDescending { it: Pair<String, GpuDriverMetadata> -> it.second.name }
|
||||||
|
?.distinct()
|
||||||
|
?.toMutableList() ?: mutableListOf()
|
||||||
|
|
||||||
|
// TODO: Get system driver information
|
||||||
|
drivers.add(0, Pair("", GpuDriverMetadata()))
|
||||||
|
return drivers
|
||||||
}
|
}
|
||||||
|
|
||||||
fun installCustomDriver(context: Context, driverPathUri: Uri?) {
|
fun installDefaultDriver() {
|
||||||
|
// Removing the installed driver will result in the backend using the default system driver.
|
||||||
|
File(driverInstallationPath!!).deleteRecursively()
|
||||||
|
initializeDriverParameters()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun copyDriverToInternalStorage(driverUri: Uri): Boolean {
|
||||||
|
// Ensure we have directories.
|
||||||
|
initializeDirectories()
|
||||||
|
|
||||||
|
// Copy the zip file URI to user data
|
||||||
|
val copiedFile =
|
||||||
|
FileUtil.copyUriToInternalStorage(driverUri, driverStoragePath) ?: return false
|
||||||
|
|
||||||
|
// Validate driver
|
||||||
|
val metadata = getMetadataFromZip(copiedFile)
|
||||||
|
if (metadata.name == null) {
|
||||||
|
copiedFile.delete()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (metadata.minApi > Build.VERSION.SDK_INT) {
|
||||||
|
copiedFile.delete()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copies driver zip into user data directory so that it can be exported along with
|
||||||
|
* other user data and also unzipped into the installation directory
|
||||||
|
*/
|
||||||
|
fun installCustomDriver(driverUri: Uri): Boolean {
|
||||||
// Revert to system default in the event the specified driver is bad.
|
// Revert to system default in the event the specified driver is bad.
|
||||||
installDefaultDriver(context)
|
installDefaultDriver()
|
||||||
|
|
||||||
// Ensure we have directories.
|
// Ensure we have directories.
|
||||||
initializeDirectories()
|
initializeDirectories()
|
||||||
|
|
||||||
// Copy the zip file URI into our private storage.
|
// Copy the zip file URI to user data
|
||||||
copyUriToInternalStorage(
|
val copiedFile =
|
||||||
context,
|
FileUtil.copyUriToInternalStorage(driverUri, driverStoragePath) ?: return false
|
||||||
driverPathUri,
|
|
||||||
driverInstallationPath!!,
|
// Validate driver
|
||||||
DRIVER_INTERNAL_FILENAME
|
val metadata = getMetadataFromZip(copiedFile)
|
||||||
)
|
if (metadata.name == null) {
|
||||||
|
copiedFile.delete()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (metadata.minApi > Build.VERSION.SDK_INT) {
|
||||||
|
copiedFile.delete()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// Unzip the driver.
|
// Unzip the driver.
|
||||||
try {
|
try {
|
||||||
unzip(driverInstallationPath + DRIVER_INTERNAL_FILENAME, driverInstallationPath!!)
|
FileUtil.unzipToInternalStorage(
|
||||||
|
BufferedInputStream(copiedFile.inputStream()),
|
||||||
|
File(driverInstallationPath!!)
|
||||||
|
)
|
||||||
} catch (e: SecurityException) {
|
} catch (e: SecurityException) {
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize the driver parameters.
|
// Initialize the driver parameters.
|
||||||
initializeDriverParameters(context)
|
initializeDriverParameters()
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unzips driver into installation directory
|
||||||
|
*/
|
||||||
|
fun installCustomDriver(driver: File): Boolean {
|
||||||
|
// Revert to system default in the event the specified driver is bad.
|
||||||
|
installDefaultDriver()
|
||||||
|
|
||||||
|
// Ensure we have directories.
|
||||||
|
initializeDirectories()
|
||||||
|
|
||||||
|
// Validate driver
|
||||||
|
val metadata = getMetadataFromZip(driver)
|
||||||
|
if (metadata.name == null) {
|
||||||
|
driver.delete()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unzip the driver to the private installation directory
|
||||||
|
try {
|
||||||
|
FileUtil.unzipToInternalStorage(
|
||||||
|
BufferedInputStream(driver.inputStream()),
|
||||||
|
File(driverInstallationPath!!)
|
||||||
|
)
|
||||||
|
} catch (e: SecurityException) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the driver parameters.
|
||||||
|
initializeDriverParameters()
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes in a zip file and reads the meta.json file for presentation to the UI
|
||||||
|
*
|
||||||
|
* @param driver Zip containing driver and meta.json file
|
||||||
|
* @return A non-null [GpuDriverMetadata] instance that may have null members
|
||||||
|
*/
|
||||||
|
fun getMetadataFromZip(driver: File): GpuDriverMetadata {
|
||||||
|
try {
|
||||||
|
ZipFile(driver).use { zf ->
|
||||||
|
val entries = zf.entries()
|
||||||
|
while (entries.hasMoreElements()) {
|
||||||
|
val entry = entries.nextElement()
|
||||||
|
if (!entry.isDirectory && entry.name.lowercase().contains(".json")) {
|
||||||
|
zf.getInputStream(entry).use {
|
||||||
|
return GpuDriverMetadata(it, entry.size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (_: ZipException) {
|
||||||
|
}
|
||||||
|
return GpuDriverMetadata()
|
||||||
}
|
}
|
||||||
|
|
||||||
external fun supportsCustomDriverLoading(): Boolean
|
external fun supportsCustomDriverLoading(): Boolean
|
||||||
|
|
||||||
// Parse the custom driver metadata to retrieve the name.
|
// Parse the custom driver metadata to retrieve the name.
|
||||||
val customDriverName: String?
|
val customDriverData: GpuDriverMetadata
|
||||||
get() {
|
get() = GpuDriverMetadata(File(driverInstallationPath + META_JSON_FILENAME))
|
||||||
val metadata = GpuDriverMetadata(driverInstallationPath + META_JSON_FILENAME)
|
|
||||||
return metadata.name
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse the custom driver metadata to retrieve the library name.
|
fun initializeDirectories() {
|
||||||
private val customDriverLibraryName: String?
|
|
||||||
get() {
|
|
||||||
// Parse the custom driver metadata to retrieve the library name.
|
|
||||||
val metadata = GpuDriverMetadata(driverInstallationPath + META_JSON_FILENAME)
|
|
||||||
return metadata.libraryName
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun initializeDirectories() {
|
|
||||||
// Ensure the file redirection directory exists.
|
// Ensure the file redirection directory exists.
|
||||||
val fileRedirectionDir = File(fileRedirectionPath!!)
|
val fileRedirectionDir = File(fileRedirectionPath!!)
|
||||||
if (!fileRedirectionDir.exists()) {
|
if (!fileRedirectionDir.exists()) {
|
||||||
@ -141,14 +211,10 @@ object GpuDriverHelper {
|
|||||||
if (!driverInstallationDir.exists()) {
|
if (!driverInstallationDir.exists()) {
|
||||||
driverInstallationDir.mkdirs()
|
driverInstallationDir.mkdirs()
|
||||||
}
|
}
|
||||||
}
|
// Ensure the driver storage directory exists
|
||||||
|
val driverStorageDirectory = File(driverStoragePath)
|
||||||
private fun deleteRecursive(fileOrDirectory: File) {
|
if (!driverStorageDirectory.exists()) {
|
||||||
if (fileOrDirectory.isDirectory) {
|
driverStorageDirectory.mkdirs()
|
||||||
for (child in fileOrDirectory.listFiles()!!) {
|
|
||||||
deleteRecursive(child)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
fileOrDirectory.delete()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,29 +4,29 @@
|
|||||||
package org.yuzu.yuzu_emu.utils
|
package org.yuzu.yuzu_emu.utils
|
||||||
|
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.nio.charset.StandardCharsets
|
|
||||||
import java.nio.file.Files
|
|
||||||
import java.nio.file.Paths
|
|
||||||
import org.json.JSONException
|
import org.json.JSONException
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
|
import java.io.File
|
||||||
|
import java.io.InputStream
|
||||||
|
|
||||||
class GpuDriverMetadata(metadataFilePath: String) {
|
class GpuDriverMetadata {
|
||||||
var name: String? = null
|
/**
|
||||||
var description: String? = null
|
* Tries to get driver metadata information from a meta.json [File]
|
||||||
var author: String? = null
|
*
|
||||||
var vendor: String? = null
|
* @param metadataFile meta.json file provided with a GPU driver
|
||||||
var driverVersion: String? = null
|
*/
|
||||||
var minApi = 0
|
constructor(metadataFile: File) {
|
||||||
var libraryName: String? = null
|
if (metadataFile.length() > MAX_META_SIZE_BYTES) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
init {
|
|
||||||
try {
|
try {
|
||||||
val json = JSONObject(getStringFromFile(metadataFilePath))
|
val json = JSONObject(FileUtil.getStringFromFile(metadataFile))
|
||||||
name = json.getString("name")
|
name = json.getString("name")
|
||||||
description = json.getString("description")
|
description = json.getString("description")
|
||||||
author = json.getString("author")
|
author = json.getString("author")
|
||||||
vendor = json.getString("vendor")
|
vendor = json.getString("vendor")
|
||||||
driverVersion = json.getString("driverVersion")
|
version = json.getString("driverVersion")
|
||||||
minApi = json.getInt("minApi")
|
minApi = json.getInt("minApi")
|
||||||
libraryName = json.getString("libraryName")
|
libraryName = json.getString("libraryName")
|
||||||
} catch (e: JSONException) {
|
} catch (e: JSONException) {
|
||||||
@ -36,12 +36,84 @@ class GpuDriverMetadata(metadataFilePath: String) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
/**
|
||||||
@Throws(IOException::class)
|
* Tries to get driver metadata information from an input stream that's intended to be
|
||||||
private fun getStringFromFile(filePath: String): String {
|
* from a zip file
|
||||||
val path = Paths.get(filePath)
|
*
|
||||||
val bytes = Files.readAllBytes(path)
|
* @param metadataStream ZipEntry input stream
|
||||||
return String(bytes, StandardCharsets.UTF_8)
|
* @param size Size of the file in bytes
|
||||||
|
*/
|
||||||
|
constructor(metadataStream: InputStream, size: Long) {
|
||||||
|
if (size > MAX_META_SIZE_BYTES) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
val json = JSONObject(FileUtil.getStringFromInputStream(metadataStream))
|
||||||
|
name = json.getString("name")
|
||||||
|
description = json.getString("description")
|
||||||
|
author = json.getString("author")
|
||||||
|
vendor = json.getString("vendor")
|
||||||
|
version = json.getString("driverVersion")
|
||||||
|
minApi = json.getInt("minApi")
|
||||||
|
libraryName = json.getString("libraryName")
|
||||||
|
} catch (e: JSONException) {
|
||||||
|
// JSON is malformed, ignore and treat as unsupported metadata.
|
||||||
|
} catch (e: IOException) {
|
||||||
|
// File is inaccessible, ignore and treat as unsupported metadata.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an empty metadata instance
|
||||||
|
*/
|
||||||
|
constructor()
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (other !is GpuDriverMetadata) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return other.name == name &&
|
||||||
|
other.description == description &&
|
||||||
|
other.author == author &&
|
||||||
|
other.vendor == vendor &&
|
||||||
|
other.version == version &&
|
||||||
|
other.minApi == minApi &&
|
||||||
|
other.libraryName == libraryName
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
var result = name?.hashCode() ?: 0
|
||||||
|
result = 31 * result + (description?.hashCode() ?: 0)
|
||||||
|
result = 31 * result + (author?.hashCode() ?: 0)
|
||||||
|
result = 31 * result + (vendor?.hashCode() ?: 0)
|
||||||
|
result = 31 * result + (version?.hashCode() ?: 0)
|
||||||
|
result = 31 * result + minApi
|
||||||
|
result = 31 * result + (libraryName?.hashCode() ?: 0)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toString(): String =
|
||||||
|
"""
|
||||||
|
Name - $name
|
||||||
|
Description - $description
|
||||||
|
Author - $author
|
||||||
|
Vendor - $vendor
|
||||||
|
Version - $version
|
||||||
|
Min API - $minApi
|
||||||
|
Library Name - $libraryName
|
||||||
|
""".trimMargin().trimIndent()
|
||||||
|
|
||||||
|
var name: String? = null
|
||||||
|
var description: String? = null
|
||||||
|
var author: String? = null
|
||||||
|
var vendor: String? = null
|
||||||
|
var version: String? = null
|
||||||
|
var minApi = 0
|
||||||
|
var libraryName: String? = null
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val MAX_META_SIZE_BYTES = 500000
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,8 +14,10 @@ add_library(yuzu-android SHARED
|
|||||||
id_cache.cpp
|
id_cache.cpp
|
||||||
id_cache.h
|
id_cache.h
|
||||||
native.cpp
|
native.cpp
|
||||||
|
native.h
|
||||||
native_config.cpp
|
native_config.cpp
|
||||||
uisettings.cpp
|
uisettings.cpp
|
||||||
|
game_metadata.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR})
|
set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR})
|
||||||
|
112
src/android/app/src/main/jni/game_metadata.cpp
Normal file
112
src/android/app/src/main/jni/game_metadata.cpp
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include <core/core.h>
|
||||||
|
#include <core/file_sys/patch_manager.h>
|
||||||
|
#include <core/loader/nro.h>
|
||||||
|
#include <jni.h>
|
||||||
|
#include "core/loader/loader.h"
|
||||||
|
#include "jni/android_common/android_common.h"
|
||||||
|
#include "native.h"
|
||||||
|
|
||||||
|
struct RomMetadata {
|
||||||
|
std::string title;
|
||||||
|
u64 programId;
|
||||||
|
std::string developer;
|
||||||
|
std::string version;
|
||||||
|
std::vector<u8> icon;
|
||||||
|
bool isHomebrew;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unordered_map<std::string, RomMetadata> m_rom_metadata_cache;
|
||||||
|
|
||||||
|
RomMetadata CacheRomMetadata(const std::string& path) {
|
||||||
|
const auto file =
|
||||||
|
Core::GetGameFileFromPath(EmulationSession::GetInstance().System().GetFilesystem(), path);
|
||||||
|
auto loader = Loader::GetLoader(EmulationSession::GetInstance().System(), file, 0, 0);
|
||||||
|
|
||||||
|
RomMetadata entry;
|
||||||
|
loader->ReadTitle(entry.title);
|
||||||
|
loader->ReadProgramId(entry.programId);
|
||||||
|
loader->ReadIcon(entry.icon);
|
||||||
|
|
||||||
|
const FileSys::PatchManager pm{
|
||||||
|
entry.programId, EmulationSession::GetInstance().System().GetFileSystemController(),
|
||||||
|
EmulationSession::GetInstance().System().GetContentProvider()};
|
||||||
|
const auto control = pm.GetControlMetadata();
|
||||||
|
|
||||||
|
if (control.first != nullptr) {
|
||||||
|
entry.developer = control.first->GetDeveloperName();
|
||||||
|
entry.version = control.first->GetVersionString();
|
||||||
|
} else {
|
||||||
|
FileSys::NACP nacp;
|
||||||
|
if (loader->ReadControlData(nacp) == Loader::ResultStatus::Success) {
|
||||||
|
entry.developer = nacp.GetDeveloperName();
|
||||||
|
} else {
|
||||||
|
entry.developer = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.version = "1.0.0";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loader->GetFileType() == Loader::FileType::NRO) {
|
||||||
|
auto loader_nro = reinterpret_cast<Loader::AppLoader_NRO*>(loader.get());
|
||||||
|
entry.isHomebrew = loader_nro->IsHomebrew();
|
||||||
|
} else {
|
||||||
|
entry.isHomebrew = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_rom_metadata_cache[path] = entry;
|
||||||
|
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
RomMetadata GetRomMetadata(const std::string& path) {
|
||||||
|
if (auto search = m_rom_metadata_cache.find(path); search != m_rom_metadata_cache.end()) {
|
||||||
|
return search->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CacheRomMetadata(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getTitle(JNIEnv* env, jobject obj,
|
||||||
|
jstring jpath) {
|
||||||
|
return ToJString(env, GetRomMetadata(GetJString(env, jpath)).title);
|
||||||
|
}
|
||||||
|
|
||||||
|
jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getProgramId(JNIEnv* env, jobject obj,
|
||||||
|
jstring jpath) {
|
||||||
|
return ToJString(env, std::to_string(GetRomMetadata(GetJString(env, jpath)).programId));
|
||||||
|
}
|
||||||
|
|
||||||
|
jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getDeveloper(JNIEnv* env, jobject obj,
|
||||||
|
jstring jpath) {
|
||||||
|
return ToJString(env, GetRomMetadata(GetJString(env, jpath)).developer);
|
||||||
|
}
|
||||||
|
|
||||||
|
jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getVersion(JNIEnv* env, jobject obj,
|
||||||
|
jstring jpath) {
|
||||||
|
return ToJString(env, GetRomMetadata(GetJString(env, jpath)).version);
|
||||||
|
}
|
||||||
|
|
||||||
|
jbyteArray Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIcon(JNIEnv* env, jobject obj,
|
||||||
|
jstring jpath) {
|
||||||
|
auto icon_data = GetRomMetadata(GetJString(env, jpath)).icon;
|
||||||
|
jbyteArray icon = env->NewByteArray(static_cast<jsize>(icon_data.size()));
|
||||||
|
env->SetByteArrayRegion(icon, 0, env->GetArrayLength(icon),
|
||||||
|
reinterpret_cast<jbyte*>(icon_data.data()));
|
||||||
|
return icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
jboolean Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIsHomebrew(JNIEnv* env, jobject obj,
|
||||||
|
jstring jpath) {
|
||||||
|
return static_cast<jboolean>(GetRomMetadata(GetJString(env, jpath)).isHomebrew);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Java_org_yuzu_yuzu_1emu_utils_GameMetadata_resetMetadata(JNIEnv* env, jobject obj) {
|
||||||
|
return m_rom_metadata_cache.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // extern "C"
|
@ -33,7 +33,6 @@
|
|||||||
#include "core/crypto/key_manager.h"
|
#include "core/crypto/key_manager.h"
|
||||||
#include "core/file_sys/card_image.h"
|
#include "core/file_sys/card_image.h"
|
||||||
#include "core/file_sys/content_archive.h"
|
#include "core/file_sys/content_archive.h"
|
||||||
#include "core/file_sys/registered_cache.h"
|
|
||||||
#include "core/file_sys/submission_package.h"
|
#include "core/file_sys/submission_package.h"
|
||||||
#include "core/file_sys/vfs.h"
|
#include "core/file_sys/vfs.h"
|
||||||
#include "core/file_sys/vfs_real.h"
|
#include "core/file_sys/vfs_real.h"
|
||||||
@ -48,515 +47,416 @@
|
|||||||
#include "core/hid/emulated_controller.h"
|
#include "core/hid/emulated_controller.h"
|
||||||
#include "core/hid/hid_core.h"
|
#include "core/hid/hid_core.h"
|
||||||
#include "core/hid/hid_types.h"
|
#include "core/hid/hid_types.h"
|
||||||
#include "core/hle/service/acc/profile_manager.h"
|
|
||||||
#include "core/hle/service/am/applet_ae.h"
|
#include "core/hle/service/am/applet_ae.h"
|
||||||
#include "core/hle/service/am/applet_oe.h"
|
#include "core/hle/service/am/applet_oe.h"
|
||||||
#include "core/hle/service/am/applets/applets.h"
|
#include "core/hle/service/am/applets/applets.h"
|
||||||
#include "core/hle/service/filesystem/filesystem.h"
|
#include "core/hle/service/filesystem/filesystem.h"
|
||||||
#include "core/loader/loader.h"
|
#include "core/loader/loader.h"
|
||||||
#include "core/perf_stats.h"
|
|
||||||
#include "jni/android_common/android_common.h"
|
#include "jni/android_common/android_common.h"
|
||||||
#include "jni/applets/software_keyboard.h"
|
|
||||||
#include "jni/config.h"
|
#include "jni/config.h"
|
||||||
#include "jni/emu_window/emu_window.h"
|
|
||||||
#include "jni/id_cache.h"
|
#include "jni/id_cache.h"
|
||||||
#include "video_core/rasterizer_interface.h"
|
#include "jni/native.h"
|
||||||
#include "video_core/renderer_base.h"
|
#include "video_core/renderer_base.h"
|
||||||
|
|
||||||
#define jconst [[maybe_unused]] const auto
|
#define jconst [[maybe_unused]] const auto
|
||||||
#define jauto [[maybe_unused]] auto
|
#define jauto [[maybe_unused]] auto
|
||||||
|
|
||||||
namespace {
|
static EmulationSession s_instance;
|
||||||
|
|
||||||
class EmulationSession final {
|
EmulationSession::EmulationSession() {
|
||||||
public:
|
m_vfs = std::make_shared<FileSys::RealVfsFilesystem>();
|
||||||
EmulationSession() {
|
}
|
||||||
m_vfs = std::make_shared<FileSys::RealVfsFilesystem>();
|
|
||||||
}
|
|
||||||
|
|
||||||
~EmulationSession() = default;
|
EmulationSession& EmulationSession::GetInstance() {
|
||||||
|
return s_instance;
|
||||||
|
}
|
||||||
|
|
||||||
static EmulationSession& GetInstance() {
|
const Core::System& EmulationSession::System() const {
|
||||||
return s_instance;
|
return m_system;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Core::System& System() const {
|
Core::System& EmulationSession::System() {
|
||||||
return m_system;
|
return m_system;
|
||||||
}
|
}
|
||||||
|
|
||||||
Core::System& System() {
|
const EmuWindow_Android& EmulationSession::Window() const {
|
||||||
return m_system;
|
return *m_window;
|
||||||
}
|
}
|
||||||
|
|
||||||
const EmuWindow_Android& Window() const {
|
EmuWindow_Android& EmulationSession::Window() {
|
||||||
return *m_window;
|
return *m_window;
|
||||||
}
|
}
|
||||||
|
|
||||||
EmuWindow_Android& Window() {
|
ANativeWindow* EmulationSession::NativeWindow() const {
|
||||||
return *m_window;
|
return m_native_window;
|
||||||
}
|
}
|
||||||
|
|
||||||
ANativeWindow* NativeWindow() const {
|
void EmulationSession::SetNativeWindow(ANativeWindow* native_window) {
|
||||||
return m_native_window;
|
m_native_window = native_window;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetNativeWindow(ANativeWindow* native_window) {
|
int EmulationSession::InstallFileToNand(std::string filename, std::string file_extension) {
|
||||||
m_native_window = native_window;
|
jconst copy_func = [](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest,
|
||||||
}
|
std::size_t block_size) {
|
||||||
|
if (src == nullptr || dest == nullptr) {
|
||||||
int InstallFileToNand(std::string filename, std::string file_extension) {
|
return false;
|
||||||
jconst copy_func = [](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest,
|
|
||||||
std::size_t block_size) {
|
|
||||||
if (src == nullptr || dest == nullptr) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!dest->Resize(src->GetSize())) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
using namespace Common::Literals;
|
|
||||||
[[maybe_unused]] std::vector<u8> buffer(1_MiB);
|
|
||||||
|
|
||||||
for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) {
|
|
||||||
jconst read = src->Read(buffer.data(), buffer.size(), i);
|
|
||||||
dest->Write(buffer.data(), read, i);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
enum InstallResult {
|
|
||||||
Success = 0,
|
|
||||||
SuccessFileOverwritten = 1,
|
|
||||||
InstallError = 2,
|
|
||||||
ErrorBaseGame = 3,
|
|
||||||
ErrorFilenameExtension = 4,
|
|
||||||
};
|
|
||||||
|
|
||||||
m_system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
|
|
||||||
m_system.GetFileSystemController().CreateFactories(*m_vfs);
|
|
||||||
|
|
||||||
[[maybe_unused]] std::shared_ptr<FileSys::NSP> nsp;
|
|
||||||
if (file_extension == "nsp") {
|
|
||||||
nsp = std::make_shared<FileSys::NSP>(m_vfs->OpenFile(filename, FileSys::Mode::Read));
|
|
||||||
if (nsp->IsExtractedType()) {
|
|
||||||
return InstallError;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return ErrorFilenameExtension;
|
|
||||||
}
|
}
|
||||||
|
if (!dest->Resize(src->GetSize())) {
|
||||||
if (!nsp) {
|
|
||||||
return InstallError;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nsp->GetStatus() != Loader::ResultStatus::Success) {
|
|
||||||
return InstallError;
|
|
||||||
}
|
|
||||||
|
|
||||||
jconst res = m_system.GetFileSystemController().GetUserNANDContents()->InstallEntry(
|
|
||||||
*nsp, true, copy_func);
|
|
||||||
|
|
||||||
switch (res) {
|
|
||||||
case FileSys::InstallResult::Success:
|
|
||||||
return Success;
|
|
||||||
case FileSys::InstallResult::OverwriteExisting:
|
|
||||||
return SuccessFileOverwritten;
|
|
||||||
case FileSys::InstallResult::ErrorBaseInstall:
|
|
||||||
return ErrorBaseGame;
|
|
||||||
default:
|
|
||||||
return InstallError;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void InitializeGpuDriver(const std::string& hook_lib_dir, const std::string& custom_driver_dir,
|
|
||||||
const std::string& custom_driver_name,
|
|
||||||
const std::string& file_redirect_dir) {
|
|
||||||
#ifdef ARCHITECTURE_arm64
|
|
||||||
void* handle{};
|
|
||||||
const char* file_redirect_dir_{};
|
|
||||||
int featureFlags{};
|
|
||||||
|
|
||||||
// Enable driver file redirection when renderer debugging is enabled.
|
|
||||||
if (Settings::values.renderer_debug && file_redirect_dir.size()) {
|
|
||||||
featureFlags |= ADRENOTOOLS_DRIVER_FILE_REDIRECT;
|
|
||||||
file_redirect_dir_ = file_redirect_dir.c_str();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to load a custom driver.
|
|
||||||
if (custom_driver_name.size()) {
|
|
||||||
handle = adrenotools_open_libvulkan(
|
|
||||||
RTLD_NOW, featureFlags | ADRENOTOOLS_DRIVER_CUSTOM, nullptr, hook_lib_dir.c_str(),
|
|
||||||
custom_driver_dir.c_str(), custom_driver_name.c_str(), file_redirect_dir_, nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to load the system driver.
|
|
||||||
if (!handle) {
|
|
||||||
handle =
|
|
||||||
adrenotools_open_libvulkan(RTLD_NOW, featureFlags, nullptr, hook_lib_dir.c_str(),
|
|
||||||
nullptr, nullptr, file_redirect_dir_, nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_vulkan_library = std::make_shared<Common::DynamicLibrary>(handle);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsRunning() const {
|
|
||||||
return m_is_running;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsPaused() const {
|
|
||||||
return m_is_running && m_is_paused;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Core::PerfStatsResults& PerfStats() const {
|
|
||||||
std::scoped_lock m_perf_stats_lock(m_perf_stats_mutex);
|
|
||||||
return m_perf_stats;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SurfaceChanged() {
|
|
||||||
if (!IsRunning()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
m_window->OnSurfaceChanged(m_native_window);
|
|
||||||
m_system.Renderer().NotifySurfaceChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ConfigureFilesystemProvider(const std::string& filepath) {
|
|
||||||
const auto file = m_system.GetFilesystem()->OpenFile(filepath, FileSys::Mode::Read);
|
|
||||||
if (!file) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto loader = Loader::GetLoader(m_system, file);
|
|
||||||
if (!loader) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto file_type = loader->GetFileType();
|
|
||||||
if (file_type == Loader::FileType::Unknown || file_type == Loader::FileType::Error) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
u64 program_id = 0;
|
|
||||||
const auto res2 = loader->ReadProgramId(program_id);
|
|
||||||
if (res2 == Loader::ResultStatus::Success && file_type == Loader::FileType::NCA) {
|
|
||||||
m_manual_provider->AddEntry(FileSys::TitleType::Application,
|
|
||||||
FileSys::GetCRTypeFromNCAType(FileSys::NCA{file}.GetType()),
|
|
||||||
program_id, file);
|
|
||||||
} else if (res2 == Loader::ResultStatus::Success &&
|
|
||||||
(file_type == Loader::FileType::XCI || file_type == Loader::FileType::NSP)) {
|
|
||||||
const auto nsp = file_type == Loader::FileType::NSP
|
|
||||||
? std::make_shared<FileSys::NSP>(file)
|
|
||||||
: FileSys::XCI{file}.GetSecurePartitionNSP();
|
|
||||||
for (const auto& title : nsp->GetNCAs()) {
|
|
||||||
for (const auto& entry : title.second) {
|
|
||||||
m_manual_provider->AddEntry(entry.first.first, entry.first.second, title.first,
|
|
||||||
entry.second->GetBaseFile());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Core::SystemResultStatus InitializeEmulation(const std::string& filepath) {
|
|
||||||
std::scoped_lock lock(m_mutex);
|
|
||||||
|
|
||||||
// Create the render window.
|
|
||||||
m_window = std::make_unique<EmuWindow_Android>(&m_input_subsystem, m_native_window,
|
|
||||||
m_vulkan_library);
|
|
||||||
|
|
||||||
m_system.SetFilesystem(m_vfs);
|
|
||||||
m_system.GetUserChannel().clear();
|
|
||||||
|
|
||||||
// Initialize system.
|
|
||||||
jauto android_keyboard = std::make_unique<SoftwareKeyboard::AndroidKeyboard>();
|
|
||||||
m_software_keyboard = android_keyboard.get();
|
|
||||||
m_system.SetShuttingDown(false);
|
|
||||||
m_system.ApplySettings();
|
|
||||||
Settings::LogSettings();
|
|
||||||
m_system.HIDCore().ReloadInputDevices();
|
|
||||||
m_system.SetAppletFrontendSet({
|
|
||||||
nullptr, // Amiibo Settings
|
|
||||||
nullptr, // Controller Selector
|
|
||||||
nullptr, // Error Display
|
|
||||||
nullptr, // Mii Editor
|
|
||||||
nullptr, // Parental Controls
|
|
||||||
nullptr, // Photo Viewer
|
|
||||||
nullptr, // Profile Selector
|
|
||||||
std::move(android_keyboard), // Software Keyboard
|
|
||||||
nullptr, // Web Browser
|
|
||||||
});
|
|
||||||
|
|
||||||
// Initialize filesystem.
|
|
||||||
m_manual_provider = std::make_unique<FileSys::ManualContentProvider>();
|
|
||||||
m_system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
|
|
||||||
m_system.RegisterContentProvider(FileSys::ContentProviderUnionSlot::FrontendManual,
|
|
||||||
m_manual_provider.get());
|
|
||||||
m_system.GetFileSystemController().CreateFactories(*m_vfs);
|
|
||||||
ConfigureFilesystemProvider(filepath);
|
|
||||||
|
|
||||||
// Initialize account manager
|
|
||||||
m_profile_manager = std::make_unique<Service::Account::ProfileManager>();
|
|
||||||
|
|
||||||
// Load the ROM.
|
|
||||||
m_load_result = m_system.Load(EmulationSession::GetInstance().Window(), filepath);
|
|
||||||
if (m_load_result != Core::SystemResultStatus::Success) {
|
|
||||||
return m_load_result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Complete initialization.
|
|
||||||
m_system.GPU().Start();
|
|
||||||
m_system.GetCpuManager().OnGpuReady();
|
|
||||||
m_system.RegisterExitCallback([&] { HaltEmulation(); });
|
|
||||||
|
|
||||||
return Core::SystemResultStatus::Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShutdownEmulation() {
|
|
||||||
std::scoped_lock lock(m_mutex);
|
|
||||||
|
|
||||||
m_is_running = false;
|
|
||||||
|
|
||||||
// Unload user input.
|
|
||||||
m_system.HIDCore().UnloadInputDevices();
|
|
||||||
|
|
||||||
// Shutdown the main emulated process
|
|
||||||
if (m_load_result == Core::SystemResultStatus::Success) {
|
|
||||||
m_system.DetachDebugger();
|
|
||||||
m_system.ShutdownMainProcess();
|
|
||||||
m_detached_tasks.WaitForAllTasks();
|
|
||||||
m_load_result = Core::SystemResultStatus::ErrorNotInitialized;
|
|
||||||
m_window.reset();
|
|
||||||
OnEmulationStopped(Core::SystemResultStatus::Success);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tear down the render window.
|
|
||||||
m_window.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
void PauseEmulation() {
|
|
||||||
std::scoped_lock lock(m_mutex);
|
|
||||||
m_system.Pause();
|
|
||||||
m_is_paused = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void UnPauseEmulation() {
|
|
||||||
std::scoped_lock lock(m_mutex);
|
|
||||||
m_system.Run();
|
|
||||||
m_is_paused = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void HaltEmulation() {
|
|
||||||
std::scoped_lock lock(m_mutex);
|
|
||||||
m_is_running = false;
|
|
||||||
m_cv.notify_one();
|
|
||||||
}
|
|
||||||
|
|
||||||
void RunEmulation() {
|
|
||||||
{
|
|
||||||
std::scoped_lock lock(m_mutex);
|
|
||||||
m_is_running = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load the disk shader cache.
|
|
||||||
if (Settings::values.use_disk_shader_cache.GetValue()) {
|
|
||||||
LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0);
|
|
||||||
m_system.Renderer().ReadRasterizer()->LoadDiskResources(
|
|
||||||
m_system.GetApplicationProcessProgramID(), std::stop_token{},
|
|
||||||
LoadDiskCacheProgress);
|
|
||||||
LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Complete, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void(m_system.Run());
|
|
||||||
|
|
||||||
if (m_system.DebuggerEnabled()) {
|
|
||||||
m_system.InitializeDebugger();
|
|
||||||
}
|
|
||||||
|
|
||||||
OnEmulationStarted();
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
{
|
|
||||||
[[maybe_unused]] std::unique_lock lock(m_mutex);
|
|
||||||
if (m_cv.wait_for(lock, std::chrono::milliseconds(800),
|
|
||||||
[&]() { return !m_is_running; })) {
|
|
||||||
// Emulation halted.
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
|
||||||
// Refresh performance stats.
|
|
||||||
std::scoped_lock m_perf_stats_lock(m_perf_stats_mutex);
|
|
||||||
m_perf_stats = m_system.GetAndResetPerfStats();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string GetRomTitle(const std::string& path) {
|
|
||||||
return GetRomMetadata(path).title;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<u8> GetRomIcon(const std::string& path) {
|
|
||||||
return GetRomMetadata(path).icon;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GetIsHomebrew(const std::string& path) {
|
|
||||||
return GetRomMetadata(path).isHomebrew;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ResetRomMetadata() {
|
|
||||||
m_rom_metadata_cache.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsHandheldOnly() {
|
|
||||||
jconst npad_style_set = m_system.HIDCore().GetSupportedStyleTag();
|
|
||||||
|
|
||||||
if (npad_style_set.fullkey == 1) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (npad_style_set.handheld == 0) {
|
using namespace Common::Literals;
|
||||||
return false;
|
[[maybe_unused]] std::vector<u8> buffer(1_MiB);
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) {
|
||||||
|
jconst read = src->Read(buffer.data(), buffer.size(), i);
|
||||||
|
dest->Write(buffer.data(), read, i);
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
return !Settings::IsDockedMode();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetDeviceType([[maybe_unused]] int index, int type) {
|
|
||||||
jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
|
|
||||||
controller->SetNpadStyleIndex(static_cast<Core::HID::NpadStyleIndex>(type));
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnGamepadConnectEvent([[maybe_unused]] int index) {
|
|
||||||
jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
|
|
||||||
|
|
||||||
// Ensure that player1 is configured correctly and handheld disconnected
|
|
||||||
if (controller->GetNpadIdType() == Core::HID::NpadIdType::Player1) {
|
|
||||||
jauto handheld =
|
|
||||||
m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld);
|
|
||||||
|
|
||||||
if (controller->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::Handheld) {
|
|
||||||
handheld->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController);
|
|
||||||
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController);
|
|
||||||
handheld->Disconnect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure that handheld is configured correctly and player 1 disconnected
|
|
||||||
if (controller->GetNpadIdType() == Core::HID::NpadIdType::Handheld) {
|
|
||||||
jauto player1 =
|
|
||||||
m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
|
|
||||||
|
|
||||||
if (controller->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::Handheld) {
|
|
||||||
player1->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld);
|
|
||||||
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld);
|
|
||||||
player1->Disconnect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!controller->IsConnected()) {
|
|
||||||
controller->Connect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnGamepadDisconnectEvent([[maybe_unused]] int index) {
|
|
||||||
jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
|
|
||||||
controller->Disconnect();
|
|
||||||
}
|
|
||||||
|
|
||||||
SoftwareKeyboard::AndroidKeyboard* SoftwareKeyboard() {
|
|
||||||
return m_software_keyboard;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
struct RomMetadata {
|
|
||||||
std::string title;
|
|
||||||
std::vector<u8> icon;
|
|
||||||
bool isHomebrew;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
RomMetadata GetRomMetadata(const std::string& path) {
|
enum InstallResult {
|
||||||
if (jauto search = m_rom_metadata_cache.find(path); search != m_rom_metadata_cache.end()) {
|
Success = 0,
|
||||||
return search->second;
|
SuccessFileOverwritten = 1,
|
||||||
|
InstallError = 2,
|
||||||
|
ErrorBaseGame = 3,
|
||||||
|
ErrorFilenameExtension = 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
m_system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
|
||||||
|
m_system.GetFileSystemController().CreateFactories(*m_vfs);
|
||||||
|
|
||||||
|
[[maybe_unused]] std::shared_ptr<FileSys::NSP> nsp;
|
||||||
|
if (file_extension == "nsp") {
|
||||||
|
nsp = std::make_shared<FileSys::NSP>(m_vfs->OpenFile(filename, FileSys::Mode::Read));
|
||||||
|
if (nsp->IsExtractedType()) {
|
||||||
|
return InstallError;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
return CacheRomMetadata(path);
|
return ErrorFilenameExtension;
|
||||||
}
|
}
|
||||||
|
|
||||||
RomMetadata CacheRomMetadata(const std::string& path) {
|
if (!nsp) {
|
||||||
jconst file = Core::GetGameFileFromPath(m_vfs, path);
|
return InstallError;
|
||||||
jauto loader = Loader::GetLoader(EmulationSession::GetInstance().System(), file, 0, 0);
|
}
|
||||||
|
|
||||||
RomMetadata entry;
|
if (nsp->GetStatus() != Loader::ResultStatus::Success) {
|
||||||
loader->ReadTitle(entry.title);
|
return InstallError;
|
||||||
loader->ReadIcon(entry.icon);
|
}
|
||||||
if (loader->GetFileType() == Loader::FileType::NRO) {
|
|
||||||
jauto loader_nro = reinterpret_cast<Loader::AppLoader_NRO*>(loader.get());
|
jconst res = m_system.GetFileSystemController().GetUserNANDContents()->InstallEntry(*nsp, true,
|
||||||
entry.isHomebrew = loader_nro->IsHomebrew();
|
copy_func);
|
||||||
} else {
|
|
||||||
entry.isHomebrew = false;
|
switch (res) {
|
||||||
|
case FileSys::InstallResult::Success:
|
||||||
|
return Success;
|
||||||
|
case FileSys::InstallResult::OverwriteExisting:
|
||||||
|
return SuccessFileOverwritten;
|
||||||
|
case FileSys::InstallResult::ErrorBaseInstall:
|
||||||
|
return ErrorBaseGame;
|
||||||
|
default:
|
||||||
|
return InstallError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmulationSession::InitializeGpuDriver(const std::string& hook_lib_dir,
|
||||||
|
const std::string& custom_driver_dir,
|
||||||
|
const std::string& custom_driver_name,
|
||||||
|
const std::string& file_redirect_dir) {
|
||||||
|
#ifdef ARCHITECTURE_arm64
|
||||||
|
void* handle{};
|
||||||
|
const char* file_redirect_dir_{};
|
||||||
|
int featureFlags{};
|
||||||
|
|
||||||
|
// Enable driver file redirection when renderer debugging is enabled.
|
||||||
|
if (Settings::values.renderer_debug && file_redirect_dir.size()) {
|
||||||
|
featureFlags |= ADRENOTOOLS_DRIVER_FILE_REDIRECT;
|
||||||
|
file_redirect_dir_ = file_redirect_dir.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to load a custom driver.
|
||||||
|
if (custom_driver_name.size()) {
|
||||||
|
handle = adrenotools_open_libvulkan(
|
||||||
|
RTLD_NOW, featureFlags | ADRENOTOOLS_DRIVER_CUSTOM, nullptr, hook_lib_dir.c_str(),
|
||||||
|
custom_driver_dir.c_str(), custom_driver_name.c_str(), file_redirect_dir_, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to load the system driver.
|
||||||
|
if (!handle) {
|
||||||
|
handle = adrenotools_open_libvulkan(RTLD_NOW, featureFlags, nullptr, hook_lib_dir.c_str(),
|
||||||
|
nullptr, nullptr, file_redirect_dir_, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_vulkan_library = std::make_shared<Common::DynamicLibrary>(handle);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EmulationSession::IsRunning() const {
|
||||||
|
return m_is_running;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EmulationSession::IsPaused() const {
|
||||||
|
return m_is_running && m_is_paused;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Core::PerfStatsResults& EmulationSession::PerfStats() const {
|
||||||
|
std::scoped_lock m_perf_stats_lock(m_perf_stats_mutex);
|
||||||
|
return m_perf_stats;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmulationSession::SurfaceChanged() {
|
||||||
|
if (!IsRunning()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_window->OnSurfaceChanged(m_native_window);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmulationSession::ConfigureFilesystemProvider(const std::string& filepath) {
|
||||||
|
const auto file = m_system.GetFilesystem()->OpenFile(filepath, FileSys::Mode::Read);
|
||||||
|
if (!file) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto loader = Loader::GetLoader(m_system, file);
|
||||||
|
if (!loader) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto file_type = loader->GetFileType();
|
||||||
|
if (file_type == Loader::FileType::Unknown || file_type == Loader::FileType::Error) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 program_id = 0;
|
||||||
|
const auto res2 = loader->ReadProgramId(program_id);
|
||||||
|
if (res2 == Loader::ResultStatus::Success && file_type == Loader::FileType::NCA) {
|
||||||
|
m_manual_provider->AddEntry(FileSys::TitleType::Application,
|
||||||
|
FileSys::GetCRTypeFromNCAType(FileSys::NCA{file}.GetType()),
|
||||||
|
program_id, file);
|
||||||
|
} else if (res2 == Loader::ResultStatus::Success &&
|
||||||
|
(file_type == Loader::FileType::XCI || file_type == Loader::FileType::NSP)) {
|
||||||
|
const auto nsp = file_type == Loader::FileType::NSP
|
||||||
|
? std::make_shared<FileSys::NSP>(file)
|
||||||
|
: FileSys::XCI{file}.GetSecurePartitionNSP();
|
||||||
|
for (const auto& title : nsp->GetNCAs()) {
|
||||||
|
for (const auto& entry : title.second) {
|
||||||
|
m_manual_provider->AddEntry(entry.first.first, entry.first.second, title.first,
|
||||||
|
entry.second->GetBaseFile());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
m_rom_metadata_cache[path] = entry;
|
Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string& filepath) {
|
||||||
|
std::scoped_lock lock(m_mutex);
|
||||||
|
|
||||||
return entry;
|
// Create the render window.
|
||||||
|
m_window =
|
||||||
|
std::make_unique<EmuWindow_Android>(&m_input_subsystem, m_native_window, m_vulkan_library);
|
||||||
|
|
||||||
|
m_system.SetFilesystem(m_vfs);
|
||||||
|
m_system.GetUserChannel().clear();
|
||||||
|
|
||||||
|
// Initialize system.
|
||||||
|
jauto android_keyboard = std::make_unique<SoftwareKeyboard::AndroidKeyboard>();
|
||||||
|
m_software_keyboard = android_keyboard.get();
|
||||||
|
m_system.SetShuttingDown(false);
|
||||||
|
m_system.ApplySettings();
|
||||||
|
Settings::LogSettings();
|
||||||
|
m_system.HIDCore().ReloadInputDevices();
|
||||||
|
m_system.SetAppletFrontendSet({
|
||||||
|
nullptr, // Amiibo Settings
|
||||||
|
nullptr, // Controller Selector
|
||||||
|
nullptr, // Error Display
|
||||||
|
nullptr, // Mii Editor
|
||||||
|
nullptr, // Parental Controls
|
||||||
|
nullptr, // Photo Viewer
|
||||||
|
nullptr, // Profile Selector
|
||||||
|
std::move(android_keyboard), // Software Keyboard
|
||||||
|
nullptr, // Web Browser
|
||||||
|
});
|
||||||
|
|
||||||
|
// Initialize filesystem.
|
||||||
|
m_manual_provider = std::make_unique<FileSys::ManualContentProvider>();
|
||||||
|
m_system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
|
||||||
|
m_system.RegisterContentProvider(FileSys::ContentProviderUnionSlot::FrontendManual,
|
||||||
|
m_manual_provider.get());
|
||||||
|
m_system.GetFileSystemController().CreateFactories(*m_vfs);
|
||||||
|
ConfigureFilesystemProvider(filepath);
|
||||||
|
|
||||||
|
// Initialize account manager
|
||||||
|
m_profile_manager = std::make_unique<Service::Account::ProfileManager>();
|
||||||
|
|
||||||
|
// Load the ROM.
|
||||||
|
m_load_result = m_system.Load(EmulationSession::GetInstance().Window(), filepath);
|
||||||
|
if (m_load_result != Core::SystemResultStatus::Success) {
|
||||||
|
return m_load_result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
// Complete initialization.
|
||||||
static void LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, int max) {
|
m_system.GPU().Start();
|
||||||
JNIEnv* env = IDCache::GetEnvForThread();
|
m_system.GetCpuManager().OnGpuReady();
|
||||||
env->CallStaticVoidMethod(IDCache::GetDiskCacheProgressClass(),
|
m_system.RegisterExitCallback([&] { HaltEmulation(); });
|
||||||
IDCache::GetDiskCacheLoadProgress(), static_cast<jint>(stage),
|
|
||||||
static_cast<jint>(progress), static_cast<jint>(max));
|
return Core::SystemResultStatus::Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmulationSession::ShutdownEmulation() {
|
||||||
|
std::scoped_lock lock(m_mutex);
|
||||||
|
|
||||||
|
m_is_running = false;
|
||||||
|
|
||||||
|
// Unload user input.
|
||||||
|
m_system.HIDCore().UnloadInputDevices();
|
||||||
|
|
||||||
|
// Shutdown the main emulated process
|
||||||
|
if (m_load_result == Core::SystemResultStatus::Success) {
|
||||||
|
m_system.DetachDebugger();
|
||||||
|
m_system.ShutdownMainProcess();
|
||||||
|
m_detached_tasks.WaitForAllTasks();
|
||||||
|
m_load_result = Core::SystemResultStatus::ErrorNotInitialized;
|
||||||
|
m_window.reset();
|
||||||
|
OnEmulationStopped(Core::SystemResultStatus::Success);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void OnEmulationStarted() {
|
// Tear down the render window.
|
||||||
JNIEnv* env = IDCache::GetEnvForThread();
|
m_window.reset();
|
||||||
env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(),
|
}
|
||||||
IDCache::GetOnEmulationStarted());
|
|
||||||
|
void EmulationSession::PauseEmulation() {
|
||||||
|
std::scoped_lock lock(m_mutex);
|
||||||
|
m_system.Pause();
|
||||||
|
m_is_paused = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmulationSession::UnPauseEmulation() {
|
||||||
|
std::scoped_lock lock(m_mutex);
|
||||||
|
m_system.Run();
|
||||||
|
m_is_paused = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmulationSession::HaltEmulation() {
|
||||||
|
std::scoped_lock lock(m_mutex);
|
||||||
|
m_is_running = false;
|
||||||
|
m_cv.notify_one();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmulationSession::RunEmulation() {
|
||||||
|
{
|
||||||
|
std::scoped_lock lock(m_mutex);
|
||||||
|
m_is_running = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void OnEmulationStopped(Core::SystemResultStatus result) {
|
// Load the disk shader cache.
|
||||||
JNIEnv* env = IDCache::GetEnvForThread();
|
if (Settings::values.use_disk_shader_cache.GetValue()) {
|
||||||
env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(),
|
LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0);
|
||||||
IDCache::GetOnEmulationStopped(), static_cast<jint>(result));
|
m_system.Renderer().ReadRasterizer()->LoadDiskResources(
|
||||||
|
m_system.GetApplicationProcessProgramID(), std::stop_token{}, LoadDiskCacheProgress);
|
||||||
|
LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Complete, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
void(m_system.Run());
|
||||||
static EmulationSession s_instance;
|
|
||||||
|
|
||||||
// Frontend management
|
if (m_system.DebuggerEnabled()) {
|
||||||
std::unordered_map<std::string, RomMetadata> m_rom_metadata_cache;
|
m_system.InitializeDebugger();
|
||||||
|
}
|
||||||
|
|
||||||
// Window management
|
OnEmulationStarted();
|
||||||
std::unique_ptr<EmuWindow_Android> m_window;
|
|
||||||
ANativeWindow* m_native_window{};
|
|
||||||
|
|
||||||
// Core emulation
|
while (true) {
|
||||||
Core::System m_system;
|
{
|
||||||
InputCommon::InputSubsystem m_input_subsystem;
|
[[maybe_unused]] std::unique_lock lock(m_mutex);
|
||||||
Common::DetachedTasks m_detached_tasks;
|
if (m_cv.wait_for(lock, std::chrono::milliseconds(800),
|
||||||
Core::PerfStatsResults m_perf_stats{};
|
[&]() { return !m_is_running; })) {
|
||||||
std::shared_ptr<FileSys::VfsFilesystem> m_vfs;
|
// Emulation halted.
|
||||||
Core::SystemResultStatus m_load_result{Core::SystemResultStatus::ErrorNotInitialized};
|
break;
|
||||||
std::atomic<bool> m_is_running = false;
|
}
|
||||||
std::atomic<bool> m_is_paused = false;
|
}
|
||||||
SoftwareKeyboard::AndroidKeyboard* m_software_keyboard{};
|
{
|
||||||
std::unique_ptr<Service::Account::ProfileManager> m_profile_manager;
|
// Refresh performance stats.
|
||||||
std::unique_ptr<FileSys::ManualContentProvider> m_manual_provider;
|
std::scoped_lock m_perf_stats_lock(m_perf_stats_mutex);
|
||||||
|
m_perf_stats = m_system.GetAndResetPerfStats();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// GPU driver parameters
|
bool EmulationSession::IsHandheldOnly() {
|
||||||
std::shared_ptr<Common::DynamicLibrary> m_vulkan_library;
|
jconst npad_style_set = m_system.HIDCore().GetSupportedStyleTag();
|
||||||
|
|
||||||
// Synchronization
|
if (npad_style_set.fullkey == 1) {
|
||||||
std::condition_variable_any m_cv;
|
return false;
|
||||||
mutable std::mutex m_perf_stats_mutex;
|
}
|
||||||
mutable std::mutex m_mutex;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*static*/ EmulationSession EmulationSession::s_instance;
|
if (npad_style_set.handheld == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
} // Anonymous namespace
|
return !Settings::IsDockedMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmulationSession::SetDeviceType([[maybe_unused]] int index, int type) {
|
||||||
|
jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
|
||||||
|
controller->SetNpadStyleIndex(static_cast<Core::HID::NpadStyleIndex>(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmulationSession::OnGamepadConnectEvent([[maybe_unused]] int index) {
|
||||||
|
jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
|
||||||
|
|
||||||
|
// Ensure that player1 is configured correctly and handheld disconnected
|
||||||
|
if (controller->GetNpadIdType() == Core::HID::NpadIdType::Player1) {
|
||||||
|
jauto handheld = m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld);
|
||||||
|
|
||||||
|
if (controller->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::Handheld) {
|
||||||
|
handheld->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController);
|
||||||
|
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController);
|
||||||
|
handheld->Disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure that handheld is configured correctly and player 1 disconnected
|
||||||
|
if (controller->GetNpadIdType() == Core::HID::NpadIdType::Handheld) {
|
||||||
|
jauto player1 = m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
|
||||||
|
|
||||||
|
if (controller->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::Handheld) {
|
||||||
|
player1->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld);
|
||||||
|
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld);
|
||||||
|
player1->Disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!controller->IsConnected()) {
|
||||||
|
controller->Connect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmulationSession::OnGamepadDisconnectEvent([[maybe_unused]] int index) {
|
||||||
|
jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
|
||||||
|
controller->Disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
SoftwareKeyboard::AndroidKeyboard* EmulationSession::SoftwareKeyboard() {
|
||||||
|
return m_software_keyboard;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmulationSession::LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress,
|
||||||
|
int max) {
|
||||||
|
JNIEnv* env = IDCache::GetEnvForThread();
|
||||||
|
env->CallStaticVoidMethod(IDCache::GetDiskCacheProgressClass(),
|
||||||
|
IDCache::GetDiskCacheLoadProgress(), static_cast<jint>(stage),
|
||||||
|
static_cast<jint>(progress), static_cast<jint>(max));
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmulationSession::OnEmulationStarted() {
|
||||||
|
JNIEnv* env = IDCache::GetEnvForThread();
|
||||||
|
env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), IDCache::GetOnEmulationStarted());
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmulationSession::OnEmulationStopped(Core::SystemResultStatus result) {
|
||||||
|
JNIEnv* env = IDCache::GetEnvForThread();
|
||||||
|
env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), IDCache::GetOnEmulationStopped(),
|
||||||
|
static_cast<jint>(result));
|
||||||
|
}
|
||||||
|
|
||||||
static Core::SystemResultStatus RunEmulation(const std::string& filepath) {
|
static Core::SystemResultStatus RunEmulation(const std::string& filepath) {
|
||||||
Common::Log::Initialize();
|
Common::Log::Initialize();
|
||||||
@ -658,10 +558,6 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_stopEmulation(JNIEnv* env, jclass cla
|
|||||||
EmulationSession::GetInstance().HaltEmulation();
|
EmulationSession::GetInstance().HaltEmulation();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_resetRomMetadata(JNIEnv* env, jclass clazz) {
|
|
||||||
EmulationSession::GetInstance().ResetRomMetadata();
|
|
||||||
}
|
|
||||||
|
|
||||||
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isRunning(JNIEnv* env, jclass clazz) {
|
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isRunning(JNIEnv* env, jclass clazz) {
|
||||||
return static_cast<jboolean>(EmulationSession::GetInstance().IsRunning());
|
return static_cast<jboolean>(EmulationSession::GetInstance().IsRunning());
|
||||||
}
|
}
|
||||||
@ -767,46 +663,6 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchReleased(JNIEnv* env, jclass c
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
jbyteArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getIcon(JNIEnv* env, jclass clazz,
|
|
||||||
jstring j_filename) {
|
|
||||||
jauto icon_data = EmulationSession::GetInstance().GetRomIcon(GetJString(env, j_filename));
|
|
||||||
jbyteArray icon = env->NewByteArray(static_cast<jsize>(icon_data.size()));
|
|
||||||
env->SetByteArrayRegion(icon, 0, env->GetArrayLength(icon),
|
|
||||||
reinterpret_cast<jbyte*>(icon_data.data()));
|
|
||||||
return icon;
|
|
||||||
}
|
|
||||||
|
|
||||||
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getTitle(JNIEnv* env, jclass clazz,
|
|
||||||
jstring j_filename) {
|
|
||||||
jauto title = EmulationSession::GetInstance().GetRomTitle(GetJString(env, j_filename));
|
|
||||||
return env->NewStringUTF(title.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getDescription(JNIEnv* env, jclass clazz,
|
|
||||||
jstring j_filename) {
|
|
||||||
return j_filename;
|
|
||||||
}
|
|
||||||
|
|
||||||
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getGameId(JNIEnv* env, jclass clazz,
|
|
||||||
jstring j_filename) {
|
|
||||||
return j_filename;
|
|
||||||
}
|
|
||||||
|
|
||||||
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getRegions(JNIEnv* env, jclass clazz,
|
|
||||||
jstring j_filename) {
|
|
||||||
return env->NewStringUTF("");
|
|
||||||
}
|
|
||||||
|
|
||||||
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getCompany(JNIEnv* env, jclass clazz,
|
|
||||||
jstring j_filename) {
|
|
||||||
return env->NewStringUTF("");
|
|
||||||
}
|
|
||||||
|
|
||||||
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isHomebrew(JNIEnv* env, jclass clazz,
|
|
||||||
jstring j_filename) {
|
|
||||||
return EmulationSession::GetInstance().GetIsHomebrew(GetJString(env, j_filename));
|
|
||||||
}
|
|
||||||
|
|
||||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmulation(JNIEnv* env, jclass clazz) {
|
void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmulation(JNIEnv* env, jclass clazz) {
|
||||||
// Create the default config.ini.
|
// Create the default config.ini.
|
||||||
Config{};
|
Config{};
|
||||||
|
84
src/android/app/src/main/jni/native.h
Normal file
84
src/android/app/src/main/jni/native.h
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include <android/native_window_jni.h>
|
||||||
|
#include "common/detached_tasks.h"
|
||||||
|
#include "core/core.h"
|
||||||
|
#include "core/file_sys/registered_cache.h"
|
||||||
|
#include "core/hle/service/acc/profile_manager.h"
|
||||||
|
#include "core/perf_stats.h"
|
||||||
|
#include "jni/applets/software_keyboard.h"
|
||||||
|
#include "jni/emu_window/emu_window.h"
|
||||||
|
#include "video_core/rasterizer_interface.h"
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
class EmulationSession final {
|
||||||
|
public:
|
||||||
|
explicit EmulationSession();
|
||||||
|
~EmulationSession() = default;
|
||||||
|
|
||||||
|
static EmulationSession& GetInstance();
|
||||||
|
const Core::System& System() const;
|
||||||
|
Core::System& System();
|
||||||
|
|
||||||
|
const EmuWindow_Android& Window() const;
|
||||||
|
EmuWindow_Android& Window();
|
||||||
|
ANativeWindow* NativeWindow() const;
|
||||||
|
void SetNativeWindow(ANativeWindow* native_window);
|
||||||
|
void SurfaceChanged();
|
||||||
|
|
||||||
|
int InstallFileToNand(std::string filename, std::string file_extension);
|
||||||
|
void InitializeGpuDriver(const std::string& hook_lib_dir, const std::string& custom_driver_dir,
|
||||||
|
const std::string& custom_driver_name,
|
||||||
|
const std::string& file_redirect_dir);
|
||||||
|
|
||||||
|
bool IsRunning() const;
|
||||||
|
bool IsPaused() const;
|
||||||
|
void PauseEmulation();
|
||||||
|
void UnPauseEmulation();
|
||||||
|
void HaltEmulation();
|
||||||
|
void RunEmulation();
|
||||||
|
void ShutdownEmulation();
|
||||||
|
|
||||||
|
const Core::PerfStatsResults& PerfStats() const;
|
||||||
|
void ConfigureFilesystemProvider(const std::string& filepath);
|
||||||
|
Core::SystemResultStatus InitializeEmulation(const std::string& filepath);
|
||||||
|
|
||||||
|
bool IsHandheldOnly();
|
||||||
|
void SetDeviceType([[maybe_unused]] int index, int type);
|
||||||
|
void OnGamepadConnectEvent([[maybe_unused]] int index);
|
||||||
|
void OnGamepadDisconnectEvent([[maybe_unused]] int index);
|
||||||
|
SoftwareKeyboard::AndroidKeyboard* SoftwareKeyboard();
|
||||||
|
|
||||||
|
private:
|
||||||
|
static void LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, int max);
|
||||||
|
static void OnEmulationStarted();
|
||||||
|
static void OnEmulationStopped(Core::SystemResultStatus result);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Window management
|
||||||
|
std::unique_ptr<EmuWindow_Android> m_window;
|
||||||
|
ANativeWindow* m_native_window{};
|
||||||
|
|
||||||
|
// Core emulation
|
||||||
|
Core::System m_system;
|
||||||
|
InputCommon::InputSubsystem m_input_subsystem;
|
||||||
|
Common::DetachedTasks m_detached_tasks;
|
||||||
|
Core::PerfStatsResults m_perf_stats{};
|
||||||
|
std::shared_ptr<FileSys::VfsFilesystem> m_vfs;
|
||||||
|
Core::SystemResultStatus m_load_result{Core::SystemResultStatus::ErrorNotInitialized};
|
||||||
|
std::atomic<bool> m_is_running = false;
|
||||||
|
std::atomic<bool> m_is_paused = false;
|
||||||
|
SoftwareKeyboard::AndroidKeyboard* m_software_keyboard{};
|
||||||
|
std::unique_ptr<Service::Account::ProfileManager> m_profile_manager;
|
||||||
|
std::unique_ptr<FileSys::ManualContentProvider> m_manual_provider;
|
||||||
|
|
||||||
|
// GPU driver parameters
|
||||||
|
std::shared_ptr<Common::DynamicLibrary> m_vulkan_library;
|
||||||
|
|
||||||
|
// Synchronization
|
||||||
|
std::condition_variable_any m_cv;
|
||||||
|
mutable std::mutex m_perf_stats_mutex;
|
||||||
|
mutable std::mutex m_mutex;
|
||||||
|
};
|
9
src/android/app/src/main/res/drawable/ic_build.xml
Normal file
9
src/android/app/src/main/res/drawable/ic_build.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="?attr/colorControlNormal"
|
||||||
|
android:pathData="M22.7,19l-9.1,-9.1c0.9,-2.3 0.4,-5 -1.5,-6.9 -2,-2 -5,-2.4 -7.4,-1.3L9,6 6,9 1.6,4.7C0.4,7.1 0.9,10.1 2.9,12.1c1.9,1.9 4.6,2.4 6.9,1.5l9.1,9.1c0.4,0.4 1,0.4 1.4,0l2.3,-2.3c0.5,-0.4 0.5,-1.1 0.1,-1.4z" />
|
||||||
|
</vector>
|
9
src/android/app/src/main/res/drawable/ic_delete.xml
Normal file
9
src/android/app/src/main/res/drawable/ic_delete.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="?attr/colorControlNormal"
|
||||||
|
android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z" />
|
||||||
|
</vector>
|
89
src/android/app/src/main/res/layout/card_driver_option.xml
Normal file
89
src/android/app/src/main/res/layout/card_driver_option.xml
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
style="?attr/materialCardViewOutlinedStyle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="16dp"
|
||||||
|
android:layout_marginVertical="12dp"
|
||||||
|
android:background="?attr/selectableItemBackground"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:padding="16dp">
|
||||||
|
|
||||||
|
<RadioButton
|
||||||
|
android:id="@+id/radio_button"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:clickable="false"
|
||||||
|
android:checked="false" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_gravity="center_vertical">
|
||||||
|
|
||||||
|
<com.google.android.material.textview.MaterialTextView
|
||||||
|
android:id="@+id/title"
|
||||||
|
style="@style/TextAppearance.Material3.TitleMedium"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:ellipsize="none"
|
||||||
|
android:marqueeRepeatLimit="marquee_forever"
|
||||||
|
android:requiresFadingEdge="horizontal"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:textAlignment="viewStart"
|
||||||
|
tools:text="@string/select_gpu_driver_default" />
|
||||||
|
|
||||||
|
<com.google.android.material.textview.MaterialTextView
|
||||||
|
android:id="@+id/version"
|
||||||
|
style="@style/TextAppearance.Material3.BodyMedium"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="6dp"
|
||||||
|
android:ellipsize="none"
|
||||||
|
android:marqueeRepeatLimit="marquee_forever"
|
||||||
|
android:requiresFadingEdge="horizontal"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:textAlignment="viewStart"
|
||||||
|
tools:text="@string/install_gpu_driver_description" />
|
||||||
|
|
||||||
|
<com.google.android.material.textview.MaterialTextView
|
||||||
|
android:id="@+id/description"
|
||||||
|
style="@style/TextAppearance.Material3.BodyMedium"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="6dp"
|
||||||
|
android:ellipsize="none"
|
||||||
|
android:marqueeRepeatLimit="marquee_forever"
|
||||||
|
android:requiresFadingEdge="horizontal"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:textAlignment="viewStart"
|
||||||
|
tools:text="@string/install_gpu_driver_description" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/button_delete"
|
||||||
|
style="@style/Widget.Material3.Button.IconButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:contentDescription="@string/delete"
|
||||||
|
android:tooltipText="@string/delete"
|
||||||
|
app:icon="@drawable/ic_delete"
|
||||||
|
app:iconTint="?attr/colorControlNormal" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
@ -1,63 +1,54 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<FrameLayout
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
<com.google.android.material.card.MaterialCardView
|
||||||
style="?attr/materialCardViewElevatedStyle"
|
|
||||||
android:id="@+id/card_game"
|
android:id="@+id/card_game"
|
||||||
|
style="?attr/materialCardViewElevatedStyle"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
android:background="?attr/selectableItemBackground"
|
android:background="?attr/selectableItemBackground"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
android:clipToPadding="false"
|
android:clipToPadding="false"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:transitionName="card_game"
|
android:transitionName="card_game"
|
||||||
android:layout_gravity="center"
|
app:cardCornerRadius="4dp"
|
||||||
app:cardElevation="0dp"
|
app:cardElevation="0dp">
|
||||||
app:cardCornerRadius="12dp">
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:padding="6dp">
|
android:padding="6dp">
|
||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
<com.google.android.material.imageview.ShapeableImageView
|
||||||
style="?attr/materialCardViewElevatedStyle"
|
android:id="@+id/image_game_screen"
|
||||||
android:id="@+id/card_game_art"
|
|
||||||
android:layout_width="150dp"
|
android:layout_width="150dp"
|
||||||
android:layout_height="150dp"
|
android:layout_height="150dp"
|
||||||
app:cardCornerRadius="4dp"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent">
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:shapeAppearance="@style/ShapeAppearance.Material3.Corner.ExtraSmall"
|
||||||
<ImageView
|
tools:src="@drawable/default_icon" />
|
||||||
android:id="@+id/image_game_screen"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
tools:src="@drawable/default_icon" />
|
|
||||||
|
|
||||||
</com.google.android.material.card.MaterialCardView>
|
|
||||||
|
|
||||||
<com.google.android.material.textview.MaterialTextView
|
<com.google.android.material.textview.MaterialTextView
|
||||||
style="@style/TextAppearance.Material3.TitleMedium"
|
|
||||||
android:id="@+id/text_game_title"
|
android:id="@+id/text_game_title"
|
||||||
|
style="@style/TextAppearance.Material3.TitleMedium"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="8dp"
|
android:layout_marginTop="8dp"
|
||||||
|
android:ellipsize="none"
|
||||||
|
android:marqueeRepeatLimit="marquee_forever"
|
||||||
|
android:requiresFadingEdge="horizontal"
|
||||||
|
android:singleLine="true"
|
||||||
android:textAlignment="center"
|
android:textAlignment="center"
|
||||||
android:textSize="14sp"
|
android:textSize="14sp"
|
||||||
android:singleLine="true"
|
app:layout_constraintEnd_toEndOf="@+id/image_game_screen"
|
||||||
android:marqueeRepeatLimit="marquee_forever"
|
app:layout_constraintStart_toStartOf="@+id/image_game_screen"
|
||||||
android:ellipsize="none"
|
app:layout_constraintTop_toBottomOf="@+id/image_game_screen"
|
||||||
android:requiresFadingEdge="horizontal"
|
|
||||||
app:layout_constraintEnd_toEndOf="@+id/card_game_art"
|
|
||||||
app:layout_constraintStart_toStartOf="@+id/card_game_art"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/card_game_art"
|
|
||||||
tools:text="The Legend of Zelda: Skyward Sword" />
|
tools:text="The Legend of Zelda: Skyward Sword" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
@ -16,7 +16,8 @@
|
|||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/option_layout"
|
android:id="@+id/option_layout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/option_icon"
|
android:id="@+id/option_icon"
|
||||||
|
@ -0,0 +1,48 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:id="@+id/coordinator_licenses"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="?attr/colorSurface">
|
||||||
|
|
||||||
|
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
|
android:id="@+id/appbar_drivers"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:fitsSystemWindows="true"
|
||||||
|
app:liftOnScrollTargetViewId="@id/list_drivers">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.MaterialToolbar
|
||||||
|
android:id="@+id/toolbar_drivers"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
app:navigationIcon="@drawable/ic_back"
|
||||||
|
app:title="@string/gpu_driver_manager" />
|
||||||
|
|
||||||
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/list_drivers"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||||
|
|
||||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
||||||
|
android:id="@+id/button_install"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="bottom|end"
|
||||||
|
android:text="@string/install"
|
||||||
|
app:icon="@drawable/ic_add"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -22,6 +22,9 @@
|
|||||||
<action
|
<action
|
||||||
android:id="@+id/action_homeSettingsFragment_to_installableFragment"
|
android:id="@+id/action_homeSettingsFragment_to_installableFragment"
|
||||||
app:destination="@id/installableFragment" />
|
app:destination="@id/installableFragment" />
|
||||||
|
<action
|
||||||
|
android:id="@+id/action_homeSettingsFragment_to_driverManagerFragment"
|
||||||
|
app:destination="@id/driverManagerFragment" />
|
||||||
</fragment>
|
</fragment>
|
||||||
|
|
||||||
<fragment
|
<fragment
|
||||||
@ -95,5 +98,9 @@
|
|||||||
android:id="@+id/installableFragment"
|
android:id="@+id/installableFragment"
|
||||||
android:name="org.yuzu.yuzu_emu.fragments.InstallableFragment"
|
android:name="org.yuzu.yuzu_emu.fragments.InstallableFragment"
|
||||||
android:label="InstallableFragment" />
|
android:label="InstallableFragment" />
|
||||||
|
<fragment
|
||||||
|
android:id="@+id/driverManagerFragment"
|
||||||
|
android:name="org.yuzu.yuzu_emu.fragments.DriverManagerFragment"
|
||||||
|
android:label="DriverManagerFragment" />
|
||||||
|
|
||||||
</navigation>
|
</navigation>
|
||||||
|
@ -168,9 +168,7 @@
|
|||||||
<string name="select_gpu_driver_title">Möchtest du deinen aktuellen GPU-Treiber ersetzen?</string>
|
<string name="select_gpu_driver_title">Möchtest du deinen aktuellen GPU-Treiber ersetzen?</string>
|
||||||
<string name="select_gpu_driver_install">Installieren</string>
|
<string name="select_gpu_driver_install">Installieren</string>
|
||||||
<string name="select_gpu_driver_default">Standard</string>
|
<string name="select_gpu_driver_default">Standard</string>
|
||||||
<string name="select_gpu_driver_install_success">%s wurde installiert</string>
|
|
||||||
<string name="select_gpu_driver_use_default">Standard GPU-Treiber wird verwendet</string>
|
<string name="select_gpu_driver_use_default">Standard GPU-Treiber wird verwendet</string>
|
||||||
<string name="select_gpu_driver_error">Ungültiger Treiber ausgewählt, Standard-Treiber wird verwendet!</string>
|
|
||||||
<string name="system_gpu_driver">System GPU-Treiber</string>
|
<string name="system_gpu_driver">System GPU-Treiber</string>
|
||||||
<string name="installing_driver">Treiber wird installiert...</string>
|
<string name="installing_driver">Treiber wird installiert...</string>
|
||||||
|
|
||||||
|
@ -171,9 +171,7 @@
|
|||||||
<string name="select_gpu_driver_title">¿Quiere reemplazar el driver de GPU actual?</string>
|
<string name="select_gpu_driver_title">¿Quiere reemplazar el driver de GPU actual?</string>
|
||||||
<string name="select_gpu_driver_install">Instalar</string>
|
<string name="select_gpu_driver_install">Instalar</string>
|
||||||
<string name="select_gpu_driver_default">Predeterminado</string>
|
<string name="select_gpu_driver_default">Predeterminado</string>
|
||||||
<string name="select_gpu_driver_install_success">Instalado %s</string>
|
|
||||||
<string name="select_gpu_driver_use_default">Usando el driver de GPU por defecto </string>
|
<string name="select_gpu_driver_use_default">Usando el driver de GPU por defecto </string>
|
||||||
<string name="select_gpu_driver_error">¡Driver no válido, utilizando el predeterminado del sistema!</string>
|
|
||||||
<string name="system_gpu_driver">Driver GPU del sistema</string>
|
<string name="system_gpu_driver">Driver GPU del sistema</string>
|
||||||
<string name="installing_driver">Instalando driver...</string>
|
<string name="installing_driver">Instalando driver...</string>
|
||||||
|
|
||||||
|
@ -171,9 +171,7 @@
|
|||||||
<string name="select_gpu_driver_title">Souhaitez vous remplacer votre pilote actuel ?</string>
|
<string name="select_gpu_driver_title">Souhaitez vous remplacer votre pilote actuel ?</string>
|
||||||
<string name="select_gpu_driver_install">Installer</string>
|
<string name="select_gpu_driver_install">Installer</string>
|
||||||
<string name="select_gpu_driver_default">Défaut</string>
|
<string name="select_gpu_driver_default">Défaut</string>
|
||||||
<string name="select_gpu_driver_install_success">%s Installé</string>
|
|
||||||
<string name="select_gpu_driver_use_default">Utilisation du pilote de GPU par défaut</string>
|
<string name="select_gpu_driver_use_default">Utilisation du pilote de GPU par défaut</string>
|
||||||
<string name="select_gpu_driver_error">Pilote non valide sélectionné, utilisation du paramètre par défaut du système !</string>
|
|
||||||
<string name="system_gpu_driver">Pilote du GPU du système</string>
|
<string name="system_gpu_driver">Pilote du GPU du système</string>
|
||||||
<string name="installing_driver">Installation du pilote...</string>
|
<string name="installing_driver">Installation du pilote...</string>
|
||||||
|
|
||||||
|
@ -171,9 +171,7 @@
|
|||||||
<string name="select_gpu_driver_title">Vuoi sostituire il driver della tua GPU attuale?</string>
|
<string name="select_gpu_driver_title">Vuoi sostituire il driver della tua GPU attuale?</string>
|
||||||
<string name="select_gpu_driver_install">Installa</string>
|
<string name="select_gpu_driver_install">Installa</string>
|
||||||
<string name="select_gpu_driver_default">Predefinito</string>
|
<string name="select_gpu_driver_default">Predefinito</string>
|
||||||
<string name="select_gpu_driver_install_success">Installato%s</string>
|
|
||||||
<string name="select_gpu_driver_use_default">Utilizza il driver predefinito della GPU.</string>
|
<string name="select_gpu_driver_use_default">Utilizza il driver predefinito della GPU.</string>
|
||||||
<string name="select_gpu_driver_error">Il driver selezionato è invalido, è in utilizzo quello predefinito di sistema!</string>
|
|
||||||
<string name="system_gpu_driver">Driver GPU del sistema</string>
|
<string name="system_gpu_driver">Driver GPU del sistema</string>
|
||||||
<string name="installing_driver">Installando i driver...</string>
|
<string name="installing_driver">Installando i driver...</string>
|
||||||
|
|
||||||
|
@ -170,9 +170,7 @@
|
|||||||
<string name="select_gpu_driver_title">現在のGPUドライバーを置き換えますか?</string>
|
<string name="select_gpu_driver_title">現在のGPUドライバーを置き換えますか?</string>
|
||||||
<string name="select_gpu_driver_install">インストール</string>
|
<string name="select_gpu_driver_install">インストール</string>
|
||||||
<string name="select_gpu_driver_default">デフォルト</string>
|
<string name="select_gpu_driver_default">デフォルト</string>
|
||||||
<string name="select_gpu_driver_install_success">%s をインストールしました</string>
|
|
||||||
<string name="select_gpu_driver_use_default">デフォルトのGPUドライバーを使用します</string>
|
<string name="select_gpu_driver_use_default">デフォルトのGPUドライバーを使用します</string>
|
||||||
<string name="select_gpu_driver_error">選択されたドライバが無効なため、システムのデフォルトを使用します!</string>
|
|
||||||
<string name="system_gpu_driver">システムのGPUドライバ</string>
|
<string name="system_gpu_driver">システムのGPUドライバ</string>
|
||||||
<string name="installing_driver">インストール中…</string>
|
<string name="installing_driver">インストール中…</string>
|
||||||
|
|
||||||
|
@ -171,9 +171,7 @@
|
|||||||
<string name="select_gpu_driver_title">현재 사용 중인 GPU 드라이버를 교체하겠습니까?</string>
|
<string name="select_gpu_driver_title">현재 사용 중인 GPU 드라이버를 교체하겠습니까?</string>
|
||||||
<string name="select_gpu_driver_install">설치</string>
|
<string name="select_gpu_driver_install">설치</string>
|
||||||
<string name="select_gpu_driver_default">기본값</string>
|
<string name="select_gpu_driver_default">기본값</string>
|
||||||
<string name="select_gpu_driver_install_success">설치된 %s</string>
|
|
||||||
<string name="select_gpu_driver_use_default">기본 GPU 드라이버 사용</string>
|
<string name="select_gpu_driver_use_default">기본 GPU 드라이버 사용</string>
|
||||||
<string name="select_gpu_driver_error">시스템 기본값을 사용하여 잘못된 드라이버를 선택했습니다!</string>
|
|
||||||
<string name="system_gpu_driver">시스템 GPU 드라이버</string>
|
<string name="system_gpu_driver">시스템 GPU 드라이버</string>
|
||||||
<string name="installing_driver">드라이버 설치 중...</string>
|
<string name="installing_driver">드라이버 설치 중...</string>
|
||||||
|
|
||||||
|
@ -171,9 +171,7 @@
|
|||||||
<string name="select_gpu_driver_title">Ønsker du å bytte ut din nåværende GPU-driver?</string>
|
<string name="select_gpu_driver_title">Ønsker du å bytte ut din nåværende GPU-driver?</string>
|
||||||
<string name="select_gpu_driver_install">Installer</string>
|
<string name="select_gpu_driver_install">Installer</string>
|
||||||
<string name="select_gpu_driver_default">Standard</string>
|
<string name="select_gpu_driver_default">Standard</string>
|
||||||
<string name="select_gpu_driver_install_success">Installert %s</string>
|
|
||||||
<string name="select_gpu_driver_use_default">Bruk av standard GPU-driver</string>
|
<string name="select_gpu_driver_use_default">Bruk av standard GPU-driver</string>
|
||||||
<string name="select_gpu_driver_error">Ugyldig driver valgt, bruker systemstandard!</string>
|
|
||||||
<string name="system_gpu_driver">Systemets GPU-driver</string>
|
<string name="system_gpu_driver">Systemets GPU-driver</string>
|
||||||
<string name="installing_driver">Installerer driver...</string>
|
<string name="installing_driver">Installerer driver...</string>
|
||||||
|
|
||||||
|
@ -171,9 +171,7 @@
|
|||||||
<string name="select_gpu_driver_title">Chcesz zastąpić obecny sterownik układu graficznego?</string>
|
<string name="select_gpu_driver_title">Chcesz zastąpić obecny sterownik układu graficznego?</string>
|
||||||
<string name="select_gpu_driver_install">Zainstaluj</string>
|
<string name="select_gpu_driver_install">Zainstaluj</string>
|
||||||
<string name="select_gpu_driver_default">Domyślne</string>
|
<string name="select_gpu_driver_default">Domyślne</string>
|
||||||
<string name="select_gpu_driver_install_success">Zainstalowano %s</string>
|
|
||||||
<string name="select_gpu_driver_use_default">Aktywny domyślny sterownik GPU</string>
|
<string name="select_gpu_driver_use_default">Aktywny domyślny sterownik GPU</string>
|
||||||
<string name="select_gpu_driver_error">Wybrano błędny sterownik, powrót do domyślnego. </string>
|
|
||||||
<string name="system_gpu_driver">Systemowy sterownik GPU</string>
|
<string name="system_gpu_driver">Systemowy sterownik GPU</string>
|
||||||
<string name="installing_driver">Instalowanie sterownika...</string>
|
<string name="installing_driver">Instalowanie sterownika...</string>
|
||||||
|
|
||||||
|
@ -171,9 +171,7 @@
|
|||||||
<string name="select_gpu_driver_title">Queres substituir o driver do GPU atual? </string>
|
<string name="select_gpu_driver_title">Queres substituir o driver do GPU atual? </string>
|
||||||
<string name="select_gpu_driver_install">Instalar</string>
|
<string name="select_gpu_driver_install">Instalar</string>
|
||||||
<string name="select_gpu_driver_default">Padrão</string>
|
<string name="select_gpu_driver_default">Padrão</string>
|
||||||
<string name="select_gpu_driver_install_success">Instalado%s</string>
|
|
||||||
<string name="select_gpu_driver_use_default">Usar o driver padrão do GPU</string>
|
<string name="select_gpu_driver_use_default">Usar o driver padrão do GPU</string>
|
||||||
<string name="select_gpu_driver_error">Driver selecionado inválido, a usar o padrão do sistema!</string>
|
|
||||||
<string name="system_gpu_driver">Driver do GPU padrão</string>
|
<string name="system_gpu_driver">Driver do GPU padrão</string>
|
||||||
<string name="installing_driver">A instalar o Driver...</string>
|
<string name="installing_driver">A instalar o Driver...</string>
|
||||||
|
|
||||||
|
@ -171,9 +171,7 @@
|
|||||||
<string name="select_gpu_driver_title">Queres substituir o driver do GPU atual? </string>
|
<string name="select_gpu_driver_title">Queres substituir o driver do GPU atual? </string>
|
||||||
<string name="select_gpu_driver_install">Instalar</string>
|
<string name="select_gpu_driver_install">Instalar</string>
|
||||||
<string name="select_gpu_driver_default">Padrão</string>
|
<string name="select_gpu_driver_default">Padrão</string>
|
||||||
<string name="select_gpu_driver_install_success">Instalado%s</string>
|
|
||||||
<string name="select_gpu_driver_use_default">Usar o driver padrão do GPU</string>
|
<string name="select_gpu_driver_use_default">Usar o driver padrão do GPU</string>
|
||||||
<string name="select_gpu_driver_error">Driver selecionado inválido, a usar o padrão do sistema!</string>
|
|
||||||
<string name="system_gpu_driver">Driver do GPU padrão</string>
|
<string name="system_gpu_driver">Driver do GPU padrão</string>
|
||||||
<string name="installing_driver">A instalar o Driver...</string>
|
<string name="installing_driver">A instalar o Driver...</string>
|
||||||
|
|
||||||
|
@ -171,9 +171,7 @@
|
|||||||
<string name="select_gpu_driver_title">Хотите заменить текущий драйвер ГП?</string>
|
<string name="select_gpu_driver_title">Хотите заменить текущий драйвер ГП?</string>
|
||||||
<string name="select_gpu_driver_install">Установить</string>
|
<string name="select_gpu_driver_install">Установить</string>
|
||||||
<string name="select_gpu_driver_default">По умолчанию</string>
|
<string name="select_gpu_driver_default">По умолчанию</string>
|
||||||
<string name="select_gpu_driver_install_success">Установлено %s</string>
|
|
||||||
<string name="select_gpu_driver_use_default">Используется стандартный драйвер ГП </string>
|
<string name="select_gpu_driver_use_default">Используется стандартный драйвер ГП </string>
|
||||||
<string name="select_gpu_driver_error">Выбран неверный драйвер, используется стандартный системный!</string>
|
|
||||||
<string name="system_gpu_driver">Системный драйвер ГП</string>
|
<string name="system_gpu_driver">Системный драйвер ГП</string>
|
||||||
<string name="installing_driver">Установка драйвера...</string>
|
<string name="installing_driver">Установка драйвера...</string>
|
||||||
|
|
||||||
|
@ -171,9 +171,7 @@
|
|||||||
<string name="select_gpu_driver_title">Хочете замінити поточний драйвер ГП?</string>
|
<string name="select_gpu_driver_title">Хочете замінити поточний драйвер ГП?</string>
|
||||||
<string name="select_gpu_driver_install">Встановити</string>
|
<string name="select_gpu_driver_install">Встановити</string>
|
||||||
<string name="select_gpu_driver_default">За замовчуванням</string>
|
<string name="select_gpu_driver_default">За замовчуванням</string>
|
||||||
<string name="select_gpu_driver_install_success">Встановлено %s</string>
|
|
||||||
<string name="select_gpu_driver_use_default">Використовується стандартний драйвер ГП</string>
|
<string name="select_gpu_driver_use_default">Використовується стандартний драйвер ГП</string>
|
||||||
<string name="select_gpu_driver_error">Обрано неправильний драйвер, використовується стандартний системний!</string>
|
|
||||||
<string name="system_gpu_driver">Системний драйвер ГП</string>
|
<string name="system_gpu_driver">Системний драйвер ГП</string>
|
||||||
<string name="installing_driver">Встановлення драйвера...</string>
|
<string name="installing_driver">Встановлення драйвера...</string>
|
||||||
|
|
||||||
|
@ -171,9 +171,7 @@
|
|||||||
<string name="select_gpu_driver_title">要取代您当前的 GPU 驱动程序吗?</string>
|
<string name="select_gpu_driver_title">要取代您当前的 GPU 驱动程序吗?</string>
|
||||||
<string name="select_gpu_driver_install">安装</string>
|
<string name="select_gpu_driver_install">安装</string>
|
||||||
<string name="select_gpu_driver_default">系统默认</string>
|
<string name="select_gpu_driver_default">系统默认</string>
|
||||||
<string name="select_gpu_driver_install_success">已安装 %s</string>
|
|
||||||
<string name="select_gpu_driver_use_default">使用默认 GPU 驱动程序</string>
|
<string name="select_gpu_driver_use_default">使用默认 GPU 驱动程序</string>
|
||||||
<string name="select_gpu_driver_error">选择的驱动程序无效,将使用系统默认的驱动程序!</string>
|
|
||||||
<string name="system_gpu_driver">系统 GPU 驱动程序</string>
|
<string name="system_gpu_driver">系统 GPU 驱动程序</string>
|
||||||
<string name="installing_driver">正在安装驱动程序…</string>
|
<string name="installing_driver">正在安装驱动程序…</string>
|
||||||
|
|
||||||
|
@ -171,9 +171,7 @@
|
|||||||
<string name="select_gpu_driver_title">要取代您目前的 GPU 驅動程式嗎?</string>
|
<string name="select_gpu_driver_title">要取代您目前的 GPU 驅動程式嗎?</string>
|
||||||
<string name="select_gpu_driver_install">安裝</string>
|
<string name="select_gpu_driver_install">安裝</string>
|
||||||
<string name="select_gpu_driver_default">預設</string>
|
<string name="select_gpu_driver_default">預設</string>
|
||||||
<string name="select_gpu_driver_install_success">已安裝 %s</string>
|
|
||||||
<string name="select_gpu_driver_use_default">使用預設 GPU 驅動程式</string>
|
<string name="select_gpu_driver_use_default">使用預設 GPU 驅動程式</string>
|
||||||
<string name="select_gpu_driver_error">選取的驅動程式無效,將使用系統預設驅動程式!</string>
|
|
||||||
<string name="system_gpu_driver">系統 GPU 驅動程式</string>
|
<string name="system_gpu_driver">系統 GPU 驅動程式</string>
|
||||||
<string name="installing_driver">正在安裝驅動程式…</string>
|
<string name="installing_driver">正在安裝驅動程式…</string>
|
||||||
|
|
||||||
|
@ -13,6 +13,8 @@
|
|||||||
<dimen name="menu_width">256dp</dimen>
|
<dimen name="menu_width">256dp</dimen>
|
||||||
<dimen name="card_width">165dp</dimen>
|
<dimen name="card_width">165dp</dimen>
|
||||||
<dimen name="icon_inset">24dp</dimen>
|
<dimen name="icon_inset">24dp</dimen>
|
||||||
|
<dimen name="spacing_bottom_list_fab">72dp</dimen>
|
||||||
|
<dimen name="spacing_fab">24dp</dimen>
|
||||||
|
|
||||||
<dimen name="dialog_margin">20dp</dimen>
|
<dimen name="dialog_margin">20dp</dimen>
|
||||||
<dimen name="elevated_app_bar">3dp</dimen>
|
<dimen name="elevated_app_bar">3dp</dimen>
|
||||||
|
@ -72,6 +72,7 @@
|
|||||||
<string name="invalid_keys_error">Invalid encryption keys</string>
|
<string name="invalid_keys_error">Invalid encryption keys</string>
|
||||||
<string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string>
|
<string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string>
|
||||||
<string name="install_keys_failure_description">The selected file is incorrect or corrupt. Please redump your keys.</string>
|
<string name="install_keys_failure_description">The selected file is incorrect or corrupt. Please redump your keys.</string>
|
||||||
|
<string name="gpu_driver_manager">GPU Driver Manager</string>
|
||||||
<string name="install_gpu_driver">Install GPU driver</string>
|
<string name="install_gpu_driver">Install GPU driver</string>
|
||||||
<string name="install_gpu_driver_description">Install alternative drivers for potentially better performance or accuracy</string>
|
<string name="install_gpu_driver_description">Install alternative drivers for potentially better performance or accuracy</string>
|
||||||
<string name="advanced_settings">Advanced settings</string>
|
<string name="advanced_settings">Advanced settings</string>
|
||||||
@ -234,15 +235,17 @@
|
|||||||
<string name="export_failed">Export failed</string>
|
<string name="export_failed">Export failed</string>
|
||||||
<string name="import_failed">Import failed</string>
|
<string name="import_failed">Import failed</string>
|
||||||
<string name="cancelling">Cancelling</string>
|
<string name="cancelling">Cancelling</string>
|
||||||
|
<string name="install">Install</string>
|
||||||
|
<string name="delete">Delete</string>
|
||||||
|
|
||||||
<!-- GPU driver installation -->
|
<!-- GPU driver installation -->
|
||||||
<string name="select_gpu_driver">Select GPU driver</string>
|
<string name="select_gpu_driver">Select GPU driver</string>
|
||||||
<string name="select_gpu_driver_title">Would you like to replace your current GPU driver?</string>
|
<string name="select_gpu_driver_title">Would you like to replace your current GPU driver?</string>
|
||||||
<string name="select_gpu_driver_install">Install</string>
|
<string name="select_gpu_driver_install">Install</string>
|
||||||
<string name="select_gpu_driver_default">Default</string>
|
<string name="select_gpu_driver_default">Default</string>
|
||||||
<string name="select_gpu_driver_install_success">Installed %s</string>
|
|
||||||
<string name="select_gpu_driver_use_default">Using default GPU driver</string>
|
<string name="select_gpu_driver_use_default">Using default GPU driver</string>
|
||||||
<string name="select_gpu_driver_error">Invalid driver selected, using system default!</string>
|
<string name="select_gpu_driver_error">Invalid driver selected</string>
|
||||||
|
<string name="driver_already_installed">Driver already installed</string>
|
||||||
<string name="system_gpu_driver">System GPU driver</string>
|
<string name="system_gpu_driver">System GPU driver</string>
|
||||||
<string name="installing_driver">Installing driver…</string>
|
<string name="installing_driver">Installing driver…</string>
|
||||||
|
|
||||||
|
@ -3,8 +3,8 @@
|
|||||||
|
|
||||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
plugins {
|
plugins {
|
||||||
id("com.android.application") version "8.0.2" apply false
|
id("com.android.application") version "8.1.2" apply false
|
||||||
id("com.android.library") version "8.0.2" apply false
|
id("com.android.library") version "8.1.2" apply false
|
||||||
id("org.jetbrains.kotlin.android") version "1.8.21" apply false
|
id("org.jetbrains.kotlin.android") version "1.8.21" apply false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,6 +77,7 @@ void AudioRenderer::Wait() {
|
|||||||
"{}, got {}",
|
"{}, got {}",
|
||||||
Message::RenderResponse, msg);
|
Message::RenderResponse, msg);
|
||||||
}
|
}
|
||||||
|
PostDSPClearCommandBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioRenderer::Send(Direction dir, u32 message) {
|
void AudioRenderer::Send(Direction dir, u32 message) {
|
||||||
@ -96,6 +97,14 @@ void AudioRenderer::SetCommandBuffer(s32 session_id, CpuAddr buffer, u64 size, u
|
|||||||
command_buffers[session_id].reset_buffer = reset;
|
command_buffers[session_id].reset_buffer = reset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AudioRenderer::PostDSPClearCommandBuffer() noexcept {
|
||||||
|
for (auto& buffer : command_buffers) {
|
||||||
|
buffer.buffer = 0;
|
||||||
|
buffer.size = 0;
|
||||||
|
buffer.reset_buffer = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
u32 AudioRenderer::GetRemainCommandCount(s32 session_id) const noexcept {
|
u32 AudioRenderer::GetRemainCommandCount(s32 session_id) const noexcept {
|
||||||
return command_buffers[session_id].remaining_command_count;
|
return command_buffers[session_id].remaining_command_count;
|
||||||
}
|
}
|
||||||
|
@ -85,6 +85,8 @@ private:
|
|||||||
*/
|
*/
|
||||||
void CreateSinkStreams();
|
void CreateSinkStreams();
|
||||||
|
|
||||||
|
void PostDSPClearCommandBuffer() noexcept;
|
||||||
|
|
||||||
/// Core system
|
/// Core system
|
||||||
Core::System& system;
|
Core::System& system;
|
||||||
/// The output sink the AudioRenderer will send samples to
|
/// The output sink the AudioRenderer will send samples to
|
||||||
|
@ -204,6 +204,10 @@ void SinkStream::ProcessAudioOutAndRender(std::span<s16> output_buffer, std::siz
|
|||||||
// paused and we'll desync, so just play silence.
|
// paused and we'll desync, so just play silence.
|
||||||
if (system.IsPaused() || system.IsShuttingDown()) {
|
if (system.IsPaused() || system.IsShuttingDown()) {
|
||||||
if (system.IsShuttingDown()) {
|
if (system.IsShuttingDown()) {
|
||||||
|
{
|
||||||
|
std::scoped_lock lk{release_mutex};
|
||||||
|
queued_buffers.store(0);
|
||||||
|
}
|
||||||
release_cv.notify_one();
|
release_cv.notify_one();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,6 +120,8 @@ add_library(common STATIC
|
|||||||
socket_types.h
|
socket_types.h
|
||||||
spin_lock.cpp
|
spin_lock.cpp
|
||||||
spin_lock.h
|
spin_lock.h
|
||||||
|
stb.cpp
|
||||||
|
stb.h
|
||||||
steady_clock.cpp
|
steady_clock.cpp
|
||||||
steady_clock.h
|
steady_clock.h
|
||||||
stream.cpp
|
stream.cpp
|
||||||
@ -189,6 +191,14 @@ if(ARCHITECTURE_x86_64)
|
|||||||
target_link_libraries(common PRIVATE xbyak::xbyak)
|
target_link_libraries(common PRIVATE xbyak::xbyak)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if (ARCHITECTURE_arm64 AND (ANDROID OR LINUX))
|
||||||
|
target_sources(common
|
||||||
|
PRIVATE
|
||||||
|
arm64/native_clock.cpp
|
||||||
|
arm64/native_clock.h
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
if (MSVC)
|
if (MSVC)
|
||||||
target_compile_definitions(common PRIVATE
|
target_compile_definitions(common PRIVATE
|
||||||
# The standard library doesn't provide any replacement for codecvt yet
|
# The standard library doesn't provide any replacement for codecvt yet
|
||||||
@ -200,6 +210,8 @@ if (MSVC)
|
|||||||
/we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data
|
/we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data
|
||||||
/we4800 # Implicit conversion from 'type' to bool. Possible information loss
|
/we4800 # Implicit conversion from 'type' to bool. Possible information loss
|
||||||
)
|
)
|
||||||
|
else()
|
||||||
|
set_source_files_properties(stb.cpp PROPERTIES COMPILE_OPTIONS "-Wno-implicit-fallthrough;-Wno-missing-declarations;-Wno-missing-field-initializers")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||||
@ -215,7 +227,7 @@ endif()
|
|||||||
|
|
||||||
create_target_directory_groups(common)
|
create_target_directory_groups(common)
|
||||||
|
|
||||||
target_link_libraries(common PUBLIC Boost::context Boost::headers fmt::fmt microprofile Threads::Threads)
|
target_link_libraries(common PUBLIC Boost::context Boost::headers fmt::fmt microprofile stb::headers Threads::Threads)
|
||||||
target_link_libraries(common PRIVATE lz4::lz4 zstd::zstd LLVM::Demangle)
|
target_link_libraries(common PRIVATE lz4::lz4 zstd::zstd LLVM::Demangle)
|
||||||
|
|
||||||
if (ANDROID)
|
if (ANDROID)
|
||||||
|
72
src/common/arm64/native_clock.cpp
Normal file
72
src/common/arm64/native_clock.cpp
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include "common/arm64/native_clock.h"
|
||||||
|
|
||||||
|
namespace Common::Arm64 {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
NativeClock::FactorType GetFixedPointFactor(u64 num, u64 den) {
|
||||||
|
return (static_cast<NativeClock::FactorType>(num) << 64) / den;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 MultiplyHigh(u64 m, NativeClock::FactorType factor) {
|
||||||
|
return static_cast<u64>((m * factor) >> 64);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
NativeClock::NativeClock() {
|
||||||
|
const u64 host_cntfrq = GetHostCNTFRQ();
|
||||||
|
ns_cntfrq_factor = GetFixedPointFactor(NsRatio::den, host_cntfrq);
|
||||||
|
us_cntfrq_factor = GetFixedPointFactor(UsRatio::den, host_cntfrq);
|
||||||
|
ms_cntfrq_factor = GetFixedPointFactor(MsRatio::den, host_cntfrq);
|
||||||
|
guest_cntfrq_factor = GetFixedPointFactor(CNTFRQ, host_cntfrq);
|
||||||
|
gputick_cntfrq_factor = GetFixedPointFactor(GPUTickFreq, host_cntfrq);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::chrono::nanoseconds NativeClock::GetTimeNS() const {
|
||||||
|
return std::chrono::nanoseconds{MultiplyHigh(GetHostTicksElapsed(), ns_cntfrq_factor)};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::chrono::microseconds NativeClock::GetTimeUS() const {
|
||||||
|
return std::chrono::microseconds{MultiplyHigh(GetHostTicksElapsed(), us_cntfrq_factor)};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::chrono::milliseconds NativeClock::GetTimeMS() const {
|
||||||
|
return std::chrono::milliseconds{MultiplyHigh(GetHostTicksElapsed(), ms_cntfrq_factor)};
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 NativeClock::GetCNTPCT() const {
|
||||||
|
return MultiplyHigh(GetHostTicksElapsed(), guest_cntfrq_factor);
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 NativeClock::GetGPUTick() const {
|
||||||
|
return MultiplyHigh(GetHostTicksElapsed(), gputick_cntfrq_factor);
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 NativeClock::GetHostTicksNow() const {
|
||||||
|
u64 cntvct_el0 = 0;
|
||||||
|
asm volatile("dsb ish\n\t"
|
||||||
|
"mrs %[cntvct_el0], cntvct_el0\n\t"
|
||||||
|
"dsb ish\n\t"
|
||||||
|
: [cntvct_el0] "=r"(cntvct_el0));
|
||||||
|
return cntvct_el0;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 NativeClock::GetHostTicksElapsed() const {
|
||||||
|
return GetHostTicksNow();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NativeClock::IsNative() const {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 NativeClock::GetHostCNTFRQ() {
|
||||||
|
u64 cntfrq_el0 = 0;
|
||||||
|
asm("mrs %[cntfrq_el0], cntfrq_el0" : [cntfrq_el0] "=r"(cntfrq_el0));
|
||||||
|
return cntfrq_el0;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Common::Arm64
|
47
src/common/arm64/native_clock.h
Normal file
47
src/common/arm64/native_clock.h
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common/wall_clock.h"
|
||||||
|
|
||||||
|
namespace Common::Arm64 {
|
||||||
|
|
||||||
|
class NativeClock final : public WallClock {
|
||||||
|
public:
|
||||||
|
explicit NativeClock();
|
||||||
|
|
||||||
|
std::chrono::nanoseconds GetTimeNS() const override;
|
||||||
|
|
||||||
|
std::chrono::microseconds GetTimeUS() const override;
|
||||||
|
|
||||||
|
std::chrono::milliseconds GetTimeMS() const override;
|
||||||
|
|
||||||
|
u64 GetCNTPCT() const override;
|
||||||
|
|
||||||
|
u64 GetGPUTick() const override;
|
||||||
|
|
||||||
|
u64 GetHostTicksNow() const override;
|
||||||
|
|
||||||
|
u64 GetHostTicksElapsed() const override;
|
||||||
|
|
||||||
|
bool IsNative() const override;
|
||||||
|
|
||||||
|
static u64 GetHostCNTFRQ();
|
||||||
|
|
||||||
|
public:
|
||||||
|
using FactorType = unsigned __int128;
|
||||||
|
|
||||||
|
FactorType GetGuestCNTFRQFactor() const {
|
||||||
|
return guest_cntfrq_factor;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
FactorType ns_cntfrq_factor;
|
||||||
|
FactorType us_cntfrq_factor;
|
||||||
|
FactorType ms_cntfrq_factor;
|
||||||
|
FactorType guest_cntfrq_factor;
|
||||||
|
FactorType gputick_cntfrq_factor;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Common::Arm64
|
@ -39,8 +39,12 @@
|
|||||||
#define Crash() exit(1)
|
#define Crash() exit(1)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define LTO_NOINLINE __attribute__((noinline))
|
||||||
|
|
||||||
#else // _MSC_VER
|
#else // _MSC_VER
|
||||||
|
|
||||||
|
#define LTO_NOINLINE
|
||||||
|
|
||||||
// Locale Cross-Compatibility
|
// Locale Cross-Compatibility
|
||||||
#define locale_t _locale_t
|
#define locale_t _locale_t
|
||||||
|
|
||||||
|
@ -211,6 +211,11 @@ struct Elf64_Rela {
|
|||||||
Elf64_Sxword r_addend; /* Addend */
|
Elf64_Sxword r_addend; /* Addend */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* RELR relocation table entry */
|
||||||
|
|
||||||
|
using Elf32_Relr = Elf32_Word;
|
||||||
|
using Elf64_Relr = Elf64_Xword;
|
||||||
|
|
||||||
/* How to extract and insert information held in the r_info field. */
|
/* How to extract and insert information held in the r_info field. */
|
||||||
|
|
||||||
static inline u32 Elf32RelSymIndex(Elf32_Word r_info) {
|
static inline u32 Elf32RelSymIndex(Elf32_Word r_info) {
|
||||||
@ -328,6 +333,9 @@ constexpr u32 ElfDtFiniArray = 26; /* Array with addresses of fini fct */
|
|||||||
constexpr u32 ElfDtInitArraySz = 27; /* Size in bytes of DT_INIT_ARRAY */
|
constexpr u32 ElfDtInitArraySz = 27; /* Size in bytes of DT_INIT_ARRAY */
|
||||||
constexpr u32 ElfDtFiniArraySz = 28; /* Size in bytes of DT_FINI_ARRAY */
|
constexpr u32 ElfDtFiniArraySz = 28; /* Size in bytes of DT_FINI_ARRAY */
|
||||||
constexpr u32 ElfDtSymtabShndx = 34; /* Address of SYMTAB_SHNDX section */
|
constexpr u32 ElfDtSymtabShndx = 34; /* Address of SYMTAB_SHNDX section */
|
||||||
|
constexpr u32 ElfDtRelrsz = 35; /* Size of RELR relative relocations */
|
||||||
|
constexpr u32 ElfDtRelr = 36; /* Address of RELR relative relocations */
|
||||||
|
constexpr u32 ElfDtRelrent = 37; /* Size of one RELR relative relocation */
|
||||||
|
|
||||||
} // namespace ELF
|
} // namespace ELF
|
||||||
} // namespace Common
|
} // namespace Common
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include "common/fs/fs_android.h"
|
#include "common/fs/fs_android.h"
|
||||||
|
#include "common/string_util.h"
|
||||||
|
|
||||||
namespace Common::FS::Android {
|
namespace Common::FS::Android {
|
||||||
|
|
||||||
@ -28,28 +29,35 @@ void RegisterCallbacks(JNIEnv* env, jclass clazz) {
|
|||||||
env->GetJavaVM(&g_jvm);
|
env->GetJavaVM(&g_jvm);
|
||||||
native_library = clazz;
|
native_library = clazz;
|
||||||
|
|
||||||
|
#define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) \
|
||||||
|
F(JMethodID, JMethodName, Signature)
|
||||||
#define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) \
|
#define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) \
|
||||||
F(JMethodID, JMethodName, Signature)
|
F(JMethodID, JMethodName, Signature)
|
||||||
#define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) \
|
#define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) \
|
||||||
F(JMethodID, JMethodName, Signature)
|
F(JMethodID, JMethodName, Signature)
|
||||||
#define F(JMethodID, JMethodName, Signature) \
|
#define F(JMethodID, JMethodName, Signature) \
|
||||||
JMethodID = env->GetStaticMethodID(native_library, JMethodName, Signature);
|
JMethodID = env->GetStaticMethodID(native_library, JMethodName, Signature);
|
||||||
|
ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH)
|
||||||
ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
|
ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
|
||||||
ANDROID_STORAGE_FUNCTIONS(FS)
|
ANDROID_STORAGE_FUNCTIONS(FS)
|
||||||
#undef F
|
#undef F
|
||||||
#undef FS
|
#undef FS
|
||||||
#undef FR
|
#undef FR
|
||||||
|
#undef FH
|
||||||
}
|
}
|
||||||
|
|
||||||
void UnRegisterCallbacks() {
|
void UnRegisterCallbacks() {
|
||||||
|
#define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) F(JMethodID)
|
||||||
#define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) F(JMethodID)
|
#define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) F(JMethodID)
|
||||||
#define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) F(JMethodID)
|
#define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) F(JMethodID)
|
||||||
#define F(JMethodID) JMethodID = nullptr;
|
#define F(JMethodID) JMethodID = nullptr;
|
||||||
|
ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH)
|
||||||
ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
|
ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
|
||||||
ANDROID_STORAGE_FUNCTIONS(FS)
|
ANDROID_STORAGE_FUNCTIONS(FS)
|
||||||
#undef F
|
#undef F
|
||||||
#undef FS
|
#undef FS
|
||||||
#undef FR
|
#undef FR
|
||||||
|
#undef FH
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsContentUri(const std::string& path) {
|
bool IsContentUri(const std::string& path) {
|
||||||
@ -95,4 +103,29 @@ ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
|
|||||||
#undef F
|
#undef F
|
||||||
#undef FR
|
#undef FR
|
||||||
|
|
||||||
|
#define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) \
|
||||||
|
F(FunctionName, JMethodID, Caller)
|
||||||
|
#define F(FunctionName, JMethodID, Caller) \
|
||||||
|
std::string FunctionName(const std::string& filepath) { \
|
||||||
|
if (JMethodID == nullptr) { \
|
||||||
|
return 0; \
|
||||||
|
} \
|
||||||
|
auto env = GetEnvForThread(); \
|
||||||
|
jstring j_filepath = env->NewStringUTF(filepath.c_str()); \
|
||||||
|
jstring j_return = \
|
||||||
|
static_cast<jstring>(env->Caller(native_library, JMethodID, j_filepath)); \
|
||||||
|
if (!j_return) { \
|
||||||
|
return {}; \
|
||||||
|
} \
|
||||||
|
const jchar* jchars = env->GetStringChars(j_return, nullptr); \
|
||||||
|
const jsize length = env->GetStringLength(j_return); \
|
||||||
|
const std::u16string_view string_view(reinterpret_cast<const char16_t*>(jchars), length); \
|
||||||
|
const std::string converted_string = Common::UTF16ToUTF8(string_view); \
|
||||||
|
env->ReleaseStringChars(j_return, jchars); \
|
||||||
|
return converted_string; \
|
||||||
|
}
|
||||||
|
ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH)
|
||||||
|
#undef F
|
||||||
|
#undef FH
|
||||||
|
|
||||||
} // namespace Common::FS::Android
|
} // namespace Common::FS::Android
|
||||||
|
@ -17,19 +17,28 @@
|
|||||||
"(Ljava/lang/String;)Z") \
|
"(Ljava/lang/String;)Z") \
|
||||||
V(Exists, bool, file_exists, CallStaticBooleanMethod, "exists", "(Ljava/lang/String;)Z")
|
V(Exists, bool, file_exists, CallStaticBooleanMethod, "exists", "(Ljava/lang/String;)Z")
|
||||||
|
|
||||||
|
#define ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(V) \
|
||||||
|
V(GetParentDirectory, get_parent_directory, CallStaticObjectMethod, "getParentDirectory", \
|
||||||
|
"(Ljava/lang/String;)Ljava/lang/String;") \
|
||||||
|
V(GetFilename, get_filename, CallStaticObjectMethod, "getFilename", \
|
||||||
|
"(Ljava/lang/String;)Ljava/lang/String;")
|
||||||
|
|
||||||
namespace Common::FS::Android {
|
namespace Common::FS::Android {
|
||||||
|
|
||||||
static JavaVM* g_jvm = nullptr;
|
static JavaVM* g_jvm = nullptr;
|
||||||
static jclass native_library = nullptr;
|
static jclass native_library = nullptr;
|
||||||
|
|
||||||
|
#define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) F(JMethodID)
|
||||||
#define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) F(JMethodID)
|
#define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) F(JMethodID)
|
||||||
#define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) F(JMethodID)
|
#define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) F(JMethodID)
|
||||||
#define F(JMethodID) static jmethodID JMethodID = nullptr;
|
#define F(JMethodID) static jmethodID JMethodID = nullptr;
|
||||||
|
ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH)
|
||||||
ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
|
ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
|
||||||
ANDROID_STORAGE_FUNCTIONS(FS)
|
ANDROID_STORAGE_FUNCTIONS(FS)
|
||||||
#undef F
|
#undef F
|
||||||
#undef FS
|
#undef FS
|
||||||
#undef FR
|
#undef FR
|
||||||
|
#undef FH
|
||||||
|
|
||||||
enum class OpenMode {
|
enum class OpenMode {
|
||||||
Read,
|
Read,
|
||||||
@ -62,4 +71,10 @@ ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
|
|||||||
#undef F
|
#undef F
|
||||||
#undef FR
|
#undef FR
|
||||||
|
|
||||||
|
#define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) F(FunctionName)
|
||||||
|
#define F(FunctionName) std::string FunctionName(const std::string& filepath);
|
||||||
|
ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH)
|
||||||
|
#undef F
|
||||||
|
#undef FH
|
||||||
|
|
||||||
} // namespace Common::FS::Android
|
} // namespace Common::FS::Android
|
||||||
|
@ -13,15 +13,18 @@
|
|||||||
#define AMIIBO_DIR "amiibo"
|
#define AMIIBO_DIR "amiibo"
|
||||||
#define CACHE_DIR "cache"
|
#define CACHE_DIR "cache"
|
||||||
#define CONFIG_DIR "config"
|
#define CONFIG_DIR "config"
|
||||||
|
#define CRASH_DUMPS_DIR "crash_dumps"
|
||||||
#define DUMP_DIR "dump"
|
#define DUMP_DIR "dump"
|
||||||
#define KEYS_DIR "keys"
|
#define KEYS_DIR "keys"
|
||||||
#define LOAD_DIR "load"
|
#define LOAD_DIR "load"
|
||||||
#define LOG_DIR "log"
|
#define LOG_DIR "log"
|
||||||
#define NAND_DIR "nand"
|
#define NAND_DIR "nand"
|
||||||
|
#define PLAY_TIME_DIR "play_time"
|
||||||
#define SCREENSHOTS_DIR "screenshots"
|
#define SCREENSHOTS_DIR "screenshots"
|
||||||
#define SDMC_DIR "sdmc"
|
#define SDMC_DIR "sdmc"
|
||||||
#define SHADER_DIR "shader"
|
#define SHADER_DIR "shader"
|
||||||
#define TAS_DIR "tas"
|
#define TAS_DIR "tas"
|
||||||
|
#define ICONS_DIR "icons"
|
||||||
|
|
||||||
// yuzu-specific files
|
// yuzu-specific files
|
||||||
|
|
||||||
|
@ -119,15 +119,18 @@ public:
|
|||||||
GenerateYuzuPath(YuzuPath::AmiiboDir, yuzu_path / AMIIBO_DIR);
|
GenerateYuzuPath(YuzuPath::AmiiboDir, yuzu_path / AMIIBO_DIR);
|
||||||
GenerateYuzuPath(YuzuPath::CacheDir, yuzu_path_cache);
|
GenerateYuzuPath(YuzuPath::CacheDir, yuzu_path_cache);
|
||||||
GenerateYuzuPath(YuzuPath::ConfigDir, yuzu_path_config);
|
GenerateYuzuPath(YuzuPath::ConfigDir, yuzu_path_config);
|
||||||
|
GenerateYuzuPath(YuzuPath::CrashDumpsDir, yuzu_path / CRASH_DUMPS_DIR);
|
||||||
GenerateYuzuPath(YuzuPath::DumpDir, yuzu_path / DUMP_DIR);
|
GenerateYuzuPath(YuzuPath::DumpDir, yuzu_path / DUMP_DIR);
|
||||||
GenerateYuzuPath(YuzuPath::KeysDir, yuzu_path / KEYS_DIR);
|
GenerateYuzuPath(YuzuPath::KeysDir, yuzu_path / KEYS_DIR);
|
||||||
GenerateYuzuPath(YuzuPath::LoadDir, yuzu_path / LOAD_DIR);
|
GenerateYuzuPath(YuzuPath::LoadDir, yuzu_path / LOAD_DIR);
|
||||||
GenerateYuzuPath(YuzuPath::LogDir, yuzu_path / LOG_DIR);
|
GenerateYuzuPath(YuzuPath::LogDir, yuzu_path / LOG_DIR);
|
||||||
GenerateYuzuPath(YuzuPath::NANDDir, yuzu_path / NAND_DIR);
|
GenerateYuzuPath(YuzuPath::NANDDir, yuzu_path / NAND_DIR);
|
||||||
|
GenerateYuzuPath(YuzuPath::PlayTimeDir, yuzu_path / PLAY_TIME_DIR);
|
||||||
GenerateYuzuPath(YuzuPath::ScreenshotsDir, yuzu_path / SCREENSHOTS_DIR);
|
GenerateYuzuPath(YuzuPath::ScreenshotsDir, yuzu_path / SCREENSHOTS_DIR);
|
||||||
GenerateYuzuPath(YuzuPath::SDMCDir, yuzu_path / SDMC_DIR);
|
GenerateYuzuPath(YuzuPath::SDMCDir, yuzu_path / SDMC_DIR);
|
||||||
GenerateYuzuPath(YuzuPath::ShaderDir, yuzu_path / SHADER_DIR);
|
GenerateYuzuPath(YuzuPath::ShaderDir, yuzu_path / SHADER_DIR);
|
||||||
GenerateYuzuPath(YuzuPath::TASDir, yuzu_path / TAS_DIR);
|
GenerateYuzuPath(YuzuPath::TASDir, yuzu_path / TAS_DIR);
|
||||||
|
GenerateYuzuPath(YuzuPath::IconsDir, yuzu_path / ICONS_DIR);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -398,6 +401,16 @@ std::string SanitizePath(std::string_view path_, DirectorySeparator directory_se
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::string_view GetParentPath(std::string_view path) {
|
std::string_view GetParentPath(std::string_view path) {
|
||||||
|
if (path.empty()) {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef ANDROID
|
||||||
|
if (path[0] != '/') {
|
||||||
|
std::string path_string{path};
|
||||||
|
return FS::Android::GetParentDirectory(path_string);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
const auto name_bck_index = path.rfind('\\');
|
const auto name_bck_index = path.rfind('\\');
|
||||||
const auto name_fwd_index = path.rfind('/');
|
const auto name_fwd_index = path.rfind('/');
|
||||||
std::size_t name_index;
|
std::size_t name_index;
|
||||||
|
@ -15,15 +15,18 @@ enum class YuzuPath {
|
|||||||
AmiiboDir, // Where Amiibo backups are stored.
|
AmiiboDir, // Where Amiibo backups are stored.
|
||||||
CacheDir, // Where cached filesystem data is stored.
|
CacheDir, // Where cached filesystem data is stored.
|
||||||
ConfigDir, // Where config files are stored.
|
ConfigDir, // Where config files are stored.
|
||||||
|
CrashDumpsDir, // Where crash dumps are stored.
|
||||||
DumpDir, // Where dumped data is stored.
|
DumpDir, // Where dumped data is stored.
|
||||||
KeysDir, // Where key files are stored.
|
KeysDir, // Where key files are stored.
|
||||||
LoadDir, // Where cheat/mod files are stored.
|
LoadDir, // Where cheat/mod files are stored.
|
||||||
LogDir, // Where log files are stored.
|
LogDir, // Where log files are stored.
|
||||||
NANDDir, // Where the emulated NAND is stored.
|
NANDDir, // Where the emulated NAND is stored.
|
||||||
|
PlayTimeDir, // Where play time data is stored.
|
||||||
ScreenshotsDir, // Where yuzu screenshots are stored.
|
ScreenshotsDir, // Where yuzu screenshots are stored.
|
||||||
SDMCDir, // Where the emulated SDMC is stored.
|
SDMCDir, // Where the emulated SDMC is stored.
|
||||||
ShaderDir, // Where shaders are stored.
|
ShaderDir, // Where shaders are stored.
|
||||||
TASDir, // Where TAS scripts are stored.
|
TASDir, // Where TAS scripts are stored.
|
||||||
|
IconsDir, // Where Icons for Windows shortcuts are stored.
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -25,6 +25,7 @@ void ConfigureNvidiaEnvironmentFlags() {
|
|||||||
|
|
||||||
void(_putenv(fmt::format("__GL_SHADER_DISK_CACHE_PATH={}", windows_path_string).c_str()));
|
void(_putenv(fmt::format("__GL_SHADER_DISK_CACHE_PATH={}", windows_path_string).c_str()));
|
||||||
void(_putenv("__GL_SHADER_DISK_CACHE_SKIP_CLEANUP=1"));
|
void(_putenv("__GL_SHADER_DISK_CACHE_SKIP_CLEANUP=1"));
|
||||||
|
void(_putenv("__GL_THREADED_OPTIMIZATIONS=1"));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user