Compare commits
109 Commits
android-12
...
android-13
Author | SHA1 | Date | |
---|---|---|---|
ca25d1cb9e | |||
5ca8629bb2 | |||
c3e377a701 | |||
24548b1f5c | |||
be0ecae108 | |||
fcd54c6479 | |||
08296f151e | |||
a134e924ff | |||
31ed6bae11 | |||
9a5ef835cc | |||
df0d3698ae | |||
51fc608f68 | |||
b30e19ba24 | |||
ec6b67d862 | |||
4d0b7f8496 | |||
e3b510a4b4 | |||
247d66a680 | |||
0047d8a01e | |||
efc0187537 | |||
b6fe8a0b3f | |||
ecaa038b4d | |||
4aac971864 | |||
6c93cdffb1 | |||
470714e2d1 | |||
6b888b0fa8 | |||
1a1393dad7 | |||
55412962c0 | |||
d920da2631 | |||
ff72bf2cb2 | |||
4efb9763d9 | |||
c600bc8652 | |||
767c4b5a99 | |||
904d03b01f | |||
9f1c9599a2 | |||
5f6666a7cd | |||
1906e2724f | |||
0c032d3f2f | |||
a6735cba5f | |||
3b872b89d1 | |||
40d4e9543b | |||
e588f341ed | |||
875246f5b2 | |||
b16fefa106 | |||
2a255b2d61 | |||
9e331f9957 | |||
9169cbf728 | |||
2f9487cd38 | |||
09f993899e | |||
edce713fc9 | |||
f75363177e | |||
4c6217f09b | |||
c95f35ea85 | |||
40357098a2 | |||
e7f4110791 | |||
ca1dd1862b | |||
737d1cea62 | |||
2f8e237ab7 | |||
5191465b0a | |||
50c604f37f | |||
dfbc22c291 | |||
a5a3167eba | |||
a423e0f9e0 | |||
511c1f0c8b | |||
8369fcd71a | |||
626916e9a4 | |||
507f360a81 | |||
5323d9f6b3 | |||
770d4b0b72 | |||
e5fed31009 | |||
f07484bc64 | |||
78b9956a04 | |||
90aa937593 | |||
940618a64d | |||
409fa5dda2 | |||
211b67668d | |||
f0cd02b9bd | |||
34101d8c5e | |||
bf8d7bc0da | |||
9543adf072 | |||
036d2686af | |||
a80e0e7da5 | |||
9631dedea9 | |||
75de0cadcf | |||
4b321c003c | |||
0a83047368 | |||
9bb8ac7cb6 | |||
d6e6ab11b1 | |||
b3a1f793c3 | |||
a294beb116 | |||
eda403388a | |||
3032980478 | |||
7f96f4db3f | |||
a0f9a3ab5b | |||
b36fec486e | |||
57cf830862 | |||
41701052d3 | |||
b0c6bf497a | |||
6a7123826a | |||
6513a356f0 | |||
65d4a16afd | |||
ca75c58f43 | |||
723df0f368 | |||
94b7ac50bb | |||
18450ebd78 | |||
efdb2e8f3d | |||
7a84a1a974 | |||
789d9c8af9 | |||
4df063209b | |||
6256e3ca8e |
5
.git-blame-ignore-revs
Normal file
5
.git-blame-ignore-revs
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||||
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
# CRLF -> LF
|
||||||
|
90aa937593e53a5d5e070fb623b228578b0b225f
|
2
.github/workflows/android-build.yml
vendored
2
.github/workflows/android-build.yml
vendored
@ -40,11 +40,11 @@ jobs:
|
|||||||
sudo apt-get install -y ccache apksigner glslang-dev glslang-tools
|
sudo apt-get install -y ccache apksigner glslang-dev glslang-tools
|
||||||
- name: Build
|
- name: Build
|
||||||
run: ./.ci/scripts/android/build.sh
|
run: ./.ci/scripts/android/build.sh
|
||||||
- name: Copy and sign artifacts
|
|
||||||
env:
|
env:
|
||||||
ANDROID_KEYSTORE_B64: ${{ secrets.ANDROID_KEYSTORE_B64 }}
|
ANDROID_KEYSTORE_B64: ${{ secrets.ANDROID_KEYSTORE_B64 }}
|
||||||
ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
|
ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
|
||||||
ANDROID_KEYSTORE_PASS: ${{ secrets.ANDROID_KEYSTORE_PASS }}
|
ANDROID_KEYSTORE_PASS: ${{ secrets.ANDROID_KEYSTORE_PASS }}
|
||||||
|
- name: Copy artifacts
|
||||||
run: ./.ci/scripts/android/upload.sh
|
run: ./.ci/scripts/android/upload.sh
|
||||||
- name: Upload
|
- name: Upload
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
|
6
.gitmodules
vendored
6
.gitmodules
vendored
@ -4,9 +4,6 @@
|
|||||||
[submodule "enet"]
|
[submodule "enet"]
|
||||||
path = externals/enet
|
path = externals/enet
|
||||||
url = https://github.com/lsalzman/enet.git
|
url = https://github.com/lsalzman/enet.git
|
||||||
[submodule "inih"]
|
|
||||||
path = externals/inih/inih
|
|
||||||
url = https://github.com/benhoyt/inih.git
|
|
||||||
[submodule "cubeb"]
|
[submodule "cubeb"]
|
||||||
path = externals/cubeb
|
path = externals/cubeb
|
||||||
url = https://github.com/mozilla/cubeb.git
|
url = https://github.com/mozilla/cubeb.git
|
||||||
@ -61,3 +58,6 @@
|
|||||||
[submodule "breakpad"]
|
[submodule "breakpad"]
|
||||||
path = externals/breakpad
|
path = externals/breakpad
|
||||||
url = https://github.com/yuzu-emu/breakpad.git
|
url = https://github.com/yuzu-emu/breakpad.git
|
||||||
|
[submodule "simpleini"]
|
||||||
|
path = externals/simpleini
|
||||||
|
url = https://github.com/brofield/simpleini.git
|
||||||
|
@ -285,7 +285,6 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
|
|||||||
find_package(Boost 1.79.0 REQUIRED context)
|
find_package(Boost 1.79.0 REQUIRED context)
|
||||||
find_package(enet 1.3 MODULE)
|
find_package(enet 1.3 MODULE)
|
||||||
find_package(fmt 9 REQUIRED)
|
find_package(fmt 9 REQUIRED)
|
||||||
find_package(inih 52 MODULE COMPONENTS INIReader)
|
|
||||||
find_package(LLVM 17.0.2 MODULE COMPONENTS Demangle)
|
find_package(LLVM 17.0.2 MODULE COMPONENTS Demangle)
|
||||||
find_package(lz4 REQUIRED)
|
find_package(lz4 REQUIRED)
|
||||||
find_package(nlohmann_json 3.8 REQUIRED)
|
find_package(nlohmann_json 3.8 REQUIRED)
|
||||||
|
@ -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()
|
|
10
README.md
10
README.md
@ -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-FileCopyrightText: 2018 yuzu Emulator Project
|
||||||
SPDX-License-Identifier: GPL-2.0-or-later
|
SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
8
externals/CMakeLists.txt
vendored
8
externals/CMakeLists.txt
vendored
@ -34,11 +34,6 @@ endif()
|
|||||||
# Glad
|
# Glad
|
||||||
add_subdirectory(glad)
|
add_subdirectory(glad)
|
||||||
|
|
||||||
# inih
|
|
||||||
if (NOT TARGET inih::INIReader)
|
|
||||||
add_subdirectory(inih)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# mbedtls
|
# mbedtls
|
||||||
add_subdirectory(mbedtls)
|
add_subdirectory(mbedtls)
|
||||||
target_include_directories(mbedtls PUBLIC ./mbedtls/include)
|
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)
|
target_link_libraries(dump_syms PRIVATE libbreakpad_client ZLIB::ZLIB)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# SimpleIni
|
||||||
|
add_subdirectory(simpleini)
|
||||||
|
13
externals/inih/CMakeLists.txt
vendored
13
externals/inih/CMakeLists.txt
vendored
@ -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
1
externals/inih/inih
vendored
Submodule externals/inih/inih deleted from 9cecf0643d
1
externals/simpleini
vendored
Submodule
1
externals/simpleini
vendored
Submodule
Submodule externals/simpleini added at 382ddbb4b9
@ -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.
|
# 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)
|
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)
|
add_definitions(-DUNICODE -D_UNICODE)
|
||||||
|
|
||||||
# /W4 - Level 4 warnings
|
# /W4 - Level 4 warnings
|
||||||
@ -54,11 +54,11 @@ if (MSVC)
|
|||||||
/GT
|
/GT
|
||||||
|
|
||||||
# Modules
|
# 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 headers diagnostics
|
||||||
/external:anglebrackets # Treats all headers included by #include <header>, where the header file is enclosed in angle brackets (< >), as external headers
|
/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
|
# Warnings
|
||||||
/W4
|
/W4
|
||||||
@ -187,6 +187,7 @@ add_subdirectory(audio_core)
|
|||||||
add_subdirectory(video_core)
|
add_subdirectory(video_core)
|
||||||
add_subdirectory(network)
|
add_subdirectory(network)
|
||||||
add_subdirectory(input_common)
|
add_subdirectory(input_common)
|
||||||
|
add_subdirectory(frontend_common)
|
||||||
add_subdirectory(shader_recompiler)
|
add_subdirectory(shader_recompiler)
|
||||||
|
|
||||||
if (YUZU_ROOM)
|
if (YUZU_ROOM)
|
||||||
|
@ -219,7 +219,6 @@ dependencies {
|
|||||||
implementation("io.coil-kt:coil:2.2.2")
|
implementation("io.coil-kt:coil:2.2.2")
|
||||||
implementation("androidx.core:core-splashscreen:1.0.1")
|
implementation("androidx.core:core-splashscreen:1.0.1")
|
||||||
implementation("androidx.window:window:1.2.0-beta03")
|
implementation("androidx.window:window:1.2.0-beta03")
|
||||||
implementation("org.ini4j:ini4j:0.5.4")
|
|
||||||
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
|
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
|
||||||
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
|
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
|
||||||
implementation("androidx.navigation:navigation-fragment-ktx:2.7.4")
|
implementation("androidx.navigation:navigation-fragment-ktx:2.7.4")
|
||||||
|
@ -230,8 +230,6 @@ object NativeLibrary {
|
|||||||
*/
|
*/
|
||||||
external fun onTouchReleased(finger_id: Int)
|
external fun onTouchReleased(finger_id: Int)
|
||||||
|
|
||||||
external fun reloadSettings()
|
|
||||||
|
|
||||||
external fun initGameIni(gameID: String?)
|
external fun initGameIni(gameID: String?)
|
||||||
|
|
||||||
external fun setAppDirectory(directory: String)
|
external fun setAppDirectory(directory: String)
|
||||||
@ -252,7 +250,7 @@ object NativeLibrary {
|
|||||||
|
|
||||||
external fun reloadKeys(): Boolean
|
external fun reloadKeys(): Boolean
|
||||||
|
|
||||||
external fun initializeSystem()
|
external fun initializeSystem(reload: Boolean)
|
||||||
|
|
||||||
external fun defaultCPUCore(): Int
|
external fun defaultCPUCore(): Int
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ import java.io.File
|
|||||||
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
|
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
|
||||||
import org.yuzu.yuzu_emu.utils.DocumentsTree
|
import org.yuzu.yuzu_emu.utils.DocumentsTree
|
||||||
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
|
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
|
||||||
|
import org.yuzu.yuzu_emu.utils.Log
|
||||||
|
|
||||||
fun Context.getPublicFilesDir(): File = getExternalFilesDir(null) ?: filesDir
|
fun Context.getPublicFilesDir(): File = getExternalFilesDir(null) ?: filesDir
|
||||||
|
|
||||||
@ -49,6 +50,7 @@ class YuzuApplication : Application() {
|
|||||||
DirectoryInitialization.start()
|
DirectoryInitialization.start()
|
||||||
GpuDriverHelper.initializeDriverParameters()
|
GpuDriverHelper.initializeDriverParameters()
|
||||||
NativeLibrary.logDeviceInfo()
|
NativeLibrary.logDeviceInfo()
|
||||||
|
Log.logDeviceInfo()
|
||||||
|
|
||||||
createNotificationChannels()
|
createNotificationChannels()
|
||||||
}
|
}
|
||||||
|
@ -107,7 +107,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
|
|||||||
|
|
||||||
val preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
|
val preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
|
||||||
if (!preferences.getBoolean(Settings.PREF_MEMORY_WARNING_SHOWN, false)) {
|
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(
|
Toast.makeText(
|
||||||
this,
|
this,
|
||||||
getString(
|
getString(
|
||||||
@ -373,8 +373,10 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
|
|||||||
val pictureInPictureParamsBuilder = PictureInPictureParams.Builder()
|
val pictureInPictureParamsBuilder = PictureInPictureParams.Builder()
|
||||||
.getPictureInPictureActionsBuilder().getPictureInPictureAspectBuilder()
|
.getPictureInPictureActionsBuilder().getPictureInPictureAspectBuilder()
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
|
val isEmulationActive = emulationViewModel.emulationStarted.value &&
|
||||||
|
!emulationViewModel.isEmulationStopping.value
|
||||||
pictureInPictureParamsBuilder.setAutoEnterEnabled(
|
pictureInPictureParamsBuilder.setAutoEnterEnabled(
|
||||||
BooleanSetting.PICTURE_IN_PICTURE.boolean
|
BooleanSetting.PICTURE_IN_PICTURE.boolean && isEmulationActive
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
setPictureInPictureParams(pictureInPictureParamsBuilder.build())
|
setPictureInPictureParams(pictureInPictureParamsBuilder.build())
|
||||||
|
@ -22,12 +22,16 @@ import androidx.core.graphics.drawable.toBitmap
|
|||||||
import androidx.core.graphics.drawable.toDrawable
|
import androidx.core.graphics.drawable.toDrawable
|
||||||
import androidx.documentfile.provider.DocumentFile
|
import androidx.documentfile.provider.DocumentFile
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.navigation.findNavController
|
import androidx.navigation.findNavController
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import androidx.recyclerview.widget.AsyncDifferConfig
|
import androidx.recyclerview.widget.AsyncDifferConfig
|
||||||
import androidx.recyclerview.widget.DiffUtil
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
import androidx.recyclerview.widget.ListAdapter
|
import androidx.recyclerview.widget.ListAdapter
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
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.HomeNavigationDirections
|
||||||
import org.yuzu.yuzu_emu.R
|
import org.yuzu.yuzu_emu.R
|
||||||
import org.yuzu.yuzu_emu.YuzuApplication
|
import org.yuzu.yuzu_emu.YuzuApplication
|
||||||
@ -92,28 +96,34 @@ class GameAdapter(private val activity: AppCompatActivity) :
|
|||||||
data = Uri.parse(holder.game.path)
|
data = Uri.parse(holder.game.path)
|
||||||
}
|
}
|
||||||
|
|
||||||
val layerDrawable = ResourcesCompat.getDrawable(
|
activity.lifecycleScope.launch {
|
||||||
YuzuApplication.appContext.resources,
|
withContext(Dispatchers.IO) {
|
||||||
R.drawable.shortcut,
|
val layerDrawable = ResourcesCompat.getDrawable(
|
||||||
null
|
YuzuApplication.appContext.resources,
|
||||||
) as LayerDrawable
|
R.drawable.shortcut,
|
||||||
layerDrawable.setDrawableByLayerId(
|
null
|
||||||
R.id.shortcut_foreground,
|
) as LayerDrawable
|
||||||
GameIconUtils.getGameIcon(holder.game).toDrawable(YuzuApplication.appContext.resources)
|
layerDrawable.setDrawableByLayerId(
|
||||||
)
|
R.id.shortcut_foreground,
|
||||||
val inset = YuzuApplication.appContext.resources
|
GameIconUtils.getGameIcon(activity, holder.game)
|
||||||
.getDimensionPixelSize(R.dimen.icon_inset)
|
.toDrawable(YuzuApplication.appContext.resources)
|
||||||
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)
|
|
||||||
)
|
)
|
||||||
)
|
val inset = YuzuApplication.appContext.resources
|
||||||
.setIntent(openIntent)
|
.getDimensionPixelSize(R.dimen.icon_inset)
|
||||||
.build()
|
layerDrawable.setLayerInset(1, inset, inset, inset, inset)
|
||||||
ShortcutManagerCompat.pushDynamicShortcut(YuzuApplication.appContext, shortcut)
|
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)
|
val action = HomeNavigationDirections.actionGlobalEmulationActivity(holder.game)
|
||||||
view.findNavController().navigate(action)
|
view.findNavController().navigate(action)
|
||||||
|
@ -7,7 +7,7 @@ import android.text.TextUtils
|
|||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import org.yuzu.yuzu_emu.R
|
import org.yuzu.yuzu_emu.R
|
||||||
import org.yuzu.yuzu_emu.YuzuApplication
|
import org.yuzu.yuzu_emu.YuzuApplication
|
||||||
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
|
import org.yuzu.yuzu_emu.utils.NativeConfig
|
||||||
|
|
||||||
object Settings {
|
object Settings {
|
||||||
private val context get() = YuzuApplication.appContext
|
private val context get() = YuzuApplication.appContext
|
||||||
@ -19,7 +19,7 @@ object Settings {
|
|||||||
context.getString(R.string.ini_saved),
|
context.getString(R.string.ini_saved),
|
||||||
Toast.LENGTH_SHORT
|
Toast.LENGTH_SHORT
|
||||||
).show()
|
).show()
|
||||||
SettingsFile.saveFile(SettingsFile.FILE_NAME_CONFIG)
|
NativeConfig.saveSettings()
|
||||||
} else {
|
} else {
|
||||||
// TODO: Save custom game settings
|
// TODO: Save custom game settings
|
||||||
Toast.makeText(
|
Toast.makeText(
|
||||||
@ -82,7 +82,6 @@ object Settings {
|
|||||||
|
|
||||||
enum class MenuTag(val titleId: Int) {
|
enum class MenuTag(val titleId: Int) {
|
||||||
SECTION_ROOT(R.string.advanced_settings),
|
SECTION_ROOT(R.string.advanced_settings),
|
||||||
SECTION_GENERAL(R.string.preferences_general),
|
|
||||||
SECTION_SYSTEM(R.string.preferences_system),
|
SECTION_SYSTEM(R.string.preferences_system),
|
||||||
SECTION_RENDERER(R.string.preferences_graphics),
|
SECTION_RENDERER(R.string.preferences_graphics),
|
||||||
SECTION_AUDIO(R.string.preferences_audio),
|
SECTION_AUDIO(R.string.preferences_audio),
|
||||||
|
@ -3,10 +3,13 @@
|
|||||||
|
|
||||||
package org.yuzu.yuzu_emu.features.settings.model.view
|
package org.yuzu.yuzu_emu.features.settings.model.view
|
||||||
|
|
||||||
|
import androidx.annotation.DrawableRes
|
||||||
|
|
||||||
class RunnableSetting(
|
class RunnableSetting(
|
||||||
titleId: Int,
|
titleId: Int,
|
||||||
descriptionId: Int,
|
descriptionId: Int,
|
||||||
val isRuntimeRunnable: Boolean,
|
val isRuntimeRunnable: Boolean,
|
||||||
|
@DrawableRes val iconId: Int = 0,
|
||||||
val runnable: () -> Unit
|
val runnable: () -> Unit
|
||||||
) : SettingsItem(emptySetting, titleId, descriptionId) {
|
) : SettingsItem(emptySetting, titleId, descriptionId) {
|
||||||
override val type = TYPE_RUNNABLE
|
override val type = TYPE_RUNNABLE
|
||||||
|
@ -3,11 +3,14 @@
|
|||||||
|
|
||||||
package org.yuzu.yuzu_emu.features.settings.model.view
|
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
|
import org.yuzu.yuzu_emu.features.settings.model.Settings
|
||||||
|
|
||||||
class SubmenuSetting(
|
class SubmenuSetting(
|
||||||
titleId: Int,
|
@StringRes titleId: Int,
|
||||||
descriptionId: Int,
|
@StringRes descriptionId: Int,
|
||||||
|
@DrawableRes val iconId: Int,
|
||||||
val menuKey: Settings.MenuTag
|
val menuKey: Settings.MenuTag
|
||||||
) : SettingsItem(emptySetting, titleId, descriptionId) {
|
) : SettingsItem(emptySetting, titleId, descriptionId) {
|
||||||
override val type = TYPE_SUBMENU
|
override val type = TYPE_SUBMENU
|
||||||
|
@ -21,7 +21,6 @@ import androidx.navigation.navArgs
|
|||||||
import com.google.android.material.color.MaterialColors
|
import com.google.android.material.color.MaterialColors
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.yuzu.yuzu_emu.NativeLibrary
|
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import org.yuzu.yuzu_emu.R
|
import org.yuzu.yuzu_emu.R
|
||||||
import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding
|
import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding
|
||||||
@ -165,11 +164,12 @@ class SettingsActivity : AppCompatActivity() {
|
|||||||
settingsViewModel.shouldSave = false
|
settingsViewModel.shouldSave = false
|
||||||
|
|
||||||
// Delete settings file because the user may have changed values that do not exist in the UI
|
// 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)
|
val settingsFile = SettingsFile.getSettingsFile(SettingsFile.FILE_NAME_CONFIG)
|
||||||
if (!settingsFile.delete()) {
|
if (!settingsFile.delete()) {
|
||||||
throw IOException("Failed to delete $settingsFile")
|
throw IOException("Failed to delete $settingsFile")
|
||||||
}
|
}
|
||||||
NativeLibrary.reloadSettings()
|
NativeConfig.initializeConfig()
|
||||||
|
|
||||||
Toast.makeText(
|
Toast.makeText(
|
||||||
applicationContext,
|
applicationContext,
|
||||||
|
@ -20,7 +20,6 @@ import androidx.lifecycle.repeatOnLifecycle
|
|||||||
import androidx.navigation.findNavController
|
import androidx.navigation.findNavController
|
||||||
import androidx.navigation.fragment.navArgs
|
import androidx.navigation.fragment.navArgs
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import com.google.android.material.divider.MaterialDividerItemDecoration
|
|
||||||
import com.google.android.material.transition.MaterialSharedAxis
|
import com.google.android.material.transition.MaterialSharedAxis
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@ -68,15 +67,9 @@ class SettingsFragment : Fragment() {
|
|||||||
)
|
)
|
||||||
|
|
||||||
binding.toolbarSettingsLayout.title = getString(args.menuTag.titleId)
|
binding.toolbarSettingsLayout.title = getString(args.menuTag.titleId)
|
||||||
val dividerDecoration = MaterialDividerItemDecoration(
|
|
||||||
requireContext(),
|
|
||||||
LinearLayoutManager.VERTICAL
|
|
||||||
)
|
|
||||||
dividerDecoration.isLastItemDecorated = false
|
|
||||||
binding.listSettings.apply {
|
binding.listSettings.apply {
|
||||||
adapter = settingsAdapter
|
adapter = settingsAdapter
|
||||||
layoutManager = LinearLayoutManager(requireContext())
|
layoutManager = LinearLayoutManager(requireContext())
|
||||||
addItemDecoration(dividerDecoration)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.toolbarSettings.setNavigationOnClickListener {
|
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) {
|
if (args.menuTag == Settings.MenuTag.SECTION_ROOT) {
|
||||||
@ -112,8 +94,6 @@ class SettingsFragment : Fragment() {
|
|||||||
binding.toolbarSettings.setOnMenuItemClickListener {
|
binding.toolbarSettings.setOnMenuItemClickListener {
|
||||||
when (it.itemId) {
|
when (it.itemId) {
|
||||||
R.id.action_search -> {
|
R.id.action_search -> {
|
||||||
reenterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true)
|
|
||||||
exitTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false)
|
|
||||||
view.findNavController()
|
view.findNavController()
|
||||||
.navigate(R.id.action_settingsFragment_to_settingsSearchFragment)
|
.navigate(R.id.action_settingsFragment_to_settingsSearchFragment)
|
||||||
true
|
true
|
||||||
@ -129,11 +109,6 @@ class SettingsFragment : Fragment() {
|
|||||||
setInsets()
|
setInsets()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
|
||||||
super.onResume()
|
|
||||||
settingsViewModel.setIsUsingSearch(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setInsets() {
|
private fun setInsets() {
|
||||||
ViewCompat.setOnApplyWindowInsetsListener(
|
ViewCompat.setOnApplyWindowInsetsListener(
|
||||||
binding.root
|
binding.root
|
||||||
@ -144,10 +119,9 @@ class SettingsFragment : Fragment() {
|
|||||||
val leftInsets = barInsets.left + cutoutInsets.left
|
val leftInsets = barInsets.left + cutoutInsets.left
|
||||||
val rightInsets = barInsets.right + cutoutInsets.right
|
val rightInsets = barInsets.right + cutoutInsets.right
|
||||||
|
|
||||||
val sideMargin = resources.getDimensionPixelSize(R.dimen.spacing_medlarge)
|
|
||||||
val mlpSettingsList = binding.listSettings.layoutParams as MarginLayoutParams
|
val mlpSettingsList = binding.listSettings.layoutParams as MarginLayoutParams
|
||||||
mlpSettingsList.leftMargin = sideMargin + leftInsets
|
mlpSettingsList.leftMargin = leftInsets
|
||||||
mlpSettingsList.rightMargin = sideMargin + rightInsets
|
mlpSettingsList.rightMargin = rightInsets
|
||||||
binding.listSettings.layoutParams = mlpSettingsList
|
binding.listSettings.layoutParams = mlpSettingsList
|
||||||
binding.listSettings.updatePadding(
|
binding.listSettings.updatePadding(
|
||||||
bottom = barInsets.bottom
|
bottom = barInsets.bottom
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
package org.yuzu.yuzu_emu.features.settings.ui
|
package org.yuzu.yuzu_emu.features.settings.ui
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
@ -32,8 +31,6 @@ class SettingsFragmentPresenter(
|
|||||||
private val preferences: SharedPreferences
|
private val preferences: SharedPreferences
|
||||||
get() = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
|
get() = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
|
||||||
|
|
||||||
private val context: Context get() = YuzuApplication.appContext
|
|
||||||
|
|
||||||
// Extension for populating settings list based on paired settings
|
// Extension for populating settings list based on paired settings
|
||||||
fun ArrayList<SettingsItem>.add(key: String) {
|
fun ArrayList<SettingsItem>.add(key: String) {
|
||||||
val item = SettingsItem.settingsItems[key]!!
|
val item = SettingsItem.settingsItems[key]!!
|
||||||
@ -53,7 +50,6 @@ class SettingsFragmentPresenter(
|
|||||||
val sl = ArrayList<SettingsItem>()
|
val sl = ArrayList<SettingsItem>()
|
||||||
when (menuTag) {
|
when (menuTag) {
|
||||||
Settings.MenuTag.SECTION_ROOT -> addConfigSettings(sl)
|
Settings.MenuTag.SECTION_ROOT -> addConfigSettings(sl)
|
||||||
Settings.MenuTag.SECTION_GENERAL -> addGeneralSettings(sl)
|
|
||||||
Settings.MenuTag.SECTION_SYSTEM -> addSystemSettings(sl)
|
Settings.MenuTag.SECTION_SYSTEM -> addSystemSettings(sl)
|
||||||
Settings.MenuTag.SECTION_RENDERER -> addGraphicsSettings(sl)
|
Settings.MenuTag.SECTION_RENDERER -> addGraphicsSettings(sl)
|
||||||
Settings.MenuTag.SECTION_AUDIO -> addAudioSettings(sl)
|
Settings.MenuTag.SECTION_AUDIO -> addAudioSettings(sl)
|
||||||
@ -75,30 +71,53 @@ class SettingsFragmentPresenter(
|
|||||||
|
|
||||||
private fun addConfigSettings(sl: ArrayList<SettingsItem>) {
|
private fun addConfigSettings(sl: ArrayList<SettingsItem>) {
|
||||||
sl.apply {
|
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(
|
add(
|
||||||
RunnableSetting(R.string.reset_to_default, 0, false) {
|
SubmenuSetting(
|
||||||
settingsViewModel.setShouldShowResetSettingsDialog(true)
|
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>) {
|
private fun addSystemSettings(sl: ArrayList<SettingsItem>) {
|
||||||
sl.apply {
|
sl.apply {
|
||||||
|
add(BooleanSetting.RENDERER_USE_SPEED_LIMIT.key)
|
||||||
|
add(ShortSetting.RENDERER_SPEED_LIMIT.key)
|
||||||
add(BooleanSetting.USE_DOCKED_MODE.key)
|
add(BooleanSetting.USE_DOCKED_MODE.key)
|
||||||
add(IntSetting.REGION_INDEX.key)
|
add(IntSetting.REGION_INDEX.key)
|
||||||
add(IntSetting.LANGUAGE_INDEX.key)
|
add(IntSetting.LANGUAGE_INDEX.key)
|
||||||
@ -116,6 +135,7 @@ class SettingsFragmentPresenter(
|
|||||||
add(IntSetting.RENDERER_ANTI_ALIASING.key)
|
add(IntSetting.RENDERER_ANTI_ALIASING.key)
|
||||||
add(IntSetting.RENDERER_SCREEN_LAYOUT.key)
|
add(IntSetting.RENDERER_SCREEN_LAYOUT.key)
|
||||||
add(IntSetting.RENDERER_ASPECT_RATIO.key)
|
add(IntSetting.RENDERER_ASPECT_RATIO.key)
|
||||||
|
add(BooleanSetting.PICTURE_IN_PICTURE.key)
|
||||||
add(BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE.key)
|
add(BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE.key)
|
||||||
add(BooleanSetting.RENDERER_FORCE_MAX_CLOCK.key)
|
add(BooleanSetting.RENDERER_FORCE_MAX_CLOCK.key)
|
||||||
add(BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS.key)
|
add(BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS.key)
|
||||||
@ -249,6 +269,7 @@ class SettingsFragmentPresenter(
|
|||||||
add(BooleanSetting.RENDERER_DEBUG.key)
|
add(BooleanSetting.RENDERER_DEBUG.key)
|
||||||
|
|
||||||
add(HeaderSetting(R.string.cpu))
|
add(HeaderSetting(R.string.cpu))
|
||||||
|
add(IntSetting.CPU_ACCURACY.key)
|
||||||
add(BooleanSetting.CPU_DEBUG_MODE.key)
|
add(BooleanSetting.CPU_DEBUG_MODE.key)
|
||||||
add(SettingsItem.FASTMEM_COMBINED)
|
add(SettingsItem.FASTMEM_COMBINED)
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
package org.yuzu.yuzu_emu.features.settings.ui.viewholder
|
package org.yuzu.yuzu_emu.features.settings.ui.viewholder
|
||||||
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import androidx.core.content.res.ResourcesCompat
|
||||||
import org.yuzu.yuzu_emu.NativeLibrary
|
import org.yuzu.yuzu_emu.NativeLibrary
|
||||||
import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
|
import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.view.RunnableSetting
|
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) {
|
override fun bind(item: SettingsItem) {
|
||||||
setting = item as RunnableSetting
|
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)
|
binding.textSettingName.setText(item.nameId)
|
||||||
if (item.descriptionId != 0) {
|
if (item.descriptionId != 0) {
|
||||||
binding.textSettingDescription.setText(item.descriptionId)
|
binding.textSettingDescription.setText(item.descriptionId)
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
package org.yuzu.yuzu_emu.features.settings.ui.viewholder
|
package org.yuzu.yuzu_emu.features.settings.ui.viewholder
|
||||||
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import androidx.core.content.res.ResourcesCompat
|
||||||
import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
|
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.SettingsItem
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.view.SubmenuSetting
|
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) {
|
override fun bind(item: SettingsItem) {
|
||||||
this.item = item as SubmenuSetting
|
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)
|
binding.textSettingName.setText(item.nameId)
|
||||||
if (item.descriptionId != 0) {
|
if (item.descriptionId != 0) {
|
||||||
binding.textSettingDescription.setText(item.descriptionId)
|
binding.textSettingDescription.setText(item.descriptionId)
|
||||||
|
@ -3,15 +3,8 @@
|
|||||||
|
|
||||||
package org.yuzu.yuzu_emu.features.settings.utils
|
package org.yuzu.yuzu_emu.features.settings.utils
|
||||||
|
|
||||||
import android.widget.Toast
|
|
||||||
import java.io.*
|
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.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.
|
* 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 {
|
object SettingsFile {
|
||||||
const val FILE_NAME_CONFIG = "config"
|
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 =
|
fun getSettingsFile(fileName: String): File =
|
||||||
File(DirectoryInitialization.userDirectory + "/config/" + fileName + ".ini")
|
File(DirectoryInitialization.userDirectory + "/config/" + fileName + ".ini")
|
||||||
}
|
}
|
||||||
|
@ -114,10 +114,10 @@ class AboutFragment : Fragment() {
|
|||||||
val leftInsets = barInsets.left + cutoutInsets.left
|
val leftInsets = barInsets.left + cutoutInsets.left
|
||||||
val rightInsets = barInsets.right + cutoutInsets.right
|
val rightInsets = barInsets.right + cutoutInsets.right
|
||||||
|
|
||||||
val mlpAppBar = binding.appbarAbout.layoutParams as MarginLayoutParams
|
val mlpToolbar = binding.toolbarAbout.layoutParams as MarginLayoutParams
|
||||||
mlpAppBar.leftMargin = leftInsets
|
mlpToolbar.leftMargin = leftInsets
|
||||||
mlpAppBar.rightMargin = rightInsets
|
mlpToolbar.rightMargin = rightInsets
|
||||||
binding.appbarAbout.layoutParams = mlpAppBar
|
binding.toolbarAbout.layoutParams = mlpToolbar
|
||||||
|
|
||||||
val mlpScrollAbout = binding.scrollAbout.layoutParams as MarginLayoutParams
|
val mlpScrollAbout = binding.scrollAbout.layoutParams as MarginLayoutParams
|
||||||
mlpScrollAbout.leftMargin = leftInsets
|
mlpScrollAbout.leftMargin = leftInsets
|
||||||
|
@ -10,7 +10,6 @@ import android.content.DialogInterface
|
|||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.content.pm.ActivityInfo
|
import android.content.pm.ActivityInfo
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import android.graphics.Color
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
@ -155,7 +154,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
|||||||
}
|
}
|
||||||
|
|
||||||
binding.surfaceEmulation.holder.addCallback(this)
|
binding.surfaceEmulation.holder.addCallback(this)
|
||||||
binding.showFpsText.setTextColor(Color.YELLOW)
|
|
||||||
binding.doneControlConfig.setOnClickListener { stopConfiguringControls() }
|
binding.doneControlConfig.setOnClickListener { stopConfiguringControls() }
|
||||||
|
|
||||||
binding.drawerLayout.addDrawerListener(object : DrawerListener {
|
binding.drawerLayout.addDrawerListener(object : DrawerListener {
|
||||||
@ -312,6 +310,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
|||||||
ViewUtils.showView(binding.surfaceInputOverlay)
|
ViewUtils.showView(binding.surfaceInputOverlay)
|
||||||
ViewUtils.hideView(binding.loadingIndicator)
|
ViewUtils.hideView(binding.loadingIndicator)
|
||||||
|
|
||||||
|
emulationState.updateSurface()
|
||||||
|
|
||||||
// Setup overlay
|
// Setup overlay
|
||||||
updateShowFpsOverlay()
|
updateShowFpsOverlay()
|
||||||
}
|
}
|
||||||
@ -412,12 +412,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
|||||||
val FRAMETIME = 2
|
val FRAMETIME = 2
|
||||||
val SPEED = 3
|
val SPEED = 3
|
||||||
perfStatsUpdater = {
|
perfStatsUpdater = {
|
||||||
if (emulationViewModel.emulationStarted.value == true) {
|
if (emulationViewModel.emulationStarted.value) {
|
||||||
val perfStats = NativeLibrary.getPerfStats()
|
val perfStats = NativeLibrary.getPerfStats()
|
||||||
if (perfStats[FPS] > 0 && _binding != null) {
|
if (_binding != null) {
|
||||||
binding.showFpsText.text = String.format("FPS: %.1f", perfStats[FPS])
|
binding.showFpsText.text = String.format("FPS: %.1f", perfStats[FPS])
|
||||||
}
|
}
|
||||||
perfStatsUpdateHandler.postDelayed(perfStatsUpdater!!, 100)
|
perfStatsUpdateHandler.postDelayed(perfStatsUpdater!!, 800)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
perfStatsUpdateHandler.post(perfStatsUpdater!!)
|
perfStatsUpdateHandler.post(perfStatsUpdater!!)
|
||||||
@ -462,7 +462,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
|||||||
if (it.orientation == FoldingFeature.Orientation.HORIZONTAL) {
|
if (it.orientation == FoldingFeature.Orientation.HORIZONTAL) {
|
||||||
// Restrict emulation and overlays to the top of the screen
|
// Restrict emulation and overlays to the top of the screen
|
||||||
binding.emulationContainer.layoutParams.height = it.bounds.top
|
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
|
// Restrict input and menu drawer to the bottom of the screen
|
||||||
binding.inputContainer.layoutParams.height = it.bounds.bottom
|
binding.inputContainer.layoutParams.height = it.bounds.bottom
|
||||||
binding.inGameMenu.layoutParams.height = it.bounds.bottom
|
binding.inGameMenu.layoutParams.height = it.bounds.bottom
|
||||||
@ -476,7 +475,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
|||||||
if (!isFolding) {
|
if (!isFolding) {
|
||||||
binding.emulationContainer.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT
|
binding.emulationContainer.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT
|
||||||
binding.inputContainer.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
|
binding.inGameMenu.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT
|
||||||
isInFoldableLayout = false
|
isInFoldableLayout = false
|
||||||
updateOrientation()
|
updateOrientation()
|
||||||
@ -484,7 +482,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
|||||||
}
|
}
|
||||||
binding.emulationContainer.requestLayout()
|
binding.emulationContainer.requestLayout()
|
||||||
binding.inputContainer.requestLayout()
|
binding.inputContainer.requestLayout()
|
||||||
binding.overlayContainer.requestLayout()
|
|
||||||
binding.inGameMenu.requestLayout()
|
binding.inGameMenu.requestLayout()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -710,24 +707,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
|||||||
}
|
}
|
||||||
|
|
||||||
v.setPadding(left, cutInsets.top, right, 0)
|
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
|
windowInsets
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -804,6 +783,13 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
fun updateSurface() {
|
||||||
|
if (surface != null) {
|
||||||
|
NativeLibrary.surfaceChanged(surface)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
fun clearSurface() {
|
fun clearSurface() {
|
||||||
if (surface == null) {
|
if (surface == null) {
|
||||||
|
@ -40,8 +40,10 @@ class SettingsSearchFragment : Fragment() {
|
|||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
enterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false)
|
enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
|
||||||
returnTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true)
|
returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
|
||||||
|
reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
|
||||||
|
exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
@ -55,7 +57,6 @@ class SettingsSearchFragment : Fragment() {
|
|||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
settingsViewModel.setIsUsingSearch(true)
|
|
||||||
|
|
||||||
if (savedInstanceState != null) {
|
if (savedInstanceState != null) {
|
||||||
binding.searchText.setText(savedInstanceState.getString(SEARCH_TEXT))
|
binding.searchText.setText(savedInstanceState.getString(SEARCH_TEXT))
|
||||||
|
@ -29,9 +29,6 @@ class SettingsViewModel : ViewModel() {
|
|||||||
val shouldReloadSettingsList: StateFlow<Boolean> get() = _shouldReloadSettingsList
|
val shouldReloadSettingsList: StateFlow<Boolean> get() = _shouldReloadSettingsList
|
||||||
private val _shouldReloadSettingsList = MutableStateFlow(false)
|
private val _shouldReloadSettingsList = MutableStateFlow(false)
|
||||||
|
|
||||||
val isUsingSearch: StateFlow<Boolean> get() = _isUsingSearch
|
|
||||||
private val _isUsingSearch = MutableStateFlow(false)
|
|
||||||
|
|
||||||
val sliderProgress: StateFlow<Int> get() = _sliderProgress
|
val sliderProgress: StateFlow<Int> get() = _sliderProgress
|
||||||
private val _sliderProgress = MutableStateFlow(-1)
|
private val _sliderProgress = MutableStateFlow(-1)
|
||||||
|
|
||||||
@ -57,10 +54,6 @@ class SettingsViewModel : ViewModel() {
|
|||||||
_shouldReloadSettingsList.value = value
|
_shouldReloadSettingsList.value = value
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setIsUsingSearch(value: Boolean) {
|
|
||||||
_isUsingSearch.value = value
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setSliderTextValue(value: Float, units: String) {
|
fun setSliderTextValue(value: Float, units: String) {
|
||||||
_sliderProgress.value = value.toInt()
|
_sliderProgress.value = value.toInt()
|
||||||
_sliderTextValue.value = String.format(
|
_sliderTextValue.value = String.format(
|
||||||
|
@ -403,7 +403,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
|||||||
} else {
|
} else {
|
||||||
firmwarePath.deleteRecursively()
|
firmwarePath.deleteRecursively()
|
||||||
cacheFirmwareDir.copyRecursively(firmwarePath, true)
|
cacheFirmwareDir.copyRecursively(firmwarePath, true)
|
||||||
NativeLibrary.initializeSystem()
|
NativeLibrary.initializeSystem(true)
|
||||||
getString(R.string.save_file_imported_success)
|
getString(R.string.save_file_imported_success)
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
@ -632,6 +632,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Clear existing user data
|
// Clear existing user data
|
||||||
|
NativeConfig.unloadConfig()
|
||||||
File(DirectoryInitialization.userDirectory!!).deleteRecursively()
|
File(DirectoryInitialization.userDirectory!!).deleteRecursively()
|
||||||
|
|
||||||
// Copy archive to internal storage
|
// Copy archive to internal storage
|
||||||
@ -649,7 +650,8 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Reinitialize relevant data
|
// Reinitialize relevant data
|
||||||
NativeLibrary.initializeSystem()
|
NativeLibrary.initializeSystem(true)
|
||||||
|
NativeConfig.initializeConfig()
|
||||||
gamesViewModel.reloadGames(false)
|
gamesViewModel.reloadGames(false)
|
||||||
|
|
||||||
return@newInstance getString(R.string.user_data_import_success)
|
return@newInstance getString(R.string.user_data_import_success)
|
||||||
|
@ -15,7 +15,8 @@ object DirectoryInitialization {
|
|||||||
fun start() {
|
fun start() {
|
||||||
if (!areDirectoriesReady) {
|
if (!areDirectoriesReady) {
|
||||||
initializeInternalStorage()
|
initializeInternalStorage()
|
||||||
NativeLibrary.initializeSystem()
|
NativeLibrary.initializeSystem(false)
|
||||||
|
NativeConfig.initializeConfig()
|
||||||
areDirectoriesReady = true
|
areDirectoriesReady = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,9 +8,9 @@ import android.graphics.BitmapFactory
|
|||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import androidx.core.graphics.drawable.toBitmap
|
import androidx.core.graphics.drawable.toBitmap
|
||||||
import androidx.core.graphics.drawable.toDrawable
|
import androidx.core.graphics.drawable.toDrawable
|
||||||
|
import androidx.lifecycle.LifecycleOwner
|
||||||
import coil.ImageLoader
|
import coil.ImageLoader
|
||||||
import coil.decode.DataSource
|
import coil.decode.DataSource
|
||||||
import coil.executeBlocking
|
|
||||||
import coil.fetch.DrawableResult
|
import coil.fetch.DrawableResult
|
||||||
import coil.fetch.FetchResult
|
import coil.fetch.FetchResult
|
||||||
import coil.fetch.Fetcher
|
import coil.fetch.Fetcher
|
||||||
@ -76,12 +76,13 @@ object GameIconUtils {
|
|||||||
imageLoader.enqueue(request)
|
imageLoader.enqueue(request)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getGameIcon(game: Game): Bitmap {
|
suspend fun getGameIcon(lifecycleOwner: LifecycleOwner, game: Game): Bitmap {
|
||||||
val request = ImageRequest.Builder(YuzuApplication.appContext)
|
val request = ImageRequest.Builder(YuzuApplication.appContext)
|
||||||
.data(game)
|
.data(game)
|
||||||
|
.lifecycle(lifecycleOwner)
|
||||||
.error(R.drawable.default_icon)
|
.error(R.drawable.default_icon)
|
||||||
.build()
|
.build()
|
||||||
return imageLoader.executeBlocking(request)
|
return imageLoader.execute(request)
|
||||||
.drawable!!.toBitmap(config = Bitmap.Config.ARGB_8888)
|
.drawable!!.toBitmap(config = Bitmap.Config.ARGB_8888)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
package org.yuzu.yuzu_emu.utils
|
package org.yuzu.yuzu_emu.utils
|
||||||
|
|
||||||
|
import android.os.Build
|
||||||
|
|
||||||
object Log {
|
object Log {
|
||||||
// Tracks whether we should share the old log or the current log
|
// Tracks whether we should share the old log or the current log
|
||||||
var gameLaunched = false
|
var gameLaunched = false
|
||||||
@ -16,4 +18,14 @@ object Log {
|
|||||||
external fun error(message: String)
|
external fun error(message: String)
|
||||||
|
|
||||||
external fun critical(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()}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ object MemoryUtil {
|
|||||||
const val Pb = Tb * 1024
|
const val Pb = Tb * 1024
|
||||||
const val Eb = Pb * 1024
|
const val Eb = Pb * 1024
|
||||||
|
|
||||||
private fun bytesToSizeUnit(size: Float): String =
|
private fun bytesToSizeUnit(size: Float, roundUp: Boolean = false): String =
|
||||||
when {
|
when {
|
||||||
size < Kb -> {
|
size < Kb -> {
|
||||||
context.getString(
|
context.getString(
|
||||||
@ -39,63 +39,59 @@ object MemoryUtil {
|
|||||||
size < Mb -> {
|
size < Mb -> {
|
||||||
context.getString(
|
context.getString(
|
||||||
R.string.memory_formatted,
|
R.string.memory_formatted,
|
||||||
(size / Kb).hundredths,
|
if (roundUp) ceil(size / Kb) else (size / Kb).hundredths,
|
||||||
context.getString(R.string.memory_kilobyte)
|
context.getString(R.string.memory_kilobyte)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
size < Gb -> {
|
size < Gb -> {
|
||||||
context.getString(
|
context.getString(
|
||||||
R.string.memory_formatted,
|
R.string.memory_formatted,
|
||||||
(size / Mb).hundredths,
|
if (roundUp) ceil(size / Mb) else (size / Mb).hundredths,
|
||||||
context.getString(R.string.memory_megabyte)
|
context.getString(R.string.memory_megabyte)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
size < Tb -> {
|
size < Tb -> {
|
||||||
context.getString(
|
context.getString(
|
||||||
R.string.memory_formatted,
|
R.string.memory_formatted,
|
||||||
(size / Gb).hundredths,
|
if (roundUp) ceil(size / Gb) else (size / Gb).hundredths,
|
||||||
context.getString(R.string.memory_gigabyte)
|
context.getString(R.string.memory_gigabyte)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
size < Pb -> {
|
size < Pb -> {
|
||||||
context.getString(
|
context.getString(
|
||||||
R.string.memory_formatted,
|
R.string.memory_formatted,
|
||||||
(size / Tb).hundredths,
|
if (roundUp) ceil(size / Tb) else (size / Tb).hundredths,
|
||||||
context.getString(R.string.memory_terabyte)
|
context.getString(R.string.memory_terabyte)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
size < Eb -> {
|
size < Eb -> {
|
||||||
context.getString(
|
context.getString(
|
||||||
R.string.memory_formatted,
|
R.string.memory_formatted,
|
||||||
(size / Pb).hundredths,
|
if (roundUp) ceil(size / Pb) else (size / Pb).hundredths,
|
||||||
context.getString(R.string.memory_petabyte)
|
context.getString(R.string.memory_petabyte)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
context.getString(
|
context.getString(
|
||||||
R.string.memory_formatted,
|
R.string.memory_formatted,
|
||||||
(size / Eb).hundredths,
|
if (roundUp) ceil(size / Eb) else (size / Eb).hundredths,
|
||||||
context.getString(R.string.memory_exabyte)
|
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
|
val totalMemory: Float
|
||||||
// the potential error created by memInfo.totalMem
|
|
||||||
private val totalMemory: Float
|
|
||||||
get() {
|
get() {
|
||||||
val memInfo = ActivityManager.MemoryInfo()
|
val memInfo = ActivityManager.MemoryInfo()
|
||||||
with(context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager) {
|
with(context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager) {
|
||||||
getMemoryInfo(memInfo)
|
getMemoryInfo(memInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ceil(
|
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
memInfo.advertisedMem.toFloat()
|
||||||
memInfo.advertisedMem.toFloat()
|
} else {
|
||||||
} else {
|
memInfo.totalMem.toFloat()
|
||||||
memInfo.totalMem.toFloat()
|
}
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isLessThan(minimum: Int, size: Float): Boolean =
|
fun isLessThan(minimum: Int, size: Float): Boolean =
|
||||||
@ -109,5 +105,7 @@ object MemoryUtil {
|
|||||||
else -> totalMemory < Kb && totalMemory < minimum
|
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)
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,30 @@
|
|||||||
package org.yuzu.yuzu_emu.utils
|
package org.yuzu.yuzu_emu.utils
|
||||||
|
|
||||||
object NativeConfig {
|
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 getBoolean(key: String, getDefault: Boolean): Boolean
|
||||||
external fun setBoolean(key: String, value: Boolean)
|
external fun setBoolean(key: String, value: Boolean)
|
||||||
|
|
||||||
|
@ -6,9 +6,6 @@ add_library(yuzu-android SHARED
|
|||||||
android_common/android_common.h
|
android_common/android_common.h
|
||||||
applets/software_keyboard.cpp
|
applets/software_keyboard.cpp
|
||||||
applets/software_keyboard.h
|
applets/software_keyboard.h
|
||||||
config.cpp
|
|
||||||
config.h
|
|
||||||
default_ini.h
|
|
||||||
emu_window/emu_window.cpp
|
emu_window/emu_window.cpp
|
||||||
emu_window/emu_window.h
|
emu_window/emu_window.h
|
||||||
id_cache.cpp
|
id_cache.cpp
|
||||||
@ -16,15 +13,17 @@ add_library(yuzu-android SHARED
|
|||||||
native.cpp
|
native.cpp
|
||||||
native.h
|
native.h
|
||||||
native_config.cpp
|
native_config.cpp
|
||||||
uisettings.cpp
|
android_settings.cpp
|
||||||
game_metadata.cpp
|
game_metadata.cpp
|
||||||
native_log.cpp
|
native_log.cpp
|
||||||
|
android_config.cpp
|
||||||
|
android_config.h
|
||||||
)
|
)
|
||||||
|
|
||||||
set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR})
|
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 audio_core common core input_common frontend_common)
|
||||||
target_link_libraries(yuzu-android PRIVATE android camera2ndk EGL glad inih jnigraphics log)
|
target_link_libraries(yuzu-android PRIVATE android camera2ndk EGL glad jnigraphics log)
|
||||||
if (ARCHITECTURE_arm64)
|
if (ARCHITECTURE_arm64)
|
||||||
target_link_libraries(yuzu-android PRIVATE adrenotools)
|
target_link_libraries(yuzu-android PRIVATE adrenotools)
|
||||||
endif()
|
endif()
|
||||||
|
70
src/android/app/src/main/jni/android_config.cpp
Normal file
70
src/android/app/src/main/jni/android_config.cpp
Normal 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];
|
||||||
|
}
|
41
src/android/app/src/main/jni/android_config.h
Normal file
41
src/android/app/src/main/jni/android_config.h
Normal 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;
|
||||||
|
};
|
@ -1,7 +1,7 @@
|
|||||||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include "uisettings.h"
|
#include "android_settings.h"
|
||||||
|
|
||||||
namespace AndroidSettings {
|
namespace AndroidSettings {
|
||||||
|
|
@ -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();
|
|
||||||
}
|
|
@ -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;
|
|
||||||
};
|
|
@ -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
|
|
@ -9,6 +9,7 @@
|
|||||||
#include "input_common/drivers/virtual_gamepad.h"
|
#include "input_common/drivers/virtual_gamepad.h"
|
||||||
#include "input_common/main.h"
|
#include "input_common/main.h"
|
||||||
#include "jni/emu_window/emu_window.h"
|
#include "jni/emu_window/emu_window.h"
|
||||||
|
#include "jni/native.h"
|
||||||
|
|
||||||
void EmuWindow_Android::OnSurfaceChanged(ANativeWindow* surface) {
|
void EmuWindow_Android::OnSurfaceChanged(ANativeWindow* surface) {
|
||||||
m_window_width = ANativeWindow_getWidth(surface);
|
m_window_width = ANativeWindow_getWidth(surface);
|
||||||
@ -57,6 +58,13 @@ void EmuWindow_Android::OnRemoveNfcTag() {
|
|||||||
m_input_subsystem->GetVirtualAmiibo()->CloseAmiibo();
|
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,
|
EmuWindow_Android::EmuWindow_Android(InputCommon::InputSubsystem* input_subsystem,
|
||||||
ANativeWindow* surface,
|
ANativeWindow* surface,
|
||||||
std::shared_ptr<Common::DynamicLibrary> driver_library)
|
std::shared_ptr<Common::DynamicLibrary> driver_library)
|
||||||
|
@ -45,7 +45,7 @@ public:
|
|||||||
float gyro_z, float accel_x, float accel_y, float accel_z);
|
float gyro_z, float accel_x, float accel_y, float accel_z);
|
||||||
void OnReadNfcTag(std::span<u8> data);
|
void OnReadNfcTag(std::span<u8> data);
|
||||||
void OnRemoveNfcTag();
|
void OnRemoveNfcTag();
|
||||||
void OnFrameDisplayed() override {}
|
void OnFrameDisplayed() override;
|
||||||
|
|
||||||
std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override {
|
std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override {
|
||||||
return {std::make_unique<GraphicsContext_Android>(m_driver_library)};
|
return {std::make_unique<GraphicsContext_Android>(m_driver_library)};
|
||||||
@ -61,4 +61,6 @@ private:
|
|||||||
float m_window_height{};
|
float m_window_height{};
|
||||||
|
|
||||||
std::shared_ptr<Common::DynamicLibrary> m_driver_library;
|
std::shared_ptr<Common::DynamicLibrary> m_driver_library;
|
||||||
|
|
||||||
|
bool m_first_frame = false;
|
||||||
};
|
};
|
||||||
|
@ -52,8 +52,8 @@
|
|||||||
#include "core/hle/service/am/applets/applets.h"
|
#include "core/hle/service/am/applets/applets.h"
|
||||||
#include "core/hle/service/filesystem/filesystem.h"
|
#include "core/hle/service/filesystem/filesystem.h"
|
||||||
#include "core/loader/loader.h"
|
#include "core/loader/loader.h"
|
||||||
|
#include "frontend_common/config.h"
|
||||||
#include "jni/android_common/android_common.h"
|
#include "jni/android_common/android_common.h"
|
||||||
#include "jni/config.h"
|
|
||||||
#include "jni/id_cache.h"
|
#include "jni/id_cache.h"
|
||||||
#include "jni/native.h"
|
#include "jni/native.h"
|
||||||
#include "video_core/renderer_base.h"
|
#include "video_core/renderer_base.h"
|
||||||
@ -199,8 +199,8 @@ bool EmulationSession::IsPaused() const {
|
|||||||
return m_is_running && m_is_paused;
|
return m_is_running && m_is_paused;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Core::PerfStatsResults& EmulationSession::PerfStats() const {
|
const Core::PerfStatsResults& EmulationSession::PerfStats() {
|
||||||
std::scoped_lock m_perf_stats_lock(m_perf_stats_mutex);
|
m_perf_stats = m_system.GetAndResetPerfStats();
|
||||||
return m_perf_stats;
|
return m_perf_stats;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -247,11 +247,13 @@ void EmulationSession::ConfigureFilesystemProvider(const std::string& filepath)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmulationSession::InitializeSystem() {
|
void EmulationSession::InitializeSystem(bool reload) {
|
||||||
// Initialize logging system
|
if (!reload) {
|
||||||
Common::Log::Initialize();
|
// Initialize logging system
|
||||||
Common::Log::SetColorConsoleBackendEnabled(true);
|
Common::Log::Initialize();
|
||||||
Common::Log::Start();
|
Common::Log::SetColorConsoleBackendEnabled(true);
|
||||||
|
Common::Log::Start();
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize filesystem.
|
// Initialize filesystem.
|
||||||
m_system.SetFilesystem(m_vfs);
|
m_system.SetFilesystem(m_vfs);
|
||||||
@ -370,8 +372,6 @@ void EmulationSession::RunEmulation() {
|
|||||||
m_system.InitializeDebugger();
|
m_system.InitializeDebugger();
|
||||||
}
|
}
|
||||||
|
|
||||||
OnEmulationStarted();
|
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
{
|
{
|
||||||
[[maybe_unused]] std::unique_lock lock(m_mutex);
|
[[maybe_unused]] std::unique_lock lock(m_mutex);
|
||||||
@ -381,11 +381,6 @@ void EmulationSession::RunEmulation() {
|
|||||||
break;
|
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) {
|
void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeSystem(JNIEnv* env, jclass clazz,
|
||||||
// Create the default config.ini.
|
jboolean reload) {
|
||||||
Config{};
|
|
||||||
// Initialize the emulated system.
|
// Initialize the emulated system.
|
||||||
EmulationSession::GetInstance().System().Initialize();
|
if (!reload) {
|
||||||
EmulationSession::GetInstance().InitializeSystem();
|
EmulationSession::GetInstance().System().Initialize();
|
||||||
|
}
|
||||||
|
EmulationSession::GetInstance().InitializeSystem(reload);
|
||||||
}
|
}
|
||||||
|
|
||||||
jint Java_org_yuzu_yuzu_1emu_NativeLibrary_defaultCPUCore(JNIEnv* env, jclass clazz) {
|
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(
|
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) {}
|
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 Java_org_yuzu_yuzu_1emu_NativeLibrary_getPerfStats(JNIEnv* env, jclass clazz) {
|
||||||
jdoubleArray j_stats = env->NewDoubleArray(4);
|
jdoubleArray j_stats = env->NewDoubleArray(4);
|
||||||
|
|
||||||
|
@ -41,9 +41,9 @@ public:
|
|||||||
void RunEmulation();
|
void RunEmulation();
|
||||||
void ShutdownEmulation();
|
void ShutdownEmulation();
|
||||||
|
|
||||||
const Core::PerfStatsResults& PerfStats() const;
|
const Core::PerfStatsResults& PerfStats();
|
||||||
void ConfigureFilesystemProvider(const std::string& filepath);
|
void ConfigureFilesystemProvider(const std::string& filepath);
|
||||||
void InitializeSystem();
|
void InitializeSystem(bool reload);
|
||||||
Core::SystemResultStatus InitializeEmulation(const std::string& filepath);
|
Core::SystemResultStatus InitializeEmulation(const std::string& filepath);
|
||||||
|
|
||||||
bool IsHandheldOnly();
|
bool IsHandheldOnly();
|
||||||
@ -52,9 +52,10 @@ public:
|
|||||||
void OnGamepadDisconnectEvent([[maybe_unused]] int index);
|
void OnGamepadDisconnectEvent([[maybe_unused]] int index);
|
||||||
SoftwareKeyboard::AndroidKeyboard* SoftwareKeyboard();
|
SoftwareKeyboard::AndroidKeyboard* SoftwareKeyboard();
|
||||||
|
|
||||||
|
static void OnEmulationStarted();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, int max);
|
static void LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, int max);
|
||||||
static void OnEmulationStarted();
|
|
||||||
static void OnEmulationStopped(Core::SystemResultStatus result);
|
static void OnEmulationStopped(Core::SystemResultStatus result);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -80,6 +81,5 @@ private:
|
|||||||
|
|
||||||
// Synchronization
|
// Synchronization
|
||||||
std::condition_variable_any m_cv;
|
std::condition_variable_any m_cv;
|
||||||
mutable std::mutex m_perf_stats_mutex;
|
|
||||||
mutable std::mutex m_mutex;
|
mutable std::mutex m_mutex;
|
||||||
};
|
};
|
||||||
|
@ -5,11 +5,14 @@
|
|||||||
|
|
||||||
#include <jni.h>
|
#include <jni.h>
|
||||||
|
|
||||||
|
#include "android_config.h"
|
||||||
|
#include "android_settings.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
|
#include "frontend_common/config.h"
|
||||||
#include "jni/android_common/android_common.h"
|
#include "jni/android_common/android_common.h"
|
||||||
#include "jni/config.h"
|
|
||||||
#include "uisettings.h"
|
std::unique_ptr<AndroidConfig> config;
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
Settings::Setting<T>* getSetting(JNIEnv* env, jstring jkey) {
|
Settings::Setting<T>* getSetting(JNIEnv* env, jstring jkey) {
|
||||||
@ -28,6 +31,22 @@ Settings::Setting<T>* getSetting(JNIEnv* env, jstring jkey) {
|
|||||||
|
|
||||||
extern "C" {
|
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,
|
jboolean Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getBoolean(JNIEnv* env, jobject obj,
|
||||||
jstring jkey, jboolean getDefault) {
|
jstring jkey, jboolean getDefault) {
|
||||||
auto setting = getSetting<bool>(env, jkey);
|
auto setting = getSetting<bool>(env, jkey);
|
||||||
|
9
src/android/app/src/main/res/drawable/ic_audio.xml
Normal file
9
src/android/app/src/main/res/drawable/ic_audio.xml
Normal 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>
|
9
src/android/app/src/main/res/drawable/ic_code.xml
Normal file
9
src/android/app/src/main/res/drawable/ic_code.xml
Normal 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>
|
9
src/android/app/src/main/res/drawable/ic_graphics.xml
Normal file
9
src/android/app/src/main/res/drawable/ic_graphics.xml
Normal 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>
|
@ -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>
|
233
src/android/app/src/main/res/layout-w600dp/fragment_about.xml
Normal file
233
src/android/app/src/main/res/layout-w600dp/fragment_about.xml
Normal 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>
|
@ -6,8 +6,8 @@
|
|||||||
android:id="@+id/option_card"
|
android:id="@+id/option_card"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginVertical="12dp"
|
android:layout_marginBottom="24dp"
|
||||||
android:layout_marginHorizontal="16dp"
|
android:layout_marginHorizontal="12dp"
|
||||||
android:background="?attr/selectableItemBackground"
|
android:background="?attr/selectableItemBackground"
|
||||||
android:backgroundTint="?attr/colorSurfaceVariant"
|
android:backgroundTint="?attr/colorSurfaceVariant"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
|
@ -38,17 +38,17 @@
|
|||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/image_logo"
|
android:id="@+id/image_logo"
|
||||||
android:layout_width="250dp"
|
android:layout_width="150dp"
|
||||||
android:layout_height="250dp"
|
android:layout_height="150dp"
|
||||||
android:layout_marginTop="20dp"
|
android:layout_marginTop="24dp"
|
||||||
|
android:layout_marginBottom="28dp"
|
||||||
android:layout_gravity="center_horizontal"
|
android:layout_gravity="center_horizontal"
|
||||||
android:src="@drawable/ic_yuzu_title" />
|
android:src="@drawable/ic_yuzu_title" />
|
||||||
|
|
||||||
<com.google.android.material.divider.MaterialDivider
|
<com.google.android.material.divider.MaterialDivider
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginHorizontal="20dp"
|
android:layout_marginHorizontal="20dp" />
|
||||||
android:layout_marginTop="28dp" />
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@ -134,18 +134,21 @@
|
|||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:id="@+id/overlay_container"
|
android:id="@+id/overlay_container"
|
||||||
android:layout_width="match_parent"
|
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"
|
android:id="@+id/show_fps_text"
|
||||||
|
style="@style/TextAppearance.Material3.BodySmall"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="left"
|
android:layout_gravity="left"
|
||||||
android:clickable="false"
|
android:clickable="false"
|
||||||
android:focusable="false"
|
android:focusable="false"
|
||||||
android:shadowColor="@android:color/black"
|
android:paddingHorizontal="20dp"
|
||||||
android:textColor="@android:color/white"
|
android:textColor="@android:color/white"
|
||||||
android:textSize="12sp"
|
android:shadowColor="@android:color/black"
|
||||||
|
android:shadowRadius="3"
|
||||||
tools:ignore="RtlHardcoded" />
|
tools:ignore="RtlHardcoded" />
|
||||||
|
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
@ -14,13 +14,14 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:background="?attr/colorSurface">
|
android:background="?attr/colorSurface"
|
||||||
|
android:paddingHorizontal="8dp">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/logo_image"
|
android:id="@+id/logo_image"
|
||||||
android:layout_width="128dp"
|
android:layout_width="96dp"
|
||||||
android:layout_height="128dp"
|
android:layout_height="96dp"
|
||||||
android:layout_margin="64dp"
|
android:layout_marginVertical="32dp"
|
||||||
android:layout_gravity="center_horizontal"
|
android:layout_gravity="center_horizontal"
|
||||||
android:src="@drawable/ic_yuzu_full" />
|
android:src="@drawable/ic_yuzu_full" />
|
||||||
|
|
||||||
|
@ -10,41 +10,59 @@
|
|||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
android:minHeight="72dp"
|
android:minHeight="72dp"
|
||||||
android:padding="@dimen/spacing_large">
|
android:padding="16dp">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical">
|
android:orientation="horizontal">
|
||||||
|
|
||||||
<com.google.android.material.textview.MaterialTextView
|
<ImageView
|
||||||
android:id="@+id/text_setting_name"
|
android:id="@+id/icon"
|
||||||
style="@style/TextAppearance.Material3.HeadlineMedium"
|
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_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:textAlignment="viewStart"
|
android:orientation="vertical">
|
||||||
android:textSize="16sp"
|
|
||||||
app:lineHeight="22dp"
|
|
||||||
tools:text="Setting Name" />
|
|
||||||
|
|
||||||
<com.google.android.material.textview.MaterialTextView
|
<com.google.android.material.textview.MaterialTextView
|
||||||
android:id="@+id/text_setting_description"
|
android:id="@+id/text_setting_name"
|
||||||
style="@style/TextAppearance.Material3.BodySmall"
|
style="@style/TextAppearance.Material3.HeadlineMedium"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="@dimen/spacing_small"
|
android:textAlignment="viewStart"
|
||||||
android:textAlignment="viewStart"
|
android:textSize="17sp"
|
||||||
tools:text="@string/app_disclaimer" />
|
app:lineHeight="22dp"
|
||||||
|
tools:text="Setting Name" />
|
||||||
|
|
||||||
<com.google.android.material.textview.MaterialTextView
|
<com.google.android.material.textview.MaterialTextView
|
||||||
android:id="@+id/text_setting_value"
|
android:id="@+id/text_setting_description"
|
||||||
style="@style/TextAppearance.Material3.LabelMedium"
|
style="@style/TextAppearance.Material3.BodySmall"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="@dimen/spacing_small"
|
android:layout_marginTop="@dimen/spacing_small"
|
||||||
android:textAlignment="viewStart"
|
android:textAlignment="viewStart"
|
||||||
android:textStyle="bold"
|
tools:text="@string/app_disclaimer" />
|
||||||
tools:text="1x" />
|
|
||||||
|
<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>
|
</LinearLayout>
|
||||||
|
|
||||||
|
@ -8,9 +8,7 @@
|
|||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:minHeight="72dp"
|
android:minHeight="72dp"
|
||||||
android:paddingVertical="@dimen/spacing_large"
|
android:padding="16dp">
|
||||||
android:paddingStart="@dimen/spacing_large"
|
|
||||||
android:paddingEnd="24dp">
|
|
||||||
|
|
||||||
<com.google.android.material.materialswitch.MaterialSwitch
|
<com.google.android.material.materialswitch.MaterialSwitch
|
||||||
android:id="@+id/switch_widget"
|
android:id="@+id/switch_widget"
|
||||||
@ -24,7 +22,7 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_alignParentTop="true"
|
android:layout_alignParentTop="true"
|
||||||
android:layout_centerVertical="true"
|
android:layout_centerVertical="true"
|
||||||
android:layout_marginEnd="@dimen/spacing_large"
|
android:layout_marginEnd="24dp"
|
||||||
android:layout_toStartOf="@+id/switch_widget"
|
android:layout_toStartOf="@+id/switch_widget"
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
@ -35,7 +33,7 @@
|
|||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:textAlignment="viewStart"
|
android:textAlignment="viewStart"
|
||||||
android:textSize="16sp"
|
android:textSize="17sp"
|
||||||
app:lineHeight="28dp"
|
app:lineHeight="28dp"
|
||||||
tools:text="@string/frame_limit_enable" />
|
tools:text="@string/frame_limit_enable" />
|
||||||
|
|
||||||
|
@ -7,7 +7,8 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="start|center_vertical"
|
android:layout_gravity="start|center_vertical"
|
||||||
android:paddingHorizontal="@dimen/spacing_large"
|
android:paddingHorizontal="@dimen/spacing_large"
|
||||||
android:paddingVertical="16dp"
|
android:paddingTop="16dp"
|
||||||
|
android:paddingBottom="8dp"
|
||||||
android:textAlignment="viewStart"
|
android:textAlignment="viewStart"
|
||||||
android:textColor="?attr/colorPrimary"
|
android:textColor="?attr/colorPrimary"
|
||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
<resources>
|
<resources>
|
||||||
|
|
||||||
<string-array name="regionNames">
|
<string-array name="regionNames">
|
||||||
<item>@string/auto</item>
|
|
||||||
<item>@string/region_australia</item>
|
<item>@string/region_australia</item>
|
||||||
<item>@string/region_china</item>
|
<item>@string/region_china</item>
|
||||||
<item>@string/region_europe</item>
|
<item>@string/region_europe</item>
|
||||||
@ -13,7 +12,6 @@
|
|||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
<integer-array name="regionValues">
|
<integer-array name="regionValues">
|
||||||
<item>-1</item>
|
|
||||||
<item>3</item>
|
<item>3</item>
|
||||||
<item>4</item>
|
<item>4</item>
|
||||||
<item>2</item>
|
<item>2</item>
|
||||||
|
@ -240,6 +240,7 @@
|
|||||||
<string name="shutting_down">Shutting down…</string>
|
<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_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">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">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="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>
|
<string name="settings_reset">Settings reset</string>
|
||||||
@ -271,10 +272,14 @@
|
|||||||
<string name="preferences_settings">Settings</string>
|
<string name="preferences_settings">Settings</string>
|
||||||
<string name="preferences_general">General</string>
|
<string name="preferences_general">General</string>
|
||||||
<string name="preferences_system">System</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">Graphics</string>
|
||||||
|
<string name="preferences_graphics_description">Accuracy level, resolution, shader cache</string>
|
||||||
<string name="preferences_audio">Audio</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_theme">Theme and color</string>
|
||||||
<string name="preferences_debug">Debug</string>
|
<string name="preferences_debug">Debug</string>
|
||||||
|
<string name="preferences_debug_description">CPU/GPU debugging, graphics API, fastmem</string>
|
||||||
|
|
||||||
<!-- ROM loading errors -->
|
<!-- ROM loading errors -->
|
||||||
<string name="loader_error_encrypted">Your ROM is encrypted</string>
|
<string name="loader_error_encrypted">Your ROM is encrypted</string>
|
||||||
|
@ -1,107 +1,107 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include "audio_core/adsp/apps/opus/opus_decode_object.h"
|
#include "audio_core/adsp/apps/opus/opus_decode_object.h"
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
|
|
||||||
namespace AudioCore::ADSP::OpusDecoder {
|
namespace AudioCore::ADSP::OpusDecoder {
|
||||||
namespace {
|
namespace {
|
||||||
bool IsValidChannelCount(u32 channel_count) {
|
bool IsValidChannelCount(u32 channel_count) {
|
||||||
return channel_count == 1 || channel_count == 2;
|
return channel_count == 1 || channel_count == 2;
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
u32 OpusDecodeObject::GetWorkBufferSize(u32 channel_count) {
|
u32 OpusDecodeObject::GetWorkBufferSize(u32 channel_count) {
|
||||||
if (!IsValidChannelCount(channel_count)) {
|
if (!IsValidChannelCount(channel_count)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return static_cast<u32>(sizeof(OpusDecodeObject)) + opus_decoder_get_size(channel_count);
|
return static_cast<u32>(sizeof(OpusDecodeObject)) + opus_decoder_get_size(channel_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
OpusDecodeObject& OpusDecodeObject::Initialize(u64 buffer, u64 buffer2) {
|
OpusDecodeObject& OpusDecodeObject::Initialize(u64 buffer, u64 buffer2) {
|
||||||
auto* new_decoder = reinterpret_cast<OpusDecodeObject*>(buffer);
|
auto* new_decoder = reinterpret_cast<OpusDecodeObject*>(buffer);
|
||||||
auto* comparison = reinterpret_cast<OpusDecodeObject*>(buffer2);
|
auto* comparison = reinterpret_cast<OpusDecodeObject*>(buffer2);
|
||||||
|
|
||||||
if (new_decoder->magic == DecodeObjectMagic) {
|
if (new_decoder->magic == DecodeObjectMagic) {
|
||||||
if (!new_decoder->initialized ||
|
if (!new_decoder->initialized ||
|
||||||
(new_decoder->initialized && new_decoder->self == comparison)) {
|
(new_decoder->initialized && new_decoder->self == comparison)) {
|
||||||
new_decoder->state_valid = true;
|
new_decoder->state_valid = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
new_decoder->initialized = false;
|
new_decoder->initialized = false;
|
||||||
new_decoder->state_valid = true;
|
new_decoder->state_valid = true;
|
||||||
}
|
}
|
||||||
return *new_decoder;
|
return *new_decoder;
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 OpusDecodeObject::InitializeDecoder(u32 sample_rate, u32 channel_count) {
|
s32 OpusDecodeObject::InitializeDecoder(u32 sample_rate, u32 channel_count) {
|
||||||
if (!state_valid) {
|
if (!state_valid) {
|
||||||
return OPUS_INVALID_STATE;
|
return OPUS_INVALID_STATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (initialized) {
|
if (initialized) {
|
||||||
return OPUS_OK;
|
return OPUS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unfortunately libopus does not expose the OpusDecoder struct publicly, so we can't include
|
// 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
|
// it in this class. Nintendo does not allocate memory, which is why we have a workbuffer
|
||||||
// provided.
|
// provided.
|
||||||
// We could use _create and have libopus allocate it for us, but then we have to separately
|
// 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
|
// track which decoder is being used between this and multistream in order to call the correct
|
||||||
// destroy from the host side.
|
// destroy from the host side.
|
||||||
// This is a bit cringe, but is safe as these objects are only ever initialized inside the given
|
// 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.
|
// workbuffer, and GetWorkBufferSize will guarantee there's enough space to follow.
|
||||||
decoder = (LibOpusDecoder*)(this + 1);
|
decoder = (LibOpusDecoder*)(this + 1);
|
||||||
s32 ret = opus_decoder_init(decoder, sample_rate, channel_count);
|
s32 ret = opus_decoder_init(decoder, sample_rate, channel_count);
|
||||||
if (ret == OPUS_OK) {
|
if (ret == OPUS_OK) {
|
||||||
magic = DecodeObjectMagic;
|
magic = DecodeObjectMagic;
|
||||||
initialized = true;
|
initialized = true;
|
||||||
state_valid = true;
|
state_valid = true;
|
||||||
self = this;
|
self = this;
|
||||||
final_range = 0;
|
final_range = 0;
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 OpusDecodeObject::Shutdown() {
|
s32 OpusDecodeObject::Shutdown() {
|
||||||
if (!state_valid) {
|
if (!state_valid) {
|
||||||
return OPUS_INVALID_STATE;
|
return OPUS_INVALID_STATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (initialized) {
|
if (initialized) {
|
||||||
magic = 0x0;
|
magic = 0x0;
|
||||||
initialized = false;
|
initialized = false;
|
||||||
state_valid = false;
|
state_valid = false;
|
||||||
self = nullptr;
|
self = nullptr;
|
||||||
final_range = 0;
|
final_range = 0;
|
||||||
decoder = nullptr;
|
decoder = nullptr;
|
||||||
}
|
}
|
||||||
return OPUS_OK;
|
return OPUS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 OpusDecodeObject::ResetDecoder() {
|
s32 OpusDecodeObject::ResetDecoder() {
|
||||||
return opus_decoder_ctl(decoder, OPUS_RESET_STATE);
|
return opus_decoder_ctl(decoder, OPUS_RESET_STATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 OpusDecodeObject::Decode(u32& out_sample_count, u64 output_data, u64 output_data_size,
|
s32 OpusDecodeObject::Decode(u32& out_sample_count, u64 output_data, u64 output_data_size,
|
||||||
u64 input_data, u64 input_data_size) {
|
u64 input_data, u64 input_data_size) {
|
||||||
ASSERT(initialized);
|
ASSERT(initialized);
|
||||||
out_sample_count = 0;
|
out_sample_count = 0;
|
||||||
|
|
||||||
if (!state_valid) {
|
if (!state_valid) {
|
||||||
return OPUS_INVALID_STATE;
|
return OPUS_INVALID_STATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto ret_code_or_samples = opus_decode(
|
auto ret_code_or_samples = opus_decode(
|
||||||
decoder, reinterpret_cast<const u8*>(input_data), static_cast<opus_int32>(input_data_size),
|
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);
|
reinterpret_cast<opus_int16*>(output_data), static_cast<opus_int32>(output_data_size), 0);
|
||||||
|
|
||||||
if (ret_code_or_samples < OPUS_OK) {
|
if (ret_code_or_samples < OPUS_OK) {
|
||||||
return ret_code_or_samples;
|
return ret_code_or_samples;
|
||||||
}
|
}
|
||||||
|
|
||||||
out_sample_count = ret_code_or_samples;
|
out_sample_count = ret_code_or_samples;
|
||||||
return opus_decoder_ctl(decoder, OPUS_GET_FINAL_RANGE_REQUEST, &final_range);
|
return opus_decoder_ctl(decoder, OPUS_GET_FINAL_RANGE_REQUEST, &final_range);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace AudioCore::ADSP::OpusDecoder
|
} // namespace AudioCore::ADSP::OpusDecoder
|
||||||
|
@ -30,9 +30,9 @@ bool IsValidMultiStreamChannelCount(u32 channel_count) {
|
|||||||
return channel_count <= OpusStreamCountMax;
|
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 &&
|
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
|
} // namespace
|
||||||
|
|
||||||
|
@ -1,111 +1,111 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include "audio_core/adsp/apps/opus/opus_multistream_decode_object.h"
|
#include "audio_core/adsp/apps/opus/opus_multistream_decode_object.h"
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
|
|
||||||
namespace AudioCore::ADSP::OpusDecoder {
|
namespace AudioCore::ADSP::OpusDecoder {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
bool IsValidChannelCount(u32 channel_count) {
|
bool IsValidChannelCount(u32 channel_count) {
|
||||||
return channel_count == 1 || channel_count == 2;
|
return channel_count == 1 || channel_count == 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsValidStreamCounts(u32 total_stream_count, u32 stereo_stream_count) {
|
bool IsValidStreamCounts(u32 total_stream_count, u32 stereo_stream_count) {
|
||||||
return total_stream_count > 0 && stereo_stream_count > 0 &&
|
return total_stream_count > 0 && stereo_stream_count > 0 &&
|
||||||
stereo_stream_count <= total_stream_count && IsValidChannelCount(total_stream_count);
|
stereo_stream_count <= total_stream_count && IsValidChannelCount(total_stream_count);
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
u32 OpusMultiStreamDecodeObject::GetWorkBufferSize(u32 total_stream_count,
|
u32 OpusMultiStreamDecodeObject::GetWorkBufferSize(u32 total_stream_count,
|
||||||
u32 stereo_stream_count) {
|
u32 stereo_stream_count) {
|
||||||
if (IsValidStreamCounts(total_stream_count, stereo_stream_count)) {
|
if (IsValidStreamCounts(total_stream_count, stereo_stream_count)) {
|
||||||
return static_cast<u32>(sizeof(OpusMultiStreamDecodeObject)) +
|
return static_cast<u32>(sizeof(OpusMultiStreamDecodeObject)) +
|
||||||
opus_multistream_decoder_get_size(total_stream_count, stereo_stream_count);
|
opus_multistream_decoder_get_size(total_stream_count, stereo_stream_count);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
OpusMultiStreamDecodeObject& OpusMultiStreamDecodeObject::Initialize(u64 buffer, u64 buffer2) {
|
OpusMultiStreamDecodeObject& OpusMultiStreamDecodeObject::Initialize(u64 buffer, u64 buffer2) {
|
||||||
auto* new_decoder = reinterpret_cast<OpusMultiStreamDecodeObject*>(buffer);
|
auto* new_decoder = reinterpret_cast<OpusMultiStreamDecodeObject*>(buffer);
|
||||||
auto* comparison = reinterpret_cast<OpusMultiStreamDecodeObject*>(buffer2);
|
auto* comparison = reinterpret_cast<OpusMultiStreamDecodeObject*>(buffer2);
|
||||||
|
|
||||||
if (new_decoder->magic == DecodeMultiStreamObjectMagic) {
|
if (new_decoder->magic == DecodeMultiStreamObjectMagic) {
|
||||||
if (!new_decoder->initialized ||
|
if (!new_decoder->initialized ||
|
||||||
(new_decoder->initialized && new_decoder->self == comparison)) {
|
(new_decoder->initialized && new_decoder->self == comparison)) {
|
||||||
new_decoder->state_valid = true;
|
new_decoder->state_valid = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
new_decoder->initialized = false;
|
new_decoder->initialized = false;
|
||||||
new_decoder->state_valid = true;
|
new_decoder->state_valid = true;
|
||||||
}
|
}
|
||||||
return *new_decoder;
|
return *new_decoder;
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 OpusMultiStreamDecodeObject::InitializeDecoder(u32 sample_rate, u32 total_stream_count,
|
s32 OpusMultiStreamDecodeObject::InitializeDecoder(u32 sample_rate, u32 total_stream_count,
|
||||||
u32 channel_count, u32 stereo_stream_count,
|
u32 channel_count, u32 stereo_stream_count,
|
||||||
u8* mappings) {
|
u8* mappings) {
|
||||||
if (!state_valid) {
|
if (!state_valid) {
|
||||||
return OPUS_INVALID_STATE;
|
return OPUS_INVALID_STATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (initialized) {
|
if (initialized) {
|
||||||
return OPUS_OK;
|
return OPUS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
// See OpusDecodeObject::InitializeDecoder for an explanation of this
|
// See OpusDecodeObject::InitializeDecoder for an explanation of this
|
||||||
decoder = (LibOpusMSDecoder*)(this + 1);
|
decoder = (LibOpusMSDecoder*)(this + 1);
|
||||||
s32 ret = opus_multistream_decoder_init(decoder, sample_rate, channel_count, total_stream_count,
|
s32 ret = opus_multistream_decoder_init(decoder, sample_rate, channel_count, total_stream_count,
|
||||||
stereo_stream_count, mappings);
|
stereo_stream_count, mappings);
|
||||||
if (ret == OPUS_OK) {
|
if (ret == OPUS_OK) {
|
||||||
magic = DecodeMultiStreamObjectMagic;
|
magic = DecodeMultiStreamObjectMagic;
|
||||||
initialized = true;
|
initialized = true;
|
||||||
state_valid = true;
|
state_valid = true;
|
||||||
self = this;
|
self = this;
|
||||||
final_range = 0;
|
final_range = 0;
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 OpusMultiStreamDecodeObject::Shutdown() {
|
s32 OpusMultiStreamDecodeObject::Shutdown() {
|
||||||
if (!state_valid) {
|
if (!state_valid) {
|
||||||
return OPUS_INVALID_STATE;
|
return OPUS_INVALID_STATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (initialized) {
|
if (initialized) {
|
||||||
magic = 0x0;
|
magic = 0x0;
|
||||||
initialized = false;
|
initialized = false;
|
||||||
state_valid = false;
|
state_valid = false;
|
||||||
self = nullptr;
|
self = nullptr;
|
||||||
final_range = 0;
|
final_range = 0;
|
||||||
decoder = nullptr;
|
decoder = nullptr;
|
||||||
}
|
}
|
||||||
return OPUS_OK;
|
return OPUS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 OpusMultiStreamDecodeObject::ResetDecoder() {
|
s32 OpusMultiStreamDecodeObject::ResetDecoder() {
|
||||||
return opus_multistream_decoder_ctl(decoder, OPUS_RESET_STATE);
|
return opus_multistream_decoder_ctl(decoder, OPUS_RESET_STATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 OpusMultiStreamDecodeObject::Decode(u32& out_sample_count, u64 output_data,
|
s32 OpusMultiStreamDecodeObject::Decode(u32& out_sample_count, u64 output_data,
|
||||||
u64 output_data_size, u64 input_data, u64 input_data_size) {
|
u64 output_data_size, u64 input_data, u64 input_data_size) {
|
||||||
ASSERT(initialized);
|
ASSERT(initialized);
|
||||||
out_sample_count = 0;
|
out_sample_count = 0;
|
||||||
|
|
||||||
if (!state_valid) {
|
if (!state_valid) {
|
||||||
return OPUS_INVALID_STATE;
|
return OPUS_INVALID_STATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto ret_code_or_samples = opus_multistream_decode(
|
auto ret_code_or_samples = opus_multistream_decode(
|
||||||
decoder, reinterpret_cast<const u8*>(input_data), static_cast<opus_int32>(input_data_size),
|
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);
|
reinterpret_cast<opus_int16*>(output_data), static_cast<opus_int32>(output_data_size), 0);
|
||||||
|
|
||||||
if (ret_code_or_samples < OPUS_OK) {
|
if (ret_code_or_samples < OPUS_OK) {
|
||||||
return ret_code_or_samples;
|
return ret_code_or_samples;
|
||||||
}
|
}
|
||||||
|
|
||||||
out_sample_count = ret_code_or_samples;
|
out_sample_count = ret_code_or_samples;
|
||||||
return opus_multistream_decoder_ctl(decoder, OPUS_GET_FINAL_RANGE_REQUEST, &final_range);
|
return opus_multistream_decoder_ctl(decoder, OPUS_GET_FINAL_RANGE_REQUEST, &final_range);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace AudioCore::ADSP::OpusDecoder
|
} // namespace AudioCore::ADSP::OpusDecoder
|
||||||
|
@ -1,179 +1,179 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include "audio_core/opus/decoder.h"
|
#include "audio_core/opus/decoder.h"
|
||||||
#include "audio_core/opus/hardware_opus.h"
|
#include "audio_core/opus/hardware_opus.h"
|
||||||
#include "audio_core/opus/parameters.h"
|
#include "audio_core/opus/parameters.h"
|
||||||
#include "common/alignment.h"
|
#include "common/alignment.h"
|
||||||
#include "common/swap.h"
|
#include "common/swap.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
|
|
||||||
namespace AudioCore::OpusDecoder {
|
namespace AudioCore::OpusDecoder {
|
||||||
using namespace Service::Audio;
|
using namespace Service::Audio;
|
||||||
namespace {
|
namespace {
|
||||||
OpusPacketHeader ReverseHeader(OpusPacketHeader header) {
|
OpusPacketHeader ReverseHeader(OpusPacketHeader header) {
|
||||||
OpusPacketHeader out;
|
OpusPacketHeader out;
|
||||||
out.size = Common::swap32(header.size);
|
out.size = Common::swap32(header.size);
|
||||||
out.final_range = Common::swap32(header.final_range);
|
out.final_range = Common::swap32(header.final_range);
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
OpusDecoder::OpusDecoder(Core::System& system_, HardwareOpus& hardware_opus_)
|
OpusDecoder::OpusDecoder(Core::System& system_, HardwareOpus& hardware_opus_)
|
||||||
: system{system_}, hardware_opus{hardware_opus_} {}
|
: system{system_}, hardware_opus{hardware_opus_} {}
|
||||||
|
|
||||||
OpusDecoder::~OpusDecoder() {
|
OpusDecoder::~OpusDecoder() {
|
||||||
if (decode_object_initialized) {
|
if (decode_object_initialized) {
|
||||||
hardware_opus.ShutdownDecodeObject(shared_buffer.get(), shared_buffer_size);
|
hardware_opus.ShutdownDecodeObject(shared_buffer.get(), shared_buffer_size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Result OpusDecoder::Initialize(OpusParametersEx& params, Kernel::KTransferMemory* transfer_memory,
|
Result OpusDecoder::Initialize(OpusParametersEx& params, Kernel::KTransferMemory* transfer_memory,
|
||||||
u64 transfer_memory_size) {
|
u64 transfer_memory_size) {
|
||||||
auto frame_size{params.use_large_frame_size ? 5760 : 1920};
|
auto frame_size{params.use_large_frame_size ? 5760 : 1920};
|
||||||
shared_buffer_size = transfer_memory_size;
|
shared_buffer_size = transfer_memory_size;
|
||||||
shared_buffer = std::make_unique<u8[]>(shared_buffer_size);
|
shared_buffer = std::make_unique<u8[]>(shared_buffer_size);
|
||||||
shared_memory_mapped = true;
|
shared_memory_mapped = true;
|
||||||
|
|
||||||
buffer_size =
|
buffer_size =
|
||||||
Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 16);
|
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};
|
out_data = {shared_buffer.get() + shared_buffer_size - buffer_size, buffer_size};
|
||||||
size_t in_data_size{0x600u};
|
size_t in_data_size{0x600u};
|
||||||
in_data = {out_data.data() - in_data_size, in_data_size};
|
in_data = {out_data.data() - in_data_size, in_data_size};
|
||||||
|
|
||||||
ON_RESULT_FAILURE {
|
ON_RESULT_FAILURE {
|
||||||
if (shared_memory_mapped) {
|
if (shared_memory_mapped) {
|
||||||
shared_memory_mapped = false;
|
shared_memory_mapped = false;
|
||||||
ASSERT(R_SUCCEEDED(hardware_opus.UnmapMemory(shared_buffer.get(), shared_buffer_size)));
|
ASSERT(R_SUCCEEDED(hardware_opus.UnmapMemory(shared_buffer.get(), shared_buffer_size)));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
R_TRY(hardware_opus.InitializeDecodeObject(params.sample_rate, params.channel_count,
|
R_TRY(hardware_opus.InitializeDecodeObject(params.sample_rate, params.channel_count,
|
||||||
shared_buffer.get(), shared_buffer_size));
|
shared_buffer.get(), shared_buffer_size));
|
||||||
|
|
||||||
sample_rate = params.sample_rate;
|
sample_rate = params.sample_rate;
|
||||||
channel_count = params.channel_count;
|
channel_count = params.channel_count;
|
||||||
use_large_frame_size = params.use_large_frame_size;
|
use_large_frame_size = params.use_large_frame_size;
|
||||||
decode_object_initialized = true;
|
decode_object_initialized = true;
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result OpusDecoder::Initialize(OpusMultiStreamParametersEx& params,
|
Result OpusDecoder::Initialize(OpusMultiStreamParametersEx& params,
|
||||||
Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size) {
|
Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size) {
|
||||||
auto frame_size{params.use_large_frame_size ? 5760 : 1920};
|
auto frame_size{params.use_large_frame_size ? 5760 : 1920};
|
||||||
shared_buffer_size = transfer_memory_size;
|
shared_buffer_size = transfer_memory_size;
|
||||||
shared_buffer = std::make_unique<u8[]>(shared_buffer_size);
|
shared_buffer = std::make_unique<u8[]>(shared_buffer_size);
|
||||||
shared_memory_mapped = true;
|
shared_memory_mapped = true;
|
||||||
|
|
||||||
buffer_size =
|
buffer_size =
|
||||||
Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 16);
|
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};
|
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)};
|
size_t in_data_size{Common::AlignUp(1500ull * params.total_stream_count, 64u)};
|
||||||
in_data = {out_data.data() - in_data_size, in_data_size};
|
in_data = {out_data.data() - in_data_size, in_data_size};
|
||||||
|
|
||||||
ON_RESULT_FAILURE {
|
ON_RESULT_FAILURE {
|
||||||
if (shared_memory_mapped) {
|
if (shared_memory_mapped) {
|
||||||
shared_memory_mapped = false;
|
shared_memory_mapped = false;
|
||||||
ASSERT(R_SUCCEEDED(hardware_opus.UnmapMemory(shared_buffer.get(), shared_buffer_size)));
|
ASSERT(R_SUCCEEDED(hardware_opus.UnmapMemory(shared_buffer.get(), shared_buffer_size)));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
R_TRY(hardware_opus.InitializeMultiStreamDecodeObject(
|
R_TRY(hardware_opus.InitializeMultiStreamDecodeObject(
|
||||||
params.sample_rate, params.channel_count, params.total_stream_count,
|
params.sample_rate, params.channel_count, params.total_stream_count,
|
||||||
params.stereo_stream_count, params.mappings.data(), shared_buffer.get(),
|
params.stereo_stream_count, params.mappings.data(), shared_buffer.get(),
|
||||||
shared_buffer_size));
|
shared_buffer_size));
|
||||||
|
|
||||||
sample_rate = params.sample_rate;
|
sample_rate = params.sample_rate;
|
||||||
channel_count = params.channel_count;
|
channel_count = params.channel_count;
|
||||||
total_stream_count = params.total_stream_count;
|
total_stream_count = params.total_stream_count;
|
||||||
stereo_stream_count = params.stereo_stream_count;
|
stereo_stream_count = params.stereo_stream_count;
|
||||||
use_large_frame_size = params.use_large_frame_size;
|
use_large_frame_size = params.use_large_frame_size;
|
||||||
decode_object_initialized = true;
|
decode_object_initialized = true;
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result OpusDecoder::DecodeInterleaved(u32* out_data_size, u64* out_time_taken,
|
Result OpusDecoder::DecodeInterleaved(u32* out_data_size, u64* out_time_taken,
|
||||||
u32* out_sample_count, std::span<const u8> input_data,
|
u32* out_sample_count, std::span<const u8> input_data,
|
||||||
std::span<u8> output_data, bool reset) {
|
std::span<u8> output_data, bool reset) {
|
||||||
u32 out_samples;
|
u32 out_samples;
|
||||||
u64 time_taken{};
|
u64 time_taken{};
|
||||||
|
|
||||||
R_UNLESS(input_data.size_bytes() > sizeof(OpusPacketHeader), ResultInputDataTooSmall);
|
R_UNLESS(input_data.size_bytes() > sizeof(OpusPacketHeader), ResultInputDataTooSmall);
|
||||||
|
|
||||||
auto* header_p{reinterpret_cast<const OpusPacketHeader*>(input_data.data())};
|
auto* header_p{reinterpret_cast<const OpusPacketHeader*>(input_data.data())};
|
||||||
OpusPacketHeader header{ReverseHeader(*header_p)};
|
OpusPacketHeader header{ReverseHeader(*header_p)};
|
||||||
|
|
||||||
R_UNLESS(in_data.size_bytes() >= header.size &&
|
R_UNLESS(in_data.size_bytes() >= header.size &&
|
||||||
header.size + sizeof(OpusPacketHeader) <= input_data.size_bytes(),
|
header.size + sizeof(OpusPacketHeader) <= input_data.size_bytes(),
|
||||||
ResultBufferTooSmall);
|
ResultBufferTooSmall);
|
||||||
|
|
||||||
if (!shared_memory_mapped) {
|
if (!shared_memory_mapped) {
|
||||||
R_TRY(hardware_opus.MapMemory(shared_buffer.get(), shared_buffer_size));
|
R_TRY(hardware_opus.MapMemory(shared_buffer.get(), shared_buffer_size));
|
||||||
shared_memory_mapped = true;
|
shared_memory_mapped = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::memcpy(in_data.data(), input_data.data() + sizeof(OpusPacketHeader), header.size);
|
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(),
|
R_TRY(hardware_opus.DecodeInterleaved(out_samples, out_data.data(), out_data.size_bytes(),
|
||||||
channel_count, in_data.data(), header.size,
|
channel_count, in_data.data(), header.size,
|
||||||
shared_buffer.get(), time_taken, reset));
|
shared_buffer.get(), time_taken, reset));
|
||||||
|
|
||||||
std::memcpy(output_data.data(), out_data.data(), out_samples * channel_count * sizeof(s16));
|
std::memcpy(output_data.data(), out_data.data(), out_samples * channel_count * sizeof(s16));
|
||||||
|
|
||||||
*out_data_size = header.size + sizeof(OpusPacketHeader);
|
*out_data_size = header.size + sizeof(OpusPacketHeader);
|
||||||
*out_sample_count = out_samples;
|
*out_sample_count = out_samples;
|
||||||
if (out_time_taken) {
|
if (out_time_taken) {
|
||||||
*out_time_taken = time_taken / 1000;
|
*out_time_taken = time_taken / 1000;
|
||||||
}
|
}
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result OpusDecoder::SetContext([[maybe_unused]] std::span<const u8> context) {
|
Result OpusDecoder::SetContext([[maybe_unused]] std::span<const u8> context) {
|
||||||
R_SUCCEED_IF(shared_memory_mapped);
|
R_SUCCEED_IF(shared_memory_mapped);
|
||||||
shared_memory_mapped = true;
|
shared_memory_mapped = true;
|
||||||
R_RETURN(hardware_opus.MapMemory(shared_buffer.get(), shared_buffer_size));
|
R_RETURN(hardware_opus.MapMemory(shared_buffer.get(), shared_buffer_size));
|
||||||
}
|
}
|
||||||
|
|
||||||
Result OpusDecoder::DecodeInterleavedForMultiStream(u32* out_data_size, u64* out_time_taken,
|
Result OpusDecoder::DecodeInterleavedForMultiStream(u32* out_data_size, u64* out_time_taken,
|
||||||
u32* out_sample_count,
|
u32* out_sample_count,
|
||||||
std::span<const u8> input_data,
|
std::span<const u8> input_data,
|
||||||
std::span<u8> output_data, bool reset) {
|
std::span<u8> output_data, bool reset) {
|
||||||
u32 out_samples;
|
u32 out_samples;
|
||||||
u64 time_taken{};
|
u64 time_taken{};
|
||||||
|
|
||||||
R_UNLESS(input_data.size_bytes() > sizeof(OpusPacketHeader), ResultInputDataTooSmall);
|
R_UNLESS(input_data.size_bytes() > sizeof(OpusPacketHeader), ResultInputDataTooSmall);
|
||||||
|
|
||||||
auto* header_p{reinterpret_cast<const OpusPacketHeader*>(input_data.data())};
|
auto* header_p{reinterpret_cast<const OpusPacketHeader*>(input_data.data())};
|
||||||
OpusPacketHeader header{ReverseHeader(*header_p)};
|
OpusPacketHeader header{ReverseHeader(*header_p)};
|
||||||
|
|
||||||
LOG_ERROR(Service_Audio, "header size 0x{:X} input data size 0x{:X} in_data size 0x{:X}",
|
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());
|
header.size, input_data.size_bytes(), in_data.size_bytes());
|
||||||
|
|
||||||
R_UNLESS(in_data.size_bytes() >= header.size &&
|
R_UNLESS(in_data.size_bytes() >= header.size &&
|
||||||
header.size + sizeof(OpusPacketHeader) <= input_data.size_bytes(),
|
header.size + sizeof(OpusPacketHeader) <= input_data.size_bytes(),
|
||||||
ResultBufferTooSmall);
|
ResultBufferTooSmall);
|
||||||
|
|
||||||
if (!shared_memory_mapped) {
|
if (!shared_memory_mapped) {
|
||||||
R_TRY(hardware_opus.MapMemory(shared_buffer.get(), shared_buffer_size));
|
R_TRY(hardware_opus.MapMemory(shared_buffer.get(), shared_buffer_size));
|
||||||
shared_memory_mapped = true;
|
shared_memory_mapped = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::memcpy(in_data.data(), input_data.data() + sizeof(OpusPacketHeader), header.size);
|
std::memcpy(in_data.data(), input_data.data() + sizeof(OpusPacketHeader), header.size);
|
||||||
|
|
||||||
R_TRY(hardware_opus.DecodeInterleavedForMultiStream(
|
R_TRY(hardware_opus.DecodeInterleavedForMultiStream(
|
||||||
out_samples, out_data.data(), out_data.size_bytes(), channel_count, in_data.data(),
|
out_samples, out_data.data(), out_data.size_bytes(), channel_count, in_data.data(),
|
||||||
header.size, shared_buffer.get(), time_taken, reset));
|
header.size, shared_buffer.get(), time_taken, reset));
|
||||||
|
|
||||||
std::memcpy(output_data.data(), out_data.data(), out_samples * channel_count * sizeof(s16));
|
std::memcpy(output_data.data(), out_data.data(), out_samples * channel_count * sizeof(s16));
|
||||||
|
|
||||||
*out_data_size = header.size + sizeof(OpusPacketHeader);
|
*out_data_size = header.size + sizeof(OpusPacketHeader);
|
||||||
*out_sample_count = out_samples;
|
*out_sample_count = out_samples;
|
||||||
if (out_time_taken) {
|
if (out_time_taken) {
|
||||||
*out_time_taken = time_taken / 1000;
|
*out_time_taken = time_taken / 1000;
|
||||||
}
|
}
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace AudioCore::OpusDecoder
|
} // namespace AudioCore::OpusDecoder
|
||||||
|
@ -1,53 +1,53 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <span>
|
#include <span>
|
||||||
|
|
||||||
#include "audio_core/opus/parameters.h"
|
#include "audio_core/opus/parameters.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "core/hle/kernel/k_transfer_memory.h"
|
#include "core/hle/kernel/k_transfer_memory.h"
|
||||||
#include "core/hle/service/audio/errors.h"
|
#include "core/hle/service/audio/errors.h"
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
class System;
|
class System;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace AudioCore::OpusDecoder {
|
namespace AudioCore::OpusDecoder {
|
||||||
class HardwareOpus;
|
class HardwareOpus;
|
||||||
|
|
||||||
class OpusDecoder {
|
class OpusDecoder {
|
||||||
public:
|
public:
|
||||||
explicit OpusDecoder(Core::System& system, HardwareOpus& hardware_opus_);
|
explicit OpusDecoder(Core::System& system, HardwareOpus& hardware_opus_);
|
||||||
~OpusDecoder();
|
~OpusDecoder();
|
||||||
|
|
||||||
Result Initialize(OpusParametersEx& params, Kernel::KTransferMemory* transfer_memory,
|
Result Initialize(OpusParametersEx& params, Kernel::KTransferMemory* transfer_memory,
|
||||||
u64 transfer_memory_size);
|
u64 transfer_memory_size);
|
||||||
Result Initialize(OpusMultiStreamParametersEx& params, Kernel::KTransferMemory* transfer_memory,
|
Result Initialize(OpusMultiStreamParametersEx& params, Kernel::KTransferMemory* transfer_memory,
|
||||||
u64 transfer_memory_size);
|
u64 transfer_memory_size);
|
||||||
Result DecodeInterleaved(u32* out_data_size, u64* out_time_taken, u32* out_sample_count,
|
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);
|
std::span<const u8> input_data, std::span<u8> output_data, bool reset);
|
||||||
Result SetContext([[maybe_unused]] std::span<const u8> context);
|
Result SetContext([[maybe_unused]] std::span<const u8> context);
|
||||||
Result DecodeInterleavedForMultiStream(u32* out_data_size, u64* out_time_taken,
|
Result DecodeInterleavedForMultiStream(u32* out_data_size, u64* out_time_taken,
|
||||||
u32* out_sample_count, std::span<const u8> input_data,
|
u32* out_sample_count, std::span<const u8> input_data,
|
||||||
std::span<u8> output_data, bool reset);
|
std::span<u8> output_data, bool reset);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Core::System& system;
|
Core::System& system;
|
||||||
HardwareOpus& hardware_opus;
|
HardwareOpus& hardware_opus;
|
||||||
std::unique_ptr<u8[]> shared_buffer{};
|
std::unique_ptr<u8[]> shared_buffer{};
|
||||||
u64 shared_buffer_size;
|
u64 shared_buffer_size;
|
||||||
std::span<u8> in_data{};
|
std::span<u8> in_data{};
|
||||||
std::span<u8> out_data{};
|
std::span<u8> out_data{};
|
||||||
u64 buffer_size{};
|
u64 buffer_size{};
|
||||||
s32 sample_rate{};
|
s32 sample_rate{};
|
||||||
s32 channel_count{};
|
s32 channel_count{};
|
||||||
bool use_large_frame_size{false};
|
bool use_large_frame_size{false};
|
||||||
s32 total_stream_count{};
|
s32 total_stream_count{};
|
||||||
s32 stereo_stream_count{};
|
s32 stereo_stream_count{};
|
||||||
bool shared_memory_mapped{false};
|
bool shared_memory_mapped{false};
|
||||||
bool decode_object_initialized{false};
|
bool decode_object_initialized{false};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace AudioCore::OpusDecoder
|
} // namespace AudioCore::OpusDecoder
|
||||||
|
@ -1,102 +1,102 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include "audio_core/adsp/apps/opus/opus_decoder.h"
|
#include "audio_core/adsp/apps/opus/opus_decoder.h"
|
||||||
#include "audio_core/opus/decoder_manager.h"
|
#include "audio_core/opus/decoder_manager.h"
|
||||||
#include "common/alignment.h"
|
#include "common/alignment.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
|
|
||||||
namespace AudioCore::OpusDecoder {
|
namespace AudioCore::OpusDecoder {
|
||||||
using namespace Service::Audio;
|
using namespace Service::Audio;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
bool IsValidChannelCount(u32 channel_count) {
|
bool IsValidChannelCount(u32 channel_count) {
|
||||||
return channel_count == 1 || channel_count == 2;
|
return channel_count == 1 || channel_count == 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsValidMultiStreamChannelCount(u32 channel_count) {
|
bool IsValidMultiStreamChannelCount(u32 channel_count) {
|
||||||
return channel_count > 0 && channel_count <= OpusStreamCountMax;
|
return channel_count > 0 && channel_count <= OpusStreamCountMax;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsValidSampleRate(u32 sample_rate) {
|
bool IsValidSampleRate(u32 sample_rate) {
|
||||||
return sample_rate == 8'000 || sample_rate == 12'000 || sample_rate == 16'000 ||
|
return sample_rate == 8'000 || sample_rate == 12'000 || sample_rate == 16'000 ||
|
||||||
sample_rate == 24'000 || sample_rate == 48'000;
|
sample_rate == 24'000 || sample_rate == 48'000;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsValidStreamCount(u32 channel_count, u32 total_stream_count, u32 stereo_stream_count) {
|
bool IsValidStreamCount(u32 channel_count, u32 total_stream_count, u32 stereo_stream_count) {
|
||||||
return total_stream_count > 0 && stereo_stream_count > 0 &&
|
return total_stream_count > 0 && static_cast<s32>(stereo_stream_count) >= 0 &&
|
||||||
stereo_stream_count <= total_stream_count &&
|
stereo_stream_count <= total_stream_count &&
|
||||||
total_stream_count + stereo_stream_count <= channel_count;
|
total_stream_count + stereo_stream_count <= channel_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
OpusDecoderManager::OpusDecoderManager(Core::System& system_)
|
OpusDecoderManager::OpusDecoderManager(Core::System& system_)
|
||||||
: system{system_}, hardware_opus{system} {
|
: system{system_}, hardware_opus{system} {
|
||||||
for (u32 i = 0; i < MaxChannels; i++) {
|
for (u32 i = 0; i < MaxChannels; i++) {
|
||||||
required_workbuffer_sizes[i] = hardware_opus.GetWorkBufferSize(1 + i);
|
required_workbuffer_sizes[i] = hardware_opus.GetWorkBufferSize(1 + i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Result OpusDecoderManager::GetWorkBufferSize(OpusParameters& params, u64& out_size) {
|
Result OpusDecoderManager::GetWorkBufferSize(OpusParameters& params, u64& out_size) {
|
||||||
OpusParametersEx ex{
|
OpusParametersEx ex{
|
||||||
.sample_rate = params.sample_rate,
|
.sample_rate = params.sample_rate,
|
||||||
.channel_count = params.channel_count,
|
.channel_count = params.channel_count,
|
||||||
.use_large_frame_size = false,
|
.use_large_frame_size = false,
|
||||||
};
|
};
|
||||||
R_RETURN(GetWorkBufferSizeExEx(ex, out_size));
|
R_RETURN(GetWorkBufferSizeExEx(ex, out_size));
|
||||||
}
|
}
|
||||||
|
|
||||||
Result OpusDecoderManager::GetWorkBufferSizeEx(OpusParametersEx& params, u64& out_size) {
|
Result OpusDecoderManager::GetWorkBufferSizeEx(OpusParametersEx& params, u64& out_size) {
|
||||||
R_RETURN(GetWorkBufferSizeExEx(params, out_size));
|
R_RETURN(GetWorkBufferSizeExEx(params, out_size));
|
||||||
}
|
}
|
||||||
|
|
||||||
Result OpusDecoderManager::GetWorkBufferSizeExEx(OpusParametersEx& params, u64& out_size) {
|
Result OpusDecoderManager::GetWorkBufferSizeExEx(OpusParametersEx& params, u64& out_size) {
|
||||||
R_UNLESS(IsValidChannelCount(params.channel_count), ResultInvalidOpusChannelCount);
|
R_UNLESS(IsValidChannelCount(params.channel_count), ResultInvalidOpusChannelCount);
|
||||||
R_UNLESS(IsValidSampleRate(params.sample_rate), ResultInvalidOpusSampleRate);
|
R_UNLESS(IsValidSampleRate(params.sample_rate), ResultInvalidOpusSampleRate);
|
||||||
|
|
||||||
auto work_buffer_size{required_workbuffer_sizes[params.channel_count - 1]};
|
auto work_buffer_size{required_workbuffer_sizes[params.channel_count - 1]};
|
||||||
auto frame_size{params.use_large_frame_size ? 5760 : 1920};
|
auto frame_size{params.use_large_frame_size ? 5760 : 1920};
|
||||||
work_buffer_size +=
|
work_buffer_size +=
|
||||||
Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 64);
|
Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 64);
|
||||||
out_size = work_buffer_size + 0x600;
|
out_size = work_buffer_size + 0x600;
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result OpusDecoderManager::GetWorkBufferSizeForMultiStream(OpusMultiStreamParameters& params,
|
Result OpusDecoderManager::GetWorkBufferSizeForMultiStream(OpusMultiStreamParameters& params,
|
||||||
u64& out_size) {
|
u64& out_size) {
|
||||||
OpusMultiStreamParametersEx ex{
|
OpusMultiStreamParametersEx ex{
|
||||||
.sample_rate = params.sample_rate,
|
.sample_rate = params.sample_rate,
|
||||||
.channel_count = params.channel_count,
|
.channel_count = params.channel_count,
|
||||||
.total_stream_count = params.total_stream_count,
|
.total_stream_count = params.total_stream_count,
|
||||||
.stereo_stream_count = params.stereo_stream_count,
|
.stereo_stream_count = params.stereo_stream_count,
|
||||||
.use_large_frame_size = false,
|
.use_large_frame_size = false,
|
||||||
.mappings = {},
|
.mappings = {},
|
||||||
};
|
};
|
||||||
R_RETURN(GetWorkBufferSizeForMultiStreamExEx(ex, out_size));
|
R_RETURN(GetWorkBufferSizeForMultiStreamExEx(ex, out_size));
|
||||||
}
|
}
|
||||||
|
|
||||||
Result OpusDecoderManager::GetWorkBufferSizeForMultiStreamEx(OpusMultiStreamParametersEx& params,
|
Result OpusDecoderManager::GetWorkBufferSizeForMultiStreamEx(OpusMultiStreamParametersEx& params,
|
||||||
u64& out_size) {
|
u64& out_size) {
|
||||||
R_RETURN(GetWorkBufferSizeForMultiStreamExEx(params, out_size));
|
R_RETURN(GetWorkBufferSizeForMultiStreamExEx(params, out_size));
|
||||||
}
|
}
|
||||||
|
|
||||||
Result OpusDecoderManager::GetWorkBufferSizeForMultiStreamExEx(OpusMultiStreamParametersEx& params,
|
Result OpusDecoderManager::GetWorkBufferSizeForMultiStreamExEx(OpusMultiStreamParametersEx& params,
|
||||||
u64& out_size) {
|
u64& out_size) {
|
||||||
R_UNLESS(IsValidMultiStreamChannelCount(params.channel_count), ResultInvalidOpusChannelCount);
|
R_UNLESS(IsValidMultiStreamChannelCount(params.channel_count), ResultInvalidOpusChannelCount);
|
||||||
R_UNLESS(IsValidSampleRate(params.sample_rate), ResultInvalidOpusSampleRate);
|
R_UNLESS(IsValidSampleRate(params.sample_rate), ResultInvalidOpusSampleRate);
|
||||||
R_UNLESS(IsValidStreamCount(params.channel_count, params.total_stream_count,
|
R_UNLESS(IsValidStreamCount(params.channel_count, params.total_stream_count,
|
||||||
params.stereo_stream_count),
|
params.stereo_stream_count),
|
||||||
ResultInvalidOpusSampleRate);
|
ResultInvalidOpusSampleRate);
|
||||||
|
|
||||||
auto work_buffer_size{hardware_opus.GetWorkBufferSizeForMultiStream(
|
auto work_buffer_size{hardware_opus.GetWorkBufferSizeForMultiStream(
|
||||||
params.total_stream_count, params.stereo_stream_count)};
|
params.total_stream_count, params.stereo_stream_count)};
|
||||||
auto frame_size{params.use_large_frame_size ? 5760 : 1920};
|
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(1500 * params.total_stream_count, 64);
|
||||||
work_buffer_size +=
|
work_buffer_size +=
|
||||||
Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 64);
|
Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 64);
|
||||||
out_size = work_buffer_size;
|
out_size = work_buffer_size;
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace AudioCore::OpusDecoder
|
} // namespace AudioCore::OpusDecoder
|
||||||
|
@ -1,38 +1,38 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "audio_core/opus/hardware_opus.h"
|
#include "audio_core/opus/hardware_opus.h"
|
||||||
#include "audio_core/opus/parameters.h"
|
#include "audio_core/opus/parameters.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "core/hle/service/audio/errors.h"
|
#include "core/hle/service/audio/errors.h"
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
class System;
|
class System;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace AudioCore::OpusDecoder {
|
namespace AudioCore::OpusDecoder {
|
||||||
|
|
||||||
class OpusDecoderManager {
|
class OpusDecoderManager {
|
||||||
public:
|
public:
|
||||||
OpusDecoderManager(Core::System& system);
|
OpusDecoderManager(Core::System& system);
|
||||||
|
|
||||||
HardwareOpus& GetHardwareOpus() {
|
HardwareOpus& GetHardwareOpus() {
|
||||||
return hardware_opus;
|
return hardware_opus;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result GetWorkBufferSize(OpusParameters& params, u64& out_size);
|
Result GetWorkBufferSize(OpusParameters& params, u64& out_size);
|
||||||
Result GetWorkBufferSizeEx(OpusParametersEx& params, u64& out_size);
|
Result GetWorkBufferSizeEx(OpusParametersEx& params, u64& out_size);
|
||||||
Result GetWorkBufferSizeExEx(OpusParametersEx& params, u64& out_size);
|
Result GetWorkBufferSizeExEx(OpusParametersEx& params, u64& out_size);
|
||||||
Result GetWorkBufferSizeForMultiStream(OpusMultiStreamParameters& params, u64& out_size);
|
Result GetWorkBufferSizeForMultiStream(OpusMultiStreamParameters& params, u64& out_size);
|
||||||
Result GetWorkBufferSizeForMultiStreamEx(OpusMultiStreamParametersEx& params, u64& out_size);
|
Result GetWorkBufferSizeForMultiStreamEx(OpusMultiStreamParametersEx& params, u64& out_size);
|
||||||
Result GetWorkBufferSizeForMultiStreamExEx(OpusMultiStreamParametersEx& params, u64& out_size);
|
Result GetWorkBufferSizeForMultiStreamExEx(OpusMultiStreamParametersEx& params, u64& out_size);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Core::System& system;
|
Core::System& system;
|
||||||
HardwareOpus hardware_opus;
|
HardwareOpus hardware_opus;
|
||||||
std::array<u64, MaxChannels> required_workbuffer_sizes{};
|
std::array<u64, MaxChannels> required_workbuffer_sizes{};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace AudioCore::OpusDecoder
|
} // namespace AudioCore::OpusDecoder
|
||||||
|
@ -1,241 +1,241 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
|
||||||
#include "audio_core/audio_core.h"
|
#include "audio_core/audio_core.h"
|
||||||
#include "audio_core/opus/hardware_opus.h"
|
#include "audio_core/opus/hardware_opus.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
|
|
||||||
namespace AudioCore::OpusDecoder {
|
namespace AudioCore::OpusDecoder {
|
||||||
namespace {
|
namespace {
|
||||||
using namespace Service::Audio;
|
using namespace Service::Audio;
|
||||||
|
|
||||||
static constexpr Result ResultCodeFromLibOpusErrorCode(u64 error_code) {
|
static constexpr Result ResultCodeFromLibOpusErrorCode(u64 error_code) {
|
||||||
s32 error{static_cast<s32>(error_code)};
|
s32 error{static_cast<s32>(error_code)};
|
||||||
ASSERT(error <= OPUS_OK);
|
ASSERT(error <= OPUS_OK);
|
||||||
switch (error) {
|
switch (error) {
|
||||||
case OPUS_ALLOC_FAIL:
|
case OPUS_ALLOC_FAIL:
|
||||||
R_THROW(ResultLibOpusAllocFail);
|
R_THROW(ResultLibOpusAllocFail);
|
||||||
case OPUS_INVALID_STATE:
|
case OPUS_INVALID_STATE:
|
||||||
R_THROW(ResultLibOpusInvalidState);
|
R_THROW(ResultLibOpusInvalidState);
|
||||||
case OPUS_UNIMPLEMENTED:
|
case OPUS_UNIMPLEMENTED:
|
||||||
R_THROW(ResultLibOpusUnimplemented);
|
R_THROW(ResultLibOpusUnimplemented);
|
||||||
case OPUS_INVALID_PACKET:
|
case OPUS_INVALID_PACKET:
|
||||||
R_THROW(ResultLibOpusInvalidPacket);
|
R_THROW(ResultLibOpusInvalidPacket);
|
||||||
case OPUS_INTERNAL_ERROR:
|
case OPUS_INTERNAL_ERROR:
|
||||||
R_THROW(ResultLibOpusInternalError);
|
R_THROW(ResultLibOpusInternalError);
|
||||||
case OPUS_BUFFER_TOO_SMALL:
|
case OPUS_BUFFER_TOO_SMALL:
|
||||||
R_THROW(ResultBufferTooSmall);
|
R_THROW(ResultBufferTooSmall);
|
||||||
case OPUS_BAD_ARG:
|
case OPUS_BAD_ARG:
|
||||||
R_THROW(ResultLibOpusBadArg);
|
R_THROW(ResultLibOpusBadArg);
|
||||||
case OPUS_OK:
|
case OPUS_OK:
|
||||||
R_RETURN(ResultSuccess);
|
R_RETURN(ResultSuccess);
|
||||||
}
|
}
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
HardwareOpus::HardwareOpus(Core::System& system_)
|
HardwareOpus::HardwareOpus(Core::System& system_)
|
||||||
: system{system_}, opus_decoder{system.AudioCore().ADSP().OpusDecoder()} {
|
: system{system_}, opus_decoder{system.AudioCore().ADSP().OpusDecoder()} {
|
||||||
opus_decoder.SetSharedMemory(shared_memory);
|
opus_decoder.SetSharedMemory(shared_memory);
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 HardwareOpus::GetWorkBufferSize(u32 channel) {
|
u64 HardwareOpus::GetWorkBufferSize(u32 channel) {
|
||||||
if (!opus_decoder.IsRunning()) {
|
if (!opus_decoder.IsRunning()) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
std::scoped_lock l{mutex};
|
std::scoped_lock l{mutex};
|
||||||
shared_memory.host_send_data[0] = channel;
|
shared_memory.host_send_data[0] = channel;
|
||||||
opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::GetWorkBufferSize);
|
opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::GetWorkBufferSize);
|
||||||
auto msg = opus_decoder.Receive(ADSP::Direction::Host);
|
auto msg = opus_decoder.Receive(ADSP::Direction::Host);
|
||||||
if (msg != ADSP::OpusDecoder::Message::GetWorkBufferSizeOK) {
|
if (msg != ADSP::OpusDecoder::Message::GetWorkBufferSizeOK) {
|
||||||
LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
|
LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
|
||||||
ADSP::OpusDecoder::Message::GetWorkBufferSizeOK, msg);
|
ADSP::OpusDecoder::Message::GetWorkBufferSizeOK, msg);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return shared_memory.dsp_return_data[0];
|
return shared_memory.dsp_return_data[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 HardwareOpus::GetWorkBufferSizeForMultiStream(u32 total_stream_count, u32 stereo_stream_count) {
|
u64 HardwareOpus::GetWorkBufferSizeForMultiStream(u32 total_stream_count, u32 stereo_stream_count) {
|
||||||
std::scoped_lock l{mutex};
|
std::scoped_lock l{mutex};
|
||||||
shared_memory.host_send_data[0] = total_stream_count;
|
shared_memory.host_send_data[0] = total_stream_count;
|
||||||
shared_memory.host_send_data[1] = stereo_stream_count;
|
shared_memory.host_send_data[1] = stereo_stream_count;
|
||||||
opus_decoder.Send(ADSP::Direction::DSP,
|
opus_decoder.Send(ADSP::Direction::DSP,
|
||||||
ADSP::OpusDecoder::Message::GetWorkBufferSizeForMultiStream);
|
ADSP::OpusDecoder::Message::GetWorkBufferSizeForMultiStream);
|
||||||
auto msg = opus_decoder.Receive(ADSP::Direction::Host);
|
auto msg = opus_decoder.Receive(ADSP::Direction::Host);
|
||||||
if (msg != ADSP::OpusDecoder::Message::GetWorkBufferSizeForMultiStreamOK) {
|
if (msg != ADSP::OpusDecoder::Message::GetWorkBufferSizeForMultiStreamOK) {
|
||||||
LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
|
LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
|
||||||
ADSP::OpusDecoder::Message::GetWorkBufferSizeForMultiStreamOK, msg);
|
ADSP::OpusDecoder::Message::GetWorkBufferSizeForMultiStreamOK, msg);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return shared_memory.dsp_return_data[0];
|
return shared_memory.dsp_return_data[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
Result HardwareOpus::InitializeDecodeObject(u32 sample_rate, u32 channel_count, void* buffer,
|
Result HardwareOpus::InitializeDecodeObject(u32 sample_rate, u32 channel_count, void* buffer,
|
||||||
u64 buffer_size) {
|
u64 buffer_size) {
|
||||||
std::scoped_lock l{mutex};
|
std::scoped_lock l{mutex};
|
||||||
shared_memory.host_send_data[0] = (u64)buffer;
|
shared_memory.host_send_data[0] = (u64)buffer;
|
||||||
shared_memory.host_send_data[1] = buffer_size;
|
shared_memory.host_send_data[1] = buffer_size;
|
||||||
shared_memory.host_send_data[2] = sample_rate;
|
shared_memory.host_send_data[2] = sample_rate;
|
||||||
shared_memory.host_send_data[3] = channel_count;
|
shared_memory.host_send_data[3] = channel_count;
|
||||||
|
|
||||||
opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::InitializeDecodeObject);
|
opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::InitializeDecodeObject);
|
||||||
auto msg = opus_decoder.Receive(ADSP::Direction::Host);
|
auto msg = opus_decoder.Receive(ADSP::Direction::Host);
|
||||||
if (msg != ADSP::OpusDecoder::Message::InitializeDecodeObjectOK) {
|
if (msg != ADSP::OpusDecoder::Message::InitializeDecodeObjectOK) {
|
||||||
LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
|
LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
|
||||||
ADSP::OpusDecoder::Message::InitializeDecodeObjectOK, msg);
|
ADSP::OpusDecoder::Message::InitializeDecodeObjectOK, msg);
|
||||||
R_THROW(ResultInvalidOpusDSPReturnCode);
|
R_THROW(ResultInvalidOpusDSPReturnCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0]));
|
R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
Result HardwareOpus::InitializeMultiStreamDecodeObject(u32 sample_rate, u32 channel_count,
|
Result HardwareOpus::InitializeMultiStreamDecodeObject(u32 sample_rate, u32 channel_count,
|
||||||
u32 total_stream_count,
|
u32 total_stream_count,
|
||||||
u32 stereo_stream_count, void* mappings,
|
u32 stereo_stream_count, void* mappings,
|
||||||
void* buffer, u64 buffer_size) {
|
void* buffer, u64 buffer_size) {
|
||||||
std::scoped_lock l{mutex};
|
std::scoped_lock l{mutex};
|
||||||
shared_memory.host_send_data[0] = (u64)buffer;
|
shared_memory.host_send_data[0] = (u64)buffer;
|
||||||
shared_memory.host_send_data[1] = buffer_size;
|
shared_memory.host_send_data[1] = buffer_size;
|
||||||
shared_memory.host_send_data[2] = sample_rate;
|
shared_memory.host_send_data[2] = sample_rate;
|
||||||
shared_memory.host_send_data[3] = channel_count;
|
shared_memory.host_send_data[3] = channel_count;
|
||||||
shared_memory.host_send_data[4] = total_stream_count;
|
shared_memory.host_send_data[4] = total_stream_count;
|
||||||
shared_memory.host_send_data[5] = stereo_stream_count;
|
shared_memory.host_send_data[5] = stereo_stream_count;
|
||||||
|
|
||||||
ASSERT(channel_count <= MaxChannels);
|
ASSERT(channel_count <= MaxChannels);
|
||||||
std::memcpy(shared_memory.channel_mapping.data(), mappings, channel_count * sizeof(u8));
|
std::memcpy(shared_memory.channel_mapping.data(), mappings, channel_count * sizeof(u8));
|
||||||
|
|
||||||
opus_decoder.Send(ADSP::Direction::DSP,
|
opus_decoder.Send(ADSP::Direction::DSP,
|
||||||
ADSP::OpusDecoder::Message::InitializeMultiStreamDecodeObject);
|
ADSP::OpusDecoder::Message::InitializeMultiStreamDecodeObject);
|
||||||
auto msg = opus_decoder.Receive(ADSP::Direction::Host);
|
auto msg = opus_decoder.Receive(ADSP::Direction::Host);
|
||||||
if (msg != ADSP::OpusDecoder::Message::InitializeMultiStreamDecodeObjectOK) {
|
if (msg != ADSP::OpusDecoder::Message::InitializeMultiStreamDecodeObjectOK) {
|
||||||
LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
|
LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
|
||||||
ADSP::OpusDecoder::Message::InitializeMultiStreamDecodeObjectOK, msg);
|
ADSP::OpusDecoder::Message::InitializeMultiStreamDecodeObjectOK, msg);
|
||||||
R_THROW(ResultInvalidOpusDSPReturnCode);
|
R_THROW(ResultInvalidOpusDSPReturnCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0]));
|
R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
Result HardwareOpus::ShutdownDecodeObject(void* buffer, u64 buffer_size) {
|
Result HardwareOpus::ShutdownDecodeObject(void* buffer, u64 buffer_size) {
|
||||||
std::scoped_lock l{mutex};
|
std::scoped_lock l{mutex};
|
||||||
shared_memory.host_send_data[0] = (u64)buffer;
|
shared_memory.host_send_data[0] = (u64)buffer;
|
||||||
shared_memory.host_send_data[1] = buffer_size;
|
shared_memory.host_send_data[1] = buffer_size;
|
||||||
|
|
||||||
opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::ShutdownDecodeObject);
|
opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::ShutdownDecodeObject);
|
||||||
auto msg = opus_decoder.Receive(ADSP::Direction::Host);
|
auto msg = opus_decoder.Receive(ADSP::Direction::Host);
|
||||||
ASSERT_MSG(msg == ADSP::OpusDecoder::Message::ShutdownDecodeObjectOK,
|
ASSERT_MSG(msg == ADSP::OpusDecoder::Message::ShutdownDecodeObjectOK,
|
||||||
"Expected Opus shutdown code {}, got {}",
|
"Expected Opus shutdown code {}, got {}",
|
||||||
ADSP::OpusDecoder::Message::ShutdownDecodeObjectOK, msg);
|
ADSP::OpusDecoder::Message::ShutdownDecodeObjectOK, msg);
|
||||||
|
|
||||||
R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0]));
|
R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
Result HardwareOpus::ShutdownMultiStreamDecodeObject(void* buffer, u64 buffer_size) {
|
Result HardwareOpus::ShutdownMultiStreamDecodeObject(void* buffer, u64 buffer_size) {
|
||||||
std::scoped_lock l{mutex};
|
std::scoped_lock l{mutex};
|
||||||
shared_memory.host_send_data[0] = (u64)buffer;
|
shared_memory.host_send_data[0] = (u64)buffer;
|
||||||
shared_memory.host_send_data[1] = buffer_size;
|
shared_memory.host_send_data[1] = buffer_size;
|
||||||
|
|
||||||
opus_decoder.Send(ADSP::Direction::DSP,
|
opus_decoder.Send(ADSP::Direction::DSP,
|
||||||
ADSP::OpusDecoder::Message::ShutdownMultiStreamDecodeObject);
|
ADSP::OpusDecoder::Message::ShutdownMultiStreamDecodeObject);
|
||||||
auto msg = opus_decoder.Receive(ADSP::Direction::Host);
|
auto msg = opus_decoder.Receive(ADSP::Direction::Host);
|
||||||
ASSERT_MSG(msg == ADSP::OpusDecoder::Message::ShutdownMultiStreamDecodeObjectOK,
|
ASSERT_MSG(msg == ADSP::OpusDecoder::Message::ShutdownMultiStreamDecodeObjectOK,
|
||||||
"Expected Opus shutdown code {}, got {}",
|
"Expected Opus shutdown code {}, got {}",
|
||||||
ADSP::OpusDecoder::Message::ShutdownMultiStreamDecodeObjectOK, msg);
|
ADSP::OpusDecoder::Message::ShutdownMultiStreamDecodeObjectOK, msg);
|
||||||
|
|
||||||
R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0]));
|
R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
Result HardwareOpus::DecodeInterleaved(u32& out_sample_count, void* output_data,
|
Result HardwareOpus::DecodeInterleaved(u32& out_sample_count, void* output_data,
|
||||||
u64 output_data_size, u32 channel_count, void* input_data,
|
u64 output_data_size, u32 channel_count, void* input_data,
|
||||||
u64 input_data_size, void* buffer, u64& out_time_taken,
|
u64 input_data_size, void* buffer, u64& out_time_taken,
|
||||||
bool reset) {
|
bool reset) {
|
||||||
std::scoped_lock l{mutex};
|
std::scoped_lock l{mutex};
|
||||||
shared_memory.host_send_data[0] = (u64)buffer;
|
shared_memory.host_send_data[0] = (u64)buffer;
|
||||||
shared_memory.host_send_data[1] = (u64)input_data;
|
shared_memory.host_send_data[1] = (u64)input_data;
|
||||||
shared_memory.host_send_data[2] = input_data_size;
|
shared_memory.host_send_data[2] = input_data_size;
|
||||||
shared_memory.host_send_data[3] = (u64)output_data;
|
shared_memory.host_send_data[3] = (u64)output_data;
|
||||||
shared_memory.host_send_data[4] = output_data_size;
|
shared_memory.host_send_data[4] = output_data_size;
|
||||||
shared_memory.host_send_data[5] = 0;
|
shared_memory.host_send_data[5] = 0;
|
||||||
shared_memory.host_send_data[6] = reset;
|
shared_memory.host_send_data[6] = reset;
|
||||||
|
|
||||||
opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::DecodeInterleaved);
|
opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::DecodeInterleaved);
|
||||||
auto msg = opus_decoder.Receive(ADSP::Direction::Host);
|
auto msg = opus_decoder.Receive(ADSP::Direction::Host);
|
||||||
if (msg != ADSP::OpusDecoder::Message::DecodeInterleavedOK) {
|
if (msg != ADSP::OpusDecoder::Message::DecodeInterleavedOK) {
|
||||||
LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
|
LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
|
||||||
ADSP::OpusDecoder::Message::DecodeInterleavedOK, msg);
|
ADSP::OpusDecoder::Message::DecodeInterleavedOK, msg);
|
||||||
R_THROW(ResultInvalidOpusDSPReturnCode);
|
R_THROW(ResultInvalidOpusDSPReturnCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto error_code{static_cast<s32>(shared_memory.dsp_return_data[0])};
|
auto error_code{static_cast<s32>(shared_memory.dsp_return_data[0])};
|
||||||
if (error_code == OPUS_OK) {
|
if (error_code == OPUS_OK) {
|
||||||
out_sample_count = static_cast<u32>(shared_memory.dsp_return_data[1]);
|
out_sample_count = static_cast<u32>(shared_memory.dsp_return_data[1]);
|
||||||
out_time_taken = 1000 * shared_memory.dsp_return_data[2];
|
out_time_taken = 1000 * shared_memory.dsp_return_data[2];
|
||||||
}
|
}
|
||||||
R_RETURN(ResultCodeFromLibOpusErrorCode(error_code));
|
R_RETURN(ResultCodeFromLibOpusErrorCode(error_code));
|
||||||
}
|
}
|
||||||
|
|
||||||
Result HardwareOpus::DecodeInterleavedForMultiStream(u32& out_sample_count, void* output_data,
|
Result HardwareOpus::DecodeInterleavedForMultiStream(u32& out_sample_count, void* output_data,
|
||||||
u64 output_data_size, u32 channel_count,
|
u64 output_data_size, u32 channel_count,
|
||||||
void* input_data, u64 input_data_size,
|
void* input_data, u64 input_data_size,
|
||||||
void* buffer, u64& out_time_taken,
|
void* buffer, u64& out_time_taken,
|
||||||
bool reset) {
|
bool reset) {
|
||||||
std::scoped_lock l{mutex};
|
std::scoped_lock l{mutex};
|
||||||
shared_memory.host_send_data[0] = (u64)buffer;
|
shared_memory.host_send_data[0] = (u64)buffer;
|
||||||
shared_memory.host_send_data[1] = (u64)input_data;
|
shared_memory.host_send_data[1] = (u64)input_data;
|
||||||
shared_memory.host_send_data[2] = input_data_size;
|
shared_memory.host_send_data[2] = input_data_size;
|
||||||
shared_memory.host_send_data[3] = (u64)output_data;
|
shared_memory.host_send_data[3] = (u64)output_data;
|
||||||
shared_memory.host_send_data[4] = output_data_size;
|
shared_memory.host_send_data[4] = output_data_size;
|
||||||
shared_memory.host_send_data[5] = 0;
|
shared_memory.host_send_data[5] = 0;
|
||||||
shared_memory.host_send_data[6] = reset;
|
shared_memory.host_send_data[6] = reset;
|
||||||
|
|
||||||
opus_decoder.Send(ADSP::Direction::DSP,
|
opus_decoder.Send(ADSP::Direction::DSP,
|
||||||
ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStream);
|
ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStream);
|
||||||
auto msg = opus_decoder.Receive(ADSP::Direction::Host);
|
auto msg = opus_decoder.Receive(ADSP::Direction::Host);
|
||||||
if (msg != ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStreamOK) {
|
if (msg != ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStreamOK) {
|
||||||
LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
|
LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
|
||||||
ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStreamOK, msg);
|
ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStreamOK, msg);
|
||||||
R_THROW(ResultInvalidOpusDSPReturnCode);
|
R_THROW(ResultInvalidOpusDSPReturnCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto error_code{static_cast<s32>(shared_memory.dsp_return_data[0])};
|
auto error_code{static_cast<s32>(shared_memory.dsp_return_data[0])};
|
||||||
if (error_code == OPUS_OK) {
|
if (error_code == OPUS_OK) {
|
||||||
out_sample_count = static_cast<u32>(shared_memory.dsp_return_data[1]);
|
out_sample_count = static_cast<u32>(shared_memory.dsp_return_data[1]);
|
||||||
out_time_taken = 1000 * shared_memory.dsp_return_data[2];
|
out_time_taken = 1000 * shared_memory.dsp_return_data[2];
|
||||||
}
|
}
|
||||||
R_RETURN(ResultCodeFromLibOpusErrorCode(error_code));
|
R_RETURN(ResultCodeFromLibOpusErrorCode(error_code));
|
||||||
}
|
}
|
||||||
|
|
||||||
Result HardwareOpus::MapMemory(void* buffer, u64 buffer_size) {
|
Result HardwareOpus::MapMemory(void* buffer, u64 buffer_size) {
|
||||||
std::scoped_lock l{mutex};
|
std::scoped_lock l{mutex};
|
||||||
shared_memory.host_send_data[0] = (u64)buffer;
|
shared_memory.host_send_data[0] = (u64)buffer;
|
||||||
shared_memory.host_send_data[1] = buffer_size;
|
shared_memory.host_send_data[1] = buffer_size;
|
||||||
|
|
||||||
opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::MapMemory);
|
opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::MapMemory);
|
||||||
auto msg = opus_decoder.Receive(ADSP::Direction::Host);
|
auto msg = opus_decoder.Receive(ADSP::Direction::Host);
|
||||||
if (msg != ADSP::OpusDecoder::Message::MapMemoryOK) {
|
if (msg != ADSP::OpusDecoder::Message::MapMemoryOK) {
|
||||||
LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
|
LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
|
||||||
ADSP::OpusDecoder::Message::MapMemoryOK, msg);
|
ADSP::OpusDecoder::Message::MapMemoryOK, msg);
|
||||||
R_THROW(ResultInvalidOpusDSPReturnCode);
|
R_THROW(ResultInvalidOpusDSPReturnCode);
|
||||||
}
|
}
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result HardwareOpus::UnmapMemory(void* buffer, u64 buffer_size) {
|
Result HardwareOpus::UnmapMemory(void* buffer, u64 buffer_size) {
|
||||||
std::scoped_lock l{mutex};
|
std::scoped_lock l{mutex};
|
||||||
shared_memory.host_send_data[0] = (u64)buffer;
|
shared_memory.host_send_data[0] = (u64)buffer;
|
||||||
shared_memory.host_send_data[1] = buffer_size;
|
shared_memory.host_send_data[1] = buffer_size;
|
||||||
|
|
||||||
opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::UnmapMemory);
|
opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::UnmapMemory);
|
||||||
auto msg = opus_decoder.Receive(ADSP::Direction::Host);
|
auto msg = opus_decoder.Receive(ADSP::Direction::Host);
|
||||||
if (msg != ADSP::OpusDecoder::Message::UnmapMemoryOK) {
|
if (msg != ADSP::OpusDecoder::Message::UnmapMemoryOK) {
|
||||||
LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
|
LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
|
||||||
ADSP::OpusDecoder::Message::UnmapMemoryOK, msg);
|
ADSP::OpusDecoder::Message::UnmapMemoryOK, msg);
|
||||||
R_THROW(ResultInvalidOpusDSPReturnCode);
|
R_THROW(ResultInvalidOpusDSPReturnCode);
|
||||||
}
|
}
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace AudioCore::OpusDecoder
|
} // namespace AudioCore::OpusDecoder
|
||||||
|
@ -1,45 +1,45 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <opus.h>
|
#include <opus.h>
|
||||||
|
|
||||||
#include "audio_core/adsp/apps/opus/opus_decoder.h"
|
#include "audio_core/adsp/apps/opus/opus_decoder.h"
|
||||||
#include "audio_core/adsp/apps/opus/shared_memory.h"
|
#include "audio_core/adsp/apps/opus/shared_memory.h"
|
||||||
#include "audio_core/adsp/mailbox.h"
|
#include "audio_core/adsp/mailbox.h"
|
||||||
#include "core/hle/service/audio/errors.h"
|
#include "core/hle/service/audio/errors.h"
|
||||||
|
|
||||||
namespace AudioCore::OpusDecoder {
|
namespace AudioCore::OpusDecoder {
|
||||||
class HardwareOpus {
|
class HardwareOpus {
|
||||||
public:
|
public:
|
||||||
HardwareOpus(Core::System& system);
|
HardwareOpus(Core::System& system);
|
||||||
|
|
||||||
u64 GetWorkBufferSize(u32 channel);
|
u64 GetWorkBufferSize(u32 channel);
|
||||||
u64 GetWorkBufferSizeForMultiStream(u32 total_stream_count, u32 stereo_stream_count);
|
u64 GetWorkBufferSizeForMultiStream(u32 total_stream_count, u32 stereo_stream_count);
|
||||||
|
|
||||||
Result InitializeDecodeObject(u32 sample_rate, u32 channel_count, void* buffer,
|
Result InitializeDecodeObject(u32 sample_rate, u32 channel_count, void* buffer,
|
||||||
u64 buffer_size);
|
u64 buffer_size);
|
||||||
Result InitializeMultiStreamDecodeObject(u32 sample_rate, u32 channel_count,
|
Result InitializeMultiStreamDecodeObject(u32 sample_rate, u32 channel_count,
|
||||||
u32 totaL_stream_count, u32 stereo_stream_count,
|
u32 totaL_stream_count, u32 stereo_stream_count,
|
||||||
void* mappings, void* buffer, u64 buffer_size);
|
void* mappings, void* buffer, u64 buffer_size);
|
||||||
Result ShutdownDecodeObject(void* buffer, u64 buffer_size);
|
Result ShutdownDecodeObject(void* buffer, u64 buffer_size);
|
||||||
Result ShutdownMultiStreamDecodeObject(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,
|
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,
|
u32 channel_count, void* input_data, u64 input_data_size, void* buffer,
|
||||||
u64& out_time_taken, bool reset);
|
u64& out_time_taken, bool reset);
|
||||||
Result DecodeInterleavedForMultiStream(u32& out_sample_count, void* output_data,
|
Result DecodeInterleavedForMultiStream(u32& out_sample_count, void* output_data,
|
||||||
u64 output_data_size, u32 channel_count,
|
u64 output_data_size, u32 channel_count,
|
||||||
void* input_data, u64 input_data_size, void* buffer,
|
void* input_data, u64 input_data_size, void* buffer,
|
||||||
u64& out_time_taken, bool reset);
|
u64& out_time_taken, bool reset);
|
||||||
Result MapMemory(void* buffer, u64 buffer_size);
|
Result MapMemory(void* buffer, u64 buffer_size);
|
||||||
Result UnmapMemory(void* buffer, u64 buffer_size);
|
Result UnmapMemory(void* buffer, u64 buffer_size);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Core::System& system;
|
Core::System& system;
|
||||||
std::mutex mutex;
|
std::mutex mutex;
|
||||||
ADSP::OpusDecoder::OpusDecoder& opus_decoder;
|
ADSP::OpusDecoder::OpusDecoder& opus_decoder;
|
||||||
ADSP::OpusDecoder::SharedMemory shared_memory;
|
ADSP::OpusDecoder::SharedMemory shared_memory;
|
||||||
};
|
};
|
||||||
} // namespace AudioCore::OpusDecoder
|
} // namespace AudioCore::OpusDecoder
|
||||||
|
@ -146,7 +146,7 @@ public:
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
paused = true;
|
SignalPause();
|
||||||
if (cubeb_stream_stop(stream_backend) != CUBEB_OK) {
|
if (cubeb_stream_stop(stream_backend) != CUBEB_OK) {
|
||||||
LOG_CRITICAL(Audio_Sink, "Error stopping cubeb stream");
|
LOG_CRITICAL(Audio_Sink, "Error stopping cubeb stream");
|
||||||
}
|
}
|
||||||
|
@ -111,7 +111,7 @@ public:
|
|||||||
if (device == 0 || paused) {
|
if (device == 0 || paused) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
paused = true;
|
SignalPause();
|
||||||
SDL_PauseAudioDevice(device, 1);
|
SDL_PauseAudioDevice(device, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -282,11 +282,19 @@ u64 SinkStream::GetExpectedPlayedSampleCount() {
|
|||||||
void SinkStream::WaitFreeSpace(std::stop_token stop_token) {
|
void SinkStream::WaitFreeSpace(std::stop_token stop_token) {
|
||||||
std::unique_lock lk{release_mutex};
|
std::unique_lock lk{release_mutex};
|
||||||
release_cv.wait_for(lk, std::chrono::milliseconds(5),
|
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) {
|
if (queued_buffers > max_queue_size + 3) {
|
||||||
Common::CondvarWait(release_cv, lk, stop_token,
|
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
|
} // namespace AudioCore::Sink
|
||||||
|
@ -213,6 +213,12 @@ public:
|
|||||||
*/
|
*/
|
||||||
void WaitFreeSpace(std::stop_token stop_token);
|
void WaitFreeSpace(std::stop_token stop_token);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/**
|
||||||
|
* Unblocks the ADSP if the stream is paused.
|
||||||
|
*/
|
||||||
|
void SignalPause();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/// Core system
|
/// Core system
|
||||||
Core::System& system;
|
Core::System& system;
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#ifdef ANDROID
|
||||||
|
#include <sys/system_properties.h>
|
||||||
|
#endif
|
||||||
#include "common/arm64/native_clock.h"
|
#include "common/arm64/native_clock.h"
|
||||||
|
|
||||||
namespace Common::Arm64 {
|
namespace Common::Arm64 {
|
||||||
@ -65,7 +68,23 @@ bool NativeClock::IsNative() const {
|
|||||||
|
|
||||||
u64 NativeClock::GetHostCNTFRQ() {
|
u64 NativeClock::GetHostCNTFRQ() {
|
||||||
u64 cntfrq_el0 = 0;
|
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;
|
return cntfrq_el0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,12 +9,12 @@ PageTable::PageTable() = default;
|
|||||||
|
|
||||||
PageTable::~PageTable() noexcept = default;
|
PageTable::~PageTable() noexcept = default;
|
||||||
|
|
||||||
bool PageTable::BeginTraversal(TraversalEntry& out_entry, TraversalContext& out_context,
|
bool PageTable::BeginTraversal(TraversalEntry* out_entry, TraversalContext* out_context,
|
||||||
u64 address) const {
|
Common::ProcessAddress address) const {
|
||||||
// Setup invalid defaults.
|
// Setup invalid defaults.
|
||||||
out_entry.phys_addr = 0;
|
out_entry->phys_addr = 0;
|
||||||
out_entry.block_size = page_size;
|
out_entry->block_size = page_size;
|
||||||
out_context.next_page = 0;
|
out_context->next_page = 0;
|
||||||
|
|
||||||
// Validate that we can read the actual entry.
|
// Validate that we can read the actual entry.
|
||||||
const auto page = address / page_size;
|
const auto page = address / page_size;
|
||||||
@ -29,20 +29,20 @@ bool PageTable::BeginTraversal(TraversalEntry& out_entry, TraversalContext& out_
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Populate the results.
|
// Populate the results.
|
||||||
out_entry.phys_addr = phys_addr + address;
|
out_entry->phys_addr = phys_addr + GetInteger(address);
|
||||||
out_context.next_page = page + 1;
|
out_context->next_page = page + 1;
|
||||||
out_context.next_offset = address + page_size;
|
out_context->next_offset = GetInteger(address) + page_size;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PageTable::ContinueTraversal(TraversalEntry& out_entry, TraversalContext& context) const {
|
bool PageTable::ContinueTraversal(TraversalEntry* out_entry, TraversalContext* context) const {
|
||||||
// Setup invalid defaults.
|
// Setup invalid defaults.
|
||||||
out_entry.phys_addr = 0;
|
out_entry->phys_addr = 0;
|
||||||
out_entry.block_size = page_size;
|
out_entry->block_size = page_size;
|
||||||
|
|
||||||
// Validate that we can read the actual entry.
|
// 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()) {
|
if (page >= backing_addr.size()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -54,9 +54,9 @@ bool PageTable::ContinueTraversal(TraversalEntry& out_entry, TraversalContext& c
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Populate the results.
|
// Populate the results.
|
||||||
out_entry.phys_addr = phys_addr + context.next_offset;
|
out_entry->phys_addr = phys_addr + context->next_offset;
|
||||||
context.next_page = page + 1;
|
context->next_page = page + 1;
|
||||||
context.next_offset += page_size;
|
context->next_offset += page_size;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
#include "common/typed_address.h"
|
||||||
#include "common/virtual_buffer.h"
|
#include "common/virtual_buffer.h"
|
||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
@ -100,9 +101,9 @@ struct PageTable {
|
|||||||
PageTable(PageTable&&) noexcept = default;
|
PageTable(PageTable&&) noexcept = default;
|
||||||
PageTable& operator=(PageTable&&) noexcept = default;
|
PageTable& operator=(PageTable&&) noexcept = default;
|
||||||
|
|
||||||
bool BeginTraversal(TraversalEntry& out_entry, TraversalContext& out_context,
|
bool BeginTraversal(TraversalEntry* out_entry, TraversalContext* out_context,
|
||||||
u64 address) const;
|
Common::ProcessAddress address) const;
|
||||||
bool ContinueTraversal(TraversalEntry& out_entry, TraversalContext& context) const;
|
bool ContinueTraversal(TraversalEntry* out_entry, TraversalContext* context) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resizes the page table to be able to accommodate enough pages within
|
* 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;
|
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
|
* Vector of memory pointers backing each page. An entry can only be non-null if the
|
||||||
* corresponding attribute element is of type `Memory`.
|
* corresponding attribute element is of type `Memory`.
|
||||||
|
@ -203,10 +203,12 @@ const char* TranslateCategory(Category category) {
|
|||||||
case Category::Ui:
|
case Category::Ui:
|
||||||
case Category::UiGeneral:
|
case Category::UiGeneral:
|
||||||
return "UI";
|
return "UI";
|
||||||
|
case Category::UiAudio:
|
||||||
|
return "UiAudio";
|
||||||
case Category::UiLayout:
|
case Category::UiLayout:
|
||||||
return "UiLayout";
|
return "UILayout";
|
||||||
case Category::UiGameList:
|
case Category::UiGameList:
|
||||||
return "UiGameList";
|
return "UIGameList";
|
||||||
case Category::Screenshots:
|
case Category::Screenshots:
|
||||||
return "Screenshots";
|
return "Screenshots";
|
||||||
case Category::Shortcuts:
|
case Category::Shortcuts:
|
||||||
|
@ -153,7 +153,7 @@ struct Values {
|
|||||||
true,
|
true,
|
||||||
true};
|
true};
|
||||||
Setting<bool, false> audio_muted{
|
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{
|
Setting<bool, false> dump_audio_commands{
|
||||||
linkage, false, "dump_audio_commands", Category::Audio, Specialization::Default, false};
|
linkage, false, "dump_audio_commands", Category::Audio, Specialization::Default, false};
|
||||||
|
|
||||||
@ -232,7 +232,11 @@ struct Values {
|
|||||||
SwitchableSetting<bool> use_asynchronous_gpu_emulation{
|
SwitchableSetting<bool> use_asynchronous_gpu_emulation{
|
||||||
linkage, true, "use_asynchronous_gpu_emulation", Category::Renderer};
|
linkage, true, "use_asynchronous_gpu_emulation", Category::Renderer};
|
||||||
SwitchableSetting<AstcDecodeMode, true> accelerate_astc{linkage,
|
SwitchableSetting<AstcDecodeMode, true> accelerate_astc{linkage,
|
||||||
|
#ifdef ANDROID
|
||||||
|
AstcDecodeMode::Cpu,
|
||||||
|
#else
|
||||||
AstcDecodeMode::Gpu,
|
AstcDecodeMode::Gpu,
|
||||||
|
#endif
|
||||||
AstcDecodeMode::Cpu,
|
AstcDecodeMode::Cpu,
|
||||||
AstcDecodeMode::CpuAsynchronous,
|
AstcDecodeMode::CpuAsynchronous,
|
||||||
"accelerate_astc",
|
"accelerate_astc",
|
||||||
@ -304,7 +308,11 @@ struct Values {
|
|||||||
linkage, 0, "bg_blue", Category::Renderer, Specialization::Default, true, true};
|
linkage, 0, "bg_blue", Category::Renderer, Specialization::Default, true, true};
|
||||||
|
|
||||||
SwitchableSetting<GpuAccuracy, true> gpu_accuracy{linkage,
|
SwitchableSetting<GpuAccuracy, true> gpu_accuracy{linkage,
|
||||||
|
#ifdef ANDROID
|
||||||
|
GpuAccuracy::Normal,
|
||||||
|
#else
|
||||||
GpuAccuracy::High,
|
GpuAccuracy::High,
|
||||||
|
#endif
|
||||||
GpuAccuracy::Normal,
|
GpuAccuracy::Normal,
|
||||||
GpuAccuracy::Extreme,
|
GpuAccuracy::Extreme,
|
||||||
"gpu_accuracy",
|
"gpu_accuracy",
|
||||||
@ -313,20 +321,38 @@ struct Values {
|
|||||||
true,
|
true,
|
||||||
true};
|
true};
|
||||||
GpuAccuracy current_gpu_accuracy{GpuAccuracy::High};
|
GpuAccuracy current_gpu_accuracy{GpuAccuracy::High};
|
||||||
SwitchableSetting<AnisotropyMode, true> max_anisotropy{
|
SwitchableSetting<AnisotropyMode, true> max_anisotropy{linkage,
|
||||||
linkage, AnisotropyMode::Automatic, AnisotropyMode::Automatic, AnisotropyMode::X16,
|
#ifdef ANDROID
|
||||||
"max_anisotropy", Category::RendererAdvanced};
|
AnisotropyMode::Default,
|
||||||
|
#else
|
||||||
|
AnisotropyMode::Automatic,
|
||||||
|
#endif
|
||||||
|
AnisotropyMode::Automatic,
|
||||||
|
AnisotropyMode::X16,
|
||||||
|
"max_anisotropy",
|
||||||
|
Category::RendererAdvanced};
|
||||||
SwitchableSetting<AstcRecompression, true> astc_recompression{linkage,
|
SwitchableSetting<AstcRecompression, true> astc_recompression{linkage,
|
||||||
AstcRecompression::Uncompressed,
|
AstcRecompression::Uncompressed,
|
||||||
AstcRecompression::Uncompressed,
|
AstcRecompression::Uncompressed,
|
||||||
AstcRecompression::Bc3,
|
AstcRecompression::Bc3,
|
||||||
"astc_recompression",
|
"astc_recompression",
|
||||||
Category::RendererAdvanced};
|
Category::RendererAdvanced};
|
||||||
SwitchableSetting<bool> async_presentation{linkage, false, "async_presentation",
|
SwitchableSetting<bool> async_presentation{linkage,
|
||||||
Category::RendererAdvanced};
|
#ifdef ANDROID
|
||||||
|
true,
|
||||||
|
#else
|
||||||
|
false,
|
||||||
|
#endif
|
||||||
|
"async_presentation", Category::RendererAdvanced};
|
||||||
SwitchableSetting<bool> renderer_force_max_clock{linkage, false, "force_max_clock",
|
SwitchableSetting<bool> renderer_force_max_clock{linkage, false, "force_max_clock",
|
||||||
Category::RendererAdvanced};
|
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};
|
Category::RendererAdvanced};
|
||||||
SwitchableSetting<bool> use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders",
|
SwitchableSetting<bool> use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders",
|
||||||
Category::RendererAdvanced};
|
Category::RendererAdvanced};
|
||||||
@ -358,6 +384,8 @@ struct Values {
|
|||||||
Category::RendererDebug};
|
Category::RendererDebug};
|
||||||
// TODO: remove this once AMDVLK supports VK_EXT_depth_bias_control
|
// TODO: remove this once AMDVLK supports VK_EXT_depth_bias_control
|
||||||
bool renderer_amdvlk_depth_bias_workaround{};
|
bool renderer_amdvlk_depth_bias_workaround{};
|
||||||
|
Setting<bool> disable_buffer_reorder{linkage, false, "disable_buffer_reorder",
|
||||||
|
Category::RendererDebug};
|
||||||
|
|
||||||
// System
|
// System
|
||||||
SwitchableSetting<Language, true> language_index{linkage,
|
SwitchableSetting<Language, true> language_index{linkage,
|
||||||
@ -390,7 +418,11 @@ struct Values {
|
|||||||
Setting<s32> current_user{linkage, 0, "current_user", Category::System};
|
Setting<s32> current_user{linkage, 0, "current_user", Category::System};
|
||||||
|
|
||||||
SwitchableSetting<ConsoleMode> use_docked_mode{linkage,
|
SwitchableSetting<ConsoleMode> use_docked_mode{linkage,
|
||||||
|
#ifdef ANDROID
|
||||||
|
ConsoleMode::Handheld,
|
||||||
|
#else
|
||||||
ConsoleMode::Docked,
|
ConsoleMode::Docked,
|
||||||
|
#endif
|
||||||
"use_docked_mode",
|
"use_docked_mode",
|
||||||
Category::System,
|
Category::System,
|
||||||
Specialization::Radio,
|
Specialization::Radio,
|
||||||
|
@ -32,6 +32,7 @@ enum class Category : u32 {
|
|||||||
AddOns,
|
AddOns,
|
||||||
Controls,
|
Controls,
|
||||||
Ui,
|
Ui,
|
||||||
|
UiAudio,
|
||||||
UiGeneral,
|
UiGeneral,
|
||||||
UiLayout,
|
UiLayout,
|
||||||
UiGameList,
|
UiGameList,
|
||||||
|
@ -271,8 +271,9 @@ add_library(core STATIC
|
|||||||
hle/kernel/k_page_heap.h
|
hle/kernel/k_page_heap.h
|
||||||
hle/kernel/k_page_group.cpp
|
hle/kernel/k_page_group.cpp
|
||||||
hle/kernel/k_page_group.h
|
hle/kernel/k_page_group.h
|
||||||
hle/kernel/k_page_table.cpp
|
|
||||||
hle/kernel/k_page_table.h
|
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_manager.h
|
||||||
hle/kernel/k_page_table_slab_heap.h
|
hle/kernel/k_page_table_slab_heap.h
|
||||||
hle/kernel/k_port.cpp
|
hle/kernel/k_port.cpp
|
||||||
@ -280,6 +281,7 @@ add_library(core STATIC
|
|||||||
hle/kernel/k_priority_queue.h
|
hle/kernel/k_priority_queue.h
|
||||||
hle/kernel/k_process.cpp
|
hle/kernel/k_process.cpp
|
||||||
hle/kernel/k_process.h
|
hle/kernel/k_process.h
|
||||||
|
hle/kernel/k_process_page_table.h
|
||||||
hle/kernel/k_readable_event.cpp
|
hle/kernel/k_readable_event.cpp
|
||||||
hle/kernel/k_readable_event.h
|
hle/kernel/k_readable_event.h
|
||||||
hle/kernel/k_resource_limit.cpp
|
hle/kernel/k_resource_limit.cpp
|
||||||
@ -330,8 +332,6 @@ add_library(core STATIC
|
|||||||
hle/kernel/physical_core.cpp
|
hle/kernel/physical_core.cpp
|
||||||
hle/kernel/physical_core.h
|
hle/kernel/physical_core.h
|
||||||
hle/kernel/physical_memory.h
|
hle/kernel/physical_memory.h
|
||||||
hle/kernel/process_capability.cpp
|
|
||||||
hle/kernel/process_capability.h
|
|
||||||
hle/kernel/slab_helpers.h
|
hle/kernel/slab_helpers.h
|
||||||
hle/kernel/svc.cpp
|
hle/kernel/svc.cpp
|
||||||
hle/kernel/svc.h
|
hle/kernel/svc.h
|
||||||
|
@ -153,6 +153,14 @@ void ARM_Interface::Run() {
|
|||||||
Kernel::KThread* current_thread{Kernel::GetCurrentThreadPointer(system.Kernel())};
|
Kernel::KThread* current_thread{Kernel::GetCurrentThreadPointer(system.Kernel())};
|
||||||
HaltReason hr{};
|
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
|
// Notify the debugger and go to sleep if a step was performed
|
||||||
// and this thread has been scheduled again.
|
// and this thread has been scheduled again.
|
||||||
if (current_thread->GetStepState() == StepState::StepPerformed) {
|
if (current_thread->GetStepState() == StepState::StepPerformed) {
|
||||||
@ -174,14 +182,6 @@ void ARM_Interface::Run() {
|
|||||||
}
|
}
|
||||||
system.ExitCPUProfile();
|
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,
|
// Notify the debugger and go to sleep if a breakpoint was hit,
|
||||||
// or if the thread is unable to continue for any reason.
|
// or if the thread is unable to continue for any reason.
|
||||||
if (True(hr & HaltReason::InstructionBreakpoint) || True(hr & HaltReason::PrefetchAbort)) {
|
if (True(hr & HaltReason::InstructionBreakpoint) || True(hr & HaltReason::PrefetchAbort)) {
|
||||||
|
@ -76,6 +76,7 @@ void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CoreTiming::ClearPendingEvents() {
|
void CoreTiming::ClearPendingEvents() {
|
||||||
|
std::scoped_lock lock{basic_lock};
|
||||||
event_queue.clear();
|
event_queue.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,6 +114,7 @@ bool CoreTiming::IsRunning() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool CoreTiming::HasPendingEvents() const {
|
bool CoreTiming::HasPendingEvents() const {
|
||||||
|
std::scoped_lock lock{basic_lock};
|
||||||
return !(wait_set && event_queue.empty());
|
return !(wait_set && event_queue.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,7 +161,7 @@ private:
|
|||||||
std::shared_ptr<EventType> ev_lost;
|
std::shared_ptr<EventType> ev_lost;
|
||||||
Common::Event event{};
|
Common::Event event{};
|
||||||
Common::Event pause_event{};
|
Common::Event pause_event{};
|
||||||
std::mutex basic_lock;
|
mutable std::mutex basic_lock;
|
||||||
std::mutex advance_lock;
|
std::mutex advance_lock;
|
||||||
std::unique_ptr<std::jthread> timer_thread;
|
std::unique_ptr<std::jthread> timer_thread;
|
||||||
std::atomic<bool> paused{};
|
std::atomic<bool> paused{};
|
||||||
|
@ -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) {
|
void GDBStub::HandleQuery(std::string_view command) {
|
||||||
if (command.starts_with("TStatus")) {
|
if (command.starts_with("TStatus")) {
|
||||||
// no tracepoint support
|
// no tracepoint support
|
||||||
@ -573,21 +687,10 @@ void GDBStub::HandleQuery(std::string_view command) {
|
|||||||
const auto target_xml{arch->GetTargetXML()};
|
const auto target_xml{arch->GetTargetXML()};
|
||||||
SendReply(PaginateBuffer(target_xml, command.substr(30)));
|
SendReply(PaginateBuffer(target_xml, command.substr(30)));
|
||||||
} else if (command.starts_with("Offsets")) {
|
} else if (command.starts_with("Offsets")) {
|
||||||
Loader::AppLoader::Modules modules;
|
const auto main_offset = FindMainModuleEntrypoint(system);
|
||||||
system.GetAppLoader().ReadNSOModules(modules);
|
SendReply(fmt::format("TextSeg={:x}", main_offset));
|
||||||
|
|
||||||
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())));
|
|
||||||
}
|
|
||||||
} else if (command.starts_with("Xfer:libraries:read::")) {
|
} else if (command.starts_with("Xfer:libraries:read::")) {
|
||||||
Loader::AppLoader::Modules modules;
|
auto modules = FindModules(system);
|
||||||
system.GetAppLoader().ReadNSOModules(modules);
|
|
||||||
|
|
||||||
std::string buffer;
|
std::string buffer;
|
||||||
buffer += R"(<?xml version="1.0"?>)";
|
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) {
|
void GDBStub::HandleRcmd(const std::vector<u8>& command) {
|
||||||
std::string_view command_str{reinterpret_cast<const char*>(&command[0]), command.size()};
|
std::string_view command_str{reinterpret_cast<const char*>(&command[0]), command.size()};
|
||||||
std::string reply;
|
std::string reply;
|
||||||
@ -767,7 +844,7 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
|
|||||||
|
|
||||||
if (command_str == "get fastmem") {
|
if (command_str == "get fastmem") {
|
||||||
if (Settings::IsFastmemEnabled()) {
|
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 = reinterpret_cast<uintptr_t>(impl.fastmem_arena);
|
||||||
const auto region_bits = impl.current_address_space_width_in_bits;
|
const auto region_bits = impl.current_address_space_width_in_bits;
|
||||||
const auto region_size = 1ULL << region_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";
|
reply = "Fastmem is not enabled.\n";
|
||||||
}
|
}
|
||||||
} else if (command_str == "get info") {
|
} else if (command_str == "get info") {
|
||||||
Loader::AppLoader::Modules modules;
|
auto modules = FindModules(system);
|
||||||
system.GetAppLoader().ReadNSOModules(modules);
|
|
||||||
|
|
||||||
reply = fmt::format("Process: {:#x} ({})\n"
|
reply = fmt::format("Process: {:#x} ({})\n"
|
||||||
"Program Id: {:#018x}\n",
|
"Program Id: {:#018x}\n",
|
||||||
process->GetProcessId(), process->GetName(), process->GetProgramId());
|
process->GetProcessId(), process->GetName(), process->GetProgramId());
|
||||||
reply += fmt::format("Layout:\n"
|
reply += fmt::format(
|
||||||
" Alias: {:#012x} - {:#012x}\n"
|
"Layout:\n"
|
||||||
" Heap: {:#012x} - {:#012x}\n"
|
" Alias: {:#012x} - {:#012x}\n"
|
||||||
" Aslr: {:#012x} - {:#012x}\n"
|
" Heap: {:#012x} - {:#012x}\n"
|
||||||
" Stack: {:#012x} - {:#012x}\n"
|
" Aslr: {:#012x} - {:#012x}\n"
|
||||||
"Modules:\n",
|
" Stack: {:#012x} - {:#012x}\n"
|
||||||
GetInteger(page_table.GetAliasRegionStart()),
|
"Modules:\n",
|
||||||
GetInteger(page_table.GetAliasRegionEnd()),
|
GetInteger(page_table.GetAliasRegionStart()),
|
||||||
GetInteger(page_table.GetHeapRegionStart()),
|
GetInteger(page_table.GetAliasRegionStart()) + page_table.GetAliasRegionSize() - 1,
|
||||||
GetInteger(page_table.GetHeapRegionEnd()),
|
GetInteger(page_table.GetHeapRegionStart()),
|
||||||
GetInteger(page_table.GetAliasCodeRegionStart()),
|
GetInteger(page_table.GetHeapRegionStart()) + page_table.GetHeapRegionSize() - 1,
|
||||||
GetInteger(page_table.GetAliasCodeRegionEnd()),
|
GetInteger(page_table.GetAliasCodeRegionStart()),
|
||||||
GetInteger(page_table.GetStackRegionStart()),
|
GetInteger(page_table.GetAliasCodeRegionStart()) + page_table.GetAliasCodeRegionSize() -
|
||||||
GetInteger(page_table.GetStackRegionEnd()));
|
1,
|
||||||
|
GetInteger(page_table.GetStackRegionStart()),
|
||||||
|
GetInteger(page_table.GetStackRegionStart()) + page_table.GetStackRegionSize() - 1);
|
||||||
|
|
||||||
for (const auto& [vaddr, name] : modules) {
|
for (const auto& [vaddr, name] : modules) {
|
||||||
reply += fmt::format(" {:#012x} - {:#012x} {}\n", vaddr,
|
reply += fmt::format(" {:#012x} - {:#012x} {}\n", vaddr,
|
||||||
@ -811,27 +889,34 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
|
|||||||
while (true) {
|
while (true) {
|
||||||
using MemoryAttribute = Kernel::Svc::MemoryAttribute;
|
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 ||
|
if (svc_mem_info.state != Kernel::Svc::MemoryState::Inaccessible ||
|
||||||
mem_info.base_address + mem_info.size - 1 != std::numeric_limits<u64>::max()) {
|
svc_mem_info.base_address + svc_mem_info.size - 1 !=
|
||||||
const char* state = GetMemoryStateName(mem_info.state);
|
std::numeric_limits<u64>::max()) {
|
||||||
const char* perm = GetMemoryPermissionString(mem_info);
|
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 l = True(svc_mem_info.attribute & MemoryAttribute::Locked) ? 'L' : '-';
|
||||||
const char i = True(mem_info.attribute & MemoryAttribute::IpcLocked) ? 'I' : '-';
|
const char i =
|
||||||
const char d = True(mem_info.attribute & MemoryAttribute::DeviceShared) ? 'D' : '-';
|
True(svc_mem_info.attribute & MemoryAttribute::IpcLocked) ? 'I' : '-';
|
||||||
const char u = True(mem_info.attribute & MemoryAttribute::Uncached) ? 'U' : '-';
|
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 =
|
const char p =
|
||||||
True(mem_info.attribute & MemoryAttribute::PermissionLocked) ? 'P' : '-';
|
True(svc_mem_info.attribute & MemoryAttribute::PermissionLocked) ? 'P' : '-';
|
||||||
|
|
||||||
reply += fmt::format(" {:#012x} - {:#012x} {} {} {}{}{}{}{} [{}, {}]\n",
|
reply += fmt::format(
|
||||||
mem_info.base_address,
|
" {:#012x} - {:#012x} {} {} {}{}{}{}{} [{}, {}]\n", svc_mem_info.base_address,
|
||||||
mem_info.base_address + mem_info.size - 1, perm, state, l, i,
|
svc_mem_info.base_address + svc_mem_info.size - 1, perm, state, l, i, d, u, p,
|
||||||
d, u, p, mem_info.ipc_count, mem_info.device_count);
|
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) {
|
if (next_address <= cur_addr) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -35,13 +35,14 @@ struct RomFSHeader {
|
|||||||
static_assert(sizeof(RomFSHeader) == 0x50, "RomFSHeader has incorrect size.");
|
static_assert(sizeof(RomFSHeader) == 0x50, "RomFSHeader has incorrect size.");
|
||||||
|
|
||||||
struct DirectoryEntry {
|
struct DirectoryEntry {
|
||||||
|
u32_le parent;
|
||||||
u32_le sibling;
|
u32_le sibling;
|
||||||
u32_le child_dir;
|
u32_le child_dir;
|
||||||
u32_le child_file;
|
u32_le child_file;
|
||||||
u32_le hash;
|
u32_le hash;
|
||||||
u32_le name_length;
|
u32_le name_length;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(DirectoryEntry) == 0x14, "DirectoryEntry has incorrect size.");
|
static_assert(sizeof(DirectoryEntry) == 0x18, "DirectoryEntry has incorrect size.");
|
||||||
|
|
||||||
struct FileEntry {
|
struct FileEntry {
|
||||||
u32_le parent;
|
u32_le parent;
|
||||||
@ -64,25 +65,22 @@ std::pair<Entry, std::string> GetEntry(const VirtualFile& file, std::size_t offs
|
|||||||
return {entry, string};
|
return {entry, string};
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProcessFile(VirtualFile file, std::size_t file_offset, std::size_t data_offset,
|
void ProcessFile(const VirtualFile& file, std::size_t file_offset, std::size_t data_offset,
|
||||||
u32 this_file_offset, std::shared_ptr<VectorVfsDirectory> parent) {
|
u32 this_file_offset, std::shared_ptr<VectorVfsDirectory>& parent) {
|
||||||
while (true) {
|
while (this_file_offset != ROMFS_ENTRY_EMPTY) {
|
||||||
auto entry = GetEntry<FileEntry>(file, file_offset + this_file_offset);
|
auto entry = GetEntry<FileEntry>(file, file_offset + this_file_offset);
|
||||||
|
|
||||||
parent->AddFile(std::make_shared<OffsetVfsFile>(
|
parent->AddFile(std::make_shared<OffsetVfsFile>(
|
||||||
file, entry.first.size, entry.first.offset + data_offset, entry.second));
|
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;
|
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::size_t data_offset, u32 this_dir_offset,
|
||||||
std::shared_ptr<VectorVfsDirectory> parent) {
|
std::shared_ptr<VectorVfsDirectory>& parent) {
|
||||||
while (true) {
|
while (this_dir_offset != ROMFS_ENTRY_EMPTY) {
|
||||||
auto entry = GetEntry<DirectoryEntry>(file, dir_offset + this_dir_offset);
|
auto entry = GetEntry<DirectoryEntry>(file, dir_offset + this_dir_offset);
|
||||||
auto current = std::make_shared<VectorVfsDirectory>(
|
auto current = std::make_shared<VectorVfsDirectory>(
|
||||||
std::vector<VirtualFile>{}, std::vector<VirtualDir>{}, entry.second);
|
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);
|
parent->AddDirectory(current);
|
||||||
if (entry.first.sibling == ROMFS_ENTRY_EMPTY)
|
|
||||||
break;
|
|
||||||
this_dir_offset = entry.first.sibling;
|
this_dir_offset = entry.first.sibling;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
|
||||||
VirtualDir ExtractRomFS(VirtualFile file, RomFSExtractionType type) {
|
VirtualDir ExtractRomFS(VirtualFile file) {
|
||||||
RomFSHeader header{};
|
RomFSHeader header{};
|
||||||
if (file->ReadObject(&header) != sizeof(RomFSHeader))
|
if (file->ReadObject(&header) != sizeof(RomFSHeader))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@ -113,27 +109,17 @@ VirtualDir ExtractRomFS(VirtualFile file, RomFSExtractionType type) {
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
const u64 file_offset = header.file_meta.offset;
|
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 =
|
auto root_container = std::make_shared<VectorVfsDirectory>();
|
||||||
std::make_shared<VectorVfsDirectory>(std::vector<VirtualFile>{}, std::vector<VirtualDir>{},
|
|
||||||
file->GetName(), file->GetContainingDirectory());
|
|
||||||
|
|
||||||
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 (auto root = root_container->GetSubdirectory(""); root) {
|
||||||
|
return std::make_shared<CachedVfsDirectory>(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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::make_shared<CachedVfsDirectory>(std::move(out));
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
VirtualFile CreateRomFS(VirtualDir dir, VirtualDir ext) {
|
VirtualFile CreateRomFS(VirtualDir dir, VirtualDir ext) {
|
||||||
|
@ -7,16 +7,9 @@
|
|||||||
|
|
||||||
namespace FileSys {
|
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
|
// Converts a RomFS binary blob to VFS Filesystem
|
||||||
// Returns nullptr on failure
|
// Returns nullptr on failure
|
||||||
VirtualDir ExtractRomFS(VirtualFile file,
|
VirtualDir ExtractRomFS(VirtualFile file);
|
||||||
RomFSExtractionType type = RomFSExtractionType::Truncated);
|
|
||||||
|
|
||||||
// Converts a VFS filesystem into a RomFS binary
|
// Converts a VFS filesystem into a RomFS binary
|
||||||
// Returns nullptr on failure
|
// Returns nullptr on failure
|
||||||
|
@ -96,18 +96,7 @@ void EmulatedController::ReloadFromSettings() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
controller.color_values = {};
|
controller.color_values = {};
|
||||||
controller.colors_state.fullkey = {
|
ReloadColorsFromSettings();
|
||||||
.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),
|
|
||||||
};
|
|
||||||
|
|
||||||
ring_params[0] = Common::ParamPackage(Settings::values.ringcon_analogs);
|
ring_params[0] = Common::ParamPackage(Settings::values.ringcon_analogs);
|
||||||
|
|
||||||
@ -128,6 +117,30 @@ void EmulatedController::ReloadFromSettings() {
|
|||||||
ReloadInput();
|
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() {
|
void EmulatedController::LoadDevices() {
|
||||||
// TODO(german77): Use more buttons to detect the correct device
|
// TODO(german77): Use more buttons to detect the correct device
|
||||||
const auto left_joycon = button_params[Settings::NativeButton::DRight];
|
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_charging = false;
|
||||||
bool is_powered = false;
|
bool is_powered = false;
|
||||||
NpadBatteryLevel battery_level = 0;
|
NpadBatteryLevel battery_level = NpadBatteryLevel::Empty;
|
||||||
switch (controller.battery_values[index]) {
|
switch (controller.battery_values[index]) {
|
||||||
case Common::Input::BatteryLevel::Charging:
|
case Common::Input::BatteryLevel::Charging:
|
||||||
is_charging = true;
|
is_charging = true;
|
||||||
is_powered = true;
|
is_powered = true;
|
||||||
battery_level = 6;
|
battery_level = NpadBatteryLevel::Full;
|
||||||
break;
|
break;
|
||||||
case Common::Input::BatteryLevel::Medium:
|
case Common::Input::BatteryLevel::Medium:
|
||||||
battery_level = 6;
|
battery_level = NpadBatteryLevel::High;
|
||||||
break;
|
break;
|
||||||
case Common::Input::BatteryLevel::Low:
|
case Common::Input::BatteryLevel::Low:
|
||||||
battery_level = 4;
|
battery_level = NpadBatteryLevel::Low;
|
||||||
break;
|
break;
|
||||||
case Common::Input::BatteryLevel::Critical:
|
case Common::Input::BatteryLevel::Critical:
|
||||||
battery_level = 2;
|
battery_level = NpadBatteryLevel::Critical;
|
||||||
break;
|
break;
|
||||||
case Common::Input::BatteryLevel::Empty:
|
case Common::Input::BatteryLevel::Empty:
|
||||||
battery_level = 0;
|
battery_level = NpadBatteryLevel::Empty;
|
||||||
break;
|
break;
|
||||||
case Common::Input::BatteryLevel::None:
|
case Common::Input::BatteryLevel::None:
|
||||||
case Common::Input::BatteryLevel::Full:
|
case Common::Input::BatteryLevel::Full:
|
||||||
default:
|
default:
|
||||||
is_powered = true;
|
is_powered = true;
|
||||||
battery_level = 8;
|
battery_level = NpadBatteryLevel::Full;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -253,6 +253,9 @@ public:
|
|||||||
/// Overrides current mapped devices with the stored configuration and reloads all input devices
|
/// Overrides current mapped devices with the stored configuration and reloads all input devices
|
||||||
void ReloadFromSettings();
|
void ReloadFromSettings();
|
||||||
|
|
||||||
|
/// Updates current colors with the ones stored in the configuration
|
||||||
|
void ReloadColorsFromSettings();
|
||||||
|
|
||||||
/// Saves the current mapped configuration
|
/// Saves the current mapped configuration
|
||||||
void SaveCurrentConfig();
|
void SaveCurrentConfig();
|
||||||
|
|
||||||
|
@ -302,6 +302,15 @@ enum class TouchScreenModeForNx : u8 {
|
|||||||
Heat2,
|
Heat2,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// This is nn::hid::system::NpadBatteryLevel
|
||||||
|
enum class NpadBatteryLevel : u32 {
|
||||||
|
Empty,
|
||||||
|
Critical,
|
||||||
|
Low,
|
||||||
|
High,
|
||||||
|
Full,
|
||||||
|
};
|
||||||
|
|
||||||
// This is nn::hid::NpadStyleTag
|
// This is nn::hid::NpadStyleTag
|
||||||
struct NpadStyleTag {
|
struct NpadStyleTag {
|
||||||
union {
|
union {
|
||||||
@ -385,16 +394,12 @@ struct NpadGcTriggerState {
|
|||||||
};
|
};
|
||||||
static_assert(sizeof(NpadGcTriggerState) == 0x10, "NpadGcTriggerState is an invalid size");
|
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
|
// This is nn::hid::system::NpadPowerInfo
|
||||||
struct NpadPowerInfo {
|
struct NpadPowerInfo {
|
||||||
bool is_powered{};
|
bool is_powered{};
|
||||||
bool is_charging{};
|
bool is_charging{};
|
||||||
INSERT_PADDING_BYTES(0x6);
|
INSERT_PADDING_BYTES(0x6);
|
||||||
NpadBatteryLevel battery_level{8};
|
NpadBatteryLevel battery_level{NpadBatteryLevel::Full};
|
||||||
};
|
};
|
||||||
static_assert(sizeof(NpadPowerInfo) == 0xC, "NpadPowerInfo is an invalid size");
|
static_assert(sizeof(NpadPowerInfo) == 0xC, "NpadPowerInfo is an invalid size");
|
||||||
|
|
||||||
|
@ -222,7 +222,7 @@ Result KSystemControl::AllocateSecureMemory(KernelCore& kernel, KVirtualAddress*
|
|||||||
};
|
};
|
||||||
|
|
||||||
// We succeeded.
|
// We succeeded.
|
||||||
*out = KPageTable::GetHeapVirtualAddress(kernel.MemoryLayout(), paddr);
|
*out = KPageTable::GetHeapVirtualAddress(kernel, paddr);
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -238,8 +238,17 @@ void KSystemControl::FreeSecureMemory(KernelCore& kernel, KVirtualAddress addres
|
|||||||
ASSERT(Common::IsAligned(size, alignment));
|
ASSERT(Common::IsAligned(size, alignment));
|
||||||
|
|
||||||
// Close the secure region's pages.
|
// Close the secure region's pages.
|
||||||
kernel.MemoryManager().Close(KPageTable::GetHeapPhysicalAddress(kernel.MemoryLayout(), address),
|
kernel.MemoryManager().Close(KPageTable::GetHeapPhysicalAddress(kernel, address),
|
||||||
size / PageSize);
|
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
|
} // namespace Kernel::Board::Nintendo::Nx
|
||||||
|
@ -8,7 +8,8 @@
|
|||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
class KernelCore;
|
class KernelCore;
|
||||||
}
|
class KResourceLimit;
|
||||||
|
} // namespace Kernel
|
||||||
|
|
||||||
namespace Kernel::Board::Nintendo::Nx {
|
namespace Kernel::Board::Nintendo::Nx {
|
||||||
|
|
||||||
@ -40,6 +41,10 @@ public:
|
|||||||
u32 pool);
|
u32 pool);
|
||||||
static void FreeSecureMemory(KernelCore& kernel, KVirtualAddress address, size_t size,
|
static void FreeSecureMemory(KernelCore& kernel, KVirtualAddress address, size_t size,
|
||||||
u32 pool);
|
u32 pool);
|
||||||
|
|
||||||
|
// Insecure Memory.
|
||||||
|
static KResourceLimit* GetInsecureMemoryResourceLimit(KernelCore& kernel);
|
||||||
|
static u32 GetInsecureMemoryPool();
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Kernel::Board::Nintendo::Nx
|
} // namespace Kernel::Board::Nintendo::Nx
|
||||||
|
@ -4,14 +4,16 @@
|
|||||||
#include "core/hardware_properties.h"
|
#include "core/hardware_properties.h"
|
||||||
#include "core/hle/kernel/k_capabilities.h"
|
#include "core/hle/kernel/k_capabilities.h"
|
||||||
#include "core/hle/kernel/k_memory_layout.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/kernel.h"
|
||||||
#include "core/hle/kernel/svc_results.h"
|
#include "core/hle/kernel/svc_results.h"
|
||||||
#include "core/hle/kernel/svc_version.h"
|
#include "core/hle/kernel/svc_version.h"
|
||||||
|
|
||||||
namespace Kernel {
|
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.
|
// We're initializing an initial process.
|
||||||
m_svc_access_flags.reset();
|
m_svc_access_flags.reset();
|
||||||
m_irq_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));
|
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.
|
// We're initializing a user process.
|
||||||
m_svc_access_flags.reset();
|
m_svc_access_flags.reset();
|
||||||
m_irq_access_flags.reset();
|
m_irq_access_flags.reset();
|
||||||
@ -121,7 +124,7 @@ Result KCapabilities::SetSyscallMaskCapability(const u32 cap, u32& set_svc) {
|
|||||||
R_SUCCEED();
|
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 range_pack = MapRange{cap};
|
||||||
const auto size_pack = MapRangeSize{size_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::UserRead
|
||||||
: KMemoryPermission::UserReadWrite;
|
: KMemoryPermission::UserReadWrite;
|
||||||
if (MapRangeSize{size_cap}.normal) {
|
if (MapRangeSize{size_cap}.normal) {
|
||||||
// R_RETURN(page_table->MapStatic(phys_addr, size, perm));
|
R_RETURN(page_table->MapStatic(phys_addr, size, perm));
|
||||||
} else {
|
} 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
|
// Get/validate address/size
|
||||||
const u64 phys_addr = MapIoPage{cap}.address.Value() * PageSize;
|
const u64 phys_addr = MapIoPage{cap}.address.Value() * PageSize;
|
||||||
const size_t num_pages = 1;
|
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);
|
R_UNLESS(((phys_addr + size - 1) & ~PhysicalMapAllowedMask) == 0, ResultInvalidAddress);
|
||||||
|
|
||||||
// Do the mapping.
|
// Do the mapping.
|
||||||
// R_RETURN(page_table->MapIo(phys_addr, size, KMemoryPermission_UserReadWrite));
|
R_RETURN(page_table->MapIo(phys_addr, size, KMemoryPermission::UserReadWrite));
|
||||||
|
|
||||||
UNIMPLEMENTED();
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename F>
|
template <typename F>
|
||||||
@ -200,13 +197,11 @@ Result KCapabilities::ProcessMapRegionCapability(const u32 cap, F f) {
|
|||||||
R_SUCCEED();
|
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.
|
// Map each region into the process's page table.
|
||||||
return ProcessMapRegionCapability(
|
return ProcessMapRegionCapability(
|
||||||
cap, [](KMemoryRegionType region_type, KMemoryPermission perm) -> Result {
|
cap, [page_table](KMemoryRegionType region_type, KMemoryPermission perm) -> Result {
|
||||||
// R_RETURN(page_table->MapRegion(region_type, perm));
|
R_RETURN(page_table->MapRegion(region_type, perm));
|
||||||
UNIMPLEMENTED();
|
|
||||||
R_SUCCEED();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -280,7 +275,7 @@ Result KCapabilities::SetDebugFlagsCapability(const u32 cap) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Result KCapabilities::SetCapability(const u32 cap, u32& set_flags, u32& set_svc,
|
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.
|
// Validate this is a capability we can act on.
|
||||||
const auto type = GetCapabilityType(cap);
|
const auto type = GetCapabilityType(cap);
|
||||||
R_UNLESS(type != CapabilityType::Invalid, ResultInvalidArgument);
|
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;
|
u32 set_flags = 0, set_svc = 0;
|
||||||
|
|
||||||
for (size_t i = 0; i < caps.size(); i++) {
|
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.
|
// Map the range.
|
||||||
R_TRY(this->MapRange_(cap, size_cap, page_table));
|
R_TRY(this->MapRange_(cap, size_cap, page_table));
|
||||||
|
} else if (GetCapabilityType(cap) == CapabilityType::MapRegion && !IsKTraceEnabled) {
|
||||||
|
continue;
|
||||||
} else {
|
} else {
|
||||||
R_TRY(this->SetCapability(cap, set_flags, set_svc, page_table));
|
R_TRY(this->SetCapability(cap, set_flags, set_svc, page_table));
|
||||||
}
|
}
|
||||||
|
@ -15,15 +15,15 @@
|
|||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
|
||||||
class KPageTable;
|
class KProcessPageTable;
|
||||||
class KernelCore;
|
class KernelCore;
|
||||||
|
|
||||||
class KCapabilities {
|
class KCapabilities {
|
||||||
public:
|
public:
|
||||||
constexpr explicit KCapabilities() = default;
|
constexpr explicit KCapabilities() = default;
|
||||||
|
|
||||||
Result InitializeForKip(std::span<const u32> kern_caps, KPageTable* page_table);
|
Result InitializeForKip(std::span<const u32> kern_caps, KProcessPageTable* page_table);
|
||||||
Result InitializeForUser(std::span<const u32> user_caps, KPageTable* page_table);
|
Result InitializeForUser(std::span<const u32> user_caps, KProcessPageTable* page_table);
|
||||||
|
|
||||||
static Result CheckCapabilities(KernelCore& kernel, std::span<const u32> user_caps);
|
static Result CheckCapabilities(KernelCore& kernel, std::span<const u32> user_caps);
|
||||||
|
|
||||||
@ -264,9 +264,9 @@ private:
|
|||||||
|
|
||||||
Result SetCorePriorityCapability(const u32 cap);
|
Result SetCorePriorityCapability(const u32 cap);
|
||||||
Result SetSyscallMaskCapability(const u32 cap, u32& set_svc);
|
Result SetSyscallMaskCapability(const u32 cap, u32& set_svc);
|
||||||
Result MapRange_(const u32 cap, const u32 size_cap, KPageTable* page_table);
|
Result MapRange_(const u32 cap, const u32 size_cap, KProcessPageTable* page_table);
|
||||||
Result MapIoPage_(const u32 cap, KPageTable* page_table);
|
Result MapIoPage_(const u32 cap, KProcessPageTable* page_table);
|
||||||
Result MapRegion_(const u32 cap, KPageTable* page_table);
|
Result MapRegion_(const u32 cap, KProcessPageTable* page_table);
|
||||||
Result SetInterruptPairCapability(const u32 cap);
|
Result SetInterruptPairCapability(const u32 cap);
|
||||||
Result SetProgramTypeCapability(const u32 cap);
|
Result SetProgramTypeCapability(const u32 cap);
|
||||||
Result SetKernelVersionCapability(const u32 cap);
|
Result SetKernelVersionCapability(const u32 cap);
|
||||||
@ -277,8 +277,9 @@ private:
|
|||||||
static Result ProcessMapRegionCapability(const u32 cap, F f);
|
static Result ProcessMapRegionCapability(const u32 cap, F f);
|
||||||
static Result CheckMapRegion(KernelCore& kernel, const u32 cap);
|
static Result CheckMapRegion(KernelCore& kernel, const u32 cap);
|
||||||
|
|
||||||
Result SetCapability(const u32 cap, u32& set_flags, u32& set_svc, KPageTable* page_table);
|
Result SetCapability(const u32 cap, u32& set_flags, u32& set_svc,
|
||||||
Result SetCapabilities(std::span<const u32> caps, KPageTable* page_table);
|
KProcessPageTable* page_table);
|
||||||
|
Result SetCapabilities(std::span<const u32> caps, KProcessPageTable* page_table);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Svc::SvcAccessFlagSet m_svc_access_flags{};
|
Svc::SvcAccessFlagSet m_svc_access_flags{};
|
||||||
|
@ -54,7 +54,7 @@ Result KDeviceAddressSpace::Detach(Svc::DeviceName device_name) {
|
|||||||
R_SUCCEED();
|
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) {
|
size_t size, u64 device_address, u32 option, bool is_aligned) {
|
||||||
// Check that the address falls within the space.
|
// Check that the address falls within the space.
|
||||||
R_UNLESS((m_space_address <= device_address &&
|
R_UNLESS((m_space_address <= device_address &&
|
||||||
@ -113,7 +113,7 @@ Result KDeviceAddressSpace::Map(KPageTable* page_table, KProcessAddress process_
|
|||||||
R_SUCCEED();
|
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) {
|
size_t size, u64 device_address) {
|
||||||
// Check that the address falls within the space.
|
// Check that the address falls within the space.
|
||||||
R_UNLESS((m_space_address <= device_address &&
|
R_UNLESS((m_space_address <= device_address &&
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
#include <string>
|
#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/k_typed_address.h"
|
||||||
#include "core/hle/kernel/slab_helpers.h"
|
#include "core/hle/kernel/slab_helpers.h"
|
||||||
#include "core/hle/result.h"
|
#include "core/hle/result.h"
|
||||||
@ -31,23 +31,23 @@ public:
|
|||||||
Result Attach(Svc::DeviceName device_name);
|
Result Attach(Svc::DeviceName device_name);
|
||||||
Result Detach(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) {
|
u64 device_address, u32 option) {
|
||||||
R_RETURN(this->Map(page_table, process_address, size, device_address, option, false));
|
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) {
|
u64 device_address, u32 option) {
|
||||||
R_RETURN(this->Map(page_table, process_address, size, device_address, option, true));
|
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);
|
u64 device_address);
|
||||||
|
|
||||||
static void Initialize();
|
static void Initialize();
|
||||||
|
|
||||||
private:
|
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);
|
u64 device_address, u32 option, bool is_aligned);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -394,6 +394,14 @@ private:
|
|||||||
return region.GetEndAddress();
|
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:
|
private:
|
||||||
u64 m_linear_phys_to_virt_diff{};
|
u64 m_linear_phys_to_virt_diff{};
|
||||||
u64 m_linear_virt_to_phys_diff{};
|
u64 m_linear_virt_to_phys_diff{};
|
||||||
|
@ -456,8 +456,7 @@ size_t KMemoryManager::Impl::Initialize(KPhysicalAddress address, size_t size,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void KMemoryManager::Impl::InitializeOptimizedMemory(KernelCore& kernel) {
|
void KMemoryManager::Impl::InitializeOptimizedMemory(KernelCore& kernel) {
|
||||||
auto optimize_pa =
|
auto optimize_pa = KPageTable::GetHeapPhysicalAddress(kernel, m_management_region);
|
||||||
KPageTable::GetHeapPhysicalAddress(kernel.MemoryLayout(), m_management_region);
|
|
||||||
auto* optimize_map = kernel.System().DeviceMemory().GetPointer<u64>(optimize_pa);
|
auto* optimize_map = kernel.System().DeviceMemory().GetPointer<u64>(optimize_pa);
|
||||||
|
|
||||||
std::memset(optimize_map, 0, CalculateOptimizedProcessOverheadSize(m_heap.GetSize()));
|
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,
|
void KMemoryManager::Impl::TrackUnoptimizedAllocation(KernelCore& kernel, KPhysicalAddress block,
|
||||||
size_t num_pages) {
|
size_t num_pages) {
|
||||||
auto optimize_pa =
|
auto optimize_pa = KPageTable::GetHeapPhysicalAddress(kernel, m_management_region);
|
||||||
KPageTable::GetHeapPhysicalAddress(kernel.MemoryLayout(), m_management_region);
|
|
||||||
auto* optimize_map = kernel.System().DeviceMemory().GetPointer<u64>(optimize_pa);
|
auto* optimize_map = kernel.System().DeviceMemory().GetPointer<u64>(optimize_pa);
|
||||||
|
|
||||||
// Get the range we're tracking.
|
// 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,
|
void KMemoryManager::Impl::TrackOptimizedAllocation(KernelCore& kernel, KPhysicalAddress block,
|
||||||
size_t num_pages) {
|
size_t num_pages) {
|
||||||
auto optimize_pa =
|
auto optimize_pa = KPageTable::GetHeapPhysicalAddress(kernel, m_management_region);
|
||||||
KPageTable::GetHeapPhysicalAddress(kernel.MemoryLayout(), m_management_region);
|
|
||||||
auto* optimize_map = kernel.System().DeviceMemory().GetPointer<u64>(optimize_pa);
|
auto* optimize_map = kernel.System().DeviceMemory().GetPointer<u64>(optimize_pa);
|
||||||
|
|
||||||
// Get the range we're tracking.
|
// 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,
|
bool KMemoryManager::Impl::ProcessOptimizedAllocation(KernelCore& kernel, KPhysicalAddress block,
|
||||||
size_t num_pages, u8 fill_pattern) {
|
size_t num_pages, u8 fill_pattern) {
|
||||||
auto& device_memory = kernel.System().DeviceMemory();
|
auto& device_memory = kernel.System().DeviceMemory();
|
||||||
auto optimize_pa =
|
auto optimize_pa = KPageTable::GetHeapPhysicalAddress(kernel, m_management_region);
|
||||||
KPageTable::GetHeapPhysicalAddress(kernel.MemoryLayout(), m_management_region);
|
|
||||||
auto* optimize_map = device_memory.GetPointer<u64>(optimize_pa);
|
auto* optimize_map = device_memory.GetPointer<u64>(optimize_pa);
|
||||||
|
|
||||||
// We want to return whether any pages were newly allocated.
|
// 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
Reference in New Issue
Block a user