Add enable required LLE modules for online features option

This commit is contained in:
PabloMK7 2025-03-13 00:24:35 +01:00
parent 42d77cd720
commit 5083c90aad
21 changed files with 448 additions and 181 deletions

View File

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

View File

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

View File

@ -1303,6 +1303,15 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
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(
SliderSetting(
IntSetting.CPU_CLOCK_SPEED,

View File

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

View File

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

View File

@ -329,6 +329,10 @@ is_new_3ds =
# 0 (default): No, 1: 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 =

View File

@ -778,5 +778,6 @@
<string name="delay_start_lle_modules_description">Delays the start of the app when LLE modules are enabled.</string>
<string name="deterministic_async_operations">Deterministic Async Operations</string>
<string name="deterministic_async_operations_description">Makes async operations deterministic for debugging. Enabling this may cause freezes.</string>
<string name="enable_required_online_lle_modules">Enable required LLE modules for online features (if installed)</string>
<string name="enable_required_online_lle_modules_desc">Enables the LLE modules needed for online multiplayer, eShop access, etc.</string>
</resources>

View File

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

View File

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

View File

@ -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<Service::CFG::Module> cfg;

View File

@ -78,7 +78,17 @@
</property>
</widget>
</item>
<item row="3" column="1">
<item row="3" column="0">
<widget class="QCheckBox" name="enable_required_online_lle_modules">
<property name="text">
<string>Enable required LLE modules for online features (if installed)</string>
</property>
<property name="toolTip">
<string>Enables the LLE modules needed for online multiplayer, eShop access, etc.</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLineEdit" name="edit_username">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
@ -91,21 +101,21 @@
</property>
</widget>
</item>
<item row="3" column="0">
<item row="4" column="0">
<widget class="QLabel" name="label_username">
<property name="text">
<string>Username</string>
</property>
</widget>
</item>
<item row="4" column="0">
<item row="5" column="0">
<widget class="QLabel" name="label_birthday">
<property name="text">
<string>Birthday</string>
</property>
</widget>
</item>
<item row="4" column="1">
<item row="5" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_birthday2">
<item>
<widget class="QComboBox" name="combo_birthmonth">
@ -176,14 +186,14 @@
</item>
</layout>
</item>
<item row="5" column="0">
<item row="6" column="0">
<widget class="QLabel" name="label_language">
<property name="text">
<string>Language</string>
</property>
</widget>
</item>
<item row="5" column="1">
<item row="6" column="1">
<widget class="QComboBox" name="combo_language">
<property name="toolTip">
<string>Note: this can be overridden when region setting is auto-select</string>
@ -250,14 +260,14 @@
</item>
</widget>
</item>
<item row="6" column="0">
<item row="7" column="0">
<widget class="QLabel" name="label_sound">
<property name="text">
<string>Sound output mode</string>
</property>
</widget>
</item>
<item row="6" column="1">
<item row="7" column="1">
<widget class="QComboBox" name="combo_sound">
<item>
<property name="text">
@ -276,24 +286,24 @@
</item>
</widget>
</item>
<item row="7" column="0">
<item row="8" column="0">
<widget class="QLabel" name="label_country">
<property name="text">
<string>Country</string>
</property>
</widget>
</item>
<item row="7" column="1">
<item row="8" column="1">
<widget class="QComboBox" name="combo_country"/>
</item>
<item row="8" column="0">
<item row="9" column="0">
<widget class="QLabel" name="label_init_clock">
<property name="text">
<string>Clock</string>
</property>
</widget>
</item>
<item row="8" column="1">
<item row="9" column="1">
<widget class="QComboBox" name="combo_init_clock">
<item>
<property name="text">
@ -307,28 +317,28 @@
</item>
</widget>
</item>
<item row="9" column="0">
<item row="10" column="0">
<widget class="QLabel" name="label_init_time">
<property name="text">
<string>Startup time</string>
</property>
</widget>
</item>
<item row="9" column="1">
<item row="10" column="1">
<widget class="QDateTimeEdit" name="edit_init_time">
<property name="displayFormat">
<string>yyyy-MM-ddTHH:mm:ss</string>
</property>
</widget>
</item>
<item row="9" column="0">
<item row="11" column="0">
<widget class="QLabel" name="label_init_time_offset">
<property name="text">
<string>Offset time</string>
</property>
</widget>
</item>
<item row="9" column="1">
<item row="11" column="1">
<layout class="QGridLayout" name="edit_init_time_offset_grid">
<item row="0" column="0">
<widget class="QSpinBox" name="edit_init_time_offset_days">
@ -352,14 +362,14 @@
</item>
</layout>
</item>
<item row="10" column="0">
<item row="12" column="0">
<widget class="QLabel" name="label_init_ticks_type">
<property name="text">
<string>Initial System Ticks</string>
</property>
</widget>
</item>
<item row="10" column="1">
<item row="12" column="1">
<widget class="QComboBox" name="combo_init_ticks_type">
<item>
<property name="text">
@ -373,14 +383,14 @@
</item>
</widget>
</item>
<item row="11" column="0">
<item row="13" column="0">
<widget class="QLabel" name="label_init_ticks_value">
<property name="text">
<string>Initial System Ticks Override</string>
</property>
</widget>
</item>
<item row="11" column="1">
<item row="13" column="1">
<widget class="QLineEdit" name="edit_init_ticks_value">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
@ -393,21 +403,21 @@
</property>
</widget>
</item>
<item row="12" column="0">
<item row="14" column="0">
<widget class="QLabel" name="label_play_coins">
<property name="text">
<string>Play Coins</string>
</property>
</widget>
</item>
<item row="12" column="1">
<item row="14" column="1">
<widget class="QSpinBox" name="spinBox_play_coins">
<property name="maximum">
<number>300</number>
</property>
</widget>
</item>
<item row="13" column="0">
<item row="15" column="0">
<widget class="QLabel" name="label_steps_per_hour">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Number of steps per hour reported by the pedometer. Range from 0 to 65,535.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
@ -417,28 +427,28 @@
</property>
</widget>
</item>
<item row="13" column="1">
<item row="15" column="1">
<widget class="QSpinBox" name="spinBox_steps_per_hour">
<property name="maximum">
<number>9999</number>
</property>
</widget>
</item>
<item row="14" column="1">
<item row="16" column="1">
<widget class="QCheckBox" name="toggle_system_setup">
<property name="text">
<string>Run System Setup when Home Menu is launched</string>
</property>
</widget>
</item>
<item row="15" column="0">
<item row="17" column="0">
<widget class="QLabel" name="label_console_id">
<property name="text">
<string>Console ID:</string>
</property>
</widget>
</item>
<item row="15" column="1">
<item row="17" column="1">
<widget class="QPushButton" name="button_regenerate_console_id">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
@ -454,14 +464,14 @@
</property>
</widget>
</item>
<item row="16" column="0">
<item row="18" column="0">
<widget class="QLabel" name="label_mac">
<property name="text">
<string>MAC:</string>
</property>
</widget>
</item>
<item row="16" column="1">
<item row="18" column="1">
<widget class="QPushButton" name="button_regenerate_mac">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
@ -477,21 +487,21 @@
</property>
</widget>
</item>
<item row="17" column="0">
<item row="19" column="0">
<widget class="QLabel" name="label_plugin_loader">
<property name="text">
<string>3GX Plugin Loader:</string>
</property>
</widget>
</item>
<item row="17" column="1">
<item row="19" column="1">
<widget class="QCheckBox" name="plugin_loader">
<property name="text">
<string>Enable 3GX plugin loader</string>
</property>
</widget>
</item>
<item row="18" column="1">
<item row="20" column="1">
<widget class="QCheckBox" name="allow_plugin_loader">
<property name="text">
<string>Allow applications to change plugin loader state</string>

View File

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

View File

@ -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.
@ -70,5 +70,43 @@ HackManager hack_manager = {
},
}},
{HackType::ONLINE_LLE_REQUIRED,
HackEntry{
.mode = HackAllowMode::FORCE,
.affected_title_ids =
{
// eShop
0x0004001000020900, // JAP
0x0004001000021900, // USA
0x0004001000022900, // EUR
0x0004001000027900, // KOR
0x0004001000028900, // TWN
// System Settings
0x0004001000020000, // JAP
0x0004001000021000, // USA
0x0004001000022000, // EUR
0x0004001000026000, // CHN
0x0004001000027000, // KOR
0x0004001000028000, // TWN
// Nintendo Network ID Settings
0x000400100002BF00, // JAP
0x000400100002C000, // USA
0x000400100002C100, // EUR
// System Settings
0x0004003000008202, // JAP
0x0004003000008F02, // USA
0x0004003000009802, // EUR
0x000400300000A102, // CHN
0x000400300000A902, // KOR
0x000400300000B102, // TWN
// Pretendo Network's Nimbus
0x000400000D40D200,
},
}},
}};
}

View File

@ -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 {};

View File

@ -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<HackType, HackEntry> entries;
};

View File

@ -450,8 +450,10 @@ struct Values {
Setting<bool> use_cpu_jit{true, "use_cpu_jit"};
SwitchableSetting<s32, true> cpu_clock_percentage{100, 5, 400, "cpu_clock_percentage"};
SwitchableSetting<bool> is_new_3ds{true, "is_new_3ds"};
SwitchableSetting<bool> lle_applets{false, "lle_applets"};
SwitchableSetting<bool> lle_applets{true, "lle_applets"};
SwitchableSetting<bool> deterministic_async_operations{false, "deterministic_async_operations"};
SwitchableSetting<bool> enable_required_online_lle_modules{
false, "enable_required_online_lle_modules"};
// Data Storage
Setting<bool> use_virtual_sd{true, "use_virtual_sd"};

View File

@ -508,8 +508,10 @@ System::ResultStatus System::Init(Frontend::EmuWindow& emu_window,
service_manager = std::make_unique<Service::SM::ServiceManager>(*this);
archive_manager = std::make_unique<Service::FS::ArchiveManager>(*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) {

View File

@ -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,13 @@ std::string GetMediaTitlePath(Service::FS::MediaType media_type) {
}
void Module::ScanForTickets() {
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");
@ -1210,6 +1212,13 @@ void Module::ScanForTickets() {
}
void Module::ScanForTitles(Service::FS::MediaType media_type) {
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<u32>(media_type)].clear();
LOG_DEBUG(Service_AM, "Starting title scan for media_type={}", static_cast<int>(media_type));
@ -1245,9 +1254,12 @@ void Module::ScanForTitles(Service::FS::MediaType media_type) {
}
void Module::ScanForAllTitles() {
ScanForTickets();
ScanForTitles(Service::FS::MediaType::NAND);
ScanForTitles(Service::FS::MediaType::SDMC);
scan_all_future = std::async([this]() {
std::scoped_lock lock(am_lists_mutex);
ScanForTicketsImpl();
ScanForTitlesImpl(Service::FS::MediaType::NAND);
ScanForTitlesImpl(Service::FS::MediaType::SDMC);
});
}
Module::Interface::Interface(std::shared_ptr<Module> am, const char* name, u32 max_session)
@ -1307,7 +1319,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<u32>(static_cast<u32>(am->am_title_list[media_type].size()));
@ -1648,6 +1660,7 @@ void Module::Interface::GetProgramList(Kernel::HLERequestContext& ctx) {
return;
}
std::scoped_lock lock(am->am_lists_mutex);
u32 media_count = static_cast<u32>(am->am_title_list[media_type].size());
u32 copied = std::min(media_count, count);
@ -1660,8 +1673,8 @@ void Module::Interface::GetProgramList(Kernel::HLERequestContext& ctx) {
}
Result GetTitleInfoFromList(std::span<const u64> title_id_list, Service::FS::MediaType media_type,
Kernel::MappedBuffer& title_info_out) {
std::size_t write_offset = 0;
std::vector<TitleInfo>& 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 +1695,7 @@ Result GetTitleInfoFromList(std::span<const u64> 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 +1785,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<u64> title_id_list;
std::vector<u64> title_id_list(title_count);
title_id_list_buffer.Read(title_id_list.data(), 0, title_count * sizeof(u64));
Result res{0};
std::vector<TitleInfo> out;
Kernel::MappedBuffer* title_id_list_buffer;
Kernel::MappedBuffer* title_info_out;
};
auto async_data = std::make_shared<AsyncData>();
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 +1989,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<u64> title_id_list;
std::vector<u64> title_id_list(title_count);
title_id_list_buffer.Read(title_id_list.data(), 0, title_count * sizeof(u64));
Result res{0};
std::vector<TitleInfo> out;
Kernel::MappedBuffer* title_id_list_buffer;
Kernel::MappedBuffer* title_info_out;
};
auto async_data = std::make_shared<AsyncData>();
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<u32>(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<u32>(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 +2136,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<u64> title_id_list;
std::vector<u64> title_id_list(title_count);
title_id_list_buffer.Read(title_id_list.data(), 0, title_count * sizeof(u64));
Result res{0};
std::vector<TitleInfo> out;
Kernel::MappedBuffer* title_id_list_buffer;
Kernel::MappedBuffer* title_info_out;
};
auto async_data = std::make_shared<AsyncData>();
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<u32>(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<u32>(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 +2385,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 +2408,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<u32>(am->am_ticket_list.size());
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
@ -2304,6 +2425,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<size_t>(ticket_index), am->am_ticket_list.size()));
@ -2631,6 +2753,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;
@ -3285,6 +3408,7 @@ void Module::Interface::EndImportTicket(Kernel::HLERequestContext& ctx) {
auto ticket_file = GetFileBackendFromSession<TicketFile>(ticket);
if (ticket_file.Succeeded()) {
rb.Push(ticket_file.Unwrap()->Commit());
std::scoped_lock lock(am->am_lists_mutex);
am->am_ticket_list.insert(std::make_pair(ticket_file.Unwrap()->GetTitleID(),
ticket_file.Unwrap()->GetTicketID()));
} else {
@ -3308,6 +3432,7 @@ void Module::Interface::BeginImportTitle(Kernel::HLERequestContext& ctx) {
am->importing_title =
std::make_shared<CurrentImportingTitle>(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
@ -3770,11 +3895,11 @@ void Module::Interface::Sign(Kernel::HLERequestContext& ctx) {
template <class Archive>
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 +3940,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 +3968,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<u32>(std::distance(range.first, range.second));
@ -3861,6 +3988,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 +4006,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<u32>(std::distance(range.first, range.second));
@ -3895,6 +4024,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<size_t>(skip),
@ -3943,6 +4073,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,6 +4137,7 @@ 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");
}

View File

@ -6,7 +6,9 @@
#include <array>
#include <functional>
#include <future>
#include <memory>
#include <mutex>
#include <string>
#include <vector>
#include <boost/serialization/array.hpp>
@ -1022,12 +1024,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,6 +1043,10 @@ private:
bool cia_installing = false;
bool force_old_device_id = false;
bool force_new_device_id = false;
std::future<void> scan_tickets_future;
std::future<void> scan_titles_future;
std::future<void> scan_all_future;
std::mutex am_lists_mutex;
std::array<std::vector<u64_le>, 3> am_title_list;
std::multimap<u64, u64> am_ticket_list;
std::shared_ptr<Kernel::Mutex> system_updater_mutex;

View File

@ -5,6 +5,7 @@
#include <algorithm>
#include <fmt/format.h>
#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<ServiceModuleInfo, 41> 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::AppLoader> 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<u64>& lle_modules, bool allow_lle) {
void Init(Core::System& core, u64 loading_titleid, std::vector<u64>& 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<u64>& 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);
}

View File

@ -184,12 +184,13 @@ private:
};
/// Initialize ServiceManager
void Init(Core::System& system, std::vector<u64>& lle_modules, bool allow_lle);
void Init(Core::System& system, u64 loading_titleid, std::vector<u64>& lle_modules, bool allow_lle);
struct ServiceModuleInfo {
std::string name;
u64 title_id;
std::function<void(Core::System&)> init_function;
bool is_online_recommended;
};
extern const std::array<ServiceModuleInfo, 41> service_module_map;