mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Add or update some save data FS shim classes for 14.0.0
- SaveData - SaveDataForDebug - SaveDataManagement
This commit is contained in:
parent
0875f5950c
commit
366aa51912
4 changed files with 345 additions and 24 deletions
|
@ -241,6 +241,16 @@ namespace LibHac.Fs.Impl
|
|||
}
|
||||
}
|
||||
|
||||
public ReadOnlySpan<byte> ToString(SaveDataFormatType value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case SaveDataFormatType.Normal: return new[] { (byte)'N', (byte)'o', (byte)'r', (byte)'m', (byte)'a', (byte)'l' };
|
||||
case SaveDataFormatType.NoJournal: return new[] { (byte)'N', (byte)'o', (byte)'J', (byte)'o', (byte)'u', (byte)'r', (byte)'n', (byte)'a', (byte)'l' };
|
||||
default: return ToValueString((int)value);
|
||||
}
|
||||
}
|
||||
|
||||
public ReadOnlySpan<byte> ToString(ContentType value)
|
||||
{
|
||||
switch (value)
|
||||
|
@ -1083,6 +1093,16 @@ namespace LibHac.Fs.Impl
|
|||
(byte)'d', (byte)':', (byte)' '
|
||||
};
|
||||
|
||||
/// <summary>"<c>, save_data_format_type: </c>"</summary>
|
||||
public static ReadOnlySpan<byte> LogSaveDataFormatType => // ", save_data_format_type: "
|
||||
new[]
|
||||
{
|
||||
(byte)',', (byte)' ', (byte)'s', (byte)'a', (byte)'v', (byte)'e', (byte)'_', (byte)'d',
|
||||
(byte)'a', (byte)'t', (byte)'a', (byte)'_', (byte)'f', (byte)'o', (byte)'r', (byte)'m',
|
||||
(byte)'a', (byte)'t', (byte)'_', (byte)'t', (byte)'y', (byte)'p', (byte)'e', (byte)':',
|
||||
(byte)' '
|
||||
};
|
||||
|
||||
/// <summary>"<c>, save_data_time_stamp: </c>"</summary>
|
||||
public static ReadOnlySpan<byte> LogSaveDataTimeStamp => // ", save_data_time_stamp: "
|
||||
new[]
|
||||
|
|
|
@ -6,7 +6,7 @@ using LibHac.Fs.Impl;
|
|||
using LibHac.FsSrv.Sf;
|
||||
using LibHac.Ncm;
|
||||
using LibHac.Os;
|
||||
|
||||
using LibHac.Util;
|
||||
using static LibHac.Fs.Impl.AccessLogStrings;
|
||||
using static LibHac.Fs.SaveData;
|
||||
using IFileSystem = LibHac.Fs.Fsa.IFileSystem;
|
||||
|
@ -17,12 +17,12 @@ namespace LibHac.Fs.Shim;
|
|||
/// <summary>
|
||||
/// Contains functions for mounting save data and checking if save data already exists or not.
|
||||
/// </summary>
|
||||
/// <remarks>Based on nnSdk 13.4.0</remarks>
|
||||
/// <remarks>Based on nnSdk 14.3.0</remarks>
|
||||
[SkipLocalsInit]
|
||||
public static class SaveData
|
||||
{
|
||||
private const long SaveDataSizeForDebug = 0x2000000;
|
||||
private const long SaveDataJournalSizeForDebug = 0x2000000;
|
||||
private const long SaveDataTotalSizeMax = 0xFA000000;
|
||||
private const int SaveDataBlockSize = 0x4000;
|
||||
|
||||
private static Result OpenSaveDataInternalStorageFileSystemImpl(FileSystemClient fs,
|
||||
ref UniqueRef<IFileSystem> outFileSystem, SaveDataSpaceId spaceId, ulong saveDataId)
|
||||
|
@ -42,6 +42,39 @@ public static class SaveData
|
|||
return Result.Success;
|
||||
}
|
||||
|
||||
private static Result ExtendSaveDataIfNeeded(FileSystemClient fs, UserId userId, long saveDataSize,
|
||||
long saveDataJournalSize)
|
||||
{
|
||||
// Find the save data for the current program.
|
||||
Result rc = SaveDataFilter.Make(out SaveDataFilter filter, InvalidProgramId.Value, SaveDataType.Account, userId,
|
||||
InvalidSystemSaveDataId, index: 0, SaveDataRank.Primary);
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
rc = fs.Impl.FindSaveDataWithFilter(out SaveDataInfo info, SaveDataSpaceId.User, in filter);
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
SaveDataSpaceId spaceId = info.SpaceId;
|
||||
ulong saveDataId = info.SaveDataId;
|
||||
|
||||
// Get the current save data's sizes.
|
||||
rc = fs.Impl.GetSaveDataAvailableSize(out long availableSize, spaceId, saveDataId);
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
rc = fs.Impl.GetSaveDataJournalSize(out long journalSize, spaceId, saveDataId);
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
// Extend the save data if it's not large enough.
|
||||
if (availableSize < saveDataSize || journalSize < saveDataJournalSize)
|
||||
{
|
||||
long newSaveDataSize = Math.Max(saveDataSize, availableSize);
|
||||
long newJournalSize = Math.Max(saveDataJournalSize, journalSize);
|
||||
rc = fs.Impl.ExtendSaveData(spaceId, saveDataId, newSaveDataSize, newJournalSize);
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
private static Result MountSaveDataImpl(this FileSystemClientImpl fs, U8Span mountName, SaveDataSpaceId spaceId,
|
||||
ProgramId programId, UserId userId, SaveDataType type, bool openReadOnly, ushort index)
|
||||
{
|
||||
|
@ -84,16 +117,26 @@ public static class SaveData
|
|||
return Result.Success;
|
||||
}
|
||||
|
||||
public static Result EnsureSaveDataForDebug(this FileSystemClientImpl fs, UserId userId)
|
||||
public static Result EnsureSaveDataImpl(this FileSystemClientImpl fs, UserId userId, long saveDataSize,
|
||||
long saveDataJournalSize, bool extendIfNeeded)
|
||||
{
|
||||
if (!Alignment.IsAlignedPow2(saveDataSize, SaveDataBlockSize))
|
||||
return ResultFs.InvalidSize.Log();
|
||||
|
||||
if (!Alignment.IsAlignedPow2(saveDataJournalSize, SaveDataBlockSize))
|
||||
return ResultFs.InvalidSize.Log();
|
||||
|
||||
if (saveDataSize + saveDataJournalSize > SaveDataTotalSizeMax)
|
||||
return ResultFs.InvalidSize.Log();
|
||||
|
||||
using SharedRef<IFileSystemProxy> fileSystemProxy = fs.GetFileSystemProxyServiceObject();
|
||||
|
||||
Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, ProgramId.InvalidId, SaveDataType.Account,
|
||||
InvalidUserId, InvalidSystemSaveDataId);
|
||||
Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, InvalidProgramId, SaveDataType.Account,
|
||||
userId, InvalidSystemSaveDataId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = SaveDataCreationInfo.Make(out SaveDataCreationInfo creationInfo, SaveDataSizeForDebug,
|
||||
SaveDataJournalSizeForDebug, default, SaveDataFlags.None, SaveDataSpaceId.User);
|
||||
rc = SaveDataCreationInfo.Make(out SaveDataCreationInfo creationInfo, saveDataSize, saveDataJournalSize,
|
||||
ownerId: 0, SaveDataFlags.None, SaveDataSpaceId.User);
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
var metaInfo = new SaveDataMetaInfo
|
||||
|
@ -106,17 +149,25 @@ public static class SaveData
|
|||
|
||||
if (rc.IsFailure())
|
||||
{
|
||||
// Return successfully if the save data already exists
|
||||
// Ensure the save is large enough if it already exists
|
||||
if (ResultFs.PathAlreadyExists.Includes(rc))
|
||||
rc.Catch();
|
||||
{
|
||||
if (extendIfNeeded)
|
||||
{
|
||||
rc = ExtendSaveDataIfNeeded(fs.Fs, userId, saveDataSize, saveDataJournalSize);
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return rc.Miss();
|
||||
}
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public static Result MountSaveData(this FileSystemClientImpl fs, U8Span mountName, UserId userId)
|
||||
public static Result MountSaveDataImpl(this FileSystemClientImpl fs, U8Span mountName, UserId userId)
|
||||
{
|
||||
return MountSaveDataImpl(fs, mountName, SaveDataSpaceId.User, ProgramId.InvalidId, userId,
|
||||
SaveDataType.Account, openReadOnly: false, index: 0);
|
||||
|
@ -130,7 +181,7 @@ public static class SaveData
|
|||
if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.Application))
|
||||
{
|
||||
Tick start = fs.Hos.Os.GetSystemTick();
|
||||
rc = MountSaveData(fs.Impl, mountName, InvalidUserId);
|
||||
rc = MountSaveDataImpl(fs.Impl, mountName, InvalidUserId);
|
||||
Tick end = fs.Hos.Os.GetSystemTick();
|
||||
|
||||
var sb = new U8StringBuilder(logBuffer, true);
|
||||
|
@ -141,7 +192,7 @@ public static class SaveData
|
|||
}
|
||||
else
|
||||
{
|
||||
rc = MountSaveData(fs.Impl, mountName, InvalidUserId);
|
||||
rc = MountSaveDataImpl(fs.Impl, mountName, InvalidUserId);
|
||||
}
|
||||
|
||||
fs.Impl.AbortIfNeeded(rc);
|
||||
|
@ -150,7 +201,7 @@ public static class SaveData
|
|||
if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.Application))
|
||||
fs.Impl.EnableFileSystemAccessorAccessLog(mountName);
|
||||
|
||||
return rc;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public static Result MountSaveData(this FileSystemClient fs, U8Span mountName, Ncm.ApplicationId applicationId,
|
||||
|
@ -185,7 +236,7 @@ public static class SaveData
|
|||
if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.Application))
|
||||
fs.Impl.EnableFileSystemAccessorAccessLog(mountName);
|
||||
|
||||
return rc;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public static Result MountSaveDataReadOnly(this FileSystemClient fs, U8Span mountName,
|
||||
|
@ -220,7 +271,7 @@ public static class SaveData
|
|||
if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.Application))
|
||||
fs.Impl.EnableFileSystemAccessorAccessLog(mountName);
|
||||
|
||||
return rc;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public static Result IsSaveDataExisting(this FileSystemClientImpl fs, out bool exists, UserId userId)
|
||||
|
@ -292,7 +343,7 @@ public static class SaveData
|
|||
if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.Application))
|
||||
fs.Impl.EnableFileSystemAccessorAccessLog(mountName);
|
||||
|
||||
return rc;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public static Result MountCacheStorage(this FileSystemClient fs, U8Span mountName)
|
||||
|
@ -324,7 +375,7 @@ public static class SaveData
|
|||
if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.Application))
|
||||
fs.Impl.EnableFileSystemAccessorAccessLog(mountName);
|
||||
|
||||
return rc;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public static Result MountCacheStorage(this FileSystemClient fs, U8Span mountName, int index)
|
||||
|
@ -358,7 +409,7 @@ public static class SaveData
|
|||
if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.Application))
|
||||
fs.Impl.EnableFileSystemAccessorAccessLog(mountName);
|
||||
|
||||
return rc;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public static Result MountCacheStorage(this FileSystemClient fs, U8Span mountName, Ncm.ApplicationId applicationId)
|
||||
|
@ -391,7 +442,7 @@ public static class SaveData
|
|||
if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System))
|
||||
fs.Impl.EnableFileSystemAccessorAccessLog(mountName);
|
||||
|
||||
return rc;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public static Result MountCacheStorage(this FileSystemClient fs, U8Span mountName, Ncm.ApplicationId applicationId,
|
||||
|
@ -426,7 +477,7 @@ public static class SaveData
|
|||
if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System))
|
||||
fs.Impl.EnableFileSystemAccessorAccessLog(mountName);
|
||||
|
||||
return rc;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public static Result OpenSaveDataInternalStorageFileSystem(this FileSystemClient fs,
|
||||
|
|
189
src/LibHac/Fs/Shim/SaveDataForDebug.cs
Normal file
189
src/LibHac/Fs/Shim/SaveDataForDebug.cs
Normal file
|
@ -0,0 +1,189 @@
|
|||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using LibHac.Common;
|
||||
using LibHac.Diag;
|
||||
using LibHac.Fs.Impl;
|
||||
using LibHac.FsSrv.Sf;
|
||||
using LibHac.Os;
|
||||
|
||||
using static LibHac.Fs.Impl.AccessLogStrings;
|
||||
using static LibHac.Fs.SaveData;
|
||||
|
||||
namespace LibHac.Fs.Shim;
|
||||
|
||||
/// <summary>
|
||||
/// Contains save data functions used for debugging during development.
|
||||
/// </summary>
|
||||
/// <remarks>Based on nnSdk 14.3.0</remarks>
|
||||
[SkipLocalsInit]
|
||||
public static class SaveDataForDebug
|
||||
{
|
||||
private const long SaveDataSizeForDebug = 0x2000000;
|
||||
private const long SaveDataJournalSizeForDebug = 0x2000000;
|
||||
|
||||
public static void SetSaveDataRootPath(this FileSystemClient fs, U8Span path)
|
||||
{
|
||||
Result rc;
|
||||
Span<byte> logBuffer = stackalloc byte[0x300];
|
||||
|
||||
if (fs.Impl.IsEnabledAccessLog() && fs.Impl.IsEnabledHandleAccessLog(null))
|
||||
{
|
||||
Tick start = fs.Hos.Os.GetSystemTick();
|
||||
rc = SetRootPath(fs, path);
|
||||
Tick end = fs.Hos.Os.GetSystemTick();
|
||||
|
||||
var sb = new U8StringBuilder(logBuffer, true);
|
||||
sb.Append(LogPath).Append(path).Append(LogQuote);
|
||||
|
||||
fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer));
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = SetRootPath(fs, path);
|
||||
}
|
||||
|
||||
fs.Impl.LogResultErrorMessage(rc);
|
||||
Abort.DoAbortUnlessSuccess(rc);
|
||||
|
||||
static Result SetRootPath(FileSystemClient fs, U8Span path)
|
||||
{
|
||||
Result rc = PathUtility.ConvertToFspPath(out FspPath sfPath, path);
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
using SharedRef<IFileSystemProxy> fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject();
|
||||
rc = fileSystemProxy.Get.SetSaveDataRootPath(in sfPath);
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
|
||||
public static void UnsetSaveDataRootPath(this FileSystemClient fs)
|
||||
{
|
||||
Result rc;
|
||||
|
||||
if (fs.Impl.IsEnabledAccessLog() && fs.Impl.IsEnabledHandleAccessLog(null))
|
||||
{
|
||||
Tick start = fs.Hos.Os.GetSystemTick();
|
||||
using SharedRef<IFileSystemProxy> fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject();
|
||||
rc = fileSystemProxy.Get.UnsetSaveDataRootPath();
|
||||
Tick end = fs.Hos.Os.GetSystemTick();
|
||||
|
||||
fs.Impl.OutputAccessLog(rc, start, end, null, U8Span.Empty);
|
||||
}
|
||||
else
|
||||
{
|
||||
using SharedRef<IFileSystemProxy> fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject();
|
||||
rc = fileSystemProxy.Get.UnsetSaveDataRootPath();
|
||||
}
|
||||
|
||||
fs.Impl.LogResultErrorMessage(rc);
|
||||
Abort.DoAbortUnlessSuccess(rc);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures the current application's debug save data is at least as large as the specified values.
|
||||
/// </summary>
|
||||
/// <remarks>Each application can have a single debug save. This save data is not associated with any
|
||||
/// user account and is intended for debug use when developing an application.</remarks>
|
||||
/// <param name="fs">The <see cref="FileSystemClient"/> to use.</param>
|
||||
/// <param name="saveDataSize">The size of the usable space in the save data.</param>
|
||||
/// <param name="saveDataJournalSize">The size of the save data's journal.</param>
|
||||
/// <returns><see cref="Result.Success"/>: The operation was successful.<br/>
|
||||
/// <see cref="ResultFs.TargetNotFound"/>: The save data was not found.<br/>
|
||||
/// <see cref="ResultFs.TargetLocked"/>: The save data is currently open or otherwise in use.<br/>
|
||||
/// <see cref="ResultFs.UsableSpaceNotEnough"/>: Insufficient free space to create or extend the save data.<br/>
|
||||
/// <see cref="ResultFs.PermissionDenied"/>: Insufficient permissions.</returns>
|
||||
public static Result EnsureSaveDataForDebug(this FileSystemClient fs, long saveDataSize, long saveDataJournalSize)
|
||||
{
|
||||
Result rc;
|
||||
Span<byte> logBuffer = stackalloc byte[0x60];
|
||||
|
||||
if (fs.Impl.IsEnabledAccessLog() && fs.Impl.IsEnabledHandleAccessLog(null))
|
||||
{
|
||||
Tick start = fs.Hos.Os.GetSystemTick();
|
||||
rc = Ensure(fs, saveDataSize, saveDataJournalSize);
|
||||
Tick end = fs.Hos.Os.GetSystemTick();
|
||||
|
||||
var sb = new U8StringBuilder(logBuffer, true);
|
||||
sb.Append(LogSaveDataSize).AppendFormat(saveDataSize, 'd')
|
||||
.Append(LogSaveDataJournalSize).AppendFormat(saveDataJournalSize, 'd');
|
||||
|
||||
fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer));
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = Ensure(fs, saveDataSize, saveDataJournalSize);
|
||||
}
|
||||
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
return Result.Success;
|
||||
|
||||
static Result Ensure(FileSystemClient fs, long saveDataSize, long saveDataJournalSize)
|
||||
{
|
||||
UserId userIdForDebug = InvalidUserId;
|
||||
|
||||
Result rc = fs.Impl.EnsureSaveDataImpl(userIdForDebug, saveDataSize, saveDataJournalSize,
|
||||
extendIfNeeded: true);
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Mounts the debug save data for the current application. Each application can have its own debug save
|
||||
/// that is not associated with any user account.
|
||||
/// </summary>
|
||||
/// <remarks>Each application can have a single debug save. This save data is not associated with any
|
||||
/// user account and is intended for debug use when developing an application.</remarks>
|
||||
/// <param name="fs">The <see cref="FileSystemClient"/> to use.</param>
|
||||
/// <param name="mountName">The mount name at which the file system will be mounted.</param>
|
||||
/// <returns><see cref="Result.Success"/>: The operation was successful.<br/>
|
||||
/// <see cref="ResultFs.TargetNotFound"/>: The save data was not found.<br/>
|
||||
/// <see cref="ResultFs.TargetLocked"/>: The save data is currently open or otherwise in use.<br/>
|
||||
/// <see cref="ResultFs.PermissionDenied"/>: Insufficient permissions.</returns>
|
||||
public static Result MountSaveDataForDebug(this FileSystemClient fs, U8Span mountName)
|
||||
{
|
||||
Result rc;
|
||||
Span<byte> logBuffer = stackalloc byte[0x30];
|
||||
|
||||
if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.Application))
|
||||
{
|
||||
Tick start = fs.Hos.Os.GetSystemTick();
|
||||
rc = Mount(fs, mountName);
|
||||
Tick end = fs.Hos.Os.GetSystemTick();
|
||||
|
||||
var sb = new U8StringBuilder(logBuffer, true);
|
||||
sb.Append(LogName).Append(mountName).Append(LogQuote);
|
||||
|
||||
fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer));
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = Mount(fs, mountName);
|
||||
}
|
||||
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.Application))
|
||||
fs.Impl.EnableFileSystemAccessorAccessLog(mountName);
|
||||
|
||||
return Result.Success;
|
||||
|
||||
static Result Mount(FileSystemClient fs, U8Span mountName)
|
||||
{
|
||||
UserId userIdForDebug = InvalidUserId;
|
||||
|
||||
Result rc = fs.Impl.EnsureSaveDataImpl(userIdForDebug, SaveDataSizeForDebug, SaveDataJournalSizeForDebug,
|
||||
extendIfNeeded: false);
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
rc = fs.Impl.MountSaveDataImpl(mountName, userIdForDebug);
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -21,7 +21,7 @@ namespace LibHac.Fs
|
|||
/// <summary>
|
||||
/// Allows iterating through the <see cref="SaveDataInfo"/> of a list of save data.
|
||||
/// </summary>
|
||||
/// <remarks>Based on nnSdk 13.4.0</remarks>
|
||||
/// <remarks>Based on nnSdk 14.3.0</remarks>
|
||||
public class SaveDataIterator : IDisposable
|
||||
{
|
||||
private readonly FileSystemClient _fsClient;
|
||||
|
@ -81,10 +81,12 @@ namespace LibHac.Fs.Shim
|
|||
/// <summary>
|
||||
/// Contains functions for creating, deleting, and otherwise managing save data.
|
||||
/// </summary>
|
||||
/// <remarks>Based on nnSdk 13.4.0</remarks>
|
||||
/// <remarks>Based on nnSdk 14.3.0</remarks>
|
||||
[SkipLocalsInit]
|
||||
public static class SaveDataManagement
|
||||
{
|
||||
private const int SaveDataBlockSize = 0x4000;
|
||||
|
||||
private class CacheStorageListCache : IDisposable
|
||||
{
|
||||
public readonly struct CacheEntry
|
||||
|
@ -990,6 +992,65 @@ namespace LibHac.Fs.Shim
|
|||
return CreateSystemSaveData(fs, spaceId, saveDataId, InvalidUserId, ownerId, size, journalSize, flags);
|
||||
}
|
||||
|
||||
public static Result CreateSystemSaveData(this FileSystemClientImpl fs, SaveDataSpaceId spaceId,
|
||||
ulong saveDataId, UserId userId, ulong ownerId, long size, long journalSize, SaveDataFlags flags,
|
||||
SaveDataFormatType formatType)
|
||||
{
|
||||
if (formatType == SaveDataFormatType.NoJournal && journalSize != 0)
|
||||
return ResultFs.InvalidArgument.Log();
|
||||
|
||||
using SharedRef<IFileSystemProxy> fileSystemProxy = fs.GetFileSystemProxyServiceObject();
|
||||
|
||||
Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, InvalidProgramId, SaveDataType.System,
|
||||
userId, saveDataId);
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
rc = SaveDataCreationInfo2.Make(out SaveDataCreationInfo2 creationInfo, in attribute, size, journalSize,
|
||||
SaveDataBlockSize, ownerId, flags, spaceId, formatType);
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
creationInfo.MetaType = SaveDataMetaType.None;
|
||||
creationInfo.MetaSize = 0;
|
||||
|
||||
return fileSystemProxy.Get.CreateSaveDataFileSystemWithCreationInfo2(in creationInfo).Ret();
|
||||
}
|
||||
|
||||
public static Result CreateSystemSaveData(this FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId,
|
||||
ulong ownerId, long size, long journalSize, SaveDataFlags flags, SaveDataFormatType formatType)
|
||||
{
|
||||
Result rc;
|
||||
Span<byte> logBuffer = stackalloc byte[0x180];
|
||||
|
||||
if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null))
|
||||
{
|
||||
Tick start = fs.Hos.Os.GetSystemTick();
|
||||
rc = CreateSystemSaveData(fs.Impl, spaceId, saveDataId, InvalidUserId, ownerId, size, journalSize,
|
||||
flags, formatType);
|
||||
Tick end = fs.Hos.Os.GetSystemTick();
|
||||
|
||||
var idString = new IdString();
|
||||
var sb = new U8StringBuilder(logBuffer, true);
|
||||
sb.Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId))
|
||||
.Append(LogSaveDataId).AppendFormat(saveDataId, 'X')
|
||||
.Append(LogUserId).AppendFormat(InvalidUserId.Id.High, 'X', 16).AppendFormat(InvalidUserId.Id.Low, 'X', 16)
|
||||
.Append(LogSaveDataOwnerId).AppendFormat(ownerId, 'X')
|
||||
.Append(LogSaveDataSize).AppendFormat(size, 'd')
|
||||
.Append(LogSaveDataJournalSize).AppendFormat(journalSize, 'd')
|
||||
.Append(LogSaveDataFlags).AppendFormat((int)flags, 'X', 8)
|
||||
.Append(LogSaveDataFormatType).Append(idString.ToString(formatType));
|
||||
|
||||
fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer));
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = CreateSystemSaveData(fs.Impl, spaceId, saveDataId, InvalidUserId, ownerId, size, journalSize,
|
||||
flags, formatType);
|
||||
}
|
||||
|
||||
fs.Impl.AbortIfNeeded(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
public static Result ExtendSaveData(this FileSystemClientImpl fs, SaveDataSpaceId spaceId, ulong saveDataId,
|
||||
long saveDataSize, long journalSize)
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue