From 6d28b643126ee5de476a89f3ff94b5cc3ead3970 Mon Sep 17 00:00:00 2001
From: Ac_K <Acoustik666@gmail.com>
Date: Fri, 5 Apr 2024 20:45:43 +0200
Subject: [PATCH] ts: Migrate service to Horizon project (#6514)

* ts: Migrate service to Horizon project

This PR migrate the `ts` service (stored in `ptm`) to the Horizon project:
- It stubs all known IPCs.
- IpcServer consts are checked by RE.

Closes #6480

* Fix args
---
 .../HOS/Services/Ptm/Ts/IMeasurementServer.cs | 39 ------------
 .../Ptm/Ipc/MeasurementServer.cs              | 63 +++++++++++++++++++
 src/Ryujinx.Horizon/Ptm/Ipc/Session.cs        | 47 ++++++++++++++
 src/Ryujinx.Horizon/Ptm/TsIpcServer.cs        | 44 +++++++++++++
 src/Ryujinx.Horizon/Ptm/TsMain.cs             | 17 +++++
 src/Ryujinx.Horizon/Sdk/Ts/DeviceCode.cs      |  8 +++
 .../Sdk/Ts/IMeasurementServer.cs              | 14 +++++
 src/Ryujinx.Horizon/Sdk/Ts/ISession.cs        | 12 ++++
 .../Sdk/Ts}/Location.cs                       |  2 +-
 src/Ryujinx.Horizon/ServiceTable.cs           |  2 +
 10 files changed, 208 insertions(+), 40 deletions(-)
 delete mode 100644 src/Ryujinx.HLE/HOS/Services/Ptm/Ts/IMeasurementServer.cs
 create mode 100644 src/Ryujinx.Horizon/Ptm/Ipc/MeasurementServer.cs
 create mode 100644 src/Ryujinx.Horizon/Ptm/Ipc/Session.cs
 create mode 100644 src/Ryujinx.Horizon/Ptm/TsIpcServer.cs
 create mode 100644 src/Ryujinx.Horizon/Ptm/TsMain.cs
 create mode 100644 src/Ryujinx.Horizon/Sdk/Ts/DeviceCode.cs
 create mode 100644 src/Ryujinx.Horizon/Sdk/Ts/IMeasurementServer.cs
 create mode 100644 src/Ryujinx.Horizon/Sdk/Ts/ISession.cs
 rename src/{Ryujinx.HLE/HOS/Services/Ptm/Ts/Types => Ryujinx.Horizon/Sdk/Ts}/Location.cs (61%)

