From d5745cae8f703c326e39893d27e5f37d44037b50 Mon Sep 17 00:00:00 2001 From: PabloMK7 Date: Thu, 13 Mar 2025 20:47:21 +0000 Subject: [PATCH] Add LLE modules for online features option + AM Refactor w/ various improvements/fixes - Added option to enable required LLE modules for online features. - (Android) Fixed bug that would cause FS RenameFile to fail sometimes on Android. - (Android) Moved New 3DS mode and LLE applets to system settings tab on Android. - (Android) Fixed cfg save data related issues (mostly Console ID). - Made AM title scanning asynchronous, which makes game boot way faster on Android on most cases. - Made more AM functions asynchronous, to prevent stutter. - Fixed bug in SOC that could cause the emulator to crash when disconnecting. - Fixed keys not being initialized when processing console unique files. --- .../java/org/citra/citra_emu/NativeLibrary.kt | 12 +- .../features/settings/model/BooleanSetting.kt | 4 +- .../features/settings/model/IntSetting.kt | 4 +- .../features/settings/ui/SettingsActivity.kt | 10 + .../settings/ui/SettingsActivityPresenter.kt | 12 +- .../settings/ui/SettingsFragmentPresenter.kt | 47 +- .../fragments/SystemFilesFragment.kt | 5 + .../citra/citra_emu/utils/DocumentsTree.kt | 9 +- src/android/app/src/main/jni/config.cpp | 3 +- src/android/app/src/main/jni/default_ini.h | 6 +- .../app/src/main/jni/system_save_game.cpp | 18 +- .../app/src/main/res/values/strings.xml | 7 +- src/citra_qt/configuration/config.cpp | 2 + .../configuration/configure_system.cpp | 12 + src/citra_qt/configuration/configure_system.h | 1 + .../configuration/configure_system.ui | 74 +-- src/citra_sdl/config.cpp | 1 + src/common/file_util.cpp | 15 +- src/common/file_util.h | 4 +- src/common/hacks/hack_list.cpp | 46 +- src/common/hacks/hack_list.h | 3 +- src/common/hacks/hack_manager.h | 24 +- src/common/settings.h | 4 +- src/core/core.cpp | 4 +- src/core/hle/service/am/am.cpp | 448 +++++++++++++----- src/core/hle/service/am/am.h | 14 + src/core/hle/service/cfg/cfg.cpp | 4 + src/core/hle/service/service.cpp | 97 ++-- src/core/hle/service/service.h | 3 +- src/core/hle/service/soc/soc_u.cpp | 4 +- src/core/hw/unique_data.cpp | 3 + 31 files changed, 646 insertions(+), 254 deletions(-) diff --git a/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.kt b/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.kt index f176f6b87..c8b3d8fee 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.kt @@ -567,7 +567,11 @@ object NativeLibrary { @JvmStatic fun createDir(directory: String, directoryName: String): Boolean = if (FileUtil.isNativePath(directory)) { - CitraApplication.documentsTree.createDir(directory, directoryName) + try { + CitraApplication.documentsTree.createDir(directory, directoryName) + } catch (e: Exception) { + false + } } else { FileUtil.createDir(directory, directoryName) != null } @@ -641,7 +645,11 @@ object NativeLibrary { @JvmStatic fun renameFile(path: String, destinationFilename: String): Boolean = if (FileUtil.isNativePath(path)) { - CitraApplication.documentsTree.renameFile(path, destinationFilename) + try { + CitraApplication.documentsTree.renameFile(path, destinationFilename) + } catch (e: Exception) { + false + } } else { FileUtil.renameFile(path, destinationFilename) } diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/BooleanSetting.kt b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/BooleanSetting.kt index 793f028f7..25d32b1ee 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/BooleanSetting.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/BooleanSetting.kt @@ -17,7 +17,8 @@ enum class BooleanSetting( INSTANT_DEBUG_LOG("instant_debug_log", Settings.SECTION_DEBUG, false), CUSTOM_LAYOUT("custom_layout",Settings.SECTION_LAYOUT,false), DELAY_START_LLE_MODULES("delay_start_for_lle_modules", Settings.SECTION_DEBUG, true), - DETERMINISTIC_ASYNC_OPERATIONS("deterministic_async_operations", Settings.SECTION_DEBUG, false); + DETERMINISTIC_ASYNC_OPERATIONS("deterministic_async_operations", Settings.SECTION_DEBUG, false), + REQUIRED_ONLINE_LLE_MODULES("enable_required_online_lle_modules", Settings.SECTION_SYSTEM, false); override var boolean: Boolean = defaultValue @@ -41,6 +42,7 @@ enum class BooleanSetting( ASYNC_SHADERS, DELAY_START_LLE_MODULES, DETERMINISTIC_ASYNC_OPERATIONS, + REQUIRED_ONLINE_LLE_MODULES, ) fun from(key: String): BooleanSetting? = diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/IntSetting.kt b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/IntSetting.kt index 0f1ca8d43..ff0ddd7ff 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/IntSetting.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/IntSetting.kt @@ -1,4 +1,4 @@ -// Copyright 2023 Citra Emulator Project +// Copyright Citra Emulator Project / Azahar Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -44,7 +44,7 @@ enum class IntSetting( PORTRAIT_BOTTOM_HEIGHT("custom_portrait_bottom_height",Settings.SECTION_LAYOUT,480), AUDIO_INPUT_TYPE("output_type", Settings.SECTION_AUDIO, 0), NEW_3DS("is_new_3ds", Settings.SECTION_SYSTEM, 1), - LLE_APPLETS("lle_applets", Settings.SECTION_SYSTEM, 0), + LLE_APPLETS("lle_applets", Settings.SECTION_SYSTEM, 1), CPU_CLOCK_SPEED("cpu_clock_percentage", Settings.SECTION_CORE, 100), LINEAR_FILTERING("filter_mode", Settings.SECTION_RENDERER, 1), SHADERS_ACCURATE_MUL("shaders_accurate_mul", Settings.SECTION_RENDERER, 0), diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsActivity.kt b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsActivity.kt index fcd7757a3..070a8f487 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsActivity.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsActivity.kt @@ -110,6 +110,16 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView { presenter.saveState(outState) } + override fun onPause() { + super.onPause() + presenter.onPause() + } + + override fun onResume() { + super.onResume() + presenter.onResume() + } + override fun onStart() { super.onStart() presenter.onStart() diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsActivityPresenter.kt b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsActivityPresenter.kt index e8bb59feb..4eba95d19 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsActivityPresenter.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsActivityPresenter.kt @@ -1,4 +1,4 @@ -// Copyright 2023 Citra Emulator Project +// Copyright Citra Emulator Project / Azahar Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -28,8 +28,15 @@ class SettingsActivityPresenter(private val activityView: SettingsActivityView) } } - fun onStart() { + fun onResume() { SystemSaveGame.load() + } + + fun onPause() { + SystemSaveGame.save() + } + + fun onStart() { prepareDirectoriesIfNeeded() } @@ -56,7 +63,6 @@ class SettingsActivityPresenter(private val activityView: SettingsActivityView) if (finishing && shouldSave) { Log.debug("[SettingsActivity] Settings activity stopping. Saving settings to INI...") settings.saveSettings(activityView) - SystemSaveGame.save() //added to ensure that layout changes take effect as soon as settings window closes NativeLibrary.reloadSettings() NativeLibrary.updateFramebuffer(NativeLibrary.isPortraitMode) diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragmentPresenter.kt b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragmentPresenter.kt index 3dde86f0e..4678924dd 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragmentPresenter.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragmentPresenter.kt @@ -253,6 +253,35 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView) override val valueAsString get() = string override val defaultValue = "AZAHAR" } + add(HeaderSetting(R.string.emulation_settings)) + add( + SwitchSetting( + IntSetting.NEW_3DS, + R.string.new_3ds, + 0, + IntSetting.NEW_3DS.key, + IntSetting.NEW_3DS.defaultValue + ) + ) + add( + SwitchSetting( + IntSetting.LLE_APPLETS, + R.string.lle_applets, + 0, + IntSetting.LLE_APPLETS.key, + IntSetting.LLE_APPLETS.defaultValue + ) + ) + add( + SwitchSetting( + BooleanSetting.REQUIRED_ONLINE_LLE_MODULES, + R.string.enable_required_online_lle_modules, + R.string.enable_required_online_lle_modules_desc, + BooleanSetting.REQUIRED_ONLINE_LLE_MODULES.key, + BooleanSetting.REQUIRED_ONLINE_LLE_MODULES.defaultValue + ) + ) + add(HeaderSetting(R.string.profile_settings)) add( StringInputSetting( usernameSetting, @@ -1285,24 +1314,6 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView) settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_debug)) sl.apply { add(HeaderSetting(R.string.debug_warning)) - add( - SwitchSetting( - IntSetting.NEW_3DS, - R.string.new_3ds, - 0, - IntSetting.NEW_3DS.key, - IntSetting.NEW_3DS.defaultValue - ) - ) - add( - SwitchSetting( - IntSetting.LLE_APPLETS, - R.string.lle_applets, - 0, - IntSetting.LLE_APPLETS.key, - IntSetting.LLE_APPLETS.defaultValue - ) - ) add( SliderSetting( IntSetting.CPU_CLOCK_SPEED, diff --git a/src/android/app/src/main/java/org/citra/citra_emu/fragments/SystemFilesFragment.kt b/src/android/app/src/main/java/org/citra/citra_emu/fragments/SystemFilesFragment.kt index 649273f2c..1f8328a74 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/fragments/SystemFilesFragment.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/fragments/SystemFilesFragment.kt @@ -87,6 +87,11 @@ class SystemFilesFragment : Fragment() { } } + override fun onResume() { + super.onResume() + SystemSaveGame.load() + } + override fun onPause() { super.onPause() SystemSaveGame.save() diff --git a/src/android/app/src/main/java/org/citra/citra_emu/utils/DocumentsTree.kt b/src/android/app/src/main/java/org/citra/citra_emu/utils/DocumentsTree.kt index 4071bd1a9..5314f0171 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/utils/DocumentsTree.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/utils/DocumentsTree.kt @@ -1,4 +1,4 @@ -// Copyright Citra Emulator Project / Lime3DS Emulator Project +// Copyright Citra Emulator Project / Azahar Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -161,8 +161,8 @@ class DocumentsTree { val node = resolvePath(filepath) ?: return false try { val filename = URLDecoder.decode(destinationFilename, FileUtil.DECODE_METHOD) - DocumentsContract.renameDocument(context.contentResolver, node.uri!!, filename) - node.rename(filename) + val newUri = DocumentsContract.renameDocument(context.contentResolver, node.uri!!, filename) + node.rename(filename, newUri) return true } catch (e: Exception) { error("[DocumentsTree]: Cannot rename file, error: " + e.message) @@ -255,10 +255,11 @@ class DocumentsTree { } @Synchronized - fun rename(name: String) { + fun rename(name: String, uri: Uri?) { parent ?: return parent!!.removeChild(this) this.name = name + this.uri = uri parent!!.addChild(this) } diff --git a/src/android/app/src/main/jni/config.cpp b/src/android/app/src/main/jni/config.cpp index 19e422324..3fd3e4f0f 100644 --- a/src/android/app/src/main/jni/config.cpp +++ b/src/android/app/src/main/jni/config.cpp @@ -1,4 +1,4 @@ -// Copyright 2014 Citra Emulator Project +// Copyright Citra Emulator Project / Azahar Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -235,6 +235,7 @@ void Config::ReadValues() { // System ReadSetting("System", Settings::values.is_new_3ds); ReadSetting("System", Settings::values.lle_applets); + ReadSetting("System", Settings::values.enable_required_online_lle_modules); ReadSetting("System", Settings::values.region_value); ReadSetting("System", Settings::values.init_clock); { diff --git a/src/android/app/src/main/jni/default_ini.h b/src/android/app/src/main/jni/default_ini.h index b60dde668..3f9c818de 100644 --- a/src/android/app/src/main/jni/default_ini.h +++ b/src/android/app/src/main/jni/default_ini.h @@ -326,9 +326,13 @@ use_virtual_sd = is_new_3ds = # Whether to use LLE system applets, if installed -# 0 (default): No, 1: Yes +# 0: No, 1 (default): Yes lle_applets = +# Whether to enable LLE modules for online play +# 0 (default): No, 1: Yes +enable_required_online_lle_modules = + # The system region that Citra will use during emulation # -1: Auto-select (default), 0: Japan, 1: USA, 2: Europe, 3: Australia, 4: China, 5: Korea, 6: Taiwan region_value = diff --git a/src/android/app/src/main/jni/system_save_game.cpp b/src/android/app/src/main/jni/system_save_game.cpp index bdf97c8bc..59b8838f6 100644 --- a/src/android/app/src/main/jni/system_save_game.cpp +++ b/src/android/app/src/main/jni/system_save_game.cpp @@ -1,4 +1,4 @@ -// Copyright 2023 Citra Emulator Project +// Copyright Citra Emulator Project / Azahar Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -8,17 +8,22 @@ #include #include "android_common/android_common.h" +static bool changes_pending = false; std::shared_ptr cfg; extern "C" { void Java_org_citra_citra_1emu_utils_SystemSaveGame_save([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jobject obj) { - cfg->UpdateConfigNANDSavegame(); + if (changes_pending) { + changes_pending = false; + cfg->UpdateConfigNANDSavegame(); + } } void Java_org_citra_citra_1emu_utils_SystemSaveGame_load([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jobject obj) { + cfg.reset(); cfg = Service::CFG::GetModule(Core::System::GetInstance()); } @@ -30,6 +35,7 @@ jboolean Java_org_citra_citra_1emu_utils_SystemSaveGame_getIsSystemSetupNeeded( void Java_org_citra_citra_1emu_utils_SystemSaveGame_setSystemSetupNeeded( [[maybe_unused]] JNIEnv* env, [[maybe_unused]] jobject obj, jboolean needed) { cfg->SetSystemSetupNeeded(needed); + changes_pending = true; } jstring Java_org_citra_citra_1emu_utils_SystemSaveGame_getUsername([[maybe_unused]] JNIEnv* env, @@ -41,6 +47,7 @@ void Java_org_citra_citra_1emu_utils_SystemSaveGame_setUsername([[maybe_unused]] [[maybe_unused]] jobject obj, jstring username) { cfg->SetUsername(Common::UTF8ToUTF16(GetJString(env, username))); + changes_pending = true; } jshortArray Java_org_citra_citra_1emu_utils_SystemSaveGame_getBirthday( @@ -57,6 +64,7 @@ void Java_org_citra_citra_1emu_utils_SystemSaveGame_setBirthday([[maybe_unused]] [[maybe_unused]] jobject obj, jshort jmonth, jshort jday) { cfg->SetBirthday(static_cast(jmonth), static_cast(jday)); + changes_pending = true; } jint Java_org_citra_citra_1emu_utils_SystemSaveGame_getSystemLanguage( @@ -68,6 +76,7 @@ void Java_org_citra_citra_1emu_utils_SystemSaveGame_setSystemLanguage([[maybe_un [[maybe_unused]] jobject obj, jint jsystemLanguage) { cfg->SetSystemLanguage(static_cast(jsystemLanguage)); + changes_pending = true; } jint Java_org_citra_citra_1emu_utils_SystemSaveGame_getSoundOutputMode( @@ -79,6 +88,7 @@ void Java_org_citra_citra_1emu_utils_SystemSaveGame_setSoundOutputMode([[maybe_u [[maybe_unused]] jobject obj, jint jmode) { cfg->SetSoundOutputMode(static_cast(jmode)); + changes_pending = true; } jshort Java_org_citra_citra_1emu_utils_SystemSaveGame_getCountryCode([[maybe_unused]] JNIEnv* env, @@ -90,6 +100,7 @@ void Java_org_citra_citra_1emu_utils_SystemSaveGame_setCountryCode([[maybe_unuse [[maybe_unused]] jobject obj, jshort jmode) { cfg->SetCountryCode(static_cast(jmode)); + changes_pending = true; } jint Java_org_citra_citra_1emu_utils_SystemSaveGame_getPlayCoins([[maybe_unused]] JNIEnv* env, @@ -101,6 +112,7 @@ void Java_org_citra_citra_1emu_utils_SystemSaveGame_setPlayCoins([[maybe_unused] [[maybe_unused]] jobject obj, jint jcoins) { Service::PTM::Module::SetPlayCoins(static_cast(jcoins)); + changes_pending = true; } jlong Java_org_citra_citra_1emu_utils_SystemSaveGame_getConsoleId([[maybe_unused]] JNIEnv* env, @@ -112,7 +124,7 @@ void Java_org_citra_citra_1emu_utils_SystemSaveGame_regenerateConsoleId( [[maybe_unused]] JNIEnv* env, [[maybe_unused]] jobject obj) { const auto [random_number, console_id] = cfg->GenerateConsoleUniqueId(); cfg->SetConsoleUniqueId(random_number, console_id); - cfg->UpdateConfigNANDSavegame(); + changes_pending = true; } } // extern "C" diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index eb108e1b6..43fb3999d 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -190,7 +190,7 @@ Number of steps per hour reported by the pedometer. Range from 0 to 65,535. Console ID Regenerate Console ID - This will replace your current virtual 3DS with a new one. Your current virtual 3DS will not be recoverable. This might have unexpected effects inside applications. This might fail if you use an outdated config save. Continue? + This will replace your current virtual 3DS console ID with a new one. Your current virtual 3DS console ID will not be recoverable. This might have unexpected effects inside applications. This might fail if you use an outdated config save. Continue? 3GX Plugin Loader Loads 3GX plugins from the emulated SD card if they are available. Allow Applications to Change Plugin Loader State @@ -778,5 +778,8 @@ Delays the start of the app when LLE modules are enabled. Deterministic Async Operations Makes async operations deterministic for debugging. Enabling this may cause freezes. - + Enable required LLE modules for online features (if installed) + Enables the LLE modules required for online multiplayer, eShop access, etc. + Emulation Settings + Profile Settings diff --git a/src/citra_qt/configuration/config.cpp b/src/citra_qt/configuration/config.cpp index 5e261dca3..22c7811da 100644 --- a/src/citra_qt/configuration/config.cpp +++ b/src/citra_qt/configuration/config.cpp @@ -725,6 +725,7 @@ void QtConfig::ReadSystemValues() { ReadGlobalSetting(Settings::values.is_new_3ds); ReadGlobalSetting(Settings::values.lle_applets); + ReadGlobalSetting(Settings::values.enable_required_online_lle_modules); ReadGlobalSetting(Settings::values.region_value); if (global) { @@ -1248,6 +1249,7 @@ void QtConfig::SaveSystemValues() { WriteGlobalSetting(Settings::values.is_new_3ds); WriteGlobalSetting(Settings::values.lle_applets); + WriteGlobalSetting(Settings::values.enable_required_online_lle_modules); WriteGlobalSetting(Settings::values.region_value); if (global) { diff --git a/src/citra_qt/configuration/configure_system.cpp b/src/citra_qt/configuration/configure_system.cpp index 920e1ce70..005ebe394 100644 --- a/src/citra_qt/configuration/configure_system.cpp +++ b/src/citra_qt/configuration/configure_system.cpp @@ -319,6 +319,8 @@ void ConfigureSystem::SetConfiguration() { ui->toggle_new_3ds->setChecked(Settings::values.is_new_3ds.GetValue()); ui->toggle_lle_applets->setChecked(Settings::values.lle_applets.GetValue()); + ui->enable_required_online_lle_modules->setChecked( + Settings::values.enable_required_online_lle_modules.GetValue()); ui->plugin_loader->setChecked(Settings::values.plugin_loader_enabled.GetValue()); ui->allow_plugin_loader->setChecked(Settings::values.allow_plugin_loader.GetValue()); } @@ -429,6 +431,9 @@ void ConfigureSystem::ApplyConfiguration() { is_new_3ds); ConfigurationShared::ApplyPerGameSetting(&Settings::values.lle_applets, ui->toggle_lle_applets, lle_applets); + ConfigurationShared::ApplyPerGameSetting( + &Settings::values.enable_required_online_lle_modules, + ui->enable_required_online_lle_modules, required_online_lle_modules); Settings::values.init_clock = static_cast(ui->combo_init_clock->currentIndex()); @@ -451,6 +456,8 @@ void ConfigureSystem::ApplyConfiguration() { Settings::values.init_time_offset = time_offset_days + time_offset_time; Settings::values.is_new_3ds = ui->toggle_new_3ds->isChecked(); Settings::values.lle_applets = ui->toggle_lle_applets->isChecked(); + Settings::values.enable_required_online_lle_modules = + ui->enable_required_online_lle_modules->isChecked(); Settings::values.plugin_loader_enabled.SetValue(ui->plugin_loader->isChecked()); Settings::values.allow_plugin_loader.SetValue(ui->allow_plugin_loader->isChecked()); @@ -605,6 +612,8 @@ void ConfigureSystem::SetupPerGameUI() { if (Settings::IsConfiguringGlobal()) { ui->toggle_new_3ds->setEnabled(Settings::values.is_new_3ds.UsingGlobal()); ui->toggle_lle_applets->setEnabled(Settings::values.lle_applets.UsingGlobal()); + ui->enable_required_online_lle_modules->setEnabled( + Settings::values.enable_required_online_lle_modules.UsingGlobal()); return; } @@ -649,4 +658,7 @@ void ConfigureSystem::SetupPerGameUI() { is_new_3ds); ConfigurationShared::SetColoredTristate(ui->toggle_lle_applets, Settings::values.lle_applets, lle_applets); + ConfigurationShared::SetColoredTristate(ui->enable_required_online_lle_modules, + Settings::values.enable_required_online_lle_modules, + required_online_lle_modules); } diff --git a/src/citra_qt/configuration/configure_system.h b/src/citra_qt/configuration/configure_system.h index f4b721527..086b84a22 100644 --- a/src/citra_qt/configuration/configure_system.h +++ b/src/citra_qt/configuration/configure_system.h @@ -63,6 +63,7 @@ private: Core::System& system; ConfigurationShared::CheckState is_new_3ds; ConfigurationShared::CheckState lle_applets; + ConfigurationShared::CheckState required_online_lle_modules; bool enabled = false; std::shared_ptr cfg; diff --git a/src/citra_qt/configuration/configure_system.ui b/src/citra_qt/configuration/configure_system.ui index ff4739245..97e4b4654 100644 --- a/src/citra_qt/configuration/configure_system.ui +++ b/src/citra_qt/configuration/configure_system.ui @@ -78,7 +78,17 @@ - + + + + Enable required LLE modules for online features (if installed) + + + Enables the LLE modules needed for online multiplayer, eShop access, etc. + + + + @@ -91,21 +101,21 @@ - + Username - + Birthday - + @@ -176,14 +186,14 @@ - + Language - + Note: this can be overridden when region setting is auto-select @@ -250,14 +260,14 @@ - + Sound output mode - + @@ -276,24 +286,24 @@ - + Country - + - + Clock - + @@ -307,28 +317,28 @@ - + Startup time - + yyyy-MM-ddTHH:mm:ss - + Offset time - + @@ -352,14 +362,14 @@ - + Initial System Ticks - + @@ -373,14 +383,14 @@ - + Initial System Ticks Override - + @@ -393,21 +403,21 @@ - + Play Coins - + 300 - + <html><head/><body><p>Number of steps per hour reported by the pedometer. Range from 0 to 65,535.</p></body></html> @@ -417,28 +427,28 @@ - + 9999 - + Run System Setup when Home Menu is launched - + Console ID: - + @@ -454,14 +464,14 @@ - + MAC: - + @@ -477,21 +487,21 @@ - + 3GX Plugin Loader: - + Enable 3GX plugin loader - + Allow applications to change plugin loader state diff --git a/src/citra_sdl/config.cpp b/src/citra_sdl/config.cpp index 3db076fab..a55b96eed 100644 --- a/src/citra_sdl/config.cpp +++ b/src/citra_sdl/config.cpp @@ -224,6 +224,7 @@ void SdlConfig::ReadValues() { // System ReadSetting("System", Settings::values.is_new_3ds); ReadSetting("System", Settings::values.lle_applets); + ReadSetting("System", Settings::values.enable_required_online_lle_modules); ReadSetting("System", Settings::values.region_value); ReadSetting("System", Settings::values.init_clock); { diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp index 217ae2db9..1ed74bd51 100644 --- a/src/common/file_util.cpp +++ b/src/common/file_util.cpp @@ -516,11 +516,16 @@ bool ForeachDirectoryEntry(u64* num_entries_out, const std::string& directory, return true; } -u64 ScanDirectoryTree(const std::string& directory, FSTEntry& parent_entry, - unsigned int recursion) { - const auto callback = [recursion, &parent_entry](u64* num_entries_out, - const std::string& directory, - const std::string& virtual_name) -> bool { +u64 ScanDirectoryTree(const std::string& directory, FSTEntry& parent_entry, unsigned int recursion, + std::atomic* stop_flag) { + const auto callback = [recursion, &parent_entry, + stop_flag](u64* num_entries_out, const std::string& directory, + const std::string& virtual_name) -> bool { + // Break early and return error if stop is requested + if (stop_flag && *stop_flag) { + return false; + } + FSTEntry entry; entry.virtualName = virtual_name; entry.physicalName = directory + DIR_SEP + virtual_name; diff --git a/src/common/file_util.h b/src/common/file_util.h index 0a1e8324c..a4881b57b 100644 --- a/src/common/file_util.h +++ b/src/common/file_util.h @@ -8,6 +8,7 @@ #pragma once #include +#include #include #include #include @@ -170,10 +171,11 @@ bool ForeachDirectoryEntry(u64* num_entries_out, const std::string& directory, * @param directory the parent directory to start scanning from * @param parent_entry FSTEntry where the filesystem tree results will be stored. * @param recursion Number of children directories to read before giving up. + * @param stop_flag Optional stop flag, the scan will stop if it becomes true * @return the total number of files/directories found */ u64 ScanDirectoryTree(const std::string& directory, FSTEntry& parent_entry, - unsigned int recursion = 0); + unsigned int recursion = 0, std::atomic* stop_flag = nullptr); /** * Recursively searches through a FSTEntry for files, and stores them. diff --git a/src/common/hacks/hack_list.cpp b/src/common/hacks/hack_list.cpp index be043ed47..eb88bccde 100644 --- a/src/common/hacks/hack_list.cpp +++ b/src/common/hacks/hack_list.cpp @@ -1,4 +1,4 @@ -// Copyright 2024 Azahar Emulator Project +// Copyright Citra Emulator Project / Azahar Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -40,7 +40,7 @@ HackManager hack_manager = { .affected_title_ids = { // The Legend of Zelda: Ocarina of Time 3D - 0x0004000000033400, // JAP + 0x0004000000033400, // JPN 0x0004000000033500, // USA 0x0004000000033600, // EUR 0x000400000008F800, // KOR @@ -49,12 +49,12 @@ HackManager hack_manager = { // Mario & Luigi: Superstar Saga + Bowsers Minions 0x00040000001B8F00, // USA 0x00040000001B9000, // EUR - 0x0004000000194B00, // JAP + 0x0004000000194B00, // JPN // Mario & Luigi: Bowsers Inside Story + Bowser Jrs Journey 0x00040000001D1400, // USA 0x00040000001D1500, // EUR - 0x00040000001CA900, // JAP + 0x00040000001CA900, // JPN }, }}, @@ -70,5 +70,43 @@ HackManager hack_manager = { }, }}, + {HackType::ONLINE_LLE_REQUIRED, + HackEntry{ + .mode = HackAllowMode::FORCE, + .affected_title_ids = + { + // eShop + 0x0004001000020900, // JPN + 0x0004001000021900, // USA + 0x0004001000022900, // EUR + 0x0004001000027900, // KOR + 0x0004001000028900, // TWN + + // System Settings + 0x0004001000020000, // JPN + 0x0004001000021000, // USA + 0x0004001000022000, // EUR + 0x0004001000026000, // CHN + 0x0004001000027000, // KOR + 0x0004001000028000, // TWN + + // Nintendo Network ID Settings + 0x000400100002BF00, // JPN + 0x000400100002C000, // USA + 0x000400100002C100, // EUR + + // System Settings + 0x0004003000008202, // JPN + 0x0004003000008F02, // USA + 0x0004003000009802, // EUR + 0x000400300000A102, // CHN + 0x000400300000A902, // KOR + 0x000400300000B102, // TWN + + // Pretendo Network's Nimbus + 0x000400000D40D200, + }, + }}, + }}; } \ No newline at end of file diff --git a/src/common/hacks/hack_list.h b/src/common/hacks/hack_list.h index 55986a617..1a21a0575 100644 --- a/src/common/hacks/hack_list.h +++ b/src/common/hacks/hack_list.h @@ -1,4 +1,4 @@ -// Copyright 2024 Azahar Emulator Project +// Copyright Citra Emulator Project / Azahar Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -12,6 +12,7 @@ enum class HackType : int { RIGHT_EYE_DISABLE, ACCURATE_MULTIPLICATION, DECRYPTION_AUTHORIZED, + ONLINE_LLE_REQUIRED, }; class UserHackData {}; diff --git a/src/common/hacks/hack_manager.h b/src/common/hacks/hack_manager.h index fba859247..f13d0580f 100644 --- a/src/common/hacks/hack_manager.h +++ b/src/common/hacks/hack_manager.h @@ -1,4 +1,4 @@ -// Copyright 2024 Azahar Emulator Project +// Copyright Citra Emulator Project / Azahar Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -32,6 +32,28 @@ struct HackManager { return (hack != nullptr) ? hack->mode : default_mode; } + /** + * Overrides the provided boolean setting depending on the hack type for the title ID + * If there is no hack, or the hack is set to allow, the setting value is returned + * If the hack disallows, false is returned. + * If the hack forces, true is returned. + */ + bool OverrideBooleanSetting(const HackType& type, u64 title_id, bool setting_value) { + const HackEntry* hack = GetHack(type, title_id); + if (hack == nullptr) + return setting_value; + switch (hack->mode) { + case HackAllowMode::DISALLOW: + return false; + case HackAllowMode::FORCE: + return true; + case HackAllowMode::ALLOW: + default: + break; + } + return setting_value; + } + std::multimap entries; }; diff --git a/src/common/settings.h b/src/common/settings.h index b3351085c..21f10e62e 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -450,8 +450,10 @@ struct Values { Setting use_cpu_jit{true, "use_cpu_jit"}; SwitchableSetting cpu_clock_percentage{100, 5, 400, "cpu_clock_percentage"}; SwitchableSetting is_new_3ds{true, "is_new_3ds"}; - SwitchableSetting lle_applets{false, "lle_applets"}; + SwitchableSetting lle_applets{true, "lle_applets"}; SwitchableSetting deterministic_async_operations{false, "deterministic_async_operations"}; + SwitchableSetting enable_required_online_lle_modules{ + false, "enable_required_online_lle_modules"}; // Data Storage Setting use_virtual_sd{true, "use_virtual_sd"}; diff --git a/src/core/core.cpp b/src/core/core.cpp index 31579acac..b017dc413 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -508,8 +508,10 @@ System::ResultStatus System::Init(Frontend::EmuWindow& emu_window, service_manager = std::make_unique(*this); archive_manager = std::make_unique(*this); + u64 loading_title_id = 0; + app_loader->ReadProgramId(loading_title_id); HW::AES::InitKeys(); - Service::Init(*this, lle_modules, !app_loader->DoingInitialSetup()); + Service::Init(*this, loading_title_id, lle_modules, !app_loader->DoingInitialSetup()); GDBStub::DeferStart(); if (!registered_image_interface) { diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 5a3098f8d..f9e31373d 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -861,11 +861,6 @@ Result TicketFile::Commit() { ticket_id = ticket.GetTicketID(); const auto ticket_path = GetTicketPath(ticket.GetTitleID(), ticket.GetTicketID()); - // Create ticket folder if it does not exist - std::string ticket_folder; - Common::SplitPath(ticket_path, &ticket_folder, nullptr, nullptr); - FileUtil::CreateFullPath(ticket_folder); - // Save ticket if (ticket.Save(ticket_path) != Loader::ResultStatus::Success) { LOG_ERROR(Service_AM, "Failed to install ticket provided to TicketFile."); @@ -1182,6 +1177,17 @@ std::string GetMediaTitlePath(Service::FS::MediaType media_type) { } void Module::ScanForTickets() { + if (Settings::values.deterministic_async_operations) { + ScanForTicketsImpl(); + } else { + scan_tickets_future = std::async([this]() { + std::scoped_lock lock(am_lists_mutex); + ScanForTicketsImpl(); + }); + } +} + +void Module::ScanForTicketsImpl() { am_ticket_list.clear(); LOG_DEBUG(Service_AM, "Starting ticket scan"); @@ -1189,8 +1195,10 @@ void Module::ScanForTickets() { std::string ticket_path = GetTicketDirectory(); FileUtil::FSTEntry entries; - FileUtil::ScanDirectoryTree(ticket_path, entries, 0); + FileUtil::ScanDirectoryTree(ticket_path, entries, 0, &stop_scan_flag); for (const FileUtil::FSTEntry& ticket : entries.children) { + if (stop_scan_flag) + break; if (ticket.virtualName.ends_with(".tik")) { std::string file_name = ticket.virtualName.substr(0, ticket.virtualName.size() - 4); auto pos = file_name.find('.'); @@ -1210,6 +1218,17 @@ void Module::ScanForTickets() { } void Module::ScanForTitles(Service::FS::MediaType media_type) { + if (Settings::values.deterministic_async_operations) { + ScanForTitlesImpl(media_type); + } else { + scan_titles_future = std::async([this, media_type]() { + std::scoped_lock lock(am_lists_mutex); + ScanForTitlesImpl(media_type); + }); + } +} + +void Module::ScanForTitlesImpl(Service::FS::MediaType media_type) { am_title_list[static_cast(media_type)].clear(); LOG_DEBUG(Service_AM, "Starting title scan for media_type={}", static_cast(media_type)); @@ -1217,9 +1236,15 @@ void Module::ScanForTitles(Service::FS::MediaType media_type) { std::string title_path = GetMediaTitlePath(media_type); FileUtil::FSTEntry entries; - FileUtil::ScanDirectoryTree(title_path, entries, 1); + FileUtil::ScanDirectoryTree(title_path, entries, 1, &stop_scan_flag); for (const FileUtil::FSTEntry& tid_high : entries.children) { + if (stop_scan_flag) { + break; + } for (const FileUtil::FSTEntry& tid_low : tid_high.children) { + if (stop_scan_flag) { + break; + } std::string tid_string = tid_high.virtualName + tid_low.virtualName; if (tid_string.length() == TITLE_ID_VALID_LENGTH) { @@ -1245,9 +1270,22 @@ void Module::ScanForTitles(Service::FS::MediaType media_type) { } void Module::ScanForAllTitles() { - ScanForTickets(); - ScanForTitles(Service::FS::MediaType::NAND); - ScanForTitles(Service::FS::MediaType::SDMC); + if (Settings::values.deterministic_async_operations) { + ScanForTicketsImpl(); + ScanForTitlesImpl(Service::FS::MediaType::NAND); + ScanForTitlesImpl(Service::FS::MediaType::SDMC); + } else { + scan_all_future = std::async([this]() { + std::scoped_lock lock(am_lists_mutex); + ScanForTicketsImpl(); + if (!stop_scan_flag) { + ScanForTitlesImpl(Service::FS::MediaType::NAND); + } + if (!stop_scan_flag) { + ScanForTitlesImpl(Service::FS::MediaType::SDMC); + } + }); + } } Module::Interface::Interface(std::shared_ptr am, const char* name, u32 max_session) @@ -1307,7 +1345,7 @@ void Module::Interface::GetNumPrograms(Kernel::HLERequestContext& ctx) { }, true); } else { - + std::scoped_lock lock(am->am_lists_mutex); IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); rb.Push(ResultSuccess); rb.Push(static_cast(am->am_title_list[media_type].size())); @@ -1648,6 +1686,7 @@ void Module::Interface::GetProgramList(Kernel::HLERequestContext& ctx) { return; } + std::scoped_lock lock(am->am_lists_mutex); u32 media_count = static_cast(am->am_title_list[media_type].size()); u32 copied = std::min(media_count, count); @@ -1660,8 +1699,8 @@ void Module::Interface::GetProgramList(Kernel::HLERequestContext& ctx) { } Result GetTitleInfoFromList(std::span title_id_list, Service::FS::MediaType media_type, - Kernel::MappedBuffer& title_info_out) { - std::size_t write_offset = 0; + std::vector& title_info_out) { + title_info_out.reserve(title_id_list.size()); for (u32 i = 0; i < title_id_list.size(); i++) { std::string tmd_path = GetTitleMetadataPath(media_type, title_id_list[i]); @@ -1682,8 +1721,7 @@ Result GetTitleInfoFromList(std::span title_id_list, Service::FS::Med } LOG_DEBUG(Service_AM, "found title_id={:016X} version={:04X}", title_id_list[i], title_info.version); - title_info_out.Write(&title_info, write_offset, sizeof(TitleInfo)); - write_offset += sizeof(TitleInfo); + title_info_out.push_back(title_info); } return ResultSuccess; @@ -1773,41 +1811,64 @@ void Module::Interface::GetProgramInfosImpl(Kernel::HLERequestContext& ctx, bool true); } else { - auto& title_id_list_buffer = rp.PopMappedBuffer(); - auto& title_info_out = rp.PopMappedBuffer(); + struct AsyncData { + Service::FS::MediaType media_type; + std::vector title_id_list; - std::vector title_id_list(title_count); - title_id_list_buffer.Read(title_id_list.data(), 0, title_count * sizeof(u64)); + Result res{0}; + std::vector out; + Kernel::MappedBuffer* title_id_list_buffer; + Kernel::MappedBuffer* title_info_out; + }; + auto async_data = std::make_shared(); + async_data->media_type = media_type; + async_data->title_id_list.resize(title_count); + async_data->title_id_list_buffer = &rp.PopMappedBuffer(); + async_data->title_id_list_buffer->Read(async_data->title_id_list.data(), 0, + title_count * sizeof(u64)); + async_data->title_info_out = &rp.PopMappedBuffer(); - // nim checks if the current importing title already exists during installation. - // Normally, since the program wouldn't be commited, getting the title info returns not - // found. However, since GetTitleInfoFromList does not care if the program was commited and - // only checks for the tmd, it will detect the title and return information while it - // shouldn't. To prevent this, we check if the importing context is present and not - // committed. If that's the case, return not found - Result result = ResultSuccess; - for (auto tid : title_id_list) { - for (auto& import_ctx : am->import_title_contexts) { - if (import_ctx.first == tid && - (import_ctx.second.state == ImportTitleContextState::WAITING_FOR_IMPORT || - import_ctx.second.state == ImportTitleContextState::WAITING_FOR_COMMIT || - import_ctx.second.state == ImportTitleContextState::RESUMABLE)) { - LOG_DEBUG(Service_AM, "title pending commit title_id={:016X}", tid); - result = Result(ErrorDescription::NotFound, ErrorModule::AM, - ErrorSummary::InvalidState, ErrorLevel::Permanent); + ctx.RunAsync( + [this, async_data](Kernel::HLERequestContext& ctx) { + // nim checks if the current importing title already exists during installation. + // Normally, since the program wouldn't be commited, getting the title info returns + // not found. However, since GetTitleInfoFromList does not care if the program was + // commited and only checks for the tmd, it will detect the title and return + // information while it shouldn't. To prevent this, we check if the importing + // context is present and not committed. If that's the case, return not found + for (auto tid : async_data->title_id_list) { + for (auto& import_ctx : am->import_title_contexts) { + if (import_ctx.first == tid && + (import_ctx.second.state == + ImportTitleContextState::WAITING_FOR_IMPORT || + import_ctx.second.state == + ImportTitleContextState::WAITING_FOR_COMMIT || + import_ctx.second.state == ImportTitleContextState::RESUMABLE)) { + LOG_DEBUG(Service_AM, "title pending commit title_id={:016X}", tid); + async_data->res = + Result(ErrorDescription::NotFound, ErrorModule::AM, + ErrorSummary::InvalidState, ErrorLevel::Permanent); + } + } } - } - } - if (result.IsSuccess()) - result = GetTitleInfoFromList(title_id_list, media_type, title_info_out); - - IPC::RequestBuilder rb = rp.MakeBuilder(1, ignore_platform ? 0 : 4); - rb.Push(result); - if (!ignore_platform) { - rb.PushMappedBuffer(title_id_list_buffer); - rb.PushMappedBuffer(title_info_out); - } + if (async_data->res.IsSuccess()) { + async_data->res = GetTitleInfoFromList(async_data->title_id_list, + async_data->media_type, async_data->out); + } + return 0; + }, + [async_data](Kernel::HLERequestContext& ctx) { + if (async_data->res.IsSuccess()) { + async_data->title_info_out->Write(async_data->out.data(), 0, + async_data->out.size() * sizeof(TitleInfo)); + } + IPC::RequestBuilder rb(ctx, 1, 4); + rb.Push(async_data->res); + rb.PushMappedBuffer(*async_data->title_id_list_buffer); + rb.PushMappedBuffer(*async_data->title_info_out); + }, + true); } } @@ -1954,32 +2015,74 @@ void Module::Interface::GetDLCTitleInfos(Kernel::HLERequestContext& ctx) { }, true); } else { - auto& title_id_list_buffer = rp.PopMappedBuffer(); - auto& title_info_out = rp.PopMappedBuffer(); + struct AsyncData { + Service::FS::MediaType media_type; + std::vector title_id_list; - std::vector title_id_list(title_count); - title_id_list_buffer.Read(title_id_list.data(), 0, title_count * sizeof(u64)); + Result res{0}; + std::vector out; + Kernel::MappedBuffer* title_id_list_buffer; + Kernel::MappedBuffer* title_info_out; + }; + auto async_data = std::make_shared(); + async_data->media_type = media_type; + async_data->title_id_list.resize(title_count); + async_data->title_id_list_buffer = &rp.PopMappedBuffer(); + async_data->title_id_list_buffer->Read(async_data->title_id_list.data(), 0, + title_count * sizeof(u64)); + async_data->title_info_out = &rp.PopMappedBuffer(); - Result result = ResultSuccess; + ctx.RunAsync( + [this, async_data](Kernel::HLERequestContext& ctx) { + // Validate that DLC TIDs were passed in + for (u32 i = 0; i < async_data->title_id_list.size(); i++) { + u32 tid_high = static_cast(async_data->title_id_list[i] >> 32); + if (tid_high != TID_HIGH_DLC) { + async_data->res = Result(ErrCodes::InvalidTIDInList, ErrorModule::AM, + ErrorSummary::InvalidArgument, ErrorLevel::Usage); + break; + } + } - // Validate that DLC TIDs were passed in - for (u32 i = 0; i < title_count; i++) { - u32 tid_high = static_cast(title_id_list[i] >> 32); - if (tid_high != TID_HIGH_DLC) { - result = Result(ErrCodes::InvalidTIDInList, ErrorModule::AM, - ErrorSummary::InvalidArgument, ErrorLevel::Usage); - break; - } - } + // nim checks if the current importing title already exists during installation. + // Normally, since the program wouldn't be commited, getting the title info returns + // not found. However, since GetTitleInfoFromList does not care if the program was + // commited and only checks for the tmd, it will detect the title and return + // information while it shouldn't. To prevent this, we check if the importing + // context is present and not committed. If that's the case, return not found + for (auto tid : async_data->title_id_list) { + for (auto& import_ctx : am->import_title_contexts) { + if (import_ctx.first == tid && + (import_ctx.second.state == + ImportTitleContextState::WAITING_FOR_IMPORT || + import_ctx.second.state == + ImportTitleContextState::WAITING_FOR_COMMIT || + import_ctx.second.state == ImportTitleContextState::RESUMABLE)) { + LOG_DEBUG(Service_AM, "title pending commit title_id={:016X}", tid); + async_data->res = + Result(ErrorDescription::NotFound, ErrorModule::AM, + ErrorSummary::InvalidState, ErrorLevel::Permanent); + } + } + } - if (result.IsSuccess()) { - result = GetTitleInfoFromList(title_id_list, media_type, title_info_out); - } - - IPC::RequestBuilder rb = rp.MakeBuilder(1, 4); - rb.Push(result); - rb.PushMappedBuffer(title_id_list_buffer); - rb.PushMappedBuffer(title_info_out); + if (async_data->res.IsSuccess()) { + async_data->res = GetTitleInfoFromList(async_data->title_id_list, + async_data->media_type, async_data->out); + } + return 0; + }, + [async_data](Kernel::HLERequestContext& ctx) { + if (async_data->res.IsSuccess()) { + async_data->title_info_out->Write(async_data->out.data(), 0, + async_data->out.size() * sizeof(TitleInfo)); + } + IPC::RequestBuilder rb(ctx, 1, 4); + rb.Push(async_data->res); + rb.PushMappedBuffer(*async_data->title_id_list_buffer); + rb.PushMappedBuffer(*async_data->title_info_out); + }, + true); } } @@ -2059,32 +2162,74 @@ void Module::Interface::GetPatchTitleInfos(Kernel::HLERequestContext& ctx) { }, true); } else { - auto& title_id_list_buffer = rp.PopMappedBuffer(); - auto& title_info_out = rp.PopMappedBuffer(); + struct AsyncData { + Service::FS::MediaType media_type; + std::vector title_id_list; - std::vector title_id_list(title_count); - title_id_list_buffer.Read(title_id_list.data(), 0, title_count * sizeof(u64)); + Result res{0}; + std::vector out; + Kernel::MappedBuffer* title_id_list_buffer; + Kernel::MappedBuffer* title_info_out; + }; + auto async_data = std::make_shared(); + async_data->media_type = media_type; + async_data->title_id_list.resize(title_count); + async_data->title_id_list_buffer = &rp.PopMappedBuffer(); + async_data->title_id_list_buffer->Read(async_data->title_id_list.data(), 0, + title_count * sizeof(u64)); + async_data->title_info_out = &rp.PopMappedBuffer(); - Result result = ResultSuccess; + ctx.RunAsync( + [this, async_data](Kernel::HLERequestContext& ctx) { + // Validate that update TIDs were passed in + for (u32 i = 0; i < async_data->title_id_list.size(); i++) { + u32 tid_high = static_cast(async_data->title_id_list[i] >> 32); + if (tid_high != TID_HIGH_UPDATE) { + async_data->res = Result(ErrCodes::InvalidTIDInList, ErrorModule::AM, + ErrorSummary::InvalidArgument, ErrorLevel::Usage); + break; + } + } - // Validate that update TIDs were passed in - for (u32 i = 0; i < title_count; i++) { - u32 tid_high = static_cast(title_id_list[i] >> 32); - if (tid_high != TID_HIGH_UPDATE) { - result = Result(ErrCodes::InvalidTIDInList, ErrorModule::AM, - ErrorSummary::InvalidArgument, ErrorLevel::Usage); - break; - } - } + // nim checks if the current importing title already exists during installation. + // Normally, since the program wouldn't be commited, getting the title info returns + // not found. However, since GetTitleInfoFromList does not care if the program was + // commited and only checks for the tmd, it will detect the title and return + // information while it shouldn't. To prevent this, we check if the importing + // context is present and not committed. If that's the case, return not found + for (auto tid : async_data->title_id_list) { + for (auto& import_ctx : am->import_title_contexts) { + if (import_ctx.first == tid && + (import_ctx.second.state == + ImportTitleContextState::WAITING_FOR_IMPORT || + import_ctx.second.state == + ImportTitleContextState::WAITING_FOR_COMMIT || + import_ctx.second.state == ImportTitleContextState::RESUMABLE)) { + LOG_DEBUG(Service_AM, "title pending commit title_id={:016X}", tid); + async_data->res = + Result(ErrorDescription::NotFound, ErrorModule::AM, + ErrorSummary::InvalidState, ErrorLevel::Permanent); + } + } + } - if (result.IsSuccess()) { - result = GetTitleInfoFromList(title_id_list, media_type, title_info_out); - } - - IPC::RequestBuilder rb = rp.MakeBuilder(1, 4); - rb.Push(result); - rb.PushMappedBuffer(title_id_list_buffer); - rb.PushMappedBuffer(title_info_out); + if (async_data->res.IsSuccess()) { + async_data->res = GetTitleInfoFromList(async_data->title_id_list, + async_data->media_type, async_data->out); + } + return 0; + }, + [async_data](Kernel::HLERequestContext& ctx) { + if (async_data->res.IsSuccess()) { + async_data->title_info_out->Write(async_data->out.data(), 0, + async_data->out.size() * sizeof(TitleInfo)); + } + IPC::RequestBuilder rb(ctx, 1, 4); + rb.Push(async_data->res); + rb.PushMappedBuffer(*async_data->title_id_list_buffer); + rb.PushMappedBuffer(*async_data->title_info_out); + }, + true); } } @@ -2266,6 +2411,7 @@ void Module::Interface::DeleteTicket(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_AM, "title_id={:016X}", title_id); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + std::scoped_lock lock(am->am_lists_mutex); auto range = am->am_ticket_list.equal_range(title_id); if (range.first == range.second) { rb.Push(Result(ErrorDescription::AlreadyDone, ErrorModule::AM, ErrorSummary::Success, @@ -2288,6 +2434,7 @@ void Module::Interface::GetNumTickets(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_AM, ""); + std::scoped_lock lock(am->am_lists_mutex); u32 ticket_count = static_cast(am->am_ticket_list.size()); IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); @@ -2304,6 +2451,7 @@ void Module::Interface::GetTicketList(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_AM, "ticket_list_count={}, ticket_index={}", ticket_list_count, ticket_index); u32 tickets_written = 0; + std::scoped_lock lock(am->am_lists_mutex); auto it = am->am_ticket_list.begin(); std::advance(it, std::min(static_cast(ticket_index), am->am_ticket_list.size())); @@ -2631,6 +2779,7 @@ void Module::Interface::GetPersonalizedTicketInfoList(Kernel::HLERequestContext& LOG_DEBUG(Service_AM, "(STUBBED) called, ticket_count={}", ticket_count); u32 written = 0; + std::scoped_lock lock(am->am_lists_mutex); for (auto it = am->am_ticket_list.begin(); it != am->am_ticket_list.end() && written < ticket_count; it++) { u64 title_id = it->first; @@ -3281,18 +3430,38 @@ void Module::Interface::EndImportTicket(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx); const auto ticket = rp.PopObject(); - IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); auto ticket_file = GetFileBackendFromSession(ticket); if (ticket_file.Succeeded()) { - rb.Push(ticket_file.Unwrap()->Commit()); - am->am_ticket_list.insert(std::make_pair(ticket_file.Unwrap()->GetTitleID(), - ticket_file.Unwrap()->GetTicketID())); + struct AsyncData { + Service::AM::TicketFile* ticket_file; + + Result res{0}; + }; + std::shared_ptr async_data = std::make_shared(); + async_data->ticket_file = ticket_file.Unwrap(); + + ctx.RunAsync( + [this, async_data](Kernel::HLERequestContext& ctx) { + async_data->res = async_data->ticket_file->Commit(); + + std::scoped_lock lock(am->am_lists_mutex); + am->am_ticket_list.insert(std::make_pair(async_data->ticket_file->GetTitleID(), + async_data->ticket_file->GetTicketID())); + + LOG_DEBUG(Service_AM, "EndImportTicket: title_id={:016X} ticket_id={:016X}", + async_data->ticket_file->GetTitleID(), + async_data->ticket_file->GetTicketID()); + return 0; + }, + [async_data](Kernel::HLERequestContext& ctx) { + IPC::RequestBuilder rb(ctx, 1, 0); + rb.Push(async_data->res); + }, + true); } else { + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(ticket_file.Code()); } - - LOG_DEBUG(Service_AM, "title_id={:016X} ticket_id={:016X}", ticket_file.Unwrap()->GetTitleID(), - ticket_file.Unwrap()->GetTicketID()); } void Module::Interface::BeginImportTitle(Kernel::HLERequestContext& ctx) { @@ -3308,6 +3477,7 @@ void Module::Interface::BeginImportTitle(Kernel::HLERequestContext& ctx) { am->importing_title = std::make_shared(Core::System::GetInstance(), title_id, media_type); + std::scoped_lock lock(am->am_lists_mutex); auto entries = am->am_ticket_list.find(title_id); if (entries == am->am_ticket_list.end()) { // Ticket is not installed @@ -3461,9 +3631,8 @@ void Module::Interface::EndImportTmd(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_AM, ""); - IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - if (!am->importing_title) { + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(Result(ErrCodes::InvalidImportState, ErrorModule::AM, ErrorSummary::InvalidState, ErrorLevel::Permanent)); return; @@ -3471,33 +3640,55 @@ void Module::Interface::EndImportTmd(Kernel::HLERequestContext& ctx) { auto tmd_file = GetFileBackendFromSession(tmd); if (tmd_file.Succeeded()) { - rb.Push(tmd_file.Unwrap()->Commit()); + struct AsyncData { + Service::AM::TMDFile* tmd_file; + bool create_context; + + Result res{0}; + }; + std::shared_ptr async_data = std::make_shared(); + async_data->tmd_file = tmd_file.Unwrap(); + async_data->create_context = create_context; + + ctx.RunAsync( + [async_data](Kernel::HLERequestContext& ctx) { + async_data->res = async_data->tmd_file->Commit(); + return 0; + }, + [this, async_data](Kernel::HLERequestContext& ctx) { + IPC::RequestBuilder rb(ctx, 1, 0); + rb.Push(async_data->res); + + if (async_data->create_context) { + const FileSys::TitleMetadata& tmd_info = am->importing_title->cia_file.GetTMD(); + + ImportTitleContext& context = am->import_title_contexts[tmd_info.GetTitleID()]; + context.title_id = tmd_info.GetTitleID(); + context.version = tmd_info.GetTitleVersion(); + context.type = 0; + context.state = ImportTitleContextState::WAITING_FOR_IMPORT; + context.size = 0; + for (size_t i = 0; i < tmd_info.GetContentCount(); i++) { + ImportContentContext content_context; + content_context.content_id = tmd_info.GetContentIDByIndex(i); + content_context.index = static_cast(i); + content_context.state = ImportTitleContextState::WAITING_FOR_IMPORT; + content_context.size = tmd_info.GetContentSizeByIndex(i); + content_context.current_size = 0; + am->import_content_contexts.insert( + std::make_pair(context.title_id, content_context)); + + context.size += content_context.size; + } + } + }, + true); + } else { + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(tmd_file.Code()); return; } - - if (create_context) { - const FileSys::TitleMetadata& tmd_info = am->importing_title->cia_file.GetTMD(); - - ImportTitleContext& context = am->import_title_contexts[tmd_info.GetTitleID()]; - context.title_id = tmd_info.GetTitleID(); - context.version = tmd_info.GetTitleVersion(); - context.type = 0; - context.state = ImportTitleContextState::WAITING_FOR_IMPORT; - context.size = 0; - for (size_t i = 0; i < tmd_info.GetContentCount(); i++) { - ImportContentContext content_context; - content_context.content_id = tmd_info.GetContentIDByIndex(i); - content_context.index = static_cast(i); - content_context.state = ImportTitleContextState::WAITING_FOR_IMPORT; - content_context.size = tmd_info.GetContentSizeByIndex(i); - content_context.current_size = 0; - am->import_content_contexts.insert(std::make_pair(context.title_id, content_context)); - - context.size += content_context.size; - } - } } void Module::Interface::CreateImportContentContexts(Kernel::HLERequestContext& ctx) { @@ -3770,11 +3961,11 @@ void Module::Interface::Sign(Kernel::HLERequestContext& ctx) { template void Module::serialize(Archive& ar, const unsigned int) { + std::scoped_lock lock(am_lists_mutex); DEBUG_SERIALIZATION_POINT; ar & cia_installing; ar & force_old_device_id; ar & force_new_device_id; - ar & am_title_list; ar & system_updater_mutex; } SERIALIZE_IMPL(Module) @@ -3815,6 +4006,7 @@ void Module::Interface::DeleteTicketId(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_AM, "title_id={:016X} ticket_id={}", title_id, ticket_id); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + std::scoped_lock lock(am->am_lists_mutex); auto range = am->am_ticket_list.equal_range(title_id); auto it = range.first; for (; it != range.second; it++) { @@ -3842,6 +4034,7 @@ void Module::Interface::GetNumTicketIds(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_AM, "title_id={:016X}", title_id); + std::scoped_lock lock(am->am_lists_mutex); auto range = am->am_ticket_list.equal_range(title_id); u32 count = static_cast(std::distance(range.first, range.second)); @@ -3861,6 +4054,7 @@ void Module::Interface::GetTicketIdList(Kernel::HLERequestContext& ctx) { auto out_buf = rp.PopMappedBuffer(); u32 index = 0; + std::scoped_lock lock(am->am_lists_mutex); for (auto [it, rangeEnd] = am->am_ticket_list.equal_range(title_id); it != rangeEnd && index < list_count; index++, it++) { u64 ticket_id = it->second; @@ -3878,6 +4072,7 @@ void Module::Interface::GetNumTicketsOfProgram(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_AM, "title_id={:016X}", title_id); + std::scoped_lock lock(am->am_lists_mutex); auto range = am->am_ticket_list.equal_range(title_id); u32 count = static_cast(std::distance(range.first, range.second)); @@ -3895,6 +4090,7 @@ void Module::Interface::ListTicketInfos(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_AM, "(STUBBED) called, ticket_count={}", ticket_count); + std::scoped_lock lock(am->am_lists_mutex); auto range = am->am_ticket_list.equal_range(title_id); auto it = range.first; std::advance(it, std::min(static_cast(skip), @@ -3943,6 +4139,7 @@ void Module::Interface::ExportTicketWrapped(Kernel::HLERequestContext& ctx) { return; } + std::scoped_lock lock(am->am_lists_mutex); auto range = am->am_ticket_list.equal_range(title_id); auto it = range.first; for (; it != range.second; it++) @@ -4006,11 +4203,14 @@ void Module::Interface::ExportTicketWrapped(Kernel::HLERequestContext& ctx) { } Module::Module(Core::System& _system) : system(_system) { + FileUtil::CreateFullPath(GetTicketDirectory()); ScanForAllTitles(); system_updater_mutex = system.Kernel().CreateMutex(false, "AM::SystemUpdaterMutex"); } -Module::~Module() = default; +Module::~Module() { + stop_scan_flag = true; +} std::shared_ptr GetModule(Core::System& system) { auto am = system.ServiceManager().GetService("am:u"); diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index e3fcbacf0..afac4dae5 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -5,8 +5,11 @@ #pragma once #include +#include #include +#include #include +#include #include #include #include @@ -1022,12 +1025,16 @@ public: private: void ScanForTickets(); + void ScanForTicketsImpl(); + /** * Scans the for titles in a storage medium for listing. * @param media_type the storage medium to scan */ void ScanForTitles(Service::FS::MediaType media_type); + void ScanForTitlesImpl(Service::FS::MediaType media_type); + /** * Scans all storage mediums for titles for listing. */ @@ -1037,8 +1044,15 @@ private: bool cia_installing = false; bool force_old_device_id = false; bool force_new_device_id = false; + + std::atomic stop_scan_flag = false; + std::future scan_tickets_future; + std::future scan_titles_future; + std::future scan_all_future; + std::mutex am_lists_mutex; std::array, 3> am_title_list; std::multimap am_ticket_list; + std::shared_ptr system_updater_mutex; std::shared_ptr importing_title; std::map import_title_contexts; diff --git a/src/core/hle/service/cfg/cfg.cpp b/src/core/hle/service/cfg/cfg.cpp index 2af087fdc..f2d0ad770 100644 --- a/src/core/hle/service/cfg/cfg.cpp +++ b/src/core/hle/service/cfg/cfg.cpp @@ -609,6 +609,8 @@ Result Module::DeleteConfigNANDSaveFile() { } Result Module::UpdateConfigNANDSavegame() { + LOG_DEBUG(Service_CFG, "Saving config file to NAND"); + FileSys::Mode mode = {}; mode.write_flag.Assign(1); mode.create_flag.Assign(1); @@ -661,6 +663,8 @@ Result Module::FormatConfig() { } Result Module::LoadConfigNANDSaveFile() { + LOG_DEBUG(Service_CFG, "Loading config file from NAND"); + const std::string& nand_directory = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir); FileSys::ArchiveFactory_SystemSaveData systemsavedata_factory(nand_directory); diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 025f566dc..b574b574a 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -5,6 +5,7 @@ #include #include #include "common/assert.h" +#include "common/hacks/hack_manager.h" #include "common/logging/log.h" #include "core/core.h" #include "core/hle/ipc.h" @@ -59,53 +60,54 @@ namespace Service { const std::array service_module_map{ - {{"FS", 0x00040130'00001102, FS::InstallInterfaces}, - {"PM", 0x00040130'00001202, PM::InstallInterfaces}, - {"LDR", 0x00040130'00003702, LDR::InstallInterfaces}, - {"PXI", 0x00040130'00001402, PXI::InstallInterfaces}, + {{"FS", 0x00040130'00001102, FS::InstallInterfaces, false}, + {"PM", 0x00040130'00001202, PM::InstallInterfaces, false}, + {"LDR", 0x00040130'00003702, LDR::InstallInterfaces, false}, + {"PXI", 0x00040130'00001402, PXI::InstallInterfaces, false}, - {"ERR", 0x00040030'00008A02, ERR::InstallInterfaces}, - {"AC", 0x00040130'00002402, AC::InstallInterfaces}, - {"ACT", 0x00040130'00003802, ACT::InstallInterfaces}, - {"AM", 0x00040130'00001502, AM::InstallInterfaces}, - {"BOSS", 0x00040130'00003402, BOSS::InstallInterfaces}, + {"ERR", 0x00040030'00008A02, ERR::InstallInterfaces, false}, + {"AC", 0x00040130'00002402, AC::InstallInterfaces, false}, + {"ACT", 0x00040130'00003802, ACT::InstallInterfaces, true}, + {"AM", 0x00040130'00001502, AM::InstallInterfaces, false}, + {"BOSS", 0x00040130'00003402, BOSS::InstallInterfaces, false}, {"CAM", 0x00040130'00001602, [](Core::System& system) { CAM::InstallInterfaces(system); Y2R::InstallInterfaces(system); - }}, - {"CECD", 0x00040130'00002602, CECD::InstallInterfaces}, - {"CFG", 0x00040130'00001702, CFG::InstallInterfaces}, - {"DLP", 0x00040130'00002802, DLP::InstallInterfaces}, - {"DSP", 0x00040130'00001A02, DSP::InstallInterfaces}, - {"FRD", 0x00040130'00003202, FRD::InstallInterfaces}, - {"GSP", 0x00040130'00001C02, GSP::InstallInterfaces}, - {"HID", 0x00040130'00001D02, HID::InstallInterfaces}, - {"IR", 0x00040130'00003302, IR::InstallInterfaces}, - {"MIC", 0x00040130'00002002, MIC::InstallInterfaces}, - {"MVD", 0x00040130'20004102, MVD::InstallInterfaces}, - {"NDM", 0x00040130'00002B02, NDM::InstallInterfaces}, - {"NEWS", 0x00040130'00003502, NEWS::InstallInterfaces}, - {"NFC", 0x00040130'00004002, NFC::InstallInterfaces}, - {"NIM", 0x00040130'00002C02, NIM::InstallInterfaces}, - {"NS", 0x00040130'00008002, APT::InstallInterfaces}, - {"NWM", 0x00040130'00002D02, NWM::InstallInterfaces}, - {"PTM", 0x00040130'00002202, PTM::InstallInterfaces}, - {"QTM", 0x00040130'00004202, QTM::InstallInterfaces}, - {"CSND", 0x00040130'00002702, CSND::InstallInterfaces}, - {"HTTP", 0x00040130'00002902, HTTP::InstallInterfaces}, - {"SOC", 0x00040130'00002E02, SOC::InstallInterfaces}, - {"SSL", 0x00040130'00002F02, SSL::InstallInterfaces}, - {"PS", 0x00040130'00003102, PS::InstallInterfaces}, - {"PLGLDR", 0x00040130'00006902, PLGLDR::InstallInterfaces}, + }, + false}, + {"CECD", 0x00040130'00002602, CECD::InstallInterfaces, false}, + {"CFG", 0x00040130'00001702, CFG::InstallInterfaces, false}, + {"DLP", 0x00040130'00002802, DLP::InstallInterfaces, false}, + {"DSP", 0x00040130'00001A02, DSP::InstallInterfaces, false}, + {"FRD", 0x00040130'00003202, FRD::InstallInterfaces, true}, + {"GSP", 0x00040130'00001C02, GSP::InstallInterfaces, false}, + {"HID", 0x00040130'00001D02, HID::InstallInterfaces, false}, + {"IR", 0x00040130'00003302, IR::InstallInterfaces, false}, + {"MIC", 0x00040130'00002002, MIC::InstallInterfaces, false}, + {"MVD", 0x00040130'20004102, MVD::InstallInterfaces, false}, + {"NDM", 0x00040130'00002B02, NDM::InstallInterfaces, false}, + {"NEWS", 0x00040130'00003502, NEWS::InstallInterfaces, false}, + {"NFC", 0x00040130'00004002, NFC::InstallInterfaces, false}, + {"NIM", 0x00040130'00002C02, NIM::InstallInterfaces, true}, + {"NS", 0x00040130'00008002, APT::InstallInterfaces, false}, + {"NWM", 0x00040130'00002D02, NWM::InstallInterfaces, false}, + {"PTM", 0x00040130'00002202, PTM::InstallInterfaces, false}, + {"QTM", 0x00040130'00004202, QTM::InstallInterfaces, false}, + {"CSND", 0x00040130'00002702, CSND::InstallInterfaces, false}, + {"HTTP", 0x00040130'00002902, HTTP::InstallInterfaces, false}, + {"SOC", 0x00040130'00002E02, SOC::InstallInterfaces, false}, + {"SSL", 0x00040130'00002F02, SSL::InstallInterfaces, false}, + {"PS", 0x00040130'00003102, PS::InstallInterfaces, false}, + {"PLGLDR", 0x00040130'00006902, PLGLDR::InstallInterfaces, false}, + {"MCU", 0x00040130'00001F02, MCU::InstallInterfaces, false}, // no HLE implementation - {"CDC", 0x00040130'00001802, nullptr}, - {"GPIO", 0x00040130'00001B02, nullptr}, - {"I2C", 0x00040130'00001E02, nullptr}, - {"MCU", 0x00040130'00001F02, MCU::InstallInterfaces}, - {"MP", 0x00040130'00002A02, nullptr}, - {"PDN", 0x00040130'00002102, nullptr}, - {"SPI", 0x00040130'00002302, nullptr}}}; + {"CDC", 0x00040130'00001802, nullptr, false}, + {"GPIO", 0x00040130'00001B02, nullptr, false}, + {"I2C", 0x00040130'00001E02, nullptr, false}, + {"MP", 0x00040130'00002A02, nullptr, false}, + {"PDN", 0x00040130'00002102, nullptr, false}, + {"SPI", 0x00040130'00002302, nullptr, false}}}; /** * Creates a function string for logging, complete with the name (or header code, depending @@ -195,8 +197,13 @@ std::string ServiceFrameworkBase::GetFunctionName(IPC::Header header) const { return itr->second.name; } -static bool AttemptLLE(const ServiceModuleInfo& service_module) { - if (!Settings::values.lle_modules.at(service_module.name)) +static bool AttemptLLE(const ServiceModuleInfo& service_module, u64 loading_titleid) { + const bool enable_recommended_lle_modules = Common::Hacks::hack_manager.OverrideBooleanSetting( + Common::Hacks::HackType::ONLINE_LLE_REQUIRED, loading_titleid, + Settings::values.enable_required_online_lle_modules.GetValue()); + + if (!Settings::values.lle_modules.at(service_module.name) && + (!enable_recommended_lle_modules || !service_module.is_online_recommended)) return false; std::unique_ptr loader = Loader::GetLoader(AM::GetTitleContentPath(FS::MediaType::NAND, service_module.title_id)); @@ -220,7 +227,7 @@ static bool AttemptLLE(const ServiceModuleInfo& service_module) { } /// Initialize ServiceManager -void Init(Core::System& core, std::vector& lle_modules, bool allow_lle) { +void Init(Core::System& core, u64 loading_titleid, std::vector& lle_modules, bool allow_lle) { SM::ServiceManager::InstallInterfaces(core); core.Kernel().SetAppMainThreadExtendedSleep(false); bool lle_module_present = false; @@ -237,7 +244,7 @@ void Init(Core::System& core, std::vector& lle_modules, bool allow_lle) { const bool has_lle = allow_lle && core.GetSaveStateStatus() != Core::System::SaveStateStatus::LOADING && - AttemptLLE(service_module); + AttemptLLE(service_module, loading_titleid); if (has_lle) { lle_modules.push_back(service_module.title_id); } diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index d61633cf6..fbe368c2c 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -184,12 +184,13 @@ private: }; /// Initialize ServiceManager -void Init(Core::System& system, std::vector& lle_modules, bool allow_lle); +void Init(Core::System& system, u64 loading_titleid, std::vector& lle_modules, bool allow_lle); struct ServiceModuleInfo { std::string name; u64 title_id; std::function init_function; + bool is_online_recommended; }; extern const std::array service_module_map; diff --git a/src/core/hle/service/soc/soc_u.cpp b/src/core/hle/service/soc/soc_u.cpp index a40084249..23bfb5aec 100644 --- a/src/core/hle/service/soc/soc_u.cpp +++ b/src/core/hle/service/soc/soc_u.cpp @@ -1,4 +1,4 @@ -// Copyright 2014 Citra Emulator Project +// Copyright Citra Emulator Project / Azahar Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -1097,6 +1097,8 @@ void SOC_U::Close(Kernel::HLERequestContext& ctx) { if (ret != 0) { ret = TranslateError(GET_ERRNO); + } else { + created_sockets.erase(socket_handle); } LOG_DEBUG(Service_SOC, "pid={}, fd={}, ret={}", pid, socket_handle, static_cast(ret)); diff --git a/src/core/hw/unique_data.cpp b/src/core/hw/unique_data.cpp index cd1a4a2ce..99ed5d871 100644 --- a/src/core/hw/unique_data.cpp +++ b/src/core/hw/unique_data.cpp @@ -59,6 +59,7 @@ SecureDataLoadStatus LoadSecureInfoA() { return SecureDataLoadStatus::IOError; } + HW::AES::InitKeys(); secure_info_a_signature_valid = secure_info_a.VerifySignature(); if (!secure_info_a_signature_valid) { LOG_WARNING(HW, "SecureInfo_A signature check failed"); @@ -90,6 +91,7 @@ SecureDataLoadStatus LoadLocalFriendCodeSeedB() { return SecureDataLoadStatus::IOError; } + HW::AES::InitKeys(); local_friend_code_seed_b_signature_valid = local_friend_code_seed_b.VerifySignature(); if (!local_friend_code_seed_b_signature_valid) { LOG_WARNING(HW, "LocalFriendCodeSeed_B signature check failed"); @@ -167,6 +169,7 @@ SecureDataLoadStatus LoadMovable() { return SecureDataLoadStatus::IOError; } + HW::AES::InitKeys(); movable_signature_valid = movable.VerifySignature(); if (!movable_signature_valid) { LOG_WARNING(HW, "LocalFriendCodeSeed_B signature check failed");