Reorganize SaveDataFileSystemService

This commit is contained in:
Alex Barney 2024-01-26 15:49:50 -07:00
parent 49edb744c5
commit beca253086
12 changed files with 705 additions and 636 deletions

View file

@ -207,7 +207,7 @@ public static class PathNormalizer
/// contains <see langword="true"/> if the path is normalized or <see langword="false"/> if it is not.
/// Contents are undefined if the function does not return <see cref="Result.Success"/>.
/// </param>
/// <param name="length">When this function returns <see cref="Result.Success"/> and
/// <param name="outNormalizedLength">When this function returns <see cref="Result.Success"/> and
/// <paramref name="isNormalized"/> is <see langword="true"/>, contains the length of the normalized path.
/// Contents are undefined if the function does not return <see cref="Result.Success"/>
/// or <paramref name="isNormalized"/> is <see langword="false"/>.
@ -216,15 +216,15 @@ public static class PathNormalizer
/// <returns><see cref="Result.Success"/>: The operation was successful.<br/>
/// <see cref="ResultFs.InvalidCharacter"/>: The path contains an invalid character.<br/>
/// <see cref="ResultFs.InvalidPathFormat"/>: The path is not in a valid format.</returns>
public static Result IsNormalized(out bool isNormalized, out int length, ReadOnlySpan<byte> path)
public static Result IsNormalized(out bool isNormalized, out int outNormalizedLength, ReadOnlySpan<byte> path)
{
return IsNormalized(out isNormalized, out length, path, false);
return IsNormalized(out isNormalized, out outNormalizedLength, path, false);
}
public static Result IsNormalized(out bool isNormalized, out int length, ReadOnlySpan<byte> path,
public static Result IsNormalized(out bool isNormalized, out int outNormalizedLength, ReadOnlySpan<byte> path,
bool allowAllCharacters)
{
UnsafeHelpers.SkipParamInit(out isNormalized, out length);
UnsafeHelpers.SkipParamInit(out isNormalized, out outNormalizedLength);
var state = PathState.Initial;
int pathLength = 0;
@ -311,7 +311,7 @@ public static class PathNormalizer
break;
}
length = pathLength;
outNormalizedLength = pathLength;
return Result.Success;
}

View file

@ -47,13 +47,13 @@ public static class FileSystemServerInitializer
InitializeFileSystemProxyServer(client, server);
var saveService = new SaveDataFileSystemService(fspConfig.SaveDataFileSystemService, processId);
using SharedRef<SaveDataFileSystemService> saveService = SaveDataFileSystemService.CreateShared(fspConfig.SaveDataFileSystemService, processId);
saveService.CleanUpTemporaryStorage().IgnoreResult();
saveService.CleanUpSaveData().IgnoreResult();
saveService.CompleteSaveDataExtension().IgnoreResult();
saveService.FixSaveData().IgnoreResult();
saveService.RecoverMultiCommit().IgnoreResult();
saveService.Get.CleanUpTemporaryStorage().IgnoreResult();
saveService.Get.CleanUpSaveData().IgnoreResult();
saveService.Get.CompleteSaveDataExtension().IgnoreResult();
saveService.Get.FixSaveData().IgnoreResult();
saveService.Get.RecoverMultiCommit().IgnoreResult();
config.StorageDeviceManagerFactory.SetReady(StorageDevicePortId.SdCard, null);
config.StorageDeviceManagerFactory.SetReady(StorageDevicePortId.GameCard, null);

View file

@ -204,7 +204,7 @@ public class DirectoryInterfaceAdapter : IDirectorySf
const int maxTryCount = 2;
UnsafeHelpers.SkipParamInit(out entriesRead);
Span<DirectoryEntry> entries = MemoryMarshal.Cast<byte, DirectoryEntry>(entryBuffer.Buffer);
Span<DirectoryEntry> entries = entryBuffer.AsSpan<DirectoryEntry>();
Result res = Result.Success;
long numRead = 0;

View file

@ -10,5 +10,5 @@ public interface ISaveDataMultiCommitCoreInterface : IDisposable
Result RecoverMultiCommit();
Result IsProvisionallyCommittedSaveData(out bool isProvisionallyCommitted, in SaveDataInfo saveInfo);
Result RecoverProvisionallyCommittedSaveData(in SaveDataInfo saveInfo, bool doRollback);
Result OpenMultiCommitContext(ref SharedRef<IFileSystem> contextFileSystem);
Result OpenMultiCommitContext(ref SharedRef<IFileSystem> outContextFileSystem);
}

View file

@ -20,7 +20,7 @@ public interface ISaveDataTransferCoreInterface : IDisposable
Result CancelSaveDataCreation(ulong saveDataId, SaveDataSpaceId spaceId);
Result OpenSaveDataFile(ref SharedRef<IFile> oufFile, SaveDataSpaceId spaceId, ulong saveDataId, OpenMode mode);
Result OpenSaveDataMetaFileRaw(ref SharedRef<IFile> outFile, SaveDataSpaceId spaceId, ulong saveDataId, SaveDataMetaType metaType, OpenMode mode);
Result OpenSaveDataInternalStorageFileSystemCore(ref SharedRef<IFileSystem> fileSystem, SaveDataSpaceId spaceId, ulong saveDataId, bool useSecondMacKey);
Result OpenSaveDataInternalStorageFileSystemCore(ref SharedRef<IFileSystem> outFileSystem, SaveDataSpaceId spaceId, ulong saveDataId, bool isTemporaryTransferSave);
Result OpenSaveDataFileSystemCore(ref SharedRef<IFileSystem> outFileSystem, out ulong outSaveDataId, SaveDataSpaceId spaceId, in SaveDataAttribute attribute, bool openReadOnly, bool cacheExtraData);
Result ExtendSaveDataFileSystem(SaveDataSpaceId spaceId, ulong saveDataId, long dataSize, long journalSize);
Result DeleteSaveDataFileSystemBySaveDataSpaceId(SaveDataSpaceId spaceId, ulong saveDataId);

View file

@ -59,8 +59,7 @@ internal readonly struct ProgramIndexRegistryService
return Result.Success;
// Verify that the provided buffer is large enough to hold "programCount" entries
ReadOnlySpan<ProgramIndexMapInfo>
mapInfo = MemoryMarshal.Cast<byte, ProgramIndexMapInfo>(programIndexMapInfo.Buffer);
ReadOnlySpan<ProgramIndexMapInfo> mapInfo = programIndexMapInfo.AsSpan<ProgramIndexMapInfo>();
if (mapInfo.Length < programCount)
return ResultFs.InvalidSize.Log();

