mirror of
https://github.com/azahar-emu/azahar.git
synced 2026-06-18 17:09:39 -04:00
Compare commits
No commits in common. "master" and "2125.0" have entirely different histories.
393 changed files with 12287 additions and 29392 deletions
|
|
@ -12,9 +12,6 @@ fi
|
||||||
|
|
||||||
echo "Tag name is: $TAG_NAME"
|
echo "Tag name is: $TAG_NAME"
|
||||||
|
|
||||||
docker build --no-cache -f docker/azahar-room/Dockerfile -t azahar-room:$TAG_NAME .
|
docker build -f docker/azahar-room/Dockerfile -t azahar-room:$TAG_NAME .
|
||||||
mkdir -p build
|
mkdir -p build
|
||||||
FILENAME="azahar-room-$TARGET-$TAG_NAME.dockerimage"
|
docker save azahar-room:$TAG_NAME > build/azahar-room-$TAG_NAME.dockerimage
|
||||||
docker save azahar-room:$TAG_NAME > build/$FILENAME
|
|
||||||
|
|
||||||
echo "DOCKER_IMAGE_PATH=artifacts/$FILENAME" >> $GITHUB_ENV
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,8 @@ cmake .. -GNinja \
|
||||||
-DCMAKE_SYSTEM_NAME=iOS \
|
-DCMAKE_SYSTEM_NAME=iOS \
|
||||||
-DCMAKE_OSX_ARCHITECTURES=arm64 \
|
-DCMAKE_OSX_ARCHITECTURES=arm64 \
|
||||||
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
|
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
|
||||||
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache
|
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
|
||||||
|
-DENABLE_QT_TRANSLATION=ON
|
||||||
ninja
|
ninja
|
||||||
|
|
||||||
ccache -s -v
|
ccache -s -v
|
||||||
|
|
|
||||||
|
|
@ -25,8 +25,9 @@ cmake .. -G Ninja \
|
||||||
-DCMAKE_BUILD_TYPE=Release \
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
-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_ROOM_STANDALONE=OFF \
|
-DENABLE_ROOM_STANDALONE=OFF \
|
||||||
-DENABLE_DISCORD_RPC=ON \
|
-DUSE_DISCORD_PRESENCE=ON \
|
||||||
"${EXTRA_CMAKE_FLAGS[@]}"
|
"${EXTRA_CMAKE_FLAGS[@]}"
|
||||||
ninja
|
ninja
|
||||||
strip -s bin/Release/*
|
strip -s bin/Release/*
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,13 @@
|
||||||
|
|
||||||
ARTIFACTS_LIST=($ARTIFACTS)
|
ARTIFACTS_LIST=($ARTIFACTS)
|
||||||
|
|
||||||
BUILD_DIR=build
|
BUNDLE_DIR=build/bundle
|
||||||
UNIVERSAL_DIR=$BUILD_DIR/universal
|
mkdir build
|
||||||
BUNDLE_DIR=$UNIVERSAL_DIR/bundle
|
|
||||||
OTHER_BUNDLE_DIR=$BUILD_DIR/x86_64/bundle
|
|
||||||
|
|
||||||
# Set up the base bundle to combine into.
|
# Set up the base artifact to combine into.
|
||||||
mkdir $UNIVERSAL_DIR
|
BASE_ARTIFACT=${ARTIFACTS_LIST[0]}
|
||||||
cp -a $BUILD_DIR/arm64/bundle $UNIVERSAL_DIR
|
BASE_ARTIFACT_ARCH="${BASE_ARTIFACT##*-}"
|
||||||
|
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.app/Contents/MacOS/azahar)
|
||||||
|
|
@ -20,18 +19,21 @@ DYLIB_PATHS=($(cd $BUNDLE_DIR && find . -name '*.dylib'))
|
||||||
unset IFS
|
unset IFS
|
||||||
|
|
||||||
# Combine all of the executable binaries and dylibs.
|
# Combine all of the executable binaries and dylibs.
|
||||||
for BIN_PATH in "${BIN_PATHS[@]}"; do
|
for OTHER_ARTIFACT in "${ARTIFACTS_LIST[@]:1}"; do
|
||||||
lipo -create -output $BUNDLE_DIR/$BIN_PATH $BUNDLE_DIR/$BIN_PATH $OTHER_BUNDLE_DIR/$BIN_PATH
|
OTHER_ARTIFACT_ARCH="${OTHER_ARTIFACT##*-}"
|
||||||
done
|
|
||||||
|
|
||||||
for DYLIB_PATH in "${DYLIB_PATHS[@]}"; do
|
for BIN_PATH in "${BIN_PATHS[@]}"; do
|
||||||
|
lipo -create -output $BUNDLE_DIR/$BIN_PATH $BUNDLE_DIR/$BIN_PATH $OTHER_ARTIFACT/$BIN_PATH
|
||||||
|
done
|
||||||
|
|
||||||
|
for DYLIB_PATH in "${DYLIB_PATHS[@]}"; do
|
||||||
# Only merge if the libraries do not have conflicting arches, otherwise it will fail.
|
# Only merge if the libraries do not have conflicting arches, otherwise it will fail.
|
||||||
DYLIB_INFO=`file $BUNDLE_DIR/$DYLIB_PATH`
|
DYLIB_INFO=`file $BUNDLE_DIR/$DYLIB_PATH`
|
||||||
|
OTHER_DYLIB_INFO=`file $OTHER_ARTIFACT/$DYLIB_PATH`
|
||||||
OTHER_DYLIB_INFO=`file $OTHER_BUNDLE_DIR/$DYLIB_PATH`
|
if ! [[ "$DYLIB_INFO" =~ "$OTHER_ARTIFACT_ARCH" ]] && ! [[ "$OTHER_DYLIB_INFO" =~ "$BASE_ARTIFACT_ARCH" ]]; then
|
||||||
if ! [[ "$DYLIB_INFO" =~ "x86_64" ]] && ! [[ "$OTHER_DYLIB_INFO" =~ "arm64" ]]; then
|
lipo -create -output $BUNDLE_DIR/$DYLIB_PATH $BUNDLE_DIR/$DYLIB_PATH $OTHER_ARTIFACT/$DYLIB_PATH
|
||||||
lipo -create -output $BUNDLE_DIR/$DYLIB_PATH $BUNDLE_DIR/$DYLIB_PATH $OTHER_BUNDLE_DIR/$DYLIB_PATH
|
|
||||||
fi
|
fi
|
||||||
|
done
|
||||||
done
|
done
|
||||||
|
|
||||||
# Remove leftover libs so that they aren't distributed
|
# Remove leftover libs so that they aren't distributed
|
||||||
|
|
|
||||||
14
.ci/macos.sh
14
.ci/macos.sh
|
|
@ -4,19 +4,23 @@ if [ "$GITHUB_REF_TYPE" == "tag" ]; then
|
||||||
export EXTRA_CMAKE_FLAGS=(-DENABLE_QT_UPDATE_CHECKER=ON)
|
export EXTRA_CMAKE_FLAGS=(-DENABLE_QT_UPDATE_CHECKER=ON)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
mkdir -p build/$BUILD_ARCH && cd build/$BUILD_ARCH
|
mkdir build && cd build
|
||||||
cmake ../.. -GNinja \
|
cmake .. -GNinja \
|
||||||
-DCMAKE_BUILD_TYPE=Release \
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
-DCMAKE_OSX_ARCHITECTURES="$BUILD_ARCH" \
|
-DCMAKE_OSX_ARCHITECTURES="$TARGET" \
|
||||||
|
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
|
||||||
|
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
|
||||||
|
-DENABLE_QT_TRANSLATION=ON \
|
||||||
-DENABLE_ROOM_STANDALONE=OFF \
|
-DENABLE_ROOM_STANDALONE=OFF \
|
||||||
-DENABLE_DISCORD_RPC=ON \
|
-DUSE_DISCORD_PRESENCE=ON \
|
||||||
"${EXTRA_CMAKE_FLAGS[@]}"
|
"${EXTRA_CMAKE_FLAGS[@]}"
|
||||||
ninja
|
ninja
|
||||||
ninja bundle
|
ninja bundle
|
||||||
mv ./bundle/azahar.app ./bundle/Azahar.app # TODO: Can this be done in CMake?
|
mv ./bundle/azahar.app ./bundle/Azahar.app # TODO: Can this be done in CMake?
|
||||||
|
|
||||||
|
ccache -s -v
|
||||||
|
|
||||||
CURRENT_ARCH=`arch`
|
CURRENT_ARCH=`arch`
|
||||||
if [ "$BUILD_ARCH" = "$CURRENT_ARCH" ]; then
|
if [ "$TARGET" = "$CURRENT_ARCH" ]; then
|
||||||
ctest -VV -C Release
|
ctest -VV -C Release
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
25
.ci/mxe.sh
25
.ci/mxe.sh
|
|
@ -1,25 +0,0 @@
|
||||||
#!/bin/bash -ex
|
|
||||||
|
|
||||||
# TODO: Why doesn't the CI environment use the PATH set in the Dockerimage?
|
|
||||||
# It works fine when using the image locally.
|
|
||||||
export PATH="/mxe/usr/bin:${PATH}"
|
|
||||||
|
|
||||||
mkdir build && cd build
|
|
||||||
|
|
||||||
if [ "$GITHUB_REF_TYPE" == "tag" ]; then
|
|
||||||
export EXTRA_CMAKE_FLAGS=(-DENABLE_QT_UPDATE_CHECKER=ON)
|
|
||||||
fi
|
|
||||||
|
|
||||||
x86_64-w64-mingw32.shared-cmake .. \
|
|
||||||
-DCMAKE_BUILD_TYPE=Release \
|
|
||||||
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
|
|
||||||
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
|
|
||||||
-DENABLE_DISCORD_RPC=ON \
|
|
||||||
-DUSE_SYSTEM_BOOST=ON \
|
|
||||||
-DUSE_SYSTEM_CRYPTOPP=ON \
|
|
||||||
"${EXTRA_CMAKE_FLAGS[@]}"
|
|
||||||
x86_64-w64-mingw32.shared-cmake --build . -- -j$(nproc)
|
|
||||||
x86_64-w64-mingw32.shared-strip -s bin/Release/*.exe
|
|
||||||
make bundle
|
|
||||||
|
|
||||||
ccache -s -v
|
|
||||||
35
.ci/pack.sh
35
.ci/pack.sh
|
|
@ -3,21 +3,20 @@
|
||||||
# Determine the full revision name.
|
# Determine the full revision name.
|
||||||
GITDATE="`git show -s --date=short --format='%ad' | sed 's/-//g'`"
|
GITDATE="`git show -s --date=short --format='%ad' | sed 's/-//g'`"
|
||||||
GITREV="`git show -s --format='%h'`"
|
GITREV="`git show -s --format='%h'`"
|
||||||
|
REV_NAME="azahar-$OS-$TARGET-$GITDATE-$GITREV"
|
||||||
|
|
||||||
|
# Determine the name of the release being built.
|
||||||
|
if [ "$GITHUB_REF_TYPE" = "tag" ]; then
|
||||||
|
RELEASE_NAME=azahar-$GITHUB_REF_NAME
|
||||||
|
REV_NAME="azahar-$OS-$TARGET-$GITHUB_REF_NAME"
|
||||||
|
else
|
||||||
|
RELEASE_NAME=azahar-head
|
||||||
|
fi
|
||||||
|
|
||||||
# Archive and upload the artifacts.
|
# Archive and upload the artifacts.
|
||||||
mkdir -p artifacts
|
mkdir -p artifacts
|
||||||
|
|
||||||
function pack_artifacts() {
|
function pack_artifacts() {
|
||||||
REV_NAME="azahar-$OS-$TARGET-$GITDATE-$GITREV"
|
|
||||||
|
|
||||||
# Determine the name of the release being built.
|
|
||||||
if [ "$GITHUB_REF_TYPE" = "tag" ]; then
|
|
||||||
RELEASE_NAME=azahar-$GITHUB_REF_NAME
|
|
||||||
REV_NAME="azahar-$OS-$TARGET-$GITHUB_REF_NAME"
|
|
||||||
else
|
|
||||||
RELEASE_NAME=azahar-head
|
|
||||||
fi
|
|
||||||
|
|
||||||
ARTIFACTS_PATH="$1"
|
ARTIFACTS_PATH="$1"
|
||||||
|
|
||||||
# Set up root directory for archive.
|
# Set up root directory for archive.
|
||||||
|
|
@ -36,10 +35,10 @@ function pack_artifacts() {
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Create .zip/.tar.gz
|
# Create .zip/.tar.gz
|
||||||
if [ "$OS" = "windows" ] && [ "$TARGET" != "mxe" ]; then
|
if [ "$OS" = "windows" ]; then
|
||||||
ARCHIVE_FULL_NAME="$ARCHIVE_NAME.zip"
|
ARCHIVE_FULL_NAME="$ARCHIVE_NAME.zip"
|
||||||
powershell Compress-Archive "$REV_NAME" "$ARCHIVE_FULL_NAME"
|
powershell Compress-Archive "$REV_NAME" "$ARCHIVE_FULL_NAME"
|
||||||
elif [ "$OS" = "android" ] || [ "$OS" = "macos" ] || [ "$TARGET" = "mxe" ]; then
|
elif [ "$OS" = "android" ] || [ "$OS" = "macos" ]; then
|
||||||
ARCHIVE_FULL_NAME="$ARCHIVE_NAME.zip"
|
ARCHIVE_FULL_NAME="$ARCHIVE_NAME.zip"
|
||||||
zip -r "$ARCHIVE_FULL_NAME" "$REV_NAME"
|
zip -r "$ARCHIVE_FULL_NAME" "$REV_NAME"
|
||||||
else
|
else
|
||||||
|
|
@ -57,23 +56,11 @@ if [ -n "$UNPACKED" ]; then
|
||||||
FILENAME=$(basename "$ARTIFACT")
|
FILENAME=$(basename "$ARTIFACT")
|
||||||
EXTENSION="${FILENAME##*.}"
|
EXTENSION="${FILENAME##*.}"
|
||||||
|
|
||||||
# TODO: Deduplicate
|
|
||||||
REV_NAME="azahar-$OS-$TARGET-$GITDATE-$GITREV"
|
|
||||||
|
|
||||||
# Determine the name of the release being built.
|
|
||||||
if [ "$GITHUB_REF_TYPE" = "tag" ]; then
|
|
||||||
RELEASE_NAME=azahar-$GITHUB_REF_NAME
|
|
||||||
REV_NAME="azahar-$OS-$TARGET-$GITHUB_REF_NAME"
|
|
||||||
else
|
|
||||||
RELEASE_NAME=azahar-head
|
|
||||||
fi
|
|
||||||
|
|
||||||
mv "$ARTIFACT" "artifacts/$REV_NAME.$EXTENSION"
|
mv "$ARTIFACT" "artifacts/$REV_NAME.$EXTENSION"
|
||||||
done
|
done
|
||||||
elif [ -n "$PACK_INDIVIDUALLY" ]; then
|
elif [ -n "$PACK_INDIVIDUALLY" ]; then
|
||||||
# Pack and upload the artifacts one-by-one.
|
# Pack and upload the artifacts one-by-one.
|
||||||
for ARTIFACT in build/bundle/*; do
|
for ARTIFACT in build/bundle/*; do
|
||||||
TARGET=$(basename "$ARTIFACT")
|
|
||||||
pack_artifacts "$ARTIFACT"
|
pack_artifacts "$ARTIFACT"
|
||||||
done
|
done
|
||||||
else
|
else
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ gcc -v
|
||||||
tx --version
|
tx --version
|
||||||
|
|
||||||
mkdir build && cd build
|
mkdir build && cd build
|
||||||
cmake .. -DGENERATE_QT_TRANSLATION=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_SDL2=OFF
|
cmake .. -DENABLE_QT_TRANSLATION=ON -DGENERATE_QT_TRANSLATION=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_SDL2=OFF
|
||||||
make translation
|
make translation
|
||||||
cd ..
|
cd ..
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,8 @@ cmake .. -G Ninja \
|
||||||
-DCMAKE_BUILD_TYPE=Release \
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
|
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
|
||||||
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
|
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
|
||||||
-DENABLE_DISCORD_RPC=ON \
|
-DENABLE_QT_TRANSLATION=ON \
|
||||||
|
-DUSE_DISCORD_PRESENCE=ON \
|
||||||
"${EXTRA_CMAKE_FLAGS[@]}"
|
"${EXTRA_CMAKE_FLAGS[@]}"
|
||||||
ninja
|
ninja
|
||||||
ninja bundle
|
ninja bundle
|
||||||
|
|
|
||||||
1
.github/PULL_REQUEST_TEMPLATE.md
vendored
1
.github/PULL_REQUEST_TEMPLATE.md
vendored
|
|
@ -1,5 +1,4 @@
|
||||||
- [ ] I have read the [Azahar AI Policy document](https://github.com/azahar-emu/azahar/blob/master/AI-POLICY.md) and have disclosed any use of AI if applicable under those terms.
|
- [ ] I have read the [Azahar AI Policy document](https://github.com/azahar-emu/azahar/blob/master/AI-POLICY.md) and have disclosed any use of AI if applicable under those terms.
|
||||||
---------
|
|
||||||
|
|
||||||
---
|
---
|
||||||
<!--
|
<!--
|
||||||
|
|
|
||||||
264
.github/workflows/build.yml
vendored
264
.github/workflows/build.yml
vendored
|
|
@ -7,41 +7,21 @@ on:
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ master ]
|
branches: [ master ]
|
||||||
|
|
||||||
permissions:
|
|
||||||
id-token: write
|
|
||||||
contents: read
|
|
||||||
attestations: write
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
source:
|
source:
|
||||||
if: ${{ !github.head_ref }}
|
if: ${{ !github.head_ref }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
- name: Pack
|
- name: Pack
|
||||||
run: ./.ci/source.sh
|
run: ./.ci/source.sh
|
||||||
- name: Generate SBOM
|
|
||||||
if: ${{ github.ref_type == 'tag' }}
|
|
||||||
uses: anchore/sbom-action@v0
|
|
||||||
with:
|
|
||||||
path: ./
|
|
||||||
format: spdx-json
|
|
||||||
output-file: artifacts/source.spdx.json
|
|
||||||
upload-artifact: false
|
|
||||||
- name: Upload
|
- name: Upload
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: source
|
name: source
|
||||||
path: artifacts/
|
path: artifacts/
|
||||||
- name: Attest artifacts
|
|
||||||
if: ${{ github.ref_type == 'tag' }}
|
|
||||||
uses: actions/attest@v4
|
|
||||||
with:
|
|
||||||
subject-path: |
|
|
||||||
artifacts/*.tar.xz
|
|
||||||
sbom-path: artifacts/source.spdx.json
|
|
||||||
|
|
||||||
linux-x86_64:
|
linux-x86_64:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
@ -59,15 +39,14 @@ jobs:
|
||||||
OS: linux
|
OS: linux
|
||||||
TARGET: ${{ matrix.target }}
|
TARGET: ${{ matrix.target }}
|
||||||
SHOULD_RUN: ${{ (matrix.target != 'appimage-wayland' || github.ref_type == 'tag') }}
|
SHOULD_RUN: ${{ (matrix.target != 'appimage-wayland' || github.ref_type == 'tag') }}
|
||||||
CACHE_ENABLED: ${{ github.ref_type != 'tag' }}
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
if: ${{ env.SHOULD_RUN == 'true' }}
|
if: ${{ env.SHOULD_RUN == 'true' }}
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
- name: Set up cache
|
- name: Set up cache
|
||||||
if: ${{ env.SHOULD_RUN == 'true' && env.CACHE_ENABLED == 'true' }}
|
if: ${{ env.SHOULD_RUN == 'true' }}
|
||||||
uses: actions/cache@v5
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: ${{ env.CCACHE_DIR }}
|
path: ${{ env.CCACHE_DIR }}
|
||||||
key: ${{ github.job }}-${{ matrix.target }}-${{ github.sha }}
|
key: ${{ github.job }}-${{ matrix.target }}-${{ github.sha }}
|
||||||
|
|
@ -85,27 +64,12 @@ jobs:
|
||||||
if: ${{ matrix.target == 'appimage-wayland' && env.SHOULD_RUN == 'true' }}
|
if: ${{ matrix.target == 'appimage-wayland' && env.SHOULD_RUN == 'true' }}
|
||||||
run: |
|
run: |
|
||||||
mv artifacts/azahar.AppImage artifacts/azahar-wayland.AppImage
|
mv artifacts/azahar.AppImage artifacts/azahar-wayland.AppImage
|
||||||
- name: Generate SBOM
|
|
||||||
if: ${{ contains(matrix.target, 'appimage') && github.ref_type == 'tag' && env.SHOULD_RUN == 'true' }}
|
|
||||||
uses: anchore/sbom-action@v0
|
|
||||||
with:
|
|
||||||
path: build/
|
|
||||||
format: spdx-json
|
|
||||||
output-file: artifacts/linux-x86_64-${{ matrix.target }}.spdx.json
|
|
||||||
upload-artifact: false
|
|
||||||
- name: Upload
|
- name: Upload
|
||||||
if: ${{ contains(matrix.target, 'appimage') && env.SHOULD_RUN == 'true' }}
|
if: ${{ contains(matrix.target, 'appimage') && env.SHOULD_RUN == 'true' }}
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: ${{ github.job }}-${{ matrix.target }}
|
name: ${{ github.job }}-${{ matrix.target }}
|
||||||
path: artifacts/
|
path: artifacts/
|
||||||
- name: Attest artifacts
|
|
||||||
if: ${{ contains(matrix.target, 'appimage') && github.ref_type == 'tag' && env.SHOULD_RUN == 'true' }}
|
|
||||||
uses: actions/attest@v4
|
|
||||||
with:
|
|
||||||
subject-path: |
|
|
||||||
artifacts/*.AppImage
|
|
||||||
sbom-path: artifacts/linux-x86_64-${{ matrix.target }}.spdx.json
|
|
||||||
|
|
||||||
linux-arm64:
|
linux-arm64:
|
||||||
runs-on: ubuntu-24.04-arm
|
runs-on: ubuntu-24.04-arm
|
||||||
|
|
@ -123,11 +87,11 @@ jobs:
|
||||||
OS: linux
|
OS: linux
|
||||||
TARGET: ${{ matrix.target }}
|
TARGET: ${{ matrix.target }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
- name: Set up cache
|
- name: Set up cache
|
||||||
uses: actions/cache@v5
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: ${{ env.CCACHE_DIR }}
|
path: ${{ env.CCACHE_DIR }}
|
||||||
key: ${{ github.job }}-${{ matrix.target }}-${{ github.sha }}
|
key: ${{ github.job }}-${{ matrix.target }}-${{ github.sha }}
|
||||||
|
|
@ -137,80 +101,85 @@ jobs:
|
||||||
run: ./.ci/linux.sh
|
run: ./.ci/linux.sh
|
||||||
|
|
||||||
macos:
|
macos:
|
||||||
runs-on: 'macos-26'
|
runs-on: ${{ (matrix.target == 'x86_64' && 'macos-26-intel') || 'macos-26' }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
target: ["x86_64", "arm64"]
|
||||||
env:
|
env:
|
||||||
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
||||||
CCACHE_COMPILERCHECK: content
|
CCACHE_COMPILERCHECK: content
|
||||||
CCACHE_SLOPPINESS: time_macros
|
CCACHE_SLOPPINESS: time_macros
|
||||||
CACHE_ENABLED: ${{ github.ref_type != 'tag' }}
|
|
||||||
OS: macos
|
OS: macos
|
||||||
|
TARGET: ${{ matrix.target }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
- name: Set up cache
|
- name: Set up cache
|
||||||
if: ${{ env.CACHE_ENABLED == 'true' }}
|
uses: actions/cache@v4
|
||||||
uses: actions/cache@v5
|
|
||||||
with:
|
with:
|
||||||
path: ${{ env.CCACHE_DIR }}
|
path: ${{ env.CCACHE_DIR }}
|
||||||
key: ${{ runner.os }}-${{ github.sha }}
|
key: ${{ runner.os }}-${{ matrix.target }}-${{ github.sha }}
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-
|
${{ runner.os }}-${{ matrix.target }}-
|
||||||
- name: Install tools
|
- name: Install tools
|
||||||
run: brew install ccache ninja spirv-tools
|
run: brew install ccache ninja spirv-tools
|
||||||
- name: Build (x86_64)
|
- name: Build
|
||||||
run: BUILD_ARCH=x86_64 ./.ci/macos.sh
|
run: ./.ci/macos.sh
|
||||||
- name: Build (arm64)
|
- name: Prepare outputs for caching
|
||||||
run: BUILD_ARCH=arm64 ./.ci/macos.sh
|
run: cp -R build/bundle $OS-$TARGET
|
||||||
|
- name: Cache outputs for universal build
|
||||||
|
uses: actions/cache/save@v4
|
||||||
|
with:
|
||||||
|
path: ${{ env.OS }}-${{ env.TARGET }}
|
||||||
|
key: ${{ runner.os }}-${{ matrix.target }}-${{ github.sha }}-${{ github.run_id }}-${{ github.run_attempt }}
|
||||||
|
- name: Pack
|
||||||
|
run: ./.ci/pack.sh
|
||||||
|
- name: Upload
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: ${{ env.OS }}-${{ env.TARGET }}
|
||||||
|
path: artifacts/
|
||||||
|
|
||||||
|
macos-universal:
|
||||||
|
runs-on: macos-26
|
||||||
|
needs: macos
|
||||||
|
env:
|
||||||
|
OS: macos
|
||||||
|
TARGET: universal
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Download x86_64 build from cache
|
||||||
|
uses: actions/cache/restore@v4
|
||||||
|
with:
|
||||||
|
path: ${{ env.OS }}-x86_64
|
||||||
|
key: ${{ runner.os }}-x86_64-${{ github.sha }}-${{ github.run_id }}-${{ github.run_attempt }}
|
||||||
|
fail-on-cache-miss: true
|
||||||
|
- name: Download ARM64 build from cache
|
||||||
|
uses: actions/cache/restore@v4
|
||||||
|
with:
|
||||||
|
path: ${{ env.OS }}-arm64
|
||||||
|
key: ${{ runner.os }}-arm64-${{ github.sha }}-${{ github.run_id }}-${{ github.run_attempt }}
|
||||||
|
fail-on-cache-miss: true
|
||||||
- name: Create universal app
|
- name: Create universal app
|
||||||
run: ./.ci/macos-universal.sh
|
run: ./.ci/macos-universal.sh
|
||||||
- name: Prepare for packing
|
|
||||||
run: |
|
|
||||||
mkdir build/bundle
|
|
||||||
cp -r build/x86_64/bundle build/bundle/x86_64
|
|
||||||
cp -r build/arm64/bundle build/bundle/arm64
|
|
||||||
cp -r build/universal/bundle build/bundle/universal
|
|
||||||
- name: Pack
|
|
||||||
env:
|
env:
|
||||||
PACK_INDIVIDUALLY: 1
|
ARTIFACTS: ${{ env.OS }}-x86_64 ${{ env.OS }}-arm64
|
||||||
|
- name: Pack
|
||||||
run: ./.ci/pack.sh
|
run: ./.ci/pack.sh
|
||||||
- name: Generate SBOM
|
|
||||||
if: ${{ github.ref_type == 'tag' }}
|
|
||||||
uses: anchore/sbom-action@v0
|
|
||||||
with:
|
|
||||||
path: build/
|
|
||||||
format: spdx-json
|
|
||||||
output-file: artifacts/macos.spdx.json
|
|
||||||
upload-artifact: false
|
|
||||||
- name: Upload
|
- name: Upload
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: ${{ env.OS }}
|
name: ${{ env.OS }}-${{ env.TARGET }}
|
||||||
path: artifacts/
|
path: artifacts/
|
||||||
- name: Attest artifacts
|
|
||||||
if: ${{ github.ref_type == 'tag' }}
|
|
||||||
uses: actions/attest@v4
|
|
||||||
with:
|
|
||||||
subject-path: |
|
|
||||||
artifacts/*.zip
|
|
||||||
sbom-path: artifacts/macos.spdx.json
|
|
||||||
|
|
||||||
windows:
|
windows:
|
||||||
|
runs-on: windows-latest
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
target: ["msvc", "msys2"]
|
||||||
- target: msvc
|
|
||||||
os: windows-latest
|
|
||||||
- target: msys2
|
|
||||||
os: windows-latest
|
|
||||||
- target: mxe
|
|
||||||
os: ubuntu-latest
|
|
||||||
container:
|
|
||||||
image: opensauce04/azahar-build-environment:latest
|
|
||||||
options: -u 1001
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
container: ${{ matrix.container }}
|
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
shell: ${{ (matrix.target == 'msys2' && 'msys2') || 'bash' }} {0}
|
shell: ${{ (matrix.target == 'msys2' && 'msys2') || 'bash' }} {0}
|
||||||
|
|
@ -218,16 +187,14 @@ jobs:
|
||||||
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
||||||
CCACHE_COMPILERCHECK: content
|
CCACHE_COMPILERCHECK: content
|
||||||
CCACHE_SLOPPINESS: time_macros
|
CCACHE_SLOPPINESS: time_macros
|
||||||
CACHE_ENABLED: ${{ github.ref_type != 'tag' }}
|
|
||||||
OS: windows
|
OS: windows
|
||||||
TARGET: ${{ matrix.target }}
|
TARGET: ${{ matrix.target }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
- name: Set up cache
|
- name: Set up cache
|
||||||
if: ${{ env.CACHE_ENABLED == 'true' }}
|
uses: actions/cache@v4
|
||||||
uses: actions/cache@v5
|
|
||||||
with:
|
with:
|
||||||
path: ${{ env.CCACHE_DIR }}
|
path: ${{ env.CCACHE_DIR }}
|
||||||
key: ${{ runner.os }}-${{ matrix.target }}-${{ github.sha }}
|
key: ${{ runner.os }}-${{ matrix.target }}-${{ github.sha }}
|
||||||
|
|
@ -235,7 +202,7 @@ jobs:
|
||||||
${{ runner.os }}-${{ matrix.target }}-
|
${{ runner.os }}-${{ matrix.target }}-
|
||||||
- name: Set up MSVC
|
- name: Set up MSVC
|
||||||
if: ${{ matrix.target == 'msvc' }}
|
if: ${{ matrix.target == 'msvc' }}
|
||||||
uses: azahar-emu/msvc-dev-cmd@v1
|
uses: ilammy/msvc-dev-cmd@v1
|
||||||
- name: Install extra tools (MSVC)
|
- name: Install extra tools (MSVC)
|
||||||
if: ${{ matrix.target == 'msvc' }}
|
if: ${{ matrix.target == 'msvc' }}
|
||||||
run: choco install ccache ninja ptime wget
|
run: choco install ccache ninja ptime wget
|
||||||
|
|
@ -256,62 +223,34 @@ jobs:
|
||||||
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)
|
- name: Install extra tools (MSYS2)
|
||||||
if: ${{ matrix.target == 'msys2' }}
|
if: ${{ matrix.target == 'msys2' }}
|
||||||
uses: crazy-max/ghaction-chocolatey@v4
|
uses: crazy-max/ghaction-chocolatey@v3
|
||||||
with:
|
with:
|
||||||
args: install ptime wget
|
args: install ptime wget
|
||||||
- name: Install NSIS
|
- name: Install NSIS
|
||||||
if: ${{ github.ref_type == 'tag' && matrix.target != 'mxe' }}
|
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
|
wget https://download.sourceforge.net/project/nsis/NSIS%203/3.11/nsis-3.11-setup.exe -O D:/a/_temp/nsis-setup.exe
|
||||||
ptime D:/a/_temp/nsis-setup.exe /S
|
ptime D:/a/_temp/nsis-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
|
||||||
- name: Build (Native)
|
- name: Build
|
||||||
if: ${{ matrix.target != 'mxe' }}
|
|
||||||
run: ./.ci/windows.sh
|
run: ./.ci/windows.sh
|
||||||
- name: Build (MXE)
|
- name: Generate installer
|
||||||
if: ${{ matrix.target == 'mxe' }}
|
if: ${{ github.ref_type == 'tag' }}
|
||||||
run: ./.ci/mxe.sh
|
|
||||||
- name: Generate installer (Native)
|
|
||||||
if: ${{ github.ref_type == 'tag' && matrix.target != 'mxe' }}
|
|
||||||
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 ..\..\artifacts 2> NUL
|
||||||
move /y *.exe ..\..\artifacts\
|
move /y *.exe ..\..\artifacts\
|
||||||
shell: cmd
|
shell: cmd
|
||||||
- name: Generate installer (MXE)
|
|
||||||
if: ${{ github.ref_type == 'tag' && matrix.target == 'mxe' }}
|
|
||||||
run: |
|
|
||||||
export PATH="/mxe/usr/bin:${PATH}" # TODO: Why do we have to do this if it's in the image?
|
|
||||||
cd src/installer
|
|
||||||
x86_64-w64-mingw32.shared-makensis -DPRODUCT_VARIANT=${{ matrix.target }} -DPRODUCT_VERSION=${{ github.ref_name }} citra.nsi
|
|
||||||
mkdir -p ../../artifacts
|
|
||||||
mv ./*.exe ../../artifacts/
|
|
||||||
- name: Pack
|
- name: Pack
|
||||||
run: ./.ci/pack.sh
|
run: ./.ci/pack.sh
|
||||||
- name: Generate SBOM
|
|
||||||
if: ${{ github.ref_type == 'tag' }}
|
|
||||||
uses: anchore/sbom-action@v0
|
|
||||||
with:
|
|
||||||
path: build/
|
|
||||||
format: spdx-json
|
|
||||||
output-file: artifacts/windows-${{ matrix.target }}.spdx.json
|
|
||||||
upload-artifact: false
|
|
||||||
- name: Upload
|
- name: Upload
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: ${{ env.OS }}-${{ env.TARGET }}
|
name: ${{ env.OS }}-${{ env.TARGET }}
|
||||||
path: artifacts/
|
path: artifacts/
|
||||||
- name: Attest artifacts
|
|
||||||
if: ${{ github.ref_type == 'tag' }}
|
|
||||||
uses: actions/attest@v4
|
|
||||||
with:
|
|
||||||
subject-path: |
|
|
||||||
artifacts/*.zip
|
|
||||||
artifacts/*.exe
|
|
||||||
sbom-path: artifacts/windows-${{ matrix.target }}.spdx.json
|
|
||||||
|
|
||||||
android:
|
android:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
@ -323,18 +262,17 @@ jobs:
|
||||||
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
||||||
CCACHE_COMPILERCHECK: content
|
CCACHE_COMPILERCHECK: content
|
||||||
CCACHE_SLOPPINESS: time_macros
|
CCACHE_SLOPPINESS: time_macros
|
||||||
CACHE_ENABLED: ${{ github.ref_type != 'tag' }}
|
|
||||||
OS: android
|
OS: android
|
||||||
TARGET: ${{ matrix.target }}
|
TARGET: ${{ matrix.target }}
|
||||||
SHOULD_RUN: ${{ (matrix.target == 'vanilla' || github.ref_type == 'tag') }}
|
SHOULD_RUN: ${{ (matrix.target == 'vanilla' || github.ref_type == 'tag') }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
if: ${{ env.SHOULD_RUN == 'true' }}
|
if: ${{ env.SHOULD_RUN == 'true' }}
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
- name: Set up cache
|
- name: Set up cache
|
||||||
if: ${{ env.SHOULD_RUN == 'true' && env.CACHE_ENABLED == 'true' }}
|
if: ${{ env.SHOULD_RUN == 'true' }}
|
||||||
uses: actions/cache@v5
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
~/.gradle/caches
|
~/.gradle/caches
|
||||||
|
|
@ -372,48 +310,23 @@ jobs:
|
||||||
working-directory: src/android/app
|
working-directory: src/android/app
|
||||||
env:
|
env:
|
||||||
UNPACKED: 1
|
UNPACKED: 1
|
||||||
- name: Generate SBOM
|
|
||||||
if: ${{ github.ref_type == 'tag' }}
|
|
||||||
uses: anchore/sbom-action@v0
|
|
||||||
with:
|
|
||||||
path: src/android
|
|
||||||
format: spdx-json
|
|
||||||
output-file: src/android/app/artifacts/android-${{ matrix.target }}.spdx.json
|
|
||||||
upload-artifact: false
|
|
||||||
- name: Upload
|
- name: Upload
|
||||||
if: ${{ env.SHOULD_RUN == 'true' }}
|
if: ${{ env.SHOULD_RUN == 'true' }}
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: ${{ env.OS }}-${{ env.TARGET }}
|
name: ${{ env.OS }}-${{ env.TARGET }}
|
||||||
path: src/android/app/artifacts/
|
path: src/android/app/artifacts/
|
||||||
- name: Attest artifacts
|
|
||||||
if: ${{ github.ref_type == 'tag' }}
|
|
||||||
uses: actions/attest@v4
|
|
||||||
with:
|
|
||||||
subject-path: |
|
|
||||||
src/android/app/artifacts/*.apk
|
|
||||||
src/android/app/artifacts/*.aab
|
|
||||||
sbom-path: src/android/app/artifacts/android-${{ matrix.target }}.spdx.json
|
|
||||||
|
|
||||||
docker:
|
docker:
|
||||||
strategy:
|
runs-on: ubuntu-latest
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- target: x86_64
|
|
||||||
os: ubuntu-24.04
|
|
||||||
- target: arm64
|
|
||||||
os: ubuntu-24.04-arm
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
container:
|
container:
|
||||||
# Can't use docker:dind for ARM64 because it's Alpine-based, see https://github.com/actions/upload-artifact/issues/739
|
image: docker:dind
|
||||||
image: earthbuild/dind:ubuntu-24.04-docker-28.5.2-1
|
|
||||||
env:
|
|
||||||
TARGET: ${{ matrix.target }}
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
|
- name: Install tools
|
||||||
|
run: apk add bash
|
||||||
- name: Fix git ownership
|
- name: Fix git ownership
|
||||||
run: git config --global --add safe.directory .
|
run: git config --global --add safe.directory .
|
||||||
- name: Build Docker image
|
- name: Build Docker image
|
||||||
|
|
@ -422,23 +335,8 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
mkdir -p artifacts
|
mkdir -p artifacts
|
||||||
mv build/*.dockerimage artifacts/
|
mv build/*.dockerimage artifacts/
|
||||||
- name: Generate SBOM
|
|
||||||
if: ${{ github.ref_type == 'tag' }}
|
|
||||||
uses: anchore/sbom-action@v0
|
|
||||||
with:
|
|
||||||
image: ${{ env.DOCKER_IMAGE_PATH }}
|
|
||||||
format: spdx-json
|
|
||||||
output-file: artifacts/docker-room.spdx.json
|
|
||||||
upload-artifact: false
|
|
||||||
- name: Upload
|
- name: Upload
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: docker-${{ env.TARGET }}
|
name: docker
|
||||||
path: artifacts/
|
path: artifacts/
|
||||||
- name: Attest artifacts
|
|
||||||
if: ${{ github.ref_type == 'tag' }}
|
|
||||||
uses: actions/attest@v4
|
|
||||||
with:
|
|
||||||
subject-path: |
|
|
||||||
artifacts/*.dockerimage
|
|
||||||
sbom-path: artifacts/docker-room.spdx.json
|
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ jobs:
|
||||||
(github.event.pull_request.author_association != 'OWNER')
|
(github.event.pull_request.author_association != 'OWNER')
|
||||||
steps:
|
steps:
|
||||||
- name: Detect PR if author is first-time contributor
|
- name: Detect PR if author is first-time contributor
|
||||||
uses: actions/github-script@v9
|
uses: actions/github-script@v7
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
const { owner, repo } = context.repo;
|
const { owner, repo } = context.repo;
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ jobs:
|
||||||
if: github.event.issue.pull_request && contains(github.event.issue.labels.*.name, 'needs verification')
|
if: github.event.issue.pull_request && contains(github.event.issue.labels.*.name, 'needs verification')
|
||||||
steps:
|
steps:
|
||||||
- name: Verify and reopen PR
|
- name: Verify and reopen PR
|
||||||
uses: actions/github-script@v9
|
uses: actions/github-script@v7
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
const { owner, repo } = context.repo;
|
const { owner, repo } = context.repo;
|
||||||
|
|
|
||||||
14
.github/workflows/format.yml
vendored
14
.github/workflows/format.yml
vendored
|
|
@ -13,22 +13,10 @@ jobs:
|
||||||
image: opensauce04/azahar-build-environment:latest
|
image: opensauce04/azahar-build-environment:latest
|
||||||
options: -u 1001
|
options: -u 1001
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Build
|
- name: Build
|
||||||
env:
|
env:
|
||||||
COMMIT_RANGE: ${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }}
|
COMMIT_RANGE: ${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }}
|
||||||
run: ./.ci/clang-format.sh
|
run: ./.ci/clang-format.sh
|
||||||
|
|
||||||
ktlint:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
container:
|
|
||||||
image: opensauce04/azahar-build-environment:latest
|
|
||||||
options: -u 1001
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v6
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
- name: Build
|
|
||||||
run: ./tools/check-kotlin-formatting.sh
|
|
||||||
178
.github/workflows/libretro.yml
vendored
178
.github/workflows/libretro.yml
vendored
|
|
@ -11,13 +11,8 @@ on:
|
||||||
env:
|
env:
|
||||||
CORE_ARGS: -DENABLE_LIBRETRO=ON
|
CORE_ARGS: -DENABLE_LIBRETRO=ON
|
||||||
|
|
||||||
permissions:
|
|
||||||
id-token: write
|
|
||||||
contents: read
|
|
||||||
attestations: write
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
libretro-android:
|
android:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
env:
|
env:
|
||||||
OS: android
|
OS: android
|
||||||
|
|
@ -28,7 +23,7 @@ jobs:
|
||||||
BUILD_DIR: build/android-arm64-v8a
|
BUILD_DIR: build/android-arm64-v8a
|
||||||
EXTRA_PATH: bin/Release
|
EXTRA_PATH: bin/Release
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
- name: Set tag name
|
- name: Set tag name
|
||||||
|
|
@ -37,10 +32,6 @@ jobs:
|
||||||
echo "GIT_TAG_NAME=$GITHUB_REF_NAME" >> $GITHUB_ENV
|
echo "GIT_TAG_NAME=$GITHUB_REF_NAME" >> $GITHUB_ENV
|
||||||
fi
|
fi
|
||||||
echo $GIT_TAG_NAME
|
echo $GIT_TAG_NAME
|
||||||
- name: Install tools
|
|
||||||
run: |
|
|
||||||
sudo apt-get update -y
|
|
||||||
sudo apt-get install -y llvm
|
|
||||||
- name: Update Android SDK CMake version
|
- name: Update Android SDK CMake version
|
||||||
run: |
|
run: |
|
||||||
echo "y" | ${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager "ndk;$ANDROID_NDK_VERSION"
|
echo "y" | ${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager "ndk;$ANDROID_NDK_VERSION"
|
||||||
|
|
@ -50,33 +41,14 @@ jobs:
|
||||||
export NDK_ROOT=${ANDROID_SDK_ROOT}/ndk/$ANDROID_NDK_VERSION
|
export NDK_ROOT=${ANDROID_SDK_ROOT}/ndk/$ANDROID_NDK_VERSION
|
||||||
${ANDROID_SDK_ROOT}/cmake/3.30.3/bin/cmake $CORE_ARGS -DANDROID_PLATFORM=android-$API_LEVEL -DCMAKE_TOOLCHAIN_FILE=$NDK_ROOT/build/cmake/android.toolchain.cmake -DANDROID_STL=c++_static -DANDROID_ABI=$ANDROID_ABI . -B $BUILD_DIR
|
${ANDROID_SDK_ROOT}/cmake/3.30.3/bin/cmake $CORE_ARGS -DANDROID_PLATFORM=android-$API_LEVEL -DCMAKE_TOOLCHAIN_FILE=$NDK_ROOT/build/cmake/android.toolchain.cmake -DANDROID_STL=c++_static -DANDROID_ABI=$ANDROID_ABI . -B $BUILD_DIR
|
||||||
${ANDROID_SDK_ROOT}/cmake/3.30.3/bin/cmake --build $BUILD_DIR --target azahar_libretro --config Release -j $(nproc)
|
${ANDROID_SDK_ROOT}/cmake/3.30.3/bin/cmake --build $BUILD_DIR --target azahar_libretro --config Release -j $(nproc)
|
||||||
llvm-strip -s $BUILD_DIR/$EXTRA_PATH/azahar_libretro.*
|
|
||||||
- name: Pack
|
- name: Pack
|
||||||
run: ./.ci/libretro-pack.sh
|
run: ./.ci/libretro-pack.sh
|
||||||
- name: Generate SBOM
|
|
||||||
if: ${{ github.ref_type == 'tag' }}
|
|
||||||
uses: anchore/sbom-action@v0
|
|
||||||
with:
|
|
||||||
path: build/
|
|
||||||
format: spdx-json
|
|
||||||
output-file: libretro-android.spdx.json
|
|
||||||
upload-artifact: false
|
|
||||||
- name: Upload
|
- name: Upload
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: ${{ env.OS }}-${{ env.TARGET }}
|
name: ${{ env.OS }}-${{ env.TARGET }}
|
||||||
path: |
|
path: ./*.zip
|
||||||
./*.zip
|
linux:
|
||||||
./*.spdx.json
|
|
||||||
- name: Attest artifacts
|
|
||||||
if: ${{ github.ref_type == 'tag' }}
|
|
||||||
uses: actions/attest@v4
|
|
||||||
with:
|
|
||||||
subject-path: |
|
|
||||||
./*.zip
|
|
||||||
sbom-path: libretro-android.spdx.json
|
|
||||||
|
|
||||||
libretro-linux:
|
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
env:
|
env:
|
||||||
OS: linux
|
OS: linux
|
||||||
|
|
@ -85,44 +57,21 @@ jobs:
|
||||||
EXTRA_PATH: bin/Release
|
EXTRA_PATH: bin/Release
|
||||||
EXTRA_CORE_ARGS: -DCMAKE_C_COMPILER=gcc-12 -DCMAKE_CXX_COMPILER=g++-12 -DENABLE_LTO=OFF
|
EXTRA_CORE_ARGS: -DCMAKE_C_COMPILER=gcc-12 -DCMAKE_CXX_COMPILER=g++-12 -DENABLE_LTO=OFF
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
- name: Install tools
|
|
||||||
run: |
|
|
||||||
sudo apt-get update -y
|
|
||||||
sudo apt-get install -y llvm
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
cmake $CORE_ARGS $EXTRA_CORE_ARGS . -B $BUILD_DIR
|
cmake $CORE_ARGS $EXTRA_CORE_ARGS . -B $BUILD_DIR
|
||||||
cmake --build $BUILD_DIR --target azahar_libretro --config Release -j $(nproc)
|
cmake --build $BUILD_DIR --target azahar_libretro --config Release -j $(nproc)
|
||||||
llvm-strip -s $BUILD_DIR/$EXTRA_PATH/azahar_libretro.*
|
|
||||||
- name: Pack
|
- name: Pack
|
||||||
run: ./.ci/libretro-pack.sh
|
run: ./.ci/libretro-pack.sh
|
||||||
- name: Generate SBOM
|
|
||||||
if: ${{ github.ref_type == 'tag' }}
|
|
||||||
uses: anchore/sbom-action@v0
|
|
||||||
with:
|
|
||||||
path: build/
|
|
||||||
format: spdx-json
|
|
||||||
output-file: libretro-linux.spdx.json
|
|
||||||
upload-artifact: false
|
|
||||||
- name: Upload
|
- name: Upload
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: ${{ env.OS }}-${{ env.TARGET }}
|
name: ${{ env.OS }}-${{ env.TARGET }}
|
||||||
path: |
|
path: ./*.zip
|
||||||
./*.zip
|
windows:
|
||||||
./*.spdx.json
|
|
||||||
- name: Attest artifacts
|
|
||||||
if: ${{ github.ref_type == 'tag' }}
|
|
||||||
uses: actions/attest@v4
|
|
||||||
with:
|
|
||||||
subject-path: |
|
|
||||||
./*.zip
|
|
||||||
sbom-path: libretro-linux.spdx.json
|
|
||||||
|
|
||||||
libretro-windows:
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
env:
|
env:
|
||||||
OS: windows
|
OS: windows
|
||||||
|
|
@ -130,10 +79,10 @@ jobs:
|
||||||
BUILD_DIR: build/windows-x86_64
|
BUILD_DIR: build/windows-x86_64
|
||||||
EXTRA_CORE_ARGS: -DENABLE_LTO=OFF -G Ninja
|
EXTRA_CORE_ARGS: -DENABLE_LTO=OFF -G Ninja
|
||||||
CMAKE: x86_64-w64-mingw32.static-cmake
|
CMAKE: x86_64-w64-mingw32.static-cmake
|
||||||
IMAGE: reallibretroretroarch/libretro-build-mxe-win-cross-cores:mingw12
|
IMAGE: git.libretro.com:5050/libretro-infrastructure/libretro-build-mxe-win-cross-cores:mingw12
|
||||||
EXTRA_PATH: bin/Release
|
EXTRA_PATH: bin/Release
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
- name: Build in cross-container
|
- name: Build in cross-container
|
||||||
|
|
@ -145,36 +94,17 @@ jobs:
|
||||||
$IMAGE \
|
$IMAGE \
|
||||||
bash -lc "\
|
bash -lc "\
|
||||||
${CMAKE} $CORE_ARGS $EXTRA_CORE_ARGS . -B $BUILD_DIR && \
|
${CMAKE} $CORE_ARGS $EXTRA_CORE_ARGS . -B $BUILD_DIR && \
|
||||||
${CMAKE} --build $BUILD_DIR --target azahar_libretro --config Release -j $(nproc) && \
|
${CMAKE} --build $BUILD_DIR --target azahar_libretro --config Release -j $(nproc)"
|
||||||
x86_64-w64-mingw32.static-strip -s $BUILD_DIR/$EXTRA_PATH/azahar_libretro.*"
|
|
||||||
- name: Pack
|
- name: Pack
|
||||||
run: ./.ci/libretro-pack.sh
|
run: ./.ci/libretro-pack.sh
|
||||||
- name: Generate SBOM
|
|
||||||
if: ${{ github.ref_type == 'tag' }}
|
|
||||||
uses: anchore/sbom-action@v0
|
|
||||||
with:
|
|
||||||
path: build/
|
|
||||||
format: spdx-json
|
|
||||||
output-file: libretro-windows.spdx.json
|
|
||||||
upload-artifact: false
|
|
||||||
- name: Upload
|
- name: Upload
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: ${{ env.OS }}-${{ env.TARGET }}
|
name: ${{ env.OS }}-${{ env.TARGET }}
|
||||||
path: |
|
path: ./*.zip
|
||||||
./*.zip
|
macos:
|
||||||
./*.spdx.json
|
|
||||||
- name: Attest artifacts
|
|
||||||
if: ${{ github.ref_type == 'tag' }}
|
|
||||||
uses: actions/attest@v4
|
|
||||||
with:
|
|
||||||
subject-path: |
|
|
||||||
./*.zip
|
|
||||||
sbom-path: libretro-windows.spdx.json
|
|
||||||
libretro-macos:
|
|
||||||
runs-on: macos-26
|
runs-on: macos-26
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
matrix:
|
||||||
target: ["x86_64", "arm64"]
|
target: ["x86_64", "arm64"]
|
||||||
env:
|
env:
|
||||||
|
|
@ -184,7 +114,7 @@ jobs:
|
||||||
BUILD_DIR: build/osx-${{ matrix.target }}
|
BUILD_DIR: build/osx-${{ matrix.target }}
|
||||||
EXTRA_PATH: bin/Release
|
EXTRA_PATH: bin/Release
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
- name: Install tools
|
- name: Install tools
|
||||||
|
|
@ -193,33 +123,14 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
cmake $CORE_ARGS -DCMAKE_OSX_ARCHITECTURES=$TARGET . -B $BUILD_DIR
|
cmake $CORE_ARGS -DCMAKE_OSX_ARCHITECTURES=$TARGET . -B $BUILD_DIR
|
||||||
cmake --build $BUILD_DIR --target azahar_libretro --config Release
|
cmake --build $BUILD_DIR --target azahar_libretro --config Release
|
||||||
strip -x $BUILD_DIR/$EXTRA_PATH/azahar_libretro.*
|
|
||||||
- name: Pack
|
- name: Pack
|
||||||
run: ./.ci/libretro-pack.sh
|
run: ./.ci/libretro-pack.sh
|
||||||
- name: Generate SBOM
|
|
||||||
if: ${{ github.ref_type == 'tag' }}
|
|
||||||
uses: anchore/sbom-action@v0
|
|
||||||
with:
|
|
||||||
path: build/
|
|
||||||
format: spdx-json
|
|
||||||
output-file: libretro-macos-${{ matrix.target }}.spdx.json
|
|
||||||
upload-artifact: false
|
|
||||||
- name: Upload
|
- name: Upload
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: ${{ env.OS }}-${{ env.TARGET }}
|
name: ${{ env.OS }}-${{ env.TARGET }}
|
||||||
path: |
|
path: ./*.zip
|
||||||
./*.zip
|
ios:
|
||||||
./*.spdx.json
|
|
||||||
- name: Attest artifacts
|
|
||||||
if: ${{ github.ref_type == 'tag' }}
|
|
||||||
uses: actions/attest@v4
|
|
||||||
with:
|
|
||||||
subject-path: |
|
|
||||||
./*.zip
|
|
||||||
sbom-path: libretro-macos-${{ matrix.target }}.spdx.json
|
|
||||||
|
|
||||||
libretro-ios:
|
|
||||||
runs-on: macos-26
|
runs-on: macos-26
|
||||||
env:
|
env:
|
||||||
OS: ios
|
OS: ios
|
||||||
|
|
@ -228,40 +139,21 @@ jobs:
|
||||||
EXTRA_PATH: bin/Release
|
EXTRA_PATH: bin/Release
|
||||||
EXTRA_CORE_ARGS: -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_C_FLAGS=-DIOS -DCMAKE_CXX_FLAGS=-DIOS -DIOS=ON -DCMAKE_SYSTEM_NAME=iOS -DCMAKE_OSX_DEPLOYMENT_TARGET=14.0 -DCITRA_USE_PRECOMPILED_HEADERS=OFF -DCMAKE_OSX_ARCHITECTURES=arm64 -DENABLE_OPT=OFF
|
EXTRA_CORE_ARGS: -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_C_FLAGS=-DIOS -DCMAKE_CXX_FLAGS=-DIOS -DIOS=ON -DCMAKE_SYSTEM_NAME=iOS -DCMAKE_OSX_DEPLOYMENT_TARGET=14.0 -DCITRA_USE_PRECOMPILED_HEADERS=OFF -DCMAKE_OSX_ARCHITECTURES=arm64 -DENABLE_OPT=OFF
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
cmake $CORE_ARGS $EXTRA_CORE_ARGS . -B $BUILD_DIR
|
cmake $CORE_ARGS $EXTRA_CORE_ARGS . -B $BUILD_DIR
|
||||||
cmake --build $BUILD_DIR --target azahar_libretro --config Release
|
cmake --build $BUILD_DIR --target azahar_libretro --config Release
|
||||||
strip -x $BUILD_DIR/$EXTRA_PATH/azahar_libretro.*
|
|
||||||
- name: Pack
|
- name: Pack
|
||||||
run: ./.ci/libretro-pack.sh
|
run: ./.ci/libretro-pack.sh
|
||||||
- name: Generate SBOM
|
|
||||||
if: ${{ github.ref_type == 'tag' }}
|
|
||||||
uses: anchore/sbom-action@v0
|
|
||||||
with:
|
|
||||||
path: build/
|
|
||||||
format: spdx-json
|
|
||||||
output-file: libretro-ios.spdx.json
|
|
||||||
upload-artifact: false
|
|
||||||
- name: Upload
|
- name: Upload
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: ${{ env.OS }}-${{ env.TARGET }}
|
name: ${{ env.OS }}-${{ env.TARGET }}
|
||||||
path: |
|
path: ./*.zip
|
||||||
./*.zip
|
tvos:
|
||||||
./*.spdx.json
|
|
||||||
- name: Attest artifacts
|
|
||||||
if: ${{ github.ref_type == 'tag' }}
|
|
||||||
uses: actions/attest@v4
|
|
||||||
with:
|
|
||||||
subject-path: |
|
|
||||||
./*.zip
|
|
||||||
sbom-path: libretro-ios.spdx.json
|
|
||||||
|
|
||||||
libretro-tvos:
|
|
||||||
runs-on: macos-26
|
runs-on: macos-26
|
||||||
env:
|
env:
|
||||||
OS: tvos
|
OS: tvos
|
||||||
|
|
@ -270,35 +162,17 @@ jobs:
|
||||||
EXTRA_PATH: bin/Release
|
EXTRA_PATH: bin/Release
|
||||||
EXTRA_CORE_ARGS: -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_C_FLAGS=-DIOS -DCMAKE_CXX_FLAGS=-DIOS -DIOS=ON -DCMAKE_SYSTEM_NAME=tvOS -DCMAKE_OSX_DEPLOYMENT_TARGET=14.0 -DCITRA_USE_PRECOMPILED_HEADERS=OFF -DCMAKE_OSX_SYSROOT=appletvos -DCMAKE_OSX_ARCHITECTURES=arm64 -DENABLE_OPT=OFF
|
EXTRA_CORE_ARGS: -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_C_FLAGS=-DIOS -DCMAKE_CXX_FLAGS=-DIOS -DIOS=ON -DCMAKE_SYSTEM_NAME=tvOS -DCMAKE_OSX_DEPLOYMENT_TARGET=14.0 -DCITRA_USE_PRECOMPILED_HEADERS=OFF -DCMAKE_OSX_SYSROOT=appletvos -DCMAKE_OSX_ARCHITECTURES=arm64 -DENABLE_OPT=OFF
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
cmake $CORE_ARGS $EXTRA_CORE_ARGS . -B $BUILD_DIR
|
cmake $CORE_ARGS $EXTRA_CORE_ARGS . -B $BUILD_DIR
|
||||||
cmake --build $BUILD_DIR --target azahar_libretro --config Release
|
cmake --build $BUILD_DIR --target azahar_libretro --config Release
|
||||||
strip -x $BUILD_DIR/$EXTRA_PATH/azahar_libretro.*
|
|
||||||
- name: Pack
|
- name: Pack
|
||||||
run: ./.ci/libretro-pack.sh
|
run: ./.ci/libretro-pack.sh
|
||||||
- name: Generate SBOM
|
|
||||||
if: ${{ github.ref_type == 'tag' }}
|
|
||||||
uses: anchore/sbom-action@v0
|
|
||||||
with:
|
|
||||||
path: build/
|
|
||||||
format: spdx-json
|
|
||||||
output-file: libretro-tvos.spdx.json
|
|
||||||
upload-artifact: false
|
|
||||||
- name: Upload
|
- name: Upload
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: ${{ env.OS }}-${{ env.TARGET }}
|
name: ${{ env.OS }}-${{ env.TARGET }}
|
||||||
path: |
|
path: ./*.zip
|
||||||
./*.zip
|
|
||||||
./*.spdx.json
|
|
||||||
- name: Attest artifacts
|
|
||||||
if: ${{ github.ref_type == 'tag' }}
|
|
||||||
uses: actions/attest@v4
|
|
||||||
with:
|
|
||||||
subject-path: |
|
|
||||||
./*.zip
|
|
||||||
sbom-path: libretro-tvos.spdx.json
|
|
||||||
|
|
|
||||||
2
.github/workflows/license-header.yml
vendored
2
.github/workflows/license-header.yml
vendored
|
|
@ -11,7 +11,7 @@ jobs:
|
||||||
image: opensauce04/azahar-build-environment:latest
|
image: opensauce04/azahar-build-environment:latest
|
||||||
options: -u 1001
|
options: -u 1001
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Fetch master branch
|
- name: Fetch master branch
|
||||||
|
|
|
||||||
2
.github/workflows/stale.yml
vendored
2
.github/workflows/stale.yml
vendored
|
|
@ -10,7 +10,7 @@ jobs:
|
||||||
permissions:
|
permissions:
|
||||||
issues: write
|
issues: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/stale@v10.2.0
|
- uses: actions/stale@v9.1.0
|
||||||
with:
|
with:
|
||||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
days-before-issue-stale: 90
|
days-before-issue-stale: 90
|
||||||
|
|
|
||||||
4
.github/workflows/transifex.yml
vendored
4
.github/workflows/transifex.yml
vendored
|
|
@ -7,10 +7,10 @@ on:
|
||||||
jobs:
|
jobs:
|
||||||
transifex:
|
transifex:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container: opensauce04/azahar-build-environment:latest
|
container: opensauce04/azahar-build-environment:transifex
|
||||||
if: ${{ github.repository == 'azahar-emu/azahar' }}
|
if: ${{ github.repository == 'azahar-emu/azahar' }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
|
||||||
6
.gitignore
vendored
6
.gitignore
vendored
|
|
@ -17,8 +17,7 @@ src/common/scm_rev.cpp
|
||||||
*.swp
|
*.swp
|
||||||
*.kdev4
|
*.kdev4
|
||||||
.markdown-preview.html
|
.markdown-preview.html
|
||||||
.idea/*
|
.idea/
|
||||||
!.idea/inspectionProfiles
|
|
||||||
.vs/
|
.vs/
|
||||||
.vscode/
|
.vscode/
|
||||||
.cache/
|
.cache/
|
||||||
|
|
@ -61,6 +60,3 @@ VULKAN_SDK/
|
||||||
# Version info files
|
# Version info files
|
||||||
GIT-COMMIT
|
GIT-COMMIT
|
||||||
GIT-TAG
|
GIT-TAG
|
||||||
|
|
||||||
# verify-release.sh downloads
|
|
||||||
verify/
|
|
||||||
14
.gitmodules
vendored
14
.gitmodules
vendored
|
|
@ -55,12 +55,18 @@
|
||||||
[submodule "sdl2"]
|
[submodule "sdl2"]
|
||||||
path = externals/sdl2/SDL
|
path = externals/sdl2/SDL
|
||||||
url = https://github.com/libsdl-org/SDL
|
url = https://github.com/libsdl-org/SDL
|
||||||
|
[submodule "cryptopp-cmake"]
|
||||||
|
path = externals/cryptopp-cmake
|
||||||
|
url = https://github.com/abdes/cryptopp-cmake.git
|
||||||
|
[submodule "cryptopp"]
|
||||||
|
path = externals/cryptopp
|
||||||
|
url = https://github.com/weidai11/cryptopp.git
|
||||||
[submodule "dds-ktx"]
|
[submodule "dds-ktx"]
|
||||||
path = externals/dds-ktx
|
path = externals/dds-ktx
|
||||||
url = https://github.com/septag/dds-ktx
|
url = https://github.com/septag/dds-ktx
|
||||||
[submodule "openal-soft"]
|
[submodule "openal-soft"]
|
||||||
path = externals/openal-soft
|
path = externals/openal-soft
|
||||||
url = https://github.com/azahar-emu/openal-soft
|
url = https://github.com/kcat/openal-soft
|
||||||
[submodule "glslang"]
|
[submodule "glslang"]
|
||||||
path = externals/glslang
|
path = externals/glslang
|
||||||
url = https://github.com/KhronosGroup/glslang
|
url = https://github.com/KhronosGroup/glslang
|
||||||
|
|
@ -100,9 +106,3 @@
|
||||||
[submodule "externals/libretro-common"]
|
[submodule "externals/libretro-common"]
|
||||||
path = externals/libretro-common/libretro-common
|
path = externals/libretro-common/libretro-common
|
||||||
url = https://github.com/libretro/libretro-common.git
|
url = https://github.com/libretro/libretro-common.git
|
||||||
[submodule "dllwalker"]
|
|
||||||
path = externals/dllwalker
|
|
||||||
url = https://github.com/azahar-emu/dllwalker
|
|
||||||
[submodule "externals/cryptopp"]
|
|
||||||
path = externals/cryptopp
|
|
||||||
url = https://github.com/cryptopp-modern/cryptopp-modern.git
|
|
||||||
|
|
|
||||||
|
|
@ -13,9 +13,6 @@ cmake_policy(SET CMP0063 NEW)
|
||||||
cmake_policy(SET CMP0127 NEW)
|
cmake_policy(SET CMP0127 NEW)
|
||||||
set(CMAKE_POLICY_DEFAULT_CMP0063 NEW)
|
set(CMAKE_POLICY_DEFAULT_CMP0063 NEW)
|
||||||
|
|
||||||
# Prefer building bundled dependencies as static instead of shared
|
|
||||||
set(BUILD_SHARED_LIBS OFF)
|
|
||||||
|
|
||||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules")
|
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules")
|
||||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/externals/cmake-modules")
|
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/externals/cmake-modules")
|
||||||
include(DownloadExternals)
|
include(DownloadExternals)
|
||||||
|
|
@ -28,15 +25,6 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Darwin" OR CMAKE_SYSTEM_NAME STREQUAL "iOS")
|
||||||
enable_language(OBJC OBJCXX)
|
enable_language(OBJC OBJCXX)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux" AND MINGW)
|
|
||||||
string(TOLOWER ${LIBTYPE} LIBTYPE_LOWER)
|
|
||||||
set(CMAKE_AR x86_64-w64-mingw32.${LIBTYPE_LOWER}-gcc-ar)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (BSD STREQUAL "OpenBSD")
|
|
||||||
add_link_options(-z wxneeded)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
option(ENABLE_LIBRETRO "Build as a LibRetro core" OFF)
|
option(ENABLE_LIBRETRO "Build as a LibRetro core" OFF)
|
||||||
|
|
||||||
# Some submodules like to pick their own default build type if not specified.
|
# Some submodules like to pick their own default build type if not specified.
|
||||||
|
|
@ -107,7 +95,7 @@ endif()
|
||||||
|
|
||||||
# Track which options were explicitly set by the user (for libretro conflict detection)
|
# Track which options were explicitly set by the user (for libretro conflict detection)
|
||||||
set(_LIBRETRO_INCOMPATIBLE_OPTIONS
|
set(_LIBRETRO_INCOMPATIBLE_OPTIONS
|
||||||
ENABLE_SDL2 ENABLE_QT ENABLE_WEB_SERVICE ENABLE_SCRIPTING ENABLE_GDBSTUB
|
ENABLE_SDL2 ENABLE_QT ENABLE_WEB_SERVICE ENABLE_SCRIPTING
|
||||||
ENABLE_OPENAL ENABLE_ROOM ENABLE_ROOM_STANDALONE ENABLE_CUBEB ENABLE_LIBUSB)
|
ENABLE_OPENAL ENABLE_ROOM ENABLE_ROOM_STANDALONE ENABLE_CUBEB ENABLE_LIBUSB)
|
||||||
set(_USER_SET_OPTIONS "")
|
set(_USER_SET_OPTIONS "")
|
||||||
foreach(_opt IN LISTS _LIBRETRO_INCOMPATIBLE_OPTIONS)
|
foreach(_opt IN LISTS _LIBRETRO_INCOMPATIBLE_OPTIONS)
|
||||||
|
|
@ -121,7 +109,7 @@ option(USE_SYSTEM_SDL2 "Use the system SDL2 lib (instead of the bundled one)" OF
|
||||||
|
|
||||||
# Set bundled qt as dependent options.
|
# Set bundled qt as dependent options.
|
||||||
option(ENABLE_QT "Enable the Qt frontend" ON)
|
option(ENABLE_QT "Enable the Qt frontend" ON)
|
||||||
option(ENABLE_QT_TRANSLATION "Enable translations for the Qt frontend" ON)
|
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)
|
||||||
|
|
@ -130,7 +118,6 @@ CMAKE_DEPENDENT_OPTION(ENABLE_ROOM_STANDALONE "Enable generating a standalone de
|
||||||
|
|
||||||
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)
|
||||||
option(ENABLE_GDBSTUB "Enable GDB stub for emulated applications" ON)
|
|
||||||
|
|
||||||
CMAKE_DEPENDENT_OPTION(ENABLE_CUBEB "Enables the cubeb audio backend" ON "NOT IOS" OFF)
|
CMAKE_DEPENDENT_OPTION(ENABLE_CUBEB "Enables the cubeb audio backend" ON "NOT IOS" OFF)
|
||||||
option(ENABLE_OPENAL "Enables the OpenAL audio backend" ON)
|
option(ENABLE_OPENAL "Enables the OpenAL audio backend" ON)
|
||||||
|
|
@ -139,10 +126,9 @@ CMAKE_DEPENDENT_OPTION(ENABLE_LIBUSB "Enable libusb for GameCube Adapter support
|
||||||
|
|
||||||
CMAKE_DEPENDENT_OPTION(ENABLE_SOFTWARE_RENDERER "Enables the software renderer" ON "NOT ANDROID" OFF)
|
CMAKE_DEPENDENT_OPTION(ENABLE_SOFTWARE_RENDERER "Enables the software renderer" ON "NOT ANDROID" OFF)
|
||||||
CMAKE_DEPENDENT_OPTION(ENABLE_OPENGL "Enables the OpenGL renderer" ${DEFAULT_ENABLE_OPENGL} "NOT APPLE" OFF)
|
CMAKE_DEPENDENT_OPTION(ENABLE_OPENGL "Enables the OpenGL renderer" ${DEFAULT_ENABLE_OPENGL} "NOT APPLE" OFF)
|
||||||
# NetBSD doesn't support Vulkan yet, remove this check when it does.
|
option(ENABLE_VULKAN "Enables the Vulkan renderer" ON)
|
||||||
CMAKE_DEPENDENT_OPTION(ENABLE_VULKAN "Enables the Vulkan renderer" ON "NOT (BSD MATCHES \"NetBSD\")" OFF)
|
|
||||||
|
|
||||||
option(ENABLE_DISCORD_RPC "Enables Discord Rich Presence" OFF)
|
option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF)
|
||||||
|
|
||||||
option(ENABLE_MICROPROFILE "Enables microprofile capabilities" OFF)
|
option(ENABLE_MICROPROFILE "Enables microprofile capabilities" OFF)
|
||||||
|
|
||||||
|
|
@ -150,8 +136,6 @@ option(ENABLE_SSE42 "Enable SSE4.2 optimizations on x86_64" ON)
|
||||||
|
|
||||||
option(ENABLE_DEVELOPER_OPTIONS "Enable functionality targeted at emulator developers" OFF)
|
option(ENABLE_DEVELOPER_OPTIONS "Enable functionality targeted at emulator developers" OFF)
|
||||||
|
|
||||||
option(ENABLE_BUILTIN_KEYBLOB "Enable the inclusion of the default crypto keys blob" ON)
|
|
||||||
|
|
||||||
# Compile options
|
# Compile options
|
||||||
CMAKE_DEPENDENT_OPTION(COMPILE_WITH_DWARF "Add DWARF debugging information" ${IS_DEBUG_BUILD} "MINGW" OFF)
|
CMAKE_DEPENDENT_OPTION(COMPILE_WITH_DWARF "Add DWARF debugging information" ${IS_DEBUG_BUILD} "MINGW" OFF)
|
||||||
option(ENABLE_LTO "Enable link time optimization" ${DEFAULT_ENABLE_LTO})
|
option(ENABLE_LTO "Enable link time optimization" ${DEFAULT_ENABLE_LTO})
|
||||||
|
|
@ -411,21 +395,13 @@ if (APPLE)
|
||||||
endif()
|
endif()
|
||||||
find_library(AVFOUNDATION_LIBRARY AVFoundation REQUIRED)
|
find_library(AVFOUNDATION_LIBRARY AVFoundation REQUIRED)
|
||||||
find_library(IOSURFACE_LIBRARY IOSurface REQUIRED)
|
find_library(IOSURFACE_LIBRARY IOSurface REQUIRED)
|
||||||
set(PLATFORM_LIBRARIES ${COCOA_LIBRARY} ${AVFOUNDATION_LIBRARY} ${IOSURFACE_LIBRARY})
|
set(PLATFORM_LIBRARIES ${COCOA_LIBRARY} ${AVFOUNDATION_LIBRARY} ${IOSURFACE_LIBRARY} ${MOLTENVK_LIBRARY})
|
||||||
|
|
||||||
if (ENABLE_VULKAN AND NOT ENABLE_LIBRETRO)
|
if (ENABLE_VULKAN AND NOT ENABLE_LIBRETRO)
|
||||||
if (USE_SYSTEM_MOLTENVK)
|
if (NOT USE_SYSTEM_MOLTENVK)
|
||||||
find_library(MOLTENVK_LIBRARY MoltenVK REQUIRED)
|
|
||||||
else()
|
|
||||||
download_moltenvk()
|
download_moltenvk()
|
||||||
if (IOS)
|
|
||||||
set(MOLTENVK_RELATIVE_LIBPATH "static/MoltenVK.xcframework/ios-arm64/libMoltenVK.a")
|
|
||||||
else()
|
|
||||||
set(MOLTENVK_RELATIVE_LIBPATH "dynamic/dylib/macOS/libMoltenVK.dylib")
|
|
||||||
endif()
|
endif()
|
||||||
set(MOLTENVK_LIBRARY "${CMAKE_BINARY_DIR}/externals/MoltenVK/MoltenVK/${MOLTENVK_RELATIVE_LIBPATH}")
|
find_library(MOLTENVK_LIBRARY MoltenVK REQUIRED)
|
||||||
endif()
|
|
||||||
|
|
||||||
message(STATUS "Using MoltenVK at ${MOLTENVK_LIBRARY}.")
|
message(STATUS "Using MoltenVK at ${MOLTENVK_LIBRARY}.")
|
||||||
set(PLATFORM_LIBRARIES ${PLATFORM_LIBRARIES} ${MOLTENVK_LIBRARY})
|
set(PLATFORM_LIBRARIES ${PLATFORM_LIBRARIES} ${MOLTENVK_LIBRARY})
|
||||||
endif()
|
endif()
|
||||||
|
|
|
||||||
|
|
@ -198,10 +198,6 @@ if (BUNDLE_TARGET_EXECUTE)
|
||||||
|
|
||||||
# On Linux, always bundle an AppImage.
|
# On Linux, always bundle an AppImage.
|
||||||
if (CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux")
|
if (CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux")
|
||||||
if (IS_MINGW)
|
|
||||||
return()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (IN_PLACE)
|
if (IN_PLACE)
|
||||||
message(FATAL_ERROR "Cannot bundle for Linux in-place.")
|
message(FATAL_ERROR "Cannot bundle for Linux in-place.")
|
||||||
endif()
|
endif()
|
||||||
|
|
@ -277,13 +273,6 @@ else()
|
||||||
|
|
||||||
# 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")
|
||||||
if (MINGW)
|
|
||||||
add_custom_command(
|
|
||||||
TARGET bundle
|
|
||||||
# The target here is arbitrary
|
|
||||||
COMMAND cp -r "$<TARGET_FILE_DIR:citra_meta>/*" "${CMAKE_BINARY_DIR}/bundle/"
|
|
||||||
POST_BUILD)
|
|
||||||
else()
|
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
TARGET bundle
|
TARGET bundle
|
||||||
COMMAND ${CMAKE_COMMAND}
|
COMMAND ${CMAKE_COMMAND}
|
||||||
|
|
@ -294,7 +283,6 @@ else()
|
||||||
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
|
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
|
||||||
POST_BUILD)
|
POST_BUILD)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
# Adds a target to the bundle target, packing in required libraries.
|
# Adds a target to the bundle target, packing in required libraries.
|
||||||
|
|
@ -305,11 +293,6 @@ else()
|
||||||
create_base_bundle_target()
|
create_base_bundle_target()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (CMAKE_HOST_SYSTEM STREQUAL "Linux" AND MINGW)
|
|
||||||
# We don't really need to "bundle" MXE builds, so don't do anything
|
|
||||||
return()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(bundle_executable_path "$<TARGET_FILE:${target_name}>")
|
set(bundle_executable_path "$<TARGET_FILE:${target_name}>")
|
||||||
if (bundle_qt AND APPLE)
|
if (bundle_qt AND APPLE)
|
||||||
# For Qt targets on Apple, expect an app bundle.
|
# For Qt targets on Apple, expect an app bundle.
|
||||||
|
|
@ -348,7 +331,6 @@ else()
|
||||||
"-DBUNDLE_LIBRARY_PATHS=\"${bundle_library_paths}\""
|
"-DBUNDLE_LIBRARY_PATHS=\"${bundle_library_paths}\""
|
||||||
"-DBUNDLE_QT=${bundle_qt}"
|
"-DBUNDLE_QT=${bundle_qt}"
|
||||||
"-DIN_PLACE=${in_place}"
|
"-DIN_PLACE=${in_place}"
|
||||||
"-DIS_MINGW=${MINGW}"
|
|
||||||
"-DLINUXDEPLOY=${CMAKE_BINARY_DIR}/externals/linuxdeploy/squashfs-root/AppRun"
|
"-DLINUXDEPLOY=${CMAKE_BINARY_DIR}/externals/linuxdeploy/squashfs-root/AppRun"
|
||||||
-P "${CMAKE_SOURCE_DIR}/CMakeModules/BundleTarget.cmake"
|
-P "${CMAKE_SOURCE_DIR}/CMakeModules/BundleTarget.cmake"
|
||||||
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}")
|
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}")
|
||||||
|
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
function(disable_pax_mprotect target)
|
|
||||||
if (BSD STREQUAL "NetBSD")
|
|
||||||
add_custom_command(TARGET ${target} POST_BUILD
|
|
||||||
COMMAND paxctl +m "$<TARGET_FILE:${target}>"
|
|
||||||
COMMENT "Disabling PaX MPROTECT restrictions for '${target}'"
|
|
||||||
VERBATIM
|
|
||||||
)
|
|
||||||
else()
|
|
||||||
message(FATAL_ERROR "disable_pax_mprotect only applies on NetBSD.")
|
|
||||||
endif()
|
|
||||||
endfunction()
|
|
||||||
|
|
@ -171,16 +171,27 @@ function(download_qt target)
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
function(download_moltenvk)
|
function(download_moltenvk)
|
||||||
|
if (IOS)
|
||||||
|
set(MOLTENVK_PLATFORM "static/MoltenVK.xcframework/ios-arm64")
|
||||||
|
else()
|
||||||
|
set(MOLTENVK_PLATFORM "dynamic/dylib/macOS")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(MOLTENVK_DIR "${CMAKE_BINARY_DIR}/externals/MoltenVK")
|
||||||
set(MOLTENVK_TAR "${CMAKE_BINARY_DIR}/externals/MoltenVK.tar")
|
set(MOLTENVK_TAR "${CMAKE_BINARY_DIR}/externals/MoltenVK.tar")
|
||||||
if (NOT EXISTS "${CMAKE_BINARY_DIR}/externals/MoltenVK")
|
if (NOT EXISTS ${MOLTENVK_DIR})
|
||||||
if (NOT EXISTS ${MOLTENVK_TAR})
|
if (NOT EXISTS ${MOLTENVK_TAR})
|
||||||
file(DOWNLOAD https://github.com/KhronosGroup/MoltenVK/releases/download/v1.4.1/MoltenVK-all.tar
|
file(DOWNLOAD https://github.com/KhronosGroup/MoltenVK/releases/download/v1.2.9/MoltenVK-all.tar
|
||||||
${MOLTENVK_TAR} SHOW_PROGRESS)
|
${MOLTENVK_TAR} SHOW_PROGRESS)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
execute_process(COMMAND ${CMAKE_COMMAND} -E tar xf "${MOLTENVK_TAR}"
|
execute_process(COMMAND ${CMAKE_COMMAND} -E tar xf "${MOLTENVK_TAR}"
|
||||||
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/externals")
|
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/externals")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# Add the MoltenVK library path to the prefix so find_library can locate it.
|
||||||
|
list(APPEND CMAKE_PREFIX_PATH "${MOLTENVK_DIR}/MoltenVK/${MOLTENVK_PLATFORM}")
|
||||||
|
set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} PARENT_SCOPE)
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
function(get_external_prefix lib_name prefix_var)
|
function(get_external_prefix lib_name prefix_var)
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,6 @@ foreach(KEY IN ITEMS
|
||||||
"use_virtual_sd"
|
"use_virtual_sd"
|
||||||
"use_custom_storage"
|
"use_custom_storage"
|
||||||
"compress_cia_installs"
|
"compress_cia_installs"
|
||||||
"async_fs_operations"
|
|
||||||
"region_value"
|
"region_value"
|
||||||
"init_clock"
|
"init_clock"
|
||||||
"init_time"
|
"init_time"
|
||||||
|
|
@ -49,7 +48,6 @@ foreach(KEY IN ITEMS
|
||||||
"texture_filter"
|
"texture_filter"
|
||||||
"texture_sampling"
|
"texture_sampling"
|
||||||
"delay_game_render_thread_us"
|
"delay_game_render_thread_us"
|
||||||
"simulate_3ds_gpu_timings"
|
|
||||||
"layout_option"
|
"layout_option"
|
||||||
"swap_screen"
|
"swap_screen"
|
||||||
"upright_screen"
|
"upright_screen"
|
||||||
|
|
@ -109,7 +107,6 @@ foreach(KEY IN ITEMS
|
||||||
"output_device"
|
"output_device"
|
||||||
"input_type"
|
"input_type"
|
||||||
"input_device"
|
"input_device"
|
||||||
"simulate_headphones_plugged"
|
|
||||||
"delay_start_for_lle_modules"
|
"delay_start_for_lle_modules"
|
||||||
"use_gdbstub"
|
"use_gdbstub"
|
||||||
"gdbstub_port"
|
"gdbstub_port"
|
||||||
|
|
@ -118,7 +115,6 @@ foreach(KEY IN ITEMS
|
||||||
"log_filter"
|
"log_filter"
|
||||||
"log_regex_filter"
|
"log_regex_filter"
|
||||||
"toggle_unique_data_console_type"
|
"toggle_unique_data_console_type"
|
||||||
"break_on_unmapped_memory_access"
|
|
||||||
"use_integer_scaling"
|
"use_integer_scaling"
|
||||||
"layouts_to_cycle"
|
"layouts_to_cycle"
|
||||||
"camera_inner_flip"
|
"camera_inner_flip"
|
||||||
|
|
@ -238,7 +234,6 @@ if (ANDROID)
|
||||||
"android_hide_images"
|
"android_hide_images"
|
||||||
"screen_orientation"
|
"screen_orientation"
|
||||||
"performance_overlay_position"
|
"performance_overlay_position"
|
||||||
"enable_secondary_display"
|
|
||||||
)
|
)
|
||||||
string(REPLACE "_" "_1" KEY_JNI_ESCAPED ${KEY})
|
string(REPLACE "_" "_1" KEY_JNI_ESCAPED ${KEY})
|
||||||
set(SETTING_KEY_LIST "${SETTING_KEY_LIST}\n\"${KEY}\",")
|
set(SETTING_KEY_LIST "${SETTING_KEY_LIST}\n\"${KEY}\",")
|
||||||
|
|
|
||||||
1
CONTRIBUTING.md
Normal file
1
CONTRIBUTING.md
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
**The Contributor's Guide has moved to [the wiki](https://github.com/citra-emu/citra/wiki/Contributing).**
|
||||||
2
dist/languages/.tx/config
vendored
2
dist/languages/.tx/config
vendored
|
|
@ -12,4 +12,4 @@ lang_map = ca@valencia:ca_ES_valencia
|
||||||
file_filter = ../../src/android/app/src/main/res/values-<lang>/strings.xml
|
file_filter = ../../src/android/app/src/main/res/values-<lang>/strings.xml
|
||||||
source_file = ../../src/android/app/src/main/res/values/strings.xml
|
source_file = ../../src/android/app/src/main/res/values/strings.xml
|
||||||
type = ANDROID
|
type = ANDROID
|
||||||
lang_map = ca@valencia:b+ca+ES+valencia, es_419:b+es+419, pt_BR:b+pt+BR, zh_CN:b+zh+CN, zh_TW:b+zh+TW
|
lang_map = es_ES:b+es+ES, hu_HU:b+hu+HU, ru_RU:b+ru+RU, pt_BR:b+pt+BR, zh_CN:b+zh+CN, pl_PL:b+pl+PL, ca@valencia:b+ca+ES+valencia, ko_KR:b+ko+KR, da_DK:b+da+DK, ja_JP:b+ja+JP, lt_LT:b+lt+LT, ro_RO:b+ro+RO, tr_TR:b+tr+TR, vi_VN:b+vi+VN, zh_TW:b+zh+TW
|
||||||
|
|
|
||||||
846
dist/languages/ca_ES_valencia.ts
vendored
846
dist/languages/ca_ES_valencia.ts
vendored
File diff suppressed because it is too large
Load diff
832
dist/languages/da.ts → dist/languages/da_DK.ts
vendored
832
dist/languages/da.ts → dist/languages/da_DK.ts
vendored
File diff suppressed because it is too large
Load diff
1070
dist/languages/de.ts
vendored
1070
dist/languages/de.ts
vendored
File diff suppressed because it is too large
Load diff
848
dist/languages/el.ts
vendored
848
dist/languages/el.ts
vendored
File diff suppressed because it is too large
Load diff
7918
dist/languages/es_419.ts
vendored
7918
dist/languages/es_419.ts
vendored
File diff suppressed because it is too large
Load diff
848
dist/languages/es.ts → dist/languages/es_ES.ts
vendored
848
dist/languages/es.ts → dist/languages/es_ES.ts
vendored
File diff suppressed because it is too large
Load diff
828
dist/languages/fi.ts
vendored
828
dist/languages/fi.ts
vendored
File diff suppressed because it is too large
Load diff
846
dist/languages/fr.ts
vendored
846
dist/languages/fr.ts
vendored
File diff suppressed because it is too large
Load diff
832
dist/languages/hu.ts → dist/languages/hu_HU.ts
vendored
832
dist/languages/hu.ts → dist/languages/hu_HU.ts
vendored
File diff suppressed because it is too large
Load diff
828
dist/languages/id.ts
vendored
828
dist/languages/id.ts
vendored
File diff suppressed because it is too large
Load diff
856
dist/languages/it.ts
vendored
856
dist/languages/it.ts
vendored
File diff suppressed because it is too large
Load diff
854
dist/languages/ja.ts → dist/languages/ja_JP.ts
vendored
854
dist/languages/ja.ts → dist/languages/ja_JP.ts
vendored
File diff suppressed because it is too large
Load diff
834
dist/languages/ko.ts → dist/languages/ko_KR.ts
vendored
834
dist/languages/ko.ts → dist/languages/ko_KR.ts
vendored
File diff suppressed because it is too large
Load diff
830
dist/languages/lt.ts → dist/languages/lt_LT.ts
vendored
830
dist/languages/lt.ts → dist/languages/lt_LT.ts
vendored
File diff suppressed because it is too large
Load diff
1270
dist/languages/nb.ts
vendored
1270
dist/languages/nb.ts
vendored
File diff suppressed because it is too large
Load diff
832
dist/languages/nl.ts
vendored
832
dist/languages/nl.ts
vendored
File diff suppressed because it is too large
Load diff
848
dist/languages/pl.ts → dist/languages/pl_PL.ts
vendored
848
dist/languages/pl.ts → dist/languages/pl_PL.ts
vendored
File diff suppressed because it is too large
Load diff
864
dist/languages/pt_BR.ts
vendored
864
dist/languages/pt_BR.ts
vendored
File diff suppressed because it is too large
Load diff
834
dist/languages/ro.ts → dist/languages/ro_RO.ts
vendored
834
dist/languages/ro.ts → dist/languages/ro_RO.ts
vendored
File diff suppressed because it is too large
Load diff
840
dist/languages/ru.ts → dist/languages/ru_RU.ts
vendored
840
dist/languages/ru.ts → dist/languages/ru_RU.ts
vendored
File diff suppressed because it is too large
Load diff
846
dist/languages/sv.ts
vendored
846
dist/languages/sv.ts
vendored
File diff suppressed because it is too large
Load diff
842
dist/languages/tr.ts → dist/languages/tr_TR.ts
vendored
842
dist/languages/tr.ts → dist/languages/tr_TR.ts
vendored
File diff suppressed because it is too large
Load diff
830
dist/languages/vi.ts → dist/languages/vi_VN.ts
vendored
830
dist/languages/vi.ts → dist/languages/vi_VN.ts
vendored
File diff suppressed because it is too large
Load diff
846
dist/languages/zh_CN.ts
vendored
846
dist/languages/zh_CN.ts
vendored
File diff suppressed because it is too large
Load diff
830
dist/languages/zh_TW.ts
vendored
830
dist/languages/zh_TW.ts
vendored
File diff suppressed because it is too large
Load diff
|
|
@ -4,19 +4,23 @@
|
||||||
# --- Builder ----------------
|
# --- Builder ----------------
|
||||||
FROM opensauce04/azahar-build-environment:latest AS builder
|
FROM opensauce04/azahar-build-environment:latest AS builder
|
||||||
|
|
||||||
RUN mkdir /var/azahar-src/
|
COPY . /var/azahar-src
|
||||||
COPY ./ /var/azahar-src/
|
|
||||||
|
|
||||||
RUN mkdir ./builddir/
|
RUN mkdir builddir && cd builddir && \
|
||||||
WORKDIR ./builddir/
|
cmake /var/azahar-src -G Ninja \
|
||||||
RUN cmake /var/azahar-src -G Ninja \
|
|
||||||
-DENABLE_QT=OFF \
|
-DENABLE_QT=OFF \
|
||||||
-DENABLE_GDBSTUB=OFF \
|
|
||||||
-DENABLE_TESTS=OFF \
|
-DENABLE_TESTS=OFF \
|
||||||
-DENABLE_ROOM=ON \
|
-DENABLE_ROOM=ON \
|
||||||
-DENABLE_ROOM_STANDALONE=ON
|
-DENABLE_ROOM_STANDALONE=ON \
|
||||||
RUN ninja
|
-DENABLE_OPENGL=OFF $( : "TODO: Can we disable these automatically when there's no frontend?") \
|
||||||
RUN mv ./bin/Release/azahar-room /usr/local/bin/
|
-DENABLE_VULKAN=OFF \
|
||||||
|
-DENABLE_SDL2=OFF \
|
||||||
|
-DENABLE_LIBUSB=OFF \
|
||||||
|
-DENABLE_CUBEB=OFF \
|
||||||
|
-DENABLE_OPENAL=OFF && \
|
||||||
|
ninja && \
|
||||||
|
mv bin/Release/azahar-room /usr/local/bin/ && \
|
||||||
|
cd .. && rm -rf builddir
|
||||||
|
|
||||||
# --- Final ------------------
|
# --- Final ------------------
|
||||||
FROM debian:trixie AS final
|
FROM debian:trixie AS final
|
||||||
|
|
|
||||||
67
externals/CMakeLists.txt
vendored
67
externals/CMakeLists.txt
vendored
|
|
@ -57,10 +57,9 @@ if (ENABLE_TESTS)
|
||||||
else()
|
else()
|
||||||
set(CATCH_INSTALL_DOCS OFF CACHE BOOL "")
|
set(CATCH_INSTALL_DOCS OFF CACHE BOOL "")
|
||||||
set(CATCH_INSTALL_EXTRAS OFF CACHE BOOL "")
|
set(CATCH_INSTALL_EXTRAS OFF CACHE BOOL "")
|
||||||
add_subdirectory(catch2 EXCLUDE_FROM_ALL)
|
add_subdirectory(catch2)
|
||||||
endif()
|
endif()
|
||||||
target_link_libraries(catch2 INTERFACE Catch2::Catch2WithMain)
|
target_link_libraries(catch2 INTERFACE Catch2::Catch2WithMain)
|
||||||
include(Catch)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Crypto++
|
# Crypto++
|
||||||
|
|
@ -69,9 +68,17 @@ if(USE_SYSTEM_CRYPTOPP)
|
||||||
add_library(cryptopp INTERFACE)
|
add_library(cryptopp INTERFACE)
|
||||||
target_link_libraries(cryptopp INTERFACE cryptopp::cryptopp)
|
target_link_libraries(cryptopp INTERFACE cryptopp::cryptopp)
|
||||||
else()
|
else()
|
||||||
|
if (WIN32 AND NOT MSVC AND "arm64" IN_LIST ARCHITECTURE)
|
||||||
|
# TODO: CryptoPP ARM64 ASM does not seem to support Windows unless compiled with MSVC.
|
||||||
|
# TODO: See https://github.com/weidai11/cryptopp/issues/1260
|
||||||
|
set(CRYPTOPP_DISABLE_ASM ON CACHE BOOL "")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(CRYPTOPP_BUILD_DOCUMENTATION OFF CACHE BOOL "")
|
||||||
set(CRYPTOPP_BUILD_TESTING OFF CACHE BOOL "")
|
set(CRYPTOPP_BUILD_TESTING OFF CACHE BOOL "")
|
||||||
set(CRYPTOPP_INSTALL OFF CACHE BOOL "")
|
set(CRYPTOPP_INSTALL OFF CACHE BOOL "")
|
||||||
add_subdirectory(cryptopp EXCLUDE_FROM_ALL)
|
set(CRYPTOPP_SOURCES "${CMAKE_SOURCE_DIR}/externals/cryptopp" CACHE STRING "")
|
||||||
|
add_subdirectory(cryptopp-cmake)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# dds-ktx
|
# dds-ktx
|
||||||
|
|
@ -104,13 +111,7 @@ endif()
|
||||||
|
|
||||||
# Oaknut
|
# Oaknut
|
||||||
if ("arm64" IN_LIST ARCHITECTURE)
|
if ("arm64" IN_LIST ARCHITECTURE)
|
||||||
if(USE_SYSTEM_OAKNUT)
|
|
||||||
find_package(oaknut REQUIRED)
|
|
||||||
add_library(oaknut INTERFACE)
|
|
||||||
target_link_libraries(oaknut INTERFACE merry::oaknut)
|
|
||||||
else()
|
|
||||||
add_subdirectory(oaknut EXCLUDE_FROM_ALL)
|
add_subdirectory(oaknut EXCLUDE_FROM_ALL)
|
||||||
endif()
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Dynarmic
|
# Dynarmic
|
||||||
|
|
@ -134,7 +135,7 @@ endif()
|
||||||
|
|
||||||
# getopt
|
# getopt
|
||||||
if (MSVC)
|
if (MSVC)
|
||||||
add_subdirectory(getopt EXCLUDE_FROM_ALL)
|
add_subdirectory(getopt)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# inih
|
# inih
|
||||||
|
|
@ -143,7 +144,7 @@ if(USE_SYSTEM_INIH)
|
||||||
add_library(inih INTERFACE)
|
add_library(inih INTERFACE)
|
||||||
target_link_libraries(inih INTERFACE inih::inih inih::inir)
|
target_link_libraries(inih INTERFACE inih::inih inih::inir)
|
||||||
else()
|
else()
|
||||||
add_subdirectory(inih EXCLUDE_FROM_ALL)
|
add_subdirectory(inih)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# MicroProfile
|
# MicroProfile
|
||||||
|
|
@ -166,7 +167,7 @@ if (NOT MSVC)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Open Source Archives
|
# Open Source Archives
|
||||||
add_subdirectory(open_source_archives EXCLUDE_FROM_ALL)
|
add_subdirectory(open_source_archives)
|
||||||
|
|
||||||
# faad2
|
# faad2
|
||||||
add_subdirectory(faad2 EXCLUDE_FROM_ALL)
|
add_subdirectory(faad2 EXCLUDE_FROM_ALL)
|
||||||
|
|
@ -205,12 +206,12 @@ add_subdirectory(teakra EXCLUDE_FROM_ALL)
|
||||||
|
|
||||||
# SDL2
|
# SDL2
|
||||||
if (ENABLE_SDL2 AND NOT USE_SYSTEM_SDL2)
|
if (ENABLE_SDL2 AND NOT USE_SYSTEM_SDL2)
|
||||||
add_subdirectory(sdl2 EXCLUDE_FROM_ALL)
|
add_subdirectory(sdl2)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# libusb
|
# libusb
|
||||||
if (ENABLE_LIBUSB AND NOT USE_SYSTEM_LIBUSB)
|
if (ENABLE_LIBUSB AND NOT USE_SYSTEM_LIBUSB)
|
||||||
add_subdirectory(libusb EXCLUDE_FROM_ALL)
|
add_subdirectory(libusb)
|
||||||
set(LIBUSB_INCLUDE_DIR "" PARENT_SCOPE)
|
set(LIBUSB_INCLUDE_DIR "" PARENT_SCOPE)
|
||||||
set(LIBUSB_LIBRARIES usb PARENT_SCOPE)
|
set(LIBUSB_LIBRARIES usb PARENT_SCOPE)
|
||||||
endif()
|
endif()
|
||||||
|
|
@ -254,7 +255,7 @@ if(USE_SYSTEM_ENET)
|
||||||
add_library(enet INTERFACE)
|
add_library(enet INTERFACE)
|
||||||
target_link_libraries(enet INTERFACE libenet::libenet)
|
target_link_libraries(enet INTERFACE libenet::libenet)
|
||||||
else()
|
else()
|
||||||
add_subdirectory(enet EXCLUDE_FROM_ALL)
|
add_subdirectory(enet)
|
||||||
target_include_directories(enet INTERFACE ./enet/include)
|
target_include_directories(enet INTERFACE ./enet/include)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
@ -276,7 +277,7 @@ if (ENABLE_CUBEB)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# DiscordRPC
|
# DiscordRPC
|
||||||
if (ENABLE_DISCORD_RPC)
|
if (USE_DISCORD_PRESENCE)
|
||||||
# rapidjson used by discord-rpc is old and doesn't correctly detect endianness for some platforms.
|
# rapidjson used by discord-rpc is old and doesn't correctly detect endianness for some platforms.
|
||||||
include(TestBigEndian)
|
include(TestBigEndian)
|
||||||
test_big_endian(RAPIDJSON_BIG_ENDIAN)
|
test_big_endian(RAPIDJSON_BIG_ENDIAN)
|
||||||
|
|
@ -319,8 +320,12 @@ endif()
|
||||||
# OpenSSL
|
# OpenSSL
|
||||||
if (USE_SYSTEM_OPENSSL)
|
if (USE_SYSTEM_OPENSSL)
|
||||||
find_package(OpenSSL 1.1)
|
find_package(OpenSSL 1.1)
|
||||||
|
if (OPENSSL_FOUND)
|
||||||
set(OPENSSL_LIBRARIES OpenSSL::SSL OpenSSL::Crypto)
|
set(OPENSSL_LIBRARIES OpenSSL::SSL OpenSSL::Crypto)
|
||||||
else()
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (NOT OPENSSL_FOUND)
|
||||||
# LibreSSL
|
# LibreSSL
|
||||||
set(LIBRESSL_SKIP_INSTALL ON CACHE BOOL "")
|
set(LIBRESSL_SKIP_INSTALL ON CACHE BOOL "")
|
||||||
set(OPENSSLDIR "/etc/ssl/")
|
set(OPENSSLDIR "/etc/ssl/")
|
||||||
|
|
@ -362,7 +367,7 @@ target_compile_options(httplib INTERFACE -DCPPHTTPLIB_OPENSSL_SUPPORT)
|
||||||
target_link_libraries(httplib INTERFACE ${OPENSSL_LIBRARIES})
|
target_link_libraries(httplib INTERFACE ${OPENSSL_LIBRARIES})
|
||||||
|
|
||||||
if (UNIX AND NOT APPLE)
|
if (UNIX AND NOT APPLE)
|
||||||
add_subdirectory(gamemode EXCLUDE_FROM_ALL)
|
add_subdirectory(gamemode)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# cpp-jwt
|
# cpp-jwt
|
||||||
|
|
@ -385,13 +390,13 @@ if(USE_SYSTEM_LODEPNG)
|
||||||
find_package(lodepng REQUIRED)
|
find_package(lodepng REQUIRED)
|
||||||
target_link_libraries(lodepng INTERFACE lodepng::lodepng)
|
target_link_libraries(lodepng INTERFACE lodepng::lodepng)
|
||||||
else()
|
else()
|
||||||
add_subdirectory(lodepng EXCLUDE_FROM_ALL)
|
add_subdirectory(lodepng)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# (xperia64): Only use libyuv on Android b/c of build issues on Windows and mandatory JPEG
|
# (xperia64): Only use libyuv on Android b/c of build issues on Windows and mandatory JPEG
|
||||||
if(ANDROID)
|
if(ANDROID)
|
||||||
# libyuv
|
# libyuv
|
||||||
add_subdirectory(libyuv EXCLUDE_FROM_ALL)
|
add_subdirectory(libyuv)
|
||||||
target_include_directories(yuv INTERFACE ./libyuv/include)
|
target_include_directories(yuv INTERFACE ./libyuv/include)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
@ -402,9 +407,6 @@ if (ENABLE_OPENAL)
|
||||||
find_package(OpenAL REQUIRED)
|
find_package(OpenAL REQUIRED)
|
||||||
target_link_libraries(OpenAL INTERFACE OpenAL::OpenAL)
|
target_link_libraries(OpenAL INTERFACE OpenAL::OpenAL)
|
||||||
else()
|
else()
|
||||||
if (BSD STREQUAL "OpenBSD")
|
|
||||||
set(ALSOFT_BACKEND_SOLARIS OFF CACHE BOOL "")
|
|
||||||
endif()
|
|
||||||
set(ALSOFT_EMBED_HRTF_DATA OFF CACHE BOOL "")
|
set(ALSOFT_EMBED_HRTF_DATA OFF CACHE BOOL "")
|
||||||
set(ALSOFT_EXAMPLES OFF CACHE BOOL "")
|
set(ALSOFT_EXAMPLES OFF CACHE BOOL "")
|
||||||
set(ALSOFT_INSTALL OFF CACHE BOOL "")
|
set(ALSOFT_INSTALL OFF CACHE BOOL "")
|
||||||
|
|
@ -420,7 +422,7 @@ endif()
|
||||||
# OpenGL dependencies
|
# OpenGL dependencies
|
||||||
if (ENABLE_OPENGL)
|
if (ENABLE_OPENGL)
|
||||||
# Glad
|
# Glad
|
||||||
add_subdirectory(glad EXCLUDE_FROM_ALL)
|
add_subdirectory(glad)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Vulkan dependencies
|
# Vulkan dependencies
|
||||||
|
|
@ -460,7 +462,7 @@ if (ENABLE_VULKAN)
|
||||||
set(ENABLE_CTEST OFF CACHE BOOL "")
|
set(ENABLE_CTEST OFF CACHE BOOL "")
|
||||||
set(ENABLE_HLSL OFF CACHE BOOL "")
|
set(ENABLE_HLSL OFF CACHE BOOL "")
|
||||||
set(BUILD_EXTERNAL OFF CACHE BOOL "")
|
set(BUILD_EXTERNAL OFF CACHE BOOL "")
|
||||||
add_subdirectory(glslang EXCLUDE_FROM_ALL)
|
add_subdirectory(glslang)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# sirit
|
# sirit
|
||||||
|
|
@ -491,24 +493,11 @@ if (ENABLE_VULKAN)
|
||||||
else()
|
else()
|
||||||
target_include_directories(vulkan-headers INTERFACE ./vulkan-headers/include)
|
target_include_directories(vulkan-headers INTERFACE ./vulkan-headers/include)
|
||||||
target_disable_warnings(vulkan-headers)
|
target_disable_warnings(vulkan-headers)
|
||||||
if (BSD STREQUAL "NetBSD")
|
|
||||||
# There may be a better way to do this with
|
|
||||||
# find_package(X11), but I couldn't get
|
|
||||||
# CMake to do it, so we're depending on
|
|
||||||
# the x11-links package and assuming the
|
|
||||||
# prefix location. -OS
|
|
||||||
target_include_directories(vulkan-headers INTERFACE
|
|
||||||
/usr/pkg/share/x11-links/include)
|
|
||||||
elseif (BSD STREQUAL "OpenBSD")
|
|
||||||
# This is fine to hardcode because it'll never change
|
|
||||||
target_include_directories(vulkan-headers INTERFACE
|
|
||||||
/usr/X11R6/include)
|
|
||||||
endif()
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# adrenotools
|
# adrenotools
|
||||||
if (ANDROID AND "arm64" IN_LIST ARCHITECTURE)
|
if (ANDROID AND "arm64" IN_LIST ARCHITECTURE)
|
||||||
add_subdirectory(libadrenotools EXCLUDE_FROM_ALL)
|
add_subdirectory(libadrenotools)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
|
||||||
2
externals/boost
vendored
2
externals/boost
vendored
|
|
@ -1 +1 @@
|
||||||
Subproject commit 6a85c3100499e886e11c87a5c2109eedacea0a61
|
Subproject commit f9b15f673a688982f78a5f63a49a27275b318e5f
|
||||||
|
|
@ -14,7 +14,6 @@ option(USE_SYSTEM_JSON "Use the system JSON (nlohmann-json3) package (instead of
|
||||||
option(USE_SYSTEM_DYNARMIC "Use the system dynarmic (instead of the bundled one)" OFF)
|
option(USE_SYSTEM_DYNARMIC "Use the system dynarmic (instead of the bundled one)" OFF)
|
||||||
option(USE_SYSTEM_FMT "Use the system fmt (instead of the bundled one)" OFF)
|
option(USE_SYSTEM_FMT "Use the system fmt (instead of the bundled one)" OFF)
|
||||||
option(USE_SYSTEM_XBYAK "Use the system xbyak (instead of the bundled one)" OFF)
|
option(USE_SYSTEM_XBYAK "Use the system xbyak (instead of the bundled one)" OFF)
|
||||||
option(USE_SYSTEM_OAKNUT "Use the system oaknut (instead of the bundled one)" OFF)
|
|
||||||
option(USE_SYSTEM_INIH "Use the system inih (instead of the bundled one)" OFF)
|
option(USE_SYSTEM_INIH "Use the system inih (instead of the bundled one)" OFF)
|
||||||
option(USE_SYSTEM_FFMPEG_HEADERS "Use the system FFmpeg headers (instead of the bundled one)" OFF)
|
option(USE_SYSTEM_FFMPEG_HEADERS "Use the system FFmpeg headers (instead of the bundled one)" OFF)
|
||||||
option(USE_SYSTEM_GLSLANG "Use the system glslang and SPIR-V libraries (instead of the bundled ones)" OFF)
|
option(USE_SYSTEM_GLSLANG "Use the system glslang and SPIR-V libraries (instead of the bundled ones)" OFF)
|
||||||
|
|
@ -41,7 +40,6 @@ CMAKE_DEPENDENT_OPTION(DISABLE_SYSTEM_JSON "Disable system JSON" OFF "USE_SYSTEM
|
||||||
CMAKE_DEPENDENT_OPTION(DISABLE_SYSTEM_DYNARMIC "Disable system Dynarmic" OFF "USE_SYSTEM_LIBS" OFF)
|
CMAKE_DEPENDENT_OPTION(DISABLE_SYSTEM_DYNARMIC "Disable system Dynarmic" OFF "USE_SYSTEM_LIBS" OFF)
|
||||||
CMAKE_DEPENDENT_OPTION(DISABLE_SYSTEM_FMT "Disable system fmt" OFF "USE_SYSTEM_LIBS" OFF)
|
CMAKE_DEPENDENT_OPTION(DISABLE_SYSTEM_FMT "Disable system fmt" OFF "USE_SYSTEM_LIBS" OFF)
|
||||||
CMAKE_DEPENDENT_OPTION(DISABLE_SYSTEM_XBYAK "Disable system xbyak" OFF "USE_SYSTEM_LIBS" OFF)
|
CMAKE_DEPENDENT_OPTION(DISABLE_SYSTEM_XBYAK "Disable system xbyak" OFF "USE_SYSTEM_LIBS" OFF)
|
||||||
CMAKE_DEPENDENT_OPTION(DISABLE_SYSTEM_OAKNUT "Disable system oaknut" OFF "USE_SYSTEM_LIBS" OFF)
|
|
||||||
CMAKE_DEPENDENT_OPTION(DISABLE_SYSTEM_INIH "Disable system inih" OFF "USE_SYSTEM_LIBS" OFF)
|
CMAKE_DEPENDENT_OPTION(DISABLE_SYSTEM_INIH "Disable system inih" OFF "USE_SYSTEM_LIBS" OFF)
|
||||||
CMAKE_DEPENDENT_OPTION(DISABLE_SYSTEM_FFMPEG_HEADERS "Disable system ffmpeg" OFF "USE_SYSTEM_LIBS" OFF)
|
CMAKE_DEPENDENT_OPTION(DISABLE_SYSTEM_FFMPEG_HEADERS "Disable system ffmpeg" OFF "USE_SYSTEM_LIBS" OFF)
|
||||||
CMAKE_DEPENDENT_OPTION(DISABLE_SYSTEM_GLSLANG "Disable system glslang" OFF "USE_SYSTEM_LIBS" OFF)
|
CMAKE_DEPENDENT_OPTION(DISABLE_SYSTEM_GLSLANG "Disable system glslang" OFF "USE_SYSTEM_LIBS" OFF)
|
||||||
|
|
@ -68,7 +66,6 @@ set(LIB_VAR_LIST
|
||||||
DYNARMIC
|
DYNARMIC
|
||||||
FMT
|
FMT
|
||||||
XBYAK
|
XBYAK
|
||||||
OAKNUT
|
|
||||||
INIH
|
INIH
|
||||||
FFMPEG_HEADERS
|
FFMPEG_HEADERS
|
||||||
GLSLANG
|
GLSLANG
|
||||||
|
|
|
||||||
2
externals/cryptopp
vendored
2
externals/cryptopp
vendored
|
|
@ -1 +1 @@
|
||||||
Subproject commit 8d92d788421483a43e09acf1cd4a2861cb2b8cab
|
Subproject commit 60f81a77e0c9a0e7ffc1ca1bc438ddfa2e43b78e
|
||||||
1
externals/cryptopp-cmake
vendored
Submodule
1
externals/cryptopp-cmake
vendored
Submodule
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 00a151f8489daaa32434ab1f340e6750793ddf0c
|
||||||
2
externals/discord-rpc
vendored
2
externals/discord-rpc
vendored
|
|
@ -1 +1 @@
|
||||||
Subproject commit 049826c3f42875c2f9599552f425e76435789cc7
|
Subproject commit cb50201fc09290cd078c7ab27917504491f7f96a
|
||||||
1
externals/dllwalker
vendored
1
externals/dllwalker
vendored
|
|
@ -1 +0,0 @@
|
||||||
Subproject commit 2f8b349c26832cae612aa7082154c0697a9cbc8e
|
|
||||||
2
externals/openal-soft
vendored
2
externals/openal-soft
vendored
|
|
@ -1 +1 @@
|
||||||
Subproject commit e399840fc6aba5f7bc3f0633e8ff10bba0640906
|
Subproject commit 90191edd20bb877c5cbddfdac7ec0fe49ad93727
|
||||||
|
|
@ -183,9 +183,6 @@ endif()
|
||||||
if(ENABLE_DEVELOPER_OPTIONS)
|
if(ENABLE_DEVELOPER_OPTIONS)
|
||||||
add_compile_definitions(ENABLE_DEVELOPER_OPTIONS)
|
add_compile_definitions(ENABLE_DEVELOPER_OPTIONS)
|
||||||
endif()
|
endif()
|
||||||
if(ENABLE_BUILTIN_KEYBLOB)
|
|
||||||
add_compile_definitions(ENABLE_BUILTIN_KEYBLOB)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
add_subdirectory(common)
|
add_subdirectory(common)
|
||||||
add_subdirectory(core)
|
add_subdirectory(core)
|
||||||
|
|
@ -203,7 +200,6 @@ if (ENABLE_QT)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (ENABLE_QT) # Or any other hypothetical future frontends
|
if (ENABLE_QT) # Or any other hypothetical future frontends
|
||||||
add_subdirectory(citra_cli)
|
|
||||||
add_subdirectory(citra_meta)
|
add_subdirectory(citra_meta)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
root = true
|
|
||||||
|
|
||||||
[*.{kt,kts}]
|
|
||||||
ij_kotlin_code_style_defaults = KOTLIN_OFFICIAL
|
|
||||||
ktlint_code_style = android_studio
|
|
||||||
|
|
||||||
# Disable wildcard imports
|
|
||||||
ij_kotlin_name_count_to_use_star_import = 2147483647
|
|
||||||
ij_kotlin_name_count_to_use_star_import_for_members = 2147483647
|
|
||||||
ij_kotlin_packages_to_use_import_on_demand = unset
|
|
||||||
|
|
||||||
# Style configuration
|
|
||||||
indent_size = 4
|
|
||||||
indent_style = space
|
|
||||||
ktlint_standard_comment-wrapping = disabled
|
|
||||||
ktlint_standard_no-unused-imports = enabled
|
|
||||||
# ^- Reportedly finicky, use for now but maybe delete later if there are issues.
|
|
||||||
# See https://github.com/ktlint/ktlint/issues/3038
|
|
||||||
ktlint_standard_package-name = disabled
|
|
||||||
max_line_length = 100
|
|
||||||
3
src/android/.gitignore
vendored
3
src/android/.gitignore
vendored
|
|
@ -34,8 +34,7 @@ captures/
|
||||||
|
|
||||||
# IntelliJ
|
# IntelliJ
|
||||||
*.iml
|
*.iml
|
||||||
.idea/*
|
.idea/
|
||||||
!.idea/inspectionProfiles
|
|
||||||
|
|
||||||
# Keystore files
|
# Keystore files
|
||||||
# Uncomment the following line if you do not want to check your keystore files in.
|
# Uncomment the following line if you do not want to check your keystore files in.
|
||||||
|
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
<component name="InspectionProjectProfileManager">
|
|
||||||
<profile version="1.0">
|
|
||||||
<option name="myName" value="Project Default" />
|
|
||||||
<inspection_tool class="GrazieInspection" enabled="false" level="GRAMMAR_ERROR" enabled_by_default="false" />
|
|
||||||
<inspection_tool class="GrazieStyle" enabled="false" level="STYLE_SUGGESTION" enabled_by_default="false" />
|
|
||||||
<inspection_tool class="LanguageDetectionInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
|
||||||
<inspection_tool class="LiftReturnOrAssignment" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
|
||||||
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
|
|
||||||
<option name="processCode" value="true" />
|
|
||||||
<option name="processLiterals" value="true" />
|
|
||||||
<option name="processComments" value="true" />
|
|
||||||
</inspection_tool>
|
|
||||||
</profile>
|
|
||||||
</component>
|
|
||||||
|
|
@ -12,7 +12,6 @@ plugins {
|
||||||
id("kotlin-parcelize")
|
id("kotlin-parcelize")
|
||||||
kotlin("plugin.serialization") version "2.0.20"
|
kotlin("plugin.serialization") version "2.0.20"
|
||||||
id("androidx.navigation.safeargs.kotlin")
|
id("androidx.navigation.safeargs.kotlin")
|
||||||
id("org.jlleitschuh.gradle.ktlint")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -25,11 +24,12 @@ val abiFilter = listOf("arm64-v8a", "x86_64")
|
||||||
|
|
||||||
val downloadedJniLibsPath = "${layout.buildDirectory.get().asFile.path}/downloadedJniLibs"
|
val downloadedJniLibsPath = "${layout.buildDirectory.get().asFile.path}/downloadedJniLibs"
|
||||||
|
|
||||||
|
@Suppress("UnstableApiUsage")
|
||||||
android {
|
android {
|
||||||
namespace = "org.citra.citra_emu"
|
namespace = "org.citra.citra_emu"
|
||||||
|
|
||||||
compileSdkVersion = "android-35"
|
compileSdkVersion = "android-35"
|
||||||
ndkVersion = "27.3.13750724"
|
ndkVersion = "27.1.12297006"
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
sourceCompatibility = JavaVersion.VERSION_17
|
sourceCompatibility = JavaVersion.VERSION_17
|
||||||
|
|
@ -80,9 +80,7 @@ android {
|
||||||
"-DENABLE_QT=0", // Don't use QT
|
"-DENABLE_QT=0", // Don't use QT
|
||||||
"-DENABLE_SDL2=0", // Don't use SDL
|
"-DENABLE_SDL2=0", // Don't use SDL
|
||||||
"-DANDROID_ARM_NEON=true", // cryptopp requires Neon to work
|
"-DANDROID_ARM_NEON=true", // cryptopp requires Neon to work
|
||||||
"-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON", // Support Android 15 16KiB page
|
"-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON" // Support Android 15 16KiB page sizes
|
||||||
// sizes
|
|
||||||
"-DENABLE_GDBSTUB=OFF" // Disable GDB stub
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -127,8 +125,7 @@ android {
|
||||||
applicationIdSuffix = ".debug"
|
applicationIdSuffix = ".debug"
|
||||||
versionNameSuffix = "-debug"
|
versionNameSuffix = "-debug"
|
||||||
signingConfig = signingConfigs.getByName("debug")
|
signingConfig = signingConfigs.getByName("debug")
|
||||||
isShrinkResources = true
|
isShrinkResources = true // TODO: Does this actually do anything when isDebuggable is enabled? -OS
|
||||||
// TODO: ^- Does this actually do anything when isDebuggable is enabled? -OS
|
|
||||||
isDebuggable = true
|
isDebuggable = true
|
||||||
isJniDebuggable = true
|
isJniDebuggable = true
|
||||||
proguardFiles(
|
proguardFiles(
|
||||||
|
|
@ -139,10 +136,8 @@ android {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Same as above, but with isDebuggable disabled.
|
// Same as above, but with isDebuggable disabled.
|
||||||
// Primarily exists to allow development on hardened_malloc systems (e.g. GrapheneOS)
|
// Primarily exists to allow development on hardened_malloc systems (e.g. GrapheneOS) without constantly tripping over years-old and seemingly harmless memory bugs.
|
||||||
// without constantly tripping over years-old and seemingly harmless memory bugs.
|
// We should fix those bugs eventually, but for now this exists as a workaround to allow other work to be done.
|
||||||
// We should fix those bugs eventually, but for now this exists as a workaround to
|
|
||||||
// allow other work to be done on these devices.
|
|
||||||
register("relWithDebInfoLite") {
|
register("relWithDebInfoLite") {
|
||||||
initWith(getByName("relWithDebInfo"))
|
initWith(getByName("relWithDebInfo"))
|
||||||
signingConfig = signingConfigs.getByName("debug")
|
signingConfig = signingConfigs.getByName("debug")
|
||||||
|
|
@ -152,7 +147,7 @@ android {
|
||||||
}
|
}
|
||||||
lint {
|
lint {
|
||||||
checkReleaseBuilds = false // Ditto
|
checkReleaseBuilds = false // Ditto
|
||||||
// ^- The name of this property is misleading, this doesn't actually disable linting for the `release` build.
|
// The name of this property is misleading, this doesn't actually disable linting for the `release` build.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -220,9 +215,7 @@ dependencies {
|
||||||
|
|
||||||
// Download Vulkan Validation Layers from the KhronosGroup GitHub.
|
// Download Vulkan Validation Layers from the KhronosGroup GitHub.
|
||||||
val downloadVulkanValidationLayers = tasks.register<Download>("downloadVulkanValidationLayers") {
|
val downloadVulkanValidationLayers = tasks.register<Download>("downloadVulkanValidationLayers") {
|
||||||
src(
|
src("https://github.com/KhronosGroup/Vulkan-ValidationLayers/releases/download/vulkan-sdk-1.4.313.0/android-binaries-1.4.313.0.zip")
|
||||||
"https://github.com/KhronosGroup/Vulkan-ValidationLayers/releases/download/vulkan-sdk-1.4.313.0/android-binaries-1.4.313.0.zip"
|
|
||||||
)
|
|
||||||
dest(file("${layout.buildDirectory.get().asFile.path}/tmp/Vulkan-ValidationLayers.zip"))
|
dest(file("${layout.buildDirectory.get().asFile.path}/tmp/Vulkan-ValidationLayers.zip"))
|
||||||
onlyIfModified(true)
|
onlyIfModified(true)
|
||||||
}
|
}
|
||||||
|
|
@ -242,11 +235,6 @@ val unzipVulkanValidationLayers = tasks.register<Copy>("unzipVulkanValidationLay
|
||||||
|
|
||||||
tasks.named("preBuild") {
|
tasks.named("preBuild") {
|
||||||
dependsOn(unzipVulkanValidationLayers)
|
dependsOn(unzipVulkanValidationLayers)
|
||||||
dependsOn("ktlintCheck")
|
|
||||||
}
|
|
||||||
|
|
||||||
ktlint {
|
|
||||||
version = "1.8.0"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getGitVersion(): String {
|
fun getGitVersion(): String {
|
||||||
|
|
@ -278,7 +266,7 @@ fun getGitHash(): String =
|
||||||
fun getBranch(): String =
|
fun getBranch(): String =
|
||||||
runGitCommand(ProcessBuilder("git", "rev-parse", "--abbrev-ref", "HEAD")) ?: "dummy-branch"
|
runGitCommand(ProcessBuilder("git", "rev-parse", "--abbrev-ref", "HEAD")) ?: "dummy-branch"
|
||||||
|
|
||||||
fun runGitCommand(command: ProcessBuilder): String? {
|
fun runGitCommand(command: ProcessBuilder) : String? {
|
||||||
try {
|
try {
|
||||||
command.directory(project.rootDir)
|
command.directory(project.rootDir)
|
||||||
val process = command.start()
|
val process = command.start()
|
||||||
|
|
@ -304,7 +292,7 @@ android.applicationVariants.configureEach {
|
||||||
val variant = this
|
val variant = this
|
||||||
val capitalizedName = variant.name.capitalizeUS()
|
val capitalizedName = variant.name.capitalizeUS()
|
||||||
|
|
||||||
val copyTask = tasks.register("copyBundle$capitalizedName") {
|
val copyTask = tasks.register("copyBundle${capitalizedName}") {
|
||||||
doLast {
|
doLast {
|
||||||
project.copy {
|
project.copy {
|
||||||
from(variant.outputs.first().outputFile.parentFile)
|
from(variant.outputs.first().outputFile.parentFile)
|
||||||
|
|
@ -318,5 +306,5 @@ android.applicationVariants.configureEach {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tasks.named("bundle$capitalizedName").configure { finalizedBy(copyTask) }
|
tasks.named("bundle${capitalizedName}").configure { finalizedBy(copyTask) }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,10 +12,10 @@ import android.content.Context
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import org.citra.citra_emu.utils.DirectoryInitialization
|
import org.citra.citra_emu.utils.DirectoryInitialization
|
||||||
import org.citra.citra_emu.utils.DocumentsTree
|
import org.citra.citra_emu.utils.DocumentsTree
|
||||||
import org.citra.citra_emu.utils.GraphicsUtil
|
import org.citra.citra_emu.utils.GpuDriverHelper
|
||||||
|
import org.citra.citra_emu.utils.PermissionsHandler
|
||||||
import org.citra.citra_emu.utils.Log
|
import org.citra.citra_emu.utils.Log
|
||||||
import org.citra.citra_emu.utils.MemoryUtil
|
import org.citra.citra_emu.utils.MemoryUtil
|
||||||
import org.citra.citra_emu.utils.PermissionsHandler
|
|
||||||
|
|
||||||
class CitraApplication : Application() {
|
class CitraApplication : Application() {
|
||||||
private fun createNotificationChannel() {
|
private fun createNotificationChannel() {
|
||||||
|
|
@ -69,7 +69,6 @@ class CitraApplication : Application() {
|
||||||
Log.info("SoC Model - ${Build.SOC_MODEL}")
|
Log.info("SoC Model - ${Build.SOC_MODEL}")
|
||||||
}
|
}
|
||||||
Log.info("Total System Memory - ${MemoryUtil.getDeviceRAM()}")
|
Log.info("Total System Memory - ${MemoryUtil.getDeviceRAM()}")
|
||||||
Log.info("OpenGL ES Renderer - ${GraphicsUtil.openGLRendererString}")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
||||||
|
|
@ -19,22 +19,20 @@ import android.view.Surface
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.annotation.Keep
|
import androidx.annotation.Keep
|
||||||
import androidx.annotation.StringRes
|
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import java.lang.ref.WeakReference
|
|
||||||
import java.util.Date
|
|
||||||
import org.citra.citra_emu.activities.EmulationActivity
|
import org.citra.citra_emu.activities.EmulationActivity
|
||||||
import org.citra.citra_emu.model.Game
|
import org.citra.citra_emu.model.Game
|
||||||
import org.citra.citra_emu.utils.BuildUtil
|
import org.citra.citra_emu.utils.BuildUtil
|
||||||
import org.citra.citra_emu.utils.FileUtil
|
import org.citra.citra_emu.utils.FileUtil
|
||||||
import org.citra.citra_emu.utils.GraphicsUtil
|
|
||||||
import org.citra.citra_emu.utils.Log
|
import org.citra.citra_emu.utils.Log
|
||||||
import org.citra.citra_emu.utils.RemovableStorageHelper
|
import org.citra.citra_emu.utils.RemovableStorageHelper
|
||||||
import org.citra.citra_emu.viewmodel.CompressProgressDialogViewModel
|
import org.citra.citra_emu.viewmodel.CompressProgressDialogViewModel
|
||||||
|
import java.lang.ref.WeakReference
|
||||||
|
import java.util.Date
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class which contains methods that interact
|
* Class which contains methods that interact
|
||||||
|
|
@ -44,7 +42,7 @@ object NativeLibrary {
|
||||||
/**
|
/**
|
||||||
* Default touchscreen device
|
* Default touchscreen device
|
||||||
*/
|
*/
|
||||||
const val TOUCHSCREEN_DEVICE = "Touchscreen"
|
const val TouchScreenDevice = "Touchscreen"
|
||||||
|
|
||||||
@JvmField
|
@JvmField
|
||||||
var sEmulationActivity = WeakReference<EmulationActivity?>(null)
|
var sEmulationActivity = WeakReference<EmulationActivity?>(null)
|
||||||
|
|
@ -136,7 +134,10 @@ object NativeLibrary {
|
||||||
*/
|
*/
|
||||||
external fun setUserDirectory(directory: String)
|
external fun setUserDirectory(directory: String)
|
||||||
|
|
||||||
data class InstalledGame(val path: String, val mediaType: Game.MediaType)
|
data class InstalledGame(
|
||||||
|
val path: String,
|
||||||
|
val mediaType: Game.MediaType
|
||||||
|
)
|
||||||
fun getInstalledGamePaths(): Array<InstalledGame> {
|
fun getInstalledGamePaths(): Array<InstalledGame> {
|
||||||
val games = getInstalledGamePathsImpl()
|
val games = getInstalledGamePathsImpl()
|
||||||
|
|
||||||
|
|
@ -251,14 +252,12 @@ object NativeLibrary {
|
||||||
external fun playTimeManagerGetCurrentTitleId(): Long
|
external fun playTimeManagerGetCurrentTitleId(): Long
|
||||||
|
|
||||||
private external fun uninstallTitle(titleId: Long, mediaType: Int): Boolean
|
private external fun uninstallTitle(titleId: Long, mediaType: Int): Boolean
|
||||||
fun uninstallTitle(titleId: Long, mediaType: Game.MediaType): Boolean =
|
fun uninstallTitle(titleId: Long, mediaType: Game.MediaType): Boolean {
|
||||||
uninstallTitle(titleId, mediaType.value)
|
return uninstallTitle(titleId, mediaType.value)
|
||||||
|
}
|
||||||
|
|
||||||
external fun nativeFileExists(path: String): Boolean
|
external fun nativeFileExists(path: String): Boolean
|
||||||
|
|
||||||
external fun deleteOpenGLShaderCache(titleId: Long)
|
|
||||||
external fun deleteVulkanShaderCache(titleId: Long)
|
|
||||||
|
|
||||||
private var coreErrorAlertResult = false
|
private var coreErrorAlertResult = false
|
||||||
private val coreErrorAlertLock = Object()
|
private val coreErrorAlertLock = Object()
|
||||||
|
|
||||||
|
|
@ -315,12 +314,6 @@ object NativeLibrary {
|
||||||
canContinue = false
|
canContinue = false
|
||||||
}
|
}
|
||||||
|
|
||||||
CoreError.ErrorCoreExceptionRaised -> {
|
|
||||||
title = emulationActivity.getString(R.string.fatal_error)
|
|
||||||
message = emulationActivity.getString(R.string.fatal_error_message)
|
|
||||||
canContinue = false
|
|
||||||
}
|
|
||||||
|
|
||||||
CoreError.ErrorUnknown -> {
|
CoreError.ErrorUnknown -> {
|
||||||
title = emulationActivity.getString(R.string.fatal_error)
|
title = emulationActivity.getString(R.string.fatal_error)
|
||||||
message = emulationActivity.getString(R.string.fatal_error_message)
|
message = emulationActivity.getString(R.string.fatal_error_message)
|
||||||
|
|
@ -345,12 +338,11 @@ object NativeLibrary {
|
||||||
return coreErrorAlertResult
|
return coreErrorAlertResult
|
||||||
}
|
}
|
||||||
|
|
||||||
@Keep
|
@get:Keep
|
||||||
@JvmStatic
|
@get:JvmStatic
|
||||||
fun isPortraitMode(): Boolean = (
|
val isPortraitMode: Boolean
|
||||||
CitraApplication.appContext.resources.configuration.orientation ==
|
get() = CitraApplication.appContext.resources.configuration.orientation ==
|
||||||
Configuration.ORIENTATION_PORTRAIT
|
Configuration.ORIENTATION_PORTRAIT
|
||||||
)
|
|
||||||
|
|
||||||
@Keep
|
@Keep
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
|
|
@ -444,7 +436,7 @@ object NativeLibrary {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resultCode == CoreError.ShutdownRequested.value) {
|
if (resultCode == EmulationErrorDialogFragment.ShutdownRequested) {
|
||||||
emulationActivity.finish()
|
emulationActivity.finish()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -463,57 +455,23 @@ object NativeLibrary {
|
||||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
emulationActivity = requireActivity() as EmulationActivity
|
emulationActivity = requireActivity() as EmulationActivity
|
||||||
|
|
||||||
val coreError = CoreError.fromInt(requireArguments().getInt(RESULT_CODE))
|
var captionId = R.string.loader_error_invalid_format
|
||||||
val title: String
|
val result = requireArguments().getInt(RESULT_CODE)
|
||||||
val message: String
|
if (result == ErrorLoader_ErrorEncrypted) {
|
||||||
when (coreError) {
|
captionId = R.string.loader_error_encrypted
|
||||||
CoreError.ErrorGetLoader,
|
|
||||||
CoreError.ErrorLoaderErrorInvalidFormat,
|
|
||||||
CoreError.ErrorSystemMode -> {
|
|
||||||
title = getString(R.string.loader_error_invalid_format)
|
|
||||||
message = getString(R.string.loader_error_invalid_format_description)
|
|
||||||
}
|
|
||||||
|
|
||||||
CoreError.ErrorLoaderErrorEncrypted -> {
|
|
||||||
title = getString(R.string.loader_error_encrypted)
|
|
||||||
message = getString(R.string.loader_error_encrypted_description)
|
|
||||||
}
|
|
||||||
|
|
||||||
CoreError.ErrorArticDisconnected -> {
|
|
||||||
title = getString(R.string.artic_base)
|
|
||||||
message = getString(R.string.artic_server_comm_error)
|
|
||||||
}
|
|
||||||
|
|
||||||
CoreError.ErrorN3DSApplication -> {
|
|
||||||
title = getString(R.string.loader_error_invalid_system_mode)
|
|
||||||
message = getString(R.string.loader_error_invalid_system_mode_description)
|
|
||||||
}
|
|
||||||
|
|
||||||
CoreError.ErrorLoaderErrorPatches -> {
|
|
||||||
title = getString(R.string.loader_error_applying_patches)
|
|
||||||
message = getString(R.string.loader_error_applying_patches_description)
|
|
||||||
}
|
|
||||||
|
|
||||||
CoreError.ErrorLoaderErrorPatchesInvalidTitle -> {
|
|
||||||
title = getString(R.string.loader_error_applying_patches)
|
|
||||||
message = getString(R.string.loader_error_patch_wrong_application)
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> {
|
|
||||||
title = getString(R.string.loader_error_generic_title)
|
|
||||||
message = getString(
|
|
||||||
R.string.loader_error_generic,
|
|
||||||
getString(coreError.stringRes),
|
|
||||||
coreError.value
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
if (result == ErrorArticDisconnected) {
|
||||||
|
captionId = R.string.artic_base
|
||||||
}
|
}
|
||||||
|
|
||||||
val alert = MaterialAlertDialogBuilder(requireContext())
|
val alert = MaterialAlertDialogBuilder(requireContext())
|
||||||
.setTitle(title)
|
.setTitle(captionId)
|
||||||
.setMessage(
|
.setMessage(
|
||||||
Html.fromHtml(
|
Html.fromHtml(
|
||||||
message,
|
if (result == ErrorArticDisconnected)
|
||||||
|
CitraApplication.appContext.resources.getString(R.string.artic_server_comm_error)
|
||||||
|
else
|
||||||
|
CitraApplication.appContext.resources.getString(R.string.redump_games),
|
||||||
Html.FROM_HTML_MODE_LEGACY
|
Html.FROM_HTML_MODE_LEGACY
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
@ -535,6 +493,20 @@ object NativeLibrary {
|
||||||
|
|
||||||
const val RESULT_CODE = "resultcode"
|
const val RESULT_CODE = "resultcode"
|
||||||
|
|
||||||
|
const val Success = 0
|
||||||
|
const val ErrorNotInitialized = 1
|
||||||
|
const val ErrorGetLoader = 2
|
||||||
|
const val ErrorSystemMode = 3
|
||||||
|
const val ErrorLoader = 4
|
||||||
|
const val ErrorLoader_ErrorEncrypted = 5
|
||||||
|
const val ErrorLoader_ErrorInvalidFormat = 6
|
||||||
|
const val ErrorLoader_ErrorGBATitle = 7
|
||||||
|
const val ErrorSystemFiles = 8
|
||||||
|
const val ErrorSavestate = 9
|
||||||
|
const val ErrorArticDisconnected = 10
|
||||||
|
const val ShutdownRequested = 11
|
||||||
|
const val ErrorUnknown = 12
|
||||||
|
|
||||||
fun newInstance(resultCode: Int): EmulationErrorDialogFragment {
|
fun newInstance(resultCode: Int): EmulationErrorDialogFragment {
|
||||||
val args = Bundle()
|
val args = Bundle()
|
||||||
args.putInt(RESULT_CODE, resultCode)
|
args.putInt(RESULT_CODE, resultCode)
|
||||||
|
|
@ -645,7 +617,7 @@ object NativeLibrary {
|
||||||
fun loadStateIfAvailable(slot: Int): Boolean {
|
fun loadStateIfAvailable(slot: Int): Boolean {
|
||||||
var available = false
|
var available = false
|
||||||
getSavestateInfo()?.forEach {
|
getSavestateInfo()?.forEach {
|
||||||
if (it.slot == slot) {
|
if (it.slot == slot){
|
||||||
available = true
|
available = true
|
||||||
return@forEach
|
return@forEach
|
||||||
}
|
}
|
||||||
|
|
@ -683,17 +655,19 @@ object NativeLibrary {
|
||||||
// Compression / Decompression
|
// Compression / Decompression
|
||||||
private external fun compressFileNative(inputPath: String?, outputPath: String): Int
|
private external fun compressFileNative(inputPath: String?, outputPath: String): Int
|
||||||
|
|
||||||
fun compressFile(inputPath: String?, outputPath: String): CompressStatus =
|
fun compressFile(inputPath: String?, outputPath: String): CompressStatus {
|
||||||
CompressStatus.fromValue(
|
return CompressStatus.fromValue(
|
||||||
compressFileNative(inputPath, outputPath)
|
compressFileNative(inputPath, outputPath)
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private external fun decompressFileNative(inputPath: String?, outputPath: String): Int
|
private external fun decompressFileNative(inputPath: String?, outputPath: String): Int
|
||||||
|
|
||||||
fun decompressFile(inputPath: String?, outputPath: String): CompressStatus =
|
fun decompressFile(inputPath: String?, outputPath: String): CompressStatus {
|
||||||
CompressStatus.fromValue(
|
return CompressStatus.fromValue(
|
||||||
decompressFileNative(inputPath, outputPath)
|
decompressFileNative(inputPath, outputPath)
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
external fun getRecommendedExtension(inputPath: String?, shouldCompress: Boolean): String
|
external fun getRecommendedExtension(inputPath: String?, shouldCompress: Boolean): String
|
||||||
|
|
||||||
|
|
@ -727,7 +701,8 @@ object NativeLibrary {
|
||||||
|
|
||||||
@Keep
|
@Keep
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun openContentUri(path: String, openMode: String): Int = if (FileUtil.isNativePath(path)) {
|
fun openContentUri(path: String, openMode: String): Int =
|
||||||
|
if (FileUtil.isNativePath(path)) {
|
||||||
CitraApplication.documentsTree.openContentUri(path, openMode)
|
CitraApplication.documentsTree.openContentUri(path, openMode)
|
||||||
} else {
|
} else {
|
||||||
FileUtil.openContentUri(path, openMode)
|
FileUtil.openContentUri(path, openMode)
|
||||||
|
|
@ -735,7 +710,8 @@ object NativeLibrary {
|
||||||
|
|
||||||
@Keep
|
@Keep
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun getFilesName(path: String): Array<String?> = if (FileUtil.isNativePath(path)) {
|
fun getFilesName(path: String): Array<String?> =
|
||||||
|
if (FileUtil.isNativePath(path)) {
|
||||||
CitraApplication.documentsTree.getFilesName(path)
|
CitraApplication.documentsTree.getFilesName(path)
|
||||||
} else {
|
} else {
|
||||||
FileUtil.getFilesName(path)
|
FileUtil.getFilesName(path)
|
||||||
|
|
@ -765,14 +741,10 @@ object NativeLibrary {
|
||||||
return primaryStoragePath + dirSep + virtualPath
|
return primaryStoragePath + dirSep + virtualPath
|
||||||
} else { // User directory probably located on a removable storage device
|
} else { // User directory probably located on a removable storage device
|
||||||
val storageIdString = pathSegment.substringBefore(":")
|
val storageIdString = pathSegment.substringBefore(":")
|
||||||
val removablePath = RemovableStorageHelper.getRemovableStoragePath(
|
val removablePath = RemovableStorageHelper.getRemovableStoragePath(CitraApplication.appContext, storageIdString)
|
||||||
CitraApplication.appContext,
|
|
||||||
storageIdString
|
|
||||||
)
|
|
||||||
|
|
||||||
if (removablePath == null) {
|
if (removablePath == null) {
|
||||||
android.util.Log.e(
|
android.util.Log.e("NativeLibrary",
|
||||||
"NativeLibrary",
|
|
||||||
"Unknown mount location for storage device '$storageIdString' (URI: $uri)"
|
"Unknown mount location for storage device '$storageIdString' (URI: $uri)"
|
||||||
)
|
)
|
||||||
return ""
|
return ""
|
||||||
|
|
@ -792,7 +764,8 @@ object NativeLibrary {
|
||||||
|
|
||||||
@Keep
|
@Keep
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun getSize(path: String): Long = if (FileUtil.isNativePath(path)) {
|
fun getSize(path: String): Long =
|
||||||
|
if (FileUtil.isNativePath(path)) {
|
||||||
CitraApplication.documentsTree.getFileSize(path)
|
CitraApplication.documentsTree.getFileSize(path)
|
||||||
} else {
|
} else {
|
||||||
FileUtil.getFileSize(path)
|
FileUtil.getFileSize(path)
|
||||||
|
|
@ -804,11 +777,8 @@ object NativeLibrary {
|
||||||
|
|
||||||
@Keep
|
@Keep
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun isUsingAngleForOpenGL(): Boolean = GraphicsUtil.isUsingAngleForOpenGL()
|
fun fileExists(path: String): Boolean =
|
||||||
|
if (FileUtil.isNativePath(path)) {
|
||||||
@Keep
|
|
||||||
@JvmStatic
|
|
||||||
fun fileExists(path: String): Boolean = if (FileUtil.isNativePath(path)) {
|
|
||||||
CitraApplication.documentsTree.exists(path)
|
CitraApplication.documentsTree.exists(path)
|
||||||
} else {
|
} else {
|
||||||
FileUtil.exists(path)
|
FileUtil.exists(path)
|
||||||
|
|
@ -816,7 +786,8 @@ object NativeLibrary {
|
||||||
|
|
||||||
@Keep
|
@Keep
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun isDirectory(path: String): Boolean = if (FileUtil.isNativePath(path)) {
|
fun isDirectory(path: String): Boolean =
|
||||||
|
if (FileUtil.isNativePath(path)) {
|
||||||
CitraApplication.documentsTree.isDirectory(path)
|
CitraApplication.documentsTree.isDirectory(path)
|
||||||
} else {
|
} else {
|
||||||
FileUtil.isDirectory(path)
|
FileUtil.isDirectory(path)
|
||||||
|
|
@ -828,7 +799,8 @@ object NativeLibrary {
|
||||||
sourcePath: String,
|
sourcePath: String,
|
||||||
destinationParentPath: String,
|
destinationParentPath: String,
|
||||||
destinationFilename: String
|
destinationFilename: String
|
||||||
): Boolean = if (FileUtil.isNativePath(sourcePath) &&
|
): Boolean =
|
||||||
|
if (FileUtil.isNativePath(sourcePath) &&
|
||||||
FileUtil.isNativePath(destinationParentPath)
|
FileUtil.isNativePath(destinationParentPath)
|
||||||
) {
|
) {
|
||||||
CitraApplication.documentsTree
|
CitraApplication.documentsTree
|
||||||
|
|
@ -874,35 +846,19 @@ object NativeLibrary {
|
||||||
|
|
||||||
@Keep
|
@Keep
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun deleteDocument(path: String): Boolean = if (FileUtil.isNativePath(path)) {
|
fun deleteDocument(path: String): Boolean =
|
||||||
|
if (FileUtil.isNativePath(path)) {
|
||||||
CitraApplication.documentsTree.deleteDocument(path)
|
CitraApplication.documentsTree.deleteDocument(path)
|
||||||
} else {
|
} else {
|
||||||
FileUtil.deleteDocument(path)
|
FileUtil.deleteDocument(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class CoreError(val value: Int, @StringRes val stringRes: Int) {
|
enum class CoreError {
|
||||||
Success(0, R.string.core_error_success),
|
ErrorSystemFiles,
|
||||||
ErrorNotInitialized(1, R.string.core_error_not_initialized),
|
ErrorSavestate,
|
||||||
ErrorGetLoader(2, R.string.core_error_get_loader),
|
ErrorArticDisconnected,
|
||||||
ErrorSystemMode(3, R.string.core_error_system_mode),
|
ErrorN3DSApplication,
|
||||||
ErrorLoader(4, R.string.core_error_loader),
|
ErrorUnknown
|
||||||
ErrorLoaderErrorEncrypted(5, R.string.core_error_loader_encrypted),
|
|
||||||
ErrorLoaderErrorInvalidFormat(6, R.string.core_error_loader_invalid_format),
|
|
||||||
ErrorLoaderErrorGBATitle(7, R.string.core_error_loader_gba_title),
|
|
||||||
ErrorLoaderErrorPatches(8, R.string.core_error_loader_error_patches),
|
|
||||||
ErrorLoaderErrorPatchesInvalidTitle(9, R.string.core_error_loader_patches_invalid_title),
|
|
||||||
ErrorSystemFiles(10, R.string.core_error_system_files),
|
|
||||||
ErrorSavestate(11, R.string.core_error_savestate),
|
|
||||||
ErrorArticDisconnected(12, R.string.core_error_artic_disconnected),
|
|
||||||
ErrorN3DSApplication(13, R.string.core_error_n3ds_application),
|
|
||||||
ErrorCoreExceptionRaised(14, R.string.core_error_core_exception_raised),
|
|
||||||
ErrorMemoryExceptionRaised(15, R.string.core_error_memory_exception_raised),
|
|
||||||
ShutdownRequested(16, R.string.core_error_shutdown_requested),
|
|
||||||
ErrorUnknown(17, R.string.core_error_unknown);
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun fromInt(value: Int): CoreError = entries.find { it.value == value } ?: ErrorUnknown
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class InstallStatus {
|
enum class InstallStatus {
|
||||||
|
|
@ -953,11 +909,7 @@ object NativeLibrary {
|
||||||
const val MESSAGE = "message"
|
const val MESSAGE = "message"
|
||||||
const val CAN_CONTINUE = "canContinue"
|
const val CAN_CONTINUE = "canContinue"
|
||||||
|
|
||||||
fun newInstance(
|
fun newInstance(title: String, message: String, canContinue: Boolean): CoreErrorDialogFragment {
|
||||||
title: String,
|
|
||||||
message: String,
|
|
||||||
canContinue: Boolean
|
|
||||||
): CoreErrorDialogFragment {
|
|
||||||
val frag = CoreErrorDialogFragment()
|
val frag = CoreErrorDialogFragment()
|
||||||
val args = Bundle()
|
val args = Bundle()
|
||||||
args.putString(TITLE, title)
|
args.putString(TITLE, title)
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ import android.Manifest.permission
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.content.pm.ActivityInfo
|
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
|
@ -31,7 +30,7 @@ import androidx.preference.PreferenceManager
|
||||||
import org.citra.citra_emu.CitraApplication
|
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.camera.StillImageCameraHelper.onFilePickerResult
|
import org.citra.citra_emu.camera.StillImageCameraHelper.OnFilePickerResult
|
||||||
import org.citra.citra_emu.contracts.OpenFileResultContract
|
import org.citra.citra_emu.contracts.OpenFileResultContract
|
||||||
import org.citra.citra_emu.databinding.ActivityEmulationBinding
|
import org.citra.citra_emu.databinding.ActivityEmulationBinding
|
||||||
import org.citra.citra_emu.display.ScreenAdjustmentUtil
|
import org.citra.citra_emu.display.ScreenAdjustmentUtil
|
||||||
|
|
@ -46,9 +45,9 @@ import org.citra.citra_emu.fragments.MessageDialogFragment
|
||||||
import org.citra.citra_emu.model.Game
|
import org.citra.citra_emu.model.Game
|
||||||
import org.citra.citra_emu.utils.BuildUtil
|
import org.citra.citra_emu.utils.BuildUtil
|
||||||
import org.citra.citra_emu.utils.ControllerMappingHelper
|
import org.citra.citra_emu.utils.ControllerMappingHelper
|
||||||
|
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.FileBrowserHelper
|
|
||||||
import org.citra.citra_emu.utils.Log
|
import org.citra.citra_emu.utils.Log
|
||||||
import org.citra.citra_emu.utils.RefreshRateUtil
|
import org.citra.citra_emu.utils.RefreshRateUtil
|
||||||
import org.citra.citra_emu.utils.ThemeUtil
|
import org.citra.citra_emu.utils.ThemeUtil
|
||||||
|
|
@ -64,7 +63,7 @@ class EmulationActivity : AppCompatActivity() {
|
||||||
private lateinit var binding: ActivityEmulationBinding
|
private lateinit var binding: ActivityEmulationBinding
|
||||||
private lateinit var screenAdjustmentUtil: ScreenAdjustmentUtil
|
private lateinit var screenAdjustmentUtil: ScreenAdjustmentUtil
|
||||||
private lateinit var hotkeyUtility: HotkeyUtility
|
private lateinit var hotkeyUtility: HotkeyUtility
|
||||||
lateinit var secondaryDisplayManager: SecondaryDisplay
|
private lateinit var secondaryDisplay: SecondaryDisplay
|
||||||
|
|
||||||
private val onShutdown = Runnable {
|
private val onShutdown = Runnable {
|
||||||
if (intent.getBooleanExtra("launched_from_shortcut", false)) {
|
if (intent.getBooleanExtra("launched_from_shortcut", false)) {
|
||||||
|
|
@ -81,9 +80,7 @@ class EmulationActivity : AppCompatActivity() {
|
||||||
return navHostFragment.getChildFragmentManager().fragments.last() as EmulationFragment
|
return navHostFragment.getChildFragmentManager().fragments.last() as EmulationFragment
|
||||||
}
|
}
|
||||||
|
|
||||||
private var isRotationBlocked: Boolean = true
|
|
||||||
private var isEmulationRunning: Boolean = false
|
private var isEmulationRunning: Boolean = false
|
||||||
private var isEmulationReady: Boolean = false
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
requestWindowFeature(Window.FEATURE_NO_TITLE)
|
requestWindowFeature(Window.FEATURE_NO_TITLE)
|
||||||
|
|
@ -92,20 +89,12 @@ class EmulationActivity : AppCompatActivity() {
|
||||||
|
|
||||||
ThemeUtil.setTheme(this)
|
ThemeUtil.setTheme(this)
|
||||||
settingsViewModel.settings.loadSettings()
|
settingsViewModel.settings.loadSettings()
|
||||||
|
|
||||||
screenAdjustmentUtil = ScreenAdjustmentUtil(this, windowManager, settingsViewModel.settings)
|
|
||||||
|
|
||||||
// Block orientation until emulation is ready to prevent unneccesary
|
|
||||||
// surface recreation until the renderer is ready.
|
|
||||||
isRotationBlocked = true
|
|
||||||
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LOCKED
|
|
||||||
|
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
secondaryDisplay = SecondaryDisplay(this)
|
||||||
secondaryDisplayManager = SecondaryDisplay(this)
|
secondaryDisplay.updateDisplay()
|
||||||
secondaryDisplayManager.updateDisplay()
|
|
||||||
|
|
||||||
binding = ActivityEmulationBinding.inflate(layoutInflater)
|
binding = ActivityEmulationBinding.inflate(layoutInflater)
|
||||||
|
screenAdjustmentUtil = ScreenAdjustmentUtil(this, windowManager, settingsViewModel.settings)
|
||||||
hotkeyUtility = HotkeyUtility(screenAdjustmentUtil, this)
|
hotkeyUtility = HotkeyUtility(screenAdjustmentUtil, this)
|
||||||
setContentView(binding.root)
|
setContentView(binding.root)
|
||||||
|
|
||||||
|
|
@ -130,6 +119,8 @@ class EmulationActivity : AppCompatActivity() {
|
||||||
isEmulationRunning = true
|
isEmulationRunning = true
|
||||||
instance = this
|
instance = this
|
||||||
|
|
||||||
|
applyOrientationSettings() // Check for orientation settings at startup
|
||||||
|
|
||||||
val game = try {
|
val game = try {
|
||||||
intent.extras?.let { extras ->
|
intent.extras?.let { extras ->
|
||||||
BundleCompat.getParcelable(extras, "game", Game::class.java)
|
BundleCompat.getParcelable(extras, "game", Game::class.java)
|
||||||
|
|
@ -145,76 +136,39 @@ class EmulationActivity : AppCompatActivity() {
|
||||||
NativeLibrary.playTimeManagerStart(game.titleId)
|
NativeLibrary.playTimeManagerStart(game.titleId)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onNewIntent(intent: Intent) {
|
|
||||||
super.onNewIntent(intent)
|
|
||||||
setIntent(intent)
|
|
||||||
|
|
||||||
NativeLibrary.stopEmulation()
|
|
||||||
NativeLibrary.playTimeManagerStop()
|
|
||||||
|
|
||||||
isEmulationReady = false
|
|
||||||
isRotationBlocked = true
|
|
||||||
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LOCKED
|
|
||||||
emulationViewModel.setEmulationStarted(false)
|
|
||||||
|
|
||||||
val game = intent.extras?.let { extras ->
|
|
||||||
BundleCompat.getParcelable(extras, "game", Game::class.java)
|
|
||||||
}
|
|
||||||
if (game != null) {
|
|
||||||
NativeLibrary.playTimeManagerStart(game.titleId)
|
|
||||||
}
|
|
||||||
|
|
||||||
val navHostFragment =
|
|
||||||
supportFragmentManager.findFragmentById(R.id.fragment_container) as NavHostFragment
|
|
||||||
navHostFragment.navController.setGraph(R.navigation.emulation_navigation, intent.extras)
|
|
||||||
}
|
|
||||||
|
|
||||||
// On some devices, the system bars will not disappear on first boot or after some
|
// On some devices, the system bars will not disappear on first boot or after some
|
||||||
// rotations. Here we set full screen immersive repeatedly in onResume and in
|
// rotations. Here we set full screen immersive repeatedly in onResume and in
|
||||||
// onWindowFocusChanged to prevent the unwanted status bar state.
|
// onWindowFocusChanged to prevent the unwanted status bar state.
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
enableFullscreenImmersive()
|
|
||||||
if (isEmulationReady) {
|
|
||||||
// If emulation is ready then unblock rotation
|
|
||||||
isRotationBlocked = false
|
|
||||||
applyOrientationSettings()
|
|
||||||
emulationViewModel.setEmulationStarted(true)
|
|
||||||
} else {
|
|
||||||
if (!isRotationBlocked) {
|
|
||||||
applyOrientationSettings()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
super.onResume()
|
super.onResume()
|
||||||
|
enableFullscreenImmersive()
|
||||||
|
applyOrientationSettings() // Check for orientation settings changes on runtime
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStop() {
|
override fun onStop() {
|
||||||
secondaryDisplayManager.releasePresentation()
|
secondaryDisplay.releasePresentation()
|
||||||
super.onStop()
|
super.onStop()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onWindowFocusChanged(hasFocus: Boolean) {
|
override fun onWindowFocusChanged(hasFocus: Boolean) {
|
||||||
enableFullscreenImmersive()
|
|
||||||
super.onWindowFocusChanged(hasFocus)
|
super.onWindowFocusChanged(hasFocus)
|
||||||
|
enableFullscreenImmersive()
|
||||||
}
|
}
|
||||||
|
|
||||||
public override fun onRestart() {
|
public override fun onRestart() {
|
||||||
super.onRestart()
|
super.onRestart()
|
||||||
secondaryDisplayManager.updateDisplay()
|
secondaryDisplay.updateDisplay()
|
||||||
NativeLibrary.reloadCameraDevices()
|
NativeLibrary.reloadCameraDevices()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSaveInstanceState(outState: Bundle) {
|
override fun onSaveInstanceState(outState: Bundle) {
|
||||||
super.onSaveInstanceState(outState)
|
super.onSaveInstanceState(outState)
|
||||||
outState.putBoolean("isEmulationRunning", isEmulationRunning)
|
outState.putBoolean("isEmulationRunning", isEmulationRunning)
|
||||||
outState.putBoolean("isEmulationReady", isEmulationReady)
|
|
||||||
outState.putBoolean("isRotationBlocked", isRotationBlocked)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
|
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
|
||||||
super.onRestoreInstanceState(savedInstanceState)
|
super.onRestoreInstanceState(savedInstanceState)
|
||||||
isEmulationRunning = savedInstanceState.getBoolean("isEmulationRunning", false)
|
isEmulationRunning = savedInstanceState.getBoolean("isEmulationRunning", false)
|
||||||
isEmulationReady = savedInstanceState.getBoolean("isEmulationReady", false)
|
|
||||||
isRotationBlocked = savedInstanceState.getBoolean("isRotationBlocked", isRotationBlocked)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
|
|
@ -222,8 +176,8 @@ class EmulationActivity : AppCompatActivity() {
|
||||||
NativeLibrary.playTimeManagerStop()
|
NativeLibrary.playTimeManagerStop()
|
||||||
isEmulationRunning = false
|
isEmulationRunning = false
|
||||||
instance = null
|
instance = null
|
||||||
secondaryDisplayManager.releasePresentation()
|
secondaryDisplay.releasePresentation()
|
||||||
secondaryDisplayManager.releaseVD()
|
secondaryDisplay.releaseVD()
|
||||||
|
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
}
|
}
|
||||||
|
|
@ -268,11 +222,6 @@ class EmulationActivity : AppCompatActivity() {
|
||||||
|
|
||||||
fun onEmulationStarted() {
|
fun onEmulationStarted() {
|
||||||
emulationViewModel.setEmulationStarted(true)
|
emulationViewModel.setEmulationStarted(true)
|
||||||
isEmulationReady = true
|
|
||||||
if (isRotationBlocked) {
|
|
||||||
isRotationBlocked = false
|
|
||||||
applyOrientationSettings()
|
|
||||||
}
|
|
||||||
Toast.makeText(
|
Toast.makeText(
|
||||||
applicationContext,
|
applicationContext,
|
||||||
getString(R.string.emulation_menu_help),
|
getString(R.string.emulation_menu_help),
|
||||||
|
|
@ -334,13 +283,11 @@ class EmulationActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
return hotkeyUtility.handleKeyPress(event)
|
return hotkeyUtility.handleKeyPress(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyEvent.ACTION_UP -> {
|
KeyEvent.ACTION_UP -> {
|
||||||
return hotkeyUtility.handleKeyRelease(event)
|
return hotkeyUtility.handleKeyRelease(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
return false
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -360,8 +307,7 @@ class EmulationActivity : AppCompatActivity() {
|
||||||
// TODO: Move this check into native code - prevents crash if input pressed before starting emulation
|
// TODO: Move this check into native code - prevents crash if input pressed before starting emulation
|
||||||
if (!NativeLibrary.isRunning() ||
|
if (!NativeLibrary.isRunning() ||
|
||||||
(event.source and InputDevice.SOURCE_CLASS_JOYSTICK == 0) ||
|
(event.source and InputDevice.SOURCE_CLASS_JOYSTICK == 0) ||
|
||||||
emulationFragment.isDrawerOpen()
|
emulationFragment.isDrawerOpen()) {
|
||||||
) {
|
|
||||||
return super.dispatchGenericMotionEvent(event)
|
return super.dispatchGenericMotionEvent(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -390,19 +336,16 @@ class EmulationActivity : AppCompatActivity() {
|
||||||
preferences.getInt(InputBindingSetting.getInputAxisButtonKey(axis), -1)
|
preferences.getInt(InputBindingSetting.getInputAxisButtonKey(axis), -1)
|
||||||
val guestOrientation =
|
val guestOrientation =
|
||||||
preferences.getInt(InputBindingSetting.getInputAxisOrientationKey(axis), -1)
|
preferences.getInt(InputBindingSetting.getInputAxisOrientationKey(axis), -1)
|
||||||
val inverted = preferences.getBoolean(
|
val inverted = preferences.getBoolean(InputBindingSetting.getInputAxisInvertedKey(axis),false);
|
||||||
InputBindingSetting.getInputAxisInvertedKey(axis),
|
|
||||||
false
|
|
||||||
)
|
|
||||||
if (nextMapping == -1 || guestOrientation == -1) {
|
if (nextMapping == -1 || guestOrientation == -1) {
|
||||||
// Axis is unmapped
|
// Axis is unmapped
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if ((value > 0f && value < 0.1f) || (value < 0f && value > -0.1f)) {
|
if (value > 0f && value < 0.1f || value < 0f && value > -0.1f) {
|
||||||
// Skip joystick wobble
|
// Skip joystick wobble
|
||||||
value = 0f
|
value = 0f
|
||||||
}
|
}
|
||||||
if (inverted) value = -value
|
if (inverted) value = -value;
|
||||||
|
|
||||||
when (nextMapping) {
|
when (nextMapping) {
|
||||||
NativeLibrary.ButtonType.STICK_LEFT -> {
|
NativeLibrary.ButtonType.STICK_LEFT -> {
|
||||||
|
|
@ -456,7 +399,7 @@ class EmulationActivity : AppCompatActivity() {
|
||||||
// Triggers L/R and ZL/ZR
|
// Triggers L/R and ZL/ZR
|
||||||
if (isTriggerPressedLMapped) {
|
if (isTriggerPressedLMapped) {
|
||||||
NativeLibrary.onGamePadEvent(
|
NativeLibrary.onGamePadEvent(
|
||||||
NativeLibrary.TOUCHSCREEN_DEVICE,
|
NativeLibrary.TouchScreenDevice,
|
||||||
NativeLibrary.ButtonType.TRIGGER_L,
|
NativeLibrary.ButtonType.TRIGGER_L,
|
||||||
if (isTriggerPressedL) {
|
if (isTriggerPressedL) {
|
||||||
NativeLibrary.ButtonState.PRESSED
|
NativeLibrary.ButtonState.PRESSED
|
||||||
|
|
@ -467,7 +410,7 @@ class EmulationActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
if (isTriggerPressedRMapped) {
|
if (isTriggerPressedRMapped) {
|
||||||
NativeLibrary.onGamePadEvent(
|
NativeLibrary.onGamePadEvent(
|
||||||
NativeLibrary.TOUCHSCREEN_DEVICE,
|
NativeLibrary.TouchScreenDevice,
|
||||||
NativeLibrary.ButtonType.TRIGGER_R,
|
NativeLibrary.ButtonType.TRIGGER_R,
|
||||||
if (isTriggerPressedR) {
|
if (isTriggerPressedR) {
|
||||||
NativeLibrary.ButtonState.PRESSED
|
NativeLibrary.ButtonState.PRESSED
|
||||||
|
|
@ -478,7 +421,7 @@ class EmulationActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
if (isTriggerPressedZLMapped) {
|
if (isTriggerPressedZLMapped) {
|
||||||
NativeLibrary.onGamePadEvent(
|
NativeLibrary.onGamePadEvent(
|
||||||
NativeLibrary.TOUCHSCREEN_DEVICE,
|
NativeLibrary.TouchScreenDevice,
|
||||||
NativeLibrary.ButtonType.BUTTON_ZL,
|
NativeLibrary.ButtonType.BUTTON_ZL,
|
||||||
if (isTriggerPressedZL) {
|
if (isTriggerPressedZL) {
|
||||||
NativeLibrary.ButtonState.PRESSED
|
NativeLibrary.ButtonState.PRESSED
|
||||||
|
|
@ -489,7 +432,7 @@ class EmulationActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
if (isTriggerPressedZRMapped) {
|
if (isTriggerPressedZRMapped) {
|
||||||
NativeLibrary.onGamePadEvent(
|
NativeLibrary.onGamePadEvent(
|
||||||
NativeLibrary.TOUCHSCREEN_DEVICE,
|
NativeLibrary.TouchScreenDevice,
|
||||||
NativeLibrary.ButtonType.BUTTON_ZR,
|
NativeLibrary.ButtonType.BUTTON_ZR,
|
||||||
if (isTriggerPressedZR) {
|
if (isTriggerPressedZR) {
|
||||||
NativeLibrary.ButtonState.PRESSED
|
NativeLibrary.ButtonState.PRESSED
|
||||||
|
|
@ -502,72 +445,72 @@ class EmulationActivity : AppCompatActivity() {
|
||||||
// Work-around to allow D-pad axis to be bound to emulated buttons
|
// Work-around to allow D-pad axis to be bound to emulated buttons
|
||||||
if (axisValuesDPad[0] == 0f) {
|
if (axisValuesDPad[0] == 0f) {
|
||||||
NativeLibrary.onGamePadEvent(
|
NativeLibrary.onGamePadEvent(
|
||||||
NativeLibrary.TOUCHSCREEN_DEVICE,
|
NativeLibrary.TouchScreenDevice,
|
||||||
NativeLibrary.ButtonType.DPAD_LEFT,
|
NativeLibrary.ButtonType.DPAD_LEFT,
|
||||||
NativeLibrary.ButtonState.RELEASED
|
NativeLibrary.ButtonState.RELEASED
|
||||||
)
|
)
|
||||||
NativeLibrary.onGamePadEvent(
|
NativeLibrary.onGamePadEvent(
|
||||||
NativeLibrary.TOUCHSCREEN_DEVICE,
|
NativeLibrary.TouchScreenDevice,
|
||||||
NativeLibrary.ButtonType.DPAD_RIGHT,
|
NativeLibrary.ButtonType.DPAD_RIGHT,
|
||||||
NativeLibrary.ButtonState.RELEASED
|
NativeLibrary.ButtonState.RELEASED
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (axisValuesDPad[0] < 0f) {
|
if (axisValuesDPad[0] < 0f) {
|
||||||
NativeLibrary.onGamePadEvent(
|
NativeLibrary.onGamePadEvent(
|
||||||
NativeLibrary.TOUCHSCREEN_DEVICE,
|
NativeLibrary.TouchScreenDevice,
|
||||||
NativeLibrary.ButtonType.DPAD_LEFT,
|
NativeLibrary.ButtonType.DPAD_LEFT,
|
||||||
NativeLibrary.ButtonState.PRESSED
|
NativeLibrary.ButtonState.PRESSED
|
||||||
)
|
)
|
||||||
NativeLibrary.onGamePadEvent(
|
NativeLibrary.onGamePadEvent(
|
||||||
NativeLibrary.TOUCHSCREEN_DEVICE,
|
NativeLibrary.TouchScreenDevice,
|
||||||
NativeLibrary.ButtonType.DPAD_RIGHT,
|
NativeLibrary.ButtonType.DPAD_RIGHT,
|
||||||
NativeLibrary.ButtonState.RELEASED
|
NativeLibrary.ButtonState.RELEASED
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (axisValuesDPad[0] > 0f) {
|
if (axisValuesDPad[0] > 0f) {
|
||||||
NativeLibrary.onGamePadEvent(
|
NativeLibrary.onGamePadEvent(
|
||||||
NativeLibrary.TOUCHSCREEN_DEVICE,
|
NativeLibrary.TouchScreenDevice,
|
||||||
NativeLibrary.ButtonType.DPAD_LEFT,
|
NativeLibrary.ButtonType.DPAD_LEFT,
|
||||||
NativeLibrary.ButtonState.RELEASED
|
NativeLibrary.ButtonState.RELEASED
|
||||||
)
|
)
|
||||||
NativeLibrary.onGamePadEvent(
|
NativeLibrary.onGamePadEvent(
|
||||||
NativeLibrary.TOUCHSCREEN_DEVICE,
|
NativeLibrary.TouchScreenDevice,
|
||||||
NativeLibrary.ButtonType.DPAD_RIGHT,
|
NativeLibrary.ButtonType.DPAD_RIGHT,
|
||||||
NativeLibrary.ButtonState.PRESSED
|
NativeLibrary.ButtonState.PRESSED
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (axisValuesDPad[1] == 0f) {
|
if (axisValuesDPad[1] == 0f) {
|
||||||
NativeLibrary.onGamePadEvent(
|
NativeLibrary.onGamePadEvent(
|
||||||
NativeLibrary.TOUCHSCREEN_DEVICE,
|
NativeLibrary.TouchScreenDevice,
|
||||||
NativeLibrary.ButtonType.DPAD_UP,
|
NativeLibrary.ButtonType.DPAD_UP,
|
||||||
NativeLibrary.ButtonState.RELEASED
|
NativeLibrary.ButtonState.RELEASED
|
||||||
)
|
)
|
||||||
NativeLibrary.onGamePadEvent(
|
NativeLibrary.onGamePadEvent(
|
||||||
NativeLibrary.TOUCHSCREEN_DEVICE,
|
NativeLibrary.TouchScreenDevice,
|
||||||
NativeLibrary.ButtonType.DPAD_DOWN,
|
NativeLibrary.ButtonType.DPAD_DOWN,
|
||||||
NativeLibrary.ButtonState.RELEASED
|
NativeLibrary.ButtonState.RELEASED
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (axisValuesDPad[1] < 0f) {
|
if (axisValuesDPad[1] < 0f) {
|
||||||
NativeLibrary.onGamePadEvent(
|
NativeLibrary.onGamePadEvent(
|
||||||
NativeLibrary.TOUCHSCREEN_DEVICE,
|
NativeLibrary.TouchScreenDevice,
|
||||||
NativeLibrary.ButtonType.DPAD_UP,
|
NativeLibrary.ButtonType.DPAD_UP,
|
||||||
NativeLibrary.ButtonState.PRESSED
|
NativeLibrary.ButtonState.PRESSED
|
||||||
)
|
)
|
||||||
NativeLibrary.onGamePadEvent(
|
NativeLibrary.onGamePadEvent(
|
||||||
NativeLibrary.TOUCHSCREEN_DEVICE,
|
NativeLibrary.TouchScreenDevice,
|
||||||
NativeLibrary.ButtonType.DPAD_DOWN,
|
NativeLibrary.ButtonType.DPAD_DOWN,
|
||||||
NativeLibrary.ButtonState.RELEASED
|
NativeLibrary.ButtonState.RELEASED
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (axisValuesDPad[1] > 0f) {
|
if (axisValuesDPad[1] > 0f) {
|
||||||
NativeLibrary.onGamePadEvent(
|
NativeLibrary.onGamePadEvent(
|
||||||
NativeLibrary.TOUCHSCREEN_DEVICE,
|
NativeLibrary.TouchScreenDevice,
|
||||||
NativeLibrary.ButtonType.DPAD_UP,
|
NativeLibrary.ButtonType.DPAD_UP,
|
||||||
NativeLibrary.ButtonState.RELEASED
|
NativeLibrary.ButtonState.RELEASED
|
||||||
)
|
)
|
||||||
NativeLibrary.onGamePadEvent(
|
NativeLibrary.onGamePadEvent(
|
||||||
NativeLibrary.TOUCHSCREEN_DEVICE,
|
NativeLibrary.TouchScreenDevice,
|
||||||
NativeLibrary.ButtonType.DPAD_DOWN,
|
NativeLibrary.ButtonType.DPAD_DOWN,
|
||||||
NativeLibrary.ButtonState.PRESSED
|
NativeLibrary.ButtonState.PRESSED
|
||||||
)
|
)
|
||||||
|
|
@ -579,9 +522,7 @@ class EmulationActivity : AppCompatActivity() {
|
||||||
registerForActivityResult(OpenFileResultContract()) { result: Intent? ->
|
registerForActivityResult(OpenFileResultContract()) { result: Intent? ->
|
||||||
if (result == null) return@registerForActivityResult
|
if (result == null) return@registerForActivityResult
|
||||||
val selectedFiles = FileBrowserHelper.getSelectedFiles(
|
val selectedFiles = FileBrowserHelper.getSelectedFiles(
|
||||||
result,
|
result, applicationContext, listOf<String>("bin")
|
||||||
applicationContext,
|
|
||||||
listOf<String>("bin")
|
|
||||||
) ?: return@registerForActivityResult
|
) ?: return@registerForActivityResult
|
||||||
if (BuildUtil.isGooglePlayBuild) {
|
if (BuildUtil.isGooglePlayBuild) {
|
||||||
onAmiiboSelected(selectedFiles[0])
|
onAmiiboSelected(selectedFiles[0])
|
||||||
|
|
@ -598,12 +539,14 @@ class EmulationActivity : AppCompatActivity() {
|
||||||
return@registerForActivityResult
|
return@registerForActivityResult
|
||||||
}
|
}
|
||||||
|
|
||||||
onFilePickerResult(result.toString())
|
OnFilePickerResult(result.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private var instance: EmulationActivity? = null
|
private var instance: EmulationActivity? = null
|
||||||
|
|
||||||
fun isRunning(): Boolean = instance?.isEmulationRunning ?: false
|
fun isRunning(): Boolean {
|
||||||
|
return instance?.isEmulationRunning ?: false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
||||||
|
|
@ -15,9 +15,9 @@ import androidx.recyclerview.widget.ListAdapter
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import org.citra.citra_emu.R
|
import org.citra.citra_emu.R
|
||||||
import org.citra.citra_emu.databinding.CardDriverOptionBinding
|
import org.citra.citra_emu.databinding.CardDriverOptionBinding
|
||||||
import org.citra.citra_emu.utils.GpuDriverHelper
|
|
||||||
import org.citra.citra_emu.utils.GpuDriverMetadata
|
import org.citra.citra_emu.utils.GpuDriverMetadata
|
||||||
import org.citra.citra_emu.viewmodel.DriverViewModel
|
import org.citra.citra_emu.viewmodel.DriverViewModel
|
||||||
|
import org.citra.citra_emu.utils.GpuDriverHelper
|
||||||
|
|
||||||
class DriverAdapter(private val driverViewModel: DriverViewModel) :
|
class DriverAdapter(private val driverViewModel: DriverViewModel) :
|
||||||
ListAdapter<Pair<Uri, GpuDriverMetadata>, DriverAdapter.DriverViewHolder>(
|
ListAdapter<Pair<Uri, GpuDriverMetadata>, DriverAdapter.DriverViewHolder>(
|
||||||
|
|
@ -105,11 +105,15 @@ class DriverAdapter(private val driverViewModel: DriverViewModel) :
|
||||||
override fun areItemsTheSame(
|
override fun areItemsTheSame(
|
||||||
oldItem: Pair<Uri, GpuDriverMetadata>,
|
oldItem: Pair<Uri, GpuDriverMetadata>,
|
||||||
newItem: Pair<Uri, GpuDriverMetadata>
|
newItem: Pair<Uri, GpuDriverMetadata>
|
||||||
): Boolean = oldItem.first == newItem.first
|
): Boolean {
|
||||||
|
return oldItem.first == newItem.first
|
||||||
|
}
|
||||||
|
|
||||||
override fun areContentsTheSame(
|
override fun areContentsTheSame(
|
||||||
oldItem: Pair<Uri, GpuDriverMetadata>,
|
oldItem: Pair<Uri, GpuDriverMetadata>,
|
||||||
newItem: Pair<Uri, GpuDriverMetadata>
|
newItem: Pair<Uri, GpuDriverMetadata>
|
||||||
): Boolean = oldItem.second == newItem.second
|
): Boolean {
|
||||||
|
return oldItem.second == newItem.second
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,25 +4,24 @@
|
||||||
|
|
||||||
package org.citra.citra_emu.adapters
|
package org.citra.citra_emu.adapters
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
|
||||||
import android.content.SharedPreferences
|
|
||||||
import android.content.pm.ShortcutInfo
|
|
||||||
import android.content.pm.ShortcutManager
|
|
||||||
import android.graphics.Bitmap
|
|
||||||
import android.graphics.BitmapFactory
|
|
||||||
import android.graphics.drawable.BitmapDrawable
|
|
||||||
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
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.ImageView
|
import android.content.Context
|
||||||
import android.widget.PopupMenu
|
import android.content.SharedPreferences
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
import android.widget.ImageView
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import android.graphics.drawable.BitmapDrawable
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.content.pm.ShortcutInfo
|
||||||
|
import android.content.pm.ShortcutManager
|
||||||
|
import android.graphics.BitmapFactory
|
||||||
import androidx.activity.result.ActivityResultLauncher
|
import androidx.activity.result.ActivityResultLauncher
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.content.edit
|
import androidx.core.content.edit
|
||||||
|
|
@ -30,23 +29,23 @@ import androidx.core.graphics.scale
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
import androidx.documentfile.provider.DocumentFile
|
import androidx.documentfile.provider.DocumentFile
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import androidx.navigation.findNavController
|
import androidx.navigation.findNavController
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import androidx.recyclerview.widget.AsyncDifferConfig
|
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
|
||||||
import com.google.android.material.color.MaterialColors
|
import com.google.android.material.color.MaterialColors
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.citra.citra_emu.CitraApplication
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import org.citra.citra_emu.HomeNavigationDirections
|
import org.citra.citra_emu.HomeNavigationDirections
|
||||||
|
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.adapters.GameAdapter.GameViewHolder
|
import org.citra.citra_emu.adapters.GameAdapter.GameViewHolder
|
||||||
|
|
@ -65,12 +64,10 @@ class GameAdapter(
|
||||||
private val activity: AppCompatActivity,
|
private val activity: AppCompatActivity,
|
||||||
private val inflater: LayoutInflater,
|
private val inflater: LayoutInflater,
|
||||||
private val openImageLauncher: ActivityResultLauncher<String>?,
|
private val openImageLauncher: ActivityResultLauncher<String>?,
|
||||||
private val onRequestCompressOrDecompress: (
|
private val onRequestCompressOrDecompress: ((inputPath: String, suggestedName: String, shouldCompress: Boolean) -> Unit)? = null
|
||||||
(inputPath: String, suggestedName: String, shouldCompress: Boolean) -> Unit
|
) :
|
||||||
)? = null
|
ListAdapter<Game, GameViewHolder>(AsyncDifferConfig.Builder(DiffCallback()).build()),
|
||||||
) : ListAdapter<Game, GameViewHolder>(AsyncDifferConfig.Builder(DiffCallback()).build()),
|
View.OnClickListener, View.OnLongClickListener {
|
||||||
View.OnClickListener,
|
|
||||||
View.OnLongClickListener {
|
|
||||||
private var lastClickTime = 0L
|
private var lastClickTime = 0L
|
||||||
private var imagePath: String? = null
|
private var imagePath: String? = null
|
||||||
private var dialogShortcutBinding: DialogShortcutBinding? = null
|
private var dialogShortcutBinding: DialogShortcutBinding? = null
|
||||||
|
|
@ -214,8 +211,7 @@ class GameAdapter(
|
||||||
binding.textGameTitle.text = game.title
|
binding.textGameTitle.text = game.title
|
||||||
binding.textCompany.text = game.company
|
binding.textCompany.text = game.company
|
||||||
binding.textGameRegion.text = game.regions
|
binding.textGameRegion.text = game.regions
|
||||||
binding.imageCartridge.visibility =
|
binding.imageCartridge.visibility = if (preferences.getString("insertedCartridge", "") != game.path) {
|
||||||
if (preferences.getString("insertedCartridge", "") != game.path) {
|
|
||||||
View.GONE
|
View.GONE
|
||||||
} else {
|
} else {
|
||||||
View.VISIBLE
|
View.VISIBLE
|
||||||
|
|
@ -223,9 +219,7 @@ class GameAdapter(
|
||||||
|
|
||||||
val backgroundColorId =
|
val backgroundColorId =
|
||||||
if (
|
if (
|
||||||
isValidGame(
|
isValidGame(game.filename.substring(game.filename.lastIndexOf(".") + 1).lowercase())
|
||||||
game.filename.substring(game.filename.lastIndexOf(".") + 1).lowercase()
|
|
||||||
)
|
|
||||||
) {
|
) {
|
||||||
R.attr.colorSurface
|
R.attr.colorSurface
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -265,45 +259,16 @@ class GameAdapter(
|
||||||
val extraDir: String
|
val extraDir: String
|
||||||
)
|
)
|
||||||
private fun getGameDirectories(game: Game): GameDirectories {
|
private fun getGameDirectories(game: Game): GameDirectories {
|
||||||
val basePath =
|
val basePath = "sdmc/Nintendo 3DS/00000000000000000000000000000000/00000000000000000000000000000000"
|
||||||
"sdmc/Nintendo 3DS/00000000000000000000000000000000/00000000000000000000000000000000"
|
|
||||||
return GameDirectories(
|
return GameDirectories(
|
||||||
gameDir = game.path.substringBeforeLast("/"),
|
gameDir = game.path.substringBeforeLast("/"),
|
||||||
saveDir =
|
saveDir = basePath + "/title/${String.format("%016x", game.titleId).lowercase().substring(0, 8)}/${String.format("%016x", game.titleId).lowercase().substring(8)}/data/00000001",
|
||||||
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)}",
|
modsDir = "load/mods/${String.format("%016X", game.titleId)}",
|
||||||
texturesDir = "load/textures/${String.format("%016X", game.titleId)}",
|
texturesDir = "load/textures/${String.format("%016X", game.titleId)}",
|
||||||
appDir = game.path.substringBeforeLast("/").split("/").filter {
|
appDir = game.path.substringBeforeLast("/").split("/").filter { it.isNotEmpty() }.joinToString("/"),
|
||||||
it.isNotEmpty()
|
dlcDir = basePath + "/title/0004008c/${String.format("%016x", game.titleId).lowercase().substring(8)}/content",
|
||||||
}.joinToString("/"),
|
updatesDir = basePath + "/title/0004000e/${String.format("%016x", game.titleId).lowercase().substring(8)}/content",
|
||||||
dlcDir =
|
extraDir = basePath + "/extdata/00000000/${String.format("%016X", game.titleId).substring(8, 14).padStart(8, '0')}"
|
||||||
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')}"
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -333,36 +298,13 @@ class GameAdapter(
|
||||||
.setType("*/*")
|
.setType("*/*")
|
||||||
|
|
||||||
val uri = when (menuItem.itemId) {
|
val uri = when (menuItem.itemId) {
|
||||||
R.id.game_context_open_app -> CitraApplication.documentsTree.folderUriHelper(
|
R.id.game_context_open_app -> CitraApplication.documentsTree.folderUriHelper(dirs.appDir)
|
||||||
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_save_dir -> CitraApplication.documentsTree.folderUriHelper(
|
R.id.game_context_open_extra -> CitraApplication.documentsTree.folderUriHelper(dirs.extraDir)
|
||||||
dirs.saveDir
|
R.id.game_context_open_textures -> CitraApplication.documentsTree.folderUriHelper(dirs.texturesDir, true)
|
||||||
)
|
R.id.game_context_open_mods -> CitraApplication.documentsTree.folderUriHelper(dirs.modsDir, true)
|
||||||
|
|
||||||
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
|
else -> null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -376,11 +318,7 @@ class GameAdapter(
|
||||||
popup.show()
|
popup.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showUninstallContextMenu(
|
private fun showUninstallContextMenu(view: View, game: Game, bottomSheetDialog: BottomSheetDialog) {
|
||||||
view: View,
|
|
||||||
game: Game,
|
|
||||||
bottomSheetDialog: BottomSheetDialog
|
|
||||||
) {
|
|
||||||
val dirs = getGameDirectories(game)
|
val dirs = getGameDirectories(game)
|
||||||
val popup = PopupMenu(view.context, view).apply {
|
val popup = PopupMenu(view.context, view).apply {
|
||||||
menuInflater.inflate(R.menu.game_context_menu_uninstall, menu)
|
menuInflater.inflate(R.menu.game_context_menu_uninstall, menu)
|
||||||
|
|
@ -403,38 +341,16 @@ class GameAdapter(
|
||||||
popup.setOnMenuItemClickListener { menuItem ->
|
popup.setOnMenuItemClickListener { menuItem ->
|
||||||
val uninstallAction: () -> Unit = {
|
val uninstallAction: () -> Unit = {
|
||||||
when (menuItem.itemId) {
|
when (menuItem.itemId) {
|
||||||
R.id.game_context_uninstall -> NativeLibrary.uninstallTitle(
|
R.id.game_context_uninstall -> NativeLibrary.uninstallTitle(titleId, game.mediaType)
|
||||||
titleId,
|
R.id.game_context_uninstall_dlc -> NativeLibrary.uninstallTitle(dlcTitleId, Game.MediaType.SDMC)
|
||||||
game.mediaType
|
R.id.game_context_uninstall_updates -> NativeLibrary.uninstallTitle(updateTitleId, Game.MediaType.SDMC)
|
||||||
)
|
|
||||||
|
|
||||||
R.id.game_context_uninstall_dlc -> NativeLibrary.uninstallTitle(
|
|
||||||
dlcTitleId,
|
|
||||||
Game.MediaType.SDMC
|
|
||||||
)
|
|
||||||
|
|
||||||
R.id.game_context_uninstall_updates -> NativeLibrary.uninstallTitle(
|
|
||||||
updateTitleId,
|
|
||||||
Game.MediaType.SDMC
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
ViewModelProvider(activity)[GamesViewModel::class.java].reloadGames(true)
|
ViewModelProvider(activity)[GamesViewModel::class.java].reloadGames(true)
|
||||||
bottomSheetDialog.dismiss()
|
bottomSheetDialog.dismiss()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (menuItem.itemId in
|
if (menuItem.itemId in listOf(R.id.game_context_uninstall, R.id.game_context_uninstall_dlc, R.id.game_context_uninstall_updates)) {
|
||||||
listOf(
|
IndeterminateProgressDialogFragment.newInstance(activity, R.string.uninstalling, false, uninstallAction)
|
||||||
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)
|
.show(activity.supportFragmentManager, IndeterminateProgressDialogFragment.TAG)
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -445,12 +361,7 @@ class GameAdapter(
|
||||||
popup.show()
|
popup.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showAboutGameDialog(
|
private fun showAboutGameDialog(context: Context, game: Game, holder: GameViewHolder, view: View) {
|
||||||
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)
|
||||||
|
|
||||||
val bottomSheetDialog = BottomSheetDialog(context)
|
val bottomSheetDialog = BottomSheetDialog(context)
|
||||||
|
|
@ -462,22 +373,12 @@ class GameAdapter(
|
||||||
bottomSheetView.findViewById<TextView>(R.id.about_game_title).text = game.title
|
bottomSheetView.findViewById<TextView>(R.id.about_game_title).text = game.title
|
||||||
bottomSheetView.findViewById<TextView>(R.id.about_game_company).text = game.company
|
bottomSheetView.findViewById<TextView>(R.id.about_game_company).text = game.company
|
||||||
bottomSheetView.findViewById<TextView>(R.id.about_game_region).text = game.regions
|
bottomSheetView.findViewById<TextView>(R.id.about_game_region).text = game.regions
|
||||||
bottomSheetView.findViewById<TextView>(R.id.about_game_id).text =
|
bottomSheetView.findViewById<TextView>(R.id.about_game_id).text = context.getString(R.string.game_context_id) + " " + String.format("%016X", game.titleId)
|
||||||
context.getString(R.string.game_context_id) + " " + String.format("%016X", game.titleId)
|
bottomSheetView.findViewById<TextView>(R.id.about_game_filename).text = context.getString(R.string.game_context_file) + " " + game.filename
|
||||||
bottomSheetView.findViewById<TextView>(R.id.about_game_filename).text =
|
bottomSheetView.findViewById<TextView>(R.id.about_game_filetype).text = context.getString(R.string.game_context_type) + " " + game.fileType
|
||||||
context.getString(R.string.game_context_file) + " " + game.filename
|
|
||||||
bottomSheetView.findViewById<TextView>(R.id.about_game_filetype).text =
|
|
||||||
context.getString(R.string.game_context_type) + " " + game.fileType
|
|
||||||
|
|
||||||
val insertButton = bottomSheetView.findViewById<MaterialButton>(
|
val insertButton = bottomSheetView.findViewById<MaterialButton>(R.id.insert_cartridge_button)
|
||||||
R.id.insert_cartridge_button
|
insertButton.text = if (inserted) { context.getString(R.string.game_context_eject) } else { context.getString(R.string.game_context_insert) }
|
||||||
)
|
|
||||||
insertButton.text =
|
|
||||||
if (inserted) {
|
|
||||||
context.getString(R.string.game_context_eject)
|
|
||||||
} else {
|
|
||||||
context.getString(R.string.game_context_insert)
|
|
||||||
}
|
|
||||||
insertButton.visibility = if (insertable) View.VISIBLE else View.GONE
|
insertButton.visibility = if (insertable) View.VISIBLE else View.GONE
|
||||||
insertButton.setOnClickListener {
|
insertButton.setOnClickListener {
|
||||||
if (inserted) {
|
if (inserted) {
|
||||||
|
|
@ -518,7 +419,7 @@ class GameAdapter(
|
||||||
val preferences = PreferenceManager.getDefaultSharedPreferences(context)
|
val preferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
|
||||||
// Default to false for zoomed in shortcut icons
|
// Default to false for zoomed in shortcut icons
|
||||||
preferences.edit {
|
preferences.edit() {
|
||||||
putBoolean(
|
putBoolean(
|
||||||
"shouldStretchIcon",
|
"shouldStretchIcon",
|
||||||
false
|
false
|
||||||
|
|
@ -550,15 +451,10 @@ class GameAdapter(
|
||||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||||
val shortcutName = dialogShortcutBinding!!.shortcutNameInput.text.toString()
|
val shortcutName = dialogShortcutBinding!!.shortcutNameInput.text.toString()
|
||||||
if (shortcutName.isEmpty()) {
|
if (shortcutName.isEmpty()) {
|
||||||
Toast.makeText(
|
Toast.makeText(context, R.string.shortcut_name_empty, Toast.LENGTH_LONG).show()
|
||||||
context,
|
|
||||||
R.string.shortcut_name_empty,
|
|
||||||
Toast.LENGTH_LONG
|
|
||||||
).show()
|
|
||||||
return@setPositiveButton
|
return@setPositiveButton
|
||||||
}
|
}
|
||||||
val iconBitmap =
|
val iconBitmap = (dialogShortcutBinding!!.shortcutIcon.drawable as BitmapDrawable).bitmap
|
||||||
(dialogShortcutBinding!!.shortcutIcon.drawable as BitmapDrawable).bitmap
|
|
||||||
val shortcutManager = activity.getSystemService(ShortcutManager::class.java)
|
val shortcutManager = activity.getSystemService(ShortcutManager::class.java)
|
||||||
|
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
|
@ -566,11 +462,9 @@ class GameAdapter(
|
||||||
val shortcut = ShortcutInfo.Builder(context, shortcutName)
|
val shortcut = ShortcutInfo.Builder(context, shortcutName)
|
||||||
.setShortLabel(shortcutName)
|
.setShortLabel(shortcutName)
|
||||||
.setIcon(icon)
|
.setIcon(icon)
|
||||||
.setIntent(
|
.setIntent(game.launchIntent.apply {
|
||||||
game.launchIntent.apply {
|
|
||||||
putExtra("launchedFromShortcut", true)
|
putExtra("launchedFromShortcut", true)
|
||||||
}
|
})
|
||||||
)
|
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
shortcutManager?.requestPinShortcut(shortcut, null)
|
shortcutManager?.requestPinShortcut(shortcut, null)
|
||||||
|
|
@ -591,9 +485,7 @@ class GameAdapter(
|
||||||
bottomSheetDialog.dismiss()
|
bottomSheetDialog.dismiss()
|
||||||
}
|
}
|
||||||
|
|
||||||
val compressDecompressButton = bottomSheetView.findViewById<MaterialButton>(
|
val compressDecompressButton = bottomSheetView.findViewById<MaterialButton>(R.id.compress_decompress)
|
||||||
R.id.compress_decompress
|
|
||||||
)
|
|
||||||
if (game.isInstalled) {
|
if (game.isInstalled) {
|
||||||
compressDecompressButton.setOnClickListener {
|
compressDecompressButton.setOnClickListener {
|
||||||
Toast.makeText(
|
Toast.makeText(
|
||||||
|
|
@ -606,90 +498,22 @@ class GameAdapter(
|
||||||
} else {
|
} else {
|
||||||
compressDecompressButton.setOnClickListener {
|
compressDecompressButton.setOnClickListener {
|
||||||
val shouldCompress = !game.isCompressed
|
val shouldCompress = !game.isCompressed
|
||||||
val recommendedExt = NativeLibrary.getRecommendedExtension(
|
val recommendedExt = NativeLibrary.getRecommendedExtension(holder.game.path, shouldCompress)
|
||||||
holder.game.path,
|
|
||||||
shouldCompress
|
|
||||||
)
|
|
||||||
val baseName = holder.game.filename.substringBeforeLast('.')
|
val baseName = holder.game.filename.substringBeforeLast('.')
|
||||||
onRequestCompressOrDecompress?.invoke(
|
onRequestCompressOrDecompress?.invoke(holder.game.path, "$baseName.$recommendedExt", shouldCompress)
|
||||||
holder.game.path,
|
|
||||||
"$baseName.$recommendedExt",
|
|
||||||
shouldCompress
|
|
||||||
)
|
|
||||||
bottomSheetDialog.dismiss()
|
bottomSheetDialog.dismiss()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
compressDecompressButton.text =
|
compressDecompressButton.text = context.getString(if (!game.isCompressed) R.string.compress else R.string.decompress)
|
||||||
context.getString(if (!game.isCompressed) R.string.compress else R.string.decompress)
|
|
||||||
|
|
||||||
bottomSheetView.findViewById<MaterialButton>(R.id.menu_button_open).setOnClickListener {
|
bottomSheetView.findViewById<MaterialButton>(R.id.menu_button_open).setOnClickListener {
|
||||||
showOpenContextMenu(it, game)
|
showOpenContextMenu(it, game)
|
||||||
}
|
}
|
||||||
|
|
||||||
bottomSheetView.findViewById<MaterialButton>(
|
bottomSheetView.findViewById<MaterialButton>(R.id.menu_button_uninstall).setOnClickListener {
|
||||||
R.id.menu_button_uninstall
|
|
||||||
).setOnClickListener {
|
|
||||||
showUninstallContextMenu(it, game, bottomSheetDialog)
|
showUninstallContextMenu(it, game, bottomSheetDialog)
|
||||||
}
|
}
|
||||||
|
|
||||||
bottomSheetView.findViewById<MaterialButton>(R.id.delete_cache).setOnClickListener {
|
|
||||||
val options =
|
|
||||||
arrayOf(context.getString(R.string.vulkan), context.getString(R.string.opengles))
|
|
||||||
var selectedIndex = -1
|
|
||||||
val dialog = MaterialAlertDialogBuilder(context)
|
|
||||||
.setTitle(R.string.delete_cache_select_backend)
|
|
||||||
.setSingleChoiceItems(options, -1) { dialog, which ->
|
|
||||||
selectedIndex = which
|
|
||||||
}
|
|
||||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
|
||||||
val progToast = Toast.makeText(
|
|
||||||
CitraApplication.appContext,
|
|
||||||
R.string.deleting_shader_cache,
|
|
||||||
Toast.LENGTH_LONG
|
|
||||||
)
|
|
||||||
progToast.show()
|
|
||||||
|
|
||||||
activity.lifecycleScope.launch(Dispatchers.IO) {
|
|
||||||
when (selectedIndex) {
|
|
||||||
0 -> {
|
|
||||||
NativeLibrary.deleteVulkanShaderCache(game.titleId)
|
|
||||||
}
|
|
||||||
|
|
||||||
1 -> {
|
|
||||||
NativeLibrary.deleteOpenGLShaderCache(game.titleId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
activity.runOnUiThread {
|
|
||||||
progToast.cancel()
|
|
||||||
Toast.makeText(
|
|
||||||
CitraApplication.appContext,
|
|
||||||
R.string.shader_cache_deleted,
|
|
||||||
Toast.LENGTH_SHORT
|
|
||||||
).show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.setNegativeButton(android.R.string.cancel) { dialog, _ ->
|
|
||||||
dialog.dismiss()
|
|
||||||
}
|
|
||||||
.create()
|
|
||||||
|
|
||||||
dialog.setOnShowListener {
|
|
||||||
val positiveButton = dialog.getButton(android.app.AlertDialog.BUTTON_POSITIVE)
|
|
||||||
|
|
||||||
positiveButton.isEnabled = false
|
|
||||||
|
|
||||||
val listView = dialog.listView
|
|
||||||
listView.setOnItemClickListener { _, _, position, _ ->
|
|
||||||
selectedIndex = position
|
|
||||||
positiveButton.isEnabled = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dialog.show()
|
|
||||||
}
|
|
||||||
|
|
||||||
val bottomSheetBehavior = bottomSheetDialog.getBehavior()
|
val bottomSheetBehavior = bottomSheetDialog.getBehavior()
|
||||||
bottomSheetBehavior.skipCollapsed = true
|
bottomSheetBehavior.skipCollapsed = true
|
||||||
bottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
|
bottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
|
||||||
|
|
@ -738,8 +562,10 @@ class GameAdapter(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isValidGame(extension: String): Boolean = Game.badExtensions.stream()
|
private fun isValidGame(extension: String): Boolean {
|
||||||
|
return Game.badExtensions.stream()
|
||||||
.noneMatch { extension == it.lowercase() }
|
.noneMatch { extension == it.lowercase() }
|
||||||
|
}
|
||||||
|
|
||||||
private class DiffCallback : DiffUtil.ItemCallback<Game>() {
|
private class DiffCallback : DiffUtil.ItemCallback<Game>() {
|
||||||
override fun areItemsTheSame(oldItem: Game, newItem: Game): Boolean {
|
override fun areItemsTheSame(oldItem: Game, newItem: Game): Boolean {
|
||||||
|
|
@ -748,6 +574,8 @@ class GameAdapter(
|
||||||
return oldItem.titleId == newItem.titleId && oldItem.title == newItem.title
|
return oldItem.titleId == newItem.titleId && oldItem.title == newItem.title
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun areContentsTheSame(oldItem: Game, newItem: Game): Boolean = oldItem == newItem
|
override fun areContentsTheSame(oldItem: Game, newItem: Game): Boolean {
|
||||||
|
return oldItem == newItem
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,24 +9,27 @@ import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.content.res.ResourcesCompat
|
import androidx.core.content.res.ResourcesCompat
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.LifecycleOwner
|
import androidx.lifecycle.LifecycleOwner
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.lifecycle.repeatOnLifecycle
|
import androidx.lifecycle.repeatOnLifecycle
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import kotlinx.coroutines.flow.collect
|
import kotlinx.coroutines.flow.collect
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import org.citra.citra_emu.R
|
||||||
import org.citra.citra_emu.databinding.CardHomeOptionBinding
|
import org.citra.citra_emu.databinding.CardHomeOptionBinding
|
||||||
import org.citra.citra_emu.fragments.MessageDialogFragment
|
import org.citra.citra_emu.fragments.MessageDialogFragment
|
||||||
import org.citra.citra_emu.model.HomeSetting
|
import org.citra.citra_emu.model.HomeSetting
|
||||||
|
import org.citra.citra_emu.viewmodel.GamesViewModel
|
||||||
|
|
||||||
class HomeSettingAdapter(
|
class HomeSettingAdapter(
|
||||||
private val activity: AppCompatActivity,
|
private val activity: AppCompatActivity,
|
||||||
private val viewLifecycle: LifecycleOwner,
|
private val viewLifecycle: LifecycleOwner,
|
||||||
var options: List<HomeSetting>
|
var options: List<HomeSetting>
|
||||||
) : RecyclerView.Adapter<HomeSettingAdapter.HomeOptionViewHolder>(),
|
) : RecyclerView.Adapter<HomeSettingAdapter.HomeOptionViewHolder>(), View.OnClickListener {
|
||||||
View.OnClickListener {
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HomeOptionViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HomeOptionViewHolder {
|
||||||
val binding =
|
val binding =
|
||||||
CardHomeOptionBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
CardHomeOptionBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||||
|
|
@ -34,7 +37,9 @@ class HomeSettingAdapter(
|
||||||
return HomeOptionViewHolder(binding)
|
return HomeOptionViewHolder(binding)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getItemCount(): Int = options.size
|
override fun getItemCount(): Int {
|
||||||
|
return options.size
|
||||||
|
}
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: HomeOptionViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: HomeOptionViewHolder, position: Int) {
|
||||||
holder.bind(options[position])
|
holder.bind(options[position])
|
||||||
|
|
|
||||||
|
|
@ -8,17 +8,18 @@ import android.content.res.ColorStateList
|
||||||
import android.text.Html
|
import android.text.Html
|
||||||
import android.text.method.LinkMovementMethod
|
import android.text.method.LinkMovementMethod
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.content.res.ResourcesCompat
|
import androidx.core.content.res.ResourcesCompat
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.google.android.material.button.MaterialButton
|
import com.google.android.material.button.MaterialButton
|
||||||
import org.citra.citra_emu.R
|
|
||||||
import org.citra.citra_emu.databinding.PageSetupBinding
|
import org.citra.citra_emu.databinding.PageSetupBinding
|
||||||
import org.citra.citra_emu.model.ButtonState
|
import org.citra.citra_emu.model.ButtonState
|
||||||
import org.citra.citra_emu.model.PageState
|
import org.citra.citra_emu.model.PageState
|
||||||
import org.citra.citra_emu.model.SetupCallback
|
import org.citra.citra_emu.model.SetupCallback
|
||||||
import org.citra.citra_emu.model.SetupPage
|
import org.citra.citra_emu.model.SetupPage
|
||||||
|
import org.citra.citra_emu.R
|
||||||
import org.citra.citra_emu.utils.ViewUtils
|
import org.citra.citra_emu.utils.ViewUtils
|
||||||
|
|
||||||
class SetupAdapter(val activity: AppCompatActivity, val pages: List<SetupPage>) :
|
class SetupAdapter(val activity: AppCompatActivity, val pages: List<SetupPage>) :
|
||||||
|
|
@ -34,8 +35,7 @@ class SetupAdapter(val activity: AppCompatActivity, val pages: List<SetupPage>)
|
||||||
holder.bind(pages[position])
|
holder.bind(pages[position])
|
||||||
|
|
||||||
inner class SetupPageViewHolder(val binding: PageSetupBinding) :
|
inner class SetupPageViewHolder(val binding: PageSetupBinding) :
|
||||||
RecyclerView.ViewHolder(binding.root),
|
RecyclerView.ViewHolder(binding.root), SetupCallback {
|
||||||
SetupCallback {
|
|
||||||
lateinit var page: SetupPage
|
lateinit var page: SetupPage
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
|
@ -49,9 +49,7 @@ class SetupAdapter(val activity: AppCompatActivity, val pages: List<SetupPage>)
|
||||||
onStepCompleted(0, pageFullyCompleted = true)
|
onStepCompleted(0, pageFullyCompleted = true)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (page.pageButtons != null &&
|
if (page.pageButtons != null && page.pageSteps.invoke() != PageState.PAGE_STEPS_COMPLETE) {
|
||||||
page.pageSteps.invoke() != PageState.PAGE_STEPS_COMPLETE
|
|
||||||
) {
|
|
||||||
for (pageButton in page.pageButtons) {
|
for (pageButton in page.pageButtons) {
|
||||||
val pageButtonView = LayoutInflater.from(activity)
|
val pageButtonView = LayoutInflater.from(activity)
|
||||||
.inflate(
|
.inflate(
|
||||||
|
|
@ -110,17 +108,9 @@ class SetupAdapter(val activity: AppCompatActivity, val pages: List<SetupPage>)
|
||||||
.alpha(0.38f)
|
.alpha(0.38f)
|
||||||
.setDuration(200)
|
.setDuration(200)
|
||||||
.start()
|
.start()
|
||||||
button.setTextColor(
|
button.setTextColor(button.context.getColor(com.google.android.material.R.color.material_on_surface_disabled))
|
||||||
button.context.getColor(
|
|
||||||
com.google.android.material.R.color.material_on_surface_disabled
|
|
||||||
)
|
|
||||||
)
|
|
||||||
button.iconTint =
|
button.iconTint =
|
||||||
ColorStateList.valueOf(
|
ColorStateList.valueOf(button.context.getColor(com.google.android.material.R.color.material_on_surface_disabled))
|
||||||
button.context.getColor(
|
|
||||||
com.google.android.material.R.color.material_on_surface_disabled
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,20 @@
|
||||||
// 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.
|
||||||
|
|
||||||
package org.citra.citra_emu.applets
|
package org.citra.citra_emu.applets
|
||||||
|
|
||||||
import androidx.annotation.Keep
|
import androidx.annotation.Keep
|
||||||
import java.io.Serializable
|
|
||||||
import org.citra.citra_emu.NativeLibrary
|
import org.citra.citra_emu.NativeLibrary
|
||||||
import org.citra.citra_emu.fragments.MiiSelectorDialogFragment
|
import org.citra.citra_emu.fragments.MiiSelectorDialogFragment
|
||||||
|
import java.io.Serializable
|
||||||
|
|
||||||
@Keep
|
@Keep
|
||||||
object MiiSelector {
|
object MiiSelector {
|
||||||
lateinit var data: MiiSelectorData
|
lateinit var data: MiiSelectorData
|
||||||
val finishLock = Object()
|
val finishLock = Object()
|
||||||
|
|
||||||
private fun executeImpl(config: MiiSelectorConfig) {
|
private fun ExecuteImpl(config: MiiSelectorConfig) {
|
||||||
val emulationActivity = NativeLibrary.sEmulationActivity.get()
|
val emulationActivity = NativeLibrary.sEmulationActivity.get()
|
||||||
data = MiiSelectorData(0, 0)
|
data = MiiSelectorData(0, 0)
|
||||||
val fragment = MiiSelectorDialogFragment.newInstance(config)
|
val fragment = MiiSelectorDialogFragment.newInstance(config)
|
||||||
|
|
@ -22,8 +22,8 @@ object MiiSelector {
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun execute(config: MiiSelectorConfig): MiiSelectorData {
|
fun Execute(config: MiiSelectorConfig): MiiSelectorData {
|
||||||
NativeLibrary.sEmulationActivity.get()!!.runOnUiThread { executeImpl(config) }
|
NativeLibrary.sEmulationActivity.get()!!.runOnUiThread { ExecuteImpl(config) }
|
||||||
synchronized(finishLock) {
|
synchronized(finishLock) {
|
||||||
try {
|
try {
|
||||||
finishLock.wait()
|
finishLock.wait()
|
||||||
|
|
@ -43,5 +43,5 @@ object MiiSelector {
|
||||||
lateinit var miiNames: Array<String>
|
lateinit var miiNames: Array<String>
|
||||||
}
|
}
|
||||||
|
|
||||||
class MiiSelectorData(var returnCode: Long, var index: Int)
|
class MiiSelectorData (var returnCode: Long, var index: Int)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
||||||
|
|
@ -7,27 +7,27 @@ package org.citra.citra_emu.applets
|
||||||
import android.text.InputFilter
|
import android.text.InputFilter
|
||||||
import android.text.Spanned
|
import android.text.Spanned
|
||||||
import androidx.annotation.Keep
|
import androidx.annotation.Keep
|
||||||
import java.io.Serializable
|
|
||||||
import org.citra.citra_emu.CitraApplication.Companion.appContext
|
import org.citra.citra_emu.CitraApplication.Companion.appContext
|
||||||
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.fragments.KeyboardDialogFragment
|
import org.citra.citra_emu.fragments.KeyboardDialogFragment
|
||||||
import org.citra.citra_emu.fragments.MessageDialogFragment
|
import org.citra.citra_emu.fragments.MessageDialogFragment
|
||||||
import org.citra.citra_emu.utils.Log
|
import org.citra.citra_emu.utils.Log
|
||||||
|
import java.io.Serializable
|
||||||
|
|
||||||
@Keep
|
@Keep
|
||||||
object SoftwareKeyboard {
|
object SoftwareKeyboard {
|
||||||
lateinit var data: KeyboardData
|
lateinit var data: KeyboardData
|
||||||
val finishLock = Object()
|
val finishLock = Object()
|
||||||
|
|
||||||
private fun executeImpl(config: KeyboardConfig) {
|
private fun ExecuteImpl(config: KeyboardConfig) {
|
||||||
val emulationActivity = NativeLibrary.sEmulationActivity.get()
|
val emulationActivity = NativeLibrary.sEmulationActivity.get()
|
||||||
data = KeyboardData(0, "")
|
data = KeyboardData(0, "")
|
||||||
KeyboardDialogFragment.newInstance(config)
|
KeyboardDialogFragment.newInstance(config)
|
||||||
.show(emulationActivity!!.supportFragmentManager, KeyboardDialogFragment.TAG)
|
.show(emulationActivity!!.supportFragmentManager, KeyboardDialogFragment.TAG)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun handleValidationError(config: KeyboardConfig, error: ValidationError) {
|
fun HandleValidationError(config: KeyboardConfig, error: ValidationError) {
|
||||||
val emulationActivity = NativeLibrary.sEmulationActivity.get()!!
|
val emulationActivity = NativeLibrary.sEmulationActivity.get()!!
|
||||||
val message: String = when (error) {
|
val message: String = when (error) {
|
||||||
ValidationError.FixedLengthRequired -> emulationActivity.getString(
|
ValidationError.FixedLengthRequired -> emulationActivity.getString(
|
||||||
|
|
@ -54,12 +54,12 @@ object SoftwareKeyboard {
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun execute(config: KeyboardConfig): KeyboardData {
|
fun Execute(config: KeyboardConfig): KeyboardData {
|
||||||
if (config.buttonConfig == ButtonConfig.NONE) {
|
if (config.buttonConfig == ButtonConfig.None) {
|
||||||
Log.error("Unexpected button config None")
|
Log.error("Unexpected button config None")
|
||||||
return KeyboardData(0, "")
|
return KeyboardData(0, "")
|
||||||
}
|
}
|
||||||
NativeLibrary.sEmulationActivity.get()!!.runOnUiThread { executeImpl(config) }
|
NativeLibrary.sEmulationActivity.get()!!.runOnUiThread { ExecuteImpl(config) }
|
||||||
synchronized(finishLock) {
|
synchronized(finishLock) {
|
||||||
try {
|
try {
|
||||||
finishLock.wait()
|
finishLock.wait()
|
||||||
|
|
@ -69,9 +69,8 @@ object SoftwareKeyboard {
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("unused")
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun showError(error: String) {
|
fun ShowError(error: String) {
|
||||||
NativeLibrary.displayAlertMsg(
|
NativeLibrary.displayAlertMsg(
|
||||||
appContext.resources.getString(R.string.software_keyboard),
|
appContext.resources.getString(R.string.software_keyboard),
|
||||||
error,
|
error,
|
||||||
|
|
@ -79,23 +78,20 @@ object SoftwareKeyboard {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("FunctionName")
|
|
||||||
private external fun ValidateFilters(text: String): ValidationError
|
private external fun ValidateFilters(text: String): ValidationError
|
||||||
|
|
||||||
@Suppress("FunctionName")
|
|
||||||
external fun ValidateInput(text: String): ValidationError
|
external fun ValidateInput(text: String): ValidationError
|
||||||
|
|
||||||
// / Corresponds to Frontend::ButtonConfig
|
/// Corresponds to Frontend::ButtonConfig
|
||||||
interface ButtonConfig {
|
interface ButtonConfig {
|
||||||
companion object {
|
companion object {
|
||||||
const val SINGLE = 0 // / Ok button
|
const val Single = 0 /// Ok button
|
||||||
const val DUAL = 1 // / Cancel | Ok buttons
|
const val Dual = 1 /// Cancel | Ok buttons
|
||||||
const val TRIPLE = 2 // / Cancel | I Forgot | Ok buttons
|
const val Triple = 2 /// Cancel | I Forgot | Ok buttons
|
||||||
const val NONE = 3 // / No button (returned by swkbdInputText in special cases)
|
const val None = 3 /// No button (returned by swkbdInputText in special cases)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// / Corresponds to Frontend::ValidationError
|
/// Corresponds to Frontend::ValidationError
|
||||||
enum class ValidationError {
|
enum class ValidationError {
|
||||||
None,
|
None,
|
||||||
|
|
||||||
|
|
@ -132,7 +128,7 @@ object SoftwareKeyboard {
|
||||||
lateinit var buttonText: Array<String>
|
lateinit var buttonText: Array<String>
|
||||||
}
|
}
|
||||||
|
|
||||||
// / Corresponds to Frontend::KeyboardData
|
/// Corresponds to Frontend::KeyboardData
|
||||||
class KeyboardData(var button: Int, var text: String)
|
class KeyboardData(var button: Int, var text: String)
|
||||||
class Filter : InputFilter {
|
class Filter : InputFilter {
|
||||||
override fun filter(
|
override fun filter(
|
||||||
|
|
|
||||||
|
|
@ -21,10 +21,9 @@ object StillImageCameraHelper {
|
||||||
private var filePickerPath: String? = null
|
private var filePickerPath: String? = null
|
||||||
|
|
||||||
// Opens file picker for camera.
|
// Opens file picker for camera.
|
||||||
@Suppress("unused")
|
|
||||||
@Keep
|
@Keep
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun openFilePicker(): String? {
|
fun OpenFilePicker(): String? {
|
||||||
val emulationActivity = NativeLibrary.sEmulationActivity.get()
|
val emulationActivity = NativeLibrary.sEmulationActivity.get()
|
||||||
|
|
||||||
// At this point, we are assuming that we already have permissions as they are
|
// At this point, we are assuming that we already have permissions as they are
|
||||||
|
|
@ -45,16 +44,15 @@ object StillImageCameraHelper {
|
||||||
|
|
||||||
// Called from EmulationActivity.
|
// Called from EmulationActivity.
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun onFilePickerResult(result: String) {
|
fun OnFilePickerResult(result: String) {
|
||||||
filePickerPath = result
|
filePickerPath = result
|
||||||
synchronized(filePickerLock) { filePickerLock.notifyAll() }
|
synchronized(filePickerLock) { filePickerLock.notifyAll() }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Blocking call. Load image from file and crop/resize it to fit in width x height.
|
// Blocking call. Load image from file and crop/resize it to fit in width x height.
|
||||||
@Suppress("unused")
|
|
||||||
@Keep
|
@Keep
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun loadImageFromFile(uri: String?, width: Int, height: Int): Bitmap? {
|
fun LoadImageFromFile(uri: String?, width: Int, height: Int): Bitmap? {
|
||||||
val context = CitraApplication.appContext
|
val context = CitraApplication.appContext
|
||||||
val request = ImageRequest.Builder(context)
|
val request = ImageRequest.Builder(context)
|
||||||
.data(uri)
|
.data(uri)
|
||||||
|
|
|
||||||
|
|
@ -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,10 +9,11 @@ import android.content.Intent
|
||||||
import androidx.activity.result.contract.ActivityResultContract
|
import androidx.activity.result.contract.ActivityResultContract
|
||||||
|
|
||||||
class OpenFileResultContract : ActivityResultContract<Boolean?, Intent?>() {
|
class OpenFileResultContract : ActivityResultContract<Boolean?, Intent?>() {
|
||||||
override fun createIntent(context: Context, input: Boolean?): Intent =
|
override fun createIntent(context: Context, input: Boolean?): Intent {
|
||||||
Intent(Intent.ACTION_OPEN_DOCUMENT)
|
return Intent(Intent.ACTION_OPEN_DOCUMENT)
|
||||||
.setType("application/octet-stream")
|
.setType("application/octet-stream")
|
||||||
.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, input)
|
.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, input)
|
||||||
|
}
|
||||||
|
|
||||||
override fun parseResult(resultCode: Int, intent: Intent?): Intent? = intent
|
override fun parseResult(resultCode: Int, intent: Intent?): Intent? = intent
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,15 @@
|
||||||
|
|
||||||
package org.citra.citra_emu.display
|
package org.citra.citra_emu.display
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.pm.ActivityInfo
|
||||||
|
import android.app.Activity
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
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.features.settings.model.BooleanSetting
|
import org.citra.citra_emu.features.settings.model.BooleanSetting
|
||||||
import org.citra.citra_emu.features.settings.model.IntListSetting
|
|
||||||
import org.citra.citra_emu.features.settings.model.IntSetting
|
import org.citra.citra_emu.features.settings.model.IntSetting
|
||||||
|
import org.citra.citra_emu.features.settings.model.IntListSetting
|
||||||
import org.citra.citra_emu.features.settings.model.Settings
|
import org.citra.citra_emu.features.settings.model.Settings
|
||||||
import org.citra.citra_emu.features.settings.utils.SettingsFile
|
import org.citra.citra_emu.features.settings.utils.SettingsFile
|
||||||
import org.citra.citra_emu.utils.EmulationMenuSettings
|
import org.citra.citra_emu.utils.EmulationMenuSettings
|
||||||
|
|
@ -19,7 +20,7 @@ import org.citra.citra_emu.utils.EmulationMenuSettings
|
||||||
class ScreenAdjustmentUtil(
|
class ScreenAdjustmentUtil(
|
||||||
private val context: Context,
|
private val context: Context,
|
||||||
private val windowManager: WindowManager,
|
private val windowManager: WindowManager,
|
||||||
private val settings: Settings
|
private val settings: Settings,
|
||||||
) {
|
) {
|
||||||
fun swapScreen() {
|
fun swapScreen() {
|
||||||
val isEnabled = !EmulationMenuSettings.swapScreens
|
val isEnabled = !EmulationMenuSettings.swapScreens
|
||||||
|
|
@ -33,18 +34,17 @@ class ScreenAdjustmentUtil(
|
||||||
}
|
}
|
||||||
|
|
||||||
fun cycleLayouts() {
|
fun cycleLayouts() {
|
||||||
val landscapeLayoutsToCycle = IntListSetting.LAYOUTS_TO_CYCLE.list
|
|
||||||
|
val landscapeLayoutsToCycle = IntListSetting.LAYOUTS_TO_CYCLE.list;
|
||||||
val landscapeValues =
|
val landscapeValues =
|
||||||
if (landscapeLayoutsToCycle.isNotEmpty()) {
|
if (landscapeLayoutsToCycle.isNotEmpty())
|
||||||
landscapeLayoutsToCycle.toIntArray()
|
landscapeLayoutsToCycle.toIntArray()
|
||||||
} else {
|
else context.resources.getIntArray(
|
||||||
context.resources.getIntArray(
|
|
||||||
R.array.landscapeValues
|
R.array.landscapeValues
|
||||||
)
|
)
|
||||||
}
|
|
||||||
val portraitValues = context.resources.getIntArray(R.array.portraitValues)
|
val portraitValues = context.resources.getIntArray(R.array.portraitValues)
|
||||||
|
|
||||||
if (NativeLibrary.isPortraitMode()) {
|
if (NativeLibrary.isPortraitMode) {
|
||||||
val currentLayout = IntSetting.PORTRAIT_SCREEN_LAYOUT.int
|
val currentLayout = IntSetting.PORTRAIT_SCREEN_LAYOUT.int
|
||||||
val pos = portraitValues.indexOf(currentLayout)
|
val pos = portraitValues.indexOf(currentLayout)
|
||||||
val layoutOption = portraitValues[(pos + 1) % portraitValues.size]
|
val layoutOption = portraitValues[(pos + 1) % portraitValues.size]
|
||||||
|
|
@ -61,32 +61,14 @@ class ScreenAdjustmentUtil(
|
||||||
IntSetting.PORTRAIT_SCREEN_LAYOUT.int = layoutOption
|
IntSetting.PORTRAIT_SCREEN_LAYOUT.int = layoutOption
|
||||||
settings.saveSetting(IntSetting.PORTRAIT_SCREEN_LAYOUT, SettingsFile.FILE_NAME_CONFIG)
|
settings.saveSetting(IntSetting.PORTRAIT_SCREEN_LAYOUT, SettingsFile.FILE_NAME_CONFIG)
|
||||||
NativeLibrary.reloadSettings()
|
NativeLibrary.reloadSettings()
|
||||||
NativeLibrary.updateFramebuffer(NativeLibrary.isPortraitMode())
|
NativeLibrary.updateFramebuffer(NativeLibrary.isPortraitMode)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun changeScreenOrientation(layoutOption: Int) {
|
fun changeScreenOrientation(layoutOption: Int) {
|
||||||
IntSetting.SCREEN_LAYOUT.int = layoutOption
|
IntSetting.SCREEN_LAYOUT.int = layoutOption
|
||||||
settings.saveSetting(IntSetting.SCREEN_LAYOUT, SettingsFile.FILE_NAME_CONFIG)
|
settings.saveSetting(IntSetting.SCREEN_LAYOUT, SettingsFile.FILE_NAME_CONFIG)
|
||||||
NativeLibrary.reloadSettings()
|
NativeLibrary.reloadSettings()
|
||||||
NativeLibrary.updateFramebuffer(NativeLibrary.isPortraitMode())
|
NativeLibrary.updateFramebuffer(NativeLibrary.isPortraitMode)
|
||||||
}
|
|
||||||
|
|
||||||
fun changeSecondaryOrientation(layoutOption: Int) {
|
|
||||||
IntSetting.SECONDARY_DISPLAY_LAYOUT.int = layoutOption
|
|
||||||
settings.saveSetting(IntSetting.SECONDARY_DISPLAY_LAYOUT, SettingsFile.FILE_NAME_CONFIG)
|
|
||||||
NativeLibrary.reloadSettings()
|
|
||||||
NativeLibrary.updateFramebuffer(NativeLibrary.isPortraitMode())
|
|
||||||
}
|
|
||||||
|
|
||||||
fun enableSecondaryDisplay(layoutOption: Int) {
|
|
||||||
BooleanSetting.ENABLE_SECONDARY_DISPLAY.boolean = true
|
|
||||||
settings.saveSetting(BooleanSetting.ENABLE_SECONDARY_DISPLAY, SettingsFile.FILE_NAME_CONFIG)
|
|
||||||
changeSecondaryOrientation(layoutOption)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun disableSecondaryDisplay() {
|
|
||||||
BooleanSetting.ENABLE_SECONDARY_DISPLAY.boolean = false
|
|
||||||
settings.saveSetting(BooleanSetting.ENABLE_SECONDARY_DISPLAY, SettingsFile.FILE_NAME_CONFIG)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun changeActivityOrientation(orientationOption: Int) {
|
fun changeActivityOrientation(orientationOption: Int) {
|
||||||
|
|
@ -101,6 +83,7 @@ class ScreenAdjustmentUtil(
|
||||||
BooleanSetting.UPRIGHT_SCREEN.boolean = !uprightBoolean
|
BooleanSetting.UPRIGHT_SCREEN.boolean = !uprightBoolean
|
||||||
settings.saveSetting(BooleanSetting.UPRIGHT_SCREEN, SettingsFile.FILE_NAME_CONFIG)
|
settings.saveSetting(BooleanSetting.UPRIGHT_SCREEN, SettingsFile.FILE_NAME_CONFIG)
|
||||||
NativeLibrary.reloadSettings()
|
NativeLibrary.reloadSettings()
|
||||||
NativeLibrary.updateFramebuffer(NativeLibrary.isPortraitMode())
|
NativeLibrary.updateFramebuffer(NativeLibrary.isPortraitMode)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,11 @@ enum class ScreenLayout(val int: Int) {
|
||||||
HYBRID_SCREEN(4),
|
HYBRID_SCREEN(4),
|
||||||
CUSTOM_LAYOUT(5);
|
CUSTOM_LAYOUT(5);
|
||||||
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun from(int: Int): ScreenLayout = entries.firstOrNull { it.int == int } ?: LARGE_SCREEN
|
fun from(int: Int): ScreenLayout {
|
||||||
|
return entries.firstOrNull { it.int == int } ?: LARGE_SCREEN
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -29,7 +32,9 @@ enum class SmallScreenPosition(val int: Int) {
|
||||||
BELOW(7);
|
BELOW(7);
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun from(int: Int): SmallScreenPosition = entries.firstOrNull { it.int == int } ?: TOP_RIGHT
|
fun from(int: Int): SmallScreenPosition {
|
||||||
|
return entries.firstOrNull { it.int == int } ?: TOP_RIGHT
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -40,27 +45,23 @@ enum class PortraitScreenLayout(val int: Int) {
|
||||||
ORIGINAL(2);
|
ORIGINAL(2);
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun from(int: Int): PortraitScreenLayout =
|
fun from(int: Int): PortraitScreenLayout {
|
||||||
entries.firstOrNull { it.int == int } ?: TOP_FULL_WIDTH
|
return entries.firstOrNull { it.int == int } ?: TOP_FULL_WIDTH
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class SecondaryDisplayLayout(val int: Int) {
|
enum class SecondaryDisplayLayout(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
|
||||||
// NONE is no longer selectable in the interface, having been replaced with
|
|
||||||
// the boolean ENABLE_SECONDARY_DISPLAY setting, but is left here for backwards compatibility
|
|
||||||
NONE(0),
|
NONE(0),
|
||||||
TOP_SCREEN(1),
|
TOP_SCREEN(1),
|
||||||
BOTTOM_SCREEN(2),
|
BOTTOM_SCREEN(2),
|
||||||
SIDE_BY_SIDE(3),
|
SIDE_BY_SIDE(3);
|
||||||
REVERSE_PRIMARY(4),
|
|
||||||
ORIGINAL(5),
|
|
||||||
HYBRID(6),
|
|
||||||
LARGE_SCREEN(7)
|
|
||||||
;
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun from(int: Int): SecondaryDisplayLayout = entries.firstOrNull { it.int == int } ?: NONE
|
fun from(int: Int): SecondaryDisplayLayout {
|
||||||
|
return entries.firstOrNull { it.int == int } ?: NONE
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -73,7 +74,9 @@ enum class StereoWhichDisplay(val int: Int) {
|
||||||
SECONDARY_ONLY(3);
|
SECONDARY_ONLY(3);
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun from(int: Int): StereoWhichDisplay = entries.firstOrNull { it.int == int } ?: NONE
|
fun from(int: Int): StereoWhichDisplay {
|
||||||
|
return entries.firstOrNull { it.int == int } ?: NONE
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -85,10 +88,12 @@ enum class StereoMode(val int: Int) {
|
||||||
SIDE_BY_SIDE_FULL(2),
|
SIDE_BY_SIDE_FULL(2),
|
||||||
ANAGLYPH(3),
|
ANAGLYPH(3),
|
||||||
INTERLACED(4),
|
INTERLACED(4),
|
||||||
REVERSE_INTERLACED(5),
|
REVERSE_INTERLACED (5),
|
||||||
CARDBOARD_VR(6);
|
CARDBOARD_VR (6);
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun from(int: Int): StereoMode = entries.firstOrNull { it.int == int } ?: OFF
|
fun from(int: Int): StereoMode {
|
||||||
|
return entries.firstOrNull { it.int == int } ?: OFF
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -8,27 +8,19 @@ import android.app.Presentation
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.hardware.display.DisplayManager
|
import android.hardware.display.DisplayManager
|
||||||
import android.hardware.display.VirtualDisplay
|
import android.hardware.display.VirtualDisplay
|
||||||
import android.os.Build
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.Display
|
import android.view.Display
|
||||||
import android.view.MotionEvent
|
import android.view.MotionEvent
|
||||||
import android.view.SurfaceHolder
|
import android.view.SurfaceHolder
|
||||||
import android.view.SurfaceView
|
import android.view.SurfaceView
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
import org.citra.citra_emu.NativeLibrary
|
|
||||||
import org.citra.citra_emu.features.settings.model.BooleanSetting
|
|
||||||
import org.citra.citra_emu.features.settings.model.IntSetting
|
import org.citra.citra_emu.features.settings.model.IntSetting
|
||||||
import org.citra.citra_emu.utils.Log
|
import org.citra.citra_emu.NativeLibrary
|
||||||
|
|
||||||
class SecondaryDisplay(val context: Context) : DisplayManager.DisplayListener {
|
class SecondaryDisplay(val context: Context) : DisplayManager.DisplayListener {
|
||||||
private var pres: SecondaryDisplayPresentation? = null
|
private var pres: SecondaryDisplayPresentation? = null
|
||||||
private val displayManager = context.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
|
private val displayManager = context.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
|
||||||
private val vd: VirtualDisplay
|
private val vd: VirtualDisplay
|
||||||
var preferredDisplayId = -1
|
|
||||||
var currentDisplayId = -1
|
|
||||||
|
|
||||||
val availableDisplays: List<Display>
|
|
||||||
get() = getSecondaryDisplays()
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
vd = displayManager.createVirtualDisplay(
|
vd = displayManager.createVirtualDisplay(
|
||||||
|
|
@ -43,40 +35,31 @@ class SecondaryDisplay(val context: Context) : DisplayManager.DisplayListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateSurface() {
|
fun updateSurface() {
|
||||||
val surface = pres?.getSurfaceHolder()?.surface
|
NativeLibrary.secondarySurfaceChanged(pres!!.getSurfaceHolder().surface)
|
||||||
if (surface != null && surface.isValid) {
|
|
||||||
NativeLibrary.secondarySurfaceChanged(surface)
|
|
||||||
} else {
|
|
||||||
Log.warning("SecondaryDisplay Attempted to update null or invalid surface")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun destroySurface() {
|
fun destroySurface() {
|
||||||
NativeLibrary.secondarySurfaceDestroyed()
|
NativeLibrary.secondarySurfaceDestroyed()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getSecondaryDisplays(): List<Display> {
|
private fun getExternalDisplay(context: Context): Display? {
|
||||||
val dm = context.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
|
val dm = context.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
|
||||||
val currentDisplayId = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
val currentDisplayId = context.display.displayId
|
||||||
context.display.displayId
|
|
||||||
} else {
|
|
||||||
@Suppress("DEPRECATION")
|
|
||||||
(context.getSystemService(Context.WINDOW_SERVICE) as WindowManager)
|
|
||||||
.defaultDisplay.displayId
|
|
||||||
}
|
|
||||||
val displays = dm.displays
|
val displays = dm.displays
|
||||||
val presDisplays = dm.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION)
|
val presDisplays = dm.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION);
|
||||||
return displays.filter {
|
val extDisplays = displays.filter {
|
||||||
val isPresentable = presDisplays.any { pd -> pd.displayId == it.displayId }
|
val isPresentable = presDisplays.any { pd -> pd.displayId == it.displayId }
|
||||||
val isNotDefaultOrPresentable =
|
val isNotDefaultOrPresentable = it.displayId != Display.DEFAULT_DISPLAY || isPresentable
|
||||||
(it != null && it.displayId != Display.DEFAULT_DISPLAY) || isPresentable
|
|
||||||
|
|
||||||
isNotDefaultOrPresentable &&
|
isNotDefaultOrPresentable &&
|
||||||
it.displayId != currentDisplayId &&
|
it.displayId != currentDisplayId &&
|
||||||
it.name != "HiddenDisplay" &&
|
it.name != "HiddenDisplay" &&
|
||||||
it.state != Display.STATE_OFF &&
|
it.state != Display.STATE_OFF &&
|
||||||
it.isValid
|
it.isValid
|
||||||
}
|
}
|
||||||
|
// if there is a display called Built-In Display or Built-In Screen, prioritize the OTHER screen
|
||||||
|
val selected = extDisplays.firstOrNull { ! it.name.contains("Built",true) }
|
||||||
|
?: extDisplays.firstOrNull()
|
||||||
|
return selected
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateDisplay() {
|
fun updateDisplay() {
|
||||||
|
|
@ -85,40 +68,21 @@ class SecondaryDisplay(val context: Context) : DisplayManager.DisplayListener {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val displayToUse = if (availableDisplays.isEmpty() ||
|
// decide if we are going to the external display or the internal one
|
||||||
// Theoretically, the NONE option is no longer selectable, but
|
var display = getExternalDisplay(context)
|
||||||
// I am leaving this in for backwards compatibility
|
if (display == null ||
|
||||||
IntSetting.SECONDARY_DISPLAY_LAYOUT.int == SecondaryDisplayLayout.NONE.int ||
|
IntSetting.SECONDARY_DISPLAY_LAYOUT.int == SecondaryDisplayLayout.NONE.int) {
|
||||||
!BooleanSetting.ENABLE_SECONDARY_DISPLAY.boolean
|
display = vd.display
|
||||||
) {
|
|
||||||
currentDisplayId = -1
|
|
||||||
vd.display
|
|
||||||
} else if (preferredDisplayId >= 0 &&
|
|
||||||
availableDisplays.any { it.displayId == preferredDisplayId }
|
|
||||||
) {
|
|
||||||
currentDisplayId = preferredDisplayId
|
|
||||||
availableDisplays.first { it.displayId == preferredDisplayId }
|
|
||||||
} else {
|
|
||||||
val dm = context.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
|
|
||||||
val default = dm.displays.first { it.displayId == Display.DEFAULT_DISPLAY }
|
|
||||||
// prioritize displays that have a different name from the default display, as
|
|
||||||
// some devices such as the Odin 2 create a permanent virtual display with the same
|
|
||||||
// name as the default display that should be skipped in most cases
|
|
||||||
currentDisplayId = availableDisplays.firstOrNull {
|
|
||||||
it.name != default.name && !it.name.contains("Built", true)
|
|
||||||
}?.displayId
|
|
||||||
?: availableDisplays[0].displayId
|
|
||||||
availableDisplays.first { it.displayId == currentDisplayId }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// if our presentation is already on the right display, ignore
|
// if our presentation is already on the right display, ignore
|
||||||
if (pres?.display == displayToUse) return
|
if (pres?.display == display) return
|
||||||
|
|
||||||
// otherwise, make a new presentation
|
// otherwise, make a new presentation
|
||||||
releasePresentation()
|
releasePresentation()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
pres = SecondaryDisplayPresentation(context, displayToUse!!, this)
|
pres = SecondaryDisplayPresentation(context, display!!, this)
|
||||||
pres?.show()
|
pres?.show()
|
||||||
}
|
}
|
||||||
// catch BadTokenException and InvalidDisplayException,
|
// catch BadTokenException and InvalidDisplayException,
|
||||||
|
|
@ -155,9 +119,7 @@ class SecondaryDisplay(val context: Context) : DisplayManager.DisplayListener {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
class SecondaryDisplayPresentation(
|
class SecondaryDisplayPresentation(
|
||||||
context: Context,
|
context: Context, display: Display, val parent: SecondaryDisplay
|
||||||
display: Display,
|
|
||||||
val parent: SecondaryDisplay
|
|
||||||
) : Presentation(context, display) {
|
) : Presentation(context, display) {
|
||||||
private lateinit var surfaceView: SurfaceView
|
private lateinit var surfaceView: SurfaceView
|
||||||
private var touchscreenPointerId = -1
|
private var touchscreenPointerId = -1
|
||||||
|
|
@ -175,21 +137,16 @@ class SecondaryDisplayPresentation(
|
||||||
surfaceView = SurfaceView(context)
|
surfaceView = SurfaceView(context)
|
||||||
surfaceView.holder.addCallback(object : SurfaceHolder.Callback {
|
surfaceView.holder.addCallback(object : SurfaceHolder.Callback {
|
||||||
override fun surfaceCreated(holder: SurfaceHolder) {
|
override fun surfaceCreated(holder: SurfaceHolder) {
|
||||||
Log.debug("SecondaryDisplay Surface created")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun surfaceChanged(
|
override fun surfaceChanged(
|
||||||
holder: SurfaceHolder,
|
holder: SurfaceHolder, format: Int, width: Int, height: Int
|
||||||
format: Int,
|
|
||||||
width: Int,
|
|
||||||
height: Int
|
|
||||||
) {
|
) {
|
||||||
Log.debug("SecondaryDisplay Surface changed: ${width}x$height")
|
|
||||||
parent.updateSurface()
|
parent.updateSurface()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun surfaceDestroyed(holder: SurfaceHolder) {
|
override fun surfaceDestroyed(holder: SurfaceHolder) {
|
||||||
Log.debug("SecondaryDisplay Surface destroyed")
|
|
||||||
parent.destroySurface()
|
parent.destroySurface()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
@ -234,5 +191,7 @@ class SecondaryDisplayPresentation(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Publicly accessible method to get the SurfaceHolder
|
// Publicly accessible method to get the SurfaceHolder
|
||||||
fun getSurfaceHolder(): SurfaceHolder = surfaceView.holder
|
fun getSurfaceHolder(): SurfaceHolder {
|
||||||
|
return surfaceView.holder
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
||||||
|
|
@ -53,7 +53,7 @@ class CheatsViewModel : ViewModel() {
|
||||||
private var selectedCheatPosition = -1
|
private var selectedCheatPosition = -1
|
||||||
|
|
||||||
fun initialize(titleId_: Long) {
|
fun initialize(titleId_: Long) {
|
||||||
titleId = titleId_
|
titleId = titleId_;
|
||||||
load()
|
load()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
||||||
|
|
@ -170,7 +170,8 @@ class CheatDetailsFragment : Fragment() {
|
||||||
binding.buttonOk.visibility = if (isEditing) View.VISIBLE else View.GONE
|
binding.buttonOk.visibility = if (isEditing) View.VISIBLE else View.GONE
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setInsets() = ViewCompat.setOnApplyWindowInsetsListener(
|
private fun setInsets() =
|
||||||
|
ViewCompat.setOnApplyWindowInsetsListener(
|
||||||
binding.root
|
binding.root
|
||||||
) { _: View?, windowInsets: WindowInsetsCompat ->
|
) { _: View?, windowInsets: WindowInsetsCompat ->
|
||||||
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
|
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 @@ class CheatsAdapter(
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class CheatViewHolder(private val binding: ListItemCheatBinding) :
|
inner class CheatViewHolder(private val binding: ListItemCheatBinding) :
|
||||||
RecyclerView.ViewHolder(binding.root),
|
RecyclerView.ViewHolder(binding.root), View.OnClickListener,
|
||||||
View.OnClickListener,
|
|
||||||
CompoundButton.OnCheckedChangeListener {
|
CompoundButton.OnCheckedChangeListener {
|
||||||
private lateinit var viewModel: CheatsViewModel
|
private lateinit var viewModel: CheatsViewModel
|
||||||
private lateinit var cheat: Cheat
|
private lateinit var cheat: Cheat
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
||||||
|
|
@ -32,9 +32,7 @@ import org.citra.citra_emu.ui.TwoPaneOnBackPressedCallback
|
||||||
import org.citra.citra_emu.ui.main.MainActivity
|
import org.citra.citra_emu.ui.main.MainActivity
|
||||||
import org.citra.citra_emu.viewmodel.HomeViewModel
|
import org.citra.citra_emu.viewmodel.HomeViewModel
|
||||||
|
|
||||||
class CheatsFragment :
|
class CheatsFragment : Fragment(), SlidingPaneLayout.PanelSlideListener {
|
||||||
Fragment(),
|
|
||||||
SlidingPaneLayout.PanelSlideListener {
|
|
||||||
private var cheatListLastFocus: View? = null
|
private var cheatListLastFocus: View? = null
|
||||||
private var cheatDetailsLastFocus: View? = null
|
private var cheatDetailsLastFocus: View? = null
|
||||||
|
|
||||||
|
|
@ -159,15 +157,12 @@ class CheatsFragment :
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onSelectedCheatChanged(selectedCheat: Cheat?) {
|
private fun onSelectedCheatChanged(selectedCheat: Cheat?) {
|
||||||
val cheatSelected = selectedCheat != null || cheatsViewModel.isEditing.value
|
val cheatSelected = selectedCheat != null || cheatsViewModel.isEditing.value!!
|
||||||
if (!cheatSelected && binding.slidingPaneLayout.isOpen) {
|
if (!cheatSelected && binding.slidingPaneLayout.isOpen) {
|
||||||
binding.slidingPaneLayout.close()
|
binding.slidingPaneLayout.close()
|
||||||
}
|
}
|
||||||
binding.slidingPaneLayout.lockMode = if (cheatSelected) {
|
binding.slidingPaneLayout.lockMode =
|
||||||
SlidingPaneLayout.LOCK_MODE_UNLOCKED
|
if (cheatSelected) SlidingPaneLayout.LOCK_MODE_UNLOCKED else SlidingPaneLayout.LOCK_MODE_LOCKED_CLOSED
|
||||||
} else {
|
|
||||||
SlidingPaneLayout.LOCK_MODE_LOCKED_CLOSED
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onListViewFocusChange(hasFocus: Boolean) {
|
fun onListViewFocusChange(hasFocus: Boolean) {
|
||||||
|
|
@ -208,8 +203,7 @@ class CheatsFragment :
|
||||||
val keyboardInsets = windowInsets.getInsets(WindowInsetsCompat.Type.ime())
|
val keyboardInsets = windowInsets.getInsets(WindowInsetsCompat.Type.ime())
|
||||||
|
|
||||||
// Set keyboard insets if the system supports smooth keyboard animations
|
// Set keyboard insets if the system supports smooth keyboard animations
|
||||||
val mlpDetails = binding.cheatDetailsContainer.layoutParams
|
val mlpDetails = binding.cheatDetailsContainer.layoutParams as ViewGroup.MarginLayoutParams
|
||||||
as ViewGroup.MarginLayoutParams
|
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
|
||||||
if (keyboardInsets.bottom > 0) {
|
if (keyboardInsets.bottom > 0) {
|
||||||
mlpDetails.bottomMargin = keyboardInsets.bottom
|
mlpDetails.bottomMargin = keyboardInsets.bottom
|
||||||
|
|
@ -237,16 +231,14 @@ class CheatsFragment :
|
||||||
runningAnimations: List<WindowInsetsAnimationCompat>
|
runningAnimations: List<WindowInsetsAnimationCompat>
|
||||||
): WindowInsetsCompat {
|
): WindowInsetsCompat {
|
||||||
val mlpDetails =
|
val mlpDetails =
|
||||||
binding.cheatDetailsContainer.layoutParams
|
binding.cheatDetailsContainer.layoutParams as ViewGroup.MarginLayoutParams
|
||||||
as ViewGroup.MarginLayoutParams
|
|
||||||
keyboardInsets = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom
|
keyboardInsets = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom
|
||||||
barInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars()).bottom
|
barInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars()).bottom
|
||||||
mlpDetails.bottomMargin = keyboardInsets.coerceAtLeast(barInsets)
|
mlpDetails.bottomMargin = keyboardInsets.coerceAtLeast(barInsets)
|
||||||
binding.cheatDetailsContainer.layoutParams = mlpDetails
|
binding.cheatDetailsContainer.layoutParams = mlpDetails
|
||||||
return insets
|
return insets
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,5 +12,5 @@ enum class Hotkey(val button: Int) {
|
||||||
QUICKSAVE(10005),
|
QUICKSAVE(10005),
|
||||||
QUICKLOAD(10006),
|
QUICKLOAD(10006),
|
||||||
TURBO_LIMIT(10007),
|
TURBO_LIMIT(10007),
|
||||||
ENABLE(10008)
|
ENABLE(10008);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,11 +11,11 @@ import androidx.preference.PreferenceManager
|
||||||
import org.citra.citra_emu.CitraApplication
|
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.display.ScreenAdjustmentUtil
|
|
||||||
import org.citra.citra_emu.features.settings.model.Settings
|
|
||||||
import org.citra.citra_emu.features.settings.model.view.InputBindingSetting
|
|
||||||
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.utils.TurboHelper
|
||||||
|
import org.citra.citra_emu.display.ScreenAdjustmentUtil
|
||||||
|
import org.citra.citra_emu.features.settings.model.view.InputBindingSetting
|
||||||
|
import org.citra.citra_emu.features.settings.model.Settings
|
||||||
|
|
||||||
class HotkeyUtility(
|
class HotkeyUtility(
|
||||||
private val screenAdjustmentUtil: ScreenAdjustmentUtil,
|
private val screenAdjustmentUtil: ScreenAdjustmentUtil,
|
||||||
|
|
@ -41,7 +41,7 @@ class HotkeyUtility(
|
||||||
// Now process all internal buttons associated with this keypress
|
// Now process all internal buttons associated with this keypress
|
||||||
for (button in buttonSet) {
|
for (button in buttonSet) {
|
||||||
currentlyPressedButtons.add(button)
|
currentlyPressedButtons.add(button)
|
||||||
// option 1 - this is the enable command, which was already handled
|
//option 1 - this is the enable command, which was already handled
|
||||||
if (button == Hotkey.ENABLE.button) {
|
if (button == Hotkey.ENABLE.button) {
|
||||||
handled = true
|
handled = true
|
||||||
}
|
}
|
||||||
|
|
@ -74,8 +74,7 @@ class HotkeyUtility(
|
||||||
val thisKeyIsHotkey =
|
val thisKeyIsHotkey =
|
||||||
!thisKeyIsEnableButton && Hotkey.entries.any { buttonSet.contains(it.button) }
|
!thisKeyIsEnableButton && Hotkey.entries.any { buttonSet.contains(it.button) }
|
||||||
if (thisKeyIsEnableButton) {
|
if (thisKeyIsEnableButton) {
|
||||||
handled = true
|
handled = true; hotkeyIsEnabled = false
|
||||||
hotkeyIsEnabled = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (button in buttonSet) {
|
for (button in buttonSet) {
|
||||||
|
|
@ -110,15 +109,10 @@ class HotkeyUtility(
|
||||||
fun handleHotkey(bindedButton: Int): Boolean {
|
fun handleHotkey(bindedButton: Int): Boolean {
|
||||||
when (bindedButton) {
|
when (bindedButton) {
|
||||||
Hotkey.SWAP_SCREEN.button -> screenAdjustmentUtil.swapScreen()
|
Hotkey.SWAP_SCREEN.button -> screenAdjustmentUtil.swapScreen()
|
||||||
|
|
||||||
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.toggleTurbo(true)
|
Hotkey.TURBO_LIMIT.button -> TurboHelper.toggleTurbo(true)
|
||||||
|
|
||||||
Hotkey.QUICKSAVE.button -> {
|
Hotkey.QUICKSAVE.button -> {
|
||||||
NativeLibrary.saveState(NativeLibrary.QUICKSAVE_SLOT)
|
NativeLibrary.saveState(NativeLibrary.QUICKSAVE_SLOT)
|
||||||
Toast.makeText(
|
Toast.makeText(
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@ object SettingKeys {
|
||||||
external fun enable_required_online_lle_modules(): String
|
external fun enable_required_online_lle_modules(): String
|
||||||
external fun use_virtual_sd(): String
|
external fun use_virtual_sd(): String
|
||||||
external fun compress_cia_installs(): String
|
external fun compress_cia_installs(): String
|
||||||
external fun async_fs_operations(): String
|
|
||||||
external fun region_value(): String
|
external fun region_value(): String
|
||||||
external fun init_clock(): String
|
external fun init_clock(): String
|
||||||
external fun init_time(): String
|
external fun init_time(): String
|
||||||
|
|
@ -46,7 +45,6 @@ object SettingKeys {
|
||||||
external fun texture_filter(): String
|
external fun texture_filter(): String
|
||||||
external fun texture_sampling(): String
|
external fun texture_sampling(): String
|
||||||
external fun delay_game_render_thread_us(): String
|
external fun delay_game_render_thread_us(): String
|
||||||
external fun simulate_3ds_gpu_timings(): String
|
|
||||||
external fun layout_option(): String
|
external fun layout_option(): String
|
||||||
external fun swap_screen(): String
|
external fun swap_screen(): String
|
||||||
external fun upright_screen(): String
|
external fun upright_screen(): String
|
||||||
|
|
@ -94,7 +92,6 @@ object SettingKeys {
|
||||||
external fun audio_emulation(): String
|
external fun audio_emulation(): String
|
||||||
external fun enable_audio_stretching(): String
|
external fun enable_audio_stretching(): String
|
||||||
external fun enable_realtime_audio(): String
|
external fun enable_realtime_audio(): String
|
||||||
external fun simulate_headphones_plugged(): String
|
|
||||||
external fun volume(): String
|
external fun volume(): String
|
||||||
external fun output_type(): String
|
external fun output_type(): String
|
||||||
external fun output_device(): String
|
external fun output_device(): String
|
||||||
|
|
@ -141,5 +138,4 @@ object SettingKeys {
|
||||||
external fun android_hide_images(): String
|
external fun android_hide_images(): String
|
||||||
external fun screen_orientation(): String
|
external fun screen_orientation(): String
|
||||||
external fun performance_overlay_position(): String
|
external fun performance_overlay_position(): String
|
||||||
external fun enable_secondary_display(): String
|
|
||||||
}
|
}
|
||||||
|
|
@ -20,63 +20,19 @@ enum class BooleanSetting(
|
||||||
SWAP_SCREEN(SettingKeys.swap_screen(), Settings.SECTION_LAYOUT, false),
|
SWAP_SCREEN(SettingKeys.swap_screen(), Settings.SECTION_LAYOUT, false),
|
||||||
INSTANT_DEBUG_LOG(SettingKeys.instant_debug_log(), Settings.SECTION_DEBUG, false),
|
INSTANT_DEBUG_LOG(SettingKeys.instant_debug_log(), Settings.SECTION_DEBUG, false),
|
||||||
ENABLE_RPC_SERVER(SettingKeys.enable_rpc_server(), Settings.SECTION_DEBUG, false),
|
ENABLE_RPC_SERVER(SettingKeys.enable_rpc_server(), Settings.SECTION_DEBUG, false),
|
||||||
TOGGLE_UNIQUE_DATA_CONSOLE_TYPE(
|
TOGGLE_UNIQUE_DATA_CONSOLE_TYPE(SettingKeys.toggle_unique_data_console_type(), Settings.SECTION_DEBUG, false),
|
||||||
SettingKeys.toggle_unique_data_console_type(),
|
SWAP_EYES_3D(SettingKeys.swap_eyes_3d(),Settings.SECTION_RENDERER, false),
|
||||||
Settings.SECTION_DEBUG,
|
|
||||||
false
|
|
||||||
),
|
|
||||||
SWAP_EYES_3D(SettingKeys.swap_eyes_3d(), Settings.SECTION_RENDERER, false),
|
|
||||||
PERF_OVERLAY_ENABLE(SettingKeys.performance_overlay_enable(), Settings.SECTION_LAYOUT, false),
|
PERF_OVERLAY_ENABLE(SettingKeys.performance_overlay_enable(), Settings.SECTION_LAYOUT, false),
|
||||||
PERF_OVERLAY_SHOW_FPS(
|
PERF_OVERLAY_SHOW_FPS(SettingKeys.performance_overlay_show_fps(), Settings.SECTION_LAYOUT, true),
|
||||||
SettingKeys.performance_overlay_show_fps(),
|
PERF_OVERLAY_SHOW_FRAMETIME(SettingKeys.performance_overlay_show_frame_time(), Settings.SECTION_LAYOUT, false),
|
||||||
Settings.SECTION_LAYOUT,
|
PERF_OVERLAY_SHOW_SPEED(SettingKeys.performance_overlay_show_speed(), Settings.SECTION_LAYOUT, false),
|
||||||
true
|
PERF_OVERLAY_SHOW_APP_RAM_USAGE(SettingKeys.performance_overlay_show_app_ram_usage(), Settings.SECTION_LAYOUT, false),
|
||||||
),
|
PERF_OVERLAY_SHOW_AVAILABLE_RAM(SettingKeys.performance_overlay_show_available_ram(), Settings.SECTION_LAYOUT, false),
|
||||||
PERF_OVERLAY_SHOW_FRAMETIME(
|
PERF_OVERLAY_SHOW_BATTERY_TEMP(SettingKeys.performance_overlay_show_battery_temp(), Settings.SECTION_LAYOUT, false),
|
||||||
SettingKeys.performance_overlay_show_frame_time(),
|
PERF_OVERLAY_BACKGROUND(SettingKeys.performance_overlay_background(), Settings.SECTION_LAYOUT, false),
|
||||||
Settings.SECTION_LAYOUT,
|
DELAY_START_LLE_MODULES(SettingKeys.delay_start_for_lle_modules(), Settings.SECTION_DEBUG, true),
|
||||||
false
|
DETERMINISTIC_ASYNC_OPERATIONS(SettingKeys.deterministic_async_operations(), Settings.SECTION_DEBUG, false),
|
||||||
),
|
REQUIRED_ONLINE_LLE_MODULES(SettingKeys.enable_required_online_lle_modules(), Settings.SECTION_SYSTEM, false),
|
||||||
PERF_OVERLAY_SHOW_SPEED(
|
|
||||||
SettingKeys.performance_overlay_show_speed(),
|
|
||||||
Settings.SECTION_LAYOUT,
|
|
||||||
false
|
|
||||||
),
|
|
||||||
PERF_OVERLAY_SHOW_APP_RAM_USAGE(
|
|
||||||
SettingKeys.performance_overlay_show_app_ram_usage(),
|
|
||||||
Settings.SECTION_LAYOUT,
|
|
||||||
false
|
|
||||||
),
|
|
||||||
PERF_OVERLAY_SHOW_AVAILABLE_RAM(
|
|
||||||
SettingKeys.performance_overlay_show_available_ram(),
|
|
||||||
Settings.SECTION_LAYOUT,
|
|
||||||
false
|
|
||||||
),
|
|
||||||
PERF_OVERLAY_SHOW_BATTERY_TEMP(
|
|
||||||
SettingKeys.performance_overlay_show_battery_temp(),
|
|
||||||
Settings.SECTION_LAYOUT,
|
|
||||||
false
|
|
||||||
),
|
|
||||||
PERF_OVERLAY_BACKGROUND(
|
|
||||||
SettingKeys.performance_overlay_background(),
|
|
||||||
Settings.SECTION_LAYOUT,
|
|
||||||
false
|
|
||||||
),
|
|
||||||
DELAY_START_LLE_MODULES(
|
|
||||||
SettingKeys.delay_start_for_lle_modules(),
|
|
||||||
Settings.SECTION_DEBUG,
|
|
||||||
true
|
|
||||||
),
|
|
||||||
DETERMINISTIC_ASYNC_OPERATIONS(
|
|
||||||
SettingKeys.deterministic_async_operations(),
|
|
||||||
Settings.SECTION_DEBUG,
|
|
||||||
false
|
|
||||||
),
|
|
||||||
REQUIRED_ONLINE_LLE_MODULES(
|
|
||||||
SettingKeys.enable_required_online_lle_modules(),
|
|
||||||
Settings.SECTION_SYSTEM,
|
|
||||||
false
|
|
||||||
),
|
|
||||||
LLE_APPLETS(SettingKeys.lle_applets(), Settings.SECTION_SYSTEM, false),
|
LLE_APPLETS(SettingKeys.lle_applets(), Settings.SECTION_SYSTEM, false),
|
||||||
NEW_3DS(SettingKeys.is_new_3ds(), Settings.SECTION_SYSTEM, true),
|
NEW_3DS(SettingKeys.is_new_3ds(), Settings.SECTION_SYSTEM, true),
|
||||||
LINEAR_FILTERING(SettingKeys.filter_mode(), Settings.SECTION_RENDERER, true),
|
LINEAR_FILTERING(SettingKeys.filter_mode(), Settings.SECTION_RENDERER, true),
|
||||||
|
|
@ -88,43 +44,19 @@ enum class BooleanSetting(
|
||||||
PRELOAD_TEXTURES(SettingKeys.preload_textures(), Settings.SECTION_UTILITY, false),
|
PRELOAD_TEXTURES(SettingKeys.preload_textures(), Settings.SECTION_UTILITY, false),
|
||||||
ENABLE_AUDIO_STRETCHING(SettingKeys.enable_audio_stretching(), Settings.SECTION_AUDIO, true),
|
ENABLE_AUDIO_STRETCHING(SettingKeys.enable_audio_stretching(), Settings.SECTION_AUDIO, true),
|
||||||
ENABLE_REALTIME_AUDIO(SettingKeys.enable_realtime_audio(), Settings.SECTION_AUDIO, false),
|
ENABLE_REALTIME_AUDIO(SettingKeys.enable_realtime_audio(), Settings.SECTION_AUDIO, false),
|
||||||
SIMULATE_HEADPHONES_PLUGGED(
|
|
||||||
SettingKeys.simulate_headphones_plugged(),
|
|
||||||
Settings.SECTION_AUDIO,
|
|
||||||
false
|
|
||||||
),
|
|
||||||
CPU_JIT(SettingKeys.use_cpu_jit(), Settings.SECTION_CORE, true),
|
CPU_JIT(SettingKeys.use_cpu_jit(), Settings.SECTION_CORE, true),
|
||||||
HW_SHADER(SettingKeys.use_hw_shader(), Settings.SECTION_RENDERER, true),
|
HW_SHADER(SettingKeys.use_hw_shader(), Settings.SECTION_RENDERER, true),
|
||||||
SHADER_JIT(SettingKeys.use_shader_jit(), Settings.SECTION_RENDERER, true),
|
SHADER_JIT(SettingKeys.use_shader_jit(), Settings.SECTION_RENDERER, true),
|
||||||
VSYNC(SettingKeys.use_vsync(), Settings.SECTION_RENDERER, false),
|
VSYNC(SettingKeys.use_vsync(), Settings.SECTION_RENDERER, false),
|
||||||
USE_FRAME_LIMIT(SettingKeys.use_frame_limit(), Settings.SECTION_RENDERER, true),
|
USE_FRAME_LIMIT(SettingKeys.use_frame_limit(), Settings.SECTION_RENDERER, true),
|
||||||
DEBUG_RENDERER(SettingKeys.renderer_debug(), Settings.SECTION_DEBUG, false),
|
DEBUG_RENDERER(SettingKeys.renderer_debug(), Settings.SECTION_DEBUG, false),
|
||||||
DISABLE_RIGHT_EYE_RENDER(
|
DISABLE_RIGHT_EYE_RENDER(SettingKeys.disable_right_eye_render(), Settings.SECTION_RENDERER, false),
|
||||||
SettingKeys.disable_right_eye_render(),
|
USE_ARTIC_BASE_CONTROLLER(SettingKeys.use_artic_base_controller(), Settings.SECTION_CONTROLS, false),
|
||||||
Settings.SECTION_RENDERER,
|
|
||||||
false
|
|
||||||
),
|
|
||||||
USE_ARTIC_BASE_CONTROLLER(
|
|
||||||
SettingKeys.use_artic_base_controller(),
|
|
||||||
Settings.SECTION_CONTROLS,
|
|
||||||
false
|
|
||||||
),
|
|
||||||
UPRIGHT_SCREEN(SettingKeys.upright_screen(), Settings.SECTION_LAYOUT, false),
|
UPRIGHT_SCREEN(SettingKeys.upright_screen(), Settings.SECTION_LAYOUT, false),
|
||||||
COMPRESS_INSTALLED_CIA_CONTENT(
|
COMPRESS_INSTALLED_CIA_CONTENT(SettingKeys.compress_cia_installs(), Settings.SECTION_STORAGE, false),
|
||||||
SettingKeys.compress_cia_installs(),
|
|
||||||
Settings.SECTION_STORAGE,
|
|
||||||
false
|
|
||||||
),
|
|
||||||
ASYNC_FS_OPERATIONS(SettingKeys.async_fs_operations(), Settings.SECTION_STORAGE, true),
|
|
||||||
ANDROID_HIDE_IMAGES(SettingKeys.android_hide_images(), Settings.SECTION_MISC, false),
|
ANDROID_HIDE_IMAGES(SettingKeys.android_hide_images(), Settings.SECTION_MISC, false),
|
||||||
APPLY_REGION_FREE_PATCH(SettingKeys.apply_region_free_patch(), Settings.SECTION_SYSTEM, true),
|
APPLY_REGION_FREE_PATCH(SettingKeys.apply_region_free_patch(), Settings.SECTION_SYSTEM, true),
|
||||||
USE_INTEGER_SCALING(SettingKeys.use_integer_scaling(), Settings.SECTION_RENDERER, false),
|
USE_INTEGER_SCALING(SettingKeys.use_integer_scaling(), Settings.SECTION_RENDERER, false);
|
||||||
ENABLE_SECONDARY_DISPLAY(SettingKeys.enable_secondary_display(), Settings.SECTION_LAYOUT, true),
|
|
||||||
SIMULATE_3DS_GPU_TIMINGS(
|
|
||||||
SettingKeys.simulate_3ds_gpu_timings(),
|
|
||||||
Settings.SECTION_RENDERER,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
|
|
||||||
override var boolean: Boolean = defaultValue
|
override var boolean: Boolean = defaultValue
|
||||||
|
|
||||||
|
|
@ -159,7 +91,6 @@ enum class BooleanSetting(
|
||||||
SHADERS_ACCURATE_MUL,
|
SHADERS_ACCURATE_MUL,
|
||||||
USE_ARTIC_BASE_CONTROLLER,
|
USE_ARTIC_BASE_CONTROLLER,
|
||||||
COMPRESS_INSTALLED_CIA_CONTENT,
|
COMPRESS_INSTALLED_CIA_CONTENT,
|
||||||
ASYNC_FS_OPERATIONS,
|
|
||||||
ANDROID_HIDE_IMAGES,
|
ANDROID_HIDE_IMAGES,
|
||||||
PERF_OVERLAY_ENABLE, // Works in overlay options, but not from the settings menu
|
PERF_OVERLAY_ENABLE, // Works in overlay options, but not from the settings menu
|
||||||
APPLY_REGION_FREE_PATCH
|
APPLY_REGION_FREE_PATCH
|
||||||
|
|
|
||||||
|
|
@ -11,12 +11,8 @@ enum class FloatSetting(
|
||||||
override val section: String,
|
override val section: String,
|
||||||
override val defaultValue: Float
|
override val defaultValue: Float
|
||||||
) : AbstractFloatSetting {
|
) : AbstractFloatSetting {
|
||||||
LARGE_SCREEN_PROPORTION(SettingKeys.large_screen_proportion(), Settings.SECTION_LAYOUT, 2.25f),
|
LARGE_SCREEN_PROPORTION(SettingKeys.large_screen_proportion(),Settings.SECTION_LAYOUT,2.25f),
|
||||||
SECOND_SCREEN_OPACITY(
|
SECOND_SCREEN_OPACITY(SettingKeys.custom_second_layer_opacity(), Settings.SECTION_RENDERER, 100f),
|
||||||
SettingKeys.custom_second_layer_opacity(),
|
|
||||||
Settings.SECTION_RENDERER,
|
|
||||||
100f
|
|
||||||
),
|
|
||||||
BACKGROUND_RED(SettingKeys.bg_red(), Settings.SECTION_RENDERER, 0f),
|
BACKGROUND_RED(SettingKeys.bg_red(), Settings.SECTION_RENDERER, 0f),
|
||||||
BACKGROUND_BLUE(SettingKeys.bg_blue(), Settings.SECTION_RENDERER, 0f),
|
BACKGROUND_BLUE(SettingKeys.bg_blue(), Settings.SECTION_RENDERER, 0f),
|
||||||
BACKGROUND_GREEN(SettingKeys.bg_green(), Settings.SECTION_RENDERER, 0f);
|
BACKGROUND_GREEN(SettingKeys.bg_green(), Settings.SECTION_RENDERER, 0f);
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,6 @@
|
||||||
|
|
||||||
package org.citra.citra_emu.features.settings.model
|
package org.citra.citra_emu.features.settings.model
|
||||||
|
|
||||||
import org.citra.citra_emu.features.settings.SettingKeys
|
|
||||||
|
|
||||||
enum class IntListSetting(
|
enum class IntListSetting(
|
||||||
override val key: String,
|
override val key: String,
|
||||||
override val section: String,
|
override val section: String,
|
||||||
|
|
@ -13,15 +11,10 @@ enum class IntListSetting(
|
||||||
val canBeEmpty: Boolean = true
|
val canBeEmpty: Boolean = true
|
||||||
) : AbstractListSetting<Int> {
|
) : AbstractListSetting<Int> {
|
||||||
|
|
||||||
LAYOUTS_TO_CYCLE(
|
LAYOUTS_TO_CYCLE("layouts_to_cycle", Settings.SECTION_LAYOUT, listOf(0, 1, 2, 3, 4, 5), canBeEmpty = false);
|
||||||
SettingKeys.layouts_to_cycle(),
|
|
||||||
Settings.SECTION_LAYOUT,
|
|
||||||
listOf(0, 1, 2, 3, 4, 5),
|
|
||||||
canBeEmpty = false
|
|
||||||
);
|
|
||||||
|
|
||||||
private var backingList: List<Int> = defaultValue
|
private var backingList: List<Int> = defaultValue
|
||||||
private var lastValidList: List<Int> = defaultValue
|
private var lastValidList : List<Int> = defaultValue
|
||||||
|
|
||||||
override var list: List<Int>
|
override var list: List<Int>
|
||||||
get() = backingList
|
get() = backingList
|
||||||
|
|
@ -37,6 +30,7 @@ enum class IntListSetting(
|
||||||
override val valueAsString: String
|
override val valueAsString: String
|
||||||
get() = list.joinToString()
|
get() = list.joinToString()
|
||||||
|
|
||||||
|
|
||||||
override val isRuntimeEditable: Boolean
|
override val isRuntimeEditable: Boolean
|
||||||
get() {
|
get() {
|
||||||
for (setting in NOT_RUNTIME_EDITABLE) {
|
for (setting in NOT_RUNTIME_EDITABLE) {
|
||||||
|
|
@ -50,7 +44,8 @@ enum class IntListSetting(
|
||||||
companion object {
|
companion object {
|
||||||
private val NOT_RUNTIME_EDITABLE: List<IntListSetting> = emptyList()
|
private val NOT_RUNTIME_EDITABLE: List<IntListSetting> = emptyList()
|
||||||
|
|
||||||
fun from(key: String): IntListSetting? = values().firstOrNull { it.key == key }
|
fun from(key: String): IntListSetting? =
|
||||||
|
values().firstOrNull { it.key == key }
|
||||||
|
|
||||||
fun clear() = values().forEach { it.list = it.defaultValue }
|
fun clear() = values().forEach { it.list = it.defaultValue }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ enum class IntSetting(
|
||||||
CAMERA_INNER_FLIP(SettingKeys.camera_inner_flip(), Settings.SECTION_CAMERA, 0),
|
CAMERA_INNER_FLIP(SettingKeys.camera_inner_flip(), Settings.SECTION_CAMERA, 0),
|
||||||
CAMERA_OUTER_LEFT_FLIP(SettingKeys.camera_outer_left_flip(), Settings.SECTION_CAMERA, 0),
|
CAMERA_OUTER_LEFT_FLIP(SettingKeys.camera_outer_left_flip(), Settings.SECTION_CAMERA, 0),
|
||||||
CAMERA_OUTER_RIGHT_FLIP(SettingKeys.camera_outer_right_flip(), Settings.SECTION_CAMERA, 0),
|
CAMERA_OUTER_RIGHT_FLIP(SettingKeys.camera_outer_right_flip(), Settings.SECTION_CAMERA, 0),
|
||||||
GRAPHICS_API(SettingKeys.graphics_api(), Settings.SECTION_RENDERER, 2),
|
GRAPHICS_API(SettingKeys.graphics_api(), Settings.SECTION_RENDERER, 1),
|
||||||
RESOLUTION_FACTOR(SettingKeys.resolution_factor(), Settings.SECTION_RENDERER, 1),
|
RESOLUTION_FACTOR(SettingKeys.resolution_factor(), Settings.SECTION_RENDERER, 1),
|
||||||
STEREOSCOPIC_3D_MODE(SettingKeys.render_3d(), Settings.SECTION_RENDERER, 2),
|
STEREOSCOPIC_3D_MODE(SettingKeys.render_3d(), Settings.SECTION_RENDERER, 2),
|
||||||
STEREOSCOPIC_3D_DEPTH(SettingKeys.factor_3d(), Settings.SECTION_RENDERER, 0),
|
STEREOSCOPIC_3D_DEPTH(SettingKeys.factor_3d(), Settings.SECTION_RENDERER, 0),
|
||||||
|
|
@ -26,30 +26,26 @@ enum class IntSetting(
|
||||||
CARDBOARD_X_SHIFT(SettingKeys.cardboard_x_shift(), Settings.SECTION_LAYOUT, 0),
|
CARDBOARD_X_SHIFT(SettingKeys.cardboard_x_shift(), Settings.SECTION_LAYOUT, 0),
|
||||||
CARDBOARD_Y_SHIFT(SettingKeys.cardboard_y_shift(), Settings.SECTION_LAYOUT, 0),
|
CARDBOARD_Y_SHIFT(SettingKeys.cardboard_y_shift(), Settings.SECTION_LAYOUT, 0),
|
||||||
SCREEN_LAYOUT(SettingKeys.layout_option(), Settings.SECTION_LAYOUT, 0),
|
SCREEN_LAYOUT(SettingKeys.layout_option(), Settings.SECTION_LAYOUT, 0),
|
||||||
SMALL_SCREEN_POSITION(SettingKeys.small_screen_position(), Settings.SECTION_LAYOUT, 0),
|
SMALL_SCREEN_POSITION(SettingKeys.small_screen_position(),Settings.SECTION_LAYOUT,0),
|
||||||
LANDSCAPE_TOP_X(SettingKeys.custom_top_x(), Settings.SECTION_LAYOUT, 0),
|
LANDSCAPE_TOP_X(SettingKeys.custom_top_x(),Settings.SECTION_LAYOUT,0),
|
||||||
LANDSCAPE_TOP_Y(SettingKeys.custom_top_y(), Settings.SECTION_LAYOUT, 0),
|
LANDSCAPE_TOP_Y(SettingKeys.custom_top_y(),Settings.SECTION_LAYOUT,0),
|
||||||
LANDSCAPE_TOP_WIDTH(SettingKeys.custom_top_width(), Settings.SECTION_LAYOUT, 800),
|
LANDSCAPE_TOP_WIDTH(SettingKeys.custom_top_width(),Settings.SECTION_LAYOUT,800),
|
||||||
LANDSCAPE_TOP_HEIGHT(SettingKeys.custom_top_height(), Settings.SECTION_LAYOUT, 480),
|
LANDSCAPE_TOP_HEIGHT(SettingKeys.custom_top_height(),Settings.SECTION_LAYOUT,480),
|
||||||
LANDSCAPE_BOTTOM_X(SettingKeys.custom_bottom_x(), Settings.SECTION_LAYOUT, 80),
|
LANDSCAPE_BOTTOM_X(SettingKeys.custom_bottom_x(),Settings.SECTION_LAYOUT,80),
|
||||||
LANDSCAPE_BOTTOM_Y(SettingKeys.custom_bottom_y(), Settings.SECTION_LAYOUT, 480),
|
LANDSCAPE_BOTTOM_Y(SettingKeys.custom_bottom_y(),Settings.SECTION_LAYOUT,480),
|
||||||
LANDSCAPE_BOTTOM_WIDTH(SettingKeys.custom_bottom_width(), Settings.SECTION_LAYOUT, 640),
|
LANDSCAPE_BOTTOM_WIDTH(SettingKeys.custom_bottom_width(),Settings.SECTION_LAYOUT,640),
|
||||||
LANDSCAPE_BOTTOM_HEIGHT(SettingKeys.custom_bottom_height(), Settings.SECTION_LAYOUT, 480),
|
LANDSCAPE_BOTTOM_HEIGHT(SettingKeys.custom_bottom_height(),Settings.SECTION_LAYOUT,480),
|
||||||
SCREEN_GAP(SettingKeys.screen_gap(), Settings.SECTION_LAYOUT, 0),
|
SCREEN_GAP(SettingKeys.screen_gap(),Settings.SECTION_LAYOUT,0),
|
||||||
PORTRAIT_SCREEN_LAYOUT(SettingKeys.portrait_layout_option(), Settings.SECTION_LAYOUT, 0),
|
PORTRAIT_SCREEN_LAYOUT(SettingKeys.portrait_layout_option(),Settings.SECTION_LAYOUT,0),
|
||||||
SECONDARY_DISPLAY_LAYOUT(SettingKeys.secondary_display_layout(), Settings.SECTION_LAYOUT, 4),
|
SECONDARY_DISPLAY_LAYOUT(SettingKeys.secondary_display_layout(),Settings.SECTION_LAYOUT,0),
|
||||||
PORTRAIT_TOP_X(SettingKeys.custom_portrait_top_x(), Settings.SECTION_LAYOUT, 0),
|
PORTRAIT_TOP_X(SettingKeys.custom_portrait_top_x(),Settings.SECTION_LAYOUT,0),
|
||||||
PORTRAIT_TOP_Y(SettingKeys.custom_portrait_top_y(), Settings.SECTION_LAYOUT, 0),
|
PORTRAIT_TOP_Y(SettingKeys.custom_portrait_top_y(),Settings.SECTION_LAYOUT,0),
|
||||||
PORTRAIT_TOP_WIDTH(SettingKeys.custom_portrait_top_width(), Settings.SECTION_LAYOUT, 800),
|
PORTRAIT_TOP_WIDTH(SettingKeys.custom_portrait_top_width(),Settings.SECTION_LAYOUT,800),
|
||||||
PORTRAIT_TOP_HEIGHT(SettingKeys.custom_portrait_top_height(), Settings.SECTION_LAYOUT, 480),
|
PORTRAIT_TOP_HEIGHT(SettingKeys.custom_portrait_top_height(),Settings.SECTION_LAYOUT,480),
|
||||||
PORTRAIT_BOTTOM_X(SettingKeys.custom_portrait_bottom_x(), Settings.SECTION_LAYOUT, 80),
|
PORTRAIT_BOTTOM_X(SettingKeys.custom_portrait_bottom_x(),Settings.SECTION_LAYOUT,80),
|
||||||
PORTRAIT_BOTTOM_Y(SettingKeys.custom_portrait_bottom_y(), Settings.SECTION_LAYOUT, 480),
|
PORTRAIT_BOTTOM_Y(SettingKeys.custom_portrait_bottom_y(),Settings.SECTION_LAYOUT,480),
|
||||||
PORTRAIT_BOTTOM_WIDTH(SettingKeys.custom_portrait_bottom_width(), Settings.SECTION_LAYOUT, 640),
|
PORTRAIT_BOTTOM_WIDTH(SettingKeys.custom_portrait_bottom_width(),Settings.SECTION_LAYOUT,640),
|
||||||
PORTRAIT_BOTTOM_HEIGHT(
|
PORTRAIT_BOTTOM_HEIGHT(SettingKeys.custom_portrait_bottom_height(),Settings.SECTION_LAYOUT,480),
|
||||||
SettingKeys.custom_portrait_bottom_height(),
|
|
||||||
Settings.SECTION_LAYOUT,
|
|
||||||
480
|
|
||||||
),
|
|
||||||
AUDIO_INPUT_TYPE(SettingKeys.input_type(), Settings.SECTION_AUDIO, 0),
|
AUDIO_INPUT_TYPE(SettingKeys.input_type(), Settings.SECTION_AUDIO, 0),
|
||||||
CPU_CLOCK_SPEED(SettingKeys.cpu_clock_percentage(), Settings.SECTION_CORE, 100),
|
CPU_CLOCK_SPEED(SettingKeys.cpu_clock_percentage(), Settings.SECTION_CORE, 100),
|
||||||
TEXTURE_FILTER(SettingKeys.texture_filter(), Settings.SECTION_RENDERER, 0),
|
TEXTURE_FILTER(SettingKeys.texture_filter(), Settings.SECTION_RENDERER, 0),
|
||||||
|
|
@ -58,12 +54,8 @@ enum class IntSetting(
|
||||||
DELAY_RENDER_THREAD_US(SettingKeys.delay_game_render_thread_us(), Settings.SECTION_RENDERER, 0),
|
DELAY_RENDER_THREAD_US(SettingKeys.delay_game_render_thread_us(), Settings.SECTION_RENDERER, 0),
|
||||||
ORIENTATION_OPTION(SettingKeys.screen_orientation(), Settings.SECTION_LAYOUT, 2),
|
ORIENTATION_OPTION(SettingKeys.screen_orientation(), Settings.SECTION_LAYOUT, 2),
|
||||||
TURBO_LIMIT(SettingKeys.turbo_limit(), Settings.SECTION_CORE, 200),
|
TURBO_LIMIT(SettingKeys.turbo_limit(), Settings.SECTION_CORE, 200),
|
||||||
PERFORMANCE_OVERLAY_POSITION(
|
PERFORMANCE_OVERLAY_POSITION(SettingKeys.performance_overlay_position(), Settings.SECTION_LAYOUT, 0),
|
||||||
SettingKeys.performance_overlay_position(),
|
RENDER_3D_WHICH_DISPLAY(SettingKeys.render_3d_which_display(),Settings.SECTION_RENDERER,0),
|
||||||
Settings.SECTION_LAYOUT,
|
|
||||||
0
|
|
||||||
),
|
|
||||||
RENDER_3D_WHICH_DISPLAY(SettingKeys.render_3d_which_display(), Settings.SECTION_RENDERER, 0),
|
|
||||||
ASPECT_RATIO(SettingKeys.aspect_ratio(), Settings.SECTION_LAYOUT, 0);
|
ASPECT_RATIO(SettingKeys.aspect_ratio(), Settings.SECTION_LAYOUT, 0);
|
||||||
|
|
||||||
override var int: Int = defaultValue
|
override var int: Int = defaultValue
|
||||||
|
|
@ -86,7 +78,7 @@ enum class IntSetting(
|
||||||
EMULATED_REGION,
|
EMULATED_REGION,
|
||||||
INIT_CLOCK,
|
INIT_CLOCK,
|
||||||
GRAPHICS_API,
|
GRAPHICS_API,
|
||||||
AUDIO_INPUT_TYPE
|
AUDIO_INPUT_TYPE,
|
||||||
)
|
)
|
||||||
|
|
||||||
fun from(key: String): IntSetting? = IntSetting.values().firstOrNull { it.key == key }
|
fun from(key: String): IntSetting? = IntSetting.values().firstOrNull { it.key == key }
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
||||||
|
|
@ -26,7 +26,9 @@ class SettingSection(val name: String) {
|
||||||
* @param key Used to retrieve the Setting.
|
* @param key Used to retrieve the Setting.
|
||||||
* @return A Setting object (you should probably cast this before using)
|
* @return A Setting object (you should probably cast this before using)
|
||||||
*/
|
*/
|
||||||
fun getSetting(key: String): AbstractSetting? = settings[key]
|
fun getSetting(key: String): AbstractSetting? {
|
||||||
|
return settings[key]
|
||||||
|
}
|
||||||
|
|
||||||
fun mergeSection(settingSection: SettingSection) {
|
fun mergeSection(settingSection: SettingSection) {
|
||||||
for (setting in settingSection.settings.values) {
|
for (setting in settingSection.settings.values) {
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,11 @@
|
||||||
package org.citra.citra_emu.features.settings.model
|
package org.citra.citra_emu.features.settings.model
|
||||||
|
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
import java.util.TreeMap
|
|
||||||
import org.citra.citra_emu.CitraApplication
|
import org.citra.citra_emu.CitraApplication
|
||||||
import org.citra.citra_emu.R
|
import org.citra.citra_emu.R
|
||||||
import org.citra.citra_emu.features.settings.ui.SettingsActivityView
|
import org.citra.citra_emu.features.settings.ui.SettingsActivityView
|
||||||
import org.citra.citra_emu.features.settings.utils.SettingsFile
|
import org.citra.citra_emu.features.settings.utils.SettingsFile
|
||||||
|
import java.util.TreeMap
|
||||||
|
|
||||||
class Settings {
|
class Settings {
|
||||||
private var gameId: String? = null
|
private var gameId: String? = null
|
||||||
|
|
@ -33,7 +33,9 @@ class Settings {
|
||||||
|
|
||||||
var sections: HashMap<String, SettingSection?> = SettingsSectionMap()
|
var sections: HashMap<String, SettingSection?> = SettingsSectionMap()
|
||||||
|
|
||||||
fun getSection(sectionName: String): SettingSection? = sections[sectionName]
|
fun getSection(sectionName: String): SettingSection? {
|
||||||
|
return sections[sectionName]
|
||||||
|
}
|
||||||
|
|
||||||
val isEmpty: Boolean
|
val isEmpty: Boolean
|
||||||
get() = sections.isEmpty()
|
get() = sections.isEmpty()
|
||||||
|
|
@ -140,7 +142,7 @@ class Settings {
|
||||||
const val HOTKEY_CLOSE_GAME = "hotkey_close_game"
|
const val HOTKEY_CLOSE_GAME = "hotkey_close_game"
|
||||||
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"
|
const val HOTKEY_TURBO_LIMIT = "hotkey_turbo_limit"
|
||||||
|
|
||||||
val buttonKeys = listOf(
|
val buttonKeys = listOf(
|
||||||
|
|
@ -208,7 +210,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
|
HOTKEY_TURBO_LIMIT
|
||||||
)
|
)
|
||||||
val hotkeyTitles = listOf(
|
val hotkeyTitles = listOf(
|
||||||
|
|
|
||||||
|
|
@ -15,17 +15,9 @@ enum class StringSetting(
|
||||||
CAMERA_INNER_NAME(SettingKeys.camera_inner_name(), Settings.SECTION_CAMERA, "ndk"),
|
CAMERA_INNER_NAME(SettingKeys.camera_inner_name(), Settings.SECTION_CAMERA, "ndk"),
|
||||||
CAMERA_INNER_CONFIG(SettingKeys.camera_inner_config(), Settings.SECTION_CAMERA, "_front"),
|
CAMERA_INNER_CONFIG(SettingKeys.camera_inner_config(), Settings.SECTION_CAMERA, "_front"),
|
||||||
CAMERA_OUTER_LEFT_NAME(SettingKeys.camera_outer_left_name(), Settings.SECTION_CAMERA, "ndk"),
|
CAMERA_OUTER_LEFT_NAME(SettingKeys.camera_outer_left_name(), Settings.SECTION_CAMERA, "ndk"),
|
||||||
CAMERA_OUTER_LEFT_CONFIG(
|
CAMERA_OUTER_LEFT_CONFIG(SettingKeys.camera_outer_left_config(), Settings.SECTION_CAMERA, "_back"),
|
||||||
SettingKeys.camera_outer_left_config(),
|
|
||||||
Settings.SECTION_CAMERA,
|
|
||||||
"_back"
|
|
||||||
),
|
|
||||||
CAMERA_OUTER_RIGHT_NAME(SettingKeys.camera_outer_right_name(), Settings.SECTION_CAMERA, "ndk"),
|
CAMERA_OUTER_RIGHT_NAME(SettingKeys.camera_outer_right_name(), Settings.SECTION_CAMERA, "ndk"),
|
||||||
CAMERA_OUTER_RIGHT_CONFIG(
|
CAMERA_OUTER_RIGHT_CONFIG(SettingKeys.camera_outer_right_config(), Settings.SECTION_CAMERA, "_back");
|
||||||
SettingKeys.camera_outer_right_config(),
|
|
||||||
Settings.SECTION_CAMERA,
|
|
||||||
"_back"
|
|
||||||
);
|
|
||||||
|
|
||||||
override var string: String = defaultValue
|
override var string: String = defaultValue
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,9 @@
|
||||||
// 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.
|
||||||
|
|
||||||
package org.citra.citra_emu.features.settings.model.view
|
package org.citra.citra_emu.features.settings.model.view
|
||||||
|
|
||||||
import androidx.annotation.StringRes
|
|
||||||
import org.citra.citra_emu.R
|
|
||||||
import org.citra.citra_emu.features.settings.model.AbstractSetting
|
import org.citra.citra_emu.features.settings.model.AbstractSetting
|
||||||
import org.citra.citra_emu.features.settings.model.AbstractStringSetting
|
import org.citra.citra_emu.features.settings.model.AbstractStringSetting
|
||||||
|
|
||||||
|
|
@ -15,9 +13,7 @@ class DateTimeSetting(
|
||||||
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,
|
override var isEnabled: Boolean = true
|
||||||
@StringRes override var disabledMessage: Int =
|
|
||||||
R.string.setting_disabled_description_incompatible_setting
|
|
||||||
) : SettingsItem(setting, titleId, descriptionId) {
|
) : SettingsItem(setting, titleId, descriptionId) {
|
||||||
override val type = TYPE_DATETIME_SETTING
|
override val type = TYPE_DATETIME_SETTING
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
// 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.
|
||||||
|
|
||||||
package org.citra.citra_emu.features.settings.model.view
|
package org.citra.citra_emu.features.settings.model.view
|
||||||
|
|
||||||
class HeaderSetting(titleId: Int, descId: Int = 0) : SettingsItem(null, titleId, descId) {
|
class HeaderSetting(titleId: Int,descId: Int = 0) : SettingsItem(null, titleId, descId) {
|
||||||
override val type = TYPE_HEADER
|
override val type = TYPE_HEADER
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,8 +20,10 @@ import org.citra.citra_emu.features.settings.model.AbstractSetting
|
||||||
import org.citra.citra_emu.features.settings.model.AbstractStringSetting
|
import org.citra.citra_emu.features.settings.model.AbstractStringSetting
|
||||||
import org.citra.citra_emu.features.settings.model.Settings
|
import org.citra.citra_emu.features.settings.model.Settings
|
||||||
|
|
||||||
class InputBindingSetting(val abstractSetting: AbstractSetting, titleId: Int) :
|
class InputBindingSetting(
|
||||||
SettingsItem(abstractSetting, titleId, 0) {
|
val abstractSetting: AbstractSetting,
|
||||||
|
titleId: Int
|
||||||
|
) : SettingsItem(abstractSetting, titleId, 0) {
|
||||||
private val context: Context get() = CitraApplication.appContext
|
private val context: Context get() = CitraApplication.appContext
|
||||||
private val preferences: SharedPreferences
|
private val preferences: SharedPreferences
|
||||||
get() = PreferenceManager.getDefaultSharedPreferences(context)
|
get() = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
|
@ -37,7 +39,8 @@ class InputBindingSetting(val abstractSetting: AbstractSetting, titleId: Int) :
|
||||||
/**
|
/**
|
||||||
* Returns true if this key is for the 3DS Circle Pad
|
* Returns true if this key is for the 3DS Circle Pad
|
||||||
*/
|
*/
|
||||||
fun isCirclePad(): Boolean = when (abstractSetting.key) {
|
fun isCirclePad(): Boolean =
|
||||||
|
when (abstractSetting.key) {
|
||||||
Settings.KEY_CIRCLEPAD_AXIS_HORIZONTAL,
|
Settings.KEY_CIRCLEPAD_AXIS_HORIZONTAL,
|
||||||
Settings.KEY_CIRCLEPAD_AXIS_VERTICAL -> true
|
Settings.KEY_CIRCLEPAD_AXIS_VERTICAL -> true
|
||||||
|
|
||||||
|
|
@ -47,7 +50,8 @@ class InputBindingSetting(val abstractSetting: AbstractSetting, titleId: Int) :
|
||||||
/**
|
/**
|
||||||
* Returns true if this key is for a horizontal axis for a 3DS analog stick or D-pad
|
* Returns true if this key is for a horizontal axis for a 3DS analog stick or D-pad
|
||||||
*/
|
*/
|
||||||
fun isHorizontalOrientation(): Boolean = when (abstractSetting.key) {
|
fun isHorizontalOrientation(): Boolean =
|
||||||
|
when (abstractSetting.key) {
|
||||||
Settings.KEY_CIRCLEPAD_AXIS_HORIZONTAL,
|
Settings.KEY_CIRCLEPAD_AXIS_HORIZONTAL,
|
||||||
Settings.KEY_CSTICK_AXIS_HORIZONTAL,
|
Settings.KEY_CSTICK_AXIS_HORIZONTAL,
|
||||||
Settings.KEY_DPAD_AXIS_HORIZONTAL -> true
|
Settings.KEY_DPAD_AXIS_HORIZONTAL -> true
|
||||||
|
|
@ -58,7 +62,8 @@ class InputBindingSetting(val abstractSetting: AbstractSetting, titleId: Int) :
|
||||||
/**
|
/**
|
||||||
* Returns true if this key is for the 3DS C-Stick
|
* Returns true if this key is for the 3DS C-Stick
|
||||||
*/
|
*/
|
||||||
fun isCStick(): Boolean = when (abstractSetting.key) {
|
fun isCStick(): Boolean =
|
||||||
|
when (abstractSetting.key) {
|
||||||
Settings.KEY_CSTICK_AXIS_HORIZONTAL,
|
Settings.KEY_CSTICK_AXIS_HORIZONTAL,
|
||||||
Settings.KEY_CSTICK_AXIS_VERTICAL -> true
|
Settings.KEY_CSTICK_AXIS_VERTICAL -> true
|
||||||
|
|
||||||
|
|
@ -68,18 +73,19 @@ class InputBindingSetting(val abstractSetting: AbstractSetting, titleId: Int) :
|
||||||
/**
|
/**
|
||||||
* Returns true if this key is for the 3DS D-Pad
|
* Returns true if this key is for the 3DS D-Pad
|
||||||
*/
|
*/
|
||||||
fun isDPad(): Boolean = when (abstractSetting.key) {
|
fun isDPad(): Boolean =
|
||||||
|
when (abstractSetting.key) {
|
||||||
Settings.KEY_DPAD_AXIS_HORIZONTAL,
|
Settings.KEY_DPAD_AXIS_HORIZONTAL,
|
||||||
Settings.KEY_DPAD_AXIS_VERTICAL -> true
|
Settings.KEY_DPAD_AXIS_VERTICAL -> true
|
||||||
|
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if this key is for the 3DS L/R or ZL/ZR buttons. Note, these are not real
|
* Returns true if this key is for the 3DS L/R or ZL/ZR buttons. Note, these are not real
|
||||||
* triggers on the 3DS, but we support them as such on a physical gamepad.
|
* triggers on the 3DS, but we support them as such on a physical gamepad.
|
||||||
*/
|
*/
|
||||||
fun isTrigger(): Boolean = when (abstractSetting.key) {
|
fun isTrigger(): Boolean =
|
||||||
|
when (abstractSetting.key) {
|
||||||
Settings.KEY_BUTTON_L,
|
Settings.KEY_BUTTON_L,
|
||||||
Settings.KEY_BUTTON_R,
|
Settings.KEY_BUTTON_R,
|
||||||
Settings.KEY_BUTTON_ZL,
|
Settings.KEY_BUTTON_ZL,
|
||||||
|
|
@ -91,12 +97,16 @@ class InputBindingSetting(val abstractSetting: AbstractSetting, titleId: Int) :
|
||||||
/**
|
/**
|
||||||
* Returns true if a gamepad axis can be used to map this key.
|
* Returns true if a gamepad axis can be used to map this key.
|
||||||
*/
|
*/
|
||||||
fun isAxisMappingSupported(): Boolean = isCirclePad() || isCStick() || isDPad() || isTrigger()
|
fun isAxisMappingSupported(): Boolean {
|
||||||
|
return isCirclePad() || isCStick() || isDPad() || isTrigger()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if a gamepad button can be used to map this key.
|
* Returns true if a gamepad button can be used to map this key.
|
||||||
*/
|
*/
|
||||||
fun isButtonMappingSupported(): Boolean = !isAxisMappingSupported() || isTrigger()
|
fun isButtonMappingSupported(): Boolean {
|
||||||
|
return !isAxisMappingSupported() || isTrigger()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the Citra button code for the settings key.
|
* Returns the Citra button code for the settings key.
|
||||||
|
|
@ -125,7 +135,7 @@ class InputBindingSetting(val abstractSetting: AbstractSetting, titleId: Int) :
|
||||||
Settings.HOTKEY_CLOSE_GAME -> Hotkey.CLOSE_GAME.button
|
Settings.HOTKEY_CLOSE_GAME -> Hotkey.CLOSE_GAME.button
|
||||||
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
|
Settings.HOTKEY_TURBO_LIMIT -> Hotkey.TURBO_LIMIT.button
|
||||||
else -> -1
|
else -> -1
|
||||||
}
|
}
|
||||||
|
|
@ -167,10 +177,10 @@ class InputBindingSetting(val abstractSetting: AbstractSetting, titleId: Int) :
|
||||||
} catch (e: ClassCastException) {
|
} catch (e: ClassCastException) {
|
||||||
// if this is an int pref, either old button or an axis, so just remove it
|
// if this is an int pref, either old button or an axis, so just remove it
|
||||||
preferences.edit().remove(oldKey).apply()
|
preferences.edit().remove(oldKey).apply()
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
buttonCodes.remove(buttonCode.toString())
|
buttonCodes.remove(buttonCode.toString());
|
||||||
preferences.edit().putStringSet(oldKey, buttonCodes).apply()
|
preferences.edit().putStringSet(oldKey,buttonCodes).apply()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -187,7 +197,7 @@ class InputBindingSetting(val abstractSetting: AbstractSetting, titleId: Int) :
|
||||||
// Cleanup old mapping for this setting
|
// Cleanup old mapping for this setting
|
||||||
removeOldMapping()
|
removeOldMapping()
|
||||||
|
|
||||||
editor.putStringSet(key, buttonCodes.mapTo(mutableSetOf()) { it.toString() })
|
editor.putStringSet(key, buttonCodes.mapTo(mutableSetOf()) {it.toString()})
|
||||||
|
|
||||||
// Write next reverse mapping for future cleanup
|
// Write next reverse mapping for future cleanup
|
||||||
editor.putString(reverseKey, key)
|
editor.putString(reverseKey, key)
|
||||||
|
|
@ -207,7 +217,7 @@ class InputBindingSetting(val abstractSetting: AbstractSetting, titleId: Int) :
|
||||||
preferences.edit()
|
preferences.edit()
|
||||||
.putInt(getInputAxisOrientationKey(axis), if (isHorizontalOrientation()) 0 else 1)
|
.putInt(getInputAxisOrientationKey(axis), if (isHorizontalOrientation()) 0 else 1)
|
||||||
.putInt(getInputAxisButtonKey(axis), value)
|
.putInt(getInputAxisButtonKey(axis), value)
|
||||||
.putBoolean(getInputAxisInvertedKey(axis), inverted)
|
.putBoolean(getInputAxisInvertedKey(axis),inverted)
|
||||||
// Write next reverse mapping for future cleanup
|
// Write next reverse mapping for future cleanup
|
||||||
.putString(reverseKey, getInputAxisKey(axis))
|
.putString(reverseKey, getInputAxisKey(axis))
|
||||||
.apply()
|
.apply()
|
||||||
|
|
@ -261,7 +271,8 @@ class InputBindingSetting(val abstractSetting: AbstractSetting, titleId: Int) :
|
||||||
companion object {
|
companion object {
|
||||||
private const val INPUT_MAPPING_PREFIX = "InputMapping"
|
private const val INPUT_MAPPING_PREFIX = "InputMapping"
|
||||||
|
|
||||||
private fun toTitleCase(raw: String): String = raw.replace("_", " ").lowercase()
|
private fun toTitleCase(raw: String): String =
|
||||||
|
raw.replace("_", " ").lowercase()
|
||||||
.split(" ").joinToString(" ") { it.replaceFirstChar { c -> c.uppercase() } }
|
.split(" ").joinToString(" ") { it.replaceFirstChar { c -> c.uppercase() } }
|
||||||
|
|
||||||
private const val BUTTON_NAME_L3 = "Button L3"
|
private const val BUTTON_NAME_L3 = "Button L3"
|
||||||
|
|
@ -276,7 +287,8 @@ class InputBindingSetting(val abstractSetting: AbstractSetting, titleId: Int) :
|
||||||
LINUX_BTN_DPAD_RIGHT to "Dpad Right"
|
LINUX_BTN_DPAD_RIGHT to "Dpad Right"
|
||||||
)
|
)
|
||||||
|
|
||||||
fun getButtonName(keyCode: Int): String = buttonNameOverrides[keyCode]
|
fun getButtonName(keyCode: Int): String =
|
||||||
|
buttonNameOverrides[keyCode]
|
||||||
?: toTitleCase(KeyEvent.keyCodeToString(keyCode).removePrefix("KEYCODE_"))
|
?: toTitleCase(KeyEvent.keyCodeToString(keyCode).removePrefix("KEYCODE_"))
|
||||||
|
|
||||||
private data class DefaultButtonMapping(
|
private data class DefaultButtonMapping(
|
||||||
|
|
@ -284,7 +296,6 @@ class InputBindingSetting(val abstractSetting: AbstractSetting, titleId: Int) :
|
||||||
val hostKeyCode: Int,
|
val hostKeyCode: Int,
|
||||||
val guestButtonCode: Int
|
val guestButtonCode: Int
|
||||||
)
|
)
|
||||||
|
|
||||||
// Auto-map always sets inverted = false. Users needing inverted axes should remap manually.
|
// Auto-map always sets inverted = false. Users needing inverted axes should remap manually.
|
||||||
private data class DefaultAxisMapping(
|
private data class DefaultAxisMapping(
|
||||||
val settingKey: String,
|
val settingKey: String,
|
||||||
|
|
@ -295,153 +306,45 @@ class InputBindingSetting(val abstractSetting: AbstractSetting, titleId: Int) :
|
||||||
)
|
)
|
||||||
|
|
||||||
private val xboxFaceButtonMappings = listOf(
|
private val xboxFaceButtonMappings = listOf(
|
||||||
DefaultButtonMapping(
|
DefaultButtonMapping(Settings.KEY_BUTTON_A, KeyEvent.KEYCODE_BUTTON_B, NativeLibrary.ButtonType.BUTTON_A),
|
||||||
Settings.KEY_BUTTON_A,
|
DefaultButtonMapping(Settings.KEY_BUTTON_B, KeyEvent.KEYCODE_BUTTON_A, NativeLibrary.ButtonType.BUTTON_B),
|
||||||
KeyEvent.KEYCODE_BUTTON_B,
|
DefaultButtonMapping(Settings.KEY_BUTTON_X, KeyEvent.KEYCODE_BUTTON_Y, NativeLibrary.ButtonType.BUTTON_X),
|
||||||
NativeLibrary.ButtonType.BUTTON_A
|
DefaultButtonMapping(Settings.KEY_BUTTON_Y, KeyEvent.KEYCODE_BUTTON_X, NativeLibrary.ButtonType.BUTTON_Y)
|
||||||
),
|
|
||||||
DefaultButtonMapping(
|
|
||||||
Settings.KEY_BUTTON_B,
|
|
||||||
KeyEvent.KEYCODE_BUTTON_A,
|
|
||||||
NativeLibrary.ButtonType.BUTTON_B
|
|
||||||
),
|
|
||||||
DefaultButtonMapping(
|
|
||||||
Settings.KEY_BUTTON_X,
|
|
||||||
KeyEvent.KEYCODE_BUTTON_Y,
|
|
||||||
NativeLibrary.ButtonType.BUTTON_X
|
|
||||||
),
|
|
||||||
DefaultButtonMapping(
|
|
||||||
Settings.KEY_BUTTON_Y,
|
|
||||||
KeyEvent.KEYCODE_BUTTON_X,
|
|
||||||
NativeLibrary.ButtonType.BUTTON_Y
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
private val nintendoFaceButtonMappings = listOf(
|
private val nintendoFaceButtonMappings = listOf(
|
||||||
DefaultButtonMapping(
|
DefaultButtonMapping(Settings.KEY_BUTTON_A, KeyEvent.KEYCODE_BUTTON_A, NativeLibrary.ButtonType.BUTTON_A),
|
||||||
Settings.KEY_BUTTON_A,
|
DefaultButtonMapping(Settings.KEY_BUTTON_B, KeyEvent.KEYCODE_BUTTON_B, NativeLibrary.ButtonType.BUTTON_B),
|
||||||
KeyEvent.KEYCODE_BUTTON_A,
|
DefaultButtonMapping(Settings.KEY_BUTTON_X, KeyEvent.KEYCODE_BUTTON_X, NativeLibrary.ButtonType.BUTTON_X),
|
||||||
NativeLibrary.ButtonType.BUTTON_A
|
DefaultButtonMapping(Settings.KEY_BUTTON_Y, KeyEvent.KEYCODE_BUTTON_Y, NativeLibrary.ButtonType.BUTTON_Y)
|
||||||
),
|
|
||||||
DefaultButtonMapping(
|
|
||||||
Settings.KEY_BUTTON_B,
|
|
||||||
KeyEvent.KEYCODE_BUTTON_B,
|
|
||||||
NativeLibrary.ButtonType.BUTTON_B
|
|
||||||
),
|
|
||||||
DefaultButtonMapping(
|
|
||||||
Settings.KEY_BUTTON_X,
|
|
||||||
KeyEvent.KEYCODE_BUTTON_X,
|
|
||||||
NativeLibrary.ButtonType.BUTTON_X
|
|
||||||
),
|
|
||||||
DefaultButtonMapping(
|
|
||||||
Settings.KEY_BUTTON_Y,
|
|
||||||
KeyEvent.KEYCODE_BUTTON_Y,
|
|
||||||
NativeLibrary.ButtonType.BUTTON_Y
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
private val commonButtonMappings = listOf(
|
private val commonButtonMappings = listOf(
|
||||||
DefaultButtonMapping(
|
DefaultButtonMapping(Settings.KEY_BUTTON_L, KeyEvent.KEYCODE_BUTTON_L1, NativeLibrary.ButtonType.TRIGGER_L),
|
||||||
Settings.KEY_BUTTON_L,
|
DefaultButtonMapping(Settings.KEY_BUTTON_R, KeyEvent.KEYCODE_BUTTON_R1, NativeLibrary.ButtonType.TRIGGER_R),
|
||||||
KeyEvent.KEYCODE_BUTTON_L1,
|
DefaultButtonMapping(Settings.KEY_BUTTON_ZL, KeyEvent.KEYCODE_BUTTON_L2, NativeLibrary.ButtonType.BUTTON_ZL),
|
||||||
NativeLibrary.ButtonType.TRIGGER_L
|
DefaultButtonMapping(Settings.KEY_BUTTON_ZR, KeyEvent.KEYCODE_BUTTON_R2, NativeLibrary.ButtonType.BUTTON_ZR),
|
||||||
),
|
DefaultButtonMapping(Settings.KEY_BUTTON_SELECT, KeyEvent.KEYCODE_BUTTON_SELECT, NativeLibrary.ButtonType.BUTTON_SELECT),
|
||||||
DefaultButtonMapping(
|
DefaultButtonMapping(Settings.KEY_BUTTON_START, KeyEvent.KEYCODE_BUTTON_START, NativeLibrary.ButtonType.BUTTON_START)
|
||||||
Settings.KEY_BUTTON_R,
|
|
||||||
KeyEvent.KEYCODE_BUTTON_R1,
|
|
||||||
NativeLibrary.ButtonType.TRIGGER_R
|
|
||||||
),
|
|
||||||
DefaultButtonMapping(
|
|
||||||
Settings.KEY_BUTTON_ZL,
|
|
||||||
KeyEvent.KEYCODE_BUTTON_L2,
|
|
||||||
NativeLibrary.ButtonType.BUTTON_ZL
|
|
||||||
),
|
|
||||||
DefaultButtonMapping(
|
|
||||||
Settings.KEY_BUTTON_ZR,
|
|
||||||
KeyEvent.KEYCODE_BUTTON_R2,
|
|
||||||
NativeLibrary.ButtonType.BUTTON_ZR
|
|
||||||
),
|
|
||||||
DefaultButtonMapping(
|
|
||||||
Settings.KEY_BUTTON_SELECT,
|
|
||||||
KeyEvent.KEYCODE_BUTTON_SELECT,
|
|
||||||
NativeLibrary.ButtonType.BUTTON_SELECT
|
|
||||||
),
|
|
||||||
DefaultButtonMapping(
|
|
||||||
Settings.KEY_BUTTON_START,
|
|
||||||
KeyEvent.KEYCODE_BUTTON_START,
|
|
||||||
NativeLibrary.ButtonType.BUTTON_START
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
private val dpadButtonMappings = listOf(
|
private val dpadButtonMappings = listOf(
|
||||||
DefaultButtonMapping(
|
DefaultButtonMapping(Settings.KEY_BUTTON_UP, KeyEvent.KEYCODE_DPAD_UP, NativeLibrary.ButtonType.DPAD_UP),
|
||||||
Settings.KEY_BUTTON_UP,
|
DefaultButtonMapping(Settings.KEY_BUTTON_DOWN, KeyEvent.KEYCODE_DPAD_DOWN, NativeLibrary.ButtonType.DPAD_DOWN),
|
||||||
KeyEvent.KEYCODE_DPAD_UP,
|
DefaultButtonMapping(Settings.KEY_BUTTON_LEFT, KeyEvent.KEYCODE_DPAD_LEFT, NativeLibrary.ButtonType.DPAD_LEFT),
|
||||||
NativeLibrary.ButtonType.DPAD_UP
|
DefaultButtonMapping(Settings.KEY_BUTTON_RIGHT, KeyEvent.KEYCODE_DPAD_RIGHT, NativeLibrary.ButtonType.DPAD_RIGHT)
|
||||||
),
|
|
||||||
DefaultButtonMapping(
|
|
||||||
Settings.KEY_BUTTON_DOWN,
|
|
||||||
KeyEvent.KEYCODE_DPAD_DOWN,
|
|
||||||
NativeLibrary.ButtonType.DPAD_DOWN
|
|
||||||
),
|
|
||||||
DefaultButtonMapping(
|
|
||||||
Settings.KEY_BUTTON_LEFT,
|
|
||||||
KeyEvent.KEYCODE_DPAD_LEFT,
|
|
||||||
NativeLibrary.ButtonType.DPAD_LEFT
|
|
||||||
),
|
|
||||||
DefaultButtonMapping(
|
|
||||||
Settings.KEY_BUTTON_RIGHT,
|
|
||||||
KeyEvent.KEYCODE_DPAD_RIGHT,
|
|
||||||
NativeLibrary.ButtonType.DPAD_RIGHT
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
private val stickAxisMappings = listOf(
|
private val stickAxisMappings = listOf(
|
||||||
DefaultAxisMapping(
|
DefaultAxisMapping(Settings.KEY_CIRCLEPAD_AXIS_HORIZONTAL, MotionEvent.AXIS_X, NativeLibrary.ButtonType.STICK_LEFT, 0, false),
|
||||||
Settings.KEY_CIRCLEPAD_AXIS_HORIZONTAL,
|
DefaultAxisMapping(Settings.KEY_CIRCLEPAD_AXIS_VERTICAL, MotionEvent.AXIS_Y, NativeLibrary.ButtonType.STICK_LEFT, 1, false),
|
||||||
MotionEvent.AXIS_X,
|
DefaultAxisMapping(Settings.KEY_CSTICK_AXIS_HORIZONTAL, MotionEvent.AXIS_Z, NativeLibrary.ButtonType.STICK_C, 0, false),
|
||||||
NativeLibrary.ButtonType.STICK_LEFT,
|
DefaultAxisMapping(Settings.KEY_CSTICK_AXIS_VERTICAL, MotionEvent.AXIS_RZ, NativeLibrary.ButtonType.STICK_C, 1, false)
|
||||||
0,
|
|
||||||
false
|
|
||||||
),
|
|
||||||
DefaultAxisMapping(
|
|
||||||
Settings.KEY_CIRCLEPAD_AXIS_VERTICAL,
|
|
||||||
MotionEvent.AXIS_Y,
|
|
||||||
NativeLibrary.ButtonType.STICK_LEFT,
|
|
||||||
1,
|
|
||||||
false
|
|
||||||
),
|
|
||||||
DefaultAxisMapping(
|
|
||||||
Settings.KEY_CSTICK_AXIS_HORIZONTAL,
|
|
||||||
MotionEvent.AXIS_Z,
|
|
||||||
NativeLibrary.ButtonType.STICK_C,
|
|
||||||
0,
|
|
||||||
false
|
|
||||||
),
|
|
||||||
DefaultAxisMapping(
|
|
||||||
Settings.KEY_CSTICK_AXIS_VERTICAL,
|
|
||||||
MotionEvent.AXIS_RZ,
|
|
||||||
NativeLibrary.ButtonType.STICK_C,
|
|
||||||
1,
|
|
||||||
false
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
private val dpadAxisMappings = listOf(
|
private val dpadAxisMappings = listOf(
|
||||||
DefaultAxisMapping(
|
DefaultAxisMapping(Settings.KEY_DPAD_AXIS_HORIZONTAL, MotionEvent.AXIS_HAT_X, NativeLibrary.ButtonType.DPAD, 0, false),
|
||||||
Settings.KEY_DPAD_AXIS_HORIZONTAL,
|
DefaultAxisMapping(Settings.KEY_DPAD_AXIS_VERTICAL, MotionEvent.AXIS_HAT_Y, NativeLibrary.ButtonType.DPAD, 1, false)
|
||||||
MotionEvent.AXIS_HAT_X,
|
|
||||||
NativeLibrary.ButtonType.DPAD,
|
|
||||||
0,
|
|
||||||
false
|
|
||||||
),
|
|
||||||
DefaultAxisMapping(
|
|
||||||
Settings.KEY_DPAD_AXIS_VERTICAL,
|
|
||||||
MotionEvent.AXIS_HAT_Y,
|
|
||||||
NativeLibrary.ButtonType.DPAD,
|
|
||||||
1,
|
|
||||||
false
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Nintendo Switch Joy-Con specific mappings.
|
// Nintendo Switch Joy-Con specific mappings.
|
||||||
|
|
@ -466,84 +369,28 @@ class InputBindingSetting(val abstractSetting: AbstractSetting, titleId: Int) :
|
||||||
// This is different from both the standard Xbox table (full swap) and the
|
// This is different from both the standard Xbox table (full swap) and the
|
||||||
// Nintendo table (no swap).
|
// Nintendo table (no swap).
|
||||||
private val joyconFaceButtonMappings = listOf(
|
private val joyconFaceButtonMappings = listOf(
|
||||||
DefaultButtonMapping(
|
DefaultButtonMapping(Settings.KEY_BUTTON_A, KeyEvent.KEYCODE_BUTTON_B, NativeLibrary.ButtonType.BUTTON_A),
|
||||||
Settings.KEY_BUTTON_A,
|
DefaultButtonMapping(Settings.KEY_BUTTON_B, KeyEvent.KEYCODE_BUTTON_A, NativeLibrary.ButtonType.BUTTON_B),
|
||||||
KeyEvent.KEYCODE_BUTTON_B,
|
DefaultButtonMapping(Settings.KEY_BUTTON_X, KeyEvent.KEYCODE_BUTTON_X, NativeLibrary.ButtonType.BUTTON_X),
|
||||||
NativeLibrary.ButtonType.BUTTON_A
|
DefaultButtonMapping(Settings.KEY_BUTTON_Y, KeyEvent.KEYCODE_BUTTON_Y, NativeLibrary.ButtonType.BUTTON_Y)
|
||||||
),
|
|
||||||
DefaultButtonMapping(
|
|
||||||
Settings.KEY_BUTTON_B,
|
|
||||||
KeyEvent.KEYCODE_BUTTON_A,
|
|
||||||
NativeLibrary.ButtonType.BUTTON_B
|
|
||||||
),
|
|
||||||
DefaultButtonMapping(
|
|
||||||
Settings.KEY_BUTTON_X,
|
|
||||||
KeyEvent.KEYCODE_BUTTON_X,
|
|
||||||
NativeLibrary.ButtonType.BUTTON_X
|
|
||||||
),
|
|
||||||
DefaultButtonMapping(
|
|
||||||
Settings.KEY_BUTTON_Y,
|
|
||||||
KeyEvent.KEYCODE_BUTTON_Y,
|
|
||||||
NativeLibrary.ButtonType.BUTTON_Y
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Joy-Con D-pad: uses Linux scan codes because Android reports BTN_DPAD_* as KEYCODE_UNKNOWN
|
// Joy-Con D-pad: uses Linux scan codes because Android reports BTN_DPAD_* as KEYCODE_UNKNOWN
|
||||||
private val joyconDpadButtonMappings = listOf(
|
private val joyconDpadButtonMappings = listOf(
|
||||||
DefaultButtonMapping(
|
DefaultButtonMapping(Settings.KEY_BUTTON_UP, LINUX_BTN_DPAD_UP, NativeLibrary.ButtonType.DPAD_UP),
|
||||||
Settings.KEY_BUTTON_UP,
|
DefaultButtonMapping(Settings.KEY_BUTTON_DOWN, LINUX_BTN_DPAD_DOWN, NativeLibrary.ButtonType.DPAD_DOWN),
|
||||||
LINUX_BTN_DPAD_UP,
|
DefaultButtonMapping(Settings.KEY_BUTTON_LEFT, LINUX_BTN_DPAD_LEFT, NativeLibrary.ButtonType.DPAD_LEFT),
|
||||||
NativeLibrary.ButtonType.DPAD_UP
|
DefaultButtonMapping(Settings.KEY_BUTTON_RIGHT, LINUX_BTN_DPAD_RIGHT, NativeLibrary.ButtonType.DPAD_RIGHT)
|
||||||
),
|
|
||||||
DefaultButtonMapping(
|
|
||||||
Settings.KEY_BUTTON_DOWN,
|
|
||||||
LINUX_BTN_DPAD_DOWN,
|
|
||||||
NativeLibrary.ButtonType.DPAD_DOWN
|
|
||||||
),
|
|
||||||
DefaultButtonMapping(
|
|
||||||
Settings.KEY_BUTTON_LEFT,
|
|
||||||
LINUX_BTN_DPAD_LEFT,
|
|
||||||
NativeLibrary.ButtonType.DPAD_LEFT
|
|
||||||
),
|
|
||||||
DefaultButtonMapping(
|
|
||||||
Settings.KEY_BUTTON_RIGHT,
|
|
||||||
LINUX_BTN_DPAD_RIGHT,
|
|
||||||
NativeLibrary.ButtonType.DPAD_RIGHT
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Joy-Con sticks: left stick is AXIS_X/Y (standard), right stick is AXIS_RX/RY
|
// Joy-Con sticks: left stick is AXIS_X/Y (standard), right stick is AXIS_RX/RY
|
||||||
// (not Z/RZ like most controllers). The horizontal axis is inverted relative to
|
// (not Z/RZ like most controllers). The horizontal axis is inverted relative to
|
||||||
// the standard orientation - verified empirically on paired Joy-Cons via Bluetooth.
|
// the standard orientation - verified empirically on paired Joy-Cons via Bluetooth.
|
||||||
private val joyconStickAxisMappings = listOf(
|
private val joyconStickAxisMappings = listOf(
|
||||||
DefaultAxisMapping(
|
DefaultAxisMapping(Settings.KEY_CIRCLEPAD_AXIS_HORIZONTAL, MotionEvent.AXIS_X, NativeLibrary.ButtonType.STICK_LEFT, 0, false),
|
||||||
Settings.KEY_CIRCLEPAD_AXIS_HORIZONTAL,
|
DefaultAxisMapping(Settings.KEY_CIRCLEPAD_AXIS_VERTICAL, MotionEvent.AXIS_Y, NativeLibrary.ButtonType.STICK_LEFT, 1, false),
|
||||||
MotionEvent.AXIS_X,
|
DefaultAxisMapping(Settings.KEY_CSTICK_AXIS_HORIZONTAL, MotionEvent.AXIS_RX, NativeLibrary.ButtonType.STICK_C, 0, true),
|
||||||
NativeLibrary.ButtonType.STICK_LEFT,
|
DefaultAxisMapping(Settings.KEY_CSTICK_AXIS_VERTICAL, MotionEvent.AXIS_RY, NativeLibrary.ButtonType.STICK_C, 1, false)
|
||||||
0,
|
|
||||||
false
|
|
||||||
),
|
|
||||||
DefaultAxisMapping(
|
|
||||||
Settings.KEY_CIRCLEPAD_AXIS_VERTICAL,
|
|
||||||
MotionEvent.AXIS_Y,
|
|
||||||
NativeLibrary.ButtonType.STICK_LEFT,
|
|
||||||
1,
|
|
||||||
false
|
|
||||||
),
|
|
||||||
DefaultAxisMapping(
|
|
||||||
Settings.KEY_CSTICK_AXIS_HORIZONTAL,
|
|
||||||
MotionEvent.AXIS_RX,
|
|
||||||
NativeLibrary.ButtonType.STICK_C,
|
|
||||||
0,
|
|
||||||
true
|
|
||||||
),
|
|
||||||
DefaultAxisMapping(
|
|
||||||
Settings.KEY_CSTICK_AXIS_VERTICAL,
|
|
||||||
MotionEvent.AXIS_RY,
|
|
||||||
NativeLibrary.ButtonType.STICK_C,
|
|
||||||
1,
|
|
||||||
false
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -570,11 +417,9 @@ class InputBindingSetting(val abstractSetting: AbstractSetting, titleId: Int) :
|
||||||
}
|
}
|
||||||
|
|
||||||
private val allBindingKeys: Set<String> by lazy {
|
private val allBindingKeys: Set<String> by lazy {
|
||||||
(
|
(Settings.buttonKeys + Settings.triggerKeys +
|
||||||
Settings.buttonKeys + Settings.triggerKeys +
|
|
||||||
Settings.circlePadKeys + Settings.cStickKeys + Settings.dPadAxisKeys +
|
Settings.circlePadKeys + Settings.cStickKeys + Settings.dPadAxisKeys +
|
||||||
Settings.dPadButtonKeys
|
Settings.dPadButtonKeys).toSet()
|
||||||
).toSet()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun clearAllBindings() {
|
fun clearAllBindings() {
|
||||||
|
|
@ -620,11 +465,7 @@ class InputBindingSetting(val abstractSetting: AbstractSetting, titleId: Int) :
|
||||||
* false if it should be mapped as individual button keycodes (DPAD_UP/DOWN/LEFT/RIGHT)
|
* false if it should be mapped as individual button keycodes (DPAD_UP/DOWN/LEFT/RIGHT)
|
||||||
*/
|
*/
|
||||||
fun applyAutoMapBindings(isNintendoLayout: Boolean, useAxisDpad: Boolean) {
|
fun applyAutoMapBindings(isNintendoLayout: Boolean, useAxisDpad: Boolean) {
|
||||||
val faceButtons = if (isNintendoLayout) {
|
val faceButtons = if (isNintendoLayout) nintendoFaceButtonMappings else xboxFaceButtonMappings
|
||||||
nintendoFaceButtonMappings
|
|
||||||
} else {
|
|
||||||
xboxFaceButtonMappings
|
|
||||||
}
|
|
||||||
val buttonMappings = if (useAxisDpad) {
|
val buttonMappings = if (useAxisDpad) {
|
||||||
faceButtons + commonButtonMappings
|
faceButtons + commonButtonMappings
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -661,16 +502,15 @@ class InputBindingSetting(val abstractSetting: AbstractSetting, titleId: Int) :
|
||||||
editor.putBoolean(getInputAxisInvertedKey(mapping.hostAxis), mapping.inverted)
|
editor.putBoolean(getInputAxisInvertedKey(mapping.hostAxis), mapping.inverted)
|
||||||
val dir = if (mapping.orientation == 0) '+' else '-'
|
val dir = if (mapping.orientation == 0) '+' else '-'
|
||||||
editor.putString(mapping.settingKey, "Axis ${mapping.hostAxis}$dir")
|
editor.putString(mapping.settingKey, "Axis ${mapping.hostAxis}$dir")
|
||||||
@Suppress("ktlint:standard:max-line-length")
|
val reverseKey = "${INPUT_MAPPING_PREFIX}_ReverseMapping_${mapping.settingKey}_${mapping.orientation}"
|
||||||
val reverseKey =
|
|
||||||
"${INPUT_MAPPING_PREFIX}_ReverseMapping_${mapping.settingKey}_${mapping.orientation}"
|
|
||||||
editor.putString(reverseKey, axisKey)
|
editor.putString(reverseKey, axisKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the settings key for the specified Citra button code.
|
* Returns the settings key for the specified Citra button code.
|
||||||
*/
|
*/
|
||||||
private fun getButtonKey(buttonCode: Int): String = when (buttonCode) {
|
private fun getButtonKey(buttonCode: Int): String =
|
||||||
|
when (buttonCode) {
|
||||||
NativeLibrary.ButtonType.BUTTON_A -> Settings.KEY_BUTTON_A
|
NativeLibrary.ButtonType.BUTTON_A -> Settings.KEY_BUTTON_A
|
||||||
NativeLibrary.ButtonType.BUTTON_B -> Settings.KEY_BUTTON_B
|
NativeLibrary.ButtonType.BUTTON_B -> Settings.KEY_BUTTON_B
|
||||||
NativeLibrary.ButtonType.BUTTON_X -> Settings.KEY_BUTTON_X
|
NativeLibrary.ButtonType.BUTTON_X -> Settings.KEY_BUTTON_X
|
||||||
|
|
@ -688,19 +528,16 @@ class InputBindingSetting(val abstractSetting: AbstractSetting, titleId: Int) :
|
||||||
NativeLibrary.ButtonType.DPAD_RIGHT -> Settings.KEY_BUTTON_RIGHT
|
NativeLibrary.ButtonType.DPAD_RIGHT -> Settings.KEY_BUTTON_RIGHT
|
||||||
else -> ""
|
else -> ""
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the mutable set of int button values this key should map to given an event
|
* Get the mutable set of int button values this key should map to given an event
|
||||||
*/
|
*/
|
||||||
fun getButtonSet(keyCode: KeyEvent): MutableSet<Int> {
|
fun getButtonSet(keyCode: KeyEvent):MutableSet<Int> {
|
||||||
val key = getInputButtonKey(keyCode)
|
val key = getInputButtonKey(keyCode)
|
||||||
val preferences = PreferenceManager.getDefaultSharedPreferences(
|
val preferences = PreferenceManager.getDefaultSharedPreferences(CitraApplication.appContext)
|
||||||
CitraApplication.appContext
|
|
||||||
)
|
|
||||||
var buttonCodes = try {
|
var buttonCodes = try {
|
||||||
preferences.getStringSet(key, mutableSetOf<String>())
|
preferences.getStringSet(key, mutableSetOf<String>())
|
||||||
} catch (e: ClassCastException) {
|
} catch (e: ClassCastException) {
|
||||||
val prefInt = preferences.getInt(key, -1)
|
val prefInt = preferences.getInt(key, -1);
|
||||||
val migratedSet = if (prefInt != -1) {
|
val migratedSet = if (prefInt != -1) {
|
||||||
mutableSetOf(prefInt.toString())
|
mutableSetOf(prefInt.toString())
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -712,17 +549,15 @@ class InputBindingSetting(val abstractSetting: AbstractSetting, titleId: Int) :
|
||||||
return buttonCodes.mapNotNull { it.toIntOrNull() }.toMutableSet()
|
return buttonCodes.mapNotNull { it.toIntOrNull() }.toMutableSet()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getInputButtonKey(keyId: Int): String =
|
private fun getInputButtonKey(keyId: Int): String = "${INPUT_MAPPING_PREFIX}_HostAxis_${keyId}"
|
||||||
"${INPUT_MAPPING_PREFIX}_HostAxis_$keyId"
|
|
||||||
|
|
||||||
/** Falls back to the scan code when keyCode is KEYCODE_UNKNOWN. */
|
/** Falls back to the scan code when keyCode is KEYCODE_UNKNOWN. */
|
||||||
fun getInputButtonKey(event: KeyEvent): String =
|
fun getInputButtonKey(event: KeyEvent): String = getInputButtonKey(translateEventToKeyId(event))
|
||||||
getInputButtonKey(translateEventToKeyId(event))
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper function to get the settings key for an gamepad axis.
|
* Helper function to get the settings key for an gamepad axis.
|
||||||
*/
|
*/
|
||||||
fun getInputAxisKey(axis: Int): String = "${INPUT_MAPPING_PREFIX}_HostAxis_$axis"
|
fun getInputAxisKey(axis: Int): String = "${INPUT_MAPPING_PREFIX}_HostAxis_${axis}"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper function to get the settings key for an gamepad axis button (stick or trigger).
|
* Helper function to get the settings key for an gamepad axis button (stick or trigger).
|
||||||
|
|
@ -740,6 +575,7 @@ class InputBindingSetting(val abstractSetting: AbstractSetting, titleId: Int) :
|
||||||
fun getInputAxisOrientationKey(axis: Int): String =
|
fun getInputAxisOrientationKey(axis: Int): String =
|
||||||
"${getInputAxisKey(axis)}_GuestOrientation"
|
"${getInputAxisKey(axis)}_GuestOrientation"
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function translates a keyEvent into an "keyid"
|
* This function translates a keyEvent into an "keyid"
|
||||||
* This key id is either the keyCode from the event, or
|
* This key id is either the keyCode from the event, or
|
||||||
|
|
@ -749,10 +585,12 @@ class InputBindingSetting(val abstractSetting: AbstractSetting, titleId: Int) :
|
||||||
* This handles keys like the media-keys on google statia-controllers
|
* This handles keys like the media-keys on google statia-controllers
|
||||||
* that don't have a conventional "mapping" and report as "unknown"
|
* that don't have a conventional "mapping" and report as "unknown"
|
||||||
*/
|
*/
|
||||||
fun translateEventToKeyId(event: KeyEvent): Int = if (event.keyCode == 0) {
|
fun translateEventToKeyId(event: KeyEvent): Int {
|
||||||
|
return if (event.keyCode == 0) {
|
||||||
event.scanCode
|
event.scanCode
|
||||||
} else {
|
} else {
|
||||||
event.keyCode
|
event.keyCode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,6 @@
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
package org.citra.citra_emu.features.settings.model.view
|
package org.citra.citra_emu.features.settings.model.view
|
||||||
import androidx.annotation.StringRes
|
|
||||||
import org.citra.citra_emu.R
|
|
||||||
import org.citra.citra_emu.features.settings.model.AbstractSetting
|
import org.citra.citra_emu.features.settings.model.AbstractSetting
|
||||||
import org.citra.citra_emu.features.settings.model.IntListSetting
|
import org.citra.citra_emu.features.settings.model.IntListSetting
|
||||||
class MultiChoiceSetting(
|
class MultiChoiceSetting(
|
||||||
|
|
@ -15,9 +13,7 @@ class MultiChoiceSetting(
|
||||||
val valuesId: Int,
|
val valuesId: Int,
|
||||||
val key: String? = null,
|
val key: String? = null,
|
||||||
val defaultValue: List<Int>? = null,
|
val defaultValue: List<Int>? = null,
|
||||||
override var isEnabled: Boolean = true,
|
override var isEnabled: Boolean = true
|
||||||
@StringRes override var disabledMessage: Int =
|
|
||||||
R.string.setting_disabled_description_incompatible_setting
|
|
||||||
) : SettingsItem(setting, titleId, descriptionId) {
|
) : SettingsItem(setting, titleId, descriptionId) {
|
||||||
override val type = TYPE_MULTI_CHOICE
|
override val type = TYPE_MULTI_CHOICE
|
||||||
|
|
||||||
|
|
@ -29,7 +25,7 @@ class MultiChoiceSetting(
|
||||||
try {
|
try {
|
||||||
val setting = setting as IntListSetting
|
val setting = setting as IntListSetting
|
||||||
return setting.list
|
return setting.list
|
||||||
} catch (_: ClassCastException) {
|
}catch (_: ClassCastException) {
|
||||||
}
|
}
|
||||||
return defaultValue!!
|
return defaultValue!!
|
||||||
}
|
}
|
||||||
|
|
@ -46,4 +42,5 @@ class MultiChoiceSetting(
|
||||||
intSetting.list = selection
|
intSetting.list = selection
|
||||||
return intSetting
|
return intSetting
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,7 @@
|
||||||
|
|
||||||
package org.citra.citra_emu.features.settings.model.view
|
package org.citra.citra_emu.features.settings.model.view
|
||||||
|
|
||||||
import androidx.annotation.StringRes
|
import org.citra.citra_emu.NativeLibrary
|
||||||
import org.citra.citra_emu.R
|
|
||||||
import org.citra.citra_emu.activities.EmulationActivity
|
import org.citra.citra_emu.activities.EmulationActivity
|
||||||
import org.citra.citra_emu.features.settings.model.AbstractSetting
|
import org.citra.citra_emu.features.settings.model.AbstractSetting
|
||||||
|
|
||||||
|
|
@ -31,9 +30,6 @@ abstract class SettingsItem(
|
||||||
|
|
||||||
open var isEnabled: Boolean = true
|
open var isEnabled: Boolean = true
|
||||||
|
|
||||||
@StringRes open var disabledMessage: Int =
|
|
||||||
R.string.setting_disabled_description_incompatible_setting
|
|
||||||
|
|
||||||
val isActive: Boolean
|
val isActive: Boolean
|
||||||
get() {
|
get() {
|
||||||
return this.isEditable && this.isEnabled
|
return this.isEditable && this.isEnabled
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue