Compare commits

..

84 Commits

Author SHA1 Message Date
lat9nq
ad9f97cd8d ci/linux: Drop linuxdeploy usage
Recent versions of Docker appear to cause the Qt linuxdeploy plugin to
throw a boost file copy error.

This switches from linuxdeploy to a script of mine I've been working on
for a while.
2022-09-26 00:35:03 -04:00
bunnei
1b1b99fbd5 Merge pull request #8941 from Kelebek1/single_core_sucks
Do not try to pause core timing from the audio thread when using single-core
2022-09-23 22:40:30 -07:00
Morph
d794ced303 Merge pull request #8945 from Tachi107/typos
chore: fix some typos
2022-09-23 20:28:43 -04:00
Morph
5082efef6c Merge pull request #8948 from german77/order
yuzu: sort input profiles by name
2022-09-23 20:28:35 -04:00
Morph
86ccc87111 Merge pull request #8933 from lat9nq/ci-use-apprun-sh
ci/linux: Use AppRun.sh to start AppImage
2022-09-23 20:28:28 -04:00
Morph
12baf88dc8 Merge pull request #8930 from lat9nq/disable-vulkan-check
yuzu qt: Add option to disable startup Vulkan check
2022-09-23 20:28:13 -04:00
Narr the Reg
f711d1ce52 yuzu: sort input profiles by name 2022-09-23 12:31:35 -05:00
Morph
70abb13a43 Merge pull request #8943 from lioncash/netiface
sockets: Make fd member variable protected
2022-09-23 09:08:07 -04:00
Morph
c2c3f2c3e7 Merge pull request #8939 from lioncash/render
audio_renderer: Make GetCommandBuffer() take a u32
2022-09-23 09:07:52 -04:00
Morph
3b77dec188 Merge pull request #8938 from lioncash/manager
audio_manager: Minor cleanup related changes
2022-09-23 09:07:38 -04:00
Andrea Pappacoda
4a493cb10f chore: fix some typos
Fix some typos reported by Lintian
2022-09-23 13:38:23 +02:00
Lioncash
c8da75b7ed sockets: Make fd member variable protected
Other things shouldn't be able to directly mess around with the
descriptor
2022-09-22 08:20:54 -04:00
Kelebek1
56b8a9ba6e Do not try to pause core timing from the audio thread when using single-core 2022-09-22 01:23:57 +01:00
Lioncash
c891497b61 audio_renderer: Make GetCommandBuffer() take a u32
This function is only ever called with unsigned types, and all of the
other interface functions take session_id as a u32, so this makes the
class a little more consistent.
2022-09-21 10:00:57 -04:00
Lioncash
00616c7ad8 audio_manager: Forward declare result type
Moves the include into the cpp file to lessen header dependencies.
2022-09-21 09:43:59 -04:00
Lioncash
75d6fe3669 audio_manager: Remove redundant cast in ThreadFunc()
We can just use a local here to get rid of a second cast.
2022-09-21 09:35:22 -04:00
Lioncash
e48e22eb52 audio_manager: move std::functions in SetOutManager/SetInManager
Prevents unnecessary reallocations in the event the captured variables
are larger than the internal std::function buffer.
2022-09-21 09:32:15 -04:00
Lioncash
da0fd51642 audio_manager: Remove unused forward declarations
Allows us to get rid of some unnecessary forward declarations and
includes.
2022-09-21 09:29:35 -04:00
Lioncash
08d81e0bd8 audio_manager: Remove unused sessions_started member variable
This isn't used, so it can be removed.
2022-09-21 09:28:05 -04:00
Lioncash
b59b967280 audio_manager: Remove dependence on system state
This isn't used by the class, so this can be removed for the time being.
2022-09-21 09:26:42 -04:00
lat9nq
37b4f79b54 ci/linux: Use AppRun.sh to start AppImage
The current AppRun is more difficult to update. This script still
uses the old version of AppImageKit-checkrt, but now we use the shell
script version so we can set our own environment variables as the
application starts up.

This specific version searches for and sets the correct root CA file to
prevent SSL errors in yuzu.
2022-09-20 18:57:27 -04:00
bunnei
8d4458ef24 Merge pull request #8849 from Morph1984/parallel-astc
astc: Enable parallel CPU astc decoding
2022-09-19 12:17:51 -07:00
lat9nq
89af4bfba8 yuzu qt: Add option to disable startup Vulkan check
The startup check apparently confuses other programs when yuzu launches
2 processes and then quickly closes one of them. Though this isn't
really our issues it's also not a big deal for me to add an option to
work around that issue.
2022-09-19 14:46:12 -04:00
bunnei
3a5f9409c8 Merge pull request #8915 from vonchenplus/opus_multi_stream
core: implement HwOpus GetWorkBufferSizeForMultiStreamEx
2022-09-17 16:07:33 -07:00
bunnei
7172339c7a Merge pull request #8827 from german77/amiibo_release
core: nfp: Implement amiibo encryption
2022-09-17 11:11:21 -07:00
bunnei
9c32f29af1 Merge pull request #8650 from Kelebek1/vsync
[Coretiming/NVNFlinger] Improve multi-core vsync timing, and core timing accuracy
2022-09-17 11:10:54 -07:00
bunnei
4a7a771340 Merge pull request #8914 from lioncash/audio-const
audio_core: Mark several member functions as const
2022-09-16 23:51:31 -07:00
bunnei
eb726677b2 Merge pull request #8916 from Docteh/muilti_build
GIT: Modify .gitignore to ignore wildcard for build directories
2022-09-16 18:19:01 -07:00
bunnei
92813f01a4 Merge pull request #8906 from Docteh/fix_icons
UI: move icons from default into colorful theme.
2022-09-16 18:12:04 -07:00
Kyle K
49870baea4 GIT: Modify .gitignore to ignore wildcard for build directories
Helps if you have multiple build folders. There are other, dark ways to
hide extra build folders from git, but this is better.

See: https://github.com/citra-emu/citra/pull/6130
2022-09-16 10:58:29 -07:00
Morph
344006b856 Merge pull request #8869 from SachinVin/cmake
core/CMakeLists.txt: Remove duplicate files.
2022-09-16 13:11:33 -04:00
Morph
8dafe15600 Merge pull request #8649 from lat9nq/common-position-independent
common: Use PROJECT_SOURCE_DIR to find CMakeModules
2022-09-16 12:48:53 -04:00
Morph
44ccec7846 Merge pull request #8682 from lat9nq/dumpy
yuzu qt: Add option to create Windows crash dumps
2022-09-16 12:47:51 -04:00
Morph
882dfa36ae Merge pull request #6667 from lat9nq/ea-appimage
ci,linux: Support Patreon releases
2022-09-16 12:47:38 -04:00
FengChen
672e61d802 core: implement HwOpus GetWorkBufferSizeForMultiStreamEx 2022-09-17 00:28:56 +08:00
Morph
60aa942210 Merge pull request #8911 from lioncash/cexpr-string
audio_device: Make AudioDeviceName constructor constexpr
2022-09-16 10:18:58 -04:00
Morph
809126c94a astc: Enable parallel CPU astc decoding
Given the issues with GPU accelerated ASTC decoding with NVIDIA's latest drivers, parallelize astc decoding on the CPU.
Uses half the available threads in the system for astc decoding.
2022-09-16 10:16:42 -04:00
Lioncash
7e3cdfc453 audio_renderer: Pass command buffer by const reference
This is just being copied and isn't modified at all.
2022-09-16 10:06:53 -04:00
Lioncash
d5d6322640 sink_stream: Mark GetQueueSize as const 2022-09-16 10:00:52 -04:00
Lioncash
6b1cb73350 node_states: Mark relevant member functions as const 2022-09-16 09:59:56 -04:00
Lioncash
e4bc7b8611 i3dl2/reverb: Mark relevant member functions as const
These two don't modify member state.
2022-09-16 09:58:49 -04:00
Lioncash
b2c2138af7 behavior_info: Mark CopyErrorInfo as const
This doesn't modify member state.

We can also mark the parameter of AppendError as const as well, since it
isn't modified.
2022-09-16 09:55:17 -04:00
Lioncash
b862d5d8d8 audio_device: Mark GetDeviceVolume as const
This doesn't modify instance state.
2022-09-16 09:52:34 -04:00
Lioncash
36c77761cf audio_render_manager: Mark several functions as const 2022-09-16 09:50:32 -04:00
Lioncash
7a5d235d94 audio_in: Mark several functions as const
These functions don't modify class state, so we can mark them as such
2022-09-16 09:45:54 -04:00
Lioncash
d1f3c121a0 audio_out: Mark several functions as const
These don't affect class state, so we can mark them as such.
2022-09-16 09:45:51 -04:00
Lioncash
e9109cb5f2 audio_buffers: Pass by const-ref in AppendBuffers
This function doesn't modify the passed in buffer, so we can make that
explicit.
2022-09-16 09:36:03 -04:00
Lioncash
cb2a33babc device_session: Convert for loop into ranged for in AppendBuffers
Simplifies the indexing code a little bit.
2022-09-16 09:32:57 -04:00
Lioncash
a278fa6e2a device_session: Pass arguments by const-ref in relevant functions
These functions don't modify the passed in audio buffers, so we can
signify that in the interface.
2022-09-16 09:31:33 -04:00
Kyle Kienapfel
9554c67809 UI: move icons from default into colorful theme.
colorful theme has been default theme for awhile. having colorful theme
try and grab icons from other theme doesn't work on Linux.

Also adding two additional icons, info is to hint to the user that they
should hit verify after pasting in a token, sync is to show that the
verification is occurring.
2022-09-15 23:00:49 -07:00
bunnei
e85bda5f31 Merge pull request #8878 from Kelebek1/remove_pause
Remove pause callbacks from coretiming
2022-09-15 13:50:13 -07:00
bunnei
b5a06bc419 Merge pull request #8902 from Morph1984/new_sd_icons
qt_themes: Update sd card icon
2022-09-15 11:47:20 -07:00
liamwhite
b06ef5d530 Merge pull request #8901 from lioncash/docs
audio_core: Amend documentation comment tags
2022-09-15 10:36:18 -04:00
Narr the Reg
0a63d43ad6 Merge pull request #8909 from Docteh/taslinky
UI: Fix link to TAS help page
2022-09-15 08:48:12 -05:00
Lioncash
2c91fbf7f1 audio_core: Amend documentation tags
Resolves a wackload of -Wdocumentation warnings due to mismatching tags
and whatnot.
2022-09-15 09:47:23 -04:00
Mai
463cc9559f Merge pull request #8904 from liushuyu/fix-xbyak-linkage
common: do not link to xbyak on non-amd64 architectures
2022-09-15 09:44:57 -04:00
Lioncash
d55046c5e9 audio_device: Mark member functions as const where applicable
These member functions don't modify any internal state.
2022-09-15 09:06:17 -04:00
Lioncash
1c7dae966d audio_device: Make AudioDeviceName constructor constexpr
These are used as read-only arrays, so we can make the data read-only
and available at compile-time.

Now constructing an AudioDevice no longer needs to initialize some
tables
2022-09-15 09:03:40 -04:00
liushuyu
7fda6de5cb common: do not link to xbyak on non-amd64 architectures 2022-09-13 17:19:37 -06:00
Kelebek1
e93e898df5 Remove pause callbacks from coretiming 2022-09-13 13:20:35 +01:00
Morph
56be24d50a qt_themes: Add colorful and dark mode sd card icons 2022-09-12 18:54:29 -04:00
Dev-draco
949917babc qt_themes: Update sd card icon 2022-09-12 18:54:29 -04:00
SachinVin
9c6cd93195 core/CMakeLists.txt: Remove duplicate files. 2022-09-08 22:03:53 +05:30
german77
063b23cc58 core: nfp: Remove magic numbers 2022-09-07 09:49:43 -05:00
german77
4834961736 core: nfp: Workaround for lack of multiple nfp interfaces 2022-09-07 01:04:00 -05:00
Narr the Reg
caa138b33f core: nfp: Correct date and amiibo name 2022-09-07 01:04:00 -05:00
Narr the Reg
19a4e12e6e core: nfp: Implement Convert and RecreateApplicationArea, accuracy fixes 2022-09-07 01:04:00 -05:00
german77
848f69eb19 core: nfp: Implement amiibo encryption 2022-09-07 01:04:00 -05:00
lat9nq
9dc9e501de ci/windows: Upload debugging symbols 2022-09-04 21:36:35 -04:00
lat9nq
12f7d42d32 mini_dump: Address review feedback
Uses fmt::print as opposed to std::fprintf. Adds a missing return.
static's a single-use function. Initializes structs as opposed to
std::memset where possible. Fixes CMake linkage.

Co-authored-by: Lioncash <mathew1800@gmail.com>

mini_dump: Use a namespace

Co-authored-by: Lioncash <mathew1800@gmail.com>
2022-09-04 21:36:35 -04:00
lat9nq
45b343d1d0 ci,workflows: Enable crash dumps on MSVC builds
ci/windows: Enable crash dumps on MinGW builds
2022-09-04 21:36:34 -04:00
lat9nq
c976613ab2 vcpkg,cmake: Use vcpkg for dbghelp 2022-09-04 21:36:05 -04:00
lat9nq
e339ec0e00 mini_dump: Check for debugger before spawning a child
mini_dump: Clean up

mini_dump: Fix MSVC error

mini_dump: Silence MSVC warning C4700

Zero initialize deb_ev.

mini_dump: Add license info
2022-09-04 21:36:05 -04:00
lat9nq
3dbaafe1f3 mini_dump: Cleanup and add comments
Removes some unnecessary code.

wip
2022-09-04 21:36:05 -04:00
lat9nq
f958cbc737 yuzu: Use a debugger to generate minidumps
yuzu: Move mini_dump out of core

startup_checks: Better exception handling
2022-09-04 21:36:04 -04:00
Morph
606cdb17d3 core_timing: Sleep in discrete intervals, yield during spin 2022-08-02 01:01:54 -04:00
Kelebek1
658e1ee426 Add missing looping event schedule signal 2022-08-02 01:01:54 -04:00
Kelebek1
83a24ad638 Make coretiming waiting more accurate 2022-08-02 01:01:54 -04:00
Kelebek1
9d3b190465 Rework multi-core vsync 2022-08-01 23:51:53 -04:00
lat9nq
260430c849 common: Use PROJECT_SOURCE_DIR to find CMakeModules
Fixes CMake configuration when yuzu is a submodule of another project.
2022-08-01 23:18:56 -04:00
lat9nq
6b58db9ccd patreon step2: Use jobs to build for Windows and Linux
Apparently the two stages were not building in parallel. Specify
individual jobs that run MSVC and Linux building instead.
2022-07-31 21:07:46 -04:00
lat9nq
d77fe3b1c2 ci/linux: EA AppImage adjustments
Prevent AppImageLauncher from trying to integrate our AppImage on end
user systems. Don't include the basic yuzu executable with EA or
Mainline.
2022-07-31 03:25:29 -04:00
lat9nq
69bd6cd490 patreon step2: Enable Linux build
I sure as heck don't know what I'm doing :)

patreon_step2: Fix caching

:limesDance: still don't know what I'm doing :limesDance:
2022-07-31 01:35:12 -04:00
lat9nq
dc915aff62 ci,linux: Support Patreon releases
The Early Access AppImage needs to be accessible through liftinstall, so
a couple modifications need to made:

The DIR_NAME needs to not include the revision info.
The EA AppImage name cannot contain revision info.
The EA AppImage has to be packaged with the rest of the yuzu package,
which means both binaries and the source are bundled with it now in an
archive.

In addition, fix the source archive so yuzu can actually be built from
it.

upload: Copy AppImage to both mainline and EA release package
2022-07-31 01:35:11 -04:00
131 changed files with 2377 additions and 961 deletions

View File

@@ -33,16 +33,14 @@ DESTDIR="$PWD/AppDir" ninja install
rm -vf AppDir/usr/bin/yuzu-cmd AppDir/usr/bin/yuzu-tester
# Download tools needed to build an AppImage
wget -nc https://github.com/yuzu-emu/ext-linux-bin/raw/main/appimage/linuxdeploy-x86_64.AppImage
wget -nc https://github.com/yuzu-emu/ext-linux-bin/raw/main/appimage/linuxdeploy-plugin-qt-x86_64.AppImage
wget -nc https://github.com/yuzu-emu/ext-linux-bin/raw/main/appimage/AppRun-patched-x86_64
wget -nc https://raw.githubusercontent.com/lat9nq/deploy/main/linux/deploy-linux.sh
wget -nc https://raw.githubusercontent.com/yuzu-emu/AppImageKit-checkrt/old/AppRun.sh
wget -nc https://github.com/yuzu-emu/ext-linux-bin/raw/main/appimage/exec-x86_64.so
# Set executable bit
chmod 755 \
AppRun-patched-x86_64 \
deploy-linux.sh \
AppRun.sh \
exec-x86_64.so \
linuxdeploy-x86_64.AppImage \
linuxdeploy-plugin-qt-x86_64.AppImage
# Workaround for https://github.com/AppImage/AppImageKit/issues/828
export APPIMAGE_EXTRACT_AND_RUN=1
@@ -52,7 +50,7 @@ mkdir -p AppDir/usr/optional/libstdc++
mkdir -p AppDir/usr/optional/libgcc_s
# Deploy yuzu's needed dependencies
./linuxdeploy-x86_64.AppImage --appdir AppDir --plugin qt
DEPLOY_QT=1 ./deploy-linux.sh AppDir/usr/bin/yuzu AppDir
# Workaround for libQt5MultimediaGstTools indirectly requiring libwayland-client and breaking Vulkan usage on end-user systems
find AppDir -type f -regex '.*libwayland-client\.so.*' -delete -print
@@ -60,6 +58,6 @@ find AppDir -type f -regex '.*libwayland-client\.so.*' -delete -print
# Workaround for building yuzu with GCC 10 but also trying to distribute it to Ubuntu 18.04 et al.
# See https://github.com/darealshinji/AppImageKit-checkrt
cp exec-x86_64.so AppDir/usr/optional/exec.so
cp AppRun-patched-x86_64 AppDir/AppRun
cp AppRun.sh AppDir/AppRun
cp --dereference /usr/lib/x86_64-linux-gnu/libstdc++.so.6 AppDir/usr/optional/libstdc++/libstdc++.so.6
cp --dereference /lib/x86_64-linux-gnu/libgcc_s.so.1 AppDir/usr/optional/libgcc_s/libgcc_s.so.1

