mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
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:
parent
9b1ced7d3b
commit
e80be498e5
19 changed files with 541 additions and 99 deletions
|
@ -1,7 +1,9 @@
|
|||
Name,Index
|
||||
Fs,2
|
||||
Loader,9
|
||||
Sf,10
|
||||
Kvdb,20
|
||||
Sm,21
|
||||
Sdmmc,24
|
||||
Bcat,122
|
||||
LibHac,428
|
|
|
@ -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
|
|
|
@ -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,
|
||||
|
||||
|
|
|
20
src/LibHac/ApplicationId.cs
Normal file
20
src/LibHac/ApplicationId.cs
Normal 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);
|
||||
}
|
||||
}
|
13
src/LibHac/Arp/ApplicationLaunchProperty.cs
Normal file
13
src/LibHac/Arp/ApplicationLaunchProperty.cs
Normal 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;
|
||||
}
|
||||
}
|
67
src/LibHac/Arp/ArpClient.cs
Normal file
67
src/LibHac/Arp/ArpClient.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
12
src/LibHac/Arp/Impl/IReader.cs
Normal file
12
src/LibHac/Arp/Impl/IReader.cs
Normal 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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
33
src/LibHac/HorizonClient.cs
Normal file
33
src/LibHac/HorizonClient.cs
Normal 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
55
src/LibHac/Sf/ResultSf.cs
Normal 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
37
src/LibHac/Sm/ResultSm.cs
Normal 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);
|
||||
}
|
||||
}
|
96
src/LibHac/Sm/ServiceManager.cs
Normal file
96
src/LibHac/Sm/ServiceManager.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
42
src/LibHac/Sm/ServiceManagerClient.cs
Normal file
42
src/LibHac/Sm/ServiceManagerClient.cs
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
41
src/LibHac/Sm/ServiceName.cs
Normal file
41
src/LibHac/Sm/ServiceName.cs
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue