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)
|
public ReadOnlySpan<byte> ToString(ContentType value)
|
||||||
{
|
{
|
||||||
switch (value)
|
switch (value)
|
||||||
|
@ -1083,6 +1093,16 @@ namespace LibHac.Fs.Impl
|
||||||
(byte)'d', (byte)':', (byte)' '
|
(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>
|
/// <summary>"<c>, save_data_time_stamp: </c>"</summary>
|
||||||
public static ReadOnlySpan<byte> LogSaveDataTimeStamp => // ", save_data_time_stamp: "
|
public static ReadOnlySpan<byte> LogSaveDataTimeStamp => // ", save_data_time_stamp: "
|
||||||
new[]
|
new[]
|
||||||
|
|
|
@ -6,7 +6,7 @@ using LibHac.Fs.Impl;
|
||||||
using LibHac.FsSrv.Sf;
|
using LibHac.FsSrv.Sf;
|
||||||
using LibHac.Ncm;
|
using LibHac.Ncm;
|
||||||
using LibHac.Os;
|
using LibHac.Os;
|
||||||
|
using LibHac.Util;
|
||||||
using static LibHac.Fs.Impl.AccessLogStrings;
|
using static LibHac.Fs.Impl.AccessLogStrings;
|
||||||
using static LibHac.Fs.SaveData;
|
using static LibHac.Fs.SaveData;
|
||||||
using IFileSystem = LibHac.Fs.Fsa.IFileSystem;
|
using IFileSystem = LibHac.Fs.Fsa.IFileSystem;
|
||||||
|
@ -17,12 +17,12 @@ namespace LibHac.Fs.Shim;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Contains functions for mounting save data and checking if save data already exists or not.
|
/// Contains functions for mounting save data and checking if save data already exists or not.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>Based on nnSdk 13.4.0</remarks>
|
/// <remarks>Based on nnSdk 14.3.0</remarks>
|
||||||
[SkipLocalsInit]
|
[SkipLocalsInit]
|
||||||
public static class SaveData
|
public static class SaveData
|
||||||
{
|
{
|
||||||
private const long SaveDataSizeForDebug = 0x2000000;
|
private const long SaveDataTotalSizeMax = 0xFA000000;
|
||||||
private const long SaveDataJournalSizeForDebug = 0x2000000;
|
private const int SaveDataBlockSize = 0x4000;
|
||||||
|
|
||||||
private static Result OpenSaveDataInternalStorageFileSystemImpl(FileSystemClient fs,
|
private static Result OpenSaveDataInternalStorageFileSystemImpl(FileSystemClient fs,
|
||||||
ref UniqueRef<IFileSystem> outFileSystem, SaveDataSpaceId spaceId, ulong saveDataId)
|
ref UniqueRef<IFileSystem> outFileSystem, SaveDataSpaceId spaceId, ulong saveDataId)
|
||||||
|
@ -42,6 +42,39 @@ public static class SaveData
|
||||||
return Result.Success;
|
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,
|
private static Result MountSaveDataImpl(this FileSystemClientImpl fs, U8Span mountName, SaveDataSpaceId spaceId,
|
||||||
ProgramId programId, UserId userId, SaveDataType type, bool openReadOnly, ushort index)
|
ProgramId programId, UserId userId, SaveDataType type, bool openReadOnly, ushort index)
|
||||||
{
|
{
|
||||||
|
@ -84,16 +117,26 @@ public static class SaveData
|
||||||
return Result.Success;
|
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();
|
using SharedRef<IFileSystemProxy> fileSystemProxy = fs.GetFileSystemProxyServiceObject();
|
||||||
|
|
||||||
Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, ProgramId.InvalidId, SaveDataType.Account,
|
Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, InvalidProgramId, SaveDataType.Account,
|
||||||
InvalidUserId, InvalidSystemSaveDataId);
|
userId, InvalidSystemSaveDataId);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
rc = SaveDataCreationInfo.Make(out SaveDataCreationInfo creationInfo, SaveDataSizeForDebug,
|
rc = SaveDataCreationInfo.Make(out SaveDataCreationInfo creationInfo, saveDataSize, saveDataJournalSize,
|
||||||
SaveDataJournalSizeForDebug, default, SaveDataFlags.None, SaveDataSpaceId.User);
|
ownerId: 0, SaveDataFlags.None, SaveDataSpaceId.User);
|
||||||
if (rc.IsFailure()) return rc.Miss();
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
var metaInfo = new SaveDataMetaInfo
|
var metaInfo = new SaveDataMetaInfo
|
||||||
|
@ -106,17 +149,25 @@ public static class SaveData
|
||||||
|
|
||||||
if (rc.IsFailure())
|
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))
|
if (ResultFs.PathAlreadyExists.Includes(rc))
|
||||||
rc.Catch();
|
{
|
||||||
|
if (extendIfNeeded)
|
||||||
|
{
|
||||||
|
rc = ExtendSaveDataIfNeeded(fs.Fs, userId, saveDataSize, saveDataJournalSize);
|
||||||
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
return rc.Miss();
|
return rc.Miss();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return Result.Success;
|
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,
|
return MountSaveDataImpl(fs, mountName, SaveDataSpaceId.User, ProgramId.InvalidId, userId,
|
||||||
SaveDataType.Account, openReadOnly: false, index: 0);
|
SaveDataType.Account, openReadOnly: false, index: 0);
|
||||||
|
@ -130,7 +181,7 @@ public static class SaveData
|
||||||
if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.Application))
|
if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.Application))
|
||||||
{
|
{
|
||||||
Tick start = fs.Hos.Os.GetSystemTick();
|
Tick start = fs.Hos.Os.GetSystemTick();
|
||||||
rc = MountSaveData(fs.Impl, mountName, InvalidUserId);
|
rc = MountSaveDataImpl(fs.Impl, mountName, InvalidUserId);
|
||||||
Tick end = fs.Hos.Os.GetSystemTick();
|
Tick end = fs.Hos.Os.GetSystemTick();
|
||||||
|
|
||||||
var sb = new U8StringBuilder(logBuffer, true);
|
var sb = new U8StringBuilder(logBuffer, true);
|
||||||
|
@ -141,7 +192,7 @@ public static class SaveData
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
rc = MountSaveData(fs.Impl, mountName, InvalidUserId);
|
rc = MountSaveDataImpl(fs.Impl, mountName, InvalidUserId);
|
||||||
}
|
}
|
||||||
|
|
||||||
fs.Impl.AbortIfNeeded(rc);
|
fs.Impl.AbortIfNeeded(rc);
|
||||||
|
@ -150,7 +201,7 @@ public static class SaveData
|
||||||
if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.Application))
|
if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.Application))
|
||||||
fs.Impl.EnableFileSystemAccessorAccessLog(mountName);
|
fs.Impl.EnableFileSystemAccessorAccessLog(mountName);
|
||||||
|
|
||||||
return rc;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Result MountSaveData(this FileSystemClient fs, U8Span mountName, Ncm.ApplicationId applicationId,
|
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))
|
if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.Application))
|
||||||
fs.Impl.EnableFileSystemAccessorAccessLog(mountName);
|
fs.Impl.EnableFileSystemAccessorAccessLog(mountName);
|
||||||
|
|
||||||
return rc;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Result MountSaveDataReadOnly(this FileSystemClient fs, U8Span mountName,
|
public static Result MountSaveDataReadOnly(this FileSystemClient fs, U8Span mountName,
|
||||||
|
@ -220,7 +271,7 @@ public static class SaveData
|
||||||
if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.Application))
|
if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.Application))
|
||||||
fs.Impl.EnableFileSystemAccessorAccessLog(mountName);
|
fs.Impl.EnableFileSystemAccessorAccessLog(mountName);
|
||||||
|
|
||||||
return rc;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Result IsSaveDataExisting(this FileSystemClientImpl fs, out bool exists, UserId userId)
|
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))
|
if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.Application))
|
||||||
fs.Impl.EnableFileSystemAccessorAccessLog(mountName);
|
fs.Impl.EnableFileSystemAccessorAccessLog(mountName);
|
||||||
|
|
||||||
return rc;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Result MountCacheStorage(this FileSystemClient fs, U8Span mountName)
|
public static Result MountCacheStorage(this FileSystemClient fs, U8Span mountName)
|
||||||
|
@ -324,7 +375,7 @@ public static class SaveData
|
||||||
if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.Application))
|
if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.Application))
|
||||||
fs.Impl.EnableFileSystemAccessorAccessLog(mountName);
|
fs.Impl.EnableFileSystemAccessorAccessLog(mountName);
|
||||||
|
|
||||||
return rc;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Result MountCacheStorage(this FileSystemClient fs, U8Span mountName, int index)
|
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))
|
if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.Application))
|
||||||
fs.Impl.EnableFileSystemAccessorAccessLog(mountName);
|
fs.Impl.EnableFileSystemAccessorAccessLog(mountName);
|
||||||
|
|
||||||
return rc;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Result MountCacheStorage(this FileSystemClient fs, U8Span mountName, Ncm.ApplicationId applicationId)
|
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))
|
if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System))
|
||||||
fs.Impl.EnableFileSystemAccessorAccessLog(mountName);
|
fs.Impl.EnableFileSystemAccessorAccessLog(mountName);
|
||||||
|
|
||||||
return rc;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Result MountCacheStorage(this FileSystemClient fs, U8Span mountName, Ncm.ApplicationId applicationId,
|
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))
|
if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System))
|
||||||
fs.Impl.EnableFileSystemAccessorAccessLog(mountName);
|
fs.Impl.EnableFileSystemAccessorAccessLog(mountName);
|
||||||
|
|
||||||
return rc;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Result OpenSaveDataInternalStorageFileSystem(this FileSystemClient fs,
|
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>
|
/// <summary>
|
||||||
/// Allows iterating through the <see cref="SaveDataInfo"/> of a list of save data.
|
/// Allows iterating through the <see cref="SaveDataInfo"/> of a list of save data.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>Based on nnSdk 13.4.0</remarks>
|
/// <remarks>Based on nnSdk 14.3.0</remarks>
|
||||||
public class SaveDataIterator : IDisposable
|
public class SaveDataIterator : IDisposable
|
||||||
{
|
{
|
||||||
private readonly FileSystemClient _fsClient;
|
private readonly FileSystemClient _fsClient;
|
||||||
|
@ -81,10 +81,12 @@ namespace LibHac.Fs.Shim
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Contains functions for creating, deleting, and otherwise managing save data.
|
/// Contains functions for creating, deleting, and otherwise managing save data.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>Based on nnSdk 13.4.0</remarks>
|
/// <remarks>Based on nnSdk 14.3.0</remarks>
|
||||||
[SkipLocalsInit]
|
[SkipLocalsInit]
|
||||||
public static class SaveDataManagement
|
public static class SaveDataManagement
|
||||||
{
|
{
|
||||||
|
private const int SaveDataBlockSize = 0x4000;
|
||||||
|
|
||||||
private class CacheStorageListCache : IDisposable
|
private class CacheStorageListCache : IDisposable
|
||||||
{
|
{
|
||||||
public readonly struct CacheEntry
|
public readonly struct CacheEntry
|
||||||
|
@ -990,6 +992,65 @@ namespace LibHac.Fs.Shim
|
||||||
return CreateSystemSaveData(fs, spaceId, saveDataId, InvalidUserId, ownerId, size, journalSize, flags);
|
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,
|
public static Result ExtendSaveData(this FileSystemClientImpl fs, SaveDataSpaceId spaceId, ulong saveDataId,
|
||||||
long saveDataSize, long journalSize)
|
long saveDataSize, long journalSize)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue