Compare commits

..

39 commits

Author SHA1 Message Date
OpenSauce04
6ecee968ea Updated French translation via Transifex
Some checks failed
citra-build / source (push) Has been cancelled
citra-format / clang-format (push) Has been cancelled
citra-build / linux (appimage) (push) Has been cancelled
citra-build / linux (fresh) (push) Has been cancelled
citra-build / macos (arm64) (push) Has been cancelled
citra-build / macos (x86_64) (push) Has been cancelled
citra-build / windows (msvc) (push) Has been cancelled
citra-build / windows (msys2) (push) Has been cancelled
citra-build / android (push) Has been cancelled
citra-build / ios (push) Has been cancelled
citra-build / macos-universal (push) Has been cancelled
2025-03-22 21:48:42 +00:00
OpenSauce04
5a4c19e01e cmake: Fixed compilation failure if CMAKE_CXX_FLAGS is an empty string 2025-03-22 21:47:50 +00:00
OpenSauce04
96d678a7ee Updated language translations via Transifex 2025-03-22 21:13:19 +00:00
OpenSauce04
7c88951845 cmake: Correctly handle _FORTIFY_SOURCE being pre-defined in CXXFLAGS 2025-03-22 21:11:35 +00:00
PabloMK7
cc7625e87c android: Show unsupported encrypted app message instead of invalid region
Some checks are pending
citra-build / macos-universal (push) Blocked by required conditions
citra-build / source (push) Waiting to run
citra-build / linux (appimage) (push) Waiting to run
citra-build / linux (fresh) (push) Waiting to run
citra-build / macos (arm64) (push) Waiting to run
citra-build / macos (x86_64) (push) Waiting to run
citra-build / windows (msvc) (push) Waiting to run
citra-build / windows (msys2) (push) Waiting to run
citra-build / android (push) Waiting to run
citra-build / ios (push) Waiting to run
citra-format / clang-format (push) Waiting to run
2025-03-22 16:56:45 +00:00
OpenSauce04
9ee1801d32 dist: Increased resolution of azahar.png from 430x to 512x 2025-03-22 16:45:58 +00:00
OpenSauce04
eb95a30176 Install 512x512 icon to CMAKE_INSTALL_PREFIX 2025-03-22 16:45:58 +00:00
OpenSauce04
50e7ffe1a4 android: Updated notification icon to reflect Azahar's logo 2025-03-22 16:20:46 +00:00
PabloMK7
da7388a5d7 Fix install CIA format string 2025-03-22 15:51:09 +00:00
PabloMK7
1df5db9128 Fix temporary frame limit 2025-03-22 15:51:09 +00:00
OpenSauce04
c411c1ef96 Applied clang-format 2025-03-22 15:51:09 +00:00
OpenSauce04
d2a58ea277 qt: Corrected broken link in .3ds/encryption warning message
Some checks are pending
citra-build / source (push) Waiting to run
citra-build / linux (appimage) (push) Waiting to run
citra-build / linux (fresh) (push) Waiting to run
citra-build / macos (arm64) (push) Waiting to run
citra-build / macos (x86_64) (push) Waiting to run
citra-build / macos-universal (push) Blocked by required conditions
citra-build / windows (msvc) (push) Waiting to run
citra-build / windows (msys2) (push) Waiting to run
citra-build / android (push) Waiting to run
citra-build / ios (push) Waiting to run
citra-format / clang-format (push) Waiting to run
2025-03-21 22:37:26 +00:00
OpenSauce04
5d1b0bef2e Updated languages via Transifex 2025-03-21 20:25:15 +00:00
OpenSauce04
3d989b6d3e Fixed encrypted+.3ds warning string being poorly formatted 2025-03-21 20:20:23 +00:00
OpenSauce04
9112dbd56d Updated languages via Transifex 2025-03-21 19:32:42 +00:00
PabloMK7
22b9c547fc qt: Change update URL to the website (#757) 2025-03-21 19:20:18 +00:00
OpenSauce04
afa0cdfe9f strings.xml: Fixed minor formatting issue 2025-03-21 19:09:11 +00:00
PabloMK7
1a73739fd8 Show warning that 3ds files are no longer supported 2025-03-21 19:09:11 +00:00
PabloMK7
67890f59fd Make AM:GetPersonalizedTicketInfoList only return personal tickets 2025-03-21 19:09:11 +00:00
OpenSauce04
bf3eb08b71 video_core: Fixed emulation window artefacts on OpenGL + Wayland 2025-03-21 17:04:48 +00:00
RedBlackAka
3587303222 installer: Clean up Windows Start Menu entry
* Clean up Windows Start Menu entry

* Clean up old Start Menu shortcuts when upgrading
2025-03-21 17:04:03 +00:00
OpenSauce04
9480f269c0 installer: Replaced reference to "Dolphin.exe" left over from the Dolphin installer we're based on 2025-03-21 17:04:01 +00:00
OpenSauce04
9c87fdf711 Updated all files under dist to refer to Azahar instead of Citra
Some checks are pending
citra-build / source (push) Waiting to run
citra-build / linux (appimage) (push) Waiting to run
citra-build / linux (fresh) (push) Waiting to run
citra-build / macos (arm64) (push) Waiting to run
citra-build / macos (x86_64) (push) Waiting to run
citra-build / macos-universal (push) Blocked by required conditions
citra-build / windows (msvc) (push) Waiting to run
citra-build / windows (msys2) (push) Waiting to run
citra-build / android (push) Waiting to run
citra-build / ios (push) Waiting to run
citra-format / clang-format (push) Waiting to run
This resolves some icon theming issues on Linux

Co-authored-by: HurricanePootis <53066639+HurricanePootis@users.noreply.github.com>
2025-03-21 16:37:37 +00:00
OpenSauce04
b0831cf453 cmake: Allow ENABLE_OPENGL option to be overridden on Linux aarch64
Some checks are pending
citra-build / source (push) Waiting to run
citra-build / linux (appimage) (push) Waiting to run
citra-build / linux (fresh) (push) Waiting to run
citra-build / macos (arm64) (push) Waiting to run
citra-build / macos (x86_64) (push) Waiting to run
citra-build / macos-universal (push) Blocked by required conditions
citra-build / windows (msvc) (push) Waiting to run
citra-build / windows (msys2) (push) Waiting to run
citra-build / android (push) Waiting to run
citra-build / ios (push) Waiting to run
citra-format / clang-format (push) Waiting to run
This option was originally disabled due to some devices not supporting OpenGL, however it was implemented by hardcoding the option to be set to OFF via CMAKE_DEPENDENT_OPTION. This change now allows the user to manually set ENABLE_OPENGL to ON in the CMake options, which was previously not possible.
2025-03-20 17:30:03 +00:00
Taylor Rodríguez
06c00a9319 Fix bug where log file was not generated on first run (#729)
Some checks are pending
citra-build / source (push) Waiting to run
citra-build / linux (appimage) (push) Waiting to run
citra-build / linux (fresh) (push) Waiting to run
citra-build / macos (arm64) (push) Waiting to run
citra-build / macos (x86_64) (push) Waiting to run
citra-build / macos-universal (push) Blocked by required conditions
citra-build / windows (msvc) (push) Waiting to run
citra-build / windows (msys2) (push) Waiting to run
citra-build / android (push) Waiting to run
citra-build / ios (push) Waiting to run
citra-format / clang-format (push) Waiting to run
* Fix bug where log file was not generated on first run

This fix resolves issue #727.

On first start, Log::Initialize attempts to create the `azahar-emu/log/`
directory. However, it fails because `azahar-emu/` does not exist. Using
FileUtil::CreateFullPath instead will create both `azahar-emu` and
`log/`.

* Update license header
2025-03-20 11:50:27 +00:00
PabloMK7
2fbfb8159c Fix language related issues (#735) 2025-03-20 11:48:29 +00:00
OpenSauce04
dd0fc33e27 ci: Enabled update checker for tagged Windows and MacOS builds
Some checks are pending
citra-build / source (push) Waiting to run
citra-build / linux (appimage) (push) Waiting to run
citra-build / linux (fresh) (push) Waiting to run
citra-build / macos (arm64) (push) Waiting to run
citra-build / macos (x86_64) (push) Waiting to run
citra-build / macos-universal (push) Blocked by required conditions
citra-build / windows (msvc) (push) Waiting to run
citra-build / windows (msys2) (push) Waiting to run
citra-build / android (push) Waiting to run
citra-build / ios (push) Waiting to run
citra-format / clang-format (push) Waiting to run
This was supposed to be enabled for all platforms, but was erroneously only enabled for Linux
2025-03-19 10:09:59 +00:00
PabloMK7
0f8765eb3e Fix VS uniform fields type declaration
Some checks failed
citra-build / source (push) Has been cancelled
citra-build / linux (appimage) (push) Has been cancelled
citra-build / linux (fresh) (push) Has been cancelled
citra-build / macos (arm64) (push) Has been cancelled
citra-build / macos (x86_64) (push) Has been cancelled
citra-build / windows (msvc) (push) Has been cancelled
citra-build / windows (msys2) (push) Has been cancelled
citra-build / android (push) Has been cancelled
citra-build / ios (push) Has been cancelled
citra-format / clang-format (push) Has been cancelled
citra-build / macos-universal (push) Has been cancelled
2025-03-17 23:01:00 +00:00
OpenSauce04
0e89ee7367 qt: Updated translations via Transifex 2025-03-17 20:13:44 +00:00
PabloMK7
54b997473d Fix uninitialized movable check on artic setup 2025-03-17 20:08:08 +00:00
PabloMK7
643f53f5f2 Implement framebuffer vertical flip flag (#699)
* Implement framebuffer vertical flip flag

* Make VerticalMirror const
2025-03-17 19:44:12 +00:00
PabloMK7
411abde5d1 Add support for uninitialized movable 2025-03-17 19:44:12 +00:00
PabloMK7
2a33d1f91b Fix artic traffic label being white on light theme 2025-03-17 19:35:20 +00:00
Mae Dartmann
d33a2cbf02 externals: Bump SDL2 to fix build with newer pipewire versions 2025-03-17 18:20:17 +00:00
OpenSauce04
8888143931 externals: Updated externals to avoid CMake 4.0 deprecation errors 2025-03-17 18:20:17 +00:00
PabloMK7
ae26f8e8d5 Fix incorrect crypto file handling if exefs override fails 2025-03-17 18:20:17 +00:00
PabloMK7
dd66e3b4a3 Fix system files setup on macos 2025-03-17 18:20:17 +00:00
PabloMK7
89d74d5763 am: Fix force new 3ds deviceID
Some checks failed
citra-format / clang-format (push) Has been cancelled
citra-build / source (push) Has been cancelled
citra-build / linux (appimage) (push) Has been cancelled
citra-build / linux (fresh) (push) Has been cancelled
citra-build / macos (arm64) (push) Has been cancelled
citra-build / macos (x86_64) (push) Has been cancelled
citra-build / windows (msvc) (push) Has been cancelled
citra-build / windows (msys2) (push) Has been cancelled
citra-build / android (push) Has been cancelled
citra-build / ios (push) Has been cancelled
citra-build / macos-universal (push) Has been cancelled
2025-03-15 23:24:36 +00:00
OpenSauce04
5e0797b112 qt: Added Report Compatibility button which redirects to Azahar compatibility list 2025-03-15 22:22:52 +00:00
217 changed files with 11478 additions and 18885 deletions

View file

@ -2,10 +2,7 @@
if [ "$TARGET" = "appimage" ]; then if [ "$TARGET" = "appimage" ]; then
# Compile the AppImage we distribute with Clang. # Compile the AppImage we distribute with Clang.
export EXTRA_CMAKE_FLAGS=(-DCMAKE_CXX_COMPILER=clang++ export EXTRA_CMAKE_FLAGS=(-DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER=clang -DCMAKE_LINKER=/etc/bin/ld.lld)
-DCMAKE_C_COMPILER=clang
-DCMAKE_LINKER=/etc/bin/ld.lld
-DENABLE_ROOM_STANDALONE=OFF)
# Bundle required QT wayland libraries # Bundle required QT wayland libraries
export EXTRA_QT_PLUGINS="waylandcompositor" export EXTRA_QT_PLUGINS="waylandcompositor"
export EXTRA_PLATFORM_PLUGINS="libqwayland-egl.so;libqwayland-generic.so" export EXTRA_PLATFORM_PLUGINS="libqwayland-egl.so;libqwayland-generic.so"

View file

@ -11,7 +11,7 @@ BASE_ARTIFACT_ARCH="${BASE_ARTIFACT##*-}"
mv $BASE_ARTIFACT $BUNDLE_DIR mv $BASE_ARTIFACT $BUNDLE_DIR
# Executable binary paths that need to be combined. # Executable binary paths that need to be combined.
BIN_PATHS=(Azahar.app/Contents/MacOS/azahar) BIN_PATHS=(azahar-room Azahar.app/Contents/MacOS/azahar)
# Dylib paths that need to be combined. # Dylib paths that need to be combined.
IFS=$'\n' IFS=$'\n'
@ -36,11 +36,8 @@ for OTHER_ARTIFACT in "${ARTIFACTS_LIST[@]:1}"; do
done done
done done
# Remove leftover libs so that they aren't distributed
rm -rf "${BUNDLE_DIR}/libs"
# Re-sign executables and bundles after combining. # Re-sign executables and bundles after combining.
APP_PATHS=(Azahar.app) APP_PATHS=(azahar-room Azahar.app)
for APP_PATH in "${APP_PATHS[@]}"; do for APP_PATH in "${APP_PATHS[@]}"; do
codesign --deep -fs - $BUNDLE_DIR/$APP_PATH codesign --deep -fs - $BUNDLE_DIR/$APP_PATH
done done

View file

@ -11,7 +11,6 @@ cmake .. -GNinja \
-DCMAKE_C_COMPILER_LAUNCHER=ccache \ -DCMAKE_C_COMPILER_LAUNCHER=ccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
-DENABLE_QT_TRANSLATION=ON \ -DENABLE_QT_TRANSLATION=ON \
-DENABLE_ROOM_STANDALONE=OFF \
-DUSE_DISCORD_PRESENCE=ON \ -DUSE_DISCORD_PRESENCE=ON \
"${EXTRA_CMAKE_FLAGS[@]}" "${EXTRA_CMAKE_FLAGS[@]}"
ninja ninja

View file

@ -14,7 +14,7 @@ else
fi fi
# Archive and upload the artifacts. # Archive and upload the artifacts.
mkdir -p artifacts mkdir artifacts
function pack_artifacts() { function pack_artifacts() {
ARTIFACTS_PATH="$1" ARTIFACTS_PATH="$1"
@ -50,6 +50,11 @@ function pack_artifacts() {
rm -rf "$REV_NAME" rm -rf "$REV_NAME"
} }
if [ "$OS" = "windows" ] && [ "$GITHUB_REF_TYPE" = "tag" ]; then
# Move the installer to the artifacts directory
mv src/installer/bin/*.exe artifacts/
fi
if [ -n "$UNPACKED" ]; then if [ -n "$UNPACKED" ]; then
# Copy the artifacts to be uploaded unpacked. # Copy the artifacts to be uploaded unpacked.
for ARTIFACT in build/bundle/*; do for ARTIFACT in build/bundle/*; do

View file

@ -4,10 +4,6 @@ GITDATE="`git show -s --date=short --format='%ad' | sed 's/-//g'`"
GITREV="`git show -s --format='%h'`" GITREV="`git show -s --format='%h'`"
REV_NAME="azahar-unified-source-${GITDATE}-${GITREV}" REV_NAME="azahar-unified-source-${GITDATE}-${GITREV}"
if [ "$GITHUB_REF_TYPE" = "tag" ]; then
REV_NAME="azahar-unified-source-$GITHUB_REF_NAME"
fi
COMPAT_LIST='dist/compatibility_list/compatibility_list.json' COMPAT_LIST='dist/compatibility_list/compatibility_list.json'
mkdir artifacts mkdir artifacts

View file

@ -49,14 +49,12 @@ jobs:
${{ runner.os }}-${{ matrix.target }}- ${{ runner.os }}-${{ matrix.target }}-
- name: Build - name: Build
run: ./.ci/linux.sh run: ./.ci/linux.sh
- name: Move AppImage to artifacts directory - name: Pack
run: ./.ci/pack.sh
if: ${{ matrix.target == 'appimage' }} if: ${{ matrix.target == 'appimage' }}
run: |
mkdir -p artifacts
mv build/bundle/*.AppImage artifacts/
- name: Upload - name: Upload
if: ${{ matrix.target == 'appimage' }}
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
if: ${{ matrix.target == 'appimage' }}
with: with:
name: ${{ env.OS }}-${{ env.TARGET }} name: ${{ env.OS }}-${{ env.TARGET }}
path: artifacts/ path: artifacts/
@ -150,19 +148,19 @@ jobs:
restore-keys: | restore-keys: |
${{ runner.os }}-${{ matrix.target }}- ${{ runner.os }}-${{ matrix.target }}-
- name: Set up MSVC - name: Set up MSVC
if: ${{ matrix.target == 'msvc' }}
uses: ilammy/msvc-dev-cmd@v1 uses: ilammy/msvc-dev-cmd@v1
if: ${{ matrix.target == 'msvc' }}
- name: Install extra tools (MSVC) - name: Install extra tools (MSVC)
run: choco install ccache ninja wget
if: ${{ matrix.target == 'msvc' }} if: ${{ matrix.target == 'msvc' }}
run: choco install ccache ninja ptime wget
- name: Install vulkan-sdk (MSVC) - name: Install vulkan-sdk (MSVC)
if: ${{ matrix.target == 'msvc' }}
run: | run: |
wget https://sdk.lunarg.com/sdk/download/1.3.296.0/windows/VulkanSDK-1.3.296.0-Installer.exe -O D:/a/_temp/vulkan.exe wget https://sdk.lunarg.com/sdk/download/1.3.296.0/windows/VulkanSDK-1.3.296.0-Installer.exe -O D:/a/_temp/vulkan.exe
D:/a/_temp/vulkan.exe --accept-licenses --default-answer --confirm-command install D:/a/_temp/vulkan.exe --accept-licenses --default-answer --confirm-command install
if: ${{ matrix.target == 'msvc' }}
- name: Set up MSYS2 - name: Set up MSYS2
if: ${{ matrix.target == 'msys2' }}
uses: msys2/setup-msys2@v2 uses: msys2/setup-msys2@v2
if: ${{ matrix.target == 'msys2' }}
with: with:
msystem: clang64 msystem: clang64
update: true update: true
@ -170,16 +168,10 @@ jobs:
pacboy: >- pacboy: >-
toolchain:p ccache:p cmake:p ninja:p spirv-tools:p toolchain:p ccache:p cmake:p ninja:p spirv-tools:p
qt6-base:p qt6-multimedia:p qt6-multimedia-wmf:p qt6-tools:p qt6-translations:p qt6-base:p qt6-multimedia:p qt6-multimedia-wmf:p qt6-tools:p qt6-translations:p
- name: Install extra tools (MSYS2)
if: ${{ matrix.target == 'msys2' }}
uses: crazy-max/ghaction-chocolatey@v3
with:
args: install ptime wget
- name: Install NSIS - name: Install NSIS
if: ${{ github.ref_type == 'tag' }}
run: | run: |
wget https://download.sourceforge.net/project/nsis/NSIS%203/3.11/nsis-3.11-setup.exe -O D:/a/_temp/nsis-setup.exe Invoke-WebRequest https://deac-riga.dl.sourceforge.net/project/nsis/NSIS%203/3.10/nsis-3.10-setup.exe?viasf=1 -OutFile C:\WINDOWS\Temp\nsis-3.10-setup.exe
ptime D:/a/_temp/nsis-setup.exe /S Invoke-Expression "& C:\WINDOWS\Temp\nsis-3.10-setup.exe \S"
shell: pwsh shell: pwsh
- name: Disable line ending translation - name: Disable line ending translation
run: git config --global core.autocrlf input run: git config --global core.autocrlf input
@ -190,8 +182,8 @@ jobs:
run: | run: |
cd src\installer cd src\installer
"C:\Program Files (x86)\NSIS\makensis.exe" /DPRODUCT_VARIANT=${{ matrix.target }} /DPRODUCT_VERSION=${{ github.ref_name }} citra.nsi "C:\Program Files (x86)\NSIS\makensis.exe" /DPRODUCT_VARIANT=${{ matrix.target }} /DPRODUCT_VERSION=${{ github.ref_name }} citra.nsi
mkdir ..\..\artifacts 2> NUL mkdir bin
move /y *.exe ..\..\artifacts\ move /y *.exe bin\
shell: cmd shell: cmd
- name: Pack - name: Pack
run: ./.ci/pack.sh run: ./.ci/pack.sh
@ -249,8 +241,8 @@ jobs:
name: ${{ env.OS }}-${{ env.TARGET }} name: ${{ env.OS }}-${{ env.TARGET }}
path: src/android/app/artifacts/ path: src/android/app/artifacts/
ios: ios:
if: ${{ !startsWith(github.ref, 'refs/tags/') }}
runs-on: macos-14 runs-on: macos-14
if: ${{ !startsWith(github.ref, 'refs/tags/') }}
env: env:
CCACHE_DIR: ${{ github.workspace }}/.ccache CCACHE_DIR: ${{ github.workspace }}/.ccache
CCACHE_COMPILERCHECK: content CCACHE_COMPILERCHECK: content

View file

@ -1,23 +0,0 @@
name: azahar-stale
on:
schedule:
- cron: "0 0 * * *"
jobs:
stale-issues:
runs-on: ubuntu-latest
permissions:
issues: write
steps:
- uses: actions/stale@v9.1.0
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-issue-stale: 90
days-before-issue-close: 10
stale-issue-message: "This issue has been marked as stale. If there is no activity within the next 10 days, this issue will be closed."
close-issue-message: "This issue has been closed as stale."
days-before-pr-stale: -1
days-before-pr-close: -1
remove-issue-stale-when-updated: true
exempt-issue-labels: "priority - low,priority - medium,priority - high,priority - urgent,documentation,enhancement,miscellaneous,task,refactor"

2
.gitmodules vendored
View file

@ -6,7 +6,7 @@
url = https://github.com/neobrain/nihstro.git url = https://github.com/neobrain/nihstro.git
[submodule "soundtouch"] [submodule "soundtouch"]
path = externals/soundtouch path = externals/soundtouch
url = https://github.com/azahar-emu/soundtouch.git url = https://codeberg.org/soundtouch/soundtouch.git
[submodule "catch2"] [submodule "catch2"]
path = externals/catch2 path = externals/catch2
url = https://github.com/catchorg/Catch2 url = https://github.com/catchorg/Catch2

View file

@ -41,8 +41,8 @@ if (APPLE)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE BOTH) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE BOTH)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE BOTH) set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE BOTH)
else() else()
# Minimum macOS 13 # Minimum macOS 11
set(CMAKE_OSX_DEPLOYMENT_TARGET "13.4") set(CMAKE_OSX_DEPLOYMENT_TARGET "11.0")
endif() endif()
endif() endif()
@ -54,13 +54,6 @@ else()
set(IS_RELEASE_BUILD ON) set(IS_RELEASE_BUILD ON)
endif() endif()
if (MSVC)
add_compile_options(
/wd4711 # Suppresses `function 'xxxxx' selected for automatic inline expansion` messages
/wd5045 # Suppresses `Compiler will insert Spectre mitigation for memory load if /Qspectre switch specified` messages
)
endif()
# LTO takes too much memory and time using MSVC. # LTO takes too much memory and time using MSVC.
if (NOT MSVC AND IS_RELEASE_BUILD) if (NOT MSVC AND IS_RELEASE_BUILD)
set(DEFAULT_ENABLE_LTO ON) set(DEFAULT_ENABLE_LTO ON)
@ -86,8 +79,7 @@ option(ENABLE_QT_TRANSLATION "Enable translations for the Qt frontend" OFF)
option(ENABLE_QT_UPDATE_CHECKER "Enable built-in update checker for the Qt frontend" OFF) option(ENABLE_QT_UPDATE_CHECKER "Enable built-in update checker for the Qt frontend" OFF)
CMAKE_DEPENDENT_OPTION(ENABLE_TESTS "Enable generating tests executable" ON "NOT IOS" OFF) CMAKE_DEPENDENT_OPTION(ENABLE_TESTS "Enable generating tests executable" ON "NOT IOS" OFF)
CMAKE_DEPENDENT_OPTION(ENABLE_ROOM "Enable dedicated room functionality" ON "NOT ANDROID AND NOT IOS" OFF) CMAKE_DEPENDENT_OPTION(ENABLE_ROOM "Enable generating dedicated room executable" ON "NOT ANDROID AND NOT IOS" OFF)
CMAKE_DEPENDENT_OPTION(ENABLE_ROOM_STANDALONE "Enable generating a standalone dedicated room executable" ON "ENABLE_ROOM" OFF)
option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON) option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON)
option(ENABLE_SCRIPTING "Enable RPC server for scripting" ON) option(ENABLE_SCRIPTING "Enable RPC server for scripting" ON)
@ -119,9 +111,6 @@ endif()
if (ENABLE_QT_TRANSLATION) if (ENABLE_QT_TRANSLATION)
add_definitions(-DENABLE_QT_TRANSLATION) add_definitions(-DENABLE_QT_TRANSLATION)
endif() endif()
if (ENABLE_ROOM)
add_definitions(-DENABLE_ROOM)
endif()
if (ENABLE_SDL2_FRONTEND) if (ENABLE_SDL2_FRONTEND)
add_definitions(-DENABLE_SDL2_FRONTEND) add_definitions(-DENABLE_SDL2_FRONTEND)
endif() endif()
@ -488,8 +477,9 @@ if (NOT ANDROID AND NOT IOS)
elseif (ENABLE_SDL2_FRONTEND) elseif (ENABLE_SDL2_FRONTEND)
bundle_target(citra_meta) bundle_target(citra_meta)
endif() endif()
if (ENABLE_ROOM_STANDALONE)
bundle_target(citra_room_standalone) if (ENABLE_ROOM)
bundle_target(citra_room)
endif() endif()
endif() endif()
@ -502,14 +492,11 @@ endif()
# http://standards.freedesktop.org/shared-mime-info-spec/shared-mime-info-spec-latest.html # http://standards.freedesktop.org/shared-mime-info-spec/shared-mime-info-spec-latest.html
if(ENABLE_QT AND UNIX AND NOT APPLE) if(ENABLE_QT AND UNIX AND NOT APPLE)
install(FILES "${PROJECT_SOURCE_DIR}/dist/azahar.desktop" install(FILES "${PROJECT_SOURCE_DIR}/dist/azahar.desktop"
DESTINATION "${CMAKE_INSTALL_PREFIX}/share/applications" DESTINATION "${CMAKE_INSTALL_PREFIX}/share/applications")
RENAME "org.azahar_emu.Azahar.desktop")
install(FILES "${PROJECT_SOURCE_DIR}/dist/azahar.svg" install(FILES "${PROJECT_SOURCE_DIR}/dist/azahar.svg"
DESTINATION "${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/scalable/apps" DESTINATION "${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/scalable/apps")
RENAME "org.azahar_emu.Azahar.svg")
install(FILES "${PROJECT_SOURCE_DIR}/dist/azahar.png" install(FILES "${PROJECT_SOURCE_DIR}/dist/azahar.png"
DESTINATION "${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/512x512/apps" DESTINATION "${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/512x512/apps")
RENAME "org.azahar_emu.Azahar.png") install(FILES "${PROJECT_SOURCE_DIR}/dist/azahar.xml"
install(FILES "${PROJECT_SOURCE_DIR}/dist/org.azahar_emu.Azahar.xml"
DESTINATION "${CMAKE_INSTALL_PREFIX}/share/mime/packages") DESTINATION "${CMAKE_INSTALL_PREFIX}/share/mime/packages")
endif() endif()

View file

@ -117,9 +117,6 @@ if (BUNDLE_TARGET_EXECUTE)
set(extra_linuxdeploy_args --plugin qt) set(extra_linuxdeploy_args --plugin qt)
endif() endif()
# Set up app icon
file(COPY_FILE "${source_path}/dist/azahar.svg" "${CMAKE_BINARY_DIR}/dist/org.azahar_emu.Azahar.svg")
message(STATUS "Creating AppDir for executable ${executable_path}") message(STATUS "Creating AppDir for executable ${executable_path}")
execute_process(COMMAND ${CMAKE_COMMAND} -E env execute_process(COMMAND ${CMAKE_COMMAND} -E env
${extra_linuxdeploy_env} ${extra_linuxdeploy_env}
@ -127,7 +124,7 @@ if (BUNDLE_TARGET_EXECUTE)
${extra_linuxdeploy_args} ${extra_linuxdeploy_args}
--plugin checkrt --plugin checkrt
--executable "${executable_path}" --executable "${executable_path}"
--icon-file "${CMAKE_BINARY_DIR}/dist/org.azahar_emu.Azahar.svg" --icon-file "${source_path}/dist/azahar.svg"
--desktop-file "${source_path}/dist/${executable_name}.desktop" --desktop-file "${source_path}/dist/${executable_name}.desktop"
--appdir "${appdir_path}" --appdir "${appdir_path}"
RESULT_VARIABLE linuxdeploy_appdir_result) RESULT_VARIABLE linuxdeploy_appdir_result)
@ -279,12 +276,22 @@ else()
add_custom_target(bundle) add_custom_target(bundle)
add_custom_command( add_custom_command(
TARGET bundle TARGET bundle
COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_BINARY_DIR}/bundle/" COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_BINARY_DIR}/bundle/")
POST_BUILD)
add_custom_command( add_custom_command(
TARGET bundle TARGET bundle
COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_SOURCE_DIR}/dist/scripting" "${CMAKE_BINARY_DIR}/bundle/scripting" COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_BINARY_DIR}/bundle/dist/")
POST_BUILD) add_custom_command(
TARGET bundle
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_SOURCE_DIR}/dist/azahar.png" "${CMAKE_BINARY_DIR}/bundle/dist/azahar.png")
add_custom_command(
TARGET bundle
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_SOURCE_DIR}/license.txt" "${CMAKE_BINARY_DIR}/bundle/")
add_custom_command(
TARGET bundle
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_SOURCE_DIR}/README.md" "${CMAKE_BINARY_DIR}/bundle/")
add_custom_command(
TARGET bundle
COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_SOURCE_DIR}/dist/scripting" "${CMAKE_BINARY_DIR}/bundle/scripting")
# On Linux, add a command to prepare linuxdeploy and any required plugins before any bundling occurs. # On Linux, add a command to prepare linuxdeploy and any required plugins before any bundling occurs.
if (CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux") if (CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux")
@ -295,8 +302,7 @@ else()
"-DLINUXDEPLOY_PATH=${CMAKE_BINARY_DIR}/externals/linuxdeploy" "-DLINUXDEPLOY_PATH=${CMAKE_BINARY_DIR}/externals/linuxdeploy"
"-DLINUXDEPLOY_ARCH=${CMAKE_HOST_SYSTEM_PROCESSOR}" "-DLINUXDEPLOY_ARCH=${CMAKE_HOST_SYSTEM_PROCESSOR}"
-P "${CMAKE_SOURCE_DIR}/CMakeModules/BundleTarget.cmake" -P "${CMAKE_SOURCE_DIR}/CMakeModules/BundleTarget.cmake"
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" WORKING_DIRECTORY "${CMAKE_BINARY_DIR}")
POST_BUILD)
endif() endif()
endfunction() endfunction()

View file

@ -10,20 +10,20 @@ It was created from the merging of PabloMK7's Citra fork and the Lime3DS project
The goal of this project is to be the de-facto platform for future development. The goal of this project is to be the de-facto platform for future development.
> [!NOTE]
> Azahar has not fully released yet. For this reason, there are no compiled binaries available for download.
>
> It is recommended that only developers and early adopters should use the emulator until our first stable release.
>
> Here be dragons.
<!--
# Installation # Installation
### Windows ### Windows & MacOS
Download the latest release from [Releases](https://github.com/azahar-emu/azahar/releases). Download the latest release from [Releases](https://github.com/azahar-emu/azahar/releases).
If you are unsure of whether you want to use MSYS2 or MSVC, use MSYS2.
### MacOS
Download the latest release from [Releases](https://github.com/azahar-emu/azahar/releases).
The `macos-universal` download will work on both Intel and Apple Silicon Macs.
--- ---
### Android ### Android
The recommended method of downloading Azahar on Android is via the [Google Play store](https://play.google.com/store/apps/details?id=io.github.lime3ds.android). The recommended method of downloading Azahar on Android is via the [Google Play store](https://play.google.com/store/apps/details?id=io.github.lime3ds.android).
@ -31,7 +31,7 @@ The recommended method of downloading Azahar on Android is via the [Google Play
Alternatively, you can install the app using Obtainium: Alternatively, you can install the app using Obtainium:
1. Download and install Obtainium from [here](https://github.com/ImranR98/Obtainium/releases) (use the file named `app-release.apk`) 1. Download and install Obtainium from [here](https://github.com/ImranR98/Obtainium/releases) (use the file named `app-release.apk`)
2. Open Obtainium and click 'Add App' 2. Open Obtainium and click 'Add App'
3. Type `https://github.com/azahar-emu/azahar` into the 'App Source URL' section 3. Type `https://github.com/azahar/azahar-emu` into the 'App Source URL' section
4. Click 'Add' 4. Click 'Add'
5. Click 'Install' 5. Click 'Install'
@ -44,8 +44,6 @@ Keep in mind that you will not recieve automatic updates when installing via the
Azahar is available as an AppImage on the [Releases](https://github.com/azahar-emu/azahar/releases) page. Azahar is available as an AppImage on the [Releases](https://github.com/azahar-emu/azahar/releases) page.
A Flatpak distribution is currently being worked on.
<!--
We are also on Flathub: We are also on Flathub:
<a href=https://flathub.org/apps/org.azahar-emu.azahar><img width='180' alt='Download on Flathub' src='https://dl.flathub.org/assets/badges/flathub-badge-en.png'/></a> <a href=https://flathub.org/apps/org.azahar-emu.azahar><img width='180' alt='Download on Flathub' src='https://dl.flathub.org/assets/badges/flathub-badge-en.png'/></a>
@ -86,13 +84,13 @@ Below are the minimum requirements to run Azahar:
### Desktop ### Desktop
``` ```
Operating System: Windows 10 (64-bit), MacOS 13 (Ventura), or modern 64-bit Linux Operating System: Windows 10 (64-bit), MacOS 13 (Ventura), or modern 64-bit Linux
CPU: x86-64/ARM64 CPU (Windows for ARM not supported). Single core performance higher than 1,800 on Passmark CPU: x86-64 (64-bit) CPU. Single core performance higher than 1,800 on Passmark
GPU: OpenGL 4.3 or Vulkan 1.1 support GPU: OpenGL 4.3 or Vulkan 1.1 support
Memory: 2GB of RAM. 4GB is recommended Memory: 2GB of RAM. 4GB is recommended
``` ```
### Android ### Android
``` ```
Operating System: Android 9.0+ (64-bit) Operating System: Android 9.0+
CPU: Snapdragon 835 SoC or better CPU: Snapdragon 835 SoC or better
GPU: OpenGL ES 3.2 or Vulkan 1.1 support GPU: OpenGL ES 3.2 or Vulkan 1.1 support
Memory: 2GB of RAM. 4GB is recommended Memory: 2GB of RAM. 4GB is recommended

View file

@ -3,7 +3,7 @@ Version=1.0
Type=Application Type=Application
Name=Azahar Room Name=Azahar Room
Comment=Multiplayer room host for Azahar Comment=Multiplayer room host for Azahar
Icon=org.azahar_emu.Azahar Icon=azahar
TryExec=azahar-room TryExec=azahar-room
Exec=azahar-room %f Exec=azahar-room %f
Categories=Game;Emulator; Categories=Game;Emulator;

2
dist/azahar.desktop vendored
View file

@ -6,7 +6,7 @@ GenericName=3DS Emulator
GenericName[fr]=Émulateur 3DS GenericName[fr]=Émulateur 3DS
Comment=Nintendo 3DS video game console emulator Comment=Nintendo 3DS video game console emulator
Comment[fr]=Émulateur de console de jeu Nintendo 3DS Comment[fr]=Émulateur de console de jeu Nintendo 3DS
Icon=org.azahar_emu.Azahar Icon=azahar
TryExec=azahar TryExec=azahar
Exec=azahar %f Exec=azahar %f
Categories=Game;Emulator; Categories=Game;Emulator;

@ -1 +1 @@
Subproject commit b153099f511759824941f5797ca69e2d39e57de3 Subproject commit 955c560089186a86a90b67f0427f6dfdabc1f177

File diff suppressed because it is too large Load diff

1071
dist/languages/da_DK.ts vendored

File diff suppressed because it is too large Load diff

986
dist/languages/de.ts vendored

File diff suppressed because it is too large Load diff

964
dist/languages/el.ts vendored

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

964
dist/languages/fi.ts vendored

File diff suppressed because it is too large Load diff

977
dist/languages/fr.ts vendored

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

964
dist/languages/id.ts vendored

File diff suppressed because it is too large Load diff

982
dist/languages/it.ts vendored

File diff suppressed because it is too large Load diff

1067
dist/languages/ja_JP.ts vendored

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

964
dist/languages/nb.ts vendored

File diff suppressed because it is too large Load diff

970
dist/languages/nl.ts vendored

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

973
dist/languages/sv.ts vendored

File diff suppressed because it is too large Load diff

1008
dist/languages/tr_TR.ts vendored

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,21 +1,15 @@
# Copyright Citra Emulator Project / Azahar Emulator Project
# Licensed under GPLv2 or any later version
# Refer to the license.txt file included.
import struct import struct
import random import random
import enum import enum
import socket import socket
CURRENT_REQUEST_VERSION = 1 CURRENT_REQUEST_VERSION = 1
MAX_REQUEST_DATA_SIZE = 1024 MAX_REQUEST_DATA_SIZE = 32
MAX_PACKET_SIZE = 1024 + 0x10 MAX_PACKET_SIZE = 48
class RequestType(enum.IntEnum): class RequestType(enum.IntEnum):
ReadMemory = 1, ReadMemory = 1,
WriteMemory = 2, WriteMemory = 2
ProcessList = 3,
SetGetProcess = 4,
CITRA_PORT = 45987 CITRA_PORT = 45987
@ -40,55 +34,6 @@ class Citra:
return raw_reply[4*4:] return raw_reply[4*4:]
return None return None
def process_list(self):
processes = {}
read_processes = 0
while True:
request_data = struct.pack("II", read_processes, 0x7FFFFFFF)
request, request_id = self._generate_header(RequestType.ProcessList, len(request_data))
request += request_data
self.socket.sendto(request, (self.address, CITRA_PORT))
raw_reply = self.socket.recv(MAX_PACKET_SIZE)
reply_data = self._read_and_validate_header(raw_reply, request_id, RequestType.ProcessList)
if reply_data:
read_count = struct.unpack("I", reply_data[0:4])[0]
reply_data = reply_data[4:]
if read_count == 0:
break
read_processes += read_count
for i in range(read_count):
proc_data = reply_data[i * 0x14 : (i + 1) * 0x14]
proc_id, title_id, proc_name = struct.unpack("<IQ8s", proc_data)
proc_name = proc_name.rstrip(b"\x00").decode("ascii")
processes[proc_id] = (title_id, proc_name)
else:
break
return processes
def get_process(self):
request_data = struct.pack("II", 0, 0)
request, request_id = self._generate_header(RequestType.SetGetProcess, len(request_data))
request += request_data
self.socket.sendto(request, (self.address, CITRA_PORT))
raw_reply = self.socket.recv(MAX_PACKET_SIZE)
reply_data = self._read_and_validate_header(raw_reply, request_id, RequestType.SetGetProcess)
if reply_data:
return struct.unpack("I", reply_data)[0]
else:
return None
def set_process(self, process_id):
request_data = struct.pack("II", 1, process_id)
request, request_id = self._generate_header(RequestType.SetGetProcess, len(request_data))
request += request_data
self.socket.sendto(request, (self.address, CITRA_PORT))
self.socket.recv(MAX_PACKET_SIZE)
def read_memory(self, read_address, read_size): def read_memory(self, read_address, read_size):
""" """
>>> c.read_memory(0x100000, 4) >>> c.read_memory(0x100000, 4)

@ -1 +1 @@
Subproject commit cb50201fc09290cd078c7ab27917504491f7f96a Subproject commit 54eb03c20351f94850bfca3955cb87465a860ef6

2
externals/sdl2/SDL vendored

@ -1 +1 @@
Subproject commit 2359383fc187386204c3bb22de89655a494cd128 Subproject commit 9e079fe9c7931738ed63d257b1d7fb8a07b66824

View file

@ -199,10 +199,6 @@ if (ENABLE_ROOM)
add_subdirectory(citra_room) add_subdirectory(citra_room)
endif() endif()
if (ENABLE_ROOM_STANDALONE)
add_subdirectory(citra_room_standalone)
endif()
if (ANDROID) if (ANDROID)
add_subdirectory(android/app/src/main/jni) add_subdirectory(android/app/src/main/jni)
target_include_directories(citra-android PRIVATE android/app/src/main) target_include_directories(citra-android PRIVATE android/app/src/main)

View file

@ -182,14 +182,6 @@ object NativeLibrary {
external fun uninstallSystemFiles(old3DS: Boolean) external fun uninstallSystemFiles(old3DS: Boolean)
external fun isFullConsoleLinked(): Boolean
external fun unlinkConsole()
external fun setTemporaryFrameLimit(speed: Double)
external fun disableTemporaryFrameLimit()
private var coreErrorAlertResult = false private var coreErrorAlertResult = false
private val coreErrorAlertLock = Object() private val coreErrorAlertLock = Object()
@ -777,7 +769,6 @@ object NativeLibrary {
const val BUTTON_DEBUG = 781 const val BUTTON_DEBUG = 781
const val BUTTON_GPIO14 = 782 const val BUTTON_GPIO14 = 782
const val BUTTON_SWAP = 800 const val BUTTON_SWAP = 800
const val BUTTON_TURBO = 801
} }
/** /**

View file

@ -1,4 +1,4 @@
// Copyright Citra Emulator Project / Azahar Emulator Project // Copyright Citra Emulator Project / Lime3DS Emulator Project
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
@ -15,7 +15,6 @@ import android.os.Bundle
import android.view.InputDevice import android.view.InputDevice
import android.view.KeyEvent import android.view.KeyEvent
import android.view.MotionEvent import android.view.MotionEvent
import android.view.Window
import android.view.WindowManager import android.view.WindowManager
import android.widget.Toast import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
@ -45,7 +44,6 @@ import org.citra.citra_emu.utils.FileBrowserHelper
import org.citra.citra_emu.utils.EmulationLifecycleUtil import org.citra.citra_emu.utils.EmulationLifecycleUtil
import org.citra.citra_emu.utils.EmulationMenuSettings import org.citra.citra_emu.utils.EmulationMenuSettings
import org.citra.citra_emu.utils.ThemeUtil import org.citra.citra_emu.utils.ThemeUtil
import org.citra.citra_emu.utils.TurboHelper
import org.citra.citra_emu.viewmodel.EmulationViewModel import org.citra.citra_emu.viewmodel.EmulationViewModel
class EmulationActivity : AppCompatActivity() { class EmulationActivity : AppCompatActivity() {
@ -53,7 +51,7 @@ class EmulationActivity : AppCompatActivity() {
get() = PreferenceManager.getDefaultSharedPreferences(CitraApplication.appContext) get() = PreferenceManager.getDefaultSharedPreferences(CitraApplication.appContext)
var isActivityRecreated = false var isActivityRecreated = false
private val emulationViewModel: EmulationViewModel by viewModels() private val emulationViewModel: EmulationViewModel by viewModels()
val settingsViewModel: SettingsViewModel by viewModels() private val settingsViewModel: SettingsViewModel by viewModels()
private lateinit var binding: ActivityEmulationBinding private lateinit var binding: ActivityEmulationBinding
private lateinit var screenAdjustmentUtil: ScreenAdjustmentUtil private lateinit var screenAdjustmentUtil: ScreenAdjustmentUtil
@ -69,8 +67,6 @@ class EmulationActivity : AppCompatActivity() {
private var isEmulationRunning: Boolean = false private var isEmulationRunning: Boolean = false
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
requestWindowFeature(Window.FEATURE_NO_TITLE)
ThemeUtil.setTheme(this) ThemeUtil.setTheme(this)
settingsViewModel.settings.loadSettings() settingsViewModel.settings.loadSettings()
@ -196,16 +192,9 @@ class EmulationActivity : AppCompatActivity() {
} }
private fun enableFullscreenImmersive() { private fun enableFullscreenImmersive() {
val attributes = window.attributes // TODO: Remove this once we properly account for display insets in the input overlay
window.attributes.layoutInDisplayCutoutMode =
attributes.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER
if (BooleanSetting.EXPAND_TO_CUTOUT_AREA.boolean) {
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
} else {
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER
}
window.attributes = attributes
WindowCompat.setDecorFitsSystemWindows(window, false) WindowCompat.setDecorFitsSystemWindows(window, false)

View file

@ -1,11 +1,10 @@
// Copyright Citra Emulator Project / Azahar Emulator Project // Copyright Citra Emulator Project / Lime3DS Emulator Project
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
package org.citra.citra_emu.adapters package org.citra.citra_emu.adapters
import android.graphics.drawable.Icon import android.graphics.drawable.Icon
import android.content.Intent
import android.net.Uri import android.net.Uri
import android.os.SystemClock import android.os.SystemClock
import android.text.TextUtils import android.text.TextUtils
@ -29,7 +28,6 @@ import androidx.recyclerview.widget.AsyncDifferConfig
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import android.widget.PopupMenu
import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.button.MaterialButton import com.google.android.material.button.MaterialButton
@ -46,9 +44,7 @@ import org.citra.citra_emu.databinding.CardGameBinding
import org.citra.citra_emu.features.cheats.ui.CheatsFragmentDirections import org.citra.citra_emu.features.cheats.ui.CheatsFragmentDirections
import org.citra.citra_emu.features.settings.ui.SettingsActivity import org.citra.citra_emu.features.settings.ui.SettingsActivity
import org.citra.citra_emu.features.settings.utils.SettingsFile import org.citra.citra_emu.features.settings.utils.SettingsFile
import org.citra.citra_emu.fragments.IndeterminateProgressDialogFragment
import org.citra.citra_emu.model.Game import org.citra.citra_emu.model.Game
import org.citra.citra_emu.utils.FileUtil
import org.citra.citra_emu.utils.GameIconUtils import org.citra.citra_emu.utils.GameIconUtils
import org.citra.citra_emu.viewmodel.GamesViewModel import org.citra.citra_emu.viewmodel.GamesViewModel
@ -207,117 +203,6 @@ class GameAdapter(private val activity: AppCompatActivity, private val inflater:
} }
} }
private data class GameDirectories(
val gameDir: String,
val saveDir: String,
val modsDir: String,
val texturesDir: String,
val appDir: String,
val dlcDir: String,
val updatesDir: String,
val extraDir: String
)
private fun getGameDirectories(game: Game): GameDirectories {
val basePath = "sdmc/Nintendo 3DS/00000000000000000000000000000000/00000000000000000000000000000000"
return GameDirectories(
gameDir = game.path.substringBeforeLast("/"),
saveDir = basePath + "/title/${String.format("%016x", game.titleId).lowercase().substring(0, 8)}/${String.format("%016x", game.titleId).lowercase().substring(8)}/data/00000001",
modsDir = "load/mods/${String.format("%016X", game.titleId)}",
texturesDir = "load/textures/${String.format("%016X", game.titleId)}",
appDir = game.path.substringBeforeLast("/").split("/").filter { it.isNotEmpty() }.joinToString("/"),
dlcDir = basePath + "/title/0004008c/${String.format("%016x", game.titleId).lowercase().substring(8)}/content",
updatesDir = basePath + "/title/0004000e/${String.format("%016x", game.titleId).lowercase().substring(8)}/content",
extraDir = basePath + "/extdata/00000000/${String.format("%016X", game.titleId).substring(8, 14).padStart(8, '0')}"
)
}
private fun showOpenContextMenu(view: View, game: Game) {
val dirs = getGameDirectories(game)
val popup = PopupMenu(view.context, view).apply {
menuInflater.inflate(R.menu.game_context_menu_open, menu)
listOf(
R.id.game_context_open_app to dirs.appDir,
R.id.game_context_open_save_dir to dirs.saveDir,
R.id.game_context_open_updates to dirs.updatesDir,
R.id.game_context_open_dlc to dirs.dlcDir,
R.id.game_context_open_extra to dirs.extraDir
).forEach { (id, dir) ->
menu.findItem(id)?.isEnabled =
CitraApplication.documentsTree.folderUriHelper(dir)?.let {
DocumentFile.fromTreeUri(view.context, it)?.exists()
} ?: false
}
}
popup.setOnMenuItemClickListener { menuItem ->
val intent = Intent(Intent.ACTION_VIEW)
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.setType("*/*")
val uri = when (menuItem.itemId) {
R.id.game_context_open_app -> CitraApplication.documentsTree.folderUriHelper(dirs.appDir)
R.id.game_context_open_save_dir -> CitraApplication.documentsTree.folderUriHelper(dirs.saveDir)
R.id.game_context_open_updates -> CitraApplication.documentsTree.folderUriHelper(dirs.updatesDir)
R.id.game_context_open_dlc -> CitraApplication.documentsTree.folderUriHelper(dirs.dlcDir)
R.id.game_context_open_extra -> CitraApplication.documentsTree.folderUriHelper(dirs.extraDir)
R.id.game_context_open_textures -> CitraApplication.documentsTree.folderUriHelper(dirs.texturesDir, true)
R.id.game_context_open_mods -> CitraApplication.documentsTree.folderUriHelper(dirs.modsDir, true)
else -> null
}
uri?.let {
intent.data = it
view.context.startActivity(intent)
true
} ?: false
}
popup.show()
}
private fun showUninstallContextMenu(view: View, game: Game, bottomSheetDialog: BottomSheetDialog) {
val dirs = getGameDirectories(game)
val popup = PopupMenu(view.context, view).apply {
menuInflater.inflate(R.menu.game_context_menu_uninstall, menu)
listOf(
R.id.game_context_uninstall to dirs.gameDir,
R.id.game_context_uninstall_dlc to dirs.dlcDir,
R.id.game_context_uninstall_updates to dirs.updatesDir
).forEach { (id, dir) ->
menu.findItem(id)?.isEnabled =
CitraApplication.documentsTree.folderUriHelper(dir)?.let {
DocumentFile.fromTreeUri(view.context, it)?.exists()
} ?: false
}
}
popup.setOnMenuItemClickListener { menuItem ->
val uninstallAction: () -> Unit = {
when (menuItem.itemId) {
R.id.game_context_uninstall -> CitraApplication.documentsTree.deleteDocument(dirs.gameDir)
R.id.game_context_uninstall_dlc -> FileUtil.deleteDocument(CitraApplication.documentsTree.folderUriHelper(dirs.dlcDir)
.toString())
R.id.game_context_uninstall_updates -> FileUtil.deleteDocument(CitraApplication.documentsTree.folderUriHelper(dirs.updatesDir)
.toString())
}
ViewModelProvider(activity)[GamesViewModel::class.java].reloadGames(true)
bottomSheetDialog.dismiss()
}
if (menuItem.itemId in listOf(R.id.game_context_uninstall, R.id.game_context_uninstall_dlc, R.id.game_context_uninstall_updates)) {
IndeterminateProgressDialogFragment.newInstance(activity, R.string.uninstalling, false, uninstallAction)
.show(activity.supportFragmentManager, IndeterminateProgressDialogFragment.TAG)
true
} else {
false
}
}
popup.show()
}
private fun showAboutGameDialog(context: Context, game: Game, holder: GameViewHolder, view: View) { private fun showAboutGameDialog(context: Context, game: Game, holder: GameViewHolder, view: View) {
val bottomSheetView = inflater.inflate(R.layout.dialog_about_game, null) val bottomSheetView = inflater.inflate(R.layout.dialog_about_game, null)
@ -360,14 +245,6 @@ class GameAdapter(private val activity: AppCompatActivity, private val inflater:
bottomSheetDialog.dismiss() bottomSheetDialog.dismiss()
} }
bottomSheetView.findViewById<MaterialButton>(R.id.menu_button_open).setOnClickListener {
showOpenContextMenu(it, game)
}
bottomSheetView.findViewById<MaterialButton>(R.id.menu_button_uninstall).setOnClickListener {
showUninstallContextMenu(it, game, bottomSheetDialog)
}
val bottomSheetBehavior = bottomSheetDialog.getBehavior() val bottomSheetBehavior = bottomSheetDialog.getBehavior()
bottomSheetBehavior.skipCollapsed = true bottomSheetBehavior.skipCollapsed = true
bottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED bottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED

View file

@ -1,4 +1,4 @@
// Copyright Citra Emulator Project / Azahar Emulator Project // Copyright 2023 Citra Emulator Project
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
@ -41,8 +41,7 @@ enum class SmallScreenPosition(val int: Int) {
enum class PortraitScreenLayout(val int: Int) { enum class PortraitScreenLayout(val int: Int) {
// These must match what is defined in src/common/settings.h // These must match what is defined in src/common/settings.h
TOP_FULL_WIDTH(0), TOP_FULL_WIDTH(0),
CUSTOM_PORTRAIT_LAYOUT(1), CUSTOM_PORTRAIT_LAYOUT(1);
ORIGINAL(2);
companion object { companion object {
fun from(int: Int): PortraitScreenLayout { fun from(int: Int): PortraitScreenLayout {

View file

@ -1,4 +1,4 @@
// Copyright Citra Emulator Project / Azahar Emulator Project // Copyright 2023 Citra Emulator Project
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
@ -10,6 +10,5 @@ enum class Hotkey(val button: Int) {
CLOSE_GAME(10003), CLOSE_GAME(10003),
PAUSE_OR_RESUME(10004), PAUSE_OR_RESUME(10004),
QUICKSAVE(10005), QUICKSAVE(10005),
QUICKLOAD(10006), QUICKLOAD(10006);
TURBO_LIMIT(10007);
} }

View file

@ -1,4 +1,4 @@
// Copyright Citra Emulator Project / Azahar Emulator Project // Copyright 2023 Citra Emulator Project
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
@ -9,14 +9,11 @@ import android.widget.Toast
import org.citra.citra_emu.NativeLibrary import org.citra.citra_emu.NativeLibrary
import org.citra.citra_emu.R import org.citra.citra_emu.R
import org.citra.citra_emu.utils.EmulationLifecycleUtil import org.citra.citra_emu.utils.EmulationLifecycleUtil
import org.citra.citra_emu.utils.TurboHelper
import org.citra.citra_emu.display.ScreenAdjustmentUtil import org.citra.citra_emu.display.ScreenAdjustmentUtil
class HotkeyUtility( class HotkeyUtility(private val screenAdjustmentUtil: ScreenAdjustmentUtil, private val context: Context) {
private val screenAdjustmentUtil: ScreenAdjustmentUtil,
private val context: Context) {
private val hotkeyButtons = Hotkey.entries.map { it.button } val hotkeyButtons = Hotkey.entries.map { it.button }
fun handleHotkey(bindedButton: Int): Boolean { fun handleHotkey(bindedButton: Int): Boolean {
if(hotkeyButtons.contains(bindedButton)) { if(hotkeyButtons.contains(bindedButton)) {
@ -25,17 +22,16 @@ class HotkeyUtility(
Hotkey.CYCLE_LAYOUT.button -> screenAdjustmentUtil.cycleLayouts() Hotkey.CYCLE_LAYOUT.button -> screenAdjustmentUtil.cycleLayouts()
Hotkey.CLOSE_GAME.button -> EmulationLifecycleUtil.closeGame() Hotkey.CLOSE_GAME.button -> EmulationLifecycleUtil.closeGame()
Hotkey.PAUSE_OR_RESUME.button -> EmulationLifecycleUtil.pauseOrResume() Hotkey.PAUSE_OR_RESUME.button -> EmulationLifecycleUtil.pauseOrResume()
Hotkey.TURBO_LIMIT.button -> TurboHelper.setTurboEnabled(!TurboHelper.isTurboSpeedEnabled())
Hotkey.QUICKSAVE.button -> { Hotkey.QUICKSAVE.button -> {
NativeLibrary.saveState(NativeLibrary.QUICKSAVE_SLOT) NativeLibrary.saveState(NativeLibrary.QUICKSAVE_SLOT)
Toast.makeText(context, Toast.makeText(context,
context.getString(R.string.saving), context.getString(R.string.quicksave_saving),
Toast.LENGTH_SHORT).show() Toast.LENGTH_SHORT).show()
} }
Hotkey.QUICKLOAD.button -> { Hotkey.QUICKLOAD.button -> {
val wasLoaded = NativeLibrary.loadStateIfAvailable(NativeLibrary.QUICKSAVE_SLOT) val wasLoaded = NativeLibrary.loadStateIfAvailable(NativeLibrary.QUICKSAVE_SLOT)
val stringRes = if(wasLoaded) { val stringRes = if(wasLoaded) {
R.string.loading R.string.quickload_loading
} else { } else {
R.string.quickload_not_found R.string.quickload_not_found
} }

View file

@ -9,7 +9,6 @@ enum class BooleanSetting(
override val section: String, override val section: String,
override val defaultValue: Boolean override val defaultValue: Boolean
) : AbstractBooleanSetting { ) : AbstractBooleanSetting {
EXPAND_TO_CUTOUT_AREA("expand_to_cutout_area", Settings.SECTION_LAYOUT, false),
SPIRV_SHADER_GEN("spirv_shader_gen", Settings.SECTION_RENDERER, true), SPIRV_SHADER_GEN("spirv_shader_gen", Settings.SECTION_RENDERER, true),
ASYNC_SHADERS("async_shader_compilation", Settings.SECTION_RENDERER, false), ASYNC_SHADERS("async_shader_compilation", Settings.SECTION_RENDERER, false),
PLUGIN_LOADER("plugin_loader", Settings.SECTION_SYSTEM, false), PLUGIN_LOADER("plugin_loader", Settings.SECTION_SYSTEM, false),

View file

@ -33,7 +33,6 @@ enum class IntSetting(
LANDSCAPE_BOTTOM_Y("custom_bottom_y",Settings.SECTION_LAYOUT,480), LANDSCAPE_BOTTOM_Y("custom_bottom_y",Settings.SECTION_LAYOUT,480),
LANDSCAPE_BOTTOM_WIDTH("custom_bottom_width",Settings.SECTION_LAYOUT,640), LANDSCAPE_BOTTOM_WIDTH("custom_bottom_width",Settings.SECTION_LAYOUT,640),
LANDSCAPE_BOTTOM_HEIGHT("custom_bottom_height",Settings.SECTION_LAYOUT,480), LANDSCAPE_BOTTOM_HEIGHT("custom_bottom_height",Settings.SECTION_LAYOUT,480),
SCREEN_GAP("screen_gap",Settings.SECTION_LAYOUT,0),
PORTRAIT_SCREEN_LAYOUT("portrait_layout_option",Settings.SECTION_LAYOUT,0), PORTRAIT_SCREEN_LAYOUT("portrait_layout_option",Settings.SECTION_LAYOUT,0),
PORTRAIT_TOP_X("custom_portrait_top_x",Settings.SECTION_LAYOUT,0), PORTRAIT_TOP_X("custom_portrait_top_x",Settings.SECTION_LAYOUT,0),
PORTRAIT_TOP_Y("custom_portrait_top_y",Settings.SECTION_LAYOUT,0), PORTRAIT_TOP_Y("custom_portrait_top_y",Settings.SECTION_LAYOUT,0),
@ -43,7 +42,7 @@ enum class IntSetting(
PORTRAIT_BOTTOM_Y("custom_portrait_bottom_y",Settings.SECTION_LAYOUT,480), PORTRAIT_BOTTOM_Y("custom_portrait_bottom_y",Settings.SECTION_LAYOUT,480),
PORTRAIT_BOTTOM_WIDTH("custom_portrait_bottom_width",Settings.SECTION_LAYOUT,640), PORTRAIT_BOTTOM_WIDTH("custom_portrait_bottom_width",Settings.SECTION_LAYOUT,640),
PORTRAIT_BOTTOM_HEIGHT("custom_portrait_bottom_height",Settings.SECTION_LAYOUT,480), PORTRAIT_BOTTOM_HEIGHT("custom_portrait_bottom_height",Settings.SECTION_LAYOUT,480),
AUDIO_INPUT_TYPE("input_type", Settings.SECTION_AUDIO, 0), AUDIO_INPUT_TYPE("output_type", Settings.SECTION_AUDIO, 0),
NEW_3DS("is_new_3ds", Settings.SECTION_SYSTEM, 1), NEW_3DS("is_new_3ds", Settings.SECTION_SYSTEM, 1),
LLE_APPLETS("lle_applets", Settings.SECTION_SYSTEM, 1), LLE_APPLETS("lle_applets", Settings.SECTION_SYSTEM, 1),
CPU_CLOCK_SPEED("cpu_clock_percentage", Settings.SECTION_CORE, 100), CPU_CLOCK_SPEED("cpu_clock_percentage", Settings.SECTION_CORE, 100),
@ -61,14 +60,11 @@ enum class IntSetting(
VSYNC("use_vsync_new", Settings.SECTION_RENDERER, 1), VSYNC("use_vsync_new", Settings.SECTION_RENDERER, 1),
DEBUG_RENDERER("renderer_debug", Settings.SECTION_DEBUG, 0), DEBUG_RENDERER("renderer_debug", Settings.SECTION_DEBUG, 0),
TEXTURE_FILTER("texture_filter", Settings.SECTION_RENDERER, 0), TEXTURE_FILTER("texture_filter", Settings.SECTION_RENDERER, 0),
TEXTURE_SAMPLING("texture_sampling", Settings.SECTION_RENDERER, 0),
USE_FRAME_LIMIT("use_frame_limit", Settings.SECTION_RENDERER, 1), USE_FRAME_LIMIT("use_frame_limit", Settings.SECTION_RENDERER, 1),
DELAY_RENDER_THREAD_US("delay_game_render_thread_us", Settings.SECTION_RENDERER, 0), DELAY_RENDER_THREAD_US("delay_game_render_thread_us", Settings.SECTION_RENDERER, 0),
USE_ARTIC_BASE_CONTROLLER("use_artic_base_controller", Settings.SECTION_CONTROLS, 0), USE_ARTIC_BASE_CONTROLLER("use_artic_base_controller", Settings.SECTION_CONTROLS, 0),
ORIENTATION_OPTION("screen_orientation", Settings.SECTION_LAYOUT, 2), ORIENTATION_OPTION("screen_orientation", Settings.SECTION_LAYOUT, 2),
DISABLE_RIGHT_EYE_RENDER("disable_right_eye_render", Settings.SECTION_RENDERER, 0), DISABLE_RIGHT_EYE_RENDER("disable_right_eye_render", Settings.SECTION_RENDERER, 0);
TURBO_LIMIT("turbo_limit", Settings.SECTION_CORE, 200);
override var int: Int = defaultValue override var int: Int = defaultValue
override val valueAsString: String override val valueAsString: String
@ -97,7 +93,7 @@ enum class IntSetting(
ASYNC_CUSTOM_LOADING, ASYNC_CUSTOM_LOADING,
AUDIO_INPUT_TYPE, AUDIO_INPUT_TYPE,
USE_ARTIC_BASE_CONTROLLER, USE_ARTIC_BASE_CONTROLLER,
SHADERS_ACCURATE_MUL SHADERS_ACCURATE_MUL,
) )
fun from(key: String): IntSetting? = IntSetting.values().firstOrNull { it.key == key } fun from(key: String): IntSetting? = IntSetting.values().firstOrNull { it.key == key }

View file

@ -1,4 +1,4 @@
// Copyright Citra Emulator Project / Azahar Emulator Project // Copyright 2023 Citra Emulator Project
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
@ -139,7 +139,6 @@ class Settings {
const val HOTKEY_PAUSE_OR_RESUME = "hotkey_pause_or_resume_game" const val HOTKEY_PAUSE_OR_RESUME = "hotkey_pause_or_resume_game"
const val HOTKEY_QUICKSAVE = "hotkey_quickload" const val HOTKEY_QUICKSAVE = "hotkey_quickload"
const val HOTKEY_QUICKlOAD = "hotkey_quickpause" const val HOTKEY_QUICKlOAD = "hotkey_quickpause"
const val HOTKEY_TURBO_LIMIT = "hotkey_turbo_limit"
val buttonKeys = listOf( val buttonKeys = listOf(
KEY_BUTTON_A, KEY_BUTTON_A,
@ -205,8 +204,7 @@ class Settings {
HOTKEY_CLOSE_GAME, HOTKEY_CLOSE_GAME,
HOTKEY_PAUSE_OR_RESUME, HOTKEY_PAUSE_OR_RESUME,
HOTKEY_QUICKSAVE, HOTKEY_QUICKSAVE,
HOTKEY_QUICKlOAD, HOTKEY_QUICKlOAD
HOTKEY_TURBO_LIMIT
) )
val hotkeyTitles = listOf( val hotkeyTitles = listOf(
R.string.emulation_swap_screens, R.string.emulation_swap_screens,
@ -215,7 +213,6 @@ class Settings {
R.string.emulation_toggle_pause, R.string.emulation_toggle_pause,
R.string.emulation_quicksave, R.string.emulation_quicksave,
R.string.emulation_quickload, R.string.emulation_quickload,
R.string.turbo_limit_hotkey
) )
const val PREF_FIRST_APP_LAUNCH = "FirstApplicationLaunch" const val PREF_FIRST_APP_LAUNCH = "FirstApplicationLaunch"

View file

@ -12,8 +12,7 @@ class DateTimeSetting(
titleId: Int, titleId: Int,
descriptionId: Int, descriptionId: Int,
val key: String? = null, val key: String? = null,
private val defaultValue: String? = null, private val defaultValue: String? = null
override var isEnabled: Boolean = true
) : SettingsItem(setting, titleId, descriptionId) { ) : SettingsItem(setting, titleId, descriptionId) {
override val type = TYPE_DATETIME_SETTING override val type = TYPE_DATETIME_SETTING

View file

@ -1,4 +1,4 @@
// Copyright Citra Emulator Project / Azahar Emulator Project // Copyright 2023 Citra Emulator Project
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
@ -133,7 +133,6 @@ class InputBindingSetting(
Settings.HOTKEY_PAUSE_OR_RESUME -> Hotkey.PAUSE_OR_RESUME.button Settings.HOTKEY_PAUSE_OR_RESUME -> Hotkey.PAUSE_OR_RESUME.button
Settings.HOTKEY_QUICKSAVE -> Hotkey.QUICKSAVE.button Settings.HOTKEY_QUICKSAVE -> Hotkey.QUICKSAVE.button
Settings.HOTKEY_QUICKlOAD -> Hotkey.QUICKLOAD.button Settings.HOTKEY_QUICKlOAD -> Hotkey.QUICKLOAD.button
Settings.HOTKEY_TURBO_LIMIT -> Hotkey.TURBO_LIMIT.button
else -> -1 else -> -1
} }

View file

@ -1,4 +1,4 @@
// Copyright Citra Emulator Project / Azahar Emulator Project // Copyright Citra Emulator Project / Lime3DS Emulator Project
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
@ -28,13 +28,6 @@ abstract class SettingsItem(
return setting?.isRuntimeEditable ?: false return setting?.isRuntimeEditable ?: false
} }
open var isEnabled: Boolean = true
val isActive: Boolean
get() {
return this.isEditable && this.isEnabled
}
companion object { companion object {
const val TYPE_HEADER = 0 const val TYPE_HEADER = 0
const val TYPE_SWITCH = 1 const val TYPE_SWITCH = 1

View file

@ -15,8 +15,7 @@ class SingleChoiceSetting(
val choicesId: Int, val choicesId: Int,
val valuesId: Int, val valuesId: Int,
val key: String? = null, val key: String? = null,
val defaultValue: Int? = null, val defaultValue: Int? = null
override var isEnabled: Boolean = true
) : SettingsItem(setting, titleId, descriptionId) { ) : SettingsItem(setting, titleId, descriptionId) {
override val type = TYPE_SINGLE_CHOICE override val type = TYPE_SINGLE_CHOICE

View file

@ -20,8 +20,7 @@ class SliderSetting(
val max: Int, val max: Int,
val units: String, val units: String,
val key: String? = null, val key: String? = null,
val defaultValue: Float? = null, val defaultValue: Float? = null
override var isEnabled: Boolean = true
) : SettingsItem(setting, titleId, descriptionId) { ) : SettingsItem(setting, titleId, descriptionId) {
override val type = TYPE_SLIDER override val type = TYPE_SLIDER
val selectedFloat: Float val selectedFloat: Float

View file

@ -12,8 +12,7 @@ class StringInputSetting(
titleId: Int, titleId: Int,
descriptionId: Int, descriptionId: Int,
val defaultValue: String, val defaultValue: String,
val characterLimit: Int = 0, val characterLimit: Int = 0
override var isEnabled: Boolean = true
) : SettingsItem(setting, titleId, descriptionId) { ) : SettingsItem(setting, titleId, descriptionId) {
override val type = TYPE_STRING_INPUT override val type = TYPE_STRING_INPUT

View file

@ -15,8 +15,7 @@ class StringSingleChoiceSetting(
val choices: Array<String>, val choices: Array<String>,
val values: Array<String>?, val values: Array<String>?,
val key: String? = null, val key: String? = null,
private val defaultValue: String? = null, private val defaultValue: String? = null
override var isEnabled: Boolean = true
) : SettingsItem(setting, titleId, descriptionId) { ) : SettingsItem(setting, titleId, descriptionId) {
override val type = TYPE_STRING_SINGLE_CHOICE override val type = TYPE_STRING_SINGLE_CHOICE

View file

@ -13,8 +13,7 @@ class SwitchSetting(
titleId: Int, titleId: Int,
descriptionId: Int, descriptionId: Int,
val key: String? = null, val key: String? = null,
val defaultValue: Any? = null, val defaultValue: Any? = null
override var isEnabled: Boolean = true
) : SettingsItem(setting, titleId, descriptionId) { ) : SettingsItem(setting, titleId, descriptionId) {
override val type = TYPE_SWITCH override val type = TYPE_SWITCH

View file

@ -12,7 +12,6 @@ import org.citra.citra_emu.features.settings.model.Settings
import org.citra.citra_emu.utils.SystemSaveGame import org.citra.citra_emu.utils.SystemSaveGame
import org.citra.citra_emu.utils.DirectoryInitialization import org.citra.citra_emu.utils.DirectoryInitialization
import org.citra.citra_emu.utils.Log import org.citra.citra_emu.utils.Log
import org.citra.citra_emu.utils.TurboHelper
class SettingsActivityPresenter(private val activityView: SettingsActivityView) { class SettingsActivityPresenter(private val activityView: SettingsActivityView) {
val settings: Settings get() = activityView.settings val settings: Settings get() = activityView.settings
@ -67,7 +66,6 @@ class SettingsActivityPresenter(private val activityView: SettingsActivityView)
//added to ensure that layout changes take effect as soon as settings window closes //added to ensure that layout changes take effect as soon as settings window closes
NativeLibrary.reloadSettings() NativeLibrary.reloadSettings()
NativeLibrary.updateFramebuffer(NativeLibrary.isPortraitMode) NativeLibrary.updateFramebuffer(NativeLibrary.isPortraitMode)
TurboHelper.reloadTurbo() // TODO: Can this go someone else? -OS
} }
NativeLibrary.reloadSettings() NativeLibrary.reloadSettings()
} }

View file

@ -7,6 +7,7 @@ package org.citra.citra_emu.features.settings.ui
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.content.DialogInterface import android.content.DialogInterface
import android.graphics.Color
import android.icu.util.Calendar import android.icu.util.Calendar
import android.icu.util.TimeZone import android.icu.util.TimeZone
import android.text.Editable import android.text.Editable
@ -16,11 +17,11 @@ import android.text.TextWatcher
import android.text.format.DateFormat import android.text.format.DateFormat
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.EditText
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.widget.doOnTextChanged import androidx.core.widget.doOnTextChanged
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.datepicker.MaterialDatePicker import com.google.android.material.datepicker.MaterialDatePicker
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
@ -65,13 +66,14 @@ import org.citra.citra_emu.features.settings.ui.viewholder.SwitchSettingViewHold
import org.citra.citra_emu.fragments.MessageDialogFragment import org.citra.citra_emu.fragments.MessageDialogFragment
import org.citra.citra_emu.fragments.MotionBottomSheetDialogFragment import org.citra.citra_emu.fragments.MotionBottomSheetDialogFragment
import org.citra.citra_emu.utils.SystemSaveGame import org.citra.citra_emu.utils.SystemSaveGame
import java.lang.IllegalStateException
import java.lang.NumberFormatException import java.lang.NumberFormatException
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import kotlin.math.roundToInt import kotlin.math.roundToInt
class SettingsAdapter( class SettingsAdapter(
private val fragmentView: SettingsFragmentView, private val fragmentView: SettingsFragmentView,
public val context: Context private val context: Context
) : RecyclerView.Adapter<SettingViewHolder?>(), DialogInterface.OnClickListener { ) : RecyclerView.Adapter<SettingViewHolder?>(), DialogInterface.OnClickListener {
private var settings: ArrayList<SettingsItem>? = null private var settings: ArrayList<SettingsItem>? = null
private var clickedItem: SettingsItem? = null private var clickedItem: SettingsItem? = null
@ -151,71 +153,15 @@ class SettingsAdapter(
return getItem(position)?.type ?: -1 return getItem(position)?.type ?: -1
} }
fun setSettingsList(newSettings: ArrayList<SettingsItem>?) { fun setSettingsList(settings: ArrayList<SettingsItem>?) {
if (settings == null) { this.settings = settings ?: arrayListOf()
settings = newSettings ?: arrayListOf() notifyDataSetChanged()
notifyDataSetChanged()
return
}
val oldSettings = settings
val diffResult = DiffUtil.calculateDiff(object : DiffUtil.Callback() {
override fun getOldListSize() = oldSettings?.size ?: 0
override fun getNewListSize() = newSettings?.size ?: 0
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val oldItem = oldSettings?.get(oldItemPosition)?.setting
val newItem = newSettings?.get(newItemPosition)?.setting
return oldItem?.key == newItem?.key
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val oldItem = oldSettings?.get(oldItemPosition)
val newItem = newSettings?.get(newItemPosition)
if (oldItem == null || newItem == null || oldItem.type != newItem.type) {
return false
}
return when (oldItem.type) {
SettingsItem.TYPE_SLIDER -> {
(oldItem as SliderSetting).isEnabled == (newItem as SliderSetting).isEnabled
}
SettingsItem.TYPE_SWITCH -> {
(oldItem as SwitchSetting).isEnabled == (newItem as SwitchSetting).isEnabled
}
SettingsItem.TYPE_SINGLE_CHOICE -> {
(oldItem as SingleChoiceSetting).isEnabled == (newItem as SingleChoiceSetting).isEnabled
}
SettingsItem.TYPE_DATETIME_SETTING -> {
(oldItem as DateTimeSetting).isEnabled == (newItem as DateTimeSetting).isEnabled
}
SettingsItem.TYPE_STRING_SINGLE_CHOICE -> {
(oldItem as StringSingleChoiceSetting).isEnabled == (newItem as StringSingleChoiceSetting).isEnabled
}
SettingsItem.TYPE_STRING_INPUT -> {
(oldItem as StringInputSetting).isEnabled == (newItem as StringInputSetting).isEnabled
}
else -> {
oldItem == newItem
}
}
}
})
settings = newSettings ?: arrayListOf()
diffResult.dispatchUpdatesTo(this)
} }
fun onBooleanClick(item: SwitchSetting, position: Int, checked: Boolean) { fun onBooleanClick(item: SwitchSetting, position: Int, checked: Boolean) {
val setting = item.setChecked(checked) val setting = item.setChecked(checked)
fragmentView.putSetting(setting) fragmentView.putSetting(setting)
fragmentView.onSettingChanged() fragmentView.onSettingChanged()
// If statement is required otherwise the app will crash on activity recreate ex. theme settings
if (fragmentView.activityView != null)
// Reload the settings list to update the UI
fragmentView.loadSettingsList()
} }
private fun onSingleChoiceClick(item: SingleChoiceSetting) { private fun onSingleChoiceClick(item: SingleChoiceSetting) {
@ -301,7 +247,6 @@ class SettingsAdapter(
notifyItemChanged(clickedPosition) notifyItemChanged(clickedPosition)
val setting = item.setSelectedValue(rtcString) val setting = item.setSelectedValue(rtcString)
fragmentView.putSetting(setting) fragmentView.putSetting(setting)
fragmentView.loadSettingsList()
clickedItem = null clickedItem = null
} }
datePicker.show( datePicker.show(
@ -457,7 +402,6 @@ class SettingsAdapter(
else -> throw IllegalStateException("Unrecognized type used for SingleChoiceSetting!") else -> throw IllegalStateException("Unrecognized type used for SingleChoiceSetting!")
} }
fragmentView?.putSetting(setting) fragmentView?.putSetting(setting)
fragmentView.loadSettingsList()
closeDialog() closeDialog()
} }
} }
@ -481,7 +425,6 @@ class SettingsAdapter(
} }
fragmentView?.putSetting(setting) fragmentView?.putSetting(setting)
fragmentView.loadSettingsList()
closeDialog() closeDialog()
} }
} }
@ -504,7 +447,6 @@ class SettingsAdapter(
fragmentView?.putSetting(setting) fragmentView?.putSetting(setting)
} }
} }
fragmentView.loadSettingsList()
closeDialog() closeDialog()
} }
} }
@ -517,7 +459,6 @@ class SettingsAdapter(
} }
val setting = it.setSelectedValue(textInputValue ?: "") val setting = it.setSelectedValue(textInputValue ?: "")
fragmentView?.putSetting(setting) fragmentView?.putSetting(setting)
fragmentView.loadSettingsList()
closeDialog() closeDialog()
} }
} }
@ -547,7 +488,6 @@ class SettingsAdapter(
} }
notifyItemChanged(position) notifyItemChanged(position)
fragmentView.onSettingChanged() fragmentView.onSettingChanged()
fragmentView.loadSettingsList()
} }
.setNegativeButton(android.R.string.cancel, null) .setNegativeButton(android.R.string.cancel, null)
.show() .show()
@ -555,19 +495,10 @@ class SettingsAdapter(
return true return true
} }
fun onClickDisabledSetting(isRuntimeDisabled: Boolean) { fun onClickDisabledSetting() {
val titleId = if (isRuntimeDisabled)
R.string.setting_not_editable
else
R.string.setting_disabled
val messageId = if (isRuntimeDisabled)
R.string.setting_not_editable_description
else
R.string.setting_disabled_description
MessageDialogFragment.newInstance( MessageDialogFragment.newInstance(
titleId, R.string.setting_not_editable,
messageId R.string.setting_not_editable_description
).show((fragmentView as SettingsFragment).childFragmentManager, MessageDialogFragment.TAG) ).show((fragmentView as SettingsFragment).childFragmentManager, MessageDialogFragment.TAG)
} }

View file

@ -13,7 +13,6 @@ import android.hardware.camera2.CameraManager
import android.os.Build import android.os.Build
import android.text.TextUtils import android.text.TextUtils
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlin.math.min import kotlin.math.min
import org.citra.citra_emu.CitraApplication import org.citra.citra_emu.CitraApplication
import org.citra.citra_emu.R import org.citra.citra_emu.R
@ -237,42 +236,6 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
IntSetting.FRAME_LIMIT.defaultValue.toFloat() IntSetting.FRAME_LIMIT.defaultValue.toFloat()
) )
) )
add(
SliderSetting(
IntSetting.TURBO_LIMIT,
R.string.turbo_limit,
R.string.turbo_limit_description,
100,
400,
"%",
IntSetting.TURBO_LIMIT.key,
IntSetting.TURBO_LIMIT.defaultValue.toFloat()
)
)
}
}
private var countryCompatibilityChanged = true
private fun checkCountryCompatibility() {
if (countryCompatibilityChanged) {
countryCompatibilityChanged = false
val compatFlags = SystemSaveGame.getCountryCompatibility(IntSetting.EMULATED_REGION.int)
if (compatFlags != 0) {
var message = ""
if (compatFlags and 1 != 0) {
message += settingsAdapter.context.getString(R.string.region_mismatch_emulated)
}
if (compatFlags and 2 != 0) {
if (message.isNotEmpty()) message += "\n\n"
message += settingsAdapter.context.getString(R.string.region_mismatch_console)
}
MaterialAlertDialogBuilder(settingsAdapter.context)
.setTitle(R.string.region_mismatch)
.setMessage(message)
.setPositiveButton(android.R.string.ok, null)
.show()
}
} }
} }
@ -319,45 +282,51 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
) )
) )
add(HeaderSetting(R.string.profile_settings)) add(HeaderSetting(R.string.profile_settings))
val regionSetting = object : AbstractIntSetting { add(
override var int: Int StringInputSetting(
get() { usernameSetting,
val ret = IntSetting.EMULATED_REGION.int R.string.username,
checkCountryCompatibility() 0,
return ret "AZAHAR",
} 10
set(value) { )
IntSetting.EMULATED_REGION.int = value )
countryCompatibilityChanged = true
checkCountryCompatibility()
}
override val key = IntSetting.EMULATED_REGION.key
override val section = null
override val isRuntimeEditable = false
override val valueAsString get() = int.toString()
override val defaultValue = IntSetting.EMULATED_REGION.defaultValue
}
add( add(
SingleChoiceSetting( SingleChoiceSetting(
regionSetting, IntSetting.EMULATED_REGION,
R.string.emulated_region, R.string.emulated_region,
0, 0,
R.array.regionNames, R.array.regionNames,
R.array.regionValues, R.array.regionValues,
IntSetting.EMULATED_REGION.key,
IntSetting.EMULATED_REGION.defaultValue
) )
) )
val systemLanguageSetting = object : AbstractIntSetting {
override var int: Int
get() = SystemSaveGame.getSystemLanguage()
set(value) = SystemSaveGame.setSystemLanguage(value)
override val key = null
override val section = null
override val isRuntimeEditable = false
override val valueAsString get() = int.toString()
override val defaultValue = 1
}
add(
SingleChoiceSetting(
systemLanguageSetting,
R.string.emulated_language,
0,
R.array.languageNames,
R.array.languageValues
)
)
val systemCountrySetting = object : AbstractShortSetting { val systemCountrySetting = object : AbstractShortSetting {
override var short: Short override var short: Short
get() { get() = SystemSaveGame.getCountryCode()
val ret = SystemSaveGame.getCountryCode() set(value) = SystemSaveGame.setCountryCode(value)
checkCountryCompatibility()
return ret;
}
set(value) {
SystemSaveGame.setCountryCode(value)
countryCompatibilityChanged = true
checkCountryCompatibility()
}
override val key = null override val key = null
override val section = null override val section = null
override val isRuntimeEditable = false override val isRuntimeEditable = false
@ -379,34 +348,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
countries.map { it.second }.toTypedArray() countries.map { it.second }.toTypedArray()
) )
) )
val systemLanguageSetting = object : AbstractIntSetting {
override var int: Int
get() = SystemSaveGame.getSystemLanguage()
set(value) = SystemSaveGame.setSystemLanguage(value)
override val key = null
override val section = null
override val isRuntimeEditable = false
override val valueAsString get() = int.toString()
override val defaultValue = 1
}
add(
SingleChoiceSetting(
systemLanguageSetting,
R.string.emulated_language,
0,
R.array.languageNames,
R.array.languageValues
)
)
add(
StringInputSetting(
usernameSetting,
R.string.username,
0,
"AZAHAR",
10
)
)
val playCoinSettings = object : AbstractIntSetting { val playCoinSettings = object : AbstractIntSetting {
override var int: Int override var int: Int
get() = SystemSaveGame.getPlayCoins() get() = SystemSaveGame.getPlayCoins()
@ -538,7 +480,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
DateTimeSetting( DateTimeSetting(
StringSetting.INIT_TIME, StringSetting.INIT_TIME,
R.string.simulated_clock, R.string.simulated_clock,
R.string.simulated_clock_description, R.string.init_time_description,
StringSetting.INIT_TIME.key, StringSetting.INIT_TIME.key,
StringSetting.INIT_TIME.defaultValue StringSetting.INIT_TIME.defaultValue
) )
@ -926,7 +868,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
R.string.factor3d, R.string.factor3d,
R.string.factor3d_description, R.string.factor3d_description,
0, 0,
255, 100,
"%", "%",
IntSetting.STEREOSCOPIC_3D_DEPTH.key, IntSetting.STEREOSCOPIC_3D_DEPTH.key,
IntSetting.STEREOSCOPIC_3D_DEPTH.defaultValue.toFloat() IntSetting.STEREOSCOPIC_3D_DEPTH.defaultValue.toFloat()
@ -1009,19 +951,6 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
) )
) )
add(HeaderSetting(R.string.advanced))
add(
SingleChoiceSetting(
IntSetting.TEXTURE_SAMPLING,
R.string.texture_sampling_name,
R.string.texture_sampling_description,
R.array.textureSamplingNames,
R.array.textureSamplingValues,
IntSetting.TEXTURE_SAMPLING.key,
IntSetting.TEXTURE_SAMPLING.defaultValue
)
)
// Disabled until custom texture implementation gets rewrite, current one overloads RAM // Disabled until custom texture implementation gets rewrite, current one overloads RAM
// and crashes Citra. // and crashes Citra.
// add( // add(
@ -1050,15 +979,6 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
IntSetting.ORIENTATION_OPTION.defaultValue IntSetting.ORIENTATION_OPTION.defaultValue
) )
) )
add(
SwitchSetting(
BooleanSetting.EXPAND_TO_CUTOUT_AREA,
R.string.expand_to_cutout_area,
R.string.expand_to_cutout_area_description,
BooleanSetting.EXPAND_TO_CUTOUT_AREA.key,
BooleanSetting.EXPAND_TO_CUTOUT_AREA.defaultValue
)
)
add( add(
SingleChoiceSetting( SingleChoiceSetting(
IntSetting.SCREEN_LAYOUT, IntSetting.SCREEN_LAYOUT,
@ -1092,18 +1012,6 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
IntSetting.SMALL_SCREEN_POSITION.defaultValue IntSetting.SMALL_SCREEN_POSITION.defaultValue
) )
) )
add(
SliderSetting(
IntSetting.SCREEN_GAP,
R.string.screen_gap,
R.string.screen_gap_description,
0,
480,
"px",
IntSetting.SCREEN_GAP.key,
IntSetting.SCREEN_GAP.defaultValue.toFloat()
)
)
add( add(
SliderSetting( SliderSetting(
FloatSetting.LARGE_SCREEN_PROPORTION, FloatSetting.LARGE_SCREEN_PROPORTION,

View file

@ -1,4 +1,4 @@
// Copyright Citra Emulator Project / Azahar Emulator Project // Copyright 2023 Citra Emulator Project
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
@ -47,7 +47,7 @@ class DateTimeViewHolder(val binding: ListItemSettingBinding, adapter: SettingsA
val dateFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM) val dateFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)
binding.textSettingValue.text = dateFormatter.format(zonedTime) binding.textSettingValue.text = dateFormatter.format(zonedTime)
if (setting.isActive) { if (setting.isEditable) {
binding.textSettingName.alpha = 1f binding.textSettingName.alpha = 1f
binding.textSettingDescription.alpha = 1f binding.textSettingDescription.alpha = 1f
binding.textSettingValue.alpha = 1f binding.textSettingValue.alpha = 1f
@ -59,18 +59,18 @@ class DateTimeViewHolder(val binding: ListItemSettingBinding, adapter: SettingsA
} }
override fun onClick(clicked: View) { override fun onClick(clicked: View) {
if (setting.isActive) { if (setting.isEditable) {
adapter.onDateTimeClick(setting, bindingAdapterPosition) adapter.onDateTimeClick(setting, bindingAdapterPosition)
} else { } else {
adapter.onClickDisabledSetting(!setting.isEditable) adapter.onClickDisabledSetting()
} }
} }
override fun onLongClick(clicked: View): Boolean { override fun onLongClick(clicked: View): Boolean {
if (setting.isActive) { if (setting.isEditable) {
return adapter.onLongClick(setting.setting!!, bindingAdapterPosition) return adapter.onLongClick(setting.setting!!, bindingAdapterPosition)
} else { } else {
adapter.onClickDisabledSetting(!setting.isEditable) adapter.onClickDisabledSetting()
} }
return false return false
} }

View file

@ -1,4 +1,4 @@
// Copyright Citra Emulator Project / Azahar Emulator Project // Copyright 2023 Citra Emulator Project
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
@ -45,7 +45,7 @@ class InputBindingSettingViewHolder(val binding: ListItemSettingBinding, adapter
if (setting.isEditable) { if (setting.isEditable) {
adapter.onInputBindingClick(setting, bindingAdapterPosition) adapter.onInputBindingClick(setting, bindingAdapterPosition)
} else { } else {
adapter.onClickDisabledSetting(!setting.isEditable) adapter.onClickDisabledSetting()
} }
} }
@ -53,7 +53,7 @@ class InputBindingSettingViewHolder(val binding: ListItemSettingBinding, adapter
if (setting.isEditable) { if (setting.isEditable) {
adapter.onLongClick(setting.setting!!, bindingAdapterPosition) adapter.onLongClick(setting.setting!!, bindingAdapterPosition)
} else { } else {
adapter.onClickDisabledSetting(!setting.isEditable) adapter.onClickDisabledSetting()
} }
return false return false
} }

View file

@ -1,4 +1,4 @@
// Copyright Citra Emulator Project / Azahar Emulator Project // Copyright 2023 Citra Emulator Project
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
@ -60,7 +60,7 @@ class RunnableViewHolder(val binding: ListItemSettingBinding, adapter: SettingsA
override fun onClick(clicked: View) { override fun onClick(clicked: View) {
if (!setting.isRuntimeRunnable && EmulationActivity.isRunning()) { if (!setting.isRuntimeRunnable && EmulationActivity.isRunning()) {
adapter.onClickDisabledSetting(true) adapter.onClickDisabledSetting()
} else { } else {
setting.runnable.invoke() setting.runnable.invoke()
} }

View file

@ -1,4 +1,4 @@
// Copyright Citra Emulator Project / Azahar Emulator Project // Copyright 2023 Citra Emulator Project
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
@ -27,7 +27,7 @@ class SingleChoiceViewHolder(val binding: ListItemSettingBinding, adapter: Setti
binding.textSettingValue.visibility = View.VISIBLE binding.textSettingValue.visibility = View.VISIBLE
binding.textSettingValue.text = getTextSetting() binding.textSettingValue.text = getTextSetting()
if (setting.isActive) { if (setting.isEditable) {
binding.textSettingName.alpha = 1f binding.textSettingName.alpha = 1f
binding.textSettingDescription.alpha = 1f binding.textSettingDescription.alpha = 1f
binding.textSettingValue.alpha = 1f binding.textSettingValue.alpha = 1f
@ -65,8 +65,8 @@ class SingleChoiceViewHolder(val binding: ListItemSettingBinding, adapter: Setti
} }
override fun onClick(clicked: View) { override fun onClick(clicked: View) {
if (!setting.isEditable || !setting.isEnabled) { if (!setting.isEditable) {
adapter.onClickDisabledSetting(!setting.isEditable) adapter.onClickDisabledSetting()
return return
} }
@ -84,10 +84,10 @@ class SingleChoiceViewHolder(val binding: ListItemSettingBinding, adapter: Setti
} }
override fun onLongClick(clicked: View): Boolean { override fun onLongClick(clicked: View): Boolean {
if (setting.isActive) { if (setting.isEditable) {
return adapter.onLongClick(setting.setting!!, bindingAdapterPosition) return adapter.onLongClick(setting.setting!!, bindingAdapterPosition)
} else { } else {
adapter.onClickDisabledSetting(!setting.isEditable) adapter.onClickDisabledSetting()
} }
return false return false
} }

View file

@ -1,4 +1,4 @@
// Copyright Citra Emulator Project / Azahar Emulator Project // Copyright 2023 Citra Emulator Project
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
@ -35,7 +35,7 @@ class SliderViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAda
else -> "${(setting.setting as AbstractIntSetting).int}${setting.units}" else -> "${(setting.setting as AbstractIntSetting).int}${setting.units}"
} }
if (setting.isActive) { if (setting.isEditable) {
binding.textSettingName.alpha = 1f binding.textSettingName.alpha = 1f
binding.textSettingDescription.alpha = 1f binding.textSettingDescription.alpha = 1f
binding.textSettingValue.alpha = 1f binding.textSettingValue.alpha = 1f
@ -47,18 +47,18 @@ class SliderViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAda
} }
override fun onClick(clicked: View) { override fun onClick(clicked: View) {
if (setting.isActive) { if (setting.isEditable) {
adapter.onSliderClick(setting, bindingAdapterPosition) adapter.onSliderClick(setting, bindingAdapterPosition)
} else { } else {
adapter.onClickDisabledSetting(!setting.isEditable) adapter.onClickDisabledSetting()
} }
} }
override fun onLongClick(clicked: View): Boolean { override fun onLongClick(clicked: View): Boolean {
if (setting.isActive) { if (setting.isEditable) {
return adapter.onLongClick(setting.setting!!, bindingAdapterPosition) return adapter.onLongClick(setting.setting!!, bindingAdapterPosition)
} else { } else {
adapter.onClickDisabledSetting(!setting.isEditable) adapter.onClickDisabledSetting()
} }
return false return false
} }

View file

@ -1,4 +1,4 @@
// Copyright Citra Emulator Project / Azahar Emulator Project // Copyright 2023 Citra Emulator Project
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
@ -25,31 +25,21 @@ class StringInputViewHolder(val binding: ListItemSettingBinding, adapter: Settin
} }
binding.textSettingValue.visibility = View.VISIBLE binding.textSettingValue.visibility = View.VISIBLE
binding.textSettingValue.text = setting.setting?.valueAsString binding.textSettingValue.text = setting.setting?.valueAsString
if (setting.isActive) {
binding.textSettingName.alpha = 1f
binding.textSettingDescription.alpha = 1f
binding.textSettingValue.alpha = 1f
} else {
binding.textSettingName.alpha = 0.5f
binding.textSettingDescription.alpha = 0.5f
binding.textSettingValue.alpha = 0.5f
}
} }
override fun onClick(clicked: View) { override fun onClick(clicked: View) {
if (!setting.isEditable || !setting.isEnabled) { if (!setting.isEditable) {
adapter.onClickDisabledSetting(!setting.isEditable) adapter.onClickDisabledSetting()
return return
} }
adapter.onStringInputClick((setting as StringInputSetting), bindingAdapterPosition) adapter.onStringInputClick((setting as StringInputSetting), bindingAdapterPosition)
} }
override fun onLongClick(clicked: View): Boolean { override fun onLongClick(clicked: View): Boolean {
if (setting.isActive) { if (setting.isEditable) {
return adapter.onLongClick(setting.setting!!, bindingAdapterPosition) return adapter.onLongClick(setting.setting!!, bindingAdapterPosition)
} else { } else {
adapter.onClickDisabledSetting(!setting.isEditable) adapter.onClickDisabledSetting()
} }
return false return false
} }

View file

@ -1,4 +1,4 @@
// Copyright Citra Emulator Project / Azahar Emulator Project // Copyright 2023 Citra Emulator Project
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
@ -33,26 +33,26 @@ class SwitchSettingViewHolder(val binding: ListItemSettingSwitchBinding, adapter
adapter.onBooleanClick(item, bindingAdapterPosition, binding.switchWidget.isChecked) adapter.onBooleanClick(item, bindingAdapterPosition, binding.switchWidget.isChecked)
} }
binding.switchWidget.isEnabled = setting.isActive binding.switchWidget.isEnabled = setting.isEditable
val textAlpha = if (setting.isActive) 1f else 0.5f val textAlpha = if (setting.isEditable) 1f else 0.5f
binding.textSettingName.alpha = textAlpha binding.textSettingName.alpha = textAlpha
binding.textSettingDescription.alpha = textAlpha binding.textSettingDescription.alpha = textAlpha
} }
override fun onClick(clicked: View) { override fun onClick(clicked: View) {
if (setting.isActive) { if (setting.isEditable) {
binding.switchWidget.toggle() binding.switchWidget.toggle()
} else { } else {
adapter.onClickDisabledSetting(!setting.isEditable) adapter.onClickDisabledSetting()
} }
} }
override fun onLongClick(clicked: View): Boolean { override fun onLongClick(clicked: View): Boolean {
if (setting.isActive) { if (setting.isEditable) {
return adapter.onLongClick(setting.setting!!, bindingAdapterPosition) return adapter.onLongClick(setting.setting!!, bindingAdapterPosition)
} else { } else {
adapter.onClickDisabledSetting(!setting.isEditable) adapter.onClickDisabledSetting()
} }
return false return false
} }

