diff --git a/src/LibHac/Fs/Shim/PosixTime.cs b/src/LibHac/Fs/Shim/PosixTime.cs
index dbb60a8c..22436ac0 100644
--- a/src/LibHac/Fs/Shim/PosixTime.cs
+++ b/src/LibHac/Fs/Shim/PosixTime.cs
@@ -3,6 +3,10 @@ using LibHac.FsSrv.Sf;
namespace LibHac.Fs.Shim;
+///
+/// Contains functions for setting the current time used by FS.
+///
+/// Based on nnSdk 13.4.0
public static class PosixTimeShim
{
public static Result SetCurrentPosixTime(this FileSystemClient fs, Time.PosixTime currentPosixTime,
@@ -15,4 +19,4 @@ public static class PosixTimeShim
fs.Impl.AbortIfNeeded(rc);
return rc;
}
-}
+}
\ No newline at end of file
diff --git a/src/LibHac/Fs/Shim/RightsId.cs b/src/LibHac/Fs/Shim/RightsId.cs
index 8796aa64..64d8639b 100644
--- a/src/LibHac/Fs/Shim/RightsId.cs
+++ b/src/LibHac/Fs/Shim/RightsId.cs
@@ -5,6 +5,10 @@ using LibHac.Spl;
namespace LibHac.Fs.Shim;
+///
+/// Contains functions for working with rights IDs and external keys for NCA encryption.
+///
+/// Based on nnSdk 13.4.0
public static class RightsIdShim
{
public static Result GetRightsId(this FileSystemClient fs, out RightsId rightsId, ProgramId programId,
@@ -85,4 +89,4 @@ public static class RightsIdShim
return Result.Success;
}
-}
+}
\ No newline at end of file
diff --git a/src/LibHac/Fs/Shim/SaveData.cs b/src/LibHac/Fs/Shim/SaveData.cs
index 9a11a4cd..3a9b387d 100644
--- a/src/LibHac/Fs/Shim/SaveData.cs
+++ b/src/LibHac/Fs/Shim/SaveData.cs
@@ -12,9 +12,34 @@ using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
namespace LibHac.Fs.Shim;
+///
+/// Contains functions for mounting save data and checking if save data already exists or not.
+///
+/// Based on nnSdk 13.4.0
[SkipLocalsInit]
public static class SaveData
{
+ private const long SaveDataSizeForDebug = 0x2000000;
+ private const long SaveDataJournalSizeForDebug = 0x2000000;
+
+ private static Result OpenSaveDataInternalStorageFileSystemImpl(FileSystemClient fs,
+ ref UniqueRef outFileSystem, SaveDataSpaceId spaceId, ulong saveDataId)
+ {
+ using SharedRef fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject();
+ using var fileSystem = new SharedRef();
+
+ Result rc = fileSystemProxy.Get.OpenSaveDataInternalStorageFileSystem(ref fileSystem.Ref(), spaceId,
+ saveDataId);
+ if (rc.IsFailure()) return rc.Miss();
+
+ using var fileSystemAdapter =
+ new UniqueRef(new FileSystemServiceObjectAdapter(ref fileSystem.Ref()));
+
+ outFileSystem.Set(ref fileSystemAdapter.Ref());
+
+ return Result.Success;
+ }
+
private static Result MountSaveDataImpl(this FileSystemClientImpl fs, U8Span mountName, SaveDataSpaceId spaceId,
ProgramId programId, UserId userId, SaveDataType type, bool openReadOnly, ushort index)
{
@@ -49,13 +74,82 @@ public static class SaveData
using var mountNameGenerator = new UniqueRef();
- rc = fs.Fs.Register(mountName, fileSystemAdapterRaw, ref fileSystemAdapter.Ref(),
- ref mountNameGenerator.Ref(), false, null, true);
+ rc = fs.Fs.Register(mountName, fileSystemAdapterRaw, ref fileSystemAdapter.Ref(), ref mountNameGenerator.Ref(),
+ false, null, true);
if (rc.IsFailure()) return rc.Miss();
return Result.Success;
}
+ public static Result EnsureSaveDataForDebug(this FileSystemClientImpl fs, UserId userId)
+ {
+ using SharedRef fileSystemProxy = fs.GetFileSystemProxyServiceObject();
+
+ Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, ProgramId.InvalidId, SaveDataType.Account,
+ UserId.InvalidId, 0);
+ if (rc.IsFailure()) return rc;
+
+ rc = SaveDataCreationInfo.Make(out SaveDataCreationInfo creationInfo, SaveDataSizeForDebug,
+ SaveDataJournalSizeForDebug, default, SaveDataFlags.None, SaveDataSpaceId.User);
+ if (rc.IsFailure()) return rc.Miss();
+
+ var metaInfo = new SaveDataMetaInfo
+ {
+ Type = SaveDataMetaType.None,
+ Size = 0
+ };
+
+ rc = fileSystemProxy.Get.CreateSaveDataFileSystem(in attribute, in creationInfo, in metaInfo);
+
+ if (rc.IsFailure())
+ {
+ // Return successfully if the save data already exists
+ if (ResultFs.PathAlreadyExists.Includes(rc))
+ rc.Catch();
+ else
+ return rc.Miss();
+ }
+
+ return Result.Success;
+ }
+
+ public static Result MountSaveData(this FileSystemClientImpl fs, U8Span mountName, UserId userId)
+ {
+ return MountSaveDataImpl(fs, mountName, SaveDataSpaceId.User, ProgramId.InvalidId, userId,
+ SaveDataType.Account, openReadOnly: false, index: 0);
+ }
+
+ public static Result MountSaveData(this FileSystemClient fs, U8Span mountName, UserId userId)
+ {
+ Result rc;
+ Span logBuffer = stackalloc byte[0x60];
+
+ if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.Application))
+ {
+ Tick start = fs.Hos.Os.GetSystemTick();
+ rc = MountSaveData(fs.Impl, mountName, UserId.InvalidId);
+ Tick end = fs.Hos.Os.GetSystemTick();
+
+ var sb = new U8StringBuilder(logBuffer, true);
+ sb.Append(LogName).Append(mountName).Append(LogQuote)
+ .Append(LogUserId).AppendFormat(userId.Id.High, 'X', 16).AppendFormat(userId.Id.Low, 'X', 16);
+
+ fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer));
+ }
+ else
+ {
+ rc = MountSaveData(fs.Impl, mountName, UserId.InvalidId);
+ }
+
+ fs.Impl.AbortIfNeeded(rc);
+ if (rc.IsFailure()) return rc;
+
+ if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.Application))
+ fs.Impl.EnableFileSystemAccessorAccessLog(mountName);
+
+ return rc;
+ }
+
public static Result MountSaveData(this FileSystemClient fs, U8Span mountName, Ncm.ApplicationId applicationId,
UserId userId)
{
@@ -126,6 +220,45 @@ public static class SaveData
return rc;
}
+ public static Result IsSaveDataExisting(this FileSystemClientImpl fs, out bool exists, UserId userId)
+ {
+ return IsSaveDataExisting(fs, out exists, default, userId);
+ }
+
+ public static Result IsSaveDataExisting(this FileSystemClientImpl fs, out bool exists,
+ Ncm.ApplicationId applicationId, UserId userId)
+ {
+ return IsSaveDataExisting(fs, out exists, default, SaveDataType.Account, userId);
+ }
+
+ public static Result IsSaveDataExisting(this FileSystemClientImpl fs, out bool exists,
+ Ncm.ApplicationId applicationId, SaveDataType type, UserId userId)
+ {
+ UnsafeHelpers.SkipParamInit(out exists);
+
+ using SharedRef fileSystemProxy = fs.GetFileSystemProxyServiceObject();
+
+ Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, applicationId, type, UserId.InvalidId, 0);
+ if (rc.IsFailure()) return rc.Miss();
+
+ using var fileSystem = new SharedRef();
+ rc = fileSystemProxy.Get.OpenSaveDataFileSystem(ref fileSystem.Ref(), SaveDataSpaceId.User, in attribute);
+
+ if (rc.IsSuccess() || ResultFs.TargetLocked.Includes(rc) || ResultFs.SaveDataExtending.Includes(rc))
+ {
+ exists = true;
+ return Result.Success;
+ }
+
+ if (ResultFs.TargetNotFound.Includes(rc))
+ {
+ exists = false;
+ return Result.Success;
+ }
+
+ return rc.Miss();
+ }
+
public static Result MountTemporaryStorage(this FileSystemClient fs, U8Span mountName)
{
Result rc;
@@ -195,6 +328,7 @@ public static class SaveData
Result rc;
Span logBuffer = stackalloc byte[0x40];
+
if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.Application))
{
Tick start = fs.Hos.Os.GetSystemTick();
@@ -223,8 +357,7 @@ public static class SaveData
return rc;
}
- public static Result MountCacheStorage(this FileSystemClient fs, U8Span mountName,
- Ncm.ApplicationId applicationId)
+ public static Result MountCacheStorage(this FileSystemClient fs, U8Span mountName, Ncm.ApplicationId applicationId)
{
Result rc;
Span logBuffer = stackalloc byte[0x50];
@@ -257,8 +390,8 @@ public static class SaveData
return rc;
}
- public static Result MountCacheStorage(this FileSystemClient fs, U8Span mountName,
- Ncm.ApplicationId applicationId, int index)
+ public static Result MountCacheStorage(this FileSystemClient fs, U8Span mountName, Ncm.ApplicationId applicationId,
+ int index)
{
Result rc;
Span logBuffer = stackalloc byte[0x60];
@@ -291,4 +424,37 @@ public static class SaveData
return rc;
}
+
+ public static Result OpenSaveDataInternalStorageFileSystem(this FileSystemClient fs,
+ ref UniqueRef outFileSystem, SaveDataSpaceId spaceId, ulong saveDataId)
+ {
+ Result rc = OpenSaveDataInternalStorageFileSystemImpl(fs, ref outFileSystem, spaceId, saveDataId);
+ fs.Impl.AbortIfNeeded(rc);
+ if (rc.IsFailure()) return rc.Miss();
+
+ return Result.Success;
+ }
+
+ public static Result MountSaveDataInternalStorage(this FileSystemClient fs, U8Span mountName,
+ SaveDataSpaceId spaceId, ulong saveDataId)
+ {
+ Result rc = Operate(fs, mountName, spaceId, saveDataId);
+
+ fs.Impl.AbortIfNeeded(rc);
+ if (rc.IsFailure()) return rc.Miss();
+
+ return Result.Success;
+
+ static Result Operate(FileSystemClient fs, U8Span mountName, SaveDataSpaceId spaceId, ulong saveDataId)
+ {
+ Result rc = fs.Impl.CheckMountName(mountName);
+ if (rc.IsFailure()) return rc.Miss();
+
+ using var fileSystem = new UniqueRef();
+ rc = OpenSaveDataInternalStorageFileSystemImpl(fs, ref fileSystem.Ref(), spaceId, saveDataId);
+ if (rc.IsFailure()) return rc.Miss();
+
+ return fs.Register(mountName, ref fileSystem.Ref());
+ }
+ }
}
\ No newline at end of file