diff options
| -rw-r--r-- | src/android/app/src/main/java/org/citron/citron_emu/activities/EmulationActivity.kt | 14 | ||||
| -rw-r--r-- | src/android/app/src/main/jni/native_library.cpp | 31 | ||||
| -rw-r--r-- | src/android/app/src/main/res/values/strings.xml | 3 | ||||
| -rw-r--r-- | src/citron/main.cpp | 11 | ||||
| -rw-r--r-- | src/core/crypto/key_manager.cpp | 27 | ||||
| -rw-r--r-- | src/core/crypto/key_manager.h | 3 | ||||
| -rw-r--r-- | src/core/file_sys/content_manager.cpp | 25 | ||||
| -rw-r--r-- | src/core/loader/loader.cpp | 18 | 
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 <b>prod.keys</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 <b>Games</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());  | 
