From 8779b31fe60c728ace89a9b5128b68feffa9c7d7 Mon Sep 17 00:00:00 2001
From: Yuri Kunde Schlesner <yuriks@yuriks.net>
Date: Fri, 30 Jan 2015 16:07:04 -0200
Subject: [PATCH] Make Port/Service registration and querying more HW-accurate

---
 src/core/hle/service/service.cpp | 134 +++++++++++++------------------
 src/core/hle/service/service.h   |  30 ++-----
 src/core/hle/service/srv.cpp     |   6 +-
 src/core/hle/svc.cpp             |  20 ++++-
 4 files changed, 82 insertions(+), 108 deletions(-)

diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 355196fd3..e0979ea5d 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -54,96 +54,76 @@
 
 namespace Service {
 
-Manager* g_manager = nullptr;  ///< Service manager
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Service Manager class
-
-void Manager::AddService(Interface* service) {
-    // TOOD(yuriks): Fix error reporting
-    m_port_map[service->GetPortName()] = Kernel::g_handle_table.Create(service).ValueOr(INVALID_HANDLE);
-    m_services.push_back(service);
-}
-
-void Manager::DeleteService(const std::string& port_name) {
-    Interface* service = FetchFromPortName(port_name);
-    m_services.erase(std::remove(m_services.begin(), m_services.end(), service), m_services.end());
-    m_port_map.erase(port_name);
-}
-
-Interface* Manager::FetchFromHandle(Handle handle) {
-    // TODO(yuriks): This function is very suspicious and should probably be exterminated.
-    return Kernel::g_handle_table.Get<Interface>(handle).get();
-}
-
-Interface* Manager::FetchFromPortName(const std::string& port_name) {
-    auto itr = m_port_map.find(port_name);
-    if (itr == m_port_map.end()) {
-        return nullptr;
-    }
-    return FetchFromHandle(itr->second);
-}
-
+std::unordered_map<std::string, Kernel::SharedPtr<Interface>> g_kernel_named_ports;
+std::unordered_map<std::string, Kernel::SharedPtr<Interface>> g_srv_services;
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 // Module interface
 
+static void AddNamedPort(Interface* interface) {
+    g_kernel_named_ports.emplace(interface->GetPortName(), interface);
+}
+
+static void AddService(Interface* interface) {
+    g_srv_services.emplace(interface->GetPortName(), interface);
+}
+
 /// Initialize ServiceManager
 void Init() {
-    g_manager = new Manager;
+    AddNamedPort(new SRV::Interface);
 
-    g_manager->AddService(new SRV::Interface);
-    g_manager->AddService(new AC_U::Interface);
-    g_manager->AddService(new ACT_U::Interface);
-    g_manager->AddService(new AM_APP::Interface);
-    g_manager->AddService(new AM_NET::Interface);
-    g_manager->AddService(new AM_SYS::Interface);
-    g_manager->AddService(new APT_A::Interface);
-    g_manager->AddService(new APT_S::Interface);
-    g_manager->AddService(new APT_U::Interface);
-    g_manager->AddService(new BOSS_P::Interface);
-    g_manager->AddService(new BOSS_U::Interface);
-    g_manager->AddService(new CAM_U::Interface);
-    g_manager->AddService(new CECD_S::Interface);
-    g_manager->AddService(new CECD_U::Interface);
-    g_manager->AddService(new CFG_I::Interface);
-    g_manager->AddService(new CFG_S::Interface);
-    g_manager->AddService(new CFG_U::Interface);
-    g_manager->AddService(new CSND_SND::Interface);
-    g_manager->AddService(new DSP_DSP::Interface);
-    g_manager->AddService(new ERR_F::Interface);
-    g_manager->AddService(new FRD_A::Interface);
-    g_manager->AddService(new FRD_U::Interface);
-    g_manager->AddService(new FS::FSUserInterface);
-    g_manager->AddService(new GSP_GPU::Interface);
-    g_manager->AddService(new GSP_LCD::Interface);
-    g_manager->AddService(new HID_User::Interface);
-    g_manager->AddService(new HID_SPVR::Interface);
-    g_manager->AddService(new HTTP_C::Interface);
-    g_manager->AddService(new IR_RST::Interface);
-    g_manager->AddService(new IR_U::Interface);
-    g_manager->AddService(new LDR_RO::Interface);
-    g_manager->AddService(new MIC_U::Interface);
-    g_manager->AddService(new NDM_U::Interface);
-    g_manager->AddService(new NEWS_S::Interface);
-    g_manager->AddService(new NEWS_U::Interface);
-    g_manager->AddService(new NIM_AOC::Interface);
-    g_manager->AddService(new NS_S::Interface);
-    g_manager->AddService(new NWM_UDS::Interface);
-    g_manager->AddService(new PM_APP::Interface);
-    g_manager->AddService(new PTM_PLAY::Interface);
-    g_manager->AddService(new PTM_U::Interface);
-    g_manager->AddService(new PTM_SYSM::Interface);
-    g_manager->AddService(new SOC_U::Interface);
-    g_manager->AddService(new SSL_C::Interface);
-    g_manager->AddService(new Y2R_U::Interface);
+    AddService(new AC_U::Interface);
+    AddService(new ACT_U::Interface);
+    AddService(new AM_APP::Interface);
+    AddService(new AM_NET::Interface);
+    AddService(new AM_SYS::Interface);
+    AddService(new APT_A::Interface);
+    AddService(new APT_S::Interface);
+    AddService(new APT_U::Interface);
+    AddService(new BOSS_P::Interface);
+    AddService(new BOSS_U::Interface);
+    AddService(new CAM_U::Interface);
+    AddService(new CECD_S::Interface);
+    AddService(new CECD_U::Interface);
+    AddService(new CFG_I::Interface);
+    AddService(new CFG_S::Interface);
+    AddService(new CFG_U::Interface);
+    AddService(new CSND_SND::Interface);
+    AddService(new DSP_DSP::Interface);
+    AddService(new ERR_F::Interface);
+    AddService(new FRD_A::Interface);
+    AddService(new FRD_U::Interface);
+    AddService(new FS::FSUserInterface);
+    AddService(new GSP_GPU::Interface);
+    AddService(new GSP_LCD::Interface);
+    AddService(new HID_User::Interface);
+    AddService(new HID_SPVR::Interface);
+    AddService(new HTTP_C::Interface);
+    AddService(new IR_RST::Interface);
+    AddService(new IR_U::Interface);
+    AddService(new LDR_RO::Interface);
+    AddService(new MIC_U::Interface);
+    AddService(new NDM_U::Interface);
+    AddService(new NEWS_S::Interface);
+    AddService(new NEWS_U::Interface);
+    AddService(new NIM_AOC::Interface);
+    AddService(new NS_S::Interface);
+    AddService(new NWM_UDS::Interface);
+    AddService(new PM_APP::Interface);
+    AddService(new PTM_PLAY::Interface);
+    AddService(new PTM_U::Interface);
+    AddService(new PTM_SYSM::Interface);
+    AddService(new SOC_U::Interface);
+    AddService(new SSL_C::Interface);
+    AddService(new Y2R_U::Interface);
 
     LOG_DEBUG(Service, "initialized OK");
 }
 
 /// Shutdown ServiceManager
 void Shutdown() {
-    delete g_manager;
+    g_srv_services.clear();
+    g_kernel_named_ports.clear();
     LOG_DEBUG(Service, "shutdown OK");
 }
 
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h
index e75d5008b..533d3565b 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -5,9 +5,10 @@
 #pragma once
 
 #include <algorithm>
-#include <vector>
 #include <map>
 #include <string>
+#include <unordered_map>
+#include <vector>
 
 #include "common/common.h"
 #include "common/string_util.h"
@@ -121,34 +122,15 @@ private:
 
 };
 
-/// Simple class to manage accessing services from ports and UID handles
-class Manager {
-public:
-    /// Add a service to the manager
-    void AddService(Interface* service);
-
-    /// Removes a service from the manager
-    void DeleteService(const std::string& port_name);
-
-    /// Get a Service Interface from its Handle
-    Interface* FetchFromHandle(Handle handle);
-
-    /// Get a Service Interface from its port
-    Interface* FetchFromPortName(const std::string& port_name);
-
-private:
-    std::vector<Interface*>     m_services;
-    std::map<std::string, u32>  m_port_map;
-};
-
 /// Initialize ServiceManager
 void Init();
 
 /// Shutdown ServiceManager
 void Shutdown();
 
-
-extern Manager* g_manager; ///< Service manager
-
+/// Map of named ports managed by the kernel, which can be retrieved using the ConnectToPort SVC.
+extern std::unordered_map<std::string, Kernel::SharedPtr<Interface>> g_kernel_named_ports;
+/// Map of services registered with the "srv:" service, retrieved using GetServiceHandle.
+extern std::unordered_map<std::string, Kernel::SharedPtr<Interface>> g_srv_services;
 
 } // namespace
diff --git a/src/core/hle/service/srv.cpp b/src/core/hle/service/srv.cpp
index aa0aac3bb..c50aebf15 100644
--- a/src/core/hle/service/srv.cpp
+++ b/src/core/hle/service/srv.cpp
@@ -35,10 +35,10 @@ static void GetServiceHandle(Service::Interface* self) {
     u32* cmd_buff = Kernel::GetCommandBuffer();
 
     std::string port_name = std::string((const char*)&cmd_buff[1], 0, Service::kMaxPortSize);
-    Service::Interface* service = Service::g_manager->FetchFromPortName(port_name);
+    auto it = Service::g_srv_services.find(port_name);
 
-    if (nullptr != service) {
-        cmd_buff[3] = service->GetHandle();
+    if (it != Service::g_srv_services.end()) {
+        cmd_buff[3] = Kernel::g_handle_table.Create(it->second).MoveFrom();
         LOG_TRACE(Service_SRV, "called port=%s, handle=0x%08X", port_name.c_str(), cmd_buff[3]);
     } else {
         LOG_ERROR(Service_SRV, "(UNIMPLEMENTED) called port=%s", port_name.c_str());
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp
index 88813c2ce..d253f4fe5 100644
--- a/src/core/hle/svc.cpp
+++ b/src/core/hle/svc.cpp
@@ -30,6 +30,11 @@ using Kernel::ERR_INVALID_HANDLE;
 
 namespace SVC {
 
+const ResultCode ERR_NOT_FOUND(ErrorDescription::NotFound, ErrorModule::Kernel,
+        ErrorSummary::NotFound, ErrorLevel::Permanent); // 0xD88007FA
+const ResultCode ERR_PORT_NAME_TOO_LONG(ErrorDescription(30), ErrorModule::OS,
+        ErrorSummary::InvalidArgument, ErrorLevel::Usage); // 0xE0E0181E
+
 /// An invalid result code that is meant to be overwritten when a thread resumes from waiting
 const ResultCode RESULT_INVALID(0xDEADC0DE);
 
@@ -94,14 +99,21 @@ static ResultCode MapMemoryBlock(Handle handle, u32 addr, u32 permissions, u32 o
 }
 
 /// Connect to an OS service given the port name, returns the handle to the port to out
-static ResultCode ConnectToPort(Handle* out, const char* port_name) {
-    Service::Interface* service = Service::g_manager->FetchFromPortName(port_name);
+static ResultCode ConnectToPort(Handle* out_handle, const char* port_name) {
+    if (port_name == nullptr)
+        return ERR_NOT_FOUND;
+    if (std::strlen(port_name) > 11)
+        return ERR_PORT_NAME_TOO_LONG;
 
     LOG_TRACE(Kernel_SVC, "called port_name=%s", port_name);
-    _assert_msg_(KERNEL, (service != nullptr), "called, but service is not implemented!");
 
-    *out = service->GetHandle();
+    auto it = Service::g_kernel_named_ports.find(port_name);
+    if (it == Service::g_kernel_named_ports.end()) {
+        LOG_WARNING(Kernel_SVC, "tried to connect to unknown port: %s", port_name);
+        return ERR_NOT_FOUND;
+    }
 
+    CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(it->second));
     return RESULT_SUCCESS;
 }