Compare commits

...

109 Commits

Author SHA1 Message Date
ca25d1cb9e Android #132 2023-11-15 00:57:02 +00:00
5ca8629bb2 Merge PR 11889 2023-11-15 00:57:02 +00:00
c3e377a701 Merge PR 11535 2023-11-15 00:57:02 +00:00
24548b1f5c Merge pull request #12034 from t895/fps-outline
android: Add drop shadow to FPS counter
2023-11-14 18:20:24 -05:00
be0ecae108 Merge pull request #12035 from t895/pip-startup-shutdown-fix
android: Don't enter PiP during startup or shutdown
2023-11-14 18:20:18 -05:00
fcd54c6479 android: Don't enter PiP during startup or shutdown 2023-11-14 18:11:38 -05:00
08296f151e android: Add drop shadow to FPS counter
Also let the style control the text size
2023-11-14 17:53:40 -05:00
a134e924ff Merge pull request #12032 from liamwhite/fruit-compiler
fix apple clang build again
2023-11-14 12:23:28 -05:00
31ed6bae11 Merge pull request #12030 from t895/blocking-fix
android: Use suspend function for creating dynamic shortcuts
2023-11-14 12:23:17 -05:00
9a5ef835cc Merge pull request #12028 from liamwhite/coretiming-shutdown
core_timing: lock event queue access
2023-11-14 12:23:11 -05:00
df0d3698ae Merge pull request #12025 from liamwhite/kernel-shutdown-deadlock
core: check for thread dpc before eret
2023-11-14 12:23:04 -05:00
51fc608f68 Merge pull request #12019 from liamwhite/more-shutdown-deadlocks
audio_core: ignore renderer wait when stream is paused
2023-11-14 12:22:56 -05:00
b30e19ba24 fix apple clang build again 2023-11-14 11:53:02 -05:00
ec6b67d862 core_timing: lock event queue access 2023-11-14 11:51:04 -05:00
4d0b7f8496 android: Use suspend function for creating dynamic shortcuts
If the coil loader ever got stuck when creating a dynamic shortcut icon, the app would freeze. This would happen most notably when booting nca format games. This pushes that process to a separate coroutine that can be cancelled by the main activity's lifecycle.
2023-11-14 10:57:00 -05:00
e3b510a4b4 core: check for thread dpc before eret 2023-11-13 11:28:23 -05:00
247d66a680 Merge pull request #11990 from german77/audio
yuzu: Save mute when in background setting
2023-11-13 09:13:12 -05:00
0047d8a01e Merge pull request #12014 from t895/shrink-logo
android: Shrink branding images
2023-11-13 09:12:16 -05:00
efc0187537 Merge pull request #12015 from t895/remove-auto
android: Remove "auto" region option
2023-11-13 09:12:07 -05:00
b6fe8a0b3f Merge pull request #12018 from t895/settings-cleanup
android: Settings visual cleanup
2023-11-13 09:11:55 -05:00
ecaa038b4d audio_core: ignore renderer wait when stream is paused 2023-11-12 23:10:53 -05:00
4aac971864 android: Use the same transition animation between every fragment in settings
The animation that I used for entering search was prone to weird visual bugs and could appear visually jarring. This just makes things appear more consistent.
2023-11-12 21:36:51 -05:00
6c93cdffb1 android: Use more padding on top of each settings header 2023-11-12 21:36:51 -05:00
470714e2d1 android: Remove dividers between each setting 2023-11-12 21:36:51 -05:00
6b888b0fa8 android: Add icons and descriptions to the first-level advanced settings page
Additionally adjusts padding to place the icons in-line with the back button in the top app bar and makes the text for normal settings appear in-line with the expanded top app bar title.
2023-11-12 21:36:50 -05:00
1a1393dad7 android: Remove general section from settings
Limit speed options were moved to system, cpu accuracy was moved to debug, and PiP was moved to graphics.
2023-11-12 19:08:57 -05:00
55412962c0 android: Remove "auto" region option
This doesn't exist and if you clicked it, your region would be set to Taiwan.
2023-11-12 15:45:42 -05:00
d920da2631 android: Add a landscape-specific layout to the about page
Moves the logo to the side to fit more information on screen
2023-11-12 13:56:42 -05:00
ff72bf2cb2 android: Shrink logo in about page 2023-11-12 13:56:13 -05:00
4efb9763d9 android: Shrink logo in settings tab
Adjusts padding between the cards and logo to fit appropriately
2023-11-12 13:55:20 -05:00
c600bc8652 android: Fix top app bar tint being cut off in the about fragment
Adjust margin on the toolbar, not the app bar
2023-11-12 13:54:31 -05:00
767c4b5a99 Merge pull request #11980 from german77/moment
service: irs: Implement moment image processor
2023-11-11 20:02:23 -05:00
904d03b01f Merge pull request #12005 from german77/homebrew_id
yuzu: Keep homebrew on the recently played list
2023-11-11 20:02:09 -05:00
9f1c9599a2 Merge pull request #12004 from liamwhite/fix-hbl
k_capabilities: ignore map region when KTrace is disabled
2023-11-11 20:02:01 -05:00
5f6666a7cd Merge pull request #12003 from liamwhite/read-modules
gdbstub: read module information from memory layout
2023-11-11 20:01:53 -05:00
1906e2724f Merge pull request #11992 from t895/frame-check
android: Hide loading animation on first frame
2023-11-11 20:01:32 -05:00
0c032d3f2f yuzu: Keep homebrew on the recently played list 2023-11-11 10:14:11 -06:00
a6735cba5f k_capabilities: ignore map region when KTrace is disabled 2023-11-11 10:45:43 -05:00
3b872b89d1 gdbstub: read module information from memory layout 2023-11-11 10:41:06 -05:00
40d4e9543b Merge pull request #11914 from liamwhite/newer-kpagetable
kernel: add KPageTableBase
2023-11-11 09:45:29 -05:00
e588f341ed service: irs: Implement moment image processor 2023-11-11 00:28:12 -06:00
875246f5b2 k_page_table: fix shutdown 2023-11-10 12:01:35 -05:00
b16fefa106 k_page_table: use more precise icache invalidates 2023-11-10 12:01:35 -05:00
2a255b2d61 kernel: add KPageTableBase
Co-authored-by: Kelebek1 <eeeedddccc@hotmail.co.uk>
2023-11-10 12:01:35 -05:00
9e331f9957 yuzu: Make mute audio persistent 2023-11-10 10:22:04 -06:00
9169cbf728 yuzu: Save mute when in background setting 2023-11-10 10:22:02 -06:00
2f9487cd38 Merge pull request #11981 from lucasreis1/patch
Allocate resources for test window before getting system info
2023-11-10 10:38:49 -05:00
09f993899e android: Hide loading animation on first frame 2023-11-09 22:27:40 -05:00
edce713fc9 Allocate resources for test window before getting system info 2023-11-07 22:47:02 -04:00
f75363177e Merge pull request #11977 from SamayXD/patch-1
Update CMakeLists.txt
2023-11-06 16:46:57 -05:00
4c6217f09b Update CMakeLists.txt 2023-11-07 02:20:29 +05:30
c95f35ea85 Update CMakeLists.txt
Updated Comments for better readability.
2023-11-07 02:13:15 +05:30
40357098a2 Merge pull request #11896 from liamwhite/crop
renderer_vulkan: fix cropping for presentation
2023-11-06 12:08:03 -05:00
e7f4110791 Merge pull request #11873 from liamwhite/buffer-control
nvdrv: add ioctl command serialization wrapper
2023-11-06 12:07:50 -05:00
ca1dd1862b Merge pull request #11972 from t895/fps-counter-adjustments
android: FPS counter adjustments
2023-11-06 11:09:59 -05:00
737d1cea62 Merge pull request #11974 from t895/ci-fix-again
ci: android: Declare secrets during build step
2023-11-06 11:03:19 -05:00
2f8e237ab7 ci: android: Declare secrets during build step 2023-11-05 21:09:21 -05:00
5191465b0a android: Simplify FPS counter padding 2023-11-05 18:34:27 -05:00
50c604f37f android: Color the FPS counter white 2023-11-05 18:29:00 -05:00
dfbc22c291 Merge pull request #11971 from german77/recent
service: am: Set the correct album program id
2023-11-05 18:27:24 -05:00
a5a3167eba service: am: Set the correct album program id 2023-11-05 17:26:34 -06:00
a423e0f9e0 renderer_vulkan: render on bottom of surface clip when flipped (#11894) 2023-11-05 21:47:35 +01:00
511c1f0c8b Merge pull request #11957 from liamwhite/null2
renderer_null: fix
2023-11-05 13:15:19 -05:00
8369fcd71a Merge pull request #11969 from german77/profile
service: acc: Ensure proper profile size
2023-11-05 13:15:14 -05:00
626916e9a4 Merge pull request #11961 from german77/recent
yuzu: Only store games in the recently played list
2023-11-05 13:15:07 -05:00
507f360a81 yuzu: Only store games in the recently played list 2023-11-05 09:34:16 -06:00
5323d9f6b3 service: acc: Ensure proper profile size 2023-11-05 09:28:22 -06:00
770d4b0b72 Merge pull request #11965 from german77/color
core: hid: Signal color updates
2023-11-04 23:26:24 -04:00
e5fed31009 Merge pull request #11963 from Kelebek1/eol_lf
Convert src/ to LF eol
2023-11-04 23:26:09 -04:00
f07484bc64 core: hid: Signal color updates 2023-11-04 14:13:18 -06:00
78b9956a04 Skip git blame 2023-11-04 18:26:49 +00:00
90aa937593 Convert files to LF eol 2023-11-04 18:25:40 +00:00
940618a64d Merge pull request #11952 from liamwhite/opus_stereo_count
opus: Allow 0 stereo count
2023-11-04 11:28:47 -04:00
409fa5dda2 Merge pull request #11960 from german77/silence
service: hid: Silence EnableUnintendedHomeButtonInputProtection
2023-11-04 11:14:27 -04:00
211b67668d Merge pull request #11959 from t895/firmware-reload-fix
android: Don't reload log/system after loading firmware/backup
2023-11-04 11:14:21 -04:00
f0cd02b9bd Merge pull request #11881 from liamwhite/sockets-safe-access
sockets: use safe access helpers
2023-11-04 11:14:08 -04:00
34101d8c5e Merge pull request #11885 from liamwhite/stop-nagging-me
qt: remove duplicate exit confirmation setting
2023-11-04 11:14:01 -04:00
bf8d7bc0da service: hid: Silence EnableUnintendedHomeButtonInputProtection 2023-11-03 23:22:28 -06:00
9543adf072 android: Always update FPS counter 2023-11-04 00:04:20 -04:00
036d2686af android: Don't reload log/system after loading firmware/backup 2023-11-03 22:49:31 -04:00
a80e0e7da5 Merge pull request #11954 from t895/log-hardware
android: Log more system information
2023-11-03 21:16:35 -04:00
9631dedea9 Merge pull request #11955 from t895/cntfrq-fix
arm: NativeClock: Special handling for bad system counter clock frequ…
2023-11-03 21:14:01 -04:00
75de0cadcf renderer_null: fix 2023-11-03 20:54:38 -04:00
4b321c003c arm: NativeClock: Special handling for bad system counter clock frequency reporting
On some devices, checking the system counter clock frequency will return 0. Substitute in the correct values to prevent issues.
2023-11-03 16:21:54 -04:00
0a83047368 android: Log more system information during startup
Logs device manufacturer/model, SoC manufacturer/model where available, and the total system memory
2023-11-03 15:52:01 -04:00
9bb8ac7cb6 android: Fix fetching system memory size from MemoryUtil
We weren't rounding up the value at a unit before (GB, MB, etc) we were rounding up the total bytes and that would do nothing. This fixes that, and the check for total system memory during first emulation start where we tried to check the required system memory against 1 gigabyte.
2023-11-03 15:51:17 -04:00
d6e6ab11b1 Merge pull request #11953 from t895/surface-tweak
android: Update surface parameters on emulation start
2023-11-03 14:35:57 -04:00
b3a1f793c3 android: Update surface parameters on emulation start
This adds a quick update that notifies the render surface if there was a change between surface creation and emulation starting.
2023-11-03 13:31:06 -04:00
a294beb116 Allow 0 stereo count 2023-11-03 11:45:40 -04:00
eda403388a Merge pull request #11948 from german77/hard_ring
service: hid: Ensure GetNextEntryIndex can't fail
2023-11-03 09:14:17 -04:00
3032980478 Merge pull request #11947 from german77/battery
core: hid: Fix wrong battery values
2023-11-03 09:14:10 -04:00
7f96f4db3f Merge pull request #11943 from liamwhite/silence-logspam
renderer_vulkan: minimize transform feedback support log
2023-11-03 09:14:02 -04:00
a0f9a3ab5b Merge pull request #11936 from liamwhite/romfs-nonsense
romfs: fix extraction of single-directory root
2023-11-03 09:13:46 -04:00
b36fec486e service: hid: Ensure GetNextEntryIndex can't fail 2023-11-02 20:33:19 -06:00
57cf830862 core: hid: Fix wrong battery values 2023-11-02 18:39:08 -06:00
41701052d3 renderer_vulkan: minimize transform feedback support log 2023-11-01 20:47:08 -04:00
b0c6bf497a romfs: fix extraction of single-directory root 2023-10-31 23:26:51 -04:00
6a7123826a qt: remove duplicate exit confirmation setting 2023-10-31 10:31:50 -04:00
6513a356f0 renderer_vulkan: fix FSR cropping 2023-10-28 11:43:00 -04:00
65d4a16afd renderer_vulkan: fix cropping for presentation 2023-10-28 00:05:06 -04:00
ca75c58f43 sockets: use safe access helpers 2023-10-25 14:07:22 -04:00
723df0f368 nvdrv: rework to remove memcpy 2023-10-25 13:05:56 -04:00
94b7ac50bb nvdrv: fix up remaining copy calls 2023-10-25 13:05:56 -04:00
18450ebd78 nvdrv: convert nvmap 2023-10-25 13:05:56 -04:00
efdb2e8f3d nvdrv: convert codec devices 2023-10-25 13:05:56 -04:00
7a84a1a974 nvdrv: convert nvhost_gpu 2023-10-25 13:05:56 -04:00
789d9c8af9 nvdrv: convert nvhost_ctrl 2023-10-25 13:05:56 -04:00
4df063209b nvdrv: convert nvhost_ctrl_gpu 2023-10-25 13:05:55 -04:00
6256e3ca8e nvdrv: add ioctl command serialization, convert nvhost_as_gpu 2023-10-25 13:05:55 -04:00
228 changed files with 12657 additions and 10412 deletions

5
.git-blame-ignore-revs Normal file
View File

@ -0,0 +1,5 @@
# SPDX-FileCopyrightText: 2023 yuzu Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
# CRLF -> LF
90aa937593e53a5d5e070fb623b228578b0b225f

View File

@ -40,11 +40,11 @@ jobs:
sudo apt-get install -y ccache apksigner glslang-dev glslang-tools
- name: Build
run: ./.ci/scripts/android/build.sh
- name: Copy and sign artifacts
env:
ANDROID_KEYSTORE_B64: ${{ secrets.ANDROID_KEYSTORE_B64 }}
ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
ANDROID_KEYSTORE_PASS: ${{ secrets.ANDROID_KEYSTORE_PASS }}
- name: Copy artifacts
run: ./.ci/scripts/android/upload.sh
- name: Upload
uses: actions/upload-artifact@v3

6
.gitmodules vendored
View File

@ -4,9 +4,6 @@
[submodule "enet"]
path = externals/enet
url = https://github.com/lsalzman/enet.git
[submodule "inih"]
path = externals/inih/inih
url = https://github.com/benhoyt/inih.git
[submodule "cubeb"]
path = externals/cubeb
url = https://github.com/mozilla/cubeb.git
@ -61,3 +58,6 @@
[submodule "breakpad"]
path = externals/breakpad
url = https://github.com/yuzu-emu/breakpad.git
[submodule "simpleini"]
path = externals/simpleini
url = https://github.com/brofield/simpleini.git

View File

@ -285,7 +285,6 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
find_package(Boost 1.79.0 REQUIRED context)
find_package(enet 1.3 MODULE)
find_package(fmt 9 REQUIRED)
find_package(inih 52 MODULE COMPONENTS INIReader)
find_package(LLVM 17.0.2 MODULE COMPONENTS Demangle)
find_package(lz4 REQUIRED)
find_package(nlohmann_json 3.8 REQUIRED)

View File

@ -1,27 +0,0 @@
# SPDX-FileCopyrightText: 2022 Alexandre Bouvier <contact@amb.tf>
#
# SPDX-License-Identifier: GPL-3.0-or-later
find_package(PkgConfig QUIET)
pkg_search_module(INIH QUIET IMPORTED_TARGET inih)
if (INIReader IN_LIST inih_FIND_COMPONENTS)
pkg_search_module(INIREADER QUIET IMPORTED_TARGET INIReader)
if (INIREADER_FOUND)
set(inih_INIReader_FOUND TRUE)
endif()
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(inih
REQUIRED_VARS INIH_LINK_LIBRARIES
VERSION_VAR INIH_VERSION
HANDLE_COMPONENTS
)
if (inih_FOUND AND NOT TARGET inih::inih)
add_library(inih::inih ALIAS PkgConfig::INIH)
endif()
if (inih_FOUND AND inih_INIReader_FOUND AND NOT TARGET inih::INIReader)
add_library(inih::INIReader ALIAS PkgConfig::INIREADER)
endif()

View File

@ -1,3 +1,13 @@
| Pull Request | Commit | Title | Author | Merged? |
|----|----|----|----|----|
| [11535](https://github.com/yuzu-emu/yuzu//pull/11535) | [`50bcfa5fb`](https://github.com/yuzu-emu/yuzu//pull/11535/files) | renderer_vulkan: Introduce separate cmd buffer for uploads | [GPUCode](https://github.com/GPUCode/) | Yes |
| [11889](https://github.com/yuzu-emu/yuzu//pull/11889) | [`a249b3018`](https://github.com/yuzu-emu/yuzu//pull/11889/files) | configuration: Unify config handling across frontends | [t895](https://github.com/t895/) | Yes |
End of merge log. You can find the original README.md below the break.
-----
<!--
SPDX-FileCopyrightText: 2018 yuzu Emulator Project
SPDX-License-Identifier: GPL-2.0-or-later

View File

@ -34,11 +34,6 @@ endif()
# Glad
add_subdirectory(glad)
# inih
if (NOT TARGET inih::INIReader)
add_subdirectory(inih)
endif()
# mbedtls
add_subdirectory(mbedtls)
target_include_directories(mbedtls PUBLIC ./mbedtls/include)
@ -295,3 +290,6 @@ if (YUZU_CRASH_DUMPS AND NOT TARGET libbreakpad_client)
target_link_libraries(dump_syms PRIVATE libbreakpad_client ZLIB::ZLIB)
endif()
endif()
# SimpleIni
add_subdirectory(simpleini)

View File

@ -1,13 +0,0 @@
# SPDX-FileCopyrightText: 2014 Gui Andrade <admin@archshift.com>
# SPDX-License-Identifier: GPL-2.0-or-later
add_library(inih
inih/ini.c
inih/ini.h
inih/cpp/INIReader.cpp
inih/cpp/INIReader.h
)
create_target_directory_groups(inih)
target_include_directories(inih INTERFACE inih/cpp)
add_library(inih::INIReader ALIAS inih)

1
externals/inih/inih vendored

Submodule externals/inih/inih deleted from 9cecf0643d

1
externals/simpleini vendored Submodule

Submodule externals/simpleini added at 382ddbb4b9

View File

@ -21,7 +21,7 @@ if (MSVC)
# Avoid windows.h from including some usually unused libs like winsocks.h, since this might cause some redefinition errors.
add_definitions(-DWIN32_LEAN_AND_MEAN)
# Ensure that projects build with Unicode support.
# Ensure that projects are built with Unicode support.
add_definitions(-DUNICODE -D_UNICODE)
# /W4 - Level 4 warnings
@ -54,11 +54,11 @@ if (MSVC)
/GT
# Modules
/experimental:module- # Disable module support explicitly due to conflicts with precompiled headers
/experimental:module- # Explicitly disable module support due to conflicts with precompiled headers.
# External headers diagnostics
/external:anglebrackets # Treats all headers included by #include <header>, where the header file is enclosed in angle brackets (< >), as external headers
/external:W0 # Sets the default warning level to 0 for external headers, effectively turning off warnings for external headers
/external:W0 # Sets the default warning level to 0 for external headers, effectively disabling warnings for them.
# Warnings
/W4
@ -187,6 +187,7 @@ add_subdirectory(audio_core)
add_subdirectory(video_core)
add_subdirectory(network)
add_subdirectory(input_common)
add_subdirectory(frontend_common)
add_subdirectory(shader_recompiler)
if (YUZU_ROOM)

View File

@ -219,7 +219,6 @@ dependencies {
implementation("io.coil-kt:coil:2.2.2")
implementation("androidx.core:core-splashscreen:1.0.1")
implementation("androidx.window:window:1.2.0-beta03")
implementation("org.ini4j:ini4j:0.5.4")
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
implementation("androidx.navigation:navigation-fragment-ktx:2.7.4")

View File

@ -230,8 +230,6 @@ object NativeLibrary {
*/
external fun onTouchReleased(finger_id: Int)
external fun reloadSettings()
external fun initGameIni(gameID: String?)
external fun setAppDirectory(directory: String)
@ -252,7 +250,7 @@ object NativeLibrary {
external fun reloadKeys(): Boolean
external fun initializeSystem()
external fun initializeSystem(reload: Boolean)
external fun defaultCPUCore(): Int

View File

@ -11,6 +11,7 @@ import java.io.File
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
import org.yuzu.yuzu_emu.utils.DocumentsTree
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import org.yuzu.yuzu_emu.utils.Log
fun Context.getPublicFilesDir(): File = getExternalFilesDir(null) ?: filesDir
@ -49,6 +50,7 @@ class YuzuApplication : Application() {
DirectoryInitialization.start()
GpuDriverHelper.initializeDriverParameters()
NativeLibrary.logDeviceInfo()
Log.logDeviceInfo()
createNotificationChannels()
}

View File

@ -107,7 +107,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
val preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
if (!preferences.getBoolean(Settings.PREF_MEMORY_WARNING_SHOWN, false)) {
if (MemoryUtil.isLessThan(MemoryUtil.REQUIRED_MEMORY, MemoryUtil.Gb)) {
if (MemoryUtil.isLessThan(MemoryUtil.REQUIRED_MEMORY, MemoryUtil.totalMemory)) {
Toast.makeText(
this,
getString(
@ -373,8 +373,10 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
val pictureInPictureParamsBuilder = PictureInPictureParams.Builder()
.getPictureInPictureActionsBuilder().getPictureInPictureAspectBuilder()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
val isEmulationActive = emulationViewModel.emulationStarted.value &&
!emulationViewModel.isEmulationStopping.value
pictureInPictureParamsBuilder.setAutoEnterEnabled(
BooleanSetting.PICTURE_IN_PICTURE.boolean
BooleanSetting.PICTURE_IN_PICTURE.boolean && isEmulationActive
)
}
setPictureInPictureParams(pictureInPictureParamsBuilder.build())

View File

@ -22,12 +22,16 @@ import androidx.core.graphics.drawable.toBitmap
import androidx.core.graphics.drawable.toDrawable
import androidx.documentfile.provider.DocumentFile
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.navigation.findNavController
import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.AsyncDifferConfig
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.yuzu.yuzu_emu.HomeNavigationDirections
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
@ -92,28 +96,34 @@ class GameAdapter(private val activity: AppCompatActivity) :
data = Uri.parse(holder.game.path)
}
val layerDrawable = ResourcesCompat.getDrawable(
YuzuApplication.appContext.resources,
R.drawable.shortcut,
null
) as LayerDrawable
layerDrawable.setDrawableByLayerId(
R.id.shortcut_foreground,
GameIconUtils.getGameIcon(holder.game).toDrawable(YuzuApplication.appContext.resources)
)
val inset = YuzuApplication.appContext.resources
.getDimensionPixelSize(R.dimen.icon_inset)
layerDrawable.setLayerInset(1, inset, inset, inset, inset)
val shortcut = ShortcutInfoCompat.Builder(YuzuApplication.appContext, holder.game.path)
.setShortLabel(holder.game.title)
.setIcon(
IconCompat.createWithAdaptiveBitmap(
layerDrawable.toBitmap(config = Bitmap.Config.ARGB_8888)
activity.lifecycleScope.launch {
withContext(Dispatchers.IO) {
val layerDrawable = ResourcesCompat.getDrawable(
YuzuApplication.appContext.resources,
R.drawable.shortcut,
null
) as LayerDrawable
layerDrawable.setDrawableByLayerId(
R.id.shortcut_foreground,
GameIconUtils.getGameIcon(activity, holder.game)
.toDrawable(YuzuApplication.appContext.resources)
)
)
.setIntent(openIntent)
.build()
ShortcutManagerCompat.pushDynamicShortcut(YuzuApplication.appContext, shortcut)
val inset = YuzuApplication.appContext.resources
.getDimensionPixelSize(R.dimen.icon_inset)
layerDrawable.setLayerInset(1, inset, inset, inset, inset)
val shortcut =
ShortcutInfoCompat.Builder(YuzuApplication.appContext, holder.game.path)
.setShortLabel(holder.game.title)
.setIcon(
IconCompat.createWithAdaptiveBitmap(
layerDrawable.toBitmap(config = Bitmap.Config.ARGB_8888)
)
)
.setIntent(openIntent)
.build()
ShortcutManagerCompat.pushDynamicShortcut(YuzuApplication.appContext, shortcut)
}
}
val action = HomeNavigationDirections.actionGlobalEmulationActivity(holder.game)
view.findNavController().navigate(action)

View File

@ -7,7 +7,7 @@ import android.text.TextUtils
import android.widget.Toast
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
import org.yuzu.yuzu_emu.utils.NativeConfig
object Settings {
private val context get() = YuzuApplication.appContext
@ -19,7 +19,7 @@ object Settings {
context.getString(R.string.ini_saved),
Toast.LENGTH_SHORT
).show()
SettingsFile.saveFile(SettingsFile.FILE_NAME_CONFIG)
NativeConfig.saveSettings()
} else {
// TODO: Save custom game settings
Toast.makeText(
@ -82,7 +82,6 @@ object Settings {
enum class MenuTag(val titleId: Int) {
SECTION_ROOT(R.string.advanced_settings),
SECTION_GENERAL(R.string.preferences_general),
SECTION_SYSTEM(R.string.preferences_system),
SECTION_RENDERER(R.string.preferences_graphics),
SECTION_AUDIO(R.string.preferences_audio),

View File

@ -3,10 +3,13 @@
package org.yuzu.yuzu_emu.features.settings.model.view
import androidx.annotation.DrawableRes
class RunnableSetting(
titleId: Int,
descriptionId: Int,
val isRuntimeRunnable: Boolean,
@DrawableRes val iconId: Int = 0,
val runnable: () -> Unit
) : SettingsItem(emptySetting, titleId, descriptionId) {
override val type = TYPE_RUNNABLE

View File

@ -3,11 +3,14 @@
package org.yuzu.yuzu_emu.features.settings.model.view
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import org.yuzu.yuzu_emu.features.settings.model.Settings
class SubmenuSetting(
titleId: Int,
descriptionId: Int,
@StringRes titleId: Int,
@StringRes descriptionId: Int,
@DrawableRes val iconId: Int,
val menuKey: Settings.MenuTag
) : SettingsItem(emptySetting, titleId, descriptionId) {
override val type = TYPE_SUBMENU

View File

@ -21,7 +21,6 @@ import androidx.navigation.navArgs
import com.google.android.material.color.MaterialColors
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.NativeLibrary
import java.io.IOException
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding
@ -165,11 +164,12 @@ class SettingsActivity : AppCompatActivity() {
settingsViewModel.shouldSave = false
// Delete settings file because the user may have changed values that do not exist in the UI
NativeConfig.unloadConfig()
val settingsFile = SettingsFile.getSettingsFile(SettingsFile.FILE_NAME_CONFIG)
if (!settingsFile.delete()) {
throw IOException("Failed to delete $settingsFile")
}
NativeLibrary.reloadSettings()
NativeConfig.initializeConfig()
Toast.makeText(
applicationContext,

View File

@ -20,7 +20,6 @@ import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.findNavController
import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.divider.MaterialDividerItemDecoration
import com.google.android.material.transition.MaterialSharedAxis
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
@ -68,15 +67,9 @@ class SettingsFragment : Fragment() {
)
binding.toolbarSettingsLayout.title = getString(args.menuTag.titleId)
val dividerDecoration = MaterialDividerItemDecoration(
requireContext(),
LinearLayoutManager.VERTICAL
)
dividerDecoration.isLastItemDecorated = false
binding.listSettings.apply {
adapter = settingsAdapter
layoutManager = LinearLayoutManager(requireContext())
addItemDecoration(dividerDecoration)
}
binding.toolbarSettings.setNavigationOnClickListener {
@ -94,17 +87,6 @@ class SettingsFragment : Fragment() {
}
}
}
launch {
settingsViewModel.isUsingSearch.collectLatest {
if (it) {
reenterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true)
exitTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false)
} else {
reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
}
}
}
}
if (args.menuTag == Settings.MenuTag.SECTION_ROOT) {
@ -112,8 +94,6 @@ class SettingsFragment : Fragment() {
binding.toolbarSettings.setOnMenuItemClickListener {
when (it.itemId) {
R.id.action_search -> {
reenterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true)
exitTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false)
view.findNavController()
.navigate(R.id.action_settingsFragment_to_settingsSearchFragment)
true
@ -129,11 +109,6 @@ class SettingsFragment : Fragment() {
setInsets()
}
override fun onResume() {
super.onResume()
settingsViewModel.setIsUsingSearch(false)
}
private fun setInsets() {
ViewCompat.setOnApplyWindowInsetsListener(
binding.root
@ -144,10 +119,9 @@ class SettingsFragment : Fragment() {
val leftInsets = barInsets.left + cutoutInsets.left
val rightInsets = barInsets.right + cutoutInsets.right
val sideMargin = resources.getDimensionPixelSize(R.dimen.spacing_medlarge)
val mlpSettingsList = binding.listSettings.layoutParams as MarginLayoutParams
mlpSettingsList.leftMargin = sideMargin + leftInsets
mlpSettingsList.rightMargin = sideMargin + rightInsets
mlpSettingsList.leftMargin = leftInsets
mlpSettingsList.rightMargin = rightInsets
binding.listSettings.layoutParams = mlpSettingsList
binding.listSettings.updatePadding(
bottom = barInsets.bottom

View File

@ -3,7 +3,6 @@
package org.yuzu.yuzu_emu.features.settings.ui
import android.content.Context
import android.content.SharedPreferences
import android.os.Build
import android.widget.Toast
@ -32,8 +31,6 @@ class SettingsFragmentPresenter(
private val preferences: SharedPreferences
get() = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
private val context: Context get() = YuzuApplication.appContext
// Extension for populating settings list based on paired settings
fun ArrayList<SettingsItem>.add(key: String) {
val item = SettingsItem.settingsItems[key]!!
@ -53,7 +50,6 @@ class SettingsFragmentPresenter(
val sl = ArrayList<SettingsItem>()
when (menuTag) {
Settings.MenuTag.SECTION_ROOT -> addConfigSettings(sl)
Settings.MenuTag.SECTION_GENERAL -> addGeneralSettings(sl)
Settings.MenuTag.SECTION_SYSTEM -> addSystemSettings(sl)
Settings.MenuTag.SECTION_RENDERER -> addGraphicsSettings(sl)
Settings.MenuTag.SECTION_AUDIO -> addAudioSettings(sl)
@ -75,30 +71,53 @@ class SettingsFragmentPresenter(
private fun addConfigSettings(sl: ArrayList<SettingsItem>) {
sl.apply {
add(SubmenuSetting(R.string.preferences_general, 0, Settings.MenuTag.SECTION_GENERAL))
add(SubmenuSetting(R.string.preferences_system, 0, Settings.MenuTag.SECTION_SYSTEM))
add(SubmenuSetting(R.string.preferences_graphics, 0, Settings.MenuTag.SECTION_RENDERER))
add(SubmenuSetting(R.string.preferences_audio, 0, Settings.MenuTag.SECTION_AUDIO))
add(SubmenuSetting(R.string.preferences_debug, 0, Settings.MenuTag.SECTION_DEBUG))
add(
RunnableSetting(R.string.reset_to_default, 0, false) {
settingsViewModel.setShouldShowResetSettingsDialog(true)
}
SubmenuSetting(
R.string.preferences_system,
R.string.preferences_system_description,
R.drawable.ic_system_settings,
Settings.MenuTag.SECTION_SYSTEM
)
)
add(
SubmenuSetting(
R.string.preferences_graphics,
R.string.preferences_graphics_description,
R.drawable.ic_graphics,
Settings.MenuTag.SECTION_RENDERER
)
)
add(
SubmenuSetting(
R.string.preferences_audio,
R.string.preferences_audio_description,
R.drawable.ic_audio,
Settings.MenuTag.SECTION_AUDIO
)
)
add(
SubmenuSetting(
R.string.preferences_debug,
R.string.preferences_debug_description,
R.drawable.ic_code,
Settings.MenuTag.SECTION_DEBUG
)
)
add(
RunnableSetting(
R.string.reset_to_default,
R.string.reset_to_default_description,
false,
R.drawable.ic_restore
) { settingsViewModel.setShouldShowResetSettingsDialog(true) }
)
}
}
private fun addGeneralSettings(sl: ArrayList<SettingsItem>) {
sl.apply {
add(BooleanSetting.RENDERER_USE_SPEED_LIMIT.key)
add(ShortSetting.RENDERER_SPEED_LIMIT.key)
add(IntSetting.CPU_ACCURACY.key)
add(BooleanSetting.PICTURE_IN_PICTURE.key)
}
}
private fun addSystemSettings(sl: ArrayList<SettingsItem>) {
sl.apply {
add(BooleanSetting.RENDERER_USE_SPEED_LIMIT.key)
add(ShortSetting.RENDERER_SPEED_LIMIT.key)
add(BooleanSetting.USE_DOCKED_MODE.key)
add(IntSetting.REGION_INDEX.key)
add(IntSetting.LANGUAGE_INDEX.key)
@ -116,6 +135,7 @@ class SettingsFragmentPresenter(
add(IntSetting.RENDERER_ANTI_ALIASING.key)
add(IntSetting.RENDERER_SCREEN_LAYOUT.key)
add(IntSetting.RENDERER_ASPECT_RATIO.key)
add(BooleanSetting.PICTURE_IN_PICTURE.key)
add(BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE.key)
add(BooleanSetting.RENDERER_FORCE_MAX_CLOCK.key)
add(BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS.key)
@ -249,6 +269,7 @@ class SettingsFragmentPresenter(
add(BooleanSetting.RENDERER_DEBUG.key)
add(HeaderSetting(R.string.cpu))
add(IntSetting.CPU_ACCURACY.key)
add(BooleanSetting.CPU_DEBUG_MODE.key)
add(SettingsItem.FASTMEM_COMBINED)
}

View File

@ -4,6 +4,7 @@
package org.yuzu.yuzu_emu.features.settings.ui.viewholder
import android.view.View
import androidx.core.content.res.ResourcesCompat
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
import org.yuzu.yuzu_emu.features.settings.model.view.RunnableSetting
@ -16,6 +17,19 @@ class RunnableViewHolder(val binding: ListItemSettingBinding, adapter: SettingsA
override fun bind(item: SettingsItem) {
setting = item as RunnableSetting
if (item.iconId != 0) {
binding.icon.visibility = View.VISIBLE
binding.icon.setImageDrawable(
ResourcesCompat.getDrawable(
binding.icon.resources,
item.iconId,
binding.icon.context.theme
)
)
} else {
binding.icon.visibility = View.GONE
}
binding.textSettingName.setText(item.nameId)
if (item.descriptionId != 0) {
binding.textSettingDescription.setText(item.descriptionId)

View File

@ -4,6 +4,7 @@
package org.yuzu.yuzu_emu.features.settings.ui.viewholder
import android.view.View
import androidx.core.content.res.ResourcesCompat
import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
import org.yuzu.yuzu_emu.features.settings.model.view.SubmenuSetting
@ -15,6 +16,19 @@ class SubmenuViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAd
override fun bind(item: SettingsItem) {
this.item = item as SubmenuSetting
if (item.iconId != 0) {
binding.icon.visibility = View.VISIBLE
binding.icon.setImageDrawable(
ResourcesCompat.getDrawable(
binding.icon.resources,
item.iconId,
binding.icon.context.theme
)
)
} else {
binding.icon.visibility = View.GONE
}
binding.textSettingName.setText(item.nameId)
if (item.descriptionId != 0) {
binding.textSettingDescription.setText(item.descriptionId)

View File

@ -3,15 +3,8 @@
package org.yuzu.yuzu_emu.features.settings.utils
import android.widget.Toast
import java.io.*
import org.ini4j.Wini
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.features.settings.model.*
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
import org.yuzu.yuzu_emu.utils.Log
import org.yuzu.yuzu_emu.utils.NativeConfig
/**
* Contains static methods for interacting with .ini files in which settings are stored.
@ -19,41 +12,6 @@ import org.yuzu.yuzu_emu.utils.NativeConfig
object SettingsFile {
const val FILE_NAME_CONFIG = "config"
/**
* Saves a Settings HashMap to a given .ini file on disk. If unsuccessful, outputs an error
* telling why it failed.
*
* @param fileName The target filename without a path or extension.
*/
fun saveFile(fileName: String) {
val ini = getSettingsFile(fileName)
try {
val wini = Wini(ini)
for (specificCategory in Settings.Category.values()) {
val categoryHeader = NativeConfig.getConfigHeader(specificCategory.ordinal)
for (setting in Settings.settingsList) {
if (setting.key!!.isEmpty()) continue
val settingCategoryHeader =
NativeConfig.getConfigHeader(setting.category.ordinal)
val iniSetting: String? = wini.get(categoryHeader, setting.key)
if (iniSetting != null || settingCategoryHeader == categoryHeader) {
wini.put(settingCategoryHeader, setting.key, setting.valueAsString)
}
}
}
wini.store()
} catch (e: IOException) {
Log.error("[SettingsFile] File not found: " + fileName + ".ini: " + e.message)
val context = YuzuApplication.appContext
Toast.makeText(
context,
context.getString(R.string.error_saving, fileName, e.message),
Toast.LENGTH_SHORT
).show()
}
}
fun getSettingsFile(fileName: String): File =
File(DirectoryInitialization.userDirectory + "/config/" + fileName + ".ini")
}

View File

@ -114,10 +114,10 @@ class AboutFragment : Fragment() {
val leftInsets = barInsets.left + cutoutInsets.left
val rightInsets = barInsets.right + cutoutInsets.right
val mlpAppBar = binding.appbarAbout.layoutParams as MarginLayoutParams
mlpAppBar.leftMargin = leftInsets
mlpAppBar.rightMargin = rightInsets
binding.appbarAbout.layoutParams = mlpAppBar
val mlpToolbar = binding.toolbarAbout.layoutParams as MarginLayoutParams
mlpToolbar.leftMargin = leftInsets
mlpToolbar.rightMargin = rightInsets
binding.toolbarAbout.layoutParams = mlpToolbar
val mlpScrollAbout = binding.scrollAbout.layoutParams as MarginLayoutParams
mlpScrollAbout.leftMargin = leftInsets

View File

@ -10,7 +10,6 @@ import android.content.DialogInterface
import android.content.SharedPreferences
import android.content.pm.ActivityInfo
import android.content.res.Configuration
import android.graphics.Color
import android.net.Uri
import android.os.Bundle
import android.os.Handler
@ -155,7 +154,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
binding.surfaceEmulation.holder.addCallback(this)
binding.showFpsText.setTextColor(Color.YELLOW)
binding.doneControlConfig.setOnClickListener { stopConfiguringControls() }
binding.drawerLayout.addDrawerListener(object : DrawerListener {
@ -312,6 +310,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
ViewUtils.showView(binding.surfaceInputOverlay)
ViewUtils.hideView(binding.loadingIndicator)
emulationState.updateSurface()
// Setup overlay
updateShowFpsOverlay()
}
@ -412,12 +412,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
val FRAMETIME = 2
val SPEED = 3
perfStatsUpdater = {
if (emulationViewModel.emulationStarted.value == true) {
if (emulationViewModel.emulationStarted.value) {
val perfStats = NativeLibrary.getPerfStats()
if (perfStats[FPS] > 0 && _binding != null) {
if (_binding != null) {
binding.showFpsText.text = String.format("FPS: %.1f", perfStats[FPS])
}
perfStatsUpdateHandler.postDelayed(perfStatsUpdater!!, 100)
perfStatsUpdateHandler.postDelayed(perfStatsUpdater!!, 800)
}
}
perfStatsUpdateHandler.post(perfStatsUpdater!!)
@ -462,7 +462,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
if (it.orientation == FoldingFeature.Orientation.HORIZONTAL) {
// Restrict emulation and overlays to the top of the screen
binding.emulationContainer.layoutParams.height = it.bounds.top
binding.overlayContainer.layoutParams.height = it.bounds.top
// Restrict input and menu drawer to the bottom of the screen
binding.inputContainer.layoutParams.height = it.bounds.bottom
binding.inGameMenu.layoutParams.height = it.bounds.bottom
@ -476,7 +475,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
if (!isFolding) {
binding.emulationContainer.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT
binding.inputContainer.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT
binding.overlayContainer.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT
binding.inGameMenu.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT
isInFoldableLayout = false
updateOrientation()
@ -484,7 +482,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
binding.emulationContainer.requestLayout()
binding.inputContainer.requestLayout()
binding.overlayContainer.requestLayout()
binding.inGameMenu.requestLayout()
}
@ -710,24 +707,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
v.setPadding(left, cutInsets.top, right, 0)
// Ensure FPS text doesn't get cut off by rounded display corners
val sidePadding = resources.getDimensionPixelSize(R.dimen.spacing_xtralarge)
if (cutInsets.left == 0) {
binding.showFpsText.setPadding(
sidePadding,
cutInsets.top,
cutInsets.right,
cutInsets.bottom
)
} else {
binding.showFpsText.setPadding(
cutInsets.left,
cutInsets.top,
cutInsets.right,
cutInsets.bottom
)
}
windowInsets
}
}
@ -804,6 +783,13 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
}
@Synchronized
fun updateSurface() {
if (surface != null) {
NativeLibrary.surfaceChanged(surface)
}
}
@Synchronized
fun clearSurface() {
if (surface == null) {

View File

@ -40,8 +40,10 @@ class SettingsSearchFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false)
returnTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true)
enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
}
override fun onCreateView(
@ -55,7 +57,6 @@ class SettingsSearchFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
settingsViewModel.setIsUsingSearch(true)
if (savedInstanceState != null) {
binding.searchText.setText(savedInstanceState.getString(SEARCH_TEXT))

View File

@ -29,9 +29,6 @@ class SettingsViewModel : ViewModel() {
val shouldReloadSettingsList: StateFlow<Boolean> get() = _shouldReloadSettingsList
private val _shouldReloadSettingsList = MutableStateFlow(false)
val isUsingSearch: StateFlow<Boolean> get() = _isUsingSearch
private val _isUsingSearch = MutableStateFlow(false)
val sliderProgress: StateFlow<Int> get() = _sliderProgress
private val _sliderProgress = MutableStateFlow(-1)
@ -57,10 +54,6 @@ class SettingsViewModel : ViewModel() {
_shouldReloadSettingsList.value = value
}
fun setIsUsingSearch(value: Boolean) {
_isUsingSearch.value = value
}
fun setSliderTextValue(value: Float, units: String) {
_sliderProgress.value = value.toInt()
_sliderTextValue.value = String.format(

View File

@ -403,7 +403,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
} else {
firmwarePath.deleteRecursively()
cacheFirmwareDir.copyRecursively(firmwarePath, true)
NativeLibrary.initializeSystem()
NativeLibrary.initializeSystem(true)
getString(R.string.save_file_imported_success)
}
} catch (e: Exception) {
@ -632,6 +632,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
}
// Clear existing user data
NativeConfig.unloadConfig()
File(DirectoryInitialization.userDirectory!!).deleteRecursively()
// Copy archive to internal storage
@ -649,7 +650,8 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
}
// Reinitialize relevant data
NativeLibrary.initializeSystem()
NativeLibrary.initializeSystem(true)
NativeConfig.initializeConfig()
gamesViewModel.reloadGames(false)
return@newInstance getString(R.string.user_data_import_success)

View File

@ -15,7 +15,8 @@ object DirectoryInitialization {
fun start() {
if (!areDirectoriesReady) {
initializeInternalStorage()
NativeLibrary.initializeSystem()
NativeLibrary.initializeSystem(false)
NativeConfig.initializeConfig()
areDirectoriesReady = true
}
}

View File

@ -8,9 +8,9 @@ import android.graphics.BitmapFactory
import android.widget.ImageView
import androidx.core.graphics.drawable.toBitmap
import androidx.core.graphics.drawable.toDrawable
import androidx.lifecycle.LifecycleOwner
import coil.ImageLoader
import coil.decode.DataSource
import coil.executeBlocking
import coil.fetch.DrawableResult
import coil.fetch.FetchResult
import coil.fetch.Fetcher
@ -76,12 +76,13 @@ object GameIconUtils {
imageLoader.enqueue(request)
}
fun getGameIcon(game: Game): Bitmap {
suspend fun getGameIcon(lifecycleOwner: LifecycleOwner, game: Game): Bitmap {
val request = ImageRequest.Builder(YuzuApplication.appContext)
.data(game)
.lifecycle(lifecycleOwner)
.error(R.drawable.default_icon)
.build()
return imageLoader.executeBlocking(request)
return imageLoader.execute(request)
.drawable!!.toBitmap(config = Bitmap.Config.ARGB_8888)
}
}

View File

@ -3,6 +3,8 @@
package org.yuzu.yuzu_emu.utils
import android.os.Build
object Log {
// Tracks whether we should share the old log or the current log
var gameLaunched = false
@ -16,4 +18,14 @@ object Log {
external fun error(message: String)
external fun critical(message: String)
fun logDeviceInfo() {
info("Device Manufacturer - ${Build.MANUFACTURER}")
info("Device Model - ${Build.MODEL}")
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.R) {
info("SoC Manufacturer - ${Build.SOC_MANUFACTURER}")
info("SoC Model - ${Build.SOC_MODEL}")
}
info("Total System Memory - ${MemoryUtil.getDeviceRAM()}")
}
}

View File

@ -27,7 +27,7 @@ object MemoryUtil {
const val Pb = Tb * 1024
const val Eb = Pb * 1024
private fun bytesToSizeUnit(size: Float): String =
private fun bytesToSizeUnit(size: Float, roundUp: Boolean = false): String =
when {
size < Kb -> {
context.getString(
@ -39,63 +39,59 @@ object MemoryUtil {
size < Mb -> {
context.getString(
R.string.memory_formatted,
(size / Kb).hundredths,
if (roundUp) ceil(size / Kb) else (size / Kb).hundredths,
context.getString(R.string.memory_kilobyte)
)
}
size < Gb -> {
context.getString(
R.string.memory_formatted,
(size / Mb).hundredths,
if (roundUp) ceil(size / Mb) else (size / Mb).hundredths,
context.getString(R.string.memory_megabyte)
)
}
size < Tb -> {
context.getString(
R.string.memory_formatted,
(size / Gb).hundredths,
if (roundUp) ceil(size / Gb) else (size / Gb).hundredths,
context.getString(R.string.memory_gigabyte)
)
}
size < Pb -> {
context.getString(
R.string.memory_formatted,
(size / Tb).hundredths,
if (roundUp) ceil(size / Tb) else (size / Tb).hundredths,
context.getString(R.string.memory_terabyte)
)
}
size < Eb -> {
context.getString(
R.string.memory_formatted,
(size / Pb).hundredths,
if (roundUp) ceil(size / Pb) else (size / Pb).hundredths,
context.getString(R.string.memory_petabyte)
)
}
else -> {
context.getString(
R.string.memory_formatted,
(size / Eb).hundredths,
if (roundUp) ceil(size / Eb) else (size / Eb).hundredths,
context.getString(R.string.memory_exabyte)
)
}
}
// Devices are unlikely to have 0.5GB increments of memory so we'll just round up to account for
// the potential error created by memInfo.totalMem
private val totalMemory: Float
val totalMemory: Float
get() {
val memInfo = ActivityManager.MemoryInfo()
with(context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager) {
getMemoryInfo(memInfo)
}
return ceil(
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
memInfo.advertisedMem.toFloat()
} else {
memInfo.totalMem.toFloat()
}
)
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
memInfo.advertisedMem.toFloat()
} else {
memInfo.totalMem.toFloat()
}
}
fun isLessThan(minimum: Int, size: Float): Boolean =
@ -109,5 +105,7 @@ object MemoryUtil {
else -> totalMemory < Kb && totalMemory < minimum
}
fun getDeviceRAM(): String = bytesToSizeUnit(totalMemory)
// Devices are unlikely to have 0.5GB increments of memory so we'll just round up to account for
// the potential error created by memInfo.totalMem
fun getDeviceRAM(): String = bytesToSizeUnit(totalMemory, true)
}

View File

@ -4,6 +4,30 @@
package org.yuzu.yuzu_emu.utils
object NativeConfig {
/**
* Creates a Config object and opens the emulation config.
*/
@Synchronized
external fun initializeConfig()
/**
* Destroys the stored config object. This automatically saves the existing config.
*/
@Synchronized
external fun unloadConfig()
/**
* Reads values saved to the config file and saves them.
*/
@Synchronized
external fun reloadSettings()
/**
* Saves settings values in memory to disk.
*/
@Synchronized
external fun saveSettings()
external fun getBoolean(key: String, getDefault: Boolean): Boolean
external fun setBoolean(key: String, value: Boolean)

View File

@ -6,9 +6,6 @@ add_library(yuzu-android SHARED
android_common/android_common.h
applets/software_keyboard.cpp
applets/software_keyboard.h
config.cpp
config.h
default_ini.h
emu_window/emu_window.cpp
emu_window/emu_window.h
id_cache.cpp
@ -16,15 +13,17 @@ add_library(yuzu-android SHARED
native.cpp
native.h
native_config.cpp
uisettings.cpp
android_settings.cpp
game_metadata.cpp
native_log.cpp
android_config.cpp
android_config.h
)
set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR})
target_link_libraries(yuzu-android PRIVATE audio_core common core input_common)
target_link_libraries(yuzu-android PRIVATE android camera2ndk EGL glad inih jnigraphics log)
target_link_libraries(yuzu-android PRIVATE audio_core common core input_common frontend_common)
target_link_libraries(yuzu-android PRIVATE android camera2ndk EGL glad jnigraphics log)
if (ARCHITECTURE_arm64)
target_link_libraries(yuzu-android PRIVATE adrenotools)
endif()

View File

@ -0,0 +1,70 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "android_config.h"
#include "android_settings.h"
#include "common/settings_setting.h"
AndroidConfig::AndroidConfig(const std::string& config_name, ConfigType config_type)
: Config(config_type) {
Initialize(config_name);
if (config_type != ConfigType::InputProfile) {
ReadAndroidValues();
SaveAndroidValues();
}
}
AndroidConfig::~AndroidConfig() {
if (global) {
AndroidConfig::SaveAllValues();
}
}
void AndroidConfig::ReloadAllValues() {
Reload();
ReadAndroidValues();
SaveAndroidValues();
}
void AndroidConfig::SaveAllValues() {
Save();
SaveAndroidValues();
}
void AndroidConfig::ReadAndroidValues() {
if (global) {
ReadAndroidUIValues();
}
}
void AndroidConfig::ReadAndroidUIValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::Android));
ReadCategory(Settings::Category::Android);
EndGroup();
}
void AndroidConfig::SaveAndroidValues() {
if (global) {
SaveAndroidUIValues();
}
WriteToIni();
}
void AndroidConfig::SaveAndroidUIValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::Android));
WriteCategory(Settings::Category::Android);
EndGroup();
}
std::vector<Settings::BasicSetting*>& AndroidConfig::FindRelevantList(Settings::Category category) {
auto& map = Settings::values.linkage.by_category;
if (map.contains(category)) {
return Settings::values.linkage.by_category[category];
}
return AndroidSettings::values.linkage.by_category[category];
}

View File

@ -0,0 +1,41 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "frontend_common/config.h"
class AndroidConfig final : public Config {
public:
explicit AndroidConfig(const std::string& config_name = "config",
ConfigType config_type = ConfigType::GlobalConfig);
~AndroidConfig() override;
void ReloadAllValues() override;
void SaveAllValues() override;
protected:
void ReadAndroidValues();
void ReadAndroidUIValues();
void ReadHidbusValues() override {}
void ReadDebugControlValues() override {}
void ReadPathValues() override {}
void ReadShortcutValues() override {}
void ReadUIValues() override {}
void ReadUIGamelistValues() override {}
void ReadUILayoutValues() override {}
void ReadMultiplayerValues() override {}
void SaveAndroidValues();
void SaveAndroidUIValues();
void SaveHidbusValues() override {}
void SaveDebugControlValues() override {}
void SavePathValues() override {}
void SaveShortcutValues() override {}
void SaveUIValues() override {}
void SaveUIGamelistValues() override {}
void SaveUILayoutValues() override {}
void SaveMultiplayerValues() override {}
std::vector<Settings::BasicSetting*>& FindRelevantList(Settings::Category category) override;
};

View File

@ -1,7 +1,7 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "uisettings.h"
#include "android_settings.h"
namespace AndroidSettings {

View File

@ -1,330 +0,0 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <memory>
#include <optional>
#include <sstream>
#include <INIReader.h>
#include "common/fs/file.h"
#include "common/fs/fs.h"
#include "common/fs/path_util.h"
#include "common/logging/log.h"
#include "common/settings.h"
#include "common/settings_enums.h"
#include "core/hle/service/acc/profile_manager.h"
#include "input_common/main.h"
#include "jni/config.h"
#include "jni/default_ini.h"
#include "uisettings.h"
namespace FS = Common::FS;
Config::Config(const std::string& config_name, ConfigType config_type)
: type(config_type), global{config_type == ConfigType::GlobalConfig} {
Initialize(config_name);
}
Config::~Config() = default;
bool Config::LoadINI(const std::string& default_contents, bool retry) {
void(FS::CreateParentDir(config_loc));
config = std::make_unique<INIReader>(FS::PathToUTF8String(config_loc));
const auto config_loc_str = FS::PathToUTF8String(config_loc);
if (config->ParseError() < 0) {
if (retry) {
LOG_WARNING(Config, "Failed to load {}. Creating file from defaults...",
config_loc_str);
void(FS::CreateParentDir(config_loc));
void(FS::WriteStringToFile(config_loc, FS::FileType::TextFile, default_contents));
config = std::make_unique<INIReader>(config_loc_str);
return LoadINI(default_contents, false);
}
LOG_ERROR(Config, "Failed.");
return false;
}
LOG_INFO(Config, "Successfully loaded {}", config_loc_str);
return true;
}
template <>
void Config::ReadSetting(const std::string& group, Settings::Setting<std::string>& setting) {
std::string setting_value = config->Get(group, setting.GetLabel(), setting.GetDefault());
if (setting_value.empty()) {
setting_value = setting.GetDefault();
}
setting = std::move(setting_value);
}
template <>
void Config::ReadSetting(const std::string& group, Settings::Setting<bool>& setting) {
setting = config->GetBoolean(group, setting.GetLabel(), setting.GetDefault());
}
template <typename Type, bool ranged>
void Config::ReadSetting(const std::string& group, Settings::Setting<Type, ranged>& setting) {
setting = static_cast<Type>(
config->GetInteger(group, setting.GetLabel(), static_cast<long>(setting.GetDefault())));
}
void Config::ReadValues() {
ReadSetting("ControlsGeneral", Settings::values.mouse_enabled);
ReadSetting("ControlsGeneral", Settings::values.touch_device);
ReadSetting("ControlsGeneral", Settings::values.keyboard_enabled);
ReadSetting("ControlsGeneral", Settings::values.debug_pad_enabled);
ReadSetting("ControlsGeneral", Settings::values.vibration_enabled);
ReadSetting("ControlsGeneral", Settings::values.enable_accurate_vibrations);
ReadSetting("ControlsGeneral", Settings::values.motion_enabled);
Settings::values.touchscreen.enabled =
config->GetBoolean("ControlsGeneral", "touch_enabled", true);
Settings::values.touchscreen.rotation_angle =
config->GetInteger("ControlsGeneral", "touch_angle", 0);
Settings::values.touchscreen.diameter_x =
config->GetInteger("ControlsGeneral", "touch_diameter_x", 15);
Settings::values.touchscreen.diameter_y =
config->GetInteger("ControlsGeneral", "touch_diameter_y", 15);
int num_touch_from_button_maps =
config->GetInteger("ControlsGeneral", "touch_from_button_map", 0);
if (num_touch_from_button_maps > 0) {
for (int i = 0; i < num_touch_from_button_maps; ++i) {
Settings::TouchFromButtonMap map;
map.name = config->Get("ControlsGeneral",
std::string("touch_from_button_maps_") + std::to_string(i) +
std::string("_name"),
"default");
const int num_touch_maps = config->GetInteger(
"ControlsGeneral",
std::string("touch_from_button_maps_") + std::to_string(i) + std::string("_count"),
0);
map.buttons.reserve(num_touch_maps);
for (int j = 0; j < num_touch_maps; ++j) {
std::string touch_mapping =
config->Get("ControlsGeneral",
std::string("touch_from_button_maps_") + std::to_string(i) +
std::string("_bind_") + std::to_string(j),
"");
map.buttons.emplace_back(std::move(touch_mapping));
}
Settings::values.touch_from_button_maps.emplace_back(std::move(map));
}
} else {
Settings::values.touch_from_button_maps.emplace_back(
Settings::TouchFromButtonMap{"default", {}});
num_touch_from_button_maps = 1;
}
Settings::values.touch_from_button_map_index = std::clamp(
Settings::values.touch_from_button_map_index.GetValue(), 0, num_touch_from_button_maps - 1);
ReadSetting("ControlsGeneral", Settings::values.udp_input_servers);
// Data Storage
ReadSetting("Data Storage", Settings::values.use_virtual_sd);
FS::SetYuzuPath(FS::YuzuPath::NANDDir,
config->Get("Data Storage", "nand_directory",
FS::GetYuzuPathString(FS::YuzuPath::NANDDir)));
FS::SetYuzuPath(FS::YuzuPath::SDMCDir,
config->Get("Data Storage", "sdmc_directory",
FS::GetYuzuPathString(FS::YuzuPath::SDMCDir)));
FS::SetYuzuPath(FS::YuzuPath::LoadDir,
config->Get("Data Storage", "load_directory",
FS::GetYuzuPathString(FS::YuzuPath::LoadDir)));
FS::SetYuzuPath(FS::YuzuPath::DumpDir,
config->Get("Data Storage", "dump_directory",
FS::GetYuzuPathString(FS::YuzuPath::DumpDir)));
ReadSetting("Data Storage", Settings::values.gamecard_inserted);
ReadSetting("Data Storage", Settings::values.gamecard_current_game);
ReadSetting("Data Storage", Settings::values.gamecard_path);
// System
ReadSetting("System", Settings::values.current_user);
Settings::values.current_user = std::clamp<int>(Settings::values.current_user.GetValue(), 0,
Service::Account::MAX_USERS - 1);
// Disable docked mode by default on Android
Settings::values.use_docked_mode.SetValue(config->GetBoolean("System", "use_docked_mode", false)
? Settings::ConsoleMode::Docked
: Settings::ConsoleMode::Handheld);
const auto rng_seed_enabled = config->GetBoolean("System", "rng_seed_enabled", false);
if (rng_seed_enabled) {
Settings::values.rng_seed.SetValue(config->GetInteger("System", "rng_seed", 0));
} else {
Settings::values.rng_seed.SetValue(0);
}
Settings::values.rng_seed_enabled.SetValue(rng_seed_enabled);
const auto custom_rtc_enabled = config->GetBoolean("System", "custom_rtc_enabled", false);
if (custom_rtc_enabled) {
Settings::values.custom_rtc = config->GetInteger("System", "custom_rtc", 0);
} else {
Settings::values.custom_rtc = 0;
}
Settings::values.custom_rtc_enabled = custom_rtc_enabled;
ReadSetting("System", Settings::values.language_index);
ReadSetting("System", Settings::values.region_index);
ReadSetting("System", Settings::values.time_zone_index);
ReadSetting("System", Settings::values.sound_index);
// Core
ReadSetting("Core", Settings::values.use_multi_core);
ReadSetting("Core", Settings::values.memory_layout_mode);
// Cpu
ReadSetting("Cpu", Settings::values.cpu_accuracy);
ReadSetting("Cpu", Settings::values.cpu_debug_mode);
ReadSetting("Cpu", Settings::values.cpuopt_page_tables);
ReadSetting("Cpu", Settings::values.cpuopt_block_linking);
ReadSetting("Cpu", Settings::values.cpuopt_return_stack_buffer);
ReadSetting("Cpu", Settings::values.cpuopt_fast_dispatcher);
ReadSetting("Cpu", Settings::values.cpuopt_context_elimination);
ReadSetting("Cpu", Settings::values.cpuopt_const_prop);
ReadSetting("Cpu", Settings::values.cpuopt_misc_ir);
ReadSetting("Cpu", Settings::values.cpuopt_reduce_misalign_checks);
ReadSetting("Cpu", Settings::values.cpuopt_fastmem);
ReadSetting("Cpu", Settings::values.cpuopt_fastmem_exclusives);
ReadSetting("Cpu", Settings::values.cpuopt_recompile_exclusives);
ReadSetting("Cpu", Settings::values.cpuopt_ignore_memory_aborts);
ReadSetting("Cpu", Settings::values.cpuopt_unsafe_unfuse_fma);
ReadSetting("Cpu", Settings::values.cpuopt_unsafe_reduce_fp_error);
ReadSetting("Cpu", Settings::values.cpuopt_unsafe_ignore_standard_fpcr);
ReadSetting("Cpu", Settings::values.cpuopt_unsafe_inaccurate_nan);
ReadSetting("Cpu", Settings::values.cpuopt_unsafe_fastmem_check);
ReadSetting("Cpu", Settings::values.cpuopt_unsafe_ignore_global_monitor);
// Renderer
ReadSetting("Renderer", Settings::values.renderer_backend);
ReadSetting("Renderer", Settings::values.renderer_debug);
ReadSetting("Renderer", Settings::values.renderer_shader_feedback);
ReadSetting("Renderer", Settings::values.enable_nsight_aftermath);
ReadSetting("Renderer", Settings::values.disable_shader_loop_safety_checks);
ReadSetting("Renderer", Settings::values.vulkan_device);
ReadSetting("Renderer", Settings::values.resolution_setup);
ReadSetting("Renderer", Settings::values.scaling_filter);
ReadSetting("Renderer", Settings::values.fsr_sharpening_slider);
ReadSetting("Renderer", Settings::values.anti_aliasing);
ReadSetting("Renderer", Settings::values.fullscreen_mode);
ReadSetting("Renderer", Settings::values.aspect_ratio);
ReadSetting("Renderer", Settings::values.max_anisotropy);
ReadSetting("Renderer", Settings::values.use_speed_limit);
ReadSetting("Renderer", Settings::values.speed_limit);
ReadSetting("Renderer", Settings::values.use_disk_shader_cache);
ReadSetting("Renderer", Settings::values.use_asynchronous_gpu_emulation);
ReadSetting("Renderer", Settings::values.vsync_mode);
ReadSetting("Renderer", Settings::values.shader_backend);
ReadSetting("Renderer", Settings::values.use_asynchronous_shaders);
ReadSetting("Renderer", Settings::values.nvdec_emulation);
ReadSetting("Renderer", Settings::values.use_fast_gpu_time);
ReadSetting("Renderer", Settings::values.use_vulkan_driver_pipeline_cache);
ReadSetting("Renderer", Settings::values.bg_red);
ReadSetting("Renderer", Settings::values.bg_green);
ReadSetting("Renderer", Settings::values.bg_blue);
// Use GPU accuracy normal by default on Android
Settings::values.gpu_accuracy = static_cast<Settings::GpuAccuracy>(config->GetInteger(
"Renderer", "gpu_accuracy", static_cast<u32>(Settings::GpuAccuracy::Normal)));
// Use GPU default anisotropic filtering on Android
Settings::values.max_anisotropy =
static_cast<Settings::AnisotropyMode>(config->GetInteger("Renderer", "max_anisotropy", 1));
// Disable ASTC compute by default on Android
Settings::values.accelerate_astc.SetValue(
config->GetBoolean("Renderer", "accelerate_astc", false) ? Settings::AstcDecodeMode::Gpu
: Settings::AstcDecodeMode::Cpu);
// Enable asynchronous presentation by default on Android
Settings::values.async_presentation =
config->GetBoolean("Renderer", "async_presentation", true);
// Disable force_max_clock by default on Android
Settings::values.renderer_force_max_clock =
config->GetBoolean("Renderer", "force_max_clock", false);
// Disable use_reactive_flushing by default on Android
Settings::values.use_reactive_flushing =
config->GetBoolean("Renderer", "use_reactive_flushing", false);
// Audio
ReadSetting("Audio", Settings::values.sink_id);
ReadSetting("Audio", Settings::values.audio_output_device_id);
ReadSetting("Audio", Settings::values.volume);
// Miscellaneous
// log_filter has a different default here than from common
Settings::values.log_filter = "*:Info";
ReadSetting("Miscellaneous", Settings::values.use_dev_keys);
// Debugging
Settings::values.record_frame_times =
config->GetBoolean("Debugging", "record_frame_times", false);
ReadSetting("Debugging", Settings::values.dump_exefs);
ReadSetting("Debugging", Settings::values.dump_nso);
ReadSetting("Debugging", Settings::values.enable_fs_access_log);
ReadSetting("Debugging", Settings::values.reporting_services);
ReadSetting("Debugging", Settings::values.quest_flag);
ReadSetting("Debugging", Settings::values.use_debug_asserts);
ReadSetting("Debugging", Settings::values.use_auto_stub);
ReadSetting("Debugging", Settings::values.disable_macro_jit);
ReadSetting("Debugging", Settings::values.disable_macro_hle);
ReadSetting("Debugging", Settings::values.use_gdbstub);
ReadSetting("Debugging", Settings::values.gdbstub_port);
const auto title_list = config->Get("AddOns", "title_ids", "");
std::stringstream ss(title_list);
std::string line;
while (std::getline(ss, line, '|')) {
const auto title_id = std::strtoul(line.c_str(), nullptr, 16);
const auto disabled_list = config->Get("AddOns", "disabled_" + line, "");
std::stringstream inner_ss(disabled_list);
std::string inner_line;
std::vector<std::string> out;
while (std::getline(inner_ss, inner_line, '|')) {
out.push_back(inner_line);
}
Settings::values.disabled_addons.insert_or_assign(title_id, out);
}
// Web Service
ReadSetting("WebService", Settings::values.enable_telemetry);
ReadSetting("WebService", Settings::values.web_api_url);
ReadSetting("WebService", Settings::values.yuzu_username);
ReadSetting("WebService", Settings::values.yuzu_token);
// Network
ReadSetting("Network", Settings::values.network_interface);
// Android
ReadSetting("Android", AndroidSettings::values.picture_in_picture);
ReadSetting("Android", AndroidSettings::values.screen_layout);
}
void Config::Initialize(const std::string& config_name) {
const auto fs_config_loc = FS::GetYuzuPath(FS::YuzuPath::ConfigDir);
const auto config_file = fmt::format("{}.ini", config_name);
switch (type) {
case ConfigType::GlobalConfig:
config_loc = FS::PathToUTF8String(fs_config_loc / config_file);
break;
case ConfigType::PerGameConfig:
config_loc = FS::PathToUTF8String(fs_config_loc / "custom" / FS::ToU8String(config_file));
break;
case ConfigType::InputProfile:
config_loc = FS::PathToUTF8String(fs_config_loc / "input" / config_file);
LoadINI(DefaultINI::android_config_file);
return;
}
LoadINI(DefaultINI::android_config_file);
ReadValues();
}

View File

@ -1,47 +0,0 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <filesystem>
#include <memory>
#include <optional>
#include <string>
#include "common/settings.h"
class INIReader;
class Config {
bool LoadINI(const std::string& default_contents = "", bool retry = true);
public:
enum class ConfigType {
GlobalConfig,
PerGameConfig,
InputProfile,
};
explicit Config(const std::string& config_name = "config",
ConfigType config_type = ConfigType::GlobalConfig);
~Config();
void Initialize(const std::string& config_name);
private:
/**
* Applies a value read from the config to a Setting.
*
* @param group The name of the INI group
* @param setting The yuzu setting to modify
*/
template <typename Type, bool ranged>
void ReadSetting(const std::string& group, Settings::Setting<Type, ranged>& setting);
void ReadValues();
const ConfigType type;
std::unique_ptr<INIReader> config;
std::string config_loc;
const bool global;
};

View File

@ -1,511 +0,0 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
namespace DefaultINI {
const char* android_config_file = R"(
[ControlsP0]
# The input devices and parameters for each Switch native input
# The config section determines the player number where the config will be applied on. For example "ControlsP0", "ControlsP1", ...
# It should be in the format of "engine:[engine_name],[param1]:[value1],[param2]:[value2]..."
# Escape characters $0 (for ':'), $1 (for ',') and $2 (for '$') can be used in values
# Indicates if this player should be connected at boot
connected=
# for button input, the following devices are available:
# - "keyboard" (default) for keyboard input. Required parameters:
# - "code": the code of the key to bind
# - "sdl" for joystick input using SDL. Required parameters:
# - "guid": SDL identification GUID of the joystick
# - "port": the index of the joystick to bind
# - "button"(optional): the index of the button to bind
# - "hat"(optional): the index of the hat to bind as direction buttons
# - "axis"(optional): the index of the axis to bind
# - "direction"(only used for hat): the direction name of the hat to bind. Can be "up", "down", "left" or "right"
# - "threshold"(only used for axis): a float value in (-1.0, 1.0) which the button is
# triggered if the axis value crosses
# - "direction"(only used for axis): "+" means the button is triggered when the axis value
# is greater than the threshold; "-" means the button is triggered when the axis value
# is smaller than the threshold
button_a=
button_b=
button_x=
button_y=
button_lstick=
button_rstick=
button_l=
button_r=
button_zl=
button_zr=
button_plus=
button_minus=
button_dleft=
button_dup=
button_dright=
button_ddown=
button_lstick_left=
button_lstick_up=
button_lstick_right=
button_lstick_down=
button_sl=
button_sr=
button_home=
button_screenshot=
# for analog input, the following devices are available:
# - "analog_from_button" (default) for emulating analog input from direction buttons. Required parameters:
# - "up", "down", "left", "right": sub-devices for each direction.
# Should be in the format as a button input devices using escape characters, for example, "engine$0keyboard$1code$00"
# - "modifier": sub-devices as a modifier.
# - "modifier_scale": a float number representing the applied modifier scale to the analog input.
# Must be in range of 0.0-1.0. Defaults to 0.5
# - "sdl" for joystick input using SDL. Required parameters:
# - "guid": SDL identification GUID of the joystick
# - "port": the index of the joystick to bind
# - "axis_x": the index of the axis to bind as x-axis (default to 0)
# - "axis_y": the index of the axis to bind as y-axis (default to 1)
lstick=
rstick=
# for motion input, the following devices are available:
# - "keyboard" (default) for emulating random motion input from buttons. Required parameters:
# - "code": the code of the key to bind
# - "sdl" for motion input using SDL. Required parameters:
# - "guid": SDL identification GUID of the joystick
# - "port": the index of the joystick to bind
# - "motion": the index of the motion sensor to bind
# - "cemuhookudp" for motion input using Cemu Hook protocol. Required parameters:
# - "guid": the IP address of the cemu hook server encoded to a hex string. for example 192.168.0.1 = "c0a80001"
# - "port": the port of the cemu hook server
# - "pad": the index of the joystick
# - "motion": the index of the motion sensor of the joystick to bind
motionleft=
motionright=
[ControlsGeneral]
# To use the debug_pad, prepend `debug_pad_` before each button setting above.
# i.e. debug_pad_button_a=
# Enable debug pad inputs to the guest
# 0 (default): Disabled, 1: Enabled
debug_pad_enabled =
# Whether to enable or disable vibration
# 0: Disabled, 1 (default): Enabled
vibration_enabled=
# Whether to enable or disable accurate vibrations
# 0 (default): Disabled, 1: Enabled
enable_accurate_vibrations=
# Enables controller motion inputs
# 0: Disabled, 1 (default): Enabled
motion_enabled =
# Defines the udp device's touch screen coordinate system for cemuhookudp devices
# - "min_x", "min_y", "max_x", "max_y"
touch_device=
# for mapping buttons to touch inputs.
#touch_from_button_map=1
#touch_from_button_maps_0_name=default
#touch_from_button_maps_0_count=2
#touch_from_button_maps_0_bind_0=foo
#touch_from_button_maps_0_bind_1=bar
# etc.
# List of Cemuhook UDP servers, delimited by ','.
# Default: 127.0.0.1:26760
# Example: 127.0.0.1:26760,123.4.5.67:26761
udp_input_servers =
# Enable controlling an axis via a mouse input.
# 0 (default): Off, 1: On
mouse_panning =
# Set mouse sensitivity.
# Default: 1.0
mouse_panning_sensitivity =
# Emulate an analog control stick from keyboard inputs.
# 0 (default): Disabled, 1: Enabled
emulate_analog_keyboard =
# Enable mouse inputs to the guest
# 0 (default): Disabled, 1: Enabled
mouse_enabled =
# Enable keyboard inputs to the guest
# 0 (default): Disabled, 1: Enabled
keyboard_enabled =
[Core]
# Whether to use multi-core for CPU emulation
# 0: Disabled, 1 (default): Enabled
use_multi_core =
# Enable unsafe extended guest system memory layout (8GB DRAM)
# 0 (default): Disabled, 1: Enabled
use_unsafe_extended_memory_layout =
[Cpu]
# Adjusts various optimizations.
# Auto-select mode enables choice unsafe optimizations.
# Accurate enables only safe optimizations.
# Unsafe allows any unsafe optimizations.
# 0 (default): Auto-select, 1: Accurate, 2: Enable unsafe optimizations
cpu_accuracy =
# Allow disabling safe optimizations.
# 0 (default): Disabled, 1: Enabled
cpu_debug_mode =
# Enable inline page tables optimization (faster guest memory access)
# 0: Disabled, 1 (default): Enabled
cpuopt_page_tables =
# Enable block linking CPU optimization (reduce block dispatcher use during predictable jumps)
# 0: Disabled, 1 (default): Enabled
cpuopt_block_linking =
# Enable return stack buffer CPU optimization (reduce block dispatcher use during predictable returns)
# 0: Disabled, 1 (default): Enabled
cpuopt_return_stack_buffer =
# Enable fast dispatcher CPU optimization (use a two-tiered dispatcher architecture)
# 0: Disabled, 1 (default): Enabled
cpuopt_fast_dispatcher =
# Enable context elimination CPU Optimization (reduce host memory use for guest context)
# 0: Disabled, 1 (default): Enabled
cpuopt_context_elimination =
# Enable constant propagation CPU optimization (basic IR optimization)
# 0: Disabled, 1 (default): Enabled
cpuopt_const_prop =
# Enable miscellaneous CPU optimizations (basic IR optimization)
# 0: Disabled, 1 (default): Enabled
cpuopt_misc_ir =
# Enable reduction of memory misalignment checks (reduce memory fallbacks for misaligned access)
# 0: Disabled, 1 (default): Enabled
cpuopt_reduce_misalign_checks =
# Enable Host MMU Emulation (faster guest memory access)
# 0: Disabled, 1 (default): Enabled
cpuopt_fastmem =
# Enable Host MMU Emulation for exclusive memory instructions (faster guest memory access)
# 0: Disabled, 1 (default): Enabled
cpuopt_fastmem_exclusives =
# Enable fallback on failure of fastmem of exclusive memory instructions (faster guest memory access)
# 0: Disabled, 1 (default): Enabled
cpuopt_recompile_exclusives =
# Enable optimization to ignore invalid memory accesses (faster guest memory access)
# 0: Disabled, 1 (default): Enabled
cpuopt_ignore_memory_aborts =
# Enable unfuse FMA (improve performance on CPUs without FMA)
# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select.
# 0: Disabled, 1 (default): Enabled
cpuopt_unsafe_unfuse_fma =
# Enable faster FRSQRTE and FRECPE
# Only enabled if cpu_accuracy is set to Unsafe.
# 0: Disabled, 1 (default): Enabled
cpuopt_unsafe_reduce_fp_error =
# Enable faster ASIMD instructions (32 bits only)
# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select.
# 0: Disabled, 1 (default): Enabled
cpuopt_unsafe_ignore_standard_fpcr =
# Enable inaccurate NaN handling
# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select.
# 0: Disabled, 1 (default): Enabled
cpuopt_unsafe_inaccurate_nan =
# Disable address space checks (64 bits only)
# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select.
# 0: Disabled, 1 (default): Enabled
cpuopt_unsafe_fastmem_check =
# Enable faster exclusive instructions
# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select.
# 0: Disabled, 1 (default): Enabled
cpuopt_unsafe_ignore_global_monitor =
[Renderer]
# Which backend API to use.
# 0: OpenGL (unsupported), 1 (default): Vulkan, 2: Null
backend =
# Whether to enable asynchronous presentation (Vulkan only)
# 0: Off, 1 (default): On
async_presentation =
# Forces the GPU to run at the maximum possible clocks (thermal constraints will still be applied).
# 0 (default): Disabled, 1: Enabled
force_max_clock =
# Enable graphics API debugging mode.
# 0 (default): Disabled, 1: Enabled
debug =
# Enable shader feedback.
# 0 (default): Disabled, 1: Enabled
renderer_shader_feedback =
# Enable Nsight Aftermath crash dumps
# 0 (default): Disabled, 1: Enabled
nsight_aftermath =
# Disable shader loop safety checks, executing the shader without loop logic changes
# 0 (default): Disabled, 1: Enabled
disable_shader_loop_safety_checks =
# Which Vulkan physical device to use (defaults to 0)
vulkan_device =
# 0: 0.5x (360p/540p) [EXPERIMENTAL]
# 1: 0.75x (540p/810p) [EXPERIMENTAL]
# 2 (default): 1x (720p/1080p)
# 3: 2x (1440p/2160p)
# 4: 3x (2160p/3240p)
# 5: 4x (2880p/4320p)
# 6: 5x (3600p/5400p)
# 7: 6x (4320p/6480p)
resolution_setup =
# Pixel filter to use when up- or down-sampling rendered frames.
# 0: Nearest Neighbor
# 1 (default): Bilinear
# 2: Bicubic
# 3: Gaussian
# 4: ScaleForce
# 5: AMD FidelityFX™ Super Resolution [Vulkan Only]
scaling_filter =
# Anti-Aliasing (AA)
# 0 (default): None, 1: FXAA
anti_aliasing =
# Whether to use fullscreen or borderless window mode
# 0 (Windows default): Borderless window, 1 (All other default): Exclusive fullscreen
fullscreen_mode =
# Aspect ratio
# 0: Default (16:9), 1: Force 4:3, 2: Force 21:9, 3: Force 16:10, 4: Stretch to Window
aspect_ratio =
# Anisotropic filtering
# 0: Default, 1: 2x, 2: 4x, 3: 8x, 4: 16x
max_anisotropy =
# Whether to enable VSync or not.
# OpenGL: Values other than 0 enable VSync
# Vulkan: FIFO is selected if the requested mode is not supported by the driver.
# FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate.
# FIFO Relaxed is similar to FIFO but allows tearing as it recovers from a slow down.
# Mailbox can have lower latency than FIFO and does not tear but may drop frames.
# Immediate (no synchronization) just presents whatever is available and can exhibit tearing.
# 0: Immediate (Off), 1 (Default): Mailbox (On), 2: FIFO, 3: FIFO Relaxed
use_vsync =
# Selects the OpenGL shader backend. NV_gpu_program5 is required for GLASM. If NV_gpu_program5 is
# not available and GLASM is selected, GLSL will be used.
# 0: GLSL, 1 (default): GLASM, 2: SPIR-V
shader_backend =
# Whether to allow asynchronous shader building.
# 0 (default): Off, 1: On
use_asynchronous_shaders =
# Uses reactive flushing instead of predictive flushing. Allowing a more accurate syncing of memory.
# 0 (default): Off, 1: On
use_reactive_flushing =
# NVDEC emulation.
# 0: Disabled, 1: CPU Decoding, 2 (default): GPU Decoding
nvdec_emulation =
# Accelerate ASTC texture decoding.
# 0 (default): Off, 1: On
accelerate_astc =
# Turns on the speed limiter, which will limit the emulation speed to the desired speed limit value
# 0: Off, 1: On (default)
use_speed_limit =
# Limits the speed of the game to run no faster than this value as a percentage of target speed
# 1 - 9999: Speed limit as a percentage of target game speed. 100 (default)
speed_limit =
# Whether to use disk based shader cache
# 0: Off, 1 (default): On
use_disk_shader_cache =
# Which gpu accuracy level to use
# 0 (default): Normal, 1: High, 2: Extreme (Very slow)
gpu_accuracy =
# Whether to use asynchronous GPU emulation
# 0 : Off (slow), 1 (default): On (fast)
use_asynchronous_gpu_emulation =
# Inform the guest that GPU operations completed more quickly than they did.
# 0: Off, 1 (default): On
use_fast_gpu_time =
# Force unmodified buffers to be flushed, which can cost performance.
# 0: Off (default), 1: On
use_pessimistic_flushes =
# Whether to use garbage collection or not for GPU caches.
# 0 (default): Off, 1: On
use_caches_gc =
# The clear color for the renderer. What shows up on the sides of the bottom screen.
# Must be in range of 0-255. Defaults to 0 for all.
bg_red =
bg_blue =
bg_green =
[Audio]
# Which audio output engine to use.
# auto (default): Auto-select
# cubeb: Cubeb audio engine (if available)
# sdl2: SDL2 audio engine (if available)
# null: No audio output
output_engine =
# Which audio device to use.
# auto (default): Auto-select
output_device =
# Output volume.
# 100 (default): 100%, 0; mute
volume =
[Data Storage]
# Whether to create a virtual SD card.
# 1: Yes, 0 (default): No
use_virtual_sd =
# Whether or not to enable gamecard emulation
# 1: Yes, 0 (default): No
gamecard_inserted =
# Whether or not the gamecard should be emulated as the current game
# If 'gamecard_inserted' is 0 this setting is irrelevant
# 1: Yes, 0 (default): No
gamecard_current_game =
# Path to an XCI file to use as the gamecard
# If 'gamecard_inserted' is 0 this setting is irrelevant
# If 'gamecard_current_game' is 1 this setting is irrelevant
gamecard_path =
[System]
# Whether the system is docked
# 1 (default): Yes, 0: No
use_docked_mode =
# Sets the seed for the RNG generator built into the switch
# rng_seed will be ignored and randomly generated if rng_seed_enabled is false
rng_seed_enabled =
rng_seed =
# Sets the current time (in seconds since 12:00 AM Jan 1, 1970) that will be used by the time service
# This will auto-increment, with the time set being the time the game is started
# This override will only occur if custom_rtc_enabled is true, otherwise the current time is used
custom_rtc_enabled =
custom_rtc =
# Sets the systems language index
# 0: Japanese, 1: English (default), 2: French, 3: German, 4: Italian, 5: Spanish, 6: Chinese,
# 7: Korean, 8: Dutch, 9: Portuguese, 10: Russian, 11: Taiwanese, 12: British English, 13: Canadian French,
# 14: Latin American Spanish, 15: Simplified Chinese, 16: Traditional Chinese, 17: Brazilian Portuguese
language_index =
# The system region that yuzu will use during emulation
# -1: Auto-select (default), 0: Japan, 1: USA, 2: Europe, 3: Australia, 4: China, 5: Korea, 6: Taiwan
region_index =
# The system time zone that yuzu will use during emulation
# 0: Auto-select (default), 1: Default (system archive value), Others: Index for specified time zone
time_zone_index =
# Sets the sound output mode.
# 0: Mono, 1 (default): Stereo, 2: Surround
sound_index =
[Miscellaneous]
# A filter which removes logs below a certain logging level.
# Examples: *:Debug Kernel.SVC:Trace Service.*:Critical
log_filter = *:Trace
# Use developer keys
# 0 (default): Disabled, 1: Enabled
use_dev_keys =
[Debugging]
# Record frame time data, can be found in the log directory. Boolean value
record_frame_times =
# Determines whether or not yuzu will dump the ExeFS of all games it attempts to load while loading them
dump_exefs=false
# Determines whether or not yuzu will dump all NSOs it attempts to load while loading them
dump_nso=false
# Determines whether or not yuzu will save the filesystem access log.
enable_fs_access_log=false
# Enables verbose reporting services
reporting_services =
# Determines whether or not yuzu will report to the game that the emulated console is in Kiosk Mode
# false: Retail/Normal Mode (default), true: Kiosk Mode
quest_flag =
# Determines whether debug asserts should be enabled, which will throw an exception on asserts.
# false: Disabled (default), true: Enabled
use_debug_asserts =
# Determines whether unimplemented HLE service calls should be automatically stubbed.
# false: Disabled (default), true: Enabled
use_auto_stub =
# Enables/Disables the macro JIT compiler
disable_macro_jit=false
# Determines whether to enable the GDB stub and wait for the debugger to attach before running.
# false: Disabled (default), true: Enabled
use_gdbstub=false
# The port to use for the GDB server, if it is enabled.
gdbstub_port=6543
[WebService]
# Whether or not to enable telemetry
# 0: No, 1 (default): Yes
enable_telemetry =
# URL for Web API
web_api_url = https://api.yuzu-emu.org
# Username and token for yuzu Web Service
# See https://profile.yuzu-emu.org/ for more info
yuzu_username =
yuzu_token =
[Network]
# Name of the network interface device to use with yuzu LAN play.
# e.g. On *nix: 'enp7s0', 'wlp6s0u1u3u3', 'lo'
# e.g. On Windows: 'Ethernet', 'Wi-Fi'
network_interface =
[AddOns]
# Used to disable add-ons
# List of title IDs of games that will have add-ons disabled (separated by '|'):
title_ids =
# For each title ID, have a key/value pair called `disabled_<title_id>` equal to the names of the add-ons to disable (sep. by '|')
# e.x. disabled_0100000000010000 = Update|DLC <- disables Updates and DLC on Super Mario Odyssey
)";
} // namespace DefaultINI

View File

@ -9,6 +9,7 @@
#include "input_common/drivers/virtual_gamepad.h"
#include "input_common/main.h"
#include "jni/emu_window/emu_window.h"
#include "jni/native.h"
void EmuWindow_Android::OnSurfaceChanged(ANativeWindow* surface) {
m_window_width = ANativeWindow_getWidth(surface);
@ -57,6 +58,13 @@ void EmuWindow_Android::OnRemoveNfcTag() {
m_input_subsystem->GetVirtualAmiibo()->CloseAmiibo();
}
void EmuWindow_Android::OnFrameDisplayed() {
if (!m_first_frame) {
EmulationSession::GetInstance().OnEmulationStarted();
m_first_frame = true;
}
}
EmuWindow_Android::EmuWindow_Android(InputCommon::InputSubsystem* input_subsystem,
ANativeWindow* surface,
std::shared_ptr<Common::DynamicLibrary> driver_library)

View File

@ -45,7 +45,7 @@ public:
float gyro_z, float accel_x, float accel_y, float accel_z);
void OnReadNfcTag(std::span<u8> data);
void OnRemoveNfcTag();
void OnFrameDisplayed() override {}
void OnFrameDisplayed() override;
std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override {
return {std::make_unique<GraphicsContext_Android>(m_driver_library)};
@ -61,4 +61,6 @@ private:
float m_window_height{};
std::shared_ptr<Common::DynamicLibrary> m_driver_library;
bool m_first_frame = false;
};

View File

@ -52,8 +52,8 @@
#include "core/hle/service/am/applets/applets.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/loader/loader.h"
#include "frontend_common/config.h"
#include "jni/android_common/android_common.h"
#include "jni/config.h"
#include "jni/id_cache.h"
#include "jni/native.h"
#include "video_core/renderer_base.h"
@ -199,8 +199,8 @@ bool EmulationSession::IsPaused() const {
return m_is_running && m_is_paused;
}
const Core::PerfStatsResults& EmulationSession::PerfStats() const {
std::scoped_lock m_perf_stats_lock(m_perf_stats_mutex);
const Core::PerfStatsResults& EmulationSession::PerfStats() {
m_perf_stats = m_system.GetAndResetPerfStats();
return m_perf_stats;
}
@ -247,11 +247,13 @@ void EmulationSession::ConfigureFilesystemProvider(const std::string& filepath)
}
}
void EmulationSession::InitializeSystem() {
// Initialize logging system
Common::Log::Initialize();
Common::Log::SetColorConsoleBackendEnabled(true);
Common::Log::Start();
void EmulationSession::InitializeSystem(bool reload) {
if (!reload) {
// Initialize logging system
Common::Log::Initialize();
Common::Log::SetColorConsoleBackendEnabled(true);
Common::Log::Start();
}
// Initialize filesystem.
m_system.SetFilesystem(m_vfs);
@ -370,8 +372,6 @@ void EmulationSession::RunEmulation() {
m_system.InitializeDebugger();
}
OnEmulationStarted();
while (true) {
{
[[maybe_unused]] std::unique_lock lock(m_mutex);
@ -381,11 +381,6 @@ void EmulationSession::RunEmulation() {
break;
}
}
{
// Refresh performance stats.
std::scoped_lock m_perf_stats_lock(m_perf_stats_mutex);
m_perf_stats = m_system.GetAndResetPerfStats();
}
}
}
@ -667,12 +662,13 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchReleased(JNIEnv* env, jclass c
}
}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeSystem(JNIEnv* env, jclass clazz) {
// Create the default config.ini.
Config{};
void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeSystem(JNIEnv* env, jclass clazz,
jboolean reload) {
// Initialize the emulated system.
EmulationSession::GetInstance().System().Initialize();
EmulationSession::GetInstance().InitializeSystem();
if (!reload) {
EmulationSession::GetInstance().System().Initialize();
}
EmulationSession::GetInstance().InitializeSystem(reload);
}
jint Java_org_yuzu_yuzu_1emu_NativeLibrary_defaultCPUCore(JNIEnv* env, jclass clazz) {
@ -682,17 +678,6 @@ jint Java_org_yuzu_yuzu_1emu_NativeLibrary_defaultCPUCore(JNIEnv* env, jclass cl
void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2Ljava_lang_String_2Z(
JNIEnv* env, jclass clazz, jstring j_file, jstring j_savestate, jboolean j_delete_savestate) {}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_reloadSettings(JNIEnv* env, jclass clazz) {
Config{};
}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_initGameIni(JNIEnv* env, jclass clazz,
jstring j_game_id) {
std::string_view game_id = env->GetStringUTFChars(j_game_id, 0);
env->ReleaseStringUTFChars(j_game_id, game_id.data());
}
jdoubleArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPerfStats(JNIEnv* env, jclass clazz) {
jdoubleArray j_stats = env->NewDoubleArray(4);

View File

@ -41,9 +41,9 @@ public:
void RunEmulation();
void ShutdownEmulation();
const Core::PerfStatsResults& PerfStats() const;
const Core::PerfStatsResults& PerfStats();
void ConfigureFilesystemProvider(const std::string& filepath);
void InitializeSystem();
void InitializeSystem(bool reload);
Core::SystemResultStatus InitializeEmulation(const std::string& filepath);
bool IsHandheldOnly();
@ -52,9 +52,10 @@ public:
void OnGamepadDisconnectEvent([[maybe_unused]] int index);
SoftwareKeyboard::AndroidKeyboard* SoftwareKeyboard();
static void OnEmulationStarted();
private:
static void LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, int max);
static void OnEmulationStarted();
static void OnEmulationStopped(Core::SystemResultStatus result);
private:
@ -80,6 +81,5 @@ private:
// Synchronization
std::condition_variable_any m_cv;
mutable std::mutex m_perf_stats_mutex;
mutable std::mutex m_mutex;
};

View File

@ -5,11 +5,14 @@
#include <jni.h>
#include "android_config.h"
#include "android_settings.h"
#include "common/logging/log.h"
#include "common/settings.h"
#include "frontend_common/config.h"
#include "jni/android_common/android_common.h"
#include "jni/config.h"
#include "uisettings.h"
std::unique_ptr<AndroidConfig> config;
template <typename T>
Settings::Setting<T>* getSetting(JNIEnv* env, jstring jkey) {
@ -28,6 +31,22 @@ Settings::Setting<T>* getSetting(JNIEnv* env, jstring jkey) {
extern "C" {
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_initializeConfig(JNIEnv* env, jobject obj) {
config = std::make_unique<AndroidConfig>();
}
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_unloadConfig(JNIEnv* env, jobject obj) {
config.reset();
}
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_reloadSettings(JNIEnv* env, jobject obj) {
config->AndroidConfig::ReloadAllValues();
}
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_saveSettings(JNIEnv* env, jobject obj) {
config->AndroidConfig::SaveAllValues();
}
jboolean Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getBoolean(JNIEnv* env, jobject obj,
jstring jkey, jboolean getDefault) {
auto setting = getSetting<bool>(env, jkey);

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24"
android:viewportWidth="24">
<path
android:fillColor="?attr/colorControlNormal"
android:pathData="M3,9v6h4l5,5L12,4L7,9L3,9zM16.5,12c0,-1.77 -1.02,-3.29 -2.5,-4.03v8.05c1.48,-0.73 2.5,-2.25 2.5,-4.02zM14,3.23v2.06c2.89,0.86 5,3.54 5,6.71s-2.11,5.85 -5,6.71v2.06c4.01,-0.91 7,-4.49 7,-8.77s-2.99,-7.86 -7,-8.77z" />
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="?attr/colorControlNormal"
android:pathData="M320,720 L80,480l240,-240 57,57 -184,184 183,183 -56,56ZM640,720 L583,663 767,479 584,296 640,240 880,480 640,720Z"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="?attr/colorControlNormal"
android:pathData="M160,840q-33,0 -56.5,-23.5T80,760v-560q0,-33 23.5,-56.5T160,120h560q33,0 56.5,23.5T800,200v80h80v80h-80v80h80v80h-80v80h80v80h-80v80q0,33 -23.5,56.5T720,840L160,840ZM160,760h560v-560L160,200v560ZM240,680h200v-160L240,520v160ZM480,400h160v-120L480,280v120ZM240,480h200v-200L240,280v200ZM480,680h160v-240L480,440v240ZM160,200v560,-560Z"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="?attr/colorControlNormal"
android:pathData="M320,960q-17,0 -28.5,-11.5T280,920q0,-17 11.5,-28.5T320,880q17,0 28.5,11.5T360,920q0,17 -11.5,28.5T320,960ZM480,960q-17,0 -28.5,-11.5T440,920q0,-17 11.5,-28.5T480,880q17,0 28.5,11.5T520,920q0,17 -11.5,28.5T480,960ZM640,960q-17,0 -28.5,-11.5T600,920q0,-17 11.5,-28.5T640,880q17,0 28.5,11.5T680,920q0,17 -11.5,28.5T640,960ZM320,800q-33,0 -56.5,-23.5T240,720v-640q0,-33 23.5,-56.5T320,0h320q33,0 56.5,23.5T720,80v640q0,33 -23.5,56.5T640,800L320,800ZM320,720h320v-40L320,680v40ZM320,600h320v-400L320,200v400ZM320,120h320v-40L320,80v40ZM320,120v-40,40ZM320,720v-40,40Z"/>
</vector>

View File

@ -0,0 +1,233 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/coordinator_about"
android:layout_width="match_parent"
android:layout_height="match_parent"
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">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar_about"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:navigationIcon="@drawable/ic_back"
app:title="@string/about" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.core.widget.NestedScrollView
android:id="@+id/scroll_about"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fadeScrollbars="false"
android:scrollbars="vertical"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout
android:id="@+id/content_about"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<ImageView
android:id="@+id/image_logo"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_gravity="center_horizontal"
android:padding="20dp"
android:src="@drawable/ic_yuzu_title" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingHorizontal="16dp"
android:paddingVertical="16dp">
<com.google.android.material.textview.MaterialTextView
style="@style/TextAppearance.Material3.TitleMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="24dp"
android:text="@string/about"
android:textAlignment="viewStart" />
<com.google.android.material.textview.MaterialTextView
style="@style/TextAppearance.Material3.BodyMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="24dp"
android:layout_marginTop="6dp"
android:text="@string/about_app_description"
android:textAlignment="viewStart" />
</LinearLayout>
<com.google.android.material.divider.MaterialDivider
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="20dp" />
<LinearLayout
android:id="@+id/button_contributors"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:orientation="vertical"
android:paddingHorizontal="16dp"
android:paddingVertical="16dp">
<com.google.android.material.textview.MaterialTextView
style="@style/TextAppearance.Material3.TitleMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="24dp"
android:text="@string/contributors"
android:textAlignment="viewStart" />
<com.google.android.material.textview.MaterialTextView
style="@style/TextAppearance.Material3.BodyMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="24dp"
android:layout_marginTop="6dp"
android:text="@string/contributors_description"
android:textAlignment="viewStart" />
</LinearLayout>
<com.google.android.material.divider.MaterialDivider
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="20dp" />
<LinearLayout
android:id="@+id/button_licenses"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:orientation="vertical"
android:paddingHorizontal="16dp"
android:paddingVertical="16dp">
<com.google.android.material.textview.MaterialTextView
style="@style/TextAppearance.Material3.TitleMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="24dp"
android:text="@string/licenses"
android:textAlignment="viewStart" />
<com.google.android.material.textview.MaterialTextView
style="@style/TextAppearance.Material3.BodyMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="24dp"
android:layout_marginTop="6dp"
android:text="@string/licenses_description"
android:textAlignment="viewStart" />
</LinearLayout>
<com.google.android.material.divider.MaterialDivider
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="20dp" />
<LinearLayout
android:id="@+id/button_build_hash"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:orientation="vertical"
android:paddingHorizontal="16dp"
android:paddingVertical="16dp">
<com.google.android.material.textview.MaterialTextView
style="@style/TextAppearance.Material3.TitleMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="24dp"
android:text="@string/build"
android:textAlignment="viewStart" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/text_build_hash"
style="@style/TextAppearance.Material3.BodyMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="24dp"
android:layout_marginTop="6dp"
android:textAlignment="viewStart"
tools:text="abc123" />
</LinearLayout>
<com.google.android.material.divider.MaterialDivider
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="20dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="40dp"
android:layout_marginTop="12dp"
android:layout_marginBottom="16dp"
android:gravity="center_horizontal"
android:orientation="horizontal">
<Button
android:id="@+id/button_discord"
style="?attr/materialIconButtonStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
app:icon="@drawable/ic_discord"
app:iconGravity="textEnd"
app:iconSize="24dp"
app:iconTint="?attr/colorOnSurface" />
<Button
android:id="@+id/button_website"
style="?attr/materialIconButtonStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
app:icon="@drawable/ic_website"
app:iconGravity="textEnd"
app:iconSize="24dp"
app:iconTint="?attr/colorOnSurface" />
<Button
android:id="@+id/button_github"
style="?attr/materialIconButtonStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
app:icon="@drawable/ic_github"
app:iconGravity="textEnd"
app:iconSize="24dp"
app:iconTint="?attr/colorOnSurface" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -6,8 +6,8 @@
android:id="@+id/option_card"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginVertical="12dp"
android:layout_marginHorizontal="16dp"
android:layout_marginBottom="24dp"
android:layout_marginHorizontal="12dp"
android:background="?attr/selectableItemBackground"
android:backgroundTint="?attr/colorSurfaceVariant"
android:clickable="true"

View File

@ -38,17 +38,17 @@
<ImageView
android:id="@+id/image_logo"
android:layout_width="250dp"
android:layout_height="250dp"
android:layout_marginTop="20dp"
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_marginTop="24dp"
android:layout_marginBottom="28dp"
android:layout_gravity="center_horizontal"
android:src="@drawable/ic_yuzu_title" />
<com.google.android.material.divider.MaterialDivider
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="20dp"
android:layout_marginTop="28dp" />
android:layout_marginHorizontal="20dp" />
<LinearLayout
android:layout_width="match_parent"

View File

@ -134,18 +134,21 @@
<FrameLayout
android:id="@+id/overlay_container"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<TextView
<com.google.android.material.textview.MaterialTextView
android:id="@+id/show_fps_text"
style="@style/TextAppearance.Material3.BodySmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left"
android:clickable="false"
android:focusable="false"
android:shadowColor="@android:color/black"
android:paddingHorizontal="20dp"
android:textColor="@android:color/white"
android:textSize="12sp"
android:shadowColor="@android:color/black"
android:shadowRadius="3"
tools:ignore="RtlHardcoded" />
</FrameLayout>

View File

@ -14,13 +14,14 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="?attr/colorSurface">
android:background="?attr/colorSurface"
android:paddingHorizontal="8dp">
<ImageView
android:id="@+id/logo_image"
android:layout_width="128dp"
android:layout_height="128dp"
android:layout_margin="64dp"
android:layout_width="96dp"
android:layout_height="96dp"
android:layout_marginVertical="32dp"
android:layout_gravity="center_horizontal"
android:src="@drawable/ic_yuzu_full" />

View File

@ -10,41 +10,59 @@
android:focusable="true"
android:gravity="center_vertical"
android:minHeight="72dp"
android:padding="@dimen/spacing_large">
android:padding="16dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
android:orientation="horizontal">
<com.google.android.material.textview.MaterialTextView
android:id="@+id/text_setting_name"
style="@style/TextAppearance.Material3.HeadlineMedium"
<ImageView
android:id="@+id/icon"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginStart="8dp"
android:layout_marginEnd="24dp"
android:layout_gravity="center_vertical"
android:visibility="gone"
app:tint="?attr/colorOnSurface" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAlignment="viewStart"
android:textSize="16sp"
app:lineHeight="22dp"
tools:text="Setting Name" />
android:orientation="vertical">
<com.google.android.material.textview.MaterialTextView
android:id="@+id/text_setting_description"
style="@style/TextAppearance.Material3.BodySmall"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/spacing_small"
android:textAlignment="viewStart"
tools:text="@string/app_disclaimer" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/text_setting_name"
style="@style/TextAppearance.Material3.HeadlineMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAlignment="viewStart"
android:textSize="17sp"
app:lineHeight="22dp"
tools:text="Setting Name" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/text_setting_value"
style="@style/TextAppearance.Material3.LabelMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/spacing_small"
android:textAlignment="viewStart"
android:textStyle="bold"
tools:text="1x" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/text_setting_description"
style="@style/TextAppearance.Material3.BodySmall"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/spacing_small"
android:textAlignment="viewStart"
tools:text="@string/app_disclaimer" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/text_setting_value"
style="@style/TextAppearance.Material3.LabelMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/spacing_small"
android:textAlignment="viewStart"
android:textStyle="bold"
android:textSize="13sp"
tools:text="1x" />
</LinearLayout>
</LinearLayout>

View File

@ -8,9 +8,7 @@
android:clickable="true"
android:focusable="true"
android:minHeight="72dp"
android:paddingVertical="@dimen/spacing_large"
android:paddingStart="@dimen/spacing_large"
android:paddingEnd="24dp">
android:padding="16dp">
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/switch_widget"
@ -24,7 +22,7 @@
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerVertical="true"
android:layout_marginEnd="@dimen/spacing_large"
android:layout_marginEnd="24dp"
android:layout_toStartOf="@+id/switch_widget"
android:gravity="center_vertical"
android:orientation="vertical">
@ -35,7 +33,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAlignment="viewStart"
android:textSize="16sp"
android:textSize="17sp"
app:lineHeight="28dp"
tools:text="@string/frame_limit_enable" />

View File

@ -7,7 +7,8 @@
android:layout_height="wrap_content"
android:layout_gravity="start|center_vertical"
android:paddingHorizontal="@dimen/spacing_large"
android:paddingVertical="16dp"
android:paddingTop="16dp"
android:paddingBottom="8dp"
android:textAlignment="viewStart"
android:textColor="?attr/colorPrimary"
android:textStyle="bold"

View File

@ -2,7 +2,6 @@
<resources>
<string-array name="regionNames">
<item>@string/auto</item>
<item>@string/region_australia</item>
<item>@string/region_china</item>
<item>@string/region_europe</item>
@ -13,7 +12,6 @@
</string-array>
<integer-array name="regionValues">
<item>-1</item>
<item>3</item>
<item>4</item>
<item>2</item>

View File

@ -240,6 +240,7 @@
<string name="shutting_down">Shutting down…</string>
<string name="reset_setting_confirmation">Do you want to reset this setting back to its default value?</string>
<string name="reset_to_default">Reset to default</string>
<string name="reset_to_default_description">Resets all advanced settings</string>
<string name="reset_all_settings">Reset all settings?</string>
<string name="reset_all_settings_description">All advanced settings will be reset to their default configuration. This can not be undone.</string>
<string name="settings_reset">Settings reset</string>
@ -271,10 +272,14 @@
<string name="preferences_settings">Settings</string>
<string name="preferences_general">General</string>
<string name="preferences_system">System</string>
<string name="preferences_system_description">Docked mode, region, language</string>
<string name="preferences_graphics">Graphics</string>
<string name="preferences_graphics_description">Accuracy level, resolution, shader cache</string>
<string name="preferences_audio">Audio</string>
<string name="preferences_audio_description">Output engine, volume</string>
<string name="preferences_theme">Theme and color</string>
<string name="preferences_debug">Debug</string>
<string name="preferences_debug_description">CPU/GPU debugging, graphics API, fastmem</string>
<!-- ROM loading errors -->
<string name="loader_error_encrypted">Your ROM is encrypted</string>

View File

@ -1,107 +1,107 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "audio_core/adsp/apps/opus/opus_decode_object.h"
#include "common/assert.h"
namespace AudioCore::ADSP::OpusDecoder {
namespace {
bool IsValidChannelCount(u32 channel_count) {
return channel_count == 1 || channel_count == 2;
}
} // namespace
u32 OpusDecodeObject::GetWorkBufferSize(u32 channel_count) {
if (!IsValidChannelCount(channel_count)) {
return 0;
}
return static_cast<u32>(sizeof(OpusDecodeObject)) + opus_decoder_get_size(channel_count);
}
OpusDecodeObject& OpusDecodeObject::Initialize(u64 buffer, u64 buffer2) {
auto* new_decoder = reinterpret_cast<OpusDecodeObject*>(buffer);
auto* comparison = reinterpret_cast<OpusDecodeObject*>(buffer2);
if (new_decoder->magic == DecodeObjectMagic) {
if (!new_decoder->initialized ||
(new_decoder->initialized && new_decoder->self == comparison)) {
new_decoder->state_valid = true;
}
} else {
new_decoder->initialized = false;
new_decoder->state_valid = true;
}
return *new_decoder;
}
s32 OpusDecodeObject::InitializeDecoder(u32 sample_rate, u32 channel_count) {
if (!state_valid) {
return OPUS_INVALID_STATE;
}
if (initialized) {
return OPUS_OK;
}
// Unfortunately libopus does not expose the OpusDecoder struct publicly, so we can't include
// it in this class. Nintendo does not allocate memory, which is why we have a workbuffer
// provided.
// We could use _create and have libopus allocate it for us, but then we have to separately
// track which decoder is being used between this and multistream in order to call the correct
// destroy from the host side.
// This is a bit cringe, but is safe as these objects are only ever initialized inside the given
// workbuffer, and GetWorkBufferSize will guarantee there's enough space to follow.
decoder = (LibOpusDecoder*)(this + 1);
s32 ret = opus_decoder_init(decoder, sample_rate, channel_count);
if (ret == OPUS_OK) {
magic = DecodeObjectMagic;
initialized = true;
state_valid = true;
self = this;
final_range = 0;
}
return ret;
}
s32 OpusDecodeObject::Shutdown() {
if (!state_valid) {
return OPUS_INVALID_STATE;
}
if (initialized) {
magic = 0x0;
initialized = false;
state_valid = false;
self = nullptr;
final_range = 0;
decoder = nullptr;
}
return OPUS_OK;
}
s32 OpusDecodeObject::ResetDecoder() {
return opus_decoder_ctl(decoder, OPUS_RESET_STATE);
}
s32 OpusDecodeObject::Decode(u32& out_sample_count, u64 output_data, u64 output_data_size,
u64 input_data, u64 input_data_size) {
ASSERT(initialized);
out_sample_count = 0;
if (!state_valid) {
return OPUS_INVALID_STATE;
}
auto ret_code_or_samples = opus_decode(
decoder, reinterpret_cast<const u8*>(input_data), static_cast<opus_int32>(input_data_size),
reinterpret_cast<opus_int16*>(output_data), static_cast<opus_int32>(output_data_size), 0);
if (ret_code_or_samples < OPUS_OK) {
return ret_code_or_samples;
}
out_sample_count = ret_code_or_samples;
return opus_decoder_ctl(decoder, OPUS_GET_FINAL_RANGE_REQUEST, &final_range);
}
} // namespace AudioCore::ADSP::OpusDecoder
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "audio_core/adsp/apps/opus/opus_decode_object.h"
#include "common/assert.h"
namespace AudioCore::ADSP::OpusDecoder {
namespace {
bool IsValidChannelCount(u32 channel_count) {
return channel_count == 1 || channel_count == 2;
}
} // namespace
u32 OpusDecodeObject::GetWorkBufferSize(u32 channel_count) {
if (!IsValidChannelCount(channel_count)) {
return 0;
}
return static_cast<u32>(sizeof(OpusDecodeObject)) + opus_decoder_get_size(channel_count);
}
OpusDecodeObject& OpusDecodeObject::Initialize(u64 buffer, u64 buffer2) {
auto* new_decoder = reinterpret_cast<OpusDecodeObject*>(buffer);
auto* comparison = reinterpret_cast<OpusDecodeObject*>(buffer2);
if (new_decoder->magic == DecodeObjectMagic) {
if (!new_decoder->initialized ||
(new_decoder->initialized && new_decoder->self == comparison)) {
new_decoder->state_valid = true;
}
} else {
new_decoder->initialized = false;
new_decoder->state_valid = true;
}
return *new_decoder;
}
s32 OpusDecodeObject::InitializeDecoder(u32 sample_rate, u32 channel_count) {
if (!state_valid) {
return OPUS_INVALID_STATE;
}
if (initialized) {
return OPUS_OK;
}
// Unfortunately libopus does not expose the OpusDecoder struct publicly, so we can't include
// it in this class. Nintendo does not allocate memory, which is why we have a workbuffer
// provided.
// We could use _create and have libopus allocate it for us, but then we have to separately
// track which decoder is being used between this and multistream in order to call the correct
// destroy from the host side.
// This is a bit cringe, but is safe as these objects are only ever initialized inside the given
// workbuffer, and GetWorkBufferSize will guarantee there's enough space to follow.
decoder = (LibOpusDecoder*)(this + 1);
s32 ret = opus_decoder_init(decoder, sample_rate, channel_count);
if (ret == OPUS_OK) {
magic = DecodeObjectMagic;
initialized = true;
state_valid = true;
self = this;
final_range = 0;
}
return ret;
}
s32 OpusDecodeObject::Shutdown() {
if (!state_valid) {
return OPUS_INVALID_STATE;
}
if (initialized) {
magic = 0x0;
initialized = false;
state_valid = false;
self = nullptr;
final_range = 0;
decoder = nullptr;
}
return OPUS_OK;
}
s32 OpusDecodeObject::ResetDecoder() {
return opus_decoder_ctl(decoder, OPUS_RESET_STATE);
}
s32 OpusDecodeObject::Decode(u32& out_sample_count, u64 output_data, u64 output_data_size,
u64 input_data, u64 input_data_size) {
ASSERT(initialized);
out_sample_count = 0;
if (!state_valid) {
return OPUS_INVALID_STATE;
}
auto ret_code_or_samples = opus_decode(
decoder, reinterpret_cast<const u8*>(input_data), static_cast<opus_int32>(input_data_size),
reinterpret_cast<opus_int16*>(output_data), static_cast<opus_int32>(output_data_size), 0);
if (ret_code_or_samples < OPUS_OK) {
return ret_code_or_samples;
}
out_sample_count = ret_code_or_samples;
return opus_decoder_ctl(decoder, OPUS_GET_FINAL_RANGE_REQUEST, &final_range);
}
} // namespace AudioCore::ADSP::OpusDecoder

View File

@ -30,9 +30,9 @@ bool IsValidMultiStreamChannelCount(u32 channel_count) {
return channel_count <= OpusStreamCountMax;
}
bool IsValidMultiStreamStreamCounts(s32 total_stream_count, s32 sterero_stream_count) {
bool IsValidMultiStreamStreamCounts(s32 total_stream_count, s32 stereo_stream_count) {
return IsValidMultiStreamChannelCount(total_stream_count) && total_stream_count > 0 &&
sterero_stream_count > 0 && sterero_stream_count <= total_stream_count;
stereo_stream_count >= 0 && stereo_stream_count <= total_stream_count;
}
} // namespace

View File

@ -1,111 +1,111 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "audio_core/adsp/apps/opus/opus_multistream_decode_object.h"
#include "common/assert.h"
namespace AudioCore::ADSP::OpusDecoder {
namespace {
bool IsValidChannelCount(u32 channel_count) {
return channel_count == 1 || channel_count == 2;
}
bool IsValidStreamCounts(u32 total_stream_count, u32 stereo_stream_count) {
return total_stream_count > 0 && stereo_stream_count > 0 &&
stereo_stream_count <= total_stream_count && IsValidChannelCount(total_stream_count);
}
} // namespace
u32 OpusMultiStreamDecodeObject::GetWorkBufferSize(u32 total_stream_count,
u32 stereo_stream_count) {
if (IsValidStreamCounts(total_stream_count, stereo_stream_count)) {
return static_cast<u32>(sizeof(OpusMultiStreamDecodeObject)) +
opus_multistream_decoder_get_size(total_stream_count, stereo_stream_count);
}
return 0;
}
OpusMultiStreamDecodeObject& OpusMultiStreamDecodeObject::Initialize(u64 buffer, u64 buffer2) {
auto* new_decoder = reinterpret_cast<OpusMultiStreamDecodeObject*>(buffer);
auto* comparison = reinterpret_cast<OpusMultiStreamDecodeObject*>(buffer2);
if (new_decoder->magic == DecodeMultiStreamObjectMagic) {
if (!new_decoder->initialized ||
(new_decoder->initialized && new_decoder->self == comparison)) {
new_decoder->state_valid = true;
}
} else {
new_decoder->initialized = false;
new_decoder->state_valid = true;
}
return *new_decoder;
}
s32 OpusMultiStreamDecodeObject::InitializeDecoder(u32 sample_rate, u32 total_stream_count,
u32 channel_count, u32 stereo_stream_count,
u8* mappings) {
if (!state_valid) {
return OPUS_INVALID_STATE;
}
if (initialized) {
return OPUS_OK;
}
// See OpusDecodeObject::InitializeDecoder for an explanation of this
decoder = (LibOpusMSDecoder*)(this + 1);
s32 ret = opus_multistream_decoder_init(decoder, sample_rate, channel_count, total_stream_count,
stereo_stream_count, mappings);
if (ret == OPUS_OK) {
magic = DecodeMultiStreamObjectMagic;
initialized = true;
state_valid = true;
self = this;
final_range = 0;
}
return ret;
}
s32 OpusMultiStreamDecodeObject::Shutdown() {
if (!state_valid) {
return OPUS_INVALID_STATE;
}
if (initialized) {
magic = 0x0;
initialized = false;
state_valid = false;
self = nullptr;
final_range = 0;
decoder = nullptr;
}
return OPUS_OK;
}
s32 OpusMultiStreamDecodeObject::ResetDecoder() {
return opus_multistream_decoder_ctl(decoder, OPUS_RESET_STATE);
}
s32 OpusMultiStreamDecodeObject::Decode(u32& out_sample_count, u64 output_data,
u64 output_data_size, u64 input_data, u64 input_data_size) {
ASSERT(initialized);
out_sample_count = 0;
if (!state_valid) {
return OPUS_INVALID_STATE;
}
auto ret_code_or_samples = opus_multistream_decode(
decoder, reinterpret_cast<const u8*>(input_data), static_cast<opus_int32>(input_data_size),
reinterpret_cast<opus_int16*>(output_data), static_cast<opus_int32>(output_data_size), 0);
if (ret_code_or_samples < OPUS_OK) {
return ret_code_or_samples;
}
out_sample_count = ret_code_or_samples;
return opus_multistream_decoder_ctl(decoder, OPUS_GET_FINAL_RANGE_REQUEST, &final_range);
}
} // namespace AudioCore::ADSP::OpusDecoder
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "audio_core/adsp/apps/opus/opus_multistream_decode_object.h"
#include "common/assert.h"
namespace AudioCore::ADSP::OpusDecoder {
namespace {
bool IsValidChannelCount(u32 channel_count) {
return channel_count == 1 || channel_count == 2;
}
bool IsValidStreamCounts(u32 total_stream_count, u32 stereo_stream_count) {
return total_stream_count > 0 && stereo_stream_count > 0 &&
stereo_stream_count <= total_stream_count && IsValidChannelCount(total_stream_count);
}
} // namespace
u32 OpusMultiStreamDecodeObject::GetWorkBufferSize(u32 total_stream_count,
u32 stereo_stream_count) {
if (IsValidStreamCounts(total_stream_count, stereo_stream_count)) {
return static_cast<u32>(sizeof(OpusMultiStreamDecodeObject)) +
opus_multistream_decoder_get_size(total_stream_count, stereo_stream_count);
}
return 0;
}
OpusMultiStreamDecodeObject& OpusMultiStreamDecodeObject::Initialize(u64 buffer, u64 buffer2) {
auto* new_decoder = reinterpret_cast<OpusMultiStreamDecodeObject*>(buffer);
auto* comparison = reinterpret_cast<OpusMultiStreamDecodeObject*>(buffer2);
if (new_decoder->magic == DecodeMultiStreamObjectMagic) {
if (!new_decoder->initialized ||
(new_decoder->initialized && new_decoder->self == comparison)) {
new_decoder->state_valid = true;
}
} else {
new_decoder->initialized = false;
new_decoder->state_valid = true;
}
return *new_decoder;
}
s32 OpusMultiStreamDecodeObject::InitializeDecoder(u32 sample_rate, u32 total_stream_count,
u32 channel_count, u32 stereo_stream_count,
u8* mappings) {
if (!state_valid) {
return OPUS_INVALID_STATE;
}
if (initialized) {
return OPUS_OK;
}
// See OpusDecodeObject::InitializeDecoder for an explanation of this
decoder = (LibOpusMSDecoder*)(this + 1);
s32 ret = opus_multistream_decoder_init(decoder, sample_rate, channel_count, total_stream_count,
stereo_stream_count, mappings);
if (ret == OPUS_OK) {
magic = DecodeMultiStreamObjectMagic;
initialized = true;
state_valid = true;
self = this;
final_range = 0;
}
return ret;
}
s32 OpusMultiStreamDecodeObject::Shutdown() {
if (!state_valid) {
return OPUS_INVALID_STATE;
}
if (initialized) {
magic = 0x0;
initialized = false;
state_valid = false;
self = nullptr;
final_range = 0;
decoder = nullptr;
}
return OPUS_OK;
}
s32 OpusMultiStreamDecodeObject::ResetDecoder() {
return opus_multistream_decoder_ctl(decoder, OPUS_RESET_STATE);
}
s32 OpusMultiStreamDecodeObject::Decode(u32& out_sample_count, u64 output_data,
u64 output_data_size, u64 input_data, u64 input_data_size) {
ASSERT(initialized);
out_sample_count = 0;
if (!state_valid) {
return OPUS_INVALID_STATE;
}
auto ret_code_or_samples = opus_multistream_decode(
decoder, reinterpret_cast<const u8*>(input_data), static_cast<opus_int32>(input_data_size),
reinterpret_cast<opus_int16*>(output_data), static_cast<opus_int32>(output_data_size), 0);
if (ret_code_or_samples < OPUS_OK) {
return ret_code_or_samples;
}
out_sample_count = ret_code_or_samples;
return opus_multistream_decoder_ctl(decoder, OPUS_GET_FINAL_RANGE_REQUEST, &final_range);
}
} // namespace AudioCore::ADSP::OpusDecoder

View File

@ -1,179 +1,179 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "audio_core/opus/decoder.h"
#include "audio_core/opus/hardware_opus.h"
#include "audio_core/opus/parameters.h"
#include "common/alignment.h"
#include "common/swap.h"
#include "core/core.h"
namespace AudioCore::OpusDecoder {
using namespace Service::Audio;
namespace {
OpusPacketHeader ReverseHeader(OpusPacketHeader header) {
OpusPacketHeader out;
out.size = Common::swap32(header.size);
out.final_range = Common::swap32(header.final_range);
return out;
}
} // namespace
OpusDecoder::OpusDecoder(Core::System& system_, HardwareOpus& hardware_opus_)
: system{system_}, hardware_opus{hardware_opus_} {}
OpusDecoder::~OpusDecoder() {
if (decode_object_initialized) {
hardware_opus.ShutdownDecodeObject(shared_buffer.get(), shared_buffer_size);
}
}
Result OpusDecoder::Initialize(OpusParametersEx& params, Kernel::KTransferMemory* transfer_memory,
u64 transfer_memory_size) {
auto frame_size{params.use_large_frame_size ? 5760 : 1920};
shared_buffer_size = transfer_memory_size;
shared_buffer = std::make_unique<u8[]>(shared_buffer_size);
shared_memory_mapped = true;
buffer_size =
Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 16);
out_data = {shared_buffer.get() + shared_buffer_size - buffer_size, buffer_size};
size_t in_data_size{0x600u};
in_data = {out_data.data() - in_data_size, in_data_size};
ON_RESULT_FAILURE {
if (shared_memory_mapped) {
shared_memory_mapped = false;
ASSERT(R_SUCCEEDED(hardware_opus.UnmapMemory(shared_buffer.get(), shared_buffer_size)));
}
};
R_TRY(hardware_opus.InitializeDecodeObject(params.sample_rate, params.channel_count,
shared_buffer.get(), shared_buffer_size));
sample_rate = params.sample_rate;
channel_count = params.channel_count;
use_large_frame_size = params.use_large_frame_size;
decode_object_initialized = true;
R_SUCCEED();
}
Result OpusDecoder::Initialize(OpusMultiStreamParametersEx& params,
Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size) {
auto frame_size{params.use_large_frame_size ? 5760 : 1920};
shared_buffer_size = transfer_memory_size;
shared_buffer = std::make_unique<u8[]>(shared_buffer_size);
shared_memory_mapped = true;
buffer_size =
Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 16);
out_data = {shared_buffer.get() + shared_buffer_size - buffer_size, buffer_size};
size_t in_data_size{Common::AlignUp(1500ull * params.total_stream_count, 64u)};
in_data = {out_data.data() - in_data_size, in_data_size};
ON_RESULT_FAILURE {
if (shared_memory_mapped) {
shared_memory_mapped = false;
ASSERT(R_SUCCEEDED(hardware_opus.UnmapMemory(shared_buffer.get(), shared_buffer_size)));
}
};
R_TRY(hardware_opus.InitializeMultiStreamDecodeObject(
params.sample_rate, params.channel_count, params.total_stream_count,
params.stereo_stream_count, params.mappings.data(), shared_buffer.get(),
shared_buffer_size));
sample_rate = params.sample_rate;
channel_count = params.channel_count;
total_stream_count = params.total_stream_count;
stereo_stream_count = params.stereo_stream_count;
use_large_frame_size = params.use_large_frame_size;
decode_object_initialized = true;
R_SUCCEED();
}
Result OpusDecoder::DecodeInterleaved(u32* out_data_size, u64* out_time_taken,
u32* out_sample_count, std::span<const u8> input_data,
std::span<u8> output_data, bool reset) {
u32 out_samples;
u64 time_taken{};
R_UNLESS(input_data.size_bytes() > sizeof(OpusPacketHeader), ResultInputDataTooSmall);
auto* header_p{reinterpret_cast<const OpusPacketHeader*>(input_data.data())};
OpusPacketHeader header{ReverseHeader(*header_p)};
R_UNLESS(in_data.size_bytes() >= header.size &&
header.size + sizeof(OpusPacketHeader) <= input_data.size_bytes(),
ResultBufferTooSmall);
if (!shared_memory_mapped) {
R_TRY(hardware_opus.MapMemory(shared_buffer.get(), shared_buffer_size));
shared_memory_mapped = true;
}
std::memcpy(in_data.data(), input_data.data() + sizeof(OpusPacketHeader), header.size);
R_TRY(hardware_opus.DecodeInterleaved(out_samples, out_data.data(), out_data.size_bytes(),
channel_count, in_data.data(), header.size,
shared_buffer.get(), time_taken, reset));
std::memcpy(output_data.data(), out_data.data(), out_samples * channel_count * sizeof(s16));
*out_data_size = header.size + sizeof(OpusPacketHeader);
*out_sample_count = out_samples;
if (out_time_taken) {
*out_time_taken = time_taken / 1000;
}
R_SUCCEED();
}
Result OpusDecoder::SetContext([[maybe_unused]] std::span<const u8> context) {
R_SUCCEED_IF(shared_memory_mapped);
shared_memory_mapped = true;
R_RETURN(hardware_opus.MapMemory(shared_buffer.get(), shared_buffer_size));
}
Result OpusDecoder::DecodeInterleavedForMultiStream(u32* out_data_size, u64* out_time_taken,
u32* out_sample_count,
std::span<const u8> input_data,
std::span<u8> output_data, bool reset) {
u32 out_samples;
u64 time_taken{};
R_UNLESS(input_data.size_bytes() > sizeof(OpusPacketHeader), ResultInputDataTooSmall);
auto* header_p{reinterpret_cast<const OpusPacketHeader*>(input_data.data())};
OpusPacketHeader header{ReverseHeader(*header_p)};
LOG_ERROR(Service_Audio, "header size 0x{:X} input data size 0x{:X} in_data size 0x{:X}",
header.size, input_data.size_bytes(), in_data.size_bytes());
R_UNLESS(in_data.size_bytes() >= header.size &&
header.size + sizeof(OpusPacketHeader) <= input_data.size_bytes(),
ResultBufferTooSmall);
if (!shared_memory_mapped) {
R_TRY(hardware_opus.MapMemory(shared_buffer.get(), shared_buffer_size));
shared_memory_mapped = true;
}
std::memcpy(in_data.data(), input_data.data() + sizeof(OpusPacketHeader), header.size);
R_TRY(hardware_opus.DecodeInterleavedForMultiStream(
out_samples, out_data.data(), out_data.size_bytes(), channel_count, in_data.data(),
header.size, shared_buffer.get(), time_taken, reset));
std::memcpy(output_data.data(), out_data.data(), out_samples * channel_count * sizeof(s16));
*out_data_size = header.size + sizeof(OpusPacketHeader);
*out_sample_count = out_samples;
if (out_time_taken) {
*out_time_taken = time_taken / 1000;
}
R_SUCCEED();
}
} // namespace AudioCore::OpusDecoder
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "audio_core/opus/decoder.h"
#include "audio_core/opus/hardware_opus.h"
#include "audio_core/opus/parameters.h"
#include "common/alignment.h"
#include "common/swap.h"
#include "core/core.h"
namespace AudioCore::OpusDecoder {
using namespace Service::Audio;
namespace {
OpusPacketHeader ReverseHeader(OpusPacketHeader header) {
OpusPacketHeader out;
out.size = Common::swap32(header.size);
out.final_range = Common::swap32(header.final_range);
return out;
}
} // namespace
OpusDecoder::OpusDecoder(Core::System& system_, HardwareOpus& hardware_opus_)
: system{system_}, hardware_opus{hardware_opus_} {}
OpusDecoder::~OpusDecoder() {
if (decode_object_initialized) {
hardware_opus.ShutdownDecodeObject(shared_buffer.get(), shared_buffer_size);
}
}
Result OpusDecoder::Initialize(OpusParametersEx& params, Kernel::KTransferMemory* transfer_memory,
u64 transfer_memory_size) {
auto frame_size{params.use_large_frame_size ? 5760 : 1920};
shared_buffer_size = transfer_memory_size;
shared_buffer = std::make_unique<u8[]>(shared_buffer_size);
shared_memory_mapped = true;
buffer_size =
Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 16);
out_data = {shared_buffer.get() + shared_buffer_size - buffer_size, buffer_size};
size_t in_data_size{0x600u};
in_data = {out_data.data() - in_data_size, in_data_size};
ON_RESULT_FAILURE {
if (shared_memory_mapped) {
shared_memory_mapped = false;
ASSERT(R_SUCCEEDED(hardware_opus.UnmapMemory(shared_buffer.get(), shared_buffer_size)));
}
};
R_TRY(hardware_opus.InitializeDecodeObject(params.sample_rate, params.channel_count,
shared_buffer.get(), shared_buffer_size));
sample_rate = params.sample_rate;
channel_count = params.channel_count;
use_large_frame_size = params.use_large_frame_size;
decode_object_initialized = true;
R_SUCCEED();
}
Result OpusDecoder::Initialize(OpusMultiStreamParametersEx& params,
Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size) {
auto frame_size{params.use_large_frame_size ? 5760 : 1920};
shared_buffer_size = transfer_memory_size;
shared_buffer = std::make_unique<u8[]>(shared_buffer_size);
shared_memory_mapped = true;
buffer_size =
Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 16);
out_data = {shared_buffer.get() + shared_buffer_size - buffer_size, buffer_size};
size_t in_data_size{Common::AlignUp(1500ull * params.total_stream_count, 64u)};
in_data = {out_data.data() - in_data_size, in_data_size};
ON_RESULT_FAILURE {
if (shared_memory_mapped) {
shared_memory_mapped = false;
ASSERT(R_SUCCEEDED(hardware_opus.UnmapMemory(shared_buffer.get(), shared_buffer_size)));
}
};
R_TRY(hardware_opus.InitializeMultiStreamDecodeObject(
params.sample_rate, params.channel_count, params.total_stream_count,
params.stereo_stream_count, params.mappings.data(), shared_buffer.get(),
shared_buffer_size));
sample_rate = params.sample_rate;
channel_count = params.channel_count;
total_stream_count = params.total_stream_count;
stereo_stream_count = params.stereo_stream_count;
use_large_frame_size = params.use_large_frame_size;
decode_object_initialized = true;
R_SUCCEED();
}
Result OpusDecoder::DecodeInterleaved(u32* out_data_size, u64* out_time_taken,
u32* out_sample_count, std::span<const u8> input_data,
std::span<u8> output_data, bool reset) {
u32 out_samples;
u64 time_taken{};
R_UNLESS(input_data.size_bytes() > sizeof(OpusPacketHeader), ResultInputDataTooSmall);
auto* header_p{reinterpret_cast<const OpusPacketHeader*>(input_data.data())};
OpusPacketHeader header{ReverseHeader(*header_p)};
R_UNLESS(in_data.size_bytes() >= header.size &&
header.size + sizeof(OpusPacketHeader) <= input_data.size_bytes(),
ResultBufferTooSmall);
if (!shared_memory_mapped) {
R_TRY(hardware_opus.MapMemory(shared_buffer.get(), shared_buffer_size));
shared_memory_mapped = true;
}
std::memcpy(in_data.data(), input_data.data() + sizeof(OpusPacketHeader), header.size);
R_TRY(hardware_opus.DecodeInterleaved(out_samples, out_data.data(), out_data.size_bytes(),
channel_count, in_data.data(), header.size,
shared_buffer.get(), time_taken, reset));
std::memcpy(output_data.data(), out_data.data(), out_samples * channel_count * sizeof(s16));
*out_data_size = header.size + sizeof(OpusPacketHeader);
*out_sample_count = out_samples;
if (out_time_taken) {
*out_time_taken = time_taken / 1000;
}
R_SUCCEED();
}
Result OpusDecoder::SetContext([[maybe_unused]] std::span<const u8> context) {
R_SUCCEED_IF(shared_memory_mapped);
shared_memory_mapped = true;
R_RETURN(hardware_opus.MapMemory(shared_buffer.get(), shared_buffer_size));
}
Result OpusDecoder::DecodeInterleavedForMultiStream(u32* out_data_size, u64* out_time_taken,
u32* out_sample_count,
std::span<const u8> input_data,
std::span<u8> output_data, bool reset) {
u32 out_samples;
u64 time_taken{};
R_UNLESS(input_data.size_bytes() > sizeof(OpusPacketHeader), ResultInputDataTooSmall);
auto* header_p{reinterpret_cast<const OpusPacketHeader*>(input_data.data())};
OpusPacketHeader header{ReverseHeader(*header_p)};
LOG_ERROR(Service_Audio, "header size 0x{:X} input data size 0x{:X} in_data size 0x{:X}",
header.size, input_data.size_bytes(), in_data.size_bytes());
R_UNLESS(in_data.size_bytes() >= header.size &&
header.size + sizeof(OpusPacketHeader) <= input_data.size_bytes(),
ResultBufferTooSmall);
if (!shared_memory_mapped) {
R_TRY(hardware_opus.MapMemory(shared_buffer.get(), shared_buffer_size));
shared_memory_mapped = true;
}
std::memcpy(in_data.data(), input_data.data() + sizeof(OpusPacketHeader), header.size);
R_TRY(hardware_opus.DecodeInterleavedForMultiStream(
out_samples, out_data.data(), out_data.size_bytes(), channel_count, in_data.data(),
header.size, shared_buffer.get(), time_taken, reset));
std::memcpy(output_data.data(), out_data.data(), out_samples * channel_count * sizeof(s16));
*out_data_size = header.size + sizeof(OpusPacketHeader);
*out_sample_count = out_samples;
if (out_time_taken) {
*out_time_taken = time_taken / 1000;
}
R_SUCCEED();
}
} // namespace AudioCore::OpusDecoder

View File

@ -1,53 +1,53 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <span>
#include "audio_core/opus/parameters.h"
#include "common/common_types.h"
#include "core/hle/kernel/k_transfer_memory.h"
#include "core/hle/service/audio/errors.h"
namespace Core {
class System;
}
namespace AudioCore::OpusDecoder {
class HardwareOpus;
class OpusDecoder {
public:
explicit OpusDecoder(Core::System& system, HardwareOpus& hardware_opus_);
~OpusDecoder();
Result Initialize(OpusParametersEx& params, Kernel::KTransferMemory* transfer_memory,
u64 transfer_memory_size);
Result Initialize(OpusMultiStreamParametersEx& params, Kernel::KTransferMemory* transfer_memory,
u64 transfer_memory_size);
Result DecodeInterleaved(u32* out_data_size, u64* out_time_taken, u32* out_sample_count,
std::span<const u8> input_data, std::span<u8> output_data, bool reset);
Result SetContext([[maybe_unused]] std::span<const u8> context);
Result DecodeInterleavedForMultiStream(u32* out_data_size, u64* out_time_taken,
u32* out_sample_count, std::span<const u8> input_data,
std::span<u8> output_data, bool reset);
private:
Core::System& system;
HardwareOpus& hardware_opus;
std::unique_ptr<u8[]> shared_buffer{};
u64 shared_buffer_size;
std::span<u8> in_data{};
std::span<u8> out_data{};
u64 buffer_size{};
s32 sample_rate{};
s32 channel_count{};
bool use_large_frame_size{false};
s32 total_stream_count{};
s32 stereo_stream_count{};
bool shared_memory_mapped{false};
bool decode_object_initialized{false};
};
} // namespace AudioCore::OpusDecoder
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <span>
#include "audio_core/opus/parameters.h"
#include "common/common_types.h"
#include "core/hle/kernel/k_transfer_memory.h"
#include "core/hle/service/audio/errors.h"
namespace Core {
class System;
}
namespace AudioCore::OpusDecoder {
class HardwareOpus;
class OpusDecoder {
public:
explicit OpusDecoder(Core::System& system, HardwareOpus& hardware_opus_);
~OpusDecoder();
Result Initialize(OpusParametersEx& params, Kernel::KTransferMemory* transfer_memory,
u64 transfer_memory_size);
Result Initialize(OpusMultiStreamParametersEx& params, Kernel::KTransferMemory* transfer_memory,
u64 transfer_memory_size);
Result DecodeInterleaved(u32* out_data_size, u64* out_time_taken, u32* out_sample_count,
std::span<const u8> input_data, std::span<u8> output_data, bool reset);
Result SetContext([[maybe_unused]] std::span<const u8> context);
Result DecodeInterleavedForMultiStream(u32* out_data_size, u64* out_time_taken,
u32* out_sample_count, std::span<const u8> input_data,
std::span<u8> output_data, bool reset);
private:
Core::System& system;
HardwareOpus& hardware_opus;
std::unique_ptr<u8[]> shared_buffer{};
u64 shared_buffer_size;
std::span<u8> in_data{};
std::span<u8> out_data{};
u64 buffer_size{};
s32 sample_rate{};
s32 channel_count{};
bool use_large_frame_size{false};
s32 total_stream_count{};
s32 stereo_stream_count{};
bool shared_memory_mapped{false};
bool decode_object_initialized{false};
};
} // namespace AudioCore::OpusDecoder

View File

@ -1,102 +1,102 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "audio_core/adsp/apps/opus/opus_decoder.h"
#include "audio_core/opus/decoder_manager.h"
#include "common/alignment.h"
#include "core/core.h"
namespace AudioCore::OpusDecoder {
using namespace Service::Audio;
namespace {
bool IsValidChannelCount(u32 channel_count) {
return channel_count == 1 || channel_count == 2;
}
bool IsValidMultiStreamChannelCount(u32 channel_count) {
return channel_count > 0 && channel_count <= OpusStreamCountMax;
}
bool IsValidSampleRate(u32 sample_rate) {
return sample_rate == 8'000 || sample_rate == 12'000 || sample_rate == 16'000 ||
sample_rate == 24'000 || sample_rate == 48'000;
}
bool IsValidStreamCount(u32 channel_count, u32 total_stream_count, u32 stereo_stream_count) {
return total_stream_count > 0 && stereo_stream_count > 0 &&
stereo_stream_count <= total_stream_count &&
total_stream_count + stereo_stream_count <= channel_count;
}
} // namespace
OpusDecoderManager::OpusDecoderManager(Core::System& system_)
: system{system_}, hardware_opus{system} {
for (u32 i = 0; i < MaxChannels; i++) {
required_workbuffer_sizes[i] = hardware_opus.GetWorkBufferSize(1 + i);
}
}
Result OpusDecoderManager::GetWorkBufferSize(OpusParameters& params, u64& out_size) {
OpusParametersEx ex{
.sample_rate = params.sample_rate,
.channel_count = params.channel_count,
.use_large_frame_size = false,
};
R_RETURN(GetWorkBufferSizeExEx(ex, out_size));
}
Result OpusDecoderManager::GetWorkBufferSizeEx(OpusParametersEx& params, u64& out_size) {
R_RETURN(GetWorkBufferSizeExEx(params, out_size));
}
Result OpusDecoderManager::GetWorkBufferSizeExEx(OpusParametersEx& params, u64& out_size) {
R_UNLESS(IsValidChannelCount(params.channel_count), ResultInvalidOpusChannelCount);
R_UNLESS(IsValidSampleRate(params.sample_rate), ResultInvalidOpusSampleRate);
auto work_buffer_size{required_workbuffer_sizes[params.channel_count - 1]};
auto frame_size{params.use_large_frame_size ? 5760 : 1920};
work_buffer_size +=
Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 64);
out_size = work_buffer_size + 0x600;
R_SUCCEED();
}
Result OpusDecoderManager::GetWorkBufferSizeForMultiStream(OpusMultiStreamParameters& params,
u64& out_size) {
OpusMultiStreamParametersEx ex{
.sample_rate = params.sample_rate,
.channel_count = params.channel_count,
.total_stream_count = params.total_stream_count,
.stereo_stream_count = params.stereo_stream_count,
.use_large_frame_size = false,
.mappings = {},
};
R_RETURN(GetWorkBufferSizeForMultiStreamExEx(ex, out_size));
}
Result OpusDecoderManager::GetWorkBufferSizeForMultiStreamEx(OpusMultiStreamParametersEx& params,
u64& out_size) {
R_RETURN(GetWorkBufferSizeForMultiStreamExEx(params, out_size));
}
Result OpusDecoderManager::GetWorkBufferSizeForMultiStreamExEx(OpusMultiStreamParametersEx& params,
u64& out_size) {
R_UNLESS(IsValidMultiStreamChannelCount(params.channel_count), ResultInvalidOpusChannelCount);
R_UNLESS(IsValidSampleRate(params.sample_rate), ResultInvalidOpusSampleRate);
R_UNLESS(IsValidStreamCount(params.channel_count, params.total_stream_count,
params.stereo_stream_count),
ResultInvalidOpusSampleRate);
auto work_buffer_size{hardware_opus.GetWorkBufferSizeForMultiStream(
params.total_stream_count, params.stereo_stream_count)};
auto frame_size{params.use_large_frame_size ? 5760 : 1920};
work_buffer_size += Common::AlignUp(1500 * params.total_stream_count, 64);
work_buffer_size +=
Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 64);
out_size = work_buffer_size;
R_SUCCEED();
}
} // namespace AudioCore::OpusDecoder
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "audio_core/adsp/apps/opus/opus_decoder.h"
#include "audio_core/opus/decoder_manager.h"
#include "common/alignment.h"
#include "core/core.h"
namespace AudioCore::OpusDecoder {
using namespace Service::Audio;
namespace {
bool IsValidChannelCount(u32 channel_count) {
return channel_count == 1 || channel_count == 2;
}
bool IsValidMultiStreamChannelCount(u32 channel_count) {
return channel_count > 0 && channel_count <= OpusStreamCountMax;
}
bool IsValidSampleRate(u32 sample_rate) {
return sample_rate == 8'000 || sample_rate == 12'000 || sample_rate == 16'000 ||
sample_rate == 24'000 || sample_rate == 48'000;
}
bool IsValidStreamCount(u32 channel_count, u32 total_stream_count, u32 stereo_stream_count) {
return total_stream_count > 0 && static_cast<s32>(stereo_stream_count) >= 0 &&
stereo_stream_count <= total_stream_count &&
total_stream_count + stereo_stream_count <= channel_count;
}
} // namespace
OpusDecoderManager::OpusDecoderManager(Core::System& system_)
: system{system_}, hardware_opus{system} {
for (u32 i = 0; i < MaxChannels; i++) {
required_workbuffer_sizes[i] = hardware_opus.GetWorkBufferSize(1 + i);
}
}
Result OpusDecoderManager::GetWorkBufferSize(OpusParameters& params, u64& out_size) {
OpusParametersEx ex{
.sample_rate = params.sample_rate,
.channel_count = params.channel_count,
.use_large_frame_size = false,
};
R_RETURN(GetWorkBufferSizeExEx(ex, out_size));
}
Result OpusDecoderManager::GetWorkBufferSizeEx(OpusParametersEx& params, u64& out_size) {
R_RETURN(GetWorkBufferSizeExEx(params, out_size));
}
Result OpusDecoderManager::GetWorkBufferSizeExEx(OpusParametersEx& params, u64& out_size) {
R_UNLESS(IsValidChannelCount(params.channel_count), ResultInvalidOpusChannelCount);
R_UNLESS(IsValidSampleRate(params.sample_rate), ResultInvalidOpusSampleRate);
auto work_buffer_size{required_workbuffer_sizes[params.channel_count - 1]};
auto frame_size{params.use_large_frame_size ? 5760 : 1920};
work_buffer_size +=
Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 64);
out_size = work_buffer_size + 0x600;
R_SUCCEED();
}
Result OpusDecoderManager::GetWorkBufferSizeForMultiStream(OpusMultiStreamParameters& params,
u64& out_size) {
OpusMultiStreamParametersEx ex{
.sample_rate = params.sample_rate,
.channel_count = params.channel_count,
.total_stream_count = params.total_stream_count,
.stereo_stream_count = params.stereo_stream_count,
.use_large_frame_size = false,
.mappings = {},
};
R_RETURN(GetWorkBufferSizeForMultiStreamExEx(ex, out_size));
}
Result OpusDecoderManager::GetWorkBufferSizeForMultiStreamEx(OpusMultiStreamParametersEx& params,
u64& out_size) {
R_RETURN(GetWorkBufferSizeForMultiStreamExEx(params, out_size));
}
Result OpusDecoderManager::GetWorkBufferSizeForMultiStreamExEx(OpusMultiStreamParametersEx& params,
u64& out_size) {
R_UNLESS(IsValidMultiStreamChannelCount(params.channel_count), ResultInvalidOpusChannelCount);
R_UNLESS(IsValidSampleRate(params.sample_rate), ResultInvalidOpusSampleRate);
R_UNLESS(IsValidStreamCount(params.channel_count, params.total_stream_count,
params.stereo_stream_count),
ResultInvalidOpusSampleRate);
auto work_buffer_size{hardware_opus.GetWorkBufferSizeForMultiStream(
params.total_stream_count, params.stereo_stream_count)};
auto frame_size{params.use_large_frame_size ? 5760 : 1920};
work_buffer_size += Common::AlignUp(1500 * params.total_stream_count, 64);
work_buffer_size +=
Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 64);
out_size = work_buffer_size;
R_SUCCEED();
}
} // namespace AudioCore::OpusDecoder

View File

@ -1,38 +1,38 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "audio_core/opus/hardware_opus.h"
#include "audio_core/opus/parameters.h"
#include "common/common_types.h"
#include "core/hle/service/audio/errors.h"
namespace Core {
class System;
}
namespace AudioCore::OpusDecoder {
class OpusDecoderManager {
public:
OpusDecoderManager(Core::System& system);
HardwareOpus& GetHardwareOpus() {
return hardware_opus;
}
Result GetWorkBufferSize(OpusParameters& params, u64& out_size);
Result GetWorkBufferSizeEx(OpusParametersEx& params, u64& out_size);
Result GetWorkBufferSizeExEx(OpusParametersEx& params, u64& out_size);
Result GetWorkBufferSizeForMultiStream(OpusMultiStreamParameters& params, u64& out_size);
Result GetWorkBufferSizeForMultiStreamEx(OpusMultiStreamParametersEx& params, u64& out_size);
Result GetWorkBufferSizeForMultiStreamExEx(OpusMultiStreamParametersEx& params, u64& out_size);
private:
Core::System& system;
HardwareOpus hardware_opus;
std::array<u64, MaxChannels> required_workbuffer_sizes{};
};
} // namespace AudioCore::OpusDecoder
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "audio_core/opus/hardware_opus.h"
#include "audio_core/opus/parameters.h"
#include "common/common_types.h"
#include "core/hle/service/audio/errors.h"
namespace Core {
class System;
}
namespace AudioCore::OpusDecoder {
class OpusDecoderManager {
public:
OpusDecoderManager(Core::System& system);
HardwareOpus& GetHardwareOpus() {
return hardware_opus;
}
Result GetWorkBufferSize(OpusParameters& params, u64& out_size);
Result GetWorkBufferSizeEx(OpusParametersEx& params, u64& out_size);
Result GetWorkBufferSizeExEx(OpusParametersEx& params, u64& out_size);
Result GetWorkBufferSizeForMultiStream(OpusMultiStreamParameters& params, u64& out_size);
Result GetWorkBufferSizeForMultiStreamEx(OpusMultiStreamParametersEx& params, u64& out_size);
Result GetWorkBufferSizeForMultiStreamExEx(OpusMultiStreamParametersEx& params, u64& out_size);
private:
Core::System& system;
HardwareOpus hardware_opus;
std::array<u64, MaxChannels> required_workbuffer_sizes{};
};
} // namespace AudioCore::OpusDecoder

View File

@ -1,241 +1,241 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <array>
#include "audio_core/audio_core.h"
#include "audio_core/opus/hardware_opus.h"
#include "core/core.h"
namespace AudioCore::OpusDecoder {
namespace {
using namespace Service::Audio;
static constexpr Result ResultCodeFromLibOpusErrorCode(u64 error_code) {
s32 error{static_cast<s32>(error_code)};
ASSERT(error <= OPUS_OK);
switch (error) {
case OPUS_ALLOC_FAIL:
R_THROW(ResultLibOpusAllocFail);
case OPUS_INVALID_STATE:
R_THROW(ResultLibOpusInvalidState);
case OPUS_UNIMPLEMENTED:
R_THROW(ResultLibOpusUnimplemented);
case OPUS_INVALID_PACKET:
R_THROW(ResultLibOpusInvalidPacket);
case OPUS_INTERNAL_ERROR:
R_THROW(ResultLibOpusInternalError);
case OPUS_BUFFER_TOO_SMALL:
R_THROW(ResultBufferTooSmall);
case OPUS_BAD_ARG:
R_THROW(ResultLibOpusBadArg);
case OPUS_OK:
R_RETURN(ResultSuccess);
}
UNREACHABLE();
}
} // namespace
HardwareOpus::HardwareOpus(Core::System& system_)
: system{system_}, opus_decoder{system.AudioCore().ADSP().OpusDecoder()} {
opus_decoder.SetSharedMemory(shared_memory);
}
u64 HardwareOpus::GetWorkBufferSize(u32 channel) {
if (!opus_decoder.IsRunning()) {
return 0;
}
std::scoped_lock l{mutex};
shared_memory.host_send_data[0] = channel;
opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::GetWorkBufferSize);
auto msg = opus_decoder.Receive(ADSP::Direction::Host);
if (msg != ADSP::OpusDecoder::Message::GetWorkBufferSizeOK) {
LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
ADSP::OpusDecoder::Message::GetWorkBufferSizeOK, msg);
return 0;
}
return shared_memory.dsp_return_data[0];
}
u64 HardwareOpus::GetWorkBufferSizeForMultiStream(u32 total_stream_count, u32 stereo_stream_count) {
std::scoped_lock l{mutex};
shared_memory.host_send_data[0] = total_stream_count;
shared_memory.host_send_data[1] = stereo_stream_count;
opus_decoder.Send(ADSP::Direction::DSP,
ADSP::OpusDecoder::Message::GetWorkBufferSizeForMultiStream);
auto msg = opus_decoder.Receive(ADSP::Direction::Host);
if (msg != ADSP::OpusDecoder::Message::GetWorkBufferSizeForMultiStreamOK) {
LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
ADSP::OpusDecoder::Message::GetWorkBufferSizeForMultiStreamOK, msg);
return 0;
}
return shared_memory.dsp_return_data[0];
}
Result HardwareOpus::InitializeDecodeObject(u32 sample_rate, u32 channel_count, void* buffer,
u64 buffer_size) {
std::scoped_lock l{mutex};
shared_memory.host_send_data[0] = (u64)buffer;
shared_memory.host_send_data[1] = buffer_size;
shared_memory.host_send_data[2] = sample_rate;
shared_memory.host_send_data[3] = channel_count;
opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::InitializeDecodeObject);
auto msg = opus_decoder.Receive(ADSP::Direction::Host);
if (msg != ADSP::OpusDecoder::Message::InitializeDecodeObjectOK) {
LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
ADSP::OpusDecoder::Message::InitializeDecodeObjectOK, msg);
R_THROW(ResultInvalidOpusDSPReturnCode);
}
R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0]));
}
Result HardwareOpus::InitializeMultiStreamDecodeObject(u32 sample_rate, u32 channel_count,
u32 total_stream_count,
u32 stereo_stream_count, void* mappings,
void* buffer, u64 buffer_size) {
std::scoped_lock l{mutex};
shared_memory.host_send_data[0] = (u64)buffer;
shared_memory.host_send_data[1] = buffer_size;
shared_memory.host_send_data[2] = sample_rate;
shared_memory.host_send_data[3] = channel_count;
shared_memory.host_send_data[4] = total_stream_count;
shared_memory.host_send_data[5] = stereo_stream_count;
ASSERT(channel_count <= MaxChannels);
std::memcpy(shared_memory.channel_mapping.data(), mappings, channel_count * sizeof(u8));
opus_decoder.Send(ADSP::Direction::DSP,
ADSP::OpusDecoder::Message::InitializeMultiStreamDecodeObject);
auto msg = opus_decoder.Receive(ADSP::Direction::Host);
if (msg != ADSP::OpusDecoder::Message::InitializeMultiStreamDecodeObjectOK) {
LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
ADSP::OpusDecoder::Message::InitializeMultiStreamDecodeObjectOK, msg);
R_THROW(ResultInvalidOpusDSPReturnCode);
}
R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0]));
}
Result HardwareOpus::ShutdownDecodeObject(void* buffer, u64 buffer_size) {
std::scoped_lock l{mutex};
shared_memory.host_send_data[0] = (u64)buffer;
shared_memory.host_send_data[1] = buffer_size;
opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::ShutdownDecodeObject);
auto msg = opus_decoder.Receive(ADSP::Direction::Host);
ASSERT_MSG(msg == ADSP::OpusDecoder::Message::ShutdownDecodeObjectOK,
"Expected Opus shutdown code {}, got {}",
ADSP::OpusDecoder::Message::ShutdownDecodeObjectOK, msg);
R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0]));
}
Result HardwareOpus::ShutdownMultiStreamDecodeObject(void* buffer, u64 buffer_size) {
std::scoped_lock l{mutex};
shared_memory.host_send_data[0] = (u64)buffer;
shared_memory.host_send_data[1] = buffer_size;
opus_decoder.Send(ADSP::Direction::DSP,
ADSP::OpusDecoder::Message::ShutdownMultiStreamDecodeObject);
auto msg = opus_decoder.Receive(ADSP::Direction::Host);
ASSERT_MSG(msg == ADSP::OpusDecoder::Message::ShutdownMultiStreamDecodeObjectOK,
"Expected Opus shutdown code {}, got {}",
ADSP::OpusDecoder::Message::ShutdownMultiStreamDecodeObjectOK, msg);
R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0]));
}
Result HardwareOpus::DecodeInterleaved(u32& out_sample_count, void* output_data,
u64 output_data_size, u32 channel_count, void* input_data,
u64 input_data_size, void* buffer, u64& out_time_taken,
bool reset) {
std::scoped_lock l{mutex};
shared_memory.host_send_data[0] = (u64)buffer;
shared_memory.host_send_data[1] = (u64)input_data;
shared_memory.host_send_data[2] = input_data_size;
shared_memory.host_send_data[3] = (u64)output_data;
shared_memory.host_send_data[4] = output_data_size;
shared_memory.host_send_data[5] = 0;
shared_memory.host_send_data[6] = reset;
opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::DecodeInterleaved);
auto msg = opus_decoder.Receive(ADSP::Direction::Host);
if (msg != ADSP::OpusDecoder::Message::DecodeInterleavedOK) {
LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
ADSP::OpusDecoder::Message::DecodeInterleavedOK, msg);
R_THROW(ResultInvalidOpusDSPReturnCode);
}
auto error_code{static_cast<s32>(shared_memory.dsp_return_data[0])};
if (error_code == OPUS_OK) {
out_sample_count = static_cast<u32>(shared_memory.dsp_return_data[1]);
out_time_taken = 1000 * shared_memory.dsp_return_data[2];
}
R_RETURN(ResultCodeFromLibOpusErrorCode(error_code));
}
Result HardwareOpus::DecodeInterleavedForMultiStream(u32& out_sample_count, void* output_data,
u64 output_data_size, u32 channel_count,
void* input_data, u64 input_data_size,
void* buffer, u64& out_time_taken,
bool reset) {
std::scoped_lock l{mutex};
shared_memory.host_send_data[0] = (u64)buffer;
shared_memory.host_send_data[1] = (u64)input_data;
shared_memory.host_send_data[2] = input_data_size;
shared_memory.host_send_data[3] = (u64)output_data;
shared_memory.host_send_data[4] = output_data_size;
shared_memory.host_send_data[5] = 0;
shared_memory.host_send_data[6] = reset;
opus_decoder.Send(ADSP::Direction::DSP,
ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStream);
auto msg = opus_decoder.Receive(ADSP::Direction::Host);
if (msg != ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStreamOK) {
LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStreamOK, msg);
R_THROW(ResultInvalidOpusDSPReturnCode);
}
auto error_code{static_cast<s32>(shared_memory.dsp_return_data[0])};
if (error_code == OPUS_OK) {
out_sample_count = static_cast<u32>(shared_memory.dsp_return_data[1]);
out_time_taken = 1000 * shared_memory.dsp_return_data[2];
}
R_RETURN(ResultCodeFromLibOpusErrorCode(error_code));
}
Result HardwareOpus::MapMemory(void* buffer, u64 buffer_size) {
std::scoped_lock l{mutex};
shared_memory.host_send_data[0] = (u64)buffer;
shared_memory.host_send_data[1] = buffer_size;
opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::MapMemory);
auto msg = opus_decoder.Receive(ADSP::Direction::Host);
if (msg != ADSP::OpusDecoder::Message::MapMemoryOK) {
LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
ADSP::OpusDecoder::Message::MapMemoryOK, msg);
R_THROW(ResultInvalidOpusDSPReturnCode);
}
R_SUCCEED();
}
Result HardwareOpus::UnmapMemory(void* buffer, u64 buffer_size) {
std::scoped_lock l{mutex};
shared_memory.host_send_data[0] = (u64)buffer;
shared_memory.host_send_data[1] = buffer_size;
opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::UnmapMemory);
auto msg = opus_decoder.Receive(ADSP::Direction::Host);
if (msg != ADSP::OpusDecoder::Message::UnmapMemoryOK) {
LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
ADSP::OpusDecoder::Message::UnmapMemoryOK, msg);
R_THROW(ResultInvalidOpusDSPReturnCode);
}
R_SUCCEED();
}
} // namespace AudioCore::OpusDecoder
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <array>
#include "audio_core/audio_core.h"
#include "audio_core/opus/hardware_opus.h"
#include "core/core.h"
namespace AudioCore::OpusDecoder {
namespace {
using namespace Service::Audio;
static constexpr Result ResultCodeFromLibOpusErrorCode(u64 error_code) {
s32 error{static_cast<s32>(error_code)};
ASSERT(error <= OPUS_OK);
switch (error) {
case OPUS_ALLOC_FAIL:
R_THROW(ResultLibOpusAllocFail);
case OPUS_INVALID_STATE:
R_THROW(ResultLibOpusInvalidState);
case OPUS_UNIMPLEMENTED:
R_THROW(ResultLibOpusUnimplemented);
case OPUS_INVALID_PACKET:
R_THROW(ResultLibOpusInvalidPacket);
case OPUS_INTERNAL_ERROR:
R_THROW(ResultLibOpusInternalError);
case OPUS_BUFFER_TOO_SMALL:
R_THROW(ResultBufferTooSmall);
case OPUS_BAD_ARG:
R_THROW(ResultLibOpusBadArg);
case OPUS_OK:
R_RETURN(ResultSuccess);
}
UNREACHABLE();
}
} // namespace
HardwareOpus::HardwareOpus(Core::System& system_)
: system{system_}, opus_decoder{system.AudioCore().ADSP().OpusDecoder()} {
opus_decoder.SetSharedMemory(shared_memory);
}
u64 HardwareOpus::GetWorkBufferSize(u32 channel) {
if (!opus_decoder.IsRunning()) {
return 0;
}
std::scoped_lock l{mutex};
shared_memory.host_send_data[0] = channel;
opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::GetWorkBufferSize);
auto msg = opus_decoder.Receive(ADSP::Direction::Host);
if (msg != ADSP::OpusDecoder::Message::GetWorkBufferSizeOK) {
LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
ADSP::OpusDecoder::Message::GetWorkBufferSizeOK, msg);
return 0;
}
return shared_memory.dsp_return_data[0];
}
u64 HardwareOpus::GetWorkBufferSizeForMultiStream(u32 total_stream_count, u32 stereo_stream_count) {
std::scoped_lock l{mutex};
shared_memory.host_send_data[0] = total_stream_count;
shared_memory.host_send_data[1] = stereo_stream_count;
opus_decoder.Send(ADSP::Direction::DSP,
ADSP::OpusDecoder::Message::GetWorkBufferSizeForMultiStream);
auto msg = opus_decoder.Receive(ADSP::Direction::Host);
if (msg != ADSP::OpusDecoder::Message::GetWorkBufferSizeForMultiStreamOK) {
LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
ADSP::OpusDecoder::Message::GetWorkBufferSizeForMultiStreamOK, msg);
return 0;
}
return shared_memory.dsp_return_data[0];
}
Result HardwareOpus::InitializeDecodeObject(u32 sample_rate, u32 channel_count, void* buffer,
u64 buffer_size) {
std::scoped_lock l{mutex};
shared_memory.host_send_data[0] = (u64)buffer;
shared_memory.host_send_data[1] = buffer_size;
shared_memory.host_send_data[2] = sample_rate;
shared_memory.host_send_data[3] = channel_count;
opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::InitializeDecodeObject);
auto msg = opus_decoder.Receive(ADSP::Direction::Host);
if (msg != ADSP::OpusDecoder::Message::InitializeDecodeObjectOK) {
LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
ADSP::OpusDecoder::Message::InitializeDecodeObjectOK, msg);
R_THROW(ResultInvalidOpusDSPReturnCode);
}
R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0]));
}
Result HardwareOpus::InitializeMultiStreamDecodeObject(u32 sample_rate, u32 channel_count,
u32 total_stream_count,
u32 stereo_stream_count, void* mappings,
void* buffer, u64 buffer_size) {
std::scoped_lock l{mutex};
shared_memory.host_send_data[0] = (u64)buffer;
shared_memory.host_send_data[1] = buffer_size;
shared_memory.host_send_data[2] = sample_rate;
shared_memory.host_send_data[3] = channel_count;
shared_memory.host_send_data[4] = total_stream_count;
shared_memory.host_send_data[5] = stereo_stream_count;
ASSERT(channel_count <= MaxChannels);
std::memcpy(shared_memory.channel_mapping.data(), mappings, channel_count * sizeof(u8));
opus_decoder.Send(ADSP::Direction::DSP,
ADSP::OpusDecoder::Message::InitializeMultiStreamDecodeObject);
auto msg = opus_decoder.Receive(ADSP::Direction::Host);
if (msg != ADSP::OpusDecoder::Message::InitializeMultiStreamDecodeObjectOK) {
LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
ADSP::OpusDecoder::Message::InitializeMultiStreamDecodeObjectOK, msg);
R_THROW(ResultInvalidOpusDSPReturnCode);
}
R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0]));
}
Result HardwareOpus::ShutdownDecodeObject(void* buffer, u64 buffer_size) {
std::scoped_lock l{mutex};
shared_memory.host_send_data[0] = (u64)buffer;
shared_memory.host_send_data[1] = buffer_size;
opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::ShutdownDecodeObject);
auto msg = opus_decoder.Receive(ADSP::Direction::Host);
ASSERT_MSG(msg == ADSP::OpusDecoder::Message::ShutdownDecodeObjectOK,
"Expected Opus shutdown code {}, got {}",
ADSP::OpusDecoder::Message::ShutdownDecodeObjectOK, msg);
R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0]));
}
Result HardwareOpus::ShutdownMultiStreamDecodeObject(void* buffer, u64 buffer_size) {
std::scoped_lock l{mutex};
shared_memory.host_send_data[0] = (u64)buffer;
shared_memory.host_send_data[1] = buffer_size;
opus_decoder.Send(ADSP::Direction::DSP,
ADSP::OpusDecoder::Message::ShutdownMultiStreamDecodeObject);
auto msg = opus_decoder.Receive(ADSP::Direction::Host);
ASSERT_MSG(msg == ADSP::OpusDecoder::Message::ShutdownMultiStreamDecodeObjectOK,
"Expected Opus shutdown code {}, got {}",
ADSP::OpusDecoder::Message::ShutdownMultiStreamDecodeObjectOK, msg);
R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0]));
}
Result HardwareOpus::DecodeInterleaved(u32& out_sample_count, void* output_data,
u64 output_data_size, u32 channel_count, void* input_data,
u64 input_data_size, void* buffer, u64& out_time_taken,
bool reset) {
std::scoped_lock l{mutex};
shared_memory.host_send_data[0] = (u64)buffer;
shared_memory.host_send_data[1] = (u64)input_data;
shared_memory.host_send_data[2] = input_data_size;
shared_memory.host_send_data[3] = (u64)output_data;
shared_memory.host_send_data[4] = output_data_size;
shared_memory.host_send_data[5] = 0;
shared_memory.host_send_data[6] = reset;
opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::DecodeInterleaved);
auto msg = opus_decoder.Receive(ADSP::Direction::Host);
if (msg != ADSP::OpusDecoder::Message::DecodeInterleavedOK) {
LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
ADSP::OpusDecoder::Message::DecodeInterleavedOK, msg);
R_THROW(ResultInvalidOpusDSPReturnCode);
}
auto error_code{static_cast<s32>(shared_memory.dsp_return_data[0])};
if (error_code == OPUS_OK) {
out_sample_count = static_cast<u32>(shared_memory.dsp_return_data[1]);
out_time_taken = 1000 * shared_memory.dsp_return_data[2];
}
R_RETURN(ResultCodeFromLibOpusErrorCode(error_code));
}
Result HardwareOpus::DecodeInterleavedForMultiStream(u32& out_sample_count, void* output_data,
u64 output_data_size, u32 channel_count,
void* input_data, u64 input_data_size,
void* buffer, u64& out_time_taken,
bool reset) {
std::scoped_lock l{mutex};
shared_memory.host_send_data[0] = (u64)buffer;
shared_memory.host_send_data[1] = (u64)input_data;
shared_memory.host_send_data[2] = input_data_size;
shared_memory.host_send_data[3] = (u64)output_data;
shared_memory.host_send_data[4] = output_data_size;
shared_memory.host_send_data[5] = 0;
shared_memory.host_send_data[6] = reset;
opus_decoder.Send(ADSP::Direction::DSP,
ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStream);
auto msg = opus_decoder.Receive(ADSP::Direction::Host);
if (msg != ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStreamOK) {
LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStreamOK, msg);
R_THROW(ResultInvalidOpusDSPReturnCode);
}
auto error_code{static_cast<s32>(shared_memory.dsp_return_data[0])};
if (error_code == OPUS_OK) {
out_sample_count = static_cast<u32>(shared_memory.dsp_return_data[1]);
out_time_taken = 1000 * shared_memory.dsp_return_data[2];
}
R_RETURN(ResultCodeFromLibOpusErrorCode(error_code));
}
Result HardwareOpus::MapMemory(void* buffer, u64 buffer_size) {
std::scoped_lock l{mutex};
shared_memory.host_send_data[0] = (u64)buffer;
shared_memory.host_send_data[1] = buffer_size;
opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::MapMemory);
auto msg = opus_decoder.Receive(ADSP::Direction::Host);
if (msg != ADSP::OpusDecoder::Message::MapMemoryOK) {
LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
ADSP::OpusDecoder::Message::MapMemoryOK, msg);
R_THROW(ResultInvalidOpusDSPReturnCode);
}
R_SUCCEED();
}
Result HardwareOpus::UnmapMemory(void* buffer, u64 buffer_size) {
std::scoped_lock l{mutex};
shared_memory.host_send_data[0] = (u64)buffer;
shared_memory.host_send_data[1] = buffer_size;
opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::UnmapMemory);
auto msg = opus_decoder.Receive(ADSP::Direction::Host);
if (msg != ADSP::OpusDecoder::Message::UnmapMemoryOK) {
LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
ADSP::OpusDecoder::Message::UnmapMemoryOK, msg);
R_THROW(ResultInvalidOpusDSPReturnCode);
}
R_SUCCEED();
}
} // namespace AudioCore::OpusDecoder

