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