View file

@ -1,4 +1,4 @@
// Copyright Citra Emulator Project / Azahar Emulator Project // Copyright 2023 Citra Emulator Project
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
@ -22,7 +22,6 @@ import android.view.Surface
import android.view.SurfaceHolder import android.view.SurfaceHolder
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.ImageView
import android.widget.PopupMenu import android.widget.PopupMenu
import android.widget.TextView import android.widget.TextView
import android.widget.Toast import android.widget.Toast
@ -233,15 +232,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
) )
} }
binding.inGameMenu.getHeaderView(0).apply { binding.inGameMenu.getHeaderView(0).findViewById<TextView>(R.id.text_game_title).text =
val titleView = findViewById<TextView>(R.id.text_game_title) game.title
val iconView = findViewById<ImageView>(R.id.game_icon)
titleView.text = game.title
GameIconUtils.loadGameIcon(requireActivity(), game, iconView)
}
binding.inGameMenu.setNavigationItemSelectedListener { binding.inGameMenu.setNavigationItemSelectedListener {
when (it.itemId) { when (it.itemId) {
R.id.menu_emulation_pause -> { R.id.menu_emulation_pause -> {
@ -455,14 +447,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
Choreographer.getInstance().postFrameCallback(this) Choreographer.getInstance().postFrameCallback(this)
if (NativeLibrary.isRunning()) { if (NativeLibrary.isRunning()) {
NativeLibrary.unPauseEmulation() NativeLibrary.unPauseEmulation()
binding.inGameMenu.menu.findItem(R.id.menu_emulation_pause)?.let { menuItem ->
menuItem.title = resources.getString(R.string.pause_emulation)
menuItem.icon = ResourcesCompat.getDrawable(
resources,
R.drawable.ic_pause,
requireContext().theme
)
}
return return
} }
@ -549,6 +533,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
val slot = i val slot = i
var enableClick = isSaving var enableClick = isSaving
val text = if (slot == NativeLibrary.QUICKSAVE_SLOT) { val text = if (slot == NativeLibrary.QUICKSAVE_SLOT) {
enableClick = false
getString(R.string.emulation_quicksave_slot) getString(R.string.emulation_quicksave_slot)
} else { } else {
getString(R.string.emulation_empty_state_slot, slot) getString(R.string.emulation_empty_state_slot, slot)
@ -557,14 +542,11 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
add(text).setEnabled(enableClick).setOnMenuItemClickListener { add(text).setEnabled(enableClick).setOnMenuItemClickListener {
if(isSaving) { if(isSaving) {
NativeLibrary.saveState(slot) NativeLibrary.saveState(slot)
Toast.makeText(context,
getString(R.string.saving),
Toast.LENGTH_SHORT).show()
} else { } else {
NativeLibrary.loadState(slot) NativeLibrary.loadState(slot)
binding.drawerLayout.close() binding.drawerLayout.close()
Toast.makeText(context, Toast.makeText(context,
getString(R.string.loading), getString(R.string.quickload_loading),
Toast.LENGTH_SHORT).show() Toast.LENGTH_SHORT).show()
} }
true true
@ -575,6 +557,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
savestates?.forEach { savestates?.forEach {
var enableClick = true var enableClick = true
val text = if(it.slot == NativeLibrary.QUICKSAVE_SLOT) { val text = if(it.slot == NativeLibrary.QUICKSAVE_SLOT) {
// do not allow saving in quicksave slot
enableClick = !isSaving
getString(R.string.emulation_occupied_quicksave_slot, it.time) getString(R.string.emulation_occupied_quicksave_slot, it.time)
} else{ } else{
getString(R.string.emulation_occupied_state_slot, it.slot, it.time) getString(R.string.emulation_occupied_state_slot, it.slot, it.time)
@ -902,10 +886,10 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
val layoutOptionMenuItem = when (IntSetting.PORTRAIT_SCREEN_LAYOUT.int) { val layoutOptionMenuItem = when (IntSetting.PORTRAIT_SCREEN_LAYOUT.int) {
PortraitScreenLayout.TOP_FULL_WIDTH.int -> PortraitScreenLayout.TOP_FULL_WIDTH.int ->
R.id.menu_portrait_layout_top_full R.id.menu_portrait_layout_top_full
PortraitScreenLayout.ORIGINAL.int ->
R.id.menu_portrait_layout_original
PortraitScreenLayout.CUSTOM_PORTRAIT_LAYOUT.int -> PortraitScreenLayout.CUSTOM_PORTRAIT_LAYOUT.int ->
R.id.menu_portrait_layout_custom R.id.menu_portrait_layout_custom
else -> else ->
R.id.menu_portrait_layout_top_full R.id.menu_portrait_layout_top_full
@ -920,11 +904,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
true true
} }
R.id.menu_portrait_layout_original -> {
screenAdjustmentUtil.changePortraitOrientation(PortraitScreenLayout.ORIGINAL.int)
true
}
R.id.menu_portrait_layout_custom -> { R.id.menu_portrait_layout_custom -> {
Toast.makeText( Toast.makeText(
requireContext(), requireContext(),
@ -954,12 +933,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
private fun showToggleControlsDialog() { private fun showToggleControlsDialog() {
val editor = preferences.edit() val editor = preferences.edit()
val enabledButtons = BooleanArray(16) val enabledButtons = BooleanArray(15)
enabledButtons.forEachIndexed { i: Int, _: Boolean -> enabledButtons.forEachIndexed { i: Int, _: Boolean ->
// Buttons that are disabled by default // Buttons that are disabled by default
var defaultValue = true var defaultValue = true
when (i) { when (i) {
6, 7, 12, 13, 14, 15 -> defaultValue = false 6, 7, 12, 13, 14 -> defaultValue = false
} }
enabledButtons[i] = preferences.getBoolean("buttonToggle$i", defaultValue) enabledButtons[i] = preferences.getBoolean("buttonToggle$i", defaultValue)
} }
@ -1137,10 +1116,10 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
.apply() .apply()
val editor = preferences.edit() val editor = preferences.edit()
for (i in 0 until 16) { for (i in 0 until 15) {
var defaultValue = true var defaultValue = true
when (i) { when (i) {
6, 7, 12, 13, 14, 15 -> defaultValue = false 6, 7, 12, 13, 14 -> defaultValue = false
} }
editor.putBoolean("buttonToggle$i", defaultValue) editor.putBoolean("buttonToggle$i", defaultValue)
} }

View file

@ -1,4 +1,4 @@
// Copyright Citra Emulator Project / Azahar Emulator Project // Copyright 2023 Citra Emulator Project
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
@ -22,10 +22,9 @@ class SelectUserDirectoryDialogFragment : DialogFragment() {
mainActivity = requireActivity() as MainActivity mainActivity = requireActivity() as MainActivity
isCancelable = false isCancelable = false
return MaterialAlertDialogBuilder(requireContext()) return MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.select_citra_user_folder) .setTitle(R.string.select_citra_user_folder)
.setMessage(R.string.selecting_user_directory_without_write_permissions) .setMessage(R.string.cannot_skip_directory_description)
.setPositiveButton(android.R.string.ok) { _: DialogInterface, _: Int -> .setPositiveButton(android.R.string.ok) { _: DialogInterface, _: Int ->
mainActivity?.openCitraDirectory?.launch(null) mainActivity?.openCitraDirectory?.launch(null)
} }

View file

@ -4,7 +4,6 @@
package org.citra.citra_emu.fragments package org.citra.citra_emu.fragments
import android.content.DialogInterface
import android.os.Bundle import android.os.Bundle
import android.text.method.LinkMovementMethod import android.text.method.LinkMovementMethod
import android.view.Gravity import android.view.Gravity
@ -158,22 +157,6 @@ class SystemFilesFragment : Fragment() {
movementMethod = LinkMovementMethod.getInstance() movementMethod = LinkMovementMethod.getInstance()
} }
binding.buttonUnlinkConsoleData.isEnabled = NativeLibrary.isFullConsoleLinked()
binding.buttonUnlinkConsoleData.setOnClickListener {
MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.delete_system_files)
.setMessage(HtmlCompat.fromHtml(
requireContext().getString(R.string.delete_system_files_description),
HtmlCompat.FROM_HTML_MODE_COMPACT
))
.setPositiveButton(android.R.string.ok) { _: DialogInterface, _: Int ->
NativeLibrary.unlinkConsole()
binding.buttonUnlinkConsoleData.isEnabled = NativeLibrary.isFullConsoleLinked()
}
.setNegativeButton(android.R.string.cancel, null)
.show()
}
binding.buttonSetUpSystemFiles.setOnClickListener { binding.buttonSetUpSystemFiles.setOnClickListener {
val inflater = LayoutInflater.from(context) val inflater = LayoutInflater.from(context)
val inputBinding = DialogSoftwareKeyboardBinding.inflate(inflater) val inputBinding = DialogSoftwareKeyboardBinding.inflate(inflater)

View file

@ -1,113 +0,0 @@
// 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.fragments
import android.app.Dialog
import android.content.DialogInterface
import android.content.SharedPreferences
import android.net.Uri
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.widget.LinearLayout
import android.widget.RadioButton
import android.widget.RadioGroup
import android.widget.TextView
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.ViewModelProvider
import androidx.preference.PreferenceManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.citra.citra_emu.CitraApplication
import org.citra.citra_emu.R
import org.citra.citra_emu.ui.main.MainActivity
import org.citra.citra_emu.utils.CitraDirectoryUtils
import org.citra.citra_emu.utils.DirectoryInitialization
import org.citra.citra_emu.utils.PermissionsHandler
import org.citra.citra_emu.viewmodel.HomeViewModel
class UpdateUserDirectoryDialogFragment : DialogFragment() {
private lateinit var mainActivity: MainActivity
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
mainActivity = requireActivity() as MainActivity
isCancelable = false
val preferences: SharedPreferences =
PreferenceManager.getDefaultSharedPreferences(CitraApplication.appContext)
val ld = preferences.getString("LIME3DS_DIRECTORY","")
val cd = preferences.getString("CITRA_DIRECTORY","")
val dialogView = LayoutInflater.from(requireContext())
.inflate(R.layout.dialog_select_which_directory, null)
val radioGroup = dialogView.findViewById<RadioGroup>(R.id.radioGroup)
val choices = listOf(
getString(R.string.keep_current_azahar_directory) to Uri.parse(cd).path,
getString(R.string.use_prior_lime3ds_directory) to Uri.parse(ld).path
)
var selected = -1 // 0 = current, 1 = prior, -1 = no selection
choices.forEachIndexed { index, (label, subtext) ->
val container = LinearLayout(requireContext()).apply {
orientation = LinearLayout.VERTICAL
setPadding(0, 16, 0, 16)
}
val radioButton = RadioButton(requireContext()).apply {
text = label
id = View.generateViewId()
}
val subTextView = TextView(requireContext()).apply {
text = subtext
setPadding(64, 4, 0, 0) // indent for visual hierarchy
setTextAppearance(android.R.style.TextAppearance_Small)
}
container.addView(radioButton)
container.addView(subTextView)
radioGroup.addView(container)
// RadioGroup expects RadioButtons directly, so we need to manage selection ourselves
radioButton.setOnClickListener {
selected = index
// Manually uncheck others
for (i in 0 until radioGroup.childCount) {
val child = radioGroup.getChildAt(i) as LinearLayout
val rb = child.getChildAt(0) as RadioButton
rb.isChecked = i == index
}
}
}
return MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.select_citra_user_folder)
.setView(dialogView)
.setPositiveButton(android.R.string.ok) { _: DialogInterface, _: Int ->
if (selected == 1) {
PermissionsHandler.setCitraDirectory(ld)
}
if (selected >= 0) {
CitraDirectoryUtils.removeLimeDirectoryPreference()
DirectoryInitialization.resetCitraDirectoryState()
DirectoryInitialization.start()
}
ViewModelProvider(mainActivity)[HomeViewModel::class.java].setPickingUserDir(false)
ViewModelProvider(mainActivity)[HomeViewModel::class.java].setUserDir(this.requireActivity(),PermissionsHandler.citraDirectory.path!!)
}
.show()
}
companion object {
const val TAG = "UpdateUserDirectoryDialogFragment"
fun newInstance(activity: FragmentActivity): UpdateUserDirectoryDialogFragment {
ViewModelProvider(activity)[HomeViewModel::class.java].setPickingUserDir(true)
return UpdateUserDirectoryDialogFragment()
}
}
}

View file

@ -1,4 +1,4 @@
// Copyright Citra Emulator Project / Azahar Emulator Project // Copyright 2023 Citra Emulator Project
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
@ -25,7 +25,6 @@ import org.citra.citra_emu.CitraApplication
import org.citra.citra_emu.NativeLibrary import org.citra.citra_emu.NativeLibrary
import org.citra.citra_emu.R import org.citra.citra_emu.R
import org.citra.citra_emu.utils.EmulationMenuSettings import org.citra.citra_emu.utils.EmulationMenuSettings
import org.citra.citra_emu.utils.TurboHelper
import java.lang.NullPointerException import java.lang.NullPointerException
import kotlin.math.min import kotlin.math.min
@ -45,7 +44,6 @@ class InputOverlay(context: Context?, attrs: AttributeSet?) : SurfaceView(contex
private var buttonBeingConfigured: InputOverlayDrawableButton? = null private var buttonBeingConfigured: InputOverlayDrawableButton? = null
private var dpadBeingConfigured: InputOverlayDrawableDpad? = null private var dpadBeingConfigured: InputOverlayDrawableDpad? = null
private var joystickBeingConfigured: InputOverlayDrawableJoystick? = null private var joystickBeingConfigured: InputOverlayDrawableJoystick? = null
private val settingsViewModel = NativeLibrary.sEmulationActivity.get()!!.settingsViewModel
// Stores the ID of the pointer that interacted with the 3DS touchscreen. // Stores the ID of the pointer that interacted with the 3DS touchscreen.
private var touchscreenPointerId = -1 private var touchscreenPointerId = -1
@ -106,10 +104,6 @@ class InputOverlay(context: Context?, attrs: AttributeSet?) : SurfaceView(contex
swapScreen() swapScreen()
} }
if (button.id == NativeLibrary.ButtonType.BUTTON_TURBO && button.status == NativeLibrary.ButtonState.PRESSED) {
TurboHelper.setTurboEnabled((!TurboHelper.isTurboSpeedEnabled()))
}
NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, button.id, button.status) NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, button.id, button.status)
shouldUpdateView = true shouldUpdateView = true
} }
@ -474,18 +468,6 @@ class InputOverlay(context: Context?, attrs: AttributeSet?) : SurfaceView(contex
) )
) )
} }
if (preferences.getBoolean("buttonToggle15", false)) {
overlayButtons.add(
initializeOverlayButton(
context,
R.drawable.button_turbo,
R.drawable.button_turbo_pressed,
NativeLibrary.ButtonType.BUTTON_TURBO,
orientation
)
)
}
} }
fun refreshControls() { fun refreshControls() {
@ -691,14 +673,6 @@ class InputOverlay(context: Context?, attrs: AttributeSet?) : SurfaceView(contex
NativeLibrary.ButtonType.BUTTON_SWAP.toString() + "-Y", NativeLibrary.ButtonType.BUTTON_SWAP.toString() + "-Y",
resources.getInteger(R.integer.N3DS_BUTTON_SWAP_Y).toFloat() / 1000 * maxY resources.getInteger(R.integer.N3DS_BUTTON_SWAP_Y).toFloat() / 1000 * maxY
) )
.putFloat(
NativeLibrary.ButtonType.BUTTON_TURBO.toString() + "-X",
resources.getInteger(R.integer.N3DS_BUTTON_TURBO_X).toFloat() / 1000 * maxX
)
.putFloat(
NativeLibrary.ButtonType.BUTTON_TURBO.toString() + "-Y",
resources.getInteger(R.integer.N3DS_BUTTON_TURBO_Y).toFloat() / 1000 * maxY
)
.apply() .apply()
} }
@ -842,14 +816,6 @@ class InputOverlay(context: Context?, attrs: AttributeSet?) : SurfaceView(contex
NativeLibrary.ButtonType.BUTTON_SWAP.toString() + portrait + "-Y", NativeLibrary.ButtonType.BUTTON_SWAP.toString() + portrait + "-Y",
resources.getInteger(R.integer.N3DS_BUTTON_SWAP_PORTRAIT_Y).toFloat() / 1000 * maxY resources.getInteger(R.integer.N3DS_BUTTON_SWAP_PORTRAIT_Y).toFloat() / 1000 * maxY
) )
.putFloat(
NativeLibrary.ButtonType.BUTTON_TURBO.toString() + portrait + "-X",
resources.getInteger(R.integer.N3DS_BUTTON_TURBO_PORTRAIT_X).toFloat() / 1000 * maxX
)
.putFloat(
NativeLibrary.ButtonType.BUTTON_TURBO.toString() + portrait + "-Y",
resources.getInteger(R.integer.N3DS_BUTTON_TURBO_PORTRAIT_Y).toFloat() / 1000 * maxY
)
.apply() .apply()
} }
@ -962,7 +928,6 @@ class InputOverlay(context: Context?, attrs: AttributeSet?) : SurfaceView(contex
NativeLibrary.ButtonType.BUTTON_START, NativeLibrary.ButtonType.BUTTON_START,
NativeLibrary.ButtonType.BUTTON_SELECT, NativeLibrary.ButtonType.BUTTON_SELECT,
NativeLibrary.ButtonType.BUTTON_SWAP -> 0.08f NativeLibrary.ButtonType.BUTTON_SWAP -> 0.08f
NativeLibrary.ButtonType.BUTTON_TURBO -> 0.10f
NativeLibrary.ButtonType.TRIGGER_L, NativeLibrary.ButtonType.TRIGGER_L,
NativeLibrary.ButtonType.TRIGGER_R, NativeLibrary.ButtonType.TRIGGER_R,

View file

@ -1,4 +1,4 @@
// Copyright Citra Emulator Project / Azahar Emulator Project // Copyright Citra Emulator Project / Lime3DS Emulator Project
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
@ -34,8 +34,10 @@ import androidx.work.OutOfQuotaPolicy
import androidx.work.WorkManager import androidx.work.WorkManager
import com.google.android.material.color.MaterialColors import com.google.android.material.color.MaterialColors
import com.google.android.material.navigation.NavigationBarView import com.google.android.material.navigation.NavigationBarView
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.citra.citra_emu.R import org.citra.citra_emu.R
import org.citra.citra_emu.activities.EmulationActivity
import org.citra.citra_emu.contracts.OpenFileResultContract import org.citra.citra_emu.contracts.OpenFileResultContract
import org.citra.citra_emu.databinding.ActivityMainBinding import org.citra.citra_emu.databinding.ActivityMainBinding
import org.citra.citra_emu.features.settings.model.Settings import org.citra.citra_emu.features.settings.model.Settings
@ -43,10 +45,8 @@ import org.citra.citra_emu.features.settings.model.SettingsViewModel
import org.citra.citra_emu.features.settings.ui.SettingsActivity import org.citra.citra_emu.features.settings.ui.SettingsActivity
import org.citra.citra_emu.features.settings.utils.SettingsFile import org.citra.citra_emu.features.settings.utils.SettingsFile
import org.citra.citra_emu.fragments.SelectUserDirectoryDialogFragment import org.citra.citra_emu.fragments.SelectUserDirectoryDialogFragment
import org.citra.citra_emu.fragments.UpdateUserDirectoryDialogFragment
import org.citra.citra_emu.utils.CiaInstallWorker import org.citra.citra_emu.utils.CiaInstallWorker
import org.citra.citra_emu.utils.CitraDirectoryHelper import org.citra.citra_emu.utils.CitraDirectoryHelper
import org.citra.citra_emu.utils.CitraDirectoryUtils
import org.citra.citra_emu.utils.DirectoryInitialization import org.citra.citra_emu.utils.DirectoryInitialization
import org.citra.citra_emu.utils.FileBrowserHelper import org.citra.citra_emu.utils.FileBrowserHelper
import org.citra.citra_emu.utils.InsetsHelper import org.citra.citra_emu.utils.InsetsHelper
@ -66,17 +66,13 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
val splashScreen = installSplashScreen() val splashScreen = installSplashScreen()
CitraDirectoryUtils.attemptAutomaticUpdateDirectory()
splashScreen.setKeepOnScreenCondition { splashScreen.setKeepOnScreenCondition {
!DirectoryInitialization.areCitraDirectoriesReady() && !DirectoryInitialization.areCitraDirectoriesReady() &&
PermissionsHandler.hasWriteAccess(this) && PermissionsHandler.hasWriteAccess(this)
!CitraDirectoryUtils.needToUpdateManually()
} }
if (PermissionsHandler.hasWriteAccess(applicationContext) && if (PermissionsHandler.hasWriteAccess(applicationContext) &&
DirectoryInitialization.areCitraDirectoriesReady() && DirectoryInitialization.areCitraDirectoriesReady()) {
!CitraDirectoryUtils.needToUpdateManually()) {
settingsViewModel.settings.loadSettings() settingsViewModel.settings.loadSettings()
} }
@ -189,9 +185,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
) { ) {
SelectUserDirectoryDialogFragment.newInstance(this) SelectUserDirectoryDialogFragment.newInstance(this)
.show(supportFragmentManager, SelectUserDirectoryDialogFragment.TAG) .show(supportFragmentManager, SelectUserDirectoryDialogFragment.TAG)
} else if (!firstTimeSetup && !homeViewModel.isPickingUserDir.value && CitraDirectoryUtils.needToUpdateManually()) {
UpdateUserDirectoryDialogFragment.newInstance(this)
.show(supportFragmentManager,UpdateUserDirectoryDialogFragment.TAG)
} }
} }

