Add a simple service manager and HorizonClient class

Modifies bdat to use the HorizonClient and adds an ArpClient. The arp server still needs to be provided.
This commit is contained in:
Alex Barney 2020-04-23 17:49:15 -07:00
parent 9b1ced7d3b
commit e80be498e5
19 changed files with 541 additions and 99 deletions

View file

@ -1,7 +1,9 @@
Name,Index
Fs,2
Loader,9
Sf,10
Kvdb,20
Sm,21
Sdmmc,24
Bcat,122
LibHac,428
1 Name Index
2 Fs 2
3 Loader 9
4 Sf 10
5 Kvdb 20
6 Sm 21
7 Sdmmc 24
8 Bcat 122
9 LibHac 428

View file

@ -1,7 +1,9 @@
Name,Namespace,Path
Fs,LibHac.Fs,LibHac/Fs/ResultFs.cs
Loader,LibHac.Loader,LibHac/Loader/ResultLoader.cs
Sf,LibHac.Sf,LibHac/Sf/ResultSf.cs
Kvdb,LibHac.Kvdb,LibHac/Kvdb/ResultKvdb.cs
Sm,LibHac.Sm,LibHac/Sm/ResultSm.cs
Sdmmc,LibHac.FsService,LibHac/FsService/ResultSdmmc.cs
Bcat,LibHac.Bcat,LibHac/Bcat/ResultBcat.cs
LibHac,LibHac.Common,LibHac/Common/ResultLibHac.cs
1 Name Namespace Path
2 Fs LibHac.Fs LibHac/Fs/ResultFs.cs
3 Loader LibHac.Loader LibHac/Loader/ResultLoader.cs
4 Sf LibHac.Sf LibHac/Sf/ResultSf.cs
5 Kvdb LibHac.Kvdb LibHac/Kvdb/ResultKvdb.cs
6 Sm LibHac.Sm LibHac/Sm/ResultSm.cs
7 Sdmmc LibHac.FsService LibHac/FsService/ResultSdmmc.cs
8 Bcat LibHac.Bcat LibHac/Bcat/ResultBcat.cs
9 LibHac LibHac.Common LibHac/Common/ResultLibHac.cs

View file

@ -268,12 +268,44 @@ Module,DescriptionStart,DescriptionEnd,Name,Summary
9,200,,InternalError,
10,1,,NotSupported,
10,3,,PreconditionViolation,
# These should be in the sf::cmif namespace
10,202,,InvalidHeaderSize,
10,211,,InvalidInHeader,
10,221,,UnknownCommandId,
10,232,,InvalidOutRawSize,
10,235,,InvalidNumInObjects,
10,236,,InvalidNumOutObjects,
10,239,,InvalidInObject,
10,261,,TargetNotFound,
10,301,,OutOfDomainEntries,
# These should be in the sf::impl namespace
10,800,899,RequestContextChanged,
10,801,809,RequestInvalidated,
10,802,,RequestInvalidatedByUser,
10,811,819,RequestDeferred,
10,812,,RequestDeferredByUser,
20,1,,TooLargeKeyOrDbFull,
20,2,,KeyNotFound,
20,4,,AllocationFailed,
20,5,,InvalidKeyValue,
20,6,,BufferInsufficient,
21,1,,OutOfProcesses,
21,2,,InvalidClient,
21,3,,OutOfSessions,
21,4,,AlreadyRegistered,
21,5,,OutOfServices,
21,6,,InvalidServiceName,
21,7,,NotRegistered,
21,8,,NotAllowed,
21,9,,TooLargeAccessControl,
24,1,,DeviceNotFound,
24,4,,DeviceAsleep,

1 Module,DescriptionStart,DescriptionEnd,Name,Summary
268 205,122,139,IrsensorDeviceError, 122,10,,SaveDataNotFound,
269 428,1,49,InvalidArgument, 122,31,,NetworkServiceAccountNotAvailable,
270 428,2,,NullArgument, 122,80,,PassphrasePathNotFound,
271 122,90,,PermissionDenied,
272 122,91,,AllocationFailed,
273 122,204,,InvalidDeliveryCacheStorageFile,
274 122,205,,StorageOpenLimitReached,
275 123,0,4999,SslService,
276 124,0,,Cancelled,
277 124,1,,CancelledByUser,
278 124,100,,UserNotExist,
279 124,200,269,NetworkServiceAccountUnavailable,
280 124,430,499,TokenCacheUnavailable,
281 124,3000,8191,NetworkCommunicationError,
282 202,140,149,Invalid,
283 202,601,,DualConnected,
284 202,602,,SameJoyTypeConnected,
285 202,603,,ColorNotAvailable,
286 202,604,,ControllerNotConnected,
287 202,3101,,Canceled,
288 202,3102,,NotSupportedNpadStyle,
289 202,3200,3209,ControllerFirmwareUpdateError,
290 202,3201,,ControllerFirmwareUpdateFailed,
291 205,110,119,IrsensorUnavailable,
292 205,110,,IrsensorUnconnected,
293 428,3,,ArgumentOutOfRange, 205,111,,IrsensorUnsupported,
294 428,4,,BufferTooSmall, 205,120,,IrsensorNotReady,
295 428,51,,ServiceNotInitialized, 205,122,139,IrsensorDeviceError,
296 428,1000,1999,InvalidData, 428,1,49,InvalidArgument,
297 428,1001,1019,InvalidKip, 428,2,,NullArgument,
298 428,1002,,InvalidKipFileSize,The size of the KIP file was smaller than expected. 428,3,,ArgumentOutOfRange,
299 428,4,,BufferTooSmall,
300 428,51,,ServiceNotInitialized,
301 428,1000,1999,InvalidData,
302 428,1001,1019,InvalidKip,
303 428,1002,,InvalidKipFileSize,The size of the KIP file was smaller than expected.
304 428,1003,,InvalidKipMagic,The magic value of the KIP file was not KIP1.
305 428,1004,,InvalidKipSegmentSize,The size of the compressed KIP segment was smaller than expected.
306 428,1005,,KipSegmentDecompressionFailed,An error occurred while decompressing a KIP segment.
307
308
309
310
311

View file

@ -0,0 +1,20 @@
using System;
namespace LibHac
{
public readonly struct ApplicationId : IEquatable<ApplicationId>
{
public static ApplicationId InvalidId => default;
public readonly ulong Value;
public ApplicationId(ulong value)
{
Value = value;
}
public override bool Equals(object obj) => obj is ApplicationId id && Equals(id);
public bool Equals(ApplicationId other) => Value == other.Value;
public override int GetHashCode() => HashCode.Combine(Value);
}
}

View file

@ -0,0 +1,13 @@
using System.Runtime.InteropServices;
namespace LibHac.Arp
{
[StructLayout(LayoutKind.Explicit, Size = 0x10)]
public struct ApplicationLaunchProperty
{
[FieldOffset(0x0)] public ApplicationId ApplicationId;
[FieldOffset(0x8)] public uint Version;
[FieldOffset(0xC)] public Ncm.StorageId BaseStorageId;
[FieldOffset(0xD)] public Ncm.StorageId UpdateStorageId;
}
}

View file

@ -0,0 +1,67 @@
using LibHac.Arp.Impl;
using LibHac.Ns;
namespace LibHac.Arp
{
public class ArpClient
{
private HorizonClient HosClient { get; }
private IReader Reader { get; set; }
private readonly object _readerInitLocker = new object();
internal ArpClient(HorizonClient horizonClient)
{
HosClient = horizonClient;
}
public Result GetApplicationLaunchProperty(out ApplicationLaunchProperty launchProperty, ulong processId)
{
EnsureReaderInitialized();
return Reader.GetApplicationLaunchProperty(out launchProperty, processId);
}
public Result GetApplicationLaunchProperty(out ApplicationLaunchProperty launchProperty, ApplicationId applicationId)
{
EnsureReaderInitialized();
return Reader.GetApplicationLaunchPropertyWithApplicationId(out launchProperty, applicationId);
}
public Result GetApplicationControlProperty(out ApplicationControlProperty controlProperty, ulong processId)
{
EnsureReaderInitialized();
return Reader.GetApplicationControlProperty(out controlProperty, processId);
}
public Result GetApplicationControlProperty(out ApplicationControlProperty controlProperty, ApplicationId applicationId)
{
EnsureReaderInitialized();
return Reader.GetApplicationControlPropertyWithApplicationId(out controlProperty, applicationId);
}
private void EnsureReaderInitialized()
{
if (Reader != null)
return;
lock (_readerInitLocker)
{
if (Reader != null)
return;
Result rc = HosClient.Sm.GetService(out IReader reader, "arp:r");
if (rc.IsFailure())
{
throw new HorizonResultException(rc, "Failed to initialize arp reader.");
}
Reader = reader;
}
}
}
}

