mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Implement extra data functions in SaveDataFileSystemService
This commit is contained in:
parent
e99d05cc84
commit
3056c5c296
6 changed files with 511 additions and 20 deletions
|
@ -1,5 +1,4 @@
|
|||
using System;
|
||||
using LibHac.Common.Keys;
|
||||
using LibHac.Fs.Impl;
|
||||
using LibHac.Fs.Shim;
|
||||
using LibHac.FsSrv.FsCreator;
|
||||
|
|
|
@ -162,6 +162,17 @@ namespace LibHac.FsSrv.Impl
|
|||
return CreateSubDirectoryFileSystem(out fileSystem, ref baseFileSystem, path);
|
||||
}
|
||||
|
||||
public static long ConvertZeroCommitId(in SaveDataExtraData extraData)
|
||||
{
|
||||
if (extraData.CommitId != 0)
|
||||
return extraData.CommitId;
|
||||
|
||||
Span<byte> hash = stackalloc byte[Crypto.Sha256.DigestSize];
|
||||
|
||||
Crypto.Sha256.GenerateSha256Hash(SpanHelpers.AsReadOnlyByteSpan(in extraData), hash);
|
||||
return BitConverter.ToInt64(hash);
|
||||
}
|
||||
|
||||
private static ReadOnlySpan<byte> FileSystemRootPath => // /
|
||||
new[]
|
||||
{
|
||||
|
|
|
@ -301,6 +301,75 @@ namespace LibHac.FsSrv
|
|||
return Result.Success;
|
||||
}
|
||||
|
||||
public static Result CheckWriteExtraData(in SaveDataAttribute attribute, in SaveDataExtraData mask,
|
||||
ProgramInfo programInfo, ExtraDataGetter extraDataGetter)
|
||||
{
|
||||
AccessControl accessControl = programInfo.AccessControl;
|
||||
|
||||
if (mask.Flags != SaveDataFlags.None)
|
||||
{
|
||||
bool canAccess = accessControl.CanCall(OperationType.WriteSaveDataFileSystemExtraDataAll) ||
|
||||
accessControl.CanCall(OperationType.WriteSaveDataFileSystemExtraDataFlags);
|
||||
|
||||
if (SaveDataProperties.IsSystemSaveData(attribute.Type))
|
||||
{
|
||||
Result rc = GetAccessibilityForSaveData(out Accessibility accessibility, programInfo,
|
||||
extraDataGetter);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
canAccess |= accessibility.CanWrite;
|
||||
}
|
||||
|
||||
if ((mask.Flags & ~SaveDataFlags.Restore) == 0)
|
||||
{
|
||||
Result rc = GetAccessibilityForSaveData(out Accessibility accessibility, programInfo,
|
||||
extraDataGetter);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
canAccess |= accessibility.CanWrite;
|
||||
}
|
||||
|
||||
if (!canAccess)
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
}
|
||||
|
||||
if (mask.TimeStamp != 0)
|
||||
{
|
||||
bool canAccess = accessControl.CanCall(OperationType.WriteSaveDataFileSystemExtraDataAll) ||
|
||||
accessControl.CanCall(OperationType.WriteSaveDataFileSystemExtraDataTimeStamp);
|
||||
|
||||
if (!canAccess)
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
}
|
||||
|
||||
if (mask.CommitId != 0)
|
||||
{
|
||||
bool canAccess = accessControl.CanCall(OperationType.WriteSaveDataFileSystemExtraDataAll) ||
|
||||
accessControl.CanCall(OperationType.WriteSaveDataFileSystemExtraDataCommitId);
|
||||
|
||||
if (!canAccess)
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
}
|
||||
|
||||
SaveDataExtraData emptyMask = default;
|
||||
SaveDataExtraData maskWithoutFlags = mask;
|
||||
maskWithoutFlags.Flags = SaveDataFlags.None;
|
||||
maskWithoutFlags.TimeStamp = 0;
|
||||
maskWithoutFlags.CommitId = 0;
|
||||
|
||||
// Full write access is needed for writing anything other than flags, timestamp or commit ID
|
||||
if (SpanHelpers.AsReadOnlyByteSpan(in emptyMask)
|
||||
.SequenceEqual(SpanHelpers.AsReadOnlyByteSpan(in maskWithoutFlags)))
|
||||
{
|
||||
bool canAccess = accessControl.CanCall(OperationType.WriteSaveDataFileSystemExtraDataAll);
|
||||
|
||||
if (!canAccess)
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public static Result CheckFind(in SaveDataFilter filter, ProgramInfo programInfo)
|
||||
{
|
||||
bool canAccess;
|
||||
|
@ -1199,68 +1268,297 @@ namespace LibHac.FsSrv
|
|||
}
|
||||
}
|
||||
|
||||
// ReSharper disable once UnusedParameter.Local
|
||||
// Nintendo used this parameter in older FS versions, but never removed it.
|
||||
private Result ReadSaveDataFileSystemExtraDataCore(out SaveDataExtraData extraData, SaveDataSpaceId spaceId,
|
||||
ulong saveDataId, bool isTemporarySaveData)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
UnsafeHelpers.SkipParamInit(out extraData);
|
||||
|
||||
using var scopedLayoutType = new ScopedStorageLayoutTypeSetter(StorageType.NonGameCard);
|
||||
|
||||
SaveDataIndexerAccessor accessor = null;
|
||||
try
|
||||
{
|
||||
Result rc = OpenSaveDataIndexerAccessor(out accessor, spaceId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = accessor.Indexer.GetKey(out SaveDataAttribute key, saveDataId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return ServiceImpl.ReadSaveDataFileSystemExtraData(out extraData, spaceId, saveDataId, key.Type,
|
||||
SaveDataRootPath);
|
||||
}
|
||||
finally
|
||||
{
|
||||
accessor?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private Result ReadSaveDataFileSystemExtraDataCore(out SaveDataExtraData extraData, SaveDataSpaceId spaceId,
|
||||
ulong saveDataId, in SaveDataExtraData extraDataMask)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
UnsafeHelpers.SkipParamInit(out extraData);
|
||||
|
||||
using var scopedLayoutType = new ScopedStorageLayoutTypeSetter(StorageType.NonGameCard);
|
||||
|
||||
Result rc = GetProgramInfo(out ProgramInfo programInfo);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
SaveDataSpaceId resolvedSpaceId;
|
||||
SaveDataAttribute key;
|
||||
|
||||
if (spaceId == SaveDataSpaceId.BisAuto)
|
||||
{
|
||||
SaveDataIndexerAccessor accessor = null;
|
||||
try
|
||||
{
|
||||
if (IsStaticSaveDataIdValueRange(saveDataId))
|
||||
{
|
||||
rc = OpenSaveDataIndexerAccessor(out accessor, SaveDataSpaceId.System);
|
||||
if (rc.IsFailure()) return rc;
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = OpenSaveDataIndexerAccessor(out accessor, SaveDataSpaceId.User);
|
||||
if (rc.IsFailure()) return rc;
|
||||
}
|
||||
|
||||
rc = accessor.Indexer.GetValue(out SaveDataIndexerValue value, saveDataId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
resolvedSpaceId = value.SpaceId;
|
||||
|
||||
rc = accessor.Indexer.GetKey(out key, saveDataId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
}
|
||||
finally
|
||||
{
|
||||
accessor?.Dispose();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SaveDataIndexerAccessor accessor = null;
|
||||
try
|
||||
{
|
||||
rc = OpenSaveDataIndexerAccessor(out accessor, spaceId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = accessor.Indexer.GetValue(out SaveDataIndexerValue value, saveDataId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
resolvedSpaceId = value.SpaceId;
|
||||
|
||||
rc = accessor.Indexer.GetKey(out key, saveDataId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
}
|
||||
finally
|
||||
{
|
||||
accessor?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
Result ReadExtraData(out SaveDataExtraData data) => ServiceImpl.ReadSaveDataFileSystemExtraData(out data,
|
||||
resolvedSpaceId, saveDataId, key.Type, new U8Span(SaveDataRootPath.Str));
|
||||
|
||||
rc = SaveDataAccessibilityChecker.CheckReadExtraData(in key, in extraDataMask, programInfo,
|
||||
ReadExtraData);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = ServiceImpl.ReadSaveDataFileSystemExtraData(out SaveDataExtraData tempExtraData, resolvedSpaceId,
|
||||
saveDataId, key.Type, new U8Span(SaveDataRootPath.Str));
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
MaskExtraData(ref tempExtraData, in extraDataMask);
|
||||
extraData = tempExtraData;
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result ReadSaveDataFileSystemExtraData(OutBuffer extraData, ulong saveDataId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
if (extraData.Size != Unsafe.SizeOf<SaveDataExtraData>())
|
||||
return ResultFs.InvalidArgument.Log();
|
||||
|
||||
// Make a mask for reading the entire extra data
|
||||
Unsafe.SkipInit(out SaveDataExtraData extraDataMask);
|
||||
SpanHelpers.AsByteSpan(ref extraDataMask).Fill(0xFF);
|
||||
|
||||
return ReadSaveDataFileSystemExtraDataCore(out SpanHelpers.AsStruct<SaveDataExtraData>(extraData.Buffer),
|
||||
SaveDataSpaceId.BisAuto, saveDataId, in extraDataMask);
|
||||
}
|
||||
|
||||
public Result ReadSaveDataFileSystemExtraDataBySaveDataAttribute(OutBuffer extraData,
|
||||
SaveDataSpaceId spaceId, in SaveDataAttribute attribute)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
if (extraData.Size != Unsafe.SizeOf<SaveDataExtraData>())
|
||||
return ResultFs.InvalidArgument.Log();
|
||||
|
||||
ref SaveDataExtraData extraDataRef = ref SpanHelpers.AsStruct<SaveDataExtraData>(extraData.Buffer);
|
||||
|
||||
Result rc = GetProgramInfo(out ProgramInfo programInfo);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
SaveDataAttribute tempAttribute = attribute;
|
||||
|
||||
if (tempAttribute.ProgramId == SaveData.AutoResolveCallerProgramId)
|
||||
{
|
||||
tempAttribute.ProgramId = ResolveDefaultSaveDataReferenceProgramId(programInfo.ProgramId);
|
||||
}
|
||||
|
||||
rc = GetSaveDataInfo(out SaveDataInfo info, spaceId, in tempAttribute);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
// Make a mask for reading the entire extra data
|
||||
Unsafe.SkipInit(out SaveDataExtraData extraDataMask);
|
||||
SpanHelpers.AsByteSpan(ref extraDataMask).Fill(0xFF);
|
||||
|
||||
return ReadSaveDataFileSystemExtraDataCore(out extraDataRef, spaceId, info.SaveDataId, in extraDataMask);
|
||||
}
|
||||
|
||||
public Result ReadSaveDataFileSystemExtraDataBySaveDataSpaceId(OutBuffer extraData,
|
||||
SaveDataSpaceId spaceId, ulong saveDataId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
if (extraData.Size != Unsafe.SizeOf<SaveDataExtraData>())
|
||||
return ResultFs.InvalidArgument.Log();
|
||||
|
||||
ref SaveDataExtraData extraDataRef = ref SpanHelpers.AsStruct<SaveDataExtraData>(extraData.Buffer);
|
||||
|
||||
// Make a mask for reading the entire extra data
|
||||
Unsafe.SkipInit(out SaveDataExtraData extraDataMask);
|
||||
SpanHelpers.AsByteSpan(ref extraDataMask).Fill(0xFF);
|
||||
|
||||
return ReadSaveDataFileSystemExtraDataCore(out extraDataRef, spaceId, saveDataId, in extraDataMask);
|
||||
}
|
||||
|
||||
public Result ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute(OutBuffer extraData,
|
||||
SaveDataSpaceId spaceId, in SaveDataAttribute attribute, InBuffer extraDataMask)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
if (extraDataMask.Size != Unsafe.SizeOf<SaveDataExtraData>())
|
||||
return ResultFs.InvalidArgument.Log();
|
||||
|
||||
public Result WriteSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute(in SaveDataAttribute attribute,
|
||||
SaveDataSpaceId spaceId, InBuffer extraData, InBuffer extraDataMask)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
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);
|
||||
|
||||
Result rc = GetProgramInfo(out ProgramInfo programInfo);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
SaveDataAttribute tempAttribute = attribute;
|
||||
|
||||
if (tempAttribute.ProgramId == SaveData.AutoResolveCallerProgramId)
|
||||
{
|
||||
tempAttribute.ProgramId = ResolveDefaultSaveDataReferenceProgramId(programInfo.ProgramId);
|
||||
}
|
||||
|
||||
rc = GetSaveDataInfo(out SaveDataInfo info, spaceId, in tempAttribute);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return ReadSaveDataFileSystemExtraDataCore(out extraDataRef, spaceId, info.SaveDataId, in maskRef);
|
||||
}
|
||||
|
||||
private Result WriteSaveDataFileSystemExtraDataCore(SaveDataSpaceId spaceId, ulong saveDataId,
|
||||
in SaveDataExtraData extraData, SaveDataType saveType, bool updateTimeStamp)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
using var scopedLayoutType = new ScopedStorageLayoutTypeSetter(StorageType.NonGameCard);
|
||||
|
||||
return ServiceImpl.WriteSaveDataFileSystemExtraData(spaceId, saveDataId, in extraData, SaveDataRootPath,
|
||||
saveType, updateTimeStamp);
|
||||
}
|
||||
|
||||
private Result WriteSaveDataFileSystemExtraDataWithMaskCore(ulong saveDataId, SaveDataSpaceId spaceId,
|
||||
in SaveDataExtraData extraData, in SaveDataExtraData extraDataMask)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
using var scopedLayoutType = new ScopedStorageLayoutTypeSetter(StorageType.NonGameCard);
|
||||
|
||||
Result rc = GetProgramInfo(out ProgramInfo programInfo);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
SaveDataIndexerAccessor accessor = null;
|
||||
try
|
||||
{
|
||||
rc = OpenSaveDataIndexerAccessor(out accessor, spaceId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = accessor.Indexer.GetKey(out SaveDataAttribute key, saveDataId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
Result ReadExtraData(out SaveDataExtraData data) => ServiceImpl.ReadSaveDataFileSystemExtraData(out data,
|
||||
spaceId, saveDataId, key.Type, new U8Span(SaveDataRootPath.Str));
|
||||
|
||||
rc = SaveDataAccessibilityChecker.CheckWriteExtraData(in key, in extraDataMask, programInfo,
|
||||
ReadExtraData);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = ServiceImpl.ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraDataModify, spaceId,
|
||||
saveDataId, key.Type, SaveDataRootPath);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
ModifySaveDataExtraData(ref extraDataModify, in extraData, in extraDataMask);
|
||||
|
||||
return ServiceImpl.WriteSaveDataFileSystemExtraData(spaceId, saveDataId, in extraDataModify,
|
||||
SaveDataRootPath, key.Type, false);
|
||||
}
|
||||
finally
|
||||
{
|
||||
accessor?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public Result WriteSaveDataFileSystemExtraData(ulong saveDataId, SaveDataSpaceId spaceId, InBuffer extraData)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
if (extraData.Size != Unsafe.SizeOf<SaveDataExtraData>())
|
||||
return ResultFs.InvalidArgument.Log();
|
||||
|
||||
ref readonly SaveDataExtraData extraDataRef =
|
||||
ref SpanHelpers.AsReadOnlyStruct<SaveDataExtraData>(extraData.Buffer);
|
||||
|
||||
var extraDataMask = new SaveDataExtraData();
|
||||
extraDataMask.Flags = unchecked((SaveDataFlags)0xFFFFFFFF);
|
||||
|
||||
return WriteSaveDataFileSystemExtraDataWithMaskCore(saveDataId, spaceId, in extraDataRef, in extraDataMask);
|
||||
}
|
||||
|
||||
public Result WriteSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute(in SaveDataAttribute attribute,
|
||||
SaveDataSpaceId spaceId, InBuffer extraData, InBuffer extraDataMask)
|
||||
{
|
||||
Result rc = GetProgramInfo(out ProgramInfo programInfo);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
SaveDataAttribute tempAttribute = attribute;
|
||||
|
||||
if (tempAttribute.ProgramId == SaveData.AutoResolveCallerProgramId)
|
||||
{
|
||||
tempAttribute.ProgramId = ResolveDefaultSaveDataReferenceProgramId(programInfo.ProgramId);
|
||||
}
|
||||
|
||||
rc = GetSaveDataInfo(out SaveDataInfo info, spaceId, in tempAttribute);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return WriteSaveDataFileSystemExtraDataWithMask(info.SaveDataId, spaceId, extraData, extraDataMask);
|
||||
}
|
||||
|
||||
public Result WriteSaveDataFileSystemExtraDataWithMask(ulong saveDataId, SaveDataSpaceId spaceId,
|
||||
InBuffer extraData, InBuffer extraDataMask)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
if (extraDataMask.Size != Unsafe.SizeOf<SaveDataExtraData>())
|
||||
return ResultFs.InvalidArgument.Log();
|
||||
|
||||
if (extraData.Size != Unsafe.SizeOf<SaveDataExtraData>())
|
||||
return ResultFs.InvalidArgument.Log();
|
||||
|
||||
ref readonly SaveDataExtraData maskRef =
|
||||
ref SpanHelpers.AsReadOnlyStruct<SaveDataExtraData>(extraDataMask.Buffer);
|
||||
|
||||
ref readonly SaveDataExtraData extraDataRef =
|
||||
ref SpanHelpers.AsReadOnlyStruct<SaveDataExtraData>(extraData.Buffer);
|
||||
|
||||
return WriteSaveDataFileSystemExtraDataWithMaskCore(saveDataId, spaceId, in extraDataRef, in maskRef);
|
||||
}
|
||||
|
||||
public Result OpenSaveDataInfoReader(out ReferenceCountedDisposable<ISaveDataInfoReader> infoReader)
|
||||
|
@ -1462,7 +1760,21 @@ namespace LibHac.FsSrv
|
|||
|
||||
public Result GetSaveDataCommitId(out long commitId, SaveDataSpaceId spaceId, ulong saveDataId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
UnsafeHelpers.SkipParamInit(out commitId);
|
||||
|
||||
Result rc = GetProgramInfo(out ProgramInfo programInfo);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (!programInfo.AccessControl.CanCall(OperationType.GetSaveDataCommitId))
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
|
||||
Unsafe.SkipInit(out SaveDataExtraData extraData);
|
||||
rc = ReadSaveDataFileSystemExtraDataBySaveDataSpaceId(OutBuffer.FromStruct(ref extraData), spaceId,
|
||||
saveDataId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
commitId = Impl.Utility.ConvertZeroCommitId(in extraData);
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result OpenSaveDataInfoReaderOnlyCacheStorage(
|
||||
|
@ -1700,9 +2012,9 @@ namespace LibHac.FsSrv
|
|||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private ProgramId ResolveDefaultSaveDataReferenceProgramId(in ProgramId programId)
|
||||
private ProgramId ResolveDefaultSaveDataReferenceProgramId(ProgramId programId)
|
||||
{
|
||||
return ServiceImpl.ResolveDefaultSaveDataReferenceProgramId(in programId);
|
||||
return ServiceImpl.ResolveDefaultSaveDataReferenceProgramId(programId);
|
||||
}
|
||||
|
||||
public Result VerifySaveDataFileSystemBySaveDataSpaceId(SaveDataSpaceId spaceId, ulong saveDataId,
|
||||
|
@ -1995,6 +2307,31 @@ namespace LibHac.FsSrv
|
|||
return (long)id < 0;
|
||||
}
|
||||
|
||||
private 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 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 StorageType DecidePossibleStorageFlag(SaveDataType type, SaveDataSpaceId spaceId)
|
||||
{
|
||||
if (type == SaveDataType.Cache || type == SaveDataType.Bcat)
|
||||
|
|
|
@ -774,7 +774,7 @@ namespace LibHac.FsSrv
|
|||
/// for their save data. The main program always has a program index of 0.</remarks>
|
||||
/// <param name="programId">The program ID to get the save data program ID for.</param>
|
||||
/// <returns>The program ID of the save data.</returns>
|
||||
public ProgramId ResolveDefaultSaveDataReferenceProgramId(in ProgramId programId)
|
||||
public ProgramId ResolveDefaultSaveDataReferenceProgramId(ProgramId programId)
|
||||
{
|
||||
// First check if there's an entry in the program index map with the program ID and program index 0
|
||||
ProgramId mainProgramId = _config.ProgramRegistryService.GetProgramIdByIndex(programId, 0);
|
||||
|
|
|
@ -22,6 +22,7 @@ namespace LibHac.Tests.Fs.FileSystemClientTests.ShimTests
|
|||
Assert.Success(rootFs.GetEntryType(out DirectoryEntryType type, "/bis/cal/file".ToU8Span()));
|
||||
Assert.Equal(DirectoryEntryType.File, type);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MountBis_MountSafePartition_OpensCorrectDirectory()
|
||||
{
|
||||
|
|
|
@ -3,6 +3,7 @@ using System.Linq;
|
|||
using LibHac.Common;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Fs.Shim;
|
||||
using LibHac.Time;
|
||||
using Xunit;
|
||||
|
||||
namespace LibHac.Tests.Fs.FileSystemClientTests.ShimTests
|
||||
|
@ -159,6 +160,90 @@ namespace LibHac.Tests.Fs.FileSystemClientTests.ShimTests
|
|||
Assert.Equal(userId, info[0].UserId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateSaveData_DoesNotExist_HasCorrectOwnerId()
|
||||
{
|
||||
uint ownerId = 1;
|
||||
|
||||
var applicationId = new Ncm.ApplicationId(ownerId);
|
||||
var userId = new UserId(5, 4);
|
||||
|
||||
FileSystemClient fs = FileSystemServerFactory.CreateClient(true);
|
||||
|
||||
// Create the save
|
||||
Assert.Success(fs.CreateSaveData(applicationId, userId, ownerId, 0x1000, 0x1000, SaveDataFlags.None));
|
||||
|
||||
// Get the created save data's ID
|
||||
Assert.Success(fs.OpenSaveDataIterator(out SaveDataIterator iterator, SaveDataSpaceId.User));
|
||||
|
||||
var info = new SaveDataInfo[2];
|
||||
iterator.ReadSaveDataInfo(out long entriesRead, info);
|
||||
|
||||
Assert.Equal(1, entriesRead);
|
||||
|
||||
// Get the created save data's owner ID
|
||||
Assert.Success(fs.GetSaveDataOwnerId(out ulong actualOwnerId, info[0].SaveDataId));
|
||||
|
||||
Assert.Equal(ownerId, actualOwnerId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateSaveData_DoesNotExist_HasCorrectFlags()
|
||||
{
|
||||
SaveDataFlags flags = SaveDataFlags.KeepAfterRefurbishment | SaveDataFlags.NeedsSecureDelete;
|
||||
|
||||
var applicationId = new Ncm.ApplicationId(1);
|
||||
var userId = new UserId(5, 4);
|
||||
|
||||
FileSystemClient fs = FileSystemServerFactory.CreateClient(true);
|
||||
|
||||
// Create the save
|
||||
Assert.Success(fs.CreateSaveData(applicationId, userId, 0, 0x1000, 0x1000, flags));
|
||||
|
||||
// Get the created save data's ID
|
||||
Assert.Success(fs.OpenSaveDataIterator(out SaveDataIterator iterator, SaveDataSpaceId.User));
|
||||
|
||||
var info = new SaveDataInfo[2];
|
||||
iterator.ReadSaveDataInfo(out long entriesRead, info);
|
||||
|
||||
Assert.Equal(1, entriesRead);
|
||||
|
||||
// Get the created save data's flags
|
||||
Assert.Success(fs.GetSaveDataFlags(out SaveDataFlags actualFlags, info[0].SaveDataId));
|
||||
|
||||
Assert.Equal(flags, actualFlags);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateSaveData_DoesNotExist_HasCorrectSizes()
|
||||
{
|
||||
long availableSize = 0x220000;
|
||||
long journalSize = 0x120000;
|
||||
|
||||
var applicationId = new Ncm.ApplicationId(1);
|
||||
var userId = new UserId(5, 4);
|
||||
|
||||
FileSystemClient fs = FileSystemServerFactory.CreateClient(true);
|
||||
|
||||
// Create the save
|
||||
Assert.Success(fs.CreateSaveData(applicationId, userId, 0, availableSize, journalSize, SaveDataFlags.None));
|
||||
|
||||
// Get the created save data's ID
|
||||
Assert.Success(fs.OpenSaveDataIterator(out SaveDataIterator iterator, SaveDataSpaceId.User));
|
||||
|
||||
var info = new SaveDataInfo[2];
|
||||
iterator.ReadSaveDataInfo(out long entriesRead, info);
|
||||
|
||||
Assert.Equal(1, entriesRead);
|
||||
|
||||
// Get the created save data's sizes
|
||||
Assert.Success(fs.GetSaveDataAvailableSize(out long actualAvailableSize, info[0].SaveDataId));
|
||||
Assert.Success(fs.GetSaveDataJournalSize(out long actualJournalSize, info[0].SaveDataId));
|
||||
|
||||
Assert.Equal(availableSize, actualAvailableSize);
|
||||
Assert.Equal(journalSize, actualJournalSize);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DeleteSaveData_DoesNotExist_ReturnsTargetNotFound()
|
||||
{
|
||||
|
@ -287,6 +372,64 @@ namespace LibHac.Tests.Fs.FileSystemClientTests.ShimTests
|
|||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetSaveDataCommitId_AfterSetSaveDataCommitIdIsCalled_ReturnsSetCommitId()
|
||||
{
|
||||
long commitId = 46506854;
|
||||
|
||||
var applicationId = new Ncm.ApplicationId(1);
|
||||
var userId = new UserId(5, 4);
|
||||
|
||||
FileSystemClient fs = FileSystemServerFactory.CreateClient(true);
|
||||
|
||||
// Create the save
|
||||
Assert.Success(fs.CreateSaveData(applicationId, userId, 0, 0x1000, 0x1000, SaveDataFlags.None));
|
||||
|
||||
// Get the created save data's ID
|
||||
Assert.Success(fs.OpenSaveDataIterator(out SaveDataIterator iterator, SaveDataSpaceId.User));
|
||||
|
||||
var info = new SaveDataInfo[2];
|
||||
iterator.ReadSaveDataInfo(out long entriesRead, info);
|
||||
|
||||
Assert.Equal(1, entriesRead);
|
||||
|
||||
// Set the new commit ID
|
||||
Assert.Success(fs.SetSaveDataCommitId(info[0].SpaceId, info[0].SaveDataId, commitId));
|
||||
|
||||
Assert.Success(fs.GetSaveDataCommitId(out long actualCommitId, info[0].SpaceId, info[0].SaveDataId));
|
||||
|
||||
Assert.Equal(commitId, actualCommitId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetSaveDataTimeStamp_AfterSetSaveDataTimeStampIsCalled_ReturnsSetTimeStamp()
|
||||
{
|
||||
var timeStamp = new PosixTime(12345678);
|
||||
|
||||
var applicationId = new Ncm.ApplicationId(1);
|
||||
var userId = new UserId(5, 4);
|
||||
|
||||
FileSystemClient fs = FileSystemServerFactory.CreateClient(true);
|
||||
|
||||
// Create the save
|
||||
Assert.Success(fs.CreateSaveData(applicationId, userId, 0, 0x1000, 0x1000, SaveDataFlags.None));
|
||||
|
||||
// Get the created save data's ID
|
||||
Assert.Success(fs.OpenSaveDataIterator(out SaveDataIterator iterator, SaveDataSpaceId.User));
|
||||
|
||||
var info = new SaveDataInfo[2];
|
||||
iterator.ReadSaveDataInfo(out long entriesRead, info);
|
||||
|
||||
Assert.Equal(1, entriesRead);
|
||||
|
||||
// Set the new timestamp
|
||||
Assert.Success(fs.SetSaveDataTimeStamp(info[0].SpaceId, info[0].SaveDataId, timeStamp));
|
||||
|
||||
Assert.Success(fs.GetSaveDataTimeStamp(out PosixTime actualTimeStamp, info[0].SpaceId, info[0].SaveDataId));
|
||||
|
||||
Assert.Equal(timeStamp, actualTimeStamp);
|
||||
}
|
||||
|
||||
private static Result PopulateSaveData(FileSystemClient fs, int count, int seed = -1)
|
||||
{
|
||||
if (seed == -1)
|
||||
|
|
Loading…
Reference in a new issue