mirror of
https://github.com/Lime3DS/Lime3DS.git
synced 2025-12-23 06:57:36 +00:00
Compare commits
64 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
275d818cd4 | ||
|
|
7a2f3fdda9 | ||
|
|
2edc8b4b73 | ||
|
|
109f5fb730 | ||
|
|
f7eaf13a4d | ||
|
|
4fdc45a771 | ||
|
|
6b05944116 | ||
|
|
b0fe4d190d | ||
|
|
9996a07e87 | ||
|
|
254ab96b63 | ||
|
|
aac54d648c | ||
|
|
456a886a33 | ||
|
|
05562046ac | ||
|
|
356aa3d3ae | ||
|
|
f1a59b2186 | ||
|
|
346bcf02ea | ||
|
|
9099b9cfb7 | ||
|
|
e1a7c24ecd | ||
|
|
a34ad3bff8 | ||
|
|
6866c01524 | ||
|
|
e0b8e8440a | ||
|
|
4cc9792f66 | ||
|
|
4574b91561 | ||
|
|
04b6c69083 | ||
|
|
375c8c1910 | ||
|
|
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
.ci/docker.sh
Executable file
4
.ci/docker.sh
Executable file
@ -0,0 +1,4 @@
|
||||
mkdir build
|
||||
|
||||
docker build -f docker/azahar-room/Dockerfile -t azahar-room .
|
||||
docker save azahar-room:latest > build/azahar-room.dockerimage
|
||||
12
.ci/linux.sh
12
.ci/linux.sh
@ -1,14 +1,16 @@
|
||||
#!/bin/bash -ex
|
||||
|
||||
if [ "$TARGET" = "appimage" ]; then
|
||||
if [[ "$TARGET" == "appimage"* ]]; then
|
||||
# Compile the AppImage we distribute with Clang.
|
||||
export EXTRA_CMAKE_FLAGS=(-DCMAKE_CXX_COMPILER=clang++
|
||||
-DCMAKE_C_COMPILER=clang
|
||||
-DCMAKE_LINKER=/etc/bin/ld.lld
|
||||
-DENABLE_ROOM_STANDALONE=OFF)
|
||||
# Bundle required QT wayland libraries
|
||||
export EXTRA_QT_PLUGINS="waylandcompositor"
|
||||
export EXTRA_PLATFORM_PLUGINS="libqwayland-egl.so;libqwayland-generic.so"
|
||||
if [ "$TARGET" = "appimage-wayland" ]; then
|
||||
# Bundle required QT wayland libraries
|
||||
export EXTRA_QT_PLUGINS="waylandcompositor"
|
||||
export EXTRA_PLATFORM_PLUGINS="libqwayland-egl.so;libqwayland-generic.so"
|
||||
fi
|
||||
else
|
||||
# For the linux-fresh verification target, verify compilation without PCH as well.
|
||||
export EXTRA_CMAKE_FLAGS=(-DCITRA_USE_PRECOMPILED_HEADERS=OFF)
|
||||
@ -30,7 +32,7 @@ cmake .. -G Ninja \
|
||||
ninja
|
||||
strip -s bin/Release/*
|
||||
|
||||
if [ "$TARGET" = "appimage" ]; then
|
||||
if [[ "$TARGET" == "appimage"* ]]; then
|
||||
ninja bundle
|
||||
# TODO: Our AppImage environment currently uses an older ccache version without the verbose flag.
|
||||
ccache -s
|
||||
|
||||
39
.github/workflows/build.yml
vendored
39
.github/workflows/build.yml
vendored
@ -22,12 +22,13 @@ jobs:
|
||||
with:
|
||||
name: source
|
||||
path: artifacts/
|
||||
|
||||
linux:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
target: ["appimage", "fresh"]
|
||||
target: ["appimage", "appimage-wayland", "fresh"]
|
||||
container:
|
||||
image: opensauce04/azahar-build-environment:latest
|
||||
options: -u 1001
|
||||
@ -51,18 +52,23 @@ jobs:
|
||||
- name: Build
|
||||
run: ./.ci/linux.sh
|
||||
- name: Move AppImage to artifacts directory
|
||||
if: ${{ matrix.target == 'appimage' }}
|
||||
if: ${{ contains(matrix.target, 'appimage') }}
|
||||
run: |
|
||||
mkdir -p artifacts
|
||||
mv build/bundle/*.AppImage artifacts/
|
||||
- name: Rename AppImage
|
||||
if: ${{ matrix.target == 'appimage-wayland' }}
|
||||
run: |
|
||||
mv artifacts/azahar.AppImage artifacts/azahar-wayland.AppImage
|
||||
- name: Upload
|
||||
if: ${{ matrix.target == 'appimage' }}
|
||||
if: ${{ contains(matrix.target, 'appimage') }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ env.OS }}-${{ env.TARGET }}
|
||||
path: artifacts/
|
||||
|
||||
macos:
|
||||
runs-on: ${{ (matrix.target == 'x86_64' && 'macos-13') || 'macos-14' }}
|
||||
runs-on: ${{ (matrix.target == 'x86_64' && 'macos-15-intel') || 'macos-26' }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@ -102,8 +108,9 @@ jobs:
|
||||
with:
|
||||
name: ${{ env.OS }}-${{ env.TARGET }}
|
||||
path: artifacts/
|
||||
|
||||
macos-universal:
|
||||
runs-on: macos-14
|
||||
runs-on: macos-26
|
||||
needs: macos
|
||||
env:
|
||||
OS: macos
|
||||
@ -133,6 +140,7 @@ jobs:
|
||||
with:
|
||||
name: ${{ env.OS }}-${{ env.TARGET }}
|
||||
path: artifacts/
|
||||
|
||||
windows:
|
||||
runs-on: windows-latest
|
||||
strategy:
|
||||
@ -210,6 +218,7 @@ jobs:
|
||||
with:
|
||||
name: ${{ env.OS }}-${{ env.TARGET }}
|
||||
path: artifacts/
|
||||
|
||||
android:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
@ -261,3 +270,23 @@ jobs:
|
||||
with:
|
||||
name: ${{ env.OS }}-${{ env.TARGET }}
|
||||
path: src/android/app/artifacts/
|
||||
|
||||
docker:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: docker:dind
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Build Docker image
|
||||
run: ./.ci/docker.sh
|
||||
- name: Move Docker image to artifacts directory
|
||||
run: |
|
||||
mkdir -p artifacts
|
||||
mv build/*.dockerimage artifacts/
|
||||
- name: Upload
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: docker
|
||||
path: artifacts/
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@ -10,6 +10,9 @@ src/installer/*.exe
|
||||
src/common/scm_rev.cpp
|
||||
.travis.descriptor.json
|
||||
|
||||
# Docker image files
|
||||
*.dockerimage
|
||||
|
||||
# Project/editor files
|
||||
*.swp
|
||||
*.kdev4
|
||||
|
||||
@ -48,6 +48,15 @@ if (APPLE)
|
||||
else()
|
||||
# Minimum macOS 13
|
||||
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()
|
||||
|
||||
@ -112,6 +121,8 @@ option(ENABLE_MICROPROFILE "Enables microprofile capabilities" OFF)
|
||||
|
||||
option(ENABLE_SSE42 "Enable SSE4.2 optimizations on x86_64" ON)
|
||||
|
||||
option(ENABLE_DEVELOPER_OPTIONS "Enable functionality targeted at emulator developers" OFF)
|
||||
|
||||
# Compile options
|
||||
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})
|
||||
@ -299,7 +310,7 @@ find_package(Threads REQUIRED)
|
||||
|
||||
if (ENABLE_QT)
|
||||
if (NOT USE_SYSTEM_QT)
|
||||
download_qt(6.7.2)
|
||||
download_qt(6.9.2)
|
||||
endif()
|
||||
|
||||
find_package(Qt6 REQUIRED COMPONENTS Widgets Multimedia Concurrent)
|
||||
|
||||
@ -130,25 +130,10 @@ if (BUNDLE_TARGET_EXECUTE)
|
||||
--icon-file "${CMAKE_BINARY_DIR}/dist/org.azahar_emu.Azahar.svg"
|
||||
--desktop-file "${source_path}/dist/${executable_name}.desktop"
|
||||
--appdir "${appdir_path}"
|
||||
RESULT_VARIABLE linuxdeploy_appdir_result)
|
||||
RESULT_VARIABLE linuxdeploy_appdir_result
|
||||
ERROR_VARIABLE linuxdeploy_appdir_error)
|
||||
if (NOT linuxdeploy_appdir_result EQUAL "0")
|
||||
message(FATAL_ERROR "linuxdeploy failed to create AppDir: ${linuxdeploy_appdir_result}")
|
||||
endif()
|
||||
|
||||
if (enable_qt)
|
||||
set(qt_hook_file "${appdir_path}/apprun-hooks/linuxdeploy-plugin-qt-hook.sh")
|
||||
file(READ "${qt_hook_file}" qt_hook_contents)
|
||||
# Add Cinnamon to list of DEs for GTK3 theming.
|
||||
string(REPLACE
|
||||
"*XFCE*"
|
||||
"*X-Cinnamon*|*XFCE*"
|
||||
qt_hook_contents "${qt_hook_contents}")
|
||||
# Wayland backend crashes due to changed schemas in Gnome 40.
|
||||
string(REPLACE
|
||||
"export QT_QPA_PLATFORMTHEME=gtk3"
|
||||
"export QT_QPA_PLATFORMTHEME=gtk3; export GDK_BACKEND=x11"
|
||||
qt_hook_contents "${qt_hook_contents}")
|
||||
file(WRITE "${qt_hook_file}" "${qt_hook_contents}")
|
||||
message(FATAL_ERROR "linuxdeploy failed to create AppDir w/ exit code ${linuxdeploy_appdir_result}:\n${linuxdeploy_appdir_error}")
|
||||
endif()
|
||||
|
||||
message(STATUS "Creating AppImage for executable ${executable_path}")
|
||||
|
||||
@ -20,9 +20,9 @@ function(determine_qt_parameters target host_out type_out arch_out arch_path_out
|
||||
set(arch_path "mingw_64")
|
||||
elseif (MSVC)
|
||||
if ("arm64" IN_LIST ARCHITECTURE)
|
||||
set(arch_path "msvc2019_arm64")
|
||||
set(arch_path "msvc2022_arm64")
|
||||
elseif ("x86_64" IN_LIST ARCHITECTURE)
|
||||
set(arch_path "msvc2019_64")
|
||||
set(arch_path "msvc2022_64")
|
||||
else()
|
||||
message(FATAL_ERROR "Unsupported bundled Qt architecture. Enable USE_SYSTEM_QT and provide your own.")
|
||||
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.
|
||||
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")
|
||||
# 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: ^ Is this still true with msvc2022?
|
||||
# set(host_arch_path "msvc2019_arm64")
|
||||
set(host_arch_path "msvc2019_64")
|
||||
set(host_arch_path "msvc2022_64")
|
||||
endif()
|
||||
set(host_arch "win64_${host_arch_path}")
|
||||
else()
|
||||
@ -105,7 +106,7 @@ function(download_qt_configuration prefix_out target host type arch arch_path ba
|
||||
|
||||
if (NOT EXISTS "${prefix}")
|
||||
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)
|
||||
set(aqt_path "${base_path}/aqt.exe")
|
||||
if (NOT EXISTS "${aqt_path}")
|
||||
|
||||
@ -19,7 +19,6 @@ Download the latest release from [Releases](https://github.com/azahar-emu/azahar
|
||||
If you are unsure of whether you want to use MSYS2 or MSVC, use MSYS2.
|
||||
|
||||
---
|
||||
|
||||
### MacOS
|
||||
|
||||
To download a build that will work on all Macs, you can download the `macos-universal` build on the [Releases](https://github.com/azahar-emu/azahar/releases) page.
|
||||
@ -55,6 +54,11 @@ The recommended format for using Azahar on Linux is the Flatpak available on Fla
|
||||
|
||||
Azahar is also available as an AppImage on the [Releases](https://github.com/azahar-emu/azahar/releases) page.
|
||||
|
||||
If you are unsure of which variant to use, we recommend using the default `azahar.AppImage`. This is because of upstream issues in the Wayland ecosystem which may cause problems when running the emulator (e.g. [#1162](https://github.com/azahar-emu/azahar/issues/1162)).
|
||||
|
||||
Unless you explicitly require native Wayland support (e.g. you are running a system with no Xwayland), the non-Wayland variant is recommended.
|
||||
|
||||
If you are using the Flatpak and run into issues with Wayland, you can disable Wayland support for the Azahar Flatpak using [Flatseal](https://flathub.org/en/apps/com.github.tchx84.Flatseal).
|
||||
|
||||
# Build instructions
|
||||
|
||||
|
||||
3
dist/apple/Info.plist.in
vendored
3
dist/apple/Info.plist.in
vendored
@ -75,6 +75,9 @@
|
||||
<true/>
|
||||
<key>NSHighResolutionCapable</key>
|
||||
<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>
|
||||
<true/>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
|
||||
2
dist/compatibility_list
vendored
2
dist/compatibility_list
vendored
@ -1 +1 @@
|
||||
Subproject commit 4f39041699412873d0afcec89a9313148a192647
|
||||
Subproject commit a36decbe43d0e5a570ac3d3ba9a0b226dc832a17
|
||||
54
dist/languages/ca_ES_valencia.ts
vendored
54
dist/languages/ca_ES_valencia.ts
vendored
@ -27,7 +27,7 @@
|
||||
<message>
|
||||
<location filename="../../src/citra_qt/aboutdialog.ui" line="30"/>
|
||||
<source><html><head/><body><p><img src=":/icons/default/256x256/azahar.png"/></p></body></html></source>
|
||||
<translation type="unfinished"/>
|
||||
<translation><html><head/><body><p><img src=":/icons/default/256x256/azahar.png"/></p></body></html></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/citra_qt/aboutdialog.ui" line="60"/>
|
||||
@ -725,7 +725,7 @@ Desitja ignorar l'error i continuar?</translation>
|
||||
<message>
|
||||
<location filename="../../src/citra_qt/configuration/configure_debug.ui" line="107"/>
|
||||
<source>Show log output in console</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Mostrar l'eixida del registre en la consola</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/citra_qt/configuration/configure_debug.ui" line="114"/>
|
||||
@ -2275,7 +2275,7 @@ Desitja ignorar l'error i continuar?</translation>
|
||||
<message>
|
||||
<location filename="../../src/citra_qt/configuration/configure_motion_touch.cpp" line="102"/>
|
||||
<source><a href='https://web.archive.org/web/20240301211230/https://citra-emu.org/wiki/using-a-controller-or-android-phone-for-motion-or-touch-input'><span style="text-decoration: underline; color:#039be5;">Learn More</span></a></source>
|
||||
<translation type="unfinished"/>
|
||||
<translation><a href='https://web.archive.org/web/20240301211230/https://citra-emu.org/wiki/using-a-controller-or-android-phone-for-motion-or-touch-input'><span style="text-decoration: underline; color:#039be5;">Més Informació</span></a></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/citra_qt/configuration/configure_motion_touch.cpp" line="209"/>
|
||||
@ -2494,12 +2494,12 @@ Desitja ignorar l'error i continuar?</translation>
|
||||
<message>
|
||||
<location filename="../../src/citra_qt/configuration/configure_storage.ui" line="187"/>
|
||||
<source>Compress installed CIA content</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Comprimir el contingut de CIAs instal·lats</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/citra_qt/configuration/configure_storage.ui" line="190"/>
|
||||
<source>Compresses the content of CIA files when installed to the emulated SD card. Only affects CIA content which is installed while the setting is enabled.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Comprimix el contingut de fitxers CIA quan són instal·lats a la SD emulada. Només afecta contingut CIA instal·lat amb esta opció activada.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/citra_qt/configuration/configure_storage.cpp" line="26"/>
|
||||
@ -4431,7 +4431,7 @@ Vols reinstal·lar els arxius de totes maneres?</translation>
|
||||
<message>
|
||||
<location filename="../../src/citra_qt/citra_qt.cpp" line="2250"/>
|
||||
<source>3DS Installation File (*.cia *.zcia)</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Fitxers d'Instalació de 3DS (*.cia *.zcia)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/citra_qt/citra_qt.cpp" line="2250"/>
|
||||
@ -4510,24 +4510,24 @@ Vols reinstal·lar els arxius de totes maneres?</translation>
|
||||
<location filename="../../src/citra_qt/citra_qt.cpp" line="3107"/>
|
||||
<location filename="../../src/citra_qt/citra_qt.cpp" line="3113"/>
|
||||
<source>Error compressing file</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Error al comprimir el fitxer</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/citra_qt/citra_qt.cpp" line="2339"/>
|
||||
<source>File compress operation failed, check log for details.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Operació de compressió fallida, mira el registre per a més detalls.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/citra_qt/citra_qt.cpp" line="2341"/>
|
||||
<location filename="../../src/citra_qt/citra_qt.cpp" line="3181"/>
|
||||
<location filename="../../src/citra_qt/citra_qt.cpp" line="3188"/>
|
||||
<source>Error decompressing file</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Error de descompressió del fitxer</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/citra_qt/citra_qt.cpp" line="2342"/>
|
||||
<source>File decompress operation failed, check log for details.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Operació de descompressió fallida, mira el registre per a més detalls.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/citra_qt/citra_qt.cpp" line="2364"/>
|
||||
@ -4663,62 +4663,62 @@ Per a veure una guia sobre com instal·lar FFmpeg, polsa Ajuda.</translation>
|
||||
<message>
|
||||
<location filename="../../src/citra_qt/citra_qt.cpp" line="3063"/>
|
||||
<source>Load 3DS ROM File</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Carregar ROM de 3DS</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/citra_qt/citra_qt.cpp" line="3064"/>
|
||||
<source>3DS ROM Files (*.cia *cci *3dsx *cxi)</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Fitxers ROM 3DS (*.cia *cci *3dsx *cxi)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/citra_qt/citra_qt.cpp" line="3108"/>
|
||||
<source>The selected file is not a compatible 3DS ROM format. Make sure you have chosen the right file, and that it is not encrypted.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>El fitxer seleccionat no és un ROM de 3DS compatible. Assegura't que has triat el fitxer correcte i que no estiga xifrat.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/citra_qt/citra_qt.cpp" line="3114"/>
|
||||
<source>The selected file is already compressed.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>El fitxer seleccionat ja està comprimit.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/citra_qt/citra_qt.cpp" line="3119"/>
|
||||
<source>3DS Compressed ROM File (*.%1)</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Fitxer ROM 3DS comprimit (*.%1)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/citra_qt/citra_qt.cpp" line="3127"/>
|
||||
<source>Save 3DS Compressed ROM File</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Desar fitxer 3DS comprimit</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/citra_qt/citra_qt.cpp" line="3152"/>
|
||||
<source>Load 3DS Compressed ROM File</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Carregar fitxer 3DS comprimit</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/citra_qt/citra_qt.cpp" line="3153"/>
|
||||
<source>3DS Compressed ROM Files (*.zcia *zcci *z3dsx *zcxi)</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Fitxer ROM 3DS comprimit (*.zcia *zcci *z3dsx *zcxi)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/citra_qt/citra_qt.cpp" line="3182"/>
|
||||
<source>The selected file is not a compatible compressed 3DS ROM format. Make sure you have chosen the right file.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>El fitxer seleccionat no és un format de ROM 3DS comprimit compatible. Assegura't d'haver triat l'arxiu correcte.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/citra_qt/citra_qt.cpp" line="3189"/>
|
||||
<source>The selected file is already decompressed.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>El fitxer seleccionat ja està descomprimit.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/citra_qt/citra_qt.cpp" line="3194"/>
|
||||
<source>3DS ROM File (*.%1)</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Fitxer ROM 3DS (*.%1)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/citra_qt/citra_qt.cpp" line="3202"/>
|
||||
<source>Save 3DS ROM File</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Desar fitxer ROM 3DS</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/citra_qt/citra_qt.cpp" line="3227"/>
|
||||
@ -4818,7 +4818,7 @@ Per a veure una guia sobre com instal·lar FFmpeg, polsa Ajuda.</translation>
|
||||
<message>
|
||||
<location filename="../../src/citra_qt/citra_qt.cpp" line="3431"/>
|
||||
<source>Frame: %1 ms (GPU: [CMD: %2 ms, SWP: %3 ms], IPC: %4 ms, SVC: %5 ms, Rem: %6 ms)</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Frame: %1 ms (GPU: [CMD: %2 ms, SWP: %3 ms], IPC: %4 ms, SVC: %5 ms, Rem: %6 ms)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/citra_qt/citra_qt.cpp" line="3440"/>
|
||||
@ -4839,7 +4839,7 @@ Per a veure una guia sobre com instal·lar FFmpeg, polsa Ajuda.</translation>
|
||||
<message>
|
||||
<location filename="../../src/citra_qt/citra_qt.cpp" line="3600"/>
|
||||
<source>%1 is missing. Please <a href='https://web.archive.org/web/20240304201103/https://citra-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-3ds-console/'>dump your system archives</a>.<br/>Continuing emulation may result in crashes and bugs.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Falta %1 . Per favor,<a href='https://web.archive.org/web/20240304201103/https://citra-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-3ds-console/'>bolca els teus arxius de sistema</a>.<br/>Continuar l'emulació pot resultar en penges i errors.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/citra_qt/citra_qt.cpp" line="3608"/>
|
||||
@ -4869,7 +4869,7 @@ Per a veure una guia sobre com instal·lar FFmpeg, polsa Ajuda.</translation>
|
||||
<message>
|
||||
<location filename="../../src/citra_qt/citra_qt.cpp" line="3627"/>
|
||||
<source>A fatal error occurred. <a href='https://web.archive.org/web/20240228001712/https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>Check the log</a> for details.<br/>Continuing emulation may result in crashes and bugs.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Error fatal.<a href='https://web.archive.org/web/20240228001712/https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>Mira el log</a>per a més detalls.<br/>Continuar l'emulació pot resultar en penges i errors.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/citra_qt/citra_qt.cpp" line="3632"/>
|
||||
@ -6387,12 +6387,12 @@ Missatge de depuració:</translation>
|
||||
<message>
|
||||
<location filename="../../src/citra_qt/main.ui" line="466"/>
|
||||
<source>Compress ROM File...</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Comprimir fitxer ROM...</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/citra_qt/main.ui" line="471"/>
|
||||
<source>Decompress ROM File...</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Descomprimir fitxer ROM...</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/citra_qt/main.ui" line="479"/>
|
||||
|
||||
4
dist/languages/es_ES.ts
vendored
4
dist/languages/es_ES.ts
vendored
@ -4517,7 +4517,7 @@ Reinstall the files anyway?</source>
|
||||
<message>
|
||||
<location filename="../../src/citra_qt/citra_qt.cpp" line="2339"/>
|
||||
<source>File compress operation failed, check log for details.</source>
|
||||
<translation>Operación de comprensión fallida, mira el registro para más detalles.</translation>
|
||||
<translation>Operación de compresión fallida, mira el registro para más detalles.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/citra_qt/citra_qt.cpp" line="2341"/>
|
||||
@ -4529,7 +4529,7 @@ Reinstall the files anyway?</source>
|
||||
<message>
|
||||
<location filename="../../src/citra_qt/citra_qt.cpp" line="2342"/>
|
||||
<source>File decompress operation failed, check log for details.</source>
|
||||
<translation>Operación de descomprensión fallida, mira el registro para más detalles.</translation>
|
||||
<translation>Operación de descompresión fallida, mira el registro para más detalles.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/citra_qt/citra_qt.cpp" line="2364"/>
|
||||
|
||||
15
dist/languages/ru_RU.ts
vendored
15
dist/languages/ru_RU.ts
vendored
@ -3894,7 +3894,7 @@ Drag points to change position, or double-click table cells to edit values.</sou
|
||||
<message>
|
||||
<location filename="../../src/citra_qt/configuration/configure_web.ui" line="26"/>
|
||||
<source>Show current application in your Discord status</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Показывать текущее приложение в статусе Discord</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -4027,7 +4027,7 @@ Drag points to change position, or double-click table cells to edit values.</sou
|
||||
<location filename="../../src/citra_qt/dumping/dumping_dialog.cpp" line="25"/>
|
||||
<location filename="../../src/citra_qt/dumping/dumping_dialog.cpp" line="85"/>
|
||||
<source>Azahar</source>
|
||||
<translation type="unfinished">Azahar</translation>
|
||||
<translation>Azahar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/citra_qt/dumping/dumping_dialog.cpp" line="25"/>
|
||||
@ -4170,7 +4170,7 @@ Please check your FFmpeg installation used for compilation.</source>
|
||||
<message>
|
||||
<location filename="../../src/citra_qt/citra_qt.cpp" line="1299"/>
|
||||
<source>GBA Virtual Console is not supported by Azahar.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Azahar не поддерживает GBA Virtual Console.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/citra_qt/citra_qt.cpp" line="1304"/>
|
||||
@ -4315,7 +4315,7 @@ Please check your FFmpeg installation used for compilation.</source>
|
||||
<location filename="../../src/citra_qt/citra_qt.cpp" line="3682"/>
|
||||
<location filename="../../src/citra_qt/citra_qt.cpp" line="3775"/>
|
||||
<source>Azahar</source>
|
||||
<translation type="unfinished">Azahar</translation>
|
||||
<translation>Azahar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/citra_qt/citra_qt.cpp" line="2066"/>
|
||||
@ -4523,7 +4523,7 @@ Reinstall the files anyway?</source>
|
||||
<location filename="../../src/citra_qt/citra_qt.cpp" line="3181"/>
|
||||
<location filename="../../src/citra_qt/citra_qt.cpp" line="3188"/>
|
||||
<source>Error decompressing file</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Ошибра при разжатии файла</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/citra_qt/citra_qt.cpp" line="2342"/>
|
||||
@ -5036,7 +5036,7 @@ Would you like to download it?</source>
|
||||
<message>
|
||||
<location filename="../../src/citra_qt/game_list.cpp" line="366"/>
|
||||
<source>Don't show again</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Не показывать снова</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/citra_qt/game_list.cpp" line="564"/>
|
||||
@ -7383,7 +7383,8 @@ They may have left the room.</source>
|
||||
<source>Azahar has detected user data for Citra.
|
||||
|
||||
</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Azahar обнаружил файлы пользователя Citra.
|
||||
</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/citra_qt/user_data_migration.cpp" line="77"/>
|
||||
|
||||
31
docker/azahar-room/Dockerfile
Normal file
31
docker/azahar-room/Dockerfile
Normal file
@ -0,0 +1,31 @@
|
||||
# This Dockerfile assumes that it is being built from the project root directory, e.g.:
|
||||
# $ docker build -f docker/azahar-room/Dockerfile -t azahar-room .
|
||||
|
||||
# --- Builder ----------------
|
||||
FROM opensauce04/azahar-build-environment:latest AS builder
|
||||
|
||||
COPY . /var/azahar-src
|
||||
|
||||
RUN mkdir builddir && cd builddir && \
|
||||
cmake /var/azahar-src -G Ninja \
|
||||
-DENABLE_QT=OFF \
|
||||
-DENABLE_TESTS=OFF \
|
||||
-DENABLE_ROOM=ON \
|
||||
-DENABLE_ROOM_STANDALONE=ON \
|
||||
-DENABLE_OPENGL=OFF $(:"TODO: Can we disable these automatically when there's no frontend?") \
|
||||
-DENABLE_VULKAN=OFF \
|
||||
-DENABLE_SDL2=OFF \
|
||||
-DENABLE_LIBUSB=OFF \
|
||||
-DENABLE_CUBEB=OFF \
|
||||
-DENABLE_OPENAL=OFF && \
|
||||
ninja && \
|
||||
mv bin/Release/azahar-room /usr/local/bin/ && \
|
||||
cd .. && rm -rf builddir
|
||||
|
||||
# --- Final ------------------
|
||||
FROM debian:trixie AS final
|
||||
|
||||
RUN apt-get update && apt-get -y full-upgrade
|
||||
RUN apt-get install -y iputils-ping net-tools
|
||||
|
||||
COPY --from=builder /usr/local/bin/azahar-room /usr/local/bin/azahar-room
|
||||
76
externals/CMakeLists.txt
vendored
76
externals/CMakeLists.txt
vendored
@ -1,24 +1,37 @@
|
||||
# Definitions for all external bundled libraries
|
||||
|
||||
# Suppress warnings from external libraries
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
|
||||
if (MSVC)
|
||||
add_compile_options(/W0)
|
||||
else()
|
||||
add_compile_options(-w)
|
||||
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)
|
||||
include(DownloadExternals)
|
||||
include(ExternalProject)
|
||||
|
||||
# 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")
|
||||
set(BOOST_ROOT "${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 "")
|
||||
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
|
||||
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
|
||||
)
|
||||
target_link_libraries(boost_iostreams PUBLIC boost)
|
||||
# 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)
|
||||
# Add additional boost libs here; remember to ALIAS them in the root CMakeLists!
|
||||
endif()
|
||||
|
||||
# Catch2
|
||||
@ -73,6 +82,7 @@ endif()
|
||||
# dds-ktx
|
||||
add_library(dds-ktx INTERFACE)
|
||||
target_include_directories(dds-ktx INTERFACE ./dds-ktx)
|
||||
target_disable_warnings(dds-ktx)
|
||||
|
||||
# fmt and Xbyak need to be added before dynarmic
|
||||
# libfmt
|
||||
@ -137,7 +147,8 @@ endif()
|
||||
|
||||
# MicroProfile
|
||||
add_library(microprofile INTERFACE)
|
||||
target_include_directories(microprofile SYSTEM INTERFACE ./microprofile)
|
||||
target_include_directories(microprofile INTERFACE ./microprofile)
|
||||
target_disable_warnings(microprofile)
|
||||
if (ENABLE_MICROPROFILE)
|
||||
target_compile_definitions(microprofile INTERFACE MICROPROFILE_ENABLED=1)
|
||||
else()
|
||||
@ -146,10 +157,11 @@ endif()
|
||||
|
||||
# Nihstro
|
||||
add_library(nihstro-headers INTERFACE)
|
||||
target_include_directories(nihstro-headers SYSTEM INTERFACE ./nihstro/include)
|
||||
if (MSVC)
|
||||
# TODO: For some reason MSVC still applies this warning even with /W0 for externals.
|
||||
target_compile_options(nihstro-headers INTERFACE /wd4715)
|
||||
target_include_directories(nihstro-headers INTERFACE ./nihstro/include)
|
||||
target_disable_warnings(nihstro-headers)
|
||||
if (NOT MSVC)
|
||||
# TODO: For some reason MSYS2 still applied this warnin even with -w
|
||||
target_compile_options(nihstro-headers INTERFACE -Wno-invalid-specialization)
|
||||
endif()
|
||||
|
||||
# Open Source Archives
|
||||
@ -173,7 +185,8 @@ if (USE_SYSTEM_FFMPEG_HEADERS)
|
||||
endif()
|
||||
if (NOT FOUND_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()
|
||||
|
||||
# SoundTouch
|
||||
@ -230,13 +243,8 @@ else()
|
||||
)
|
||||
target_link_libraries(zstd_seekable PUBLIC libzstd_static)
|
||||
|
||||
target_link_libraries(libzstd_static INTERFACE zstd_seekable)
|
||||
|
||||
add_library(zstd ALIAS libzstd_static)
|
||||
|
||||
install(TARGETS zstd_seekable
|
||||
EXPORT zstdExports
|
||||
)
|
||||
add_library(zstd INTERFACE)
|
||||
target_link_libraries(zstd INTERFACE libzstd_static zstd_seekable)
|
||||
endif()
|
||||
|
||||
# ENet
|
||||
@ -294,7 +302,8 @@ if (USE_SYSTEM_JSON)
|
||||
# Citra uses "#include <json.hpp>" so we have to add this manually
|
||||
target_include_directories(json-headers SYSTEM INTERFACE "${NLOHMANN_PREFIX}/nlohmann")
|
||||
else()
|
||||
target_include_directories(json-headers SYSTEM INTERFACE ./json)
|
||||
target_include_directories(json-headers INTERFACE ./json)
|
||||
target_disable_warnings(json-headers)
|
||||
endif()
|
||||
|
||||
# OpenSSL
|
||||
@ -310,7 +319,8 @@ if (NOT OPENSSL_FOUND)
|
||||
set(LIBRESSL_SKIP_INSTALL ON CACHE BOOL "")
|
||||
set(OPENSSLDIR "/etc/ssl/")
|
||||
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)
|
||||
get_directory_property(OPENSSL_LIBRARIES
|
||||
DIRECTORY libressl
|
||||
@ -327,17 +337,20 @@ if(USE_SYSTEM_CPP_HTTPLIB)
|
||||
get_target_property(HTTP_LIBS httplib::httplib INTERFACE_LINK_LIBRARIES)
|
||||
if(HTTP_LIBS)
|
||||
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()
|
||||
if(CppHttp_FOUND)
|
||||
target_link_libraries(httplib INTERFACE httplib::httplib)
|
||||
else()
|
||||
message(STATUS "Cpp-httplib not found or not suitable version! Falling back to bundled...")
|
||||
target_include_directories(httplib SYSTEM INTERFACE ./httplib)
|
||||
endif()
|
||||
target_include_directories(httplib INTERFACE ./httplib)
|
||||
target_disable_warnings(httplib)
|
||||
endif()
|
||||
endif()
|
||||
else()
|
||||
target_include_directories(httplib SYSTEM INTERFACE ./httplib)
|
||||
target_include_directories(httplib INTERFACE ./httplib)
|
||||
target_disable_warnings(httplib)
|
||||
endif()
|
||||
target_compile_options(httplib INTERFACE -DCPPHTTPLIB_OPENSSL_SUPPORT)
|
||||
target_link_libraries(httplib INTERFACE ${OPENSSL_LIBRARIES})
|
||||
@ -354,7 +367,8 @@ if (ENABLE_WEB_SERVICE)
|
||||
target_link_libraries(cpp-jwt INTERFACE cpp-jwt::cpp-jwt)
|
||||
else()
|
||||
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)
|
||||
endif()
|
||||
endif()
|
||||
@ -453,7 +467,8 @@ if (ENABLE_VULKAN)
|
||||
endif()
|
||||
else()
|
||||
add_library(vma INTERFACE)
|
||||
target_include_directories(vma SYSTEM INTERFACE ./vma/include)
|
||||
target_include_directories(vma INTERFACE ./vma/include)
|
||||
target_disable_warnings(vma)
|
||||
endif()
|
||||
|
||||
# vulkan-headers
|
||||
@ -465,7 +480,8 @@ if (ENABLE_VULKAN)
|
||||
target_link_libraries(vulkan-headers INTERFACE Vulkan::Headers)
|
||||
endif()
|
||||
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()
|
||||
|
||||
# adrenotools
|
||||
|
||||
2
externals/boost
vendored
2
externals/boost
vendored
@ -1 +1 @@
|
||||
Subproject commit 3c27c785ad0f8a742af02e620dc225673f3a12d8
|
||||
Subproject commit 04f8fd64dababe6c82151792a134e3a03b395fe6
|
||||
2
externals/fmt
vendored
2
externals/fmt
vendored
@ -1 +1 @@
|
||||
Subproject commit 123913715afeb8a437e6388b4473fcc4753e1c9a
|
||||
Subproject commit e424e3f2e607da02742f73db84873b8084fc714c
|
||||
2
externals/teakra
vendored
2
externals/teakra
vendored
@ -1 +1 @@
|
||||
Subproject commit 01db7cdd00aabcce559a8dddce8798dabb71949b
|
||||
Subproject commit 3d697a18df504f4677b65129d9ab14c7c597e3eb
|
||||
@ -174,6 +174,9 @@ endif()
|
||||
if(ENABLE_VULKAN)
|
||||
add_compile_definitions(ENABLE_VULKAN)
|
||||
endif()
|
||||
if(ENABLE_DEVELOPER_OPTIONS)
|
||||
add_compile_definitions(ENABLE_DEVELOPER_OPTIONS)
|
||||
endif()
|
||||
|
||||
add_subdirectory(common)
|
||||
add_subdirectory(core)
|
||||
|
||||
@ -64,7 +64,7 @@ android {
|
||||
// The application ID refers to Lime3DS to allow for
|
||||
// the Play Store listing, which was originally set up for Lime3DS, to still be used.
|
||||
applicationId = "io.github.lime3ds.android"
|
||||
minSdk = 28
|
||||
minSdk = 29
|
||||
targetSdk = 35
|
||||
versionCode = autoVersion
|
||||
versionName = getGitVersion()
|
||||
@ -125,7 +125,7 @@ android {
|
||||
applicationIdSuffix = ".debug"
|
||||
versionNameSuffix = "-debug"
|
||||
signingConfig = signingConfigs.getByName("debug")
|
||||
isShrinkResources = true
|
||||
isShrinkResources = true // TODO: Does this actually do anything when isDebuggable is enabled? -OS
|
||||
isDebuggable = true
|
||||
isJniDebuggable = true
|
||||
proguardFiles(
|
||||
@ -135,6 +135,22 @@ android {
|
||||
isDefault = true
|
||||
}
|
||||
|
||||
// Same as above, but with isDebuggable disabled.
|
||||
// Primarily exists to allow development on hardened_malloc systems (e.g. GrapheneOS) without constantly tripping over years-old and seemingly harmless memory bugs.
|
||||
// We should fix those bugs eventually, but for now this exists as a workaround to allow other work to be done.
|
||||
register("relWithDebInfoLite") {
|
||||
initWith(getByName("relWithDebInfo"))
|
||||
signingConfig = signingConfigs.getByName("debug")
|
||||
isDebuggable = false
|
||||
installation {
|
||||
enableBaselineProfile = false // Disabled by default when isDebuggable is true
|
||||
}
|
||||
lint {
|
||||
checkReleaseBuilds = false // Ditto
|
||||
// The name of this property is misleading, this doesn't actually disable linting for the `release` build.
|
||||
}
|
||||
}
|
||||
|
||||
// Signed by debug key disallowing distribution on Play Store.
|
||||
// Attaches 'debug' suffix to version and package name, allowing installation alongside the release build.
|
||||
debug {
|
||||
@ -186,7 +202,7 @@ dependencies {
|
||||
|
||||
// Download Vulkan Validation Layers from the KhronosGroup GitHub.
|
||||
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"))
|
||||
onlyIfModified(true)
|
||||
}
|
||||
|
||||
@ -96,6 +96,24 @@ object NativeLibrary {
|
||||
*/
|
||||
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 getTitleId(filename: String): Long
|
||||
|
||||
@ -10,6 +10,7 @@ import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.InputDevice
|
||||
import android.view.KeyEvent
|
||||
@ -47,6 +48,7 @@ import org.citra.citra_emu.utils.FileBrowserHelper
|
||||
import org.citra.citra_emu.utils.EmulationLifecycleUtil
|
||||
import org.citra.citra_emu.utils.EmulationMenuSettings
|
||||
import org.citra.citra_emu.utils.Log
|
||||
import org.citra.citra_emu.utils.RefreshRateUtil
|
||||
import org.citra.citra_emu.utils.ThemeUtil
|
||||
import org.citra.citra_emu.viewmodel.EmulationViewModel
|
||||
|
||||
@ -60,7 +62,15 @@ class EmulationActivity : AppCompatActivity() {
|
||||
private lateinit var binding: ActivityEmulationBinding
|
||||
private lateinit var screenAdjustmentUtil: ScreenAdjustmentUtil
|
||||
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
|
||||
get() {
|
||||
@ -74,11 +84,13 @@ class EmulationActivity : AppCompatActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
requestWindowFeature(Window.FEATURE_NO_TITLE)
|
||||
|
||||
RefreshRateUtil.enforceRefreshRate(this, sixtyHz = true)
|
||||
|
||||
ThemeUtil.setTheme(this)
|
||||
settingsViewModel.settings.loadSettings()
|
||||
super.onCreate(savedInstanceState)
|
||||
secondaryDisplay = SecondaryDisplay(this);
|
||||
secondaryDisplay.updateDisplay();
|
||||
secondaryDisplay = SecondaryDisplay(this)
|
||||
secondaryDisplay.updateDisplay()
|
||||
|
||||
binding = ActivityEmulationBinding.inflate(layoutInflater)
|
||||
screenAdjustmentUtil = ScreenAdjustmentUtil(this, windowManager, settingsViewModel.settings)
|
||||
@ -101,13 +113,7 @@ class EmulationActivity : AppCompatActivity() {
|
||||
windowManager.defaultDisplay.rotation
|
||||
)
|
||||
|
||||
EmulationLifecycleUtil.addShutdownHook(hook = {
|
||||
if (intent.getBooleanExtra("launched_from_shortcut", false)) {
|
||||
finishAffinity()
|
||||
} else {
|
||||
this.finish()
|
||||
}
|
||||
})
|
||||
EmulationLifecycleUtil.addShutdownHook(onShutdown)
|
||||
|
||||
isEmulationRunning = true
|
||||
instance = this
|
||||
@ -165,12 +171,12 @@ class EmulationActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
EmulationLifecycleUtil.clear()
|
||||
EmulationLifecycleUtil.removeHook(onShutdown)
|
||||
NativeLibrary.playTimeManagerStop()
|
||||
isEmulationRunning = false
|
||||
instance = null
|
||||
secondaryDisplay.releasePresentation()
|
||||
secondaryDisplay.releaseVD();
|
||||
secondaryDisplay.releaseVD()
|
||||
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
@ -11,29 +11,30 @@ import android.hardware.display.DisplayManager
|
||||
import android.hardware.display.VirtualDisplay
|
||||
import android.os.Bundle
|
||||
import android.view.Display
|
||||
import android.view.MotionEvent
|
||||
import android.view.Surface
|
||||
import android.view.SurfaceHolder
|
||||
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.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 val displayManager = context.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
|
||||
private val vd: VirtualDisplay
|
||||
|
||||
init {
|
||||
val st = SurfaceTexture(0)
|
||||
st.setDefaultBufferSize(1920, 1080)
|
||||
val vdSurface = Surface(st)
|
||||
vd = displayManager.createVirtualDisplay(
|
||||
"HiddenDisplay",
|
||||
1920,
|
||||
1080,
|
||||
320,
|
||||
vdSurface,
|
||||
null,
|
||||
DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION
|
||||
)
|
||||
displayManager.registerDisplayListener(this, null)
|
||||
}
|
||||
|
||||
fun updateSurface() {
|
||||
@ -44,16 +45,23 @@ class SecondaryDisplay(val context: Context) {
|
||||
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() {
|
||||
// decide if we are going to the external display or the internal one
|
||||
var display = getCustomerDisplay()
|
||||
var display = getExternalDisplay(context)
|
||||
if (display == null ||
|
||||
IntSetting.SECONDARY_DISPLAY_LAYOUT.int == SecondaryDisplayLayout.NONE.int) {
|
||||
display = vd.display
|
||||
}
|
||||
|
||||
// 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
|
||||
releasePresentation()
|
||||
@ -61,29 +69,41 @@ class SecondaryDisplay(val context: Context) {
|
||||
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() {
|
||||
pres?.dismiss()
|
||||
pres = null
|
||||
}
|
||||
|
||||
fun releaseVD() {
|
||||
displayManager.unregisterDisplayListener(this)
|
||||
vd.release()
|
||||
}
|
||||
|
||||
override fun onDisplayAdded(displayId: Int) {
|
||||
updateDisplay()
|
||||
}
|
||||
|
||||
override fun onDisplayRemoved(displayId: Int) {
|
||||
updateDisplay()
|
||||
}
|
||||
override fun onDisplayChanged(displayId: Int) {
|
||||
updateDisplay()
|
||||
}
|
||||
}
|
||||
class SecondaryDisplayPresentation(
|
||||
context: Context, display: Display, val parent: SecondaryDisplay
|
||||
) : Presentation(context, display) {
|
||||
private lateinit var surfaceView: SurfaceView
|
||||
private var touchscreenPointerId = -1
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
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
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
@ -19,13 +19,14 @@ enum class BooleanSetting(
|
||||
INSTANT_DEBUG_LOG("instant_debug_log", Settings.SECTION_DEBUG, false),
|
||||
ENABLE_RPC_SERVER("enable_rpc_server", Settings.SECTION_DEBUG, false),
|
||||
CUSTOM_LAYOUT("custom_layout",Settings.SECTION_LAYOUT,false),
|
||||
OVERLAY_SHOW_FPS("overlay_show_fps", Settings.SECTION_LAYOUT, true),
|
||||
OVERLAY_SHOW_FRAMETIME("overlay_show_frame_time", Settings.SECTION_LAYOUT, false),
|
||||
OVERLAY_SHOW_SPEED("overlay_show_speed", Settings.SECTION_LAYOUT, false),
|
||||
OVERLAY_SHOW_APP_RAM_USAGE("overlay_show_app_ram_usage", Settings.SECTION_LAYOUT, false),
|
||||
OVERLAY_SHOW_AVAILABLE_RAM("overlay_show_available_ram", Settings.SECTION_LAYOUT, false),
|
||||
OVERLAY_SHOW_BATTERY_TEMP("overlay_show_battery_temp", Settings.SECTION_LAYOUT, false),
|
||||
OVERLAY_BACKGROUND("overlay_background", Settings.SECTION_LAYOUT, false),
|
||||
PERF_OVERLAY_ENABLE("performance_overlay_enable", Settings.SECTION_LAYOUT, false),
|
||||
PERF_OVERLAY_SHOW_FPS("performance_overlay_show_fps", Settings.SECTION_LAYOUT, true),
|
||||
PERF_OVERLAY_SHOW_FRAMETIME("performance_overlay_show_frame_time", Settings.SECTION_LAYOUT, false),
|
||||
PERF_OVERLAY_SHOW_SPEED("performance_overlay_show_speed", Settings.SECTION_LAYOUT, false),
|
||||
PERF_OVERLAY_SHOW_APP_RAM_USAGE("performance_overlay_show_app_ram_usage", Settings.SECTION_LAYOUT, false),
|
||||
PERF_OVERLAY_SHOW_AVAILABLE_RAM("performance_overlay_show_available_ram", 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),
|
||||
DETERMINISTIC_ASYNC_OPERATIONS("deterministic_async_operations", Settings.SECTION_DEBUG, false),
|
||||
REQUIRED_ONLINE_LLE_MODULES("enable_required_online_lle_modules", Settings.SECTION_SYSTEM, false),
|
||||
@ -43,13 +44,14 @@ enum class BooleanSetting(
|
||||
CPU_JIT("use_cpu_jit", Settings.SECTION_CORE, true),
|
||||
HW_SHADER("use_hw_shader", Settings.SECTION_RENDERER, true),
|
||||
SHADER_JIT("use_shader_jit", Settings.SECTION_RENDERER, true),
|
||||
VSYNC("use_vsync_new", Settings.SECTION_RENDERER, true),
|
||||
VSYNC("use_vsync", Settings.SECTION_RENDERER, false),
|
||||
USE_FRAME_LIMIT("use_frame_limit", Settings.SECTION_RENDERER, true),
|
||||
DEBUG_RENDERER("renderer_debug", Settings.SECTION_DEBUG, 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),
|
||||
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
|
||||
|
||||
@ -83,6 +85,8 @@ enum class BooleanSetting(
|
||||
SHADERS_ACCURATE_MUL,
|
||||
USE_ARTIC_BASE_CONTROLLER,
|
||||
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? =
|
||||
|
||||
@ -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
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
@ -10,6 +10,10 @@ enum class FloatSetting(
|
||||
override val defaultValue: Float
|
||||
) : AbstractFloatSetting {
|
||||
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);
|
||||
|
||||
override var float: Float = defaultValue
|
||||
|
||||
@ -16,6 +16,7 @@ import org.citra.citra_emu.NativeLibrary
|
||||
import org.citra.citra_emu.R
|
||||
import org.citra.citra_emu.features.hotkeys.Hotkey
|
||||
import org.citra.citra_emu.features.settings.model.AbstractSetting
|
||||
import org.citra.citra_emu.features.settings.model.AbstractStringSetting
|
||||
import org.citra.citra_emu.features.settings.model.Settings
|
||||
|
||||
class InputBindingSetting(
|
||||
@ -161,6 +162,7 @@ class InputBindingSetting(
|
||||
fun removeOldMapping() {
|
||||
// Try remove all possible keys we wrote for this setting
|
||||
val oldKey = preferences.getString(reverseKey, "")
|
||||
(setting as AbstractStringSetting).string = ""
|
||||
if (oldKey != "") {
|
||||
preferences.edit()
|
||||
.remove(abstractSetting.key) // Used for ui text
|
||||
|
||||
@ -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
|
||||
// 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.ScaledFloatSetting
|
||||
import org.citra.citra_emu.utils.Log
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
class SliderSetting(
|
||||
setting: AbstractSetting?,
|
||||
@ -27,7 +26,8 @@ class SliderSetting(
|
||||
val selectedFloat: Float
|
||||
get() {
|
||||
val setting = setting ?: return defaultValue!!.toFloat()
|
||||
return when (setting) {
|
||||
|
||||
val ret = when (setting) {
|
||||
is AbstractIntSetting -> setting.int.toFloat()
|
||||
is FloatSetting -> setting.float
|
||||
is ScaledFloatSetting -> setting.float
|
||||
@ -36,8 +36,8 @@ class SliderSetting(
|
||||
-1f
|
||||
}
|
||||
}
|
||||
return ret.coerceIn(min.toFloat(), max.toFloat())
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
|
||||
@ -7,6 +7,7 @@ package org.citra.citra_emu.features.settings.ui
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.ViewGroup.MarginLayoutParams
|
||||
@ -37,6 +38,7 @@ import org.citra.citra_emu.features.settings.utils.SettingsFile
|
||||
import org.citra.citra_emu.utils.SystemSaveGame
|
||||
import org.citra.citra_emu.utils.DirectoryInitialization
|
||||
import org.citra.citra_emu.utils.InsetsHelper
|
||||
import org.citra.citra_emu.utils.RefreshRateUtil
|
||||
import org.citra.citra_emu.utils.ThemeUtil
|
||||
|
||||
class SettingsActivity : AppCompatActivity(), SettingsActivityView {
|
||||
@ -49,6 +51,8 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView {
|
||||
override val settings: Settings get() = settingsViewModel.settings
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
RefreshRateUtil.enforceRefreshRate(this)
|
||||
|
||||
ThemeUtil.setTheme(this)
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
@ -4,14 +4,19 @@
|
||||
|
||||
package org.citra.citra_emu.features.settings.ui
|
||||
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
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.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.utils.SystemSaveGame
|
||||
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.PermissionsHandler
|
||||
import org.citra.citra_emu.utils.TurboHelper
|
||||
|
||||
class SettingsActivityPresenter(private val activityView: SettingsActivityView) {
|
||||
@ -60,6 +65,32 @@ class SettingsActivityPresenter(private val activityView: SettingsActivityView)
|
||||
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) {
|
||||
if (finishing && shouldSave) {
|
||||
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
|
||||
NativeLibrary.reloadSettings()
|
||||
NativeLibrary.updateFramebuffer(NativeLibrary.isPortraitMode)
|
||||
updateAndroidImageVisibility()
|
||||
TurboHelper.reloadTurbo(false) // TODO: Can this go somewhere else? -OS
|
||||
}
|
||||
NativeLibrary.reloadSettings()
|
||||
|
||||
@ -555,6 +555,21 @@ class SettingsAdapter(
|
||||
return true
|
||||
}
|
||||
|
||||
fun onInputBindingLongClick(setting: InputBindingSetting, position: Int): Boolean {
|
||||
MaterialAlertDialogBuilder(context)
|
||||
.setMessage(R.string.reset_setting_confirmation)
|
||||
.setPositiveButton(android.R.string.ok) { _: DialogInterface, _: Int ->
|
||||
setting.removeOldMapping()
|
||||
notifyItemChanged(position)
|
||||
fragmentView.onSettingChanged()
|
||||
fragmentView.loadSettingsList()
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.show()
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
fun onClickDisabledSetting(isRuntimeDisabled: Boolean) {
|
||||
val titleId = if (isRuntimeDisabled)
|
||||
R.string.setting_not_editable
|
||||
|
||||
@ -248,6 +248,15 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
|
||||
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
|
||||
)
|
||||
)
|
||||
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(
|
||||
SubmenuSetting(
|
||||
R.string.performance_overlay_options,
|
||||
@ -1201,38 +1293,29 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
|
||||
|
||||
add(
|
||||
SwitchSetting(
|
||||
object : AbstractBooleanSetting {
|
||||
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()
|
||||
},
|
||||
BooleanSetting.PERF_OVERLAY_ENABLE,
|
||||
R.string.performance_overlay_enable,
|
||||
0,
|
||||
"EmulationMenuSettings_showPerfPerformanceOverlay",
|
||||
false
|
||||
BooleanSetting.PERF_OVERLAY_ENABLE.key,
|
||||
BooleanSetting.PERF_OVERLAY_ENABLE.defaultValue
|
||||
)
|
||||
)
|
||||
|
||||
add(
|
||||
SwitchSetting(
|
||||
BooleanSetting.OVERLAY_BACKGROUND,
|
||||
R.string.overlay_background,
|
||||
R.string.overlay_background_description,
|
||||
"overlay_background",
|
||||
false
|
||||
BooleanSetting.PERF_OVERLAY_BACKGROUND,
|
||||
R.string.performance_overlay_background,
|
||||
R.string.performance_overlay_background_description,
|
||||
BooleanSetting.PERF_OVERLAY_BACKGROUND.key,
|
||||
BooleanSetting.PERF_OVERLAY_BACKGROUND.defaultValue
|
||||
)
|
||||
)
|
||||
|
||||
add(
|
||||
SingleChoiceSetting(
|
||||
IntSetting.PERFORMANCE_OVERLAY_POSITION,
|
||||
R.string.overlay_position,
|
||||
R.string.overlay_position_description,
|
||||
R.string.performance_overlay_position,
|
||||
R.string.performance_overlay_position_description,
|
||||
R.array.statsPosition,
|
||||
R.array.statsPositionValues,
|
||||
)
|
||||
@ -1243,61 +1326,61 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
|
||||
|
||||
add(
|
||||
SwitchSetting(
|
||||
BooleanSetting.OVERLAY_SHOW_FPS,
|
||||
R.string.overlay_show_fps,
|
||||
R.string.overlay_show_fps_description,
|
||||
"overlay_show_fps",
|
||||
true
|
||||
BooleanSetting.PERF_OVERLAY_SHOW_FPS,
|
||||
R.string.performance_overlay_show_fps,
|
||||
R.string.performance_overlay_show_fps_description,
|
||||
BooleanSetting.PERF_OVERLAY_SHOW_FPS.key,
|
||||
BooleanSetting.PERF_OVERLAY_SHOW_FPS.defaultValue
|
||||
)
|
||||
)
|
||||
|
||||
add(
|
||||
SwitchSetting(
|
||||
BooleanSetting.OVERLAY_SHOW_FRAMETIME,
|
||||
R.string.overlay_show_frametime,
|
||||
R.string.overlay_show_frametime_description,
|
||||
"overlay_show_frame_time",
|
||||
true
|
||||
BooleanSetting.PERF_OVERLAY_SHOW_FRAMETIME,
|
||||
R.string.performance_overlay_show_frametime,
|
||||
R.string.performance_overlay_show_frametime_description,
|
||||
BooleanSetting.PERF_OVERLAY_SHOW_FRAMETIME.key,
|
||||
BooleanSetting.PERF_OVERLAY_SHOW_FRAMETIME.defaultValue
|
||||
)
|
||||
)
|
||||
|
||||
add(
|
||||
SwitchSetting(
|
||||
BooleanSetting.OVERLAY_SHOW_SPEED,
|
||||
R.string.overlay_show_speed,
|
||||
R.string.overlay_show_speed_description,
|
||||
"overlay_show_speed",
|
||||
false
|
||||
BooleanSetting.PERF_OVERLAY_SHOW_SPEED,
|
||||
R.string.performance_overlay_show_speed,
|
||||
R.string.performance_overlay_show_speed_description,
|
||||
BooleanSetting.PERF_OVERLAY_SHOW_SPEED.key,
|
||||
BooleanSetting.PERF_OVERLAY_SHOW_SPEED.defaultValue
|
||||
)
|
||||
)
|
||||
|
||||
add(
|
||||
SwitchSetting(
|
||||
BooleanSetting.OVERLAY_SHOW_APP_RAM_USAGE,
|
||||
R.string.overlay_show_app_ram_usage,
|
||||
R.string.overlay_show_app_ram_usage_description,
|
||||
"overlay_show_app_ram_usage",
|
||||
false
|
||||
BooleanSetting.PERF_OVERLAY_SHOW_APP_RAM_USAGE,
|
||||
R.string.performance_overlay_show_app_ram_usage,
|
||||
R.string.performance_overlay_show_app_ram_usage_description,
|
||||
BooleanSetting.PERF_OVERLAY_SHOW_APP_RAM_USAGE.key,
|
||||
BooleanSetting.PERF_OVERLAY_SHOW_APP_RAM_USAGE.defaultValue
|
||||
)
|
||||
)
|
||||
|
||||
add(
|
||||
SwitchSetting(
|
||||
BooleanSetting.OVERLAY_SHOW_AVAILABLE_RAM,
|
||||
R.string.overlay_show_available_ram,
|
||||
R.string.overlay_show_available_ram_description,
|
||||
"overlay_show_available_ram",
|
||||
false
|
||||
BooleanSetting.PERF_OVERLAY_SHOW_AVAILABLE_RAM,
|
||||
R.string.performance_overlay_show_available_ram,
|
||||
R.string.performance_overlay_show_available_ram_description,
|
||||
BooleanSetting.PERF_OVERLAY_SHOW_AVAILABLE_RAM.key,
|
||||
BooleanSetting.PERF_OVERLAY_SHOW_AVAILABLE_RAM.defaultValue
|
||||
)
|
||||
)
|
||||
|
||||
add(
|
||||
SwitchSetting(
|
||||
BooleanSetting.OVERLAY_SHOW_BATTERY_TEMP,
|
||||
R.string.overlay_show_battery_temp,
|
||||
R.string.overlay_show_battery_temp_description,
|
||||
"overlay_show_battery_temp",
|
||||
false
|
||||
BooleanSetting.PERF_OVERLAY_SHOW_BATTERY_TEMP,
|
||||
R.string.performance_overlay_show_battery_temp,
|
||||
R.string.performance_overlay_show_battery_temp_description,
|
||||
BooleanSetting.PERF_OVERLAY_SHOW_BATTERY_TEMP.key,
|
||||
BooleanSetting.PERF_OVERLAY_SHOW_BATTERY_TEMP.defaultValue
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@ -51,7 +51,7 @@ class InputBindingSettingViewHolder(val binding: ListItemSettingBinding, adapter
|
||||
|
||||
override fun onLongClick(clicked: View): Boolean {
|
||||
if (setting.isEditable) {
|
||||
adapter.onLongClick(setting.setting!!, bindingAdapterPosition)
|
||||
adapter.onInputBindingLongClick(setting, bindingAdapterPosition)
|
||||
} else {
|
||||
adapter.onClickDisabledSetting(!setting.isEditable)
|
||||
}
|
||||
|
||||
@ -66,6 +66,7 @@ import org.citra.citra_emu.display.ScreenAdjustmentUtil
|
||||
import org.citra.citra_emu.display.ScreenLayout
|
||||
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.Settings
|
||||
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.utils.SettingsFile
|
||||
@ -100,6 +101,10 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
|
||||
|
||||
private val emulationViewModel: EmulationViewModel by activityViewModels()
|
||||
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) {
|
||||
super.onAttach(context)
|
||||
@ -155,9 +160,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
|
||||
retainInstance = true
|
||||
emulationState = EmulationState(game.path)
|
||||
emulationActivity = requireActivity() as EmulationActivity
|
||||
screenAdjustmentUtil = ScreenAdjustmentUtil(requireContext(), requireActivity().windowManager, settingsViewModel.settings)
|
||||
EmulationLifecycleUtil.addShutdownHook(hook = { emulationState.stop() })
|
||||
EmulationLifecycleUtil.addPauseResumeHook(hook = { togglePause() })
|
||||
screenAdjustmentUtil = ScreenAdjustmentUtil(requireContext(), requireActivity().windowManager, settings)
|
||||
EmulationLifecycleUtil.addPauseResumeHook(onPause)
|
||||
EmulationLifecycleUtil.addShutdownHook(onShutdown)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
@ -507,6 +512,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
|
||||
super.onDetach()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
EmulationLifecycleUtil.removeHook(onPause)
|
||||
EmulationLifecycleUtil.removeHook(onShutdown)
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
private fun setupCitraDirectoriesThenStartEmulation() {
|
||||
val directoryInitializationState = DirectoryInitialization.start()
|
||||
if (directoryInitializationState ===
|
||||
@ -662,7 +673,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
|
||||
popupMenu.menu.apply {
|
||||
findItem(R.id.menu_show_overlay).isChecked = EmulationMenuSettings.showOverlay
|
||||
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_emulation_joystick_rel_center).isChecked =
|
||||
EmulationMenuSettings.joystickRelCenter
|
||||
@ -679,7 +690,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
|
||||
}
|
||||
|
||||
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()
|
||||
true
|
||||
}
|
||||
@ -1202,7 +1214,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
|
||||
perfStatsUpdateHandler.removeCallbacks(perfStatsUpdater!!)
|
||||
}
|
||||
|
||||
if (EmulationMenuSettings.showPerformanceOverlay) {
|
||||
if (BooleanSetting.PERF_OVERLAY_ENABLE.boolean) {
|
||||
val SYSTEM_FPS = 0
|
||||
val FPS = 1
|
||||
val SPEED = 2
|
||||
@ -1217,11 +1229,11 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
|
||||
val perfStats = NativeLibrary.getPerfStats()
|
||||
val dividerString = "\u00A0\u2502 "
|
||||
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()))
|
||||
}
|
||||
|
||||
if (BooleanSetting.OVERLAY_SHOW_FRAMETIME.boolean) {
|
||||
if (BooleanSetting.PERF_OVERLAY_SHOW_FRAMETIME.boolean) {
|
||||
if (sb.isNotEmpty()) sb.append(dividerString)
|
||||
sb.append(
|
||||
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)
|
||||
sb.append(
|
||||
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)
|
||||
val appRamUsage =
|
||||
File("/proc/self/statm").readLines()[0].split(' ')[1].toLong() * 4096 / 1000000
|
||||
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)
|
||||
context?.let { ctx ->
|
||||
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)
|
||||
val batteryTemp = getBatteryTemperature()
|
||||
val tempF = celsiusToFahrenheit(batteryTemp)
|
||||
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)
|
||||
} else {
|
||||
binding.performanceOverlayShowText.setBackgroundResource(0)
|
||||
|
||||
@ -153,8 +153,7 @@ class InputOverlay(context: Context?, attrs: AttributeSet?) : SurfaceView(contex
|
||||
if (isActionMove) {
|
||||
NativeLibrary.onTouchMoved(xPosition.toFloat(), yPosition.toFloat())
|
||||
continue
|
||||
}
|
||||
else if (isActionUp) {
|
||||
} else if (isActionUp) {
|
||||
NativeLibrary.onTouchEvent(0f, 0f, false)
|
||||
break // Up and down actions shouldn't loop
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@ package org.citra.citra_emu.ui.main
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.ViewGroup.MarginLayoutParams
|
||||
@ -51,6 +52,7 @@ import org.citra.citra_emu.utils.CitraDirectoryUtils
|
||||
import org.citra.citra_emu.utils.DirectoryInitialization
|
||||
import org.citra.citra_emu.utils.FileBrowserHelper
|
||||
import org.citra.citra_emu.utils.InsetsHelper
|
||||
import org.citra.citra_emu.utils.RefreshRateUtil
|
||||
import org.citra.citra_emu.utils.PermissionsHandler
|
||||
import org.citra.citra_emu.utils.ThemeUtil
|
||||
import org.citra.citra_emu.viewmodel.GamesViewModel
|
||||
@ -66,6 +68,8 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||
override var themeId: Int = 0
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
RefreshRateUtil.enforceRefreshRate(this)
|
||||
|
||||
val splashScreen = installSplashScreen()
|
||||
CitraDirectoryUtils.attemptAutomaticUpdateDirectory()
|
||||
splashScreen.setKeepOnScreenCondition {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Citra Emulator Project
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
@ -18,15 +18,27 @@ object EmulationLifecycleUtil {
|
||||
}
|
||||
|
||||
fun addShutdownHook(hook: Runnable) {
|
||||
shutdownHooks.add(hook)
|
||||
if (shutdownHooks.contains(hook)) {
|
||||
Log.warning("[EmulationLifecycleUtil] Tried to add shutdown hook for function that already existed. Skipping.")
|
||||
} else {
|
||||
shutdownHooks.add(hook)
|
||||
}
|
||||
}
|
||||
|
||||
fun addPauseResumeHook(hook: Runnable) {
|
||||
pauseResumeHooks.add(hook)
|
||||
if (pauseResumeHooks.contains(hook)) {
|
||||
Log.warning("[EmulationLifecycleUtil] Tried to add pause resume hook for function that already existed. Skipping.")
|
||||
} else {
|
||||
pauseResumeHooks.add(hook)
|
||||
}
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
pauseResumeHooks.clear()
|
||||
shutdownHooks.clear()
|
||||
fun removeHook(hook: Runnable) {
|
||||
if (pauseResumeHooks.contains(hook)) {
|
||||
pauseResumeHooks.remove(hook)
|
||||
}
|
||||
if (shutdownHooks.contains(hook)) {
|
||||
shutdownHooks.remove(hook)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -35,13 +35,6 @@ object EmulationMenuSettings {
|
||||
.apply()
|
||||
}
|
||||
|
||||
var showPerformanceOverlay: Boolean
|
||||
get() = preferences.getBoolean("EmulationMenuSettings_showPerformanceOverlay", false)
|
||||
set(value) {
|
||||
preferences.edit()
|
||||
.putBoolean("EmulationMenuSettings_showPerformanceOverlay", value)
|
||||
.apply()
|
||||
}
|
||||
var hapticFeedback: Boolean
|
||||
get() = preferences.getBoolean("EmulationMenuSettings_HapticFeedback", true)
|
||||
set(value) {
|
||||
|
||||
@ -0,0 +1,53 @@
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
package org.citra.citra_emu.utils
|
||||
import android.app.Activity
|
||||
import android.os.Build
|
||||
import androidx.annotation.RequiresApi
|
||||
|
||||
object RefreshRateUtil {
|
||||
// Since Android 15, the OS automatically runs apps categorized as games with a
|
||||
// 60hz refresh rate by default, regardless of the refresh rate set by the user.
|
||||
//
|
||||
// This function sets the refresh rate to either the maximum allowed refresh rate or
|
||||
// 60hz depending on the value of the `sixtyHz` parameter.
|
||||
//
|
||||
// Note: This isn't always the maximum refresh rate that the display is *capable of*,
|
||||
// but is instead the refresh rate chosen by the user in the Android system settings.
|
||||
// For example, if the user selected 120hz in the settings, but the display is capable
|
||||
// of 144hz, 120hz will be treated as the maximum within this function.
|
||||
fun enforceRefreshRate(activity: Activity, sixtyHz: Boolean = false) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
|
||||
return
|
||||
}
|
||||
|
||||
val display = activity.display
|
||||
val window = activity.window
|
||||
|
||||
display?.let {
|
||||
// Get all supported modes and find the one with the highest refresh rate
|
||||
val supportedModes = it.supportedModes
|
||||
val maxRefreshRate = supportedModes.maxByOrNull { mode -> mode.refreshRate }
|
||||
|
||||
if (maxRefreshRate == null) {
|
||||
return
|
||||
}
|
||||
|
||||
var newModeId: Int?
|
||||
if (sixtyHz) {
|
||||
newModeId = supportedModes.firstOrNull { mode -> mode.refreshRate == 60f }?.modeId
|
||||
} else {
|
||||
// Set the preferred display mode to the one with the highest refresh rate
|
||||
newModeId = maxRefreshRate.modeId
|
||||
}
|
||||
|
||||
if (newModeId == null) {
|
||||
return
|
||||
}
|
||||
|
||||
window.attributes.preferredDisplayModeId = newModeId
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -147,7 +147,7 @@ void Config::ReadValues() {
|
||||
ReadSetting("Renderer", Settings::values.use_shader_jit);
|
||||
ReadSetting("Renderer", Settings::values.resolution_factor);
|
||||
ReadSetting("Renderer", Settings::values.use_disk_shader_cache);
|
||||
ReadSetting("Renderer", Settings::values.use_vsync_new);
|
||||
ReadSetting("Renderer", Settings::values.use_vsync);
|
||||
ReadSetting("Renderer", Settings::values.texture_filter);
|
||||
ReadSetting("Renderer", Settings::values.texture_sampling);
|
||||
ReadSetting("Renderer", Settings::values.turbo_limit);
|
||||
@ -172,6 +172,7 @@ void Config::ReadValues() {
|
||||
ReadSetting("Renderer", Settings::values.bg_red);
|
||||
ReadSetting("Renderer", Settings::values.bg_green);
|
||||
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.disable_right_eye_render);
|
||||
|
||||
|
||||
@ -134,10 +134,10 @@ use_shader_jit =
|
||||
# 0 (default): Game Controlled, 1: Nearest Neighbor, 2: Linear
|
||||
texture_sampling =
|
||||
|
||||
# Forces VSync on the display thread. Usually doesn't impact performance, but on some drivers it can
|
||||
# so only turn this off if you notice a speed difference.
|
||||
# 0: Off, 1 (default): On
|
||||
use_vsync_new =
|
||||
# Forces VSync on the display thread. Can cause input delay, so only turn this on
|
||||
# if you have screen tearing, which is unusual on Android
|
||||
# 0 (default): Off, 1: On
|
||||
use_vsync =
|
||||
|
||||
# Reduce stuttering by storing and loading generated shaders to disk
|
||||
# 0: Off, 1 (default. On)
|
||||
@ -148,10 +148,6 @@ use_disk_shader_cache =
|
||||
# factor for the 3DS resolution
|
||||
resolution_factor =
|
||||
|
||||
# Whether to enable V-Sync (caps the framerate at 60FPS) or not.
|
||||
# 0 (default): Off, 1: On
|
||||
vsync_enabled =
|
||||
|
||||
# Turns on the frame limiter, which will limit frames output to the target game speed
|
||||
# 0: Off, 1: On (default)
|
||||
use_frame_limit =
|
||||
@ -170,6 +166,9 @@ bg_red =
|
||||
bg_blue =
|
||||
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
|
||||
# 0 (default): Off, 1: Side by Side, 2: Reverse Side by Side, 3: Anaglyph, 4: Interlaced, 5: Reverse Interlaced, 6: Cardboard VR
|
||||
render_3d =
|
||||
|
||||
@ -122,7 +122,7 @@ EmuWindow_Android_OpenGL::EmuWindow_Android_OpenGL(Core::System& system_, ANativ
|
||||
LOG_CRITICAL(Frontend, "gladLoadGLES2Loader() failed");
|
||||
return;
|
||||
}
|
||||
if (!eglSwapInterval(egl_display, Settings::values.use_vsync_new ? 1 : 0)) {
|
||||
if (!eglSwapInterval(egl_display, Settings::values.use_vsync ? 1 : 0)) {
|
||||
LOG_CRITICAL(Frontend, "eglSwapInterval() failed");
|
||||
return;
|
||||
}
|
||||
@ -218,7 +218,7 @@ void EmuWindow_Android_OpenGL::TryPresenting() {
|
||||
}
|
||||
eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
eglSwapInterval(egl_display, Settings::values.use_vsync_new ? 1 : 0);
|
||||
eglSwapInterval(egl_display, Settings::values.use_vsync ? 1 : 0);
|
||||
system.GPU().Renderer().TryPresent(0, is_secondary);
|
||||
eglSwapBuffers(egl_display, egl_surface);
|
||||
}
|
||||
|
||||
@ -657,6 +657,25 @@ void Java_org_citra_citra_1emu_NativeLibrary_onTouchMoved([[maybe_unused]] JNIEn
|
||||
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,
|
||||
jstring j_filename) {
|
||||
std::string filepath = GetJString(env, j_filename);
|
||||
|
||||
@ -193,6 +193,10 @@
|
||||
<string name="region_mismatch">Advertiment Regió No Vàlida</string>
|
||||
<string name="region_mismatch_emulated">La configuració del país no és vàlida per a la regió emulada seleccionada.</string>
|
||||
<string name="region_mismatch_console">La configuració del país no és vàlida per a la consola vinculada actual.</string>
|
||||
<string name="storage">Emmagatzematge</string>
|
||||
<string name="compress_cia_installs">Comprimir el contingut de CIAs instal·lats</string>
|
||||
<string name="compress_cia_installs_description">Comprimix el contingut de fitxers CIA quan són instal·lats a la SD emulada. Només afecta contingut CIA instal·lat amb esta opció activada.</string>
|
||||
|
||||
<!-- Camera settings strings -->
|
||||
<string name="inner_camera">Càmera interior</string>
|
||||
<string name="outer_left_camera">Càmera esquerra externa</string>
|
||||
@ -397,6 +401,10 @@ S\'esperen errors gràfics temporals quan estigue activat.</string>
|
||||
<string name="emulation_configure_controls">Configurar Controls</string>
|
||||
<string name="emulation_edit_layout">Editar Estil</string>
|
||||
<string name="emulation_done">Fet</string>
|
||||
<string name="emulation_button_sliding">Lliscament de botons</string>
|
||||
<string name="emulation_button_sliding_disabled">Mantindre el botó pressionat originalment</string>
|
||||
<string name="emulation_button_sliding_enabled">Mantindre el botó pressionat actualment</string>
|
||||
<string name="emulation_button_sliding_alternative">Mantindre el botó original i actualment pressionat</string>
|
||||
<string name="emulation_toggle_controls">Activar Controls</string>
|
||||
<string name="emulation_control_scale">Ajustar Escala</string>
|
||||
<string name="emulation_control_scale_global">Escala Global</string>
|
||||
@ -409,6 +417,8 @@ S\'esperen errors gràfics temporals quan estigue activat.</string>
|
||||
<string name="emulation_aspect_ratio">Relació d\'Aspecte</string>
|
||||
<string name="emulation_switch_screen_layout">Estil de Pantalla Apaïsada</string>
|
||||
<string name="emulation_switch_portrait_layout">Estil de Pantalla de Perfil</string>
|
||||
<string name="emulation_switch_secondary_layout">Estil de Pantalla Secundària</string>
|
||||
<string name="emulation_switch_secondary_layout_description">La disposició de la pantalla secundària connectada, amb cable o sense fil (Chromecast, Miracast)</string>
|
||||
<string name="emulation_screen_layout_largescreen">Pantalla amplia</string>
|
||||
<string name="emulation_screen_layout_portrait">Vertical</string>
|
||||
<string name="emulation_screen_layout_single">Pantalla Única</string>
|
||||
@ -416,6 +426,7 @@ S\'esperen errors gràfics temporals quan estigue activat.</string>
|
||||
<string name="emulation_screen_layout_hybrid">Pantalles híbrides</string>
|
||||
<string name="emulation_screen_layout_original">Original</string>
|
||||
<string name="emulation_portrait_layout_top_full">Per omissió</string>
|
||||
<string name="emulation_secondary_display_default">Per defecte del sistema (espill)</string>
|
||||
<string name="emulation_screen_layout_custom">Estil Personalitzat</string>
|
||||
<string name="emulation_small_screen_position">Posició de Pantalla Xicoteta</string>
|
||||
<string name="small_screen_position_description">On hauria d\'aparéixer la pantalla xicoteta en relació amb la gran en Proporció de Pantalla Gran?</string>
|
||||
@ -536,6 +547,10 @@ S\'esperen errors gràfics temporals quan estigue activat.</string>
|
||||
<string name="create_shortcut">Crear drecera</string>
|
||||
<string name="shortcut_name_empty">El nom de la drecera no pot estar buit</string>
|
||||
<string name="shortcut_image_stretch_toggle">Allargar per a ajustar la imatge</string>
|
||||
<string name="game_context_id">ID:</string>
|
||||
<string name="game_context_file">Fitxer:</string>
|
||||
<string name="game_context_type">Tipus:</string>
|
||||
|
||||
<!-- Performance Overlay settings -->
|
||||
<string name="performance_overlay_show">Mostrar informació de rendiment</string>
|
||||
<string name="performance_overlay_options">Informació de rendiment</string>
|
||||
|
||||
@ -194,7 +194,7 @@
|
||||
<string name="region_mismatch_emulated">La configuración del país no es válida para la región emulada seleccionada.</string>
|
||||
<string name="region_mismatch_console">La configuración del país no es válida para la consola vinculada actual.</string>
|
||||
<string name="storage">Almacenamiento</string>
|
||||
<string name="compress_cia_installs">Comprimir el contenido CIA instalado</string>
|
||||
<string name="compress_cia_installs">Comprimir el contenido de CIAs instalados</string>
|
||||
<string name="compress_cia_installs_description">Comprime el contenido de archivos CIA cuando son instalados a la SD emulada. Solo afecta contenido CIA instalado con esta opción activada.</string>
|
||||
|
||||
<!-- Camera settings strings -->
|
||||
@ -402,9 +402,9 @@ Se esperan fallos gráficos temporales cuando ésta esté activado.</string>
|
||||
<string name="emulation_edit_layout">Editar Estilo</string>
|
||||
<string name="emulation_done">Hecho</string>
|
||||
<string name="emulation_button_sliding">Deslizamiento de botones</string>
|
||||
<string name="emulation_button_sliding_disabled">Mantenga el botón presionado originalmente</string>
|
||||
<string name="emulation_button_sliding_enabled">Mantenga el botón presionado actualmente</string>
|
||||
<string name="emulation_button_sliding_alternative">Mantenga el botón original y actualmente presionado</string>
|
||||
<string name="emulation_button_sliding_disabled">Mantener el botón presionado originalmente</string>
|
||||
<string name="emulation_button_sliding_enabled">Mantener el botón presionado actualmente</string>
|
||||
<string name="emulation_button_sliding_alternative">Mantener el botón original y actualmente presionado</string>
|
||||
<string name="emulation_toggle_controls">Activar Controles</string>
|
||||
<string name="emulation_control_scale">Ajustar Escala</string>
|
||||
<string name="emulation_control_scale_global">Escala Global</string>
|
||||
@ -417,6 +417,8 @@ Se esperan fallos gráficos temporales cuando ésta esté activado.</string>
|
||||
<string name="emulation_aspect_ratio">Relación de Aspecto</string>
|
||||
<string name="emulation_switch_screen_layout">Estilo de Pantalla Apaisada</string>
|
||||
<string name="emulation_switch_portrait_layout">Estilo de Pantalla de Perfil</string>
|
||||
<string name="emulation_switch_secondary_layout">Estilo de Pantalla Secundaria</string>
|
||||
<string name="emulation_switch_secondary_layout_description">El estilo de la pantalla secundaria conectada, con cable o inalámbrica (Chromecast, Miracast)</string>
|
||||
<string name="emulation_screen_layout_largescreen">Pantalla amplia</string>
|
||||
<string name="emulation_screen_layout_portrait">Retrato</string>
|
||||
<string name="emulation_screen_layout_single">Pantalla Única</string>
|
||||
@ -424,6 +426,7 @@ Se esperan fallos gráficos temporales cuando ésta esté activado.</string>
|
||||
<string name="emulation_screen_layout_hybrid">Pantallas híbridas</string>
|
||||
<string name="emulation_screen_layout_original">Original</string>
|
||||
<string name="emulation_portrait_layout_top_full">Por defecto</string>
|
||||
<string name="emulation_secondary_display_default">Por defecto del sistema (espejo)</string>
|
||||
<string name="emulation_screen_layout_custom">Estilo personalizado</string>
|
||||
<string name="emulation_small_screen_position">Posición Pantalla Pequeña</string>
|
||||
<string name="small_screen_position_description">¿Dónde debería aparecer la pantalla pequeña en relación con la grande en Disposicion de Pantalla Grande?</string>
|
||||
|
||||
@ -417,6 +417,7 @@
|
||||
<string name="emulation_switch_screen_layout">Geometryczny Układ Ekranu</string>
|
||||
<string name="emulation_switch_portrait_layout">Pionowy Układ Ekranu</string>
|
||||
<string name="emulation_switch_secondary_layout">Układ ekranu wyświetlacza dodatkowego</string>
|
||||
<string name="emulation_switch_secondary_layout_description">Układ używany przez podłączony dodatkowy ekran, przewodowy lub bezprzewodowy (Chromecast, Miracast)</string>
|
||||
<string name="emulation_screen_layout_largescreen">Duży Ekran</string>
|
||||
<string name="emulation_screen_layout_portrait">Ekran</string>
|
||||
<string name="emulation_screen_layout_single">Pojedynczy ekran</string>
|
||||
@ -424,6 +425,7 @@
|
||||
<string name="emulation_screen_layout_hybrid">Hybrydowy Ekran</string>
|
||||
<string name="emulation_screen_layout_original">Oryginalny</string>
|
||||
<string name="emulation_portrait_layout_top_full">Domyślny</string>
|
||||
<string name="emulation_secondary_display_default">Ustawienia domyślne systemu (mirror)</string>
|
||||
<string name="emulation_screen_layout_custom">Niestandardowy Układ</string>
|
||||
<string name="emulation_small_screen_position">Pozycja małego ekranu</string>
|
||||
<string name="small_screen_position_description">Gdzie powinien być wyświetlany mały ekran względem dużego w układzie dużego ekranu?</string>
|
||||
|
||||
@ -148,12 +148,12 @@
|
||||
</integer-array>
|
||||
|
||||
<string-array name="statsPosition">
|
||||
<item>@string/overlay_position_top_left</item>
|
||||
<item>@string/overlay_position_center_top</item>
|
||||
<item>@string/overlay_position_top_right</item>
|
||||
<item>@string/overlay_position_bottom_left</item>
|
||||
<item>@string/overlay_position_center_bottom</item>
|
||||
<item>@string/overlay_position_bottom_right</item>
|
||||
<item>@string/performance_overlay_position_top_left</item>
|
||||
<item>@string/performance_overlay_position_center_top</item>
|
||||
<item>@string/performance_overlay_position_top_right</item>
|
||||
<item>@string/performance_overlay_position_bottom_left</item>
|
||||
<item>@string/performance_overlay_position_center_bottom</item>
|
||||
<item>@string/performance_overlay_position_bottom_right</item>
|
||||
</string-array>
|
||||
<integer-array name="statsPositionValues">
|
||||
<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="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="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="linear_filtering">Linear Filtering</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_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="advanced">Advanced</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="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="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="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_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="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_description">Emulation speed limit used while the turbo hotkey is active.</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_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="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="audio_input_type">Audio Input Device</string>
|
||||
<string name="sound_output_mode">Sound Output Mode</string>
|
||||
@ -318,12 +320,12 @@
|
||||
<string name="hw_shaders_description">Uses hardware to emulate 3DS shaders. When enabled, game performance will be significantly improved.</string>
|
||||
<string name="cpu_clock_speed">CPU Clock Speed</string>
|
||||
<string name="vsync">Enable V-Sync</string>
|
||||
<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. Can cause additional input latency but may reduce tearing in some cases.</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="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="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="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>
|
||||
@ -448,6 +450,13 @@
|
||||
<string name="emulation_portrait_layout_top_full">Default</string>
|
||||
<string name="emulation_secondary_display_default">System Default (mirror)</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="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>
|
||||
@ -576,28 +585,28 @@
|
||||
<string name="performance_overlay_options">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="overlay_show_fps">Show FPS</string>
|
||||
<string name="overlay_show_fps_description">Display current frames per second.</string>
|
||||
<string name="overlay_show_frametime">Show Frametime</string>
|
||||
<string name="overlay_show_frametime_description">Display current frametime.</string>
|
||||
<string name="overlay_show_speed">Show Speed</string>
|
||||
<string name="overlay_show_speed_description">Display current emulation speed percentage.</string>
|
||||
<string name="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="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="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="overlay_position">Overlay Position</string>
|
||||
<string name="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="overlay_position_top_right">Top Right</string>
|
||||
<string name="overlay_position_bottom_left">Bottom Left</string>
|
||||
<string name="overlay_position_bottom_right">Bottom Right</string>
|
||||
<string name="overlay_position_center_top">Center Top</string>
|
||||
<string name="overlay_position_center_bottom">Center Bottom</string>
|
||||
<string name="overlay_background">Overlay Background</string>
|
||||
<string name="overlay_background_description">Adds a background behind the overlay for easier reading.</string>
|
||||
<string name="performance_overlay_show_fps">Show FPS</string>
|
||||
<string name="performance_overlay_show_fps_description">Display current frames per second.</string>
|
||||
<string name="performance_overlay_show_frametime">Show Frametime</string>
|
||||
<string name="performance_overlay_show_frametime_description">Display current frametime.</string>
|
||||
<string name="performance_overlay_show_speed">Show Speed</string>
|
||||
<string name="performance_overlay_show_speed_description">Display current emulation speed percentage.</string>
|
||||
<string name="performance_overlay_show_app_ram_usage">Show App Memory Usage</string>
|
||||
<string name="performance_overlay_show_app_ram_usage_description">Display the amount of RAM getting used by the emulator.</string>
|
||||
<string name="performance_overlay_show_available_ram">Show Available Memory</string>
|
||||
<string name="performance_overlay_show_available_ram_description">Display the amount of RAM which is available.</string>
|
||||
<string name="performance_overlay_show_battery_temp">Show Battery Temperature</string>
|
||||
<string name="performance_overlay_show_battery_temp_description">Display current Battery temperature in Celsius and Fahrenheit.</string>
|
||||
<string name="performance_overlay_position">Overlay Position</string>
|
||||
<string name="performance_overlay_position_description">Choose where the performance overlay is displayed on the screen.</string>
|
||||
<string name="performance_overlay_position_top_left">Top Left</string>
|
||||
<string name="performance_overlay_position_top_right">Top Right</string>
|
||||
<string name="performance_overlay_position_bottom_left">Bottom Left</string>
|
||||
<string name="performance_overlay_position_bottom_right">Bottom Right</string>
|
||||
<string name="performance_overlay_position_center_top">Center Top</string>
|
||||
<string name="performance_overlay_position_center_bottom">Center Bottom</string>
|
||||
<string name="performance_overlay_background">Overlay Background</string>
|
||||
<string name="performance_overlay_background_description">Adds a background behind the overlay for easier reading.</string>
|
||||
|
||||
<!-- Cheats -->
|
||||
<string name="cheats">Cheats</string>
|
||||
|
||||
@ -4,8 +4,8 @@
|
||||
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
plugins {
|
||||
id("com.android.application") version "8.11.1" apply false
|
||||
id("com.android.library") version "8.11.1" apply false
|
||||
id("com.android.application") version "8.13.1" apply false
|
||||
id("com.android.library") version "8.13.1" apply false
|
||||
id("org.jetbrains.kotlin.android") version "2.0.20" apply false
|
||||
id("org.jetbrains.kotlin.plugin.serialization") version "2.0.20"
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2017 Citra Emulator Project
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2017 Citra Emulator Project
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
@ -86,9 +86,6 @@ public:
|
||||
*/
|
||||
virtual void PipeWrite(DspPipe pipe_number, std::span<const u8> buffer) = 0;
|
||||
|
||||
/// Returns a reference to the array backing DSP memory
|
||||
virtual std::array<u8, Memory::DSP_RAM_SIZE>& GetDspMemory() = 0;
|
||||
|
||||
/// Sets the handler for the interrupts we trigger
|
||||
virtual void SetInterruptHandler(
|
||||
std::function<void(Service::DSP::InterruptType type, DspPipe pipe)> handler) = 0;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2017 Citra Emulator Project
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
@ -60,8 +60,6 @@ public:
|
||||
std::size_t GetPipeReadableSize(DspPipe pipe_number) const;
|
||||
void PipeWrite(DspPipe pipe_number, std::span<const u8> buffer);
|
||||
|
||||
std::array<u8, Memory::DSP_RAM_SIZE>& GetDspMemory();
|
||||
|
||||
void SetInterruptHandler(
|
||||
std::function<void(Service::DSP::InterruptType type, DspPipe pipe)> handler);
|
||||
|
||||
@ -81,7 +79,10 @@ private:
|
||||
DspState dsp_state = DspState::Off;
|
||||
std::array<std::vector<u8>, num_dsp_pipe> pipe_data{};
|
||||
|
||||
HLE::DspMemory dsp_memory;
|
||||
public:
|
||||
HLE::DspMemory* dsp_memory;
|
||||
|
||||
private:
|
||||
std::array<HLE::Source, HLE::num_sources> sources{{
|
||||
HLE::Source(0), HLE::Source(1), HLE::Source(2), HLE::Source(3), HLE::Source(4),
|
||||
HLE::Source(5), HLE::Source(6), HLE::Source(7), HLE::Source(8), HLE::Source(9),
|
||||
@ -103,7 +104,6 @@ private:
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar & dsp_state;
|
||||
ar & pipe_data;
|
||||
ar & dsp_memory.raw_memory;
|
||||
ar & sources;
|
||||
ar & mixers;
|
||||
// interrupt_handler is reregistered when loading state from DSP_DSP
|
||||
@ -113,7 +113,8 @@ private:
|
||||
|
||||
DspHle::Impl::Impl(DspHle& parent_, Memory::MemorySystem& memory, Core::Timing& timing)
|
||||
: parent(parent_), core_timing(timing) {
|
||||
dsp_memory.raw_memory.fill(0);
|
||||
dsp_memory = reinterpret_cast<HLE::DspMemory*>(memory.GetDspMemory(0));
|
||||
dsp_memory->raw_memory.fill(0);
|
||||
|
||||
for (auto& source : sources) {
|
||||
source.SetMemory(memory);
|
||||
@ -284,10 +285,6 @@ void DspHle::Impl::PipeWrite(DspPipe pipe_number, std::span<const u8> buffer) {
|
||||
}
|
||||
}
|
||||
|
||||
std::array<u8, Memory::DSP_RAM_SIZE>& DspHle::Impl::GetDspMemory() {
|
||||
return dsp_memory.raw_memory;
|
||||
}
|
||||
|
||||
void DspHle::Impl::SetInterruptHandler(
|
||||
std::function<void(Service::DSP::InterruptType type, DspPipe pipe)> handler) {
|
||||
interrupt_handler = handler;
|
||||
@ -343,8 +340,8 @@ void DspHle::Impl::AudioPipeWriteStructAddresses() {
|
||||
size_t DspHle::Impl::CurrentRegionIndex() const {
|
||||
// The region with the higher frame counter is chosen unless there is wraparound.
|
||||
// This function only returns a 0 or 1.
|
||||
const u16 frame_counter_0 = dsp_memory.region_0.frame_counter;
|
||||
const u16 frame_counter_1 = dsp_memory.region_1.frame_counter;
|
||||
const u16 frame_counter_0 = dsp_memory->region_0.frame_counter;
|
||||
const u16 frame_counter_1 = dsp_memory->region_1.frame_counter;
|
||||
|
||||
if (frame_counter_0 == 0xFFFFu && frame_counter_1 != 0xFFFEu) {
|
||||
// Wraparound has occurred.
|
||||
@ -360,11 +357,11 @@ size_t DspHle::Impl::CurrentRegionIndex() const {
|
||||
}
|
||||
|
||||
HLE::SharedMemory& DspHle::Impl::ReadRegion() {
|
||||
return CurrentRegionIndex() == 0 ? dsp_memory.region_0 : dsp_memory.region_1;
|
||||
return CurrentRegionIndex() == 0 ? dsp_memory->region_0 : dsp_memory->region_1;
|
||||
}
|
||||
|
||||
HLE::SharedMemory& DspHle::Impl::WriteRegion() {
|
||||
return CurrentRegionIndex() != 0 ? dsp_memory.region_0 : dsp_memory.region_1;
|
||||
return CurrentRegionIndex() != 0 ? dsp_memory->region_0 : dsp_memory->region_1;
|
||||
}
|
||||
|
||||
StereoFrame16 DspHle::Impl::GenerateCurrentFrame() {
|
||||
@ -454,10 +451,6 @@ void DspHle::PipeWrite(DspPipe pipe_number, std::span<const u8> buffer) {
|
||||
impl->PipeWrite(pipe_number, buffer);
|
||||
}
|
||||
|
||||
std::array<u8, Memory::DSP_RAM_SIZE>& DspHle::GetDspMemory() {
|
||||
return impl->GetDspMemory();
|
||||
}
|
||||
|
||||
void DspHle::SetInterruptHandler(
|
||||
std::function<void(Service::DSP::InterruptType type, DspPipe pipe)> handler) {
|
||||
impl->SetInterruptHandler(handler);
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2017 Citra Emulator Project
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
@ -37,8 +37,6 @@ public:
|
||||
std::size_t GetPipeReadableSize(DspPipe pipe_number) const override;
|
||||
void PipeWrite(DspPipe pipe_number, std::span<const u8> buffer) override;
|
||||
|
||||
std::array<u8, Memory::DSP_RAM_SIZE>& GetDspMemory() override;
|
||||
|
||||
void SetInterruptHandler(
|
||||
std::function<void(Service::DSP::InterruptType type, DspPipe pipe)> handler) override;
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2018 Citra Emulator Project
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
@ -121,7 +121,9 @@ static u8 PipeIndexToSlotIndex(u8 pipe_index, PipeDirection direction) {
|
||||
}
|
||||
|
||||
struct DspLle::Impl final {
|
||||
Impl(Core::Timing& timing, bool multithread) : core_timing(timing), multithread(multithread) {
|
||||
Impl(Core::Timing& timing, Memory::MemorySystem& memory, bool multithread)
|
||||
: teakra(Teakra::UserConfig{.dsp_memory = memory.GetDspMemory(0)}), core_timing(timing),
|
||||
multithread(multithread) {
|
||||
teakra_slice_event = core_timing.RegisterEvent(
|
||||
"DSP slice", [this](u64, int late) { TeakraSliceEvent(static_cast<u64>(late)); });
|
||||
}
|
||||
@ -189,12 +191,12 @@ struct DspLle::Impl final {
|
||||
}
|
||||
|
||||
u8* GetDspDataPointer(u32 baddr) {
|
||||
auto& memory = teakra.GetDspMemory();
|
||||
uint8_t* memory = teakra.GetDspMemory();
|
||||
return &memory[DspDataOffset + baddr];
|
||||
}
|
||||
|
||||
const u8* GetDspDataPointer(u32 baddr) const {
|
||||
auto& memory = teakra.GetDspMemory();
|
||||
const uint8_t* memory = teakra.GetDspMemory();
|
||||
return &memory[DspDataOffset + baddr];
|
||||
}
|
||||
|
||||
@ -312,9 +314,9 @@ struct DspLle::Impl final {
|
||||
teakra.Reset();
|
||||
|
||||
Dsp1 dsp(buffer);
|
||||
auto& dsp_memory = teakra.GetDspMemory();
|
||||
u8* program = dsp_memory.data();
|
||||
u8* data = dsp_memory.data() + DspDataOffset;
|
||||
auto dsp_memory = teakra.GetDspMemory();
|
||||
u8* program = dsp_memory;
|
||||
u8* data = dsp_memory + DspDataOffset;
|
||||
for (const auto& segment : dsp.segments) {
|
||||
if (segment.memory_type == SegmentType::ProgramA ||
|
||||
segment.memory_type == SegmentType::ProgramB) {
|
||||
@ -403,10 +405,6 @@ void DspLle::PipeWrite(DspPipe pipe_number, std::span<const u8> buffer) {
|
||||
impl->WritePipe(static_cast<u8>(pipe_number), buffer);
|
||||
}
|
||||
|
||||
std::array<u8, Memory::DSP_RAM_SIZE>& DspLle::GetDspMemory() {
|
||||
return impl->teakra.GetDspMemory();
|
||||
}
|
||||
|
||||
void DspLle::SetInterruptHandler(
|
||||
std::function<void(Service::DSP::InterruptType type, DspPipe pipe)> handler) {
|
||||
impl->teakra.SetRecvDataHandler(0, [this, handler]() {
|
||||
@ -469,7 +467,7 @@ DspLle::DspLle(Core::System& system, bool multithread)
|
||||
|
||||
DspLle::DspLle(Core::System& system, Memory::MemorySystem& memory, Core::Timing& timing,
|
||||
bool multithread)
|
||||
: DspInterface(system), impl(std::make_unique<Impl>(timing, multithread)) {
|
||||
: DspInterface(system), impl(std::make_unique<Impl>(timing, memory, multithread)) {
|
||||
Teakra::AHBMCallback ahbm;
|
||||
ahbm.read8 = [&memory](u32 address) -> u8 {
|
||||
return *memory.GetFCRAMPointer(address - Memory::FCRAM_PADDR);
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2018 Citra Emulator Project
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
@ -31,8 +31,6 @@ public:
|
||||
std::size_t GetPipeReadableSize(DspPipe pipe_number) const override;
|
||||
void PipeWrite(DspPipe pipe_number, std::span<const u8> buffer) override;
|
||||
|
||||
std::array<u8, Memory::DSP_RAM_SIZE>& GetDspMemory() override;
|
||||
|
||||
void SetInterruptHandler(
|
||||
std::function<void(Service::DSP::InterruptType type, DspPipe pipe)> handler) override;
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
@ -9,7 +9,7 @@
|
||||
|
||||
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
|
||||
|
||||
@ -60,6 +60,10 @@ if (ENABLE_QT AND UNIX AND NOT APPLE)
|
||||
target_link_libraries(citra_meta PRIVATE Qt6::DBus gamemode)
|
||||
endif()
|
||||
|
||||
if (ENABLE_QT AND APPLE)
|
||||
target_link_libraries(citra_meta PRIVATE Qt6::GuiPrivate)
|
||||
endif()
|
||||
|
||||
if (ENABLE_QT AND USE_DISCORD_PRESENCE)
|
||||
target_link_libraries(citra_meta PRIVATE discord-rpc)
|
||||
endif()
|
||||
|
||||
@ -172,12 +172,12 @@ add_library(citra_qt STATIC EXCLUDE_FROM_ALL
|
||||
multiplayer/state.h
|
||||
multiplayer/validation.h
|
||||
precompiled_headers.h
|
||||
qt_image_interface.cpp
|
||||
qt_image_interface.h
|
||||
uisettings.cpp
|
||||
uisettings.h
|
||||
user_data_migration.cpp
|
||||
user_data_migration.h
|
||||
qt_image_interface.cpp
|
||||
qt_image_interface.h
|
||||
util/clickable_label.cpp
|
||||
util/clickable_label.h
|
||||
util/graphics_device_info.cpp
|
||||
@ -190,6 +190,13 @@ add_library(citra_qt STATIC EXCLUDE_FROM_ALL
|
||||
util/util.h
|
||||
)
|
||||
|
||||
if (APPLE)
|
||||
target_sources(citra_qt PUBLIC
|
||||
qt_swizzle.h
|
||||
qt_swizzle.mm
|
||||
)
|
||||
endif()
|
||||
|
||||
file(GLOB COMPAT_LIST
|
||||
${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc
|
||||
${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
|
||||
@ -272,7 +279,12 @@ endif()
|
||||
|
||||
if (NOT WIN32)
|
||||
find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
|
||||
target_include_directories(citra_qt PRIVATE ${Qt6Gui_PRIVATE_INCLUDE_DIRS})
|
||||
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})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (UNIX AND NOT APPLE)
|
||||
|
||||
@ -754,7 +754,7 @@ bool GRenderWindow::InitializeOpenGL() {
|
||||
child->SetContext(std::move(child_context));
|
||||
|
||||
auto format = child_widget->windowHandle()->format();
|
||||
format.setSwapInterval(Settings::values.use_vsync_new.GetValue());
|
||||
format.setSwapInterval(Settings::values.use_vsync.GetValue());
|
||||
child_widget->windowHandle()->setFormat(format);
|
||||
|
||||
return true;
|
||||
|
||||
@ -69,6 +69,7 @@
|
||||
#include "citra_qt/movie/movie_record_dialog.h"
|
||||
#include "citra_qt/multiplayer/state.h"
|
||||
#include "citra_qt/qt_image_interface.h"
|
||||
#include "citra_qt/qt_swizzle.h"
|
||||
#include "citra_qt/uisettings.h"
|
||||
#include "common/play_time_manager.h"
|
||||
#ifdef ENABLE_QT_UPDATE_CHECKER
|
||||
@ -979,6 +980,10 @@ void GMainWindow::ConnectWidgetEvents() {
|
||||
connect(game_list, &GameList::ShowList, this, &GMainWindow::OnGameListShowList);
|
||||
connect(game_list, &GameList::PopulatingCompleted, this,
|
||||
[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,
|
||||
&GMainWindow::OnGameListOpenPerGameProperties);
|
||||
@ -1575,6 +1580,16 @@ void GMainWindow::ShutdownGame() {
|
||||
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) {
|
||||
UISettings::values.recent_files.prepend(filename);
|
||||
UISettings::values.recent_files.removeDuplicates();
|
||||
@ -2275,8 +2290,8 @@ void GMainWindow::OnMenuBootHomeMenu(u32 region) {
|
||||
void GMainWindow::InstallCIA(QStringList filepaths) {
|
||||
ui->action_Install_CIA->setEnabled(false);
|
||||
game_list->SetDirectoryWatcherEnabled(false);
|
||||
progress_bar->show();
|
||||
progress_bar->setMaximum(INT_MAX);
|
||||
|
||||
emit UpdateProgress(0, 0);
|
||||
|
||||
(void)QtConcurrent::run([&, filepaths] {
|
||||
Service::AM::InstallStatus status;
|
||||
@ -2292,6 +2307,11 @@ void GMainWindow::InstallCIA(QStringList filepaths) {
|
||||
}
|
||||
|
||||
void GMainWindow::OnUpdateProgress(std::size_t written, std::size_t total) {
|
||||
if (written == 0 and total == 0) {
|
||||
progress_bar->show();
|
||||
progress_bar->setValue(0);
|
||||
progress_bar->setMaximum(INT_MAX);
|
||||
}
|
||||
progress_bar->setValue(
|
||||
static_cast<int>(INT_MAX * (static_cast<double>(written) / static_cast<double>(total))));
|
||||
}
|
||||
@ -2335,11 +2355,19 @@ void GMainWindow::OnCompressFinished(bool is_compress, bool success) {
|
||||
|
||||
if (!success) {
|
||||
if (is_compress) {
|
||||
QMessageBox::critical(this, tr("Error compressing file"),
|
||||
tr("File compress operation failed, check log for details."));
|
||||
QMessageBox::critical(this, tr("Z3DS Compression"),
|
||||
tr("Failed to compress some files, check log for details."));
|
||||
} else {
|
||||
QMessageBox::critical(this, tr("Error decompressing file"),
|
||||
tr("File decompress operation failed, check log for details."));
|
||||
QMessageBox::critical(this, tr("Z3DS Compression"),
|
||||
tr("Failed to decompress some files, check log for details."));
|
||||
}
|
||||
} else {
|
||||
if (is_compress) {
|
||||
QMessageBox::information(this, tr("Z3DS Compression"),
|
||||
tr("All files have been compressed successfully."));
|
||||
} else {
|
||||
QMessageBox::information(this, tr("Z3DS Compression"),
|
||||
tr("All files have been decompressed successfully."));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3051,43 +3079,27 @@ void GMainWindow::OnDumpVideo() {
|
||||
}
|
||||
}
|
||||
|
||||
void GMainWindow::OnCompressFile() {
|
||||
// NOTE: Encrypted files SHOULD NEVER be compressed, otherwise the resulting
|
||||
// compressed file will have very poor compression ratios, due to the high
|
||||
// entropy caused by encryption. This may cause confusion to the user as they
|
||||
// will see the files do not compress well and blame the emulator.
|
||||
//
|
||||
// This is enforced using the loaders as they already return an error on encryption.
|
||||
|
||||
QString filepath = QFileDialog::getOpenFileName(
|
||||
this, tr("Load 3DS ROM File"), UISettings::values.roms_path,
|
||||
tr("3DS ROM Files (*.cia *cci *3dsx *cxi)") + QStringLiteral(";;") + tr("All Files (*.*)"));
|
||||
|
||||
if (filepath.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
std::string in_path = filepath.toStdString();
|
||||
|
||||
// Identify file type
|
||||
static std::optional<std::pair<Loader::AppLoader::CompressFileInfo, size_t>> GetCompressFileInfo(
|
||||
const std::string& filepath, bool compress) {
|
||||
Loader::AppLoader::CompressFileInfo compress_info{};
|
||||
compress_info.is_supported = false;
|
||||
size_t frame_size{};
|
||||
{
|
||||
auto loader = Loader::GetLoader(in_path);
|
||||
if (loader) {
|
||||
compress_info = loader->GetCompressFileInfo();
|
||||
frame_size = FileUtil::Z3DSWriteIOFile::DEFAULT_FRAME_SIZE;
|
||||
} else {
|
||||
bool is_compressed = false;
|
||||
if (Service::AM::CheckCIAToInstall(in_path, is_compressed, true) ==
|
||||
Service::AM::InstallStatus::Success) {
|
||||
auto meta_info = Service::AM::GetCIAInfos(in_path);
|
||||
compress_info.is_supported = true;
|
||||
compress_info.is_compressed = is_compressed;
|
||||
compress_info.recommended_compressed_extension = "zcia";
|
||||
compress_info.recommended_uncompressed_extension = "cia";
|
||||
compress_info.underlying_magic = std::array<u8, 4>({'C', 'I', 'A', '\0'});
|
||||
frame_size = FileUtil::Z3DSWriteIOFile::DEFAULT_CIA_FRAME_SIZE;
|
||||
auto loader = Loader::GetLoader(filepath);
|
||||
if (loader) {
|
||||
compress_info = loader->GetCompressFileInfo();
|
||||
frame_size = FileUtil::Z3DSWriteIOFile::DEFAULT_FRAME_SIZE;
|
||||
} else {
|
||||
bool is_compressed = false;
|
||||
if (Service::AM::CheckCIAToInstall(filepath, is_compressed, compress ? true : false) ==
|
||||
Service::AM::InstallStatus::Success) {
|
||||
compress_info.is_supported = true;
|
||||
compress_info.is_compressed = is_compressed;
|
||||
compress_info.recommended_compressed_extension = "zcia";
|
||||
compress_info.recommended_uncompressed_extension = "cia";
|
||||
compress_info.underlying_magic = std::array<u8, 4>({'C', 'I', 'A', '\0'});
|
||||
frame_size = FileUtil::Z3DSWriteIOFile::DEFAULT_CIA_FRAME_SIZE;
|
||||
if (compress) {
|
||||
auto meta_info = Service::AM::GetCIAInfos(filepath);
|
||||
if (meta_info.Succeeded()) {
|
||||
const auto& meta_info_val = meta_info.Unwrap();
|
||||
std::vector<u8> value(sizeof(Service::AM::TitleInfo));
|
||||
@ -3102,122 +3114,218 @@ void GMainWindow::OnCompressFile() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!compress_info.is_supported) {
|
||||
QMessageBox::critical(
|
||||
this, tr("Error compressing file"),
|
||||
tr("The selected file is not a compatible 3DS ROM format. Make sure you have "
|
||||
"chosen the right file, and that it is not encrypted."));
|
||||
return;
|
||||
LOG_ERROR(Frontend,
|
||||
"Error {} file {}, the selected file is not a compatible 3DS ROM format or is "
|
||||
"encrypted.",
|
||||
compress ? "compressing" : "decompressing", filepath);
|
||||
return {};
|
||||
}
|
||||
if (compress_info.is_compressed) {
|
||||
QMessageBox::warning(this, tr("Error compressing file"),
|
||||
tr("The selected file is already compressed."));
|
||||
if (compress_info.is_compressed && compress) {
|
||||
LOG_ERROR(Frontend, "Error compressing file {}, the selected file is already compressed",
|
||||
filepath);
|
||||
return {};
|
||||
}
|
||||
if (!compress_info.is_compressed && !compress) {
|
||||
LOG_ERROR(Frontend,
|
||||
"Error decompressing file {}, the selected file is already decompressed",
|
||||
filepath);
|
||||
return {};
|
||||
}
|
||||
|
||||
return std::pair(compress_info, frame_size);
|
||||
}
|
||||
|
||||
void GMainWindow::OnCompressFile() {
|
||||
// NOTE: Encrypted files SHOULD NEVER be compressed, otherwise the resulting
|
||||
// compressed file will have very poor compression ratios, due to the high
|
||||
// entropy caused by encryption. This may cause confusion to the user as they
|
||||
// will see the files do not compress well and blame the emulator.
|
||||
//
|
||||
// This is enforced using the loaders as they already return an error on encryption.
|
||||
|
||||
QStringList filepaths =
|
||||
QFileDialog::getOpenFileNames(this, tr("Load 3DS ROM Files"), UISettings::values.roms_path,
|
||||
tr("3DS ROM Files (*.cia *.cci *.3dsx *.cxi)") +
|
||||
QStringLiteral(";;") + tr("All Files (*.*)"));
|
||||
|
||||
QString out_path;
|
||||
|
||||
if (filepaths.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString out_filter =
|
||||
tr("3DS Compressed ROM File (*.%1)")
|
||||
.arg(QString::fromStdString(compress_info.recommended_compressed_extension));
|
||||
|
||||
QFileInfo fileinfo(filepath);
|
||||
QString final_path = fileinfo.path() + QStringLiteral(DIR_SEP) + fileinfo.completeBaseName() +
|
||||
QStringLiteral(".") +
|
||||
QString::fromStdString(compress_info.recommended_compressed_extension);
|
||||
|
||||
filepath = QFileDialog::getSaveFileName(this, tr("Save 3DS Compressed ROM File"), final_path,
|
||||
out_filter);
|
||||
if (filepath.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
std::string out_path = filepath.toStdString();
|
||||
|
||||
progress_bar->show();
|
||||
progress_bar->setMaximum(INT_MAX);
|
||||
|
||||
(void)QtConcurrent::run([&, in_path, out_path, compress_info, frame_size] {
|
||||
const auto progress = [&](std::size_t written, std::size_t total) {
|
||||
emit UpdateProgress(written, total);
|
||||
};
|
||||
bool success =
|
||||
FileUtil::CompressZ3DSFile(in_path, out_path, compress_info.underlying_magic,
|
||||
frame_size, progress, compress_info.default_metadata);
|
||||
if (!success) {
|
||||
FileUtil::Delete(out_path);
|
||||
bool single_file = filepaths.size() == 1;
|
||||
if (single_file) {
|
||||
// If it's a single file, ask the user for the output file.
|
||||
auto compress_info = GetCompressFileInfo(filepaths[0].toStdString(), true);
|
||||
if (!compress_info.has_value()) {
|
||||
emit CompressFinished(true, false);
|
||||
return;
|
||||
}
|
||||
emit OnCompressFinished(true, success);
|
||||
|
||||
QFileInfo fileinfo(filepaths[0]);
|
||||
QString final_path =
|
||||
fileinfo.path() + QStringLiteral(DIR_SEP) + fileinfo.completeBaseName() +
|
||||
QStringLiteral(".") +
|
||||
QString::fromStdString(compress_info.value().first.recommended_compressed_extension);
|
||||
|
||||
QString out_filter = tr("3DS Compressed ROM File (*.%1)")
|
||||
.arg(QString::fromStdString(
|
||||
compress_info.value().first.recommended_compressed_extension));
|
||||
out_path = QFileDialog::getSaveFileName(this, tr("Save 3DS Compressed ROM File"),
|
||||
final_path, out_filter);
|
||||
if (out_path.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// Otherwise, ask the user the directory to output the files.
|
||||
out_path = QFileDialog::getExistingDirectory(
|
||||
this, tr("Select Output 3DS Compressed ROM Folder"), UISettings::values.roms_path,
|
||||
QFileDialog::ShowDirsOnly);
|
||||
if (out_path.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
(void)QtConcurrent::run([&, filepaths, out_path] {
|
||||
bool single_file = filepaths.size() == 1;
|
||||
QString out_filepath;
|
||||
bool total_success = true;
|
||||
|
||||
for (const QString& filepath : filepaths) {
|
||||
|
||||
std::string in_path = filepath.toStdString();
|
||||
|
||||
// Identify file type
|
||||
auto compress_info = GetCompressFileInfo(filepath.toStdString(), true);
|
||||
if (!compress_info.has_value()) {
|
||||
total_success = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (single_file) {
|
||||
out_filepath = out_path;
|
||||
} else {
|
||||
QFileInfo fileinfo(filepath);
|
||||
out_filepath = out_path + QStringLiteral(DIR_SEP) + fileinfo.completeBaseName() +
|
||||
QStringLiteral(".") +
|
||||
QString::fromStdString(
|
||||
compress_info.value().first.recommended_compressed_extension);
|
||||
}
|
||||
|
||||
std::string out_path = out_filepath.toStdString();
|
||||
|
||||
emit UpdateProgress(0, 0);
|
||||
|
||||
const auto progress = [&](std::size_t written, std::size_t total) {
|
||||
emit UpdateProgress(written, total);
|
||||
};
|
||||
bool success = FileUtil::CompressZ3DSFile(in_path, out_path,
|
||||
compress_info.value().first.underlying_magic,
|
||||
compress_info.value().second, progress,
|
||||
compress_info.value().first.default_metadata);
|
||||
if (!success) {
|
||||
total_success = false;
|
||||
FileUtil::Delete(out_path);
|
||||
}
|
||||
}
|
||||
|
||||
emit CompressFinished(true, total_success);
|
||||
});
|
||||
}
|
||||
|
||||
void GMainWindow::OnDecompressFile() {
|
||||
QString filepath = QFileDialog::getOpenFileName(
|
||||
this, tr("Load 3DS Compressed ROM File"), UISettings::values.roms_path,
|
||||
|
||||
QStringList filepaths = QFileDialog::getOpenFileNames(
|
||||
this, tr("Load 3DS Compressed ROM Files"), UISettings::values.roms_path,
|
||||
tr("3DS Compressed ROM Files (*.zcia *zcci *z3dsx *zcxi)") + QStringLiteral(";;") +
|
||||
tr("All Files (*.*)"));
|
||||
|
||||
if (filepath.isEmpty()) {
|
||||
QString out_path;
|
||||
|
||||
if (filepaths.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
std::string in_path = filepath.toStdString();
|
||||
|
||||
// Identify file type
|
||||
Loader::AppLoader::CompressFileInfo compress_info{};
|
||||
compress_info.is_supported = false;
|
||||
{
|
||||
auto loader = Loader::GetLoader(in_path);
|
||||
if (loader) {
|
||||
compress_info = loader->GetCompressFileInfo();
|
||||
} else {
|
||||
bool is_compressed = false;
|
||||
if (Service::AM::CheckCIAToInstall(in_path, is_compressed, false) ==
|
||||
Service::AM::InstallStatus::Success) {
|
||||
compress_info.is_supported = true;
|
||||
compress_info.is_compressed = is_compressed;
|
||||
compress_info.recommended_compressed_extension = "zcia";
|
||||
compress_info.recommended_uncompressed_extension = "cia";
|
||||
compress_info.underlying_magic = std::array<u8, 4>({'C', 'I', 'A', '\0'});
|
||||
bool single_file = filepaths.size() == 1;
|
||||
if (single_file) {
|
||||
// If it's a single file, ask the user for the output file.
|
||||
auto compress_info = GetCompressFileInfo(filepaths[0].toStdString(), false);
|
||||
if (!compress_info.has_value()) {
|
||||
emit CompressFinished(false, false);
|
||||
return;
|
||||
}
|
||||
|
||||
QFileInfo fileinfo(filepaths[0]);
|
||||
QString final_path =
|
||||
fileinfo.path() + QStringLiteral(DIR_SEP) + fileinfo.completeBaseName() +
|
||||
QStringLiteral(".") +
|
||||
QString::fromStdString(compress_info.value().first.recommended_uncompressed_extension);
|
||||
|
||||
QString out_filter =
|
||||
tr("3DS ROM File (*.%1)")
|
||||
.arg(QString::fromStdString(
|
||||
compress_info.value().first.recommended_uncompressed_extension));
|
||||
out_path =
|
||||
QFileDialog::getSaveFileName(this, tr("Save 3DS ROM File"), final_path, out_filter);
|
||||
if (out_path.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// Otherwise, ask the user the directory to output the files.
|
||||
out_path = QFileDialog::getExistingDirectory(this, tr("Select Output 3DS ROM Folder"),
|
||||
UISettings::values.roms_path,
|
||||
QFileDialog::ShowDirsOnly);
|
||||
if (out_path.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
(void)QtConcurrent::run([&, filepaths, out_path] {
|
||||
bool single_file = filepaths.size() == 1;
|
||||
QString out_filepath;
|
||||
bool total_success = true;
|
||||
|
||||
for (const QString& filepath : filepaths) {
|
||||
|
||||
std::string in_path = filepath.toStdString();
|
||||
|
||||
// Identify file type
|
||||
auto compress_info = GetCompressFileInfo(filepath.toStdString(), false);
|
||||
if (!compress_info.has_value()) {
|
||||
total_success = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (single_file) {
|
||||
out_filepath = out_path;
|
||||
} else {
|
||||
QFileInfo fileinfo(filepath);
|
||||
out_filepath = out_path + QStringLiteral(DIR_SEP) + fileinfo.completeBaseName() +
|
||||
QStringLiteral(".") +
|
||||
QString::fromStdString(
|
||||
compress_info.value().first.recommended_uncompressed_extension);
|
||||
}
|
||||
|
||||
std::string out_path = out_filepath.toStdString();
|
||||
|
||||
emit UpdateProgress(0, 0);
|
||||
|
||||
const auto progress = [&](std::size_t written, std::size_t total) {
|
||||
emit UpdateProgress(written, total);
|
||||
};
|
||||
|
||||
// TODO(PabloMK7): What should we do with the metadata?
|
||||
bool success = FileUtil::DeCompressZ3DSFile(in_path, out_path, progress);
|
||||
if (!success) {
|
||||
total_success = false;
|
||||
FileUtil::Delete(out_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!compress_info.is_supported) {
|
||||
QMessageBox::critical(this, tr("Error decompressing file"),
|
||||
tr("The selected file is not a compatible compressed 3DS ROM format. "
|
||||
"Make sure you have "
|
||||
"chosen the right file."));
|
||||
return;
|
||||
}
|
||||
if (!compress_info.is_compressed) {
|
||||
QMessageBox::warning(this, tr("Error decompressing file"),
|
||||
tr("The selected file is already decompressed."));
|
||||
return;
|
||||
}
|
||||
|
||||
QString out_filter =
|
||||
tr("3DS ROM File (*.%1)")
|
||||
.arg(QString::fromStdString(compress_info.recommended_uncompressed_extension));
|
||||
|
||||
QFileInfo fileinfo(filepath);
|
||||
QString final_path = fileinfo.path() + QStringLiteral(DIR_SEP) + fileinfo.completeBaseName() +
|
||||
QStringLiteral(".") +
|
||||
QString::fromStdString(compress_info.recommended_uncompressed_extension);
|
||||
|
||||
filepath = QFileDialog::getSaveFileName(this, tr("Save 3DS ROM File"), final_path, out_filter);
|
||||
if (filepath.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
std::string out_path = filepath.toStdString();
|
||||
|
||||
progress_bar->show();
|
||||
progress_bar->setMaximum(INT_MAX);
|
||||
|
||||
(void)QtConcurrent::run([&, in_path, out_path, compress_info] {
|
||||
const auto progress = [&](std::size_t written, std::size_t total) {
|
||||
emit UpdateProgress(written, total);
|
||||
};
|
||||
// TODO(PabloMK7): What should we do with the metadata?
|
||||
bool success = FileUtil::DeCompressZ3DSFile(in_path, out_path, progress);
|
||||
if (!success) {
|
||||
FileUtil::Delete(out_path);
|
||||
}
|
||||
emit OnCompressFinished(false, success);
|
||||
emit CompressFinished(false, total_success);
|
||||
});
|
||||
}
|
||||
|
||||
@ -4112,6 +4220,11 @@ static Qt::HighDpiScaleFactorRoundingPolicy GetHighDpiRoundingPolicy() {
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
#if MICROPROFILE_ENABLED
|
||||
|
||||
@ -306,6 +306,7 @@ private slots:
|
||||
#endif
|
||||
void OnSwitchDiskResources(VideoCore::LoadCallbackStage stage, std::size_t value,
|
||||
std::size_t total);
|
||||
void StartLaunchStressTest(const QString& game_path);
|
||||
|
||||
private:
|
||||
Q_INVOKABLE void OnMoviePlaybackCompleted();
|
||||
|
||||
@ -371,7 +371,7 @@ void QtConfig::ReadControlValues() {
|
||||
const auto append_profile = [this, num_touch_from_button_maps] {
|
||||
Settings::InputProfile profile;
|
||||
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) {
|
||||
std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
|
||||
profile.buttons[i] = ReadSetting(QString::fromUtf8(Settings::NativeButton::mapping[i]),
|
||||
@ -685,7 +685,7 @@ void QtConfig::ReadRendererValues() {
|
||||
ReadGlobalSetting(Settings::values.use_hw_shader);
|
||||
ReadGlobalSetting(Settings::values.shaders_accurate_mul);
|
||||
ReadGlobalSetting(Settings::values.use_disk_shader_cache);
|
||||
ReadGlobalSetting(Settings::values.use_vsync_new);
|
||||
ReadGlobalSetting(Settings::values.use_vsync);
|
||||
ReadGlobalSetting(Settings::values.resolution_factor);
|
||||
ReadGlobalSetting(Settings::values.frame_limit);
|
||||
ReadGlobalSetting(Settings::values.turbo_limit);
|
||||
@ -1216,7 +1216,7 @@ void QtConfig::SaveRendererValues() {
|
||||
WriteGlobalSetting(Settings::values.use_hw_shader);
|
||||
WriteGlobalSetting(Settings::values.shaders_accurate_mul);
|
||||
WriteGlobalSetting(Settings::values.use_disk_shader_cache);
|
||||
WriteGlobalSetting(Settings::values.use_vsync_new);
|
||||
WriteGlobalSetting(Settings::values.use_vsync);
|
||||
WriteGlobalSetting(Settings::values.resolution_factor);
|
||||
WriteGlobalSetting(Settings::values.frame_limit);
|
||||
WriteGlobalSetting(Settings::values.turbo_limit);
|
||||
|
||||
@ -35,7 +35,7 @@
|
||||
<item>
|
||||
<widget class="QLabel" name="label_emulation">
|
||||
<property name="text">
|
||||
<string>Emulation:</string>
|
||||
<string>Emulation</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@ -28,7 +28,7 @@
|
||||
<string>Select the camera to configure</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Camera to configure:</string>
|
||||
<string>Camera to Configure</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -59,7 +59,7 @@
|
||||
<string>Select the camera mode (single or double)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Camera mode:</string>
|
||||
<string>Camera mode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -90,7 +90,7 @@
|
||||
<string>Select the position of camera to configure</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Camera position:</string>
|
||||
<string>Camera position</string>
|
||||
</property>
|
||||
</widget>
|
||||
</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>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Camera Image Source:</string>
|
||||
<string>Camera Image Source</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -166,7 +166,7 @@
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>File:</string>
|
||||
<string>File</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -193,7 +193,7 @@
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Camera:</string>
|
||||
<string>Camera</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -234,7 +234,7 @@
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Flip:</string>
|
||||
<string>Flip</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@ -113,7 +113,7 @@
|
||||
<item>
|
||||
<widget class="QCheckBox" name="toggle_linear_filter">
|
||||
<property name="text">
|
||||
<string>Enable Linear Filtering</string>
|
||||
<string>Enable linear filtering</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -320,7 +320,7 @@
|
||||
<item>
|
||||
<widget class="QCheckBox" name="disable_right_eye_render">
|
||||
<property name="text">
|
||||
<string>Disable Right Eye Rendering</string>
|
||||
<string>Disable right eye rendering</string>
|
||||
</property>
|
||||
<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>
|
||||
@ -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>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Use Custom Textures</string>
|
||||
<string>Use custom textures</string>
|
||||
</property>
|
||||
</widget>
|
||||
</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>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Dump Textures</string>
|
||||
<string>Dump textures</string>
|
||||
</property>
|
||||
</widget>
|
||||
</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>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Preload Custom Textures</string>
|
||||
<string>Preload custom textures</string>
|
||||
</property>
|
||||
</widget>
|
||||
</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>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Async Custom Texture Loading</string>
|
||||
<string>Async custom texture loading</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@ -97,7 +97,7 @@
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Set emulation speed:</string>
|
||||
<string>Set emulation speed</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
@ -105,7 +105,7 @@
|
||||
<item>
|
||||
<widget class="QLabel" name="label_emulation_speed">
|
||||
<property name="text">
|
||||
<string>Emulation Speed:</string>
|
||||
<string>Emulation Speed</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@ -142,7 +142,7 @@ void ConfigureGraphics::SetConfiguration() {
|
||||
ui->toggle_hw_shader->setChecked(Settings::values.use_hw_shader.GetValue());
|
||||
ui->toggle_accurate_mul->setChecked(Settings::values.shaders_accurate_mul.GetValue());
|
||||
ui->toggle_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache.GetValue());
|
||||
ui->toggle_vsync_new->setChecked(Settings::values.use_vsync_new.GetValue());
|
||||
ui->toggle_vsync->setChecked(Settings::values.use_vsync.GetValue());
|
||||
ui->spirv_shader_gen->setChecked(Settings::values.spirv_shader_gen.GetValue());
|
||||
ui->disable_spirv_optimizer->setChecked(Settings::values.disable_spirv_optimizer.GetValue());
|
||||
ui->toggle_async_shaders->setChecked(Settings::values.async_shader_compilation.GetValue());
|
||||
@ -174,8 +174,8 @@ void ConfigureGraphics::ApplyConfiguration() {
|
||||
ui->texture_sampling_combobox);
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_disk_shader_cache,
|
||||
ui->toggle_disk_shader_cache, use_disk_shader_cache);
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_vsync_new, ui->toggle_vsync_new,
|
||||
use_vsync_new);
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_vsync, ui->toggle_vsync,
|
||||
use_vsync);
|
||||
ConfigurationShared::ApplyPerGameSetting(
|
||||
&Settings::values.delay_game_render_thread_us, ui->delay_render_combo,
|
||||
[this](s32) { return ui->delay_render_slider->value(); });
|
||||
@ -197,8 +197,8 @@ void ConfigureGraphics::SetupPerGameUI() {
|
||||
ui->toggle_accurate_mul->setEnabled(Settings::values.shaders_accurate_mul.UsingGlobal());
|
||||
ui->toggle_disk_shader_cache->setEnabled(
|
||||
Settings::values.use_disk_shader_cache.UsingGlobal());
|
||||
ui->toggle_vsync_new->setEnabled(ui->toggle_vsync_new->isEnabled() &&
|
||||
Settings::values.use_vsync_new.UsingGlobal());
|
||||
ui->toggle_vsync->setEnabled(ui->toggle_vsync->isEnabled() &&
|
||||
Settings::values.use_vsync.UsingGlobal());
|
||||
ui->toggle_async_shaders->setEnabled(
|
||||
Settings::values.async_shader_compilation.UsingGlobal());
|
||||
ui->widget_texture_sampling->setEnabled(Settings::values.texture_sampling.UsingGlobal());
|
||||
@ -236,8 +236,8 @@ void ConfigureGraphics::SetupPerGameUI() {
|
||||
ConfigurationShared::SetColoredTristate(ui->toggle_disk_shader_cache,
|
||||
Settings::values.use_disk_shader_cache,
|
||||
use_disk_shader_cache);
|
||||
ConfigurationShared::SetColoredTristate(ui->toggle_vsync_new, Settings::values.use_vsync_new,
|
||||
use_vsync_new);
|
||||
ConfigurationShared::SetColoredTristate(ui->toggle_vsync, Settings::values.use_vsync,
|
||||
use_vsync);
|
||||
ConfigurationShared::SetColoredTristate(ui->toggle_async_shaders,
|
||||
Settings::values.async_shader_compilation,
|
||||
async_shader_compilation);
|
||||
|
||||
@ -38,7 +38,7 @@ private:
|
||||
ConfigurationShared::CheckState use_hw_shader;
|
||||
ConfigurationShared::CheckState shaders_accurate_mul;
|
||||
ConfigurationShared::CheckState use_disk_shader_cache;
|
||||
ConfigurationShared::CheckState use_vsync_new;
|
||||
ConfigurationShared::CheckState use_vsync;
|
||||
ConfigurationShared::CheckState async_shader_compilation;
|
||||
ConfigurationShared::CheckState async_presentation;
|
||||
ConfigurationShared::CheckState spirv_shader_gen;
|
||||
|
||||
@ -132,14 +132,14 @@
|
||||
<item>
|
||||
<widget class="QCheckBox" name="spirv_shader_gen">
|
||||
<property name="text">
|
||||
<string>SPIR-V Shader Generation</string>
|
||||
<string>SPIR-V shader generation</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="disable_spirv_optimizer">
|
||||
<property name="text">
|
||||
<string>Disable GLSL -> SPIR-V Optimizer</string>
|
||||
<string>Disable GLSL -> SPIR-V optimizer</string>
|
||||
</property>
|
||||
<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>
|
||||
@ -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>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Enable Hardware Shader</string>
|
||||
<string>Enable hardware shader</string>
|
||||
</property>
|
||||
</widget>
|
||||
</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>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Accurate Multiplication</string>
|
||||
<string>Accurate multiplication</string>
|
||||
</property>
|
||||
</widget>
|
||||
</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>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Enable Shader JIT</string>
|
||||
<string>Enable shader JIT</string>
|
||||
</property>
|
||||
</widget>
|
||||
</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>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Enable Async Shader Compilation</string>
|
||||
<string>Enable async shader compilation</string>
|
||||
</property>
|
||||
</widget>
|
||||
</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>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Enable Async Presentation</string>
|
||||
<string>Enable async presentation</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -303,12 +303,12 @@
|
||||
<string><html><head/><body><p>Reduce stuttering by storing and loading generated shaders to disk.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Use Disk Shader Cache</string>
|
||||
<string>Use disk shader cache</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="toggle_vsync_new">
|
||||
<widget class="QCheckBox" name="toggle_vsync">
|
||||
<property name="toolTip">
|
||||
<string>VSync prevents the screen from tearing, but some graphics cards have lower performance with VSync enabled. Keep it enabled if you don't notice a performance difference.</string>
|
||||
</property>
|
||||
@ -349,7 +349,7 @@
|
||||
<item>
|
||||
<widget class="QLabel" name="label_delay_render">
|
||||
<property name="text">
|
||||
<string>Delay application render thread:</string>
|
||||
<string>Delay Application Render Thread</string>
|
||||
</property>
|
||||
<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>
|
||||
@ -416,7 +416,7 @@
|
||||
<tabstop>toggle_accurate_mul</tabstop>
|
||||
<tabstop>toggle_shader_jit</tabstop>
|
||||
<tabstop>toggle_disk_shader_cache</tabstop>
|
||||
<tabstop>toggle_vsync_new</tabstop>
|
||||
<tabstop>toggle_vsync</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections/>
|
||||
|
||||
@ -132,14 +132,14 @@
|
||||
<item>
|
||||
<widget class="QCheckBox" name="toggle_swap_screen">
|
||||
<property name="text">
|
||||
<string>Swap Screens</string>
|
||||
<string>Swap screens</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="toggle_upright_screen">
|
||||
<property name="text">
|
||||
<string>Rotate Screens Upright</string>
|
||||
<string>Rotate screens upright</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -531,7 +531,7 @@
|
||||
<item>
|
||||
<widget class="QLabel" name="lb_opacity_second_layer">
|
||||
<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>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@ -27,7 +27,7 @@
|
||||
<item>
|
||||
<widget class="QCheckBox" name="toggle_virtual_sd">
|
||||
<property name="text">
|
||||
<string>Use Virtual SD</string>
|
||||
<string>Use virtual SD card</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -42,7 +42,7 @@
|
||||
<item>
|
||||
<widget class="QCheckBox" name="toggle_custom_storage">
|
||||
<property name="text">
|
||||
<string>Use Custom Storage</string>
|
||||
<string>Use custom storage location</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@ -92,7 +92,7 @@ online features (if installed)</string>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="region_label">
|
||||
<property name="text">
|
||||
<string>Region:</string>
|
||||
<string>Region</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -549,7 +549,7 @@ online features (if installed)</string>
|
||||
<item row="20" column="0">
|
||||
<widget class="QLabel" name="label_plugin_loader">
|
||||
<property name="text">
|
||||
<string>3GX Plugin Loader:</string>
|
||||
<string>3GX Plugin Loader</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@ -37,7 +37,7 @@
|
||||
<item>
|
||||
<widget class="QLabel" name="language_label">
|
||||
<property name="text">
|
||||
<string>Interface language:</string>
|
||||
<string>Interface Language</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -51,7 +51,7 @@
|
||||
<item>
|
||||
<widget class="QLabel" name="theme_label">
|
||||
<property name="text">
|
||||
<string>Theme:</string>
|
||||
<string>Theme</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -78,7 +78,7 @@
|
||||
<item>
|
||||
<widget class="QLabel" name="icon_size_label">
|
||||
<property name="text">
|
||||
<string>Icon Size:</string>
|
||||
<string>Icon Size</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -108,7 +108,7 @@
|
||||
<item>
|
||||
<widget class="QLabel" name="row_1_text_label">
|
||||
<property name="text">
|
||||
<string>Row 1 Text:</string>
|
||||
<string>Row 1 Text</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -148,7 +148,7 @@
|
||||
<item>
|
||||
<widget class="QLabel" name="row_2_text_label">
|
||||
<property name="text">
|
||||
<string>Row 2 Text:</string>
|
||||
<string>Row 2 Text</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -191,14 +191,14 @@
|
||||
<item>
|
||||
<widget class="QCheckBox" name="toggle_hide_no_icon">
|
||||
<property name="text">
|
||||
<string>Hide Titles without Icon</string>
|
||||
<string>Hide titles without icon</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="toggle_single_line_mode">
|
||||
<property name="text">
|
||||
<string>Single Line Mode</string>
|
||||
<string>Single line mode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -218,7 +218,7 @@
|
||||
<item>
|
||||
<widget class="QCheckBox" name="show_advanced_frametime_info">
|
||||
<property name="text">
|
||||
<string>Show Advanced Frame Time Info</string>
|
||||
<string>Show advanced frame time info</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@ -644,6 +644,11 @@ void GameList::AddGamePopup(QMenu& context_menu, const QString& path, const QStr
|
||||
shortcut_menu->addAction(tr("Add to Applications Menu"));
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_DEVELOPER_OPTIONS
|
||||
context_menu.addSeparator();
|
||||
QAction* stress_test_launch = context_menu.addAction(tr("Stress Test: App Launch"));
|
||||
#endif
|
||||
|
||||
context_menu.addSeparator();
|
||||
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); });
|
||||
connect(remove_play_time_data, &QAction::triggered,
|
||||
[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,
|
||||
[this, path]() { emit OpenPerGameGeneralRequested(path); });
|
||||
connect(open_shader_cache_location, &QAction::triggered, this, [this, program_id] {
|
||||
|
||||
@ -107,6 +107,7 @@ signals:
|
||||
void AddDirectory();
|
||||
void ShowList(bool show);
|
||||
void PopulatingCompleted();
|
||||
void StartingLaunchStressTest(const QString& game_path);
|
||||
|
||||
private slots:
|
||||
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
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
@ -90,7 +90,8 @@ void DirectConnectWindow::Connect() {
|
||||
room_member->Join(ui->nickname->text().toStdString(),
|
||||
Service::CFG::GetConsoleIdHash(system),
|
||||
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);
|
||||
|
||||
@ -193,8 +193,8 @@ void HostRoomWindow::Host() {
|
||||
}
|
||||
#endif
|
||||
member->Join(ui->username->text().toStdString(), Service::CFG::GetConsoleIdHash(system),
|
||||
"127.0.0.1", static_cast<u16>(port), 0, Network::NoPreferredMac, password,
|
||||
token);
|
||||
"127.0.0.1", static_cast<u16>(port), 0,
|
||||
Service::CFG::GetConsoleMacAddress(system), password, token);
|
||||
|
||||
// Store settings
|
||||
UISettings::values.room_nickname = ui->username->text();
|
||||
|
||||
@ -175,7 +175,8 @@ void Lobby::OnJoinRoom(const QModelIndex& source) {
|
||||
#endif
|
||||
if (auto room_member = Network::GetRoomMember().lock()) {
|
||||
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);
|
||||
|
||||
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
|
||||
@ -145,7 +145,7 @@ void SdlConfig::ReadValues() {
|
||||
ReadSetting("Renderer", Settings::values.resolution_factor);
|
||||
ReadSetting("Renderer", Settings::values.use_disk_shader_cache);
|
||||
ReadSetting("Renderer", Settings::values.frame_limit);
|
||||
ReadSetting("Renderer", Settings::values.use_vsync_new);
|
||||
ReadSetting("Renderer", Settings::values.use_vsync);
|
||||
ReadSetting("Renderer", Settings::values.texture_filter);
|
||||
ReadSetting("Renderer", Settings::values.texture_sampling);
|
||||
ReadSetting("Renderer", Settings::values.delay_game_render_thread_us);
|
||||
|
||||
@ -121,7 +121,7 @@ use_shader_jit =
|
||||
# Forces VSync on the display thread. Usually doesn't impact performance, but on some drivers it can
|
||||
# so only turn this off if you notice a speed difference.
|
||||
# 0: Off, 1 (default): On
|
||||
use_vsync_new =
|
||||
use_vsync =
|
||||
|
||||
# Reduce stuttering by storing and loading generated shaders to disk
|
||||
# 0: Off, 1 (default. On)
|
||||
|
||||
@ -67,6 +67,9 @@ HackManager hack_manager = {
|
||||
0x0004013000002C02, // Normal
|
||||
0x0004013000002C03, // Safe mode
|
||||
0x0004013020002C03, // New 3DS safe mode
|
||||
|
||||
// DLP
|
||||
0x0004013000002802,
|
||||
},
|
||||
}},
|
||||
|
||||
|
||||
@ -98,7 +98,7 @@ void LogSettings() {
|
||||
log_setting("Renderer_UseShaderJit", values.use_shader_jit.GetValue());
|
||||
log_setting("Renderer_UseResolutionFactor", values.resolution_factor.GetValue());
|
||||
log_setting("Renderer_FrameLimit", values.frame_limit.GetValue());
|
||||
log_setting("Renderer_VSyncNew", values.use_vsync_new.GetValue());
|
||||
log_setting("Renderer_VSyncNew", values.use_vsync.GetValue());
|
||||
log_setting("Renderer_PostProcessingShader", values.pp_shader_name.GetValue());
|
||||
log_setting("Renderer_FilterMode", values.filter_mode.GetValue());
|
||||
log_setting("Renderer_TextureFilter", GetTextureFilterName(values.texture_filter.GetValue()));
|
||||
@ -200,7 +200,7 @@ void RestoreGlobalState(bool is_powered_on) {
|
||||
values.use_hw_shader.SetGlobal(true);
|
||||
values.use_disk_shader_cache.SetGlobal(true);
|
||||
values.shaders_accurate_mul.SetGlobal(true);
|
||||
values.use_vsync_new.SetGlobal(true);
|
||||
values.use_vsync.SetGlobal(true);
|
||||
values.resolution_factor.SetGlobal(true);
|
||||
values.frame_limit.SetGlobal(true);
|
||||
values.texture_filter.SetGlobal(true);
|
||||
|
||||
@ -506,7 +506,7 @@ struct Values {
|
||||
SwitchableSetting<bool> use_hw_shader{true, "use_hw_shader"};
|
||||
SwitchableSetting<bool> use_disk_shader_cache{true, "use_disk_shader_cache"};
|
||||
SwitchableSetting<bool> shaders_accurate_mul{true, "shaders_accurate_mul"};
|
||||
SwitchableSetting<bool> use_vsync_new{true, "use_vsync_new"};
|
||||
SwitchableSetting<bool> use_vsync{true, "use_vsync"};
|
||||
Setting<bool> use_shader_jit{true, "use_shader_jit"};
|
||||
SwitchableSetting<u32, true> resolution_factor{1, 0, 10, "resolution_factor"};
|
||||
SwitchableSetting<double, true> frame_limit{100, 0, 1000, "frame_limit"};
|
||||
@ -585,9 +585,9 @@ struct Values {
|
||||
SwitchableSetting<bool> enable_realtime_audio{false, "enable_realtime_audio"};
|
||||
SwitchableSetting<float, true> volume{1.f, 0.f, 1.f, "volume"};
|
||||
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<std::string> input_device{"auto", "input_device"};
|
||||
Setting<std::string> input_device{"Auto", "input_device"};
|
||||
|
||||
// Camera
|
||||
std::array<std::string, Service::CAM::NumCameras> camera_name;
|
||||
|
||||
@ -13,7 +13,7 @@
|
||||
#include <mutex>
|
||||
#include <sstream>
|
||||
#include <zstd.h>
|
||||
#include <zstd/contrib/seekable_format/zstd_seekable.h>
|
||||
#include <zstd_seekable.h>
|
||||
|
||||
#include <boost/serialization/base_object.hpp>
|
||||
#include <boost/serialization/unique_ptr.hpp>
|
||||
|
||||
@ -496,8 +496,6 @@ System::ResultStatus System::Init(Frontend::EmuWindow& emu_window,
|
||||
dsp_core = std::make_unique<AudioCore::DspLle>(*this, multithread);
|
||||
}
|
||||
|
||||
memory->SetDSP(*dsp_core);
|
||||
|
||||
dsp_core->SetSink(Settings::values.output_type.GetValue(),
|
||||
Settings::values.output_device.GetValue());
|
||||
dsp_core->EnableStretching(Settings::values.enable_audio_stretching.GetValue());
|
||||
@ -819,7 +817,6 @@ void System::serialize(Archive& ar, const unsigned int file_version) {
|
||||
u32 cheats_pid;
|
||||
ar & cheats_pid;
|
||||
timing->UnlockEventQueue();
|
||||
memory->SetDSP(*dsp_core);
|
||||
cheat_engine.Connect(cheats_pid);
|
||||
|
||||
// Re-register gpu callback, because gsp service changed after service_manager got
|
||||
|
||||
@ -66,6 +66,10 @@ bool EmuWindow::IsWithinTouchscreen(const Layout::FramebufferLayout& layout, uns
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!layout.bottom_screen_enabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Settings::StereoRenderOption render_3d_mode = Settings::values.render_3d.GetValue();
|
||||
|
||||
if (render_3d_mode == Settings::StereoRenderOption::SideBySide ||
|
||||
|
||||
@ -864,8 +864,10 @@ bool CIAFile::Close() {
|
||||
if (!complete) {
|
||||
LOG_ERROR(Service_AM, "CIAFile closed prematurely, aborting install...");
|
||||
if (!is_additional_content) {
|
||||
FileUtil::DeleteDirRecursively(
|
||||
GetTitlePath(media_type, container.GetTitleMetadata().GetTitleID()));
|
||||
// Only delete the content folder as there may be user save data in the title folder.
|
||||
const std::string title_content_path =
|
||||
GetTitlePath(media_type, container.GetTitleMetadata().GetTitleID()) + "content/";
|
||||
FileUtil::DeleteDirRecursively(title_content_path);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -3253,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
|
||||
// contents out to sdmc/nand
|
||||
const FileSys::Path cia_path = {};
|
||||
auto file = std::make_shared<Service::FS::File>(
|
||||
am->system.Kernel(), std::make_unique<CIAFile>(am->system, FS::MediaType::NAND), cia_path);
|
||||
std::shared_ptr<Service::FS::File> file;
|
||||
{
|
||||
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;
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
|
||||
@ -3446,7 +3454,7 @@ void Module::Interface::GetProgramInfoFromCia(Kernel::HLERequestContext& ctx) {
|
||||
title_info.version = tmd.GetTitleVersion();
|
||||
title_info.type = tmd.GetTitleType();
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(8, 0);
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(7, 0);
|
||||
rb.Push(ResultSuccess);
|
||||
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(), ""));
|
||||
}
|
||||
|
||||
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> ret;
|
||||
int last = -1;
|
||||
|
||||
@ -672,6 +672,8 @@ u64 MacToU64(const std::string& mac);
|
||||
|
||||
std::string GenerateRandomMAC();
|
||||
|
||||
std::array<u8, 6> GetConsoleMacAddress(Core::System& system);
|
||||
|
||||
} // namespace Service::CFG
|
||||
|
||||
SERVICE_CONSTRUCT(Service::CFG::Module)
|
||||
|
||||
@ -467,6 +467,8 @@ void GSP_GPU::TriggerCmdReqQueue(Kernel::HLERequestContext& ctx) {
|
||||
void GSP_GPU::ImportDisplayCaptureInfo(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
|
||||
LOG_DEBUG(Service_GSP, "called");
|
||||
|
||||
if (active_thread_id == std::numeric_limits<u32>::max()) {
|
||||
LOG_WARNING(Service_GSP, "Called without an active thread.");
|
||||
|
||||
@ -503,28 +505,64 @@ void GSP_GPU::ImportDisplayCaptureInfo(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushRaw(top_entry);
|
||||
rb.PushRaw(bottom_entry);
|
||||
|
||||
LOG_WARNING(Service_GSP, "called");
|
||||
}
|
||||
|
||||
static void CopyFrameBuffer(Core::System& system, VAddr dst, VAddr src, u32 stride, u32 lines) {
|
||||
auto dst_ptr = system.Memory().GetPointer(dst);
|
||||
const auto src_ptr = system.Memory().GetPointer(src);
|
||||
static void CopyFrameBuffer(Core::System& system, VAddr dst, VAddr src, u32 dst_stride,
|
||||
u32 src_stride, u32 lines) {
|
||||
auto* dst_ptr = system.Memory().GetPointer(dst);
|
||||
const auto* src_ptr = system.Memory().GetPointer(src);
|
||||
|
||||
if (!dst_ptr || !src_ptr) {
|
||||
LOG_WARNING(Service_GSP,
|
||||
"Could not resolve pointers for framebuffer capture, skipping screen.");
|
||||
return;
|
||||
}
|
||||
|
||||
system.Memory().RasterizerFlushVirtualRegion(src, stride * lines, Memory::FlushMode::Flush);
|
||||
std::memcpy(dst_ptr, src_ptr, stride * lines);
|
||||
system.Memory().RasterizerFlushVirtualRegion(dst, stride * lines,
|
||||
system.Memory().RasterizerFlushVirtualRegion(src, src_stride * lines, Memory::FlushMode::Flush);
|
||||
|
||||
const u32 copy_bytes_per_line = std::min(src_stride, dst_stride);
|
||||
for (u32 y = 0; y < lines; ++y) {
|
||||
std::memcpy(dst_ptr, src_ptr, copy_bytes_per_line);
|
||||
src_ptr += src_stride;
|
||||
dst_ptr += dst_stride;
|
||||
}
|
||||
|
||||
system.Memory().RasterizerFlushVirtualRegion(dst, dst_stride * lines,
|
||||
Memory::FlushMode::Invalidate);
|
||||
}
|
||||
|
||||
static void ClearFramebuffer(Core::System& system, VAddr dst, u32 dst_stride, u32 lines) {
|
||||
auto* dst_ptr = system.Memory().GetPointer(dst);
|
||||
|
||||
if (!dst_ptr) {
|
||||
LOG_WARNING(Service_GSP,
|
||||
"Could not resolve pointers for framebuffer clear, skipping screen.");
|
||||
return;
|
||||
}
|
||||
|
||||
const u32 set_bytes_per_line = dst_stride;
|
||||
for (u32 y = 0; y < lines; ++y) {
|
||||
std::memset(dst_ptr, 0, set_bytes_per_line);
|
||||
dst_ptr += dst_stride;
|
||||
}
|
||||
|
||||
system.Memory().RasterizerFlushVirtualRegion(dst, dst_stride * lines,
|
||||
Memory::FlushMode::Invalidate);
|
||||
}
|
||||
|
||||
void GSP_GPU::SaveVramSysArea(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
|
||||
LOG_DEBUG(Service_GSP, "called");
|
||||
|
||||
// Taken from GSP decomp. TODO: GSP seems to so something special
|
||||
// when the fb format results in bpp of 0 or 4, most likely clearing
|
||||
// it, more research needed.
|
||||
static const u8 bpp_per_format[] = {// Valid values
|
||||
4, 3, 2, 2, 2,
|
||||
// Invalid values
|
||||
0, 0, 0};
|
||||
|
||||
if (active_thread_id == std::numeric_limits<u32>::max()) {
|
||||
LOG_WARNING(Service_GSP, "Called without an active thread.");
|
||||
|
||||
@ -534,47 +572,71 @@ void GSP_GPU::SaveVramSysArea(Kernel::HLERequestContext& ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_INFO(Service_GSP, "called");
|
||||
|
||||
// TODO: This should also save LCD register state.
|
||||
system.Memory().RasterizerFlushVirtualRegion(Memory::VRAM_VADDR, Memory::VRAM_SIZE,
|
||||
Memory::FlushMode::Flush);
|
||||
const auto vram = system.Memory().GetPointer(Memory::VRAM_VADDR);
|
||||
saved_vram.emplace(std::vector<u8>(Memory::VRAM_SIZE));
|
||||
std::memcpy(saved_vram.get().data(), vram, Memory::VRAM_SIZE);
|
||||
|
||||
const auto top_screen = GetFrameBufferInfo(active_thread_id, 0);
|
||||
auto top_screen = GetFrameBufferInfo(active_thread_id, 0);
|
||||
if (top_screen) {
|
||||
u8 bytes_per_pixel =
|
||||
bpp_per_format[top_screen->framebuffer_info[top_screen->index].GetPixelFormat()];
|
||||
const auto top_fb = top_screen->framebuffer_info[top_screen->index];
|
||||
if (top_fb.address_left) {
|
||||
if (top_fb.address_left && bytes_per_pixel != 0 && bytes_per_pixel != 4) {
|
||||
CopyFrameBuffer(system, FRAMEBUFFER_SAVE_AREA_TOP_LEFT, top_fb.address_left,
|
||||
top_fb.stride, TOP_FRAMEBUFFER_HEIGHT);
|
||||
FRAMEBUFFER_WIDTH * bytes_per_pixel, top_fb.stride,
|
||||
TOP_FRAMEBUFFER_HEIGHT);
|
||||
} else {
|
||||
LOG_WARNING(Service_GSP, "No framebuffer bound to top left screen, skipping capture.");
|
||||
LOG_DEBUG(Service_GSP, "Invalid framebuffer bound to top left screen, clearing...");
|
||||
ClearFramebuffer(system, FRAMEBUFFER_SAVE_AREA_TOP_LEFT,
|
||||
FRAMEBUFFER_WIDTH * bytes_per_pixel, TOP_FRAMEBUFFER_HEIGHT);
|
||||
}
|
||||
if (top_fb.address_right) {
|
||||
if (top_fb.address_right && bytes_per_pixel != 0 && bytes_per_pixel != 4) {
|
||||
CopyFrameBuffer(system, FRAMEBUFFER_SAVE_AREA_TOP_RIGHT, top_fb.address_right,
|
||||
top_fb.stride, TOP_FRAMEBUFFER_HEIGHT);
|
||||
FRAMEBUFFER_WIDTH * bytes_per_pixel, top_fb.stride,
|
||||
TOP_FRAMEBUFFER_HEIGHT);
|
||||
} else {
|
||||
LOG_WARNING(Service_GSP, "No framebuffer bound to top right screen, skipping capture.");
|
||||
LOG_DEBUG(Service_GSP, "Invalid framebuffer bound to top right screen, clearing...");
|
||||
ClearFramebuffer(system, FRAMEBUFFER_SAVE_AREA_TOP_RIGHT,
|
||||
FRAMEBUFFER_WIDTH * bytes_per_pixel, TOP_FRAMEBUFFER_HEIGHT);
|
||||
}
|
||||
|
||||
FrameBufferInfo fb_info = top_screen->framebuffer_info[top_screen->index];
|
||||
|
||||
fb_info.address_left = FRAMEBUFFER_SAVE_AREA_TOP_LEFT;
|
||||
fb_info.address_right = FRAMEBUFFER_SAVE_AREA_TOP_RIGHT;
|
||||
fb_info.stride = FRAMEBUFFER_WIDTH * bytes_per_pixel;
|
||||
system.GPU().SetBufferSwap(0, fb_info);
|
||||
} else {
|
||||
LOG_WARNING(Service_GSP, "No top screen bound, skipping capture.");
|
||||
}
|
||||
|
||||
const auto bottom_screen = GetFrameBufferInfo(active_thread_id, 1);
|
||||
auto bottom_screen = GetFrameBufferInfo(active_thread_id, 1);
|
||||
if (bottom_screen) {
|
||||
u8 bytes_per_pixel =
|
||||
bpp_per_format[bottom_screen->framebuffer_info[bottom_screen->index].GetPixelFormat()];
|
||||
const auto bottom_fb = bottom_screen->framebuffer_info[bottom_screen->index];
|
||||
if (bottom_fb.address_left) {
|
||||
if (bottom_fb.address_left && bytes_per_pixel != 0 && bytes_per_pixel != 4) {
|
||||
CopyFrameBuffer(system, FRAMEBUFFER_SAVE_AREA_BOTTOM, bottom_fb.address_left,
|
||||
bottom_fb.stride, BOTTOM_FRAMEBUFFER_HEIGHT);
|
||||
FRAMEBUFFER_WIDTH * bytes_per_pixel, bottom_fb.stride,
|
||||
BOTTOM_FRAMEBUFFER_HEIGHT);
|
||||
} else {
|
||||
LOG_WARNING(Service_GSP, "No framebuffer bound to bottom screen, skipping capture.");
|
||||
LOG_DEBUG(Service_GSP, "Invalid framebuffer bound to bottom screen, clearing...");
|
||||
ClearFramebuffer(system, FRAMEBUFFER_SAVE_AREA_BOTTOM,
|
||||
FRAMEBUFFER_WIDTH * bytes_per_pixel, BOTTOM_FRAMEBUFFER_HEIGHT);
|
||||
}
|
||||
FrameBufferInfo fb_info = bottom_screen->framebuffer_info[bottom_screen->index];
|
||||
|
||||
fb_info.address_left = FRAMEBUFFER_SAVE_AREA_BOTTOM;
|
||||
fb_info.stride = FRAMEBUFFER_WIDTH * bytes_per_pixel;
|
||||
system.GPU().SetBufferSwap(1, fb_info);
|
||||
} else {
|
||||
LOG_WARNING(Service_GSP, "No bottom screen bound, skipping capture.");
|
||||
}
|
||||
|
||||
// Real GSP waits for VBlank here, but we don't need it (?).
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
@ -582,16 +644,31 @@ void GSP_GPU::SaveVramSysArea(Kernel::HLERequestContext& ctx) {
|
||||
void GSP_GPU::RestoreVramSysArea(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
|
||||
LOG_INFO(Service_GSP, "called");
|
||||
LOG_DEBUG(Service_GSP, "called");
|
||||
|
||||
if (saved_vram) {
|
||||
// TODO: This should also restore LCD register state.
|
||||
auto vram = system.Memory().GetPointer(Memory::VRAM_VADDR);
|
||||
std::memcpy(vram, saved_vram.get().data(), Memory::VRAM_SIZE);
|
||||
system.Memory().RasterizerFlushVirtualRegion(Memory::VRAM_VADDR, Memory::VRAM_SIZE,
|
||||
Memory::FlushMode::Invalidate);
|
||||
}
|
||||
|
||||
auto top_screen = GetFrameBufferInfo(active_thread_id, 0);
|
||||
if (top_screen) {
|
||||
system.GPU().SetBufferSwap(0, top_screen->framebuffer_info[top_screen->index]);
|
||||
} else {
|
||||
LOG_WARNING(Service_GSP, "No top screen bound, skipping restore.");
|
||||
}
|
||||
|
||||
auto bottom_screen = GetFrameBufferInfo(active_thread_id, 1);
|
||||
if (bottom_screen) {
|
||||
system.GPU().SetBufferSwap(1, bottom_screen->framebuffer_info[top_screen->index]);
|
||||
} else {
|
||||
LOG_WARNING(Service_GSP, "No bottom screen bound, skipping restore.");
|
||||
}
|
||||
|
||||
// Real GSP waits for VBlank here, but we don't need it (?).
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
@ -30,6 +30,8 @@ class SharedMemory;
|
||||
namespace Service::GSP {
|
||||
|
||||
struct FrameBufferInfo {
|
||||
static constexpr u32 PIXEL_FORMAT_MASK = 0x7;
|
||||
|
||||
u32 active_fb; // 0 = first, 1 = second
|
||||
u32 address_left;
|
||||
u32 address_right;
|
||||
@ -37,6 +39,10 @@ struct FrameBufferInfo {
|
||||
u32 format; // maps to 0x1EF00X70 ?
|
||||
u32 shown_fb; // maps to 0x1EF00X78 ?
|
||||
u32 unknown;
|
||||
|
||||
u32 GetPixelFormat() {
|
||||
return format & PIXEL_FORMAT_MASK;
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(FrameBufferInfo) == 0x1c, "Struct has incorrect size");
|
||||
|
||||
|
||||
@ -106,15 +106,19 @@ void NWM_UDS::BroadcastNodeMap() {
|
||||
packet.channel = network_channel;
|
||||
packet.type = Network::WifiPacket::PacketType::NodeMap;
|
||||
packet.destination_address = Network::BroadcastMac;
|
||||
std::size_t num_entries = std::count_if(node_map.begin(), node_map.end(),
|
||||
[](const auto& node) { return node.second.connected; });
|
||||
auto node_can_broad = [](auto& node) -> bool {
|
||||
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;
|
||||
packet.data.resize(sizeof(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::size_t offset = sizeof(num_entries);
|
||||
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 + sizeof(node.first), &node.second.node_id,
|
||||
sizeof(node.second.node_id));
|
||||
@ -185,7 +189,8 @@ void NWM_UDS::HandleAssociationResponseFrame(const Network::WifiPacket& packet)
|
||||
using Network::WifiPacket;
|
||||
WifiPacket eapol_start;
|
||||
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.
|
||||
eapol_start.destination_address = packet.transmitter_address;
|
||||
eapol_start.type = WifiPacket::PacketType::Data;
|
||||
@ -217,24 +222,36 @@ void NWM_UDS::HandleEAPoLPacket(const Network::WifiPacket& packet) {
|
||||
|
||||
ASSERT(connection_status.max_nodes != connection_status.total_nodes);
|
||||
|
||||
auto node = DeserializeNodeInfoFromFrame(packet.data);
|
||||
auto eapol_start = DeserializeEAPolStartPacket(packet.data);
|
||||
|
||||
// Get an unused network node id
|
||||
u16 node_id = GetNextAvailableNodeId();
|
||||
node.network_node_id = node_id;
|
||||
auto node = DeserializeNodeInfo(eapol_start.node);
|
||||
|
||||
connection_status.node_bitmask |= 1 << (node_id - 1);
|
||||
connection_status.changed_nodes |= 1 << (node_id - 1);
|
||||
connection_status.nodes[node_id - 1] = node.network_node_id;
|
||||
connection_status.total_nodes++;
|
||||
if (eapol_start.conn_type == ConnectionType::Client) {
|
||||
// Get an unused network node id
|
||||
u16 node_id = GetNextAvailableNodeId();
|
||||
node.network_node_id = node_id;
|
||||
|
||||
node_info[node_id - 1] = node;
|
||||
network_info.total_nodes++;
|
||||
connection_status.node_bitmask |= 1 << (node_id - 1);
|
||||
connection_status.changed_nodes |= 1 << (node_id - 1);
|
||||
connection_status.nodes[node_id - 1] = node.network_node_id;
|
||||
connection_status.total_nodes++;
|
||||
|
||||
node_map[packet.transmitter_address].node_id = node.network_node_id;
|
||||
node_map[packet.transmitter_address].connected = true;
|
||||
node_info[node_id - 1] = node;
|
||||
network_info.total_nodes++;
|
||||
|
||||
BroadcastNodeMap();
|
||||
node_map[packet.transmitter_address].node_id = node.network_node_id;
|
||||
node_map[packet.transmitter_address].connected = true;
|
||||
node_map[packet.transmitter_address].spec = false;
|
||||
|
||||
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.
|
||||
using Network::WifiPacket;
|
||||
@ -282,15 +299,23 @@ void NWM_UDS::HandleEAPoLPacket(const Network::WifiPacket& packet) {
|
||||
node_info[index - 1] = DeserializeNodeInfo(node);
|
||||
}
|
||||
|
||||
if (conn_type == ConnectionType::Client) {
|
||||
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 = NetworkStatus::ConnectedAsClient;
|
||||
connection_status.status_change_reason = NetworkStatusChangeReason::ConnectionEstablished;
|
||||
// Some games require ConnectToNetwork to block, for now it doesn't
|
||||
// If blocking is implemented this lock needs to be changed,
|
||||
// otherwise it might cause deadlocks
|
||||
connection_status_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
|
||||
// On a 3ds this packet wouldn't be addressed to already connected clients
|
||||
// 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()};
|
||||
|
||||
if (connection_status.status != NetworkStatus::ConnectedAsHost &&
|
||||
connection_status.status != NetworkStatus::ConnectedAsClient) {
|
||||
// TODO(B3N30): Handle spectators
|
||||
LOG_DEBUG(Service_NWM, "Ignored SecureDataPacket, because connection status is {}",
|
||||
connection_status.status != NetworkStatus::ConnectedAsClient &&
|
||||
connection_status.status != NetworkStatus::ConnectedAsSpectator) {
|
||||
LOG_DEBUG(Service_NWM, "Ignored SecureDataPacket because connection status is {}",
|
||||
static_cast<u32>(connection_status.status));
|
||||
return;
|
||||
}
|
||||
@ -370,12 +395,14 @@ void NWM_UDS::HandleSecureDataPacket(const Network::WifiPacket& packet) {
|
||||
// TODO(B3N30): Allow more than one bind node per channel.
|
||||
auto channel_info = channel_data.find(secure_data.data_channel);
|
||||
// Ignore packets from channels we're not interested in.
|
||||
if (channel_info == channel_data.end())
|
||||
if (channel_info == channel_data.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// Add the received packet to the data queue.
|
||||
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
|
||||
if (GetAuthenticationSeqNumber(packet.data) == AuthenticationSeq::SEQ1) {
|
||||
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);
|
||||
if (connection_status.status != NetworkStatus::ConnectedAsHost) {
|
||||
@ -454,13 +483,13 @@ void NWM_UDS::HandleAuthenticationFrame(const Network::WifiPacket& packet) {
|
||||
return;
|
||||
}
|
||||
// Respond with an authentication response frame with SEQ2
|
||||
auth_request.channel = network_channel;
|
||||
auth_request.data = GenerateAuthenticationFrame(AuthenticationSeq::SEQ2);
|
||||
auth_request.destination_address = packet.transmitter_address;
|
||||
auth_request.type = WifiPacket::PacketType::Authentication;
|
||||
auth_response.channel = network_channel;
|
||||
auth_response.data = GenerateAuthenticationFrame(AuthenticationSeq::SEQ2);
|
||||
auth_response.destination_address = packet.transmitter_address;
|
||||
auth_response.type = WifiPacket::PacketType::Authentication;
|
||||
node_map[packet.transmitter_address].connected = false;
|
||||
}
|
||||
SendPacket(auth_request);
|
||||
SendPacket(auth_response);
|
||||
|
||||
SendAssociationResponseFrame(packet.transmitter_address);
|
||||
}
|
||||
@ -495,16 +524,16 @@ void NWM_UDS::HandleDeauthenticationFrame(const Network::WifiPacket& packet) {
|
||||
return;
|
||||
}
|
||||
|
||||
connection_status.node_bitmask &= ~(1 << (node.node_id - 1));
|
||||
connection_status.changed_nodes |= 1 << (node.node_id - 1);
|
||||
connection_status.total_nodes--;
|
||||
connection_status.nodes[node.node_id - 1] = 0;
|
||||
|
||||
network_info.total_nodes--;
|
||||
// TODO(B3N30): broadcast new connection_status to clients
|
||||
if (!node.spec) {
|
||||
connection_status.node_bitmask &= ~(1 << (node.node_id - 1));
|
||||
connection_status.changed_nodes |= 1 << (node.node_id - 1);
|
||||
connection_status.total_nodes--;
|
||||
connection_status.nodes[node.node_id - 1] = 0;
|
||||
|
||||
network_info.total_nodes--;
|
||||
// TODO(B3N30): broadcast new connection_status to clients
|
||||
}
|
||||
node_it->Reset();
|
||||
|
||||
connection_status_event->Signal();
|
||||
}
|
||||
|
||||
@ -588,14 +617,19 @@ void NWM_UDS::RecvBeaconBroadcastData(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
|
||||
u32 out_buffer_size = rp.Pop<u32>();
|
||||
|
||||
// scan input struct
|
||||
u32 unk1 = rp.Pop<u32>();
|
||||
u32 unk2 = rp.Pop<u32>();
|
||||
|
||||
MacAddress mac_address;
|
||||
rp.PopRaw(mac_address);
|
||||
|
||||
// uninitialized data in scan input struct
|
||||
rp.Skip(9, false);
|
||||
|
||||
// end scan input struct
|
||||
|
||||
u32 wlan_comm_id = rp.Pop<u32>();
|
||||
u32 id = rp.Pop<u32>();
|
||||
// From 3dbrew:
|
||||
@ -1042,6 +1076,7 @@ void NWM_UDS::DestroyNetwork(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
void NWM_UDS::DisconnectNetwork(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_NWM, "disconnecting from network");
|
||||
IPC::RequestParser rp(ctx);
|
||||
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) {
|
||||
network_info = {};
|
||||
std::memcpy(&network_info, network_info_buffer.data(), network_info_buffer.size());
|
||||
conn_type = static_cast<ConnectionType>(connection_type);
|
||||
|
||||
// Start the connection sequence
|
||||
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
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
@ -18,6 +18,7 @@
|
||||
#include <boost/serialization/export.hpp>
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/hle/service/nwm/uds_common.h"
|
||||
#include "core/hle/service/service.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.
|
||||
constexpr u32 UDSMaxNodes = 16;
|
||||
|
||||
constexpr u16 NodeIDSpec = 0;
|
||||
|
||||
struct NodeInfo {
|
||||
u64_le friend_code_seed;
|
||||
std::array<u16_le, 10> username;
|
||||
@ -95,7 +98,7 @@ static_assert(sizeof(ConnectionStatus) == 0x30, "ConnectionStatus has incorrect
|
||||
struct NetworkInfo {
|
||||
std::array<u8, 6> host_mac_address;
|
||||
u8 channel;
|
||||
INSERT_PADDING_BYTES(1);
|
||||
u8 unk1;
|
||||
u8 initialized;
|
||||
INSERT_PADDING_BYTES(3);
|
||||
std::array<u8, 3> oui_value;
|
||||
@ -477,7 +480,7 @@ private:
|
||||
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.
|
||||
*/
|
||||
void StartConnectionSequence(const MacAddress& server);
|
||||
@ -526,6 +529,9 @@ private:
|
||||
// Node information about our own system.
|
||||
NodeInfo current_node;
|
||||
|
||||
// whether you are connecting as a client or a spec
|
||||
ConnectionType conn_type;
|
||||
|
||||
struct BindNodeData {
|
||||
u32 bind_node_id; ///< Id of the bind node associated with this data.
|
||||
u8 channel; ///< Channel that this bind node was bound to.
|
||||
@ -548,6 +554,7 @@ private:
|
||||
// Mapping of mac addresses to their respective node_ids.
|
||||
struct Node {
|
||||
bool connected;
|
||||
bool spec;
|
||||
u16 node_id;
|
||||
|
||||
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
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
@ -286,9 +286,11 @@ SecureDataHeader ParseSecureDataHeader(std::span<const u8> data) {
|
||||
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{};
|
||||
eapol_start.association_id = association_id;
|
||||
eapol_start.conn_type = conn_type;
|
||||
eapol_start.node.friend_code_seed = node_info.friend_code_seed;
|
||||
|
||||
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
|
||||
std::memcpy(&eapol_start, frame.data() + sizeof(LLCHeader), sizeof(eapol_start));
|
||||
|
||||
NodeInfo 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;
|
||||
return DeserializeNodeInfo(eapol_start.node);
|
||||
}
|
||||
|
||||
NodeInfo DeserializeNodeInfo(const EAPoLNodeInfo& node) {
|
||||
@ -380,4 +376,11 @@ EAPoLLogoffPacket ParseEAPoLLogoffFrame(std::span<const u8> frame) {
|
||||
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
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2017 Citra Emulator Project
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/hle/service/nwm/uds_beacon.h"
|
||||
#include "core/hle/service/nwm/uds_common.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Service::NWM {
|
||||
@ -90,9 +91,8 @@ constexpr u16 EAPoLStartMagic = 0x201;
|
||||
struct EAPoLStartPacket {
|
||||
u16_be magic = EAPoLStartMagic;
|
||||
u16_be association_id;
|
||||
// This value is hardcoded to 1 in the NWM module.
|
||||
u16_be unknown = 1;
|
||||
INSERT_PADDING_BYTES(2);
|
||||
enum_le<ConnectionType> conn_type;
|
||||
INSERT_PADDING_BYTES(3);
|
||||
EAPoLNodeInfo node;
|
||||
};
|
||||
|
||||
@ -132,7 +132,8 @@ SecureDataHeader ParseSecureDataHeader(std::span<const u8> data);
|
||||
* communication.
|
||||
* @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.
|
||||
@ -151,6 +152,8 @@ u16 GetEAPoLFrameType(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.
|
||||
*/
|
||||
|
||||
@ -78,7 +78,7 @@ const std::array<ServiceModuleInfo, 41> service_module_map{
|
||||
false},
|
||||
{"CECD", 0x00040130'00002602, CECD::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},
|
||||
{"FRD", 0x00040130'00003202, FRD::InstallInterfaces, true},
|
||||
{"GSP", 0x00040130'00001C02, GSP::InstallInterfaces, false},
|
||||
@ -178,7 +178,7 @@ void ServiceFrameworkBase::ReportUnimplementedFunction(u32* cmd_buf, const Funct
|
||||
void ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& context) {
|
||||
auto itr = handlers.find(context.CommandHeader().command_id.Value());
|
||||
const FunctionInfoBase* info = itr == handlers.end() ? nullptr : &itr->second;
|
||||
if (info == nullptr || info->handler_callback == nullptr) {
|
||||
if (info == nullptr || !info->implemented) {
|
||||
context.ReportUnimplemented();
|
||||
return ReportUnimplementedFunction(context.CommandBuffer(), info);
|
||||
}
|
||||
|
||||
@ -82,6 +82,7 @@ private:
|
||||
|
||||
struct FunctionInfoBase {
|
||||
u32 command_id;
|
||||
bool implemented;
|
||||
HandlerFnP<ServiceFrameworkBase> handler_callback;
|
||||
const char* name;
|
||||
};
|
||||
@ -96,6 +97,8 @@ private:
|
||||
void RegisterHandlersBase(const FunctionInfoBase* functions, std::size_t n);
|
||||
void ReportUnimplementedFunction(u32* cmd_buf, const FunctionInfoBase* info);
|
||||
|
||||
void Empty(Kernel::HLERequestContext& ctx) {}
|
||||
|
||||
/// Identifier string used to connect to the service.
|
||||
std::string service_name;
|
||||
/// Maximum number of concurrent sessions that this service can handle.
|
||||
@ -134,9 +137,11 @@ protected:
|
||||
*/
|
||||
constexpr FunctionInfo(u32 command_id, HandlerFnP<Self> handler_callback, const char* name)
|
||||
: FunctionInfoBase{
|
||||
command_id,
|
||||
command_id, handler_callback != nullptr,
|
||||
// Type-erase member function pointer by casting it down to the base class.
|
||||
static_cast<HandlerFnP<ServiceFrameworkBase>>(handler_callback), name} {}
|
||||
handler_callback ? static_cast<HandlerFnP<ServiceFrameworkBase>>(handler_callback)
|
||||
: &ServiceFrameworkBase::Empty,
|
||||
name} {}
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@ -164,7 +164,11 @@ std::unique_ptr<AppLoader> GetLoader(const std::string& filename) {
|
||||
FileType filename_type = GuessFromExtension(filename_extension);
|
||||
|
||||
if (type != filename_type) {
|
||||
LOG_WARNING(Loader, "File {} has a different type than its extension.", filename);
|
||||
// Do not show the error for CIA files, as their type cannot be determined.
|
||||
if (!(type == FileType::Unknown && filename_type == FileType::CIA)) {
|
||||
LOG_WARNING(Loader, "File {} has a different type than its extension.", filename);
|
||||
}
|
||||
|
||||
if (FileType::Unknown == type)
|
||||
type = filename_type;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user