diff --git a/common/defaults/loader.ini b/common/defaults/loader.ini index 1049ef9dd..ac56e2b9b 100644 --- a/common/defaults/loader.ini +++ b/common/defaults/loader.ini @@ -1,4 +1,7 @@ -[config] -hbl_tid=010000000000100D -hbl_path=atmosphere/hbl.nsp +[hbl_config] +title_id=010000000000100D +path=atmosphere/hbl.nsp +override_key=!R + +[default_config] override_key=!R \ No newline at end of file diff --git a/stratosphere/loader/source/ldr_content_management.cpp b/stratosphere/loader/source/ldr_content_management.cpp index b5fbbb491..346846714 100644 --- a/stratosphere/loader/source/ldr_content_management.cpp +++ b/stratosphere/loader/source/ldr_content_management.cpp @@ -38,10 +38,26 @@ static bool g_has_initialized_fs_dev = false; /* Default to Key R, hold disables override, HBL at atmosphere/hbl.nsp. */ static bool g_mounted_hbl_nsp = false; static char g_hbl_sd_path[FS_MAX_PATH+1] = "@Sdcard:/atmosphere/hbl.nsp\x00"; -static u64 g_override_key_combination = KEY_R; -static bool g_override_by_default = true; -static u64 g_override_hbl_tid = 0x010000000000100D; -static bool g_override_any_app = false; + +static OverrideKey g_default_override_key = { + .key_combination = KEY_R, + .override_by_default = true +}; + +struct HblOverrideConfig { + OverrideKey override_key; + u64 title_id; + bool override_any_app; +}; + +static HblOverrideConfig g_hbl_override_config = { + .override_key = { + .key_combination = KEY_R, + .override_by_default = true + }, + .title_id = 0x010000000000100D, + .override_any_app = false +}; /* Static buffer for loader.ini contents at runtime. */ static char g_config_ini_data[0x800]; @@ -62,7 +78,7 @@ Result ContentManagement::MountCode(u64 tid, FsStorageId sid) { RefreshConfigurationData(); } - if (ShouldOverrideContents(tid) && R_SUCCEEDED(MountCodeNspOnSd(tid))) { + if (ShouldOverrideContentsWithSD(tid) && R_SUCCEEDED(MountCodeNspOnSd(tid))) { return 0x0; } @@ -204,72 +220,100 @@ void ContentManagement::SetCreatedTitle(u64 tid) { } } +static OverrideKey ParseOverrideKey(const char *value) { + OverrideKey cfg; + + /* Parse on by default. */ + if (value[0] == '!') { + cfg.override_by_default = true; + value++; + } else { + cfg.override_by_default = false; + } + + /* Parse key combination. */ + if (strcasecmp(value, "A") == 0) { + cfg.key_combination = KEY_A; + } else if (strcasecmp(value, "B") == 0) { + cfg.key_combination = KEY_B; + } else if (strcasecmp(value, "X") == 0) { + cfg.key_combination = KEY_X; + } else if (strcasecmp(value, "Y") == 0) { + cfg.key_combination = KEY_Y; + } else if (strcasecmp(value, "LS") == 0) { + cfg.key_combination = KEY_LSTICK; + } else if (strcasecmp(value, "RS") == 0) { + cfg.key_combination = KEY_RSTICK; + } else if (strcasecmp(value, "L") == 0) { + cfg.key_combination = KEY_L; + } else if (strcasecmp(value, "R") == 0) { + cfg.key_combination = KEY_R; + } else if (strcasecmp(value, "ZL") == 0) { + cfg.key_combination = KEY_ZL; + } else if (strcasecmp(value, "ZR") == 0) { + cfg.key_combination = KEY_ZR; + } else if (strcasecmp(value, "PLUS") == 0) { + cfg.key_combination = KEY_PLUS; + } else if (strcasecmp(value, "MINUS") == 0) { + cfg.key_combination = KEY_MINUS; + } else if (strcasecmp(value, "DLEFT") == 0) { + cfg.key_combination = KEY_DLEFT; + } else if (strcasecmp(value, "DUP") == 0) { + cfg.key_combination = KEY_DUP; + } else if (strcasecmp(value, "DRIGHT") == 0) { + cfg.key_combination = KEY_DRIGHT; + } else if (strcasecmp(value, "DDOWN") == 0) { + cfg.key_combination = KEY_DDOWN; + } else if (strcasecmp(value, "SL") == 0) { + cfg.key_combination = KEY_SL; + } else if (strcasecmp(value, "SR") == 0) { + cfg.key_combination = KEY_SR; + } else { + cfg.key_combination = 0; + } + + return cfg; +} + static int LoaderIniHandler(void *user, const char *section, const char *name, const char *value) { /* Taken and modified, with love, from Rajkosto's implementation. */ - if (strcasecmp(section, "config") == 0) { - if (strcasecmp(name, "hbl_tid") == 0) { + if (strcasecmp(section, "hbl_config") == 0) { + if (strcasecmp(name, "title_id") == 0) { if (strcasecmp(value, "app") == 0) { - g_override_any_app = true; + g_hbl_override_config.override_any_app = true; } else { u64 override_tid = strtoul(value, NULL, 16); if (override_tid != 0) { - g_override_hbl_tid = override_tid; + g_hbl_override_config.title_id = override_tid; } } - } else if (strcasecmp(name, "hbl_path") == 0) { + } else if (strcasecmp(name, "path") == 0) { while (*value == '/' || *value == '\\') { value++; } snprintf(g_hbl_sd_path, FS_MAX_PATH, "@Sdcard:/%s", value); g_hbl_sd_path[FS_MAX_PATH] = 0; } else if (strcasecmp(name, "override_key") == 0) { - if (value[0] == '!') { - g_override_by_default = true; - value++; - } else { - g_override_by_default = false; - } - - if (strcasecmp(value, "A") == 0) { - g_override_key_combination = KEY_A; - } else if (strcasecmp(value, "B") == 0) { - g_override_key_combination = KEY_B; - } else if (strcasecmp(value, "X") == 0) { - g_override_key_combination = KEY_X; - } else if (strcasecmp(value, "Y") == 0) { - g_override_key_combination = KEY_Y; - } else if (strcasecmp(value, "LS") == 0) { - g_override_key_combination = KEY_LSTICK; - } else if (strcasecmp(value, "RS") == 0) { - g_override_key_combination = KEY_RSTICK; - } else if (strcasecmp(value, "L") == 0) { - g_override_key_combination = KEY_L; - } else if (strcasecmp(value, "R") == 0) { - g_override_key_combination = KEY_R; - } else if (strcasecmp(value, "ZL") == 0) { - g_override_key_combination = KEY_ZL; - } else if (strcasecmp(value, "ZR") == 0) { - g_override_key_combination = KEY_ZR; - } else if (strcasecmp(value, "PLUS") == 0) { - g_override_key_combination = KEY_PLUS; - } else if (strcasecmp(value, "MINUS") == 0) { - g_override_key_combination = KEY_MINUS; - } else if (strcasecmp(value, "DLEFT") == 0) { - g_override_key_combination = KEY_DLEFT; - } else if (strcasecmp(value, "DUP") == 0) { - g_override_key_combination = KEY_DUP; - } else if (strcasecmp(value, "DRIGHT") == 0) { - g_override_key_combination = KEY_DRIGHT; - } else if (strcasecmp(value, "DDOWN") == 0) { - g_override_key_combination = KEY_DDOWN; - } else if (strcasecmp(value, "SL") == 0) { - g_override_key_combination = KEY_SL; - } else if (strcasecmp(value, "SR") == 0) { - g_override_key_combination = KEY_SR; - } else { - g_override_key_combination = 0; - } + g_hbl_override_config.override_key = ParseOverrideKey(value); + } + } else if (strcasecmp(section, "default_config") == 0) { + if (strcasecmp(name, "override_key") == 0) { + g_default_override_key = ParseOverrideKey(value); + } + } else { + return 0; + } + return 1; +} + +static int LoaderTitleSpecificIniHandler(void *user, const char *section, const char *name, const char *value) { + /* We'll output an override key when relevant. */ + OverrideKey *user_cfg = reinterpret_cast(user); + + if (strcasecmp(section, "override_config") == 0) { + if (strcasecmp(name, "override_key") == 0) { + *user_cfg = ParseOverrideKey(value); } } else { return 0; @@ -309,18 +353,56 @@ void ContentManagement::TryMountSdCard() { } } -bool ContentManagement::ShouldReplaceWithHBL(u64 tid) { - return g_mounted_hbl_nsp && ((g_override_any_app && IsApplicationTid(tid)) || (!g_override_any_app && tid == g_override_hbl_tid)); +static bool IsHBLTitleId(u64 tid) { + return ((g_hbl_override_config.override_any_app && IsApplicationTid(tid)) || (!g_hbl_override_config.override_any_app && tid == g_hbl_override_config.title_id)); } -bool ContentManagement::ShouldOverrideContents(u64 tid) { - if (tid >= 0x0100000000001000 && HasCreatedTitle(0x0100000000001000)) { - u64 kDown = 0; - bool keys_triggered = (R_SUCCEEDED(HidManagement::GetKeysDown(&kDown)) && ((kDown & g_override_key_combination) != 0)); - return g_has_initialized_fs_dev && (g_override_by_default ^ keys_triggered); +OverrideKey ContentManagement::GetTitleOverrideKey(u64 tid) { + OverrideKey cfg = g_default_override_key; + char path[FS_MAX_PATH+1] = {0}; + snprintf(path, FS_MAX_PATH, "sdmc:/atmosphere/titles/%016lx/config.ini", tid); + + + FILE *config = fopen(path, "r"); + if (config != NULL) { + ON_SCOPE_EXIT { fclose(config); }; + + /* Parse current title ini. */ + ini_parse_file(config, LoaderTitleSpecificIniHandler, &cfg); + } + + return cfg; +} + +static bool ShouldOverrideContents(OverrideKey *cfg) { + u64 kDown = 0; + bool keys_triggered = (R_SUCCEEDED(HidManagement::GetKeysDown(&kDown)) && ((kDown & cfg->key_combination) != 0)); + return g_has_initialized_fs_dev && (cfg->override_by_default ^ keys_triggered); +} + +bool ContentManagement::ShouldOverrideContentsWithHBL(u64 tid) { + if (g_mounted_hbl_nsp && tid >= 0x0100000000001000 && HasCreatedTitle(0x0100000000001000)) { + /* Return whether we should override contents with HBL. */ + return IsHBLTitleId(tid) && ShouldOverrideContents(&g_hbl_override_config.override_key); } else { - /* Always redirect before qlaunch. */ - return g_has_initialized_fs_dev; + /* Don't override if we failed to mount HBL or haven't launched qlaunch. */ + return false; + } +} + +bool ContentManagement::ShouldOverrideContentsWithSD(u64 tid) { + if (g_has_initialized_fs_dev) { + if (tid >= 0x0100000000001000 && HasCreatedTitle(0x0100000000001000)) { + /* Check whether we should override with non-HBL. */ + OverrideKey title_cfg = GetTitleOverrideKey(tid); + return ShouldOverrideContents(&title_cfg); + } else { + /* Always redirect before qlaunch. */ + return true; + } + } else { + /* Never redirect before we can do so. */ + return false; } } diff --git a/stratosphere/loader/source/ldr_content_management.hpp b/stratosphere/loader/source/ldr_content_management.hpp index a9974530a..60b01d7e2 100644 --- a/stratosphere/loader/source/ldr_content_management.hpp +++ b/stratosphere/loader/source/ldr_content_management.hpp @@ -19,6 +19,11 @@ #include "ldr_registration.hpp" +struct OverrideKey { + u64 key_combination; + bool override_by_default; +}; + class ContentManagement { public: static Result MountCode(u64 tid, FsStorageId sid); @@ -37,8 +42,9 @@ class ContentManagement { static void RefreshConfigurationData(); static void TryMountSdCard(); - static bool ShouldReplaceWithHBL(u64 tid); - static bool ShouldOverrideContents(u64 tid); + static OverrideKey GetTitleOverrideKey(u64 tid); + static bool ShouldOverrideContentsWithSD(u64 tid); + static bool ShouldOverrideContentsWithHBL(u64 tid); /* SetExternalContentSource extension */ class ExternalContentSource { diff --git a/stratosphere/loader/source/ldr_npdm.cpp b/stratosphere/loader/source/ldr_npdm.cpp index 7eb114cfc..d7ec3a517 100644 --- a/stratosphere/loader/source/ldr_npdm.cpp +++ b/stratosphere/loader/source/ldr_npdm.cpp @@ -63,16 +63,21 @@ FILE *NpdmUtils::OpenNpdm(u64 title_id) { if ((ecs = ContentManagement::GetExternalContentSource(title_id)) != nullptr) { return OpenNpdmFromECS(ecs); } - - if (ContentManagement::ShouldOverrideContents(title_id)) { - if (ContentManagement::ShouldReplaceWithHBL(title_id)) { - return OpenNpdmFromHBL(); - } + + /* First, check HBL. */ + if (ContentManagement::ShouldOverrideContentsWithHBL(title_id)) { + return OpenNpdmFromHBL(); + } + + /* Next, check other override. */ + if (ContentManagement::ShouldOverrideContentsWithSD(title_id)) { FILE *f_out = OpenNpdmFromSdCard(title_id); if (f_out != NULL) { return f_out; } } + + /* Last resort: real exefs. */ return OpenNpdmFromExeFS(); } @@ -193,8 +198,7 @@ Result NpdmUtils::LoadNpdm(u64 tid, NpdmInfo *out) { info->acid->title_id_range_max = tid; info->aci0->title_id = tid; - if (ContentManagement::ShouldOverrideContents(tid) && ContentManagement::ShouldReplaceWithHBL(tid) - && R_SUCCEEDED(LoadNpdmInternal(OpenNpdmFromExeFS(), &g_original_npdm_cache))) { + if (ContentManagement::ShouldOverrideContentsWithHBL(tid) && R_SUCCEEDED(LoadNpdmInternal(OpenNpdmFromExeFS(), &g_original_npdm_cache))) { NpdmInfo *original_info = &g_original_npdm_cache.info; /* Fix pool partition. */ if (kernelAbove500()) { diff --git a/stratosphere/loader/source/ldr_nso.cpp b/stratosphere/loader/source/ldr_nso.cpp index bdc65eb3d..61502a7b2 100644 --- a/stratosphere/loader/source/ldr_nso.cpp +++ b/stratosphere/loader/source/ldr_nso.cpp @@ -71,11 +71,14 @@ FILE *NsoUtils::OpenNso(unsigned int index, u64 title_id) { if ((ecs = ContentManagement::GetExternalContentSource(title_id)) != nullptr) { return OpenNsoFromECS(index, ecs); } + + /* First, check HBL. */ + if (ContentManagement::ShouldOverrideContentsWithHBL(title_id)) { + return OpenNsoFromHBL(index); + } - if (ContentManagement::ShouldOverrideContents(title_id)) { - if (ContentManagement::ShouldReplaceWithHBL(title_id)) { - return OpenNsoFromHBL(index); - } + /* Next, check secondary override. */ + if (ContentManagement::ShouldOverrideContentsWithSD(title_id)) { FILE *f_out = OpenNsoFromSdCard(index, title_id); if (f_out != NULL) { return f_out; @@ -83,6 +86,8 @@ FILE *NsoUtils::OpenNso(unsigned int index, u64 title_id) { return NULL; } } + + /* Finally, default to exefs. */ return OpenNsoFromExeFS(index); }