Compare commits
184 Commits
android-92
...
android-11
Author | SHA1 | Date | |
---|---|---|---|
47a0339fdf | |||
67bc22bb11 | |||
43be2bfe33 | |||
79ba5d9c26 | |||
008d7e8c5f | |||
6eb3a583cb | |||
e0834ee50b | |||
9274eaecd0 | |||
c733620024 | |||
897b411ae7 | |||
94836ba3b1 | |||
b1909b0435 | |||
1cc764988f | |||
da5c49f22d | |||
6b93b0b08c | |||
3d4a064674 | |||
cfe73af6f2 | |||
77fb9d415b | |||
bbdaa62175 | |||
db37e583ff | |||
d28e826e47 | |||
13beb85514 | |||
4b06bcc82c | |||
12ebc8d9d1 | |||
2b85e9e997 | |||
59b62c6507 | |||
2e760a9833 | |||
bab4a13a41 | |||
b56c7397ad | |||
249db0a59b | |||
9526ce95dd | |||
687158fe00 | |||
d8507332c1 | |||
67e983a354 | |||
f21058a6c0 | |||
b456af31e6 | |||
0441853d0f | |||
60a1c6b95b | |||
794e6c7a96 | |||
22afa2c7a3 | |||
85a89ca3e3 | |||
26776c0e60 | |||
e02ee8e59d | |||
134ecca9b0 | |||
c5f1ec8040 | |||
765ea9b79d | |||
c5bdc0054c | |||
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 | |||
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 |
@ -5,6 +5,6 @@
|
||||
|
||||
GITDATE="`git show -s --date=short --format='%ad' | sed 's/-//g'`"
|
||||
GITREV="`git show -s --format='%h'`"
|
||||
ARTIFACTS_DIR="artifacts"
|
||||
ARTIFACTS_DIR="$PWD/artifacts"
|
||||
|
||||
mkdir -p "${ARTIFACTS_DIR}/"
|
||||
|
@ -11,7 +11,7 @@ ccache -s
|
||||
mkdir build || true && cd build
|
||||
cmake .. \
|
||||
-DBoost_USE_STATIC_LIBS=ON \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-DCMAKE_CXX_FLAGS="-march=x86-64-v2" \
|
||||
-DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ \
|
||||
-DCMAKE_C_COMPILER=/usr/lib/ccache/gcc \
|
||||
@ -31,6 +31,19 @@ ccache -s
|
||||
|
||||
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
|
||||
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"
|
||||
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
|
||||
|
@ -3,4 +3,4 @@
|
||||
|
||||
[codespell]
|
||||
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
|
||||
|
@ -147,3 +147,7 @@ License: GPL-3.0-or-later
|
||||
Files: src/android/gradle/wrapper/*
|
||||
Copyright: 2023 yuzu Emulator Project
|
||||
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(CMakeDependentOption)
|
||||
include(CTest)
|
||||
include(FetchContent)
|
||||
|
||||
# Set bundled sdl2/qt as dependent options.
|
||||
# OFF by default, but if ENABLE_SDL2 and MSVC are true then ON
|
||||
@ -99,47 +98,8 @@ if (ANDROID AND YUZU_DOWNLOAD_ANDROID_VVL)
|
||||
DESTINATION "${vvl_lib_path}")
|
||||
endif()
|
||||
|
||||
# On Android, fetch and compile libcxx before doing anything else
|
||||
if (ANDROID)
|
||||
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()
|
||||
|
||||
if (YUZU_USE_BUNDLED_VCPKG)
|
||||
@ -329,11 +289,12 @@ find_package(Boost 1.79.0 REQUIRED context)
|
||||
find_package(enet 1.3 MODULE)
|
||||
find_package(fmt 9 REQUIRED)
|
||||
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(nlohmann_json 3.8 REQUIRED)
|
||||
find_package(Opus 1.3 MODULE)
|
||||
find_package(RenderDoc MODULE)
|
||||
find_package(stb MODULE)
|
||||
find_package(VulkanMemoryAllocator CONFIG)
|
||||
find_package(ZLIB 1.2 REQUIRED)
|
||||
find_package(zstd 1.5 REQUIRED)
|
||||
@ -400,6 +361,9 @@ function(set_yuzu_qt_components)
|
||||
if (ENABLE_QT_TRANSLATION)
|
||||
list(APPEND YUZU_QT_COMPONENTS2 LinguistTools)
|
||||
endif()
|
||||
if (USE_DISCORD_PRESENCE)
|
||||
list(APPEND YUZU_QT_COMPONENTS2 Network)
|
||||
endif()
|
||||
set(YUZU_QT_COMPONENTS ${YUZU_QT_COMPONENTS2} PARENT_SCOPE)
|
||||
endfunction(set_yuzu_qt_components)
|
||||
|
||||
|
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,12 @@
|
||||
| Pull Request | Commit | Title | Author | Merged? |
|
||||
|----|----|----|----|----|
|
||||
| [11827](https://github.com/yuzu-emu/yuzu//pull/11827) | [`689f346e9`](https://github.com/yuzu-emu/yuzu//pull/11827/files) | nvnflinger: fix reporting and freeing of preallocated buffers | [liamwhite](https://github.com/liamwhite/) | Yes |
|
||||
|
||||
|
||||
End of merge log. You can find the original README.md below the break.
|
||||
|
||||
-----
|
||||
|
||||
<!--
|
||||
SPDX-FileCopyrightText: 2018 yuzu Emulator Project
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
4
dist/qt_themes/default/style.qss
vendored
4
dist/qt_themes/default/style.qss
vendored
@ -120,6 +120,10 @@ QWidget#connectedControllers {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
QWidget#closeButtons {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
QWidget#playersSupported,
|
||||
QWidget#controllersSupported,
|
||||
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;
|
||||
}
|
||||
|
||||
QWidget#closeButtons {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
QWidget#playersSupported,
|
||||
QWidget#controllersSupported,
|
||||
QWidget#controllerSupported1,
|
||||
|
@ -2305,6 +2305,10 @@ QWidget#connectedControllers {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
QWidget#closeButtons {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
QWidget#playersSupported,
|
||||
QWidget#controllersSupported,
|
||||
QWidget#controllerSupported1,
|
||||
|
4
externals/CMakeLists.txt
vendored
4
externals/CMakeLists.txt
vendored
@ -171,6 +171,10 @@ endif()
|
||||
add_library(stb stb/stb_dxt.cpp)
|
||||
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)
|
||||
target_include_directories(bc_decoder PUBLIC ./bc_decoder)
|
||||
|
||||
|
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)
|
||||
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_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
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
@ -27,7 +27,7 @@ android {
|
||||
namespace = "org.yuzu.yuzu_emu"
|
||||
|
||||
compileSdkVersion = "android-34"
|
||||
ndkVersion = "25.2.9519653"
|
||||
ndkVersion = "26.1.10909125"
|
||||
|
||||
buildFeatures {
|
||||
viewBinding = true
|
||||
@ -203,23 +203,23 @@ ktlint {
|
||||
}
|
||||
|
||||
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.recyclerview:recyclerview:1.3.0")
|
||||
implementation("androidx.recyclerview:recyclerview:1.3.1")
|
||||
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("com.google.android.material:material:1.9.0")
|
||||
implementation("androidx.preference:preference:1.2.0")
|
||||
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1")
|
||||
implementation("androidx.preference:preference-ktx:1.2.1")
|
||||
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2")
|
||||
implementation("io.coil-kt:coil:2.2.2")
|
||||
implementation("androidx.core:core-splashscreen:1.0.1")
|
||||
implementation("androidx.window:window:1.2.0-beta03")
|
||||
implementation("org.ini4j:ini4j:0.5.4")
|
||||
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
|
||||
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
|
||||
implementation("androidx.navigation:navigation-fragment-ktx:2.6.0")
|
||||
implementation("androidx.navigation:navigation-ui-ktx:2.6.0")
|
||||
implementation("androidx.navigation:navigation-fragment-ktx:2.7.4")
|
||||
implementation("androidx.navigation:navigation-ui-ktx:2.7.4")
|
||||
implementation("info.debatty:java-string-similarity:2.0.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:localeConfig="@xml/locales_config"
|
||||
android:banner="@drawable/tv_banner"
|
||||
android:extractNativeLibs="true"
|
||||
android:fullBackupContent="@xml/data_extraction_rules"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules_api_31"
|
||||
android:enableOnBackInvokedCallback="true">
|
||||
|
@ -15,13 +15,9 @@ import androidx.annotation.Keep
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
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.utils.DocumentsTree.Companion.isNativePath
|
||||
import org.yuzu.yuzu_emu.utils.FileUtil.exists
|
||||
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.FileUtil
|
||||
import org.yuzu.yuzu_emu.utils.Log
|
||||
import org.yuzu.yuzu_emu.utils.SerializableHelper.serializable
|
||||
|
||||
@ -75,7 +71,7 @@ object NativeLibrary {
|
||||
return if (isNativePath(path!!)) {
|
||||
YuzuApplication.documentsTree!!.openContentUri(path, openmode)
|
||||
} else {
|
||||
openContentUri(appContext, path, openmode)
|
||||
FileUtil.openContentUri(path, openmode)
|
||||
}
|
||||
}
|
||||
|
||||
@ -85,7 +81,7 @@ object NativeLibrary {
|
||||
return if (isNativePath(path!!)) {
|
||||
YuzuApplication.documentsTree!!.getFileSize(path)
|
||||
} else {
|
||||
getFileSize(appContext, path)
|
||||
FileUtil.getFileSize(path)
|
||||
}
|
||||
}
|
||||
|
||||
@ -95,7 +91,7 @@ object NativeLibrary {
|
||||
return if (isNativePath(path!!)) {
|
||||
YuzuApplication.documentsTree!!.exists(path)
|
||||
} else {
|
||||
exists(appContext, path)
|
||||
FileUtil.exists(path)
|
||||
}
|
||||
}
|
||||
|
||||
@ -105,7 +101,7 @@ object NativeLibrary {
|
||||
return if (isNativePath(path!!)) {
|
||||
YuzuApplication.documentsTree!!.isDirectory(path)
|
||||
} else {
|
||||
isDirectory(appContext, path)
|
||||
FileUtil.isDirectory(path)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -47,7 +47,7 @@ class YuzuApplication : Application() {
|
||||
application = this
|
||||
documentsTree = DocumentsTree()
|
||||
DirectoryInitialization.start()
|
||||
GpuDriverHelper.initializeDriverParameters(applicationContext)
|
||||
GpuDriverHelper.initializeDriverParameters()
|
||||
NativeLibrary.logDeviceInfo()
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
@ -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"
|
||||
}
|
||||
}
|
@ -39,6 +39,7 @@ import androidx.window.layout.WindowLayoutInfo
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.slider.Slider
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
import org.yuzu.yuzu_emu.HomeNavigationDirections
|
||||
@ -50,6 +51,7 @@ import org.yuzu.yuzu_emu.databinding.DialogOverlayAdjustBinding
|
||||
import org.yuzu.yuzu_emu.databinding.FragmentEmulationBinding
|
||||
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
|
||||
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.EmulationViewModel
|
||||
import org.yuzu.yuzu_emu.overlay.InputOverlay
|
||||
@ -70,6 +72,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
private lateinit var game: Game
|
||||
|
||||
private val emulationViewModel: EmulationViewModel by activityViewModels()
|
||||
private val driverViewModel: DriverViewModel by activityViewModels()
|
||||
|
||||
private var isInFoldableLayout = false
|
||||
|
||||
@ -299,6 +302,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 +350,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
if (!DirectoryInitialization.areDirectoriesReady) {
|
||||
DirectoryInitialization.start()
|
||||
}
|
||||
|
||||
updateScreenLayout()
|
||||
|
||||
emulationState.run(emulationActivity!!.isActivityRecreated)
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
if (emulationState.isRunning && emulationActivity?.isInPictureInPictureMode != true) {
|
||||
emulationState.pause()
|
||||
|
@ -5,7 +5,6 @@ package org.yuzu.yuzu_emu.fragments
|
||||
|
||||
import android.Manifest
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.DialogInterface
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Bundle
|
||||
@ -28,7 +27,6 @@ import androidx.fragment.app.activityViewModels
|
||||
import androidx.navigation.findNavController
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.transition.MaterialSharedAxis
|
||||
import org.yuzu.yuzu_emu.BuildConfig
|
||||
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.features.DocumentProvider
|
||||
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.HomeViewModel
|
||||
import org.yuzu.yuzu_emu.ui.main.MainActivity
|
||||
@ -50,6 +49,7 @@ class HomeSettingsFragment : Fragment() {
|
||||
private lateinit var mainActivity: MainActivity
|
||||
|
||||
private val homeViewModel: HomeViewModel by activityViewModels()
|
||||
private val driverViewModel: DriverViewModel by activityViewModels()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
@ -107,13 +107,17 @@ class HomeSettingsFragment : Fragment() {
|
||||
)
|
||||
add(
|
||||
HomeSetting(
|
||||
R.string.install_gpu_driver,
|
||||
R.string.gpu_driver_manager,
|
||||
R.string.install_gpu_driver_description,
|
||||
R.drawable.ic_exit,
|
||||
{ driverInstaller() },
|
||||
R.drawable.ic_build,
|
||||
{
|
||||
binding.root.findNavController()
|
||||
.navigate(R.id.action_homeSettingsFragment_to_driverManagerFragment)
|
||||
},
|
||||
{ GpuDriverHelper.supportsCustomDriverLoading() },
|
||||
R.string.custom_driver_not_supported,
|
||||
R.string.custom_driver_not_supported_description
|
||||
R.string.custom_driver_not_supported_description,
|
||||
driverViewModel.selectedDriverMetadata
|
||||
)
|
||||
)
|
||||
add(
|
||||
@ -292,31 +296,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() {
|
||||
val file = DocumentFile.fromSingleUri(
|
||||
mainActivity,
|
||||
|
@ -10,8 +10,8 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
@ -78,6 +78,10 @@ class IndeterminateProgressDialogFragment : DialogFragment() {
|
||||
requireActivity().supportFragmentManager,
|
||||
MessageDialogFragment.TAG
|
||||
)
|
||||
|
||||
else -> {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
taskViewModel.clear()
|
||||
}
|
||||
@ -115,7 +119,7 @@ class IndeterminateProgressDialogFragment : DialogFragment() {
|
||||
private const val CANCELLABLE = "Cancellable"
|
||||
|
||||
fun newInstance(
|
||||
activity: AppCompatActivity,
|
||||
activity: FragmentActivity,
|
||||
titleId: Int,
|
||||
cancellable: Boolean = false,
|
||||
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)
|
||||
}
|
||||
}
|
@ -29,12 +29,10 @@ import androidx.navigation.fragment.NavHostFragment
|
||||
import androidx.navigation.ui.setupWithNavController
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.google.android.material.color.MaterialColors
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.navigation.NavigationBarView
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import java.io.File
|
||||
import java.io.FilenameFilter
|
||||
import java.io.IOException
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
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.activities.EmulationActivity
|
||||
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.settings.model.Settings
|
||||
import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment
|
||||
@ -343,11 +340,10 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||
|
||||
val dstPath = DirectoryInitialization.userDirectory + "/keys/"
|
||||
if (FileUtil.copyUriToInternalStorage(
|
||||
applicationContext,
|
||||
result,
|
||||
dstPath,
|
||||
"prod.keys"
|
||||
)
|
||||
) != null
|
||||
) {
|
||||
if (NativeLibrary.reloadKeys()) {
|
||||
Toast.makeText(
|
||||
@ -446,11 +442,10 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||
|
||||
val dstPath = DirectoryInitialization.userDirectory + "/keys/"
|
||||
if (FileUtil.copyUriToInternalStorage(
|
||||
applicationContext,
|
||||
result,
|
||||
dstPath,
|
||||
"key_retail.bin"
|
||||
)
|
||||
) != null
|
||||
) {
|
||||
if (NativeLibrary.reloadKeys()) {
|
||||
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(
|
||||
ActivityResultContracts.OpenMultipleDocuments()
|
||||
) { documents: List<Uri> ->
|
||||
|
@ -7,7 +7,6 @@ import android.net.Uri
|
||||
import androidx.documentfile.provider.DocumentFile
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
import org.yuzu.yuzu_emu.YuzuApplication
|
||||
import org.yuzu.yuzu_emu.model.MinimalDocumentFile
|
||||
|
||||
class DocumentsTree {
|
||||
@ -22,7 +21,7 @@ class DocumentsTree {
|
||||
|
||||
fun openContentUri(filepath: String, openMode: String?): Int {
|
||||
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 {
|
||||
@ -30,7 +29,7 @@ class DocumentsTree {
|
||||
return if (node == null || node.isDirectory) {
|
||||
0
|
||||
} else {
|
||||
FileUtil.getFileSize(YuzuApplication.appContext, node.uri.toString())
|
||||
FileUtil.getFileSize(node.uri.toString())
|
||||
}
|
||||
}
|
||||
|
||||
@ -67,7 +66,7 @@ class DocumentsTree {
|
||||
* @param parent parent node of this level
|
||||
*/
|
||||
private fun structTree(parent: DocumentsNode) {
|
||||
val documents = FileUtil.listFiles(YuzuApplication.appContext, parent.uri!!)
|
||||
val documents = FileUtil.listFiles(parent.uri!!)
|
||||
for (document in documents) {
|
||||
val node = DocumentsNode(document)
|
||||
node.parent = parent
|
||||
|
@ -3,7 +3,6 @@
|
||||
|
||||
package org.yuzu.yuzu_emu.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.database.Cursor
|
||||
import android.net.Uri
|
||||
import android.provider.DocumentsContract
|
||||
@ -11,7 +10,6 @@ import androidx.documentfile.provider.DocumentFile
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import java.io.BufferedInputStream
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
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.TaskState
|
||||
import java.io.BufferedOutputStream
|
||||
import java.lang.NullPointerException
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.util.zip.ZipOutputStream
|
||||
|
||||
object FileUtil {
|
||||
@ -29,6 +29,8 @@ object FileUtil {
|
||||
const val APPLICATION_OCTET_STREAM = "application/octet-stream"
|
||||
const val TEXT_PLAIN = "text/plain"
|
||||
|
||||
private val context get() = YuzuApplication.appContext
|
||||
|
||||
/**
|
||||
* Create a file from directory with filename.
|
||||
* @param context Application context
|
||||
@ -36,11 +38,11 @@ object FileUtil {
|
||||
* @param filename file display name.
|
||||
* @return boolean
|
||||
*/
|
||||
fun createFile(context: Context?, directory: String?, filename: String): DocumentFile? {
|
||||
fun createFile(directory: String?, filename: String): DocumentFile? {
|
||||
var decodedFilename = filename
|
||||
try {
|
||||
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)
|
||||
var mimeType = APPLICATION_OCTET_STREAM
|
||||
if (decodedFilename.endsWith(".txt")) {
|
||||
@ -56,16 +58,15 @@ object FileUtil {
|
||||
|
||||
/**
|
||||
* Create a directory from directory with filename.
|
||||
* @param context Application context
|
||||
* @param directory parent path for directory.
|
||||
* @param directoryName directory display name.
|
||||
* @return boolean
|
||||
*/
|
||||
fun createDir(context: Context?, directory: String?, directoryName: String?): DocumentFile? {
|
||||
fun createDir(directory: String?, directoryName: String?): DocumentFile? {
|
||||
var decodedDirectoryName = directoryName
|
||||
try {
|
||||
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)
|
||||
val isExist = parent.findFile(decodedDirectoryName)
|
||||
return isExist ?: parent.createDirectory(decodedDirectoryName)
|
||||
@ -77,13 +78,12 @@ object FileUtil {
|
||||
|
||||
/**
|
||||
* Open content uri and return file descriptor to JNI.
|
||||
* @param context Application context
|
||||
* @param path Native content uri path
|
||||
* @param openMode will be one of "r", "r", "rw", "wa", "rwa"
|
||||
* @return file descriptor
|
||||
*/
|
||||
@JvmStatic
|
||||
fun openContentUri(context: Context, path: String, openMode: String?): Int {
|
||||
fun openContentUri(path: String, openMode: String?): Int {
|
||||
try {
|
||||
val uri = Uri.parse(path)
|
||||
val parcelFileDescriptor = context.contentResolver.openFileDescriptor(uri, openMode!!)
|
||||
@ -103,11 +103,10 @@ object FileUtil {
|
||||
/**
|
||||
* Reference: https://stackoverflow.com/questions/42186820/documentfile-is-very-slow
|
||||
* This function will be faster than DoucmentFile.listFiles
|
||||
* @param context Application context
|
||||
* @param uri Directory uri.
|
||||
* @return CheapDocument lists.
|
||||
*/
|
||||
fun listFiles(context: Context, uri: Uri): Array<MinimalDocumentFile> {
|
||||
fun listFiles(uri: Uri): Array<MinimalDocumentFile> {
|
||||
val resolver = context.contentResolver
|
||||
val columns = arrayOf(
|
||||
DocumentsContract.Document.COLUMN_DOCUMENT_ID,
|
||||
@ -145,7 +144,7 @@ object FileUtil {
|
||||
* @param path Native content uri path
|
||||
* @return bool
|
||||
*/
|
||||
fun exists(context: Context, path: String?): Boolean {
|
||||
fun exists(path: String?): Boolean {
|
||||
var c: Cursor? = null
|
||||
try {
|
||||
val mUri = Uri.parse(path)
|
||||
@ -165,7 +164,7 @@ object FileUtil {
|
||||
* @param path content uri path
|
||||
* @return bool
|
||||
*/
|
||||
fun isDirectory(context: Context, path: String): Boolean {
|
||||
fun isDirectory(path: String): Boolean {
|
||||
val resolver = context.contentResolver
|
||||
val columns = arrayOf(
|
||||
DocumentsContract.Document.COLUMN_MIME_TYPE
|
||||
@ -210,10 +209,10 @@ object FileUtil {
|
||||
return filename
|
||||
}
|
||||
|
||||
fun getFilesName(context: Context, path: String): Array<String> {
|
||||
fun getFilesName(path: String): Array<String> {
|
||||
val uri = Uri.parse(path)
|
||||
val files: MutableList<String> = ArrayList()
|
||||
for (file in listFiles(context, uri)) {
|
||||
for (file in listFiles(uri)) {
|
||||
files.add(file.filename)
|
||||
}
|
||||
return files.toTypedArray()
|
||||
@ -225,7 +224,7 @@ object FileUtil {
|
||||
* @return long file size
|
||||
*/
|
||||
@JvmStatic
|
||||
fun getFileSize(context: Context, path: String): Long {
|
||||
fun getFileSize(path: String): Long {
|
||||
val resolver = context.contentResolver
|
||||
val columns = arrayOf(
|
||||
DocumentsContract.Document.COLUMN_SIZE
|
||||
@ -245,44 +244,38 @@ object FileUtil {
|
||||
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(
|
||||
context: Context,
|
||||
sourceUri: Uri?,
|
||||
sourceUri: Uri,
|
||||
destinationParentPath: String,
|
||||
destinationFilename: String
|
||||
): Boolean {
|
||||
var input: InputStream? = null
|
||||
var output: FileOutputStream? = null
|
||||
destinationFilename: String = ""
|
||||
): File? =
|
||||
try {
|
||||
input = context.contentResolver.openInputStream(sourceUri!!)
|
||||
output = FileOutputStream("$destinationParentPath/$destinationFilename")
|
||||
val buffer = ByteArray(1024)
|
||||
var len: Int
|
||||
while (input!!.read(buffer).also { len = it } != -1) {
|
||||
output.write(buffer, 0, len)
|
||||
val fileName =
|
||||
if (destinationFilename == "") getFilename(sourceUri) else "/$destinationFilename"
|
||||
val inputStream = context.contentResolver.openInputStream(sourceUri)!!
|
||||
|
||||
val destinationFile = File("$destinationParentPath$fileName")
|
||||
if (destinationFile.exists()) {
|
||||
destinationFile.delete()
|
||||
}
|
||||
output.flush()
|
||||
return true
|
||||
} catch (e: Exception) {
|
||||
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.outputStream().use { fos ->
|
||||
inputStream.use { it.copyTo(fos) }
|
||||
}
|
||||
destinationFile
|
||||
} catch (e: IOException) {
|
||||
null
|
||||
} catch (e: NullPointerException) {
|
||||
null
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the given zip file into the given directory.
|
||||
@ -368,4 +361,12 @@ object FileUtil {
|
||||
return fileName.substring(fileName.lastIndexOf(".") + 1)
|
||||
.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.
|
||||
NativeLibrary.reloadKeys()
|
||||
|
||||
addGamesRecursive(games, FileUtil.listFiles(context, gamesUri), 3)
|
||||
addGamesRecursive(games, FileUtil.listFiles(gamesUri), 3)
|
||||
|
||||
// Cache list of games found on disk
|
||||
val serializedGames = mutableSetOf<String>()
|
||||
@ -58,7 +58,7 @@ object GameHelper {
|
||||
if (it.isDirectory) {
|
||||
addGamesRecursive(
|
||||
games,
|
||||
FileUtil.listFiles(YuzuApplication.appContext, it.uri),
|
||||
FileUtil.listFiles(it.uri),
|
||||
depth - 1
|
||||
)
|
||||
} else {
|
||||
|
@ -3,64 +3,33 @@
|
||||
|
||||
package org.yuzu.yuzu_emu.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import java.io.BufferedInputStream
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.io.FileOutputStream
|
||||
import java.io.IOException
|
||||
import java.util.zip.ZipInputStream
|
||||
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 {
|
||||
private const val META_JSON_FILENAME = "meta.json"
|
||||
private const val DRIVER_INTERNAL_FILENAME = "gpu_driver.zip"
|
||||
private var fileRedirectionPath: String? = null
|
||||
private var driverInstallationPath: String? = null
|
||||
var driverInstallationPath: String? = null
|
||||
private var hookLibPath: String? = null
|
||||
|
||||
@Throws(IOException::class)
|
||||
private fun unzip(zipFilePath: String, destDir: String) {
|
||||
val dir = File(destDir)
|
||||
val driverStoragePath get() = DirectoryInitialization.userDirectory!! + "/gpu_drivers/"
|
||||
|
||||
// Create output directory if it doesn't exist
|
||||
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) {
|
||||
fun initializeDriverParameters() {
|
||||
try {
|
||||
// Initialize the file redirection directory.
|
||||
fileRedirectionPath =
|
||||
context.getExternalFilesDir(null)!!.canonicalPath + "/gpu/vk_file_redirect/"
|
||||
fileRedirectionPath = YuzuApplication.appContext
|
||||
.getExternalFilesDir(null)!!.canonicalPath + "/gpu/vk_file_redirect/"
|
||||
|
||||
// Initialize the driver installation directory.
|
||||
driverInstallationPath = context.filesDir.canonicalPath + "/gpu_driver/"
|
||||
driverInstallationPath = YuzuApplication.appContext
|
||||
.filesDir.canonicalPath + "/gpu_driver/"
|
||||
} catch (e: IOException) {
|
||||
throw RuntimeException(e)
|
||||
}
|
||||
@ -69,68 +38,169 @@ object GpuDriverHelper {
|
||||
initializeDirectories()
|
||||
|
||||
// Initialize hook libraries directory.
|
||||
hookLibPath = context.applicationInfo.nativeLibraryDir + "/"
|
||||
hookLibPath = YuzuApplication.appContext.applicationInfo.nativeLibraryDir + "/"
|
||||
|
||||
// Initialize GPU driver.
|
||||
NativeLibrary.initializeGpuDriver(
|
||||
hookLibPath,
|
||||
driverInstallationPath,
|
||||
customDriverLibraryName,
|
||||
customDriverData.libraryName,
|
||||
fileRedirectionPath
|
||||
)
|
||||
}
|
||||
|
||||
fun installDefaultDriver(context: Context) {
|
||||
// Removing the installed driver will result in the backend using the default system driver.
|
||||
val driverInstallationDir = File(driverInstallationPath!!)
|
||||
deleteRecursive(driverInstallationDir)
|
||||
initializeDriverParameters(context)
|
||||
fun getDrivers(): MutableList<Pair<String, GpuDriverMetadata>> {
|
||||
val driverZips = File(driverStoragePath).listFiles()
|
||||
val drivers: MutableList<Pair<String, GpuDriverMetadata>> =
|
||||
driverZips
|
||||
?.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.
|
||||
installDefaultDriver(context)
|
||||
installDefaultDriver()
|
||||
|
||||
// Ensure we have directories.
|
||||
initializeDirectories()
|
||||
|
||||
// Copy the zip file URI into our private storage.
|
||||
copyUriToInternalStorage(
|
||||
context,
|
||||
driverPathUri,
|
||||
driverInstallationPath!!,
|
||||
DRIVER_INTERNAL_FILENAME
|
||||
)
|
||||
// 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
|
||||
}
|
||||
|
||||
// Unzip the driver.
|
||||
try {
|
||||
unzip(driverInstallationPath + DRIVER_INTERNAL_FILENAME, driverInstallationPath!!)
|
||||
FileUtil.unzipToInternalStorage(
|
||||
BufferedInputStream(copiedFile.inputStream()),
|
||||
File(driverInstallationPath!!)
|
||||
)
|
||||
} catch (e: SecurityException) {
|
||||
return
|
||||
return false
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
// Parse the custom driver metadata to retrieve the name.
|
||||
val customDriverName: String?
|
||||
get() {
|
||||
val metadata = GpuDriverMetadata(driverInstallationPath + META_JSON_FILENAME)
|
||||
return metadata.name
|
||||
}
|
||||
val customDriverData: GpuDriverMetadata
|
||||
get() = GpuDriverMetadata(File(driverInstallationPath + META_JSON_FILENAME))
|
||||
|
||||
// Parse the custom driver metadata to retrieve the library name.
|
||||
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() {
|
||||
fun initializeDirectories() {
|
||||
// Ensure the file redirection directory exists.
|
||||
val fileRedirectionDir = File(fileRedirectionPath!!)
|
||||
if (!fileRedirectionDir.exists()) {
|
||||
@ -141,14 +211,10 @@ object GpuDriverHelper {
|
||||
if (!driverInstallationDir.exists()) {
|
||||
driverInstallationDir.mkdirs()
|
||||
}
|
||||
}
|
||||
|
||||
private fun deleteRecursive(fileOrDirectory: File) {
|
||||
if (fileOrDirectory.isDirectory) {
|
||||
for (child in fileOrDirectory.listFiles()!!) {
|
||||
deleteRecursive(child)
|
||||
}
|
||||
// Ensure the driver storage directory exists
|
||||
val driverStorageDirectory = File(driverStoragePath)
|
||||
if (!driverStorageDirectory.exists()) {
|
||||
driverStorageDirectory.mkdirs()
|
||||
}
|
||||
fileOrDirectory.delete()
|
||||
}
|
||||
}
|
||||
|
@ -4,29 +4,29 @@
|
||||
package org.yuzu.yuzu_emu.utils
|
||||
|
||||
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.JSONObject
|
||||
import java.io.File
|
||||
import java.io.InputStream
|
||||
|
||||
class GpuDriverMetadata(metadataFilePath: String) {
|
||||
var name: String? = null
|
||||
var description: String? = null
|
||||
var author: String? = null
|
||||
var vendor: String? = null
|
||||
var driverVersion: String? = null
|
||||
var minApi = 0
|
||||
var libraryName: String? = null
|
||||
class GpuDriverMetadata {
|
||||
/**
|
||||
* Tries to get driver metadata information from a meta.json [File]
|
||||
*
|
||||
* @param metadataFile meta.json file provided with a GPU driver
|
||||
*/
|
||||
constructor(metadataFile: File) {
|
||||
if (metadataFile.length() > MAX_META_SIZE_BYTES) {
|
||||
return
|
||||
}
|
||||
|
||||
init {
|
||||
try {
|
||||
val json = JSONObject(getStringFromFile(metadataFilePath))
|
||||
val json = JSONObject(FileUtil.getStringFromFile(metadataFile))
|
||||
name = json.getString("name")
|
||||
description = json.getString("description")
|
||||
author = json.getString("author")
|
||||
vendor = json.getString("vendor")
|
||||
driverVersion = json.getString("driverVersion")
|
||||
version = json.getString("driverVersion")
|
||||
minApi = json.getInt("minApi")
|
||||
libraryName = json.getString("libraryName")
|
||||
} catch (e: JSONException) {
|
||||
@ -36,12 +36,84 @@ class GpuDriverMetadata(metadataFilePath: String) {
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
@Throws(IOException::class)
|
||||
private fun getStringFromFile(filePath: String): String {
|
||||
val path = Paths.get(filePath)
|
||||
val bytes = Files.readAllBytes(path)
|
||||
return String(bytes, StandardCharsets.UTF_8)
|
||||
/**
|
||||
* Tries to get driver metadata information from an input stream that's intended to be
|
||||
* from a zip file
|
||||
*
|
||||
* @param metadataStream ZipEntry input stream
|
||||
* @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
|
||||
}
|
||||
}
|
||||
|
@ -218,7 +218,6 @@ public:
|
||||
return;
|
||||
}
|
||||
m_window->OnSurfaceChanged(m_native_window);
|
||||
m_system.Renderer().NotifySurfaceChanged();
|
||||
}
|
||||
|
||||
void ConfigureFilesystemProvider(const std::string& filepath) {
|
||||
|
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>
|
@ -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
|
||||
android:id="@+id/action_homeSettingsFragment_to_installableFragment"
|
||||
app:destination="@id/installableFragment" />
|
||||
<action
|
||||
android:id="@+id/action_homeSettingsFragment_to_driverManagerFragment"
|
||||
app:destination="@id/driverManagerFragment" />
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
@ -95,5 +98,9 @@
|
||||
android:id="@+id/installableFragment"
|
||||
android:name="org.yuzu.yuzu_emu.fragments.InstallableFragment"
|
||||
android:label="InstallableFragment" />
|
||||
<fragment
|
||||
android:id="@+id/driverManagerFragment"
|
||||
android:name="org.yuzu.yuzu_emu.fragments.DriverManagerFragment"
|
||||
android:label="DriverManagerFragment" />
|
||||
|
||||
</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_install">Installieren</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_error">Ungültiger Treiber ausgewählt, Standard-Treiber wird verwendet!</string>
|
||||
<string name="system_gpu_driver">System GPU-Treiber</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_install">Instalar</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_error">¡Driver no válido, utilizando el predeterminado del sistema!</string>
|
||||
<string name="system_gpu_driver">Driver GPU del sistema</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_install">Installer</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_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="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_install">Installa</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_error">Il driver selezionato è invalido, è in utilizzo quello predefinito di sistema!</string>
|
||||
<string name="system_gpu_driver">Driver GPU del sistema</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_install">インストール</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_error">選択されたドライバが無効なため、システムのデフォルトを使用します!</string>
|
||||
<string name="system_gpu_driver">システムのGPUドライバ</string>
|
||||
<string name="installing_driver">インストール中…</string>
|
||||
|
||||
|
@ -171,9 +171,7 @@
|
||||
<string name="select_gpu_driver_title">현재 사용 중인 GPU 드라이버를 교체하겠습니까?</string>
|
||||
<string name="select_gpu_driver_install">설치</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_error">시스템 기본값을 사용하여 잘못된 드라이버를 선택했습니다!</string>
|
||||
<string name="system_gpu_driver">시스템 GPU 드라이버</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_install">Installer</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_error">Ugyldig driver valgt, bruker systemstandard!</string>
|
||||
<string name="system_gpu_driver">Systemets GPU-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_install">Zainstaluj</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_error">Wybrano błędny sterownik, powrót do domyślnego. </string>
|
||||
<string name="system_gpu_driver">Systemowy sterownik GPU</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_install">Instalar</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_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="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_install">Instalar</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_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="installing_driver">A instalar o Driver...</string>
|
||||
|
||||
|
@ -171,9 +171,7 @@
|
||||
<string name="select_gpu_driver_title">Хотите заменить текущий драйвер ГП?</string>
|
||||
<string name="select_gpu_driver_install">Установить</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_error">Выбран неверный драйвер, используется стандартный системный!</string>
|
||||
<string name="system_gpu_driver">Системный драйвер ГП</string>
|
||||
<string name="installing_driver">Установка драйвера...</string>
|
||||
|
||||
|
@ -171,9 +171,7 @@
|
||||
<string name="select_gpu_driver_title">Хочете замінити поточний драйвер ГП?</string>
|
||||
<string name="select_gpu_driver_install">Встановити</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_error">Обрано неправильний драйвер, використовується стандартний системний!</string>
|
||||
<string name="system_gpu_driver">Системний драйвер ГП</string>
|
||||
<string name="installing_driver">Встановлення драйвера...</string>
|
||||
|
||||
|
@ -171,9 +171,7 @@
|
||||
<string name="select_gpu_driver_title">要取代您当前的 GPU 驱动程序吗?</string>
|
||||
<string name="select_gpu_driver_install">安装</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_error">选择的驱动程序无效,将使用系统默认的驱动程序!</string>
|
||||
<string name="system_gpu_driver">系统 GPU 驱动程序</string>
|
||||
<string name="installing_driver">正在安装驱动程序…</string>
|
||||
|
||||
|
@ -171,9 +171,7 @@
|
||||
<string name="select_gpu_driver_title">要取代您目前的 GPU 驅動程式嗎?</string>
|
||||
<string name="select_gpu_driver_install">安裝</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_error">選取的驅動程式無效,將使用系統預設驅動程式!</string>
|
||||
<string name="system_gpu_driver">系統 GPU 驅動程式</string>
|
||||
<string name="installing_driver">正在安裝驅動程式…</string>
|
||||
|
||||
|
@ -13,6 +13,8 @@
|
||||
<dimen name="menu_width">256dp</dimen>
|
||||
<dimen name="card_width">165dp</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="elevated_app_bar">3dp</dimen>
|
||||
|
@ -72,6 +72,7 @@
|
||||
<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="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_description">Install alternative drivers for potentially better performance or accuracy</string>
|
||||
<string name="advanced_settings">Advanced settings</string>
|
||||
@ -234,15 +235,17 @@
|
||||
<string name="export_failed">Export failed</string>
|
||||
<string name="import_failed">Import failed</string>
|
||||
<string name="cancelling">Cancelling</string>
|
||||
<string name="install">Install</string>
|
||||
<string name="delete">Delete</string>
|
||||
|
||||
<!-- GPU driver installation -->
|
||||
<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_install">Install</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_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="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.
|
||||
plugins {
|
||||
id("com.android.application") version "8.0.2" apply false
|
||||
id("com.android.library") version "8.0.2" apply false
|
||||
id("com.android.application") version "8.1.2" apply false
|
||||
id("com.android.library") version "8.1.2" apply false
|
||||
id("org.jetbrains.kotlin.android") version "1.8.21" apply false
|
||||
}
|
||||
|
||||
|
@ -77,6 +77,7 @@ void AudioRenderer::Wait() {
|
||||
"{}, got {}",
|
||||
Message::RenderResponse, msg);
|
||||
}
|
||||
PostDSPClearCommandBuffer();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 {
|
||||
return command_buffers[session_id].remaining_command_count;
|
||||
}
|
||||
|
@ -85,6 +85,8 @@ private:
|
||||
*/
|
||||
void CreateSinkStreams();
|
||||
|
||||
void PostDSPClearCommandBuffer() noexcept;
|
||||
|
||||
/// Core system
|
||||
Core::System& system;
|
||||
/// 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.
|
||||
if (system.IsPaused() || system.IsShuttingDown()) {
|
||||
if (system.IsShuttingDown()) {
|
||||
{
|
||||
std::scoped_lock lk{release_mutex};
|
||||
queued_buffers.store(0);
|
||||
}
|
||||
release_cv.notify_one();
|
||||
}
|
||||
|
||||
|
@ -120,6 +120,8 @@ add_library(common STATIC
|
||||
socket_types.h
|
||||
spin_lock.cpp
|
||||
spin_lock.h
|
||||
stb.cpp
|
||||
stb.h
|
||||
steady_clock.cpp
|
||||
steady_clock.h
|
||||
stream.cpp
|
||||
@ -189,6 +191,14 @@ if(ARCHITECTURE_x86_64)
|
||||
target_link_libraries(common PRIVATE xbyak::xbyak)
|
||||
endif()
|
||||
|
||||
if (ARCHITECTURE_arm64 AND (ANDROID OR LINUX))
|
||||
target_sources(common
|
||||
PRIVATE
|
||||
arm64/native_clock.cpp
|
||||
arm64/native_clock.h
|
||||
)
|
||||
endif()
|
||||
|
||||
if (MSVC)
|
||||
target_compile_definitions(common PRIVATE
|
||||
# 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
|
||||
/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()
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
@ -215,7 +227,7 @@ endif()
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
#endif
|
||||
|
||||
#define LTO_NOINLINE __attribute__((noinline))
|
||||
|
||||
#else // _MSC_VER
|
||||
|
||||
#define LTO_NOINLINE
|
||||
|
||||
// Locale Cross-Compatibility
|
||||
#define locale_t _locale_t
|
||||
|
||||
|
@ -211,6 +211,11 @@ struct Elf64_Rela {
|
||||
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. */
|
||||
|
||||
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 ElfDtFiniArraySz = 28; /* Size in bytes of DT_FINI_ARRAY */
|
||||
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 Common
|
||||
|
@ -18,10 +18,12 @@
|
||||
#define LOAD_DIR "load"
|
||||
#define LOG_DIR "log"
|
||||
#define NAND_DIR "nand"
|
||||
#define PLAY_TIME_DIR "play_time"
|
||||
#define SCREENSHOTS_DIR "screenshots"
|
||||
#define SDMC_DIR "sdmc"
|
||||
#define SHADER_DIR "shader"
|
||||
#define TAS_DIR "tas"
|
||||
#define ICONS_DIR "icons"
|
||||
|
||||
// yuzu-specific files
|
||||
|
||||
|
@ -124,10 +124,12 @@ public:
|
||||
GenerateYuzuPath(YuzuPath::LoadDir, yuzu_path / LOAD_DIR);
|
||||
GenerateYuzuPath(YuzuPath::LogDir, yuzu_path / LOG_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::SDMCDir, yuzu_path / SDMC_DIR);
|
||||
GenerateYuzuPath(YuzuPath::ShaderDir, yuzu_path / SHADER_DIR);
|
||||
GenerateYuzuPath(YuzuPath::TASDir, yuzu_path / TAS_DIR);
|
||||
GenerateYuzuPath(YuzuPath::IconsDir, yuzu_path / ICONS_DIR);
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -20,10 +20,12 @@ enum class YuzuPath {
|
||||
LoadDir, // Where cheat/mod files are stored.
|
||||
LogDir, // Where log files are stored.
|
||||
NANDDir, // Where the emulated NAND is stored.
|
||||
PlayTimeDir, // Where play time data is stored.
|
||||
ScreenshotsDir, // Where yuzu screenshots are stored.
|
||||
SDMCDir, // Where the emulated SDMC is stored.
|
||||
ShaderDir, // Where shaders are stored.
|
||||
TASDir, // Where TAS scripts are stored.
|
||||
IconsDir, // Where Icons for Windows shortcuts are stored.
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -15,12 +15,13 @@
|
||||
#include <condition_variable>
|
||||
#include <stop_token>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
|
||||
namespace Common {
|
||||
|
||||
template <typename Condvar, typename Lock, typename Pred>
|
||||
void CondvarWait(Condvar& cv, std::unique_lock<Lock>& lk, std::stop_token token, Pred&& pred) {
|
||||
cv.wait(lk, token, std::move(pred));
|
||||
cv.wait(lk, token, std::forward<Pred>(pred));
|
||||
}
|
||||
|
||||
template <typename Rep, typename Period>
|
||||
@ -109,7 +110,7 @@ public:
|
||||
|
||||
// Insert the callback.
|
||||
stop_state_callback ret = ++m_next_callback;
|
||||
m_callbacks.emplace(ret, move(f));
|
||||
m_callbacks.emplace(ret, std::move(f));
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -162,7 +163,7 @@ private:
|
||||
friend class stop_source;
|
||||
template <typename Callback>
|
||||
friend class stop_callback;
|
||||
stop_token(shared_ptr<polyfill::stop_state> stop_state) : m_stop_state(move(stop_state)) {}
|
||||
stop_token(shared_ptr<polyfill::stop_state> stop_state) : m_stop_state(std::move(stop_state)) {}
|
||||
|
||||
private:
|
||||
shared_ptr<polyfill::stop_state> m_stop_state;
|
||||
@ -198,7 +199,7 @@ public:
|
||||
private:
|
||||
friend class jthread;
|
||||
explicit stop_source(shared_ptr<polyfill::stop_state> stop_state)
|
||||
: m_stop_state(move(stop_state)) {}
|
||||
: m_stop_state(std::move(stop_state)) {}
|
||||
|
||||
private:
|
||||
shared_ptr<polyfill::stop_state> m_stop_state;
|
||||
@ -218,16 +219,16 @@ public:
|
||||
C&& cb) noexcept(is_nothrow_constructible_v<Callback, C>)
|
||||
: m_stop_state(st.m_stop_state) {
|
||||
if (m_stop_state) {
|
||||
m_callback = m_stop_state->insert_callback(move(cb));
|
||||
m_callback = m_stop_state->insert_callback(std::move(cb));
|
||||
}
|
||||
}
|
||||
template <typename C>
|
||||
requires constructible_from<Callback, C>
|
||||
explicit stop_callback(stop_token&& st,
|
||||
C&& cb) noexcept(is_nothrow_constructible_v<Callback, C>)
|
||||
: m_stop_state(move(st.m_stop_state)) {
|
||||
: m_stop_state(std::move(st.m_stop_state)) {
|
||||
if (m_stop_state) {
|
||||
m_callback = m_stop_state->insert_callback(move(cb));
|
||||
m_callback = m_stop_state->insert_callback(std::move(cb));
|
||||
}
|
||||
}
|
||||
~stop_callback() {
|
||||
@ -260,7 +261,7 @@ public:
|
||||
typename = enable_if_t<!is_same_v<remove_cvref_t<F>, jthread>>>
|
||||
explicit jthread(F&& f, Args&&... args)
|
||||
: m_stop_state(make_shared<polyfill::stop_state>()),
|
||||
m_thread(make_thread(move(f), move(args)...)) {}
|
||||
m_thread(make_thread(std::forward<F>(f), std::forward<Args>(args)...)) {}
|
||||
|
||||
~jthread() {
|
||||
if (joinable()) {
|
||||
@ -317,9 +318,9 @@ private:
|
||||
template <typename F, typename... Args>
|
||||
thread make_thread(F&& f, Args&&... args) {
|
||||
if constexpr (is_invocable_v<decay_t<F>, stop_token, decay_t<Args>...>) {
|
||||
return thread(move(f), get_stop_token(), move(args)...);
|
||||
return thread(std::forward<F>(f), get_stop_token(), std::forward<Args>(args)...);
|
||||
} else {
|
||||
return thread(move(f), move(args)...);
|
||||
return thread(std::forward<F>(f), std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -45,6 +45,7 @@ SWITCHABLE(CpuAccuracy, true);
|
||||
SWITCHABLE(FullscreenMode, true);
|
||||
SWITCHABLE(GpuAccuracy, true);
|
||||
SWITCHABLE(Language, true);
|
||||
SWITCHABLE(MemoryLayout, true);
|
||||
SWITCHABLE(NvdecEmulation, false);
|
||||
SWITCHABLE(Region, true);
|
||||
SWITCHABLE(RendererBackend, true);
|
||||
@ -61,6 +62,10 @@ SWITCHABLE(u32, false);
|
||||
SWITCHABLE(u8, false);
|
||||
SWITCHABLE(u8, true);
|
||||
|
||||
// Used in UISettings
|
||||
// TODO see if we can move this to uisettings.cpp
|
||||
SWITCHABLE(ConfirmStop, true);
|
||||
|
||||
#undef SETTING
|
||||
#undef SWITCHABLE
|
||||
#endif
|
||||
|
@ -67,6 +67,7 @@ SWITCHABLE(CpuAccuracy, true);
|
||||
SWITCHABLE(FullscreenMode, true);
|
||||
SWITCHABLE(GpuAccuracy, true);
|
||||
SWITCHABLE(Language, true);
|
||||
SWITCHABLE(MemoryLayout, true);
|
||||
SWITCHABLE(NvdecEmulation, false);
|
||||
SWITCHABLE(Region, true);
|
||||
SWITCHABLE(RendererBackend, true);
|
||||
@ -83,6 +84,10 @@ SWITCHABLE(u32, false);
|
||||
SWITCHABLE(u8, false);
|
||||
SWITCHABLE(u8, true);
|
||||
|
||||
// Used in UISettings
|
||||
// TODO see if we can move this to uisettings.h
|
||||
SWITCHABLE(ConfirmStop, true);
|
||||
|
||||
#undef SETTING
|
||||
#undef SWITCHABLE
|
||||
#endif
|
||||
|
@ -133,6 +133,8 @@ ENUM(CpuAccuracy, Auto, Accurate, Unsafe, Paranoid);
|
||||
|
||||
ENUM(MemoryLayout, Memory_4Gb, Memory_6Gb, Memory_8Gb);
|
||||
|
||||
ENUM(ConfirmStop, Ask_Always, Ask_Based_On_Game, Ask_Never);
|
||||
|
||||
ENUM(FullscreenMode, Borderless, Exclusive);
|
||||
|
||||
ENUM(NvdecEmulation, Off, Cpu, Gpu);
|
||||
|
8
src/common/stb.cpp
Normal file
8
src/common/stb.cpp
Normal file
@ -0,0 +1,8 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#define STB_IMAGE_RESIZE_IMPLEMENTATION
|
||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
|
||||
#include "common/stb.h"
|
8
src/common/stb.h
Normal file
8
src/common/stb.h
Normal file
@ -0,0 +1,8 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stb_image.h>
|
||||
#include <stb_image_resize.h>
|
||||
#include <stb_image_write.h>
|
@ -135,6 +135,11 @@ std::u16string UTF8ToUTF16(std::string_view input) {
|
||||
return convert.from_bytes(input.data(), input.data() + input.size());
|
||||
}
|
||||
|
||||
std::u32string UTF8ToUTF32(std::string_view input) {
|
||||
std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> convert;
|
||||
return convert.from_bytes(input.data(), input.data() + input.size());
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
static std::wstring CPToUTF16(u32 code_page, std::string_view input) {
|
||||
const auto size =
|
||||
|
@ -38,6 +38,7 @@ bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _
|
||||
|
||||
[[nodiscard]] std::string UTF16ToUTF8(std::u16string_view input);
|
||||
[[nodiscard]] std::u16string UTF8ToUTF16(std::string_view input);
|
||||
[[nodiscard]] std::u32string UTF8ToUTF32(std::string_view input);
|
||||
|
||||
#ifdef _WIN32
|
||||
[[nodiscard]] std::string UTF16ToUTF8(std::wstring_view input);
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <mach/mach.h>
|
||||
#elif defined(_WIN32)
|
||||
#include <windows.h>
|
||||
#include "common/string_util.h"
|
||||
#else
|
||||
#if defined(__Bitrig__) || defined(__DragonFly__) || defined(__FreeBSD__) || defined(__OpenBSD__)
|
||||
#include <pthread_np.h>
|
||||
@ -82,29 +83,8 @@ void SetCurrentThreadPriority(ThreadPriority new_priority) {
|
||||
#ifdef _MSC_VER
|
||||
|
||||
// Sets the debugger-visible name of the current thread.
|
||||
// Uses trick documented in:
|
||||
// https://docs.microsoft.com/en-us/visualstudio/debugger/how-to-set-a-thread-name-in-native-code
|
||||
void SetCurrentThreadName(const char* name) {
|
||||
static const DWORD MS_VC_EXCEPTION = 0x406D1388;
|
||||
|
||||
#pragma pack(push, 8)
|
||||
struct THREADNAME_INFO {
|
||||
DWORD dwType; // must be 0x1000
|
||||
LPCSTR szName; // pointer to name (in user addr space)
|
||||
DWORD dwThreadID; // thread ID (-1=caller thread)
|
||||
DWORD dwFlags; // reserved for future use, must be zero
|
||||
} info;
|
||||
#pragma pack(pop)
|
||||
|
||||
info.dwType = 0x1000;
|
||||
info.szName = name;
|
||||
info.dwThreadID = std::numeric_limits<DWORD>::max();
|
||||
info.dwFlags = 0;
|
||||
|
||||
__try {
|
||||
RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)&info);
|
||||
} __except (EXCEPTION_CONTINUE_EXECUTION) {
|
||||
}
|
||||
SetThreadDescription(GetCurrentThread(), UTF8ToUTF16W(name).data());
|
||||
}
|
||||
|
||||
#else // !MSVC_VER, so must be POSIX threads
|
||||
|
@ -10,6 +10,10 @@
|
||||
#include "common/x64/rdtsc.h"
|
||||
#endif
|
||||
|
||||
#if defined(ARCHITECTURE_arm64) && defined(__linux__)
|
||||
#include "common/arm64/native_clock.h"
|
||||
#endif
|
||||
|
||||
namespace Common {
|
||||
|
||||
class StandardWallClock final : public WallClock {
|
||||
@ -53,7 +57,7 @@ private:
|
||||
};
|
||||
|
||||
std::unique_ptr<WallClock> CreateOptimalClock() {
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
#if defined(ARCHITECTURE_x86_64)
|
||||
const auto& caps = GetCPUCaps();
|
||||
|
||||
if (caps.invariant_tsc && caps.tsc_frequency >= std::nano::den) {
|
||||
@ -64,6 +68,8 @@ std::unique_ptr<WallClock> CreateOptimalClock() {
|
||||
// - Is not more precise than 1 GHz (1ns resolution)
|
||||
return std::make_unique<StandardWallClock>();
|
||||
}
|
||||
#elif defined(ARCHITECTURE_arm64) && defined(__linux__)
|
||||
return std::make_unique<Arm64::NativeClock>();
|
||||
#else
|
||||
return std::make_unique<StandardWallClock>();
|
||||
#endif
|
||||
|
@ -466,14 +466,18 @@ add_library(core STATIC
|
||||
hle/service/caps/caps_a.h
|
||||
hle/service/caps/caps_c.cpp
|
||||
hle/service/caps/caps_c.h
|
||||
hle/service/caps/caps_u.cpp
|
||||
hle/service/caps/caps_u.h
|
||||
hle/service/caps/caps_manager.cpp
|
||||
hle/service/caps/caps_manager.h
|
||||
hle/service/caps/caps_result.h
|
||||
hle/service/caps/caps_sc.cpp
|
||||
hle/service/caps/caps_sc.h
|
||||
hle/service/caps/caps_ss.cpp
|
||||
hle/service/caps/caps_ss.h
|
||||
hle/service/caps/caps_su.cpp
|
||||
hle/service/caps/caps_su.h
|
||||
hle/service/caps/caps_types.h
|
||||
hle/service/caps/caps_u.cpp
|
||||
hle/service/caps/caps_u.h
|
||||
hle/service/erpt/erpt.cpp
|
||||
hle/service/erpt/erpt.h
|
||||
hle/service/es/es.cpp
|
||||
|
@ -116,11 +116,8 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
|
||||
}
|
||||
}
|
||||
|
||||
if (concat.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return FileSys::ConcatenatedVfsFile::MakeConcatenatedFile(concat, dir->GetName());
|
||||
return FileSys::ConcatenatedVfsFile::MakeConcatenatedFile(dir->GetName(),
|
||||
std::move(concat));
|
||||
}
|
||||
|
||||
if (Common::FS::IsDir(path)) {
|
||||
@ -1078,6 +1075,10 @@ void System::ApplySettings() {
|
||||
impl->RefreshTime();
|
||||
|
||||
if (IsPoweredOn()) {
|
||||
if (Settings::values.custom_rtc_enabled) {
|
||||
const s64 posix_time{Settings::values.custom_rtc.GetValue()};
|
||||
GetTimeManager().UpdateLocalSystemClockTime(posix_time);
|
||||
}
|
||||
Renderer().RefreshBaseSettings();
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,8 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <atomic>
|
||||
#include <codecvt>
|
||||
#include <locale>
|
||||
#include <numeric>
|
||||
#include <optional>
|
||||
#include <thread>
|
||||
@ -12,6 +14,7 @@
|
||||
#include "common/logging/log.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "common/settings.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/arm/arm_interface.h"
|
||||
#include "core/core.h"
|
||||
#include "core/debugger/gdbstub.h"
|
||||
@ -68,10 +71,16 @@ static std::string EscapeGDB(std::string_view data) {
|
||||
}
|
||||
|
||||
static std::string EscapeXML(std::string_view data) {
|
||||
std::u32string converted = U"[Encoding error]";
|
||||
try {
|
||||
converted = Common::UTF8ToUTF32(data);
|
||||
} catch (std::range_error&) {
|
||||
}
|
||||
|
||||
std::string escaped;
|
||||
escaped.reserve(data.size());
|
||||
|
||||
for (char c : data) {
|
||||
for (char32_t c : converted) {
|
||||
switch (c) {
|
||||
case '&':
|
||||
escaped += "&";
|
||||
@ -86,7 +95,11 @@ static std::string EscapeXML(std::string_view data) {
|
||||
escaped += ">";
|
||||
break;
|
||||
default:
|
||||
escaped += c;
|
||||
if (c > 0x7f) {
|
||||
escaped += fmt::format("&#{};", static_cast<u32>(c));
|
||||
} else {
|
||||
escaped += static_cast<char>(c);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -809,11 +822,13 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
|
||||
const char i = True(mem_info.attribute & MemoryAttribute::IpcLocked) ? 'I' : '-';
|
||||
const char d = True(mem_info.attribute & MemoryAttribute::DeviceShared) ? 'D' : '-';
|
||||
const char u = True(mem_info.attribute & MemoryAttribute::Uncached) ? 'U' : '-';
|
||||
const char p =
|
||||
True(mem_info.attribute & MemoryAttribute::PermissionLocked) ? 'P' : '-';
|
||||
|
||||
reply +=
|
||||
fmt::format(" {:#012x} - {:#012x} {} {} {}{}{}{} [{}, {}]\n",
|
||||
mem_info.base_address, mem_info.base_address + mem_info.size - 1,
|
||||
perm, state, l, i, d, u, mem_info.ipc_count, mem_info.device_count);
|
||||
reply += fmt::format(" {:#012x} - {:#012x} {} {} {}{}{}{}{} [{}, {}]\n",
|
||||
mem_info.base_address,
|
||||
mem_info.base_address + mem_info.size - 1, perm, state, l, i,
|
||||
d, u, p, mem_info.ipc_count, mem_info.device_count);
|
||||
}
|
||||
|
||||
const uintptr_t next_address = mem_info.base_address + mem_info.size;
|
||||
|
@ -107,62 +107,56 @@ static u64 romfs_get_hash_table_count(u64 num_entries) {
|
||||
|
||||
void RomFSBuildContext::VisitDirectory(VirtualDir romfs_dir, VirtualDir ext_dir,
|
||||
std::shared_ptr<RomFSBuildDirectoryContext> parent) {
|
||||
std::vector<std::shared_ptr<RomFSBuildDirectoryContext>> child_dirs;
|
||||
for (auto& child_romfs_file : romfs_dir->GetFiles()) {
|
||||
const auto name = child_romfs_file->GetName();
|
||||
const auto child = std::make_shared<RomFSBuildFileContext>();
|
||||
// Set child's path.
|
||||
child->cur_path_ofs = parent->path_len + 1;
|
||||
child->path_len = child->cur_path_ofs + static_cast<u32>(name.size());
|
||||
child->path = parent->path + "/" + name;
|
||||
|
||||
const auto entries = romfs_dir->GetEntries();
|
||||
if (ext_dir != nullptr && ext_dir->GetFile(name + ".stub") != nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const auto& kv : entries) {
|
||||
if (kv.second == VfsEntryType::Directory) {
|
||||
const auto child = std::make_shared<RomFSBuildDirectoryContext>();
|
||||
// Set child's path.
|
||||
child->cur_path_ofs = parent->path_len + 1;
|
||||
child->path_len = child->cur_path_ofs + static_cast<u32>(kv.first.size());
|
||||
child->path = parent->path + "/" + kv.first;
|
||||
// Sanity check on path_len
|
||||
ASSERT(child->path_len < FS_MAX_PATH);
|
||||
|
||||
if (ext_dir != nullptr && ext_dir->GetFile(kv.first + ".stub") != nullptr) {
|
||||
continue;
|
||||
}
|
||||
child->source = std::move(child_romfs_file);
|
||||
|
||||
// Sanity check on path_len
|
||||
ASSERT(child->path_len < FS_MAX_PATH);
|
||||
|
||||
if (AddDirectory(parent, child)) {
|
||||
child_dirs.push_back(child);
|
||||
}
|
||||
} else {
|
||||
const auto child = std::make_shared<RomFSBuildFileContext>();
|
||||
// Set child's path.
|
||||
child->cur_path_ofs = parent->path_len + 1;
|
||||
child->path_len = child->cur_path_ofs + static_cast<u32>(kv.first.size());
|
||||
child->path = parent->path + "/" + kv.first;
|
||||
|
||||
if (ext_dir != nullptr && ext_dir->GetFile(kv.first + ".stub") != nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Sanity check on path_len
|
||||
ASSERT(child->path_len < FS_MAX_PATH);
|
||||
|
||||
child->source = romfs_dir->GetFile(kv.first);
|
||||
|
||||
if (ext_dir != nullptr) {
|
||||
if (const auto ips = ext_dir->GetFile(kv.first + ".ips")) {
|
||||
if (auto patched = PatchIPS(child->source, ips)) {
|
||||
child->source = std::move(patched);
|
||||
}
|
||||
if (ext_dir != nullptr) {
|
||||
if (const auto ips = ext_dir->GetFile(name + ".ips")) {
|
||||
if (auto patched = PatchIPS(child->source, ips)) {
|
||||
child->source = std::move(patched);
|
||||
}
|
||||
}
|
||||
|
||||
child->size = child->source->GetSize();
|
||||
|
||||
AddFile(parent, child);
|
||||
}
|
||||
|
||||
child->size = child->source->GetSize();
|
||||
|
||||
AddFile(parent, child);
|
||||
}
|
||||
|
||||
for (auto& child : child_dirs) {
|
||||
auto subdir_name = std::string_view(child->path).substr(child->cur_path_ofs);
|
||||
auto child_romfs_dir = romfs_dir->GetSubdirectory(subdir_name);
|
||||
auto child_ext_dir = ext_dir != nullptr ? ext_dir->GetSubdirectory(subdir_name) : nullptr;
|
||||
for (auto& child_romfs_dir : romfs_dir->GetSubdirectories()) {
|
||||
const auto name = child_romfs_dir->GetName();
|
||||
const auto child = std::make_shared<RomFSBuildDirectoryContext>();
|
||||
// Set child's path.
|
||||
child->cur_path_ofs = parent->path_len + 1;
|
||||
child->path_len = child->cur_path_ofs + static_cast<u32>(name.size());
|
||||
child->path = parent->path + "/" + name;
|
||||
|
||||
if (ext_dir != nullptr && ext_dir->GetFile(name + ".stub") != nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Sanity check on path_len
|
||||
ASSERT(child->path_len < FS_MAX_PATH);
|
||||
|
||||
if (!AddDirectory(parent, child)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto child_ext_dir = ext_dir != nullptr ? ext_dir->GetSubdirectory(name) : nullptr;
|
||||
this->VisitDirectory(child_romfs_dir, child_ext_dir, child);
|
||||
}
|
||||
}
|
||||
@ -293,7 +287,7 @@ std::multimap<u64, VirtualFile> RomFSBuildContext::Build() {
|
||||
|
||||
cur_entry.name_size = name_size;
|
||||
|
||||
out.emplace(cur_file->offset + ROMFS_FILEPARTITION_OFS, cur_file->source);
|
||||
out.emplace(cur_file->offset + ROMFS_FILEPARTITION_OFS, std::move(cur_file->source));
|
||||
std::memcpy(file_table.data() + cur_file->entry_offset, &cur_entry, sizeof(RomFSFileEntry));
|
||||
std::memset(file_table.data() + cur_file->entry_offset + sizeof(RomFSFileEntry), 0,
|
||||
Common::AlignUp(cur_entry.name_size, 4));
|
||||
|
@ -377,16 +377,16 @@ static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType t
|
||||
|
||||
auto romfs_dir = FindSubdirectoryCaseless(subdir, "romfs");
|
||||
if (romfs_dir != nullptr)
|
||||
layers.push_back(std::make_shared<CachedVfsDirectory>(romfs_dir));
|
||||
layers.emplace_back(std::make_shared<CachedVfsDirectory>(std::move(romfs_dir)));
|
||||
|
||||
auto ext_dir = FindSubdirectoryCaseless(subdir, "romfs_ext");
|
||||
if (ext_dir != nullptr)
|
||||
layers_ext.push_back(std::make_shared<CachedVfsDirectory>(ext_dir));
|
||||
layers_ext.emplace_back(std::make_shared<CachedVfsDirectory>(std::move(ext_dir)));
|
||||
|
||||
if (type == ContentRecordType::HtmlDocument) {
|
||||
auto manual_dir = FindSubdirectoryCaseless(subdir, "manual_html");
|
||||
if (manual_dir != nullptr)
|
||||
layers.push_back(std::make_shared<CachedVfsDirectory>(manual_dir));
|
||||
layers.emplace_back(std::make_shared<CachedVfsDirectory>(std::move(manual_dir)));
|
||||
}
|
||||
}
|
||||
|
||||
@ -400,7 +400,7 @@ static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType t
|
||||
return;
|
||||
}
|
||||
|
||||
layers.push_back(std::move(extracted));
|
||||
layers.emplace_back(std::move(extracted));
|
||||
|
||||
auto layered = LayeredVfsDirectory::MakeLayeredDirectory(std::move(layers));
|
||||
if (layered == nullptr) {
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "core/file_sys/program_metadata.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
#include "core/loader/loader.h"
|
||||
@ -95,6 +96,13 @@ Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) {
|
||||
return Loader::ResultStatus::Success;
|
||||
}
|
||||
|
||||
Loader::ResultStatus ProgramMetadata::Reload(VirtualFile file) {
|
||||
const u64 original_program_id = aci_header.title_id;
|
||||
SCOPE_EXIT({ aci_header.title_id = original_program_id; });
|
||||
|
||||
return this->Load(file);
|
||||
}
|
||||
|
||||
/*static*/ ProgramMetadata ProgramMetadata::GetDefault() {
|
||||
// Allow use of cores 0~3 and thread priorities 1~63.
|
||||
constexpr u32 default_thread_info_capability = 0x30007F7;
|
||||
|
@ -56,6 +56,7 @@ public:
|
||||
static ProgramMetadata GetDefault();
|
||||
|
||||
Loader::ResultStatus Load(VirtualFile file);
|
||||
Loader::ResultStatus Reload(VirtualFile file);
|
||||
|
||||
/// Load from parameters instead of NPDM file, used for KIP
|
||||
void LoadManual(bool is_64_bit, ProgramAddressSpaceType address_space, s32 main_thread_prio,
|
||||
|
@ -322,7 +322,8 @@ VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& open_di
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return ConcatenatedVfsFile::MakeConcatenatedFile(concat, concat.front()->GetName());
|
||||
auto name = concat.front()->GetName();
|
||||
return ConcatenatedVfsFile::MakeConcatenatedFile(std::move(name), std::move(concat));
|
||||
}
|
||||
|
||||
VirtualFile RegisteredCache::GetFileAtID(NcaID id) const {
|
||||
|
@ -133,7 +133,7 @@ VirtualDir ExtractRomFS(VirtualFile file, RomFSExtractionType type) {
|
||||
out = out->GetSubdirectories().front();
|
||||
}
|
||||
|
||||
return std::make_shared<CachedVfsDirectory>(out);
|
||||
return std::make_shared<CachedVfsDirectory>(std::move(out));
|
||||
}
|
||||
|
||||
VirtualFile CreateRomFS(VirtualDir dir, VirtualDir ext) {
|
||||
@ -141,8 +141,7 @@ VirtualFile CreateRomFS(VirtualDir dir, VirtualDir ext) {
|
||||
return nullptr;
|
||||
|
||||
RomFSBuildContext ctx{dir, ext};
|
||||
auto file_map = ctx.Build();
|
||||
return ConcatenatedVfsFile::MakeConcatenatedFile(0, file_map, dir->GetName());
|
||||
return ConcatenatedVfsFile::MakeConcatenatedFile(0, dir->GetName(), ctx.Build());
|
||||
}
|
||||
|
||||
} // namespace FileSys
|
||||
|
@ -1,6 +1,7 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/file_sys/system_archive/system_version.h"
|
||||
#include "core/file_sys/vfs_vector.h"
|
||||
#include "core/hle/api_version.h"
|
||||
@ -12,6 +13,9 @@ std::string GetLongDisplayVersion() {
|
||||
}
|
||||
|
||||
VirtualDir SystemVersion() {
|
||||
LOG_WARNING(Common_Filesystem, "called - Using hardcoded firmware version '{}'",
|
||||
GetLongDisplayVersion());
|
||||
|
||||
VirtualFile file = std::make_shared<VectorVfsFile>(std::vector<u8>(0x100), "file");
|
||||
file->WriteObject(HLE::ApiVersion::HOS_VERSION_MAJOR, 0);
|
||||
file->WriteObject(HLE::ApiVersion::HOS_VERSION_MINOR, 1);
|
||||
|
@ -6,13 +6,13 @@
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
CachedVfsDirectory::CachedVfsDirectory(VirtualDir& source_dir)
|
||||
CachedVfsDirectory::CachedVfsDirectory(VirtualDir&& source_dir)
|
||||
: name(source_dir->GetName()), parent(source_dir->GetParentDirectory()) {
|
||||
for (auto& dir : source_dir->GetSubdirectories()) {
|
||||
dirs.emplace(dir->GetName(), std::make_shared<CachedVfsDirectory>(dir));
|
||||
dirs.emplace(dir->GetName(), std::make_shared<CachedVfsDirectory>(std::move(dir)));
|
||||
}
|
||||
for (auto& file : source_dir->GetFiles()) {
|
||||
files.emplace(file->GetName(), file);
|
||||
files.emplace(file->GetName(), std::move(file));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,7 @@ namespace FileSys {
|
||||
|
||||
class CachedVfsDirectory : public ReadOnlyVfsDirectory {
|
||||
public:
|
||||
CachedVfsDirectory(VirtualDir& source_directory);
|
||||
CachedVfsDirectory(VirtualDir&& source_directory);
|
||||
|
||||
~CachedVfsDirectory() override;
|
||||
VirtualFile GetFile(std::string_view file_name) const override;
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
ConcatenatedVfsFile::ConcatenatedVfsFile(ConcatenationMap&& concatenation_map_, std::string&& name_)
|
||||
ConcatenatedVfsFile::ConcatenatedVfsFile(std::string&& name_, ConcatenationMap&& concatenation_map_)
|
||||
: concatenation_map(std::move(concatenation_map_)), name(std::move(name_)) {
|
||||
DEBUG_ASSERT(this->VerifyContinuity());
|
||||
}
|
||||
@ -30,8 +30,8 @@ bool ConcatenatedVfsFile::VerifyContinuity() const {
|
||||
|
||||
ConcatenatedVfsFile::~ConcatenatedVfsFile() = default;
|
||||
|
||||
VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(const std::vector<VirtualFile>& files,
|
||||
std::string&& name) {
|
||||
VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(std::string&& name,
|
||||
std::vector<VirtualFile>&& files) {
|
||||
// Fold trivial cases.
|
||||
if (files.empty()) {
|
||||
return nullptr;
|
||||
@ -46,20 +46,21 @@ VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(const std::vector<VirtualF
|
||||
u64 last_offset = 0;
|
||||
|
||||
for (auto& file : files) {
|
||||
const auto size = file->GetSize();
|
||||
|
||||
concatenation_map.emplace_back(ConcatenationEntry{
|
||||
.offset = last_offset,
|
||||
.file = file,
|
||||
.file = std::move(file),
|
||||
});
|
||||
|
||||
last_offset += file->GetSize();
|
||||
last_offset += size;
|
||||
}
|
||||
|
||||
return VirtualFile(new ConcatenatedVfsFile(std::move(concatenation_map), std::move(name)));
|
||||
return VirtualFile(new ConcatenatedVfsFile(std::move(name), std::move(concatenation_map)));
|
||||
}
|
||||
|
||||
VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(u8 filler_byte,
|
||||
const std::multimap<u64, VirtualFile>& files,
|
||||
std::string&& name) {
|
||||
VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(u8 filler_byte, std::string&& name,
|
||||
std::multimap<u64, VirtualFile>&& files) {
|
||||
// Fold trivial cases.
|
||||
if (files.empty()) {
|
||||
return nullptr;
|
||||
@ -76,6 +77,8 @@ VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(u8 filler_byte,
|
||||
|
||||
// Iteration of a multimap is ordered, so offset will be strictly non-decreasing.
|
||||
for (auto& [offset, file] : files) {
|
||||
const auto size = file->GetSize();
|
||||
|
||||
if (offset > last_offset) {
|
||||
concatenation_map.emplace_back(ConcatenationEntry{
|
||||
.offset = last_offset,
|
||||
@ -85,13 +88,13 @@ VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(u8 filler_byte,
|
||||
|
||||
concatenation_map.emplace_back(ConcatenationEntry{
|
||||
.offset = offset,
|
||||
.file = file,
|
||||
.file = std::move(file),
|
||||
});
|
||||
|
||||
last_offset = offset + file->GetSize();
|
||||
last_offset = offset + size;
|
||||
}
|
||||
|
||||
return VirtualFile(new ConcatenatedVfsFile(std::move(concatenation_map), std::move(name)));
|
||||
return VirtualFile(new ConcatenatedVfsFile(std::move(name), std::move(concatenation_map)));
|
||||
}
|
||||
|
||||
std::string ConcatenatedVfsFile::GetName() const {
|
||||
|
@ -24,22 +24,20 @@ private:
|
||||
};
|
||||
using ConcatenationMap = std::vector<ConcatenationEntry>;
|
||||
|
||||
explicit ConcatenatedVfsFile(std::vector<ConcatenationEntry>&& concatenation_map,
|
||||
std::string&& name);
|
||||
explicit ConcatenatedVfsFile(std::string&& name,
|
||||
std::vector<ConcatenationEntry>&& concatenation_map);
|
||||
bool VerifyContinuity() const;
|
||||
|
||||
public:
|
||||
~ConcatenatedVfsFile() override;
|
||||
|
||||
/// Wrapper function to allow for more efficient handling of files.size() == 0, 1 cases.
|
||||
static VirtualFile MakeConcatenatedFile(const std::vector<VirtualFile>& files,
|
||||
std::string&& name);
|
||||
static VirtualFile MakeConcatenatedFile(std::string&& name, std::vector<VirtualFile>&& files);
|
||||
|
||||
/// Convenience function that turns a map of offsets to files into a concatenated file, filling
|
||||
/// gaps with a given filler byte.
|
||||
static VirtualFile MakeConcatenatedFile(u8 filler_byte,
|
||||
const std::multimap<u64, VirtualFile>& files,
|
||||
std::string&& name);
|
||||
static VirtualFile MakeConcatenatedFile(u8 filler_byte, std::string&& name,
|
||||
std::multimap<u64, VirtualFile>&& files);
|
||||
|
||||
std::string GetName() const override;
|
||||
std::size_t GetSize() const override;
|
||||
|
@ -38,7 +38,7 @@ VirtualDir LayeredVfsDirectory::GetDirectoryRelative(std::string_view path) cons
|
||||
for (const auto& layer : dirs) {
|
||||
auto dir = layer->GetDirectoryRelative(path);
|
||||
if (dir != nullptr) {
|
||||
out.push_back(std::move(dir));
|
||||
out.emplace_back(std::move(dir));
|
||||
}
|
||||
}
|
||||
|
||||
@ -62,11 +62,11 @@ std::vector<VirtualFile> LayeredVfsDirectory::GetFiles() const {
|
||||
std::set<std::string, std::less<>> out_names;
|
||||
|
||||
for (const auto& layer : dirs) {
|
||||
for (const auto& file : layer->GetFiles()) {
|
||||
for (auto& file : layer->GetFiles()) {
|
||||
auto file_name = file->GetName();
|
||||
if (!out_names.contains(file_name)) {
|
||||
out_names.emplace(std::move(file_name));
|
||||
out.push_back(file);
|
||||
out.emplace_back(std::move(file));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -86,7 +86,7 @@ std::vector<VirtualDir> LayeredVfsDirectory::GetSubdirectories() const {
|
||||
std::vector<VirtualDir> out;
|
||||
out.reserve(names.size());
|
||||
for (const auto& subdir : names)
|
||||
out.push_back(GetSubdirectory(subdir));
|
||||
out.emplace_back(GetSubdirectory(subdir));
|
||||
|
||||
return out;
|
||||
}
|
||||
|
@ -106,7 +106,7 @@ static_assert(KernelPageBufferAdditionalSize ==
|
||||
/// memory.
|
||||
static KPhysicalAddress TranslateSlabAddrToPhysical(KMemoryLayout& memory_layout,
|
||||
KVirtualAddress slab_addr) {
|
||||
slab_addr -= GetInteger(memory_layout.GetSlabRegionAddress());
|
||||
slab_addr -= memory_layout.GetSlabRegion().GetAddress();
|
||||
return GetInteger(slab_addr) + Core::DramMemoryMap::SlabHeapBase;
|
||||
}
|
||||
|
||||
@ -196,7 +196,12 @@ void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) {
|
||||
auto& kernel = system.Kernel();
|
||||
|
||||
// Get the start of the slab region, since that's where we'll be working.
|
||||
KVirtualAddress address = memory_layout.GetSlabRegionAddress();
|
||||
const KMemoryRegion& slab_region = memory_layout.GetSlabRegion();
|
||||
KVirtualAddress address = slab_region.GetAddress();
|
||||
|
||||
// Clear the slab region.
|
||||
// TODO: implement access to kernel VAs.
|
||||
// std::memset(device_ptr, 0, slab_region.GetSize());
|
||||
|
||||
// Initialize slab type array to be in sorted order.
|
||||
std::array<KSlabType, KSlabType_Count> slab_types;
|
||||
|
@ -19,4 +19,8 @@ static inline KPhysicalAddress GetInitialProcessBinaryPhysicalAddress() {
|
||||
MainMemoryAddress);
|
||||
}
|
||||
|
||||
static inline size_t GetInitialProcessBinarySize() {
|
||||
return InitialProcessBinarySizeMax;
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
@ -36,6 +36,7 @@ enum class KMemoryState : u32 {
|
||||
FlagCanChangeAttribute = (1 << 24),
|
||||
FlagCanCodeMemory = (1 << 25),
|
||||
FlagLinearMapped = (1 << 26),
|
||||
FlagCanPermissionLock = (1 << 27),
|
||||
|
||||
FlagsData = FlagCanReprotect | FlagCanUseIpc | FlagCanUseNonDeviceIpc | FlagCanUseNonSecureIpc |
|
||||
FlagMapped | FlagCanAlias | FlagCanTransfer | FlagCanQueryPhysical |
|
||||
@ -50,12 +51,16 @@ enum class KMemoryState : u32 {
|
||||
FlagLinearMapped,
|
||||
|
||||
Free = static_cast<u32>(Svc::MemoryState::Free),
|
||||
Io = static_cast<u32>(Svc::MemoryState::Io) | FlagMapped | FlagCanDeviceMap |
|
||||
FlagCanAlignedDeviceMap,
|
||||
|
||||
IoMemory = static_cast<u32>(Svc::MemoryState::Io) | FlagMapped | FlagCanDeviceMap |
|
||||
FlagCanAlignedDeviceMap,
|
||||
IoRegister =
|
||||
static_cast<u32>(Svc::MemoryState::Io) | FlagCanDeviceMap | FlagCanAlignedDeviceMap,
|
||||
|
||||
Static = static_cast<u32>(Svc::MemoryState::Static) | FlagMapped | FlagCanQueryPhysical,
|
||||
Code = static_cast<u32>(Svc::MemoryState::Code) | FlagsCode | FlagCanMapProcess,
|
||||
CodeData = static_cast<u32>(Svc::MemoryState::CodeData) | FlagsData | FlagCanMapProcess |
|
||||
FlagCanCodeMemory,
|
||||
FlagCanCodeMemory | FlagCanPermissionLock,
|
||||
Normal = static_cast<u32>(Svc::MemoryState::Normal) | FlagsData | FlagCanCodeMemory,
|
||||
Shared = static_cast<u32>(Svc::MemoryState::Shared) | FlagMapped | FlagReferenceCounted |
|
||||
FlagLinearMapped,
|
||||
@ -65,7 +70,8 @@ enum class KMemoryState : u32 {
|
||||
AliasCode = static_cast<u32>(Svc::MemoryState::AliasCode) | FlagsCode | FlagCanMapProcess |
|
||||
FlagCanCodeAlias,
|
||||
AliasCodeData = static_cast<u32>(Svc::MemoryState::AliasCodeData) | FlagsData |
|
||||
FlagCanMapProcess | FlagCanCodeAlias | FlagCanCodeMemory,
|
||||
FlagCanMapProcess | FlagCanCodeAlias | FlagCanCodeMemory |
|
||||
FlagCanPermissionLock,
|
||||
|
||||
Ipc = static_cast<u32>(Svc::MemoryState::Ipc) | FlagsMisc | FlagCanAlignedDeviceMap |
|
||||
FlagCanUseIpc | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
|
||||
@ -73,7 +79,7 @@ enum class KMemoryState : u32 {
|
||||
Stack = static_cast<u32>(Svc::MemoryState::Stack) | FlagsMisc | FlagCanAlignedDeviceMap |
|
||||
FlagCanUseIpc | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
|
||||
|
||||
ThreadLocal = static_cast<u32>(Svc::MemoryState::ThreadLocal) | FlagMapped | FlagLinearMapped,
|
||||
ThreadLocal = static_cast<u32>(Svc::MemoryState::ThreadLocal) | FlagLinearMapped,
|
||||
|
||||
Transfered = static_cast<u32>(Svc::MemoryState::Transfered) | FlagsMisc |
|
||||
FlagCanAlignedDeviceMap | FlagCanChangeAttribute | FlagCanUseIpc |
|
||||
@ -94,7 +100,7 @@ enum class KMemoryState : u32 {
|
||||
NonDeviceIpc =
|
||||
static_cast<u32>(Svc::MemoryState::NonDeviceIpc) | FlagsMisc | FlagCanUseNonDeviceIpc,
|
||||
|
||||
Kernel = static_cast<u32>(Svc::MemoryState::Kernel) | FlagMapped,
|
||||
Kernel = static_cast<u32>(Svc::MemoryState::Kernel),
|
||||
|
||||
GeneratedCode = static_cast<u32>(Svc::MemoryState::GeneratedCode) | FlagMapped |
|
||||
FlagReferenceCounted | FlagCanDebug | FlagLinearMapped,
|
||||
@ -105,34 +111,36 @@ enum class KMemoryState : u32 {
|
||||
|
||||
Insecure = static_cast<u32>(Svc::MemoryState::Insecure) | FlagMapped | FlagReferenceCounted |
|
||||
FlagLinearMapped | FlagCanChangeAttribute | FlagCanDeviceMap |
|
||||
FlagCanAlignedDeviceMap | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
|
||||
FlagCanAlignedDeviceMap | FlagCanQueryPhysical | FlagCanUseNonSecureIpc |
|
||||
FlagCanUseNonDeviceIpc,
|
||||
};
|
||||
DECLARE_ENUM_FLAG_OPERATORS(KMemoryState);
|
||||
|
||||
static_assert(static_cast<u32>(KMemoryState::Free) == 0x00000000);
|
||||
static_assert(static_cast<u32>(KMemoryState::Io) == 0x00182001);
|
||||
static_assert(static_cast<u32>(KMemoryState::IoMemory) == 0x00182001);
|
||||
static_assert(static_cast<u32>(KMemoryState::IoRegister) == 0x00180001);
|
||||
static_assert(static_cast<u32>(KMemoryState::Static) == 0x00042002);
|
||||
static_assert(static_cast<u32>(KMemoryState::Code) == 0x04DC7E03);
|
||||
static_assert(static_cast<u32>(KMemoryState::CodeData) == 0x07FEBD04);
|
||||
static_assert(static_cast<u32>(KMemoryState::CodeData) == 0x0FFEBD04);
|
||||
static_assert(static_cast<u32>(KMemoryState::Normal) == 0x077EBD05);
|
||||
static_assert(static_cast<u32>(KMemoryState::Shared) == 0x04402006);
|
||||
|
||||
static_assert(static_cast<u32>(KMemoryState::AliasCode) == 0x04DD7E08);
|
||||
static_assert(static_cast<u32>(KMemoryState::AliasCodeData) == 0x07FFBD09);
|
||||
static_assert(static_cast<u32>(KMemoryState::AliasCodeData) == 0x0FFFBD09);
|
||||
static_assert(static_cast<u32>(KMemoryState::Ipc) == 0x045C3C0A);
|
||||
static_assert(static_cast<u32>(KMemoryState::Stack) == 0x045C3C0B);
|
||||
static_assert(static_cast<u32>(KMemoryState::ThreadLocal) == 0x0400200C);
|
||||
static_assert(static_cast<u32>(KMemoryState::ThreadLocal) == 0x0400000C);
|
||||
static_assert(static_cast<u32>(KMemoryState::Transfered) == 0x055C3C0D);
|
||||
static_assert(static_cast<u32>(KMemoryState::SharedTransfered) == 0x045C380E);
|
||||
static_assert(static_cast<u32>(KMemoryState::SharedCode) == 0x0440380F);
|
||||
static_assert(static_cast<u32>(KMemoryState::Inaccessible) == 0x00000010);
|
||||
static_assert(static_cast<u32>(KMemoryState::NonSecureIpc) == 0x045C3811);
|
||||
static_assert(static_cast<u32>(KMemoryState::NonDeviceIpc) == 0x044C2812);
|
||||
static_assert(static_cast<u32>(KMemoryState::Kernel) == 0x00002013);
|
||||
static_assert(static_cast<u32>(KMemoryState::Kernel) == 0x00000013);
|
||||
static_assert(static_cast<u32>(KMemoryState::GeneratedCode) == 0x04402214);
|
||||
static_assert(static_cast<u32>(KMemoryState::CodeOut) == 0x04402015);
|
||||
static_assert(static_cast<u32>(KMemoryState::Coverage) == 0x00002016);
|
||||
static_assert(static_cast<u32>(KMemoryState::Insecure) == 0x05583817);
|
||||
static_assert(static_cast<u32>(KMemoryState::Insecure) == 0x055C3817);
|
||||
|
||||
enum class KMemoryPermission : u8 {
|
||||
None = 0,
|
||||
@ -182,8 +190,9 @@ enum class KMemoryAttribute : u8 {
|
||||
IpcLocked = static_cast<u8>(Svc::MemoryAttribute::IpcLocked),
|
||||
DeviceShared = static_cast<u8>(Svc::MemoryAttribute::DeviceShared),
|
||||
Uncached = static_cast<u8>(Svc::MemoryAttribute::Uncached),
|
||||
PermissionLocked = static_cast<u8>(Svc::MemoryAttribute::PermissionLocked),
|
||||
|
||||
SetMask = Uncached,
|
||||
SetMask = Uncached | PermissionLocked,
|
||||
};
|
||||
DECLARE_ENUM_FLAG_OPERATORS(KMemoryAttribute);
|
||||
|
||||
@ -261,6 +270,10 @@ struct KMemoryInfo {
|
||||
return m_state;
|
||||
}
|
||||
|
||||
constexpr Svc::MemoryState GetSvcState() const {
|
||||
return static_cast<Svc::MemoryState>(m_state & KMemoryState::Mask);
|
||||
}
|
||||
|
||||
constexpr KMemoryPermission GetPermission() const {
|
||||
return m_permission;
|
||||
}
|
||||
@ -326,6 +339,10 @@ public:
|
||||
return this->GetEndAddress() - 1;
|
||||
}
|
||||
|
||||
constexpr KMemoryState GetState() const {
|
||||
return m_memory_state;
|
||||
}
|
||||
|
||||
constexpr u16 GetIpcLockCount() const {
|
||||
return m_ipc_lock_count;
|
||||
}
|
||||
@ -443,6 +460,13 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
constexpr void UpdateAttribute(KMemoryAttribute mask, KMemoryAttribute attr) {
|
||||
ASSERT(False(mask & KMemoryAttribute::IpcLocked));
|
||||
ASSERT(False(mask & KMemoryAttribute::DeviceShared));
|
||||
|
||||
m_attribute = (m_attribute & ~mask) | attr;
|
||||
}
|
||||
|
||||
constexpr void Split(KMemoryBlock* block, KProcessAddress addr) {
|
||||
ASSERT(this->GetAddress() < addr);
|
||||
ASSERT(this->Contains(addr));
|
||||
|
@ -160,8 +160,8 @@ void KMemoryBlockManager::Update(KMemoryBlockManagerUpdateAllocator* allocator,
|
||||
}
|
||||
|
||||
// Update block state.
|
||||
it->Update(state, perm, attr, cur_address == address, static_cast<u8>(set_disable_attr),
|
||||
static_cast<u8>(clear_disable_attr));
|
||||
it->Update(state, perm, attr, it->GetAddress() == address,
|
||||
static_cast<u8>(set_disable_attr), static_cast<u8>(clear_disable_attr));
|
||||
cur_address += cur_info.GetSize();
|
||||
remaining_pages -= cur_info.GetNumPages();
|
||||
}
|
||||
@ -175,7 +175,9 @@ void KMemoryBlockManager::UpdateIfMatch(KMemoryBlockManagerUpdateAllocator* allo
|
||||
KProcessAddress address, size_t num_pages,
|
||||
KMemoryState test_state, KMemoryPermission test_perm,
|
||||
KMemoryAttribute test_attr, KMemoryState state,
|
||||
KMemoryPermission perm, KMemoryAttribute attr) {
|
||||
KMemoryPermission perm, KMemoryAttribute attr,
|
||||
KMemoryBlockDisableMergeAttribute set_disable_attr,
|
||||
KMemoryBlockDisableMergeAttribute clear_disable_attr) {
|
||||
// Ensure for auditing that we never end up with an invalid tree.
|
||||
KScopedMemoryBlockManagerAuditor auditor(this);
|
||||
ASSERT(Common::IsAligned(GetInteger(address), PageSize));
|
||||
@ -214,7 +216,8 @@ void KMemoryBlockManager::UpdateIfMatch(KMemoryBlockManagerUpdateAllocator* allo
|
||||
}
|
||||
|
||||
// Update block state.
|
||||
it->Update(state, perm, attr, false, 0, 0);
|
||||
it->Update(state, perm, attr, false, static_cast<u8>(set_disable_attr),
|
||||
static_cast<u8>(clear_disable_attr));
|
||||
cur_address += cur_info.GetSize();
|
||||
remaining_pages -= cur_info.GetNumPages();
|
||||
} else {
|
||||
@ -284,6 +287,65 @@ void KMemoryBlockManager::UpdateLock(KMemoryBlockManagerUpdateAllocator* allocat
|
||||
this->CoalesceForUpdate(allocator, address, num_pages);
|
||||
}
|
||||
|
||||
void KMemoryBlockManager::UpdateAttribute(KMemoryBlockManagerUpdateAllocator* allocator,
|
||||
KProcessAddress address, size_t num_pages,
|
||||
KMemoryAttribute mask, KMemoryAttribute attr) {
|
||||
// Ensure for auditing that we never end up with an invalid tree.
|
||||
KScopedMemoryBlockManagerAuditor auditor(this);
|
||||
ASSERT(Common::IsAligned(GetInteger(address), PageSize));
|
||||
|
||||
KProcessAddress cur_address = address;
|
||||
size_t remaining_pages = num_pages;
|
||||
iterator it = this->FindIterator(address);
|
||||
|
||||
while (remaining_pages > 0) {
|
||||
const size_t remaining_size = remaining_pages * PageSize;
|
||||
KMemoryInfo cur_info = it->GetMemoryInfo();
|
||||
|
||||
if ((it->GetAttribute() & mask) != attr) {
|
||||
// If we need to, create a new block before and insert it.
|
||||
if (cur_info.GetAddress() != GetInteger(cur_address)) {
|
||||
KMemoryBlock* new_block = allocator->Allocate();
|
||||
|
||||
it->Split(new_block, cur_address);
|
||||
it = m_memory_block_tree.insert(*new_block);
|
||||
it++;
|
||||
|
||||
cur_info = it->GetMemoryInfo();
|
||||
cur_address = cur_info.GetAddress();
|
||||
}
|
||||
|
||||
// If we need to, create a new block after and insert it.
|
||||
if (cur_info.GetSize() > remaining_size) {
|
||||
KMemoryBlock* new_block = allocator->Allocate();
|
||||
|
||||
it->Split(new_block, cur_address + remaining_size);
|
||||
it = m_memory_block_tree.insert(*new_block);
|
||||
|
||||
cur_info = it->GetMemoryInfo();
|
||||
}
|
||||
|
||||
// Update block state.
|
||||
it->UpdateAttribute(mask, attr);
|
||||
cur_address += cur_info.GetSize();
|
||||
remaining_pages -= cur_info.GetNumPages();
|
||||
} else {
|
||||
// If we already have the right attributes, just advance.
|
||||
if (cur_address + remaining_size < cur_info.GetEndAddress()) {
|
||||
remaining_pages = 0;
|
||||
cur_address += remaining_size;
|
||||
} else {
|
||||
remaining_pages =
|
||||
(cur_address + remaining_size - cur_info.GetEndAddress()) / PageSize;
|
||||
cur_address = cur_info.GetEndAddress();
|
||||
}
|
||||
}
|
||||
it++;
|
||||
}
|
||||
|
||||
this->CoalesceForUpdate(allocator, address, num_pages);
|
||||
}
|
||||
|
||||
// Debug.
|
||||
bool KMemoryBlockManager::CheckState() const {
|
||||
// Loop over every block, ensuring that we are sorted and coalesced.
|
||||
|
@ -115,7 +115,11 @@ public:
|
||||
void UpdateIfMatch(KMemoryBlockManagerUpdateAllocator* allocator, KProcessAddress address,
|
||||
size_t num_pages, KMemoryState test_state, KMemoryPermission test_perm,
|
||||
KMemoryAttribute test_attr, KMemoryState state, KMemoryPermission perm,
|
||||
KMemoryAttribute attr);
|
||||
KMemoryAttribute attr, KMemoryBlockDisableMergeAttribute set_disable_attr,
|
||||
KMemoryBlockDisableMergeAttribute clear_disable_attr);
|
||||
|
||||
void UpdateAttribute(KMemoryBlockManagerUpdateAllocator* allocator, KProcessAddress address,
|
||||
size_t num_pages, KMemoryAttribute mask, KMemoryAttribute attr);
|
||||
|
||||
iterator FindIterator(KProcessAddress address) const {
|
||||
return m_memory_block_tree.find(KMemoryBlock(
|
||||
|
@ -137,11 +137,9 @@ public:
|
||||
return GetStackTopAddress(core_id, KMemoryRegionType_KernelMiscExceptionStack);
|
||||
}
|
||||
|
||||
KVirtualAddress GetSlabRegionAddress() const {
|
||||
return Dereference(GetVirtualMemoryRegionTree().FindByType(KMemoryRegionType_KernelSlab))
|
||||
.GetAddress();
|
||||
const KMemoryRegion& GetSlabRegion() const {
|
||||
return Dereference(GetVirtualMemoryRegionTree().FindByType(KMemoryRegionType_KernelSlab));
|
||||
}
|
||||
|
||||
const KMemoryRegion& GetDeviceRegion(KMemoryRegionType type) const {
|
||||
return Dereference(GetPhysicalMemoryRegionTree().FindFirstDerived(type));
|
||||
}
|
||||
|
@ -119,7 +119,8 @@ void KMemoryManager::Initialize(KVirtualAddress management_region, size_t manage
|
||||
// Free each region to its corresponding heap.
|
||||
size_t reserved_sizes[MaxManagerCount] = {};
|
||||
const KPhysicalAddress ini_start = GetInitialProcessBinaryPhysicalAddress();
|
||||
const KPhysicalAddress ini_end = ini_start + InitialProcessBinarySizeMax;
|
||||
const size_t ini_size = GetInitialProcessBinarySize();
|
||||
const KPhysicalAddress ini_end = ini_start + ini_size;
|
||||
const KPhysicalAddress ini_last = ini_end - 1;
|
||||
for (const auto& it : m_system.Kernel().MemoryLayout().GetPhysicalMemoryRegionTree()) {
|
||||
if (it.IsDerivedFrom(KMemoryRegionType_DramUserPool)) {
|
||||
@ -137,13 +138,13 @@ void KMemoryManager::Initialize(KVirtualAddress management_region, size_t manage
|
||||
}
|
||||
|
||||
// Open/reserve the ini memory.
|
||||
manager.OpenFirst(ini_start, InitialProcessBinarySizeMax / PageSize);
|
||||
reserved_sizes[it.GetAttributes()] += InitialProcessBinarySizeMax;
|
||||
manager.OpenFirst(ini_start, ini_size / PageSize);
|
||||
reserved_sizes[it.GetAttributes()] += ini_size;
|
||||
|
||||
// Free memory after the ini to the heap.
|
||||
if (ini_last != cur_last) {
|
||||
ASSERT(cur_end != 0);
|
||||
manager.Free(ini_end, cur_end - ini_end);
|
||||
manager.Free(ini_end, (cur_end - ini_end) / PageSize);
|
||||
}
|
||||
} else {
|
||||
// Ensure there's no partial overlap with the ini image.
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user