View file

@ -0,0 +1,12 @@
using LibHac.Ns;
namespace LibHac.Arp.Impl
{
public interface IReader
{
Result GetApplicationLaunchProperty(out ApplicationLaunchProperty launchProperty, ulong processId);
Result GetApplicationLaunchPropertyWithApplicationId(out ApplicationLaunchProperty launchProperty, ApplicationId applicationId);
Result GetApplicationControlProperty(out ApplicationControlProperty controlProperty, ulong processId);
Result GetApplicationControlPropertyWithApplicationId(out ApplicationControlProperty controlProperty, ApplicationId applicationId);
}
}

View file

@ -2,7 +2,6 @@
using LibHac.Bcat.Detail.Ipc;
using LibHac.Bcat.Detail.Service;
using LibHac.Bcat.Detail.Service.Core;
using LibHac.Common;
using LibHac.Fs;
namespace LibHac.Bcat
@ -11,42 +10,52 @@ namespace LibHac.Bcat
{
private const int ServiceTypeCount = 4;
private Horizon ServiceManager { get; }
internal HorizonClient Hos { get; }
private ServiceCreator[] ServiceCreators { get; } = new ServiceCreator[ServiceTypeCount];
private readonly object _bcatServiceInitLocker = new object();
private readonly object _storageManagerInitLocker = new object();
private readonly object _fsInitLocker = new object();
private DeliveryCacheStorageManager StorageManager { get; set; }
private FileSystemClient FsClient { get; set; }
public BcatServer(Horizon horizon)
public BcatServer(HorizonClient horizonClient)
{
ServiceManager = horizon;
Hos = horizonClient;
InitServiceCreator(BcatServiceType.BcatU, AccessControl.Bit1);
InitServiceCreator(BcatServiceType.BcatS, AccessControl.Bit2);
InitServiceCreator(BcatServiceType.BcatM, AccessControl.Bit2 | AccessControl.Bit3);
InitServiceCreator(BcatServiceType.BcatA, AccessControl.All);
InitBcatService(BcatServiceType.BcatU, "bcat:u", AccessControl.MountOwnDeliveryCacheStorage);
InitBcatService(BcatServiceType.BcatS, "bcat:s", AccessControl.MountOthersDeliveryCacheStorage);
InitBcatService(BcatServiceType.BcatM, "bcat:m", AccessControl.MountOthersDeliveryCacheStorage | AccessControl.DeliveryTaskManagement);
InitBcatService(BcatServiceType.BcatA, "bcat:a", AccessControl.All);
}
public Result GetServiceCreator(out IServiceCreator serviceCreator, BcatServiceType type)
private void InitBcatService(BcatServiceType type, string name, AccessControl accessControl)
{
if ((uint)type >= ServiceTypeCount)
InitServiceCreator(type, name, accessControl);
IServiceCreator service = GetServiceCreator(type);
Result rc = Hos.Sm.RegisterService(service, name);
if (rc.IsFailure())
{
serviceCreator = default;
return ResultLibHac.ArgumentOutOfRange.Log();
throw new HorizonResultException(rc, "Abort");
}
serviceCreator = ServiceCreators[(int)type];
return Result.Success;
}
private void InitServiceCreator(BcatServiceType type, AccessControl accessControl)
private void InitServiceCreator(BcatServiceType type, string name, AccessControl accessControl)
{
lock (_bcatServiceInitLocker)
{
Debug.Assert((uint)type < ServiceTypeCount);
ServiceCreators[(int)type] = new ServiceCreator(this, name, accessControl);
}
}
private IServiceCreator GetServiceCreator(BcatServiceType type)
{
Debug.Assert((uint)type < ServiceTypeCount);
ServiceCreators[(int)type] = new ServiceCreator(this, type, accessControl);
return ServiceCreators[(int)type];
}
internal DeliveryCacheStorageManager GetStorageManager()
@ -56,7 +65,7 @@ namespace LibHac.Bcat
internal FileSystemClient GetFsClient()
{
return FsClient ?? InitFsClient();
return Hos.Fs;
}
private DeliveryCacheStorageManager InitStorageManager()
@ -72,26 +81,5 @@ namespace LibHac.Bcat
return StorageManager;
}
}
private FileSystemClient InitFsClient()
{
lock (_fsInitLocker)
{
if (FsClient != null)
{
return FsClient;
}
Result rc = ServiceManager.OpenFileSystemClient(out FileSystemClient fsClient);
if (!rc.IsSuccess())
throw new HorizonResultException(rc, "Abort");
fsClient.SetAccessLogTarget(AccessLogTarget.All);
FsClient = fsClient;
return fsClient;
}
}
}
}

View file

@ -4,7 +4,10 @@ namespace LibHac.Bcat.Detail.Ipc
{
public interface IServiceCreator
{
Result CreateDeliveryCacheStorageService(out IDeliveryCacheStorageService service,
ulong processId);
Result CreateDeliveryCacheStorageServiceWithApplicationId(out IDeliveryCacheStorageService service,
TitleId applicationId);
ApplicationId applicationId);
}
}

View file

@ -6,11 +6,10 @@ namespace LibHac.Bcat.Detail.Service
internal enum AccessControl
{
None = 0,
Bit0 = 1 << 0,
Bit1 = 1 << 1,
Bit2 = 1 << 2,
Bit3 = 1 << 3,
Bit4 = 1 << 4,
MountOwnDeliveryCacheStorage = 1 << 1,
MountOthersDeliveryCacheStorage = 1 << 2,
DeliveryTaskManagement = 1 << 3,
Debug = 1 << 4,
All = ~0
}
}

View file

@ -159,6 +159,7 @@ namespace LibHac.Bcat.Detail.Service.Core
public void GetPassphrasePath(Span<byte> pathBuffer, ulong applicationId)
{
// returns "mount:/passphrase.bin"
lock (_locker)
{
var sb = new U8StringBuilder(pathBuffer);
@ -169,6 +170,7 @@ namespace LibHac.Bcat.Detail.Service.Core
public void GetDeliveryListPath(Span<byte> pathBuffer, ulong applicationId)
{
// returns "mount:/list.msgpack"
lock (_locker)
{
var sb = new U8StringBuilder(pathBuffer);
@ -179,6 +181,7 @@ namespace LibHac.Bcat.Detail.Service.Core
public void GetEtagFilePath(Span<byte> pathBuffer, ulong applicationId)
{
// returns "mount:/etag.bin"
lock (_locker)
{
var sb = new U8StringBuilder(pathBuffer);
@ -189,6 +192,7 @@ namespace LibHac.Bcat.Detail.Service.Core
public void GetNaRequiredPath(Span<byte> pathBuffer, ulong applicationId)
{
// returns "mount:/na_required"
lock (_locker)
{
var sb = new U8StringBuilder(pathBuffer);
@ -199,6 +203,7 @@ namespace LibHac.Bcat.Detail.Service.Core
public void GetIndexLockPath(Span<byte> pathBuffer, ulong applicationId)
{
// returns "mount:/index.lock"
lock (_locker)
{
var sb = new U8StringBuilder(pathBuffer);
@ -210,6 +215,7 @@ namespace LibHac.Bcat.Detail.Service.Core
public void GetFilePath(Span<byte> pathBuffer, ulong applicationId, ref DirectoryName directoryName,
ref FileName fileName)
{
// returns "mount:/directories/%s/files/%s", directoryName, fileName
lock (_locker)
{
var sb = new U8StringBuilder(pathBuffer);
@ -224,6 +230,7 @@ namespace LibHac.Bcat.Detail.Service.Core
public void GetFilesMetaPath(Span<byte> pathBuffer, ulong applicationId, ref DirectoryName directoryName)
{
// returns "mount:/directories/%s/files.meta", directoryName
lock (_locker)
{
var sb = new U8StringBuilder(pathBuffer);
@ -237,6 +244,7 @@ namespace LibHac.Bcat.Detail.Service.Core
public void GetDirectoriesPath(Span<byte> pathBuffer, ulong applicationId)
{
// returns "mount:/directories"
lock (_locker)
{
var sb = new U8StringBuilder(pathBuffer);
@ -247,6 +255,7 @@ namespace LibHac.Bcat.Detail.Service.Core
public void GetDirectoryPath(Span<byte> pathBuffer, ulong applicationId, ref DirectoryName directoryName)
{
// returns "mount:/directories/%s", directoryName
lock (_locker)
{
var sb = new U8StringBuilder(pathBuffer);
@ -259,6 +268,7 @@ namespace LibHac.Bcat.Detail.Service.Core
public void GetDirectoriesMetaPath(Span<byte> pathBuffer, ulong applicationId)
{
// returns "mount:/directories.meta"
lock (_locker)
{
var sb = new U8StringBuilder(pathBuffer);
@ -304,9 +314,11 @@ namespace LibHac.Bcat.Detail.Service.Core
private int FindEntry(ulong applicationId)
{
for (int i = 0; i < Entries.Length; i++)
Entry[] entries = Entries;
for (int i = 0; i < entries.Length; i++)
{
if (Entries[i].ApplicationId == applicationId)
if (entries[i].ApplicationId == applicationId)
{
return i;
}

View file

@ -1,5 +1,5 @@
using LibHac.Bcat.Detail.Ipc;
using LibHac.Ncm;
using LibHac.Arp;
using LibHac.Bcat.Detail.Ipc;
namespace LibHac.Bcat.Detail.Service
{
@ -8,29 +8,44 @@ namespace LibHac.Bcat.Detail.Service
private BcatServer Server { get; }
// ReSharper disable once UnusedAutoPropertyAccessor.Local
private BcatServiceType ServiceType { get; }
private string ServiceName { get; }
private AccessControl AccessControl { get; }
public ServiceCreator(BcatServer server, BcatServiceType type, AccessControl accessControl)
public ServiceCreator(BcatServer server, string serviceName, AccessControl accessControl)
{
Server = server;
ServiceType = type;
ServiceName = serviceName;
AccessControl = accessControl;
}
public Result CreateDeliveryCacheStorageServiceWithApplicationId(out IDeliveryCacheStorageService service,
TitleId applicationId)
public Result CreateDeliveryCacheStorageService(out IDeliveryCacheStorageService service, ulong processId)
{
service = default;
Result rc = Server.Hos.Arp.GetApplicationLaunchProperty(out ApplicationLaunchProperty launchProperty,
processId);
if (!AccessControl.HasFlag(AccessControl.Bit2))
if (rc.IsFailure())
{
service = default;
return ResultBcat.NotFound.LogConverted(rc);
}
return CreateDeliveryCacheStorageServiceImpl(out service, launchProperty.ApplicationId);
}
public Result CreateDeliveryCacheStorageServiceWithApplicationId(out IDeliveryCacheStorageService service,
ApplicationId applicationId)
{
if (!AccessControl.HasFlag(AccessControl.MountOthersDeliveryCacheStorage))
{
service = default;
return ResultBcat.PermissionDenied.Log();
}
return CreateDeliveryCacheStorageServiceImpl(out service, applicationId);
}
private Result CreateDeliveryCacheStorageServiceImpl(out IDeliveryCacheStorageService service,
TitleId applicationId)
ApplicationId applicationId)
{
service = default;

View file

@ -1,38 +1,26 @@
using LibHac.Bcat;
using LibHac.Bcat.Detail.Ipc;
using LibHac.Common;
using LibHac.Common;
using LibHac.Fs;
using LibHac.FsService;
using LibHac.FsService.Creators;
using LibHac.Sm;
namespace LibHac
{
public class Horizon
{
internal ITimeSpanGenerator Time { get; }
public FileSystemServer FileSystemServer { get; private set; }
public BcatServer BcatServer { get; private set; }
private FileSystemServer FileSystemServer { get; set; }
internal ServiceManager ServiceManager { get; }
private readonly object _initLocker = new object();
public Horizon(ITimeSpanGenerator timer)
{
Time = timer ?? new StopWatchTimeSpanGenerator();
ServiceManager = new ServiceManager(this);
}
public Result OpenFileSystemProxyService(out IFileSystemProxy service)
{
if (FileSystemServer is null)
{
service = default;
return ResultLibHac.ServiceNotInitialized.Log();
}
service = FileSystemServer.CreateFileSystemProxyService();
return Result.Success;
}
public Result OpenFileSystemClient(out FileSystemClient client)
private Result OpenFileSystemClient(out FileSystemClient client)
{
if (FileSystemServer is null)
{
@ -44,32 +32,17 @@ namespace LibHac
return Result.Success;
}
public Result OpenBcatUService(out IServiceCreator service) => OpenBcatService(out service, BcatServiceType.BcatU);
public Result OpenBcatSService(out IServiceCreator service) => OpenBcatService(out service, BcatServiceType.BcatS);
public Result OpenBcatMService(out IServiceCreator service) => OpenBcatService(out service, BcatServiceType.BcatM);
public Result OpenBcatAService(out IServiceCreator service) => OpenBcatService(out service, BcatServiceType.BcatA);
private Result OpenBcatService(out IServiceCreator service, BcatServiceType type)
public Result CreateHorizonClient(out HorizonClient client)
{
if (BcatServer is null)
Result rc = OpenFileSystemClient(out FileSystemClient fsClient);
if (rc.IsFailure())
{
service = default;
return ResultLibHac.ServiceNotInitialized.Log();
client = default;
return rc;
}
return BcatServer.GetServiceCreator(out service, type);
}
public void InitializeBcatServer()
{
if (BcatServer != null) return;
lock (_initLocker)
{
if (BcatServer != null) return;
BcatServer = new BcatServer(this);
}
client = new HorizonClient(this, fsClient);
return Result.Success;
}
public void InitializeFileSystemServer(FileSystemCreators fsCreators, IDeviceOperator deviceOperator)

View file

@ -0,0 +1,33 @@
using System;
using LibHac.Arp;
using LibHac.Fs;
using LibHac.Sm;
namespace LibHac
{
public class HorizonClient
{
private Horizon Horizon { get; }
private Lazy<ArpClient> ArpLazy { get; }
public FileSystemClient Fs { get; }
public ServiceManagerClient Sm { get; }
public ArpClient Arp => ArpLazy.Value;
internal HorizonClient(Horizon horizon, FileSystemClient fsClient)
{
Horizon = horizon;
Fs = fsClient;
Sm = new ServiceManagerClient(horizon.ServiceManager);
ArpLazy = new Lazy<ArpClient>(InitArpClient, true);
}
private ArpClient InitArpClient()
{
return new ArpClient(this);
}
}
}

55
src/LibHac/Sf/ResultSf.cs Normal file
View file

@ -0,0 +1,55 @@
//-----------------------------------------------------------------------------
// This file was automatically generated.
// Changes to this file will be lost when the file is regenerated.
//
// To change this file, modify /build/CodeGen/results.csv at the root of this
// repo and run the build script.
//
// The script can be run with the "codegen" option to run only the
// code generation portion of the build.
//-----------------------------------------------------------------------------
using System.Runtime.CompilerServices;
namespace LibHac.Sf
{
public static class ResultSf
{
public const int ModuleSf = 10;
/// <summary>Error code: 2010-0001; Inner value: 0x20a</summary>
public static Result.Base NotSupported => new Result.Base(ModuleSf, 1);
/// <summary>Error code: 2010-0003; Inner value: 0x60a</summary>
public static Result.Base PreconditionViolation => new Result.Base(ModuleSf, 3);
/// <summary>Error code: 2010-0202; Inner value: 0x1940a</summary>
public static Result.Base InvalidHeaderSize => new Result.Base(ModuleSf, 202);
/// <summary>Error code: 2010-0211; Inner value: 0x1a60a</summary>
public static Result.Base InvalidInHeader => new Result.Base(ModuleSf, 211);
/// <summary>Error code: 2010-0221; Inner value: 0x1ba0a</summary>
public static Result.Base UnknownCommandId => new Result.Base(ModuleSf, 221);
/// <summary>Error code: 2010-0232; Inner value: 0x1d00a</summary>
public static Result.Base InvalidOutRawSize => new Result.Base(ModuleSf, 232);
/// <summary>Error code: 2010-0235; Inner value: 0x1d60a</summary>
public static Result.Base InvalidNumInObjects => new Result.Base(ModuleSf, 235);
/// <summary>Error code: 2010-0236; Inner value: 0x1d80a</summary>
public static Result.Base InvalidNumOutObjects => new Result.Base(ModuleSf, 236);
/// <summary>Error code: 2010-0239; Inner value: 0x1de0a</summary>
public static Result.Base InvalidInObject => new Result.Base(ModuleSf, 239);
/// <summary>Error code: 2010-0261; Inner value: 0x20a0a</summary>
public static Result.Base TargetNotFound => new Result.Base(ModuleSf, 261);
/// <summary>Error code: 2010-0301; Inner value: 0x25a0a</summary>
public static Result.Base OutOfDomainEntries => new Result.Base(ModuleSf, 301);
/// <summary>Error code: 2010-0800; Range: 800-899; Inner value: 0x6400a</summary>
public static Result.Base RequestContextChanged { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleSf, 800, 899); }
/// <summary>Error code: 2010-0801; Range: 801-809; Inner value: 0x6420a</summary>
public static Result.Base RequestInvalidated { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleSf, 801, 809); }
/// <summary>Error code: 2010-0802; Inner value: 0x6440a</summary>
public static Result.Base RequestInvalidatedByUser => new Result.Base(ModuleSf, 802);
/// <summary>Error code: 2010-0811; Range: 811-819; Inner value: 0x6560a</summary>
public static Result.Base RequestDeferred { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleSf, 811, 819); }
/// <summary>Error code: 2010-0812; Inner value: 0x6580a</summary>
public static Result.Base RequestDeferredByUser => new Result.Base(ModuleSf, 812);
}
}

37
src/LibHac/Sm/ResultSm.cs Normal file
View file

@ -0,0 +1,37 @@
//-----------------------------------------------------------------------------
// This file was automatically generated.
// Changes to this file will be lost when the file is regenerated.
//
// To change this file, modify /build/CodeGen/results.csv at the root of this
// repo and run the build script.
//
// The script can be run with the "codegen" option to run only the
// code generation portion of the build.
//-----------------------------------------------------------------------------
namespace LibHac.Sm
{
public static class ResultSm
{
public const int ModuleSm = 21;
/// <summary>Error code: 2021-0001; Inner value: 0x215</summary>
public static Result.Base OutOfProcesses => new Result.Base(ModuleSm, 1);
/// <summary>Error code: 2021-0002; Inner value: 0x415</summary>
public static Result.Base InvalidClient => new Result.Base(ModuleSm, 2);
/// <summary>Error code: 2021-0003; Inner value: 0x615</summary>
public static Result.Base OutOfSessions => new Result.Base(ModuleSm, 3);
/// <summary>Error code: 2021-0004; Inner value: 0x815</summary>
public static Result.Base AlreadyRegistered => new Result.Base(ModuleSm, 4);
/// <summary>Error code: 2021-0005; Inner value: 0xa15</summary>
public static Result.Base OutOfServices => new Result.Base(ModuleSm, 5);
/// <summary>Error code: 2021-0006; Inner value: 0xc15</summary>
public static Result.Base InvalidServiceName => new Result.Base(ModuleSm, 6);
/// <summary>Error code: 2021-0007; Inner value: 0xe15</summary>
public static Result.Base NotRegistered => new Result.Base(ModuleSm, 7);
/// <summary>Error code: 2021-0008; Inner value: 0x1015</summary>
public static Result.Base NotAllowed => new Result.Base(ModuleSm, 8);
/// <summary>Error code: 2021-0009; Inner value: 0x1215</summary>
public static Result.Base TooLargeAccessControl => new Result.Base(ModuleSm, 9);
}
}

View file

@ -0,0 +1,96 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using LibHac.Common;
using LibHac.Sf;
namespace LibHac.Sm
{
// This is basically a makeshift service manager that doesn't do anything
// other than keep service objects for now. It's just here so other stuff
// isn't blocked waiting for something better.
internal class ServiceManager
{
private Horizon Horizon { get; }
private Dictionary<ServiceName, object> Services { get; } = new Dictionary<ServiceName, object>();
public ServiceManager(Horizon horizon)
{
Horizon = horizon;
}
internal Result GetService(out object serviceObject, ServiceName serviceName)
{
serviceObject = default;
Result rc = ValidateServiceName(serviceName);
if (rc.IsFailure()) return rc;
if (!Services.TryGetValue(serviceName, out serviceObject))
{
return ResultSf.RequestDeferredByUser.Log();
}
return Result.Success;
}
internal Result RegisterService(object serviceObject, ServiceName serviceName)
{
Result rc = ValidateServiceName(serviceName);
if (rc.IsFailure()) return rc;
if (!Services.TryAdd(serviceName, serviceObject))
{
return ResultSm.AlreadyRegistered.Log();
}
return Result.Success;
}
internal Result UnregisterService(ServiceName serviceName)
{
Result rc = ValidateServiceName(serviceName);
if (rc.IsFailure()) return rc;
if (!Services.Remove(serviceName, out object service))
{
return ResultSm.NotRegistered.Log();
}
if (service is IDisposable disposable)
{
disposable.Dispose();
}
return Result.Success;
}
private Result ValidateServiceName(ServiceName name)
{
// Service names must be non-empty.
if (name.Name == 0)
return ResultSm.InvalidServiceName.Log();
// Get name length.
int nameLen;
for (nameLen = 1; nameLen < Unsafe.SizeOf<ServiceName>(); nameLen++)
{
if (SpanHelpers.AsReadOnlyByteSpan(ref name)[nameLen] == 0)
{
break;
}
}
// Names must be all-zero after they end.
for (; nameLen < Unsafe.SizeOf<ServiceName>(); nameLen++)
{
if (SpanHelpers.AsReadOnlyByteSpan(ref name)[nameLen] != 0)
{
return ResultSm.InvalidServiceName.Log();
}
}
return Result.Success;
}
}
}

View file

@ -0,0 +1,42 @@
using System;
namespace LibHac.Sm
{
public class ServiceManagerClient
{
private ServiceManager Server { get; }
internal ServiceManagerClient(ServiceManager server)
{
Server = server;
}
public Result GetService<T>(out T serviceObject, ReadOnlySpan<char> name)
{
Result rc = Server.GetService(out object service, ServiceName.Encode(name));
if (rc.IsFailure())
{
serviceObject = default;
return rc;
}
if (service is T typedService)
{
serviceObject = typedService;
return Result.Success;
}
throw new InvalidCastException("The service object is not of the specified type.");
}
public Result RegisterService(object serviceObject, ReadOnlySpan<char> name)
{
return Server.RegisterService(serviceObject, ServiceName.Encode(name));
}
public Result UnregisterService(ReadOnlySpan<char> name)
{
return Server.UnregisterService(ServiceName.Encode(name));
}
}
}

View file

@ -0,0 +1,41 @@
using System;
using System.Diagnostics;
using LibHac.Common;
namespace LibHac.Sm
{
[DebuggerDisplay("{ToString()}")]
public readonly struct ServiceName : IEquatable<ServiceName>
{
private const int MaxLength = 8;
public readonly ulong Name;
public static ServiceName Encode(ReadOnlySpan<char> name)
{
var outName = new ServiceName();
int length = Math.Min(MaxLength, name.Length);
for (int i = 0; i < length; i++)
{
SpanHelpers.AsByteSpan(ref outName)[i] = (byte)name[i];
}
return outName;
}
public override bool Equals(object obj) => obj is ServiceName name && Equals(name);
public bool Equals(ServiceName other) => Name == other.Name;
public override int GetHashCode() => Name.GetHashCode();
public static bool operator ==(ServiceName left, ServiceName right) => left.Equals(right);
public static bool operator !=(ServiceName left, ServiceName right) => !(left == right);
public override string ToString()
{
ulong name = Name;
return StringUtils.Utf8ZToString(SpanHelpers.AsReadOnlyByteSpan(ref name));
}
}
}