Compare commits

...

98 Commits

Author SHA1 Message Date
9e15bf125f Android 248 2024-02-14 00:56:53 +00:00
88fe6185a4 Merge yuzu-emu#13000 2024-02-14 00:56:53 +00:00
3b47d624ed Merge yuzu-emu#12996 2024-02-14 00:56:53 +00:00
680addb499 Merge yuzu-emu#12993 2024-02-14 00:56:53 +00:00
e9b198279f Merge yuzu-emu#12749 2024-02-14 00:56:53 +00:00
c395897b85 Merge yuzu-emu#12461 2024-02-14 00:56:52 +00:00
1af4e990e9 Merge yuzu-emu#10529 2024-02-14 00:56:52 +00:00
ad4ae39903 Merge pull request #13009 from t895/message-dialog-fix
android: Message dialog tweaks
2024-02-13 14:46:56 -05:00
fefdba05ca Merge pull request #13007 from t895/screen-bias
android: Expose FSR slider and add vertical alignment setting
2024-02-13 14:46:48 -05:00
f813dc78b2 android: Prevent user from dismissing mod/cheat notice
Makes sure that a user can't miss this dialog by touching outside the window. They must press "OK" or "Close" to continue.
2024-02-13 13:46:14 -05:00
10ba318807 android: Show cancel option for delete addons dialog 2024-02-13 13:45:17 -05:00
86fc1e5b32 android: Swap ok and close default strings for MessageDialogFragment 2024-02-13 13:44:35 -05:00
3c823254ff android: Add screen vertical alignment setting
It's a bit of a hack since I'm moving the view instead of telling the Vulkan surface to bias itself to the top/bottom/center but it works fine for now.
2024-02-13 10:10:59 -05:00
a0513bc45b android: Expose FSR sharpness slider 2024-02-13 10:04:59 -05:00
95d96cfe66 Merge pull request #12974 from german77/ldn-interface
service: ldn: Migrate and refractor service to new IPC
2024-02-13 08:18:31 -05:00
f75fceb3c0 Merge pull request #12975 from FernandoS27/keep-your-own-vodoo-doll-away-from-gf
Texture Cache: Fix untracking on GPU remap
2024-02-13 08:17:59 -05:00
3511d5552a Merge pull request #12989 from german77/hotcake
yuzu: Allow non npad hotkeys and disable controller navigation requirement
2024-02-13 08:17:50 -05:00
f27bdce70f Merge pull request #12998 from t895/swap-clear-actions
android: Swap confirmation buttons for delete save data dialog
2024-02-13 08:17:39 -05:00
85fd2bcb82 Merge pull request #12941 from FearlessTobi/setting-tooltips
shared_translation: Add tooltips for yuzu settings
2024-02-13 08:17:32 -05:00
836592c447 android: Swap confirmation buttons for delete save data dialog 2024-02-12 16:54:46 -05:00
fbc1b61bff android: Extend MessageDialogFragment to support a negative action and button titles 2024-02-12 16:54:19 -05:00
2ff45cd0da Merge pull request #12756 from liamwhite/applet-multiprocess-hwc
general: applet multiprocess
2024-02-11 20:58:28 -06:00
b6b56f48b7 Merge pull request #12991 from german77/news2
service: news: Stub remaining functions
2024-02-11 21:44:23 -05:00
04a9d14f35 service: news: Stub remaining functions 2024-02-11 17:56:26 -06:00
26e028808a yuzu: Allow non npad hotkeys and disable controller navigation requirement 2024-02-11 16:29:31 -06:00
2053ff96fc service: ldn: Migrate and refractor service to new IPC 2024-02-11 13:11:11 -06:00
98db796fde Merge pull request #12986 from t895/input-config-clear-fix
config: Always delete control settings in ClearControlPlayerValues
2024-02-11 12:24:18 -05:00
adebc96a9c config: Always delete control settings in ClearControlPlayerValues 2024-02-11 07:35:54 -05:00
564a65a82e Merge pull request #12981 from lat9nq/tzdb-asan-custom
tzdb_to_nx: Update to latest
2024-02-10 22:42:47 -05:00
501ff2eda5 Merge pull request #12980 from merryhime/race-me
dynarmic: Fix invalidation race
2024-02-10 22:42:42 -05:00
999ec5739d Merge pull request #12978 from liamwhite/ffs-qcom
host_shaders: add vendor workaround for adreno drivers
2024-02-10 22:42:25 -05:00
68b2db21b1 tzdb_to_nx: Update to latest
Includes memory leak fix.

Includes option to specify a custom zoneinfo dir.
2024-02-10 17:45:18 -05:00
2337397a15 Merge pull request #12969 from german77/bcat-interface
service: bcat: Migrate and refractor service to new IPC
2024-02-10 16:00:43 -05:00
7c56ecca3f Merge pull request #12949 from liamwhite/multi-wait
service: add os types and multi wait API
2024-02-10 16:00:34 -05:00
211544fbc8 externals: Update dynarmic to 6.6.3 2024-02-10 19:40:47 +00:00
4677fd3f64 am: use applet program loading for tested versions 2024-02-10 12:38:19 -05:00
4eeac731ff host_shaders: add vendor workaround for adreno drivers 2024-02-10 12:02:37 -05:00
9ce43ee677 Texture Cache: Fix untracking on GPU remap 2024-02-10 14:49:49 +01:00
816d03f7d9 service: bcat: Address review issues 2024-02-10 00:23:23 -06:00
909f7eb3d2 service: bcat: Implement news interfaces 2024-02-10 00:23:22 -06:00
7c2e9a6596 service: bcat: Migrate and refractor service to new IPC 2024-02-10 00:23:22 -06:00
fe6934593f Fix multiplayer player count color in dark themes | Temp fix until #12744: Add green color for counts > 0 and < max_players - 1 (#12930)
* fix intended player count color in dark themes

* Refactor

* Change to green color for white and dark themes

* Add const to the colors and extra name for green color
2024-02-09 18:45:11 -06:00
52c8adc7ed Merge pull request #12951 from liamwhite/more-ipc
ipc: additional fixes
2024-02-09 10:51:03 -06:00
7ec7ff0f30 Merge pull request #12920 from t895/jni-common
android: Move JNI setup and helpers to common
2024-02-09 11:49:25 -05:00
a133eadf06 Merge pull request #12927 from german77/cheat-pause
dmnt: cheat: Add pause and resume support
2024-02-09 11:47:34 -05:00
89dd0fa932 Merge pull request #12968 from t895/thermal-status
android: Thermal throttling indicator
2024-02-09 11:47:17 -05:00
a9dcfe2a42 Merge pull request #12964 from t895/foreground-service-test
android: Remove foreground service
2024-02-09 11:47:11 -05:00
2ad8d614b5 Merge pull request #12966 from german77/free_npad
service: hid: Free npad applet resource
2024-02-09 11:47:05 -05:00
b206ea5cfe am: fix focus states and display of indirect keyboard 2024-02-09 09:20:53 -05:00
70590f79f8 am: stub SetMediaPlaybackState for self controller 2024-02-09 09:20:53 -05:00
fa12384350 general: add default configurations for applet mode 2024-02-09 09:20:53 -05:00
78aac6b403 gpu: dependency-inject scaling/antialiasing filter state for capture layers 2024-02-09 09:20:53 -05:00
0cb413c3d3 nvnflinger/gpu: implement applet capture 2024-02-09 09:20:53 -05:00
962c82540c nvnflinger/gpu: implement blending 2024-02-09 09:20:53 -05:00
06fd7f2012 nvservices: unmap only on last container free 2024-02-09 09:20:53 -05:00
0cbb555e9a video_core: defensively program around unmapped device pointers 2024-02-09 09:20:53 -05:00
2e8c21ad2d core: fix multiprocess with nce 2024-02-09 09:20:53 -05:00
f44183db9e android: Use utility function for applying view margins 2024-02-09 07:07:06 -05:00
5fa9bc192c android: Add thermal throttling overlay 2024-02-09 07:07:05 -05:00
f9a559d2b7 Merge pull request #12967 from german77/let_me_out
service: Fix OutLargeData attributes
2024-02-08 21:33:22 -05:00
af87365672 android: Remove foreground service 2024-02-08 21:04:14 -05:00
03a23c037a service: Fix OutLargeData attributes 2024-02-08 19:40:06 -06:00
0ac777460d service: hid: Free npad applet resource 2024-02-08 18:50:54 -06:00
71e59bdcd8 Merge pull request #12963 from t895/versioning-fix
android: Fix regex for git version
2024-02-08 17:03:32 -05:00
0a1283f94f android: Fix regex for git version 2024-02-08 14:24:15 -05:00
2600ac65c8 android: Run OnEmulationStarted frontend callback in another thread
The JVM has problems with attaching to a Fiber so we start a new thread and wait for the result here.
2024-02-08 14:13:46 -05:00
c8e8c614a0 common: fs: Expand android macros 2024-02-08 14:13:46 -05:00
e7c4c8b993 android: Move JNI setup and helpers to common 2024-02-08 13:45:26 -05:00
7cfb51e5e7 shared_translation: Add tooltips for general settings 2024-02-08 18:13:22 +01:00
f049453dd6 Merge pull request #12903 from liamwhite/const-offset
shader_recompiler: use only ConstOffset for OpImageFetch
2024-02-08 17:00:45 +01:00
cac37a6f6e Merge pull request #12954 from german77/hidbus-interface
service: hid: Migrate hidbus to new interface
2024-02-08 11:00:11 -05:00
263dfa95e4 Merge pull request #12914 from FernandoS27/vc-refactor
VideoCore Refactor Part 1.
2024-02-08 10:59:59 -05:00
bc9711cb1e Merge pull request #12953 from FernandoS27/zero-fps-mah-ass
SMMU: Ensure the backing address range matches the current
2024-02-08 10:59:52 -05:00
b4d88a7bb4 service: hid: Migrate hidbus to new interface 2024-02-07 18:07:32 -06:00
ae833aa9c0 SMMU: Ensure the backing address range matches the current 2024-02-07 23:47:42 +01:00
4463ded603 Merge pull request #12939 from german77/wonder
dmnt: cheat: Invalidate cache on memory writes
2024-02-07 15:33:44 -05:00
159dec01ee Merge pull request #12932 from german77/any-key-is-good
yuzu: Make controller keys easier to assign
2024-02-07 15:33:39 -05:00
6319bafafa Merge pull request #12912 from FearlessTobi/ports-feb-24
Port some small changes from Citra (web_backend and translations)
2024-02-07 15:33:28 -05:00
c000a5ff09 Merge pull request #12909 from t895/play-store-automation
ci: android: Play store publishing setup
2024-02-07 15:32:42 -05:00
fee263c59c ipc: additional fixes 2024-02-07 15:06:15 -05:00
5a64a77df3 glue: use multi wait API 2024-02-07 12:15:01 -05:00
6810929f6a server_manager: use multi wait API 2024-02-07 12:15:01 -05:00
9404633bfd service: add os types and multi wait API 2024-02-07 12:14:46 -05:00
12f86f89fc yuzu: Make controller keys easier to assign 2024-02-06 16:51:39 -06:00
9858ea79fb dmnt: cheat: Invalidate cache on memory writes 2024-02-06 13:49:48 -06:00
2c357c929c shared_translation: Add tooltips for advanced graphics and system settings 2024-02-06 16:42:57 +01:00
482e203d5c shared_translation: Add tooltips for core and graphics settings 2024-02-06 16:29:13 +01:00
c0a383d960 web_backend: Fix compilation 2024-02-06 15:48:04 +01:00
fa47ac1c9f Common: Rename SplitRangeSet to OverlapRangeSet 2024-02-05 23:01:17 +01:00
c52d7cc694 dmnt: cheat: Add pause and resume support 2024-02-05 14:38:26 -06:00
f296a9ce9a shader_recompiler: use only ConstOffset for OpImageFetch 2024-02-05 12:01:09 -05:00
0d5a3abeae Buffer Cache: Refactor to use Range sets instead 2024-02-05 11:06:52 +01:00
accccc0cbf NVDRV: Refactor HeapMapper to use RangeSets 2024-02-04 20:01:50 +01:00
01ba6cf610 Common: Introduce Range Sets 2024-02-04 20:01:50 +01:00
4841dc0b74 VideoCore: Move Slot Vector to Common 2024-02-04 20:01:47 +01:00
185125e4e4 citra_qt/configure_ui: Show country of language in the combobox
This prevents an issue where we had seperate versions of the same language for different regions and they were not distinguishable (e.g. "Chinese (China)" and "Chinese (Taiwan)").

Also makes it so we do not need to hardcode specific languages anymore.
2024-02-04 17:06:44 +01:00
99ea31faa8 ci: android: Play store publishing setup 2024-02-04 10:54:18 -05:00
9ade941de1 web_backend: Sync with Citra implementation
While porting https://github.com/citra-emu/citra/pull/7347, I noticed the code of yuzu was not up-to-date with the implementation from Citra.
2024-02-04 16:51:52 +01:00
429 changed files with 24104 additions and 9619 deletions

View File

@ -0,0 +1,21 @@
#!/bin/bash -ex
# SPDX-FileCopyrightText: 2024 yuzu Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
export NDK_CCACHE="$(which ccache)"
ccache -s
export ANDROID_KEYSTORE_FILE="${GITHUB_WORKSPACE}/ks.jks"
base64 --decode <<< "${EA_PLAY_ANDROID_KEYSTORE_B64}" > "${ANDROID_KEYSTORE_FILE}"
export ANDROID_KEY_ALIAS="${PLAY_ANDROID_KEY_ALIAS}"
export ANDROID_KEYSTORE_PASS="${PLAY_ANDROID_KEYSTORE_PASS}"
export SERVICE_ACCOUNT_KEY_PATH="${GITHUB_WORKSPACE}/sa.json"
base64 --decode <<< "${EA_SERVICE_ACCOUNT_KEY_B64}" > "${SERVICE_ACCOUNT_KEY_PATH}"
./gradlew "publishEaReleaseBundle"
ccache -s
if [ ! -z "${ANDROID_KEYSTORE_B64}" ]; then
rm "${ANDROID_KEYSTORE_FILE}"
fi

View File

@ -0,0 +1,21 @@
#!/bin/bash -ex
# SPDX-FileCopyrightText: 2024 yuzu Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
export NDK_CCACHE="$(which ccache)"
ccache -s
export ANDROID_KEYSTORE_FILE="${GITHUB_WORKSPACE}/ks.jks"
base64 --decode <<< "${MAINLINE_PLAY_ANDROID_KEYSTORE_B64}" > "${ANDROID_KEYSTORE_FILE}"
export ANDROID_KEY_ALIAS="${PLAY_ANDROID_KEY_ALIAS}"
export ANDROID_KEYSTORE_PASS="${PLAY_ANDROID_KEYSTORE_PASS}"
export SERVICE_ACCOUNT_KEY_PATH="${GITHUB_WORKSPACE}/sa.json"
base64 --decode <<< "${MAINLINE_SERVICE_ACCOUNT_KEY_B64}" > "${SERVICE_ACCOUNT_KEY_PATH}"
./gradlew "publishMainlineReleaseBundle"
ccache -s
if [ ! -z "${ANDROID_KEYSTORE_B64}" ]; then
rm "${ANDROID_KEYSTORE_FILE}"
fi

View File

@ -0,0 +1,66 @@
# SPDX-FileCopyrightText: 2024 yuzu Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
name: yuzu-android-ea-play-release
on:
workflow_dispatch:
inputs:
release-track:
description: 'Play store release track (internal/alpha/beta/production)'
required: true
default: 'alpha'
jobs:
android:
runs-on: ubuntu-latest
if: ${{ github.repository == 'yuzu-emu/yuzu' }}
steps:
- uses: actions/checkout@v3
name: Checkout
with:
fetch-depth: 0
submodules: true
token: ${{ secrets.ALT_GITHUB_TOKEN }}
- run: npm install execa@5
- uses: actions/github-script@v5
name: 'Merge and publish Android EA changes'
env:
ALT_GITHUB_TOKEN: ${{ secrets.ALT_GITHUB_TOKEN }}
BUILD_EA: true
with:
script: |
const execa = require("execa");
const mergebot = require('./.github/workflows/android-merge.js').mergebot;
process.chdir('${{ github.workspace }}');
mergebot(github, context, execa);
- name: Get tag name
run: echo "GIT_TAG_NAME=$(cat tag-name.txt)" >> $GITHUB_ENV
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y ccache apksigner glslang-dev glslang-tools
- name: Build
run: ./.ci/scripts/android/eabuild.sh
env:
EA_PLAY_ANDROID_KEYSTORE_B64: ${{ secrets.PLAY_ANDROID_KEYSTORE_B64 }}
PLAY_ANDROID_KEY_ALIAS: ${{ secrets.PLAY_ANDROID_KEY_ALIAS }}
PLAY_ANDROID_KEYSTORE_PASS: ${{ secrets.PLAY_ANDROID_KEYSTORE_PASS }}
EA_SERVICE_ACCOUNT_KEY_B64: ${{ secrets.EA_SERVICE_ACCOUNT_KEY_B64 }}
STORE_TRACK: ${{ github.event.inputs.release-track }}
AUTO_VERSIONED: true
BUILD_EA: true
- name: Create release
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ env.EA_TAG_NAME }}
name: ${{ env.EA_TAG_NAME }}
draft: false
prerelease: false
repository: yuzu/yuzu-android
token: ${{ secrets.ALT_GITHUB_TOKEN }}

View File

@ -0,0 +1,59 @@
# SPDX-FileCopyrightText: 2024 yuzu Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
name: yuzu-android-mainline-play-release
on:
workflow_dispatch:
inputs:
release-tag:
description: 'Tag # from yuzu-android that you want to build and publish'
required: true
default: '200'
release-track:
description: 'Play store release track (internal/alpha/beta/production)'
required: true
default: 'alpha'
jobs:
android:
runs-on: ubuntu-latest
if: ${{ github.repository == 'yuzu-emu/yuzu' }}
steps:
- uses: actions/checkout@v3
name: Checkout
with:
fetch-depth: 0
submodules: true
token: ${{ secrets.ALT_GITHUB_TOKEN }}
- run: npm install execa@5
- uses: actions/github-script@v5
name: 'Pull mainline tag'
env:
MAINLINE_TAG: ${{ github.event.inputs.release-tag }}
with:
script: |
const execa = require("execa");
const mergebot = require('./.github/workflows/android-merge.js').getMainlineTag;
process.chdir('${{ github.workspace }}');
mergebot(execa);
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y ccache apksigner glslang-dev glslang-tools
- name: Build
run: |
echo "GIT_TAG_NAME=android-${{ github.event.inputs.releast-tag }}" >> $GITHUB_ENV
./.ci/scripts/android/mainlinebuild.sh
env:
MAINLINE_PLAY_ANDROID_KEYSTORE_B64: ${{ secrets.PLAY_ANDROID_KEYSTORE_B64 }}
PLAY_ANDROID_KEY_ALIAS: ${{ secrets.PLAY_ANDROID_KEY_ALIAS }}
PLAY_ANDROID_KEYSTORE_PASS: ${{ secrets.PLAY_ANDROID_KEYSTORE_PASS }}
SERVICE_ACCOUNT_KEY_B64: ${{ secrets.MAINLINE_SERVICE_ACCOUNT_KEY_B64 }}
STORE_TRACK: ${{ github.event.inputs.release-track }}
AUTO_VERSIONED: true

View File

