Compare commits

...

54 Commits

Author SHA1 Message Date
50135e480a Android 237 2024-02-09 00:56:52 +00:00
8495adfed5 Merge yuzu-emu#12964 2024-02-09 00:56:52 +00:00
0b624b8bfa Merge yuzu-emu#12955 2024-02-09 00:56:52 +00:00
98d0ccc78c Merge yuzu-emu#12951 2024-02-09 00:56:52 +00:00
bfc714e16c Merge yuzu-emu#12920 2024-02-09 00:56:51 +00:00
18fa807cd3 Merge yuzu-emu#12756 2024-02-09 00:56:51 +00:00
d5a7230036 Merge yuzu-emu#12749 2024-02-09 00:56:51 +00:00
afb77e967f Merge yuzu-emu#12461 2024-02-09 00:56:51 +00: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
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
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
c10e720ba9 Merge pull request #12883 from FernandoS27/memory_manager_mem
MemoryManager: Reduce the page table size based on last big page address.
2024-02-06 10:25:03 -05:00
5016de3626 Merge pull request #12928 from german77/motion-mp
service: hid: Add multiprocess support to six axis input
2024-02-06 10:24:46 -05:00
d5fb9fd12c Merge pull request #12933 from german77/irs-interface
service: irs: Migrate service to new interface
2024-02-06 10:24:30 -05:00
c79b3af610 Merge pull request #12934 from german77/hid_debug_interface
service: hid: Migrate hid debug service to new interface
2024-02-06 10:24:20 -05:00
c0a383d960 web_backend: Fix compilation 2024-02-06 15:48:04 +01:00
b6106604c4 service: hid: Migrate hid debug service to new interface 2024-02-06 00:38:46 -06:00
12b6162852 service: irs: Migrate service to new interface 2024-02-06 00:14:16 -06:00
8f192b494a service: hid: Add multiprocess support to six axis input 2024-02-05 17:19:31 -06:00
372897aac4 service: hid: Ensure aruid data is initialized 2024-02-05 17:17:21 -06:00
fa47ac1c9f Common: Rename SplitRangeSet to OverlapRangeSet 2024-02-05 23:01:17 +01:00
a2f23746c2 Merge pull request #12905 from liamwhite/hwc-release
nvnflinger: release buffers before presentation sleep
2024-02-05 13:43:22 -05:00
215b13f2a2 Merge pull request #12924 from liamwhite/pedantic-unsigned
typed_address: test values are unsigned
2024-02-05 13:43:06 -05:00
35ed9425d7 Merge pull request #12925 from german77/linux-tab
yuzu: Fully hide linux tab
2024-02-05 13:41:31 -05:00
74cc8721c7 Merge pull request #12915 from german77/cheat
dmnt: cheats: Update cheat vm to latest version
2024-02-05 13:41:21 -05:00
8ef1db78b0 Merge pull request #12916 from liamwhite/float-fix
gdb: fix load/save of fp values in a32
2024-02-05 13:41:15 -05:00
18c8f10ff2 Merge pull request #12922 from FearlessTobi/lang-mappins
.tx/config: Use language mappings for android "tx pull"
2024-02-05 13:40:53 -05:00
96d881f087 yuzu: Fully hide linux tab 2024-02-05 11:58:20 -06:00
0e950baf41 typed_address: test values are unsigned 2024-02-05 12:47:10 -05:00
8113f55f4b dmnt: cheats: Silence memory errors 2024-02-05 11:08:24 -06:00
f296a9ce9a shader_recompiler: use only ConstOffset for OpImageFetch 2024-02-05 12:01:09 -05:00
ddbefc71cb .tx/config: Use language mappings for android "tx pull"
The language names we are using in the android resources differ from those on Transifex.

We need to manually specify mappings for them, so Transifex is able to place the files in the correct folders.
2024-02-05 15:57:13 +01:00
0d5a3abeae Buffer Cache: Refactor to use Range sets instead 2024-02-05 11:06:52 +01:00
85143e8376 gdb: fix load/save of fp values in a32 2024-02-04 20:28:43 -05:00
504abbd6e0 dmnt: cheats: Update cheat vm to latest version 2024-02-04 17:46:20 -06: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
5eb5c96750 nvnflinger: release buffers before presentation sleep 2024-02-03 17:14:43 -05:00
f740d8b9be MemoryManager: Reduce the page table size based on last big page address. 2024-02-01 13:00:36 +01:00
200 changed files with 6447 additions and 3748 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);
if (BUILD_EA) {
await tagAndPushEA(github, 'yuzu-emu', `yuzu-android`, execa);
} else {
await generateReadme(pulls, context, mergeResults, execa); await generateReadme(pulls, context, mergeResults, execa);
await tagAndPush(github, 'yuzu-emu', `yuzu-android`, execa, true); 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,18 @@
| Pull Request | Commit | Title | Author | Merged? |
|----|----|----|----|----|
| [12461](https://github.com/yuzu-emu/yuzu//pull/12461) | [`54eb8dcc9`](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 |
| [12756](https://github.com/yuzu-emu/yuzu//pull/12756) | [`cd3de0848`](https://github.com/yuzu-emu/yuzu//pull/12756/files) | general: applet multiprocess | [liamwhite](https://github.com/liamwhite/) | Yes |
| [12920](https://github.com/yuzu-emu/yuzu//pull/12920) | [`2600ac65c`](https://github.com/yuzu-emu/yuzu//pull/12920/files) | android: Move JNI setup and helpers to common | [t895](https://github.com/t895/) | Yes |
| [12951](https://github.com/yuzu-emu/yuzu//pull/12951) | [`fee263c59`](https://github.com/yuzu-emu/yuzu//pull/12951/files) | ipc: additional fixes | [liamwhite](https://github.com/liamwhite/) | Yes |
| [12955](https://github.com/yuzu-emu/yuzu//pull/12955) | [`8d2ad3d8f`](https://github.com/yuzu-emu/yuzu//pull/12955/files) | dmnt: cheat: Avoid invalidating cache on 32bit | [german77](https://github.com/german77/) | Yes |
| [12964](https://github.com/yuzu-emu/yuzu//pull/12964) | [`d93459db6`](https://github.com/yuzu-emu/yuzu//pull/12964/files) | android: Remove foreground service | [t895](https://github.com/t895/) | 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

@ -11,3 +11,4 @@ type = QT
file_filter = ../../src/android/app/src/main/res/values-<lang>/strings.xml file_filter = ../../src/android/app/src/main/res/values-<lang>/strings.xml
source_file = ../../src/android/app/src/main/res/values/strings.xml source_file = ../../src/android/app/src/main/res/values/strings.xml
type = ANDROID type = ANDROID
lang_map = ja_JP:ja, ko_KR:ko, pt_BR:pt-rBR, pt_PT:pt-rPT, ru_RU:ru, vi_VN:vi, zh_CN:zh-rCN, zh_TW:zh-rTW

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

@ -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

@ -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

@ -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"
@ -57,8 +59,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 +228,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 +411,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 +493,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 +503,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 +541,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 +571,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 +592,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 +748,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 +770,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 +788,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 +822,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 +864,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 +882,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 +914,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 +925,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 +948,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 +978,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 +991,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,20 +262,20 @@ 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);
} }
@ -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

@ -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>

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) { \ auto env = Common::Android::GetEnvForThread();
return 0; \ return static_cast<u64>(env->CallStaticLongMethod(
} \ native_library, s_get_size,
auto env = GetEnvForThread(); \ Common::Android::ToJString(Common::Android::GetEnvForThread(), filepath)));
jstring j_filepath = env->NewStringUTF(filepath.c_str()); \
return env->Caller(native_library, JMethodID, j_filepath); \
} }
ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
#undef F
#undef FR
#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) { \ auto env = Common::Android::GetEnvForThread();
return 0; \ return env->CallStaticBooleanMethod(
} \ native_library, s_is_directory,
auto env = GetEnvForThread(); \ Common::Android::ToJString(Common::Android::GetEnvForThread(), filepath));
jstring j_filepath = env->NewStringUTF(filepath.c_str()); \ }
jstring j_return = \
static_cast<jstring>(env->Caller(native_library, JMethodID, j_filepath)); \ bool Exists(const std::string& filepath) {
if (!j_return) { \ if (s_file_exists == nullptr) {
return {}; \ return 0;
} \ }
const jchar* jchars = env->GetStringChars(j_return, nullptr); \ auto env = Common::Android::GetEnvForThread();
const jsize length = env->GetStringLength(j_return); \ return env->CallStaticBooleanMethod(
const std::u16string_view string_view(reinterpret_cast<const char16_t*>(jchars), length); \ native_library, s_file_exists,
const std::string converted_string = Common::UTF16ToUTF8(string_view); \ Common::Android::ToJString(Common::Android::GetEnvForThread(), filepath));
env->ReleaseStringChars(j_return, jchars); \ }
return converted_string; \
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);
} }
ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH)
#undef F
#undef FH
} // 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

@ -186,68 +186,68 @@ static_assert(std::is_trivially_destructible_v<PhysicalAddress>);
static_assert(std::is_trivially_destructible_v<VirtualAddress>); static_assert(std::is_trivially_destructible_v<VirtualAddress>);
static_assert(std::is_trivially_destructible_v<ProcessAddress>); static_assert(std::is_trivially_destructible_v<ProcessAddress>);
static_assert(Null<uint64_t> == 0); static_assert(Null<uint64_t> == 0U);
static_assert(Null<PhysicalAddress> == Null<uint64_t>); static_assert(Null<PhysicalAddress> == Null<uint64_t>);
static_assert(Null<VirtualAddress> == Null<uint64_t>); static_assert(Null<VirtualAddress> == Null<uint64_t>);
static_assert(Null<ProcessAddress> == Null<uint64_t>); static_assert(Null<ProcessAddress> == Null<uint64_t>);
// Constructor/assignment validations. // Constructor/assignment validations.
static_assert([] { static_assert([] {
const PhysicalAddress a(5); const PhysicalAddress a(5U);
PhysicalAddress b(a); PhysicalAddress b(a);
return b; return b;
}() == PhysicalAddress(5)); }() == PhysicalAddress(5U));
static_assert([] { static_assert([] {
const PhysicalAddress a(5); const PhysicalAddress a(5U);
PhysicalAddress b(10); PhysicalAddress b(10U);
b = a; b = a;
return b; return b;
}() == PhysicalAddress(5)); }() == PhysicalAddress(5U));
// Arithmetic validations. // Arithmetic validations.
static_assert(PhysicalAddress(10) + 5 == PhysicalAddress(15)); static_assert(PhysicalAddress(10U) + 5U == PhysicalAddress(15U));
static_assert(PhysicalAddress(10) - 5 == PhysicalAddress(5)); static_assert(PhysicalAddress(10U) - 5U == PhysicalAddress(5U));
static_assert([] { static_assert([] {
PhysicalAddress v(10); PhysicalAddress v(10U);
v += 5; v += 5U;
return v; return v;
}() == PhysicalAddress(15)); }() == PhysicalAddress(15U));
static_assert([] { static_assert([] {
PhysicalAddress v(10); PhysicalAddress v(10U);
v -= 5; v -= 5U;
return v; return v;
}() == PhysicalAddress(5)); }() == PhysicalAddress(5U));
static_assert(PhysicalAddress(10)++ == PhysicalAddress(10)); static_assert(PhysicalAddress(10U)++ == PhysicalAddress(10U));
static_assert(++PhysicalAddress(10) == PhysicalAddress(11)); static_assert(++PhysicalAddress(10U) == PhysicalAddress(11U));
static_assert(PhysicalAddress(10)-- == PhysicalAddress(10)); static_assert(PhysicalAddress(10U)-- == PhysicalAddress(10U));
static_assert(--PhysicalAddress(10) == PhysicalAddress(9)); static_assert(--PhysicalAddress(10U) == PhysicalAddress(9U));
// Logical validations. // Logical validations.
static_assert((PhysicalAddress(0b11111111) >> 1) == 0b01111111); static_assert((PhysicalAddress(0b11111111U) >> 1) == 0b01111111U);
static_assert((PhysicalAddress(0b10101010) >> 1) == 0b01010101); static_assert((PhysicalAddress(0b10101010U) >> 1) == 0b01010101U);
static_assert((PhysicalAddress(0b11111111) << 1) == 0b111111110); static_assert((PhysicalAddress(0b11111111U) << 1) == 0b111111110U);
static_assert((PhysicalAddress(0b01010101) << 1) == 0b10101010); static_assert((PhysicalAddress(0b01010101U) << 1) == 0b10101010U);
static_assert((PhysicalAddress(0b11111111) & 0b01010101) == 0b01010101); static_assert((PhysicalAddress(0b11111111U) & 0b01010101U) == 0b01010101U);
static_assert((PhysicalAddress(0b11111111) & 0b10101010) == 0b10101010); static_assert((PhysicalAddress(0b11111111U) & 0b10101010U) == 0b10101010U);
static_assert((PhysicalAddress(0b01010101) & 0b10101010) == 0b00000000); static_assert((PhysicalAddress(0b01010101U) & 0b10101010U) == 0b00000000U);
static_assert((PhysicalAddress(0b00000000) | 0b01010101) == 0b01010101); static_assert((PhysicalAddress(0b00000000U) | 0b01010101U) == 0b01010101U);
static_assert((PhysicalAddress(0b11111111) | 0b01010101) == 0b11111111); static_assert((PhysicalAddress(0b11111111U) | 0b01010101U) == 0b11111111U);
static_assert((PhysicalAddress(0b10101010) | 0b01010101) == 0b11111111); static_assert((PhysicalAddress(0b10101010U) | 0b01010101U) == 0b11111111U);
// Comparisons. // Comparisons.
static_assert(PhysicalAddress(0) == PhysicalAddress(0)); static_assert(PhysicalAddress(0U) == PhysicalAddress(0U));
static_assert(PhysicalAddress(0) != PhysicalAddress(1)); static_assert(PhysicalAddress(0U) != PhysicalAddress(1U));
static_assert(PhysicalAddress(0) < PhysicalAddress(1)); static_assert(PhysicalAddress(0U) < PhysicalAddress(1U));
static_assert(PhysicalAddress(0) <= PhysicalAddress(1)); static_assert(PhysicalAddress(0U) <= PhysicalAddress(1U));
static_assert(PhysicalAddress(1) > PhysicalAddress(0)); static_assert(PhysicalAddress(1U) > PhysicalAddress(0U));
static_assert(PhysicalAddress(1) >= PhysicalAddress(0)); static_assert(PhysicalAddress(1U) >= PhysicalAddress(0U));
static_assert(!(PhysicalAddress(0) == PhysicalAddress(1))); static_assert(!(PhysicalAddress(0U) == PhysicalAddress(1U)));
static_assert(!(PhysicalAddress(0) != PhysicalAddress(0))); static_assert(!(PhysicalAddress(0U) != PhysicalAddress(0U)));
static_assert(!(PhysicalAddress(1) < PhysicalAddress(0))); static_assert(!(PhysicalAddress(1U) < PhysicalAddress(0U)));
static_assert(!(PhysicalAddress(1) <= PhysicalAddress(0))); static_assert(!(PhysicalAddress(1U) <= PhysicalAddress(0U)));
static_assert(!(PhysicalAddress(0) > PhysicalAddress(1))); static_assert(!(PhysicalAddress(0U) > PhysicalAddress(1U)));
static_assert(!(PhysicalAddress(0) >= PhysicalAddress(1))); static_assert(!(PhysicalAddress(0U) >= PhysicalAddress(1U)));
} // namespace Common } // namespace Common

View File