View file

@ -1,46 +0,0 @@
// 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.content.SharedPreferences
import androidx.preference.PreferenceManager
import org.citra.citra_emu.CitraApplication
object CitraDirectoryUtils {
const val CITRA_DIRECTORY = "CITRA_DIRECTORY"
const val LIME3DS_DIRECTORY = "LIME3DS_DIRECTORY"
val preferences: SharedPreferences =
PreferenceManager.getDefaultSharedPreferences(CitraApplication.appContext)
fun needToUpdateManually(): Boolean {
val directoryString = preferences.getString(CITRA_DIRECTORY, "")
val limeDirectoryString = preferences.getString(LIME3DS_DIRECTORY,"")
return (directoryString != "" && limeDirectoryString != "" && directoryString != limeDirectoryString)
}
fun attemptAutomaticUpdateDirectory() {
val directoryString = preferences.getString(CITRA_DIRECTORY, "")
val limeDirectoryString = preferences.getString(LIME3DS_DIRECTORY,"")
if (needToUpdateManually()) {
return;
}
if (directoryString == "" && limeDirectoryString != "") {
// Upgrade from Lime3DS to Azahar
PermissionsHandler.setCitraDirectory(limeDirectoryString)
removeLimeDirectoryPreference()
DirectoryInitialization.resetCitraDirectoryState()
DirectoryInitialization.start()
} else if (directoryString != "" && directoryString == limeDirectoryString) {
// Both the Lime3DS and Azahar directories are the same,
// so delete the obsolete Lime3DS value.
removeLimeDirectoryPreference()
}
}
fun removeLimeDirectoryPreference() {
preferences.edit().remove(LIME3DS_DIRECTORY).apply()
}
}