@ -6,9 +6,12 @@
const fs = require("fs"); const fs = require("fs");
// which label to check for changes // which label to check for changes
const CHANGE_LABEL = 'android-merge'; const CHANGE_LABEL_MAINLINE = 'android-merge';
const CHANGE_LABEL_EA = 'android-ea-merge';
// how far back in time should we consider the changes are "recent"? (default: 24 hours) // how far back in time should we consider the changes are "recent"? (default: 24 hours)
const DETECTION_TIME_FRAME = (parseInt(process.env.DETECTION_TIME_FRAME)) || (24 * 3600 * 1000); const DETECTION_TIME_FRAME = (parseInt(process.env.DETECTION_TIME_FRAME)) || (24 * 3600 * 1000);
const BUILD_EA = process.env.BUILD_EA == 'true';
const MAINLINE_TAG = process.env.MAINLINE_TAG;
async function checkBaseChanges(github) { async function checkBaseChanges(github) {
// query the commit date of the latest commit on this branch // query the commit date of the latest commit on this branch
@ -40,20 +43,7 @@ async function checkBaseChanges(github) {
async function checkAndroidChanges(github) { async function checkAndroidChanges(github) {
if (checkBaseChanges(github)) return true; if (checkBaseChanges(github)) return true;
const query = `query($owner:String!, $name:String!, $label:String!) { const pulls = getPulls(github, false);
repository(name:$name, owner:$owner) {
pullRequests(labels: [$label], states: OPEN, first: 100) {
nodes { number headRepository { pushedAt } }
}
}
}`;
const variables = {
owner: 'yuzu-emu',
name: 'yuzu',
label: CHANGE_LABEL,
};
const result = await github.graphql(query, variables);
const pulls = result.repository.pullRequests.nodes;
for (let i = 0; i < pulls.length; i++) { for (let i = 0; i < pulls.length; i++) {
let pull = pulls[i]; let pull = pulls[i];
if (new Date() - new Date(pull.headRepository.pushedAt) <= DETECTION_TIME_FRAME) { if (new Date() - new Date(pull.headRepository.pushedAt) <= DETECTION_TIME_FRAME) {
@ -83,7 +73,13 @@ async function tagAndPush(github, owner, repo, execa, commit=false) {
}; };
const tags = await github.graphql(query, variables); const tags = await github.graphql(query, variables);
const tagList = tags.repository.refs.nodes; const tagList = tags.repository.refs.nodes;
const lastTag = tagList[0] ? tagList[0].name : 'dummy-0'; let lastTag = 'android-1';
for (let i = 0; i < tagList.length; ++i) {
if (tagList[i].name.includes('android-')) {
lastTag = tagList[i].name;
break;
}
}
const tagNumber = /\w+-(\d+)/.exec(lastTag)[1] | 0; const tagNumber = /\w+-(\d+)/.exec(lastTag)[1] | 0;
const channel = repo.split('-')[1]; const channel = repo.split('-')[1];
const newTag = `${channel}-${tagNumber + 1}`; const newTag = `${channel}-${tagNumber + 1}`;
@ -101,6 +97,48 @@ async function tagAndPush(github, owner, repo, execa, commit=false) {
console.info('Successfully pushed new changes.'); console.info('Successfully pushed new changes.');
} }
async function tagAndPushEA(github, owner, repo, execa) {
let altToken = process.env.ALT_GITHUB_TOKEN;
if (!altToken) {
throw `Please set ALT_GITHUB_TOKEN environment variable. This token should have write access to ${owner}/${repo}.`;
}
const query = `query ($owner:String!, $name:String!) {
repository(name:$name, owner:$owner) {
refs(refPrefix: "refs/tags/", orderBy: {field: TAG_COMMIT_DATE, direction: DESC}, first: 10) {
nodes { name }
}
}
}`;
const variables = {
owner: owner,
name: repo,
};
const tags = await github.graphql(query, variables);
const tagList = tags.repository.refs.nodes;
let lastTag = 'ea-1';
for (let i = 0; i < tagList.length; ++i) {
if (tagList[i].name.includes('ea-')) {
lastTag = tagList[i].name;
break;
}
}
const tagNumber = /\w+-(\d+)/.exec(lastTag)[1] | 0;
const newTag = `ea-${tagNumber + 1}`;
console.log(`New tag: ${newTag}`);
console.info('Pushing tags to GitHub ...');
await execa("git", ["remote", "add", "android", "https://github.com/yuzu-emu/yuzu-android.git"]);
await execa("git", ["fetch", "android"]);
await execa("git", ['tag', newTag]);
await execa("git", ['push', 'android', `${newTag}`]);
fs.writeFile('tag-name.txt', newTag, (err) => {
if (err) throw 'Could not write tag name to file!'
})
console.info('Successfully pushed new changes.');
}
async function generateReadme(pulls, context, mergeResults, execa) { async function generateReadme(pulls, context, mergeResults, execa) {
let baseUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/`; let baseUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/`;
let output = let output =
@ -202,10 +240,7 @@ async function resetBranch(execa) {
} }
} }
async function mergebot(github, context, execa) { async function getPulls(github) {
// Reset our local copy of master to what appears on yuzu-emu/yuzu - master
await resetBranch(execa);
const query = `query ($owner:String!, $name:String!, $label:String!) { const query = `query ($owner:String!, $name:String!, $label:String!) {
repository(name:$name, owner:$owner) { repository(name:$name, owner:$owner) {
pullRequests(labels: [$label], states: OPEN, first: 100) { pullRequests(labels: [$label], states: OPEN, first: 100) {
@ -215,13 +250,49 @@ async function mergebot(github, context, execa) {
} }
} }
}`; }`;
const variables = { const mainlineVariables = {
owner: 'yuzu-emu', owner: 'yuzu-emu',
name: 'yuzu', name: 'yuzu',
label: CHANGE_LABEL, label: CHANGE_LABEL_MAINLINE,
}; };
const result = await github.graphql(query, variables); const mainlineResult = await github.graphql(query, mainlineVariables);
const pulls = result.repository.pullRequests.nodes; const pulls = mainlineResult.repository.pullRequests.nodes;
if (BUILD_EA) {
const eaVariables = {
owner: 'yuzu-emu',
name: 'yuzu',
label: CHANGE_LABEL_EA,
};
const eaResult = await github.graphql(query, eaVariables);
const eaPulls = eaResult.repository.pullRequests.nodes;
return pulls.concat(eaPulls);
}
return pulls;
}
async function getMainlineTag(execa) {
console.log(`::group::Getting mainline tag android-${MAINLINE_TAG}`);
let hasFailed = false;
try {
await execa("git", ["remote", "add", "mainline", "https://github.com/yuzu-emu/yuzu-android.git"]);
await execa("git", ["fetch", "mainline", "--tags"]);
await execa("git", ["checkout", `tags/android-${MAINLINE_TAG}`]);
await execa("git", ["submodule", "update", "--init", "--recursive"]);
} catch (err) {
console.log('::error title=Failed pull tag');
hasFailed = true;
}
console.log("::endgroup::");
if (hasFailed) {
throw 'Failed pull mainline tag. Aborting!';
}
}
async function mergebot(github, context, execa) {
// Reset our local copy of master to what appears on yuzu-emu/yuzu - master
await resetBranch(execa);
const pulls = await getPulls(github);
let displayList = []; let displayList = [];
for (let i = 0; i < pulls.length; i++) { for (let i = 0; i < pulls.length; i++) {
let pull = pulls[i]; let pull = pulls[i];
@ -231,11 +302,17 @@ async function mergebot(github, context, execa) {
console.table(displayList); console.table(displayList);
await fetchPullRequests(pulls, "https://github.com/yuzu-emu/yuzu", execa); await fetchPullRequests(pulls, "https://github.com/yuzu-emu/yuzu", execa);
const mergeResults = await mergePullRequests(pulls, execa); const mergeResults = await mergePullRequests(pulls, execa);
await generateReadme(pulls, context, mergeResults, execa);
await tagAndPush(github, 'yuzu-emu', `yuzu-android`, execa, true); if (BUILD_EA) {
await tagAndPushEA(github, 'yuzu-emu', `yuzu-android`, execa);
} else {
await generateReadme(pulls, context, mergeResults, execa);
await tagAndPush(github, 'yuzu-emu', `yuzu-android`, execa, true);
}
} }
module.exports.mergebot = mergebot; module.exports.mergebot = mergebot;
module.exports.checkAndroidChanges = checkAndroidChanges; module.exports.checkAndroidChanges = checkAndroidChanges;
module.exports.tagAndPush = tagAndPush; module.exports.tagAndPush = tagAndPush;
module.exports.checkBaseChanges = checkBaseChanges; module.exports.checkBaseChanges = checkBaseChanges;
module.exports.getMainlineTag = getMainlineTag;

View File

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2023 yuzu Emulator Project # SPDX-FileCopyrightText: 2024 yuzu Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later # SPDX-License-Identifier: GPL-2.0-or-later
name: yuzu-android-publish name: yuzu-android-publish
@ -16,7 +16,7 @@ on:
jobs: jobs:
android: android:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: ${{ github.event.inputs.android != 'false' && github.repository == 'yuzu-emu/yuzu-android' }} if: ${{ github.event.inputs.android != 'false' && github.repository == 'yuzu-emu/yuzu' }}
steps: steps:
# this checkout is required to make sure the GitHub Actions scripts are available # this checkout is required to make sure the GitHub Actions scripts are available
- uses: actions/checkout@v3 - uses: actions/checkout@v3

View File

@ -1,3 +1,17 @@
| Pull Request | Commit | Title | Author | Merged? |
|----|----|----|----|----|
| [10529](https://github.com/yuzu-emu/yuzu//pull/10529) | [`368bf2211`](https://github.com/yuzu-emu/yuzu//pull/10529/files) | caches: make critical reclamation less eager and possible in more cases | [liamwhite](https://github.com/liamwhite/) | Yes |
| [12461](https://github.com/yuzu-emu/yuzu//pull/12461) | [`acc26667b`](https://github.com/yuzu-emu/yuzu//pull/12461/files) | Rework Nvdec and VIC to fix out-of-order videos, and speed up decoding. | [Kelebek1](https://github.com/Kelebek1/) | Yes |
| [12749](https://github.com/yuzu-emu/yuzu//pull/12749) | [`aad4b0d6f`](https://github.com/yuzu-emu/yuzu//pull/12749/files) | general: workarounds for SMMU syncing issues | [liamwhite](https://github.com/liamwhite/) | Yes |
| [12993](https://github.com/yuzu-emu/yuzu//pull/12993) | [`bca698a17`](https://github.com/yuzu-emu/yuzu//pull/12993/files) | am: rewrite part 1 | [liamwhite](https://github.com/liamwhite/) | Yes |
| [12996](https://github.com/yuzu-emu/yuzu//pull/12996) | [`75bfbadb2`](https://github.com/yuzu-emu/yuzu//pull/12996/files) | service: set: Migrate ISettingsServer to new interface | [german77](https://github.com/german77/) | Yes |
| [13000](https://github.com/yuzu-emu/yuzu//pull/13000) | [`461eaca7e`](https://github.com/yuzu-emu/yuzu//pull/13000/files) | device_memory_manager: skip unregistered interfaces on invalidate | [liamwhite](https://github.com/liamwhite/) | Yes |
End of merge log. You can find the original README.md below the break.
-----
<!-- <!--
SPDX-FileCopyrightText: 2018 yuzu Emulator Project SPDX-FileCopyrightText: 2018 yuzu Emulator Project
SPDX-License-Identifier: GPL-2.0-or-later SPDX-License-Identifier: GPL-2.0-or-later

View File

@ -314,3 +314,10 @@ endif()
if (NOT TARGET SimpleIni::SimpleIni) if (NOT TARGET SimpleIni::SimpleIni)
add_subdirectory(simpleini) add_subdirectory(simpleini)
endif() endif()
# sse2neon
if (ARCHITECTURE_arm64 AND NOT TARGET sse2neon)
add_library(sse2neon INTERFACE)
target_include_directories(sse2neon INTERFACE sse2neon)
endif()

9282
externals/sse2neon/sse2neon.h vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -164,6 +164,7 @@ else()
if (MINGW) if (MINGW)
add_definitions(-DMINGW_HAS_SECURE_API) add_definitions(-DMINGW_HAS_SECURE_API)
add_compile_options("-msse4.1")
if (MINGW_STATIC_BUILD) if (MINGW_STATIC_BUILD)
add_definitions(-DQT_STATICPLUGIN) add_definitions(-DQT_STATICPLUGIN)

View File

@ -3,8 +3,8 @@
import android.annotation.SuppressLint import android.annotation.SuppressLint
import kotlin.collections.setOf import kotlin.collections.setOf
import org.jetbrains.kotlin.konan.properties.Properties
import org.jlleitschuh.gradle.ktlint.reporter.ReporterType import org.jlleitschuh.gradle.ktlint.reporter.ReporterType
import com.github.triplet.gradle.androidpublisher.ReleaseStatus
plugins { plugins {
id("com.android.application") id("com.android.application")
@ -13,6 +13,7 @@ plugins {
kotlin("plugin.serialization") version "1.9.20" kotlin("plugin.serialization") version "1.9.20"
id("androidx.navigation.safeargs.kotlin") id("androidx.navigation.safeargs.kotlin")
id("org.jlleitschuh.gradle.ktlint") version "11.4.0" id("org.jlleitschuh.gradle.ktlint") version "11.4.0"
id("com.github.triplet.play") version "3.8.6"
} }
/** /**
@ -58,15 +59,7 @@ android {
targetSdk = 34 targetSdk = 34
versionName = getGitVersion() versionName = getGitVersion()
// If you want to use autoVersion for the versionCode, create a property in local.properties versionCode = if (System.getenv("AUTO_VERSIONED") == "true") {
// named "autoVersioned" and set it to "true"
val properties = Properties()
val versionProperty = try {
properties.load(project.rootProject.file("local.properties").inputStream())
properties.getProperty("autoVersioned") ?: ""
} catch (e: Exception) { "" }
versionCode = if (versionProperty == "true") {
autoVersion autoVersion
} else { } else {
1 1
@ -221,6 +214,15 @@ ktlint {
} }
} }
play {
val keyPath = System.getenv("SERVICE_ACCOUNT_KEY_PATH")
if (keyPath != null) {
serviceAccountCredentials.set(File(keyPath))
}
track.set(System.getenv("STORE_TRACK") ?: "internal")
releaseStatus.set(ReleaseStatus.COMPLETED)
}
dependencies { dependencies {
implementation("androidx.core:core-ktx:1.12.0") implementation("androidx.core:core-ktx:1.12.0")
implementation("androidx.appcompat:appcompat:1.6.1") implementation("androidx.appcompat:appcompat:1.6.1")
@ -257,12 +259,18 @@ fun runGitCommand(command: List<String>): String {
} }
fun getGitVersion(): String { fun getGitVersion(): String {
val gitVersion = runGitCommand(
listOf(
"git",
"describe",
"--always",
"--long"
)
).replace(Regex("(-0)?-[^-]+$"), "")
val versionName = if (System.getenv("GITHUB_ACTIONS") != null) { val versionName = if (System.getenv("GITHUB_ACTIONS") != null) {
val gitTag = System.getenv("GIT_TAG_NAME") ?: "" System.getenv("GIT_TAG_NAME") ?: gitVersion
gitTag
} else { } else {
runGitCommand(listOf("git", "describe", "--always", "--long")) gitVersion
.replace(Regex("(-0)?-[^-]+$"), "")
} }
return versionName.ifEmpty { "0.0" } return versionName.ifEmpty { "0.0" }
} }

View File

@ -12,8 +12,6 @@ SPDX-License-Identifier: GPL-3.0-or-later
<uses-feature android:name="android.hardware.vulkan.version" android:version="0x401000" android:required="true" /> <uses-feature android:name="android.hardware.vulkan.version" android:version="0x401000" android:required="true" />
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
<uses-permission android:name="android.permission.NFC" /> <uses-permission android:name="android.permission.NFC" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
@ -80,10 +78,6 @@ SPDX-License-Identifier: GPL-3.0-or-later
android:resource="@xml/nfc_tech_filter" /> android:resource="@xml/nfc_tech_filter" />
</activity> </activity>
<service android:name="org.yuzu.yuzu_emu.utils.ForegroundService" android:foregroundServiceType="specialUse">
<property android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE" android:value="Keep emulation running in background"/>
</service>
<provider <provider
android:name=".features.DocumentProvider" android:name=".features.DocumentProvider"
android:authorities="${applicationId}.user" android:authorities="${applicationId}.user"

View File

@ -17,17 +17,6 @@ fun Context.getPublicFilesDir(): File = getExternalFilesDir(null) ?: filesDir
class YuzuApplication : Application() { class YuzuApplication : Application() {
private fun createNotificationChannels() { private fun createNotificationChannels() {
val emulationChannel = NotificationChannel(
getString(R.string.emulation_notification_channel_id),
getString(R.string.emulation_notification_channel_name),
NotificationManager.IMPORTANCE_LOW
)
emulationChannel.description = getString(
R.string.emulation_notification_channel_description
)
emulationChannel.setSound(null, null)
emulationChannel.vibrationPattern = null
val noticeChannel = NotificationChannel( val noticeChannel = NotificationChannel(
getString(R.string.notice_notification_channel_id), getString(R.string.notice_notification_channel_id),
getString(R.string.notice_notification_channel_name), getString(R.string.notice_notification_channel_name),
@ -39,7 +28,6 @@ class YuzuApplication : Application() {
// Register the channel with the system; you can't change the importance // Register the channel with the system; you can't change the importance
// or other notification behaviors after this // or other notification behaviors after this
val notificationManager = getSystemService(NotificationManager::class.java) val notificationManager = getSystemService(NotificationManager::class.java)
notificationManager.createNotificationChannel(emulationChannel)
notificationManager.createNotificationChannel(noticeChannel) notificationManager.createNotificationChannel(noticeChannel)
} }

View File

@ -4,7 +4,6 @@
package org.yuzu.yuzu_emu.activities package org.yuzu.yuzu_emu.activities
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.Activity
import android.app.PendingIntent import android.app.PendingIntent
import android.app.PictureInPictureParams import android.app.PictureInPictureParams
import android.app.RemoteAction import android.app.RemoteAction
@ -45,7 +44,6 @@ import org.yuzu.yuzu_emu.features.settings.model.IntSetting
import org.yuzu.yuzu_emu.features.settings.model.Settings import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.model.EmulationViewModel import org.yuzu.yuzu_emu.model.EmulationViewModel
import org.yuzu.yuzu_emu.model.Game import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.utils.ForegroundService
import org.yuzu.yuzu_emu.utils.InputHandler import org.yuzu.yuzu_emu.utils.InputHandler
import org.yuzu.yuzu_emu.utils.Log import org.yuzu.yuzu_emu.utils.Log
import org.yuzu.yuzu_emu.utils.MemoryUtil import org.yuzu.yuzu_emu.utils.MemoryUtil
@ -74,11 +72,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
private val emulationViewModel: EmulationViewModel by viewModels() private val emulationViewModel: EmulationViewModel by viewModels()
override fun onDestroy() {
stopForegroundService(this)
super.onDestroy()
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
Log.gameLaunched = true Log.gameLaunched = true
ThemeHelper.setTheme(this) ThemeHelper.setTheme(this)
@ -125,10 +118,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
.apply() .apply()
} }
} }
// Start a foreground service to prevent the app from getting killed in the background
val startIntent = Intent(this, ForegroundService::class.java)
startForegroundService(startIntent)
} }
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean { override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
@ -481,12 +470,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
activity.startActivity(launcher) activity.startActivity(launcher)
} }
fun stopForegroundService(activity: Activity) {
val startIntent = Intent(activity, ForegroundService::class.java)
startIntent.action = ForegroundService.ACTION_STOP
activity.startForegroundService(startIntent)
}
private fun areCoordinatesOutside(view: View?, x: Float, y: Float): Boolean { private fun areCoordinatesOutside(view: View?, x: Float, y: Float): Boolean {
if (view == null) { if (view == null) {
return true return true

View File

@ -25,7 +25,8 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
HAPTIC_FEEDBACK("haptic_feedback"), HAPTIC_FEEDBACK("haptic_feedback"),
SHOW_PERFORMANCE_OVERLAY("show_performance_overlay"), SHOW_PERFORMANCE_OVERLAY("show_performance_overlay"),
SHOW_INPUT_OVERLAY("show_input_overlay"), SHOW_INPUT_OVERLAY("show_input_overlay"),
TOUCHSCREEN("touchscreen"); TOUCHSCREEN("touchscreen"),
SHOW_THERMAL_OVERLAY("show_thermal_overlay");
override fun getBoolean(needsGlobal: Boolean): Boolean = override fun getBoolean(needsGlobal: Boolean): Boolean =
NativeConfig.getBoolean(key, needsGlobal) NativeConfig.getBoolean(key, needsGlobal)

View File

@ -24,7 +24,9 @@ enum class IntSetting(override val key: String) : AbstractIntSetting {
THEME_MODE("theme_mode"), THEME_MODE("theme_mode"),
OVERLAY_SCALE("control_scale"), OVERLAY_SCALE("control_scale"),
OVERLAY_OPACITY("control_opacity"), OVERLAY_OPACITY("control_opacity"),
LOCK_DRAWER("lock_drawer"); LOCK_DRAWER("lock_drawer"),
VERTICAL_ALIGNMENT("vertical_alignment"),
FSR_SHARPENING_SLIDER("fsr_sharpening_slider");
override fun getInt(needsGlobal: Boolean): Int = NativeConfig.getInt(key, needsGlobal) override fun getInt(needsGlobal: Boolean): Int = NativeConfig.getInt(key, needsGlobal)

View File

@ -93,4 +93,15 @@ object Settings {
entries.firstOrNull { it.int == int } ?: Unspecified entries.firstOrNull { it.int == int } ?: Unspecified
} }
} }
enum class EmulationVerticalAlignment(val int: Int) {
Top(1),
Center(0),
Bottom(2);
companion object {
fun from(int: Int): EmulationVerticalAlignment =
entries.firstOrNull { it.int == int } ?: Center
}
}
} }

View File

@ -188,6 +188,16 @@ abstract class SettingsItem(
R.array.rendererScalingFilterValues R.array.rendererScalingFilterValues
) )
) )
put(
SliderSetting(
IntSetting.FSR_SHARPENING_SLIDER,
R.string.fsr_sharpness,
R.string.fsr_sharpness_description,
0,
100,
"%"
)
)
put( put(
SingleChoiceSetting( SingleChoiceSetting(
IntSetting.RENDERER_ANTI_ALIASING, IntSetting.RENDERER_ANTI_ALIASING,
@ -215,6 +225,15 @@ abstract class SettingsItem(
R.array.rendererAspectRatioValues R.array.rendererAspectRatioValues
) )
) )
put(
SingleChoiceSetting(
IntSetting.VERTICAL_ALIGNMENT,
R.string.vertical_alignment,
0,
R.array.verticalAlignmentEntries,
R.array.verticalAlignmentValues
)
)
put( put(
SwitchSetting( SwitchSetting(
BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE, BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE,

View File

@ -8,7 +8,6 @@ import android.os.Bundle
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.view.ViewGroup.MarginLayoutParams
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding import androidx.core.view.updatePadding
@ -27,6 +26,7 @@ import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.FragmentSettingsBinding import org.yuzu.yuzu_emu.databinding.FragmentSettingsBinding
import org.yuzu.yuzu_emu.features.settings.model.Settings import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.model.SettingsViewModel import org.yuzu.yuzu_emu.model.SettingsViewModel
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
class SettingsFragment : Fragment() { class SettingsFragment : Fragment() {
private lateinit var presenter: SettingsFragmentPresenter private lateinit var presenter: SettingsFragmentPresenter
@ -125,18 +125,10 @@ class SettingsFragment : Fragment() {
val leftInsets = barInsets.left + cutoutInsets.left val leftInsets = barInsets.left + cutoutInsets.left
val rightInsets = barInsets.right + cutoutInsets.right val rightInsets = barInsets.right + cutoutInsets.right
val mlpSettingsList = binding.listSettings.layoutParams as MarginLayoutParams binding.listSettings.updateMargins(left = leftInsets, right = rightInsets)
mlpSettingsList.leftMargin = leftInsets binding.listSettings.updatePadding(bottom = barInsets.bottom)
mlpSettingsList.rightMargin = rightInsets
binding.listSettings.layoutParams = mlpSettingsList
binding.listSettings.updatePadding(
bottom = barInsets.bottom
)
val mlpAppBar = binding.appbarSettings.layoutParams as MarginLayoutParams binding.appbarSettings.updateMargins(left = leftInsets, right = rightInsets)
mlpAppBar.leftMargin = leftInsets
mlpAppBar.rightMargin = rightInsets
binding.appbarSettings.layoutParams = mlpAppBar
windowInsets windowInsets
} }
} }

View File

@ -143,10 +143,12 @@ class SettingsFragmentPresenter(
add(IntSetting.RENDERER_RESOLUTION.key) add(IntSetting.RENDERER_RESOLUTION.key)
add(IntSetting.RENDERER_VSYNC.key) add(IntSetting.RENDERER_VSYNC.key)
add(IntSetting.RENDERER_SCALING_FILTER.key) add(IntSetting.RENDERER_SCALING_FILTER.key)
add(IntSetting.FSR_SHARPENING_SLIDER.key)
add(IntSetting.RENDERER_ANTI_ALIASING.key) add(IntSetting.RENDERER_ANTI_ALIASING.key)
add(IntSetting.MAX_ANISOTROPY.key) add(IntSetting.MAX_ANISOTROPY.key)
add(IntSetting.RENDERER_SCREEN_LAYOUT.key) add(IntSetting.RENDERER_SCREEN_LAYOUT.key)
add(IntSetting.RENDERER_ASPECT_RATIO.key) add(IntSetting.RENDERER_ASPECT_RATIO.key)
add(IntSetting.VERTICAL_ALIGNMENT.key)
add(BooleanSetting.PICTURE_IN_PICTURE.key) add(BooleanSetting.PICTURE_IN_PICTURE.key)
add(BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE.key) add(BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE.key)
add(BooleanSetting.RENDERER_FORCE_MAX_CLOCK.key) add(BooleanSetting.RENDERER_FORCE_MAX_CLOCK.key)

View File

@ -13,7 +13,6 @@ import android.os.Bundle
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.view.ViewGroup.MarginLayoutParams
import android.widget.Toast import android.widget.Toast
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsCompat
@ -26,6 +25,7 @@ import org.yuzu.yuzu_emu.BuildConfig
import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.FragmentAboutBinding import org.yuzu.yuzu_emu.databinding.FragmentAboutBinding
import org.yuzu.yuzu_emu.model.HomeViewModel import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
class AboutFragment : Fragment() { class AboutFragment : Fragment() {
private var _binding: FragmentAboutBinding? = null private var _binding: FragmentAboutBinding? = null
@ -114,15 +114,8 @@ class AboutFragment : Fragment() {
val leftInsets = barInsets.left + cutoutInsets.left val leftInsets = barInsets.left + cutoutInsets.left
val rightInsets = barInsets.right + cutoutInsets.right val rightInsets = barInsets.right + cutoutInsets.right
val mlpToolbar = binding.toolbarAbout.layoutParams as MarginLayoutParams binding.toolbarAbout.updateMargins(left = leftInsets, right = rightInsets)
mlpToolbar.leftMargin = leftInsets binding.scrollAbout.updateMargins(left = leftInsets, right = rightInsets)
mlpToolbar.rightMargin = rightInsets
binding.toolbarAbout.layoutParams = mlpToolbar
val mlpScrollAbout = binding.scrollAbout.layoutParams as MarginLayoutParams
mlpScrollAbout.leftMargin = leftInsets
mlpScrollAbout.rightMargin = rightInsets
binding.scrollAbout.layoutParams = mlpScrollAbout
binding.contentAbout.updatePadding(bottom = barInsets.bottom) binding.contentAbout.updatePadding(bottom = barInsets.bottom)

View File

@ -31,6 +31,7 @@ import org.yuzu.yuzu_emu.model.AddonViewModel
import org.yuzu.yuzu_emu.model.HomeViewModel import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.utils.AddonUtil import org.yuzu.yuzu_emu.utils.AddonUtil
import org.yuzu.yuzu_emu.utils.FileUtil.copyFilesTo import org.yuzu.yuzu_emu.utils.FileUtil.copyFilesTo
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
import java.io.File import java.io.File
class AddonsFragment : Fragment() { class AddonsFragment : Fragment() {
@ -103,7 +104,10 @@ class AddonsFragment : Fragment() {
requireActivity(), requireActivity(),
titleId = R.string.addon_notice, titleId = R.string.addon_notice,
descriptionId = R.string.addon_notice_description, descriptionId = R.string.addon_notice_description,
positiveAction = { addonViewModel.showModInstallPicker(true) } dismissible = false,
positiveAction = { addonViewModel.showModInstallPicker(true) },
negativeAction = {},
negativeButtonTitleId = R.string.close
).show(parentFragmentManager, MessageDialogFragment.TAG) ).show(parentFragmentManager, MessageDialogFragment.TAG)
addonViewModel.showModNoticeDialog(false) addonViewModel.showModNoticeDialog(false)
} }
@ -118,7 +122,8 @@ class AddonsFragment : Fragment() {
requireActivity(), requireActivity(),
titleId = R.string.confirm_uninstall, titleId = R.string.confirm_uninstall,
descriptionId = R.string.confirm_uninstall_description, descriptionId = R.string.confirm_uninstall_description,
positiveAction = { addonViewModel.onDeleteAddon(it) } positiveAction = { addonViewModel.onDeleteAddon(it) },
negativeAction = {}
).show(parentFragmentManager, MessageDialogFragment.TAG) ).show(parentFragmentManager, MessageDialogFragment.TAG)
addonViewModel.setAddonToDelete(null) addonViewModel.setAddonToDelete(null)
} }
@ -202,27 +207,19 @@ class AddonsFragment : Fragment() {
val leftInsets = barInsets.left + cutoutInsets.left val leftInsets = barInsets.left + cutoutInsets.left
val rightInsets = barInsets.right + cutoutInsets.right val rightInsets = barInsets.right + cutoutInsets.right
val mlpToolbar = binding.toolbarAddons.layoutParams as ViewGroup.MarginLayoutParams binding.toolbarAddons.updateMargins(left = leftInsets, right = rightInsets)
mlpToolbar.leftMargin = leftInsets binding.listAddons.updateMargins(left = leftInsets, right = rightInsets)
mlpToolbar.rightMargin = rightInsets
binding.toolbarAddons.layoutParams = mlpToolbar
val mlpAddonsList = binding.listAddons.layoutParams as ViewGroup.MarginLayoutParams
mlpAddonsList.leftMargin = leftInsets
mlpAddonsList.rightMargin = rightInsets
binding.listAddons.layoutParams = mlpAddonsList
binding.listAddons.updatePadding( binding.listAddons.updatePadding(
bottom = barInsets.bottom + bottom = barInsets.bottom +
resources.getDimensionPixelSize(R.dimen.spacing_bottom_list_fab) resources.getDimensionPixelSize(R.dimen.spacing_bottom_list_fab)
) )
val fabSpacing = resources.getDimensionPixelSize(R.dimen.spacing_fab) val fabSpacing = resources.getDimensionPixelSize(R.dimen.spacing_fab)
val mlpFab = binding.buttonInstall.updateMargins(
binding.buttonInstall.layoutParams as ViewGroup.MarginLayoutParams left = leftInsets + fabSpacing,
mlpFab.leftMargin = leftInsets + fabSpacing right = rightInsets + fabSpacing,
mlpFab.rightMargin = rightInsets + fabSpacing bottom = barInsets.bottom + fabSpacing
mlpFab.bottomMargin = barInsets.bottom + fabSpacing )
binding.buttonInstall.layoutParams = mlpFab
windowInsets windowInsets
} }

View File

@ -21,6 +21,7 @@ import org.yuzu.yuzu_emu.databinding.FragmentAppletLauncherBinding
import org.yuzu.yuzu_emu.model.Applet import org.yuzu.yuzu_emu.model.Applet
import org.yuzu.yuzu_emu.model.AppletInfo import org.yuzu.yuzu_emu.model.AppletInfo
import org.yuzu.yuzu_emu.model.HomeViewModel import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
class AppletLauncherFragment : Fragment() { class AppletLauncherFragment : Fragment() {
private var _binding: FragmentAppletLauncherBinding? = null private var _binding: FragmentAppletLauncherBinding? = null
@ -95,16 +96,8 @@ class AppletLauncherFragment : Fragment() {
val leftInsets = barInsets.left + cutoutInsets.left val leftInsets = barInsets.left + cutoutInsets.left
val rightInsets = barInsets.right + cutoutInsets.right val rightInsets = barInsets.right + cutoutInsets.right
val mlpAppBar = binding.toolbarApplets.layoutParams as ViewGroup.MarginLayoutParams binding.toolbarApplets.updateMargins(left = leftInsets, right = rightInsets)
mlpAppBar.leftMargin = leftInsets binding.listApplets.updateMargins(left = leftInsets, right = rightInsets)
mlpAppBar.rightMargin = rightInsets
binding.toolbarApplets.layoutParams = mlpAppBar
val mlpListApplets =
binding.listApplets.layoutParams as ViewGroup.MarginLayoutParams
mlpListApplets.leftMargin = leftInsets
mlpListApplets.rightMargin = rightInsets
binding.listApplets.layoutParams = mlpListApplets
binding.listApplets.updatePadding(bottom = barInsets.bottom) binding.listApplets.updatePadding(bottom = barInsets.bottom)

View File

@ -34,6 +34,7 @@ import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.utils.FileUtil import org.yuzu.yuzu_emu.utils.FileUtil
import org.yuzu.yuzu_emu.utils.GpuDriverHelper import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import org.yuzu.yuzu_emu.utils.NativeConfig import org.yuzu.yuzu_emu.utils.NativeConfig
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
@ -141,23 +142,15 @@ class DriverManagerFragment : Fragment() {
val leftInsets = barInsets.left + cutoutInsets.left val leftInsets = barInsets.left + cutoutInsets.left
val rightInsets = barInsets.right + cutoutInsets.right val rightInsets = barInsets.right + cutoutInsets.right
val mlpAppBar = binding.toolbarDrivers.layoutParams as ViewGroup.MarginLayoutParams binding.toolbarDrivers.updateMargins(left = leftInsets, right = rightInsets)
mlpAppBar.leftMargin = leftInsets binding.listDrivers.updateMargins(left = leftInsets, right = rightInsets)
mlpAppBar.rightMargin = rightInsets
binding.toolbarDrivers.layoutParams = mlpAppBar
val mlplistDrivers = binding.listDrivers.layoutParams as ViewGroup.MarginLayoutParams
mlplistDrivers.leftMargin = leftInsets
mlplistDrivers.rightMargin = rightInsets
binding.listDrivers.layoutParams = mlplistDrivers
val fabSpacing = resources.getDimensionPixelSize(R.dimen.spacing_fab) val fabSpacing = resources.getDimensionPixelSize(R.dimen.spacing_fab)
val mlpFab = binding.buttonInstall.updateMargins(
binding.buttonInstall.layoutParams as ViewGroup.MarginLayoutParams left = leftInsets + fabSpacing,
mlpFab.leftMargin = leftInsets + fabSpacing right = rightInsets + fabSpacing,
mlpFab.rightMargin = rightInsets + fabSpacing bottom = barInsets.bottom + fabSpacing
mlpFab.bottomMargin = barInsets.bottom + fabSpacing )
binding.buttonInstall.layoutParams = mlpFab
binding.listDrivers.updatePadding( binding.listDrivers.updatePadding(
bottom = barInsets.bottom + bottom = barInsets.bottom +

View File

@ -19,6 +19,7 @@ import com.google.android.material.transition.MaterialSharedAxis
import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.FragmentEarlyAccessBinding import org.yuzu.yuzu_emu.databinding.FragmentEarlyAccessBinding
import org.yuzu.yuzu_emu.model.HomeViewModel import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
class EarlyAccessFragment : Fragment() { class EarlyAccessFragment : Fragment() {
private var _binding: FragmentEarlyAccessBinding? = null private var _binding: FragmentEarlyAccessBinding? = null
@ -73,10 +74,7 @@ class EarlyAccessFragment : Fragment() {
val leftInsets = barInsets.left + cutoutInsets.left val leftInsets = barInsets.left + cutoutInsets.left
val rightInsets = barInsets.right + cutoutInsets.right val rightInsets = barInsets.right + cutoutInsets.right
val mlpAppBar = binding.appbarEa.layoutParams as ViewGroup.MarginLayoutParams binding.appbarEa.updateMargins(left = leftInsets, right = rightInsets)
mlpAppBar.leftMargin = leftInsets
mlpAppBar.rightMargin = rightInsets
binding.appbarEa.layoutParams = mlpAppBar
binding.scrollEa.updatePadding( binding.scrollEa.updatePadding(
left = leftInsets, left = leftInsets,

View File

@ -13,8 +13,11 @@ import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import android.os.PowerManager
import android.os.SystemClock import android.os.SystemClock
import android.util.Rational
import android.view.* import android.view.*
import android.widget.FrameLayout
import android.widget.TextView import android.widget.TextView
import android.widget.Toast import android.widget.Toast
import androidx.activity.OnBackPressedCallback import androidx.activity.OnBackPressedCallback
@ -23,6 +26,8 @@ import androidx.core.content.res.ResourcesCompat
import androidx.core.graphics.Insets import androidx.core.graphics.Insets
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updateLayoutParams
import androidx.core.view.updatePadding
import androidx.drawerlayout.widget.DrawerLayout import androidx.drawerlayout.widget.DrawerLayout
import androidx.drawerlayout.widget.DrawerLayout.DrawerListener import androidx.drawerlayout.widget.DrawerLayout.DrawerListener
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
@ -38,7 +43,6 @@ import androidx.window.layout.WindowLayoutInfo
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.slider.Slider import com.google.android.material.slider.Slider
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.HomeNavigationDirections import org.yuzu.yuzu_emu.HomeNavigationDirections
@ -51,6 +55,7 @@ import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
import org.yuzu.yuzu_emu.features.settings.model.IntSetting import org.yuzu.yuzu_emu.features.settings.model.IntSetting
import org.yuzu.yuzu_emu.features.settings.model.Settings import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.features.settings.model.Settings.EmulationOrientation import org.yuzu.yuzu_emu.features.settings.model.Settings.EmulationOrientation
import org.yuzu.yuzu_emu.features.settings.model.Settings.EmulationVerticalAlignment
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
import org.yuzu.yuzu_emu.model.DriverViewModel import org.yuzu.yuzu_emu.model.DriverViewModel
import org.yuzu.yuzu_emu.model.Game import org.yuzu.yuzu_emu.model.Game
@ -64,6 +69,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
private lateinit var emulationState: EmulationState private lateinit var emulationState: EmulationState
private var emulationActivity: EmulationActivity? = null private var emulationActivity: EmulationActivity? = null
private var perfStatsUpdater: (() -> Unit)? = null private var perfStatsUpdater: (() -> Unit)? = null
private var thermalStatsUpdater: (() -> Unit)? = null
private var _binding: FragmentEmulationBinding? = null private var _binding: FragmentEmulationBinding? = null
private val binding get() = _binding!! private val binding get() = _binding!!
@ -77,6 +83,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
private var isInFoldableLayout = false private var isInFoldableLayout = false
private lateinit var powerManager: PowerManager
override fun onAttach(context: Context) { override fun onAttach(context: Context) {
super.onAttach(context) super.onAttach(context)
if (context is EmulationActivity) { if (context is EmulationActivity) {
@ -102,6 +110,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
updateOrientation() updateOrientation()
powerManager = requireContext().getSystemService(Context.POWER_SERVICE) as PowerManager
val intentUri: Uri? = requireActivity().intent.data val intentUri: Uri? = requireActivity().intent.data
var intentGame: Game? = null var intentGame: Game? = null
if (intentUri != null) { if (intentUri != null) {
@ -394,8 +404,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
emulationState.updateSurface() emulationState.updateSurface()
// Setup overlay // Setup overlays
updateShowFpsOverlay() updateShowFpsOverlay()
updateThermalOverlay()
} }
} }
} }
@ -553,6 +564,38 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
} }
} }
private fun updateThermalOverlay() {
if (BooleanSetting.SHOW_THERMAL_OVERLAY.getBoolean()) {
thermalStatsUpdater = {
if (emulationViewModel.emulationStarted.value &&
!emulationViewModel.isEmulationStopping.value
) {
val thermalStatus = when (powerManager.currentThermalStatus) {
PowerManager.THERMAL_STATUS_LIGHT -> "😥"
PowerManager.THERMAL_STATUS_MODERATE -> "🥵"
PowerManager.THERMAL_STATUS_SEVERE -> "🔥"
PowerManager.THERMAL_STATUS_CRITICAL,
PowerManager.THERMAL_STATUS_EMERGENCY,
PowerManager.THERMAL_STATUS_SHUTDOWN -> "☢️"
else -> "🙂"
}
if (_binding != null) {
binding.showThermalsText.text = thermalStatus
}
thermalStatsUpdateHandler.postDelayed(thermalStatsUpdater!!, 1000)
}
}
thermalStatsUpdateHandler.post(thermalStatsUpdater!!)
binding.showThermalsText.visibility = View.VISIBLE
} else {
if (thermalStatsUpdater != null) {
thermalStatsUpdateHandler.removeCallbacks(thermalStatsUpdater!!)
}
binding.showThermalsText.visibility = View.GONE
}
}
@SuppressLint("SourceLockedOrientationActivity") @SuppressLint("SourceLockedOrientationActivity")
private fun updateOrientation() { private fun updateOrientation() {
emulationActivity?.let { emulationActivity?.let {
@ -578,7 +621,46 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
} }
private fun updateScreenLayout() { private fun updateScreenLayout() {
binding.surfaceEmulation.setAspectRatio(null) val verticalAlignment =
EmulationVerticalAlignment.from(IntSetting.VERTICAL_ALIGNMENT.getInt())
val aspectRatio = when (IntSetting.RENDERER_ASPECT_RATIO.getInt()) {
0 -> Rational(16, 9)
1 -> Rational(4, 3)
2 -> Rational(21, 9)
3 -> Rational(16, 10)
else -> null // Best fit
}
when (verticalAlignment) {
EmulationVerticalAlignment.Top -> {
binding.surfaceEmulation.setAspectRatio(aspectRatio)
val params = FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
params.gravity = Gravity.TOP or Gravity.CENTER_HORIZONTAL
binding.surfaceEmulation.layoutParams = params
}
EmulationVerticalAlignment.Center -> {
binding.surfaceEmulation.setAspectRatio(null)
binding.surfaceEmulation.updateLayoutParams {
width = ViewGroup.LayoutParams.MATCH_PARENT
height = ViewGroup.LayoutParams.MATCH_PARENT
}
}
EmulationVerticalAlignment.Bottom -> {
binding.surfaceEmulation.setAspectRatio(aspectRatio)
val params =
FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
params.gravity = Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL
binding.surfaceEmulation.layoutParams = params
}
}
emulationState.updateSurface()
emulationActivity?.buildPictureInPictureParams() emulationActivity?.buildPictureInPictureParams()
updateOrientation() updateOrientation()
} }
@ -641,6 +723,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
popup.menu.apply { popup.menu.apply {
findItem(R.id.menu_toggle_fps).isChecked = findItem(R.id.menu_toggle_fps).isChecked =
BooleanSetting.SHOW_PERFORMANCE_OVERLAY.getBoolean() BooleanSetting.SHOW_PERFORMANCE_OVERLAY.getBoolean()
findItem(R.id.thermal_indicator).isChecked =
BooleanSetting.SHOW_THERMAL_OVERLAY.getBoolean()
findItem(R.id.menu_rel_stick_center).isChecked = findItem(R.id.menu_rel_stick_center).isChecked =
BooleanSetting.JOYSTICK_REL_CENTER.getBoolean() BooleanSetting.JOYSTICK_REL_CENTER.getBoolean()
findItem(R.id.menu_dpad_slide).isChecked = BooleanSetting.DPAD_SLIDE.getBoolean() findItem(R.id.menu_dpad_slide).isChecked = BooleanSetting.DPAD_SLIDE.getBoolean()
@ -660,6 +744,13 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
true true
} }
R.id.thermal_indicator -> {
it.isChecked = !it.isChecked
BooleanSetting.SHOW_THERMAL_OVERLAY.setBoolean(it.isChecked)
updateThermalOverlay()
true
}
R.id.menu_edit_overlay -> { R.id.menu_edit_overlay -> {
binding.drawerLayout.close() binding.drawerLayout.close()
binding.surfaceInputOverlay.requestFocus() binding.surfaceInputOverlay.requestFocus()
@ -850,7 +941,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
right = cutInsets.right right = cutInsets.right
} }
v.setPadding(left, cutInsets.top, right, 0) v.updatePadding(left = left, top = cutInsets.top, right = right)
windowInsets windowInsets
} }
} }
@ -1003,5 +1094,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
companion object { companion object {
private val perfStatsUpdateHandler = Handler(Looper.myLooper()!!) private val perfStatsUpdateHandler = Handler(Looper.myLooper()!!)
private val thermalStatsUpdateHandler = Handler(Looper.myLooper()!!)
} }
} }

View File

@ -26,6 +26,7 @@ import org.yuzu.yuzu_emu.databinding.FragmentFoldersBinding
import org.yuzu.yuzu_emu.model.GamesViewModel import org.yuzu.yuzu_emu.model.GamesViewModel
import org.yuzu.yuzu_emu.model.HomeViewModel import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.ui.main.MainActivity import org.yuzu.yuzu_emu.ui.main.MainActivity
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
class GameFoldersFragment : Fragment() { class GameFoldersFragment : Fragment() {
private var _binding: FragmentFoldersBinding? = null private var _binding: FragmentFoldersBinding? = null
@ -100,23 +101,16 @@ class GameFoldersFragment : Fragment() {
val leftInsets = barInsets.left + cutoutInsets.left val leftInsets = barInsets.left + cutoutInsets.left
val rightInsets = barInsets.right + cutoutInsets.right val rightInsets = barInsets.right + cutoutInsets.right
val mlpToolbar = binding.toolbarFolders.layoutParams as ViewGroup.MarginLayoutParams binding.toolbarFolders.updateMargins(left = leftInsets, right = rightInsets)
mlpToolbar.leftMargin = leftInsets
mlpToolbar.rightMargin = rightInsets
binding.toolbarFolders.layoutParams = mlpToolbar
val fabSpacing = resources.getDimensionPixelSize(R.dimen.spacing_fab) val fabSpacing = resources.getDimensionPixelSize(R.dimen.spacing_fab)
val mlpFab = binding.buttonAdd.updateMargins(
binding.buttonAdd.layoutParams as ViewGroup.MarginLayoutParams left = leftInsets + fabSpacing,
mlpFab.leftMargin = leftInsets + fabSpacing right = rightInsets + fabSpacing,
mlpFab.rightMargin = rightInsets + fabSpacing bottom = barInsets.bottom + fabSpacing
mlpFab.bottomMargin = barInsets.bottom + fabSpacing )
binding.buttonAdd.layoutParams = mlpFab
val mlpListFolders = binding.listFolders.layoutParams as ViewGroup.MarginLayoutParams binding.listFolders.updateMargins(left = leftInsets, right = rightInsets)
mlpListFolders.leftMargin = leftInsets
mlpListFolders.rightMargin = rightInsets
binding.listFolders.layoutParams = mlpListFolders
binding.listFolders.updatePadding( binding.listFolders.updatePadding(
bottom = barInsets.bottom + bottom = barInsets.bottom +

View File

@ -27,6 +27,7 @@ import org.yuzu.yuzu_emu.databinding.FragmentGameInfoBinding
import org.yuzu.yuzu_emu.model.GameVerificationResult import org.yuzu.yuzu_emu.model.GameVerificationResult
import org.yuzu.yuzu_emu.model.HomeViewModel import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.utils.GameMetadata import org.yuzu.yuzu_emu.utils.GameMetadata
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
class GameInfoFragment : Fragment() { class GameInfoFragment : Fragment() {
private var _binding: FragmentGameInfoBinding? = null private var _binding: FragmentGameInfoBinding? = null
@ -122,11 +123,13 @@ class GameInfoFragment : Fragment() {
titleId = R.string.verify_success, titleId = R.string.verify_success,
descriptionId = R.string.operation_completed_successfully descriptionId = R.string.operation_completed_successfully
) )
GameVerificationResult.Failed -> GameVerificationResult.Failed ->
MessageDialogFragment.newInstance( MessageDialogFragment.newInstance(
titleId = R.string.verify_failure, titleId = R.string.verify_failure,
descriptionId = R.string.verify_failure_description descriptionId = R.string.verify_failure_description
) )
GameVerificationResult.NotImplemented -> GameVerificationResult.NotImplemented ->
MessageDialogFragment.newInstance( MessageDialogFragment.newInstance(
titleId = R.string.verify_no_result, titleId = R.string.verify_no_result,
@ -165,15 +168,8 @@ class GameInfoFragment : Fragment() {
val leftInsets = barInsets.left + cutoutInsets.left val leftInsets = barInsets.left + cutoutInsets.left
val rightInsets = barInsets.right + cutoutInsets.right val rightInsets = barInsets.right + cutoutInsets.right
val mlpToolbar = binding.toolbarInfo.layoutParams as ViewGroup.MarginLayoutParams binding.toolbarInfo.updateMargins(left = leftInsets, right = rightInsets)
mlpToolbar.leftMargin = leftInsets binding.scrollInfo.updateMargins(left = leftInsets, right = rightInsets)
mlpToolbar.rightMargin = rightInsets
binding.toolbarInfo.layoutParams = mlpToolbar
val mlpScrollAbout = binding.scrollInfo.layoutParams as ViewGroup.MarginLayoutParams
mlpScrollAbout.leftMargin = leftInsets
mlpScrollAbout.rightMargin = rightInsets
binding.scrollInfo.layoutParams = mlpScrollAbout
binding.contentInfo.updatePadding(bottom = barInsets.bottom) binding.contentInfo.updatePadding(bottom = barInsets.bottom)

View File

@ -46,6 +46,7 @@ import org.yuzu.yuzu_emu.utils.FileUtil
import org.yuzu.yuzu_emu.utils.GameIconUtils import org.yuzu.yuzu_emu.utils.GameIconUtils
import org.yuzu.yuzu_emu.utils.GpuDriverHelper import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import org.yuzu.yuzu_emu.utils.MemoryUtil import org.yuzu.yuzu_emu.utils.MemoryUtil
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
import java.io.BufferedOutputStream import java.io.BufferedOutputStream
import java.io.File import java.io.File
@ -242,7 +243,9 @@ class GamePropertiesFragment : Fragment() {
requireActivity(), requireActivity(),
titleId = R.string.delete_save_data, titleId = R.string.delete_save_data,
descriptionId = R.string.delete_save_data_warning_description, descriptionId = R.string.delete_save_data_warning_description,
positiveAction = { positiveButtonTitleId = android.R.string.cancel,
negativeButtonTitleId = android.R.string.ok,
negativeAction = {
File(args.game.saveDir).deleteRecursively() File(args.game.saveDir).deleteRecursively()
Toast.makeText( Toast.makeText(
YuzuApplication.appContext, YuzuApplication.appContext,
@ -320,46 +323,25 @@ class GamePropertiesFragment : Fragment() {
val smallLayout = resources.getBoolean(R.bool.small_layout) val smallLayout = resources.getBoolean(R.bool.small_layout)
if (smallLayout) { if (smallLayout) {
val mlpListAll = binding.listAll.updateMargins(left = leftInsets, right = rightInsets)
binding.listAll.layoutParams as ViewGroup.MarginLayoutParams
mlpListAll.leftMargin = leftInsets
mlpListAll.rightMargin = rightInsets
binding.listAll.layoutParams = mlpListAll
} else { } else {
if (ViewCompat.getLayoutDirection(binding.root) == if (ViewCompat.getLayoutDirection(binding.root) ==
ViewCompat.LAYOUT_DIRECTION_LTR ViewCompat.LAYOUT_DIRECTION_LTR
) { ) {
val mlpListAll = binding.listAll.updateMargins(right = rightInsets)
binding.listAll.layoutParams as ViewGroup.MarginLayoutParams binding.iconLayout!!.updateMargins(top = barInsets.top, left = leftInsets)
mlpListAll.rightMargin = rightInsets
binding.listAll.layoutParams = mlpListAll
val mlpIconLayout =
binding.iconLayout!!.layoutParams as ViewGroup.MarginLayoutParams
mlpIconLayout.topMargin = barInsets.top
mlpIconLayout.leftMargin = leftInsets
binding.iconLayout!!.layoutParams = mlpIconLayout
} else { } else {
val mlpListAll = binding.listAll.updateMargins(left = leftInsets)
binding.listAll.layoutParams as ViewGroup.MarginLayoutParams binding.iconLayout!!.updateMargins(top = barInsets.top, right = rightInsets)
mlpListAll.leftMargin = leftInsets
binding.listAll.layoutParams = mlpListAll
val mlpIconLayout =
binding.iconLayout!!.layoutParams as ViewGroup.MarginLayoutParams
mlpIconLayout.topMargin = barInsets.top
mlpIconLayout.rightMargin = rightInsets
binding.iconLayout!!.layoutParams = mlpIconLayout
} }
} }
val fabSpacing = resources.getDimensionPixelSize(R.dimen.spacing_fab) val fabSpacing = resources.getDimensionPixelSize(R.dimen.spacing_fab)
val mlpFab = binding.buttonStart.updateMargins(
binding.buttonStart.layoutParams as ViewGroup.MarginLayoutParams left = leftInsets + fabSpacing,
mlpFab.leftMargin = leftInsets + fabSpacing right = rightInsets + fabSpacing,
mlpFab.rightMargin = rightInsets + fabSpacing bottom = barInsets.bottom + fabSpacing
mlpFab.bottomMargin = barInsets.bottom + fabSpacing )
binding.buttonStart.layoutParams = mlpFab
binding.layoutAll.updatePadding( binding.layoutAll.updatePadding(
top = barInsets.top, top = barInsets.top,

View File

@ -12,7 +12,6 @@ import android.provider.DocumentsContract
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.view.ViewGroup.MarginLayoutParams
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat import androidx.core.app.ActivityCompat
@ -44,6 +43,7 @@ import org.yuzu.yuzu_emu.ui.main.MainActivity
import org.yuzu.yuzu_emu.utils.FileUtil import org.yuzu.yuzu_emu.utils.FileUtil
import org.yuzu.yuzu_emu.utils.GpuDriverHelper import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import org.yuzu.yuzu_emu.utils.Log import org.yuzu.yuzu_emu.utils.Log
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
class HomeSettingsFragment : Fragment() { class HomeSettingsFragment : Fragment() {
private var _binding: FragmentHomeSettingsBinding? = null private var _binding: FragmentHomeSettingsBinding? = null
@ -408,10 +408,7 @@ class HomeSettingsFragment : Fragment() {
bottom = barInsets.bottom bottom = barInsets.bottom
) )
val mlpScrollSettings = binding.scrollViewSettings.layoutParams as MarginLayoutParams binding.scrollViewSettings.updateMargins(left = leftInsets, right = rightInsets)
mlpScrollSettings.leftMargin = leftInsets
mlpScrollSettings.rightMargin = rightInsets
binding.scrollViewSettings.layoutParams = mlpScrollSettings
binding.linearLayoutSettings.updatePadding(bottom = spacingNavigation) binding.linearLayoutSettings.updatePadding(bottom = spacingNavigation)

View File

@ -34,6 +34,7 @@ import org.yuzu.yuzu_emu.model.TaskState
import org.yuzu.yuzu_emu.ui.main.MainActivity import org.yuzu.yuzu_emu.ui.main.MainActivity
import org.yuzu.yuzu_emu.utils.DirectoryInitialization import org.yuzu.yuzu_emu.utils.DirectoryInitialization
import org.yuzu.yuzu_emu.utils.FileUtil import org.yuzu.yuzu_emu.utils.FileUtil
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
import java.io.BufferedOutputStream import java.io.BufferedOutputStream
import java.io.File import java.io.File
import java.math.BigInteger import java.math.BigInteger
@ -172,16 +173,8 @@ class InstallableFragment : Fragment() {
val leftInsets = barInsets.left + cutoutInsets.left val leftInsets = barInsets.left + cutoutInsets.left
val rightInsets = barInsets.right + cutoutInsets.right val rightInsets = barInsets.right + cutoutInsets.right
val mlpAppBar = binding.toolbarInstallables.layoutParams as ViewGroup.MarginLayoutParams binding.toolbarInstallables.updateMargins(left = leftInsets, right = rightInsets)
mlpAppBar.leftMargin = leftInsets binding.listInstallables.updateMargins(left = leftInsets, right = rightInsets)
mlpAppBar.rightMargin = rightInsets
binding.toolbarInstallables.layoutParams = mlpAppBar
val mlpScrollAbout =
binding.listInstallables.layoutParams as ViewGroup.MarginLayoutParams
mlpScrollAbout.leftMargin = leftInsets
mlpScrollAbout.rightMargin = rightInsets
binding.listInstallables.layoutParams = mlpScrollAbout
binding.listInstallables.updatePadding(bottom = barInsets.bottom) binding.listInstallables.updatePadding(bottom = barInsets.bottom)

View File

@ -7,7 +7,6 @@ import android.os.Bundle
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.view.ViewGroup.MarginLayoutParams
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsCompat
@ -22,6 +21,7 @@ import org.yuzu.yuzu_emu.adapters.LicenseAdapter
import org.yuzu.yuzu_emu.databinding.FragmentLicensesBinding import org.yuzu.yuzu_emu.databinding.FragmentLicensesBinding
import org.yuzu.yuzu_emu.model.HomeViewModel import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.model.License import org.yuzu.yuzu_emu.model.License
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
class LicensesFragment : Fragment() { class LicensesFragment : Fragment() {
private var _binding: FragmentLicensesBinding? = null private var _binding: FragmentLicensesBinding? = null
@ -122,15 +122,8 @@ class LicensesFragment : Fragment() {
val leftInsets = barInsets.left + cutoutInsets.left val leftInsets = barInsets.left + cutoutInsets.left
val rightInsets = barInsets.right + cutoutInsets.right val rightInsets = barInsets.right + cutoutInsets.right
val mlpAppBar = binding.appbarLicenses.layoutParams as MarginLayoutParams binding.appbarLicenses.updateMargins(left = leftInsets, right = rightInsets)
mlpAppBar.leftMargin = leftInsets binding.listLicenses.updateMargins(left = leftInsets, right = rightInsets)
mlpAppBar.rightMargin = rightInsets
binding.appbarLicenses.layoutParams = mlpAppBar
val mlpScrollAbout = binding.listLicenses.layoutParams as MarginLayoutParams
mlpScrollAbout.leftMargin = leftInsets
mlpScrollAbout.rightMargin = rightInsets
binding.listLicenses.layoutParams = mlpScrollAbout
binding.listLicenses.updatePadding(bottom = barInsets.bottom) binding.listLicenses.updatePadding(bottom = barInsets.bottom)

View File

@ -4,7 +4,6 @@
package org.yuzu.yuzu_emu.fragments package org.yuzu.yuzu_emu.fragments
import android.app.Dialog import android.app.Dialog
import android.content.DialogInterface
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
@ -16,18 +15,52 @@ import androidx.lifecycle.ViewModelProvider
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.model.MessageDialogViewModel import org.yuzu.yuzu_emu.model.MessageDialogViewModel
import org.yuzu.yuzu_emu.utils.Log
class MessageDialogFragment : DialogFragment() { class MessageDialogFragment : DialogFragment() {
private val messageDialogViewModel: MessageDialogViewModel by activityViewModels() private val messageDialogViewModel: MessageDialogViewModel by activityViewModels()
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val titleId = requireArguments().getInt(TITLE_ID) val titleId = requireArguments().getInt(TITLE_ID)
val titleString = requireArguments().getString(TITLE_STRING)!! val title = if (titleId != 0) {
getString(titleId)
} else {
requireArguments().getString(TITLE_STRING)!!
}
val descriptionId = requireArguments().getInt(DESCRIPTION_ID) val descriptionId = requireArguments().getInt(DESCRIPTION_ID)
val descriptionString = requireArguments().getString(DESCRIPTION_STRING)!! val description = if (descriptionId != 0) {
getString(descriptionId)
} else {
requireArguments().getString(DESCRIPTION_STRING)!!
}
val positiveButtonId = requireArguments().getInt(POSITIVE_BUTTON_TITLE_ID)
val positiveButtonString = requireArguments().getString(POSITIVE_BUTTON_TITLE_STRING)!!
val positiveButton = if (positiveButtonId != 0) {
getString(positiveButtonId)
} else if (positiveButtonString.isNotEmpty()) {
positiveButtonString
} else if (messageDialogViewModel.positiveAction != null) {
getString(android.R.string.ok)
} else {
getString(R.string.close)
}
val negativeButtonId = requireArguments().getInt(NEGATIVE_BUTTON_TITLE_ID)
val negativeButtonString = requireArguments().getString(NEGATIVE_BUTTON_TITLE_STRING)!!
val negativeButton = if (negativeButtonId != 0) {
getString(negativeButtonId)
} else if (negativeButtonString.isNotEmpty()) {
negativeButtonString
} else {
getString(android.R.string.cancel)
}
val helpLinkId = requireArguments().getInt(HELP_LINK) val helpLinkId = requireArguments().getInt(HELP_LINK)
val dismissible = requireArguments().getBoolean(DISMISSIBLE) val dismissible = requireArguments().getBoolean(DISMISSIBLE)
val clearPositiveAction = requireArguments().getBoolean(CLEAR_POSITIVE_ACTION) val clearPositiveAction = requireArguments().getBoolean(CLEAR_ACTIONS)
val showNegativeButton = requireArguments().getBoolean(SHOW_NEGATIVE_BUTTON)
val builder = MaterialAlertDialogBuilder(requireContext()) val builder = MaterialAlertDialogBuilder(requireContext())
@ -35,21 +68,19 @@ class MessageDialogFragment : DialogFragment() {
messageDialogViewModel.positiveAction = null messageDialogViewModel.positiveAction = null
} }
if (messageDialogViewModel.positiveAction == null) { builder.setPositiveButton(positiveButton) { _, _ ->
builder.setPositiveButton(R.string.close, null) messageDialogViewModel.positiveAction?.invoke()
} else { }
builder.setPositiveButton(android.R.string.ok) { _: DialogInterface, _: Int -> if (messageDialogViewModel.negativeAction != null || showNegativeButton) {
messageDialogViewModel.positiveAction?.invoke() builder.setNegativeButton(negativeButton) { _, _ ->
}.setNegativeButton(android.R.string.cancel, null) messageDialogViewModel.negativeAction?.invoke()
}
} }
if (titleId != 0) builder.setTitle(titleId) if (title.isNotEmpty()) builder.setTitle(title)
if (titleString.isNotEmpty()) builder.setTitle(titleString) if (description.isNotEmpty()) {
builder.setMessage(Html.fromHtml(description, Html.FROM_HTML_MODE_LEGACY))
if (descriptionId != 0) {
builder.setMessage(Html.fromHtml(getString(descriptionId), Html.FROM_HTML_MODE_LEGACY))
} }
if (descriptionString.isNotEmpty()) builder.setMessage(descriptionString)
if (helpLinkId != 0) { if (helpLinkId != 0) {
builder.setNeutralButton(R.string.learn_more) { _, _ -> builder.setNeutralButton(R.string.learn_more) { _, _ ->
@ -76,8 +107,41 @@ class MessageDialogFragment : DialogFragment() {
private const val DESCRIPTION_STRING = "DescriptionString" private const val DESCRIPTION_STRING = "DescriptionString"
private const val HELP_LINK = "Link" private const val HELP_LINK = "Link"
private const val DISMISSIBLE = "Dismissible" private const val DISMISSIBLE = "Dismissible"
private const val CLEAR_POSITIVE_ACTION = "ClearPositiveAction" private const val CLEAR_ACTIONS = "ClearActions"
private const val POSITIVE_BUTTON_TITLE_ID = "PositiveButtonTitleId"
private const val POSITIVE_BUTTON_TITLE_STRING = "PositiveButtonTitleString"
private const val SHOW_NEGATIVE_BUTTON = "ShowNegativeButton"
private const val NEGATIVE_BUTTON_TITLE_ID = "NegativeButtonTitleId"
private const val NEGATIVE_BUTTON_TITLE_STRING = "NegativeButtonTitleString"
/**
* Creates a new [MessageDialogFragment] instance.
* @param activity Activity that will hold a [MessageDialogViewModel] instance if using
* [positiveAction] or [negativeAction].
* @param titleId String resource ID that will be used for the title. [titleString] used if 0.
* @param titleString String that will be used for the title. No title is set if empty.
* @param descriptionId String resource ID that will be used for the description.
* [descriptionString] used if 0.
* @param descriptionString String that will be used for the description.
* No description is set if empty.
* @param helpLinkId String resource ID that contains a help link. Will be added as a neutral
* button with the title R.string.help.
* @param dismissible Whether the dialog is dismissible or not. Typically used to ensure that
* the user clicks on one of the dialog buttons before closing.
* @param positiveButtonTitleId String resource ID that will be used for the positive button.
* [positiveButtonTitleString] used if 0.
* @param positiveButtonTitleString String that will be used for the positive button.
* android.R.string.close used if empty. android.R.string.ok will be used if [positiveAction]
* is not null.
* @param positiveAction Lambda to run when the positive button is clicked.
* @param showNegativeButton Normally the negative button isn't shown if there is no
* [negativeAction] set. This can override that behavior to always show a button.
* @param negativeButtonTitleId String resource ID that will be used for the negative button.
* [negativeButtonTitleString] used if 0.
* @param negativeButtonTitleString String that will be used for the negative button.
* android.R.string.cancel used if empty.
* @param negativeAction Lambda to run when the negative button is clicked
*/
fun newInstance( fun newInstance(
activity: FragmentActivity? = null, activity: FragmentActivity? = null,
titleId: Int = 0, titleId: Int = 0,
@ -86,16 +150,27 @@ class MessageDialogFragment : DialogFragment() {
descriptionString: String = "", descriptionString: String = "",
helpLinkId: Int = 0, helpLinkId: Int = 0,
dismissible: Boolean = true, dismissible: Boolean = true,
positiveAction: (() -> Unit)? = null positiveButtonTitleId: Int = 0,
positiveButtonTitleString: String = "",
positiveAction: (() -> Unit)? = null,
showNegativeButton: Boolean = false,
negativeButtonTitleId: Int = 0,
negativeButtonTitleString: String = "",
negativeAction: (() -> Unit)? = null
): MessageDialogFragment { ): MessageDialogFragment {
var clearPositiveAction = false var clearActions = false
if (activity != null) { if (activity != null) {
ViewModelProvider(activity)[MessageDialogViewModel::class.java].apply { ViewModelProvider(activity)[MessageDialogViewModel::class.java].apply {
clear() clear()
this.positiveAction = positiveAction this.positiveAction = positiveAction
this.negativeAction = negativeAction
} }
} else { } else {
clearPositiveAction = true clearActions = true
}
if (activity == null && (positiveAction == null || negativeAction == null)) {
Log.warning("[$TAG] Tried to set action with no activity!")
} }
val dialog = MessageDialogFragment() val dialog = MessageDialogFragment()
@ -106,7 +181,12 @@ class MessageDialogFragment : DialogFragment() {
putString(DESCRIPTION_STRING, descriptionString) putString(DESCRIPTION_STRING, descriptionString)
putInt(HELP_LINK, helpLinkId) putInt(HELP_LINK, helpLinkId)
putBoolean(DISMISSIBLE, dismissible) putBoolean(DISMISSIBLE, dismissible)
putBoolean(CLEAR_POSITIVE_ACTION, clearPositiveAction) putBoolean(CLEAR_ACTIONS, clearActions)
putInt(POSITIVE_BUTTON_TITLE_ID, positiveButtonTitleId)
putString(POSITIVE_BUTTON_TITLE_STRING, positiveButtonTitleString)
putBoolean(SHOW_NEGATIVE_BUTTON, showNegativeButton)
putInt(NEGATIVE_BUTTON_TITLE_ID, negativeButtonTitleId)
putString(NEGATIVE_BUTTON_TITLE_STRING, negativeButtonTitleString)
} }
dialog.arguments = bundle dialog.arguments = bundle
return dialog return dialog

View File

@ -29,6 +29,7 @@ import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
import org.yuzu.yuzu_emu.model.SettingsViewModel import org.yuzu.yuzu_emu.model.SettingsViewModel
import org.yuzu.yuzu_emu.utils.NativeConfig import org.yuzu.yuzu_emu.utils.NativeConfig
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
class SettingsSearchFragment : Fragment() { class SettingsSearchFragment : Fragment() {
private var _binding: FragmentSettingsSearchBinding? = null private var _binding: FragmentSettingsSearchBinding? = null
@ -174,15 +175,14 @@ class SettingsSearchFragment : Fragment() {
bottom = barInsets.bottom bottom = barInsets.bottom
) )
val mlpSettingsList = binding.settingsList.layoutParams as ViewGroup.MarginLayoutParams binding.settingsList.updateMargins(
mlpSettingsList.leftMargin = leftInsets + sideMargin left = leftInsets + sideMargin,
mlpSettingsList.rightMargin = rightInsets + sideMargin right = rightInsets + sideMargin
binding.settingsList.layoutParams = mlpSettingsList )
binding.divider.updateMargins(
val mlpDivider = binding.divider.layoutParams as ViewGroup.MarginLayoutParams left = leftInsets + sideMargin,
mlpDivider.leftMargin = leftInsets + sideMargin right = rightInsets + sideMargin
mlpDivider.rightMargin = rightInsets + sideMargin )
binding.divider.layoutParams = mlpDivider
windowInsets windowInsets
} }

View File

@ -7,8 +7,10 @@ import androidx.lifecycle.ViewModel
class MessageDialogViewModel : ViewModel() { class MessageDialogViewModel : ViewModel() {
var positiveAction: (() -> Unit)? = null var positiveAction: (() -> Unit)? = null
var negativeAction: (() -> Unit)? = null
fun clear() { fun clear() {
positiveAction = null positiveAction = null
negativeAction = null
} }
} }

View File

@ -8,7 +8,6 @@ import android.os.Bundle
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.view.ViewGroup.MarginLayoutParams
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsCompat
@ -27,6 +26,7 @@ import org.yuzu.yuzu_emu.databinding.FragmentGamesBinding
import org.yuzu.yuzu_emu.layout.AutofitGridLayoutManager import org.yuzu.yuzu_emu.layout.AutofitGridLayoutManager
import org.yuzu.yuzu_emu.model.GamesViewModel import org.yuzu.yuzu_emu.model.GamesViewModel
import org.yuzu.yuzu_emu.model.HomeViewModel import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
class GamesFragment : Fragment() { class GamesFragment : Fragment() {
private var _binding: FragmentGamesBinding? = null private var _binding: FragmentGamesBinding? = null
@ -169,15 +169,16 @@ class GamesFragment : Fragment() {
val leftInsets = barInsets.left + cutoutInsets.left val leftInsets = barInsets.left + cutoutInsets.left
val rightInsets = barInsets.right + cutoutInsets.right val rightInsets = barInsets.right + cutoutInsets.right
val mlpSwipe = binding.swipeRefresh.layoutParams as MarginLayoutParams val left: Int
val right: Int
if (ViewCompat.getLayoutDirection(view) == ViewCompat.LAYOUT_DIRECTION_LTR) { if (ViewCompat.getLayoutDirection(view) == ViewCompat.LAYOUT_DIRECTION_LTR) {
mlpSwipe.leftMargin = leftInsets + spacingNavigationRail left = leftInsets + spacingNavigationRail
mlpSwipe.rightMargin = rightInsets right = rightInsets
} else { } else {
mlpSwipe.leftMargin = leftInsets left = leftInsets
mlpSwipe.rightMargin = rightInsets + spacingNavigationRail right = rightInsets + spacingNavigationRail
} }
binding.swipeRefresh.layoutParams = mlpSwipe binding.swipeRefresh.updateMargins(left = left, right = right)
binding.noticeText.updatePadding(bottom = spacingNavigation) binding.noticeText.updatePadding(bottom = spacingNavigation)

View File

@ -34,7 +34,6 @@ import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.HomeNavigationDirections import org.yuzu.yuzu_emu.HomeNavigationDirections
import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.activities.EmulationActivity
import org.yuzu.yuzu_emu.databinding.ActivityMainBinding import org.yuzu.yuzu_emu.databinding.ActivityMainBinding
import org.yuzu.yuzu_emu.features.settings.model.Settings import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.fragments.AddGameFolderDialogFragment import org.yuzu.yuzu_emu.fragments.AddGameFolderDialogFragment
@ -177,9 +176,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
} }
} }
// Dismiss previous notifications (should not happen unless a crash occurred)
EmulationActivity.stopForegroundService(this)
setInsets() setInsets()
} }
@ -298,11 +294,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
super.onResume() super.onResume()
} }
override fun onDestroy() {
EmulationActivity.stopForegroundService(this)
super.onDestroy()
}
private fun setInsets() = private fun setInsets() =
ViewCompat.setOnApplyWindowInsetsListener( ViewCompat.setOnApplyWindowInsetsListener(
binding.root binding.root

View File

@ -1,70 +0,0 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.utils
import android.app.PendingIntent
import android.app.Service
import android.content.Intent
import android.os.IBinder
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.activities.EmulationActivity
/**
* A service that shows a permanent notification in the background to avoid the app getting
* cleared from memory by the system.
*/
class ForegroundService : Service() {
companion object {
const val EMULATION_RUNNING_NOTIFICATION = 0x1000
const val ACTION_STOP = "stop"
}
private fun showRunningNotification() {
// Intent is used to resume emulation if the notification is clicked
val contentIntent = PendingIntent.getActivity(
this,
0,
Intent(this, EmulationActivity::class.java),
PendingIntent.FLAG_IMMUTABLE
)
val builder =
NotificationCompat.Builder(this, getString(R.string.emulation_notification_channel_id))
.setSmallIcon(R.drawable.ic_stat_notification_logo)
.setContentTitle(getString(R.string.app_name))
.setContentText(getString(R.string.emulation_notification_running))
.setPriority(NotificationCompat.PRIORITY_LOW)
.setOngoing(true)
.setVibrate(null)
.setSound(null)
.setContentIntent(contentIntent)
startForeground(EMULATION_RUNNING_NOTIFICATION, builder.build())
}
override fun onBind(intent: Intent): IBinder? {
return null
}
override fun onCreate() {
showRunningNotification()
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
if (intent == null) {
return START_NOT_STICKY
}
if (intent.action == ACTION_STOP) {
NotificationManagerCompat.from(this).cancel(EMULATION_RUNNING_NOTIFICATION)
stopForeground(STOP_FOREGROUND_REMOVE)
stopSelfResult(startId)
}
return START_STICKY
}
override fun onDestroy() {
NotificationManagerCompat.from(this).cancel(EMULATION_RUNNING_NOTIFICATION)
}
}

View File

@ -4,6 +4,7 @@
package org.yuzu.yuzu_emu.utils package org.yuzu.yuzu_emu.utils
import android.view.View import android.view.View
import android.view.ViewGroup
object ViewUtils { object ViewUtils {
fun showView(view: View, length: Long = 300) { fun showView(view: View, length: Long = 300) {
@ -32,4 +33,28 @@ object ViewUtils {
view.visibility = View.INVISIBLE view.visibility = View.INVISIBLE
}.start() }.start()
} }
fun View.updateMargins(
left: Int = -1,
top: Int = -1,
right: Int = -1,
bottom: Int = -1
) {
val layoutParams = this.layoutParams as ViewGroup.MarginLayoutParams
layoutParams.apply {
if (left != -1) {
leftMargin = left
}
if (top != -1) {
topMargin = top
}
if (right != -1) {
rightMargin = right
}
if (bottom != -1) {
bottomMargin = bottom
}
}
this.layoutParams = layoutParams
}
} }

View File

@ -2,14 +2,8 @@
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
add_library(yuzu-android SHARED add_library(yuzu-android SHARED
android_common/android_common.cpp
android_common/android_common.h
applets/software_keyboard.cpp
applets/software_keyboard.h
emu_window/emu_window.cpp emu_window/emu_window.cpp
emu_window/emu_window.h emu_window/emu_window.h
id_cache.cpp
id_cache.h
native.cpp native.cpp
native.h native.h
native_config.cpp native_config.cpp

View File

@ -38,6 +38,13 @@ struct Values {
Settings::Specialization::Default, Settings::Specialization::Default,
true, true,
true}; true};
Settings::Setting<s32> vertical_alignment{linkage,
0,
"vertical_alignment",
Settings::Category::Android,
Settings::Specialization::Default,
true,
true};
Settings::SwitchableSetting<std::string, false> driver_path{linkage, "", "driver_path", Settings::SwitchableSetting<std::string, false> driver_path{linkage, "", "driver_path",
Settings::Category::GpuDriver}; Settings::Category::GpuDriver};
@ -60,6 +67,8 @@ struct Values {
Settings::Category::Overlay}; Settings::Category::Overlay};
Settings::Setting<bool> show_performance_overlay{linkage, true, "show_performance_overlay", Settings::Setting<bool> show_performance_overlay{linkage, true, "show_performance_overlay",
Settings::Category::Overlay}; Settings::Category::Overlay};
Settings::Setting<bool> show_thermal_overlay{linkage, false, "show_thermal_overlay",
Settings::Category::Overlay};
Settings::Setting<bool> show_input_overlay{linkage, true, "show_input_overlay", Settings::Setting<bool> show_input_overlay{linkage, true, "show_input_overlay",
Settings::Category::Overlay}; Settings::Category::Overlay};
Settings::Setting<bool> touchscreen{linkage, true, "touchscreen", Settings::Category::Overlay}; Settings::Setting<bool> touchscreen{linkage, true, "touchscreen", Settings::Category::Overlay};

View File

@ -3,6 +3,7 @@
#include <android/native_window_jni.h> #include <android/native_window_jni.h>
#include "common/android/id_cache.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "input_common/drivers/touch_screen.h" #include "input_common/drivers/touch_screen.h"
#include "input_common/drivers/virtual_amiibo.h" #include "input_common/drivers/virtual_amiibo.h"
@ -60,7 +61,8 @@ void EmuWindow_Android::OnRemoveNfcTag() {
void EmuWindow_Android::OnFrameDisplayed() { void EmuWindow_Android::OnFrameDisplayed() {
if (!m_first_frame) { if (!m_first_frame) {
EmulationSession::GetInstance().OnEmulationStarted(); Common::Android::RunJNIOnFiber<void>(
[&](JNIEnv* env) { EmulationSession::GetInstance().OnEmulationStarted(); });
m_first_frame = true; m_first_frame = true;
} }
} }

View File

@ -1,13 +1,12 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include "common/android/android_common.h"
#include "core/core.h" #include "core/core.h"
#include "core/file_sys/fs_filesystem.h" #include "core/file_sys/fs_filesystem.h"
#include "core/file_sys/patch_manager.h" #include "core/file_sys/patch_manager.h"
#include "core/loader/loader.h" #include "core/loader/loader.h"
#include "core/loader/nro.h" #include "core/loader/nro.h"
#include "jni.h"
#include "jni/android_common/android_common.h"
#include "native.h" #include "native.h"
struct RomMetadata { struct RomMetadata {
@ -79,7 +78,7 @@ extern "C" {
jboolean Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIsValid(JNIEnv* env, jobject obj, jboolean Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIsValid(JNIEnv* env, jobject obj,
jstring jpath) { jstring jpath) {
const auto file = EmulationSession::GetInstance().System().GetFilesystem()->OpenFile( const auto file = EmulationSession::GetInstance().System().GetFilesystem()->OpenFile(
GetJString(env, jpath), FileSys::OpenMode::Read); Common::Android::GetJString(env, jpath), FileSys::OpenMode::Read);
if (!file) { if (!file) {
return false; return false;
} }
@ -104,27 +103,31 @@ jboolean Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIsValid(JNIEnv* env, jobj
jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getTitle(JNIEnv* env, jobject obj, jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getTitle(JNIEnv* env, jobject obj,
jstring jpath) { jstring jpath) {
return ToJString(env, GetRomMetadata(GetJString(env, jpath)).title); return Common::Android::ToJString(
env, GetRomMetadata(Common::Android::GetJString(env, jpath)).title);
} }
jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getProgramId(JNIEnv* env, jobject obj, jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getProgramId(JNIEnv* env, jobject obj,
jstring jpath) { jstring jpath) {
return ToJString(env, std::to_string(GetRomMetadata(GetJString(env, jpath)).programId)); return Common::Android::ToJString(
env, std::to_string(GetRomMetadata(Common::Android::GetJString(env, jpath)).programId));
} }
jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getDeveloper(JNIEnv* env, jobject obj, jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getDeveloper(JNIEnv* env, jobject obj,
jstring jpath) { jstring jpath) {
return ToJString(env, GetRomMetadata(GetJString(env, jpath)).developer); return Common::Android::ToJString(
env, GetRomMetadata(Common::Android::GetJString(env, jpath)).developer);
} }
jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getVersion(JNIEnv* env, jobject obj, jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getVersion(JNIEnv* env, jobject obj,
jstring jpath, jboolean jreload) { jstring jpath, jboolean jreload) {
return ToJString(env, GetRomMetadata(GetJString(env, jpath), jreload).version); return Common::Android::ToJString(
env, GetRomMetadata(Common::Android::GetJString(env, jpath), jreload).version);
} }
jbyteArray Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIcon(JNIEnv* env, jobject obj, jbyteArray Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIcon(JNIEnv* env, jobject obj,
jstring jpath) { jstring jpath) {
auto icon_data = GetRomMetadata(GetJString(env, jpath)).icon; auto icon_data = GetRomMetadata(Common::Android::GetJString(env, jpath)).icon;
jbyteArray icon = env->NewByteArray(static_cast<jsize>(icon_data.size())); jbyteArray icon = env->NewByteArray(static_cast<jsize>(icon_data.size()));
env->SetByteArrayRegion(icon, 0, env->GetArrayLength(icon), env->SetByteArrayRegion(icon, 0, env->GetArrayLength(icon),
reinterpret_cast<jbyte*>(icon_data.data())); reinterpret_cast<jbyte*>(icon_data.data()));
@ -133,7 +136,8 @@ jbyteArray Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIcon(JNIEnv* env, jobje
jboolean Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIsHomebrew(JNIEnv* env, jobject obj, jboolean Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIsHomebrew(JNIEnv* env, jobject obj,
jstring jpath) { jstring jpath) {
return static_cast<jboolean>(GetRomMetadata(GetJString(env, jpath)).isHomebrew); return static_cast<jboolean>(
GetRomMetadata(Common::Android::GetJString(env, jpath)).isHomebrew);
} }
void Java_org_yuzu_yuzu_1emu_utils_GameMetadata_resetMetadata(JNIEnv* env, jobject obj) { void Java_org_yuzu_yuzu_1emu_utils_GameMetadata_resetMetadata(JNIEnv* env, jobject obj) {

View File

@ -20,6 +20,8 @@
#include <frontend_common/content_manager.h> #include <frontend_common/content_manager.h>
#include <jni.h> #include <jni.h>
#include "common/android/android_common.h"
#include "common/android/id_cache.h"
#include "common/detached_tasks.h" #include "common/detached_tasks.h"
#include "common/dynamic_library.h" #include "common/dynamic_library.h"
#include "common/fs/path_util.h" #include "common/fs/path_util.h"
@ -47,9 +49,7 @@
#include "core/frontend/applets/profile_select.h" #include "core/frontend/applets/profile_select.h"
#include "core/frontend/applets/software_keyboard.h" #include "core/frontend/applets/software_keyboard.h"
#include "core/frontend/applets/web_browser.h" #include "core/frontend/applets/web_browser.h"
#include "core/hle/service/am/applet_ae.h"
#include "core/hle/service/am/applet_manager.h" #include "core/hle/service/am/applet_manager.h"
#include "core/hle/service/am/applet_oe.h"
#include "core/hle/service/am/frontend/applets.h" #include "core/hle/service/am/frontend/applets.h"
#include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/filesystem/filesystem.h"
#include "core/loader/loader.h" #include "core/loader/loader.h"
@ -57,8 +57,6 @@
#include "hid_core/frontend/emulated_controller.h" #include "hid_core/frontend/emulated_controller.h"
#include "hid_core/hid_core.h" #include "hid_core/hid_core.h"
#include "hid_core/hid_types.h" #include "hid_core/hid_types.h"
#include "jni/android_common/android_common.h"
#include "jni/id_cache.h"
#include "jni/native.h" #include "jni/native.h"
#include "video_core/renderer_base.h" #include "video_core/renderer_base.h"
#include "video_core/renderer_vulkan/renderer_vulkan.h" #include "video_core/renderer_vulkan/renderer_vulkan.h"
@ -228,7 +226,7 @@ Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string
std::make_unique<EmuWindow_Android>(&m_input_subsystem, m_native_window, m_vulkan_library); std::make_unique<EmuWindow_Android>(&m_input_subsystem, m_native_window, m_vulkan_library);
// Initialize system. // Initialize system.
jauto android_keyboard = std::make_unique<SoftwareKeyboard::AndroidKeyboard>(); jauto android_keyboard = std::make_unique<Common::Android::SoftwareKeyboard::AndroidKeyboard>();
m_software_keyboard = android_keyboard.get(); m_software_keyboard = android_keyboard.get();
m_system.SetShuttingDown(false); m_system.SetShuttingDown(false);
m_system.ApplySettings(); m_system.ApplySettings();
@ -411,37 +409,39 @@ void EmulationSession::OnGamepadDisconnectEvent([[maybe_unused]] int index) {
controller->Disconnect(); controller->Disconnect();
} }
SoftwareKeyboard::AndroidKeyboard* EmulationSession::SoftwareKeyboard() { Common::Android::SoftwareKeyboard::AndroidKeyboard* EmulationSession::SoftwareKeyboard() {
return m_software_keyboard; return m_software_keyboard;
} }
void EmulationSession::LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, void EmulationSession::LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress,
int max) { int max) {
JNIEnv* env = IDCache::GetEnvForThread(); JNIEnv* env = Common::Android::GetEnvForThread();
env->CallStaticVoidMethod(IDCache::GetDiskCacheProgressClass(), env->CallStaticVoidMethod(Common::Android::GetDiskCacheProgressClass(),
IDCache::GetDiskCacheLoadProgress(), static_cast<jint>(stage), Common::Android::GetDiskCacheLoadProgress(), static_cast<jint>(stage),
static_cast<jint>(progress), static_cast<jint>(max)); static_cast<jint>(progress), static_cast<jint>(max));
} }
void EmulationSession::OnEmulationStarted() { void EmulationSession::OnEmulationStarted() {
JNIEnv* env = IDCache::GetEnvForThread(); JNIEnv* env = Common::Android::GetEnvForThread();
env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), IDCache::GetOnEmulationStarted()); env->CallStaticVoidMethod(Common::Android::GetNativeLibraryClass(),
Common::Android::GetOnEmulationStarted());
} }
void EmulationSession::OnEmulationStopped(Core::SystemResultStatus result) { void EmulationSession::OnEmulationStopped(Core::SystemResultStatus result) {
JNIEnv* env = IDCache::GetEnvForThread(); JNIEnv* env = Common::Android::GetEnvForThread();
env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), IDCache::GetOnEmulationStopped(), env->CallStaticVoidMethod(Common::Android::GetNativeLibraryClass(),
static_cast<jint>(result)); Common::Android::GetOnEmulationStopped(), static_cast<jint>(result));
} }
void EmulationSession::ChangeProgram(std::size_t program_index) { void EmulationSession::ChangeProgram(std::size_t program_index) {
JNIEnv* env = IDCache::GetEnvForThread(); JNIEnv* env = Common::Android::GetEnvForThread();
env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), IDCache::GetOnProgramChanged(), env->CallStaticVoidMethod(Common::Android::GetNativeLibraryClass(),
Common::Android::GetOnProgramChanged(),
static_cast<jint>(program_index)); static_cast<jint>(program_index));
} }
u64 EmulationSession::GetProgramId(JNIEnv* env, jstring jprogramId) { u64 EmulationSession::GetProgramId(JNIEnv* env, jstring jprogramId) {
auto program_id_string = GetJString(env, jprogramId); auto program_id_string = Common::Android::GetJString(env, jprogramId);
try { try {
return std::stoull(program_id_string); return std::stoull(program_id_string);
} catch (...) { } catch (...) {
@ -491,7 +491,7 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceDestroyed(JNIEnv* env, jobject
void Java_org_yuzu_yuzu_1emu_NativeLibrary_setAppDirectory(JNIEnv* env, jobject instance, void Java_org_yuzu_yuzu_1emu_NativeLibrary_setAppDirectory(JNIEnv* env, jobject instance,
[[maybe_unused]] jstring j_directory) { [[maybe_unused]] jstring j_directory) {
Common::FS::SetAppDirectory(GetJString(env, j_directory)); Common::FS::SetAppDirectory(Common::Android::GetJString(env, j_directory));
} }
int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env, jobject instance, int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env, jobject instance,
@ -501,21 +501,22 @@ int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env, jobject
jlambdaClass, "invoke", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); jlambdaClass, "invoke", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
const auto callback = [env, jcallback, jlambdaInvokeMethod](size_t max, size_t progress) { const auto callback = [env, jcallback, jlambdaInvokeMethod](size_t max, size_t progress) {
auto jwasCancelled = env->CallObjectMethod(jcallback, jlambdaInvokeMethod, auto jwasCancelled = env->CallObjectMethod(jcallback, jlambdaInvokeMethod,
ToJDouble(env, max), ToJDouble(env, progress)); Common::Android::ToJDouble(env, max),
return GetJBoolean(env, jwasCancelled); Common::Android::ToJDouble(env, progress));
return Common::Android::GetJBoolean(env, jwasCancelled);
}; };
return static_cast<int>( return static_cast<int>(
ContentManager::InstallNSP(EmulationSession::GetInstance().System(), ContentManager::InstallNSP(EmulationSession::GetInstance().System(),
*EmulationSession::GetInstance().System().GetFilesystem(), *EmulationSession::GetInstance().System().GetFilesystem(),
GetJString(env, j_file), callback)); Common::Android::GetJString(env, j_file), callback));
} }
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_doesUpdateMatchProgram(JNIEnv* env, jobject jobj, jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_doesUpdateMatchProgram(JNIEnv* env, jobject jobj,
jstring jprogramId, jstring jprogramId,
jstring jupdatePath) { jstring jupdatePath) {
u64 program_id = EmulationSession::GetProgramId(env, jprogramId); u64 program_id = EmulationSession::GetProgramId(env, jprogramId);
std::string updatePath = GetJString(env, jupdatePath); std::string updatePath = Common::Android::GetJString(env, jupdatePath);
std::shared_ptr<FileSys::NSP> nsp = std::make_shared<FileSys::NSP>( std::shared_ptr<FileSys::NSP> nsp = std::make_shared<FileSys::NSP>(
EmulationSession::GetInstance().System().GetFilesystem()->OpenFile( EmulationSession::GetInstance().System().GetFilesystem()->OpenFile(
updatePath, FileSys::OpenMode::Read)); updatePath, FileSys::OpenMode::Read));
@ -538,8 +539,10 @@ void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeGpuDriver(JNIEnv* e
jstring custom_driver_name, jstring custom_driver_name,
jstring file_redirect_dir) { jstring file_redirect_dir) {
EmulationSession::GetInstance().InitializeGpuDriver( EmulationSession::GetInstance().InitializeGpuDriver(
GetJString(env, hook_lib_dir), GetJString(env, custom_driver_dir), Common::Android::GetJString(env, hook_lib_dir),
GetJString(env, custom_driver_name), GetJString(env, file_redirect_dir)); Common::Android::GetJString(env, custom_driver_dir),
Common::Android::GetJString(env, custom_driver_name),
Common::Android::GetJString(env, file_redirect_dir));
} }
[[maybe_unused]] static bool CheckKgslPresent() { [[maybe_unused]] static bool CheckKgslPresent() {
@ -566,7 +569,7 @@ jobjectArray Java_org_yuzu_yuzu_1emu_utils_GpuDriverHelper_getSystemDriverInfo(
JNIEnv* env, jobject j_obj, jobject j_surf, jstring j_hook_lib_dir) { JNIEnv* env, jobject j_obj, jobject j_surf, jstring j_hook_lib_dir) {
const char* file_redirect_dir_{}; const char* file_redirect_dir_{};
int featureFlags{}; int featureFlags{};
std::string hook_lib_dir = GetJString(env, j_hook_lib_dir); std::string hook_lib_dir = Common::Android::GetJString(env, j_hook_lib_dir);
auto handle = adrenotools_open_libvulkan(RTLD_NOW, featureFlags, nullptr, hook_lib_dir.c_str(), auto handle = adrenotools_open_libvulkan(RTLD_NOW, featureFlags, nullptr, hook_lib_dir.c_str(),
nullptr, nullptr, file_redirect_dir_, nullptr); nullptr, nullptr, file_redirect_dir_, nullptr);
auto driver_library = std::make_shared<Common::DynamicLibrary>(handle); auto driver_library = std::make_shared<Common::DynamicLibrary>(handle);
@ -587,9 +590,10 @@ jobjectArray Java_org_yuzu_yuzu_1emu_utils_GpuDriverHelper_getSystemDriverInfo(
fmt::format("{}.{}.{}", VK_API_VERSION_MAJOR(driver_version), fmt::format("{}.{}.{}", VK_API_VERSION_MAJOR(driver_version),
VK_API_VERSION_MINOR(driver_version), VK_API_VERSION_PATCH(driver_version)); VK_API_VERSION_MINOR(driver_version), VK_API_VERSION_PATCH(driver_version));
jobjectArray j_driver_info = jobjectArray j_driver_info = env->NewObjectArray(
env->NewObjectArray(2, IDCache::GetStringClass(), ToJString(env, version_string)); 2, Common::Android::GetStringClass(), Common::Android::ToJString(env, version_string));
env->SetObjectArrayElement(j_driver_info, 1, ToJString(env, device.GetDriverName())); env->SetObjectArrayElement(j_driver_info, 1,
Common::Android::ToJString(env, device.GetDriverName()));
return j_driver_info; return j_driver_info;
} }
@ -742,15 +746,15 @@ jdoubleArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPerfStats(JNIEnv* env, jcl
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getCpuBackend(JNIEnv* env, jclass clazz) { jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getCpuBackend(JNIEnv* env, jclass clazz) {
if (Settings::IsNceEnabled()) { if (Settings::IsNceEnabled()) {
return ToJString(env, "NCE"); return Common::Android::ToJString(env, "NCE");
} }
return ToJString(env, "JIT"); return Common::Android::ToJString(env, "JIT");
} }
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getGpuDriver(JNIEnv* env, jobject jobj) { jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getGpuDriver(JNIEnv* env, jobject jobj) {
return ToJString(env, return Common::Android::ToJString(
EmulationSession::GetInstance().System().GPU().Renderer().GetDeviceVendor()); env, EmulationSession::GetInstance().System().GPU().Renderer().GetDeviceVendor());
} }
void Java_org_yuzu_yuzu_1emu_NativeLibrary_applySettings(JNIEnv* env, jobject jobj) { void Java_org_yuzu_yuzu_1emu_NativeLibrary_applySettings(JNIEnv* env, jobject jobj) {
@ -764,13 +768,14 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_logSettings(JNIEnv* env, jobject jobj
void Java_org_yuzu_yuzu_1emu_NativeLibrary_run(JNIEnv* env, jobject jobj, jstring j_path, void Java_org_yuzu_yuzu_1emu_NativeLibrary_run(JNIEnv* env, jobject jobj, jstring j_path,
jint j_program_index, jint j_program_index,
jboolean j_frontend_initiated) { jboolean j_frontend_initiated) {
const std::string path = GetJString(env, j_path); const std::string path = Common::Android::GetJString(env, j_path);
const Core::SystemResultStatus result{ const Core::SystemResultStatus result{
RunEmulation(path, j_program_index, j_frontend_initiated)}; RunEmulation(path, j_program_index, j_frontend_initiated)};
if (result != Core::SystemResultStatus::Success) { if (result != Core::SystemResultStatus::Success) {
env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), env->CallStaticVoidMethod(Common::Android::GetNativeLibraryClass(),
IDCache::GetExitEmulationActivity(), static_cast<int>(result)); Common::Android::GetExitEmulationActivity(),
static_cast<int>(result));
} }
} }
@ -781,7 +786,7 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_logDeviceInfo(JNIEnv* env, jclass cla
void Java_org_yuzu_yuzu_1emu_NativeLibrary_submitInlineKeyboardText(JNIEnv* env, jclass clazz, void Java_org_yuzu_yuzu_1emu_NativeLibrary_submitInlineKeyboardText(JNIEnv* env, jclass clazz,
jstring j_text) { jstring j_text) {
const std::u16string input = Common::UTF8ToUTF16(GetJString(env, j_text)); const std::u16string input = Common::UTF8ToUTF16(Common::Android::GetJString(env, j_text));
EmulationSession::GetInstance().SoftwareKeyboard()->SubmitInlineKeyboardText(input); EmulationSession::GetInstance().SoftwareKeyboard()->SubmitInlineKeyboardText(input);
} }
@ -815,16 +820,16 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getAppletLaunchPath(JNIEnv* env, j
auto bis_system = auto bis_system =
EmulationSession::GetInstance().System().GetFileSystemController().GetSystemNANDContents(); EmulationSession::GetInstance().System().GetFileSystemController().GetSystemNANDContents();
if (!bis_system) { if (!bis_system) {
return ToJString(env, ""); return Common::Android::ToJString(env, "");
} }
auto applet_nca = auto applet_nca =
bis_system->GetEntry(static_cast<u64>(jid), FileSys::ContentRecordType::Program); bis_system->GetEntry(static_cast<u64>(jid), FileSys::ContentRecordType::Program);
if (!applet_nca) { if (!applet_nca) {
return ToJString(env, ""); return Common::Android::ToJString(env, "");
} }
return ToJString(env, applet_nca->GetFullPath()); return Common::Android::ToJString(env, applet_nca->GetFullPath());
} }
void Java_org_yuzu_yuzu_1emu_NativeLibrary_setCurrentAppletId(JNIEnv* env, jclass clazz, void Java_org_yuzu_yuzu_1emu_NativeLibrary_setCurrentAppletId(JNIEnv* env, jclass clazz,
@ -857,7 +862,7 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isFirmwareAvailable(JNIEnv* env,
jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPatchesForFile(JNIEnv* env, jobject jobj, jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPatchesForFile(JNIEnv* env, jobject jobj,
jstring jpath, jstring jpath,
jstring jprogramId) { jstring jprogramId) {
const auto path = GetJString(env, jpath); const auto path = Common::Android::GetJString(env, jpath);
const auto vFile = const auto vFile =
Core::GetGameFileFromPath(EmulationSession::GetInstance().System().GetFilesystem(), path); Core::GetGameFileFromPath(EmulationSession::GetInstance().System().GetFilesystem(), path);
if (vFile == nullptr) { if (vFile == nullptr) {
@ -875,14 +880,15 @@ jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPatchesForFile(JNIEnv* env
auto patches = pm.GetPatches(update_raw); auto patches = pm.GetPatches(update_raw);
jobjectArray jpatchArray = jobjectArray jpatchArray =
env->NewObjectArray(patches.size(), IDCache::GetPatchClass(), nullptr); env->NewObjectArray(patches.size(), Common::Android::GetPatchClass(), nullptr);
int i = 0; int i = 0;
for (const auto& patch : patches) { for (const auto& patch : patches) {
jobject jpatch = env->NewObject( jobject jpatch = env->NewObject(
IDCache::GetPatchClass(), IDCache::GetPatchConstructor(), patch.enabled, Common::Android::GetPatchClass(), Common::Android::GetPatchConstructor(), patch.enabled,
ToJString(env, patch.name), ToJString(env, patch.version), Common::Android::ToJString(env, patch.name),
static_cast<jint>(patch.type), ToJString(env, std::to_string(patch.program_id)), Common::Android::ToJString(env, patch.version), static_cast<jint>(patch.type),
ToJString(env, std::to_string(patch.title_id))); Common::Android::ToJString(env, std::to_string(patch.program_id)),
Common::Android::ToJString(env, std::to_string(patch.title_id)));
env->SetObjectArrayElement(jpatchArray, i, jpatch); env->SetObjectArrayElement(jpatchArray, i, jpatch);
++i; ++i;
} }
@ -906,7 +912,7 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeMod(JNIEnv* env, jobject jobj,
jstring jname) { jstring jname) {
auto program_id = EmulationSession::GetProgramId(env, jprogramId); auto program_id = EmulationSession::GetProgramId(env, jprogramId);
ContentManager::RemoveMod(EmulationSession::GetInstance().System().GetFileSystemController(), ContentManager::RemoveMod(EmulationSession::GetInstance().System().GetFileSystemController(),
program_id, GetJString(env, jname)); program_id, Common::Android::GetJString(env, jname));
} }
jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyInstalledContents(JNIEnv* env, jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyInstalledContents(JNIEnv* env,
@ -917,17 +923,18 @@ jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyInstalledContents(JNIEn
jlambdaClass, "invoke", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); jlambdaClass, "invoke", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
const auto callback = [env, jcallback, jlambdaInvokeMethod](size_t max, size_t progress) { const auto callback = [env, jcallback, jlambdaInvokeMethod](size_t max, size_t progress) {
auto jwasCancelled = env->CallObjectMethod(jcallback, jlambdaInvokeMethod, auto jwasCancelled = env->CallObjectMethod(jcallback, jlambdaInvokeMethod,
ToJDouble(env, max), ToJDouble(env, progress)); Common::Android::ToJDouble(env, max),
return GetJBoolean(env, jwasCancelled); Common::Android::ToJDouble(env, progress));
return Common::Android::GetJBoolean(env, jwasCancelled);
}; };
auto& session = EmulationSession::GetInstance(); auto& session = EmulationSession::GetInstance();
std::vector<std::string> result = ContentManager::VerifyInstalledContents( std::vector<std::string> result = ContentManager::VerifyInstalledContents(
session.System(), *session.GetContentProvider(), callback); session.System(), *session.GetContentProvider(), callback);
jobjectArray jresult = jobjectArray jresult = env->NewObjectArray(result.size(), Common::Android::GetStringClass(),
env->NewObjectArray(result.size(), IDCache::GetStringClass(), ToJString(env, "")); Common::Android::ToJString(env, ""));
for (size_t i = 0; i < result.size(); ++i) { for (size_t i = 0; i < result.size(); ++i) {
env->SetObjectArrayElement(jresult, i, ToJString(env, result[i])); env->SetObjectArrayElement(jresult, i, Common::Android::ToJString(env, result[i]));
} }
return jresult; return jresult;
} }
@ -939,19 +946,20 @@ jint Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyGameContents(JNIEnv* env, jobje
jlambdaClass, "invoke", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); jlambdaClass, "invoke", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
const auto callback = [env, jcallback, jlambdaInvokeMethod](size_t max, size_t progress) { const auto callback = [env, jcallback, jlambdaInvokeMethod](size_t max, size_t progress) {
auto jwasCancelled = env->CallObjectMethod(jcallback, jlambdaInvokeMethod, auto jwasCancelled = env->CallObjectMethod(jcallback, jlambdaInvokeMethod,
ToJDouble(env, max), ToJDouble(env, progress)); Common::Android::ToJDouble(env, max),
return GetJBoolean(env, jwasCancelled); Common::Android::ToJDouble(env, progress));
return Common::Android::GetJBoolean(env, jwasCancelled);
}; };
auto& session = EmulationSession::GetInstance(); auto& session = EmulationSession::GetInstance();
return static_cast<jint>( return static_cast<jint>(ContentManager::VerifyGameContents(
ContentManager::VerifyGameContents(session.System(), GetJString(env, jpath), callback)); session.System(), Common::Android::GetJString(env, jpath), callback));
} }
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject jobj, jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject jobj,
jstring jprogramId) { jstring jprogramId) {
auto program_id = EmulationSession::GetProgramId(env, jprogramId); auto program_id = EmulationSession::GetProgramId(env, jprogramId);
if (program_id == 0) { if (program_id == 0) {
return ToJString(env, ""); return Common::Android::ToJString(env, "");
} }
auto& system = EmulationSession::GetInstance().System(); auto& system = EmulationSession::GetInstance().System();
@ -968,7 +976,7 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject j
const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath( const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath(
{}, vfsNandDir, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData, {}, vfsNandDir, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData,
program_id, user_id->AsU128(), 0); program_id, user_id->AsU128(), 0);
return ToJString(env, user_save_data_path); return Common::Android::ToJString(env, user_save_data_path);
} }
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getDefaultProfileSaveDataRoot(JNIEnv* env, jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getDefaultProfileSaveDataRoot(JNIEnv* env,
@ -981,12 +989,13 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getDefaultProfileSaveDataRoot(JNIE
const auto user_save_data_root = const auto user_save_data_root =
FileSys::SaveDataFactory::GetUserGameSaveDataRoot(user_id->AsU128(), jfuture); FileSys::SaveDataFactory::GetUserGameSaveDataRoot(user_id->AsU128(), jfuture);
return ToJString(env, user_save_data_root); return Common::Android::ToJString(env, user_save_data_root);
} }
void Java_org_yuzu_yuzu_1emu_NativeLibrary_addFileToFilesystemProvider(JNIEnv* env, jobject jobj, void Java_org_yuzu_yuzu_1emu_NativeLibrary_addFileToFilesystemProvider(JNIEnv* env, jobject jobj,
jstring jpath) { jstring jpath) {
EmulationSession::GetInstance().ConfigureFilesystemProvider(GetJString(env, jpath)); EmulationSession::GetInstance().ConfigureFilesystemProvider(
Common::Android::GetJString(env, jpath));
} }
void Java_org_yuzu_yuzu_1emu_NativeLibrary_clearFilesystemProvider(JNIEnv* env, jobject jobj) { void Java_org_yuzu_yuzu_1emu_NativeLibrary_clearFilesystemProvider(JNIEnv* env, jobject jobj) {

View File

@ -2,13 +2,13 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <android/native_window_jni.h> #include <android/native_window_jni.h>
#include "common/android/applets/software_keyboard.h"
#include "common/detached_tasks.h" #include "common/detached_tasks.h"
#include "core/core.h" #include "core/core.h"
#include "core/file_sys/registered_cache.h" #include "core/file_sys/registered_cache.h"
#include "core/hle/service/acc/profile_manager.h" #include "core/hle/service/acc/profile_manager.h"
#include "core/perf_stats.h" #include "core/perf_stats.h"
#include "frontend_common/content_manager.h" #include "frontend_common/content_manager.h"
#include "jni/applets/software_keyboard.h"
#include "jni/emu_window/emu_window.h" #include "jni/emu_window/emu_window.h"
#include "video_core/rasterizer_interface.h" #include "video_core/rasterizer_interface.h"
@ -54,7 +54,7 @@ public:
void SetDeviceType([[maybe_unused]] int index, int type); void SetDeviceType([[maybe_unused]] int index, int type);
void OnGamepadConnectEvent([[maybe_unused]] int index); void OnGamepadConnectEvent([[maybe_unused]] int index);
void OnGamepadDisconnectEvent([[maybe_unused]] int index); void OnGamepadDisconnectEvent([[maybe_unused]] int index);
SoftwareKeyboard::AndroidKeyboard* SoftwareKeyboard(); Common::Android::SoftwareKeyboard::AndroidKeyboard* SoftwareKeyboard();
static void OnEmulationStarted(); static void OnEmulationStarted();
@ -79,7 +79,7 @@ private:
Core::SystemResultStatus m_load_result{Core::SystemResultStatus::ErrorNotInitialized}; Core::SystemResultStatus m_load_result{Core::SystemResultStatus::ErrorNotInitialized};
std::atomic<bool> m_is_running = false; std::atomic<bool> m_is_running = false;
std::atomic<bool> m_is_paused = false; std::atomic<bool> m_is_paused = false;
SoftwareKeyboard::AndroidKeyboard* m_software_keyboard{}; Common::Android::SoftwareKeyboard::AndroidKeyboard* m_software_keyboard{};
std::unique_ptr<FileSys::ManualContentProvider> m_manual_provider; std::unique_ptr<FileSys::ManualContentProvider> m_manual_provider;
int m_applet_id{1}; int m_applet_id{1};

View File

@ -8,11 +8,11 @@
#include "android_config.h" #include "android_config.h"
#include "android_settings.h" #include "android_settings.h"
#include "common/android/android_common.h"
#include "common/android/id_cache.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/settings.h" #include "common/settings.h"
#include "frontend_common/config.h" #include "frontend_common/config.h"
#include "jni/android_common/android_common.h"
#include "jni/id_cache.h"
#include "native.h" #include "native.h"
std::unique_ptr<AndroidConfig> global_config; std::unique_ptr<AndroidConfig> global_config;
@ -20,7 +20,7 @@ std::unique_ptr<AndroidConfig> per_game_config;
template <typename T> template <typename T>
Settings::Setting<T>* getSetting(JNIEnv* env, jstring jkey) { Settings::Setting<T>* getSetting(JNIEnv* env, jstring jkey) {
auto key = GetJString(env, jkey); auto key = Common::Android::GetJString(env, jkey);
auto basic_setting = Settings::values.linkage.by_key[key]; auto basic_setting = Settings::values.linkage.by_key[key];
if (basic_setting != 0) { if (basic_setting != 0) {
return static_cast<Settings::Setting<T>*>(basic_setting); return static_cast<Settings::Setting<T>*>(basic_setting);
@ -55,7 +55,7 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_initializePerGameConfig(JNIEnv*
jstring jprogramId, jstring jprogramId,
jstring jfileName) { jstring jfileName) {
auto program_id = EmulationSession::GetProgramId(env, jprogramId); auto program_id = EmulationSession::GetProgramId(env, jprogramId);
auto file_name = GetJString(env, jfileName); auto file_name = Common::Android::GetJString(env, jfileName);
const auto config_file_name = program_id == 0 ? file_name : fmt::format("{:016X}", program_id); const auto config_file_name = program_id == 0 ? file_name : fmt::format("{:016X}", program_id);
per_game_config = per_game_config =
std::make_unique<AndroidConfig>(config_file_name, Config::ConfigType::PerGameConfig); std::make_unique<AndroidConfig>(config_file_name, Config::ConfigType::PerGameConfig);
@ -186,9 +186,9 @@ jstring Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getString(JNIEnv* env, jobjec
jboolean needGlobal) { jboolean needGlobal) {
auto setting = getSetting<std::string>(env, jkey); auto setting = getSetting<std::string>(env, jkey);
if (setting == nullptr) { if (setting == nullptr) {
return ToJString(env, ""); return Common::Android::ToJString(env, "");
} }
return ToJString(env, setting->GetValue(static_cast<bool>(needGlobal))); return Common::Android::ToJString(env, setting->GetValue(static_cast<bool>(needGlobal)));
} }
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setString(JNIEnv* env, jobject obj, jstring jkey, void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setString(JNIEnv* env, jobject obj, jstring jkey,
@ -198,7 +198,7 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setString(JNIEnv* env, jobject o
return; return;
} }
setting->SetValue(GetJString(env, value)); setting->SetValue(Common::Android::GetJString(env, value));
} }
jboolean Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getIsRuntimeModifiable(JNIEnv* env, jobject obj, jboolean Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getIsRuntimeModifiable(JNIEnv* env, jobject obj,
@ -214,13 +214,13 @@ jstring Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getPairedSettingKey(JNIEnv* e
jstring jkey) { jstring jkey) {
auto setting = getSetting<std::string>(env, jkey); auto setting = getSetting<std::string>(env, jkey);
if (setting == nullptr) { if (setting == nullptr) {
return ToJString(env, ""); return Common::Android::ToJString(env, "");
} }
if (setting->PairedSetting() == nullptr) { if (setting->PairedSetting() == nullptr) {
return ToJString(env, ""); return Common::Android::ToJString(env, "");
} }
return ToJString(env, setting->PairedSetting()->GetLabel()); return Common::Android::ToJString(env, setting->PairedSetting()->GetLabel());
} }
jboolean Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getIsSwitchable(JNIEnv* env, jobject obj, jboolean Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getIsSwitchable(JNIEnv* env, jobject obj,
@ -262,21 +262,21 @@ jstring Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getDefaultToString(JNIEnv* en
jstring jkey) { jstring jkey) {
auto setting = getSetting<std::string>(env, jkey); auto setting = getSetting<std::string>(env, jkey);
if (setting != nullptr) { if (setting != nullptr) {
return ToJString(env, setting->DefaultToString()); return Common::Android::ToJString(env, setting->DefaultToString());
} }
return ToJString(env, ""); return Common::Android::ToJString(env, "");
} }
jobjectArray Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getGameDirs(JNIEnv* env, jobject obj) { jobjectArray Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getGameDirs(JNIEnv* env, jobject obj) {
jclass gameDirClass = IDCache::GetGameDirClass(); jclass gameDirClass = Common::Android::GetGameDirClass();
jmethodID gameDirConstructor = IDCache::GetGameDirConstructor(); jmethodID gameDirConstructor = Common::Android::GetGameDirConstructor();
jobjectArray jgameDirArray = jobjectArray jgameDirArray =
env->NewObjectArray(AndroidSettings::values.game_dirs.size(), gameDirClass, nullptr); env->NewObjectArray(AndroidSettings::values.game_dirs.size(), gameDirClass, nullptr);
for (size_t i = 0; i < AndroidSettings::values.game_dirs.size(); ++i) { for (size_t i = 0; i < AndroidSettings::values.game_dirs.size(); ++i) {
jobject jgameDir = jobject jgameDir = env->NewObject(
env->NewObject(gameDirClass, gameDirConstructor, gameDirClass, gameDirConstructor,
ToJString(env, AndroidSettings::values.game_dirs[i].path), Common::Android::ToJString(env, AndroidSettings::values.game_dirs[i].path),
static_cast<jboolean>(AndroidSettings::values.game_dirs[i].deep_scan)); static_cast<jboolean>(AndroidSettings::values.game_dirs[i].deep_scan));
env->SetObjectArrayElement(jgameDirArray, i, jgameDir); env->SetObjectArrayElement(jgameDirArray, i, jgameDir);
} }
return jgameDirArray; return jgameDirArray;
@ -292,14 +292,14 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setGameDirs(JNIEnv* env, jobject
} }
jobject dir = env->GetObjectArrayElement(gameDirs, 0); jobject dir = env->GetObjectArrayElement(gameDirs, 0);
jclass gameDirClass = IDCache::GetGameDirClass(); jclass gameDirClass = Common::Android::GetGameDirClass();
jfieldID uriStringField = env->GetFieldID(gameDirClass, "uriString", "Ljava/lang/String;"); jfieldID uriStringField = env->GetFieldID(gameDirClass, "uriString", "Ljava/lang/String;");
jfieldID deepScanBooleanField = env->GetFieldID(gameDirClass, "deepScan", "Z"); jfieldID deepScanBooleanField = env->GetFieldID(gameDirClass, "deepScan", "Z");
for (int i = 0; i < size; ++i) { for (int i = 0; i < size; ++i) {
dir = env->GetObjectArrayElement(gameDirs, i); dir = env->GetObjectArrayElement(gameDirs, i);
jstring juriString = static_cast<jstring>(env->GetObjectField(dir, uriStringField)); jstring juriString = static_cast<jstring>(env->GetObjectField(dir, uriStringField));
jboolean jdeepScanBoolean = env->GetBooleanField(dir, deepScanBooleanField); jboolean jdeepScanBoolean = env->GetBooleanField(dir, deepScanBooleanField);
std::string uriString = GetJString(env, juriString); std::string uriString = Common::Android::GetJString(env, juriString);
AndroidSettings::values.game_dirs.push_back( AndroidSettings::values.game_dirs.push_back(
AndroidSettings::GameDir{uriString, static_cast<bool>(jdeepScanBoolean)}); AndroidSettings::GameDir{uriString, static_cast<bool>(jdeepScanBoolean)});
} }
@ -307,13 +307,13 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setGameDirs(JNIEnv* env, jobject
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_addGameDir(JNIEnv* env, jobject obj, void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_addGameDir(JNIEnv* env, jobject obj,
jobject gameDir) { jobject gameDir) {
jclass gameDirClass = IDCache::GetGameDirClass(); jclass gameDirClass = Common::Android::GetGameDirClass();
jfieldID uriStringField = env->GetFieldID(gameDirClass, "uriString", "Ljava/lang/String;"); jfieldID uriStringField = env->GetFieldID(gameDirClass, "uriString", "Ljava/lang/String;");
jfieldID deepScanBooleanField = env->GetFieldID(gameDirClass, "deepScan", "Z"); jfieldID deepScanBooleanField = env->GetFieldID(gameDirClass, "deepScan", "Z");
jstring juriString = static_cast<jstring>(env->GetObjectField(gameDir, uriStringField)); jstring juriString = static_cast<jstring>(env->GetObjectField(gameDir, uriStringField));
jboolean jdeepScanBoolean = env->GetBooleanField(gameDir, deepScanBooleanField); jboolean jdeepScanBoolean = env->GetBooleanField(gameDir, deepScanBooleanField);
std::string uriString = GetJString(env, juriString); std::string uriString = Common::Android::GetJString(env, juriString);
AndroidSettings::values.game_dirs.push_back( AndroidSettings::values.game_dirs.push_back(
AndroidSettings::GameDir{uriString, static_cast<bool>(jdeepScanBoolean)}); AndroidSettings::GameDir{uriString, static_cast<bool>(jdeepScanBoolean)});
} }
@ -323,9 +323,11 @@ jobjectArray Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getDisabledAddons(JNIEnv
auto program_id = EmulationSession::GetProgramId(env, jprogramId); auto program_id = EmulationSession::GetProgramId(env, jprogramId);
auto& disabledAddons = Settings::values.disabled_addons[program_id]; auto& disabledAddons = Settings::values.disabled_addons[program_id];
jobjectArray jdisabledAddonsArray = jobjectArray jdisabledAddonsArray =
env->NewObjectArray(disabledAddons.size(), IDCache::GetStringClass(), ToJString(env, "")); env->NewObjectArray(disabledAddons.size(), Common::Android::GetStringClass(),
Common::Android::ToJString(env, ""));
for (size_t i = 0; i < disabledAddons.size(); ++i) { for (size_t i = 0; i < disabledAddons.size(); ++i) {
env->SetObjectArrayElement(jdisabledAddonsArray, i, ToJString(env, disabledAddons[i])); env->SetObjectArrayElement(jdisabledAddonsArray, i,
Common::Android::ToJString(env, disabledAddons[i]));
} }
return jdisabledAddonsArray; return jdisabledAddonsArray;
} }
@ -339,7 +341,7 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setDisabledAddons(JNIEnv* env, j
const int size = env->GetArrayLength(jdisabledAddons); const int size = env->GetArrayLength(jdisabledAddons);
for (int i = 0; i < size; ++i) { for (int i = 0; i < size; ++i) {
auto jaddon = static_cast<jstring>(env->GetObjectArrayElement(jdisabledAddons, i)); auto jaddon = static_cast<jstring>(env->GetObjectArrayElement(jdisabledAddons, i));
disabled_addons.push_back(GetJString(env, jaddon)); disabled_addons.push_back(Common::Android::GetJString(env, jaddon));
} }
Settings::values.disabled_addons[program_id] = disabled_addons; Settings::values.disabled_addons[program_id] = disabled_addons;
} }
@ -348,26 +350,27 @@ jobjectArray Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getOverlayControlData(JN
jobject obj) { jobject obj) {
jobjectArray joverlayControlDataArray = jobjectArray joverlayControlDataArray =
env->NewObjectArray(AndroidSettings::values.overlay_control_data.size(), env->NewObjectArray(AndroidSettings::values.overlay_control_data.size(),
IDCache::GetOverlayControlDataClass(), nullptr); Common::Android::GetOverlayControlDataClass(), nullptr);
for (size_t i = 0; i < AndroidSettings::values.overlay_control_data.size(); ++i) { for (size_t i = 0; i < AndroidSettings::values.overlay_control_data.size(); ++i) {
const auto& control_data = AndroidSettings::values.overlay_control_data[i]; const auto& control_data = AndroidSettings::values.overlay_control_data[i];
jobject jlandscapePosition = jobject jlandscapePosition =
env->NewObject(IDCache::GetPairClass(), IDCache::GetPairConstructor(), env->NewObject(Common::Android::GetPairClass(), Common::Android::GetPairConstructor(),
ToJDouble(env, control_data.landscape_position.first), Common::Android::ToJDouble(env, control_data.landscape_position.first),
ToJDouble(env, control_data.landscape_position.second)); Common::Android::ToJDouble(env, control_data.landscape_position.second));
jobject jportraitPosition = jobject jportraitPosition =
env->NewObject(IDCache::GetPairClass(), IDCache::GetPairConstructor(), env->NewObject(Common::Android::GetPairClass(), Common::Android::GetPairConstructor(),
ToJDouble(env, control_data.portrait_position.first), Common::Android::ToJDouble(env, control_data.portrait_position.first),
ToJDouble(env, control_data.portrait_position.second)); Common::Android::ToJDouble(env, control_data.portrait_position.second));
jobject jfoldablePosition = jobject jfoldablePosition =
env->NewObject(IDCache::GetPairClass(), IDCache::GetPairConstructor(), env->NewObject(Common::Android::GetPairClass(), Common::Android::GetPairConstructor(),
ToJDouble(env, control_data.foldable_position.first), Common::Android::ToJDouble(env, control_data.foldable_position.first),
ToJDouble(env, control_data.foldable_position.second)); Common::Android::ToJDouble(env, control_data.foldable_position.second));
jobject jcontrolData = env->NewObject( jobject jcontrolData =
IDCache::GetOverlayControlDataClass(), IDCache::GetOverlayControlDataConstructor(), env->NewObject(Common::Android::GetOverlayControlDataClass(),
ToJString(env, control_data.id), control_data.enabled, jlandscapePosition, Common::Android::GetOverlayControlDataConstructor(),
jportraitPosition, jfoldablePosition); Common::Android::ToJString(env, control_data.id), control_data.enabled,
jlandscapePosition, jportraitPosition, jfoldablePosition);
env->SetObjectArrayElement(joverlayControlDataArray, i, jcontrolData); env->SetObjectArrayElement(joverlayControlDataArray, i, jcontrolData);
} }
return joverlayControlDataArray; return joverlayControlDataArray;
@ -384,33 +387,41 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setOverlayControlData(
for (int i = 0; i < size; ++i) { for (int i = 0; i < size; ++i) {
jobject joverlayControlData = env->GetObjectArrayElement(joverlayControlDataArray, i); jobject joverlayControlData = env->GetObjectArrayElement(joverlayControlDataArray, i);
jstring jidString = static_cast<jstring>( jstring jidString = static_cast<jstring>(env->GetObjectField(
env->GetObjectField(joverlayControlData, IDCache::GetOverlayControlDataIdField())); joverlayControlData, Common::Android::GetOverlayControlDataIdField()));
bool enabled = static_cast<bool>(env->GetBooleanField( bool enabled = static_cast<bool>(env->GetBooleanField(
joverlayControlData, IDCache::GetOverlayControlDataEnabledField())); joverlayControlData, Common::Android::GetOverlayControlDataEnabledField()));
jobject jlandscapePosition = env->GetObjectField( jobject jlandscapePosition = env->GetObjectField(
joverlayControlData, IDCache::GetOverlayControlDataLandscapePositionField()); joverlayControlData, Common::Android::GetOverlayControlDataLandscapePositionField());
std::pair<double, double> landscape_position = std::make_pair( std::pair<double, double> landscape_position = std::make_pair(
GetJDouble(env, env->GetObjectField(jlandscapePosition, IDCache::GetPairFirstField())), Common::Android::GetJDouble(
GetJDouble(env, env, env->GetObjectField(jlandscapePosition, Common::Android::GetPairFirstField())),
env->GetObjectField(jlandscapePosition, IDCache::GetPairSecondField()))); Common::Android::GetJDouble(
env,
env->GetObjectField(jlandscapePosition, Common::Android::GetPairSecondField())));
jobject jportraitPosition = env->GetObjectField( jobject jportraitPosition = env->GetObjectField(
joverlayControlData, IDCache::GetOverlayControlDataPortraitPositionField()); joverlayControlData, Common::Android::GetOverlayControlDataPortraitPositionField());
std::pair<double, double> portrait_position = std::make_pair( std::pair<double, double> portrait_position = std::make_pair(
GetJDouble(env, env->GetObjectField(jportraitPosition, IDCache::GetPairFirstField())), Common::Android::GetJDouble(
GetJDouble(env, env->GetObjectField(jportraitPosition, IDCache::GetPairSecondField()))); env, env->GetObjectField(jportraitPosition, Common::Android::GetPairFirstField())),
Common::Android::GetJDouble(
env,
env->GetObjectField(jportraitPosition, Common::Android::GetPairSecondField())));
jobject jfoldablePosition = env->GetObjectField( jobject jfoldablePosition = env->GetObjectField(
joverlayControlData, IDCache::GetOverlayControlDataFoldablePositionField()); joverlayControlData, Common::Android::GetOverlayControlDataFoldablePositionField());
std::pair<double, double> foldable_position = std::make_pair( std::pair<double, double> foldable_position = std::make_pair(
GetJDouble(env, env->GetObjectField(jfoldablePosition, IDCache::GetPairFirstField())), Common::Android::GetJDouble(
GetJDouble(env, env->GetObjectField(jfoldablePosition, IDCache::GetPairSecondField()))); env, env->GetObjectField(jfoldablePosition, Common::Android::GetPairFirstField())),
Common::Android::GetJDouble(
env,
env->GetObjectField(jfoldablePosition, Common::Android::GetPairSecondField())));
AndroidSettings::values.overlay_control_data.push_back(AndroidSettings::OverlayControlData{ AndroidSettings::values.overlay_control_data.push_back(AndroidSettings::OverlayControlData{
GetJString(env, jidString), enabled, landscape_position, portrait_position, Common::Android::GetJString(env, jidString), enabled, landscape_position,
foldable_position}); portrait_position, foldable_position});
} }
} }

View File

@ -1,31 +1,30 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project // SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <common/android/android_common.h>
#include <common/logging/log.h> #include <common/logging/log.h>
#include <jni.h> #include <jni.h>
#include "android_common/android_common.h"
extern "C" { extern "C" {
void Java_org_yuzu_yuzu_1emu_utils_Log_debug(JNIEnv* env, jobject obj, jstring jmessage) { void Java_org_yuzu_yuzu_1emu_utils_Log_debug(JNIEnv* env, jobject obj, jstring jmessage) {
LOG_DEBUG(Frontend, "{}", GetJString(env, jmessage)); LOG_DEBUG(Frontend, "{}", Common::Android::GetJString(env, jmessage));
} }
void Java_org_yuzu_yuzu_1emu_utils_Log_warning(JNIEnv* env, jobject obj, jstring jmessage) { void Java_org_yuzu_yuzu_1emu_utils_Log_warning(JNIEnv* env, jobject obj, jstring jmessage) {
LOG_WARNING(Frontend, "{}", GetJString(env, jmessage)); LOG_WARNING(Frontend, "{}", Common::Android::GetJString(env, jmessage));
} }
void Java_org_yuzu_yuzu_1emu_utils_Log_info(JNIEnv* env, jobject obj, jstring jmessage) { void Java_org_yuzu_yuzu_1emu_utils_Log_info(JNIEnv* env, jobject obj, jstring jmessage) {
LOG_INFO(Frontend, "{}", GetJString(env, jmessage)); LOG_INFO(Frontend, "{}", Common::Android::GetJString(env, jmessage));
} }
void Java_org_yuzu_yuzu_1emu_utils_Log_error(JNIEnv* env, jobject obj, jstring jmessage) { void Java_org_yuzu_yuzu_1emu_utils_Log_error(JNIEnv* env, jobject obj, jstring jmessage) {
LOG_ERROR(Frontend, "{}", GetJString(env, jmessage)); LOG_ERROR(Frontend, "{}", Common::Android::GetJString(env, jmessage));
} }
void Java_org_yuzu_yuzu_1emu_utils_Log_critical(JNIEnv* env, jobject obj, jstring jmessage) { void Java_org_yuzu_yuzu_1emu_utils_Log_critical(JNIEnv* env, jobject obj, jstring jmessage) {
LOG_CRITICAL(Frontend, "{}", GetJString(env, jmessage)); LOG_CRITICAL(Frontend, "{}", Common::Android::GetJString(env, jmessage));
} }
} // extern "C" } // extern "C"

View File

@ -140,6 +140,7 @@
android:id="@+id/overlay_container" android:id="@+id/overlay_container"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_marginHorizontal="20dp"
android:fitsSystemWindows="true"> android:fitsSystemWindows="true">
<com.google.android.material.textview.MaterialTextView <com.google.android.material.textview.MaterialTextView
@ -150,7 +151,19 @@
android:layout_gravity="left" android:layout_gravity="left"
android:clickable="false" android:clickable="false"
android:focusable="false" android:focusable="false"
android:paddingHorizontal="20dp" android:textColor="@android:color/white"
android:shadowColor="@android:color/black"
android:shadowRadius="3"
tools:ignore="RtlHardcoded" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/show_thermals_text"
style="@style/TextAppearance.Material3.BodySmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:clickable="false"
android:focusable="false"
android:textColor="@android:color/white" android:textColor="@android:color/white"
android:shadowColor="@android:color/black" android:shadowColor="@android:color/black"
android:shadowRadius="3" android:shadowRadius="3"

View File

@ -6,6 +6,11 @@
android:title="@string/emulation_fps_counter" android:title="@string/emulation_fps_counter"
android:checkable="true" /> android:checkable="true" />
<item
android:id="@+id/thermal_indicator"
android:title="@string/emulation_thermal_indicator"
android:checkable="true" />
<item <item
android:id="@+id/menu_edit_overlay" android:id="@+id/menu_edit_overlay"
android:title="@string/emulation_touch_overlay_edit" /> android:title="@string/emulation_touch_overlay_edit" />

View File

@ -1,9 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<string name="emulation_notification_channel_name">المحاكي نشط</string>
<string name="emulation_notification_channel_description">اظهار اشعار دائم عندما يكون المحاكي نشطاً</string>
<string name="emulation_notification_running">يوزو قيد التشغيل</string>
<string name="notice_notification_channel_name">الإشعارات والأخطاء</string> <string name="notice_notification_channel_name">الإشعارات والأخطاء</string>
<string name="notice_notification_channel_description">اظهار اشعار عند حصول اي مشكلة.</string> <string name="notice_notification_channel_description">اظهار اشعار عند حصول اي مشكلة.</string>
<string name="notification_permission_not_granted">لم يتم منح إذن الإشعار</string> <string name="notification_permission_not_granted">لم يتم منح إذن الإشعار</string>

View File

@ -2,9 +2,6 @@
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<string name="app_disclaimer">ئەم نەرمەکاڵایە یارییەکانی کۆنسۆلی نینتێندۆ سویچ کارپێدەکات. هیچ ناونیشانێکی یاری و کلیلی تێدا نییە..&lt;br /&gt;&lt;br /&gt;پێش ئەوەی دەست پێ بکەیت، تکایە شوێنی فایلی <![CDATA[<b> prod.keys </b>]]> دیاریبکە لە نێو کۆگای ئامێرەکەت.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">زیاتر فێربە</a>]]></string> <string name="app_disclaimer">ئەم نەرمەکاڵایە یارییەکانی کۆنسۆلی نینتێندۆ سویچ کارپێدەکات. هیچ ناونیشانێکی یاری و کلیلی تێدا نییە..&lt;br /&gt;&lt;br /&gt;پێش ئەوەی دەست پێ بکەیت، تکایە شوێنی فایلی <![CDATA[<b> prod.keys </b>]]> دیاریبکە لە نێو کۆگای ئامێرەکەت.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">زیاتر فێربە</a>]]></string>
<string name="emulation_notification_channel_name">ئیمولەیشن کارایە</string>
<string name="emulation_notification_channel_description">ئاگادارکردنەوەیەکی بەردەوام نیشان دەدات کاتێک ئیمولەیشن کاردەکات.</string>
<string name="emulation_notification_running">یوزو کاردەکات</string>
<string name="notice_notification_channel_name">ئاگاداری و هەڵەکان</string> <string name="notice_notification_channel_name">ئاگاداری و هەڵەکان</string>
<string name="notice_notification_channel_description">ئاگادارکردنەوەکان پیشان دەدات کاتێک شتێک بە هەڵەدا دەچێت.</string> <string name="notice_notification_channel_description">ئاگادارکردنەوەکان پیشان دەدات کاتێک شتێک بە هەڵەدا دەچێت.</string>
<string name="notification_permission_not_granted">مۆڵەتی ئاگادارکردنەوە نەدراوە!</string> <string name="notification_permission_not_granted">مۆڵەتی ئاگادارکردنەوە نەدراوە!</string>

View File

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<string name="emulation_notification_channel_name">Emulace je aktivní</string>
<string name="notice_notification_channel_name">Upozornění a chyby</string> <string name="notice_notification_channel_name">Upozornění a chyby</string>
<string name="notice_notification_channel_description">Ukáže oznámení v případě chyby.</string> <string name="notice_notification_channel_description">Ukáže oznámení v případě chyby.</string>
<string name="notification_permission_not_granted">Oznámení nejsou oprávněna!</string> <string name="notification_permission_not_granted">Oznámení nejsou oprávněna!</string>

View File

@ -2,9 +2,6 @@
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<string name="app_disclaimer">Diese Software kann Spiele für die Nintendo Switch abspielen. Keine Spiele oder Spielekeys sind enthalten.&lt;br /&gt;&lt;br /&gt;Bevor du beginnst, bitte halte deine <![CDATA[<b> prod.keys </b>]]> auf deinem Gerät bereit. .&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Mehr Infos</a>]]></string> <string name="app_disclaimer">Diese Software kann Spiele für die Nintendo Switch abspielen. Keine Spiele oder Spielekeys sind enthalten.&lt;br /&gt;&lt;br /&gt;Bevor du beginnst, bitte halte deine <![CDATA[<b> prod.keys </b>]]> auf deinem Gerät bereit. .&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Mehr Infos</a>]]></string>
<string name="emulation_notification_channel_name">Emulation ist aktiv</string>
<string name="emulation_notification_channel_description">Zeigt eine dauerhafte Benachrichtigung an, wenn die Emulation läuft.</string>
<string name="emulation_notification_running">yuzu läuft</string>
<string name="notice_notification_channel_name">Hinweise und Fehler</string> <string name="notice_notification_channel_name">Hinweise und Fehler</string>
<string name="notice_notification_channel_description">Zeigt Benachrichtigungen an, wenn etwas schief läuft.</string> <string name="notice_notification_channel_description">Zeigt Benachrichtigungen an, wenn etwas schief läuft.</string>
<string name="notification_permission_not_granted">Berechtigung für Benachrichtigungen nicht erlaubt!</string> <string name="notification_permission_not_granted">Berechtigung für Benachrichtigungen nicht erlaubt!</string>

View File

@ -2,9 +2,6 @@
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<string name="app_disclaimer">Este software ejecuta juegos para la videoconsola Nintendo Switch. Los videojuegos o claves no vienen incluidos.&lt;br /&gt;&lt;br /&gt;Antes de empezar, por favor, localice el archivo <![CDATA[<b> prod.keys </b>]]>en el almacenamiento de su dispositivo..&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Saber más</a>]]></string> <string name="app_disclaimer">Este software ejecuta juegos para la videoconsola Nintendo Switch. Los videojuegos o claves no vienen incluidos.&lt;br /&gt;&lt;br /&gt;Antes de empezar, por favor, localice el archivo <![CDATA[<b> prod.keys </b>]]>en el almacenamiento de su dispositivo..&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Saber más</a>]]></string>
<string name="emulation_notification_channel_name">Emulación activa</string>
<string name="emulation_notification_channel_description">Muestra una notificación persistente cuando la emulación está activa.</string>
<string name="emulation_notification_running">yuzu está ejecutándose</string>
<string name="notice_notification_channel_name">Avisos y errores</string> <string name="notice_notification_channel_name">Avisos y errores</string>
<string name="notice_notification_channel_description">Mostrar notificaciones cuándo algo vaya mal.</string> <string name="notice_notification_channel_description">Mostrar notificaciones cuándo algo vaya mal.</string>
<string name="notification_permission_not_granted">¡Permisos de notificación no concedidos!</string> <string name="notification_permission_not_granted">¡Permisos de notificación no concedidos!</string>

View File

@ -2,9 +2,6 @@
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<string name="app_disclaimer">Ce logiciel exécutera des jeux pour la console de jeu Nintendo Switch. Aucun jeux ou clés n\'est inclus.&lt;br /&gt;&lt;br /&gt;Avant de commencer, veuillez localiser votre fichier <![CDATA[<b> prod.keys </b>]]> sur le stockage de votre appareil.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">En savoir plus</a>]]></string> <string name="app_disclaimer">Ce logiciel exécutera des jeux pour la console de jeu Nintendo Switch. Aucun jeux ou clés n\'est inclus.&lt;br /&gt;&lt;br /&gt;Avant de commencer, veuillez localiser votre fichier <![CDATA[<b> prod.keys </b>]]> sur le stockage de votre appareil.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">En savoir plus</a>]]></string>
<string name="emulation_notification_channel_name">L\'émulation est active</string>
<string name="emulation_notification_channel_description">Affiche une notification persistante lorsque l\'émulation est en cours d\'exécution.</string>
<string name="emulation_notification_running">yuzu est en cours d\'exécution</string>
<string name="notice_notification_channel_name">Avis et erreurs</string> <string name="notice_notification_channel_name">Avis et erreurs</string>
<string name="notice_notification_channel_description">Affiche des notifications en cas de problème.</string> <string name="notice_notification_channel_description">Affiche des notifications en cas de problème.</string>
<string name="notification_permission_not_granted">Permission de notification non accordée !</string> <string name="notification_permission_not_granted">Permission de notification non accordée !</string>

View File

@ -2,9 +2,6 @@
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<string name="app_disclaimer">התוכנה תריץ משחקים לקונסולת ה Nintendo Switch. אף משחק או קבצים בעלי זכויות יוצרים נכללים.&lt;br /&gt;&lt;br /&gt; לפני שאת/ה מתחיל בבקשה מצא את קובץ <![CDATA[<b>prod.keys</b>]]> על המכשיר.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">קרא עוד</a>]]></string> <string name="app_disclaimer">התוכנה תריץ משחקים לקונסולת ה Nintendo Switch. אף משחק או קבצים בעלי זכויות יוצרים נכללים.&lt;br /&gt;&lt;br /&gt; לפני שאת/ה מתחיל בבקשה מצא את קובץ <![CDATA[<b>prod.keys</b>]]> על המכשיר.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">קרא עוד</a>]]></string>
<string name="emulation_notification_channel_name">אמולציה פעילה</string>
<string name="emulation_notification_channel_description">מציג התראה מתמשכת כאשר האמולציה פועלת.</string>
<string name="emulation_notification_running">yuzu רץ</string>
<string name="notice_notification_channel_name">התראות ותקלות</string> <string name="notice_notification_channel_name">התראות ותקלות</string>
<string name="notice_notification_channel_description">מציג התראות כאשר משהו הולך לא כשורה.</string> <string name="notice_notification_channel_description">מציג התראות כאשר משהו הולך לא כשורה.</string>
<string name="notification_permission_not_granted">הרשאות התראות לא ניתנה!</string> <string name="notification_permission_not_granted">הרשאות התראות לא ניתנה!</string>

View File

@ -2,9 +2,6 @@
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<string name="app_disclaimer">Ez a szoftver Nintendo Switch játékkonzolhoz készült játékokat futtat. Nem tartalmaz játékokat vagy kulcsokat. .&lt;br /&gt;&lt;br /&gt;Mielőtt hozzákezdenél, kérjük, válaszd ki a <![CDATA[<b>prod.keys</b>]]> fájl helyét a készülék tárhelyén&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Tudj meg többet</a>]]></string> <string name="app_disclaimer">Ez a szoftver Nintendo Switch játékkonzolhoz készült játékokat futtat. Nem tartalmaz játékokat vagy kulcsokat. .&lt;br /&gt;&lt;br /&gt;Mielőtt hozzákezdenél, kérjük, válaszd ki a <![CDATA[<b>prod.keys</b>]]> fájl helyét a készülék tárhelyén&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Tudj meg többet</a>]]></string>
<string name="emulation_notification_channel_name">Emuláció aktív</string>
<string name="emulation_notification_channel_description">Állandó értesítést jelenít meg, amíg az emuláció fut.</string>
<string name="emulation_notification_running">A yuzu fut</string>
<string name="notice_notification_channel_name">Megjegyzések és hibák</string> <string name="notice_notification_channel_name">Megjegyzések és hibák</string>
<string name="notice_notification_channel_description">Értesítések megjelenítése, ha valami rosszul sül el.</string> <string name="notice_notification_channel_description">Értesítések megjelenítése, ha valami rosszul sül el.</string>
<string name="notification_permission_not_granted">Nincs engedély az értesítés megjelenítéséhez!</string> <string name="notification_permission_not_granted">Nincs engedély az értesítés megjelenítéséhez!</string>

View File

@ -2,9 +2,6 @@
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<string name="app_disclaimer">Questo software permette di giocare ai giochi della console Nintendo Switch. Nessun gioco o chiave è inclusa.&lt;br /&gt;&lt;br /&gt;Prima di iniziare, perfavore individua il file <![CDATA[<b>prod.keys </b>]]> nella memoria del tuo dispositivo.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Scopri di più</a>]]></string> <string name="app_disclaimer">Questo software permette di giocare ai giochi della console Nintendo Switch. Nessun gioco o chiave è inclusa.&lt;br /&gt;&lt;br /&gt;Prima di iniziare, perfavore individua il file <![CDATA[<b>prod.keys </b>]]> nella memoria del tuo dispositivo.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Scopri di più</a>]]></string>
<string name="emulation_notification_channel_name">L\'emulatore è attivo</string>
<string name="emulation_notification_channel_description">Mostra una notifica persistente quando l\'emulatore è in esecuzione.</string>
<string name="emulation_notification_running">yuzu è in esecuzione</string>
<string name="notice_notification_channel_name">Avvisi ed errori</string> <string name="notice_notification_channel_name">Avvisi ed errori</string>
<string name="notice_notification_channel_description">Mostra le notifiche quando qualcosa va storto.</string> <string name="notice_notification_channel_description">Mostra le notifiche quando qualcosa va storto.</string>
<string name="notification_permission_not_granted">Autorizzazione di notifica non concessa!</string> <string name="notification_permission_not_granted">Autorizzazione di notifica non concessa!</string>

View File

@ -2,9 +2,6 @@
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<string name="app_disclaimer">このソフトウェアでは、Nintendo Switchのゲームを実行できます。 ゲームソフトやキーは含まれません。&lt;br /&gt;&lt;br /&gt;事前に、 <![CDATA[<b> prod.keys </b>]]> ファイルをストレージに配置しておいてください。&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">詳細</a>]]></string> <string name="app_disclaimer">このソフトウェアでは、Nintendo Switchのゲームを実行できます。 ゲームソフトやキーは含まれません。&lt;br /&gt;&lt;br /&gt;事前に、 <![CDATA[<b> prod.keys </b>]]> ファイルをストレージに配置しておいてください。&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">詳細</a>]]></string>
<string name="emulation_notification_channel_name">エミュレーションが有効です</string>
<string name="emulation_notification_channel_description">エミュレーションの実行中に常設通知を表示します。</string>
<string name="emulation_notification_running">yuzu は実行中です</string>
<string name="notice_notification_channel_name">通知とエラー</string> <string name="notice_notification_channel_name">通知とエラー</string>
<string name="notice_notification_channel_description">問題の発生時に通知を表示します。</string> <string name="notice_notification_channel_description">問題の発生時に通知を表示します。</string>
<string name="notification_permission_not_granted">通知が許可されていません!</string> <string name="notification_permission_not_granted">通知が許可されていません!</string>

View File

@ -2,9 +2,6 @@
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<string name="app_disclaimer">이 소프트웨어는 Nintendo Switch 게임을 실행합니다. 게임 타이틀이나 키는 포함되어 있지 않습니다.&lt;br /&gt;&lt;br /&gt;시작하기 전에 장치 저장소에서 <![CDATA[<b> prod.keys </b>]]> 파일을 찾아주세요.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">자세히 알아보기</a>]]></string> <string name="app_disclaimer">이 소프트웨어는 Nintendo Switch 게임을 실행합니다. 게임 타이틀이나 키는 포함되어 있지 않습니다.&lt;br /&gt;&lt;br /&gt;시작하기 전에 장치 저장소에서 <![CDATA[<b> prod.keys </b>]]> 파일을 찾아주세요.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">자세히 알아보기</a>]]></string>
<string name="emulation_notification_channel_name">에뮬레이션이 활성화됨</string>
<string name="emulation_notification_channel_description">에뮬레이션이 실행 중일 때 지속적으로 알림을 표시합니다.</string>
<string name="emulation_notification_running">yuzu가 실행 중입니다.</string>
<string name="notice_notification_channel_name">알림 및 오류</string> <string name="notice_notification_channel_name">알림 및 오류</string>
<string name="notice_notification_channel_description">문제가 발생하면 알림을 표시합니다.</string> <string name="notice_notification_channel_description">문제가 발생하면 알림을 표시합니다.</string>
<string name="notification_permission_not_granted">알림 권한이 부여되지 않았습니다!</string> <string name="notification_permission_not_granted">알림 권한이 부여되지 않았습니다!</string>

View File

@ -2,9 +2,6 @@
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<string name="app_disclaimer">Denne programvaren vil kjøre spill for Nintendo Switch-spillkonsollen. Ingen spilltitler eller nøkler er inkludert.&lt;br /&gt;&lt;br /&gt;Før du begynner, må du finne <![CDATA[<b> prod.keys </b>]]> filen din på enhetslagringen.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Lær mer</a>]]></string> <string name="app_disclaimer">Denne programvaren vil kjøre spill for Nintendo Switch-spillkonsollen. Ingen spilltitler eller nøkler er inkludert.&lt;br /&gt;&lt;br /&gt;Før du begynner, må du finne <![CDATA[<b> prod.keys </b>]]> filen din på enhetslagringen.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Lær mer</a>]]></string>
<string name="emulation_notification_channel_name">Emulering er aktiv</string>
<string name="emulation_notification_channel_description">Viser et vedvarende varsel når emuleringen kjører.</string>
<string name="emulation_notification_running">Yuzu kjører</string>
<string name="notice_notification_channel_name">Merknader og feil</string> <string name="notice_notification_channel_name">Merknader og feil</string>
<string name="notice_notification_channel_description">Viser varsler når noe går galt.</string> <string name="notice_notification_channel_description">Viser varsler når noe går galt.</string>
<string name="notification_permission_not_granted">Varslingstillatelse ikke gitt!</string> <string name="notification_permission_not_granted">Varslingstillatelse ikke gitt!</string>

View File

@ -2,9 +2,6 @@
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<string name="app_disclaimer">To oprogramowanie umożliwia uruchomienie gier z konsoli Nintendo Switch. Nie zawiera gier ani wymaganych kluczy.&lt;br /&gt;&lt;br /&gt;Zanim zaczniesz, wybierz plik kluczy <![CDATA[<b> prod.keys </b>]]> z katalogu w pamięci masowej.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Dowiedz się więcej</a>]]></string> <string name="app_disclaimer">To oprogramowanie umożliwia uruchomienie gier z konsoli Nintendo Switch. Nie zawiera gier ani wymaganych kluczy.&lt;br /&gt;&lt;br /&gt;Zanim zaczniesz, wybierz plik kluczy <![CDATA[<b> prod.keys </b>]]> z katalogu w pamięci masowej.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Dowiedz się więcej</a>]]></string>
<string name="emulation_notification_channel_name">Emulacja jest uruchomiona</string>
<string name="emulation_notification_channel_description">Pokaż trwałe powiadomienie gdy emulacja jest uruchomiona.</string>
<string name="emulation_notification_running">yuzu jest uruchomiony</string>
<string name="notice_notification_channel_name">Powiadomienia błędy</string> <string name="notice_notification_channel_name">Powiadomienia błędy</string>
<string name="notice_notification_channel_description">Pokaż powiadomienie gdy coś pójdzie źle</string> <string name="notice_notification_channel_description">Pokaż powiadomienie gdy coś pójdzie źle</string>
<string name="notification_permission_not_granted">Nie zezwolono na powiadomienia!</string> <string name="notification_permission_not_granted">Nie zezwolono na powiadomienia!</string>

View File

@ -2,9 +2,6 @@
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<string name="app_disclaimer">Este software executa jogos do console Nintendo Switch. Não estão inclusos nem jogos ou chaves.&lt;br /&gt;&lt;br /&gt;Antes de começar, por favor localize o arquivo <![CDATA[<b> prod.keys </b>]]> no armazenamento de seu dispositivo.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Saiba mais</a>]]></string> <string name="app_disclaimer">Este software executa jogos do console Nintendo Switch. Não estão inclusos nem jogos ou chaves.&lt;br /&gt;&lt;br /&gt;Antes de começar, por favor localize o arquivo <![CDATA[<b> prod.keys </b>]]> no armazenamento de seu dispositivo.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Saiba mais</a>]]></string>
<string name="emulation_notification_channel_name">A emulação está Ativa</string>
<string name="emulation_notification_channel_description">Mostra uma notificação permanente enquanto a emulação estiver em andamento.</string>
<string name="emulation_notification_running">O Yuzu está em execução </string>
<string name="notice_notification_channel_name">Notificações e erros</string> <string name="notice_notification_channel_name">Notificações e erros</string>
<string name="notice_notification_channel_description">Mostra notificações quando algo dá errado.</string> <string name="notice_notification_channel_description">Mostra notificações quando algo dá errado.</string>
<string name="notification_permission_not_granted">Acesso às notificações não concedido!</string> <string name="notification_permission_not_granted">Acesso às notificações não concedido!</string>

