mirror of
https://github.com/azahar-emu/azahar
synced 2025-11-07 07:29:58 +01:00
Compare commits
39 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
67f6735f02 | ||
|
|
1f483e1d33 | ||
|
|
068fec0d5f | ||
|
|
f309d4881a | ||
|
|
80ba2058a9 | ||
|
|
3716f6b9b6 | ||
|
|
3af2cd1227 | ||
|
|
0be78e9550 | ||
|
|
eb1197a65c | ||
|
|
a674eb8367 | ||
|
|
118d124852 | ||
|
|
34d46528eb | ||
|
|
72c924a456 | ||
|
|
f27490aeab | ||
|
|
ed451a72d9 | ||
|
|
5c89977605 | ||
|
|
deda004227 | ||
|
|
64f5277789 | ||
|
|
5c5b1cdf45 | ||
|
|
750286ae97 | ||
|
|
f35091eeb4 | ||
|
|
8cd3bcc0e3 | ||
|
|
e200bcbcbc | ||
|
|
a2e6891f01 | ||
|
|
e0078b2407 | ||
|
|
1830290e55 | ||
|
|
70f9379eef | ||
|
|
b63d7841dd | ||
|
|
944cb7ab51 | ||
|
|
e5c3a0eef7 | ||
|
|
16b8a78571 | ||
|
|
bf03cac9e1 | ||
|
|
b6be443069 | ||
|
|
10f6e1f6f7 | ||
|
|
6483b33ee1 | ||
|
|
485db276e3 | ||
|
|
20d577f9e3 | ||
|
|
48db1c1de5 | ||
|
|
219b8dca2e |
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
@ -62,7 +62,7 @@ jobs:
|
|||||||
name: ${{ env.OS }}-${{ env.TARGET }}
|
name: ${{ env.OS }}-${{ env.TARGET }}
|
||||||
path: artifacts/
|
path: artifacts/
|
||||||
macos:
|
macos:
|
||||||
runs-on: ${{ (matrix.target == 'x86_64' && 'macos-13') || 'macos-14' }}
|
runs-on: ${{ (matrix.target == 'x86_64' && 'macos-15-intel') || 'macos-26' }}
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
@ -103,7 +103,7 @@ jobs:
|
|||||||
name: ${{ env.OS }}-${{ env.TARGET }}
|
name: ${{ env.OS }}-${{ env.TARGET }}
|
||||||
path: artifacts/
|
path: artifacts/
|
||||||
macos-universal:
|
macos-universal:
|
||||||
runs-on: macos-14
|
runs-on: macos-26
|
||||||
needs: macos
|
needs: macos
|
||||||
env:
|
env:
|
||||||
OS: macos
|
OS: macos
|
||||||
|
|||||||
@ -48,6 +48,15 @@ if (APPLE)
|
|||||||
else()
|
else()
|
||||||
# Minimum macOS 13
|
# Minimum macOS 13
|
||||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "13.4")
|
set(CMAKE_OSX_DEPLOYMENT_TARGET "13.4")
|
||||||
|
|
||||||
|
# Catch compiler issue on AppleClang versions below 15.0
|
||||||
|
# TODO: Remove this check when we drop macOS 13 Ventura
|
||||||
|
if (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang" AND
|
||||||
|
CMAKE_CXX_COMPILER_VERSION VERSION_LESS 15.0)
|
||||||
|
message(FATAL_ERROR "AppleClang 15.0 or later is required due to a compiler bug in earlier versions.\n"
|
||||||
|
"Current version: ${CMAKE_CXX_COMPILER_VERSION}\n"
|
||||||
|
"After updating, delete 'CMakeCache.txt' in the build directory.")
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@ -112,6 +121,8 @@ option(ENABLE_MICROPROFILE "Enables microprofile capabilities" OFF)
|
|||||||
|
|
||||||
option(ENABLE_SSE42 "Enable SSE4.2 optimizations on x86_64" ON)
|
option(ENABLE_SSE42 "Enable SSE4.2 optimizations on x86_64" ON)
|
||||||
|
|
||||||
|
option(ENABLE_DEVELOPER_OPTIONS "Enable functionality targeted at emulator developers" OFF)
|
||||||
|
|
||||||
# Compile options
|
# Compile options
|
||||||
CMAKE_DEPENDENT_OPTION(COMPILE_WITH_DWARF "Add DWARF debugging information" ${IS_DEBUG_BUILD} "MINGW" OFF)
|
CMAKE_DEPENDENT_OPTION(COMPILE_WITH_DWARF "Add DWARF debugging information" ${IS_DEBUG_BUILD} "MINGW" OFF)
|
||||||
option(ENABLE_LTO "Enable link time optimization" ${DEFAULT_ENABLE_LTO})
|
option(ENABLE_LTO "Enable link time optimization" ${DEFAULT_ENABLE_LTO})
|
||||||
@ -299,7 +310,7 @@ find_package(Threads REQUIRED)
|
|||||||
|
|
||||||
if (ENABLE_QT)
|
if (ENABLE_QT)
|
||||||
if (NOT USE_SYSTEM_QT)
|
if (NOT USE_SYSTEM_QT)
|
||||||
download_qt(6.7.2)
|
download_qt(6.9.2)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
find_package(Qt6 REQUIRED COMPONENTS Widgets Multimedia Concurrent)
|
find_package(Qt6 REQUIRED COMPONENTS Widgets Multimedia Concurrent)
|
||||||
|
|||||||
@ -20,9 +20,9 @@ function(determine_qt_parameters target host_out type_out arch_out arch_path_out
|
|||||||
set(arch_path "mingw_64")
|
set(arch_path "mingw_64")
|
||||||
elseif (MSVC)
|
elseif (MSVC)
|
||||||
if ("arm64" IN_LIST ARCHITECTURE)
|
if ("arm64" IN_LIST ARCHITECTURE)
|
||||||
set(arch_path "msvc2019_arm64")
|
set(arch_path "msvc2022_arm64")
|
||||||
elseif ("x86_64" IN_LIST ARCHITECTURE)
|
elseif ("x86_64" IN_LIST ARCHITECTURE)
|
||||||
set(arch_path "msvc2019_64")
|
set(arch_path "msvc2022_64")
|
||||||
else()
|
else()
|
||||||
message(FATAL_ERROR "Unsupported bundled Qt architecture. Enable USE_SYSTEM_QT and provide your own.")
|
message(FATAL_ERROR "Unsupported bundled Qt architecture. Enable USE_SYSTEM_QT and provide your own.")
|
||||||
endif()
|
endif()
|
||||||
@ -30,12 +30,13 @@ function(determine_qt_parameters target host_out type_out arch_out arch_path_out
|
|||||||
|
|
||||||
# In case we're cross-compiling, prepare to also fetch the correct host Qt tools.
|
# In case we're cross-compiling, prepare to also fetch the correct host Qt tools.
|
||||||
if (CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "AMD64")
|
if (CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "AMD64")
|
||||||
set(host_arch_path "msvc2019_64")
|
set(host_arch_path "msvc2022_64")
|
||||||
elseif (CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "ARM64")
|
elseif (CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "ARM64")
|
||||||
# TODO: msvc2019_arm64 doesn't include some of the required tools for some reason,
|
# TODO: msvc2019_arm64 doesn't include some of the required tools for some reason,
|
||||||
# TODO: so until it does, just use msvc2019_64 under x86_64 emulation.
|
# TODO: so until it does, just use msvc2019_64 under x86_64 emulation.
|
||||||
|
# TODO: ^ Is this still true with msvc2022?
|
||||||
# set(host_arch_path "msvc2019_arm64")
|
# set(host_arch_path "msvc2019_arm64")
|
||||||
set(host_arch_path "msvc2019_64")
|
set(host_arch_path "msvc2022_64")
|
||||||
endif()
|
endif()
|
||||||
set(host_arch "win64_${host_arch_path}")
|
set(host_arch "win64_${host_arch_path}")
|
||||||
else()
|
else()
|
||||||
@ -105,7 +106,7 @@ function(download_qt_configuration prefix_out target host type arch arch_path ba
|
|||||||
|
|
||||||
if (NOT EXISTS "${prefix}")
|
if (NOT EXISTS "${prefix}")
|
||||||
message(STATUS "Downloading Qt binaries for ${target}:${host}:${type}:${arch}:${arch_path}")
|
message(STATUS "Downloading Qt binaries for ${target}:${host}:${type}:${arch}:${arch_path}")
|
||||||
set(AQT_PREBUILD_BASE_URL "https://github.com/miurahr/aqtinstall/releases/download/v3.1.18")
|
set(AQT_PREBUILD_BASE_URL "https://github.com/miurahr/aqtinstall/releases/download/v3.3.0")
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
set(aqt_path "${base_path}/aqt.exe")
|
set(aqt_path "${base_path}/aqt.exe")
|
||||||
if (NOT EXISTS "${aqt_path}")
|
if (NOT EXISTS "${aqt_path}")
|
||||||
|
|||||||
3
dist/apple/Info.plist.in
vendored
3
dist/apple/Info.plist.in
vendored
@ -75,6 +75,9 @@
|
|||||||
<true/>
|
<true/>
|
||||||
<key>NSHighResolutionCapable</key>
|
<key>NSHighResolutionCapable</key>
|
||||||
<string>True</string>
|
<string>True</string>
|
||||||
|
<key>UIDesignRequiresCompatibility</key>
|
||||||
|
<true/> <!-- Remove when Qt Liquid Glass issues are fixed upstream:
|
||||||
|
https://bugreports.qt.io/browse/QTBUG-138942 -->
|
||||||
<key>UIFileSharingEnabled</key>
|
<key>UIFileSharingEnabled</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>UILaunchStoryboardName</key>
|
<key>UILaunchStoryboardName</key>
|
||||||
|
|||||||
2
dist/compatibility_list
vendored
2
dist/compatibility_list
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 4f39041699412873d0afcec89a9313148a192647
|
Subproject commit a36decbe43d0e5a570ac3d3ba9a0b226dc832a17
|
||||||
65
externals/CMakeLists.txt
vendored
65
externals/CMakeLists.txt
vendored
@ -1,24 +1,37 @@
|
|||||||
# Definitions for all external bundled libraries
|
# Definitions for all external bundled libraries
|
||||||
|
|
||||||
# Suppress warnings from external libraries
|
# Suppress warnings from external libraries
|
||||||
if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
|
if (MSVC)
|
||||||
add_compile_options(/W0)
|
add_compile_options(/W0)
|
||||||
else()
|
else()
|
||||||
add_compile_options(-w)
|
add_compile_options(-w)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
function(target_disable_warnings target)
|
||||||
|
if (MSVC)
|
||||||
|
target_compile_options(${target} INTERFACE /W0)
|
||||||
|
else()
|
||||||
|
target_compile_options(${target} INTERFACE -w)
|
||||||
|
endif()
|
||||||
|
endfunction()
|
||||||
|
|
||||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/CMakeModules)
|
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/CMakeModules)
|
||||||
include(DownloadExternals)
|
include(DownloadExternals)
|
||||||
include(ExternalProject)
|
include(ExternalProject)
|
||||||
|
|
||||||
# Boost
|
# Boost
|
||||||
if (NOT USE_SYSTEM_BOOST)
|
if (USE_SYSTEM_BOOST)
|
||||||
|
unset(BOOST_ROOT CACHE)
|
||||||
|
unset(Boost_INCLUDE_DIR CACHE)
|
||||||
|
set(Boost_NO_SYSTEM_PATHS OFF CACHE BOOL "" FORCE)
|
||||||
|
else()
|
||||||
message(STATUS "Including vendored Boost library")
|
message(STATUS "Including vendored Boost library")
|
||||||
set(BOOST_ROOT "${CMAKE_SOURCE_DIR}/externals/boost" CACHE STRING "")
|
set(BOOST_ROOT "${CMAKE_SOURCE_DIR}/externals/boost" CACHE STRING "")
|
||||||
set(Boost_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/externals/boost" CACHE STRING "")
|
set(Boost_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/externals/boost" CACHE STRING "")
|
||||||
set(Boost_NO_SYSTEM_PATHS ON CACHE BOOL "")
|
set(Boost_NO_SYSTEM_PATHS ON CACHE BOOL "")
|
||||||
add_library(boost INTERFACE)
|
add_library(boost INTERFACE)
|
||||||
target_include_directories(boost SYSTEM INTERFACE ${Boost_INCLUDE_DIR})
|
target_include_directories(boost INTERFACE ${Boost_INCLUDE_DIR})
|
||||||
|
target_disable_warnings(boost)
|
||||||
|
|
||||||
# Boost::serialization
|
# Boost::serialization
|
||||||
file(GLOB boost_serialization_SRC "${CMAKE_SOURCE_DIR}/externals/boost/libs/serialization/src/*.cpp")
|
file(GLOB boost_serialization_SRC "${CMAKE_SOURCE_DIR}/externals/boost/libs/serialization/src/*.cpp")
|
||||||
@ -33,11 +46,7 @@ if (NOT USE_SYSTEM_BOOST)
|
|||||||
${CMAKE_SOURCE_DIR}/externals/boost/libs/iostreams/src/mapped_file.cpp
|
${CMAKE_SOURCE_DIR}/externals/boost/libs/iostreams/src/mapped_file.cpp
|
||||||
)
|
)
|
||||||
target_link_libraries(boost_iostreams PUBLIC boost)
|
target_link_libraries(boost_iostreams PUBLIC boost)
|
||||||
# Add additional boost libs here; remember to ALIAS them in the root CMakeLists!
|
# Add additional boost libs here; remember to ALIAS them in the root CMakeLists!
|
||||||
else()
|
|
||||||
unset(BOOST_ROOT CACHE)
|
|
||||||
unset(Boost_INCLUDE_DIR CACHE)
|
|
||||||
set(Boost_NO_SYSTEM_PATHS OFF CACHE BOOL "" FORCE)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Catch2
|
# Catch2
|
||||||
@ -73,6 +82,7 @@ endif()
|
|||||||
# dds-ktx
|
# dds-ktx
|
||||||
add_library(dds-ktx INTERFACE)
|
add_library(dds-ktx INTERFACE)
|
||||||
target_include_directories(dds-ktx INTERFACE ./dds-ktx)
|
target_include_directories(dds-ktx INTERFACE ./dds-ktx)
|
||||||
|
target_disable_warnings(dds-ktx)
|
||||||
|
|
||||||
# fmt and Xbyak need to be added before dynarmic
|
# fmt and Xbyak need to be added before dynarmic
|
||||||
# libfmt
|
# libfmt
|
||||||
@ -137,7 +147,8 @@ endif()
|
|||||||
|
|
||||||
# MicroProfile
|
# MicroProfile
|
||||||
add_library(microprofile INTERFACE)
|
add_library(microprofile INTERFACE)
|
||||||
target_include_directories(microprofile SYSTEM INTERFACE ./microprofile)
|
target_include_directories(microprofile INTERFACE ./microprofile)
|
||||||
|
target_disable_warnings(microprofile)
|
||||||
if (ENABLE_MICROPROFILE)
|
if (ENABLE_MICROPROFILE)
|
||||||
target_compile_definitions(microprofile INTERFACE MICROPROFILE_ENABLED=1)
|
target_compile_definitions(microprofile INTERFACE MICROPROFILE_ENABLED=1)
|
||||||
else()
|
else()
|
||||||
@ -146,10 +157,11 @@ endif()
|
|||||||
|
|
||||||
# Nihstro
|
# Nihstro
|
||||||
add_library(nihstro-headers INTERFACE)
|
add_library(nihstro-headers INTERFACE)
|
||||||
target_include_directories(nihstro-headers SYSTEM INTERFACE ./nihstro/include)
|
target_include_directories(nihstro-headers INTERFACE ./nihstro/include)
|
||||||
if (MSVC)
|
target_disable_warnings(nihstro-headers)
|
||||||
# TODO: For some reason MSVC still applies this warning even with /W0 for externals.
|
if (NOT MSVC)
|
||||||
target_compile_options(nihstro-headers INTERFACE /wd4715)
|
# TODO: For some reason MSYS2 still applied this warnin even with -w
|
||||||
|
target_compile_options(nihstro-headers INTERFACE -Wno-invalid-specialization)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Open Source Archives
|
# Open Source Archives
|
||||||
@ -173,7 +185,8 @@ if (USE_SYSTEM_FFMPEG_HEADERS)
|
|||||||
endif()
|
endif()
|
||||||
if (NOT FOUND_FFMPEG_HEADERS)
|
if (NOT FOUND_FFMPEG_HEADERS)
|
||||||
message(STATUS "Using bundled ffmpeg headers.")
|
message(STATUS "Using bundled ffmpeg headers.")
|
||||||
target_include_directories(library-headers SYSTEM INTERFACE ./library-headers/ffmpeg/include)
|
target_include_directories(library-headers INTERFACE ./library-headers/ffmpeg/include)
|
||||||
|
target_disable_warnings(library-headers)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# SoundTouch
|
# SoundTouch
|
||||||
@ -294,7 +307,8 @@ if (USE_SYSTEM_JSON)
|
|||||||
# Citra uses "#include <json.hpp>" so we have to add this manually
|
# Citra uses "#include <json.hpp>" so we have to add this manually
|
||||||
target_include_directories(json-headers SYSTEM INTERFACE "${NLOHMANN_PREFIX}/nlohmann")
|
target_include_directories(json-headers SYSTEM INTERFACE "${NLOHMANN_PREFIX}/nlohmann")
|
||||||
else()
|
else()
|
||||||
target_include_directories(json-headers SYSTEM INTERFACE ./json)
|
target_include_directories(json-headers INTERFACE ./json)
|
||||||
|
target_disable_warnings(json-headers)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# OpenSSL
|
# OpenSSL
|
||||||
@ -310,7 +324,8 @@ if (NOT OPENSSL_FOUND)
|
|||||||
set(LIBRESSL_SKIP_INSTALL ON CACHE BOOL "")
|
set(LIBRESSL_SKIP_INSTALL ON CACHE BOOL "")
|
||||||
set(OPENSSLDIR "/etc/ssl/")
|
set(OPENSSLDIR "/etc/ssl/")
|
||||||
add_subdirectory(libressl EXCLUDE_FROM_ALL)
|
add_subdirectory(libressl EXCLUDE_FROM_ALL)
|
||||||
target_include_directories(ssl SYSTEM INTERFACE ./libressl/include)
|
target_include_directories(ssl INTERFACE ./libressl/include)
|
||||||
|
target_disable_warnings(ssl)
|
||||||
target_compile_definitions(ssl PRIVATE -DHAVE_INET_NTOP)
|
target_compile_definitions(ssl PRIVATE -DHAVE_INET_NTOP)
|
||||||
get_directory_property(OPENSSL_LIBRARIES
|
get_directory_property(OPENSSL_LIBRARIES
|
||||||
DIRECTORY libressl
|
DIRECTORY libressl
|
||||||
@ -327,17 +342,20 @@ if(USE_SYSTEM_CPP_HTTPLIB)
|
|||||||
get_target_property(HTTP_LIBS httplib::httplib INTERFACE_LINK_LIBRARIES)
|
get_target_property(HTTP_LIBS httplib::httplib INTERFACE_LINK_LIBRARIES)
|
||||||
if(HTTP_LIBS)
|
if(HTTP_LIBS)
|
||||||
message(WARNING "Shared cpp-http (${HTTP_LIBS}) not supported. Falling back to bundled...")
|
message(WARNING "Shared cpp-http (${HTTP_LIBS}) not supported. Falling back to bundled...")
|
||||||
target_include_directories(httplib SYSTEM INTERFACE ./httplib)
|
target_include_directories(httplib INTERFACE ./httplib)
|
||||||
|
target_disable_warnings(httplib)
|
||||||
else()
|
else()
|
||||||
if(CppHttp_FOUND)
|
if(CppHttp_FOUND)
|
||||||
target_link_libraries(httplib INTERFACE httplib::httplib)
|
target_link_libraries(httplib INTERFACE httplib::httplib)
|
||||||
else()
|
else()
|
||||||
message(STATUS "Cpp-httplib not found or not suitable version! Falling back to bundled...")
|
message(STATUS "Cpp-httplib not found or not suitable version! Falling back to bundled...")
|
||||||
target_include_directories(httplib SYSTEM INTERFACE ./httplib)
|
target_include_directories(httplib INTERFACE ./httplib)
|
||||||
|
target_disable_warnings(httplib)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
else()
|
else()
|
||||||
target_include_directories(httplib SYSTEM INTERFACE ./httplib)
|
target_include_directories(httplib INTERFACE ./httplib)
|
||||||
|
target_disable_warnings(httplib)
|
||||||
endif()
|
endif()
|
||||||
target_compile_options(httplib INTERFACE -DCPPHTTPLIB_OPENSSL_SUPPORT)
|
target_compile_options(httplib INTERFACE -DCPPHTTPLIB_OPENSSL_SUPPORT)
|
||||||
target_link_libraries(httplib INTERFACE ${OPENSSL_LIBRARIES})
|
target_link_libraries(httplib INTERFACE ${OPENSSL_LIBRARIES})
|
||||||
@ -354,7 +372,8 @@ if (ENABLE_WEB_SERVICE)
|
|||||||
target_link_libraries(cpp-jwt INTERFACE cpp-jwt::cpp-jwt)
|
target_link_libraries(cpp-jwt INTERFACE cpp-jwt::cpp-jwt)
|
||||||
else()
|
else()
|
||||||
add_library(cpp-jwt INTERFACE)
|
add_library(cpp-jwt INTERFACE)
|
||||||
target_include_directories(cpp-jwt SYSTEM INTERFACE ./cpp-jwt/include)
|
target_include_directories(cpp-jwt INTERFACE ./cpp-jwt/include)
|
||||||
|
target_disable_warnings(cpp-jwt)
|
||||||
target_compile_definitions(cpp-jwt INTERFACE CPP_JWT_USE_VENDORED_NLOHMANN_JSON)
|
target_compile_definitions(cpp-jwt INTERFACE CPP_JWT_USE_VENDORED_NLOHMANN_JSON)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
@ -453,7 +472,8 @@ if (ENABLE_VULKAN)
|
|||||||
endif()
|
endif()
|
||||||
else()
|
else()
|
||||||
add_library(vma INTERFACE)
|
add_library(vma INTERFACE)
|
||||||
target_include_directories(vma SYSTEM INTERFACE ./vma/include)
|
target_include_directories(vma INTERFACE ./vma/include)
|
||||||
|
target_disable_warnings(vma)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# vulkan-headers
|
# vulkan-headers
|
||||||
@ -465,7 +485,8 @@ if (ENABLE_VULKAN)
|
|||||||
target_link_libraries(vulkan-headers INTERFACE Vulkan::Headers)
|
target_link_libraries(vulkan-headers INTERFACE Vulkan::Headers)
|
||||||
endif()
|
endif()
|
||||||
else()
|
else()
|
||||||
target_include_directories(vulkan-headers SYSTEM INTERFACE ./vulkan-headers/include)
|
target_include_directories(vulkan-headers INTERFACE ./vulkan-headers/include)
|
||||||
|
target_disable_warnings(vulkan-headers)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# adrenotools
|
# adrenotools
|
||||||
|
|||||||
2
externals/fmt
vendored
2
externals/fmt
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 123913715afeb8a437e6388b4473fcc4753e1c9a
|
Subproject commit e424e3f2e607da02742f73db84873b8084fc714c
|
||||||
@ -174,6 +174,9 @@ endif()
|
|||||||
if(ENABLE_VULKAN)
|
if(ENABLE_VULKAN)
|
||||||
add_compile_definitions(ENABLE_VULKAN)
|
add_compile_definitions(ENABLE_VULKAN)
|
||||||
endif()
|
endif()
|
||||||
|
if(ENABLE_DEVELOPER_OPTIONS)
|
||||||
|
add_compile_definitions(ENABLE_DEVELOPER_OPTIONS)
|
||||||
|
endif()
|
||||||
|
|
||||||
add_subdirectory(common)
|
add_subdirectory(common)
|
||||||
add_subdirectory(core)
|
add_subdirectory(core)
|
||||||
|
|||||||
@ -64,7 +64,7 @@ android {
|
|||||||
// The application ID refers to Lime3DS to allow for
|
// The application ID refers to Lime3DS to allow for
|
||||||
// the Play Store listing, which was originally set up for Lime3DS, to still be used.
|
// the Play Store listing, which was originally set up for Lime3DS, to still be used.
|
||||||
applicationId = "io.github.lime3ds.android"
|
applicationId = "io.github.lime3ds.android"
|
||||||
minSdk = 28
|
minSdk = 29
|
||||||
targetSdk = 35
|
targetSdk = 35
|
||||||
versionCode = autoVersion
|
versionCode = autoVersion
|
||||||
versionName = getGitVersion()
|
versionName = getGitVersion()
|
||||||
@ -186,7 +186,7 @@ dependencies {
|
|||||||
|
|
||||||
// Download Vulkan Validation Layers from the KhronosGroup GitHub.
|
// Download Vulkan Validation Layers from the KhronosGroup GitHub.
|
||||||
val downloadVulkanValidationLayers = tasks.register<Download>("downloadVulkanValidationLayers") {
|
val downloadVulkanValidationLayers = tasks.register<Download>("downloadVulkanValidationLayers") {
|
||||||
src("https://github.com/KhronosGroup/Vulkan-ValidationLayers/releases/download/vulkan-sdk-1.4.304.1/android-binaries-1.4.304.1.zip")
|
src("https://github.com/KhronosGroup/Vulkan-ValidationLayers/releases/download/vulkan-sdk-1.4.313.0/android-binaries-1.4.313.0.zip")
|
||||||
dest(file("${layout.buildDirectory.get().asFile.path}/tmp/Vulkan-ValidationLayers.zip"))
|
dest(file("${layout.buildDirectory.get().asFile.path}/tmp/Vulkan-ValidationLayers.zip"))
|
||||||
onlyIfModified(true)
|
onlyIfModified(true)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -96,6 +96,24 @@ object NativeLibrary {
|
|||||||
*/
|
*/
|
||||||
external fun onTouchMoved(xAxis: Float, yAxis: Float)
|
external fun onTouchMoved(xAxis: Float, yAxis: Float)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles touch events on the secondary display.
|
||||||
|
*
|
||||||
|
* @param xAxis The value of the x-axis.
|
||||||
|
* @param yAxis The value of the y-axis.
|
||||||
|
* @param pressed To identify if the touch held down or released.
|
||||||
|
* @return true if the pointer is within the touchscreen
|
||||||
|
*/
|
||||||
|
external fun onSecondaryTouchEvent(xAxis: Float, yAxis: Float, pressed: Boolean): Boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles touch movement on the secondary display.
|
||||||
|
*
|
||||||
|
* @param xAxis The value of the instantaneous x-axis.
|
||||||
|
* @param yAxis The value of the instantaneous y-axis.
|
||||||
|
*/
|
||||||
|
external fun onSecondaryTouchMoved(xAxis: Float, yAxis: Float)
|
||||||
|
|
||||||
external fun reloadSettings()
|
external fun reloadSettings()
|
||||||
|
|
||||||
external fun getTitleId(filename: String): Long
|
external fun getTitleId(filename: String): Long
|
||||||
|
|||||||
@ -60,7 +60,15 @@ class EmulationActivity : AppCompatActivity() {
|
|||||||
private lateinit var binding: ActivityEmulationBinding
|
private lateinit var binding: ActivityEmulationBinding
|
||||||
private lateinit var screenAdjustmentUtil: ScreenAdjustmentUtil
|
private lateinit var screenAdjustmentUtil: ScreenAdjustmentUtil
|
||||||
private lateinit var hotkeyUtility: HotkeyUtility
|
private lateinit var hotkeyUtility: HotkeyUtility
|
||||||
private lateinit var secondaryDisplay: SecondaryDisplay;
|
private lateinit var secondaryDisplay: SecondaryDisplay
|
||||||
|
|
||||||
|
private val onShutdown = Runnable {
|
||||||
|
if (intent.getBooleanExtra("launched_from_shortcut", false)) {
|
||||||
|
finishAffinity()
|
||||||
|
} else {
|
||||||
|
this.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private val emulationFragment: EmulationFragment
|
private val emulationFragment: EmulationFragment
|
||||||
get() {
|
get() {
|
||||||
@ -77,8 +85,8 @@ class EmulationActivity : AppCompatActivity() {
|
|||||||
ThemeUtil.setTheme(this)
|
ThemeUtil.setTheme(this)
|
||||||
settingsViewModel.settings.loadSettings()
|
settingsViewModel.settings.loadSettings()
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
secondaryDisplay = SecondaryDisplay(this);
|
secondaryDisplay = SecondaryDisplay(this)
|
||||||
secondaryDisplay.updateDisplay();
|
secondaryDisplay.updateDisplay()
|
||||||
|
|
||||||
binding = ActivityEmulationBinding.inflate(layoutInflater)
|
binding = ActivityEmulationBinding.inflate(layoutInflater)
|
||||||
screenAdjustmentUtil = ScreenAdjustmentUtil(this, windowManager, settingsViewModel.settings)
|
screenAdjustmentUtil = ScreenAdjustmentUtil(this, windowManager, settingsViewModel.settings)
|
||||||
@ -101,13 +109,7 @@ class EmulationActivity : AppCompatActivity() {
|
|||||||
windowManager.defaultDisplay.rotation
|
windowManager.defaultDisplay.rotation
|
||||||
)
|
)
|
||||||
|
|
||||||
EmulationLifecycleUtil.addShutdownHook(hook = {
|
EmulationLifecycleUtil.addShutdownHook(onShutdown)
|
||||||
if (intent.getBooleanExtra("launched_from_shortcut", false)) {
|
|
||||||
finishAffinity()
|
|
||||||
} else {
|
|
||||||
this.finish()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
isEmulationRunning = true
|
isEmulationRunning = true
|
||||||
instance = this
|
instance = this
|
||||||
@ -165,12 +167,12 @@ class EmulationActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
EmulationLifecycleUtil.clear()
|
EmulationLifecycleUtil.removeHook(onShutdown)
|
||||||
NativeLibrary.playTimeManagerStop()
|
NativeLibrary.playTimeManagerStop()
|
||||||
isEmulationRunning = false
|
isEmulationRunning = false
|
||||||
instance = null
|
instance = null
|
||||||
secondaryDisplay.releasePresentation()
|
secondaryDisplay.releasePresentation()
|
||||||
secondaryDisplay.releaseVD();
|
secondaryDisplay.releaseVD()
|
||||||
|
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,29 +11,30 @@ import android.hardware.display.DisplayManager
|
|||||||
import android.hardware.display.VirtualDisplay
|
import android.hardware.display.VirtualDisplay
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.Display
|
import android.view.Display
|
||||||
|
import android.view.MotionEvent
|
||||||
import android.view.Surface
|
import android.view.Surface
|
||||||
import android.view.SurfaceHolder
|
import android.view.SurfaceHolder
|
||||||
import android.view.SurfaceView
|
import android.view.SurfaceView
|
||||||
import org.citra.citra_emu.NativeLibrary
|
import android.view.WindowManager
|
||||||
import org.citra.citra_emu.features.settings.model.IntSetting
|
import org.citra.citra_emu.features.settings.model.IntSetting
|
||||||
|
import org.citra.citra_emu.display.SecondaryDisplayLayout
|
||||||
|
import org.citra.citra_emu.NativeLibrary
|
||||||
|
|
||||||
class SecondaryDisplay(val context: Context) {
|
class SecondaryDisplay(val context: Context) : DisplayManager.DisplayListener {
|
||||||
private var pres: SecondaryDisplayPresentation? = null
|
private var pres: SecondaryDisplayPresentation? = null
|
||||||
private val displayManager = context.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
|
private val displayManager = context.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
|
||||||
private val vd: VirtualDisplay
|
private val vd: VirtualDisplay
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val st = SurfaceTexture(0)
|
|
||||||
st.setDefaultBufferSize(1920, 1080)
|
|
||||||
val vdSurface = Surface(st)
|
|
||||||
vd = displayManager.createVirtualDisplay(
|
vd = displayManager.createVirtualDisplay(
|
||||||
"HiddenDisplay",
|
"HiddenDisplay",
|
||||||
1920,
|
1920,
|
||||||
1080,
|
1080,
|
||||||
320,
|
320,
|
||||||
vdSurface,
|
null,
|
||||||
DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION
|
DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION
|
||||||
)
|
)
|
||||||
|
displayManager.registerDisplayListener(this, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateSurface() {
|
fun updateSurface() {
|
||||||
@ -44,16 +45,23 @@ class SecondaryDisplay(val context: Context) {
|
|||||||
NativeLibrary.secondarySurfaceDestroyed()
|
NativeLibrary.secondarySurfaceDestroyed()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getExternalDisplay(context: Context): Display? {
|
||||||
|
val dm = context.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
|
||||||
|
val internalId = context.display.displayId ?: Display.DEFAULT_DISPLAY
|
||||||
|
val displays = dm.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION)
|
||||||
|
return displays.firstOrNull { it.displayId != internalId && it.name != "HiddenDisplay" }
|
||||||
|
}
|
||||||
|
|
||||||
fun updateDisplay() {
|
fun updateDisplay() {
|
||||||
// decide if we are going to the external display or the internal one
|
// decide if we are going to the external display or the internal one
|
||||||
var display = getCustomerDisplay()
|
var display = getExternalDisplay(context)
|
||||||
if (display == null ||
|
if (display == null ||
|
||||||
IntSetting.SECONDARY_DISPLAY_LAYOUT.int == SecondaryDisplayLayout.NONE.int) {
|
IntSetting.SECONDARY_DISPLAY_LAYOUT.int == SecondaryDisplayLayout.NONE.int) {
|
||||||
display = vd.display
|
display = vd.display
|
||||||
}
|
}
|
||||||
|
|
||||||
// if our presentation is already on the right display, ignore
|
// if our presentation is already on the right display, ignore
|
||||||
if (pres?.display == display) return;
|
if (pres?.display == display) return
|
||||||
|
|
||||||
// otherwise, make a new presentation
|
// otherwise, make a new presentation
|
||||||
releasePresentation()
|
releasePresentation()
|
||||||
@ -61,29 +69,41 @@ class SecondaryDisplay(val context: Context) {
|
|||||||
pres?.show()
|
pres?.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getCustomerDisplay(): Display? {
|
|
||||||
val displays = displayManager.displays
|
|
||||||
// code taken from MelonDS dual screen - should fix odin 2 detection bug
|
|
||||||
return displayManager.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION)
|
|
||||||
.firstOrNull { it.displayId != Display.DEFAULT_DISPLAY && it.name != "Built-in Screen" && it.name != "HiddenDisplay"}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun releasePresentation() {
|
fun releasePresentation() {
|
||||||
pres?.dismiss()
|
pres?.dismiss()
|
||||||
pres = null
|
pres = null
|
||||||
}
|
}
|
||||||
|
|
||||||
fun releaseVD() {
|
fun releaseVD() {
|
||||||
|
displayManager.unregisterDisplayListener(this)
|
||||||
vd.release()
|
vd.release()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onDisplayAdded(displayId: Int) {
|
||||||
|
updateDisplay()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDisplayRemoved(displayId: Int) {
|
||||||
|
updateDisplay()
|
||||||
|
}
|
||||||
|
override fun onDisplayChanged(displayId: Int) {
|
||||||
|
updateDisplay()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
class SecondaryDisplayPresentation(
|
class SecondaryDisplayPresentation(
|
||||||
context: Context, display: Display, val parent: SecondaryDisplay
|
context: Context, display: Display, val parent: SecondaryDisplay
|
||||||
) : Presentation(context, display) {
|
) : Presentation(context, display) {
|
||||||
private lateinit var surfaceView: SurfaceView
|
private lateinit var surfaceView: SurfaceView
|
||||||
|
private var touchscreenPointerId = -1
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
window?.setFlags(
|
||||||
|
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or
|
||||||
|
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
|
||||||
|
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or
|
||||||
|
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
|
||||||
|
)
|
||||||
|
|
||||||
// Initialize SurfaceView
|
// Initialize SurfaceView
|
||||||
surfaceView = SurfaceView(context)
|
surfaceView = SurfaceView(context)
|
||||||
@ -103,6 +123,42 @@ class SecondaryDisplayPresentation(
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
this.surfaceView.setOnTouchListener { _, event ->
|
||||||
|
|
||||||
|
val pointerIndex = event.actionIndex
|
||||||
|
val pointerId = event.getPointerId(pointerIndex)
|
||||||
|
when (event.actionMasked) {
|
||||||
|
MotionEvent.ACTION_DOWN, MotionEvent.ACTION_POINTER_DOWN -> {
|
||||||
|
if (touchscreenPointerId == -1) {
|
||||||
|
touchscreenPointerId = pointerId
|
||||||
|
NativeLibrary.onSecondaryTouchEvent(
|
||||||
|
event.getX(pointerIndex),
|
||||||
|
event.getY(pointerIndex),
|
||||||
|
true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MotionEvent.ACTION_MOVE -> {
|
||||||
|
val index = event.findPointerIndex(touchscreenPointerId)
|
||||||
|
if (index != -1) {
|
||||||
|
NativeLibrary.onSecondaryTouchMoved(
|
||||||
|
event.getX(index),
|
||||||
|
event.getY(index)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MotionEvent.ACTION_UP, MotionEvent.ACTION_POINTER_UP, MotionEvent.ACTION_CANCEL -> {
|
||||||
|
if (pointerId == touchscreenPointerId) {
|
||||||
|
NativeLibrary.onSecondaryTouchEvent(0f, 0f, false)
|
||||||
|
touchscreenPointerId = -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
setContentView(surfaceView) // Set SurfaceView as content
|
setContentView(surfaceView) // Set SurfaceView as content
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -19,13 +19,14 @@ enum class BooleanSetting(
|
|||||||
INSTANT_DEBUG_LOG("instant_debug_log", Settings.SECTION_DEBUG, false),
|
INSTANT_DEBUG_LOG("instant_debug_log", Settings.SECTION_DEBUG, false),
|
||||||
ENABLE_RPC_SERVER("enable_rpc_server", Settings.SECTION_DEBUG, false),
|
ENABLE_RPC_SERVER("enable_rpc_server", Settings.SECTION_DEBUG, false),
|
||||||
CUSTOM_LAYOUT("custom_layout",Settings.SECTION_LAYOUT,false),
|
CUSTOM_LAYOUT("custom_layout",Settings.SECTION_LAYOUT,false),
|
||||||
OVERLAY_SHOW_FPS("overlay_show_fps", Settings.SECTION_LAYOUT, true),
|
PERF_OVERLAY_ENABLE("performance_overlay_enable", Settings.SECTION_LAYOUT, false),
|
||||||
OVERLAY_SHOW_FRAMETIME("overlay_show_frame_time", Settings.SECTION_LAYOUT, false),
|
PERF_OVERLAY_SHOW_FPS("performance_overlay_show_fps", Settings.SECTION_LAYOUT, true),
|
||||||
OVERLAY_SHOW_SPEED("overlay_show_speed", Settings.SECTION_LAYOUT, false),
|
PERF_OVERLAY_SHOW_FRAMETIME("performance_overlay_show_frame_time", Settings.SECTION_LAYOUT, false),
|
||||||
OVERLAY_SHOW_APP_RAM_USAGE("overlay_show_app_ram_usage", Settings.SECTION_LAYOUT, false),
|
PERF_OVERLAY_SHOW_SPEED("performance_overlay_show_speed", Settings.SECTION_LAYOUT, false),
|
||||||
OVERLAY_SHOW_AVAILABLE_RAM("overlay_show_available_ram", Settings.SECTION_LAYOUT, false),
|
PERF_OVERLAY_SHOW_APP_RAM_USAGE("performance_overlay_show_app_ram_usage", Settings.SECTION_LAYOUT, false),
|
||||||
OVERLAY_SHOW_BATTERY_TEMP("overlay_show_battery_temp", Settings.SECTION_LAYOUT, false),
|
PERF_OVERLAY_SHOW_AVAILABLE_RAM("performance_overlay_show_available_ram", Settings.SECTION_LAYOUT, false),
|
||||||
OVERLAY_BACKGROUND("overlay_background", Settings.SECTION_LAYOUT, false),
|
PERF_OVERLAY_SHOW_BATTERY_TEMP("performance_overlay_show_battery_temp", Settings.SECTION_LAYOUT, false),
|
||||||
|
PERF_OVERLAY_BACKGROUND("performance_overlay_background", Settings.SECTION_LAYOUT, false),
|
||||||
DELAY_START_LLE_MODULES("delay_start_for_lle_modules", Settings.SECTION_DEBUG, true),
|
DELAY_START_LLE_MODULES("delay_start_for_lle_modules", Settings.SECTION_DEBUG, true),
|
||||||
DETERMINISTIC_ASYNC_OPERATIONS("deterministic_async_operations", Settings.SECTION_DEBUG, false),
|
DETERMINISTIC_ASYNC_OPERATIONS("deterministic_async_operations", Settings.SECTION_DEBUG, false),
|
||||||
REQUIRED_ONLINE_LLE_MODULES("enable_required_online_lle_modules", Settings.SECTION_SYSTEM, false),
|
REQUIRED_ONLINE_LLE_MODULES("enable_required_online_lle_modules", Settings.SECTION_SYSTEM, false),
|
||||||
@ -49,7 +50,8 @@ enum class BooleanSetting(
|
|||||||
DISABLE_RIGHT_EYE_RENDER("disable_right_eye_render", Settings.SECTION_RENDERER, false),
|
DISABLE_RIGHT_EYE_RENDER("disable_right_eye_render", Settings.SECTION_RENDERER, false),
|
||||||
USE_ARTIC_BASE_CONTROLLER("use_artic_base_controller", Settings.SECTION_CONTROLS, false),
|
USE_ARTIC_BASE_CONTROLLER("use_artic_base_controller", Settings.SECTION_CONTROLS, false),
|
||||||
UPRIGHT_SCREEN("upright_screen", Settings.SECTION_LAYOUT, false),
|
UPRIGHT_SCREEN("upright_screen", Settings.SECTION_LAYOUT, false),
|
||||||
COMPRESS_INSTALLED_CIA_CONTENT("compress_cia_installs", Settings.SECTION_STORAGE, false);
|
COMPRESS_INSTALLED_CIA_CONTENT("compress_cia_installs", Settings.SECTION_STORAGE, false),
|
||||||
|
ANDROID_HIDE_IMAGES("android_hide_images", Settings.SECTION_CORE, false);
|
||||||
|
|
||||||
override var boolean: Boolean = defaultValue
|
override var boolean: Boolean = defaultValue
|
||||||
|
|
||||||
@ -83,6 +85,8 @@ enum class BooleanSetting(
|
|||||||
SHADERS_ACCURATE_MUL,
|
SHADERS_ACCURATE_MUL,
|
||||||
USE_ARTIC_BASE_CONTROLLER,
|
USE_ARTIC_BASE_CONTROLLER,
|
||||||
COMPRESS_INSTALLED_CIA_CONTENT,
|
COMPRESS_INSTALLED_CIA_CONTENT,
|
||||||
|
ANDROID_HIDE_IMAGES,
|
||||||
|
PERF_OVERLAY_ENABLE // Works in overlay options, but not from the settings menu
|
||||||
)
|
)
|
||||||
|
|
||||||
fun from(key: String): BooleanSetting? =
|
fun from(key: String): BooleanSetting? =
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
// Copyright Citra Emulator Project / Lime3DS Emulator Project
|
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
@ -10,6 +10,10 @@ enum class FloatSetting(
|
|||||||
override val defaultValue: Float
|
override val defaultValue: Float
|
||||||
) : AbstractFloatSetting {
|
) : AbstractFloatSetting {
|
||||||
LARGE_SCREEN_PROPORTION("large_screen_proportion",Settings.SECTION_LAYOUT,2.25f),
|
LARGE_SCREEN_PROPORTION("large_screen_proportion",Settings.SECTION_LAYOUT,2.25f),
|
||||||
|
SECOND_SCREEN_OPACITY("custom_second_layer_opacity", Settings.SECTION_RENDERER, 100f),
|
||||||
|
BACKGROUND_RED("bg_red", Settings.SECTION_RENDERER, 0f),
|
||||||
|
BACKGROUND_BLUE("bg_blue", Settings.SECTION_RENDERER, 0f),
|
||||||
|
BACKGROUND_GREEN("bg_green", Settings.SECTION_RENDERER, 0f),
|
||||||
EMPTY_SETTING("", "", 0.0f);
|
EMPTY_SETTING("", "", 0.0f);
|
||||||
|
|
||||||
override var float: Float = defaultValue
|
override var float: Float = defaultValue
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
// Copyright Citra Emulator Project / Lime3DS Emulator Project
|
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
@ -10,7 +10,6 @@ import org.citra.citra_emu.features.settings.model.AbstractSetting
|
|||||||
import org.citra.citra_emu.features.settings.model.FloatSetting
|
import org.citra.citra_emu.features.settings.model.FloatSetting
|
||||||
import org.citra.citra_emu.features.settings.model.ScaledFloatSetting
|
import org.citra.citra_emu.features.settings.model.ScaledFloatSetting
|
||||||
import org.citra.citra_emu.utils.Log
|
import org.citra.citra_emu.utils.Log
|
||||||
import kotlin.math.roundToInt
|
|
||||||
|
|
||||||
class SliderSetting(
|
class SliderSetting(
|
||||||
setting: AbstractSetting?,
|
setting: AbstractSetting?,
|
||||||
@ -27,7 +26,8 @@ class SliderSetting(
|
|||||||
val selectedFloat: Float
|
val selectedFloat: Float
|
||||||
get() {
|
get() {
|
||||||
val setting = setting ?: return defaultValue!!.toFloat()
|
val setting = setting ?: return defaultValue!!.toFloat()
|
||||||
return when (setting) {
|
|
||||||
|
val ret = when (setting) {
|
||||||
is AbstractIntSetting -> setting.int.toFloat()
|
is AbstractIntSetting -> setting.int.toFloat()
|
||||||
is FloatSetting -> setting.float
|
is FloatSetting -> setting.float
|
||||||
is ScaledFloatSetting -> setting.float
|
is ScaledFloatSetting -> setting.float
|
||||||
@ -36,8 +36,8 @@ class SliderSetting(
|
|||||||
-1f
|
-1f
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return ret.coerceIn(min.toFloat(), max.toFloat())
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write a value to the backing int. If that int was previously null,
|
* Write a value to the backing int. If that int was previously null,
|
||||||
* initializes a new one and returns it, so it can be added to the Hashmap.
|
* initializes a new one and returns it, so it can be added to the Hashmap.
|
||||||
|
|||||||
@ -4,14 +4,19 @@
|
|||||||
|
|
||||||
package org.citra.citra_emu.features.settings.ui
|
package org.citra.citra_emu.features.settings.ui
|
||||||
|
|
||||||
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
|
import androidx.documentfile.provider.DocumentFile
|
||||||
|
import org.citra.citra_emu.CitraApplication
|
||||||
import org.citra.citra_emu.NativeLibrary
|
import org.citra.citra_emu.NativeLibrary
|
||||||
import org.citra.citra_emu.features.settings.model.IntSetting
|
import org.citra.citra_emu.features.settings.model.BooleanSetting
|
||||||
import org.citra.citra_emu.features.settings.model.Settings
|
import org.citra.citra_emu.features.settings.model.Settings
|
||||||
import org.citra.citra_emu.utils.SystemSaveGame
|
import org.citra.citra_emu.utils.SystemSaveGame
|
||||||
import org.citra.citra_emu.utils.DirectoryInitialization
|
import org.citra.citra_emu.utils.DirectoryInitialization
|
||||||
|
import org.citra.citra_emu.utils.FileUtil
|
||||||
import org.citra.citra_emu.utils.Log
|
import org.citra.citra_emu.utils.Log
|
||||||
|
import org.citra.citra_emu.utils.PermissionsHandler
|
||||||
import org.citra.citra_emu.utils.TurboHelper
|
import org.citra.citra_emu.utils.TurboHelper
|
||||||
|
|
||||||
class SettingsActivityPresenter(private val activityView: SettingsActivityView) {
|
class SettingsActivityPresenter(private val activityView: SettingsActivityView) {
|
||||||
@ -60,6 +65,32 @@ class SettingsActivityPresenter(private val activityView: SettingsActivityView)
|
|||||||
loadSettingsUI()
|
loadSettingsUI()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun updateAndroidImageVisibility() {
|
||||||
|
val dataDirTreeUri: Uri
|
||||||
|
val dataDirDocument: DocumentFile
|
||||||
|
val nomediaFileDocument: DocumentFile?
|
||||||
|
val nomediaFileExists: Boolean
|
||||||
|
try {
|
||||||
|
dataDirTreeUri = PermissionsHandler.citraDirectory
|
||||||
|
dataDirDocument = DocumentFile.fromTreeUri(CitraApplication.appContext, dataDirTreeUri)!!
|
||||||
|
nomediaFileDocument = dataDirDocument.findFile(".nomedia")
|
||||||
|
nomediaFileExists = (nomediaFileDocument != null)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.error("[SettingsActivity]: Error occurred while trying to find .nomedia, error: " + e.message)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BooleanSetting.ANDROID_HIDE_IMAGES.boolean) {
|
||||||
|
if (!nomediaFileExists) {
|
||||||
|
Log.info("[SettingsActivity]: Attempting to create .nomedia in user data directory")
|
||||||
|
FileUtil.createFile(dataDirTreeUri.toString(), ".nomedia")
|
||||||
|
}
|
||||||
|
} else if (nomediaFileExists) {
|
||||||
|
Log.info("[SettingsActivity]: Attempting to delete .nomedia in user data directory")
|
||||||
|
nomediaFileDocument!!.delete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun onStop(finishing: Boolean) {
|
fun onStop(finishing: Boolean) {
|
||||||
if (finishing && shouldSave) {
|
if (finishing && shouldSave) {
|
||||||
Log.debug("[SettingsActivity] Settings activity stopping. Saving settings to INI...")
|
Log.debug("[SettingsActivity] Settings activity stopping. Saving settings to INI...")
|
||||||
@ -67,6 +98,7 @@ class SettingsActivityPresenter(private val activityView: SettingsActivityView)
|
|||||||
//added to ensure that layout changes take effect as soon as settings window closes
|
//added to ensure that layout changes take effect as soon as settings window closes
|
||||||
NativeLibrary.reloadSettings()
|
NativeLibrary.reloadSettings()
|
||||||
NativeLibrary.updateFramebuffer(NativeLibrary.isPortraitMode)
|
NativeLibrary.updateFramebuffer(NativeLibrary.isPortraitMode)
|
||||||
|
updateAndroidImageVisibility()
|
||||||
TurboHelper.reloadTurbo(false) // TODO: Can this go somewhere else? -OS
|
TurboHelper.reloadTurbo(false) // TODO: Can this go somewhere else? -OS
|
||||||
}
|
}
|
||||||
NativeLibrary.reloadSettings()
|
NativeLibrary.reloadSettings()
|
||||||
|
|||||||
@ -248,6 +248,15 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
|
|||||||
IntSetting.TURBO_LIMIT.defaultValue.toFloat()
|
IntSetting.TURBO_LIMIT.defaultValue.toFloat()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
add(
|
||||||
|
SwitchSetting(
|
||||||
|
BooleanSetting.ANDROID_HIDE_IMAGES,
|
||||||
|
R.string.android_hide_images,
|
||||||
|
R.string.android_hide_images_description,
|
||||||
|
BooleanSetting.ANDROID_HIDE_IMAGES.key,
|
||||||
|
BooleanSetting.ANDROID_HIDE_IMAGES.defaultValue
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1166,6 +1175,89 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
|
|||||||
FloatSetting.LARGE_SCREEN_PROPORTION.defaultValue
|
FloatSetting.LARGE_SCREEN_PROPORTION.defaultValue
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
add(
|
||||||
|
SliderSetting(
|
||||||
|
FloatSetting.SECOND_SCREEN_OPACITY,
|
||||||
|
R.string.second_screen_opacity,
|
||||||
|
R.string.second_screen_opacity_description,
|
||||||
|
0,
|
||||||
|
100,
|
||||||
|
"%",
|
||||||
|
FloatSetting.SECOND_SCREEN_OPACITY.key,
|
||||||
|
FloatSetting.SECOND_SCREEN_OPACITY.defaultValue,
|
||||||
|
isEnabled = IntSetting.SCREEN_LAYOUT.int == 5
|
||||||
|
)
|
||||||
|
)
|
||||||
|
add(HeaderSetting(R.string.bg_color, R.string.bg_color_description))
|
||||||
|
val bgRedSetting = object : AbstractIntSetting {
|
||||||
|
override var int: Int
|
||||||
|
get() = (FloatSetting.BACKGROUND_RED.float * 255).toInt()
|
||||||
|
set(value) {
|
||||||
|
FloatSetting.BACKGROUND_RED.float = value.toFloat() / 255
|
||||||
|
settings.saveSetting(FloatSetting.BACKGROUND_RED, SettingsFile.FILE_NAME_CONFIG)
|
||||||
|
}
|
||||||
|
override val key = null
|
||||||
|
override val section = null
|
||||||
|
override val isRuntimeEditable = false
|
||||||
|
override val valueAsString = int.toString()
|
||||||
|
override val defaultValue = FloatSetting.BACKGROUND_RED.defaultValue
|
||||||
|
}
|
||||||
|
add(
|
||||||
|
SliderSetting(
|
||||||
|
bgRedSetting,
|
||||||
|
R.string.bg_red,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
255,
|
||||||
|
""
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val bgGreenSetting = object : AbstractIntSetting {
|
||||||
|
override var int: Int
|
||||||
|
get() = (FloatSetting.BACKGROUND_GREEN.float * 255).toInt()
|
||||||
|
set(value) {
|
||||||
|
FloatSetting.BACKGROUND_GREEN.float = value.toFloat() / 255
|
||||||
|
settings.saveSetting(FloatSetting.BACKGROUND_GREEN, SettingsFile.FILE_NAME_CONFIG)
|
||||||
|
}
|
||||||
|
override val key = null
|
||||||
|
override val section = null
|
||||||
|
override val isRuntimeEditable = false
|
||||||
|
override val valueAsString = int.toString()
|
||||||
|
override val defaultValue = FloatSetting.BACKGROUND_GREEN.defaultValue
|
||||||
|
}
|
||||||
|
add(
|
||||||
|
SliderSetting(
|
||||||
|
bgGreenSetting,
|
||||||
|
R.string.bg_green,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
255,
|
||||||
|
""
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val bgBlueSetting = object : AbstractIntSetting {
|
||||||
|
override var int: Int
|
||||||
|
get() = (FloatSetting.BACKGROUND_BLUE.float * 255).toInt()
|
||||||
|
set(value) {
|
||||||
|
FloatSetting.BACKGROUND_BLUE.float = value.toFloat() / 255
|
||||||
|
settings.saveSetting(FloatSetting.BACKGROUND_BLUE, SettingsFile.FILE_NAME_CONFIG)
|
||||||
|
}
|
||||||
|
override val key = null
|
||||||
|
override val section = null
|
||||||
|
override val isRuntimeEditable = false
|
||||||
|
override val valueAsString = int.toString()
|
||||||
|
override val defaultValue = FloatSetting.BACKGROUND_BLUE.defaultValue
|
||||||
|
}
|
||||||
|
add(
|
||||||
|
SliderSetting(
|
||||||
|
bgBlueSetting,
|
||||||
|
R.string.bg_blue,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
255,
|
||||||
|
""
|
||||||
|
)
|
||||||
|
)
|
||||||
add(
|
add(
|
||||||
SubmenuSetting(
|
SubmenuSetting(
|
||||||
R.string.performance_overlay_options,
|
R.string.performance_overlay_options,
|
||||||
@ -1201,38 +1293,29 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
|
|||||||
|
|
||||||
add(
|
add(
|
||||||
SwitchSetting(
|
SwitchSetting(
|
||||||
object : AbstractBooleanSetting {
|
BooleanSetting.PERF_OVERLAY_ENABLE,
|
||||||
override val key = "EmulationMenuSettings_showPerfPerformanceOverlay"
|
|
||||||
override val section = Settings.SECTION_LAYOUT
|
|
||||||
override val defaultValue = false
|
|
||||||
override var boolean: Boolean
|
|
||||||
get() = EmulationMenuSettings.showPerformanceOverlay
|
|
||||||
set(value) { EmulationMenuSettings.showPerformanceOverlay = value }
|
|
||||||
override val isRuntimeEditable = true
|
|
||||||
override val valueAsString: String get() = boolean.toString()
|
|
||||||
},
|
|
||||||
R.string.performance_overlay_enable,
|
R.string.performance_overlay_enable,
|
||||||
0,
|
0,
|
||||||
"EmulationMenuSettings_showPerfPerformanceOverlay",
|
BooleanSetting.PERF_OVERLAY_ENABLE.key,
|
||||||
false
|
BooleanSetting.PERF_OVERLAY_ENABLE.defaultValue
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
add(
|
add(
|
||||||
SwitchSetting(
|
SwitchSetting(
|
||||||
BooleanSetting.OVERLAY_BACKGROUND,
|
BooleanSetting.PERF_OVERLAY_BACKGROUND,
|
||||||
R.string.overlay_background,
|
R.string.performance_overlay_background,
|
||||||
R.string.overlay_background_description,
|
R.string.performance_overlay_background_description,
|
||||||
"overlay_background",
|
BooleanSetting.PERF_OVERLAY_BACKGROUND.key,
|
||||||
false
|
BooleanSetting.PERF_OVERLAY_BACKGROUND.defaultValue
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
add(
|
add(
|
||||||
SingleChoiceSetting(
|
SingleChoiceSetting(
|
||||||
IntSetting.PERFORMANCE_OVERLAY_POSITION,
|
IntSetting.PERFORMANCE_OVERLAY_POSITION,
|
||||||
R.string.overlay_position,
|
R.string.performance_overlay_position,
|
||||||
R.string.overlay_position_description,
|
R.string.performance_overlay_position_description,
|
||||||
R.array.statsPosition,
|
R.array.statsPosition,
|
||||||
R.array.statsPositionValues,
|
R.array.statsPositionValues,
|
||||||
)
|
)
|
||||||
@ -1243,61 +1326,61 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
|
|||||||
|
|
||||||
add(
|
add(
|
||||||
SwitchSetting(
|
SwitchSetting(
|
||||||
BooleanSetting.OVERLAY_SHOW_FPS,
|
BooleanSetting.PERF_OVERLAY_SHOW_FPS,
|
||||||
R.string.overlay_show_fps,
|
R.string.performance_overlay_show_fps,
|
||||||
R.string.overlay_show_fps_description,
|
R.string.performance_overlay_show_fps_description,
|
||||||
"overlay_show_fps",
|
BooleanSetting.PERF_OVERLAY_SHOW_FPS.key,
|
||||||
true
|
BooleanSetting.PERF_OVERLAY_SHOW_FPS.defaultValue
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
add(
|
add(
|
||||||
SwitchSetting(
|
SwitchSetting(
|
||||||
BooleanSetting.OVERLAY_SHOW_FRAMETIME,
|
BooleanSetting.PERF_OVERLAY_SHOW_FRAMETIME,
|
||||||
R.string.overlay_show_frametime,
|
R.string.performance_overlay_show_frametime,
|
||||||
R.string.overlay_show_frametime_description,
|
R.string.performance_overlay_show_frametime_description,
|
||||||
"overlay_show_frame_time",
|
BooleanSetting.PERF_OVERLAY_SHOW_FRAMETIME.key,
|
||||||
true
|
BooleanSetting.PERF_OVERLAY_SHOW_FRAMETIME.defaultValue
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
add(
|
add(
|
||||||
SwitchSetting(
|
SwitchSetting(
|
||||||
BooleanSetting.OVERLAY_SHOW_SPEED,
|
BooleanSetting.PERF_OVERLAY_SHOW_SPEED,
|
||||||
R.string.overlay_show_speed,
|
R.string.performance_overlay_show_speed,
|
||||||
R.string.overlay_show_speed_description,
|
R.string.performance_overlay_show_speed_description,
|
||||||
"overlay_show_speed",
|
BooleanSetting.PERF_OVERLAY_SHOW_SPEED.key,
|
||||||
false
|
BooleanSetting.PERF_OVERLAY_SHOW_SPEED.defaultValue
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
add(
|
add(
|
||||||
SwitchSetting(
|
SwitchSetting(
|
||||||
BooleanSetting.OVERLAY_SHOW_APP_RAM_USAGE,
|
BooleanSetting.PERF_OVERLAY_SHOW_APP_RAM_USAGE,
|
||||||
R.string.overlay_show_app_ram_usage,
|
R.string.performance_overlay_show_app_ram_usage,
|
||||||
R.string.overlay_show_app_ram_usage_description,
|
R.string.performance_overlay_show_app_ram_usage_description,
|
||||||
"overlay_show_app_ram_usage",
|
BooleanSetting.PERF_OVERLAY_SHOW_APP_RAM_USAGE.key,
|
||||||
false
|
BooleanSetting.PERF_OVERLAY_SHOW_APP_RAM_USAGE.defaultValue
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
add(
|
add(
|
||||||
SwitchSetting(
|
SwitchSetting(
|
||||||
BooleanSetting.OVERLAY_SHOW_AVAILABLE_RAM,
|
BooleanSetting.PERF_OVERLAY_SHOW_AVAILABLE_RAM,
|
||||||
R.string.overlay_show_available_ram,
|
R.string.performance_overlay_show_available_ram,
|
||||||
R.string.overlay_show_available_ram_description,
|
R.string.performance_overlay_show_available_ram_description,
|
||||||
"overlay_show_available_ram",
|
BooleanSetting.PERF_OVERLAY_SHOW_AVAILABLE_RAM.key,
|
||||||
false
|
BooleanSetting.PERF_OVERLAY_SHOW_AVAILABLE_RAM.defaultValue
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
add(
|
add(
|
||||||
SwitchSetting(
|
SwitchSetting(
|
||||||
BooleanSetting.OVERLAY_SHOW_BATTERY_TEMP,
|
BooleanSetting.PERF_OVERLAY_SHOW_BATTERY_TEMP,
|
||||||
R.string.overlay_show_battery_temp,
|
R.string.performance_overlay_show_battery_temp,
|
||||||
R.string.overlay_show_battery_temp_description,
|
R.string.performance_overlay_show_battery_temp_description,
|
||||||
"overlay_show_battery_temp",
|
BooleanSetting.PERF_OVERLAY_SHOW_BATTERY_TEMP.key,
|
||||||
false
|
BooleanSetting.PERF_OVERLAY_SHOW_BATTERY_TEMP.defaultValue
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -66,6 +66,7 @@ import org.citra.citra_emu.display.ScreenAdjustmentUtil
|
|||||||
import org.citra.citra_emu.display.ScreenLayout
|
import org.citra.citra_emu.display.ScreenLayout
|
||||||
import org.citra.citra_emu.features.settings.model.BooleanSetting
|
import org.citra.citra_emu.features.settings.model.BooleanSetting
|
||||||
import org.citra.citra_emu.features.settings.model.IntSetting
|
import org.citra.citra_emu.features.settings.model.IntSetting
|
||||||
|
import org.citra.citra_emu.features.settings.model.Settings
|
||||||
import org.citra.citra_emu.features.settings.model.SettingsViewModel
|
import org.citra.citra_emu.features.settings.model.SettingsViewModel
|
||||||
import org.citra.citra_emu.features.settings.ui.SettingsActivity
|
import org.citra.citra_emu.features.settings.ui.SettingsActivity
|
||||||
import org.citra.citra_emu.features.settings.utils.SettingsFile
|
import org.citra.citra_emu.features.settings.utils.SettingsFile
|
||||||
@ -100,6 +101,10 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
|
|||||||
|
|
||||||
private val emulationViewModel: EmulationViewModel by activityViewModels()
|
private val emulationViewModel: EmulationViewModel by activityViewModels()
|
||||||
private val settingsViewModel: SettingsViewModel by viewModels()
|
private val settingsViewModel: SettingsViewModel by viewModels()
|
||||||
|
private val settings get() = settingsViewModel.settings
|
||||||
|
|
||||||
|
private val onPause = Runnable{ togglePause() }
|
||||||
|
private val onShutdown = Runnable{ emulationState.stop() }
|
||||||
|
|
||||||
override fun onAttach(context: Context) {
|
override fun onAttach(context: Context) {
|
||||||
super.onAttach(context)
|
super.onAttach(context)
|
||||||
@ -155,9 +160,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
|
|||||||
retainInstance = true
|
retainInstance = true
|
||||||
emulationState = EmulationState(game.path)
|
emulationState = EmulationState(game.path)
|
||||||
emulationActivity = requireActivity() as EmulationActivity
|
emulationActivity = requireActivity() as EmulationActivity
|
||||||
screenAdjustmentUtil = ScreenAdjustmentUtil(requireContext(), requireActivity().windowManager, settingsViewModel.settings)
|
screenAdjustmentUtil = ScreenAdjustmentUtil(requireContext(), requireActivity().windowManager, settings)
|
||||||
EmulationLifecycleUtil.addShutdownHook(hook = { emulationState.stop() })
|
EmulationLifecycleUtil.addPauseResumeHook(onPause)
|
||||||
EmulationLifecycleUtil.addPauseResumeHook(hook = { togglePause() })
|
EmulationLifecycleUtil.addShutdownHook(onShutdown)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
@ -507,6 +512,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
|
|||||||
super.onDetach()
|
super.onDetach()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
EmulationLifecycleUtil.removeHook(onPause)
|
||||||
|
EmulationLifecycleUtil.removeHook(onShutdown)
|
||||||
|
super.onDestroy()
|
||||||
|
}
|
||||||
|
|
||||||
private fun setupCitraDirectoriesThenStartEmulation() {
|
private fun setupCitraDirectoriesThenStartEmulation() {
|
||||||
val directoryInitializationState = DirectoryInitialization.start()
|
val directoryInitializationState = DirectoryInitialization.start()
|
||||||
if (directoryInitializationState ===
|
if (directoryInitializationState ===
|
||||||
@ -662,7 +673,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
|
|||||||
popupMenu.menu.apply {
|
popupMenu.menu.apply {
|
||||||
findItem(R.id.menu_show_overlay).isChecked = EmulationMenuSettings.showOverlay
|
findItem(R.id.menu_show_overlay).isChecked = EmulationMenuSettings.showOverlay
|
||||||
findItem(R.id.menu_performance_overlay_show).isChecked =
|
findItem(R.id.menu_performance_overlay_show).isChecked =
|
||||||
EmulationMenuSettings.showPerformanceOverlay
|
BooleanSetting.PERF_OVERLAY_ENABLE.boolean
|
||||||
findItem(R.id.menu_haptic_feedback).isChecked = EmulationMenuSettings.hapticFeedback
|
findItem(R.id.menu_haptic_feedback).isChecked = EmulationMenuSettings.hapticFeedback
|
||||||
findItem(R.id.menu_emulation_joystick_rel_center).isChecked =
|
findItem(R.id.menu_emulation_joystick_rel_center).isChecked =
|
||||||
EmulationMenuSettings.joystickRelCenter
|
EmulationMenuSettings.joystickRelCenter
|
||||||
@ -679,7 +690,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
|
|||||||
}
|
}
|
||||||
|
|
||||||
R.id.menu_performance_overlay_show -> {
|
R.id.menu_performance_overlay_show -> {
|
||||||
EmulationMenuSettings.showPerformanceOverlay = !EmulationMenuSettings.showPerformanceOverlay
|
BooleanSetting.PERF_OVERLAY_ENABLE.boolean = !BooleanSetting.PERF_OVERLAY_ENABLE.boolean
|
||||||
|
settings.saveSetting(BooleanSetting.PERF_OVERLAY_ENABLE, SettingsFile.FILE_NAME_CONFIG)
|
||||||
updateShowPerformanceOverlay()
|
updateShowPerformanceOverlay()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
@ -1202,7 +1214,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
|
|||||||
perfStatsUpdateHandler.removeCallbacks(perfStatsUpdater!!)
|
perfStatsUpdateHandler.removeCallbacks(perfStatsUpdater!!)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (EmulationMenuSettings.showPerformanceOverlay) {
|
if (BooleanSetting.PERF_OVERLAY_ENABLE.boolean) {
|
||||||
val SYSTEM_FPS = 0
|
val SYSTEM_FPS = 0
|
||||||
val FPS = 1
|
val FPS = 1
|
||||||
val SPEED = 2
|
val SPEED = 2
|
||||||
@ -1217,11 +1229,11 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
|
|||||||
val perfStats = NativeLibrary.getPerfStats()
|
val perfStats = NativeLibrary.getPerfStats()
|
||||||
val dividerString = "\u00A0\u2502 "
|
val dividerString = "\u00A0\u2502 "
|
||||||
if (perfStats[FPS] > 0) {
|
if (perfStats[FPS] > 0) {
|
||||||
if (BooleanSetting.OVERLAY_SHOW_FPS.boolean) {
|
if (BooleanSetting.PERF_OVERLAY_SHOW_FPS.boolean) {
|
||||||
sb.append(String.format("FPS:\u00A0%d", (perfStats[FPS] + 0.5).toInt()))
|
sb.append(String.format("FPS:\u00A0%d", (perfStats[FPS] + 0.5).toInt()))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (BooleanSetting.OVERLAY_SHOW_FRAMETIME.boolean) {
|
if (BooleanSetting.PERF_OVERLAY_SHOW_FRAMETIME.boolean) {
|
||||||
if (sb.isNotEmpty()) sb.append(dividerString)
|
if (sb.isNotEmpty()) sb.append(dividerString)
|
||||||
sb.append(
|
sb.append(
|
||||||
String.format(
|
String.format(
|
||||||
@ -1236,7 +1248,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (BooleanSetting.OVERLAY_SHOW_SPEED.boolean) {
|
if (BooleanSetting.PERF_OVERLAY_SHOW_SPEED.boolean) {
|
||||||
if (sb.isNotEmpty()) sb.append(dividerString)
|
if (sb.isNotEmpty()) sb.append(dividerString)
|
||||||
sb.append(
|
sb.append(
|
||||||
String.format(
|
String.format(
|
||||||
@ -1246,14 +1258,14 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (BooleanSetting.OVERLAY_SHOW_APP_RAM_USAGE.boolean) {
|
if (BooleanSetting.PERF_OVERLAY_SHOW_APP_RAM_USAGE.boolean) {
|
||||||
if (sb.isNotEmpty()) sb.append(dividerString)
|
if (sb.isNotEmpty()) sb.append(dividerString)
|
||||||
val appRamUsage =
|
val appRamUsage =
|
||||||
File("/proc/self/statm").readLines()[0].split(' ')[1].toLong() * 4096 / 1000000
|
File("/proc/self/statm").readLines()[0].split(' ')[1].toLong() * 4096 / 1000000
|
||||||
sb.append("Process\u00A0RAM:\u00A0$appRamUsage\u00A0MB")
|
sb.append("Process\u00A0RAM:\u00A0$appRamUsage\u00A0MB")
|
||||||
}
|
}
|
||||||
|
|
||||||
if (BooleanSetting.OVERLAY_SHOW_AVAILABLE_RAM.boolean) {
|
if (BooleanSetting.PERF_OVERLAY_SHOW_AVAILABLE_RAM.boolean) {
|
||||||
if (sb.isNotEmpty()) sb.append(dividerString)
|
if (sb.isNotEmpty()) sb.append(dividerString)
|
||||||
context?.let { ctx ->
|
context?.let { ctx ->
|
||||||
val activityManager =
|
val activityManager =
|
||||||
@ -1266,14 +1278,14 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (BooleanSetting.OVERLAY_SHOW_BATTERY_TEMP.boolean) {
|
if (BooleanSetting.PERF_OVERLAY_SHOW_BATTERY_TEMP.boolean) {
|
||||||
if (sb.isNotEmpty()) sb.append(dividerString)
|
if (sb.isNotEmpty()) sb.append(dividerString)
|
||||||
val batteryTemp = getBatteryTemperature()
|
val batteryTemp = getBatteryTemperature()
|
||||||
val tempF = celsiusToFahrenheit(batteryTemp)
|
val tempF = celsiusToFahrenheit(batteryTemp)
|
||||||
sb.append(String.format("%.1f°C/%.1f°F", batteryTemp, tempF))
|
sb.append(String.format("%.1f°C/%.1f°F", batteryTemp, tempF))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (BooleanSetting.OVERLAY_BACKGROUND.boolean) {
|
if (BooleanSetting.PERF_OVERLAY_BACKGROUND.boolean) {
|
||||||
binding.performanceOverlayShowText.setBackgroundResource(R.color.citra_transparent_black)
|
binding.performanceOverlayShowText.setBackgroundResource(R.color.citra_transparent_black)
|
||||||
} else {
|
} else {
|
||||||
binding.performanceOverlayShowText.setBackgroundResource(0)
|
binding.performanceOverlayShowText.setBackgroundResource(0)
|
||||||
|
|||||||
@ -153,8 +153,7 @@ class InputOverlay(context: Context?, attrs: AttributeSet?) : SurfaceView(contex
|
|||||||
if (isActionMove) {
|
if (isActionMove) {
|
||||||
NativeLibrary.onTouchMoved(xPosition.toFloat(), yPosition.toFloat())
|
NativeLibrary.onTouchMoved(xPosition.toFloat(), yPosition.toFloat())
|
||||||
continue
|
continue
|
||||||
}
|
} else if (isActionUp) {
|
||||||
else if (isActionUp) {
|
|
||||||
NativeLibrary.onTouchEvent(0f, 0f, false)
|
NativeLibrary.onTouchEvent(0f, 0f, false)
|
||||||
break // Up and down actions shouldn't loop
|
break // Up and down actions shouldn't loop
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2023 Citra Emulator Project
|
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
@ -18,15 +18,27 @@ object EmulationLifecycleUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun addShutdownHook(hook: Runnable) {
|
fun addShutdownHook(hook: Runnable) {
|
||||||
|
if (shutdownHooks.contains(hook)) {
|
||||||
|
Log.warning("[EmulationLifecycleUtil] Tried to add shutdown hook for function that already existed. Skipping.")
|
||||||
|
} else {
|
||||||
shutdownHooks.add(hook)
|
shutdownHooks.add(hook)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun addPauseResumeHook(hook: Runnable) {
|
fun addPauseResumeHook(hook: Runnable) {
|
||||||
|
if (pauseResumeHooks.contains(hook)) {
|
||||||
|
Log.warning("[EmulationLifecycleUtil] Tried to add pause resume hook for function that already existed. Skipping.")
|
||||||
|
} else {
|
||||||
pauseResumeHooks.add(hook)
|
pauseResumeHooks.add(hook)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun clear() {
|
fun removeHook(hook: Runnable) {
|
||||||
pauseResumeHooks.clear()
|
if (pauseResumeHooks.contains(hook)) {
|
||||||
shutdownHooks.clear()
|
pauseResumeHooks.remove(hook)
|
||||||
|
}
|
||||||
|
if (shutdownHooks.contains(hook)) {
|
||||||
|
shutdownHooks.remove(hook)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -35,13 +35,6 @@ object EmulationMenuSettings {
|
|||||||
.apply()
|
.apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
var showPerformanceOverlay: Boolean
|
|
||||||
get() = preferences.getBoolean("EmulationMenuSettings_showPerformanceOverlay", false)
|
|
||||||
set(value) {
|
|
||||||
preferences.edit()
|
|
||||||
.putBoolean("EmulationMenuSettings_showPerformanceOverlay", value)
|
|
||||||
.apply()
|
|
||||||
}
|
|
||||||
var hapticFeedback: Boolean
|
var hapticFeedback: Boolean
|
||||||
get() = preferences.getBoolean("EmulationMenuSettings_HapticFeedback", true)
|
get() = preferences.getBoolean("EmulationMenuSettings_HapticFeedback", true)
|
||||||
set(value) {
|
set(value) {
|
||||||
|
|||||||
@ -172,6 +172,7 @@ void Config::ReadValues() {
|
|||||||
ReadSetting("Renderer", Settings::values.bg_red);
|
ReadSetting("Renderer", Settings::values.bg_red);
|
||||||
ReadSetting("Renderer", Settings::values.bg_green);
|
ReadSetting("Renderer", Settings::values.bg_green);
|
||||||
ReadSetting("Renderer", Settings::values.bg_blue);
|
ReadSetting("Renderer", Settings::values.bg_blue);
|
||||||
|
ReadSetting("Renderer", Settings::values.custom_second_layer_opacity);
|
||||||
ReadSetting("Renderer", Settings::values.delay_game_render_thread_us);
|
ReadSetting("Renderer", Settings::values.delay_game_render_thread_us);
|
||||||
ReadSetting("Renderer", Settings::values.disable_right_eye_render);
|
ReadSetting("Renderer", Settings::values.disable_right_eye_render);
|
||||||
|
|
||||||
|
|||||||
@ -170,6 +170,9 @@ bg_red =
|
|||||||
bg_blue =
|
bg_blue =
|
||||||
bg_green =
|
bg_green =
|
||||||
|
|
||||||
|
# Opacity of second layer when using custom layout option (bottom screen unless swapped). Useful if positioning on top of the first layer.
|
||||||
|
custom_second_layer_opacity =
|
||||||
|
|
||||||
# Whether and how Stereoscopic 3D should be rendered
|
# Whether and how Stereoscopic 3D should be rendered
|
||||||
# 0 (default): Off, 1: Side by Side, 2: Reverse Side by Side, 3: Anaglyph, 4: Interlaced, 5: Reverse Interlaced, 6: Cardboard VR
|
# 0 (default): Off, 1: Side by Side, 2: Reverse Side by Side, 3: Anaglyph, 4: Interlaced, 5: Reverse Interlaced, 6: Cardboard VR
|
||||||
render_3d =
|
render_3d =
|
||||||
|
|||||||
@ -657,6 +657,25 @@ void Java_org_citra_citra_1emu_NativeLibrary_onTouchMoved([[maybe_unused]] JNIEn
|
|||||||
window->OnTouchMoved((int)x, (int)y);
|
window->OnTouchMoved((int)x, (int)y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
jboolean Java_org_citra_citra_1emu_NativeLibrary_onSecondaryTouchEvent([[maybe_unused]] JNIEnv* env,
|
||||||
|
[[maybe_unused]] jobject obj,
|
||||||
|
jfloat x, jfloat y,
|
||||||
|
jboolean pressed) {
|
||||||
|
if (!secondary_window) {
|
||||||
|
return JNI_FALSE;
|
||||||
|
}
|
||||||
|
return static_cast<jboolean>(secondary_window->OnTouchEvent(
|
||||||
|
static_cast<int>(x + 0.5), static_cast<int>(y + 0.5), pressed));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Java_org_citra_citra_1emu_NativeLibrary_onSecondaryTouchMoved([[maybe_unused]] JNIEnv* env,
|
||||||
|
[[maybe_unused]] jobject obj,
|
||||||
|
jfloat x, jfloat y) {
|
||||||
|
if (secondary_window) {
|
||||||
|
secondary_window->OnTouchMoved((int)x, (int)y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
jlong Java_org_citra_citra_1emu_NativeLibrary_getTitleId(JNIEnv* env, [[maybe_unused]] jobject obj,
|
jlong Java_org_citra_citra_1emu_NativeLibrary_getTitleId(JNIEnv* env, [[maybe_unused]] jobject obj,
|
||||||
jstring j_filename) {
|
jstring j_filename) {
|
||||||
std::string filepath = GetJString(env, j_filename);
|
std::string filepath = GetJString(env, j_filename);
|
||||||
|
|||||||
@ -148,12 +148,12 @@
|
|||||||
</integer-array>
|
</integer-array>
|
||||||
|
|
||||||
<string-array name="statsPosition">
|
<string-array name="statsPosition">
|
||||||
<item>@string/overlay_position_top_left</item>
|
<item>@string/performance_overlay_position_top_left</item>
|
||||||
<item>@string/overlay_position_center_top</item>
|
<item>@string/performance_overlay_position_center_top</item>
|
||||||
<item>@string/overlay_position_top_right</item>
|
<item>@string/performance_overlay_position_top_right</item>
|
||||||
<item>@string/overlay_position_bottom_left</item>
|
<item>@string/performance_overlay_position_bottom_left</item>
|
||||||
<item>@string/overlay_position_center_bottom</item>
|
<item>@string/performance_overlay_position_center_bottom</item>
|
||||||
<item>@string/overlay_position_bottom_right</item>
|
<item>@string/performance_overlay_position_bottom_right</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
<integer-array name="statsPositionValues">
|
<integer-array name="statsPositionValues">
|
||||||
<item>0</item>
|
<item>0</item>
|
||||||
|
|||||||
@ -238,25 +238,27 @@
|
|||||||
<string name="spirv_shader_gen_description">Emits the fragment shader used to emulate PICA using SPIR-V instead of GLSL</string>
|
<string name="spirv_shader_gen_description">Emits the fragment shader used to emulate PICA using SPIR-V instead of GLSL</string>
|
||||||
<string name="disable_spirv_optimizer">Disable SPIR-V Optimizer</string>
|
<string name="disable_spirv_optimizer">Disable SPIR-V Optimizer</string>
|
||||||
<string name="disable_spirv_optimizer_description">Disables the SPIR-V optimization pass, reducing stuttering considerably while barely affecting performance.</string>
|
<string name="disable_spirv_optimizer_description">Disables the SPIR-V optimization pass, reducing stuttering considerably while barely affecting performance.</string>
|
||||||
<string name="async_shaders">Enable asynchronous shader compilation</string>
|
<string name="async_shaders">Enable Asynchronous Shader Compilation</string>
|
||||||
<string name="async_shaders_description">Compiles shaders in the background to reduce stuttering during gameplay. When enabled expect temporary graphical glitches</string>
|
<string name="async_shaders_description">Compiles shaders in the background to reduce stuttering during gameplay. When enabled expect temporary graphical glitches</string>
|
||||||
<string name="linear_filtering">Linear Filtering</string>
|
<string name="linear_filtering">Linear Filtering</string>
|
||||||
<string name="linear_filtering_description">Enables linear filtering, which causes game visuals to appear smoother.</string>
|
<string name="linear_filtering_description">Enables linear filtering, which causes game visuals to appear smoother.</string>
|
||||||
<string name="texture_filter_name">Texture Filter</string>
|
<string name="texture_filter_name">Texture Filter</string>
|
||||||
<string name="texture_filter_description">Enhances the visuals of applications by applying a filter to textures. The supported filters are Anime4K Ultrafast, Bicubic, ScaleForce, xBRZ freescale, and MMPX.</string>
|
<string name="texture_filter_description">Enhances the visuals of applications by applying a filter to textures. The supported filters are Anime4K Ultrafast, Bicubic, ScaleForce, xBRZ freescale, and MMPX.</string>
|
||||||
<string name="delay_render_thread">Delay game render thread</string>
|
<string name="delay_render_thread">Delay Game Render Thread</string>
|
||||||
<string name="delay_render_thread_description">Delay the game render thread when it submits data to the GPU. Helps with performance issues in the (very few) applications with dynamic framerates.</string>
|
<string name="delay_render_thread_description">Delay the game render thread when it submits data to the GPU. Helps with performance issues in the (very few) applications with dynamic framerates.</string>
|
||||||
<string name="advanced">Advanced</string>
|
<string name="advanced">Advanced</string>
|
||||||
<string name="texture_sampling_name">Texture Sampling</string>
|
<string name="texture_sampling_name">Texture Sampling</string>
|
||||||
<string name="texture_sampling_description">Overrides the sampling filter used by games. This can be useful in certain cases with poorly behaved games when upscaling. If unsure, set this to Game Controlled.</string>
|
<string name="texture_sampling_description">Overrides the sampling filter used by games. This can be useful in certain cases with poorly behaved games when upscaling. If unsure, set this to Game Controlled.</string>
|
||||||
<string name="shaders_accurate_mul">Accurate Multiplication</string>
|
<string name="shaders_accurate_mul">Accurate Multiplication</string>
|
||||||
<string name="shaders_accurate_mul_description">Uses more accurate multiplication in hardware shaders, which may fix some graphical bugs. When enabled, performance will be reduced.</string>
|
<string name="shaders_accurate_mul_description">Uses more accurate multiplication in hardware shaders, which may fix some graphical bugs. When enabled, performance will be reduced.</string>
|
||||||
<string name="asynchronous_gpu">Enable asynchronous GPU emulation</string>
|
<string name="asynchronous_gpu">Enable Asynchronous GPU Emulation</string>
|
||||||
<string name="asynchronous_gpu_description">Uses a separate thread to emulate the GPU asynchronously. When enabled, performance will be improved.</string>
|
<string name="asynchronous_gpu_description">Uses a separate thread to emulate the GPU asynchronously. When enabled, performance will be improved.</string>
|
||||||
<string name="frame_limit_enable">Limit Speed</string>
|
<string name="frame_limit_enable">Limit Speed</string>
|
||||||
<string name="frame_limit_enable_description">When enabled, emulation speed will be limited to a specified percentage of normal speed. If disabled, emulation speed will be uncapped and the turbo speed hotkey will not work.</string>
|
<string name="frame_limit_enable_description">When enabled, emulation speed will be limited to a specified percentage of normal speed. If disabled, emulation speed will be uncapped and the turbo speed hotkey will not work.</string>
|
||||||
<string name="frame_limit_slider">Limit Speed Percent</string>
|
<string name="frame_limit_slider">Limit Speed Percent</string>
|
||||||
<string name="frame_limit_slider_description">Specifies the percentage to limit emulation speed. With the default of 100% emulation will be limited to normal speed. Values higher or lower will increase or decrease the speed limit.</string>
|
<string name="frame_limit_slider_description">Specifies the percentage to limit emulation speed. With the default of 100% emulation will be limited to normal speed. Values higher or lower will increase or decrease the speed limit.</string>
|
||||||
|
<string name="android_hide_images">Hide 3DS Images from Android</string>
|
||||||
|
<string name="android_hide_images_description">Prevent 3DS camera, screenshot, and custom texture images from being indexed by Android and displayed in the gallery. Your device may need to be rebooted after changing this setting to take effect.</string>
|
||||||
<string name="turbo_limit">Turbo Speed Limit</string>
|
<string name="turbo_limit">Turbo Speed Limit</string>
|
||||||
<string name="turbo_limit_description">Emulation speed limit used while the turbo hotkey is active.</string>
|
<string name="turbo_limit_description">Emulation speed limit used while the turbo hotkey is active.</string>
|
||||||
<string name="expand_to_cutout_area">Expand to Cutout Area</string>
|
<string name="expand_to_cutout_area">Expand to Cutout Area</string>
|
||||||
@ -306,7 +308,7 @@
|
|||||||
<string name="audio_volume">Volume</string>
|
<string name="audio_volume">Volume</string>
|
||||||
<string name="audio_stretch">Audio Stretching</string>
|
<string name="audio_stretch">Audio Stretching</string>
|
||||||
<string name="audio_stretch_description">Stretches audio to reduce stuttering. When enabled, increases audio latency and slightly reduces performance.</string>
|
<string name="audio_stretch_description">Stretches audio to reduce stuttering. When enabled, increases audio latency and slightly reduces performance.</string>
|
||||||
<string name="realtime_audio">Enable realtime audio</string>
|
<string name="realtime_audio">Enable Realtime Audio</string>
|
||||||
<string name="realtime_audio_description">Scales audio playback speed to account for drops in emulation framerate. This means that audio will play at full speed even while the game framerate is low. May cause audio desync issues.</string>
|
<string name="realtime_audio_description">Scales audio playback speed to account for drops in emulation framerate. This means that audio will play at full speed even while the game framerate is low. May cause audio desync issues.</string>
|
||||||
<string name="audio_input_type">Audio Input Device</string>
|
<string name="audio_input_type">Audio Input Device</string>
|
||||||
<string name="sound_output_mode">Sound Output Mode</string>
|
<string name="sound_output_mode">Sound Output Mode</string>
|
||||||
@ -321,9 +323,9 @@
|
|||||||
<string name="vsync_description">Synchronizes the game frame rate to the refresh rate of your device.</string>
|
<string name="vsync_description">Synchronizes the game frame rate to the refresh rate of your device.</string>
|
||||||
<string name="renderer_debug">Debug Renderer</string>
|
<string name="renderer_debug">Debug Renderer</string>
|
||||||
<string name="renderer_debug_description">Log additional graphics related debug information. When enabled, game performance will be significantly reduced.</string>
|
<string name="renderer_debug_description">Log additional graphics related debug information. When enabled, game performance will be significantly reduced.</string>
|
||||||
<string name="instant_debug_log">Flush log output on every message</string>
|
<string name="instant_debug_log">Flush Log Output on Every Message</string>
|
||||||
<string name="instant_debug_log_description">Immediately commits the debug log to file. Use this if Azahar crashes and the log output is being cut.</string>
|
<string name="instant_debug_log_description">Immediately commits the debug log to file. Use this if Azahar crashes and the log output is being cut.</string>
|
||||||
<string name="delay_start_lle_modules">Delay start with LLE modules</string>
|
<string name="delay_start_lle_modules">Delay Start With LLE Modules</string>
|
||||||
<string name="delay_start_lle_modules_description">Delays the start of the app when LLE modules are enabled.</string>
|
<string name="delay_start_lle_modules_description">Delays the start of the app when LLE modules are enabled.</string>
|
||||||
<string name="deterministic_async_operations">Deterministic Async Operations</string>
|
<string name="deterministic_async_operations">Deterministic Async Operations</string>
|
||||||
<string name="deterministic_async_operations_description">Makes async operations deterministic for debugging. Enabling this may cause freezes.</string>
|
<string name="deterministic_async_operations_description">Makes async operations deterministic for debugging. Enabling this may cause freezes.</string>
|
||||||
@ -448,6 +450,13 @@
|
|||||||
<string name="emulation_portrait_layout_top_full">Default</string>
|
<string name="emulation_portrait_layout_top_full">Default</string>
|
||||||
<string name="emulation_secondary_display_default">System Default (mirror)</string>
|
<string name="emulation_secondary_display_default">System Default (mirror)</string>
|
||||||
<string name="emulation_screen_layout_custom">Custom Layout</string>
|
<string name="emulation_screen_layout_custom">Custom Layout</string>
|
||||||
|
<string name="bg_color">Background Color</string>
|
||||||
|
<string name="bg_color_description">The color which appears behind the screens during emulation, represented as an RGB value.</string>
|
||||||
|
<string name="bg_red">Red</string>
|
||||||
|
<string name="bg_green">Green</string>
|
||||||
|
<string name="bg_blue">Blue</string>
|
||||||
|
<string name="second_screen_opacity">Custom Layout Second Screen Opacity</string>
|
||||||
|
<string name="second_screen_opacity_description">The opacity of the second 3DS screen when using a custom screen layout. Useful if the second screen is to be positioned on top of the first screen.</string>
|
||||||
<string name="emulation_small_screen_position">Small Screen Position</string>
|
<string name="emulation_small_screen_position">Small Screen Position</string>
|
||||||
<string name="small_screen_position_description">Where should the small screen appear relative to the large one in Large Screen Layout?</string>
|
<string name="small_screen_position_description">Where should the small screen appear relative to the large one in Large Screen Layout?</string>
|
||||||
<string name="small_screen_position_top_right">Top Right</string>
|
<string name="small_screen_position_top_right">Top Right</string>
|
||||||
@ -576,28 +585,28 @@
|
|||||||
<string name="performance_overlay_options">Performance Overlay</string>
|
<string name="performance_overlay_options">Performance Overlay</string>
|
||||||
<string name="performance_overlay_enable">Enable Performance Overlay</string>
|
<string name="performance_overlay_enable">Enable Performance Overlay</string>
|
||||||
<string name="performance_overlay_options_description">Configure whether the performance overlay is shown and what information is displayed.</string>
|
<string name="performance_overlay_options_description">Configure whether the performance overlay is shown and what information is displayed.</string>
|
||||||
<string name="overlay_show_fps">Show FPS</string>
|
<string name="performance_overlay_show_fps">Show FPS</string>
|
||||||
<string name="overlay_show_fps_description">Display current frames per second.</string>
|
<string name="performance_overlay_show_fps_description">Display current frames per second.</string>
|
||||||
<string name="overlay_show_frametime">Show Frametime</string>
|
<string name="performance_overlay_show_frametime">Show Frametime</string>
|
||||||
<string name="overlay_show_frametime_description">Display current frametime.</string>
|
<string name="performance_overlay_show_frametime_description">Display current frametime.</string>
|
||||||
<string name="overlay_show_speed">Show Speed</string>
|
<string name="performance_overlay_show_speed">Show Speed</string>
|
||||||
<string name="overlay_show_speed_description">Display current emulation speed percentage.</string>
|
<string name="performance_overlay_show_speed_description">Display current emulation speed percentage.</string>
|
||||||
<string name="overlay_show_app_ram_usage">Show App Memory Usage</string>
|
<string name="performance_overlay_show_app_ram_usage">Show App Memory Usage</string>
|
||||||
<string name="overlay_show_app_ram_usage_description">Display the amount of RAM getting used by the emulator.</string>
|
<string name="performance_overlay_show_app_ram_usage_description">Display the amount of RAM getting used by the emulator.</string>
|
||||||
<string name="overlay_show_available_ram">Show Available Memory</string>
|
<string name="performance_overlay_show_available_ram">Show Available Memory</string>
|
||||||
<string name="overlay_show_available_ram_description">Display the amount of RAM which is available.</string>
|
<string name="performance_overlay_show_available_ram_description">Display the amount of RAM which is available.</string>
|
||||||
<string name="overlay_show_battery_temp">Show Battery Temperature</string>
|
<string name="performance_overlay_show_battery_temp">Show Battery Temperature</string>
|
||||||
<string name="overlay_show_battery_temp_description">Display current Battery temperature in Celsius and Fahrenheit.</string>
|
<string name="performance_overlay_show_battery_temp_description">Display current Battery temperature in Celsius and Fahrenheit.</string>
|
||||||
<string name="overlay_position">Overlay Position</string>
|
<string name="performance_overlay_position">Overlay Position</string>
|
||||||
<string name="overlay_position_description">Choose where the performance overlay is displayed on the screen.</string>
|
<string name="performance_overlay_position_description">Choose where the performance overlay is displayed on the screen.</string>
|
||||||
<string name="overlay_position_top_left">Top Left</string>
|
<string name="performance_overlay_position_top_left">Top Left</string>
|
||||||
<string name="overlay_position_top_right">Top Right</string>
|
<string name="performance_overlay_position_top_right">Top Right</string>
|
||||||
<string name="overlay_position_bottom_left">Bottom Left</string>
|
<string name="performance_overlay_position_bottom_left">Bottom Left</string>
|
||||||
<string name="overlay_position_bottom_right">Bottom Right</string>
|
<string name="performance_overlay_position_bottom_right">Bottom Right</string>
|
||||||
<string name="overlay_position_center_top">Center Top</string>
|
<string name="performance_overlay_position_center_top">Center Top</string>
|
||||||
<string name="overlay_position_center_bottom">Center Bottom</string>
|
<string name="performance_overlay_position_center_bottom">Center Bottom</string>
|
||||||
<string name="overlay_background">Overlay Background</string>
|
<string name="performance_overlay_background">Overlay Background</string>
|
||||||
<string name="overlay_background_description">Adds a background behind the overlay for easier reading.</string>
|
<string name="performance_overlay_background_description">Adds a background behind the overlay for easier reading.</string>
|
||||||
|
|
||||||
<!-- Cheats -->
|
<!-- Cheats -->
|
||||||
<string name="cheats">Cheats</string>
|
<string name="cheats">Cheats</string>
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2016 Citra Emulator Project
|
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
namespace AudioCore {
|
namespace AudioCore {
|
||||||
|
|
||||||
constexpr char auto_device_name[] = "auto";
|
constexpr char auto_device_name[] = "Auto";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class is an interface for an audio sink. An audio sink accepts samples in stereo signed
|
* This class is an interface for an audio sink. An audio sink accepts samples in stereo signed
|
||||||
|
|||||||
@ -60,6 +60,10 @@ if (ENABLE_QT AND UNIX AND NOT APPLE)
|
|||||||
target_link_libraries(citra_meta PRIVATE Qt6::DBus gamemode)
|
target_link_libraries(citra_meta PRIVATE Qt6::DBus gamemode)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if (ENABLE_QT AND APPLE)
|
||||||
|
target_link_libraries(citra_meta PRIVATE Qt6::GuiPrivate)
|
||||||
|
endif()
|
||||||
|
|
||||||
if (ENABLE_QT AND USE_DISCORD_PRESENCE)
|
if (ENABLE_QT AND USE_DISCORD_PRESENCE)
|
||||||
target_link_libraries(citra_meta PRIVATE discord-rpc)
|
target_link_libraries(citra_meta PRIVATE discord-rpc)
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@ -172,12 +172,12 @@ add_library(citra_qt STATIC EXCLUDE_FROM_ALL
|
|||||||
multiplayer/state.h
|
multiplayer/state.h
|
||||||
multiplayer/validation.h
|
multiplayer/validation.h
|
||||||
precompiled_headers.h
|
precompiled_headers.h
|
||||||
|
qt_image_interface.cpp
|
||||||
|
qt_image_interface.h
|
||||||
uisettings.cpp
|
uisettings.cpp
|
||||||
uisettings.h
|
uisettings.h
|
||||||
user_data_migration.cpp
|
user_data_migration.cpp
|
||||||
user_data_migration.h
|
user_data_migration.h
|
||||||
qt_image_interface.cpp
|
|
||||||
qt_image_interface.h
|
|
||||||
util/clickable_label.cpp
|
util/clickable_label.cpp
|
||||||
util/clickable_label.h
|
util/clickable_label.h
|
||||||
util/graphics_device_info.cpp
|
util/graphics_device_info.cpp
|
||||||
@ -190,6 +190,13 @@ add_library(citra_qt STATIC EXCLUDE_FROM_ALL
|
|||||||
util/util.h
|
util/util.h
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (APPLE)
|
||||||
|
target_sources(citra_qt PUBLIC
|
||||||
|
qt_swizzle.h
|
||||||
|
qt_swizzle.mm
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
file(GLOB COMPAT_LIST
|
file(GLOB COMPAT_LIST
|
||||||
${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc
|
${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc
|
||||||
${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
|
${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
|
||||||
@ -272,7 +279,12 @@ endif()
|
|||||||
|
|
||||||
if (NOT WIN32)
|
if (NOT WIN32)
|
||||||
find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
|
find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
|
||||||
|
if(Qt6_VERSION VERSION_GREATER_EQUAL "6.10.0")
|
||||||
|
find_package(Qt6 REQUIRED COMPONENTS GuiPrivate)
|
||||||
|
target_link_libraries(citra_qt PRIVATE Qt6::GuiPrivate)
|
||||||
|
else()
|
||||||
target_include_directories(citra_qt PRIVATE ${Qt6Gui_PRIVATE_INCLUDE_DIRS})
|
target_include_directories(citra_qt PRIVATE ${Qt6Gui_PRIVATE_INCLUDE_DIRS})
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (UNIX AND NOT APPLE)
|
if (UNIX AND NOT APPLE)
|
||||||
|
|||||||
@ -69,6 +69,7 @@
|
|||||||
#include "citra_qt/movie/movie_record_dialog.h"
|
#include "citra_qt/movie/movie_record_dialog.h"
|
||||||
#include "citra_qt/multiplayer/state.h"
|
#include "citra_qt/multiplayer/state.h"
|
||||||
#include "citra_qt/qt_image_interface.h"
|
#include "citra_qt/qt_image_interface.h"
|
||||||
|
#include "citra_qt/qt_swizzle.h"
|
||||||
#include "citra_qt/uisettings.h"
|
#include "citra_qt/uisettings.h"
|
||||||
#include "common/play_time_manager.h"
|
#include "common/play_time_manager.h"
|
||||||
#ifdef ENABLE_QT_UPDATE_CHECKER
|
#ifdef ENABLE_QT_UPDATE_CHECKER
|
||||||
@ -979,6 +980,10 @@ void GMainWindow::ConnectWidgetEvents() {
|
|||||||
connect(game_list, &GameList::ShowList, this, &GMainWindow::OnGameListShowList);
|
connect(game_list, &GameList::ShowList, this, &GMainWindow::OnGameListShowList);
|
||||||
connect(game_list, &GameList::PopulatingCompleted, this,
|
connect(game_list, &GameList::PopulatingCompleted, this,
|
||||||
[this] { multiplayer_state->UpdateGameList(game_list->GetModel()); });
|
[this] { multiplayer_state->UpdateGameList(game_list->GetModel()); });
|
||||||
|
#ifdef ENABLE_DEVELOPER_OPTIONS
|
||||||
|
connect(game_list, &GameList::StartingLaunchStressTest, this,
|
||||||
|
&GMainWindow::StartLaunchStressTest);
|
||||||
|
#endif
|
||||||
|
|
||||||
connect(game_list, &GameList::OpenPerGameGeneralRequested, this,
|
connect(game_list, &GameList::OpenPerGameGeneralRequested, this,
|
||||||
&GMainWindow::OnGameListOpenPerGameProperties);
|
&GMainWindow::OnGameListOpenPerGameProperties);
|
||||||
@ -1575,6 +1580,16 @@ void GMainWindow::ShutdownGame() {
|
|||||||
secondary_window->ReleaseRenderTarget();
|
secondary_window->ReleaseRenderTarget();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GMainWindow::StartLaunchStressTest(const QString& game_path) {
|
||||||
|
QThreadPool::globalInstance()->start([this, game_path] {
|
||||||
|
do {
|
||||||
|
ui->action_Stop->trigger();
|
||||||
|
emit game_list->GameChosen(game_path);
|
||||||
|
QThread::sleep(2);
|
||||||
|
} while (emulation_running);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void GMainWindow::StoreRecentFile(const QString& filename) {
|
void GMainWindow::StoreRecentFile(const QString& filename) {
|
||||||
UISettings::values.recent_files.prepend(filename);
|
UISettings::values.recent_files.prepend(filename);
|
||||||
UISettings::values.recent_files.removeDuplicates();
|
UISettings::values.recent_files.removeDuplicates();
|
||||||
@ -4112,6 +4127,11 @@ static Qt::HighDpiScaleFactorRoundingPolicy GetHighDpiRoundingPolicy() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void LaunchQtFrontend(int argc, char* argv[]) {
|
void LaunchQtFrontend(int argc, char* argv[]) {
|
||||||
|
#ifdef __APPLE__
|
||||||
|
// Ensure that the linker doesn't optimize qt_swizzle.mm out of existence.
|
||||||
|
QtSwizzle::Dummy();
|
||||||
|
#endif
|
||||||
|
|
||||||
Common::DetachedTasks detached_tasks;
|
Common::DetachedTasks detached_tasks;
|
||||||
|
|
||||||
#if MICROPROFILE_ENABLED
|
#if MICROPROFILE_ENABLED
|
||||||
|
|||||||
@ -306,6 +306,7 @@ private slots:
|
|||||||
#endif
|
#endif
|
||||||
void OnSwitchDiskResources(VideoCore::LoadCallbackStage stage, std::size_t value,
|
void OnSwitchDiskResources(VideoCore::LoadCallbackStage stage, std::size_t value,
|
||||||
std::size_t total);
|
std::size_t total);
|
||||||
|
void StartLaunchStressTest(const QString& game_path);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Q_INVOKABLE void OnMoviePlaybackCompleted();
|
Q_INVOKABLE void OnMoviePlaybackCompleted();
|
||||||
|
|||||||
@ -371,7 +371,7 @@ void QtConfig::ReadControlValues() {
|
|||||||
const auto append_profile = [this, num_touch_from_button_maps] {
|
const auto append_profile = [this, num_touch_from_button_maps] {
|
||||||
Settings::InputProfile profile;
|
Settings::InputProfile profile;
|
||||||
profile.name =
|
profile.name =
|
||||||
ReadSetting(QStringLiteral("name"), QStringLiteral("default")).toString().toStdString();
|
ReadSetting(QStringLiteral("name"), QStringLiteral("Default")).toString().toStdString();
|
||||||
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
|
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
|
||||||
std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
|
std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
|
||||||
profile.buttons[i] = ReadSetting(QString::fromUtf8(Settings::NativeButton::mapping[i]),
|
profile.buttons[i] = ReadSetting(QString::fromUtf8(Settings::NativeButton::mapping[i]),
|
||||||
|
|||||||
@ -35,7 +35,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="label_emulation">
|
<widget class="QLabel" name="label_emulation">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Emulation:</string>
|
<string>Emulation</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
|||||||
@ -28,7 +28,7 @@
|
|||||||
<string>Select the camera to configure</string>
|
<string>Select the camera to configure</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Camera to configure:</string>
|
<string>Camera to Configure</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -59,7 +59,7 @@
|
|||||||
<string>Select the camera mode (single or double)</string>
|
<string>Select the camera mode (single or double)</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Camera mode:</string>
|
<string>Camera mode</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -90,7 +90,7 @@
|
|||||||
<string>Select the position of camera to configure</string>
|
<string>Select the position of camera to configure</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Camera position:</string>
|
<string>Camera position</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -130,7 +130,7 @@
|
|||||||
<string>Select where the image of the emulated camera comes from. It may be an image or a real camera.</string>
|
<string>Select where the image of the emulated camera comes from. It may be an image or a real camera.</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Camera Image Source:</string>
|
<string>Camera Image Source</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -166,7 +166,7 @@
|
|||||||
<enum>QFrame::NoFrame</enum>
|
<enum>QFrame::NoFrame</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>File:</string>
|
<string>File</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -193,7 +193,7 @@
|
|||||||
<enum>QFrame::NoFrame</enum>
|
<enum>QFrame::NoFrame</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Camera:</string>
|
<string>Camera</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -234,7 +234,7 @@
|
|||||||
<enum>QFrame::NoFrame</enum>
|
<enum>QFrame::NoFrame</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Flip:</string>
|
<string>Flip</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
|||||||
@ -113,7 +113,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="toggle_linear_filter">
|
<widget class="QCheckBox" name="toggle_linear_filter">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Enable Linear Filtering</string>
|
<string>Enable linear filtering</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -320,7 +320,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="disable_right_eye_render">
|
<widget class="QCheckBox" name="disable_right_eye_render">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Disable Right Eye Rendering</string>
|
<string>Disable right eye rendering</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string><html><head/><body><p>Disable Right Eye Rendering</p><p>Disables rendering the right eye image when not using stereoscopic mode. Greatly improves performance in some applications, but can cause flickering in others.</p></body></html></string>
|
<string><html><head/><body><p>Disable Right Eye Rendering</p><p>Disables rendering the right eye image when not using stereoscopic mode. Greatly improves performance in some applications, but can cause flickering in others.</p></body></html></string>
|
||||||
@ -342,7 +342,7 @@
|
|||||||
<string><html><head/><body><p>Replace textures with PNG files.</p><p>Textures are loaded from load/textures/[Title ID]/.</p></body></html></string>
|
<string><html><head/><body><p>Replace textures with PNG files.</p><p>Textures are loaded from load/textures/[Title ID]/.</p></body></html></string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Use Custom Textures</string>
|
<string>Use custom textures</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -352,7 +352,7 @@
|
|||||||
<string><html><head/><body><p>Dump textures to PNG files.</p><p>Textures are dumped to dump/textures/[Title ID]/.</p></body></html></string>
|
<string><html><head/><body><p>Dump textures to PNG files.</p><p>Textures are dumped to dump/textures/[Title ID]/.</p></body></html></string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Dump Textures</string>
|
<string>Dump textures</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -362,7 +362,7 @@
|
|||||||
<string><html><head/><body><p>Load all custom textures into memory on boot, instead of loading them when the application requires them.</p></body></html></string>
|
<string><html><head/><body><p>Load all custom textures into memory on boot, instead of loading them when the application requires them.</p></body></html></string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Preload Custom Textures</string>
|
<string>Preload custom textures</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -372,7 +372,7 @@
|
|||||||
<string><html><head/><body><p>Load custom textures asynchronously with background threads to reduce loading stutter</p></body></html></string>
|
<string><html><head/><body><p>Load custom textures asynchronously with background threads to reduce loading stutter</p></body></html></string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Async Custom Texture Loading</string>
|
<string>Async custom texture loading</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
|||||||
@ -97,7 +97,7 @@
|
|||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Set emulation speed:</string>
|
<string>Set emulation speed</string>
|
||||||
</property>
|
</property>
|
||||||
</item>
|
</item>
|
||||||
</widget>
|
</widget>
|
||||||
@ -105,7 +105,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="label_emulation_speed">
|
<widget class="QLabel" name="label_emulation_speed">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Emulation Speed:</string>
|
<string>Emulation Speed</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
|||||||
@ -132,14 +132,14 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="spirv_shader_gen">
|
<widget class="QCheckBox" name="spirv_shader_gen">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>SPIR-V Shader Generation</string>
|
<string>SPIR-V shader generation</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="disable_spirv_optimizer">
|
<widget class="QCheckBox" name="disable_spirv_optimizer">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Disable GLSL -> SPIR-V Optimizer</string>
|
<string>Disable GLSL -> SPIR-V optimizer</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string><html><head/><body><p>Disables the SPIR-V optimization pass, reducing stuttering considerably while barely affecting performance.</p></body></html></string>
|
<string><html><head/><body><p>Disables the SPIR-V optimization pass, reducing stuttering considerably while barely affecting performance.</p></body></html></string>
|
||||||
@ -176,7 +176,7 @@
|
|||||||
<string><html><head/><body><p>Use the selected graphics API to accelerate shader emulation.</p><p>Requires a relatively powerful GPU for better performance.</p></body></html></string>
|
<string><html><head/><body><p>Use the selected graphics API to accelerate shader emulation.</p><p>Requires a relatively powerful GPU for better performance.</p></body></html></string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Enable Hardware Shader</string>
|
<string>Enable hardware shader</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -201,7 +201,7 @@
|
|||||||
<string><html><head/><body><p>Correctly handle all edge cases in multiplication operation in shaders. </p><p>Some applications requires this to be enabled for the hardware shader to render properly.</p><p>However this would reduce performance in most applications.</p></body></html></string>
|
<string><html><head/><body><p>Correctly handle all edge cases in multiplication operation in shaders. </p><p>Some applications requires this to be enabled for the hardware shader to render properly.</p><p>However this would reduce performance in most applications.</p></body></html></string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Accurate Multiplication</string>
|
<string>Accurate multiplication</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -217,7 +217,7 @@
|
|||||||
<string><html><head/><body><p>Use the JIT engine instead of the interpreter for software shader emulation. </p><p>Enable this for better performance.</p></body></html></string>
|
<string><html><head/><body><p>Use the JIT engine instead of the interpreter for software shader emulation. </p><p>Enable this for better performance.</p></body></html></string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Enable Shader JIT</string>
|
<string>Enable shader JIT</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -227,7 +227,7 @@
|
|||||||
<string><html><head/><body><p>Compile shaders using background threads to avoid shader compilation stutter. Expect temporary graphical glitches</p></body></html></string>
|
<string><html><head/><body><p>Compile shaders using background threads to avoid shader compilation stutter. Expect temporary graphical glitches</p></body></html></string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Enable Async Shader Compilation</string>
|
<string>Enable async shader compilation</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -237,7 +237,7 @@
|
|||||||
<string><html><head/><body><p>Perform presentation on separate threads. Improves performance when using Vulkan in most applications.</p></body></html></string>
|
<string><html><head/><body><p>Perform presentation on separate threads. Improves performance when using Vulkan in most applications.</p></body></html></string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Enable Async Presentation</string>
|
<string>Enable async presentation</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -303,7 +303,7 @@
|
|||||||
<string><html><head/><body><p>Reduce stuttering by storing and loading generated shaders to disk.</p></body></html></string>
|
<string><html><head/><body><p>Reduce stuttering by storing and loading generated shaders to disk.</p></body></html></string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Use Disk Shader Cache</string>
|
<string>Use disk shader cache</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -349,7 +349,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="label_delay_render">
|
<widget class="QLabel" name="label_delay_render">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Delay application render thread:</string>
|
<string>Delay Application Render Thread</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string><html><head/><body><p>Delays the emulated application render thread the specified amount of milliseconds every time it submits render commands to the GPU.</p><p>Adjust this feature in the (very few) dynamic framerate applications to fix performance issues.</p></body></html></string>
|
<string><html><head/><body><p>Delays the emulated application render thread the specified amount of milliseconds every time it submits render commands to the GPU.</p><p>Adjust this feature in the (very few) dynamic framerate applications to fix performance issues.</p></body></html></string>
|
||||||
|
|||||||
@ -132,14 +132,14 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="toggle_swap_screen">
|
<widget class="QCheckBox" name="toggle_swap_screen">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Swap Screens</string>
|
<string>Swap screens</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="toggle_upright_screen">
|
<widget class="QCheckBox" name="toggle_upright_screen">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Rotate Screens Upright</string>
|
<string>Rotate screens upright</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -531,7 +531,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="lb_opacity_second_layer">
|
<widget class="QLabel" name="lb_opacity_second_layer">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string><html><head/><body><p>Bottom Screen Opacity % (OpenGL Only)</p></body></html></string>
|
<string><html><head/><body><p>Bottom Screen Opacity %</p></body></html></string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
|||||||
@ -27,7 +27,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="toggle_virtual_sd">
|
<widget class="QCheckBox" name="toggle_virtual_sd">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Use Virtual SD</string>
|
<string>Use virtual SD card</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -42,7 +42,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="toggle_custom_storage">
|
<widget class="QCheckBox" name="toggle_custom_storage">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Use Custom Storage</string>
|
<string>Use custom storage location</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
|||||||
@ -92,7 +92,7 @@ online features (if installed)</string>
|
|||||||
<item row="3" column="0">
|
<item row="3" column="0">
|
||||||
<widget class="QLabel" name="region_label">
|
<widget class="QLabel" name="region_label">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Region:</string>
|
<string>Region</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -549,7 +549,7 @@ online features (if installed)</string>
|
|||||||
<item row="20" column="0">
|
<item row="20" column="0">
|
||||||
<widget class="QLabel" name="label_plugin_loader">
|
<widget class="QLabel" name="label_plugin_loader">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>3GX Plugin Loader:</string>
|
<string>3GX Plugin Loader</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
|||||||
@ -37,7 +37,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="language_label">
|
<widget class="QLabel" name="language_label">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Interface language:</string>
|
<string>Interface Language</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -51,7 +51,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="theme_label">
|
<widget class="QLabel" name="theme_label">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Theme:</string>
|
<string>Theme</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -78,7 +78,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="icon_size_label">
|
<widget class="QLabel" name="icon_size_label">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Icon Size:</string>
|
<string>Icon Size</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -108,7 +108,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="row_1_text_label">
|
<widget class="QLabel" name="row_1_text_label">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Row 1 Text:</string>
|
<string>Row 1 Text</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -148,7 +148,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="row_2_text_label">
|
<widget class="QLabel" name="row_2_text_label">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Row 2 Text:</string>
|
<string>Row 2 Text</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -191,14 +191,14 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="toggle_hide_no_icon">
|
<widget class="QCheckBox" name="toggle_hide_no_icon">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Hide Titles without Icon</string>
|
<string>Hide titles without icon</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="toggle_single_line_mode">
|
<widget class="QCheckBox" name="toggle_single_line_mode">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Single Line Mode</string>
|
<string>Single line mode</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -218,7 +218,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="show_advanced_frametime_info">
|
<widget class="QCheckBox" name="show_advanced_frametime_info">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Show Advanced Frame Time Info</string>
|
<string>Show advanced frame time info</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
|||||||
@ -644,6 +644,11 @@ void GameList::AddGamePopup(QMenu& context_menu, const QString& path, const QStr
|
|||||||
shortcut_menu->addAction(tr("Add to Applications Menu"));
|
shortcut_menu->addAction(tr("Add to Applications Menu"));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef ENABLE_DEVELOPER_OPTIONS
|
||||||
|
context_menu.addSeparator();
|
||||||
|
QAction* stress_test_launch = context_menu.addAction(tr("Stress Test: App Launch"));
|
||||||
|
#endif
|
||||||
|
|
||||||
context_menu.addSeparator();
|
context_menu.addSeparator();
|
||||||
QAction* properties = context_menu.addAction(tr("Properties"));
|
QAction* properties = context_menu.addAction(tr("Properties"));
|
||||||
|
|
||||||
@ -755,6 +760,10 @@ void GameList::AddGamePopup(QMenu& context_menu, const QString& path, const QStr
|
|||||||
[this, path, program_id] { emit DumpRomFSRequested(path, program_id); });
|
[this, path, program_id] { emit DumpRomFSRequested(path, program_id); });
|
||||||
connect(remove_play_time_data, &QAction::triggered,
|
connect(remove_play_time_data, &QAction::triggered,
|
||||||
[this, program_id]() { emit RemovePlayTimeRequested(program_id); });
|
[this, program_id]() { emit RemovePlayTimeRequested(program_id); });
|
||||||
|
#ifdef ENABLE_DEVELOPER_OPTIONS
|
||||||
|
connect(stress_test_launch, &QAction::triggered,
|
||||||
|
[this, path]() { emit StartingLaunchStressTest(path); });
|
||||||
|
#endif
|
||||||
connect(properties, &QAction::triggered, this,
|
connect(properties, &QAction::triggered, this,
|
||||||
[this, path]() { emit OpenPerGameGeneralRequested(path); });
|
[this, path]() { emit OpenPerGameGeneralRequested(path); });
|
||||||
connect(open_shader_cache_location, &QAction::triggered, this, [this, program_id] {
|
connect(open_shader_cache_location, &QAction::triggered, this, [this, program_id] {
|
||||||
|
|||||||
@ -107,6 +107,7 @@ signals:
|
|||||||
void AddDirectory();
|
void AddDirectory();
|
||||||
void ShowList(bool show);
|
void ShowList(bool show);
|
||||||
void PopulatingCompleted();
|
void PopulatingCompleted();
|
||||||
|
void StartingLaunchStressTest(const QString& game_path);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void OnItemExpanded(const QModelIndex& item);
|
void OnItemExpanded(const QModelIndex& item);
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2017 Citra Emulator Project
|
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
@ -90,7 +90,8 @@ void DirectConnectWindow::Connect() {
|
|||||||
room_member->Join(ui->nickname->text().toStdString(),
|
room_member->Join(ui->nickname->text().toStdString(),
|
||||||
Service::CFG::GetConsoleIdHash(system),
|
Service::CFG::GetConsoleIdHash(system),
|
||||||
ui->ip->text().toStdString().c_str(), port, 0,
|
ui->ip->text().toStdString().c_str(), port, 0,
|
||||||
Network::NoPreferredMac, ui->password->text().toStdString().c_str());
|
Service::CFG::GetConsoleMacAddress(system),
|
||||||
|
ui->password->text().toStdString().c_str());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
watcher->setFuture(f);
|
watcher->setFuture(f);
|
||||||
|
|||||||
@ -193,8 +193,8 @@ void HostRoomWindow::Host() {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
member->Join(ui->username->text().toStdString(), Service::CFG::GetConsoleIdHash(system),
|
member->Join(ui->username->text().toStdString(), Service::CFG::GetConsoleIdHash(system),
|
||||||
"127.0.0.1", static_cast<u16>(port), 0, Network::NoPreferredMac, password,
|
"127.0.0.1", static_cast<u16>(port), 0,
|
||||||
token);
|
Service::CFG::GetConsoleMacAddress(system), password, token);
|
||||||
|
|
||||||
// Store settings
|
// Store settings
|
||||||
UISettings::values.room_nickname = ui->username->text();
|
UISettings::values.room_nickname = ui->username->text();
|
||||||
|
|||||||
@ -175,7 +175,8 @@ void Lobby::OnJoinRoom(const QModelIndex& source) {
|
|||||||
#endif
|
#endif
|
||||||
if (auto room_member = Network::GetRoomMember().lock()) {
|
if (auto room_member = Network::GetRoomMember().lock()) {
|
||||||
room_member->Join(nickname, Service::CFG::GetConsoleIdHash(system), ip.c_str(),
|
room_member->Join(nickname, Service::CFG::GetConsoleIdHash(system), ip.c_str(),
|
||||||
static_cast<u16>(port), 0, Network::NoPreferredMac, password, token);
|
static_cast<u16>(port), 0, Service::CFG::GetConsoleMacAddress(system),
|
||||||
|
password, token);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
watcher->setFuture(f);
|
watcher->setFuture(f);
|
||||||
|
|||||||
9
src/citra_qt/qt_swizzle.h
Normal file
9
src/citra_qt/qt_swizzle.h
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
namespace QtSwizzle {
|
||||||
|
|
||||||
|
void Dummy();
|
||||||
|
|
||||||
|
} // namespace QtSwizzle
|
||||||
48
src/citra_qt/qt_swizzle.mm
Normal file
48
src/citra_qt/qt_swizzle.mm
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#import <QtGui/private/qmetallayer_p.h>
|
||||||
|
#import <objc/runtime.h>
|
||||||
|
|
||||||
|
namespace QtSwizzle {
|
||||||
|
|
||||||
|
void Dummy() {
|
||||||
|
// Call this anywhere to make sure that qt_swizzle.mm is linked.
|
||||||
|
// noop
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QtSwizzle
|
||||||
|
|
||||||
|
@implementation QMetalLayer (AzaharPatch)
|
||||||
|
|
||||||
|
+ (void)load {
|
||||||
|
static dispatch_once_t onceToken;
|
||||||
|
dispatch_once(&onceToken, ^{
|
||||||
|
Class targetClass = [self class];
|
||||||
|
|
||||||
|
// Get the original and swizzled methods
|
||||||
|
Method originalMethod =
|
||||||
|
class_getInstanceMethod(targetClass, @selector(setNeedsDisplayInRect:));
|
||||||
|
Method swizzledMethod =
|
||||||
|
class_getInstanceMethod(targetClass, @selector(swizzled_setNeedsDisplayInRect:));
|
||||||
|
|
||||||
|
// Swap the implementations
|
||||||
|
method_exchangeImplementations(originalMethod, swizzledMethod);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)swizzled_setNeedsDisplayInRect:(CGRect)rect {
|
||||||
|
constexpr auto tooBig = 1e10; // Arbitrary large number
|
||||||
|
|
||||||
|
// Check for problematic huge rectangles
|
||||||
|
if ((!self.needsDisplay) && (rect.size.width > tooBig || rect.size.height > tooBig ||
|
||||||
|
rect.origin.x < -tooBig || rect.origin.y < -tooBig)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call the original implementation
|
||||||
|
[self swizzled_setNeedsDisplayInRect:rect];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
@ -67,6 +67,9 @@ HackManager hack_manager = {
|
|||||||
0x0004013000002C02, // Normal
|
0x0004013000002C02, // Normal
|
||||||
0x0004013000002C03, // Safe mode
|
0x0004013000002C03, // Safe mode
|
||||||
0x0004013020002C03, // New 3DS safe mode
|
0x0004013020002C03, // New 3DS safe mode
|
||||||
|
|
||||||
|
// DLP
|
||||||
|
0x0004013000002802,
|
||||||
},
|
},
|
||||||
}},
|
}},
|
||||||
|
|
||||||
|
|||||||
@ -585,9 +585,9 @@ struct Values {
|
|||||||
SwitchableSetting<bool> enable_realtime_audio{false, "enable_realtime_audio"};
|
SwitchableSetting<bool> enable_realtime_audio{false, "enable_realtime_audio"};
|
||||||
SwitchableSetting<float, true> volume{1.f, 0.f, 1.f, "volume"};
|
SwitchableSetting<float, true> volume{1.f, 0.f, 1.f, "volume"};
|
||||||
Setting<AudioCore::SinkType> output_type{AudioCore::SinkType::Auto, "output_type"};
|
Setting<AudioCore::SinkType> output_type{AudioCore::SinkType::Auto, "output_type"};
|
||||||
Setting<std::string> output_device{"auto", "output_device"};
|
Setting<std::string> output_device{"Auto", "output_device"};
|
||||||
Setting<AudioCore::InputType> input_type{AudioCore::InputType::Auto, "input_type"};
|
Setting<AudioCore::InputType> input_type{AudioCore::InputType::Auto, "input_type"};
|
||||||
Setting<std::string> input_device{"auto", "input_device"};
|
Setting<std::string> input_device{"Auto", "input_device"};
|
||||||
|
|
||||||
// Camera
|
// Camera
|
||||||
std::array<std::string, Service::CAM::NumCameras> camera_name;
|
std::array<std::string, Service::CAM::NumCameras> camera_name;
|
||||||
|
|||||||
@ -66,6 +66,10 @@ bool EmuWindow::IsWithinTouchscreen(const Layout::FramebufferLayout& layout, uns
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (!layout.bottom_screen_enabled) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
Settings::StereoRenderOption render_3d_mode = Settings::values.render_3d.GetValue();
|
Settings::StereoRenderOption render_3d_mode = Settings::values.render_3d.GetValue();
|
||||||
|
|
||||||
if (render_3d_mode == Settings::StereoRenderOption::SideBySide ||
|
if (render_3d_mode == Settings::StereoRenderOption::SideBySide ||
|
||||||
|
|||||||
@ -3255,9 +3255,15 @@ void Module::Interface::BeginImportProgramTemporarily(Kernel::HLERequestContext&
|
|||||||
// Create our CIAFile handle for the app to write to, and while the app writes Citra will store
|
// Create our CIAFile handle for the app to write to, and while the app writes Citra will store
|
||||||
// contents out to sdmc/nand
|
// contents out to sdmc/nand
|
||||||
const FileSys::Path cia_path = {};
|
const FileSys::Path cia_path = {};
|
||||||
auto file = std::make_shared<Service::FS::File>(
|
std::shared_ptr<Service::FS::File> file;
|
||||||
am->system.Kernel(), std::make_unique<CIAFile>(am->system, FS::MediaType::NAND), cia_path);
|
{
|
||||||
|
auto cia_file = std::make_unique<CIAFile>(am->system, FS::MediaType::NAND);
|
||||||
|
|
||||||
|
AuthorizeCIAFileDecryption(cia_file.get(), ctx);
|
||||||
|
|
||||||
|
file =
|
||||||
|
std::make_shared<Service::FS::File>(am->system.Kernel(), std::move(cia_file), cia_path);
|
||||||
|
}
|
||||||
am->cia_installing = true;
|
am->cia_installing = true;
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
|
||||||
@ -3448,7 +3454,7 @@ void Module::Interface::GetProgramInfoFromCia(Kernel::HLERequestContext& ctx) {
|
|||||||
title_info.version = tmd.GetTitleVersion();
|
title_info.version = tmd.GetTitleVersion();
|
||||||
title_info.type = tmd.GetTitleType();
|
title_info.type = tmd.GetTitleType();
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(8, 0);
|
IPC::RequestBuilder rb = rp.MakeBuilder(7, 0);
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
rb.PushRaw<TitleInfo>(title_info);
|
rb.PushRaw<TitleInfo>(title_info);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1264,6 +1264,10 @@ std::string GetConsoleIdHash(Core::System& system) {
|
|||||||
return fmt::format("{:02x}", fmt::join(hash.begin(), hash.end(), ""));
|
return fmt::format("{:02x}", fmt::join(hash.begin(), hash.end(), ""));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::array<u8, 6> GetConsoleMacAddress(Core::System& system) {
|
||||||
|
return MacToArray(GetModule(system)->GetMacAddress());
|
||||||
|
}
|
||||||
|
|
||||||
std::array<u8, 6> MacToArray(const std::string& mac) {
|
std::array<u8, 6> MacToArray(const std::string& mac) {
|
||||||
std::array<u8, 6> ret;
|
std::array<u8, 6> ret;
|
||||||
int last = -1;
|
int last = -1;
|
||||||
|
|||||||
@ -672,6 +672,8 @@ u64 MacToU64(const std::string& mac);
|
|||||||
|
|
||||||
std::string GenerateRandomMAC();
|
std::string GenerateRandomMAC();
|
||||||
|
|
||||||
|
std::array<u8, 6> GetConsoleMacAddress(Core::System& system);
|
||||||
|
|
||||||
} // namespace Service::CFG
|
} // namespace Service::CFG
|
||||||
|
|
||||||
SERVICE_CONSTRUCT(Service::CFG::Module)
|
SERVICE_CONSTRUCT(Service::CFG::Module)
|
||||||
|
|||||||
@ -106,15 +106,19 @@ void NWM_UDS::BroadcastNodeMap() {
|
|||||||
packet.channel = network_channel;
|
packet.channel = network_channel;
|
||||||
packet.type = Network::WifiPacket::PacketType::NodeMap;
|
packet.type = Network::WifiPacket::PacketType::NodeMap;
|
||||||
packet.destination_address = Network::BroadcastMac;
|
packet.destination_address = Network::BroadcastMac;
|
||||||
std::size_t num_entries = std::count_if(node_map.begin(), node_map.end(),
|
auto node_can_broad = [](auto& node) -> bool {
|
||||||
[](const auto& node) { return node.second.connected; });
|
return node.second.connected && !node.second.spec;
|
||||||
|
};
|
||||||
|
std::size_t num_entries =
|
||||||
|
std::count_if(node_map.begin(), node_map.end(),
|
||||||
|
[&node_can_broad](const auto& node) { return node_can_broad(node); });
|
||||||
using node_t = decltype(node_map)::value_type;
|
using node_t = decltype(node_map)::value_type;
|
||||||
packet.data.resize(sizeof(num_entries) +
|
packet.data.resize(sizeof(num_entries) +
|
||||||
(sizeof(node_t::first) + sizeof(node_t::second.node_id)) * num_entries);
|
(sizeof(node_t::first) + sizeof(node_t::second.node_id)) * num_entries);
|
||||||
std::memcpy(packet.data.data(), &num_entries, sizeof(num_entries));
|
std::memcpy(packet.data.data(), &num_entries, sizeof(num_entries));
|
||||||
std::size_t offset = sizeof(num_entries);
|
std::size_t offset = sizeof(num_entries);
|
||||||
for (const auto& node : node_map) {
|
for (const auto& node : node_map) {
|
||||||
if (node.second.connected) {
|
if (node_can_broad(node)) {
|
||||||
std::memcpy(packet.data.data() + offset, node.first.data(), sizeof(node.first));
|
std::memcpy(packet.data.data() + offset, node.first.data(), sizeof(node.first));
|
||||||
std::memcpy(packet.data.data() + offset + sizeof(node.first), &node.second.node_id,
|
std::memcpy(packet.data.data() + offset + sizeof(node.first), &node.second.node_id,
|
||||||
sizeof(node.second.node_id));
|
sizeof(node.second.node_id));
|
||||||
@ -185,7 +189,8 @@ void NWM_UDS::HandleAssociationResponseFrame(const Network::WifiPacket& packet)
|
|||||||
using Network::WifiPacket;
|
using Network::WifiPacket;
|
||||||
WifiPacket eapol_start;
|
WifiPacket eapol_start;
|
||||||
eapol_start.channel = network_channel;
|
eapol_start.channel = network_channel;
|
||||||
eapol_start.data = GenerateEAPoLStartFrame(std::get<u16>(assoc_result), current_node);
|
eapol_start.data =
|
||||||
|
GenerateEAPoLStartFrame(std::get<u16>(assoc_result), conn_type, current_node);
|
||||||
// TODO(B3N30): Encrypt the packet.
|
// TODO(B3N30): Encrypt the packet.
|
||||||
eapol_start.destination_address = packet.transmitter_address;
|
eapol_start.destination_address = packet.transmitter_address;
|
||||||
eapol_start.type = WifiPacket::PacketType::Data;
|
eapol_start.type = WifiPacket::PacketType::Data;
|
||||||
@ -217,8 +222,11 @@ void NWM_UDS::HandleEAPoLPacket(const Network::WifiPacket& packet) {
|
|||||||
|
|
||||||
ASSERT(connection_status.max_nodes != connection_status.total_nodes);
|
ASSERT(connection_status.max_nodes != connection_status.total_nodes);
|
||||||
|
|
||||||
auto node = DeserializeNodeInfoFromFrame(packet.data);
|
auto eapol_start = DeserializeEAPolStartPacket(packet.data);
|
||||||
|
|
||||||
|
auto node = DeserializeNodeInfo(eapol_start.node);
|
||||||
|
|
||||||
|
if (eapol_start.conn_type == ConnectionType::Client) {
|
||||||
// Get an unused network node id
|
// Get an unused network node id
|
||||||
u16 node_id = GetNextAvailableNodeId();
|
u16 node_id = GetNextAvailableNodeId();
|
||||||
node.network_node_id = node_id;
|
node.network_node_id = node_id;
|
||||||
@ -233,8 +241,17 @@ void NWM_UDS::HandleEAPoLPacket(const Network::WifiPacket& packet) {
|
|||||||
|
|
||||||
node_map[packet.transmitter_address].node_id = node.network_node_id;
|
node_map[packet.transmitter_address].node_id = node.network_node_id;
|
||||||
node_map[packet.transmitter_address].connected = true;
|
node_map[packet.transmitter_address].connected = true;
|
||||||
|
node_map[packet.transmitter_address].spec = false;
|
||||||
|
|
||||||
BroadcastNodeMap();
|
BroadcastNodeMap();
|
||||||
|
} else if (eapol_start.conn_type == ConnectionType::Spectator) {
|
||||||
|
node_map[packet.transmitter_address].node_id = NodeIDSpec;
|
||||||
|
node_map[packet.transmitter_address].connected = true;
|
||||||
|
node_map[packet.transmitter_address].spec = true;
|
||||||
|
} else {
|
||||||
|
LOG_ERROR(Service_NWM, "Client tried connecting with unknown connection type: 0x{:x}",
|
||||||
|
static_cast<u32>(eapol_start.conn_type));
|
||||||
|
}
|
||||||
|
|
||||||
// Send the EAPoL-Logoff packet.
|
// Send the EAPoL-Logoff packet.
|
||||||
using Network::WifiPacket;
|
using Network::WifiPacket;
|
||||||
@ -282,15 +299,23 @@ void NWM_UDS::HandleEAPoLPacket(const Network::WifiPacket& packet) {
|
|||||||
node_info[index - 1] = DeserializeNodeInfo(node);
|
node_info[index - 1] = DeserializeNodeInfo(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
// We're now connected, signal the application
|
if (conn_type == ConnectionType::Client) {
|
||||||
connection_status.status = NetworkStatus::ConnectedAsClient;
|
connection_status.status = NetworkStatus::ConnectedAsClient;
|
||||||
|
} else if (conn_type == ConnectionType::Spectator) {
|
||||||
|
connection_status.status = NetworkStatus::ConnectedAsSpectator;
|
||||||
|
} else {
|
||||||
|
LOG_ERROR(Service_NWM, "Unknown connection type: 0x{:x}", static_cast<u32>(conn_type));
|
||||||
|
}
|
||||||
|
|
||||||
|
// We're now connected, signal the application
|
||||||
connection_status.status_change_reason = NetworkStatusChangeReason::ConnectionEstablished;
|
connection_status.status_change_reason = NetworkStatusChangeReason::ConnectionEstablished;
|
||||||
// Some games require ConnectToNetwork to block, for now it doesn't
|
// Some games require ConnectToNetwork to block, for now it doesn't
|
||||||
// If blocking is implemented this lock needs to be changed,
|
// If blocking is implemented this lock needs to be changed,
|
||||||
// otherwise it might cause deadlocks
|
// otherwise it might cause deadlocks
|
||||||
connection_status_event->Signal();
|
connection_status_event->Signal();
|
||||||
connection_event->Signal();
|
connection_event->Signal();
|
||||||
} else if (connection_status.status == NetworkStatus::ConnectedAsClient) {
|
} else if (connection_status.status == NetworkStatus::ConnectedAsClient ||
|
||||||
|
connection_status.status == NetworkStatus::ConnectedAsSpectator) {
|
||||||
// TODO(B3N30): Remove that section and send/receive a proper connection_status packet
|
// TODO(B3N30): Remove that section and send/receive a proper connection_status packet
|
||||||
// On a 3ds this packet wouldn't be addressed to already connected clients
|
// On a 3ds this packet wouldn't be addressed to already connected clients
|
||||||
// We use this information because in the current implementation the host
|
// We use this information because in the current implementation the host
|
||||||
@ -328,9 +353,9 @@ void NWM_UDS::HandleSecureDataPacket(const Network::WifiPacket& packet) {
|
|||||||
std::scoped_lock lock{connection_status_mutex, system.Kernel().GetHLELock()};
|
std::scoped_lock lock{connection_status_mutex, system.Kernel().GetHLELock()};
|
||||||
|
|
||||||
if (connection_status.status != NetworkStatus::ConnectedAsHost &&
|
if (connection_status.status != NetworkStatus::ConnectedAsHost &&
|
||||||
connection_status.status != NetworkStatus::ConnectedAsClient) {
|
connection_status.status != NetworkStatus::ConnectedAsClient &&
|
||||||
// TODO(B3N30): Handle spectators
|
connection_status.status != NetworkStatus::ConnectedAsSpectator) {
|
||||||
LOG_DEBUG(Service_NWM, "Ignored SecureDataPacket, because connection status is {}",
|
LOG_DEBUG(Service_NWM, "Ignored SecureDataPacket because connection status is {}",
|
||||||
static_cast<u32>(connection_status.status));
|
static_cast<u32>(connection_status.status));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -370,12 +395,14 @@ void NWM_UDS::HandleSecureDataPacket(const Network::WifiPacket& packet) {
|
|||||||
// TODO(B3N30): Allow more than one bind node per channel.
|
// TODO(B3N30): Allow more than one bind node per channel.
|
||||||
auto channel_info = channel_data.find(secure_data.data_channel);
|
auto channel_info = channel_data.find(secure_data.data_channel);
|
||||||
// Ignore packets from channels we're not interested in.
|
// Ignore packets from channels we're not interested in.
|
||||||
if (channel_info == channel_data.end())
|
if (channel_info == channel_data.end()) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (channel_info->second.network_node_id != BroadcastNetworkNodeId &&
|
if (channel_info->second.network_node_id != BroadcastNetworkNodeId &&
|
||||||
channel_info->second.network_node_id != secure_data.src_node_id)
|
channel_info->second.network_node_id != secure_data.src_node_id) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Add the received packet to the data queue.
|
// Add the received packet to the data queue.
|
||||||
channel_info->second.received_packets.emplace_back(packet.data);
|
channel_info->second.received_packets.emplace_back(packet.data);
|
||||||
@ -432,7 +459,9 @@ void NWM_UDS::HandleAuthenticationFrame(const Network::WifiPacket& packet) {
|
|||||||
// Only the SEQ1 auth frame is handled here, the SEQ2 frame doesn't need any special behavior
|
// Only the SEQ1 auth frame is handled here, the SEQ2 frame doesn't need any special behavior
|
||||||
if (GetAuthenticationSeqNumber(packet.data) == AuthenticationSeq::SEQ1) {
|
if (GetAuthenticationSeqNumber(packet.data) == AuthenticationSeq::SEQ1) {
|
||||||
using Network::WifiPacket;
|
using Network::WifiPacket;
|
||||||
WifiPacket auth_request;
|
AuthenticationFrame auth_request;
|
||||||
|
memcpy(&auth_request, packet.data.data(), sizeof(auth_request));
|
||||||
|
WifiPacket auth_response;
|
||||||
{
|
{
|
||||||
std::scoped_lock lock(connection_status_mutex);
|
std::scoped_lock lock(connection_status_mutex);
|
||||||
if (connection_status.status != NetworkStatus::ConnectedAsHost) {
|
if (connection_status.status != NetworkStatus::ConnectedAsHost) {
|
||||||
@ -454,13 +483,13 @@ void NWM_UDS::HandleAuthenticationFrame(const Network::WifiPacket& packet) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Respond with an authentication response frame with SEQ2
|
// Respond with an authentication response frame with SEQ2
|
||||||
auth_request.channel = network_channel;
|
auth_response.channel = network_channel;
|
||||||
auth_request.data = GenerateAuthenticationFrame(AuthenticationSeq::SEQ2);
|
auth_response.data = GenerateAuthenticationFrame(AuthenticationSeq::SEQ2);
|
||||||
auth_request.destination_address = packet.transmitter_address;
|
auth_response.destination_address = packet.transmitter_address;
|
||||||
auth_request.type = WifiPacket::PacketType::Authentication;
|
auth_response.type = WifiPacket::PacketType::Authentication;
|
||||||
node_map[packet.transmitter_address].connected = false;
|
node_map[packet.transmitter_address].connected = false;
|
||||||
}
|
}
|
||||||
SendPacket(auth_request);
|
SendPacket(auth_response);
|
||||||
|
|
||||||
SendAssociationResponseFrame(packet.transmitter_address);
|
SendAssociationResponseFrame(packet.transmitter_address);
|
||||||
}
|
}
|
||||||
@ -495,6 +524,7 @@ void NWM_UDS::HandleDeauthenticationFrame(const Network::WifiPacket& packet) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!node.spec) {
|
||||||
connection_status.node_bitmask &= ~(1 << (node.node_id - 1));
|
connection_status.node_bitmask &= ~(1 << (node.node_id - 1));
|
||||||
connection_status.changed_nodes |= 1 << (node.node_id - 1);
|
connection_status.changed_nodes |= 1 << (node.node_id - 1);
|
||||||
connection_status.total_nodes--;
|
connection_status.total_nodes--;
|
||||||
@ -502,9 +532,8 @@ void NWM_UDS::HandleDeauthenticationFrame(const Network::WifiPacket& packet) {
|
|||||||
|
|
||||||
network_info.total_nodes--;
|
network_info.total_nodes--;
|
||||||
// TODO(B3N30): broadcast new connection_status to clients
|
// TODO(B3N30): broadcast new connection_status to clients
|
||||||
|
}
|
||||||
node_it->Reset();
|
node_it->Reset();
|
||||||
|
|
||||||
connection_status_event->Signal();
|
connection_status_event->Signal();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -588,14 +617,19 @@ void NWM_UDS::RecvBeaconBroadcastData(Kernel::HLERequestContext& ctx) {
|
|||||||
IPC::RequestParser rp(ctx);
|
IPC::RequestParser rp(ctx);
|
||||||
|
|
||||||
u32 out_buffer_size = rp.Pop<u32>();
|
u32 out_buffer_size = rp.Pop<u32>();
|
||||||
|
|
||||||
|
// scan input struct
|
||||||
u32 unk1 = rp.Pop<u32>();
|
u32 unk1 = rp.Pop<u32>();
|
||||||
u32 unk2 = rp.Pop<u32>();
|
u32 unk2 = rp.Pop<u32>();
|
||||||
|
|
||||||
MacAddress mac_address;
|
MacAddress mac_address;
|
||||||
rp.PopRaw(mac_address);
|
rp.PopRaw(mac_address);
|
||||||
|
|
||||||
|
// uninitialized data in scan input struct
|
||||||
rp.Skip(9, false);
|
rp.Skip(9, false);
|
||||||
|
|
||||||
|
// end scan input struct
|
||||||
|
|
||||||
u32 wlan_comm_id = rp.Pop<u32>();
|
u32 wlan_comm_id = rp.Pop<u32>();
|
||||||
u32 id = rp.Pop<u32>();
|
u32 id = rp.Pop<u32>();
|
||||||
// From 3dbrew:
|
// From 3dbrew:
|
||||||
@ -1042,6 +1076,7 @@ void NWM_UDS::DestroyNetwork(Kernel::HLERequestContext& ctx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void NWM_UDS::DisconnectNetwork(Kernel::HLERequestContext& ctx) {
|
void NWM_UDS::DisconnectNetwork(Kernel::HLERequestContext& ctx) {
|
||||||
|
LOG_DEBUG(Service_NWM, "disconnecting from network");
|
||||||
IPC::RequestParser rp(ctx);
|
IPC::RequestParser rp(ctx);
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
|
|
||||||
@ -1277,6 +1312,7 @@ void NWM_UDS::ConnectToNetwork(Kernel::HLERequestContext& ctx, u16 command_id,
|
|||||||
std::vector<u8> passphrase) {
|
std::vector<u8> passphrase) {
|
||||||
network_info = {};
|
network_info = {};
|
||||||
std::memcpy(&network_info, network_info_buffer.data(), network_info_buffer.size());
|
std::memcpy(&network_info, network_info_buffer.data(), network_info_buffer.size());
|
||||||
|
conn_type = static_cast<ConnectionType>(connection_type);
|
||||||
|
|
||||||
// Start the connection sequence
|
// Start the connection sequence
|
||||||
StartConnectionSequence(network_info.host_mac_address);
|
StartConnectionSequence(network_info.host_mac_address);
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2014 Citra Emulator Project
|
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
@ -18,6 +18,7 @@
|
|||||||
#include <boost/serialization/export.hpp>
|
#include <boost/serialization/export.hpp>
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/swap.h"
|
#include "common/swap.h"
|
||||||
|
#include "core/hle/service/nwm/uds_common.h"
|
||||||
#include "core/hle/service/service.h"
|
#include "core/hle/service/service.h"
|
||||||
#include "network/network.h"
|
#include "network/network.h"
|
||||||
|
|
||||||
@ -47,6 +48,8 @@ const u16 DefaultBeaconInterval = 100;
|
|||||||
/// The maximum number of nodes that can exist in an UDS session.
|
/// The maximum number of nodes that can exist in an UDS session.
|
||||||
constexpr u32 UDSMaxNodes = 16;
|
constexpr u32 UDSMaxNodes = 16;
|
||||||
|
|
||||||
|
constexpr u16 NodeIDSpec = 0;
|
||||||
|
|
||||||
struct NodeInfo {
|
struct NodeInfo {
|
||||||
u64_le friend_code_seed;
|
u64_le friend_code_seed;
|
||||||
std::array<u16_le, 10> username;
|
std::array<u16_le, 10> username;
|
||||||
@ -95,7 +98,7 @@ static_assert(sizeof(ConnectionStatus) == 0x30, "ConnectionStatus has incorrect
|
|||||||
struct NetworkInfo {
|
struct NetworkInfo {
|
||||||
std::array<u8, 6> host_mac_address;
|
std::array<u8, 6> host_mac_address;
|
||||||
u8 channel;
|
u8 channel;
|
||||||
INSERT_PADDING_BYTES(1);
|
u8 unk1;
|
||||||
u8 initialized;
|
u8 initialized;
|
||||||
INSERT_PADDING_BYTES(3);
|
INSERT_PADDING_BYTES(3);
|
||||||
std::array<u8, 3> oui_value;
|
std::array<u8, 3> oui_value;
|
||||||
@ -477,7 +480,7 @@ private:
|
|||||||
void HandleSecureDataPacket(const Network::WifiPacket& packet);
|
void HandleSecureDataPacket(const Network::WifiPacket& packet);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Start a connection sequence with an UDS server. The sequence starts by sending an 802.11
|
* Start a connection sequence with a UDS server. The sequence starts by sending an 802.11
|
||||||
* authentication frame with SEQ1.
|
* authentication frame with SEQ1.
|
||||||
*/
|
*/
|
||||||
void StartConnectionSequence(const MacAddress& server);
|
void StartConnectionSequence(const MacAddress& server);
|
||||||
@ -526,6 +529,9 @@ private:
|
|||||||
// Node information about our own system.
|
// Node information about our own system.
|
||||||
NodeInfo current_node;
|
NodeInfo current_node;
|
||||||
|
|
||||||
|
// whether you are connecting as a client or a spec
|
||||||
|
ConnectionType conn_type;
|
||||||
|
|
||||||
struct BindNodeData {
|
struct BindNodeData {
|
||||||
u32 bind_node_id; ///< Id of the bind node associated with this data.
|
u32 bind_node_id; ///< Id of the bind node associated with this data.
|
||||||
u8 channel; ///< Channel that this bind node was bound to.
|
u8 channel; ///< Channel that this bind node was bound to.
|
||||||
@ -548,6 +554,7 @@ private:
|
|||||||
// Mapping of mac addresses to their respective node_ids.
|
// Mapping of mac addresses to their respective node_ids.
|
||||||
struct Node {
|
struct Node {
|
||||||
bool connected;
|
bool connected;
|
||||||
|
bool spec;
|
||||||
u16 node_id;
|
u16 node_id;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
16
src/core/hle/service/nwm/uds_common.h
Normal file
16
src/core/hle/service/nwm/uds_common.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "core/hle/service/service.h"
|
||||||
|
|
||||||
|
namespace Service::NWM {
|
||||||
|
|
||||||
|
enum class ConnectionType : u8 {
|
||||||
|
Client = 0x1,
|
||||||
|
Spectator = 0x2,
|
||||||
|
};
|
||||||
|
|
||||||
|
}; // namespace Service::NWM
|
||||||
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2017 Citra Emulator Project
|
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
@ -286,9 +286,11 @@ SecureDataHeader ParseSecureDataHeader(std::span<const u8> data) {
|
|||||||
return header;
|
return header;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<u8> GenerateEAPoLStartFrame(u16 association_id, const NodeInfo& node_info) {
|
std::vector<u8> GenerateEAPoLStartFrame(u16 association_id, ConnectionType conn_type,
|
||||||
|
const NodeInfo& node_info) {
|
||||||
EAPoLStartPacket eapol_start{};
|
EAPoLStartPacket eapol_start{};
|
||||||
eapol_start.association_id = association_id;
|
eapol_start.association_id = association_id;
|
||||||
|
eapol_start.conn_type = conn_type;
|
||||||
eapol_start.node.friend_code_seed = node_info.friend_code_seed;
|
eapol_start.node.friend_code_seed = node_info.friend_code_seed;
|
||||||
|
|
||||||
std::copy(node_info.username.begin(), node_info.username.end(),
|
std::copy(node_info.username.begin(), node_info.username.end(),
|
||||||
@ -327,13 +329,7 @@ NodeInfo DeserializeNodeInfoFromFrame(std::span<const u8> frame) {
|
|||||||
// Skip the LLC header
|
// Skip the LLC header
|
||||||
std::memcpy(&eapol_start, frame.data() + sizeof(LLCHeader), sizeof(eapol_start));
|
std::memcpy(&eapol_start, frame.data() + sizeof(LLCHeader), sizeof(eapol_start));
|
||||||
|
|
||||||
NodeInfo node{};
|
return DeserializeNodeInfo(eapol_start.node);
|
||||||
node.friend_code_seed = eapol_start.node.friend_code_seed;
|
|
||||||
|
|
||||||
std::copy(eapol_start.node.username.begin(), eapol_start.node.username.end(),
|
|
||||||
node.username.begin());
|
|
||||||
|
|
||||||
return node;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeInfo DeserializeNodeInfo(const EAPoLNodeInfo& node) {
|
NodeInfo DeserializeNodeInfo(const EAPoLNodeInfo& node) {
|
||||||
@ -380,4 +376,11 @@ EAPoLLogoffPacket ParseEAPoLLogoffFrame(std::span<const u8> frame) {
|
|||||||
return eapol_logoff;
|
return eapol_logoff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EAPoLStartPacket DeserializeEAPolStartPacket(std::span<const u8> frame) {
|
||||||
|
EAPoLStartPacket eapol_start;
|
||||||
|
|
||||||
|
std::memcpy(&eapol_start, frame.data() + sizeof(LLCHeader), sizeof(eapol_start));
|
||||||
|
return eapol_start;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Service::NWM
|
} // namespace Service::NWM
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2017 Citra Emulator Project
|
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
@ -10,6 +10,7 @@
|
|||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/swap.h"
|
#include "common/swap.h"
|
||||||
#include "core/hle/service/nwm/uds_beacon.h"
|
#include "core/hle/service/nwm/uds_beacon.h"
|
||||||
|
#include "core/hle/service/nwm/uds_common.h"
|
||||||
#include "core/hle/service/service.h"
|
#include "core/hle/service/service.h"
|
||||||
|
|
||||||
namespace Service::NWM {
|
namespace Service::NWM {
|
||||||
@ -90,9 +91,8 @@ constexpr u16 EAPoLStartMagic = 0x201;
|
|||||||
struct EAPoLStartPacket {
|
struct EAPoLStartPacket {
|
||||||
u16_be magic = EAPoLStartMagic;
|
u16_be magic = EAPoLStartMagic;
|
||||||
u16_be association_id;
|
u16_be association_id;
|
||||||
// This value is hardcoded to 1 in the NWM module.
|
enum_le<ConnectionType> conn_type;
|
||||||
u16_be unknown = 1;
|
INSERT_PADDING_BYTES(3);
|
||||||
INSERT_PADDING_BYTES(2);
|
|
||||||
EAPoLNodeInfo node;
|
EAPoLNodeInfo node;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -132,7 +132,8 @@ SecureDataHeader ParseSecureDataHeader(std::span<const u8> data);
|
|||||||
* communication.
|
* communication.
|
||||||
* @returns The generated frame body.
|
* @returns The generated frame body.
|
||||||
*/
|
*/
|
||||||
std::vector<u8> GenerateEAPoLStartFrame(u16 association_id, const NodeInfo& node_info);
|
std::vector<u8> GenerateEAPoLStartFrame(u16 association_id, ConnectionType conn_type,
|
||||||
|
const NodeInfo& node_info);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Returns the EtherType of the specified 802.11 frame.
|
* Returns the EtherType of the specified 802.11 frame.
|
||||||
@ -151,6 +152,8 @@ u16 GetEAPoLFrameType(std::span<const u8> frame);
|
|||||||
*/
|
*/
|
||||||
NodeInfo DeserializeNodeInfoFromFrame(std::span<const u8> frame);
|
NodeInfo DeserializeNodeInfoFromFrame(std::span<const u8> frame);
|
||||||
|
|
||||||
|
EAPoLStartPacket DeserializeEAPolStartPacket(std::span<const u8> frame);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Returns a NodeInfo constructed from the data in the specified EAPoLNodeInfo.
|
* Returns a NodeInfo constructed from the data in the specified EAPoLNodeInfo.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -78,7 +78,7 @@ const std::array<ServiceModuleInfo, 41> service_module_map{
|
|||||||
false},
|
false},
|
||||||
{"CECD", 0x00040130'00002602, CECD::InstallInterfaces, false},
|
{"CECD", 0x00040130'00002602, CECD::InstallInterfaces, false},
|
||||||
{"CFG", 0x00040130'00001702, CFG::InstallInterfaces, false},
|
{"CFG", 0x00040130'00001702, CFG::InstallInterfaces, false},
|
||||||
{"DLP", 0x00040130'00002802, DLP::InstallInterfaces, false},
|
{"DLP", 0x00040130'00002802, DLP::InstallInterfaces, true},
|
||||||
{"DSP", 0x00040130'00001A02, DSP::InstallInterfaces, false},
|
{"DSP", 0x00040130'00001A02, DSP::InstallInterfaces, false},
|
||||||
{"FRD", 0x00040130'00003202, FRD::InstallInterfaces, true},
|
{"FRD", 0x00040130'00003202, FRD::InstallInterfaces, true},
|
||||||
{"GSP", 0x00040130'00001C02, GSP::InstallInterfaces, false},
|
{"GSP", 0x00040130'00001C02, GSP::InstallInterfaces, false},
|
||||||
|
|||||||
@ -710,10 +710,7 @@ void RendererOpenGL::DrawScreens(const Layout::FramebufferLayout& layout, bool f
|
|||||||
}
|
}
|
||||||
|
|
||||||
void RendererOpenGL::ApplySecondLayerOpacity(bool isPortrait) {
|
void RendererOpenGL::ApplySecondLayerOpacity(bool isPortrait) {
|
||||||
// TODO: Allow for second layer opacity in portrait mode android
|
if (Settings::values.layout_option.GetValue() == Settings::LayoutOption::CustomLayout &&
|
||||||
|
|
||||||
if (!isPortrait &&
|
|
||||||
(Settings::values.layout_option.GetValue() == Settings::LayoutOption::CustomLayout) &&
|
|
||||||
Settings::values.custom_second_layer_opacity.GetValue() < 100) {
|
Settings::values.custom_second_layer_opacity.GetValue() < 100) {
|
||||||
state.blend.src_rgb_func = GL_CONSTANT_ALPHA;
|
state.blend.src_rgb_func = GL_CONSTANT_ALPHA;
|
||||||
state.blend.src_a_func = GL_CONSTANT_ALPHA;
|
state.blend.src_a_func = GL_CONSTANT_ALPHA;
|
||||||
@ -724,8 +721,7 @@ void RendererOpenGL::ApplySecondLayerOpacity(bool isPortrait) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void RendererOpenGL::ResetSecondLayerOpacity(bool isPortrait) {
|
void RendererOpenGL::ResetSecondLayerOpacity(bool isPortrait) {
|
||||||
if (!isPortrait &&
|
if (Settings::values.layout_option.GetValue() == Settings::LayoutOption::CustomLayout &&
|
||||||
(Settings::values.layout_option.GetValue() == Settings::LayoutOption::CustomLayout) &&
|
|
||||||
Settings::values.custom_second_layer_opacity.GetValue() < 100) {
|
Settings::values.custom_second_layer_opacity.GetValue() < 100) {
|
||||||
state.blend.src_rgb_func = GL_ONE;
|
state.blend.src_rgb_func = GL_ONE;
|
||||||
state.blend.dst_rgb_func = GL_ZERO;
|
state.blend.dst_rgb_func = GL_ZERO;
|
||||||
|
|||||||
@ -376,7 +376,13 @@ void RendererVulkan::BuildPipelines() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const vk::PipelineColorBlendAttachmentState colorblend_attachment = {
|
const vk::PipelineColorBlendAttachmentState colorblend_attachment = {
|
||||||
.blendEnable = false,
|
.blendEnable = true,
|
||||||
|
.srcColorBlendFactor = vk::BlendFactor::eConstantAlpha,
|
||||||
|
.dstColorBlendFactor = vk::BlendFactor::eOneMinusConstantAlpha,
|
||||||
|
.colorBlendOp = vk::BlendOp::eAdd,
|
||||||
|
.srcAlphaBlendFactor = vk::BlendFactor::eConstantAlpha,
|
||||||
|
.dstAlphaBlendFactor = vk::BlendFactor::eOneMinusConstantAlpha,
|
||||||
|
.alphaBlendOp = vk::BlendOp::eAdd,
|
||||||
.colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG |
|
.colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG |
|
||||||
vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA,
|
vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA,
|
||||||
};
|
};
|
||||||
@ -385,7 +391,6 @@ void RendererVulkan::BuildPipelines() {
|
|||||||
.logicOpEnable = false,
|
.logicOpEnable = false,
|
||||||
.attachmentCount = 1,
|
.attachmentCount = 1,
|
||||||
.pAttachments = &colorblend_attachment,
|
.pAttachments = &colorblend_attachment,
|
||||||
.blendConstants = std::array{1.0f, 1.0f, 1.0f, 1.0f},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const vk::Viewport placeholder_viewport = vk::Viewport{0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f};
|
const vk::Viewport placeholder_viewport = vk::Viewport{0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f};
|
||||||
@ -398,6 +403,7 @@ void RendererVulkan::BuildPipelines() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const std::array dynamic_states = {
|
const std::array dynamic_states = {
|
||||||
|
vk::DynamicState::eBlendConstants,
|
||||||
vk::DynamicState::eViewport,
|
vk::DynamicState::eViewport,
|
||||||
vk::DynamicState::eScissor,
|
vk::DynamicState::eScissor,
|
||||||
};
|
};
|
||||||
@ -729,6 +735,13 @@ void RendererVulkan::DrawSingleScreenStereo(u32 screen_id_l, u32 screen_id_r, fl
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RendererVulkan::ApplySecondLayerOpacity(float alpha) {
|
||||||
|
scheduler.Record([alpha](vk::CommandBuffer cmdbuf) {
|
||||||
|
const std::array<float, 4> blend_constants = {0.0f, 0.0f, 0.0f, alpha};
|
||||||
|
cmdbuf.setBlendConstants(blend_constants.data());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void RendererVulkan::DrawTopScreen(const Layout::FramebufferLayout& layout,
|
void RendererVulkan::DrawTopScreen(const Layout::FramebufferLayout& layout,
|
||||||
const Common::Rectangle<u32>& top_screen) {
|
const Common::Rectangle<u32>& top_screen) {
|
||||||
if (!layout.top_screen_enabled) {
|
if (!layout.top_screen_enabled) {
|
||||||
@ -867,13 +880,30 @@ void RendererVulkan::DrawScreens(Frame* frame, const Layout::FramebufferLayout&
|
|||||||
draw_info.modelview = MakeOrthographicMatrix(layout.width, layout.height);
|
draw_info.modelview = MakeOrthographicMatrix(layout.width, layout.height);
|
||||||
|
|
||||||
draw_info.layer = 0;
|
draw_info.layer = 0;
|
||||||
|
|
||||||
|
// Apply the initial default opacity value; Needed to avoid flickering
|
||||||
|
ApplySecondLayerOpacity(1.0f);
|
||||||
|
|
||||||
|
bool use_custom_opacity =
|
||||||
|
Settings::values.layout_option.GetValue() == Settings::LayoutOption::CustomLayout &&
|
||||||
|
Settings::values.custom_second_layer_opacity.GetValue() < 100;
|
||||||
|
float second_alpha = use_custom_opacity
|
||||||
|
? Settings::values.custom_second_layer_opacity.GetValue() / 100.0f
|
||||||
|
: 1.0f;
|
||||||
|
|
||||||
if (!Settings::values.swap_screen.GetValue()) {
|
if (!Settings::values.swap_screen.GetValue()) {
|
||||||
DrawTopScreen(layout, top_screen);
|
DrawTopScreen(layout, top_screen);
|
||||||
draw_info.layer = 0;
|
draw_info.layer = 0;
|
||||||
|
if (use_custom_opacity) {
|
||||||
|
ApplySecondLayerOpacity(second_alpha);
|
||||||
|
}
|
||||||
DrawBottomScreen(layout, bottom_screen);
|
DrawBottomScreen(layout, bottom_screen);
|
||||||
} else {
|
} else {
|
||||||
DrawBottomScreen(layout, bottom_screen);
|
DrawBottomScreen(layout, bottom_screen);
|
||||||
draw_info.layer = 0;
|
draw_info.layer = 0;
|
||||||
|
if (use_custom_opacity) {
|
||||||
|
ApplySecondLayerOpacity(second_alpha);
|
||||||
|
}
|
||||||
DrawTopScreen(layout, top_screen);
|
DrawTopScreen(layout, top_screen);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -98,12 +98,16 @@ private:
|
|||||||
void DrawScreens(Frame* frame, const Layout::FramebufferLayout& layout, bool flipped);
|
void DrawScreens(Frame* frame, const Layout::FramebufferLayout& layout, bool flipped);
|
||||||
void DrawBottomScreen(const Layout::FramebufferLayout& layout,
|
void DrawBottomScreen(const Layout::FramebufferLayout& layout,
|
||||||
const Common::Rectangle<u32>& bottom_screen);
|
const Common::Rectangle<u32>& bottom_screen);
|
||||||
|
|
||||||
void DrawTopScreen(const Layout::FramebufferLayout& layout,
|
void DrawTopScreen(const Layout::FramebufferLayout& layout,
|
||||||
const Common::Rectangle<u32>& top_screen);
|
const Common::Rectangle<u32>& top_screen);
|
||||||
void DrawSingleScreen(u32 screen_id, float x, float y, float w, float h,
|
void DrawSingleScreen(u32 screen_id, float x, float y, float w, float h,
|
||||||
Layout::DisplayOrientation orientation);
|
Layout::DisplayOrientation orientation);
|
||||||
void DrawSingleScreenStereo(u32 screen_id_l, u32 screen_id_r, float x, float y, float w,
|
void DrawSingleScreenStereo(u32 screen_id_l, u32 screen_id_r, float x, float y, float w,
|
||||||
float h, Layout::DisplayOrientation orientation);
|
float h, Layout::DisplayOrientation orientation);
|
||||||
|
|
||||||
|
void ApplySecondLayerOpacity(float alpha);
|
||||||
|
|
||||||
void LoadFBToScreenInfo(const Pica::FramebufferConfig& framebuffer, ScreenInfo& screen_info,
|
void LoadFBToScreenInfo(const Pica::FramebufferConfig& framebuffer, ScreenInfo& screen_info,
|
||||||
bool right_eye);
|
bool right_eye);
|
||||||
void FillScreen(Common::Vec3<u8> color, const TextureInfo& texture);
|
void FillScreen(Common::Vec3<u8> color, const TextureInfo& texture);
|
||||||
|
|||||||
@ -556,12 +556,15 @@ bool PipelineCache::EnsureDirectories() const {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return create_dir(FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir)) &&
|
return create_dir(FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir)) &&
|
||||||
create_dir(GetPipelineCacheDir());
|
create_dir(GetVulkanDir()) && create_dir(GetPipelineCacheDir());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string PipelineCache::GetVulkanDir() const {
|
||||||
|
return FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir) + "vulkan" + DIR_SEP;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string PipelineCache::GetPipelineCacheDir() const {
|
std::string PipelineCache::GetPipelineCacheDir() const {
|
||||||
return FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir) + "vulkan" + DIR_SEP + "pipeline" +
|
return GetVulkanDir() + "pipeline" + DIR_SEP;
|
||||||
DIR_SEP;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PipelineCache::SwitchPipelineCache(u64 title_id, const std::atomic_bool& stop_loading,
|
void PipelineCache::SwitchPipelineCache(u64 title_id, const std::atomic_bool& stop_loading,
|
||||||
|
|||||||
@ -108,6 +108,9 @@ private:
|
|||||||
/// Create pipeline cache directories. Returns true on success.
|
/// Create pipeline cache directories. Returns true on success.
|
||||||
bool EnsureDirectories() const;
|
bool EnsureDirectories() const;
|
||||||
|
|
||||||
|
/// Returns the Vulkan shader directory
|
||||||
|
std::string GetVulkanDir() const;
|
||||||
|
|
||||||
/// Returns the pipeline cache storage dir
|
/// Returns the pipeline cache storage dir
|
||||||
std::string GetPipelineCacheDir() const;
|
std::string GetPipelineCacheDir() const;
|
||||||
|
|
||||||
|
|||||||
@ -7,7 +7,7 @@ The scripts in this directory assume that your current working directory is the
|
|||||||
## Pre-release checklist
|
## Pre-release checklist
|
||||||
|
|
||||||
- [ ] Update compatibility list
|
- [ ] Update compatibility list
|
||||||
- [ ] Update translations
|
- [ ] If this is a major release (2123.1 -> major.minor), update translations
|
||||||
|
|
||||||
### Note:
|
### Note:
|
||||||
|
|
||||||
|
|||||||
6
tools/purge-github-cache.sh
Executable file
6
tools/purge-github-cache.sh
Executable file
@ -0,0 +1,6 @@
|
|||||||
|
#!/bin/bash -ex
|
||||||
|
|
||||||
|
# This script assumes that the Github CLI is installed and that
|
||||||
|
# the authenticated user has appropriate authorization.
|
||||||
|
|
||||||
|
gh cache delete --all
|
||||||
Loading…
x
Reference in New Issue
Block a user