aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/android/app/build.gradle.kts1
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt21
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt1
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt8
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt12
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ProgressDialogFragment.kt11
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt42
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt9
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt117
-rw-r--r--src/android/app/src/main/jni/android_settings.h8
-rw-r--r--src/android/app/src/main/jni/native.cpp32
-rw-r--r--src/android/app/src/main/res/values/strings.xml20
-rw-r--r--src/core/hle/api_version.h17
-rw-r--r--src/core/hle/kernel/svc_version.h7
-rw-r--r--src/yuzu/main.cpp33
-rw-r--r--src/yuzu/main.h4
18 files changed, 287 insertions, 62 deletions
diff --git a/src/android/app/build.gradle.kts b/src/android/app/build.gradle.kts
index cc38141456..fb15783426 100644
--- a/src/android/app/build.gradle.kts
+++ b/src/android/app/build.gradle.kts
@@ -246,6 +246,7 @@ dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3")
implementation("androidx.compose.ui:ui-graphics-android:1.7.8")
implementation("androidx.compose.ui:ui-text-android:1.7.8")
+ implementation("net.swiftzer.semver:semver:2.0.0")
}
fun runGitCommand(command: List<String>): String {
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 3435d08132..c7d6d6c33a 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
@@ -16,6 +16,7 @@ import android.view.View
import android.widget.TextView
import androidx.annotation.Keep
import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import net.swiftzer.semver.SemVer
import java.lang.ref.WeakReference
import org.yuzu.yuzu_emu.activities.EmulationActivity
import org.yuzu.yuzu_emu.fragments.CoreErrorDialogFragment
@@ -408,6 +409,26 @@ object NativeLibrary {
external fun isFirmwareAvailable(): Boolean
/**
+ * Gets the firmware version.
+ *
+ * @return Reported firmware version
+ */
+ external fun firmwareVersion(): String
+
+ fun isFirmwareSupported(): Boolean {
+ var version: SemVer
+
+ try {
+ version = SemVer.parse(firmwareVersion())
+ } catch (_: Exception) {
+ return false
+ }
+ val max = SemVer(19, 0, 1)
+
+ return version <= max
+ }
+
+ /**
* Checks the PatchManager for any addons that are available
*
* @param path Path to game file. Can be a [Uri].
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt
index f43e5f9dd6..4c527163c8 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt
@@ -41,6 +41,7 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
SHOW_DEVICE_MODEL("show_device_model"),
SHOW_GPU_MODEL("show_gpu_model"),
SHOW_SOC_MODEL("show_soc_model"),
+ SHOW_FW_VERSION("show_firmware_version"),
SOC_OVERLAY_BACKGROUND("soc_overlay_background"),
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt
index 84c9f419a4..b52355e255 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt
@@ -460,6 +460,14 @@ abstract class SettingsItem(
descriptionId = R.string.show_soc_model_description
)
)
+ put(
+ SwitchSetting(
+ BooleanSetting.SHOW_FW_VERSION,
+ titleId = R.string.show_fw_version,
+ descriptionId = R.string.show_fw_version_description
+ )
+ )
+
put(
SingleChoiceSetting(
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
index 24214c3895..2337bc5821 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
@@ -271,6 +271,8 @@ class SettingsFragmentPresenter(
if (Build.VERSION.SDK_INT >= 31) {
add(BooleanSetting.SHOW_SOC_MODEL.key)
}
+
+ add(BooleanSetting.SHOW_FW_VERSION.key)
}
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
index 4b7da6ef23..641522de4d 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
@@ -1,9 +1,8 @@
-// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-// SPDX-FileCopyrightText: 2025 Eden Emulator Project
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
+// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.fragments
@@ -754,6 +753,11 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
}
+ if (BooleanSetting.SHOW_FW_VERSION.getBoolean(NativeConfig.isPerGameConfigLoaded())) {
+ if (sb.isNotEmpty()) sb.append(" | ")
+ sb.append(NativeLibrary.firmwareVersion())
+ }
+
binding.showSocOverlayText.text = sb.toString()
if (BooleanSetting.SOC_OVERLAY_BACKGROUND.getBoolean(NativeConfig.isPerGameConfigLoaded())) {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt
index f9ecd6f49f..f39d9514b3 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2025 Eden Emulator Project
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.fragments
@@ -137,7 +137,7 @@ class HomeSettingsFragment : Fragment() {
binding.root.findNavController()
.navigate(R.id.action_homeSettingsFragment_to_appletLauncherFragment)
},
- { NativeLibrary.isFirmwareAvailable() },
+ { NativeLibrary.isFirmwareAvailable() && NativeLibrary.isFirmwareSupported() },
R.string.applets_error_firmware,
R.string.applets_error_description
)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ProgressDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ProgressDialogFragment.kt
index ee3bb0386a..26fcf7c0db 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ProgressDialogFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ProgressDialogFragment.kt
@@ -1,9 +1,13 @@
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.fragments
import android.app.Dialog
+import android.content.DialogInterface
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@@ -28,6 +32,8 @@ class ProgressDialogFragment : DialogFragment() {
private val PROGRESS_BAR_RESOLUTION = 1000
+ var onDialogComplete: (() -> Unit)? = null
+
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val titleId = requireArguments().getInt(TITLE)
val cancellable = requireArguments().getBoolean(CANCELLABLE)
@@ -121,6 +127,11 @@ class ProgressDialogFragment : DialogFragment() {
}
}
+ override fun onDismiss(dialog: DialogInterface) {
+ super.onDismiss(dialog)
+ onDialogComplete?.invoke()
+ }
+
companion object {
const val TAG = "IndeterminateProgressDialogFragment"
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt
index 6828b32e84..6443067885 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2025 Eden Emulator Project
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.fragments
@@ -129,7 +129,7 @@ class SetupFragment : Fragment() {
0,
{
if (NotificationManagerCompat.from(requireContext())
- .areNotificationsEnabled()
+ .areNotificationsEnabled()
) {
StepState.COMPLETE
} else {
@@ -168,6 +168,32 @@ class SetupFragment : Fragment() {
)
add(
SetupPage(
+ R.drawable.ic_firmware,
+ R.string.firmware,
+ R.string.firmware_description,
+ R.drawable.ic_add,
+ true,
+ R.string.select_firmware,
+ {
+ firmwareCallback = it
+ getFirmware.launch(arrayOf("application/zip"))
+ },
+ true,
+ R.string.install_firmware_warning,
+ R.string.install_firmware_warning_description,
+ R.string.install_firmware_warning_help,
+ {
+ if (NativeLibrary.isFirmwareAvailable()) {
+ StepState.COMPLETE
+ } else {
+ StepState.INCOMPLETE
+ }
+ }
+ )
+ )
+
+ add(
+ SetupPage(
R.drawable.ic_controller,
R.string.games,
R.string.games_description,
@@ -321,6 +347,7 @@ class SetupFragment : Fragment() {
}
private lateinit var keyCallback: SetupCallback
+ private lateinit var firmwareCallback: SetupCallback
val getProdKey =
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
@@ -332,6 +359,17 @@ class SetupFragment : Fragment() {
}
}
+ val getFirmware =
+ registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
+ if (result != null) {
+ mainActivity.processFirmware(result) {
+ if (NativeLibrary.isFirmwareAvailable()) {
+ firmwareCallback.onStepCompleted()
+ }
+ }
+ }
+ }
+
private lateinit var gamesDirCallback: SetupCallback
val getGamesDirectory =
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt
index 08eee8c815..97a60ee184 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2025 Eden Emulator Project
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.model
@@ -31,6 +31,9 @@ class HomeViewModel : ViewModel() {
private val _checkKeys = MutableStateFlow(false)
val checkKeys = _checkKeys.asStateFlow()
+ private val _checkFirmware = MutableStateFlow(false)
+ val checkFirmware = _checkFirmware.asStateFlow()
+
var navigatedToSetup = false
fun setStatusBarShadeVisibility(visible: Boolean) {
@@ -63,4 +66,8 @@ class HomeViewModel : ViewModel() {
fun setCheckKeys(value: Boolean) {
_checkKeys.value = value
}
+
+ fun setCheckFirmware(value: Boolean) {
+ _checkFirmware.value = value
+ }
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
index 3e3759d7eb..3a9c5486a8 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2025 Eden Emulator Project
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.ui.main
@@ -62,6 +62,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
private val CHECKED_DECRYPTION = "CheckedDecryption"
private var checkedDecryption = false
+ private val CHECKED_FIRMWARE = "CheckedFirmware"
+ private var checkedFirmware = false
+
override fun onCreate(savedInstanceState: Bundle?) {
val splashScreen = installSplashScreen()
splashScreen.setKeepOnScreenCondition { !DirectoryInitialization.areDirectoriesReady }
@@ -76,17 +79,27 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
if (savedInstanceState != null) {
checkedDecryption = savedInstanceState.getBoolean(CHECKED_DECRYPTION)
+ checkedFirmware = savedInstanceState.getBoolean(CHECKED_FIRMWARE)
}
if (!checkedDecryption) {
val firstTimeSetup = PreferenceManager.getDefaultSharedPreferences(applicationContext)
.getBoolean(Settings.PREF_FIRST_APP_LAUNCH, true)
if (!firstTimeSetup) {
checkKeys()
- showPreAlphaWarningDialog()
}
checkedDecryption = true
}
+ if (!checkedFirmware) {
+ val firstTimeSetup = PreferenceManager.getDefaultSharedPreferences(applicationContext)
+ .getBoolean(Settings.PREF_FIRST_APP_LAUNCH, true)
+ if (!firstTimeSetup) {
+ checkFirmware()
+ showPreAlphaWarningDialog()
+ }
+ checkedFirmware = true
+ }
+
WindowCompat.setDecorFitsSystemWindows(window, false)
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING)
@@ -135,6 +148,10 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
if (it) checkKeys()
}
+ homeViewModel.checkFirmware.collect(this, resetState = { homeViewModel.setCheckFirmware(false) }) {
+ if (it) checkFirmware()
+ }
+
setInsets()
}
@@ -175,9 +192,21 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
}
}
+ private fun checkFirmware() {
+ if (!NativeLibrary.isFirmwareAvailable() || !NativeLibrary.isFirmwareSupported()) {
+ MessageDialogFragment.newInstance(
+ titleId = R.string.firmware_missing,
+ descriptionId = R.string.firmware_missing_description,
+ helpLinkId = R.string.firmware_missing_help
+ ).show(supportFragmentManager, MessageDialogFragment.TAG)
+ }
+ }
+
+
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putBoolean(CHECKED_DECRYPTION, checkedDecryption)
+ outState.putBoolean(CHECKED_FIRMWARE, checkedFirmware)
}
fun finishSetup(navController: NavController) {
@@ -306,6 +335,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
Toast.LENGTH_SHORT
).show()
homeViewModel.setCheckKeys(true)
+ homeViewModel.setCheckFirmware(true)
gamesViewModel.reloadGames(true)
return true
} else {
@@ -327,47 +357,55 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
return@registerForActivityResult
}
- val filterNCA = FilenameFilter { _, dirName -> dirName.endsWith(".nca") }
+ processFirmware(result)
+ }
- val firmwarePath =
- File(DirectoryInitialization.userDirectory + "/nand/system/Contents/registered/")
- val cacheFirmwareDir = File("${cacheDir.path}/registered/")
+ fun processFirmware(result: Uri, onComplete: (() -> Unit)? = null) {
+ val filterNCA = FilenameFilter { _, dirName -> dirName.endsWith(".nca") }
- ProgressDialogFragment.newInstance(
- this,
- R.string.firmware_installing
- ) { progressCallback, _ ->
- var messageToShow: Any
- try {
- FileUtil.unzipToInternalStorage(
- result.toString(),
- cacheFirmwareDir,
- progressCallback
+ val firmwarePath =
+ File(DirectoryInitialization.userDirectory + "/nand/system/Contents/registered/")
+ val cacheFirmwareDir = File("${cacheDir.path}/registered/")
+
+ ProgressDialogFragment.newInstance(
+ this,
+ R.string.firmware_installing
+ ) { progressCallback, _ ->
+ var messageToShow: Any
+ try {
+ FileUtil.unzipToInternalStorage(
+ result.toString(),
+ cacheFirmwareDir,
+ progressCallback
+ )
+ val unfilteredNumOfFiles = cacheFirmwareDir.list()?.size ?: -1
+ val filteredNumOfFiles = cacheFirmwareDir.list(filterNCA)?.size ?: -2
+ messageToShow = if (unfilteredNumOfFiles != filteredNumOfFiles) {
+ MessageDialogFragment.newInstance(
+ this,
+ titleId = R.string.firmware_installed_failure,
+ descriptionId = R.string.firmware_installed_failure_description
)
- val unfilteredNumOfFiles = cacheFirmwareDir.list()?.size ?: -1
- val filteredNumOfFiles = cacheFirmwareDir.list(filterNCA)?.size ?: -2
- messageToShow = if (unfilteredNumOfFiles != filteredNumOfFiles) {
- MessageDialogFragment.newInstance(
- this,
- titleId = R.string.firmware_installed_failure,
- descriptionId = R.string.firmware_installed_failure_description
- )
- } else {
- firmwarePath.deleteRecursively()
- cacheFirmwareDir.copyRecursively(firmwarePath, true)
- NativeLibrary.initializeSystem(true)
- homeViewModel.setCheckKeys(true)
- getString(R.string.save_file_imported_success)
- }
- } catch (e: Exception) {
- Log.error("[MainActivity] Firmware install failed - ${e.message}")
- messageToShow = getString(R.string.fatal_error)
- } finally {
- cacheFirmwareDir.deleteRecursively()
+ } else {
+ firmwarePath.deleteRecursively()
+ cacheFirmwareDir.copyRecursively(firmwarePath, true)
+ NativeLibrary.initializeSystem(true)
+ homeViewModel.setCheckKeys(true)
+ homeViewModel.setCheckFirmware(true)
+ getString(R.string.save_file_imported_success)
}
- messageToShow
- }.show(supportFragmentManager, ProgressDialogFragment.TAG)
- }
+ } catch (e: Exception) {
+ Log.error("[MainActivity] Firmware install failed - ${e.message}")
+ messageToShow = getString(R.string.fatal_error)
+ } finally {
+ cacheFirmwareDir.deleteRecursively()
+ }
+ messageToShow
+ }.apply {
+ onDialogComplete = onComplete
+ }.show(supportFragmentManager, ProgressDialogFragment.TAG)
+ }
+
fun uninstallFirmware() {
val firmwarePath = File(DirectoryInitialization.userDirectory + "/nand/system/Contents/registered/")
ProgressDialogFragment.newInstance(
@@ -382,6 +420,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
// Optionally reinitialize the system or perform other necessary steps
NativeLibrary.initializeSystem(true)
homeViewModel.setCheckKeys(true)
+ homeViewModel.setCheckFirmware(true)
messageToShow = getString(R.string.firmware_uninstalled_success)
} else {
messageToShow = getString(R.string.firmware_uninstalled_failure)
diff --git a/src/android/app/src/main/jni/android_settings.h b/src/android/app/src/main/jni/android_settings.h
index 14d16a2182..67d70a6adc 100644
--- a/src/android/app/src/main/jni/android_settings.h
+++ b/src/android/app/src/main/jni/android_settings.h
@@ -1,3 +1,6 @@
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -136,6 +139,11 @@ namespace AndroidSettings {
Settings::Specialization::Default, true, true,
&show_performance_overlay};
+ Settings::Setting<bool> show_fw_version{linkage, true, "show_firmware_version",
+ Settings::Category::Overlay,
+ Settings::Specialization::Default, true, true,
+ &show_performance_overlay};
+
Settings::Setting<bool> soc_overlay_background{linkage, false, "soc_overlay_background",
Settings::Category::Overlay,
Settings::Specialization::Default, true, true,
diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp
index cc00cfe024..4dd6f833b0 100644
--- a/src/android/app/src/main/jni/native.cpp
+++ b/src/android/app/src/main/jni/native.cpp
@@ -57,6 +57,7 @@
#include "core/hle/service/am/applet_manager.h"
#include "core/hle/service/am/frontend/applets.h"
#include "core/hle/service/filesystem/filesystem.h"
+#include "core/hle/service/set/system_settings_server.h"
#include "core/loader/loader.h"
#include "frontend_common/config.h"
#include "hid_core/frontend/emulated_controller.h"
@@ -762,22 +763,47 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_setCabinetMode(JNIEnv* env, jclass cl
static_cast<Service::NFP::CabinetMode>(jcabinetMode));
}
-jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isFirmwareAvailable(JNIEnv* env, jclass clazz) {
+bool isFirmwarePresent() {
auto bis_system =
- EmulationSession::GetInstance().System().GetFileSystemController().GetSystemNANDContents();
+ EmulationSession::GetInstance().System().GetFileSystemController().GetSystemNANDContents();
if (!bis_system) {
return false;
}
// Query an applet to see if it's available
auto applet_nca =
- bis_system->GetEntry(0x010000000000100Dull, FileSys::ContentRecordType::Program);
+ bis_system->GetEntry(0x010000000000100Dull, FileSys::ContentRecordType::Program);
if (!applet_nca) {
return false;
}
return true;
}
+jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isFirmwareAvailable(JNIEnv* env, jclass clazz) {
+ return isFirmwarePresent();
+}
+
+// TODO(crueter): This check is nonfunctional...
+jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_firmwareVersion(JNIEnv* env, jclass clazz) {
+ Service::Set::FirmwareVersionFormat firmware_data{};
+ const auto result = Service::Set::GetFirmwareVersionImpl(
+ firmware_data, EmulationSession::GetInstance().System(),
+ Service::Set::GetFirmwareVersionType::Version2);
+
+ if (result.IsError() || !isFirmwarePresent()) {
+ LOG_INFO(Frontend, "Installed firmware: No firmware available");
+
+ return Common::Android::ToJString(env, "N/A");
+ }
+
+ const std::string display_version(firmware_data.display_version.data());
+ const std::string display_title(firmware_data.display_title.data());
+
+ LOG_INFO(Frontend, "Installed firmware: {}", display_title);
+
+ return Common::Android::ToJString(env, display_version);
+}
+
jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPatchesForFile(JNIEnv* env, jobject jobj,
jstring jpath,
jstring jprogramId) {
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index 10d02b4b82..d09da35546 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -58,6 +58,8 @@
<string name="show_gpu_model_description">Display the host GPU model</string>
<string name="show_soc_model">Show SoC Model</string>
<string name="show_soc_model_description">Display the host SoC model</string>
+ <string name="show_fw_version">Show Firmware Version</string>
+ <string name="show_fw_version_description">Display the installed firmware version</string>
<!-- Eden`s Veil -->
<string name="eden_veil">Eden’s Veil</string>
@@ -220,6 +222,9 @@
<string name="keys">Keys</string>
<string name="keys_description">Select your &lt;b>prod.keys&lt;/b> file with the button below.</string>
<string name="select_keys">Select Keys</string>
+ <string name="firmware">Firmware</string>
+ <string name="firmware_description">Select your &lt;b>firmware.zip&lt;/b> file with the button below.\nEden currently requires version &lt;b>19.0.1&lt;/b> or below.</string>
+ <string name="select_firmware">Select Firmware</string>
<string name="games">Games</string>
<string name="games_description">Select your &lt;b>Games&lt;/b> folder with the button below.</string>
<string name="done">Done</string>
@@ -232,6 +237,8 @@
<string name="step_complete">Complete!</string>
<!-- Home strings -->
+ <string name="firmware_version">Firmware:</string>
+
<string name="alphabetical">Alphabetical</string>
<string name="view_list">List</string>
<string name="view_grid">Grid</string>
@@ -262,6 +269,9 @@
<string name="install_prod_keys_warning">Skip adding keys?</string>
<string name="install_prod_keys_warning_description">Valid keys are required to emulate retail games. Only homebrew apps will function if you continue.</string>
<string name="install_prod_keys_warning_help">https://yuzu-mirror.github.io/help/quickstart/#guide-introduction</string>
+ <string name="install_firmware_warning">Skip adding firmware?</string>
+ <string name="install_firmware_warning_description">Many games require access to firmware to run properly.</string>
+ <string name="install_firmware_warning_help">https://yuzu-mirror.github.io/help/quickstart/#guide-introduction</string>
<string name="notifications">Notifications</string>
<string name="notifications_description">Grant the notification permission with the button below.</string>
<string name="give_permission">Grant permission</string>
@@ -357,18 +367,24 @@
<string name="no_save_data_found">No save data found</string>
<string name="verify_installed_content">Verify installed content</string>
<string name="verify_installed_content_description">Checks all installed content for corruption</string>
+
<string name="keys_missing">Encryption keys are missing</string>
<string name="keys_missing_description">Firmware and retail games cannot be decrypted</string>
<string name="keys_missing_help">https://yuzu-mirror.github.io/help/quickstart/#dumping-decryption-keys</string>
+ <string name="firmware_missing">Firmware is missing or too new</string>
+ <string name="firmware_missing_description">Some games may not function properly. Eden requires firmware 19.0.1 or below.</string>
+ <string name="firmware_missing_help">https://yuzu-mirror.github.io/help/quickstart/#dumping-system-firmware</string>
+
<!-- Applet launcher strings -->
<string name="qlaunch_applet">Qlaunch</string>
<string name="qlaunch_description">Launch applications from the system home screen</string>
<string name="applets">Applet launcher</string>
<string name="applets_description">Launch system applets using installed firmware</string>
- <string name="applets_error_firmware">Firmware not installed</string>
+ <string name="applets_error_firmware">Firmware not installed or invalid version</string>
<string name="applets_error_applet">Applet not available</string>
- <string name="applets_error_description"><![CDATA[Please ensure your <a href="https://yuzu-mirror.github.io/help/quickstart/#dumping-prodkeys-and-titlekeys">prod.keys</a> file and <a href="https://yuzu-mirror.github.io/help/quickstart/#dumping-system-firmware">firmware</a> are installed and try again.]]></string>
+ <string name="applets_error_description"><![CDATA[Please ensure your <a href="https://yuzu-mirror.github.io/help/quickstart/#dumping-prodkeys-and-titlekeys">prod.keys</a> file and
+ <a href="https://yuzu-mirror.github.io/help/quickstart/#dumping-system-firmware">firmware</a> are installed and try again.<br>Additionally, ensure your firmware is of version 19.0.1 or older.]]></string>
<string name="album_applet">Album</string>
<string name="album_applet_description">See images stored in the user screenshots folder with the system photo viewer</string>
<string name="mii_edit_applet">Mii edit</string>
diff --git a/src/core/hle/api_version.h b/src/core/hle/api_version.h
index 20e5c7e459..e18397cc04 100644
--- a/src/core/hle/api_version.h
+++ b/src/core/hle/api_version.h
@@ -1,3 +1,6 @@
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -11,8 +14,8 @@ namespace HLE::ApiVersion {
// Horizon OS version constants.
-constexpr u8 HOS_VERSION_MAJOR = 19;
-constexpr u8 HOS_VERSION_MINOR = 0;
+constexpr u8 HOS_VERSION_MAJOR = 20;
+constexpr u8 HOS_VERSION_MINOR = 1;
constexpr u8 HOS_VERSION_MICRO = 1;
// NintendoSDK version constants.
@@ -21,15 +24,15 @@ constexpr u8 SDK_REVISION_MAJOR = 1;
constexpr u8 SDK_REVISION_MINOR = 0;
constexpr char PLATFORM_STRING[] = "NX";
-constexpr char VERSION_HASH[] = "835c78223df116284ef7e36e8441760edc81729c";
-constexpr char DISPLAY_VERSION[] = "19.0.1";
-constexpr char DISPLAY_TITLE[] = "NintendoSDK Firmware for NX 19.0.1-1.0";
+constexpr char VERSION_HASH[] = "9ffad64d79dd150490201461bdf66c8db963f57d";
+constexpr char DISPLAY_VERSION[] = "20.1.1";
+constexpr char DISPLAY_TITLE[] = "NintendoSDK Firmware for NX 20.1.1-1.0";
// Atmosphere version constants.
constexpr u8 ATMOSPHERE_RELEASE_VERSION_MAJOR = 1;
-constexpr u8 ATMOSPHERE_RELEASE_VERSION_MINOR = 0;
-constexpr u8 ATMOSPHERE_RELEASE_VERSION_MICRO = 0;
+constexpr u8 ATMOSPHERE_RELEASE_VERSION_MINOR = 9;
+constexpr u8 ATMOSPHERE_RELEASE_VERSION_MICRO = 1;
constexpr u32 AtmosphereTargetFirmwareWithRevision(u8 major, u8 minor, u8 micro, u8 rev) {
return u32{major} << 24 | u32{minor} << 16 | u32{micro} << 8 | u32{rev};
diff --git a/src/core/hle/kernel/svc_version.h b/src/core/hle/kernel/svc_version.h
index 3eb95aa7b2..b47874dcde 100644
--- a/src/core/hle/kernel/svc_version.h
+++ b/src/core/hle/kernel/svc_version.h
@@ -1,3 +1,6 @@
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -50,8 +53,8 @@ constexpr inline u32 RequiredKernelVersion =
// This is the highest SVC version supported, to be updated on new kernel releases.
// NOTE: Official kernel versions have SVC major = SDK major + 4, SVC minor = SDK minor.
-constexpr inline u32 SupportedKernelMajorVersion = ConvertToSvcMajorVersion(15);
-constexpr inline u32 SupportedKernelMinorVersion = ConvertToSvcMinorVersion(3);
+constexpr inline u32 SupportedKernelMajorVersion = ConvertToSvcMajorVersion(20);
+constexpr inline u32 SupportedKernelMinorVersion = ConvertToSvcMinorVersion(5);
constexpr inline u32 SupportedKernelVersion =
EncodeKernelVersion(SupportedKernelMajorVersion, SupportedKernelMinorVersion);
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index cd3239205e..9002fdd0b9 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -462,6 +462,9 @@ GMainWindow::GMainWindow(bool has_broken_vulkan)
// Gen keys if necessary
OnCheckFirmwareDecryption();
+ // Check firmware
+ OnCheckFirmware();
+
game_list->LoadCompatibilityList();
// force reload on first load to ensure add-ons get updated
game_list->PopulateAsync(UISettings::values.game_dirs, false);
@@ -4326,6 +4329,7 @@ void GMainWindow::OnInstallFirmware() {
progress.close();
OnCheckFirmwareDecryption();
+ OnCheckFirmware();
}
void GMainWindow::OnInstallDecryptionKeys() {
@@ -4404,6 +4408,7 @@ void GMainWindow::OnInstallDecryptionKeys() {
}
OnCheckFirmwareDecryption();
+ OnCheckFirmware();
}
void GMainWindow::OnAbout() {
@@ -5067,6 +5072,34 @@ void GMainWindow::OnCheckFirmwareDecryption() {
UpdateMenuState();
}
+void GMainWindow::OnCheckFirmware()
+{
+ if (!CheckFirmwarePresence()) {
+ QMessageBox::warning(
+ this, tr("Firmware Missing"),
+ tr("Firmware missing. Firmware is required to run certain games and use the Home Menu.\n"
+ "Eden only works with firmware 19.0.1 and earlier."));
+ } else {
+ Service::Set::FirmwareVersionFormat firmware_data{};
+ const auto result = Service::Set::GetFirmwareVersionImpl(
+ firmware_data, *system, Service::Set::GetFirmwareVersionType::Version2);
+
+ if (result.IsError()) {
+ LOG_INFO(Frontend, "Unable to read firmware");
+ QMessageBox::warning(
+ this, tr("Firmware Corrupted"),
+ tr("Firmware reported as present, but was unable to be read. Check for decryption keys and redump firmware if necessary."));
+ return;
+ }
+
+ if (firmware_data.major > 19) {
+ QMessageBox::warning(
+ this, tr("Firmware Too New"),
+ tr("Firmware is too new. Eden only works with firmware 19.0.1 and earlier."));
+ }
+ }
+}
+
bool GMainWindow::CheckFirmwarePresence() {
constexpr u64 MiiEditId = static_cast<u64>(Service::AM::AppletProgramId::MiiEdit);
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 6e1a47f5ac..84588f641a 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -1,3 +1,6 @@
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -414,6 +417,7 @@ private slots:
void OnCreateHomeMenuShortcut(GameListShortcutTarget target);
void OnCaptureScreenshot();
void OnCheckFirmwareDecryption();
+ void OnCheckFirmware();
void OnLanguageChanged(const QString& locale);
void OnMouseActivity();
bool OnShutdownBegin();