View File

@ -2,9 +2,6 @@
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<string name="app_disclaimer">Este software corre jogos para a consola Nintendo Switch. Não estão incluídas nem jogos ou chaves. &lt;br /&gt;&lt;br /&gt;Antes de começares, por favor localiza o ficheiro <![CDATA[1 prod.keys 1]]> no armazenamento do teu dispositivo.&lt;br /&gt;&lt;br /&gt;<![CDATA[2Learn more2]]></string> <string name="app_disclaimer">Este software corre jogos para a consola Nintendo Switch. Não estão incluídas nem jogos ou chaves. &lt;br /&gt;&lt;br /&gt;Antes de começares, por favor localiza o ficheiro <![CDATA[1 prod.keys 1]]> no armazenamento do teu dispositivo.&lt;br /&gt;&lt;br /&gt;<![CDATA[2Learn more2]]></string>
<string name="emulation_notification_channel_name">Emulação está Ativa</string>
<string name="emulation_notification_channel_description">Mostra uma notificação permanente enquanto a emulação está a correr.</string>
<string name="emulation_notification_running">Yuzu está em execução </string>
<string name="notice_notification_channel_name">Notificações e erros</string> <string name="notice_notification_channel_name">Notificações e erros</string>
<string name="notice_notification_channel_description">Mostra notificações quendo algo corre mal.</string> <string name="notice_notification_channel_description">Mostra notificações quendo algo corre mal.</string>
<string name="notification_permission_not_granted">Permissões de notificação não permitidas </string> <string name="notification_permission_not_granted">Permissões de notificação não permitidas </string>

