mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Add main parts of CreateSaveDataFileSystem
This commit is contained in:
parent
fef6d19900
commit
92049bf9b7
15 changed files with 640 additions and 44 deletions
16
src/LibHac/Common/SystemTitleIds.cs
Normal file
16
src/LibHac/Common/SystemTitleIds.cs
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
using LibHac.Ncm;
|
||||||
|
|
||||||
|
namespace LibHac.Common
|
||||||
|
{
|
||||||
|
public static class SystemTitleIds
|
||||||
|
{
|
||||||
|
public static TitleId Fs => new TitleId(0x0100000000000000);
|
||||||
|
public static TitleId Loader => new TitleId(0x0100000000000001);
|
||||||
|
public static TitleId Ncm => new TitleId(0x0100000000000002);
|
||||||
|
public static TitleId ProcessManager => new TitleId(0x0100000000000003);
|
||||||
|
public static TitleId Sm => new TitleId(0x0100000000000004);
|
||||||
|
public static TitleId Boot => new TitleId(0x0100000000000005);
|
||||||
|
|
||||||
|
public static TitleId Bcat => new TitleId(0x010000000000000C);
|
||||||
|
}
|
||||||
|
}
|
|
@ -112,6 +112,13 @@ namespace LibHac.Fs
|
||||||
public Span<byte> Hash => SpanHelpers.CreateSpan(ref _hashStart, HashLength);
|
public Span<byte> Hash => SpanHelpers.CreateSpan(ref _hashStart, HashLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
|
public struct OptionalHashSalt
|
||||||
|
{
|
||||||
|
public bool IsSet;
|
||||||
|
public HashSalt HashSalt;
|
||||||
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Explicit, Size = 0x10)]
|
[StructLayout(LayoutKind.Explicit, Size = 0x10)]
|
||||||
public struct SaveMetaCreateInfo
|
public struct SaveMetaCreateInfo
|
||||||
{
|
{
|
||||||
|
@ -125,7 +132,7 @@ namespace LibHac.Fs
|
||||||
[FieldOffset(0x00)] public long Size;
|
[FieldOffset(0x00)] public long Size;
|
||||||
[FieldOffset(0x08)] public long JournalSize;
|
[FieldOffset(0x08)] public long JournalSize;
|
||||||
[FieldOffset(0x10)] public ulong BlockSize;
|
[FieldOffset(0x10)] public ulong BlockSize;
|
||||||
[FieldOffset(0x18)] public ulong OwnerId;
|
[FieldOffset(0x18)] public TitleId OwnerId;
|
||||||
[FieldOffset(0x20)] public uint Flags;
|
[FieldOffset(0x20)] public uint Flags;
|
||||||
[FieldOffset(0x24)] public SaveDataSpaceId SpaceId;
|
[FieldOffset(0x24)] public SaveDataSpaceId SpaceId;
|
||||||
[FieldOffset(0x25)] public bool Field25;
|
[FieldOffset(0x25)] public bool Field25;
|
||||||
|
|
|
@ -18,7 +18,32 @@ namespace LibHac.Fs.Shim
|
||||||
rc = MountSaveDataImpl(fs, mountName, SaveDataSpaceId.User, titleId, userId, SaveDataType.SaveData, false, 0);
|
rc = MountSaveDataImpl(fs, mountName, SaveDataSpaceId.User, titleId, userId, SaveDataType.SaveData, false, 0);
|
||||||
TimeSpan endTime = fs.Time.GetCurrent();
|
TimeSpan endTime = fs.Time.GetCurrent();
|
||||||
|
|
||||||
fs.OutputAccessLog(rc, startTime, endTime, $", name: \"{mountName.ToString()}\", applicationid: 0x{titleId}, userid: {userId}");
|
fs.OutputAccessLog(rc, startTime, endTime, $", name: \"{mountName.ToString()}\", applicationid: 0x{titleId}, userid: 0x{userId}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rc = MountSaveDataImpl(fs, mountName, SaveDataSpaceId.User, titleId, userId, SaveDataType.SaveData, false, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rc.IsSuccess() && fs.IsEnabledAccessLog(LocalAccessLogMode.Application))
|
||||||
|
{
|
||||||
|
fs.EnableFileSystemAccessorAccessLog(mountName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Result MountSaveDataReadOnly(this FileSystemClient fs, U8Span mountName, TitleId titleId, UserId userId)
|
||||||
|
{
|
||||||
|
Result rc;
|
||||||
|
|
||||||
|
if (fs.IsEnabledAccessLog(LocalAccessLogMode.Application))
|
||||||
|
{
|
||||||
|
TimeSpan startTime = fs.Time.GetCurrent();
|
||||||
|
rc = MountSaveDataImpl(fs, mountName, SaveDataSpaceId.User, titleId, userId, SaveDataType.SaveData, true, 0);
|
||||||
|
TimeSpan endTime = fs.Time.GetCurrent();
|
||||||
|
|
||||||
|
fs.OutputAccessLog(rc, startTime, endTime, $", name: \"{mountName.ToString()}\", applicationid: 0x{titleId}, userid: 0x{userId}");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,11 +1,85 @@
|
||||||
using LibHac.FsService;
|
using LibHac.FsService;
|
||||||
|
using LibHac.FsSystem.Save;
|
||||||
|
using LibHac.Ncm;
|
||||||
|
|
||||||
namespace LibHac.Fs.Shim
|
namespace LibHac.Fs.Shim
|
||||||
{
|
{
|
||||||
public static class SaveDataManagement
|
public static class SaveDataManagement
|
||||||
{
|
{
|
||||||
|
public static Result CreateSaveData(this FileSystemClient fs, TitleId titleId, UserId userId, TitleId ownerId,
|
||||||
|
long size, long journalSize, uint flags)
|
||||||
|
{
|
||||||
|
return fs.RunOperationWithAccessLog(LocalAccessLogMode.System,
|
||||||
|
() =>
|
||||||
|
{
|
||||||
|
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
||||||
|
|
||||||
|
var attribute = new SaveDataAttribute
|
||||||
|
{
|
||||||
|
TitleId = titleId,
|
||||||
|
UserId = userId,
|
||||||
|
Type = SaveDataType.SaveData
|
||||||
|
};
|
||||||
|
|
||||||
|
var createInfo = new SaveDataCreateInfo
|
||||||
|
{
|
||||||
|
Size = size,
|
||||||
|
JournalSize = journalSize,
|
||||||
|
BlockSize = 0x4000,
|
||||||
|
OwnerId = ownerId,
|
||||||
|
Flags = flags,
|
||||||
|
SpaceId = SaveDataSpaceId.User
|
||||||
|
};
|
||||||
|
|
||||||
|
var metaInfo = new SaveMetaCreateInfo
|
||||||
|
{
|
||||||
|
Type = SaveMetaType.Thumbnail,
|
||||||
|
Size = 0x40060
|
||||||
|
};
|
||||||
|
|
||||||
|
return fsProxy.CreateSaveDataFileSystem(ref attribute, ref createInfo, ref metaInfo);
|
||||||
|
},
|
||||||
|
() => $", applicationid: 0x{titleId.Value:X}, userid: 0x{userId}, save_data_owner_id: 0x{ownerId.Value:X}, save_data_size: {size}, save_data_journal_size: {journalSize}, save_data_flags: 0x{flags:x8}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Result CreateSaveData(this FileSystemClient fs, TitleId titleId, UserId userId, TitleId ownerId,
|
||||||
|
long size, long journalSize, HashSalt hashSalt, uint flags)
|
||||||
|
{
|
||||||
|
return fs.RunOperationWithAccessLog(LocalAccessLogMode.System,
|
||||||
|
() =>
|
||||||
|
{
|
||||||
|
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
||||||
|
|
||||||
|
var attribute = new SaveDataAttribute
|
||||||
|
{
|
||||||
|
TitleId = titleId,
|
||||||
|
UserId = userId,
|
||||||
|
Type = SaveDataType.SaveData
|
||||||
|
};
|
||||||
|
|
||||||
|
var createInfo = new SaveDataCreateInfo
|
||||||
|
{
|
||||||
|
Size = size,
|
||||||
|
JournalSize = journalSize,
|
||||||
|
BlockSize = 0x4000,
|
||||||
|
OwnerId = ownerId,
|
||||||
|
Flags = flags,
|
||||||
|
SpaceId = SaveDataSpaceId.User
|
||||||
|
};
|
||||||
|
|
||||||
|
var metaInfo = new SaveMetaCreateInfo
|
||||||
|
{
|
||||||
|
Type = SaveMetaType.Thumbnail,
|
||||||
|
Size = 0x40060
|
||||||
|
};
|
||||||
|
|
||||||
|
return fsProxy.CreateSaveDataFileSystemWithHashSalt(ref attribute, ref createInfo, ref metaInfo, ref hashSalt);
|
||||||
|
},
|
||||||
|
() => $", applicationid: 0x{titleId.Value:X}, userid: 0x{userId}, save_data_owner_id: 0x{ownerId.Value:X}, save_data_size: {size}, save_data_journal_size: {journalSize}, save_data_flags: 0x{flags:x8}");
|
||||||
|
}
|
||||||
|
|
||||||
public static Result CreateSystemSaveData(this FileSystemClient fs, SaveDataSpaceId spaceId,
|
public static Result CreateSystemSaveData(this FileSystemClient fs, SaveDataSpaceId spaceId,
|
||||||
ulong saveDataId, UserId userId, ulong ownerId, long size, long journalSize, uint flags)
|
ulong saveDataId, UserId userId, TitleId ownerId, long size, long journalSize, uint flags)
|
||||||
{
|
{
|
||||||
return fs.RunOperationWithAccessLog(LocalAccessLogMode.System,
|
return fs.RunOperationWithAccessLog(LocalAccessLogMode.System,
|
||||||
() =>
|
() =>
|
||||||
|
@ -30,11 +104,11 @@ namespace LibHac.Fs.Shim
|
||||||
|
|
||||||
return fsProxy.CreateSaveDataFileSystemBySystemSaveDataId(ref attribute, ref createInfo);
|
return fsProxy.CreateSaveDataFileSystemBySystemSaveDataId(ref attribute, ref createInfo);
|
||||||
},
|
},
|
||||||
() => $", savedataspaceid: {spaceId}, savedataid: 0x{saveDataId:X}, userid: 0x{userId.Id.High:X16}{userId.Id.Low:X16}, save_data_owner_id: 0x{ownerId:X}, save_data_size: {size}, save_data_journal_size: {journalSize}, save_data_flags: 0x{flags:X8}");
|
() => $", savedataspaceid: {spaceId}, savedataid: 0x{saveDataId:X}, userid: 0x{userId.Id.High:X16}{userId.Id.Low:X16}, save_data_owner_id: 0x{ownerId.Value:X}, save_data_size: {size}, save_data_journal_size: {journalSize}, save_data_flags: 0x{flags:X8}");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Result CreateSystemSaveData(this FileSystemClient fs, ulong saveDataId, UserId userId,
|
public static Result CreateSystemSaveData(this FileSystemClient fs, ulong saveDataId, UserId userId,
|
||||||
ulong ownerId, long size, long journalSize, uint flags)
|
TitleId ownerId, long size, long journalSize, uint flags)
|
||||||
{
|
{
|
||||||
return CreateSystemSaveData(fs, SaveDataSpaceId.System, saveDataId, userId, ownerId, size, journalSize, flags);
|
return CreateSystemSaveData(fs, SaveDataSpaceId.System, saveDataId, userId, ownerId, size, journalSize, flags);
|
||||||
}
|
}
|
||||||
|
@ -42,10 +116,10 @@ namespace LibHac.Fs.Shim
|
||||||
public static Result CreateSystemSaveData(this FileSystemClient fs, ulong saveDataId, UserId userId, long size,
|
public static Result CreateSystemSaveData(this FileSystemClient fs, ulong saveDataId, UserId userId, long size,
|
||||||
long journalSize, uint flags)
|
long journalSize, uint flags)
|
||||||
{
|
{
|
||||||
return CreateSystemSaveData(fs, SaveDataSpaceId.System, saveDataId, userId, 0, size, journalSize, flags);
|
return CreateSystemSaveData(fs, SaveDataSpaceId.System, saveDataId, userId, TitleId.Zero, size, journalSize, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Result CreateSystemSaveData(this FileSystemClient fs, ulong saveDataId, ulong ownerId, long size,
|
public static Result CreateSystemSaveData(this FileSystemClient fs, ulong saveDataId, TitleId ownerId, long size,
|
||||||
long journalSize, uint flags)
|
long journalSize, uint flags)
|
||||||
{
|
{
|
||||||
return CreateSystemSaveData(fs, SaveDataSpaceId.System, saveDataId, UserId.Zero, ownerId, size, journalSize, flags);
|
return CreateSystemSaveData(fs, SaveDataSpaceId.System, saveDataId, UserId.Zero, ownerId, size, journalSize, flags);
|
||||||
|
@ -54,11 +128,11 @@ namespace LibHac.Fs.Shim
|
||||||
public static Result CreateSystemSaveData(this FileSystemClient fs, ulong saveDataId, long size,
|
public static Result CreateSystemSaveData(this FileSystemClient fs, ulong saveDataId, long size,
|
||||||
long journalSize, uint flags)
|
long journalSize, uint flags)
|
||||||
{
|
{
|
||||||
return CreateSystemSaveData(fs, SaveDataSpaceId.System, saveDataId, UserId.Zero, 0, size, journalSize, flags);
|
return CreateSystemSaveData(fs, SaveDataSpaceId.System, saveDataId, UserId.Zero, TitleId.Zero, size, journalSize, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Result CreateSystemSaveData(this FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId,
|
public static Result CreateSystemSaveData(this FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId,
|
||||||
ulong ownerId, long size, long journalSize, uint flags)
|
TitleId ownerId, long size, long journalSize, uint flags)
|
||||||
{
|
{
|
||||||
return CreateSystemSaveData(fs, spaceId, saveDataId, UserId.Zero, ownerId, size, journalSize, flags);
|
return CreateSystemSaveData(fs, spaceId, saveDataId, UserId.Zero, ownerId, size, journalSize, flags);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ using LibHac.Common;
|
||||||
|
|
||||||
namespace LibHac.Fs
|
namespace LibHac.Fs
|
||||||
{
|
{
|
||||||
[DebuggerDisplay("{ToString(),nq}")]
|
[DebuggerDisplay("0x{ToString(),nq}")]
|
||||||
[StructLayout(LayoutKind.Sequential, Size = 0x10)]
|
[StructLayout(LayoutKind.Sequential, Size = 0x10)]
|
||||||
public struct UserId : IEquatable<UserId>, IComparable<UserId>, IComparable
|
public struct UserId : IEquatable<UserId>, IComparable<UserId>, IComparable
|
||||||
{
|
{
|
||||||
|
@ -25,7 +25,7 @@ namespace LibHac.Fs
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return $"0x{Id.High:X16}{Id.Low:X16}";
|
return $"{Id.High:X16}{Id.Low:X16}";
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Equals(UserId other) => Id == other.Id;
|
public bool Equals(UserId other) => Id == other.Id;
|
||||||
|
|
|
@ -3,6 +3,7 @@ using LibHac.Common;
|
||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
using LibHac.FsSystem;
|
using LibHac.FsSystem;
|
||||||
using LibHac.FsSystem.Save;
|
using LibHac.FsSystem.Save;
|
||||||
|
using LibHac.Kvdb;
|
||||||
using LibHac.Ncm;
|
using LibHac.Ncm;
|
||||||
using LibHac.Spl;
|
using LibHac.Spl;
|
||||||
|
|
||||||
|
@ -123,21 +124,188 @@ namespace LibHac.FsService
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Result CreateSaveDataFileSystemImpl(ref SaveDataAttribute attribute, ref SaveDataCreateInfo createInfo,
|
||||||
|
ref SaveMetaCreateInfo metaCreateInfo, ref OptionalHashSalt hashSalt, bool something)
|
||||||
|
{
|
||||||
|
ulong saveDataId;
|
||||||
|
Result rc;
|
||||||
|
|
||||||
|
SaveDataIndexerReader reader = default;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (attribute.SaveDataId == FileSystemServer.SaveIndexerId)
|
||||||
|
{
|
||||||
|
saveDataId = FileSystemServer.SaveIndexerId;
|
||||||
|
rc = FsProxyCore.DoesSaveDataExist(out bool saveExists, createInfo.SpaceId, saveDataId);
|
||||||
|
|
||||||
|
if (rc.IsSuccess() && saveExists)
|
||||||
|
{
|
||||||
|
return ResultFs.PathAlreadyExists.Log();
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rc = FsServer.SaveDataIndexerManager.GetSaveDataIndexer(out reader, createInfo.SpaceId);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
SaveDataAttribute indexerKey = attribute;
|
||||||
|
|
||||||
|
if (attribute.SaveDataId != 0 || attribute.UserId == UserId.Zero)
|
||||||
|
{
|
||||||
|
saveDataId = attribute.SaveDataId;
|
||||||
|
|
||||||
|
rc = reader.Indexer.AddSystemSaveData(ref indexerKey);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (attribute.Type != SaveDataType.SystemSaveData &&
|
||||||
|
attribute.Type != SaveDataType.BcatSystemStorage)
|
||||||
|
{
|
||||||
|
if (reader.Indexer.IsFull())
|
||||||
|
{
|
||||||
|
return ResultKvdb.TooLargeKeyOrDbFull.Log();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = reader.Indexer.Add(out saveDataId, ref indexerKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rc == ResultFs.SaveDataPathAlreadyExists)
|
||||||
|
{
|
||||||
|
return ResultFs.PathAlreadyExists.LogConverted(rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = reader.Indexer.SetState(saveDataId, 1);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
SaveDataSpaceId indexerSpaceId = GetSpaceIdForIndexer(createInfo.SpaceId);
|
||||||
|
|
||||||
|
rc = reader.Indexer.SetSpaceId(saveDataId, indexerSpaceId);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
// todo: calculate size
|
||||||
|
long size = 0;
|
||||||
|
|
||||||
|
rc = reader.Indexer.SetSize(saveDataId, size);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = FsProxyCore.CreateSaveDataFileSystem(saveDataId, ref attribute, ref createInfo, SaveDataRootPath,
|
||||||
|
hashSalt, false);
|
||||||
|
|
||||||
|
if (rc.IsFailure())
|
||||||
|
{
|
||||||
|
// todo: remove and recreate
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (metaCreateInfo.Type != SaveMetaType.None)
|
||||||
|
{
|
||||||
|
rc = FsProxyCore.CreateSaveDataMetaFile(saveDataId, createInfo.SpaceId, metaCreateInfo.Type,
|
||||||
|
metaCreateInfo.Size);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
if(metaCreateInfo.Type == SaveMetaType.Thumbnail)
|
||||||
|
{
|
||||||
|
rc = FsProxyCore.OpenSaveDataMetaFile(out IFile metaFile, saveDataId, createInfo.SpaceId,
|
||||||
|
metaCreateInfo.Type);
|
||||||
|
|
||||||
|
using(metaFile)
|
||||||
|
{
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
ReadOnlySpan<byte> metaFileData = stackalloc byte[0x20];
|
||||||
|
|
||||||
|
rc = metaFile.Write(0, metaFileData, WriteOption.Flush);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attribute.SaveDataId == FileSystemServer.SaveIndexerId || something)
|
||||||
|
{
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = reader.Indexer.SetState(saveDataId, 0);
|
||||||
|
|
||||||
|
if (rc.IsFailure())
|
||||||
|
{
|
||||||
|
// Delete if flags allow
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = reader.Indexer.Commit();
|
||||||
|
|
||||||
|
if (rc.IsFailure())
|
||||||
|
{
|
||||||
|
// Delete if flags allow
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
reader.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Result CreateSaveDataFileSystem(ref SaveDataAttribute attribute, ref SaveDataCreateInfo createInfo,
|
public Result CreateSaveDataFileSystem(ref SaveDataAttribute attribute, ref SaveDataCreateInfo createInfo,
|
||||||
ref SaveMetaCreateInfo metaCreateInfo)
|
ref SaveMetaCreateInfo metaCreateInfo)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
OptionalHashSalt hashSalt = default;
|
||||||
|
|
||||||
|
return CreateUserSaveDataFileSystem(ref attribute, ref createInfo, ref metaCreateInfo, ref hashSalt);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result CreateSaveDataFileSystemWithHashSalt(ref SaveDataAttribute attribute, ref SaveDataCreateInfo createInfo,
|
public Result CreateSaveDataFileSystemWithHashSalt(ref SaveDataAttribute attribute, ref SaveDataCreateInfo createInfo,
|
||||||
ref SaveMetaCreateInfo metaCreateInfo, ref HashSalt hashSalt)
|
ref SaveMetaCreateInfo metaCreateInfo, ref HashSalt hashSalt)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
var hashSaltCopy = new OptionalHashSalt
|
||||||
|
{
|
||||||
|
IsSet = true,
|
||||||
|
HashSalt = hashSalt
|
||||||
|
};
|
||||||
|
|
||||||
|
return CreateUserSaveDataFileSystem(ref attribute, ref createInfo, ref metaCreateInfo, ref hashSaltCopy);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result CreateUserSaveDataFileSystem(ref SaveDataAttribute attribute, ref SaveDataCreateInfo createInfo,
|
||||||
|
ref SaveMetaCreateInfo metaCreateInfo, ref OptionalHashSalt hashSalt)
|
||||||
|
{
|
||||||
|
return CreateSaveDataFileSystemImpl(ref attribute, ref createInfo, ref metaCreateInfo, ref hashSalt, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result CreateSaveDataFileSystemBySystemSaveDataId(ref SaveDataAttribute attribute, ref SaveDataCreateInfo createInfo)
|
public Result CreateSaveDataFileSystemBySystemSaveDataId(ref SaveDataAttribute attribute, ref SaveDataCreateInfo createInfo)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
|
||||||
|
if (!IsSystemSaveDataId(attribute.SaveDataId))
|
||||||
|
return ResultFs.InvalidArgument.Log();
|
||||||
|
|
||||||
|
SaveDataCreateInfo newCreateInfo = createInfo;
|
||||||
|
|
||||||
|
if (createInfo.OwnerId == TitleId.Zero)
|
||||||
|
{
|
||||||
|
// Assign the current program's ID
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Missing permission checks
|
||||||
|
|
||||||
|
if (attribute.Type == SaveDataType.BcatSystemStorage)
|
||||||
|
{
|
||||||
|
newCreateInfo.OwnerId = SystemTitleIds.Bcat;
|
||||||
|
}
|
||||||
|
|
||||||
|
SaveMetaCreateInfo metaCreateInfo = default;
|
||||||
|
OptionalHashSalt optionalHashSalt = default;
|
||||||
|
|
||||||
|
return CreateSaveDataFileSystemImpl(ref attribute, ref newCreateInfo, ref metaCreateInfo,
|
||||||
|
ref optionalHashSalt, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result ExtendSaveDataFileSystem(SaveDataSpaceId spaceId, ulong saveDataId, long dataSize, long journalSize)
|
public Result ExtendSaveDataFileSystem(SaveDataSpaceId spaceId, ulong saveDataId, long dataSize, long journalSize)
|
||||||
|
@ -161,15 +329,13 @@ namespace LibHac.FsService
|
||||||
{
|
{
|
||||||
SaveDataAttribute indexerKey = attribute;
|
SaveDataAttribute indexerKey = attribute;
|
||||||
|
|
||||||
Result rc = FsServer.SaveDataIndexerManager.GetSaveDataIndexer(out SaveDataIndexerReader reader, spaceId);
|
Result rc = FsServer.SaveDataIndexerManager.GetSaveDataIndexer(out SaveDataIndexerReader tmpReader, spaceId);
|
||||||
using SaveDataIndexerReader c = reader;
|
using SaveDataIndexerReader reader = tmpReader;
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
c.Indexer.Get(out SaveDataIndexerValue indexerValue, ref indexerKey);
|
reader.Indexer.Get(out SaveDataIndexerValue indexerValue, ref indexerKey);
|
||||||
|
|
||||||
SaveDataSpaceId indexerSpaceId = spaceId == SaveDataSpaceId.ProperSystem || spaceId == SaveDataSpaceId.Safe
|
SaveDataSpaceId indexerSpaceId = GetSpaceIdForIndexer(spaceId);
|
||||||
? SaveDataSpaceId.System
|
|
||||||
: spaceId;
|
|
||||||
|
|
||||||
if (indexerValue.SpaceId != indexerSpaceId)
|
if (indexerValue.SpaceId != indexerSpaceId)
|
||||||
return ResultFs.TargetNotFound.Log();
|
return ResultFs.TargetNotFound.Log();
|
||||||
|
@ -652,5 +818,12 @@ namespace LibHac.FsService
|
||||||
{
|
{
|
||||||
return (long)id < 0;
|
return (long)id < 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static SaveDataSpaceId GetSpaceIdForIndexer(SaveDataSpaceId spaceId)
|
||||||
|
{
|
||||||
|
return spaceId == SaveDataSpaceId.ProperSystem || spaceId == SaveDataSpaceId.Safe
|
||||||
|
? SaveDataSpaceId.System
|
||||||
|
: spaceId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using LibHac.Common;
|
||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
using LibHac.Fs.Shim;
|
using LibHac.Fs.Shim;
|
||||||
using LibHac.FsSystem;
|
using LibHac.FsSystem;
|
||||||
|
@ -189,6 +190,31 @@ namespace LibHac.FsService
|
||||||
return spaceId == SaveDataSpaceId.User && !string.IsNullOrWhiteSpace(saveDataRootPath);
|
return spaceId == SaveDataSpaceId.User && !string.IsNullOrWhiteSpace(saveDataRootPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Result DoesSaveDataExist(out bool exists, SaveDataSpaceId spaceId, ulong saveDataId)
|
||||||
|
{
|
||||||
|
exists = false;
|
||||||
|
|
||||||
|
Result rc = OpenSaveDataDirectory(out IFileSystem fileSystem, spaceId, string.Empty, true);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
string saveDataPath = $"/{saveDataId:x16}";
|
||||||
|
|
||||||
|
rc = fileSystem.GetEntryType(out _, saveDataPath);
|
||||||
|
|
||||||
|
if (rc.IsFailure())
|
||||||
|
{
|
||||||
|
if (rc == ResultFs.PathNotFound)
|
||||||
|
{
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
exists = true;
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
public Result OpenSaveDataFileSystem(out IFileSystem fileSystem, SaveDataSpaceId spaceId, ulong saveDataId,
|
public Result OpenSaveDataFileSystem(out IFileSystem fileSystem, SaveDataSpaceId spaceId, ulong saveDataId,
|
||||||
string saveDataRootPath, bool openReadOnly, SaveDataType type, bool cacheExtraData)
|
string saveDataRootPath, bool openReadOnly, SaveDataType type, bool cacheExtraData)
|
||||||
{
|
{
|
||||||
|
@ -202,14 +228,8 @@ namespace LibHac.FsService
|
||||||
|
|
||||||
if (allowDirectorySaveData)
|
if (allowDirectorySaveData)
|
||||||
{
|
{
|
||||||
try
|
rc = saveDirFs.EnsureDirectoryExists(GetSaveDataIdPath(saveDataId));
|
||||||
{
|
if (rc.IsFailure()) return rc;
|
||||||
saveDirFs.EnsureDirectoryExists(GetSaveDataIdPath(saveDataId));
|
|
||||||
}
|
|
||||||
catch (HorizonResultException ex)
|
|
||||||
{
|
|
||||||
return ex.ResultValue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Missing save FS cache lookup
|
// Missing save FS cache lookup
|
||||||
|
@ -299,6 +319,47 @@ namespace LibHac.FsService
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Result OpenSaveDataMetaFile(out IFile file, ulong saveDataId, SaveDataSpaceId spaceId, SaveMetaType type)
|
||||||
|
{
|
||||||
|
file = default;
|
||||||
|
|
||||||
|
string metaDirPath = $"/saveMeta/{saveDataId:x16}";
|
||||||
|
|
||||||
|
Result rc = OpenSaveDataDirectoryImpl(out IFileSystem tmpMetaDirFs, spaceId, metaDirPath, true);
|
||||||
|
using IFileSystem metaDirFs = tmpMetaDirFs;
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
string metaFilePath = $"/{(int)type:x8}.meta";
|
||||||
|
|
||||||
|
return metaDirFs.OpenFile(out file, metaFilePath, OpenMode.ReadWrite);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result CreateSaveDataMetaFile(ulong saveDataId, SaveDataSpaceId spaceId, SaveMetaType type, long size)
|
||||||
|
{
|
||||||
|
string metaDirPath = $"/saveMeta/{saveDataId:x16}";
|
||||||
|
|
||||||
|
Result rc = OpenSaveDataDirectoryImpl(out IFileSystem tmpMetaDirFs, spaceId, metaDirPath, true);
|
||||||
|
using IFileSystem metaDirFs = tmpMetaDirFs;
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
string metaFilePath = $"/{(int)type:x8}.meta";
|
||||||
|
|
||||||
|
if (size < 0) return ResultFs.ValueOutOfRange.Log();
|
||||||
|
|
||||||
|
return metaDirFs.CreateFile(metaFilePath, size, CreateFileOptions.None);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result CreateSaveDataFileSystem(ulong saveDataId, ref SaveDataAttribute attribute,
|
||||||
|
ref SaveDataCreateInfo createInfo, U8Span rootPath, OptionalHashSalt hashSalt, bool something)
|
||||||
|
{
|
||||||
|
// Use directory save data for now
|
||||||
|
|
||||||
|
Result rc = OpenSaveDataDirectory(out IFileSystem fileSystem, createInfo.SpaceId, string.Empty, false);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
return fileSystem.EnsureDirectoryExists(GetSaveDataIdPath(saveDataId));
|
||||||
|
}
|
||||||
|
|
||||||
public Result SetGlobalAccessLogMode(GlobalAccessLogMode mode)
|
public Result SetGlobalAccessLogMode(GlobalAccessLogMode mode)
|
||||||
{
|
{
|
||||||
LogMode = mode;
|
LogMode = mode;
|
||||||
|
|
|
@ -4,6 +4,14 @@ namespace LibHac.FsService
|
||||||
{
|
{
|
||||||
public interface ISaveDataIndexer
|
public interface ISaveDataIndexer
|
||||||
{
|
{
|
||||||
|
Result Commit();
|
||||||
|
Result Add(out ulong saveDataId, ref SaveDataAttribute key);
|
||||||
Result Get(out SaveDataIndexerValue value, ref SaveDataAttribute key);
|
Result Get(out SaveDataIndexerValue value, ref SaveDataAttribute key);
|
||||||
|
Result AddSystemSaveData(ref SaveDataAttribute key);
|
||||||
|
bool IsFull();
|
||||||
|
Result SetSpaceId(ulong saveDataId, SaveDataSpaceId spaceId);
|
||||||
|
Result SetSize(ulong saveDataId, long size);
|
||||||
|
Result SetState(ulong saveDataId, byte state);
|
||||||
|
Result GetBySaveDataId(out SaveDataIndexerValue value, ulong saveDataId);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,8 +1,11 @@
|
||||||
using System.Diagnostics;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
using LibHac.Fs.Shim;
|
using LibHac.Fs.Shim;
|
||||||
using LibHac.Kvdb;
|
using LibHac.Kvdb;
|
||||||
|
using LibHac.Ncm;
|
||||||
|
|
||||||
namespace LibHac.FsService
|
namespace LibHac.FsService
|
||||||
{
|
{
|
||||||
|
@ -19,7 +22,7 @@ namespace LibHac.FsService
|
||||||
private object Locker { get; } = new object();
|
private object Locker { get; } = new object();
|
||||||
private bool IsInitialized { get; set; }
|
private bool IsInitialized { get; set; }
|
||||||
private bool IsKvdbLoaded { get; set; }
|
private bool IsKvdbLoaded { get; set; }
|
||||||
private long LastPublishedId { get; set; }
|
private ulong LastPublishedId { get; set; }
|
||||||
|
|
||||||
public SaveDataIndexer(FileSystemClient fsClient, string mountName, SaveDataSpaceId spaceId, ulong saveDataId)
|
public SaveDataIndexer(FileSystemClient fsClient, string mountName, SaveDataSpaceId spaceId, ulong saveDataId)
|
||||||
{
|
{
|
||||||
|
@ -29,6 +32,102 @@ namespace LibHac.FsService
|
||||||
SpaceId = spaceId;
|
SpaceId = spaceId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Result Commit()
|
||||||
|
{
|
||||||
|
lock (Locker)
|
||||||
|
{
|
||||||
|
Result rc = Initialize();
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
rc = EnsureKvDatabaseLoaded(false);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
var mount = new Mounter();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
rc = mount.Initialize(FsClient, MountName, SpaceId, SaveDataId);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
rc = KvDatabase.WriteDatabaseToFile();
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
string idFilePath = $"{MountName}:/{LastIdFileName}";
|
||||||
|
|
||||||
|
rc = FsClient.OpenFile(out FileHandle handle, idFilePath, OpenMode.Write);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
bool fileAlreadyClosed = false;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ulong lastId = LastPublishedId;
|
||||||
|
|
||||||
|
rc = FsClient.WriteFile(handle, 0, SpanHelpers.AsByteSpan(ref lastId), WriteOption.None);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
rc = FsClient.FlushFile(handle);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
FsClient.CloseFile(handle);
|
||||||
|
fileAlreadyClosed = true;
|
||||||
|
|
||||||
|
return FsClient.Commit(MountName);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (!fileAlreadyClosed)
|
||||||
|
{
|
||||||
|
FsClient.CloseFile(handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
mount.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result Add(out ulong saveDataId, ref SaveDataAttribute key)
|
||||||
|
{
|
||||||
|
saveDataId = default;
|
||||||
|
|
||||||
|
lock (Locker)
|
||||||
|
{
|
||||||
|
Result rc = Initialize();
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
rc = EnsureKvDatabaseLoaded(false);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
SaveDataIndexerValue value = default;
|
||||||
|
|
||||||
|
rc = KvDatabase.Get(ref key, SpanHelpers.AsByteSpan(ref value));
|
||||||
|
|
||||||
|
if (rc.IsSuccess())
|
||||||
|
{
|
||||||
|
return ResultFs.SaveDataPathAlreadyExists.Log();
|
||||||
|
}
|
||||||
|
|
||||||
|
LastPublishedId++;
|
||||||
|
ulong newSaveDataId = LastPublishedId;
|
||||||
|
|
||||||
|
value = new SaveDataIndexerValue { SaveDataId = newSaveDataId };
|
||||||
|
|
||||||
|
rc = KvDatabase.Set(ref key, SpanHelpers.AsByteSpan(ref value));
|
||||||
|
|
||||||
|
if (rc.IsFailure())
|
||||||
|
{
|
||||||
|
LastPublishedId--;
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
saveDataId = newSaveDataId;
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Result Get(out SaveDataIndexerValue value, ref SaveDataAttribute key)
|
public Result Get(out SaveDataIndexerValue value, ref SaveDataAttribute key)
|
||||||
{
|
{
|
||||||
value = default;
|
value = default;
|
||||||
|
@ -52,6 +151,124 @@ namespace LibHac.FsService
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Result AddSystemSaveData(ref SaveDataAttribute key)
|
||||||
|
{
|
||||||
|
lock (Locker)
|
||||||
|
{
|
||||||
|
Result rc = Initialize();
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
rc = EnsureKvDatabaseLoaded(false);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
foreach (KeyValuePair<SaveDataAttribute, byte[]> kvp in KvDatabase)
|
||||||
|
{
|
||||||
|
ref SaveDataIndexerValue value = ref Unsafe.As<byte, SaveDataIndexerValue>(ref kvp.Value[0]);
|
||||||
|
|
||||||
|
if (key.SaveDataId == value.SaveDataId)
|
||||||
|
{
|
||||||
|
return ResultFs.SaveDataPathAlreadyExists.Log();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var newValue = new SaveDataIndexerValue
|
||||||
|
{
|
||||||
|
SaveDataId = key.SaveDataId
|
||||||
|
};
|
||||||
|
|
||||||
|
rc = KvDatabase.Set(ref key, SpanHelpers.AsByteSpan(ref newValue));
|
||||||
|
|
||||||
|
if (rc.IsFailure())
|
||||||
|
{
|
||||||
|
// todo: Missing some function call here
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsFull()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result SetSpaceId(ulong saveDataId, SaveDataSpaceId spaceId)
|
||||||
|
{
|
||||||
|
lock (Locker)
|
||||||
|
{
|
||||||
|
if (!TryGetBySaveDataIdInternal(out SaveDataAttribute key, out SaveDataIndexerValue value, saveDataId))
|
||||||
|
{
|
||||||
|
return ResultFs.TargetNotFound.Log();
|
||||||
|
}
|
||||||
|
|
||||||
|
value.SpaceId = spaceId;
|
||||||
|
|
||||||
|
return KvDatabase.Set(ref key, SpanHelpers.AsByteSpan(ref value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result SetSize(ulong saveDataId, long size)
|
||||||
|
{
|
||||||
|
lock (Locker)
|
||||||
|
{
|
||||||
|
if (!TryGetBySaveDataIdInternal(out SaveDataAttribute key, out SaveDataIndexerValue value, saveDataId))
|
||||||
|
{
|
||||||
|
return ResultFs.TargetNotFound.Log();
|
||||||
|
}
|
||||||
|
|
||||||
|
value.Size = size;
|
||||||
|
|
||||||
|
return KvDatabase.Set(ref key, SpanHelpers.AsByteSpan(ref value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result SetState(ulong saveDataId, byte state)
|
||||||
|
{
|
||||||
|
lock (Locker)
|
||||||
|
{
|
||||||
|
if (!TryGetBySaveDataIdInternal(out SaveDataAttribute key, out SaveDataIndexerValue value, saveDataId))
|
||||||
|
{
|
||||||
|
return ResultFs.TargetNotFound.Log();
|
||||||
|
}
|
||||||
|
|
||||||
|
value.State = state;
|
||||||
|
|
||||||
|
return KvDatabase.Set(ref key, SpanHelpers.AsByteSpan(ref value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result GetBySaveDataId(out SaveDataIndexerValue value, ulong saveDataId)
|
||||||
|
{
|
||||||
|
lock (Locker)
|
||||||
|
{
|
||||||
|
if (TryGetBySaveDataIdInternal(out _, out value, saveDataId))
|
||||||
|
{
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultFs.TargetNotFound.Log();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryGetBySaveDataIdInternal(out SaveDataAttribute key, out SaveDataIndexerValue value, ulong saveDataId)
|
||||||
|
{
|
||||||
|
foreach (KeyValuePair<SaveDataAttribute, byte[]> kvp in KvDatabase)
|
||||||
|
{
|
||||||
|
ref SaveDataIndexerValue currentValue = ref Unsafe.As<byte, SaveDataIndexerValue>(ref kvp.Value[0]);
|
||||||
|
|
||||||
|
if (currentValue.SaveDataId == saveDataId)
|
||||||
|
{
|
||||||
|
key = kvp.Key;
|
||||||
|
value = currentValue;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
key = default;
|
||||||
|
value = default;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private Result Initialize()
|
private Result Initialize()
|
||||||
{
|
{
|
||||||
if (IsInitialized) return Result.Success;
|
if (IsInitialized) return Result.Success;
|
||||||
|
@ -132,7 +349,7 @@ namespace LibHac.FsService
|
||||||
{
|
{
|
||||||
if (!createdNewFile)
|
if (!createdNewFile)
|
||||||
{
|
{
|
||||||
long lastId = default;
|
ulong lastId = default;
|
||||||
|
|
||||||
rc = FsClient.ReadFile(handle, 0, SpanHelpers.AsByteSpan(ref lastId));
|
rc = FsClient.ReadFile(handle, 0, SpanHelpers.AsByteSpan(ref lastId));
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
@ -179,7 +396,7 @@ namespace LibHac.FsService
|
||||||
{
|
{
|
||||||
if (rc == ResultFs.TargetNotFound)
|
if (rc == ResultFs.TargetNotFound)
|
||||||
{
|
{
|
||||||
rc = FsClient.CreateSystemSaveData(spaceId, saveDataId, 0, 0xC0000, 0xC0000, 0);
|
rc = FsClient.CreateSystemSaveData(spaceId, saveDataId, TitleId.Zero, 0xC0000, 0xC0000, 0);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
rc = FsClient.MountSystemSaveData(MountName.ToU8Span(), spaceId, saveDataId);
|
rc = FsClient.MountSystemSaveData(MountName.ToU8Span(), spaceId, saveDataId);
|
||||||
|
@ -195,7 +412,7 @@ namespace LibHac.FsService
|
||||||
rc = FsClient.DeleteSaveData(spaceId, saveDataId);
|
rc = FsClient.DeleteSaveData(spaceId, saveDataId);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
rc = FsClient.CreateSystemSaveData(spaceId, saveDataId, 0, 0xC0000, 0xC0000, 0);
|
rc = FsClient.CreateSystemSaveData(spaceId, saveDataId, TitleId.Zero, 0xC0000, 0xC0000, 0);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
rc = FsClient.MountSystemSaveData(MountName.ToU8Span(), spaceId, saveDataId);
|
rc = FsClient.MountSystemSaveData(MountName.ToU8Span(), spaceId, saveDataId);
|
||||||
|
|
|
@ -98,18 +98,25 @@ namespace LibHac.FsService
|
||||||
|
|
||||||
public ref struct SaveDataIndexerReader
|
public ref struct SaveDataIndexerReader
|
||||||
{
|
{
|
||||||
private object Locker;
|
private bool _isInitialized;
|
||||||
|
private object _locker;
|
||||||
public ISaveDataIndexer Indexer;
|
public ISaveDataIndexer Indexer;
|
||||||
|
|
||||||
internal SaveDataIndexerReader(ISaveDataIndexer indexer, object locker)
|
internal SaveDataIndexerReader(ISaveDataIndexer indexer, object locker)
|
||||||
{
|
{
|
||||||
Locker = locker;
|
_isInitialized = true;
|
||||||
|
_locker = locker;
|
||||||
Indexer = indexer;
|
Indexer = indexer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
Monitor.Exit(Locker);
|
if (_isInitialized)
|
||||||
|
{
|
||||||
|
Monitor.Exit(_locker);
|
||||||
|
|
||||||
|
_isInitialized = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ namespace LibHac.FsService
|
||||||
public struct SaveDataIndexerValue
|
public struct SaveDataIndexerValue
|
||||||
{
|
{
|
||||||
[FieldOffset(0x00)] public ulong SaveDataId;
|
[FieldOffset(0x00)] public ulong SaveDataId;
|
||||||
[FieldOffset(0x08)] public ulong Size;
|
[FieldOffset(0x08)] public long Size;
|
||||||
[FieldOffset(0x10)] public ulong Field10;
|
[FieldOffset(0x10)] public ulong Field10;
|
||||||
[FieldOffset(0x18)] public SaveDataSpaceId SpaceId;
|
[FieldOffset(0x18)] public SaveDataSpaceId SpaceId;
|
||||||
[FieldOffset(0x19)] public byte State;
|
[FieldOffset(0x19)] public byte State;
|
||||||
|
|
|
@ -47,6 +47,8 @@ namespace LibHac.FsSystem
|
||||||
{
|
{
|
||||||
ParentFs.NotifyCloseWritableFile();
|
ParentFs.NotifyCloseWritableFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BaseFile?.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -245,10 +245,10 @@ namespace LibHac.FsSystem
|
||||||
return (rc.IsSuccess() && type == DirectoryEntryType.File);
|
return (rc.IsSuccess() && type == DirectoryEntryType.File);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void EnsureDirectoryExists(this IFileSystem fs, string path)
|
public static Result EnsureDirectoryExists(this IFileSystem fs, string path)
|
||||||
{
|
{
|
||||||
path = PathTools.Normalize(path);
|
path = PathTools.Normalize(path);
|
||||||
if (fs.DirectoryExists(path)) return;
|
if (fs.DirectoryExists(path)) return Result.Success;
|
||||||
|
|
||||||
// Find the first subdirectory in the chain that doesn't exist
|
// Find the first subdirectory in the chain that doesn't exist
|
||||||
int i;
|
int i;
|
||||||
|
@ -276,11 +276,12 @@ namespace LibHac.FsSystem
|
||||||
{
|
{
|
||||||
string subPath = path.Substring(0, i);
|
string subPath = path.Substring(0, i);
|
||||||
|
|
||||||
fs.CreateDirectory(subPath);
|
Result rc = fs.CreateDirectory(subPath);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fs.CreateDirectory(path);
|
return fs.CreateDirectory(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void CreateOrOverwriteFile(this IFileSystem fs, string path, long size)
|
public static void CreateOrOverwriteFile(this IFileSystem fs, string path, long size)
|
||||||
|
|
|
@ -44,13 +44,18 @@ namespace LibHac.Kvdb
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result Set(ref TKey key, byte[] value)
|
public Result Set(ref TKey key, ReadOnlySpan<byte> value)
|
||||||
{
|
{
|
||||||
KvDict[key] = value;
|
KvDict[key] = value.ToArray();
|
||||||
|
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Dictionary<TKey, byte[]>.Enumerator GetEnumerator()
|
||||||
|
{
|
||||||
|
return KvDict.GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
public Result ReadDatabaseFromBuffer(ReadOnlySpan<byte> data)
|
public Result ReadDatabaseFromBuffer(ReadOnlySpan<byte> data)
|
||||||
{
|
{
|
||||||
KvDict.Clear();
|
KvDict.Clear();
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
{
|
{
|
||||||
public const int ModuleKvdb = 20;
|
public const int ModuleKvdb = 20;
|
||||||
|
|
||||||
public static Result TooLargeKey => new Result(ModuleKvdb, 1);
|
public static Result TooLargeKeyOrDbFull => new Result(ModuleKvdb, 1);
|
||||||
public static Result KeyNotFound => new Result(ModuleKvdb, 2);
|
public static Result KeyNotFound => new Result(ModuleKvdb, 2);
|
||||||
public static Result AllocationFailed => new Result(ModuleKvdb, 4);
|
public static Result AllocationFailed => new Result(ModuleKvdb, 4);
|
||||||
public static Result InvalidKeyValue => new Result(ModuleKvdb, 5);
|
public static Result InvalidKeyValue => new Result(ModuleKvdb, 5);
|
||||||
|
|
Loading…
Reference in a new issue