View file

@ -106,40 +106,6 @@ class DocumentsTree {
return node.uri ?: return Uri.EMPTY return node.uri ?: return Uri.EMPTY
} }
@Synchronized
fun folderUriHelper(path: String, createIfNotExists: Boolean = false): Uri? {
root ?: return null
val components = path.split(DELIMITER).filter { it.isNotEmpty() }
var current = root
for (component in components) {
if (!current!!.loaded) {
structTree(current)
}
var child = current.findChild(component)
// Create directory if it doesn't exist and creation is enabled
if (child == null && createIfNotExists) {
try {
val createdDir = FileUtil.createDir(current.uri.toString(), component) ?: return null
child = DocumentsNode(createdDir, true).apply {
parent = current
}
current.addChild(child)
} catch (e: Exception) {
error("[DocumentsTree]: Cannot create directory, error: " + e.message)
return null
}
} else if (child == null) {
return null
}
current = child
}
return current?.uri
}
@Synchronized @Synchronized
fun isDirectory(filepath: String): Boolean { fun isDirectory(filepath: String): Boolean {
val node = resolvePath(filepath) ?: return false val node = resolvePath(filepath) ?: return false

View file

@ -48,8 +48,6 @@ object SystemSaveGame {
external fun getMac(): String external fun getMac(): String
external fun regenerateMac() external fun regenerateMac()
external fun getCountryCompatibility(region: Int): Int
} }
enum class BirthdayMonth(val code: Int, val days: Int) { enum class BirthdayMonth(val code: Int, val days: Int) {

View file

@ -1,39 +0,0 @@
// 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.widget.Toast
import org.citra.citra_emu.CitraApplication
import org.citra.citra_emu.NativeLibrary
import org.citra.citra_emu.R
import org.citra.citra_emu.features.settings.model.IntSetting
object TurboHelper {
private var turboSpeedEnabled = false
fun isTurboSpeedEnabled(): Boolean {
return turboSpeedEnabled
}
fun setTurboEnabled(state: Boolean) {
turboSpeedEnabled = state
reloadTurbo()
}
fun reloadTurbo() {
val context = CitraApplication.appContext
val toastMessage: String
if (turboSpeedEnabled) {
NativeLibrary.setTemporaryFrameLimit(IntSetting.TURBO_LIMIT.int.toDouble())
toastMessage = context.getString(R.string.turbo_enabled_toast)
} else {
NativeLibrary.disableTemporaryFrameLimit()
toastMessage = context.getString(R.string.turbo_disabled_toast)
}
Toast.makeText(context, toastMessage, Toast.LENGTH_SHORT).show()
}
}

View file

@ -149,8 +149,8 @@ void Config::ReadValues() {
ReadSetting("Renderer", Settings::values.use_vsync_new); ReadSetting("Renderer", Settings::values.use_vsync_new);
ReadSetting("Renderer", Settings::values.texture_filter); ReadSetting("Renderer", Settings::values.texture_filter);
ReadSetting("Renderer", Settings::values.texture_sampling); ReadSetting("Renderer", Settings::values.texture_sampling);
ReadSetting("Renderer", Settings::values.turbo_limit);
// Workaround to map Android setting for enabling the frame limiter to the format Citra expects // Work around to map Android setting for enabling the frame limiter to the format Citra expects
if (sdl2_config->GetBoolean("Renderer", "use_frame_limit", true)) { if (sdl2_config->GetBoolean("Renderer", "use_frame_limit", true)) {
ReadSetting("Renderer", Settings::values.frame_limit); ReadSetting("Renderer", Settings::values.frame_limit);
} else { } else {
@ -183,13 +183,11 @@ void Config::ReadValues() {
layoutInt = static_cast<int>(Settings::LayoutOption::LargeScreen); layoutInt = static_cast<int>(Settings::LayoutOption::LargeScreen);
} }
Settings::values.layout_option = static_cast<Settings::LayoutOption>(layoutInt); Settings::values.layout_option = static_cast<Settings::LayoutOption>(layoutInt);
Settings::values.screen_gap = static_cast<int>(sdl2_config->GetReal("Layout", "screen_gap", 0));
Settings::values.large_screen_proportion = Settings::values.large_screen_proportion =
static_cast<float>(sdl2_config->GetReal("Layout", "large_screen_proportion", 2.25)); static_cast<float>(sdl2_config->GetReal("Layout", "large_screen_proportion", 2.25));
Settings::values.small_screen_position = static_cast<Settings::SmallScreenPosition>( Settings::values.small_screen_position = static_cast<Settings::SmallScreenPosition>(
sdl2_config->GetInteger("Layout", "small_screen_position", sdl2_config->GetInteger("Layout", "small_screen_position",
static_cast<int>(Settings::SmallScreenPosition::TopRight))); static_cast<int>(Settings::SmallScreenPosition::TopRight)));
ReadSetting("Layout", Settings::values.screen_gap);
ReadSetting("Layout", Settings::values.custom_top_x); ReadSetting("Layout", Settings::values.custom_top_x);
ReadSetting("Layout", Settings::values.custom_top_y); ReadSetting("Layout", Settings::values.custom_top_y);
ReadSetting("Layout", Settings::values.custom_top_width); ReadSetting("Layout", Settings::values.custom_top_width);

View file

@ -125,11 +125,6 @@ shaders_accurate_mul =
# 0: Interpreter (slow), 1 (default): JIT (fast) # 0: Interpreter (slow), 1 (default): JIT (fast)
use_shader_jit = use_shader_jit =
# Overrides the sampling filter used by games. This can be useful in certain
# cases with poorly behaved games when upscaling.
# 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 # 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. # so only turn this off if you notice a speed difference.
# 0: Off, 1 (default): On # 0: Off, 1 (default): On
@ -156,10 +151,6 @@ use_frame_limit =
# 1 - 9999: Speed limit as a percentage of target game speed. 100 (default) # 1 - 9999: Speed limit as a percentage of target game speed. 100 (default)
frame_limit = frame_limit =
# Alternative frame limit which can be triggered by the user
# 1 - 9999: Speed limit as a percentage of target game speed. 100 (default)
turbo_limit =
# The clear color for the renderer. What shows up on the sides of the bottom screen. # The clear color for the renderer. What shows up on the sides of the bottom screen.
# Must be in range of 0.0-1.0. Defaults to 0.0 for all. # Must be in range of 0.0-1.0. Defaults to 0.0 for all.
bg_red = bg_red =
@ -171,7 +162,7 @@ bg_green =
render_3d = render_3d =
# Change 3D Intensity # Change 3D Intensity
# 0 - 255: Intensity. 0 (default) # 0 - 100: Intensity. 0 (default)
factor_3d = factor_3d =
# The name of the post processing shader to apply. # The name of the post processing shader to apply.
@ -206,12 +197,6 @@ disable_right_eye_render =
# 5: Custom Layout # 5: Custom Layout
layout_option = layout_option =
# Screen Gap - adds a gap between screens in all two-screen modes
# Measured in pixels relative to the 240px default height of the screens
# Scales with the larger screen (so 24 is 10% of the larger screen height)
# Default value is 0.0
screen_gap =
# Large Screen Proportion - Relative size of large:small in large screen mode # Large Screen Proportion - Relative size of large:small in large screen mode
# Default value is 2.25 # Default value is 2.25
large_screen_proportion = large_screen_proportion =
@ -266,10 +251,6 @@ custom_portrait_bottom_height =
# 0 (default): Top Screen is prominent, 1: Bottom Screen is prominent # 0 (default): Top Screen is prominent, 1: Bottom Screen is prominent
swap_screen = swap_screen =
# Expands the display area to include the cutout (or notch) area
# 0 (default): Off, 1: On
expand_to_cutout_area =
# Screen placement settings when using Cardboard VR (render3d = 4) # Screen placement settings when using Cardboard VR (render3d = 4)
# 30 - 100: Screen size as a percentage of the viewport. 85 (default) # 30 - 100: Screen size as a percentage of the viewport. 85 (default)
cardboard_screen_size = cardboard_screen_size =

View file

@ -36,7 +36,6 @@
#include "core/frontend/camera/factory.h" #include "core/frontend/camera/factory.h"
#include "core/hle/service/am/am.h" #include "core/hle/service/am/am.h"
#include "core/hle/service/nfc/nfc.h" #include "core/hle/service/nfc/nfc.h"
#include "core/hw/unique_data.h"
#include "core/loader/loader.h" #include "core/loader/loader.h"
#include "core/savestate.h" #include "core/savestate.h"
#include "core/system_titles.h" #include "core/system_titles.h"
@ -215,8 +214,7 @@ static Core::System::ResultStatus RunCitra(const std::string& filepath) {
LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0);
std::unique_ptr<Frontend::GraphicsContext> cpu_context; std::unique_ptr<Frontend::GraphicsContext> cpu_context;
system.GPU().Renderer().Rasterizer()->LoadDefaultDiskResources(stop_run, system.GPU().Renderer().Rasterizer()->LoadDiskResources(stop_run, &LoadDiskCacheProgress);
&LoadDiskCacheProgress);
LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Complete, 0, 0); LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Complete, 0, 0);
@ -774,22 +772,4 @@ void Java_org_citra_citra_1emu_NativeLibrary_logDeviceInfo([[maybe_unused]] JNIE
LOG_INFO(Frontend, "Host OS: Android API level {}", android_get_device_api_level()); LOG_INFO(Frontend, "Host OS: Android API level {}", android_get_device_api_level());
} }
jboolean Java_org_citra_citra_1emu_NativeLibrary_isFullConsoleLinked(JNIEnv* env, jobject obj) {
return HW::UniqueData::IsFullConsoleLinked();
}
void Java_org_citra_citra_1emu_NativeLibrary_unlinkConsole(JNIEnv* env, jobject obj) {
HW::UniqueData::UnlinkConsole();
}
void Java_org_citra_citra_1emu_NativeLibrary_setTemporaryFrameLimit(JNIEnv* env, jobject obj,
jdouble speed) {
Settings::temporary_frame_limit = speed;
Settings::is_temporary_frame_limit = true;
}
void Java_org_citra_citra_1emu_NativeLibrary_disableTemporaryFrameLimit(JNIEnv* env, jobject obj) {
Settings::is_temporary_frame_limit = false;
}
} // extern "C" } // extern "C"

View file

@ -6,7 +6,6 @@
#include <core/core.h> #include <core/core.h>
#include <core/hle/service/cfg/cfg.h> #include <core/hle/service/cfg/cfg.h>
#include <core/hle/service/ptm/ptm.h> #include <core/hle/service/ptm/ptm.h>
#include <core/hw/unique_data.h>
#include "android_common/android_common.h" #include "android_common/android_common.h"
static bool changes_pending = false; static bool changes_pending = false;
@ -139,21 +138,4 @@ void Java_org_citra_citra_1emu_utils_SystemSaveGame_regenerateMac(JNIEnv* env,
cfg->SaveMacAddress(); cfg->SaveMacAddress();
} }
jint Java_org_citra_citra_1emu_utils_SystemSaveGame_getCountryCompatibility(
JNIEnv* env, [[maybe_unused]] jobject obj, jint region) {
int res = 0;
u8 country = cfg->GetCountryCode();
if (region != Settings::REGION_VALUE_AUTO_SELECT &&
!Service::CFG::Module::IsValidRegionCountry(static_cast<u32>(region), country)) {
res |= 1;
}
if (HW::UniqueData::GetSecureInfoA().IsValid()) {
region = static_cast<jint>(cfg->GetRegionValue(true));
if (!Service::CFG::Module::IsValidRegionCountry(static_cast<u32>(region), country)) {
res |= 2;
}
}
return res;
}
} // extern "C" } // extern "C"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

View file

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="?attr/colorControlNormal"
android:pathData="M20,6h-8l-2,-2H4C2.89,4 2,4.89 2,6v12c0,1.1 0.89,2 2,2h16c1.1,0 2,-0.9 2,-2V8C22,6.89 21.1,6 20,6zM19,18H5V8h14v10zM12,9l-4,4h3v3h2v-3h3L12,9z"/>
</vector>

View file

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="?attr/colorControlNormal"
android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z"/>
</vector>

View file

@ -110,24 +110,6 @@
android:text="@string/play" android:text="@string/play"
app:icon="@drawable/ic_play" /> app:icon="@drawable/ic_play" />
<com.google.android.material.button.MaterialButton
android:id="@+id/menu_button_open"
style="@style/Widget.Material3.Button.IconButton.Filled.Tonal"
android:layout_width="48dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
app:icon="@drawable/ic_open"
app:iconGravity="textStart" />
<com.google.android.material.button.MaterialButton
android:id="@+id/menu_button_uninstall"
style="@style/Widget.Material3.Button.IconButton.Filled.Tonal"
android:layout_width="48dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
app:icon="@drawable/ic_uninstall"
app:iconGravity="textStart" />
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/game_shortcut" android:id="@+id/game_shortcut"
style="@style/Widget.Material3.Button.IconButton.Filled.Tonal" style="@style/Widget.Material3.Button.IconButton.Filled.Tonal"
@ -138,6 +120,11 @@
app:icon="@drawable/ic_shortcut" app:icon="@drawable/ic_shortcut"
app:iconGravity="textStart" /> app:iconGravity="textStart" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1" />
</LinearLayout> </LinearLayout>
<LinearLayout <LinearLayout
@ -161,7 +148,6 @@
android:contentDescription="@string/cheats" android:contentDescription="@string/cheats"
android:text="@string/cheats" /> android:text="@string/cheats" />
</LinearLayout> </LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -1,44 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingTop="12dp"
android:paddingLeft="24dp"
android:paddingRight="24dp"
android:paddingBottom="0dp">
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?android:attr/listDivider" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:fadeScrollbars="false"
android:paddingTop="2dp"
android:paddingBottom="6dp"
android:overScrollMode="ifContentScrolls">
<TextView
android:id="@+id/messageText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/select_which_user_directory_to_use"
android:textAppearance="?attr/textAppearanceBodyMedium"
android:paddingBottom="16dp" />
</ScrollView>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginBottom="4dp"
android:background="?android:attr/listDivider" />
<RadioGroup
android:id="@+id/radioGroup"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation = "vertical" />
</LinearLayout>