View File

@ -2,9 +2,6 @@
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<string name="app_disclaimer">Это программное обеспечение позволяет запускать игры для игровой консоли Nintendo Switch. Мы не предоставляем сами игры или ключи.&lt;br /&gt;&lt;br /&gt;Перед началом работы найдите файл <![CDATA[<b> prod.keys </b>]]> в хранилище устройства..&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Узнать больше</a>]]></string> <string name="app_disclaimer">Это программное обеспечение позволяет запускать игры для игровой консоли Nintendo Switch. Мы не предоставляем сами игры или ключи.&lt;br /&gt;&lt;br /&gt;Перед началом работы найдите файл <![CDATA[<b> prod.keys </b>]]> в хранилище устройства..&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Узнать больше</a>]]></string>
<string name="emulation_notification_channel_name">Эмуляция активна</string>
<string name="emulation_notification_channel_description">Показывает постоянное уведомление, когда запущена эмуляция.</string>
<string name="emulation_notification_running">yuzu запущен</string>
<string name="notice_notification_channel_name">Уведомления и ошибки</string> <string name="notice_notification_channel_name">Уведомления и ошибки</string>
<string name="notice_notification_channel_description">Показывать уведомления, когда что-то пошло не так</string> <string name="notice_notification_channel_description">Показывать уведомления, когда что-то пошло не так</string>
<string name="notification_permission_not_granted">Вы не предоставили разрешение на уведомления!</string> <string name="notification_permission_not_granted">Вы не предоставили разрешение на уведомления!</string>