View File

@ -1,45 +1,45 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <mutex>
#include <opus.h>
#include "audio_core/adsp/apps/opus/opus_decoder.h"
#include "audio_core/adsp/apps/opus/shared_memory.h"
#include "audio_core/adsp/mailbox.h"
#include "core/hle/service/audio/errors.h"
namespace AudioCore::OpusDecoder {
class HardwareOpus {
public:
HardwareOpus(Core::System& system);
u64 GetWorkBufferSize(u32 channel);
u64 GetWorkBufferSizeForMultiStream(u32 total_stream_count, u32 stereo_stream_count);
Result InitializeDecodeObject(u32 sample_rate, u32 channel_count, void* buffer,
u64 buffer_size);
Result InitializeMultiStreamDecodeObject(u32 sample_rate, u32 channel_count,
u32 totaL_stream_count, u32 stereo_stream_count,
void* mappings, void* buffer, u64 buffer_size);
Result ShutdownDecodeObject(void* buffer, u64 buffer_size);
Result ShutdownMultiStreamDecodeObject(void* buffer, u64 buffer_size);
Result DecodeInterleaved(u32& out_sample_count, void* output_data, u64 output_data_size,
u32 channel_count, void* input_data, u64 input_data_size, void* buffer,
u64& out_time_taken, bool reset);
Result DecodeInterleavedForMultiStream(u32& out_sample_count, void* output_data,
u64 output_data_size, u32 channel_count,
void* input_data, u64 input_data_size, void* buffer,
u64& out_time_taken, bool reset);
Result MapMemory(void* buffer, u64 buffer_size);
Result UnmapMemory(void* buffer, u64 buffer_size);
private:
Core::System& system;
std::mutex mutex;
ADSP::OpusDecoder::OpusDecoder& opus_decoder;
ADSP::OpusDecoder::SharedMemory shared_memory;
};
} // namespace AudioCore::OpusDecoder
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <mutex>
#include <opus.h>
#include "audio_core/adsp/apps/opus/opus_decoder.h"
#include "audio_core/adsp/apps/opus/shared_memory.h"
#include "audio_core/adsp/mailbox.h"
#include "core/hle/service/audio/errors.h"
namespace AudioCore::OpusDecoder {
class HardwareOpus {
public:
HardwareOpus(Core::System& system);
u64 GetWorkBufferSize(u32 channel);
u64 GetWorkBufferSizeForMultiStream(u32 total_stream_count, u32 stereo_stream_count);
Result InitializeDecodeObject(u32 sample_rate, u32 channel_count, void* buffer,
u64 buffer_size);
Result InitializeMultiStreamDecodeObject(u32 sample_rate, u32 channel_count,
u32 totaL_stream_count, u32 stereo_stream_count,
void* mappings, void* buffer, u64 buffer_size);
Result ShutdownDecodeObject(void* buffer, u64 buffer_size);
Result ShutdownMultiStreamDecodeObject(void* buffer, u64 buffer_size);
Result DecodeInterleaved(u32& out_sample_count, void* output_data, u64 output_data_size,
u32 channel_count, void* input_data, u64 input_data_size, void* buffer,
u64& out_time_taken, bool reset);
Result DecodeInterleavedForMultiStream(u32& out_sample_count, void* output_data,
u64 output_data_size, u32 channel_count,
void* input_data, u64 input_data_size, void* buffer,
u64& out_time_taken, bool reset);
Result MapMemory(void* buffer, u64 buffer_size);
Result UnmapMemory(void* buffer, u64 buffer_size);
private:
Core::System& system;
std::mutex mutex;
ADSP::OpusDecoder::OpusDecoder& opus_decoder;
ADSP::OpusDecoder::SharedMemory shared_memory;
};
} // namespace AudioCore::OpusDecoder