View file

@ -61,13 +61,6 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/setup_tool_connect" /> android:text="@string/setup_tool_connect" />
<Button
android:id="@+id/button_unlink_console_data"
style="@style/Widget.Material3.Button.UnelevatedButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/delete_system_files" />
<View <View
android:id="@+id/divider2" android:id="@+id/divider2"
android:layout_width="match_parent" android:layout_width="match_parent"

View file

@ -1,26 +1,14 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <com.google.android.material.textview.MaterialTextView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/text_game_title"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="24dp" android:layout_marginTop="24dp"
android:layout_marginStart="24dp" android:layout_marginStart="24dp"
android:layout_marginEnd="24dp"> android:layout_marginEnd="24dp"
android:textAppearance="?attr/textAppearanceHeadlineMedium"
<ImageView android:textColor="?attr/colorOnSurface"
android:id="@+id/game_icon" android:textAlignment="viewStart"
android:layout_width="48dp" tools:text="Super Mario 3D Land" />
android:layout_height="48dp"
android:layout_marginEnd="16dp"/>
<com.google.android.material.textview.MaterialTextView
android:id="@+id/text_game_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?attr/textAppearanceHeadlineMedium"
android:textColor="?attr/colorOnSurface"
android:textAlignment="viewStart"
tools:text="text_game_title" />
</LinearLayout>

View file

@ -1,24 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/game_context_open_app"
android:title="@string/game_context_open_app" />
<item
android:id="@+id/game_context_open_save_dir"
android:title="@string/game_context_open_save_dir" />
<item
android:id="@+id/game_context_open_updates"
android:title="@string/game_context_open_updates" />
<item
android:id="@+id/game_context_open_dlc"
android:title="@string/game_context_open_dlc" />
<item
android:id="@+id/game_context_open_extra"
android:title="@string/game_context_open_extra" />
<item
android:id="@+id/game_context_open_textures"
android:title="@string/game_context_open_textures" />
<item
android:id="@+id/game_context_open_mods"
android:title="@string/game_context_open_mods" />
</menu>

View file

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/game_context_uninstall"
android:title="@string/uninstall_cia" />
<item
android:id="@+id/game_context_uninstall_dlc"
android:title="@string/game_context_uninstall_dlc" />
<item
android:id="@+id/game_context_uninstall_updates"
android:title="@string/game_context_uninstall_updates" />
</menu>

Some files were not shown because too many files have changed in this diff Show more