View File

@@ -5,21 +5,24 @@
. .ci/scripts/common/pre-upload.sh
APPIMAGE_NAME="yuzu-${GITDATE}-${GITREV}.AppImage"
REV_NAME="yuzu-linux-${GITDATE}-${GITREV}"
APPIMAGE_NAME="yuzu-${RELEASE_NAME}-${GITDATE}-${GITREV}.AppImage"
BASE_NAME="yuzu-linux"
REV_NAME="${BASE_NAME}-${GITDATE}-${GITREV}"
ARCHIVE_NAME="${REV_NAME}.tar.xz"
COMPRESSION_FLAGS="-cJvf"
if [ "${RELEASE_NAME}" = "mainline" ]; then
DIR_NAME="${REV_NAME}"
if [ "${RELEASE_NAME}" = "mainline" ] || [ "${RELEASE_NAME}" = "early-access" ]; then
DIR_NAME="${BASE_NAME}-${RELEASE_NAME}"
else
DIR_NAME="${REV_NAME}_${RELEASE_NAME}"
DIR_NAME="${REV_NAME}-${RELEASE_NAME}"
fi
mkdir "$DIR_NAME"
cp build/bin/yuzu-cmd "$DIR_NAME"
cp build/bin/yuzu "$DIR_NAME"
if [ "${RELEASE_NAME}" != "early-access" ] && [ "${RELEASE_NAME}" != "mainline" ]; then
cp build/bin/yuzu "$DIR_NAME"
fi
# Build an AppImage
cd build
@@ -32,6 +35,11 @@ if ! ./appimagetool-x86_64.AppImage --version; then
export APPIMAGE_EXTRACT_AND_RUN=1
fi
# Don't let AppImageLauncher ask to integrate EA
if [ "${RELEASE_NAME}" = "mainline" ] || [ "${RELEASE_NAME}" = "early-access" ]; then
echo "X-AppImage-Integrate=false" >> AppDir/org.yuzu_emu.yuzu.desktop
fi
if [ "${RELEASE_NAME}" = "mainline" ]; then
# Generate update information if releasing to mainline
./appimagetool-x86_64.AppImage -u "gh-releases-zsync|yuzu-emu|yuzu-${RELEASE_NAME}|latest|yuzu-*.AppImage.zsync" AppDir "${APPIMAGE_NAME}"
@@ -46,4 +54,9 @@ if [ -f "build/${APPIMAGE_NAME}.zsync" ]; then
cp "build/${APPIMAGE_NAME}.zsync" "${ARTIFACTS_DIR}/"
fi
# Copy the AppImage to the general release directory and remove git revision info
if [ "${RELEASE_NAME}" = "mainline" ] || [ "${RELEASE_NAME}" = "early-access" ]; then
cp "build/${APPIMAGE_NAME}" "${DIR_NAME}/yuzu-${RELEASE_NAME}.AppImage"
fi
. .ci/scripts/common/post-upload.sh

View File

@@ -21,6 +21,7 @@ cmake .. \
-DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON \
-DENABLE_QT_TRANSLATION=ON \
-DUSE_CCACHE=ON \
-DYUZU_CRASH_DUMPS=ON \
-DYUZU_USE_BUNDLED_SDL2=OFF \
-DYUZU_USE_EXTERNAL_SDL2=OFF \
-DYUZU_TESTS=OFF \

View File