View File

@ -146,7 +146,7 @@ public:
return;
}
paused = true;
SignalPause();
if (cubeb_stream_stop(stream_backend) != CUBEB_OK) {
LOG_CRITICAL(Audio_Sink, "Error stopping cubeb stream");
}

View File

@ -111,7 +111,7 @@ public:
if (device == 0 || paused) {
return;
}
paused = true;
SignalPause();
SDL_PauseAudioDevice(device, 1);
}

View File

@ -282,11 +282,19 @@ u64 SinkStream::GetExpectedPlayedSampleCount() {
void SinkStream::WaitFreeSpace(std::stop_token stop_token) {
std::unique_lock lk{release_mutex};
release_cv.wait_for(lk, std::chrono::milliseconds(5),
[this]() { return queued_buffers < max_queue_size; });
[this]() { return paused || queued_buffers < max_queue_size; });
if (queued_buffers > max_queue_size + 3) {
Common::CondvarWait(release_cv, lk, stop_token,
[this] { return queued_buffers < max_queue_size; });
[this] { return paused || queued_buffers < max_queue_size; });
}
}
void SinkStream::SignalPause() {
{
std::scoped_lock lk{release_mutex};
paused = true;
}
release_cv.notify_one();
}
} // namespace AudioCore::Sink

View File

@ -213,6 +213,12 @@ public:
*/
void WaitFreeSpace(std::stop_token stop_token);
protected:
/**
* Unblocks the ADSP if the stream is paused.
*/
void SignalPause();
protected:
/// Core system
Core::System& system;

View File

@ -1,6 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#ifdef ANDROID
#include <sys/system_properties.h>
#endif
#include "common/arm64/native_clock.h"
namespace Common::Arm64 {
@ -65,7 +68,23 @@ bool NativeClock::IsNative() const {
u64 NativeClock::GetHostCNTFRQ() {
u64 cntfrq_el0 = 0;
asm("mrs %[cntfrq_el0], cntfrq_el0" : [cntfrq_el0] "=r"(cntfrq_el0));
std::string_view board{""};
#ifdef ANDROID
char buffer[PROP_VALUE_MAX];
int len{__system_property_get("ro.product.board", buffer)};
board = std::string_view(buffer, static_cast<size_t>(len));
#endif
if (board == "s5e9925") { // Exynos 2200
cntfrq_el0 = 25600000;
} else if (board == "exynos2100") { // Exynos 2100
cntfrq_el0 = 26000000;
} else if (board == "exynos9810") { // Exynos 9810
cntfrq_el0 = 26000000;
} else if (board == "s5e8825") { // Exynos 1280
cntfrq_el0 = 26000000;
} else {
asm("mrs %[cntfrq_el0], cntfrq_el0" : [cntfrq_el0] "=r"(cntfrq_el0));
}
return cntfrq_el0;
}

View File

@ -9,12 +9,12 @@ PageTable::PageTable() = default;
PageTable::~PageTable() noexcept = default;
bool PageTable::BeginTraversal(TraversalEntry& out_entry, TraversalContext& out_context,
u64 address) const {
bool PageTable::BeginTraversal(TraversalEntry* out_entry, TraversalContext* out_context,
Common::ProcessAddress address) const {
// Setup invalid defaults.
out_entry.phys_addr = 0;
out_entry.block_size = page_size;
out_context.next_page = 0;
out_entry->phys_addr = 0;
out_entry->block_size = page_size;
out_context->next_page = 0;
// Validate that we can read the actual entry.
const auto page = address / page_size;
@ -29,20 +29,20 @@ bool PageTable::BeginTraversal(TraversalEntry& out_entry, TraversalContext& out_
}
// Populate the results.
out_entry.phys_addr = phys_addr + address;
out_context.next_page = page + 1;
out_context.next_offset = address + page_size;
out_entry->phys_addr = phys_addr + GetInteger(address);
out_context->next_page = page + 1;
out_context->next_offset = GetInteger(address) + page_size;
return true;
}
bool PageTable::ContinueTraversal(TraversalEntry& out_entry, TraversalContext& context) const {
bool PageTable::ContinueTraversal(TraversalEntry* out_entry, TraversalContext* context) const {
// Setup invalid defaults.
out_entry.phys_addr = 0;
out_entry.block_size = page_size;
out_entry->phys_addr = 0;
out_entry->block_size = page_size;
// Validate that we can read the actual entry.
const auto page = context.next_page;
const auto page = context->next_page;
if (page >= backing_addr.size()) {
return false;
}
@ -54,9 +54,9 @@ bool PageTable::ContinueTraversal(TraversalEntry& out_entry, TraversalContext& c
}
// Populate the results.
out_entry.phys_addr = phys_addr + context.next_offset;
context.next_page = page + 1;
context.next_offset += page_size;
out_entry->phys_addr = phys_addr + context->next_offset;
context->next_page = page + 1;
context->next_offset += page_size;
return true;
}

View File

@ -6,6 +6,7 @@
#include <atomic>
#include "common/common_types.h"
#include "common/typed_address.h"
#include "common/virtual_buffer.h"
namespace Common {
@ -100,9 +101,9 @@ struct PageTable {
PageTable(PageTable&&) noexcept = default;
PageTable& operator=(PageTable&&) noexcept = default;
bool BeginTraversal(TraversalEntry& out_entry, TraversalContext& out_context,
u64 address) const;
bool ContinueTraversal(TraversalEntry& out_entry, TraversalContext& context) const;
bool BeginTraversal(TraversalEntry* out_entry, TraversalContext* out_context,
Common::ProcessAddress address) const;
bool ContinueTraversal(TraversalEntry* out_entry, TraversalContext* context) const;
/**
* Resizes the page table to be able to accommodate enough pages within
@ -117,6 +118,16 @@ struct PageTable {
return current_address_space_width_in_bits;
}
bool GetPhysicalAddress(Common::PhysicalAddress* out_phys_addr,
Common::ProcessAddress virt_addr) const {
if (virt_addr > (1ULL << this->GetAddressSpaceBits())) {
return false;
}
*out_phys_addr = backing_addr[virt_addr / page_size] + GetInteger(virt_addr);
return true;
}
/**
* Vector of memory pointers backing each page. An entry can only be non-null if the
* corresponding attribute element is of type `Memory`.

View File

@ -203,10 +203,12 @@ const char* TranslateCategory(Category category) {
case Category::Ui:
case Category::UiGeneral:
return "UI";
case Category::UiAudio:
return "UiAudio";
case Category::UiLayout:
return "UiLayout";
return "UILayout";
case Category::UiGameList:
return "UiGameList";
return "UIGameList";
case Category::Screenshots:
return "Screenshots";
case Category::Shortcuts:

View File

@ -153,7 +153,7 @@ struct Values {
true,
true};
Setting<bool, false> audio_muted{
linkage, false, "audio_muted", Category::Audio, Specialization::Default, false, true};
linkage, false, "audio_muted", Category::Audio, Specialization::Default, true, true};
Setting<bool, false> dump_audio_commands{
linkage, false, "dump_audio_commands", Category::Audio, Specialization::Default, false};
@ -232,7 +232,11 @@ struct Values {
SwitchableSetting<bool> use_asynchronous_gpu_emulation{
linkage, true, "use_asynchronous_gpu_emulation", Category::Renderer};
SwitchableSetting<AstcDecodeMode, true> accelerate_astc{linkage,
#ifdef ANDROID
AstcDecodeMode::Cpu,
#else
AstcDecodeMode::Gpu,
#endif
AstcDecodeMode::Cpu,
AstcDecodeMode::CpuAsynchronous,
"accelerate_astc",
@ -304,7 +308,11 @@ struct Values {
linkage, 0, "bg_blue", Category::Renderer, Specialization::Default, true, true};
SwitchableSetting<GpuAccuracy, true> gpu_accuracy{linkage,
#ifdef ANDROID
GpuAccuracy::Normal,
#else
GpuAccuracy::High,
#endif
GpuAccuracy::Normal,
GpuAccuracy::Extreme,
"gpu_accuracy",
@ -313,20 +321,38 @@ struct Values {
true,
true};
GpuAccuracy current_gpu_accuracy{GpuAccuracy::High};
SwitchableSetting<AnisotropyMode, true> max_anisotropy{
linkage, AnisotropyMode::Automatic, AnisotropyMode::Automatic, AnisotropyMode::X16,
"max_anisotropy", Category::RendererAdvanced};
SwitchableSetting<AnisotropyMode, true> max_anisotropy{linkage,
#ifdef ANDROID
AnisotropyMode::Default,
#else
AnisotropyMode::Automatic,
#endif
AnisotropyMode::Automatic,
AnisotropyMode::X16,
"max_anisotropy",
Category::RendererAdvanced};
SwitchableSetting<AstcRecompression, true> astc_recompression{linkage,
AstcRecompression::Uncompressed,
AstcRecompression::Uncompressed,
AstcRecompression::Bc3,
"astc_recompression",
Category::RendererAdvanced};
SwitchableSetting<bool> async_presentation{linkage, false, "async_presentation",
Category::RendererAdvanced};
SwitchableSetting<bool> async_presentation{linkage,
#ifdef ANDROID
true,
#else
false,
#endif
"async_presentation", Category::RendererAdvanced};
SwitchableSetting<bool> renderer_force_max_clock{linkage, false, "force_max_clock",
Category::RendererAdvanced};
SwitchableSetting<bool> use_reactive_flushing{linkage, true, "use_reactive_flushing",
SwitchableSetting<bool> use_reactive_flushing{linkage,
#ifdef ANDROID
false,
#else
true,
#endif
"use_reactive_flushing",
Category::RendererAdvanced};
SwitchableSetting<bool> use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders",
Category::RendererAdvanced};
@ -358,6 +384,8 @@ struct Values {
Category::RendererDebug};
// TODO: remove this once AMDVLK supports VK_EXT_depth_bias_control
bool renderer_amdvlk_depth_bias_workaround{};
Setting<bool> disable_buffer_reorder{linkage, false, "disable_buffer_reorder",
Category::RendererDebug};
// System
SwitchableSetting<Language, true> language_index{linkage,
@ -390,7 +418,11 @@ struct Values {
Setting<s32> current_user{linkage, 0, "current_user", Category::System};
SwitchableSetting<ConsoleMode> use_docked_mode{linkage,
#ifdef ANDROID
ConsoleMode::Handheld,
#else
ConsoleMode::Docked,
#endif
"use_docked_mode",
Category::System,
Specialization::Radio,

View File

@ -32,6 +32,7 @@ enum class Category : u32 {
AddOns,
Controls,
Ui,
UiAudio,
UiGeneral,
UiLayout,
UiGameList,

View File

@ -271,8 +271,9 @@ add_library(core STATIC
hle/kernel/k_page_heap.h
hle/kernel/k_page_group.cpp
hle/kernel/k_page_group.h
hle/kernel/k_page_table.cpp
hle/kernel/k_page_table.h
hle/kernel/k_page_table_base.cpp
hle/kernel/k_page_table_base.h
hle/kernel/k_page_table_manager.h
hle/kernel/k_page_table_slab_heap.h
hle/kernel/k_port.cpp
@ -280,6 +281,7 @@ add_library(core STATIC
hle/kernel/k_priority_queue.h
hle/kernel/k_process.cpp
hle/kernel/k_process.h
hle/kernel/k_process_page_table.h
hle/kernel/k_readable_event.cpp
hle/kernel/k_readable_event.h
hle/kernel/k_resource_limit.cpp
@ -330,8 +332,6 @@ add_library(core STATIC
hle/kernel/physical_core.cpp
hle/kernel/physical_core.h
hle/kernel/physical_memory.h
hle/kernel/process_capability.cpp
hle/kernel/process_capability.h
hle/kernel/slab_helpers.h
hle/kernel/svc.cpp
hle/kernel/svc.h

View File

@ -153,6 +153,14 @@ void ARM_Interface::Run() {
Kernel::KThread* current_thread{Kernel::GetCurrentThreadPointer(system.Kernel())};
HaltReason hr{};
// If the thread is scheduled for termination, exit the thread.
if (current_thread->HasDpc()) {
if (current_thread->IsTerminationRequested()) {
current_thread->Exit();
UNREACHABLE();
}
}
// Notify the debugger and go to sleep if a step was performed
// and this thread has been scheduled again.
if (current_thread->GetStepState() == StepState::StepPerformed) {
@ -174,14 +182,6 @@ void ARM_Interface::Run() {
}
system.ExitCPUProfile();
// If the thread is scheduled for termination, exit the thread.
if (current_thread->HasDpc()) {
if (current_thread->IsTerminationRequested()) {
current_thread->Exit();
UNREACHABLE();
}
}
// Notify the debugger and go to sleep if a breakpoint was hit,
// or if the thread is unable to continue for any reason.
if (True(hr & HaltReason::InstructionBreakpoint) || True(hr & HaltReason::PrefetchAbort)) {

View File

@ -76,6 +76,7 @@ void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) {
}
void CoreTiming::ClearPendingEvents() {
std::scoped_lock lock{basic_lock};
event_queue.clear();
}
@ -113,6 +114,7 @@ bool CoreTiming::IsRunning() const {
}
bool CoreTiming::HasPendingEvents() const {
std::scoped_lock lock{basic_lock};
return !(wait_set && event_queue.empty());
}

View File

@ -161,7 +161,7 @@ private:
std::shared_ptr<EventType> ev_lost;
Common::Event event{};
Common::Event pause_event{};
std::mutex basic_lock;
mutable std::mutex basic_lock;
std::mutex advance_lock;
std::unique_ptr<std::jthread> timer_thread;
std::atomic<bool> paused{};

View File

@ -562,6 +562,120 @@ static std::string PaginateBuffer(std::string_view buffer, std::string_view requ
}
}
static VAddr GetModuleEnd(Kernel::KProcessPageTable& page_table, VAddr base) {
Kernel::KMemoryInfo mem_info;
Kernel::Svc::MemoryInfo svc_mem_info;
Kernel::Svc::PageInfo page_info;
VAddr cur_addr{base};
// Expect: r-x Code (.text)
R_ASSERT(page_table.QueryInfo(std::addressof(mem_info), std::addressof(page_info), cur_addr));
svc_mem_info = mem_info.GetSvcMemoryInfo();
cur_addr = svc_mem_info.base_address + svc_mem_info.size;
if (svc_mem_info.state != Kernel::Svc::MemoryState::Code ||
svc_mem_info.permission != Kernel::Svc::MemoryPermission::ReadExecute) {
return cur_addr - 1;
}
// Expect: r-- Code (.rodata)
R_ASSERT(page_table.QueryInfo(std::addressof(mem_info), std::addressof(page_info), cur_addr));
svc_mem_info = mem_info.GetSvcMemoryInfo();
cur_addr = svc_mem_info.base_address + svc_mem_info.size;
if (svc_mem_info.state != Kernel::Svc::MemoryState::Code ||
svc_mem_info.permission != Kernel::Svc::MemoryPermission::Read) {
return cur_addr - 1;
}
// Expect: rw- CodeData (.data)
R_ASSERT(page_table.QueryInfo(std::addressof(mem_info), std::addressof(page_info), cur_addr));
svc_mem_info = mem_info.GetSvcMemoryInfo();
cur_addr = svc_mem_info.base_address + svc_mem_info.size;
return cur_addr - 1;
}
static Loader::AppLoader::Modules FindModules(Core::System& system) {
Loader::AppLoader::Modules modules;
auto& page_table = system.ApplicationProcess()->GetPageTable();
auto& memory = system.ApplicationMemory();
VAddr cur_addr = 0;
// Look for executable sections in Code or AliasCode regions.
while (true) {
Kernel::KMemoryInfo mem_info{};
Kernel::Svc::PageInfo page_info{};
R_ASSERT(
page_table.QueryInfo(std::addressof(mem_info), std::addressof(page_info), cur_addr));
auto svc_mem_info = mem_info.GetSvcMemoryInfo();
if (svc_mem_info.permission == Kernel::Svc::MemoryPermission::ReadExecute &&
(svc_mem_info.state == Kernel::Svc::MemoryState::Code ||
svc_mem_info.state == Kernel::Svc::MemoryState::AliasCode)) {
// Try to read the module name from its path.
constexpr s32 PathLengthMax = 0x200;
struct {
u32 zero;
s32 path_length;
std::array<char, PathLengthMax> path;
} module_path;
if (memory.ReadBlock(svc_mem_info.base_address + svc_mem_info.size, &module_path,
sizeof(module_path))) {
if (module_path.zero == 0 && module_path.path_length > 0) {
// Truncate module name.
module_path.path[PathLengthMax - 1] = '\0';
// Ignore leading directories.
char* path_pointer = module_path.path.data();
for (s32 i = 0; i < std::min(PathLengthMax, module_path.path_length) &&
module_path.path[i] != '\0';
i++) {
if (module_path.path[i] == '/' || module_path.path[i] == '\\') {
path_pointer = module_path.path.data() + i + 1;
}
}
// Insert output.
modules.emplace(svc_mem_info.base_address, path_pointer);
}
}
}
// Check if we're done.
const uintptr_t next_address = svc_mem_info.base_address + svc_mem_info.size;
if (next_address <= cur_addr) {
break;
}
cur_addr = next_address;
}
return modules;
}
static VAddr FindMainModuleEntrypoint(Core::System& system) {
Loader::AppLoader::Modules modules;
system.GetAppLoader().ReadNSOModules(modules);
// Do we have a module named main?
const auto main = std::find_if(modules.begin(), modules.end(),
[](const auto& key) { return key.second == "main"; });
if (main != modules.end()) {
return main->first;
}
// Do we have any loaded executable sections?
modules = FindModules(system);
if (!modules.empty()) {
return modules.begin()->first;
}
// As a last resort, use the start of the code region.
return GetInteger(system.ApplicationProcess()->GetPageTable().GetCodeRegionStart());
}
void GDBStub::HandleQuery(std::string_view command) {
if (command.starts_with("TStatus")) {
// no tracepoint support
@ -573,21 +687,10 @@ void GDBStub::HandleQuery(std::string_view command) {
const auto target_xml{arch->GetTargetXML()};
SendReply(PaginateBuffer(target_xml, command.substr(30)));
} else if (command.starts_with("Offsets")) {
Loader::AppLoader::Modules modules;
system.GetAppLoader().ReadNSOModules(modules);
const auto main = std::find_if(modules.begin(), modules.end(),
[](const auto& key) { return key.second == "main"; });
if (main != modules.end()) {
SendReply(fmt::format("TextSeg={:x}", main->first));
} else {
SendReply(fmt::format(
"TextSeg={:x}",
GetInteger(system.ApplicationProcess()->GetPageTable().GetCodeRegionStart())));
}
const auto main_offset = FindMainModuleEntrypoint(system);
SendReply(fmt::format("TextSeg={:x}", main_offset));
} else if (command.starts_with("Xfer:libraries:read::")) {
Loader::AppLoader::Modules modules;
system.GetAppLoader().ReadNSOModules(modules);
auto modules = FindModules(system);
std::string buffer;
buffer += R"(<?xml version="1.0"?>)";
@ -727,32 +830,6 @@ static constexpr const char* GetMemoryPermissionString(const Kernel::Svc::Memory
}
}
static VAddr GetModuleEnd(Kernel::KPageTable& page_table, VAddr base) {
Kernel::Svc::MemoryInfo mem_info;
VAddr cur_addr{base};
// Expect: r-x Code (.text)
mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo();
cur_addr = mem_info.base_address + mem_info.size;
if (mem_info.state != Kernel::Svc::MemoryState::Code ||
mem_info.permission != Kernel::Svc::MemoryPermission::ReadExecute) {
return cur_addr - 1;
}
// Expect: r-- Code (.rodata)
mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo();
cur_addr = mem_info.base_address + mem_info.size;
if (mem_info.state != Kernel::Svc::MemoryState::Code ||
mem_info.permission != Kernel::Svc::MemoryPermission::Read) {
return cur_addr - 1;
}
// Expect: rw- CodeData (.data)
mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo();
cur_addr = mem_info.base_address + mem_info.size;
return cur_addr - 1;
}
void GDBStub::HandleRcmd(const std::vector<u8>& command) {
std::string_view command_str{reinterpret_cast<const char*>(&command[0]), command.size()};
std::string reply;
@ -767,7 +844,7 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
if (command_str == "get fastmem") {
if (Settings::IsFastmemEnabled()) {
const auto& impl = page_table.PageTableImpl();
const auto& impl = page_table.GetImpl();
const auto region = reinterpret_cast<uintptr_t>(impl.fastmem_arena);
const auto region_bits = impl.current_address_space_width_in_bits;
const auto region_size = 1ULL << region_bits;
@ -779,26 +856,27 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
reply = "Fastmem is not enabled.\n";
}
} else if (command_str == "get info") {
Loader::AppLoader::Modules modules;
system.GetAppLoader().ReadNSOModules(modules);
auto modules = FindModules(system);
reply = fmt::format("Process: {:#x} ({})\n"
"Program Id: {:#018x}\n",
process->GetProcessId(), process->GetName(), process->GetProgramId());
reply += fmt::format("Layout:\n"
" Alias: {:#012x} - {:#012x}\n"
" Heap: {:#012x} - {:#012x}\n"
" Aslr: {:#012x} - {:#012x}\n"
" Stack: {:#012x} - {:#012x}\n"
"Modules:\n",
GetInteger(page_table.GetAliasRegionStart()),
GetInteger(page_table.GetAliasRegionEnd()),
GetInteger(page_table.GetHeapRegionStart()),
GetInteger(page_table.GetHeapRegionEnd()),
GetInteger(page_table.GetAliasCodeRegionStart()),
GetInteger(page_table.GetAliasCodeRegionEnd()),
GetInteger(page_table.GetStackRegionStart()),
GetInteger(page_table.GetStackRegionEnd()));
reply += fmt::format(
"Layout:\n"
" Alias: {:#012x} - {:#012x}\n"
" Heap: {:#012x} - {:#012x}\n"
" Aslr: {:#012x} - {:#012x}\n"
" Stack: {:#012x} - {:#012x}\n"
"Modules:\n",
GetInteger(page_table.GetAliasRegionStart()),
GetInteger(page_table.GetAliasRegionStart()) + page_table.GetAliasRegionSize() - 1,
GetInteger(page_table.GetHeapRegionStart()),
GetInteger(page_table.GetHeapRegionStart()) + page_table.GetHeapRegionSize() - 1,
GetInteger(page_table.GetAliasCodeRegionStart()),
GetInteger(page_table.GetAliasCodeRegionStart()) + page_table.GetAliasCodeRegionSize() -
1,
GetInteger(page_table.GetStackRegionStart()),
GetInteger(page_table.GetStackRegionStart()) + page_table.GetStackRegionSize() - 1);
for (const auto& [vaddr, name] : modules) {
reply += fmt::format(" {:#012x} - {:#012x} {}\n", vaddr,
@ -811,27 +889,34 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
while (true) {
using MemoryAttribute = Kernel::Svc::MemoryAttribute;
auto mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo();
Kernel::KMemoryInfo mem_info{};
Kernel::Svc::PageInfo page_info{};
R_ASSERT(page_table.QueryInfo(std::addressof(mem_info), std::addressof(page_info),
cur_addr));
auto svc_mem_info = mem_info.GetSvcMemoryInfo();
if (mem_info.state != Kernel::Svc::MemoryState::Inaccessible ||
mem_info.base_address + mem_info.size - 1 != std::numeric_limits<u64>::max()) {
const char* state = GetMemoryStateName(mem_info.state);
const char* perm = GetMemoryPermissionString(mem_info);
if (svc_mem_info.state != Kernel::Svc::MemoryState::Inaccessible ||
svc_mem_info.base_address + svc_mem_info.size - 1 !=
std::numeric_limits<u64>::max()) {
const char* state = GetMemoryStateName(svc_mem_info.state);
const char* perm = GetMemoryPermissionString(svc_mem_info);
const char l = True(mem_info.attribute & MemoryAttribute::Locked) ? 'L' : '-';
const char i = True(mem_info.attribute & MemoryAttribute::IpcLocked) ? 'I' : '-';
const char d = True(mem_info.attribute & MemoryAttribute::DeviceShared) ? 'D' : '-';
const char u = True(mem_info.attribute & MemoryAttribute::Uncached) ? 'U' : '-';
const char l = True(svc_mem_info.attribute & MemoryAttribute::Locked) ? 'L' : '-';
const char i =
True(svc_mem_info.attribute & MemoryAttribute::IpcLocked) ? 'I' : '-';
const char d =
True(svc_mem_info.attribute & MemoryAttribute::DeviceShared) ? 'D' : '-';
const char u = True(svc_mem_info.attribute & MemoryAttribute::Uncached) ? 'U' : '-';
const char p =
True(mem_info.attribute & MemoryAttribute::PermissionLocked) ? 'P' : '-';
True(svc_mem_info.attribute & MemoryAttribute::PermissionLocked) ? 'P' : '-';
reply += fmt::format(" {:#012x} - {:#012x} {} {} {}{}{}{}{} [{}, {}]\n",
mem_info.base_address,
mem_info.base_address + mem_info.size - 1, perm, state, l, i,
d, u, p, mem_info.ipc_count, mem_info.device_count);
reply += fmt::format(
" {:#012x} - {:#012x} {} {} {}{}{}{}{} [{}, {}]\n", svc_mem_info.base_address,
svc_mem_info.base_address + svc_mem_info.size - 1, perm, state, l, i, d, u, p,
svc_mem_info.ipc_count, svc_mem_info.device_count);
}
const uintptr_t next_address = mem_info.base_address + mem_info.size;
const uintptr_t next_address = svc_mem_info.base_address + svc_mem_info.size;
if (next_address <= cur_addr) {
break;
}

View File

@ -35,13 +35,14 @@ struct RomFSHeader {
static_assert(sizeof(RomFSHeader) == 0x50, "RomFSHeader has incorrect size.");
struct DirectoryEntry {
u32_le parent;
u32_le sibling;
u32_le child_dir;
u32_le child_file;
u32_le hash;
u32_le name_length;
};
static_assert(sizeof(DirectoryEntry) == 0x14, "DirectoryEntry has incorrect size.");
static_assert(sizeof(DirectoryEntry) == 0x18, "DirectoryEntry has incorrect size.");
struct FileEntry {
u32_le parent;
@ -64,25 +65,22 @@ std::pair<Entry, std::string> GetEntry(const VirtualFile& file, std::size_t offs
return {entry, string};
}
void ProcessFile(VirtualFile file, std::size_t file_offset, std::size_t data_offset,
u32 this_file_offset, std::shared_ptr<VectorVfsDirectory> parent) {
while (true) {
void ProcessFile(const VirtualFile& file, std::size_t file_offset, std::size_t data_offset,
u32 this_file_offset, std::shared_ptr<VectorVfsDirectory>& parent) {
while (this_file_offset != ROMFS_ENTRY_EMPTY) {
auto entry = GetEntry<FileEntry>(file, file_offset + this_file_offset);
parent->AddFile(std::make_shared<OffsetVfsFile>(
file, entry.first.size, entry.first.offset + data_offset, entry.second));
if (entry.first.sibling == ROMFS_ENTRY_EMPTY)
break;
this_file_offset = entry.first.sibling;
}
}
void ProcessDirectory(VirtualFile file, std::size_t dir_offset, std::size_t file_offset,
void ProcessDirectory(const VirtualFile& file, std::size_t dir_offset, std::size_t file_offset,
std::size_t data_offset, u32 this_dir_offset,
std::shared_ptr<VectorVfsDirectory> parent) {
while (true) {
std::shared_ptr<VectorVfsDirectory>& parent) {
while (this_dir_offset != ROMFS_ENTRY_EMPTY) {
auto entry = GetEntry<DirectoryEntry>(file, dir_offset + this_dir_offset);
auto current = std::make_shared<VectorVfsDirectory>(
std::vector<VirtualFile>{}, std::vector<VirtualDir>{}, entry.second);
@ -97,14 +95,12 @@ void ProcessDirectory(VirtualFile file, std::size_t dir_offset, std::size_t file
}
parent->AddDirectory(current);
if (entry.first.sibling == ROMFS_ENTRY_EMPTY)
break;
this_dir_offset = entry.first.sibling;
}
}
} // Anonymous namespace
VirtualDir ExtractRomFS(VirtualFile file, RomFSExtractionType type) {
VirtualDir ExtractRomFS(VirtualFile file) {
RomFSHeader header{};
if (file->ReadObject(&header) != sizeof(RomFSHeader))
return nullptr;
@ -113,27 +109,17 @@ VirtualDir ExtractRomFS(VirtualFile file, RomFSExtractionType type) {
return nullptr;
const u64 file_offset = header.file_meta.offset;
const u64 dir_offset = header.directory_meta.offset + 4;
const u64 dir_offset = header.directory_meta.offset;
auto root =
std::make_shared<VectorVfsDirectory>(std::vector<VirtualFile>{}, std::vector<VirtualDir>{},
file->GetName(), file->GetContainingDirectory());
auto root_container = std::make_shared<VectorVfsDirectory>();
ProcessDirectory(file, dir_offset, file_offset, header.data_offset, 0, root);
ProcessDirectory(file, dir_offset, file_offset, header.data_offset, 0, root_container);
VirtualDir out = std::move(root);
if (type == RomFSExtractionType::SingleDiscard)
return out->GetSubdirectories().front();
while (out->GetSubdirectories().size() == 1 && out->GetFiles().empty()) {
if (Common::ToLower(out->GetSubdirectories().front()->GetName()) == "data" &&
type == RomFSExtractionType::Truncated)
break;
out = out->GetSubdirectories().front();
if (auto root = root_container->GetSubdirectory(""); root) {
return std::make_shared<CachedVfsDirectory>(std::move(root));
}
return std::make_shared<CachedVfsDirectory>(std::move(out));
return nullptr;
}
VirtualFile CreateRomFS(VirtualDir dir, VirtualDir ext) {

View File

@ -7,16 +7,9 @@
namespace FileSys {
enum class RomFSExtractionType {
Full, // Includes data directory
Truncated, // Traverses into data directory
SingleDiscard, // Traverses into the first subdirectory of root
};
// Converts a RomFS binary blob to VFS Filesystem
// Returns nullptr on failure
VirtualDir ExtractRomFS(VirtualFile file,
RomFSExtractionType type = RomFSExtractionType::Truncated);
VirtualDir ExtractRomFS(VirtualFile file);
// Converts a VFS filesystem into a RomFS binary
// Returns nullptr on failure

View File

@ -96,18 +96,7 @@ void EmulatedController::ReloadFromSettings() {
}
controller.color_values = {};
controller.colors_state.fullkey = {
.body = GetNpadColor(player.body_color_left),
.button = GetNpadColor(player.button_color_left),
};
controller.colors_state.left = {
.body = GetNpadColor(player.body_color_left),
.button = GetNpadColor(player.button_color_left),
};
controller.colors_state.right = {
.body = GetNpadColor(player.body_color_right),
.button = GetNpadColor(player.button_color_right),
};
ReloadColorsFromSettings();
ring_params[0] = Common::ParamPackage(Settings::values.ringcon_analogs);
@ -128,6 +117,30 @@ void EmulatedController::ReloadFromSettings() {
ReloadInput();
}
void EmulatedController::ReloadColorsFromSettings() {
const auto player_index = NpadIdTypeToIndex(npad_id_type);
const auto& player = Settings::values.players.GetValue()[player_index];
// Avoid updating colors if overridden by physical controller
if (controller.color_values[LeftIndex].body != 0 &&
controller.color_values[RightIndex].body != 0) {
return;
}
controller.colors_state.fullkey = {
.body = GetNpadColor(player.body_color_left),
.button = GetNpadColor(player.button_color_left),
};
controller.colors_state.left = {
.body = GetNpadColor(player.body_color_left),
.button = GetNpadColor(player.button_color_left),
};
controller.colors_state.right = {
.body = GetNpadColor(player.body_color_right),
.button = GetNpadColor(player.button_color_right),
};
}
void EmulatedController::LoadDevices() {
// TODO(german77): Use more buttons to detect the correct device
const auto left_joycon = button_params[Settings::NativeButton::DRight];
@ -1091,30 +1104,30 @@ void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callbac
bool is_charging = false;
bool is_powered = false;
NpadBatteryLevel battery_level = 0;
NpadBatteryLevel battery_level = NpadBatteryLevel::Empty;
switch (controller.battery_values[index]) {
case Common::Input::BatteryLevel::Charging:
is_charging = true;
is_powered = true;
battery_level = 6;
battery_level = NpadBatteryLevel::Full;
break;
case Common::Input::BatteryLevel::Medium:
battery_level = 6;
battery_level = NpadBatteryLevel::High;
break;
case Common::Input::BatteryLevel::Low:
battery_level = 4;
battery_level = NpadBatteryLevel::Low;
break;
case Common::Input::BatteryLevel::Critical:
battery_level = 2;
battery_level = NpadBatteryLevel::Critical;
break;
case Common::Input::BatteryLevel::Empty:
battery_level = 0;
battery_level = NpadBatteryLevel::Empty;
break;
case Common::Input::BatteryLevel::None:
case Common::Input::BatteryLevel::Full:
default:
is_powered = true;
battery_level = 8;
battery_level = NpadBatteryLevel::Full;
break;
}

View File

@ -253,6 +253,9 @@ public:
/// Overrides current mapped devices with the stored configuration and reloads all input devices
void ReloadFromSettings();
/// Updates current colors with the ones stored in the configuration
void ReloadColorsFromSettings();
/// Saves the current mapped configuration
void SaveCurrentConfig();

View File

@ -302,6 +302,15 @@ enum class TouchScreenModeForNx : u8 {
Heat2,
};
// This is nn::hid::system::NpadBatteryLevel
enum class NpadBatteryLevel : u32 {
Empty,
Critical,
Low,
High,
Full,
};
// This is nn::hid::NpadStyleTag
struct NpadStyleTag {
union {
@ -385,16 +394,12 @@ struct NpadGcTriggerState {
};
static_assert(sizeof(NpadGcTriggerState) == 0x10, "NpadGcTriggerState is an invalid size");
// This is nn::hid::system::NpadBatteryLevel
using NpadBatteryLevel = u32;
static_assert(sizeof(NpadBatteryLevel) == 0x4, "NpadBatteryLevel is an invalid size");
// This is nn::hid::system::NpadPowerInfo
struct NpadPowerInfo {
bool is_powered{};
bool is_charging{};
INSERT_PADDING_BYTES(0x6);
NpadBatteryLevel battery_level{8};
NpadBatteryLevel battery_level{NpadBatteryLevel::Full};
};
static_assert(sizeof(NpadPowerInfo) == 0xC, "NpadPowerInfo is an invalid size");

View File

@ -222,7 +222,7 @@ Result KSystemControl::AllocateSecureMemory(KernelCore& kernel, KVirtualAddress*
};
// We succeeded.
*out = KPageTable::GetHeapVirtualAddress(kernel.MemoryLayout(), paddr);
*out = KPageTable::GetHeapVirtualAddress(kernel, paddr);
R_SUCCEED();
}
@ -238,8 +238,17 @@ void KSystemControl::FreeSecureMemory(KernelCore& kernel, KVirtualAddress addres
ASSERT(Common::IsAligned(size, alignment));
// Close the secure region's pages.
kernel.MemoryManager().Close(KPageTable::GetHeapPhysicalAddress(kernel.MemoryLayout(), address),
kernel.MemoryManager().Close(KPageTable::GetHeapPhysicalAddress(kernel, address),
size / PageSize);
}
// Insecure Memory.
KResourceLimit* KSystemControl::GetInsecureMemoryResourceLimit(KernelCore& kernel) {
return kernel.GetSystemResourceLimit();
}
u32 KSystemControl::GetInsecureMemoryPool() {
return static_cast<u32>(KMemoryManager::Pool::SystemNonSecure);
}
} // namespace Kernel::Board::Nintendo::Nx

View File

@ -8,7 +8,8 @@
namespace Kernel {
class KernelCore;
}
class KResourceLimit;
} // namespace Kernel
namespace Kernel::Board::Nintendo::Nx {
@ -40,6 +41,10 @@ public:
u32 pool);
static void FreeSecureMemory(KernelCore& kernel, KVirtualAddress address, size_t size,
u32 pool);
// Insecure Memory.
static KResourceLimit* GetInsecureMemoryResourceLimit(KernelCore& kernel);
static u32 GetInsecureMemoryPool();
};
} // namespace Kernel::Board::Nintendo::Nx

View File

@ -4,14 +4,16 @@
#include "core/hardware_properties.h"
#include "core/hle/kernel/k_capabilities.h"
#include "core/hle/kernel/k_memory_layout.h"
#include "core/hle/kernel/k_page_table.h"
#include "core/hle/kernel/k_process_page_table.h"
#include "core/hle/kernel/k_trace.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/svc_results.h"
#include "core/hle/kernel/svc_version.h"
namespace Kernel {
Result KCapabilities::InitializeForKip(std::span<const u32> kern_caps, KPageTable* page_table) {
Result KCapabilities::InitializeForKip(std::span<const u32> kern_caps,
KProcessPageTable* page_table) {
// We're initializing an initial process.
m_svc_access_flags.reset();
m_irq_access_flags.reset();
@ -41,7 +43,8 @@ Result KCapabilities::InitializeForKip(std::span<const u32> kern_caps, KPageTabl
R_RETURN(this->SetCapabilities(kern_caps, page_table));
}
Result KCapabilities::InitializeForUser(std::span<const u32> user_caps, KPageTable* page_table) {
Result KCapabilities::InitializeForUser(std::span<const u32> user_caps,
KProcessPageTable* page_table) {
// We're initializing a user process.
m_svc_access_flags.reset();
m_irq_access_flags.reset();
@ -121,7 +124,7 @@ Result KCapabilities::SetSyscallMaskCapability(const u32 cap, u32& set_svc) {
R_SUCCEED();
}
Result KCapabilities::MapRange_(const u32 cap, const u32 size_cap, KPageTable* page_table) {
Result KCapabilities::MapRange_(const u32 cap, const u32 size_cap, KProcessPageTable* page_table) {
const auto range_pack = MapRange{cap};
const auto size_pack = MapRangeSize{size_cap};
@ -142,16 +145,13 @@ Result KCapabilities::MapRange_(const u32 cap, const u32 size_cap, KPageTable* p
? KMemoryPermission::UserRead
: KMemoryPermission::UserReadWrite;
if (MapRangeSize{size_cap}.normal) {
// R_RETURN(page_table->MapStatic(phys_addr, size, perm));
R_RETURN(page_table->MapStatic(phys_addr, size, perm));
} else {
// R_RETURN(page_table->MapIo(phys_addr, size, perm));
R_RETURN(page_table->MapIo(phys_addr, size, perm));
}
UNIMPLEMENTED();
R_SUCCEED();
}
Result KCapabilities::MapIoPage_(const u32 cap, KPageTable* page_table) {
Result KCapabilities::MapIoPage_(const u32 cap, KProcessPageTable* page_table) {
// Get/validate address/size
const u64 phys_addr = MapIoPage{cap}.address.Value() * PageSize;
const size_t num_pages = 1;
@ -160,10 +160,7 @@ Result KCapabilities::MapIoPage_(const u32 cap, KPageTable* page_table) {
R_UNLESS(((phys_addr + size - 1) & ~PhysicalMapAllowedMask) == 0, ResultInvalidAddress);
// Do the mapping.
// R_RETURN(page_table->MapIo(phys_addr, size, KMemoryPermission_UserReadWrite));
UNIMPLEMENTED();
R_SUCCEED();
R_RETURN(page_table->MapIo(phys_addr, size, KMemoryPermission::UserReadWrite));
}
template <typename F>
@ -200,13 +197,11 @@ Result KCapabilities::ProcessMapRegionCapability(const u32 cap, F f) {
R_SUCCEED();
}
Result KCapabilities::MapRegion_(const u32 cap, KPageTable* page_table) {
Result KCapabilities::MapRegion_(const u32 cap, KProcessPageTable* page_table) {
// Map each region into the process's page table.
return ProcessMapRegionCapability(
cap, [](KMemoryRegionType region_type, KMemoryPermission perm) -> Result {
// R_RETURN(page_table->MapRegion(region_type, perm));
UNIMPLEMENTED();
R_SUCCEED();
cap, [page_table](KMemoryRegionType region_type, KMemoryPermission perm) -> Result {
R_RETURN(page_table->MapRegion(region_type, perm));
});
}
@ -280,7 +275,7 @@ Result KCapabilities::SetDebugFlagsCapability(const u32 cap) {
}
Result KCapabilities::SetCapability(const u32 cap, u32& set_flags, u32& set_svc,
KPageTable* page_table) {
KProcessPageTable* page_table) {
// Validate this is a capability we can act on.
const auto type = GetCapabilityType(cap);
R_UNLESS(type != CapabilityType::Invalid, ResultInvalidArgument);
@ -318,7 +313,7 @@ Result KCapabilities::SetCapability(const u32 cap, u32& set_flags, u32& set_svc,
}
}
Result KCapabilities::SetCapabilities(std::span<const u32> caps, KPageTable* page_table) {
Result KCapabilities::SetCapabilities(std::span<const u32> caps, KProcessPageTable* page_table) {
u32 set_flags = 0, set_svc = 0;
for (size_t i = 0; i < caps.size(); i++) {
@ -335,6 +330,8 @@ Result KCapabilities::SetCapabilities(std::span<const u32> caps, KPageTable* pag
// Map the range.
R_TRY(this->MapRange_(cap, size_cap, page_table));
} else if (GetCapabilityType(cap) == CapabilityType::MapRegion && !IsKTraceEnabled) {
continue;
} else {
R_TRY(this->SetCapability(cap, set_flags, set_svc, page_table));
}

View File

@ -15,15 +15,15 @@
namespace Kernel {
class KPageTable;
class KProcessPageTable;
class KernelCore;
class KCapabilities {
public:
constexpr explicit KCapabilities() = default;
Result InitializeForKip(std::span<const u32> kern_caps, KPageTable* page_table);
Result InitializeForUser(std::span<const u32> user_caps, KPageTable* page_table);
Result InitializeForKip(std::span<const u32> kern_caps, KProcessPageTable* page_table);
Result InitializeForUser(std::span<const u32> user_caps, KProcessPageTable* page_table);
static Result CheckCapabilities(KernelCore& kernel, std::span<const u32> user_caps);
@ -264,9 +264,9 @@ private:
Result SetCorePriorityCapability(const u32 cap);
Result SetSyscallMaskCapability(const u32 cap, u32& set_svc);
Result MapRange_(const u32 cap, const u32 size_cap, KPageTable* page_table);
Result MapIoPage_(const u32 cap, KPageTable* page_table);
Result MapRegion_(const u32 cap, KPageTable* page_table);
Result MapRange_(const u32 cap, const u32 size_cap, KProcessPageTable* page_table);
Result MapIoPage_(const u32 cap, KProcessPageTable* page_table);
Result MapRegion_(const u32 cap, KProcessPageTable* page_table);
Result SetInterruptPairCapability(const u32 cap);
Result SetProgramTypeCapability(const u32 cap);
Result SetKernelVersionCapability(const u32 cap);
@ -277,8 +277,9 @@ private:
static Result ProcessMapRegionCapability(const u32 cap, F f);
static Result CheckMapRegion(KernelCore& kernel, const u32 cap);
Result SetCapability(const u32 cap, u32& set_flags, u32& set_svc, KPageTable* page_table);
Result SetCapabilities(std::span<const u32> caps, KPageTable* page_table);
Result SetCapability(const u32 cap, u32& set_flags, u32& set_svc,
KProcessPageTable* page_table);
Result SetCapabilities(std::span<const u32> caps, KProcessPageTable* page_table);
private:
Svc::SvcAccessFlagSet m_svc_access_flags{};

View File

@ -54,7 +54,7 @@ Result KDeviceAddressSpace::Detach(Svc::DeviceName device_name) {
R_SUCCEED();
}
Result KDeviceAddressSpace::Map(KPageTable* page_table, KProcessAddress process_address,
Result KDeviceAddressSpace::Map(KProcessPageTable* page_table, KProcessAddress process_address,
size_t size, u64 device_address, u32 option, bool is_aligned) {
// Check that the address falls within the space.
R_UNLESS((m_space_address <= device_address &&
@ -113,7 +113,7 @@ Result KDeviceAddressSpace::Map(KPageTable* page_table, KProcessAddress process_
R_SUCCEED();
}
Result KDeviceAddressSpace::Unmap(KPageTable* page_table, KProcessAddress process_address,
Result KDeviceAddressSpace::Unmap(KProcessPageTable* page_table, KProcessAddress process_address,
size_t size, u64 device_address) {
// Check that the address falls within the space.
R_UNLESS((m_space_address <= device_address &&

View File

@ -5,7 +5,7 @@
#include <string>
#include "core/hle/kernel/k_page_table.h"
#include "core/hle/kernel/k_process_page_table.h"
#include "core/hle/kernel/k_typed_address.h"
#include "core/hle/kernel/slab_helpers.h"
#include "core/hle/result.h"
@ -31,23 +31,23 @@ public:
Result Attach(Svc::DeviceName device_name);
Result Detach(Svc::DeviceName device_name);
Result MapByForce(KPageTable* page_table, KProcessAddress process_address, size_t size,
Result MapByForce(KProcessPageTable* page_table, KProcessAddress process_address, size_t size,
u64 device_address, u32 option) {
R_RETURN(this->Map(page_table, process_address, size, device_address, option, false));
}
Result MapAligned(KPageTable* page_table, KProcessAddress process_address, size_t size,
Result MapAligned(KProcessPageTable* page_table, KProcessAddress process_address, size_t size,
u64 device_address, u32 option) {
R_RETURN(this->Map(page_table, process_address, size, device_address, option, true));
}
Result Unmap(KPageTable* page_table, KProcessAddress process_address, size_t size,
Result Unmap(KProcessPageTable* page_table, KProcessAddress process_address, size_t size,
u64 device_address);
static void Initialize();
private:
Result Map(KPageTable* page_table, KProcessAddress process_address, size_t size,
Result Map(KProcessPageTable* page_table, KProcessAddress process_address, size_t size,
u64 device_address, u32 option, bool is_aligned);
private:

View File

@ -394,6 +394,14 @@ private:
return region.GetEndAddress();
}
public:
static const KMemoryRegion* Find(const KMemoryLayout& layout, KVirtualAddress address) {
return Find(address, layout.GetVirtualMemoryRegionTree());
}
static const KMemoryRegion* Find(const KMemoryLayout& layout, KPhysicalAddress address) {
return Find(address, layout.GetPhysicalMemoryRegionTree());
}
private:
u64 m_linear_phys_to_virt_diff{};
u64 m_linear_virt_to_phys_diff{};

View File

@ -456,8 +456,7 @@ size_t KMemoryManager::Impl::Initialize(KPhysicalAddress address, size_t size,
}
void KMemoryManager::Impl::InitializeOptimizedMemory(KernelCore& kernel) {
auto optimize_pa =
KPageTable::GetHeapPhysicalAddress(kernel.MemoryLayout(), m_management_region);
auto optimize_pa = KPageTable::GetHeapPhysicalAddress(kernel, m_management_region);
auto* optimize_map = kernel.System().DeviceMemory().GetPointer<u64>(optimize_pa);
std::memset(optimize_map, 0, CalculateOptimizedProcessOverheadSize(m_heap.GetSize()));
@ -465,8 +464,7 @@ void KMemoryManager::Impl::InitializeOptimizedMemory(KernelCore& kernel) {
void KMemoryManager::Impl::TrackUnoptimizedAllocation(KernelCore& kernel, KPhysicalAddress block,
size_t num_pages) {
auto optimize_pa =
KPageTable::GetHeapPhysicalAddress(kernel.MemoryLayout(), m_management_region);
auto optimize_pa = KPageTable::GetHeapPhysicalAddress(kernel, m_management_region);
auto* optimize_map = kernel.System().DeviceMemory().GetPointer<u64>(optimize_pa);
// Get the range we're tracking.
@ -485,8 +483,7 @@ void KMemoryManager::Impl::TrackUnoptimizedAllocation(KernelCore& kernel, KPhysi
void KMemoryManager::Impl::TrackOptimizedAllocation(KernelCore& kernel, KPhysicalAddress block,
size_t num_pages) {
auto optimize_pa =
KPageTable::GetHeapPhysicalAddress(kernel.MemoryLayout(), m_management_region);
auto optimize_pa = KPageTable::GetHeapPhysicalAddress(kernel, m_management_region);
auto* optimize_map = kernel.System().DeviceMemory().GetPointer<u64>(optimize_pa);
// Get the range we're tracking.
@ -506,8 +503,7 @@ void KMemoryManager::Impl::TrackOptimizedAllocation(KernelCore& kernel, KPhysica
bool KMemoryManager::Impl::ProcessOptimizedAllocation(KernelCore& kernel, KPhysicalAddress block,
size_t num_pages, u8 fill_pattern) {
auto& device_memory = kernel.System().DeviceMemory();
auto optimize_pa =
KPageTable::GetHeapPhysicalAddress(kernel.MemoryLayout(), m_management_region);
auto optimize_pa = KPageTable::GetHeapPhysicalAddress(kernel, m_management_region);
auto* optimize_map = device_memory.GetPointer<u64>(optimize_pa);
// We want to return whether any pages were newly allocated.

File diff suppressed because it is too large Load Diff

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