aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Ryujinx.HLE/HOS/Horizon.cs21
-rw-r--r--Ryujinx.HLE/HOS/Services/Arp/LibHacIReader.cs52
-rw-r--r--Ryujinx.HLE/HOS/Services/Bcat/IServiceCreator.cs56
-rw-r--r--Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheDirectoryService.cs63
-rw-r--r--Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheFileService.cs76
-rw-r--r--Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheStorageService.cs71
-rw-r--r--Ryujinx.HLE/Ryujinx.HLE.csproj2
-rw-r--r--Ryujinx/Ui/GameTableContextMenu.cs21
-rw-r--r--Ryujinx/Ui/GameTableContextMenu.glade9
9 files changed, 328 insertions, 43 deletions
diff --git a/Ryujinx.HLE/HOS/Horizon.cs b/Ryujinx.HLE/HOS/Horizon.cs
index 79fef93d..2dfa2757 100644
--- a/Ryujinx.HLE/HOS/Horizon.cs
+++ b/Ryujinx.HLE/HOS/Horizon.cs
@@ -1,5 +1,6 @@
using LibHac;
using LibHac.Account;
+using LibHac.Bcat;
using LibHac.Common;
using LibHac.Fs;
using LibHac.FsSystem;
@@ -18,6 +19,7 @@ using Ryujinx.HLE.HOS.Kernel.Memory;
using Ryujinx.HLE.HOS.Kernel.Process;
using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy;
+using Ryujinx.HLE.HOS.Services.Arp;
using Ryujinx.HLE.HOS.Services.Mii;
using Ryujinx.HLE.HOS.Services.Nv;
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl;
@@ -144,6 +146,9 @@ namespace Ryujinx.HLE.HOS
internal NvHostSyncpt HostSyncpoint { get; private set; }
+ internal LibHac.Horizon LibHacHorizonServer { get; private set; }
+ internal HorizonClient LibHacHorizonClient { get; private set; }
+
public Horizon(Switch device, ContentManager contentManager)
{
ControlData = new BlitStruct<ApplicationControlProperty>(1);
@@ -280,6 +285,22 @@ namespace Ryujinx.HLE.HOS
SurfaceFlinger = new SurfaceFlinger(device);
ConfigurationState.Instance.System.EnableDockedMode.Event += OnDockedModeChange;
+
+ InitLibHacHorizon();
+ }
+
+ private void InitLibHacHorizon()
+ {
+ LibHac.Horizon horizon = new LibHac.Horizon(null, Device.FileSystem.FsServer);
+
+ horizon.CreateHorizonClient(out HorizonClient ryujinxClient).ThrowIfFailure();
+ horizon.CreateHorizonClient(out HorizonClient bcatClient).ThrowIfFailure();
+
+ ryujinxClient.Sm.RegisterService(new LibHacIReader(this), "arp:r").ThrowIfFailure();
+ new BcatServer(bcatClient);
+
+ LibHacHorizonServer = horizon;
+ LibHacHorizonClient = ryujinxClient;
}
private void OnDockedModeChange(object sender, ReactiveEventArgs<bool> e)
diff --git a/Ryujinx.HLE/HOS/Services/Arp/LibHacIReader.cs b/Ryujinx.HLE/HOS/Services/Arp/LibHacIReader.cs
new file mode 100644
index 00000000..77f02e8d
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Arp/LibHacIReader.cs
@@ -0,0 +1,52 @@
+using LibHac;
+using LibHac.Arp.Impl;
+using LibHac.Ncm;
+using LibHac.Ns;
+using System;
+
+using ApplicationId = LibHac.ApplicationId;
+
+namespace Ryujinx.HLE.HOS.Services.Arp
+{
+ class LibHacIReader : IReader
+ {
+ private Horizon System { get; }
+
+ public LibHacIReader(Horizon system)
+ {
+ System = system;
+ }
+
+ public Result GetApplicationLaunchProperty(out LibHac.Arp.ApplicationLaunchProperty launchProperty, ulong processId)
+ {
+ launchProperty = new LibHac.Arp.ApplicationLaunchProperty();
+
+ launchProperty.BaseStorageId = StorageId.BuiltInUser;
+ launchProperty.ApplicationId = new ApplicationId(System.TitleId);
+
+ return Result.Success;
+ }
+
+ public Result GetApplicationLaunchPropertyWithApplicationId(out LibHac.Arp.ApplicationLaunchProperty launchProperty,
+ ApplicationId applicationId)
+ {
+ launchProperty = new LibHac.Arp.ApplicationLaunchProperty();
+
+ launchProperty.BaseStorageId = StorageId.BuiltInUser;
+ launchProperty.ApplicationId = applicationId;
+
+ return Result.Success;
+ }
+
+ public Result GetApplicationControlProperty(out ApplicationControlProperty controlProperty, ulong processId)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Result GetApplicationControlPropertyWithApplicationId(out ApplicationControlProperty controlProperty,
+ ApplicationId applicationId)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Services/Bcat/IServiceCreator.cs b/Ryujinx.HLE/HOS/Services/Bcat/IServiceCreator.cs
index ec34f540..ac1abc35 100644
--- a/Ryujinx.HLE/HOS/Services/Bcat/IServiceCreator.cs
+++ b/Ryujinx.HLE/HOS/Services/Bcat/IServiceCreator.cs
@@ -1,18 +1,25 @@
+using LibHac;
+using Ryujinx.Common;
using Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator;
using Ryujinx.HLE.HOS.Services.Arp;
namespace Ryujinx.HLE.HOS.Services.Bcat
{
- [Service("bcat:a")]
- [Service("bcat:m")]
- [Service("bcat:u")]
- [Service("bcat:s")]
+ [Service("bcat:a", "bcat:a")]
+ [Service("bcat:m", "bcat:m")]
+ [Service("bcat:u", "bcat:u")]
+ [Service("bcat:s", "bcat:s")]
class IServiceCreator : IpcService
{
- public IServiceCreator(ServiceCtx context) { }
+ private LibHac.Bcat.Detail.Ipc.IServiceCreator _base;
+
+ public IServiceCreator(ServiceCtx context, string serviceName)
+ {
+ context.Device.System.LibHacHorizonClient.Sm.GetService(out _base, serviceName).ThrowIfFailure();
+ }
[Command(0)]
- // CreateBcatService(u64, pid) -> object<nn::bcat::detail::ipc::IBcatService>
+ // CreateBcatService(pid) -> object<nn::bcat::detail::ipc::IBcatService>
public ResultCode CreateBcatService(ServiceCtx context)
{
// TODO: Call arp:r GetApplicationLaunchProperty with the pid to get the TitleId.
@@ -30,21 +37,36 @@ namespace Ryujinx.HLE.HOS.Services.Bcat
}
[Command(1)]
- // CreateDeliveryCacheStorageService(u64, pid) -> object<nn::bcat::detail::ipc::IDeliveryCacheStorageService>
+ // CreateDeliveryCacheStorageService(pid) -> object<nn::bcat::detail::ipc::IDeliveryCacheStorageService>
public ResultCode CreateDeliveryCacheStorageService(ServiceCtx context)
{
- // TODO: Call arp:r GetApplicationLaunchProperty with the pid to get the TitleId.
- // Add an instance of nn::bcat::detail::service::core::ApplicationStorageManager who load "bcat-dc-X:/" system save data,
- // return ResultCode.NullSaveData if failed.
- // Where X depend of the ApplicationLaunchProperty stored in an array (range 0-3).
- // Add an instance of nn::bcat::detail::service::ServiceMemoryManager.
+ ulong pid = context.RequestData.ReadUInt64();
- MakeObject(context, new IDeliveryCacheStorageService(context, ApplicationLaunchProperty.GetByPid(context)));
+ Result rc = _base.CreateDeliveryCacheStorageService(out LibHac.Bcat.Detail.Ipc.IDeliveryCacheStorageService serv, pid);
- // NOTE: If the IDeliveryCacheStorageService is null this error is returned, Doesn't occur in our case.
- // return ResultCode.NullObject;
+ if (rc.IsSuccess())
+ {
+ MakeObject(context, new IDeliveryCacheStorageService(context, serv));
+ }
- return ResultCode.Success;
+ return (ResultCode)rc.Value;
+ }
+
+ [Command(2)]
+ // CreateDeliveryCacheStorageServiceWithApplicationId(nn::ApplicationId) -> object<nn::bcat::detail::ipc::IDeliveryCacheStorageService>
+ public ResultCode CreateDeliveryCacheStorageServiceWithApplicationId(ServiceCtx context)
+ {
+ ApplicationId applicationId = context.RequestData.ReadStruct<ApplicationId>();
+
+ Result rc = _base.CreateDeliveryCacheStorageServiceWithApplicationId(out LibHac.Bcat.Detail.Ipc.IDeliveryCacheStorageService serv,
+ applicationId);
+
+ if (rc.IsSuccess())
+ {
+ MakeObject(context, new IDeliveryCacheStorageService(context, serv));
+ }
+
+ return (ResultCode)rc.Value;
}
}
-} \ No newline at end of file
+}
diff --git a/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheDirectoryService.cs b/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheDirectoryService.cs
new file mode 100644
index 00000000..ce5c575e
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheDirectoryService.cs
@@ -0,0 +1,63 @@
+using LibHac;
+using LibHac.Bcat;
+using Ryujinx.Common;
+using System;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator
+{
+ class IDeliveryCacheDirectoryService : IpcService, IDisposable
+ {
+ private LibHac.Bcat.Detail.Ipc.IDeliveryCacheDirectoryService _base;
+
+ public IDeliveryCacheDirectoryService(LibHac.Bcat.Detail.Ipc.IDeliveryCacheDirectoryService baseService)
+ {
+ _base = baseService;
+ }
+
+ [Command(0)]
+ // Open(nn::bcat::DirectoryName)
+ public ResultCode Open(ServiceCtx context)
+ {
+ DirectoryName directoryName = context.RequestData.ReadStruct<DirectoryName>();
+
+ Result result = _base.Open(ref directoryName);
+
+ return (ResultCode)result.Value;
+ }
+
+ [Command(1)]
+ // Read() -> (u32, buffer<nn::bcat::DeliveryCacheDirectoryEntry, 6>)
+ public ResultCode Read(ServiceCtx context)
+ {
+ long position = context.Request.ReceiveBuff[0].Position;
+ long size = context.Request.ReceiveBuff[0].Size;
+
+ byte[] data = new byte[size];
+
+ Result result = _base.Read(out int entriesRead, MemoryMarshal.Cast<byte, DeliveryCacheDirectoryEntry>(data));
+
+ context.Memory.WriteBytes(position, data);
+
+ context.ResponseData.Write(entriesRead);
+
+ return (ResultCode)result.Value;
+ }
+
+ [Command(2)]
+ // GetCount() -> u32
+ public ResultCode GetCount(ServiceCtx context)
+ {
+ Result result = _base.GetCount(out int count);
+
+ context.ResponseData.Write(count);
+
+ return (ResultCode)result.Value;
+ }
+
+ public void Dispose()
+ {
+ _base?.Dispose();
+ }
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheFileService.cs b/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheFileService.cs
new file mode 100644
index 00000000..a621283f
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheFileService.cs
@@ -0,0 +1,76 @@
+using LibHac;
+using LibHac.Bcat;
+using Ryujinx.Common;
+using System;
+
+namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator
+{
+ class IDeliveryCacheFileService : IpcService, IDisposable
+ {
+ private LibHac.Bcat.Detail.Ipc.IDeliveryCacheFileService _base;
+
+ public IDeliveryCacheFileService(LibHac.Bcat.Detail.Ipc.IDeliveryCacheFileService baseService)
+ {
+ _base = baseService;
+ }
+
+ [Command(0)]
+ // Open(nn::bcat::DirectoryName, nn::bcat::FileName)
+ public ResultCode Open(ServiceCtx context)
+ {
+ DirectoryName directoryName = context.RequestData.ReadStruct<DirectoryName>();
+ FileName fileName = context.RequestData.ReadStruct<FileName>();
+
+ Result result = _base.Open(ref directoryName, ref fileName);
+
+ return (ResultCode)result.Value;
+ }
+
+ [Command(1)]
+ // Read(u64) -> (u64, buffer<bytes, 6>)
+ public ResultCode Read(ServiceCtx context)
+ {
+ long position = context.Request.ReceiveBuff[0].Position;
+ long size = context.Request.ReceiveBuff[0].Size;
+
+ long offset = context.RequestData.ReadInt64();
+
+ byte[] data = new byte[size];
+
+ Result result = _base.Read(out long bytesRead, offset, data);
+
+ context.Memory.WriteBytes(position, data);
+
+ context.ResponseData.Write(bytesRead);
+
+ return (ResultCode)result.Value;
+ }
+
+ [Command(2)]
+ // GetSize() -> u64
+ public ResultCode GetSize(ServiceCtx context)
+ {
+ Result result = _base.GetSize(out long size);
+
+ context.ResponseData.Write(size);
+
+ return (ResultCode)result.Value;
+ }
+
+ [Command(3)]
+ // GetDigest() -> nn::bcat::Digest
+ public ResultCode GetDigest(ServiceCtx context)
+ {
+ Result result = _base.GetDigest(out Digest digest);
+
+ context.ResponseData.WriteStruct(digest);
+
+ return (ResultCode)result.Value;
+ }
+
+ public void Dispose()
+ {
+ _base?.Dispose();
+ }
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheStorageService.cs b/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheStorageService.cs
index cad44370..344eb54e 100644
--- a/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheStorageService.cs
+++ b/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheStorageService.cs
@@ -1,47 +1,68 @@
-using Ryujinx.HLE.HOS.Services.Arp;
+using LibHac;
+using LibHac.Bcat;
using System;
-using System.Text;
+using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator
{
- class IDeliveryCacheStorageService : IpcService
+ class IDeliveryCacheStorageService : IpcService, IDisposable
{
- private const int DeliveryCacheDirectoriesLimit = 100;
- private const int DeliveryCacheDirectoryNameLength = 32;
+ private LibHac.Bcat.Detail.Ipc.IDeliveryCacheStorageService _base;
- private string[] _deliveryCacheDirectories = new string[0];
+ public IDeliveryCacheStorageService(ServiceCtx context, LibHac.Bcat.Detail.Ipc.IDeliveryCacheStorageService baseService)
+ {
+ _base = baseService;
+ }
- public IDeliveryCacheStorageService(ServiceCtx context, ApplicationLaunchProperty applicationLaunchProperty)
+ [Command(0)]
+ // CreateFileService() -> object<nn::bcat::detail::ipc::IDeliveryCacheFileService>
+ public ResultCode CreateFileService(ServiceCtx context)
{
- // TODO: Read directories.meta file from the save data (loaded in IServiceCreator) in _deliveryCacheDirectories.
+ Result result = _base.CreateFileService(out LibHac.Bcat.Detail.Ipc.IDeliveryCacheFileService service);
+
+ if (result.IsSuccess())
+ {
+ MakeObject(context, new IDeliveryCacheFileService(service));
+ }
+
+ return (ResultCode)result.Value;
+ }
+
+ [Command(1)]
+ // CreateDirectoryService() -> object<nn::bcat::detail::ipc::IDeliveryCacheDirectoryService>
+ public ResultCode CreateDirectoryService(ServiceCtx context)
+ {
+ Result result = _base.CreateDirectoryService(out LibHac.Bcat.Detail.Ipc.IDeliveryCacheDirectoryService service);
+
+ if (result.IsSuccess())
+ {
+ MakeObject(context, new IDeliveryCacheDirectoryService(service));
+ }
+
+ return (ResultCode)result.Value;
}
[Command(10)]
// EnumerateDeliveryCacheDirectory() -> (u32, buffer<nn::bcat::DirectoryName, 6>)
public ResultCode EnumerateDeliveryCacheDirectory(ServiceCtx context)
{
- long outputPosition = context.Request.ReceiveBuff[0].Position;
- long outputSize = context.Request.ReceiveBuff[0].Size;
+ long position = context.Request.ReceiveBuff[0].Position;
+ long size = context.Request.ReceiveBuff[0].Size;
- for (int index = 0; index < _deliveryCacheDirectories.Length; index++)
- {
- if (index == DeliveryCacheDirectoriesLimit - 1)
- {
- break;
- }
+ byte[] data = new byte[size];
- byte[] directoryNameBuffer = Encoding.ASCII.GetBytes(_deliveryCacheDirectories[index]);
+ Result result = _base.EnumerateDeliveryCacheDirectory(out int count, MemoryMarshal.Cast<byte, DirectoryName>(data));
- Array.Resize(ref directoryNameBuffer, DeliveryCacheDirectoryNameLength);
+ context.Memory.WriteBytes(position, data);
- directoryNameBuffer[DeliveryCacheDirectoryNameLength - 1] = 0x00;
-
- context.Memory.WriteBytes(outputPosition + index * DeliveryCacheDirectoryNameLength, directoryNameBuffer);
- }
+ context.ResponseData.Write(count);
- context.ResponseData.Write(_deliveryCacheDirectories.Length);
+ return (ResultCode)result.Value;
+ }
- return ResultCode.Success;
+ public void Dispose()
+ {
+ _base?.Dispose();
}
}
-} \ No newline at end of file
+}
diff --git a/Ryujinx.HLE/Ryujinx.HLE.csproj b/Ryujinx.HLE/Ryujinx.HLE.csproj
index 8a7d4a0f..d82fc402 100644
--- a/Ryujinx.HLE/Ryujinx.HLE.csproj
+++ b/Ryujinx.HLE/Ryujinx.HLE.csproj
@@ -52,7 +52,7 @@
<ItemGroup>
<PackageReference Include="Concentus" Version="1.1.7" />
- <PackageReference Include="LibHac" Version="0.10.0" />
+ <PackageReference Include="LibHac" Version="0.11.0" />
<PackageReference Include="MsgPack.Cli" Version="1.0.1" />
</ItemGroup>
diff --git a/Ryujinx/Ui/GameTableContextMenu.cs b/Ryujinx/Ui/GameTableContextMenu.cs
index 5db34ecb..8bead1e3 100644
--- a/Ryujinx/Ui/GameTableContextMenu.cs
+++ b/Ryujinx/Ui/GameTableContextMenu.cs
@@ -40,6 +40,7 @@ namespace Ryujinx.Ui
#pragma warning disable IDE0044
[GUI] MenuItem _openSaveUserDir;
[GUI] MenuItem _openSaveDeviceDir;
+ [GUI] MenuItem _openSaveBcatDir;
[GUI] MenuItem _manageTitleUpdates;
[GUI] MenuItem _extractRomFs;
[GUI] MenuItem _extractExeFs;
@@ -61,6 +62,7 @@ namespace Ryujinx.Ui
_openSaveUserDir.Activated += OpenSaveUserDir_Clicked;
_openSaveDeviceDir.Activated += OpenSaveDeviceDir_Clicked;
+ _openSaveBcatDir.Activated += OpenSaveBcatDir_Clicked;
_manageTitleUpdates.Activated += ManageTitleUpdates_Clicked;
_extractRomFs.Activated += ExtractRomFs_Clicked;
_extractExeFs.Activated += ExtractExeFs_Clicked;
@@ -68,6 +70,7 @@ namespace Ryujinx.Ui
_openSaveUserDir.Sensitive = !Util.IsEmpty(controlData.ByteSpan) && controlData.Value.UserAccountSaveDataSize > 0;
_openSaveDeviceDir.Sensitive = !Util.IsEmpty(controlData.ByteSpan) && controlData.Value.DeviceSaveDataSize > 0;
+ _openSaveBcatDir.Sensitive = !Util.IsEmpty(controlData.ByteSpan) && controlData.Value.BcatDeliveryCacheStorageSize > 0;
string ext = System.IO.Path.GetExtension(_gameTableStore.GetValue(_rowIter, 9).ToString()).ToLower();
if (ext != ".nca" && ext != ".nsp" && ext != ".pfs0" && ext != ".xci")
@@ -516,6 +519,24 @@ namespace Ryujinx.Ui
OpenSaveDir(titleName, titleIdNumber, filter);
}
+ private void OpenSaveBcatDir_Clicked(object sender, EventArgs args)
+ {
+ string titleName = _gameTableStore.GetValue(_rowIter, 2).ToString().Split("\n")[0];
+ string titleId = _gameTableStore.GetValue(_rowIter, 2).ToString().Split("\n")[1].ToLower();
+
+ if (!ulong.TryParse(titleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong titleIdNumber))
+ {
+ GtkDialog.CreateErrorDialog("UI error: The selected game did not have a valid title ID");
+
+ return;
+ }
+
+ SaveDataFilter filter = new SaveDataFilter();
+ filter.SetSaveDataType(SaveDataType.Bcat);
+
+ OpenSaveDir(titleName, titleIdNumber, filter);
+ }
+
private void ManageTitleUpdates_Clicked(object sender, EventArgs args)
{
string titleName = _gameTableStore.GetValue(_rowIter, 2).ToString().Split("\n")[0];
diff --git a/Ryujinx/Ui/GameTableContextMenu.glade b/Ryujinx/Ui/GameTableContextMenu.glade
index 8f71ecf7..e648b745 100644
--- a/Ryujinx/Ui/GameTableContextMenu.glade
+++ b/Ryujinx/Ui/GameTableContextMenu.glade
@@ -24,6 +24,15 @@
</object>
</child>
<child>
+ <object class="GtkMenuItem" id="_openSaveBcatDir">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="tooltip_text" translatable="yes">Open the folder where the BCAT save for the application is loaded</property>
+ <property name="label" translatable="yes">Open BCAT Save Directory</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
<object class="GtkSeparatorMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>