Update to LibHac 0.13.1 (#2328)

Update the LibHac dependency to version 0.13.1. This brings a ton of improvements and changes such as:
- Refactor `FsSrv` to match the official refactoring done in FS.
- Change how the `Horizon` and `HorizonClient` classes are handled. Each client created represents a different process with its own process ID and client state.
- Add FS access control to handle permissions for FS service method calls.
- Add FS program registry to keep track of the program ID, location and permissions of each process.
- Add FS program index map info manager to track the program IDs and indexes of multi-application programs.
- Add all FS IPC interfaces.
- Rewrite `Fs.Fsa` code to be more accurate.
- Rewrite a lot of `FsSrv` code to be more accurate.
- Extend directory save data to store `SaveDataExtraData`
- Extend directory save data to lock the save directory to allow only one accessor at a time.
- Improve waiting and retrying when encountering access issues in `LocalFileSystem` and `DirectorySaveDataFileSystem`.
- More `IFileSystemProxy` methods should work now.
- Probably a bunch more stuff.

On the Ryujinx side:
- Forward most `IFileSystemProxy` methods to LibHac.
- Register programs and program index map info when launching an application.
- Remove hacks and workarounds for missing LibHac functionality.
- Recreate missing save data extra data found on emulator startup.
- Create system save data that wasn't indexed correctly on an older LibHac version.

`FsSrv` now enforces access control for each process. When a process tries to open a save data file system, FS reads the save's extra data to determine who the save owner is and if the caller has permission to open the save data. Previously-created save data did not have extra data created when the save was created.
With access control checks in place, this means that processes with no permissions (most games) wouldn't be able to access their own save data. The extra data can be partially created from data in the save data indexer, which should be enough for access control purposes.
This commit is contained in:
Alex Barney 2021-07-13 01:19:28 -07:00 committed by GitHub
parent 04dce402ac
commit 19afb3209c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
31 changed files with 1795 additions and 574 deletions

View file

@ -2,12 +2,9 @@
using LibHac.Fs;
using LibHac.Fs.Shim;
using Ryujinx.Common;
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.FileSystem.Content;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace Ryujinx.HLE.HOS.Services.Account.Acc
@ -16,16 +13,20 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
{
public static readonly UserId DefaultUserId = new UserId("00000000000000010000000000000000");
private readonly VirtualFileSystem _virtualFileSystem;
private readonly AccountSaveDataManager _accountSaveDataManager;
// Todo: The account service doesn't have the permissions to delete save data. Qlaunch takes care of deleting
// save data, so we're currently passing a client with full permissions. Consider moving save data deletion
// outside of the AccountManager.
private readonly HorizonClient _horizonClient;
private ConcurrentDictionary<string, UserProfile> _profiles;
public UserProfile LastOpenedUser { get; private set; }
public AccountManager(VirtualFileSystem virtualFileSystem)
public AccountManager(HorizonClient horizonClient)
{
_virtualFileSystem = virtualFileSystem;
_horizonClient = horizonClient;
_profiles = new ConcurrentDictionary<string, UserProfile>();
@ -169,31 +170,22 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
SaveDataFilter saveDataFilter = new SaveDataFilter();
saveDataFilter.SetUserId(new LibHac.Fs.UserId((ulong)userId.High, (ulong)userId.Low));
Result result = _virtualFileSystem.FsClient.OpenSaveDataIterator(out SaveDataIterator saveDataIterator, SaveDataSpaceId.User, ref saveDataFilter);
if (result.IsSuccess())
_horizonClient.Fs.OpenSaveDataIterator(out SaveDataIterator saveDataIterator, SaveDataSpaceId.User, in saveDataFilter).ThrowIfFailure();
Span<SaveDataInfo> saveDataInfo = stackalloc SaveDataInfo[10];
while (true)
{
Span<SaveDataInfo> saveDataInfo = stackalloc SaveDataInfo[10];
saveDataIterator.ReadSaveDataInfo(out long readCount, saveDataInfo).ThrowIfFailure();
while (true)
if (readCount == 0)
{
saveDataIterator.ReadSaveDataInfo(out long readCount, saveDataInfo);
break;
}
if (readCount == 0)
{
break;
}
for (int i = 0; i < readCount; i++)
{
// TODO: We use Directory.Delete workaround because DeleteSaveData softlock without, due to a bug in LibHac 0.12.0.
string savePath = Path.Combine(_virtualFileSystem.GetNandPath(), $"user/save/{saveDataInfo[i].SaveDataId:x16}");
string saveMetaPath = Path.Combine(_virtualFileSystem.GetNandPath(), $"user/saveMeta/{saveDataInfo[i].SaveDataId:x16}");
Directory.Delete(savePath, true);
Directory.Delete(saveMetaPath, true);
_virtualFileSystem.FsClient.DeleteSaveData(SaveDataSpaceId.User, saveDataInfo[i].SaveDataId);
}
for (int i = 0; i < readCount; i++)
{
_horizonClient.Fs.DeleteSaveData(SaveDataSpaceId.User, saveDataInfo[i].SaveDataId).ThrowIfFailure();
}
}
}

View file

@ -37,6 +37,8 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati
private int _notificationStorageChannelEventHandle;
private int _healthWarningDisappearedSystemEventHandle;
private HorizonClient _horizon;
public IApplicationFunctions(Horizon system)
{
// TODO: Find where they are signaled.
@ -44,6 +46,8 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati
_friendInvitationStorageChannelEvent = new KEvent(system.KernelContext);
_notificationStorageChannelEvent = new KEvent(system.KernelContext);
_healthWarningDisappearedSystemEvent = new KEvent(system.KernelContext);
_horizon = system.LibHacHorizonManager.AmClient;
}
[CommandHipc(1)]
@ -103,14 +107,16 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati
// EnsureSaveData(nn::account::Uid) -> u64
public ResultCode EnsureSaveData(ServiceCtx context)
{
Uid userId = context.RequestData.ReadStruct<AccountUid>().ToLibHacUid();
ApplicationId applicationId = new ApplicationId(context.Process.TitleId);
Uid userId = context.RequestData.ReadStruct<AccountUid>().ToLibHacUid();
// Mask out the low nibble of the program ID to get the application ID
ApplicationId applicationId = new ApplicationId(context.Device.Application.TitleId & ~0xFul);
BlitStruct<ApplicationControlProperty> controlHolder = context.Device.Application.ControlData;
ref ApplicationControlProperty control = ref controlHolder.Value;
if (LibHac.Utilities.IsEmpty(controlHolder.ByteSpan))
if (LibHac.Utilities.IsZeros(controlHolder.ByteSpan))
{
// If the current application doesn't have a loaded control property, create a dummy one
// and set the savedata sizes so a user savedata will be created.
@ -124,7 +130,8 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati
"No control file was found for this game. Using a dummy one instead. This may cause inaccuracies in some games.");
}
Result result = EnsureApplicationSaveData(context.Device.FileSystem.FsClient, out long requiredSize, applicationId, ref control, ref userId);
HorizonClient hos = context.Device.System.LibHacHorizonManager.AmClient;
Result result = EnsureApplicationSaveData(hos.Fs, out long requiredSize, applicationId, ref control, ref userId);
context.ResponseData.Write(requiredSize);
@ -195,7 +202,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati
public ResultCode ExtendSaveData(ServiceCtx context)
{
SaveDataType saveDataType = (SaveDataType)context.RequestData.ReadUInt64();
Uid userId = context.RequestData.ReadStruct<AccountUid>().ToLibHacUid();
Uid userId = context.RequestData.ReadStruct<Uid>();
ulong saveDataSize = context.RequestData.ReadUInt64();
ulong journalSize = context.RequestData.ReadUInt64();
@ -217,7 +224,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati
public ResultCode GetSaveDataSize(ServiceCtx context)
{
SaveDataType saveDataType = (SaveDataType)context.RequestData.ReadUInt64();
Uid userId = context.RequestData.ReadStruct<AccountUid>().ToLibHacUid();
Uid userId = context.RequestData.ReadStruct<Uid>();
// NOTE: Service calls nn::fs::FindSaveDataWithFilter with SaveDataType = 1 hardcoded.
// Then it calls nn::fs::GetSaveDataAvailableSize and nn::fs::GetSaveDataJournalSize to get the sizes.
@ -231,6 +238,31 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati
return ResultCode.Success;
}
[CommandHipc(27)] // 5.0.0+
// CreateCacheStorage(u16 index, s64 save_size, s64 journal_size) -> (u32 storageTarget, u64 requiredSize)
public ResultCode CreateCacheStorage(ServiceCtx context)
{
ushort index = (ushort)context.RequestData.ReadUInt64();
long saveSize = context.RequestData.ReadInt64();
long journalSize = context.RequestData.ReadInt64();
// Mask out the low nibble of the program ID to get the application ID
ApplicationId applicationId = new ApplicationId(context.Device.Application.TitleId & ~0xFul);
BlitStruct<ApplicationControlProperty> controlHolder = context.Device.Application.ControlData;
Result result = _horizon.Fs.CreateApplicationCacheStorage(out long requiredSize,
out CacheStorageTargetMedia storageTarget, applicationId, ref controlHolder.Value, index, saveSize,
journalSize);
if (result.IsFailure()) return (ResultCode)result.Value;
context.ResponseData.Write((ulong)storageTarget);
context.ResponseData.Write(requiredSize);
return ResultCode.Success;
}
[CommandHipc(30)]
// BeginBlockingHomeButtonShortAndLongPressed()
public ResultCode BeginBlockingHomeButtonShortAndLongPressed(ServiceCtx context)
@ -517,7 +549,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_gpuErrorDetectedSystemEventHandle);
// NOTE: This is used by "sdk" NSO during applet-application initialization.
// A seperate thread is setup where event-waiting is handled.
// A separate thread is setup where event-waiting is handled.
// When the Event is signaled, official sw will assert.
return ResultCode.Success;

View file

@ -9,19 +9,14 @@ namespace Ryujinx.HLE.HOS.Services.Arp
{
class LibHacIReader : LibHac.Arp.Impl.IReader
{
private Horizon System { get; }
public LibHacIReader(Horizon system)
{
System = system;
}
public ApplicationId ApplicationId { get; set; }
public Result GetApplicationLaunchProperty(out LibHac.Arp.ApplicationLaunchProperty launchProperty, ulong processId)
{
launchProperty = new LibHac.Arp.ApplicationLaunchProperty
{
BaseStorageId = StorageId.BuiltInUser,
ApplicationId = new ApplicationId(System.Device.Application.TitleId)
ApplicationId = ApplicationId
};
return Result.Success;
@ -47,5 +42,27 @@ namespace Ryujinx.HLE.HOS.Services.Arp
{
throw new NotImplementedException();
}
public Result GetServiceObject(out object serviceObject)
{
throw new NotImplementedException();
}
}
internal class LibHacArpServiceObject : LibHac.Sm.IServiceObject
{
private LibHacIReader _serviceObject;
public LibHacArpServiceObject(LibHacIReader serviceObject)
{
_serviceObject = serviceObject;
}
public Result GetServiceObject(out object serviceObject)
{
serviceObject = _serviceObject;
return Result.Success;
}
}
}

View file

@ -11,11 +11,12 @@ namespace Ryujinx.HLE.HOS.Services.Bcat
[Service("bcat:s", "bcat:s")]
class IServiceCreator : IpcService
{
private LibHac.Bcat.Detail.Ipc.IServiceCreator _base;
private LibHac.Bcat.Impl.Ipc.IServiceCreator _base;
public IServiceCreator(ServiceCtx context, string serviceName)
{
context.Device.System.LibHacHorizonClient.Sm.GetService(out _base, serviceName).ThrowIfFailure();
var applicationClient = context.Device.System.LibHacHorizonManager.ApplicationClient;
applicationClient.Sm.GetService(out _base, serviceName).ThrowIfFailure();
}
[CommandHipc(0)]
@ -42,7 +43,7 @@ namespace Ryujinx.HLE.HOS.Services.Bcat
{
ulong pid = context.RequestData.ReadUInt64();
Result rc = _base.CreateDeliveryCacheStorageService(out LibHac.Bcat.Detail.Ipc.IDeliveryCacheStorageService serv, pid);
Result rc = _base.CreateDeliveryCacheStorageService(out LibHac.Bcat.Impl.Ipc.IDeliveryCacheStorageService serv, pid);
if (rc.IsSuccess())
{
@ -58,7 +59,7 @@ namespace Ryujinx.HLE.HOS.Services.Bcat
{
ApplicationId applicationId = context.RequestData.ReadStruct<ApplicationId>();
Result rc = _base.CreateDeliveryCacheStorageServiceWithApplicationId(out LibHac.Bcat.Detail.Ipc.IDeliveryCacheStorageService serv,
Result rc = _base.CreateDeliveryCacheStorageServiceWithApplicationId(out LibHac.Bcat.Impl.Ipc.IDeliveryCacheStorageService serv,
applicationId);
if (rc.IsSuccess())

View file

@ -7,9 +7,9 @@ namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator
{
class IDeliveryCacheDirectoryService : DisposableIpcService
{
private LibHac.Bcat.Detail.Ipc.IDeliveryCacheDirectoryService _base;
private LibHac.Bcat.Impl.Ipc.IDeliveryCacheDirectoryService _base;
public IDeliveryCacheDirectoryService(LibHac.Bcat.Detail.Ipc.IDeliveryCacheDirectoryService baseService)
public IDeliveryCacheDirectoryService(LibHac.Bcat.Impl.Ipc.IDeliveryCacheDirectoryService baseService)
{
_base = baseService;
}

View file

@ -6,9 +6,9 @@ namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator
{
class IDeliveryCacheFileService : DisposableIpcService
{
private LibHac.Bcat.Detail.Ipc.IDeliveryCacheFileService _base;
private LibHac.Bcat.Impl.Ipc.IDeliveryCacheFileService _base;
public IDeliveryCacheFileService(LibHac.Bcat.Detail.Ipc.IDeliveryCacheFileService baseService)
public IDeliveryCacheFileService(LibHac.Bcat.Impl.Ipc.IDeliveryCacheFileService baseService)
{
_base = baseService;
}

View file

@ -6,9 +6,9 @@ namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator
{
class IDeliveryCacheStorageService : DisposableIpcService
{
private LibHac.Bcat.Detail.Ipc.IDeliveryCacheStorageService _base;
private LibHac.Bcat.Impl.Ipc.IDeliveryCacheStorageService _base;
public IDeliveryCacheStorageService(ServiceCtx context, LibHac.Bcat.Detail.Ipc.IDeliveryCacheStorageService baseService)
public IDeliveryCacheStorageService(ServiceCtx context, LibHac.Bcat.Impl.Ipc.IDeliveryCacheStorageService baseService)
{
_base = baseService;
}
@ -17,7 +17,7 @@ namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator
// CreateFileService() -> object<nn::bcat::detail::ipc::IDeliveryCacheFileService>
public ResultCode CreateFileService(ServiceCtx context)
{
Result result = _base.CreateFileService(out LibHac.Bcat.Detail.Ipc.IDeliveryCacheFileService service);
Result result = _base.CreateFileService(out LibHac.Bcat.Impl.Ipc.IDeliveryCacheFileService service);
if (result.IsSuccess())
{
@ -31,7 +31,7 @@ namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator
// CreateDirectoryService() -> object<nn::bcat::detail::ipc::IDeliveryCacheDirectoryService>
public ResultCode CreateDirectoryService(ServiceCtx context)
{
Result result = _base.CreateDirectoryService(out LibHac.Bcat.Detail.Ipc.IDeliveryCacheDirectoryService service);
Result result = _base.CreateDirectoryService(out LibHac.Bcat.Impl.Ipc.IDeliveryCacheDirectoryService service);
if (result.IsSuccess())
{

View file

@ -1,10 +1,16 @@
using LibHac;
using LibHac.Common;
using LibHac.Common.Keys;
using LibHac.Fs;
using LibHac.FsSrv.Impl;
using LibHac.FsSrv.Sf;
using LibHac.FsSystem;
using LibHac.FsSystem.NcaUtils;
using LibHac.Spl;
using System;
using System.IO;
using System.Runtime.InteropServices;
using Path = System.IO.Path;
namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
{
@ -16,12 +22,12 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
try
{
LocalStorage storage = new LocalStorage(pfsPath, FileAccess.Read, FileMode.Open);
PartitionFileSystem nsp = new PartitionFileSystem(storage);
LocalStorage storage = new LocalStorage(pfsPath, FileAccess.Read, FileMode.Open);
ReferenceCountedDisposable<LibHac.Fs.Fsa.IFileSystem> nsp = new(new PartitionFileSystem(storage));
ImportTitleKeysFromNsp(nsp, context.Device.System.KeySet);
ImportTitleKeysFromNsp(nsp.Target, context.Device.System.KeySet);
openedFileSystem = new IFileSystem(nsp);
openedFileSystem = new IFileSystem(FileSystemInterfaceAdapter.CreateShared(ref nsp));
}
catch (HorizonResultException ex)
{
@ -45,8 +51,9 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
}
LibHac.Fs.Fsa.IFileSystem fileSystem = nca.OpenFileSystem(NcaSectionType.Data, context.Device.System.FsIntegrityCheckLevel);
var sharedFs = new ReferenceCountedDisposable<LibHac.Fs.Fsa.IFileSystem>(fileSystem);
openedFileSystem = new IFileSystem(fileSystem);
openedFileSystem = new IFileSystem(FileSystemInterfaceAdapter.CreateShared(ref sharedFs));
}
catch (HorizonResultException ex)
{
@ -99,7 +106,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
return ResultCode.PathDoesNotExist;
}
public static void ImportTitleKeysFromNsp(LibHac.Fs.Fsa.IFileSystem nsp, Keyset keySet)
public static void ImportTitleKeysFromNsp(LibHac.Fs.Fsa.IFileSystem nsp, KeySet keySet)
{
foreach (DirectoryEntryEx ticketEntry in nsp.EnumerateEntries("/", "*.tik"))
{
@ -125,5 +132,27 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
return FsPath.FromSpan(out path, pathBytes);
}
public static ref readonly FspPath GetFspPath(ServiceCtx context, int index = 0)
{
ulong position = (ulong)context.Request.PtrBuff[index].Position;
ulong size = (ulong)context.Request.PtrBuff[index].Size;
ReadOnlySpan<byte> buffer = context.Memory.GetSpan(position, (int)size);
ReadOnlySpan<FspPath> fspBuffer = MemoryMarshal.Cast<byte, FspPath>(buffer);
return ref fspBuffer[0];
}
public static ref readonly LibHac.FsSrv.Sf.Path GetSfPath(ServiceCtx context, int index = 0)
{
ulong position = (ulong)context.Request.PtrBuff[index].Position;
ulong size = (ulong)context.Request.PtrBuff[index].Size;
ReadOnlySpan<byte> buffer = context.Memory.GetSpan(position, (int)size);
ReadOnlySpan<LibHac.FsSrv.Sf.Path> pathBuffer = MemoryMarshal.Cast<byte, LibHac.FsSrv.Sf.Path>(buffer);
return ref pathBuffer[0];
}
}
}

View file

@ -1,15 +1,13 @@
using LibHac;
using LibHac.Fs;
using System;
using System.Runtime.InteropServices;
using LibHac.Sf;
namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
{
class IDirectory : IpcService
class IDirectory : DisposableIpcService
{
private LibHac.Fs.Fsa.IDirectory _baseDirectory;
private ReferenceCountedDisposable<LibHac.FsSrv.Sf.IDirectory> _baseDirectory;
public IDirectory(LibHac.Fs.Fsa.IDirectory directory)
public IDirectory(ReferenceCountedDisposable<LibHac.FsSrv.Sf.IDirectory> directory)
{
_baseDirectory = directory;
}
@ -19,14 +17,13 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
public ResultCode Read(ServiceCtx context)
{
ulong bufferPosition = context.Request.ReceiveBuff[0].Position;
ulong bufferLen = context.Request.ReceiveBuff[0].Size;
ulong bufferLen = context.Request.ReceiveBuff[0].Size;
byte[] entriesBytes = new byte[bufferLen];
Span<DirectoryEntry> entries = MemoryMarshal.Cast<byte, DirectoryEntry>(entriesBytes);
byte[] entryBuffer = new byte[bufferLen];
Result result = _baseDirectory.Read(out long entriesRead, entries);
Result result = _baseDirectory.Target.Read(out long entriesRead, new OutBuffer(entryBuffer));
context.Memory.Write(bufferPosition, entriesBytes);
context.Memory.Write(bufferPosition, entryBuffer);
context.ResponseData.Write(entriesRead);
return (ResultCode)result.Value;
@ -36,11 +33,19 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
// GetEntryCount() -> u64
public ResultCode GetEntryCount(ServiceCtx context)
{
Result result = _baseDirectory.GetEntryCount(out long entryCount);
Result result = _baseDirectory.Target.GetEntryCount(out long entryCount);
context.ResponseData.Write(entryCount);
return (ResultCode)result.Value;
}
protected override void Dispose(bool isDisposing)
{
if (isDisposing)
{
_baseDirectory?.Dispose();
}
}
}
}

View file

@ -1,13 +1,15 @@
using LibHac;
using LibHac.Fs;
using LibHac.Sf;
using Ryujinx.Common;
namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
{
class IFile : DisposableIpcService
{
private LibHac.Fs.Fsa.IFile _baseFile;
private ReferenceCountedDisposable<LibHac.FsSrv.Sf.IFile> _baseFile;
public IFile(LibHac.Fs.Fsa.IFile baseFile)
public IFile(ReferenceCountedDisposable<LibHac.FsSrv.Sf.IFile> baseFile)
{
_baseFile = baseFile;
}
@ -18,15 +20,15 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
{
ulong position = context.Request.ReceiveBuff[0].Position;
ReadOption readOption = new ReadOption(context.RequestData.ReadInt32());
ReadOption readOption = context.RequestData.ReadStruct<ReadOption>();
context.RequestData.BaseStream.Position += 4;
long offset = context.RequestData.ReadInt64();
long size = context.RequestData.ReadInt64();
byte[] data = new byte[size];
byte[] data = new byte[context.Request.ReceiveBuff[0].Size];
Result result = _baseFile.Read(out long bytesRead, offset, data, readOption);
Result result = _baseFile.Target.Read(out long bytesRead, offset, new OutBuffer(data), size, readOption);
context.Memory.Write(position, data);
@ -41,24 +43,24 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
{
ulong position = context.Request.SendBuff[0].Position;
WriteOption writeOption = new WriteOption(context.RequestData.ReadInt32());
WriteOption writeOption = context.RequestData.ReadStruct<WriteOption>();
context.RequestData.BaseStream.Position += 4;
long offset = context.RequestData.ReadInt64();
long size = context.RequestData.ReadInt64();
byte[] data = new byte[size];
byte[] data = new byte[context.Request.SendBuff[0].Size];
context.Memory.Read(position, data);
return (ResultCode)_baseFile.Write(offset, data, writeOption).Value;
return (ResultCode)_baseFile.Target.Write(offset, new InBuffer(data), size, writeOption).Value;
}
[CommandHipc(2)]
// Flush()
public ResultCode Flush(ServiceCtx context)
{
return (ResultCode)_baseFile.Flush().Value;
return (ResultCode)_baseFile.Target.Flush().Value;
}
[CommandHipc(3)]
@ -67,14 +69,14 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
{
long size = context.RequestData.ReadInt64();
return (ResultCode)_baseFile.SetSize(size).Value;
return (ResultCode)_baseFile.Target.SetSize(size).Value;
}
[CommandHipc(4)]
// GetSize() -> u64 fileSize
public ResultCode GetSize(ServiceCtx context)
{
Result result = _baseFile.GetSize(out long size);
Result result = _baseFile.Target.GetSize(out long size);
context.ResponseData.Write(size);

View file

@ -1,21 +1,19 @@
using LibHac;
using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Fsa;
using static Ryujinx.HLE.Utilities.StringUtils;
using LibHac.FsSrv.Sf;
namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
{
class IFileSystem : IpcService
class IFileSystem : DisposableIpcService
{
private LibHac.Fs.Fsa.IFileSystem _fileSystem;
private ReferenceCountedDisposable<LibHac.FsSrv.Sf.IFileSystem> _fileSystem;
public IFileSystem(LibHac.Fs.Fsa.IFileSystem provider)
public IFileSystem(ReferenceCountedDisposable<LibHac.FsSrv.Sf.IFileSystem> provider)
{
_fileSystem = provider;
}
public LibHac.Fs.Fsa.IFileSystem GetBaseFileSystem()
public ReferenceCountedDisposable<LibHac.FsSrv.Sf.IFileSystem> GetBaseFileSystem()
{
return _fileSystem;
}
@ -24,79 +22,79 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
// CreateFile(u32 createOption, u64 size, buffer<bytes<0x301>, 0x19, 0x301> path)
public ResultCode CreateFile(ServiceCtx context)
{
U8Span name = ReadUtf8Span(context);
ref readonly Path name = ref FileSystemProxyHelper.GetSfPath(context);
CreateFileOptions createOption = (CreateFileOptions)context.RequestData.ReadInt32();
int createOption = context.RequestData.ReadInt32();
context.RequestData.BaseStream.Position += 4;
long size = context.RequestData.ReadInt64();
return (ResultCode)_fileSystem.CreateFile(name, size, createOption).Value;
return (ResultCode)_fileSystem.Target.CreateFile(in name, size, createOption).Value;
}
[CommandHipc(1)]
// DeleteFile(buffer<bytes<0x301>, 0x19, 0x301> path)
public ResultCode DeleteFile(ServiceCtx context)
{
U8Span name = ReadUtf8Span(context);
ref readonly Path name = ref FileSystemProxyHelper.GetSfPath(context);
return (ResultCode)_fileSystem.DeleteFile(name).Value;
return (ResultCode)_fileSystem.Target.DeleteFile(in name).Value;
}
[CommandHipc(2)]
// CreateDirectory(buffer<bytes<0x301>, 0x19, 0x301> path)
public ResultCode CreateDirectory(ServiceCtx context)
{
U8Span name = ReadUtf8Span(context);
ref readonly Path name = ref FileSystemProxyHelper.GetSfPath(context);
return (ResultCode)_fileSystem.CreateDirectory(name).Value;
return (ResultCode)_fileSystem.Target.CreateDirectory(in name).Value;
}
[CommandHipc(3)]
// DeleteDirectory(buffer<bytes<0x301>, 0x19, 0x301> path)
public ResultCode DeleteDirectory(ServiceCtx context)
{
U8Span name = ReadUtf8Span(context);
ref readonly Path name = ref FileSystemProxyHelper.GetSfPath(context);
return (ResultCode)_fileSystem.DeleteDirectory(name).Value;
return (ResultCode)_fileSystem.Target.DeleteDirectory(in name).Value;
}
[CommandHipc(4)]
// DeleteDirectoryRecursively(buffer<bytes<0x301>, 0x19, 0x301> path)
public ResultCode DeleteDirectoryRecursively(ServiceCtx context)
{
U8Span name = ReadUtf8Span(context);
ref readonly Path name = ref FileSystemProxyHelper.GetSfPath(context);
return (ResultCode)_fileSystem.DeleteDirectoryRecursively(name).Value;
return (ResultCode)_fileSystem.Target.DeleteDirectoryRecursively(in name).Value;
}
[CommandHipc(5)]
// RenameFile(buffer<bytes<0x301>, 0x19, 0x301> oldPath, buffer<bytes<0x301>, 0x19, 0x301> newPath)
public ResultCode RenameFile(ServiceCtx context)
{
U8Span oldName = ReadUtf8Span(context, 0);
U8Span newName = ReadUtf8Span(context, 1);
ref readonly Path currentName = ref FileSystemProxyHelper.GetSfPath(context, index: 0);
ref readonly Path newName = ref FileSystemProxyHelper.GetSfPath(context, index: 1);
return (ResultCode)_fileSystem.RenameFile(oldName, newName).Value;
return (ResultCode)_fileSystem.Target.RenameFile(in currentName, in newName).Value;
}
[CommandHipc(6)]
// RenameDirectory(buffer<bytes<0x301>, 0x19, 0x301> oldPath, buffer<bytes<0x301>, 0x19, 0x301> newPath)
public ResultCode RenameDirectory(ServiceCtx context)
{
U8Span oldName = ReadUtf8Span(context, 0);
U8Span newName = ReadUtf8Span(context, 1);
ref readonly Path currentName = ref FileSystemProxyHelper.GetSfPath(context, index: 0);
ref readonly Path newName = ref FileSystemProxyHelper.GetSfPath(context, index: 1);
return (ResultCode)_fileSystem.RenameDirectory(oldName, newName).Value;
return (ResultCode)_fileSystem.Target.RenameDirectory(in currentName, in newName).Value;
}
[CommandHipc(7)]
// GetEntryType(buffer<bytes<0x301>, 0x19, 0x301> path) -> nn::fssrv::sf::DirectoryEntryType
public ResultCode GetEntryType(ServiceCtx context)
{
U8Span name = ReadUtf8Span(context);
ref readonly Path name = ref FileSystemProxyHelper.GetSfPath(context);
Result result = _fileSystem.GetEntryType(out DirectoryEntryType entryType, name);
Result result = _fileSystem.Target.GetEntryType(out uint entryType, in name);
context.ResponseData.Write((int)entryType);
@ -107,11 +105,11 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
// OpenFile(u32 mode, buffer<bytes<0x301>, 0x19, 0x301> path) -> object<nn::fssrv::sf::IFile> file
public ResultCode OpenFile(ServiceCtx context)
{
OpenMode mode = (OpenMode)context.RequestData.ReadInt32();
uint mode = context.RequestData.ReadUInt32();
U8Span name = ReadUtf8Span(context);
ref readonly Path name = ref FileSystemProxyHelper.GetSfPath(context);
Result result = _fileSystem.OpenFile(out LibHac.Fs.Fsa.IFile file, name, mode);
Result result = _fileSystem.Target.OpenFile(out ReferenceCountedDisposable<LibHac.FsSrv.Sf.IFile> file, in name, mode);
if (result.IsSuccess())
{
@ -127,11 +125,11 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
// OpenDirectory(u32 filter_flags, buffer<bytes<0x301>, 0x19, 0x301> path) -> object<nn::fssrv::sf::IDirectory> directory
public ResultCode OpenDirectory(ServiceCtx context)
{
OpenDirectoryMode mode = (OpenDirectoryMode)context.RequestData.ReadInt32();
uint mode = context.RequestData.ReadUInt32();
U8Span name = ReadUtf8Span(context);
ref readonly Path name = ref FileSystemProxyHelper.GetSfPath(context);
Result result = _fileSystem.OpenDirectory(out LibHac.Fs.Fsa.IDirectory dir, name, mode);
Result result = _fileSystem.Target.OpenDirectory(out ReferenceCountedDisposable<LibHac.FsSrv.Sf.IDirectory> dir, name, mode);
if (result.IsSuccess())
{
@ -147,16 +145,16 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
// Commit()
public ResultCode Commit(ServiceCtx context)
{
return (ResultCode)_fileSystem.Commit().Value;
return (ResultCode)_fileSystem.Target.Commit().Value;
}
[CommandHipc(11)]
// GetFreeSpaceSize(buffer<bytes<0x301>, 0x19, 0x301> path) -> u64 totalFreeSpace
public ResultCode GetFreeSpaceSize(ServiceCtx context)
{
U8Span name = ReadUtf8Span(context);
ref readonly Path name = ref FileSystemProxyHelper.GetSfPath(context);
Result result = _fileSystem.GetFreeSpaceSize(out long size, name);
Result result = _fileSystem.Target.GetFreeSpaceSize(out long size, in name);
context.ResponseData.Write(size);
@ -167,9 +165,9 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
// GetTotalSpaceSize(buffer<bytes<0x301>, 0x19, 0x301> path) -> u64 totalSize
public ResultCode GetTotalSpaceSize(ServiceCtx context)
{
U8Span name = ReadUtf8Span(context);
ref readonly Path name = ref FileSystemProxyHelper.GetSfPath(context);
Result result = _fileSystem.GetTotalSpaceSize(out long size, name);
Result result = _fileSystem.Target.GetTotalSpaceSize(out long size, in name);
context.ResponseData.Write(size);
@ -180,18 +178,18 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
// CleanDirectoryRecursively(buffer<bytes<0x301>, 0x19, 0x301> path)
public ResultCode CleanDirectoryRecursively(ServiceCtx context)
{
U8Span name = ReadUtf8Span(context);
ref readonly Path name = ref FileSystemProxyHelper.GetSfPath(context);
return (ResultCode)_fileSystem.CleanDirectoryRecursively(name).Value;
return (ResultCode)_fileSystem.Target.CleanDirectoryRecursively(in name).Value;
}
[CommandHipc(14)]
// GetFileTimeStampRaw(buffer<bytes<0x301>, 0x19, 0x301> path) -> bytes<0x20> timestamp
public ResultCode GetFileTimeStampRaw(ServiceCtx context)
{
U8Span name = ReadUtf8Span(context);
ref readonly Path name = ref FileSystemProxyHelper.GetSfPath(context);
Result result = _fileSystem.GetFileTimeStampRaw(out FileTimeStampRaw timestamp, name);
Result result = _fileSystem.Target.GetFileTimeStampRaw(out FileTimeStampRaw timestamp, in name);
context.ResponseData.Write(timestamp.Created);
context.ResponseData.Write(timestamp.Modified);
@ -206,5 +204,13 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
return (ResultCode)result.Value;
}
protected override void Dispose(bool isDisposing)
{
if (isDisposing)
{
_fileSystem?.Dispose();
}
}
}
}

View file

@ -1,13 +1,14 @@
using LibHac;
using LibHac.Sf;
using Ryujinx.HLE.HOS.Ipc;
namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
{
class IStorage : DisposableIpcService
{
private LibHac.Fs.IStorage _baseStorage;
private ReferenceCountedDisposable<LibHac.FsSrv.Sf.IStorage> _baseStorage;
public IStorage(LibHac.Fs.IStorage baseStorage)
public IStorage(ReferenceCountedDisposable<LibHac.FsSrv.Sf.IStorage> baseStorage)
{
_baseStorage = baseStorage;
}
@ -31,7 +32,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
byte[] data = new byte[size];
Result result = _baseStorage.Read((long)offset, data);
Result result = _baseStorage.Target.Read((long)offset, new OutBuffer(data), (long)size);
context.Memory.Write(buffDesc.Position, data);
@ -45,7 +46,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
// GetSize() -> u64 size
public ResultCode GetSize(ServiceCtx context)
{
Result result = _baseStorage.GetSize(out long size);
Result result = _baseStorage.Target.GetSize(out long size);
context.ResponseData.Write(size);

View file

@ -3,11 +3,11 @@ using LibHac.FsSrv;
namespace Ryujinx.HLE.HOS.Services.Fs
{
class IDeviceOperator : IpcService
class IDeviceOperator : DisposableIpcService
{
private LibHac.FsSrv.IDeviceOperator _baseOperator;
private ReferenceCountedDisposable<LibHac.FsSrv.Sf.IDeviceOperator> _baseOperator;
public IDeviceOperator(LibHac.FsSrv.IDeviceOperator baseOperator)
public IDeviceOperator(ReferenceCountedDisposable<LibHac.FsSrv.Sf.IDeviceOperator> baseOperator)
{
_baseOperator = baseOperator;
}
@ -16,7 +16,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs
// IsSdCardInserted() -> b8 is_inserted
public ResultCode IsSdCardInserted(ServiceCtx context)
{
Result result = _baseOperator.IsSdCardInserted(out bool isInserted);
Result result = _baseOperator.Target.IsSdCardInserted(out bool isInserted);
context.ResponseData.Write(isInserted);
@ -27,7 +27,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs
// IsGameCardInserted() -> b8 is_inserted
public ResultCode IsGameCardInserted(ServiceCtx context)
{
Result result = _baseOperator.IsGameCardInserted(out bool isInserted);
Result result = _baseOperator.Target.IsGameCardInserted(out bool isInserted);
context.ResponseData.Write(isInserted);
@ -38,11 +38,19 @@ namespace Ryujinx.HLE.HOS.Services.Fs
// GetGameCardHandle() -> u32 gamecard_handle
public ResultCode GetGameCardHandle(ServiceCtx context)
{
Result result = _baseOperator.GetGameCardHandle(out GameCardHandle handle);
Result result = _baseOperator.Target.GetGameCardHandle(out GameCardHandle handle);
context.ResponseData.Write(handle.Value);
return (ResultCode)result.Value;
}
protected override void Dispose(bool isDisposing)
{
if (isDisposing)
{
_baseOperator?.Dispose();
}
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -3,11 +3,11 @@ using Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy;
namespace Ryujinx.HLE.HOS.Services.Fs
{
class IMultiCommitManager : IpcService // 6.0.0+
class IMultiCommitManager : DisposableIpcService // 6.0.0+
{
private LibHac.FsSrv.IMultiCommitManager _baseCommitManager;
private ReferenceCountedDisposable<LibHac.FsSrv.Sf.IMultiCommitManager> _baseCommitManager;
public IMultiCommitManager(LibHac.FsSrv.IMultiCommitManager baseCommitManager)
public IMultiCommitManager(ReferenceCountedDisposable<LibHac.FsSrv.Sf.IMultiCommitManager> baseCommitManager)
{
_baseCommitManager = baseCommitManager;
}
@ -18,7 +18,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs
{
IFileSystem fileSystem = GetObject<IFileSystem>(context, 0);
Result result = _baseCommitManager.Add(fileSystem.GetBaseFileSystem());
Result result = _baseCommitManager.Target.Add(fileSystem.GetBaseFileSystem());
return (ResultCode)result.Value;
}
@ -27,9 +27,17 @@ namespace Ryujinx.HLE.HOS.Services.Fs
// Commit()
public ResultCode Commit(ServiceCtx context)
{
Result result = _baseCommitManager.Commit();
Result result = _baseCommitManager.Target.Commit();
return (ResultCode)result.Value;
}
protected override void Dispose(bool isDisposing)
{
if (isDisposing)
{
_baseCommitManager?.Dispose();
}
}
}
}

View file

@ -1,13 +1,13 @@
using System;
using LibHac;
using LibHac;
using LibHac.Sf;
namespace Ryujinx.HLE.HOS.Services.Fs
{
class ISaveDataInfoReader : DisposableIpcService
{
private ReferenceCountedDisposable<LibHac.FsSrv.ISaveDataInfoReader> _baseReader;
private ReferenceCountedDisposable<LibHac.FsSrv.Sf.ISaveDataInfoReader> _baseReader;
public ISaveDataInfoReader(ReferenceCountedDisposable<LibHac.FsSrv.ISaveDataInfoReader> baseReader)
public ISaveDataInfoReader(ReferenceCountedDisposable<LibHac.FsSrv.Sf.ISaveDataInfoReader> baseReader)
{
_baseReader = baseReader;
}
@ -21,7 +21,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs
byte[] infoBuffer = new byte[bufferLen];
Result result = _baseReader.Target.Read(out long readCount, infoBuffer);
Result result = _baseReader.Target.Read(out long readCount, new OutBuffer(infoBuffer));
context.Memory.Write(bufferPosition, infoBuffer);
context.ResponseData.Write(readCount);

View file

@ -1,4 +1,5 @@
using Ryujinx.HLE.HOS.Services.Mii.Types;
using LibHac;
using Ryujinx.HLE.HOS.Services.Mii.Types;
using System;
namespace Ryujinx.HLE.HOS.Services.Mii
@ -147,9 +148,9 @@ namespace Ryujinx.HLE.HOS.Services.Mii
return GetDefault(flag, ref count, elements);
}
public ResultCode InitializeDatabase(Switch device)
public ResultCode InitializeDatabase(HorizonClient horizonClient)
{
_miiDatabase.InitializeDatabase(device);
_miiDatabase.InitializeDatabase(horizonClient);
_miiDatabase.LoadFromFile(out _isBroken);
// Nintendo ignore any error code from before

View file

@ -1,7 +1,9 @@
using LibHac;
using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Fsa;
using LibHac.Fs.Shim;
using LibHac.Ncm;
using Ryujinx.HLE.HOS.Services.Mii.Types;
using System.Runtime.CompilerServices;
@ -14,8 +16,6 @@ namespace Ryujinx.HLE.HOS.Services.Mii
private const ulong DatabaseTestSaveDataId = 0x8000000000000031;
private const ulong DatabaseSaveDataId = 0x8000000000000030;
private const ulong NsTitleId = 0x010000000000001F;
private const ulong SdbTitleId = 0x0100000000000039;
private static U8String DatabasePath = new U8String("mii:/MiiDatabase.dat");
private static U8String MountName = new U8String("mii");
@ -23,7 +23,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii
private NintendoFigurineDatabase _database;
private bool _isDirty;
private FileSystemClient _filesystemClient;
private HorizonClient _horizonClient;
protected ulong UpdateCounter { get; private set; }
@ -94,74 +94,62 @@ namespace Ryujinx.HLE.HOS.Services.Mii
return virtualIndex;
}
public void InitializeDatabase(Switch device)
public void InitializeDatabase(HorizonClient horizonClient)
{
_filesystemClient = device.FileSystem.FsClient;
_horizonClient = horizonClient;
// Ensure we have valid data in the database
_database.Format();
// TODO: Unmount is currently not implemented properly at dispose, implement that and decrement MountCounter.
MountCounter = 0;
MountSave();
}
private Result MountSave()
{
Result result = Result.Success;
if (MountCounter == 0)
if (MountCounter != 0)
{
ulong targetSaveDataId;
ulong targetTitleId;
MountCounter++;
return Result.Success;
}
ulong saveDataId = IsTestModeEnabled ? DatabaseTestSaveDataId : DatabaseSaveDataId;
Result result = _horizonClient.Fs.MountSystemSaveData(MountName, SaveDataSpaceId.System, saveDataId);
if (result.IsFailure())
{
if (!ResultFs.TargetNotFound.Includes(result))
return result;
if (IsTestModeEnabled)
{
targetSaveDataId = DatabaseTestSaveDataId;
targetTitleId = SdbTitleId;
result = _horizonClient.Fs.CreateSystemSaveData(saveDataId, 0x10000, 0x10000,
SaveDataFlags.KeepAfterResettingSystemSaveDataWithoutUserSaveData);
if (result.IsFailure()) return result;
}
else
{
targetSaveDataId = DatabaseSaveDataId;
// Nintendo use NS TitleID when creating the production save even on sdb, let's follow that behaviour.
targetTitleId = NsTitleId;
result = _horizonClient.Fs.CreateSystemSaveData(saveDataId, SystemProgramId.Ns.Value, 0x10000,
0x10000, SaveDataFlags.KeepAfterResettingSystemSaveDataWithoutUserSaveData);
if (result.IsFailure()) return result;
}
U8Span mountName = new U8Span(MountName);
result = _filesystemClient.MountSystemSaveData(mountName, SaveDataSpaceId.System, targetSaveDataId);
if (result.IsFailure())
{
if (ResultFs.TargetNotFound.Includes(result))
{
// TODO: We're currently always specifying the owner ID because FS doesn't have a way of
// knowing which process called it
result = _filesystemClient.CreateSystemSaveData(targetSaveDataId, targetTitleId, 0x10000,
0x10000, SaveDataFlags.KeepAfterResettingSystemSaveDataWithoutUserSaveData);
if (result.IsFailure()) return result;
result = _filesystemClient.MountSystemSaveData(mountName, SaveDataSpaceId.System, targetSaveDataId);
if (result.IsFailure()) return result;
}
}
if (result == Result.Success)
{
MountCounter++;
}
result = _horizonClient.Fs.MountSystemSaveData(MountName, SaveDataSpaceId.System, saveDataId);
if (result.IsFailure()) return result;
}
if (result == Result.Success)
{
MountCounter++;
}
return result;
}
public ResultCode DeleteFile()
{
ResultCode result = (ResultCode)_filesystemClient.DeleteFile(DatabasePath).Value;
ResultCode result = (ResultCode)_horizonClient.Fs.DeleteFile(DatabasePath).Value;
_filesystemClient.Commit(MountName);
_horizonClient.Fs.Commit(MountName);
return result;
}
@ -179,17 +167,17 @@ namespace Ryujinx.HLE.HOS.Services.Mii
ResetDatabase();
Result result = _filesystemClient.OpenFile(out FileHandle handle, DatabasePath, OpenMode.Read);
Result result = _horizonClient.Fs.OpenFile(out FileHandle handle, DatabasePath, OpenMode.Read);
if (result.IsSuccess())
{
result = _filesystemClient.GetFileSize(out long fileSize, handle);
result = _horizonClient.Fs.GetFileSize(out long fileSize, handle);
if (result.IsSuccess())
{
if (fileSize == Unsafe.SizeOf<NintendoFigurineDatabase>())
{
result = _filesystemClient.ReadFile(handle, 0, _database.AsSpan());
result = _horizonClient.Fs.ReadFile(handle, 0, _database.AsSpan());
if (result.IsSuccess())
{
@ -211,7 +199,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii
}
}
_filesystemClient.CloseFile(handle);
_horizonClient.Fs.CloseFile(handle);
return (ResultCode)result.Value;
}
@ -225,32 +213,32 @@ namespace Ryujinx.HLE.HOS.Services.Mii
private Result ForceSaveDatabase()
{
Result result = _filesystemClient.CreateFile(DatabasePath, Unsafe.SizeOf<NintendoFigurineDatabase>());
Result result = _horizonClient.Fs.CreateFile(DatabasePath, Unsafe.SizeOf<NintendoFigurineDatabase>());
if (result.IsSuccess() || ResultFs.PathAlreadyExists.Includes(result))
{
result = _filesystemClient.OpenFile(out FileHandle handle, DatabasePath, OpenMode.Write);
result = _horizonClient.Fs.OpenFile(out FileHandle handle, DatabasePath, OpenMode.Write);
if (result.IsSuccess())
{
result = _filesystemClient.GetFileSize(out long fileSize, handle);
result = _horizonClient.Fs.GetFileSize(out long fileSize, handle);
if (result.IsSuccess())
{
// If the size doesn't match, recreate the file
if (fileSize != Unsafe.SizeOf<NintendoFigurineDatabase>())
{
_filesystemClient.CloseFile(handle);
_horizonClient.Fs.CloseFile(handle);
result = _filesystemClient.DeleteFile(DatabasePath);
result = _horizonClient.Fs.DeleteFile(DatabasePath);
if (result.IsSuccess())
{
result = _filesystemClient.CreateFile(DatabasePath, Unsafe.SizeOf<NintendoFigurineDatabase>());
result = _horizonClient.Fs.CreateFile(DatabasePath, Unsafe.SizeOf<NintendoFigurineDatabase>());
if (result.IsSuccess())
{
result = _filesystemClient.OpenFile(out handle, DatabasePath, OpenMode.Write);
result = _horizonClient.Fs.OpenFile(out handle, DatabasePath, OpenMode.Write);
}
}
@ -260,10 +248,10 @@ namespace Ryujinx.HLE.HOS.Services.Mii
}
}
result = _filesystemClient.WriteFile(handle, 0, _database.AsReadOnlySpan(), WriteOption.Flush);
result = _horizonClient.Fs.WriteFile(handle, 0, _database.AsReadOnlySpan(), WriteOption.Flush);
}
_filesystemClient.CloseFile(handle);
_horizonClient.Fs.CloseFile(handle);
}
}
@ -271,7 +259,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii
{
_isDirty = false;
result = _filesystemClient.Commit(MountName);
result = _horizonClient.Fs.Commit(MountName);
}
return result;