View File

@ -2,9 +2,6 @@
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<string name="app_disclaimer">Це програмне забезпечення дозволяє запускати ігри для ігрової консолі Nintendo Switch. Ми не надаємо самі ігри або ключі.&lt;br /&gt;&lt;br /&gt;Перед початком роботи знайдіть ваш файл <![CDATA[<b> prod.keys </b>]]> у сховищі пристрою.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Дізнатися більше</a>]]></string> <string name="app_disclaimer">Це програмне забезпечення дозволяє запускати ігри для ігрової консолі Nintendo Switch. Ми не надаємо самі ігри або ключі.&lt;br /&gt;&lt;br /&gt;Перед початком роботи знайдіть ваш файл <![CDATA[<b> prod.keys </b>]]> у сховищі пристрою.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Дізнатися більше</a>]]></string>
<string name="emulation_notification_channel_name">Емуляція активна</string>
<string name="emulation_notification_channel_description">Показує постійне сповіщення, коли запущено емуляцію.</string>
<string name="emulation_notification_running">yuzu запущено</string>
<string name="notice_notification_channel_name">Сповіщення та помилки</string> <string name="notice_notification_channel_name">Сповіщення та помилки</string>
<string name="notice_notification_channel_description">Показувати сповіщення, коли щось пішло не так</string> <string name="notice_notification_channel_description">Показувати сповіщення, коли щось пішло не так</string>
<string name="notification_permission_not_granted">Ви не надали дозвіл сповіщень!</string> <string name="notification_permission_not_granted">Ви не надали дозвіл сповіщень!</string>

