Update SaveDataFileSystemServiceImpl for 14.0.0

This commit is contained in:
Alex Barney 2022-04-28 19:16:24 -07:00
parent 7b44441ff2
commit 0875f5950c
11 changed files with 182 additions and 172 deletions

View file

@ -1070,7 +1070,7 @@ Module,DescriptionStart,DescriptionEnd,Flags,Namespace,Name,Summary
2,6397,,,,UnsupportedOperateRangeForRegionSwitchStorage, 2,6397,,,,UnsupportedOperateRangeForRegionSwitchStorage,
2,6400,6449,,,PermissionDenied, 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,6450,,,,PortAcceptableCountLimited,
2,6452,,,,NcaExternalKeyInconsistent, 2,6452,,,,NcaExternalKeyInconsistent,

1 Module DescriptionStart DescriptionEnd Flags Namespace Name Summary
1070 3 511 SessionClosedForReply
1071 3 512 ReceiveListBroken
1072 4 9 InvalidHandle
1073 4 2001 InvalidArgument
1074 4 2003 InvalidServerHandle
1075 4 2014 InvalidSize
1076 4 2021 Cancelled

View file

@ -6,7 +6,7 @@ namespace LibHac.Fs;
public struct ProgramIndexMapInfo public struct ProgramIndexMapInfo
{ {
public ProgramId ProgramId; public ProgramId ProgramId;
public ProgramId MainProgramId; public Ncm.ApplicationId MainProgramId;
public byte ProgramIndex; public byte ProgramIndex;
public Array15<byte> Reserved; public Array15<byte> Reserved;
} }

View file

@ -1964,7 +1964,7 @@ public static class ResultFs
/// <summary>Error code: 2002-6400; Range: 6400-6449; Inner value: 0x320002</summary> /// <summary>Error code: 2002-6400; Range: 6400-6449; Inner value: 0x320002</summary>
public static Result.Base PermissionDenied { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 6400, 6449); } public static Result.Base PermissionDenied { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 6400, 6449); }
/// <summary>Returned when opening a host FS on a retail device.<br/>Error code: 2002-6403; Inner value: 0x320602</summary> /// <summary>Returned when opening a host FS on a retail device.<br/>Error code: 2002-6403; Inner value: 0x320602</summary>
public static Result.Base PermissionDeniedForCreateHostFileSystem => new Result.Base(ModuleFs, 6403); public static Result.Base HostFileSystemOperationDisabled => new Result.Base(ModuleFs, 6403);
/// <summary>Error code: 2002-6450; Inner value: 0x326402</summary> /// <summary>Error code: 2002-6450; Inner value: 0x326402</summary>
public static Result.Base PortAcceptableCountLimited => new Result.Base(ModuleFs, 6450); public static Result.Base PortAcceptableCountLimited => new Result.Base(ModuleFs, 6450);

View file

@ -7,7 +7,11 @@ namespace LibHac.Fs.Shim;
public enum DebugOptionKey : uint public enum DebugOptionKey : uint
{ {
SaveDataEncryption = 0x20454453 // "SDE " SaveDataEncryption = 0x20454453, // "SDE "
SaveDataJournalMetaVerification = 0x20454453, // "JMV "
SaveDataRemapMetaVerification = 0x20454453, // "RMV "
SaveDataHashAlgorithm = 0x20454453, // "SDHA"
SaveDataHashSalt = 0x20454453 // "SDHS"
} }
/// <summary> /// <summary>

View file

@ -138,6 +138,7 @@ public static class FileSystemServerInitializer
saveFsServiceConfig.IsPseudoSaveData = () => true; saveFsServiceConfig.IsPseudoSaveData = () => true;
saveFsServiceConfig.SaveDataFileSystemCacheCount = 1; saveFsServiceConfig.SaveDataFileSystemCacheCount = 1;
saveFsServiceConfig.SaveIndexerManager = saveDataIndexerManager; saveFsServiceConfig.SaveIndexerManager = saveDataIndexerManager;
saveFsServiceConfig.DebugConfigService = debugConfigurationService;
saveFsServiceConfig.FsServer = server; saveFsServiceConfig.FsServer = server;
var saveFsService = new SaveDataFileSystemServiceImpl(in saveFsServiceConfig); var saveFsService = new SaveDataFileSystemServiceImpl(in saveFsServiceConfig);

View file

@ -1,9 +1,10 @@
using LibHac.Fs.Fsa; using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Fsa;
namespace LibHac.FsSrv.FsCreator; namespace LibHac.FsSrv.FsCreator;
public interface ILocalFileSystemCreator public interface ILocalFileSystemCreator
{ {
Result Create(out IFileSystem fileSystem, bool someBool); Result Create(ref SharedRef<IFileSystem> outFileSystem, in Path rootPath, bool openCaseSensitive, bool ensureRootPathExists, Result pathNotFoundResult);
Result Create(out IFileSystem fileSystem, string path, bool openCaseSensitive);
} }

View file

@ -18,5 +18,8 @@ public interface ISaveDataFileSystemCreator
Result CreateExtraDataAccessor(ref SharedRef<ISaveDataExtraDataAccessor> outExtraDataAccessor, Result CreateExtraDataAccessor(ref SharedRef<ISaveDataExtraDataAccessor> outExtraDataAccessor,
ref SharedRef<IFileSystem> baseFileSystem); ref SharedRef<IFileSystem> baseFileSystem);
void SetSdCardEncryptionSeed(ReadOnlySpan<byte> seed); Result IsDataEncrypted(out bool isEncrypted, ref SharedRef<IFileSystem> baseFileSystem, ulong saveDataId,
IBufferManager bufferManager, bool isDeviceUniqueMac, bool isReconstructible);
void SetMacGenerationSeed(ReadOnlySpan<byte> seed);
} }

View file

@ -120,7 +120,13 @@ public class SaveDataFileSystemCreator : ISaveDataFileSystemCreator
throw new NotImplementedException(); throw new NotImplementedException();
} }
public void SetSdCardEncryptionSeed(ReadOnlySpan<byte> seed) public Result IsDataEncrypted(out bool isEncrypted, ref SharedRef<IFileSystem> baseFileSystem, ulong saveDataId,
IBufferManager bufferManager, bool isDeviceUniqueMac, bool isReconstructible)
{
throw new NotImplementedException();
}
public void SetMacGenerationSeed(ReadOnlySpan<byte> seed)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }

View file

@ -5,6 +5,7 @@ using LibHac.Common.FixedArrays;
using LibHac.Diag; using LibHac.Diag;
using LibHac.Fs; using LibHac.Fs;
using LibHac.Fs.Fsa; using LibHac.Fs.Fsa;
using LibHac.Fs.Shim;
using LibHac.FsSrv.FsCreator; using LibHac.FsSrv.FsCreator;
using LibHac.FsSrv.Impl; using LibHac.FsSrv.Impl;
using LibHac.FsSystem; using LibHac.FsSystem;
@ -15,8 +16,15 @@ using Utility = LibHac.FsSrv.Impl.Utility;
namespace LibHac.FsSrv; namespace LibHac.FsSrv;
/// <summary>
/// Handles the lower-level operations on save data.
/// <see cref="SaveDataFileSystemService"/> uses this class to provide save data APIs at a higher level of abstraction.
/// </summary>
/// <remarks>Based on FS 14.1.0 (nnSdk 14.3.0)</remarks>
public class SaveDataFileSystemServiceImpl : IDisposable public class SaveDataFileSystemServiceImpl : IDisposable
{ {
private static readonly bool UseTargetManager = true;
private Configuration _config; private Configuration _config;
private EncryptionSeed _encryptionSeed; private EncryptionSeed _encryptionSeed;
@ -59,6 +67,7 @@ public class SaveDataFileSystemServiceImpl : IDisposable
public int SaveDataFileSystemCacheCount; public int SaveDataFileSystemCacheCount;
public Func<bool> IsPseudoSaveData; public Func<bool> IsPseudoSaveData;
public ISaveDataIndexerManager SaveIndexerManager; public ISaveDataIndexerManager SaveIndexerManager;
public DebugConfigurationServiceImpl DebugConfigService;
// LibHac additions // LibHac additions
public FileSystemServer FsServer; public FileSystemServer FsServer;
@ -78,6 +87,11 @@ public class SaveDataFileSystemServiceImpl : IDisposable
throw new NotImplementedException(); throw new NotImplementedException();
} }
private static Result WipeMasterHeader(IFileSystem fileSystem, in Path filePath, RandomDataGenerator random)
{
throw new NotImplementedException();
}
public SaveDataFileSystemServiceImpl(in Configuration configuration) public SaveDataFileSystemServiceImpl(in Configuration configuration)
{ {
_config = configuration; _config = configuration;
@ -96,6 +110,11 @@ public class SaveDataFileSystemServiceImpl : IDisposable
_saveExtraDataCacheManager.Dispose(); _saveExtraDataCacheManager.Dispose();
} }
public DebugConfigurationServiceImpl GetDebugConfigurationService()
{
return _config.DebugConfigService;
}
public Result DoesSaveDataEntityExist(out bool exists, SaveDataSpaceId spaceId, ulong saveDataId) public Result DoesSaveDataEntityExist(out bool exists, SaveDataSpaceId spaceId, ulong saveDataId)
{ {
UnsafeHelpers.SkipParamInit(out exists); UnsafeHelpers.SkipParamInit(out exists);
@ -218,7 +237,7 @@ public class SaveDataFileSystemServiceImpl : IDisposable
saveDataMetaIdDirectoryNameBuffer.Items, saveDataId); saveDataMetaIdDirectoryNameBuffer.Items, saveDataId);
if (rc.IsFailure()) return rc.Miss(); 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<IFileSystem> outFileSystem, public Result OpenSaveDataInternalStorageFileSystem(ref SharedRef<IFileSystem> outFileSystem,
@ -228,14 +247,14 @@ public class SaveDataFileSystemServiceImpl : IDisposable
throw new NotImplementedException(); throw new NotImplementedException();
} }
public Result ExtendSaveDataFileSystemCore(out long extendedTotalSize, ulong saveDataId, SaveDataSpaceId spaceId, private Result OpenSaveDataImageFile(ref UniqueRef<IFile> outFile, SaveDataSpaceId spaceId, ulong saveDataId,
SaveDataType type, long dataSize, long journalSize, in Path saveDataRootPath, bool isExtensionStart) in Path saveDataRootPath)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
private Result OpenSaveDataImageFile(ref UniqueRef<IFile> outFile, SaveDataSpaceId spaceId, ulong saveDataId, public Result ExtendSaveDataFileSystemCore(out long extendedTotalSize, ulong saveDataId, SaveDataSpaceId spaceId,
in Path saveDataRootPath) SaveDataType type, long dataSize, long journalSize, in Path saveDataRootPath, bool isExtensionStart)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
@ -330,8 +349,8 @@ public class SaveDataFileSystemServiceImpl : IDisposable
ReadOnlySpan<byte> metaDirName = // /saveMeta ReadOnlySpan<byte> metaDirName = // /saveMeta
new[] new[]
{ {
(byte)'/', (byte)'s', (byte)'a', (byte)'v', (byte)'e', (byte)'M', (byte)'e', (byte)'t', (byte)'/', (byte)'s', (byte)'a', (byte)'v', (byte)'e', (byte)'M', (byte)'e', (byte)'t',
(byte)'a' (byte)'a'
}; };
Unsafe.SkipInit(out Array18<byte> saveDataIdDirectoryNameBuffer); Unsafe.SkipInit(out Array18<byte> saveDataIdDirectoryNameBuffer);
@ -342,7 +361,8 @@ public class SaveDataFileSystemServiceImpl : IDisposable
Result rc = PathFunctions.SetUpFixedPath(ref saveDataMetaDirectoryName.Ref(), metaDirName); Result rc = PathFunctions.SetUpFixedPath(ref saveDataMetaDirectoryName.Ref(), metaDirName);
if (rc.IsFailure()) return rc.Miss(); 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(); if (rc.IsFailure()) return rc.Miss();
using var saveDataIdDirectoryName = new Path(); using var saveDataIdDirectoryName = new Path();
@ -355,8 +375,8 @@ public class SaveDataFileSystemServiceImpl : IDisposable
if (rc.IsFailure()) if (rc.IsFailure())
{ {
if (!ResultFs.PathNotFound.Includes(rc)) if (ResultFs.PathNotFound.Includes(rc))
return rc.Catch().Handle(); return Result.Success;
return rc.Miss(); 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> hashSalt,
bool skipFormat)
{
// Use directory save data for now
Unsafe.SkipInit(out Array18<byte> saveImageNameBuffer);
using var fileSystem = new SharedRef<IFileSystem>();
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<ISaveDataFileSystem>();
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, public Result DeleteSaveDataFileSystem(SaveDataSpaceId spaceId, ulong saveDataId, bool wipeSaveFile,
in Path saveDataRootPath) in Path saveDataRootPath)
{ {
@ -555,7 +512,27 @@ public class SaveDataFileSystemServiceImpl : IDisposable
{ {
if (wipeSaveFile) 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<IFileSystem> tempFileSystem = SharedRef<IFileSystem>.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); rc = fileSystem.Get.DeleteFile(in saveImageName);
@ -570,42 +547,38 @@ public class SaveDataFileSystemServiceImpl : IDisposable
{ {
UnsafeHelpers.SkipParamInit(out extraData); UnsafeHelpers.SkipParamInit(out extraData);
// Nintendo returns blank extra data for directory save data. // Emulated save data on a host device doesn't have extra data.
// We've extended directory save data to store extra data so we don't need to do that. if (IsAllowedDirectorySaveData(spaceId, in saveDataRootPath))
{
extraData = default;
return Result.Success;
}
using UniqueLockRef<SdkRecursiveMutexType> scopedLockFsCache = _saveFileSystemCacheManager.GetScopedLock(); using UniqueLockRef<SdkRecursiveMutexType> scopedLockFsCache = _saveFileSystemCacheManager.GetScopedLock();
using UniqueLockRef<SdkRecursiveMutexType> scopedLockExtraDataCache = _saveExtraDataCacheManager.GetScopedLock(); using UniqueLockRef<SdkRecursiveMutexType> scopedLockExtraDataCache = _saveExtraDataCacheManager.GetScopedLock();
using var unusedSaveDataFs = new SharedRef<IFileSystem>();
using var extraDataAccessor = new SharedRef<ISaveDataExtraDataAccessor>(); using var extraDataAccessor = new SharedRef<ISaveDataExtraDataAccessor>();
// Try to grab an extra data accessor for the requested save from the cache. // Try to grab an extra data accessor for the requested save from the cache.
Result rc = _saveExtraDataCacheManager.GetCache(ref extraDataAccessor.Ref(), spaceId, saveDataId); 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<IFileSystem>();
// 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()) if (rc.IsFailure())
{ {
// No extra data accessor was registered for the requested save data. // Try to open the extra data accessor if it's not in the cache.
// Return a blank extra data struct.
extraData = new SaveDataExtraData(); // We won't actually use the returned save data FS.
return rc; // 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); rc = extraDataAccessor.Get.ReadExtraData(out extraData);
if (rc.IsFailure()) return rc.Miss(); if (rc.IsFailure()) return rc.Miss();
@ -615,12 +588,16 @@ public class SaveDataFileSystemServiceImpl : IDisposable
public Result WriteSaveDataFileSystemExtraData(SaveDataSpaceId spaceId, ulong saveDataId, public Result WriteSaveDataFileSystemExtraData(SaveDataSpaceId spaceId, ulong saveDataId,
in SaveDataExtraData extraData, in Path saveDataRootPath, SaveDataType type, bool updateTimeStamp) in SaveDataExtraData extraData, in Path saveDataRootPath, SaveDataType type, bool updateTimeStamp)
{ {
// Nintendo does nothing when writing directory save data extra data. // Emulated save data on a host device doesn't have extra data.
// We've extended directory save data to store extra data so we don't return early. if (IsAllowedDirectorySaveData(spaceId, in saveDataRootPath))
{
return Result.Success;
}
using UniqueLockRef<SdkRecursiveMutexType> scopedLockFsCache = _saveFileSystemCacheManager.GetScopedLock(); using UniqueLockRef<SdkRecursiveMutexType> scopedLockFsCache = _saveFileSystemCacheManager.GetScopedLock();
using UniqueLockRef<SdkRecursiveMutexType> scopedLockExtraDataCache = _saveExtraDataCacheManager.GetScopedLock(); using UniqueLockRef<SdkRecursiveMutexType> scopedLockExtraDataCache = _saveExtraDataCacheManager.GetScopedLock();
using var unusedSaveDataFs = new SharedRef<IFileSystem>();
using var extraDataAccessor = new SharedRef<ISaveDataExtraDataAccessor>(); using var extraDataAccessor = new SharedRef<ISaveDataExtraDataAccessor>();
// Try to grab an extra data accessor for the requested save from the cache. // 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()) if (rc.IsFailure())
{ {
// No accessor was found in the cache. Try to open one. // No accessor was found in the cache. Try to open one.
using var unusedSaveDataFs = new SharedRef<IFileSystem>();
// We won't actually use the returned save data FS. // We won't actually use the returned save data FS.
// Opening the FS should cache an extra data accessor for it. // 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. // Try to grab an accessor from the cache again.
rc = _saveExtraDataCacheManager.GetCache(ref extraDataAccessor.Ref(), spaceId, saveDataId); rc = _saveExtraDataCacheManager.GetCache(ref extraDataAccessor.Ref(), spaceId, saveDataId);
if (rc.IsFailure()) return rc.Miss();
if (rc.IsFailure())
{
// No extra data accessor was registered for the requested save data, so don't do anything.
return Result.Success;
}
} }
// We should have a valid accessor if we've reached this point. // 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(); 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<IFileSystem> outFileSystem, public Result OpenSaveDataDirectoryFileSystem(ref SharedRef<IFileSystem> outFileSystem,
@ -689,29 +660,39 @@ public class SaveDataFileSystemServiceImpl : IDisposable
if (allowEmulatedSave && IsAllowedDirectorySaveData(spaceId, in saveDataRootPath)) if (allowEmulatedSave && IsAllowedDirectorySaveData(spaceId, in saveDataRootPath))
{ {
using (var tmFileSystem = new SharedRef<IFileSystem>()) if (UseTargetManager)
{ {
// Ensure the target save data directory exists using (var tmFileSystem = new SharedRef<IFileSystem>())
rc = _config.TargetManagerFsCreator.Create(ref tmFileSystem.Ref(), in saveDataRootPath, {
openCaseSensitive: false, ensureRootPathExists: true, ResultFs.SaveDataRootPathUnavailable.Value); // 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(); 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; return Result.Success;
} }
using var saveDataDirPath = new Path(); using var saveDataAreaDirectoryName = new Path();
ReadOnlySpan<byte> saveDirName; ReadOnlySpan<byte> saveDirName;
if (spaceId == SaveDataSpaceId.Temporary) if (spaceId == SaveDataSpaceId.Temporary)
@ -723,48 +704,60 @@ public class SaveDataFileSystemServiceImpl : IDisposable
saveDirName = new[] { (byte)'/', (byte)'s', (byte)'a', (byte)'v', (byte)'e' }; // /save 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(); 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(); if (rc.IsFailure()) return rc.Miss();
return Result.Success; return Result.Success;
} }
public Result OpenSaveDataDirectoryFileSystemImpl(ref SharedRef<IFileSystem> outFileSystem, public Result OpenSaveDataDirectoryFileSystemImpl(ref SharedRef<IFileSystem> 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<IFileSystem> outFileSystem,
SaveDataSpaceId spaceId, in Path directoryPath, bool createIfMissing)
{ {
using var baseFileSystem = new SharedRef<IFileSystem>(); using var baseFileSystem = new SharedRef<IFileSystem>();
Result rc;
switch (spaceId) switch (spaceId)
{ {
case SaveDataSpaceId.System: 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(); 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); createIfMissing);
if (rc.IsFailure()) return rc.Miss(); if (rc.IsFailure()) return rc.Miss();
return Result.Success; break;
}
case SaveDataSpaceId.User: case SaveDataSpaceId.User:
case SaveDataSpaceId.Temporary: 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(); 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(); if (rc.IsFailure()) return rc.Miss();
return Result.Success; break;
}
case SaveDataSpaceId.SdSystem: case SaveDataSpaceId.SdSystem:
case SaveDataSpaceId.SdUser: 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(); if (rc.IsFailure()) return rc.Miss();
Unsafe.SkipInit(out Array64<byte> pathParentBuffer); Unsafe.SkipInit(out Array64<byte> pathParentBuffer);
@ -775,11 +768,11 @@ public class SaveDataFileSystemServiceImpl : IDisposable
if (rc.IsFailure()) return rc.Miss(); if (rc.IsFailure()) return rc.Miss();
using var pathSdRoot = new Path(); 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(); if (rc.IsFailure()) return rc.Miss();
using SharedRef<IFileSystem> tempFileSystem = using SharedRef<IFileSystem> tempFileSystem = SharedRef<IFileSystem>.CreateMove(ref baseFileSystem.Ref());
SharedRef<IFileSystem>.CreateMove(ref baseFileSystem.Ref());
rc = Utility.WrapSubDirectory(ref baseFileSystem.Ref(), ref tempFileSystem.Ref(), in pathSdRoot, createIfMissing); rc = Utility.WrapSubDirectory(ref baseFileSystem.Ref(), ref tempFileSystem.Ref(), in pathSdRoot, createIfMissing);
if (rc.IsFailure()) return rc.Miss(); if (rc.IsFailure()) return rc.Miss();
@ -787,37 +780,40 @@ public class SaveDataFileSystemServiceImpl : IDisposable
IEncryptedFileSystemCreator.KeyId.Save, in _encryptionSeed); IEncryptedFileSystemCreator.KeyId.Save, in _encryptionSeed);
if (rc.IsFailure()) return rc.Miss(); if (rc.IsFailure()) return rc.Miss();
return Result.Success; break;
} }
case SaveDataSpaceId.ProperSystem: 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(); 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(); if (rc.IsFailure()) return rc.Miss();
return Result.Success; break;
}
case SaveDataSpaceId.SafeMode: 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(); 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(); if (rc.IsFailure()) return rc.Miss();
return Result.Success; break;
}
default: default:
return ResultFs.InvalidArgument.Log(); return ResultFs.InvalidArgument.Log();
} }
}
public Result OpenSaveDataDirectoryFileSystemImpl(ref SharedRef<IFileSystem> outFileSystem, return Result.Success;
SaveDataSpaceId spaceId, in Path basePath)
{
return OpenSaveDataDirectoryFileSystemImpl(ref outFileSystem, spaceId, in basePath, true);
} }
public Result IsProvisionallyCommittedSaveData(out bool isProvisionallyCommitted, in SaveDataInfo saveInfo) public Result IsProvisionallyCommittedSaveData(out bool isProvisionallyCommitted, in SaveDataInfo saveInfo)
@ -837,7 +833,7 @@ public class SaveDataFileSystemServiceImpl : IDisposable
{ {
_encryptionSeed = seed; _encryptionSeed = seed;
_config.SaveFsCreator.SetSdCardEncryptionSeed(seed.Value); _config.SaveFsCreator.SetMacGenerationSeed(seed.Value);
_config.SaveIndexerManager.InvalidateIndexer(SaveDataSpaceId.SdSystem); _config.SaveIndexerManager.InvalidateIndexer(SaveDataSpaceId.SdSystem);
_config.SaveIndexerManager.InvalidateIndexer(SaveDataSpaceId.SdUser); _config.SaveIndexerManager.InvalidateIndexer(SaveDataSpaceId.SdUser);
@ -855,7 +851,7 @@ public class SaveDataFileSystemServiceImpl : IDisposable
} }
/// <summary> /// <summary>
/// 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.
/// </summary> /// </summary>
/// <remarks>In a standard application the program ID will be the same as the input program ID. /// <remarks>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 /// In multi-program applications all sub-programs use the program ID of the main program
@ -864,15 +860,15 @@ public class SaveDataFileSystemServiceImpl : IDisposable
/// <returns>The program ID of the save data.</returns> /// <returns>The program ID of the save data.</returns>
public ProgramId ResolveDefaultSaveDataReferenceProgramId(ProgramId programId) 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 // 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, 0); ProgramId mainProgramId = _config.ProgramRegistryService.GetProgramIdByIndex(programId, programIndex: 0);
if (mainProgramId != ProgramId.InvalidId) if (mainProgramId != ProgramId.InvalidId)
{ {
return mainProgramId; 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<ProgramIndexMapInfo> mapInfo = _config.ProgramRegistryService.GetProgramIndexMapInfo(programId); Optional<ProgramIndexMapInfo> mapInfo = _config.ProgramRegistryService.GetProgramIndexMapInfo(programId);
if (mapInfo.HasValue) if (mapInfo.HasValue)
@ -894,7 +890,6 @@ public class SaveDataFileSystemServiceImpl : IDisposable
UnsafeHelpers.SkipParamInit(out count); UnsafeHelpers.SkipParamInit(out count);
using var accessor = new UniqueRef<SaveDataIndexerAccessor>(); using var accessor = new UniqueRef<SaveDataIndexerAccessor>();
Result rc = OpenSaveDataIndexerAccessor(ref accessor.Ref(), out bool _, SaveDataSpaceId.User); Result rc = OpenSaveDataIndexerAccessor(ref accessor.Ref(), out bool _, SaveDataSpaceId.User);
if (rc.IsFailure()) return rc.Miss(); if (rc.IsFailure()) return rc.Miss();
@ -905,7 +900,7 @@ public class SaveDataFileSystemServiceImpl : IDisposable
public Result OpenSaveDataIndexerAccessor(ref UniqueRef<SaveDataIndexerAccessor> outAccessor, public Result OpenSaveDataIndexerAccessor(ref UniqueRef<SaveDataIndexerAccessor> outAccessor,
out bool isInitialOpen, SaveDataSpaceId spaceId) 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() public void ResetTemporaryStorageIndexer()

View file

@ -307,7 +307,7 @@ public class SaveDataManagement
Span<ProgramIndexMapInfo> mapInfo = stackalloc ProgramIndexMapInfo[5]; Span<ProgramIndexMapInfo> mapInfo = stackalloc ProgramIndexMapInfo[5];
var mainProgramId = new ProgramId(0x123456); var mainProgramId = new Ncm.ApplicationId(0x123456);
var programId = new ProgramId(mainProgramId.Value + 2); var programId = new ProgramId(mainProgramId.Value + 2);
for (int i = 0; i < mapInfo.Length; i++) for (int i = 0; i < mapInfo.Length; i++)

View file

@ -34,7 +34,7 @@ public class ProgramIndexMapInfoTests
for (int i = 0; i < map.Length; i++) 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].ProgramId = new ProgramId((ulong)(i + 1));
map[i].ProgramIndex = (byte)i; map[i].ProgramIndex = (byte)i;
} }
@ -60,7 +60,7 @@ public class ProgramIndexMapInfoTests
for (int i = 0; i < map.Length; i++) 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].ProgramId = new ProgramId((ulong)idCreator(i));
map[i].ProgramIndex = (byte)i; map[i].ProgramIndex = (byte)i;
} }