Compare commits
12 commits
ftx1
...
title-meta
Author | SHA1 | Date | |
---|---|---|---|
|
b443287007 | ||
|
8360cdf82e | ||
|
25ddc846bb | ||
|
67881d2215 | ||
|
c5797fdff6 | ||
|
8d56fa3194 | ||
|
be38c8b7a2 | ||
|
e00db910f1 | ||
|
bf331abcdc | ||
|
b22ad8035c | ||
|
74d75505e9 | ||
|
6ee5e7e629 |
689 changed files with 8917 additions and 49057 deletions
3
.github/FUNDING.yml
vendored
3
.github/FUNDING.yml
vendored
|
@ -1,3 +0,0 @@
|
|||
# These is the list of funding model platforms used by Skyline
|
||||
|
||||
patreon: skyline_emu
|
111
.github/workflows/ci.yml
vendored
111
.github/workflows/ci.yml
vendored
|
@ -1,107 +1,74 @@
|
|||
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
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Restore CCache
|
||||
uses: hendrikmuhs/ccache-action@v1.2
|
||||
with:
|
||||
max-size: 3Gi
|
||||
|
||||
- name: Restore Gradle Cache
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.gradle/
|
||||
key: ${{ runner.os }}-gradle-${{ hashFiles('**/build.gradle') }}-${{ hashFiles('app/**/*.xml') }}-${{ hashFiles('app/**.kt', 'app/**.java') }}
|
||||
path: /root/.gradle/
|
||||
key: ${{ runner.os }}-gradle-${{ hashFiles('**/build.gradle') }}
|
||||
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: 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
|
||||
run: echo "yes" | $ANDROID_HOME/tools/bin/sdkmanager "cmake;3.18.1" "ndk;24.0.7856742" --channel=3 | grep -v = || true
|
||||
|
||||
- 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: Android Lint
|
||||
run: |
|
||||
chmod +x gradlew
|
||||
./gradlew --stacktrace lint
|
||||
|
||||
- name: Upload Lint Report
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: lint-result.html
|
||||
path: app/build/reports/lint-results-debug.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
|
||||
run: ./gradlew --stacktrace 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 Signed Debug APK
|
||||
if: env.IS_SKYLINE_SIGNED == 'true' && env.UPLOAD_ARTIFACTS == 'true'
|
||||
uses: actions/upload-artifact@v3
|
||||
- name: Upload Debug APK
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: skyline-${{ github.run_number }}-reldebug.apk
|
||||
path: skyline-${{ github.run_number }}-reldebug.apk
|
||||
name: app-debug.apk
|
||||
path: app/build/outputs/apk/debug/
|
||||
|
||||
- name: Upload Signed Release APK
|
||||
if: env.IS_SKYLINE_SIGNED == 'true' && env.UPLOAD_ARTIFACTS == 'true'
|
||||
uses: actions/upload-artifact@v3
|
||||
- name: Upload Release APK
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: skyline-${{ github.run_number }}-release.apk
|
||||
path: skyline-${{ github.run_number }}-release.apk
|
||||
name: app-release.apk
|
||||
path: app/build/outputs/apk/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
|
||||
- name: Upload R8 Mapping
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: skyline-${{ github.run_number }}-unsigned-reldebug.apk
|
||||
path: skyline-${{ github.run_number }}-unsigned-reldebug.apk
|
||||
name: mapping.txt
|
||||
path: app/build/outputs/mapping/release/
|
||||
|
||||
- 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/
|
||||
|
|
20
.github/workflows/edge-ci.yml
vendored
20
.github/workflows/edge-ci.yml
vendored
|
@ -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
|
13
.gitignore
vendored
13
.gitignore
vendored
|
@ -46,12 +46,8 @@ captures/
|
|||
.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
|
||||
|
@ -100,12 +96,3 @@ lint/reports/
|
|||
|
||||
# Adreno Validation Layer
|
||||
libVkLayer_adreno.so
|
||||
|
||||
# Skyline logs
|
||||
*.sklog
|
||||
|
||||
# Android Studio Profiler Traces
|
||||
*.trace
|
||||
|
||||
# Output Metadata
|
||||
output-metadata.json
|
||||
|
|
29
.gitmodules
vendored
29
.gitmodules
vendored
|
@ -11,6 +11,9 @@
|
|||
[submodule "Frozen"]
|
||||
path = app/libraries/frozen
|
||||
url = https://github.com/serge-sans-paille/frozen
|
||||
[submodule "PugiXML"]
|
||||
path = app/libraries/pugixml
|
||||
url = https://github.com/zeux/pugixml.git
|
||||
[submodule "tzcode"]
|
||||
path = app/libraries/tzcode
|
||||
url = https://github.com/skyline-emu/tz
|
||||
|
@ -33,33 +36,9 @@
|
|||
url = https://github.com/xiph/opus
|
||||
[submodule "Boost"]
|
||||
path = app/libraries/boost
|
||||
url = https://github.com/skyline-emu/boost.git
|
||||
url = https://github.com/boostorg/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
|
||||
|
|
24
.idea/codeStyles/Project.xml
generated
24
.idea/codeStyles/Project.xml
generated
|
@ -25,7 +25,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 +54,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="" />
|
||||
|
|
6
.idea/compiler.xml
generated
Normal file
6
.idea/compiler.xml
generated
Normal file
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<bytecodeTargetLevel target="11" />
|
||||
</component>
|
||||
</project>
|
39
.idea/inspectionProfiles/Project_Default.xml
generated
39
.idea/inspectionProfiles/Project_Default.xml
generated
|
@ -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-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-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,7 @@
|
|||
<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="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 +1107,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 +1116,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 +1159,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 +1167,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" />
|
||||
|
|
5
.idea/kotlinc.xml
generated
5
.idea/kotlinc.xml
generated
|
@ -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>
|
|
@ -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>
|
||||
|
|
8
.idea/runConfigurations/Main.xml
generated
8
.idea/runConfigurations/Main.xml
generated
|
@ -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>
|
||||
|
|
10
.idea/runConfigurations/Settings.xml
generated
10
.idea/runConfigurations/Settings.xml
generated
|
@ -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">
|
||||
|
|
3
.idea/scopes/ShaderCompiler.xml
generated
3
.idea/scopes/ShaderCompiler.xml
generated
|
@ -1,3 +0,0 @@
|
|||
<component name="DependencyValidationManager">
|
||||
<scope name="ShaderCompiler" pattern="file[skyline.app.main]:shader-compiler//*" />
|
||||
</component>
|
4
.idea/scopes/SkylineKotlin.xml
generated
4
.idea/scopes/SkylineKotlin.xml
generated
|
@ -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>
|
||||
|
|
2
.idea/scopes/SkylineNative.xml
generated
2
.idea/scopes/SkylineNative.xml
generated
|
@ -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" />
|
||||
<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||file[skyline.app]:src/main/cpp//*.natvis" />
|
||||
</component>
|
4
.idea/scopes/SkylineXml.xml
generated
4
.idea/scopes/SkylineXml.xml
generated
|
@ -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>
|
||||
|
|
118
BUILDING.md
118
BUILDING.md
|
@ -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
|
||||
```
|
|
@ -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
|
||||
|
|
20
README.md
20
README.md
|
@ -1,8 +1,3 @@
|
|||
<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>
|
||||
|
@ -11,16 +6,11 @@
|
|||
<img src="https://img.shields.io/discord/545842171459272705.svg?label=&logo=discord&logoColor=ffffff&color=5865F2&labelColor=404EED">
|
||||
</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>
|
||||
</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,12 +22,12 @@ 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**.
|
||||
|
||||
* **[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.
|
||||
* **[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**.
|
||||
|
||||
* **[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, its 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.
|
||||
|
||||
---
|
||||
|
||||
### Disclaimer
|
||||
|
|
|
@ -9,12 +9,11 @@ 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 "${CMAKE_CXX_FLAGS} -fno-strict-aliasing -Wno-unused-command-line-argument")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "-Ofast -flto=full -fno-stack-protector -DNDEBUG")
|
||||
|
||||
# Build all libraries with -Ofast but with default debug data (-g) for debug and reldebug builds
|
||||
# Build all libraries with -Ofast but with default debug data (-g) for debug builds
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "-Ofast")
|
||||
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-Ofast")
|
||||
|
||||
# libcxx
|
||||
set(ANDROID_STL "none")
|
||||
|
@ -39,11 +38,6 @@ set(LIBCXXABI_LIBCXX_INCLUDES "${LIBCXX_TARGET_INCLUDE_DIRECTORY}" CACHE STRING
|
|||
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")
|
||||
|
||||
|
@ -51,6 +45,10 @@ add_subdirectory("libraries/fmt")
|
|||
add_subdirectory("libraries/tzcode")
|
||||
target_compile_options(tzcode PRIVATE -Wno-everything)
|
||||
|
||||
# Oboe
|
||||
add_subdirectory("libraries/oboe")
|
||||
include_directories(SYSTEM "libraries/oboe/include")
|
||||
|
||||
# LZ4
|
||||
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)
|
||||
|
@ -61,11 +59,7 @@ include_directories(SYSTEM "libraries/lz4/lib")
|
|||
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)
|
||||
|
||||
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
|
||||
|
||||
|
@ -74,6 +68,9 @@ include_directories("libraries/vkma/include")
|
|||
add_library(vkma STATIC libraries/vkma.cpp)
|
||||
target_compile_options(vkma PRIVATE -Wno-everything)
|
||||
|
||||
# PugiXML (Header-only mode)
|
||||
include_directories(SYSTEM "libraries/pugixml/src")
|
||||
|
||||
# Frozen
|
||||
include_directories(SYSTEM "libraries/frozen/include")
|
||||
|
||||
|
@ -91,77 +88,27 @@ 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")
|
||||
|
||||
# Perfetto SDK
|
||||
include_directories(SYSTEM "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")
|
||||
|
||||
# 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")
|
||||
# Boost
|
||||
set(Boost_USE_STATIC_LIBS ON)
|
||||
set(Boost_USE_MULTITHREADED ON)
|
||||
add_subdirectory("libraries/boost")
|
||||
|
||||
# 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)
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g3 -glldb -gdwarf-5")
|
||||
|
||||
# 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,49 +125,17 @@ 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/audio/track.cpp
|
||||
${source_DIR}/skyline/audio/resampler.cpp
|
||||
${source_DIR}/skyline/audio/adpcm_decoder.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/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
|
||||
|
@ -230,17 +145,9 @@ add_library(skyline SHARED
|
|||
${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/soc/gm20b/engines/maxwell/macro_interpreter.cpp
|
||||
${source_DIR}/skyline/input/npad.cpp
|
||||
${source_DIR}/skyline/input/npad_device.cpp
|
||||
${source_DIR}/skyline/input/touch.cpp
|
||||
|
@ -252,7 +159,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
|
||||
|
@ -268,12 +174,13 @@ add_library(skyline SHARED
|
|||
${source_DIR}/skyline/services/base_service.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 +202,13 @@ 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
|
||||
|
@ -338,13 +221,11 @@ add_library(skyline SHARED
|
|||
${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
|
||||
|
@ -366,13 +247,10 @@ add_library(skyline SHARED
|
|||
${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 +261,30 @@ 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_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)
|
||||
|
||||
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)
|
||||
# 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} ${library})
|
||||
endforeach (library)
|
||||
endfunction(target_link_libraries_system)
|
||||
|
||||
target_link_libraries_system(skyline android perfetto fmt lz4_static tzcode oboe vkma mbedcrypto opus Boost::container)
|
||||
|
|
137
app/build.gradle
137
app/build.gradle
|
@ -2,75 +2,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"))
|
||||
id 'kotlinx-serialization'
|
||||
id 'dagger.hilt.android.plugin'
|
||||
}
|
||||
|
||||
android {
|
||||
namespace 'emu.skyline'
|
||||
compileSdk 33
|
||||
|
||||
var isBuildSigned = (System.getenv("CI") == "true") && (System.getenv("IS_SKYLINE_SIGNED") == "true")
|
||||
|
||||
compileSdkVersion 31
|
||||
buildToolsVersion '31.0.0'
|
||||
defaultConfig {
|
||||
applicationId "skyline.emu"
|
||||
|
||||
minSdk 29
|
||||
targetSdk 33
|
||||
minSdkVersion 29
|
||||
targetSdkVersion 31
|
||||
|
||||
versionCode 3
|
||||
versionName "0.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")
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
|
@ -84,42 +44,13 @@ 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"]
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -128,32 +59,26 @@ android {
|
|||
}
|
||||
|
||||
/* Linting */
|
||||
lint {
|
||||
lintOptions {
|
||||
disable 'IconLocation'
|
||||
}
|
||||
|
||||
/* NDK and CMake */
|
||||
ndkVersion '25.0.8775105'
|
||||
/* NDK */
|
||||
ndkVersion '24.0.7856742'
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
version '3.22.1+'
|
||||
version '3.18.1+'
|
||||
path "CMakeLists.txt"
|
||||
}
|
||||
}
|
||||
|
||||
/* Android Assets */
|
||||
androidResources {
|
||||
ignoreAssetsPattern '*.md'
|
||||
aaptOptions {
|
||||
ignoreAssetsPattern "*.md"
|
||||
}
|
||||
|
||||
/* Vulkan Validation Layers */
|
||||
sourceSets {
|
||||
reldebug {
|
||||
jniLibs {
|
||||
srcDir "libraries/vklayers"
|
||||
}
|
||||
}
|
||||
|
||||
debug {
|
||||
jniLibs {
|
||||
srcDir "libraries/vklayers"
|
||||
|
@ -164,31 +89,29 @@ android {
|
|||
|
||||
dependencies {
|
||||
/* 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.core:core-ktx:1.6.0"
|
||||
implementation 'androidx.appcompat:appcompat:1.3.1'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.1'
|
||||
implementation 'androidx.preference:preference-ktx:1.1.1'
|
||||
implementation "androidx.recyclerview:recyclerview:1.2.1"
|
||||
implementation 'com.google.android.material:material:1.4.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 "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
|
||||
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
|
||||
implementation 'androidx.fragment:fragment-ktx:1.3.6'
|
||||
implementation "com.google.dagger:hilt-android:$hilt_version"
|
||||
kapt "com.google.dagger:hilt-compiler:$hilt_version"
|
||||
kapt "com.google.dagger:hilt-android-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 "org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2"
|
||||
|
||||
/* Other Java */
|
||||
implementation 'info.debatty:java-string-similarity:2.0.0'
|
||||
implementation 'com.github.KikiManjaro:colorpicker:v1.1.12'
|
||||
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
|
||||
implementation "com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:0.8.0"
|
||||
}
|
||||
|
||||
kapt {
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
Subproject commit 19d1998c5a39dc18f90086e068ce7b3331110d74
|
|
@ -1 +0,0 @@
|
|||
Subproject commit 76440e0a3554433398c0ef03233f862a6e86f5ee
|
|
@ -1 +1 @@
|
|||
Subproject commit 06d52af216340ed46b865d01c4f7c0d7a8cc5918
|
||||
Subproject commit e32407f2b4cac7f0d739f10821d7f7e0c81e8436
|
|
@ -1 +0,0 @@
|
|||
Subproject commit 2d93d734ecff78deb060abd727712bae8ab0489a
|
|
@ -1 +1 @@
|
|||
Subproject commit abffdd88767791ef6da4d2df7ec7ab158eb8b775
|
||||
Subproject commit 08e3a5ccd952edee36b3c002e3a29c6b1b5153de
|
|
@ -1 +1 @@
|
|||
Subproject commit 9053f015ce1317d3c8bac0111c516b377c2e287e
|
||||
Subproject commit 855ea841a93bf304065e5152909983b1b85ffabb
|
1
app/libraries/pugixml
Submodule
1
app/libraries/pugixml
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 9e382f98076e57581fcc61323728443374889646
|
|
@ -1 +0,0 @@
|
|||
Subproject commit 83783f578e0e6666d68a3bf17b0038a80e62530e
|
|
@ -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 4bcd301070d7d4a3d6ca191da96430025d708a6a
|
Binary file not shown.
|
@ -1 +1 @@
|
|||
Subproject commit 936bc4b57e7ffa5906a786735537c5493224e7d6
|
||||
Subproject commit 895b080a3c2189feaea0919af8982e9a248ff7d6
|
4
app/proguard-rules.pro
vendored
4
app/proguard-rules.pro
vendored
|
@ -4,10 +4,8 @@
|
|||
|
||||
# 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
|
||||
# Kotlinx Serialization rules
|
||||
# Keep `Companion` object fields of serializable classes.
|
||||
# This avoids serializer lookup through `getDeclaredClasses` as done for named companion objects.
|
||||
-if @kotlinx.serialization.Serializable class **
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<?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" />
|
||||
|
@ -12,25 +13,18 @@
|
|||
|
||||
<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:label="@string/app_name"
|
||||
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="emu.skyline.MainActivity"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
|
@ -38,41 +32,43 @@
|
|||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".settings.SettingsActivity"
|
||||
android:name="emu.skyline.SettingsActivity"
|
||||
android:exported="true"
|
||||
android:label="@string/settings"
|
||||
android:launchMode="singleTop"
|
||||
android:parentActivityName=".MainActivity">
|
||||
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: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:parentActivityName=".settings.SettingsActivity">
|
||||
android:screenOrientation="landscape"
|
||||
tools:ignore="LockedOrientationActivity">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value="emu.skyline.input.ControllerActivity" />
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".preference.GpuDriverActivity"
|
||||
android:exported="true"
|
||||
android:parentActivityName=".settings.SettingsActivity">
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".input.onscreen.OnScreenEditActivity"
|
||||
android:exported="true"
|
||||
android:screenOrientation="sensorLandscape"
|
||||
tools:ignore="LockedOrientationActivity"
|
||||
android:parentActivityName=".input.ControllerActivity">
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".EmulationActivity"
|
||||
android:supportsPictureInPicture="true"
|
||||
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|uiMode"
|
||||
android:name="emu.skyline.EmulationActivity"
|
||||
android:configChanges="orientation|screenSize"
|
||||
android:exported="true"
|
||||
android:launchMode="singleTask"
|
||||
android:process="${emulationProcess}"
|
||||
android:parentActivityName=".MainActivity">
|
||||
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" />
|
||||
|
@ -94,41 +90,27 @@
|
|||
android:mimeType="text/plain"
|
||||
android:pathPattern=".*\\.nro"
|
||||
android:scheme="content"
|
||||
tools:ignore="IntentFilterUniqueDataAttributes" />
|
||||
tools:ignore="AppLinkUrlError" />
|
||||
<data
|
||||
android:mimeType="application/octet-stream"
|
||||
android:pathPattern=".*\\.nro"
|
||||
android:scheme="content"
|
||||
tools:ignore="IntentFilterUniqueDataAttributes" />
|
||||
tools:ignore="AppLinkUrlError" />
|
||||
<data
|
||||
android:mimeType="application/nro"
|
||||
android:scheme="content"
|
||||
tools:ignore="IntentFilterUniqueDataAttributes" />
|
||||
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">
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="skyline.emu.fileprovider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data
|
||||
android:name="autoStoreLocales"
|
||||
android:value="true" />
|
||||
</service>
|
||||
|
||||
<meta-data
|
||||
android:name="com.android.graphics.injectLayers.enable"
|
||||
android:value="true" />
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/filepaths" />
|
||||
</provider>
|
||||
</application>
|
||||
</manifest>
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,3 +1,4 @@
|
|||
#### 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)
|
||||
#### Skyline FOSS Shared Fonts: (Credit to [FearlessTobi/yuzu_system_archives](https://github.com/FearlessTobi/yuzu_system_archives) for font choice)
|
||||
* [FontStandard](FontStandard.ttf), [FontKorean](FontKorean.ttf), [FontChineseSimplified](FontChineseSimplified.ttf) and [FontChineseTraditional](FontChineseTraditional.ttf) are using [Open Sans Regular](https://fonts.google.com/specimen/Open+Sans), which is licensed under [Apache 2.0](https://www.apache.org/licenses/LICENSE-2.0)
|
||||
* [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)
|
||||
* [FontExtendedChineseSimplified](FontExtendedChineseSimplified.ttf) is using [Source Sans Pro](https://fonts.google.com/specimen/Source+Sans+Pro), which is licensed under [Open Font License](https://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL)
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 19 KiB |
Binary file not shown.
Binary file not shown.
|
@ -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);
|
||||
}
|
|
@ -3,12 +3,13 @@
|
|||
|
||||
#include <csignal>
|
||||
#include <pthread.h>
|
||||
#include <unistd.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"
|
||||
|
@ -27,7 +28,6 @@ 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() {
|
||||
|
@ -57,12 +57,12 @@ static std::string GetTimeZoneName() {
|
|||
extern "C" JNIEXPORT void Java_emu_skyline_SkylineApplication_initializeLog(
|
||||
JNIEnv *env,
|
||||
jobject,
|
||||
jstring publicAppFilesPathJstring,
|
||||
jstring appFilesPathJstring,
|
||||
jint logLevel
|
||||
) {
|
||||
skyline::JniString publicAppFilesPath(env, publicAppFilesPathJstring);
|
||||
std::string appFilesPath{env->GetStringUTFChars(appFilesPathJstring, nullptr)};
|
||||
skyline::Logger::configLevel = static_cast<skyline::Logger::LogLevel>(logLevel);
|
||||
skyline::Logger::LoaderContext.Initialize(publicAppFilesPath + "logs/loader.sklog");
|
||||
skyline::Logger::LoaderContext.Initialize(appFilesPath + "loader.sklog");
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_executeApplication(
|
||||
|
@ -71,10 +71,9 @@ extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_executeApplication(
|
|||
jstring romUriJstring,
|
||||
jint romType,
|
||||
jint romFd,
|
||||
jobject settingsInstance,
|
||||
jstring publicAppFilesPathJstring,
|
||||
jstring privateAppFilesPathJstring,
|
||||
jstring nativeLibraryPathJstring,
|
||||
jint preferenceFd,
|
||||
jint systemLanguage,
|
||||
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
|
||||
|
@ -84,42 +83,36 @@ extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_executeApplication(
|
|||
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");
|
||||
skyline::JniString appFilesPath(env, appFilesPathJstring);
|
||||
skyline::Logger::EmulationContext.Initialize(appFilesPath + "emulation.sklog");
|
||||
|
||||
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,
|
||||
appFilesPath,
|
||||
GetTimeZoneName(),
|
||||
static_cast<skyline::language::SystemLanguage>(systemLanguage),
|
||||
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();
|
||||
|
||||
skyline::Logger::DebugNoPrefix("Launching ROM {}", skyline::JniString(env, romUriJstring));
|
||||
skyline::Logger::InfoNoPrefix("Launching ROM {}", skyline::JniString(env, romUriJstring));
|
||||
|
||||
os->Execute(romFd, static_cast<skyline::loader::RomFormat>(romType));
|
||||
} catch (std::exception &e) {
|
||||
|
@ -190,17 +183,17 @@ extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_updatePerformanceSt
|
|||
env->SetFloatField(thiz, averageFrametimeDeviationField, AverageFrametimeDeviationMs);
|
||||
}
|
||||
|
||||
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)};
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -209,7 +202,7 @@ extern "C" JNIEXPORT void JNICALL Java_emu_skyline_input_InputHandler_00024Compa
|
|||
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
|
||||
|
@ -218,19 +211,7 @@ extern "C" JNIEXPORT void JNICALL Java_emu_skyline_input_InputHandler_00024Compa
|
|||
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()};
|
||||
|
@ -243,14 +224,3 @@ extern "C" JNIEXPORT void JNICALL Java_emu_skyline_input_InputHandler_00024Compa
|
|||
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);
|
||||
}
|
||||
|
|
|
@ -13,8 +13,6 @@
|
|||
#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;
|
||||
|
||||
skyline::loader::RomFormat format{static_cast<skyline::loader::RomFormat>(jformat)};
|
||||
|
||||
skyline::Logger::SetContext(&skyline::Logger::LoaderContext);
|
||||
|
@ -51,10 +49,8 @@ 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))};
|
||||
|
@ -62,8 +58,6 @@ extern "C" JNIEXPORT jint JNICALL Java_emu_skyline_loader_RomFile_populate(JNIEn
|
|||
language = loader->nacp->GetFirstSupportedTitleLanguage();
|
||||
|
||||
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)};
|
||||
|
|
|
@ -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)};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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) {}
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
}
|
|
@ -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) {}
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
}
|
|
@ -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) {}
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
}
|
|
@ -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}));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
}
|
|
@ -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} {}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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) {}
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
}
|
|
@ -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) * static_cast<size_t>(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));
|
||||
}, static_cast<ssize_t>(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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,33 +3,59 @@
|
|||
|
||||
#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();
|
||||
void Pause() {
|
||||
outputStream->requestPause();
|
||||
}
|
||||
|
||||
void Resume();
|
||||
void Resume() {
|
||||
outputStream->requestStart();
|
||||
}
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
};
|
||||
}
|
||||
|
|
52
app/src/main/cpp/skyline/audio/adpcm_decoder.cpp
Normal file
52
app/src/main/cpp/skyline/audio/adpcm_decoder.cpp
Normal 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;
|
||||
}
|
||||
}
|
36
app/src/main/cpp/skyline/audio/adpcm_decoder.h
Normal file
36
app/src/main/cpp/skyline/audio/adpcm_decoder.h
Normal 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);
|
||||
};
|
||||
}
|
50
app/src/main/cpp/skyline/audio/common.h
Normal file
50
app/src/main/cpp/skyline/audio/common.h
Normal 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())));
|
||||
}
|
||||
}
|
||||
}
|
158
app/src/main/cpp/skyline/audio/resampler.cpp
Normal file
158
app/src/main/cpp/skyline/audio/resampler.cpp
Normal file
|
@ -0,0 +1,158 @@
|
|||
// 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++) {
|
||||
if (((inIndex + 3) * channelCount + channel) >= inputBuffer.size())
|
||||
continue;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
25
app/src/main/cpp/skyline/audio/resampler.h
Normal file
25
app/src/main/cpp/skyline/audio/resampler.h
Normal 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);
|
||||
};
|
||||
}
|
76
app/src/main/cpp/skyline/audio/track.cpp
Normal file
76
app/src/main/cpp/skyline/audio/track.cpp
Normal file
|
@ -0,0 +1,76 @@
|
|||
// 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();
|
||||
}
|
||||
}
|
75
app/src/main/cpp/skyline/audio/track.h
Normal file
75
app/src/main/cpp/skyline/audio/track.h
Normal 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();
|
||||
};
|
||||
}
|
|
@ -15,9 +15,7 @@
|
|||
#include <string>
|
||||
#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>
|
||||
|
@ -65,13 +63,13 @@ namespace skyline {
|
|||
std::shared_ptr<JvmManager> jvm;
|
||||
std::shared_ptr<Settings> settings;
|
||||
std::shared_ptr<loader::Loader> loader;
|
||||
std::shared_ptr<nce::NCE> nce;
|
||||
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<nce::NCE> nce;
|
||||
std::shared_ptr<kernel::Scheduler> scheduler;
|
||||
std::shared_ptr<input::Input> input;
|
||||
};
|
||||
|
|
|
@ -3,18 +3,13 @@
|
|||
|
||||
#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 {};
|
||||
|
||||
/**
|
||||
|
@ -55,7 +50,7 @@ namespace skyline {
|
|||
}
|
||||
};
|
||||
|
||||
SharedSpinLock blockMutex;
|
||||
std::mutex blockMutex;
|
||||
std::vector<Block> blocks{Block{}};
|
||||
|
||||
/**
|
||||
|
@ -78,6 +73,16 @@ namespace skyline {
|
|||
FlatAddressSpaceMap(VaType vaLimit, std::function<void(VaType, VaType)> unmapCallback = {});
|
||||
|
||||
FlatAddressSpaceMap() = default;
|
||||
|
||||
void Map(VaType virt, PaType phys, VaType size, ExtraBlockInfo extraInfo = {}) {
|
||||
std::scoped_lock lock(blockMutex);
|
||||
MapLocked(virt, phys, size, extraInfo);
|
||||
}
|
||||
|
||||
void Unmap(VaType virt, VaType size) {
|
||||
std::scoped_lock lock(blockMutex);
|
||||
UnmapLocked(virt, size);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -90,41 +95,12 @@ namespace skyline {
|
|||
/**
|
||||
* @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>
|
||||
template<typename VaType, VaType UnmappedVa, size_t AddressSpaceBits> 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();
|
||||
|
||||
|
@ -138,128 +114,38 @@ namespace skyline {
|
|||
}
|
||||
|
||||
/**
|
||||
* @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
|
||||
* @brief Returns a vector of all physical ranges inside of the given virtual range
|
||||
*/
|
||||
__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);
|
||||
}
|
||||
std::vector<span<u8>> TranslateRange(VaType virt, VaType size);
|
||||
|
||||
/**
|
||||
* @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 = {});
|
||||
void Read(u8 *destination, VaType virt, VaType size);
|
||||
|
||||
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);
|
||||
void Read(span <T> destination, VaType virt) {
|
||||
Read(reinterpret_cast<u8 *>(destination.data()), virt, destination.size_bytes());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T Read(VaType virt, std::function<void(span<u8>)> cpuAccessCallback = {}) {
|
||||
T Read(VaType virt) {
|
||||
T obj;
|
||||
Read(reinterpret_cast<u8 *>(&obj), virt, sizeof(T), cpuAccessCallback);
|
||||
Read(reinterpret_cast<u8 *>(&obj), virt, sizeof(T));
|
||||
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 = {});
|
||||
void Write(VaType virt, u8 *source, VaType size);
|
||||
|
||||
template<typename T>
|
||||
void Write(VaType virt, span<T> source, std::function<void(span<u8>)> cpuAccessCallback = {}) {
|
||||
void Write(VaType virt, span <T> source) {
|
||||
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);
|
||||
template<typename T>
|
||||
void Write(VaType virt, T source) {
|
||||
Write(virt, reinterpret_cast<u8 *>(&source), sizeof(T));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
#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 MM_MEMBER(returnType) template<typename VaType, VaType UnmappedVa, size_t AddressSpaceBits> requires AddressSpaceValid<VaType, AddressSpaceBits> returnType FlatMemoryManager<VaType, UnmappedVa, AddressSpaceBits>
|
||||
|
||||
#define ALLOC_MEMBER(returnType) template<typename VaType, VaType UnmappedVa, size_t AddressSpaceBits> requires AddressSpaceValid<VaType, AddressSpaceBits> returnType FlatAllocator<VaType, UnmappedVa, AddressSpaceBits>
|
||||
|
||||
|
@ -223,11 +223,20 @@ namespace skyline {
|
|||
unmapCallback(virt, size);
|
||||
}
|
||||
|
||||
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(TranslatedAddressRange)::TranslateRangeImpl(VaType virt, VaType size, std::function<void(span<u8>)> cpuAccessCallback) {
|
||||
MM_MEMBER()::~FlatMemoryManager() {
|
||||
munmap(sparseMap, SparseMapSize);
|
||||
}
|
||||
|
||||
MM_MEMBER(std::vector<span<u8>>)::TranslateRange(VaType virt, VaType size) {
|
||||
TRACE_EVENT("containers", "FlatMemoryManager::TranslateRange");
|
||||
|
||||
TranslatedAddressRange ranges;
|
||||
std::scoped_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;
|
||||
|
@ -238,21 +247,19 @@ namespace skyline {
|
|||
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);
|
||||
std::vector<span<u8>> ranges;
|
||||
|
||||
// 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});
|
||||
while (size) {
|
||||
// Return a zeroed out map to emulate sparse mappings
|
||||
if (predecessor->extraInfo.sparseMapped) {
|
||||
if (blockSize > SparseMapSize)
|
||||
throw exception("Size of the sparse map is too small to fit block of size: 0x{:X}", blockSize);
|
||||
|
||||
blockPhys = sparseMap;
|
||||
}
|
||||
|
||||
ranges.push_back(span(blockPhys, blockSize));
|
||||
|
||||
size -= blockSize;
|
||||
|
||||
if (size) {
|
||||
|
@ -265,20 +272,10 @@ namespace skyline {
|
|||
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) {
|
||||
MM_MEMBER(void)::Read(u8 *destination, VaType virt, VaType size) {
|
||||
TRACE_EVENT("containers", "FlatMemoryManager::Read");
|
||||
|
||||
std::shared_lock lock(this->blockMutex);
|
||||
std::scoped_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;
|
||||
|
@ -294,14 +291,10 @@ namespace skyline {
|
|||
if (predecessor->phys == nullptr) {
|
||||
throw exception("Page fault at 0x{:X}", predecessor->virt);
|
||||
} else {
|
||||
if (predecessor->extraInfo.sparseMapped) { // Sparse mappings read all zeroes
|
||||
if (predecessor->extraInfo.sparseMapped) // Sparse mappings read all zeroes
|
||||
std::memset(destination, 0, blockReadSize);
|
||||
} else {
|
||||
if (cpuAccessCallback)
|
||||
cpuAccessCallback(span{blockPhys, blockReadSize});
|
||||
|
||||
else
|
||||
std::memcpy(destination, blockPhys, blockReadSize);
|
||||
}
|
||||
}
|
||||
|
||||
destination += blockReadSize;
|
||||
|
@ -315,10 +308,10 @@ namespace skyline {
|
|||
}
|
||||
}
|
||||
|
||||
MM_MEMBER(void)::Write(VaType virt, u8 *source, VaType size, std::function<void(span<u8>)> cpuAccessCallback) {
|
||||
MM_MEMBER(void)::Write(VaType virt, u8 *source, VaType size) {
|
||||
TRACE_EVENT("containers", "FlatMemoryManager::Write");
|
||||
|
||||
std::shared_lock lock(this->blockMutex);
|
||||
std::scoped_lock lock(this->blockMutex);
|
||||
|
||||
VaType virtEnd{virt + size};
|
||||
|
||||
|
@ -336,12 +329,8 @@ namespace skyline {
|
|||
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});
|
||||
|
||||
if (!predecessor->extraInfo.sparseMapped) // Sparse mappings ignore writes
|
||||
std::memcpy(blockPhys, source, blockWriteSize);
|
||||
}
|
||||
}
|
||||
|
||||
source += blockWriteSize;
|
||||
|
@ -353,75 +342,7 @@ namespace skyline {
|
|||
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) {}
|
||||
|
@ -470,7 +391,7 @@ namespace skyline {
|
|||
if (this->blocks.size() <= 2)
|
||||
throw exception("Unexpected allocator state!");
|
||||
|
||||
auto searchPredecessor{std::next(this->blocks.begin())};
|
||||
auto searchPredecessor{this->blocks.begin()};
|
||||
auto searchSuccessor{std::next(searchPredecessor)};
|
||||
|
||||
while (searchSuccessor != this->blocks.end() &&
|
||||
|
@ -490,12 +411,10 @@ namespace skyline {
|
|||
}
|
||||
|
||||
ALLOC_MEMBER(void)::AllocateFixed(VaType virt, VaType size) {
|
||||
std::scoped_lock lock(this->blockMutex);
|
||||
this->MapLocked(virt, true, size, {});
|
||||
this->Map(virt, true, size);
|
||||
}
|
||||
|
||||
ALLOC_MEMBER(void)::Free(VaType virt, VaType size) {
|
||||
std::scoped_lock lock(this->blockMutex);
|
||||
this->UnmapLocked(virt, size);
|
||||
this->Unmap(virt, size);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
};
|
||||
};
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
}
|
|
@ -42,12 +42,6 @@ namespace skyline {
|
|||
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 {
|
||||
|
@ -76,6 +70,15 @@ namespace skyline {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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(util::Format(formatStr, args...)) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A deduction guide for overloads required for std::visit with std::variant
|
||||
*/
|
||||
|
|
|
@ -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, static_cast<size_t>(sizeEnd - copyOffset) * sizeof(Type));
|
||||
copyOffset -= sizeEnd;
|
||||
}
|
||||
} else {
|
||||
std::memcpy(pointer, start, static_cast<size_t>(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, static_cast<size_t>(sizeBegin - copyOffset) * sizeof(Type));
|
||||
} else {
|
||||
std::memcpy(pointer, array.begin(), static_cast<size_t>(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, static_cast<size_t>(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, static_cast<size_t>(sizePreStart) * sizeof(Type));
|
||||
|
||||
if (end == array.end())
|
||||
end = array.begin() + sizePreStart;
|
||||
else
|
||||
end += sizePreStart;
|
||||
|
||||
pointer += sizePreStart;
|
||||
size -= sizePreStart;
|
||||
|
||||
if (sizePostStart)
|
||||
std::memcpy(end, pointer, static_cast<size_t>(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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <common/trace.h>
|
||||
#include <common/spin_lock.h>
|
||||
#include <common.h>
|
||||
|
||||
namespace skyline {
|
||||
|
@ -15,12 +14,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 +49,20 @@ 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) {
|
||||
template<typename F>
|
||||
[[noreturn]] void Process(F function) {
|
||||
TRACE_EVENT_BEGIN("containers", "CircularQueue::Process");
|
||||
|
||||
while (true) {
|
||||
if (start == end) {
|
||||
std::unique_lock productionLock{productionMutex};
|
||||
std::unique_lock lock(productionMutex);
|
||||
|
||||
TRACE_EVENT_END("containers");
|
||||
preWait();
|
||||
produceCondition.wait(productionLock, [this]() { return start != end; });
|
||||
produceCondition.wait(lock, [this]() { return start != end; });
|
||||
TRACE_EVENT_BEGIN("containers", "CircularQueue::Process");
|
||||
}
|
||||
|
||||
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 +74,32 @@ namespace skyline {
|
|||
}
|
||||
}
|
||||
|
||||
Type Pop() {
|
||||
{
|
||||
std::unique_lock productionLock{productionMutex};
|
||||
produceCondition.wait(productionLock, [this]() { return start != end; });
|
||||
}
|
||||
|
||||
std::scoped_lock comsumptionLock{consumptionMutex};
|
||||
auto next{start + 1};
|
||||
void Push(const Type &item) {
|
||||
std::unique_lock lock(productionMutex);
|
||||
auto next{end + 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;
|
||||
if (next == start) {
|
||||
std::unique_lock consumeLock(consumptionMutex);
|
||||
consumeCondition.wait(consumeLock, [=]() { return next != start; });
|
||||
}
|
||||
*next = item;
|
||||
end = next;
|
||||
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 +107,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();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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()) {}
|
||||
};
|
||||
}
|
|
@ -1,69 +0,0 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <unistd.h>
|
||||
#include "base.h"
|
||||
|
||||
namespace skyline {
|
||||
/**
|
||||
* @brief A RAII wrapper around Linux file descriptors which automatically closes the file descriptor when it goes out of scope and duplicates them on copies
|
||||
* @note This class should **always** be moved rather than copied where possible to avoid a system call for duplicating file descriptors
|
||||
*/
|
||||
class FileDescriptor {
|
||||
private:
|
||||
int fd;
|
||||
|
||||
public:
|
||||
FileDescriptor() : fd(-1) {}
|
||||
|
||||
FileDescriptor(int fd) : fd(fd) {}
|
||||
|
||||
FileDescriptor &operator=(int newFd) {
|
||||
if (fd != -1)
|
||||
close(fd);
|
||||
fd = newFd;
|
||||
return *this;
|
||||
}
|
||||
|
||||
FileDescriptor(const FileDescriptor &other) : fd(dup(other.fd)) {
|
||||
if (fd == -1)
|
||||
throw exception("Failed to duplicate file descriptor: {}", strerror(errno));
|
||||
}
|
||||
|
||||
FileDescriptor &operator=(const FileDescriptor &other) {
|
||||
if (fd != -1)
|
||||
close(fd);
|
||||
fd = dup(other.fd);
|
||||
if (fd == -1)
|
||||
throw exception("Failed to duplicate file descriptor: {}", strerror(errno));
|
||||
return *this;
|
||||
}
|
||||
|
||||
FileDescriptor(FileDescriptor &&other) : fd(other.fd) {
|
||||
other.fd = -1;
|
||||
}
|
||||
|
||||
FileDescriptor &operator=(FileDescriptor &&other) {
|
||||
if (fd != -1)
|
||||
close(fd);
|
||||
fd = other.fd;
|
||||
other.fd = -1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
~FileDescriptor() {
|
||||
if (fd != -1)
|
||||
close(fd);
|
||||
}
|
||||
|
||||
operator int() const {
|
||||
return fd;
|
||||
}
|
||||
|
||||
int operator*() const {
|
||||
return fd;
|
||||
}
|
||||
};
|
||||
}
|
|
@ -1,119 +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 "span.h"
|
||||
|
||||
namespace skyline {
|
||||
/**
|
||||
* @brief Stores a list of non-overlapping intervals
|
||||
*/
|
||||
template<typename SizeType>
|
||||
class IntervalList {
|
||||
public:
|
||||
using DifferenceType = decltype(std::declval<SizeType>() - std::declval<SizeType>());
|
||||
|
||||
struct Interval {
|
||||
SizeType offset;
|
||||
SizeType end;
|
||||
|
||||
Interval() = default;
|
||||
|
||||
Interval(SizeType offset, SizeType end) : offset{offset}, end{end} {}
|
||||
|
||||
Interval(span<u8> interval) : offset{interval.data()}, end{interval.data() + interval.size()} {}
|
||||
};
|
||||
|
||||
private:
|
||||
std::vector<Interval> intervals; //!< A list of intervals sorted by their end offset
|
||||
|
||||
public:
|
||||
struct QueryResult {
|
||||
bool enclosed; //!< If the given offset was enclosed by an interval
|
||||
DifferenceType size; //!< Size of the interval starting from the query offset, or distance to the next interval if `enclosed` is false (if there is no next interval size is 0)
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Clears all inserted intervals from the map
|
||||
*/
|
||||
void Clear() {
|
||||
intervals.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Forces future accesses to the given interval to use the shadow copy
|
||||
*/
|
||||
void Insert(Interval entry) {
|
||||
auto firstIt{std::lower_bound(intervals.begin(), intervals.end(), entry, [](const auto &lhs, const auto &rhs) {
|
||||
return lhs.end < rhs.offset;
|
||||
})}; // Lowest offset entry that (maybe) overlaps with the new entry
|
||||
|
||||
if (firstIt == intervals.end() || firstIt->offset >= entry.end) {
|
||||
intervals.insert(firstIt, entry);
|
||||
return;
|
||||
}
|
||||
// Now firstIt will always overlap
|
||||
|
||||
auto lastIt{firstIt}; // Highest offset entry that overlaps with the new entry
|
||||
while (std::next(lastIt) != intervals.end() && std::next(lastIt)->offset < entry.end)
|
||||
lastIt++;
|
||||
|
||||
// Since firstIt and lastIt both are guaranteed to overlap, max them to get the new entry's end
|
||||
SizeType end{std::max(std::max(firstIt->end, entry.end), lastIt->end)};
|
||||
|
||||
// Erase all overlapping entries but the first
|
||||
auto eraseStartIt{std::next(firstIt)};
|
||||
auto eraseEndIt{std::next(lastIt)};
|
||||
if (eraseStartIt != eraseEndIt) {
|
||||
lastIt = intervals.erase(eraseStartIt, eraseEndIt);
|
||||
firstIt = std::prev(lastIt);
|
||||
}
|
||||
|
||||
firstIt->offset = std::min(entry.offset, firstIt->offset);
|
||||
firstIt->end = end;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Merge the given interval into the list
|
||||
*/
|
||||
void Merge(const IntervalList<SizeType> &list) {
|
||||
for (auto &entry : list.intervals)
|
||||
Insert(entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A struct describing the interval containing `offset`
|
||||
*/
|
||||
QueryResult Query(SizeType offset) {
|
||||
auto it{std::lower_bound(intervals.begin(), intervals.end(), offset, [](const auto &lhs, const auto &rhs) {
|
||||
return lhs.end < rhs;
|
||||
})}; // Lowest offset entry that (maybe) overlaps with the new entry
|
||||
|
||||
if (it == intervals.end()) // No overlaps past offset
|
||||
return {false, {}};
|
||||
else if (it->offset > offset) // No overlap, return the distance to the next possible overlap
|
||||
return {false, it->offset - offset};
|
||||
else // Overlap, return the distance to the end of the overlap
|
||||
return {true, it->end - offset};
|
||||
}
|
||||
|
||||
/**
|
||||
* @return If the given interval intersects with any of the intervals in the list
|
||||
*/
|
||||
bool Intersect(Interval interval) {
|
||||
SizeType offset{interval.offset};
|
||||
while (offset < interval.end) {
|
||||
if (auto result{Query(offset)}; result.enclosed)
|
||||
return true;
|
||||
else if (result.size)
|
||||
offset += result.size;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
|
@ -1,248 +0,0 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "utils.h"
|
||||
#include "span.h"
|
||||
|
||||
namespace skyline {
|
||||
/**
|
||||
* @brief An associative map with groups of overlapping intervals associated with a value with support for range-based lookups
|
||||
* @tparam AddressType The type of address used for lookups and insertions
|
||||
* @tparam EntryType The type of entry that is stored for a collection of intervals
|
||||
*/
|
||||
template<typename AddressType, typename EntryType>
|
||||
class IntervalMap {
|
||||
public:
|
||||
struct Interval {
|
||||
AddressType start;
|
||||
AddressType end;
|
||||
|
||||
Interval(AddressType start, AddressType end) : start(start), end(end) {}
|
||||
|
||||
size_t Size() const {
|
||||
return static_cast<size_t>(end - start);
|
||||
}
|
||||
|
||||
Interval Align(size_t alignment) const {
|
||||
return Interval(util::AlignDown(start, alignment), util::AlignUp(end, alignment));
|
||||
}
|
||||
|
||||
bool operator==(const Interval &other) const {
|
||||
return start == other.start && end == other.end;
|
||||
}
|
||||
|
||||
bool operator<(AddressType address) const {
|
||||
return start < address;
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
struct EntryGroup {
|
||||
std::vector<Interval> intervals;
|
||||
EntryType value;
|
||||
|
||||
EntryGroup(Interval interval, EntryType value) : intervals(1, interval), value(std::move(value)) {}
|
||||
|
||||
EntryGroup(span<Interval> intervals, EntryType value) : intervals(intervals), value(std::move(value)) {}
|
||||
|
||||
template<typename T>
|
||||
EntryGroup(span<span<T>> lIntervals, EntryType value) : value(std::move(value)) {
|
||||
intervals.reserve(lIntervals.size());
|
||||
for (const auto &interval : lIntervals)
|
||||
intervals.emplace_back(interval.data(), interval.data() + interval.size());
|
||||
}
|
||||
};
|
||||
|
||||
std::list<EntryGroup> groups;
|
||||
|
||||
public:
|
||||
using GroupHandle = typename std::list<EntryGroup>::iterator;
|
||||
|
||||
private:
|
||||
struct Entry : public Interval {
|
||||
GroupHandle group;
|
||||
|
||||
Entry(AddressType start, AddressType end, GroupHandle group) : Interval{start, end}, group{group} {}
|
||||
|
||||
bool operator==(const GroupHandle &pGroup) const {
|
||||
return group == pGroup;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param entries References to entries inside an EntryGroup, it is UB to supply a reference to an entry that is not in an EntryGroup
|
||||
* @return If any entry in the supplied entries belongs to the specified group
|
||||
*/
|
||||
static bool IsGroupInEntries(GroupHandle group, const std::vector<std::reference_wrapper<EntryType>> &entries) {
|
||||
for (const auto &entry : entries) {
|
||||
auto entryPtr{reinterpret_cast<u8 *>(&entry.get())};
|
||||
auto entryGroup{reinterpret_cast<EntryGroup *>(entryPtr - offsetof(EntryGroup, value))}; // We exploit the fact that the entry is stored in the EntryGroup structure to get a pointer to it
|
||||
if (entryGroup == &*group)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<Entry> entries;
|
||||
|
||||
public:
|
||||
IntervalMap() = default;
|
||||
|
||||
IntervalMap(const IntervalMap &) = delete;
|
||||
|
||||
IntervalMap(IntervalMap &&) = delete;
|
||||
|
||||
IntervalMap &operator=(const IntervalMap &) = delete;
|
||||
|
||||
IntervalMap &operator=(IntervalMap &&) = delete;
|
||||
|
||||
GroupHandle Insert(AddressType start, AddressType end, EntryType value) {
|
||||
GroupHandle group{groups.emplace(groups.begin(), Interval{start, end}, value)};
|
||||
entries.emplace(std::lower_bound(entries.begin(), entries.end(), end), start, end, group);
|
||||
return group;
|
||||
}
|
||||
|
||||
GroupHandle Insert(span<Interval> intervals, EntryType value) {
|
||||
GroupHandle group{groups.emplace(groups.begin(), intervals, value)};
|
||||
for (const auto &interval : intervals)
|
||||
entries.emplace(std::lower_bound(entries.begin(), entries.end(), interval.end), interval.start, interval.end, group);
|
||||
return group;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
GroupHandle Insert(span<span<T>> intervals, EntryType value) requires std::is_pointer_v<AddressType> {
|
||||
GroupHandle group{groups.emplace(groups.begin(), intervals, std::move(value))};
|
||||
for (const auto &interval : intervals)
|
||||
entries.emplace(std::lower_bound(entries.begin(), entries.end(), interval.data()), interval.data(), interval.data() + interval.size(), group);
|
||||
return group;
|
||||
}
|
||||
|
||||
void Remove(GroupHandle group) {
|
||||
for (auto it{entries.begin()}; it != entries.end();) {
|
||||
if (it->group == group)
|
||||
it = entries.erase(it);
|
||||
else
|
||||
it++;
|
||||
}
|
||||
groups.erase(group);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A nullable pointer to any entry overlapping with the given address
|
||||
*/
|
||||
EntryType *Get(AddressType address) {
|
||||
for (auto entryIt{std::lower_bound(entries.begin(), entries.end(), address)}; entryIt != entries.begin() && (--entryIt)->start <= address;)
|
||||
if (entryIt->end > address)
|
||||
return &entryIt->group->value;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A vector of non-nullable pointers to entries overlapping with the given interval
|
||||
*/
|
||||
std::vector<std::reference_wrapper<EntryType>> GetRange(Interval interval) {
|
||||
std::vector<std::reference_wrapper<EntryType>> result;
|
||||
for (auto entry{std::lower_bound(entries.begin(), entries.end(), interval.end)}; entry != entries.begin() && (--entry)->start < interval.end;)
|
||||
if (entry->end > interval.start && !IsGroupInEntries(entry->group, result))
|
||||
result.emplace_back(entry->group->value);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return All entries overlapping with the given interval and a list of intervals they recursively cover with alignment for page-based lookup semantics
|
||||
* @note This function is specifically designed for memory faulting lookups and has design-decisions that correspond to that which might not work for other uses
|
||||
*/
|
||||
template<size_t Alignment>
|
||||
std::pair<std::vector<std::reference_wrapper<EntryType>>, std::vector<Interval>> GetAlignedRecursiveRange(Interval interval) {
|
||||
std::vector<std::reference_wrapper<EntryType>> queryEntries;
|
||||
std::vector<Interval> intervals;
|
||||
|
||||
interval = interval.Align(Alignment);
|
||||
|
||||
auto entry{std::lower_bound(entries.begin(), entries.end(), interval.end)};
|
||||
bool exclusiveEntry{entry == entries.begin() || std::prev(entry) == entries.begin() || std::prev(entry, 2)->start >= interval.end}; //!< If this entry exclusively occupies an aligned region
|
||||
while (entry != entries.begin() && (--entry)->start < interval.end) {
|
||||
if (entry->end > interval.start && !IsGroupInEntries(entry->group, queryEntries)) {
|
||||
// We found a unique and overlapping entry in the supplied interval
|
||||
queryEntries.emplace_back(entry->group->value);
|
||||
|
||||
for (const auto &entryInterval : entry->group->intervals) {
|
||||
/* We need to find intervals that are covered by this entry and adding which will minimize future calls to this function, these are designed with memory faulting in mind. There's a few cases to consider:
|
||||
* 1. The entry exclusively occupies the lookup region - Entries are assumed to be rarely accessed in a partial manner, so we want to get add all intervals covered by the entry which includes all entries on those intervals and all exclusive intervals covered by those entries recursively
|
||||
* 2. The entry doesn't exclusively occupy the lookup region - We want to get all exclusive intervals covered by the entry where the entry is the only entry on those intervals, this is as we don't know what entry will be read in its entirety
|
||||
* 3. The entry doesn't exclusively occupy the lookup region, but the interval matches the entry's interval - This case is implicitly the same as (1) as we want to add all entries overlapping with the current interval
|
||||
*/
|
||||
|
||||
auto alignedEntryInterval{entryInterval.Align(Alignment)};
|
||||
|
||||
if (exclusiveEntry || entryInterval == *entry) {
|
||||
// Case (1)/(3) - We want to add all entries overlapping with the current interval and their exclusive intervals recursively
|
||||
for (auto recursedEntry{std::lower_bound(entries.begin(), entries.end(), alignedEntryInterval.end)}; recursedEntry != entries.begin() && (--recursedEntry)->start < alignedEntryInterval.end;) {
|
||||
if (recursedEntry->end > alignedEntryInterval.start && recursedEntry->group != entry->group && !IsGroupInEntries(recursedEntry->group, queryEntries)) {
|
||||
queryEntries.emplace_back(recursedEntry->group->value);
|
||||
|
||||
for (const auto &entryInterval2 : recursedEntry->group->intervals) {
|
||||
// Similar to case (2) below but for the recursed entry
|
||||
bool exclusiveIntervalEntry{true};
|
||||
auto alignedEntryInterval2{entryInterval2.Align(Alignment)};
|
||||
|
||||
auto recursedEntry2{std::lower_bound(entries.begin(), entries.end(), alignedEntryInterval2.end)};
|
||||
for (; recursedEntry2 != entries.begin() && (--recursedEntry2)->start < alignedEntryInterval2.end;) {
|
||||
if (recursedEntry2->end > alignedEntryInterval2.start && recursedEntry2->group != recursedEntry->group && recursedEntry2->group != entry->group) {
|
||||
exclusiveIntervalEntry = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (exclusiveIntervalEntry)
|
||||
intervals.emplace(std::lower_bound(intervals.begin(), intervals.end(), alignedEntryInterval2.end), alignedEntryInterval2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
intervals.emplace(std::lower_bound(intervals.begin(), intervals.end(), alignedEntryInterval.start), alignedEntryInterval);
|
||||
} else {
|
||||
// Case (2) - We only want to add this interval if it only contains the entry
|
||||
bool exclusiveIntervalEntry{true};
|
||||
|
||||
for (auto recursedEntry{std::lower_bound(entries.begin(), entries.end(), alignedEntryInterval.end)}; recursedEntry != entries.begin() && (--recursedEntry)->start < alignedEntryInterval.end;) {
|
||||
if (recursedEntry->end > alignedEntryInterval.start && recursedEntry->group != entry->group) {
|
||||
exclusiveIntervalEntry = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (exclusiveIntervalEntry)
|
||||
intervals.emplace(std::lower_bound(intervals.begin(), intervals.end(), alignedEntryInterval.start), alignedEntryInterval);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Coalescing pass for combining all intervals that are adjacent to each other
|
||||
for (auto it{intervals.begin()}; it != intervals.end();) {
|
||||
auto next{std::next(it)};
|
||||
if (next != intervals.end() && it->end >= next->start) {
|
||||
if (it->start > next->start)
|
||||
it->start = next->start;
|
||||
if (it->end < next->end)
|
||||
it->end = next->end;
|
||||
it = std::prev(intervals.erase(next));
|
||||
} else {
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
return std::pair{queryEntries, intervals};
|
||||
}
|
||||
|
||||
template<size_t Alignment>
|
||||
std::pair<std::vector<std::reference_wrapper<EntryType>>, std::vector<Interval>> GetAlignedRecursiveRange(AddressType address) {
|
||||
return GetAlignedRecursiveRange<Alignment>(Interval{address, address + 1});
|
||||
}
|
||||
};
|
||||
}
|
|
@ -22,7 +22,7 @@ namespace skyline {
|
|||
*/
|
||||
#define LANGUAGES \
|
||||
LANG_ENTRY(Japanese, ja, 0, 2, MAP) \
|
||||
LANG_ENTRY(AmericanEnglish, en-US, 1, 0, MAP) \
|
||||
LANG_ENTRY(AmericanEnglish, en-us, 1, 0, MAP) \
|
||||
LANG_ENTRY(French, fr, 2, 3, MAP) \
|
||||
LANG_ENTRY(German, de, 3, 4, MAP) \
|
||||
LANG_ENTRY(Italian, it, 4, 7, MAP) \
|
||||
|
@ -106,53 +106,4 @@ namespace skyline {
|
|||
|
||||
#undef LANGUAGES
|
||||
}
|
||||
|
||||
namespace region {
|
||||
/**
|
||||
* @brief The list of all regions
|
||||
* @url https://switchbrew.org/wiki/Settings_services#RegionCode_2
|
||||
*/
|
||||
enum class RegionCode : i32 {
|
||||
Auto = -1, // Automatically determine region based on the selected language
|
||||
Japan = 0,
|
||||
Usa = 1,
|
||||
Europe = 2,
|
||||
Australia = 3,
|
||||
HongKongTaiwanKorea = 4,
|
||||
China = 5,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Returns the region code for the given system language
|
||||
*/
|
||||
constexpr region::RegionCode GetRegionCodeForSystemLanguage(language::SystemLanguage systemLanguage) {
|
||||
switch (systemLanguage) {
|
||||
case language::SystemLanguage::Japanese:
|
||||
return region::RegionCode::Japan;
|
||||
case language::SystemLanguage::AmericanEnglish:
|
||||
case language::SystemLanguage::CanadianFrench:
|
||||
case language::SystemLanguage::LatinAmericanSpanish:
|
||||
case language::SystemLanguage::BrazilianPortuguese:
|
||||
return region::RegionCode::Usa;
|
||||
case language::SystemLanguage::French:
|
||||
case language::SystemLanguage::German:
|
||||
case language::SystemLanguage::Italian:
|
||||
case language::SystemLanguage::Spanish:
|
||||
case language::SystemLanguage::Dutch:
|
||||
case language::SystemLanguage::Portuguese:
|
||||
case language::SystemLanguage::Russian:
|
||||
case language::SystemLanguage::BritishEnglish:
|
||||
return region::RegionCode::Europe;
|
||||
case language::SystemLanguage::Chinese:
|
||||
case language::SystemLanguage::SimplifiedChinese:
|
||||
case language::SystemLanguage::TraditionalChinese:
|
||||
return region::RegionCode::China;
|
||||
case language::SystemLanguage::Taiwanese:
|
||||
case language::SystemLanguage::Korean:
|
||||
return region::RegionCode::HongKongTaiwanKorea;
|
||||
default:
|
||||
throw exception("Invalid system language: {}", systemLanguage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,142 +0,0 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <common.h>
|
||||
|
||||
namespace skyline {
|
||||
/**
|
||||
* @brief Typeless allocation state holder for LinearAllocator<T>
|
||||
* @tparam NewChunkSize Step size in bytes for overflow chunk allocations
|
||||
*/
|
||||
template<size_t NewChunkSize = (1024 * 1024)> // Default to 1MB
|
||||
class LinearAllocatorState {
|
||||
private:
|
||||
std::vector<u8> mainChunk; //!< The primary backing for the allocator, will grow during `Reset` calls if the previous set of allocations overflowed
|
||||
std::list<std::vector<u8>> overflowChunks; //!< Overflow backing chunks used to hold allocations that can't fit in `mainChunk` until it can be resized
|
||||
u8 *ptr{}; //!< Points to a free region of memory of size `chunkRemainingBytes`
|
||||
size_t chunkRemainingBytes{NewChunkSize}; //!< Remaining bytes left in the current chunk
|
||||
|
||||
size_t allocCount{}; //!< The number of currently unfreed allocations
|
||||
|
||||
public:
|
||||
LinearAllocatorState() {
|
||||
mainChunk.reserve(NewChunkSize);
|
||||
ptr = mainChunk.data();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Allocates a contiguous chunk of memory of size `size` aligned to the maximum possible required alignment
|
||||
*/
|
||||
u8 *Allocate(size_t unalignedSize, bool track = true) {
|
||||
// Align the size to the maximum required alignment for standard types
|
||||
size_t size{util::AlignUp(unalignedSize, alignof(std::max_align_t))};
|
||||
|
||||
// Allocated memory cannot span across multiple chunks
|
||||
if (size > NewChunkSize)
|
||||
throw std::bad_alloc();
|
||||
|
||||
if (chunkRemainingBytes < size) {
|
||||
// If there is no space left in the current chunk allocate a new overflow one
|
||||
auto &newChunk{overflowChunks.emplace_back()};
|
||||
newChunk.reserve(NewChunkSize);
|
||||
ptr = newChunk.data();
|
||||
chunkRemainingBytes = NewChunkSize;
|
||||
}
|
||||
|
||||
auto allocatedPtr{ptr};
|
||||
|
||||
// Move ourselves along
|
||||
chunkRemainingBytes -= size;
|
||||
ptr += size;
|
||||
|
||||
if (track)
|
||||
allocCount++;
|
||||
|
||||
return allocatedPtr;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T *AllocateUntracked() {
|
||||
return reinterpret_cast<T *>(Allocate(sizeof(T), false));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
span<T> AllocateUntracked(size_t count) {
|
||||
return {reinterpret_cast<T *>(Allocate(sizeof(T) * count, false)), count};
|
||||
}
|
||||
|
||||
template<typename T, class... Args>
|
||||
T *EmplaceUntracked(Args &&... args) {
|
||||
return std::construct_at(AllocateUntracked<T>(), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Decreases allocation count, should be called an equal number of times to `Allocate`
|
||||
*/
|
||||
void Deallocate() {
|
||||
allocCount--;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Resizes the main chunk to fit any previously needed overflow chunks and allows memory to be reused again for further allocations
|
||||
* @note There **must** be no allocations leftover when this is called
|
||||
*/
|
||||
void Reset() {
|
||||
if (allocCount)
|
||||
// If we still have allocations remaining then throw
|
||||
throw std::bad_alloc();
|
||||
|
||||
if (size_t overflowSize{overflowChunks.size() * NewChunkSize}) {
|
||||
// Expand the main chunk so that it can fit any previously needed overflow chunks
|
||||
overflowChunks.clear();
|
||||
mainChunk.reserve(overflowSize + mainChunk.capacity());
|
||||
}
|
||||
|
||||
ptr = mainChunk.data();
|
||||
chunkRemainingBytes = mainChunk.capacity();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Linear allocator conforming to the C++ 'Allocator' named requirement, forwards all allocation requests to the passed in state
|
||||
*/
|
||||
template<typename T, typename State = LinearAllocatorState<>>
|
||||
class LinearAllocator {
|
||||
private:
|
||||
State &state; //!< Backing allocation state holder
|
||||
|
||||
public:
|
||||
using value_type = T;
|
||||
|
||||
template<typename, typename> friend class LinearAllocator; //!< Required for copy ctor from other types
|
||||
|
||||
/**
|
||||
* @note This is explicitly not explicit to avoid the need to repeat the allocator type when constructing a new object
|
||||
*/
|
||||
LinearAllocator(State &state) : state(state) {}
|
||||
|
||||
template<typename U>
|
||||
LinearAllocator(const LinearAllocator<U> &other) : state(other.state) {};
|
||||
|
||||
template<typename U>
|
||||
LinearAllocator(LinearAllocator<U> &&other) : state(other.state) {};
|
||||
|
||||
[[nodiscard]] T *allocate(size_t n) {
|
||||
return reinterpret_cast<T *>(state.Allocate(sizeof(T) * n));
|
||||
}
|
||||
|
||||
void deallocate(T *obj, size_t n) noexcept {
|
||||
state.Deallocate();
|
||||
}
|
||||
|
||||
bool operator==(const LinearAllocator &other) const noexcept {
|
||||
return &state == &other.state;
|
||||
}
|
||||
|
||||
bool operator!=(const LinearAllocator &other) const noexcept {
|
||||
return &state != &other.state;
|
||||
}
|
||||
};
|
||||
}
|
|
@ -1,78 +0,0 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <atomic>
|
||||
|
||||
namespace skyline {
|
||||
/**
|
||||
* @brief A wrapper around a shared_ptr<T> which can be utilized to perform transactional atomic operations to lock the underlying resource and attain stability in the pointer value
|
||||
* @note Any operations directly accessing the value are **NOT** atomic and should be done after a locking transaction
|
||||
*/
|
||||
template<typename Type>
|
||||
class LockableSharedPtr : public std::shared_ptr<Type> {
|
||||
public:
|
||||
using std::shared_ptr<Type>::shared_ptr;
|
||||
using std::shared_ptr<Type>::operator=;
|
||||
|
||||
LockableSharedPtr(std::shared_ptr<Type> &&ptr) : std::shared_ptr<Type>{std::move(ptr)} {}
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief A lock function for the underlying object that conforms to the BasicLockable named requirement
|
||||
*/
|
||||
static void DefaultLockFunction(Type *object) {
|
||||
object->lock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief An unlock function for the underlying object that conforms to the BasicLockable named requirement
|
||||
*/
|
||||
static void DefaultUnlockFunction(Type *object) {
|
||||
object->unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A try_lock function for the underlying object that conforms to the Lockable named requirement
|
||||
*/
|
||||
static bool DefaultTryLockFunction(Type *object) {
|
||||
return object->try_lock();
|
||||
}
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Locks the underlying object with the supplied lock/unlock functions
|
||||
*/
|
||||
template<typename LockFunction = typeof(DefaultLockFunction), typename UnlockFunction = typeof(DefaultUnlockFunction)>
|
||||
void Lock(LockFunction lock = DefaultLockFunction, UnlockFunction unlock = DefaultUnlockFunction) const {
|
||||
while (true) {
|
||||
auto object{this->get()};
|
||||
lock(object);
|
||||
|
||||
if (this->get() == object)
|
||||
return;
|
||||
|
||||
unlock(object);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Attempts to lock the underlying object with the supplied try_lock/unlock functions
|
||||
*/
|
||||
template<typename TryLockFunction = typeof(DefaultTryLockFunction), typename UnlockFunction = typeof(DefaultUnlockFunction)>
|
||||
bool TryLock(TryLockFunction tryLock = DefaultTryLockFunction, UnlockFunction unlock = DefaultUnlockFunction) const {
|
||||
while (true) {
|
||||
auto object{this->get()};
|
||||
bool wasLocked{tryLock(object)};
|
||||
|
||||
if (this->get() == object)
|
||||
return wasLocked;
|
||||
|
||||
if (wasLocked)
|
||||
unlock(object);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
|
@ -12,18 +12,10 @@ namespace skyline {
|
|||
}
|
||||
|
||||
void Logger::LoggerContext::Finalize() {
|
||||
std::scoped_lock lock{mutex};
|
||||
logFile.close();
|
||||
}
|
||||
|
||||
void Logger::LoggerContext::TryFlush() {
|
||||
std::unique_lock lock(mutex, std::try_to_lock);
|
||||
if (lock)
|
||||
logFile.flush();
|
||||
}
|
||||
|
||||
void Logger::LoggerContext::Flush() {
|
||||
std::scoped_lock lock{mutex};
|
||||
logFile.flush();
|
||||
}
|
||||
|
||||
|
@ -65,7 +57,7 @@ namespace skyline {
|
|||
}
|
||||
|
||||
void Logger::LoggerContext::Write(const std::string &str) {
|
||||
std::scoped_lock guard{mutex};
|
||||
std::lock_guard guard(mutex);
|
||||
logFile << str;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -40,8 +40,6 @@ namespace skyline {
|
|||
|
||||
void Finalize();
|
||||
|
||||
void TryFlush();
|
||||
|
||||
void Flush();
|
||||
|
||||
void Write(const std::string &str);
|
||||
|
@ -134,50 +132,38 @@ namespace skyline {
|
|||
|
||||
template<typename... Args>
|
||||
static void Debug(FunctionString<const char *> formatString, Args &&... args) {
|
||||
#ifndef NDEBUG
|
||||
if (LogLevel::Debug <= configLevel)
|
||||
Write(LogLevel::Debug, util::Format(*formatString, args...));
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
static void Debug(FunctionString<std::string> formatString, Args &&... args) {
|
||||
#ifndef NDEBUG
|
||||
if (LogLevel::Debug <= configLevel)
|
||||
Write(LogLevel::Debug, util::Format(*formatString, args...));
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename S, typename... Args>
|
||||
static void DebugNoPrefix(S formatString, Args &&... args) {
|
||||
#ifndef NDEBUG
|
||||
if (LogLevel::Debug <= configLevel)
|
||||
Write(LogLevel::Debug, util::Format(formatString, args...));
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
static void Verbose(FunctionString<const char *> formatString, Args &&... args) {
|
||||
#ifndef NDEBUG
|
||||
if (LogLevel::Verbose <= configLevel)
|
||||
Write(LogLevel::Verbose, util::Format(*formatString, args...));
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
static void Verbose(FunctionString<std::string> formatString, Args &&... args) {
|
||||
#ifndef NDEBUG
|
||||
if (LogLevel::Verbose <= configLevel)
|
||||
Write(LogLevel::Verbose, util::Format(*formatString, args...));
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename S, typename... Args>
|
||||
static void VerboseNoPrefix(S formatString, Args &&... args) {
|
||||
#ifndef NDEBUG
|
||||
if (LogLevel::Verbose <= configLevel)
|
||||
Write(LogLevel::Verbose, util::Format(formatString, args...));
|
||||
#endif
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -14,14 +14,14 @@
|
|||
* @brief Creates a function to convert an enumerant to its string representation at runtime
|
||||
* @example ENUM_STRING(Example, { ENUM_CASE(A); ENUM_CASE(B); })
|
||||
*/
|
||||
#define ENUM_STRING(name, cases) \
|
||||
constexpr static const char *ToString(name value) { \
|
||||
using ENUM_TYPE = name; \
|
||||
switch (value) { \
|
||||
cases \
|
||||
default: \
|
||||
return "Unknown"; \
|
||||
} \
|
||||
#define ENUM_STRING(name, cases) \
|
||||
constexpr const char *ToString(name value) { \
|
||||
using ENUM_TYPE = name; \
|
||||
switch (value) { \
|
||||
cases \
|
||||
default: \
|
||||
return "Unknown"; \
|
||||
} \
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -29,7 +29,7 @@
|
|||
*/
|
||||
#define ENUM_CASE_PAIR(key, value) \
|
||||
case ENUM_TYPE::key: \
|
||||
return value
|
||||
return value
|
||||
|
||||
/**
|
||||
* @brief Creates a switch case statement to convert an enumerant to the given values
|
||||
|
|
|
@ -22,9 +22,7 @@ namespace skyline {
|
|||
*/
|
||||
constexpr Result() = default;
|
||||
|
||||
constexpr Result(u16 module, u16 id) : module{module}, id{id} {}
|
||||
|
||||
constexpr explicit Result(u32 raw) : raw{raw} {}
|
||||
constexpr explicit Result(u16 module, u16 id) : module(module), id(id) {}
|
||||
|
||||
constexpr operator u32() const {
|
||||
return raw;
|
||||
|
|
|
@ -1,170 +0,0 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <sys/mman.h>
|
||||
#include "span.h"
|
||||
|
||||
namespace skyline {
|
||||
/**
|
||||
* @brief A two-level segment table implementation that utilizes kernel-backed demand paging, the multi-level aspect is to allow for a SegmentType that applies to a large amount of segments to be mapped at a lower granularity (L2) and go to a higher granuality (L1) as needed
|
||||
* @tparam SegmentType The type of segment to use for the segment table entries, this must be a trivial type as it'll be returned filled with 0s when a segment is unset
|
||||
* @tparam Size The size of the table in terms of units (not segments unless L1Bits = 1), this represents size in bytes for a segment table covering address space
|
||||
* @tparam L1Bits The size of an L1 segment as a power of 2, this should be lower than L2 and will determine the minimum granularity of the table
|
||||
* @tparam L2Bits The size of an L2 segment as a power of 2, this should be higher than L2 and will determine the maximum granularity of the table
|
||||
* @tparam EnablePointerAccess Whether or not to enable pointer access to the table, this is useful when host addresses are used as the key for the table
|
||||
* @note This class is **NOT** thread-safe, any access to the table must be protected by a mutex
|
||||
*/
|
||||
template<typename SegmentType, size_t Size, size_t L1Bits, size_t L2Bits, bool EnablePointerAccess = false> requires std::is_trivial_v<SegmentType>
|
||||
class SegmentTable {
|
||||
private:
|
||||
static constexpr size_t L1Size{1 << L1Bits}, L1Entries{util::DivideCeil(Size, L1Size)};
|
||||
span<SegmentType, L1Entries> level1Table; //!< The first level of the segment table, this is the highest granularity of the table and contains only the segment
|
||||
|
||||
/**
|
||||
* @brief An entry in a segment table level aside from the lowest level which directly holds the type, this has an associated segment and a flag if the lookup should move to a higher granularity (and the corresponding lower level)
|
||||
*/
|
||||
struct alignas(8) RangeEntry {
|
||||
bool valid; //!< If the associated segment is valid, the entry must not be accessed without checking validity first and an invalid entry implies going to the next level
|
||||
SegmentType segment; //!< The segment associated with the entry, this is 0'd out if the entry is unset
|
||||
};
|
||||
|
||||
static constexpr size_t L2Size{1 << L2Bits}, L2Entries{util::DivideCeil(Size, L2Size)}, L1inL2Count{L1Size / L2Size};
|
||||
span<RangeEntry, L2Entries> level2Table; //!< The second level of the segment table, this is the lowest granularity of the table
|
||||
|
||||
template<typename Type, size_t Amount>
|
||||
static span<Type, Amount> AllocateTable() {
|
||||
void *ptr{mmap(nullptr, Amount * sizeof(Type), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE, -1, 0)};
|
||||
if (ptr == MAP_FAILED)
|
||||
throw exception{"Failed to allocate 0x{:X} bytes of memory for segment table: {}", Amount * sizeof(Type), strerror(errno)};
|
||||
return span<Type, Amount>(static_cast<Type *>(ptr), Amount);
|
||||
}
|
||||
|
||||
public:
|
||||
SegmentTable() : level1Table{AllocateTable<SegmentType, L1Entries>()}, level2Table{AllocateTable<RangeEntry, L2Entries>()} {}
|
||||
|
||||
SegmentTable(const SegmentTable &other) : level1Table{AllocateTable<SegmentType, L1Entries>()}, level2Table{AllocateTable<RangeEntry, L2Entries>()} {
|
||||
level1Table.copy_from(other.level1Table);
|
||||
level2Table.copy_from(other.level2Table);
|
||||
}
|
||||
|
||||
SegmentTable &operator=(const SegmentTable &other) {
|
||||
level1Table = AllocateTable<SegmentType, L1Entries>();
|
||||
level2Table = AllocateTable<RangeEntry, L2Entries>();
|
||||
|
||||
level1Table.copy_from(other.level1Table);
|
||||
level2Table.copy_from(other.level2Table);
|
||||
return *this;
|
||||
}
|
||||
|
||||
SegmentTable(SegmentTable &&other) : level1Table{std::exchange(other.level1Table, nullptr)}, level2Table{std::exchange(other.level2Table, nullptr)} {}
|
||||
|
||||
SegmentTable &operator=(SegmentTable &&other) {
|
||||
level1Table = std::exchange(other.level1Table, nullptr);
|
||||
level2Table = std::exchange(other.level2Table, nullptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
~SegmentTable() {
|
||||
if (level1Table.valid())
|
||||
munmap(level1Table.data(), level1Table.size_bytes());
|
||||
if (level2Table.valid())
|
||||
munmap(level2Table.data(), level2Table.size_bytes());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A read-only reference to the segment at the given index, this'll return a 0'd out segment if the segment is unset
|
||||
*/
|
||||
const SegmentType &operator[](size_t index) const {
|
||||
auto &l2Entry{level2Table[index >> L2Bits]};
|
||||
if (l2Entry.valid) [[likely]]
|
||||
return l2Entry.segment;
|
||||
else
|
||||
return level1Table[index >> L1Bits];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets a segment of segments between the start and end to the supplied value
|
||||
*/
|
||||
void Set(size_t start, size_t end, SegmentType segment) {
|
||||
size_t l2AlignedAddress{util::AlignUp(start, L2Size)};
|
||||
|
||||
size_t l1StartPaddingStart{start >> L1Bits};
|
||||
size_t l1StartPaddingEnd{l2AlignedAddress < end ? (l2AlignedAddress >> L1Bits) : (end >> L1Bits)};
|
||||
if (l1StartPaddingStart != l1StartPaddingEnd) {
|
||||
auto &l2Entry{level2Table[start >> L2Bits]};
|
||||
if (l2Entry.valid) {
|
||||
l2Entry.valid = false;
|
||||
|
||||
size_t l1L2Start{(start >> L2Bits) << (L2Bits - L1Bits)};
|
||||
for (size_t i{l1L2Start}; i < l1StartPaddingStart; i++)
|
||||
level1Table[i] = l2Entry.segment;
|
||||
|
||||
for (size_t i{l1StartPaddingStart}; i < l1StartPaddingEnd; i++)
|
||||
level1Table[i] = segment;
|
||||
|
||||
size_t l1L2End{l2AlignedAddress >> L1Bits};
|
||||
for (size_t i{l1StartPaddingEnd}; i < l1L2End; i++)
|
||||
level1Table[i] = l2Entry.segment;
|
||||
} else {
|
||||
for (size_t i{l1StartPaddingStart}; i < l1StartPaddingEnd; i++)
|
||||
level1Table[i] = segment;
|
||||
}
|
||||
}
|
||||
|
||||
if (end <= l2AlignedAddress)
|
||||
return;
|
||||
|
||||
size_t l2IndexStart{l2AlignedAddress >> L2Bits};
|
||||
size_t l2IndexEnd{end >> L2Bits};
|
||||
for (size_t i{l2IndexStart}; i < l2IndexEnd; i++) {
|
||||
auto &l2Entry{level2Table[i]};
|
||||
l2Entry.segment = segment;
|
||||
l2Entry.valid = true;
|
||||
}
|
||||
|
||||
size_t l1EndPaddingStart{l2IndexEnd << (L2Bits - L1Bits)};
|
||||
size_t l1EndPaddingEnd{end >> L1Bits};
|
||||
if (l1EndPaddingStart != l1EndPaddingEnd) {
|
||||
auto &l2Entry{level2Table[l2IndexEnd]};
|
||||
if (l2Entry.valid) {
|
||||
l2Entry.valid = false;
|
||||
|
||||
for (size_t i{l1EndPaddingStart}; i < l1EndPaddingEnd; i++)
|
||||
level1Table[i] = segment;
|
||||
|
||||
for (size_t i{l1EndPaddingEnd}; i < l1EndPaddingStart + L1inL2Count; i++)
|
||||
level1Table[i] = l2Entry.segment;
|
||||
} else {
|
||||
for (size_t i{l1EndPaddingStart}; i < l1EndPaddingEnd; i++)
|
||||
level1Table[i] = segment;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Helpers for pointer-based access */
|
||||
|
||||
template<typename T>
|
||||
requires std::is_pointer_v<T>
|
||||
const SegmentType &operator[](T pointer) const {
|
||||
return (*this)[reinterpret_cast<size_t>(pointer)];
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
requires std::is_pointer_v<T>
|
||||
void Set(T pointer, SegmentType segment) {
|
||||
Set(reinterpret_cast<size_t>(pointer), segment);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
requires std::is_pointer_v<T>
|
||||
void Set(T start, T end, SegmentType segment) {
|
||||
Set(reinterpret_cast<size_t>(start), reinterpret_cast<size_t>(end), segment);
|
||||
}
|
||||
|
||||
void Set(span<u8> span, SegmentType segment) {
|
||||
Set(reinterpret_cast<size_t>(span.begin().base()), reinterpret_cast<size_t>(span.end().base()));
|
||||
}
|
||||
};
|
||||
}
|
56
app/src/main/cpp/skyline/common/settings.cpp
Normal file
56
app/src/main/cpp/skyline/common/settings.cpp
Normal file
|
@ -0,0 +1,56 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
|
||||
#define PUGIXML_HEADER_ONLY
|
||||
|
||||
#include <pugixml.hpp>
|
||||
#include "settings.h"
|
||||
|
||||
namespace skyline {
|
||||
Settings::Settings(int fd) {
|
||||
pugi::xml_document document;
|
||||
auto result{document.load_file(fmt::format("/proc/self/fd/{}", fd).c_str())};
|
||||
if (!result)
|
||||
throw exception("PugiXML Error: {} at {}", result.description(), result.offset);
|
||||
|
||||
#define PREF_ELEM(name, memberName, rhs) std::make_pair(std::string(name), [](Settings &settings, const pugi::xml_node &element) { settings.memberName = rhs; })
|
||||
|
||||
std::tuple preferences{
|
||||
PREF_ELEM("log_level", logLevel, static_cast<Logger::LogLevel>(element.text().as_uint(static_cast<unsigned int>(Logger::LogLevel::Info)))),
|
||||
PREF_ELEM("username_value", username, element.text().as_string()),
|
||||
PREF_ELEM("operation_mode", operationMode, element.attribute("value").as_bool()),
|
||||
PREF_ELEM("force_triple_buffering", forceTripleBuffering, element.attribute("value").as_bool()),
|
||||
PREF_ELEM("disable_frame_throttling", disableFrameThrottling, element.attribute("value").as_bool()),
|
||||
};
|
||||
|
||||
#undef PREF_ELEM
|
||||
|
||||
std::bitset<std::tuple_size_v<typeof(preferences)>> preferencesSet{}; // A bitfield to keep track of all the preferences we've set
|
||||
for (auto element{document.last_child().first_child()}; element; element = element.next_sibling()) {
|
||||
std::string_view name{element.attribute("name").value()};
|
||||
std::apply([&](auto... preferences) {
|
||||
size_t index{};
|
||||
([&](auto preference) {
|
||||
if (name.size() == preference.first.size() && name.starts_with(preference.first)) {
|
||||
preference.second(*this, element);
|
||||
preferencesSet.set(index);
|
||||
}
|
||||
index++;
|
||||
}(preferences), ...);
|
||||
}, preferences);
|
||||
}
|
||||
|
||||
if (!preferencesSet.all()) {
|
||||
std::string unsetPreferences;
|
||||
std::apply([&](auto... preferences) {
|
||||
size_t index{};
|
||||
([&](auto preference) {
|
||||
if (!preferencesSet.test(index))
|
||||
unsetPreferences += std::string("\n* ") + preference.first;
|
||||
index++;
|
||||
}(preferences), ...);
|
||||
}, preferences);
|
||||
throw exception("Cannot find the following preferences:{}", unsetPreferences);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,101 +3,23 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "language.h"
|
||||
#include <common.h>
|
||||
|
||||
namespace skyline {
|
||||
/**
|
||||
* @brief The Settings class provides a simple interface to access user-defined settings, update values and subscribe callbacks to observe changes.
|
||||
* @brief The Settings class is used to access preferences set in the Kotlin component of Skyline
|
||||
*/
|
||||
class Settings {
|
||||
template<typename T>
|
||||
class Setting {
|
||||
using Callback = std::function<void(const T &)>;
|
||||
std::vector<Callback> callbacks; //!< Callbacks to be called when this setting changes
|
||||
T value;
|
||||
std::mutex valueMutex;
|
||||
std::mutex callbackMutex;
|
||||
|
||||
/**
|
||||
* @brief Calls all callbacks registered for this setting
|
||||
* @note Locking of the setting value must be handled by the caller
|
||||
*/
|
||||
void OnSettingChanged() {
|
||||
std::scoped_lock lock{callbackMutex};
|
||||
for (const auto &callback : callbacks)
|
||||
callback(value);
|
||||
}
|
||||
|
||||
public:
|
||||
/**
|
||||
* @return The underlying setting value
|
||||
*/
|
||||
const T &operator*() {
|
||||
std::scoped_lock lock{valueMutex};
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the underlying setting value, signalling any callbacks if necessary
|
||||
*/
|
||||
void operator=(T newValue) {
|
||||
std::scoped_lock lock{valueMutex};
|
||||
if (value != newValue) {
|
||||
value = std::move(newValue);
|
||||
OnSettingChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Register a callback to be run when this setting changes
|
||||
*/
|
||||
void AddCallback(Callback callback) {
|
||||
std::scoped_lock lock{callbackMutex};
|
||||
callbacks.push_back(std::move(callback));
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
// System
|
||||
Setting<bool> isDocked; //!< If the emulated Switch should be handheld or docked
|
||||
Setting<std::string> usernameValue; //!< The user name to be supplied to the guest
|
||||
Setting<std::string> profilePictureValue; //!< The profile picture path to be supplied to the guest
|
||||
Setting<language::SystemLanguage> systemLanguage; //!< The system language
|
||||
Setting<region::RegionCode> systemRegion; //!< The system region
|
||||
|
||||
// Display
|
||||
Setting<bool> forceTripleBuffering; //!< If the presentation engine should always triple buffer even if the swapchain supports double buffering
|
||||
Setting<bool> disableFrameThrottling; //!< Allow the guest to submit frames without any blocking calls
|
||||
Setting<bool> disableShaderCache; //!< Prevents cached shaders from being loaded and disables caching of new shaders
|
||||
|
||||
// GPU
|
||||
Setting<std::string> gpuDriver; //!< The label of the GPU driver to use
|
||||
Setting<std::string> gpuDriverLibraryName; //!< The name of the GPU driver library to use
|
||||
Setting<u32> executorSlotCountScale; //!< Number of GPU executor slots that can be used concurrently
|
||||
Setting<u32> executorFlushThreshold; //!< Number of commands that need to accumulate before they're flushed to the GPU
|
||||
Setting<bool> useDirectMemoryImport; //!< If buffer emulation should be done by importing guest buffer mappings
|
||||
Setting<bool> forceMaxGpuClocks; //!< If the GPU should be forced to run at maximum clocks
|
||||
Setting<bool> freeGuestTextureMemory; //!< If guest textrue memory should be freed when the owning texture is GPU dirty
|
||||
|
||||
// Hacks
|
||||
Setting<bool> enableFastGpuReadbackHack; //!< If the CPU texture readback skipping hack should be used
|
||||
Setting<bool> enableFastReadbackWrites; //!< If buffers should be treated as CPU dirty when written with the readback hack
|
||||
Setting<bool> disableSubgroupShuffle; //!< If shader subgroup suffle operations should be ignored
|
||||
|
||||
// Audio
|
||||
Setting<bool> isAudioOutputDisabled; //!< Disables audio output
|
||||
|
||||
// Debug
|
||||
Setting<bool> validationLayer; //!< If the vulkan validation layer is enabled
|
||||
|
||||
Settings() = default;
|
||||
|
||||
virtual ~Settings() = default;
|
||||
Logger::LogLevel logLevel; //!< The minimum level that logs need to be for them to be printed
|
||||
std::string username; //!< The name set by the user to be supplied to the guest
|
||||
bool operationMode; //!< If the emulated Switch should be handheld or docked
|
||||
bool forceTripleBuffering; //!< If the presentation engine should always triple buffer even if the swapchain supports double buffering
|
||||
bool disableFrameThrottling; //!< Allow the guest to submit frames without any blocking calls
|
||||
|
||||
/**
|
||||
* @brief Updates settings with the given values
|
||||
* @note This method is platform-specific and must be overridden
|
||||
* @param fd An FD to the preference XML file
|
||||
*/
|
||||
virtual void Update() = 0;
|
||||
Settings(int fd);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -14,12 +14,7 @@ namespace skyline::signal {
|
|||
std::rethrow_exception(SignalExceptionPtr);
|
||||
}
|
||||
|
||||
void SleepTillExit() {
|
||||
// We don't want to actually exit the process ourselves as it'll automatically be restarted gracefully due to a timeout after being unable to exit within a fixed duration
|
||||
Logger::EmulationContext.TryFlush(); // We want to attempt to flush logs before exiting
|
||||
while (true)
|
||||
sleep(std::numeric_limits<int>::max()); // We just trigger the timeout wait by sleeping forever
|
||||
}
|
||||
std::terminate_handler terminateHandler{};
|
||||
|
||||
inline StackFrame *SafeFrameRecurse(size_t depth, StackFrame *frame) {
|
||||
if (frame) {
|
||||
|
@ -27,17 +22,17 @@ namespace skyline::signal {
|
|||
if (frame->lr && frame->next)
|
||||
frame = frame->next;
|
||||
else
|
||||
SleepTillExit();
|
||||
terminateHandler();
|
||||
}
|
||||
} else {
|
||||
SleepTillExit();
|
||||
terminateHandler();
|
||||
}
|
||||
return frame;
|
||||
}
|
||||
|
||||
void TerminateHandler() {
|
||||
auto exception{std::current_exception()};
|
||||
if (exception && exception == SignalExceptionPtr) {
|
||||
if (terminateHandler && exception && exception == SignalExceptionPtr) {
|
||||
StackFrame *frame;
|
||||
asm("MOV %0, FP" : "=r"(frame));
|
||||
frame = SafeFrameRecurse(2, frame); // We unroll past 'std::terminate'
|
||||
|
@ -61,14 +56,14 @@ namespace skyline::signal {
|
|||
frame = SafeFrameRecurse(2, lookupFrame);
|
||||
hasAdvanced = true;
|
||||
} else {
|
||||
SleepTillExit(); // We presumably have no exception handlers left on the stack to consume the exception, it's time to quit
|
||||
terminateHandler(); // We presumably have no exception handlers left on the stack to consume the exception, it's time to quit
|
||||
}
|
||||
}
|
||||
lookupFrame = lookupFrame->next;
|
||||
}
|
||||
|
||||
if (!frame->next)
|
||||
SleepTillExit(); // We don't know the frame's stack boundaries, the only option is to quit
|
||||
terminateHandler(); // We don't know the frame's stack boundaries, the only option is to quit
|
||||
|
||||
asm("MOV SP, %x0\n\t" // Stack frame is the first item on a function's stack, it's used to calculate calling function's stack pointer
|
||||
"MOV LR, %x1\n\t"
|
||||
|
@ -78,7 +73,7 @@ namespace skyline::signal {
|
|||
|
||||
__builtin_unreachable();
|
||||
} else {
|
||||
SleepTillExit(); // We don't want to delegate to the older terminate handler as it might cause an exit
|
||||
terminateHandler();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -99,9 +94,11 @@ namespace skyline::signal {
|
|||
SignalExceptionPtr = std::make_exception_ptr(signalException);
|
||||
context->uc_mcontext.pc = reinterpret_cast<u64>(&ExceptionThrow);
|
||||
|
||||
std::set_terminate(TerminateHandler);
|
||||
|
||||
Logger::EmulationContext.TryFlush(); // We want to attempt to flush all logs in case exception handling fails and infloops
|
||||
auto handler{std::get_terminate()};
|
||||
if (handler != TerminateHandler) {
|
||||
terminateHandler = handler;
|
||||
std::set_terminate(TerminateHandler);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Signature>
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue