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.
This commit is contained in:
Zephyron 2025-02-28 16:15:10 +10:00
parent a442078ee4
commit 84e5fbc089
8 changed files with 129 additions and 3 deletions

View file

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

View file

@ -296,6 +296,9 @@ public:
void ReloadKeys();
bool AreKeysLoaded() const;
// Check if firmware is installed by verifying essential keys
bool IsFirmwareAvailable() const;
private:
KeyManager();

View file

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

View file

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