@ -383,7 +383,7 @@ std::string GDBStubA32::RegRead(const Kernel::KThread* thread, size_t id) const
} else if (id == CPSR_REGISTER) { } else if (id == CPSR_REGISTER) {
return ValueToHex(context.pstate); return ValueToHex(context.pstate);
} else if (id >= D0_REGISTER && id < Q0_REGISTER) { } else if (id >= D0_REGISTER && id < Q0_REGISTER) {
return ValueToHex(fprs[id - D0_REGISTER][0]); return ValueToHex(fprs[(id - D0_REGISTER) / 2][(id - D0_REGISTER) % 2]);
} else if (id >= Q0_REGISTER && id < FPSCR_REGISTER) { } else if (id >= Q0_REGISTER && id < FPSCR_REGISTER) {
return ValueToHex(fprs[id - Q0_REGISTER]); return ValueToHex(fprs[id - Q0_REGISTER]);
} else if (id == FPSCR_REGISTER) { } else if (id == FPSCR_REGISTER) {
@ -406,7 +406,7 @@ void GDBStubA32::RegWrite(Kernel::KThread* thread, size_t id, std::string_view v
} else if (id == CPSR_REGISTER) { } else if (id == CPSR_REGISTER) {
context.pstate = HexToValue<u32>(value); context.pstate = HexToValue<u32>(value);
} else if (id >= D0_REGISTER && id < Q0_REGISTER) { } else if (id >= D0_REGISTER && id < Q0_REGISTER) {
fprs[id - D0_REGISTER] = {HexToValue<u64>(value), 0}; fprs[(id - D0_REGISTER) / 2][(id - D0_REGISTER) % 2] = HexToValue<u64>(value);
} else if (id >= Q0_REGISTER && id < FPSCR_REGISTER) { } else if (id >= Q0_REGISTER && id < FPSCR_REGISTER) {
fprs[id - Q0_REGISTER] = HexToValue<u128>(value); fprs[id - Q0_REGISTER] = HexToValue<u128>(value);
} else if (id == FPSCR_REGISTER) { } else if (id == FPSCR_REGISTER) {

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

@ -532,6 +532,7 @@ void DeviceMemoryManager<Traits>::UpdatePagesCachedCount(DAddr addr, size_t size
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 +548,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

@ -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

@ -130,9 +130,9 @@ enum class AppletProgramId : u64 {
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,
}; };

View File

@ -68,9 +68,9 @@ void SoftwareKeyboard::Initialize() {
case LibraryAppletMode::AllForeground: case LibraryAppletMode::AllForeground:
InitializeForeground(); InitializeForeground();
break; break;
case LibraryAppletMode::Background: case LibraryAppletMode::PartialForeground:
case LibraryAppletMode::BackgroundIndirectDisplay: case LibraryAppletMode::PartialForegroundIndirectDisplay:
InitializeBackground(applet_mode); InitializePartialForeground(applet_mode);
break; break;
default: default:
ASSERT_MSG(false, "Invalid LibraryAppletMode={}", applet_mode); ASSERT_MSG(false, "Invalid LibraryAppletMode={}", applet_mode);
@ -243,7 +243,7 @@ void SoftwareKeyboard::InitializeForeground() {
InitializeFrontendNormalKeyboard(); InitializeFrontendNormalKeyboard();
} }
void SoftwareKeyboard::InitializeBackground(LibraryAppletMode library_applet_mode) { void SoftwareKeyboard::InitializePartialForeground(LibraryAppletMode library_applet_mode) {
LOG_INFO(Service_AM, "Initializing Inline Software Keyboard Applet."); LOG_INFO(Service_AM, "Initializing Inline Software Keyboard Applet.");
is_background = true; is_background = true;
@ -258,9 +258,9 @@ void SoftwareKeyboard::InitializeBackground(LibraryAppletMode library_applet_mod
swkbd_inline_initialize_arg.size()); swkbd_inline_initialize_arg.size());
if (swkbd_initialize_arg.library_applet_mode_flag) { if (swkbd_initialize_arg.library_applet_mode_flag) {
ASSERT(library_applet_mode == LibraryAppletMode::Background); ASSERT(library_applet_mode == LibraryAppletMode::PartialForeground);
} else { } else {
ASSERT(library_applet_mode == LibraryAppletMode::BackgroundIndirectDisplay); ASSERT(library_applet_mode == LibraryAppletMode::PartialForegroundIndirectDisplay);
} }
} }

View File

@ -62,7 +62,7 @@ private:
void InitializeForeground(); void InitializeForeground();
/// Initializes the inline software keyboard. /// Initializes the inline software keyboard.
void InitializeBackground(LibraryAppletMode library_applet_mode); void InitializePartialForeground(LibraryAppletMode library_applet_mode);
/// Processes the text check sent by the application. /// Processes the text check sent by the application.
void ProcessTextCheck(); void ProcessTextCheck();

View File

@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include "common/settings.h"
#include "core/hle/kernel/k_transfer_memory.h" #include "core/hle/kernel/k_transfer_memory.h"
#include "core/hle/service/am/applet_data_broker.h" #include "core/hle/service/am/applet_data_broker.h"
#include "core/hle/service/am/applet_manager.h" #include "core/hle/service/am/applet_manager.h"
@ -16,6 +17,34 @@ namespace Service::AM {
namespace { namespace {
bool ShouldCreateGuestApplet(AppletId applet_id) {
#define X(Name, name) \
if (applet_id == AppletId::Name && \
Settings::values.name##_applet_mode.GetValue() != Settings::AppletMode::LLE) { \
return false; \
}
X(Cabinet, cabinet)
X(Controller, controller)
X(DataErase, data_erase)
X(Error, error)
X(NetConnect, net_connect)
X(ProfileSelect, player_select)
X(SoftwareKeyboard, swkbd)
X(MiiEdit, mii_edit)
X(Web, web)
X(Shop, shop)
X(PhotoViewer, photo_viewer)
X(OfflineWeb, offline_web)
X(LoginShare, login_share)
X(WebAuth, wifi_web_auth)
X(MyPage, my_page)
#undef X
return true;
}
AppletProgramId AppletIdToProgramId(AppletId applet_id) { AppletProgramId AppletIdToProgramId(AppletId applet_id) {
switch (applet_id) { switch (applet_id) {
case AppletId::OverlayDisplay: case AppletId::OverlayDisplay:
@ -63,8 +92,9 @@ AppletProgramId AppletIdToProgramId(AppletId applet_id) {
} }
} }
[[maybe_unused]] std::shared_ptr<ILibraryAppletAccessor> CreateGuestApplet( std::shared_ptr<ILibraryAppletAccessor> CreateGuestApplet(Core::System& system,
Core::System& system, std::shared_ptr<Applet> caller_applet, AppletId applet_id, std::shared_ptr<Applet> caller_applet,
AppletId applet_id,
LibraryAppletMode mode) { LibraryAppletMode mode) {
const auto program_id = static_cast<u64>(AppletIdToProgramId(applet_id)); const auto program_id = static_cast<u64>(AppletIdToProgramId(applet_id));
if (program_id == 0) { if (program_id == 0) {
@ -87,24 +117,18 @@ AppletProgramId AppletIdToProgramId(AppletId applet_id) {
// Set focus state // Set focus state
switch (mode) { switch (mode) {
case LibraryAppletMode::AllForeground: case LibraryAppletMode::AllForeground:
case LibraryAppletMode::NoUI: case LibraryAppletMode::NoUi:
applet->focus_state = FocusState::InFocus; case LibraryAppletMode::PartialForeground:
case LibraryAppletMode::PartialForegroundIndirectDisplay:
applet->hid_registration.EnableAppletToGetInput(true); applet->hid_registration.EnableAppletToGetInput(true);
applet->focus_state = FocusState::InFocus;
applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground); applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground);
applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged);
break; break;
case LibraryAppletMode::AllForegroundInitiallyHidden: case LibraryAppletMode::AllForegroundInitiallyHidden:
applet->system_buffer_manager.SetWindowVisibility(false);
applet->focus_state = FocusState::NotInFocus;
applet->hid_registration.EnableAppletToGetInput(false); applet->hid_registration.EnableAppletToGetInput(false);
applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged); applet->focus_state = FocusState::NotInFocus;
break; applet->system_buffer_manager.SetWindowVisibility(false);
case LibraryAppletMode::Background: applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoBackground);
case LibraryAppletMode::BackgroundIndirectDisplay:
default:
applet->focus_state = FocusState::Background;
applet->hid_registration.EnableAppletToGetInput(true);
applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged);
break; break;
} }
@ -117,8 +141,9 @@ AppletProgramId AppletIdToProgramId(AppletId applet_id) {
return std::make_shared<ILibraryAppletAccessor>(system, broker, applet); return std::make_shared<ILibraryAppletAccessor>(system, broker, applet);
} }
[[maybe_unused]] std::shared_ptr<ILibraryAppletAccessor> CreateFrontendApplet( std::shared_ptr<ILibraryAppletAccessor> CreateFrontendApplet(Core::System& system,
Core::System& system, std::shared_ptr<Applet> caller_applet, AppletId applet_id, std::shared_ptr<Applet> caller_applet,
AppletId applet_id,
LibraryAppletMode mode) { LibraryAppletMode mode) {
const auto program_id = static_cast<u64>(AppletIdToProgramId(applet_id)); const auto program_id = static_cast<u64>(AppletIdToProgramId(applet_id));
@ -163,7 +188,13 @@ void ILibraryAppletCreator::CreateLibraryApplet(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called with applet_id={:08X}, applet_mode={:08X}", applet_id, LOG_DEBUG(Service_AM, "called with applet_id={:08X}, applet_mode={:08X}", applet_id,
applet_mode); applet_mode);
auto library_applet = CreateFrontendApplet(system, applet, applet_id, applet_mode); std::shared_ptr<ILibraryAppletAccessor> library_applet;
if (ShouldCreateGuestApplet(applet_id)) {
library_applet = CreateGuestApplet(system, applet, applet_id, applet_mode);
}
if (!library_applet) {
library_applet = CreateFrontendApplet(system, applet, applet_id, applet_mode);
}
if (!library_applet) { if (!library_applet) {
LOG_ERROR(Service_AM, "Applet doesn't exist! applet_id={}", applet_id); LOG_ERROR(Service_AM, "Applet doesn't exist! applet_id={}", applet_id);

View File

@ -1,10 +1,13 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
#include "core/hle/result.h"
#include "core/hle/service/am/am_results.h" #include "core/hle/service/am/am_results.h"
#include "core/hle/service/am/frontend/applets.h" #include "core/hle/service/am/frontend/applets.h"
#include "core/hle/service/am/self_controller.h" #include "core/hle/service/am/self_controller.h"
#include "core/hle/service/caps/caps_su.h" #include "core/hle/service/caps/caps_su.h"
#include "core/hle/service/hle_ipc.h"
#include "core/hle/service/ipc_helpers.h" #include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/nvnflinger/fb_share_buffer_manager.h" #include "core/hle/service/nvnflinger/fb_share_buffer_manager.h"
#include "core/hle/service/nvnflinger/nvnflinger.h" #include "core/hle/service/nvnflinger/nvnflinger.h"
@ -47,7 +50,7 @@ ISelfController::ISelfController(Core::System& system_, std::shared_ptr<Applet>
{50, &ISelfController::SetHandlesRequestToDisplay, "SetHandlesRequestToDisplay"}, {50, &ISelfController::SetHandlesRequestToDisplay, "SetHandlesRequestToDisplay"},
{51, &ISelfController::ApproveToDisplay, "ApproveToDisplay"}, {51, &ISelfController::ApproveToDisplay, "ApproveToDisplay"},
{60, nullptr, "OverrideAutoSleepTimeAndDimmingTime"}, {60, nullptr, "OverrideAutoSleepTimeAndDimmingTime"},
{61, nullptr, "SetMediaPlaybackState"}, {61, &ISelfController::SetMediaPlaybackState, "SetMediaPlaybackState"},
{62, &ISelfController::SetIdleTimeDetectionExtension, "SetIdleTimeDetectionExtension"}, {62, &ISelfController::SetIdleTimeDetectionExtension, "SetIdleTimeDetectionExtension"},
{63, &ISelfController::GetIdleTimeDetectionExtension, "GetIdleTimeDetectionExtension"}, {63, &ISelfController::GetIdleTimeDetectionExtension, "GetIdleTimeDetectionExtension"},
{64, nullptr, "SetInputDetectionSourceSet"}, {64, nullptr, "SetInputDetectionSourceSet"},
@ -288,7 +291,8 @@ void ISelfController::GetSystemSharedBufferHandle(HLERequestContext& ctx) {
} }
Result ISelfController::EnsureBufferSharingEnabled(Kernel::KProcess* process) { Result ISelfController::EnsureBufferSharingEnabled(Kernel::KProcess* process) {
if (applet->system_buffer_manager.Initialize(&nvnflinger, process, applet->applet_id)) { if (applet->system_buffer_manager.Initialize(&nvnflinger, process, applet->applet_id,
applet->library_applet_mode)) {
return ResultSuccess; return ResultSuccess;
} }
@ -323,6 +327,16 @@ void ISelfController::ApproveToDisplay(HLERequestContext& ctx) {
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
} }
void ISelfController::SetMediaPlaybackState(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u8 state = rp.Pop<u8>();
LOG_WARNING(Service_AM, "(STUBBED) called, state={}", state);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void ISelfController::SetIdleTimeDetectionExtension(HLERequestContext& ctx) { void ISelfController::SetIdleTimeDetectionExtension(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx}; IPC::RequestParser rp{ctx};

View File

@ -3,6 +3,7 @@
#pragma once #pragma once
#include "core/hle/service/hle_ipc.h"
#include "core/hle/service/kernel_helpers.h" #include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/service.h" #include "core/hle/service/service.h"
@ -38,6 +39,7 @@ private:
void CreateManagedDisplaySeparableLayer(HLERequestContext& ctx); void CreateManagedDisplaySeparableLayer(HLERequestContext& ctx);
void SetHandlesRequestToDisplay(HLERequestContext& ctx); void SetHandlesRequestToDisplay(HLERequestContext& ctx);
void ApproveToDisplay(HLERequestContext& ctx); void ApproveToDisplay(HLERequestContext& ctx);
void SetMediaPlaybackState(HLERequestContext& ctx);
void SetIdleTimeDetectionExtension(HLERequestContext& ctx); void SetIdleTimeDetectionExtension(HLERequestContext& ctx);
void GetIdleTimeDetectionExtension(HLERequestContext& ctx); void GetIdleTimeDetectionExtension(HLERequestContext& ctx);
void ReportUserIsActive(HLERequestContext& ctx); void ReportUserIsActive(HLERequestContext& ctx);

View File

@ -17,11 +17,12 @@ SystemBufferManager::~SystemBufferManager() {
// Clean up shared layers. // Clean up shared layers.
if (m_buffer_sharing_enabled) { if (m_buffer_sharing_enabled) {
m_nvnflinger->GetSystemBufferManager().Finalize(m_process);
} }
} }
bool SystemBufferManager::Initialize(Nvnflinger::Nvnflinger* nvnflinger, Kernel::KProcess* process, bool SystemBufferManager::Initialize(Nvnflinger::Nvnflinger* nvnflinger, Kernel::KProcess* process,
AppletId applet_id) { AppletId applet_id, LibraryAppletMode mode) {
if (m_nvnflinger) { if (m_nvnflinger) {
return m_buffer_sharing_enabled; return m_buffer_sharing_enabled;
} }
@ -36,9 +37,15 @@ bool SystemBufferManager::Initialize(Nvnflinger::Nvnflinger* nvnflinger, Kernel:
return false; return false;
} }
Nvnflinger::LayerBlending blending = Nvnflinger::LayerBlending::None;
if (mode == LibraryAppletMode::PartialForeground ||
mode == LibraryAppletMode::PartialForegroundIndirectDisplay) {
blending = Nvnflinger::LayerBlending::Coverage;
}
const auto display_id = m_nvnflinger->OpenDisplay("Default").value(); const auto display_id = m_nvnflinger->OpenDisplay("Default").value();
const auto res = m_nvnflinger->GetSystemBufferManager().Initialize( const auto res = m_nvnflinger->GetSystemBufferManager().Initialize(
&m_system_shared_buffer_id, &m_system_shared_layer_id, display_id); m_process, &m_system_shared_buffer_id, &m_system_shared_layer_id, display_id, blending);
if (res.IsSuccess()) { if (res.IsSuccess()) {
m_buffer_sharing_enabled = true; m_buffer_sharing_enabled = true;
@ -62,8 +69,12 @@ void SystemBufferManager::SetWindowVisibility(bool visible) {
Result SystemBufferManager::WriteAppletCaptureBuffer(bool* out_was_written, Result SystemBufferManager::WriteAppletCaptureBuffer(bool* out_was_written,
s32* out_fbshare_layer_index) { s32* out_fbshare_layer_index) {
// TODO if (!m_buffer_sharing_enabled) {
R_SUCCEED(); return VI::ResultPermissionDenied;
}
return m_nvnflinger->GetSystemBufferManager().WriteAppletCaptureBuffer(out_was_written,
out_fbshare_layer_index);
} }
} // namespace Service::AM } // namespace Service::AM

View File

@ -27,7 +27,8 @@ public:
SystemBufferManager(); SystemBufferManager();
~SystemBufferManager(); ~SystemBufferManager();
bool Initialize(Nvnflinger::Nvnflinger* flinger, Kernel::KProcess* process, AppletId applet_id); bool Initialize(Nvnflinger::Nvnflinger* flinger, Kernel::KProcess* process, AppletId applet_id,
LibraryAppletMode mode);
void GetSystemSharedLayerHandle(u64* out_system_shared_buffer_id, void GetSystemSharedLayerHandle(u64* out_system_shared_buffer_id,
u64* out_system_shared_layer_id) { u64* out_system_shared_layer_id) {

View File

@ -62,12 +62,12 @@ void IWindowController::SetAppletWindowVisibility(HLERequestContext& ctx) {
applet->hid_registration.EnableAppletToGetInput(visible); applet->hid_registration.EnableAppletToGetInput(visible);
if (visible) { if (visible) {
applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground);
applet->focus_state = FocusState::InFocus; applet->focus_state = FocusState::InFocus;
applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground);
} else { } else {
applet->focus_state = FocusState::NotInFocus; applet->focus_state = FocusState::NotInFocus;
applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoBackground);
} }
applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged);
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);

View File

@ -280,7 +280,7 @@ void ReadInArgument(bool is_domain, CallArguments& args, const u8* raw_data, HLE
u32 value{}; u32 value{};
std::memcpy(&value, raw_data + ArgOffset, ArgSize); std::memcpy(&value, raw_data + ArgOffset, ArgSize);
std::get<ArgIndex>(args) = ctx.GetDomainHandler<ArgType::Type>(value - 1); std::get<ArgIndex>(args) = ctx.GetDomainHandler<typename ArgType::element_type>(value - 1);
return ReadInArgument<MethodArguments, CallArguments, ArgAlign, ArgEnd, HandleIndex, InBufferIndex, OutBufferIndex, true, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp); return ReadInArgument<MethodArguments, CallArguments, ArgAlign, ArgEnd, HandleIndex, InBufferIndex, OutBufferIndex, true, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp);
} else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InCopyHandle) { } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InCopyHandle) {

View File

@ -65,6 +65,14 @@ struct ClientProcessId {
}; };
struct ProcessId { struct ProcessId {
explicit ProcessId() : pid() {}
explicit ProcessId(u64 p) : pid(p) {}
/* implicit */ ProcessId(const ClientProcessId& c) : pid(c.pid) {}
bool operator==(const ProcessId& rhs) const {
return pid == rhs.pid;
}
explicit operator bool() const { explicit operator bool() const {
return pid != 0; return pid != 0;
} }

View File

@ -36,7 +36,7 @@ void LoopProcess(Core::System& system) {
server_manager->RegisterNamedService( server_manager->RegisterNamedService(
"hid:sys", std::make_shared<IHidSystemServer>(system, resource_manager, firmware_settings)); "hid:sys", std::make_shared<IHidSystemServer>(system, resource_manager, firmware_settings));
server_manager->RegisterNamedService("hidbus", std::make_shared<HidBus>(system)); server_manager->RegisterNamedService("hidbus", std::make_shared<Hidbus>(system));
server_manager->RegisterNamedService("irs", std::make_shared<IRS::IRS>(system)); server_manager->RegisterNamedService("irs", std::make_shared<IRS::IRS>(system));
server_manager->RegisterNamedService("irs:sys", std::make_shared<IRS::IRS_SYS>(system)); server_manager->RegisterNamedService("irs:sys", std::make_shared<IRS::IRS_SYS>(system));

View File

@ -3,6 +3,7 @@
#include <algorithm> #include <algorithm>
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/hid/hid_debug_server.h" #include "core/hle/service/hid/hid_debug_server.h"
#include "core/hle/service/ipc_helpers.h" #include "core/hle/service/ipc_helpers.h"
#include "hid_core/hid_types.h" #include "hid_core/hid_types.h"
@ -11,7 +12,6 @@
#include "hid_core/resources/touch_screen/gesture.h" #include "hid_core/resources/touch_screen/gesture.h"
#include "hid_core/resources/touch_screen/touch_screen.h" #include "hid_core/resources/touch_screen/touch_screen.h"
#include "hid_core/resources/touch_screen/touch_types.h"
namespace Service::HID { namespace Service::HID {
@ -24,14 +24,14 @@ IHidDebugServer::IHidDebugServer(Core::System& system_, std::shared_ptr<Resource
{0, nullptr, "DeactivateDebugPad"}, {0, nullptr, "DeactivateDebugPad"},
{1, nullptr, "SetDebugPadAutoPilotState"}, {1, nullptr, "SetDebugPadAutoPilotState"},
{2, nullptr, "UnsetDebugPadAutoPilotState"}, {2, nullptr, "UnsetDebugPadAutoPilotState"},
{10, &IHidDebugServer::DeactivateTouchScreen, "DeactivateTouchScreen"}, {10, C<&IHidDebugServer::DeactivateTouchScreen>, "DeactivateTouchScreen"},
{11, &IHidDebugServer::SetTouchScreenAutoPilotState, "SetTouchScreenAutoPilotState"}, {11, C<&IHidDebugServer::SetTouchScreenAutoPilotState>, "SetTouchScreenAutoPilotState"},
{12, &IHidDebugServer::UnsetTouchScreenAutoPilotState, "UnsetTouchScreenAutoPilotState"}, {12, C<&IHidDebugServer::UnsetTouchScreenAutoPilotState>, "UnsetTouchScreenAutoPilotState"},
{13, &IHidDebugServer::GetTouchScreenConfiguration, "GetTouchScreenConfiguration"}, {13, C<&IHidDebugServer::GetTouchScreenConfiguration>, "GetTouchScreenConfiguration"},
{14, &IHidDebugServer::ProcessTouchScreenAutoTune, "ProcessTouchScreenAutoTune"}, {14, C<&IHidDebugServer::ProcessTouchScreenAutoTune>, "ProcessTouchScreenAutoTune"},
{15, &IHidDebugServer::ForceStopTouchScreenManagement, "ForceStopTouchScreenManagement"}, {15, C<&IHidDebugServer::ForceStopTouchScreenManagement>, "ForceStopTouchScreenManagement"},
{16, &IHidDebugServer::ForceRestartTouchScreenManagement, "ForceRestartTouchScreenManagement"}, {16, C<&IHidDebugServer::ForceRestartTouchScreenManagement>, "ForceRestartTouchScreenManagement"},
{17, &IHidDebugServer::IsTouchScreenManaged, "IsTouchScreenManaged"}, {17, C<&IHidDebugServer::IsTouchScreenManaged>, "IsTouchScreenManaged"},
{20, nullptr, "DeactivateMouse"}, {20, nullptr, "DeactivateMouse"},
{21, nullptr, "SetMouseAutoPilotState"}, {21, nullptr, "SetMouseAutoPilotState"},
{22, nullptr, "UnsetMouseAutoPilotState"}, {22, nullptr, "UnsetMouseAutoPilotState"},
@ -47,7 +47,7 @@ IHidDebugServer::IHidDebugServer(Core::System& system_, std::shared_ptr<Resource
{60, nullptr, "ClearNpadSystemCommonPolicy"}, {60, nullptr, "ClearNpadSystemCommonPolicy"},
{61, nullptr, "DeactivateNpad"}, {61, nullptr, "DeactivateNpad"},
{62, nullptr, "ForceDisconnectNpad"}, {62, nullptr, "ForceDisconnectNpad"},
{91, &IHidDebugServer::DeactivateGesture, "DeactivateGesture"}, {91, C<&IHidDebugServer::DeactivateGesture>, "DeactivateGesture"},
{110, nullptr, "DeactivateHomeButton"}, {110, nullptr, "DeactivateHomeButton"},
{111, nullptr, "SetHomeButtonAutoPilotState"}, {111, nullptr, "SetHomeButtonAutoPilotState"},
{112, nullptr, "UnsetHomeButtonAutoPilotState"}, {112, nullptr, "UnsetHomeButtonAutoPilotState"},
@ -160,169 +160,122 @@ IHidDebugServer::IHidDebugServer(Core::System& system_, std::shared_ptr<Resource
} }
IHidDebugServer::~IHidDebugServer() = default; IHidDebugServer::~IHidDebugServer() = default;
void IHidDebugServer::DeactivateTouchScreen(HLERequestContext& ctx) {
Result IHidDebugServer::DeactivateTouchScreen() {
LOG_INFO(Service_HID, "called"); LOG_INFO(Service_HID, "called");
Result result = ResultSuccess;
if (!firmware_settings->IsDeviceManaged()) { if (!firmware_settings->IsDeviceManaged()) {
result = GetResourceManager()->GetTouchScreen()->Deactivate(); R_RETURN(GetResourceManager()->GetTouchScreen()->Deactivate());
} }
IPC::ResponseBuilder rb{ctx, 2}; R_SUCCEED();
rb.Push(result);
} }
void IHidDebugServer::SetTouchScreenAutoPilotState(HLERequestContext& ctx) { Result IHidDebugServer::SetTouchScreenAutoPilotState(
InArray<TouchState, BufferAttr_HipcMapAlias> auto_pilot_buffer) {
AutoPilotState auto_pilot{}; AutoPilotState auto_pilot{};
auto_pilot.count = ctx.GetReadBufferNumElements<TouchState>();
const auto buffer = ctx.ReadBuffer();
auto_pilot.count = std::min(auto_pilot.count, static_cast<u64>(auto_pilot.state.size())); auto_pilot.count =
memcpy(auto_pilot.state.data(), buffer.data(), auto_pilot.count * sizeof(TouchState)); static_cast<u64>(std::min(auto_pilot_buffer.size(), auto_pilot.state.size()));
memcpy(auto_pilot.state.data(), auto_pilot_buffer.data(),
auto_pilot.count * sizeof(TouchState));
LOG_INFO(Service_HID, "called, auto_pilot_count={}", auto_pilot.count); LOG_INFO(Service_HID, "called, auto_pilot_count={}", auto_pilot.count);
const Result result = R_RETURN(GetResourceManager()->GetTouchScreen()->SetTouchScreenAutoPilotState(auto_pilot));
GetResourceManager()->GetTouchScreen()->SetTouchScreenAutoPilotState(auto_pilot);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
} }
void IHidDebugServer::UnsetTouchScreenAutoPilotState(HLERequestContext& ctx) { Result IHidDebugServer::UnsetTouchScreenAutoPilotState() {
LOG_INFO(Service_HID, "called"); LOG_INFO(Service_HID, "called");
R_RETURN(GetResourceManager()->GetTouchScreen()->UnsetTouchScreenAutoPilotState());
const Result result = GetResourceManager()->GetTouchScreen()->UnsetTouchScreenAutoPilotState();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
} }
void IHidDebugServer::GetTouchScreenConfiguration(HLERequestContext& ctx) { Result IHidDebugServer::GetTouchScreenConfiguration(
IPC::RequestParser rp{ctx}; Out<Core::HID::TouchScreenConfigurationForNx> out_touchscreen_config,
const auto applet_resource_user_id{rp.Pop<u64>()}; ClientAppletResourceUserId aruid) {
LOG_INFO(Service_HID, "called, applet_resource_user_id={}", aruid.pid);
LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); R_TRY(GetResourceManager()->GetTouchScreen()->GetTouchScreenConfiguration(
*out_touchscreen_config, aruid.pid));
Core::HID::TouchScreenConfigurationForNx touchscreen_config{}; if (out_touchscreen_config->mode != Core::HID::TouchScreenModeForNx::Heat2 &&
const Result result = GetResourceManager()->GetTouchScreen()->GetTouchScreenConfiguration( out_touchscreen_config->mode != Core::HID::TouchScreenModeForNx::Finger) {
touchscreen_config, applet_resource_user_id); out_touchscreen_config->mode = Core::HID::TouchScreenModeForNx::UseSystemSetting;
if (touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Heat2 &&
touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Finger) {
touchscreen_config.mode = Core::HID::TouchScreenModeForNx::UseSystemSetting;
} }
IPC::ResponseBuilder rb{ctx, 6}; R_SUCCEED();
rb.Push(result);
rb.PushRaw(touchscreen_config);
} }
void IHidDebugServer::ProcessTouchScreenAutoTune(HLERequestContext& ctx) { Result IHidDebugServer::ProcessTouchScreenAutoTune() {
LOG_INFO(Service_HID, "called"); LOG_INFO(Service_HID, "called");
R_RETURN(GetResourceManager()->GetTouchScreen()->ProcessTouchScreenAutoTune());
Result result = GetResourceManager()->GetTouchScreen()->ProcessTouchScreenAutoTune();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
} }
void IHidDebugServer::ForceStopTouchScreenManagement(HLERequestContext& ctx) { Result IHidDebugServer::ForceStopTouchScreenManagement() {
LOG_INFO(Service_HID, "called"); LOG_INFO(Service_HID, "called");
if (!firmware_settings->IsDeviceManaged()) { if (!firmware_settings->IsDeviceManaged()) {
IPC::ResponseBuilder rb{ctx, 2}; R_SUCCEED();
rb.Push(ResultSuccess);
return;
} }
Result result = ResultSuccess;
bool is_touch_active{};
bool is_gesture_active{};
auto touch_screen = GetResourceManager()->GetTouchScreen(); auto touch_screen = GetResourceManager()->GetTouchScreen();
auto gesture = GetResourceManager()->GetGesture(); auto gesture = GetResourceManager()->GetGesture();
if (firmware_settings->IsTouchI2cManaged()) { if (firmware_settings->IsTouchI2cManaged()) {
result = touch_screen->IsActive(is_touch_active); bool is_touch_active{};
if (result.IsSuccess()) { bool is_gesture_active{};
result = gesture->IsActive(is_gesture_active); R_TRY(touch_screen->IsActive(is_touch_active));
R_TRY(gesture->IsActive(is_gesture_active));
if (is_touch_active) {
R_TRY(touch_screen->Deactivate());
} }
if (result.IsSuccess() && is_touch_active) { if (is_gesture_active) {
result = touch_screen->Deactivate(); R_TRY(gesture->Deactivate());
}
if (result.IsSuccess() && is_gesture_active) {
result = gesture->Deactivate();
} }
} }
IPC::ResponseBuilder rb{ctx, 2}; R_SUCCEED();
rb.Push(result);
} }
void IHidDebugServer::ForceRestartTouchScreenManagement(HLERequestContext& ctx) { Result IHidDebugServer::ForceRestartTouchScreenManagement(u32 basic_gesture_id,
IPC::RequestParser rp{ctx}; ClientAppletResourceUserId aruid) {
struct Parameters {
u32 basic_gesture_id;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
LOG_INFO(Service_HID, "called, basic_gesture_id={}, applet_resource_user_id={}", LOG_INFO(Service_HID, "called, basic_gesture_id={}, applet_resource_user_id={}",
parameters.basic_gesture_id, parameters.applet_resource_user_id); basic_gesture_id, aruid.pid);
Result result = ResultSuccess;
auto touch_screen = GetResourceManager()->GetTouchScreen(); auto touch_screen = GetResourceManager()->GetTouchScreen();
auto gesture = GetResourceManager()->GetGesture(); auto gesture = GetResourceManager()->GetGesture();
if (firmware_settings->IsDeviceManaged() && firmware_settings->IsTouchI2cManaged()) { if (firmware_settings->IsDeviceManaged() && firmware_settings->IsTouchI2cManaged()) {
result = gesture->Activate(); R_TRY(gesture->Activate());
if (result.IsSuccess()) { R_TRY(gesture->Activate(aruid.pid, basic_gesture_id));
result = R_TRY(touch_screen->Activate());
gesture->Activate(parameters.applet_resource_user_id, parameters.basic_gesture_id); R_TRY(touch_screen->Activate(aruid.pid));
}
if (result.IsSuccess()) {
result = touch_screen->Activate();
}
if (result.IsSuccess()) {
result = touch_screen->Activate(parameters.applet_resource_user_id);
}
} }
IPC::ResponseBuilder rb{ctx, 2}; R_SUCCEED();
rb.Push(result);
} }
void IHidDebugServer::IsTouchScreenManaged(HLERequestContext& ctx) { Result IHidDebugServer::IsTouchScreenManaged(Out<bool> out_is_managed) {
LOG_INFO(Service_HID, "called"); LOG_INFO(Service_HID, "called");
bool is_touch_active{}; bool is_touch_active{};
bool is_gesture_active{}; bool is_gesture_active{};
R_TRY(GetResourceManager()->GetTouchScreen()->IsActive(is_touch_active));
R_TRY(GetResourceManager()->GetGesture()->IsActive(is_gesture_active));
Result result = GetResourceManager()->GetTouchScreen()->IsActive(is_touch_active); *out_is_managed = is_touch_active || is_gesture_active;
if (result.IsSuccess()) { R_SUCCEED();
result = GetResourceManager()->GetGesture()->IsActive(is_gesture_active);
} }
IPC::ResponseBuilder rb{ctx, 3}; Result IHidDebugServer::DeactivateGesture() {
rb.Push(result);
rb.Push(is_touch_active | is_gesture_active);
}
void IHidDebugServer::DeactivateGesture(HLERequestContext& ctx) {
LOG_INFO(Service_HID, "called"); LOG_INFO(Service_HID, "called");
Result result = ResultSuccess;
if (!firmware_settings->IsDeviceManaged()) { if (!firmware_settings->IsDeviceManaged()) {
result = GetResourceManager()->GetGesture()->Deactivate(); R_RETURN(GetResourceManager()->GetGesture()->Deactivate());
} }
IPC::ResponseBuilder rb{ctx, 2}; R_SUCCEED();
rb.Push(result);
} }
std::shared_ptr<ResourceManager> IHidDebugServer::GetResourceManager() { std::shared_ptr<ResourceManager> IHidDebugServer::GetResourceManager() {

View File

@ -3,7 +3,9 @@
#pragma once #pragma once
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/service.h" #include "core/hle/service/service.h"
#include "hid_core/resources/touch_screen/touch_types.h"
namespace Core { namespace Core {
class System; class System;
@ -20,15 +22,19 @@ public:
~IHidDebugServer() override; ~IHidDebugServer() override;
private: private:
void DeactivateTouchScreen(HLERequestContext& ctx); Result DeactivateTouchScreen();
void SetTouchScreenAutoPilotState(HLERequestContext& ctx); Result SetTouchScreenAutoPilotState(
void UnsetTouchScreenAutoPilotState(HLERequestContext& ctx); InArray<TouchState, BufferAttr_HipcMapAlias> auto_pilot_buffer);
void GetTouchScreenConfiguration(HLERequestContext& ctx); Result UnsetTouchScreenAutoPilotState();
void ProcessTouchScreenAutoTune(HLERequestContext& ctx); Result GetTouchScreenConfiguration(
void ForceStopTouchScreenManagement(HLERequestContext& ctx); Out<Core::HID::TouchScreenConfigurationForNx> out_touchscreen_config,
void ForceRestartTouchScreenManagement(HLERequestContext& ctx); ClientAppletResourceUserId aruid);
void IsTouchScreenManaged(HLERequestContext& ctx); Result ProcessTouchScreenAutoTune();
void DeactivateGesture(HLERequestContext& ctx); Result ForceStopTouchScreenManagement();
Result ForceRestartTouchScreenManagement(u32 basic_gesture_id,
ClientAppletResourceUserId aruid);
Result IsTouchScreenManaged(Out<bool> out_is_managed);
Result DeactivateGesture();
std::shared_ptr<ResourceManager> GetResourceManager(); std::shared_ptr<ResourceManager> GetResourceManager();

View File

@ -9,6 +9,7 @@
#include "core/hle/kernel/k_readable_event.h" #include "core/hle/kernel/k_readable_event.h"
#include "core/hle/kernel/k_shared_memory.h" #include "core/hle/kernel/k_shared_memory.h"
#include "core/hle/kernel/k_transfer_memory.h" #include "core/hle/kernel/k_transfer_memory.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/hid/hidbus.h" #include "core/hle/service/hid/hidbus.h"
#include "core/hle/service/ipc_helpers.h" #include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/service.h" #include "core/hle/service/service.h"
@ -22,25 +23,25 @@ namespace Service::HID {
// (15ms, 66Hz) // (15ms, 66Hz)
constexpr auto hidbus_update_ns = std::chrono::nanoseconds{15 * 1000 * 1000}; constexpr auto hidbus_update_ns = std::chrono::nanoseconds{15 * 1000 * 1000};
HidBus::HidBus(Core::System& system_) Hidbus::Hidbus(Core::System& system_)
: ServiceFramework{system_, "hidbus"}, service_context{system_, service_name} { : ServiceFramework{system_, "hidbus"}, service_context{system_, service_name} {
// clang-format off // clang-format off
static const FunctionInfo functions[] = { static const FunctionInfo functions[] = {
{1, &HidBus::GetBusHandle, "GetBusHandle"}, {1, C<&Hidbus::GetBusHandle>, "GetBusHandle"},
{2, &HidBus::IsExternalDeviceConnected, "IsExternalDeviceConnected"}, {2, C<&Hidbus::IsExternalDeviceConnected>, "IsExternalDeviceConnected"},
{3, &HidBus::Initialize, "Initialize"}, {3, C<&Hidbus::Initialize>, "Initialize"},
{4, &HidBus::Finalize, "Finalize"}, {4, C<&Hidbus::Finalize>, "Finalize"},
{5, &HidBus::EnableExternalDevice, "EnableExternalDevice"}, {5, C<&Hidbus::EnableExternalDevice>, "EnableExternalDevice"},
{6, &HidBus::GetExternalDeviceId, "GetExternalDeviceId"}, {6, C<&Hidbus::GetExternalDeviceId>, "GetExternalDeviceId"},
{7, &HidBus::SendCommandAsync, "SendCommandAsync"}, {7, C<&Hidbus::SendCommandAsync>, "SendCommandAsync"},
{8, &HidBus::GetSendCommandAsynceResult, "GetSendCommandAsynceResult"}, {8, C<&Hidbus::GetSendCommandAsynceResult>, "GetSendCommandAsynceResult"},
{9, &HidBus::SetEventForSendCommandAsycResult, "SetEventForSendCommandAsycResult"}, {9, C<&Hidbus::SetEventForSendCommandAsycResult>, "SetEventForSendCommandAsycResult"},
{10, &HidBus::GetSharedMemoryHandle, "GetSharedMemoryHandle"}, {10, C<&Hidbus::GetSharedMemoryHandle>, "GetSharedMemoryHandle"},
{11, &HidBus::EnableJoyPollingReceiveMode, "EnableJoyPollingReceiveMode"}, {11, C<&Hidbus::EnableJoyPollingReceiveMode>, "EnableJoyPollingReceiveMode"},
{12, &HidBus::DisableJoyPollingReceiveMode, "DisableJoyPollingReceiveMode"}, {12, C<&Hidbus::DisableJoyPollingReceiveMode>, "DisableJoyPollingReceiveMode"},
{13, nullptr, "GetPollingData"}, {13, nullptr, "GetPollingData"},
{14, &HidBus::SetStatusManagerType, "SetStatusManagerType"}, {14, C<&Hidbus::SetStatusManagerType>, "SetStatusManagerType"},
}; };
// clang-format on // clang-format on
@ -60,11 +61,11 @@ HidBus::HidBus(Core::System& system_)
hidbus_update_event); hidbus_update_event);
} }
HidBus::~HidBus() { Hidbus::~Hidbus() {
system.CoreTiming().UnscheduleEvent(hidbus_update_event); system.CoreTiming().UnscheduleEvent(hidbus_update_event);
} }
void HidBus::UpdateHidbus(std::chrono::nanoseconds ns_late) { void Hidbus::UpdateHidbus(std::chrono::nanoseconds ns_late) {
if (is_hidbus_enabled) { if (is_hidbus_enabled) {
for (std::size_t i = 0; i < devices.size(); ++i) { for (std::size_t i = 0; i < devices.size(); ++i) {
if (!devices[i].is_device_initialized) { if (!devices[i].is_device_initialized) {
@ -84,7 +85,7 @@ void HidBus::UpdateHidbus(std::chrono::nanoseconds ns_late) {
} }
} }
std::optional<std::size_t> HidBus::GetDeviceIndexFromHandle(BusHandle handle) const { std::optional<std::size_t> Hidbus::GetDeviceIndexFromHandle(BusHandle handle) const {
for (std::size_t i = 0; i < devices.size(); ++i) { for (std::size_t i = 0; i < devices.size(); ++i) {
const auto& device_handle = devices[i].handle; const auto& device_handle = devices[i].handle;
if (handle.abstracted_pad_id == device_handle.abstracted_pad_id && if (handle.abstracted_pad_id == device_handle.abstracted_pad_id &&
@ -98,20 +99,11 @@ std::optional<std::size_t> HidBus::GetDeviceIndexFromHandle(BusHandle handle) co
return std::nullopt; return std::nullopt;
} }
void HidBus::GetBusHandle(HLERequestContext& ctx) { Result Hidbus::GetBusHandle(Out<bool> out_is_valid, Out<BusHandle> out_bus_handle,
IPC::RequestParser rp{ctx}; Core::HID::NpadIdType npad_id, BusType bus_type,
struct Parameters { AppletResourceUserId aruid) {
Core::HID::NpadIdType npad_id; LOG_INFO(Service_HID, "called, npad_id={}, bus_type={}, applet_resource_user_id={}", npad_id,
INSERT_PADDING_WORDS_NOINIT(1); bus_type, aruid.pid);
BusType bus_type;
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
LOG_INFO(Service_HID, "called, npad_id={}, bus_type={}, applet_resource_user_id={}",
parameters.npad_id, parameters.bus_type, parameters.applet_resource_user_id);
bool is_handle_found = 0; bool is_handle_found = 0;
std::size_t handle_index = 0; std::size_t handle_index = 0;
@ -121,8 +113,8 @@ void HidBus::GetBusHandle(HLERequestContext& ctx) {
if (!handle.is_valid) { if (!handle.is_valid) {
continue; continue;
} }
if (static_cast<Core::HID::NpadIdType>(handle.player_number) == parameters.npad_id && if (handle.player_number.As<Core::HID::NpadIdType>() == npad_id &&
handle.bus_type_id == static_cast<u8>(parameters.bus_type)) { handle.bus_type_id == static_cast<u8>(bus_type)) {
is_handle_found = true; is_handle_found = true;
handle_index = i; handle_index = i;
break; break;
@ -135,84 +127,55 @@ void HidBus::GetBusHandle(HLERequestContext& ctx) {
if (devices[i].handle.is_valid) { if (devices[i].handle.is_valid) {
continue; continue;
} }
devices[i].handle = { devices[i].handle.raw = 0;
.abstracted_pad_id = static_cast<u8>(i), devices[i].handle.abstracted_pad_id.Assign(i);
.internal_index = static_cast<u8>(i), devices[i].handle.internal_index.Assign(i);
.player_number = static_cast<u8>(parameters.npad_id), devices[i].handle.player_number.Assign(static_cast<u8>(npad_id));
.bus_type_id = static_cast<u8>(parameters.bus_type), devices[i].handle.bus_type_id.Assign(static_cast<u8>(bus_type));
.is_valid = true, devices[i].handle.is_valid.Assign(true);
};
handle_index = i; handle_index = i;
break; break;
} }
} }
struct OutData { *out_is_valid = true;
bool is_valid; *out_bus_handle = devices[handle_index].handle;
INSERT_PADDING_BYTES(7); R_SUCCEED();
BusHandle handle;
};
static_assert(sizeof(OutData) == 0x10, "OutData has incorrect size.");
const OutData out_data{
.is_valid = true,
.handle = devices[handle_index].handle,
};
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(ResultSuccess);
rb.PushRaw(out_data);
} }
void HidBus::IsExternalDeviceConnected(HLERequestContext& ctx) { Result Hidbus::IsExternalDeviceConnected(Out<bool> out_is_connected, BusHandle bus_handle) {
IPC::RequestParser rp{ctx};
const auto bus_handle_{rp.PopRaw<BusHandle>()};
LOG_INFO(Service_HID, LOG_INFO(Service_HID,
"Called, abstracted_pad_id={}, bus_type={}, internal_index={}, " "Called, abstracted_pad_id={}, bus_type={}, internal_index={}, "
"player_number={}, is_valid={}", "player_number={}, is_valid={}",
bus_handle_.abstracted_pad_id, bus_handle_.bus_type_id, bus_handle_.internal_index, bus_handle.abstracted_pad_id, bus_handle.bus_type_id, bus_handle.internal_index,
bus_handle_.player_number, bus_handle_.is_valid); bus_handle.player_number, bus_handle.is_valid);
const auto device_index = GetDeviceIndexFromHandle(bus_handle_); const auto device_index = GetDeviceIndexFromHandle(bus_handle);
if (device_index) { R_UNLESS(device_index.has_value(), ResultUnknown);
const auto& device = devices[device_index.value()].device;
const bool is_attached = device->IsDeviceActivated();
IPC::ResponseBuilder rb{ctx, 3}; *out_is_connected = devices[device_index.value()].device->IsDeviceActivated();
rb.Push(ResultSuccess); R_SUCCEED();
rb.Push(is_attached);
return;
} }
LOG_ERROR(Service_HID, "Invalid handle"); Result Hidbus::Initialize(BusHandle bus_handle, AppletResourceUserId aruid) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}
void HidBus::Initialize(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto bus_handle_{rp.PopRaw<BusHandle>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_INFO(Service_HID, LOG_INFO(Service_HID,
"called, abstracted_pad_id={} bus_type={} internal_index={} " "called, abstracted_pad_id={} bus_type={} internal_index={} "
"player_number={} is_valid={}, applet_resource_user_id={}", "player_number={} is_valid={}, applet_resource_user_id={}",
bus_handle_.abstracted_pad_id, bus_handle_.bus_type_id, bus_handle_.internal_index, bus_handle.abstracted_pad_id, bus_handle.bus_type_id, bus_handle.internal_index,
bus_handle_.player_number, bus_handle_.is_valid, applet_resource_user_id); bus_handle.player_number, bus_handle.is_valid, aruid.pid);
is_hidbus_enabled = true; is_hidbus_enabled = true;
const auto device_index = GetDeviceIndexFromHandle(bus_handle_); const auto device_index = GetDeviceIndexFromHandle(bus_handle);
R_UNLESS(device_index.has_value(), ResultUnknown);
if (device_index) {
const auto entry_index = devices[device_index.value()].handle.internal_index; const auto entry_index = devices[device_index.value()].handle.internal_index;
auto& cur_entry = hidbus_status.entries[entry_index]; auto& cur_entry = hidbus_status.entries[entry_index];
if (bus_handle_.internal_index == 0 && Settings::values.enable_ring_controller) { if (bus_handle.internal_index == 0 && Settings::values.enable_ring_controller) {
MakeDevice<RingController>(bus_handle_); MakeDevice<RingController>(bus_handle);
devices[device_index.value()].is_device_initialized = true; devices[device_index.value()].is_device_initialized = true;
devices[device_index.value()].device->ActivateDevice(); devices[device_index.value()].device->ActivateDevice();
cur_entry.is_in_focus = true; cur_entry.is_in_focus = true;
@ -221,7 +184,7 @@ void HidBus::Initialize(HLERequestContext& ctx) {
cur_entry.is_enabled = false; cur_entry.is_enabled = false;
cur_entry.is_polling_mode = false; cur_entry.is_polling_mode = false;
} else { } else {
MakeDevice<HidbusStubbed>(bus_handle_); MakeDevice<HidbusStubbed>(bus_handle);
devices[device_index.value()].is_device_initialized = true; devices[device_index.value()].is_device_initialized = true;
cur_entry.is_in_focus = true; cur_entry.is_in_focus = true;
cur_entry.is_connected = false; cur_entry.is_connected = false;
@ -232,32 +195,20 @@ void HidBus::Initialize(HLERequestContext& ctx) {
std::memcpy(system.Kernel().GetHidBusSharedMem().GetPointer(), &hidbus_status, std::memcpy(system.Kernel().GetHidBusSharedMem().GetPointer(), &hidbus_status,
sizeof(hidbus_status)); sizeof(hidbus_status));
R_SUCCEED();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
return;
} }
LOG_ERROR(Service_HID, "Invalid handle"); Result Hidbus::Finalize(BusHandle bus_handle, AppletResourceUserId aruid) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}
void HidBus::Finalize(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto bus_handle_{rp.PopRaw<BusHandle>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_INFO(Service_HID, LOG_INFO(Service_HID,
"called, abstracted_pad_id={}, bus_type={}, internal_index={}, " "called, abstracted_pad_id={}, bus_type={}, internal_index={}, "
"player_number={}, is_valid={}, applet_resource_user_id={}", "player_number={}, is_valid={}, applet_resource_user_id={}",
bus_handle_.abstracted_pad_id, bus_handle_.bus_type_id, bus_handle_.internal_index, bus_handle.abstracted_pad_id, bus_handle.bus_type_id, bus_handle.internal_index,
bus_handle_.player_number, bus_handle_.is_valid, applet_resource_user_id); bus_handle.player_number, bus_handle.is_valid, aruid.pid);
const auto device_index = GetDeviceIndexFromHandle(bus_handle_); const auto device_index = GetDeviceIndexFromHandle(bus_handle);
R_UNLESS(device_index.has_value(), ResultUnknown);
if (device_index) {
const auto entry_index = devices[device_index.value()].handle.internal_index; const auto entry_index = devices[device_index.value()].handle.internal_index;
auto& cur_entry = hidbus_status.entries[entry_index]; auto& cur_entry = hidbus_status.entries[entry_index];
auto& device = devices[device_index.value()].device; auto& device = devices[device_index.value()].device;
@ -271,252 +222,136 @@ void HidBus::Finalize(HLERequestContext& ctx) {
cur_entry.is_polling_mode = false; cur_entry.is_polling_mode = false;
std::memcpy(system.Kernel().GetHidBusSharedMem().GetPointer(), &hidbus_status, std::memcpy(system.Kernel().GetHidBusSharedMem().GetPointer(), &hidbus_status,
sizeof(hidbus_status)); sizeof(hidbus_status));
R_SUCCEED();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
return;
} }
LOG_ERROR(Service_HID, "Invalid handle"); Result Hidbus::EnableExternalDevice(bool is_enabled, BusHandle bus_handle, u64 inval,
IPC::ResponseBuilder rb{ctx, 2}; AppletResourceUserId aruid) {
rb.Push(ResultUnknown);
return;
}
void HidBus::EnableExternalDevice(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
bool enable;
INSERT_PADDING_BYTES_NOINIT(7);
BusHandle bus_handle;
u64 inval;
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x20, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
LOG_DEBUG(Service_HID, LOG_DEBUG(Service_HID,
"called, enable={}, abstracted_pad_id={}, bus_type={}, internal_index={}, " "called, enable={}, abstracted_pad_id={}, bus_type={}, internal_index={}, "
"player_number={}, is_valid={}, inval={}, applet_resource_user_id{}", "player_number={}, is_valid={}, inval={}, applet_resource_user_id{}",
parameters.enable, parameters.bus_handle.abstracted_pad_id, is_enabled, bus_handle.abstracted_pad_id, bus_handle.bus_type_id,
parameters.bus_handle.bus_type_id, parameters.bus_handle.internal_index, bus_handle.internal_index, bus_handle.player_number, bus_handle.is_valid, inval,
parameters.bus_handle.player_number, parameters.bus_handle.is_valid, parameters.inval, aruid.pid);
parameters.applet_resource_user_id);
const auto device_index = GetDeviceIndexFromHandle(parameters.bus_handle); const auto device_index = GetDeviceIndexFromHandle(bus_handle);
if (device_index) { R_UNLESS(device_index.has_value(), ResultUnknown);
auto& device = devices[device_index.value()].device; devices[device_index.value()].device->Enable(is_enabled);
device->Enable(parameters.enable); R_SUCCEED();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
return;
} }
LOG_ERROR(Service_HID, "Invalid handle"); Result Hidbus::GetExternalDeviceId(Out<u32> out_device_id, BusHandle bus_handle) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}
void HidBus::GetExternalDeviceId(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto bus_handle_{rp.PopRaw<BusHandle>()};
LOG_DEBUG(Service_HID, LOG_DEBUG(Service_HID,
"called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, " "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, "
"is_valid={}", "is_valid={}",
bus_handle_.abstracted_pad_id, bus_handle_.bus_type_id, bus_handle_.internal_index, bus_handle.abstracted_pad_id, bus_handle.bus_type_id, bus_handle.internal_index,
bus_handle_.player_number, bus_handle_.is_valid); bus_handle.player_number, bus_handle.is_valid);
const auto device_index = GetDeviceIndexFromHandle(bus_handle_); const auto device_index = GetDeviceIndexFromHandle(bus_handle);
if (device_index) { R_UNLESS(device_index.has_value(), ResultUnknown);
const auto& device = devices[device_index.value()].device;
u32 device_id = device->GetDeviceId(); *out_device_id = devices[device_index.value()].device->GetDeviceId();
IPC::ResponseBuilder rb{ctx, 3}; R_SUCCEED();
rb.Push(ResultSuccess);
rb.Push<u32>(device_id);
return;
} }
LOG_ERROR(Service_HID, "Invalid handle"); Result Hidbus::SendCommandAsync(BusHandle bus_handle,
IPC::ResponseBuilder rb{ctx, 2}; InBuffer<BufferAttr_HipcAutoSelect> buffer_data) {
rb.Push(ResultUnknown);
return;
}
void HidBus::SendCommandAsync(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto data = ctx.ReadBuffer();
const auto bus_handle_{rp.PopRaw<BusHandle>()};
LOG_DEBUG(Service_HID, LOG_DEBUG(Service_HID,
"called, data_size={}, abstracted_pad_id={}, bus_type={}, internal_index={}, " "called, data_size={}, abstracted_pad_id={}, bus_type={}, internal_index={}, "
"player_number={}, is_valid={}", "player_number={}, is_valid={}",
data.size(), bus_handle_.abstracted_pad_id, bus_handle_.bus_type_id, buffer_data.size(), bus_handle.abstracted_pad_id, bus_handle.bus_type_id,
bus_handle_.internal_index, bus_handle_.player_number, bus_handle_.is_valid); bus_handle.internal_index, bus_handle.player_number, bus_handle.is_valid);
const auto device_index = GetDeviceIndexFromHandle(bus_handle_); const auto device_index = GetDeviceIndexFromHandle(bus_handle);
if (device_index) { R_UNLESS(device_index.has_value(), ResultUnknown);
auto& device = devices[device_index.value()].device;
device->SetCommand(data);
IPC::ResponseBuilder rb{ctx, 2}; devices[device_index.value()].device->SetCommand(buffer_data);
rb.Push(ResultSuccess); R_SUCCEED();
return;
}
LOG_ERROR(Service_HID, "Invalid handle");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}; };
void HidBus::GetSendCommandAsynceResult(HLERequestContext& ctx) { Result Hidbus::GetSendCommandAsynceResult(Out<u64> out_data_size, BusHandle bus_handle,
IPC::RequestParser rp{ctx}; OutBuffer<BufferAttr_HipcAutoSelect> out_buffer_data) {
const auto bus_handle_{rp.PopRaw<BusHandle>()};
LOG_DEBUG(Service_HID, LOG_DEBUG(Service_HID,
"called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, " "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, "
"is_valid={}", "is_valid={}",
bus_handle_.abstracted_pad_id, bus_handle_.bus_type_id, bus_handle_.internal_index, bus_handle.abstracted_pad_id, bus_handle.bus_type_id, bus_handle.internal_index,
bus_handle_.player_number, bus_handle_.is_valid); bus_handle.player_number, bus_handle.is_valid);
const auto device_index = GetDeviceIndexFromHandle(bus_handle_); const auto device_index = GetDeviceIndexFromHandle(bus_handle);
if (device_index) { R_UNLESS(device_index.has_value(), ResultUnknown);
const auto& device = devices[device_index.value()].device;
const std::vector<u8> data = device->GetReply();
const u64 data_size = ctx.WriteBuffer(data);
IPC::ResponseBuilder rb{ctx, 4}; *out_data_size = devices[device_index.value()].device->GetReply(out_buffer_data);
rb.Push(ResultSuccess); R_SUCCEED();
rb.Push<u64>(data_size);
return;
}
LOG_ERROR(Service_HID, "Invalid handle");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}; };
void HidBus::SetEventForSendCommandAsycResult(HLERequestContext& ctx) { Result Hidbus::SetEventForSendCommandAsycResult(OutCopyHandle<Kernel::KReadableEvent> out_event,
IPC::RequestParser rp{ctx}; BusHandle bus_handle) {
const auto bus_handle_{rp.PopRaw<BusHandle>()};
LOG_INFO(Service_HID, LOG_INFO(Service_HID,
"called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, " "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, "
"is_valid={}", "is_valid={}",
bus_handle_.abstracted_pad_id, bus_handle_.bus_type_id, bus_handle_.internal_index, bus_handle.abstracted_pad_id, bus_handle.bus_type_id, bus_handle.internal_index,
bus_handle_.player_number, bus_handle_.is_valid); bus_handle.player_number, bus_handle.is_valid);
const auto device_index = GetDeviceIndexFromHandle(bus_handle_); const auto device_index = GetDeviceIndexFromHandle(bus_handle);
if (device_index) { R_UNLESS(device_index.has_value(), ResultUnknown);
const auto& device = devices[device_index.value()].device;
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(device->GetSendCommandAsycEvent());
return;
}
LOG_ERROR(Service_HID, "Invalid handle"); *out_event = &devices[device_index.value()].device->GetSendCommandAsycEvent();
IPC::ResponseBuilder rb{ctx, 2}; R_SUCCEED();
rb.Push(ResultUnknown);
return;
}; };
void HidBus::GetSharedMemoryHandle(HLERequestContext& ctx) { Result Hidbus::GetSharedMemoryHandle(OutCopyHandle<Kernel::KSharedMemory> out_shared_memory) {
LOG_DEBUG(Service_HID, "called"); LOG_DEBUG(Service_HID, "called");
IPC::ResponseBuilder rb{ctx, 2, 1}; *out_shared_memory = &system.Kernel().GetHidBusSharedMem();
rb.Push(ResultSuccess); R_SUCCEED();
rb.PushCopyObjects(&system.Kernel().GetHidBusSharedMem());
} }
void HidBus::EnableJoyPollingReceiveMode(HLERequestContext& ctx) { Result Hidbus::EnableJoyPollingReceiveMode(u32 t_mem_size, JoyPollingMode polling_mode,
IPC::RequestParser rp{ctx}; BusHandle bus_handle,
const auto t_mem_size{rp.Pop<u32>()}; InCopyHandle<Kernel::KTransferMemory> t_mem) {
const auto t_mem_handle{ctx.GetCopyHandle(0)};
const auto polling_mode_{rp.PopEnum<JoyPollingMode>()};
const auto bus_handle_{rp.PopRaw<BusHandle>()};
ASSERT_MSG(t_mem_size == 0x1000, "t_mem_size is not 0x1000 bytes"); ASSERT_MSG(t_mem_size == 0x1000, "t_mem_size is not 0x1000 bytes");
ASSERT_MSG(t_mem->GetSize() == t_mem_size, "t_mem has incorrect size");
auto t_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(t_mem_handle);
if (t_mem.IsNull()) {
LOG_ERROR(Service_HID, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}
ASSERT_MSG(t_mem->GetSize() == 0x1000, "t_mem has incorrect size");
LOG_INFO(Service_HID, LOG_INFO(Service_HID,
"called, t_mem_handle=0x{:08X}, polling_mode={}, abstracted_pad_id={}, bus_type={}, " "called, polling_mode={}, abstracted_pad_id={}, bus_type={}, "
"internal_index={}, player_number={}, is_valid={}", "internal_index={}, player_number={}, is_valid={}",
t_mem_handle, polling_mode_, bus_handle_.abstracted_pad_id, bus_handle_.bus_type_id, polling_mode, bus_handle.abstracted_pad_id, bus_handle.bus_type_id,
bus_handle_.internal_index, bus_handle_.player_number, bus_handle_.is_valid); bus_handle.internal_index, bus_handle.player_number, bus_handle.is_valid);
const auto device_index = GetDeviceIndexFromHandle(bus_handle_); const auto device_index = GetDeviceIndexFromHandle(bus_handle);
R_UNLESS(device_index.has_value(), ResultUnknown);
if (device_index) {
auto& device = devices[device_index.value()].device; auto& device = devices[device_index.value()].device;
device->SetPollingMode(polling_mode_); device->SetPollingMode(polling_mode);
device->SetTransferMemoryAddress(t_mem->GetSourceAddress()); device->SetTransferMemoryAddress(t_mem->GetSourceAddress());
R_SUCCEED();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
return;
} }
LOG_ERROR(Service_HID, "Invalid handle"); Result Hidbus::DisableJoyPollingReceiveMode(BusHandle bus_handle) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}
void HidBus::DisableJoyPollingReceiveMode(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto bus_handle_{rp.PopRaw<BusHandle>()};
LOG_INFO(Service_HID, LOG_INFO(Service_HID,
"called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, " "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, "
"is_valid={}", "is_valid={}",
bus_handle_.abstracted_pad_id, bus_handle_.bus_type_id, bus_handle_.internal_index, bus_handle.abstracted_pad_id, bus_handle.bus_type_id, bus_handle.internal_index,
bus_handle_.player_number, bus_handle_.is_valid); bus_handle.player_number, bus_handle.is_valid);
const auto device_index = GetDeviceIndexFromHandle(bus_handle_); const auto device_index = GetDeviceIndexFromHandle(bus_handle);
R_UNLESS(device_index.has_value(), ResultUnknown);
if (device_index) {
auto& device = devices[device_index.value()].device; auto& device = devices[device_index.value()].device;
device->DisablePollingMode(); device->DisablePollingMode();
R_SUCCEED();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
return;
} }
LOG_ERROR(Service_HID, "Invalid handle"); Result Hidbus::SetStatusManagerType(StatusManagerType manager_type) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}
void HidBus::SetStatusManagerType(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto manager_type{rp.PopEnum<StatusManagerType>()};
LOG_WARNING(Service_HID, "(STUBBED) called, manager_type={}", manager_type); LOG_WARNING(Service_HID, "(STUBBED) called, manager_type={}", manager_type);
R_SUCCEED();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}; };
} // namespace Service::HID } // namespace Service::HID

View File

@ -5,8 +5,10 @@
#include <functional> #include <functional>
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/kernel_helpers.h" #include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/service.h" #include "core/hle/service/service.h"
#include "hid_core/hid_types.h"
#include "hid_core/hidbus/hidbus_base.h" #include "hid_core/hidbus/hidbus_base.h"
namespace Core::Timing { namespace Core::Timing {
@ -19,10 +21,10 @@ class System;
namespace Service::HID { namespace Service::HID {
class HidBus final : public ServiceFramework<HidBus> { class Hidbus final : public ServiceFramework<Hidbus> {
public: public:
explicit HidBus(Core::System& system_); explicit Hidbus(Core::System& system_);
~HidBus() override; ~Hidbus() override;
private: private:
static const std::size_t max_number_of_handles = 0x13; static const std::size_t max_number_of_handles = 0x13;
@ -41,7 +43,7 @@ private:
}; };
// This is nn::hidbus::BusType // This is nn::hidbus::BusType
enum class BusType : u32 { enum class BusType : u64 {
LeftJoyRail, LeftJoyRail,
RightJoyRail, RightJoyRail,
InternalBus, // Lark microphone InternalBus, // Lark microphone
@ -51,11 +53,15 @@ private:
// This is nn::hidbus::BusHandle // This is nn::hidbus::BusHandle
struct BusHandle { struct BusHandle {
u32 abstracted_pad_id; union {
u8 internal_index; u64 raw{};
u8 player_number;
u8 bus_type_id; BitField<0, 32, u64> abstracted_pad_id;
bool is_valid; BitField<32, 8, u64> internal_index;
BitField<40, 8, u64> player_number;
BitField<48, 8, u64> bus_type_id;
BitField<56, 1, u64> is_valid;
};
}; };
static_assert(sizeof(BusHandle) == 0x8, "BusHandle is an invalid size"); static_assert(sizeof(BusHandle) == 0x8, "BusHandle is an invalid size");
@ -94,19 +100,38 @@ private:
std::unique_ptr<HidbusBase> device{nullptr}; std::unique_ptr<HidbusBase> device{nullptr};
}; };
void GetBusHandle(HLERequestContext& ctx); Result GetBusHandle(Out<bool> out_is_valid, Out<BusHandle> out_bus_handle,
void IsExternalDeviceConnected(HLERequestContext& ctx); Core::HID::NpadIdType npad_id, BusType bus_type,
void Initialize(HLERequestContext& ctx); AppletResourceUserId aruid);
void Finalize(HLERequestContext& ctx);
void EnableExternalDevice(HLERequestContext& ctx); Result IsExternalDeviceConnected(Out<bool> out_is_connected, BusHandle bus_handle);
void GetExternalDeviceId(HLERequestContext& ctx);
void SendCommandAsync(HLERequestContext& ctx); Result Initialize(BusHandle bus_handle, AppletResourceUserId aruid);
void GetSendCommandAsynceResult(HLERequestContext& ctx);
void SetEventForSendCommandAsycResult(HLERequestContext& ctx); Result Finalize(BusHandle bus_handle, AppletResourceUserId aruid);
void GetSharedMemoryHandle(HLERequestContext& ctx);
void EnableJoyPollingReceiveMode(HLERequestContext& ctx); Result EnableExternalDevice(bool is_enabled, BusHandle bus_handle, u64 inval,
void DisableJoyPollingReceiveMode(HLERequestContext& ctx); AppletResourceUserId aruid);
void SetStatusManagerType(HLERequestContext& ctx);
Result GetExternalDeviceId(Out<u32> out_device_id, BusHandle bus_handle);
Result SendCommandAsync(BusHandle bus_handle, InBuffer<BufferAttr_HipcAutoSelect> buffer_data);
Result GetSendCommandAsynceResult(Out<u64> out_data_size, BusHandle bus_handle,
OutBuffer<BufferAttr_HipcAutoSelect> out_buffer_data);
Result SetEventForSendCommandAsycResult(OutCopyHandle<Kernel::KReadableEvent> out_event,
BusHandle bus_handle);
Result GetSharedMemoryHandle(OutCopyHandle<Kernel::KSharedMemory> out_shared_memory);
Result EnableJoyPollingReceiveMode(u32 t_mem_size, JoyPollingMode polling_mode,
BusHandle bus_handle,
InCopyHandle<Kernel::KTransferMemory> t_mem);
Result DisableJoyPollingReceiveMode(BusHandle bus_handle);
Result SetStatusManagerType(StatusManagerType manager_type);
void UpdateHidbus(std::chrono::nanoseconds ns_late); void UpdateHidbus(std::chrono::nanoseconds ns_late);
std::optional<std::size_t> GetDeviceIndexFromHandle(BusHandle handle) const; std::optional<std::size_t> GetDeviceIndexFromHandle(BusHandle handle) const;

View File

@ -9,6 +9,7 @@
#include "core/hle/kernel/k_shared_memory.h" #include "core/hle/kernel/k_shared_memory.h"
#include "core/hle/kernel/k_transfer_memory.h" #include "core/hle/kernel/k_transfer_memory.h"
#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/kernel.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/hid/irs.h" #include "core/hle/service/hid/irs.h"
#include "core/hle/service/ipc_helpers.h" #include "core/hle/service/ipc_helpers.h"
#include "core/memory.h" #include "core/memory.h"
@ -28,24 +29,24 @@ namespace Service::IRS {
IRS::IRS(Core::System& system_) : ServiceFramework{system_, "irs"} { IRS::IRS(Core::System& system_) : ServiceFramework{system_, "irs"} {
// clang-format off // clang-format off
static const FunctionInfo functions[] = { static const FunctionInfo functions[] = {
{302, &IRS::ActivateIrsensor, "ActivateIrsensor"}, {302, C<&IRS::ActivateIrsensor>, "ActivateIrsensor"},
{303, &IRS::DeactivateIrsensor, "DeactivateIrsensor"}, {303, C<&IRS::DeactivateIrsensor>, "DeactivateIrsensor"},
{304, &IRS::GetIrsensorSharedMemoryHandle, "GetIrsensorSharedMemoryHandle"}, {304, C<&IRS::GetIrsensorSharedMemoryHandle>, "GetIrsensorSharedMemoryHandle"},
{305, &IRS::StopImageProcessor, "StopImageProcessor"}, {305, C<&IRS::StopImageProcessor>, "StopImageProcessor"},
{306, &IRS::RunMomentProcessor, "RunMomentProcessor"}, {306, C<&IRS::RunMomentProcessor>, "RunMomentProcessor"},
{307, &IRS::RunClusteringProcessor, "RunClusteringProcessor"}, {307, C<&IRS::RunClusteringProcessor>, "RunClusteringProcessor"},
{308, &IRS::RunImageTransferProcessor, "RunImageTransferProcessor"}, {308, C<&IRS::RunImageTransferProcessor>, "RunImageTransferProcessor"},
{309, &IRS::GetImageTransferProcessorState, "GetImageTransferProcessorState"}, {309, C<&IRS::GetImageTransferProcessorState>, "GetImageTransferProcessorState"},
{310, &IRS::RunTeraPluginProcessor, "RunTeraPluginProcessor"}, {310, C<&IRS::RunTeraPluginProcessor>, "RunTeraPluginProcessor"},
{311, &IRS::GetNpadIrCameraHandle, "GetNpadIrCameraHandle"}, {311, C<&IRS::GetNpadIrCameraHandle>, "GetNpadIrCameraHandle"},
{312, &IRS::RunPointingProcessor, "RunPointingProcessor"}, {312, C<&IRS::RunPointingProcessor>, "RunPointingProcessor"},
{313, &IRS::SuspendImageProcessor, "SuspendImageProcessor"}, {313, C<&IRS::SuspendImageProcessor>, "SuspendImageProcessor"},
{314, &IRS::CheckFirmwareVersion, "CheckFirmwareVersion"}, {314, C<&IRS::CheckFirmwareVersion>, "CheckFirmwareVersion"},
{315, &IRS::SetFunctionLevel, "SetFunctionLevel"}, {315, C<&IRS::SetFunctionLevel>, "SetFunctionLevel"},
{316, &IRS::RunImageTransferExProcessor, "RunImageTransferExProcessor"}, {316, C<&IRS::RunImageTransferExProcessor>, "RunImageTransferExProcessor"},
{317, &IRS::RunIrLedProcessor, "RunIrLedProcessor"}, {317, C<&IRS::RunIrLedProcessor>, "RunIrLedProcessor"},
{318, &IRS::StopImageProcessorAsync, "StopImageProcessorAsync"}, {318, C<&IRS::StopImageProcessorAsync>, "StopImageProcessorAsync"},
{319, &IRS::ActivateIrsensorWithFunctionLevel, "ActivateIrsensorWithFunctionLevel"}, {319, C<&IRS::ActivateIrsensorWithFunctionLevel>, "ActivateIrsensorWithFunctionLevel"},
}; };
// clang-format on // clang-format on
@ -57,489 +58,292 @@ IRS::IRS(Core::System& system_) : ServiceFramework{system_, "irs"} {
} }
IRS::~IRS() = default; IRS::~IRS() = default;
void IRS::ActivateIrsensor(HLERequestContext& ctx) { Result IRS::ActivateIrsensor(ClientAppletResourceUserId aruid) {
IPC::RequestParser rp{ctx}; LOG_WARNING(Service_IRS, "(STUBBED) called, applet_resource_user_id={}", aruid.pid);
const auto applet_resource_user_id{rp.Pop<u64>()}; R_SUCCEED();
LOG_WARNING(Service_IRS, "(STUBBED) called, applet_resource_user_id={}",
applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
} }
void IRS::DeactivateIrsensor(HLERequestContext& ctx) { Result IRS::DeactivateIrsensor(ClientAppletResourceUserId aruid) {
IPC::RequestParser rp{ctx}; LOG_WARNING(Service_IRS, "(STUBBED) called, applet_resource_user_id={}", aruid.pid);
const auto applet_resource_user_id{rp.Pop<u64>()}; R_SUCCEED();
LOG_WARNING(Service_IRS, "(STUBBED) called, applet_resource_user_id={}",
applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
} }
void IRS::GetIrsensorSharedMemoryHandle(HLERequestContext& ctx) { Result IRS::GetIrsensorSharedMemoryHandle(OutCopyHandle<Kernel::KSharedMemory> out_shared_memory,
IPC::RequestParser rp{ctx}; ClientAppletResourceUserId aruid) {
const auto applet_resource_user_id{rp.Pop<u64>()}; LOG_DEBUG(Service_IRS, "called, applet_resource_user_id={}", aruid.pid);
LOG_DEBUG(Service_IRS, "called, applet_resource_user_id={}", applet_resource_user_id); *out_shared_memory = &system.Kernel().GetIrsSharedMem();
R_SUCCEED();
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(&system.Kernel().GetIrsSharedMem());
} }
void IRS::StopImageProcessor(HLERequestContext& ctx) { Result IRS::StopImageProcessor(Core::IrSensor::IrCameraHandle camera_handle,
IPC::RequestParser rp{ctx}; ClientAppletResourceUserId aruid) {
struct Parameters {
Core::IrSensor::IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
LOG_WARNING(Service_IRS, LOG_WARNING(Service_IRS,
"(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, camera_handle.npad_type, camera_handle.npad_id, aruid.pid);
parameters.applet_resource_user_id);
R_TRY(IsIrCameraHandleValid(camera_handle));
auto result = IsIrCameraHandleValid(parameters.camera_handle);
if (result.IsSuccess()) {
// TODO: Stop Image processor // TODO: Stop Image processor
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::Active); Common::Input::PollingMode::Active);
result = ResultSuccess; R_SUCCEED();
} }
IPC::ResponseBuilder rb{ctx, 2}; Result IRS::RunMomentProcessor(
rb.Push(result); Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid,
} const Core::IrSensor::PackedMomentProcessorConfig& processor_config) {
void IRS::RunMomentProcessor(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Core::IrSensor::IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
Core::IrSensor::PackedMomentProcessorConfig processor_config;
};
static_assert(sizeof(Parameters) == 0x30, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
LOG_WARNING(Service_IRS, LOG_WARNING(Service_IRS,
"(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, camera_handle.npad_type, camera_handle.npad_id, aruid.pid);
parameters.applet_resource_user_id);
const auto result = IsIrCameraHandleValid(parameters.camera_handle); R_TRY(IsIrCameraHandleValid(camera_handle));
if (result.IsSuccess()) { auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); MakeProcessorWithCoreContext<MomentProcessor>(camera_handle, device);
MakeProcessorWithCoreContext<MomentProcessor>(parameters.camera_handle, device); auto& image_transfer_processor = GetProcessor<MomentProcessor>(camera_handle);
auto& image_transfer_processor = GetProcessor<MomentProcessor>(parameters.camera_handle); image_transfer_processor.SetConfig(processor_config);
image_transfer_processor.SetConfig(parameters.processor_config);
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::IR); Common::Input::PollingMode::IR);
R_SUCCEED();
} }
IPC::ResponseBuilder rb{ctx, 2}; Result IRS::RunClusteringProcessor(
rb.Push(result); Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid,
} const Core::IrSensor::PackedClusteringProcessorConfig& processor_config) {
void IRS::RunClusteringProcessor(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Core::IrSensor::IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
Core::IrSensor::PackedClusteringProcessorConfig processor_config;
};
static_assert(sizeof(Parameters) == 0x38, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
LOG_WARNING(Service_IRS, LOG_WARNING(Service_IRS,
"(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, camera_handle.npad_type, camera_handle.npad_id, aruid.pid);
parameters.applet_resource_user_id);
auto result = IsIrCameraHandleValid(parameters.camera_handle); R_TRY(IsIrCameraHandleValid(camera_handle));
if (result.IsSuccess()) { auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); MakeProcessorWithCoreContext<ClusteringProcessor>(camera_handle, device);
MakeProcessorWithCoreContext<ClusteringProcessor>(parameters.camera_handle, device); auto& image_transfer_processor = GetProcessor<ClusteringProcessor>(camera_handle);
auto& image_transfer_processor = image_transfer_processor.SetConfig(processor_config);
GetProcessor<ClusteringProcessor>(parameters.camera_handle);
image_transfer_processor.SetConfig(parameters.processor_config);
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::IR); Common::Input::PollingMode::IR);
R_SUCCEED();
} }
IPC::ResponseBuilder rb{ctx, 2}; Result IRS::RunImageTransferProcessor(
rb.Push(result); Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid,
} const Core::IrSensor::PackedImageTransferProcessorConfig& processor_config,
u64 transfer_memory_size, InCopyHandle<Kernel::KTransferMemory> t_mem) {
void IRS::RunImageTransferProcessor(HLERequestContext& ctx) { ASSERT_MSG(t_mem->GetSize() == transfer_memory_size, "t_mem has incorrect size");
IPC::RequestParser rp{ctx};
struct Parameters {
Core::IrSensor::IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
Core::IrSensor::PackedImageTransferProcessorConfig processor_config;
u32 transfer_memory_size;
};
static_assert(sizeof(Parameters) == 0x30, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
const auto t_mem_handle{ctx.GetCopyHandle(0)};
auto t_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(t_mem_handle);
if (t_mem.IsNull()) {
LOG_ERROR(Service_IRS, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}
ASSERT_MSG(t_mem->GetSize() == parameters.transfer_memory_size, "t_mem has incorrect size");
LOG_INFO(Service_IRS, LOG_INFO(Service_IRS,
"called, npad_type={}, npad_id={}, transfer_memory_size={}, transfer_memory_size={}, " "called, npad_type={}, npad_id={}, transfer_memory_size={}, transfer_memory_size={}, "
"applet_resource_user_id={}", "applet_resource_user_id={}",
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, camera_handle.npad_type, camera_handle.npad_id, transfer_memory_size, t_mem->GetSize(),
parameters.transfer_memory_size, t_mem->GetSize(), parameters.applet_resource_user_id); aruid.pid);
const auto result = IsIrCameraHandleValid(parameters.camera_handle); R_TRY(IsIrCameraHandleValid(camera_handle));
if (result.IsSuccess()) { auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); MakeProcessorWithCoreContext<ImageTransferProcessor>(camera_handle, device);
MakeProcessorWithCoreContext<ImageTransferProcessor>(parameters.camera_handle, device); auto& image_transfer_processor = GetProcessor<ImageTransferProcessor>(camera_handle);
auto& image_transfer_processor = image_transfer_processor.SetConfig(processor_config);
GetProcessor<ImageTransferProcessor>(parameters.camera_handle);
image_transfer_processor.SetConfig(parameters.processor_config);
image_transfer_processor.SetTransferMemoryAddress(t_mem->GetSourceAddress()); image_transfer_processor.SetTransferMemoryAddress(t_mem->GetSourceAddress());
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::IR); Common::Input::PollingMode::IR);
R_SUCCEED();
} }
IPC::ResponseBuilder rb{ctx, 2}; Result IRS::GetImageTransferProcessorState(
rb.Push(result); Out<Core::IrSensor::ImageTransferProcessorState> out_state,
} Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid,
OutBuffer<BufferAttr_HipcMapAlias> out_buffer_data) {
void IRS::GetImageTransferProcessorState(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Core::IrSensor::IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
LOG_DEBUG(Service_IRS, "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", LOG_DEBUG(Service_IRS, "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, camera_handle.npad_type, camera_handle.npad_id, aruid.pid);
parameters.applet_resource_user_id);
const auto result = IsIrCameraHandleValid(parameters.camera_handle); R_TRY(IsIrCameraHandleValid(camera_handle));
if (result.IsError()) {
IPC::ResponseBuilder rb{ctx, 2}; const auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
rb.Push(result);
return; R_TRY(IsIrCameraHandleValid(camera_handle));
R_UNLESS(device.mode == Core::IrSensor::IrSensorMode::ImageTransferProcessor,
InvalidProcessorState);
*out_state = GetProcessor<ImageTransferProcessor>(camera_handle).GetState(out_buffer_data);
R_SUCCEED();
} }
const auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); Result IRS::RunTeraPluginProcessor(Core::IrSensor::IrCameraHandle camera_handle,
Core::IrSensor::PackedTeraPluginProcessorConfig processor_config,
if (device.mode != Core::IrSensor::IrSensorMode::ImageTransferProcessor) { ClientAppletResourceUserId aruid) {
IPC::ResponseBuilder rb{ctx, 2}; LOG_WARNING(Service_IRS,
rb.Push(InvalidProcessorState);
return;
}
std::vector<u8> data{};
const auto& image_transfer_processor =
GetProcessor<ImageTransferProcessor>(parameters.camera_handle);
const auto& state = image_transfer_processor.GetState(data);
ctx.WriteBuffer(data);
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(ResultSuccess);
rb.PushRaw(state);
}
void IRS::RunTeraPluginProcessor(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Core::IrSensor::IrCameraHandle camera_handle;
Core::IrSensor::PackedTeraPluginProcessorConfig processor_config;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
LOG_WARNING(
Service_IRS,
"(STUBBED) called, npad_type={}, npad_id={}, mode={}, mcu_version={}.{}, " "(STUBBED) called, npad_type={}, npad_id={}, mode={}, mcu_version={}.{}, "
"applet_resource_user_id={}", "applet_resource_user_id={}",
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, camera_handle.npad_type, camera_handle.npad_id, processor_config.mode,
parameters.processor_config.mode, parameters.processor_config.required_mcu_version.major, processor_config.required_mcu_version.major,
parameters.processor_config.required_mcu_version.minor, parameters.applet_resource_user_id); processor_config.required_mcu_version.minor, aruid.pid);
const auto result = IsIrCameraHandleValid(parameters.camera_handle); R_TRY(IsIrCameraHandleValid(camera_handle));
if (result.IsSuccess()) { auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); MakeProcessor<TeraPluginProcessor>(camera_handle, device);
MakeProcessor<TeraPluginProcessor>(parameters.camera_handle, device); auto& image_transfer_processor = GetProcessor<TeraPluginProcessor>(camera_handle);
auto& image_transfer_processor = image_transfer_processor.SetConfig(processor_config);
GetProcessor<TeraPluginProcessor>(parameters.camera_handle);
image_transfer_processor.SetConfig(parameters.processor_config);
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::IR); Common::Input::PollingMode::IR);
R_SUCCEED();
} }
IPC::ResponseBuilder rb{ctx, 2}; Result IRS::GetNpadIrCameraHandle(Out<Core::IrSensor::IrCameraHandle> out_camera_handle,
rb.Push(result); Core::HID::NpadIdType npad_id) {
} R_UNLESS(HID::IsNpadIdValid(npad_id), HID::ResultInvalidNpadId);
void IRS::GetNpadIrCameraHandle(HLERequestContext& ctx) { *out_camera_handle = {
IPC::RequestParser rp{ctx};
const auto npad_id{rp.PopEnum<Core::HID::NpadIdType>()};
if (npad_id > Core::HID::NpadIdType::Player8 && npad_id != Core::HID::NpadIdType::Invalid &&
npad_id != Core::HID::NpadIdType::Handheld) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(Service::HID::ResultInvalidNpadId);
return;
}
Core::IrSensor::IrCameraHandle camera_handle{
.npad_id = static_cast<u8>(HID::NpadIdTypeToIndex(npad_id)), .npad_id = static_cast<u8>(HID::NpadIdTypeToIndex(npad_id)),
.npad_type = Core::HID::NpadStyleIndex::None, .npad_type = Core::HID::NpadStyleIndex::None,
}; };
LOG_INFO(Service_IRS, "called, npad_id={}, camera_npad_id={}, camera_npad_type={}", npad_id, LOG_INFO(Service_IRS, "called, npad_id={}, camera_npad_id={}, camera_npad_type={}", npad_id,
camera_handle.npad_id, camera_handle.npad_type); out_camera_handle->npad_id, out_camera_handle->npad_type);
IPC::ResponseBuilder rb{ctx, 3}; R_SUCCEED();
rb.Push(ResultSuccess);
rb.PushRaw(camera_handle);
} }
void IRS::RunPointingProcessor(HLERequestContext& ctx) { Result IRS::RunPointingProcessor(
IPC::RequestParser rp{ctx}; Core::IrSensor::IrCameraHandle camera_handle,
const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()}; const Core::IrSensor::PackedPointingProcessorConfig& processor_config,
const auto processor_config{rp.PopRaw<Core::IrSensor::PackedPointingProcessorConfig>()}; ClientAppletResourceUserId aruid) {
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_WARNING( LOG_WARNING(
Service_IRS, Service_IRS,
"(STUBBED) called, npad_type={}, npad_id={}, mcu_version={}.{}, applet_resource_user_id={}", "(STUBBED) called, npad_type={}, npad_id={}, mcu_version={}.{}, applet_resource_user_id={}",
camera_handle.npad_type, camera_handle.npad_id, processor_config.required_mcu_version.major, camera_handle.npad_type, camera_handle.npad_id, processor_config.required_mcu_version.major,
processor_config.required_mcu_version.minor, applet_resource_user_id); processor_config.required_mcu_version.minor, aruid.pid);
auto result = IsIrCameraHandleValid(camera_handle); R_TRY(IsIrCameraHandleValid(camera_handle));
if (result.IsSuccess()) {
auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle); auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
MakeProcessor<PointingProcessor>(camera_handle, device); MakeProcessor<PointingProcessor>(camera_handle, device);
auto& image_transfer_processor = GetProcessor<PointingProcessor>(camera_handle); auto& image_transfer_processor = GetProcessor<PointingProcessor>(camera_handle);
image_transfer_processor.SetConfig(processor_config); image_transfer_processor.SetConfig(processor_config);
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::IR); Common::Input::PollingMode::IR);
R_SUCCEED();
} }
IPC::ResponseBuilder rb{ctx, 2}; Result IRS::SuspendImageProcessor(Core::IrSensor::IrCameraHandle camera_handle,
rb.Push(result); ClientAppletResourceUserId aruid) {
}
void IRS::SuspendImageProcessor(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Core::IrSensor::IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
LOG_WARNING(Service_IRS, LOG_WARNING(Service_IRS,
"(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, camera_handle.npad_type, camera_handle.npad_id, aruid.pid);
parameters.applet_resource_user_id);
R_TRY(IsIrCameraHandleValid(camera_handle));
auto result = IsIrCameraHandleValid(parameters.camera_handle);
if (result.IsSuccess()) {
// TODO: Suspend image processor // TODO: Suspend image processor
result = ResultSuccess;
R_SUCCEED();
} }
IPC::ResponseBuilder rb{ctx, 2}; Result IRS::CheckFirmwareVersion(Core::IrSensor::IrCameraHandle camera_handle,
rb.Push(result); Core::IrSensor::PackedMcuVersion mcu_version,
} ClientAppletResourceUserId aruid) {
void IRS::CheckFirmwareVersion(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()};
const auto mcu_version{rp.PopRaw<Core::IrSensor::PackedMcuVersion>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_WARNING( LOG_WARNING(
Service_IRS, Service_IRS,
"(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}, mcu_version={}.{}", "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}, mcu_version={}.{}",
camera_handle.npad_type, camera_handle.npad_id, applet_resource_user_id, mcu_version.major, camera_handle.npad_type, camera_handle.npad_id, aruid.pid, mcu_version.major,
mcu_version.minor); mcu_version.minor);
auto result = IsIrCameraHandleValid(camera_handle); R_TRY(IsIrCameraHandleValid(camera_handle));
if (result.IsSuccess()) {
// TODO: Check firmware version // TODO: Check firmware version
result = ResultSuccess;
R_SUCCEED();
} }
IPC::ResponseBuilder rb{ctx, 2}; Result IRS::SetFunctionLevel(Core::IrSensor::IrCameraHandle camera_handle,
rb.Push(result); Core::IrSensor::PackedFunctionLevel function_level,
} ClientAppletResourceUserId aruid) {
void IRS::SetFunctionLevel(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()};
const auto function_level{rp.PopRaw<Core::IrSensor::PackedFunctionLevel>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_WARNING( LOG_WARNING(
Service_IRS, Service_IRS,
"(STUBBED) called, npad_type={}, npad_id={}, function_level={}, applet_resource_user_id={}", "(STUBBED) called, npad_type={}, npad_id={}, function_level={}, applet_resource_user_id={}",
camera_handle.npad_type, camera_handle.npad_id, function_level.function_level, camera_handle.npad_type, camera_handle.npad_id, function_level.function_level, aruid.pid);
applet_resource_user_id);
R_TRY(IsIrCameraHandleValid(camera_handle));
auto result = IsIrCameraHandleValid(camera_handle);
if (result.IsSuccess()) {
// TODO: Set Function level // TODO: Set Function level
result = ResultSuccess;
R_SUCCEED();
} }
IPC::ResponseBuilder rb{ctx, 2}; Result IRS::RunImageTransferExProcessor(
rb.Push(result); Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid,
} const Core::IrSensor::PackedImageTransferProcessorExConfig& processor_config,
u64 transfer_memory_size, InCopyHandle<Kernel::KTransferMemory> t_mem) {
void IRS::RunImageTransferExProcessor(HLERequestContext& ctx) { ASSERT_MSG(t_mem->GetSize() == transfer_memory_size, "t_mem has incorrect size");
IPC::RequestParser rp{ctx};
struct Parameters {
Core::IrSensor::IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
Core::IrSensor::PackedImageTransferProcessorExConfig processor_config;
u64 transfer_memory_size;
};
static_assert(sizeof(Parameters) == 0x38, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
const auto t_mem_handle{ctx.GetCopyHandle(0)};
auto t_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(t_mem_handle);
LOG_INFO(Service_IRS, LOG_INFO(Service_IRS,
"called, npad_type={}, npad_id={}, transfer_memory_size={}, " "called, npad_type={}, npad_id={}, transfer_memory_size={}, "
"applet_resource_user_id={}", "applet_resource_user_id={}",
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, camera_handle.npad_type, camera_handle.npad_id, transfer_memory_size, aruid.pid);
parameters.transfer_memory_size, parameters.applet_resource_user_id);
auto result = IsIrCameraHandleValid(parameters.camera_handle); R_TRY(IsIrCameraHandleValid(camera_handle));
if (result.IsSuccess()) { auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); MakeProcessorWithCoreContext<ImageTransferProcessor>(camera_handle, device);
MakeProcessorWithCoreContext<ImageTransferProcessor>(parameters.camera_handle, device); auto& image_transfer_processor = GetProcessor<ImageTransferProcessor>(camera_handle);
auto& image_transfer_processor = image_transfer_processor.SetConfig(processor_config);
GetProcessor<ImageTransferProcessor>(parameters.camera_handle);
image_transfer_processor.SetConfig(parameters.processor_config);
image_transfer_processor.SetTransferMemoryAddress(t_mem->GetSourceAddress()); image_transfer_processor.SetTransferMemoryAddress(t_mem->GetSourceAddress());
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::IR); Common::Input::PollingMode::IR);
R_SUCCEED();
} }
IPC::ResponseBuilder rb{ctx, 2}; Result IRS::RunIrLedProcessor(Core::IrSensor::IrCameraHandle camera_handle,
rb.Push(result); Core::IrSensor::PackedIrLedProcessorConfig processor_config,
} ClientAppletResourceUserId aruid) {
void IRS::RunIrLedProcessor(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()};
const auto processor_config{rp.PopRaw<Core::IrSensor::PackedIrLedProcessorConfig>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_WARNING(Service_IRS, LOG_WARNING(Service_IRS,
"(STUBBED) called, npad_type={}, npad_id={}, light_target={}, mcu_version={}.{} " "(STUBBED) called, npad_type={}, npad_id={}, light_target={}, mcu_version={}.{} "
"applet_resource_user_id={}", "applet_resource_user_id={}",
camera_handle.npad_type, camera_handle.npad_id, processor_config.light_target, camera_handle.npad_type, camera_handle.npad_id, processor_config.light_target,
processor_config.required_mcu_version.major, processor_config.required_mcu_version.major,
processor_config.required_mcu_version.minor, applet_resource_user_id); processor_config.required_mcu_version.minor, aruid.pid);
auto result = IsIrCameraHandleValid(camera_handle); R_TRY(IsIrCameraHandleValid(camera_handle));
if (result.IsSuccess()) {
auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle); auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
MakeProcessor<IrLedProcessor>(camera_handle, device); MakeProcessor<IrLedProcessor>(camera_handle, device);
auto& image_transfer_processor = GetProcessor<IrLedProcessor>(camera_handle); auto& image_transfer_processor = GetProcessor<IrLedProcessor>(camera_handle);
image_transfer_processor.SetConfig(processor_config); image_transfer_processor.SetConfig(processor_config);
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::IR); Common::Input::PollingMode::IR);
R_SUCCEED();
} }
IPC::ResponseBuilder rb{ctx, 2}; Result IRS::StopImageProcessorAsync(Core::IrSensor::IrCameraHandle camera_handle,
rb.Push(result); ClientAppletResourceUserId aruid) {
}
void IRS::StopImageProcessorAsync(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Core::IrSensor::IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
LOG_WARNING(Service_IRS, LOG_WARNING(Service_IRS,
"(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, camera_handle.npad_type, camera_handle.npad_id, aruid.pid);
parameters.applet_resource_user_id);
R_TRY(IsIrCameraHandleValid(camera_handle));
auto result = IsIrCameraHandleValid(parameters.camera_handle);
if (result.IsSuccess()) {
// TODO: Stop image processor async // TODO: Stop image processor async
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::Active); Common::Input::PollingMode::Active);
result = ResultSuccess;
R_SUCCEED();
} }
IPC::ResponseBuilder rb{ctx, 2}; Result IRS::ActivateIrsensorWithFunctionLevel(Core::IrSensor::PackedFunctionLevel function_level,
rb.Push(result); ClientAppletResourceUserId aruid) {
}
void IRS::ActivateIrsensorWithFunctionLevel(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Core::IrSensor::PackedFunctionLevel function_level;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
LOG_WARNING(Service_IRS, "(STUBBED) called, function_level={}, applet_resource_user_id={}", LOG_WARNING(Service_IRS, "(STUBBED) called, function_level={}, applet_resource_user_id={}",
parameters.function_level.function_level, parameters.applet_resource_user_id); function_level.function_level, aruid.pid);
R_SUCCEED();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
} }
Result IRS::IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_handle) const { Result IRS::IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_handle) const {

View File

@ -4,6 +4,7 @@
#pragma once #pragma once
#include "core/core.h" #include "core/core.h"
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/service.h" #include "core/hle/service/service.h"
#include "hid_core/hid_types.h" #include "hid_core/hid_types.h"
#include "hid_core/irsensor/irs_types.h" #include "hid_core/irsensor/irs_types.h"
@ -35,26 +36,73 @@ private:
}; };
static_assert(sizeof(StatusManager) == 0x8000, "StatusManager is an invalid size"); static_assert(sizeof(StatusManager) == 0x8000, "StatusManager is an invalid size");
void ActivateIrsensor(HLERequestContext& ctx); Result ActivateIrsensor(ClientAppletResourceUserId aruid);
void DeactivateIrsensor(HLERequestContext& ctx);
void GetIrsensorSharedMemoryHandle(HLERequestContext& ctx); Result DeactivateIrsensor(ClientAppletResourceUserId aruid);
void StopImageProcessor(HLERequestContext& ctx);
void RunMomentProcessor(HLERequestContext& ctx); Result GetIrsensorSharedMemoryHandle(OutCopyHandle<Kernel::KSharedMemory> out_shared_memory,
void RunClusteringProcessor(HLERequestContext& ctx); ClientAppletResourceUserId aruid);
void RunImageTransferProcessor(HLERequestContext& ctx); Result StopImageProcessor(Core::IrSensor::IrCameraHandle camera_handle,
void GetImageTransferProcessorState(HLERequestContext& ctx); ClientAppletResourceUserId aruid);
void RunTeraPluginProcessor(HLERequestContext& ctx);
void GetNpadIrCameraHandle(HLERequestContext& ctx); Result RunMomentProcessor(Core::IrSensor::IrCameraHandle camera_handle,
void RunPointingProcessor(HLERequestContext& ctx); ClientAppletResourceUserId aruid,
void SuspendImageProcessor(HLERequestContext& ctx); const Core::IrSensor::PackedMomentProcessorConfig& processor_config);
void CheckFirmwareVersion(HLERequestContext& ctx);
void SetFunctionLevel(HLERequestContext& ctx); Result RunClusteringProcessor(
void RunImageTransferExProcessor(HLERequestContext& ctx); Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid,
void RunIrLedProcessor(HLERequestContext& ctx); const Core::IrSensor::PackedClusteringProcessorConfig& processor_config);
void StopImageProcessorAsync(HLERequestContext& ctx);
void ActivateIrsensorWithFunctionLevel(HLERequestContext& ctx); Result RunImageTransferProcessor(
Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid,
const Core::IrSensor::PackedImageTransferProcessorConfig& processor_config,
u64 transfer_memory_size, InCopyHandle<Kernel::KTransferMemory> t_mem);
Result GetImageTransferProcessorState(
Out<Core::IrSensor::ImageTransferProcessorState> out_state,
Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid,
OutBuffer<BufferAttr_HipcMapAlias> out_buffer_data);
Result RunTeraPluginProcessor(Core::IrSensor::IrCameraHandle camera_handle,
Core::IrSensor::PackedTeraPluginProcessorConfig processor_config,
ClientAppletResourceUserId aruid);
Result GetNpadIrCameraHandle(Out<Core::IrSensor::IrCameraHandle> out_camera_handle,
Core::HID::NpadIdType npad_id);
Result RunPointingProcessor(
Core::IrSensor::IrCameraHandle camera_handle,
const Core::IrSensor::PackedPointingProcessorConfig& processor_config,
ClientAppletResourceUserId aruid);
Result SuspendImageProcessor(Core::IrSensor::IrCameraHandle camera_handle,
ClientAppletResourceUserId aruid);
Result CheckFirmwareVersion(Core::IrSensor::IrCameraHandle camera_handle,
Core::IrSensor::PackedMcuVersion mcu_version,
ClientAppletResourceUserId aruid);
Result SetFunctionLevel(Core::IrSensor::IrCameraHandle camera_handle,
Core::IrSensor::PackedFunctionLevel function_level,
ClientAppletResourceUserId aruid);
Result RunImageTransferExProcessor(
Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid,
const Core::IrSensor::PackedImageTransferProcessorExConfig& processor_config,
u64 transfer_memory_size, InCopyHandle<Kernel::KTransferMemory> t_mem);
Result RunIrLedProcessor(Core::IrSensor::IrCameraHandle camera_handle,
Core::IrSensor::PackedIrLedProcessorConfig processor_config,
ClientAppletResourceUserId aruid);
Result StopImageProcessorAsync(Core::IrSensor::IrCameraHandle camera_handle,
ClientAppletResourceUserId aruid);
Result ActivateIrsensorWithFunctionLevel(Core::IrSensor::PackedFunctionLevel function_level,
ClientAppletResourceUserId aruid);
Result IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_handle) const; Result IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_handle) const;
Core::IrSensor::DeviceFormat& GetIrCameraSharedMemoryDeviceEntry( Core::IrSensor::DeviceFormat& GetIrCameraSharedMemoryDeviceEntry(
const Core::IrSensor::IrCameraHandle& camera_handle); const Core::IrSensor::IrCameraHandle& camera_handle);

View File

@ -299,8 +299,12 @@ Result HLERequestContext::WriteToOutgoingCommandBuffer() {
if (GetManager()->IsDomain()) { if (GetManager()->IsDomain()) {
current_offset = domain_offset - static_cast<u32>(outgoing_domain_objects.size()); current_offset = domain_offset - static_cast<u32>(outgoing_domain_objects.size());
for (auto& object : outgoing_domain_objects) { for (auto& object : outgoing_domain_objects) {
if (object) {
GetManager()->AppendDomainHandler(std::move(object)); GetManager()->AppendDomainHandler(std::move(object));
cmd_buf[current_offset++] = static_cast<u32_le>(GetManager()->DomainHandlerCount()); cmd_buf[current_offset++] = static_cast<u32_le>(GetManager()->DomainHandlerCount());
} else {
cmd_buf[current_offset++] = 0;
}
} }
} }

View File

@ -49,6 +49,7 @@ SessionId Container::OpenSession(Kernel::KProcess* process) {
continue; continue;
} }
if (session.process == process) { if (session.process == process) {
session.ref_count++;
return session.id; return session.id;
} }
} }
@ -66,6 +67,7 @@ SessionId Container::OpenSession(Kernel::KProcess* process) {
} }
auto& session = impl->sessions[new_id]; auto& session = impl->sessions[new_id];
session.is_active = true; session.is_active = true;
session.ref_count = 1;
// Optimization // Optimization
if (process->IsApplication()) { if (process->IsApplication()) {
auto& page_table = process->GetPageTable().GetBasePageTable(); auto& page_table = process->GetPageTable().GetBasePageTable();
@ -114,8 +116,11 @@ SessionId Container::OpenSession(Kernel::KProcess* process) {
void Container::CloseSession(SessionId session_id) { void Container::CloseSession(SessionId session_id) {
std::scoped_lock lk(impl->session_guard); std::scoped_lock lk(impl->session_guard);
impl->file.UnmapAllHandles(session_id);
auto& session = impl->sessions[session_id.id]; auto& session = impl->sessions[session_id.id];
if (--session.ref_count > 0) {
return;
}
impl->file.UnmapAllHandles(session_id);
auto& smmu = impl->host1x.MemoryManager(); auto& smmu = impl->host1x.MemoryManager();
if (session.has_preallocated_area) { if (session.has_preallocated_area) {
const DAddr region_start = session.mapper->GetRegionStart(); const DAddr region_start = session.mapper->GetRegionStart();

View File

@ -46,6 +46,7 @@ struct Session {
bool has_preallocated_area{}; bool has_preallocated_area{};
std::unique_ptr<HeapMapper> mapper{}; std::unique_ptr<HeapMapper> mapper{};
bool is_active{}; bool is_active{};
s32 ref_count{};
}; };
class Container { class Container {
@ -67,10 +68,7 @@ public:
const SyncpointManager& GetSyncpointManager() const; const SyncpointManager& GetSyncpointManager() const;
struct Host1xDeviceFileData { struct Host1xDeviceFileData {
std::unordered_map<DeviceFD, u32> fd_to_id{};
std::deque<u32> syncpts_accumulated{}; std::deque<u32> syncpts_accumulated{};
u32 nvdec_next_id{};
u32 vic_next_id{};
}; };
Host1xDeviceFileData& Host1xDeviceFile(); Host1xDeviceFileData& Host1xDeviceFile();

View File

@ -3,110 +3,21 @@
#include <mutex> #include <mutex>
#include <boost/container/small_vector.hpp> #include "common/range_sets.h"
#define BOOST_NO_MT #include "common/range_sets.inc"
#include <boost/pool/detail/mutex.hpp>
#undef BOOST_NO_MT
#include <boost/icl/interval.hpp>
#include <boost/icl/interval_base_set.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 "core/hle/service/nvdrv/core/heap_mapper.h" #include "core/hle/service/nvdrv/core/heap_mapper.h"
#include "video_core/host1x/host1x.h" #include "video_core/host1x/host1x.h"
namespace boost {
template <typename T>
class fast_pool_allocator<T, default_user_allocator_new_delete, details::pool::null_mutex, 4096, 0>;
}
namespace Service::Nvidia::NvCore { namespace Service::Nvidia::NvCore {
using IntervalCompare = std::less<DAddr>;
using IntervalInstance = boost::icl::interval_type_default<DAddr, std::less>;
using IntervalAllocator = boost::fast_pool_allocator<DAddr>;
using IntervalSet = boost::icl::interval_set<DAddr>;
using IntervalType = typename IntervalSet::interval_type;
template <typename Type>
struct counter_add_functor : public boost::icl::identity_based_inplace_combine<Type> {
// types
typedef counter_add_functor<Type> type;
typedef boost::icl::identity_based_inplace_combine<Type> base_type;
// public member functions
void operator()(Type& current, const Type& added) const {
current += added;
if (current < base_type::identity_element()) {
current = base_type::identity_element();
}
}
// public static functions
static void version(Type&){};
};
using OverlapCombine = counter_add_functor<int>;
using OverlapSection = boost::icl::inter_section<int>;
using OverlapCounter = boost::icl::split_interval_map<DAddr, int>;
struct HeapMapper::HeapMapperInternal { struct HeapMapper::HeapMapperInternal {
HeapMapperInternal(Tegra::Host1x::Host1x& host1x) : device_memory{host1x.MemoryManager()} {} HeapMapperInternal(Tegra::Host1x::Host1x& host1x) : m_device_memory{host1x.MemoryManager()} {}
~HeapMapperInternal() = default; ~HeapMapperInternal() = default;
template <typename Func> Common::RangeSet<VAddr> m_temporary_set;
void ForEachInOverlapCounter(OverlapCounter& current_range, VAddr cpu_addr, u64 size, Common::OverlapRangeSet<VAddr> m_mapped_ranges;
Func&& func) { Tegra::MaxwellDeviceMemoryManager& m_device_memory;
const DAddr start_address = cpu_addr; std::mutex m_guard;
const DAddr end_address = start_address + size;
const IntervalType search_interval{start_address, end_address};
auto it = current_range.lower_bound(search_interval);
if (it == current_range.end()) {
return;
}
auto end_it = current_range.upper_bound(search_interval);
for (; it != end_it; it++) {
auto& inter = it->first;
DAddr inter_addr_end = inter.upper();
DAddr 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);
}
}
void RemoveEachInOverlapCounter(OverlapCounter& current_range,
const IntervalType search_interval, int subtract_value) {
bool any_removals = false;
current_range.add(std::make_pair(search_interval, subtract_value));
do {
any_removals = false;
auto it = current_range.lower_bound(search_interval);
if (it == current_range.end()) {
return;
}
auto end_it = current_range.upper_bound(search_interval);
for (; it != end_it; it++) {
if (it->second <= 0) {
any_removals = true;
current_range.erase(it);
break;
}
}
} while (any_removals);
}
IntervalSet base_set;
OverlapCounter mapping_overlaps;
Tegra::MaxwellDeviceMemoryManager& device_memory;
std::mutex guard;
}; };
HeapMapper::HeapMapper(VAddr start_vaddress, DAddr start_daddress, size_t size, Core::Asid asid, HeapMapper::HeapMapper(VAddr start_vaddress, DAddr start_daddress, size_t size, Core::Asid asid,
@ -116,60 +27,48 @@ HeapMapper::HeapMapper(VAddr start_vaddress, DAddr start_daddress, size_t size,
} }
HeapMapper::~HeapMapper() { HeapMapper::~HeapMapper() {
m_internal->device_memory.Unmap(m_daddress, m_size); // Unmap whatever has been mapped.
m_internal->m_mapped_ranges.ForEach([this](VAddr start_addr, VAddr end_addr, s32 count) {
const size_t sub_size = end_addr - start_addr;
const size_t offset = start_addr - m_vaddress;
m_internal->m_device_memory.Unmap(m_daddress + offset, sub_size);
});
} }
DAddr HeapMapper::Map(VAddr start, size_t size) { DAddr HeapMapper::Map(VAddr start, size_t size) {
std::scoped_lock lk(m_internal->guard); std::scoped_lock lk(m_internal->m_guard);
m_internal->base_set.clear(); // Add the mapping range to a temporary range set.
const IntervalType interval{start, start + size}; m_internal->m_temporary_set.Clear();
m_internal->base_set.insert(interval); m_internal->m_temporary_set.Add(start, size);
m_internal->ForEachInOverlapCounter(m_internal->mapping_overlaps, start, size,
[this](VAddr start_addr, VAddr end_addr, int) { // Remove anything that's already mapped from the temporary range set.
const IntervalType other{start_addr, end_addr}; m_internal->m_mapped_ranges.ForEachInRange(
m_internal->base_set.subtract(other); start, size, [this](VAddr start_addr, VAddr end_addr, s32) {
m_internal->m_temporary_set.Subtract(start_addr, end_addr - start_addr);
}); });
if (!m_internal->base_set.empty()) {
auto it = m_internal->base_set.begin(); // Map anything that has not been mapped yet.
auto end_it = m_internal->base_set.end(); m_internal->m_temporary_set.ForEach([this](VAddr start_addr, VAddr end_addr) {
for (; it != end_it; it++) { const size_t sub_size = end_addr - start_addr;
const VAddr inter_addr_end = it->upper(); const size_t offset = start_addr - m_vaddress;
const VAddr inter_addr = it->lower(); m_internal->m_device_memory.Map(m_daddress + offset, m_vaddress + offset, sub_size, m_asid);
const size_t offset = inter_addr - m_vaddress; });
const size_t sub_size = inter_addr_end - inter_addr;
m_internal->device_memory.Map(m_daddress + offset, m_vaddress + offset, sub_size, // Add the mapping range to the split map, to register the map and overlaps.
m_asid); m_internal->m_mapped_ranges.Add(start, size);
} m_internal->m_temporary_set.Clear();
} return m_daddress + static_cast<DAddr>(start - m_vaddress);
m_internal->mapping_overlaps += std::make_pair(interval, 1);
m_internal->base_set.clear();
return m_daddress + (start - m_vaddress);
} }
void HeapMapper::Unmap(VAddr start, size_t size) { void HeapMapper::Unmap(VAddr start, size_t size) {
std::scoped_lock lk(m_internal->guard); std::scoped_lock lk(m_internal->m_guard);
m_internal->base_set.clear();
m_internal->ForEachInOverlapCounter(m_internal->mapping_overlaps, start, size, // Just subtract the range and whatever is deleted, unmap it.
[this](VAddr start_addr, VAddr end_addr, int value) { m_internal->m_mapped_ranges.Subtract(start, size, [this](VAddr start_addr, VAddr end_addr) {
if (value <= 1) { const size_t sub_size = end_addr - start_addr;
const IntervalType other{start_addr, end_addr}; const size_t offset = start_addr - m_vaddress;
m_internal->base_set.insert(other); m_internal->m_device_memory.Unmap(m_daddress + offset, sub_size);
}
}); });
if (!m_internal->base_set.empty()) {
auto it = m_internal->base_set.begin();
auto end_it = m_internal->base_set.end();
for (; it != end_it; it++) {
const VAddr inter_addr_end = it->upper();
const VAddr inter_addr = it->lower();
const size_t offset = inter_addr - m_vaddress;
const size_t sub_size = inter_addr_end - inter_addr;
m_internal->device_memory.Unmap(m_daddress + offset, sub_size);
}
}
const IntervalType to_remove{start, start + size};
m_internal->RemoveEachInOverlapCounter(m_internal->mapping_overlaps, to_remove, -1);
m_internal->base_set.clear();
} }
} // namespace Service::Nvidia::NvCore } // namespace Service::Nvidia::NvCore

View File

@ -333,10 +333,14 @@ void NvMap::UnmapAllHandles(NvCore::SessionId session_id) {
}(); }();
for (auto& [id, handle] : handles_copy) { for (auto& [id, handle] : handles_copy) {
if (handle->session_id.id == session_id.id) { {
FreeHandle(id, false); std::scoped_lock lk{handle->mutex};
if (handle->session_id.id != session_id.id || handle->dupes <= 0) {
continue;
} }
} }
FreeHandle(id, false);
}
} }
} // namespace Service::Nvidia::NvCore } // namespace Service::Nvidia::NvCore

View File

@ -15,6 +15,22 @@
namespace Service::Nvidia::Devices { namespace Service::Nvidia::Devices {
namespace {
Tegra::BlendMode ConvertBlending(Service::Nvnflinger::LayerBlending blending) {
switch (blending) {
case Service::Nvnflinger::LayerBlending::None:
default:
return Tegra::BlendMode::Opaque;
case Service::Nvnflinger::LayerBlending::Premultiplied:
return Tegra::BlendMode::Premultiplied;
case Service::Nvnflinger::LayerBlending::Coverage:
return Tegra::BlendMode::Coverage;
}
}
} // namespace
nvdisp_disp0::nvdisp_disp0(Core::System& system_, NvCore::Container& core) nvdisp_disp0::nvdisp_disp0(Core::System& system_, NvCore::Container& core)
: nvdevice{system_}, container{core}, nvmap{core.GetNvMapFile()} {} : nvdevice{system_}, container{core}, nvmap{core.GetNvMapFile()} {}
nvdisp_disp0::~nvdisp_disp0() = default; nvdisp_disp0::~nvdisp_disp0() = default;
@ -56,6 +72,7 @@ void nvdisp_disp0::Composite(std::span<const Nvnflinger::HwcLayer> sorted_layers
.pixel_format = layer.format, .pixel_format = layer.format,
.transform_flags = layer.transform, .transform_flags = layer.transform,
.crop_rect = layer.crop_rect, .crop_rect = layer.crop_rect,
.blending = ConvertBlending(layer.blending),
}); });
for (size_t i = 0; i < layer.acquire_fence.num_fences; i++) { for (size_t i = 0; i < layer.acquire_fence.num_fences; i++) {

View File

@ -123,6 +123,8 @@ NvResult nvhost_as_gpu::AllocAsEx(IoctlAllocAsEx& params) {
vm.va_range_end = params.va_range_end; vm.va_range_end = params.va_range_end;
} }
const u64 max_big_page_bits = Common::Log2Ceil64(vm.va_range_end);
const auto start_pages{static_cast<u32>(vm.va_range_start >> VM::PAGE_SIZE_BITS)}; const auto start_pages{static_cast<u32>(vm.va_range_start >> VM::PAGE_SIZE_BITS)};
const auto end_pages{static_cast<u32>(vm.va_range_split >> VM::PAGE_SIZE_BITS)}; const auto end_pages{static_cast<u32>(vm.va_range_split >> VM::PAGE_SIZE_BITS)};
vm.small_page_allocator = std::make_shared<VM::Allocator>(start_pages, end_pages); vm.small_page_allocator = std::make_shared<VM::Allocator>(start_pages, end_pages);
@ -132,8 +134,8 @@ NvResult nvhost_as_gpu::AllocAsEx(IoctlAllocAsEx& params) {
static_cast<u32>((vm.va_range_end - vm.va_range_split) >> vm.big_page_size_bits)}; static_cast<u32>((vm.va_range_end - vm.va_range_split) >> vm.big_page_size_bits)};
vm.big_page_allocator = std::make_unique<VM::Allocator>(start_big_pages, end_big_pages); vm.big_page_allocator = std::make_unique<VM::Allocator>(start_big_pages, end_big_pages);
gmmu = std::make_shared<Tegra::MemoryManager>(system, 40, vm.big_page_size_bits, gmmu = std::make_shared<Tegra::MemoryManager>(system, max_big_page_bits, vm.va_range_split,
VM::PAGE_SIZE_BITS); vm.big_page_size_bits, VM::PAGE_SIZE_BITS);
system.GPU().InitAddressSpace(*gmmu); system.GPU().InitAddressSpace(*gmmu);
vm.initialised = true; vm.initialised = true;

View File

@ -8,6 +8,7 @@
#include "core/hle/service/nvdrv/core/container.h" #include "core/hle/service/nvdrv/core/container.h"
#include "core/hle/service/nvdrv/devices/ioctl_serialization.h" #include "core/hle/service/nvdrv/devices/ioctl_serialization.h"
#include "core/hle/service/nvdrv/devices/nvhost_nvdec.h" #include "core/hle/service/nvdrv/devices/nvhost_nvdec.h"
#include "video_core/host1x/host1x.h"
#include "video_core/renderer_base.h" #include "video_core/renderer_base.h"
namespace Service::Nvidia::Devices { namespace Service::Nvidia::Devices {
@ -21,13 +22,8 @@ NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> in
switch (command.group) { switch (command.group) {
case 0x0: case 0x0:
switch (command.cmd) { switch (command.cmd) {
case 0x1: { case 0x1:
auto& host1x_file = core.Host1xDeviceFile();
if (!host1x_file.fd_to_id.contains(fd)) {
host1x_file.fd_to_id[fd] = host1x_file.nvdec_next_id++;
}
return WrapFixedVariable(this, &nvhost_nvdec::Submit, input, output, fd); return WrapFixedVariable(this, &nvhost_nvdec::Submit, input, output, fd);
}
case 0x2: case 0x2:
return WrapFixed(this, &nvhost_nvdec::GetSyncpoint, input, output); return WrapFixed(this, &nvhost_nvdec::GetSyncpoint, input, output);
case 0x3: case 0x3:
@ -72,15 +68,12 @@ void nvhost_nvdec::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {
LOG_INFO(Service_NVDRV, "NVDEC video stream started"); LOG_INFO(Service_NVDRV, "NVDEC video stream started");
system.SetNVDECActive(true); system.SetNVDECActive(true);
sessions[fd] = session_id; sessions[fd] = session_id;
host1x.StartDevice(fd, Tegra::Host1x::ChannelType::NvDec, channel_syncpoint);
} }
void nvhost_nvdec::OnClose(DeviceFD fd) { void nvhost_nvdec::OnClose(DeviceFD fd) {
LOG_INFO(Service_NVDRV, "NVDEC video stream ended"); LOG_INFO(Service_NVDRV, "NVDEC video stream ended");
auto& host1x_file = core.Host1xDeviceFile(); host1x.StopDevice(fd, Tegra::Host1x::ChannelType::NvDec);
const auto iter = host1x_file.fd_to_id.find(fd);
if (iter != host1x_file.fd_to_id.end()) {
system.GPU().ClearCdmaInstance(iter->second);
}
system.SetNVDECActive(false); system.SetNVDECActive(false);
auto it = sessions.find(fd); auto it = sessions.find(fd);
if (it != sessions.end()) { if (it != sessions.end()) {

View File

@ -55,8 +55,9 @@ std::size_t WriteVectors(std::span<u8> dst, const std::vector<T>& src, std::size
nvhost_nvdec_common::nvhost_nvdec_common(Core::System& system_, NvCore::Container& core_, nvhost_nvdec_common::nvhost_nvdec_common(Core::System& system_, NvCore::Container& core_,
NvCore::ChannelType channel_type_) NvCore::ChannelType channel_type_)
: nvdevice{system_}, core{core_}, syncpoint_manager{core.GetSyncpointManager()}, : nvdevice{system_}, host1x{system_.Host1x()}, core{core_},
nvmap{core.GetNvMapFile()}, channel_type{channel_type_} { syncpoint_manager{core.GetSyncpointManager()}, nvmap{core.GetNvMapFile()},
channel_type{channel_type_} {
auto& syncpts_accumulated = core.Host1xDeviceFile().syncpts_accumulated; auto& syncpts_accumulated = core.Host1xDeviceFile().syncpts_accumulated;
if (syncpts_accumulated.empty()) { if (syncpts_accumulated.empty()) {
channel_syncpoint = syncpoint_manager.AllocateSyncpoint(false); channel_syncpoint = syncpoint_manager.AllocateSyncpoint(false);
@ -95,24 +96,24 @@ NvResult nvhost_nvdec_common::Submit(IoctlSubmit& params, std::span<u8> data, De
offset += SliceVectors(data, syncpt_increments, params.syncpoint_count, offset); offset += SliceVectors(data, syncpt_increments, params.syncpoint_count, offset);
offset += SliceVectors(data, fence_thresholds, params.fence_count, offset); offset += SliceVectors(data, fence_thresholds, params.fence_count, offset);
auto& gpu = system.GPU();
auto* session = core.GetSession(sessions[fd]); auto* session = core.GetSession(sessions[fd]);
if (gpu.UseNvdec()) {
for (std::size_t i = 0; i < syncpt_increments.size(); i++) { for (std::size_t i = 0; i < syncpt_increments.size(); i++) {
const SyncptIncr& syncpt_incr = syncpt_increments[i]; const SyncptIncr& syncpt_incr = syncpt_increments[i];
fence_thresholds[i] = fence_thresholds[i] =
syncpoint_manager.IncrementSyncpointMaxExt(syncpt_incr.id, syncpt_incr.increments); syncpoint_manager.IncrementSyncpointMaxExt(syncpt_incr.id, syncpt_incr.increments);
} }
}
for (const auto& cmd_buffer : command_buffers) { for (const auto& cmd_buffer : command_buffers) {
const auto object = nvmap.GetHandle(cmd_buffer.memory_id); const auto object = nvmap.GetHandle(cmd_buffer.memory_id);
ASSERT_OR_EXECUTE(object, return NvResult::InvalidState;); ASSERT_OR_EXECUTE(object, return NvResult::InvalidState;);
Tegra::ChCommandHeaderList cmdlist(cmd_buffer.word_count); Core::Memory::CpuGuestMemory<Tegra::ChCommandHeader,
session->process->GetMemory().ReadBlock(object->address + cmd_buffer.offset, cmdlist.data(), Core::Memory::GuestMemoryFlags::SafeRead>
cmdlist.size() * sizeof(u32)); cmdlist(session->process->GetMemory(), object->address + cmd_buffer.offset,
gpu.PushCommandBuffer(core.Host1xDeviceFile().fd_to_id[fd], cmdlist); cmd_buffer.word_count);
host1x.PushEntries(fd, std::move(cmdlist));
} }
// Some games expect command_buffers to be written back // Some games expect command_buffers to be written back
offset = 0; offset = 0;
offset += WriteVectors(data, command_buffers, offset); offset += WriteVectors(data, command_buffers, offset);

View File

@ -119,6 +119,7 @@ protected:
Kernel::KEvent* QueryEvent(u32 event_id) override; Kernel::KEvent* QueryEvent(u32 event_id) override;
Tegra::Host1x::Host1x& host1x;
u32 channel_syncpoint; u32 channel_syncpoint;
s32_le nvmap_fd{}; s32_le nvmap_fd{};
u32_le submit_timeout{}; u32_le submit_timeout{};

View File

@ -7,6 +7,7 @@
#include "core/hle/service/nvdrv/core/container.h" #include "core/hle/service/nvdrv/core/container.h"
#include "core/hle/service/nvdrv/devices/ioctl_serialization.h" #include "core/hle/service/nvdrv/devices/ioctl_serialization.h"
#include "core/hle/service/nvdrv/devices/nvhost_vic.h" #include "core/hle/service/nvdrv/devices/nvhost_vic.h"
#include "video_core/host1x/host1x.h"
#include "video_core/renderer_base.h" #include "video_core/renderer_base.h"
namespace Service::Nvidia::Devices { namespace Service::Nvidia::Devices {
@ -21,13 +22,8 @@ NvResult nvhost_vic::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> inpu
switch (command.group) { switch (command.group) {
case 0x0: case 0x0:
switch (command.cmd) { switch (command.cmd) {
case 0x1: { case 0x1:
auto& host1x_file = core.Host1xDeviceFile();
if (!host1x_file.fd_to_id.contains(fd)) {
host1x_file.fd_to_id[fd] = host1x_file.vic_next_id++;
}
return WrapFixedVariable(this, &nvhost_vic::Submit, input, output, fd); return WrapFixedVariable(this, &nvhost_vic::Submit, input, output, fd);
}
case 0x2: case 0x2:
return WrapFixed(this, &nvhost_vic::GetSyncpoint, input, output); return WrapFixed(this, &nvhost_vic::GetSyncpoint, input, output);
case 0x3: case 0x3:
@ -70,14 +66,11 @@ NvResult nvhost_vic::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> inpu
void nvhost_vic::OnOpen(NvCore::SessionId session_id, DeviceFD fd) { void nvhost_vic::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {
sessions[fd] = session_id; sessions[fd] = session_id;
host1x.StartDevice(fd, Tegra::Host1x::ChannelType::VIC, channel_syncpoint);
} }
void nvhost_vic::OnClose(DeviceFD fd) { void nvhost_vic::OnClose(DeviceFD fd) {
auto& host1x_file = core.Host1xDeviceFile(); host1x.StopDevice(fd, Tegra::Host1x::ChannelType::VIC);
const auto iter = host1x_file.fd_to_id.find(fd);
if (iter != host1x_file.fd_to_id.end()) {
system.GPU().ClearCdmaInstance(iter->second);
}
sessions.erase(fd); sessions.erase(fd);
} }

View File

@ -14,24 +14,20 @@
#include "core/hle/service/nvnflinger/ui/graphic_buffer.h" #include "core/hle/service/nvnflinger/ui/graphic_buffer.h"
#include "core/hle/service/vi/layer/vi_layer.h" #include "core/hle/service/vi/layer/vi_layer.h"
#include "core/hle/service/vi/vi_results.h" #include "core/hle/service/vi/vi_results.h"
#include "video_core/gpu.h"
#include "video_core/host1x/host1x.h"
namespace Service::Nvnflinger { namespace Service::Nvnflinger {
namespace { namespace {
Result AllocateIoForProcessAddressSpace(Common::ProcessAddress* out_map_address, Result AllocateSharedBufferMemory(std::unique_ptr<Kernel::KPageGroup>* out_page_group,
std::unique_ptr<Kernel::KPageGroup>* out_page_group,
Core::System& system, u32 size) { Core::System& system, u32 size) {
using Core::Memory::YUZU_PAGESIZE; using Core::Memory::YUZU_PAGESIZE;
// Allocate memory for the system shared buffer. // Allocate memory for the system shared buffer.
// FIXME: Because the gmmu can only point to cpu addresses, we need
// to map this in the application space to allow it to be used.
// FIXME: Add proper smmu emulation.
// FIXME: This memory belongs to vi's .data section. // FIXME: This memory belongs to vi's .data section.
auto& kernel = system.Kernel(); auto& kernel = system.Kernel();
auto* process = system.ApplicationProcess();
auto& page_table = process->GetPageTable();
// Hold a temporary page group reference while we try to map it. // Hold a temporary page group reference while we try to map it.
auto pg = std::make_unique<Kernel::KPageGroup>( auto pg = std::make_unique<Kernel::KPageGroup>(
@ -43,6 +39,30 @@ Result AllocateIoForProcessAddressSpace(Common::ProcessAddress* out_map_address,
Kernel::KMemoryManager::EncodeOption(Kernel::KMemoryManager::Pool::Secure, Kernel::KMemoryManager::EncodeOption(Kernel::KMemoryManager::Pool::Secure,
Kernel::KMemoryManager::Direction::FromBack))); Kernel::KMemoryManager::Direction::FromBack)));
// Fill the output data with red.
for (auto& block : *pg) {
u32* start = system.DeviceMemory().GetPointer<u32>(block.GetAddress());
u32* end = system.DeviceMemory().GetPointer<u32>(block.GetAddress() + block.GetSize());
for (; start < end; start++) {
*start = 0xFF0000FF;
}
}
// Return the mapped page group.
*out_page_group = std::move(pg);
// We succeeded.
R_SUCCEED();
}
Result MapSharedBufferIntoProcessAddressSpace(Common::ProcessAddress* out_map_address,
std::unique_ptr<Kernel::KPageGroup>& pg,
Kernel::KProcess* process, Core::System& system) {
using Core::Memory::YUZU_PAGESIZE;
auto& page_table = process->GetPageTable();
// Get bounds of where mapping is possible. // Get bounds of where mapping is possible.
const VAddr alias_code_begin = GetInteger(page_table.GetAliasCodeRegionStart()); const VAddr alias_code_begin = GetInteger(page_table.GetAliasCodeRegionStart());
const VAddr alias_code_size = page_table.GetAliasCodeRegionSize() / YUZU_PAGESIZE; const VAddr alias_code_size = page_table.GetAliasCodeRegionSize() / YUZU_PAGESIZE;
@ -64,9 +84,6 @@ Result AllocateIoForProcessAddressSpace(Common::ProcessAddress* out_map_address,
// Return failure, if necessary // Return failure, if necessary
R_UNLESS(i < 64, res); R_UNLESS(i < 64, res);
// Return the mapped page group.
*out_page_group = std::move(pg);
// We succeeded. // We succeeded.
R_SUCCEED(); R_SUCCEED();
} }
@ -135,6 +152,13 @@ Result AllocateHandleForBuffer(u32* out_handle, Nvidia::Module& nvdrv, Nvidia::D
R_RETURN(AllocNvMapHandle(*nvmap, *out_handle, buffer, size, nvmap_fd)); R_RETURN(AllocNvMapHandle(*nvmap, *out_handle, buffer, size, nvmap_fd));
} }
void FreeHandle(u32 handle, Nvidia::Module& nvdrv, Nvidia::DeviceFD nvmap_fd) {
auto nvmap = nvdrv.GetDevice<Nvidia::Devices::nvmap>(nvmap_fd);
ASSERT(nvmap != nullptr);
R_ASSERT(FreeNvMapHandle(*nvmap, handle, nvmap_fd));
}
constexpr auto SharedBufferBlockLinearFormat = android::PixelFormat::Rgba8888; constexpr auto SharedBufferBlockLinearFormat = android::PixelFormat::Rgba8888;
constexpr u32 SharedBufferBlockLinearBpp = 4; constexpr u32 SharedBufferBlockLinearBpp = 4;
@ -186,53 +210,97 @@ FbShareBufferManager::FbShareBufferManager(Core::System& system, Nvnflinger& fli
FbShareBufferManager::~FbShareBufferManager() = default; FbShareBufferManager::~FbShareBufferManager() = default;
Result FbShareBufferManager::Initialize(u64* out_buffer_id, u64* out_layer_id, u64 display_id) { Result FbShareBufferManager::Initialize(Kernel::KProcess* owner_process, u64* out_buffer_id,
u64* out_layer_handle, u64 display_id,
LayerBlending blending) {
std::scoped_lock lk{m_guard}; std::scoped_lock lk{m_guard};
// Ensure we have not already created a buffer. // Ensure we haven't already created.
R_UNLESS(m_buffer_id == 0, VI::ResultOperationFailed); const u64 aruid = owner_process->GetProcessId();
R_UNLESS(!m_sessions.contains(aruid), VI::ResultPermissionDenied);
// Allocate memory and space for the shared buffer. // Allocate memory for the shared buffer if needed.
Common::ProcessAddress map_address; if (!m_buffer_page_group) {
R_TRY(AllocateIoForProcessAddressSpace(std::addressof(map_address), R_TRY(AllocateSharedBufferMemory(std::addressof(m_buffer_page_group), m_system,
std::addressof(m_buffer_page_group), m_system,
SharedBufferSize)); SharedBufferSize));
auto& container = m_nvdrv->GetContainer(); // Record buffer id.
m_session_id = container.OpenSession(m_system.ApplicationProcess());
m_nvmap_fd = m_nvdrv->Open("/dev/nvmap", m_session_id);
// Create an nvmap handle for the buffer and assign the memory to it.
R_TRY(AllocateHandleForBuffer(std::addressof(m_buffer_nvmap_handle), *m_nvdrv, m_nvmap_fd,
map_address, SharedBufferSize));
// Record the display id.
m_display_id = display_id;
// Create and open a layer for the display.
m_layer_id = m_flinger.CreateLayer(m_display_id).value();
m_flinger.OpenLayer(m_layer_id);
// Set up the buffer.
m_buffer_id = m_next_buffer_id++; m_buffer_id = m_next_buffer_id++;
// Record display id.
m_display_id = display_id;
}
// Map into process.
Common::ProcessAddress map_address{};
R_TRY(MapSharedBufferIntoProcessAddressSpace(std::addressof(map_address), m_buffer_page_group,
owner_process, m_system));
// Create new session.
auto [it, was_emplaced] = m_sessions.emplace(aruid, FbShareSession{});
auto& session = it->second;
auto& container = m_nvdrv->GetContainer();
session.session_id = container.OpenSession(owner_process);
session.nvmap_fd = m_nvdrv->Open("/dev/nvmap", session.session_id);
// Create an nvmap handle for the buffer and assign the memory to it.
R_TRY(AllocateHandleForBuffer(std::addressof(session.buffer_nvmap_handle), *m_nvdrv,
session.nvmap_fd, map_address, SharedBufferSize));
// Create and open a layer for the display.
session.layer_id = m_flinger.CreateLayer(m_display_id, blending).value();
m_flinger.OpenLayer(session.layer_id);
// Get the layer. // Get the layer.
VI::Layer* layer = m_flinger.FindLayer(m_display_id, m_layer_id); VI::Layer* layer = m_flinger.FindLayer(m_display_id, session.layer_id);
ASSERT(layer != nullptr); ASSERT(layer != nullptr);
// Get the producer and set preallocated buffers. // Get the producer and set preallocated buffers.
auto& producer = layer->GetBufferQueue(); auto& producer = layer->GetBufferQueue();
MakeGraphicBuffer(producer, 0, m_buffer_nvmap_handle); MakeGraphicBuffer(producer, 0, session.buffer_nvmap_handle);
MakeGraphicBuffer(producer, 1, m_buffer_nvmap_handle); MakeGraphicBuffer(producer, 1, session.buffer_nvmap_handle);
// Assign outputs. // Assign outputs.
*out_buffer_id = m_buffer_id; *out_buffer_id = m_buffer_id;
*out_layer_id = m_layer_id; *out_layer_handle = session.layer_id;
// We succeeded. // We succeeded.
R_SUCCEED(); R_SUCCEED();
} }
void FbShareBufferManager::Finalize(Kernel::KProcess* owner_process) {
std::scoped_lock lk{m_guard};
if (m_buffer_id == 0) {
return;
}
const u64 aruid = owner_process->GetProcessId();
const auto it = m_sessions.find(aruid);
if (it == m_sessions.end()) {
return;
}
auto& session = it->second;
// Destroy the layer.
m_flinger.DestroyLayer(session.layer_id);
// Close nvmap handle.
FreeHandle(session.buffer_nvmap_handle, *m_nvdrv, session.nvmap_fd);
// Close nvmap device.
m_nvdrv->Close(session.nvmap_fd);
// Close session.
auto& container = m_nvdrv->GetContainer();
container.CloseSession(session.session_id);
// Erase.
m_sessions.erase(it);
}
Result FbShareBufferManager::GetSharedBufferMemoryHandleId(u64* out_buffer_size, Result FbShareBufferManager::GetSharedBufferMemoryHandleId(u64* out_buffer_size,
s32* out_nvmap_handle, s32* out_nvmap_handle,
SharedMemoryPoolLayout* out_pool_layout, SharedMemoryPoolLayout* out_pool_layout,
@ -242,17 +310,18 @@ Result FbShareBufferManager::GetSharedBufferMemoryHandleId(u64* out_buffer_size,
R_UNLESS(m_buffer_id > 0, VI::ResultNotFound); R_UNLESS(m_buffer_id > 0, VI::ResultNotFound);
R_UNLESS(buffer_id == m_buffer_id, VI::ResultNotFound); R_UNLESS(buffer_id == m_buffer_id, VI::ResultNotFound);
R_UNLESS(m_sessions.contains(applet_resource_user_id), VI::ResultNotFound);
*out_pool_layout = SharedBufferPoolLayout; *out_pool_layout = SharedBufferPoolLayout;
*out_buffer_size = SharedBufferSize; *out_buffer_size = SharedBufferSize;
*out_nvmap_handle = m_buffer_nvmap_handle; *out_nvmap_handle = m_sessions[applet_resource_user_id].buffer_nvmap_handle;
R_SUCCEED(); R_SUCCEED();
} }
Result FbShareBufferManager::GetLayerFromId(VI::Layer** out_layer, u64 layer_id) { Result FbShareBufferManager::GetLayerFromId(VI::Layer** out_layer, u64 layer_id) {
// Ensure the layer id is valid. // Ensure the layer id is valid.
R_UNLESS(m_layer_id > 0 && layer_id == m_layer_id, VI::ResultNotFound); R_UNLESS(layer_id > 0, VI::ResultNotFound);
// Get the layer. // Get the layer.
VI::Layer* layer = m_flinger.FindLayer(m_display_id, layer_id); VI::Layer* layer = m_flinger.FindLayer(m_display_id, layer_id);
@ -309,6 +378,10 @@ Result FbShareBufferManager::PresentSharedFrameBuffer(android::Fence fence,
android::Status::NoError, android::Status::NoError,
VI::ResultOperationFailed); VI::ResultOperationFailed);
ON_RESULT_FAILURE {
producer.CancelBuffer(static_cast<s32>(slot), fence);
};
// Queue the buffer to the producer. // Queue the buffer to the producer.
android::QueueBufferInput input{}; android::QueueBufferInput input{};
android::QueueBufferOutput output{}; android::QueueBufferOutput output{};
@ -342,4 +415,33 @@ Result FbShareBufferManager::GetSharedFrameBufferAcquirableEvent(Kernel::KReadab
R_SUCCEED(); R_SUCCEED();
} }
Result FbShareBufferManager::WriteAppletCaptureBuffer(bool* out_was_written, s32* out_layer_index) {
std::vector<u8> capture_buffer(m_system.GPU().GetAppletCaptureBuffer());
Common::ScratchBuffer<u32> scratch;
// TODO: this could be optimized
s64 e = -1280 * 768 * 4;
for (auto& block : *m_buffer_page_group) {
u8* start = m_system.DeviceMemory().GetPointer<u8>(block.GetAddress());
u8* end = m_system.DeviceMemory().GetPointer<u8>(block.GetAddress() + block.GetSize());
for (; start < end; start++) {
*start = 0;
if (e >= 0 && e < static_cast<s64>(capture_buffer.size())) {
*start = capture_buffer[e];
}
e++;
}
m_system.GPU().Host1x().MemoryManager().ApplyOpOnPointer(start, scratch, [&](DAddr addr) {
m_system.GPU().InvalidateRegion(addr, end - start);
});
}
*out_was_written = true;
*out_layer_index = 1;
R_SUCCEED();
}
} // namespace Service::Nvnflinger } // namespace Service::Nvnflinger

View File

@ -3,9 +3,12 @@
#pragma once #pragma once
#include <map>
#include "common/math_util.h" #include "common/math_util.h"
#include "core/hle/service/nvdrv/core/container.h" #include "core/hle/service/nvdrv/core/container.h"
#include "core/hle/service/nvdrv/nvdata.h" #include "core/hle/service/nvdrv/nvdata.h"
#include "core/hle/service/nvnflinger/hwc_layer.h"
#include "core/hle/service/nvnflinger/nvnflinger.h" #include "core/hle/service/nvnflinger/nvnflinger.h"
#include "core/hle/service/nvnflinger/ui/fence.h" #include "core/hle/service/nvnflinger/ui/fence.h"
@ -29,13 +32,18 @@ struct SharedMemoryPoolLayout {
}; };
static_assert(sizeof(SharedMemoryPoolLayout) == 0x188, "SharedMemoryPoolLayout has wrong size"); static_assert(sizeof(SharedMemoryPoolLayout) == 0x188, "SharedMemoryPoolLayout has wrong size");
struct FbShareSession;
class FbShareBufferManager final { class FbShareBufferManager final {
public: public:
explicit FbShareBufferManager(Core::System& system, Nvnflinger& flinger, explicit FbShareBufferManager(Core::System& system, Nvnflinger& flinger,
std::shared_ptr<Nvidia::Module> nvdrv); std::shared_ptr<Nvidia::Module> nvdrv);
~FbShareBufferManager(); ~FbShareBufferManager();
Result Initialize(u64* out_buffer_id, u64* out_layer_handle, u64 display_id); Result Initialize(Kernel::KProcess* owner_process, u64* out_buffer_id, u64* out_layer_handle,
u64 display_id, LayerBlending blending);
void Finalize(Kernel::KProcess* owner_process);
Result GetSharedBufferMemoryHandleId(u64* out_buffer_size, s32* out_nvmap_handle, Result GetSharedBufferMemoryHandleId(u64* out_buffer_size, s32* out_nvmap_handle,
SharedMemoryPoolLayout* out_pool_layout, u64 buffer_id, SharedMemoryPoolLayout* out_pool_layout, u64 buffer_id,
u64 applet_resource_user_id); u64 applet_resource_user_id);
@ -45,6 +53,8 @@ public:
u32 transform, s32 swap_interval, u64 layer_id, s64 slot); u32 transform, s32 swap_interval, u64 layer_id, s64 slot);
Result GetSharedFrameBufferAcquirableEvent(Kernel::KReadableEvent** out_event, u64 layer_id); Result GetSharedFrameBufferAcquirableEvent(Kernel::KReadableEvent** out_event, u64 layer_id);
Result WriteAppletCaptureBuffer(bool* out_was_written, s32* out_layer_index);
private: private:
Result GetLayerFromId(VI::Layer** out_layer, u64 layer_id); Result GetLayerFromId(VI::Layer** out_layer, u64 layer_id);
@ -52,11 +62,8 @@ private:
u64 m_next_buffer_id = 1; u64 m_next_buffer_id = 1;
u64 m_display_id = 0; u64 m_display_id = 0;
u64 m_buffer_id = 0; u64 m_buffer_id = 0;
u64 m_layer_id = 0;
u32 m_buffer_nvmap_handle = 0;
SharedMemoryPoolLayout m_pool_layout = {}; SharedMemoryPoolLayout m_pool_layout = {};
Nvidia::DeviceFD m_nvmap_fd = {}; std::map<u64, FbShareSession> m_sessions;
Nvidia::NvCore::SessionId m_session_id = {};
std::unique_ptr<Kernel::KPageGroup> m_buffer_page_group; std::unique_ptr<Kernel::KPageGroup> m_buffer_page_group;
std::mutex m_guard; std::mutex m_guard;
@ -65,4 +72,11 @@ private:
std::shared_ptr<Nvidia::Module> m_nvdrv; std::shared_ptr<Nvidia::Module> m_nvdrv;
}; };
struct FbShareSession {
Nvidia::DeviceFD nvmap_fd = {};
Nvidia::NvCore::SessionId session_id = {};
u64 layer_id = {};
u32 buffer_nvmap_handle = 0;
};
} // namespace Service::Nvnflinger } // namespace Service::Nvnflinger

View File

@ -7,7 +7,6 @@
#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h" #include "core/hle/service/nvdrv/devices/nvdisp_disp0.h"
#include "core/hle/service/nvnflinger/buffer_item.h" #include "core/hle/service/nvnflinger/buffer_item.h"
#include "core/hle/service/nvnflinger/buffer_item_consumer.h" #include "core/hle/service/nvnflinger/buffer_item_consumer.h"
#include "core/hle/service/nvnflinger/buffer_queue_producer.h"
#include "core/hle/service/nvnflinger/hardware_composer.h" #include "core/hle/service/nvnflinger/hardware_composer.h"
#include "core/hle/service/nvnflinger/hwc_layer.h" #include "core/hle/service/nvnflinger/hwc_layer.h"
#include "core/hle/service/nvnflinger/ui/graphic_buffer.h" #include "core/hle/service/nvnflinger/ui/graphic_buffer.h"
@ -46,31 +45,9 @@ HardwareComposer::HardwareComposer() = default;
HardwareComposer::~HardwareComposer() = default; HardwareComposer::~HardwareComposer() = default;
u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, VI::Display& display, u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, VI::Display& display,
Nvidia::Devices::nvdisp_disp0& nvdisp, u32 frame_advance) { Nvidia::Devices::nvdisp_disp0& nvdisp) {
boost::container::small_vector<HwcLayer, 2> composition_stack; boost::container::small_vector<HwcLayer, 2> composition_stack;
m_frame_number += frame_advance;
// Release any necessary framebuffers.
for (auto& [layer_id, framebuffer] : m_framebuffers) {
if (framebuffer.release_frame_number > m_frame_number) {
// Not yet ready to release this framebuffer.
continue;
}
if (!framebuffer.is_acquired) {
// Already released.
continue;
}
if (auto* layer = display.FindLayer(layer_id); layer != nullptr) {
// TODO: support release fence
// This is needed to prevent screen tearing
layer->GetConsumer().ReleaseBuffer(framebuffer.item, android::Fence::NoFence());
framebuffer.is_acquired = false;
}
}
// Set default speed limit to 100%. // Set default speed limit to 100%.
*out_speed_scale = 1.0f; *out_speed_scale = 1.0f;
@ -109,6 +86,7 @@ u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, VI::Display& display,
.height = igbp_buffer.Height(), .height = igbp_buffer.Height(),
.stride = igbp_buffer.Stride(), .stride = igbp_buffer.Stride(),
.z_index = 0, .z_index = 0,
.blending = layer.GetBlending(),
.transform = static_cast<android::BufferTransformFlags>(item.transform), .transform = static_cast<android::BufferTransformFlags>(item.transform),
.crop_rect = item.crop, .crop_rect = item.crop,
.acquire_fence = item.fence, .acquire_fence = item.fence,
@ -142,7 +120,30 @@ u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, VI::Display& display,
MicroProfileFlip(); MicroProfileFlip();
// Advance by at least one frame. // Advance by at least one frame.
return swap_interval.value_or(1); const u32 frame_advance = swap_interval.value_or(1);
m_frame_number += frame_advance;
// Release any necessary framebuffers.
for (auto& [layer_id, framebuffer] : m_framebuffers) {
if (framebuffer.release_frame_number > m_frame_number) {
// Not yet ready to release this framebuffer.
continue;
}
if (!framebuffer.is_acquired) {
// Already released.
continue;
}
if (auto* layer = display.FindLayer(layer_id); layer != nullptr) {
// TODO: support release fence
// This is needed to prevent screen tearing
layer->GetConsumer().ReleaseBuffer(framebuffer.item, android::Fence::NoFence());
framebuffer.is_acquired = false;
}
}
return frame_advance;
} }
void HardwareComposer::RemoveLayerLocked(VI::Display& display, LayerId layer_id) { void HardwareComposer::RemoveLayerLocked(VI::Display& display, LayerId layer_id) {

View File

@ -27,7 +27,7 @@ public:
~HardwareComposer(); ~HardwareComposer();
u32 ComposeLocked(f32* out_speed_scale, VI::Display& display, u32 ComposeLocked(f32* out_speed_scale, VI::Display& display,
Nvidia::Devices::nvdisp_disp0& nvdisp, u32 frame_advance); Nvidia::Devices::nvdisp_disp0& nvdisp);
void RemoveLayerLocked(VI::Display& display, LayerId layer_id); void RemoveLayerLocked(VI::Display& display, LayerId layer_id);
private: private:

View File

@ -11,6 +11,18 @@
namespace Service::Nvnflinger { namespace Service::Nvnflinger {
// hwc_layer_t::blending values
enum class LayerBlending : u32 {
// No blending
None = 0x100,
// ONE / ONE_MINUS_SRC_ALPHA
Premultiplied = 0x105,
// SRC_ALPHA / ONE_MINUS_SRC_ALPHA
Coverage = 0x405,
};
struct HwcLayer { struct HwcLayer {
u32 buffer_handle; u32 buffer_handle;
u32 offset; u32 offset;
@ -19,6 +31,7 @@ struct HwcLayer {
u32 height; u32 height;
u32 stride; u32 stride;
s32 z_index; s32 z_index;
LayerBlending blending;
android::BufferTransformFlags transform; android::BufferTransformFlags transform;
Common::Rectangle<int> crop_rect; Common::Rectangle<int> crop_rect;
android::Fence acquire_fence; android::Fence acquire_fence;

View File

@ -157,7 +157,7 @@ bool Nvnflinger::CloseDisplay(u64 display_id) {
return true; return true;
} }
std::optional<u64> Nvnflinger::CreateLayer(u64 display_id) { std::optional<u64> Nvnflinger::CreateLayer(u64 display_id, LayerBlending blending) {
const auto lock_guard = Lock(); const auto lock_guard = Lock();
auto* const display = FindDisplay(display_id); auto* const display = FindDisplay(display_id);
@ -166,13 +166,14 @@ std::optional<u64> Nvnflinger::CreateLayer(u64 display_id) {
} }
const u64 layer_id = next_layer_id++; const u64 layer_id = next_layer_id++;
CreateLayerAtId(*display, layer_id); CreateLayerAtId(*display, layer_id, blending);
return layer_id; return layer_id;
} }
void Nvnflinger::CreateLayerAtId(VI::Display& display, u64 layer_id) { void Nvnflinger::CreateLayerAtId(VI::Display& display, u64 layer_id, LayerBlending blending) {
const auto buffer_id = next_buffer_queue_id++; const auto buffer_id = next_buffer_queue_id++;
display.CreateLayer(layer_id, buffer_id, nvdrv->container); display.CreateLayer(layer_id, buffer_id, nvdrv->container);
display.FindLayer(layer_id)->SetBlending(blending);
} }
bool Nvnflinger::OpenLayer(u64 layer_id) { bool Nvnflinger::OpenLayer(u64 layer_id) {
@ -291,8 +292,7 @@ void Nvnflinger::Compose() {
auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>(disp_fd); auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>(disp_fd);
ASSERT(nvdisp); ASSERT(nvdisp);
swap_interval = display.GetComposer().ComposeLocked(&compose_speed_scale, display, *nvdisp, swap_interval = display.GetComposer().ComposeLocked(&compose_speed_scale, display, *nvdisp);
swap_interval);
} }
} }

View File

@ -15,6 +15,7 @@
#include "common/thread.h" #include "common/thread.h"
#include "core/hle/result.h" #include "core/hle/result.h"
#include "core/hle/service/kernel_helpers.h" #include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/nvnflinger/hwc_layer.h"
namespace Common { namespace Common {
class Event; class Event;
@ -72,7 +73,8 @@ public:
/// Creates a layer on the specified display and returns the layer ID. /// Creates a layer on the specified display and returns the layer ID.
/// ///
/// If an invalid display ID is specified, then an empty optional is returned. /// If an invalid display ID is specified, then an empty optional is returned.
[[nodiscard]] std::optional<u64> CreateLayer(u64 display_id); [[nodiscard]] std::optional<u64> CreateLayer(u64 display_id,
LayerBlending blending = LayerBlending::None);
/// Opens a layer on all displays for the given layer ID. /// Opens a layer on all displays for the given layer ID.
bool OpenLayer(u64 layer_id); bool OpenLayer(u64 layer_id);
@ -128,7 +130,7 @@ private:
[[nodiscard]] VI::Layer* FindLayer(u64 display_id, u64 layer_id); [[nodiscard]] VI::Layer* FindLayer(u64 display_id, u64 layer_id);
/// Creates a layer with the specified layer ID in the desired display. /// Creates a layer with the specified layer ID in the desired display.
void CreateLayerAtId(VI::Display& display, u64 layer_id); void CreateLayerAtId(VI::Display& display, u64 layer_id, LayerBlending blending);
void SplitVSync(std::stop_token stop_token); void SplitVSync(std::stop_token stop_token);

View File

@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/nvnflinger/hwc_layer.h"
#include "core/hle/service/vi/layer/vi_layer.h" #include "core/hle/service/vi/layer/vi_layer.h"
namespace Service::VI { namespace Service::VI {
@ -8,8 +9,9 @@ namespace Service::VI {
Layer::Layer(u64 layer_id_, u32 binder_id_, android::BufferQueueCore& core_, Layer::Layer(u64 layer_id_, u32 binder_id_, android::BufferQueueCore& core_,
android::BufferQueueProducer& binder_, android::BufferQueueProducer& binder_,
std::shared_ptr<android::BufferItemConsumer>&& consumer_) std::shared_ptr<android::BufferItemConsumer>&& consumer_)
: layer_id{layer_id_}, binder_id{binder_id_}, core{core_}, binder{binder_}, : layer_id{layer_id_}, binder_id{binder_id_}, core{core_}, binder{binder_}, consumer{std::move(
consumer{std::move(consumer_)}, open{false}, visible{true} {} consumer_)},
blending{Nvnflinger::LayerBlending::None}, open{false}, visible{true} {}
Layer::~Layer() = default; Layer::~Layer() = default;

View File

@ -14,6 +14,10 @@ class BufferQueueCore;
class BufferQueueProducer; class BufferQueueProducer;
} // namespace Service::android } // namespace Service::android
namespace Service::Nvnflinger {
enum class LayerBlending : u32;
}
namespace Service::VI { namespace Service::VI {
/// Represents a single display layer. /// Represents a single display layer.
@ -92,12 +96,21 @@ public:
return !std::exchange(open, true); return !std::exchange(open, true);
} }
Nvnflinger::LayerBlending GetBlending() {
return blending;
}
void SetBlending(Nvnflinger::LayerBlending b) {
blending = b;
}
private: private:
const u64 layer_id; const u64 layer_id;
const u32 binder_id; const u32 binder_id;
android::BufferQueueCore& core; android::BufferQueueCore& core;
android::BufferQueueProducer& binder; android::BufferQueueProducer& binder;
std::shared_ptr<android::BufferItemConsumer> consumer; std::shared_ptr<android::BufferItemConsumer> consumer;
Service::Nvnflinger::LayerBlending blending;
bool open; bool open;
bool visible; bool visible;
}; };

View File

@ -64,6 +64,8 @@ public:
Memory(Memory&&) = default; Memory(Memory&&) = default;
Memory& operator=(Memory&&) = delete; Memory& operator=(Memory&&) = delete;
static constexpr bool HAS_FLUSH_INVALIDATION = false;
/** /**
* Resets the state of the Memory system. * Resets the state of the Memory system.
*/ */

View File

@ -5,10 +5,12 @@
#include "common/hex_util.h" #include "common/hex_util.h"
#include "common/microprofile.h" #include "common/microprofile.h"
#include "common/swap.h" #include "common/swap.h"
#include "core/arm/debug.h"
#include "core/core.h" #include "core/core.h"
#include "core/core_timing.h" #include "core/core_timing.h"
#include "core/hle/kernel/k_page_table.h" #include "core/hle/kernel/k_page_table.h"
#include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_process_page_table.h"
#include "core/hle/service/hid/hid_server.h" #include "core/hle/service/hid/hid_server.h"
#include "core/hle/service/sm/sm.h" #include "core/hle/service/sm/sm.h"
#include "core/memory.h" #include "core/memory.h"
@ -46,12 +48,26 @@ StandardVmCallbacks::StandardVmCallbacks(System& system_, const CheatProcessMeta
StandardVmCallbacks::~StandardVmCallbacks() = default; StandardVmCallbacks::~StandardVmCallbacks() = default;
void StandardVmCallbacks::MemoryRead(VAddr address, void* data, u64 size) { void StandardVmCallbacks::MemoryReadUnsafe(VAddr address, void* data, u64 size) {
system.ApplicationMemory().ReadBlock(SanitizeAddress(address), data, size); // Return zero on invalid address
if (!IsAddressInRange(address) || !system.ApplicationMemory().IsValidVirtualAddress(address)) {
std::memset(data, 0, size);
return;
} }
void StandardVmCallbacks::MemoryWrite(VAddr address, const void* data, u64 size) { system.ApplicationMemory().ReadBlock(address, data, size);
system.ApplicationMemory().WriteBlock(SanitizeAddress(address), data, size); }
void StandardVmCallbacks::MemoryWriteUnsafe(VAddr address, const void* data, u64 size) {
// Skip invalid memory write address
if (!IsAddressInRange(address) || !system.ApplicationMemory().IsValidVirtualAddress(address)) {
return;
}
if (system.ApplicationMemory().WriteBlock(address, data, size) &&
system.ApplicationProcess()->Is64Bit()) {
Core::InvalidateInstructionCacheRange(system.ApplicationProcess(), address, size);
}
} }
u64 StandardVmCallbacks::HidKeysDown() { u64 StandardVmCallbacks::HidKeysDown() {
@ -81,21 +97,25 @@ void StandardVmCallbacks::CommandLog(std::string_view data) {
data.back() == '\n' ? data.substr(0, data.size() - 1) : data); data.back() == '\n' ? data.substr(0, data.size() - 1) : data);
} }
VAddr StandardVmCallbacks::SanitizeAddress(VAddr in) const { bool StandardVmCallbacks::IsAddressInRange(VAddr in) const {
if ((in < metadata.main_nso_extents.base || if ((in < metadata.main_nso_extents.base ||
in >= metadata.main_nso_extents.base + metadata.main_nso_extents.size) && in >= metadata.main_nso_extents.base + metadata.main_nso_extents.size) &&
(in < metadata.heap_extents.base || (in < metadata.heap_extents.base ||
in >= metadata.heap_extents.base + metadata.heap_extents.size)) { in >= metadata.heap_extents.base + metadata.heap_extents.size) &&
LOG_ERROR(CheatEngine, (in < metadata.alias_extents.base ||
in >= metadata.heap_extents.base + metadata.alias_extents.size) &&
(in < metadata.aslr_extents.base ||
in >= metadata.heap_extents.base + metadata.aslr_extents.size)) {
LOG_DEBUG(CheatEngine,
"Cheat attempting to access memory at invalid address={:016X}, if this " "Cheat attempting to access memory at invalid address={:016X}, if this "
"persists, " "persists, "
"the cheat may be incorrect. However, this may be normal early in execution if " "the cheat may be incorrect. However, this may be normal early in execution if "
"the game has not properly set up yet.", "the game has not properly set up yet.",
in); in);
return 0; ///< Invalid addresses will hard crash return false; ///< Invalid addresses will hard crash
} }
return in; return true;
} }
CheatParser::~CheatParser() = default; CheatParser::~CheatParser() = default;
@ -211,16 +231,14 @@ void CheatEngine::Initialize() {
.base = GetInteger(page_table.GetHeapRegionStart()), .base = GetInteger(page_table.GetHeapRegionStart()),
.size = page_table.GetHeapRegionSize(), .size = page_table.GetHeapRegionSize(),
}; };
metadata.aslr_extents = {
metadata.address_space_extents = {
.base = GetInteger(page_table.GetAddressSpaceStart()),
.size = page_table.GetAddressSpaceSize(),
};
metadata.alias_extents = {
.base = GetInteger(page_table.GetAliasCodeRegionStart()), .base = GetInteger(page_table.GetAliasCodeRegionStart()),
.size = page_table.GetAliasCodeRegionSize(), .size = page_table.GetAliasCodeRegionSize(),
}; };
metadata.alias_extents = {
.base = GetInteger(page_table.GetAliasRegionStart()),
.size = page_table.GetAliasRegionSize(),
};
is_pending_reload.exchange(true); is_pending_reload.exchange(true);
} }

View File

@ -27,17 +27,17 @@ public:
StandardVmCallbacks(System& system_, const CheatProcessMetadata& metadata_); StandardVmCallbacks(System& system_, const CheatProcessMetadata& metadata_);
~StandardVmCallbacks() override; ~StandardVmCallbacks() override;
void MemoryRead(VAddr address, void* data, u64 size) override; void MemoryReadUnsafe(VAddr address, void* data, u64 size) override;
void MemoryWrite(VAddr address, const void* data, u64 size) override; void MemoryWriteUnsafe(VAddr address, const void* data, u64 size) override;
u64 HidKeysDown() override; u64 HidKeysDown() override;
void DebugLog(u8 id, u64 value) override; void DebugLog(u8 id, u64 value) override;
void CommandLog(std::string_view data) override; void CommandLog(std::string_view data) override;
private: private:
VAddr SanitizeAddress(VAddr address) const; bool IsAddressInRange(VAddr address) const;
const CheatProcessMetadata& metadata; const CheatProcessMetadata& metadata;
System& system; Core::System& system;
}; };
// Intermediary class that parses a text file or other disk format for storing cheats into a // Intermediary class that parses a text file or other disk format for storing cheats into a

View File

@ -18,7 +18,7 @@ struct CheatProcessMetadata {
MemoryRegionExtents main_nso_extents{}; MemoryRegionExtents main_nso_extents{};
MemoryRegionExtents heap_extents{}; MemoryRegionExtents heap_extents{};
MemoryRegionExtents alias_extents{}; MemoryRegionExtents alias_extents{};
MemoryRegionExtents address_space_extents{}; MemoryRegionExtents aslr_extents{};
std::array<u8, 0x20> main_nso_build_id{}; std::array<u8, 0x20> main_nso_build_id{};
}; };

View File

@ -322,8 +322,9 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
} break; } break;
case CheatVmOpcodeType::EndConditionalBlock: { case CheatVmOpcodeType::EndConditionalBlock: {
// 20000000 // 20000000
// There's actually nothing left to process here! opcode.opcode = EndConditionalOpcode{
opcode.opcode = EndConditionalOpcode{}; .is_else = ((first_dword >> 24) & 0xf) == 1,
};
} break; } break;
case CheatVmOpcodeType::ControlLoop: { case CheatVmOpcodeType::ControlLoop: {
// 300R0000 VVVVVVVV // 300R0000 VVVVVVVV
@ -555,6 +556,18 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
.idx = first_dword & 0xF, .idx = first_dword & 0xF,
}; };
} break; } break;
case CheatVmOpcodeType::PauseProcess: {
/* FF0????? */
/* FF0 = opcode 0xFF0 */
/* Pauses the current process. */
opcode.opcode = PauseProcessOpcode{};
} break;
case CheatVmOpcodeType::ResumeProcess: {
/* FF0????? */
/* FF0 = opcode 0xFF0 */
/* Pauses the current process. */
opcode.opcode = ResumeProcessOpcode{};
} break;
case CheatVmOpcodeType::DebugLog: { case CheatVmOpcodeType::DebugLog: {
// FFFTIX## // FFFTIX##
// FFFTI0Ma aaaaaaaa // FFFTI0Ma aaaaaaaa
@ -621,7 +634,7 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
return valid; return valid;
} }
void DmntCheatVm::SkipConditionalBlock() { void DmntCheatVm::SkipConditionalBlock(bool is_if) {
if (condition_depth > 0) { if (condition_depth > 0) {
// We want to continue until we're out of the current block. // We want to continue until we're out of the current block.
const std::size_t desired_depth = condition_depth - 1; const std::size_t desired_depth = condition_depth - 1;
@ -637,8 +650,12 @@ void DmntCheatVm::SkipConditionalBlock() {
// We also support nesting of conditional blocks, and Gateway does not. // We also support nesting of conditional blocks, and Gateway does not.
if (skip_opcode.begin_conditional_block) { if (skip_opcode.begin_conditional_block) {
condition_depth++; condition_depth++;
} else if (std::holds_alternative<EndConditionalOpcode>(skip_opcode.opcode)) { } else if (auto end_cond = std::get_if<EndConditionalOpcode>(&skip_opcode.opcode)) {
if (!end_cond->is_else) {
condition_depth--; condition_depth--;
} else if (is_if && condition_depth - 1 == desired_depth) {
break;
}
} }
} }
} else { } else {
@ -675,6 +692,10 @@ u64 DmntCheatVm::GetCheatProcessAddress(const CheatProcessMetadata& metadata,
return metadata.main_nso_extents.base + rel_address; return metadata.main_nso_extents.base + rel_address;
case MemoryAccessType::Heap: case MemoryAccessType::Heap:
return metadata.heap_extents.base + rel_address; return metadata.heap_extents.base + rel_address;
case MemoryAccessType::Alias:
return metadata.alias_extents.base + rel_address;
case MemoryAccessType::Aslr:
return metadata.aslr_extents.base + rel_address;
} }
} }
@ -682,7 +703,6 @@ void DmntCheatVm::ResetState() {
registers.fill(0); registers.fill(0);
saved_values.fill(0); saved_values.fill(0);
loop_tops.fill(0); loop_tops.fill(0);
static_registers.fill(0);
instruction_ptr = 0; instruction_ptr = 0;
condition_depth = 0; condition_depth = 0;
decode_success = true; decode_success = true;
@ -753,7 +773,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
case 2: case 2:
case 4: case 4:
case 8: case 8:
callbacks->MemoryWrite(dst_address, &dst_value, store_static->bit_width); callbacks->MemoryWriteUnsafe(dst_address, &dst_value, store_static->bit_width);
break; break;
} }
} else if (auto begin_cond = std::get_if<BeginConditionalOpcode>(&cur_opcode.opcode)) { } else if (auto begin_cond = std::get_if<BeginConditionalOpcode>(&cur_opcode.opcode)) {
@ -766,7 +786,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
case 2: case 2:
case 4: case 4:
case 8: case 8:
callbacks->MemoryRead(src_address, &src_value, begin_cond->bit_width); callbacks->MemoryReadUnsafe(src_address, &src_value, begin_cond->bit_width);
break; break;
} }
// Check against condition. // Check against condition.
@ -794,14 +814,19 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
} }
// Skip conditional block if condition not met. // Skip conditional block if condition not met.
if (!cond_met) { if (!cond_met) {
SkipConditionalBlock(); SkipConditionalBlock(true);
} }
} else if (std::holds_alternative<EndConditionalOpcode>(cur_opcode.opcode)) { } else if (auto end_cond = std::get_if<EndConditionalOpcode>(&cur_opcode.opcode)) {
// Decrement the condition depth. if (end_cond->is_else) {
// We will assume, graciously, that mismatched conditional block ends are a nop. /* Skip to the end of the conditional block. */
this->SkipConditionalBlock(false);
} else {
/* Decrement the condition depth. */
/* We will assume, graciously, that mismatched conditional block ends are a nop. */
if (condition_depth > 0) { if (condition_depth > 0) {
condition_depth--; condition_depth--;
} }
}
} else if (auto ctrl_loop = std::get_if<ControlLoopOpcode>(&cur_opcode.opcode)) { } else if (auto ctrl_loop = std::get_if<ControlLoopOpcode>(&cur_opcode.opcode)) {
if (ctrl_loop->start_loop) { if (ctrl_loop->start_loop) {
// Start a loop. // Start a loop.
@ -832,7 +857,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
case 2: case 2:
case 4: case 4:
case 8: case 8:
callbacks->MemoryRead(src_address, &registers[ldr_memory->reg_index], callbacks->MemoryReadUnsafe(src_address, &registers[ldr_memory->reg_index],
ldr_memory->bit_width); ldr_memory->bit_width);
break; break;
} }
@ -849,7 +874,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
case 2: case 2:
case 4: case 4:
case 8: case 8:
callbacks->MemoryWrite(dst_address, &dst_value, str_static->bit_width); callbacks->MemoryWriteUnsafe(dst_address, &dst_value, str_static->bit_width);
break; break;
} }
// Increment register if relevant. // Increment register if relevant.
@ -908,7 +933,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
// Check for keypress. // Check for keypress.
if ((begin_keypress_cond->key_mask & kDown) != begin_keypress_cond->key_mask) { if ((begin_keypress_cond->key_mask & kDown) != begin_keypress_cond->key_mask) {
// Keys not pressed. Skip conditional block. // Keys not pressed. Skip conditional block.
SkipConditionalBlock(); SkipConditionalBlock(true);
} }
} else if (auto perform_math_reg = } else if (auto perform_math_reg =
std::get_if<PerformArithmeticRegisterOpcode>(&cur_opcode.opcode)) { std::get_if<PerformArithmeticRegisterOpcode>(&cur_opcode.opcode)) {
@ -1007,7 +1032,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
case 2: case 2:
case 4: case 4:
case 8: case 8:
callbacks->MemoryWrite(dst_address, &dst_value, str_register->bit_width); callbacks->MemoryWriteUnsafe(dst_address, &dst_value, str_register->bit_width);
break; break;
} }
@ -1086,7 +1111,8 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
case 2: case 2:
case 4: case 4:
case 8: case 8:
callbacks->MemoryRead(cond_address, &cond_value, begin_reg_cond->bit_width); callbacks->MemoryReadUnsafe(cond_address, &cond_value,
begin_reg_cond->bit_width);
break; break;
} }
} }
@ -1116,7 +1142,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
// Skip conditional block if condition not met. // Skip conditional block if condition not met.
if (!cond_met) { if (!cond_met) {
SkipConditionalBlock(); SkipConditionalBlock(true);
} }
} else if (auto save_restore_reg = } else if (auto save_restore_reg =
std::get_if<SaveRestoreRegisterOpcode>(&cur_opcode.opcode)) { std::get_if<SaveRestoreRegisterOpcode>(&cur_opcode.opcode)) {
@ -1178,6 +1204,10 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
// Store a register to a static register. // Store a register to a static register.
static_registers[rw_static_reg->static_idx] = registers[rw_static_reg->idx]; static_registers[rw_static_reg->static_idx] = registers[rw_static_reg->idx];
} }
} else if (std::holds_alternative<PauseProcessOpcode>(cur_opcode.opcode)) {
// TODO: Pause cheat process
} else if (std::holds_alternative<ResumeProcessOpcode>(cur_opcode.opcode)) {
// TODO: Resume cheat process
} else if (auto debug_log = std::get_if<DebugLogOpcode>(&cur_opcode.opcode)) { } else if (auto debug_log = std::get_if<DebugLogOpcode>(&cur_opcode.opcode)) {
// Read value from memory. // Read value from memory.
u64 log_value = 0; u64 log_value = 0;
@ -1224,7 +1254,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
case 2: case 2:
case 4: case 4:
case 8: case 8:
callbacks->MemoryRead(val_address, &log_value, debug_log->bit_width); callbacks->MemoryReadUnsafe(val_address, &log_value, debug_log->bit_width);
break; break;
} }
} }

View File

@ -42,12 +42,16 @@ enum class CheatVmOpcodeType : u32 {
DoubleExtendedWidth = 0xF0, DoubleExtendedWidth = 0xF0,
// Double-extended width opcodes. // Double-extended width opcodes.
PauseProcess = 0xFF0,
ResumeProcess = 0xFF1,
DebugLog = 0xFFF, DebugLog = 0xFFF,
}; };
enum class MemoryAccessType : u32 { enum class MemoryAccessType : u32 {
MainNso = 0, MainNso = 0,
Heap = 1, Heap = 1,
Alias = 2,
Aslr = 3,
}; };
enum class ConditionalComparisonType : u32 { enum class ConditionalComparisonType : u32 {
@ -131,7 +135,9 @@ struct BeginConditionalOpcode {
VmInt value{}; VmInt value{};
}; };
struct EndConditionalOpcode {}; struct EndConditionalOpcode {
bool is_else;
};
struct ControlLoopOpcode { struct ControlLoopOpcode {
bool start_loop{}; bool start_loop{};
@ -222,6 +228,10 @@ struct ReadWriteStaticRegisterOpcode {
u32 idx{}; u32 idx{};
}; };
struct PauseProcessOpcode {};
struct ResumeProcessOpcode {};
struct DebugLogOpcode { struct DebugLogOpcode {
u32 bit_width{}; u32 bit_width{};
u32 log_id{}; u32 log_id{};
@ -244,8 +254,8 @@ struct CheatVmOpcode {
PerformArithmeticStaticOpcode, BeginKeypressConditionalOpcode, PerformArithmeticStaticOpcode, BeginKeypressConditionalOpcode,
PerformArithmeticRegisterOpcode, StoreRegisterToAddressOpcode, PerformArithmeticRegisterOpcode, StoreRegisterToAddressOpcode,
BeginRegisterConditionalOpcode, SaveRestoreRegisterOpcode, BeginRegisterConditionalOpcode, SaveRestoreRegisterOpcode,
SaveRestoreRegisterMaskOpcode, ReadWriteStaticRegisterOpcode, DebugLogOpcode, SaveRestoreRegisterMaskOpcode, ReadWriteStaticRegisterOpcode, PauseProcessOpcode,
UnrecognizedInstruction> ResumeProcessOpcode, DebugLogOpcode, UnrecognizedInstruction>
opcode{}; opcode{};
}; };
@ -256,8 +266,8 @@ public:
public: public:
virtual ~Callbacks(); virtual ~Callbacks();
virtual void MemoryRead(VAddr address, void* data, u64 size) = 0; virtual void MemoryReadUnsafe(VAddr address, void* data, u64 size) = 0;
virtual void MemoryWrite(VAddr address, const void* data, u64 size) = 0; virtual void MemoryWriteUnsafe(VAddr address, const void* data, u64 size) = 0;
virtual u64 HidKeysDown() = 0; virtual u64 HidKeysDown() = 0;
@ -296,7 +306,7 @@ private:
std::array<std::size_t, NumRegisters> loop_tops{}; std::array<std::size_t, NumRegisters> loop_tops{};
bool DecodeNextOpcode(CheatVmOpcode& out); bool DecodeNextOpcode(CheatVmOpcode& out);
void SkipConditionalBlock(); void SkipConditionalBlock(bool is_if);
void ResetState(); void ResetState();
// For implementing the DebugLog opcode. // For implementing the DebugLog opcode.

View File

@ -401,6 +401,14 @@ void Config::ReadNetworkValues() {
EndGroup(); EndGroup();
} }
void Config::ReadLibraryAppletValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::LibraryApplet));
ReadCategory(Settings::Category::LibraryApplet);
EndGroup();
}
void Config::ReadValues() { void Config::ReadValues() {
if (global) { if (global) {
ReadDataStorageValues(); ReadDataStorageValues();
@ -410,6 +418,7 @@ void Config::ReadValues() {
ReadServiceValues(); ReadServiceValues();
ReadWebServiceValues(); ReadWebServiceValues();
ReadMiscellaneousValues(); ReadMiscellaneousValues();
ReadLibraryAppletValues();
} }
ReadControlValues(); ReadControlValues();
ReadCoreValues(); ReadCoreValues();
@ -511,6 +520,7 @@ void Config::SaveValues() {
SaveNetworkValues(); SaveNetworkValues();
SaveWebServiceValues(); SaveWebServiceValues();
SaveMiscellaneousValues(); SaveMiscellaneousValues();
SaveLibraryAppletValues();
} else { } else {
LOG_DEBUG(Config, "Saving only generic configuration values"); LOG_DEBUG(Config, "Saving only generic configuration values");
} }
@ -691,6 +701,14 @@ void Config::SaveWebServiceValues() {
EndGroup(); EndGroup();
} }
void Config::SaveLibraryAppletValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::LibraryApplet));
WriteCategory(Settings::Category::LibraryApplet);
EndGroup();
}
bool Config::ReadBooleanSetting(const std::string& key, const std::optional<bool> default_value) { bool Config::ReadBooleanSetting(const std::string& key, const std::optional<bool> default_value) {
std::string full_key = GetFullKey(key, false); std::string full_key = GetFullKey(key, false);
if (!default_value.has_value()) { if (!default_value.has_value()) {

View File

@ -88,6 +88,7 @@ protected:
void ReadSystemValues(); void ReadSystemValues();
void ReadWebServiceValues(); void ReadWebServiceValues();
void ReadNetworkValues(); void ReadNetworkValues();
void ReadLibraryAppletValues();
// Read platform specific sections // Read platform specific sections
virtual void ReadHidbusValues() = 0; virtual void ReadHidbusValues() = 0;
@ -121,6 +122,7 @@ protected:
void SaveScreenshotValues(); void SaveScreenshotValues();
void SaveSystemValues(); void SaveSystemValues();
void SaveWebServiceValues(); void SaveWebServiceValues();
void SaveLibraryAppletValues();
// Save platform specific sections // Save platform specific sections
virtual void SaveHidbusValues() = 0; virtual void SaveHidbusValues() = 0;

View File

@ -160,7 +160,7 @@ public:
} }
// Returns a reply from a command // Returns a reply from a command
virtual std::vector<u8> GetReply() const { virtual u64 GetReply(std::span<u8> out_data) const {
return {}; return {};
} }

View File

@ -90,32 +90,32 @@ u8 RingController::GetDeviceId() const {
return device_id; return device_id;
} }
std::vector<u8> RingController::GetReply() const { u64 RingController::GetReply(std::span<u8> out_data) const {
const RingConCommands current_command = command; const RingConCommands current_command = command;
switch (current_command) { switch (current_command) {
case RingConCommands::GetFirmwareVersion: case RingConCommands::GetFirmwareVersion:
return GetFirmwareVersionReply(); return GetFirmwareVersionReply(out_data);
case RingConCommands::ReadId: case RingConCommands::ReadId:
return GetReadIdReply(); return GetReadIdReply(out_data);
case RingConCommands::c20105: case RingConCommands::c20105:
return GetC020105Reply(); return GetC020105Reply(out_data);
case RingConCommands::ReadUnkCal: case RingConCommands::ReadUnkCal:
return GetReadUnkCalReply(); return GetReadUnkCalReply(out_data);
case RingConCommands::ReadFactoryCal: case RingConCommands::ReadFactoryCal:
return GetReadFactoryCalReply(); return GetReadFactoryCalReply(out_data);
case RingConCommands::ReadUserCal: case RingConCommands::ReadUserCal:
return GetReadUserCalReply(); return GetReadUserCalReply(out_data);
case RingConCommands::ReadRepCount: case RingConCommands::ReadRepCount:
return GetReadRepCountReply(); return GetReadRepCountReply(out_data);
case RingConCommands::ReadTotalPushCount: case RingConCommands::ReadTotalPushCount:
return GetReadTotalPushCountReply(); return GetReadTotalPushCountReply(out_data);
case RingConCommands::ResetRepCount: case RingConCommands::ResetRepCount:
return GetResetRepCountReply(); return GetResetRepCountReply(out_data);
case RingConCommands::SaveCalData: case RingConCommands::SaveCalData:
return GetSaveDataReply(); return GetSaveDataReply(out_data);
default: default:
return GetErrorReply(); return GetErrorReply(out_data);
} }
} }
@ -163,16 +163,16 @@ bool RingController::SetCommand(std::span<const u8> data) {
} }
} }
std::vector<u8> RingController::GetFirmwareVersionReply() const { u64 RingController::GetFirmwareVersionReply(std::span<u8> out_data) const {
const FirmwareVersionReply reply{ const FirmwareVersionReply reply{
.status = DataValid::Valid, .status = DataValid::Valid,
.firmware = version, .firmware = version,
}; };
return GetDataVector(reply); return GetData(reply, out_data);
} }
std::vector<u8> RingController::GetReadIdReply() const { u64 RingController::GetReadIdReply(std::span<u8> out_data) const {
// The values are hardcoded from a real joycon // The values are hardcoded from a real joycon
const ReadIdReply reply{ const ReadIdReply reply{
.status = DataValid::Valid, .status = DataValid::Valid,
@ -184,83 +184,83 @@ std::vector<u8> RingController::GetReadIdReply() const {
.id_h_x4 = 8245, .id_h_x4 = 8245,
}; };
return GetDataVector(reply); return GetData(reply, out_data);
} }
std::vector<u8> RingController::GetC020105Reply() const { u64 RingController::GetC020105Reply(std::span<u8> out_data) const {
const Cmd020105Reply reply{ const Cmd020105Reply reply{
.status = DataValid::Valid, .status = DataValid::Valid,
.data = 1, .data = 1,
}; };
return GetDataVector(reply); return GetData(reply, out_data);
} }
std::vector<u8> RingController::GetReadUnkCalReply() const { u64 RingController::GetReadUnkCalReply(std::span<u8> out_data) const {
const ReadUnkCalReply reply{ const ReadUnkCalReply reply{
.status = DataValid::Valid, .status = DataValid::Valid,
.data = 0, .data = 0,
}; };
return GetDataVector(reply); return GetData(reply, out_data);
} }
std::vector<u8> RingController::GetReadFactoryCalReply() const { u64 RingController::GetReadFactoryCalReply(std::span<u8> out_data) const {
const ReadFactoryCalReply reply{ const ReadFactoryCalReply reply{
.status = DataValid::Valid, .status = DataValid::Valid,
.calibration = factory_calibration, .calibration = factory_calibration,
}; };
return GetDataVector(reply); return GetData(reply, out_data);
} }
std::vector<u8> RingController::GetReadUserCalReply() const { u64 RingController::GetReadUserCalReply(std::span<u8> out_data) const {
const ReadUserCalReply reply{ const ReadUserCalReply reply{
.status = DataValid::Valid, .status = DataValid::Valid,
.calibration = user_calibration, .calibration = user_calibration,
}; };
return GetDataVector(reply); return GetData(reply, out_data);
} }
std::vector<u8> RingController::GetReadRepCountReply() const { u64 RingController::GetReadRepCountReply(std::span<u8> out_data) const {
const GetThreeByteReply reply{ const GetThreeByteReply reply{
.status = DataValid::Valid, .status = DataValid::Valid,
.data = {total_rep_count, 0, 0}, .data = {total_rep_count, 0, 0},
.crc = GetCrcValue({total_rep_count, 0, 0, 0}), .crc = GetCrcValue({total_rep_count, 0, 0, 0}),
}; };
return GetDataVector(reply); return GetData(reply, out_data);
} }
std::vector<u8> RingController::GetReadTotalPushCountReply() const { u64 RingController::GetReadTotalPushCountReply(std::span<u8> out_data) const {
const GetThreeByteReply reply{ const GetThreeByteReply reply{
.status = DataValid::Valid, .status = DataValid::Valid,
.data = {total_push_count, 0, 0}, .data = {total_push_count, 0, 0},
.crc = GetCrcValue({total_push_count, 0, 0, 0}), .crc = GetCrcValue({total_push_count, 0, 0, 0}),
}; };
return GetDataVector(reply); return GetData(reply, out_data);
} }
std::vector<u8> RingController::GetResetRepCountReply() const { u64 RingController::GetResetRepCountReply(std::span<u8> out_data) const {
return GetReadRepCountReply(); return GetReadRepCountReply(out_data);
} }
std::vector<u8> RingController::GetSaveDataReply() const { u64 RingController::GetSaveDataReply(std::span<u8> out_data) const {
const StatusReply reply{ const StatusReply reply{
.status = DataValid::Valid, .status = DataValid::Valid,
}; };
return GetDataVector(reply); return GetData(reply, out_data);
} }
std::vector<u8> RingController::GetErrorReply() const { u64 RingController::GetErrorReply(std::span<u8> out_data) const {
const ErrorReply reply{ const ErrorReply reply{
.status = DataValid::BadCRC, .status = DataValid::BadCRC,
}; };
return GetDataVector(reply); return GetData(reply, out_data);
} }
u8 RingController::GetCrcValue(const std::vector<u8>& data) const { u8 RingController::GetCrcValue(const std::vector<u8>& data) const {
@ -281,12 +281,11 @@ u8 RingController::GetCrcValue(const std::vector<u8>& data) const {
} }
template <typename T> template <typename T>
std::vector<u8> RingController::GetDataVector(const T& reply) const { u64 RingController::GetData(const T& reply, std::span<u8> out_data) const {
static_assert(std::is_trivially_copyable_v<T>); static_assert(std::is_trivially_copyable_v<T>);
std::vector<u8> data; const auto data_size = static_cast<u64>(std::min(sizeof(reply), out_data.size()));
data.resize(sizeof(reply)); std::memcpy(out_data.data(), &reply, data_size);
std::memcpy(data.data(), &reply, sizeof(reply)); return data_size;
return data;
} }
} // namespace Service::HID } // namespace Service::HID

View File

@ -34,7 +34,7 @@ public:
bool SetCommand(std::span<const u8> data) override; bool SetCommand(std::span<const u8> data) override;
// Returns a reply from a command // Returns a reply from a command
std::vector<u8> GetReply() const override; u64 GetReply(std::span<u8> data) const override;
private: private:
// These values are obtained from a real ring controller // These values are obtained from a real ring controller
@ -184,44 +184,44 @@ private:
RingConData GetSensorValue() const; RingConData GetSensorValue() const;
// Returns 8 byte reply with firmware version // Returns 8 byte reply with firmware version
std::vector<u8> GetFirmwareVersionReply() const; u64 GetFirmwareVersionReply(std::span<u8> out_data) const;
// Returns 16 byte reply with ID values // Returns 16 byte reply with ID values
std::vector<u8> GetReadIdReply() const; u64 GetReadIdReply(std::span<u8> out_data) const;
// (STUBBED) Returns 8 byte reply // (STUBBED) Returns 8 byte reply
std::vector<u8> GetC020105Reply() const; u64 GetC020105Reply(std::span<u8> out_data) const;
// (STUBBED) Returns 8 byte empty reply // (STUBBED) Returns 8 byte empty reply
std::vector<u8> GetReadUnkCalReply() const; u64 GetReadUnkCalReply(std::span<u8> out_data) const;
// Returns 20 byte reply with factory calibration values // Returns 20 byte reply with factory calibration values
std::vector<u8> GetReadFactoryCalReply() const; u64 GetReadFactoryCalReply(std::span<u8> out_data) const;
// Returns 20 byte reply with user calibration values // Returns 20 byte reply with user calibration values
std::vector<u8> GetReadUserCalReply() const; u64 GetReadUserCalReply(std::span<u8> out_data) const;
// Returns 8 byte reply // Returns 8 byte reply
std::vector<u8> GetReadRepCountReply() const; u64 GetReadRepCountReply(std::span<u8> out_data) const;
// Returns 8 byte reply // Returns 8 byte reply
std::vector<u8> GetReadTotalPushCountReply() const; u64 GetReadTotalPushCountReply(std::span<u8> out_data) const;
// Returns 8 byte reply // Returns 8 byte reply
std::vector<u8> GetResetRepCountReply() const; u64 GetResetRepCountReply(std::span<u8> out_data) const;
// Returns 4 byte save data reply // Returns 4 byte save data reply
std::vector<u8> GetSaveDataReply() const; u64 GetSaveDataReply(std::span<u8> out_data) const;
// Returns 8 byte error reply // Returns 8 byte error reply
std::vector<u8> GetErrorReply() const; u64 GetErrorReply(std::span<u8> out_data) const;
// Returns 8 bit redundancy check from provided data // Returns 8 bit redundancy check from provided data
u8 GetCrcValue(const std::vector<u8>& data) const; u8 GetCrcValue(const std::vector<u8>& data) const;
// Converts structs to an u8 vector equivalent // Converts structs to an u8 vector equivalent
template <typename T> template <typename T>
std::vector<u8> GetDataVector(const T& reply) const; u64 GetData(const T& reply, std::span<u8> out_data) const;
RingConCommands command{RingConCommands::Error}; RingConCommands command{RingConCommands::Error};

View File

@ -38,7 +38,7 @@ u8 Starlink::GetDeviceId() const {
return DEVICE_ID; return DEVICE_ID;
} }
std::vector<u8> Starlink::GetReply() const { u64 Starlink::GetReply(std::span<u8> out_data) const {
return {}; return {};
} }

