[frontend] Firmware setup & requirement (#222)

Currently Android only, will need to be added to desktop.

Android incorrectly records firmware as 19.0.1 if on a higher version...

TODO:
- [x] desktop
- [x] fix android

Signed-off-by: crueter <swurl@swurl.xyz>

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/222
Co-authored-by: crueter <swurl@swurl.xyz>
Co-committed-by: crueter <swurl@swurl.xyz>
This commit is contained in:
crueter 2025-06-27 23:23:25 +00:00 committed by crueter
parent 20bf141faa
commit f121df0aa3
22 changed files with 379 additions and 74 deletions

View file

@ -7,16 +7,9 @@ license_header = <<~EOF
EOF
print 'Getting branch changes...'
puts "\n"
branch_name = `git rev-parse --abbrev-ref HEAD`.chomp
print branch_name
puts "\n"
branch_commits = `git log #{branch_name} --not master --pretty=format:"%h"`.split("\n")
print branch_commits
puts "\n"
branch_commit_range = "#{branch_commits[-1]}^..#{branch_commits[0]}"
print branch_commit_range
puts "\n"
branch_changed_files = `git diff-tree --no-commit-id --name-only #{branch_commit_range} -r`.split("\n")
puts 'done'

85
.ci/license-header.sh Executable file
View file

@ -0,0 +1,85 @@
#!/bin/sh -e
HEADER="$(cat "$PWD/.ci/license/header.txt")"
echo "Getting branch changes"
# I created this cursed POSIX abomination only to discover a better solution
#BRANCH=`git rev-parse --abbrev-ref HEAD`
#COMMITS=`git log ${BRANCH} --not master --pretty=format:"%h"`
#RANGE="${COMMITS[${#COMMITS[@]}-1]}^..${COMMITS[0]}"
#FILES=`git diff-tree --no-commit-id --name-only ${RANGE} -r`
FILES=$(git diff --name-only master)
echo "Done"
for file in $FILES; do
EXTENSION="${file##*.}"
case "$EXTENSION" in
kts|kt|cpp|h)
CONTENT="`cat $file`"
case "$CONTENT" in
"$HEADER"*) ;;
*) BAD_FILES="$BAD_FILES $file" ;;
esac
;;
esac
done
if [ "$BAD_FILES" = "" ]; then
echo
echo "All good."
exit
fi
echo "The following files have incorrect license headers:"
echo
for file in $BAD_FILES; do echo $file; done
cat << EOF
The following license header should be added to the start of all offending files:
=== BEGIN ===
$HEADER
=== END ===
If some of the code in this PR is not being contributed by the original author,
the files which have been exclusively changed by that code can be ignored.
If this happens, this PR requirement can be bypassed once all other files are addressed.
EOF
if [ "$FIX" = "true" ]; then
echo
echo "FIX set to true. Fixing headers."
echo
for file in $BAD_FILES; do
cat $file > $file.bak
cat .ci/license/header.txt > $file
echo >> $file
cat $file.bak >> $file
rm $file.bak
git add $file
done
echo "License headers fixed."
fi
if [ "$COMMIT" = "true" ]; then
echo
echo "COMMIT set to true. Committing changes."
echo
git commit -m "Fix license headers"
echo "Changes committed. You may now push."
fi
exit

2
.ci/license/header.txt Normal file
View file

@ -0,0 +1,2 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later

View file

@ -16,7 +16,7 @@ jobs:
run: git fetch origin master:master
- name: Make script executable
run: chmod +x ./.ci/license-header.rb
run: chmod +x ./.ci/license-header.sh
- name: Check license headers
run: ./.ci/license-header.rb
run: ./.ci/license-header.sh

View file

@ -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 {

View file

@ -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
@ -407,6 +408,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
*

View file

@ -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"),

View file

@ -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(

View file

@ -271,6 +271,8 @@ class SettingsFragmentPresenter(
if (Build.VERSION.SDK_INT >= 31) {
add(BooleanSetting.SHOW_SOC_MODEL.key)
}
add(BooleanSetting.SHOW_FW_VERSION.key)
}
}

View file

@ -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())) {

View file

@ -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
)

View file

@ -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"

View file

@ -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
@ -166,6 +166,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,
@ -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 =

View file

@ -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
}
}

View file

@ -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,6 +357,10 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
return@registerForActivityResult
}
processFirmware(result)
}
fun processFirmware(result: Uri, onComplete: (() -> Unit)? = null) {
val filterNCA = FilenameFilter { _, dirName -> dirName.endsWith(".nca") }
val firmwarePath =
@ -357,6 +391,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
cacheFirmwareDir.copyRecursively(firmwarePath, true)
NativeLibrary.initializeSystem(true)
homeViewModel.setCheckKeys(true)
homeViewModel.setCheckFirmware(true)
getString(R.string.save_file_imported_success)
}
} catch (e: Exception) {
@ -366,8 +401,11 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
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)

View file

@ -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,

View file

@ -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,7 +763,7 @@ 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();
if (!bis_system) {
@ -778,6 +779,31 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isFirmwareAvailable(JNIEnv* env,
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) {

View file

@ -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">Edens 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>

View file

@ -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};

View file

@ -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);

View file

@ -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);

View file

@ -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();