Compare commits

..

12 commits

Author SHA1 Message Date
PixelyIon
036416fde7 Address CR Comments (#150)
Correct a single grammatical error
2021-03-23 02:24:16 +05:30
PixelyIon
662470c415 Add Default Run Configuration + Ignore runConfigurations.xml
This commit adds a default runConfiguration which just launches the default activity without any special parameters, it also adds `.idea/runConfigurations.xml` to `.gitignore` as it is a legacy file which may be generated by older versions of Android Studio.
2021-03-23 02:24:16 +05:30
PixelyIon
5da5b518c5 Revamp README
The README had rendering issues on GitHub Mobile due to the "Special Thanks" ("Credits" previously) images not being correctly sized which has been resolved by removing those images. In addition, the badges at the top had unnecessary extra spacing on mobile which was required on desktop and has been fixed by exploiting differential behavior between them regarding nesting HTML line breaks (`<br>`) within a HTML hyperlink tag (`<a>`) which causes a space on desktop while not showing up on mobile.

This commit also refactors the entire file for better readability by changes such as using **bold** to emphasize points and explicit Markdown dividers (`---`) between sections. 

A distinct change is clarifying that Skyline is not a Ryujinx derivative as we found a lot of people who thought this by:
* Renaming the "Credits" section to "Special Thanks"
* Explicitly specifying that Skyline is not based on it in **bold** at the end of the description
2021-03-23 02:24:16 +05:30
PixelyIon
acc5668274 Disable All Warnings from TZCode
This just disables all compiler warnings generated while compiling TZCode as those are irrelevant while compiling Skyline and need to be tackled in that repository.
2021-03-21 20:34:59 +05:30
PixelyIon
1c5281eb0e Fix BT Audio Stuttering Issues
This fixes audio stuttering which occurred on certain BT audio devices by requesting an exclusive stream from Oboe alongside a low-latency stream.

Co-authored-by: Billy Laws <blaws05@gmail.com>
2021-03-21 20:13:29 +05:30
PixelyIon
b0e53d4aad Extend Perfetto Tracing
Add Tracing for SVCs, Services, NVDRV, and Synchronization Primitives. In addition, fix `TRACE_EVENT_END("guest")` being emitted when a signal is received while being in the guest rather than host which would cause an exception. This commit also disables warnings for the Perfetto library as we do not control fixing them.
2021-03-21 20:13:29 +05:30
PixelyIon
c452cd2bf2 Extend SVC table with names
This extend a descriptor table for the SVCs with names for every SVC alongside their function pointer. The names are then used for logging and eventually tracing.
2021-03-21 20:13:03 +05:30
PixelyIon
711d4f8824 Optimize NvDevice Function Lookup
This essentially optimizes NvDevice in the same way services were, refer to the last commit for details
2021-03-21 20:13:03 +05:30
PixelyIon
5149b333d7 Optimize Service Function Lookup
This moves from using std::function with a this pointer binding (which would likely cause a heap allocation) to returning the this pointer in a structure which implements operator() to do the call with it. It also moves to using const char* for strings from std::string_view which was pointless in this scenario due to it's usage being limited to being a C-string for the most part, it also integrates the class name directly into the string which allows us to avoid runtime string concatenation in libfmt and RTTI for finding the class name.
2021-03-21 20:13:03 +05:30
PixelyIon
4e51d76fa6 Logger Function Prefix Support 2021-03-21 20:13:03 +05:30
Billy Laws
a1d90f053a Implement Perfetto Tracing
This commit implements tracing Skyline using https://perfetto.dev/ for SVCs, IPC, Scheduler and Presentation
2021-03-21 20:13:03 +05:30
PixelyIon
c7de7890b6 Improve KMemory + Fix Service Implementation Warning
* Improve KMemory Comments
* Add parameter prefix 'p-' to `KPrivateMemory::UpdatePermission`
* Fix the missing trailing double quote in missing service prints, this was due to `stringName` being padded with extra 0s
2021-03-21 20:10:27 +05:30
801 changed files with 12391 additions and 64106 deletions

3
.github/FUNDING.yml vendored
View file

@ -1,3 +0,0 @@
# These is the list of funding model platforms used by Skyline
patreon: skyline_emu

View file

@ -1,107 +1,69 @@
name: CI
on:
push:
pull_request:
types: [opened, synchronize, reopened, labeled]
on: [push, pull_request]
jobs:
build:
# Skip 'labeled' events that didn't add the 'ci' label
if: |
github.event_name != 'pull_request' ||
github.event.action != 'labeled' ||
github.event.label.name == 'ci'
runs-on: ubuntu-latest
env:
JVM_OPTS: -Xmx6G
IS_SKYLINE_SIGNED: ${{ secrets.KEYSTORE != '' }}
UPLOAD_ARTIFACTS: ${{ github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'ci') }}
CMAKE_VERSION: "3.22.1"
NDK_VERSION: "25.0.8775105"
steps:
- name: Git Checkout
uses: actions/checkout@v3
with:
submodules: recursive
- name: Git Checkout
uses: actions/checkout@v2
with:
submodules: true
- name: Restore CCache
uses: hendrikmuhs/ccache-action@v1.2
with:
max-size: 3Gi
- name: Restore Gradle Cache
uses: actions/cache@v2
with:
path: /root/.gradle/
key: ${{ runner.os }}-gradle-${{ hashFiles('**/build.gradle') }}
restore-keys: |
${{ runner.os }}-gradle-
- name: Restore Gradle Cache
uses: actions/cache@v3
with:
path: ~/.gradle/
key: ${{ runner.os }}-gradle-${{ hashFiles('**/build.gradle') }}-${{ hashFiles('app/**/*.xml') }}-${{ hashFiles('app/**.kt', 'app/**.java') }}
restore-keys: |
${{ runner.os }}-gradle-${{ hashFiles('**/build.gradle') }}-${{ hashFiles('app/**/*.xml') }}-
${{ runner.os }}-gradle-${{ hashFiles('**/build.gradle') }}-
${{ runner.os }}-gradle-
- name: Restore CXX Cache
uses: actions/cache@v2
with:
path: app/.cxx/
key: ${{ runner.os }}-cxx-${{ hashFiles('**/CMakeLists.txt') }}
restore-keys: |
${{ runner.os }}-cxx-
- name: Install Ninja Build
run: |
sudo apt-get install -y ninja-build
ln -s /usr/bin/ninja .
- name: Setup Environment for Gradle & Ninja Build
run: |
chmod +x gradlew
sudo apt-get install -y ninja-build
- name: Install CMake & Android NDK
run: echo "yes" | $ANDROID_HOME/tools/bin/sdkmanager "cmake;${{ env.CMAKE_VERSION }}" "ndk;${{ env.NDK_VERSION }}" --channel=3 | grep -v = || true
- name: Android Lint
run: ./gradlew --stacktrace lint
- name: Decode Keystore
if: env.IS_SKYLINE_SIGNED == 'true'
env:
KEYSTORE_ENCODED: ${{ secrets.KEYSTORE }}
run: echo $KEYSTORE_ENCODED | base64 --decode > "/home/runner/keystore.jks"
- name: Upload Lint Report
uses: actions/upload-artifact@v2
with:
name: lint-result.html
path: app/build/reports/lint-results.html
- name: Android Assemble
env:
SIGNING_STORE_PATH: "/home/runner/keystore.jks"
SIGNING_STORE_PASSWORD: ${{ secrets.SIGNING_STORE_PASSWORD }}
SIGNING_KEY_ALIAS: ${{ secrets.SIGNING_KEY_ALIAS }}
SIGNING_KEY_PASSWORD: ${{ secrets.SIGNING_KEY_PASSWORD }}
CMAKE_C_COMPILER_LAUNCHER: "ccache"
CMAKE_CXX_COMPILER_LAUNCHER: "ccache"
CCACHE_COMPILERCHECK: "string:${{ env.NDK_VERSION }}"
run: ./gradlew --stacktrace --configuration-cache --build-cache --parallel --configure-on-demand assembleFullRelease assembleFullReldebug
- name: Android Assemble
run: ./gradlew assemble
- name: Rename APKs (Signed)
if: env.IS_SKYLINE_SIGNED == 'true' && env.UPLOAD_ARTIFACTS == 'true'
run: |
mv app/build/outputs/apk/full/reldebug/app-full-reldebug.apk skyline-$GITHUB_RUN_NUMBER-reldebug.apk
mv app/build/outputs/apk/full/release/app-full-release.apk skyline-$GITHUB_RUN_NUMBER-release.apk
- name: Upload Debug APK
uses: actions/upload-artifact@v2
with:
name: app-debug.apk
path: app/build/outputs/apk/debug/
- name: Upload Signed Debug APK
if: env.IS_SKYLINE_SIGNED == 'true' && env.UPLOAD_ARTIFACTS == 'true'
uses: actions/upload-artifact@v3
with:
name: skyline-${{ github.run_number }}-reldebug.apk
path: skyline-${{ github.run_number }}-reldebug.apk
- name: Upload Release APK
uses: actions/upload-artifact@v2
with:
name: app-release.apk
path: app/build/outputs/apk/release/
- name: Upload Signed Release APK
if: env.IS_SKYLINE_SIGNED == 'true' && env.UPLOAD_ARTIFACTS == 'true'
uses: actions/upload-artifact@v3
with:
name: skyline-${{ github.run_number }}-release.apk
path: skyline-${{ github.run_number }}-release.apk
- name: Upload R8 Mapping
uses: actions/upload-artifact@v2
with:
name: mapping.txt
path: app/build/outputs/mapping/release/
- name: Rename APKs (Unsigned)
if: env.IS_SKYLINE_SIGNED == 'false' && env.UPLOAD_ARTIFACTS == 'true'
run: |
mv app/build/outputs/apk/full/reldebug/app-full-reldebug.apk skyline-$GITHUB_RUN_NUMBER-unsigned-reldebug.apk
mv app/build/outputs/apk/full/release/app-full-release.apk skyline-$GITHUB_RUN_NUMBER-unsigned-release.apk
- name: Upload Unsigned Debug APK
if: env.IS_SKYLINE_SIGNED == 'false' && env.UPLOAD_ARTIFACTS == 'true'
uses: actions/upload-artifact@v3
with:
name: skyline-${{ github.run_number }}-unsigned-reldebug.apk
path: skyline-${{ github.run_number }}-unsigned-reldebug.apk
- name: Upload Unsigned Release APK
if: env.IS_SKYLINE_SIGNED == 'false' && env.UPLOAD_ARTIFACTS == 'true'
uses: actions/upload-artifact@v3
with:
name: skyline-${{ github.run_number }}-unsigned-release.apk
path: skyline-${{ github.run_number }}-unsigned-release.apk
- name: Delete Build Folder
run: rm -rf app/build/

View file

@ -1,20 +0,0 @@
name: Edge CI
on:
pull_request_target:
types: [synchronize, reopened, closed, labeled, unlabeled]
jobs:
update-patches:
# Run if the 'edge' label was added/removed, or if an edge PR was synchronized/reopened/closed
if: |
github.event.label.name == 'edge' ||
!contains(github.event.action, 'labeled') && contains(github.event.pull_request.labels.*.name, 'edge')
runs-on: ubuntu-latest
steps:
- name: Update Edge patches
uses: peter-evans/repository-dispatch@v2
with:
token: ${{ secrets.EDGE_PATCH_PAT }}
repository: skyline-emu/edge-patch
event-type: update-patches

158
.gitignore vendored
View file

@ -1,111 +1,99 @@
# Source: https://github.com/github/gitignore/blob/master/Android.gitignore
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# Built application files
*.apk
*.aar
*.ap_
*.aab
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# Files for the ART/Dalvik VM
# Files for the ART/Dalvik VM
*.dex
# Java class files
*.class
# Generated files
bin/
gen/
out/
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
.idea/modules.xml
.idea/*.iml
.idea/modules
*.iml
*.ipr
# CMake
cmake-build-*/
# Gradle files
.gradle/
build/
# Local configuration file (sdk path, etc)
# Android Studio
.idea/caches
.idea/assetWizardSettings.xml
.idea/runConfigurations.xml
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# Local configuration file (SDK path, etc)
local.properties
# Proguard folder generated by Eclipse
proguard/
# External native build folder generated in Android Studio 2.2 and later
.externalNativeBuild/
.cxx/
# Log Files
*.log
# IntelliJ
out/
# Android Studio Navigation editor temp files
.navigation/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
# Android Studio captures folder
captures/
# IntelliJ
*.iml
.idea/workspace.xml
.idea/tasks.xml
.idea/gradle.xml
.idea/assetWizardSettings.xml
.idea/dictionaries
.idea/libraries
.idea/jarRepositories.xml
.idea/misc.xml
.idea/compiler.xml
deploymentTargetDropDown.xml
render.experimental.xml
# Android Studio 3 in .gitignore file.
.idea/caches
.idea/modules
.idea/modules.xml
.idea/navEditor.xml
.idea/runConfigurations.xml
# Keystore files
*.jks
*.keystore
# External native build folder generated in Android Studio 2.2 and later
.externalNativeBuild
.cxx/
# Google Services (e.g. APIs or Firebase)
google-services.json
# Freeline
freeline.py
freeline/
freeline_project_description.json
# fastlane
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots
fastlane/test_output
fastlane/readme.md
# Version control
vcs.xml
# lint
lint/intermediates/
lint/generated/
lint/outputs/
lint/tmp/
lint/reports/
# Android Profiling
*.hprof
# VSCode
.vscode/
# Discord plugin for IntelliJ IDEA
.idea/discord.xml
# Adreno Validation Layer
libVkLayer_adreno.so
# Skyline logs
*.sklog
# Android Studio Profiler Traces
*.trace
# Output Metadata
output-metadata.json

62
.gitmodules vendored
View file

@ -1,65 +1,27 @@
[submodule "{fmt}"]
[submodule "app/libraries/fmt"]
path = app/libraries/fmt
url = https://github.com/fmtlib/fmt
[submodule "Oboe"]
[submodule "app/libraries/oboe"]
path = app/libraries/oboe
url = https://github.com/google/oboe
branch = 1.3-stable
[submodule "LZ4"]
[submodule "app/libraries/vkhpp"]
path = app/libraries/vkhpp
url = https://github.com/skyline-emu/vkhpp
[submodule "app/libraries/lz4"]
path = app/libraries/lz4
url = https://github.com/lz4/lz4.git
[submodule "Frozen"]
[submodule "app/libraries/frozen"]
path = app/libraries/frozen
url = https://github.com/serge-sans-paille/frozen
[submodule "tzcode"]
[submodule "app/libraries/pugixml"]
path = app/libraries/pugixml
url = https://github.com/zeux/pugixml.git
[submodule "app/libraries/tzcode"]
path = app/libraries/tzcode
url = https://github.com/skyline-emu/tz
branch = master
[submodule "Perfetto"]
[submodule "app/libraries/perfetto"]
path = app/libraries/perfetto
url = https://android.googlesource.com/platform/external/perfetto
branch = releases/v12.x
[submodule "Vulkan-Hpp"]
path = app/libraries/vkhpp
url = https://github.com/KhronosGroup/Vulkan-Hpp
[submodule "Vulkan Memory Allocator"]
path = app/libraries/vkma
url = https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator
[submodule "Mbed TLS"]
path = app/libraries/mbedtls
url = https://github.com/ARMmbed/mbedtls
[submodule "Opus"]
path = app/libraries/opus
url = https://github.com/xiph/opus
[submodule "Boost"]
path = app/libraries/boost
url = https://github.com/skyline-emu/boost.git
ignore = all
[submodule "LLVM"]
path = app/libraries/llvm
url = https://github.com/llvm/llvm-project.git
shallow = true
[submodule "C++ Range v3"]
path = app/libraries/range
url = https://github.com/ericniebler/range-v3
[submodule "Sirit"]
path = app/libraries/sirit
url = https://github.com/yuzu-emu/sirit
[submodule "Shader Compiler"]
path = app/libraries/shader-compiler
url = https://github.com/skyline-emu/shader-compiler.git
[submodule "libadrenotools"]
path = app/libraries/adrenotools
url = https://github.com/bylaws/libadrenotools/
[submodule "app/libraries/robin-map"]
path = app/libraries/robin-map
url = https://github.com/Tessil/robin-map
[submodule "app/libraries/thread-pool"]
path = app/libraries/thread-pool
url = https://github.com/bshoshany/thread-pool.git
[submodule "app/libraries/cubeb"]
path = app/libraries/cubeb
url = https://github.com/skyline-emu/cubeb
[submodule "app/libraries/audio-core"]
path = app/libraries/audio-core
url = https://github.com/skyline-emu/audio-core

View file

@ -7,6 +7,22 @@
<option name="FORMATTER_OFF_TAG" value="@fmt:off" />
<option name="SOFT_MARGINS" value="80,140" />
<JetCodeStyleSettings>
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
<value>
<package name="java.util" alias="false" withSubpackages="false" />
<package name="kotlinx.android.synthetic" alias="false" withSubpackages="true" />
<package name="io.ktor" alias="false" withSubpackages="true" />
</value>
</option>
<option name="PACKAGES_IMPORT_LAYOUT">
<value>
<package name="" alias="false" withSubpackages="true" />
<package name="java" alias="false" withSubpackages="true" />
<package name="javax" alias="false" withSubpackages="true" />
<package name="kotlin" alias="false" withSubpackages="true" />
<package name="" alias="true" withSubpackages="true" />
</value>
</option>
<option name="SPACE_BEFORE_TYPE_COLON" value="true" />
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings>
@ -25,7 +41,6 @@
<option name="CLASS_CONSTRUCTOR_INIT_LIST_WRAP" value="0" />
<option name="SUPERCLASS_LIST_WRAP" value="0" />
<option name="ALIGN_INIT_LIST_IN_COLUMNS" value="false" />
<option name="SPACE_BEFORE_COLON_IN_FOREACH" value="true" />
<option name="SPACE_BEFORE_DICTIONARY_LITERAL_COLON" value="true" />
<option name="ADD_BRIEF_TAG" value="true" />
<option name="HEADER_GUARD_STYLE_PATTERN" value="${PROJECT_NAME}_${PROJECT_REL_PATH}_${FILE_NAME}_${EXT}" />
@ -55,23 +70,12 @@
</option>
</Objective-C>
<Objective-C-extensions>
<rules>
<rule entity="NAMESPACE" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />
<rule entity="MACRO" visibility="ANY" specifier="ANY" prefix="" style="SCREAMING_SNAKE_CASE" suffix="" />
<rule entity="CLASS" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
<rule entity="ENUM" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
<rule entity="ENUMERATOR" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
<rule entity="TYPEDEF" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
<rule entity="UNION" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
<rule entity="CLASS_MEMBER_FUNCTION,STRUCT_MEMBER_FUNCTION" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
<rule entity="CLASS_MEMBER_FIELD,STRUCT_MEMBER_FIELD" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />
<rule entity="GLOBAL_FUNCTION" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
<rule entity="GLOBAL_VARIABLE" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
<rule entity="PARAMETER" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />
<rule entity="LOCAL_VARIABLE" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />
</rules>
</Objective-C-extensions>
<Objective-C-extensions>
<extensions>
<pair source="cpp" header="h" fileNamingConvention="SNAKE_CASE" />
<pair source="c" header="h" fileNamingConvention="SNAKE_CASE" />
<pair source="cpp" header="h" fileNamingConvention="PASCAL_CASE" />
<pair source="cpp" header="h" fileNamingConvention="NONE" />
</extensions>
<rules>
<rule entity="NAMESPACE" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />
<rule entity="MACRO" visibility="ANY" specifier="ANY" prefix="" style="SCREAMING_SNAKE_CASE" suffix="" />
@ -122,10 +126,6 @@
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="XML">
<option name="FORCE_REARRANGE_MODE" value="1" />
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions>
<arrangement>
<rules>
<section>

6
.idea/compiler.xml generated Normal file
View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="11" />
</component>
</project>

View file

@ -81,6 +81,7 @@
<inspection_tool class="AssertsWithoutMessages" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AssertsWithoutMessagesTestNG" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AssignmentOrReturnOfFieldWithMutableType" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AssignmentToCatchBlockParameter" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AssignmentToForLoopParameter" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_checkForeachParameters" value="false" />
</inspection_tool>
@ -91,6 +92,7 @@
<inspection_tool class="AssignmentToNull" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AssignmentToStaticFieldFromInstanceMethod" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AssignmentToSuperclassField" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AssignmentUsedAsCondition" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AutoBoxing" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoreAddedToCollection" value="false" />
</inspection_tool>
@ -120,6 +122,7 @@
</inspection_tool>
<inspection_tool class="BadOddness" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="BigDecimalEquals" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="BigDecimalLegacyMethod" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="BlockMarkerComments" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="BooleanExpressionMayBeConditional" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="BooleanMethodNameMustStartWithQuestion" enabled="true" level="WARNING" enabled_by_default="true">
@ -168,7 +171,7 @@
</inspection_tool>
<inspection_tool class="CheckedExceptionClass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ClangTidy" enabled="true" level="WARNING" enabled_by_default="true">
<option name="clangTidyChecks" value="-*,bugprone-argument-comment,bugprone-assert-side-effect,bugprone-bad-signal-to-kill-thread,bugprone-branch-clone,bugprone-copy-constructor-init,bugprone-dangling-handle,bugprone-dynamic-static-initializers,bugprone-fold-init-type,bugprone-forward-declaration-namespace,bugprone-forwarding-reference-overload,bugprone-inaccurate-erase,bugprone-incorrect-roundings,bugprone-integer-division,bugprone-lambda-function-name,bugprone-macro-repeated-side-effects,bugprone-misplaced-operator-in-strlen-in-alloc,bugprone-misplaced-pointer-arithmetic-in-alloc,bugprone-misplaced-widening-cast,bugprone-move-forwarding-reference,bugprone-multiple-statement-macro,bugprone-no-escape,bugprone-not-null-terminated-result,bugprone-parent-virtual-call,bugprone-posix-return,bugprone-reserved-identifier,bugprone-sizeof-container,bugprone-sizeof-expression,bugprone-spuriously-wake-up-functions,bugprone-string-constructor,bugprone-string-integer-assignment,bugprone-string-literal-with-embedded-nul,bugprone-suspicious-enum-usage,bugprone-suspicious-include,bugprone-suspicious-memset-usage,bugprone-suspicious-missing-comma,bugprone-suspicious-semicolon,bugprone-suspicious-string-compare,bugprone-swapped-arguments,bugprone-terminating-continue,bugprone-throw-keyword-missing,bugprone-too-small-loop-variable,bugprone-undefined-memory-manipulation,bugprone-undelegated-constructor,bugprone-unhandled-self-assignment,bugprone-unused-raii,bugprone-unused-return-value,bugprone-use-after-move,bugprone-virtual-near-miss,cert-dcl21-cpp,cert-dcl58-cpp,cert-err34-c,cert-err52-cpp,cert-err58-cpp,cert-err60-cpp,cert-flp30-c,cert-msc50-cpp,cert-msc51-cpp,cert-str34-c,cppcoreguidelines-interfaces-global-init,cppcoreguidelines-narrowing-conversions,cppcoreguidelines-pro-type-member-init,cppcoreguidelines-pro-type-static-cast-downcast,cppcoreguidelines-slicing,google-default-arguments,google-explicit-constructor,google-runtime-operator,hicpp-exception-baseclass,hicpp-multiway-paths-covered,misc-misplaced-const,misc-new-delete-overloads,misc-no-recursion,misc-non-copyable-objects,misc-throw-by-value-catch-by-reference,misc-unconventional-assign-operator,misc-uniqueptr-reset-release,modernize-avoid-bind,modernize-concat-nested-namespaces,modernize-deprecated-ios-base-aliases,modernize-loop-convert,modernize-make-shared,modernize-make-unique,modernize-pass-by-value,modernize-raw-string-literal,modernize-redundant-void-arg,modernize-replace-auto-ptr,modernize-replace-disallow-copy-and-assign-macro,modernize-replace-random-shuffle,modernize-return-braced-init-list,modernize-shrink-to-fit,modernize-unary-static-assert,modernize-use-auto,modernize-use-bool-literals,modernize-use-emplace,modernize-use-equals-default,modernize-use-equals-delete,modernize-use-nodiscard,modernize-use-noexcept,modernize-use-nullptr,modernize-use-override,modernize-use-transparent-functors,modernize-use-uncaught-exceptions,mpi-buffer-deref,mpi-type-mismatch,openmp-use-default-none,performance-faster-string-find,performance-for-range-copy,performance-implicit-conversion-in-loop,performance-inefficient-algorithm,performance-inefficient-string-concatenation,performance-inefficient-vector-operation,performance-move-const-arg,performance-move-constructor-init,performance-no-automatic-move,performance-noexcept-move-constructor,performance-trivially-destructible,performance-type-promotion-in-math-fn,performance-unnecessary-copy-initialization,performance-unnecessary-value-param,portability-simd-intrinsics,readability-avoid-const-params-in-decls,readability-const-return-type,readability-container-size-empty,readability-convert-member-functions-to-static,readability-delete-null-pointer,readability-deleted-default,readability-inconsistent-declaration-parameter-name,readability-misleading-indentation,readability-misplaced-array-index,readability-redundant-control-flow,readability-redundant-declaration,readability-redundant-function-ptr-dereference,readability-redundant-smartptr-get,readability-redundant-string-cstr,readability-redundant-string-init,readability-simplify-subscript-expr,readability-static-accessed-through-instance,readability-static-definition-in-anonymous-namespace,readability-string-compare,readability-uniqueptr-delete-release,readability-use-anyofallof" />
<option name="clangTidyChecks" value="-*,bugprone-argument-comment,bugprone-assert-side-effect,bugprone-bad-signal-to-kill-thread,bugprone-branch-clone,bugprone-copy-constructor-init,bugprone-dangling-handle,bugprone-dynamic-static-initializers,bugprone-fold-init-type,bugprone-forward-declaration-namespace,bugprone-forwarding-reference-overload,bugprone-inaccurate-erase,bugprone-incorrect-roundings,bugprone-integer-division,bugprone-lambda-function-name,bugprone-macro-parentheses,bugprone-macro-repeated-side-effects,bugprone-misplaced-operator-in-strlen-in-alloc,bugprone-misplaced-pointer-arithmetic-in-alloc,bugprone-misplaced-widening-cast,bugprone-move-forwarding-reference,bugprone-multiple-statement-macro,bugprone-no-escape,bugprone-not-null-terminated-result,bugprone-parent-virtual-call,bugprone-posix-return,bugprone-reserved-identifier,bugprone-sizeof-container,bugprone-sizeof-expression,bugprone-spuriously-wake-up-functions,bugprone-string-constructor,bugprone-string-integer-assignment,bugprone-string-literal-with-embedded-nul,bugprone-suspicious-enum-usage,bugprone-suspicious-include,bugprone-suspicious-memset-usage,bugprone-suspicious-missing-comma,bugprone-suspicious-semicolon,bugprone-suspicious-string-compare,bugprone-swapped-arguments,bugprone-terminating-continue,bugprone-throw-keyword-missing,bugprone-too-small-loop-variable,bugprone-undefined-memory-manipulation,bugprone-undelegated-constructor,bugprone-unhandled-self-assignment,bugprone-unused-raii,bugprone-unused-return-value,bugprone-use-after-move,bugprone-virtual-near-miss,cert-dcl21-cpp,cert-dcl58-cpp,cert-err34-c,cert-err52-cpp,cert-err60-cpp,cert-flp30-c,cert-msc50-cpp,cert-msc51-cpp,cert-str34-c,cppcoreguidelines-interfaces-global-init,cppcoreguidelines-narrowing-conversions,cppcoreguidelines-pro-type-static-cast-downcast,cppcoreguidelines-slicing,google-default-arguments,google-explicit-constructor,google-runtime-operator,hicpp-exception-baseclass,hicpp-multiway-paths-covered,misc-misplaced-const,misc-new-delete-overloads,misc-no-recursion,misc-non-copyable-objects,misc-throw-by-value-catch-by-reference,misc-unconventional-assign-operator,misc-uniqueptr-reset-release,modernize-avoid-bind,modernize-concat-nested-namespaces,modernize-deprecated-headers,modernize-deprecated-ios-base-aliases,modernize-loop-convert,modernize-make-shared,modernize-make-unique,modernize-pass-by-value,modernize-raw-string-literal,modernize-redundant-void-arg,modernize-replace-auto-ptr,modernize-replace-disallow-copy-and-assign-macro,modernize-replace-random-shuffle,modernize-return-braced-init-list,modernize-shrink-to-fit,modernize-unary-static-assert,modernize-use-auto,modernize-use-bool-literals,modernize-use-emplace,modernize-use-equals-default,modernize-use-equals-delete,modernize-use-nodiscard,modernize-use-noexcept,modernize-use-nullptr,modernize-use-override,modernize-use-transparent-functors,modernize-use-uncaught-exceptions,mpi-buffer-deref,mpi-type-mismatch,openmp-use-default-none,performance-faster-string-find,performance-for-range-copy,performance-implicit-conversion-in-loop,performance-inefficient-algorithm,performance-inefficient-string-concatenation,performance-inefficient-vector-operation,performance-move-const-arg,performance-move-constructor-init,performance-no-automatic-move,performance-noexcept-move-constructor,performance-trivially-destructible,performance-type-promotion-in-math-fn,performance-unnecessary-copy-initialization,performance-unnecessary-value-param,portability-simd-intrinsics,readability-avoid-const-params-in-decls,readability-const-return-type,readability-container-size-empty,readability-convert-member-functions-to-static,readability-delete-null-pointer,readability-deleted-default,readability-inconsistent-declaration-parameter-name,readability-make-member-function-const,readability-misleading-indentation,readability-misplaced-array-index,readability-non-const-parameter,readability-redundant-control-flow,readability-redundant-declaration,readability-redundant-function-ptr-dereference,readability-redundant-smartptr-get,readability-redundant-string-cstr,readability-redundant-string-init,readability-simplify-subscript-expr,readability-static-accessed-through-instance,readability-static-definition-in-anonymous-namespace,readability-string-compare,readability-uniqueptr-delete-release,readability-use-anyofallof" />
</inspection_tool>
<inspection_tool class="ClassComplexity" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_limit" value="80" />
@ -244,6 +247,7 @@
<inspection_tool class="CompareToUsesNonFinalVariable" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ComparisonOfShortAndChar" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ConditionSignal" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ConditionalExpressionWithIdenticalBranches" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ConfusingFloatingPointLiteral" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ConfusingMainMethod" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ConfusingOctalEscape" enabled="true" level="WARNING" enabled_by_default="true" />
@ -257,13 +261,11 @@
<inspection_tool class="ConstantConditionsOC" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="ConstantDeclaredInAbstractClass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ConstantDeclaredInInterface" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ConstantFunctionResult" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="ConstantJUnitAssertArgument" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ConstantMathCall" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ConstantOnLHSOfComparison" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ConstantOnRHSOfComparison" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ConstantOnWrongSideOfComparison" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ConstantParameter" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="ConstantTestNGAssertArgument" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ConstantValueVariableUse" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ConstructionIsNotAllowed" enabled="true" level="ERROR" enabled_by_default="true" />
@ -283,7 +285,6 @@
<inspection_tool class="CyclomaticComplexity" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_limit" value="10" />
</inspection_tool>
<inspection_tool class="DanglingPointer" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="DanglingPointers" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="DateToString" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="DeclareCollectionAsInterface" enabled="true" level="WARNING" enabled_by_default="true">
@ -295,6 +296,9 @@
<inspection_tool class="DesignForExtension" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="DisjointPackage" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="DollarSignInName" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="DoubleCheckedLocking" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoreOnVolatileVariables" value="false" />
</inspection_tool>
<inspection_tool class="DoubleLiteralMayBeFloatLiteral" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="DriverManagerGetConnection" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="DuplicateBooleanBranch" enabled="true" level="WARNING" enabled_by_default="true" />
@ -313,7 +317,7 @@
<option name="commentsAreContent" value="true" />
</inspection_tool>
<inspection_tool class="EmptyDirectory" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="EndlessLoop" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="EmptySynchronizedStatement" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="EnumAsName" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="EnumClass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="EnumerationCanBeIteration" enabled="true" level="WARNING" enabled_by_default="true" />
@ -359,9 +363,11 @@
<inspection_tool class="FieldNotUsedInToString" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="FinalClass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="FinalMethod" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="FinalMethodInFinalClass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="Finalize" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoreTrivialFinalizers" value="true" />
</inspection_tool>
<inspection_tool class="FinalizeNotProtected" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="FloatingPointEquality" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ForLoopReplaceableByWhile" enabled="true" level="INFORMATION" enabled_by_default="true">
<option name="m_ignoreLoopsWithoutConditions" value="false" />
@ -400,7 +406,6 @@
<inspection_tool class="IncompatibleTypes" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="InconsistentLanguageLevel" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="IncrementDecrementUsedAsExpression" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="InfiniteRecursion" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="InitializationIssue" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="InitializerIssues" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="InnerClassOnInterface" enabled="true" level="WARNING" enabled_by_default="true">
@ -473,7 +478,6 @@
<option name="REPORT_VARIABLES" value="true" />
<option name="REPORT_PARAMETERS" value="true" />
</inspection_tool>
<inspection_tool class="LocalValueEscapesScope" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="LocalVariableHidingMemberVariable" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_ignoreInvisibleFields" value="true" />
<option name="m_ignoreStaticMethods" value="true" />
@ -587,6 +591,7 @@
<inspection_tool class="NestingDepth" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_limit" value="5" />
</inspection_tool>
<inspection_tool class="NewClassNamingConvention" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NewExceptionWithoutArguments" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NewMethodNamingConvention" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NoDefaultBaseConstructor" enabled="true" level="ERROR" enabled_by_default="true" />
@ -600,6 +605,7 @@
</inspection_tool>
<inspection_tool class="NonExceptionNameEndsWithException" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NonFinalClone" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NonFinalFieldInEnum" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NonFinalFieldInImmutable" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NonFinalFieldOfException" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NonFinalGuard" enabled="true" level="WARNING" enabled_by_default="true" />
@ -634,7 +640,6 @@
<inspection_tool class="NotSuperclass" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="NotifyCalledOnCondition" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NotifyWithoutCorrespondingWait" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NullDereference" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="NullDereferences" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="NullThrown" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NumericToString" enabled="true" level="WARNING" enabled_by_default="true" />
@ -735,6 +740,7 @@
<inspection_tool class="PreviewMultipleParameterProviders" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="PreviewMustBeTopLevelFunction" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="PreviewNeedsComposableAnnotation" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="PrivateMemberAccessBetweenOuterAndInnerClass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ProblematicVarargsMethodOverride" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ProjectFingerprint" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="PropKeysLabel" enabled="false" level="WARNING" enabled_by_default="false" />
@ -744,6 +750,7 @@
<option name="ignoreEnums" value="false" />
<option name="ignoreInterfaces" value="false" />
</inspection_tool>
<inspection_tool class="ProtectedMemberInFinalClass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="PublicConstructor" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="PublicConstructorInNonPublicClass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="PublicFieldAccessedInSynchronizedContext" enabled="true" level="WARNING" enabled_by_default="true" />
@ -911,7 +918,10 @@
<inspection_tool class="SerialPersistentFieldsWithWrongSignature" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SerialVersionUIDNotStaticFinal" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SerializableDeserializableClassInSecureContext" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SerializableHasSerialVersionUIDField" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SerializableHasSerialVersionUIDField" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoreAnonymousInnerClasses" value="false" />
<option name="superClassString" value="java.awt.Component" />
</inspection_tool>
<inspection_tool class="SerializableHasSerializationMethods" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoreAnonymousInnerClasses" value="false" />
<option name="superClassString" value="java.awt.Component" />
@ -1015,6 +1025,7 @@
<inspection_tool class="SystemGetenv" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SystemOutErr" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SystemProperties" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SystemRunFinalizersOnExit" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SystemSetSecurityManager" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="TemplateArgumentsIssues" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="TestCaseInProductCode" enabled="true" level="WARNING" enabled_by_default="true" />
@ -1061,6 +1072,7 @@
<option name="onlyWeakentoInterface" value="true" />
</inspection_tool>
<inspection_tool class="TypeParameterExtendsFinalClass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnaryPlus" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UncheckedExceptionClass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnconditionalWait" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UndeclaredTests" enabled="true" level="WARNING" enabled_by_default="true" />
@ -1085,6 +1097,8 @@
<inspection_tool class="UnnecessarySuperConstructor" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnnecessarySuperQualifier" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnnecessaryThis" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnnecessaryToStringCall" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnnecessaryUnaryMinus" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnnecessaryUnicodeEscape" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnqualifiedInnerClassAccess" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoreReferencesToLocalInnerClasses" value="false" />
@ -1094,7 +1108,6 @@
<option name="m_ignoreStaticMethodCalls" value="false" />
<option name="m_ignoreStaticAccessFromStaticContext" value="false" />
</inspection_tool>
<inspection_tool class="UnreachableCallsOfFunction" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="UnreachableCode" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="UnsecureRandomNumberGeneration" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnusedClass" enabled="false" level="WARNING" enabled_by_default="false" />
@ -1104,12 +1117,12 @@
<inspection_tool class="UnusedIncludeDirective" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="UnusedInstanceVariable" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="UnusedLibrary" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnusedLocalVariable" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="UnusedLocalVariable" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="UnusedLocalization" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="UnusedMacro" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="UnusedMainParameter" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="UnusedMethod" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="UnusedParameter" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="UnusedParameter" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="UnusedProperty" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="UnusedStruct" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="UnusedTemplateParameter" enabled="false" level="WARNING" enabled_by_default="false" />
@ -1147,6 +1160,7 @@
<inspection_tool class="WaitNotInLoop" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="WaitNotifyNotInSynchronizedContext" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="WaitOrAwaitWithoutTimeout" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="WaitWhileHoldingTwoLocks" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="WaitWithoutCorrespondingNotify" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="WeakerAccess" enabled="true" level="WARNING" enabled_by_default="true">
<option name="SUGGEST_PACKAGE_LOCAL_FOR_MEMBERS" value="true" />
@ -1154,7 +1168,7 @@
<option name="SUGGEST_PRIVATE_FOR_INNERS" value="false" />
</inspection_tool>
<inspection_tool class="ZeroLengthArrayInitialization" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="unused" enabled="false" level="WARNING" enabled_by_default="false" checkParameterExcludingHierarchy="false">
<inspection_tool class="unused" enabled="false" level="WARNING" enabled_by_default="false">
<option name="LOCAL_VARIABLE" value="true" />
<option name="FIELD" value="true" />
<option name="METHOD" value="true" />

40
.idea/jarRepositories.xml generated Normal file
View file

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RemoteRepositoriesConfiguration">
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Maven Central repository" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
</remote-repository>
<remote-repository>
<option name="id" value="BintrayJCenter" />
<option name="name" value="BintrayJCenter" />
<option name="url" value="https://jcenter.bintray.com/" />
</remote-repository>
<remote-repository>
<option name="id" value="Google" />
<option name="name" value="Google" />
<option name="url" value="https://dl.google.com/dl/android/maven2/" />
</remote-repository>
<remote-repository>
<option name="id" value="MavenRepo" />
<option name="name" value="MavenRepo" />
<option name="url" value="https://repo.maven.apache.org/maven2/" />
</remote-repository>
<remote-repository>
<option name="id" value="maven" />
<option name="name" value="maven" />
<option name="url" value="file:/$PROJECT_DIR$/app/libraries/" />
</remote-repository>
<remote-repository>
<option name="id" value="MavenLocal" />
<option name="name" value="MavenLocal" />
<option name="url" value="file:/$USER_HOME$/.m2/repository/" />
</remote-repository>
</component>
</project>

5
.idea/kotlinc.xml generated
View file

@ -1,9 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Kotlin2JvmCompilerArguments">
<option name="jvmTarget" value="1.8" />
</component>
<component name="KotlinJpsPluginSettings">
<option name="version" value="1.7.21" />
<option name="jvmTarget" value="15" />
</component>
</project>

59
.idea/misc.xml generated Normal file
View file

@ -0,0 +1,59 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="JavadocGenerationManager">
<option name="OUTPUT_DIRECTORY" value="$PROJECT_DIR$/../LightSwitchEXTRA/JDoc" />
<option name="OPTION_SCOPE" value="private" />
</component>
<component name="NullableNotNullManager">
<option name="myDefaultNullable" value="org.jetbrains.annotations.Nullable" />
<option name="myDefaultNotNull" value="androidx.annotation.NonNull" />
<option name="myNullables">
<value>
<list size="15">
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" />
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" />
<item index="2" class="java.lang.String" itemvalue="javax.annotation.CheckForNull" />
<item index="3" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" />
<item index="4" class="java.lang.String" itemvalue="android.support.annotation.Nullable" />
<item index="5" class="java.lang.String" itemvalue="androidx.annotation.Nullable" />
<item index="6" class="java.lang.String" itemvalue="androidx.annotation.RecentlyNullable" />
<item index="7" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.qual.Nullable" />
<item index="8" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NullableDecl" />
<item index="9" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NullableType" />
<item index="10" class="java.lang.String" itemvalue="android.annotation.Nullable" />
<item index="11" class="java.lang.String" itemvalue="com.android.annotations.Nullable" />
<item index="12" class="java.lang.String" itemvalue="org.eclipse.jdt.annotation.Nullable" />
<item index="13" class="java.lang.String" itemvalue="io.reactivex.annotations.Nullable" />
<item index="14" class="java.lang.String" itemvalue="io.reactivex.rxjava3.annotations.Nullable" />
</list>
</value>
</option>
<option name="myNotNulls">
<value>
<list size="14">
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" />
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" />
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" />
<item index="3" class="java.lang.String" itemvalue="android.support.annotation.NonNull" />
<item index="4" class="java.lang.String" itemvalue="androidx.annotation.NonNull" />
<item index="5" class="java.lang.String" itemvalue="androidx.annotation.RecentlyNonNull" />
<item index="6" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.qual.NonNull" />
<item index="7" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NonNullDecl" />
<item index="8" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NonNullType" />
<item index="9" class="java.lang.String" itemvalue="android.annotation.NonNull" />
<item index="10" class="java.lang.String" itemvalue="com.android.annotations.NonNull" />
<item index="11" class="java.lang.String" itemvalue="org.eclipse.jdt.annotation.NonNull" />
<item index="12" class="java.lang.String" itemvalue="io.reactivex.annotations.NonNull" />
<item index="13" class="java.lang.String" itemvalue="io.reactivex.rxjava3.annotations.NonNull" />
</list>
</value>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="false" project-jdk-name="JDK" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>

View file

@ -1,6 +1,6 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Controller Configuration" type="AndroidRunConfigurationType" factoryName="Android App" activateToolWindowBeforeRun="false">
<module name="skyline.app.main" />
<module name="skyline.app" />
<option name="DEPLOY" value="true" />
<option name="DEPLOY_APK_FROM_BUNDLE" value="false" />
<option name="DEPLOY_AS_INSTANT" value="false" />
@ -8,13 +8,13 @@
<option name="PM_INSTALL_OPTIONS" value="" />
<option name="ALL_USERS" value="false" />
<option name="ALWAYS_INSTALL_WITH_PM" value="false" />
<option name="CLEAR_APP_STORAGE" value="false" />
<option name="DYNAMIC_FEATURES_DISABLED_LIST" value="" />
<option name="ACTIVITY_EXTRA_FLAGS" value="" />
<option name="MODE" value="specific_activity" />
<option name="CLEAR_LOGCAT" value="false" />
<option name="SHOW_LOGCAT_AUTOMATICALLY" value="false" />
<option name="INSPECTION_WITHOUT_ACTIVITY_RESTART" value="false" />
<option name="SKIP_NOOP_APK_INSTALLATIONS" value="true" />
<option name="FORCE_STOP_RUNNING_APP" value="true" />
<option name="TARGET_SELECTION_MODE" value="DEVICE_AND_SNAPSHOT_COMBO_BOX" />
<option name="SELECTED_CLOUD_MATRIX_CONFIGURATION_ID" value="-1" />
<option name="SELECTED_CLOUD_MATRIX_PROJECT_ID" value="" />
@ -45,7 +45,7 @@
<option name="ADVANCED_PROFILING_ENABLED" value="false" />
<option name="STARTUP_PROFILING_ENABLED" value="false" />
<option name="STARTUP_CPU_PROFILING_ENABLED" value="false" />
<option name="STARTUP_CPU_PROFILING_CONFIGURATION_NAME" value="Callstack Sample" />
<option name="STARTUP_CPU_PROFILING_CONFIGURATION_NAME" value="Sample Java Methods" />
<option name="STARTUP_NATIVE_MEMORY_PROFILING_ENABLED" value="false" />
<option name="NATIVE_MEMORY_SAMPLE_RATE_BYTES" value="2048" />
</Profilers>

View file

@ -1,6 +1,6 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Main" type="AndroidRunConfigurationType" factoryName="Android App" activateToolWindowBeforeRun="false">
<module name="skyline.app.main" />
<module name="skyline.app" />
<option name="DEPLOY" value="true" />
<option name="DEPLOY_APK_FROM_BUNDLE" value="false" />
<option name="DEPLOY_AS_INSTANT" value="false" />
@ -8,13 +8,13 @@
<option name="PM_INSTALL_OPTIONS" value="" />
<option name="ALL_USERS" value="false" />
<option name="ALWAYS_INSTALL_WITH_PM" value="false" />
<option name="CLEAR_APP_STORAGE" value="false" />
<option name="DYNAMIC_FEATURES_DISABLED_LIST" value="" />
<option name="ACTIVITY_EXTRA_FLAGS" value="" />
<option name="MODE" value="default_activity" />
<option name="CLEAR_LOGCAT" value="false" />
<option name="SHOW_LOGCAT_AUTOMATICALLY" value="false" />
<option name="INSPECTION_WITHOUT_ACTIVITY_RESTART" value="false" />
<option name="SKIP_NOOP_APK_INSTALLATIONS" value="true" />
<option name="FORCE_STOP_RUNNING_APP" value="true" />
<option name="TARGET_SELECTION_MODE" value="DEVICE_AND_SNAPSHOT_COMBO_BOX" />
<option name="SELECTED_CLOUD_MATRIX_CONFIGURATION_ID" value="-1" />
<option name="SELECTED_CLOUD_MATRIX_PROJECT_ID" value="" />
@ -45,7 +45,7 @@
<option name="ADVANCED_PROFILING_ENABLED" value="false" />
<option name="STARTUP_PROFILING_ENABLED" value="false" />
<option name="STARTUP_CPU_PROFILING_ENABLED" value="false" />
<option name="STARTUP_CPU_PROFILING_CONFIGURATION_NAME" value="Callstack Sample" />
<option name="STARTUP_CPU_PROFILING_CONFIGURATION_NAME" value="Sample Java Methods" />
<option name="STARTUP_NATIVE_MEMORY_PROFILING_ENABLED" value="false" />
<option name="NATIVE_MEMORY_SAMPLE_RATE_BYTES" value="2048" />
</Profilers>

View file

@ -1,6 +1,6 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Settings" type="AndroidRunConfigurationType" factoryName="Android App" activateToolWindowBeforeRun="false">
<module name="skyline.app.main" />
<module name="skyline.app" />
<option name="DEPLOY" value="true" />
<option name="DEPLOY_APK_FROM_BUNDLE" value="false" />
<option name="DEPLOY_AS_INSTANT" value="false" />
@ -8,13 +8,13 @@
<option name="PM_INSTALL_OPTIONS" value="" />
<option name="ALL_USERS" value="false" />
<option name="ALWAYS_INSTALL_WITH_PM" value="false" />
<option name="CLEAR_APP_STORAGE" value="false" />
<option name="DYNAMIC_FEATURES_DISABLED_LIST" value="" />
<option name="ACTIVITY_EXTRA_FLAGS" value="" />
<option name="MODE" value="specific_activity" />
<option name="CLEAR_LOGCAT" value="false" />
<option name="SHOW_LOGCAT_AUTOMATICALLY" value="false" />
<option name="INSPECTION_WITHOUT_ACTIVITY_RESTART" value="false" />
<option name="SKIP_NOOP_APK_INSTALLATIONS" value="true" />
<option name="FORCE_STOP_RUNNING_APP" value="true" />
<option name="TARGET_SELECTION_MODE" value="DEVICE_AND_SNAPSHOT_COMBO_BOX" />
<option name="SELECTED_CLOUD_MATRIX_CONFIGURATION_ID" value="-1" />
<option name="SELECTED_CLOUD_MATRIX_PROJECT_ID" value="" />
@ -45,12 +45,12 @@
<option name="ADVANCED_PROFILING_ENABLED" value="false" />
<option name="STARTUP_PROFILING_ENABLED" value="false" />
<option name="STARTUP_CPU_PROFILING_ENABLED" value="false" />
<option name="STARTUP_CPU_PROFILING_CONFIGURATION_NAME" value="Callstack Sample" />
<option name="STARTUP_CPU_PROFILING_CONFIGURATION_NAME" value="Sample Java Methods" />
<option name="STARTUP_NATIVE_MEMORY_PROFILING_ENABLED" value="false" />
<option name="NATIVE_MEMORY_SAMPLE_RATE_BYTES" value="2048" />
</Profilers>
<option name="DEEP_LINK" value="" />
<option name="ACTIVITY_CLASS" value="emu.skyline.settings.SettingsActivity" />
<option name="ACTIVITY_CLASS" value="emu.skyline.SettingsActivity" />
<option name="SEARCH_ACTIVITY_IN_GLOBAL_SCOPE" value="false" />
<option name="SKIP_ACTIVITY_VALIDATION" value="false" />
<method v="2">

View file

@ -1,3 +0,0 @@
<component name="DependencyValidationManager">
<scope name="ShaderCompiler" pattern="file[skyline.app.main]:shader-compiler//*" />
</component>

View file

@ -1,3 +1,3 @@
<component name="DependencyValidationManager">
<scope name="SkylineKotlin" pattern="file[skyline.app.main]:java//*.kt" />
</component>
<scope name="SkylineKotlin" pattern="file[skyline.app]:src/main/java//*.kt" />
</component>

View file

@ -1,3 +1,3 @@
<component name="DependencyValidationManager">
<scope name="SkylineLibraries" pattern="(file[skyline.app]:libraries/fmt/include//*||file[skyline.app]:libraries/frozen/include//*||file[skyline.app]:libraries/lz4/lib//*||file[skyline.app]:libraries/oboe/include//*||file[skyline.app]:libraries/perfetto/include//*||file:libraries/pugixml/src/pugixml.hpp||file[skyline.app]:libraries/tzcode/include/*||file[skyline.app]:libraries/vkhpp/vulkan//*||file[skyline.app]:libraries/vkhpp/Vulkan-Headers/include//*)&amp;&amp;!file:libraries/vkhpp/Vulkan-Headers/include/vulkan/vulkan.hpp||file:libraries/vkma/include/vk_mem_alloc.h" />
</component>
<scope name="SkylineLibraries" pattern="file[skyline.app]:libraries//*" />
</component>

View file

@ -1,3 +1,3 @@
<component name="DependencyValidationManager">
<scope name="SkylineNative" pattern="file[skyline.app.main]:cpp//*.cpp||file[skyline.app.main]:cpp//*.h||file[skyline.app.main]:cpp//*.S||file[skyline.app.main]:cpp//*.natvis" />
</component>
<scope name="SkylineNative" pattern="file[skyline.app]:src/main/cpp//*.cpp||file[skyline.app]:src/main/cpp//*.h||file[skyline.app]:src/main/cpp//*.S" />
</component>

View file

@ -1,3 +1,3 @@
<component name="DependencyValidationManager">
<scope name="SkylineXml" pattern="file[skyline.app.main]:res/layout/*||file[app.main]:res/menu/*||file[skyline.app.main]:res/values/*||file[skyline.app.main]:res/values-night/*||file[skyline.app.main]:res/xml/*" />
</component>
<scope name="SkylineXml" pattern="file[skyline.app]:src/main/res/layout/*||file[app]:src/main/res/menu/*||file[skyline.app]:src/main/res/values/*||file[skyline.app]:src/main/res/values-night/*||file[skyline.app]:src/main/res/xml/*" />
</component>

14
.idea/vcs.xml generated Normal file
View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
<mapping directory="$PROJECT_DIR$/app/libraries/fmt" vcs="Git" />
<mapping directory="$PROJECT_DIR$/app/libraries/frozen" vcs="Git" />
<mapping directory="$PROJECT_DIR$/app/libraries/lz4" vcs="Git" />
<mapping directory="$PROJECT_DIR$/app/libraries/oboe" vcs="Git" />
<mapping directory="$PROJECT_DIR$/app/libraries/perfetto" vcs="Git" />
<mapping directory="$PROJECT_DIR$/app/libraries/pugixml" vcs="Git" />
<mapping directory="$PROJECT_DIR$/app/libraries/tzcode" vcs="Git" />
<mapping directory="$PROJECT_DIR$/app/libraries/vkhpp" vcs="Git" />
</component>
</project>

View file

@ -1,118 +0,0 @@
# Building Guide
### Software needed
* [Git](https://git-scm.com/download)
* [Android Studio](https://developer.android.com/studio) We recommend to get the latest stable version
### Steps
<details><summary><i>Windows only</i></summary>
<p>
> In a terminal prompt with **administrator** privileges, enable git symlinks globally:
>
> ```cmd
> git config --global core.symlinks true
> ```
>
> Use this elevated prompt for the next steps.
</p>
</details>
Clone the repo **recursively**, either with your preferred Git GUI or with the command below:
```cmd
git clone https://github.com/skyline-emu/skyline.git --recursive
```
Open Android Studio
<details><summary><i>First time users only</i></summary>
<p>
> If you opened Android Studio for the first time, choose the `Standard` install type and complete the setup wizard leaving all settings to their default value.
> <p><img height="400" src="https://user-images.githubusercontent.com/37104290/162196602-4c142ed0-0c26-4628-8062-7ac9785201cc.png"></p>
> If you get any errors on "Intel® HAXM" or "Android Emulator Hypervisor Driver for AMD Processors", you can safely ignore them as they won't be used for Skyline.
</p>
</details>
Import the project by clicking on the `Open` icon, then in the file picker choose the `skyline` folder you just cloned in the steps above:
<p>
<img height="400" src="https://user-images.githubusercontent.com/37104290/162200497-dddfa9f0-00c6-4a32-84c2-1f0ff743a7e2.png">
<img height="400" src="https://user-images.githubusercontent.com/37104290/162196879-08d9684b-c6a2-4636-9c23-c026cb7d7494.png">
</p>
Exclude the following folders from indexing:
- `app/libraries/llvm`
- `app/libraries/boost`
To exclude a folder, switch to the project view:
<p>
<img height="400" src="https://user-images.githubusercontent.com/37104290/163343887-56a0b170-2249-45c4-a758-2b33cbfbc4ab.png">
<img height="400" src="https://user-images.githubusercontent.com/37104290/163343932-bed0c59c-7aaa-44f6-bdc4-0a10f966fb56.png">
</p>
In the project view navigate to the `app/libraries` folder, right-click on the folder you want to exclude and navigate the menus to the `Exclude` option:
<p>
<img height="400" src="https://user-images.githubusercontent.com/37104290/162200274-f739e960-82ca-4b12-95eb-caa88a063d61.png">
<img height="400" src="https://user-images.githubusercontent.com/37104290/162196999-a0376e13-0399-4352-a30d-85d6785151a9.png">
</p>
If an `Invalid Gradle JDK configuration found` error comes up, select `Use Embedded JDK`:
<p><img height="250" src="https://user-images.githubusercontent.com/37104290/162197215-b28ea3ec-ed5c-4d83-ac9a-19e892caa129.png"></p>
An error about NDK not being configured will be shown:
<p><img height="250" src="https://user-images.githubusercontent.com/37104290/162197226-3d9bf980-19af-4cad-86a3-c43cad05e185.png"></p>
Ignore the suggested version reported by Android Studio. Instead, find the NDK version to download inside the `app/build.gradle` file:
```gradle
ndkVersion 'X.Y.Z'
```
From that same file also note down the CMake version required:
```gradle
externalNativeBuild {
cmake {
version 'A.B.C+'
path "CMakeLists.txt"
}
}
```
Open the SDK manager from the top-right toolbar:
<p><img height="75" src="https://user-images.githubusercontent.com/37104290/162198029-4f29c50c-75eb-49ce-b05f-bb6413bf844c.png"></p>
Navigate to the `SDK Tools` tab and enable the `Show Package Details` checkbox in the bottom-right corner:
<p><img height="400" src="https://user-images.githubusercontent.com/37104290/162198766-37045d58-352c-48d6-b8de-67c1ddb7c757.png"></p>
Expand the `NDK (Side by side)` and `CMake` entries, select the appropriate version from the previous step, then click `OK`.
Finally, sync the project:
<p><img height="75" src="https://user-images.githubusercontent.com/37104290/162199780-b5406b5d-480d-4371-9dc4-5cfc6d655746.png"></p>
## Common issues (and how to fix them)
* `Cmake Error: CMake was unable to find a build program corresponding to "Ninja"`
Check that you installed the correct CMake version in the Android Studio SDK Manager. If you're sure you have the correct one, you may try adding the path to CMake binaries installed by Android Studio to the `local.properties` file:
```properties
cmake.dir=<path-to-cmake-folder>
```
E.g. on Windows:
```properties
cmake.dir=C\:\\Users\\skyline\\AppData\\Local\\Android\\Sdk\\cmake\\3.18.1
```
* `'shader_compiler/*.h' file not found`
You didn't clone the repository with symlinks enabled. Windows requires administrator privileges to create symlinks so it's likely it didn't create them.
In an **administrator** terminal prompt navigate to the Skyline root project folder and run:
```cmd
git submodule deinit app/libraries/shader-compiler
git config core.symlinks true
git submodule foreach git config core.symlinks true
git submodule update --init --recursive app/libraries/shader-compiler
```
If you'd like to, you can enable symlinks globally by running: (this will only affect new repositories)
```cmd
git config --global core.symlinks true
```

View file

@ -14,9 +14,6 @@ Android Studio comes with a code formatter in-built, this can fix minor mistakes
This can also be done by using `Ctrl + Alt + L` on Windows, `Ctrl + Shift + Alt + L` on Linux and `Option + Command + L` on macOS.
### Skyline Edge
Any code that's been PR'd to the Skyline repository will only be in Edge builds for two weeks, after which it will be merged into the mainline branch. This is to ensure that any bugs that may have been introduced by the PR are caught and fixed before it's merged into the mainline branch. If you have any issues with this, you can request that we add the `CI` tag to your PRs so that CI builds are provided pre-merge.
## C++
### Include Order
* STD includes
@ -60,45 +57,45 @@ We generally follow the rule of **"Functional Spacing"**, that being spacing bet
Here are a few examples of this to help with intution:
* Spacing should generally follow multiple related variable declarations, this applies even more if error checking code needs to follow it
```cpp
auto a{GetSomethingA()};
auto b{GetSomethingB()};
auto c{GetSomethingC()};
auto a = GetSomethingA();
auto b = GetSomethingB();
auto c = GetSomethingC();
auto result{DoSomething(a, b, c)};
auto result = DoSomething(a, b, c);
if (!result)
throw exception("DoSomething has failed: {}", result);
```
* If a function doesn't require multiple variable declarations, the function call should be right after the variable declaration
```cpp
auto a{GetClassA()};
auto a = GetClassA();
a.DoSomething();
auto b{GetClassB()};
auto b = GetClassB();
b.DoSomething();
```
* If a single variable is used by a single-line control flow statement, there can be no spaces after it's declaration
```cpp
auto a{GetClass()};
auto a = GetClass();
if (a.fail)
throw exception();
```
* **Be consistent** (The above examples assume that `a`/`b`/`c` are correlated)
* Inconsistent
```cpp
auto a{GetClassA()};
auto a = GetClassA();
a.DoSomething();
auto b{GetClassB()};
auto c{GetClassC()};
auto b = GetClassB();
auto c = GetClassC();
b.DoSomething();
c.DoSomething();
```
* Consistent #1
```cpp
auto a{GetClassA()};
auto b{GetClassB()};
auto c{GetClassC()};
auto a = GetClassA();
auto b = GetClassB();
auto c = GetClassC();
a.DoSomething();
b.DoSomething();
@ -106,13 +103,13 @@ Here are a few examples of this to help with intution:
```
* Consistent #2
```cpp
auto a{GetClassA()};
auto a = GetClassA();
a.DoSomething();
auto b{GetClassB()};
auto b = GetClassB();
b.DoSomething();
auto c{GetClassC()};
auto c = GetClassC();
c.DoSomething();
```
@ -165,8 +162,8 @@ Here are a few examples of this to help with intution:
We generally support the usage of functional programming and lambda, usage of it for assigning conditional variables is recommended especially if it would otherwise be a nested ternary statement:
* With Lambda (Inlined function)
```cpp
auto a{random()};
auto b{[a] {
auto a = random();
auto b = [a] {
if (a > 1000)
return 0;
else if (a > 500)
@ -175,12 +172,12 @@ We generally support the usage of functional programming and lambda, usage of it
return 2;
else
return 3;
}()};
}();
```
* With Ternary Operator
```cpp
auto a{random()};
auto b{(a > 1000) ? 0 : ((a > 500) ? 1 : (a > 250 ? 2 : 3))};
auto a = random();
auto b = (a > 1000) ? 0 : ((a > 500) ? 1 : (a > 250 ? 2 : 3));
```
### References
@ -213,8 +210,8 @@ Use C++ range-based iterators for any C++ container iteration unless it can be p
### Usage of auto
Use `auto` to assign a variable the type of the value it's being assigned to, but not where a different type is desired. So, as a rule of thumb always specify the type when setting something from a number rather than depending on `auto`. In addition, prefer not to use `auto` in cases where it's hard to determine the return type due to assigned value being complex.
```cpp
u8 a{20}; // `20` won't be stored in a `u8` but rather in a `int` (i32, generally) if `auto` is used
auto b{std::make_shared<Something>()}; // In this case `auto` is used to avoid typing out `std::shared_ptr<Something>`
u8 a = 20; // `20` won't be stored in a `u8` but rather in a `int` (i32, generally) if `auto` is used
auto b = std::make_shared<Something>(); // In this case `auto` is used to avoid typing out `std::shared_ptr<Something>`
```
### Primitive Types
@ -228,55 +225,6 @@ In addition, try to `constexpr` as much as possible including constructors and f
We should also mention that this isn't promoting the usage of `const`, it's use is actually discouraged out of references, in which case it is extremely encouraged. In addition, pointers are a general exception to this, using `const` with them isn't encouraged nor discouraged. Another exception are class functions, they can be made `const` if used from a `const` reference/pointer and don't
modify any members but do not do this preemptively.
### Initialization
We use bracketed initialization as opposed to traditional initalization due to the better type checking it offers and the consistency with designated initalizers.
* Correct
```c++
int a{FindA()};
static constexpr size_t AConstant{1ULL << 63}
for (int i{}; i < a; i++);
```
* Incorrect
```c++
int a = FindA();
static constexpr size_t AConstant = 1ULL << 63;
for (int i = 0; i < a; i++);
```
### Wrapping
We do not enforce a particular limit on line lengths however excessively long lines that may be difficult to read when soft-wrapped should be wrapped semantically. See the below examples:
* Correct:
```c++
PresentationEngine::PresentationEngine(const DeviceState &state, GPU &gpu)
: state(state),
gpu(gpu),
acquireFence(gpu.vkDevice, vk::FenceCreateInfo{}),
presentationTrack(static_cast<u64>(trace::TrackIds::Presentation), perfetto::ProcessTrack::Current()),
choreographerThread(&PresentationEngine::ChoreographerThread, this),
vsyncEvent(std::make_shared<kernel::type::KEvent>(state, true)) {
```
* Incorrect:
```c++
PresentationEngine::PresentationEngine(const DeviceState &state, GPU &gpu) : state(state), gpu(gpu), acquireFence(gpu.vkDevice, vk::FenceCreateInfo{}), presentationTrack(static_cast<u64>(trace::TrackIds::Presentation), perfetto::ProcessTrack::Current()), choreographerThread(&PresentationEngine::ChoreographerThread, this), vsyncEvent(std::make_shared<kernel::type::KEvent>(state, true)) {
```
* Incorrect
```c++
PresentationEngine::PresentationEngine(const DeviceState &state, GPU &gpu)
: state(state), gpu(gpu), acquireFence(gpu.vkDevice, vk::FenceCreateInfo{}), presentationTrack(static_cast<u64>(trace::TrackIds::Presentation), perfetto::ProcessTrack::Current()),
choreographerThread(&PresentationEngine::ChoreographerThread, this),
vsyncEvent(std::make_shared<kernel::type::KEvent>(state, true)) {
```
### Vulkan.hpp Header Size
The size of the header imported for [Vulkan-Hpp](https://github.com/KhronosGroup/Vulkan-Hpp) is extremely large and exceeds the CLion default analysis limit, it is required to run for properly annotating any code which uses components from it. To override this limit, refer to this [article from JetBrains](https://www.jetbrains.com/help/objc/configuring-file-size-limit.html#file-size-limit) or navigate to Help -> Edit Custom Properties and add `idea.max.intellisense.filesize=20000` to set the maximum limit to 20MB which should be adequate for it.
## Kotlin
### Naming rules
* Enumerator: `PascalCase` **(1)**

View file

@ -1,26 +1,17 @@
<p>
<h3 align="center">Development on Skyline has been ceased</h3>
<p align="center">All Skyline code is within this repository or <a href="https://github.com/skyline-emu/skyline-dev"><code>skyline-dev</code></a>. Anyone is free to fork the code to continue the work as long as they follow our <a href="LICENSE.md">MPL-2.0 license</a>, it should be noted that Skyline branding isn't licensed.</p>
</p>
<h1 align="center">
<a href="https://github.com/skyline-emu/skyline" target="_blank">
<img height="60%" width="60%" src="https://raw.github.com/skyline-emu/branding/master/banner/skyline-banner-rounded.png"><br>
<img height="60%" width="60%" src="https://i.imgur.com/6PJ7Ml2.png"><br>
</a>
<a href="https://discord.gg/XnbXNQM" target="_blank">
<img src="https://img.shields.io/discord/545842171459272705.svg?label=&logo=discord&logoColor=ffffff&color=5865F2&labelColor=404EED">
<img src="https://img.shields.io/discord/545842171459272705?label=Discord&logo=Discord&color=yellow">
</a>
<a href="https://github.com/skyline-emu/skyline/actions/workflows/ci.yml" target="_blank">
<img src="https://github.com/skyline-emu/skyline/actions/workflows/ci.yml/badge.svg"><br>
<img src="https://github.com/skyline-emu/skyline/actions/workflows/ci.yml/badge.svg"/><br>
</a>
<img src="https://forthebadge.com/images/badges/built-for-android.svg"/>
</h1>
<p align="center">
<b><a href="CONTRIBUTING.md">Contributing Guide</a><a href="BUILDING.md">Building Guide</a></b>
</p>
<p align="center">
<b>Skyline</b> was an experimental emulator that runs on <b>ARMv8 Android™</b> devices and emulates the functionality of a <b>Nintendo Switch™</b> system, licensed under <a href="https://github.com/skyline-emu/skyline/blob/master/LICENSE.md"><b>Mozilla Public License 2.0</b></a>
<b>Skyline</b> is an experimental emulator that runs on <b>ARMv8 Android™</b> devices and emulates the functionality of a <b>Nintendo Switch™</b> system, licensed under <a href="https://github.com/skyline-emu/skyline/blob/master/LICENSE.md"><b>Mozilla Public License 2.0</b></a>
</p>
---
@ -32,11 +23,11 @@ You can contact the core developers of Skyline at our **[Discord](https://discor
### Special Thanks
A few noteworthy teams/projects who've helped us along the way are:
* **[Ryujinx](https://ryujinx.org/):** We've used Ryujinx for reference throughout the project, the accuracy of their HLE implementations of Switch subsystems make it an amazing reference. The team behind the project has been extremely helpful with any queries we've had and have constantly helped us with any issues we've come across. **It should be noted that Skyline is not based on Ryujinx**.
* **[Ryujinx](https://ryujinx.org/):** We've used Ryujinx for reference throughout the project, the amount of accuracy of their HLE kernel implementation is what makes them such an amazing reference. The team behind the project has been really helpful with any queries we've had. **It should be noted that Skyline is not based on Ryujinx**.
* **[yuzu](https://yuzu-emu.org/):** Skyline's shader compiler is a **fork** of *yuzu*'s shader compiler with Skyline-specific changes, using it allowed us to focus on the parts of GPU emulation that we could specifically optimize for mobile while having a high-quality shader compiler implementation as a base. The team behind *yuzu* has also often helped us and have graciously provided us with a license exemption.
* **[Switchbrew](https://github.com/switchbrew/):** We've extensively used Switchbrew whether that be their **[wiki](https://switchbrew.org/)** with it's colossal amount of information on the Switch that has saved us countless hours of time or **[libnx](https://github.com/switchbrew/libnx)** which was crucial to initial development of the emulator to ensure that our HLE kernel and sysmodule implementations were accurate.
* **[Switchbrew](https://github.com/switchbrew/):** We've extensively used Switchbrew whether that be their **[wiki](https://switchbrew.org/)** with its colossal amount of information on the Switch that has saved us countless hours of time or **[libnx](https://github.com/switchbrew/libnx)** which was crucial to initial development of the emulator to ensure that our HLE kernel and sysmodule implementations were accurate.
* **[Atmosphère](https://github.com/Atmosphere-NX/Atmosphere):** We've used [libmesosphere](https://github.com/Atmosphere-NX/Atmosphere/tree/master/libraries/libmesosphere) as another reference for our HLE kernel, it's faithfulness to the HOS kernel helps us a lot. This makes it invaluably important for us as a tool for writing code that can accurately emulate HOS behavior without missing any crucial parts.
---

View file

@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.16)
project(Skyline LANGUAGES C CXX ASM VERSION 0.3)
project(Skyline LANGUAGES CXX ASM VERSION 0.3)
set(BUILD_TESTS OFF CACHE BOOL "Build Tests" FORCE)
set(BUILD_TESTING OFF CACHE BOOL "Build Testing" FORCE)
@ -9,159 +9,44 @@ set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
set(source_DIR ${CMAKE_SOURCE_DIR}/src/main/cpp)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-strict-aliasing -Wno-unused-command-line-argument -fwrapv")
set(CMAKE_CXX_FLAGS_RELEASE "-Ofast -flto=full -fno-stack-protector -DNDEBUG")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-strict-aliasing")
set(CMAKE_CXX_FLAGS_RELEASE "-Ofast -flto=full -fno-stack-protector -Wno-unused-command-line-argument")
if (uppercase_CMAKE_BUILD_TYPE STREQUAL "RELEASE")
add_compile_definitions(NDEBUG)
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
endif ()
# Build all libraries with -Ofast but with default debug data (-g) for debug and reldebug builds
set(CMAKE_CXX_FLAGS_DEBUG "-Ofast")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-Ofast")
# libcxx
set(ANDROID_STL "none")
set(LIBCXX_INCLUDE_TESTS OFF)
set(LIBCXX_INCLUDE_BENCHMARKS OFF)
set(LIBCXX_INCLUDE_DOCS OFF)
set(LIBCXX_ENABLE_SHARED FALSE)
set(LIBCXX_ENABLE_ASSERTIONS FALSE)
set(LIBCXX_STANDALONE_BUILD FALSE)
add_subdirectory("libraries/llvm/libcxx")
link_libraries(cxx_static)
get_target_property(LIBCXX_INCLUDE_COMPILE_OPTION cxx-headers INTERFACE_COMPILE_OPTIONS)
string(REGEX REPLACE "-I" "" LIBCXX_INCLUDE_DIRECTORY_LIST "${LIBCXX_INCLUDE_COMPILE_OPTION}")
list(GET LIBCXX_INCLUDE_DIRECTORY_LIST 1 LIBCXX_TARGET_INCLUDE_DIRECTORY) # We just want the target include directory
set_target_properties(cxx-headers PROPERTIES INTERFACE_COMPILE_OPTIONS -isystem${LIBCXX_TARGET_INCLUDE_DIRECTORY})
# libcxxabi
set(LIBCXXABI_INCLUDE_TESTS OFF)
set(LIBCXXABI_ENABLE_SHARED FALSE)
set(LIBCXXABI_STANDALONE_BUILD FALSE)
set(LIBCXXABI_LIBCXX_INCLUDES "${LIBCXX_TARGET_INCLUDE_DIRECTORY}" CACHE STRING "" FORCE)
add_subdirectory("libraries/llvm/libcxxabi")
link_libraries(cxxabi_static)
# Skyline's Boost fork
set(Boost_USE_STATIC_LIBS ON)
set(Boost_USE_MULTITHREADED ON)
add_subdirectory("libraries/boost")
# {fmt}
add_subdirectory("libraries/fmt")
# TzCode
add_subdirectory("libraries/tzcode")
target_compile_options(tzcode PRIVATE -Wno-everything)
# LZ4
add_subdirectory("libraries/oboe")
include_directories("libraries/oboe/include")
set(LZ4_BUILD_CLI OFF CACHE BOOL "Build LZ4 CLI" FORCE)
set(LZ4_BUILD_LEGACY_LZ4C OFF CACHE BOOL "Build lz4c progam with legacy argument support" FORCE)
add_subdirectory("libraries/lz4/build/cmake")
include_directories(SYSTEM "libraries/lz4/lib")
include_directories("libraries/lz4/lib")
# Vulkan + Vulkan-Hpp
add_compile_definitions(VK_USE_PLATFORM_ANDROID_KHR) # We want all the Android-specific structures to be defined
add_compile_definitions(VULKAN_HPP_NO_SPACESHIP_OPERATOR) # libcxx doesn't implement operator<=> for std::array which breaks this
add_compile_definitions(VULKAN_HPP_NO_STRUCT_CONSTRUCTORS) # We want to use designated initializers in Vulkan-Hpp
add_compile_definitions(VULKAN_HPP_NO_SETTERS)
add_compile_definitions(VULKAN_HPP_NO_SMART_HANDLE)
include_directories("libraries/vkhpp/include")
include_directories("libraries/pugixml/src") # We use PugiXML in header-only mode
include_directories("libraries/frozen/include")
add_compile_definitions(VULKAN_HPP_DISPATCH_LOADER_DYNAMIC=1) # We use the dynamic loader rather than the static one to avoid an additional level of indirection
add_compile_definitions(VULKAN_HPP_ENABLE_DYNAMIC_LOADER_TOOL=0) # We disable the dynamic loader tool so we can supply our own getInstanceProcAddress function from a custom driver
include_directories(SYSTEM "libraries/vkhpp")
include_directories(SYSTEM "libraries/vkhpp/Vulkan-Headers/include") # We use base Vulkan headers from this to ensure version parity with Vulkan-Hpp
# Vulkan Memory Allocator
include_directories("libraries/vkma/include")
add_library(vkma STATIC libraries/vkma.cpp)
target_compile_options(vkma PRIVATE -Wno-everything)
# Frozen
include_directories(SYSTEM "libraries/frozen/include")
# MbedTLS
set(ENABLE_TESTING OFF CACHE BOOL "Build mbed TLS tests." FORCE)
set(ENABLE_PROGRAMS OFF CACHE BOOL "Build mbed TLS programs." FORCE)
set(UNSAFE_BUILD ON CACHE BOOL "Allow unsafe builds. These builds ARE NOT SECURE." FORCE)
add_subdirectory("libraries/mbedtls")
include_directories(SYSTEM "libraries/mbedtls/include")
target_compile_options(mbedcrypto PRIVATE -Wno-everything)
# Opus
set(OPUS_INSTALL_CMAKE_CONFIG_MODULE OFF CACHE BOOL "Install Opus CMake package config module" FORCE)
include_directories(SYSTEM "libraries/opus/include")
add_subdirectory("libraries/opus")
target_compile_definitions(opus PRIVATE OPUS_WILL_BE_SLOW=1) # libopus will warn when built without optimizations
# Cubeb
set(USE_OPENSL ON)
set(USE_SANITIZERS OFF)
set(USE_LAZY_LOAD_LIBS OFF)
set(USE_AAUDIO OFF)
set(BUNDLE_SPEEX ON)
add_subdirectory("libraries/cubeb")
include_directories(SYSTEM "libraries/cubeb/include")
find_package(mbedtls REQUIRED CONFIG)
# Perfetto SDK
include_directories(SYSTEM "libraries/perfetto/sdk")
include_directories(libraries/perfetto/sdk)
add_library(perfetto STATIC libraries/perfetto/sdk/perfetto.cc)
target_compile_options(perfetto PRIVATE -Wno-everything)
# C++ Range v3
add_subdirectory("libraries/range")
include_directories(${source_DIR}/skyline)
# Sirit
add_subdirectory("libraries/sirit")
# libadrenotools
add_subdirectory("libraries/adrenotools")
# Tessil Robin Map
add_subdirectory("libraries/robin-map")
# Renderdoc in-app API
include_directories("libraries/renderdoc")
# Thread Pool
include_directories("libraries/thread-pool")
# Build Skyline with full debugging data and -Og for debug builds
set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g3 -glldb -gdwarf-5 -fno-omit-frame-pointer")
# Build Skyline with full debugging data and some optimizations for reldebug builds, build speed is pioritised
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O1 -g3 -glldb -gdwarf-5 -fno-omit-frame-pointer -fno-stack-protector")
# Include headers from libraries as system headers to silence warnings from them
function(target_link_libraries_system target)
set(libraries ${ARGN})
foreach (library ${libraries})
if (TARGET ${library})
get_target_property(library_include_directories ${library} INTERFACE_INCLUDE_DIRECTORIES)
if (NOT "${library_include_directories}" STREQUAL "library_include_directories-NOTFOUND")
target_include_directories(${target} SYSTEM PRIVATE ${library_include_directories})
endif ()
endif ()
target_link_libraries(${target} PRIVATE ${library})
endforeach (library)
endfunction(target_link_libraries_system)
# yuzu Shader Compiler
add_subdirectory("libraries/shader-compiler")
target_include_directories(shader_recompiler PUBLIC "libraries/shader-compiler/include")
target_link_libraries_system(shader_recompiler Boost::intrusive Boost::container range-v3)
# yuzu Audio Core
add_subdirectory("libraries/audio-core")
include_directories(SYSTEM "libraries/audio-core/include")
target_link_libraries_system(audio_core Boost::intrusive Boost::container range-v3)
# Skyline
add_library(skyline SHARED
${source_DIR}/driver_jni.cpp
${source_DIR}/emu_jni.cpp
${source_DIR}/loader_jni.cpp
${source_DIR}/skyline/common.cpp
${source_DIR}/skyline/common/exception.cpp
${source_DIR}/skyline/common/logger.cpp
${source_DIR}/skyline/common/settings.cpp
${source_DIR}/skyline/common/signal.cpp
${source_DIR}/skyline/common/spin_lock.cpp
${source_DIR}/skyline/common/uuid.cpp
${source_DIR}/skyline/common/trace.cpp
${source_DIR}/skyline/nce/guest.S
@ -178,69 +63,16 @@ add_library(skyline SHARED
${source_DIR}/skyline/kernel/types/KPrivateMemory.cpp
${source_DIR}/skyline/kernel/types/KSyncObject.cpp
${source_DIR}/skyline/audio.cpp
${source_DIR}/skyline/gpu.cpp
${source_DIR}/skyline/gpu/trait_manager.cpp
${source_DIR}/skyline/gpu/memory_manager.cpp
${source_DIR}/skyline/gpu/texture_manager.cpp
${source_DIR}/skyline/gpu/buffer_manager.cpp
${source_DIR}/skyline/gpu/command_scheduler.cpp
${source_DIR}/skyline/gpu/descriptor_allocator.cpp
${source_DIR}/skyline/gpu/texture/bc_decoder.cpp
${source_DIR}/skyline/gpu/texture/texture.cpp
${source_DIR}/skyline/gpu/texture/layout.cpp
${source_DIR}/skyline/gpu/buffer.cpp
${source_DIR}/skyline/gpu/megabuffer.cpp
${source_DIR}/skyline/audio/track.cpp
${source_DIR}/skyline/audio/resampler.cpp
${source_DIR}/skyline/audio/adpcm_decoder.cpp
${source_DIR}/skyline/gpu/presentation_engine.cpp
${source_DIR}/skyline/gpu/shader_manager.cpp
${source_DIR}/skyline/gpu/pipeline_cache_manager.cpp
${source_DIR}/skyline/gpu/graphics_pipeline_assembler.cpp
${source_DIR}/skyline/gpu/cache/renderpass_cache.cpp
${source_DIR}/skyline/gpu/cache/framebuffer_cache.cpp
${source_DIR}/skyline/gpu/interconnect/fermi_2d.cpp
${source_DIR}/skyline/gpu/interconnect/maxwell_dma.cpp
${source_DIR}/skyline/gpu/interconnect/inline2memory.cpp
${source_DIR}/skyline/gpu/interconnect/maxwell_3d/active_state.cpp
${source_DIR}/skyline/gpu/interconnect/maxwell_3d/pipeline_state.cpp
${source_DIR}/skyline/gpu/interconnect/maxwell_3d/graphics_pipeline_state_accessor.cpp
${source_DIR}/skyline/gpu/interconnect/maxwell_3d/packed_pipeline_state.cpp
${source_DIR}/skyline/gpu/interconnect/maxwell_3d/pipeline_manager.cpp
${source_DIR}/skyline/gpu/interconnect/maxwell_3d/constant_buffers.cpp
${source_DIR}/skyline/gpu/interconnect/maxwell_3d/queries.cpp
${source_DIR}/skyline/gpu/interconnect/maxwell_3d/maxwell_3d.cpp
${source_DIR}/skyline/gpu/interconnect/kepler_compute/pipeline_manager.cpp
${source_DIR}/skyline/gpu/interconnect/kepler_compute/pipeline_state.cpp
${source_DIR}/skyline/gpu/interconnect/kepler_compute/kepler_compute.cpp
${source_DIR}/skyline/gpu/interconnect/kepler_compute/constant_buffers.cpp
${source_DIR}/skyline/gpu/interconnect/command_executor.cpp
${source_DIR}/skyline/gpu/interconnect/command_nodes.cpp
${source_DIR}/skyline/gpu/interconnect/conversion/quads.cpp
${source_DIR}/skyline/gpu/interconnect/common/common.cpp
${source_DIR}/skyline/gpu/interconnect/common/samplers.cpp
${source_DIR}/skyline/gpu/interconnect/common/textures.cpp
${source_DIR}/skyline/gpu/interconnect/common/shader_cache.cpp
${source_DIR}/skyline/gpu/interconnect/common/pipeline_state_bundle.cpp
${source_DIR}/skyline/gpu/interconnect/common/file_pipeline_state_accessor.cpp
${source_DIR}/skyline/gpu/shaders/helper_shaders.cpp
${source_DIR}/skyline/soc/smmu.cpp
${source_DIR}/skyline/soc/host1x/syncpoint.cpp
${source_DIR}/skyline/soc/host1x/command_fifo.cpp
${source_DIR}/skyline/soc/host1x/classes/host1x.cpp
${source_DIR}/skyline/soc/host1x/classes/vic.cpp
${source_DIR}/skyline/soc/host1x/classes/nvdec.cpp
${source_DIR}/skyline/soc/gm20b/channel.cpp
${source_DIR}/skyline/soc/gm20b/gpfifo.cpp
${source_DIR}/skyline/soc/gm20b/gmmu.cpp
${source_DIR}/skyline/soc/gm20b/macro/macro_state.cpp
${source_DIR}/skyline/soc/gm20b/macro/macro_interpreter.cpp
${source_DIR}/skyline/soc/gm20b/engines/engine.cpp
${source_DIR}/skyline/soc/gm20b/engines/gpfifo.cpp
${source_DIR}/skyline/soc/gm20b/engines/maxwell_3d.cpp
${source_DIR}/skyline/soc/gm20b/engines/inline2memory.cpp
${source_DIR}/skyline/soc/gm20b/engines/kepler_compute.cpp
${source_DIR}/skyline/soc/gm20b/engines/maxwell_dma.cpp
${source_DIR}/skyline/soc/gm20b/engines/maxwell/initialization.cpp
${source_DIR}/skyline/soc/gm20b/engines/fermi_2d.cpp
${source_DIR}/skyline/input.cpp
${source_DIR}/skyline/gpu/macro_interpreter.cpp
${source_DIR}/skyline/gpu/memory_manager.cpp
${source_DIR}/skyline/gpu/gpfifo.cpp
${source_DIR}/skyline/gpu/syncpoint.cpp
${source_DIR}/skyline/gpu/texture.cpp
${source_DIR}/skyline/gpu/engines/maxwell_3d.cpp
${source_DIR}/skyline/input/npad.cpp
${source_DIR}/skyline/input/npad_device.cpp
${source_DIR}/skyline/input/touch.cpp
@ -252,7 +84,6 @@ add_library(skyline SHARED
${source_DIR}/skyline/loader/nca.cpp
${source_DIR}/skyline/loader/xci.cpp
${source_DIR}/skyline/loader/nsp.cpp
${source_DIR}/skyline/hle/symbol_hooks.cpp
${source_DIR}/skyline/vfs/partition_filesystem.cpp
${source_DIR}/skyline/vfs/ctr_encrypted_backing.cpp
${source_DIR}/skyline/vfs/rom_filesystem.cpp
@ -266,14 +97,16 @@ add_library(skyline SHARED
${source_DIR}/skyline/vfs/ticket.cpp
${source_DIR}/skyline/services/serviceman.cpp
${source_DIR}/skyline/services/base_service.cpp
${source_DIR}/skyline/services/common/parcel.cpp
${source_DIR}/skyline/services/sm/IUserInterface.cpp
${source_DIR}/skyline/services/fatalsrv/IService.cpp
${source_DIR}/skyline/services/audio/IAudioInManager.cpp
${source_DIR}/skyline/services/audio/IAudioOutManager.cpp
${source_DIR}/skyline/services/audio/IAudioOut.cpp
${source_DIR}/skyline/services/audio/IAudioDevice.cpp
${source_DIR}/skyline/services/audio/IAudioRendererManager.cpp
${source_DIR}/skyline/services/audio/IAudioRenderer.cpp
${source_DIR}/skyline/services/audio/IAudioRenderer/IAudioRenderer.cpp
${source_DIR}/skyline/services/audio/IAudioRenderer/voice.cpp
${source_DIR}/skyline/services/audio/IAudioRenderer/memory_pool.cpp
${source_DIR}/skyline/services/settings/ISettingsServer.cpp
${source_DIR}/skyline/services/settings/ISystemSettingsServer.cpp
${source_DIR}/skyline/services/apm/IManager.cpp
@ -295,37 +128,11 @@ add_library(skyline SHARED
${source_DIR}/skyline/services/am/controller/ISelfController.cpp
${source_DIR}/skyline/services/am/controller/IWindowController.cpp
${source_DIR}/skyline/services/am/storage/IStorage.cpp
${source_DIR}/skyline/services/am/storage/VectorIStorage.cpp
${source_DIR}/skyline/services/am/storage/TransferMemoryIStorage.cpp
${source_DIR}/skyline/services/am/storage/IStorageAccessor.cpp
${source_DIR}/skyline/services/am/applet/ILibraryAppletAccessor.cpp
${source_DIR}/skyline/services/am/applet/IApplet.cpp
${source_DIR}/skyline/services/bcat/IBcatService.cpp
${source_DIR}/skyline/services/bcat/IDeliveryCacheStorageService.cpp
${source_DIR}/skyline/services/bcat/IDeliveryCacheFileService.cpp
${source_DIR}/skyline/services/bcat/IDeliveryCacheDirectoryService.cpp
${source_DIR}/skyline/services/bcat/IServiceCreator.cpp
${source_DIR}/skyline/services/bt/IBluetoothUser.cpp
${source_DIR}/skyline/services/btm/IBtmUser.cpp
${source_DIR}/skyline/services/btm/IBtmUserCore.cpp
${source_DIR}/skyline/services/capsrv/IAlbumAccessorService.cpp
${source_DIR}/skyline/services/capsrv/ICaptureControllerService.cpp
${source_DIR}/skyline/services/capsrv/IAlbumApplicationService.cpp
${source_DIR}/skyline/services/capsrv/IScreenShotApplicationService.cpp
${source_DIR}/skyline/services/ro/IRoInterface.cpp
${source_DIR}/skyline/applet/applet_creator.cpp
${source_DIR}/skyline/applet/controller_applet.cpp
${source_DIR}/skyline/applet/error_applet.cpp
${source_DIR}/skyline/applet/player_select_applet.cpp
${source_DIR}/skyline/applet/web_applet.cpp
${source_DIR}/skyline/applet/swkbd/software_keyboard_applet.cpp
${source_DIR}/skyline/applet/swkbd/software_keyboard_config.cpp
${source_DIR}/skyline/services/codec/IHardwareOpusDecoder.cpp
${source_DIR}/skyline/services/codec/IHardwareOpusDecoderManager.cpp
${source_DIR}/skyline/services/hid/IHidServer.cpp
${source_DIR}/skyline/services/hid/IAppletResource.cpp
${source_DIR}/skyline/services/hid/IActiveVibrationDeviceList.cpp
${source_DIR}/skyline/services/irs/IIrSensorServer.cpp
${source_DIR}/skyline/services/timesrv/common.cpp
${source_DIR}/skyline/services/timesrv/core.cpp
${source_DIR}/skyline/services/timesrv/time_shared_memory.cpp
@ -337,42 +144,33 @@ add_library(skyline SHARED
${source_DIR}/skyline/services/timesrv/ITimeZoneService.cpp
${source_DIR}/skyline/services/glue/IStaticService.cpp
${source_DIR}/skyline/services/glue/ITimeZoneService.cpp
${source_DIR}/skyline/services/glue/IWriterForSystem.cpp
${source_DIR}/skyline/services/glue/INotificationServicesForApplication.cpp
${source_DIR}/skyline/services/fssrv/IFileSystemProxy.cpp
${source_DIR}/skyline/services/fssrv/IFileSystem.cpp
${source_DIR}/skyline/services/fssrv/IFile.cpp
${source_DIR}/skyline/services/fssrv/IStorage.cpp
${source_DIR}/skyline/services/fssrv/IDirectory.cpp
${source_DIR}/skyline/services/fssrv/IMultiCommitManager.cpp
${source_DIR}/skyline/services/nvdrv/INvDrvServices.cpp
${source_DIR}/skyline/services/nvdrv/driver.cpp
${source_DIR}/skyline/services/nvdrv/core/nvmap.cpp
${source_DIR}/skyline/services/nvdrv/core/syncpoint_manager.cpp
${source_DIR}/skyline/services/nvdrv/devices/nvdevice.cpp
${source_DIR}/skyline/services/nvdrv/devices/nvmap.cpp
${source_DIR}/skyline/services/nvdrv/devices/nvhost/as_gpu.cpp
${source_DIR}/skyline/services/nvdrv/devices/nvhost/ctrl.cpp
${source_DIR}/skyline/services/nvdrv/devices/nvhost/ctrl_gpu.cpp
${source_DIR}/skyline/services/nvdrv/devices/nvhost/gpu_channel.cpp
${source_DIR}/skyline/services/nvdrv/devices/nvhost/host1x_channel.cpp
${source_DIR}/skyline/services/hosbinder/parcel.cpp
${source_DIR}/skyline/services/nvdrv/devices/nvhost_ctrl_gpu.cpp
${source_DIR}/skyline/services/nvdrv/devices/nvhost_ctrl.cpp
${source_DIR}/skyline/services/nvdrv/devices/nvhost_channel.cpp
${source_DIR}/skyline/services/nvdrv/devices/nvhost_as_gpu.cpp
${source_DIR}/skyline/services/nvdrv/devices/nvhost_syncpoint.cpp
${source_DIR}/skyline/services/hosbinder/IHOSBinderDriver.cpp
${source_DIR}/skyline/services/hosbinder/GraphicBufferProducer.cpp
${source_DIR}/skyline/services/visrv/IDisplayService.cpp
${source_DIR}/skyline/services/visrv/IApplicationDisplayService.cpp
${source_DIR}/skyline/services/visrv/IManagerDisplayService.cpp
${source_DIR}/skyline/services/visrv/IRootService.cpp
${source_DIR}/skyline/services/visrv/IManagerRootService.cpp
${source_DIR}/skyline/services/visrv/ISystemDisplayService.cpp
${source_DIR}/skyline/services/pl/IPlatformServiceManager.cpp
${source_DIR}/skyline/services/aocsrv/IAddOnContentManager.cpp
${source_DIR}/skyline/services/aocsrv/IPurchaseEventManager.cpp
${source_DIR}/skyline/services/pctl/IParentalControlServiceFactory.cpp
${source_DIR}/skyline/services/pctl/IParentalControlService.cpp
${source_DIR}/skyline/services/lm/ILogService.cpp
${source_DIR}/skyline/services/lm/ILogger.cpp
${source_DIR}/skyline/services/ldn/IUserServiceCreator.cpp
${source_DIR}/skyline/services/ldn/IUserLocalCommunicationService.cpp
${source_DIR}/skyline/services/account/IAccountServiceForApplication.cpp
${source_DIR}/skyline/services/account/IManagerForApplication.cpp
${source_DIR}/skyline/services/account/IProfile.cpp
@ -383,27 +181,12 @@ add_library(skyline SHARED
${source_DIR}/skyline/services/nfp/IUser.cpp
${source_DIR}/skyline/services/nifm/IStaticService.cpp
${source_DIR}/skyline/services/nifm/IGeneralService.cpp
${source_DIR}/skyline/services/nifm/IScanRequest.cpp
${source_DIR}/skyline/services/nifm/IRequest.cpp
${source_DIR}/skyline/services/nim/IShopServiceAccessor.cpp
${source_DIR}/skyline/services/nim/IShopServiceAccessServer.cpp
${source_DIR}/skyline/services/nim/IShopServiceAccessServerInterface.cpp
${source_DIR}/skyline/services/nim/IShopServiceAsync.cpp
${source_DIR}/skyline/services/socket/bsd/IClient.cpp
${source_DIR}/skyline/services/socket/nsd/IManager.cpp
${source_DIR}/skyline/services/socket/sfdnsres/IResolver.cpp
${source_DIR}/skyline/services/spl/IRandomInterface.cpp
${source_DIR}/skyline/services/ssl/ISslService.cpp
${source_DIR}/skyline/services/ssl/ISslContext.cpp
${source_DIR}/skyline/services/prepo/IPrepoService.cpp
${source_DIR}/skyline/services/mmnv/IRequest.cpp
${source_DIR}/skyline/services/mii/IStaticService.cpp
${source_DIR}/skyline/services/mii/IDatabaseService.cpp
${source_DIR}/skyline/services/olsc/IOlscServiceForApplication.cpp
)
target_include_directories(skyline PRIVATE ${source_DIR}/skyline)
# target_precompile_headers(skyline PRIVATE ${source_DIR}/skyline/common.h) # PCH will currently break Intellisense
target_compile_options(skyline PRIVATE -Wall -Wno-unknown-attributes -Wno-c++20-extensions -Wno-c++17-extensions -Wno-c99-designator -Wno-reorder -Wno-missing-braces -Wno-unused-variable -Wno-unused-private-field -Wno-dangling-else -Wconversion -fsigned-bitfields)
target_link_libraries(skyline PRIVATE shader_recompiler audio_core)
target_link_libraries_system(skyline android perfetto fmt lz4_static tzcode vkma mbedcrypto opus Boost::intrusive Boost::container range-v3 adrenotools tsl::robin_map)
target_link_libraries(skyline vulkan android perfetto fmt lz4_static tzcode oboe mbedtls::mbedcrypto)
target_compile_options(skyline PRIVATE -Wall -Wno-unknown-attributes -Wno-c++20-extensions -Wno-c++17-extensions -Wno-c99-designator -Wno-reorder -Wno-missing-braces -Wno-unused-variable -Wno-unused-private-field)

View file

@ -1,78 +1,35 @@
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'kotlin-kapt'
id 'com.google.dagger.hilt.android'
id 'idea'
id 'org.jetbrains.kotlin.plugin.serialization' version "$kotlin_version"
}
idea.module {
// These are not viable to index on most systems so exclude them to prevent IDE crashes
excludeDirs.add(file("libraries/boost"))
excludeDirs.add(file("libraries/llvm"))
}
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
namespace 'emu.skyline'
compileSdk 33
var isBuildSigned = (System.getenv("CI") == "true") && (System.getenv("IS_SKYLINE_SIGNED") == "true")
compileSdkVersion 30
buildToolsVersion '30.0.3'
defaultConfig {
applicationId "skyline.emu"
minSdk 29
targetSdk 33
minSdkVersion 26
targetSdkVersion 30
versionCode 3
versionName "0.0.3"
versionName "0.3"
ndk {
//noinspection ChromeOsAbiSupport
abiFilters "arm64-v8a"
}
if (isBuildSigned)
manifestPlaceholders += [shouldSaveUserData: "true"]
else
manifestPlaceholders += [shouldSaveUserData: "false"]
// Only enable separate process for release builds
manifestPlaceholders += [emulationProcess: ""]
def locales = ["en", "de", "el", "es", "es-419", "fr", "hu", "id", "it", "ja", "ko", "pl", "ru", "ta", "zh-Hans", "zh-Hant"]
// Add available locales to the build config so that they can be accessed from the app
buildConfigField "String[]", "AVAILABLE_APP_LANGUAGES", "new String[]{\"" + locales.join("\",\"") + "\"}"
// Uncomment the following line whenever AAPT2 will properly support BCP47 language tags
//resourceConfigurations += locales
}
/* JVM Bytecode Options */
def javaVersion = JavaVersion.VERSION_1_8
compileOptions {
sourceCompatibility = javaVersion
targetCompatibility = javaVersion
}
kotlinOptions {
jvmTarget = javaVersion.toString()
freeCompilerArgs += "-opt-in=kotlin.RequiresOptIn"
}
compileOptions {
sourceCompatibility javaVersion
targetCompatibility javaVersion
}
packagingOptions {
jniLibs.useLegacyPackaging = true
}
signingConfigs {
ci {
storeFile file(System.getenv("SIGNING_STORE_PATH") ?: "${System.getenv("user.home")}/keystore.jks")
storePassword System.getenv("SIGNING_STORE_PASSWORD")
keyAlias System.getenv("SIGNING_KEY_ALIAS")
keyPassword System.getenv("SIGNING_KEY_PASSWORD")
}
}
/* Build Options */
buildTypes {
release {
debuggable true
@ -84,113 +41,63 @@ android {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
signingConfig = isBuildSigned ? signingConfigs.ci : signingConfigs.debug
manifestPlaceholders += [emulationProcess: ":emulationProcess"]
}
reldebug {
debuggable true
externalNativeBuild {
cmake {
arguments "-DCMAKE_BUILD_TYPE=RELWITHDEBINFO"
}
}
minifyEnabled false
shrinkResources false
signingConfig = isBuildSigned ? signingConfigs.ci : signingConfigs.debug
signingConfig signingConfigs.debug
}
debug {
debuggable true
minifyEnabled false
shrinkResources false
signingConfig = isBuildSigned ? signingConfigs.ci : signingConfigs.debug
}
}
flavorDimensions += "version"
productFlavors {
full {
dimension = "version"
manifestPlaceholders += [appLabel: "Skyline"]
}
dev {
dimension = "version"
applicationIdSuffix = ".dev"
versionNameSuffix = "-dev"
manifestPlaceholders += [appLabel: "Skyline Dev"]
}
}
buildFeatures {
viewBinding true
prefab true
viewBinding false
}
/* Android Extensions */
androidExtensions {
/* TODO: Remove this after migrating to View Bindings */
experimental = true
}
/* Linting */
lint {
lintOptions {
disable 'IconLocation'
}
/* NDK and CMake */
ndkVersion '25.0.8775105'
/* NDK */
ndkVersion '22.0.7026061'
externalNativeBuild {
cmake {
version '3.22.1+'
version '3.18.1+'
path "CMakeLists.txt"
}
}
/* Android Assets */
androidResources {
ignoreAssetsPattern '*.md'
}
/* Vulkan Validation Layers */
sourceSets {
reldebug {
jniLibs {
srcDir "libraries/vklayers"
}
}
debug {
jniLibs {
srcDir "libraries/vklayers"
}
}
aaptOptions {
ignoreAssetsPattern "*.md"
}
}
dependencies {
/* Filetrees */
implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
/* Google */
implementation 'androidx.core:core-ktx:1.9.0'
implementation 'androidx.appcompat:appcompat:1.6.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.preference:preference-ktx:1.2.0'
implementation 'androidx.activity:activity-ktx:1.6.1'
implementation 'com.google.android.material:material:1.8.0'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'androidx.preference:preference-ktx:1.1.1'
implementation 'com.google.android.material:material:1.3.0'
implementation 'androidx.documentfile:documentfile:1.0.1'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.5.1"
implementation 'androidx.fragment:fragment-ktx:1.5.5'
implementation "com.google.dagger:hilt-android:$hilt_version"
kapt "com.google.dagger:hilt-compiler:$hilt_version"
implementation 'com.google.android.flexbox:flexbox:3.0.0'
/* Kotlin */
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.1"
/* JetBrains */
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "androidx.core:core-ktx:1.3.2"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
/* Other Java */
implementation 'info.debatty:java-string-similarity:2.0.0'
implementation 'com.github.KikiManjaro:colorpicker:v1.1.12'
}
kapt {
correctErrorTypes true
/* NDK */
implementation files("libraries/mbedtls.aar")
}

@ -1 +0,0 @@
Subproject commit 19d1998c5a39dc18f90086e068ce7b3331110d74

@ -1 +0,0 @@
Subproject commit 76440e0a3554433398c0ef03233f862a6e86f5ee

@ -1 +0,0 @@
Subproject commit 06d52af216340ed46b865d01c4f7c0d7a8cc5918

@ -1 +0,0 @@
Subproject commit 2d93d734ecff78deb060abd727712bae8ab0489a

@ -1 +1 @@
Subproject commit dcd282bb268a0766f6d273b6e41a3a96719bbbfb
Subproject commit 9c418bc468baf434a848010bff74663e1f820e79

@ -1 +1 @@
Subproject commit 7264ab0eae2a072afa55d617d5e9e11bcb464aae
Subproject commit e6ddc432d0ba4e42542b5867bdc932b5f6d1e08d

@ -1 +0,0 @@
Subproject commit abffdd88767791ef6da4d2df7ec7ab158eb8b775

@ -1 +0,0 @@
Subproject commit d895668359eb8ba7536a04f5e2be1e5268c86b33

BIN
app/libraries/mbedtls.aar Normal file

Binary file not shown.

@ -1 +1 @@
Subproject commit 9053f015ce1317d3c8bac0111c516b377c2e287e
Subproject commit 5eb2ea6899367f9cdc5a262f340c06e1a7c284b2

@ -1 +0,0 @@
Subproject commit 6b6035ae4a29abbd237463d84a45fbeb0d92bc18

@ -1 +1 @@
Subproject commit e5293f1cf1800d5776f4bfe96d1ec0aeec30cd46
Subproject commit 3f02be823cef0f54e720c0382ffc4507f48e6e4b

1
app/libraries/pugixml Submodule

@ -0,0 +1 @@
Subproject commit 08b3433180727ea2f78fe02e860a08471db1e03c

@ -1 +0,0 @@
Subproject commit 83783f578e0e6666d68a3bf17b0038a80e62530e

View file

@ -1,707 +0,0 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2019-2022 Baldur Karlsson
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#pragma once
//////////////////////////////////////////////////////////////////////////////////////////////////
//
// Documentation for the API is available at https://renderdoc.org/docs/in_application_api.html
//
#if !defined(RENDERDOC_NO_STDINT)
#include <stdint.h>
#endif
#if defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER)
#define RENDERDOC_CC __cdecl
#elif defined(__linux__)
#define RENDERDOC_CC
#elif defined(__APPLE__)
#define RENDERDOC_CC
#else
#error "Unknown platform"
#endif
#ifdef __cplusplus
extern "C" {
#endif
//////////////////////////////////////////////////////////////////////////////////////////////////
// Constants not used directly in below API
// This is a GUID/magic value used for when applications pass a path where shader debug
// information can be found to match up with a stripped shader.
// the define can be used like so: const GUID RENDERDOC_ShaderDebugMagicValue =
// RENDERDOC_ShaderDebugMagicValue_value
#define RENDERDOC_ShaderDebugMagicValue_struct \
{ \
0xeab25520, 0x6670, 0x4865, 0x84, 0x29, 0x6c, 0x8, 0x51, 0x54, 0x00, 0xff \
}
// as an alternative when you want a byte array (assuming x86 endianness):
#define RENDERDOC_ShaderDebugMagicValue_bytearray \
{ \
0x20, 0x55, 0xb2, 0xea, 0x70, 0x66, 0x65, 0x48, 0x84, 0x29, 0x6c, 0x8, 0x51, 0x54, 0x00, 0xff \
}
// truncated version when only a uint64_t is available (e.g. Vulkan tags):
#define RENDERDOC_ShaderDebugMagicValue_truncated 0x48656670eab25520ULL
//////////////////////////////////////////////////////////////////////////////////////////////////
// RenderDoc capture options
//
typedef enum RENDERDOC_CaptureOption {
// Allow the application to enable vsync
//
// Default - enabled
//
// 1 - The application can enable or disable vsync at will
// 0 - vsync is force disabled
eRENDERDOC_Option_AllowVSync = 0,
// Allow the application to enable fullscreen
//
// Default - enabled
//
// 1 - The application can enable or disable fullscreen at will
// 0 - fullscreen is force disabled
eRENDERDOC_Option_AllowFullscreen = 1,
// Record API debugging events and messages
//
// Default - disabled
//
// 1 - Enable built-in API debugging features and records the results into
// the capture, which is matched up with events on replay
// 0 - no API debugging is forcibly enabled
eRENDERDOC_Option_APIValidation = 2,
eRENDERDOC_Option_DebugDeviceMode = 2, // deprecated name of this enum
// Capture CPU callstacks for API events
//
// Default - disabled
//
// 1 - Enables capturing of callstacks
// 0 - no callstacks are captured
eRENDERDOC_Option_CaptureCallstacks = 3,
// When capturing CPU callstacks, only capture them from actions.
// This option does nothing without the above option being enabled
//
// Default - disabled
//
// 1 - Only captures callstacks for actions.
// Ignored if CaptureCallstacks is disabled
// 0 - Callstacks, if enabled, are captured for every event.
eRENDERDOC_Option_CaptureCallstacksOnlyDraws = 4,
eRENDERDOC_Option_CaptureCallstacksOnlyActions = 4,
// Specify a delay in seconds to wait for a debugger to attach, after
// creating or injecting into a process, before continuing to allow it to run.
//
// 0 indicates no delay, and the process will run immediately after injection
//
// Default - 0 seconds
//
eRENDERDOC_Option_DelayForDebugger = 5,
// Verify buffer access. This includes checking the memory returned by a Map() call to
// detect any out-of-bounds modification, as well as initialising buffers with undefined contents
// to a marker value to catch use of uninitialised memory.
//
// NOTE: This option is only valid for OpenGL and D3D11. Explicit APIs such as D3D12 and Vulkan do
// not do the same kind of interception & checking and undefined contents are really undefined.
//
// Default - disabled
//
// 1 - Verify buffer access
// 0 - No verification is performed, and overwriting bounds may cause crashes or corruption in
// RenderDoc.
eRENDERDOC_Option_VerifyBufferAccess = 6,
// The old name for eRENDERDOC_Option_VerifyBufferAccess was eRENDERDOC_Option_VerifyMapWrites.
// This option now controls the filling of uninitialised buffers with 0xdddddddd which was
// previously always enabled
eRENDERDOC_Option_VerifyMapWrites = eRENDERDOC_Option_VerifyBufferAccess,
// Hooks any system API calls that create child processes, and injects
// RenderDoc into them recursively with the same options.
//
// Default - disabled
//
// 1 - Hooks into spawned child processes
// 0 - Child processes are not hooked by RenderDoc
eRENDERDOC_Option_HookIntoChildren = 7,
// By default RenderDoc only includes resources in the final capture necessary
// for that frame, this allows you to override that behaviour.
//
// Default - disabled
//
// 1 - all live resources at the time of capture are included in the capture
// and available for inspection
// 0 - only the resources referenced by the captured frame are included
eRENDERDOC_Option_RefAllResources = 8,
// **NOTE**: As of RenderDoc v1.1 this option has been deprecated. Setting or
// getting it will be ignored, to allow compatibility with older versions.
// In v1.1 the option acts as if it's always enabled.
//
// By default RenderDoc skips saving initial states for resources where the
// previous contents don't appear to be used, assuming that writes before
// reads indicate previous contents aren't used.
//
// Default - disabled
//
// 1 - initial contents at the start of each captured frame are saved, even if
// they are later overwritten or cleared before being used.
// 0 - unless a read is detected, initial contents will not be saved and will
// appear as black or empty data.
eRENDERDOC_Option_SaveAllInitials = 9,
// In APIs that allow for the recording of command lists to be replayed later,
// RenderDoc may choose to not capture command lists before a frame capture is
// triggered, to reduce overheads. This means any command lists recorded once
// and replayed many times will not be available and may cause a failure to
// capture.
//
// NOTE: This is only true for APIs where multithreading is difficult or
// discouraged. Newer APIs like Vulkan and D3D12 will ignore this option
// and always capture all command lists since the API is heavily oriented
// around it and the overheads have been reduced by API design.
//
// 1 - All command lists are captured from the start of the application
// 0 - Command lists are only captured if their recording begins during
// the period when a frame capture is in progress.
eRENDERDOC_Option_CaptureAllCmdLists = 10,
// Mute API debugging output when the API validation mode option is enabled
//
// Default - enabled
//
// 1 - Mute any API debug messages from being displayed or passed through
// 0 - API debugging is displayed as normal
eRENDERDOC_Option_DebugOutputMute = 11,
// Option to allow vendor extensions to be used even when they may be
// incompatible with RenderDoc and cause corrupted replays or crashes.
//
// Default - inactive
//
// No values are documented, this option should only be used when absolutely
// necessary as directed by a RenderDoc developer.
eRENDERDOC_Option_AllowUnsupportedVendorExtensions = 12,
} RENDERDOC_CaptureOption;
// Sets an option that controls how RenderDoc behaves on capture.
//
// Returns 1 if the option and value are valid
// Returns 0 if either is invalid and the option is unchanged
typedef int(RENDERDOC_CC *pRENDERDOC_SetCaptureOptionU32)(RENDERDOC_CaptureOption opt, uint32_t val);
typedef int(RENDERDOC_CC *pRENDERDOC_SetCaptureOptionF32)(RENDERDOC_CaptureOption opt, float val);
// Gets the current value of an option as a uint32_t
//
// If the option is invalid, 0xffffffff is returned
typedef uint32_t(RENDERDOC_CC *pRENDERDOC_GetCaptureOptionU32)(RENDERDOC_CaptureOption opt);
// Gets the current value of an option as a float
//
// If the option is invalid, -FLT_MAX is returned
typedef float(RENDERDOC_CC *pRENDERDOC_GetCaptureOptionF32)(RENDERDOC_CaptureOption opt);
typedef enum RENDERDOC_InputButton {
// '0' - '9' matches ASCII values
eRENDERDOC_Key_0 = 0x30,
eRENDERDOC_Key_1 = 0x31,
eRENDERDOC_Key_2 = 0x32,
eRENDERDOC_Key_3 = 0x33,
eRENDERDOC_Key_4 = 0x34,
eRENDERDOC_Key_5 = 0x35,
eRENDERDOC_Key_6 = 0x36,
eRENDERDOC_Key_7 = 0x37,
eRENDERDOC_Key_8 = 0x38,
eRENDERDOC_Key_9 = 0x39,
// 'A' - 'Z' matches ASCII values
eRENDERDOC_Key_A = 0x41,
eRENDERDOC_Key_B = 0x42,
eRENDERDOC_Key_C = 0x43,
eRENDERDOC_Key_D = 0x44,
eRENDERDOC_Key_E = 0x45,
eRENDERDOC_Key_F = 0x46,
eRENDERDOC_Key_G = 0x47,
eRENDERDOC_Key_H = 0x48,
eRENDERDOC_Key_I = 0x49,
eRENDERDOC_Key_J = 0x4A,
eRENDERDOC_Key_K = 0x4B,
eRENDERDOC_Key_L = 0x4C,
eRENDERDOC_Key_M = 0x4D,
eRENDERDOC_Key_N = 0x4E,
eRENDERDOC_Key_O = 0x4F,
eRENDERDOC_Key_P = 0x50,
eRENDERDOC_Key_Q = 0x51,
eRENDERDOC_Key_R = 0x52,
eRENDERDOC_Key_S = 0x53,
eRENDERDOC_Key_T = 0x54,
eRENDERDOC_Key_U = 0x55,
eRENDERDOC_Key_V = 0x56,
eRENDERDOC_Key_W = 0x57,
eRENDERDOC_Key_X = 0x58,
eRENDERDOC_Key_Y = 0x59,
eRENDERDOC_Key_Z = 0x5A,
// leave the rest of the ASCII range free
// in case we want to use it later
eRENDERDOC_Key_NonPrintable = 0x100,
eRENDERDOC_Key_Divide,
eRENDERDOC_Key_Multiply,
eRENDERDOC_Key_Subtract,
eRENDERDOC_Key_Plus,
eRENDERDOC_Key_F1,
eRENDERDOC_Key_F2,
eRENDERDOC_Key_F3,
eRENDERDOC_Key_F4,
eRENDERDOC_Key_F5,
eRENDERDOC_Key_F6,
eRENDERDOC_Key_F7,
eRENDERDOC_Key_F8,
eRENDERDOC_Key_F9,
eRENDERDOC_Key_F10,
eRENDERDOC_Key_F11,
eRENDERDOC_Key_F12,
eRENDERDOC_Key_Home,
eRENDERDOC_Key_End,
eRENDERDOC_Key_Insert,
eRENDERDOC_Key_Delete,
eRENDERDOC_Key_PageUp,
eRENDERDOC_Key_PageDn,
eRENDERDOC_Key_Backspace,
eRENDERDOC_Key_Tab,
eRENDERDOC_Key_PrtScrn,
eRENDERDOC_Key_Pause,
eRENDERDOC_Key_Max,
} RENDERDOC_InputButton;
// Sets which key or keys can be used to toggle focus between multiple windows
//
// If keys is NULL or num is 0, toggle keys will be disabled
typedef void(RENDERDOC_CC *pRENDERDOC_SetFocusToggleKeys)(RENDERDOC_InputButton *keys, int num);
// Sets which key or keys can be used to capture the next frame
//
// If keys is NULL or num is 0, captures keys will be disabled
typedef void(RENDERDOC_CC *pRENDERDOC_SetCaptureKeys)(RENDERDOC_InputButton *keys, int num);
typedef enum RENDERDOC_OverlayBits {
// This single bit controls whether the overlay is enabled or disabled globally
eRENDERDOC_Overlay_Enabled = 0x1,
// Show the average framerate over several seconds as well as min/max
eRENDERDOC_Overlay_FrameRate = 0x2,
// Show the current frame number
eRENDERDOC_Overlay_FrameNumber = 0x4,
// Show a list of recent captures, and how many captures have been made
eRENDERDOC_Overlay_CaptureList = 0x8,
// Default values for the overlay mask
eRENDERDOC_Overlay_Default = (eRENDERDOC_Overlay_Enabled | eRENDERDOC_Overlay_FrameRate |
eRENDERDOC_Overlay_FrameNumber | eRENDERDOC_Overlay_CaptureList),
// Enable all bits
eRENDERDOC_Overlay_All = ~0U,
// Disable all bits
eRENDERDOC_Overlay_None = 0,
} RENDERDOC_OverlayBits;
// returns the overlay bits that have been set
typedef uint32_t(RENDERDOC_CC *pRENDERDOC_GetOverlayBits)();
// sets the overlay bits with an and & or mask
typedef void(RENDERDOC_CC *pRENDERDOC_MaskOverlayBits)(uint32_t And, uint32_t Or);
// this function will attempt to remove RenderDoc's hooks in the application.
//
// Note: that this can only work correctly if done immediately after
// the module is loaded, before any API work happens. RenderDoc will remove its
// injected hooks and shut down. Behaviour is undefined if this is called
// after any API functions have been called, and there is still no guarantee of
// success.
typedef void(RENDERDOC_CC *pRENDERDOC_RemoveHooks)();
// DEPRECATED: compatibility for code compiled against pre-1.4.1 headers.
typedef pRENDERDOC_RemoveHooks pRENDERDOC_Shutdown;
// This function will unload RenderDoc's crash handler.
//
// If you use your own crash handler and don't want RenderDoc's handler to
// intercede, you can call this function to unload it and any unhandled
// exceptions will pass to the next handler.
typedef void(RENDERDOC_CC *pRENDERDOC_UnloadCrashHandler)();
// Sets the capture file path template
//
// pathtemplate is a UTF-8 string that gives a template for how captures will be named
// and where they will be saved.
//
// Any extension is stripped off the path, and captures are saved in the directory
// specified, and named with the filename and the frame number appended. If the
// directory does not exist it will be created, including any parent directories.
//
// If pathtemplate is NULL, the template will remain unchanged
//
// Example:
//
// SetCaptureFilePathTemplate("my_captures/example");
//
// Capture #1 -> my_captures/example_frame123.rdc
// Capture #2 -> my_captures/example_frame456.rdc
typedef void(RENDERDOC_CC *pRENDERDOC_SetCaptureFilePathTemplate)(const char *pathtemplate);
// returns the current capture path template, see SetCaptureFileTemplate above, as a UTF-8 string
typedef const char *(RENDERDOC_CC *pRENDERDOC_GetCaptureFilePathTemplate)();
// DEPRECATED: compatibility for code compiled against pre-1.1.2 headers.
typedef pRENDERDOC_SetCaptureFilePathTemplate pRENDERDOC_SetLogFilePathTemplate;
typedef pRENDERDOC_GetCaptureFilePathTemplate pRENDERDOC_GetLogFilePathTemplate;
// returns the number of captures that have been made
typedef uint32_t(RENDERDOC_CC *pRENDERDOC_GetNumCaptures)();
// This function returns the details of a capture, by index. New captures are added
// to the end of the list.
//
// filename will be filled with the absolute path to the capture file, as a UTF-8 string
// pathlength will be written with the length in bytes of the filename string
// timestamp will be written with the time of the capture, in seconds since the Unix epoch
//
// Any of the parameters can be NULL and they'll be skipped.
//
// The function will return 1 if the capture index is valid, or 0 if the index is invalid
// If the index is invalid, the values will be unchanged
//
// Note: when captures are deleted in the UI they will remain in this list, so the
// capture path may not exist anymore.
typedef uint32_t(RENDERDOC_CC *pRENDERDOC_GetCapture)(uint32_t idx, char *filename,
uint32_t *pathlength, uint64_t *timestamp);
// Sets the comments associated with a capture file. These comments are displayed in the
// UI program when opening.
//
// filePath should be a path to the capture file to add comments to. If set to NULL or ""
// the most recent capture file created made will be used instead.
// comments should be a NULL-terminated UTF-8 string to add as comments.
//
// Any existing comments will be overwritten.
typedef void(RENDERDOC_CC *pRENDERDOC_SetCaptureFileComments)(const char *filePath,
const char *comments);
// returns 1 if the RenderDoc UI is connected to this application, 0 otherwise
typedef uint32_t(RENDERDOC_CC *pRENDERDOC_IsTargetControlConnected)();
// DEPRECATED: compatibility for code compiled against pre-1.1.1 headers.
// This was renamed to IsTargetControlConnected in API 1.1.1, the old typedef is kept here for
// backwards compatibility with old code, it is castable either way since it's ABI compatible
// as the same function pointer type.
typedef pRENDERDOC_IsTargetControlConnected pRENDERDOC_IsRemoteAccessConnected;
// This function will launch the Replay UI associated with the RenderDoc library injected
// into the running application.
//
// if connectTargetControl is 1, the Replay UI will be launched with a command line parameter
// to connect to this application
// cmdline is the rest of the command line, as a UTF-8 string. E.g. a captures to open
// if cmdline is NULL, the command line will be empty.
//
// returns the PID of the replay UI if successful, 0 if not successful.
typedef uint32_t(RENDERDOC_CC *pRENDERDOC_LaunchReplayUI)(uint32_t connectTargetControl,
const char *cmdline);
// RenderDoc can return a higher version than requested if it's backwards compatible,
// this function returns the actual version returned. If a parameter is NULL, it will be
// ignored and the others will be filled out.
typedef void(RENDERDOC_CC *pRENDERDOC_GetAPIVersion)(int *major, int *minor, int *patch);
//////////////////////////////////////////////////////////////////////////
// Capturing functions
//
// A device pointer is a pointer to the API's root handle.
//
// This would be an ID3D11Device, HGLRC/GLXContext, ID3D12Device, etc
typedef void *RENDERDOC_DevicePointer;
// A window handle is the OS's native window handle
//
// This would be an HWND, GLXDrawable, etc
typedef void *RENDERDOC_WindowHandle;
// A helper macro for Vulkan, where the device handle cannot be used directly.
//
// Passing the VkInstance to this macro will return the RENDERDOC_DevicePointer to use.
//
// Specifically, the value needed is the dispatch table pointer, which sits as the first
// pointer-sized object in the memory pointed to by the VkInstance. Thus we cast to a void** and
// indirect once.
#define RENDERDOC_DEVICEPOINTER_FROM_VKINSTANCE(inst) (*((void **)(inst)))
// This sets the RenderDoc in-app overlay in the API/window pair as 'active' and it will
// respond to keypresses. Neither parameter can be NULL
typedef void(RENDERDOC_CC *pRENDERDOC_SetActiveWindow)(RENDERDOC_DevicePointer device,
RENDERDOC_WindowHandle wndHandle);
// capture the next frame on whichever window and API is currently considered active
typedef void(RENDERDOC_CC *pRENDERDOC_TriggerCapture)();
// capture the next N frames on whichever window and API is currently considered active
typedef void(RENDERDOC_CC *pRENDERDOC_TriggerMultiFrameCapture)(uint32_t numFrames);
// When choosing either a device pointer or a window handle to capture, you can pass NULL.
// Passing NULL specifies a 'wildcard' match against anything. This allows you to specify
// any API rendering to a specific window, or a specific API instance rendering to any window,
// or in the simplest case of one window and one API, you can just pass NULL for both.
//
// In either case, if there are two or more possible matching (device,window) pairs it
// is undefined which one will be captured.
//
// Note: for headless rendering you can pass NULL for the window handle and either specify
// a device pointer or leave it NULL as above.
// Immediately starts capturing API calls on the specified device pointer and window handle.
//
// If there is no matching thing to capture (e.g. no supported API has been initialised),
// this will do nothing.
//
// The results are undefined (including crashes) if two captures are started overlapping,
// even on separate devices and/oror windows.
typedef void(RENDERDOC_CC *pRENDERDOC_StartFrameCapture)(RENDERDOC_DevicePointer device,
RENDERDOC_WindowHandle wndHandle);
// Returns whether or not a frame capture is currently ongoing anywhere.
//
// This will return 1 if a capture is ongoing, and 0 if there is no capture running
typedef uint32_t(RENDERDOC_CC *pRENDERDOC_IsFrameCapturing)();
// Ends capturing immediately.
//
// This will return 1 if the capture succeeded, and 0 if there was an error capturing.
typedef uint32_t(RENDERDOC_CC *pRENDERDOC_EndFrameCapture)(RENDERDOC_DevicePointer device,
RENDERDOC_WindowHandle wndHandle);
// Ends capturing immediately and discard any data stored without saving to disk.
//
// This will return 1 if the capture was discarded, and 0 if there was an error or no capture
// was in progress
typedef uint32_t(RENDERDOC_CC *pRENDERDOC_DiscardFrameCapture)(RENDERDOC_DevicePointer device,
RENDERDOC_WindowHandle wndHandle);
// Requests that the replay UI show itself (if hidden or not the current top window). This can be
// used in conjunction with IsTargetControlConnected and LaunchReplayUI to intelligently handle
// showing the UI after making a capture.
//
// This will return 1 if the request was successfully passed on, though it's not guaranteed that
// the UI will be on top in all cases depending on OS rules. It will return 0 if there is no current
// target control connection to make such a request, or if there was another error
typedef uint32_t(RENDERDOC_CC *pRENDERDOC_ShowReplayUI)();
//////////////////////////////////////////////////////////////////////////////////////////////////
// RenderDoc API versions
//
// RenderDoc uses semantic versioning (http://semver.org/).
//
// MAJOR version is incremented when incompatible API changes happen.
// MINOR version is incremented when functionality is added in a backwards-compatible manner.
// PATCH version is incremented when backwards-compatible bug fixes happen.
//
// Note that this means the API returned can be higher than the one you might have requested.
// e.g. if you are running against a newer RenderDoc that supports 1.0.1, it will be returned
// instead of 1.0.0. You can check this with the GetAPIVersion entry point
typedef enum RENDERDOC_Version {
eRENDERDOC_API_Version_1_0_0 = 10000, // RENDERDOC_API_1_0_0 = 1 00 00
eRENDERDOC_API_Version_1_0_1 = 10001, // RENDERDOC_API_1_0_1 = 1 00 01
eRENDERDOC_API_Version_1_0_2 = 10002, // RENDERDOC_API_1_0_2 = 1 00 02
eRENDERDOC_API_Version_1_1_0 = 10100, // RENDERDOC_API_1_1_0 = 1 01 00
eRENDERDOC_API_Version_1_1_1 = 10101, // RENDERDOC_API_1_1_1 = 1 01 01
eRENDERDOC_API_Version_1_1_2 = 10102, // RENDERDOC_API_1_1_2 = 1 01 02
eRENDERDOC_API_Version_1_2_0 = 10200, // RENDERDOC_API_1_2_0 = 1 02 00
eRENDERDOC_API_Version_1_3_0 = 10300, // RENDERDOC_API_1_3_0 = 1 03 00
eRENDERDOC_API_Version_1_4_0 = 10400, // RENDERDOC_API_1_4_0 = 1 04 00
eRENDERDOC_API_Version_1_4_1 = 10401, // RENDERDOC_API_1_4_1 = 1 04 01
eRENDERDOC_API_Version_1_4_2 = 10402, // RENDERDOC_API_1_4_2 = 1 04 02
eRENDERDOC_API_Version_1_5_0 = 10500, // RENDERDOC_API_1_5_0 = 1 05 00
} RENDERDOC_Version;
// API version changelog:
//
// 1.0.0 - initial release
// 1.0.1 - Bugfix: IsFrameCapturing() was returning false for captures that were triggered
// by keypress or TriggerCapture, instead of Start/EndFrameCapture.
// 1.0.2 - Refactor: Renamed eRENDERDOC_Option_DebugDeviceMode to eRENDERDOC_Option_APIValidation
// 1.1.0 - Add feature: TriggerMultiFrameCapture(). Backwards compatible with 1.0.x since the new
// function pointer is added to the end of the struct, the original layout is identical
// 1.1.1 - Refactor: Renamed remote access to target control (to better disambiguate from remote
// replay/remote server concept in replay UI)
// 1.1.2 - Refactor: Renamed "log file" in function names to just capture, to clarify that these
// are captures and not debug logging files. This is the first API version in the v1.0
// branch.
// 1.2.0 - Added feature: SetCaptureFileComments() to add comments to a capture file that will be
// displayed in the UI program on load.
// 1.3.0 - Added feature: New capture option eRENDERDOC_Option_AllowUnsupportedVendorExtensions
// which allows users to opt-in to allowing unsupported vendor extensions to function.
// Should be used at the user's own risk.
// Refactor: Renamed eRENDERDOC_Option_VerifyMapWrites to
// eRENDERDOC_Option_VerifyBufferAccess, which now also controls initialisation to
// 0xdddddddd of uninitialised buffer contents.
// 1.4.0 - Added feature: DiscardFrameCapture() to discard a frame capture in progress and stop
// capturing without saving anything to disk.
// 1.4.1 - Refactor: Renamed Shutdown to RemoveHooks to better clarify what is happening
// 1.4.2 - Refactor: Renamed 'draws' to 'actions' in callstack capture option.
// 1.5.0 - Added feature: ShowReplayUI() to request that the replay UI show itself if connected
typedef struct RENDERDOC_API_1_5_0
{
pRENDERDOC_GetAPIVersion GetAPIVersion;
pRENDERDOC_SetCaptureOptionU32 SetCaptureOptionU32;
pRENDERDOC_SetCaptureOptionF32 SetCaptureOptionF32;
pRENDERDOC_GetCaptureOptionU32 GetCaptureOptionU32;
pRENDERDOC_GetCaptureOptionF32 GetCaptureOptionF32;
pRENDERDOC_SetFocusToggleKeys SetFocusToggleKeys;
pRENDERDOC_SetCaptureKeys SetCaptureKeys;
pRENDERDOC_GetOverlayBits GetOverlayBits;
pRENDERDOC_MaskOverlayBits MaskOverlayBits;
// Shutdown was renamed to RemoveHooks in 1.4.1.
// These unions allow old code to continue compiling without changes
union
{
pRENDERDOC_Shutdown Shutdown;
pRENDERDOC_RemoveHooks RemoveHooks;
};
pRENDERDOC_UnloadCrashHandler UnloadCrashHandler;
// Get/SetLogFilePathTemplate was renamed to Get/SetCaptureFilePathTemplate in 1.1.2.
// These unions allow old code to continue compiling without changes
union
{
// deprecated name
pRENDERDOC_SetLogFilePathTemplate SetLogFilePathTemplate;
// current name
pRENDERDOC_SetCaptureFilePathTemplate SetCaptureFilePathTemplate;
};
union
{
// deprecated name
pRENDERDOC_GetLogFilePathTemplate GetLogFilePathTemplate;
// current name
pRENDERDOC_GetCaptureFilePathTemplate GetCaptureFilePathTemplate;
};
pRENDERDOC_GetNumCaptures GetNumCaptures;
pRENDERDOC_GetCapture GetCapture;
pRENDERDOC_TriggerCapture TriggerCapture;
// IsRemoteAccessConnected was renamed to IsTargetControlConnected in 1.1.1.
// This union allows old code to continue compiling without changes
union
{
// deprecated name
pRENDERDOC_IsRemoteAccessConnected IsRemoteAccessConnected;
// current name
pRENDERDOC_IsTargetControlConnected IsTargetControlConnected;
};
pRENDERDOC_LaunchReplayUI LaunchReplayUI;
pRENDERDOC_SetActiveWindow SetActiveWindow;
pRENDERDOC_StartFrameCapture StartFrameCapture;
pRENDERDOC_IsFrameCapturing IsFrameCapturing;
pRENDERDOC_EndFrameCapture EndFrameCapture;
// new function in 1.1.0
pRENDERDOC_TriggerMultiFrameCapture TriggerMultiFrameCapture;
// new function in 1.2.0
pRENDERDOC_SetCaptureFileComments SetCaptureFileComments;
// new function in 1.4.0
pRENDERDOC_DiscardFrameCapture DiscardFrameCapture;
// new function in 1.5.0
pRENDERDOC_ShowReplayUI ShowReplayUI;
} RENDERDOC_API_1_5_0;
typedef RENDERDOC_API_1_5_0 RENDERDOC_API_1_0_0;
typedef RENDERDOC_API_1_5_0 RENDERDOC_API_1_0_1;
typedef RENDERDOC_API_1_5_0 RENDERDOC_API_1_0_2;
typedef RENDERDOC_API_1_5_0 RENDERDOC_API_1_1_0;
typedef RENDERDOC_API_1_5_0 RENDERDOC_API_1_1_1;
typedef RENDERDOC_API_1_5_0 RENDERDOC_API_1_1_2;
typedef RENDERDOC_API_1_5_0 RENDERDOC_API_1_2_0;
typedef RENDERDOC_API_1_5_0 RENDERDOC_API_1_3_0;
typedef RENDERDOC_API_1_5_0 RENDERDOC_API_1_4_0;
typedef RENDERDOC_API_1_5_0 RENDERDOC_API_1_4_1;
typedef RENDERDOC_API_1_5_0 RENDERDOC_API_1_4_2;
//////////////////////////////////////////////////////////////////////////////////////////////////
// RenderDoc API entry point
//
// This entry point can be obtained via GetProcAddress/dlsym if RenderDoc is available.
//
// The name is the same as the typedef - "RENDERDOC_GetAPI"
//
// This function is not thread safe, and should not be called on multiple threads at once.
// Ideally, call this once as early as possible in your application's startup, before doing
// any API work, since some configuration functionality etc has to be done also before
// initialising any APIs.
//
// Parameters:
// version is a single value from the RENDERDOC_Version above.
//
// outAPIPointers will be filled out with a pointer to the corresponding struct of function
// pointers.
//
// Returns:
// 1 - if the outAPIPointers has been filled with a pointer to the API struct requested
// 0 - if the requested version is not supported or the arguments are invalid.
//
typedef int(RENDERDOC_CC *pRENDERDOC_GetAPI)(RENDERDOC_Version version, void **outAPIPointers);
#ifdef __cplusplus
} // extern "C"
#endif

@ -1 +0,0 @@
Subproject commit f8e0f679d2fad8b47f6300f25ab844fd49a1f7e5

@ -1 +0,0 @@
Subproject commit 0e22c80ff7b1bb4c4c5c7c3dd2e91b4a58ad519d

@ -1 +0,0 @@
Subproject commit d7ad93a88864bda94e282e95028f90b5784e4d20

@ -1 +0,0 @@
Subproject commit 67fad04348b91cf93bdfad7495d298f54825602c

@ -1 +1 @@
Subproject commit de5ced3d4b76dd24bc43628127e26a9c7eb098d3
Subproject commit f638311337d6b0eaf63e84c6e9afe1227539e0d3

@ -1 +1 @@
Subproject commit 80dada6a7b455cf18c39788d70aa7711323ea977
Subproject commit a4b8f74942a932ea191dc95cc4a210fea524508f

View file

@ -1,208 +0,0 @@
The majority of files in this project use the Apache 2.0 License.
There are a few exceptions and their license can be found in the source.
Any license deviations from Apache 2.0 are "more permissive" licenses.
Any file without a license in it's source defaults to the repository Apache 2.0 License.
===========================================================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

@ -1 +0,0 @@
Subproject commit 936bc4b57e7ffa5906a786735537c5493224e7d6

View file

@ -1,7 +0,0 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/)
#define VMA_IMPLEMENTATION
#define VMA_STATIC_VULKAN_FUNCTIONS 0
#define VMA_DYNAMIC_VULKAN_FUNCTIONS 0
#include <vk_mem_alloc.h>

View file

@ -4,33 +4,3 @@
# Retain all classes within Skyline for traces + JNI access + Serializable classes
-keep class emu.skyline.** { *; }
# Keep kotlin classes so that kotlin reflection works
-keep class kotlin.** {*;}
# https://github.com/Kotlin/kotlinx.serialization#android
# Keep `Companion` object fields of serializable classes.
# This avoids serializer lookup through `getDeclaredClasses` as done for named companion objects.
-if @kotlinx.serialization.Serializable class **
-keepclassmembers class <1> {
static <1>$Companion Companion;
}
# Keep `serializer()` on companion objects (both default and named) of serializable classes.
-if @kotlinx.serialization.Serializable class ** {
static **$* *;
}
-keepclassmembers class <2>$<3> {
kotlinx.serialization.KSerializer serializer(...);
}
# Keep `INSTANCE.serializer()` of serializable objects.
-if @kotlinx.serialization.Serializable class ** {
public static ** INSTANCE;
}
-keepclassmembers class <1> {
public static <1> INSTANCE;
kotlinx.serialization.KSerializer serializer(...);
}
# @Serializable and @Polymorphic are used at runtime for polymorphic serialization.
-keepattributes RuntimeVisibleAnnotations,AnnotationDefault

View file

@ -1,78 +1,83 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
xmlns:tools="http://schemas.android.com/tools"
package="emu.skyline">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-feature
android:name="android.hardware.vulkan.version"
android:required="true"
android:version="0x401000" />
android:glEsVersion="0x00030001"
android:required="true" />
<uses-permission android:name="android.permission.VIBRATE" />
<application
android:name=".SkylineApplication"
android:enableOnBackInvokedCallback="true"
android:allowBackup="true"
android:extractNativeLibs="true"
android:fullBackupContent="@xml/backup_descriptor"
android:hasFragileUserData="${shouldSaveUserData}"
android:icon="@drawable/logo_skyline"
android:isGame="true"
android:label="${appLabel}"
android:localeConfig="@xml/locales_config"
android:supportsRtl="true"
android:theme="@style/AppTheme"
tools:ignore="GoogleAppIndexingWarning,UnusedAttribute">
<profileable android:shell="true" />
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop">
android:name=".SkylineApplication"
android:allowBackup="true"
android:extractNativeLibs="true"
android:fullBackupContent="@xml/backup_descriptor"
android:icon="@drawable/logo_skyline"
android:isGame="true"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme"
tools:ignore="GoogleAppIndexingWarning,UnusedAttribute">
<activity android:name="emu.skyline.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".settings.SettingsActivity"
android:exported="true"
android:label="@string/settings"
android:launchMode="singleTop"
android:parentActivityName=".MainActivity">
android:name="emu.skyline.LogActivity"
android:label="@string/log"
android:parentActivityName="emu.skyline.MainActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="emu.skyline.MainActivity" />
</activity>
<activity
android:name=".input.ControllerActivity"
android:exported="true"
android:parentActivityName=".settings.SettingsActivity">
android:name="emu.skyline.SettingsActivity"
android:exported="true"
android:label="@string/settings"
android:parentActivityName="emu.skyline.MainActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="emu.skyline.MainActivity" />
</activity>
<activity
android:name=".preference.GpuDriverActivity"
android:exported="true"
android:parentActivityName=".settings.SettingsActivity">
<activity android:name="emu.skyline.preference.FolderActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="emu.skyline.SettingsActivity" />
</activity>
<activity
android:name=".input.onscreen.OnScreenEditActivity"
android:exported="true"
android:screenOrientation="sensorLandscape"
tools:ignore="LockedOrientationActivity"
android:parentActivityName=".input.ControllerActivity">
<activity android:name="emu.skyline.preference.FileActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="emu.skyline.SettingsActivity" />
</activity>
<activity
android:name=".EmulationActivity"
android:supportsPictureInPicture="true"
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|uiMode"
android:exported="true"
android:launchMode="singleTask"
android:process="${emulationProcess}"
android:parentActivityName=".MainActivity">
android:name="emu.skyline.input.ControllerActivity"
android:exported="true">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="emu.skyline.SettingsActivity" />
</activity>
<activity
android:name="emu.skyline.input.onscreen.OnScreenEditActivity"
android:exported="true"
android:screenOrientation="landscape"
tools:ignore="LockedOrientationActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="emu.skyline.input.ControllerActivity" />
</activity>
<activity
android:name="emu.skyline.EmulationActivity"
android:configChanges="orientation|screenSize"
android:launchMode="singleInstance"
android:screenOrientation="landscape"
tools:ignore="LockedOrientationActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="emu.skyline.MainActivity" />
<intent-filter>
<action android:name="android.intent.action.VIEW" />
@ -81,54 +86,30 @@
<category android:name="android.intent.category.DEFAULT" />
<data
android:mimeType="application/nro"
android:pathPattern=".*\\.nro"
android:scheme="content"
tools:ignore="AppLinkUrlError" />
android:mimeType="application/nro"
android:pathPattern=".*\\.nro"
android:scheme="content"
tools:ignore="AppLinkUrlError" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data
android:mimeType="text/plain"
android:pathPattern=".*\\.nro"
android:scheme="content"
tools:ignore="IntentFilterUniqueDataAttributes" />
android:mimeType="text/plain"
android:pathPattern=".*\\.nro"
android:scheme="content"
tools:ignore="AppLinkUrlError" />
<data
android:mimeType="application/octet-stream"
android:pathPattern=".*\\.nro"
android:scheme="content"
tools:ignore="IntentFilterUniqueDataAttributes" />
android:mimeType="application/octet-stream"
android:pathPattern=".*\\.nro"
android:scheme="content"
tools:ignore="AppLinkUrlError" />
<data
android:mimeType="application/nro"
android:scheme="content"
tools:ignore="IntentFilterUniqueDataAttributes" />
android:mimeType="application/nro"
android:scheme="content"
tools:ignore="AppLinkUrlError" />
</intent-filter>
</activity>
<provider
android:name=".provider.DocumentsProvider"
android:authorities="${applicationId}.provider"
android:exported="true"
android:grantUriPermissions="true"
android:permission="android.permission.MANAGE_DOCUMENTS">
<intent-filter>
<action android:name="android.content.action.DOCUMENTS_PROVIDER" />
</intent-filter>
</provider>
<service
android:name="androidx.appcompat.app.AppLocalesMetadataHolderService"
android:enabled="false"
android:exported="false">
<meta-data
android:name="autoStoreLocales"
android:value="true" />
</service>
<meta-data
android:name="com.android.graphics.injectLayers.enable"
android:value="true" />
</application>
</manifest>

View file

@ -1,3 +0,0 @@
#### Skyline FOSS Shared Fonts:
* [FontStandard](FontStandard.ttf), [FontKorean](FontKorean.ttf), [FontChineseSimplified](FontChineseSimplified.ttf), [FontChineseTraditional](FontChineseTraditional.ttf) and [FontExtendedChineseSimplified](FontExtendedChineseSimplified.ttf) are using [Noto Sans CJK](https://github.com/googlefonts/noto-cjk), which is licensed under [Open Font License](https://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL)
* [FontNintendoExtended](FontNintendoExtended.ttf) is using [Roboto](https://fonts.google.com/specimen/Roboto), which is licensed under [Apache 2.0](https://www.apache.org/licenses/LICENSE-2.0)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

View file

@ -1,50 +0,0 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <jni.h>
#include <dlfcn.h>
#include <fcntl.h>
#include <vulkan/vulkan_raii.hpp>
#include <adrenotools/driver.h>
#include "skyline/common/signal.h"
#include "skyline/common/utils.h"
extern "C" JNIEXPORT jobjectArray JNICALL Java_emu_skyline_utils_GpuDriverHelper_00024Companion_getSystemDriverInfo(JNIEnv *env, jobject) {
auto libvulkanHandle{dlopen("libvulkan.so", RTLD_NOW)};
vk::raii::Context vkContext{reinterpret_cast<PFN_vkGetInstanceProcAddr>(dlsym(libvulkanHandle, "vkGetInstanceProcAddr"))};
vk::raii::Instance vkInstance{vkContext, vk::InstanceCreateInfo{}};
vk::raii::PhysicalDevice physicalDevice{std::move(vk::raii::PhysicalDevices(vkInstance).front())}; // Get the first device as we aren't expecting multiple GPUs
auto deviceProperties2{physicalDevice.getProperties2<vk::PhysicalDeviceProperties2, vk::PhysicalDeviceDriverProperties>()};
auto properties{deviceProperties2.get<vk::PhysicalDeviceProperties2>().properties};
auto driverId{vk::to_string(deviceProperties2.get<vk::PhysicalDeviceDriverProperties>().driverID)};
auto driverVersion{skyline::util::Format("{}.{}.{}", VK_API_VERSION_MAJOR(properties.driverVersion), VK_API_VERSION_MINOR(properties.driverVersion), VK_API_VERSION_PATCH(properties.driverVersion))};
auto array = env->NewObjectArray(2, env->FindClass("java/lang/String"), nullptr);
env->SetObjectArrayElement(array, 0, env->NewStringUTF(driverId.c_str()));
env->SetObjectArrayElement(array, 1, env->NewStringUTF(driverVersion.c_str()));
return array;
}
static bool CheckKgslPresent() {
constexpr auto KgslPath{"/dev/kgsl-3d0"};
return access(KgslPath, F_OK) == 0;
}
extern "C" JNIEXPORT jboolean JNICALL Java_emu_skyline_utils_GpuDriverHelper_00024Companion_supportsCustomDriverLoading(JNIEnv *env, jobject instance) {
// If the KGSL device exists custom drivers can be loaded using adrenotools
return CheckKgslPresent();
}
extern "C" JNIEXPORT jboolean JNICALL Java_emu_skyline_utils_GpuDriverHelper_00024Companion_supportsForceMaxGpuClocks(JNIEnv *env, jobject instance) {
// If the KGSL device exists adrenotools can be used to set GPU turbo mode
return CheckKgslPresent();
}
extern "C" JNIEXPORT void JNICALL Java_emu_skyline_utils_GpuDriverHelper_00024Companion_forceMaxGpuClocks(JNIEnv *env, jobject instance, jboolean enable) {
adrenotools_set_turbo(enable);
}

View file

@ -3,35 +3,31 @@
#include <csignal>
#include <pthread.h>
#include <unistd.h>
#include <android/log.h>
#include <android/asset_manager_jni.h>
#include <sys/system_properties.h>
#include "skyline/common.h"
#include "skyline/common/language.h"
#include "skyline/common/signal.h"
#include "skyline/common/android_settings.h"
#include "skyline/common/settings.h"
#include "skyline/common/trace.h"
#include "skyline/loader/loader.h"
#include "skyline/vfs/android_asset_filesystem.h"
#include "skyline/os.h"
#include "skyline/jvm.h"
#include "skyline/gpu.h"
#include "skyline/audio.h"
#include "skyline/input.h"
#include "skyline/kernel/types/KProcess.h"
jint Fps; //!< An approximation of the amount of frames being submitted every second
jfloat AverageFrametimeMs; //!< The average time it takes for a frame to be rendered and presented in milliseconds
jfloat AverageFrametimeDeviationMs; //!< The average deviation of the average frametimes in milliseconds
skyline::u16 Fps;
skyline::u32 FrameTime;
std::weak_ptr<skyline::kernel::OS> OsWeak;
std::weak_ptr<skyline::gpu::GPU> GpuWeak;
std::weak_ptr<skyline::audio::Audio> AudioWeak;
std::weak_ptr<skyline::input::Input> InputWeak;
std::weak_ptr<skyline::Settings> SettingsWeak;
// https://cs.android.com/android/platform/superproject/+/master:bionic/libc/tzcode/bionic.cpp;l=43;drc=master;bpv=1;bpt=1
static std::string GetTimeZoneName() {
const char *nameEnv = getenv("TZ");
const char* nameEnv = getenv("TZ");
if (nameEnv)
return std::string(nameEnv);
@ -54,101 +50,68 @@ static std::string GetTimeZoneName() {
return "GMT";
}
extern "C" JNIEXPORT void Java_emu_skyline_SkylineApplication_initializeLog(
JNIEnv *env,
jobject,
jstring publicAppFilesPathJstring,
jint logLevel
) {
skyline::JniString publicAppFilesPath(env, publicAppFilesPathJstring);
skyline::Logger::configLevel = static_cast<skyline::Logger::LogLevel>(logLevel);
skyline::Logger::LoaderContext.Initialize(publicAppFilesPath + "logs/loader.sklog");
}
extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_executeApplication(
JNIEnv *env,
jobject instance,
jstring romUriJstring,
jint romType,
jint romFd,
jobject settingsInstance,
jstring publicAppFilesPathJstring,
jstring privateAppFilesPathJstring,
jstring nativeLibraryPathJstring,
jobject assetManager
) {
extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_executeApplication(JNIEnv *env, jobject instance, jstring romUriJstring, jint romType, jint romFd, jint preferenceFd, jstring appFilesPathJstring, jobject assetManager) {
skyline::signal::ScopedStackBlocker stackBlocker; // We do not want anything to unwind past JNI code as there are invalid stack frames which can lead to a segmentation fault
Fps = 0;
AverageFrametimeMs = AverageFrametimeDeviationMs = 0.0f;
Fps = FrameTime = 0;
pthread_setname_np(pthread_self(), "EmuMain");
auto jvmManager{std::make_shared<skyline::JvmManager>(env, instance)};
auto settings{std::make_shared<skyline::Settings>(preferenceFd)};
close(preferenceFd);
std::shared_ptr<skyline::Settings> settings{std::make_shared<skyline::AndroidSettings>(env, settingsInstance)};
skyline::JniString publicAppFilesPath(env, publicAppFilesPathJstring);
skyline::Logger::EmulationContext.Initialize(publicAppFilesPath + "logs/emulation.sklog");
auto appFilesPath{env->GetStringUTFChars(appFilesPathJstring, nullptr)};
auto logger{std::make_shared<skyline::Logger>(std::string(appFilesPath) + "skyline.log", settings->logLevel)};
auto start{std::chrono::steady_clock::now()};
// Initialize tracing
perfetto::TracingInitArgs args;
args.backends |= perfetto::kSystemBackend;
args.shmem_size_hint_kb = 0x200000;
perfetto::Tracing::Initialize(args);
perfetto::TrackEvent::Register();
try {
skyline::JniString nativeLibraryPath(env, nativeLibraryPathJstring);
skyline::JniString privateAppFilesPath{env, privateAppFilesPathJstring};
auto os{std::make_shared<skyline::kernel::OS>(
jvmManager,
settings,
publicAppFilesPath,
privateAppFilesPath,
nativeLibraryPath,
GetTimeZoneName(),
std::make_shared<skyline::vfs::AndroidAssetFileSystem>(AAssetManager_fromJava(env, assetManager))
)};
auto os{std::make_shared<skyline::kernel::OS>(jvmManager, logger, settings, std::string(appFilesPath), GetTimeZoneName(), std::make_shared<skyline::vfs::AndroidAssetFileSystem>(AAssetManager_fromJava(env, assetManager)))};
OsWeak = os;
GpuWeak = os->state.gpu;
AudioWeak = os->state.audio;
InputWeak = os->state.input;
SettingsWeak = settings;
jvmManager->InitializeControllers();
env->ReleaseStringUTFChars(appFilesPathJstring, appFilesPath);
skyline::Logger::DebugNoPrefix("Launching ROM {}", skyline::JniString(env, romUriJstring));
auto romUri{env->GetStringUTFChars(romUriJstring, nullptr)};
logger->InfoNoPrefix("Launching ROM {}", romUri);
env->ReleaseStringUTFChars(romUriJstring, romUri);
os->Execute(romFd, static_cast<skyline::loader::RomFormat>(romType));
} catch (std::exception &e) {
skyline::Logger::ErrorNoPrefix("An uncaught exception has occurred: {}", e.what());
logger->Error("An exception has occurred: {}", e.what());
} catch (const skyline::signal::SignalException &e) {
skyline::Logger::ErrorNoPrefix("An uncaught exception has occurred: {}", e.what());
logger->Error("An exception has occurred: {}", e.what());
} catch (...) {
skyline::Logger::ErrorNoPrefix("An unknown uncaught exception has occurred");
logger->Error("An unknown exception has occurred");
}
perfetto::TrackEvent::Flush();
InputWeak.reset();
auto end{std::chrono::steady_clock::now()};
skyline::Logger::Write(skyline::Logger::LogLevel::Info, fmt::format("Emulation has ended in {}ms", std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count()));
logger->InfoNoPrefix("Emulation has ended");
auto end{std::chrono::steady_clock::now()};
logger->InfoNoPrefix("Done in: {} ms", (std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count()));
skyline::Logger::EmulationContext.Finalize();
close(romFd);
}
extern "C" JNIEXPORT jboolean Java_emu_skyline_EmulationActivity_stopEmulation(JNIEnv *, jobject, jboolean join) {
extern "C" JNIEXPORT jboolean Java_emu_skyline_EmulationActivity_stopEmulation(JNIEnv *, jobject) {
auto os{OsWeak.lock()};
if (!os)
return false;
auto process{os->state.process};
if (!process)
return false;
process->Kill(join, false, true);
process->Kill(true, false, true);
return true;
}
@ -160,15 +123,6 @@ extern "C" JNIEXPORT jboolean Java_emu_skyline_EmulationActivity_setSurface(JNIE
return true;
}
extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_changeAudioStatus(JNIEnv *, jobject, jboolean play) {
auto audio{AudioWeak.lock()};
if (audio)
if (play)
audio->Resume();
else
audio->Pause();
}
extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_updatePerformanceStatistics(JNIEnv *env, jobject thiz) {
static jclass clazz{};
if (!clazz)
@ -179,58 +133,41 @@ extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_updatePerformanceSt
fpsField = env->GetFieldID(clazz, "fps", "I");
env->SetIntField(thiz, fpsField, Fps);
static jfieldID averageFrametimeField{};
if (!averageFrametimeField)
averageFrametimeField = env->GetFieldID(clazz, "averageFrametime", "F");
env->SetFloatField(thiz, averageFrametimeField, AverageFrametimeMs);
static jfieldID averageFrametimeDeviationField{};
if (!averageFrametimeDeviationField)
averageFrametimeDeviationField = env->GetFieldID(clazz, "averageFrametimeDeviation", "F");
env->SetFloatField(thiz, averageFrametimeDeviationField, AverageFrametimeDeviationMs);
static jfieldID frametimeField{};
if (!frametimeField)
frametimeField = env->GetFieldID(clazz, "frametime", "F");
env->SetFloatField(thiz, frametimeField, static_cast<float>(FrameTime) / 100);
}
extern "C" JNIEXPORT void JNICALL Java_emu_skyline_input_InputHandler_00024Companion_setController(JNIEnv *, jobject, jint index, jint type, jint partnerIndex) {
extern "C" JNIEXPORT void JNICALL Java_emu_skyline_EmulationActivity_setController(JNIEnv *, jobject, jint index, jint type, jint partnerIndex) {
auto input{InputWeak.lock()};
std::lock_guard guard(input->npad.mutex);
input->npad.controllers[static_cast<size_t>(index)] = skyline::input::GuestController{static_cast<skyline::input::NpadControllerType>(type), static_cast<skyline::i8>(partnerIndex)};
input->npad.controllers[index] = skyline::input::GuestController{static_cast<skyline::input::NpadControllerType>(type), static_cast<skyline::i8>(partnerIndex)};
}
extern "C" JNIEXPORT void JNICALL Java_emu_skyline_input_InputHandler_00024Companion_updateControllers(JNIEnv *, jobject) {
extern "C" JNIEXPORT void JNICALL Java_emu_skyline_EmulationActivity_updateControllers(JNIEnv *, jobject) {
InputWeak.lock()->npad.Update();
}
extern "C" JNIEXPORT void JNICALL Java_emu_skyline_input_InputHandler_00024Companion_setButtonState(JNIEnv *, jobject, jint index, jlong mask, jboolean pressed) {
extern "C" JNIEXPORT void JNICALL Java_emu_skyline_EmulationActivity_setButtonState(JNIEnv *, jobject, jint index, jlong mask, jboolean pressed) {
auto input{InputWeak.lock()};
if (!input)
return; // We don't mind if we miss button updates while input hasn't been initialized
auto device{input->npad.controllers[static_cast<size_t>(index)].device};
auto device{input->npad.controllers[index].device};
if (device)
device->SetButtonState(skyline::input::NpadButton{.raw = static_cast<skyline::u64>(mask)}, pressed);
}
extern "C" JNIEXPORT void JNICALL Java_emu_skyline_input_InputHandler_00024Companion_setAxisValue(JNIEnv *, jobject, jint index, jint axis, jint value) {
extern "C" JNIEXPORT void JNICALL Java_emu_skyline_EmulationActivity_setAxisValue(JNIEnv *, jobject, jint index, jint axis, jint value) {
auto input{InputWeak.lock()};
if (!input)
return; // We don't mind if we miss axis updates while input hasn't been initialized
auto device{input->npad.controllers[static_cast<size_t>(index)].device};
auto device{input->npad.controllers[index].device};
if (device)
device->SetAxisValue(static_cast<skyline::input::NpadAxisId>(axis), value);
}
extern "C" JNIEXPORT void JNICALL Java_emu_skyline_input_InputHandler_00024Companion_setMotionState(JNIEnv *env, jobject, jint index, jint motionId, jobject value) {
auto input{InputWeak.lock()};
if (!input)
return; // We don't mind if we miss motion updates while input hasn't been initialized
const auto motionValue = reinterpret_cast<skyline::input::MotionSensorState*>(env->GetDirectBufferAddress(value));
auto device{input->npad.controllers[static_cast<size_t>(index)].device};
if (device)
device->SetMotionValue(static_cast<skyline::input::MotionId>(motionId), motionValue);
}
extern "C" JNIEXPORT void JNICALL Java_emu_skyline_input_InputHandler_00024Companion_setTouchState(JNIEnv *env, jobject, jintArray pointsJni) {
extern "C" JNIEXPORT void JNICALL Java_emu_skyline_EmulationActivity_setTouchState(JNIEnv *env, jobject, jintArray pointsJni) {
using Point = skyline::input::TouchScreenPoint;
auto input{InputWeak.lock()};
@ -238,19 +175,7 @@ extern "C" JNIEXPORT void JNICALL Java_emu_skyline_input_InputHandler_00024Compa
return; // We don't mind if we miss touch updates while input hasn't been initialized
jboolean isCopy{false};
skyline::span<Point> points(reinterpret_cast<Point *>(env->GetIntArrayElements(pointsJni, &isCopy)),
static_cast<size_t>(env->GetArrayLength(pointsJni)) / (sizeof(Point) / sizeof(jint)));
skyline::span<Point> points(reinterpret_cast<Point *>(env->GetIntArrayElements(pointsJni, &isCopy)), env->GetArrayLength(pointsJni) / (sizeof(Point) / sizeof(jint)));
input->touch.SetState(points);
env->ReleaseIntArrayElements(pointsJni, reinterpret_cast<jint *>(points.data()), JNI_ABORT);
}
extern "C" JNIEXPORT void JNICALL Java_emu_skyline_settings_NativeSettings_updateNative(JNIEnv *env, jobject) {
auto settings{SettingsWeak.lock()};
if (!settings)
return; // We don't mind if we miss settings updates while settings haven't been initialized
settings->Update();
}
extern "C" JNIEXPORT void JNICALL Java_emu_skyline_settings_NativeSettings_00024Companion_setLogLevel(JNIEnv *, jobject, jint logLevel) {
skyline::Logger::configLevel = static_cast<skyline::Logger::LogLevel>(logLevel);
}

View file

@ -1,7 +1,6 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include "skyline/common/logger.h"
#include "skyline/crypto/key_store.h"
#include "skyline/vfs/nca.h"
#include "skyline/vfs/os_backing.h"
@ -12,14 +11,13 @@
#include "skyline/loader/nsp.h"
#include "skyline/jvm.h"
extern "C" JNIEXPORT jint JNICALL Java_emu_skyline_loader_RomFile_populate(JNIEnv *env, jobject thiz, jint jformat, jint fd, jstring appFilesPathJstring, jint systemLanguage) {
skyline::signal::ScopedStackBlocker stackBlocker;
extern "C" JNIEXPORT jint JNICALL Java_emu_skyline_loader_RomFile_populate(JNIEnv *env, jobject thiz, jint jformat, jint fd, jstring appFilesPathJstring) {
skyline::loader::RomFormat format{static_cast<skyline::loader::RomFormat>(jformat)};
skyline::Logger::SetContext(&skyline::Logger::LoaderContext);
auto appFilesPath{env->GetStringUTFChars(appFilesPathJstring, nullptr)};
auto keyStore{std::make_shared<skyline::crypto::KeyStore>(appFilesPath)};
env->ReleaseStringUTFChars(appFilesPathJstring, appFilesPath);
auto keyStore{std::make_shared<skyline::crypto::KeyStore>(skyline::JniString(env, appFilesPathJstring))};
std::unique_ptr<skyline::loader::Loader> loader;
try {
auto backing{std::make_shared<skyline::vfs::OsBacking>(fd)};
@ -51,24 +49,16 @@ extern "C" JNIEXPORT jint JNICALL Java_emu_skyline_loader_RomFile_populate(JNIEn
jclass clazz{env->GetObjectClass(thiz)};
jfieldID applicationNameField{env->GetFieldID(clazz, "applicationName", "Ljava/lang/String;")};
jfieldID applicationTitleIdField{env->GetFieldID(clazz, "applicationTitleId", "Ljava/lang/String;")};
jfieldID applicationAuthorField{env->GetFieldID(clazz, "applicationAuthor", "Ljava/lang/String;")};
jfieldID rawIconField{env->GetFieldID(clazz, "rawIcon", "[B")};
jfieldID applicationVersionField{env->GetFieldID(clazz, "applicationVersion", "Ljava/lang/String;")};
if (loader->nacp) {
auto language{skyline::language::GetApplicationLanguage(static_cast<skyline::language::SystemLanguage>(systemLanguage))};
if (((1 << static_cast<skyline::u32>(language)) & loader->nacp->supportedTitleLanguages) == 0)
language = loader->nacp->GetFirstSupportedTitleLanguage();
env->SetObjectField(thiz, applicationNameField, env->NewStringUTF(loader->nacp->applicationName.c_str()));
env->SetObjectField(thiz, applicationAuthorField, env->NewStringUTF(loader->nacp->applicationPublisher.c_str()));
env->SetObjectField(thiz, applicationNameField, env->NewStringUTF(loader->nacp->GetApplicationName(language).c_str()));
env->SetObjectField(thiz, applicationVersionField, env->NewStringUTF(loader->nacp->GetApplicationVersion().c_str()));
env->SetObjectField(thiz, applicationTitleIdField, env->NewStringUTF(loader->nacp->GetSaveDataOwnerId().c_str()));
env->SetObjectField(thiz, applicationAuthorField, env->NewStringUTF(loader->nacp->GetApplicationPublisher(language).c_str()));
auto icon{loader->GetIcon(language)};
jbyteArray iconByteArray{env->NewByteArray(static_cast<jsize>(icon.size()))};
env->SetByteArrayRegion(iconByteArray, 0, static_cast<jsize>(icon.size()), reinterpret_cast<const jbyte *>(icon.data()));
auto icon{loader->GetIcon()};
jbyteArray iconByteArray{env->NewByteArray(icon.size())};
env->SetByteArrayRegion(iconByteArray, 0, icon.size(), reinterpret_cast<const jbyte *>(icon.data()));
env->SetObjectField(thiz, rawIconField, iconByteArray);
}

View file

@ -1,34 +0,0 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include "applet_creator.h"
#include "controller_applet.h"
#include "error_applet.h"
#include "player_select_applet.h"
#include "web_applet.h"
#include "swkbd/software_keyboard_applet.h"
namespace skyline::applet {
std::shared_ptr<service::am::IApplet> CreateApplet(
const DeviceState &state, service::ServiceManager &manager,
applet::AppletId appletId, std::shared_ptr<kernel::type::KEvent> onAppletStateChanged,
std::shared_ptr<kernel::type::KEvent> onNormalDataPushFromApplet,
std::shared_ptr<kernel::type::KEvent> onInteractiveDataPushFromApplet,
service::applet::LibraryAppletMode appletMode) {
switch (appletId) {
case AppletId::LibraryAppletController:
return std::make_shared<ControllerApplet>(state, manager, std::move(onAppletStateChanged), std::move(onNormalDataPushFromApplet), std::move(onInteractiveDataPushFromApplet), appletMode);
case AppletId::LibraryAppletPlayerSelect:
return std::make_shared<PlayerSelectApplet>(state, manager, std::move(onAppletStateChanged), std::move(onNormalDataPushFromApplet), std::move(onInteractiveDataPushFromApplet), appletMode);
case AppletId::LibraryAppletSwkbd:
return std::make_shared<swkbd::SoftwareKeyboardApplet>(state, manager, std::move(onAppletStateChanged), std::move(onNormalDataPushFromApplet), std::move(onInteractiveDataPushFromApplet), appletMode);
case AppletId::LibraryAppletError:
return std::make_shared<ErrorApplet>(state, manager, std::move(onAppletStateChanged), std::move(onNormalDataPushFromApplet), std::move(onInteractiveDataPushFromApplet), appletMode);
case AppletId::LibraryAppletOfflineWeb:
case AppletId::LibraryAppletShop:
return std::make_shared<WebApplet>(state, manager, std::move(onAppletStateChanged), std::move(onNormalDataPushFromApplet), std::move(onInteractiveDataPushFromApplet), appletMode);
default:
throw exception{"Unimplemented Applet: 0x{:X} ({})", static_cast<u32>(appletId), ToString(appletId)};
}
}
}

View file

@ -1,81 +0,0 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
#pragma once
#include <common/macros.h>
#include <services/am/applet/IApplet.h>
#include <services/applet/common_arguments.h>
namespace skyline::applet {
#define APPLETS \
APPLET_ENTRY(None, 0x000) \
APPLET_ENTRY(Application, 0x001) \
APPLET_ENTRY(OverlayApplet, 0x002) \
APPLET_ENTRY(SystemAppletMenu, 0x003) \
APPLET_ENTRY(SystemApplication, 0x004) \
APPLET_ENTRY(LibraryAppletAuth, 0x00A) \
APPLET_ENTRY(LibraryAppletCabinet, 0x00B) \
APPLET_ENTRY(LibraryAppletController, 0x00C) \
APPLET_ENTRY(LibraryAppletDataErase, 0x00D) \
APPLET_ENTRY(LibraryAppletError, 0x00E) \
APPLET_ENTRY(LibraryAppletNetConnect, 0x00F) \
APPLET_ENTRY(LibraryAppletPlayerSelect, 0x010) \
APPLET_ENTRY(LibraryAppletSwkbd, 0x011) \
APPLET_ENTRY(LibraryAppletMiiEdit, 0x012) \
APPLET_ENTRY(LibraryAppletWeb, 0x013) \
APPLET_ENTRY(LibraryAppletShop, 0x014) \
APPLET_ENTRY(LibraryAppletPhotoViewer, 0x015) \
APPLET_ENTRY(LibraryAppletSet, 0x016) \
APPLET_ENTRY(LibraryAppletOfflineWeb, 0x017) \
APPLET_ENTRY(LibraryAppletLoginShare, 0x018) \
APPLET_ENTRY(LibraryAppletWifiWebAuth, 0x019) \
APPLET_ENTRY(LibraryAppletMyPage, 0x01A) \
APPLET_ENTRY(LibraryAppletGift, 0x01B) \
APPLET_ENTRY(LibraryAppletUserMigration, 0x01C) \
APPLET_ENTRY(LibraryAppletPreomiaSys, 0x01D) \
APPLET_ENTRY(LibraryAppletStory, 0x01E) \
APPLET_ENTRY(LibraryAppletPreomiaUsr, 0x01F) \
APPLET_ENTRY(LibraryAppletPreomiaUsrDummy, 0x020) \
APPLET_ENTRY(LibraryAppletSample, 0x021) \
APPLET_ENTRY(DevlopmentTool, 0x3E8) \
APPLET_ENTRY(CombinationLA, 0x3F1) \
APPLET_ENTRY(AeSystemApplet, 0x3F2) \
APPLET_ENTRY(AeOverlayApplet, 0x3F3) \
APPLET_ENTRY(AeStarter, 0x3F4) \
APPLET_ENTRY(AeLibraryAppletAlone, 0x3F5) \
APPLET_ENTRY(AeLibraryApplet1, 0x3F6) \
APPLET_ENTRY(AeLibraryApplet2, 0x3F7) \
APPLET_ENTRY(AeLibraryApplet3, 0x3F8) \
APPLET_ENTRY(AeLibraryApplet4, 0x3F9) \
APPLET_ENTRY(AppletISA, 0x3FA) \
APPLET_ENTRY(AppletIOA, 0x3FB) \
APPLET_ENTRY(AppletISTA, 0x3FC) \
APPLET_ENTRY(AppletILA1, 0x3FD) \
APPLET_ENTRY(AppletILA2, 0x3FE)
/**
* @url https://switchbrew.org/wiki/Applet_Manager_services#AppletId
*/
enum class AppletId : u32 {
#define APPLET_ENTRY(name, id) name = id,
APPLETS
#undef APPLET_ENTRY
};
#define APPLET_ENTRY(name, id) ENUM_CASE(name);
ENUM_STRING(AppletId, APPLETS)
#undef APPLET_ENTRY
/**
* @brief Creates an Applet of the appropiate class depending on the AppletId
*/
std::shared_ptr<service::am::IApplet> CreateApplet(
const DeviceState &state, service::ServiceManager &manager,
applet::AppletId appletId, std::shared_ptr<kernel::type::KEvent> onAppletStateChanged,
std::shared_ptr<kernel::type::KEvent> onNormalDataPushFromApplet,
std::shared_ptr<kernel::type::KEvent> onInteractiveDataPushFromApplet,
service::applet::LibraryAppletMode appletMode);
}

View file

@ -1,114 +0,0 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
// Copyright © 2020 yuzu Emulator Project (https://github.com/yuzu-emu/)
#include <input.h>
#include <input/npad.h>
#include <services/applet/common_arguments.h>
#include <services/am/storage/ObjIStorage.h>
#include "controller_applet.h"
namespace skyline::applet {
ControllerApplet::ControllerApplet(const DeviceState &state,
service::ServiceManager &manager,
std::shared_ptr<kernel::type::KEvent> onAppletStateChanged,
std::shared_ptr<kernel::type::KEvent> onNormalDataPushFromApplet,
std::shared_ptr<kernel::type::KEvent> onInteractiveDataPushFromApplet,
service::applet::LibraryAppletMode appletMode)
: IApplet{state, manager, std::move(onAppletStateChanged), std::move(onNormalDataPushFromApplet), std::move(onInteractiveDataPushFromApplet), appletMode} {}
void ControllerApplet::HandleShowControllerSupport(input::NpadStyleSet styleSet, ControllerAppletVersion version, span<u8> arg) {
// Generic macro due to both versions of arguments sharing the same fields but having different layouts
auto handle{[&](auto controllerSupportModeArg) {
Logger::InfoNoPrefix("Controller Support: "
"Player Count: {} - {}, "
"Take Over Connection: {}, Left Justify: {}, Dual Joy-Con Allowed: {}, Single Mode Enabled: {}, "
"Identification Color Enabled: {}, Explain Text Enabled: {}",
controllerSupportModeArg.playerCountMin, controllerSupportModeArg.playerCountMax,
controllerSupportModeArg.enableTakeOverConnection, controllerSupportModeArg.enableLeftJustify, controllerSupportModeArg.enablePermitJoyDual, controllerSupportModeArg.enableSingleMode,
controllerSupportModeArg.enableIdentificationColor, controllerSupportModeArg.enableExplainText);
// Here is where we would trigger the applet UI
auto &npad{state.input->npad};
std::scoped_lock lock{npad.mutex};
PushNormalDataAndSignal(std::make_shared<service::am::ObjIStorage<ControllerSupportResultInfo>>(state, manager, ControllerSupportResultInfo{
.playerCount = static_cast<i8>(controllerSupportModeArg.enableSingleMode ? 1 : npad.GetConnectedControllerCount()),
.selectedId = [&npad]() {
if (npad.controllers[0].device) {
return npad.controllers[0].device->id;
} else {
Logger::Warn("Controller requested but none connected!");
return input::NpadId::Player1; // Fallback to player 1
}
}(),
.result = {}
}));
}};
switch (version) {
case ControllerAppletVersion::Version3:
case ControllerAppletVersion::Version4:
case ControllerAppletVersion::Version5:
handle(arg.as<ControllerSupportArgOld>());
break;
case ControllerAppletVersion::Version7:
case ControllerAppletVersion::Version8:
handle(arg.as<ControllerSupportArgNew>());
break;
default:
Logger::Warn("Unsupported controller applet version {}", static_cast<u32>(version));
break;
}
}
Result ControllerApplet::Start() {
auto commonArg{PopNormalInput<service::applet::CommonArguments>()};
ControllerAppletVersion appletVersion{commonArg.apiVersion};
auto argPrivate{PopNormalInput<ControllerSupportArgPrivate>()};
// Some games such as Cave Story+ set invalid values for the ControllerSupportMode so use argSize to derive it if necessary (from yuzu)
if (argPrivate.mode >= ControllerSupportMode::MaxControllerSupportMode) {
switch (argPrivate.argSize) {
case sizeof(ControllerSupportArgOld):
case sizeof(ControllerSupportArgNew):
argPrivate.mode = ControllerSupportMode::ShowControllerSupport;
break;
default:
// TODO: when we support other modes make sure to add them here too
break;
}
}
std::scoped_lock lock{normalInputDataMutex};
switch (argPrivate.mode) {
case ControllerSupportMode::ShowControllerSupport:
HandleShowControllerSupport(argPrivate.styleSet, appletVersion, normalInputData.front()->GetSpan());
normalInputData.pop();
break;
default:
Logger::Warn("Controller applet mode {} is unimplemented", static_cast<u32>(argPrivate.mode));
normalInputData.pop();
// Return empty result
PushNormalDataAndSignal(std::make_shared<service::am::ObjIStorage<Result>>(state, manager, Result{}));
break;
}
// Notify the guest that we've finished running
onAppletStateChanged->Signal();
return {};
}
Result ControllerApplet::GetResult() {
return {};
}
void ControllerApplet::PushNormalDataToApplet(std::shared_ptr<service::am::IStorage> data) {
PushNormalInput(data);
}
void ControllerApplet::PushInteractiveDataToApplet(std::shared_ptr<service::am::IStorage> data) {}
}

View file

@ -1,118 +0,0 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
#pragma once
#include <services/am/applet/IApplet.h>
#include <services/applet/common_arguments.h>
#include <input/npad_device.h>
namespace skyline::applet {
/**
* @brief The Controller applet is responsible for notifiying the user of a games controller requirements and for allowing user management og controllers
*/
class ControllerApplet : public service::am::IApplet, service::am::EnableNormalQueue {
private:
/**
* @brief The version of the controller applet interface that an application supports
*/
enum class ControllerAppletVersion : u32 {
Version3 = 0x3, // 1.0.0 - 2.3.0
Version4 = 0x4, // 3.0.0 - 5.1.0
Version5 = 0x5, // 6.0.0 - 7.0.1
// No version 6
Version7 = 0x7, // 8.0.0 - 10.2.0
Version8 = 0x8, // 11.0.0+
};
/**
* @brief The requested mode of the controller applet, determines the specific UI that should be shown to the user
*/
enum class ControllerSupportMode : u8 {
ShowControllerSupport = 0,
ShowControllerStrapGuide = 1,
ShowControllerFirmwareUpdate = 2,
ShowControllerKeyRemappingForSystem = 3,
MaxControllerSupportMode
};
/**
* @brief The caller that is requesting the controller applet
*/
enum class ControllerSupportCaller : u8 {
Application = 1,
System = 2
};
/**
* @brief Common set of arguments supplied for all controller applet invocations
*/
struct ControllerSupportArgPrivate {
u32 argPrivateSize;
u32 argSize;
bool flag0;
bool flag1;
ControllerSupportMode mode;
ControllerSupportCaller caller;
input::NpadStyleSet styleSet;
u32 joyHoldType;
};
static_assert(sizeof(ControllerSupportArgPrivate) == 0x14);
/**
* @brief Set of arguments required for the ShowControllerSupport mode, templated since the number of controller supported varies based on applet version
*/
template<u8 NumControllersSupported>
struct ControllerSupportArg {
using IdentificationColor = std::array<u8, 4>; // RGBA colour code
using ExplainText = std::array<char, 128 + 1>; // 128 chars + null terminator
i8 playerCountMin{};
i8 playerCountMax{};
bool enableTakeOverConnection{};
bool enableLeftJustify{};
bool enablePermitJoyDual{};
bool enableSingleMode{};
bool enableIdentificationColor{};
std::array<IdentificationColor, NumControllersSupported> identification_colors{};
bool enableExplainText{};
std::array<ExplainText, NumControllersSupported> explain_text{};
};
// Applet versions 3-5 inclusive allow 4 controllers maximum
using ControllerSupportArgOld = ControllerSupportArg<4>;
static_assert(sizeof(ControllerSupportArgOld) == 0x21C);
// Applet versions 6-8 allow 8 controllers maximum
using ControllerSupportArgNew = ControllerSupportArg<8>;
static_assert(sizeof(ControllerSupportArgNew) == 0x430);
/**
* @brief The result type of the controller applet controller support mode
*/
struct ControllerSupportResultInfo {
i8 playerCount;
u8 _pad_[3];
input::NpadId selectedId;
Result result;
};
static_assert(sizeof(ControllerSupportResultInfo) == 0xC);
/**
* @brief Handles the 'ShowControllerSupport' mode of the controller applet
*/
void HandleShowControllerSupport(input::NpadStyleSet styleSet, ControllerAppletVersion version, span<u8> arg);
public:
ControllerApplet(const DeviceState &state, service::ServiceManager &manager, std::shared_ptr<kernel::type::KEvent> onAppletStateChanged, std::shared_ptr<kernel::type::KEvent> onNormalDataPushFromApplet, std::shared_ptr<kernel::type::KEvent> onInteractiveDataPushFromApplet, service::applet::LibraryAppletMode appletMode);
Result Start() override;
Result GetResult() override;
void PushNormalDataToApplet(std::shared_ptr<service::am::IStorage> data) override;
void PushInteractiveDataToApplet(std::shared_ptr<service::am::IStorage> data) override;
};
}

View file

@ -1,63 +0,0 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include "error_applet.h"
#include "services/am/storage/ObjIStorage.h"
namespace skyline::applet {
ErrorApplet::ErrorApplet(const DeviceState &state,
service::ServiceManager &manager,
std::shared_ptr<kernel::type::KEvent> onAppletStateChanged,
std::shared_ptr<kernel::type::KEvent> onNormalDataPushFromApplet,
std::shared_ptr<kernel::type::KEvent> onInteractiveDataPushFromApplet,
service::applet::LibraryAppletMode appletMode)
: IApplet{state, manager, std::move(onAppletStateChanged), std::move(onNormalDataPushFromApplet), std::move(onInteractiveDataPushFromApplet), appletMode} {}
Result ErrorApplet::Start() {
auto commonArg{PopNormalInput<service::applet::CommonArguments>()};
errorStorage = PopNormalInput();
auto errorCommonHeader{errorStorage->GetSpan().as<ErrorCommonHeader>()};
Logger::Debug("ErrorApplet: version: 0x{:X}, type: 0x{:X}", commonArg.apiVersion, errorCommonHeader.type);
switch (errorCommonHeader.type) {
case ErrorType::ErrorCommonArg:
HandleErrorCommonArg();
break;
case ErrorType::ApplicationErrorArg:
HandleApplicationErrorArg();
break;
default:
throw exception("ErrorApplet type 0x{:X} is not implemented", errorCommonHeader.type);
}
// Notify the guest that we've finished running
onAppletStateChanged->Signal();
return {};
}
void ErrorApplet::HandleErrorCommonArg() {
auto errorCommonArg{errorStorage->GetSpan().as<ErrorCommonArg>()};
Logger::Error("ErrorApplet: error code: 0x{:X}, result: 0x{:X}", errorCommonArg.errorCode, errorCommonArg.result);
}
void ErrorApplet::HandleApplicationErrorArg() {
auto applicationErrorStorage{errorStorage->GetSpan().as<ApplicationErrorArg>()};
if (applicationErrorStorage.fullscreenMessage[0] == '\0')
Logger::ErrorNoPrefix("Application Error: {}", applicationErrorStorage.dialogMessage.data());
else
Logger::ErrorNoPrefix("Application Error: {}\nFull message: {}", applicationErrorStorage.dialogMessage.data(), applicationErrorStorage.fullscreenMessage.data());
}
Result ErrorApplet::GetResult() {
return {};
}
void ErrorApplet::PushNormalDataToApplet(std::shared_ptr<service::am::IStorage> data) {
PushNormalInput(data);
}
void ErrorApplet::PushInteractiveDataToApplet(std::shared_ptr<service::am::IStorage> data) {}
}

View file

@ -1,81 +0,0 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
// Copyright © 2020 Ryujinx Team and Contributors (https://github.com/ryujinx/)
#pragma once
#include <services/am/applet/IApplet.h>
#include "common/language.h"
namespace skyline::applet {
/**
* @brief The Error Applet is utilized by the guest to display an error to the user, this class prints the supplied error to the logger
* @url https://switchbrew.org/wiki/Error_Applet
* @url https://switchbrew.org/wiki/Error_applet
*/
class ErrorApplet : public service::am::IApplet, service::am::EnableNormalQueue {
private:
#pragma pack(push, 1)
enum class ErrorType : u8 {
ErrorCommonArg = 0,
SystemErrorArg = 1,
ApplicationErrorArg = 2,
ErrorEulaArg = 3,
ErrorPctlArg = 4,
ErrorRecordArg = 5,
SystemUpdateEulaArg = 8
};
/**
* @url https://switchbrew.org/wiki/Error_Applet#ErrorCommonHeader
*/
struct ErrorCommonHeader {
ErrorType type;
u8 jump;
u8 _pad_[0x3];
u8 contextFlag;
u8 resultFlag;
u8 contextFlag2;
};
static_assert(sizeof(ErrorCommonHeader) == 0x8);
/**
* @url https://switchbrew.org/wiki/Error_Applet#ErrorCommonArg
*/
struct ErrorCommonArg {
ErrorCommonHeader header;
u64 errorCode;
Result result;
};
struct ApplicationErrorArg {
ErrorCommonHeader commonHeader;
u32 errorNumber;
LanguageCode languageCode;
std::array<char, 0x800> dialogMessage;
std::array<char, 0x800> fullscreenMessage; //!< The message displayed when the user clicks on "Details", when not set this disables displaying Details
};
static_assert(sizeof(ApplicationErrorArg) == 0x1014);
#pragma pack(pop)
std::shared_ptr<service::am::IStorage> errorStorage;
void HandleErrorCommonArg();
void HandleApplicationErrorArg();
public:
ErrorApplet(const DeviceState &state, service::ServiceManager &manager, std::shared_ptr<kernel::type::KEvent> onAppletStateChanged, std::shared_ptr<kernel::type::KEvent> onNormalDataPushFromApplet, std::shared_ptr<kernel::type::KEvent> onInteractiveDataPushFromApplet, service::applet::LibraryAppletMode appletMode);
Result Start() override;
Result GetResult() override;
void PushNormalDataToApplet(std::shared_ptr<service::am::IStorage> data) override;
void PushInteractiveDataToApplet(std::shared_ptr<service::am::IStorage> data) override;
};
}

View file

@ -1,32 +0,0 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <services/am/storage/ObjIStorage.h>
#include "player_select_applet.h"
namespace skyline::applet {
PlayerSelectApplet::PlayerSelectApplet(const DeviceState &state,
service::ServiceManager &manager,
std::shared_ptr<kernel::type::KEvent> onAppletStateChanged,
std::shared_ptr<kernel::type::KEvent> onNormalDataPushFromApplet,
std::shared_ptr<kernel::type::KEvent> onInteractiveDataPushFromApplet,
service::applet::LibraryAppletMode appletMode)
: IApplet{state, manager, std::move(onAppletStateChanged), std::move(onNormalDataPushFromApplet), std::move(onInteractiveDataPushFromApplet), appletMode} {}
Result PlayerSelectApplet::Start() {
// Return default user
PushNormalDataAndSignal(std::make_shared<service::am::ObjIStorage<AccountResult>>(state, manager, AccountResult{}));
// Notify the guest that we've finished running
onAppletStateChanged->Signal();
return {};
};
Result PlayerSelectApplet::GetResult() {
return {};
}
void PlayerSelectApplet::PushNormalDataToApplet(std::shared_ptr<service::am::IStorage> data) {}
void PlayerSelectApplet::PushInteractiveDataToApplet(std::shared_ptr<service::am::IStorage> data) {}
}

View file

@ -1,37 +0,0 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
#pragma once
#include <common/uuid.h>
#include <services/account/IAccountServiceForApplication.h>
#include <services/am/applet/IApplet.h>
namespace skyline::applet {
/**
* @brief The player select applet is responsible for allowing the user to select a player to use with the application
*/
class PlayerSelectApplet : public service::am::IApplet {
private:
/**
* @brief Result structure for the player select applet
*/
struct AccountResult {
Result result{};
u32 _pad_;
service::account::UserId accountId{constant::DefaultUserId};
};
static_assert(sizeof(AccountResult) == 0x18);
public:
PlayerSelectApplet(const DeviceState &state, service::ServiceManager &manager, std::shared_ptr<kernel::type::KEvent> onAppletStateChanged, std::shared_ptr<kernel::type::KEvent> onNormalDataPushFromApplet, std::shared_ptr<kernel::type::KEvent> onInteractiveDataPushFromApplet, service::applet::LibraryAppletMode appletMode);
Result Start() override;
Result GetResult() override;
void PushNormalDataToApplet(std::shared_ptr<service::am::IStorage> data) override;
void PushInteractiveDataToApplet(std::shared_ptr<service::am::IStorage> data) override;
};
}

View file

@ -1,182 +0,0 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
// Copyright © 2019-2022 Ryujinx Team and Contributors
#include <codecvt>
#include <services/am/storage/ObjIStorage.h>
#include <common/settings.h>
#include "software_keyboard_applet.h"
#include <jvm.h>
class Utf8Utf16Converter : public std::codecvt<char16_t, char8_t, std::mbstate_t> {
public:
~Utf8Utf16Converter() override = default;
};
namespace skyline::applet::swkbd {
static void WriteStringToSpan(span<u8> chars, std::u16string_view text, bool useUtf8Storage) {
if (useUtf8Storage) {
auto u8chars{chars.cast<char8_t>()};
Utf8Utf16Converter::state_type convert_state;
const char16_t *from_next;
char8_t *to_next;
Utf8Utf16Converter().out(convert_state, text.data(), text.end(), from_next, u8chars.data(), u8chars.end().base(), to_next);
// Null terminate the string, if it isn't out of bounds
if (to_next < reinterpret_cast<const char8_t *>(text.end()))
*to_next = u8'\0';
} else {
std::memcpy(chars.data(), text.data(), std::min(text.size() * sizeof(char16_t), chars.size()));
// Null terminate the string, if it isn't out of bounds
if (text.size() * sizeof(char16_t) < chars.size())
*(reinterpret_cast<char16_t *>(chars.data()) + text.size()) = u'\0';
}
}
SoftwareKeyboardApplet::ValidationRequest::ValidationRequest(std::u16string_view text, bool useUtf8Storage) : size{sizeof(ValidationRequest)} {
WriteStringToSpan(chars, text, useUtf8Storage);
}
SoftwareKeyboardApplet::OutputResult::OutputResult(CloseResult closeResult, std::u16string_view text, bool useUtf8Storage) : closeResult{closeResult} {
WriteStringToSpan(chars, text, useUtf8Storage);
}
static std::u16string FillDefaultText(u32 minLength, u32 maxLength) {
std::u16string text{u"Skyline"};
while (text.size() < minLength)
text += u"Emulator" + text;
if (text.size() > maxLength)
text.resize(maxLength);
return text;
}
void SoftwareKeyboardApplet::SendResult() {
if (dialog)
state.jvm->CloseKeyboard(dialog);
PushNormalDataAndSignal(std::make_shared<service::am::ObjIStorage<OutputResult>>(state, manager, OutputResult{currentResult, currentText, config.commonConfig.isUseUtf8}));
onAppletStateChanged->Signal();
}
SoftwareKeyboardApplet::SoftwareKeyboardApplet(
const DeviceState &state,
service::ServiceManager &manager,
std::shared_ptr<kernel::type::KEvent> onAppletStateChanged,
std::shared_ptr<kernel::type::KEvent> onNormalDataPushFromApplet,
std::shared_ptr<kernel::type::KEvent> onInteractiveDataPushFromApplet,
service::applet::LibraryAppletMode appletMode)
: IApplet{state,
manager,
std::move(onAppletStateChanged),
std::move(onNormalDataPushFromApplet),
std::move(onInteractiveDataPushFromApplet),
appletMode}, mode{appletMode} {
}
Result SoftwareKeyboardApplet::Start() {
if (mode != service::applet::LibraryAppletMode::AllForeground) {
Logger::Warn("Stubbing out InlineKeyboard!");
SendResult();
return {};
}
std::scoped_lock lock{normalInputDataMutex};
auto commonArgs{normalInputData.front()->GetSpan().as<service::applet::CommonArguments>()};
normalInputData.pop();
auto configSpan{normalInputData.front()->GetSpan()};
normalInputData.pop();
config = [&] {
if (commonArgs.apiVersion < 0x30007)
return KeyboardConfigVB{configSpan.as<KeyboardConfigV0>()};
else if (commonArgs.apiVersion < 0x6000B)
return KeyboardConfigVB{configSpan.as<KeyboardConfigV7>()};
else
return configSpan.as<KeyboardConfigVB>();
}();
Logger::Debug("Swkbd Config:\n* KeyboardMode: {}\n* InvalidCharFlags: {:#09b}\n* TextMaxLength: {}\n* TextMinLength: {}\n* PasswordMode: {}\n* InputFormMode: {}\n* IsUseNewLine: {}\n* IsUseTextCheck: {}",
static_cast<u32>(config.commonConfig.keyboardMode),
config.commonConfig.invalidCharFlags.raw,
config.commonConfig.textMaxLength,
config.commonConfig.textMinLength,
static_cast<u32>(config.commonConfig.passwordMode),
static_cast<u32>(config.commonConfig.inputFormMode),
config.commonConfig.isUseNewLine,
config.commonConfig.isUseTextCheck
);
auto maxChars{static_cast<u32>(SwkbdTextBytes / (config.commonConfig.isUseUtf8 ? sizeof(char8_t) : sizeof(char16_t)))};
config.commonConfig.textMaxLength = std::min(config.commonConfig.textMaxLength, maxChars);
if (config.commonConfig.textMaxLength == 0)
config.commonConfig.textMaxLength = maxChars;
config.commonConfig.textMinLength = std::min(config.commonConfig.textMinLength, config.commonConfig.textMaxLength);
if (config.commonConfig.textMaxLength > MaxOneLineChars)
config.commonConfig.inputFormMode = InputFormMode::MultiLine;
if (!normalInputData.empty() && config.commonConfig.initialStringLength > 0)
currentText = std::u16string(normalInputData.front()->GetSpan().subspan(config.commonConfig.initialStringOffset).cast<char16_t>().data(), config.commonConfig.initialStringLength);
dialog = state.jvm->ShowKeyboard(*reinterpret_cast<JvmManager::KeyboardConfig *>(&config), currentText);
if (!dialog) {
Logger::Warn("Couldn't show keyboard dialog, using default text");
currentResult = CloseResult::Enter;
currentText = FillDefaultText(config.commonConfig.textMinLength, config.commonConfig.textMaxLength);
} else {
auto result{state.jvm->WaitForSubmitOrCancel(dialog)};
currentResult = static_cast<CloseResult>(result.first);
currentText = result.second;
}
if (config.commonConfig.isUseTextCheck && currentResult == CloseResult::Enter) {
PushInteractiveDataAndSignal(std::make_shared<service::am::ObjIStorage<ValidationRequest>>(state, manager, ValidationRequest{currentText, config.commonConfig.isUseUtf8}));
validationPending = true;
} else {
SendResult();
}
return {};
}
Result SoftwareKeyboardApplet::GetResult() {
return {};
}
void SoftwareKeyboardApplet::PushNormalDataToApplet(std::shared_ptr<service::am::IStorage> data) {
PushNormalInput(data);
}
void SoftwareKeyboardApplet::PushInteractiveDataToApplet(std::shared_ptr<service::am::IStorage> data) {
if (validationPending) {
auto dataSpan{data->GetSpan()};
auto validationResult{dataSpan.as<ValidationResult>()};
if (validationResult.result == TextCheckResult::Success) {
validationPending = false;
SendResult();
} else {
if (dialog) {
if (static_cast<CloseResult>(state.jvm->ShowValidationResult(dialog, static_cast<JvmManager::KeyboardTextCheckResult>(validationResult.result), std::u16string(validationResult.chars.data()))) == CloseResult::Enter) {
// Accepted on confirmation dialog
validationPending = false;
SendResult();
} else {
// Cancelled or failed validation, go back to waiting for text
auto result{state.jvm->WaitForSubmitOrCancel(dialog)};
currentResult = static_cast<CloseResult>(result.first);
currentText = result.second;
if (currentResult == CloseResult::Enter) {
PushInteractiveDataAndSignal(std::make_shared<service::am::ObjIStorage<ValidationRequest>>(state, manager, ValidationRequest{currentText, config.commonConfig.isUseUtf8}));
} else {
SendResult();
}
}
} else {
std::array<u8, SwkbdTextBytes> chars{};
WriteStringToSpan(chars, std::u16string(validationResult.chars.data()), true);
std::string message{reinterpret_cast<char *>(chars.data())};
if (validationResult.result == TextCheckResult::ShowFailureDialog)
Logger::Warn("Sending default text despite being rejected by the guest with message: \"{}\"", message);
else
Logger::Debug("Guest asked to confirm default text with message: \"{}\"", message);
PushNormalDataAndSignal(std::make_shared<service::am::ObjIStorage<OutputResult>>(state, manager, OutputResult{CloseResult::Enter, currentText, config.commonConfig.isUseUtf8}));
}
}
}
}
}

View file

@ -1,97 +0,0 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
#pragma once
#include <services/am/applet/IApplet.h>
#include <services/applet/common_arguments.h>
#include <jvm.h>
#include "software_keyboard_config.h"
namespace skyline::applet::swkbd {
static_assert(sizeof(KeyboardConfigVB) == sizeof(JvmManager::KeyboardConfig));
/**
* @url https://switchbrew.org/wiki/Software_Keyboard
* @brief An implementation for the Software Keyboard (swkbd) Applet which handles translating guest applet transactions to the appropriate host behavior
*/
class SoftwareKeyboardApplet : public service::am::IApplet, service::am::EnableNormalQueue {
private:
/**
* @url https://switchbrew.org/wiki/Software_Keyboard#CloseResult
*/
enum class CloseResult : u32 {
Enter = 0x0,
Cancel = 0x1,
};
/**
* @url https://switchbrew.org/wiki/Software_Keyboard#TextCheckResult
*/
enum class TextCheckResult : u32 {
Success = 0x0,
ShowFailureDialog = 0x1,
ShowConfirmDialog = 0x2,
};
static constexpr u32 SwkbdTextBytes{0x7D4}; //!< Size of the returned IStorage buffer that's used to return the input text
static constexpr u32 MaxOneLineChars{32}; //!< The maximum number of characters for which anything other than InputFormMode::MultiLine is used
#pragma pack(push, 1)
/**
* @brief The final result after the swkbd has closed
*/
struct OutputResult {
CloseResult closeResult;
std::array<u8, SwkbdTextBytes> chars{};
OutputResult(CloseResult closeResult, std::u16string_view text, bool useUtf8Storage);
};
static_assert(sizeof(OutputResult) == 0x7D8);
/**
* @brief A request for validating a string inside guest code, this is pushed via the interactive queue
*/
struct ValidationRequest {
u64 size;
std::array<u8, SwkbdTextBytes> chars{};
ValidationRequest(std::u16string_view text, bool useUtf8Storage);
};
static_assert(sizeof(ValidationRequest) == 0x7DC);
/**
* @brief The result of validating text submitted to the guest
*/
struct ValidationResult {
TextCheckResult result;
std::array<char16_t, SwkbdTextBytes / sizeof(char16_t)> chars;
};
static_assert(sizeof(ValidationResult) == 0x7D8);
#pragma pack(pop)
KeyboardConfigVB config{};
service::applet::LibraryAppletMode mode{};
bool validationPending{};
std::u16string currentText{};
CloseResult currentResult{};
jobject dialog{};
void SendResult();
public:
SoftwareKeyboardApplet(const DeviceState &state, service::ServiceManager &manager, std::shared_ptr<kernel::type::KEvent> onAppletStateChanged, std::shared_ptr<kernel::type::KEvent> onNormalDataPushFromApplet, std::shared_ptr<kernel::type::KEvent> onInteractiveDataPushFromApplet, service::applet::LibraryAppletMode appletMode);
Result Start() override;
Result GetResult() override;
void PushNormalDataToApplet(std::shared_ptr<service::am::IStorage> data) override;
void PushInteractiveDataToApplet(std::shared_ptr<service::am::IStorage> data) override;
};
}

View file

@ -1,13 +0,0 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
// Copyright © 2019-2022 Ryujinx Team and Contributors
#include "software_keyboard_config.h"
namespace skyline::applet::swkbd {
KeyboardConfigVB::KeyboardConfigVB() = default;
KeyboardConfigVB::KeyboardConfigVB(const KeyboardConfigV7 &v7config) : commonConfig{v7config.commonConfig}, separateTextPos{v7config.separateTextPos} {}
KeyboardConfigVB::KeyboardConfigVB(const KeyboardConfigV0 &v0config) : commonConfig{v0config.commonConfig} {}
}

View file

@ -1,187 +0,0 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
// Copyright © 2019-2022 Ryujinx Team and Contributors
#pragma once
#include <services/account/IAccountServiceForApplication.h>
namespace skyline::applet::swkbd {
/**
* @brief Specifies the characters the keyboard should allow you to input
* @url https://switchbrew.org/wiki/Software_Keyboard#KeyboardMode
*/
enum class KeyboardMode : u32 {
Full = 0x0,
Numeric = 0x1,
ASCII = 0x2,
FullLatin = 0x3,
Alphabet = 0x4,
SimplifiedChinese = 0x5,
TraditionalChinese = 0x6,
Korean = 0x7,
LanguageSet2 = 0x8,
LanguageSet2Latin = 0x9,
};
/**
* @brief Specifies the characters that you shouldn't be allowed to input
* @url https://switchbrew.org/wiki/Software_Keyboard#InvalidCharFlags
*/
union InvalidCharFlags {
u32 raw;
struct {
u32 _pad_ : 1;
u32 space : 1;
u32 atMark : 1;
u32 percent : 1;
u32 slash : 1;
u32 backslash : 1;
u32 numeric : 1;
u32 outsideOfDownloadCode : 1;
u32 outsideOfMiiNickName : 1;
} flags;
};
/**
* @brief Specifies where the cursor should initially be on the initial string
* @url https://switchbrew.org/wiki/Software_Keyboard#InitialCursorPos
*/
enum class InitialCursorPos : u32 {
First = 0x0,
Last = 0x1,
};
/**
* @url https://switchbrew.org/wiki/Software_Keyboard#PasswordMode
*/
enum class PasswordMode : u32 {
Show = 0x0,
Hide = 0x1, //!< Hides any inputted text to prevent a password from being leaked
};
/**
* @url https://switchbrew.org/wiki/Software_Keyboard#InputFormMode
* @note Only applies when 1 <= textMaxLength <= 32, otherwise Multiline is used
*/
enum class InputFormMode : u32 {
OneLine = 0x0,
MultiLine = 0x1,
Separate = 0x2, //!< Used with separateTextPos
};
/**
* @brief Specifies the language of custom dictionary entries
* @url https://switchbrew.org/wiki/Software_Keyboard#DictionaryLanguage
*/
enum class DictionaryLanguage : u16 {
Japanese = 0x00,
AmericanEnglish = 0x01,
CanadianFrench = 0x02,
LatinAmericanSpanish = 0x03,
Reserved1 = 0x04,
BritishEnglish = 0x05,
French = 0x06,
German = 0x07,
Spanish = 0x08,
Italian = 0x09,
Dutch = 0x0A,
Portuguese = 0x0B,
Russian = 0x0C,
Reserved2 = 0x0D,
SimplifiedChinesePinyin = 0x0E,
TraditionalChineseCangjie = 0x0F,
TraditionalChineseSimplifiedCangjie = 0x10,
TraditionalChineseZhuyin = 0x11,
Korean = 0x12,
};
/**
* @brief A descriptor of a custom dictionary entry
* @url https://switchbrew.org/wiki/Software_Keyboard#DictionaryInfo
*/
struct DictionaryInfo {
u32 offset;
u16 size;
DictionaryLanguage language;
};
/**
* @brief The keyboard config that's common across all versions
* @url https://switchbrew.org/wiki/Software_Keyboard#KeyboardConfig
*/
struct CommonKeyboardConfig {
KeyboardMode keyboardMode;
std::array<char16_t, 0x9> okText;
char16_t leftOptionalSymbolKey;
char16_t rightOptionalSymbolKey;
bool isPredictionEnabled;
u8 _pad0_;
InvalidCharFlags invalidCharFlags;
InitialCursorPos initialCursorPos;
std::array<char16_t, 0x41> headerText;
std::array<char16_t, 0x81> subText;
std::array<char16_t, 0x101> guideText;
u8 _pad2_[0x2];
u32 textMaxLength;
u32 textMinLength;
PasswordMode passwordMode;
InputFormMode inputFormMode;
bool isUseNewLine;
bool isUseUtf8;
bool isUseBlurBackground;
u8 _pad3_;
u32 initialStringOffset;
u32 initialStringLength;
u32 userDictionaryOffset;
u32 userDictionaryNum;
bool isUseTextCheck;
u8 reserved[0x3];
};
static_assert(sizeof(CommonKeyboardConfig) == 0x3D4);
/**
* @brief The keyboard config for the first api version
* @url https://switchbrew.org/wiki/Software_Keyboard#KeyboardConfig
*/
struct KeyboardConfigV0 {
CommonKeyboardConfig commonConfig;
u8 _pad0_[0x4];
u64 textCheckCallback{};
};
static_assert(sizeof(KeyboardConfigV0) == 0x3E0);
/**
* @brief The keyboard config as of api version 0x30007
* @url https://switchbrew.org/wiki/Software_Keyboard#KeyboardConfig
*/
struct KeyboardConfigV7 {
CommonKeyboardConfig commonConfig;
u8 _pad0_[0x4];
u64 textCheckCallback;
std::array<u32, 0x8> separateTextPos;
};
static_assert(sizeof(KeyboardConfigV7) == 0x400);
/**
* @brief The keyboard config as of api version 0x6000B
* @url https://switchbrew.org/wiki/Software_Keyboard#KeyboardConfig
*/
struct KeyboardConfigVB {
CommonKeyboardConfig commonConfig{};
std::array<u32, 0x8> separateTextPos{0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF};
std::array<DictionaryInfo, 0x18> customisedDictionaryInfoList{};
u8 customisedDictionaryCount{};
bool isCancelButtonDisabled{};
u8 reserved0[0xD];
u8 trigger{};
u8 reserved1[0x4];
KeyboardConfigVB();
KeyboardConfigVB(const KeyboardConfigV7 &v7config);
KeyboardConfigVB(const KeyboardConfigV0 &v0config);
};
static_assert(sizeof(KeyboardConfigVB) == 0x4C8);
}

View file

@ -1,47 +0,0 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
// Copyright © 2020 yuzu Emulator Project (https://github.com/yuzu-emu/)
#include <input.h>
#include <input/npad.h>
#include <services/applet/common_arguments.h>
#include <services/am/storage/ObjIStorage.h>
#include "web_applet.h"
namespace skyline::applet {
WebApplet::WebApplet(const DeviceState &state,
service::ServiceManager &manager,
std::shared_ptr<kernel::type::KEvent> onAppletStateChanged,
std::shared_ptr<kernel::type::KEvent> onNormalDataPushFromApplet,
std::shared_ptr<kernel::type::KEvent> onInteractiveDataPushFromApplet,
service::applet::LibraryAppletMode appletMode)
: IApplet{state, manager, std::move(onAppletStateChanged), std::move(onNormalDataPushFromApplet), std::move(onInteractiveDataPushFromApplet), appletMode} {}
Result WebApplet::Start() {
auto commonArg{PopNormalInput<service::applet::CommonArguments>()};
auto argHeader{PopNormalInput<WebArgHeader>()};
if ((commonArg.apiVersion >= 0x80000 && argHeader.shimKind == ShimKind::Web) || (commonArg.apiVersion >= 0x30000 && argHeader.shimKind == ShimKind::Share))
Logger::Error("OfflineWeb TLV output is unsupported!");
PushNormalDataAndSignal(std::make_shared<service::am::ObjIStorage<WebCommonReturnValue>>(state, manager, WebCommonReturnValue{
.exitReason = WebExitReason::WindowClosed,
.lastUrl = {'h', 't', 't', 'p', ':', '/', '/', 'l', 'o', 'c', 'a', 'l', 'h', 'o', 's', 't', '/'},
.lastUrlSize = 17
}));
// Notify the guest that we've finished running
onAppletStateChanged->Signal();
return {};
}
Result WebApplet::GetResult() {
return {};
}
void WebApplet::PushNormalDataToApplet(std::shared_ptr<service::am::IStorage> data) {
PushNormalInput(data);
}
void WebApplet::PushInteractiveDataToApplet(std::shared_ptr<service::am::IStorage> data) {}
}

View file

@ -1,75 +0,0 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
// Copyright © 2020 Ryujinx Team and Contributors (https://github.com/ryujinx/)
#pragma once
#include <services/am/applet/IApplet.h>
namespace skyline::applet {
/**
* @brief The Web applet is utilized by the guest to display web pages using the built-in web browser
*/
class WebApplet : public service::am::IApplet, service::am::EnableNormalQueue {
private:
#pragma pack(push, 1)
/**
* @brief Type of web-applet to launch
* @url https://switchbrew.org/wiki/Internet_Browser#ShimKind
*/
enum class ShimKind : u32 {
Shop = 1,
Login = 2,
Offline = 3,
Share = 4,
Web = 5,
Wifi = 6,
Lobby = 7,
Lhub = 8,
};
enum class WebExitReason : u32 {
EndButtonPressed = 0,
BackButtonPressed = 1,
ExitRequested = 2,
CallbackURL = 3,
WindowClosed = 4,
ErrorDialog = 7,
};
/**
* @brief Common return value struct for all web-applet commands
*/
struct WebCommonReturnValue {
WebExitReason exitReason;
u32 _pad_;
std::array<char, 0x1000> lastUrl;
u64 lastUrlSize;
};
static_assert(sizeof(WebCommonReturnValue) == 0x1010);
/**
* @brief The input data for the web-applet
*/
struct WebArgHeader {
u16 count;
u16 _pad_;
ShimKind shimKind;
};
#pragma pack(pop)
public:
WebApplet(const DeviceState &state, service::ServiceManager &manager, std::shared_ptr<kernel::type::KEvent> onAppletStateChanged, std::shared_ptr<kernel::type::KEvent> onNormalDataPushFromApplet, std::shared_ptr<kernel::type::KEvent> onInteractiveDataPushFromApplet, service::applet::LibraryAppletMode appletMode);
Result Start() override;
Result GetResult() override;
void PushNormalDataToApplet(std::shared_ptr<service::am::IStorage> data) override;
void PushInteractiveDataToApplet(std::shared_ptr<service::am::IStorage> data) override;
};
}

View file

@ -1,55 +1,78 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <audio_core/audio_core.h>
#include <audio_core/audio_out_manager.h>
#include <audio_core/common/settings.h>
#include <common/settings.h>
#include <common/utils.h>
#include "audio.h"
namespace AudioCore::Log {
void Debug(const std::string &message) {
skyline::Logger::Write(skyline::Logger::LogLevel::Debug, message);
}
void Info(const std::string &message) {
skyline::Logger::Write(skyline::Logger::LogLevel::Info, message);
}
void Warn(const std::string &message) {
skyline::Logger::Write(skyline::Logger::LogLevel::Warn, message);
}
void Error(const std::string &message) {
skyline::Logger::Write(skyline::Logger::LogLevel::Error, message);
}
}
namespace Core::Timing {
skyline::u64 GetClockTicks() {
return skyline::util::GetTimeTicks();
}
std::chrono::nanoseconds GetClockNs() {
return std::chrono::nanoseconds{skyline::util::GetTimeNs()};
}
}
namespace skyline::audio {
Audio::Audio(const DeviceState &state)
: audioOutManager{std::make_unique<AudioCore::AudioOut::Manager>(audioSystem)},
audioRendererManager{std::make_unique<AudioCore::AudioRenderer::Manager>(audioSystem)} {
AudioCore::Settings::values.volume = *state.settings->isAudioOutputDisabled ? 0 : 200;
Audio::Audio(const DeviceState &state) : oboe::AudioStreamCallback() {
builder.setChannelCount(constant::ChannelCount);
builder.setSampleRate(constant::SampleRate);
builder.setFormat(constant::PcmFormat);
builder.setFramesPerCallback(constant::MixBufferSize);
builder.setUsage(oboe::Usage::Game);
builder.setCallback(this);
builder.setSharingMode(oboe::SharingMode::Exclusive);
builder.setPerformanceMode(oboe::PerformanceMode::LowLatency);
builder.openManagedStream(outputStream);
outputStream->requestStart();
}
Audio::~Audio() = default;
void Audio::Pause() {
audioSystem.AudioCore().GetOutputSink().SetSystemVolume(0.0f);
Audio::~Audio() {
outputStream->requestStop();
}
void Audio::Resume() {
audioSystem.AudioCore().GetOutputSink().SetSystemVolume(1.0f);
std::shared_ptr<AudioTrack> Audio::OpenTrack(u8 channelCount, u32 sampleRate, const std::function<void()> &releaseCallback) {
std::lock_guard trackGuard(trackLock);
auto track{std::make_shared<AudioTrack>(channelCount, sampleRate, releaseCallback)};
audioTracks.push_back(track);
return track;
}
void Audio::CloseTrack(std::shared_ptr<AudioTrack> &track) {
std::lock_guard trackGuard(trackLock);
audioTracks.erase(std::remove(audioTracks.begin(), audioTracks.end(), track), audioTracks.end());
track.reset();
}
oboe::DataCallbackResult Audio::onAudioReady(oboe::AudioStream *audioStream, void *audioData, int32_t numFrames) {
auto destBuffer{static_cast<i16 *>(audioData)};
auto streamSamples{static_cast<size_t>(numFrames) * audioStream->getChannelCount()};
size_t writtenSamples{};
{
std::lock_guard trackGuard(trackLock);
for (auto &track : audioTracks) {
if (track->playbackState == AudioOutState::Stopped)
continue;
std::lock_guard bufferGuard(track->bufferLock);
auto trackSamples{track->samples.Read(span(destBuffer, streamSamples), [](i16 *source, i16 *destination) {
*destination = Saturate<i16, i32>(static_cast<u32>(*destination) + static_cast<u32>(*source));
}, writtenSamples)};
writtenSamples = std::max(trackSamples, writtenSamples);
track->sampleCounter += trackSamples;
track->CheckReleasedBuffers();
}
}
if (streamSamples > writtenSamples)
memset(destBuffer + writtenSamples, 0, (streamSamples - writtenSamples) * sizeof(i16));
return oboe::DataCallbackResult::Continue;
}
void Audio::onErrorAfterClose(oboe::AudioStream *audioStream, oboe::Result error) {
if (error == oboe::Result::ErrorDisconnected) {
builder.openManagedStream(outputStream);
outputStream->requestStart();
}
}
}

View file

@ -3,33 +3,51 @@
#pragma once
#include <audio_core/core/core.h>
#include <common.h>
namespace AudioCore::AudioOut {
class Manager;
}
namespace AudioCore::AudioRenderer {
class Manager;
}
#include <audio/track.h>
namespace skyline::audio {
/**
* @brief The Audio class is used to bridge yuzu's audio core with services
* @brief The Audio class is used to mix audio from all tracks
*/
class Audio {
public:
Core::System audioSystem{};
std::unique_ptr<AudioCore::AudioOut::Manager> audioOutManager;
std::unique_ptr<AudioCore::AudioRenderer::Manager> audioRendererManager;
class Audio : public oboe::AudioStreamCallback {
private:
oboe::AudioStreamBuilder builder;
oboe::ManagedStream outputStream;
std::vector<std::shared_ptr<AudioTrack>> audioTracks;
std::mutex trackLock; //!< Synchronizes modifications to the audio tracks
public:
Audio(const DeviceState &state);
~Audio();
void Pause();
/**
* @brief Opens a new track that can be used to play sound
* @param channelCount The amount channels that are present in the track
* @param sampleRate The sample rate of the track
* @param releaseCallback The callback to call when a buffer has been released
* @return A shared pointer to a new AudioTrack object
*/
std::shared_ptr<AudioTrack> OpenTrack(u8 channelCount, u32 sampleRate, const std::function<void()> &releaseCallback);
void Resume();
/**
* @brief Closes a track and frees its data
*/
void CloseTrack(std::shared_ptr<AudioTrack> &track);
/**
* @brief The callback oboe uses to get audio sample data
* @param audioStream The audio stream we are being called by
* @param audioData The raw audio sample data
* @param numFrames The amount of frames the sample data needs to contain
*/
oboe::DataCallbackResult onAudioReady(oboe::AudioStream *audioStream, void *audioData, int32_t numFrames);
/**
* @brief The callback oboe uses to notify the application about stream closure
* @param audioStream The audio stream we are being called by
* @param error The error due to which the stream is being closed
*/
void onErrorAfterClose(oboe::AudioStream *audioStream, oboe::Result error);
};
}

View file

@ -0,0 +1,52 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include "common.h"
#include "adpcm_decoder.h"
namespace skyline::audio {
AdpcmDecoder::AdpcmDecoder(std::vector<std::array<i16, 2>> coefficients) : coefficients(std::move(coefficients)) {}
std::vector<i16> AdpcmDecoder::Decode(span<u8> adpcmData) {
constexpr size_t BytesPerFrame{0x8};
constexpr size_t SamplesPerFrame{0xE};
size_t remainingSamples{(adpcmData.size() / BytesPerFrame) * SamplesPerFrame};
std::vector<i16> output;
output.reserve(remainingSamples);
size_t inputOffset{};
while (inputOffset < adpcmData.size()) {
FrameHeader header{adpcmData[inputOffset++]};
size_t frameSamples{std::min(SamplesPerFrame, remainingSamples)};
i32 ctx{};
for (size_t index{}; index < frameSamples; index++) {
i32 sample{};
if (index & 1) {
sample = (ctx << 28) >> 28;
} else {
ctx = adpcmData[inputOffset++];
sample = (ctx << 24) >> 28;
}
i32 prediction{history[0] * coefficients[header.coefficientIndex][0] + history[1] * coefficients[header.coefficientIndex][1]};
sample = (sample * (0x800 << header.scale) + prediction + 0x400) >> 11;
auto saturated{audio::Saturate<i16, i32>(sample)};
output.push_back(saturated);
history[1] = history[0];
history[0] = saturated;
}
remainingSamples -= frameSamples;
}
return output;
}
}

View file

@ -0,0 +1,36 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#pragma once
#include <common.h>
namespace skyline::audio {
/**
* @brief The AdpcmDecoder class handles decoding single channel ADPCM (Adaptive Differential Pulse-Code Modulation) data
*/
class AdpcmDecoder {
private:
union FrameHeader {
u8 raw;
struct {
u8 scale : 4; //!< The scale factor for this frame
u8 coefficientIndex : 3;
u8 _pad_ : 1;
};
};
static_assert(sizeof(FrameHeader) == 0x1);
std::array<i32, 2> history{}; //!< The previous samples for decoding the ADPCM stream
std::vector<std::array<i16, 2>> coefficients; //!< The coefficients for decoding the ADPCM stream
public:
AdpcmDecoder(std::vector<std::array<i16, 2>> coefficients);
/**
* @brief Decodes a buffer of ADPCM data into I16 PCM
*/
std::vector<i16> Decode(span <u8> adpcmData);
};
}

View file

@ -0,0 +1,50 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#pragma once
#include <oboe/Oboe.h>
#include <common.h>
namespace skyline {
namespace constant {
constexpr u16 SampleRate{48000}; //!< The common sampling rate to use for audio output
constexpr u8 ChannelCount{2}; //!< The common amount of channels to use for audio output
constexpr u16 MixBufferSize{960}; //!< The size of the mix buffer by default
constexpr auto PcmFormat{oboe::AudioFormat::I16}; //!< The common PCM data format to use for audio output
}
namespace audio {
enum class AudioFormat : u8 {
Invalid = 0, //!< An invalid PCM format
Int8 = 1, //!< 8 bit integer PCM
Int16 = 2, //!< 16 bit integer PCM
Int24 = 3, //!< 24 bit integer PCM
Int32 = 4, //!< 32 bit integer PCM
Float = 5, //!< Floating point PCM
ADPCM = 6, //!< Adaptive differential PCM
};
enum class AudioOutState : u8 {
Started = 0, //!< Stream is started and is playing
Stopped = 1, //!< Stream is stopped, there are no samples left to play
Paused = 2, //!< Stream is paused, some samples may not have been played yet
};
struct BufferIdentifier {
u64 tag;
u64 finalSample; //!< The final sample this buffer will be played in, after that the buffer can be safely released
bool released; //!< If the buffer has been released (fully played back)
};
/**
* @brief Saturates the specified value according to the numeric limits of Out
* @tparam Out The return value type and the numeric limit clamp
* @tparam Intermediate The intermediate type that is converted to from In before clamping
*/
template<typename Out, typename Intermediate, typename In>
inline Out Saturate(In value) {
return static_cast<Out>(std::clamp(static_cast<Intermediate>(value), static_cast<Intermediate>(std::numeric_limits<Out>::min()), static_cast<Intermediate>(std::numeric_limits<Out>::max())));
}
}
}

View file

@ -0,0 +1,155 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include "common.h"
#include "resampler.h"
namespace skyline::audio {
/**
* @brief The coefficients for each index of a single output frame
*/
struct LutEntry {
i32 a;
i32 b;
i32 c;
i32 d;
};
// @fmt:off
constexpr std::array<LutEntry, 128> CurveLut0{{
{6600, 19426, 6722, 3}, {6479, 19424, 6845, 9}, {6359, 19419, 6968, 15}, {6239, 19412, 7093, 22},
{6121, 19403, 7219, 28}, {6004, 19391, 7345, 34}, {5888, 19377, 7472, 41}, {5773, 19361, 7600, 48},
{5659, 19342, 7728, 55}, {5546, 19321, 7857, 62}, {5434, 19298, 7987, 69}, {5323, 19273, 8118, 77},
{5213, 19245, 8249, 84}, {5104, 19215, 8381, 92}, {4997, 19183, 8513, 101}, {4890, 19148, 8646, 109},
{4785, 19112, 8780, 118}, {4681, 19073, 8914, 127}, {4579, 19031, 9048, 137}, {4477, 18988, 9183, 147},
{4377, 18942, 9318, 157}, {4277, 18895, 9454, 168}, {4179, 18845, 9590, 179}, {4083, 18793, 9726, 190},
{3987, 18738, 9863, 202}, {3893, 18682, 10000, 215}, {3800, 18624, 10137, 228}, {3709, 18563, 10274, 241},
{3618, 18500, 10411, 255}, {3529, 18436, 10549, 270}, {3441, 18369, 10687, 285}, {3355, 18300, 10824, 300},
{3269, 18230, 10962, 317}, {3186, 18157, 11100, 334}, {3103, 18082, 11238, 351}, {3022, 18006, 11375, 369},
{2942, 17927, 11513, 388}, {2863, 17847, 11650, 408}, {2785, 17765, 11788, 428}, {2709, 17681, 11925, 449},
{2635, 17595, 12062, 471}, {2561, 17507, 12198, 494}, {2489, 17418, 12334, 517}, {2418, 17327, 12470, 541},
{2348, 17234, 12606, 566}, {2280, 17140, 12741, 592}, {2213, 17044, 12876, 619}, {2147, 16946, 13010, 647},
{2083, 16846, 13144, 675}, {2020, 16745, 13277, 704}, {1958, 16643, 13409, 735}, {1897, 16539, 13541, 766},
{1838, 16434, 13673, 798}, {1780, 16327, 13803, 832}, {1723, 16218, 13933, 866}, {1667, 16109, 14062, 901},
{1613, 15998, 14191, 937}, {1560, 15885, 14318, 975}, {1508, 15772, 14445, 1013}, {1457, 15657, 14571, 1052},
{1407, 15540, 14695, 1093}, {1359, 15423, 14819, 1134}, {1312, 15304, 14942, 1177}, {1266, 15185, 15064, 1221},
{1221, 15064, 15185, 1266}, {1177, 14942, 15304, 1312}, {1134, 14819, 15423, 1359}, {1093, 14695, 15540, 1407},
{1052, 14571, 15657, 1457}, {1013, 14445, 15772, 1508}, {975, 14318, 15885, 1560}, {937, 14191, 15998, 1613},
{901, 14062, 16109, 1667}, {866, 13933, 16218, 1723}, {832, 13803, 16327, 1780}, {798, 13673, 16434, 1838},
{766, 13541, 16539, 1897}, {735, 13409, 16643, 1958}, {704, 13277, 16745, 2020}, {675, 13144, 16846, 2083},
{647, 13010, 16946, 2147}, {619, 12876, 17044, 2213}, {592, 12741, 17140, 2280}, {566, 12606, 17234, 2348},
{541, 12470, 17327, 2418}, {517, 12334, 17418, 2489}, {494, 12198, 17507, 2561}, {471, 12062, 17595, 2635},
{449, 11925, 17681, 2709}, {428, 11788, 17765, 2785}, {408, 11650, 17847, 2863}, {388, 11513, 17927, 2942},
{369, 11375, 18006, 3022}, {351, 11238, 18082, 3103}, {334, 11100, 18157, 3186}, {317, 10962, 18230, 3269},
{300, 10824, 18300, 3355}, {285, 10687, 18369, 3441}, {270, 10549, 18436, 3529}, {255, 10411, 18500, 3618},
{241, 10274, 18563, 3709}, {228, 10137, 18624, 3800}, {215, 10000, 18682, 3893}, {202, 9863, 18738, 3987},
{190, 9726, 18793, 4083}, {179, 9590, 18845, 4179}, {168, 9454, 18895, 4277}, {157, 9318, 18942, 4377},
{147, 9183, 18988, 4477}, {137, 9048, 19031, 4579}, {127, 8914, 19073, 4681}, {118, 8780, 19112, 4785},
{109, 8646, 19148, 4890}, {101, 8513, 19183, 4997}, {92, 8381, 19215, 5104}, {84, 8249, 19245, 5213},
{77, 8118, 19273, 5323}, {69, 7987, 19298, 5434}, {62, 7857, 19321, 5546}, {55, 7728, 19342, 5659},
{48, 7600, 19361, 5773}, {41, 7472, 19377, 5888}, {34, 7345, 19391, 6004}, {28, 7219, 19403, 6121},
{22, 7093, 19412, 6239}, {15, 6968, 19419, 6359}, {9, 6845, 19424, 6479}, {3, 6722, 19426, 6600}}};
constexpr std::array<LutEntry, 128> CurveLut1{{
{-68, 32639, 69, -5}, {-200, 32630, 212, -15}, {-328, 32613, 359, -26}, {-450, 32586, 512, -36},
{-568, 32551, 669, -47}, {-680, 32507, 832, -58}, {-788, 32454, 1000, -69}, {-891, 32393, 1174, -80},
{-990, 32323, 1352, -92}, {-1084, 32244, 1536, -103}, {-1173, 32157, 1724, -115}, {-1258, 32061, 1919, -128},
{-1338, 31956, 2118, -140}, {-1414, 31844, 2322, -153}, {-1486, 31723, 2532, -167}, {-1554, 31593, 2747, -180},
{-1617, 31456, 2967, -194}, {-1676, 31310, 3192, -209}, {-1732, 31157, 3422, -224}, {-1783, 30995, 3657, -240},
{-1830, 30826, 3897, -256}, {-1874, 30649, 4143, -272}, {-1914, 30464, 4393, -289}, {-1951, 30272, 4648, -307},
{-1984, 30072, 4908, -325}, {-2014, 29866, 5172, -343}, {-2040, 29652, 5442, -362}, {-2063, 29431, 5716, -382},
{-2083, 29203, 5994, -403}, {-2100, 28968, 6277, -424}, {-2114, 28727, 6565, -445}, {-2125, 28480, 6857, -468},
{-2133, 28226, 7153, -490}, {-2139, 27966, 7453, -514}, {-2142, 27700, 7758, -538}, {-2142, 27428, 8066, -563},
{-2141, 27151, 8378, -588}, {-2136, 26867, 8694, -614}, {-2130, 26579, 9013, -641}, {-2121, 26285, 9336, -668},
{-2111, 25987, 9663, -696}, {-2098, 25683, 9993, -724}, {-2084, 25375, 10326, -753}, {-2067, 25063, 10662, -783},
{-2049, 24746, 11000, -813}, {-2030, 24425, 11342, -844}, {-2009, 24100, 11686, -875}, {-1986, 23771, 12033, -907},
{-1962, 23438, 12382, -939}, {-1937, 23103, 12733, -972}, {-1911, 22764, 13086, -1005}, {-1883, 22422, 13441, -1039},
{-1855, 22077, 13798, -1072}, {-1825, 21729, 14156, -1107}, {-1795, 21380, 14516, -1141}, {-1764, 21027, 14877, -1176},
{-1732, 20673, 15239, -1211}, {-1700, 20317, 15602, -1246}, {-1667, 19959, 15965, -1282}, {-1633, 19600, 16329, -1317},
{-1599, 19239, 16694, -1353}, {-1564, 18878, 17058, -1388}, {-1530, 18515, 17423, -1424}, {-1495, 18151, 17787, -1459},
{-1459, 17787, 18151, -1495}, {-1424, 17423, 18515, -1530}, {-1388, 17058, 18878, -1564}, {-1353, 16694, 19239, -1599},
{-1317, 16329, 19600, -1633}, {-1282, 15965, 19959, -1667}, {-1246, 15602, 20317, -1700}, {-1211, 15239, 20673, -1732},
{-1176, 14877, 21027, -1764}, {-1141, 14516, 21380, -1795}, {-1107, 14156, 21729, -1825}, {-1072, 13798, 22077, -1855},
{-1039, 13441, 22422, -1883}, {-1005, 13086, 22764, -1911}, {-972, 12733, 23103, -1937}, {-939, 12382, 23438, -1962},
{-907, 12033, 23771, -1986}, {-875, 11686, 24100, -2009}, {-844, 11342, 24425, -2030}, {-813, 11000, 24746, -2049},
{-783, 10662, 25063, -2067}, {-753, 10326, 25375, -2084}, {-724, 9993, 25683, -2098}, {-696, 9663, 25987, -2111},
{-668, 9336, 26285, -2121}, {-641, 9013, 26579, -2130}, {-614, 8694, 26867, -2136}, {-588, 8378, 27151, -2141},
{-563, 8066, 27428, -2142}, {-538, 7758, 27700, -2142}, {-514, 7453, 27966, -2139}, {-490, 7153, 28226, -2133},
{-468, 6857, 28480, -2125}, {-445, 6565, 28727, -2114}, {-424, 6277, 28968, -2100}, {-403, 5994, 29203, -2083},
{-382, 5716, 29431, -2063}, {-362, 5442, 29652, -2040}, {-343, 5172, 29866, -2014}, {-325, 4908, 30072, -1984},
{-307, 4648, 30272, -1951}, {-289, 4393, 30464, -1914}, {-272, 4143, 30649, -1874}, {-256, 3897, 30826, -1830},
{-240, 3657, 30995, -1783}, {-224, 3422, 31157, -1732}, {-209, 3192, 31310, -1676}, {-194, 2967, 31456, -1617},
{-180, 2747, 31593, -1554}, {-167, 2532, 31723, -1486}, {-153, 2322, 31844, -1414}, {-140, 2118, 31956, -1338},
{-128, 1919, 32061, -1258}, {-115, 1724, 32157, -1173}, {-103, 1536, 32244, -1084}, {-92, 1352, 32323, -990},
{-80, 1174, 32393, -891}, {-69, 1000, 32454, -788}, {-58, 832, 32507, -680}, {-47, 669, 32551, -568},
{-36, 512, 32586, -450}, {-26, 359, 32613, -328}, {-15, 212, 32630, -200}, {-5, 69, 32639, -68}}};
constexpr std::array<LutEntry, 128> CurveLut2{{
{3195, 26287, 3329, -32}, {3064, 26281, 3467, -34}, {2936, 26270, 3608, -38}, {2811, 26253, 3751, -42},
{2688, 26230, 3897, -46}, {2568, 26202, 4046, -50}, {2451, 26169, 4199, -54}, {2338, 26130, 4354, -58},
{2227, 26085, 4512, -63}, {2120, 26035, 4673, -67}, {2015, 25980, 4837, -72}, {1912, 25919, 5004, -76},
{1813, 25852, 5174, -81}, {1716, 25780, 5347, -87}, {1622, 25704, 5522, -92}, {1531, 25621, 5701, -98},
{1442, 25533, 5882, -103}, {1357, 25440, 6066, -109}, {1274, 25342, 6253, -115}, {1193, 25239, 6442, -121},
{1115, 25131, 6635, -127}, {1040, 25018, 6830, -133}, {967, 24899, 7027, -140}, {897, 24776, 7227, -146},
{829, 24648, 7430, -153}, {764, 24516, 7635, -159}, {701, 24379, 7842, -166}, {641, 24237, 8052, -174},
{583, 24091, 8264, -181}, {526, 23940, 8478, -187}, {472, 23785, 8695, -194}, {420, 23626, 8914, -202},
{371, 23462, 9135, -209}, {324, 23295, 9358, -215}, {279, 23123, 9583, -222}, {236, 22948, 9809, -230},
{194, 22769, 10038, -237}, {154, 22586, 10269, -243}, {117, 22399, 10501, -250}, {81, 22208, 10735, -258},
{47, 22015, 10970, -265}, {15, 21818, 11206, -271}, {-16, 21618, 11444, -277}, {-44, 21415, 11684, -283},
{-71, 21208, 11924, -290}, {-97, 20999, 12166, -296}, {-121, 20786, 12409, -302}, {-143, 20571, 12653, -306},
{-163, 20354, 12898, -311}, {-183, 20134, 13143, -316}, {-201, 19911, 13389, -321}, {-218, 19686, 13635, -325},
{-234, 19459, 13882, -328}, {-248, 19230, 14130, -332}, {-261, 18998, 14377, -335}, {-273, 18765, 14625, -337},
{-284, 18531, 14873, -339}, {-294, 18295, 15121, -341}, {-302, 18057, 15369, -341}, {-310, 17817, 15617, -341},
{-317, 17577, 15864, -340}, {-323, 17335, 16111, -340}, {-328, 17092, 16357, -338}, {-332, 16848, 16603, -336},
{-336, 16603, 16848, -332}, {-338, 16357, 17092, -328}, {-340, 16111, 17335, -323}, {-340, 15864, 17577, -317},
{-341, 15617, 17817, -310}, {-341, 15369, 18057, -302}, {-341, 15121, 18295, -294}, {-339, 14873, 18531, -284},
{-337, 14625, 18765, -273}, {-335, 14377, 18998, -261}, {-332, 14130, 19230, -248}, {-328, 13882, 19459, -234},
{-325, 13635, 19686, -218}, {-321, 13389, 19911, -201}, {-316, 13143, 20134, -183}, {-311, 12898, 20354, -163},
{-306, 12653, 20571, -143}, {-302, 12409, 20786, -121}, {-296, 12166, 20999, -97}, {-290, 11924, 21208, -71},
{-283, 11684, 21415, -44}, {-277, 11444, 21618, -16}, {-271, 11206, 21818, 15}, {-265, 10970, 22015, 47},
{-258, 10735, 22208, 81}, {-250, 10501, 22399, 117}, {-243, 10269, 22586, 154}, {-237, 10038, 22769, 194},
{-230, 9809, 22948, 236}, {-222, 9583, 23123, 279}, {-215, 9358, 23295, 324}, {-209, 9135, 23462, 371},
{-202, 8914, 23626, 420}, {-194, 8695, 23785, 472}, {-187, 8478, 23940, 526}, {-181, 8264, 24091, 583},
{-174, 8052, 24237, 641}, {-166, 7842, 24379, 701}, {-159, 7635, 24516, 764}, {-153, 7430, 24648, 829},
{-146, 7227, 24776, 897}, {-140, 7027, 24899, 967}, {-133, 6830, 25018, 1040}, {-127, 6635, 25131, 1115},
{-121, 6442, 25239, 1193}, {-115, 6253, 25342, 1274}, {-109, 6066, 25440, 1357}, {-103, 5882, 25533, 1442},
{-98, 5701, 25621, 1531}, {-92, 5522, 25704, 1622}, {-87, 5347, 25780, 1716}, {-81, 5174, 25852, 1813},
{-76, 5004, 25919, 1912}, {-72, 4837, 25980, 2015}, {-67, 4673, 26035, 2120}, {-63, 4512, 26085, 2227},
{-58, 4354, 26130, 2338}, {-54, 4199, 26169, 2451}, {-50, 4046, 26202, 2568}, {-46, 3897, 26230, 2688},
{-42, 3751, 26253, 2811}, {-38, 3608, 26270, 2936}, {-34, 3467, 26281, 3064}, {-32, 3329, 26287, 3195}}};
// @fmt:on
std::vector<i16> Resampler::ResampleBuffer(span<i16> inputBuffer, double ratio, u8 channelCount) {
auto step{static_cast<u32>(ratio * 0x8000)};
auto outputSize{static_cast<size_t>(inputBuffer.size() / ratio)};
std::vector<i16> outputBuffer(outputSize);
const std::array<LutEntry, 128> &lut = [step] {
if (step > 0xAAAA)
return CurveLut0;
else if (step <= 0x8000)
return CurveLut1;
else
return CurveLut2;
}();
for (size_t outIndex{}, inIndex{}; outIndex < outputSize; outIndex += channelCount) {
u32 lutIndex{fraction >> 8};
for (u8 channel{}; channel < channelCount; channel++) {
i32 data{inputBuffer[(inIndex + 0) * channelCount + channel] * lut[lutIndex].a +
inputBuffer[(inIndex + 1) * channelCount + channel] * lut[lutIndex].b +
inputBuffer[(inIndex + 2) * channelCount + channel] * lut[lutIndex].c +
inputBuffer[(inIndex + 3) * channelCount + channel] * lut[lutIndex].d};
outputBuffer[outIndex + channel] = Saturate<i16, i32>(data >> 15);
}
u32 newOffset{fraction + step};
inIndex += newOffset >> 15;
fraction = newOffset & 0x7FFF;
}
return outputBuffer;
}
}

View file

@ -0,0 +1,25 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#pragma once
#include <common.h>
namespace skyline::audio {
/**
* @brief The Resampler class handles resampling audio PCM data
*/
class Resampler {
private:
u32 fraction{}; //!< The fractional value used for storing the resamplers last frame
public:
/**
* @brief Resamples the given sample buffer by the given ratio
* @param inputBuffer A buffer containing PCM sample data
* @param ratio The conversion ratio needed
* @param channelCount The amount of channels the buffer contains
*/
std::vector<i16> ResampleBuffer(span <i16> inputBuffer, double ratio, u8 channelCount);
};
}

View file

@ -0,0 +1,73 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include "track.h"
namespace skyline::audio {
AudioTrack::AudioTrack(u8 channelCount, u32 sampleRate, std::function<void()> releaseCallback) : channelCount(channelCount), sampleRate(sampleRate), releaseCallback(std::move(releaseCallback)) {
if (sampleRate != constant::SampleRate)
throw exception("Unsupported audio sample rate: {}", sampleRate);
if (channelCount != constant::ChannelCount)
throw exception("Unsupported quantity of audio channels: {}", channelCount);
}
void AudioTrack::Stop() {
while (!identifiers.end()->released);
playbackState = AudioOutState::Stopped;
}
bool AudioTrack::ContainsBuffer(u64 tag) {
// Iterate from front of queue as we don't want released samples
for (auto identifier{identifiers.crbegin()}; identifier != identifiers.crend(); identifier++) {
if (identifier->released)
return false;
if (identifier->tag == tag)
return true;
}
return false;
}
std::vector<u64> AudioTrack::GetReleasedBuffers(u32 max) {
std::vector<u64> bufferIds;
std::lock_guard trackGuard(bufferLock);
for (u32 index{}; index < max; index++) {
if (identifiers.empty() || !identifiers.back().released)
break;
bufferIds.push_back(identifiers.back().tag);
identifiers.pop_back();
}
return bufferIds;
}
void AudioTrack::AppendBuffer(u64 tag, span<i16> buffer) {
BufferIdentifier identifier{
.released = false,
.tag = tag,
.finalSample = identifiers.empty() ? (buffer.size()) : (buffer.size() + identifiers.front().finalSample)
};
std::lock_guard guard(bufferLock);
identifiers.push_front(identifier);
samples.Append(buffer);
}
void AudioTrack::CheckReleasedBuffers() {
bool anyReleased{};
for (auto &identifier : identifiers) {
if (identifier.finalSample <= sampleCounter && !identifier.released) {
anyReleased = true;
identifier.released = true;
}
}
if (anyReleased)
releaseCallback();
}
}

View file

@ -0,0 +1,75 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#pragma once
#include <kernel/types/KEvent.h>
#include <common/circular_buffer.h>
#include "common.h"
namespace skyline::audio {
/**
* @brief The AudioTrack class manages the buffers for an audio stream
*/
class AudioTrack {
private:
std::function<void()> releaseCallback; //!< Callback called when a buffer has been played
std::deque<BufferIdentifier> identifiers; //!< Queue of all appended buffer identifiers
u8 channelCount;
u32 sampleRate;
public:
CircularBuffer<i16, constant::SampleRate * constant::ChannelCount * 10> samples; //!< A circular buffer with all appended audio samples
std::mutex bufferLock; //!< Synchronizes appending to audio buffers
AudioOutState playbackState{AudioOutState::Stopped}; //!< The current state of playback
u64 sampleCounter{}; //!< A counter used for tracking when buffers have been played and can be released
/**
* @param channelCount The amount channels that will be present in the track
* @param sampleRate The sample rate to use for the track
* @param releaseCallback A callback to call when a buffer has been played
*/
AudioTrack(u8 channelCount, u32 sampleRate, std::function<void()> releaseCallback);
/**
* @brief Starts audio playback using data from appended buffers
*/
void Start() {
playbackState = AudioOutState::Started;
}
/**
* @brief Stops audio playback, this waits for audio playback to finish before returning
*/
void Stop();
/**
* @brief Checks if a buffer has been released
* @param tag The tag of the buffer to check
* @return True if the given buffer hasn't been released
*/
bool ContainsBuffer(u64 tag);
/**
* @brief Gets the IDs of all newly released buffers
* @param max The maximum amount of buffers to return
* @return A vector containing the identifiers of the buffers
*/
std::vector<u64> GetReleasedBuffers(u32 max);
/**
* @brief Appends audio samples to the output buffer
* @param tag The tag of the buffer
* @param buffer A span containing the source sample buffer
*/
void AppendBuffer(u64 tag, span<i16> buffer = {});
/**
* @brief Checks if any buffers have been released and calls the appropriate callback for them
* @note bufferLock MUST be locked when calling this
*/
void CheckReleasedBuffers();
};
}

View file

@ -1,28 +1,64 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <android/log.h>
#include "common.h"
#include "nce.h"
#include "soc.h"
#include "gpu.h"
#include "audio.h"
#include "input.h"
#include "kernel/types/KProcess.h"
#include "kernel/types/KThread.h"
namespace skyline {
DeviceState::DeviceState(kernel::OS *os, std::shared_ptr<JvmManager> jvmManager, std::shared_ptr<Settings> settings)
: os(os), jvm(std::move(jvmManager)), settings(std::move(settings)) {
Logger::Logger(const std::string &path, LogLevel configLevel) : configLevel(configLevel) {
logFile.open(path, std::ios::trunc);
UpdateTag();
WriteHeader("Logging started");
}
Logger::~Logger() {
WriteHeader("Logging ended");
logFile.flush();
}
thread_local static std::string logTag, threadName;
void Logger::UpdateTag() {
std::array<char, 16> name;
if (!pthread_getname_np(pthread_self(), name.data(), name.size()))
threadName = name.data();
else
threadName = "unk";
logTag = std::string("emu-cpp-") + threadName;
}
void Logger::WriteHeader(const std::string &str) {
__android_log_write(ANDROID_LOG_INFO, "emu-cpp", str.c_str());
std::lock_guard guard(mutex);
logFile << "\0360\035" << str << '\n';
}
void Logger::Write(LogLevel level, const std::string &str) {
constexpr std::array<char, 5> levelCharacter{'E', 'W', 'I', 'D', 'V'}; // The LogLevel as written out to a file
constexpr std::array<int, 5> levelAlog{ANDROID_LOG_ERROR, ANDROID_LOG_WARN, ANDROID_LOG_INFO, ANDROID_LOG_DEBUG, ANDROID_LOG_VERBOSE}; // This corresponds to LogLevel and provides its equivalent for NDK Logging
if (logTag.empty())
UpdateTag();
__android_log_write(levelAlog[static_cast<u8>(level)], logTag.c_str(), str.c_str());
std::lock_guard guard(mutex);
logFile << "\0361\035" << levelCharacter[static_cast<u8>(level)] << '\035' << threadName << '\035' << str << '\n'; // We use RS (\036) and GS (\035) as our delimiters
}
DeviceState::DeviceState(kernel::OS *os, std::shared_ptr<JvmManager> jvmManager, std::shared_ptr<Settings> settings, std::shared_ptr<Logger> logger)
: os(os), jvm(std::move(jvmManager)), settings(std::move(settings)), logger(std::move(logger)) {
// We assign these later as they use the state in their constructor and we don't want null pointers
gpu = std::make_shared<gpu::GPU>(*this);
soc = std::make_shared<soc::SOC>(*this);
audio = std::make_shared<audio::Audio>(*this);
nce = std::make_shared<nce::NCE>(*this);
scheduler = std::make_shared<kernel::Scheduler>(*this);
input = std::make_shared<input::Input>(*this);
}
DeviceState::~DeviceState() {
if (process)
process->ClearHandleTable();
}
}

View file

@ -3,26 +3,602 @@
#pragma once
#include <bit>
#include <map>
#include <unordered_map>
#include <span>
#include <list>
#include <vector>
#include <span>
#include <fstream>
#include <mutex>
#include <shared_mutex>
#include <functional>
#include <thread>
#include <string>
#include <cstdint>
#include <stdexcept>
#include <string>
#include <sstream>
#include <memory>
#include <compare>
#include <condition_variable>
#include <boost/container/small_vector.hpp>
#include <common/exception.h>
#include <common/span.h>
#include <common/result.h>
#include <common/logger.h>
#include <sys/mman.h>
#include <fmt/format.h>
#include <frozen/unordered_map.h>
#include <frozen/string.h>
#include <jni.h>
#define FORCE_INLINE __attribute__((always_inline)) inline // NOLINT(cppcoreguidelines-macro-usage)
namespace fmt {
/**
* @brief A std::bitset formatter for {fmt}
*/
template<size_t N>
struct formatter<std::bitset<N>> : formatter<std::string> {
template<typename FormatContext>
constexpr auto format(const std::bitset<N> &s, FormatContext &ctx) {
return formatter<std::string>::format(s.to_string(), ctx);
}
};
}
namespace skyline {
using u128 = __uint128_t; //!< Unsigned 128-bit integer
using u64 = __uint64_t; //!< Unsigned 64-bit integer
using u32 = __uint32_t; //!< Unsigned 32-bit integer
using u16 = __uint16_t; //!< Unsigned 16-bit integer
using u8 = __uint8_t; //!< Unsigned 8-bit integer
using i128 = __int128_t; //!< Signed 128-bit integer
using i64 = __int64_t; //!< Signed 64-bit integer
using i32 = __int32_t; //!< Signed 32-bit integer
using i16 = __int16_t; //!< Signed 16-bit integer
using i8 = __int8_t; //!< Signed 8-bit integer
using KHandle = u32; //!< The type of a kernel handle
namespace frz = frozen;
/**
* @brief The result of an operation in HOS
* @url https://switchbrew.org/wiki/Error_codes
*/
union Result {
u32 raw{};
struct __attribute__((packed)) {
u16 module : 9;
u16 id : 12;
};
/**
* @note Success is 0, it's the only result that's not specific to a module
*/
constexpr Result() = default;
constexpr explicit Result(u16 module, u16 id) : module(module), id(id) {}
constexpr operator u32() const {
return raw;
}
};
/**
* @brief A wrapper around std::optional that also stores a HOS result code
* @tparam T The object type to hold
*/
template<typename T>
class ResultValue {
static_assert(!std::is_same<T, Result>::value);
private:
std::optional<T> value;
public:
Result result;
constexpr ResultValue(T value) : value(value) {};
constexpr ResultValue(Result result) : result(result) {};
template<typename U>
constexpr ResultValue(ResultValue<U> result) : result(result) {};
constexpr operator Result() const {
return result;
}
explicit constexpr operator bool() const {
return value.has_value();
}
constexpr T& operator*() {
return *value;
}
constexpr T* operator->() {
return &*value;
}
};
namespace constant {
// Display
constexpr u16 HandheldResolutionW{1280}; //!< The width component of the handheld resolution
constexpr u16 HandheldResolutionH{720}; //!< The height component of the handheld resolution
constexpr u16 DockedResolutionW{1920}; //!< The width component of the docked resolution
constexpr u16 DockedResolutionH{1080}; //!< The height component of the docked resolution
// Time
constexpr u64 NsInSecond{1000000000}; //!< The amount of nanoseconds in a second
constexpr u64 NsInDay{86400000000000UL}; //!< The amount of nanoseconds in a day
}
namespace util {
/**
* @brief A way to implicitly cast all pointers to uintptr_t, this is used for {fmt} as we use 0x{:X} to print pointers
* @note There's the exception of signed char pointers as they represent C Strings
* @note This does not cover std::shared_ptr or std::unique_ptr and those will have to be explicitly casted to uintptr_t or passed through fmt::ptr
*/
template<typename T>
constexpr auto FmtCast(T object) {
if constexpr (std::is_pointer<T>::value)
if constexpr (std::is_same<char, typename std::remove_cv<typename std::remove_pointer<T>::type>::type>::value)
return reinterpret_cast<typename std::common_type<char *, T>::type>(object);
else
return reinterpret_cast<const uintptr_t>(object);
else
return object;
}
/**
* @brief {fmt}::format but with FmtCast built into it
*/
template<typename S, typename... Args>
auto Format(S formatString, Args &&... args) {
return fmt::format(formatString, FmtCast(args)...);
}
}
/**
* @brief A wrapper over std::runtime_error with {fmt} formatting
*/
class exception : public std::runtime_error {
public:
template<typename S, typename... Args>
exception(const S &formatStr, Args &&... args) : runtime_error(fmt::format(formatStr, util::FmtCast(args)...)) {}
};
namespace util {
/**
* @brief Returns the current time in nanoseconds
* @return The current time in nanoseconds
*/
inline u64 GetTimeNs() {
u64 frequency;
asm("MRS %0, CNTFRQ_EL0" : "=r"(frequency));
u64 ticks;
asm("MRS %0, CNTVCT_EL0" : "=r"(ticks));
return ((ticks / frequency) * constant::NsInSecond) + (((ticks % frequency) * constant::NsInSecond + (frequency / 2)) / frequency);
}
/**
* @brief Returns the current time in arbitrary ticks
* @return The current time in ticks
*/
inline u64 GetTimeTicks() {
u64 ticks;
asm("MRS %0, CNTVCT_EL0" : "=r"(ticks));
return ticks;
}
/**
* @brief A way to implicitly convert a pointer to uintptr_t and leave it unaffected if it isn't a pointer
*/
template<typename T>
T PointerValue(T item) {
return item;
}
template<typename T>
uintptr_t PointerValue(T *item) {
return reinterpret_cast<uintptr_t>(item);
}
/**
* @brief A way to implicitly convert an integral to a pointer, if the return type is a pointer
*/
template<typename Return, typename T>
Return ValuePointer(T item) {
if constexpr (std::is_pointer<Return>::value)
return reinterpret_cast<Return>(item);
else
return item;
}
/**
* @return The value aligned up to the next multiple
* @note The multiple needs to be a power of 2
*/
template<typename TypeVal, typename TypeMul>
constexpr TypeVal AlignUp(TypeVal value, TypeMul multiple) {
multiple--;
return ValuePointer<TypeVal>((PointerValue(value) + multiple) & ~(multiple));
}
/**
* @return The value aligned down to the previous multiple
* @note The multiple needs to be a power of 2
*/
template<typename TypeVal, typename TypeMul>
constexpr TypeVal AlignDown(TypeVal value, TypeMul multiple) {
return ValuePointer<TypeVal>(PointerValue(value) & ~(multiple - 1));
}
/**
* @return If the address is aligned with the multiple
*/
template<typename TypeVal, typename TypeMul>
constexpr bool IsAligned(TypeVal value, TypeMul multiple) {
if ((multiple & (multiple - 1)) == 0)
return !(PointerValue(value) & (multiple - 1U));
else
return (PointerValue(value) % multiple) == 0;
}
/**
* @return If the value is page aligned
*/
template<typename TypeVal>
constexpr bool PageAligned(TypeVal value) {
return IsAligned(value, PAGE_SIZE);
}
/**
* @return If the value is word aligned
*/
template<typename TypeVal>
constexpr bool WordAligned(TypeVal value) {
return IsAligned(value, WORD_BIT / 8);
}
/**
* @param string The string to create a magic from
* @return The magic of the supplied string
*/
template<typename Type>
constexpr Type MakeMagic(std::string_view string) {
Type object{};
size_t offset{};
for (auto &character : string) {
object |= static_cast<Type>(character) << offset;
offset += sizeof(character) * 8;
}
return object;
}
constexpr u8 HexDigitToNibble(char digit) {
if (digit >= '0' && digit <= '9')
return digit - '0';
else if (digit >= 'a' && digit <= 'f')
return digit - 'a' + 10;
else if (digit >= 'A' && digit <= 'F')
return digit - 'A' + 10;
throw exception("Invalid hex character: '{}'", digit);
}
template<size_t Size>
constexpr std::array<u8, Size> HexStringToArray(std::string_view string) {
if (string.size() != Size * 2)
throw exception("String size: {} (Expected {})", string.size(), Size);
std::array<u8, Size> result;
for (size_t i{}; i < Size; i++) {
size_t index{i * 2};
result[i] = (HexDigitToNibble(string[index]) << 4) | HexDigitToNibble(string[index + 1]);
}
return result;
}
template<typename Type>
constexpr Type HexStringToInt(std::string_view string) {
if (string.size() > sizeof(Type) * 2)
throw exception("String size larger than type: {} (sizeof(Type): {})", string.size(), sizeof(Type));
Type result{};
size_t offset{(sizeof(Type) * 8) - 4};
for (size_t index{}; index < string.size(); index++, offset -= 4) {
char digit{string[index]};
if (digit >= '0' && digit <= '9')
result |= static_cast<Type>(digit - '0') << offset;
else if (digit >= 'a' && digit <= 'f')
result |= static_cast<Type>(digit - 'a' + 10) << offset;
else if (digit >= 'A' && digit <= 'F')
result |= static_cast<Type>(digit - 'A' + 10) << offset;
else
break;
}
return result >> (offset + 4);
}
template<size_t N>
constexpr std::array<u8, N> SwapEndianness(std::array<u8, N> in) {
std::reverse(in.begin(), in.end());
return in;
}
constexpr u64 SwapEndianness(u64 in) {
return __builtin_bswap64(in);
}
constexpr u32 SwapEndianness(u32 in) {
return __builtin_bswap32(in);
}
constexpr u16 SwapEndianness(u16 in) {
return __builtin_bswap16(in);
}
/**
* @brief A compile-time hash function as std::hash isn't constexpr
*/
constexpr std::size_t Hash(std::string_view view) {
return frz::elsa<frz::string>{}(frz::string(view.data(), view.size()), 0);
}
}
/**
* @brief A custom wrapper over span that adds several useful methods to it
* @note This class is completely transparent, it implicitly converts from and to span
*/
template<typename T, size_t Extent = std::dynamic_extent>
class span : public std::span<T, Extent> {
public:
using std::span<T, Extent>::span;
using std::span<T, Extent>::operator=;
typedef typename std::span<T, Extent>::element_type element_type;
typedef typename std::span<T, Extent>::size_type size_type;
constexpr span(const std::span<T, Extent> &spn) : std::span<T, Extent>(spn) {}
/**
* @brief We want to support implicitly casting from std::string_view -> span as it's just a specialization of a data view which span is a generic form of, the opposite doesn't hold true as not all data held by a span is string data therefore the conversion isn't implicit there
*/
template<typename Traits>
constexpr span(const std::basic_string_view<T, Traits> &string) : std::span<T, Extent>(const_cast<T *>(string.data()), string.size()) {}
template<typename Out>
constexpr Out &as() {
if (span::size_bytes() >= sizeof(Out))
return *reinterpret_cast<Out *>(span::data());
throw exception("Span size is less than Out type size (0x{:X}/0x{:X})", span::size_bytes(), sizeof(Out));
}
/**
* @param nullTerminated If true and the string is null-terminated, a view of it will be returned (not including the null terminator itself), otherwise the entire span will be returned as a string view
*/
constexpr std::string_view as_string(bool nullTerminated = false) {
return std::string_view(reinterpret_cast<char *>(span::data()), nullTerminated ? (std::find(span::begin(), span::end(), 0) - span::begin()) : span::size_bytes());
}
template<typename Out, size_t OutExtent = std::dynamic_extent>
constexpr span<Out> cast() {
if (util::IsAligned(span::size_bytes(), sizeof(Out)))
return span<Out, OutExtent>(reinterpret_cast<Out *>(span::data()), span::size_bytes() / sizeof(Out));
throw exception("Span size not aligned with Out type size (0x{:X}/0x{:X})", span::size_bytes(), sizeof(Out));
}
/**
* @brief Copies data from the supplied span into this one
* @param amount The amount of elements that need to be copied (in terms of the supplied span), 0 will try to copy the entirety of the other span
*/
template<typename In, size_t InExtent>
constexpr void copy_from(const span<In, InExtent> spn, size_type amount = 0) {
auto size{amount ? amount * sizeof(In) : spn.size_bytes()};
if (span::size_bytes() < size)
throw exception("Data being copied is larger than this span");
std::memmove(span::data(), spn.data(), size);
}
/**
* @brief Implicit type conversion for copy_from, this allows passing in std::vector/std::array in directly is automatically passed by reference which is important for any containers
*/
template<typename In>
constexpr void copy_from(const In &in, size_type amount = 0) {
copy_from(span<typename std::add_const<typename In::value_type>::type>(in), amount);
}
/** Base Class Functions that return an instance of it, we upcast them **/
template<size_t Count>
constexpr span<T, Count> first() const noexcept {
return std::span<T, Extent>::template first<Count>();
}
template<size_t Count>
constexpr span<T, Count> last() const noexcept {
return std::span<T, Extent>::template last<Count>();
}
constexpr span<element_type, std::dynamic_extent> first(size_type count) const noexcept {
return std::span<T, Extent>::first(count);
}
constexpr span<element_type, std::dynamic_extent> last(size_type count) const noexcept {
return std::span<T, Extent>::last(count);
}
template<size_t Offset, size_t Count = std::dynamic_extent>
constexpr auto subspan() const noexcept -> span<T, Count != std::dynamic_extent ? Count : Extent - Offset> {
return std::span<T, Extent>::template subspan<Offset, Count>();
}
constexpr span<T, std::dynamic_extent> subspan(size_type offset, size_type count = std::dynamic_extent) const noexcept {
return std::span<T, Extent>::subspan(offset, count);
}
};
/**
* @brief Deduction guides required for arguments to span, CTAD will fail for iterators, arrays and containers without this
*/
template<typename It, typename End, size_t Extent = std::dynamic_extent>
span(It, End) -> span<typename std::iterator_traits<It>::value_type, Extent>;
template<typename T, size_t Size>
span(T (&)[Size]) -> span<T, Size>;
template<typename T, size_t Size>
span(std::array<T, Size> &) -> span<T, Size>;
template<typename T, size_t Size>
span(const std::array<T, Size> &) -> span<const T, Size>;
template<typename Container>
span(Container &) -> span<typename Container::value_type>;
template<typename Container>
span(const Container &) -> span<const typename Container::value_type>;
/**
* @brief A wrapper around writing logs into a log file and logcat using Android Log APIs
*/
class Logger {
private:
std::ofstream logFile; //!< An output stream to the log file
std::mutex mutex; //!< Synchronizes all output I/O to ensure there are no races
public:
enum class LogLevel {
Error,
Warn,
Info,
Debug,
Verbose,
};
LogLevel configLevel; //!< The minimum level of logs to write
/**
* @param path The path of the log file
* @param configLevel The minimum level of logs to write
*/
Logger(const std::string &path, LogLevel configLevel);
/**
* @brief Writes the termination message to the log file
*/
~Logger();
/**
* @brief Update the tag in log messages with a new thread name
*/
static void UpdateTag();
/**
* @brief Writes a header, should only be used for emulation starting and ending
*/
void WriteHeader(const std::string &str);
void Write(LogLevel level, const std::string &str);
/**
* @brief A wrapper around a string which captures the calling function using Clang source location builtins
* @note A function needs to be declared for every argument template specialization as CTAD cannot work with implicit casting
* @url https://clang.llvm.org/docs/LanguageExtensions.html#source-location-builtins
*/
template<typename S>
struct FunctionString {
S string;
const char *function;
FunctionString(S string, const char *function = __builtin_FUNCTION()) : string(std::move(string)), function(function) {}
std::string operator*() {
return std::string(function) + ": " + std::string(string);
}
};
template<typename... Args>
void Error(FunctionString<const char*> formatString, Args &&... args) {
if (LogLevel::Error <= configLevel)
Write(LogLevel::Error, fmt::format(*formatString, util::FmtCast(args)...));
}
template<typename... Args>
void Error(FunctionString<std::string> formatString, Args &&... args) {
if (LogLevel::Error <= configLevel)
Write(LogLevel::Error, fmt::format(*formatString, util::FmtCast(args)...));
}
template<typename S, typename... Args>
void ErrorNoPrefix(S formatString, Args &&... args) {
if (LogLevel::Error <= configLevel)
Write(LogLevel::Error, fmt::format(formatString, util::FmtCast(args)...));
}
template<typename... Args>
void Warn(FunctionString<const char*> formatString, Args &&... args) {
if (LogLevel::Warn <= configLevel)
Write(LogLevel::Warn, fmt::format(*formatString, util::FmtCast(args)...));
}
template<typename... Args>
void Warn(FunctionString<std::string> formatString, Args &&... args) {
if (LogLevel::Warn <= configLevel)
Write(LogLevel::Warn, fmt::format(*formatString, util::FmtCast(args)...));
}
template<typename S, typename... Args>
void WarnNoPrefix(S formatString, Args &&... args) {
if (LogLevel::Warn <= configLevel)
Write(LogLevel::Warn, fmt::format(formatString, util::FmtCast(args)...));
}
template<typename... Args>
void Info(FunctionString<const char*> formatString, Args &&... args) {
if (LogLevel::Info <= configLevel)
Write(LogLevel::Info, fmt::format(*formatString, util::FmtCast(args)...));
}
template<typename... Args>
void Info(FunctionString<std::string> formatString, Args &&... args) {
if (LogLevel::Info <= configLevel)
Write(LogLevel::Info, fmt::format(*formatString, util::FmtCast(args)...));
}
template<typename S, typename... Args>
void InfoNoPrefix(S formatString, Args &&... args) {
if (LogLevel::Info <= configLevel)
Write(LogLevel::Info, fmt::format(formatString, util::FmtCast(args)...));
}
template<typename... Args>
void Debug(FunctionString<const char*> formatString, Args &&... args) {
if (LogLevel::Debug <= configLevel)
Write(LogLevel::Debug, fmt::format(*formatString, util::FmtCast(args)...));
}
template<typename... Args>
void Debug(FunctionString<std::string> formatString, Args &&... args) {
if (LogLevel::Debug <= configLevel)
Write(LogLevel::Debug, fmt::format(*formatString, util::FmtCast(args)...));
}
template<typename S, typename... Args>
void DebugNoPrefix(S formatString, Args &&... args) {
if (LogLevel::Debug <= configLevel)
Write(LogLevel::Debug, fmt::format(formatString, util::FmtCast(args)...));
}
template<typename... Args>
void Verbose(FunctionString<const char*> formatString, Args &&... args) {
if (LogLevel::Verbose <= configLevel)
Write(LogLevel::Verbose, fmt::format(*formatString, util::FmtCast(args)...));
}
template<typename... Args>
void Verbose(FunctionString<std::string> formatString, Args &&... args) {
if (LogLevel::Verbose <= configLevel)
Write(LogLevel::Verbose, fmt::format(*formatString, util::FmtCast(args)...));
}
template<typename S, typename... Args>
void VerboseNoPrefix(S formatString, Args &&... args) {
if (LogLevel::Verbose <= configLevel)
Write(LogLevel::Verbose, fmt::format(formatString, util::FmtCast(args)...));
}
};
class Settings;
namespace nce {
class NCE;
@ -32,9 +608,6 @@ namespace skyline {
namespace gpu {
class GPU;
}
namespace soc {
class SOC;
}
namespace kernel {
namespace type {
class KProcess;
@ -57,22 +630,20 @@ namespace skyline {
* @brief The state of the entire emulator is contained within this class, all objects related to emulation are tied into it
*/
struct DeviceState {
DeviceState(kernel::OS *os, std::shared_ptr<JvmManager> jvmManager, std::shared_ptr<Settings> settings);
~DeviceState();
DeviceState(kernel::OS *os, std::shared_ptr<JvmManager> jvmManager, std::shared_ptr<Settings> settings, std::shared_ptr<Logger> logger);
kernel::OS *os;
std::shared_ptr<JvmManager> jvm;
std::shared_ptr<Settings> settings;
std::shared_ptr<Logger> logger;
std::shared_ptr<loader::Loader> loader;
std::shared_ptr<gpu::GPU> gpu;
std::shared_ptr<audio::Audio> audio;
std::shared_ptr<nce::NCE> nce;
std::shared_ptr<kernel::type::KProcess> process{};
std::shared_ptr<kernel::Scheduler> scheduler;
std::shared_ptr<kernel::type::KProcess> process;
static thread_local inline std::shared_ptr<kernel::type::KThread> thread{}; //!< The KThread of the thread which accesses this object
static thread_local inline nce::ThreadContext *ctx{}; //!< The context of the guest thread for the corresponding host thread
std::shared_ptr<gpu::GPU> gpu;
std::shared_ptr<soc::SOC> soc;
std::shared_ptr<audio::Audio> audio;
std::shared_ptr<kernel::Scheduler> scheduler;
std::shared_ptr<input::Input> input;
};
}

View file

@ -1,293 +0,0 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/)
#pragma once
#include <boost/container/small_vector.hpp>
#include <concepts>
#include <common.h>
#include "segment_table.h"
#include "spin_lock.h"
namespace skyline {
template<typename VaType, size_t AddressSpaceBits>
concept AddressSpaceValid = std::is_unsigned_v<VaType> && sizeof(VaType) * 8 >= AddressSpaceBits;
using TranslatedAddressRange = boost::container::small_vector<span<u8>, 1>;
struct EmptyStruct {};
/**
* @brief FlatAddressSpaceMap provides a generic VA->PA mapping implementation using a sorted vector
*/
template<typename VaType, VaType UnmappedVa, typename PaType, PaType UnmappedPa, bool PaContigSplit, size_t AddressSpaceBits, typename ExtraBlockInfo = EmptyStruct> requires AddressSpaceValid<VaType, AddressSpaceBits>
class FlatAddressSpaceMap {
private:
std::function<void(VaType, VaType)> unmapCallback{}; //!< Callback called when the mappings in an region have changed
protected:
/**
* @brief Represents a block of memory in the AS, the physical mapping is contiguous until another block with a different phys address is hit
*/
struct Block {
VaType virt{UnmappedVa}; //!< VA of the block
PaType phys{UnmappedPa}; //!< PA of the block, will increase 1-1 with VA until a new block is encountered
[[no_unique_address]] ExtraBlockInfo extraInfo;
Block() = default;
Block(VaType virt, PaType phys, ExtraBlockInfo extraInfo) : virt(virt), phys(phys), extraInfo(extraInfo) {}
constexpr bool Valid() {
return virt != UnmappedVa;
}
constexpr bool Mapped() {
return phys != UnmappedPa;
}
constexpr bool Unmapped() {
return phys == UnmappedPa;
}
bool operator<(const VaType &pVirt) const {
return virt < pVirt;
}
};
SharedSpinLock blockMutex;
std::vector<Block> blocks{Block{}};
/**
* @brief Maps a PA range into the given AS region
* @note blockMutex MUST be locked when calling this
*/
void MapLocked(VaType virt, PaType phys, VaType size, ExtraBlockInfo extraInfo);
/**
* @brief Unmaps the given range and merges it with other unmapped regions
* @note blockMutex MUST be locked when calling this
*/
void UnmapLocked(VaType virt, VaType size);
public:
static constexpr VaType VaMaximum{(1ULL << (AddressSpaceBits - 1)) + ((1ULL << (AddressSpaceBits - 1)) - 1)}; //!< The maximum VA that this AS can technically reach
VaType vaLimit{VaMaximum}; //!< A soft limit on the maximum VA of the AS
FlatAddressSpaceMap(VaType vaLimit, std::function<void(VaType, VaType)> unmapCallback = {});
FlatAddressSpaceMap() = default;
};
/**
* @brief Hold memory manager specific block info
*/
struct MemoryManagerBlockInfo {
bool sparseMapped;
};
/**
* @brief FlatMemoryManager specialises FlatAddressSpaceMap to focus on pointers as PAs, adding read/write functions and sparse mapping support
*/
template<typename VaType, VaType UnmappedVa, size_t AddressSpaceBits, size_t VaGranularityBits, size_t VaL2GranularityBits> requires AddressSpaceValid<VaType, AddressSpaceBits>
class FlatMemoryManager : public FlatAddressSpaceMap<VaType, UnmappedVa, u8 *, nullptr, true, AddressSpaceBits, MemoryManagerBlockInfo> {
private:
static constexpr u64 SparseMapSize{0x400000000}; //!< 16GiB pool size for sparse mappings returned by TranslateRange, this number is arbritary and should be large enough to fit the largest sparse mapping in the AS
u8 *sparseMap; //!< Pointer to a zero filled memory region that is returned by TranslateRange for sparse mappings
/**
* @brief Version of `Block` that is trivial so it can be stored in a segment table for rapid lookups, also holds an additional extent member
*/
struct SegmentTableEntry {
VaType virt;
u8 *phys;
VaType extent;
MemoryManagerBlockInfo extraInfo;
};
static constexpr size_t AddressSpaceSize{1ULL << AddressSpaceBits};
SegmentTable<SegmentTableEntry, AddressSpaceSize, VaGranularityBits, VaL2GranularityBits> blockSegmentTable; //!< A page table of all buffer mappings for O(1) lookups on full matches
TranslatedAddressRange TranslateRangeImpl(VaType virt, VaType size, std::function<void(span<u8>)> cpuAccessCallback = {});
std::pair<span<u8>, size_t> LookupBlockLocked(VaType virt, std::function<void(span<u8>)> cpuAccessCallback = {}) {
const auto &blockEntry{this->blockSegmentTable[virt]};
VaType segmentOffset{virt - blockEntry.virt};
if (blockEntry.extraInfo.sparseMapped || blockEntry.phys == nullptr)
return {span<u8>{static_cast<u8*>(nullptr), blockEntry.extent}, segmentOffset};
span<u8> blockSpan{blockEntry.phys, blockEntry.extent};
if (cpuAccessCallback)
cpuAccessCallback(blockSpan);
return {blockSpan, segmentOffset};
}
public:
FlatMemoryManager();
~FlatMemoryManager();
/**
* @return A placeholder address for sparse mapped regions, this means nothing
*/
static u8 *SparsePlaceholderAddress() {
return reinterpret_cast<u8 *>(0xCAFEBABE);
}
/**
* @brief Looks up the mapped region that contains the given VA
* @return A span of the mapped region and the offset of the input VA in the region
*/
__attribute__((always_inline)) std::pair<span<u8>, VaType> LookupBlock(VaType virt, std::function<void(span<u8>)> cpuAccessCallback = {}) {
std::shared_lock lock{this->blockMutex};
return LookupBlockLocked(virt, cpuAccessCallback);
}
/**
* @brief Translates a region in the VA space to a corresponding set of regions in the PA space
*/
TranslatedAddressRange TranslateRange(VaType virt, VaType size, std::function<void(span<u8>)> cpuAccessCallback = {}) {
std::shared_lock lock{this->blockMutex};
// Fast path for when the range is mapped in a single block
auto [blockSpan, rangeOffset]{LookupBlockLocked(virt, cpuAccessCallback)};
if (blockSpan.size() - rangeOffset >= size) {
TranslatedAddressRange ranges;
ranges.push_back(blockSpan.subspan(blockSpan.valid() ? rangeOffset : 0, size));
return ranges;
}
return TranslateRangeImpl(virt, size, cpuAccessCallback);
}
void Read(u8 *destination, VaType virt, VaType size, std::function<void(span<u8>)> cpuAccessCallback = {});
template<typename T>
void Read(span <T> destination, VaType virt, std::function<void(span<u8>)> cpuAccessCallback = {}) {
Read(reinterpret_cast<u8 *>(destination.data()), virt, destination.size_bytes(), cpuAccessCallback);
}
template<typename T>
T Read(VaType virt, std::function<void(span<u8>)> cpuAccessCallback = {}) {
T obj;
Read(reinterpret_cast<u8 *>(&obj), virt, sizeof(T), cpuAccessCallback);
return obj;
}
/**
* @brief Writes contents starting from the virtual address till the end of the span or an unmapped block has been hit or when `function` returns a non-nullopt value
* @param function A function that is called on every block where it should return an end offset into the block when it wants to end reading or std::nullopt when it wants to continue reading
* @return A span into the supplied container with the contents of the memory region
* @note The function will **NOT** be run on any sparse block
* @note The function will provide no feedback on if the end has been reached or if there was an early exit
*/
template<typename Function, typename Container>
span<u8> ReadTill(Container& destination, VaType virt, Function function, std::function<void(span<u8>)> cpuAccessCallback = {}) {
//TRACE_EVENT("containers", "FlatMemoryManager::ReadTill");
std::shared_lock lock(this->blockMutex);
auto successor{std::upper_bound(this->blocks.begin(), this->blocks.end(), virt, [](auto virt, const auto &block) {
return virt < block.virt;
})};
auto predecessor{std::prev(successor)};
auto pointer{destination.data()};
auto remainingSize{destination.size()};
u8 *blockPhys{predecessor->phys + (virt - predecessor->virt)};
VaType blockReadSize{std::min(successor->virt - virt, remainingSize)};
while (remainingSize) {
if (predecessor->phys == nullptr) {
return {destination.data(), destination.size() - remainingSize};
} else {
if (predecessor->extraInfo.sparseMapped) {
std::memset(pointer, 0, blockReadSize);
} else {
span<u8> cpuBlock{blockPhys, blockReadSize};
if (cpuAccessCallback)
cpuAccessCallback(cpuBlock);
auto end{function(cpuBlock)};
std::memcpy(pointer, blockPhys, end ? *end : blockReadSize);
if (end)
return {destination.data(), (destination.size() - remainingSize) + *end};
}
}
pointer += blockReadSize;
remainingSize -= blockReadSize;
if (remainingSize) {
predecessor = successor++;
blockPhys = predecessor->phys;
blockReadSize = std::min(successor->virt - predecessor->virt, remainingSize);
}
}
return {destination.data(), destination.size()};
}
void Write(VaType virt, u8 *source, VaType size, std::function<void(span<u8>)> cpuAccessCallback = {});
template<typename T>
void Write(VaType virt, span<T> source, std::function<void(span<u8>)> cpuAccessCallback = {}) {
Write(virt, reinterpret_cast<u8 *>(source.data()), source.size_bytes());
}
void Write(VaType virt, util::TrivialObject auto source, std::function<void(span<u8>)> cpuAccessCallback = {}) {
Write(virt, reinterpret_cast<u8 *>(&source), sizeof(source), cpuAccessCallback);
}
void Copy(VaType dst, VaType src, VaType size, std::function<void(span<u8>)> cpuAccessCallback = {});
void Map(VaType virt, u8 *phys, VaType size, MemoryManagerBlockInfo extraInfo = {}) {
std::scoped_lock lock(this->blockMutex);
blockSegmentTable.Set(virt, virt + size, {virt, phys, size, extraInfo});
this->MapLocked(virt, phys, size, extraInfo);
}
void Unmap(VaType virt, VaType size) {
std::scoped_lock lock(this->blockMutex);
blockSegmentTable.Set(virt, virt + size, {});
this->UnmapLocked(virt, size);
}
};
/**
* @brief FlatMemoryManager specialises FlatAddressSpaceMap to work as an allocator, with an initial, fast linear pass and a subsequent slower pass that iterates until it finds a free block
*/
template<typename VaType, VaType UnmappedVa, size_t AddressSpaceBits> requires AddressSpaceValid<VaType, AddressSpaceBits>
class FlatAllocator : public FlatAddressSpaceMap<VaType, UnmappedVa, bool, false, false, AddressSpaceBits> {
private:
using Base = FlatAddressSpaceMap<VaType, UnmappedVa, bool, false, false, AddressSpaceBits>;
VaType currentLinearAllocEnd; //!< The end address for the initial linear allocation pass, once this reaches the AS limit the slower allocation path will be used
public:
VaType vaStart; //!< The base VA of the allocator, no allocations will be below this
FlatAllocator(VaType vaStart, VaType vaLimit = Base::VaMaximum);
/**
* @brief Allocates a region in the AS of the given size and returns its address
*/
VaType Allocate(VaType size);
/**
* @brief Marks the given region in the AS as allocated
*/
void AllocateFixed(VaType virt, VaType size);
/**
* @brief Frees an AS region so it can be used again
*/
void Free(VaType virt, VaType size);
};
}

View file

@ -1,501 +0,0 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <common/trace.h>
#include <kernel/types/KProcess.h>
#include "address_space.h"
#define MAP_MEMBER(returnType) template<typename VaType, VaType UnmappedVa, typename PaType, PaType UnmappedPa, bool PaContigSplit, size_t AddressSpaceBits, typename ExtraBlockInfo> requires AddressSpaceValid<VaType, AddressSpaceBits> returnType FlatAddressSpaceMap<VaType, UnmappedVa, PaType, UnmappedPa, PaContigSplit, AddressSpaceBits, ExtraBlockInfo>
#define MM_MEMBER(returnType) template<typename VaType, VaType UnmappedVa, size_t AddressSpaceBits, size_t VaGranularityBits, size_t VaL2GranularityBits> requires AddressSpaceValid<VaType, AddressSpaceBits> returnType FlatMemoryManager<VaType, UnmappedVa, AddressSpaceBits, VaGranularityBits, VaL2GranularityBits>
#define ALLOC_MEMBER(returnType) template<typename VaType, VaType UnmappedVa, size_t AddressSpaceBits> requires AddressSpaceValid<VaType, AddressSpaceBits> returnType FlatAllocator<VaType, UnmappedVa, AddressSpaceBits>
namespace skyline {
MAP_MEMBER()::FlatAddressSpaceMap(VaType vaLimit, std::function<void(VaType, VaType)> unmapCallback) :
vaLimit(vaLimit),
unmapCallback(std::move(unmapCallback)) {
if (vaLimit > VaMaximum)
throw exception("Invalid VA limit!");
}
MAP_MEMBER(void)::MapLocked(VaType virt, PaType phys, VaType size, ExtraBlockInfo extraInfo) {
TRACE_EVENT("containers", "FlatAddressSpaceMap::Map");
VaType virtEnd{virt + size};
if (virtEnd > vaLimit)
throw exception("Trying to map a block past the VA limit: virtEnd: 0x{:X}, vaLimit: 0x{:X}", virtEnd, vaLimit);
auto blockEndSuccessor{std::lower_bound(blocks.begin(), blocks.end(), virtEnd)};
if (blockEndSuccessor == blocks.begin())
throw exception("Trying to map a block before the VA start: virtEnd: 0x{:X}", virtEnd);
auto blockEndPredecessor{std::prev(blockEndSuccessor)};
if (blockEndSuccessor != blocks.end()) {
// We have blocks in front of us, if one is directly in front then we don't have to add a tail
if (blockEndSuccessor->virt != virtEnd) {
PaType tailPhys{[&]() -> PaType {
if (!PaContigSplit || blockEndPredecessor->Unmapped())
return blockEndPredecessor->phys; // Always propagate unmapped regions rather than calculating offset
else
return blockEndPredecessor->phys + virtEnd - blockEndPredecessor->virt;
}()};
if (blockEndPredecessor->virt >= virt) {
// If this block's start would be overlapped by the map then reuse it as a tail block
blockEndPredecessor->virt = virtEnd;
blockEndPredecessor->phys = tailPhys;
blockEndPredecessor->extraInfo = blockEndPredecessor->extraInfo;
// No longer predecessor anymore
blockEndSuccessor = blockEndPredecessor--;
} else {
// Else insert a new one and we're done
blocks.insert(blockEndSuccessor, {Block(virt, phys, extraInfo), Block(virtEnd, tailPhys, blockEndPredecessor->extraInfo)});
if (unmapCallback)
unmapCallback(virt, size);
return;
}
}
} else {
// blockEndPredecessor will always be unmapped as blocks has to be terminated by an unmapped chunk
if (blockEndPredecessor != blocks.begin() && blockEndPredecessor->virt >= virt) {
// Move the unmapped block start backwards
blockEndPredecessor->virt = virtEnd;
// No longer predecessor anymore
blockEndSuccessor = blockEndPredecessor--;
} else {
// Else insert a new one and we're done
blocks.insert(blockEndSuccessor, {Block(virt, phys, extraInfo), Block(virtEnd, UnmappedPa, {})});
if (unmapCallback)
unmapCallback(virt, size);
return;
}
}
auto blockStartSuccessor{blockEndSuccessor};
// Walk the block vector to find the start successor as this is more efficient than another binary search in most scenarios
while (std::prev(blockStartSuccessor)->virt >= virt)
blockStartSuccessor--;
// Check that the start successor is either the end block or something in between
if (blockStartSuccessor->virt > virtEnd) {
throw exception("Unsorted block in AS map: virt: 0x{:X}", blockStartSuccessor->virt);
} else if (blockStartSuccessor->virt == virtEnd) {
// We need to create a new block as there are none spare that we would overwrite
blocks.insert(blockStartSuccessor, Block(virt, phys, extraInfo));
} else {
// Erase overwritten blocks
if (auto eraseStart{std::next(blockStartSuccessor)}; eraseStart != blockEndSuccessor)
blocks.erase(eraseStart, blockEndSuccessor);
// Reuse a block that would otherwise be overwritten as a start block
blockStartSuccessor->virt = virt;
blockStartSuccessor->phys = phys;
blockStartSuccessor->extraInfo = extraInfo;
}
if (unmapCallback)
unmapCallback(virt, size);
}
MAP_MEMBER(void)::UnmapLocked(VaType virt, VaType size) {
TRACE_EVENT("containers", "FlatAddressSpaceMap::Unmap");
VaType virtEnd{virt + size};
if (virtEnd > vaLimit)
throw exception("Trying to map a block past the VA limit: virtEnd: 0x{:X}, vaLimit: 0x{:X}", virtEnd, vaLimit);
auto blockEndSuccessor{std::lower_bound(blocks.begin(), blocks.end(), virtEnd)};
if (blockEndSuccessor == blocks.begin())
throw exception("Trying to unmap a block before the VA start: virtEnd: 0x{:X}", virtEnd);
auto blockEndPredecessor{std::prev(blockEndSuccessor)};
auto walkBackToPredecessor{[&](auto iter) {
while (iter->virt >= virt)
iter--;
return iter;
}};
auto eraseBlocksWithEndUnmapped{[&](auto unmappedEnd) {
auto blockStartPredecessor{walkBackToPredecessor(unmappedEnd)};
auto blockStartSuccessor{std::next(blockStartPredecessor)};
auto eraseEnd{[&]() {
if (blockStartPredecessor->Unmapped()) {
// If the start predecessor is unmapped then we can erase everything in our region and be done
return std::next(unmappedEnd);
} else {
// Else reuse the end predecessor as the start of our unmapped region then erase all up to it
unmappedEnd->virt = virt;
return unmappedEnd;
}
}()};
// We can't have two unmapped regions after each other
if (eraseEnd != blocks.end() && (eraseEnd == blockStartSuccessor || (blockStartPredecessor->Unmapped() && eraseEnd->Unmapped())))
throw exception("Multiple contiguous unmapped regions are unsupported!");
blocks.erase(blockStartSuccessor, eraseEnd);
}};
// We can avoid any splitting logic if these are the case
if (blockEndPredecessor->Unmapped()) {
if (blockEndPredecessor->virt > virt)
eraseBlocksWithEndUnmapped(blockEndPredecessor);
if (unmapCallback)
unmapCallback(virt, size);
return; // The region is unmapped, bail out early
} else if (blockEndSuccessor->virt == virtEnd && blockEndSuccessor->Unmapped()) {
eraseBlocksWithEndUnmapped(blockEndSuccessor);
if (unmapCallback)
unmapCallback(virt, size);
return; // The region is unmapped here and doesn't need splitting, bail out early
} else if (blockEndSuccessor == blocks.end()) {
// This should never happen as the end should always follow an unmapped block
throw exception("Unexpected Memory Manager state!");
} else if (blockEndSuccessor->virt != virtEnd) {
// If one block is directly in front then we don't have to add a tail
// The previous block is mapped so we will need to add a tail with an offset
PaType tailPhys{[&]() {
if constexpr (PaContigSplit)
return blockEndPredecessor->phys + virtEnd - blockEndPredecessor->virt;
else
return blockEndPredecessor->phys;
}()};
if (blockEndPredecessor->virt >= virt) {
// If this block's start would be overlapped by the unmap then reuse it as a tail block
blockEndPredecessor->virt = virtEnd;
blockEndPredecessor->phys = tailPhys;
// No longer predecessor anymore
blockEndSuccessor = blockEndPredecessor--;
} else {
blocks.insert(blockEndSuccessor, {Block(virt, UnmappedPa, {}), Block(virtEnd, tailPhys, blockEndPredecessor->extraInfo)});
if (unmapCallback)
unmapCallback(virt, size);
return; // The previous block is mapped and ends before
}
}
// Walk the block vector to find the start predecessor as this is more efficient than another binary search in most scenarios
auto blockStartPredecessor{walkBackToPredecessor(blockEndSuccessor)};
auto blockStartSuccessor{std::next(blockStartPredecessor)};
if (blockStartSuccessor->virt > virtEnd) {
throw exception("Unsorted block in AS map: virt: 0x{:X}", blockStartSuccessor->virt);
} else if (blockStartSuccessor->virt == virtEnd) {
// There are no blocks between the start and the end that would let us skip inserting a new one for head
// The previous block is may be unmapped, if so we don't need to insert any unmaps after it
if (blockStartPredecessor->Mapped())
blocks.insert(blockStartSuccessor, Block(virt, UnmappedPa, {}));
} else if (blockStartPredecessor->Unmapped()) {
// If the previous block is unmapped
blocks.erase(blockStartSuccessor, blockEndPredecessor);
} else {
// Erase overwritten blocks, skipping the first one as we have written the unmapped start block there
if (auto eraseStart{std::next(blockStartSuccessor)}; eraseStart != blockEndSuccessor)
blocks.erase(eraseStart, blockEndSuccessor);
// Add in the unmapped block header
blockStartSuccessor->virt = virt;
blockStartSuccessor->phys = UnmappedPa;
}
if (unmapCallback)
unmapCallback(virt, size);
}
MM_MEMBER(TranslatedAddressRange)::TranslateRangeImpl(VaType virt, VaType size, std::function<void(span<u8>)> cpuAccessCallback) {
TRACE_EVENT("containers", "FlatMemoryManager::TranslateRange");
TranslatedAddressRange ranges;
auto successor{std::upper_bound(this->blocks.begin(), this->blocks.end(), virt, [] (auto virt, const auto &block) {
return virt < block.virt;
})};
auto predecessor{std::prev(successor)};
u8 *blockPhys{predecessor->phys + (virt - predecessor->virt)};
VaType blockSize{std::min(successor->virt - virt, size)};
while (size) {
if (predecessor->phys) {
span cpuBlock{blockPhys, blockSize};
if (cpuAccessCallback)
cpuAccessCallback(cpuBlock);
// Batch contiguous ranges into one
if (!ranges.empty() && ranges.back().data() + ranges.back().size() == cpuBlock.data())
ranges.back() = {ranges.back().data(), ranges.back().size() + cpuBlock.size()};
else
ranges.push_back(cpuBlock);
} else {
ranges.push_back(span<u8>{static_cast<u8*>(nullptr), blockSize});
}
size -= blockSize;
if (size) {
predecessor = successor++;
blockPhys = predecessor->phys;
blockSize = std::min(successor->virt - predecessor->virt, size);
}
}
return ranges;
}
MM_MEMBER()::FlatMemoryManager() {
sparseMap = static_cast<u8 *>(mmap(0, SparseMapSize, PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0));
if (!sparseMap)
throw exception("Failed to mmap sparse map!");
}
MM_MEMBER()::~FlatMemoryManager() {
munmap(sparseMap, SparseMapSize);
}
MM_MEMBER(void)::Read(u8 *destination, VaType virt, VaType size, std::function<void(span<u8>)> cpuAccessCallback) {
TRACE_EVENT("containers", "FlatMemoryManager::Read");
std::shared_lock lock(this->blockMutex);
auto successor{std::upper_bound(this->blocks.begin(), this->blocks.end(), virt, [] (auto virt, const auto &block) {
return virt < block.virt;
})};
auto predecessor{std::prev(successor)};
u8 *blockPhys{predecessor->phys + (virt - predecessor->virt)};
VaType blockReadSize{std::min(successor->virt - virt, size)};
// Reads may span across multiple individual blocks
while (size) {
if (predecessor->phys == nullptr) {
throw exception("Page fault at 0x{:X}", predecessor->virt);
} else {
if (predecessor->extraInfo.sparseMapped) { // Sparse mappings read all zeroes
std::memset(destination, 0, blockReadSize);
} else {
if (cpuAccessCallback)
cpuAccessCallback(span{blockPhys, blockReadSize});
std::memcpy(destination, blockPhys, blockReadSize);
}
}
destination += blockReadSize;
size -= blockReadSize;
if (size) {
predecessor = successor++;
blockPhys = predecessor->phys;
blockReadSize = std::min(successor->virt - predecessor->virt, size);
}
}
}
MM_MEMBER(void)::Write(VaType virt, u8 *source, VaType size, std::function<void(span<u8>)> cpuAccessCallback) {
TRACE_EVENT("containers", "FlatMemoryManager::Write");
std::shared_lock lock(this->blockMutex);
VaType virtEnd{virt + size};
auto successor{std::upper_bound(this->blocks.begin(), this->blocks.end(), virt, [] (auto virt, const auto &block) {
return virt < block.virt;
})};
auto predecessor{std::prev(successor)};
u8 *blockPhys{predecessor->phys + (virt - predecessor->virt)};
VaType blockWriteSize{std::min(successor->virt - virt, size)};
// Writes may span across multiple individual blocks
while (size) {
if (predecessor->phys == nullptr) {
throw exception("Page fault at 0x{:X}", predecessor->virt);
} else {
if (!predecessor->extraInfo.sparseMapped) { // Sparse mappings ignore writes
if (cpuAccessCallback)
cpuAccessCallback(span{blockPhys, blockWriteSize});
std::memcpy(blockPhys, source, blockWriteSize);
}
}
source += blockWriteSize;
size -= blockWriteSize;
if (size) {
predecessor = successor++;
blockPhys = predecessor->phys;
blockWriteSize = std::min(successor->virt - predecessor->virt, size);
}
}
}
MM_MEMBER(void)::Copy(VaType dst, VaType src, VaType size, std::function<void(span<u8>)> cpuAccessCallback) {
TRACE_EVENT("containers", "FlatMemoryManager::Copy");
std::shared_lock lock(this->blockMutex);
VaType srcEnd{src + size};
VaType dstEnd{dst + size};
auto srcSuccessor{std::upper_bound(this->blocks.begin(), this->blocks.end(), src, [] (auto virt, const auto &block) {
return virt < block.virt;
})};
auto dstSuccessor{std::upper_bound(this->blocks.begin(), this->blocks.end(), dst, [] (auto virt, const auto &block) {
return virt < block.virt;
})};
auto srcPredecessor{std::prev(srcSuccessor)};
auto dstPredecessor{std::prev(dstSuccessor)};
u8 *srcBlockPhys{srcPredecessor->phys + (src - srcPredecessor->virt)};
u8 *dstBlockPhys{dstPredecessor->phys + (dst - dstPredecessor->virt)};
VaType srcBlockRemainingSize{srcSuccessor->virt - src};
VaType dstBlockRemainingSize{dstSuccessor->virt - dst};
VaType blockCopySize{std::min({srcBlockRemainingSize, dstBlockRemainingSize, size})};
// Writes may span across multiple individual blocks
while (size) {
if (srcPredecessor->phys == nullptr) {
throw exception("Page fault at 0x{:X}", srcPredecessor->virt);
} else if (dstPredecessor->phys == nullptr) {
throw exception("Page fault at 0x{:X}", dstPredecessor->virt);
} else { [[likely]]
if (srcPredecessor->extraInfo.sparseMapped) {
std::memset(dstBlockPhys, 0, blockCopySize);
} else [[likely]] {
if (cpuAccessCallback) {
cpuAccessCallback(span{dstBlockPhys, blockCopySize});
cpuAccessCallback(span{srcBlockPhys, blockCopySize});
}
std::memcpy(dstBlockPhys, srcBlockPhys, blockCopySize);
}
}
dstBlockPhys += blockCopySize;
srcBlockPhys += blockCopySize;
size -= blockCopySize;
srcBlockRemainingSize -= blockCopySize;
dstBlockRemainingSize -= blockCopySize;
if (size) {
if (!srcBlockRemainingSize) {
srcPredecessor = srcSuccessor++;
srcBlockPhys = srcPredecessor->phys;
srcBlockRemainingSize = srcSuccessor->virt - srcPredecessor->virt;
blockCopySize = std::min({srcBlockRemainingSize, dstBlockRemainingSize, size});
}
if (!dstBlockRemainingSize) {
dstPredecessor = dstSuccessor++;
dstBlockPhys = dstPredecessor->phys;
dstBlockRemainingSize = dstSuccessor->virt - dstPredecessor->virt;
blockCopySize = std::min({srcBlockRemainingSize, dstBlockRemainingSize, size});
}
}
}
}
ALLOC_MEMBER()::FlatAllocator(VaType vaStart, VaType vaLimit) : Base(vaLimit), vaStart(vaStart), currentLinearAllocEnd(vaStart) {}
ALLOC_MEMBER(VaType)::Allocate(VaType size) {
TRACE_EVENT("containers", "FlatAllocator::Allocate");
std::scoped_lock lock(this->blockMutex);
VaType allocStart{UnmappedVa};
VaType allocEnd{currentLinearAllocEnd + size};
// Avoid searching backwards in the address space if possible
if (allocEnd >= currentLinearAllocEnd && allocEnd <= this->vaLimit) {
auto allocEndSuccessor{std::lower_bound(this->blocks.begin(), this->blocks.end(), allocEnd)};
if (allocEndSuccessor == this->blocks.begin())
throw exception("First block in AS map is invalid!");
auto allocEndPredecessor{std::prev(allocEndSuccessor)};
if (allocEndPredecessor->virt <= currentLinearAllocEnd) {
allocStart = currentLinearAllocEnd;
} else {
// Skip over fixed any mappings in front of us
while (allocEndSuccessor != this->blocks.end()) {
if (allocEndSuccessor->virt - allocEndPredecessor->virt < size || allocEndPredecessor->Mapped() ) {
allocStart = allocEndPredecessor->virt;
break;
}
allocEndPredecessor = allocEndSuccessor++;
// Use the VA limit to calculate if we can fit in the final block since it has no successor
if (allocEndSuccessor == this->blocks.end()) {
allocEnd = allocEndPredecessor->virt + size;
if (allocEnd >= allocEndPredecessor->virt && allocEnd <= this->vaLimit)
allocStart = allocEndPredecessor->virt;
}
}
}
}
if (allocStart != UnmappedVa) {
currentLinearAllocEnd = allocStart + size;
} else { // If linear allocation overflows the AS then find a gap
if (this->blocks.size() <= 2)
throw exception("Unexpected allocator state!");
auto searchPredecessor{std::next(this->blocks.begin())};
auto searchSuccessor{std::next(searchPredecessor)};
while (searchSuccessor != this->blocks.end() &&
(searchSuccessor->virt - searchPredecessor->virt < size || searchPredecessor->Mapped())) {
searchPredecessor = searchSuccessor++;
}
if (searchSuccessor != this->blocks.end())
allocStart = searchPredecessor->virt;
else
return {}; // AS is full
}
this->MapLocked(allocStart, true, size, {});
return allocStart;
}
ALLOC_MEMBER(void)::AllocateFixed(VaType virt, VaType size) {
std::scoped_lock lock(this->blockMutex);
this->MapLocked(virt, true, size, {});
}
ALLOC_MEMBER(void)::Free(VaType virt, VaType size) {
std::scoped_lock lock(this->blockMutex);
this->UnmapLocked(virt, size);
}
}

View file

@ -1,56 +0,0 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
#pragma once
#include "settings.h"
#include "jvm.h"
namespace skyline {
/**
* @brief Handles settings on the android platform
* @note Lifetime of this class must not exceed the one of the JNIEnv contained inside ktSettings
*/
class AndroidSettings final : public Settings {
private:
KtSettings ktSettings;
public:
/**
* @note Will construct the underlying KtSettings object in-place
*/
AndroidSettings(JNIEnv *env, jobject settingsInstance) : ktSettings(env, settingsInstance) {
Update();
}
/**
* @note Will take ownership of the passed KtSettings object
*/
AndroidSettings(KtSettings &&ktSettings) : ktSettings(std::move(ktSettings)) {
Update();
}
void Update() override {
isDocked = ktSettings.GetBool("isDocked");
usernameValue = std::move(ktSettings.GetString("usernameValue"));
profilePictureValue = ktSettings.GetString("profilePictureValue");
systemLanguage = ktSettings.GetInt<skyline::language::SystemLanguage>("systemLanguage");
systemRegion = ktSettings.GetInt<skyline::region::RegionCode>("systemRegion");
forceTripleBuffering = ktSettings.GetBool("forceTripleBuffering");
disableFrameThrottling = ktSettings.GetBool("disableFrameThrottling");
gpuDriver = ktSettings.GetString("gpuDriver");
gpuDriverLibraryName = ktSettings.GetString("gpuDriverLibraryName");
executorSlotCountScale = ktSettings.GetInt<u32>("executorSlotCountScale");
executorFlushThreshold = ktSettings.GetInt<u32>("executorFlushThreshold");
useDirectMemoryImport = ktSettings.GetBool("useDirectMemoryImport");
forceMaxGpuClocks = ktSettings.GetBool("forceMaxGpuClocks");
disableShaderCache = ktSettings.GetBool("disableShaderCache");
freeGuestTextureMemory = ktSettings.GetBool("freeGuestTextureMemory");
enableFastGpuReadbackHack = ktSettings.GetBool("enableFastGpuReadbackHack");
enableFastReadbackWrites = ktSettings.GetBool("enableFastReadbackWrites");
disableSubgroupShuffle = ktSettings.GetBool("disableSubgroupShuffle");
isAudioOutputDisabled = ktSettings.GetBool("isAudioOutputDisabled");
validationLayer = ktSettings.GetBool("validationLayer");
};
};
}

View file

@ -1,110 +0,0 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
#pragma once
#include <atomic>
namespace skyline {
/**
* @brief A singly-linked list with atomic access to allow for lock-free access semantics
*/
template<typename Type>
class AtomicForwardList {
private:
struct Node {
Node *next;
Type value;
};
std::atomic<Node *> head{}; //!< The head of the list
public:
AtomicForwardList() = default;
AtomicForwardList(const AtomicForwardList &) = delete;
AtomicForwardList(AtomicForwardList &&other) {
head = other.head.load();
while (!other.head.compare_exchange_strong(head, nullptr, std::memory_order_release, std::memory_order_consume));
}
~AtomicForwardList() {
Clear();
}
/**
* @brief Clears all the items from the list while deallocating them
*/
void Clear() {
auto current{head.exchange(nullptr, std::memory_order_acquire)};
while (current) {
auto next{current->next};
delete current;
current = next;
}
}
/**
* @brief Appends an item to the start of the list
*/
void Append(Type item) {
auto node{new Node{nullptr, item}};
auto next{head.load(std::memory_order_consume)};
do {
node->next = next;
} while (!head.compare_exchange_strong(next, node, std::memory_order_release, std::memory_order_consume));
}
/**
* @brief Appends multiple items to the start of the list
*/
void Append(std::initializer_list<Type> items) {
if (std::empty(items))
return;
Node* firstNode{new Node{nullptr, *items.begin()}};
Node* lastNode{firstNode};
for (auto item{items.begin() + 1}; item != items.end(); item++)
lastNode = new Node{lastNode, *item};
auto next{head.load(std::memory_order_consume)};
do {
firstNode->next = next;
} while (!head.compare_exchange_strong(next, lastNode, std::memory_order_release, std::memory_order_consume));
}
template<typename... Items>
void Append(Items &&... items) {
Append(std::initializer_list<Type>{std::forward<Items>(items)...});
}
/**
* @brief Iterates over every single list item and calls the given function
* @note This function is **not** thread-safe when used with Clear() as the item may be deallocated while iterating
*/
template <typename Function>
void Iterate(Function function) {
auto current{head.load(std::memory_order_consume)};
while (current) {
function(current->value);
current = current->next;
}
}
/**
* @brief Iterates over every single list item and calls the given function till the given predicate returns false
* @note This function is **not** thread-safe when used with Clear() as the item may be deallocated while iterating
*/
template <typename Function>
bool AllOf(Function function) {
auto current{head.load(std::memory_order_consume)};
while (current) {
if (!function(current->value))
return false;
current = current->next;
}
return true;
}
};
}

View file

@ -1,85 +0,0 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/)
#pragma once
#include <cstdint>
#include <stdexcept>
#include <variant>
#include <bitset>
#include <fmt/format.h>
namespace fmt {
/**
* @brief A std::bitset formatter for {fmt}
*/
template<size_t N>
struct formatter<std::bitset<N>> : formatter<std::string> {
template<typename FormatContext>
constexpr auto format(const std::bitset<N> &s, FormatContext &ctx) {
return formatter<std::string>::format(s.to_string(), ctx);
}
};
}
namespace skyline {
using u128 = __uint128_t; //!< Unsigned 128-bit integer
using u64 = __uint64_t; //!< Unsigned 64-bit integer
using u32 = __uint32_t; //!< Unsigned 32-bit integer
using u16 = __uint16_t; //!< Unsigned 16-bit integer
using u8 = __uint8_t; //!< Unsigned 8-bit integer
using i128 = __int128_t; //!< Signed 128-bit integer
using i64 = __int64_t; //!< Signed 64-bit integer
using i32 = __int32_t; //!< Signed 32-bit integer
using i16 = __int16_t; //!< Signed 16-bit integer
using i8 = __int8_t; //!< Signed 8-bit integer
using KHandle = u32; //!< The type of a kernel handle
namespace constant {
// Time
constexpr i64 NsInMicrosecond{1000}; //!< The amount of nanoseconds in a microsecond
constexpr i64 NsInSecond{1000000000}; //!< The amount of nanoseconds in a second
constexpr i64 NsInMillisecond{1000000}; //!< The amount of nanoseconds in a millisecond
constexpr i64 NsInDay{86400000000000UL}; //!< The amount of nanoseconds in a day
constexpr size_t AddressSpaceSize{1ULL << 39}; //!< The size of the host CPU AS in bytes
constexpr size_t PageSize{0x1000}; //!< The size of a host page
constexpr size_t PageSizeBits{12}; //!< log2(PageSize)
static_assert(PageSize == PAGE_SIZE);
}
namespace util {
/**
* @brief A way to implicitly cast all pointers to uintptr_t, this is used for {fmt} as we use 0x{:X} to print pointers
* @note There's the exception of signed char pointers as they represent C Strings
* @note This does not cover std::shared_ptr or std::unique_ptr and those will have to be explicitly casted to uintptr_t or passed through fmt::ptr
*/
template<typename T>
constexpr auto FmtCast(T object) {
if constexpr (std::is_pointer<T>::value)
if constexpr (std::is_same<char, typename std::remove_cv<typename std::remove_pointer<T>::type>::type>::value)
return reinterpret_cast<typename std::common_type<char *, T>::type>(object);
else
return reinterpret_cast<const uintptr_t>(object);
else
return object;
}
/**
* @brief {fmt}::format but with FmtCast built into it
*/
template<typename S, typename... Args>
constexpr auto Format(S formatString, Args &&... args) {
return fmt::format(fmt::runtime(formatString), FmtCast(args)...);
}
}
/**
* @brief A deduction guide for overloads required for std::visit with std::variant
*/
template<class... Ts>
struct VariantVisitor : Ts ... { using Ts::operator()...; };
template<class... Ts> VariantVisitor(Ts...) -> VariantVisitor<Ts...>;
}

View file

@ -28,9 +28,8 @@ namespace skyline {
* @param copyOffset The offset into the buffer after which to use memcpy rather than copyFunction, -1 will use it for the entire buffer
* @return The amount of data written into the input buffer in units of Type
*/
template<typename CopyFunc>
size_t Read(span <Type> buffer, CopyFunc copyFunction) {
std::scoped_lock guard{mtx};
size_t Read(span <Type> buffer, void copyFunction(Type *, Type *) = {}, ssize_t copyOffset = -1) {
std::lock_guard guard(mtx);
if (empty)
return 0;
@ -49,19 +48,38 @@ namespace skyline {
size = sizeBegin + sizeEnd;
}
{
auto sourceEnd{start + sizeEnd};
if (copyFunction && copyOffset) {
auto sourceEnd{start + ((copyOffset != -1) ? copyOffset : sizeEnd)};
for (; start < sourceEnd; start++, pointer++)
copyFunction(start, pointer);
for (auto source{start}, destination{pointer}; source < sourceEnd; source++, destination++)
copyFunction(source, destination);
if (copyOffset != -1) {
std::memcpy(pointer + copyOffset, start + copyOffset, (sizeEnd - copyOffset) * sizeof(Type));
copyOffset -= sizeEnd;
}
} else {
std::memcpy(pointer, start, sizeEnd * sizeof(Type));
}
if (sizeBegin) {
start = array.begin();
auto sourceEnd{array.begin() + sizeBegin};
pointer += sizeEnd;
for (; start < sourceEnd; start++, pointer++)
copyFunction(start, pointer);
if (sizeBegin) {
if (copyFunction && copyOffset) {
auto sourceEnd{array.begin() + ((copyOffset != -1) ? copyOffset : sizeBegin)};
for (auto source{array.begin()}, destination{pointer}; source < sourceEnd; source++, destination++)
copyFunction(source, destination);
if (copyOffset != -1)
std::memcpy(array.begin() + copyOffset, pointer + copyOffset, (sizeBegin - copyOffset) * sizeof(Type));
} else {
std::memcpy(pointer, array.begin(), sizeBegin * sizeof(Type));
}
start = array.begin() + sizeBegin;
} else {
start += sizeEnd;
}
if (start == end)
@ -74,27 +92,51 @@ namespace skyline {
* @brief Appends data from the specified buffer into this buffer
*/
void Append(span <Type> buffer) {
std::scoped_lock guard{mtx};
std::lock_guard guard(mtx);
Type *pointer{buffer.data()};
ssize_t remainingSize{static_cast<ssize_t>(buffer.size())};
while (remainingSize) {
ssize_t writeSize{std::min([&]() {
if (start <= end)
return array.end() - end;
else
return start - end - 1;
}(), remainingSize)};
ssize_t size{static_cast<ssize_t>(buffer.size())};
while (size) {
if (start <= end && end != array.end()) {
auto sizeEnd{std::min(array.end() - end, size)};
std::memcpy(end, pointer, sizeEnd * sizeof(Type));
if (writeSize) {
std::memcpy(end, pointer, static_cast<size_t>(writeSize) * sizeof(Type));
remainingSize -= writeSize;
end += writeSize;
pointer += writeSize;
pointer += sizeEnd;
size -= sizeEnd;
end += sizeEnd;
} else {
auto sizePreStart{(end == array.end()) ? std::min(start - array.begin(), size) : std::min(start - end, size)};
auto sizePostStart{std::min(array.end() - start, size - sizePreStart)};
if (sizePreStart)
std::memcpy((end == array.end()) ? array.begin() : end, pointer, sizePreStart * sizeof(Type));
if (end == array.end())
end = array.begin() + sizePreStart;
else
end += sizePreStart;
pointer += sizePreStart;
size -= sizePreStart;
if (sizePostStart)
std::memcpy(end, pointer, sizePostStart * sizeof(Type));
if (start == array.end())
start = array.begin() + sizePostStart;
else
start += sizePostStart;
if (end == array.end())
end = array.begin() + sizePostStart;
else
end += sizePostStart;
pointer += sizePostStart;
size -= sizePostStart;
}
if (end == array.end())
end = array.begin();
empty = false;
}
}

View file

@ -3,8 +3,6 @@
#pragma once
#include <common/trace.h>
#include <common/spin_lock.h>
#include <common.h>
namespace skyline {
@ -15,12 +13,12 @@ namespace skyline {
class CircularQueue {
private:
std::vector<u8> vector; //!< The internal vector holding the circular queue's data, we use a byte vector due to the default item construction/destruction semantics not being appropriate for a circular buffer
std::atomic<Type *> start{reinterpret_cast<Type *>(vector.begin().base())}; //!< The start/oldest element of the queue
std::atomic<Type *> end{reinterpret_cast<Type *>(vector.begin().base())}; //!< The end/newest element of the queue
SpinLock consumptionMutex;
std::condition_variable_any consumeCondition;
SpinLock productionMutex;
std::condition_variable_any produceCondition;
Type *start{reinterpret_cast<Type *>(vector.begin().base())}; //!< The start/oldest element of the queue
Type *end{reinterpret_cast<Type *>(vector.begin().base())}; //!< The end/newest element of the queue
std::mutex consumptionMutex;
std::condition_variable consumeCondition;
std::mutex productionMutex;
std::condition_variable produceCondition;
public:
/**
@ -50,22 +48,15 @@ namespace skyline {
/**
* @brief A blocking for-each that runs on every item and waits till new items to run on them as well
* @param function A function that is called for each item (with the only parameter as a reference to that item)
* @param preWait An optional function that's called prior to waiting on more items to be queued
*/
template<typename F1, typename F2>
[[noreturn]] void Process(F1 function, F2 preWait) {
TRACE_EVENT_BEGIN("containers", "CircularQueue::Process");
template<typename F>
[[noreturn]] void Process(F function) {
while (true) {
if (start == end) {
std::unique_lock productionLock{productionMutex};
TRACE_EVENT_END("containers");
preWait();
produceCondition.wait(productionLock, [this]() { return start != end; });
TRACE_EVENT_BEGIN("containers", "CircularQueue::Process");
std::unique_lock lock(productionMutex);
produceCondition.wait(lock, [this]() { return start != end; });
}
std::scoped_lock comsumptionLock{consumptionMutex};
while (start != end) {
auto next{start + 1};
next = (next == reinterpret_cast<Type *>(vector.end().base())) ? reinterpret_cast<Type *>(vector.begin().base()) : next;
@ -77,58 +68,30 @@ namespace skyline {
}
}
Type Pop() {
{
std::unique_lock productionLock{productionMutex};
produceCondition.wait(productionLock, [this]() { return start != end; });
void Push(const Type &item) {
std::unique_lock lock(productionMutex);
end = (end == reinterpret_cast<Type *>(vector.end().base()) - 1) ? reinterpret_cast<Type *>(vector.begin().base()) : end;
if (start == end + 1) {
std::unique_lock consumeLock(consumptionMutex);
consumeCondition.wait(consumeLock, [=]() { return start != end + 1; });
}
std::scoped_lock comsumptionLock{consumptionMutex};
auto next{start + 1};
next = (next == reinterpret_cast<Type *>(vector.end().base())) ? reinterpret_cast<Type *>(vector.begin().base()) : next;
Type item{*next};
start = next;
consumeCondition.notify_one();
return item;
*end = item;
produceCondition.notify_one();
}
void Push(const Type &item) {
Type *waitNext{};
Type *waitEnd{};
while (true) {
if (waitNext) {
std::unique_lock consumeLock{consumptionMutex};
consumeCondition.wait(consumeLock, [=]() { return waitNext != start || waitEnd != end; });
waitNext = nullptr;
waitEnd = nullptr;
}
std::scoped_lock lock{productionMutex};
void Append(span <Type> buffer) {
std::unique_lock lock(productionMutex);
for (const auto &item : buffer) {
auto next{end + 1};
next = (next == reinterpret_cast<Type *>(vector.end().base())) ? reinterpret_cast<Type *>(vector.begin().base()) : next;
if (next == start) {
waitNext = next;
waitEnd = end;
continue;
std::unique_lock consumeLock(consumptionMutex);
consumeCondition.wait(consumeLock, [=]() { return next != start; });
}
*next = item;
end = next;
produceCondition.notify_one();
break;
end = next++;
}
}
/**
* @note The appended elements may not necessarily be directly contiguous as another thread could push elements in between those in the span
*/
void Append(span<Type> buffer) {
for (const auto &item : buffer)
Push(item);
produceCondition.notify_one();
}
/**
@ -136,9 +99,19 @@ namespace skyline {
* @param tranformation A function that takes in an item of TransformedType as input and returns an item of Type
*/
template<typename TransformedType, typename Transformation>
void AppendTranform(TransformedType &container, Transformation transformation) {
for (const auto &item : container)
Push(transformation(item));
void AppendTranform(span <TransformedType> buffer, Transformation transformation) {
std::unique_lock lock(productionMutex);
for (const auto &item : buffer) {
auto next{end + 1};
next = (next == reinterpret_cast<Type *>(vector.end().base())) ? reinterpret_cast<Type *>(vector.begin().base()) : next;
if (next == start) {
std::unique_lock consumeLock(consumptionMutex);
consumeCondition.wait(consumeLock, [=]() { return next != start; });
}
*next = transformation(item);
end = next;
}
produceCondition.notify_one();
}
};
}

View file

@ -1,261 +0,0 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
#pragma once
#include "base.h"
#include "exception.h"
#include "logger.h"
#include "span.h"
namespace skyline::dirty {
template<size_t, size_t, size_t>
class Manager;
/**
* @brief An opaque handle to a dirty subresource
*/
struct Handle {
private:
template<size_t, size_t, size_t>
friend class Manager;
bool *dirtyPtr; //!< Underlying target ptr
public:
explicit Handle(bool *dirtyPtr) : dirtyPtr{dirtyPtr} {}
};
/**
* @brief Implements a way to track dirty subresources within a region of memory
* @tparam ManagedResourceSize Size of the managed resource in bytes
* @tparam Granularity Minimum granularity of a subresource in bytes
* @tparam OverlapPoolSize Size of the pool used to store handles when there are multiple bound to the same subresource
* @note This class is *NOT* thread-safe
*/
template<size_t ManagedResourceSize, size_t Granularity, size_t OverlapPoolSize = 0x2000>
class Manager {
private:
struct BindingState {
enum class Type : u32 {
None, //!< No handles are bound
Inline, //!< `inlineDirtyPtr` contains a pointer to the single bound handle for the entry
OverlapSpan, //!< `overlapSpanDirtyPtrs` contains an array of pointers to handles bound to the entry
} type{Type::None};
u32 overlapSpanSize{}; //!< Size of the array that overlapSpanDirtyPtrs points to
union {
bool *inlineDirtyPtr;
bool **overlapSpanDirtyPtrs;
};
/**
* @return An array of pointers to handles bound to the entry
*/
span<bool *> GetOverlapSpan() {
return {overlapSpanDirtyPtrs, overlapSpanSize};
}
};
std::array<bool *, OverlapPoolSize> overlapPool{}; //!< Backing pool for `overlapSpanDirtyPtrs` in entry
bool **freeOverlapPtr{}; //!< Pointer to the next free entry in `overlapPool`
std::array<BindingState, ManagedResourceSize / Granularity> states{}; //!< The dirty binding states for the entire managed resource
uintptr_t managedResourceBaseAddress; //!< The base address of the managed resource
public:
template<typename ManagedResourceType> requires (std::is_standard_layout_v<ManagedResourceType> && sizeof(ManagedResourceType) == ManagedResourceSize)
Manager(ManagedResourceType &managedResource) : managedResourceBaseAddress{reinterpret_cast<uintptr_t>(&managedResource)}, freeOverlapPtr{overlapPool.data()} {}
/**
* @brief Binds a handle to a subresource, automatically handling overlaps
*/
void Bind(Handle handle, uintptr_t subresourceAddress, size_t subresourceSizeBytes) {
if (managedResourceBaseAddress > subresourceAddress)
throw exception("Dirty subresource address is below the managed resource base address");
size_t subresourceAddressOffset{subresourceAddress - managedResourceBaseAddress};
// Validate
if (subresourceAddressOffset < 0 || (subresourceAddressOffset + subresourceSizeBytes) >= ManagedResourceSize)
throw exception("Dirty subresource address is not within the managed resource");
if (subresourceSizeBytes % Granularity)
throw exception("Dirty subresource size isn't aligned to the tracking granularity");
if (subresourceAddressOffset % Granularity)
throw exception("Dirty subresource offset isn't aligned to the tracking granularity");
// Insert
size_t subresourceIndex{static_cast<size_t>(subresourceAddressOffset / Granularity)};
size_t subresourceSize{subresourceSizeBytes / Granularity};
for (size_t i{subresourceIndex}; i < subresourceIndex + subresourceSize; i++) {
auto &state{states[i]};
if (state.type == BindingState::Type::None) {
state.type = BindingState::Type::Inline;
state.inlineDirtyPtr = handle.dirtyPtr;
} else if (state.type == BindingState::Type::Inline) {
state.type = BindingState::Type::OverlapSpan;
// Save the old inline dirty pointer since we'll need to insert it into the new overlap span and setting the overlap span ptr will overwrite it
bool *origDirtyPtr{state.inlineDirtyPtr};
// Point to a new pool allocation that can hold the overlap
state.overlapSpanDirtyPtrs = freeOverlapPtr;
state.overlapSpanSize = 2; // Existing inline handle + our new handle
// Check if the pool allocation is valid
if (freeOverlapPtr + state.overlapSpanSize >= overlapPool.end())
throw exception("Dirty overlap pool is full");
// Write overlap to our new pool allocation
*freeOverlapPtr++ = origDirtyPtr;
*freeOverlapPtr++ = handle.dirtyPtr;
} else if (state.type == BindingState::Type::OverlapSpan) {
auto originalOverlapSpan{state.GetOverlapSpan()};
// Point to a new pool allocation that can hold all the old overlaps + our new overlap
state.overlapSpanSize++;
state.overlapSpanDirtyPtrs = freeOverlapPtr;
// Check if the pool allocation is valid
if (freeOverlapPtr + state.overlapSpanSize >= overlapPool.end())
throw exception("Dirty overlap pool is full");
// Write all overlaps to our new pool allocation
auto newOverlapSpan{state.GetOverlapSpan()};
newOverlapSpan.copy_from(originalOverlapSpan); // Copy old overlaps
*newOverlapSpan.last(1).data() = handle.dirtyPtr; // Write new overlap
freeOverlapPtr += state.overlapSpanSize;
}
}
}
template<typename SubresourceType> requires std::is_standard_layout_v<SubresourceType>
void Bind(Handle handle, SubresourceType &subresource) {
Bind(handle, reinterpret_cast<uintptr_t>(&subresource), sizeof(SubresourceType));
}
template<typename... Args>
void Bind(Handle handle, Args && ...subresources) {
(Bind(handle, subresources), ...);
}
/**
* @brief Marks part of the managed resource as dirty
* @note This *MUST NOT* be called after any bound handles have been destroyed
*/
void MarkDirty(size_t index) {
auto &state{states[index]};
if (state.type == BindingState::Type::None) [[likely]] {
return;
} else if (state.type == BindingState::Type::Inline) {
*state.inlineDirtyPtr = true;
} else if (state.type == BindingState::Type::OverlapSpan) [[unlikely]] {
for (auto &dirtyPtr : state.GetOverlapSpan())
*dirtyPtr = true;
}
}
};
/**
* @brief Encapsulates a dirty subresource to allow for automatic binding on construction
*/
template<typename T>
class BoundSubresource {
private:
const T subresource;
public:
template<typename ManagerT>
BoundSubresource(ManagerT &manager, dirty::Handle handle, const T &subresource) : subresource{subresource} {
subresource.DirtyBind(manager, handle);
}
const T *operator->() const {
return &subresource;
}
const T &operator*() const {
return subresource;
}
};
class ManualDirty {};
/**
* @brief ManualDirty but with a `Refresh()` method that should be called for every `Update()`, irrespective of dirty state
*/
class RefreshableManualDirty : ManualDirty {};
/**
* @brief ManualDirty but with a `PurgeCaches()` method to purge caches that would usually be kept even after being marked dirty
*/
class CachedManualDirty : ManualDirty {};
/**
* @brief Wrapper around an object that holds dirty state and provides convinient functionality for dirty tracking
*/
template<typename T> requires (std::is_base_of_v<ManualDirty, T>)
class ManualDirtyState {
private:
T value; //!< The underlying object
bool dirty{true}; //!< Whether the value is dirty
/**
* @return An opaque handle that can be used to modify dirty state
*/
Handle GetDirtyHandle() {
return Handle{&dirty};
}
public:
template<typename... Args>
ManualDirtyState(Args &&... args) : value{GetDirtyHandle(), std::forward<Args>(args)...} {}
/**
* @brief Cleans the object of its dirty state and refreshes it if necessary
* @note This *MUST* be called before any accesses to the underlying object without *ANY* calls to `MarkDirty()` in between
*/
template<typename... Args>
void Update(Args &&... args) {
if (dirty) {
dirty = false;
value.Flush(std::forward<Args>(args)...);
} else if constexpr (std::is_base_of_v<RefreshableManualDirty, T>) {
if (value.Refresh(std::forward<Args>(args)...))
value.Flush(std::forward<Args>(args)...);
}
}
/**
* @brief Marks the object as dirty
* @param purgeCaches Whether to purge caches of the object that would usually be kept even after being marked dirty
*/
void MarkDirty(bool purgeCaches) {
dirty = true;
if constexpr (std::is_base_of_v<CachedManualDirty, T>)
if (purgeCaches)
value.PurgeCaches();
}
/**
* @brief Returns a reference to the underlying object
* @note `Update()` *MUST* be called before calling this, without *ANY* calls to `MarkDirty()` in between
*/
T &Get() {
return value;
}
template<typename... Args>
T &UpdateGet(Args &&... args) {
Update(std::forward<Args>(args)...);
return value;
}
};
}

View file

@ -1,20 +0,0 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include "signal.h"
#include "exception.h"
namespace skyline {
std::vector<void *> exception::GetStackFrames() {
std::vector<void*> frames;
signal::StackFrame *frame{};
asm("MOV %0, FP" : "=r"(frame));
if (frame)
frame = frame->next; // We want to skip the first frame as it's going to be the caller of this function
while (frame && frame->lr) {
frames.push_back(frame->lr);
frame = frame->next;
}
return frames;
}
}

View file

@ -1,26 +0,0 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
#pragma once
#include <vector>
#include "base.h"
namespace skyline {
/**
* @brief A wrapper over std::runtime_error with {fmt} formatting and stack tracing support
*/
class exception : public std::runtime_error {
public:
std::vector<void *> frames; //!< All frames from the stack trace during the exception
/**
* @return A vector of all frames on the caller's stack
* @note This cannot be inlined because it depends on having one stack frame itself consistently
*/
static std::vector<void*> GetStackFrames() __attribute__((noinline));
template<typename S, typename... Args>
exception(const S &formatStr, Args &&... args) : runtime_error(util::Format(formatStr, args...)), frames(GetStackFrames()) {}
};
}

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