@@ -65,8 +65,8 @@ if ("$env:GITHUB_ACTIONS" -eq "true") {
# None of the other GHA builds are including source, so commenting out today
#Copy-Item $MSVC_SOURCE_TARXZ -Destination "artifacts"
# Are debug symbols important?
# cp .\build\bin\yuzu*.pdb .\pdb\
# Debugging symbols
cp .\build\bin\yuzu*.pdb .\artifacts\
# Write out a tag BUILD_TAG to environment for the Upload step
# We're getting ${{ github.event.number }} as $env:PR_NUMBER"

View File

@@ -9,7 +9,7 @@ parameters:
steps:
- script: choco install vulkan-sdk
displayName: 'Install vulkan-sdk'
- script: refreshenv && mkdir build && cd build && cmake -G "Visual Studio 17 2022" -A x64 -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DYUZU_TESTS=OFF -DUSE_DISCORD_PRESENCE=ON -DENABLE_QT_TRANSLATION=ON -DDISPLAY_VERSION=${{ parameters['version'] }} -DCMAKE_BUILD_TYPE=Release .. && cd ..
- script: refreshenv && mkdir build && cd build && cmake -G "Visual Studio 17 2022" -A x64 -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DYUZU_TESTS=OFF -DUSE_DISCORD_PRESENCE=ON -DENABLE_QT_TRANSLATION=ON -DDISPLAY_VERSION=${{ parameters['version'] }} -DCMAKE_BUILD_TYPE=Release -DYUZU_CRASH_DUMPS=ON .. && cd ..
displayName: 'Configure CMake'
- task: MSBuild@1
displayName: 'Build'

View File

@@ -11,9 +11,30 @@ stages:
- stage: build
displayName: 'build'
jobs:
- job: build
- job: linux
timeoutInMinutes: 120
displayName: 'windows-msvc'
displayName: 'linux'
pool:
vmImage: ubuntu-latest
strategy:
maxParallel: 10
matrix:
linux:
BuildSuffix: 'linux'
ScriptFolder: 'linux'
steps:
- template: ./templates/sync-source.yml
parameters:
artifactSource: $(parameters.artifactSource)
needSubmodules: 'true'
- template: ./templates/build-single.yml
parameters:
artifactSource: 'false'
cache: $(parameters.cache)
version: $(DisplayVersion)
- job: msvc
timeoutInMinutes: 120
displayName: 'windows'
pool:
vmImage: windows-2022
steps:

View File

@@ -104,7 +104,7 @@ jobs:
run: |
glslangValidator --version
mkdir build
cmake . -B build -GNinja -DCMAKE_TOOLCHAIN_FILE="CMakeModules/MSVCCache.cmake" -DUSE_CCACHE=ON -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=ON -DUSE_DISCORD_PRESENCE=ON -DENABLE_QT_TRANSLATION=ON -DCMAKE_BUILD_TYPE=Release -DGIT_BRANCH=pr-verify
cmake . -B build -GNinja -DCMAKE_TOOLCHAIN_FILE="CMakeModules/MSVCCache.cmake" -DUSE_CCACHE=ON -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=ON -DUSE_DISCORD_PRESENCE=ON -DENABLE_QT_TRANSLATION=ON -DCMAKE_BUILD_TYPE=Release -DGIT_BRANCH=pr-verify -DYUZU_CRASH_DUMPS=ON
- name: Build
run: cmake --build build
- name: Cache Summary

2
.gitignore vendored
View File

@@ -2,7 +2,7 @@
# SPDX-License-Identifier: GPL-2.0-or-later
# Build directory
[Bb]uild/
[Bb]uild*/
doc-build/
# Generated source files

View File

@@ -6,6 +6,7 @@ Files: dist/english_plurals/*
dist/icons/controller/*.png
dist/icons/overlay/*.png
dist/languages/*
dist/qt_themes/*/icons/48x48/sd_card.png
dist/qt_themes/*/icons/index.theme
dist/qt_themes/default/style.qss
Copyright: yuzu Emulator Project
@@ -51,6 +52,8 @@ Files: dist/qt_themes/colorful/icons/16x16/lock.png
dist/qt_themes/colorful/icons/48x48/chip.png
dist/qt_themes/colorful/icons/48x48/folder.png
dist/qt_themes/colorful_dark/icons/16x16/lock.png
dist/qt_themes/colorful/icons/16x16/info.png
dist/qt_themes/colorful/icons/16x16/sync.png
Copyright: Icons8
License: MIT
Comment: https://github.com/icons8/flat-color-icons
@@ -66,11 +69,9 @@ Files: dist/qt_themes/*/icons/48x48/no_avatar.png
Copyright: Ionic (http://ionic.io/)
License: MIT
Files: dist/qt_themes/*/icons/48x48/sd_card.png
dist/qt_themes/colorful/icons/48x48/star.png
dist/qt_themes/default/icons/16x16/checked.png
dist/qt_themes/default/icons/16x16/failed.png
Files: dist/qt_themes/colorful/icons/48x48/star.png
dist/qt_themes/colorful/icons/16x16/checked.png
dist/qt_themes/colorful/icons/16x16/failed.png
Copyright: SVG Repo
License: CC0-1.0

View File

@@ -38,6 +38,8 @@ option(YUZU_USE_BUNDLED_OPUS "Compile bundled opus" ON)
option(YUZU_TESTS "Compile tests" ON)
CMAKE_DEPENDENT_OPTION(YUZU_CRASH_DUMPS "Compile Windows crash dump (Minidump) support" OFF "WIN32" OFF)
option(YUZU_USE_BUNDLED_VCPKG "Use vcpkg for yuzu dependencies" "${MSVC}")
option(YUZU_CHECK_SUBMODULES "Check if submodules are present" ON)
@@ -46,6 +48,9 @@ if (YUZU_USE_BUNDLED_VCPKG)
if (YUZU_TESTS)
list(APPEND VCPKG_MANIFEST_FEATURES "yuzu-tests")
endif()
if (YUZU_CRASH_DUMPS)
list(APPEND VCPKG_MANIFEST_FEATURES "dbghelp")
endif()
include(${CMAKE_SOURCE_DIR}/externals/vcpkg/scripts/buildsystems/vcpkg.cmake)
elseif(NOT "$ENV{VCPKG_TOOLCHAIN_FILE}" STREQUAL "")
@@ -447,6 +452,13 @@ elseif (WIN32)
# PSAPI is the Process Status API
set(PLATFORM_LIBRARIES ${PLATFORM_LIBRARIES} psapi imm32 version)
endif()
if (YUZU_CRASH_DUMPS)
find_library(DBGHELP_LIBRARY dbghelp)
if ("${DBGHELP_LIBRARY}" STREQUAL "DBGHELP_LIBRARY-NOTFOUND")
message(FATAL_ERROR "YUZU_CRASH_DUMPS enabled but dbghelp library not found")
endif()
endif()
elseif (CMAKE_SYSTEM_NAME MATCHES "^(Linux|kFreeBSD|GNU|SunOS)$")
set(PLATFORM_LIBRARIES rt)
endif()

View File

Before

Width:  |  Height:  |  Size: 414 B

After

Width:  |  Height:  |  Size: 414 B

View File

Before

Width:  |  Height:  |  Size: 431 B

After

Width:  |  Height:  |  Size: 431 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 428 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 548 B

View File

Before

Width:  |  Height:  |  Size: 349 B

After

Width:  |  Height:  |  Size: 349 B

View File

Before

Width:  |  Height:  |  Size: 678 B

After

Width:  |  Height:  |  Size: 678 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 981 B

After

Width:  |  Height:  |  Size: 228 B

View File

@@ -1,7 +1,6 @@
[Icon Theme]
Name=colorful
Comment=Colorful theme
Inherits=default
Directories=16x16,48x48,256x256
[16x16]

View File

@@ -6,14 +6,20 @@ SPDX-License-Identifier: GPL-2.0-or-later
<RCC>
<qresource prefix="icons/colorful">
<file alias="index.theme">icons/index.theme</file>
<file alias="16x16/checked.png">icons/16x16/checked.png</file>
<file alias="16x16/connected.png">icons/16x16/connected.png</file>
<file alias="16x16/connected_notification.png">icons/16x16/connected_notification.png</file>
<file alias="16x16/disconnected.png">icons/16x16/disconnected.png</file>
<file alias="16x16/failed.png">icons/16x16/failed.png</file>
<file alias="16x16/info.png">icons/16x16/info.png</file>
<file alias="16x16/lock.png">icons/16x16/lock.png</file>
<file alias="16x16/sync.png">icons/16x16/sync.png</file>
<file alias="16x16/view-refresh.png">icons/16x16/view-refresh.png</file>
<file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file>
<file alias="48x48/chip.png">icons/48x48/chip.png</file>
<file alias="48x48/folder.png">icons/48x48/folder.png</file>
<file alias="48x48/list-add.png">icons/48x48/list-add.png</file>
<file alias="48x48/no_avatar.png">icons/48x48/no_avatar.png</file>
<file alias="48x48/sd_card.png">icons/48x48/sd_card.png</file>
<file alias="48x48/star.png">icons/48x48/star.png</file>
<file alias="256x256/plus_folder.png">icons/256x256/plus_folder.png</file>

View File

@@ -5,19 +5,9 @@ SPDX-License-Identifier: GPL-2.0-or-later
<RCC>
<qresource prefix="icons/colorful_dark">
<file alias="16x16/connected.png">../colorful/icons/16x16/connected.png</file>
<file alias="16x16/connected_notification.png">../colorful/icons/16x16/connected_notification.png</file>
<file alias="16x16/disconnected.png">../colorful/icons/16x16/disconnected.png</file>
<file alias="index.theme">icons/index.theme</file>
<file alias="16x16/lock.png">icons/16x16/lock.png</file>
<file alias="16x16/view-refresh.png">icons/16x16/view-refresh.png</file>
<file alias="48x48/bad_folder.png">../colorful/icons/48x48/bad_folder.png</file>
<file alias="48x48/chip.png">../colorful/icons/48x48/chip.png</file>
<file alias="48x48/folder.png">../colorful/icons/48x48/folder.png</file>
<file alias="48x48/no_avatar.png">../qdarkstyle/icons/48x48/no_avatar.png</file>
<file alias="48x48/list-add.png">../colorful/icons/48x48/list-add.png</file>
<file alias="48x48/sd_card.png">../colorful/icons/48x48/sd_card.png</file>
<file alias="256x256/plus_folder.png">../colorful/icons/256x256/plus_folder.png</file>
</qresource>
<qresource prefix="qss_icons">

View File

@@ -5,23 +5,20 @@ SPDX-License-Identifier: GPL-2.0-or-later
<RCC>
<qresource prefix="icons/default">
<!-- "colorful" is now the default theme, add new icons there -->
<file alias="index.theme">icons/index.theme</file>
<file alias="16x16/checked.png">icons/16x16/checked.png</file>
<file alias="16x16/failed.png">icons/16x16/failed.png</file>
<file alias="16x16/lock.png">icons/16x16/lock.png</file>
<file alias="16x16/connected.png">icons/16x16/connected.png</file>
<file alias="16x16/disconnected.png">icons/16x16/disconnected.png</file>
<file alias="16x16/connected_notification.png">icons/16x16/connected_notification.png</file>
<file alias="16x16/view-refresh.png">icons/16x16/view-refresh.png</file>
<file alias="16x16/disconnected.png">icons/16x16/disconnected.png</file>
<file alias="16x16/lock.png">icons/16x16/lock.png</file>
<file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file>
<file alias="48x48/chip.png">icons/48x48/chip.png</file>
<file alias="48x48/folder.png">icons/48x48/folder.png</file>
<file alias="48x48/no_avatar.png">icons/48x48/no_avatar.png</file>
<file alias="48x48/list-add.png">icons/48x48/list-add.png</file>
<file alias="48x48/sd_card.png">icons/48x48/sd_card.png</file>
<file alias="48x48/star.png">icons/48x48/star.png</file>
<file alias="256x256/yuzu.png">icons/256x256/yuzu.png</file>
<file alias="256x256/plus_folder.png">icons/256x256/plus_folder.png</file>
<file alias="256x256/yuzu.png">icons/256x256/yuzu.png</file>
</qresource>
<qresource prefix="default">
<file>style.qss</file>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 561 B

After

Width:  |  Height:  |  Size: 198 B

View File

@@ -1,6 +1,7 @@
[Icon Theme]
Name=default
Comment=default theme
Inherits=colorful
Directories=16x16,48x48,256x256
[16x16]
@@ -10,4 +11,4 @@ Size=16
Size=48
[256x256]
Size=256
Size=256

Binary file not shown.

Before

Width:  |  Height:  |  Size: 587 B

After

Width:  |  Height:  |  Size: 214 B

View File

@@ -1,7 +1,7 @@
[Icon Theme]
Name=qdarkstyle
Comment=dark theme
Inherits=default
Inherits=colorful
Directories=16x16,48x48,256x256
[16x16]
@@ -11,4 +11,4 @@ Size=16
Size=48
[256x256]
Size=256
Size=256

View File

@@ -1,7 +1,7 @@
[Icon Theme]
Name=qdarkstyle_midnight_blue
Comment=dark theme
Inherits=default
Inherits=colorful
Directories=16x16,48x48,256x256
[16x16]

View File

@@ -8,7 +8,7 @@
namespace AudioCore {
AudioCore::AudioCore(Core::System& system) : audio_manager{std::make_unique<AudioManager>(system)} {
AudioCore::AudioCore(Core::System& system) : audio_manager{std::make_unique<AudioManager>()} {
CreateSinks();
// Must be created after the sinks
adsp = std::make_unique<AudioRenderer::ADSP::ADSP>(system, *output_sink);
@@ -47,16 +47,6 @@ AudioRenderer::ADSP::ADSP& AudioCore::GetADSP() {
return *adsp;
}
void AudioCore::PauseSinks(const bool pausing) const {
if (pausing) {
output_sink->PauseStreams();
input_sink->PauseStreams();
} else {
output_sink->UnpauseStreams();
input_sink->UnpauseStreams();
}
}
void AudioCore::SetNVDECActive(bool active) {
nvdec_active = active;
}

View File

@@ -17,7 +17,7 @@ namespace AudioCore {
class AudioManager;
/**
* Main audio class, sotred inside the core, and holding the audio manager, all sinks, and the ADSP.
* Main audio class, stored inside the core, and holding the audio manager, all sinks, and the ADSP.
*/
class AudioCore {
public:
@@ -57,14 +57,6 @@ public:
*/
AudioRenderer::ADSP::ADSP& GetADSP();
/**
* Pause the sink. Called from the core.
*
* @param pausing - Is this pause due to an actual pause, or shutdown?
* Unfortunately, shutdown also pauses streams, which can cause issues.
*/
void PauseSinks(bool pausing) const;
/**
* Toggle NVDEC state, used to avoid stall in playback.
*

View File

@@ -14,7 +14,7 @@ namespace AudioCore {
* Responsible for the input/output events, set by the stream backend when buffers are consumed, and
* waited on by the audio manager. These callbacks signal the game's events to keep the audio buffer
* recycling going.
* In a real Switch this is not a seprate class, and exists entirely within the audio manager.
* In a real Switch this is not a separate class, and exists entirely within the audio manager.
* On the Switch it's implemented more simply through a MultiWaitEventHolder, where it can
* wait on multiple events at once, and the events are not needed by the backend.
*/
@@ -81,7 +81,7 @@ public:
void ClearEvents();
private:
/// Lock, used bythe audio manager
/// Lock, used by the audio manager
std::mutex event_lock;
/// Array of events, one per system type (see Type), last event is used to terminate
std::array<std::atomic<bool>, 4> events_signalled;

View File

@@ -82,7 +82,7 @@ u32 Manager::GetDeviceNames(std::vector<AudioRenderer::AudioDevice::AudioDeviceN
auto input_devices{Sink::GetDeviceListForSink(Settings::values.sink_id.GetValue(), true)};
if (input_devices.size() > 1) {
names.push_back(AudioRenderer::AudioDevice::AudioDeviceName("Uac"));
names.emplace_back("Uac");
return 1;
}
return 0;

View File

@@ -59,9 +59,10 @@ public:
/**
* Get a list of audio in device names.
*
* @oaram names - Output container to write names to.
* @param max_count - Maximum numebr of deivce names to write. Unused
* @param names - Output container to write names to.
* @param max_count - Maximum number of device names to write. Unused
* @param filter - Should the list be filtered? Unused.
*
* @return Number of names written.
*/
u32 GetDeviceNames(std::vector<AudioRenderer::AudioDevice::AudioDeviceName>& names,

View File

@@ -1,14 +1,13 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "audio_core/audio_in_manager.h"
#include "audio_core/audio_manager.h"
#include "audio_core/audio_out_manager.h"
#include "core/core.h"
#include "core/hle/service/audio/errors.h"
namespace AudioCore {
AudioManager::AudioManager(Core::System& system_) : system{system_} {
AudioManager::AudioManager() {
thread = std::jthread([this]() { ThreadFunc(); });
}
@@ -27,7 +26,7 @@ Result AudioManager::SetOutManager(BufferEventFunc buffer_func) {
const auto index{events.GetManagerIndex(Event::Type::AudioOutManager)};
if (buffer_events[index] == nullptr) {
buffer_events[index] = buffer_func;
buffer_events[index] = std::move(buffer_func);
needs_update = true;
events.SetAudioEvent(Event::Type::AudioOutManager, true);
}
@@ -43,7 +42,7 @@ Result AudioManager::SetInManager(BufferEventFunc buffer_func) {
const auto index{events.GetManagerIndex(Event::Type::AudioInManager)};
if (buffer_events[index] == nullptr) {
buffer_events[index] = buffer_func;
buffer_events[index] = std::move(buffer_func);
needs_update = true;
events.SetAudioEvent(Event::Type::AudioInManager, true);
}
@@ -60,19 +59,21 @@ void AudioManager::ThreadFunc() {
running = true;
while (running) {
auto timed_out{events.Wait(l, std::chrono::seconds(2))};
const auto timed_out{events.Wait(l, std::chrono::seconds(2))};
if (events.CheckAudioEventSet(Event::Type::Max)) {
break;
}
for (size_t i = 0; i < buffer_events.size(); i++) {
if (events.CheckAudioEventSet(Event::Type(i)) || timed_out) {
const auto event_type = static_cast<Event::Type>(i);
if (events.CheckAudioEventSet(event_type) || timed_out) {
if (buffer_events[i]) {
buffer_events[i]();
}
}
events.SetAudioEvent(Event::Type(i), false);
events.SetAudioEvent(event_type, false);
}
}
}

View File

@@ -10,22 +10,11 @@
#include <thread>
#include "audio_core/audio_event.h"
#include "core/hle/service/audio/errors.h"
namespace Core {
class System;
}
union Result;
namespace AudioCore {
namespace AudioOut {
class Manager;
}
namespace AudioIn {
class Manager;
}
/**
* The AudioManager's main purpose is to wait for buffer events for the audio in and out managers,
* and call an associated callback to release buffers.
@@ -43,7 +32,7 @@ class AudioManager {
using BufferEventFunc = std::function<void()>;
public:
explicit AudioManager(Core::System& system);
explicit AudioManager();
/**
* Shutdown the audio manager.
@@ -76,14 +65,10 @@ public:
private:
/**
* Main thread, waiting on a manager signal and calling the registered fucntion.
* Main thread, waiting on a manager signal and calling the registered function.
*/
void ThreadFunc();
/// Core system
Core::System& system;
/// Have sessions started palying?
bool sessions_started{};
/// Is the main thread running?
std::atomic<bool> running{};
/// Unused

View File

@@ -74,7 +74,7 @@ void Manager::BufferReleaseAndRegister() {
u32 Manager::GetAudioOutDeviceNames(
std::vector<AudioRenderer::AudioDevice::AudioDeviceName>& names) const {
names.push_back({"DeviceOut"});
names.emplace_back("DeviceOut");
return 1;
}

View File

@@ -25,8 +25,8 @@ SystemManager& Manager::GetSystemManager() {
return *system_manager;
}
auto Manager::GetWorkBufferSize(const AudioRendererParameterInternal& params, u64& out_count)
-> Result {
Result Manager::GetWorkBufferSize(const AudioRendererParameterInternal& params,
u64& out_count) const {
if (!CheckValidRevision(params.revision)) {
return Service::Audio::ERR_INVALID_REVISION;
}
@@ -54,7 +54,7 @@ void Manager::ReleaseSessionId(const s32 session_id) {
session_ids[--session_count] = session_id;
}
u32 Manager::GetSessionCount() {
u32 Manager::GetSessionCount() const {
std::scoped_lock l{session_lock};
return session_count;
}

View File

@@ -46,7 +46,7 @@ public:
* @param out_count - Output size of the required workbuffer.
* @return Result code.
*/
Result GetWorkBufferSize(const AudioRendererParameterInternal& params, u64& out_count);
Result GetWorkBufferSize(const AudioRendererParameterInternal& params, u64& out_count) const;
/**
* Get a new session id.
@@ -60,14 +60,14 @@ public:
*
* @return The number of active sessions.
*/
u32 GetSessionCount();
u32 GetSessionCount() const;
/**
* Add a renderer system to the manager.
* The system will be reguarly called to generate commands for the AudioRenderer.
* The system will be regularly called to generate commands for the AudioRenderer.
*
* @param system - The system to add.
* @return True if the system was sucessfully added, otherwise false.
* @return True if the system was successfully added, otherwise false.
*/
bool AddSystem(System& system);
@@ -75,7 +75,7 @@ public:
* Remove a renderer system from the manager.
*
* @param system - The system to remove.
* @return True if the system was sucessfully removed, otherwise false.
* @return True if the system was successfully removed, otherwise false.
*/
bool RemoveSystem(System& system);
@@ -94,7 +94,7 @@ private:
/// Number of active renderers
u32 session_count{};
/// Lock for interacting with the sessions
std::mutex session_lock{};
mutable std::mutex session_lock{};
/// Regularly generates commands from the registered systems for the AudioRenderer
std::unique_ptr<SystemManager> system_manager{};
};

View File

@@ -36,7 +36,7 @@ public:
*
* @param buffer - The new buffer.
*/
void AppendBuffer(AudioBuffer& buffer) {
void AppendBuffer(const AudioBuffer& buffer) {
std::scoped_lock l{lock};
buffers[appended_index] = buffer;
appended_count++;
@@ -88,10 +88,12 @@ public:
/**
* Release all registered buffers.
*
* @param timestamp - The released timestamp for this buffer.
* @param core_timing - The CoreTiming instance
* @param session - The device session
*
* @return Is the buffer was released.
*/
bool ReleaseBuffers(Core::Timing::CoreTiming& core_timing, DeviceSession& session) {
bool ReleaseBuffers(const Core::Timing::CoreTiming& core_timing, const DeviceSession& session) {
std::scoped_lock l{lock};
bool buffer_released{false};
while (registered_count > 0) {

View File

@@ -73,12 +73,12 @@ void DeviceSession::Stop() {
}
}
void DeviceSession::AppendBuffers(std::span<AudioBuffer> buffers) const {
for (size_t i = 0; i < buffers.size(); i++) {
void DeviceSession::AppendBuffers(std::span<const AudioBuffer> buffers) const {
for (const auto& buffer : buffers) {
Sink::SinkBuffer new_buffer{
.frames = buffers[i].size / (channel_count * sizeof(s16)),
.frames = buffer.size / (channel_count * sizeof(s16)),
.frames_played = 0,
.tag = buffers[i].tag,
.tag = buffer.tag,
.consumed = false,
};
@@ -86,21 +86,21 @@ void DeviceSession::AppendBuffers(std::span<AudioBuffer> buffers) const {
std::vector<s16> samples{};
stream->AppendBuffer(new_buffer, samples);
} else {
std::vector<s16> samples(buffers[i].size / sizeof(s16));
system.Memory().ReadBlockUnsafe(buffers[i].samples, samples.data(), buffers[i].size);
std::vector<s16> samples(buffer.size / sizeof(s16));
system.Memory().ReadBlockUnsafe(buffer.samples, samples.data(), buffer.size);
stream->AppendBuffer(new_buffer, samples);
}
}
}
void DeviceSession::ReleaseBuffer(AudioBuffer& buffer) const {
void DeviceSession::ReleaseBuffer(const AudioBuffer& buffer) const {
if (type == Sink::StreamType::In) {
auto samples{stream->ReleaseBuffer(buffer.size / sizeof(s16))};
system.Memory().WriteBlockUnsafe(buffer.samples, samples.data(), buffer.size);
}
}
bool DeviceSession::IsBufferConsumed(AudioBuffer& buffer) const {
bool DeviceSession::IsBufferConsumed(const AudioBuffer& buffer) const {
return played_sample_count >= buffer.end_timestamp;
}

View File

@@ -62,22 +62,23 @@ public:
*
* @param buffers - The buffers to play.
*/
void AppendBuffers(std::span<AudioBuffer> buffers) const;
void AppendBuffers(std::span<const AudioBuffer> buffers) const;
/**
* (Audio In only) Pop samples from the backend, and write them back to this buffer's address.
*
* @param buffer - The buffer to write to.
*/
void ReleaseBuffer(AudioBuffer& buffer) const;
void ReleaseBuffer(const AudioBuffer& buffer) const;
/**
* Check if the buffer for the given tag has been consumed by the backend.
*
* @param tag - Unqiue tag of the buffer to check.
* @param buffer - the buffer to check.
*
* @return true if the buffer has been consumed, otherwise false.
*/
bool IsBufferConsumed(AudioBuffer& buffer) const;
bool IsBufferConsumed(const AudioBuffer& buffer) const;
/**
* Start this device session, starting the backend stream.

View File

@@ -72,7 +72,7 @@ Kernel::KReadableEvent& In::GetBufferEvent() {
return event->GetReadableEvent();
}
f32 In::GetVolume() {
f32 In::GetVolume() const {
std::scoped_lock l{parent_mutex};
return system.GetVolume();
}
@@ -82,17 +82,17 @@ void In::SetVolume(f32 volume) {
system.SetVolume(volume);
}
bool In::ContainsAudioBuffer(u64 tag) {
bool In::ContainsAudioBuffer(u64 tag) const {
std::scoped_lock l{parent_mutex};
return system.ContainsAudioBuffer(tag);
}
u32 In::GetBufferCount() {
u32 In::GetBufferCount() const {
std::scoped_lock l{parent_mutex};
return system.GetBufferCount();
}
u64 In::GetPlayedSampleCount() {
u64 In::GetPlayedSampleCount() const {
std::scoped_lock l{parent_mutex};
return system.GetPlayedSampleCount();
}

View File

@@ -102,7 +102,7 @@ public:
*
* @return The current volume.
*/
f32 GetVolume();
f32 GetVolume() const;
/**
* Set the system volume.
@@ -117,21 +117,21 @@ public:
* @param tag - The tag to search for.
* @return True if the buffer is in the system, otherwise false.
*/
bool ContainsAudioBuffer(u64 tag);
bool ContainsAudioBuffer(u64 tag) const;
/**
* Get the maximum number of buffers.
*
* @return The maximum number of buffers.
*/
u32 GetBufferCount();
u32 GetBufferCount() const;
/**
* Get the total played sample count for this audio in.
*
* @return The played sample count.
*/
u64 GetPlayedSampleCount();
u64 GetPlayedSampleCount() const;
private:
/// The AudioIn::Manager this audio in is registered with

View File

@@ -34,16 +34,16 @@ size_t System::GetSessionId() const {
return session_id;
}
std::string_view System::GetDefaultDeviceName() {
std::string_view System::GetDefaultDeviceName() const {
return "BuiltInHeadset";
}
std::string_view System::GetDefaultUacDeviceName() {
std::string_view System::GetDefaultUacDeviceName() const {
return "Uac";
}
Result System::IsConfigValid(const std::string_view device_name,
const AudioInParameter& in_params) {
const AudioInParameter& in_params) const {
if ((device_name.size() > 0) &&
(device_name != GetDefaultDeviceName() && device_name != GetDefaultUacDeviceName())) {
return Service::Audio::ERR_INVALID_DEVICE_NAME;
@@ -114,12 +114,14 @@ bool System::AppendBuffer(const AudioInBuffer& buffer, const u64 tag) {
}
const auto timestamp{buffers.GetNextTimestamp()};
AudioBuffer new_buffer{.start_timestamp = timestamp,
.end_timestamp = timestamp + buffer.size / (channel_count * sizeof(s16)),
.played_timestamp = 0,
.samples = buffer.samples,
.tag = tag,
.size = buffer.size};
const AudioBuffer new_buffer{
.start_timestamp = timestamp,
.end_timestamp = timestamp + buffer.size / (channel_count * sizeof(s16)),
.played_timestamp = 0,
.samples = buffer.samples,
.tag = tag,
.size = buffer.size,
};
buffers.AppendBuffer(new_buffer);
RegisterBuffers();
@@ -200,11 +202,11 @@ void System::SetVolume(const f32 volume_) {
session->SetVolume(volume_);
}
bool System::ContainsAudioBuffer(const u64 tag) {
bool System::ContainsAudioBuffer(const u64 tag) const {
return buffers.ContainsBuffer(tag);
}
u32 System::GetBufferCount() {
u32 System::GetBufferCount() const {
return buffers.GetAppendedRegisteredCount();
}

View File

@@ -68,7 +68,7 @@ public:
*
* @return The default audio input device name.
*/
std::string_view GetDefaultDeviceName();
std::string_view GetDefaultDeviceName() const;
/**
* Get the default USB audio input device name.
@@ -77,7 +77,7 @@ public:
*
* @return The default USB audio input device name.
*/
std::string_view GetDefaultUacDeviceName();
std::string_view GetDefaultUacDeviceName() const;
/**
* Is the given initialize config valid?
@@ -86,7 +86,7 @@ public:
* @param in_params - Input parameters, see AudioInParameter.
* @return Result code.
*/
Result IsConfigValid(std::string_view device_name, const AudioInParameter& in_params);
Result IsConfigValid(std::string_view device_name, const AudioInParameter& in_params) const;
/**
* Initialize this system.
@@ -208,7 +208,7 @@ public:
/**
* Set this system's current volume.
*
* @param The new volume.
* @param volume The new volume.
*/
void SetVolume(f32 volume);
@@ -218,14 +218,14 @@ public:
* @param tag - Unique tag to search for.
* @return True if the buffer is in the system, otherwise false.
*/
bool ContainsAudioBuffer(u64 tag);
bool ContainsAudioBuffer(u64 tag) const;
/**
* Get the maximum number of usable buffers (default 32).
*
* @return The number of buffers.
*/
u32 GetBufferCount();
u32 GetBufferCount() const;
/**
* Get the total number of samples played by this system.

View File

@@ -72,7 +72,7 @@ Kernel::KReadableEvent& Out::GetBufferEvent() {
return event->GetReadableEvent();
}
f32 Out::GetVolume() {
f32 Out::GetVolume() const {
std::scoped_lock l{parent_mutex};
return system.GetVolume();
}
@@ -82,17 +82,17 @@ void Out::SetVolume(const f32 volume) {
system.SetVolume(volume);
}
bool Out::ContainsAudioBuffer(const u64 tag) {
bool Out::ContainsAudioBuffer(const u64 tag) const {
std::scoped_lock l{parent_mutex};
return system.ContainsAudioBuffer(tag);
}
u32 Out::GetBufferCount() {
u32 Out::GetBufferCount() const {
std::scoped_lock l{parent_mutex};
return system.GetBufferCount();
}
u64 Out::GetPlayedSampleCount() {
u64 Out::GetPlayedSampleCount() const {
std::scoped_lock l{parent_mutex};
return system.GetPlayedSampleCount();
}

View File

@@ -102,7 +102,7 @@ public:
*
* @return The current volume.
*/
f32 GetVolume();
f32 GetVolume() const;
/**
* Set the system volume.
@@ -117,21 +117,21 @@ public:
* @param tag - The tag to search for.
* @return True if the buffer is in the system, otherwise false.
*/
bool ContainsAudioBuffer(u64 tag);
bool ContainsAudioBuffer(u64 tag) const;
/**
* Get the maximum number of buffers.
*
* @return The maximum number of buffers.
*/
u32 GetBufferCount();
u32 GetBufferCount() const;
/**
* Get the total played sample count for this audio out.
*
* @return The played sample count.
*/
u64 GetPlayedSampleCount();
u64 GetPlayedSampleCount() const;
private:
/// The AudioOut::Manager this audio out is registered with

View File

@@ -27,11 +27,12 @@ void System::Finalize() {
buffer_event->GetWritableEvent().Signal();
}
std::string_view System::GetDefaultOutputDeviceName() {
std::string_view System::GetDefaultOutputDeviceName() const {
return "DeviceOut";
}
Result System::IsConfigValid(std::string_view device_name, const AudioOutParameter& in_params) {
Result System::IsConfigValid(std::string_view device_name,
const AudioOutParameter& in_params) const {
if ((device_name.size() > 0) && (device_name != GetDefaultOutputDeviceName())) {
return Service::Audio::ERR_INVALID_DEVICE_NAME;
}
@@ -113,12 +114,14 @@ bool System::AppendBuffer(const AudioOutBuffer& buffer, u64 tag) {
}
const auto timestamp{buffers.GetNextTimestamp()};
AudioBuffer new_buffer{.start_timestamp = timestamp,
.end_timestamp = timestamp + buffer.size / (channel_count * sizeof(s16)),
.played_timestamp = 0,
.samples = buffer.samples,
.tag = tag,
.size = buffer.size};
const AudioBuffer new_buffer{
.start_timestamp = timestamp,
.end_timestamp = timestamp + buffer.size / (channel_count * sizeof(s16)),
.played_timestamp = 0,
.samples = buffer.samples,
.tag = tag,
.size = buffer.size,
};
buffers.AppendBuffer(new_buffer);
RegisterBuffers();
@@ -198,11 +201,11 @@ void System::SetVolume(const f32 volume_) {
session->SetVolume(volume_);
}
bool System::ContainsAudioBuffer(const u64 tag) {
bool System::ContainsAudioBuffer(const u64 tag) const {
return buffers.ContainsBuffer(tag);
}
u32 System::GetBufferCount() {
u32 System::GetBufferCount() const {
return buffers.GetAppendedRegisteredCount();
}

View File

@@ -68,7 +68,7 @@ public:
*
* @return The default audio output device name.
*/
std::string_view GetDefaultOutputDeviceName();
std::string_view GetDefaultOutputDeviceName() const;
/**
* Is the given initialize config valid?
@@ -77,7 +77,7 @@ public:
* @param in_params - Input parameters, see AudioOutParameter.
* @return Result code.
*/
Result IsConfigValid(std::string_view device_name, const AudioOutParameter& in_params);
Result IsConfigValid(std::string_view device_name, const AudioOutParameter& in_params) const;
/**
* Initialize this system.
@@ -199,7 +199,7 @@ public:
/**
* Set this system's current volume.
*
* @param The new volume.
* @param volume The new volume.
*/
void SetVolume(f32 volume);
@@ -209,14 +209,14 @@ public:
* @param tag - Unique tag to search for.
* @return True if the buffer is in the system, otherwise false.
*/
bool ContainsAudioBuffer(u64 tag);
bool ContainsAudioBuffer(u64 tag) const;
/**
* Get the maximum number of usable buffers (default 32).
*
* @return The number of buffers.
*/
u32 GetBufferCount();
u32 GetBufferCount() const;
/**
* Get the total number of samples played by this system.

View File

@@ -50,7 +50,7 @@ u32 ADSP::GetRemainCommandCount(const u32 session_id) const {
return render_mailbox.GetRemainCommandCount(session_id);
}
void ADSP::SendCommandBuffer(const u32 session_id, CommandBuffer& command_buffer) {
void ADSP::SendCommandBuffer(const u32 session_id, const CommandBuffer& command_buffer) {
render_mailbox.SetCommandBuffer(session_id, command_buffer);
}

View File

@@ -63,8 +63,6 @@ public:
/**
* Stop the ADSP.
*
* @return True if started or already running, otherwise false.
*/
void Stop();
@@ -133,7 +131,7 @@ public:
* @param session_id - The session id to check (0 or 1).
* @param command_buffer - The command buffer to process.
*/
void SendCommandBuffer(u32 session_id, CommandBuffer& command_buffer);
void SendCommandBuffer(u32 session_id, const CommandBuffer& command_buffer);
/**
* Clear the command buffers (does not clear the time taken or the remaining command count)

View File

@@ -47,11 +47,11 @@ RenderMessage AudioRenderer_Mailbox::ADSPWaitMessage() {
return msg;
}
CommandBuffer& AudioRenderer_Mailbox::GetCommandBuffer(const s32 session_id) {
CommandBuffer& AudioRenderer_Mailbox::GetCommandBuffer(const u32 session_id) {
return command_buffers[session_id];
}
void AudioRenderer_Mailbox::SetCommandBuffer(const u32 session_id, CommandBuffer& buffer) {
void AudioRenderer_Mailbox::SetCommandBuffer(const u32 session_id, const CommandBuffer& buffer) {
command_buffers[session_id] = buffer;
}

View File

@@ -52,7 +52,7 @@ public:
/**
* Send a message from the host to the AudioRenderer.
*
* @param message_ - The message to send to the AudioRenderer.
* @param message - The message to send to the AudioRenderer.
*/
void HostSendMessage(RenderMessage message);
@@ -66,7 +66,7 @@ public:
/**
* Send a message from the AudioRenderer to the host.
*
* @param message_ - The message to send to the host.
* @param message - The message to send to the host.
*/
void ADSPSendMessage(RenderMessage message);
@@ -83,7 +83,7 @@ public:
* @param session_id - The session id to get (0 or 1).
* @return The command buffer.
*/
CommandBuffer& GetCommandBuffer(s32 session_id);
CommandBuffer& GetCommandBuffer(u32 session_id);
/**
* Set the command buffer with the given session id (0 or 1).
@@ -91,7 +91,7 @@ public:
* @param session_id - The session id to get (0 or 1).
* @param buffer - The command buffer to set.
*/
void SetCommandBuffer(u32 session_id, CommandBuffer& buffer);
void SetCommandBuffer(u32 session_id, const CommandBuffer& buffer);
/**
* Get the total render time taken for the last command lists sent.
@@ -163,7 +163,7 @@ public:
/**
* Start the AudioRenderer.
*
* @param The mailbox to use for this session.
* @param mailbox The mailbox to use for this session.
*/
void Start(AudioRenderer_Mailbox* mailbox);

View File

@@ -33,10 +33,10 @@ public:
/**
* Initialize the processor.
*
* @param system_ - The core system.
* @param buffer - The command buffer to process.
* @param size - The size of the buffer.
* @param stream_ - The stream to be used for sending the samples.
* @param system - The core system.
* @param buffer - The command buffer to process.
* @param size - The size of the buffer.
* @param stream - The stream to be used for sending the samples.
*/
void Initialize(Core::System& system, CpuAddr buffer, u64 size, Sink::SinkStream* stream);
@@ -72,7 +72,8 @@ public:
/**
* Process the command list.
*
* @param index - Index of the current command list.
* @param session_id - Session ID for the commands being processed.
*
* @return The time taken to process.
*/
u64 Process(u32 session_id);
@@ -89,7 +90,7 @@ public:
u8* commands{};
/// The command buffer size
u64 commands_buffer_size{};
/// The maximum processing time alloted
/// The maximum processing time allotted
u64 max_process_time{};
/// The number of commands in the buffer
u32 command_count{};

View File

@@ -1,6 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <array>
#include <span>
#include "audio_core/audio_core.h"
#include "audio_core/common/feature_support.h"
#include "audio_core/renderer/audio_device.h"
@@ -9,14 +12,33 @@
namespace AudioCore::AudioRenderer {
constexpr std::array usb_device_names{
AudioDevice::AudioDeviceName{"AudioStereoJackOutput"},
AudioDevice::AudioDeviceName{"AudioBuiltInSpeakerOutput"},
AudioDevice::AudioDeviceName{"AudioTvOutput"},
AudioDevice::AudioDeviceName{"AudioUsbDeviceOutput"},
};
constexpr std::array device_names{
AudioDevice::AudioDeviceName{"AudioStereoJackOutput"},
AudioDevice::AudioDeviceName{"AudioBuiltInSpeakerOutput"},
AudioDevice::AudioDeviceName{"AudioTvOutput"},
};
constexpr std::array output_device_names{
AudioDevice::AudioDeviceName{"AudioBuiltInSpeakerOutput"},
AudioDevice::AudioDeviceName{"AudioTvOutput"},
AudioDevice::AudioDeviceName{"AudioExternalOutput"},
};
AudioDevice::AudioDevice(Core::System& system, const u64 applet_resource_user_id_,
const u32 revision)
: output_sink{system.AudioCore().GetOutputSink()},
applet_resource_user_id{applet_resource_user_id_}, user_revision{revision} {}
u32 AudioDevice::ListAudioDeviceName(std::vector<AudioDeviceName>& out_buffer,
const size_t max_count) {
std::span<AudioDeviceName> names{};
const size_t max_count) const {
std::span<const AudioDeviceName> names{};
if (CheckFeatureSupported(SupportTags::AudioUsbDeviceOutput, user_revision)) {
names = usb_device_names;
@@ -24,7 +46,7 @@ u32 AudioDevice::ListAudioDeviceName(std::vector<AudioDeviceName>& out_buffer,
names = device_names;
}
u32 out_count{static_cast<u32>(std::min(max_count, names.size()))};
const u32 out_count{static_cast<u32>(std::min(max_count, names.size()))};
for (u32 i = 0; i < out_count; i++) {
out_buffer.push_back(names[i]);
}
@@ -32,8 +54,8 @@ u32 AudioDevice::ListAudioDeviceName(std::vector<AudioDeviceName>& out_buffer,
}
u32 AudioDevice::ListAudioOutputDeviceName(std::vector<AudioDeviceName>& out_buffer,
const size_t max_count) {
u32 out_count{static_cast<u32>(std::min(max_count, output_device_names.size()))};
const size_t max_count) const {
const u32 out_count{static_cast<u32>(std::min(max_count, output_device_names.size()))};
for (u32 i = 0; i < out_count; i++) {
out_buffer.push_back(output_device_names[i]);
@@ -45,7 +67,7 @@ void AudioDevice::SetDeviceVolumes(const f32 volume) {
output_sink.SetDeviceVolume(volume);
}
f32 AudioDevice::GetDeviceVolume([[maybe_unused]] std::string_view name) {
f32 AudioDevice::GetDeviceVolume([[maybe_unused]] std::string_view name) const {
return output_sink.GetDeviceVolume();
}

View File

@@ -3,7 +3,7 @@
#pragma once
#include <span>
#include <string_view>
#include "audio_core/audio_render_manager.h"
@@ -23,21 +23,13 @@ namespace AudioRenderer {
class AudioDevice {
public:
struct AudioDeviceName {
std::array<char, 0x100> name;
std::array<char, 0x100> name{};
AudioDeviceName(const char* name_) {
std::strncpy(name.data(), name_, name.size());
constexpr AudioDeviceName(std::string_view name_) {
name_.copy(name.data(), name.size() - 1);
}
};
std::array<AudioDeviceName, 4> usb_device_names{"AudioStereoJackOutput",
"AudioBuiltInSpeakerOutput", "AudioTvOutput",
"AudioUsbDeviceOutput"};
std::array<AudioDeviceName, 3> device_names{"AudioStereoJackOutput",
"AudioBuiltInSpeakerOutput", "AudioTvOutput"};
std::array<AudioDeviceName, 3> output_device_names{"AudioBuiltInSpeakerOutput", "AudioTvOutput",
"AudioExternalOutput"};
explicit AudioDevice(Core::System& system, u64 applet_resource_user_id, u32 revision);
/**
@@ -47,7 +39,7 @@ public:
* @param max_count - Maximum number of devices to write (count of out_buffer).
* @return Number of device names written.
*/
u32 ListAudioDeviceName(std::vector<AudioDeviceName>& out_buffer, size_t max_count);
u32 ListAudioDeviceName(std::vector<AudioDeviceName>& out_buffer, size_t max_count) const;
/**
* Get a list of the available output devices.
@@ -57,7 +49,7 @@ public:
* @param max_count - Maximum number of devices to write (count of out_buffer).
* @return Number of device names written.
*/
u32 ListAudioOutputDeviceName(std::vector<AudioDeviceName>& out_buffer, size_t max_count);
u32 ListAudioOutputDeviceName(std::vector<AudioDeviceName>& out_buffer, size_t max_count) const;
/**
* Set the volume of all streams in the backend sink.
@@ -73,7 +65,7 @@ public:
* @param name - Name of the device to check. Unused.
* @return Volume of the device.
*/
f32 GetDeviceVolume(std::string_view name);
f32 GetDeviceVolume(std::string_view name) const;
private:
/// Backend output sink for the device

View File

@@ -34,7 +34,7 @@ void BehaviorInfo::ClearError() {
error_count = 0;
}
void BehaviorInfo::AppendError(ErrorInfo& error) {
void BehaviorInfo::AppendError(const ErrorInfo& error) {
LOG_ERROR(Service_Audio, "Error during RequestUpdate, reporting code {:04X} address {:08X}",
error.error_code.raw, error.address);
if (error_count < MaxErrors) {
@@ -42,7 +42,7 @@ void BehaviorInfo::AppendError(ErrorInfo& error) {
}
}
void BehaviorInfo::CopyErrorInfo(std::span<ErrorInfo> out_errors, u32& out_count) {
void BehaviorInfo::CopyErrorInfo(std::span<ErrorInfo> out_errors, u32& out_count) const {
out_count = std::min(error_count, MaxErrors);
for (size_t i = 0; i < MaxErrors; i++) {

View File

@@ -94,7 +94,7 @@ public:
*
* @param error - The new error.
*/
void AppendError(ErrorInfo& error);
void AppendError(const ErrorInfo& error);
/**
* Copy errors to the given output container.
@@ -102,7 +102,7 @@ public:
* @param out_errors - Output container to receive the errors.
* @param out_count - The number of errors written.
*/
void CopyErrorInfo(std::span<ErrorInfo> out_errors, u32& out_count);
void CopyErrorInfo(std::span<ErrorInfo> out_errors, u32& out_count) const;
/**
* Update the behaviour flags.

View File

@@ -485,7 +485,7 @@ Result InfoUpdater::UpdateBehaviorInfo(BehaviorInfo& behaviour_) {
return ResultSuccess;
}
Result InfoUpdater::UpdateErrorInfo(BehaviorInfo& behaviour_) {
Result InfoUpdater::UpdateErrorInfo(const BehaviorInfo& behaviour_) {
auto out_params{reinterpret_cast<BehaviorInfo::OutStatus*>(output)};
behaviour_.CopyErrorInfo(out_params->errors, out_params->error_count);

View File

@@ -130,7 +130,7 @@ public:
* @param behaviour - Behaviour to update.
* @return Result code.
*/
Result UpdateErrorInfo(BehaviorInfo& behaviour);
Result UpdateErrorInfo(const BehaviorInfo& behaviour);
/**
* Update splitter.

View File

@@ -191,6 +191,7 @@ public:
* @param volume - Current mix volume used for calculating the ramp.
* @param prev_volume - Previous mix volume, used for calculating the ramp,
* also applied to the input.
* @param prev_samples - Previous sample buffer. Used for depopping.
* @param precision - Number of decimal bits for fixed point operations.
*/
void GenerateMixRampCommand(s32 node_id, s16 buffer_count, s16 input_index, s16 output_index,
@@ -208,6 +209,7 @@ public:
* @param volumes - Current mix volumes used for calculating the ramp.
* @param prev_volumes - Previous mix volumes, used for calculating the ramp,
* also applied to the input.
* @param prev_samples - Previous sample buffer. Used for depopping.
* @param precision - Number of decimal bits for fixed point operations.
*/
void GenerateMixRampGroupedCommand(s32 node_id, s16 buffer_count, s16 input_index,
@@ -297,11 +299,11 @@ public:
/**
* Generate a device sink command, adding it to the command list.
*
* @param node_id - Node id of the voice this command is generated for.
* @param buffer_offset - Base mix buffer offset to use.
* @param sink_info - The sink_info to generate this command from.
* @session_id - System session id this command is generated from.
* @samples_buffer - The buffer to be sent to the sink if upsampling is not used.
* @param node_id - Node id of the voice this command is generated for.
* @param buffer_offset - Base mix buffer offset to use.
* @param sink_info - The sink_info to generate this command from.
* @param session_id - System session id this command is generated from.
* @param samples_buffer - The buffer to be sent to the sink if upsampling is not used.
*/
void GenerateDeviceSinkCommand(s32 node_id, s16 buffer_offset, SinkInfoBase& sink_info,
u32 session_id, std::span<s32> samples_buffer);

View File

@@ -197,9 +197,9 @@ public:
/**
* Generate an I3DL2 reverb effect command.
*
* @param buffer_offset - Base mix buffer offset to use.
* @param effect_info_base - I3DL2Reverb effect info.
* @param node_id - Node id of the mix this command is generated for.
* @param buffer_offset - Base mix buffer offset to use.
* @param effect_info - I3DL2Reverb effect info.
* @param node_id - Node id of the mix this command is generated for.
*/
void GenerateI3dl2ReverbEffectCommand(s16 buffer_offset, EffectInfoBase& effect_info,
s32 node_id);
@@ -207,18 +207,18 @@ public:
/**
* Generate an aux effect command.
*
* @param buffer_offset - Base mix buffer offset to use.
* @param effect_info_base - Aux effect info.
* @param node_id - Node id of the mix this command is generated for.
* @param buffer_offset - Base mix buffer offset to use.
* @param effect_info - Aux effect info.
* @param node_id - Node id of the mix this command is generated for.
*/
void GenerateAuxCommand(s16 buffer_offset, EffectInfoBase& effect_info, s32 node_id);
/**
* Generate a biquad filter effect command.
*
* @param buffer_offset - Base mix buffer offset to use.
* @param effect_info_base - Aux effect info.
* @param node_id - Node id of the mix this command is generated for.
* @param buffer_offset - Base mix buffer offset to use.
* @param effect_info - Aux effect info.
* @param node_id - Node id of the mix this command is generated for.
*/
void GenerateBiquadFilterEffectCommand(s16 buffer_offset, EffectInfoBase& effect_info,
s32 node_id);
@@ -226,10 +226,10 @@ public:
/**
* Generate a light limiter effect command.
*
* @param buffer_offset - Base mix buffer offset to use.
* @param effect_info_base - Limiter effect info.
* @param node_id - Node id of the mix this command is generated for.
* @param effect_index - Index for the statistics state.
* @param buffer_offset - Base mix buffer offset to use.
* @param effect_info - Limiter effect info.
* @param node_id - Node id of the mix this command is generated for.
* @param effect_index - Index for the statistics state.
*/
void GenerateLightLimiterEffectCommand(s16 buffer_offset, EffectInfoBase& effect_info,
s32 node_id, u32 effect_index);
@@ -238,21 +238,20 @@ public:
* Generate a capture effect command.
* Writes a mix buffer back to game memory.
*
* @param buffer_offset - Base mix buffer offset to use.
* @param effect_info_base - Capture effect info.
* @param node_id - Node id of the mix this command is generated for.
* @param buffer_offset - Base mix buffer offset to use.
* @param effect_info - Capture effect info.
* @param node_id - Node id of the mix this command is generated for.
*/
void GenerateCaptureCommand(s16 buffer_offset, EffectInfoBase& effect_info, s32 node_id);
/**
* Generate a compressor effect command.
*
* @param buffer_offset - Base mix buffer offset to use.
* @param effect_info_base - Compressor effect info.
* @param node_id - Node id of the mix this command is generated for.
* @param buffer_offset - Base mix buffer offset to use.
* @param effect_info - Compressor effect info.
* @param node_id - Node id of the mix this command is generated for.
*/
void GenerateCompressorCommand(const s16 buffer_offset, EffectInfoBase& effect_info,
const s32 node_id);
void GenerateCompressorCommand(s16 buffer_offset, EffectInfoBase& effect_info, s32 node_id);
/**
* Generate all effect commands for a mix.
@@ -318,8 +317,9 @@ public:
* Generate a performance command.
* Used to report performance metrics of the AudioRenderer back to the game.
*
* @param buffer_offset - Base mix buffer offset to use.
* @param sink_info - Sink info to generate the commands from.
* @param node_id - Node ID of the mix this command is generated for
* @param state - Output state of the generated performance command
* @param entry_addresses - Addresses to be written
*/
void GeneratePerformanceCommand(s32 node_id, PerformanceState state,
const PerformanceEntryAddresses& entry_addresses);

View File

@@ -7,17 +7,7 @@
#include "common/logging/log.h"
namespace AudioCore::AudioRenderer {
/**
* Mix input mix buffer into output mix buffer, with volume applied to the input.
*
* @tparam Q - Number of bits for fixed point operations.
* @param output - Output mix buffer.
* @param input - Input mix buffer.
* @param volume - Volume applied to the input.
* @param ramp - Ramp applied to volume every sample.
* @param sample_count - Number of samples to process.
* @return The final gained input sample, used for depopping.
*/
template <size_t Q>
s32 ApplyMixRamp(std::span<s32> output, std::span<const s32> input, const f32 volume_,
const f32 ramp_, const u32 sample_count) {
@@ -40,10 +30,8 @@ s32 ApplyMixRamp(std::span<s32> output, std::span<const s32> input, const f32 vo
return sample.to_int();
}
template s32 ApplyMixRamp<15>(std::span<s32>, std::span<const s32>, const f32, const f32,
const u32);
template s32 ApplyMixRamp<23>(std::span<s32>, std::span<const s32>, const f32, const f32,
const u32);
template s32 ApplyMixRamp<15>(std::span<s32>, std::span<const s32>, f32, f32, u32);
template s32 ApplyMixRamp<23>(std::span<s32>, std::span<const s32>, f32, f32, u32);
void MixRampCommand::Dump(const ADSP::CommandListProcessor& processor, std::string& string) {
const auto ramp{(volume - prev_volume) / static_cast<f32>(processor.sample_count)};

View File

@@ -61,13 +61,13 @@ struct MixRampCommand : ICommand {
* @tparam Q - Number of bits for fixed point operations.
* @param output - Output mix buffer.
* @param input - Input mix buffer.
* @param volume - Volume applied to the input.
* @param ramp - Ramp applied to volume every sample.
* @param volume_ - Volume applied to the input.
* @param ramp_ - Ramp applied to volume every sample.
* @param sample_count - Number of samples to process.
* @return The final gained input sample, used for depopping.
*/
template <size_t Q>
s32 ApplyMixRamp(std::span<s32> output, std::span<const s32> input, const f32 volume_,
const f32 ramp_, const u32 sample_count);
s32 ApplyMixRamp(std::span<s32> output, std::span<const s32> input, f32 volume_, f32 ramp_,
u32 sample_count);
} // namespace AudioCore::AudioRenderer

View File

@@ -50,9 +50,9 @@ struct MixRampGroupedCommand : ICommand {
std::array<s16, MaxMixBuffers> inputs;
/// Output mix buffer indexes for each mix buffer
std::array<s16, MaxMixBuffers> outputs;
/// Previous mix vloumes for each mix buffer
/// Previous mix volumes for each mix buffer
std::array<f32, MaxMixBuffers> prev_volumes;
/// Current mix vloumes for each mix buffer
/// Current mix volumes for each mix buffer
std::array<f32, MaxMixBuffers> volumes;
/// Pointer to the previous sample buffer, used for depop
CpuAddr previous_samples;

View File

@@ -15,15 +15,15 @@ class EffectContext {
public:
/**
* Initialize the effect context
* @param effect_infos List of effect infos for this context
* @param effect_count The number of effects in the list
* @param result_states_cpu The workbuffer of result states for the CPU for this context
* @param result_states_dsp The workbuffer of result states for the DSP for this context
* @param state_count The number of result states
* @param effect_infos_ - List of effect infos for this context
* @param effect_count_ - The number of effects in the list
* @param result_states_cpu_ - The workbuffer of result states for the CPU for this context
* @param result_states_dsp_ - The workbuffer of result states for the DSP for this context
* @param dsp_state_count - The number of result states
*/
void Initialize(std::span<EffectInfoBase> effect_infos_, const u32 effect_count_,
void Initialize(std::span<EffectInfoBase> effect_infos_, u32 effect_count_,
std::span<EffectResultState> result_states_cpu_,
std::span<EffectResultState> result_states_dsp_, const size_t dsp_state_count);
std::span<EffectResultState> result_states_dsp_, size_t dsp_state_count);
/**
* Get the EffectInfo for a given index

View File

@@ -291,7 +291,7 @@ public:
* Update the info with new parameters, version 1.
*
* @param error_info - Used to write call result code.
* @param in_params - New parameters to update the info with.
* @param params - New parameters to update the info with.
* @param pool_mapper - Pool for mapping buffers.
*/
virtual void Update(BehaviorInfo::ErrorInfo& error_info,
@@ -305,7 +305,7 @@ public:
* Update the info with new parameters, version 2.
*
* @param error_info - Used to write call result code.
* @param in_params - New parameters to update the info with.
* @param params - New parameters to update the info with.
* @param pool_mapper - Pool for mapping buffers.
*/
virtual void Update(BehaviorInfo::ErrorInfo& error_info,

View File

@@ -99,7 +99,7 @@ public:
return out_sample;
}
Common::FixedPoint<50, 14> Read() {
Common::FixedPoint<50, 14> Read() const {
return *output;
}
@@ -110,7 +110,7 @@ public:
}
}
Common::FixedPoint<50, 14> TapOut(const s32 index) {
Common::FixedPoint<50, 14> TapOut(const s32 index) const {
auto out{input - (index + 1)};
if (out < buffer.data()) {
out += max_delay + 1;

View File

@@ -95,7 +95,7 @@ public:
return out_sample;
}
Common::FixedPoint<50, 14> Read() {
Common::FixedPoint<50, 14> Read() const {
return *output;
}
@@ -106,7 +106,7 @@ public:
}
}
Common::FixedPoint<50, 14> TapOut(const s32 index) {
Common::FixedPoint<50, 14> TapOut(const s32 index) const {
auto out{input - (index + 1)};
if (out < buffer.data()) {
out += sample_count;

View File

@@ -19,8 +19,8 @@ public:
/**
* Setup a new AddressInfo.
*
* @param cpu_address - The CPU address of this region.
* @param size - The size of this region.
* @param cpu_address_ - The CPU address of this region.
* @param size_ - The size of this region.
*/
void Setup(CpuAddr cpu_address_, u64 size_) {
cpu_address = cpu_address_;
@@ -42,7 +42,6 @@ public:
* Assign this region to a memory pool.
*
* @param memory_pool_ - Memory pool to assign.
* @return The CpuAddr address of this region.
*/
void SetPool(MemoryPoolInfo* memory_pool_) {
memory_pool = memory_pool_;

View File

@@ -56,7 +56,7 @@ class NodeStates {
*
* @return The current stack position.
*/
u32 Count() {
u32 Count() const {
return pos;
}
@@ -83,7 +83,7 @@ class NodeStates {
*
* @return The node on the top of the stack.
*/
u32 top() {
u32 top() const {
return stack[pos - 1];
}
@@ -112,11 +112,11 @@ public:
/**
* Initialize the node states.
*
* @param buffer - The workbuffer to use. Unused.
* @param buffer_ - The workbuffer to use. Unused.
* @param node_buffer_size - The size of the workbuffer. Unused.
* @param count - The number of nodes in the graph.
*/
void Initialize(std::span<u8> nodes, u64 node_buffer_size, u32 count);
void Initialize(std::span<u8> buffer_, u64 node_buffer_size, u32 count);
/**
* Sort the graph. Only calls DepthFirstSearch.

View File

@@ -73,7 +73,8 @@ public:
* Calculate the required size for the performance workbuffer.
*
* @param behavior - Check which version is supported.
* @param params - Input parameters.
* @param params - Input parameters.
*
* @return Required workbuffer size.
*/
static u64 GetRequiredBufferSizeForPerformanceMetricsPerFrame(
@@ -104,7 +105,7 @@ public:
* @param workbuffer - Workbuffer to use for performance frames.
* @param workbuffer_size - Size of the workbuffer.
* @param params - Input parameters.
* @param behavior - Behaviour to check version and data format.
* @param behavior - Behaviour to check version and data format.
* @param memory_pool - Used to translate the workbuffer address for the DSP.
*/
virtual void Initialize(std::span<u8> workbuffer, u64 workbuffer_size,
@@ -160,7 +161,8 @@ public:
* workbuffer, to be written by the AudioRenderer.
*
* @param addresses - Filled with pointers to the new detail, which should be passed
* to the AudioRenderer with Performance commands to be written.
* to the AudioRenderer with Performance commands to be written.
* @param detail_type - Performance detail type.
* @param entry_type - The type of this detail. See PerformanceEntryType
* @param node_id - Node id for this detail.
* @return True if a new detail was created and the offsets are valid, otherwise false.

View File

@@ -22,9 +22,7 @@ SystemManager::SystemManager(Core::System& core_)
thread_event{Core::Timing::CreateEvent(
"AudioRendererSystemManager", [this](std::uintptr_t, s64 time, std::chrono::nanoseconds) {
return ThreadFunc2(time);
})} {
core.CoreTiming().RegisterPauseCallback([this](bool paused) { PauseCallback(paused); });
}
})} {}
SystemManager::~SystemManager() {
Stop();
@@ -125,11 +123,4 @@ std::optional<std::chrono::nanoseconds> SystemManager::ThreadFunc2(s64 time) {
return std::nullopt;
}
void SystemManager::PauseCallback(bool paused) {
if (paused && core.IsPoweredOn() && core.IsShuttingDown()) {
update.store(true);
update.notify_all();
}
}
} // namespace AudioCore::AudioRenderer

View File

@@ -73,13 +73,6 @@ private:
*/
std::optional<std::chrono::nanoseconds> ThreadFunc2(s64 time);
/**
* Callback from core timing when pausing, used to detect shutdowns and stop ThreadFunc.
*
* @param paused - Are we pausing or resuming?
*/
void PauseCallback(bool paused);
enum class StreamState {
Filling,
Steady,
@@ -106,8 +99,6 @@ private:
std::shared_ptr<Core::Timing::EventType> thread_event;
/// Atomic for main thread to wait on
std::atomic<bool> update{};
/// Current state of the streams
StreamState state{StreamState::Filling};
};
} // namespace AudioCore::AudioRenderer

View File

@@ -27,7 +27,7 @@ public:
/**
* Free the given upsampler.
*
* @param The upsampler to be freed.
* @param info The upsampler to be freed.
*/
void Free(UpsamplerInfo* info);

View File

@@ -185,7 +185,8 @@ public:
/**
* Does this voice ned an update?
*
* @param params - Input parametetrs to check matching.
* @param params - Input parameters to check matching.
*
* @return True if this voice needs an update, otherwise false.
*/
bool ShouldUpdateParameters(const InParameter& params) const;
@@ -194,9 +195,9 @@ public:
* Update the parameters of this voice.
*
* @param error_info - Output error code.
* @param params - Input parametters to udpate from.
* @param params - Input parameters to update from.
* @param pool_mapper - Used to map buffers.
* @param behavior - behavior to check supported features.
* @param behavior - behavior to check supported features.
*/
void UpdateParameters(BehaviorInfo::ErrorInfo& error_info, const InParameter& params,
const PoolMapper& pool_mapper, const BehaviorInfo& behavior);
@@ -218,12 +219,12 @@ public:
/**
* Update all wavebuffers.
*
* @param error_infos - Output 2D array of errors, 2 per wavebuffer.
* @param error_count - Number of errors provided. Unused.
* @param params - Input parametters to be used for the update.
* @param error_infos - Output 2D array of errors, 2 per wavebuffer.
* @param error_count - Number of errors provided. Unused.
* @param params - Input parameters to be used for the update.
* @param voice_states - The voice states for each channel in this voice to be updated.
* @param pool_mapper - Used to map the wavebuffers.
* @param behavior - Used to check for supported features.
* @param pool_mapper - Used to map the wavebuffers.
* @param behavior - Used to check for supported features.
*/
void UpdateWaveBuffers(std::span<std::array<BehaviorInfo::ErrorInfo, 2>> error_infos,
u32 error_count, const InParameter& params,
@@ -233,13 +234,13 @@ public:
/**
* Update a wavebuffer.
*
* @param error_infos - Output array of errors.
* @param error_info - Output array of errors.
* @param wave_buffer - The wavebuffer to be updated.
* @param wave_buffer_internal - Input parametters to be used for the update.
* @param sample_format - Sample format of the wavebuffer.
* @param valid - Is this wavebuffer valid?
* @param pool_mapper - Used to map the wavebuffers.
* @param behavior - Used to check for supported features.
* @param behavior - Used to check for supported features.
*/
void UpdateWaveBuffer(std::span<BehaviorInfo::ErrorInfo> error_info, WaveBuffer& wave_buffer,
const WaveBufferInternal& wave_buffer_internal,
@@ -276,7 +277,7 @@ public:
/**
* Check if this voice has any mixing connections.
*
* @return True if this voice participes in mixing, otherwise false.
* @return True if this voice participates in mixing, otherwise false.
*/
bool HasAnyConnection() const;
@@ -301,7 +302,8 @@ public:
/**
* Update this voice on command generation.
*
* @param voice_states - Voice states for these wavebuffers.
* @param voice_context - Voice context for these wavebuffers.
*
* @return True if this voice should be generated, otherwise false.
*/
bool UpdateForCommandGeneration(VoiceContext& voice_context);

View File

@@ -129,20 +129,13 @@ public:
* Default false.
*/
void Start(bool resume = false) override {
if (!ctx) {
if (!ctx || !paused) {
return;
}
if (resume && was_playing) {
if (cubeb_stream_start(stream_backend) != CUBEB_OK) {
LOG_CRITICAL(Audio_Sink, "Error starting cubeb stream");
}
paused = false;
} else if (!resume) {
if (cubeb_stream_start(stream_backend) != CUBEB_OK) {
LOG_CRITICAL(Audio_Sink, "Error starting cubeb stream");
}
paused = false;
paused = false;
if (cubeb_stream_start(stream_backend) != CUBEB_OK) {
LOG_CRITICAL(Audio_Sink, "Error starting cubeb stream");
}
}
@@ -151,16 +144,15 @@ public:
*/
void Stop() override {
Unstall();
if (!ctx) {
if (!ctx || paused) {
return;
}
paused = true;
if (cubeb_stream_stop(stream_backend) != CUBEB_OK) {
LOG_CRITICAL(Audio_Sink, "Error stopping cubeb stream");
}
was_playing.store(!paused);
paused = true;
}
private:
@@ -286,18 +278,6 @@ void CubebSink::CloseStreams() {
sink_streams.clear();
}
void CubebSink::PauseStreams() {
for (auto& stream : sink_streams) {
stream->Stop();
}
}
void CubebSink::UnpauseStreams() {
for (auto& stream : sink_streams) {
stream->Start(true);
}
}
f32 CubebSink::GetDeviceVolume() const {
if (sink_streams.empty()) {
return 1.0f;

View File

@@ -34,8 +34,7 @@ public:
* May differ from the device's channel count.
* @param name - Name of this stream.
* @param type - Type of this stream, render/in/out.
* @param event - Audio render only, a signal used to prevent the renderer running too
* fast.
*
* @return A pointer to the created SinkStream
*/
SinkStream* AcquireSinkStream(Core::System& system, u32 system_channels,
@@ -53,16 +52,6 @@ public:
*/
void CloseStreams() override;
/**
* Pause all streams.
*/
void PauseStreams() override;
/**
* Unpause all streams.
*/
void UnpauseStreams() override;
/**
* Get the device volume. Set from calls to the IAudioDevice service.
*
@@ -101,7 +90,7 @@ private:
};
/**
* Get a list of conencted devices from Cubeb.
* Get a list of connected devices from Cubeb.
*
* @param capture - Return input (capture) devices if true, otherwise output devices.
*/

View File

@@ -44,8 +44,6 @@ public:
void CloseStream(SinkStream*) override {}
void CloseStreams() override {}
void PauseStreams() override {}
void UnpauseStreams() override {}
f32 GetDeviceVolume() const override {
return 1.0f;
}

View File

@@ -108,17 +108,12 @@ public:
* Default false.
*/
void Start(bool resume = false) override {
if (device == 0) {
if (device == 0 || !paused) {
return;
}
if (resume && was_playing) {
SDL_PauseAudioDevice(device, 0);
paused = false;
} else if (!resume) {
SDL_PauseAudioDevice(device, 0);
paused = false;
}
paused = false;
SDL_PauseAudioDevice(device, 0);
}
/**
@@ -126,11 +121,11 @@ public:
*/
void Stop() override {
Unstall();
if (device == 0) {
if (device == 0 || paused) {
return;
}
SDL_PauseAudioDevice(device, 1);
paused = true;
SDL_PauseAudioDevice(device, 1);
}
private:
@@ -207,18 +202,6 @@ void SDLSink::CloseStreams() {
sink_streams.clear();
}
void SDLSink::PauseStreams() {
for (auto& stream : sink_streams) {
stream->Stop();
}
}
void SDLSink::UnpauseStreams() {
for (auto& stream : sink_streams) {
stream->Start();
}
}
f32 SDLSink::GetDeviceVolume() const {
if (sink_streams.empty()) {
return 1.0f;

View File

@@ -32,8 +32,7 @@ public:
* May differ from the device's channel count.
* @param name - Name of this stream.
* @param type - Type of this stream, render/in/out.
* @param event - Audio render only, a signal used to prevent the renderer running too
* fast.
*
* @return A pointer to the created SinkStream
*/
SinkStream* AcquireSinkStream(Core::System& system, u32 system_channels,
@@ -51,16 +50,6 @@ public:
*/
void CloseStreams() override;
/**
* Pause all streams.
*/
void PauseStreams() override;
/**
* Unpause all streams.
*/
void UnpauseStreams() override;
/**
* Get the device volume. Set from calls to the IAudioDevice service.
*
@@ -92,7 +81,7 @@ private:
};
/**
* Get a list of conencted devices from Cubeb.
* Get a list of connected devices from SDL.
*
* @param capture - Return input (capture) devices if true, otherwise output devices.
*/

View File

@@ -39,16 +39,6 @@ public:
*/
virtual void CloseStreams() = 0;
/**
* Pause all streams.
*/
virtual void PauseStreams() = 0;
/**
* Unpause all streams.
*/
virtual void UnpauseStreams() = 0;
/**
* Create a new sink stream, kept within this sink, with a pointer returned for use.
* Do not free the returned pointer. When done with the stream, call CloseStream on the sink.
@@ -58,8 +48,7 @@ public:
* May differ from the device's channel count.
* @param name - Name of this stream.
* @param type - Type of this stream, render/in/out.
* @param event - Audio render only, a signal used to prevent the renderer running too
* fast.
*
* @return A pointer to the created SinkStream
*/
virtual SinkStream* AcquireSinkStream(Core::System& system, u32 system_channels,

View File

@@ -143,6 +143,12 @@ void SinkStream::ProcessAudioIn(std::span<const s16> input_buffer, std::size_t n
const std::size_t frame_size_bytes = frame_size * sizeof(s16);
size_t frames_written{0};
// If we're paused or going to shut down, we don't want to consume buffers as coretiming is
// paused and we'll desync, so just return.
if (system.IsPaused() || system.IsShuttingDown()) {
return;
}
if (queued_buffers > max_queue_size) {
Stall();
}
@@ -193,13 +199,28 @@ void SinkStream::ProcessAudioOutAndRender(std::span<s16> output_buffer, std::siz
const std::size_t frame_size_bytes = frame_size * sizeof(s16);
size_t frames_written{0};
// If we're paused or going to shut down, we don't want to consume buffers as coretiming is
// paused and we'll desync, so just play silence.
if (system.IsPaused() || system.IsShuttingDown()) {
constexpr std::array<s16, 6> silence{};
for (size_t i = frames_written; i < num_frames; i++) {
std::memcpy(&output_buffer[i * frame_size], &silence[0], frame_size_bytes);
}
return;
}
// Due to many frames being queued up with nvdec (5 frames or so?), a lot of buffers also get
// queued up (30+) but not all at once, which causes constant stalling here, so just let the
// video play out without attempting to stall.
// Can hopefully remove this later with a more complete NVDEC implementation.
const auto nvdec_active{system.AudioCore().IsNVDECActive()};
if (!nvdec_active && queued_buffers > max_queue_size) {
// Core timing cannot be paused in single-core mode, so Stall ends up being called over and over
// and never recovers to a normal state, so just skip attempting to sync things on single-core.
if (system.IsMulticore() && !nvdec_active && queued_buffers > max_queue_size) {
Stall();
} else if (system.IsMulticore() && queued_buffers <= max_queue_size) {
Unstall();
}
while (frames_written < num_frames) {
@@ -239,7 +260,7 @@ void SinkStream::ProcessAudioOutAndRender(std::span<s16> output_buffer, std::siz
std::memcpy(&last_frame[0], &output_buffer[(frames_written - 1) * frame_size],
frame_size_bytes);
if (stalled && queued_buffers <= max_queue_size) {
if (system.IsMulticore() && queued_buffers <= max_queue_size) {
Unstall();
}
}

View File

@@ -151,7 +151,7 @@ public:
*
* @return The number of queued buffers.
*/
u32 GetQueueSize() {
u32 GetQueueSize() const {
return queued_buffers.load();
}
@@ -214,14 +214,12 @@ protected:
Core::System& system;
/// Type of this stream
StreamType type;
/// Set by the audio render/in/out systen which uses this stream
/// Set by the audio render/in/out system which uses this stream
u32 system_channels{2};
/// Channels supported by hardware
u32 device_channels{2};
/// Is this stream currently paused?
std::atomic<bool> paused{true};
/// Was this stream previously playing?
std::atomic<bool> was_playing{false};
/// Name of this stream
std::string name{};

View File

@@ -19,7 +19,7 @@ find_package(Git QUIET)
add_custom_command(OUTPUT scm_rev.cpp
COMMAND ${CMAKE_COMMAND}
-DSRC_DIR=${CMAKE_SOURCE_DIR}
-DSRC_DIR=${PROJECT_SOURCE_DIR}
-DBUILD_REPOSITORY=${BUILD_REPOSITORY}
-DTITLE_BAR_FORMAT_IDLE=${TITLE_BAR_FORMAT_IDLE}
-DTITLE_BAR_FORMAT_RUNNING=${TITLE_BAR_FORMAT_RUNNING}
@@ -31,13 +31,13 @@ add_custom_command(OUTPUT scm_rev.cpp
-DGIT_BRANCH=${GIT_BRANCH}
-DBUILD_FULLNAME=${BUILD_FULLNAME}
-DGIT_EXECUTABLE=${GIT_EXECUTABLE}
-P ${CMAKE_SOURCE_DIR}/CMakeModules/GenerateSCMRev.cmake
-P ${PROJECT_SOURCE_DIR}/CMakeModules/GenerateSCMRev.cmake
DEPENDS
# Check that the scm_rev files haven't changed
"${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp.in"
"${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.h"
# technically we should regenerate if the git version changed, but its not worth the effort imo
"${CMAKE_SOURCE_DIR}/CMakeModules/GenerateSCMRev.cmake"
"${PROJECT_SOURCE_DIR}/CMakeModules/GenerateSCMRev.cmake"
VERBATIM
)
@@ -166,6 +166,7 @@ if(ARCHITECTURE_x86_64)
x64/xbyak_abi.h
x64/xbyak_util.h
)
target_link_libraries(common PRIVATE xbyak)
endif()
if (MSVC)
@@ -189,7 +190,7 @@ endif()
create_target_directory_groups(common)
target_link_libraries(common PUBLIC ${Boost_LIBRARIES} fmt::fmt microprofile Threads::Threads)
target_link_libraries(common PRIVATE lz4::lz4 xbyak)
target_link_libraries(common PRIVATE lz4::lz4)
if (TARGET zstd::zstd)
target_link_libraries(common PRIVATE zstd::zstd)
else()

View File

@@ -530,6 +530,8 @@ struct Values {
Setting<bool> use_debug_asserts{false, "use_debug_asserts"};
Setting<bool> use_auto_stub{false, "use_auto_stub"};
Setting<bool> enable_all_controllers{false, "enable_all_controllers"};
Setting<bool> create_crash_dumps{false, "create_crash_dumps"};
Setting<bool> perform_vulkan_check{true, "perform_vulkan_check"};
// Miscellaneous
Setting<std::string> log_filter{"*:Info", "log_filter"};

View File

@@ -54,6 +54,10 @@ public:
is_set = false;
}
[[nodiscard]] bool IsSet() {
return is_set;
}
private:
std::condition_variable condvar;
std::mutex mutex;

View File

@@ -4,12 +4,6 @@
add_library(core STATIC
arm/arm_interface.h
arm/arm_interface.cpp
arm/dynarmic/arm_dynarmic_32.cpp
arm/dynarmic/arm_dynarmic_32.h
arm/dynarmic/arm_dynarmic_64.cpp
arm/dynarmic/arm_dynarmic_64.h
arm/dynarmic/arm_dynarmic_cp15.cpp
arm/dynarmic/arm_dynarmic_cp15.h
arm/dynarmic/arm_exclusive_monitor.cpp
arm/dynarmic/arm_exclusive_monitor.h
arm/exclusive_monitor.cpp
@@ -525,6 +519,9 @@ add_library(core STATIC
hle/service/ncm/ncm.h
hle/service/nfc/nfc.cpp
hle/service/nfc/nfc.h
hle/service/nfp/amiibo_crypto.cpp
hle/service/nfp/amiibo_crypto.h
hle/service/nfp/amiibo_types.h
hle/service/nfp/nfp.cpp
hle/service/nfp/nfp.h
hle/service/nfp/nfp_user.cpp

View File

@@ -141,8 +141,6 @@ struct System::Impl {
core_timing.SyncPause(false);
is_paused = false;
audio_core->PauseSinks(false);
return status;
}
@@ -150,8 +148,6 @@ struct System::Impl {
std::unique_lock<std::mutex> lk(suspend_guard);
status = SystemResultStatus::Success;
audio_core->PauseSinks(true);
core_timing.SyncPause(true);
kernel.Suspend(true);
is_paused = true;

View File

@@ -73,7 +73,6 @@ void CoreTiming::Shutdown() {
if (timer_thread) {
timer_thread->join();
}
pause_callbacks.clear();
ClearPendingEvents();
timer_thread.reset();
has_started = false;
@@ -86,10 +85,6 @@ void CoreTiming::Pause(bool is_paused) {
if (!is_paused) {
pause_end_time = GetGlobalTimeNs().count();
}
for (auto& cb : pause_callbacks) {
cb(is_paused);
}
}
void CoreTiming::SyncPause(bool is_paused) {
@@ -110,10 +105,6 @@ void CoreTiming::SyncPause(bool is_paused) {
if (!is_paused) {
pause_end_time = GetGlobalTimeNs().count();
}
for (auto& cb : pause_callbacks) {
cb(is_paused);
}
}
bool CoreTiming::IsRunning() const {
@@ -143,13 +134,17 @@ void CoreTiming::ScheduleLoopingEvent(std::chrono::nanoseconds start_time,
std::chrono::nanoseconds resched_time,
const std::shared_ptr<EventType>& event_type,
std::uintptr_t user_data, bool absolute_time) {
std::scoped_lock scope{basic_lock};
const auto next_time{absolute_time ? start_time : GetGlobalTimeNs() + start_time};
{
std::scoped_lock scope{basic_lock};
const auto next_time{absolute_time ? start_time : GetGlobalTimeNs() + start_time};
event_queue.emplace_back(
Event{next_time.count(), event_fifo_id++, user_data, event_type, resched_time.count()});
event_queue.emplace_back(
Event{next_time.count(), event_fifo_id++, user_data, event_type, resched_time.count()});
std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
}
event.Set();
}
void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type,
@@ -219,11 +214,6 @@ void CoreTiming::RemoveEvent(const std::shared_ptr<EventType>& event_type) {
}
}
void CoreTiming::RegisterPauseCallback(PauseCallback&& callback) {
std::scoped_lock lock{basic_lock};
pause_callbacks.emplace_back(std::move(callback));
}
std::optional<s64> CoreTiming::Advance() {
std::scoped_lock lock{advance_lock, basic_lock};
global_timer = GetGlobalTimeNs().count();
@@ -243,17 +233,17 @@ std::optional<s64> CoreTiming::Advance() {
basic_lock.lock();
if (evt.reschedule_time != 0) {
// If this event was scheduled into a pause, its time now is going to be way behind.
// Re-set this event to continue from the end of the pause.
auto next_time{evt.time + evt.reschedule_time};
if (evt.time < pause_end_time) {
next_time = pause_end_time + evt.reschedule_time;
}
const auto next_schedule_time{new_schedule_time.has_value()
? new_schedule_time.value().count()
: evt.reschedule_time};
// If this event was scheduled into a pause, its time now is going to be way behind.
// Re-set this event to continue from the end of the pause.
auto next_time{evt.time + next_schedule_time};
if (evt.time < pause_end_time) {
next_time = pause_end_time + next_schedule_time;
}
event_queue.emplace_back(
Event{next_time, event_fifo_id++, evt.user_data, evt.type, next_schedule_time});
std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
@@ -264,8 +254,7 @@ std::optional<s64> CoreTiming::Advance() {
}
if (!event_queue.empty()) {
const s64 next_time = event_queue.front().time - global_timer;
return next_time;
return event_queue.front().time;
} else {
return std::nullopt;
}
@@ -278,11 +267,29 @@ void CoreTiming::ThreadLoop() {
paused_set = false;
const auto next_time = Advance();
if (next_time) {
if (*next_time > 0) {
std::chrono::nanoseconds next_time_ns = std::chrono::nanoseconds(*next_time);
event.WaitFor(next_time_ns);
// There are more events left in the queue, wait until the next event.
const auto wait_time = *next_time - GetGlobalTimeNs().count();
if (wait_time > 0) {
// Assume a timer resolution of 1ms.
static constexpr s64 TimerResolutionNS = 1000000;
// Sleep in discrete intervals of the timer resolution, and spin the rest.
const auto sleep_time = wait_time - (wait_time % TimerResolutionNS);
if (sleep_time > 0) {
event.WaitFor(std::chrono::nanoseconds(sleep_time));
}
while (!paused && !event.IsSet() && GetGlobalTimeNs().count() < *next_time) {
// Yield to reduce thread starvation.
std::this_thread::yield();
}
if (event.IsSet()) {
event.Reset();
}
}
} else {
// Queue is empty, wait until another event is scheduled and signals us to continue.
wait_set = true;
event.Wait();
}

View File

@@ -22,7 +22,6 @@ namespace Core::Timing {
/// A callback that may be scheduled for a particular core timing event.
using TimedCallback = std::function<std::optional<std::chrono::nanoseconds>(
std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late)>;
using PauseCallback = std::function<void(bool paused)>;
/// Contains the characteristics of a particular event.
struct EventType {
@@ -134,9 +133,6 @@ public:
/// Checks for events manually and returns time in nanoseconds for next event, threadsafe.
std::optional<s64> Advance();
/// Register a callback function to be called when coretiming pauses.
void RegisterPauseCallback(PauseCallback&& callback);
private:
struct Event;
@@ -176,8 +172,6 @@ private:
/// Cycle timing
u64 ticks{};
s64 downcount{};
std::vector<PauseCallback> pause_callbacks{};
};
/// Creates a core timing event with the given name and callback.

View File

@@ -32,7 +32,7 @@ enum class MiiEditResult : u32 {
};
struct MiiEditCharInfo {
Service::Mii::MiiInfo mii_info{};
Service::Mii::CharInfo mii_info{};
};
static_assert(sizeof(MiiEditCharInfo) == 0x58, "MiiEditCharInfo has incorrect size.");

View File

@@ -246,9 +246,8 @@ void AudOutU::ListAudioOuts(Kernel::HLERequestContext& ctx) {
const auto write_count =
static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName));
std::vector<AudioDevice::AudioDeviceName> device_names{};
std::string print_names{};
if (write_count > 0) {
device_names.push_back(AudioDevice::AudioDeviceName("DeviceOut"));
device_names.emplace_back("DeviceOut");
LOG_DEBUG(Service_Audio, "called. \nName=DeviceOut");
} else {
LOG_DEBUG(Service_Audio, "called. Empty buffer passed in.");

View File

@@ -252,7 +252,7 @@ private:
std::vector<AudioDevice::AudioDeviceName> out_names{};
u32 out_count = impl->ListAudioDeviceName(out_names, in_count);
const u32 out_count = impl->ListAudioDeviceName(out_names, in_count);
std::string out{};
for (u32 i = 0; i < out_count; i++) {
@@ -365,7 +365,7 @@ private:
std::vector<AudioDevice::AudioDeviceName> out_names{};
u32 out_count = impl->ListAudioOutputDeviceName(out_names, in_count);
const u32 out_count = impl->ListAudioOutputDeviceName(out_names, in_count);
std::string out{};
for (u32 i = 0; i < out_count; i++) {

View File

@@ -255,6 +255,32 @@ void HwOpus::GetWorkBufferSizeEx(Kernel::HLERequestContext& ctx) {
GetWorkBufferSize(ctx);
}
void HwOpus::GetWorkBufferSizeForMultiStreamEx(Kernel::HLERequestContext& ctx) {
OpusMultiStreamParametersEx param;
std::memcpy(&param, ctx.ReadBuffer().data(), ctx.GetReadBufferSize());
const auto sample_rate = param.sample_rate;
const auto channel_count = param.channel_count;
const auto number_streams = param.number_streams;
const auto number_stereo_streams = param.number_stereo_streams;
LOG_DEBUG(
Audio,
"called with sample_rate={}, channel_count={}, number_streams={}, number_stereo_streams={}",
sample_rate, channel_count, number_streams, number_stereo_streams);
ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 ||
sample_rate == 12000 || sample_rate == 8000,
"Invalid sample rate");
const u32 worker_buffer_sz =
static_cast<u32>(opus_multistream_decoder_get_size(number_streams, number_stereo_streams));
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push<u32>(worker_buffer_sz);
}
void HwOpus::OpenHardwareOpusDecoder(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto sample_rate = rp.Pop<u32>();
@@ -335,7 +361,7 @@ HwOpus::HwOpus(Core::System& system_) : ServiceFramework{system_, "hwopus"} {
{4, &HwOpus::OpenHardwareOpusDecoderEx, "OpenHardwareOpusDecoderEx"},
{5, &HwOpus::GetWorkBufferSizeEx, "GetWorkBufferSizeEx"},
{6, nullptr, "OpenHardwareOpusDecoderForMultiStreamEx"},
{7, nullptr, "GetWorkBufferSizeForMultiStreamEx"},
{7, &HwOpus::GetWorkBufferSizeForMultiStreamEx, "GetWorkBufferSizeForMultiStreamEx"},
};
RegisterHandlers(functions);
}

View File

@@ -11,6 +11,16 @@ class System;
namespace Service::Audio {
struct OpusMultiStreamParametersEx {
u32 sample_rate;
u32 channel_count;
u32 number_streams;
u32 number_stereo_streams;
u32 use_large_frame_size;
u32 padding;
std::array<u32, 64> channel_mappings;
};
class HwOpus final : public ServiceFramework<HwOpus> {
public:
explicit HwOpus(Core::System& system_);
@@ -21,6 +31,7 @@ private:
void OpenHardwareOpusDecoderEx(Kernel::HLERequestContext& ctx);
void GetWorkBufferSize(Kernel::HLERequestContext& ctx);
void GetWorkBufferSizeEx(Kernel::HLERequestContext& ctx);
void GetWorkBufferSizeForMultiStreamEx(Kernel::HLERequestContext& ctx);
};
} // namespace Service::Audio

View File

@@ -43,7 +43,7 @@ public:
{20, nullptr, "IsBrokenDatabaseWithClearFlag"},
{21, &IDatabaseService::GetIndex, "GetIndex"},
{22, &IDatabaseService::SetInterfaceVersion, "SetInterfaceVersion"},
{23, nullptr, "Convert"},
{23, &IDatabaseService::Convert, "Convert"},
{24, nullptr, "ConvertCoreDataToCharInfo"},
{25, nullptr, "ConvertCharInfoToCoreData"},
{26, nullptr, "Append"},
@@ -130,7 +130,7 @@ private:
return;
}
std::vector<MiiInfo> values;
std::vector<CharInfo> values;
for (const auto& element : *result) {
values.emplace_back(element.info);
}
@@ -144,7 +144,7 @@ private:
void UpdateLatest(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto info{rp.PopRaw<MiiInfo>()};
const auto info{rp.PopRaw<CharInfo>()};
const auto source_flag{rp.PopRaw<SourceFlag>()};
LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag);
@@ -156,9 +156,9 @@ private:
return;
}
IPC::ResponseBuilder rb{ctx, 2 + sizeof(MiiInfo) / sizeof(u32)};
IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)};
rb.Push(ResultSuccess);
rb.PushRaw<MiiInfo>(*result);
rb.PushRaw<CharInfo>(*result);
}
void BuildRandom(Kernel::HLERequestContext& ctx) {
@@ -191,9 +191,9 @@ private:
return;
}
IPC::ResponseBuilder rb{ctx, 2 + sizeof(MiiInfo) / sizeof(u32)};
IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)};
rb.Push(ResultSuccess);
rb.PushRaw<MiiInfo>(manager.BuildRandom(age, gender, race));
rb.PushRaw<CharInfo>(manager.BuildRandom(age, gender, race));
}
void BuildDefault(Kernel::HLERequestContext& ctx) {
@@ -210,14 +210,14 @@ private:
return;
}
IPC::ResponseBuilder rb{ctx, 2 + sizeof(MiiInfo) / sizeof(u32)};
IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)};
rb.Push(ResultSuccess);
rb.PushRaw<MiiInfo>(manager.BuildDefault(index));
rb.PushRaw<CharInfo>(manager.BuildDefault(index));
}
void GetIndex(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto info{rp.PopRaw<MiiInfo>()};
const auto info{rp.PopRaw<CharInfo>()};
LOG_DEBUG(Service_Mii, "called");
@@ -239,6 +239,18 @@ private:
rb.Push(ResultSuccess);
}
void Convert(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto mii_v3{rp.PopRaw<Ver3StoreData>()};
LOG_INFO(Service_Mii, "called");
IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)};
rb.Push(ResultSuccess);
rb.PushRaw<CharInfo>(manager.ConvertV3ToCharInfo(mii_v3));
}
constexpr bool IsInterfaceVersionSupported(u32 interface_version) const {
return current_interface_version >= interface_version;
}

View File

@@ -42,7 +42,7 @@ std::array<T, DestArraySize> ResizeArray(const std::array<T, SourceArraySize>& i
return out;
}
MiiInfo ConvertStoreDataToInfo(const MiiStoreData& data) {
CharInfo ConvertStoreDataToInfo(const MiiStoreData& data) {
MiiStoreBitFields bf;
std::memcpy(&bf, data.data.data.data(), sizeof(MiiStoreBitFields));
@@ -409,8 +409,8 @@ u32 MiiManager::GetCount(SourceFlag source_flag) const {
return static_cast<u32>(count);
}
ResultVal<MiiInfo> MiiManager::UpdateLatest([[maybe_unused]] const MiiInfo& info,
SourceFlag source_flag) {
ResultVal<CharInfo> MiiManager::UpdateLatest([[maybe_unused]] const CharInfo& info,
SourceFlag source_flag) {
if ((source_flag & SourceFlag::Database) == SourceFlag::None) {
return ERROR_CANNOT_FIND_ENTRY;
}
@@ -419,14 +419,91 @@ ResultVal<MiiInfo> MiiManager::UpdateLatest([[maybe_unused]] const MiiInfo& info
return ERROR_CANNOT_FIND_ENTRY;
}
MiiInfo MiiManager::BuildRandom(Age age, Gender gender, Race race) {
CharInfo MiiManager::BuildRandom(Age age, Gender gender, Race race) {
return ConvertStoreDataToInfo(BuildRandomStoreData(age, gender, race, user_id));
}
MiiInfo MiiManager::BuildDefault(std::size_t index) {
CharInfo MiiManager::BuildDefault(std::size_t index) {
return ConvertStoreDataToInfo(BuildDefaultStoreData(RawData::DefaultMii.at(index), user_id));
}
CharInfo MiiManager::ConvertV3ToCharInfo(Ver3StoreData mii_v3) const {
Service::Mii::MiiManager manager;
auto mii = manager.BuildDefault(0);
// Check if mii data exist
if (mii_v3.mii_name[0] == 0) {
return mii;
}
// TODO: We are ignoring a bunch of data from the mii_v3
mii.gender = static_cast<u8>(mii_v3.mii_information.gender);
mii.favorite_color = static_cast<u8>(mii_v3.mii_information.favorite_color);
mii.height = mii_v3.height;
mii.build = mii_v3.build;
memset(mii.name.data(), 0, sizeof(mii.name));
memcpy(mii.name.data(), mii_v3.mii_name.data(), sizeof(mii_v3.mii_name));
mii.font_region = mii_v3.region_information.character_set;
mii.faceline_type = mii_v3.appearance_bits1.face_shape;
mii.faceline_color = mii_v3.appearance_bits1.skin_color;
mii.faceline_wrinkle = mii_v3.appearance_bits2.wrinkles;
mii.faceline_make = mii_v3.appearance_bits2.makeup;
mii.hair_type = mii_v3.hair_style;
mii.hair_color = mii_v3.appearance_bits3.hair_color;
mii.hair_flip = mii_v3.appearance_bits3.flip_hair;
mii.eye_type = static_cast<u8>(mii_v3.appearance_bits4.eye_type);
mii.eye_color = static_cast<u8>(mii_v3.appearance_bits4.eye_color);
mii.eye_scale = static_cast<u8>(mii_v3.appearance_bits4.eye_scale);
mii.eye_aspect = static_cast<u8>(mii_v3.appearance_bits4.eye_vertical_stretch);
mii.eye_rotate = static_cast<u8>(mii_v3.appearance_bits4.eye_rotation);
mii.eye_x = static_cast<u8>(mii_v3.appearance_bits4.eye_spacing);
mii.eye_y = static_cast<u8>(mii_v3.appearance_bits4.eye_y_position);
mii.eyebrow_type = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_style);
mii.eyebrow_color = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_color);
mii.eyebrow_scale = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_scale);
mii.eyebrow_aspect = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_yscale);
mii.eyebrow_rotate = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_rotation);
mii.eyebrow_x = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_spacing);
mii.eyebrow_y = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_y_position);
mii.nose_type = static_cast<u8>(mii_v3.appearance_bits6.nose_type);
mii.nose_scale = static_cast<u8>(mii_v3.appearance_bits6.nose_scale);
mii.nose_y = static_cast<u8>(mii_v3.appearance_bits6.nose_y_position);
mii.mouth_type = static_cast<u8>(mii_v3.appearance_bits7.mouth_type);
mii.mouth_color = static_cast<u8>(mii_v3.appearance_bits7.mouth_color);
mii.mouth_scale = static_cast<u8>(mii_v3.appearance_bits7.mouth_scale);
mii.mouth_aspect = static_cast<u8>(mii_v3.appearance_bits7.mouth_horizontal_stretch);
mii.mouth_y = static_cast<u8>(mii_v3.appearance_bits8.mouth_y_position);
mii.mustache_type = static_cast<u8>(mii_v3.appearance_bits8.mustache_type);
mii.mustache_scale = static_cast<u8>(mii_v3.appearance_bits9.mustache_scale);
mii.mustache_y = static_cast<u8>(mii_v3.appearance_bits9.mustache_y_position);
mii.beard_type = static_cast<u8>(mii_v3.appearance_bits9.bear_type);
mii.beard_color = static_cast<u8>(mii_v3.appearance_bits9.facial_hair_color);
mii.glasses_type = static_cast<u8>(mii_v3.appearance_bits10.glasses_type);
mii.glasses_color = static_cast<u8>(mii_v3.appearance_bits10.glasses_color);
mii.glasses_scale = static_cast<u8>(mii_v3.appearance_bits10.glasses_scale);
mii.glasses_y = static_cast<u8>(mii_v3.appearance_bits10.glasses_y_position);
mii.mole_type = static_cast<u8>(mii_v3.appearance_bits11.mole_enabled);
mii.mole_scale = static_cast<u8>(mii_v3.appearance_bits11.mole_scale);
mii.mole_x = static_cast<u8>(mii_v3.appearance_bits11.mole_x_position);
mii.mole_y = static_cast<u8>(mii_v3.appearance_bits11.mole_y_position);
// TODO: Validate mii data
return mii;
}
ResultVal<std::vector<MiiInfoElement>> MiiManager::GetDefault(SourceFlag source_flag) {
std::vector<MiiInfoElement> result;
@@ -441,7 +518,7 @@ ResultVal<std::vector<MiiInfoElement>> MiiManager::GetDefault(SourceFlag source_
return result;
}
Result MiiManager::GetIndex([[maybe_unused]] const MiiInfo& info, u32& index) {
Result MiiManager::GetIndex([[maybe_unused]] const CharInfo& info, u32& index) {
constexpr u32 INVALID_INDEX{0xFFFFFFFF};
index = INVALID_INDEX;

View File

@@ -19,11 +19,12 @@ public:
bool CheckAndResetUpdateCounter(SourceFlag source_flag, u64& current_update_counter);
bool IsFullDatabase() const;
u32 GetCount(SourceFlag source_flag) const;
ResultVal<MiiInfo> UpdateLatest(const MiiInfo& info, SourceFlag source_flag);
MiiInfo BuildRandom(Age age, Gender gender, Race race);
MiiInfo BuildDefault(std::size_t index);
ResultVal<CharInfo> UpdateLatest(const CharInfo& info, SourceFlag source_flag);
CharInfo BuildRandom(Age age, Gender gender, Race race);
CharInfo BuildDefault(std::size_t index);
CharInfo ConvertV3ToCharInfo(Ver3StoreData mii_v3) const;
ResultVal<std::vector<MiiInfoElement>> GetDefault(SourceFlag source_flag);
Result GetIndex(const MiiInfo& info, u32& index);
Result GetIndex(const CharInfo& info, u32& index);
private:
const Common::UUID user_id{};

View File

@@ -86,7 +86,8 @@ enum class SourceFlag : u32 {
};
DECLARE_ENUM_FLAG_OPERATORS(SourceFlag);
struct MiiInfo {
// nn::mii::CharInfo
struct CharInfo {
Common::UUID uuid;
std::array<char16_t, 11> name;
u8 font_region;
@@ -140,16 +141,16 @@ struct MiiInfo {
u8 mole_y;
u8 padding;
};
static_assert(sizeof(MiiInfo) == 0x58, "MiiInfo has incorrect size.");
static_assert(std::has_unique_object_representations_v<MiiInfo>,
"All bits of MiiInfo must contribute to its value.");
static_assert(sizeof(CharInfo) == 0x58, "CharInfo has incorrect size.");
static_assert(std::has_unique_object_representations_v<CharInfo>,
"All bits of CharInfo must contribute to its value.");
#pragma pack(push, 4)
struct MiiInfoElement {
MiiInfoElement(const MiiInfo& info_, Source source_) : info{info_}, source{source_} {}
MiiInfoElement(const CharInfo& info_, Source source_) : info{info_}, source{source_} {}
MiiInfo info{};
CharInfo info{};
Source source{};
};
static_assert(sizeof(MiiInfoElement) == 0x5c, "MiiInfoElement has incorrect size.");
@@ -243,6 +244,131 @@ static_assert(sizeof(MiiStoreBitFields) == 0x1c, "MiiStoreBitFields has incorrec
static_assert(std::is_trivially_copyable_v<MiiStoreBitFields>,
"MiiStoreBitFields is not trivially copyable.");
// This is nn::mii::Ver3StoreData
// Based on citra HLE::Applets::MiiData and PretendoNetwork.
// https://github.com/citra-emu/citra/blob/master/src/core/hle/applets/mii_selector.h#L48
// https://github.com/PretendoNetwork/mii-js/blob/master/mii.js#L299
struct Ver3StoreData {
u8 version;
union {
u8 raw;
BitField<0, 1, u8> allow_copying;
BitField<1, 1, u8> profanity_flag;
BitField<2, 2, u8> region_lock;
BitField<4, 2, u8> character_set;
} region_information;
u16_be mii_id;
u64_be system_id;
u32_be specialness_and_creation_date;
std::array<u8, 0x6> creator_mac;
u16_be padding;
union {
u16 raw;
BitField<0, 1, u16> gender;
BitField<1, 4, u16> birth_month;
BitField<5, 5, u16> birth_day;
BitField<10, 4, u16> favorite_color;
BitField<14, 1, u16> favorite;
} mii_information;
std::array<char16_t, 0xA> mii_name;
u8 height;
u8 build;
union {
u8 raw;
BitField<0, 1, u8> disable_sharing;
BitField<1, 4, u8> face_shape;
BitField<5, 3, u8> skin_color;
} appearance_bits1;
union {
u8 raw;
BitField<0, 4, u8> wrinkles;
BitField<4, 4, u8> makeup;
} appearance_bits2;
u8 hair_style;
union {
u8 raw;
BitField<0, 3, u8> hair_color;
BitField<3, 1, u8> flip_hair;
} appearance_bits3;
union {
u32 raw;
BitField<0, 6, u32> eye_type;
BitField<6, 3, u32> eye_color;
BitField<9, 4, u32> eye_scale;
BitField<13, 3, u32> eye_vertical_stretch;
BitField<16, 5, u32> eye_rotation;
BitField<21, 4, u32> eye_spacing;
BitField<25, 5, u32> eye_y_position;
} appearance_bits4;
union {
u32 raw;
BitField<0, 5, u32> eyebrow_style;
BitField<5, 3, u32> eyebrow_color;
BitField<8, 4, u32> eyebrow_scale;
BitField<12, 3, u32> eyebrow_yscale;
BitField<16, 4, u32> eyebrow_rotation;
BitField<21, 4, u32> eyebrow_spacing;
BitField<25, 5, u32> eyebrow_y_position;
} appearance_bits5;
union {
u16 raw;
BitField<0, 5, u16> nose_type;
BitField<5, 4, u16> nose_scale;
BitField<9, 5, u16> nose_y_position;
} appearance_bits6;
union {
u16 raw;
BitField<0, 6, u16> mouth_type;
BitField<6, 3, u16> mouth_color;
BitField<9, 4, u16> mouth_scale;
BitField<13, 3, u16> mouth_horizontal_stretch;
} appearance_bits7;
union {
u8 raw;
BitField<0, 5, u8> mouth_y_position;
BitField<5, 3, u8> mustache_type;
} appearance_bits8;
u8 allow_copying;
union {
u16 raw;
BitField<0, 3, u16> bear_type;
BitField<3, 3, u16> facial_hair_color;
BitField<6, 4, u16> mustache_scale;
BitField<10, 5, u16> mustache_y_position;
} appearance_bits9;
union {
u16 raw;
BitField<0, 4, u16> glasses_type;
BitField<4, 3, u16> glasses_color;
BitField<7, 4, u16> glasses_scale;
BitField<11, 5, u16> glasses_y_position;
} appearance_bits10;
union {
u16 raw;
BitField<0, 1, u16> mole_enabled;
BitField<1, 4, u16> mole_scale;
BitField<5, 5, u16> mole_x_position;
BitField<10, 5, u16> mole_y_position;
} appearance_bits11;
std::array<u16_le, 0xA> author_name;
INSERT_PADDING_BYTES(0x4);
};
static_assert(sizeof(Ver3StoreData) == 0x60, "Ver3StoreData is an invalid size");
struct MiiStoreData {
using Name = std::array<char16_t, 10>;

View File

@@ -0,0 +1,383 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2017 socram8888/amiitool
// SPDX-License-Identifier: MIT
#include <array>
#include <mbedtls/aes.h>
#include <mbedtls/hmac_drbg.h>
#include "common/fs/file.h"
#include "common/fs/path_util.h"
#include "common/logging/log.h"
#include "core/hle/service/mii/mii_manager.h"
#include "core/hle/service/nfp/amiibo_crypto.h"
namespace Service::NFP::AmiiboCrypto {
bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file) {
const auto& amiibo_data = ntag_file.user_memory;
LOG_DEBUG(Service_NFP, "uuid_lock=0x{0:x}", ntag_file.static_lock);
LOG_DEBUG(Service_NFP, "compability_container=0x{0:x}", ntag_file.compability_container);
LOG_INFO(Service_NFP, "write_count={}", amiibo_data.write_counter);
LOG_INFO(Service_NFP, "character_id=0x{0:x}", amiibo_data.model_info.character_id);
LOG_INFO(Service_NFP, "character_variant={}", amiibo_data.model_info.character_variant);
LOG_INFO(Service_NFP, "amiibo_type={}", amiibo_data.model_info.amiibo_type);
LOG_INFO(Service_NFP, "model_number=0x{0:x}", amiibo_data.model_info.model_number);
LOG_INFO(Service_NFP, "series={}", amiibo_data.model_info.series);
LOG_DEBUG(Service_NFP, "fixed_value=0x{0:x}", amiibo_data.model_info.constant_value);
LOG_DEBUG(Service_NFP, "tag_dynamic_lock=0x{0:x}", ntag_file.dynamic_lock);
LOG_DEBUG(Service_NFP, "tag_CFG0=0x{0:x}", ntag_file.CFG0);
LOG_DEBUG(Service_NFP, "tag_CFG1=0x{0:x}", ntag_file.CFG1);
// Validate UUID
constexpr u8 CT = 0x88; // As defined in `ISO / IEC 14443 - 3`
if ((CT ^ ntag_file.uuid[0] ^ ntag_file.uuid[1] ^ ntag_file.uuid[2]) != ntag_file.uuid[3]) {
return false;
}
if ((ntag_file.uuid[4] ^ ntag_file.uuid[5] ^ ntag_file.uuid[6] ^ ntag_file.uuid[7]) !=
ntag_file.uuid[8]) {
return false;
}
// Check against all know constants on an amiibo binary
if (ntag_file.static_lock != 0xE00F) {
return false;
}
if (ntag_file.compability_container != 0xEEFF10F1U) {
return false;
}
if (amiibo_data.constant_value != 0xA5) {
return false;
}
if (amiibo_data.model_info.constant_value != 0x02) {
return false;
}
// dynamic_lock value apparently is not constant
// ntag_file.dynamic_lock == 0x0F0001
if (ntag_file.CFG0 != 0x04000000U) {
return false;
}
if (ntag_file.CFG1 != 0x5F) {
return false;
}
return true;
}
NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data) {
NTAG215File encoded_data{};
memcpy(encoded_data.uuid2.data(), nfc_data.uuid.data() + 0x8, sizeof(encoded_data.uuid2));
encoded_data.static_lock = nfc_data.static_lock;
encoded_data.compability_container = nfc_data.compability_container;
encoded_data.hmac_data = nfc_data.user_memory.hmac_data;
encoded_data.constant_value = nfc_data.user_memory.constant_value;
encoded_data.write_counter = nfc_data.user_memory.write_counter;
encoded_data.settings = nfc_data.user_memory.settings;
encoded_data.owner_mii = nfc_data.user_memory.owner_mii;
encoded_data.title_id = nfc_data.user_memory.title_id;
encoded_data.applicaton_write_counter = nfc_data.user_memory.applicaton_write_counter;
encoded_data.application_area_id = nfc_data.user_memory.application_area_id;
encoded_data.unknown = nfc_data.user_memory.unknown;
encoded_data.hash = nfc_data.user_memory.hash;
encoded_data.application_area = nfc_data.user_memory.application_area;
encoded_data.hmac_tag = nfc_data.user_memory.hmac_tag;
memcpy(encoded_data.uuid.data(), nfc_data.uuid.data(), sizeof(encoded_data.uuid));
encoded_data.model_info = nfc_data.user_memory.model_info;
encoded_data.keygen_salt = nfc_data.user_memory.keygen_salt;
encoded_data.dynamic_lock = nfc_data.dynamic_lock;
encoded_data.CFG0 = nfc_data.CFG0;
encoded_data.CFG1 = nfc_data.CFG1;
encoded_data.password = nfc_data.password;
return encoded_data;
}
EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data) {
EncryptedNTAG215File nfc_data{};
memcpy(nfc_data.uuid.data() + 0x8, encoded_data.uuid2.data(), sizeof(encoded_data.uuid2));
memcpy(nfc_data.uuid.data(), encoded_data.uuid.data(), sizeof(encoded_data.uuid));
nfc_data.static_lock = encoded_data.static_lock;
nfc_data.compability_container = encoded_data.compability_container;
nfc_data.user_memory.hmac_data = encoded_data.hmac_data;
nfc_data.user_memory.constant_value = encoded_data.constant_value;
nfc_data.user_memory.write_counter = encoded_data.write_counter;
nfc_data.user_memory.settings = encoded_data.settings;
nfc_data.user_memory.owner_mii = encoded_data.owner_mii;
nfc_data.user_memory.title_id = encoded_data.title_id;
nfc_data.user_memory.applicaton_write_counter = encoded_data.applicaton_write_counter;
nfc_data.user_memory.application_area_id = encoded_data.application_area_id;
nfc_data.user_memory.unknown = encoded_data.unknown;
nfc_data.user_memory.hash = encoded_data.hash;
nfc_data.user_memory.application_area = encoded_data.application_area;
nfc_data.user_memory.hmac_tag = encoded_data.hmac_tag;
nfc_data.user_memory.model_info = encoded_data.model_info;
nfc_data.user_memory.keygen_salt = encoded_data.keygen_salt;
nfc_data.dynamic_lock = encoded_data.dynamic_lock;
nfc_data.CFG0 = encoded_data.CFG0;
nfc_data.CFG1 = encoded_data.CFG1;
nfc_data.password = encoded_data.password;
return nfc_data;
}
u32 GetTagPassword(const TagUuid& uuid) {
// Verifiy that the generated password is correct
u32 password = 0xAA ^ (uuid[1] ^ uuid[3]);
password &= (0x55 ^ (uuid[2] ^ uuid[4])) << 8;
password &= (0xAA ^ (uuid[3] ^ uuid[5])) << 16;
password &= (0x55 ^ (uuid[4] ^ uuid[6])) << 24;
return password;
}
HashSeed GetSeed(const NTAG215File& data) {
HashSeed seed{
.magic = data.write_counter,
.padding = {},
.uuid1 = {},
.uuid2 = {},
.keygen_salt = data.keygen_salt,
};
// Copy the first 8 bytes of uuid
memcpy(seed.uuid1.data(), data.uuid.data(), sizeof(seed.uuid1));
memcpy(seed.uuid2.data(), data.uuid.data(), sizeof(seed.uuid2));
return seed;
}
std::vector<u8> GenerateInternalKey(const InternalKey& key, const HashSeed& seed) {
const std::size_t seedPart1Len = sizeof(key.magic_bytes) - key.magic_length;
const std::size_t string_size = key.type_string.size();
std::vector<u8> output(string_size + seedPart1Len);
// Copy whole type string
memccpy(output.data(), key.type_string.data(), '\0', string_size);
// Append (16 - magic_length) from the input seed
memcpy(output.data() + string_size, &seed, seedPart1Len);
// Append all bytes from magicBytes
output.insert(output.end(), key.magic_bytes.begin(),
key.magic_bytes.begin() + key.magic_length);
output.insert(output.end(), seed.uuid1.begin(), seed.uuid1.end());
output.insert(output.end(), seed.uuid2.begin(), seed.uuid2.end());
for (std::size_t i = 0; i < sizeof(seed.keygen_salt); i++) {
output.emplace_back(static_cast<u8>(seed.keygen_salt[i] ^ key.xor_pad[i]));
}
return output;
}
void CryptoInit(CryptoCtx& ctx, mbedtls_md_context_t& hmac_ctx, const HmacKey& hmac_key,
const std::vector<u8>& seed) {
// Initialize context
ctx.used = false;
ctx.counter = 0;
ctx.buffer_size = sizeof(ctx.counter) + seed.size();
memcpy(ctx.buffer.data() + sizeof(u16), seed.data(), seed.size());
// Initialize HMAC context
mbedtls_md_init(&hmac_ctx);
mbedtls_md_setup(&hmac_ctx, mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), 1);
mbedtls_md_hmac_starts(&hmac_ctx, hmac_key.data(), hmac_key.size());
}
void CryptoStep(CryptoCtx& ctx, mbedtls_md_context_t& hmac_ctx, DrgbOutput& output) {
// If used at least once, reinitialize the HMAC
if (ctx.used) {
mbedtls_md_hmac_reset(&hmac_ctx);
}
ctx.used = true;
// Store counter in big endian, and increment it
ctx.buffer[0] = static_cast<u8>(ctx.counter >> 8);
ctx.buffer[1] = static_cast<u8>(ctx.counter >> 0);
ctx.counter++;
// Do HMAC magic
mbedtls_md_hmac_update(&hmac_ctx, reinterpret_cast<const unsigned char*>(ctx.buffer.data()),
ctx.buffer_size);
mbedtls_md_hmac_finish(&hmac_ctx, output.data());
}
DerivedKeys GenerateKey(const InternalKey& key, const NTAG215File& data) {
const auto seed = GetSeed(data);
// Generate internal seed
const std::vector<u8> internal_key = GenerateInternalKey(key, seed);
// Initialize context
CryptoCtx ctx{};
mbedtls_md_context_t hmac_ctx;
CryptoInit(ctx, hmac_ctx, key.hmac_key, internal_key);
// Generate derived keys
DerivedKeys derived_keys{};
std::array<DrgbOutput, 2> temp{};
CryptoStep(ctx, hmac_ctx, temp[0]);
CryptoStep(ctx, hmac_ctx, temp[1]);
memcpy(&derived_keys, temp.data(), sizeof(DerivedKeys));
// Cleanup context
mbedtls_md_free(&hmac_ctx);
return derived_keys;
}
void Cipher(const DerivedKeys& keys, const NTAG215File& in_data, NTAG215File& out_data) {
mbedtls_aes_context aes;
std::size_t nc_off = 0;
std::array<u8, sizeof(keys.aes_iv)> nonce_counter{};
std::array<u8, sizeof(keys.aes_iv)> stream_block{};
const auto aes_key_size = static_cast<u32>(keys.aes_key.size() * 8);
mbedtls_aes_setkey_enc(&aes, keys.aes_key.data(), aes_key_size);
memcpy(nonce_counter.data(), keys.aes_iv.data(), sizeof(keys.aes_iv));
constexpr std::size_t encrypted_data_size = HMAC_TAG_START - SETTINGS_START;
mbedtls_aes_crypt_ctr(&aes, encrypted_data_size, &nc_off, nonce_counter.data(),
stream_block.data(),
reinterpret_cast<const unsigned char*>(&in_data.settings),
reinterpret_cast<unsigned char*>(&out_data.settings));
// Copy the rest of the data directly
out_data.uuid2 = in_data.uuid2;
out_data.static_lock = in_data.static_lock;
out_data.compability_container = in_data.compability_container;
out_data.constant_value = in_data.constant_value;
out_data.write_counter = in_data.write_counter;
out_data.uuid = in_data.uuid;
out_data.model_info = in_data.model_info;
out_data.keygen_salt = in_data.keygen_salt;
out_data.dynamic_lock = in_data.dynamic_lock;
out_data.CFG0 = in_data.CFG0;
out_data.CFG1 = in_data.CFG1;
out_data.password = in_data.password;
}
bool LoadKeys(InternalKey& locked_secret, InternalKey& unfixed_info) {
const auto yuzu_keys_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::KeysDir);
const Common::FS::IOFile keys_file{yuzu_keys_dir / "key_retail.bin",
Common::FS::FileAccessMode::Read,
Common::FS::FileType::BinaryFile};
if (!keys_file.IsOpen()) {
LOG_ERROR(Service_NFP, "No keys detected");
return false;
}
if (keys_file.Read(unfixed_info) != 1) {
LOG_ERROR(Service_NFP, "Failed to read unfixed_info");
return false;
}
if (keys_file.Read(locked_secret) != 1) {
LOG_ERROR(Service_NFP, "Failed to read locked-secret");
return false;
}
return true;
}
bool DecodeAmiibo(const EncryptedNTAG215File& encrypted_tag_data, NTAG215File& tag_data) {
InternalKey locked_secret{};
InternalKey unfixed_info{};
if (!LoadKeys(locked_secret, unfixed_info)) {
return false;
}
// Generate keys
NTAG215File encoded_data = NfcDataToEncodedData(encrypted_tag_data);
const auto data_keys = GenerateKey(unfixed_info, encoded_data);
const auto tag_keys = GenerateKey(locked_secret, encoded_data);
// Decrypt
Cipher(data_keys, encoded_data, tag_data);
// Regenerate tag HMAC. Note: order matters, data HMAC depends on tag HMAC!
constexpr std::size_t input_length = DYNAMIC_LOCK_START - UUID_START;
mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), tag_keys.hmac_key.data(),
sizeof(HmacKey), reinterpret_cast<const unsigned char*>(&tag_data.uuid),
input_length, reinterpret_cast<unsigned char*>(&tag_data.hmac_tag));
// Regenerate data HMAC
constexpr std::size_t input_length2 = DYNAMIC_LOCK_START - WRITE_COUNTER_START;
mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), data_keys.hmac_key.data(),
sizeof(HmacKey),
reinterpret_cast<const unsigned char*>(&tag_data.write_counter), input_length2,
reinterpret_cast<unsigned char*>(&tag_data.hmac_data));
if (tag_data.hmac_data != encrypted_tag_data.user_memory.hmac_data) {
LOG_ERROR(Service_NFP, "hmac_data doesn't match");
return false;
}
if (tag_data.hmac_tag != encrypted_tag_data.user_memory.hmac_tag) {
LOG_ERROR(Service_NFP, "hmac_tag doesn't match");
return false;
}
return true;
}
bool EncodeAmiibo(const NTAG215File& tag_data, EncryptedNTAG215File& encrypted_tag_data) {
InternalKey locked_secret{};
InternalKey unfixed_info{};
if (!LoadKeys(locked_secret, unfixed_info)) {
return false;
}
// Generate keys
const auto data_keys = GenerateKey(unfixed_info, tag_data);
const auto tag_keys = GenerateKey(locked_secret, tag_data);
NTAG215File encoded_tag_data{};
// Generate tag HMAC
constexpr std::size_t input_length = DYNAMIC_LOCK_START - UUID_START;
constexpr std::size_t input_length2 = HMAC_TAG_START - WRITE_COUNTER_START;
mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), tag_keys.hmac_key.data(),
sizeof(HmacKey), reinterpret_cast<const unsigned char*>(&tag_data.uuid),
input_length, reinterpret_cast<unsigned char*>(&encoded_tag_data.hmac_tag));
// Init mbedtls HMAC context
mbedtls_md_context_t ctx;
mbedtls_md_init(&ctx);
mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), 1);
// Generate data HMAC
mbedtls_md_hmac_starts(&ctx, data_keys.hmac_key.data(), sizeof(HmacKey));
mbedtls_md_hmac_update(&ctx, reinterpret_cast<const unsigned char*>(&tag_data.write_counter),
input_length2); // Data
mbedtls_md_hmac_update(&ctx, reinterpret_cast<unsigned char*>(&encoded_tag_data.hmac_tag),
sizeof(HashData)); // Tag HMAC
mbedtls_md_hmac_update(&ctx, reinterpret_cast<const unsigned char*>(&tag_data.uuid),
input_length);
mbedtls_md_hmac_finish(&ctx, reinterpret_cast<unsigned char*>(&encoded_tag_data.hmac_data));
// HMAC cleanup
mbedtls_md_free(&ctx);
// Encrypt
Cipher(data_keys, tag_data, encoded_tag_data);
// Convert back to hardware
encrypted_tag_data = EncodedDataToNfcData(encoded_tag_data);
return true;
}
} // namespace Service::NFP::AmiiboCrypto

View File

@@ -0,0 +1,98 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <array>
#include "core/hle/service/nfp/amiibo_types.h"
struct mbedtls_md_context_t;
namespace Service::NFP::AmiiboCrypto {
// Byte locations in Service::NFP::NTAG215File
constexpr std::size_t HMAC_DATA_START = 0x8;
constexpr std::size_t SETTINGS_START = 0x2c;
constexpr std::size_t WRITE_COUNTER_START = 0x29;
constexpr std::size_t HMAC_TAG_START = 0x1B4;
constexpr std::size_t UUID_START = 0x1D4;
constexpr std::size_t DYNAMIC_LOCK_START = 0x208;
using HmacKey = std::array<u8, 0x10>;
using DrgbOutput = std::array<u8, 0x20>;
struct HashSeed {
u16 magic;
std::array<u8, 0xE> padding;
std::array<u8, 0x8> uuid1;
std::array<u8, 0x8> uuid2;
std::array<u8, 0x20> keygen_salt;
};
static_assert(sizeof(HashSeed) == 0x40, "HashSeed is an invalid size");
struct InternalKey {
HmacKey hmac_key;
std::array<char, 0xE> type_string;
u8 reserved;
u8 magic_length;
std::array<u8, 0x10> magic_bytes;
std::array<u8, 0x20> xor_pad;
};
static_assert(sizeof(InternalKey) == 0x50, "InternalKey is an invalid size");
static_assert(std::is_trivially_copyable_v<InternalKey>, "InternalKey must be trivially copyable.");
struct CryptoCtx {
std::array<char, 480> buffer;
bool used;
std::size_t buffer_size;
s16 counter;
};
struct DerivedKeys {
std::array<u8, 0x10> aes_key;
std::array<u8, 0x10> aes_iv;
std::array<u8, 0x10> hmac_key;
};
static_assert(sizeof(DerivedKeys) == 0x30, "DerivedKeys is an invalid size");
/// Validates that the amiibo file is not corrupted
bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file);
/// Converts from encrypted file format to encoded file format
NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data);
/// Converts from encoded file format to encrypted file format
EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data);
/// Returns password needed to allow write access to protected memory
u32 GetTagPassword(const TagUuid& uuid);
// Generates Seed needed for key derivation
HashSeed GetSeed(const NTAG215File& data);
// Middle step on the generation of derived keys
std::vector<u8> GenerateInternalKey(const InternalKey& key, const HashSeed& seed);
// Initializes mbedtls context
void CryptoInit(CryptoCtx& ctx, mbedtls_md_context_t& hmac_ctx, const HmacKey& hmac_key,
const std::vector<u8>& seed);
// Feeds data to mbedtls context to generate the derived key
void CryptoStep(CryptoCtx& ctx, mbedtls_md_context_t& hmac_ctx, DrgbOutput& output);
// Generates the derived key from amiibo data
DerivedKeys GenerateKey(const InternalKey& key, const NTAG215File& data);
// Encodes or decodes amiibo data
void Cipher(const DerivedKeys& keys, const NTAG215File& in_data, NTAG215File& out_data);
/// Loads both amiibo keys from key_retail.bin
bool LoadKeys(InternalKey& locked_secret, InternalKey& unfixed_info);
/// Decodes encripted amiibo data returns true if output is valid
bool DecodeAmiibo(const EncryptedNTAG215File& encrypted_tag_data, NTAG215File& tag_data);
/// Encodes plain amiibo data returns true if output is valid
bool EncodeAmiibo(const NTAG215File& tag_data, EncryptedNTAG215File& encrypted_tag_data);
} // namespace Service::NFP::AmiiboCrypto

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