summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorZephyron <zephyron@citron-emu.orgq>2025-02-28 16:15:10 +1000
committerZephyron <zephyron@citron-emu.orgq>2025-02-28 16:15:10 +1000
commit84e5fbc0899bb838ae2c813edee30b32735f4a5f (patch)
tree82b19cd571cf314a50bf132d36821ac15c860e4c /src
parenta442078ee4c257e8c013a6edeec72de2267eb9da (diff)
feat: Make firmware mandatory for title launching
This commit implements a requirement for firmware to be installed before titles can be launched, similar to how keys are required. Changes include: - Add IsFirmwareAvailable method to KeyManager to check for essential keys - Add CheckFirmwarePresence method to verify actual firmware files (NCAs) - Add firmware checks in game loading process for both desktop and Android - Add error messages when firmware is missing - Add strings for firmware-related error messages The implementation checks for both essential keys and the presence of system applets like Mii Edit and Home Menu to ensure proper firmware is installed. Games will not launch if firmware is missing, and users will be shown an appropriate error message.
Diffstat (limited to 'src')
-rw-r--r--src/android/app/src/main/java/org/citron/citron_emu/activities/EmulationActivity.kt14
-rw-r--r--src/android/app/src/main/jni/native_library.cpp31
-rw-r--r--src/android/app/src/main/res/values/strings.xml3
-rw-r--r--src/citron/main.cpp11
-rw-r--r--src/core/crypto/key_manager.cpp27
-rw-r--r--src/core/crypto/key_manager.h3
-rw-r--r--src/core/file_sys/content_manager.cpp25
-rw-r--r--src/core/loader/loader.cpp18
8 files changed, 129 insertions, 3 deletions
diff --git a/src/android/app/src/main/java/org/citron/citron_emu/activities/EmulationActivity.kt b/src/android/app/src/main/java/org/citron/citron_emu/activities/EmulationActivity.kt
index bef01f156..164e85b49 100644
--- a/src/android/app/src/main/java/org/citron/citron_emu/activities/EmulationActivity.kt
+++ b/src/android/app/src/main/java/org/citron/citron_emu/activities/EmulationActivity.kt
@@ -4,6 +4,7 @@
package org.citron.citron_emu.activities
import android.annotation.SuppressLint
+import android.app.AlertDialog
import android.app.PendingIntent
import android.app.PictureInPictureParams
import android.app.RemoteAction
@@ -80,6 +81,19 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
super.onCreate(savedInstanceState)
+ // Check if firmware is available
+ if (!NativeLibrary.isFirmwareAvailable() || !NativeLibrary.checkFirmwarePresence()) {
+ AlertDialog.Builder(this)
+ .setTitle(R.string.firmware_missing_title)
+ .setMessage(R.string.firmware_missing_message)
+ .setPositiveButton(R.string.ok) { _, _ ->
+ finish()
+ }
+ .setCancelable(false)
+ .show()
+ return
+ }
+
// Add license verification at the start
LicenseVerifier.verifyLicense(this)
diff --git a/src/android/app/src/main/jni/native_library.cpp b/src/android/app/src/main/jni/native_library.cpp
new file mode 100644
index 000000000..41152ef41
--- /dev/null
+++ b/src/android/app/src/main/jni/native_library.cpp
@@ -0,0 +1,31 @@
+#include "core/crypto/key_manager.h"
+#include "core/hle/service/am/am.h"
+#include "core/file_sys/registered_cache.h"
+#include "core/file_sys/content_archive.h"
+#include "core/system.h"
+
+extern "C" {
+
+JNIEXPORT jboolean JNICALL Java_org_citron_citron_1emu_NativeLibrary_isFirmwareAvailable(
+ JNIEnv* env, jobject obj) {
+ return Core::Crypto::KeyManager::Instance().IsFirmwareAvailable();
+}
+
+JNIEXPORT jboolean JNICALL Java_org_citron_citron_1emu_NativeLibrary_checkFirmwarePresence(
+ JNIEnv* env, jobject obj) {
+ constexpr u64 MiiEditId = 0x0100000000001009; // Mii Edit applet ID
+ constexpr u64 QLaunchId = 0x0100000000001000; // Home Menu applet ID
+
+ auto& system = Core::System::GetInstance();
+ auto bis_system = system.GetFileSystemController().GetSystemNANDContents();
+ if (!bis_system) {
+ return false;
+ }
+
+ auto mii_applet_nca = bis_system->GetEntry(MiiEditId, FileSys::ContentRecordType::Program);
+ auto qlaunch_nca = bis_system->GetEntry(QLaunchId, FileSys::ContentRecordType::Program);
+
+ return (mii_applet_nca != nullptr && qlaunch_nca != nullptr);
+}
+
+} // extern "C" \ No newline at end of file
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index ce2b21bf1..355384ab4 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -23,6 +23,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_missing_title">Missing Firmware</string>
+ <string name="firmware_missing_message">Firmware is required to launch games.\n\nPlease install firmware by placing your Switch firmware files in the appropriate location.</string>
+ <string name="ok">OK</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>
diff --git a/src/citron/main.cpp b/src/citron/main.cpp
index cb6cedd19..d4ff764a8 100644
--- a/src/citron/main.cpp
+++ b/src/citron/main.cpp
@@ -4787,19 +4787,24 @@ void GMainWindow::OnCheckFirmwareDecryption() {
}
bool GMainWindow::CheckFirmwarePresence() {
- constexpr u64 MiiEditId = static_cast<u64>(Service::AM::AppletProgramId::MiiEdit);
+ constexpr u64 MiiEditId = 0x0100000000001009; // Mii Edit applet ID
+ constexpr u64 QLaunchId = 0x0100000000001000; // Home Menu applet ID
auto bis_system = system->GetFileSystemController().GetSystemNANDContents();
if (!bis_system) {
return false;
}
+ // Check for essential system applets
auto mii_applet_nca = bis_system->GetEntry(MiiEditId, FileSys::ContentRecordType::Program);
- if (!mii_applet_nca) {
+ auto qlaunch_nca = bis_system->GetEntry(QLaunchId, FileSys::ContentRecordType::Program);
+
+ if (!mii_applet_nca || !qlaunch_nca) {
return false;
}
- return true;
+ // Also check for essential keys
+ return Core::Crypto::KeyManager::Instance().IsFirmwareAvailable();
}
void GMainWindow::SetFirmwareVersion() {
diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp
index e61a59fc9..eb5dd8cb1 100644
--- a/src/core/crypto/key_manager.cpp
+++ b/src/core/crypto/key_manager.cpp
@@ -1290,4 +1290,31 @@ bool KeyManager::AddTicket(const Ticket& ticket) {
SetKey(S128KeyType::Titlekey, key.value(), rights_id[1], rights_id[0]);
return true;
}
+
+bool KeyManager::IsFirmwareAvailable() const {
+ // Check for essential keys that would only be present with firmware
+ if (!HasKey(S128KeyType::Master, 0)) {
+ return false;
+ }
+
+ // Check for at least one titlekek
+ bool has_titlekek = false;
+ for (size_t i = 0; i < CURRENT_CRYPTO_REVISION; ++i) {
+ if (HasKey(S128KeyType::Titlekek, i)) {
+ has_titlekek = true;
+ break;
+ }
+ }
+
+ if (!has_titlekek) {
+ return false;
+ }
+
+ // Check for header key
+ if (!HasKey(S256KeyType::Header)) {
+ return false;
+ }
+
+ return true;
+}
} // namespace Core::Crypto
diff --git a/src/core/crypto/key_manager.h b/src/core/crypto/key_manager.h
index 0adf3701f..2a5f0c093 100644
--- a/src/core/crypto/key_manager.h
+++ b/src/core/crypto/key_manager.h
@@ -296,6 +296,9 @@ public:
void ReloadKeys();
bool AreKeysLoaded() const;
+ // Check if firmware is installed by verifying essential keys
+ bool IsFirmwareAvailable() const;
+
private:
KeyManager();
diff --git a/src/core/file_sys/content_manager.cpp b/src/core/file_sys/content_manager.cpp
new file mode 100644
index 000000000..fd53978fc
--- /dev/null
+++ b/src/core/file_sys/content_manager.cpp
@@ -0,0 +1,25 @@
+#include "core/system.h"
+#include "core/file_sys/registered_cache.h"
+#include "core/file_sys/content_archive.h"
+#include "core/crypto/key_manager.h"
+
+bool ContentManager::IsFirmwareAvailable() {
+ constexpr u64 MiiEditId = 0x0100000000001009; // Mii Edit applet ID
+ constexpr u64 QLaunchId = 0x0100000000001000; // Home Menu applet ID
+
+ auto& system = Core::System::GetInstance();
+ auto bis_system = system.GetFileSystemController().GetSystemNANDContents();
+ if (!bis_system) {
+ return false;
+ }
+
+ auto mii_applet_nca = bis_system->GetEntry(MiiEditId, FileSys::ContentRecordType::Program);
+ auto qlaunch_nca = bis_system->GetEntry(QLaunchId, FileSys::ContentRecordType::Program);
+
+ if (!mii_applet_nca || !qlaunch_nca) {
+ return false;
+ }
+
+ // Also check for essential keys
+ return Core::Crypto::KeyManager::Instance().IsFirmwareAvailable();
+} \ No newline at end of file
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp
index b6e355622..0135d6f81 100644
--- a/src/core/loader/loader.cpp
+++ b/src/core/loader/loader.cpp
@@ -19,6 +19,10 @@
#include "core/loader/nso.h"
#include "core/loader/nsp.h"
#include "core/loader/xci.h"
+#include "core/hle/service/am/am.h"
+#include "core/hle/service/filesystem/filesystem.h"
+#include "core/file_sys/registered_cache.h"
+#include "core/file_sys/content_archive.h"
namespace Loader {
@@ -250,6 +254,20 @@ std::unique_ptr<AppLoader> GetLoader(Core::System& system, FileSys::VirtualFile
return nullptr;
}
+ // Check if firmware is available
+ constexpr u64 MiiEditId = 0x0100000000001009; // Mii Edit applet ID
+ auto bis_system = system.GetFileSystemController().GetSystemNANDContents();
+ if (bis_system) {
+ auto mii_applet_nca = bis_system->GetEntry(MiiEditId, FileSys::ContentRecordType::Program);
+ if (!mii_applet_nca) {
+ LOG_ERROR(Loader, "Firmware is required to launch games but is not available");
+ return nullptr;
+ }
+ } else {
+ LOG_ERROR(Loader, "System NAND contents not available");
+ return nullptr;
+ }
+
FileType type = IdentifyFile(file);
const FileType filename_type = GuessFromFilename(file->GetName());