mirror of
https://github.com/azahar-emu/azahar
synced 2026-04-23 09:23:56 +00:00
Compare commits
46 commits
2125.0-rc5
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b3ee2d8ac5 | ||
|
|
9701a3d874 | ||
|
|
a276623dbb | ||
|
|
2fff086e81 | ||
|
|
5bc58c78ed | ||
|
|
0fe6a8c7df | ||
|
|
d4b5633cf0 | ||
|
|
afbaf8e485 | ||
|
|
52b1e01a6f | ||
|
|
f1cd5f5ff4 | ||
|
|
1edc5de18e | ||
|
|
727377c012 | ||
|
|
4dbe0fd497 | ||
|
|
6d230d28da | ||
|
|
336d871a7f | ||
|
|
e8c75b4107 | ||
|
|
0fc3d692b9 | ||
|
|
5983a23d38 | ||
|
|
599069eb33 | ||
|
|
6eca4afac5 | ||
|
|
b2faa299d5 | ||
|
|
3d69741076 | ||
|
|
d29e15f219 | ||
|
|
c650473fdc | ||
|
|
000530c028 | ||
|
|
df05b5f3db | ||
|
|
06a535f50e | ||
|
|
4cbd75b413 | ||
|
|
60f331b43b | ||
|
|
ba6f8cb744 | ||
|
|
118579adb3 | ||
|
|
23393904e0 | ||
|
|
3066887ff4 | ||
|
|
901f010913 | ||
|
|
5fc9732f05 | ||
|
|
39363cd435 | ||
|
|
60661c3b8b | ||
|
|
be0f096f48 | ||
|
|
6201256e15 | ||
|
|
af188bb7b7 | ||
|
|
0862e5e98a | ||
|
|
49b0bef17d | ||
|
|
f14f095e72 | ||
|
|
7e58ac5bcf | ||
|
|
7220bd2edd | ||
|
|
d4e9daa739 |
|
|
@ -2,13 +2,14 @@
|
|||
|
||||
ARTIFACTS_LIST=($ARTIFACTS)
|
||||
|
||||
BUNDLE_DIR=build/bundle
|
||||
mkdir build
|
||||
BUILD_DIR=build
|
||||
UNIVERSAL_DIR=$BUILD_DIR/universal
|
||||
BUNDLE_DIR=$UNIVERSAL_DIR/bundle
|
||||
OTHER_BUNDLE_DIR=$BUILD_DIR/x86_64/bundle
|
||||
|
||||
# Set up the base artifact to combine into.
|
||||
BASE_ARTIFACT=${ARTIFACTS_LIST[0]}
|
||||
BASE_ARTIFACT_ARCH="${BASE_ARTIFACT##*-}"
|
||||
mv $BASE_ARTIFACT $BUNDLE_DIR
|
||||
# Set up the base bundle to combine into.
|
||||
mkdir $UNIVERSAL_DIR
|
||||
cp -a $BUILD_DIR/arm64/bundle $UNIVERSAL_DIR
|
||||
|
||||
# Executable binary paths that need to be combined.
|
||||
BIN_PATHS=(Azahar.app/Contents/MacOS/azahar)
|
||||
|
|
@ -19,21 +20,18 @@ DYLIB_PATHS=($(cd $BUNDLE_DIR && find . -name '*.dylib'))
|
|||
unset IFS
|
||||
|
||||
# Combine all of the executable binaries and dylibs.
|
||||
for OTHER_ARTIFACT in "${ARTIFACTS_LIST[@]:1}"; do
|
||||
OTHER_ARTIFACT_ARCH="${OTHER_ARTIFACT##*-}"
|
||||
for BIN_PATH in "${BIN_PATHS[@]}"; do
|
||||
lipo -create -output $BUNDLE_DIR/$BIN_PATH $BUNDLE_DIR/$BIN_PATH $OTHER_BUNDLE_DIR/$BIN_PATH
|
||||
done
|
||||
|
||||
for BIN_PATH in "${BIN_PATHS[@]}"; do
|
||||
lipo -create -output $BUNDLE_DIR/$BIN_PATH $BUNDLE_DIR/$BIN_PATH $OTHER_ARTIFACT/$BIN_PATH
|
||||
done
|
||||
for DYLIB_PATH in "${DYLIB_PATHS[@]}"; do
|
||||
# Only merge if the libraries do not have conflicting arches, otherwise it will fail.
|
||||
DYLIB_INFO=`file $BUNDLE_DIR/$DYLIB_PATH`
|
||||
|
||||
for DYLIB_PATH in "${DYLIB_PATHS[@]}"; do
|
||||
# Only merge if the libraries do not have conflicting arches, otherwise it will fail.
|
||||
DYLIB_INFO=`file $BUNDLE_DIR/$DYLIB_PATH`
|
||||
OTHER_DYLIB_INFO=`file $OTHER_ARTIFACT/$DYLIB_PATH`
|
||||
if ! [[ "$DYLIB_INFO" =~ "$OTHER_ARTIFACT_ARCH" ]] && ! [[ "$OTHER_DYLIB_INFO" =~ "$BASE_ARTIFACT_ARCH" ]]; then
|
||||
lipo -create -output $BUNDLE_DIR/$DYLIB_PATH $BUNDLE_DIR/$DYLIB_PATH $OTHER_ARTIFACT/$DYLIB_PATH
|
||||
fi
|
||||
done
|
||||
OTHER_DYLIB_INFO=`file $OTHER_BUNDLE_DIR/$DYLIB_PATH`
|
||||
if ! [[ "$DYLIB_INFO" =~ "x86_64" ]] && ! [[ "$OTHER_DYLIB_INFO" =~ "arm64" ]]; then
|
||||
lipo -create -output $BUNDLE_DIR/$DYLIB_PATH $BUNDLE_DIR/$DYLIB_PATH $OTHER_BUNDLE_DIR/$DYLIB_PATH
|
||||
fi
|
||||
done
|
||||
|
||||
# Remove leftover libs so that they aren't distributed
|
||||
|
|
|
|||
11
.ci/macos.sh
11
.ci/macos.sh
|
|
@ -4,12 +4,10 @@ if [ "$GITHUB_REF_TYPE" == "tag" ]; then
|
|||
export EXTRA_CMAKE_FLAGS=(-DENABLE_QT_UPDATE_CHECKER=ON)
|
||||
fi
|
||||
|
||||
mkdir build && cd build
|
||||
cmake .. -GNinja \
|
||||
mkdir -p build/$BUILD_ARCH && cd build/$BUILD_ARCH
|
||||
cmake ../.. -GNinja \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_OSX_ARCHITECTURES="$TARGET" \
|
||||
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_OSX_ARCHITECTURES="$BUILD_ARCH" \
|
||||
-DENABLE_QT_TRANSLATION=ON \
|
||||
-DENABLE_ROOM_STANDALONE=OFF \
|
||||
-DUSE_DISCORD_PRESENCE=ON \
|
||||
|
|
@ -18,9 +16,8 @@ ninja
|
|||
ninja bundle
|
||||
mv ./bundle/azahar.app ./bundle/Azahar.app # TODO: Can this be done in CMake?
|
||||
|
||||
ccache -s -v
|
||||
|
||||
CURRENT_ARCH=`arch`
|
||||
if [ "$TARGET" = "$CURRENT_ARCH" ]; then
|
||||
if [ "$BUILD_ARCH" = "$CURRENT_ARCH" ]; then
|
||||
ctest -VV -C Release
|
||||
fi
|
||||
|
|
|
|||
31
.ci/pack.sh
31
.ci/pack.sh
|
|
@ -3,20 +3,21 @@
|
|||
# Determine the full revision name.
|
||||
GITDATE="`git show -s --date=short --format='%ad' | sed 's/-//g'`"
|
||||
GITREV="`git show -s --format='%h'`"
|
||||
REV_NAME="azahar-$OS-$TARGET-$GITDATE-$GITREV"
|
||||
|
||||
# Determine the name of the release being built.
|
||||
if [ "$GITHUB_REF_TYPE" = "tag" ]; then
|
||||
RELEASE_NAME=azahar-$GITHUB_REF_NAME
|
||||
REV_NAME="azahar-$OS-$TARGET-$GITHUB_REF_NAME"
|
||||
else
|
||||
RELEASE_NAME=azahar-head
|
||||
fi
|
||||
|
||||
# Archive and upload the artifacts.
|
||||
mkdir -p artifacts
|
||||
|
||||
function pack_artifacts() {
|
||||
REV_NAME="azahar-$OS-$TARGET-$GITDATE-$GITREV"
|
||||
|
||||
# Determine the name of the release being built.
|
||||
if [ "$GITHUB_REF_TYPE" = "tag" ]; then
|
||||
RELEASE_NAME=azahar-$GITHUB_REF_NAME
|
||||
REV_NAME="azahar-$OS-$TARGET-$GITHUB_REF_NAME"
|
||||
else
|
||||
RELEASE_NAME=azahar-head
|
||||
fi
|
||||
|
||||
ARTIFACTS_PATH="$1"
|
||||
|
||||
# Set up root directory for archive.
|
||||
|
|
@ -56,11 +57,23 @@ if [ -n "$UNPACKED" ]; then
|
|||
FILENAME=$(basename "$ARTIFACT")
|
||||
EXTENSION="${FILENAME##*.}"
|
||||
|
||||
# TODO: Deduplicate
|
||||
REV_NAME="azahar-$OS-$TARGET-$GITDATE-$GITREV"
|
||||
|
||||
# Determine the name of the release being built.
|
||||
if [ "$GITHUB_REF_TYPE" = "tag" ]; then
|
||||
RELEASE_NAME=azahar-$GITHUB_REF_NAME
|
||||
REV_NAME="azahar-$OS-$TARGET-$GITHUB_REF_NAME"
|
||||
else
|
||||
RELEASE_NAME=azahar-head
|
||||
fi
|
||||
|
||||
mv "$ARTIFACT" "artifacts/$REV_NAME.$EXTENSION"
|
||||
done
|
||||
elif [ -n "$PACK_INDIVIDUALLY" ]; then
|
||||
# Pack and upload the artifacts one-by-one.
|
||||
for ARTIFACT in build/bundle/*; do
|
||||
TARGET=$(basename "$ARTIFACT")
|
||||
pack_artifacts "$ARTIFACT"
|
||||
done
|
||||
else
|
||||
|
|
|
|||
66
.github/workflows/build.yml
vendored
66
.github/workflows/build.yml
vendored
|
|
@ -101,17 +101,12 @@ jobs:
|
|||
run: ./.ci/linux.sh
|
||||
|
||||
macos:
|
||||
runs-on: ${{ (matrix.target == 'x86_64' && 'macos-26-intel') || 'macos-26' }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
target: ["x86_64", "arm64"]
|
||||
runs-on: 'macos-26'
|
||||
env:
|
||||
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
||||
CCACHE_COMPILERCHECK: content
|
||||
CCACHE_SLOPPINESS: time_macros
|
||||
OS: macos
|
||||
TARGET: ${{ matrix.target }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
|
|
@ -120,58 +115,31 @@ jobs:
|
|||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ env.CCACHE_DIR }}
|
||||
key: ${{ runner.os }}-${{ matrix.target }}-${{ github.sha }}
|
||||
key: ${{ runner.os }}-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-${{ matrix.target }}-
|
||||
${{ runner.os }}-
|
||||
- name: Install tools
|
||||
run: brew install ccache ninja spirv-tools
|
||||
- name: Build
|
||||
run: ./.ci/macos.sh
|
||||
- name: Prepare outputs for caching
|
||||
run: cp -R build/bundle $OS-$TARGET
|
||||
- name: Cache outputs for universal build
|
||||
uses: actions/cache/save@v4
|
||||
with:
|
||||
path: ${{ env.OS }}-${{ env.TARGET }}
|
||||
key: ${{ runner.os }}-${{ matrix.target }}-${{ github.sha }}-${{ github.run_id }}-${{ github.run_attempt }}
|
||||
- name: Pack
|
||||
run: ./.ci/pack.sh
|
||||
- name: Upload
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ env.OS }}-${{ env.TARGET }}
|
||||
path: artifacts/
|
||||
|
||||
macos-universal:
|
||||
runs-on: macos-26
|
||||
needs: macos
|
||||
env:
|
||||
OS: macos
|
||||
TARGET: universal
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Download x86_64 build from cache
|
||||
uses: actions/cache/restore@v4
|
||||
with:
|
||||
path: ${{ env.OS }}-x86_64
|
||||
key: ${{ runner.os }}-x86_64-${{ github.sha }}-${{ github.run_id }}-${{ github.run_attempt }}
|
||||
fail-on-cache-miss: true
|
||||
- name: Download ARM64 build from cache
|
||||
uses: actions/cache/restore@v4
|
||||
with:
|
||||
path: ${{ env.OS }}-arm64
|
||||
key: ${{ runner.os }}-arm64-${{ github.sha }}-${{ github.run_id }}-${{ github.run_attempt }}
|
||||
fail-on-cache-miss: true
|
||||
- name: Build (x86_64)
|
||||
run: BUILD_ARCH=x86_64 ./.ci/macos.sh
|
||||
- name: Build (arm64)
|
||||
run: BUILD_ARCH=arm64 ./.ci/macos.sh
|
||||
- name: Create universal app
|
||||
run: ./.ci/macos-universal.sh
|
||||
env:
|
||||
ARTIFACTS: ${{ env.OS }}-x86_64 ${{ env.OS }}-arm64
|
||||
- name: Prepare for packing
|
||||
run: |
|
||||
mkdir build/bundle
|
||||
cp -r build/x86_64/bundle build/bundle/x86_64
|
||||
cp -r build/arm64/bundle build/bundle/arm64
|
||||
cp -r build/universal/bundle build/bundle/universal
|
||||
- name: Pack
|
||||
env:
|
||||
PACK_INDIVIDUALLY: 1
|
||||
run: ./.ci/pack.sh
|
||||
- name: Upload
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ env.OS }}-${{ env.TARGET }}
|
||||
name: ${{ env.OS }}
|
||||
path: artifacts/
|
||||
|
||||
windows:
|
||||
|
|
@ -339,4 +307,4 @@ jobs:
|
|||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: docker
|
||||
path: artifacts/
|
||||
path: artifacts/
|
||||
|
|
|
|||
|
|
@ -25,6 +25,10 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Darwin" OR CMAKE_SYSTEM_NAME STREQUAL "iOS")
|
|||
enable_language(OBJC OBJCXX)
|
||||
endif()
|
||||
|
||||
if (BSD STREQUAL "OpenBSD")
|
||||
add_link_options(-z wxneeded)
|
||||
endif()
|
||||
|
||||
option(ENABLE_LIBRETRO "Build as a LibRetro core" OFF)
|
||||
|
||||
# Some submodules like to pick their own default build type if not specified.
|
||||
|
|
@ -126,7 +130,8 @@ CMAKE_DEPENDENT_OPTION(ENABLE_LIBUSB "Enable libusb for GameCube Adapter support
|
|||
|
||||
CMAKE_DEPENDENT_OPTION(ENABLE_SOFTWARE_RENDERER "Enables the software renderer" ON "NOT ANDROID" OFF)
|
||||
CMAKE_DEPENDENT_OPTION(ENABLE_OPENGL "Enables the OpenGL renderer" ${DEFAULT_ENABLE_OPENGL} "NOT APPLE" OFF)
|
||||
option(ENABLE_VULKAN "Enables the Vulkan renderer" ON)
|
||||
# NetBSD doesn't support Vulkan yet, remove this check when it does.
|
||||
CMAKE_DEPENDENT_OPTION(ENABLE_VULKAN "Enables the Vulkan renderer" ON "NOT (BSD MATCHES \"NetBSD\")" OFF)
|
||||
|
||||
option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF)
|
||||
|
||||
|
|
@ -136,6 +141,8 @@ option(ENABLE_SSE42 "Enable SSE4.2 optimizations on x86_64" ON)
|
|||
|
||||
option(ENABLE_DEVELOPER_OPTIONS "Enable functionality targeted at emulator developers" OFF)
|
||||
|
||||
option(ENABLE_BUILTIN_KEYBLOB "Enable the inclusion of the default crypto keys blob" ON)
|
||||
|
||||
# Compile options
|
||||
CMAKE_DEPENDENT_OPTION(COMPILE_WITH_DWARF "Add DWARF debugging information" ${IS_DEBUG_BUILD} "MINGW" OFF)
|
||||
option(ENABLE_LTO "Enable link time optimization" ${DEFAULT_ENABLE_LTO})
|
||||
|
|
|
|||
11
CMakeModules/DisablePaxMprotect.cmake
Normal file
11
CMakeModules/DisablePaxMprotect.cmake
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
function(disable_pax_mprotect target)
|
||||
if (BSD STREQUAL "NetBSD")
|
||||
add_custom_command(TARGET ${target} POST_BUILD
|
||||
COMMAND paxctl +m "$<TARGET_FILE:${target}>"
|
||||
COMMENT "Disabling PaX MPROTECT restrictions for '${target}'"
|
||||
VERBATIM
|
||||
)
|
||||
else()
|
||||
message(FATAL_ERROR "disable_pax_mprotect only applies on NetBSD.")
|
||||
endif()
|
||||
endfunction()
|
||||
2
dist/languages/.tx/config
vendored
2
dist/languages/.tx/config
vendored
|
|
@ -12,4 +12,4 @@ lang_map = ca@valencia:ca_ES_valencia
|
|||
file_filter = ../../src/android/app/src/main/res/values-<lang>/strings.xml
|
||||
source_file = ../../src/android/app/src/main/res/values/strings.xml
|
||||
type = ANDROID
|
||||
lang_map = es_ES:b+es+ES, hu_HU:b+hu+HU, ru_RU:b+ru+RU, pt_BR:b+pt+BR, zh_CN:b+zh+CN, pl_PL:b+pl+PL, ca@valencia:b+ca+ES+valencia, ko_KR:b+ko+KR, da_DK:b+da+DK, ja_JP:b+ja+JP, lt_LT:b+lt+LT, ro_RO:b+ro+RO, tr_TR:b+tr+TR, vi_VN:b+vi+VN, zh_TW:b+zh+TW
|
||||
lang_map = es_ES:b+es+ES, hu_HU:b+hu+HU, ru_RU:b+ru+RU, pt_BR:b+pt+BR, zh_CN:b+zh+CN, pl_PL:b+pl+PL, ca@valencia:b+ca+ES+valencia, ko_KR:b+ko+KR, da_DK:b+da+DK, ja_JP:b+ja+JP, lt_LT:b+lt+LT, ro_RO:b+ro+RO, tr_TR:b+tr+TR, vi_VN:b+vi+VN, zh_TW:b+zh+TW, es_419:b+es+419
|
||||
|
|
|
|||
419
dist/languages/ca_ES_valencia.ts
vendored
419
dist/languages/ca_ES_valencia.ts
vendored
File diff suppressed because it is too large
Load diff
419
dist/languages/da_DK.ts
vendored
419
dist/languages/da_DK.ts
vendored
File diff suppressed because it is too large
Load diff
643
dist/languages/de.ts
vendored
643
dist/languages/de.ts
vendored
File diff suppressed because it is too large
Load diff
419
dist/languages/el.ts
vendored
419
dist/languages/el.ts
vendored
File diff suppressed because it is too large
Load diff
7815
dist/languages/es_419.ts
vendored
Normal file
7815
dist/languages/es_419.ts
vendored
Normal file
File diff suppressed because it is too large
Load diff
419
dist/languages/es_ES.ts
vendored
419
dist/languages/es_ES.ts
vendored
File diff suppressed because it is too large
Load diff
419
dist/languages/fi.ts
vendored
419
dist/languages/fi.ts
vendored
File diff suppressed because it is too large
Load diff
419
dist/languages/fr.ts
vendored
419
dist/languages/fr.ts
vendored
File diff suppressed because it is too large
Load diff
419
dist/languages/hu_HU.ts
vendored
419
dist/languages/hu_HU.ts
vendored
File diff suppressed because it is too large
Load diff
419
dist/languages/id.ts
vendored
419
dist/languages/id.ts
vendored
File diff suppressed because it is too large
Load diff
421
dist/languages/it.ts
vendored
421
dist/languages/it.ts
vendored
File diff suppressed because it is too large
Load diff
419
dist/languages/ja_JP.ts
vendored
419
dist/languages/ja_JP.ts
vendored
File diff suppressed because it is too large
Load diff
419
dist/languages/ko_KR.ts
vendored
419
dist/languages/ko_KR.ts
vendored
File diff suppressed because it is too large
Load diff
419
dist/languages/lt_LT.ts
vendored
419
dist/languages/lt_LT.ts
vendored
File diff suppressed because it is too large
Load diff
843
dist/languages/nb.ts
vendored
843
dist/languages/nb.ts
vendored
File diff suppressed because it is too large
Load diff
419
dist/languages/nl.ts
vendored
419
dist/languages/nl.ts
vendored
File diff suppressed because it is too large
Load diff
419
dist/languages/pl_PL.ts
vendored
419
dist/languages/pl_PL.ts
vendored
File diff suppressed because it is too large
Load diff
419
dist/languages/pt_BR.ts
vendored
419
dist/languages/pt_BR.ts
vendored
File diff suppressed because it is too large
Load diff
419
dist/languages/ro_RO.ts
vendored
419
dist/languages/ro_RO.ts
vendored
File diff suppressed because it is too large
Load diff
419
dist/languages/ru_RU.ts
vendored
419
dist/languages/ru_RU.ts
vendored
File diff suppressed because it is too large
Load diff
419
dist/languages/sv.ts
vendored
419
dist/languages/sv.ts
vendored
File diff suppressed because it is too large
Load diff
419
dist/languages/tr_TR.ts
vendored
419
dist/languages/tr_TR.ts
vendored
File diff suppressed because it is too large
Load diff
419
dist/languages/vi_VN.ts
vendored
419
dist/languages/vi_VN.ts
vendored
File diff suppressed because it is too large
Load diff
419
dist/languages/zh_CN.ts
vendored
419
dist/languages/zh_CN.ts
vendored
File diff suppressed because it is too large
Load diff
419
dist/languages/zh_TW.ts
vendored
419
dist/languages/zh_TW.ts
vendored
File diff suppressed because it is too large
Load diff
24
externals/CMakeLists.txt
vendored
24
externals/CMakeLists.txt
vendored
|
|
@ -60,6 +60,7 @@ if (ENABLE_TESTS)
|
|||
add_subdirectory(catch2)
|
||||
endif()
|
||||
target_link_libraries(catch2 INTERFACE Catch2::Catch2WithMain)
|
||||
include(Catch)
|
||||
endif()
|
||||
|
||||
# Crypto++
|
||||
|
|
@ -111,7 +112,13 @@ endif()
|
|||
|
||||
# Oaknut
|
||||
if ("arm64" IN_LIST ARCHITECTURE)
|
||||
add_subdirectory(oaknut EXCLUDE_FROM_ALL)
|
||||
if(USE_SYSTEM_OAKNUT)
|
||||
find_package(oaknut REQUIRED)
|
||||
add_library(oaknut INTERFACE)
|
||||
target_link_libraries(oaknut INTERFACE merry::oaknut)
|
||||
else()
|
||||
add_subdirectory(oaknut EXCLUDE_FROM_ALL)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Dynarmic
|
||||
|
|
@ -493,6 +500,19 @@ if (ENABLE_VULKAN)
|
|||
else()
|
||||
target_include_directories(vulkan-headers INTERFACE ./vulkan-headers/include)
|
||||
target_disable_warnings(vulkan-headers)
|
||||
if (BSD STREQUAL "NetBSD")
|
||||
# There may be a better way to do this with
|
||||
# find_package(X11), but I couldn't get
|
||||
# CMake to do it, so we're depending on
|
||||
# the x11-links package and assuming the
|
||||
# prefix location. -OS
|
||||
target_include_directories(vulkan-headers INTERFACE
|
||||
/usr/pkg/share/x11-links/include)
|
||||
elseif (BSD STREQUAL "OpenBSD")
|
||||
# This is fine to hardcode because it'll never change
|
||||
target_include_directories(vulkan-headers INTERFACE
|
||||
/usr/X11R6/include)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# adrenotools
|
||||
|
|
@ -513,4 +533,4 @@ elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|ARM64|armv8")
|
|||
else()
|
||||
target_compile_definitions(xxhash PRIVATE XXH_VECTOR=XXH_SCALAR)
|
||||
message(STATUS "Disabling SIMD for xxHash")
|
||||
endif()
|
||||
endif()
|
||||
|
|
|
|||
2
externals/boost
vendored
2
externals/boost
vendored
|
|
@ -1 +1 @@
|
|||
Subproject commit f9b15f673a688982f78a5f63a49a27275b318e5f
|
||||
Subproject commit 6a85c3100499e886e11c87a5c2109eedacea0a61
|
||||
|
|
@ -14,6 +14,7 @@ option(USE_SYSTEM_JSON "Use the system JSON (nlohmann-json3) package (instead of
|
|||
option(USE_SYSTEM_DYNARMIC "Use the system dynarmic (instead of the bundled one)" OFF)
|
||||
option(USE_SYSTEM_FMT "Use the system fmt (instead of the bundled one)" OFF)
|
||||
option(USE_SYSTEM_XBYAK "Use the system xbyak (instead of the bundled one)" OFF)
|
||||
option(USE_SYSTEM_OAKNUT "Use the system oaknut (instead of the bundled one)" OFF)
|
||||
option(USE_SYSTEM_INIH "Use the system inih (instead of the bundled one)" OFF)
|
||||
option(USE_SYSTEM_FFMPEG_HEADERS "Use the system FFmpeg headers (instead of the bundled one)" OFF)
|
||||
option(USE_SYSTEM_GLSLANG "Use the system glslang and SPIR-V libraries (instead of the bundled ones)" OFF)
|
||||
|
|
@ -40,6 +41,7 @@ CMAKE_DEPENDENT_OPTION(DISABLE_SYSTEM_JSON "Disable system JSON" OFF "USE_SYSTEM
|
|||
CMAKE_DEPENDENT_OPTION(DISABLE_SYSTEM_DYNARMIC "Disable system Dynarmic" OFF "USE_SYSTEM_LIBS" OFF)
|
||||
CMAKE_DEPENDENT_OPTION(DISABLE_SYSTEM_FMT "Disable system fmt" OFF "USE_SYSTEM_LIBS" OFF)
|
||||
CMAKE_DEPENDENT_OPTION(DISABLE_SYSTEM_XBYAK "Disable system xbyak" OFF "USE_SYSTEM_LIBS" OFF)
|
||||
CMAKE_DEPENDENT_OPTION(DISABLE_SYSTEM_OAKNUT "Disable system oaknut" OFF "USE_SYSTEM_LIBS" OFF)
|
||||
CMAKE_DEPENDENT_OPTION(DISABLE_SYSTEM_INIH "Disable system inih" OFF "USE_SYSTEM_LIBS" OFF)
|
||||
CMAKE_DEPENDENT_OPTION(DISABLE_SYSTEM_FFMPEG_HEADERS "Disable system ffmpeg" OFF "USE_SYSTEM_LIBS" OFF)
|
||||
CMAKE_DEPENDENT_OPTION(DISABLE_SYSTEM_GLSLANG "Disable system glslang" OFF "USE_SYSTEM_LIBS" OFF)
|
||||
|
|
@ -66,6 +68,7 @@ set(LIB_VAR_LIST
|
|||
DYNARMIC
|
||||
FMT
|
||||
XBYAK
|
||||
OAKNUT
|
||||
INIH
|
||||
FFMPEG_HEADERS
|
||||
GLSLANG
|
||||
|
|
|
|||
|
|
@ -183,6 +183,9 @@ endif()
|
|||
if(ENABLE_DEVELOPER_OPTIONS)
|
||||
add_compile_definitions(ENABLE_DEVELOPER_OPTIONS)
|
||||
endif()
|
||||
if(ENABLE_BUILTIN_KEYBLOB)
|
||||
add_compile_definitions(ENABLE_BUILTIN_KEYBLOB)
|
||||
endif()
|
||||
|
||||
add_subdirectory(common)
|
||||
add_subdirectory(core)
|
||||
|
|
|
|||
|
|
@ -258,6 +258,9 @@ object NativeLibrary {
|
|||
|
||||
external fun nativeFileExists(path: String): Boolean
|
||||
|
||||
external fun deleteOpenGLShaderCache(titleId: Long)
|
||||
external fun deleteVulkanShaderCache(titleId: Long)
|
||||
|
||||
private var coreErrorAlertResult = false
|
||||
private val coreErrorAlertLock = Object()
|
||||
|
||||
|
|
@ -504,8 +507,9 @@ object NativeLibrary {
|
|||
const val ErrorSystemFiles = 8
|
||||
const val ErrorSavestate = 9
|
||||
const val ErrorArticDisconnected = 10
|
||||
const val ShutdownRequested = 11
|
||||
const val ErrorUnknown = 12
|
||||
const val ErrorN3DSApplication = 11
|
||||
const val ShutdownRequested = 12
|
||||
const val ErrorUnknown = 13
|
||||
|
||||
fun newInstance(resultCode: Int): EmulationErrorDialogFragment {
|
||||
val args = Bundle()
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import android.Manifest.permission
|
|||
import android.annotation.SuppressLint
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.content.pm.ActivityInfo
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
|
|
@ -80,7 +81,9 @@ class EmulationActivity : AppCompatActivity() {
|
|||
return navHostFragment.getChildFragmentManager().fragments.last() as EmulationFragment
|
||||
}
|
||||
|
||||
private var isRotationBlocked: Boolean = true
|
||||
private var isEmulationRunning: Boolean = false
|
||||
private var isEmulationReady: Boolean = false
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
requestWindowFeature(Window.FEATURE_NO_TITLE)
|
||||
|
|
@ -89,12 +92,20 @@ class EmulationActivity : AppCompatActivity() {
|
|||
|
||||
ThemeUtil.setTheme(this)
|
||||
settingsViewModel.settings.loadSettings()
|
||||
|
||||
screenAdjustmentUtil = ScreenAdjustmentUtil(this, windowManager, settingsViewModel.settings)
|
||||
|
||||
// Block orientation until emulation is ready to prevent unneccesary
|
||||
// surface recreation until the renderer is ready.
|
||||
isRotationBlocked = true
|
||||
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LOCKED
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
secondaryDisplay = SecondaryDisplay(this)
|
||||
secondaryDisplay.updateDisplay()
|
||||
|
||||
binding = ActivityEmulationBinding.inflate(layoutInflater)
|
||||
screenAdjustmentUtil = ScreenAdjustmentUtil(this, windowManager, settingsViewModel.settings)
|
||||
hotkeyUtility = HotkeyUtility(screenAdjustmentUtil, this)
|
||||
setContentView(binding.root)
|
||||
|
||||
|
|
@ -119,8 +130,6 @@ class EmulationActivity : AppCompatActivity() {
|
|||
isEmulationRunning = true
|
||||
instance = this
|
||||
|
||||
applyOrientationSettings() // Check for orientation settings at startup
|
||||
|
||||
val game = try {
|
||||
intent.extras?.let { extras ->
|
||||
BundleCompat.getParcelable(extras, "game", Game::class.java)
|
||||
|
|
@ -136,13 +145,46 @@ class EmulationActivity : AppCompatActivity() {
|
|||
NativeLibrary.playTimeManagerStart(game.titleId)
|
||||
}
|
||||
|
||||
override fun onNewIntent(intent: Intent) {
|
||||
super.onNewIntent(intent)
|
||||
setIntent(intent)
|
||||
|
||||
NativeLibrary.stopEmulation()
|
||||
NativeLibrary.playTimeManagerStop()
|
||||
|
||||
isEmulationReady = false
|
||||
isRotationBlocked = true
|
||||
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LOCKED
|
||||
emulationViewModel.setEmulationStarted(false)
|
||||
|
||||
val game = intent.extras?.let { extras ->
|
||||
BundleCompat.getParcelable(extras, "game", Game::class.java)
|
||||
}
|
||||
if (game != null) {
|
||||
NativeLibrary.playTimeManagerStart(game.titleId)
|
||||
}
|
||||
|
||||
val navHostFragment =
|
||||
supportFragmentManager.findFragmentById(R.id.fragment_container) as NavHostFragment
|
||||
navHostFragment.navController.setGraph(R.navigation.emulation_navigation, intent.extras)
|
||||
}
|
||||
|
||||
// On some devices, the system bars will not disappear on first boot or after some
|
||||
// rotations. Here we set full screen immersive repeatedly in onResume and in
|
||||
// onWindowFocusChanged to prevent the unwanted status bar state.
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
enableFullscreenImmersive()
|
||||
applyOrientationSettings() // Check for orientation settings changes on runtime
|
||||
if (isEmulationReady) {
|
||||
// If emulation is ready then unblock rotation
|
||||
isRotationBlocked = false
|
||||
applyOrientationSettings()
|
||||
emulationViewModel.setEmulationStarted(true)
|
||||
} else {
|
||||
if (!isRotationBlocked) {
|
||||
applyOrientationSettings()
|
||||
}
|
||||
}
|
||||
super.onResume()
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
|
|
@ -151,8 +193,8 @@ class EmulationActivity : AppCompatActivity() {
|
|||
}
|
||||
|
||||
override fun onWindowFocusChanged(hasFocus: Boolean) {
|
||||
super.onWindowFocusChanged(hasFocus)
|
||||
enableFullscreenImmersive()
|
||||
super.onWindowFocusChanged(hasFocus)
|
||||
}
|
||||
|
||||
public override fun onRestart() {
|
||||
|
|
@ -164,11 +206,15 @@ class EmulationActivity : AppCompatActivity() {
|
|||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
outState.putBoolean("isEmulationRunning", isEmulationRunning)
|
||||
outState.putBoolean("isEmulationReady", isEmulationReady)
|
||||
outState.putBoolean("isRotationBlocked", isRotationBlocked)
|
||||
}
|
||||
|
||||
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
|
||||
super.onRestoreInstanceState(savedInstanceState)
|
||||
isEmulationRunning = savedInstanceState.getBoolean("isEmulationRunning", false)
|
||||
isEmulationReady = savedInstanceState.getBoolean("isEmulationReady", false)
|
||||
isRotationBlocked = savedInstanceState.getBoolean("isRotationBlocked", isRotationBlocked)
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
|
|
@ -222,6 +268,11 @@ class EmulationActivity : AppCompatActivity() {
|
|||
|
||||
fun onEmulationStarted() {
|
||||
emulationViewModel.setEmulationStarted(true)
|
||||
isEmulationReady = true
|
||||
if (isRotationBlocked) {
|
||||
isRotationBlocked = false
|
||||
applyOrientationSettings()
|
||||
}
|
||||
Toast.makeText(
|
||||
applicationContext,
|
||||
getString(R.string.emulation_menu_help),
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ import androidx.recyclerview.widget.DiffUtil
|
|||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import android.widget.PopupMenu
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import com.google.android.material.button.MaterialButton
|
||||
|
|
@ -514,6 +515,63 @@ class GameAdapter(
|
|||
showUninstallContextMenu(it, game, bottomSheetDialog)
|
||||
}
|
||||
|
||||
bottomSheetView.findViewById<MaterialButton>(R.id.delete_cache).setOnClickListener {
|
||||
val options = arrayOf(context.getString(R.string.vulkan), context.getString(R.string.opengles))
|
||||
var selectedIndex = -1
|
||||
val dialog = MaterialAlertDialogBuilder(context)
|
||||
.setTitle(R.string.delete_cache_select_backend)
|
||||
.setSingleChoiceItems(options, -1) { dialog, which ->
|
||||
selectedIndex = which
|
||||
}
|
||||
.setPositiveButton(android.R.string.ok) {_, _ ->
|
||||
val progToast = Toast.makeText(
|
||||
CitraApplication.appContext,
|
||||
R.string.deleting_shader_cache,
|
||||
Toast.LENGTH_LONG
|
||||
)
|
||||
progToast.show()
|
||||
|
||||
activity.lifecycleScope.launch(Dispatchers.IO) {
|
||||
|
||||
when (selectedIndex) {
|
||||
0 -> {
|
||||
NativeLibrary.deleteVulkanShaderCache(game.titleId)
|
||||
}
|
||||
1 -> {
|
||||
NativeLibrary.deleteOpenGLShaderCache(game.titleId)
|
||||
}
|
||||
}
|
||||
|
||||
activity.runOnUiThread {
|
||||
progToast.cancel()
|
||||
Toast.makeText(
|
||||
CitraApplication.appContext,
|
||||
R.string.shader_cache_deleted,
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel) { dialog, _ ->
|
||||
dialog.dismiss()
|
||||
}
|
||||
.create()
|
||||
|
||||
dialog.setOnShowListener {
|
||||
val positiveButton = dialog.getButton(android.app.AlertDialog.BUTTON_POSITIVE)
|
||||
|
||||
positiveButton.isEnabled = false
|
||||
|
||||
val listView = dialog.listView
|
||||
listView.setOnItemClickListener { _, _, position, _ ->
|
||||
selectedIndex = position
|
||||
positiveButton.isEnabled = true
|
||||
}
|
||||
}
|
||||
|
||||
dialog.show()
|
||||
}
|
||||
|
||||
val bottomSheetBehavior = bottomSheetDialog.getBehavior()
|
||||
bottomSheetBehavior.skipCollapsed = true
|
||||
bottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ enum class IntSetting(
|
|||
CAMERA_INNER_FLIP(SettingKeys.camera_inner_flip(), Settings.SECTION_CAMERA, 0),
|
||||
CAMERA_OUTER_LEFT_FLIP(SettingKeys.camera_outer_left_flip(), Settings.SECTION_CAMERA, 0),
|
||||
CAMERA_OUTER_RIGHT_FLIP(SettingKeys.camera_outer_right_flip(), Settings.SECTION_CAMERA, 0),
|
||||
GRAPHICS_API(SettingKeys.graphics_api(), Settings.SECTION_RENDERER, 1),
|
||||
GRAPHICS_API(SettingKeys.graphics_api(), Settings.SECTION_RENDERER, 2),
|
||||
RESOLUTION_FACTOR(SettingKeys.resolution_factor(), Settings.SECTION_RENDERER, 1),
|
||||
STEREOSCOPIC_3D_MODE(SettingKeys.render_3d(), Settings.SECTION_RENDERER, 2),
|
||||
STEREOSCOPIC_3D_DEPTH(SettingKeys.factor_3d(), Settings.SECTION_RENDERER, 0),
|
||||
|
|
|
|||
|
|
@ -1275,7 +1275,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
|
|||
override val section = null
|
||||
override val isRuntimeEditable = false
|
||||
override val valueAsString = int.toString()
|
||||
override val defaultValue = FloatSetting.BACKGROUND_RED.defaultValue
|
||||
override val defaultValue = FloatSetting.BACKGROUND_RED.defaultValue.toInt()
|
||||
}
|
||||
add(
|
||||
SliderSetting(
|
||||
|
|
@ -1298,7 +1298,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
|
|||
override val section = null
|
||||
override val isRuntimeEditable = false
|
||||
override val valueAsString = int.toString()
|
||||
override val defaultValue = FloatSetting.BACKGROUND_GREEN.defaultValue
|
||||
override val defaultValue = FloatSetting.BACKGROUND_GREEN.defaultValue.toInt()
|
||||
}
|
||||
add(
|
||||
SliderSetting(
|
||||
|
|
@ -1321,7 +1321,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
|
|||
override val section = null
|
||||
override val isRuntimeEditable = false
|
||||
override val valueAsString = int.toString()
|
||||
override val defaultValue = FloatSetting.BACKGROUND_BLUE.defaultValue
|
||||
override val defaultValue = FloatSetting.BACKGROUND_BLUE.defaultValue.toInt()
|
||||
}
|
||||
add(
|
||||
SliderSetting(
|
||||
|
|
|
|||
|
|
@ -543,6 +543,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
|
|||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
if (::emulationState.isInitialized && requireActivity().isFinishing) {
|
||||
emulationState.stop()
|
||||
}
|
||||
EmulationLifecycleUtil.removeHook(onPause)
|
||||
EmulationLifecycleUtil.removeHook(onShutdown)
|
||||
if (gameFd != null) {
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ import org.citra.citra_emu.adapters.GameAdapter
|
|||
import org.citra.citra_emu.databinding.FragmentGamesBinding
|
||||
import org.citra.citra_emu.features.settings.model.Settings
|
||||
import org.citra.citra_emu.model.Game
|
||||
import org.citra.citra_emu.utils.BuildUtil
|
||||
import org.citra.citra_emu.viewmodel.CompressProgressDialogViewModel
|
||||
import org.citra.citra_emu.viewmodel.GamesViewModel
|
||||
import org.citra.citra_emu.viewmodel.HomeViewModel
|
||||
|
|
@ -60,8 +61,14 @@ class GamesFragment : Fragment() {
|
|||
companion object {
|
||||
fun doCompression(fragment: Fragment, gamesViewModel: GamesViewModel, inputPath: String?, outputUri: Uri?, shouldCompress: Boolean) {
|
||||
if (outputUri != null) {
|
||||
val outputPath: String =
|
||||
if (!BuildUtil.isGooglePlayBuild) {
|
||||
"!" + NativeLibrary.getNativePath(outputUri)
|
||||
} else {
|
||||
outputUri.toString()
|
||||
}
|
||||
CompressProgressDialogViewModel.reset()
|
||||
val dialog = CompressProgressDialogFragment.newInstance(shouldCompress, outputUri.toString())
|
||||
val dialog = CompressProgressDialogFragment.newInstance(shouldCompress, outputPath)
|
||||
dialog.showNow(
|
||||
fragment.requireActivity().supportFragmentManager,
|
||||
CompressProgressDialogFragment.TAG
|
||||
|
|
@ -69,9 +76,9 @@ class GamesFragment : Fragment() {
|
|||
|
||||
fragment.lifecycleScope.launch(Dispatchers.IO) {
|
||||
val status = if (shouldCompress) {
|
||||
NativeLibrary.compressFile(inputPath, outputUri.toString())
|
||||
NativeLibrary.compressFile(inputPath, outputPath)
|
||||
} else {
|
||||
NativeLibrary.decompressFile(inputPath, outputUri.toString())
|
||||
NativeLibrary.decompressFile(inputPath, outputPath)
|
||||
}
|
||||
|
||||
fragment.requireActivity().runOnUiThread {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2023 Citra Emulator Project
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
|
|
@ -78,8 +78,9 @@ class GamesViewModel : ViewModel() {
|
|||
val filteredList = sortedList.filter {
|
||||
if (it.isSystemTitle) {
|
||||
it.isVisibleSystemTitle
|
||||
} else {
|
||||
true
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
_games.value = filteredList
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ static const char* android_config_default_file_content = (BOOST_HANA_STRING(R"(
|
|||
|
||||
[Renderer]
|
||||
# Whether to render using OpenGL
|
||||
# 1: OpenGL ES (default), 2: Vulkan
|
||||
# 1: OpenGL ES, 2: Vulkan (default)
|
||||
)") DECLARE_KEY(graphics_api) BOOST_HANA_STRING(R"(
|
||||
|
||||
# Whether to compile shaders on multiple worker threads (Vulkan only)
|
||||
|
|
|
|||
|
|
@ -1130,4 +1130,44 @@ jboolean Java_org_citra_citra_1emu_NativeLibrary_nativeFileExists(JNIEnv* env, j
|
|||
return FileUtil::Exists(path);
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
void Java_org_citra_citra_1emu_NativeLibrary_deleteOpenGLShaderCache(JNIEnv* env, jobject obj,
|
||||
jlong title_id) {
|
||||
for (const std::string_view cache_type : {"separable", "conventional"}) {
|
||||
const std::string path =
|
||||
fmt::format("{}opengl/precompiled/{}/{:016X}.bin",
|
||||
FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir), cache_type, title_id);
|
||||
LOG_INFO(Frontend, "Deleting shader file: {}", path);
|
||||
FileUtil::Delete(path);
|
||||
}
|
||||
const std::string path =
|
||||
fmt::format("{}opengl/transferable/{:016X}.bin",
|
||||
FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir), title_id);
|
||||
LOG_INFO(Frontend, "Deleting shader file: {}", path);
|
||||
FileUtil::Delete(path);
|
||||
}
|
||||
|
||||
void Java_org_citra_citra_1emu_NativeLibrary_deleteVulkanShaderCache(JNIEnv* env, jobject obj,
|
||||
jlong title_id) {
|
||||
for (const std::string_view cache_type : {"vs", "fs", "gs", "pl"}) {
|
||||
const std::string path =
|
||||
fmt::format("{}vulkan/transferable/{:016X}_{}.vkch",
|
||||
FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir), title_id, cache_type);
|
||||
LOG_INFO(Frontend, "Deleting shader file: {}", path);
|
||||
FileUtil::Delete(path);
|
||||
}
|
||||
|
||||
FileUtil::ForeachDirectoryEntry(
|
||||
nullptr,
|
||||
fmt::format("{}vulkan/pipeline", FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir)),
|
||||
[title_id]([[maybe_unused]] u64* num_entries_out, const std::string& directory,
|
||||
const std::string& virtual_name) {
|
||||
if (virtual_name.starts_with(fmt::format("{:016X}", title_id))) {
|
||||
std::string path = directory + DIR_SEP + virtual_name;
|
||||
LOG_INFO(Frontend, "Deleting shader file: {}", path);
|
||||
FileUtil::Delete(path);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
|
@ -161,7 +161,7 @@
|
|||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/game_button_tray"
|
||||
android:id="@+id/horizontal_layout_2"
|
||||
style="@style/ThemeOverlay.Material3.Button.IconButton.Filled.Tonal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
|
@ -199,19 +199,24 @@
|
|||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/compress_tray"
|
||||
android:id="@+id/horizontal_layout_3"
|
||||
style="@style/ThemeOverlay.Material3.Button.IconButton.Filled.Tonal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:gravity="start|center"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/game_button_tray">
|
||||
app:layout_constraintTop_toBottomOf="@+id/horizontal_layout_2">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/delete_cache"
|
||||
style="@style/Widget.Material3.Button.TonalButton.Icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:contentDescription="@string/delete_shader_cache"
|
||||
android:text="@string/delete_shader_cache" />
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
|
|||
|
|
@ -5,13 +5,13 @@
|
|||
android:id="@+id/coordinator_about"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true"
|
||||
android:background="?attr/colorSurface">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/appbar_about"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:fitsSystemWindows="true">
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/toolbar_system_files"
|
||||
|
|
|
|||
|
|
@ -566,6 +566,10 @@ S\'esperen errors gràfics temporals quan estigue activat.</string>
|
|||
<!-- Disk Shader Cache -->
|
||||
<string name="preparing_shaders">Preparant ombrejadors</string>
|
||||
<string name="building_shaders">Construint%s</string>
|
||||
<string name="delete_shader_cache">Eliminar cache d\'ombrejadors</string>
|
||||
<string name="delete_cache_select_backend">Selecciona l\'API gràfica per a eliminar la cache d\'ombrejadors</string>
|
||||
<string name="deleting_shader_cache">Eliminant la caché d\'ombrejadors, per favor espere…</string>
|
||||
<string name="shader_cache_deleted">Caché d\'ombrejadors eliminada</string>
|
||||
|
||||
<!-- About Game Dialog -->
|
||||
<string name="play">Jugar</string>
|
||||
|
|
|
|||
333
src/android/app/src/main/res/values-b+es+419/strings.xml
Normal file
333
src/android/app/src/main/res/values-b+es+419/strings.xml
Normal file
|
|
@ -0,0 +1,333 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<string name="app_disclaimer">Este software ejecutará aplicaciones para la consola portátil Nintendo 3DS. No se incluyen juegos.\n\nAntes de comenzar con la emulación, seleccione una carpeta para almacenar los datos de usuario de Azahar en.\n\nQué es ésto:\n<a href='https://web.archive.org/web/20240304193549/https://github.com/citra-emu/citra/wiki/Citra-Android-user-data-and-storage'>Wiki - Datos de usuario de Azahar Android y almacenamiento</a></string>
|
||||
<string name="app_notification_channel_description">Notificaciones del emulador 3DS Azahar</string>
|
||||
<string name="app_notification_running">Azahar está ejecutándose</string>
|
||||
<string name="app_game_install_description">A continuación, deberás seleccionar una Carpeta de Aplicaciones. Azahar mostrará todas las ROM de 3DS de la carpeta seleccionada en la aplicación.\n\nLas ROM CIA, actualizaciones y los DLC deben instalarse por separado haciendo clic en el icono de la carpeta y seleccionando Instalar CIA.</string>
|
||||
|
||||
<!-- Home Strings -->
|
||||
<string name="grid_menu_core_settings">Configuración</string>
|
||||
<string name="home_options">Opciones</string>
|
||||
<string name="home_search">Buscar</string>
|
||||
<string name="home_games">Aplicaciones</string>
|
||||
<string name="settings_description">Configurar opciones del emulador</string>
|
||||
<string name="install_game_content">Instalar archivo CIA</string>
|
||||
<string name="install_game_content_description">Instalar aplicaciones, actualizaciones o DLC</string>
|
||||
<string name="share_log">Compartir Registro</string>
|
||||
<string name="share_log_description">Compartir el archivo de registro de Azahar para depurar problemas</string>
|
||||
<string name="gpu_driver_manager">Administrador de drivers de GPU</string>
|
||||
<string name="install_gpu_driver">Instalar drivers de GPU</string>
|
||||
<string name="install_gpu_driver_description">Instala drivers alternativos para intentar mejorar el rendimiento o la precisión</string>
|
||||
<string name="driver_already_installed">Driver ya instalado</string>
|
||||
<string name="custom_driver_not_supported">Drivers personalizados no compatibles</string>
|
||||
<string name="custom_driver_not_supported_description">La carga de drivers personalizados no es compatible en este dispositivo.\n¡Comprueba esta opción otra vez en el futuro para ver si ya está disponible!</string>
|
||||
<string name="share_log_not_found">No se encontró ningún archivo de registro</string>
|
||||
<string name="select_games_folder">Selecciona el directorio de aplicaciones</string>
|
||||
<string name="select_games_folder_description">Permite que Azahar llene la lista de aplicaciones</string>
|
||||
<string name="about">Acerca de</string>
|
||||
<string name="citra_description">Un emulador de 3DS de código abierto</string>
|
||||
<string name="about_description">Versión de compilación, créditos y más</string>
|
||||
<string name="games_dir_selected">Directorio de aplicaciones seleccionado</string>
|
||||
<string name="select_citra_user_folder_home_description">Cambia los archivos que Azahar usa para cargar aplicaciones</string>
|
||||
<string name="theme_and_color_description">Cambia la apariencia de la app</string>
|
||||
<string name="install_cia_title">Instalar CIA</string>
|
||||
|
||||
<!-- GPU driver installation -->
|
||||
<string name="select_gpu_driver">Seleccionar driver de GPU</string>
|
||||
<string name="select_gpu_driver_title">¿Quieres reemplazar tu driver actual de GPU?</string>
|
||||
<string name="select_gpu_driver_install">Instalar</string>
|
||||
<string name="select_gpu_driver_default">Por defecto</string>
|
||||
<string name="select_gpu_driver_install_success">Instalado %s</string>
|
||||
<string name="select_gpu_driver_use_default">Usando el driver por defecto de la GPU</string>
|
||||
<string name="select_gpu_driver_error">¡El driver seleccionado no es válido, se usará el driver por defecto del sistema!</string>
|
||||
<string name="system_gpu_driver">Driver de la GPU del sistema</string>
|
||||
<string name="installing_driver">Instalando el driver...</string>
|
||||
|
||||
<!-- About screen strings -->
|
||||
<string name="copied_to_clipboard">Copiado al portapapeles</string>
|
||||
<string name="contributors">Colaboradores</string>
|
||||
<string name="contributors_description">Colaboradores que hicieron posible Azahar</string>
|
||||
<string name="licenses_description">Proyectos utilizados por Azahar para Android</string>
|
||||
<string name="build">Compilación</string>
|
||||
<string name="licenses">Licencias</string>
|
||||
<!-- Setup strings -->
|
||||
<string name="welcome">¡Te damos la bienvenida!</string>
|
||||
<string name="welcome_description">Aprende a configurar <b>Azahar</b> y disfruta de la emulación.</string>
|
||||
<string name="get_started">Comenzar</string>
|
||||
<string name="step_complete">¡Completado!</string>
|
||||
<string name="games">Aplicaciones</string>
|
||||
<string name="games_description">Selecciona la carpeta de <b>Aplicaciones</b> con el botón de abajo.</string>
|
||||
<string name="done">Terminado</string>
|
||||
<string name="done_description">¡Ya estás listo!\n¡Disfruta del emulador!</string>
|
||||
<string name="text_continue">Continuar</string>
|
||||
<string name="notifications">Notificaciones</string>
|
||||
<string name="notifications_description">Concede el permiso de notificaciones con el botón de abajo.</string>
|
||||
<string name="give_permission">Conceder permiso</string>
|
||||
<string name="notification_warning">¿Omitir el permiso de notificaciones?</string>
|
||||
<string name="notification_warning_description">Azahar no podrá notificarte sobre información importante.</string>
|
||||
<string name="filesystem_permission_warning">Faltan permisos</string>
|
||||
<string name="filesystem_permission_warning_description">Azahar necesita permiso para administrar archivos en este dispositivo para poder almacenar y administrar los datos de usuario.\n\nPor favor, concede el permiso antes de continuar.</string>
|
||||
<string name="camera_permission">Cámara</string>
|
||||
<string name="camera_permission_description">Concede el permiso de cámara para emular la cámara de la 3DS.</string>
|
||||
<string name="microphone_permission">Micrófono</string>
|
||||
<string name="microphone_permission_description">Concede el permiso de micrófono para emular el micrófono de la 3DS.</string>
|
||||
<string name="filesystem_permission">Sistema de archivos</string>
|
||||
<string name="filesystem_permission_description">Concede el permiso de sistema de archivos para permitir que Azahar almacene archivos.</string>
|
||||
<string name="permission_denied">Permiso denegado</string>
|
||||
<string name="add_games_warning">¿Omitir la selección de la carpeta de aplicaciones?</string>
|
||||
<string name="add_games_warning_description">No se mostrará nada en la lista de aplicaciones si no se selecciona una carpeta.</string>
|
||||
<string name="permissions">Permisos</string>
|
||||
<string name="select_emulator_data_folders">Carpetas de datos</string>
|
||||
<string name="select_emulator_data_folders_description"><![CDATA[Seleccionar carpetas de datos<br/>(Carpeta de usuario es necesaria)]]></string>
|
||||
<string name="permissions_description">Concede permisos opcionales para usar funciones específicas del emulador</string>
|
||||
<string name="warning_help">Ayuda</string>
|
||||
<string name="warning_skip">Omitir</string>
|
||||
<string name="warning_cancel">Cancelar</string>
|
||||
<string name="select_citra_user_folder">Selecciona la carpeta de usuario</string>
|
||||
<string name="select_citra_user_folder_description"><![CDATA[Selecciona tu directorio de <a href=\"https://web.archive.org/web/20240304193549/https://github.com/citra-emu/citra/wiki/Citra-Android-user-data-and-storage\">datos de usuario</a> con el botón de abajo.]]></string>
|
||||
<string name="select_which_user_directory_to_use">Parece que tienes directorios de usuario configurados tanto para Lime3DS como para Azahar. Probablemente se deba a que actualizaste a Azahar y, cuando se te pidió, elegiste un directorio de usuario diferente al que usabas para Lime3DS.\n\nEsto puede haberte hecho pensar que perdiste partidas guardadas u otras configuraciones; te pedimos disculpas si eso ocurrió.\n\n¿Prefieres volver a usar tu directorio de usuario original de Lime3DS, restaurando la configuración y las partidas guardadas de Lime3DS, o conservar tu directorio de usuario actual de Azahar?\n\nNinguno de los directorios se eliminará, independientemente de tu elección, y puedes cambiar libremente entre ellos usando la opción \"Selecciona la carpeta de usuario\".</string>
|
||||
<string name="keep_current_azahar_directory">Mantener el directorio actual de Azahar</string>
|
||||
<string name="use_prior_lime3ds_directory">Usar el directorio de Lime3DS anterior</string>
|
||||
<string name="select">Seleccionar</string>
|
||||
<string name="cannot_skip">No puedes omitir configurar la carpeta de usuario</string>
|
||||
<string name="cannot_skip_directory_description">Este paso es necesario para permitir que Azahar funcione. Por favor, selecciona un directorio y luego puedes continuar.</string>
|
||||
<string name="selecting_user_directory_without_write_permissions">Has perdido los permisos de escritura en tu <a href="https://web.archive.org/web/20240304193549/https://github.com/citra-emu/citra/wiki/Citra-Android-user-data-and-storage">directorio de datos de usuario</a>, donde se guardan las partidas guardadas y otra información. Esto puede ocurrir después de actualizar Android o algunas aplicaciones. Vuelve a seleccionar el directorio para recuperar los permisos y poder continuar.</string>
|
||||
<string name="invalid_selection">Selección no válida</string>
|
||||
<string name="invalid_user_directory">La selección del directorio de usuario no es válida.\nVuelve a seleccionar el directorio de usuario, asegurándote de navegar hasta él desde la raíz del almacenamiento del dispositivo.</string>
|
||||
<string name="filesystem_permission_lost">Azahar ha perdido el permiso para administrar archivos en este dispositivo. Esto puede ocurrir después de actualizar Android o algunas aplicaciones. Vuelve a conceder este permiso en la siguiente pantalla para seguir usando la aplicación.</string>
|
||||
<string name="set_up_theme_settings">Configuración del tema</string>
|
||||
<string name="setup_theme_settings_description">Configura tus preferencias de tema de Azahar.</string>
|
||||
<string name="setup_set_theme">Establecer tema</string>
|
||||
|
||||
<!-- Search Strings -->
|
||||
<string name="search_and_filter_games">Buscar y filtrar aplicaciones</string>
|
||||
<string name="home_search_games">Buscar aplicaciones</string>
|
||||
<string name="search_recently_played">Jugado recientemente</string>
|
||||
<string name="search_recently_added">Añadido recientemente</string>
|
||||
<string name="search_installed">Instalados</string>
|
||||
|
||||
<!-- Input related strings -->
|
||||
<string name="controller_auto_map">Asignar control automáticamente</string>
|
||||
<string name="controller_auto_map_description">Aplica la asignación de control estándar para todos los botones y ejes</string>
|
||||
<string name="auto_map_prompt">¡Presiona este botón en tu control!</string>
|
||||
<string name="auto_map_image_description">Botones frontales de 3DS en rombo con el botón A resaltado</string>
|
||||
<string name="controller_clear_all">Eliminar todas las asignaciones</string>
|
||||
<string name="controller_clear_all_confirm">Esto eliminará todas los asignaciones del control actual.</string>
|
||||
<string name="controller_circlepad">Circle Pad</string>
|
||||
<string name="controller_c">Palanca C</string>
|
||||
<string name="controller_hotkeys">Teclas de atajo</string>
|
||||
<string name="controller_hotkeys_description">Si la tecla \"Habilitar teclas de acceso rápido\" está asignada, se debe presionar esa tecla además de la tecla de acceso rápido asignada</string>
|
||||
<string name="controller_hotkey_enable_button">Habilitar teclas de acceso rápido</string>
|
||||
<string name="controller_triggers">Gatillos</string>
|
||||
<string name="controller_trigger">Gatillo</string>
|
||||
<string name="controller_dpad">Cruceta</string>
|
||||
<string name="controller_dpad_axis">Cruceta (eje)</string>
|
||||
<string name="controller_dpad_axis_description">Es posible que algunos controles no puedan asignar la Cruceta como un eje. Si ese es el caso, utilice la sección Cruceta (botones).</string>
|
||||
<string name="controller_dpad_button">Cruceta (botón)</string>
|
||||
<string name="controller_dpad_button_description">Solamente asigne la Cruceta a éstos si tiene problemas con las asignaciones de botones de la Cruceta (eje).</string>
|
||||
<string name="controller_axis_vertical">Eje vertical</string>
|
||||
<string name="controller_axis_horizontal">Eje horizontal</string>
|
||||
<string name="direction_up">Arriba</string>
|
||||
<string name="direction_down">Abajo</string>
|
||||
<string name="direction_left">Izquierda</string>
|
||||
<string name="direction_right">Derecha</string>
|
||||
<string name="input_dialog_title">Asigna %1$s %2$s</string>
|
||||
<string name="input_dialog_description">Presiona un botón o mueve una palanca</string>
|
||||
<string name="input_binding">Asignación de botones</string>
|
||||
<string name="input_binding_description">Pulsa un botón o mueve una palanca para asignarlo a %1$s.</string>
|
||||
<string name="input_binding_description_vertical_axis">Presiona ARRIBA en tu joystick.</string>
|
||||
<string name="input_binding_description_horizontal_axis">Presiona DERECHA en tu joystick.</string>
|
||||
<string name="button_home">HOME</string>
|
||||
<string name="button_swap">Intercambiar pantallas</string>
|
||||
<string name="button_turbo">Turbo</string>
|
||||
<string name="input_message_analog_only">¡Este control debe asignarse a una palanca analógica o a un eje de la Cruceta!</string>
|
||||
<string name="input_message_button_only">¡Este dispositivo debe asignarse a un botón del control!</string>
|
||||
<string name="turbo_limit_hotkey">Velocidad turbo</string>
|
||||
<string name="turbo_enabled_toast">Velocidad turbo activada</string>
|
||||
<string name="turbo_disabled_toast">Velocidad turbo desactivada</string>
|
||||
|
||||
<!-- System files strings -->
|
||||
<string name="setup_system_files">Archivos del sistema</string>
|
||||
<string name="setup_system_files_description">Realizar operaciones de archivos del sistema, como instalar archivos del sistema o iniciar el Menú Home</string>
|
||||
<string name="setup_tool_connect">Conectar con la herramienta de configuración Artic</string>
|
||||
<string name="setup_system_files_preamble"><![CDATA[Azahar necesita archivos de una consola real para poder utilizar algunas de sus funciones. Puedes obtener los archivos con la <a href=https://github.com/azahar-emu/ArticSetupTool>herramienta de configuración Artic</a>.<br>Notas:<ul><li><b>Esta operación instalará archivos únicos de la consola en Azahar, ¡no compartas las carpetas de usuario ni nand después de realizar el proceso de configuración!</b></li><li>Tras la configuración, Azahar se vinculará a la consola que ha ejecutado la herramienta de configuración. Puedes desvincular la consola más tarde desde la pestaña \"Archivos de sistema\" del menú de opciones del emulador.</li><li>No te conectes en línea con Azahar y la consola 3DS al mismo tiempo después de configurar los archivos del sistema, ya que esto podría causar problemas.</li><li>Se necesita la configuración de Old 3DS para que la New 3DS funcione (se recomienda configurar ambos modos).</li><li>Ambos modos de configuración funcionarán independientemente del modelo de la consola donde se ejecute la herramienta.</li></ul>]]></string>
|
||||
<string name="setup_system_files_detect">Obteniendo el estado actual de los archivos del sistema, por favor espere...</string>
|
||||
<string name="delete_system_files">Desvincular datos únicos de la consola</string>
|
||||
<string name="delete_system_files_description"><![CDATA[Esta acción desvinculará tu consola real de Azahar, con las siguientes consecuencias:<br><ul><li>OTP, SecureInfo y LocalFriendCodeSeed serán eliminados de Azahar.</li><li>Tu lista de amigos se restablecerá y se cerrará la sesión de tu cuenta NNID/PNID.</li><li>Los archivos del sistema y los títulos de la eShop obtenidos a través de Azahar obtenidos a través de Azahar se volverán inaccesibles hasta que la misma consola se vincule nuevamente usando la herramienta de configuración (los datos guardados no se perderán).</li></ul><br>¿Continuar?]]></string>
|
||||
<string name="setup_system_files_o3ds">Configuración de Old 3DS</string>
|
||||
<string name="setup_system_files_n3ds">Configuración de New 3DS</string>
|
||||
<string name="setup_system_files_possible">La configuración es posible.</string>
|
||||
<string name="setup_system_files_o3ds_needed">La configuración Old 3DS es necesaria primero.</string>
|
||||
<string name="setup_system_files_completed">Configuración completa.</string>
|
||||
<string name="setup_system_files_enter_address">Ingresa la dirección de la herramienta de configuración Artic</string>
|
||||
<string name="setup_system_files_preparing">Preparando configuración, por favor espere...</string>
|
||||
<string name="boot_home_menu">Cargar el Menú HOME</string>
|
||||
<string name="show_home_apps">Mostrar las aplicaciones del menú HOME en la lista de aplicaciones</string>
|
||||
<string name="run_system_setup">Ejecutar la configuración de la consola cuando se ejecute el Menú HOME</string>
|
||||
<string name="home_menu">Menú HOME</string>
|
||||
|
||||
<!-- Generic buttons (Shared with lots of stuff) -->
|
||||
<string name="generic_buttons">Botones</string>
|
||||
<string name="button">Botón</string>
|
||||
|
||||
<!-- System settings strings -->
|
||||
<string name="emulation_settings">Opciones de emulación</string>
|
||||
<string name="username">Nombre de usuario</string>
|
||||
<string name="new_3ds">Modo New 3DS</string>
|
||||
<string name="lle_applets">Usar Applets LLE (si están instaladas)</string>
|
||||
<string name="apply_region_free_patch">Aplicar parche de región libre a las aplicaciones instaladas</string>
|
||||
<string name="apply_region_free_patch_desc">Parchea la región de las aplicaciones instaladas para que estén libres de región, de modo que siempre aparezcan en el menú home.</string>
|
||||
<string name="enable_required_online_lle_modules">Habilitar los módulos LLE necesarios para las funciones en línea (si están instalados)</string>
|
||||
<string name="enable_required_online_lle_modules_desc">Habilita los módulos LLE necesarios para el modo multijugador en línea, acceso a la eShop, etc.</string>
|
||||
<string name="clock">Reloj</string>
|
||||
<string name="init_clock">Reloj</string>
|
||||
<string name="init_clock_description">Configura el reloj emulado de la 3DS para que tenga la misma fecha y hora de tu dispositivo o para simular una distinta.</string>
|
||||
<string name="device_clock">Reloj del sistema</string>
|
||||
<string name="simulated_clock">Reloj simulado</string>
|
||||
<string name="simulated_clock_description">Si el reloj está en \"Reloj simulado\", esto cambia la fecha y hora de inicio.</string>
|
||||
<string name="profile_settings">Opciones de perfil</string>
|
||||
<string name="emulated_region">Región</string>
|
||||
<string name="emulated_language">Idioma</string>
|
||||
<string name="birthday">Cumpleaños</string>
|
||||
<string name="birthday_month">Mes</string>
|
||||
<string name="birthday_day">Día</string>
|
||||
<string name="country">País</string>
|
||||
<string name="play_coins">Monedas de juego</string>
|
||||
<string name="steps_per_hour">Pasos por hora del podómetro</string>
|
||||
<string name="steps_per_hour_description">Número de pasos por hora reportados por el podómetro. Rango de 0 a 65.535.</string>
|
||||
<string name="console_id">ID de la Consola</string>
|
||||
<string name="regenerate_console_id">Regenerar ID de la consola</string>
|
||||
<string name="regenerate_console_id_description">Esto reemplazará tu ID de consola de 3DS virtual por una nueva. Tu ID virtual actual será irrecuperable. Esto puede tener efectos inesperados en algunas aplicaciones. Si usas un archivo de configuración obsoleto esto podría fallar. ¿Quieres continuar?</string>
|
||||
<string name="mac_address">Dirección MAC</string>
|
||||
<string name="regenerate_mac_address">Regenerar dirección MAC</string>
|
||||
<string name="regenerate_mac_address_description">Esto reemplazará tu dirección MAC actual por una nueva. No se recomienda hacerlo si obtuviste la dirección MAC de tu consola real con la herramienta de configuración. ¿Quieres continuar?</string>
|
||||
<string name="plugin_loader">Cargador de complementos 3GX</string>
|
||||
<string name="plugin_loader_description">Carga los complementos 3GX de la SD emulada si están disponibles.</string>
|
||||
<string name="allow_plugin_loader">Permite que las aplicaciones cambien el estado del cargador de complementos.</string>
|
||||
<string name="allow_plugin_loader_description">Permite que las apps de homebrew activen el cargador de complementos incluso cuando está desactivado.</string>
|
||||
<string name="region_mismatch">Advertencia de región no válida</string>
|
||||
<string name="region_mismatch_emulated">La configuración del país no es válida para la región emulada seleccionada.</string>
|
||||
<string name="region_mismatch_console">La configuración del país no es válida para la consola vinculada actual.</string>
|
||||
<string name="storage">Almacenamiento</string>
|
||||
<string name="compress_cia_installs">Comprimir contenido CIA instalado</string>
|
||||
<string name="compress_cia_installs_description">Comprime el contenido de archivos CIA cuando son instalados a la SD emulada. Solo afecta contenido CIA instalado con esta opción activada.</string>
|
||||
|
||||
<!-- Camera settings strings -->
|
||||
<string name="inner_camera">Cámara interior</string>
|
||||
<string name="outer_left_camera">Cámara izquierda externa</string>
|
||||
<string name="outer_right_camera">Cámara derecha externa</string>
|
||||
<string name="image_source">Fuente de la imagen de la cámara</string>
|
||||
<string name="image_source_description">Configura la fuente de imagen de la cámara virtual. Puedes usar un archivo de imagen o una cámara si es compatible.</string>
|
||||
<string name="camera_device">Cámara</string>
|
||||
<string name="camera_device_description">Si la \"Fuente de imagen\" está en \"Cámara del dispositivo\", se usará la cámara del propio dispositivo.</string>
|
||||
<string name="camera_facing_front">Frontal</string>
|
||||
<string name="camera_facing_back">Trasera</string>
|
||||
<string name="camera_facing_external">Externa</string>
|
||||
<string name="image_flip">Rotación</string>
|
||||
|
||||
<!-- Graphics settings strings -->
|
||||
<string name="renderer">Motor gráfico</string>
|
||||
<string name="graphics_api">API gráfica</string>
|
||||
<string name="spirv_shader_gen">Activar generación de shaders SPIR-V</string>
|
||||
<string name="spirv_shader_gen_description">Usa SPIR-V en vez de GLSL para emitir el shader de fragmentos usado para emular PICA.</string>
|
||||
<string name="disable_spirv_optimizer">Desactivar el optimizador de SPIR-V</string>
|
||||
<string name="disable_spirv_optimizer_description">Desactiva el paso de optimización SPIR-V reduciendo considerablemente los tirones y afectando muy poco el rendimiento.</string>
|
||||
<string name="async_shaders">Activar compilación asíncrona de shaders</string>
|
||||
<string name="async_shaders_description">Compila los shaders en segundo plano para reducir los tirones durante la partida. Habrá fallos gráficos temporales cuando esté activado.</string>
|
||||
<string name="linear_filtering">Filtrado lineal</string>
|
||||
<string name="linear_filtering_description">Activa el filtrado linear, que hace que los gráficos del juego se vean más suaves.</string>
|
||||
<string name="use_integer_scaling">Escalado de enteros</string>
|
||||
<string name="use_integer_scaling_description">Escala las pantallas usando un multiplicador entero basado en la resolución original de 3DS. En diseños con dos tamaños de pantalla diferentes, la más grande usará el escalado de enteros.</string>
|
||||
<string name="texture_filter_name">Filtrado de texturas</string>
|
||||
<string name="texture_filter_description">Mejora los gráficos visuales de las aplicaciones aplicando un filtro a las texturas. Los filtros compatibles son Anime4K Ultrafast, Bicubic, ScaleForce, xBRZ freescale y MMPX.</string>
|
||||
<string name="delay_render_thread">Atrasar hilo del motor gráfico del juego</string>
|
||||
<string name="delay_render_thread_description">Retrasa el hilo del motor gráfico del juego cuando envía datos a la GPU. Ayuda con los problemas de rendimiento en las (muy pocas) aplicaciones con tasas de fotogramas dinámicas.</string>
|
||||
<string name="advanced">Avanzado</string>
|
||||
<string name="texture_sampling_name">Muestreo de texturas</string>
|
||||
<string name="texture_sampling_description">Sobrescribe el filtro de muestreo original del juego. Puede ser útil en ciertos casos con juegos con problemas gráficos al aumentar la resolución. Si no estás seguro, déjalo en Controlado por juego.</string>
|
||||
<string name="shaders_accurate_mul">Multiplicación precisa</string>
|
||||
<string name="shaders_accurate_mul_description">Usa una multiplicación más precisa en los shaders por hardware, lo cual puede solucionar ciertos errores gráficos. Al activarse, el rendimiento disminuirá.</string>
|
||||
<string name="asynchronous_gpu">Activar emulación asíncrona de la GPU</string>
|
||||
<string name="asynchronous_gpu_description">Usa un hilo separado para emular la GPU de manera asíncrona. Al activarse, el rendimiento mejorará.</string>
|
||||
<string name="frame_limit_enable">Límite de velocidad</string>
|
||||
<string name="frame_limit_enable_description">Si se activa, la velocidad de emulación se limitará a un porcentaje determinado de velocidad normal. Si se desactiva, la velocidad no tendrá límite y el atajo para la velocidad velocidad turbo no funcionará.</string>
|
||||
<string name="frame_limit_slider">Limitar porcentaje de velocidad</string>
|
||||
<string name="frame_limit_slider_description">Especifica el valor al que se limita la velocidad de emulación. Con el valor por defecto del 100%, la emulación se limitará a la velocidad normal. Los valores altos o bajos aumentarán o reducirán el límite de velocidad.</string>
|
||||
<string name="android_hide_images">Ocultar las imágenes de 3DS de la galería de Android</string>
|
||||
<string name="android_hide_images_description">Evita que Android indexe las imágenes de la cámara, capturas de pantalla y texturas personalizadas de la 3DS y las muestre en la galería. Es posible que tengas que reiniciar el dispositivo después de cambiar esta configuración para que surta efecto.</string>
|
||||
<string name="turbo_limit">Límite de velocidad turbo</string>
|
||||
<string name="turbo_limit_description">Límite de velocidad de emulación que se aplica al activar el turbo.</string>
|
||||
<string name="expand_to_cutout_area">Expandir al área de recorte</string>
|
||||
<string name="expand_to_cutout_area_description">Amplía la pantalla para incluir el recorte de la cámara (o notch).</string>
|
||||
<string name="internal_resolution">Resolución interna</string>
|
||||
<string name="internal_resolution_description">Especifica la resolución de renderizado. Una alta resolución mejorará considerablemente la calidad visual, pero tendrá un gran impacto en el rendimiento y puede causar fallos en ciertas aplicaciones.</string>
|
||||
<string name="internal_resolution_setting_auto">Auto (Tamaño pantalla)</string>
|
||||
<string name="internal_resolution_setting_1x">Nativa (400x240)</string>
|
||||
<string name="internal_resolution_setting_2x">2x Nativa (800x480)</string>
|
||||
<string name="internal_resolution_setting_3x">3x Nativa (1200x720)</string>
|
||||
<string name="internal_resolution_setting_4x">4x Nativa (1600x960)</string>
|
||||
<string name="internal_resolution_setting_5x">5x Nativa (2000x1200)</string>
|
||||
<string name="internal_resolution_setting_6x">6x Nativa (2400x1440)</string>
|
||||
<string name="internal_resolution_setting_7x">7x Nativa (2800x1680)</string>
|
||||
<string name="internal_resolution_setting_8x">8x Nativa (3200x1920)</string>
|
||||
<string name="internal_resolution_setting_9x">9x Nativa (3600x2160)</string>
|
||||
<string name="internal_resolution_setting_10x">10x Nativa (4000x2400)</string>
|
||||
<string name="performance_warning">¡Desactivar esta opción reducirá notablemente el rendimiento de la emulación! Para obtener la mejor experiencia, se recomienda dejarla activada.</string>
|
||||
<string name="debug_warning">Aviso: Modificar estos ajustes reducirá la velocidad de emulación.</string>
|
||||
<string name="stereoscopy">Estereoscopia</string>
|
||||
<string name="render3d">Modo 3D estereoscópico</string>
|
||||
<string name="render3d_description">Elige el modo 3D estereoscópico para el renderizado 3D. Los modos de lado a lado son los más comunes en la actualidad. Los modos anaglifo y entrelazado se aplicarán siempre a todas las pantallas conectadas.</string>
|
||||
<string name="factor3d">Profundidad</string>
|
||||
<string name="factor3d_description">Especifica el valor del regulador 3D. Debería ajustarse por encima del 0% cuando el 3D estereoscópico esté activado.\nNota: Los valores de profundidad superiores al 100% no son posibles en el hardware real y pueden causar problemas gráficos.</string>
|
||||
<string name="disable_right_eye_render">Desactivar el renderizado de ojo derecho</string>
|
||||
<string name="disable_right_eye_render_description">Mejora mucho el rendimiento en algunas aplicaciones, pero puede causar parpadeo en otras.</string>
|
||||
<string name="swap_eyes_3d">Intercambiar ojos</string>
|
||||
<string name="swap_eyes_3d_description">Intercambia qué ojo se muestra en cada lado. Combinado con el modo lado a lado, ¡permite ver en 3D cruzando los ojos!</string>
|
||||
<string name="render_3d_which_display">Renderizado 3D estereoscópico</string>
|
||||
<string name="render_3d_which_display_description">Si se enciende el 3D estereoscópico, y en que pantallas. Las opciones para una sola pantalla solo son relevantes si hay varias pantallas conectadas.</string>
|
||||
<string name="render_3d_which_display_both">Activado (todas las pantallas)</string>
|
||||
<string name="render_3d_which_display_primary">Encendido (Solo Pantalla Principal)</string>
|
||||
<string name="render_3d_which_display_secondary">Encendido (Solo Pantalla Secundaria)</string>
|
||||
<string name="cardboard_vr">Cardboard VR</string>
|
||||
<string name="cardboard_screen_size">Tamaño de la pantalla para Cardboard</string>
|
||||
<string name="cardboard_screen_size_description">Escala la pantalla a un porcentaje de su tamaño original.</string>
|
||||
<string name="cardboard_x_shift">Desplazamiento horizontal</string>
|
||||
<string name="cardboard_x_shift_description">Especifica el porcentaje de espacio vacío para desplazar las pantallas horizontalmente. Los valores positivos acercan los dos ojos al medio, mientras que los negativos los alejan.</string>
|
||||
<string name="cardboard_y_shift">Desplazamiento vertical</string>
|
||||
<string name="cardboard_y_shift_description">Especifica el porcentaje de espacio vacío para desplazar las pantallas verticalmente. Los valores positivos mueven los dos ojos hacia abajo, mientras que los negativos los mueve hacia arriba.</string>
|
||||
<string name="use_shader_jit">Shader JIT</string>
|
||||
<string name="use_disk_shader_cache">Caché de shader en disco</string>
|
||||
<string name="use_disk_shader_cache_description">Reduce los tirones al guardar y cargar shaders generados en el disco. No se puede usar sin activar la opción shader por hardware.</string>
|
||||
<string name="utility">Utilidades</string>
|
||||
<string name="dump_textures">Volcar texturas</string>
|
||||
<string name="dump_textures_description">Las texturas se vuelcan en dump/textures/[Title ID]/.</string>
|
||||
<string name="custom_textures">Texturas personalizadas</string>
|
||||
<string name="custom_textures_description">Las texturas se cargan desde load/textures/[Title ID]/.</string>
|
||||
<string name="preload_textures">Precargar texturas personalizadas</string>
|
||||
<string name="preload_textures_description">Carga todas las texturas personalizadas en la memoria. Esta función puede usar mucha memoria.</string>
|
||||
<string name="async_custom_loading">Carga asíncrona de texturas personalizadas</string>
|
||||
<string name="async_custom_loading_description">Carga las texturas personalizadas de manera asíncrona con hilos de fondo para reducir los tirones de carga.</string>
|
||||
|
||||
<!-- Audio settings strings -->
|
||||
<string name="audio_volume">Volumen</string>
|
||||
<string name="audio_stretch">Estiramiento de audio</string>
|
||||
<string name="audio_stretch_description">Estira el audio para reducir los tirones. Al activarse, la latencia del audio se incrementará y reducirá un poco el rendimiento.</string>
|
||||
<string name="realtime_audio">Activar audio en tiempo real</string>
|
||||
<string name="realtime_audio_description">Escala la velocidad de reproducción de audio para compensar por perdidas en la velocidad de fotogramas durante la emulación. Esto significa que el audio se reproducirá a velocidad completa incluso cuando la velocidad de fotogramas del juego sea baja. Puede causar problemas de desincronización de audio.</string>
|
||||
<string name="audio_input_type">Dispositivo de entrada de audio</string>
|
||||
<string name="sound_output_mode">Modo de salida del audio</string>
|
||||
|
||||
<!-- Debug settings strings -->
|
||||
<string name="cpu_jit">CPU JIT</string>
|
||||
<string name="cpu_jit_description">Usa el compilador Just-in-Time (JIT) para emular la CPU. Cuando este encendido, el rendimiento de los juegos mejorará significativamente. </string>
|
||||
<string name="hw_shaders">Activar shader por hardware</string>
|
||||
<string name="hw_shaders_description">Usa el hardware para emular los shaders de la 3DS. Al activarse, el rendimiento mejorará notablemente.</string>
|
||||
<string name="cpu_clock_speed">Velocidad de reloj de la CPU</string>
|
||||
<string name="vsync">Activar V-Sync</string>
|
||||
<string name="vsync_description">Sincroniza la velocidad de fotogramas con la tasa de actualización de tu dispositivo. Puede causar latencia de entrada adicional, pero puede reducir el rasgado de imagen en algunos casos.</string>
|
||||
<string name="renderer_debug">Renderizador de depuración</string>
|
||||
<string name="toggle_unique_data_console_type">Alternar tipo de consola.</string>
|
||||
<string name="emulation_toggle_controls">Alternar Controles</string>
|
||||
<string name="emulation_switch_secondary_layout_description">El diseño usado por una pantalla secundaria conectada, alámbrica o inalámbrica (Chromecast, Miracast)</string>
|
||||
</resources>
|
||||
|
|
@ -566,6 +566,10 @@ Se esperan fallos gráficos temporales cuando ésta esté activado.</string>
|
|||
<!-- Disk Shader Cache -->
|
||||
<string name="preparing_shaders">Preparando shaders</string>
|
||||
<string name="building_shaders">Construyendo%s</string>
|
||||
<string name="delete_shader_cache">Eliminar caché de sombreadores </string>
|
||||
<string name="delete_cache_select_backend">Selecciona la API gráfica para eliminar la caché de sombreadores</string>
|
||||
<string name="deleting_shader_cache">Eliminando la caché de sombreadores, por favor espere…</string>
|
||||
<string name="shader_cache_deleted">Caché de sombreadores eliminada</string>
|
||||
|
||||
<!-- About Game Dialog -->
|
||||
<string name="play">Jugar</string>
|
||||
|
|
|
|||
|
|
@ -565,7 +565,6 @@
|
|||
<!-- Disk Shader Cache -->
|
||||
<string name="preparing_shaders">Przygotowanie shaderów</string>
|
||||
<string name="building_shaders">Tworzenie%s</string>
|
||||
|
||||
<!-- About Game Dialog -->
|
||||
<string name="play">Odtwórz</string>
|
||||
<string name="uninstall_cia">Odinstaluj aktualizacje</string>
|
||||
|
|
|
|||
|
|
@ -565,6 +565,10 @@
|
|||
<!-- Disk Shader Cache -->
|
||||
<string name="preparing_shaders">Preparando Shaders</string>
|
||||
<string name="building_shaders">Construindo %s</string>
|
||||
<string name="delete_shader_cache">Excluir Cache de Shaders</string>
|
||||
<string name="delete_cache_select_backend">Selecione a API gráfica para excluir o cache de shaders</string>
|
||||
<string name="deleting_shader_cache">Excluindo cache de shaders do título, aguarde...</string>
|
||||
<string name="shader_cache_deleted">Cache de shaders excluído</string>
|
||||
|
||||
<!-- About Game Dialog -->
|
||||
<string name="play">Jogar</string>
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@
|
|||
<string name="give_permission">Berechtigung erteilen</string>
|
||||
<string name="notification_warning">Möchtest du keine Benachrichtigungsberechtigung gewähren?</string>
|
||||
<string name="notification_warning_description">Azahar wird dich nicht über wichtige Informationen benachrichtigen.</string>
|
||||
<string name="filesystem_permission_warning">Fehlende Berechtigungen</string>
|
||||
<string name="camera_permission">Kamera</string>
|
||||
<string name="camera_permission_description">Teile unten die Kameraberechtigung, um die 3DS-Kamera zu emulieren</string>
|
||||
<string name="microphone_permission">Mikrofon</string>
|
||||
|
|
@ -89,6 +90,7 @@
|
|||
<string name="cannot_skip">Der Nutzer Ordner muss gesetzt sein</string>
|
||||
<string name="cannot_skip_directory_description">Dieser Schritt ist nötig, damit Azahar funktionieren kann. Bitte wähle ein Verzeichnis aus, damit du fortfahren kannst.</string>
|
||||
<string name="selecting_user_directory_without_write_permissions">Die Schreibrechte auf dein <a href="https://web.archive.org/web/20240304193549/https://github.com/citra-emu/citra/wiki/Citra-Android-user-data-and-storage">Nutzerdaten</a>-Verzeichnis, wo deine Speicherstände und andere Informationen gespeichert sind, fehlen. Dies kann durch ein Anwendungsupdate, oder ein Androidupdate passiert sein. Bitte wähle das Verzeichnis erneut aus, damit die Berechtigungen wiederhergestellt werden können.</string>
|
||||
<string name="invalid_selection">Ungültige Auswahl</string>
|
||||
<string name="set_up_theme_settings">Design-Einstellung</string>
|
||||
<string name="setup_theme_settings_description">Konfigurieren sie ihre Designeinstellungen für Azahar.</string>
|
||||
<string name="setup_set_theme">Design wählen</string>
|
||||
|
|
@ -100,6 +102,7 @@
|
|||
<string name="search_recently_added">Kürzlich hinzugefügt</string>
|
||||
<string name="search_installed">Installiert</string>
|
||||
|
||||
<string name="controller_clear_all">Alle Bindungen zurücksetzen</string>
|
||||
<string name="controller_circlepad">Schiebepad</string>
|
||||
<string name="controller_c">C-Stick</string>
|
||||
<string name="controller_hotkeys">Tastenkürzel</string>
|
||||
|
|
@ -214,6 +217,7 @@
|
|||
<string name="async_shaders_description">Kompiliere Shader im Hintergrund, um das Stottern des Spiels zu reduzieren. Dadurch kann es zu temporären grafischen Fehlern während des Spielens kommen.</string>
|
||||
<string name="linear_filtering">Lineare Filterung</string>
|
||||
<string name="linear_filtering_description">Aktiviert lineare Filterung, welche die Spieltexturen glättet.</string>
|
||||
<string name="use_integer_scaling">Integer-Skalierung</string>
|
||||
<string name="texture_filter_name">Texturfilter</string>
|
||||
<string name="texture_filter_description">Verbessert die Optik von Anwendungen durch Anwenden eines Filters auf Texturen. Die unterstützten Filter sind Anime4K Ultrafast, Bicubic, ScaleForce, xBRZ freescale und MMPX.</string>
|
||||
<string name="delay_render_thread_description">Verzögert den Render-Thread des Spiels, wenn er Daten an die GPU sendet. Hilft bei Leistungsproblemen in den (sehr wenigen) Anwendungen mit dynamischen Frameraten.</string>
|
||||
|
|
@ -222,6 +226,7 @@
|
|||
<string name="texture_sampling_description">Setzt den von Spielen verwendeten Sampling-Filter außer Kraft. Dies kann in bestimmten Fällen nützlich sein, wenn sich die Spiele beim Hochskalieren schlecht verhalten. Wenn du dir unsicher bist, setze diese Einstellung auf „Spielgesteuert“</string>
|
||||
<string name="shaders_accurate_mul">Genaue Multiplikation</string>
|
||||
<string name="shaders_accurate_mul_description">Benutzt genauere Multiplikation in Hardware-Shadern, welche einige Grafikbugs fixen kann. Wenn aktiviert, ist die Leistung reduziert.</string>
|
||||
<string name="asynchronous_gpu">Aktiviere Asynchrone GPU-Emulation</string>
|
||||
<string name="asynchronous_gpu_description">Verwendet einen separaten Thread, um die GPU asynchron zu emulieren. Wenn aktiviert, wird die Leistung verbessert.</string>
|
||||
<string name="frame_limit_enable">Höchstgeschwindigkeit</string>
|
||||
<string name="frame_limit_enable_description">Wenn aktiviert, wird die Emulationsgeschwindigkeit auf einen angegebenen Prozentsatz der normalen Geschwindigkeit begrenzt. Wenn diese Option deaktiviert ist, wird die Emulationsgeschwindigkeit nicht begrenzt und der Hotkey für die Turbogeschwindigkeit funktioniert nicht.</string>
|
||||
|
|
@ -252,6 +257,7 @@
|
|||
<string name="factor3d_description">Gibt den Wert des 3D-Schiebereglers an. Dieser Wert sollte auf mehr als 0 % eingestellt werden, wenn Stereoskopisches 3D aktiviert ist.\nHinweis: Tiefenwerte über 100% werden nicht von echter Hardware unterstützt und könnten zu grafischen Fehlern führen.</string>
|
||||
<string name="disable_right_eye_render">Rendering für das rechte Auge deaktivieren</string>
|
||||
<string name="disable_right_eye_render_description">Verbessert die Leistung in einigen Anwendungen erheblich, kann in anderen jedoch zu Flackern führen.</string>
|
||||
<string name="swap_eyes_3d">Augen tauschen</string>
|
||||
<string name="cardboard_vr">Karton-VR</string>
|
||||
<string name="cardboard_screen_size">Karton-Bildschirmgröße</string>
|
||||
<string name="cardboard_screen_size_description">Skaliert den Bildschirm auf einen Prozentsatz seiner Originalgröße.</string>
|
||||
|
|
@ -276,6 +282,7 @@
|
|||
<string name="audio_volume">Lautstärke</string>
|
||||
<string name="audio_stretch">Audiodehnung</string>
|
||||
<string name="audio_stretch_description">Dehnt Audio, um Stottern zu reduzieren. Wenn aktiviert, wird die Audiolatenz erhöht und die Leistung leicht verschlechtert.</string>
|
||||
<string name="realtime_audio">Echtzeitaudio aktivieren</string>
|
||||
<string name="realtime_audio_description">Skaliert die Tonabspielgeschwindigkeit, um Einbrüche in der Emulationsframerate zu minimieren. Das bedeutet, dass der Ton in voller Geschwindigkeit abspielt, selbst wenn die Framerate des Spiels niedrig ist. Kann zu Tonverschiebungen führen.</string>
|
||||
<string name="audio_input_type">Audioeingabegerät</string>
|
||||
<string name="sound_output_mode">Tonausgabemodus</string>
|
||||
|
|
@ -289,7 +296,9 @@
|
|||
<string name="vsync">V-Sync aktivieren</string>
|
||||
<string name="renderer_debug">Debug-Renderer</string>
|
||||
<string name="renderer_debug_description">Zusätzliche grafisch spezifische Debuginformationen werden protokolliert. Wenn dies aktiviert ist, ist die Leistung des Spiels minimal reduziert.</string>
|
||||
<string name="instant_debug_log">Log Output bei jeder Nachricht leeren</string>
|
||||
<string name="instant_debug_log_description">Überträgt das Debugprotokoll sofort in eine Datei. Verwenden Sie dies, wenn Azahar abstürzt und die Protokollausgabe abgeschnitten wird.</string>
|
||||
<string name="delay_start_lle_modules">Start mit LLE-Module verzögern</string>
|
||||
<string name="delay_start_lle_modules_description">Verzögert den Start der App, wenn die LLE-Module aktiviert sind.</string>
|
||||
<string name="deterministic_async_operations">Deterministische asynchrone Operationen</string>
|
||||
<string name="deterministic_async_operations_description">Asynchrone Operationen werden für Debug-Zwecke deterministisch. Die Aktivierung dieser Funktion kann zum Einfrieren führen.</string>
|
||||
|
|
@ -404,6 +413,10 @@
|
|||
<string name="emulation_screen_layout_original">Original</string>
|
||||
<string name="emulation_portrait_layout_top_full">Standard</string>
|
||||
<string name="emulation_screen_layout_custom">Benutzerdefinierte Anordnung</string>
|
||||
<string name="bg_color">Hintergrundfarbe</string>
|
||||
<string name="bg_red">Rot</string>
|
||||
<string name="bg_green">Grün</string>
|
||||
<string name="bg_blue">Blau</string>
|
||||
<string name="emulation_small_screen_position">Position des kleinen Bildschirms</string>
|
||||
<string name="small_screen_position_description">Wo soll der kleine Bildschirm im Verhältnis zum großen Bildschirm in der Großbild-Anordnung erscheinen?</string>
|
||||
<string name="small_screen_position_top_right">Oben Rechts</string>
|
||||
|
|
@ -520,11 +533,27 @@
|
|||
<string name="create_shortcut">Verknüpfung erstellen</string>
|
||||
<string name="shortcut_name_empty">Verknüpfungsname darf nicht leer sein</string>
|
||||
<string name="shortcut_image_stretch_toggle">Bildgröße anpassen</string>
|
||||
<string name="game_context_id">ID:</string>
|
||||
<string name="game_context_file">Datei:</string>
|
||||
<string name="game_context_insert">Spielkarte einsetzen</string>
|
||||
<string name="game_context_eject">Spielkarte Entfernen</string>
|
||||
|
||||
<!-- Performance Overlay settings -->
|
||||
<string name="performance_overlay_show">Leistungs-Overlay anzeigen</string>
|
||||
<string name="performance_overlay_options">Leistungs-Overlay</string>
|
||||
<string name="performance_overlay_enable">Leistungsoverlay aktivieren</string>
|
||||
<string name="performance_overlay_options_description">Konfigurieren Sie, ob das Leistungs-Overlay angezeigt wird und welche Informationen angezeigt werden.</string>
|
||||
<string name="performance_overlay_show_fps">FPS anzeigen</string>
|
||||
<string name="performance_overlay_show_frametime">Frametime anzeigen</string>
|
||||
<string name="performance_overlay_show_speed">Geschwindigkeit anzeigen</string>
|
||||
<string name="performance_overlay_show_app_ram_usage">App-Speichernutzung anzeigen</string>
|
||||
<string name="performance_overlay_show_available_ram">Verfügbaren Speicher anzeigen</string>
|
||||
<string name="performance_overlay_show_battery_temp">Batterietemperatur anzeigen</string>
|
||||
<string name="performance_overlay_position">Overlay-Position</string>
|
||||
<string name="performance_overlay_position_top_left">Oben Links</string>
|
||||
<string name="performance_overlay_position_top_right">Oben Rechts</string>
|
||||
<string name="performance_overlay_position_bottom_left">Unten Links</string>
|
||||
<string name="performance_overlay_position_bottom_right">Unten Rechts</string>
|
||||
<!-- Cheats -->
|
||||
<string name="cheats">Cheats</string>
|
||||
<string name="cheats_add">Cheat hinzufügen</string>
|
||||
|
|
@ -815,4 +844,10 @@
|
|||
<string name="emulation_occupied_quicksave_slot">Schnellspeichern - %1$tF %1$tR</string>
|
||||
<string name="quickload_not_found">Kein Schnellspeicher vorhanden.</string>
|
||||
|
||||
<!-- File Compression -->
|
||||
<string name="compress">Komprimiere</string>
|
||||
<string name="compressing">Wird komprimiert...</string>
|
||||
<string name="decompress">Dekomprimiere</string>
|
||||
<string name="decompressing">Wird dekomprimiert...</string>
|
||||
<string name="compress_success">Alle Dateien wurden erfolgreich komprimiert.</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -565,6 +565,10 @@
|
|||
<!-- Disk Shader Cache -->
|
||||
<string name="preparing_shaders">Préparation des shaders</string>
|
||||
<string name="building_shaders">Construction %s</string>
|
||||
<string name="delete_shader_cache">Supprimer le cache de shaders</string>
|
||||
<string name="delete_cache_select_backend">Sélectionnez l\'API graphique pour laquelle vous voulez supprimer le cache de shaders</string>
|
||||
<string name="deleting_shader_cache">Suppression du cache de shaders pour cette application, veuillez patienter ...</string>
|
||||
<string name="shader_cache_deleted">Le cache de shaders a été supprimé.</string>
|
||||
|
||||
<!-- About Game Dialog -->
|
||||
<string name="play">Jouer</string>
|
||||
|
|
|
|||
|
|
@ -565,6 +565,10 @@
|
|||
<!-- Disk Shader Cache -->
|
||||
<string name="preparing_shaders">Preparazione degli shader</string>
|
||||
<string name="building_shaders">Compilazione %s</string>
|
||||
<string name="delete_shader_cache">Cancella cache shader</string>
|
||||
<string name="delete_cache_select_backend">Seleziona per quale API grafica eliminare la cache shader</string>
|
||||
<string name="deleting_shader_cache">Eliminazione della cache shader per il titolo, attendi...</string>
|
||||
<string name="shader_cache_deleted">Cache shader cancellate</string>
|
||||
|
||||
<!-- About Game Dialog -->
|
||||
<string name="play">Riproduci</string>
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@
|
|||
<string name="emulation_configure_controls">Konfigurer Kontroller</string>
|
||||
<string name="emulation_edit_layout">Endre Utseende </string>
|
||||
<string name="emulation_done">Ferdig</string>
|
||||
<string name="emulation_toggle_controls">Veksle Kontroller</string>
|
||||
<string name="emulation_toggle_controls">Bytt Kontrollene</string>
|
||||
<string name="emulation_control_scale">Juster Skala</string>
|
||||
<string name="emulation_open_settings">Åpne Innstillinger</string>
|
||||
<string name="emulation_switch_screen_layout">Landskap Skjermoppsett</string>
|
||||
|
|
|
|||
|
|
@ -565,7 +565,6 @@
|
|||
<!-- Disk Shader Cache -->
|
||||
<string name="preparing_shaders">Förbereder shaders</string>
|
||||
<string name="building_shaders">Bygger %s</string>
|
||||
|
||||
<!-- About Game Dialog -->
|
||||
<string name="play">Spela</string>
|
||||
<string name="uninstall_cia">Avinstallera applikation</string>
|
||||
|
|
|
|||
|
|
@ -586,6 +586,10 @@
|
|||
<!-- Disk Shader Cache -->
|
||||
<string name="preparing_shaders">Preparing Shaders</string>
|
||||
<string name="building_shaders">Building %s</string>
|
||||
<string name="delete_shader_cache">Delete Shader Cache</string>
|
||||
<string name="delete_cache_select_backend">Select which graphics API to delete the shader cache</string>
|
||||
<string name="deleting_shader_cache">Deleting shader cache for title, please wait…</string>
|
||||
<string name="shader_cache_deleted">Shader cache deleted</string>
|
||||
|
||||
<!-- About Game Dialog -->
|
||||
<string name="play">Play</string>
|
||||
|
|
|
|||
|
|
@ -31,6 +31,11 @@ add_library(azahar_libretro SHARED
|
|||
|
||||
create_target_directory_groups(azahar_libretro)
|
||||
|
||||
if (BSD STREQUAL "NetBSD")
|
||||
include(DisablePaxMprotect)
|
||||
disable_pax_mprotect(azahar_libretro)
|
||||
endif()
|
||||
|
||||
target_link_libraries(citra_common PRIVATE libretro)
|
||||
target_link_libraries(citra_core PRIVATE libretro)
|
||||
target_link_libraries(video_core PRIVATE libretro)
|
||||
|
|
|
|||
|
|
@ -105,31 +105,6 @@ unsigned retro_api_version() {
|
|||
return RETRO_API_VERSION;
|
||||
}
|
||||
|
||||
void LibRetro::OnConfigureEnvironment() {
|
||||
|
||||
#ifdef HAVE_LIBRETRO_VFS
|
||||
struct retro_vfs_interface_info vfs_iface_info{1, nullptr};
|
||||
LibRetro::SetVFSCallback(&vfs_iface_info);
|
||||
#endif
|
||||
|
||||
LibRetro::RegisterCoreOptions();
|
||||
|
||||
static const struct retro_controller_description controllers[] = {
|
||||
{"Nintendo 3DS", RETRO_DEVICE_JOYPAD},
|
||||
};
|
||||
|
||||
static const struct retro_controller_info ports[] = {
|
||||
{controllers, 1},
|
||||
{nullptr, 0},
|
||||
};
|
||||
|
||||
LibRetro::SetControllerInfo(ports);
|
||||
}
|
||||
|
||||
uintptr_t LibRetro::GetFramebuffer() {
|
||||
return emu_instance->hw_render.get_current_framebuffer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates Citra's settings with Libretro's.
|
||||
*/
|
||||
|
|
@ -598,6 +573,7 @@ bool retro_load_game(const struct retro_game_info* info) {
|
|||
LibRetro::DisplayMessage("Failed to set HW renderer");
|
||||
return false;
|
||||
}
|
||||
LibRetro::SetFramebufferCallback(emu_instance->hw_render.get_current_framebuffer);
|
||||
#endif
|
||||
break;
|
||||
case Settings::GraphicsAPI::Vulkan:
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
#include "audio_core/libretro_sink.h"
|
||||
#include "common/scm_rev.h"
|
||||
#include "core/3ds.h"
|
||||
#include "core_settings.h"
|
||||
#include "emu_window/libretro_window.h"
|
||||
#include "environment.h"
|
||||
|
||||
|
|
@ -26,6 +27,7 @@ static retro_audio_sample_batch_t audio_batch_cb;
|
|||
static retro_environment_t environ_cb;
|
||||
static retro_input_poll_t input_poll_cb;
|
||||
static retro_input_state_t input_state_cb;
|
||||
static retro_hw_get_current_framebuffer_t framebuffer_cb;
|
||||
|
||||
} // namespace
|
||||
|
||||
|
|
@ -61,7 +63,14 @@ bool GetMicrophoneInterface(struct retro_microphone_interface* mic_interface) {
|
|||
}
|
||||
|
||||
Settings::GraphicsAPI GetPreferredRenderer() {
|
||||
// try and maintain the current driver
|
||||
// On Android, we really want to default to Vulkan if we can, so we'll ignore the frontend's
|
||||
// recommendation if possible...
|
||||
#if defined(ANDROID) && defined(ENABLE_VULKAN)
|
||||
return Settings::GraphicsAPI::Vulkan;
|
||||
#endif
|
||||
// ...Otherwise negotiate with the RetroArch frontend as usual
|
||||
|
||||
// Attempt to use the renderer recommended by the frontend if possible
|
||||
retro_hw_context_type context_type = RETRO_HW_CONTEXT_OPENGL;
|
||||
environ_cb(RETRO_ENVIRONMENT_GET_PREFERRED_HW_RENDER, &context_type);
|
||||
switch (context_type) {
|
||||
|
|
@ -80,7 +89,7 @@ Settings::GraphicsAPI GetPreferredRenderer() {
|
|||
default:
|
||||
break;
|
||||
}
|
||||
// we can't maintain the current driver, need to switch
|
||||
// We can't get a recommendation from the frontend, so fall back to whatever's available
|
||||
#if defined(ENABLE_VULKAN)
|
||||
return Settings::GraphicsAPI::Vulkan;
|
||||
#elif defined(ENABLE_OPENGL)
|
||||
|
|
@ -225,6 +234,34 @@ bool CanUseJIT() {
|
|||
}
|
||||
#endif
|
||||
|
||||
void OnConfigureEnvironment() {
|
||||
#ifdef HAVE_LIBRETRO_VFS
|
||||
struct retro_vfs_interface_info vfs_iface_info{1, nullptr};
|
||||
SetVFSCallback(&vfs_iface_info);
|
||||
#endif
|
||||
|
||||
RegisterCoreOptions();
|
||||
|
||||
static const struct retro_controller_description controllers[] = {
|
||||
{"Nintendo 3DS", RETRO_DEVICE_JOYPAD},
|
||||
};
|
||||
|
||||
static const struct retro_controller_info ports[] = {
|
||||
{controllers, 1},
|
||||
{nullptr, 0},
|
||||
};
|
||||
|
||||
SetControllerInfo(ports);
|
||||
}
|
||||
|
||||
void SetFramebufferCallback(retro_hw_get_current_framebuffer_t cb) {
|
||||
framebuffer_cb = cb;
|
||||
}
|
||||
|
||||
uintptr_t GetFramebuffer() {
|
||||
return framebuffer_cb ? framebuffer_cb() : 0;
|
||||
}
|
||||
|
||||
}; // namespace LibRetro
|
||||
|
||||
void retro_get_system_info(struct retro_system_info* info) {
|
||||
|
|
|
|||
|
|
@ -109,6 +109,9 @@ bool HasUpdatedConfig();
|
|||
/// Returns the current framebuffer.
|
||||
uintptr_t GetFramebuffer();
|
||||
|
||||
/// Sets the callback used by GetFramebuffer().
|
||||
void SetFramebufferCallback(retro_hw_get_current_framebuffer_t cb);
|
||||
|
||||
/// Tells the frontend that we are done.
|
||||
bool Shutdown();
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,11 @@ add_executable(citra_meta
|
|||
|
||||
set_target_properties(citra_meta PROPERTIES OUTPUT_NAME "azahar")
|
||||
|
||||
if (BSD STREQUAL "NetBSD")
|
||||
include(DisablePaxMprotect)
|
||||
disable_pax_mprotect(citra_meta)
|
||||
endif()
|
||||
|
||||
if (APPLE)
|
||||
set(DIST_DIR "../../dist/apple")
|
||||
set(APPLE_RESOURCES
|
||||
|
|
|
|||
|
|
@ -174,6 +174,8 @@ add_library(citra_qt STATIC EXCLUDE_FROM_ALL
|
|||
multiplayer/state.cpp
|
||||
multiplayer/state.h
|
||||
multiplayer/validation.h
|
||||
notification_led.cpp
|
||||
notification_led.h
|
||||
precompiled_headers.h
|
||||
qt_image_interface.cpp
|
||||
qt_image_interface.h
|
||||
|
|
@ -275,6 +277,10 @@ target_link_libraries(citra_qt PRIVATE audio_core citra_common citra_core input_
|
|||
target_link_libraries(citra_qt PRIVATE Boost::boost nihstro-headers Qt6::Widgets Qt6::Multimedia Qt6::Concurrent)
|
||||
target_link_libraries(citra_qt PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
|
||||
|
||||
if (MINGW)
|
||||
target_link_libraries(citra_qt PRIVATE dwmapi)
|
||||
endif()
|
||||
|
||||
if (ENABLE_OPENGL)
|
||||
target_link_libraries(citra_qt PRIVATE glad)
|
||||
endif()
|
||||
|
|
|
|||
|
|
@ -142,6 +142,61 @@ constexpr int default_mouse_timeout = 2500;
|
|||
|
||||
const int GMainWindow::max_recent_files_item;
|
||||
|
||||
// There is a bug in the QT implementation on MSYS2 builds
|
||||
// that cause corners to appear when the app is switched to
|
||||
// fullscreen. The following code aims to fix that issue
|
||||
// until it is addressed upstream. It works by manually
|
||||
// disabling corners through the DWM API.
|
||||
// TODO(PabloMK7): Remove once the upstream bug is solved.
|
||||
#if defined(_WIN32) && !defined(_MSC_VER)
|
||||
#define NEEDS_ROUND_CORNERS_FIX
|
||||
#endif
|
||||
|
||||
#ifdef NEEDS_ROUND_CORNERS_FIX
|
||||
#include <dwmapi.h>
|
||||
class WindowCornerManager {
|
||||
public:
|
||||
static WindowCornerManager& instance() {
|
||||
static WindowCornerManager inst;
|
||||
return inst;
|
||||
}
|
||||
|
||||
void blockRoundedCorners(QWidget* widget, bool block) {
|
||||
HWND hwnd = reinterpret_cast<HWND>(widget->winId());
|
||||
DWORD pref;
|
||||
|
||||
if (block) {
|
||||
pref = DWMWCP_DEFAULT;
|
||||
if (SUCCEEDED(DwmGetWindowAttribute(hwnd, DWMWA_WINDOW_CORNER_PREFERENCE, &pref,
|
||||
sizeof(pref)))) {
|
||||
original_prefs[hwnd] = pref;
|
||||
} else {
|
||||
original_prefs[hwnd] = DWMWCP_DEFAULT;
|
||||
}
|
||||
|
||||
pref = DWMWCP_DONOTROUND;
|
||||
DwmSetWindowAttribute(hwnd, DWMWA_WINDOW_CORNER_PREFERENCE, &pref, sizeof(pref));
|
||||
} else {
|
||||
auto it = original_prefs.find(hwnd);
|
||||
if (it == original_prefs.end())
|
||||
return;
|
||||
|
||||
pref = it->second;
|
||||
|
||||
DwmSetWindowAttribute(hwnd, DWMWA_WINDOW_CORNER_PREFERENCE, &pref, sizeof(pref));
|
||||
|
||||
original_prefs.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
WindowCornerManager() = default;
|
||||
~WindowCornerManager() = default;
|
||||
|
||||
std::unordered_map<HWND, DWORD> original_prefs;
|
||||
};
|
||||
#endif
|
||||
|
||||
static QString PrettyProductName() {
|
||||
#ifdef _WIN32
|
||||
// After Windows 10 Version 2004, Microsoft decided to switch to a different notation: 20H2
|
||||
|
|
@ -352,6 +407,8 @@ GMainWindow::GMainWindow(Core::System& system_)
|
|||
Camera::RegisterFactory("image", std::make_unique<Camera::StillImageCameraFactory>());
|
||||
Camera::RegisterFactory("qt", std::make_unique<Camera::QtMultimediaCameraFactory>(qt_cameras));
|
||||
|
||||
system.RegisterInfoLEDColorChanged([this]() { emit InfoLEDColorChanged(); });
|
||||
|
||||
LoadTranslation();
|
||||
|
||||
Pica::g_debug_context = Pica::DebugContext::Construct();
|
||||
|
|
@ -364,7 +421,7 @@ GMainWindow::GMainWindow(Core::System& system_)
|
|||
|
||||
#ifdef USE_DISCORD_PRESENCE
|
||||
SetDiscordEnabled(UISettings::values.enable_discord_presence.GetValue());
|
||||
discord_rpc->Update();
|
||||
discord_rpc->Update(false);
|
||||
#endif
|
||||
|
||||
play_time_manager = std::make_unique<PlayTime::PlayTimeManager>();
|
||||
|
|
@ -465,24 +522,10 @@ GMainWindow::GMainWindow(Core::System& system_)
|
|||
|
||||
#ifdef ENABLE_OPENGL
|
||||
gl_renderer = GetOpenGLRenderer();
|
||||
#if defined(_WIN32)
|
||||
if (gl_renderer.startsWith(QStringLiteral("D3D12"))) {
|
||||
// OpenGLOn12 supports but does not yet advertise OpenGL 4.0+
|
||||
// We can override the version here to allow Citra to work.
|
||||
// TODO: Remove this when OpenGL 4.0+ is advertised.
|
||||
qputenv("MESA_GL_VERSION_OVERRIDE", "4.6");
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_VULKAN
|
||||
physical_devices = GetVulkanPhysicalDevices();
|
||||
if (physical_devices.empty()) {
|
||||
QMessageBox::warning(this, tr("No Suitable Vulkan Devices Detected"),
|
||||
tr("Vulkan initialization failed during boot.<br/>"
|
||||
"Your GPU may not support Vulkan 1.1, or you do not "
|
||||
"have the latest graphics driver."));
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!game_path.isEmpty()) {
|
||||
|
|
@ -620,6 +663,20 @@ void GMainWindow::InitializeWidgets() {
|
|||
statusBar()->addPermanentWidget(multiplayer_state->GetStatusText());
|
||||
statusBar()->addPermanentWidget(multiplayer_state->GetStatusIcon());
|
||||
|
||||
QFrame* sep = new QFrame(this);
|
||||
sep->setFrameShape(QFrame::VLine);
|
||||
sep->setFrameShadow(QFrame::Sunken);
|
||||
sep->setFixedHeight(16);
|
||||
statusBar()->addPermanentWidget(sep);
|
||||
|
||||
notification_led = new LedWidget();
|
||||
notification_led->setToolTip(tr("Emulated notification LED"));
|
||||
statusBar()->addPermanentWidget(notification_led);
|
||||
connect(this, &GMainWindow::InfoLEDColorChanged, this, [this] {
|
||||
auto led_color = system.GetInfoLEDColor();
|
||||
notification_led->setColor(QColor(led_color.r(), led_color.g(), led_color.b()));
|
||||
});
|
||||
|
||||
statusBar()->setVisible(true);
|
||||
|
||||
// Removes an ugly inner border from the status bar widgets under Linux
|
||||
|
|
@ -973,7 +1030,7 @@ void GMainWindow::OnAppFocusStateChanged(Qt::ApplicationState state) {
|
|||
OnPauseGame();
|
||||
} else if (!emu_thread->IsRunning() && auto_paused && state == Qt::ApplicationActive) {
|
||||
auto_paused = false;
|
||||
OnStartGame();
|
||||
OnResumeGame(false);
|
||||
}
|
||||
}
|
||||
if (UISettings::values.mute_when_in_background) {
|
||||
|
|
@ -1528,7 +1585,7 @@ void GMainWindow::BootGame(const QString& filename) {
|
|||
ShowFullscreen();
|
||||
}
|
||||
|
||||
OnStartGame();
|
||||
OnResumeGame(true);
|
||||
}
|
||||
|
||||
void GMainWindow::ShutdownGame() {
|
||||
|
|
@ -1581,7 +1638,7 @@ void GMainWindow::ShutdownGame() {
|
|||
OnCloseMovie();
|
||||
|
||||
#ifdef USE_DISCORD_PRESENCE
|
||||
discord_rpc->Update();
|
||||
discord_rpc->Update(false);
|
||||
#endif
|
||||
#ifdef __unix__
|
||||
Common::Linux::StopGamemode();
|
||||
|
|
@ -1614,6 +1671,7 @@ void GMainWindow::ShutdownGame() {
|
|||
emu_speed_label->setVisible(false);
|
||||
game_fps_label->setVisible(false);
|
||||
emu_frametime_label->setVisible(false);
|
||||
notification_led->setColor(QColor(0, 0, 0));
|
||||
|
||||
UpdateSaveStates();
|
||||
|
||||
|
|
@ -2505,7 +2563,7 @@ void GMainWindow::OnMenuRecentFile() {
|
|||
}
|
||||
}
|
||||
|
||||
void GMainWindow::OnStartGame() {
|
||||
void GMainWindow::OnResumeGame(bool first_start) {
|
||||
qt_cameras->ResumeCameras();
|
||||
|
||||
PreventOSSleep();
|
||||
|
|
@ -2522,9 +2580,12 @@ void GMainWindow::OnStartGame() {
|
|||
play_time_manager->SetProgramId(game_title_id);
|
||||
play_time_manager->Start();
|
||||
|
||||
if (first_start) {
|
||||
#ifdef USE_DISCORD_PRESENCE
|
||||
discord_rpc->Update();
|
||||
discord_rpc->Update(true);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef __unix__
|
||||
Common::Linux::StartGamemode();
|
||||
#endif
|
||||
|
|
@ -2560,7 +2621,7 @@ void GMainWindow::OnPauseContinueGame() {
|
|||
if (emu_thread->IsRunning() && !system.frame_limiter.IsFrameAdvancing()) {
|
||||
OnPauseGame();
|
||||
} else {
|
||||
OnStartGame();
|
||||
OnResumeGame(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2599,8 +2660,14 @@ void GMainWindow::ToggleSecondaryFullscreen() {
|
|||
return;
|
||||
}
|
||||
if (secondary_window->isFullScreen()) {
|
||||
#ifdef NEEDS_ROUND_CORNERS_FIX
|
||||
WindowCornerManager::instance().blockRoundedCorners(secondary_window, false);
|
||||
#endif
|
||||
secondary_window->showNormal();
|
||||
} else {
|
||||
#ifdef NEEDS_ROUND_CORNERS_FIX
|
||||
WindowCornerManager::instance().blockRoundedCorners(secondary_window, true);
|
||||
#endif
|
||||
secondary_window->showFullScreen();
|
||||
}
|
||||
}
|
||||
|
|
@ -2610,9 +2677,15 @@ void GMainWindow::ShowFullscreen() {
|
|||
UISettings::values.geometry = saveGeometry();
|
||||
ui->menubar->hide();
|
||||
statusBar()->hide();
|
||||
#ifdef NEEDS_ROUND_CORNERS_FIX
|
||||
WindowCornerManager::instance().blockRoundedCorners(this, true);
|
||||
#endif
|
||||
showFullScreen();
|
||||
} else {
|
||||
UISettings::values.renderwindow_geometry = render_window->saveGeometry();
|
||||
#ifdef NEEDS_ROUND_CORNERS_FIX
|
||||
WindowCornerManager::instance().blockRoundedCorners(render_window, true);
|
||||
#endif
|
||||
render_window->showFullScreen();
|
||||
}
|
||||
}
|
||||
|
|
@ -2621,9 +2694,15 @@ void GMainWindow::HideFullscreen() {
|
|||
if (ui->action_Single_Window_Mode->isChecked()) {
|
||||
statusBar()->setVisible(ui->action_Show_Status_Bar->isChecked());
|
||||
ui->menubar->show();
|
||||
#ifdef NEEDS_ROUND_CORNERS_FIX
|
||||
WindowCornerManager::instance().blockRoundedCorners(this, false);
|
||||
#endif
|
||||
showNormal();
|
||||
restoreGeometry(UISettings::values.geometry);
|
||||
} else {
|
||||
#ifdef NEEDS_ROUND_CORNERS_FIX
|
||||
WindowCornerManager::instance().blockRoundedCorners(render_window, false);
|
||||
#endif
|
||||
render_window->showNormal();
|
||||
render_window->restoreGeometry(UISettings::values.renderwindow_geometry);
|
||||
}
|
||||
|
|
@ -2862,6 +2941,7 @@ void GMainWindow::OnConfigure() {
|
|||
#ifdef USE_DISCORD_PRESENCE
|
||||
if (UISettings::values.enable_discord_presence.GetValue() != old_discord_presence) {
|
||||
SetDiscordEnabled(UISettings::values.enable_discord_presence.GetValue());
|
||||
discord_rpc->Update(system.IsPoweredOn());
|
||||
}
|
||||
#endif
|
||||
#ifdef __unix__
|
||||
|
|
@ -3029,7 +3109,7 @@ void GMainWindow::OnCloseMovie() {
|
|||
}
|
||||
|
||||
if (was_running) {
|
||||
OnStartGame();
|
||||
OnResumeGame(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3051,7 +3131,7 @@ void GMainWindow::OnSaveMovie() {
|
|||
}
|
||||
|
||||
if (was_running) {
|
||||
OnStartGame();
|
||||
OnResumeGame(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3095,7 +3175,7 @@ void GMainWindow::OnCaptureScreenshot() {
|
|||
screenshot_window->CaptureScreenshot(
|
||||
UISettings::values.screenshot_resolution_factor.GetValue(),
|
||||
QString::fromStdString(path));
|
||||
OnStartGame();
|
||||
OnResumeGame(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3498,7 +3578,7 @@ void GMainWindow::OnStopVideoDumping() {
|
|||
ShutdownGame();
|
||||
} else if (game_paused_for_dumping) {
|
||||
game_paused_for_dumping = false;
|
||||
OnStartGame();
|
||||
OnResumeGame(false);
|
||||
}
|
||||
});
|
||||
future_watcher->setFuture(future);
|
||||
|
|
@ -4232,7 +4312,6 @@ void GMainWindow::SetDiscordEnabled([[maybe_unused]] bool state) {
|
|||
} else {
|
||||
discord_rpc = std::make_unique<DiscordRPC::NullImpl>();
|
||||
}
|
||||
discord_rpc->Update();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
#include <QTranslator>
|
||||
#include "citra_qt/compatibility_list.h"
|
||||
#include "citra_qt/hotkeys.h"
|
||||
#include "citra_qt/notification_led.h"
|
||||
#include "citra_qt/user_data_migration.h"
|
||||
#include "core/core.h"
|
||||
#include "core/savestate.h"
|
||||
|
|
@ -147,6 +148,7 @@ signals:
|
|||
void CIAInstallReport(Service::AM::InstallStatus status, QString filepath);
|
||||
void CompressFinished(bool is_compress, bool success);
|
||||
void CIAInstallFinished();
|
||||
void InfoLEDColorChanged();
|
||||
// Signal that tells widgets to update icons to use the current theme
|
||||
void UpdateThemedIcons();
|
||||
|
||||
|
|
@ -229,7 +231,7 @@ private:
|
|||
void ShowFFmpegErrorMessage();
|
||||
|
||||
private slots:
|
||||
void OnStartGame();
|
||||
void OnResumeGame(bool first_start);
|
||||
void OnRestartGame();
|
||||
void OnPauseGame();
|
||||
void OnPauseContinueGame();
|
||||
|
|
@ -364,6 +366,8 @@ private:
|
|||
|
||||
MultiplayerState* multiplayer_state = nullptr;
|
||||
|
||||
LedWidget* notification_led = nullptr;
|
||||
|
||||
// Created before `config` to ensure that emu data directory
|
||||
// isn't created before the check is performed
|
||||
UserDataMigrator user_data_migrator;
|
||||
|
|
|
|||
|
|
@ -670,12 +670,16 @@ void ConfigureSystem::RefreshSecureDataStatus() {
|
|||
return tr("Status: Loaded (Invalid Signature)");
|
||||
case HW::UniqueData::SecureDataLoadStatus::RegionChanged:
|
||||
return tr("Status: Loaded (Region Changed)");
|
||||
case HW::UniqueData::SecureDataLoadStatus::CannotValidateSignature:
|
||||
return tr("Status: Loaded (Cannot Validate Signature)");
|
||||
case HW::UniqueData::SecureDataLoadStatus::NotFound:
|
||||
return tr("Status: Not Found");
|
||||
case HW::UniqueData::SecureDataLoadStatus::Invalid:
|
||||
return tr("Status: Invalid");
|
||||
case HW::UniqueData::SecureDataLoadStatus::IOError:
|
||||
return tr("Status: IO Error");
|
||||
case HW::UniqueData::SecureDataLoadStatus::NoCryptoKeys:
|
||||
return tr("Status: Missing Crypto Keys");
|
||||
default:
|
||||
return QString();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2018 Citra Emulator Project
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
|
|
@ -11,7 +11,7 @@ public:
|
|||
virtual ~DiscordInterface() = default;
|
||||
|
||||
virtual void Pause() = 0;
|
||||
virtual void Update() = 0;
|
||||
virtual void Update(bool is_powered_on) = 0;
|
||||
};
|
||||
|
||||
class NullImpl : public DiscordInterface {
|
||||
|
|
@ -19,7 +19,7 @@ public:
|
|||
~NullImpl() = default;
|
||||
|
||||
void Pause() override {}
|
||||
void Update() override {}
|
||||
void Update(bool is_powered_on) override {}
|
||||
};
|
||||
|
||||
} // namespace DiscordRPC
|
||||
|
|
|
|||
|
|
@ -30,14 +30,21 @@ void DiscordImpl::Pause() {
|
|||
Discord_ClearPresence();
|
||||
}
|
||||
|
||||
void DiscordImpl::Update() {
|
||||
void DiscordImpl::Update(bool is_powered_on) {
|
||||
s64 start_time = std::chrono::duration_cast<std::chrono::seconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch())
|
||||
.count();
|
||||
auto truncate = [](const std::string& str, std::size_t maxLen = 128) -> std::string {
|
||||
if (str.length() <= maxLen) {
|
||||
return str;
|
||||
}
|
||||
return str.substr(0, maxLen - 3) + "...";
|
||||
};
|
||||
|
||||
std::string title;
|
||||
const bool is_powered_on = system.IsPoweredOn();
|
||||
if (is_powered_on) {
|
||||
system.GetAppLoader().ReadTitle(title);
|
||||
title = truncate("Playing: " + title);
|
||||
}
|
||||
|
||||
DiscordRichPresence presence{};
|
||||
|
|
@ -45,9 +52,6 @@ void DiscordImpl::Update() {
|
|||
presence.largeImageText = "An open source emulator for the Nintendo 3DS";
|
||||
if (is_powered_on) {
|
||||
presence.state = title.c_str();
|
||||
presence.details = "Currently in game";
|
||||
} else {
|
||||
presence.details = "Not in game";
|
||||
}
|
||||
presence.startTimestamp = start_time;
|
||||
Discord_UpdatePresence(&presence);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2018 Citra Emulator Project
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
|
|
@ -18,7 +18,7 @@ public:
|
|||
~DiscordImpl() override;
|
||||
|
||||
void Pause() override;
|
||||
void Update() override;
|
||||
void Update(bool is_powered_on) override;
|
||||
|
||||
private:
|
||||
const Core::System& system;
|
||||
|
|
|
|||
87
src/citra_qt/notification_led.cpp
Normal file
87
src/citra_qt/notification_led.cpp
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <QPainter>
|
||||
#include <QRadialGradient>
|
||||
#include "citra_qt/notification_led.h"
|
||||
|
||||
LedWidget::LedWidget(QWidget* parent) : QWidget(parent), color(0, 0, 0) {
|
||||
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
}
|
||||
|
||||
QSize LedWidget::sizeHint() const {
|
||||
return QSize(16, 16);
|
||||
}
|
||||
|
||||
QSize LedWidget::minimumSizeHint() const {
|
||||
return QSize(16, 16);
|
||||
}
|
||||
|
||||
void LedWidget::setColor(const QColor& _color) {
|
||||
if (color == _color)
|
||||
return;
|
||||
|
||||
color = _color;
|
||||
update();
|
||||
}
|
||||
|
||||
QColor LedWidget::lerpColor(const QColor& a, const QColor& b, float t) {
|
||||
t = std::clamp(t, 0.0f, 1.0f);
|
||||
|
||||
return QColor(int(a.red() + (b.red() - a.red()) * t),
|
||||
int(a.green() + (b.green() - a.green()) * t),
|
||||
int(a.blue() + (b.blue() - a.blue()) * t));
|
||||
}
|
||||
|
||||
QColor LedWidget::blendLedColor(int r, int g, int b) const {
|
||||
// Default "off" color
|
||||
const QColor off_color(64, 64, 64);
|
||||
|
||||
// If completely off, just show gray and skip further calculations
|
||||
if (r == 0 && g == 0 && b == 0)
|
||||
return off_color;
|
||||
|
||||
// Normalize lit color so hue stays pure
|
||||
int max_c = std::max({r, g, b});
|
||||
QColor lit_color((r * 255) / max_c, (g * 255) / max_c, (b * 255) / max_c);
|
||||
|
||||
// Convert PWM duty to perceived brightness.
|
||||
// This gives better results as LED RGB values
|
||||
// are not linear.
|
||||
constexpr float gamma = 2.4f;
|
||||
float pwm = max_c / 255.0;
|
||||
float t = std::pow(pwm, 1.f / gamma);
|
||||
|
||||
return lerpColor(off_color, lit_color, t * 0.8f);
|
||||
}
|
||||
|
||||
void LedWidget::paintEvent(QPaintEvent*) {
|
||||
QPainter p(this);
|
||||
p.setRenderHint(QPainter::Antialiasing);
|
||||
|
||||
QRectF rect = this->rect().adjusted(0, 2, 0, -2);
|
||||
|
||||
qreal size = std::min(rect.width(), rect.height());
|
||||
QRectF circle((rect.center().x() - size / 2.f) - 2, rect.center().y() - size / 2.f, size, size);
|
||||
|
||||
QPointF center = circle.center();
|
||||
qreal radius = circle.width() / 2.f;
|
||||
|
||||
QColor base = blendLedColor(color.red(), color.green(), color.blue());
|
||||
|
||||
QRadialGradient g(center, radius);
|
||||
|
||||
QColor inner = base.lighter(135);
|
||||
QColor outer = base.darker(125);
|
||||
|
||||
g.setColorAt(0.f, inner);
|
||||
g.setColorAt(0.7f, base);
|
||||
g.setColorAt(1.f, outer);
|
||||
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(g);
|
||||
p.drawEllipse(circle);
|
||||
}
|
||||
30
src/citra_qt/notification_led.h
Normal file
30
src/citra_qt/notification_led.h
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QColor>
|
||||
#include <QWidget>
|
||||
|
||||
class LedWidget : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit LedWidget(QWidget* parent = nullptr);
|
||||
|
||||
QSize sizeHint() const override;
|
||||
QSize minimumSizeHint() const override;
|
||||
|
||||
void setColor(const QColor& color);
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent* event) override;
|
||||
|
||||
private:
|
||||
QColor blendLedColor(int r, int g, int b) const;
|
||||
static QColor lerpColor(const QColor& a, const QColor& b, float t);
|
||||
|
||||
private:
|
||||
QColor color;
|
||||
};
|
||||
|
|
@ -4,6 +4,11 @@ add_executable(citra_room_standalone
|
|||
|
||||
set_target_properties(citra_room_standalone PROPERTIES OUTPUT_NAME "azahar-room")
|
||||
|
||||
if (BSD STREQUAL "NetBSD")
|
||||
include(DisablePaxMprotect)
|
||||
disable_pax_mprotect(citra_room_standalone)
|
||||
endif()
|
||||
|
||||
target_link_libraries(citra_room_standalone PRIVATE citra_room)
|
||||
|
||||
if(UNIX AND NOT APPLE)
|
||||
|
|
|
|||
|
|
@ -498,8 +498,11 @@ struct Values {
|
|||
Setting<bool> apply_region_free_patch{true, Keys::apply_region_free_patch};
|
||||
|
||||
// Renderer
|
||||
// clang-format off
|
||||
SwitchableSetting<GraphicsAPI, true> graphics_api{
|
||||
#if defined(ENABLE_OPENGL)
|
||||
#if defined(ANDROID) && defined(ENABLE_VULKAN) // Prefer Vulkan on Android, OpenGL on everything else
|
||||
GraphicsAPI::Vulkan,
|
||||
#elif defined(ENABLE_OPENGL)
|
||||
GraphicsAPI::OpenGL,
|
||||
#elif defined(ENABLE_VULKAN)
|
||||
GraphicsAPI::Vulkan,
|
||||
|
|
@ -510,6 +513,7 @@ struct Values {
|
|||
#error "At least one renderer must be enabled."
|
||||
#endif
|
||||
GraphicsAPI::Software, GraphicsAPI::Vulkan, Keys::graphics_api};
|
||||
// clang-format on
|
||||
SwitchableSetting<u32> physical_device{0, Keys::physical_device};
|
||||
Setting<bool> use_gles{false, Keys::use_gles};
|
||||
Setting<bool> renderer_debug{false, Keys::renderer_debug};
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2014 Citra Emulator Project / PPSSPP Project
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
|
|
@ -7,6 +7,7 @@
|
|||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <deque>
|
||||
#include <tuple>
|
||||
#include <boost/serialization/deque.hpp>
|
||||
#include <boost/serialization/split_member.hpp>
|
||||
#include "common/common_types.h"
|
||||
|
|
@ -52,33 +53,35 @@ struct ThreadQueueList {
|
|||
return T();
|
||||
}
|
||||
|
||||
T pop_first() {
|
||||
std::pair<Priority, T> pop_first() {
|
||||
Queue* cur = first;
|
||||
while (cur != nullptr) {
|
||||
if (!cur->data.empty()) {
|
||||
auto tmp = std::move(cur->data.front());
|
||||
cur->data.pop_front();
|
||||
return tmp;
|
||||
Priority prio = static_cast<Priority>(ToIndex(cur));
|
||||
return {prio, tmp};
|
||||
}
|
||||
cur = cur->next_nonempty;
|
||||
}
|
||||
|
||||
return T();
|
||||
return {Priority(), T()};
|
||||
}
|
||||
|
||||
T pop_first_better(Priority priority) {
|
||||
std::pair<Priority, T> pop_first_better(Priority priority) {
|
||||
Queue* cur = first;
|
||||
Queue* stop = &queues[priority];
|
||||
while (cur < stop) {
|
||||
if (!cur->data.empty()) {
|
||||
auto tmp = std::move(cur->data.front());
|
||||
cur->data.pop_front();
|
||||
return tmp;
|
||||
Priority prio = static_cast<Priority>(ToIndex(cur));
|
||||
return {prio, tmp};
|
||||
}
|
||||
cur = cur->next_nonempty;
|
||||
}
|
||||
|
||||
return T();
|
||||
return {Priority(), T()};
|
||||
}
|
||||
|
||||
void push_front(Priority priority, const T& thread_id) {
|
||||
|
|
|
|||
|
|
@ -350,6 +350,8 @@ add_library(citra_core STATIC
|
|||
hle/service/ldr_ro/ldr_ro.h
|
||||
hle/service/mcu/mcu_hwc.cpp
|
||||
hle/service/mcu/mcu_hwc.h
|
||||
hle/service/mcu/mcu_rtc.cpp
|
||||
hle/service/mcu/mcu_rtc.h
|
||||
hle/service/mcu/mcu.cpp
|
||||
hle/service/mcu/mcu.h
|
||||
hle/service/mic/mic_u.cpp
|
||||
|
|
@ -462,7 +464,6 @@ add_library(citra_core STATIC
|
|||
hw/aes/key.h
|
||||
hw/ecc.cpp
|
||||
hw/ecc.h
|
||||
hw/default_keys.h
|
||||
hw/rsa/rsa.cpp
|
||||
hw/rsa/rsa.h
|
||||
hw/unique_data.cpp
|
||||
|
|
@ -500,6 +501,10 @@ add_library(citra_core STATIC
|
|||
tracer/recorder.h
|
||||
)
|
||||
|
||||
if (ENABLE_BUILTIN_KEYBLOB)
|
||||
target_sources(citra_core PRIVATE hw/default_keys.h)
|
||||
endif()
|
||||
|
||||
create_target_directory_groups(citra_core)
|
||||
|
||||
target_link_libraries(citra_core PUBLIC citra_common PRIVATE audio_core network video_core)
|
||||
|
|
|
|||
|
|
@ -590,6 +590,8 @@ System::ResultStatus System::Init(Frontend::EmuWindow& emu_window,
|
|||
plg_ldr->SetAllowGameChangeState(Settings::values.allow_plugin_loader.GetValue());
|
||||
}
|
||||
|
||||
SetInfoLEDColor({});
|
||||
|
||||
LOG_DEBUG(Core, "Initialized OK");
|
||||
|
||||
is_powered_on = true;
|
||||
|
|
@ -720,6 +722,8 @@ void System::Shutdown(bool is_deserializing) {
|
|||
|
||||
memory.reset();
|
||||
|
||||
SetInfoLEDColor({});
|
||||
|
||||
LOG_DEBUG(Core, "Shutdown OK");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
#include <boost/optional.hpp>
|
||||
#include <boost/serialization/version.hpp>
|
||||
#include "common/common_types.h"
|
||||
#include "common/vector_math.h"
|
||||
#include "core/arm/arm_interface.h"
|
||||
#include "core/cheats/cheats.h"
|
||||
#include "core/hle/service/apt/applet_manager.h"
|
||||
|
|
@ -381,6 +382,27 @@ public:
|
|||
|
||||
bool IsInitialSetup();
|
||||
|
||||
// This returns the 3DS notification LED RGB value.
|
||||
// Keep in mind this is used as a PWM duty cycle on real HW,
|
||||
// so the percieved LED brightness is not linear.
|
||||
const Common::Vec3<u8>& GetInfoLEDColor() const {
|
||||
return info_led_color;
|
||||
}
|
||||
|
||||
void SetInfoLEDColor(const Common::Vec3<u8>& color) {
|
||||
if (color == info_led_color)
|
||||
return;
|
||||
|
||||
info_led_color = color;
|
||||
if (info_led_color_changed) {
|
||||
info_led_color_changed();
|
||||
}
|
||||
}
|
||||
|
||||
void RegisterInfoLEDColorChanged(const std::function<void()>& func) {
|
||||
info_led_color_changed = func;
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* Initialize the emulated system.
|
||||
|
|
@ -487,6 +509,9 @@ private:
|
|||
|
||||
std::vector<u64> lle_modules;
|
||||
|
||||
Common::Vec3<u8> info_led_color;
|
||||
std::function<void()> info_led_color_changed;
|
||||
|
||||
friend class boost::serialization::access;
|
||||
template <typename Archive>
|
||||
void serialize(Archive& ar, const unsigned int file_version);
|
||||
|
|
|
|||
|
|
@ -182,7 +182,14 @@ struct ExHeader_ARM11_SystemLocalCaps {
|
|||
BitField<4, 4, u8> system_mode;
|
||||
};
|
||||
u8 priority;
|
||||
u8 resource_limit_descriptor[0x10][2];
|
||||
union {
|
||||
u16 core1_schedule_flags;
|
||||
BitField<0, 7, u16> max_cpu;
|
||||
// Schedule mode flag, 0 -> "single", 1 -> "multi"
|
||||
BitField<7, 1, u16> schedule_mode;
|
||||
BitField<8, 8, u16> unknown;
|
||||
};
|
||||
u8 resource_limit_descriptor[0xF][2]; // Always 0 (unused?)
|
||||
ExHeader_StorageInfo storage_info;
|
||||
u8 service_access_control[0x20][8];
|
||||
u8 ex_service_access_control[0x2][8];
|
||||
|
|
|
|||
|
|
@ -202,6 +202,14 @@ void KernelSystem::RestoreMemoryState(u64 title_id) {
|
|||
}
|
||||
}
|
||||
|
||||
void KernelSystem::SetCore1ScheduleMode(Core1ScheduleMode mode) {
|
||||
GetThreadManager(1).SetScheduleMode(mode);
|
||||
}
|
||||
|
||||
void KernelSystem::UpdateCore1AppCpuLimit() {
|
||||
GetThreadManager(1).UpdateAppCpuLimit();
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
void KernelSystem::serialize(Archive& ar, const unsigned int) {
|
||||
ar & memory_regions;
|
||||
|
|
@ -246,4 +254,10 @@ void New3dsHwCapabilities::serialize(Archive& ar, const unsigned int) {
|
|||
}
|
||||
SERIALIZE_IMPL(New3dsHwCapabilities)
|
||||
|
||||
template <class Archive>
|
||||
void Core1CpuTime::serialize(Archive& ar, const unsigned int) {
|
||||
ar & raw;
|
||||
}
|
||||
SERIALIZE_IMPL(Core1CpuTime)
|
||||
|
||||
} // namespace Kernel
|
||||
|
|
|
|||
|
|
@ -136,6 +136,43 @@ private:
|
|||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
enum class Core1ScheduleMode : u32 {
|
||||
// https://github.com/LumaTeam/Luma3DS/blob/e35972ea/sysmodules/pm/source/reslimit.c#L144
|
||||
// Starting by "sysmodule" threads, alternatively allow (if preemptible) only sysmodule
|
||||
// threads, and then only application threads to run. This happens at a rate of 2ms *
|
||||
// (cpuTime/100).
|
||||
Multi,
|
||||
// This divides the core1 time into slices of 25ms. Only one application thread is allowed to be
|
||||
// created on core1. The "application" thread is given cpuTime% of the slice. The "sysmodules"
|
||||
// threads are given a total of (90 - cpuTime)% of the slice. 10% remain for other threads. This
|
||||
// mode is half-broken due to a kernel bug.
|
||||
Single,
|
||||
};
|
||||
|
||||
class Core1CpuTime {
|
||||
public:
|
||||
Core1CpuTime() = default;
|
||||
constexpr Core1CpuTime(s32 value) : raw(value) {}
|
||||
|
||||
static const Core1CpuTime PREEMPTION_DISABLED;
|
||||
static const Core1CpuTime PREEMPTION_SYSMODULE;
|
||||
static const Core1CpuTime PREEMPTION_EXCEMPTED;
|
||||
|
||||
operator s32() const {
|
||||
return raw;
|
||||
}
|
||||
|
||||
private:
|
||||
s32 raw{};
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int);
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
inline constexpr Core1CpuTime Core1CpuTime::PREEMPTION_DISABLED{0};
|
||||
inline constexpr Core1CpuTime Core1CpuTime::PREEMPTION_SYSMODULE{1000};
|
||||
inline constexpr Core1CpuTime Core1CpuTime::PREEMPTION_EXCEMPTED{10000};
|
||||
|
||||
class KernelSystem {
|
||||
public:
|
||||
explicit KernelSystem(Memory::MemorySystem& memory, Core::Timing& timing,
|
||||
|
|
@ -378,6 +415,10 @@ public:
|
|||
|
||||
void RestoreMemoryState(u64 title_id);
|
||||
|
||||
void SetCore1ScheduleMode(Core1ScheduleMode mode);
|
||||
|
||||
void UpdateCore1AppCpuLimit();
|
||||
|
||||
private:
|
||||
void MemoryInit(MemoryMode memory_mode, u64 override_init_time);
|
||||
|
||||
|
|
@ -447,3 +488,4 @@ private:
|
|||
} // namespace Kernel
|
||||
|
||||
BOOST_CLASS_EXPORT_KEY(Kernel::New3dsHwCapabilities)
|
||||
BOOST_CLASS_EXPORT_KEY(Kernel::Core1CpuTime)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2015 Citra Emulator Project
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
|
|
@ -9,19 +9,24 @@
|
|||
#include "common/archives.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/resource_limit.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
|
||||
SERIALIZE_EXPORT_IMPL(Kernel::ResourceLimit)
|
||||
SERIALIZE_EXPORT_IMPL(Kernel::ResourceLimitList)
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
ResourceLimit::ResourceLimit(KernelSystem& kernel) : Object(kernel) {}
|
||||
ResourceLimit::ResourceLimit(KernelSystem& _kernel) : Object(_kernel), kernel(_kernel) {}
|
||||
|
||||
ResourceLimit::~ResourceLimit() = default;
|
||||
|
||||
std::shared_ptr<ResourceLimit> ResourceLimit::Create(KernelSystem& kernel, std::string name) {
|
||||
std::shared_ptr<ResourceLimit> ResourceLimit::Create(KernelSystem& kernel,
|
||||
ResourceLimitCategory category,
|
||||
std::string name) {
|
||||
auto resource_limit = std::make_shared<ResourceLimit>(kernel);
|
||||
resource_limit->m_category = category;
|
||||
resource_limit->m_name = std::move(name);
|
||||
return resource_limit;
|
||||
}
|
||||
|
|
@ -36,6 +41,11 @@ s32 ResourceLimit::GetLimitValue(ResourceLimitType type) const {
|
|||
return m_limit_values[index];
|
||||
}
|
||||
|
||||
void ResourceLimit::SetCurrentValue(ResourceLimitType type, s32 value) {
|
||||
const auto index = static_cast<std::size_t>(type);
|
||||
m_current_values[index] = value;
|
||||
}
|
||||
|
||||
void ResourceLimit::SetLimitValue(ResourceLimitType type, s32 value) {
|
||||
const auto index = static_cast<std::size_t>(type);
|
||||
m_limit_values[index] = value;
|
||||
|
|
@ -66,9 +76,89 @@ bool ResourceLimit::Release(ResourceLimitType type, s32 amount) {
|
|||
return true;
|
||||
}
|
||||
|
||||
void ResourceLimit::ApplyAppMaxCPUSetting(std::shared_ptr<Kernel::Process>& process, u8 exh_mode,
|
||||
u8 exh_cpu_limit) {
|
||||
|
||||
// List of 1.0 titles with max CPU time overrides. This is not a "hack" list,
|
||||
// these values actually exist in the 3DS kernel.
|
||||
static constexpr std::array<std::pair<u32, Core1CpuTime>, 16> cpu_time_overrides = {{
|
||||
// 3DS sound (JPN, USA, EUR)
|
||||
{0x205, Core1CpuTime::PREEMPTION_EXCEMPTED},
|
||||
{0x215, Core1CpuTime::PREEMPTION_EXCEMPTED},
|
||||
{0x225, Core1CpuTime::PREEMPTION_EXCEMPTED},
|
||||
|
||||
// Star Fox 64 3D (JPN)
|
||||
{0x304, Core1CpuTime::PREEMPTION_EXCEMPTED},
|
||||
|
||||
// Super Monkey Ball 3D (JPN)
|
||||
{0x32E, Core1CpuTime::PREEMPTION_EXCEMPTED},
|
||||
|
||||
// The Legend of Zelda: Ocarina of Time 3D (JPN, USA, EUR)
|
||||
{0x334, 30},
|
||||
{0x335, 30},
|
||||
{0x336, 30},
|
||||
|
||||
// Doctor Lautrec to Boukyaku no Kishidan (JPN, USA, EUR)
|
||||
{0x348, Core1CpuTime::PREEMPTION_EXCEMPTED},
|
||||
{0x368, Core1CpuTime::PREEMPTION_EXCEMPTED},
|
||||
{0x562, Core1CpuTime::PREEMPTION_EXCEMPTED},
|
||||
|
||||
// Pro Yakyuu Spirits 2011 (JPN)
|
||||
{0x349, Core1CpuTime::PREEMPTION_EXCEMPTED},
|
||||
|
||||
// Super Monkey Ball 3D (USA, EUR)
|
||||
{0x370, Core1CpuTime::PREEMPTION_EXCEMPTED},
|
||||
{0x389, Core1CpuTime::PREEMPTION_EXCEMPTED},
|
||||
|
||||
// Star Fox 64 3D (USA, EUR)
|
||||
{0x490, Core1CpuTime::PREEMPTION_EXCEMPTED},
|
||||
{0x491, Core1CpuTime::PREEMPTION_EXCEMPTED},
|
||||
|
||||
}};
|
||||
|
||||
Core1ScheduleMode final_mode{};
|
||||
Core1CpuTime limit_cpu_time{};
|
||||
Core1CpuTime set_cpu_time = Core1CpuTime::PREEMPTION_DISABLED;
|
||||
|
||||
if (exh_mode == 0 && exh_cpu_limit == 0) {
|
||||
// This is a 1.0 app with the CPU time value not set. Use default or hardcoded value.
|
||||
|
||||
final_mode = Core1ScheduleMode::Single;
|
||||
limit_cpu_time = 80;
|
||||
set_cpu_time = 25;
|
||||
|
||||
u32 uID = static_cast<u32>(((process->codeset->program_id >> 8) & 0xFFFFF));
|
||||
|
||||
auto it = std::find_if(cpu_time_overrides.begin(), cpu_time_overrides.end(),
|
||||
[uID](auto& v) { return v.first == uID; });
|
||||
|
||||
if (it != cpu_time_overrides.end()) {
|
||||
const Core1CpuTime& time = it->second;
|
||||
if (static_cast<u32>(time) > 100 && static_cast<u32>(time) < 200) {
|
||||
// This code path is actually unused
|
||||
final_mode = Core1ScheduleMode::Multi;
|
||||
set_cpu_time = limit_cpu_time = static_cast<u32>(time) - 100;
|
||||
} else {
|
||||
final_mode = Core1ScheduleMode::Single;
|
||||
set_cpu_time = limit_cpu_time = time;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
final_mode = (exh_mode == 0) ? Core1ScheduleMode::Single : Core1ScheduleMode::Multi;
|
||||
limit_cpu_time = exh_cpu_limit;
|
||||
}
|
||||
|
||||
// Normally done by PM through svcSetKernelState and svcSetResourceLimitValues.
|
||||
SetLimitValue(ResourceLimitType::CpuTime, limit_cpu_time);
|
||||
SetCurrentValue(ResourceLimitType::CpuTime, set_cpu_time);
|
||||
kernel.SetCore1ScheduleMode(final_mode);
|
||||
kernel.UpdateCore1AppCpuLimit();
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
void ResourceLimit::serialize(Archive& ar, const unsigned int) {
|
||||
ar& boost::serialization::base_object<Object>(*this);
|
||||
ar & m_category;
|
||||
ar & m_name;
|
||||
ar & m_limit_values;
|
||||
ar & m_current_values;
|
||||
|
|
@ -82,7 +172,8 @@ ResourceLimitList::ResourceLimitList(KernelSystem& kernel) {
|
|||
const auto& appmemalloc = kernel.GetMemoryRegion(MemoryRegion::APPLICATION);
|
||||
|
||||
// Create the Application resource limit
|
||||
auto resource_limit = ResourceLimit::Create(kernel, "Applications");
|
||||
auto resource_limit =
|
||||
ResourceLimit::Create(kernel, ResourceLimitCategory::Application, "Applications");
|
||||
resource_limit->SetLimitValue(ResourceLimitType::Priority, 0x18);
|
||||
resource_limit->SetLimitValue(ResourceLimitType::Commit, appmemalloc->size);
|
||||
resource_limit->SetLimitValue(ResourceLimitType::Thread, 0x20);
|
||||
|
|
@ -92,11 +183,12 @@ ResourceLimitList::ResourceLimitList(KernelSystem& kernel) {
|
|||
resource_limit->SetLimitValue(ResourceLimitType::Timer, 0x8);
|
||||
resource_limit->SetLimitValue(ResourceLimitType::SharedMemory, 0x10);
|
||||
resource_limit->SetLimitValue(ResourceLimitType::AddressArbiter, 0x2);
|
||||
resource_limit->SetLimitValue(ResourceLimitType::CpuTime, 0x0);
|
||||
resource_limits[static_cast<u8>(ResourceLimitCategory::Application)] = resource_limit;
|
||||
resource_limit->SetLimitValue(ResourceLimitType::CpuTime, Core1CpuTime::PREEMPTION_DISABLED);
|
||||
resource_limits[static_cast<u8>(resource_limit->GetCategory())] = resource_limit;
|
||||
|
||||
// Create the SysApplet resource limit
|
||||
resource_limit = ResourceLimit::Create(kernel, "System Applets");
|
||||
resource_limit =
|
||||
ResourceLimit::Create(kernel, ResourceLimitCategory::SysApplet, "System Applets");
|
||||
resource_limit->SetLimitValue(ResourceLimitType::Priority, 0x4);
|
||||
resource_limit->SetLimitValue(ResourceLimitType::Commit, is_new_3ds ? 0x5E06000 : 0x2606000);
|
||||
resource_limit->SetLimitValue(ResourceLimitType::Thread, is_new_3ds ? 0x1D : 0xE);
|
||||
|
|
@ -106,11 +198,12 @@ ResourceLimitList::ResourceLimitList(KernelSystem& kernel) {
|
|||
resource_limit->SetLimitValue(ResourceLimitType::Timer, 0x4);
|
||||
resource_limit->SetLimitValue(ResourceLimitType::SharedMemory, 0x8);
|
||||
resource_limit->SetLimitValue(ResourceLimitType::AddressArbiter, 0x3);
|
||||
resource_limit->SetLimitValue(ResourceLimitType::CpuTime, 0x2710);
|
||||
resource_limits[static_cast<u8>(ResourceLimitCategory::SysApplet)] = resource_limit;
|
||||
resource_limit->SetLimitValue(ResourceLimitType::CpuTime, Core1CpuTime::PREEMPTION_EXCEMPTED);
|
||||
resource_limits[static_cast<u8>(resource_limit->GetCategory())] = resource_limit;
|
||||
|
||||
// Create the LibApplet resource limit
|
||||
resource_limit = ResourceLimit::Create(kernel, "Library Applets");
|
||||
resource_limit =
|
||||
ResourceLimit::Create(kernel, ResourceLimitCategory::LibApplet, "Library Applets");
|
||||
resource_limit->SetLimitValue(ResourceLimitType::Priority, 0x4);
|
||||
resource_limit->SetLimitValue(ResourceLimitType::Commit, 0x602000);
|
||||
resource_limit->SetLimitValue(ResourceLimitType::Thread, 0xE);
|
||||
|
|
@ -120,11 +213,11 @@ ResourceLimitList::ResourceLimitList(KernelSystem& kernel) {
|
|||
resource_limit->SetLimitValue(ResourceLimitType::Timer, 0x4);
|
||||
resource_limit->SetLimitValue(ResourceLimitType::SharedMemory, 0x8);
|
||||
resource_limit->SetLimitValue(ResourceLimitType::AddressArbiter, 0x1);
|
||||
resource_limit->SetLimitValue(ResourceLimitType::CpuTime, 0x2710);
|
||||
resource_limits[static_cast<u8>(ResourceLimitCategory::LibApplet)] = resource_limit;
|
||||
resource_limit->SetLimitValue(ResourceLimitType::CpuTime, Core1CpuTime::PREEMPTION_EXCEMPTED);
|
||||
resource_limits[static_cast<u8>(resource_limit->GetCategory())] = resource_limit;
|
||||
|
||||
// Create the Other resource limit
|
||||
resource_limit = ResourceLimit::Create(kernel, "Others");
|
||||
resource_limit = ResourceLimit::Create(kernel, ResourceLimitCategory::Other, "Others");
|
||||
resource_limit->SetLimitValue(ResourceLimitType::Priority, 0x4);
|
||||
resource_limit->SetLimitValue(ResourceLimitType::Commit, is_new_3ds ? 0x2182000 : 0x1682000);
|
||||
resource_limit->SetLimitValue(ResourceLimitType::Thread, is_new_3ds ? 0xE1 : 0xCA);
|
||||
|
|
@ -134,8 +227,8 @@ ResourceLimitList::ResourceLimitList(KernelSystem& kernel) {
|
|||
resource_limit->SetLimitValue(ResourceLimitType::Timer, is_new_3ds ? 0x2C : 0x2B);
|
||||
resource_limit->SetLimitValue(ResourceLimitType::SharedMemory, is_new_3ds ? 0x1F : 0x1E);
|
||||
resource_limit->SetLimitValue(ResourceLimitType::AddressArbiter, is_new_3ds ? 0x2D : 0x2B);
|
||||
resource_limit->SetLimitValue(ResourceLimitType::CpuTime, 0x3E8);
|
||||
resource_limits[static_cast<u8>(ResourceLimitCategory::Other)] = resource_limit;
|
||||
resource_limit->SetLimitValue(ResourceLimitType::CpuTime, Core1CpuTime::PREEMPTION_SYSMODULE);
|
||||
resource_limits[static_cast<u8>(resource_limit->GetCategory())] = resource_limit;
|
||||
}
|
||||
|
||||
ResourceLimitList::~ResourceLimitList() = default;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2015 Citra Emulator Project
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
|
|
@ -42,6 +42,7 @@ public:
|
|||
* Creates a resource limit object.
|
||||
*/
|
||||
static std::shared_ptr<ResourceLimit> Create(KernelSystem& kernel,
|
||||
ResourceLimitCategory category,
|
||||
std::string name = "Unknown");
|
||||
|
||||
std::string GetTypeName() const override {
|
||||
|
|
@ -56,15 +57,25 @@ public:
|
|||
return HANDLE_TYPE;
|
||||
}
|
||||
|
||||
ResourceLimitCategory GetCategory() const {
|
||||
return m_category;
|
||||
}
|
||||
|
||||
s32 GetCurrentValue(ResourceLimitType type) const;
|
||||
s32 GetLimitValue(ResourceLimitType type) const;
|
||||
|
||||
void SetCurrentValue(ResourceLimitType name, s32 value);
|
||||
void SetLimitValue(ResourceLimitType name, s32 value);
|
||||
|
||||
bool Reserve(ResourceLimitType type, s32 amount);
|
||||
bool Release(ResourceLimitType type, s32 amount);
|
||||
|
||||
void ApplyAppMaxCPUSetting(std::shared_ptr<Kernel::Process>& process, u8 exh_mode,
|
||||
u8 exh_cpu_limit);
|
||||
|
||||
private:
|
||||
Kernel::KernelSystem& kernel;
|
||||
ResourceLimitCategory m_category;
|
||||
using ResourceArray = std::array<s32, static_cast<std::size_t>(ResourceLimitType::Max)>;
|
||||
ResourceArray m_limit_values{};
|
||||
ResourceArray m_current_values{};
|
||||
|
|
|
|||
|
|
@ -2218,67 +2218,69 @@ Result SVC::ControlProcess(Handle process_handle, u32 process_OP, u32 varg2, u32
|
|||
// SVCs that couldn't be profiled.
|
||||
const std::array<SVC::FunctionDef, 180> SVC::SVC_Table{{
|
||||
{0x00, nullptr, "Unknown", 0},
|
||||
{0x01, &SVC::Wrap<&SVC::ControlMemory>, "ControlMemory", 1000},
|
||||
{0x02, &SVC::Wrap<&SVC::QueryMemory>, "QueryMemory", 1000},
|
||||
{0x01, &SVC::Wrap<&SVC::ControlMemory, 0x01>, "ControlMemory", 1000},
|
||||
{0x02, &SVC::Wrap<&SVC::QueryMemory, 0x02>, "QueryMemory", 1000},
|
||||
{0x03, &SVC::ExitProcess, "ExitProcess", 1000},
|
||||
{0x04, nullptr, "GetProcessAffinityMask", 1000},
|
||||
{0x05, nullptr, "SetProcessAffinityMask", 1000},
|
||||
{0x06, nullptr, "GetProcessIdealProcessor", 1000},
|
||||
{0x07, nullptr, "SetProcessIdealProcessor", 1000},
|
||||
{0x08, &SVC::Wrap<&SVC::CreateThread>, "CreateThread", 5214},
|
||||
{0x08, &SVC::Wrap<&SVC::CreateThread, 0x08>, "CreateThread", 5214},
|
||||
{0x09, &SVC::ExitThread, "ExitThread", 1000},
|
||||
{0x0A, &SVC::Wrap<&SVC::SleepThread>, "SleepThread", 946},
|
||||
{0x0B, &SVC::Wrap<&SVC::GetThreadPriority>, "GetThreadPriority", 616},
|
||||
{0x0C, &SVC::Wrap<&SVC::SetThreadPriority>, "SetThreadPriority", 1812},
|
||||
{0x0A, &SVC::Wrap<&SVC::SleepThread, 0x0A>, "SleepThread", 946},
|
||||
{0x0B, &SVC::Wrap<&SVC::GetThreadPriority, 0x0B>, "GetThreadPriority", 616},
|
||||
{0x0C, &SVC::Wrap<&SVC::SetThreadPriority, 0x0C>, "SetThreadPriority", 1812},
|
||||
{0x0D, nullptr, "GetThreadAffinityMask", 1000},
|
||||
{0x0E, nullptr, "SetThreadAffinityMask", 1000},
|
||||
{0x0F, nullptr, "GetThreadIdealProcessor", 1000},
|
||||
{0x10, nullptr, "SetThreadIdealProcessor", 1000},
|
||||
{0x11, nullptr, "GetCurrentProcessorNumber", 1000},
|
||||
{0x12, nullptr, "Run", 1000},
|
||||
{0x13, &SVC::Wrap<&SVC::CreateMutex>, "CreateMutex", 1000},
|
||||
{0x14, &SVC::Wrap<&SVC::ReleaseMutex>, "ReleaseMutex", 1324},
|
||||
{0x15, &SVC::Wrap<&SVC::CreateSemaphore>, "CreateSemaphore", 1000},
|
||||
{0x16, &SVC::Wrap<&SVC::ReleaseSemaphore>, "ReleaseSemaphore", 2713},
|
||||
{0x17, &SVC::Wrap<&SVC::CreateEvent>, "CreateEvent", 4329},
|
||||
{0x18, &SVC::Wrap<&SVC::SignalEvent>, "SignalEvent", 3285},
|
||||
{0x19, &SVC::Wrap<&SVC::ClearEvent>, "ClearEvent", 1389},
|
||||
{0x1A, &SVC::Wrap<&SVC::CreateTimer>, "CreateTimer", 1000},
|
||||
{0x1B, &SVC::Wrap<&SVC::SetTimer>, "SetTimer", 5163},
|
||||
{0x1C, &SVC::Wrap<&SVC::CancelTimer>, "CancelTimer", 1000},
|
||||
{0x1D, &SVC::Wrap<&SVC::ClearTimer>, "ClearTimer", 1000},
|
||||
{0x1E, &SVC::Wrap<&SVC::CreateMemoryBlock>, "CreateMemoryBlock", 1000},
|
||||
{0x1F, &SVC::Wrap<&SVC::MapMemoryBlock>, "MapMemoryBlock", 1000},
|
||||
{0x20, &SVC::Wrap<&SVC::UnmapMemoryBlock>, "UnmapMemoryBlock", 1000},
|
||||
{0x21, &SVC::Wrap<&SVC::CreateAddressArbiter>, "CreateAddressArbiter", 1000},
|
||||
{0x22, &SVC::Wrap<&SVC::ArbitrateAddress>, "ArbitrateAddress", 5664},
|
||||
{0x23, &SVC::Wrap<&SVC::CloseHandle>, "CloseHandle", 2937},
|
||||
{0x24, &SVC::Wrap<&SVC::WaitSynchronization1>, "WaitSynchronization1", 4005},
|
||||
{0x25, &SVC::Wrap<&SVC::WaitSynchronizationN>, "WaitSynchronizationN", 6918},
|
||||
{0x13, &SVC::Wrap<&SVC::CreateMutex, 0x13>, "CreateMutex", 1000},
|
||||
{0x14, &SVC::Wrap<&SVC::ReleaseMutex, 0x14>, "ReleaseMutex", 1324},
|
||||
{0x15, &SVC::Wrap<&SVC::CreateSemaphore, 0x15>, "CreateSemaphore", 1000},
|
||||
{0x16, &SVC::Wrap<&SVC::ReleaseSemaphore, 0x16>, "ReleaseSemaphore", 2713},
|
||||
{0x17, &SVC::Wrap<&SVC::CreateEvent, 0x17>, "CreateEvent", 4329},
|
||||
{0x18, &SVC::Wrap<&SVC::SignalEvent, 0x18>, "SignalEvent", 3285},
|
||||
{0x19, &SVC::Wrap<&SVC::ClearEvent, 0x19>, "ClearEvent", 1389},
|
||||
{0x1A, &SVC::Wrap<&SVC::CreateTimer, 0x1A>, "CreateTimer", 1000},
|
||||
{0x1B, &SVC::Wrap<&SVC::SetTimer, 0x1B>, "SetTimer", 5163},
|
||||
{0x1C, &SVC::Wrap<&SVC::CancelTimer, 0x1C>, "CancelTimer", 1000},
|
||||
{0x1D, &SVC::Wrap<&SVC::ClearTimer, 0x1D>, "ClearTimer", 1000},
|
||||
{0x1E, &SVC::Wrap<&SVC::CreateMemoryBlock, 0x1E>, "CreateMemoryBlock", 1000},
|
||||
{0x1F, &SVC::Wrap<&SVC::MapMemoryBlock, 0x1F>, "MapMemoryBlock", 1000},
|
||||
{0x20, &SVC::Wrap<&SVC::UnmapMemoryBlock, 0x20>, "UnmapMemoryBlock", 1000},
|
||||
{0x21, &SVC::Wrap<&SVC::CreateAddressArbiter, 0x21>, "CreateAddressArbiter", 1000},
|
||||
{0x22, &SVC::Wrap<&SVC::ArbitrateAddress, 0x22>, "ArbitrateAddress", 5664},
|
||||
{0x23, &SVC::Wrap<&SVC::CloseHandle, 0x23>, "CloseHandle", 2937},
|
||||
{0x24, &SVC::Wrap<&SVC::WaitSynchronization1, 0x24>, "WaitSynchronization1", 4005},
|
||||
{0x25, &SVC::Wrap<&SVC::WaitSynchronizationN, 0x25>, "WaitSynchronizationN", 6918},
|
||||
{0x26, nullptr, "SignalAndWait", 1000},
|
||||
{0x27, &SVC::Wrap<&SVC::DuplicateHandle>, "DuplicateHandle", 1000},
|
||||
{0x28, &SVC::Wrap<&SVC::GetSystemTick>, "GetSystemTick", 340},
|
||||
{0x29, &SVC::Wrap<&SVC::GetHandleInfo>, "GetHandleInfo", 1000},
|
||||
{0x2A, &SVC::Wrap<&SVC::GetSystemInfo>, "GetSystemInfo", 1000},
|
||||
{0x2B, &SVC::Wrap<&SVC::GetProcessInfo>, "GetProcessInfo", 1510},
|
||||
{0x2C, &SVC::Wrap<&SVC::GetThreadInfo>, "GetThreadInfo", 1000},
|
||||
{0x2D, &SVC::Wrap<&SVC::ConnectToPort>, "ConnectToPort", 1000},
|
||||
{0x27, &SVC::Wrap<&SVC::DuplicateHandle, 0x27>, "DuplicateHandle", 1000},
|
||||
{0x28, &SVC::Wrap<&SVC::GetSystemTick, 0x28>, "GetSystemTick", 340},
|
||||
{0x29, &SVC::Wrap<&SVC::GetHandleInfo, 0x29>, "GetHandleInfo", 1000},
|
||||
{0x2A, &SVC::Wrap<&SVC::GetSystemInfo, 0x2A>, "GetSystemInfo", 1000},
|
||||
{0x2B, &SVC::Wrap<&SVC::GetProcessInfo, 0x2B>, "GetProcessInfo", 1510},
|
||||
{0x2C, &SVC::Wrap<&SVC::GetThreadInfo, 0x2C>, "GetThreadInfo", 1000},
|
||||
{0x2D, &SVC::Wrap<&SVC::ConnectToPort, 0x2D>, "ConnectToPort", 1000},
|
||||
{0x2E, nullptr, "SendSyncRequest1", 1000},
|
||||
{0x2F, nullptr, "SendSyncRequest2", 1000},
|
||||
{0x30, nullptr, "SendSyncRequest3", 1000},
|
||||
{0x31, nullptr, "SendSyncRequest4", 1000},
|
||||
{0x32, &SVC::Wrap<&SVC::SendSyncRequest>, "SendSyncRequest", 5825},
|
||||
{0x33, &SVC::Wrap<&SVC::OpenProcess>, "OpenProcess", 1000},
|
||||
{0x34, &SVC::Wrap<&SVC::OpenThread>, "OpenThread", 1000},
|
||||
{0x35, &SVC::Wrap<&SVC::GetProcessId>, "GetProcessId", 1000},
|
||||
{0x36, &SVC::Wrap<&SVC::GetProcessIdOfThread>, "GetProcessIdOfThread", 1000},
|
||||
{0x37, &SVC::Wrap<&SVC::GetThreadId>, "GetThreadId", 677},
|
||||
{0x38, &SVC::Wrap<&SVC::GetResourceLimit>, "GetResourceLimit", 1000},
|
||||
{0x39, &SVC::Wrap<&SVC::GetResourceLimitLimitValues>, "GetResourceLimitLimitValues", 1000},
|
||||
{0x3A, &SVC::Wrap<&SVC::GetResourceLimitCurrentValues>, "GetResourceLimitCurrentValues", 1000},
|
||||
{0x32, &SVC::Wrap<&SVC::SendSyncRequest, 0x32>, "SendSyncRequest", 5825},
|
||||
{0x33, &SVC::Wrap<&SVC::OpenProcess, 0x33>, "OpenProcess", 1000},
|
||||
{0x34, &SVC::Wrap<&SVC::OpenThread, 0x34>, "OpenThread", 1000},
|
||||
{0x35, &SVC::Wrap<&SVC::GetProcessId, 0x35>, "GetProcessId", 1000},
|
||||
{0x36, &SVC::Wrap<&SVC::GetProcessIdOfThread, 0x36>, "GetProcessIdOfThread", 1000},
|
||||
{0x37, &SVC::Wrap<&SVC::GetThreadId, 0x37>, "GetThreadId", 677},
|
||||
{0x38, &SVC::Wrap<&SVC::GetResourceLimit, 0x38>, "GetResourceLimit", 1000},
|
||||
{0x39, &SVC::Wrap<&SVC::GetResourceLimitLimitValues, 0x39>, "GetResourceLimitLimitValues",
|
||||
1000},
|
||||
{0x3A, &SVC::Wrap<&SVC::GetResourceLimitCurrentValues, 0x3A>, "GetResourceLimitCurrentValues",
|
||||
1000},
|
||||
{0x3B, nullptr, "GetThreadContext", 1000},
|
||||
{0x3C, &SVC::Wrap<&SVC::Break>, "Break", 1000},
|
||||
{0x3D, &SVC::Wrap<&SVC::OutputDebugString>, "OutputDebugString", 1000},
|
||||
{0x3C, &SVC::Wrap<&SVC::Break, 0x3C>, "Break", 1000},
|
||||
{0x3D, &SVC::Wrap<&SVC::OutputDebugString, 0x3D>, "OutputDebugString", 1000},
|
||||
{0x3E, nullptr, "ControlPerformanceCounter", 1000},
|
||||
{0x3F, nullptr, "Unknown", 1000},
|
||||
{0x40, nullptr, "Unknown", 1000},
|
||||
|
|
@ -2288,20 +2290,20 @@ const std::array<SVC::FunctionDef, 180> SVC::SVC_Table{{
|
|||
{0x44, nullptr, "Unknown", 1000},
|
||||
{0x45, nullptr, "Unknown", 1000},
|
||||
{0x46, nullptr, "Unknown", 1000},
|
||||
{0x47, &SVC::Wrap<&SVC::CreatePort>, "CreatePort", 1000},
|
||||
{0x48, &SVC::Wrap<&SVC::CreateSessionToPort>, "CreateSessionToPort", 5122},
|
||||
{0x49, &SVC::Wrap<&SVC::CreateSession>, "CreateSession", 3492},
|
||||
{0x4A, &SVC::Wrap<&SVC::AcceptSession>, "AcceptSession", 1842},
|
||||
{0x47, &SVC::Wrap<&SVC::CreatePort, 0x47>, "CreatePort", 1000},
|
||||
{0x48, &SVC::Wrap<&SVC::CreateSessionToPort, 0x48>, "CreateSessionToPort", 5122},
|
||||
{0x49, &SVC::Wrap<&SVC::CreateSession, 0x49>, "CreateSession", 3492},
|
||||
{0x4A, &SVC::Wrap<&SVC::AcceptSession, 0x4A>, "AcceptSession", 1842},
|
||||
{0x4B, nullptr, "ReplyAndReceive1", 1000},
|
||||
{0x4C, nullptr, "ReplyAndReceive2", 1000},
|
||||
{0x4D, nullptr, "ReplyAndReceive3", 1000},
|
||||
{0x4E, nullptr, "ReplyAndReceive4", 1000},
|
||||
{0x4F, &SVC::Wrap<&SVC::ReplyAndReceive>, "ReplyAndReceive", 8762},
|
||||
{0x4F, &SVC::Wrap<&SVC::ReplyAndReceive, 0x4F>, "ReplyAndReceive", 8762},
|
||||
{0x50, nullptr, "BindInterrupt", 1000},
|
||||
{0x51, nullptr, "UnbindInterrupt", 1000},
|
||||
{0x52, &SVC::Wrap<&SVC::InvalidateProcessDataCache>, "InvalidateProcessDataCache", 9609},
|
||||
{0x53, &SVC::Wrap<&SVC::StoreProcessDataCache>, "StoreProcessDataCache", 7174},
|
||||
{0x54, &SVC::Wrap<&SVC::FlushProcessDataCache>, "FlushProcessDataCache", 9084},
|
||||
{0x52, &SVC::Wrap<&SVC::InvalidateProcessDataCache, 0x52>, "InvalidateProcessDataCache", 9609},
|
||||
{0x53, &SVC::Wrap<&SVC::StoreProcessDataCache, 0x53>, "StoreProcessDataCache", 7174},
|
||||
{0x54, &SVC::Wrap<&SVC::FlushProcessDataCache, 0x54>, "FlushProcessDataCache", 9084},
|
||||
{0x55, nullptr, "StartInterProcessDma", 9146},
|
||||
{0x56, nullptr, "StopDma", 1163},
|
||||
{0x57, nullptr, "GetDmaState", 2222},
|
||||
|
|
@ -2318,7 +2320,7 @@ const std::array<SVC::FunctionDef, 180> SVC::SVC_Table{{
|
|||
{0x62, nullptr, "TerminateDebugProcess", 1000},
|
||||
{0x63, nullptr, "GetProcessDebugEvent", 1000},
|
||||
{0x64, nullptr, "ContinueDebugEvent", 1000},
|
||||
{0x65, &SVC::Wrap<&SVC::GetProcessList>, "GetProcessList", 1000},
|
||||
{0x65, &SVC::Wrap<&SVC::GetProcessList, 0x65>, "GetProcessList", 1000},
|
||||
{0x66, nullptr, "GetThreadList", 1000},
|
||||
{0x67, nullptr, "GetDebugThreadContext", 1000},
|
||||
{0x68, nullptr, "SetDebugThreadContext", 1000},
|
||||
|
|
@ -2335,14 +2337,15 @@ const std::array<SVC::FunctionDef, 180> SVC::SVC_Table{{
|
|||
{0x73, nullptr, "CreateCodeSet", 1000},
|
||||
{0x74, nullptr, "RandomStub", 1000},
|
||||
{0x75, nullptr, "CreateProcess", 1000},
|
||||
{0x76, &SVC::Wrap<&SVC::TerminateProcess>, "TerminateProcess", 1000},
|
||||
{0x76, &SVC::Wrap<&SVC::TerminateProcess, 0x76>, "TerminateProcess", 1000},
|
||||
{0x77, nullptr, "SetProcessResourceLimits", 1000},
|
||||
{0x78, nullptr, "CreateResourceLimit", 1000},
|
||||
{0x79, &SVC::Wrap<&SVC::SetResourceLimitLimitValues>, "SetResourceLimitLimitValues", 1000},
|
||||
{0x79, &SVC::Wrap<&SVC::SetResourceLimitLimitValues, 0x79>, "SetResourceLimitLimitValues",
|
||||
1000},
|
||||
{0x7A, nullptr, "AddCodeSegment", 1000},
|
||||
{0x7B, nullptr, "Backdoor", 1000},
|
||||
{0x7C, &SVC::Wrap<&SVC::KernelSetState>, "KernelSetState", 1000},
|
||||
{0x7D, &SVC::Wrap<&SVC::QueryProcessMemory>, "QueryProcessMemory", 1000},
|
||||
{0x7C, &SVC::Wrap<&SVC::KernelSetState, 0x7C>, "KernelSetState", 1000},
|
||||
{0x7D, &SVC::Wrap<&SVC::QueryProcessMemory, 0x7D>, "QueryProcessMemory", 1000},
|
||||
// Custom SVCs
|
||||
{0x7E, nullptr, "Unused", 1000},
|
||||
{0x7F, nullptr, "Unused", 1000},
|
||||
|
|
@ -2362,13 +2365,13 @@ const std::array<SVC::FunctionDef, 180> SVC::SVC_Table{{
|
|||
{0x8D, nullptr, "Unused", 1000},
|
||||
{0x8E, nullptr, "Unused", 1000},
|
||||
{0x8F, nullptr, "Unused", 1000},
|
||||
{0x90, &SVC::Wrap<&SVC::ConvertVaToPa>, "ConvertVaToPa", 1000},
|
||||
{0x90, &SVC::Wrap<&SVC::ConvertVaToPa, 0x90>, "ConvertVaToPa", 1000},
|
||||
{0x91, nullptr, "FlushDataCacheRange", 1000},
|
||||
{0x92, nullptr, "FlushEntireDataCache", 1000},
|
||||
{0x93, &SVC::Wrap<&SVC::InvalidateInstructionCacheRange>, "InvalidateInstructionCacheRange",
|
||||
1000},
|
||||
{0x94, &SVC::Wrap<&SVC::InvalidateEntireInstructionCache>, "InvalidateEntireInstructionCache",
|
||||
1000},
|
||||
{0x93, &SVC::Wrap<&SVC::InvalidateInstructionCacheRange, 0x93>,
|
||||
"InvalidateInstructionCacheRange", 1000},
|
||||
{0x94, &SVC::Wrap<&SVC::InvalidateEntireInstructionCache, 0x94>,
|
||||
"InvalidateEntireInstructionCache", 1000},
|
||||
{0x95, nullptr, "Unused", 1000},
|
||||
{0x96, nullptr, "Unused", 1000},
|
||||
{0x97, nullptr, "Unused", 1000},
|
||||
|
|
@ -2380,8 +2383,8 @@ const std::array<SVC::FunctionDef, 180> SVC::SVC_Table{{
|
|||
{0x9D, nullptr, "Unused", 1000},
|
||||
{0x9E, nullptr, "Unused", 1000},
|
||||
{0x9F, nullptr, "Unused", 1000},
|
||||
{0xA0, &SVC::Wrap<&SVC::MapProcessMemoryEx>, "MapProcessMemoryEx", 1000},
|
||||
{0xA1, &SVC::Wrap<&SVC::UnmapProcessMemoryEx>, "UnmapProcessMemoryEx", 1000},
|
||||
{0xA0, &SVC::Wrap<&SVC::MapProcessMemoryEx, 0xA0>, "MapProcessMemoryEx", 1000},
|
||||
{0xA1, &SVC::Wrap<&SVC::UnmapProcessMemoryEx, 0xA1>, "UnmapProcessMemoryEx", 1000},
|
||||
{0xA2, nullptr, "ControlMemoryEx", 1000},
|
||||
{0xA3, nullptr, "ControlMemoryUnsafe", 1000},
|
||||
{0xA4, nullptr, "Unused", 1000},
|
||||
|
|
@ -2399,7 +2402,7 @@ const std::array<SVC::FunctionDef, 180> SVC::SVC_Table{{
|
|||
{0xB0, nullptr, "ControlService", 1000},
|
||||
{0xB1, nullptr, "CopyHandle", 1000},
|
||||
{0xB2, nullptr, "TranslateHandle", 1000},
|
||||
{0xB3, &SVC::Wrap<&SVC::ControlProcess>, "ControlProcess", 1000},
|
||||
{0xB3, &SVC::Wrap<&SVC::ControlProcess, 0xB3>, "ControlProcess", 1000},
|
||||
}};
|
||||
|
||||
const SVC::FunctionDef* SVC::GetSVCInfo(u32 func_num) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2014 Citra Emulator Project
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
|
|
@ -21,9 +21,9 @@ namespace Kernel {
|
|||
template <typename Context>
|
||||
class SVCWrapper {
|
||||
protected:
|
||||
template <auto F>
|
||||
template <auto F, int ID = 0xFF>
|
||||
void Wrap() {
|
||||
WrapHelper<decltype(F)>::Call(*static_cast<Context*>(this), F);
|
||||
WrapHelper<decltype(F)>::Call(*static_cast<Context*>(this), F, ID);
|
||||
};
|
||||
|
||||
private:
|
||||
|
|
@ -249,18 +249,18 @@ private:
|
|||
// T is the current param to do I/O.
|
||||
// Ts are params whose I/O is not handled yet.
|
||||
template <typename... Us>
|
||||
static void Call(Context& context, SVCT svc, Us... u) {
|
||||
static void Call(Context& context, SVCT svc, int id, Us... u) {
|
||||
static_assert(std::is_same_v<SVCT, R (Context::*)(Us..., T, Ts...)>);
|
||||
constexpr std::size_t current_param_index = sizeof...(Us);
|
||||
if constexpr (std::is_pointer_v<T>) {
|
||||
using OutputT = std::remove_pointer_t<T>;
|
||||
OutputT output;
|
||||
WrapPass<SVCT, R, Ts...>::Call(context, svc, u..., &output);
|
||||
WrapPass<SVCT, R, Ts...>::Call(context, svc, id, u..., &output);
|
||||
SetParam<current_param_index, OutputT, R, Us..., T, Ts...>(context, output);
|
||||
} else {
|
||||
T input;
|
||||
GetParam<current_param_index, T, R, Us..., T, Ts...>(context, input);
|
||||
WrapPass<SVCT, R, Ts...>::Call(context, svc, u..., input);
|
||||
WrapPass<SVCT, R, Ts...>::Call(context, svc, id, u..., input);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -269,7 +269,7 @@ private:
|
|||
struct WrapPass<SVCT, R /*empty for T, Ts...*/> {
|
||||
// Call function R(Context::svc)(Us...) and transfer the return value to registers
|
||||
template <typename... Us>
|
||||
static void Call(Context& context, SVCT svc, Us... u) {
|
||||
static void Call(Context& context, SVCT svc, [[maybe_unused]] int id, Us... u) {
|
||||
static_assert(std::is_same_v<SVCT, R (Context::*)(Us...)>);
|
||||
if constexpr (std::is_void_v<R>) {
|
||||
(context.*svc)(u...);
|
||||
|
|
@ -284,16 +284,18 @@ private:
|
|||
struct WrapPass<SVCT, Result /*empty for T, Ts...*/> {
|
||||
// Call function R(Context::svc)(Us...) and transfer the return value to registers
|
||||
template <typename... Us>
|
||||
static void Call(Context& context, SVCT svc, Us... u) {
|
||||
static void Call(Context& context, SVCT svc, int id, Us... u) {
|
||||
static_assert(std::is_same_v<SVCT, Result (Context::*)(Us...)>);
|
||||
if constexpr (std::is_void_v<Result>) {
|
||||
(context.*svc)(u...);
|
||||
} else {
|
||||
Result r = (context.*svc)(u...);
|
||||
if (r.IsError()) {
|
||||
LOG_ERROR(Kernel_SVC, "level={} summary={} module={} description={}",
|
||||
r.level.ExtractValue(r.raw), r.summary.ExtractValue(r.raw),
|
||||
r.module.ExtractValue(r.raw), r.description.ExtractValue(r.raw));
|
||||
LOG_ERROR(
|
||||
Kernel_SVC,
|
||||
"svc=0x{:02X} raw=0x{:08X} level={} summary={} module={} description={}",
|
||||
id, r.raw, r.level.ExtractValue(r.raw), r.summary.ExtractValue(r.raw),
|
||||
r.module.ExtractValue(r.raw), r.description.ExtractValue(r.raw));
|
||||
}
|
||||
SetParam<INDEX_RETURN, Result, Result, Us...>(context, r);
|
||||
}
|
||||
|
|
@ -305,8 +307,8 @@ private:
|
|||
|
||||
template <typename R, typename... T>
|
||||
struct WrapHelper<R (Context::*)(T...)> {
|
||||
static void Call(Context& context, R (Context::*svc)(T...)) {
|
||||
WrapPass<decltype(svc), R, T...>::Call(context, svc /*Empty for Us*/);
|
||||
static void Call(Context& context, R (Context::*svc)(T...), int id) {
|
||||
WrapPass<decltype(svc), R, T...>::Call(context, svc, id /*Empty for Us*/);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -37,6 +37,9 @@ void ThreadManager::serialize(Archive& ar, const unsigned int) {
|
|||
ar & ready_queue;
|
||||
ar & wakeup_callback_table;
|
||||
ar & thread_list;
|
||||
ar & current_schedule_mode;
|
||||
ar & single_time_limiter;
|
||||
ar & multi_time_limiter;
|
||||
}
|
||||
SERIALIZE_IMPL(ThreadManager)
|
||||
|
||||
|
|
@ -56,6 +59,7 @@ void Thread::serialize(Archive& ar, const unsigned int file_version) {
|
|||
ar & held_mutexes;
|
||||
ar & pending_mutexes;
|
||||
ar & owner_process;
|
||||
ar & resource_limit_category;
|
||||
ar & wait_objects;
|
||||
ar & wait_address;
|
||||
ar & name;
|
||||
|
|
@ -179,33 +183,49 @@ void ThreadManager::SwitchContext(Thread* new_thread) {
|
|||
}
|
||||
|
||||
Thread* ThreadManager::PopNextReadyThread() {
|
||||
Thread* next = nullptr;
|
||||
Thread* next;
|
||||
Thread* thread = GetCurrentThread();
|
||||
|
||||
if (thread && thread->status == ThreadStatus::Running) {
|
||||
do {
|
||||
// We have to do better than the current thread.
|
||||
// This call returns null when that's not possible.
|
||||
next = ready_queue.pop_first_better(thread->current_priority);
|
||||
if (!next) {
|
||||
// Otherwise just keep going with the current thread
|
||||
next = thread;
|
||||
break;
|
||||
} else if (!next->can_schedule)
|
||||
unscheduled_ready_queue.push_back(next);
|
||||
} while (!next->can_schedule);
|
||||
} else {
|
||||
do {
|
||||
next = ready_queue.pop_first();
|
||||
if (next && !next->can_schedule)
|
||||
unscheduled_ready_queue.push_back(next);
|
||||
} while (next && !next->can_schedule);
|
||||
}
|
||||
while (true) {
|
||||
std::vector<std::pair<u32, Thread*>> skipped;
|
||||
u32 next_priority{};
|
||||
next = nullptr;
|
||||
|
||||
while (!unscheduled_ready_queue.empty()) {
|
||||
auto t = std::move(unscheduled_ready_queue.back());
|
||||
ready_queue.push_back(t->current_priority, t);
|
||||
unscheduled_ready_queue.pop_back();
|
||||
if (thread && thread->status == ThreadStatus::Running) {
|
||||
do {
|
||||
// We have to do better than the current thread.
|
||||
// This call returns null when that's not possible.
|
||||
std::tie(next_priority, next) =
|
||||
ready_queue.pop_first_better(thread->current_priority);
|
||||
if (!next) {
|
||||
// Otherwise just keep going with the current thread
|
||||
next = thread;
|
||||
break;
|
||||
} else if (!next->can_schedule) {
|
||||
skipped.push_back({next_priority, next});
|
||||
}
|
||||
|
||||
} while (!next->can_schedule);
|
||||
} else {
|
||||
do {
|
||||
std::tie(next_priority, next) = ready_queue.pop_first();
|
||||
if (next && !next->can_schedule) {
|
||||
skipped.push_back({next_priority, next});
|
||||
}
|
||||
} while (next && !next->can_schedule);
|
||||
}
|
||||
|
||||
for (auto it = skipped.rbegin(); it != skipped.rend(); it++) {
|
||||
ready_queue.push_front(it->first, it->second);
|
||||
}
|
||||
|
||||
// Try to time limit the selected thread on core 1
|
||||
if (core_id == 1 && next && GetCpuLimiter()->DoTimeLimit(next)) {
|
||||
// If the thread is time limited, select the next one
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return next;
|
||||
|
|
@ -392,6 +412,7 @@ ResultVal<std::shared_ptr<Thread>> KernelSystem::CreateThread(
|
|||
thread->name = std::move(name);
|
||||
thread_managers[processor_id]->wakeup_callback_table[thread->thread_id] = thread.get();
|
||||
thread->owner_process = owner_process;
|
||||
thread->resource_limit_category = owner_process->resource_limit->GetCategory();
|
||||
CASCADE_RESULT(thread->tls_address, owner_process->AllocateThreadLocalStorage());
|
||||
|
||||
// TODO(peachum): move to ScheduleThread() when scheduler is added so selected core is used
|
||||
|
|
@ -516,10 +537,158 @@ VAddr Thread::GetCommandBufferAddress() const {
|
|||
return GetTLSAddress() + command_header_offset;
|
||||
}
|
||||
|
||||
ThreadManager::ThreadManager(Kernel::KernelSystem& kernel, u32 core_id) : kernel(kernel) {
|
||||
CpuLimiter::~CpuLimiter() {}
|
||||
|
||||
CpuLimiterMulti::CpuLimiterMulti(Kernel::KernelSystem& _kernel) : kernel(_kernel) {}
|
||||
|
||||
void CpuLimiterMulti::Initialize(bool is_single) {
|
||||
// TODO(PabloMK7): The is_single variable is needed to prevent
|
||||
// registering an event twice with the same name. Once CpuLimiterSingle
|
||||
// is implemented we can remove it.
|
||||
tick_event = kernel.timing.RegisterEvent(
|
||||
fmt::format("Kernel::{}::tick_event", is_single ? "CpuLimiterSingle" : "CpuLimiterMulti"),
|
||||
[this](u64, s64 cycles_late) { this->OnTick(cycles_late); });
|
||||
}
|
||||
|
||||
void CpuLimiterMulti::Start() {
|
||||
if (ready) {
|
||||
return;
|
||||
}
|
||||
ready = true;
|
||||
active = false;
|
||||
curr_state = SchedState::APP; // So that ChangeState starts with SYS
|
||||
app_cpu_time = Core1CpuTime::PREEMPTION_DISABLED;
|
||||
}
|
||||
|
||||
void CpuLimiterMulti::End() {
|
||||
if (!ready) {
|
||||
return;
|
||||
}
|
||||
ready = false;
|
||||
active = false;
|
||||
kernel.timing.UnscheduleEvent(tick_event, 0);
|
||||
WakeupSleepingThreads();
|
||||
}
|
||||
|
||||
void CpuLimiterMulti::UpdateAppCpuLimit() {
|
||||
if (!ready) {
|
||||
return;
|
||||
}
|
||||
|
||||
app_cpu_time = static_cast<u32>(kernel.ResourceLimit()
|
||||
.GetForCategory(Kernel::ResourceLimitCategory::Application)
|
||||
->GetCurrentValue(Kernel::ResourceLimitType::CpuTime));
|
||||
if (app_cpu_time == Core1CpuTime::PREEMPTION_DISABLED) {
|
||||
// No preemption, disable event
|
||||
active = false;
|
||||
kernel.timing.UnscheduleEvent(tick_event, 0);
|
||||
WakeupSleepingThreads();
|
||||
} else {
|
||||
// Start preempting, enable event
|
||||
if (active) {
|
||||
// If we were active already, unschedule first
|
||||
// so that the event is not scheduled twice.
|
||||
// We could just not call ChangeState instead,
|
||||
// but this allows adjusting the timing of the
|
||||
// event sooner.
|
||||
kernel.timing.UnscheduleEvent(tick_event, 0);
|
||||
}
|
||||
active = true;
|
||||
ChangeState(0);
|
||||
}
|
||||
}
|
||||
|
||||
bool CpuLimiterMulti::DoTimeLimit(Thread* thread) {
|
||||
if (!ready || !active) {
|
||||
// Preemption is not active, don't do anything.
|
||||
return false;
|
||||
}
|
||||
if (kernel.ResourceLimit()
|
||||
.GetForCategory(thread->resource_limit_category)
|
||||
->GetLimitValue(ResourceLimitType::CpuTime) == Core1CpuTime::PREEMPTION_EXCEMPTED) {
|
||||
// The thread is excempted from preemption
|
||||
return false;
|
||||
}
|
||||
|
||||
// On real hardware, the kernel uses a KPreemptionTimer to determine if a
|
||||
// thread needs to be time limited. This properly uses the resource limit
|
||||
// value to check if it is a sysmodule or not. We can do this instead and
|
||||
// it should be good enough. TODO(PabloMK7): fix?
|
||||
if (thread->resource_limit_category == ResourceLimitCategory::Application &&
|
||||
curr_state == SchedState::SYS ||
|
||||
thread->resource_limit_category == ResourceLimitCategory::Other &&
|
||||
curr_state == SchedState::APP) {
|
||||
// Block thread as not in the current mode
|
||||
thread->status = ThreadStatus::WaitSleep;
|
||||
sleeping_thread_ids.push(thread->thread_id);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void CpuLimiterMulti::OnTick(s64 cycles_late) {
|
||||
WakeupSleepingThreads();
|
||||
ChangeState(cycles_late);
|
||||
}
|
||||
|
||||
void CpuLimiterMulti::ChangeState(s64 cycles_late) {
|
||||
curr_state = (curr_state == SchedState::SYS) ? SchedState::APP : SchedState::SYS;
|
||||
|
||||
s64 next_timer = base_tick_interval * (app_cpu_time / 100.f);
|
||||
if (curr_state == SchedState::SYS) {
|
||||
next_timer = base_tick_interval - next_timer;
|
||||
}
|
||||
if (next_timer > cycles_late) {
|
||||
next_timer -= cycles_late;
|
||||
}
|
||||
kernel.timing.ScheduleEvent(next_timer, tick_event, 0, 1);
|
||||
}
|
||||
|
||||
void CpuLimiterMulti::WakeupSleepingThreads() {
|
||||
while (!sleeping_thread_ids.empty()) {
|
||||
u32 curr_id = sleeping_thread_ids.front();
|
||||
|
||||
auto thread = kernel.GetThreadManager(1).GetThreadByID(curr_id);
|
||||
if (thread && thread->status == ThreadStatus::WaitSleep) {
|
||||
thread->ResumeFromWait();
|
||||
}
|
||||
|
||||
sleeping_thread_ids.pop();
|
||||
}
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
void CpuLimiterMulti::serialize(Archive& ar, const unsigned int) {
|
||||
ar & ready;
|
||||
ar & active;
|
||||
ar & app_cpu_time;
|
||||
ar & curr_state;
|
||||
std::vector<u32> v;
|
||||
if (Archive::is_loading::value) {
|
||||
ar & v;
|
||||
for (auto it : v) {
|
||||
sleeping_thread_ids.push(it);
|
||||
}
|
||||
} else {
|
||||
std::queue<u32> temp = sleeping_thread_ids;
|
||||
while (!temp.empty()) {
|
||||
v.push_back(temp.front());
|
||||
temp.pop();
|
||||
}
|
||||
ar & v;
|
||||
}
|
||||
}
|
||||
|
||||
ThreadManager::ThreadManager(Kernel::KernelSystem& kernel, u32 core_id)
|
||||
: kernel(kernel), core_id(core_id), current_schedule_mode(Core1ScheduleMode::Multi),
|
||||
single_time_limiter(kernel), multi_time_limiter(kernel) {
|
||||
ThreadWakeupEventType = kernel.timing.RegisterEvent(
|
||||
"ThreadWakeupCallback_" + std::to_string(core_id),
|
||||
[this](u64 thread_id, s64 cycle_late) { ThreadWakeupCallback(thread_id, cycle_late); });
|
||||
if (core_id == 1) {
|
||||
single_time_limiter.Initialize(true);
|
||||
multi_time_limiter.Initialize(false);
|
||||
}
|
||||
}
|
||||
|
||||
ThreadManager::~ThreadManager() {
|
||||
|
|
@ -532,13 +701,33 @@ std::span<const std::shared_ptr<Thread>> ThreadManager::GetThreadList() const {
|
|||
return thread_list;
|
||||
}
|
||||
|
||||
std::shared_ptr<Thread> ThreadManager::GetThreadByID(u32 thread_id) const {
|
||||
for (auto& thread : thread_list) {
|
||||
if (thread->thread_id == thread_id) {
|
||||
return thread;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ThreadManager::SetScheduleMode(Core1ScheduleMode mode) {
|
||||
GetCpuLimiter()->End();
|
||||
current_schedule_mode = mode;
|
||||
if (mode == Core1ScheduleMode::Single) {
|
||||
LOG_WARNING(Kernel, "Unimplemented \"Single\" schedule mode.");
|
||||
}
|
||||
GetCpuLimiter()->Start();
|
||||
}
|
||||
|
||||
void ThreadManager::UpdateAppCpuLimit() {
|
||||
GetCpuLimiter()->UpdateAppCpuLimit();
|
||||
}
|
||||
|
||||
std::shared_ptr<Thread> KernelSystem::GetThreadByID(u32 thread_id) const {
|
||||
for (u32 core_id = 0; core_id < Core::System::GetInstance().GetNumCores(); core_id++) {
|
||||
const auto thread_list = GetThreadManager(core_id).GetThreadList();
|
||||
for (auto& thread : thread_list) {
|
||||
if (thread->thread_id == thread_id) {
|
||||
return thread;
|
||||
}
|
||||
auto ret = GetThreadManager(core_id).GetThreadByID(thread_id);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
|
|
|
|||
|
|
@ -15,11 +15,13 @@
|
|||
#include <vector>
|
||||
#include <boost/container/flat_set.hpp>
|
||||
#include <boost/serialization/export.hpp>
|
||||
#include <queue>
|
||||
#include "common/common_types.h"
|
||||
#include "common/thread_queue_list.h"
|
||||
#include "core/arm/arm_interface.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/kernel/resource_limit.h"
|
||||
#include "core/hle/kernel/wait_object.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
|
|
@ -49,7 +51,7 @@ enum class ThreadStatus {
|
|||
Running, ///< Currently running
|
||||
Ready, ///< Ready to run
|
||||
WaitArb, ///< Waiting on an address arbiter
|
||||
WaitSleep, ///< Waiting due to a SleepThread SVC
|
||||
WaitSleep, ///< Waiting due to a SleepThread SVC or time limited
|
||||
WaitIPC, ///< Waiting for the reply from an IPC request
|
||||
WaitSynchAny, ///< Waiting due to WaitSynch1 or WaitSynchN with wait_all = false
|
||||
WaitSynchAll, ///< Waiting due to WaitSynchronizationN with wait_all = true
|
||||
|
|
@ -81,6 +83,64 @@ private:
|
|||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
class CpuLimiter {
|
||||
public:
|
||||
CpuLimiter() = default;
|
||||
virtual ~CpuLimiter() = 0;
|
||||
|
||||
virtual void Start() = 0;
|
||||
virtual void End() = 0;
|
||||
|
||||
virtual void UpdateAppCpuLimit() = 0;
|
||||
|
||||
virtual bool DoTimeLimit(Thread* thread) = 0;
|
||||
};
|
||||
|
||||
class CpuLimiterMulti : public CpuLimiter {
|
||||
public:
|
||||
CpuLimiterMulti(Kernel::KernelSystem& kernel);
|
||||
~CpuLimiterMulti() override = default;
|
||||
|
||||
void Initialize(bool is_single);
|
||||
|
||||
void Start() override;
|
||||
void End() override;
|
||||
|
||||
void UpdateAppCpuLimit() override;
|
||||
|
||||
bool DoTimeLimit(Thread* thread) override;
|
||||
|
||||
private:
|
||||
enum class SchedState : u32 {
|
||||
APP,
|
||||
SYS,
|
||||
};
|
||||
|
||||
void OnTick(s64 cycles_late);
|
||||
|
||||
void ChangeState(s64 cycles_late);
|
||||
|
||||
void WakeupSleepingThreads();
|
||||
|
||||
static constexpr u64 base_tick_interval = nsToCycles(2'000'000); // 2ms
|
||||
|
||||
Kernel::KernelSystem& kernel;
|
||||
Core::TimingEventType* tick_event{};
|
||||
|
||||
bool ready = false;
|
||||
bool active = false;
|
||||
Core1CpuTime app_cpu_time = Core1CpuTime::PREEMPTION_DISABLED;
|
||||
SchedState curr_state{};
|
||||
std::queue<u32> sleeping_thread_ids;
|
||||
|
||||
friend class boost::serialization::access;
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int);
|
||||
};
|
||||
|
||||
// TODO(PabloMK7): Replace with proper implementation
|
||||
using CpuLimiterSingle = CpuLimiterMulti;
|
||||
|
||||
class ThreadManager {
|
||||
public:
|
||||
explicit ThreadManager(Kernel::KernelSystem& kernel, u32 core_id);
|
||||
|
|
@ -126,10 +186,21 @@ public:
|
|||
*/
|
||||
std::span<const std::shared_ptr<Thread>> GetThreadList() const;
|
||||
|
||||
std::shared_ptr<Thread> GetThreadByID(u32 thread_id) const;
|
||||
|
||||
void SetCPU(Core::ARM_Interface& cpu_) {
|
||||
cpu = &cpu_;
|
||||
}
|
||||
|
||||
void SetScheduleMode(Core1ScheduleMode mode);
|
||||
|
||||
void UpdateAppCpuLimit();
|
||||
|
||||
CpuLimiter* GetCpuLimiter() {
|
||||
return (current_schedule_mode == Core1ScheduleMode::Single) ? &single_time_limiter
|
||||
: &multi_time_limiter;
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* Switches the CPU's active thread context to that of the specified thread
|
||||
|
|
@ -151,11 +222,11 @@ private:
|
|||
void ThreadWakeupCallback(u64 thread_id, s64 cycles_late);
|
||||
|
||||
Kernel::KernelSystem& kernel;
|
||||
Core::ARM_Interface* cpu;
|
||||
u32 core_id;
|
||||
Core::ARM_Interface* cpu{};
|
||||
|
||||
std::shared_ptr<Thread> current_thread;
|
||||
Common::ThreadQueueList<Thread*, ThreadPrioLowest + 1> ready_queue;
|
||||
std::deque<Thread*> unscheduled_ready_queue;
|
||||
std::unordered_map<u64, Thread*> wakeup_callback_table;
|
||||
|
||||
/// Event type for the thread wake up event
|
||||
|
|
@ -164,6 +235,10 @@ private:
|
|||
// Lists all threadsthat aren't deleted.
|
||||
std::vector<std::shared_ptr<Thread>> thread_list;
|
||||
|
||||
Core1ScheduleMode current_schedule_mode{};
|
||||
CpuLimiterSingle single_time_limiter;
|
||||
CpuLimiterMulti multi_time_limiter;
|
||||
|
||||
friend class Thread;
|
||||
friend class KernelSystem;
|
||||
|
||||
|
|
@ -316,6 +391,8 @@ public:
|
|||
|
||||
std::weak_ptr<Process> owner_process{}; ///< Process that owns this thread
|
||||
|
||||
ResourceLimitCategory resource_limit_category{};
|
||||
|
||||
/// Objects that the thread is waiting on, in the same order as they were
|
||||
/// passed to WaitSynchronization1/N.
|
||||
std::vector<std::shared_ptr<WaitObject>> wait_objects{};
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
#include "core/hle/service/cfg/cfg.h"
|
||||
#include "core/hle/service/fs/archive.h"
|
||||
#include "core/hle/service/fs/fs_user.h"
|
||||
#include "core/hle/service/pm/pm_app.h"
|
||||
#include "core/hle/service/ptm/ptm.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hw/aes/ccm.h"
|
||||
|
|
@ -47,7 +48,6 @@ void Module::serialize(Archive& ar, const unsigned int file_version) {
|
|||
ar & shared_font_mem;
|
||||
ar & shared_font_loaded;
|
||||
ar & shared_font_relocated;
|
||||
ar & cpu_percent;
|
||||
ar & screen_capture_post_permission;
|
||||
ar & applet_manager;
|
||||
ar & wireless_reboot_info;
|
||||
|
|
@ -731,29 +731,39 @@ void Module::APTInterface::SetAppCpuTimeLimit(Kernel::HLERequestContext& ctx) {
|
|||
const auto must_be_one = rp.Pop<u32>();
|
||||
const auto value = rp.Pop<u32>();
|
||||
|
||||
LOG_WARNING(Service_APT, "(STUBBED) called, must_be_one={}, value={}", must_be_one, value);
|
||||
if (must_be_one != 1) {
|
||||
LOG_ERROR(Service_APT, "This value should be one, but is actually {}!", must_be_one);
|
||||
}
|
||||
|
||||
apt->cpu_percent = value;
|
||||
|
||||
auto pm_app = Service::PM::GetServiceAPP(apt->system);
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(ResultSuccess); // No error
|
||||
if (pm_app) {
|
||||
rb.Push(pm_app->UpdateResourceLimit(Kernel::ResourceLimitType::CpuTime, value));
|
||||
} else {
|
||||
LOG_ERROR(Service_APT, "Failed to get PM:APP module");
|
||||
rb.Push(ResultUnknown);
|
||||
}
|
||||
}
|
||||
|
||||
void Module::APTInterface::GetAppCpuTimeLimit(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
const auto must_be_one = rp.Pop<u32>();
|
||||
|
||||
LOG_WARNING(Service_APT, "(STUBBED) called, must_be_one={}", must_be_one);
|
||||
if (must_be_one != 1) {
|
||||
LOG_ERROR(Service_APT, "This value should be one, but is actually {}!", must_be_one);
|
||||
}
|
||||
|
||||
auto pm_app = Service::PM::GetServiceAPP(apt->system);
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
|
||||
rb.Push(ResultSuccess); // No error
|
||||
rb.Push(apt->cpu_percent);
|
||||
if (pm_app) {
|
||||
auto res = pm_app->GetResourceLimit(Kernel::ResourceLimitType::CpuTime);
|
||||
rb.Push(res.Code());
|
||||
rb.Push(res.ValueOr(u32{}));
|
||||
} else {
|
||||
LOG_ERROR(Service_APT, "Failed to get PM:APP module");
|
||||
rb.Push(ResultUnknown);
|
||||
rb.Push(u32{});
|
||||
}
|
||||
}
|
||||
|
||||
void Module::APTInterface::PrepareToStartLibraryApplet(Kernel::HLERequestContext& ctx) {
|
||||
|
|
|
|||
|
|
@ -1089,8 +1089,6 @@ private:
|
|||
bool shared_font_loaded = false;
|
||||
bool shared_font_relocated = false;
|
||||
|
||||
u32 cpu_percent = 0; ///< CPU time available to the running application
|
||||
|
||||
ScreencapPostPermission screen_capture_post_permission =
|
||||
ScreencapPostPermission::CleanThePermission; // TODO(JamePeng): verify the initial value
|
||||
|
||||
|
|
|
|||
|
|
@ -1,16 +1,18 @@
|
|||
// Copyright 2024 Citra Emulator Project
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/mcu/mcu.h"
|
||||
#include "core/hle/service/mcu/mcu_hwc.h"
|
||||
#include "core/hle/service/mcu/mcu_rtc.h"
|
||||
|
||||
namespace Service::MCU {
|
||||
|
||||
void InstallInterfaces(Core::System& system) {
|
||||
auto& service_manager = system.ServiceManager();
|
||||
std::make_shared<HWC>()->InstallAsService(service_manager);
|
||||
std::make_shared<HWC>(system)->InstallAsService(service_manager);
|
||||
std::make_shared<RTC>(system)->InstallAsService(service_manager);
|
||||
}
|
||||
|
||||
} // namespace Service::MCU
|
||||
|
|
|
|||
|
|
@ -1,15 +1,18 @@
|
|||
// Copyright 2024 Citra Emulator Project
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/archives.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/service/mcu/mcu_hwc.h"
|
||||
#include "core/hle/service/mcu/mcu_rtc.h"
|
||||
|
||||
SERVICE_CONSTRUCT_IMPL(Service::MCU::HWC)
|
||||
SERIALIZE_EXPORT_IMPL(Service::MCU::HWC)
|
||||
|
||||
namespace Service::MCU {
|
||||
|
||||
HWC::HWC() : ServiceFramework("mcu::HWC", 1) {
|
||||
HWC::HWC(Core::System& _system) : ServiceFramework("mcu::HWC", 1), system(_system) {
|
||||
static const FunctionInfo functions[] = {
|
||||
// clang-format off
|
||||
{0x0001, nullptr, "ReadRegister"},
|
||||
|
|
@ -21,7 +24,7 @@ HWC::HWC() : ServiceFramework("mcu::HWC", 1) {
|
|||
{0x0007, nullptr, "SetWifiLEDState"},
|
||||
{0x0008, nullptr, "SetCameraLEDPattern"},
|
||||
{0x0009, nullptr, "Set3DLEDState"},
|
||||
{0x000A, nullptr, "SetInfoLEDPattern"},
|
||||
{0x000A, &HWC::SetInfoLEDPattern, "SetInfoLEDPattern"},
|
||||
{0x000B, nullptr, "GetSoundVolume"},
|
||||
{0x000C, nullptr, "SetTopScreenFlicker"},
|
||||
{0x000D, nullptr, "SetBottomScreenFlicker"},
|
||||
|
|
@ -33,4 +36,19 @@ HWC::HWC() : ServiceFramework("mcu::HWC", 1) {
|
|||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
void HWC::SetInfoLEDPattern(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
auto pat = rp.PopRaw<MCU::InfoLedPattern>();
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
|
||||
auto mcu_rtc = MCU::RTC::GetService(system);
|
||||
if (mcu_rtc) {
|
||||
mcu_rtc->UpdateInfoLEDPattern(pat);
|
||||
rb.Push(ResultSuccess);
|
||||
} else {
|
||||
rb.Push(ResultUnknown);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Service::MCU
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2024 Citra Emulator Project
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
|
|
@ -10,12 +10,17 @@ namespace Service::MCU {
|
|||
|
||||
class HWC final : public ServiceFramework<HWC> {
|
||||
public:
|
||||
explicit HWC();
|
||||
explicit HWC(Core::System& _system);
|
||||
|
||||
private:
|
||||
Core::System& system;
|
||||
|
||||
void SetInfoLEDPattern(Kernel::HLERequestContext& ctx);
|
||||
|
||||
SERVICE_SERIALIZATION_SIMPLE
|
||||
};
|
||||
|
||||
} // namespace Service::MCU
|
||||
|
||||
SERVICE_CONSTRUCT(Service::MCU::HWC)
|
||||
BOOST_CLASS_EXPORT_KEY(Service::MCU::HWC)
|
||||
|
|
|
|||
293
src/core/hle/service/mcu/mcu_rtc.cpp
Normal file
293
src/core/hle/service/mcu/mcu_rtc.cpp
Normal file
|
|
@ -0,0 +1,293 @@
|
|||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include "common/archives.h"
|
||||
#include "common/vector_math.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/service/mcu/mcu.h"
|
||||
#include "core/hle/service/mcu/mcu_rtc.h"
|
||||
|
||||
SERVICE_CONSTRUCT_IMPL(Service::MCU::RTC)
|
||||
SERIALIZE_EXPORT_IMPL(Service::MCU::RTC)
|
||||
|
||||
namespace Service::MCU {
|
||||
|
||||
class InfoLedHandler {
|
||||
public:
|
||||
InfoLedHandler() = default;
|
||||
~InfoLedHandler() = default;
|
||||
|
||||
static constexpr s64 CALLBACK_PERIOD_NS = 1'000'000'000ll / 60; // 60Hz (~16ms)
|
||||
static constexpr s64 MCU_TICK_PERIOD_NS = 1'000'000'000ll / 512; // 512Hz (~2ms)
|
||||
|
||||
void SetPattern(const InfoLedPattern& p) {
|
||||
current_pattern = p;
|
||||
pattern_changed = true;
|
||||
}
|
||||
|
||||
void SetHeader(const InfoLedPattern::Header& header) {
|
||||
current_pattern.header = header;
|
||||
pattern_changed = true;
|
||||
}
|
||||
|
||||
// The MCU led code is updated with a frequency of 512Hz on real hardware. However
|
||||
// it is not a very relevant feature for emulation, so to prevent slicing the core
|
||||
// timing too much let's update it every frame instead (60Hz) and adjust for it.
|
||||
void Tick(s64 cycles_late) {
|
||||
|
||||
const s64 late_ns = cyclesToNs(cycles_late);
|
||||
|
||||
// Accumulate elapsed time.
|
||||
arm_time_ns += CALLBACK_PERIOD_NS + late_ns;
|
||||
if (arm_time_ns < 0)
|
||||
arm_time_ns = 0;
|
||||
|
||||
// Sync the MCU state up to the current ARM time
|
||||
while (arm_time_ns >= MCU_TICK_PERIOD_NS) {
|
||||
arm_time_ns -= MCU_TICK_PERIOD_NS;
|
||||
TickMCULed();
|
||||
}
|
||||
}
|
||||
|
||||
Common::Vec3<u8> Color() const {
|
||||
return result_color;
|
||||
}
|
||||
|
||||
// To save CPU time, do not tick if all smooth state has finished
|
||||
// and the pattern is all zero.
|
||||
bool NeedsTicking() {
|
||||
auto patAllZero = [this]() -> bool {
|
||||
u32* data = reinterpret_cast<u32*>(¤t_pattern);
|
||||
for (size_t i = 0; i < sizeof(InfoLedPattern) / sizeof(u32); i++) {
|
||||
if (data[i])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
return !patAllZero() || !state_r.Finished() || !state_g.Finished() || !state_b.Finished();
|
||||
}
|
||||
|
||||
bool Status() const {
|
||||
return status_finished;
|
||||
}
|
||||
|
||||
private:
|
||||
struct LedSmoothState {
|
||||
s16 target = 0;
|
||||
s16 increment = 0;
|
||||
s16 current = 0;
|
||||
|
||||
bool Finished() {
|
||||
return current == target;
|
||||
}
|
||||
|
||||
friend class boost::serialization::access;
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar & target;
|
||||
ar & increment;
|
||||
ar & current;
|
||||
}
|
||||
};
|
||||
|
||||
// Decompilation of MCU function at address 0x2f44
|
||||
void setSmoothState(LedSmoothState& state, u8 color) {
|
||||
// Looks like the color is multiplied for better precision
|
||||
state.target = static_cast<s16>(color) * 128;
|
||||
|
||||
// Real HW makes sure ticks_to_progress is not 0 when the led pattern
|
||||
// is set through I2C. We check for it here instead as it's equivalent.
|
||||
const u8 ticks = std::max<u8>(current_pattern.header.ticks_to_progress, 1);
|
||||
state.increment = (state.target - state.current) / ticks;
|
||||
}
|
||||
|
||||
// Decompilation of MCU function at address 0x2dc0
|
||||
static u8 updateSmoothState(LedSmoothState& status) {
|
||||
if (!status.Finished()) {
|
||||
if (std::abs(status.target - status.current) > std::abs(status.increment)) {
|
||||
status.current += status.increment;
|
||||
} else {
|
||||
status.current = status.target;
|
||||
}
|
||||
}
|
||||
|
||||
return static_cast<u8>(status.current / 128);
|
||||
}
|
||||
|
||||
// Decompilation of MCU function at address 0x2f6b
|
||||
// This function is called every 1/512 seconds
|
||||
void TickMCULed() {
|
||||
|
||||
// Here, a few things happen.
|
||||
// If a global variable is set to 2 (0xff904), the led state is cleared.
|
||||
// If a global variable bit 0 is set (0xffe98), this function does not run at all.
|
||||
// If a global variable bit 7 is set (0xffe97), this function takes another path which
|
||||
// runs function 0x2f1d instead of setSmoothState() to set the LED smooth status.
|
||||
// This function seems to setup smooth to fade to off state.
|
||||
// TODO(PabloMK7): Figure out what those mean. Maybe power on/off related
|
||||
|
||||
if (pattern_changed) {
|
||||
pattern_changed = false;
|
||||
status_finished = false;
|
||||
ticks_to_next_index = 0;
|
||||
index = 0;
|
||||
} else {
|
||||
if (ticks_to_next_index == 0) {
|
||||
ticks_to_next_index = current_pattern.header.ticks_per_index;
|
||||
|
||||
if (index < InfoLedPattern::PATTERN_INDEX_COUNT - 1) {
|
||||
status_finished = false;
|
||||
index = (index + 1) % InfoLedPattern::PATTERN_INDEX_COUNT;
|
||||
last_index_repeat_times = 0;
|
||||
} else {
|
||||
status_finished = true;
|
||||
if (current_pattern.header.last_index_repeat_times != 0xFF) {
|
||||
last_index_repeat_times++;
|
||||
if (last_index_repeat_times >
|
||||
current_pattern.header.last_index_repeat_times) {
|
||||
index = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set smooth for the next index
|
||||
setSmoothState(state_r, current_pattern.r[index]);
|
||||
setSmoothState(state_g, current_pattern.g[index]);
|
||||
setSmoothState(state_b, current_pattern.b[index]);
|
||||
}
|
||||
ticks_to_next_index--;
|
||||
}
|
||||
|
||||
// Update smooth state
|
||||
result_color.r() = updateSmoothState(state_r);
|
||||
result_color.g() = updateSmoothState(state_g);
|
||||
result_color.b() = updateSmoothState(state_b);
|
||||
}
|
||||
|
||||
private:
|
||||
InfoLedPattern current_pattern{};
|
||||
|
||||
bool pattern_changed = false;
|
||||
bool status_finished = false;
|
||||
|
||||
u8 ticks_to_next_index = 0;
|
||||
u8 index = 0;
|
||||
u8 last_index_repeat_times = 0;
|
||||
|
||||
LedSmoothState state_r{};
|
||||
LedSmoothState state_g{};
|
||||
LedSmoothState state_b{};
|
||||
|
||||
Common::Vec3<u8> result_color{};
|
||||
|
||||
s64 arm_time_ns = 0;
|
||||
|
||||
friend class boost::serialization::access;
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar & current_pattern;
|
||||
ar & pattern_changed;
|
||||
ar & status_finished;
|
||||
ar & ticks_to_next_index;
|
||||
ar & index;
|
||||
ar & last_index_repeat_times;
|
||||
ar & state_r;
|
||||
ar & state_g;
|
||||
ar & state_b;
|
||||
ar & result_color;
|
||||
ar & arm_time_ns;
|
||||
}
|
||||
};
|
||||
|
||||
RTC::RTC(Core::System& _system) : ServiceFramework("mcu::RTC", 1), system(_system) {
|
||||
static const FunctionInfo functions[] = {
|
||||
// clang-format off
|
||||
{0x003B, &RTC::SetInfoLEDPattern, "SetInfoLEDPattern"},
|
||||
{0x003C, &RTC::SetInfoLEDPatternHeader, "SetInfoLEDPattern"},
|
||||
{0x003D, &RTC::GetInfoLEDStatus, "SetInfoLEDPattern"},
|
||||
// clang-format on
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
|
||||
info_led = std::make_unique<InfoLedHandler>();
|
||||
info_led_tick_event =
|
||||
system.Kernel().timing.RegisterEvent("MCUTickInfoLED", [this](u64, s64 cycles_late) {
|
||||
info_led->Tick(cycles_late);
|
||||
system.SetInfoLEDColor(info_led->Color());
|
||||
if (info_led->NeedsTicking()) {
|
||||
system.Kernel().timing.ScheduleEvent(nsToCycles(InfoLedHandler::CALLBACK_PERIOD_NS),
|
||||
info_led_tick_event, 0, 1);
|
||||
} else {
|
||||
info_led_ticking = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
RTC::~RTC() {}
|
||||
|
||||
void RTC::UpdateInfoLEDPattern(const InfoLedPattern& pat) {
|
||||
info_led->SetPattern(pat);
|
||||
if (!info_led_ticking) {
|
||||
system.Kernel().timing.ScheduleEvent(0, info_led_tick_event, 0, 1);
|
||||
info_led_ticking = true;
|
||||
}
|
||||
}
|
||||
|
||||
void RTC::UpdateInfoLEDHeader(const InfoLedPattern::Header& header) {
|
||||
info_led->SetHeader(header);
|
||||
if (!info_led_ticking) {
|
||||
system.Kernel().timing.ScheduleEvent(0, info_led_tick_event, 0, 1);
|
||||
info_led_ticking = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool RTC::GetInfoLEDStatusFinished() {
|
||||
return info_led->Status();
|
||||
}
|
||||
|
||||
std::shared_ptr<RTC> RTC::GetService(Core::System& system) {
|
||||
return system.ServiceManager().GetService<RTC>("mcu::RTC");
|
||||
}
|
||||
|
||||
void RTC::SetInfoLEDPattern(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
auto pat = rp.PopRaw<InfoLedPattern>();
|
||||
|
||||
UpdateInfoLEDPattern(pat);
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void RTC::SetInfoLEDPatternHeader(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
auto head = rp.PopRaw<InfoLedPattern::Header>();
|
||||
|
||||
UpdateInfoLEDHeader(head);
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void RTC::GetInfoLEDStatus(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(static_cast<u8>(GetInfoLEDStatusFinished()));
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
void RTC::serialize(Archive& ar, const unsigned int) {
|
||||
DEBUG_SERIALIZATION_POINT;
|
||||
ar& boost::serialization::base_object<Kernel::SessionRequestHandler>(*this);
|
||||
ar & info_led;
|
||||
ar & info_led_ticking;
|
||||
}
|
||||
|
||||
} // namespace Service::MCU
|
||||
83
src/core/hle/service/mcu/mcu_rtc.h
Normal file
83
src/core/hle/service/mcu/mcu_rtc.h
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Service::MCU {
|
||||
class InfoLedHandler;
|
||||
|
||||
struct InfoLedPattern {
|
||||
static constexpr size_t PATTERN_INDEX_COUNT = 32;
|
||||
|
||||
struct Header {
|
||||
u8 ticks_per_index{}; // Amount of ticks to stay in the current index (1 tick == 1/512 s)
|
||||
u8 ticks_to_progress{}; // Amount of ticks to go from the previous value to the current
|
||||
// index value. Normally, this only makes sense to be set to 0 to
|
||||
// disable interpolation, or equal to "ticks_per_index" for linear
|
||||
// interpolation. Any other value breaks the interpolation math.
|
||||
u8 last_index_repeat_times{}; // Amount of times to repeat the last index, as if the color
|
||||
// array had "last_index_repeat_times" more elements equal to
|
||||
// the last array value. (0xFF means repeat forever)
|
||||
u8 padding{};
|
||||
} header;
|
||||
|
||||
// RGB color elements, corresponding to the LED PWM duty cycle.
|
||||
// (0x0 -> fully off, 0xFF -> fully on)
|
||||
std::array<u8, PATTERN_INDEX_COUNT> r{};
|
||||
std::array<u8, PATTERN_INDEX_COUNT> g{};
|
||||
std::array<u8, PATTERN_INDEX_COUNT> b{};
|
||||
|
||||
friend class boost::serialization::access;
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar & header.ticks_per_index;
|
||||
ar & header.ticks_to_progress;
|
||||
ar & header.last_index_repeat_times;
|
||||
ar & header.padding;
|
||||
|
||||
ar & r;
|
||||
ar & g;
|
||||
ar & b;
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(InfoLedPattern) == 0x64);
|
||||
|
||||
class RTC final : public ServiceFramework<RTC> {
|
||||
public:
|
||||
explicit RTC(Core::System& _system);
|
||||
~RTC();
|
||||
|
||||
void UpdateInfoLEDPattern(const InfoLedPattern& pat);
|
||||
|
||||
void UpdateInfoLEDHeader(const InfoLedPattern::Header& header);
|
||||
|
||||
bool GetInfoLEDStatusFinished();
|
||||
|
||||
static std::shared_ptr<RTC> GetService(Core::System& system);
|
||||
|
||||
private:
|
||||
void SetInfoLEDPattern(Kernel::HLERequestContext& ctx);
|
||||
|
||||
void SetInfoLEDPatternHeader(Kernel::HLERequestContext& ctx);
|
||||
|
||||
void GetInfoLEDStatus(Kernel::HLERequestContext& ctx);
|
||||
|
||||
Core::System& system;
|
||||
|
||||
std::unique_ptr<InfoLedHandler> info_led;
|
||||
Core::TimingEventType* info_led_tick_event{};
|
||||
bool info_led_ticking{};
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int);
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
} // namespace Service::MCU
|
||||
|
||||
SERVICE_CONSTRUCT(Service::MCU::RTC)
|
||||
BOOST_CLASS_EXPORT_KEY(Service::MCU::RTC)
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2018 Citra Emulator Project
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
|
|
@ -11,7 +11,7 @@ namespace Service::PM {
|
|||
|
||||
void InstallInterfaces(Core::System& system) {
|
||||
auto& service_manager = system.ServiceManager();
|
||||
std::make_shared<PM_APP>()->InstallAsService(service_manager);
|
||||
std::make_shared<PM_APP>(system)->InstallAsService(service_manager);
|
||||
std::make_shared<PM_DBG>()->InstallAsService(service_manager);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,16 +1,19 @@
|
|||
// Copyright 2014 Citra Emulator Project
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/archives.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/service/pm/pm_app.h"
|
||||
|
||||
SERVICE_CONSTRUCT_IMPL(Service::PM::PM_APP)
|
||||
SERIALIZE_EXPORT_IMPL(Service::PM::PM_APP)
|
||||
|
||||
namespace Service::PM {
|
||||
|
||||
PM_APP::PM_APP() : ServiceFramework("pm:app", 3) {
|
||||
PM_APP::PM_APP(Core::System& _system) : ServiceFramework("pm:app", 3), system(_system) {
|
||||
static const FunctionInfo functions[] = {
|
||||
// clang-format off
|
||||
{0x0001, nullptr, "LaunchTitle"},
|
||||
|
|
@ -22,8 +25,8 @@ PM_APP::PM_APP() : ServiceFramework("pm:app", 3) {
|
|||
{0x0007, nullptr, "GetFIRMLaunchParams"},
|
||||
{0x0008, nullptr, "GetTitleExheaderFlags"},
|
||||
{0x0009, nullptr, "SetFIRMLaunchParams"},
|
||||
{0x000A, nullptr, "SetAppResourceLimit"},
|
||||
{0x000B, nullptr, "GetAppResourceLimit"},
|
||||
{0x000A, &PM_APP::SetAppResourceLimit, "SetAppResourceLimit"},
|
||||
{0x000B, &PM_APP::GetAppResourceLimit, "GetAppResourceLimit"},
|
||||
{0x000C, nullptr, "UnregisterProcess"},
|
||||
{0x000D, nullptr, "LaunchTitleUpdate"},
|
||||
// clang-format on
|
||||
|
|
@ -32,4 +35,65 @@ PM_APP::PM_APP() : ServiceFramework("pm:app", 3) {
|
|||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
Result PM_APP::UpdateResourceLimit(Kernel::ResourceLimitType type, u32 value) {
|
||||
auto res_limit =
|
||||
system.Kernel().ResourceLimit().GetForCategory(Kernel::ResourceLimitCategory::Application);
|
||||
|
||||
if (type != Kernel::ResourceLimitType::CpuTime) {
|
||||
return Result{ErrorDescription::NotImplemented, ErrorModule::PM,
|
||||
ErrorSummary::InvalidArgument, ErrorLevel::Permanent};
|
||||
}
|
||||
|
||||
if (value <= res_limit->GetLimitValue(Kernel::ResourceLimitType::CpuTime)) {
|
||||
res_limit->SetCurrentValue(Kernel::ResourceLimitType::CpuTime, value);
|
||||
system.Kernel().UpdateCore1AppCpuLimit();
|
||||
}
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultVal<u32> PM_APP::GetResourceLimit(Kernel::ResourceLimitType type) {
|
||||
auto res_limit =
|
||||
system.Kernel().ResourceLimit().GetForCategory(Kernel::ResourceLimitCategory::Application);
|
||||
|
||||
if (type != Kernel::ResourceLimitType::CpuTime) {
|
||||
return Result{ErrorDescription::NotImplemented, ErrorModule::PM,
|
||||
ErrorSummary::InvalidArgument, ErrorLevel::Permanent};
|
||||
}
|
||||
|
||||
return static_cast<u32>(res_limit->GetCurrentValue(Kernel::ResourceLimitType::CpuTime));
|
||||
}
|
||||
|
||||
void PM_APP::SetAppResourceLimit(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
rp.Skip(1, false);
|
||||
auto type = static_cast<Kernel::ResourceLimitType>(rp.Pop<u32>());
|
||||
u32 value = rp.Pop<s32>();
|
||||
rp.Skip(2, false);
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(UpdateResourceLimit(type, value));
|
||||
}
|
||||
|
||||
void PM_APP::GetAppResourceLimit(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
rp.Skip(1, false);
|
||||
auto type = static_cast<Kernel::ResourceLimitType>(rp.Pop<u32>());
|
||||
rp.Skip(3, false);
|
||||
|
||||
u64 res_value = 0;
|
||||
auto res = GetResourceLimit(type);
|
||||
if (res.Succeeded()) {
|
||||
res_value = static_cast<u64>(res.Unwrap());
|
||||
}
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(3, 0);
|
||||
rb.Push(res.Code());
|
||||
rb.Push(res_value);
|
||||
}
|
||||
|
||||
std::shared_ptr<PM_APP> GetServiceAPP(Core::System& system) {
|
||||
return system.ServiceManager().GetService<PM_APP>("pm:app");
|
||||
}
|
||||
|
||||
} // namespace Service::PM
|
||||
|
|
|
|||
|
|
@ -1,22 +1,36 @@
|
|||
// Copyright 2014 Citra Emulator Project
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/kernel/resource_limit.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Service::PM {
|
||||
|
||||
class PM_APP final : public ServiceFramework<PM_APP> {
|
||||
public:
|
||||
PM_APP();
|
||||
explicit PM_APP(Core::System& system);
|
||||
~PM_APP() = default;
|
||||
|
||||
Result UpdateResourceLimit(Kernel::ResourceLimitType type, u32 value);
|
||||
|
||||
ResultVal<u32> GetResourceLimit(Kernel::ResourceLimitType type);
|
||||
|
||||
private:
|
||||
Core::System& system;
|
||||
|
||||
void SetAppResourceLimit(Kernel::HLERequestContext& ctx);
|
||||
|
||||
void GetAppResourceLimit(Kernel::HLERequestContext& ctx);
|
||||
|
||||
SERVICE_SERIALIZATION_SIMPLE
|
||||
};
|
||||
|
||||
std::shared_ptr<PM_APP> GetServiceAPP(Core::System& system);
|
||||
|
||||
} // namespace Service::PM
|
||||
|
||||
SERVICE_CONSTRUCT(Service::PM::PM_APP)
|
||||
BOOST_CLASS_EXPORT_KEY(Service::PM::PM_APP)
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
#include "core/file_sys/errors.h"
|
||||
#include "core/file_sys/file_backend.h"
|
||||
#include "core/hle/kernel/shared_page.h"
|
||||
#include "core/hle/service/mcu/mcu_rtc.h"
|
||||
#include "core/hle/service/ptm/ptm.h"
|
||||
#include "core/hle/service/ptm/ptm_gets.h"
|
||||
#include "core/hle/service/ptm/ptm_play.h"
|
||||
|
|
@ -133,6 +134,51 @@ void Module::Interface::CheckNew3DS(Kernel::HLERequestContext& ctx) {
|
|||
Service::PTM::CheckNew3DS(rb);
|
||||
}
|
||||
|
||||
void Module::Interface::SetInfoLEDPattern(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
auto pat = rp.PopRaw<MCU::InfoLedPattern>();
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
|
||||
auto mcu_rtc = MCU::RTC::GetService(ptm->system);
|
||||
if (mcu_rtc) {
|
||||
mcu_rtc->UpdateInfoLEDPattern(pat);
|
||||
rb.Push(ResultSuccess);
|
||||
} else {
|
||||
rb.Push(ResultUnknown);
|
||||
}
|
||||
}
|
||||
|
||||
void Module::Interface::SetInfoLEDPatternHeader(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
auto head = rp.PopRaw<MCU::InfoLedPattern::Header>();
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
|
||||
auto mcu_rtc = MCU::RTC::GetService(ptm->system);
|
||||
if (mcu_rtc) {
|
||||
mcu_rtc->UpdateInfoLEDHeader(head);
|
||||
rb.Push(ResultSuccess);
|
||||
} else {
|
||||
rb.Push(ResultUnknown);
|
||||
}
|
||||
}
|
||||
|
||||
void Module::Interface::GetInfoLEDStatus(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
|
||||
|
||||
auto mcu_rtc = MCU::RTC::GetService(ptm->system);
|
||||
if (mcu_rtc) {
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(static_cast<u8>(mcu_rtc->GetInfoLEDStatusFinished()));
|
||||
} else {
|
||||
rb.Push(ResultUnknown);
|
||||
rb.Push(u8{});
|
||||
}
|
||||
}
|
||||
|
||||
void Module::Interface::GetSystemTime(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2015 Citra Emulator Project
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
|
|
@ -139,6 +139,12 @@ public:
|
|||
*/
|
||||
void CheckNew3DS(Kernel::HLERequestContext& ctx);
|
||||
|
||||
void SetInfoLEDPattern(Kernel::HLERequestContext& ctx);
|
||||
|
||||
void SetInfoLEDPatternHeader(Kernel::HLERequestContext& ctx);
|
||||
|
||||
void GetInfoLEDStatus(Kernel::HLERequestContext& ctx);
|
||||
|
||||
/**
|
||||
* PTM::GetSystemTime service function
|
||||
* Outputs:
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue