diff --git a/build/CodeGen/results.csv b/build/CodeGen/results.csv index 9b30f760..ef973e5e 100644 --- a/build/CodeGen/results.csv +++ b/build/CodeGen/results.csv @@ -1070,7 +1070,7 @@ Module,DescriptionStart,DescriptionEnd,Flags,Namespace,Name,Summary 2,6397,,,,UnsupportedOperateRangeForRegionSwitchStorage, 2,6400,6449,,,PermissionDenied, -2,6403,,,,PermissionDeniedForCreateHostFileSystem,Returned when opening a host FS on a retail device. +2,6403,,,,HostFileSystemOperationDisabled,Returned when opening a host FS on a retail device. 2,6450,,,,PortAcceptableCountLimited, 2,6452,,,,NcaExternalKeyInconsistent, diff --git a/src/LibHac/Fs/ProgramIndexMapInfo.cs b/src/LibHac/Fs/ProgramIndexMapInfo.cs index 5d326151..692d43c3 100644 --- a/src/LibHac/Fs/ProgramIndexMapInfo.cs +++ b/src/LibHac/Fs/ProgramIndexMapInfo.cs @@ -6,7 +6,7 @@ namespace LibHac.Fs; public struct ProgramIndexMapInfo { public ProgramId ProgramId; - public ProgramId MainProgramId; + public Ncm.ApplicationId MainProgramId; public byte ProgramIndex; public Array15 Reserved; } \ No newline at end of file diff --git a/src/LibHac/Fs/ResultFs.cs b/src/LibHac/Fs/ResultFs.cs index 20ee475b..b5c576c7 100644 --- a/src/LibHac/Fs/ResultFs.cs +++ b/src/LibHac/Fs/ResultFs.cs @@ -1964,7 +1964,7 @@ public static class ResultFs /// Error code: 2002-6400; Range: 6400-6449; Inner value: 0x320002 public static Result.Base PermissionDenied { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 6400, 6449); } /// Returned when opening a host FS on a retail device.
Error code: 2002-6403; Inner value: 0x320602
- public static Result.Base PermissionDeniedForCreateHostFileSystem => new Result.Base(ModuleFs, 6403); + public static Result.Base HostFileSystemOperationDisabled => new Result.Base(ModuleFs, 6403); /// Error code: 2002-6450; Inner value: 0x326402 public static Result.Base PortAcceptableCountLimited => new Result.Base(ModuleFs, 6450); diff --git a/src/LibHac/Fs/Shim/Debug.cs b/src/LibHac/Fs/Shim/Debug.cs index be7307a7..e34323e3 100644 --- a/src/LibHac/Fs/Shim/Debug.cs +++ b/src/LibHac/Fs/Shim/Debug.cs @@ -7,7 +7,11 @@ namespace LibHac.Fs.Shim; public enum DebugOptionKey : uint { - SaveDataEncryption = 0x20454453 // "SDE " + SaveDataEncryption = 0x20454453, // "SDE " + SaveDataJournalMetaVerification = 0x20454453, // "JMV " + SaveDataRemapMetaVerification = 0x20454453, // "RMV " + SaveDataHashAlgorithm = 0x20454453, // "SDHA" + SaveDataHashSalt = 0x20454453 // "SDHS" } /// diff --git a/src/LibHac/FsSrv/FileSystemServerInitializer.cs b/src/LibHac/FsSrv/FileSystemServerInitializer.cs index 0e131516..107fa3e1 100644 --- a/src/LibHac/FsSrv/FileSystemServerInitializer.cs +++ b/src/LibHac/FsSrv/FileSystemServerInitializer.cs @@ -138,6 +138,7 @@ public static class FileSystemServerInitializer saveFsServiceConfig.IsPseudoSaveData = () => true; saveFsServiceConfig.SaveDataFileSystemCacheCount = 1; saveFsServiceConfig.SaveIndexerManager = saveDataIndexerManager; + saveFsServiceConfig.DebugConfigService = debugConfigurationService; saveFsServiceConfig.FsServer = server; var saveFsService = new SaveDataFileSystemServiceImpl(in saveFsServiceConfig); diff --git a/src/LibHac/FsSrv/FsCreator/ILocalFileSystemCreator.cs b/src/LibHac/FsSrv/FsCreator/ILocalFileSystemCreator.cs index 439af2dc..b8737308 100644 --- a/src/LibHac/FsSrv/FsCreator/ILocalFileSystemCreator.cs +++ b/src/LibHac/FsSrv/FsCreator/ILocalFileSystemCreator.cs @@ -1,9 +1,10 @@ -using LibHac.Fs.Fsa; +using LibHac.Common; +using LibHac.Fs; +using LibHac.Fs.Fsa; namespace LibHac.FsSrv.FsCreator; public interface ILocalFileSystemCreator { - Result Create(out IFileSystem fileSystem, bool someBool); - Result Create(out IFileSystem fileSystem, string path, bool openCaseSensitive); -} + Result Create(ref SharedRef outFileSystem, in Path rootPath, bool openCaseSensitive, bool ensureRootPathExists, Result pathNotFoundResult); +} \ No newline at end of file diff --git a/src/LibHac/FsSrv/FsCreator/ISaveDataFileSystemCreator.cs b/src/LibHac/FsSrv/FsCreator/ISaveDataFileSystemCreator.cs index be6a6c27..f46fe530 100644 --- a/src/LibHac/FsSrv/FsCreator/ISaveDataFileSystemCreator.cs +++ b/src/LibHac/FsSrv/FsCreator/ISaveDataFileSystemCreator.cs @@ -18,5 +18,8 @@ public interface ISaveDataFileSystemCreator Result CreateExtraDataAccessor(ref SharedRef outExtraDataAccessor, ref SharedRef baseFileSystem); - void SetSdCardEncryptionSeed(ReadOnlySpan seed); -} + Result IsDataEncrypted(out bool isEncrypted, ref SharedRef baseFileSystem, ulong saveDataId, + IBufferManager bufferManager, bool isDeviceUniqueMac, bool isReconstructible); + + void SetMacGenerationSeed(ReadOnlySpan seed); +} \ No newline at end of file diff --git a/src/LibHac/FsSrv/FsCreator/SaveDataFileSystemCreator.cs b/src/LibHac/FsSrv/FsCreator/SaveDataFileSystemCreator.cs index ca94f92d..577feb51 100644 --- a/src/LibHac/FsSrv/FsCreator/SaveDataFileSystemCreator.cs +++ b/src/LibHac/FsSrv/FsCreator/SaveDataFileSystemCreator.cs @@ -120,7 +120,13 @@ public class SaveDataFileSystemCreator : ISaveDataFileSystemCreator throw new NotImplementedException(); } - public void SetSdCardEncryptionSeed(ReadOnlySpan seed) + public Result IsDataEncrypted(out bool isEncrypted, ref SharedRef baseFileSystem, ulong saveDataId, + IBufferManager bufferManager, bool isDeviceUniqueMac, bool isReconstructible) + { + throw new NotImplementedException(); + } + + public void SetMacGenerationSeed(ReadOnlySpan seed) { throw new NotImplementedException(); } diff --git a/src/LibHac/FsSrv/SaveDataFileSystemServiceImpl.cs b/src/LibHac/FsSrv/SaveDataFileSystemServiceImpl.cs index d04a86d9..f176d54e 100644 --- a/src/LibHac/FsSrv/SaveDataFileSystemServiceImpl.cs +++ b/src/LibHac/FsSrv/SaveDataFileSystemServiceImpl.cs @@ -5,6 +5,7 @@ using LibHac.Common.FixedArrays; using LibHac.Diag; using LibHac.Fs; using LibHac.Fs.Fsa; +using LibHac.Fs.Shim; using LibHac.FsSrv.FsCreator; using LibHac.FsSrv.Impl; using LibHac.FsSystem; @@ -15,8 +16,15 @@ using Utility = LibHac.FsSrv.Impl.Utility; namespace LibHac.FsSrv; +/// +/// Handles the lower-level operations on save data. +/// uses this class to provide save data APIs at a higher level of abstraction. +/// +/// Based on FS 14.1.0 (nnSdk 14.3.0) public class SaveDataFileSystemServiceImpl : IDisposable { + private static readonly bool UseTargetManager = true; + private Configuration _config; private EncryptionSeed _encryptionSeed; @@ -59,6 +67,7 @@ public class SaveDataFileSystemServiceImpl : IDisposable public int SaveDataFileSystemCacheCount; public Func IsPseudoSaveData; public ISaveDataIndexerManager SaveIndexerManager; + public DebugConfigurationServiceImpl DebugConfigService; // LibHac additions public FileSystemServer FsServer; @@ -78,6 +87,11 @@ public class SaveDataFileSystemServiceImpl : IDisposable throw new NotImplementedException(); } + private static Result WipeMasterHeader(IFileSystem fileSystem, in Path filePath, RandomDataGenerator random) + { + throw new NotImplementedException(); + } + public SaveDataFileSystemServiceImpl(in Configuration configuration) { _config = configuration; @@ -96,6 +110,11 @@ public class SaveDataFileSystemServiceImpl : IDisposable _saveExtraDataCacheManager.Dispose(); } + public DebugConfigurationServiceImpl GetDebugConfigurationService() + { + return _config.DebugConfigService; + } + public Result DoesSaveDataEntityExist(out bool exists, SaveDataSpaceId spaceId, ulong saveDataId) { UnsafeHelpers.SkipParamInit(out exists); @@ -218,7 +237,7 @@ public class SaveDataFileSystemServiceImpl : IDisposable saveDataMetaIdDirectoryNameBuffer.Items, saveDataId); if (rc.IsFailure()) return rc.Miss(); - return OpenSaveDataDirectoryFileSystemImpl(ref outFileSystem, spaceId, in saveDataMetaIdDirectoryName); + return OpenSaveDataDirectoryFileSystemImpl(ref outFileSystem, spaceId, in saveDataMetaIdDirectoryName).Ret(); } public Result OpenSaveDataInternalStorageFileSystem(ref SharedRef outFileSystem, @@ -228,14 +247,14 @@ public class SaveDataFileSystemServiceImpl : IDisposable throw new NotImplementedException(); } - public Result ExtendSaveDataFileSystemCore(out long extendedTotalSize, ulong saveDataId, SaveDataSpaceId spaceId, - SaveDataType type, long dataSize, long journalSize, in Path saveDataRootPath, bool isExtensionStart) + private Result OpenSaveDataImageFile(ref UniqueRef outFile, SaveDataSpaceId spaceId, ulong saveDataId, + in Path saveDataRootPath) { throw new NotImplementedException(); } - private Result OpenSaveDataImageFile(ref UniqueRef outFile, SaveDataSpaceId spaceId, ulong saveDataId, - in Path saveDataRootPath) + public Result ExtendSaveDataFileSystemCore(out long extendedTotalSize, ulong saveDataId, SaveDataSpaceId spaceId, + SaveDataType type, long dataSize, long journalSize, in Path saveDataRootPath, bool isExtensionStart) { throw new NotImplementedException(); } @@ -330,8 +349,8 @@ public class SaveDataFileSystemServiceImpl : IDisposable ReadOnlySpan metaDirName = // /saveMeta new[] { - (byte)'/', (byte)'s', (byte)'a', (byte)'v', (byte)'e', (byte)'M', (byte)'e', (byte)'t', - (byte)'a' + (byte)'/', (byte)'s', (byte)'a', (byte)'v', (byte)'e', (byte)'M', (byte)'e', (byte)'t', + (byte)'a' }; Unsafe.SkipInit(out Array18 saveDataIdDirectoryNameBuffer); @@ -342,7 +361,8 @@ public class SaveDataFileSystemServiceImpl : IDisposable Result rc = PathFunctions.SetUpFixedPath(ref saveDataMetaDirectoryName.Ref(), metaDirName); if (rc.IsFailure()) return rc.Miss(); - rc = OpenSaveDataDirectoryFileSystemImpl(ref fileSystem.Ref(), spaceId, in saveDataMetaDirectoryName, false); + rc = OpenSaveDataDirectoryFileSystemImpl(ref fileSystem.Ref(), spaceId, in saveDataMetaDirectoryName, + createIfMissing: false); if (rc.IsFailure()) return rc.Miss(); using var saveDataIdDirectoryName = new Path(); @@ -355,8 +375,8 @@ public class SaveDataFileSystemServiceImpl : IDisposable if (rc.IsFailure()) { - if (!ResultFs.PathNotFound.Includes(rc)) - return rc.Catch().Handle(); + if (ResultFs.PathNotFound.Includes(rc)) + return Result.Success; return rc.Miss(); } @@ -461,69 +481,6 @@ public class SaveDataFileSystemServiceImpl : IDisposable } } - public Result CreateSaveDataFileSystem(ulong saveDataId, in SaveDataAttribute attribute, - in SaveDataCreationInfo creationInfo, in Path saveDataRootPath, in Optional hashSalt, - bool skipFormat) - { - // Use directory save data for now - - Unsafe.SkipInit(out Array18 saveImageNameBuffer); - - using var fileSystem = new SharedRef(); - - Result rc = OpenSaveDataDirectoryFileSystem(ref fileSystem.Ref(), creationInfo.SpaceId, - in saveDataRootPath, false); - if (rc.IsFailure()) return rc.Miss(); - - using var saveImageName = new Path(); - rc = PathFunctions.SetUpFixedPathSaveId(ref saveImageName.Ref(), saveImageNameBuffer.Items, saveDataId); - if (rc.IsFailure()) return rc.Miss(); - - if (_config.IsPseudoSaveData()) - { - rc = FsSystem.Utility.EnsureDirectory(fileSystem.Get, in saveImageName); - if (rc.IsFailure()) return rc.Miss(); - - using var saveFileSystem = new SharedRef(); - - bool isJournalingSupported = SaveDataProperties.IsJournalingSupported(attribute.Type); - bool isReconstructible = SaveDataProperties.IsReconstructible(attribute.Type, creationInfo.SpaceId); - - rc = _config.SaveFsCreator.Create(ref saveFileSystem.Ref(), ref fileSystem.Ref(), creationInfo.SpaceId, - saveDataId, allowDirectorySaveData: true, isDeviceUniqueMac: false, isJournalingSupported, - isMultiCommitSupported: false, openReadOnly: false, openShared: false, _timeStampGetter, - isReconstructible); - if (rc.IsFailure()) return rc.Miss(); - - var extraData = new SaveDataExtraData(); - extraData.Attribute = attribute; - extraData.OwnerId = creationInfo.OwnerId; - - rc = GetSaveDataCommitTimeStamp(out extraData.TimeStamp); - if (rc.IsFailure()) - extraData.TimeStamp = 0; - - extraData.CommitId = 0; - _config.GenerateRandomData(SpanHelpers.AsByteSpan(ref extraData.CommitId)); - - extraData.Flags = creationInfo.Flags; - extraData.DataSize = creationInfo.Size; - extraData.JournalSize = creationInfo.JournalSize; - - rc = saveFileSystem.Get.WriteExtraData(in extraData); - if (rc.IsFailure()) return rc.Miss(); - - rc = saveFileSystem.Get.CommitExtraData(true); - if (rc.IsFailure()) return rc.Miss(); - } - else - { - throw new NotImplementedException(); - } - - return Result.Success; - } - public Result DeleteSaveDataFileSystem(SaveDataSpaceId spaceId, ulong saveDataId, bool wipeSaveFile, in Path saveDataRootPath) { @@ -555,7 +512,27 @@ public class SaveDataFileSystemServiceImpl : IDisposable { if (wipeSaveFile) { - WipeData(fileSystem.Get, in saveImageName, _config.GenerateRandomData).IgnoreResult(); + // If we need to wipe the save file, check if the save is encrypted. If it is, we only wipe the master + // header because it contains the key data needed to decrypt the save. + bool isDataEncrypted = false; + if (GetDebugConfigurationService().Get(DebugOptionKey.SaveDataEncryption, 0) != 0) + { + using SharedRef tempFileSystem = SharedRef.CreateCopy(in fileSystem); + rc = _config.SaveFsCreator.IsDataEncrypted(out isDataEncrypted, ref tempFileSystem.Ref(), + saveDataId, _config.BufferManager, IsDeviceUniqueMac(spaceId), isReconstructible: false); + + if (rc.IsFailure()) + isDataEncrypted = false; + } + + if (isDataEncrypted) + { + WipeMasterHeader(fileSystem.Get, in saveImageName, _config.GenerateRandomData).IgnoreResult(); + } + else + { + WipeData(fileSystem.Get, in saveImageName, _config.GenerateRandomData).IgnoreResult(); + } } rc = fileSystem.Get.DeleteFile(in saveImageName); @@ -570,42 +547,38 @@ public class SaveDataFileSystemServiceImpl : IDisposable { UnsafeHelpers.SkipParamInit(out extraData); - // Nintendo returns blank extra data for directory save data. - // We've extended directory save data to store extra data so we don't need to do that. + // Emulated save data on a host device doesn't have extra data. + if (IsAllowedDirectorySaveData(spaceId, in saveDataRootPath)) + { + extraData = default; + return Result.Success; + } using UniqueLockRef scopedLockFsCache = _saveFileSystemCacheManager.GetScopedLock(); using UniqueLockRef scopedLockExtraDataCache = _saveExtraDataCacheManager.GetScopedLock(); + using var unusedSaveDataFs = new SharedRef(); using var extraDataAccessor = new SharedRef(); // Try to grab an extra data accessor for the requested save from the cache. Result rc = _saveExtraDataCacheManager.GetCache(ref extraDataAccessor.Ref(), spaceId, saveDataId); - if (rc.IsSuccess()) - { - // An extra data accessor was found in the cache. Read the extra data from it. - return extraDataAccessor.Get.ReadExtraData(out extraData); - } - - using var unusedSaveDataFs = new SharedRef(); - - // We won't actually use the returned save data FS. - // Opening the FS should cache an extra data accessor for it. - rc = OpenSaveDataFileSystem(ref unusedSaveDataFs.Ref(), spaceId, saveDataId, saveDataRootPath, - openReadOnly: true, type, cacheExtraData: true); - if (rc.IsFailure()) return rc.Miss(); - - // Try to grab an accessor from the cache again. - rc = _saveExtraDataCacheManager.GetCache(ref extraDataAccessor.Ref(), spaceId, saveDataId); - if (rc.IsFailure()) { - // No extra data accessor was registered for the requested save data. - // Return a blank extra data struct. - extraData = new SaveDataExtraData(); - return rc; + // Try to open the extra data accessor if it's not in the cache. + + // We won't actually use the returned save data FS. + // Opening the FS should cache an extra data accessor for it. + rc = OpenSaveDataFileSystem(ref unusedSaveDataFs.Ref(), spaceId, saveDataId, saveDataRootPath, + openReadOnly: true, type, cacheExtraData: true); + if (rc.IsFailure()) return rc.Miss(); + + // Try to grab an accessor from the cache again. + rc = _saveExtraDataCacheManager.GetCache(ref extraDataAccessor.Ref(), spaceId, saveDataId); + if (rc.IsFailure()) return rc.Miss(); } + // We successfully got an extra data accessor. Read the extra data from it. rc = extraDataAccessor.Get.ReadExtraData(out extraData); if (rc.IsFailure()) return rc.Miss(); @@ -615,12 +588,16 @@ public class SaveDataFileSystemServiceImpl : IDisposable public Result WriteSaveDataFileSystemExtraData(SaveDataSpaceId spaceId, ulong saveDataId, in SaveDataExtraData extraData, in Path saveDataRootPath, SaveDataType type, bool updateTimeStamp) { - // Nintendo does nothing when writing directory save data extra data. - // We've extended directory save data to store extra data so we don't return early. + // Emulated save data on a host device doesn't have extra data. + if (IsAllowedDirectorySaveData(spaceId, in saveDataRootPath)) + { + return Result.Success; + } using UniqueLockRef scopedLockFsCache = _saveFileSystemCacheManager.GetScopedLock(); using UniqueLockRef scopedLockExtraDataCache = _saveExtraDataCacheManager.GetScopedLock(); + using var unusedSaveDataFs = new SharedRef(); using var extraDataAccessor = new SharedRef(); // Try to grab an extra data accessor for the requested save from the cache. @@ -629,7 +606,6 @@ public class SaveDataFileSystemServiceImpl : IDisposable if (rc.IsFailure()) { // No accessor was found in the cache. Try to open one. - using var unusedSaveDataFs = new SharedRef(); // We won't actually use the returned save data FS. // Opening the FS should cache an extra data accessor for it. @@ -639,12 +615,7 @@ public class SaveDataFileSystemServiceImpl : IDisposable // Try to grab an accessor from the cache again. rc = _saveExtraDataCacheManager.GetCache(ref extraDataAccessor.Ref(), spaceId, saveDataId); - - if (rc.IsFailure()) - { - // No extra data accessor was registered for the requested save data, so don't do anything. - return Result.Success; - } + if (rc.IsFailure()) return rc.Miss(); } // We should have a valid accessor if we've reached this point. @@ -679,7 +650,7 @@ public class SaveDataFileSystemServiceImpl : IDisposable { using var rootPath = new Path(); - return OpenSaveDataDirectoryFileSystem(ref outFileSystem, spaceId, in rootPath, true); + return OpenSaveDataDirectoryFileSystem(ref outFileSystem, spaceId, in rootPath, allowEmulatedSave: true); } public Result OpenSaveDataDirectoryFileSystem(ref SharedRef outFileSystem, @@ -689,29 +660,39 @@ public class SaveDataFileSystemServiceImpl : IDisposable if (allowEmulatedSave && IsAllowedDirectorySaveData(spaceId, in saveDataRootPath)) { - using (var tmFileSystem = new SharedRef()) + if (UseTargetManager) { - // Ensure the target save data directory exists - rc = _config.TargetManagerFsCreator.Create(ref tmFileSystem.Ref(), in saveDataRootPath, - openCaseSensitive: false, ensureRootPathExists: true, ResultFs.SaveDataRootPathUnavailable.Value); + using (var tmFileSystem = new SharedRef()) + { + // Ensure the target save data directory exists + rc = _config.TargetManagerFsCreator.Create(ref tmFileSystem.Ref(), in saveDataRootPath, + openCaseSensitive: false, ensureRootPathExists: true, + ResultFs.SaveDataRootPathUnavailable.Value); + if (rc.IsFailure()) return rc.Miss(); + } + + using var path = new Path(); + rc = path.Initialize(in saveDataRootPath); + if (rc.IsFailure()) return rc.Miss(); + + rc = _config.TargetManagerFsCreator.NormalizeCaseOfPath(out bool isTargetFsCaseSensitive, ref path.Ref()); + if (rc.IsFailure()) return rc.Miss(); + + rc = _config.TargetManagerFsCreator.Create(ref outFileSystem, in path, isTargetFsCaseSensitive, + ensureRootPathExists: false, ResultFs.SaveDataRootPathUnavailable.Value); + if (rc.IsFailure()) return rc.Miss(); + } + else + { + rc = _config.LocalFsCreator.Create(ref outFileSystem, in saveDataRootPath, openCaseSensitive: true, + ensureRootPathExists: true, ResultFs.SaveDataRootPathUnavailable.Value); if (rc.IsFailure()) return rc.Miss(); } - - using var path = new Path(); - rc = path.Initialize(in saveDataRootPath); - if (rc.IsFailure()) return rc.Miss(); - - rc = _config.TargetManagerFsCreator.NormalizeCaseOfPath(out bool isTargetFsCaseSensitive, ref path.Ref()); - if (rc.IsFailure()) return rc.Miss(); - - rc = _config.TargetManagerFsCreator.Create(ref outFileSystem, in path, isTargetFsCaseSensitive, - ensureRootPathExists: false, ResultFs.SaveDataRootPathUnavailable.Value); - if (rc.IsFailure()) return rc.Miss(); return Result.Success; } - using var saveDataDirPath = new Path(); + using var saveDataAreaDirectoryName = new Path(); ReadOnlySpan saveDirName; if (spaceId == SaveDataSpaceId.Temporary) @@ -723,48 +704,60 @@ public class SaveDataFileSystemServiceImpl : IDisposable saveDirName = new[] { (byte)'/', (byte)'s', (byte)'a', (byte)'v', (byte)'e' }; // /save } - rc = PathFunctions.SetUpFixedPath(ref saveDataDirPath.Ref(), saveDirName); + rc = PathFunctions.SetUpFixedPath(ref saveDataAreaDirectoryName.Ref(), saveDirName); if (rc.IsFailure()) return rc.Miss(); - rc = OpenSaveDataDirectoryFileSystemImpl(ref outFileSystem, spaceId, in saveDataDirPath, true); + rc = OpenSaveDataDirectoryFileSystemImpl(ref outFileSystem, spaceId, in saveDataAreaDirectoryName); if (rc.IsFailure()) return rc.Miss(); return Result.Success; } public Result OpenSaveDataDirectoryFileSystemImpl(ref SharedRef outFileSystem, - SaveDataSpaceId spaceId, in Path basePath, bool createIfMissing) + SaveDataSpaceId spaceId, in Path directoryPath) + { + return OpenSaveDataDirectoryFileSystemImpl(ref outFileSystem, spaceId, in directoryPath, createIfMissing: true); + } + + public Result OpenSaveDataDirectoryFileSystemImpl(ref SharedRef outFileSystem, + SaveDataSpaceId spaceId, in Path directoryPath, bool createIfMissing) { using var baseFileSystem = new SharedRef(); - Result rc; - switch (spaceId) { case SaveDataSpaceId.System: - rc = _config.BaseFsService.OpenBisFileSystem(ref baseFileSystem.Ref(), BisPartitionId.System, true); + { + Result rc = _config.BaseFsService.OpenBisFileSystem(ref baseFileSystem.Ref(), BisPartitionId.System, + caseSensitive: true); if (rc.IsFailure()) return rc.Miss(); - rc = Utility.WrapSubDirectory(ref outFileSystem, ref baseFileSystem.Ref(), in basePath, + rc = Utility.WrapSubDirectory(ref outFileSystem, ref baseFileSystem.Ref(), in directoryPath, createIfMissing); if (rc.IsFailure()) return rc.Miss(); - return Result.Success; + break; + } case SaveDataSpaceId.User: case SaveDataSpaceId.Temporary: - rc = _config.BaseFsService.OpenBisFileSystem(ref baseFileSystem.Ref(), BisPartitionId.User, true); + { + Result rc = _config.BaseFsService.OpenBisFileSystem(ref baseFileSystem.Ref(), BisPartitionId.User, + caseSensitive: true); if (rc.IsFailure()) return rc.Miss(); - rc = Utility.WrapSubDirectory(ref outFileSystem, ref baseFileSystem.Ref(), in basePath, createIfMissing); + rc = Utility.WrapSubDirectory(ref outFileSystem, ref baseFileSystem.Ref(), in directoryPath, + createIfMissing); if (rc.IsFailure()) return rc.Miss(); - return Result.Success; + break; + } case SaveDataSpaceId.SdSystem: case SaveDataSpaceId.SdUser: { - rc = _config.BaseFsService.OpenSdCardProxyFileSystem(ref baseFileSystem.Ref(), true); + Result rc = _config.BaseFsService.OpenSdCardProxyFileSystem(ref baseFileSystem.Ref(), + openCaseSensitive: true); if (rc.IsFailure()) return rc.Miss(); Unsafe.SkipInit(out Array64 pathParentBuffer); @@ -775,11 +768,11 @@ public class SaveDataFileSystemServiceImpl : IDisposable if (rc.IsFailure()) return rc.Miss(); using var pathSdRoot = new Path(); - rc = pathSdRoot.Combine(in pathParent, in basePath); + rc = pathSdRoot.Combine(in pathParent, in directoryPath); if (rc.IsFailure()) return rc.Miss(); - using SharedRef tempFileSystem = - SharedRef.CreateMove(ref baseFileSystem.Ref()); + using SharedRef tempFileSystem = SharedRef.CreateMove(ref baseFileSystem.Ref()); + rc = Utility.WrapSubDirectory(ref baseFileSystem.Ref(), ref tempFileSystem.Ref(), in pathSdRoot, createIfMissing); if (rc.IsFailure()) return rc.Miss(); @@ -787,37 +780,40 @@ public class SaveDataFileSystemServiceImpl : IDisposable IEncryptedFileSystemCreator.KeyId.Save, in _encryptionSeed); if (rc.IsFailure()) return rc.Miss(); - return Result.Success; + break; } case SaveDataSpaceId.ProperSystem: - rc = _config.BaseFsService.OpenBisFileSystem(ref baseFileSystem.Ref(), - BisPartitionId.SystemProperPartition, true); + { + Result rc = _config.BaseFsService.OpenBisFileSystem(ref baseFileSystem.Ref(), + BisPartitionId.SystemProperPartition, caseSensitive: true); if (rc.IsFailure()) return rc.Miss(); - rc = Utility.WrapSubDirectory(ref outFileSystem, ref baseFileSystem.Ref(), in basePath, createIfMissing); + rc = Utility.WrapSubDirectory(ref outFileSystem, ref baseFileSystem.Ref(), in directoryPath, + createIfMissing); if (rc.IsFailure()) return rc.Miss(); - return Result.Success; + break; + } case SaveDataSpaceId.SafeMode: - rc = _config.BaseFsService.OpenBisFileSystem(ref baseFileSystem.Ref(), BisPartitionId.SafeMode, true); + { + Result rc = _config.BaseFsService.OpenBisFileSystem(ref baseFileSystem.Ref(), BisPartitionId.SafeMode, + caseSensitive: true); if (rc.IsFailure()) return rc.Miss(); - rc = Utility.WrapSubDirectory(ref outFileSystem, ref baseFileSystem.Ref(), in basePath, createIfMissing); + rc = Utility.WrapSubDirectory(ref outFileSystem, ref baseFileSystem.Ref(), in directoryPath, + createIfMissing); if (rc.IsFailure()) return rc.Miss(); - return Result.Success; + break; + } default: return ResultFs.InvalidArgument.Log(); } - } - public Result OpenSaveDataDirectoryFileSystemImpl(ref SharedRef outFileSystem, - SaveDataSpaceId spaceId, in Path basePath) - { - return OpenSaveDataDirectoryFileSystemImpl(ref outFileSystem, spaceId, in basePath, true); + return Result.Success; } public Result IsProvisionallyCommittedSaveData(out bool isProvisionallyCommitted, in SaveDataInfo saveInfo) @@ -837,7 +833,7 @@ public class SaveDataFileSystemServiceImpl : IDisposable { _encryptionSeed = seed; - _config.SaveFsCreator.SetSdCardEncryptionSeed(seed.Value); + _config.SaveFsCreator.SetMacGenerationSeed(seed.Value); _config.SaveIndexerManager.InvalidateIndexer(SaveDataSpaceId.SdSystem); _config.SaveIndexerManager.InvalidateIndexer(SaveDataSpaceId.SdUser); @@ -855,7 +851,7 @@ public class SaveDataFileSystemServiceImpl : IDisposable } /// - /// Gets the program ID of the save data associated with the specified programID. + /// Gets the program ID of the save data associated with the specified program ID. /// /// In a standard application the program ID will be the same as the input program ID. /// In multi-program applications all sub-programs use the program ID of the main program @@ -864,15 +860,15 @@ public class SaveDataFileSystemServiceImpl : IDisposable /// The program ID of the save data. public ProgramId ResolveDefaultSaveDataReferenceProgramId(ProgramId programId) { - // First check if there's an entry in the program index map with the program ID and program index 0 - ProgramId mainProgramId = _config.ProgramRegistryService.GetProgramIdByIndex(programId, 0); + // First check if the program ID is part of a multi-program application that contains a program with index 0. + ProgramId mainProgramId = _config.ProgramRegistryService.GetProgramIdByIndex(programId, programIndex: 0); if (mainProgramId != ProgramId.InvalidId) { return mainProgramId; } - // Check if there's an entry with the program ID, ignoring program index + // Get the current program's map info and return the main program ID. Optional mapInfo = _config.ProgramRegistryService.GetProgramIndexMapInfo(programId); if (mapInfo.HasValue) @@ -894,7 +890,6 @@ public class SaveDataFileSystemServiceImpl : IDisposable UnsafeHelpers.SkipParamInit(out count); using var accessor = new UniqueRef(); - Result rc = OpenSaveDataIndexerAccessor(ref accessor.Ref(), out bool _, SaveDataSpaceId.User); if (rc.IsFailure()) return rc.Miss(); @@ -905,7 +900,7 @@ public class SaveDataFileSystemServiceImpl : IDisposable public Result OpenSaveDataIndexerAccessor(ref UniqueRef outAccessor, out bool isInitialOpen, SaveDataSpaceId spaceId) { - return _config.SaveIndexerManager.OpenSaveDataIndexerAccessor(ref outAccessor, out isInitialOpen, spaceId); + return _config.SaveIndexerManager.OpenSaveDataIndexerAccessor(ref outAccessor, out isInitialOpen, spaceId).Ret(); } public void ResetTemporaryStorageIndexer() diff --git a/tests/LibHac.Tests/Fs/FileSystemClientTests/ShimTests/SaveDataManagement.cs b/tests/LibHac.Tests/Fs/FileSystemClientTests/ShimTests/SaveDataManagement.cs index 2b8af33a..a3cf4783 100644 --- a/tests/LibHac.Tests/Fs/FileSystemClientTests/ShimTests/SaveDataManagement.cs +++ b/tests/LibHac.Tests/Fs/FileSystemClientTests/ShimTests/SaveDataManagement.cs @@ -307,7 +307,7 @@ public class SaveDataManagement Span mapInfo = stackalloc ProgramIndexMapInfo[5]; - var mainProgramId = new ProgramId(0x123456); + var mainProgramId = new Ncm.ApplicationId(0x123456); var programId = new ProgramId(mainProgramId.Value + 2); for (int i = 0; i < mapInfo.Length; i++) diff --git a/tests/LibHac.Tests/FsSrv/ProgramIndexMapInfoTests.cs b/tests/LibHac.Tests/FsSrv/ProgramIndexMapInfoTests.cs index afa913b6..3489a08b 100644 --- a/tests/LibHac.Tests/FsSrv/ProgramIndexMapInfoTests.cs +++ b/tests/LibHac.Tests/FsSrv/ProgramIndexMapInfoTests.cs @@ -34,7 +34,7 @@ public class ProgramIndexMapInfoTests for (int i = 0; i < map.Length; i++) { - map[i].MainProgramId = new ProgramId(1); + map[i].MainProgramId = new Ncm.ApplicationId(1); map[i].ProgramId = new ProgramId((ulong)(i + 1)); map[i].ProgramIndex = (byte)i; } @@ -60,7 +60,7 @@ public class ProgramIndexMapInfoTests for (int i = 0; i < map.Length; i++) { - map[i].MainProgramId = new ProgramId((ulong)idCreator(0)); + map[i].MainProgramId = new Ncm.ApplicationId((ulong)idCreator(0)); map[i].ProgramId = new ProgramId((ulong)idCreator(i)); map[i].ProgramIndex = (byte)i; }