View file

@ -16,6 +16,7 @@ using LibHac.Sf;
using LibHac.Util;
using static LibHac.Fs.SaveData;
using static LibHac.Fs.StringTraits;
using static LibHac.FsSrv.Anonymous;
using IFileSystem = LibHac.Fs.Fsa.IFileSystem;
using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
using IFile = LibHac.Fs.Fsa.IFile;
@ -25,47 +26,7 @@ using Utility = LibHac.FsSystem.Utility;
namespace LibHac.FsSrv;
/// <summary>
/// Handles save-data-related calls for <see cref="FileSystemProxyImpl"/>.
/// </summary>
/// <remarks>FS will have one instance of this class for every connected process.
/// The FS permissions of the calling process are checked on every function call.
/// <br/>Based on nnSdk 14.3.0 (FS 14.1.0)</remarks>
internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISaveDataMultiCommitCoreInterface
{
private const int OpenEntrySemaphoreCount = 256;
private const int SaveMountSemaphoreCount = 10;
private const int SaveDataBlockSize = 0x4000;
private WeakRef<SaveDataFileSystemService> _selfReference;
private SaveDataFileSystemServiceImpl _serviceImpl;
private ulong _processId;
private Path.Stored _saveDataRootPath;
private SemaphoreAdapter _openEntryCountSemaphore;
private SemaphoreAdapter _saveDataMountCountSemaphore;
private HorizonClient Hos => _serviceImpl.Hos;
private SharedRef<SaveDataFileSystemService> GetSharedFromThis() =>
SharedRef<SaveDataFileSystemService>.Create(in _selfReference);
private SharedRef<ISaveDataMultiCommitCoreInterface> GetSharedMultiCommitInterfaceFromThis() =>
SharedRef<ISaveDataMultiCommitCoreInterface>.Create(in _selfReference);
public static SharedRef<SaveDataFileSystemService> CreateShared(SaveDataFileSystemServiceImpl serviceImpl, ulong processId)
{
// Create the service
var saveService = new SaveDataFileSystemService(serviceImpl, processId);
// Wrap the service in a ref-counter and give the service a weak self-reference
using var sharedService = new SharedRef<SaveDataFileSystemService>(saveService);
saveService._selfReference.Set(in sharedService);
return SharedRef<SaveDataFileSystemService>.CreateMove(ref sharedService.Ref);
}
private class SaveDataOpenCountAdapter : IEntryOpenCountSemaphoreManager
file class SaveDataOpenCountAdapter : IEntryOpenCountSemaphoreManager
{
private SharedRef<SaveDataFileSystemService> _saveService;
@ -85,7 +46,69 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
}
}
private static Result CheckOpenSaveDataInfoReaderAccessControl(ProgramInfo programInfo, SaveDataSpaceId spaceId)
file static class Anonymous
{
public static SaveDataSpaceId ConvertToRealSpaceId(SaveDataSpaceId spaceId)
{
return spaceId == SaveDataSpaceId.ProperSystem || spaceId == SaveDataSpaceId.SafeMode
? SaveDataSpaceId.System
: spaceId;
}
public static bool IsStaticSaveDataIdValueRange(ulong id)
{
return unchecked((long)id) < 0;
}
public static bool IsDirectorySaveDataExtraData(in SaveDataExtraData extraData)
{
return extraData.OwnerId == 0 && extraData.DataSize == 0 && extraData.JournalSize == 0;
}
public static void ModifySaveDataExtraData(ref SaveDataExtraData currentExtraData, in SaveDataExtraData extraData,
in SaveDataExtraData extraDataMask)
{
Span<byte> currentExtraDataBytes = SpanHelpers.AsByteSpan(ref currentExtraData);
ReadOnlySpan<byte> extraDataBytes = SpanHelpers.AsReadOnlyByteSpan(in extraData);
ReadOnlySpan<byte> extraDataMaskBytes = SpanHelpers.AsReadOnlyByteSpan(in extraDataMask);
for (int i = 0; i < Unsafe.SizeOf<SaveDataExtraData>(); i++)
{
currentExtraDataBytes[i] = (byte)(extraDataBytes[i] & extraDataMaskBytes[i] |
currentExtraDataBytes[i] & ~extraDataMaskBytes[i]);
}
}
public static void MaskExtraData(ref SaveDataExtraData extraData, in SaveDataExtraData extraDataMask)
{
Span<byte> extraDataBytes = SpanHelpers.AsByteSpan(ref extraData);
ReadOnlySpan<byte> extraDataMaskBytes = SpanHelpers.AsReadOnlyByteSpan(in extraDataMask);
for (int i = 0; i < Unsafe.SizeOf<SaveDataExtraData>(); i++)
{
extraDataBytes[i] &= extraDataMaskBytes[i];
}
}
public static StorageLayoutType DecidePossibleStorageFlag(SaveDataType type, SaveDataSpaceId spaceId)
{
if (type == SaveDataType.Cache || type == SaveDataType.Bcat)
return StorageLayoutType.Bis | StorageLayoutType.SdCard | StorageLayoutType.Usb;
if (type == SaveDataType.System && (spaceId == SaveDataSpaceId.SdSystem || spaceId == SaveDataSpaceId.SdUser))
return StorageLayoutType.SdCard | StorageLayoutType.Usb;
return StorageLayoutType.Bis;
}
public static SaveDataFormatType GetSaveDataFormatType(in SaveDataAttribute attribute)
{
return SaveDataProperties.IsJournalingSupported(attribute.Type)
? SaveDataFormatType.Normal
: SaveDataFormatType.NoJournal;
}
public static Result CheckOpenSaveDataInfoReaderAccessControl(ProgramInfo programInfo, SaveDataSpaceId spaceId)
{
Assert.SdkNotNull(programInfo);
@ -110,10 +133,11 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
return Result.Success;
}
}
private static class SaveDataAccessibilityChecker
file static class SaveDataAccessibilityChecker
{
public delegate Result ExtraDataGetter(out SaveDataExtraData extraData);
public delegate Result ExtraDataReader(out SaveDataExtraData extraData);
public static Result CheckCreate(in SaveDataAttribute attribute, ulong ownerId, ProgramInfo programInfo,
ProgramId programId)
@ -204,11 +228,11 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
}
public static Result CheckOpen(in SaveDataAttribute attribute, ProgramInfo programInfo,
ExtraDataGetter extraDataGetter)
ExtraDataReader readExtraData)
{
AccessControl accessControl = programInfo.AccessControl;
Result res = GetAccessibilityForSaveData(out Accessibility accessibility, programInfo, extraDataGetter);
Result res = GetAccessibilityForSaveData(out Accessibility accessibility, programInfo, readExtraData);
if (res.IsFailure()) return res.Miss();
// Note: This is correct. Even if a program only has read accessibility to a save data,
@ -234,7 +258,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
}
public static Result CheckDelete(in SaveDataAttribute attribute, ProgramInfo programInfo,
ExtraDataGetter extraDataGetter)
ExtraDataReader readExtraData)
{
AccessControl accessControl = programInfo.AccessControl;
@ -252,7 +276,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
}
// Otherwise the program needs the DeleteOwnSaveData permission and write access to the save
Result res = GetAccessibilityForSaveData(out Accessibility accessibility, programInfo, extraDataGetter);
Result res = GetAccessibilityForSaveData(out Accessibility accessibility, programInfo, readExtraData);
if (res.IsFailure()) return res.Miss();
if (accessControl.CanCall(OperationType.DeleteOwnSaveData) && accessibility.CanWrite)
@ -264,7 +288,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
}
public static Result CheckExtend(SaveDataSpaceId spaceId, in SaveDataAttribute attribute,
ProgramInfo programInfo, ExtraDataGetter extraDataGetter)
ProgramInfo programInfo, ExtraDataReader readExtraData)
{
AccessControl accessControl = programInfo.AccessControl;
@ -276,7 +300,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
case SaveDataSpaceId.SafeMode:
{
Result res = GetAccessibilityForSaveData(out Accessibility accessibility, programInfo,
extraDataGetter);
readExtraData);
if (res.IsFailure()) return res.Miss();
// The program needs the ExtendSystemSaveData permission and either one of
@ -296,7 +320,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
bool canAccess = accessControl.CanCall(OperationType.ExtendSaveData);
Result res = GetAccessibilityForSaveData(out Accessibility accessibility, programInfo,
extraDataGetter);
readExtraData);
if (res.IsFailure()) return res.Miss();
if (attribute.ProgramId == programInfo.ProgramId || accessibility.CanRead)
@ -323,13 +347,13 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
}
public static Result CheckReadExtraData(in SaveDataAttribute attribute, in SaveDataExtraData mask,
ProgramInfo programInfo, ExtraDataGetter extraDataGetter)
ProgramInfo programInfo, ExtraDataReader readExtraData)
{
AccessControl accessControl = programInfo.AccessControl;
bool canAccess = accessControl.CanCall(OperationType.ReadSaveDataFileSystemExtraData);
Result res = GetAccessibilityForSaveData(out Accessibility accessibility, programInfo, extraDataGetter);
Result res = GetAccessibilityForSaveData(out Accessibility accessibility, programInfo, readExtraData);
if (res.IsFailure()) return res.Miss();
SaveDataExtraData emptyMask = default;
@ -365,7 +389,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
}
public static Result CheckWriteExtraData(in SaveDataAttribute attribute, in SaveDataExtraData mask,
ProgramInfo programInfo, ExtraDataGetter extraDataGetter)
ProgramInfo programInfo, ExtraDataReader readExtraData)
{
AccessControl accessControl = programInfo.AccessControl;
@ -377,7 +401,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
if (SaveDataProperties.IsSystemSaveData(attribute.Type))
{
Result res = GetAccessibilityForSaveData(out Accessibility accessibility, programInfo,
extraDataGetter);
readExtraData);
if (res.IsFailure()) return res.Miss();
canAccess |= accessibility.CanWrite;
@ -386,7 +410,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
if ((mask.Flags & ~SaveDataFlags.Restore) == 0)
{
Result res = GetAccessibilityForSaveData(out Accessibility accessibility, programInfo,
extraDataGetter);
readExtraData);
if (res.IsFailure()) return res.Miss();
canAccess |= accessibility.CanWrite;
@ -494,11 +518,11 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
}
private static Result GetAccessibilityForSaveData(out Accessibility accessibility, ProgramInfo programInfo,
ExtraDataGetter extraDataGetter)
ExtraDataReader readExtraData)
{
UnsafeHelpers.SkipParamInit(out accessibility);
Result res = extraDataGetter(out SaveDataExtraData extraData);
Result res = readExtraData(out SaveDataExtraData extraData);
if (res.IsFailure())
{
if (ResultFs.TargetNotFound.Includes(res))
@ -522,92 +546,78 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
}
}
public SaveDataFileSystemService(SaveDataFileSystemServiceImpl serviceImpl, ulong processId)
/// <summary>
/// Handles save-data-related calls for <see cref="FileSystemProxyImpl"/>.
/// </summary>
/// <remarks>FS will have one instance of this class for every connected process.
/// The FS permissions of the calling process are checked on every function call.
/// <br/>Based on nnSdk 14.3.0 (FS 14.1.0)</remarks>
internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISaveDataMultiCommitCoreInterface
{
private const int OpenEntrySemaphoreCount = 256;
private const int SaveMountSemaphoreCount = 10;
private const int SaveDataBlockSize = 0x4000;
private const ulong UnspecifiedSaveDataId = ulong.MaxValue;
private WeakRef<SaveDataFileSystemService> _selfReference;
private SaveDataFileSystemServiceImpl _serviceImpl;
private ulong _processId;
private Path.Stored _saveDataRootPath;
private SemaphoreAdapter _openEntryCountSemaphore;
private SemaphoreAdapter _saveDataMountCountSemaphore;
private HorizonClient Hos => _serviceImpl.Hos;
private SharedRef<SaveDataFileSystemService> GetSharedFromThis() =>
SharedRef<SaveDataFileSystemService>.Create(in _selfReference);
private SharedRef<ISaveDataMultiCommitCoreInterface> GetSharedMultiCommitInterfaceFromThis() =>
SharedRef<ISaveDataMultiCommitCoreInterface>.Create(in _selfReference);
public static SharedRef<SaveDataFileSystemService> CreateShared(SaveDataFileSystemServiceImpl serviceImpl, ulong processId)
{
// Create the service
var saveService = new SaveDataFileSystemService(serviceImpl, processId);
// Wrap the service in a ref-counter and give the service a weak self-reference
using var sharedService = new SharedRef<SaveDataFileSystemService>(saveService);
saveService._selfReference.Set(in sharedService);
return SharedRef<SaveDataFileSystemService>.CreateMove(ref sharedService.Ref);
}
private SaveDataFileSystemService(SaveDataFileSystemServiceImpl serviceImpl, ulong processId)
{
_serviceImpl = serviceImpl;
_processId = processId;
using var path = new Path();
_openEntryCountSemaphore = new SemaphoreAdapter(OpenEntrySemaphoreCount, OpenEntrySemaphoreCount);
_saveDataMountCountSemaphore = new SemaphoreAdapter(SaveMountSemaphoreCount, SaveMountSemaphoreCount);
path.InitializeAsEmpty().IgnoreResult();
_saveDataRootPath.Initialize(in path).IgnoreResult();
}
public void Dispose()
{
_openEntryCountSemaphore.Dispose();
_saveDataMountCountSemaphore.Dispose();
_openEntryCountSemaphore.Dispose();
_saveDataRootPath.Dispose();
_selfReference.Destroy();
}
private Result GetProgramInfo(out ProgramInfo programInfo)
{
var registry = new ProgramRegistryImpl(_serviceImpl.FsServer);
return registry.GetProgramInfo(out programInfo, _processId);
return registry.GetProgramInfo(out programInfo, _processId).Ret();
}
private Result GetProgramInfoByProgramId(out ProgramInfo programInfo, ulong programId)
{
var registry = new ProgramRegistryImpl(_serviceImpl.FsServer);
return registry.GetProgramInfoByProgramId(out programInfo, programId);
}
private static SaveDataSpaceId ConvertToRealSpaceId(SaveDataSpaceId spaceId)
{
return spaceId == SaveDataSpaceId.ProperSystem || spaceId == SaveDataSpaceId.SafeMode
? SaveDataSpaceId.System
: spaceId;
}
private static bool IsStaticSaveDataIdValueRange(ulong id)
{
return unchecked((long)id) < 0;
}
private static bool IsDirectorySaveDataExtraData(in SaveDataExtraData extraData)
{
return extraData.OwnerId == 0 && extraData.DataSize == 0 && extraData.JournalSize == 0;
}
private static void ModifySaveDataExtraData(ref SaveDataExtraData currentExtraData, in SaveDataExtraData extraData,
in SaveDataExtraData extraDataMask)
{
Span<byte> currentExtraDataBytes = SpanHelpers.AsByteSpan(ref currentExtraData);
ReadOnlySpan<byte> extraDataBytes = SpanHelpers.AsReadOnlyByteSpan(in extraData);
ReadOnlySpan<byte> extraDataMaskBytes = SpanHelpers.AsReadOnlyByteSpan(in extraDataMask);
for (int i = 0; i < Unsafe.SizeOf<SaveDataExtraData>(); i++)
{
currentExtraDataBytes[i] = (byte)(extraDataBytes[i] & extraDataMaskBytes[i] |
currentExtraDataBytes[i] & ~extraDataMaskBytes[i]);
}
}
private static void MaskExtraData(ref SaveDataExtraData extraData, in SaveDataExtraData extraDataMask)
{
Span<byte> extraDataBytes = SpanHelpers.AsByteSpan(ref extraData);
ReadOnlySpan<byte> extraDataMaskBytes = SpanHelpers.AsReadOnlyByteSpan(in extraDataMask);
for (int i = 0; i < Unsafe.SizeOf<SaveDataExtraData>(); i++)
{
extraDataBytes[i] &= extraDataMaskBytes[i];
}
}
private static StorageLayoutType DecidePossibleStorageFlag(SaveDataType type, SaveDataSpaceId spaceId)
{
if (type == SaveDataType.Cache || type == SaveDataType.Bcat)
return StorageLayoutType.Bis | StorageLayoutType.SdCard | StorageLayoutType.Usb;
if (type == SaveDataType.System && (spaceId == SaveDataSpaceId.SdSystem || spaceId == SaveDataSpaceId.SdUser))
return StorageLayoutType.SdCard | StorageLayoutType.Usb;
return StorageLayoutType.Bis;
}
private static SaveDataFormatType GetSaveDataFormatType(in SaveDataAttribute attribute)
{
return SaveDataProperties.IsJournalingSupported(attribute.Type)
? SaveDataFormatType.Normal
: SaveDataFormatType.NoJournal;
return registry.GetProgramInfoByProgramId(out programInfo, programId).Ret();
}
public Result GetFreeSpaceSizeForSaveData(out long outFreeSpaceSize, SaveDataSpaceId spaceId)
@ -617,7 +627,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
using var scopedContext = new ScopedStorageLayoutTypeSetter(StorageLayoutType.NonGameCard);
using var fileSystem = new SharedRef<IFileSystem>();
Result res = _serviceImpl.OpenSaveDataDirectoryFileSystem(ref fileSystem.Ref, spaceId);
Result res = _serviceImpl.OpenSaveDataDirectoryFileSystem(ref fileSystem.Ref, spaceId, UnspecifiedSaveDataId);
if (res.IsFailure()) return res.Miss();
using var pathRoot = new Path();
@ -654,14 +664,14 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
res = OpenSaveDataIndexerAccessor(ref accessor.Ref, SaveDataSpaceId.System);
if (res.IsFailure()) return res.Miss();
ReadOnlySpan<ulong> ids = MemoryMarshal.Cast<byte, ulong>(saveDataIds.Buffer);
ReadOnlySpan<ulong> saveDataIdArray = saveDataIds.AsSpan<ulong>();
try
{
// Try to set the state of all the save IDs as being marked for deletion.
for (int i = 0; i < ids.Length; i++)
for (int i = 0; i < saveDataIdArray.Length; i++)
{
res = accessor.Get.GetInterface().SetState(ids[i], SaveDataState.MarkedForDeletion);
res = accessor.Get.GetInterface().SetState(saveDataIdArray[i], SaveDataState.MarkedForDeletion);
if (res.IsFailure()) return res.Miss();
}
@ -686,13 +696,6 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
}
}
public Result DeleteSaveDataFileSystem(ulong saveDataId)
{
using var scopedContext = new ScopedStorageLayoutTypeSetter(StorageLayoutType.Bis);
return DeleteSaveDataFileSystemCommon(SaveDataSpaceId.System, saveDataId).Ret();
}
private Result DeleteSaveDataFileSystemCore(SaveDataSpaceId spaceId, ulong saveDataId, bool wipeSaveFile)
{
// Delete the save data's meta files
@ -709,6 +712,13 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
return Result.Success;
}
public Result DeleteSaveDataFileSystem(ulong saveDataId)
{
using var scopedContext = new ScopedStorageLayoutTypeSetter(StorageLayoutType.Bis);
return DeleteSaveDataFileSystemCommon(SaveDataSpaceId.System, saveDataId).Ret();
}
public Result DeleteSaveDataFileSystemBySaveDataSpaceId(SaveDataSpaceId spaceId, ulong saveDataId)
{
using var scopedContext = new ScopedStorageLayoutTypeSetter(StorageLayoutType.NonGameCard);
@ -777,13 +787,13 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
if (res.IsFailure()) return res.Miss();
Result GetExtraData(out SaveDataExtraData data)
Result ReadExtraData(out SaveDataExtraData data)
{
var path = _saveDataRootPath.DangerousGetPath();
using Path path = _saveDataRootPath.DangerousGetPath();
return _serviceImpl.ReadSaveDataFileSystemExtraData(out data, targetSpaceId, saveDataId, key.Type, in path);
}
res = SaveDataAccessibilityChecker.CheckDelete(in key, programInfo, GetExtraData);
res = SaveDataAccessibilityChecker.CheckDelete(in key, programInfo, ReadExtraData);
if (res.IsFailure()) return res.Miss();
// Pre-delete checks successful. Put the save in the Processing state until deletion is finished.
@ -822,7 +832,36 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
public Result SwapSaveDataKeyAndState(SaveDataSpaceId spaceId, ulong saveDataId1, ulong saveDataId2)
{
throw new NotImplementedException();
using var accessor = new UniqueRef<SaveDataIndexerAccessor>();
Result res = OpenSaveDataIndexerAccessor(ref accessor.Ref, spaceId);
if (res.IsFailure()) return res.Miss();
res = accessor.Get.GetInterface().GetKey(out SaveDataAttribute lhsKey, saveDataId1);
if (res.IsFailure()) return res.Miss();
res = accessor.Get.GetInterface().GetValue(out SaveDataIndexerValue lhsValue, saveDataId1);
if (res.IsFailure()) return res.Miss();
res = accessor.Get.GetInterface().GetKey(out SaveDataAttribute rhsKey, saveDataId2);
if (res.IsFailure()) return res.Miss();
res = accessor.Get.GetInterface().GetValue(out SaveDataIndexerValue rhsValue, saveDataId2);
if (res.IsFailure()) return res.Miss();
var rhsState = rhsValue.State;
rhsValue.State = lhsValue.State;
lhsValue.State = rhsState;
res = accessor.Get.GetInterface().SetValue(in rhsKey, in lhsValue);
if (res.IsFailure()) return res.Miss();
res = accessor.Get.GetInterface().SetValue(in lhsKey, in rhsValue);
if (res.IsFailure()) return res.Miss();
res = accessor.Get.GetInterface().Commit();
if (res.IsFailure()) return res.Miss();
return Result.Success;
}
public Result SetSaveDataState(SaveDataSpaceId spaceId, ulong saveDataId, SaveDataState state)
@ -842,7 +881,35 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
public Result CancelSaveDataCreation(ulong saveDataId, SaveDataSpaceId spaceId)
{
throw new NotImplementedException();
using var scopedContext = new ScopedStorageLayoutTypeSetter(StorageLayoutType.NonGameCard);
// Don't allow deleting the save data indexer
if (saveDataId == SaveIndexerId)
return ResultFs.InvalidArgument.Log();
// Delete the actual save data
Result res = DeleteSaveDataFileSystemCore(spaceId, saveDataId, wipeSaveFile: false);
if (res.IsFailure()) return res.Miss();
// Make sure the save data isn't in certain save data spaces
using var accessor = new UniqueRef<SaveDataIndexerAccessor>();
res = OpenSaveDataIndexerAccessor(ref accessor.Ref, spaceId);
if (res.IsFailure()) return res.Miss();
res = accessor.Get.GetInterface().GetValue(out SaveDataIndexerValue value, saveDataId);
if (res.IsFailure()) return res.Miss();
if (value.SpaceId != ConvertToRealSpaceId(spaceId))
return ResultFs.TargetNotFound.Log();
// Delete the save data from the indexer
res = accessor.Get.GetInterface().Delete(saveDataId);
if (res.IsFailure()) return res.Miss();
res = accessor.Get.GetInterface().Commit();
if (res.IsFailure()) return res.Miss();
return Result.Success;
}
public Result SetSaveDataRootPath(ref readonly FspPath path)
@ -926,6 +993,30 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
throw new NotImplementedException();
}
private Result CreateSaveDataFileSystemCore(in SaveDataAttribute attribute, in SaveDataCreationInfo creationInfo,
in SaveDataMetaInfo metaInfo, in Optional<HashSalt> hashSalt, bool leaveUnfinalized)
{
// Changed: The original allocates a SaveDataCreationInfo2 on the heap for some reason
Result res = SaveDataCreationInfo2.Make(out SaveDataCreationInfo2 newCreationInfo, in attribute,
creationInfo.Size, creationInfo.JournalSize, creationInfo.BlockSize, creationInfo.OwnerId,
creationInfo.Flags, creationInfo.SpaceId, GetSaveDataFormatType(in attribute));
if (res.IsFailure()) return res.Miss();
newCreationInfo.IsHashSaltEnabled = hashSalt.HasValue;
if (hashSalt.HasValue)
{
newCreationInfo.HashSalt = hashSalt.ValueRo;
}
newCreationInfo.MetaType = metaInfo.Type;
newCreationInfo.MetaSize = metaInfo.Size;
res = CreateSaveDataFileSystemCore(in newCreationInfo, leaveUnfinalized);
if (res.IsFailure()) return res.Miss();
return Result.Success;
}
private Result CreateSaveDataFileSystemCore(in SaveDataCreationInfo2 creationInfo, bool leaveUnfinalized)
{
ulong saveDataId = 0;
@ -1144,30 +1235,6 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
}
}
private Result CreateSaveDataFileSystemCore(in SaveDataAttribute attribute, in SaveDataCreationInfo creationInfo,
in SaveDataMetaInfo metaInfo, in Optional<HashSalt> hashSalt, bool leaveUnfinalized)
{
// Changed: The original allocates a SaveDataCreationInfo2 on the heap for some reason
Result res = SaveDataCreationInfo2.Make(out SaveDataCreationInfo2 newCreationInfo, in attribute,
creationInfo.Size, creationInfo.JournalSize, creationInfo.BlockSize, creationInfo.OwnerId,
creationInfo.Flags, creationInfo.SpaceId, GetSaveDataFormatType(in attribute));
if (res.IsFailure()) return res.Miss();
newCreationInfo.IsHashSaltEnabled = hashSalt.HasValue;
if (hashSalt.HasValue)
{
newCreationInfo.HashSalt = hashSalt.ValueRo;
}
newCreationInfo.MetaType = metaInfo.Type;
newCreationInfo.MetaSize = metaInfo.Size;
res = CreateSaveDataFileSystemCore(in newCreationInfo, leaveUnfinalized);
if (res.IsFailure()) return res.Miss();
return Result.Success;
}
public Result GetSaveDataInfo(out SaveDataInfo info, SaveDataSpaceId spaceId, SaveDataAttribute attribute)
{
UnsafeHelpers.SkipParamInit(out info);
@ -1439,18 +1506,6 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
return Result.Success;
}
public Result OpenSaveDataFileSystem(ref SharedRef<IFileSystemSf> fileSystem, SaveDataSpaceId spaceId,
in SaveDataAttribute attribute)
{
return OpenUserSaveDataFileSystem(ref fileSystem, spaceId, in attribute, openReadOnly: false).Ret();
}
public Result OpenReadOnlySaveDataFileSystem(ref SharedRef<IFileSystemSf> fileSystem, SaveDataSpaceId spaceId,
in SaveDataAttribute attribute)
{
return OpenUserSaveDataFileSystem(ref fileSystem, spaceId, in attribute, openReadOnly: true).Ret();
}
public Result OpenSaveDataFileSystemCore(ref SharedRef<IFileSystem> outFileSystem, out ulong outSaveDataId,
SaveDataSpaceId spaceId, in SaveDataAttribute attribute, bool openReadOnly, bool cacheExtraData)
{
@ -1651,6 +1706,18 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
openReadOnly).Ret();
}
public Result OpenSaveDataFileSystem(ref SharedRef<IFileSystemSf> fileSystem, SaveDataSpaceId spaceId,
in SaveDataAttribute attribute)
{
return OpenUserSaveDataFileSystem(ref fileSystem, spaceId, in attribute, openReadOnly: false).Ret();
}
public Result OpenReadOnlySaveDataFileSystem(ref SharedRef<IFileSystemSf> fileSystem, SaveDataSpaceId spaceId,
in SaveDataAttribute attribute)
{
return OpenUserSaveDataFileSystem(ref fileSystem, spaceId, in attribute, openReadOnly: true).Ret();
}
public Result OpenSaveDataFileSystemBySystemSaveDataId(ref SharedRef<IFileSystemSf> outFileSystem,
SaveDataSpaceId spaceId, in SaveDataAttribute attribute)
{
@ -1830,8 +1897,8 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
Unsafe.SkipInit(out SaveDataExtraData extraDataMask);
SpanHelpers.AsByteSpan(ref extraDataMask).Fill(0xFF);
return ReadSaveDataFileSystemExtraDataCore(out SpanHelpers.AsStruct<SaveDataExtraData>(extraData.Buffer),
spaceId: default, saveDataId, in extraDataMask).Ret();
return ReadSaveDataFileSystemExtraDataCore(out extraData.As<SaveDataExtraData>(), spaceId: default, saveDataId,
in extraDataMask).Ret();
}
public Result ReadSaveDataFileSystemExtraDataBySaveDataAttribute(OutBuffer extraData, SaveDataSpaceId spaceId,
@ -1840,7 +1907,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
if (extraData.Size != Unsafe.SizeOf<SaveDataExtraData>())
return ResultFs.InvalidArgument.Log();
ref SaveDataExtraData extraDataRef = ref SpanHelpers.AsStruct<SaveDataExtraData>(extraData.Buffer);
ref SaveDataExtraData extraDataRef = ref extraData.As<SaveDataExtraData>();
Result res = GetProgramInfo(out ProgramInfo programInfo);
if (res.IsFailure()) return res.Miss();
@ -1868,7 +1935,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
if (extraData.Size != Unsafe.SizeOf<SaveDataExtraData>())
return ResultFs.InvalidArgument.Log();
ref SaveDataExtraData extraDataRef = ref SpanHelpers.AsStruct<SaveDataExtraData>(extraData.Buffer);
ref SaveDataExtraData extraDataRef = ref extraData.As<SaveDataExtraData>();
// Make a mask for reading the entire extra data
Unsafe.SkipInit(out SaveDataExtraData extraDataMask);
@ -1886,10 +1953,8 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
if (extraData.Size != Unsafe.SizeOf<SaveDataExtraData>())
return ResultFs.InvalidArgument.Log();
ref readonly SaveDataExtraData maskRef =
ref SpanHelpers.AsReadOnlyStruct<SaveDataExtraData>(extraDataMask.Buffer);
ref SaveDataExtraData extraDataRef = ref SpanHelpers.AsStruct<SaveDataExtraData>(extraData.Buffer);
ref readonly SaveDataExtraData maskRef = ref extraDataMask.As<SaveDataExtraData>();
ref SaveDataExtraData extraDataRef = ref extraData.As<SaveDataExtraData>();
Result res = GetProgramInfo(out ProgramInfo programInfo);
if (res.IsFailure()) return res.Miss();
@ -1959,8 +2024,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
if (extraData.Size != Unsafe.SizeOf<SaveDataExtraData>())
return ResultFs.InvalidArgument.Log();
ref readonly SaveDataExtraData extraDataRef =
ref SpanHelpers.AsReadOnlyStruct<SaveDataExtraData>(extraData.Buffer);
ref readonly SaveDataExtraData extraDataRef = ref extraData.As<SaveDataExtraData>();
SaveDataExtraData extraDataMask = default;
extraDataMask.Flags = unchecked((SaveDataFlags)0xFFFFFFFF);
@ -1996,11 +2060,8 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
if (extraDataMask.Size != Unsafe.SizeOf<SaveDataExtraData>())
return ResultFs.InvalidArgument.Log();
ref readonly SaveDataExtraData extraDataRef =
ref SpanHelpers.AsReadOnlyStruct<SaveDataExtraData>(extraData.Buffer);
ref readonly SaveDataExtraData maskRef =
ref SpanHelpers.AsReadOnlyStruct<SaveDataExtraData>(extraDataMask.Buffer);
ref readonly SaveDataExtraData extraDataRef = ref extraData.As<SaveDataExtraData>();
ref readonly SaveDataExtraData maskRef = ref extraDataMask.As<SaveDataExtraData>();
return WriteSaveDataFileSystemExtraDataWithMaskCore(saveDataId, spaceId, in extraDataRef, in maskRef).Ret();
}
@ -2157,8 +2218,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
var infoFilter = new SaveDataInfoFilter(ConvertToRealSpaceId(spaceId), in tempFilter);
return FindSaveDataWithFilterImpl(out count, out SpanHelpers.AsStruct<SaveDataInfo>(saveDataInfoBuffer.Buffer),
spaceId, in infoFilter).Ret();
return FindSaveDataWithFilterImpl(out count, out saveDataInfoBuffer.As<SaveDataInfo>(), spaceId, in infoFilter).Ret();
}
private Result CreateEmptyThumbnailFile(SaveDataSpaceId spaceId, ulong saveDataId)
@ -2178,13 +2238,13 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
return Result.Success;
}
private Result OpenSaveDataInternalStorageFileSystemCore(ref SharedRef<IFileSystem> fileSystem,
SaveDataSpaceId spaceId, ulong saveDataId, bool useSecondMacKey)
private Result OpenSaveDataInternalStorageFileSystemCore(ref SharedRef<IFileSystem> outFileSystem,
SaveDataSpaceId spaceId, ulong saveDataId, bool isTemporaryTransferSave)
{
throw new NotImplementedException();
}
public Result OpenSaveDataInternalStorageFileSystem(ref SharedRef<IFileSystemSf> fileSystem,
public Result OpenSaveDataInternalStorageFileSystem(ref SharedRef<IFileSystemSf> outFileSystem,
SaveDataSpaceId spaceId, ulong saveDataId)
{
throw new NotImplementedException();
@ -2391,34 +2451,34 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
return Result.Success;
}
public Result OpenSaveDataTransferManager(ref SharedRef<ISaveDataTransferManager> manager)
public Result OpenSaveDataTransferManager(ref SharedRef<ISaveDataTransferManager> outManager)
{
throw new NotImplementedException();
}
public Result OpenSaveDataTransferManagerVersion2(ref SharedRef<ISaveDataTransferManagerWithDivision> manager)
public Result OpenSaveDataTransferManagerVersion2(ref SharedRef<ISaveDataTransferManagerWithDivision> outManager)
{
throw new NotImplementedException();
}
public Result OpenSaveDataTransferManagerForSaveDataRepair(
ref SharedRef<ISaveDataTransferManagerForSaveDataRepair> manager)
ref SharedRef<ISaveDataTransferManagerForSaveDataRepair> outManager)
{
throw new NotImplementedException();
}
public Result OpenSaveDataTransferManagerForRepair(ref SharedRef<ISaveDataTransferManagerForRepair> manager)
public Result OpenSaveDataTransferManagerForRepair(ref SharedRef<ISaveDataTransferManagerForRepair> outManager)
{
throw new NotImplementedException();
}
private Result OpenSaveDataTransferProhibiterCore(ref SharedRef<ISaveDataTransferProhibiter> prohibiter,
private Result OpenSaveDataTransferProhibiterCore(ref SharedRef<ISaveDataTransferProhibiter> outProhibiter,
Ncm.ApplicationId applicationId)
{
throw new NotImplementedException();
}
public Result OpenSaveDataTransferProhibiter(ref SharedRef<ISaveDataTransferProhibiter> prohibiter,
public Result OpenSaveDataTransferProhibiter(ref SharedRef<ISaveDataTransferProhibiter> outProhibiter,
Ncm.ApplicationId applicationId)
{
Result res = GetProgramInfo(out ProgramInfo programInfo);
@ -2429,7 +2489,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
res = SaveDataAccessibilityChecker.CheckOpenProhibiter(applicationId, programInfo);
if (res.IsFailure()) return res.Miss();
return OpenSaveDataTransferProhibiterCore(ref prohibiter, applicationId).Ret();
return OpenSaveDataTransferProhibiterCore(ref outProhibiter, applicationId).Ret();
}
public bool IsProhibited(ref UniqueLock<SdkMutex> outLock, ApplicationId applicationId)
@ -2437,7 +2497,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
throw new NotImplementedException();
}
public Result OpenSaveDataMover(ref SharedRef<Sf.ISaveDataMover> saveMover, SaveDataSpaceId sourceSpaceId,
public Result OpenSaveDataMover(ref SharedRef<Sf.ISaveDataMover> outSaveMover, SaveDataSpaceId sourceSpaceId,
SaveDataSpaceId destinationSpaceId, NativeHandle workBufferHandle, ulong workBufferSize)
{
throw new NotImplementedException();
@ -2460,7 +2520,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
if (bufferIdCount > 0)
{
ids = MemoryMarshal.Cast<byte, Ncm.ApplicationId>(idBuffer.Buffer);
ids = idBuffer.AsSpan<Ncm.ApplicationId>();
if (ids.Length < bufferIdCount)
return ResultFs.InvalidSize.Log();
@ -2582,7 +2642,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
using var scopedContext = new ScopedStorageLayoutTypeSetter(StorageLayoutType.Bis);
using var fileSystem = new SharedRef<IFileSystem>();
Result res = _serviceImpl.OpenSaveDataDirectoryFileSystem(ref fileSystem.Ref, SaveDataSpaceId.Temporary);
Result res = _serviceImpl.OpenSaveDataDirectoryFileSystem(ref fileSystem.Ref, SaveDataSpaceId.Temporary, UnspecifiedSaveDataId);
if (res.IsFailure()) return res.Miss();
using var pathRoot = new Path();
@ -2611,7 +2671,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
return Result.Success;
}
public Result OpenMultiCommitContext(ref SharedRef<IFileSystem> contextFileSystem)
public Result OpenMultiCommitContext(ref SharedRef<IFileSystem> outContextFileSystem)
{
Result res = SaveDataAttribute.Make(out SaveDataAttribute attribute, new ProgramId(MultiCommitManager.ProgramId),
SaveDataType.System, InvalidUserId, MultiCommitManager.SaveDataId, index: 0);
@ -2623,7 +2683,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
openReadOnly: false, cacheExtraData: true);
if (res.IsFailure()) return res.Miss();
contextFileSystem.SetByMove(ref fileSystem.Ref);
outContextFileSystem.SetByMove(ref fileSystem.Ref);
return Result.Success;
}
@ -2663,7 +2723,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
return Result.Success;
}
private Result TryAcquireSaveDataEntryOpenCountSemaphore(ref UniqueRef<IUniqueLock> outSemaphoreLock)
public Result TryAcquireSaveDataEntryOpenCountSemaphore(ref UniqueRef<IUniqueLock> outSemaphoreLock)
{
using SharedRef<SaveDataFileSystemService> saveService = GetSharedFromThis();
@ -2746,9 +2806,9 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
}
Result ISaveDataTransferCoreInterface.OpenSaveDataInternalStorageFileSystemCore(
ref SharedRef<IFileSystem> fileSystem, SaveDataSpaceId spaceId, ulong saveDataId, bool useSecondMacKey)
ref SharedRef<IFileSystem> outFileSystem, SaveDataSpaceId spaceId, ulong saveDataId, bool isTemporaryTransferSave)
{
return OpenSaveDataInternalStorageFileSystemCore(ref fileSystem, spaceId, saveDataId, useSecondMacKey);
return OpenSaveDataInternalStorageFileSystemCore(ref outFileSystem, spaceId, saveDataId, isTemporaryTransferSave);
}
Result ISaveDataTransferCoreInterface.OpenSaveDataMetaFileRaw(ref SharedRef<IFile> outFile, SaveDataSpaceId spaceId,

View file

@ -121,7 +121,7 @@ public class SaveDataFileSystemServiceImpl : IDisposable
using var fileSystem = new SharedRef<IFileSystem>();
Result res = OpenSaveDataDirectoryFileSystem(ref fileSystem.Ref, spaceId);
Result res = OpenSaveDataDirectoryFileSystem(ref fileSystem.Ref, spaceId, saveDataId);
if (res.IsFailure()) return res.Miss();
// Get the path of the save data
@ -160,7 +160,7 @@ public class SaveDataFileSystemServiceImpl : IDisposable
{
using var fileSystem = new SharedRef<IFileSystem>();
Result res = OpenSaveDataDirectoryFileSystem(ref fileSystem.Ref, spaceId, in saveDataRootPath, true);
Result res = OpenSaveDataDirectoryFileSystem(ref fileSystem.Ref, spaceId, saveDataId, in saveDataRootPath, true);
if (res.IsFailure()) return res.Miss();
bool isEmulatedOnHost = IsAllowedDirectorySaveData(spaceId, in saveDataRootPath);
@ -413,8 +413,8 @@ public class SaveDataFileSystemServiceImpl : IDisposable
using var fileSystem = new SharedRef<IFileSystem>();
Result res = OpenSaveDataDirectoryFileSystem(ref fileSystem.Ref, creationInfo.SpaceId, in saveDataRootPath,
allowEmulatedSave: false);
Result res = OpenSaveDataDirectoryFileSystem(ref fileSystem.Ref, creationInfo.SpaceId, saveDataId,
in saveDataRootPath, allowEmulatedSave: false);
if (res.IsFailure()) return res.Miss();
using scoped var saveImageName = new Path();
@ -486,7 +486,7 @@ public class SaveDataFileSystemServiceImpl : IDisposable
_saveFileSystemCacheManager.Unregister(spaceId, saveDataId);
// Open the directory containing the save data
Result res = OpenSaveDataDirectoryFileSystem(ref fileSystem.Ref, spaceId, in saveDataRootPath, false);
Result res = OpenSaveDataDirectoryFileSystem(ref fileSystem.Ref, spaceId, saveDataId, in saveDataRootPath, false);
if (res.IsFailure()) return res.Miss();
using scoped var saveImageName = new Path();
@ -642,15 +642,15 @@ public class SaveDataFileSystemServiceImpl : IDisposable
}
public Result OpenSaveDataDirectoryFileSystem(ref SharedRef<IFileSystem> outFileSystem,
SaveDataSpaceId spaceId)
SaveDataSpaceId spaceId, ulong saveDataId)
{
using scoped var rootPath = new Path();
return OpenSaveDataDirectoryFileSystem(ref outFileSystem, spaceId, in rootPath, allowEmulatedSave: true);
return OpenSaveDataDirectoryFileSystem(ref outFileSystem, spaceId, saveDataId, in rootPath, allowEmulatedSave: true);
}
public Result OpenSaveDataDirectoryFileSystem(ref SharedRef<IFileSystem> outFileSystem,
SaveDataSpaceId spaceId, ref readonly Path saveDataRootPath, bool allowEmulatedSave)
SaveDataSpaceId spaceId, ulong saveDataId, ref readonly Path saveDataRootPath, bool allowEmulatedSave)
{
Result res;

View file

@ -156,7 +156,7 @@ public class SaveDataIndexer : ISaveDataIndexer
if (_handle != _indexer.GetHandle())
return ResultFs.InvalidHandle.Log();
Span<SaveDataInfo> outInfos = MemoryMarshal.Cast<byte, SaveDataInfo>(saveDataInfoBuffer.Buffer);
Span<SaveDataInfo> outInfos = saveDataInfoBuffer.AsSpan<SaveDataInfo>();
int count;
for (count = 0; !_iterator.IsEnd() && count < outInfos.Length; count++)

View file

@ -53,7 +53,7 @@ internal class SaveDataIndexerLiteInfoReader : SaveDataInfoReaderImpl
if (saveDataInfoBuffer.Size < Unsafe.SizeOf<SaveDataInfo>())
return ResultFs.InvalidSize.Log();
Unsafe.As<byte, SaveDataInfo>(ref MemoryMarshal.GetReference(saveDataInfoBuffer.Buffer)) = _info;
saveDataInfoBuffer.As<SaveDataInfo>() = _info;
readCount = 1;
_finishedIterating = true;

View file

@ -132,7 +132,7 @@ internal class SaveDataInfoFilterReader : SaveDataInfoReaderImpl
{
UnsafeHelpers.SkipParamInit(out readCount);
Span<SaveDataInfo> outInfo = MemoryMarshal.Cast<byte, SaveDataInfo>(saveDataInfoBuffer.Buffer);
Span<SaveDataInfo> outInfo = saveDataInfoBuffer.AsSpan<SaveDataInfo>();
int count = 0;
while (count < outInfo.Length)

View file

@ -22,6 +22,11 @@ public readonly ref struct InBuffer
return ref SpanHelpers.AsReadOnlyStruct<T>(_buffer);
}
public ReadOnlySpan<T> AsSpan<T>() where T : unmanaged
{
return MemoryMarshal.Cast<byte, T>(_buffer);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static InBuffer FromSpan<T>(ReadOnlySpan<T> buffer) where T : unmanaged
{
@ -52,6 +57,11 @@ public readonly ref struct OutBuffer
return ref SpanHelpers.AsStruct<T>(_buffer);
}
public Span<T> AsSpan<T>() where T : unmanaged
{
return MemoryMarshal.Cast<byte, T>(_buffer);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static OutBuffer FromSpan<T>(Span<T> buffer) where T : unmanaged
{