diff options
Diffstat (limited to 'src/Ryujinx.Horizon/Bcat')
13 files changed, 508 insertions, 0 deletions
diff --git a/src/Ryujinx.Horizon/Bcat/BcatIpcServer.cs b/src/Ryujinx.Horizon/Bcat/BcatIpcServer.cs new file mode 100644 index 00000000..4a5378af --- /dev/null +++ b/src/Ryujinx.Horizon/Bcat/BcatIpcServer.cs @@ -0,0 +1,48 @@ +using Ryujinx.Horizon.Bcat.Ipc; +using Ryujinx.Horizon.Bcat.Types; +using Ryujinx.Horizon.Sdk.Sf.Hipc; +using Ryujinx.Horizon.Sdk.Sm; + +namespace Ryujinx.Horizon.Bcat +{ + internal class BcatIpcServer + { + private const int BcatMaxSessionsCount = 8; + private const int BcatTotalMaxSessionsCount = BcatMaxSessionsCount * 4; + + private const int PointerBufferSize = 0x400; + private const int MaxDomains = 64; + private const int MaxDomainObjects = 64; + private const int MaxPortsCount = 4; + + private SmApi _sm; + private BcatServerManager _serverManager; + + private static readonly ManagerOptions _bcatManagerOptions = new(PointerBufferSize, MaxDomains, MaxDomainObjects, false); + + internal void Initialize() + { + HeapAllocator allocator = new(); + + _sm = new SmApi(); + _sm.Initialize().AbortOnFailure(); + + _serverManager = new BcatServerManager(allocator, _sm, MaxPortsCount, _bcatManagerOptions, BcatTotalMaxSessionsCount); + + _serverManager.RegisterServer((int)BcatPortIndex.Admin, ServiceName.Encode("bcat:a"), BcatMaxSessionsCount); + _serverManager.RegisterServer((int)BcatPortIndex.Manager, ServiceName.Encode("bcat:m"), BcatMaxSessionsCount); + _serverManager.RegisterServer((int)BcatPortIndex.User, ServiceName.Encode("bcat:u"), BcatMaxSessionsCount); + _serverManager.RegisterServer((int)BcatPortIndex.System, ServiceName.Encode("bcat:s"), BcatMaxSessionsCount); + } + + public void ServiceRequests() + { + _serverManager.ServiceRequests(); + } + + public void Shutdown() + { + _serverManager.Dispose(); + } + } +} diff --git a/src/Ryujinx.Horizon/Bcat/BcatMain.cs b/src/Ryujinx.Horizon/Bcat/BcatMain.cs new file mode 100644 index 00000000..d4166fb1 --- /dev/null +++ b/src/Ryujinx.Horizon/Bcat/BcatMain.cs @@ -0,0 +1,24 @@ +using Ryujinx.Horizon.LogManager; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Ryujinx.Horizon.Bcat +{ + internal class BcatMain : IService + { + public static void Main(ServiceTable serviceTable) + { + BcatIpcServer ipcServer = new(); + + ipcServer.Initialize(); + + serviceTable.SignalServiceReady(); + + ipcServer.ServiceRequests(); + ipcServer.Shutdown(); + } + } +} diff --git a/src/Ryujinx.Horizon/Bcat/BcatResult.cs b/src/Ryujinx.Horizon/Bcat/BcatResult.cs new file mode 100644 index 00000000..014c52e7 --- /dev/null +++ b/src/Ryujinx.Horizon/Bcat/BcatResult.cs @@ -0,0 +1,29 @@ +using Ryujinx.Horizon.Common; + +namespace Ryujinx.Horizon.Bcat +{ + class BcatResult + { + private const int ModuleId = 122; + + public static Result Success => new(ModuleId, 0); + public static Result InvalidArgument => new(ModuleId, 1); + public static Result NotFound => new(ModuleId, 2); + public static Result TargetLocked => new(ModuleId, 3); + public static Result TargetAlreadyMounted => new(ModuleId, 4); + public static Result TargetNotMounted => new(ModuleId, 5); + public static Result AlreadyOpen => new(ModuleId, 6); + public static Result NotOpen => new(ModuleId, 7); + public static Result InternetRequestDenied => new(ModuleId, 8); + public static Result ServiceOpenLimitReached => new(ModuleId, 9); + public static Result SaveDataNotFound => new(ModuleId, 10); + public static Result NetworkServiceAccountNotAvailable => new(ModuleId, 31); + public static Result PassphrasePathNotFound => new(ModuleId, 80); + public static Result DataVerificationFailed => new(ModuleId, 81); + public static Result PermissionDenied => new(ModuleId, 90); + public static Result AllocationFailed => new(ModuleId, 91); + public static Result InvalidOperation => new(ModuleId, 98); + public static Result InvalidDeliveryCacheStorageFile => new(ModuleId, 204); + public static Result StorageOpenLimitReached => new(ModuleId, 205); + } +} diff --git a/src/Ryujinx.Horizon/Bcat/BcatServerManager.cs b/src/Ryujinx.Horizon/Bcat/BcatServerManager.cs new file mode 100644 index 00000000..953f6a22 --- /dev/null +++ b/src/Ryujinx.Horizon/Bcat/BcatServerManager.cs @@ -0,0 +1,28 @@ +using Ryujinx.Horizon.Bcat.Ipc; +using Ryujinx.Horizon.Bcat.Types; +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Sf.Hipc; +using Ryujinx.Horizon.Sdk.Sm; +using System; + +namespace Ryujinx.Horizon.Bcat +{ + class BcatServerManager : ServerManager + { + public BcatServerManager(HeapAllocator allocator, SmApi sm, int maxPorts, ManagerOptions options, int maxSessions) : base(allocator, sm, maxPorts, options, maxSessions) + { + } + + protected override Result OnNeedsToAccept(int portIndex, Server server) + { + return (BcatPortIndex)portIndex switch + { + BcatPortIndex.Admin => AcceptImpl(server, new BcatService(BcatServicePermissionLevel.Admin)), + BcatPortIndex.Manager => AcceptImpl(server, new BcatService(BcatServicePermissionLevel.Manager)), + BcatPortIndex.User => AcceptImpl(server, new BcatService(BcatServicePermissionLevel.User)), + BcatPortIndex.System => AcceptImpl(server, new BcatService(BcatServicePermissionLevel.System)), + _ => throw new ArgumentOutOfRangeException(nameof(portIndex)), + }; + } + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator.cs b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator.cs new file mode 100644 index 00000000..d1610f7d --- /dev/null +++ b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator.cs @@ -0,0 +1,82 @@ +using LibHac.Common; +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Bcat; +using Ryujinx.Horizon.Sdk.Sf; +using System; +using System.Threading; +using ApplicationId = Ryujinx.Horizon.Sdk.Ncm.ApplicationId; + +namespace Ryujinx.Horizon.Bcat.Ipc +{ + partial class ServiceCreator : IServiceCreator, IDisposable + { + private SharedRef<LibHac.Bcat.Impl.Ipc.IServiceCreator> _libHacService; + + private int _disposalState; + + public ServiceCreator(string serviceName) + { + HorizonStatic.Options.BcatClient.Sm.GetService(ref _libHacService, serviceName).ThrowIfFailure(); + } + + [CmifCommand(0)] + public Result CreateBcatService(out IBcatService bcatService, [ClientProcessId] ulong pid) + { + // TODO: Call arp:r GetApplicationLaunchProperty with the pid to get the TitleId. + // Add an instance of nn::bcat::detail::service::core::PassphraseManager. + // Add an instance of nn::bcat::detail::service::ServiceMemoryManager. + // Add an instance of nn::bcat::detail::service::core::TaskManager who loads "bcat-sys:/" system save data and opens "dc/task.bin". + // If the file don't exist, create a new one (with a size of 0x800 bytes) and write 2 empty structs with a size of 0x400 bytes. + + bcatService = new BcatService(Bcat.Types.BcatServicePermissionLevel.User); + + return Result.Success; + } + + [CmifCommand(1)] + public Result CreateDeliveryCacheStorageService(out IDeliveryCacheStorageService service, [ClientProcessId] ulong pid) + { + using var libHacService = new SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheStorageService>(); + + var resultCode = _libHacService.Get.CreateDeliveryCacheStorageService(ref libHacService.Ref, pid); + + if (resultCode.IsSuccess()) + { + service = new DeliveryCacheStorageService(ref libHacService.Ref); + } + else + { + service = null; + } + + return resultCode.ToHorizonResult(); + } + + [CmifCommand(2)] + public Result CreateDeliveryCacheStorageServiceWithApplicationId(out IDeliveryCacheStorageService service, ApplicationId applicationId) + { + using var libHacService = new SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheStorageService>(); + + var resultCode = _libHacService.Get.CreateDeliveryCacheStorageServiceWithApplicationId(ref libHacService.Ref, new LibHac.ApplicationId(applicationId.Id)); + + if (resultCode.IsSuccess()) + { + service = new DeliveryCacheStorageService(ref libHacService.Ref); + } + else + { + service = null; + } + + return resultCode.ToHorizonResult(); + } + + public void Dispose() + { + if (Interlocked.Exchange(ref _disposalState, 1) == 0) + { + _libHacService.Destroy(); + } + } + } +} diff --git a/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/BcatService.cs b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/BcatService.cs new file mode 100644 index 00000000..e82f597e --- /dev/null +++ b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/BcatService.cs @@ -0,0 +1,25 @@ +using Ryujinx.Horizon.Bcat.Types; +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Bcat; +using Ryujinx.Horizon.Sdk.Sf; + +namespace Ryujinx.Horizon.Bcat.Ipc +{ + partial class BcatService : IBcatService + { + private readonly BcatServicePermissionLevel _permissionLevel; + + public BcatService(BcatServicePermissionLevel permissionLevel) + { + _permissionLevel = permissionLevel; + } + + [CmifCommand(10100)] + public Result RequestSyncDeliveryCache(out IDeliveryCacheProgressService deliveryCacheProgressService) + { + deliveryCacheProgressService = new DeliveryCacheProgressService(); + + return Result.Success; + } + } +} diff --git a/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheDirectoryService.cs b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheDirectoryService.cs new file mode 100644 index 00000000..dd13eefb --- /dev/null +++ b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheDirectoryService.cs @@ -0,0 +1,48 @@ +using LibHac.Bcat; +using LibHac.Common; +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Bcat; +using Ryujinx.Horizon.Sdk.Sf; +using Ryujinx.Horizon.Sdk.Sf.Hipc; +using System; +using System.Threading; + +namespace Ryujinx.Horizon.Bcat.Ipc +{ + partial class DeliveryCacheDirectoryService : IDeliveryCacheDirectoryService, IDisposable + { + private SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheDirectoryService> _libHacService; + private int _disposalState; + + public DeliveryCacheDirectoryService(ref SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheDirectoryService> libHacService) + { + _libHacService = SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheDirectoryService>.CreateMove(ref libHacService); + } + + [CmifCommand(0)] + public Result Open(DirectoryName directoryName) + { + return _libHacService.Get.Open(ref directoryName).ToHorizonResult(); + } + + [CmifCommand(1)] + public Result Read(out int entriesRead, [Buffer(Sdk.Sf.Hipc.HipcBufferFlags.Out | HipcBufferFlags.MapAlias)] Span<DeliveryCacheDirectoryEntry> entriesBuffer) + { + return _libHacService.Get.Read(out entriesRead, entriesBuffer).ToHorizonResult(); + } + + [CmifCommand(2)] + public Result GetCount(out int count) + { + return _libHacService.Get.GetCount(out count).ToHorizonResult(); + } + + public void Dispose() + { + if (Interlocked.Exchange(ref _disposalState, 1) == 0) + { + _libHacService.Destroy(); + } + } + } +} diff --git a/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheFileService.cs b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheFileService.cs new file mode 100644 index 00000000..d23f5f41 --- /dev/null +++ b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheFileService.cs @@ -0,0 +1,54 @@ +using LibHac.Bcat; +using LibHac.Common; +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Bcat; +using Ryujinx.Horizon.Sdk.Sf; +using Ryujinx.Horizon.Sdk.Sf.Hipc; +using System; +using System.Threading; + +namespace Ryujinx.Horizon.Bcat.Ipc +{ + partial class DeliveryCacheFileService : IDeliveryCacheFileService, IDisposable + { + private SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheFileService> _libHacService; + private int _disposalState; + + public DeliveryCacheFileService(ref SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheFileService> libHacService) + { + _libHacService = SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheFileService>.CreateMove(ref libHacService); + } + + [CmifCommand(0)] + public Result Open(DirectoryName directoryName, FileName fileName) + { + return _libHacService.Get.Open(ref directoryName, ref fileName).ToHorizonResult(); + } + + [CmifCommand(1)] + public Result Read(long offset, out long bytesRead, [Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias)] Span<byte> data) + { + return _libHacService.Get.Read(out bytesRead, offset, data).ToHorizonResult(); + } + + [CmifCommand(2)] + public Result GetSize(out long size) + { + return _libHacService.Get.GetSize(out size).ToHorizonResult(); + } + + [CmifCommand(3)] + public Result GetDigest(out Digest digest) + { + return _libHacService.Get.GetDigest(out digest).ToHorizonResult(); + } + + public void Dispose() + { + if (Interlocked.Exchange(ref _disposalState, 1) == 0) + { + _libHacService.Destroy(); + } + } + } +} diff --git a/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheProgressService.cs b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheProgressService.cs new file mode 100644 index 00000000..91aa2686 --- /dev/null +++ b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheProgressService.cs @@ -0,0 +1,58 @@ +using Ryujinx.Common.Logging; +using Ryujinx.Horizon.Bcat.Ipc.Types; +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Bcat; +using Ryujinx.Horizon.Sdk.OsTypes; +using Ryujinx.Horizon.Sdk.Sf; +using Ryujinx.Horizon.Sdk.Sf.Hipc; +using System; +using System.Threading; + +namespace Ryujinx.Horizon.Bcat.Ipc +{ + partial class DeliveryCacheProgressService : IDeliveryCacheProgressService, IDisposable + { + private int _handle; + private SystemEventType _systemEvent; + private int _disposalState; + + [CmifCommand(0)] + public Result GetEvent([CopyHandle] out int handle) + { + if (_handle == 0) + { + Os.CreateSystemEvent(out _systemEvent, EventClearMode.ManualClear, true).AbortOnFailure(); + + _handle = Os.GetReadableHandleOfSystemEvent(ref _systemEvent); + } + + handle = _handle; + + Logger.Stub?.PrintStub(LogClass.ServiceBcat); + + return Result.Success; + } + + [CmifCommand(1)] + public Result GetImpl([Buffer(HipcBufferFlags.Out | HipcBufferFlags.Pointer, 0x200)] out DeliveryCacheProgressImpl deliveryCacheProgressImpl) + { + deliveryCacheProgressImpl = new DeliveryCacheProgressImpl + { + State = DeliveryCacheProgressImpl.Status.Done, + Result = 0 + }; + + Logger.Stub?.PrintStub(LogClass.ServiceBcat); + + return Result.Success; + } + + public void Dispose() + { + if (_handle != 0 && Interlocked.Exchange(ref _disposalState, 1) == 0) + { + Os.DestroySystemEvent(ref _systemEvent); + } + } + } +} diff --git a/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheStorageService.cs b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheStorageService.cs new file mode 100644 index 00000000..9507a8a0 --- /dev/null +++ b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/DeliveryCacheStorageService.cs @@ -0,0 +1,74 @@ +using LibHac.Bcat; +using LibHac.Common; +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Bcat; +using Ryujinx.Horizon.Sdk.Sf; +using Ryujinx.Horizon.Sdk.Sf.Hipc; +using System; +using System.Threading; + +namespace Ryujinx.Horizon.Bcat.Ipc +{ + partial class DeliveryCacheStorageService : IDeliveryCacheStorageService, IDisposable + { + private SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheStorageService> _libHacService; + private int _disposalState; + + public DeliveryCacheStorageService(ref SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheStorageService> libHacService) + { + _libHacService = SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheStorageService>.CreateMove(ref libHacService); + } + + [CmifCommand(0)] + public Result CreateFileService(out IDeliveryCacheFileService service) + { + using var libHacService = new SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheFileService>(); + + var resultCode = _libHacService.Get.CreateFileService(ref libHacService.Ref); + + if (resultCode.IsSuccess()) + { + service = new DeliveryCacheFileService(ref libHacService.Ref); + } + else + { + service = null; + } + + return resultCode.ToHorizonResult(); + } + + [CmifCommand(1)] + public Result CreateDirectoryService(out IDeliveryCacheDirectoryService service) + { + using var libHacService = new SharedRef<LibHac.Bcat.Impl.Ipc.IDeliveryCacheDirectoryService>(); + + var resultCode = _libHacService.Get.CreateDirectoryService(ref libHacService.Ref); + + if (resultCode.IsSuccess()) + { + service = new DeliveryCacheDirectoryService(ref libHacService.Ref); + } + else + { + service = null; + } + + return resultCode.ToHorizonResult(); + } + + [CmifCommand(10)] + public Result EnumerateDeliveryCacheDirectory(out int count, [Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias)] Span<DirectoryName> directoryNames) + { + return _libHacService.Get.EnumerateDeliveryCacheDirectory(out count, directoryNames).ToHorizonResult(); + } + + public void Dispose() + { + if (Interlocked.Exchange(ref _disposalState, 1) == 0) + { + _libHacService.Destroy(); + } + } + } +} diff --git a/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/Types/DeliveryCacheProgressImpl.cs b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/Types/DeliveryCacheProgressImpl.cs new file mode 100644 index 00000000..10e0b54f --- /dev/null +++ b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/Types/DeliveryCacheProgressImpl.cs @@ -0,0 +1,18 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.Horizon.Bcat.Ipc.Types +{ + [StructLayout(LayoutKind.Sequential, Size = 0x200)] + public struct DeliveryCacheProgressImpl + { + public enum Status + { + // TODO: determine other values + Done = 9 + } + + public Status State; + public uint Result; + // TODO: reverse the rest of the structure + } +} diff --git a/src/Ryujinx.Horizon/Bcat/Types/BcatPortIndex.cs b/src/Ryujinx.Horizon/Bcat/Types/BcatPortIndex.cs new file mode 100644 index 00000000..e448dfdc --- /dev/null +++ b/src/Ryujinx.Horizon/Bcat/Types/BcatPortIndex.cs @@ -0,0 +1,10 @@ +namespace Ryujinx.Horizon.Bcat.Types +{ + enum BcatPortIndex + { + Admin, + Manager, + User, + System + } +} diff --git a/src/Ryujinx.Horizon/Bcat/Types/BcatServicePermissionLevel.cs b/src/Ryujinx.Horizon/Bcat/Types/BcatServicePermissionLevel.cs new file mode 100644 index 00000000..54d7461a --- /dev/null +++ b/src/Ryujinx.Horizon/Bcat/Types/BcatServicePermissionLevel.cs @@ -0,0 +1,10 @@ +namespace Ryujinx.Horizon.Bcat.Types +{ + enum BcatServicePermissionLevel + { + Admin = -1, + User = 1, + System = 2, + Manager = 6 + } +}
\ No newline at end of file |