diff options
Diffstat (limited to 'src')
35 files changed, 316 insertions, 91 deletions
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt index c11b6bc16..22af9e435 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt | |||
| @@ -223,6 +223,8 @@ object NativeLibrary { | |||
| 223 | 223 | ||
| 224 | external fun getCompany(filename: String): String | 224 | external fun getCompany(filename: String): String |
| 225 | 225 | ||
| 226 | external fun isHomebrew(filename: String): Boolean | ||
| 227 | |||
| 226 | external fun setAppDirectory(directory: String) | 228 | external fun setAppDirectory(directory: String) |
| 227 | 229 | ||
| 228 | external fun initializeGpuDriver( | 230 | external fun initializeGpuDriver( |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt index 94d5156cf..f4db61cb3 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt | |||
| @@ -263,7 +263,8 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { | |||
| 263 | val config: Configuration = resources.configuration | 263 | val config: Configuration = resources.configuration |
| 264 | 264 | ||
| 265 | if ((config.screenLayout and Configuration.SCREENLAYOUT_LONG_YES) != 0 || | 265 | if ((config.screenLayout and Configuration.SCREENLAYOUT_LONG_YES) != 0 || |
| 266 | (config.screenLayout and Configuration.SCREENLAYOUT_LONG_NO) == 0) { | 266 | (config.screenLayout and Configuration.SCREENLAYOUT_LONG_NO) == 0 || |
| 267 | (config.screenLayout and Configuration.SCREENLAYOUT_SIZE_SMALL) != 0) { | ||
| 267 | return rotation | 268 | return rotation |
| 268 | } | 269 | } |
| 269 | when (rotation) { | 270 | when (rotation) { |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt index ebc0f164a..adbe3696b 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt | |||
| @@ -127,13 +127,7 @@ class SearchFragment : Fragment() { | |||
| 127 | } | 127 | } |
| 128 | } | 128 | } |
| 129 | 129 | ||
| 130 | R.id.chip_homebrew -> { | 130 | R.id.chip_homebrew -> baseList.filter { it.isHomebrew } |
| 131 | baseList.filter { | ||
| 132 | Log.error("Guh - ${it.path}") | ||
| 133 | FileUtil.hasExtension(it.path, "nro") | ||
| 134 | || FileUtil.hasExtension(it.path, "nso") | ||
| 135 | } | ||
| 136 | } | ||
| 137 | 131 | ||
| 138 | R.id.chip_retail -> baseList.filter { | 132 | R.id.chip_retail -> baseList.filter { |
| 139 | FileUtil.hasExtension(it.path, "xci") | 133 | FileUtil.hasExtension(it.path, "xci") |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt index 2a17653b2..3d6782c49 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt | |||
| @@ -16,7 +16,8 @@ class Game( | |||
| 16 | val regions: String, | 16 | val regions: String, |
| 17 | val path: String, | 17 | val path: String, |
| 18 | val gameId: String, | 18 | val gameId: String, |
| 19 | val company: String | 19 | val company: String, |
| 20 | val isHomebrew: Boolean | ||
| 20 | ) : Parcelable { | 21 | ) : Parcelable { |
| 21 | val keyAddedToLibraryTime get() = "${gameId}_AddedToLibraryTime" | 22 | val keyAddedToLibraryTime get() = "${gameId}_AddedToLibraryTime" |
| 22 | val keyLastPlayedTime get() = "${gameId}_LastPlayed" | 23 | val keyLastPlayedTime get() = "${gameId}_LastPlayed" |
| @@ -31,6 +32,7 @@ class Game( | |||
| 31 | && path == other.path | 32 | && path == other.path |
| 32 | && gameId == other.gameId | 33 | && gameId == other.gameId |
| 33 | && company == other.company | 34 | && company == other.company |
| 35 | && isHomebrew == other.isHomebrew | ||
| 34 | } | 36 | } |
| 35 | 37 | ||
| 36 | companion object { | 38 | companion object { |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt index 7059856f1..d9b301210 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt | |||
| @@ -13,6 +13,8 @@ import androidx.preference.PreferenceManager | |||
| 13 | import kotlinx.coroutines.Dispatchers | 13 | import kotlinx.coroutines.Dispatchers |
| 14 | import kotlinx.coroutines.launch | 14 | import kotlinx.coroutines.launch |
| 15 | import kotlinx.coroutines.withContext | 15 | import kotlinx.coroutines.withContext |
| 16 | import kotlinx.serialization.ExperimentalSerializationApi | ||
| 17 | import kotlinx.serialization.MissingFieldException | ||
| 16 | import kotlinx.serialization.decodeFromString | 18 | import kotlinx.serialization.decodeFromString |
| 17 | import kotlinx.serialization.json.Json | 19 | import kotlinx.serialization.json.Json |
| 18 | import org.yuzu.yuzu_emu.NativeLibrary | 20 | import org.yuzu.yuzu_emu.NativeLibrary |
| @@ -20,6 +22,7 @@ import org.yuzu.yuzu_emu.YuzuApplication | |||
| 20 | import org.yuzu.yuzu_emu.utils.GameHelper | 22 | import org.yuzu.yuzu_emu.utils.GameHelper |
| 21 | import java.util.Locale | 23 | import java.util.Locale |
| 22 | 24 | ||
| 25 | @OptIn(ExperimentalSerializationApi::class) | ||
| 23 | class GamesViewModel : ViewModel() { | 26 | class GamesViewModel : ViewModel() { |
| 24 | private val _games = MutableLiveData<List<Game>>(emptyList()) | 27 | private val _games = MutableLiveData<List<Game>>(emptyList()) |
| 25 | val games: LiveData<List<Game>> get() = _games | 28 | val games: LiveData<List<Game>> get() = _games |
| @@ -49,7 +52,13 @@ class GamesViewModel : ViewModel() { | |||
| 49 | if (storedGames!!.isNotEmpty()) { | 52 | if (storedGames!!.isNotEmpty()) { |
| 50 | val deserializedGames = mutableSetOf<Game>() | 53 | val deserializedGames = mutableSetOf<Game>() |
| 51 | storedGames.forEach { | 54 | storedGames.forEach { |
| 52 | val game: Game = Json.decodeFromString(it) | 55 | val game: Game |
| 56 | try { | ||
| 57 | game = Json.decodeFromString(it) | ||
| 58 | } catch (e: MissingFieldException) { | ||
| 59 | return@forEach | ||
| 60 | } | ||
| 61 | |||
| 53 | val gameExists = | 62 | val gameExists = |
| 54 | DocumentFile.fromSingleUri(YuzuApplication.appContext, Uri.parse(game.path)) | 63 | DocumentFile.fromSingleUri(YuzuApplication.appContext, Uri.parse(game.path)) |
| 55 | ?.exists() | 64 | ?.exists() |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt index c9f5797ac..aa424c768 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt | |||
| @@ -765,18 +765,20 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context | |||
| 765 | // If we have API access, calculate the safe area to draw the overlay | 765 | // If we have API access, calculate the safe area to draw the overlay |
| 766 | var cutoutLeft = 0 | 766 | var cutoutLeft = 0 |
| 767 | var cutoutBottom = 0 | 767 | var cutoutBottom = 0 |
| 768 | val insets = context.windowManager.currentWindowMetrics.windowInsets.displayCutout | 768 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { |
| 769 | if (insets != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { | 769 | val insets = context.windowManager.currentWindowMetrics.windowInsets.displayCutout |
| 770 | if (insets.boundingRectTop.bottom != 0 && insets.boundingRectTop.bottom > maxY / 2) | 770 | if (insets != null) { |
| 771 | insets.boundingRectTop.bottom.toFloat() else maxY | 771 | if (insets.boundingRectTop.bottom != 0 && insets.boundingRectTop.bottom > maxY / 2) |
| 772 | if (insets.boundingRectRight.left != 0 && insets.boundingRectRight.left > maxX / 2) | 772 | insets.boundingRectTop.bottom.toFloat() else maxY |
| 773 | insets.boundingRectRight.left.toFloat() else maxX | 773 | if (insets.boundingRectRight.left != 0 && insets.boundingRectRight.left > maxX / 2) |
| 774 | 774 | insets.boundingRectRight.left.toFloat() else maxX | |
| 775 | minX = insets.boundingRectLeft.right - insets.boundingRectLeft.left | 775 | |
| 776 | minY = insets.boundingRectBottom.top - insets.boundingRectBottom.bottom | 776 | minX = insets.boundingRectLeft.right - insets.boundingRectLeft.left |
| 777 | 777 | minY = insets.boundingRectBottom.top - insets.boundingRectBottom.bottom | |
| 778 | cutoutLeft = insets.boundingRectRight.right - insets.boundingRectRight.left | 778 | |
| 779 | cutoutBottom = insets.boundingRectTop.top - insets.boundingRectTop.bottom | 779 | cutoutLeft = insets.boundingRectRight.right - insets.boundingRectRight.left |
| 780 | cutoutBottom = insets.boundingRectTop.top - insets.boundingRectTop.bottom | ||
| 781 | } | ||
| 780 | } | 782 | } |
| 781 | 783 | ||
| 782 | // This makes sure that if we have an inset on one side of the screen, we mirror it on | 784 | // This makes sure that if we have an inset on one side of the screen, we mirror it on |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt index ba6b5783e..42b207618 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt | |||
| @@ -6,7 +6,6 @@ package org.yuzu.yuzu_emu.utils | |||
| 6 | import android.content.SharedPreferences | 6 | import android.content.SharedPreferences |
| 7 | import android.net.Uri | 7 | import android.net.Uri |
| 8 | import androidx.preference.PreferenceManager | 8 | import androidx.preference.PreferenceManager |
| 9 | import kotlinx.serialization.decodeFromString | ||
| 10 | import kotlinx.serialization.encodeToString | 9 | import kotlinx.serialization.encodeToString |
| 11 | import kotlinx.serialization.json.Json | 10 | import kotlinx.serialization.json.Json |
| 12 | import org.yuzu.yuzu_emu.NativeLibrary | 11 | import org.yuzu.yuzu_emu.NativeLibrary |
| @@ -83,7 +82,8 @@ object GameHelper { | |||
| 83 | NativeLibrary.getRegions(filePath), | 82 | NativeLibrary.getRegions(filePath), |
| 84 | filePath, | 83 | filePath, |
| 85 | gameId, | 84 | gameId, |
| 86 | NativeLibrary.getCompany(filePath) | 85 | NativeLibrary.getCompany(filePath), |
| 86 | NativeLibrary.isHomebrew(filePath) | ||
| 87 | ) | 87 | ) |
| 88 | 88 | ||
| 89 | val addedTime = preferences.getLong(newGame.keyAddedToLibraryTime, 0L) | 89 | val addedTime = preferences.getLong(newGame.keyAddedToLibraryTime, 0L) |
diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index b87e04b3d..03cb0b74b 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | 13 | ||
| 14 | #include <android/api-level.h> | 14 | #include <android/api-level.h> |
| 15 | #include <android/native_window_jni.h> | 15 | #include <android/native_window_jni.h> |
| 16 | #include <core/loader/nro.h> | ||
| 16 | 17 | ||
| 17 | #include "common/detached_tasks.h" | 18 | #include "common/detached_tasks.h" |
| 18 | #include "common/dynamic_library.h" | 19 | #include "common/dynamic_library.h" |
| @@ -281,6 +282,10 @@ public: | |||
| 281 | return GetRomMetadata(path).icon; | 282 | return GetRomMetadata(path).icon; |
| 282 | } | 283 | } |
| 283 | 284 | ||
| 285 | bool GetIsHomebrew(const std::string& path) { | ||
| 286 | return GetRomMetadata(path).isHomebrew; | ||
| 287 | } | ||
| 288 | |||
| 284 | void ResetRomMetadata() { | 289 | void ResetRomMetadata() { |
| 285 | m_rom_metadata_cache.clear(); | 290 | m_rom_metadata_cache.clear(); |
| 286 | } | 291 | } |
| @@ -348,6 +353,7 @@ private: | |||
| 348 | struct RomMetadata { | 353 | struct RomMetadata { |
| 349 | std::string title; | 354 | std::string title; |
| 350 | std::vector<u8> icon; | 355 | std::vector<u8> icon; |
| 356 | bool isHomebrew; | ||
| 351 | }; | 357 | }; |
| 352 | 358 | ||
| 353 | RomMetadata GetRomMetadata(const std::string& path) { | 359 | RomMetadata GetRomMetadata(const std::string& path) { |
| @@ -360,11 +366,17 @@ private: | |||
| 360 | 366 | ||
| 361 | RomMetadata CacheRomMetadata(const std::string& path) { | 367 | RomMetadata CacheRomMetadata(const std::string& path) { |
| 362 | const auto file = Core::GetGameFileFromPath(m_vfs, path); | 368 | const auto file = Core::GetGameFileFromPath(m_vfs, path); |
| 363 | const auto loader = Loader::GetLoader(EmulationSession::GetInstance().System(), file, 0, 0); | 369 | auto loader = Loader::GetLoader(EmulationSession::GetInstance().System(), file, 0, 0); |
| 364 | 370 | ||
| 365 | RomMetadata entry; | 371 | RomMetadata entry; |
| 366 | loader->ReadTitle(entry.title); | 372 | loader->ReadTitle(entry.title); |
| 367 | loader->ReadIcon(entry.icon); | 373 | loader->ReadIcon(entry.icon); |
| 374 | if (loader->GetFileType() == Loader::FileType::NRO) { | ||
| 375 | auto loader_nro = dynamic_cast<Loader::AppLoader_NRO*>(loader.get()); | ||
| 376 | entry.isHomebrew = loader_nro->IsHomebrew(); | ||
| 377 | } else { | ||
| 378 | entry.isHomebrew = false; | ||
| 379 | } | ||
| 368 | 380 | ||
| 369 | m_rom_metadata_cache[path] = entry; | 381 | m_rom_metadata_cache[path] = entry; |
| 370 | 382 | ||
| @@ -662,6 +674,12 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getCompany([[maybe_unused]] JNIEnv | |||
| 662 | return env->NewStringUTF(""); | 674 | return env->NewStringUTF(""); |
| 663 | } | 675 | } |
| 664 | 676 | ||
| 677 | jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isHomebrew([[maybe_unused]] JNIEnv* env, | ||
| 678 | [[maybe_unused]] jclass clazz, | ||
| 679 | [[maybe_unused]] jstring j_filename) { | ||
| 680 | return EmulationSession::GetInstance().GetIsHomebrew(GetJString(env, j_filename)); | ||
| 681 | } | ||
| 682 | |||
| 665 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmulation | 683 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmulation |
| 666 | [[maybe_unused]] (JNIEnv* env, [[maybe_unused]] jclass clazz) { | 684 | [[maybe_unused]] (JNIEnv* env, [[maybe_unused]] jclass clazz) { |
| 667 | // Create the default config.ini. | 685 | // Create the default config.ini. |
diff --git a/src/audio_core/audio_core.cpp b/src/audio_core/audio_core.cpp index 07a679c32..703ef4494 100644 --- a/src/audio_core/audio_core.cpp +++ b/src/audio_core/audio_core.cpp | |||
| @@ -47,12 +47,4 @@ AudioRenderer::ADSP::ADSP& AudioCore::GetADSP() { | |||
| 47 | return *adsp; | 47 | return *adsp; |
| 48 | } | 48 | } |
| 49 | 49 | ||
| 50 | void AudioCore::SetNVDECActive(bool active) { | ||
| 51 | nvdec_active = active; | ||
| 52 | } | ||
| 53 | |||
| 54 | bool AudioCore::IsNVDECActive() const { | ||
| 55 | return nvdec_active; | ||
| 56 | } | ||
| 57 | |||
| 58 | } // namespace AudioCore | 50 | } // namespace AudioCore |
diff --git a/src/audio_core/audio_core.h b/src/audio_core/audio_core.h index e33e00a3e..ea047773e 100644 --- a/src/audio_core/audio_core.h +++ b/src/audio_core/audio_core.h | |||
| @@ -57,18 +57,6 @@ public: | |||
| 57 | */ | 57 | */ |
| 58 | AudioRenderer::ADSP::ADSP& GetADSP(); | 58 | AudioRenderer::ADSP::ADSP& GetADSP(); |
| 59 | 59 | ||
| 60 | /** | ||
| 61 | * Toggle NVDEC state, used to avoid stall in playback. | ||
| 62 | * | ||
| 63 | * @param active - Set true if nvdec is active, otherwise false. | ||
| 64 | */ | ||
| 65 | void SetNVDECActive(bool active); | ||
| 66 | |||
| 67 | /** | ||
| 68 | * Get NVDEC state. | ||
| 69 | */ | ||
| 70 | bool IsNVDECActive() const; | ||
| 71 | |||
| 72 | private: | 60 | private: |
| 73 | /** | 61 | /** |
| 74 | * Create the sinks on startup. | 62 | * Create the sinks on startup. |
| @@ -83,8 +71,6 @@ private: | |||
| 83 | std::unique_ptr<Sink::Sink> input_sink; | 71 | std::unique_ptr<Sink::Sink> input_sink; |
| 84 | /// The ADSP in the sysmodule | 72 | /// The ADSP in the sysmodule |
| 85 | std::unique_ptr<AudioRenderer::ADSP::ADSP> adsp; | 73 | std::unique_ptr<AudioRenderer::ADSP::ADSP> adsp; |
| 86 | /// Is NVDec currently active? | ||
| 87 | bool nvdec_active{false}; | ||
| 88 | }; | 74 | }; |
| 89 | 75 | ||
| 90 | } // namespace AudioCore | 76 | } // namespace AudioCore |
diff --git a/src/common/fs/fs_paths.h b/src/common/fs/fs_paths.h index c77c112f1..61bac9eba 100644 --- a/src/common/fs/fs_paths.h +++ b/src/common/fs/fs_paths.h | |||
| @@ -10,6 +10,7 @@ | |||
| 10 | 10 | ||
| 11 | // Sub-directories contained within a yuzu data directory | 11 | // Sub-directories contained within a yuzu data directory |
| 12 | 12 | ||
| 13 | #define AMIIBO_DIR "amiibo" | ||
| 13 | #define CACHE_DIR "cache" | 14 | #define CACHE_DIR "cache" |
| 14 | #define CONFIG_DIR "config" | 15 | #define CONFIG_DIR "config" |
| 15 | #define DUMP_DIR "dump" | 16 | #define DUMP_DIR "dump" |
diff --git a/src/common/fs/path_util.cpp b/src/common/fs/path_util.cpp index e026a13d9..d71cfacc6 100644 --- a/src/common/fs/path_util.cpp +++ b/src/common/fs/path_util.cpp | |||
| @@ -114,6 +114,7 @@ public: | |||
| 114 | #endif | 114 | #endif |
| 115 | 115 | ||
| 116 | GenerateYuzuPath(YuzuPath::YuzuDir, yuzu_path); | 116 | GenerateYuzuPath(YuzuPath::YuzuDir, yuzu_path); |
| 117 | GenerateYuzuPath(YuzuPath::AmiiboDir, yuzu_path / AMIIBO_DIR); | ||
| 117 | GenerateYuzuPath(YuzuPath::CacheDir, yuzu_path_cache); | 118 | GenerateYuzuPath(YuzuPath::CacheDir, yuzu_path_cache); |
| 118 | GenerateYuzuPath(YuzuPath::ConfigDir, yuzu_path_config); | 119 | GenerateYuzuPath(YuzuPath::ConfigDir, yuzu_path_config); |
| 119 | GenerateYuzuPath(YuzuPath::DumpDir, yuzu_path / DUMP_DIR); | 120 | GenerateYuzuPath(YuzuPath::DumpDir, yuzu_path / DUMP_DIR); |
diff --git a/src/common/fs/path_util.h b/src/common/fs/path_util.h index 7cfe85b70..ba28964d0 100644 --- a/src/common/fs/path_util.h +++ b/src/common/fs/path_util.h | |||
| @@ -12,6 +12,7 @@ namespace Common::FS { | |||
| 12 | 12 | ||
| 13 | enum class YuzuPath { | 13 | enum class YuzuPath { |
| 14 | YuzuDir, // Where yuzu stores its data. | 14 | YuzuDir, // Where yuzu stores its data. |
| 15 | AmiiboDir, // Where Amiibo backups are stored. | ||
| 15 | CacheDir, // Where cached filesystem data is stored. | 16 | CacheDir, // Where cached filesystem data is stored. |
| 16 | ConfigDir, // Where config files are stored. | 17 | ConfigDir, // Where config files are stored. |
| 17 | DumpDir, // Where dumped data is stored. | 18 | DumpDir, // Where dumped data is stored. |
diff --git a/src/common/settings.cpp b/src/common/settings.cpp index ff53e80bb..9ff3edabb 100644 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp | |||
| @@ -235,6 +235,7 @@ void RestoreGlobalState(bool is_powered_on) { | |||
| 235 | values.bg_green.SetGlobal(true); | 235 | values.bg_green.SetGlobal(true); |
| 236 | values.bg_blue.SetGlobal(true); | 236 | values.bg_blue.SetGlobal(true); |
| 237 | values.enable_compute_pipelines.SetGlobal(true); | 237 | values.enable_compute_pipelines.SetGlobal(true); |
| 238 | values.use_video_framerate.SetGlobal(true); | ||
| 238 | 239 | ||
| 239 | // System | 240 | // System |
| 240 | values.language_index.SetGlobal(true); | 241 | values.language_index.SetGlobal(true); |
diff --git a/src/common/settings.h b/src/common/settings.h index 7f865b2a7..9682281b0 100644 --- a/src/common/settings.h +++ b/src/common/settings.h | |||
| @@ -482,6 +482,7 @@ struct Values { | |||
| 482 | SwitchableSetting<AstcRecompression, true> astc_recompression{ | 482 | SwitchableSetting<AstcRecompression, true> astc_recompression{ |
| 483 | AstcRecompression::Uncompressed, AstcRecompression::Uncompressed, AstcRecompression::Bc3, | 483 | AstcRecompression::Uncompressed, AstcRecompression::Uncompressed, AstcRecompression::Bc3, |
| 484 | "astc_recompression"}; | 484 | "astc_recompression"}; |
| 485 | SwitchableSetting<bool> use_video_framerate{false, "use_video_framerate"}; | ||
| 485 | 486 | ||
| 486 | SwitchableSetting<u8> bg_red{0, "bg_red"}; | 487 | SwitchableSetting<u8> bg_red{0, "bg_red"}; |
| 487 | SwitchableSetting<u8> bg_green{0, "bg_green"}; | 488 | SwitchableSetting<u8> bg_green{0, "bg_green"}; |
diff --git a/src/common/uuid.cpp b/src/common/uuid.cpp index 89e1ed225..035df7fe0 100644 --- a/src/common/uuid.cpp +++ b/src/common/uuid.cpp | |||
| @@ -48,7 +48,7 @@ std::array<u8, 0x10> ConstructFromRawString(std::string_view raw_string) { | |||
| 48 | } | 48 | } |
| 49 | 49 | ||
| 50 | std::array<u8, 0x10> ConstructFromFormattedString(std::string_view formatted_string) { | 50 | std::array<u8, 0x10> ConstructFromFormattedString(std::string_view formatted_string) { |
| 51 | std::array<u8, 0x10> uuid; | 51 | std::array<u8, 0x10> uuid{}; |
| 52 | 52 | ||
| 53 | size_t i = 0; | 53 | size_t i = 0; |
| 54 | 54 | ||
diff --git a/src/core/core.cpp b/src/core/core.cpp index 4406ae30e..7ba704f18 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp | |||
| @@ -216,6 +216,14 @@ struct System::Impl { | |||
| 216 | } | 216 | } |
| 217 | } | 217 | } |
| 218 | 218 | ||
| 219 | void SetNVDECActive(bool is_nvdec_active) { | ||
| 220 | nvdec_active = is_nvdec_active; | ||
| 221 | } | ||
| 222 | |||
| 223 | bool GetNVDECActive() { | ||
| 224 | return nvdec_active; | ||
| 225 | } | ||
| 226 | |||
| 219 | void InitializeDebugger(System& system, u16 port) { | 227 | void InitializeDebugger(System& system, u16 port) { |
| 220 | debugger = std::make_unique<Debugger>(system, port); | 228 | debugger = std::make_unique<Debugger>(system, port); |
| 221 | } | 229 | } |
| @@ -485,6 +493,8 @@ struct System::Impl { | |||
| 485 | std::atomic_bool is_powered_on{}; | 493 | std::atomic_bool is_powered_on{}; |
| 486 | bool exit_lock = false; | 494 | bool exit_lock = false; |
| 487 | 495 | ||
| 496 | bool nvdec_active{}; | ||
| 497 | |||
| 488 | Reporter reporter; | 498 | Reporter reporter; |
| 489 | std::unique_ptr<Memory::CheatEngine> cheat_engine; | 499 | std::unique_ptr<Memory::CheatEngine> cheat_engine; |
| 490 | std::unique_ptr<Tools::Freezer> memory_freezer; | 500 | std::unique_ptr<Tools::Freezer> memory_freezer; |
| @@ -594,6 +604,14 @@ void System::UnstallApplication() { | |||
| 594 | impl->UnstallApplication(); | 604 | impl->UnstallApplication(); |
| 595 | } | 605 | } |
| 596 | 606 | ||
| 607 | void System::SetNVDECActive(bool is_nvdec_active) { | ||
| 608 | impl->SetNVDECActive(is_nvdec_active); | ||
| 609 | } | ||
| 610 | |||
| 611 | bool System::GetNVDECActive() { | ||
| 612 | return impl->GetNVDECActive(); | ||
| 613 | } | ||
| 614 | |||
| 597 | void System::InitializeDebugger() { | 615 | void System::InitializeDebugger() { |
| 598 | impl->InitializeDebugger(*this, Settings::values.gdbstub_port.GetValue()); | 616 | impl->InitializeDebugger(*this, Settings::values.gdbstub_port.GetValue()); |
| 599 | } | 617 | } |
diff --git a/src/core/core.h b/src/core/core.h index 4f153154f..ff2e4bd30 100644 --- a/src/core/core.h +++ b/src/core/core.h | |||
| @@ -189,6 +189,9 @@ public: | |||
| 189 | std::unique_lock<std::mutex> StallApplication(); | 189 | std::unique_lock<std::mutex> StallApplication(); |
| 190 | void UnstallApplication(); | 190 | void UnstallApplication(); |
| 191 | 191 | ||
| 192 | void SetNVDECActive(bool is_nvdec_active); | ||
| 193 | [[nodiscard]] bool GetNVDECActive(); | ||
| 194 | |||
| 192 | /** | 195 | /** |
| 193 | * Initialize the debugger. | 196 | * Initialize the debugger. |
| 194 | */ | 197 | */ |
diff --git a/src/core/hle/service/nfc/common/device.cpp b/src/core/hle/service/nfc/common/device.cpp index 0bd7900e1..b14f682b5 100644 --- a/src/core/hle/service/nfc/common/device.cpp +++ b/src/core/hle/service/nfc/common/device.cpp | |||
| @@ -12,6 +12,11 @@ | |||
| 12 | #pragma warning(pop) | 12 | #pragma warning(pop) |
| 13 | #endif | 13 | #endif |
| 14 | 14 | ||
| 15 | #include <fmt/format.h> | ||
| 16 | |||
| 17 | #include "common/fs/file.h" | ||
| 18 | #include "common/fs/fs.h" | ||
| 19 | #include "common/fs/path_util.h" | ||
| 15 | #include "common/input.h" | 20 | #include "common/input.h" |
| 16 | #include "common/logging/log.h" | 21 | #include "common/logging/log.h" |
| 17 | #include "common/string_util.h" | 22 | #include "common/string_util.h" |
| @@ -136,7 +141,7 @@ bool NfcDevice::LoadNfcTag(std::span<const u8> data) { | |||
| 136 | if (!NFP::AmiiboCrypto::IsKeyAvailable()) { | 141 | if (!NFP::AmiiboCrypto::IsKeyAvailable()) { |
| 137 | LOG_INFO(Service_NFC, "Loading amiibo without keys"); | 142 | LOG_INFO(Service_NFC, "Loading amiibo without keys"); |
| 138 | memcpy(&encrypted_tag_data, data.data(), sizeof(NFP::EncryptedNTAG215File)); | 143 | memcpy(&encrypted_tag_data, data.data(), sizeof(NFP::EncryptedNTAG215File)); |
| 139 | BuildAmiiboWithoutKeys(); | 144 | BuildAmiiboWithoutKeys(tag_data, encrypted_tag_data); |
| 140 | is_plain_amiibo = true; | 145 | is_plain_amiibo = true; |
| 141 | is_write_protected = true; | 146 | is_write_protected = true; |
| 142 | return true; | 147 | return true; |
| @@ -366,16 +371,25 @@ Result NfcDevice::Mount(NFP::ModelType model_type, NFP::MountTarget mount_target | |||
| 366 | 371 | ||
| 367 | // The loaded amiibo is not encrypted | 372 | // The loaded amiibo is not encrypted |
| 368 | if (is_plain_amiibo) { | 373 | if (is_plain_amiibo) { |
| 374 | std::vector<u8> data(sizeof(NFP::NTAG215File)); | ||
| 375 | memcpy(data.data(), &tag_data, sizeof(tag_data)); | ||
| 376 | WriteBackupData(tag_data.uid, data); | ||
| 377 | |||
| 369 | device_state = DeviceState::TagMounted; | 378 | device_state = DeviceState::TagMounted; |
| 370 | mount_target = mount_target_; | 379 | mount_target = mount_target_; |
| 371 | return ResultSuccess; | 380 | return ResultSuccess; |
| 372 | } | 381 | } |
| 373 | 382 | ||
| 374 | if (!NFP::AmiiboCrypto::DecodeAmiibo(encrypted_tag_data, tag_data)) { | 383 | if (!NFP::AmiiboCrypto::DecodeAmiibo(encrypted_tag_data, tag_data)) { |
| 375 | LOG_ERROR(Service_NFP, "Can't decode amiibo {}", device_state); | 384 | bool has_backup = HasBackup(encrypted_tag_data.uuid.uid).IsSuccess(); |
| 376 | return ResultCorruptedData; | 385 | LOG_ERROR(Service_NFP, "Can't decode amiibo, has_backup= {}", has_backup); |
| 386 | return has_backup ? ResultCorruptedDataWithBackup : ResultCorruptedData; | ||
| 377 | } | 387 | } |
| 378 | 388 | ||
| 389 | std::vector<u8> data(sizeof(NFP::EncryptedNTAG215File)); | ||
| 390 | memcpy(data.data(), &encrypted_tag_data, sizeof(encrypted_tag_data)); | ||
| 391 | WriteBackupData(encrypted_tag_data.uuid.uid, data); | ||
| 392 | |||
| 379 | device_state = DeviceState::TagMounted; | 393 | device_state = DeviceState::TagMounted; |
| 380 | mount_target = mount_target_; | 394 | mount_target = mount_target_; |
| 381 | return ResultSuccess; | 395 | return ResultSuccess; |
| @@ -470,6 +484,7 @@ Result NfcDevice::FlushWithBreak(NFP::BreakType break_type) { | |||
| 470 | std::vector<u8> data(sizeof(NFP::EncryptedNTAG215File)); | 484 | std::vector<u8> data(sizeof(NFP::EncryptedNTAG215File)); |
| 471 | if (is_plain_amiibo) { | 485 | if (is_plain_amiibo) { |
| 472 | memcpy(data.data(), &tag_data, sizeof(tag_data)); | 486 | memcpy(data.data(), &tag_data, sizeof(tag_data)); |
| 487 | WriteBackupData(tag_data.uid, data); | ||
| 473 | } else { | 488 | } else { |
| 474 | if (!NFP::AmiiboCrypto::EncodeAmiibo(tag_data, encrypted_tag_data)) { | 489 | if (!NFP::AmiiboCrypto::EncodeAmiibo(tag_data, encrypted_tag_data)) { |
| 475 | LOG_ERROR(Service_NFP, "Failed to encode data"); | 490 | LOG_ERROR(Service_NFP, "Failed to encode data"); |
| @@ -477,6 +492,7 @@ Result NfcDevice::FlushWithBreak(NFP::BreakType break_type) { | |||
| 477 | } | 492 | } |
| 478 | 493 | ||
| 479 | memcpy(data.data(), &encrypted_tag_data, sizeof(encrypted_tag_data)); | 494 | memcpy(data.data(), &encrypted_tag_data, sizeof(encrypted_tag_data)); |
| 495 | WriteBackupData(encrypted_tag_data.uuid.uid, data); | ||
| 480 | } | 496 | } |
| 481 | 497 | ||
| 482 | if (!npad_device->WriteNfc(data)) { | 498 | if (!npad_device->WriteNfc(data)) { |
| @@ -488,7 +504,7 @@ Result NfcDevice::FlushWithBreak(NFP::BreakType break_type) { | |||
| 488 | } | 504 | } |
| 489 | 505 | ||
| 490 | Result NfcDevice::Restore() { | 506 | Result NfcDevice::Restore() { |
| 491 | if (device_state != DeviceState::TagMounted) { | 507 | if (device_state != DeviceState::TagFound) { |
| 492 | LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); | 508 | LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); |
| 493 | if (device_state == DeviceState::TagRemoved) { | 509 | if (device_state == DeviceState::TagRemoved) { |
| 494 | return ResultTagRemoved; | 510 | return ResultTagRemoved; |
| @@ -496,13 +512,59 @@ Result NfcDevice::Restore() { | |||
| 496 | return ResultWrongDeviceState; | 512 | return ResultWrongDeviceState; |
| 497 | } | 513 | } |
| 498 | 514 | ||
| 499 | if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) { | 515 | NFC::TagInfo tag_info{}; |
| 500 | LOG_ERROR(Service_NFC, "Amiibo is read only", device_state); | 516 | std::array<u8, sizeof(NFP::EncryptedNTAG215File)> data{}; |
| 501 | return ResultWrongDeviceState; | 517 | Result result = GetTagInfo(tag_info, false); |
| 518 | |||
| 519 | if (result.IsError()) { | ||
| 520 | return result; | ||
| 502 | } | 521 | } |
| 503 | 522 | ||
| 504 | // TODO: Load amiibo from backup on system | 523 | result = ReadBackupData(tag_info.uuid, data); |
| 505 | LOG_ERROR(Service_NFP, "Not Implemented"); | 524 | |
| 525 | if (result.IsError()) { | ||
| 526 | return result; | ||
| 527 | } | ||
| 528 | |||
| 529 | NFP::NTAG215File temporary_tag_data{}; | ||
| 530 | NFP::EncryptedNTAG215File temporary_encrypted_tag_data{}; | ||
| 531 | |||
| 532 | // Fallback for encrypted amiibos without keys | ||
| 533 | if (is_write_protected) { | ||
| 534 | return ResultWriteAmiiboFailed; | ||
| 535 | } | ||
| 536 | |||
| 537 | // Fallback for plain amiibos | ||
| 538 | if (is_plain_amiibo) { | ||
| 539 | LOG_INFO(Service_NFP, "Restoring backup of plain amiibo"); | ||
| 540 | memcpy(&temporary_tag_data, data.data(), sizeof(NFP::EncryptedNTAG215File)); | ||
| 541 | temporary_encrypted_tag_data = NFP::AmiiboCrypto::EncodedDataToNfcData(temporary_tag_data); | ||
| 542 | } | ||
| 543 | |||
| 544 | if (!is_plain_amiibo) { | ||
| 545 | LOG_INFO(Service_NFP, "Restoring backup of encrypted amiibo"); | ||
| 546 | temporary_tag_data = {}; | ||
| 547 | memcpy(&temporary_encrypted_tag_data, data.data(), sizeof(NFP::EncryptedNTAG215File)); | ||
| 548 | } | ||
| 549 | |||
| 550 | if (!NFP::AmiiboCrypto::IsAmiiboValid(temporary_encrypted_tag_data)) { | ||
| 551 | return ResultNotAnAmiibo; | ||
| 552 | } | ||
| 553 | |||
| 554 | if (!is_plain_amiibo) { | ||
| 555 | if (!NFP::AmiiboCrypto::DecodeAmiibo(temporary_encrypted_tag_data, temporary_tag_data)) { | ||
| 556 | LOG_ERROR(Service_NFP, "Can't decode amiibo"); | ||
| 557 | return ResultCorruptedData; | ||
| 558 | } | ||
| 559 | } | ||
| 560 | |||
| 561 | // Overwrite tag contents with backup and mount the tag | ||
| 562 | tag_data = temporary_tag_data; | ||
| 563 | encrypted_tag_data = temporary_encrypted_tag_data; | ||
| 564 | device_state = DeviceState::TagMounted; | ||
| 565 | mount_target = NFP::MountTarget::All; | ||
| 566 | is_data_moddified = true; | ||
| 567 | |||
| 506 | return ResultSuccess; | 568 | return ResultSuccess; |
| 507 | } | 569 | } |
| 508 | 570 | ||
| @@ -1132,13 +1194,69 @@ Result NfcDevice::BreakTag(NFP::BreakType break_type) { | |||
| 1132 | return FlushWithBreak(break_type); | 1194 | return FlushWithBreak(break_type); |
| 1133 | } | 1195 | } |
| 1134 | 1196 | ||
| 1135 | Result NfcDevice::ReadBackupData(std::span<u8> data) const { | 1197 | Result NfcDevice::HasBackup(const NFC::UniqueSerialNumber& uid) const { |
| 1136 | // Not implemented | 1198 | constexpr auto backup_dir = "backup"; |
| 1199 | const auto yuzu_amiibo_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::AmiiboDir); | ||
| 1200 | const auto file_name = fmt::format("{0:02x}.bin", fmt::join(uid, "")); | ||
| 1201 | |||
| 1202 | if (!Common::FS::Exists(yuzu_amiibo_dir / backup_dir / file_name)) { | ||
| 1203 | return ResultUnableToAccessBackupFile; | ||
| 1204 | } | ||
| 1205 | |||
| 1137 | return ResultSuccess; | 1206 | return ResultSuccess; |
| 1138 | } | 1207 | } |
| 1139 | 1208 | ||
| 1140 | Result NfcDevice::WriteBackupData(std::span<const u8> data) { | 1209 | Result NfcDevice::ReadBackupData(const NFC::UniqueSerialNumber& uid, std::span<u8> data) const { |
| 1141 | // Not implemented | 1210 | constexpr auto backup_dir = "backup"; |
| 1211 | const auto yuzu_amiibo_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::AmiiboDir); | ||
| 1212 | const auto file_name = fmt::format("{0:02x}.bin", fmt::join(uid, "")); | ||
| 1213 | |||
| 1214 | const Common::FS::IOFile keys_file{yuzu_amiibo_dir / backup_dir / file_name, | ||
| 1215 | Common::FS::FileAccessMode::Read, | ||
| 1216 | Common::FS::FileType::BinaryFile}; | ||
| 1217 | |||
| 1218 | if (!keys_file.IsOpen()) { | ||
| 1219 | LOG_ERROR(Service_NFP, "Failed to open amiibo backup"); | ||
| 1220 | return ResultUnableToAccessBackupFile; | ||
| 1221 | } | ||
| 1222 | |||
| 1223 | if (keys_file.Read(data) != data.size()) { | ||
| 1224 | LOG_ERROR(Service_NFP, "Failed to read amiibo backup"); | ||
| 1225 | return ResultUnableToAccessBackupFile; | ||
| 1226 | } | ||
| 1227 | |||
| 1228 | return ResultSuccess; | ||
| 1229 | } | ||
| 1230 | |||
| 1231 | Result NfcDevice::WriteBackupData(const NFC::UniqueSerialNumber& uid, std::span<const u8> data) { | ||
| 1232 | constexpr auto backup_dir = "backup"; | ||
| 1233 | const auto yuzu_amiibo_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::AmiiboDir); | ||
| 1234 | const auto file_name = fmt::format("{0:02x}.bin", fmt::join(uid, "")); | ||
| 1235 | |||
| 1236 | if (HasBackup(uid).IsError()) { | ||
| 1237 | if (!Common::FS::CreateDir(yuzu_amiibo_dir / backup_dir)) { | ||
| 1238 | return ResultBackupPathAlreadyExist; | ||
| 1239 | } | ||
| 1240 | |||
| 1241 | if (!Common::FS::NewFile(yuzu_amiibo_dir / backup_dir / file_name)) { | ||
| 1242 | return ResultBackupPathAlreadyExist; | ||
| 1243 | } | ||
| 1244 | } | ||
| 1245 | |||
| 1246 | const Common::FS::IOFile keys_file{yuzu_amiibo_dir / backup_dir / file_name, | ||
| 1247 | Common::FS::FileAccessMode::ReadWrite, | ||
| 1248 | Common::FS::FileType::BinaryFile}; | ||
| 1249 | |||
| 1250 | if (!keys_file.IsOpen()) { | ||
| 1251 | LOG_ERROR(Service_NFP, "Failed to open amiibo backup"); | ||
| 1252 | return ResultUnableToAccessBackupFile; | ||
| 1253 | } | ||
| 1254 | |||
| 1255 | if (keys_file.Write(data) != data.size()) { | ||
| 1256 | LOG_ERROR(Service_NFP, "Failed to write amiibo backup"); | ||
| 1257 | return ResultUnableToAccessBackupFile; | ||
| 1258 | } | ||
| 1259 | |||
| 1142 | return ResultSuccess; | 1260 | return ResultSuccess; |
| 1143 | } | 1261 | } |
| 1144 | 1262 | ||
| @@ -1177,7 +1295,8 @@ NFP::AmiiboName NfcDevice::GetAmiiboName(const NFP::AmiiboSettings& settings) co | |||
| 1177 | return amiibo_name; | 1295 | return amiibo_name; |
| 1178 | } | 1296 | } |
| 1179 | 1297 | ||
| 1180 | void NfcDevice::SetAmiiboName(NFP::AmiiboSettings& settings, const NFP::AmiiboName& amiibo_name) { | 1298 | void NfcDevice::SetAmiiboName(NFP::AmiiboSettings& settings, |
| 1299 | const NFP::AmiiboName& amiibo_name) const { | ||
| 1181 | std::array<char16_t, NFP::amiibo_name_length> settings_amiibo_name{}; | 1300 | std::array<char16_t, NFP::amiibo_name_length> settings_amiibo_name{}; |
| 1182 | 1301 | ||
| 1183 | // Convert from utf8 to utf16 | 1302 | // Convert from utf8 to utf16 |
| @@ -1258,22 +1377,23 @@ void NfcDevice::UpdateRegisterInfoCrc() { | |||
| 1258 | tag_data.register_info_crc = crc.checksum(); | 1377 | tag_data.register_info_crc = crc.checksum(); |
| 1259 | } | 1378 | } |
| 1260 | 1379 | ||
| 1261 | void NfcDevice::BuildAmiiboWithoutKeys() { | 1380 | void NfcDevice::BuildAmiiboWithoutKeys(NFP::NTAG215File& stubbed_tag_data, |
| 1381 | const NFP::EncryptedNTAG215File& encrypted_file) const { | ||
| 1262 | Service::Mii::MiiManager manager; | 1382 | Service::Mii::MiiManager manager; |
| 1263 | auto& settings = tag_data.settings; | 1383 | auto& settings = stubbed_tag_data.settings; |
| 1264 | 1384 | ||
| 1265 | tag_data = NFP::AmiiboCrypto::NfcDataToEncodedData(encrypted_tag_data); | 1385 | stubbed_tag_data = NFP::AmiiboCrypto::NfcDataToEncodedData(encrypted_file); |
| 1266 | 1386 | ||
| 1267 | // Common info | 1387 | // Common info |
| 1268 | tag_data.write_counter = 0; | 1388 | stubbed_tag_data.write_counter = 0; |
| 1269 | tag_data.amiibo_version = 0; | 1389 | stubbed_tag_data.amiibo_version = 0; |
| 1270 | settings.write_date = GetAmiiboDate(GetCurrentPosixTime()); | 1390 | settings.write_date = GetAmiiboDate(GetCurrentPosixTime()); |
| 1271 | 1391 | ||
| 1272 | // Register info | 1392 | // Register info |
| 1273 | SetAmiiboName(settings, {'y', 'u', 'z', 'u', 'A', 'm', 'i', 'i', 'b', 'o'}); | 1393 | SetAmiiboName(settings, {'y', 'u', 'z', 'u', 'A', 'm', 'i', 'i', 'b', 'o'}); |
| 1274 | settings.settings.font_region.Assign(0); | 1394 | settings.settings.font_region.Assign(0); |
| 1275 | settings.init_date = GetAmiiboDate(GetCurrentPosixTime()); | 1395 | settings.init_date = GetAmiiboDate(GetCurrentPosixTime()); |
| 1276 | tag_data.owner_mii = manager.BuildFromStoreData(manager.BuildDefault(0)); | 1396 | stubbed_tag_data.owner_mii = manager.BuildFromStoreData(manager.BuildDefault(0)); |
| 1277 | 1397 | ||
| 1278 | // Admin info | 1398 | // Admin info |
| 1279 | settings.settings.amiibo_initialized.Assign(1); | 1399 | settings.settings.amiibo_initialized.Assign(1); |
diff --git a/src/core/hle/service/nfc/common/device.h b/src/core/hle/service/nfc/common/device.h index 6a37e8458..6f049b687 100644 --- a/src/core/hle/service/nfc/common/device.h +++ b/src/core/hle/service/nfc/common/device.h | |||
| @@ -86,8 +86,9 @@ public: | |||
| 86 | Result GetAll(NFP::NfpData& data) const; | 86 | Result GetAll(NFP::NfpData& data) const; |
| 87 | Result SetAll(const NFP::NfpData& data); | 87 | Result SetAll(const NFP::NfpData& data); |
| 88 | Result BreakTag(NFP::BreakType break_type); | 88 | Result BreakTag(NFP::BreakType break_type); |
| 89 | Result ReadBackupData(std::span<u8> data) const; | 89 | Result HasBackup(const NFC::UniqueSerialNumber& uid) const; |
| 90 | Result WriteBackupData(std::span<const u8> data); | 90 | Result ReadBackupData(const NFC::UniqueSerialNumber& uid, std::span<u8> data) const; |
| 91 | Result WriteBackupData(const NFC::UniqueSerialNumber& uid, std::span<const u8> data); | ||
| 91 | Result WriteNtf(std::span<const u8> data); | 92 | Result WriteNtf(std::span<const u8> data); |
| 92 | 93 | ||
| 93 | u64 GetHandle() const; | 94 | u64 GetHandle() const; |
| @@ -103,14 +104,15 @@ private: | |||
| 103 | void CloseNfcTag(); | 104 | void CloseNfcTag(); |
| 104 | 105 | ||
| 105 | NFP::AmiiboName GetAmiiboName(const NFP::AmiiboSettings& settings) const; | 106 | NFP::AmiiboName GetAmiiboName(const NFP::AmiiboSettings& settings) const; |
| 106 | void SetAmiiboName(NFP::AmiiboSettings& settings, const NFP::AmiiboName& amiibo_name); | 107 | void SetAmiiboName(NFP::AmiiboSettings& settings, const NFP::AmiiboName& amiibo_name) const; |
| 107 | NFP::AmiiboDate GetAmiiboDate(s64 posix_time) const; | 108 | NFP::AmiiboDate GetAmiiboDate(s64 posix_time) const; |
| 108 | u64 GetCurrentPosixTime() const; | 109 | u64 GetCurrentPosixTime() const; |
| 109 | u64 RemoveVersionByte(u64 application_id) const; | 110 | u64 RemoveVersionByte(u64 application_id) const; |
| 110 | void UpdateSettingsCrc(); | 111 | void UpdateSettingsCrc(); |
| 111 | void UpdateRegisterInfoCrc(); | 112 | void UpdateRegisterInfoCrc(); |
| 112 | 113 | ||
| 113 | void BuildAmiiboWithoutKeys(); | 114 | void BuildAmiiboWithoutKeys(NFP::NTAG215File& stubbed_tag_data, |
| 115 | const NFP::EncryptedNTAG215File& encrypted_file) const; | ||
| 114 | 116 | ||
| 115 | bool is_controller_set{}; | 117 | bool is_controller_set{}; |
| 116 | int callback_key; | 118 | int callback_key; |
diff --git a/src/core/hle/service/nfc/common/device_manager.cpp b/src/core/hle/service/nfc/common/device_manager.cpp index d5deaaf27..cffd602df 100644 --- a/src/core/hle/service/nfc/common/device_manager.cpp +++ b/src/core/hle/service/nfc/common/device_manager.cpp | |||
| @@ -543,9 +543,14 @@ Result DeviceManager::ReadBackupData(u64 device_handle, std::span<u8> data) cons | |||
| 543 | 543 | ||
| 544 | std::shared_ptr<NfcDevice> device = nullptr; | 544 | std::shared_ptr<NfcDevice> device = nullptr; |
| 545 | auto result = GetDeviceHandle(device_handle, device); | 545 | auto result = GetDeviceHandle(device_handle, device); |
| 546 | NFC::TagInfo tag_info{}; | ||
| 546 | 547 | ||
| 547 | if (result.IsSuccess()) { | 548 | if (result.IsSuccess()) { |
| 548 | result = device->ReadBackupData(data); | 549 | result = device->GetTagInfo(tag_info, false); |
| 550 | } | ||
| 551 | |||
| 552 | if (result.IsSuccess()) { | ||
| 553 | result = device->ReadBackupData(tag_info.uuid, data); | ||
| 549 | result = VerifyDeviceResult(device, result); | 554 | result = VerifyDeviceResult(device, result); |
| 550 | } | 555 | } |
| 551 | 556 | ||
| @@ -557,9 +562,14 @@ Result DeviceManager::WriteBackupData(u64 device_handle, std::span<const u8> dat | |||
| 557 | 562 | ||
| 558 | std::shared_ptr<NfcDevice> device = nullptr; | 563 | std::shared_ptr<NfcDevice> device = nullptr; |
| 559 | auto result = GetDeviceHandle(device_handle, device); | 564 | auto result = GetDeviceHandle(device_handle, device); |
| 565 | NFC::TagInfo tag_info{}; | ||
| 566 | |||
| 567 | if (result.IsSuccess()) { | ||
| 568 | result = device->GetTagInfo(tag_info, false); | ||
| 569 | } | ||
| 560 | 570 | ||
| 561 | if (result.IsSuccess()) { | 571 | if (result.IsSuccess()) { |
| 562 | result = device->WriteBackupData(data); | 572 | result = device->WriteBackupData(tag_info.uuid, data); |
| 563 | result = VerifyDeviceResult(device, result); | 573 | result = VerifyDeviceResult(device, result); |
| 564 | } | 574 | } |
| 565 | 575 | ||
diff --git a/src/core/hle/service/nfc/nfc_interface.cpp b/src/core/hle/service/nfc/nfc_interface.cpp index 0fa29d398..198d0f2b9 100644 --- a/src/core/hle/service/nfc/nfc_interface.cpp +++ b/src/core/hle/service/nfc/nfc_interface.cpp | |||
| @@ -302,7 +302,7 @@ Result NfcInterface::TranslateResultToServiceError(Result result) const { | |||
| 302 | return TranslateResultToNfp(result); | 302 | return TranslateResultToNfp(result); |
| 303 | } | 303 | } |
| 304 | default: | 304 | default: |
| 305 | if (result != ResultUnknown216) { | 305 | if (result != ResultBackupPathAlreadyExist) { |
| 306 | return result; | 306 | return result; |
| 307 | } | 307 | } |
| 308 | return ResultUnknown74; | 308 | return ResultUnknown74; |
| @@ -343,6 +343,9 @@ Result NfcInterface::TranslateResultToNfp(Result result) const { | |||
| 343 | if (result == ResultApplicationAreaIsNotInitialized) { | 343 | if (result == ResultApplicationAreaIsNotInitialized) { |
| 344 | return NFP::ResultApplicationAreaIsNotInitialized; | 344 | return NFP::ResultApplicationAreaIsNotInitialized; |
| 345 | } | 345 | } |
| 346 | if (result == ResultCorruptedDataWithBackup) { | ||
| 347 | return NFP::ResultCorruptedDataWithBackup; | ||
| 348 | } | ||
| 346 | if (result == ResultCorruptedData) { | 349 | if (result == ResultCorruptedData) { |
| 347 | return NFP::ResultCorruptedData; | 350 | return NFP::ResultCorruptedData; |
| 348 | } | 351 | } |
| @@ -355,6 +358,9 @@ Result NfcInterface::TranslateResultToNfp(Result result) const { | |||
| 355 | if (result == ResultNotAnAmiibo) { | 358 | if (result == ResultNotAnAmiibo) { |
| 356 | return NFP::ResultNotAnAmiibo; | 359 | return NFP::ResultNotAnAmiibo; |
| 357 | } | 360 | } |
| 361 | if (result == ResultUnableToAccessBackupFile) { | ||
| 362 | return NFP::ResultUnableToAccessBackupFile; | ||
| 363 | } | ||
| 358 | LOG_WARNING(Service_NFC, "Result conversion not handled"); | 364 | LOG_WARNING(Service_NFC, "Result conversion not handled"); |
| 359 | return result; | 365 | return result; |
| 360 | } | 366 | } |
diff --git a/src/core/hle/service/nfc/nfc_result.h b/src/core/hle/service/nfc/nfc_result.h index 917d79ef8..59a808740 100644 --- a/src/core/hle/service/nfc/nfc_result.h +++ b/src/core/hle/service/nfc/nfc_result.h | |||
| @@ -9,20 +9,22 @@ namespace Service::NFC { | |||
| 9 | 9 | ||
| 10 | constexpr Result ResultDeviceNotFound(ErrorModule::NFC, 64); | 10 | constexpr Result ResultDeviceNotFound(ErrorModule::NFC, 64); |
| 11 | constexpr Result ResultInvalidArgument(ErrorModule::NFC, 65); | 11 | constexpr Result ResultInvalidArgument(ErrorModule::NFC, 65); |
| 12 | constexpr Result ResultWrongApplicationAreaSize(ErrorModule::NFP, 68); | 12 | constexpr Result ResultWrongApplicationAreaSize(ErrorModule::NFC, 68); |
| 13 | constexpr Result ResultWrongDeviceState(ErrorModule::NFC, 73); | 13 | constexpr Result ResultWrongDeviceState(ErrorModule::NFC, 73); |
| 14 | constexpr Result ResultUnknown74(ErrorModule::NFC, 74); | 14 | constexpr Result ResultUnknown74(ErrorModule::NFC, 74); |
| 15 | constexpr Result ResultUnknown76(ErrorModule::NFC, 76); | 15 | constexpr Result ResultUnknown76(ErrorModule::NFC, 76); |
| 16 | constexpr Result ResultNfcNotInitialized(ErrorModule::NFC, 77); | 16 | constexpr Result ResultNfcNotInitialized(ErrorModule::NFC, 77); |
| 17 | constexpr Result ResultNfcDisabled(ErrorModule::NFC, 80); | 17 | constexpr Result ResultNfcDisabled(ErrorModule::NFC, 80); |
| 18 | constexpr Result ResultWriteAmiiboFailed(ErrorModule::NFP, 88); | 18 | constexpr Result ResultWriteAmiiboFailed(ErrorModule::NFC, 88); |
| 19 | constexpr Result ResultTagRemoved(ErrorModule::NFC, 97); | 19 | constexpr Result ResultTagRemoved(ErrorModule::NFC, 97); |
| 20 | constexpr Result ResultRegistrationIsNotInitialized(ErrorModule::NFP, 120); | 20 | constexpr Result ResultUnableToAccessBackupFile(ErrorModule::NFC, 113); |
| 21 | constexpr Result ResultApplicationAreaIsNotInitialized(ErrorModule::NFP, 128); | 21 | constexpr Result ResultRegistrationIsNotInitialized(ErrorModule::NFC, 120); |
| 22 | constexpr Result ResultCorruptedData(ErrorModule::NFP, 144); | 22 | constexpr Result ResultApplicationAreaIsNotInitialized(ErrorModule::NFC, 128); |
| 23 | constexpr Result ResultWrongApplicationAreaId(ErrorModule::NFP, 152); | 23 | constexpr Result ResultCorruptedDataWithBackup(ErrorModule::NFC, 136); |
| 24 | constexpr Result ResultApplicationAreaExist(ErrorModule::NFP, 168); | 24 | constexpr Result ResultCorruptedData(ErrorModule::NFC, 144); |
| 25 | constexpr Result ResultNotAnAmiibo(ErrorModule::NFP, 178); | 25 | constexpr Result ResultWrongApplicationAreaId(ErrorModule::NFC, 152); |
| 26 | constexpr Result ResultUnknown216(ErrorModule::NFC, 216); | 26 | constexpr Result ResultApplicationAreaExist(ErrorModule::NFC, 168); |
| 27 | constexpr Result ResultNotAnAmiibo(ErrorModule::NFC, 178); | ||
| 28 | constexpr Result ResultBackupPathAlreadyExist(ErrorModule::NFC, 216); | ||
| 27 | 29 | ||
| 28 | } // namespace Service::NFC | 30 | } // namespace Service::NFC |
diff --git a/src/core/hle/service/nfp/nfp_interface.cpp b/src/core/hle/service/nfp/nfp_interface.cpp index 21d159154..34ef9d82d 100644 --- a/src/core/hle/service/nfp/nfp_interface.cpp +++ b/src/core/hle/service/nfp/nfp_interface.cpp | |||
| @@ -126,7 +126,7 @@ void Interface::Flush(HLERequestContext& ctx) { | |||
| 126 | void Interface::Restore(HLERequestContext& ctx) { | 126 | void Interface::Restore(HLERequestContext& ctx) { |
| 127 | IPC::RequestParser rp{ctx}; | 127 | IPC::RequestParser rp{ctx}; |
| 128 | const auto device_handle{rp.Pop<u64>()}; | 128 | const auto device_handle{rp.Pop<u64>()}; |
| 129 | LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}", device_handle); | 129 | LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); |
| 130 | 130 | ||
| 131 | auto result = GetManager()->Restore(device_handle); | 131 | auto result = GetManager()->Restore(device_handle); |
| 132 | result = TranslateResultToServiceError(result); | 132 | result = TranslateResultToServiceError(result); |
| @@ -394,7 +394,7 @@ void Interface::BreakTag(HLERequestContext& ctx) { | |||
| 394 | void Interface::ReadBackupData(HLERequestContext& ctx) { | 394 | void Interface::ReadBackupData(HLERequestContext& ctx) { |
| 395 | IPC::RequestParser rp{ctx}; | 395 | IPC::RequestParser rp{ctx}; |
| 396 | const auto device_handle{rp.Pop<u64>()}; | 396 | const auto device_handle{rp.Pop<u64>()}; |
| 397 | LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}", device_handle); | 397 | LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); |
| 398 | 398 | ||
| 399 | std::vector<u8> backup_data{}; | 399 | std::vector<u8> backup_data{}; |
| 400 | auto result = GetManager()->ReadBackupData(device_handle, backup_data); | 400 | auto result = GetManager()->ReadBackupData(device_handle, backup_data); |
| @@ -412,7 +412,7 @@ void Interface::WriteBackupData(HLERequestContext& ctx) { | |||
| 412 | IPC::RequestParser rp{ctx}; | 412 | IPC::RequestParser rp{ctx}; |
| 413 | const auto device_handle{rp.Pop<u64>()}; | 413 | const auto device_handle{rp.Pop<u64>()}; |
| 414 | const auto backup_data_buffer{ctx.ReadBuffer()}; | 414 | const auto backup_data_buffer{ctx.ReadBuffer()}; |
| 415 | LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}", device_handle); | 415 | LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); |
| 416 | 416 | ||
| 417 | auto result = GetManager()->WriteBackupData(device_handle, backup_data_buffer); | 417 | auto result = GetManager()->WriteBackupData(device_handle, backup_data_buffer); |
| 418 | result = TranslateResultToServiceError(result); | 418 | result = TranslateResultToServiceError(result); |
diff --git a/src/core/hle/service/nfp/nfp_result.h b/src/core/hle/service/nfp/nfp_result.h index 4c126cd81..618533843 100644 --- a/src/core/hle/service/nfp/nfp_result.h +++ b/src/core/hle/service/nfp/nfp_result.h | |||
| @@ -17,9 +17,11 @@ constexpr Result ResultWriteAmiiboFailed(ErrorModule::NFP, 88); | |||
| 17 | constexpr Result ResultTagRemoved(ErrorModule::NFP, 97); | 17 | constexpr Result ResultTagRemoved(ErrorModule::NFP, 97); |
| 18 | constexpr Result ResultRegistrationIsNotInitialized(ErrorModule::NFP, 120); | 18 | constexpr Result ResultRegistrationIsNotInitialized(ErrorModule::NFP, 120); |
| 19 | constexpr Result ResultApplicationAreaIsNotInitialized(ErrorModule::NFP, 128); | 19 | constexpr Result ResultApplicationAreaIsNotInitialized(ErrorModule::NFP, 128); |
| 20 | constexpr Result ResultCorruptedDataWithBackup(ErrorModule::NFP, 136); | ||
| 20 | constexpr Result ResultCorruptedData(ErrorModule::NFP, 144); | 21 | constexpr Result ResultCorruptedData(ErrorModule::NFP, 144); |
| 21 | constexpr Result ResultWrongApplicationAreaId(ErrorModule::NFP, 152); | 22 | constexpr Result ResultWrongApplicationAreaId(ErrorModule::NFP, 152); |
| 22 | constexpr Result ResultApplicationAreaExist(ErrorModule::NFP, 168); | 23 | constexpr Result ResultApplicationAreaExist(ErrorModule::NFP, 168); |
| 23 | constexpr Result ResultNotAnAmiibo(ErrorModule::NFP, 178); | 24 | constexpr Result ResultNotAnAmiibo(ErrorModule::NFP, 178); |
| 25 | constexpr Result ResultUnableToAccessBackupFile(ErrorModule::NFP, 200); | ||
| 24 | 26 | ||
| 25 | } // namespace Service::NFP | 27 | } // namespace Service::NFP |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp index 0c7aee1b8..dc45169ad 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp | |||
| @@ -69,7 +69,7 @@ NvResult nvhost_nvdec::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> in | |||
| 69 | 69 | ||
| 70 | void nvhost_nvdec::OnOpen(DeviceFD fd) { | 70 | void nvhost_nvdec::OnOpen(DeviceFD fd) { |
| 71 | LOG_INFO(Service_NVDRV, "NVDEC video stream started"); | 71 | LOG_INFO(Service_NVDRV, "NVDEC video stream started"); |
| 72 | system.AudioCore().SetNVDECActive(true); | 72 | system.SetNVDECActive(true); |
| 73 | } | 73 | } |
| 74 | 74 | ||
| 75 | void nvhost_nvdec::OnClose(DeviceFD fd) { | 75 | void nvhost_nvdec::OnClose(DeviceFD fd) { |
| @@ -79,7 +79,7 @@ void nvhost_nvdec::OnClose(DeviceFD fd) { | |||
| 79 | if (iter != host1x_file.fd_to_id.end()) { | 79 | if (iter != host1x_file.fd_to_id.end()) { |
| 80 | system.GPU().ClearCdmaInstance(iter->second); | 80 | system.GPU().ClearCdmaInstance(iter->second); |
| 81 | } | 81 | } |
| 82 | system.AudioCore().SetNVDECActive(false); | 82 | system.SetNVDECActive(false); |
| 83 | } | 83 | } |
| 84 | 84 | ||
| 85 | } // namespace Service::Nvidia::Devices | 85 | } // namespace Service::Nvidia::Devices |
diff --git a/src/core/hle/service/nvnflinger/nvnflinger.cpp b/src/core/hle/service/nvnflinger/nvnflinger.cpp index 4988e6e17..da2d5890f 100644 --- a/src/core/hle/service/nvnflinger/nvnflinger.cpp +++ b/src/core/hle/service/nvnflinger/nvnflinger.cpp | |||
| @@ -324,6 +324,10 @@ s64 Nvnflinger::GetNextTicks() const { | |||
| 324 | speed_scale = 0.01f; | 324 | speed_scale = 0.01f; |
| 325 | } | 325 | } |
| 326 | } | 326 | } |
| 327 | if (system.GetNVDECActive() && settings.use_video_framerate.GetValue()) { | ||
| 328 | // Run at intended presentation rate during video playback. | ||
| 329 | speed_scale = 1.f; | ||
| 330 | } | ||
| 327 | 331 | ||
| 328 | // As an extension, treat nonpositive swap interval as framerate multiplier. | 332 | // As an extension, treat nonpositive swap interval as framerate multiplier. |
| 329 | const f32 effective_fps = swap_interval <= 0 ? 120.f * static_cast<f32>(1 - swap_interval) | 333 | const f32 effective_fps = swap_interval <= 0 ? 120.f * static_cast<f32>(1 - swap_interval) |
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp index 73d04d7ee..7be6cf5f3 100644 --- a/src/core/loader/nro.cpp +++ b/src/core/loader/nro.cpp | |||
| @@ -33,7 +33,8 @@ static_assert(sizeof(NroSegmentHeader) == 0x8, "NroSegmentHeader has incorrect s | |||
| 33 | struct NroHeader { | 33 | struct NroHeader { |
| 34 | INSERT_PADDING_BYTES(0x4); | 34 | INSERT_PADDING_BYTES(0x4); |
| 35 | u32_le module_header_offset; | 35 | u32_le module_header_offset; |
| 36 | INSERT_PADDING_BYTES(0x8); | 36 | u32 magic_ext1; |
| 37 | u32 magic_ext2; | ||
| 37 | u32_le magic; | 38 | u32_le magic; |
| 38 | INSERT_PADDING_BYTES(0x4); | 39 | INSERT_PADDING_BYTES(0x4); |
| 39 | u32_le file_size; | 40 | u32_le file_size; |
| @@ -124,6 +125,16 @@ FileType AppLoader_NRO::IdentifyType(const FileSys::VirtualFile& nro_file) { | |||
| 124 | return FileType::Error; | 125 | return FileType::Error; |
| 125 | } | 126 | } |
| 126 | 127 | ||
| 128 | bool AppLoader_NRO::IsHomebrew() { | ||
| 129 | // Read NSO header | ||
| 130 | NroHeader nro_header{}; | ||
| 131 | if (sizeof(NroHeader) != file->ReadObject(&nro_header)) { | ||
| 132 | return false; | ||
| 133 | } | ||
| 134 | return nro_header.magic_ext1 == Common::MakeMagic('H', 'O', 'M', 'E') && | ||
| 135 | nro_header.magic_ext2 == Common::MakeMagic('B', 'R', 'E', 'W'); | ||
| 136 | } | ||
| 137 | |||
| 127 | static constexpr u32 PageAlignSize(u32 size) { | 138 | static constexpr u32 PageAlignSize(u32 size) { |
| 128 | return static_cast<u32>((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK); | 139 | return static_cast<u32>((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK); |
| 129 | } | 140 | } |
diff --git a/src/core/loader/nro.h b/src/core/loader/nro.h index ccb77b581..8de6eebc6 100644 --- a/src/core/loader/nro.h +++ b/src/core/loader/nro.h | |||
| @@ -38,6 +38,8 @@ public: | |||
| 38 | */ | 38 | */ |
| 39 | static FileType IdentifyType(const FileSys::VirtualFile& nro_file); | 39 | static FileType IdentifyType(const FileSys::VirtualFile& nro_file); |
| 40 | 40 | ||
| 41 | bool IsHomebrew(); | ||
| 42 | |||
| 41 | FileType GetFileType() const override { | 43 | FileType GetFileType() const override { |
| 42 | return IdentifyType(file); | 44 | return IdentifyType(file); |
| 43 | } | 45 | } |
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 6288fef62..bac9dff90 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp | |||
| @@ -101,6 +101,12 @@ const std::map<Settings::RendererBackend, QString> Config::renderer_backend_text | |||
| 101 | {Settings::RendererBackend::Null, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Null"))}, | 101 | {Settings::RendererBackend::Null, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Null"))}, |
| 102 | }; | 102 | }; |
| 103 | 103 | ||
| 104 | const std::map<Settings::ShaderBackend, QString> Config::shader_backend_texts_map = { | ||
| 105 | {Settings::ShaderBackend::GLSL, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "GLSL"))}, | ||
| 106 | {Settings::ShaderBackend::GLASM, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "GLASM"))}, | ||
| 107 | {Settings::ShaderBackend::SPIRV, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "SPIRV"))}, | ||
| 108 | }; | ||
| 109 | |||
| 104 | // This shouldn't have anything except static initializers (no functions). So | 110 | // This shouldn't have anything except static initializers (no functions). So |
| 105 | // QKeySequence(...).toString() is NOT ALLOWED HERE. | 111 | // QKeySequence(...).toString() is NOT ALLOWED HERE. |
| 106 | // This must be in alphabetical order according to action name as it must have the same order as | 112 | // This must be in alphabetical order according to action name as it must have the same order as |
| @@ -754,6 +760,7 @@ void Config::ReadRendererValues() { | |||
| 754 | ReadGlobalSetting(Settings::values.use_fast_gpu_time); | 760 | ReadGlobalSetting(Settings::values.use_fast_gpu_time); |
| 755 | ReadGlobalSetting(Settings::values.use_vulkan_driver_pipeline_cache); | 761 | ReadGlobalSetting(Settings::values.use_vulkan_driver_pipeline_cache); |
| 756 | ReadGlobalSetting(Settings::values.enable_compute_pipelines); | 762 | ReadGlobalSetting(Settings::values.enable_compute_pipelines); |
| 763 | ReadGlobalSetting(Settings::values.use_video_framerate); | ||
| 757 | ReadGlobalSetting(Settings::values.bg_red); | 764 | ReadGlobalSetting(Settings::values.bg_red); |
| 758 | ReadGlobalSetting(Settings::values.bg_green); | 765 | ReadGlobalSetting(Settings::values.bg_green); |
| 759 | ReadGlobalSetting(Settings::values.bg_blue); | 766 | ReadGlobalSetting(Settings::values.bg_blue); |
| @@ -1409,6 +1416,7 @@ void Config::SaveRendererValues() { | |||
| 1409 | WriteGlobalSetting(Settings::values.use_fast_gpu_time); | 1416 | WriteGlobalSetting(Settings::values.use_fast_gpu_time); |
| 1410 | WriteGlobalSetting(Settings::values.use_vulkan_driver_pipeline_cache); | 1417 | WriteGlobalSetting(Settings::values.use_vulkan_driver_pipeline_cache); |
| 1411 | WriteGlobalSetting(Settings::values.enable_compute_pipelines); | 1418 | WriteGlobalSetting(Settings::values.enable_compute_pipelines); |
| 1419 | WriteGlobalSetting(Settings::values.use_video_framerate); | ||
| 1412 | WriteGlobalSetting(Settings::values.bg_red); | 1420 | WriteGlobalSetting(Settings::values.bg_red); |
| 1413 | WriteGlobalSetting(Settings::values.bg_green); | 1421 | WriteGlobalSetting(Settings::values.bg_green); |
| 1414 | WriteGlobalSetting(Settings::values.bg_blue); | 1422 | WriteGlobalSetting(Settings::values.bg_blue); |
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h index ad590ea9e..0fd4baf6b 100644 --- a/src/yuzu/configuration/config.h +++ b/src/yuzu/configuration/config.h | |||
| @@ -54,6 +54,7 @@ public: | |||
| 54 | static const std::map<bool, QString> use_docked_mode_texts_map; | 54 | static const std::map<bool, QString> use_docked_mode_texts_map; |
| 55 | static const std::map<Settings::GPUAccuracy, QString> gpu_accuracy_texts_map; | 55 | static const std::map<Settings::GPUAccuracy, QString> gpu_accuracy_texts_map; |
| 56 | static const std::map<Settings::RendererBackend, QString> renderer_backend_texts_map; | 56 | static const std::map<Settings::RendererBackend, QString> renderer_backend_texts_map; |
| 57 | static const std::map<Settings::ShaderBackend, QString> shader_backend_texts_map; | ||
| 57 | 58 | ||
| 58 | static constexpr UISettings::Theme default_theme{ | 59 | static constexpr UISettings::Theme default_theme{ |
| 59 | #ifdef _WIN32 | 60 | #ifdef _WIN32 |
diff --git a/src/yuzu/configuration/configure_graphics_advanced.cpp b/src/yuzu/configuration/configure_graphics_advanced.cpp index 896863f87..0463ac8b9 100644 --- a/src/yuzu/configuration/configure_graphics_advanced.cpp +++ b/src/yuzu/configuration/configure_graphics_advanced.cpp | |||
| @@ -42,6 +42,7 @@ void ConfigureGraphicsAdvanced::SetConfiguration() { | |||
| 42 | Settings::values.use_vulkan_driver_pipeline_cache.GetValue()); | 42 | Settings::values.use_vulkan_driver_pipeline_cache.GetValue()); |
| 43 | ui->enable_compute_pipelines_checkbox->setChecked( | 43 | ui->enable_compute_pipelines_checkbox->setChecked( |
| 44 | Settings::values.enable_compute_pipelines.GetValue()); | 44 | Settings::values.enable_compute_pipelines.GetValue()); |
| 45 | ui->use_video_framerate_checkbox->setChecked(Settings::values.use_video_framerate.GetValue()); | ||
| 45 | 46 | ||
| 46 | if (Settings::IsConfiguringGlobal()) { | 47 | if (Settings::IsConfiguringGlobal()) { |
| 47 | ui->gpu_accuracy->setCurrentIndex( | 48 | ui->gpu_accuracy->setCurrentIndex( |
| @@ -91,6 +92,8 @@ void ConfigureGraphicsAdvanced::ApplyConfiguration() { | |||
| 91 | ConfigurationShared::ApplyPerGameSetting(&Settings::values.enable_compute_pipelines, | 92 | ConfigurationShared::ApplyPerGameSetting(&Settings::values.enable_compute_pipelines, |
| 92 | ui->enable_compute_pipelines_checkbox, | 93 | ui->enable_compute_pipelines_checkbox, |
| 93 | enable_compute_pipelines); | 94 | enable_compute_pipelines); |
| 95 | ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_video_framerate, | ||
| 96 | ui->use_video_framerate_checkbox, use_video_framerate); | ||
| 94 | } | 97 | } |
| 95 | 98 | ||
| 96 | void ConfigureGraphicsAdvanced::changeEvent(QEvent* event) { | 99 | void ConfigureGraphicsAdvanced::changeEvent(QEvent* event) { |
| @@ -125,6 +128,8 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() { | |||
| 125 | Settings::values.max_anisotropy.UsingGlobal()); | 128 | Settings::values.max_anisotropy.UsingGlobal()); |
| 126 | ui->enable_compute_pipelines_checkbox->setEnabled( | 129 | ui->enable_compute_pipelines_checkbox->setEnabled( |
| 127 | Settings::values.enable_compute_pipelines.UsingGlobal()); | 130 | Settings::values.enable_compute_pipelines.UsingGlobal()); |
| 131 | ui->use_video_framerate_checkbox->setEnabled( | ||
| 132 | Settings::values.use_video_framerate.UsingGlobal()); | ||
| 128 | 133 | ||
| 129 | return; | 134 | return; |
| 130 | } | 135 | } |
| @@ -149,6 +154,9 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() { | |||
| 149 | ConfigurationShared::SetColoredTristate(ui->enable_compute_pipelines_checkbox, | 154 | ConfigurationShared::SetColoredTristate(ui->enable_compute_pipelines_checkbox, |
| 150 | Settings::values.enable_compute_pipelines, | 155 | Settings::values.enable_compute_pipelines, |
| 151 | enable_compute_pipelines); | 156 | enable_compute_pipelines); |
| 157 | ConfigurationShared::SetColoredTristate(ui->use_video_framerate_checkbox, | ||
| 158 | Settings::values.use_video_framerate, | ||
| 159 | use_video_framerate); | ||
| 152 | ConfigurationShared::SetColoredComboBox( | 160 | ConfigurationShared::SetColoredComboBox( |
| 153 | ui->gpu_accuracy, ui->label_gpu_accuracy, | 161 | ui->gpu_accuracy, ui->label_gpu_accuracy, |
| 154 | static_cast<int>(Settings::values.gpu_accuracy.GetValue(true))); | 162 | static_cast<int>(Settings::values.gpu_accuracy.GetValue(true))); |
diff --git a/src/yuzu/configuration/configure_graphics_advanced.h b/src/yuzu/configuration/configure_graphics_advanced.h index 1c7b636b9..a4dc8ceb0 100644 --- a/src/yuzu/configuration/configure_graphics_advanced.h +++ b/src/yuzu/configuration/configure_graphics_advanced.h | |||
| @@ -47,6 +47,7 @@ private: | |||
| 47 | ConfigurationShared::CheckState use_fast_gpu_time; | 47 | ConfigurationShared::CheckState use_fast_gpu_time; |
| 48 | ConfigurationShared::CheckState use_vulkan_driver_pipeline_cache; | 48 | ConfigurationShared::CheckState use_vulkan_driver_pipeline_cache; |
| 49 | ConfigurationShared::CheckState enable_compute_pipelines; | 49 | ConfigurationShared::CheckState enable_compute_pipelines; |
| 50 | ConfigurationShared::CheckState use_video_framerate; | ||
| 50 | 51 | ||
| 51 | const Core::System& system; | 52 | const Core::System& system; |
| 52 | }; | 53 | }; |
diff --git a/src/yuzu/configuration/configure_graphics_advanced.ui b/src/yuzu/configuration/configure_graphics_advanced.ui index 37757a918..e7f0ef6be 100644 --- a/src/yuzu/configuration/configure_graphics_advanced.ui +++ b/src/yuzu/configuration/configure_graphics_advanced.ui | |||
| @@ -192,6 +192,16 @@ Compute pipelines are always enabled on all other drivers.</string> | |||
| 192 | </widget> | 192 | </widget> |
| 193 | </item> | 193 | </item> |
| 194 | <item> | 194 | <item> |
| 195 | <widget class="QCheckBox" name="use_video_framerate_checkbox"> | ||
| 196 | <property name="toolTip"> | ||
| 197 | <string>Run the game at normal speed during video playback, even when the framerate is unlocked.</string> | ||
| 198 | </property> | ||
| 199 | <property name="text"> | ||
| 200 | <string>Sync to framerate of video playback</string> | ||
| 201 | </property> | ||
| 202 | </widget> | ||
| 203 | </item> | ||
| 204 | <item> | ||
| 195 | <widget class="QWidget" name="af_layout" native="true"> | 205 | <widget class="QWidget" name="af_layout" native="true"> |
| 196 | <layout class="QHBoxLayout" name="horizontalLayout_1"> | 206 | <layout class="QHBoxLayout" name="horizontalLayout_1"> |
| 197 | <property name="leftMargin"> | 207 | <property name="leftMargin"> |
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 145fea5f1..9d06b21b6 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -4116,7 +4116,13 @@ void GMainWindow::UpdateDockedButton() { | |||
| 4116 | void GMainWindow::UpdateAPIText() { | 4116 | void GMainWindow::UpdateAPIText() { |
| 4117 | const auto api = Settings::values.renderer_backend.GetValue(); | 4117 | const auto api = Settings::values.renderer_backend.GetValue(); |
| 4118 | const auto renderer_status_text = Config::renderer_backend_texts_map.find(api)->second; | 4118 | const auto renderer_status_text = Config::renderer_backend_texts_map.find(api)->second; |
| 4119 | renderer_status_button->setText(renderer_status_text.toUpper()); | 4119 | renderer_status_button->setText( |
| 4120 | api == Settings::RendererBackend::OpenGL | ||
| 4121 | ? tr("%1 %2").arg( | ||
| 4122 | renderer_status_text.toUpper(), | ||
| 4123 | Config::shader_backend_texts_map.find(Settings::values.shader_backend.GetValue()) | ||
| 4124 | ->second) | ||
| 4125 | : renderer_status_text.toUpper()); | ||
| 4120 | } | 4126 | } |
| 4121 | 4127 | ||
| 4122 | void GMainWindow::UpdateFilterText() { | 4128 | void GMainWindow::UpdateFilterText() { |