Compare commits
62 Commits
android-49
...
android-55
Author | SHA1 | Date | |
---|---|---|---|
48d16031e9 | |||
2f03fac9a0 | |||
3d5ecc1f08 | |||
52c71e7eb6 | |||
1f04a3dd55 | |||
d360abadba | |||
2dbe067d74 | |||
2f18fa5cd1 | |||
1615a86375 | |||
199de26995 | |||
6c68b07a67 | |||
1d201c71dc | |||
4077ff6851 | |||
164f880f23 | |||
954144e22b | |||
c2f827b85e | |||
037f82025c | |||
338d6f29b1 | |||
ada4697300 | |||
acc99433c7 | |||
6c4abd23be | |||
84b384fbea | |||
3620533995 | |||
c5105b65d5 | |||
1ac2615adb | |||
d7a0b8c373 | |||
6bb02dcb8a | |||
32c453a5f1 | |||
91eb5afd0b | |||
bc4e58eb51 | |||
8674724ef0 | |||
a8edbb7019 | |||
d8c8fbe41f | |||
234cc45192 | |||
b923f5aa7e | |||
18ad55be0b | |||
4e71628097 | |||
92e6ff30a1 | |||
2e55459e03 | |||
8677d98a10 | |||
49df2b9715 | |||
2f2de400e1 | |||
9e134c3da2 | |||
59b3c30f94 | |||
a669e37ddb | |||
7d89f2c146 | |||
51ffc2c66c | |||
e41655960e | |||
1cdd11d9f5 | |||
39c8ddcda2 | |||
2c4ebeb51d | |||
00af46c356 | |||
ce0f1baf51 | |||
75f5b3177d | |||
3c45452fae | |||
ab862207d7 | |||
7f8335f4ae | |||
6ed5b581f0 | |||
387ede76d2 | |||
8a4cb3f902 | |||
0e443dcb05 | |||
0cd9d51e06 |
@ -524,7 +524,7 @@ if (ENABLE_SDL2)
|
||||
if (YUZU_USE_BUNDLED_SDL2)
|
||||
# Detect toolchain and platform
|
||||
if ((MSVC_VERSION GREATER_EQUAL 1920 AND MSVC_VERSION LESS 1940) AND ARCHITECTURE_x86_64)
|
||||
set(SDL2_VER "SDL2-2.28.1")
|
||||
set(SDL2_VER "SDL2-2.28.2")
|
||||
else()
|
||||
message(FATAL_ERROR "No bundled SDL2 binaries for your toolchain. Disable YUZU_USE_BUNDLED_SDL2 and provide your own.")
|
||||
endif()
|
||||
|
@ -1,3 +1,11 @@
|
||||
| Pull Request | Commit | Title | Author | Merged? |
|
||||
|----|----|----|----|----|
|
||||
|
||||
|
||||
End of merge log. You can find the original README.md below the break.
|
||||
|
||||
-----
|
||||
|
||||
<!--
|
||||
SPDX-FileCopyrightText: 2018 yuzu Emulator Project
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
5
dist/qt_themes/default/style.qss
vendored
5
dist/qt_themes/default/style.qss
vendored
@ -78,6 +78,11 @@ QPushButton#buttonRefreshDevices {
|
||||
max-height: 21px;
|
||||
}
|
||||
|
||||
QPushButton#button_reset_defaults {
|
||||
min-width: 57px;
|
||||
padding: 4px 8px;
|
||||
}
|
||||
|
||||
QWidget#bottomPerGameInput,
|
||||
QWidget#topControllerApplet,
|
||||
QWidget#bottomControllerApplet,
|
||||
|
@ -2228,6 +2228,10 @@ QPushButton#buttonRefreshDevices {
|
||||
padding: 0px 0px;
|
||||
}
|
||||
|
||||
QPushButton#button_reset_defaults {
|
||||
padding: 3px 6px;
|
||||
}
|
||||
|
||||
QSpinBox#spinboxLStickRange,
|
||||
QSpinBox#spinboxRStickRange,
|
||||
QSpinBox#vibrationSpinPlayer1,
|
||||
|
14
externals/CMakeLists.txt
vendored
14
externals/CMakeLists.txt
vendored
@ -42,6 +42,11 @@ endif()
|
||||
# mbedtls
|
||||
add_subdirectory(mbedtls)
|
||||
target_include_directories(mbedtls PUBLIC ./mbedtls/include)
|
||||
if (NOT MSVC)
|
||||
target_compile_options(mbedcrypto PRIVATE
|
||||
-Wno-unused-but-set-variable
|
||||
-Wno-string-concatenation)
|
||||
endif()
|
||||
|
||||
# MicroProfile
|
||||
add_library(microprofile INTERFACE)
|
||||
@ -94,6 +99,12 @@ if (ENABLE_CUBEB AND NOT TARGET cubeb::cubeb)
|
||||
set(BUILD_TOOLS OFF)
|
||||
add_subdirectory(cubeb)
|
||||
add_library(cubeb::cubeb ALIAS cubeb)
|
||||
if (NOT MSVC)
|
||||
if (TARGET speex)
|
||||
target_compile_options(speex PRIVATE -Wno-sign-compare)
|
||||
endif()
|
||||
target_compile_options(cubeb PRIVATE -Wno-implicit-const-int-float-conversion)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# DiscordRPC
|
||||
@ -151,6 +162,9 @@ endif()
|
||||
if (NOT TARGET LLVM::Demangle)
|
||||
add_library(demangle demangle/ItaniumDemangle.cpp)
|
||||
target_include_directories(demangle PUBLIC ./demangle)
|
||||
if (NOT MSVC)
|
||||
target_compile_options(demangle PRIVATE -Wno-deprecated-declarations) # std::is_pod
|
||||
endif()
|
||||
add_library(LLVM::Demangle ALIAS demangle)
|
||||
endif()
|
||||
|
||||
|
2
externals/SDL
vendored
2
externals/SDL
vendored
Submodule externals/SDL updated: 116a5344ff...031912c4b6
4
externals/ffmpeg/CMakeLists.txt
vendored
4
externals/ffmpeg/CMakeLists.txt
vendored
@ -164,7 +164,7 @@ if (NOT WIN32 AND NOT ANDROID)
|
||||
--enable-decoder=h264
|
||||
--enable-decoder=vp8
|
||||
--enable-decoder=vp9
|
||||
--enable-filter=yadif
|
||||
--enable-filter=yadif,scale
|
||||
--cc="${FFmpeg_CC}"
|
||||
--cxx="${FFmpeg_CXX}"
|
||||
${FFmpeg_HWACCEL_FLAGS}
|
||||
@ -254,7 +254,7 @@ elseif(ANDROID)
|
||||
set(FFmpeg_INCLUDE_DIR "${FFmpeg_INCLUDE_DIR}" PARENT_SCOPE)
|
||||
elseif(WIN32)
|
||||
# Use yuzu FFmpeg binaries
|
||||
set(FFmpeg_EXT_NAME "ffmpeg-5.1.3")
|
||||
set(FFmpeg_EXT_NAME "ffmpeg-6.0")
|
||||
set(FFmpeg_PATH "${CMAKE_BINARY_DIR}/externals/${FFmpeg_EXT_NAME}")
|
||||
download_bundled_external("ffmpeg/" ${FFmpeg_EXT_NAME} "")
|
||||
set(FFmpeg_FOUND YES)
|
||||
|
@ -114,16 +114,19 @@ else()
|
||||
-Wno-attributes
|
||||
-Wno-invalid-offsetof
|
||||
-Wno-unused-parameter
|
||||
|
||||
$<$<CXX_COMPILER_ID:Clang>:-Wno-braced-scalar-init>
|
||||
$<$<CXX_COMPILER_ID:Clang>:-Wno-unused-private-field>
|
||||
$<$<CXX_COMPILER_ID:Clang>:-Werror=shadow-uncaptured-local>
|
||||
$<$<CXX_COMPILER_ID:Clang>:-Werror=implicit-fallthrough>
|
||||
$<$<CXX_COMPILER_ID:Clang>:-Werror=type-limits>
|
||||
$<$<CXX_COMPILER_ID:AppleClang>:-Wno-braced-scalar-init>
|
||||
$<$<CXX_COMPILER_ID:AppleClang>:-Wno-unused-private-field>
|
||||
)
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES Clang) # Clang or AppleClang
|
||||
add_compile_options(
|
||||
-Wno-braced-scalar-init
|
||||
-Wno-unused-private-field
|
||||
-Wno-nullability-completeness
|
||||
-Werror=shadow-uncaptured-local
|
||||
-Werror=implicit-fallthrough
|
||||
-Werror=type-limits
|
||||
)
|
||||
endif()
|
||||
|
||||
if (ARCHITECTURE_x86_64)
|
||||
add_compile_options("-mcx16")
|
||||
add_compile_options("-fwrapv")
|
||||
|
@ -25,6 +25,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
android:hasFragileUserData="false"
|
||||
android:supportsRtl="true"
|
||||
android:isGame="true"
|
||||
android:appCategory="game"
|
||||
android:localeConfig="@xml/locales_config"
|
||||
android:banner="@drawable/tv_banner"
|
||||
android:extractNativeLibs="true"
|
||||
@ -55,7 +56,6 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
android:name="org.yuzu.yuzu_emu.activities.EmulationActivity"
|
||||
android:theme="@style/Theme.Yuzu.Main"
|
||||
android:launchMode="singleTop"
|
||||
android:screenOrientation="userLandscape"
|
||||
android:supportsPictureInPicture="true"
|
||||
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|uiMode"
|
||||
android:exported="true">
|
||||
@ -66,6 +66,14 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
<data android:mimeType="application/octet-stream" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data
|
||||
android:mimeType="application/octet-stream"
|
||||
android:scheme="content"/>
|
||||
</intent-filter>
|
||||
|
||||
<meta-data
|
||||
android:name="android.nfc.action.TECH_DISCOVERED"
|
||||
android:resource="@xml/nfc_tech_filter" />
|
||||
|
@ -11,7 +11,6 @@ import android.view.View
|
||||
import android.view.ViewGroup.MarginLayoutParams
|
||||
import android.widget.Toast
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.activity.viewModels
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.ViewCompat
|
||||
@ -246,17 +245,5 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView {
|
||||
settings.putExtra(ARG_GAME_ID, gameId)
|
||||
context.startActivity(settings)
|
||||
}
|
||||
|
||||
fun launch(
|
||||
context: Context,
|
||||
launcher: ActivityResultLauncher<Intent>,
|
||||
menuTag: String?,
|
||||
gameId: String?
|
||||
) {
|
||||
val settings = Intent(context, SettingsActivity::class.java)
|
||||
settings.putExtra(ARG_MENU_TAG, menuTag)
|
||||
settings.putExtra(ARG_GAME_ID, gameId)
|
||||
launcher.launch(settings)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,11 +7,11 @@ import android.annotation.SuppressLint
|
||||
import android.app.AlertDialog
|
||||
import android.content.Context
|
||||
import android.content.DialogInterface
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.content.pm.ActivityInfo
|
||||
import android.content.res.Configuration
|
||||
import android.graphics.Color
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
@ -19,8 +19,6 @@ import android.util.Rational
|
||||
import android.view.*
|
||||
import android.widget.TextView
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.widget.PopupMenu
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import androidx.core.graphics.Insets
|
||||
@ -50,6 +48,7 @@ import org.yuzu.yuzu_emu.features.settings.model.IntSetting
|
||||
import org.yuzu.yuzu_emu.features.settings.model.Settings
|
||||
import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivity
|
||||
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
|
||||
import org.yuzu.yuzu_emu.model.Game
|
||||
import org.yuzu.yuzu_emu.overlay.InputOverlay
|
||||
import org.yuzu.yuzu_emu.utils.*
|
||||
|
||||
@ -62,12 +61,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
private var _binding: FragmentEmulationBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
|
||||
val args by navArgs<EmulationFragmentArgs>()
|
||||
private val args by navArgs<EmulationFragmentArgs>()
|
||||
|
||||
private lateinit var game: Game
|
||||
|
||||
private var isInFoldableLayout = false
|
||||
|
||||
private lateinit var onReturnFromSettings: ActivityResultLauncher<Intent>
|
||||
|
||||
override fun onAttach(context: Context) {
|
||||
super.onAttach(context)
|
||||
if (context is EmulationActivity) {
|
||||
@ -81,11 +80,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
.collect { updateFoldableLayout(context, it) }
|
||||
}
|
||||
}
|
||||
|
||||
onReturnFromSettings = context.activityResultRegistry.register(
|
||||
"SettingsResult",
|
||||
ActivityResultContracts.StartActivityForResult()
|
||||
) { updateScreenLayout() }
|
||||
} else {
|
||||
throw IllegalStateException("EmulationFragment must have EmulationActivity parent")
|
||||
}
|
||||
@ -97,10 +91,25 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
val intentUri: Uri? = requireActivity().intent.data
|
||||
var intentGame: Game? = null
|
||||
if (intentUri != null) {
|
||||
intentGame = if (Game.extensions.contains(FileUtil.getExtension(intentUri))) {
|
||||
GameHelper.getGame(requireActivity().intent.data!!, false)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
game = if (args.game != null) {
|
||||
args.game!!
|
||||
} else {
|
||||
intentGame ?: error("[EmulationFragment] No bootable game present!")
|
||||
}
|
||||
|
||||
// So this fragment doesn't restart on configuration changes; i.e. rotation.
|
||||
retainInstance = true
|
||||
preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
|
||||
emulationState = EmulationState(args.game.path)
|
||||
emulationState = EmulationState(game.path)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -124,7 +133,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
updateShowFpsOverlay()
|
||||
|
||||
binding.inGameMenu.getHeaderView(0).findViewById<TextView>(R.id.text_game_title).text =
|
||||
args.game.title
|
||||
game.title
|
||||
binding.inGameMenu.setNavigationItemSelectedListener {
|
||||
when (it.itemId) {
|
||||
R.id.menu_pause_emulation -> {
|
||||
@ -149,12 +158,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
}
|
||||
|
||||
R.id.menu_settings -> {
|
||||
SettingsActivity.launch(
|
||||
requireContext(),
|
||||
onReturnFromSettings,
|
||||
SettingsFile.FILE_NAME_CONFIG,
|
||||
""
|
||||
)
|
||||
SettingsActivity.launch(requireContext(), SettingsFile.FILE_NAME_CONFIG, "")
|
||||
true
|
||||
}
|
||||
|
||||
|
@ -63,13 +63,13 @@ object GameHelper {
|
||||
)
|
||||
} else {
|
||||
if (Game.extensions.contains(FileUtil.getExtension(it.uri))) {
|
||||
games.add(getGame(it.uri))
|
||||
games.add(getGame(it.uri, true))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getGame(uri: Uri): Game {
|
||||
fun getGame(uri: Uri, addedToLibrary: Boolean): Game {
|
||||
val filePath = uri.toString()
|
||||
var name = NativeLibrary.getTitle(filePath)
|
||||
|
||||
@ -94,12 +94,14 @@ object GameHelper {
|
||||
NativeLibrary.isHomebrew(filePath)
|
||||
)
|
||||
|
||||
if (addedToLibrary) {
|
||||
val addedTime = preferences.getLong(newGame.keyAddedToLibraryTime, 0L)
|
||||
if (addedTime == 0L) {
|
||||
preferences.edit()
|
||||
.putLong(newGame.keyAddedToLibraryTime, System.currentTimeMillis())
|
||||
.apply()
|
||||
}
|
||||
}
|
||||
|
||||
return newGame
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.util.Rational
|
||||
import android.view.SurfaceView
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
class FixedRatioSurfaceView @JvmOverloads constructor(
|
||||
context: Context,
|
||||
@ -22,27 +21,44 @@ class FixedRatioSurfaceView @JvmOverloads constructor(
|
||||
*/
|
||||
fun setAspectRatio(ratio: Rational?) {
|
||||
aspectRatio = ratio?.toFloat() ?: 0f
|
||||
requestLayout()
|
||||
}
|
||||
|
||||
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
|
||||
val width = MeasureSpec.getSize(widthMeasureSpec)
|
||||
val height = MeasureSpec.getSize(heightMeasureSpec)
|
||||
val displayWidth: Float = MeasureSpec.getSize(widthMeasureSpec).toFloat()
|
||||
val displayHeight: Float = MeasureSpec.getSize(heightMeasureSpec).toFloat()
|
||||
if (aspectRatio != 0f) {
|
||||
val newWidth: Int
|
||||
val newHeight: Int
|
||||
if (height * aspectRatio < width) {
|
||||
newWidth = (height * aspectRatio).roundToInt()
|
||||
newHeight = height
|
||||
val displayAspect = displayWidth / displayHeight
|
||||
if (displayAspect < aspectRatio) {
|
||||
// Max out width
|
||||
val halfHeight = displayHeight / 2
|
||||
val surfaceHeight = displayWidth / aspectRatio
|
||||
val newTop: Float = halfHeight - (surfaceHeight / 2)
|
||||
val newBottom: Float = halfHeight + (surfaceHeight / 2)
|
||||
super.onMeasure(
|
||||
widthMeasureSpec,
|
||||
MeasureSpec.makeMeasureSpec(
|
||||
newBottom.toInt() - newTop.toInt(),
|
||||
MeasureSpec.EXACTLY
|
||||
)
|
||||
)
|
||||
return
|
||||
} else {
|
||||
newWidth = width
|
||||
newHeight = (width / aspectRatio).roundToInt()
|
||||
// Max out height
|
||||
val halfWidth = displayWidth / 2
|
||||
val surfaceWidth = displayHeight * aspectRatio
|
||||
val newLeft: Float = halfWidth - (surfaceWidth / 2)
|
||||
val newRight: Float = halfWidth + (surfaceWidth / 2)
|
||||
super.onMeasure(
|
||||
MeasureSpec.makeMeasureSpec(
|
||||
newRight.toInt() - newLeft.toInt(),
|
||||
MeasureSpec.EXACTLY
|
||||
),
|
||||
heightMeasureSpec
|
||||
)
|
||||
return
|
||||
}
|
||||
val left = (width - newWidth) / 2
|
||||
val top = (height - newHeight) / 2
|
||||
setLeftTopRightBottom(left, top, left + newWidth, top + newHeight)
|
||||
} else {
|
||||
setLeftTopRightBottom(0, 0, width, height)
|
||||
}
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@
|
||||
#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"
|
||||
@ -144,7 +145,9 @@ void Config::ReadValues() {
|
||||
Service::Account::MAX_USERS - 1);
|
||||
|
||||
// Disable docked mode by default on Android
|
||||
Settings::values.use_docked_mode = config->GetBoolean("System", "use_docked_mode", false);
|
||||
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) {
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include "core/cpu_manager.h"
|
||||
#include "core/crypto/key_manager.h"
|
||||
#include "core/file_sys/card_image.h"
|
||||
#include "core/file_sys/content_archive.h"
|
||||
#include "core/file_sys/registered_cache.h"
|
||||
#include "core/file_sys/submission_package.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
@ -224,6 +225,42 @@ public:
|
||||
m_system.Renderer().NotifySurfaceChanged();
|
||||
}
|
||||
|
||||
void ConfigureFilesystemProvider(const std::string& filepath) {
|
||||
const auto file = m_system.GetFilesystem()->OpenFile(filepath, FileSys::Mode::Read);
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto loader = Loader::GetLoader(m_system, file);
|
||||
if (!loader) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto file_type = loader->GetFileType();
|
||||
if (file_type == Loader::FileType::Unknown || file_type == Loader::FileType::Error) {
|
||||
return;
|
||||
}
|
||||
|
||||
u64 program_id = 0;
|
||||
const auto res2 = loader->ReadProgramId(program_id);
|
||||
if (res2 == Loader::ResultStatus::Success && file_type == Loader::FileType::NCA) {
|
||||
m_manual_provider->AddEntry(FileSys::TitleType::Application,
|
||||
FileSys::GetCRTypeFromNCAType(FileSys::NCA{file}.GetType()),
|
||||
program_id, file);
|
||||
} else if (res2 == Loader::ResultStatus::Success &&
|
||||
(file_type == Loader::FileType::XCI || file_type == Loader::FileType::NSP)) {
|
||||
const auto nsp = file_type == Loader::FileType::NSP
|
||||
? std::make_shared<FileSys::NSP>(file)
|
||||
: FileSys::XCI{file}.GetSecurePartitionNSP();
|
||||
for (const auto& title : nsp->GetNCAs()) {
|
||||
for (const auto& entry : title.second) {
|
||||
m_manual_provider->AddEntry(entry.first.first, entry.first.second, title.first,
|
||||
entry.second->GetBaseFile());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Core::SystemResultStatus InitializeEmulation(const std::string& filepath) {
|
||||
std::scoped_lock lock(m_mutex);
|
||||
|
||||
@ -254,8 +291,14 @@ public:
|
||||
std::move(android_keyboard), // Software Keyboard
|
||||
nullptr, // Web Browser
|
||||
});
|
||||
|
||||
// Initialize filesystem.
|
||||
m_manual_provider = std::make_unique<FileSys::ManualContentProvider>();
|
||||
m_system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
|
||||
m_system.RegisterContentProvider(FileSys::ContentProviderUnionSlot::FrontendManual,
|
||||
m_manual_provider.get());
|
||||
m_system.GetFileSystemController().CreateFactories(*m_vfs);
|
||||
ConfigureFilesystemProvider(filepath);
|
||||
|
||||
// Initialize account manager
|
||||
m_profile_manager = std::make_unique<Service::Account::ProfileManager>();
|
||||
@ -377,7 +420,7 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
return !Settings::values.use_docked_mode.GetValue();
|
||||
return !Settings::IsDockedMode();
|
||||
}
|
||||
|
||||
void SetDeviceType([[maybe_unused]] int index, int type) {
|
||||
@ -489,6 +532,7 @@ private:
|
||||
bool m_is_paused{};
|
||||
SoftwareKeyboard::AndroidKeyboard* m_software_keyboard{};
|
||||
std::unique_ptr<Service::Account::ProfileManager> m_profile_manager;
|
||||
std::unique_ptr<FileSys::ManualContentProvider> m_manual_provider;
|
||||
|
||||
// GPU driver parameters
|
||||
std::shared_ptr<Common::DynamicLibrary> m_vulkan_library;
|
||||
|
@ -12,7 +12,9 @@
|
||||
tools:layout="@layout/fragment_emulation" >
|
||||
<argument
|
||||
android:name="game"
|
||||
app:argType="org.yuzu.yuzu_emu.model.Game" />
|
||||
app:argType="org.yuzu.yuzu_emu.model.Game"
|
||||
app:nullable="true"
|
||||
android:defaultValue="@null" />
|
||||
</fragment>
|
||||
|
||||
</navigation>
|
||||
|
@ -62,7 +62,9 @@
|
||||
android:label="EmulationActivity">
|
||||
<argument
|
||||
android:name="game"
|
||||
app:argType="org.yuzu.yuzu_emu.model.Game" />
|
||||
app:argType="org.yuzu.yuzu_emu.model.Game"
|
||||
app:nullable="true"
|
||||
android:defaultValue="@null" />
|
||||
</activity>
|
||||
|
||||
<action
|
||||
|
@ -778,7 +778,7 @@ u32 System::DropVoices(CommandBuffer& command_buffer, u32 estimated_process_time
|
||||
while (i < command_buffer.count) {
|
||||
const auto node_id{cmd->node_id};
|
||||
const auto node_id_type{cmd->node_id >> 28};
|
||||
const auto node_id_base{cmd->node_id & 0xFFF};
|
||||
const auto node_id_base{(cmd->node_id >> 16) & 0xFFF};
|
||||
|
||||
// If the new estimated process time falls below the limit, we're done dropping.
|
||||
if (estimated_process_time <= time_limit) {
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "audio_core/sink/cubeb_sink.h"
|
||||
#include "audio_core/sink/sink_stream.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "core/core.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
@ -332,25 +333,38 @@ std::vector<std::string> ListCubebSinkDevices(bool capture) {
|
||||
return device_list;
|
||||
}
|
||||
|
||||
u32 GetCubebLatency() {
|
||||
cubeb* ctx;
|
||||
namespace {
|
||||
static long TmpDataCallback(cubeb_stream*, void*, const void*, void*, long) {
|
||||
return TargetSampleCount;
|
||||
}
|
||||
static void TmpStateCallback(cubeb_stream*, void*, cubeb_state) {}
|
||||
} // namespace
|
||||
|
||||
bool IsCubebSuitable() {
|
||||
#if !defined(HAVE_CUBEB)
|
||||
return false;
|
||||
#else
|
||||
cubeb* ctx{nullptr};
|
||||
|
||||
#ifdef _WIN32
|
||||
auto com_init_result = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
|
||||
#endif
|
||||
|
||||
// Init cubeb
|
||||
if (cubeb_init(&ctx, "yuzu Latency Getter", nullptr) != CUBEB_OK) {
|
||||
LOG_CRITICAL(Audio_Sink, "cubeb_init failed");
|
||||
// Return a large latency so we choose SDL instead.
|
||||
return 10000u;
|
||||
LOG_ERROR(Audio_Sink, "Cubeb failed to init, it is not suitable.");
|
||||
return false;
|
||||
}
|
||||
|
||||
SCOPE_EXIT({ cubeb_destroy(ctx); });
|
||||
|
||||
#ifdef _WIN32
|
||||
if (SUCCEEDED(com_init_result)) {
|
||||
CoUninitialize();
|
||||
}
|
||||
#endif
|
||||
|
||||
// Get min latency
|
||||
cubeb_stream_params params{};
|
||||
params.rate = TargetSampleRate;
|
||||
params.channels = 2;
|
||||
@ -361,12 +375,27 @@ u32 GetCubebLatency() {
|
||||
u32 latency{0};
|
||||
const auto latency_error = cubeb_get_min_latency(ctx, ¶ms, &latency);
|
||||
if (latency_error != CUBEB_OK) {
|
||||
LOG_CRITICAL(Audio_Sink, "Error getting minimum latency, error: {}", latency_error);
|
||||
latency = TargetSampleCount * 2;
|
||||
LOG_ERROR(Audio_Sink, "Cubeb could not get min latency, it is not suitable.");
|
||||
return false;
|
||||
}
|
||||
latency = std::max(latency, TargetSampleCount * 2);
|
||||
cubeb_destroy(ctx);
|
||||
return latency;
|
||||
|
||||
// Test opening a device with standard parameters
|
||||
cubeb_devid output_device{0};
|
||||
cubeb_devid input_device{0};
|
||||
std::string name{"Yuzu test"};
|
||||
cubeb_stream* stream{nullptr};
|
||||
|
||||
if (cubeb_stream_init(ctx, &stream, name.c_str(), input_device, nullptr, output_device, ¶ms,
|
||||
latency, &TmpDataCallback, &TmpStateCallback, nullptr) != CUBEB_OK) {
|
||||
LOG_CRITICAL(Audio_Sink, "Cubeb could not open a device, it is not suitable.");
|
||||
return false;
|
||||
}
|
||||
|
||||
cubeb_stream_stop(stream);
|
||||
cubeb_stream_destroy(stream);
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace AudioCore::Sink
|
||||
|
@ -97,10 +97,11 @@ private:
|
||||
std::vector<std::string> ListCubebSinkDevices(bool capture);
|
||||
|
||||
/**
|
||||
* Get the reported latency for this sink.
|
||||
* Check if this backend is suitable for use.
|
||||
* Checks if enabled, its latency, whether it opens successfully, etc.
|
||||
*
|
||||
* @return Minimum latency for this sink.
|
||||
* @return True is this backend is suitable, false otherwise.
|
||||
*/
|
||||
u32 GetCubebLatency();
|
||||
bool IsCubebSuitable();
|
||||
|
||||
} // namespace AudioCore::Sink
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "audio_core/sink/sdl2_sink.h"
|
||||
#include "audio_core/sink/sink_stream.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "core/core.h"
|
||||
|
||||
namespace AudioCore::Sink {
|
||||
@ -84,6 +85,7 @@ public:
|
||||
}
|
||||
|
||||
Stop();
|
||||
SDL_ClearQueuedAudio(device);
|
||||
SDL_CloseAudioDevice(device);
|
||||
}
|
||||
|
||||
@ -227,8 +229,42 @@ std::vector<std::string> ListSDLSinkDevices(bool capture) {
|
||||
return device_list;
|
||||
}
|
||||
|
||||
u32 GetSDLLatency() {
|
||||
return TargetSampleCount * 2;
|
||||
bool IsSDLSuitable() {
|
||||
#if !defined(HAVE_SDL2)
|
||||
return false;
|
||||
#else
|
||||
// Check SDL can init
|
||||
if (!SDL_WasInit(SDL_INIT_AUDIO)) {
|
||||
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
|
||||
LOG_ERROR(Audio_Sink, "SDL failed to init, it is not suitable. Error: {}",
|
||||
SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// We can set any latency frequency we want with SDL, so no need to check that.
|
||||
|
||||
// Check we can open a device with standard parameters
|
||||
SDL_AudioSpec spec;
|
||||
spec.freq = TargetSampleRate;
|
||||
spec.channels = 2u;
|
||||
spec.format = AUDIO_S16SYS;
|
||||
spec.samples = TargetSampleCount * 2;
|
||||
spec.callback = nullptr;
|
||||
spec.userdata = nullptr;
|
||||
|
||||
SDL_AudioSpec obtained;
|
||||
auto device = SDL_OpenAudioDevice(nullptr, false, &spec, &obtained, false);
|
||||
|
||||
if (device == 0) {
|
||||
LOG_ERROR(Audio_Sink, "SDL failed to open a device, it is not suitable. Error: {}",
|
||||
SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
SDL_CloseAudioDevice(device);
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace AudioCore::Sink
|
||||
|
@ -88,10 +88,11 @@ private:
|
||||
std::vector<std::string> ListSDLSinkDevices(bool capture);
|
||||
|
||||
/**
|
||||
* Get the reported latency for this sink.
|
||||
* Check if this backend is suitable for use.
|
||||
* Checks if enabled, its latency, whether it opens successfully, etc.
|
||||
*
|
||||
* @return Minimum latency for this sink.
|
||||
* @return True is this backend is suitable, false otherwise.
|
||||
*/
|
||||
u32 GetSDLLatency();
|
||||
bool IsSDLSuitable();
|
||||
|
||||
} // namespace AudioCore::Sink
|
||||
|
@ -22,7 +22,7 @@ namespace {
|
||||
struct SinkDetails {
|
||||
using FactoryFn = std::unique_ptr<Sink> (*)(std::string_view);
|
||||
using ListDevicesFn = std::vector<std::string> (*)(bool);
|
||||
using LatencyFn = u32 (*)();
|
||||
using SuitableFn = bool (*)();
|
||||
|
||||
/// Name for this sink.
|
||||
Settings::AudioEngine id;
|
||||
@ -30,8 +30,8 @@ struct SinkDetails {
|
||||
FactoryFn factory;
|
||||
/// A method to call to list available devices.
|
||||
ListDevicesFn list_devices;
|
||||
/// Method to get the latency of this backend.
|
||||
LatencyFn latency;
|
||||
/// Check whether this backend is suitable to be used.
|
||||
SuitableFn is_suitable;
|
||||
};
|
||||
|
||||
// sink_details is ordered in terms of desirability, with the best choice at the top.
|
||||
@ -43,7 +43,7 @@ constexpr SinkDetails sink_details[] = {
|
||||
return std::make_unique<CubebSink>(device_id);
|
||||
},
|
||||
&ListCubebSinkDevices,
|
||||
&GetCubebLatency,
|
||||
&IsCubebSuitable,
|
||||
},
|
||||
#endif
|
||||
#ifdef HAVE_SDL2
|
||||
@ -53,14 +53,17 @@ constexpr SinkDetails sink_details[] = {
|
||||
return std::make_unique<SDLSink>(device_id);
|
||||
},
|
||||
&ListSDLSinkDevices,
|
||||
&GetSDLLatency,
|
||||
&IsSDLSuitable,
|
||||
},
|
||||
#endif
|
||||
SinkDetails{Settings::AudioEngine::Null,
|
||||
SinkDetails{
|
||||
Settings::AudioEngine::Null,
|
||||
[](std::string_view device_id) -> std::unique_ptr<Sink> {
|
||||
return std::make_unique<NullSink>(device_id);
|
||||
},
|
||||
[](bool capture) { return std::vector<std::string>{"null"}; }, []() { return 0u; }},
|
||||
[](bool capture) { return std::vector<std::string>{"null"}; },
|
||||
[]() { return true; },
|
||||
},
|
||||
};
|
||||
|
||||
const SinkDetails& GetOutputSinkDetails(Settings::AudioEngine sink_id) {
|
||||
@ -72,18 +75,22 @@ const SinkDetails& GetOutputSinkDetails(Settings::AudioEngine sink_id) {
|
||||
auto iter = find_backend(sink_id);
|
||||
|
||||
if (sink_id == Settings::AudioEngine::Auto) {
|
||||
// Auto-select a backend. Prefer CubeB, but it may report a large minimum latency which
|
||||
// causes audio issues, in that case go with SDL.
|
||||
#if defined(HAVE_CUBEB) && defined(HAVE_SDL2)
|
||||
iter = find_backend(Settings::AudioEngine::Cubeb);
|
||||
if (iter->latency() > TargetSampleCount * 3) {
|
||||
iter = find_backend(Settings::AudioEngine::Sdl2);
|
||||
// Auto-select a backend. Use the sink details ordering, preferring cubeb first, checking
|
||||
// that the backend is available and suitable to use.
|
||||
for (auto& details : sink_details) {
|
||||
if (details.is_suitable()) {
|
||||
iter = &details;
|
||||
break;
|
||||
}
|
||||
}
|
||||
#else
|
||||
iter = std::begin(sink_details);
|
||||
#endif
|
||||
LOG_INFO(Service_Audio, "Auto-selecting the {} backend",
|
||||
Settings::CanonicalizeEnum(iter->id));
|
||||
} else {
|
||||
if (iter != std::end(sink_details) && !iter->is_suitable()) {
|
||||
LOG_ERROR(Service_Audio, "Selected backend {} is not suitable, falling back to null",
|
||||
Settings::CanonicalizeEnum(iter->id));
|
||||
iter = find_backend(Settings::AudioEngine::Null);
|
||||
}
|
||||
}
|
||||
|
||||
if (iter == std::end(sink_details)) {
|
||||
|
@ -2,6 +2,7 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <version>
|
||||
#include "common/settings_enums.h"
|
||||
#if __cpp_lib_chrono >= 201907L
|
||||
#include <chrono>
|
||||
#include <exception>
|
||||
@ -145,6 +146,10 @@ bool IsFastmemEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsDockedMode() {
|
||||
return values.use_docked_mode.GetValue() == Settings::ConsoleMode::Docked;
|
||||
}
|
||||
|
||||
float Volume() {
|
||||
if (values.audio_muted) {
|
||||
return 0.0f;
|
||||
|
@ -379,7 +379,13 @@ struct Values {
|
||||
|
||||
Setting<s32> current_user{linkage, 0, "current_user", Category::System};
|
||||
|
||||
SwitchableSetting<bool> use_docked_mode{linkage, true, "use_docked_mode", Category::System};
|
||||
SwitchableSetting<ConsoleMode> use_docked_mode{linkage,
|
||||
ConsoleMode::Docked,
|
||||
"use_docked_mode",
|
||||
Category::System,
|
||||
Specialization::Radio,
|
||||
true,
|
||||
true};
|
||||
|
||||
// Controls
|
||||
InputSetting<std::array<PlayerInput, 10>> players;
|
||||
@ -519,6 +525,8 @@ bool IsGPULevelHigh();
|
||||
|
||||
bool IsFastmemEnabled();
|
||||
|
||||
bool IsDockedMode();
|
||||
|
||||
float Volume();
|
||||
|
||||
std::string GetTimeZoneString(TimeZone time_zone);
|
||||
|
@ -56,6 +56,7 @@ enum Specialization : u8 {
|
||||
Scalar = 5, // Values are continuous
|
||||
Countable = 6, // Can be stepped through
|
||||
Paired = 7, // Another setting is associated with this setting
|
||||
Radio = 8, // Setting should be presented in a radio group
|
||||
|
||||
Percentage = (1 << SpecializationAttributeOffset), // Should be represented as a percentage
|
||||
};
|
||||
|
@ -146,6 +146,8 @@ ENUM(AntiAliasing, None, Fxaa, Smaa, MaxEnum);
|
||||
|
||||
ENUM(AspectRatio, R16_9, R4_3, R21_9, R16_10, Stretch);
|
||||
|
||||
ENUM(ConsoleMode, Handheld, Docked);
|
||||
|
||||
template <typename Type>
|
||||
inline std::string CanonicalizeEnum(Type id) {
|
||||
const auto group = EnumMetadata<Type>::Canonicalizations();
|
||||
|
@ -460,11 +460,6 @@ S operator&(const S& i, const swap_struct_t<T, F> v) {
|
||||
return i & v.swap();
|
||||
}
|
||||
|
||||
template <typename S, typename T, typename F>
|
||||
S operator&(const swap_struct_t<T, F> v, const S& i) {
|
||||
return static_cast<S>(v.swap() & i);
|
||||
}
|
||||
|
||||
// Comparison
|
||||
template <typename S, typename T, typename F>
|
||||
bool operator<(const S& p, const swap_struct_t<T, F> v) {
|
||||
|
@ -606,9 +606,9 @@ InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_ex
|
||||
const auto result = RemoveExistingEntry(title_id);
|
||||
|
||||
// Install Metadata File
|
||||
const auto res = RawInstallNCA(**meta_iter, copy, overwrite_if_exists, meta_id_data);
|
||||
if (res != InstallResult::Success) {
|
||||
return res;
|
||||
const auto meta_result = RawInstallNCA(**meta_iter, copy, overwrite_if_exists, meta_id_data);
|
||||
if (meta_result != InstallResult::Success) {
|
||||
return meta_result;
|
||||
}
|
||||
|
||||
// Install all the other NCAs
|
||||
@ -621,9 +621,19 @@ InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_ex
|
||||
if (nca == nullptr) {
|
||||
return InstallResult::ErrorCopyFailed;
|
||||
}
|
||||
const auto res2 = RawInstallNCA(*nca, copy, overwrite_if_exists, record.nca_id);
|
||||
if (res2 != InstallResult::Success) {
|
||||
return res2;
|
||||
if (nca->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS &&
|
||||
nca->GetTitleId() != title_id) {
|
||||
// Create fake cnmt for patch to multiprogram application
|
||||
const auto sub_nca_result =
|
||||
InstallEntry(*nca, TitleType::Update, overwrite_if_exists, copy);
|
||||
if (sub_nca_result != InstallResult::Success) {
|
||||
return sub_nca_result;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
const auto nca_result = RawInstallNCA(*nca, copy, overwrite_if_exists, record.nca_id);
|
||||
if (nca_result != InstallResult::Success) {
|
||||
return nca_result;
|
||||
}
|
||||
}
|
||||
|
||||
@ -663,6 +673,8 @@ InstallResult RegisteredCache::InstallEntry(const NCA& nca, TitleType type,
|
||||
}
|
||||
|
||||
bool RegisteredCache::RemoveExistingEntry(u64 title_id) const {
|
||||
bool removed_data = false;
|
||||
|
||||
const auto delete_nca = [this](const NcaID& id) {
|
||||
const auto path = GetRelativePathFromNcaID(id, false, true, false);
|
||||
|
||||
@ -706,11 +718,18 @@ bool RegisteredCache::RemoveExistingEntry(u64 title_id) const {
|
||||
const auto deleted_html = delete_nca(html_id);
|
||||
const auto deleted_legal = delete_nca(legal_id);
|
||||
|
||||
return deleted_meta && (deleted_meta || deleted_program || deleted_data ||
|
||||
deleted_control || deleted_html || deleted_legal);
|
||||
removed_data |= (deleted_meta || deleted_program || deleted_data || deleted_control ||
|
||||
deleted_html || deleted_legal);
|
||||
}
|
||||
|
||||
return false;
|
||||
// If patch entries for any program exist in yuzu meta, remove them
|
||||
for (u8 i = 0; i < 0x10; i++) {
|
||||
const auto meta_dir = dir->CreateDirectoryRelative("yuzu_meta");
|
||||
const auto filename = GetCNMTName(TitleType::Update, title_id + i);
|
||||
removed_data |= meta_dir->DeleteFile(filename);
|
||||
}
|
||||
|
||||
return removed_data;
|
||||
}
|
||||
|
||||
InstallResult RegisteredCache::RawInstallNCA(const NCA& nca, const VfsCopyFunction& copy,
|
||||
|
@ -3,6 +3,8 @@
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/settings.h"
|
||||
#include "common/settings_enums.h"
|
||||
#include "core/frontend/applets/controller.h"
|
||||
#include "core/hid/emulated_controller.h"
|
||||
#include "core/hid/hid_core.h"
|
||||
@ -62,7 +64,7 @@ void DefaultControllerApplet::ReconfigureControllers(ReconfigureCallback callbac
|
||||
controller->Connect(true);
|
||||
}
|
||||
} else if (index == 0 && parameters.enable_single_mode && parameters.allow_handheld &&
|
||||
!Settings::values.use_docked_mode.GetValue()) {
|
||||
!Settings::IsDockedMode()) {
|
||||
// We should *never* reach here under any normal circumstances.
|
||||
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld);
|
||||
controller->Connect(true);
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/settings.h"
|
||||
#include "common/settings_enums.h"
|
||||
#include "core/frontend/framebuffer_layout.h"
|
||||
|
||||
namespace Layout {
|
||||
@ -49,7 +50,7 @@ FramebufferLayout DefaultFrameLayout(u32 width, u32 height) {
|
||||
}
|
||||
|
||||
FramebufferLayout FrameLayoutFromResolutionScale(f32 res_scale) {
|
||||
const bool is_docked = Settings::values.use_docked_mode.GetValue();
|
||||
const bool is_docked = Settings::IsDockedMode();
|
||||
const u32 screen_width = is_docked ? ScreenDocked::Width : ScreenUndocked::Width;
|
||||
const u32 screen_height = is_docked ? ScreenDocked::Height : ScreenUndocked::Height;
|
||||
|
||||
|
@ -38,7 +38,7 @@ namespace {
|
||||
*/
|
||||
void SetupMainThread(Core::System& system, KProcess& owner_process, u32 priority,
|
||||
KProcessAddress stack_top) {
|
||||
const KProcessAddress entry_point = owner_process.GetPageTable().GetCodeRegionStart();
|
||||
const KProcessAddress entry_point = owner_process.GetEntryPoint();
|
||||
ASSERT(owner_process.GetResourceLimit()->Reserve(LimitableResource::ThreadCountMax, 1));
|
||||
|
||||
KThread* thread = KThread::Create(system.Kernel());
|
||||
@ -358,6 +358,21 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:
|
||||
m_system_resource_size = metadata.GetSystemResourceSize();
|
||||
m_image_size = code_size;
|
||||
|
||||
if (metadata.GetAddressSpaceType() == FileSys::ProgramAddressSpaceType::Is39Bit) {
|
||||
// For 39-bit processes, the ASLR region starts at 0x800'0000 and is ~512GiB large.
|
||||
// However, some (buggy) programs/libraries like skyline incorrectly depend on the
|
||||
// existence of ASLR pages before the entry point, so we will adjust the load address
|
||||
// to point to about 2GiB into the ASLR region.
|
||||
m_code_address = 0x8000'0000;
|
||||
} else {
|
||||
// All other processes can be mapped at the beginning of the code region.
|
||||
if (metadata.GetAddressSpaceType() == FileSys::ProgramAddressSpaceType::Is36Bit) {
|
||||
m_code_address = 0x800'0000;
|
||||
} else {
|
||||
m_code_address = 0x20'0000;
|
||||
}
|
||||
}
|
||||
|
||||
KScopedResourceReservation memory_reservation(
|
||||
m_resource_limit, LimitableResource::PhysicalMemoryMax, code_size + m_system_resource_size);
|
||||
if (!memory_reservation.Succeeded()) {
|
||||
@ -368,15 +383,15 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:
|
||||
// Initialize process address space
|
||||
if (const Result result{m_page_table.InitializeForProcess(
|
||||
metadata.GetAddressSpaceType(), false, false, false, KMemoryManager::Pool::Application,
|
||||
0x8000000, code_size, std::addressof(m_kernel.GetAppSystemResource()), m_resource_limit,
|
||||
m_kernel.System().ApplicationMemory())};
|
||||
this->GetEntryPoint(), code_size, std::addressof(m_kernel.GetAppSystemResource()),
|
||||
m_resource_limit, m_kernel.System().ApplicationMemory())};
|
||||
result.IsError()) {
|
||||
R_RETURN(result);
|
||||
}
|
||||
|
||||
// Map process code region
|
||||
if (const Result result{m_page_table.MapProcessCode(m_page_table.GetCodeRegionStart(),
|
||||
code_size / PageSize, KMemoryState::Code,
|
||||
if (const Result result{m_page_table.MapProcessCode(this->GetEntryPoint(), code_size / PageSize,
|
||||
KMemoryState::Code,
|
||||
KMemoryPermission::None)};
|
||||
result.IsError()) {
|
||||
R_RETURN(result);
|
||||
|
@ -177,6 +177,10 @@ public:
|
||||
return m_program_id;
|
||||
}
|
||||
|
||||
KProcessAddress GetEntryPoint() const {
|
||||
return m_code_address;
|
||||
}
|
||||
|
||||
/// Gets the resource limit descriptor for this process
|
||||
KResourceLimit* GetResourceLimit() const;
|
||||
|
||||
@ -485,6 +489,9 @@ private:
|
||||
/// Address indicating the location of the process' dedicated TLS region.
|
||||
KProcessAddress m_plr_address = 0;
|
||||
|
||||
/// Address indicating the location of the process's entry point.
|
||||
KProcessAddress m_code_address = 0;
|
||||
|
||||
/// Random values for svcGetInfo RandomEntropy
|
||||
std::array<u64, RANDOM_ENTROPY_SIZE> m_random_entropy{};
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include <cinttypes>
|
||||
#include <cstring>
|
||||
#include "common/settings.h"
|
||||
#include "common/settings_enums.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
@ -833,7 +834,7 @@ void ICommonStateGetter::GetDefaultDisplayResolution(HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(ResultSuccess);
|
||||
|
||||
if (Settings::values.use_docked_mode.GetValue()) {
|
||||
if (Settings::IsDockedMode()) {
|
||||
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth));
|
||||
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight));
|
||||
} else {
|
||||
@ -921,7 +922,7 @@ void IStorage::Open(HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
void ICommonStateGetter::GetOperationMode(HLERequestContext& ctx) {
|
||||
const bool use_docked_mode{Settings::values.use_docked_mode.GetValue()};
|
||||
const bool use_docked_mode{Settings::IsDockedMode()};
|
||||
LOG_DEBUG(Service_AM, "called, use_docked_mode={}", use_docked_mode);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "common/settings.h"
|
||||
#include "common/settings_enums.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/service/apm/apm_controller.h"
|
||||
|
||||
@ -67,8 +68,7 @@ void Controller::SetFromCpuBoostMode(CpuBoostMode mode) {
|
||||
}
|
||||
|
||||
PerformanceMode Controller::GetCurrentPerformanceMode() const {
|
||||
return Settings::values.use_docked_mode.GetValue() ? PerformanceMode::Boost
|
||||
: PerformanceMode::Normal;
|
||||
return Settings::IsDockedMode() ? PerformanceMode::Boost : PerformanceMode::Normal;
|
||||
}
|
||||
|
||||
PerformanceConfiguration Controller::GetCurrentPerformanceConfiguration(PerformanceMode mode) {
|
||||
|
@ -174,7 +174,7 @@ public:
|
||||
{6, &IHardwareOpusDecoderManager::DecodeInterleaved, "DecodeInterleavedWithPerfAndResetOld"},
|
||||
{7, nullptr, "DecodeInterleavedForMultiStreamWithPerfAndResetOld"},
|
||||
{8, &IHardwareOpusDecoderManager::DecodeInterleaved, "DecodeInterleaved"},
|
||||
{9, nullptr, "DecodeInterleavedForMultiStream"},
|
||||
{9, &IHardwareOpusDecoderManager::DecodeInterleavedForMultiStream, "DecodeInterleavedForMultiStream"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@ -206,6 +206,16 @@ private:
|
||||
decoder_state.DecodeInterleaved(ctx, OpusDecoderState::PerfTime::Enabled, extra_behavior);
|
||||
}
|
||||
|
||||
void DecodeInterleavedForMultiStream(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Audio, "called");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto extra_behavior = rp.Pop<bool>() ? OpusDecoderState::ExtraBehavior::ResetContext
|
||||
: OpusDecoderState::ExtraBehavior::None;
|
||||
|
||||
decoder_state.DecodeInterleaved(ctx, OpusDecoderState::PerfTime::Enabled, extra_behavior);
|
||||
}
|
||||
|
||||
OpusDecoderState decoder_state;
|
||||
};
|
||||
|
||||
@ -354,6 +364,40 @@ void HwOpus::OpenHardwareOpusDecoderEx(HLERequestContext& ctx) {
|
||||
system, OpusDecoderState{std::move(decoder), sample_rate, channel_count});
|
||||
}
|
||||
|
||||
void HwOpus::OpenHardwareOpusDecoderForMultiStreamEx(HLERequestContext& ctx) {
|
||||
OpusMultiStreamParametersEx params;
|
||||
std::memcpy(¶ms, ctx.ReadBuffer().data(), ctx.GetReadBufferSize());
|
||||
|
||||
const auto& sample_rate = params.sample_rate;
|
||||
const auto& channel_count = params.channel_count;
|
||||
|
||||
LOG_INFO(
|
||||
Audio,
|
||||
"called with sample_rate={}, channel_count={}, number_streams={}, number_stereo_streams={}",
|
||||
sample_rate, channel_count, params.number_streams, params.number_stereo_streams);
|
||||
|
||||
ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 ||
|
||||
sample_rate == 12000 || sample_rate == 8000,
|
||||
"Invalid sample rate");
|
||||
|
||||
int error = 0;
|
||||
OpusDecoderPtr decoder{opus_multistream_decoder_create(
|
||||
sample_rate, static_cast<int>(channel_count), params.number_streams,
|
||||
params.number_stereo_streams, params.channel_mappings.data(), &error)};
|
||||
if (error != OPUS_OK || decoder == nullptr) {
|
||||
LOG_ERROR(Audio, "Failed to create Opus decoder (error={}).", error);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
// TODO(ogniK): Use correct error code
|
||||
rb.Push(ResultUnknown);
|
||||
return;
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IHardwareOpusDecoderManager>(
|
||||
system, OpusDecoderState{std::move(decoder), sample_rate, channel_count});
|
||||
}
|
||||
|
||||
HwOpus::HwOpus(Core::System& system_) : ServiceFramework{system_, "hwopus"} {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &HwOpus::OpenHardwareOpusDecoder, "OpenHardwareOpusDecoder"},
|
||||
@ -362,7 +406,8 @@ HwOpus::HwOpus(Core::System& system_) : ServiceFramework{system_, "hwopus"} {
|
||||
{3, nullptr, "GetWorkBufferSizeForMultiStream"},
|
||||
{4, &HwOpus::OpenHardwareOpusDecoderEx, "OpenHardwareOpusDecoderEx"},
|
||||
{5, &HwOpus::GetWorkBufferSizeEx, "GetWorkBufferSizeEx"},
|
||||
{6, nullptr, "OpenHardwareOpusDecoderForMultiStreamEx"},
|
||||
{6, &HwOpus::OpenHardwareOpusDecoderForMultiStreamEx,
|
||||
"OpenHardwareOpusDecoderForMultiStreamEx"},
|
||||
{7, &HwOpus::GetWorkBufferSizeForMultiStreamEx, "GetWorkBufferSizeForMultiStreamEx"},
|
||||
{8, nullptr, "GetWorkBufferSizeExEx"},
|
||||
{9, nullptr, "GetWorkBufferSizeForMultiStreamExEx"},
|
||||
|
@ -18,8 +18,10 @@ struct OpusMultiStreamParametersEx {
|
||||
u32 number_stereo_streams;
|
||||
u32 use_large_frame_size;
|
||||
u32 padding;
|
||||
std::array<u32, 64> channel_mappings;
|
||||
std::array<u8, 0x100> channel_mappings;
|
||||
};
|
||||
static_assert(sizeof(OpusMultiStreamParametersEx) == 0x118,
|
||||
"OpusMultiStreamParametersEx has incorrect size");
|
||||
|
||||
class HwOpus final : public ServiceFramework<HwOpus> {
|
||||
public:
|
||||
@ -29,6 +31,7 @@ public:
|
||||
private:
|
||||
void OpenHardwareOpusDecoder(HLERequestContext& ctx);
|
||||
void OpenHardwareOpusDecoderEx(HLERequestContext& ctx);
|
||||
void OpenHardwareOpusDecoderForMultiStreamEx(HLERequestContext& ctx);
|
||||
void GetWorkBufferSize(HLERequestContext& ctx);
|
||||
void GetWorkBufferSizeEx(HLERequestContext& ctx);
|
||||
void GetWorkBufferSizeForMultiStreamEx(HLERequestContext& ctx);
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include <utility>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/fs/fs.h"
|
||||
#include "common/fs/path_util.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/core.h"
|
||||
@ -154,10 +155,18 @@ Result VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_,
|
||||
std::string src_path(Common::FS::SanitizePath(src_path_));
|
||||
std::string dest_path(Common::FS::SanitizePath(dest_path_));
|
||||
auto src = backing->GetFileRelative(src_path);
|
||||
auto dst = backing->GetFileRelative(dest_path);
|
||||
if (Common::FS::GetParentPath(src_path) == Common::FS::GetParentPath(dest_path)) {
|
||||
// Use more-optimized vfs implementation rename.
|
||||
if (src == nullptr)
|
||||
if (src == nullptr) {
|
||||
return FileSys::ERROR_PATH_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (dst && Common::FS::Exists(dst->GetFullPath())) {
|
||||
LOG_ERROR(Service_FS, "File at new_path={} already exists", dst->GetFullPath());
|
||||
return FileSys::ERROR_PATH_ALREADY_EXISTS;
|
||||
}
|
||||
|
||||
if (!src->Rename(Common::FS::GetFilename(dest_path))) {
|
||||
// TODO(DarkLordZach): Find a better error code for this
|
||||
return ResultUnknown;
|
||||
|
@ -331,7 +331,7 @@ Controller_Gesture::GestureProperties Controller_Gesture::GetGestureProperties()
|
||||
};
|
||||
|
||||
// Hack: There is no touch in docked but games still allow it
|
||||
if (Settings::values.use_docked_mode.GetValue()) {
|
||||
if (Settings::IsDockedMode()) {
|
||||
gesture.points[id] = {
|
||||
.x = static_cast<s32>(active_x * Layout::ScreenDocked::Width),
|
||||
.y = static_cast<s32>(active_y * Layout::ScreenDocked::Height),
|
||||
|
@ -1518,7 +1518,7 @@ bool Controller_NPad::IsControllerSupported(Core::HID::NpadStyleIndex controller
|
||||
return false;
|
||||
}
|
||||
// Handheld shouldn't be supported in docked mode
|
||||
if (Settings::values.use_docked_mode.GetValue()) {
|
||||
if (Settings::IsDockedMode()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -204,9 +204,11 @@ void nvhost_as_gpu::FreeMappingLocked(u64 offset) {
|
||||
if (!mapping->fixed) {
|
||||
auto& allocator{mapping->big_page ? *vm.big_page_allocator : *vm.small_page_allocator};
|
||||
u32 page_size_bits{mapping->big_page ? vm.big_page_size_bits : VM::PAGE_SIZE_BITS};
|
||||
u32 page_size{mapping->big_page ? vm.big_page_size : VM::YUZU_PAGESIZE};
|
||||
u64 aligned_size{Common::AlignUp(mapping->size, page_size)};
|
||||
|
||||
allocator.Free(static_cast<u32>(mapping->offset >> page_size_bits),
|
||||
static_cast<u32>(mapping->size >> page_size_bits));
|
||||
static_cast<u32>(aligned_size >> page_size_bits));
|
||||
}
|
||||
|
||||
// Sparse mappings shouldn't be fully unmapped, just returned to their sparse state
|
||||
|
@ -79,8 +79,8 @@ protected:
|
||||
using HandlerFnP = void (Self::*)(HLERequestContext&);
|
||||
|
||||
/// Used to gain exclusive access to the service members, e.g. from CoreTiming thread.
|
||||
[[nodiscard]] std::scoped_lock<std::mutex> LockService() {
|
||||
return std::scoped_lock{lock_service};
|
||||
[[nodiscard]] virtual std::unique_lock<std::mutex> LockService() {
|
||||
return std::unique_lock{lock_service};
|
||||
}
|
||||
|
||||
/// System context that the service operates under.
|
||||
|
@ -1029,6 +1029,11 @@ BSD::~BSD() {
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_lock<std::mutex> BSD::LockService() {
|
||||
// Do not lock socket IClient instances.
|
||||
return {};
|
||||
}
|
||||
|
||||
BSDCFG::BSDCFG(Core::System& system_) : ServiceFramework{system_, "bsdcfg"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
|
@ -186,6 +186,9 @@ private:
|
||||
|
||||
// Callback identifier for the OnProxyPacketReceived event.
|
||||
Network::RoomMember::CallbackHandle<Network::ProxyPacket> proxy_packet_received;
|
||||
|
||||
protected:
|
||||
virtual std::unique_lock<std::mutex> LockService() override;
|
||||
};
|
||||
|
||||
class BSDCFG final : public ServiceFramework<BSDCFG> {
|
||||
|
@ -19,6 +19,12 @@ enum class ServerEnvironmentType : u8 {
|
||||
Dp,
|
||||
};
|
||||
|
||||
// This is nn::nsd::EnvironmentIdentifier
|
||||
struct EnvironmentIdentifier {
|
||||
std::array<u8, 8> identifier;
|
||||
};
|
||||
static_assert(sizeof(EnvironmentIdentifier) == 0x8);
|
||||
|
||||
NSD::NSD(Core::System& system_, const char* name) : ServiceFramework{system_, name} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
@ -101,8 +107,9 @@ void NSD::ResolveEx(HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
void NSD::GetEnvironmentIdentifier(HLERequestContext& ctx) {
|
||||
const std::string environment_identifier = "lp1";
|
||||
ctx.WriteBuffer(environment_identifier);
|
||||
constexpr EnvironmentIdentifier lp1 = {
|
||||
.identifier = {'l', 'p', '1', '\0', '\0', '\0', '\0', '\0'}};
|
||||
ctx.WriteBuffer(lp1);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
|
@ -150,6 +150,12 @@ static std::pair<u32, GetAddrInfoError> GetHostByNameRequestImpl(HLERequestConte
|
||||
const std::string host = Common::StringFromBuffer(host_buffer);
|
||||
// For now, ignore options, which are in input buffer 1 for GetHostByNameRequestWithOptions.
|
||||
|
||||
// Prevent resolution of Nintendo servers
|
||||
if (host.find("srv.nintendo.net") != std::string::npos) {
|
||||
LOG_WARNING(Network, "Resolution of hostname {} requested, returning EAI_AGAIN", host);
|
||||
return {0, GetAddrInfoError::AGAIN};
|
||||
}
|
||||
|
||||
auto res = Network::GetAddressInfo(host, /*service*/ std::nullopt);
|
||||
if (!res.has_value()) {
|
||||
return {0, Translate(res.error())};
|
||||
@ -261,6 +267,12 @@ static std::pair<u32, GetAddrInfoError> GetAddrInfoRequestImpl(HLERequestContext
|
||||
const auto host_buffer = ctx.ReadBuffer(0);
|
||||
const std::string host = Common::StringFromBuffer(host_buffer);
|
||||
|
||||
// Prevent resolution of Nintendo servers
|
||||
if (host.find("srv.nintendo.net") != std::string::npos) {
|
||||
LOG_WARNING(Network, "Resolution of hostname {} requested, returning EAI_AGAIN", host);
|
||||
return {0, GetAddrInfoError::AGAIN};
|
||||
}
|
||||
|
||||
std::optional<std::string> service = std::nullopt;
|
||||
if (ctx.CanReadBuffer(1)) {
|
||||
const std::span<const u8> service_buffer = ctx.ReadBuffer(1);
|
||||
|
@ -139,7 +139,6 @@ private:
|
||||
bool do_not_close_socket = false;
|
||||
bool get_server_cert_chain = false;
|
||||
std::shared_ptr<Network::SocketBase> socket;
|
||||
bool did_set_host_name = false;
|
||||
bool did_handshake = false;
|
||||
|
||||
Result SetSocketDescriptorImpl(s32* out_fd, s32 fd) {
|
||||
@ -174,11 +173,7 @@ private:
|
||||
Result SetHostNameImpl(const std::string& hostname) {
|
||||
LOG_DEBUG(Service_SSL, "called. hostname={}", hostname);
|
||||
ASSERT(!did_handshake);
|
||||
Result res = backend->SetHostName(hostname);
|
||||
if (res == ResultSuccess) {
|
||||
did_set_host_name = true;
|
||||
}
|
||||
return res;
|
||||
return backend->SetHostName(hostname);
|
||||
}
|
||||
|
||||
Result SetVerifyOptionImpl(u32 option) {
|
||||
@ -208,9 +203,6 @@ private:
|
||||
|
||||
Result DoHandshakeImpl() {
|
||||
ASSERT_OR_EXECUTE(!did_handshake && socket, { return ResultNoSocket; });
|
||||
ASSERT_OR_EXECUTE_MSG(
|
||||
did_set_host_name, { return ResultInternalError; },
|
||||
"Expected SetHostName before DoHandshake");
|
||||
Result res = backend->DoHandshake();
|
||||
did_handshake = res.IsSuccess();
|
||||
return res;
|
||||
|
@ -167,9 +167,8 @@ public:
|
||||
}
|
||||
|
||||
~SSLConnectionBackendOpenSSL() {
|
||||
// these are null-tolerant:
|
||||
// this is null-tolerant:
|
||||
SSL_free(ssl);
|
||||
BIO_free(bio);
|
||||
}
|
||||
|
||||
static void KeyLogCallback(const SSL* ssl, const char* line) {
|
||||
|
@ -32,7 +32,7 @@ static void OneTimeInit() {
|
||||
schannel_cred.dwVersion = SCHANNEL_CRED_VERSION;
|
||||
schannel_cred.dwFlags =
|
||||
SCH_USE_STRONG_CRYPTO | // don't allow insecure protocols
|
||||
SCH_CRED_AUTO_CRED_VALIDATION | // validate certs
|
||||
SCH_CRED_NO_SERVERNAME_CHECK | // don't validate server names
|
||||
SCH_CRED_NO_DEFAULT_CREDS; // don't automatically present a client certificate
|
||||
// ^ I'm assuming that nobody would want to connect Yuzu to a
|
||||
// service that requires some OS-provided corporate client
|
||||
@ -227,10 +227,9 @@ public:
|
||||
ciphertext_read_buf.size());
|
||||
}
|
||||
|
||||
const SECURITY_STATUS ret =
|
||||
InitializeSecurityContextA(&cred_handle, initial_call_done ? &ctxt : nullptr,
|
||||
// Caller ensured we have set a hostname:
|
||||
const_cast<char*>(hostname.value().c_str()), req,
|
||||
char* hostname_ptr = hostname ? const_cast<char*>(hostname->c_str()) : nullptr;
|
||||
const SECURITY_STATUS ret = InitializeSecurityContextA(
|
||||
&cred_handle, initial_call_done ? &ctxt : nullptr, hostname_ptr, req,
|
||||
0, // Reserved1
|
||||
0, // TargetDataRep not used with Schannel
|
||||
initial_call_done ? &input_desc : nullptr,
|
||||
|
@ -217,7 +217,7 @@ private:
|
||||
IPC::ResponseBuilder rb{ctx, 6};
|
||||
rb.Push(ResultSuccess);
|
||||
|
||||
if (Settings::values.use_docked_mode.GetValue()) {
|
||||
if (Settings::IsDockedMode()) {
|
||||
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth));
|
||||
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight));
|
||||
} else {
|
||||
|
@ -153,7 +153,7 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
|
||||
|
||||
// Load NSO modules
|
||||
modules.clear();
|
||||
const VAddr base_address{GetInteger(process.GetPageTable().GetCodeRegionStart())};
|
||||
const VAddr base_address{GetInteger(process.GetEntryPoint())};
|
||||
VAddr next_load_addr{base_address};
|
||||
const FileSys::PatchManager pm{metadata.GetTitleID(), system.GetFileSystemController(),
|
||||
system.GetContentProvider()};
|
||||
|
@ -96,7 +96,7 @@ AppLoader::LoadResult AppLoader_KIP::Load(Kernel::KProcess& process,
|
||||
}
|
||||
|
||||
codeset.memory = std::move(program_image);
|
||||
const VAddr base_address = GetInteger(process.GetPageTable().GetCodeRegionStart());
|
||||
const VAddr base_address = GetInteger(process.GetEntryPoint());
|
||||
process.LoadModule(std::move(codeset), base_address);
|
||||
|
||||
LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", kip->GetName(), base_address);
|
||||
|
@ -203,7 +203,7 @@ static bool LoadNroImpl(Kernel::KProcess& process, const std::vector<u8>& data)
|
||||
|
||||
// Load codeset for current process
|
||||
codeset.memory = std::move(program_image);
|
||||
process.LoadModule(std::move(codeset), process.GetPageTable().GetCodeRegionStart());
|
||||
process.LoadModule(std::move(codeset), process.GetEntryPoint());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -167,7 +167,7 @@ AppLoader_NSO::LoadResult AppLoader_NSO::Load(Kernel::KProcess& process, Core::S
|
||||
modules.clear();
|
||||
|
||||
// Load module
|
||||
const VAddr base_address = GetInteger(process.GetPageTable().GetCodeRegionStart());
|
||||
const VAddr base_address = GetInteger(process.GetEntryPoint());
|
||||
if (!LoadModule(process, system, *file, base_address, true, true)) {
|
||||
return {ResultStatus::ErrorLoadingNSO, {}};
|
||||
}
|
||||
|
@ -117,8 +117,8 @@ json GetProcessorStateDataAuto(Core::System& system) {
|
||||
arm.SaveContext(context);
|
||||
|
||||
return GetProcessorStateData(process->Is64BitProcess() ? "AArch64" : "AArch32",
|
||||
GetInteger(process->GetPageTable().GetCodeRegionStart()),
|
||||
context.sp, context.pc, context.pstate, context.cpu_registers);
|
||||
GetInteger(process->GetEntryPoint()), context.sp, context.pc,
|
||||
context.pstate, context.cpu_registers);
|
||||
}
|
||||
|
||||
json GetBacktraceData(Core::System& system) {
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "common/logging/log.h"
|
||||
|
||||
#include "common/settings.h"
|
||||
#include "common/settings_enums.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
#include "core/loader/loader.h"
|
||||
@ -275,7 +276,7 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader,
|
||||
static_cast<u32>(Settings::values.shader_backend.GetValue()));
|
||||
AddField(field_type, "Renderer_UseAsynchronousShaders",
|
||||
Settings::values.use_asynchronous_shaders.GetValue());
|
||||
AddField(field_type, "System_UseDockedMode", Settings::values.use_docked_mode.GetValue());
|
||||
AddField(field_type, "System_UseDockedMode", Settings::IsDockedMode());
|
||||
}
|
||||
|
||||
bool TelemetrySession::SubmitTestcase() {
|
||||
|
@ -835,15 +835,15 @@ public:
|
||||
return input_engine->SupportsNfc(identifier);
|
||||
}
|
||||
|
||||
Common::Input::NfcState StartNfcPolling() {
|
||||
Common::Input::NfcState StartNfcPolling() override {
|
||||
return input_engine->StartNfcPolling(identifier);
|
||||
}
|
||||
|
||||
Common::Input::NfcState StopNfcPolling() {
|
||||
Common::Input::NfcState StopNfcPolling() override {
|
||||
return input_engine->StopNfcPolling(identifier);
|
||||
}
|
||||
|
||||
Common::Input::NfcState ReadAmiiboData(std::vector<u8>& out_data) {
|
||||
Common::Input::NfcState ReadAmiiboData(std::vector<u8>& out_data) override {
|
||||
return input_engine->ReadAmiiboData(identifier, out_data);
|
||||
}
|
||||
|
||||
@ -852,11 +852,11 @@ public:
|
||||
}
|
||||
|
||||
Common::Input::NfcState ReadMifareData(const Common::Input::MifareRequest& request,
|
||||
Common::Input::MifareRequest& out_data) {
|
||||
Common::Input::MifareRequest& out_data) override {
|
||||
return input_engine->ReadMifareData(identifier, request, out_data);
|
||||
}
|
||||
|
||||
Common::Input::NfcState WriteMifareData(const Common::Input::MifareRequest& request) {
|
||||
Common::Input::NfcState WriteMifareData(const Common::Input::MifareRequest& request) override {
|
||||
return input_engine->WriteMifareData(identifier, request);
|
||||
}
|
||||
|
||||
|
@ -289,8 +289,11 @@ std::pair<typename P::Buffer*, u32> BufferCache<P>::ObtainBuffer(GPUVAddr gpu_ad
|
||||
MarkWrittenBuffer(buffer_id, *cpu_addr, size);
|
||||
break;
|
||||
case ObtainBufferOperation::DiscardWrite: {
|
||||
IntervalType interval{*cpu_addr, size};
|
||||
VAddr cpu_addr_start = Common::AlignDown(*cpu_addr, 64);
|
||||
VAddr cpu_addr_end = Common::AlignUp(*cpu_addr + size, 64);
|
||||
IntervalType interval{cpu_addr_start, cpu_addr_end};
|
||||
ClearDownload(interval);
|
||||
common_ranges.subtract(interval);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@ -1159,6 +1162,11 @@ void BufferCache<P>::UpdateDrawIndirect() {
|
||||
.size = static_cast<u32>(size),
|
||||
.buffer_id = FindBuffer(*cpu_addr, static_cast<u32>(size)),
|
||||
};
|
||||
VAddr cpu_addr_start = Common::AlignDown(*cpu_addr, 64);
|
||||
VAddr cpu_addr_end = Common::AlignUp(*cpu_addr + size, 64);
|
||||
IntervalType interval{cpu_addr_start, cpu_addr_end};
|
||||
ClearDownload(interval);
|
||||
common_ranges.subtract(interval);
|
||||
};
|
||||
if (current_draw_indirect->include_count) {
|
||||
update(current_draw_indirect->count_start_address, sizeof(u32),
|
||||
|
@ -319,6 +319,7 @@ void Codec::Decode() {
|
||||
LOG_WARNING(Service_NVDRV, "Zero width or height in frame");
|
||||
return;
|
||||
}
|
||||
bool is_interlaced = initial_frame->interlaced_frame != 0;
|
||||
if (av_codec_ctx->hw_device_ctx) {
|
||||
final_frame = AVFramePtr{av_frame_alloc(), AVFrameDeleter};
|
||||
ASSERT_MSG(final_frame, "av_frame_alloc final_frame failed");
|
||||
@ -334,7 +335,7 @@ void Codec::Decode() {
|
||||
UNIMPLEMENTED_MSG("Unexpected video format: {}", final_frame->format);
|
||||
return;
|
||||
}
|
||||
if (!final_frame->interlaced_frame) {
|
||||
if (!is_interlaced) {
|
||||
av_frames.push(std::move(final_frame));
|
||||
} else {
|
||||
if (!filters_initialized) {
|
||||
|
@ -27,14 +27,24 @@ MICROPROFILE_DEFINE(MacroHLE, "GPU", "Execute macro HLE", MP_RGB(128, 192, 192))
|
||||
|
||||
namespace Tegra {
|
||||
|
||||
static void Dump(u64 hash, std::span<const u32> code) {
|
||||
static void Dump(u64 hash, std::span<const u32> code, bool decompiled = false) {
|
||||
const auto base_dir{Common::FS::GetYuzuPath(Common::FS::YuzuPath::DumpDir)};
|
||||
const auto macro_dir{base_dir / "macros"};
|
||||
if (!Common::FS::CreateDir(base_dir) || !Common::FS::CreateDir(macro_dir)) {
|
||||
LOG_ERROR(Common_Filesystem, "Failed to create macro dump directories");
|
||||
return;
|
||||
}
|
||||
const auto name{macro_dir / fmt::format("{:016x}.macro", hash)};
|
||||
auto name{macro_dir / fmt::format("{:016x}.macro", hash)};
|
||||
|
||||
if (decompiled) {
|
||||
auto new_name{macro_dir / fmt::format("decompiled_{:016x}.macro", hash)};
|
||||
if (Common::FS::Exists(name)) {
|
||||
(void)Common::FS::RenameFile(name, new_name);
|
||||
return;
|
||||
}
|
||||
name = new_name;
|
||||
}
|
||||
|
||||
std::fstream macro_file(name, std::ios::out | std::ios::binary);
|
||||
if (!macro_file) {
|
||||
LOG_ERROR(Common_Filesystem, "Unable to open or create file at {}",
|
||||
@ -90,9 +100,6 @@ void MacroEngine::Execute(u32 method, const std::vector<u32>& parameters) {
|
||||
if (!mid_method.has_value()) {
|
||||
cache_info.lle_program = Compile(macro_code->second);
|
||||
cache_info.hash = Common::HashValue(macro_code->second);
|
||||
if (Settings::values.dump_macros) {
|
||||
Dump(cache_info.hash, macro_code->second);
|
||||
}
|
||||
} else {
|
||||
const auto& macro_cached = uploaded_macro_code[mid_method.value()];
|
||||
const auto rebased_method = method - mid_method.value();
|
||||
@ -102,9 +109,6 @@ void MacroEngine::Execute(u32 method, const std::vector<u32>& parameters) {
|
||||
code.size() * sizeof(u32));
|
||||
cache_info.hash = Common::HashValue(code);
|
||||
cache_info.lle_program = Compile(code);
|
||||
if (Settings::values.dump_macros) {
|
||||
Dump(cache_info.hash, code);
|
||||
}
|
||||
}
|
||||
|
||||
auto hle_program = hle_macros->GetHLEProgram(cache_info.hash);
|
||||
@ -117,6 +121,10 @@ void MacroEngine::Execute(u32 method, const std::vector<u32>& parameters) {
|
||||
MICROPROFILE_SCOPE(MacroHLE);
|
||||
cache_info.hle_program->Execute(parameters, method);
|
||||
}
|
||||
|
||||
if (Settings::values.dump_macros) {
|
||||
Dump(cache_info.hash, macro_code->second, cache_info.has_hle_program);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -611,9 +611,6 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
|
||||
|
||||
const u32 cfg_offset{static_cast<u32>(env.StartAddress() + sizeof(Shader::ProgramHeader))};
|
||||
Shader::Maxwell::Flow::CFG cfg(env, pools.flow_block, cfg_offset, index == 0);
|
||||
if (Settings::values.dump_shaders) {
|
||||
env.Dump(hash, key.unique_hashes[index]);
|
||||
}
|
||||
if (!uses_vertex_a || index != 1) {
|
||||
// Normal path
|
||||
programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info);
|
||||
@ -624,6 +621,10 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
|
||||
programs[index] = MergeDualVertexPrograms(program_va, program_vb, env);
|
||||
}
|
||||
|
||||
if (Settings::values.dump_shaders) {
|
||||
env.Dump(hash, key.unique_hashes[index]);
|
||||
}
|
||||
|
||||
if (programs[index].info.requires_layer_emulation) {
|
||||
layer_source_program = &programs[index];
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations" // for deprecated OpenSSL functions
|
||||
#endif
|
||||
#include <jwt/jwt.hpp>
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
|
@ -5,6 +5,8 @@
|
||||
#include <thread>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/settings.h"
|
||||
#include "common/settings_enums.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hid/emulated_controller.h"
|
||||
@ -226,9 +228,11 @@ int QtControllerSelectorDialog::exec() {
|
||||
}
|
||||
|
||||
void QtControllerSelectorDialog::ApplyConfiguration() {
|
||||
const bool pre_docked_mode = Settings::values.use_docked_mode.GetValue();
|
||||
Settings::values.use_docked_mode.SetValue(ui->radioDocked->isChecked());
|
||||
OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode.GetValue(), system);
|
||||
const bool pre_docked_mode = Settings::IsDockedMode();
|
||||
const bool docked_mode_selected = ui->radioDocked->isChecked();
|
||||
Settings::values.use_docked_mode.SetValue(
|
||||
docked_mode_selected ? Settings::ConsoleMode::Docked : Settings::ConsoleMode::Handheld);
|
||||
OnDockedModeChanged(pre_docked_mode, docked_mode_selected, system);
|
||||
|
||||
Settings::values.vibration_enabled.SetValue(ui->vibrationGroup->isChecked());
|
||||
Settings::values.motion_enabled.SetValue(ui->motionGroup->isChecked());
|
||||
@ -616,8 +620,8 @@ void QtControllerSelectorDialog::UpdateDockedState(bool is_handheld) {
|
||||
ui->radioDocked->setEnabled(!is_handheld);
|
||||
ui->radioUndocked->setEnabled(!is_handheld);
|
||||
|
||||
ui->radioDocked->setChecked(Settings::values.use_docked_mode.GetValue());
|
||||
ui->radioUndocked->setChecked(!Settings::values.use_docked_mode.GetValue());
|
||||
ui->radioDocked->setChecked(Settings::IsDockedMode());
|
||||
ui->radioUndocked->setChecked(!Settings::IsDockedMode());
|
||||
|
||||
// Also force into undocked mode if the controller type is handheld.
|
||||
if (is_handheld) {
|
||||
|
@ -928,7 +928,7 @@ void GRenderWindow::CaptureScreenshot(const QString& screenshot_path) {
|
||||
const Layout::FramebufferLayout layout{[]() {
|
||||
u32 height = UISettings::values.screenshot_height.GetValue();
|
||||
if (height == 0) {
|
||||
height = Settings::values.use_docked_mode.GetValue() ? Layout::ScreenDocked::Height
|
||||
height = Settings::IsDockedMode() ? Layout::ScreenDocked::Height
|
||||
: Layout::ScreenUndocked::Height;
|
||||
height *= Settings::values.resolution_info.up_factor;
|
||||
}
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "common/fs/path_util.h"
|
||||
#include "common/settings.h"
|
||||
#include "common/settings_common.h"
|
||||
#include "common/settings_enums.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/acc/profile_manager.h"
|
||||
#include "core/hle/service/hid/controllers/npad.h"
|
||||
@ -85,9 +86,9 @@ const std::map<Settings::ScalingFilter, QString> Config::scaling_filter_texts_ma
|
||||
{Settings::ScalingFilter::Fsr, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "FSR"))},
|
||||
};
|
||||
|
||||
const std::map<bool, QString> Config::use_docked_mode_texts_map = {
|
||||
{true, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Docked"))},
|
||||
{false, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Handheld"))},
|
||||
const std::map<Settings::ConsoleMode, QString> Config::use_docked_mode_texts_map = {
|
||||
{Settings::ConsoleMode::Docked, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Docked"))},
|
||||
{Settings::ConsoleMode::Handheld, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Handheld"))},
|
||||
};
|
||||
|
||||
const std::map<Settings::GpuAccuracy, QString> Config::gpu_accuracy_texts_map = {
|
||||
@ -376,7 +377,7 @@ void Config::ReadControlValues() {
|
||||
const auto controller_type = Settings::values.players.GetValue()[0].controller_type;
|
||||
if (controller_type == Settings::ControllerType::Handheld) {
|
||||
Settings::values.use_docked_mode.SetGlobal(!IsCustomConfig());
|
||||
Settings::values.use_docked_mode.SetValue(false);
|
||||
Settings::values.use_docked_mode.SetValue(Settings::ConsoleMode::Handheld);
|
||||
}
|
||||
|
||||
if (IsCustomConfig()) {
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include <QMetaType>
|
||||
#include <QVariant>
|
||||
#include "common/settings.h"
|
||||
#include "common/settings_enums.h"
|
||||
#include "yuzu/uisettings.h"
|
||||
|
||||
class QSettings;
|
||||
@ -51,7 +52,7 @@ public:
|
||||
|
||||
static const std::map<Settings::AntiAliasing, QString> anti_aliasing_texts_map;
|
||||
static const std::map<Settings::ScalingFilter, QString> scaling_filter_texts_map;
|
||||
static const std::map<bool, QString> use_docked_mode_texts_map;
|
||||
static const std::map<Settings::ConsoleMode, QString> use_docked_mode_texts_map;
|
||||
static const std::map<Settings::GpuAccuracy, QString> gpu_accuracy_texts_map;
|
||||
static const std::map<Settings::RendererBackend, QString> renderer_backend_texts_map;
|
||||
static const std::map<Settings::ShaderBackend, QString> shader_backend_texts_map;
|
||||
|
@ -4,6 +4,8 @@
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
|
||||
#include "common/settings.h"
|
||||
#include "common/settings_enums.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hid/emulated_controller.h"
|
||||
#include "core/hid/hid_core.h"
|
||||
@ -197,9 +199,11 @@ void ConfigureInput::ApplyConfiguration() {
|
||||
|
||||
advanced->ApplyConfiguration();
|
||||
|
||||
const bool pre_docked_mode = Settings::values.use_docked_mode.GetValue();
|
||||
Settings::values.use_docked_mode.SetValue(ui->radioDocked->isChecked());
|
||||
OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode.GetValue(), system);
|
||||
const bool pre_docked_mode = Settings::IsDockedMode();
|
||||
const bool docked_mode_selected = ui->radioDocked->isChecked();
|
||||
Settings::values.use_docked_mode.SetValue(
|
||||
docked_mode_selected ? Settings::ConsoleMode::Docked : Settings::ConsoleMode::Handheld);
|
||||
OnDockedModeChanged(pre_docked_mode, docked_mode_selected, system);
|
||||
|
||||
Settings::values.vibration_enabled.SetValue(ui->vibrationGroup->isChecked());
|
||||
Settings::values.motion_enabled.SetValue(ui->motionGroup->isChecked());
|
||||
@ -267,8 +271,8 @@ void ConfigureInput::UpdateDockedState(bool is_handheld) {
|
||||
ui->radioDocked->setEnabled(!is_handheld);
|
||||
ui->radioUndocked->setEnabled(!is_handheld);
|
||||
|
||||
ui->radioDocked->setChecked(Settings::values.use_docked_mode.GetValue());
|
||||
ui->radioUndocked->setChecked(!Settings::values.use_docked_mode.GetValue());
|
||||
ui->radioDocked->setChecked(Settings::IsDockedMode());
|
||||
ui->radioUndocked->setChecked(!Settings::IsDockedMode());
|
||||
|
||||
// Also force into undocked mode if the controller type is handheld.
|
||||
if (is_handheld) {
|
||||
|
@ -18,6 +18,7 @@
|
||||
|
||||
#include "common/fs/fs_util.h"
|
||||
#include "common/settings_enums.h"
|
||||
#include "common/settings_input.h"
|
||||
#include "configuration/shared_widget.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
@ -98,6 +99,12 @@ void ConfigurePerGame::ApplyConfiguration() {
|
||||
addons_tab->ApplyConfiguration();
|
||||
input_tab->ApplyConfiguration();
|
||||
|
||||
if (Settings::IsDockedMode() && Settings::values.players.GetValue()[0].controller_type ==
|
||||
Settings::ControllerType::Handheld) {
|
||||
Settings::values.use_docked_mode.SetValue(Settings::ConsoleMode::Handheld);
|
||||
Settings::values.use_docked_mode.SetGlobal(true);
|
||||
}
|
||||
|
||||
system.ApplySettings();
|
||||
Settings::LogSettings();
|
||||
|
||||
|
@ -106,6 +106,11 @@ void ConfigureSystem::Setup(const ConfigurationShared::Builder& builder) {
|
||||
push(Settings::values.linkage.by_category[Settings::Category::System]);
|
||||
|
||||
for (auto setting : settings) {
|
||||
if (setting->Id() == Settings::values.use_docked_mode.Id() &&
|
||||
Settings::IsConfiguringGlobal()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ConfigurationShared::Widget* widget = builder.BuildWidget(setting, apply_funcs);
|
||||
|
||||
if (widget == nullptr) {
|
||||
|
@ -135,7 +135,7 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) {
|
||||
INSERT(Settings, region_index, "Region:", "");
|
||||
INSERT(Settings, time_zone_index, "Time Zone:", "");
|
||||
INSERT(Settings, sound_index, "Sound Output Mode:", "");
|
||||
INSERT(Settings, use_docked_mode, "", "");
|
||||
INSERT(Settings, use_docked_mode, "Console Mode:", "");
|
||||
INSERT(Settings, current_user, "", "");
|
||||
|
||||
// Controls
|
||||
@ -379,6 +379,9 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent) {
|
||||
PAIR(MemoryLayout, Memory_6Gb, "6GB DRAM (Unsafe)"),
|
||||
PAIR(MemoryLayout, Memory_8Gb, "8GB DRAM (Unsafe)"),
|
||||
}});
|
||||
translations->insert(
|
||||
{Settings::EnumMetadata<Settings::ConsoleMode>::Index(),
|
||||
{PAIR(ConsoleMode, Docked, "Docked"), PAIR(ConsoleMode, Handheld, "Handheld")}});
|
||||
|
||||
#undef PAIR
|
||||
#undef CTX_PAIR
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include <QLineEdit>
|
||||
#include <QObject>
|
||||
#include <QPushButton>
|
||||
#include <QRadioButton>
|
||||
#include <QRegularExpression>
|
||||
#include <QSizePolicy>
|
||||
#include <QSlider>
|
||||
@ -171,6 +172,65 @@ QWidget* Widget::CreateCombobox(std::function<std::string()>& serializer,
|
||||
return combobox;
|
||||
}
|
||||
|
||||
QWidget* Widget::CreateRadioGroup(std::function<std::string()>& serializer,
|
||||
std::function<void()>& restore_func,
|
||||
const std::function<void()>& touch) {
|
||||
const auto type = setting.EnumIndex();
|
||||
|
||||
QWidget* group = new QWidget(this);
|
||||
QHBoxLayout* layout = new QHBoxLayout(group);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
group->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
|
||||
|
||||
const ComboboxTranslations* enumeration{nullptr};
|
||||
if (combobox_enumerations.contains(type)) {
|
||||
enumeration = &combobox_enumerations.at(type);
|
||||
for (const auto& [id, name] : *enumeration) {
|
||||
QRadioButton* radio_button = new QRadioButton(name, group);
|
||||
layout->addWidget(radio_button);
|
||||
radio_buttons.push_back({id, radio_button});
|
||||
}
|
||||
} else {
|
||||
return group;
|
||||
}
|
||||
|
||||
const auto get_selected = [=]() -> u32 {
|
||||
for (const auto& [id, button] : radio_buttons) {
|
||||
if (button->isChecked()) {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
|
||||
const auto set_index = [=](u32 value) {
|
||||
for (const auto& [id, button] : radio_buttons) {
|
||||
button->setChecked(id == value);
|
||||
}
|
||||
};
|
||||
|
||||
const u32 setting_value = std::stoi(setting.ToString());
|
||||
set_index(setting_value);
|
||||
|
||||
serializer = [get_selected]() {
|
||||
int current = get_selected();
|
||||
return std::to_string(current);
|
||||
};
|
||||
|
||||
restore_func = [this, set_index]() {
|
||||
const u32 global_value = std::stoi(RelevantDefault(setting));
|
||||
set_index(global_value);
|
||||
};
|
||||
|
||||
if (!Settings::IsConfiguringGlobal()) {
|
||||
for (const auto& [id, button] : radio_buttons) {
|
||||
QObject::connect(button, &QAbstractButton::clicked, [touch]() { touch(); });
|
||||
}
|
||||
}
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
QWidget* Widget::CreateLineEdit(std::function<std::string()>& serializer,
|
||||
std::function<void()>& restore_func,
|
||||
const std::function<void()>& touch, bool managed) {
|
||||
@ -235,6 +295,7 @@ QWidget* Widget::CreateSlider(bool reversed, float multiplier, const QString& gi
|
||||
slider->setValue(std::stoi(setting.ToString()));
|
||||
|
||||
slider->setInvertedAppearance(reversed);
|
||||
slider->setInvertedControls(reversed);
|
||||
|
||||
serializer = [this]() { return std::to_string(slider->value()); };
|
||||
restore_func = [this]() { slider->setValue(std::stoi(RelevantDefault(setting))); };
|
||||
@ -410,6 +471,8 @@ void Widget::SetupComponent(const QString& label, std::function<void()>& load_fu
|
||||
return RequestType::Slider;
|
||||
case Settings::Specialization::Countable:
|
||||
return RequestType::SpinBox;
|
||||
case Settings::Specialization::Radio:
|
||||
return RequestType::RadioGroup;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -438,7 +501,11 @@ void Widget::SetupComponent(const QString& label, std::function<void()>& load_fu
|
||||
if (setting.TypeId() == typeid(bool)) {
|
||||
data_component = CreateCheckBox(&setting, label, serializer, restore_func, touch);
|
||||
} else if (setting.IsEnum()) {
|
||||
if (request == RequestType::RadioGroup) {
|
||||
data_component = CreateRadioGroup(serializer, restore_func, touch);
|
||||
} else {
|
||||
data_component = CreateCombobox(serializer, restore_func, touch);
|
||||
}
|
||||
} else if (type == typeid(u32) || type == typeid(int) || type == typeid(u16) ||
|
||||
type == typeid(s64) || type == typeid(u8)) {
|
||||
switch (request) {
|
||||
|
@ -22,6 +22,7 @@ class QObject;
|
||||
class QPushButton;
|
||||
class QSlider;
|
||||
class QSpinBox;
|
||||
class QRadioButton;
|
||||
|
||||
namespace Settings {
|
||||
class BasicSetting;
|
||||
@ -38,6 +39,7 @@ enum class RequestType {
|
||||
LineEdit,
|
||||
HexEdit,
|
||||
DateTimeEdit,
|
||||
RadioGroup,
|
||||
MaxEnum,
|
||||
};
|
||||
|
||||
@ -91,6 +93,7 @@ public:
|
||||
QSlider* slider{};
|
||||
QComboBox* combobox{};
|
||||
QDateTimeEdit* date_time_edit{};
|
||||
std::vector<std::pair<u32, QRadioButton*>> radio_buttons{};
|
||||
|
||||
private:
|
||||
void SetupComponent(const QString& label, std::function<void()>& load_func, bool managed,
|
||||
@ -106,6 +109,9 @@ private:
|
||||
QWidget* CreateCombobox(std::function<std::string()>& serializer,
|
||||
std::function<void()>& restore_func,
|
||||
const std::function<void()>& touch);
|
||||
QWidget* CreateRadioGroup(std::function<std::string()>& serializer,
|
||||
std::function<void()>& restore_func,
|
||||
const std::function<void()>& touch);
|
||||
QWidget* CreateLineEdit(std::function<std::string()>& serializer,
|
||||
std::function<void()>& restore_func, const std::function<void()>& touch,
|
||||
bool managed = true);
|
||||
|
@ -191,8 +191,9 @@ QString FormatPatchNameVersions(const FileSys::PatchManager& patch_manager,
|
||||
}
|
||||
|
||||
QList<QStandardItem*> MakeGameListEntry(const std::string& path, const std::string& name,
|
||||
const std::vector<u8>& icon, Loader::AppLoader& loader,
|
||||
u64 program_id, const CompatibilityList& compatibility_list,
|
||||
const std::size_t size, const std::vector<u8>& icon,
|
||||
Loader::AppLoader& loader, u64 program_id,
|
||||
const CompatibilityList& compatibility_list,
|
||||
const FileSys::PatchManager& patch) {
|
||||
const auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
|
||||
|
||||
@ -210,7 +211,7 @@ QList<QStandardItem*> MakeGameListEntry(const std::string& path, const std::stri
|
||||
file_type_string, program_id),
|
||||
new GameListItemCompat(compatibility),
|
||||
new GameListItem(file_type_string),
|
||||
new GameListItemSize(Common::FS::GetSize(path)),
|
||||
new GameListItemSize(size),
|
||||
};
|
||||
|
||||
const auto patch_versions = GetGameListCachedObject(
|
||||
@ -278,8 +279,8 @@ void GameListWorker::AddTitlesToGameList(GameListDir* parent_dir) {
|
||||
GetMetadataFromControlNCA(patch, *control, icon, name);
|
||||
}
|
||||
|
||||
emit EntryReady(MakeGameListEntry(file->GetFullPath(), name, icon, *loader, program_id,
|
||||
compatibility_list, patch),
|
||||
emit EntryReady(MakeGameListEntry(file->GetFullPath(), name, file->GetSize(), icon, *loader,
|
||||
program_id, compatibility_list, patch),
|
||||
parent_dir);
|
||||
}
|
||||
}
|
||||
@ -354,8 +355,9 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa
|
||||
const FileSys::PatchManager patch{id, system.GetFileSystemController(),
|
||||
system.GetContentProvider()};
|
||||
|
||||
emit EntryReady(MakeGameListEntry(physical_name, name, icon, *loader, id,
|
||||
compatibility_list, patch),
|
||||
emit EntryReady(MakeGameListEntry(physical_name, name,
|
||||
Common::FS::GetSize(physical_name), icon,
|
||||
*loader, id, compatibility_list, patch),
|
||||
parent_dir);
|
||||
}
|
||||
} else {
|
||||
@ -368,8 +370,9 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa
|
||||
const FileSys::PatchManager patch{program_id, system.GetFileSystemController(),
|
||||
system.GetContentProvider()};
|
||||
|
||||
emit EntryReady(MakeGameListEntry(physical_name, name, icon, *loader,
|
||||
program_id, compatibility_list, patch),
|
||||
emit EntryReady(
|
||||
MakeGameListEntry(physical_name, name, Common::FS::GetSize(physical_name),
|
||||
icon, *loader, program_id, compatibility_list, patch),
|
||||
parent_dir);
|
||||
}
|
||||
}
|
||||
|
@ -1158,9 +1158,9 @@ void GMainWindow::InitializeWidgets() {
|
||||
[this](const QPoint& menu_location) {
|
||||
QMenu context_menu;
|
||||
|
||||
for (auto const& docked_mode_pair : Config::use_docked_mode_texts_map) {
|
||||
context_menu.addAction(docked_mode_pair.second, [this, docked_mode_pair] {
|
||||
if (docked_mode_pair.first != Settings::values.use_docked_mode.GetValue()) {
|
||||
for (auto const& pair : Config::use_docked_mode_texts_map) {
|
||||
context_menu.addAction(pair.second, [this, &pair] {
|
||||
if (pair.first != Settings::values.use_docked_mode.GetValue()) {
|
||||
OnToggleDockedMode();
|
||||
}
|
||||
});
|
||||
@ -1811,6 +1811,43 @@ bool GMainWindow::SelectAndSetCurrentUser(
|
||||
return true;
|
||||
}
|
||||
|
||||
void GMainWindow::ConfigureFilesystemProvider(const std::string& filepath) {
|
||||
// Ensure all NCAs are registered before launching the game
|
||||
const auto file = vfs->OpenFile(filepath, FileSys::Mode::Read);
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto loader = Loader::GetLoader(*system, file);
|
||||
if (!loader) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto file_type = loader->GetFileType();
|
||||
if (file_type == Loader::FileType::Unknown || file_type == Loader::FileType::Error) {
|
||||
return;
|
||||
}
|
||||
|
||||
u64 program_id = 0;
|
||||
const auto res2 = loader->ReadProgramId(program_id);
|
||||
if (res2 == Loader::ResultStatus::Success && file_type == Loader::FileType::NCA) {
|
||||
provider->AddEntry(FileSys::TitleType::Application,
|
||||
FileSys::GetCRTypeFromNCAType(FileSys::NCA{file}.GetType()), program_id,
|
||||
file);
|
||||
} else if (res2 == Loader::ResultStatus::Success &&
|
||||
(file_type == Loader::FileType::XCI || file_type == Loader::FileType::NSP)) {
|
||||
const auto nsp = file_type == Loader::FileType::NSP
|
||||
? std::make_shared<FileSys::NSP>(file)
|
||||
: FileSys::XCI{file}.GetSecurePartitionNSP();
|
||||
for (const auto& title : nsp->GetNCAs()) {
|
||||
for (const auto& entry : title.second) {
|
||||
provider->AddEntry(entry.first.first, entry.first.second, title.first,
|
||||
entry.second->GetBaseFile());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t program_index,
|
||||
StartGameType type) {
|
||||
LOG_INFO(Frontend, "yuzu starting...");
|
||||
@ -1825,6 +1862,7 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t
|
||||
|
||||
last_filename_booted = filename;
|
||||
|
||||
ConfigureFilesystemProvider(filename.toStdString());
|
||||
const auto v_file = Core::GetGameFileFromPath(vfs, filename.toUtf8().constData());
|
||||
const auto loader = Loader::GetLoader(*system, v_file, program_id, program_index);
|
||||
|
||||
@ -3636,7 +3674,7 @@ void GMainWindow::OnTasReset() {
|
||||
}
|
||||
|
||||
void GMainWindow::OnToggleDockedMode() {
|
||||
const bool is_docked = Settings::values.use_docked_mode.GetValue();
|
||||
const bool is_docked = Settings::IsDockedMode();
|
||||
auto* player_1 = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
|
||||
auto* handheld = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld);
|
||||
|
||||
@ -3650,7 +3688,8 @@ void GMainWindow::OnToggleDockedMode() {
|
||||
controller_dialog->refreshConfiguration();
|
||||
}
|
||||
|
||||
Settings::values.use_docked_mode.SetValue(!is_docked);
|
||||
Settings::values.use_docked_mode.SetValue(is_docked ? Settings::ConsoleMode::Handheld
|
||||
: Settings::ConsoleMode::Docked);
|
||||
UpdateDockedButton();
|
||||
OnDockedModeChanged(is_docked, !is_docked, *system);
|
||||
}
|
||||
@ -4080,10 +4119,10 @@ void GMainWindow::UpdateGPUAccuracyButton() {
|
||||
}
|
||||
|
||||
void GMainWindow::UpdateDockedButton() {
|
||||
const bool is_docked = Settings::values.use_docked_mode.GetValue();
|
||||
dock_status_button->setChecked(is_docked);
|
||||
const auto console_mode = Settings::values.use_docked_mode.GetValue();
|
||||
dock_status_button->setChecked(Settings::IsDockedMode());
|
||||
dock_status_button->setText(
|
||||
Config::use_docked_mode_texts_map.find(is_docked)->second.toUpper());
|
||||
Config::use_docked_mode_texts_map.find(console_mode)->second.toUpper());
|
||||
}
|
||||
|
||||
void GMainWindow::UpdateAPIText() {
|
||||
|
@ -399,6 +399,7 @@ private:
|
||||
void OpenPerGameConfiguration(u64 title_id, const std::string& file_name);
|
||||
bool CheckDarkMode();
|
||||
bool CheckSystemArchiveDecryption();
|
||||
void ConfigureFilesystemProvider(const std::string& filepath);
|
||||
|
||||
QString GetTasStateDescription() const;
|
||||
bool CreateShortcut(const std::string& shortcut_path, const std::string& title,
|
||||
|
Reference in New Issue
Block a user