View File

@ -31,7 +31,7 @@ public:
bool SetCommand(std::span<const u8> data) override; bool SetCommand(std::span<const u8> data) override;
// Returns a reply from a command // Returns a reply from a command
std::vector<u8> GetReply() const override; u64 GetReply(std::span<u8> out_data) const override;
}; };
} // namespace Service::HID } // namespace Service::HID

View File

@ -38,7 +38,7 @@ u8 HidbusStubbed::GetDeviceId() const {
return DEVICE_ID; return DEVICE_ID;
} }
std::vector<u8> HidbusStubbed::GetReply() const { u64 HidbusStubbed::GetReply(std::span<u8> out_data) const {
return {}; return {};
} }

View File

@ -31,7 +31,7 @@ public:
bool SetCommand(std::span<const u8> data) override; bool SetCommand(std::span<const u8> data) override;
// Returns a reply from a command // Returns a reply from a command
std::vector<u8> GetReply() const override; u64 GetReply(std::span<u8> out_data) const override;
}; };
} // namespace Service::HID } // namespace Service::HID

View File

@ -145,9 +145,8 @@ void ImageTransferProcessor::SetTransferMemoryAddress(Common::ProcessAddress t_m
} }
Core::IrSensor::ImageTransferProcessorState ImageTransferProcessor::GetState( Core::IrSensor::ImageTransferProcessorState ImageTransferProcessor::GetState(
std::vector<u8>& data) const { std::span<u8> data) const {
const auto size = GetDataSize(current_config.trimming_format); const auto size = std::min(GetDataSize(current_config.trimming_format), data.size());
data.resize(size);
system.ApplicationMemory().ReadBlock(transfer_memory, data.data(), size); system.ApplicationMemory().ReadBlock(transfer_memory, data.data(), size);
return processor_state; return processor_state;
} }

View File

@ -3,6 +3,8 @@
#pragma once #pragma once
#include <span>
#include "common/typed_address.h" #include "common/typed_address.h"
#include "hid_core/irsensor/irs_types.h" #include "hid_core/irsensor/irs_types.h"
#include "hid_core/irsensor/processor_base.h" #include "hid_core/irsensor/processor_base.h"
@ -39,7 +41,7 @@ public:
// Transfer memory where the image data will be stored // Transfer memory where the image data will be stored
void SetTransferMemoryAddress(Common::ProcessAddress t_mem); void SetTransferMemoryAddress(Common::ProcessAddress t_mem);
Core::IrSensor::ImageTransferProcessorState GetState(std::vector<u8>& data) const; Core::IrSensor::ImageTransferProcessorState GetState(std::span<u8> data) const;
private: private:
// This is nn::irsensor::ImageTransferProcessorConfig // This is nn::irsensor::ImageTransferProcessorConfig

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