diff --git a/src/Ryujinx.HLE/HOS/Services/Ptm/Ts/IMeasurementServer.cs b/src/Ryujinx.HLE/HOS/Services/Ptm/Ts/IMeasurementServer.cs
deleted file mode 100644
index 66ffd0a49e..0000000000
--- a/src/Ryujinx.HLE/HOS/Services/Ptm/Ts/IMeasurementServer.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-using Ryujinx.Common.Logging;
-using Ryujinx.HLE.HOS.Services.Ptm.Ts.Types;
-
-namespace Ryujinx.HLE.HOS.Services.Ptm.Ts
-{
-    [Service("ts")]
-    class IMeasurementServer : IpcService
-    {
-        private const uint DefaultTemperature = 42u;
-
-        public IMeasurementServer(ServiceCtx context) { }
-
-        [CommandCmif(1)]
-        // GetTemperature(Location location) -> u32
-        public ResultCode GetTemperature(ServiceCtx context)
-        {
-            Location location = (Location)context.RequestData.ReadByte();
-
-            Logger.Stub?.PrintStub(LogClass.ServicePtm, new { location });
-
-            context.ResponseData.Write(DefaultTemperature);
-
-            return ResultCode.Success;
-        }
-
-        [CommandCmif(3)]
-        // GetTemperatureMilliC(Location location) -> u32
-        public ResultCode GetTemperatureMilliC(ServiceCtx context)
-        {
-            Location location = (Location)context.RequestData.ReadByte();
-
-            Logger.Stub?.PrintStub(LogClass.ServicePtm, new { location });
-
-            context.ResponseData.Write(DefaultTemperature * 1000);
-
-            return ResultCode.Success;
-        }
-    }
-}
diff --git a/src/Ryujinx.Horizon/Ptm/Ipc/MeasurementServer.cs b/src/Ryujinx.Horizon/Ptm/Ipc/MeasurementServer.cs
new file mode 100644
index 0000000000..ce7c0474a7
--- /dev/null
+++ b/src/Ryujinx.Horizon/Ptm/Ipc/MeasurementServer.cs
@@ -0,0 +1,63 @@
+using Ryujinx.Common.Logging;
+using Ryujinx.Horizon.Common;
+using Ryujinx.Horizon.Sdk.Sf;
+using Ryujinx.Horizon.Sdk.Ts;
+using Ryujinx.Horizon.Ts.Ipc;
+
+namespace Ryujinx.Horizon.Ptm.Ipc
+{
+    partial class MeasurementServer : IMeasurementServer
+    {
+        // NOTE: Values are randomly choosen.
+        public const int DefaultTemperature = 42;
+        public const int MinimumTemperature = 0;
+        public const int MaximumTemperature = 100;
+
+        [CmifCommand(0)] // 1.0.0-16.1.0
+        public Result GetTemperatureRange(out int minimumTemperature, out int maximumTemperature, Location location)
+        {
+            Logger.Stub?.PrintStub(LogClass.ServicePtm, new { location });
+
+            minimumTemperature = MinimumTemperature;
+            maximumTemperature = MaximumTemperature;
+
+            return Result.Success;
+        }
+
+        [CmifCommand(1)] // 1.0.0-16.1.0
+        public Result GetTemperature(out int temperature, Location location)
+        {
+            Logger.Stub?.PrintStub(LogClass.ServicePtm, new { location });
+
+            temperature = DefaultTemperature;
+
+            return Result.Success;
+        }
+
+        [CmifCommand(2)] // 1.0.0-13.2.1
+        public Result SetMeasurementMode(Location location, byte measurementMode)
+        {
+            Logger.Stub?.PrintStub(LogClass.ServicePtm, new { location, measurementMode });
+
+            return Result.Success;
+        }
+
+        [CmifCommand(3)] // 1.0.0-13.2.1
+        public Result GetTemperatureMilliC(out int temperatureMilliC, Location location)
+        {
+            Logger.Stub?.PrintStub(LogClass.ServicePtm, new { location });
+
+            temperatureMilliC = DefaultTemperature * 1000;
+
+            return Result.Success;
+        }
+
+        [CmifCommand(4)] // 8.0.0+
+        public Result OpenSession(out ISession session, DeviceCode deviceCode)
+        {
+            session = new Session(deviceCode);
+
+            return Result.Success;
+        }
+    }
+}
diff --git a/src/Ryujinx.Horizon/Ptm/Ipc/Session.cs b/src/Ryujinx.Horizon/Ptm/Ipc/Session.cs
new file mode 100644
index 0000000000..191a4b3af6
--- /dev/null
+++ b/src/Ryujinx.Horizon/Ptm/Ipc/Session.cs
@@ -0,0 +1,47 @@
+using Ryujinx.Common.Logging;
+using Ryujinx.Horizon.Common;
+using Ryujinx.Horizon.Ptm.Ipc;
+using Ryujinx.Horizon.Sdk.Sf;
+using Ryujinx.Horizon.Sdk.Ts;
+
+namespace Ryujinx.Horizon.Ts.Ipc
+{
+    partial class Session : ISession
+    {
+        private readonly DeviceCode _deviceCode;
+
+        public Session(DeviceCode deviceCode)
+        {
+            _deviceCode = deviceCode;
+        }
+
+        [CmifCommand(0)]
+        public Result GetTemperatureRange(out int minimumTemperature, out int maximumTemperature)
+        {
+            Logger.Stub?.PrintStub(LogClass.ServicePtm, new { _deviceCode });
+
+            minimumTemperature = MeasurementServer.MinimumTemperature;
+            maximumTemperature = MeasurementServer.MaximumTemperature;
+
+            return Result.Success;
+        }
+
+        [CmifCommand(2)]
+        public Result SetMeasurementMode(byte measurementMode)
+        {
+            Logger.Stub?.PrintStub(LogClass.ServicePtm, new { _deviceCode, measurementMode });
+
+            return Result.Success;
+        }
+
+        [CmifCommand(4)]
+        public Result GetTemperature(out int temperature)
+        {
+            Logger.Stub?.PrintStub(LogClass.ServicePtm, new { _deviceCode });
+
+            temperature = MeasurementServer.DefaultTemperature;
+
+            return Result.Success;
+        }
+    }
+}
diff --git a/src/Ryujinx.Horizon/Ptm/TsIpcServer.cs b/src/Ryujinx.Horizon/Ptm/TsIpcServer.cs
new file mode 100644
index 0000000000..db25d8e2e9
--- /dev/null
+++ b/src/Ryujinx.Horizon/Ptm/TsIpcServer.cs
@@ -0,0 +1,44 @@
+using Ryujinx.Horizon.Ptm.Ipc;
+using Ryujinx.Horizon.Sdk.Sf.Hipc;
+using Ryujinx.Horizon.Sdk.Sm;
+
+namespace Ryujinx.Horizon.Ptm
+{
+    class TsIpcServer
+    {
+        private const int MaxSessionsCount = 4;
+
+        private const int PointerBufferSize = 0;
+        private const int MaxDomains = 0;
+        private const int MaxDomainObjects = 0;
+        private const int MaxPortsCount = 1;
+
+        private static readonly ManagerOptions _managerOptions = new(PointerBufferSize, MaxDomains, MaxDomainObjects, false);
+
+        private SmApi _sm;
+        private ServerManager _serverManager;
+
+        public void Initialize()
+        {
+            HeapAllocator allocator = new();
+
+            _sm = new SmApi();
+            _sm.Initialize().AbortOnFailure();
+
+            _serverManager = new ServerManager(allocator, _sm, MaxPortsCount, _managerOptions, MaxSessionsCount);
+
+            _serverManager.RegisterObjectForServer(new MeasurementServer(), ServiceName.Encode("ts"), MaxSessionsCount);
+        }
+
+        public void ServiceRequests()
+        {
+            _serverManager.ServiceRequests();
+        }
+
+        public void Shutdown()
+        {
+            _serverManager.Dispose();
+            _sm.Dispose();
+        }
+    }
+}
diff --git a/src/Ryujinx.Horizon/Ptm/TsMain.cs b/src/Ryujinx.Horizon/Ptm/TsMain.cs
new file mode 100644
index 0000000000..237d52cd4c
--- /dev/null
+++ b/src/Ryujinx.Horizon/Ptm/TsMain.cs
@@ -0,0 +1,17 @@
+namespace Ryujinx.Horizon.Ptm
+{
+    class TsMain : IService
+    {
+        public static void Main(ServiceTable serviceTable)
+        {
+            TsIpcServer ipcServer = new();
+
+            ipcServer.Initialize();
+
+            serviceTable.SignalServiceReady();
+
+            ipcServer.ServiceRequests();
+            ipcServer.Shutdown();
+        }
+    }
+}
diff --git a/src/Ryujinx.Horizon/Sdk/Ts/DeviceCode.cs b/src/Ryujinx.Horizon/Sdk/Ts/DeviceCode.cs
new file mode 100644
index 0000000000..4fce4238ee
--- /dev/null
+++ b/src/Ryujinx.Horizon/Sdk/Ts/DeviceCode.cs
@@ -0,0 +1,8 @@
+namespace Ryujinx.Horizon.Sdk.Ts
+{
+    enum DeviceCode : uint
+    {
+        Internal = 0x41000001,
+        External = 0x41000002,
+    }
+}
diff --git a/src/Ryujinx.Horizon/Sdk/Ts/IMeasurementServer.cs b/src/Ryujinx.Horizon/Sdk/Ts/IMeasurementServer.cs
new file mode 100644
index 0000000000..ba9c2a748d
--- /dev/null
+++ b/src/Ryujinx.Horizon/Sdk/Ts/IMeasurementServer.cs
@@ -0,0 +1,14 @@
+using Ryujinx.Horizon.Common;
+using Ryujinx.Horizon.Sdk.Sf;
+
+namespace Ryujinx.Horizon.Sdk.Ts
+{
+    interface IMeasurementServer : IServiceObject
+    {
+        Result GetTemperatureRange(out int minimumTemperature, out int maximumTemperature, Location location);
+        Result GetTemperature(out int temperature, Location location);
+        Result SetMeasurementMode(Location location, byte measurementMode);
+        Result GetTemperatureMilliC(out int temperatureMilliC, Location location);
+        Result OpenSession(out ISession session, DeviceCode deviceCode);
+    }
+}
diff --git a/src/Ryujinx.Horizon/Sdk/Ts/ISession.cs b/src/Ryujinx.Horizon/Sdk/Ts/ISession.cs
new file mode 100644
index 0000000000..23c0d94f64
--- /dev/null
+++ b/src/Ryujinx.Horizon/Sdk/Ts/ISession.cs
@@ -0,0 +1,12 @@
+using Ryujinx.Horizon.Common;
+using Ryujinx.Horizon.Sdk.Sf;
+
+namespace Ryujinx.Horizon.Sdk.Ts
+{
+    interface ISession : IServiceObject
+    {
+        Result GetTemperatureRange(out int minimumTemperature, out int maximumTemperature);
+        Result GetTemperature(out int temperature);
+        Result SetMeasurementMode(byte measurementMode);
+    }
+}
diff --git a/src/Ryujinx.HLE/HOS/Services/Ptm/Ts/Types/Location.cs b/src/Ryujinx.Horizon/Sdk/Ts/Location.cs
similarity index 61%
rename from src/Ryujinx.HLE/HOS/Services/Ptm/Ts/Types/Location.cs
rename to src/Ryujinx.Horizon/Sdk/Ts/Location.cs
index 409188a979..177b0ee880 100644
--- a/src/Ryujinx.HLE/HOS/Services/Ptm/Ts/Types/Location.cs
+++ b/src/Ryujinx.Horizon/Sdk/Ts/Location.cs
@@ -1,4 +1,4 @@
-namespace Ryujinx.HLE.HOS.Services.Ptm.Ts.Types
+namespace Ryujinx.Horizon.Sdk.Ts
 {
     enum Location : byte
     {
diff --git a/src/Ryujinx.Horizon/ServiceTable.cs b/src/Ryujinx.Horizon/ServiceTable.cs
index b81e62a474..28c43a716f 100644
--- a/src/Ryujinx.Horizon/ServiceTable.cs
+++ b/src/Ryujinx.Horizon/ServiceTable.cs
@@ -11,6 +11,7 @@ using Ryujinx.Horizon.Ngc;
 using Ryujinx.Horizon.Ovln;
 using Ryujinx.Horizon.Prepo;
 using Ryujinx.Horizon.Psc;
+using Ryujinx.Horizon.Ptm;
 using Ryujinx.Horizon.Sdk.Arp;
 using Ryujinx.Horizon.Srepo;
 using Ryujinx.Horizon.Usb;
@@ -54,6 +55,7 @@ namespace Ryujinx.Horizon
             RegisterService<PrepoMain>();
             RegisterService<PscMain>();
             RegisterService<SrepoMain>();
+            RegisterService<TsMain>();
             RegisterService<UsbMain>();
             RegisterService<WlanMain>();