View File

@ -2,9 +2,6 @@
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<string name="app_disclaimer">Phần mềm này sẽ chạy các game cho máy chơi game Nintendo Switch. Không có title games hoặc keys được bao gồm.&lt;br /&gt;&lt;br /&gt;Trước khi bạn bắt đầu, hãy tìm tập tin <![CDATA[<b> prod.keys </b>]]> trên bộ nhớ thiết bị của bạn.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Tìm hiểu thêm</a>]]></string> <string name="app_disclaimer">Phần mềm này sẽ chạy các game cho máy chơi game Nintendo Switch. Không có title games hoặc keys được bao gồm.&lt;br /&gt;&lt;br /&gt;Trước khi bạn bắt đầu, hãy tìm tập tin <![CDATA[<b> prod.keys </b>]]> trên bộ nhớ thiết bị của bạn.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">Tìm hiểu thêm</a>]]></string>
<string name="emulation_notification_channel_name">Giả lập đang chạy</string>
<string name="emulation_notification_channel_description">Hiển thị thông báo liên tục khi giả lập đang chạy.</string>
<string name="emulation_notification_running">yuzu đang chạy</string>
<string name="notice_notification_channel_name">Thông báo và lỗi</string> <string name="notice_notification_channel_name">Thông báo và lỗi</string>
<string name="notice_notification_channel_description">Hiển thị thông báo khi có sự cố xảy ra.</string> <string name="notice_notification_channel_description">Hiển thị thông báo khi có sự cố xảy ra.</string>
<string name="notification_permission_not_granted">Ứng dụng không được cấp quyền thông báo!</string> <string name="notification_permission_not_granted">Ứng dụng không được cấp quyền thông báo!</string>

View File

@ -2,9 +2,6 @@
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<string name="app_disclaimer">此软件可以运行 Nintendo Switch 游戏,但不包含任何游戏和密钥文件。&lt;br /&gt;&lt;br /&gt;在开始前,请找到放置于设备存储中的 <![CDATA[<b> prod.keys </b>]]> 文件。&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">了解更多</a>]]></string> <string name="app_disclaimer">此软件可以运行 Nintendo Switch 游戏,但不包含任何游戏和密钥文件。&lt;br /&gt;&lt;br /&gt;在开始前,请找到放置于设备存储中的 <![CDATA[<b> prod.keys </b>]]> 文件。&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">了解更多</a>]]></string>
<string name="emulation_notification_channel_name">正在进行模拟</string>
<string name="emulation_notification_channel_description">在模拟运行时显示持久通知。</string>
<string name="emulation_notification_running">yuzu 正在运行</string>
<string name="notice_notification_channel_name">通知及错误提醒</string> <string name="notice_notification_channel_name">通知及错误提醒</string>
<string name="notice_notification_channel_description">当发生错误时显示通知。</string> <string name="notice_notification_channel_description">当发生错误时显示通知。</string>
<string name="notification_permission_not_granted">未授予通知权限!</string> <string name="notification_permission_not_granted">未授予通知权限!</string>

View File

@ -2,9 +2,6 @@
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<string name="app_disclaimer">此軟體可以執行 Nintendo Switch 主機遊戲,但不包含任何遊戲和金鑰。&lt;br /&gt;&lt;br /&gt;在您開始前,請找到放置於您的裝置儲存空間的 <![CDATA[<b> prod.keys </b>]]> 檔案。&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">深入瞭解</a>]]></string> <string name="app_disclaimer">此軟體可以執行 Nintendo Switch 主機遊戲,但不包含任何遊戲和金鑰。&lt;br /&gt;&lt;br /&gt;在您開始前,請找到放置於您的裝置儲存空間的 <![CDATA[<b> prod.keys </b>]]> 檔案。&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-emu.org/help/quickstart\">深入瞭解</a>]]></string>
<string name="emulation_notification_channel_name">模擬進行中</string>
<string name="emulation_notification_channel_description">在模擬執行時顯示持續通知。</string>
<string name="emulation_notification_running">yuzu 正在執行</string>
<string name="notice_notification_channel_name">通知和錯誤</string> <string name="notice_notification_channel_name">通知和錯誤</string>
<string name="notice_notification_channel_description">發生錯誤時顯示通知。</string> <string name="notice_notification_channel_description">發生錯誤時顯示通知。</string>
<string name="notification_permission_not_granted">未授予通知權限!</string> <string name="notification_permission_not_granted">未授予通知權限!</string>

View File

@ -292,4 +292,15 @@
<item>5</item> <item>5</item>
</integer-array> </integer-array>
<string-array name="verticalAlignmentEntries">
<item>@string/top</item>
<item>@string/center</item>
<item>@string/bottom</item>
</string-array>
<integer-array name="verticalAlignmentValues">
<item>1</item>
<item>0</item>
<item>2</item>
</integer-array>
</resources> </resources>

View File

@ -4,10 +4,6 @@
<!-- General application strings --> <!-- General application strings -->
<string name="app_name" translatable="false">yuzu</string> <string name="app_name" translatable="false">yuzu</string>
<string name="app_disclaimer">This software will run games for the Nintendo Switch game console. No game titles or keys are included.&lt;br /&gt;&lt;br /&gt;Before you begin, please locate your <![CDATA[<b> prod.keys </b>]]> file on your device storage.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href="https://yuzu-emu.org/help/quickstart">Learn more</a>]]></string> <string name="app_disclaimer">This software will run games for the Nintendo Switch game console. No game titles or keys are included.&lt;br /&gt;&lt;br /&gt;Before you begin, please locate your <![CDATA[<b> prod.keys </b>]]> file on your device storage.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href="https://yuzu-emu.org/help/quickstart">Learn more</a>]]></string>
<string name="emulation_notification_channel_name">Emulation is Active</string>
<string name="emulation_notification_channel_id" translatable="false">emulationIsActive</string>
<string name="emulation_notification_channel_description">Shows a persistent notification when emulation is running.</string>
<string name="emulation_notification_running">yuzu is running</string>
<string name="notice_notification_channel_name">Notices and errors</string> <string name="notice_notification_channel_name">Notices and errors</string>
<string name="notice_notification_channel_id" translatable="false">noticesAndErrors</string> <string name="notice_notification_channel_id" translatable="false">noticesAndErrors</string>
<string name="notice_notification_channel_description">Shows notifications when something goes wrong.</string> <string name="notice_notification_channel_description">Shows notifications when something goes wrong.</string>
@ -230,6 +226,8 @@
<string name="renderer_screen_layout">Orientation</string> <string name="renderer_screen_layout">Orientation</string>
<string name="renderer_aspect_ratio">Aspect ratio</string> <string name="renderer_aspect_ratio">Aspect ratio</string>
<string name="renderer_scaling_filter">Window adapting filter</string> <string name="renderer_scaling_filter">Window adapting filter</string>
<string name="fsr_sharpness">FSR sharpness</string>
<string name="fsr_sharpness_description">Determines how sharpened the image will look while using FSR\'s dynamic contrast</string>
<string name="renderer_anti_aliasing">Anti-aliasing method</string> <string name="renderer_anti_aliasing">Anti-aliasing method</string>
<string name="renderer_force_max_clock">Force maximum clocks (Adreno only)</string> <string name="renderer_force_max_clock">Force maximum clocks (Adreno only)</string>
<string name="renderer_force_max_clock_description">Forces the GPU to run at the maximum possible clocks (thermal constraints will still be applied).</string> <string name="renderer_force_max_clock_description">Forces the GPU to run at the maximum possible clocks (thermal constraints will still be applied).</string>
@ -380,6 +378,7 @@
<string name="emulation_exit">Exit emulation</string> <string name="emulation_exit">Exit emulation</string>
<string name="emulation_done">Done</string> <string name="emulation_done">Done</string>
<string name="emulation_fps_counter">FPS counter</string> <string name="emulation_fps_counter">FPS counter</string>
<string name="emulation_thermal_indicator">Thermal indicator</string>
<string name="emulation_toggle_controls">Toggle controls</string> <string name="emulation_toggle_controls">Toggle controls</string>
<string name="emulation_rel_stick_center">Relative stick center</string> <string name="emulation_rel_stick_center">Relative stick center</string>
<string name="emulation_dpad_slide">D-pad slide</string> <string name="emulation_dpad_slide">D-pad slide</string>
@ -561,6 +560,12 @@
<string name="mute">Mute</string> <string name="mute">Mute</string>
<string name="unmute">Unmute</string> <string name="unmute">Unmute</string>
<!-- Emulation vertical alignment -->
<string name="vertical_alignment">Vertical alignment</string>
<string name="top">Top</string>
<string name="center">Center</string>
<string name="bottom">Bottom</string>
<!-- Licenses screen strings --> <!-- Licenses screen strings -->
<string name="licenses">Licenses</string> <string name="licenses">Licenses</string>
<string name="license_fidelityfx_fsr" translatable="false">FidelityFX-FSR</string> <string name="license_fidelityfx_fsr" translatable="false">FidelityFX-FSR</string>

View File

