diff --git a/src/citra/config.cpp b/src/citra/config.cpp index 91e25ca5c..64d0914a2 100644 --- a/src/citra/config.cpp +++ b/src/citra/config.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include +#include #include #include #include "citra/config.h" @@ -10,6 +11,7 @@ #include "common/file_util.h" #include "common/logging/log.h" #include "common/param_package.h" +#include "core/hle/service/service.h" #include "core/settings.h" #include "input_common/main.h" #include "input_common/udp/client.h" @@ -195,6 +197,11 @@ void Config::ReadValues() { Settings::values.gdbstub_port = static_cast(sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689)); + for (const auto& service_module : Service::service_module_map) { + bool use_lle = sdl2_config->GetBoolean("Debugging", "LLE\\" + service_module.name, false); + Settings::values.lle_modules.emplace(service_module.name, use_lle); + } + // Web Service Settings::values.enable_telemetry = sdl2_config->GetBoolean("WebService", "enable_telemetry", true); diff --git a/src/citra/default_ini.h b/src/citra/default_ini.h index 4150a2292..f0d5e3c15 100644 --- a/src/citra/default_ini.h +++ b/src/citra/default_ini.h @@ -227,6 +227,7 @@ log_filter = *:Info # Port for listening to GDB connections. use_gdbstub=false gdbstub_port=24689 +# To LLE a service module add "LLE\=true" [WebService] # Whether or not to enable telemetry diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt index 9e4403200..571a30d7c 100644 --- a/src/citra_qt/CMakeLists.txt +++ b/src/citra_qt/CMakeLists.txt @@ -60,6 +60,8 @@ add_executable(citra-qt debugger/graphics/graphics_tracing.h debugger/graphics/graphics_vertex_shader.cpp debugger/graphics/graphics_vertex_shader.h + debugger/lle_service_modules.cpp + debugger/lle_service_modules.h debugger/profiler.cpp debugger/profiler.h debugger/registers.cpp diff --git a/src/citra_qt/configuration/config.cpp b/src/citra_qt/configuration/config.cpp index 3e6303a10..2ae3e95fe 100644 --- a/src/citra_qt/configuration/config.cpp +++ b/src/citra_qt/configuration/config.cpp @@ -2,10 +2,12 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include #include #include "citra_qt/configuration/config.h" #include "citra_qt/ui_settings.h" #include "common/file_util.h" +#include "core/hle/service/service.h" #include "input_common/main.h" #include "input_common/udp/client.h" #include "network/network.h" @@ -174,6 +176,13 @@ void Config::ReadValues() { qt_config->beginGroup("Debugging"); Settings::values.use_gdbstub = ReadSetting("use_gdbstub", false).toBool(); Settings::values.gdbstub_port = ReadSetting("gdbstub_port", 24689).toInt(); + + qt_config->beginGroup("LLE"); + for (const auto& service_module : Service::service_module_map) { + bool use_lle = ReadSetting(QString::fromStdString(service_module.name), false).toBool(); + Settings::values.lle_modules.emplace(service_module.name, use_lle); + } + qt_config->endGroup(); qt_config->endGroup(); qt_config->beginGroup("WebService"); @@ -403,6 +412,12 @@ void Config::SaveValues() { qt_config->beginGroup("Debugging"); WriteSetting("use_gdbstub", Settings::values.use_gdbstub, false); WriteSetting("gdbstub_port", Settings::values.gdbstub_port, 24689); + + qt_config->beginGroup("LLE"); + for (const auto& service_module : Settings::values.lle_modules) { + WriteSetting(QString::fromStdString(service_module.first), service_module.second, false); + } + qt_config->endGroup(); qt_config->endGroup(); qt_config->beginGroup("WebService"); diff --git a/src/citra_qt/debugger/lle_service_modules.cpp b/src/citra_qt/debugger/lle_service_modules.cpp new file mode 100644 index 000000000..eefc0cc12 --- /dev/null +++ b/src/citra_qt/debugger/lle_service_modules.cpp @@ -0,0 +1,32 @@ +// Copyright 2018 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include +#include +#include +#include "citra_qt/debugger/lle_service_modules.h" +#include "core/settings.h" + +LLEServiceModulesWidget::LLEServiceModulesWidget(QWidget* parent) + : QDockWidget(tr("Toggle LLE Service Modules"), parent) { + QScrollArea* scroll_area = new QScrollArea; + QLayout* scroll_layout = new QVBoxLayout; + for (const auto& service_module : Settings::values.lle_modules) { + QCheckBox* check_box = + new QCheckBox(QString::fromStdString(service_module.first), scroll_area); + check_box->setChecked(service_module.second); + connect(check_box, &QCheckBox::toggled, [check_box] { + Settings::values.lle_modules.find(check_box->text().toStdString())->second = + check_box->isChecked(); + }); + scroll_layout->addWidget(check_box); + } + QWidget* scroll_area_contents = new QWidget; + scroll_area_contents->setLayout(scroll_layout); + scroll_area->setWidget(scroll_area_contents); + setWidget(scroll_area); +} + +LLEServiceModulesWidget::~LLEServiceModulesWidget() = default; diff --git a/src/citra_qt/debugger/lle_service_modules.h b/src/citra_qt/debugger/lle_service_modules.h new file mode 100644 index 000000000..9059f676c --- /dev/null +++ b/src/citra_qt/debugger/lle_service_modules.h @@ -0,0 +1,15 @@ +// Copyright 2018 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include + +class LLEServiceModulesWidget : public QDockWidget { + Q_OBJECT + +public: + explicit LLEServiceModulesWidget(QWidget* parent = nullptr); + ~LLEServiceModulesWidget(); +}; diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index 577025e27..3c6ff42d6 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -30,6 +30,7 @@ #include "citra_qt/debugger/graphics/graphics_surface.h" #include "citra_qt/debugger/graphics/graphics_tracing.h" #include "citra_qt/debugger/graphics/graphics_vertex_shader.h" +#include "citra_qt/debugger/lle_service_modules.h" #include "citra_qt/debugger/profiler.h" #include "citra_qt/debugger/registers.h" #include "citra_qt/debugger/wait_tree.h" @@ -304,6 +305,15 @@ void GMainWindow::InitializeDebugWidgets() { &WaitTreeWidget::OnEmulationStarting); connect(this, &GMainWindow::EmulationStopping, waitTreeWidget, &WaitTreeWidget::OnEmulationStopping); + + lleServiceModulesWidget = new LLEServiceModulesWidget(this); + addDockWidget(Qt::RightDockWidgetArea, lleServiceModulesWidget); + lleServiceModulesWidget->hide(); + debug_menu->addAction(lleServiceModulesWidget->toggleViewAction()); + connect(this, &GMainWindow::EmulationStarting, + [this] { lleServiceModulesWidget->setDisabled(true); }); + connect(this, &GMainWindow::EmulationStopping, waitTreeWidget, + [this] { lleServiceModulesWidget->setDisabled(false); }); } void GMainWindow::InitializeRecentFileMenuActions() { diff --git a/src/citra_qt/main.h b/src/citra_qt/main.h index 70677b66a..b551c1c3b 100644 --- a/src/citra_qt/main.h +++ b/src/citra_qt/main.h @@ -28,6 +28,7 @@ class GraphicsBreakPointsWidget; class GraphicsTracingWidget; class GraphicsVertexShaderWidget; class GRenderWindow; +class LLEServiceModulesWidget; class MicroProfileDialog; class MultiplayerState; class ProfilerWidget; @@ -217,6 +218,7 @@ private: GraphicsBreakPointsWidget* graphicsBreakpointsWidget; GraphicsVertexShaderWidget* graphicsVertexShaderWidget; GraphicsTracingWidget* graphicsTracingWidget; + LLEServiceModulesWidget* lleServiceModulesWidget; WaitTreeWidget* waitTreeWidget; Updater* updater; diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 4480574dc..2c580e738 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -64,6 +64,58 @@ namespace Service { std::unordered_map> g_kernel_named_ports; +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}, + + {"ERR", 0x00040030'00008A02, [](SM::ServiceManager& sm) { ERR::InstallInterfaces(); }}, + {"AC", 0x00040130'00002402, AC::InstallInterfaces}, + {"ACT", 0x00040130'00003802, ACT::InstallInterfaces}, + {"AM", 0x00040130'00001502, AM::InstallInterfaces}, + {"BOSS", 0x00040130'00003402, BOSS::InstallInterfaces}, + {"CAM", 0x00040130'00001602, + [](SM::ServiceManager& sm) { + CAM::InstallInterfaces(sm); + Y2R::InstallInterfaces(sm); + }}, + {"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, + [](SM::ServiceManager& sm) { + NS::InstallInterfaces(sm); + APT::InstallInterfaces(sm); + }}, + {"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}, + // no HLE implementation + {"CDC", 0x00040130'00001802, nullptr}, + {"GPIO", 0x00040130'00001B02, nullptr}, + {"I2C", 0x00040130'00001E02, nullptr}, + {"MCU", 0x00040130'00001F02, nullptr}, + {"MP", 0x00040130'00002A02, nullptr}, + {"PDN", 0x00040130'00002102, nullptr}, + {"PS", 0x00040130'00003102, nullptr}, + {"SPI", 0x00040130'00002302, nullptr}}}; + /** * Creates a function string for logging, complete with the name (or header code, depending * on what's passed in) the port name, and all the cmd_buff arguments. @@ -166,50 +218,32 @@ void AddNamedPort(std::string name, SharedPtr port) { g_kernel_named_ports.emplace(std::move(name), std::move(port)); } +static bool AttemptLLE(const ServiceModuleInfo& service_module) { + if (!Settings::values.lle_modules.at(service_module.name)) + return false; + std::unique_ptr loader = + Loader::GetLoader(AM::GetTitleContentPath(FS::MediaType::NAND, service_module.title_id)); + if (!loader) { + LOG_ERROR(Service, + "Service module \"{}\" could not be loaded; Defaulting to HLE implementation.", + service_module.name); + return false; + } + SharedPtr process; + loader->Load(process); + LOG_DEBUG(Service, "Service module \"{}\" has been successfully loaded.", service_module.name); + return true; +} + /// Initialize ServiceManager void Init(std::shared_ptr& sm) { + FS::ArchiveInit(); SM::ServiceManager::InstallInterfaces(sm); - ERR::InstallInterfaces(); - - PS::InstallInterfaces(*sm); - PXI::InstallInterfaces(*sm); - NS::InstallInterfaces(*sm); - AC::InstallInterfaces(*sm); - LDR::InstallInterfaces(*sm); - MIC::InstallInterfaces(*sm); - NWM::InstallInterfaces(*sm); - - FS::InstallInterfaces(*sm); - FS::ArchiveInit(); - ACT::InstallInterfaces(*sm); - AM::InstallInterfaces(*sm); - APT::InstallInterfaces(*sm); - BOSS::InstallInterfaces(*sm); - CAM::InstallInterfaces(*sm); - CECD::InstallInterfaces(*sm); - CFG::InstallInterfaces(*sm); - DLP::InstallInterfaces(*sm); - DSP::InstallInterfaces(*sm); - FRD::InstallInterfaces(*sm); - GSP::InstallInterfaces(*sm); - HID::InstallInterfaces(*sm); - IR::InstallInterfaces(*sm); - MVD::InstallInterfaces(*sm); - NDM::InstallInterfaces(*sm); - NEWS::InstallInterfaces(*sm); - NFC::InstallInterfaces(*sm); - NIM::InstallInterfaces(*sm); - PTM::InstallInterfaces(*sm); - QTM::InstallInterfaces(*sm); - - CSND::InstallInterfaces(*sm); - HTTP::InstallInterfaces(*sm); - PM::InstallInterfaces(*sm); - SOC::InstallInterfaces(*sm); - SSL::InstallInterfaces(*sm); - Y2R::InstallInterfaces(*sm); - + for (const auto& service_module : service_module_map) { + if (!AttemptLLE(service_module) && service_module.init_function != nullptr) + service_module.init_function(*sm); + } LOG_DEBUG(Service, "initialized OK"); } diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index 8a0dfc0af..e99fb3474 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -4,7 +4,9 @@ #pragma once +#include #include +#include #include #include #include @@ -12,6 +14,7 @@ #include "common/common_types.h" #include "core/hle/kernel/hle_ipc.h" #include "core/hle/kernel/kernel.h" +#include "core/hle/service/sm/sm.h" //////////////////////////////////////////////////////////////////////////////////////////////////// // Namespace Service @@ -189,6 +192,14 @@ void Shutdown(); /// Map of named ports managed by the kernel, which can be retrieved using the ConnectToPort SVC. extern std::unordered_map> g_kernel_named_ports; +struct ServiceModuleInfo { + std::string name; + u64 title_id; + std::function init_function; +}; + +extern const std::array service_module_map; + /// Adds a port to the named port table void AddNamedPort(std::string name, Kernel::SharedPtr port); diff --git a/src/core/settings.h b/src/core/settings.h index ab1454126..980a41109 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -6,6 +6,7 @@ #include #include +#include #include "common/common_types.h" #include "core/hle/service/cam/cam.h" @@ -152,6 +153,7 @@ struct Values { bool use_gdbstub; u16 gdbstub_port; std::string log_filter; + std::unordered_map lle_modules; // Movie std::string movie_play;