@ -107,6 +107,8 @@ add_library(common STATIC
quaternion.h quaternion.h
range_map.h range_map.h
range_mutex.h range_mutex.h
range_sets.h
range_sets.inc
reader_writer_queue.h reader_writer_queue.h
ring_buffer.h ring_buffer.h
${CMAKE_CURRENT_BINARY_DIR}/scm_rev.cpp ${CMAKE_CURRENT_BINARY_DIR}/scm_rev.cpp
@ -121,6 +123,7 @@ add_library(common STATIC
settings_input.cpp settings_input.cpp
settings_input.h settings_input.h
settings_setting.h settings_setting.h
slot_vector.h
socket_types.h socket_types.h
spin_lock.cpp spin_lock.cpp
spin_lock.h spin_lock.h
@ -179,9 +182,15 @@ endif()
if(ANDROID) if(ANDROID)
target_sources(common target_sources(common
PRIVATE PUBLIC
fs/fs_android.cpp fs/fs_android.cpp
fs/fs_android.h fs/fs_android.h
android/android_common.cpp
android/android_common.h
android/id_cache.cpp
android/id_cache.h
android/applets/software_keyboard.cpp
android/applets/software_keyboard.h
) )
endif() endif()

View File

@ -1,15 +1,17 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include "jni/android_common/android_common.h" #include "android_common.h"
#include <string> #include <string>
#include <string_view> #include <string_view>
#include <jni.h> #include <jni.h>
#include "common/android/id_cache.h"
#include "common/string_util.h" #include "common/string_util.h"
#include "jni/id_cache.h"
namespace Common::Android {
std::string GetJString(JNIEnv* env, jstring jstr) { std::string GetJString(JNIEnv* env, jstring jstr) {
if (!jstr) { if (!jstr) {
@ -18,7 +20,8 @@ std::string GetJString(JNIEnv* env, jstring jstr) {
const jchar* jchars = env->GetStringChars(jstr, nullptr); const jchar* jchars = env->GetStringChars(jstr, nullptr);
const jsize length = env->GetStringLength(jstr); const jsize length = env->GetStringLength(jstr);
const std::u16string_view string_view(reinterpret_cast<const char16_t*>(jchars), length); const std::u16string_view string_view(reinterpret_cast<const char16_t*>(jchars),
static_cast<u32>(length));
const std::string converted_string = Common::UTF16ToUTF8(string_view); const std::string converted_string = Common::UTF16ToUTF8(string_view);
env->ReleaseStringChars(jstr, jchars); env->ReleaseStringChars(jstr, jchars);
@ -36,25 +39,27 @@ jstring ToJString(JNIEnv* env, std::u16string_view str) {
} }
double GetJDouble(JNIEnv* env, jobject jdouble) { double GetJDouble(JNIEnv* env, jobject jdouble) {
return env->GetDoubleField(jdouble, IDCache::GetDoubleValueField()); return env->GetDoubleField(jdouble, GetDoubleValueField());
} }
jobject ToJDouble(JNIEnv* env, double value) { jobject ToJDouble(JNIEnv* env, double value) {
return env->NewObject(IDCache::GetDoubleClass(), IDCache::GetDoubleConstructor(), value); return env->NewObject(GetDoubleClass(), GetDoubleConstructor(), value);
} }
s32 GetJInteger(JNIEnv* env, jobject jinteger) { s32 GetJInteger(JNIEnv* env, jobject jinteger) {
return env->GetIntField(jinteger, IDCache::GetIntegerValueField()); return env->GetIntField(jinteger, GetIntegerValueField());
} }
jobject ToJInteger(JNIEnv* env, s32 value) { jobject ToJInteger(JNIEnv* env, s32 value) {
return env->NewObject(IDCache::GetIntegerClass(), IDCache::GetIntegerConstructor(), value); return env->NewObject(GetIntegerClass(), GetIntegerConstructor(), value);
} }
bool GetJBoolean(JNIEnv* env, jobject jboolean) { bool GetJBoolean(JNIEnv* env, jobject jboolean) {
return env->GetBooleanField(jboolean, IDCache::GetBooleanValueField()); return env->GetBooleanField(jboolean, GetBooleanValueField());
} }
jobject ToJBoolean(JNIEnv* env, bool value) { jobject ToJBoolean(JNIEnv* env, bool value) {
return env->NewObject(IDCache::GetBooleanClass(), IDCache::GetBooleanConstructor(), value); return env->NewObject(GetBooleanClass(), GetBooleanConstructor(), value);
} }
} // namespace Common::Android

View File

@ -8,6 +8,8 @@
#include <jni.h> #include <jni.h>
#include "common/common_types.h" #include "common/common_types.h"
namespace Common::Android {
std::string GetJString(JNIEnv* env, jstring jstr); std::string GetJString(JNIEnv* env, jstring jstr);
jstring ToJString(JNIEnv* env, std::string_view str); jstring ToJString(JNIEnv* env, std::string_view str);
jstring ToJString(JNIEnv* env, std::u16string_view str); jstring ToJString(JNIEnv* env, std::u16string_view str);
@ -20,3 +22,5 @@ jobject ToJInteger(JNIEnv* env, s32 value);
bool GetJBoolean(JNIEnv* env, jobject jboolean); bool GetJBoolean(JNIEnv* env, jobject jboolean);
jobject ToJBoolean(JNIEnv* env, bool value); jobject ToJBoolean(JNIEnv* env, bool value);
} // namespace Common::Android

View File

@ -6,12 +6,12 @@
#include <jni.h> #include <jni.h>
#include "common/android/android_common.h"
#include "common/android/applets/software_keyboard.h"
#include "common/android/id_cache.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/string_util.h" #include "common/string_util.h"
#include "core/core.h" #include "core/core.h"
#include "jni/android_common/android_common.h"
#include "jni/applets/software_keyboard.h"
#include "jni/id_cache.h"
static jclass s_software_keyboard_class; static jclass s_software_keyboard_class;
static jclass s_keyboard_config_class; static jclass s_keyboard_config_class;
@ -19,10 +19,10 @@ static jclass s_keyboard_data_class;
static jmethodID s_swkbd_execute_normal; static jmethodID s_swkbd_execute_normal;
static jmethodID s_swkbd_execute_inline; static jmethodID s_swkbd_execute_inline;
namespace SoftwareKeyboard { namespace Common::Android::SoftwareKeyboard {
static jobject ToJKeyboardParams(const Core::Frontend::KeyboardInitializeParameters& config) { static jobject ToJKeyboardParams(const Core::Frontend::KeyboardInitializeParameters& config) {
JNIEnv* env = IDCache::GetEnvForThread(); JNIEnv* env = GetEnvForThread();
jobject object = env->AllocObject(s_keyboard_config_class); jobject object = env->AllocObject(s_keyboard_config_class);
env->SetObjectField(object, env->SetObjectField(object,
@ -78,7 +78,7 @@ static jobject ToJKeyboardParams(const Core::Frontend::KeyboardInitializeParamet
} }
AndroidKeyboard::ResultData AndroidKeyboard::ResultData::CreateFromFrontend(jobject object) { AndroidKeyboard::ResultData AndroidKeyboard::ResultData::CreateFromFrontend(jobject object) {
JNIEnv* env = IDCache::GetEnvForThread(); JNIEnv* env = GetEnvForThread();
const jstring string = reinterpret_cast<jstring>(env->GetObjectField( const jstring string = reinterpret_cast<jstring>(env->GetObjectField(
object, env->GetFieldID(s_keyboard_data_class, "text", "Ljava/lang/String;"))); object, env->GetFieldID(s_keyboard_data_class, "text", "Ljava/lang/String;")));
return ResultData{GetJString(env, string), return ResultData{GetJString(env, string),
@ -141,7 +141,7 @@ void AndroidKeyboard::ShowNormalKeyboard() const {
// Pivot to a new thread, as we cannot call GetEnvForThread() from a Fiber. // Pivot to a new thread, as we cannot call GetEnvForThread() from a Fiber.
std::thread([&] { std::thread([&] {
data = ResultData::CreateFromFrontend(IDCache::GetEnvForThread()->CallStaticObjectMethod( data = ResultData::CreateFromFrontend(GetEnvForThread()->CallStaticObjectMethod(
s_software_keyboard_class, s_swkbd_execute_normal, ToJKeyboardParams(parameters))); s_software_keyboard_class, s_swkbd_execute_normal, ToJKeyboardParams(parameters)));
}).join(); }).join();
@ -183,8 +183,8 @@ void AndroidKeyboard::ShowInlineKeyboard(
// Pivot to a new thread, as we cannot call GetEnvForThread() from a Fiber. // Pivot to a new thread, as we cannot call GetEnvForThread() from a Fiber.
m_is_inline_active = true; m_is_inline_active = true;
std::thread([&] { std::thread([&] {
IDCache::GetEnvForThread()->CallStaticVoidMethod( GetEnvForThread()->CallStaticVoidMethod(s_software_keyboard_class, s_swkbd_execute_inline,
s_software_keyboard_class, s_swkbd_execute_inline, ToJKeyboardParams(parameters)); ToJKeyboardParams(parameters));
}).join(); }).join();
} }
@ -220,7 +220,7 @@ void AndroidKeyboard::SubmitInlineKeyboardText(std::u16string submitted_text) {
m_current_text += submitted_text; m_current_text += submitted_text;
submit_inline_callback(Service::AM::Frontend::SwkbdReplyType::ChangedString, m_current_text, submit_inline_callback(Service::AM::Frontend::SwkbdReplyType::ChangedString, m_current_text,
m_current_text.size()); static_cast<int>(m_current_text.size()));
} }
void AndroidKeyboard::SubmitInlineKeyboardInput(int key_code) { void AndroidKeyboard::SubmitInlineKeyboardInput(int key_code) {
@ -242,7 +242,7 @@ void AndroidKeyboard::SubmitInlineKeyboardInput(int key_code) {
case KEYCODE_DEL: case KEYCODE_DEL:
m_current_text.pop_back(); m_current_text.pop_back();
submit_inline_callback(Service::AM::Frontend::SwkbdReplyType::ChangedString, m_current_text, submit_inline_callback(Service::AM::Frontend::SwkbdReplyType::ChangedString, m_current_text,
m_current_text.size()); static_cast<int>(m_current_text.size()));
break; break;
} }
} }
@ -274,4 +274,4 @@ void CleanupJNI(JNIEnv* env) {
env->DeleteGlobalRef(s_keyboard_data_class); env->DeleteGlobalRef(s_keyboard_data_class);
} }
} // namespace SoftwareKeyboard } // namespace Common::Android::SoftwareKeyboard

View File

@ -7,7 +7,7 @@
#include "core/frontend/applets/software_keyboard.h" #include "core/frontend/applets/software_keyboard.h"
namespace SoftwareKeyboard { namespace Common::Android::SoftwareKeyboard {
class AndroidKeyboard final : public Core::Frontend::SoftwareKeyboardApplet { class AndroidKeyboard final : public Core::Frontend::SoftwareKeyboardApplet {
public: public:
@ -66,7 +66,7 @@ void InitJNI(JNIEnv* env);
// Should be called in JNI_Unload // Should be called in JNI_Unload
void CleanupJNI(JNIEnv* env); void CleanupJNI(JNIEnv* env);
} // namespace SoftwareKeyboard } // namespace Common::Android::SoftwareKeyboard
// Native function calls // Native function calls
extern "C" { extern "C" {

View File

@ -3,10 +3,10 @@
#include <jni.h> #include <jni.h>
#include "applets/software_keyboard.h"
#include "common/android/id_cache.h"
#include "common/assert.h" #include "common/assert.h"
#include "common/fs/fs_android.h" #include "common/fs/fs_android.h"
#include "jni/applets/software_keyboard.h"
#include "jni/id_cache.h"
#include "video_core/rasterizer_interface.h" #include "video_core/rasterizer_interface.h"
static JavaVM* s_java_vm; static JavaVM* s_java_vm;
@ -67,7 +67,7 @@ static jfieldID s_boolean_value_field;
static constexpr jint JNI_VERSION = JNI_VERSION_1_6; static constexpr jint JNI_VERSION = JNI_VERSION_1_6;
namespace IDCache { namespace Common::Android {
JNIEnv* GetEnvForThread() { JNIEnv* GetEnvForThread() {
thread_local static struct OwnedEnv { thread_local static struct OwnedEnv {
@ -276,8 +276,6 @@ jfieldID GetBooleanValueField() {
return s_boolean_value_field; return s_boolean_value_field;
} }
} // namespace IDCache
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
@ -393,7 +391,7 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
Common::FS::Android::RegisterCallbacks(env, s_native_library_class); Common::FS::Android::RegisterCallbacks(env, s_native_library_class);
// Initialize applets // Initialize applets
SoftwareKeyboard::InitJNI(env); Common::Android::SoftwareKeyboard::InitJNI(env);
return JNI_VERSION; return JNI_VERSION;
} }
@ -426,3 +424,5 @@ void JNI_OnUnload(JavaVM* vm, void* reserved) {
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif
} // namespace Common::Android

View File

@ -3,20 +3,40 @@
#pragma once #pragma once
#include <future>
#include <jni.h> #include <jni.h>
#include "video_core/rasterizer_interface.h" #include "video_core/rasterizer_interface.h"
namespace IDCache { namespace Common::Android {
JNIEnv* GetEnvForThread(); JNIEnv* GetEnvForThread();
/**
* Starts a new thread to run JNI. Intended to be used when you must run JNI from a fiber.
* @tparam T Typename of the return value for the work param
* @param work Lambda that runs JNI code. This function will take care of attaching this thread to
* the JVM
* @return The result from the work lambda param
*/
template <typename T = void>
T RunJNIOnFiber(const std::function<T(JNIEnv*)>& work) {
std::future<T> j_result = std::async(std::launch::async, [&] {
auto env = GetEnvForThread();
return work(env);
});
return j_result.get();
}
jclass GetNativeLibraryClass(); jclass GetNativeLibraryClass();
jclass GetDiskCacheProgressClass(); jclass GetDiskCacheProgressClass();
jclass GetDiskCacheLoadCallbackStageClass(); jclass GetDiskCacheLoadCallbackStageClass();
jclass GetGameDirClass(); jclass GetGameDirClass();
jmethodID GetGameDirConstructor(); jmethodID GetGameDirConstructor();
jmethodID GetExitEmulationActivity();
jmethodID GetDiskCacheLoadProgress(); jmethodID GetDiskCacheLoadProgress();
jmethodID GetExitEmulationActivity();
jmethodID GetOnEmulationStarted(); jmethodID GetOnEmulationStarted();
jmethodID GetOnEmulationStopped(); jmethodID GetOnEmulationStopped();
jmethodID GetOnProgramChanged(); jmethodID GetOnProgramChanged();
@ -65,4 +85,4 @@ jclass GetBooleanClass();
jmethodID GetBooleanConstructor(); jmethodID GetBooleanConstructor();
jfieldID GetBooleanValueField(); jfieldID GetBooleanValueField();
} // namespace IDCache } // namespace Common::Android

View File

@ -1,63 +1,38 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include "common/android/android_common.h"
#include "common/android/id_cache.h"
#include "common/assert.h"
#include "common/fs/fs_android.h" #include "common/fs/fs_android.h"
#include "common/string_util.h" #include "common/string_util.h"
namespace Common::FS::Android { namespace Common::FS::Android {
JNIEnv* GetEnvForThread() {
thread_local static struct OwnedEnv {
OwnedEnv() {
status = g_jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);
if (status == JNI_EDETACHED)
g_jvm->AttachCurrentThread(&env, nullptr);
}
~OwnedEnv() {
if (status == JNI_EDETACHED)
g_jvm->DetachCurrentThread();
}
int status;
JNIEnv* env = nullptr;
} owned;
return owned.env;
}
void RegisterCallbacks(JNIEnv* env, jclass clazz) { void RegisterCallbacks(JNIEnv* env, jclass clazz) {
env->GetJavaVM(&g_jvm); env->GetJavaVM(&g_jvm);
native_library = clazz; native_library = clazz;
#define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) \ s_get_parent_directory = env->GetStaticMethodID(native_library, "getParentDirectory",
F(JMethodID, JMethodName, Signature) "(Ljava/lang/String;)Ljava/lang/String;");
#define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) \ s_get_filename = env->GetStaticMethodID(native_library, "getFilename",
F(JMethodID, JMethodName, Signature) "(Ljava/lang/String;)Ljava/lang/String;");
#define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) \ s_get_size = env->GetStaticMethodID(native_library, "getSize", "(Ljava/lang/String;)J");
F(JMethodID, JMethodName, Signature) s_is_directory = env->GetStaticMethodID(native_library, "isDirectory", "(Ljava/lang/String;)Z");
#define F(JMethodID, JMethodName, Signature) \ s_file_exists = env->GetStaticMethodID(native_library, "exists", "(Ljava/lang/String;)Z");
JMethodID = env->GetStaticMethodID(native_library, JMethodName, Signature); s_open_content_uri = env->GetStaticMethodID(native_library, "openContentUri",
ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH) "(Ljava/lang/String;Ljava/lang/String;)I");
ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
ANDROID_STORAGE_FUNCTIONS(FS)
#undef F
#undef FS
#undef FR
#undef FH
} }
void UnRegisterCallbacks() { void UnRegisterCallbacks() {
#define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) F(JMethodID) s_get_parent_directory = nullptr;
#define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) F(JMethodID) s_get_filename = nullptr;
#define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) F(JMethodID)
#define F(JMethodID) JMethodID = nullptr; s_get_size = nullptr;
ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH) s_is_directory = nullptr;
ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR) s_file_exists = nullptr;
ANDROID_STORAGE_FUNCTIONS(FS)
#undef F s_open_content_uri = nullptr;
#undef FS
#undef FR
#undef FH
} }
bool IsContentUri(const std::string& path) { bool IsContentUri(const std::string& path) {
@ -69,8 +44,8 @@ bool IsContentUri(const std::string& path) {
return path.find(prefix) == 0; return path.find(prefix) == 0;
} }
int OpenContentUri(const std::string& filepath, OpenMode openmode) { s32 OpenContentUri(const std::string& filepath, OpenMode openmode) {
if (open_content_uri == nullptr) if (s_open_content_uri == nullptr)
return -1; return -1;
const char* mode = ""; const char* mode = "";
@ -82,50 +57,66 @@ int OpenContentUri(const std::string& filepath, OpenMode openmode) {
UNIMPLEMENTED(); UNIMPLEMENTED();
return -1; return -1;
} }
auto env = GetEnvForThread(); auto env = Common::Android::GetEnvForThread();
jstring j_filepath = env->NewStringUTF(filepath.c_str()); jstring j_filepath = Common::Android::ToJString(env, filepath);
jstring j_mode = env->NewStringUTF(mode); jstring j_mode = Common::Android::ToJString(env, mode);
return env->CallStaticIntMethod(native_library, open_content_uri, j_filepath, j_mode); return env->CallStaticIntMethod(native_library, s_open_content_uri, j_filepath, j_mode);
} }
#define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) \ u64 GetSize(const std::string& filepath) {
F(FunctionName, ReturnValue, JMethodID, Caller) if (s_get_size == nullptr) {
#define F(FunctionName, ReturnValue, JMethodID, Caller) \ return 0;
ReturnValue FunctionName(const std::string& filepath) { \
if (JMethodID == nullptr) { \
return 0; \
} \
auto env = GetEnvForThread(); \
jstring j_filepath = env->NewStringUTF(filepath.c_str()); \
return env->Caller(native_library, JMethodID, j_filepath); \
} }
ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR) auto env = Common::Android::GetEnvForThread();
#undef F return static_cast<u64>(env->CallStaticLongMethod(
#undef FR native_library, s_get_size,
Common::Android::ToJString(Common::Android::GetEnvForThread(), filepath)));
}
#define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) \ bool IsDirectory(const std::string& filepath) {
F(FunctionName, JMethodID, Caller) if (s_is_directory == nullptr) {
#define F(FunctionName, JMethodID, Caller) \ return 0;
std::string FunctionName(const std::string& filepath) { \
if (JMethodID == nullptr) { \
return 0; \
} \
auto env = GetEnvForThread(); \
jstring j_filepath = env->NewStringUTF(filepath.c_str()); \
jstring j_return = \
static_cast<jstring>(env->Caller(native_library, JMethodID, j_filepath)); \
if (!j_return) { \
return {}; \
} \
const jchar* jchars = env->GetStringChars(j_return, nullptr); \
const jsize length = env->GetStringLength(j_return); \
const std::u16string_view string_view(reinterpret_cast<const char16_t*>(jchars), length); \
const std::string converted_string = Common::UTF16ToUTF8(string_view); \
env->ReleaseStringChars(j_return, jchars); \
return converted_string; \
} }
ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH) auto env = Common::Android::GetEnvForThread();
#undef F return env->CallStaticBooleanMethod(
#undef FH native_library, s_is_directory,
Common::Android::ToJString(Common::Android::GetEnvForThread(), filepath));
}
bool Exists(const std::string& filepath) {
if (s_file_exists == nullptr) {
return 0;
}
auto env = Common::Android::GetEnvForThread();
return env->CallStaticBooleanMethod(
native_library, s_file_exists,
Common::Android::ToJString(Common::Android::GetEnvForThread(), filepath));
}
std::string GetParentDirectory(const std::string& filepath) {
if (s_get_parent_directory == nullptr) {
return 0;
}
auto env = Common::Android::GetEnvForThread();
jstring j_return = static_cast<jstring>(env->CallStaticObjectMethod(
native_library, s_get_parent_directory, Common::Android::ToJString(env, filepath)));
if (!j_return) {
return {};
}
return Common::Android::GetJString(env, j_return);
}
std::string GetFilename(const std::string& filepath) {
if (s_get_filename == nullptr) {
return 0;
}
auto env = Common::Android::GetEnvForThread();
jstring j_return = static_cast<jstring>(env->CallStaticObjectMethod(
native_library, s_get_filename, Common::Android::ToJString(env, filepath)));
if (!j_return) {
return {};
}
return Common::Android::GetJString(env, j_return);
}
} // namespace Common::FS::Android } // namespace Common::FS::Android

View File

@ -7,38 +7,17 @@
#include <vector> #include <vector>
#include <jni.h> #include <jni.h>
#define ANDROID_STORAGE_FUNCTIONS(V) \
V(OpenContentUri, int, (const std::string& filepath, OpenMode openmode), open_content_uri, \
"openContentUri", "(Ljava/lang/String;Ljava/lang/String;)I")
#define ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(V) \
V(GetSize, std::uint64_t, get_size, CallStaticLongMethod, "getSize", "(Ljava/lang/String;)J") \
V(IsDirectory, bool, is_directory, CallStaticBooleanMethod, "isDirectory", \
"(Ljava/lang/String;)Z") \
V(Exists, bool, file_exists, CallStaticBooleanMethod, "exists", "(Ljava/lang/String;)Z")
#define ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(V) \
V(GetParentDirectory, get_parent_directory, CallStaticObjectMethod, "getParentDirectory", \
"(Ljava/lang/String;)Ljava/lang/String;") \
V(GetFilename, get_filename, CallStaticObjectMethod, "getFilename", \
"(Ljava/lang/String;)Ljava/lang/String;")
namespace Common::FS::Android { namespace Common::FS::Android {
static JavaVM* g_jvm = nullptr; static JavaVM* g_jvm = nullptr;
static jclass native_library = nullptr; static jclass native_library = nullptr;
#define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) F(JMethodID) static jmethodID s_get_parent_directory;
#define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) F(JMethodID) static jmethodID s_get_filename;
#define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) F(JMethodID) static jmethodID s_get_size;
#define F(JMethodID) static jmethodID JMethodID = nullptr; static jmethodID s_is_directory;
ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH) static jmethodID s_file_exists;
ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR) static jmethodID s_open_content_uri;
ANDROID_STORAGE_FUNCTIONS(FS)
#undef F
#undef FS
#undef FR
#undef FH
enum class OpenMode { enum class OpenMode {
Read, Read,
@ -57,24 +36,11 @@ void UnRegisterCallbacks();
bool IsContentUri(const std::string& path); bool IsContentUri(const std::string& path);
#define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) \ int OpenContentUri(const std::string& filepath, OpenMode openmode);
F(FunctionName, Parameters, ReturnValue) std::uint64_t GetSize(const std::string& filepath);
#define F(FunctionName, Parameters, ReturnValue) ReturnValue FunctionName Parameters; bool IsDirectory(const std::string& filepath);
ANDROID_STORAGE_FUNCTIONS(FS) bool Exists(const std::string& filepath);
#undef F std::string GetParentDirectory(const std::string& filepath);
#undef FS std::string GetFilename(const std::string& filepath);
#define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) \
F(FunctionName, ReturnValue)
#define F(FunctionName, ReturnValue) ReturnValue FunctionName(const std::string& filepath);
ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
#undef F
#undef FR
#define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) F(FunctionName)
#define F(FunctionName) std::string FunctionName(const std::string& filepath);
ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH)
#undef F
#undef FH
} // namespace Common::FS::Android } // namespace Common::FS::Android

73
src/common/range_sets.h Normal file
View File

@ -0,0 +1,73 @@
// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <memory>
#include "common/common_types.h"
namespace Common {
template <typename AddressType>
class RangeSet {
public:
RangeSet();
~RangeSet();
RangeSet(RangeSet const&) = delete;
RangeSet& operator=(RangeSet const&) = delete;
RangeSet(RangeSet&& other);
RangeSet& operator=(RangeSet&& other);
void Add(AddressType base_address, size_t size);
void Subtract(AddressType base_address, size_t size);
void Clear();
bool Empty() const;
template <typename Func>
void ForEach(Func&& func) const;
template <typename Func>
void ForEachInRange(AddressType device_addr, size_t size, Func&& func) const;
private:
struct RangeSetImpl;
std::unique_ptr<RangeSetImpl> m_impl;
};
template <typename AddressType>
class OverlapRangeSet {
public:
OverlapRangeSet();
~OverlapRangeSet();
OverlapRangeSet(OverlapRangeSet const&) = delete;
OverlapRangeSet& operator=(OverlapRangeSet const&) = delete;
OverlapRangeSet(OverlapRangeSet&& other);
OverlapRangeSet& operator=(OverlapRangeSet&& other);
void Add(AddressType base_address, size_t size);
void Subtract(AddressType base_address, size_t size);
template <typename Func>
void Subtract(AddressType base_address, size_t size, Func&& on_delete);
void DeleteAll(AddressType base_address, size_t size);
void Clear();
bool Empty() const;
template <typename Func>
void ForEach(Func&& func) const;
template <typename Func>
void ForEachInRange(AddressType device_addr, size_t size, Func&& func) const;
private:
struct OverlapRangeSetImpl;
std::unique_ptr<OverlapRangeSetImpl> m_impl;
};
} // namespace Common

304
src/common/range_sets.inc Normal file
View File

@ -0,0 +1,304 @@
// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <limits>
#include <utility>
#include <boost/icl/interval.hpp>
#include <boost/icl/interval_base_set.hpp>
#include <boost/icl/interval_map.hpp>
#include <boost/icl/interval_set.hpp>
#include <boost/icl/split_interval_map.hpp>
#include <boost/pool/pool.hpp>
#include <boost/pool/pool_alloc.hpp>
#include <boost/pool/poolfwd.hpp>
#include "common/range_sets.h"
namespace Common {
namespace {
template <class T>
using RangeSetsAllocator =
boost::fast_pool_allocator<T, boost::default_user_allocator_new_delete,
boost::details::pool::default_mutex, 1024, 2048>;
}
template <typename AddressType>
struct RangeSet<AddressType>::RangeSetImpl {
using IntervalSet = boost::icl::interval_set<
AddressType, std::less, ICL_INTERVAL_INSTANCE(ICL_INTERVAL_DEFAULT, AddressType, std::less),
RangeSetsAllocator>;
using IntervalType = typename IntervalSet::interval_type;
RangeSetImpl() = default;
~RangeSetImpl() = default;
void Add(AddressType base_address, size_t size) {
AddressType end_address = base_address + static_cast<AddressType>(size);
IntervalType interval{base_address, end_address};
m_ranges_set.add(interval);
}
void Subtract(AddressType base_address, size_t size) {
AddressType end_address = base_address + static_cast<AddressType>(size);
IntervalType interval{base_address, end_address};
m_ranges_set.subtract(interval);
}
template <typename Func>
void ForEach(Func&& func) const {
if (m_ranges_set.empty()) {
return;
}
auto it = m_ranges_set.begin();
auto end_it = m_ranges_set.end();
for (; it != end_it; it++) {
const AddressType inter_addr_end = it->upper();
const AddressType inter_addr = it->lower();
func(inter_addr, inter_addr_end);
}
}
template <typename Func>
void ForEachInRange(AddressType base_addr, size_t size, Func&& func) const {
if (m_ranges_set.empty()) {
return;
}
const AddressType start_address = base_addr;
const AddressType end_address = start_address + size;
const RangeSetImpl::IntervalType search_interval{start_address, end_address};
auto it = m_ranges_set.lower_bound(search_interval);
if (it == m_ranges_set.end()) {
return;
}
auto end_it = m_ranges_set.upper_bound(search_interval);
for (; it != end_it; it++) {
AddressType inter_addr_end = it->upper();
AddressType inter_addr = it->lower();
if (inter_addr_end > end_address) {
inter_addr_end = end_address;
}
if (inter_addr < start_address) {
inter_addr = start_address;
}
func(inter_addr, inter_addr_end);
}
}
IntervalSet m_ranges_set;
};
template <typename AddressType>
struct OverlapRangeSet<AddressType>::OverlapRangeSetImpl {
using IntervalSet = boost::icl::split_interval_map<
AddressType, s32, boost::icl::partial_enricher, std::less, boost::icl::inplace_plus,
boost::icl::inter_section,
ICL_INTERVAL_INSTANCE(ICL_INTERVAL_DEFAULT, AddressType, std::less), RangeSetsAllocator>;
using IntervalType = typename IntervalSet::interval_type;
OverlapRangeSetImpl() = default;
~OverlapRangeSetImpl() = default;
void Add(AddressType base_address, size_t size) {
AddressType end_address = base_address + static_cast<AddressType>(size);
IntervalType interval{base_address, end_address};
m_split_ranges_set += std::make_pair(interval, 1);
}
template <bool has_on_delete, typename Func>
void Subtract(AddressType base_address, size_t size, s32 amount,
[[maybe_unused]] Func&& on_delete) {
if (m_split_ranges_set.empty()) {
return;
}
AddressType end_address = base_address + static_cast<AddressType>(size);
IntervalType interval{base_address, end_address};
bool any_removals = false;
m_split_ranges_set += std::make_pair(interval, -amount);
do {
any_removals = false;
auto it = m_split_ranges_set.lower_bound(interval);
if (it == m_split_ranges_set.end()) {
return;
}
auto end_it = m_split_ranges_set.upper_bound(interval);
for (; it != end_it; it++) {
if (it->second <= 0) {
if constexpr (has_on_delete) {
if (it->second == 0) {
on_delete(it->first.lower(), it->first.upper());
}
}
any_removals = true;
m_split_ranges_set.erase(it);
break;
}
}
} while (any_removals);
}
template <typename Func>
void ForEach(Func&& func) const {
if (m_split_ranges_set.empty()) {
return;
}
auto it = m_split_ranges_set.begin();
auto end_it = m_split_ranges_set.end();
for (; it != end_it; it++) {
const AddressType inter_addr_end = it->first.upper();
const AddressType inter_addr = it->first.lower();
func(inter_addr, inter_addr_end, it->second);
}
}
template <typename Func>
void ForEachInRange(AddressType base_address, size_t size, Func&& func) const {
if (m_split_ranges_set.empty()) {
return;
}
const AddressType start_address = base_address;
const AddressType end_address = start_address + size;
const OverlapRangeSetImpl::IntervalType search_interval{start_address, end_address};
auto it = m_split_ranges_set.lower_bound(search_interval);
if (it == m_split_ranges_set.end()) {
return;
}
auto end_it = m_split_ranges_set.upper_bound(search_interval);
for (; it != end_it; it++) {
auto& inter = it->first;
AddressType inter_addr_end = inter.upper();
AddressType inter_addr = inter.lower();
if (inter_addr_end > end_address) {
inter_addr_end = end_address;
}
if (inter_addr < start_address) {
inter_addr = start_address;
}
func(inter_addr, inter_addr_end, it->second);
}
}
IntervalSet m_split_ranges_set;
};
template <typename AddressType>
RangeSet<AddressType>::RangeSet() {
m_impl = std::make_unique<RangeSet<AddressType>::RangeSetImpl>();
}
template <typename AddressType>
RangeSet<AddressType>::~RangeSet() = default;
template <typename AddressType>
RangeSet<AddressType>::RangeSet(RangeSet&& other) {
m_impl = std::make_unique<RangeSet<AddressType>::RangeSetImpl>();
m_impl->m_ranges_set = std::move(other.m_impl->m_ranges_set);
}
template <typename AddressType>
RangeSet<AddressType>& RangeSet<AddressType>::operator=(RangeSet&& other) {
m_impl->m_ranges_set = std::move(other.m_impl->m_ranges_set);
}
template <typename AddressType>
void RangeSet<AddressType>::Add(AddressType base_address, size_t size) {
m_impl->Add(base_address, size);
}
template <typename AddressType>
void RangeSet<AddressType>::Subtract(AddressType base_address, size_t size) {
m_impl->Subtract(base_address, size);
}
template <typename AddressType>
void RangeSet<AddressType>::Clear() {
m_impl->m_ranges_set.clear();
}
template <typename AddressType>
bool RangeSet<AddressType>::Empty() const {
return m_impl->m_ranges_set.empty();
}
template <typename AddressType>
template <typename Func>
void RangeSet<AddressType>::ForEach(Func&& func) const {
m_impl->ForEach(std::move(func));
}
template <typename AddressType>
template <typename Func>
void RangeSet<AddressType>::ForEachInRange(AddressType base_address, size_t size,
Func&& func) const {
m_impl->ForEachInRange(base_address, size, std::move(func));
}
template <typename AddressType>
OverlapRangeSet<AddressType>::OverlapRangeSet() {
m_impl = std::make_unique<OverlapRangeSet<AddressType>::OverlapRangeSetImpl>();
}
template <typename AddressType>
OverlapRangeSet<AddressType>::~OverlapRangeSet() = default;
template <typename AddressType>
OverlapRangeSet<AddressType>::OverlapRangeSet(OverlapRangeSet&& other) {
m_impl = std::make_unique<OverlapRangeSet<AddressType>::OverlapRangeSetImpl>();
m_impl->m_split_ranges_set = std::move(other.m_impl->m_split_ranges_set);
}
template <typename AddressType>
OverlapRangeSet<AddressType>& OverlapRangeSet<AddressType>::operator=(OverlapRangeSet&& other) {
m_impl->m_split_ranges_set = std::move(other.m_impl->m_split_ranges_set);
}
template <typename AddressType>
void OverlapRangeSet<AddressType>::Add(AddressType base_address, size_t size) {
m_impl->Add(base_address, size);
}
template <typename AddressType>
void OverlapRangeSet<AddressType>::Subtract(AddressType base_address, size_t size) {
m_impl->template Subtract<false>(base_address, size, 1, [](AddressType, AddressType) {});
}
template <typename AddressType>
template <typename Func>
void OverlapRangeSet<AddressType>::Subtract(AddressType base_address, size_t size,
Func&& on_delete) {
m_impl->template Subtract<true, Func>(base_address, size, 1, std::move(on_delete));
}
template <typename AddressType>
void OverlapRangeSet<AddressType>::DeleteAll(AddressType base_address, size_t size) {
m_impl->template Subtract<false>(base_address, size, std::numeric_limits<s32>::max(),
[](AddressType, AddressType) {});
}
template <typename AddressType>
void OverlapRangeSet<AddressType>::Clear() {
m_impl->m_split_ranges_set.clear();
}
template <typename AddressType>
bool OverlapRangeSet<AddressType>::Empty() const {
return m_impl->m_split_ranges_set.empty();
}
template <typename AddressType>
template <typename Func>
void OverlapRangeSet<AddressType>::ForEach(Func&& func) const {
m_impl->ForEach(func);
}
template <typename AddressType>
template <typename Func>
void OverlapRangeSet<AddressType>::ForEachInRange(AddressType base_address, size_t size,
Func&& func) const {
m_impl->ForEachInRange(base_address, size, std::move(func));
}
} // namespace Common

View File

@ -30,6 +30,7 @@ namespace Settings {
#define SETTING(TYPE, RANGED) template class Setting<TYPE, RANGED> #define SETTING(TYPE, RANGED) template class Setting<TYPE, RANGED>
#define SWITCHABLE(TYPE, RANGED) template class SwitchableSetting<TYPE, RANGED> #define SWITCHABLE(TYPE, RANGED) template class SwitchableSetting<TYPE, RANGED>
SETTING(AppletMode, false);
SETTING(AudioEngine, false); SETTING(AudioEngine, false);
SETTING(bool, false); SETTING(bool, false);
SETTING(int, false); SETTING(int, false);
@ -215,6 +216,8 @@ const char* TranslateCategory(Category category) {
return "Debugging"; return "Debugging";
case Category::GpuDriver: case Category::GpuDriver:
return "GpuDriver"; return "GpuDriver";
case Category::LibraryApplet:
return "LibraryApplet";
case Category::Miscellaneous: case Category::Miscellaneous:
return "Miscellaneous"; return "Miscellaneous";
case Category::Network: case Category::Network:

View File

@ -133,6 +133,38 @@ struct TouchFromButtonMap {
struct Values { struct Values {
Linkage linkage{}; Linkage linkage{};
// Applet
Setting<AppletMode> cabinet_applet_mode{linkage, AppletMode::LLE, "cabinet_applet_mode",
Category::LibraryApplet};
Setting<AppletMode> controller_applet_mode{linkage, AppletMode::HLE, "controller_applet_mode",
Category::LibraryApplet};
Setting<AppletMode> data_erase_applet_mode{linkage, AppletMode::HLE, "data_erase_applet_mode",
Category::LibraryApplet};
Setting<AppletMode> error_applet_mode{linkage, AppletMode::HLE, "error_applet_mode",
Category::LibraryApplet};
Setting<AppletMode> net_connect_applet_mode{linkage, AppletMode::HLE, "net_connect_applet_mode",
Category::LibraryApplet};
Setting<AppletMode> player_select_applet_mode{
linkage, AppletMode::HLE, "player_select_applet_mode", Category::LibraryApplet};
Setting<AppletMode> swkbd_applet_mode{linkage, AppletMode::LLE, "swkbd_applet_mode",
Category::LibraryApplet};
Setting<AppletMode> mii_edit_applet_mode{linkage, AppletMode::LLE, "mii_edit_applet_mode",
Category::LibraryApplet};
Setting<AppletMode> web_applet_mode{linkage, AppletMode::HLE, "web_applet_mode",
Category::LibraryApplet};
Setting<AppletMode> shop_applet_mode{linkage, AppletMode::HLE, "shop_applet_mode",
Category::LibraryApplet};
Setting<AppletMode> photo_viewer_applet_mode{
linkage, AppletMode::LLE, "photo_viewer_applet_mode", Category::LibraryApplet};
Setting<AppletMode> offline_web_applet_mode{linkage, AppletMode::LLE, "offline_web_applet_mode",
Category::LibraryApplet};
Setting<AppletMode> login_share_applet_mode{linkage, AppletMode::HLE, "login_share_applet_mode",
Category::LibraryApplet};
Setting<AppletMode> wifi_web_auth_applet_mode{
linkage, AppletMode::HLE, "wifi_web_auth_applet_mode", Category::LibraryApplet};
Setting<AppletMode> my_page_applet_mode{linkage, AppletMode::LLE, "my_page_applet_mode",
Category::LibraryApplet};
// Audio // Audio
SwitchableSetting<AudioEngine> sink_id{linkage, AudioEngine::Auto, "output_engine", SwitchableSetting<AudioEngine> sink_id{linkage, AudioEngine::Auto, "output_engine",
Category::Audio, Specialization::RuntimeList}; Category::Audio, Specialization::RuntimeList};

View File

@ -44,6 +44,7 @@ enum class Category : u32 {
Services, Services,
Paths, Paths,
Linux, Linux,
LibraryApplet,
MaxEnum, MaxEnum,
}; };

View File

@ -151,6 +151,8 @@ ENUM(AspectRatio, R16_9, R4_3, R21_9, R16_10, Stretch);
ENUM(ConsoleMode, Handheld, Docked); ENUM(ConsoleMode, Handheld, Docked);
ENUM(AppletMode, HLE, LLE);
template <typename Type> template <typename Type>
inline std::string CanonicalizeEnum(Type id) { inline std::string CanonicalizeEnum(Type id) {
const auto group = EnumMetadata<Type>::Canonicalizations(); const auto group = EnumMetadata<Type>::Canonicalizations();

View File

@ -14,7 +14,7 @@
#include "common/common_types.h" #include "common/common_types.h"
#include "common/polyfill_ranges.h" #include "common/polyfill_ranges.h"
namespace VideoCommon { namespace Common {
struct SlotId { struct SlotId {
static constexpr u32 INVALID_INDEX = std::numeric_limits<u32>::max(); static constexpr u32 INVALID_INDEX = std::numeric_limits<u32>::max();
@ -217,11 +217,11 @@ private:
std::vector<u32> free_list; std::vector<u32> free_list;
}; };
} // namespace VideoCommon } // namespace Common
template <> template <>
struct std::hash<VideoCommon::SlotId> { struct std::hash<Common::SlotId> {
size_t operator()(const VideoCommon::SlotId& id) const noexcept { size_t operator()(const Common::SlotId& id) const noexcept {
return std::hash<u32>{}(id.index); return std::hash<u32>{}(id.index);
} }
}; };

View File

@ -417,74 +417,72 @@ add_library(core STATIC
hle/service/am/am_types.h hle/service/am/am_types.h
hle/service/am/applet.cpp hle/service/am/applet.cpp
hle/service/am/applet.h hle/service/am/applet.h
hle/service/am/applet_ae.cpp
hle/service/am/applet_ae.h
hle/service/am/applet_manager.cpp hle/service/am/applet_manager.cpp
hle/service/am/applet_data_broker.cpp hle/service/am/applet_data_broker.cpp
hle/service/am/applet_data_broker.h hle/service/am/applet_data_broker.h
hle/service/am/applet_manager.h hle/service/am/applet_manager.h
hle/service/am/applet_oe.cpp
hle/service/am/applet_oe.h
hle/service/am/applet_common_functions.cpp
hle/service/am/applet_common_functions.h
hle/service/am/applet_message_queue.cpp hle/service/am/applet_message_queue.cpp
hle/service/am/applet_message_queue.h hle/service/am/applet_message_queue.h
hle/service/am/application_creator.cpp
hle/service/am/application_creator.h
hle/service/am/application_functions.cpp
hle/service/am/application_functions.h
hle/service/am/application_proxy.cpp
hle/service/am/application_proxy.h
hle/service/am/audio_controller.cpp
hle/service/am/audio_controller.h
hle/service/am/common_state_getter.cpp
hle/service/am/common_state_getter.h
hle/service/am/debug_functions.cpp
hle/service/am/debug_functions.h
hle/service/am/display_controller.cpp
hle/service/am/display_controller.h
hle/service/am/global_state_controller.cpp
hle/service/am/global_state_controller.h
hle/service/am/hid_registration.cpp hle/service/am/hid_registration.cpp
hle/service/am/hid_registration.h hle/service/am/hid_registration.h
hle/service/am/home_menu_functions.cpp
hle/service/am/home_menu_functions.h
hle/service/am/idle.cpp
hle/service/am/idle.h
hle/service/am/library_applet_accessor.cpp
hle/service/am/library_applet_accessor.h
hle/service/am/library_applet_creator.cpp
hle/service/am/library_applet_creator.h
hle/service/am/library_applet_proxy.cpp
hle/service/am/library_applet_proxy.h
hle/service/am/library_applet_self_accessor.cpp
hle/service/am/library_applet_self_accessor.h
hle/service/am/library_applet_storage.cpp hle/service/am/library_applet_storage.cpp
hle/service/am/library_applet_storage.h hle/service/am/library_applet_storage.h
hle/service/am/lock_accessor.cpp
hle/service/am/lock_accessor.h
hle/service/am/managed_layer_holder.cpp hle/service/am/managed_layer_holder.cpp
hle/service/am/managed_layer_holder.h hle/service/am/managed_layer_holder.h
hle/service/am/omm.cpp
hle/service/am/omm.h
hle/service/am/process_winding_controller.cpp
hle/service/am/process_winding_controller.h
hle/service/am/process.cpp hle/service/am/process.cpp
hle/service/am/process.h hle/service/am/process.h
hle/service/am/self_controller.cpp hle/service/am/service/all_system_applet_proxies_service.cpp
hle/service/am/self_controller.h hle/service/am/service/all_system_applet_proxies_service.h
hle/service/am/system_applet_proxy.cpp hle/service/am/service/applet_common_functions.cpp
hle/service/am/system_applet_proxy.h hle/service/am/service/applet_common_functions.h
hle/service/am/service/application_accessor.cpp
hle/service/am/service/application_accessor.h
hle/service/am/service/application_creator.cpp
hle/service/am/service/application_creator.h
hle/service/am/service/application_functions.cpp
hle/service/am/service/application_functions.h
hle/service/am/service/application_proxy_service.cpp
hle/service/am/service/application_proxy_service.h
hle/service/am/service/application_proxy.cpp
hle/service/am/service/application_proxy.h
hle/service/am/service/audio_controller.cpp
hle/service/am/service/audio_controller.h
hle/service/am/service/common_state_getter.cpp
hle/service/am/service/common_state_getter.h
hle/service/am/service/cradle_firmware_updater.cpp
hle/service/am/service/cradle_firmware_updater.h
hle/service/am/service/debug_functions.cpp
hle/service/am/service/debug_functions.h
hle/service/am/service/display_controller.cpp
hle/service/am/service/display_controller.h
hle/service/am/service/global_state_controller.cpp
hle/service/am/service/global_state_controller.h
hle/service/am/service/home_menu_functions.cpp
hle/service/am/service/home_menu_functions.h
hle/service/am/service/library_applet_accessor.cpp
hle/service/am/service/library_applet_accessor.h
hle/service/am/service/library_applet_creator.cpp
hle/service/am/service/library_applet_creator.h
hle/service/am/service/library_applet_proxy.cpp
hle/service/am/service/library_applet_proxy.h
hle/service/am/service/library_applet_self_accessor.cpp
hle/service/am/service/library_applet_self_accessor.h
hle/service/am/service/lock_accessor.cpp
hle/service/am/service/lock_accessor.h
hle/service/am/service/process_winding_controller.cpp
hle/service/am/service/process_winding_controller.h
hle/service/am/service/self_controller.cpp
hle/service/am/service/self_controller.h
hle/service/am/service/storage_accessor.cpp
hle/service/am/service/storage_accessor.h
hle/service/am/service/storage.cpp
hle/service/am/service/storage.h
hle/service/am/service/system_applet_proxy.cpp
hle/service/am/service/system_applet_proxy.h
hle/service/am/service/window_controller.cpp
hle/service/am/service/window_controller.h
hle/service/am/system_buffer_manager.cpp hle/service/am/system_buffer_manager.cpp
hle/service/am/system_buffer_manager.h hle/service/am/system_buffer_manager.h
hle/service/am/spsm.cpp
hle/service/am/spsm.h
hle/service/am/storage_accessor.cpp
hle/service/am/storage_accessor.h
hle/service/am/storage.cpp
hle/service/am/storage.h
hle/service/am/window_controller.cpp
hle/service/am/window_controller.h
hle/service/aoc/aoc_u.cpp hle/service/aoc/aoc_u.cpp
hle/service/aoc/aoc_u.h hle/service/aoc/aoc_u.h
hle/service/apm/apm.cpp hle/service/apm/apm.cpp
@ -512,10 +510,35 @@ add_library(core STATIC
hle/service/audio/hwopus.h hle/service/audio/hwopus.h
hle/service/bcat/backend/backend.cpp hle/service/bcat/backend/backend.cpp
hle/service/bcat/backend/backend.h hle/service/bcat/backend/backend.h
hle/service/bcat/news/newly_arrived_event_holder.cpp
hle/service/bcat/news/newly_arrived_event_holder.h
hle/service/bcat/news/news_data_service.cpp
hle/service/bcat/news/news_data_service.h
hle/service/bcat/news/news_database_service.cpp
hle/service/bcat/news/news_database_service.h
hle/service/bcat/news/news_service.cpp
hle/service/bcat/news/news_service.h
hle/service/bcat/news/overwrite_event_holder.cpp
hle/service/bcat/news/overwrite_event_holder.h
hle/service/bcat/news/service_creator.cpp
hle/service/bcat/news/service_creator.h
hle/service/bcat/bcat.cpp hle/service/bcat/bcat.cpp
hle/service/bcat/bcat.h hle/service/bcat/bcat.h
hle/service/bcat/bcat_module.cpp hle/service/bcat/bcat_result.h
hle/service/bcat/bcat_module.h hle/service/bcat/bcat_service.cpp
hle/service/bcat/bcat_service.h
hle/service/bcat/bcat_types.h
hle/service/bcat/bcat_util.h
hle/service/bcat/delivery_cache_directory_service.cpp
hle/service/bcat/delivery_cache_directory_service.h
hle/service/bcat/delivery_cache_file_service.cpp
hle/service/bcat/delivery_cache_file_service.h
hle/service/bcat/delivery_cache_progress_service.cpp
hle/service/bcat/delivery_cache_progress_service.h
hle/service/bcat/delivery_cache_storage_service.cpp
hle/service/bcat/delivery_cache_storage_service.h
hle/service/bcat/service_creator.cpp
hle/service/bcat/service_creator.h
hle/service/bpc/bpc.cpp hle/service/bpc/bpc.cpp
hle/service/bpc/bpc.h hle/service/bpc/bpc.h
hle/service/btdrv/btdrv.cpp hle/service/btdrv/btdrv.cpp
@ -548,8 +571,6 @@ add_library(core STATIC
hle/service/es/es.h hle/service/es/es.h
hle/service/eupld/eupld.cpp hle/service/eupld/eupld.cpp
hle/service/eupld/eupld.h hle/service/eupld/eupld.h
hle/service/event.cpp
hle/service/event.h
hle/service/fatal/fatal.cpp hle/service/fatal/fatal.cpp
hle/service/fatal/fatal.h hle/service/fatal/fatal.h
hle/service/fatal/fatal_p.cpp hle/service/fatal/fatal_p.cpp
@ -645,6 +666,18 @@ add_library(core STATIC
hle/service/ldn/ldn.h hle/service/ldn/ldn.h
hle/service/ldn/ldn_results.h hle/service/ldn/ldn_results.h
hle/service/ldn/ldn_types.h hle/service/ldn/ldn_types.h
hle/service/ldn/monitor_service.cpp
hle/service/ldn/monitor_service.h
hle/service/ldn/sf_monitor_service.cpp
hle/service/ldn/sf_monitor_service.h
hle/service/ldn/sf_service.cpp
hle/service/ldn/sf_service.h
hle/service/ldn/sf_service_monitor.cpp
hle/service/ldn/sf_service_monitor.h
hle/service/ldn/system_local_communication_service.cpp
hle/service/ldn/system_local_communication_service.h
hle/service/ldn/user_local_communication_service.cpp
hle/service/ldn/user_local_communication_service.h
hle/service/ldr/ldr.cpp hle/service/ldr/ldr.cpp
hle/service/ldr/ldr.h hle/service/ldr/ldr.h
hle/service/lm/lm.cpp hle/service/lm/lm.cpp
@ -676,8 +709,6 @@ add_library(core STATIC
hle/service/mm/mm_u.h hle/service/mm/mm_u.h
hle/service/mnpp/mnpp_app.cpp hle/service/mnpp/mnpp_app.cpp
hle/service/mnpp/mnpp_app.h hle/service/mnpp/mnpp_app.h
hle/service/mutex.cpp
hle/service/mutex.h
hle/service/ncm/ncm.cpp hle/service/ncm/ncm.cpp
hle/service/ncm/ncm.h hle/service/ncm/ncm.h
hle/service/nfc/common/amiibo_crypto.cpp hle/service/nfc/common/amiibo_crypto.cpp
@ -790,6 +821,23 @@ add_library(core STATIC
hle/service/nvnflinger/window.h hle/service/nvnflinger/window.h
hle/service/olsc/olsc.cpp hle/service/olsc/olsc.cpp
hle/service/olsc/olsc.h hle/service/olsc/olsc.h
hle/service/omm/omm.cpp
hle/service/omm/omm.h
hle/service/omm/operation_mode_manager.cpp
hle/service/omm/operation_mode_manager.h
hle/service/omm/policy_manager_system.cpp
hle/service/omm/policy_manager_system.h
hle/service/omm/power_state_interface.cpp
hle/service/omm/power_state_interface.h
hle/service/os/event.cpp
hle/service/os/event.h
hle/service/os/multi_wait_holder.cpp
hle/service/os/multi_wait_holder.h
hle/service/os/multi_wait_utils.h
hle/service/os/multi_wait.cpp
hle/service/os/multi_wait.h
hle/service/os/mutex.cpp
hle/service/os/mutex.h
hle/service/pcie/pcie.cpp hle/service/pcie/pcie.cpp
hle/service/pcie/pcie.h hle/service/pcie/pcie.h
hle/service/pctl/pctl.cpp hle/service/pctl/pctl.cpp
@ -872,6 +920,7 @@ add_library(core STATIC
hle/service/set/factory_settings_server.h hle/service/set/factory_settings_server.h
hle/service/set/firmware_debug_settings_server.cpp hle/service/set/firmware_debug_settings_server.cpp
hle/service/set/firmware_debug_settings_server.h hle/service/set/firmware_debug_settings_server.h
hle/service/set/key_code_map.h
hle/service/set/settings.cpp hle/service/set/settings.cpp
hle/service/set/settings.h hle/service/set/settings.h
hle/service/set/settings_server.cpp hle/service/set/settings_server.cpp

View File

@ -43,6 +43,8 @@ public:
DeviceMemoryManager(const DeviceMemory& device_memory); DeviceMemoryManager(const DeviceMemory& device_memory);
~DeviceMemoryManager(); ~DeviceMemoryManager();
static constexpr bool HAS_FLUSH_INVALIDATION = true;
void BindInterface(DeviceInterface* device_inter); void BindInterface(DeviceInterface* device_inter);
DAddr Allocate(size_t size); DAddr Allocate(size_t size);

View File

@ -522,16 +522,21 @@ void DeviceMemoryManager<Traits>::UpdatePagesCachedCount(DAddr addr, size_t size
auto* memory_device_inter = registered_processes[asid.id]; auto* memory_device_inter = registered_processes[asid.id];
const auto release_pending = [&] { const auto release_pending = [&] {
if (uncache_bytes > 0) { if (uncache_bytes > 0) {
MarkRegionCaching(memory_device_inter, uncache_begin << Memory::YUZU_PAGEBITS, if (memory_device_inter != nullptr) {
uncache_bytes, false); MarkRegionCaching(memory_device_inter, uncache_begin << Memory::YUZU_PAGEBITS,
uncache_bytes, false);
}
uncache_bytes = 0; uncache_bytes = 0;
} }
if (cache_bytes > 0) { if (cache_bytes > 0) {
MarkRegionCaching(memory_device_inter, cache_begin << Memory::YUZU_PAGEBITS, if (memory_device_inter != nullptr) {
cache_bytes, true); MarkRegionCaching(memory_device_inter, cache_begin << Memory::YUZU_PAGEBITS,
cache_bytes, true);
}
cache_bytes = 0; cache_bytes = 0;
} }
}; };
size_t old_vpage = (base_vaddress >> Memory::YUZU_PAGEBITS) - 1;
for (; page != page_end; ++page) { for (; page != page_end; ++page) {
CounterAtomicType& count = cached_pages->at(page >> subentries_shift).Count(page); CounterAtomicType& count = cached_pages->at(page >> subentries_shift).Count(page);
auto [asid_2, vpage] = ExtractCPUBacking(page); auto [asid_2, vpage] = ExtractCPUBacking(page);
@ -547,6 +552,12 @@ void DeviceMemoryManager<Traits>::UpdatePagesCachedCount(DAddr addr, size_t size
memory_device_inter = registered_processes[asid_2.id]; memory_device_inter = registered_processes[asid_2.id];
} }
if (vpage != old_vpage + 1) [[unlikely]] {
release_pending();
}
old_vpage = vpage;
// Adds or subtracts 1, as count is a unsigned 8-bit value // Adds or subtracts 1, as count is a unsigned 8-bit value
count.fetch_add(static_cast<CounterType>(delta), std::memory_order_release); count.fetch_add(static_cast<CounterType>(delta), std::memory_order_release);

View File

@ -172,6 +172,10 @@ u32 NCA::GetSDKVersion() const {
return reader->GetSdkAddonVersion(); return reader->GetSdkAddonVersion();
} }
u8 NCA::GetKeyGeneration() const {
return reader->GetKeyGeneration();
}
bool NCA::IsUpdate() const { bool NCA::IsUpdate() const {
return is_update; return is_update;
} }

View File

@ -77,6 +77,7 @@ public:
u64 GetTitleId() const; u64 GetTitleId() const;
RightsId GetRightsId() const; RightsId GetRightsId() const;
u32 GetSDKVersion() const; u32 GetSDKVersion() const;
u8 GetKeyGeneration() const;
bool IsUpdate() const; bool IsUpdate() const;
VirtualFile GetRomFS() const; VirtualFile GetRomFS() const;

View File

@ -91,6 +91,7 @@ constexpr Result ResultWriteNotPermitted{ErrorModule::FS, 6203};
constexpr Result ResultUnsupportedSetSizeForIndirectStorage{ErrorModule::FS, 6325}; constexpr Result ResultUnsupportedSetSizeForIndirectStorage{ErrorModule::FS, 6325};
constexpr Result ResultUnsupportedWriteForCompressedStorage{ErrorModule::FS, 6387}; constexpr Result ResultUnsupportedWriteForCompressedStorage{ErrorModule::FS, 6387};
constexpr Result ResultUnsupportedOperateRangeForCompressedStorage{ErrorModule::FS, 6388}; constexpr Result ResultUnsupportedOperateRangeForCompressedStorage{ErrorModule::FS, 6388};
constexpr Result ResultPermissionDenied{ErrorModule::FS, 6400};
constexpr Result ResultBufferAllocationFailed{ErrorModule::FS, 6705}; constexpr Result ResultBufferAllocationFailed{ErrorModule::FS, 6705};
} // namespace FileSys } // namespace FileSys

View File

@ -44,15 +44,32 @@ public:
GuestMemory() = delete; GuestMemory() = delete;
explicit GuestMemory(M& memory, u64 addr, std::size_t size, explicit GuestMemory(M& memory, u64 addr, std::size_t size,
Common::ScratchBuffer<T>* backup = nullptr) Common::ScratchBuffer<T>* backup = nullptr)
: m_memory{memory}, m_addr{addr}, m_size{size} { : m_memory{&memory}, m_addr{addr}, m_size{size} {
static_assert(FLAGS & GuestMemoryFlags::Read || FLAGS & GuestMemoryFlags::Write); static_assert(FLAGS & GuestMemoryFlags::Read || FLAGS & GuestMemoryFlags::Write);
if constexpr (FLAGS & GuestMemoryFlags::Read) { if constexpr (!(FLAGS & GuestMemoryFlags::Read)) {
if (!this->TrySetSpan()) {
if (backup) {
backup->resize_destructive(this->size());
m_data_span = *backup;
m_span_valid = true;
m_is_data_copy = true;
} else {
m_data_copy.resize(this->size());
m_data_span = std::span(m_data_copy);
m_span_valid = true;
m_is_data_copy = true;
}
}
} else if constexpr (FLAGS & GuestMemoryFlags::Read) {
Read(addr, size, backup); Read(addr, size, backup);
} }
} }
~GuestMemory() = default; ~GuestMemory() = default;
GuestMemory(GuestMemory&& rhs) = default;
GuestMemory& operator=(GuestMemory&& rhs) = default;
T* data() noexcept { T* data() noexcept {
return m_data_span.data(); return m_data_span.data();
} }
@ -109,8 +126,8 @@ public:
} }
if (this->TrySetSpan()) { if (this->TrySetSpan()) {
if constexpr (FLAGS & GuestMemoryFlags::Safe) { if constexpr (FLAGS & GuestMemoryFlags::Safe && M::HAS_FLUSH_INVALIDATION) {
m_memory.FlushRegion(m_addr, this->size_bytes()); m_memory->FlushRegion(m_addr, this->size_bytes());
} }
} else { } else {
if (backup) { if (backup) {
@ -123,9 +140,9 @@ public:
m_is_data_copy = true; m_is_data_copy = true;
m_span_valid = true; m_span_valid = true;
if constexpr (FLAGS & GuestMemoryFlags::Safe) { if constexpr (FLAGS & GuestMemoryFlags::Safe) {
m_memory.ReadBlock(m_addr, this->data(), this->size_bytes()); m_memory->ReadBlock(m_addr, this->data(), this->size_bytes());
} else { } else {
m_memory.ReadBlockUnsafe(m_addr, this->data(), this->size_bytes()); m_memory->ReadBlockUnsafe(m_addr, this->data(), this->size_bytes());
} }
} }
return m_data_span; return m_data_span;
@ -133,18 +150,19 @@ public:
void Write(std::span<T> write_data) noexcept { void Write(std::span<T> write_data) noexcept {
if constexpr (FLAGS & GuestMemoryFlags::Cached) { if constexpr (FLAGS & GuestMemoryFlags::Cached) {
m_memory.WriteBlockCached(m_addr, write_data.data(), this->size_bytes()); m_memory->WriteBlockCached(m_addr, write_data.data(), this->size_bytes());
} else if constexpr (FLAGS & GuestMemoryFlags::Safe) { } else if constexpr (FLAGS & GuestMemoryFlags::Safe) {
m_memory.WriteBlock(m_addr, write_data.data(), this->size_bytes()); m_memory->WriteBlock(m_addr, write_data.data(), this->size_bytes());
} else { } else {
m_memory.WriteBlockUnsafe(m_addr, write_data.data(), this->size_bytes()); m_memory->WriteBlockUnsafe(m_addr, write_data.data(), this->size_bytes());
} }
} }
bool TrySetSpan() noexcept { bool TrySetSpan() noexcept {
if (u8* ptr = m_memory.GetSpan(m_addr, this->size_bytes()); ptr) { if (u8* ptr = m_memory->GetSpan(m_addr, this->size_bytes()); ptr) {
m_data_span = {reinterpret_cast<T*>(ptr), this->size()}; m_data_span = {reinterpret_cast<T*>(ptr), this->size()};
m_span_valid = true; m_span_valid = true;
m_is_data_copy = false;
return true; return true;
} }
return false; return false;
@ -159,7 +177,7 @@ protected:
return m_addr_changed; return m_addr_changed;
} }
M& m_memory; M* m_memory;
u64 m_addr{}; u64 m_addr{};
size_t m_size{}; size_t m_size{};
std::span<T> m_data_span{}; std::span<T> m_data_span{};
@ -175,17 +193,7 @@ public:
GuestMemoryScoped() = delete; GuestMemoryScoped() = delete;
explicit GuestMemoryScoped(M& memory, u64 addr, std::size_t size, explicit GuestMemoryScoped(M& memory, u64 addr, std::size_t size,
Common::ScratchBuffer<T>* backup = nullptr) Common::ScratchBuffer<T>* backup = nullptr)
: GuestMemory<M, T, FLAGS>(memory, addr, size, backup) { : GuestMemory<M, T, FLAGS>(memory, addr, size, backup) {}
if constexpr (!(FLAGS & GuestMemoryFlags::Read)) {
if (!this->TrySetSpan()) {
if (backup) {
this->m_data_span = *backup;
this->m_span_valid = true;
this->m_is_data_copy = true;
}
}
}
}
~GuestMemoryScoped() { ~GuestMemoryScoped() {
if constexpr (FLAGS & GuestMemoryFlags::Write) { if constexpr (FLAGS & GuestMemoryFlags::Write) {
@ -196,15 +204,17 @@ public:
if (this->AddressChanged() || this->IsDataCopy()) { if (this->AddressChanged() || this->IsDataCopy()) {
ASSERT(this->m_span_valid); ASSERT(this->m_span_valid);
if constexpr (FLAGS & GuestMemoryFlags::Cached) { if constexpr (FLAGS & GuestMemoryFlags::Cached) {
this->m_memory.WriteBlockCached(this->m_addr, this->data(), this->size_bytes()); this->m_memory->WriteBlockCached(this->m_addr, this->data(),
this->size_bytes());
} else if constexpr (FLAGS & GuestMemoryFlags::Safe) { } else if constexpr (FLAGS & GuestMemoryFlags::Safe) {
this->m_memory.WriteBlock(this->m_addr, this->data(), this->size_bytes()); this->m_memory->WriteBlock(this->m_addr, this->data(), this->size_bytes());
} else { } else {
this->m_memory.WriteBlockUnsafe(this->m_addr, this->data(), this->size_bytes()); this->m_memory->WriteBlockUnsafe(this->m_addr, this->data(),
this->size_bytes());
} }
} else if constexpr ((FLAGS & GuestMemoryFlags::Safe) || } else if constexpr ((FLAGS & GuestMemoryFlags::Safe) ||
(FLAGS & GuestMemoryFlags::Cached)) { (FLAGS & GuestMemoryFlags::Cached)) {
this->m_memory.InvalidateRegion(this->m_addr, this->size_bytes()); this->m_memory->InvalidateRegion(this->m_addr, this->size_bytes());
} }
} }
} }

View File

@ -4,8 +4,9 @@
#include <random> #include <random>
#include "common/scope_exit.h" #include "common/scope_exit.h"
#include "common/settings.h" #include "common/settings.h"
#include "core/arm/dynarmic/arm_dynarmic.h"
#include "core/arm/dynarmic/dynarmic_exclusive_monitor.h"
#include "core/core.h" #include "core/core.h"
#include "core/gpu_dirty_memory_manager.h"
#include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_scoped_resource_reservation.h" #include "core/hle/kernel/k_scoped_resource_reservation.h"
#include "core/hle/kernel/k_shared_memory.h" #include "core/hle/kernel/k_shared_memory.h"
@ -1258,6 +1259,10 @@ void KProcess::InitializeInterfaces() {
#ifdef HAS_NCE #ifdef HAS_NCE
if (this->IsApplication() && Settings::IsNceEnabled()) { if (this->IsApplication() && Settings::IsNceEnabled()) {
// Register the scoped JIT handler before creating any NCE instances
// so that its signal handler will appear first in the signal chain.
Core::ScopedJitExecution::RegisterHandler();
for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
m_arm_interfaces[i] = std::make_unique<Core::ArmNce>(m_kernel.System(), true, i); m_arm_interfaces[i] = std::make_unique<Core::ArmNce>(m_kernel.System(), true, i);
} }

View File

@ -2,11 +2,8 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/am/am.h" #include "core/hle/service/am/am.h"
#include "core/hle/service/am/applet_ae.h" #include "core/hle/service/am/service/all_system_applet_proxies_service.h"
#include "core/hle/service/am/applet_oe.h" #include "core/hle/service/am/service/application_proxy_service.h"
#include "core/hle/service/am/idle.h"
#include "core/hle/service/am/omm.h"
#include "core/hle/service/am/spsm.h"
#include "core/hle/service/server_manager.h" #include "core/hle/service/server_manager.h"
namespace Service::AM { namespace Service::AM {
@ -14,13 +11,10 @@ namespace Service::AM {
void LoopProcess(Nvnflinger::Nvnflinger& nvnflinger, Core::System& system) { void LoopProcess(Nvnflinger::Nvnflinger& nvnflinger, Core::System& system) {
auto server_manager = std::make_unique<ServerManager>(system); auto server_manager = std::make_unique<ServerManager>(system);
server_manager->RegisterNamedService("appletAE", server_manager->RegisterNamedService(
std::make_shared<AppletAE>(nvnflinger, system)); "appletAE", std::make_shared<IAllSystemAppletProxiesService>(system, nvnflinger));
server_manager->RegisterNamedService("appletOE", server_manager->RegisterNamedService(
std::make_shared<AppletOE>(nvnflinger, system)); "appletOE", std::make_shared<IApplicationProxyService>(system, nvnflinger));
server_manager->RegisterNamedService("idle:sys", std::make_shared<IdleSys>(system));
server_manager->RegisterNamedService("omm", std::make_shared<OMM>(system));
server_manager->RegisterNamedService("spsm", std::make_shared<SPSM>(system));
ServerManager::RunServer(std::move(server_manager)); ServerManager::RunServer(std::move(server_manager));
} }

View File

@ -18,7 +18,7 @@ enum class AppletType {
SystemApplet, SystemApplet,
}; };
enum class GameplayRecordingState : u32 { enum class GamePlayRecordingState : u32 {
Disabled, Disabled,
Enabled, Enabled,
}; };
@ -67,10 +67,9 @@ enum class ScreenshotPermission : u32 {
}; };
struct FocusHandlingMode { struct FocusHandlingMode {
bool unknown0; bool notify;
bool unknown1; bool background;
bool unknown2; bool suspend;
bool unknown3;
}; };
enum class IdleTimeDetectionExtension : u32 { enum class IdleTimeDetectionExtension : u32 {
@ -128,14 +127,53 @@ enum class AppletProgramId : u64 {
MaxProgramId = 0x0100000000001FFFull, MaxProgramId = 0x0100000000001FFFull,
}; };
// This is nn::am::AppletMessage
enum class AppletMessage : u32 {
None = 0,
ChangeIntoForeground = 1,
ChangeIntoBackground = 2,
Exit = 4,
ApplicationExited = 6,
FocusStateChanged = 15,
Resume = 16,
DetectShortPressingHomeButton = 20,
DetectLongPressingHomeButton = 21,
DetectShortPressingPowerButton = 22,
DetectMiddlePressingPowerButton = 23,
DetectLongPressingPowerButton = 24,
RequestToPrepareSleep = 25,
FinishedSleepSequence = 26,
SleepRequiredByHighTemperature = 27,
SleepRequiredByLowBattery = 28,
AutoPowerDown = 29,
OperationModeChanged = 30,
PerformanceModeChanged = 31,
DetectReceivingCecSystemStandby = 32,
SdCardRemoved = 33,
LaunchApplicationRequested = 50,
RequestToDisplay = 51,
ShowApplicationLogo = 55,
HideApplicationLogo = 56,
ForceHideApplicationLogo = 57,
FloatingApplicationDetected = 60,
DetectShortPressingCaptureButton = 90,
AlbumScreenShotTaken = 92,
AlbumRecordingSaved = 93,
};
enum class LibraryAppletMode : u32 { enum class LibraryAppletMode : u32 {
AllForeground = 0, AllForeground = 0,
Background = 1, PartialForeground = 1,
NoUI = 2, NoUi = 2,
BackgroundIndirectDisplay = 3, PartialForegroundIndirectDisplay = 3,
AllForegroundInitiallyHidden = 4, AllForegroundInitiallyHidden = 4,
}; };
enum class LaunchParameterKind : u32 {
UserChannel = 1,
AccountPreselectedUser = 2,
};
enum class CommonArgumentVersion : u32 { enum class CommonArgumentVersion : u32 {
Version0, Version0,
Version1, Version1,
@ -152,6 +190,22 @@ enum class ThemeColor : u32 {
BasicBlack = 3, BasicBlack = 3,
}; };
enum class InputDetectionPolicy : u32 {
Unknown0 = 0,
Unknown1 = 1,
};
enum class WindowOriginMode : u32 {
LowerLeft = 0,
UpperLeft = 1,
};
enum class ProgramSpecifyKind : u32 {
ExecuteProgram = 0,
JumpToSubApplicationProgramForDevelopment = 1,
RestartProgram = 2,
};
struct CommonArguments { struct CommonArguments {
CommonArgumentVersion arguments_version; CommonArgumentVersion arguments_version;
CommonArgumentSize size; CommonArgumentSize size;
@ -169,6 +223,27 @@ struct AppletIdentityInfo {
}; };
static_assert(sizeof(AppletIdentityInfo) == 0x10, "AppletIdentityInfo has incorrect size."); static_assert(sizeof(AppletIdentityInfo) == 0x10, "AppletIdentityInfo has incorrect size.");
struct AppletAttribute {
u8 flag;
INSERT_PADDING_BYTES_NOINIT(0x7F);
};
static_assert(sizeof(AppletAttribute) == 0x80, "AppletAttribute has incorrect size.");
// This is nn::oe::DisplayVersion
struct DisplayVersion {
std::array<char, 0x10> string;
};
static_assert(sizeof(DisplayVersion) == 0x10, "DisplayVersion has incorrect size.");
// This is nn::pdm::ApplicationPlayStatistics
struct ApplicationPlayStatistics {
u64 application_id;
u64 play_time_ns;
u64 launch_count;
};
static_assert(sizeof(ApplicationPlayStatistics) == 0x18,
"ApplicationPlayStatistics has incorrect size.");
using AppletResourceUserId = u64; using AppletResourceUserId = u64;
using ProgramId = u64; using ProgramId = u64;

View File

@ -3,14 +3,13 @@
#pragma once #pragma once
#include <list>
#include <mutex> #include <mutex>
#include "common/math_util.h" #include "common/math_util.h"
#include "core/hle/service/apm/apm_controller.h" #include "core/hle/service/apm/apm_controller.h"
#include "core/hle/service/caps/caps_types.h" #include "core/hle/service/caps/caps_types.h"
#include "core/hle/service/event.h"
#include "core/hle/service/kernel_helpers.h" #include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/os/event.h"
#include "core/hle/service/service.h" #include "core/hle/service/service.h"
#include "core/hle/service/am/am_types.h" #include "core/hle/service/am/am_types.h"
@ -18,7 +17,6 @@
#include "core/hle/service/am/hid_registration.h" #include "core/hle/service/am/hid_registration.h"
#include "core/hle/service/am/managed_layer_holder.h" #include "core/hle/service/am/managed_layer_holder.h"
#include "core/hle/service/am/process.h" #include "core/hle/service/am/process.h"
#include "core/hle/service/am/storage.h"
#include "core/hle/service/am/system_buffer_manager.h" #include "core/hle/service/am/system_buffer_manager.h"
namespace Service::AM { namespace Service::AM {
@ -76,8 +74,8 @@ struct Applet {
u32 application_core_usage_mode{}; u32 application_core_usage_mode{};
// Application functions // Application functions
bool gameplay_recording_supported{}; bool game_play_recording_supported{};
GameplayRecordingState gameplay_recording_state{GameplayRecordingState::Disabled}; GamePlayRecordingState game_play_recording_state{GamePlayRecordingState::Disabled};
bool jit_service_launched{}; bool jit_service_launched{};
bool is_running{}; bool is_running{};
bool application_crash_report_enabled{}; bool application_crash_report_enabled{};

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