diff --git a/src/LibHac/Fs/Common/Path.cs b/src/LibHac/Fs/Common/Path.cs
index fcff40fa..054d0869 100644
--- a/src/LibHac/Fs/Common/Path.cs
+++ b/src/LibHac/Fs/Common/Path.cs
@@ -84,14 +84,6 @@ public static class PathExtensions
return p == null;
}
}
-
- public static unsafe bool IsNullRef(ref readonly int path)
- {
- fixed (int* p = &path)
- {
- return p == null;
- }
- }
#pragma warning restore CS8500 // This takes the address of, gets the size of, or declares a pointer to a managed type
#pragma warning restore LH0001 // DoNotCopyValue
}
diff --git a/src/LibHac/Fs/FsEnums.cs b/src/LibHac/Fs/FsEnums.cs
index c255d46e..c98d5a2b 100644
--- a/src/LibHac/Fs/FsEnums.cs
+++ b/src/LibHac/Fs/FsEnums.cs
@@ -21,7 +21,9 @@ public enum BisPartitionId
System = 31,
SystemProperEncryption = 32,
SystemProperPartition = 33,
- SignedSystemPartitionOnSafeMode = 34
+ SignedSystemPartitionOnSafeMode = 34,
+ DeviceTreeBlob = 35,
+ System0 = 36
}
public enum ContentStorageId
diff --git a/src/LibHac/Fs/Impl/CommonMountNames.cs b/src/LibHac/Fs/Impl/CommonMountNames.cs
index 5fce8a99..392f9118 100644
--- a/src/LibHac/Fs/Impl/CommonMountNames.cs
+++ b/src/LibHac/Fs/Impl/CommonMountNames.cs
@@ -10,6 +10,9 @@ public static class CommonMountNames
/// "@Host"
public static ReadOnlySpan HostRootFileSystemMountName => "@Host"u8;
+ /// "@Local"
+ public static ReadOnlySpan LocalRootFileSystemMountName => "@Local"u8;
+
/// "@Sdcard"
public static ReadOnlySpan SdCardFileSystemMountName => "@Sdcard"u8;
@@ -38,6 +41,9 @@ public static class CommonMountNames
/// "@System"
public static ReadOnlySpan BisSystemPartitionMountName => "@System"u8;
+ /// "@System0"
+ public static ReadOnlySpan BisSystemPartition0MountName => "@System0"u8;
+
//Content storage names.
/// "@SystemContent"
public static ReadOnlySpan ContentStorageSystemMountName => "@SystemContent"u8;
diff --git a/src/LibHac/FsSrv/FileSystemProxyCoreImpl.cs b/src/LibHac/FsSrv/FileSystemProxyCoreImpl.cs
index 7bcf0ed1..22dc09b4 100644
--- a/src/LibHac/FsSrv/FileSystemProxyCoreImpl.cs
+++ b/src/LibHac/FsSrv/FileSystemProxyCoreImpl.cs
@@ -65,7 +65,7 @@ public class FileSystemProxyCoreImpl
if (res.IsFailure()) return res.Miss();
tempFs.SetByMove(ref fileSystem.Ref);
- res = _fsCreators.EncryptedFileSystemCreator.Create(ref fileSystem.Ref, ref tempFs.Ref,
+ res = _fsCreators.EncryptedFileSystemCreator.Create(ref fileSystem.Ref, in tempFs,
IEncryptedFileSystemCreator.KeyId.CustomStorage, in _sdEncryptionSeed);
if (res.IsFailure()) return res.Miss();
}
diff --git a/src/LibHac/FsSrv/FileSystemServerInitializer.cs b/src/LibHac/FsSrv/FileSystemServerInitializer.cs
index 6de58e05..4834828f 100644
--- a/src/LibHac/FsSrv/FileSystemServerInitializer.cs
+++ b/src/LibHac/FsSrv/FileSystemServerInitializer.cs
@@ -13,8 +13,10 @@ namespace LibHac.FsSrv;
public static class FileSystemServerInitializer
{
- private const ulong SpeedEmulationProgramIdMinimum = 0x100000000000000;
- private const ulong SpeedEmulationProgramIdMaximum = 0x100000000001FFF;
+ private const ulong SpeedEmulationProgramIdWithoutPlatformIdMinimum = 0;
+ private const ulong SpeedEmulationProgramIdWithoutPlatformIdMaximum = 0x1FFF;
+
+ private const uint ContentDivisionSize = ConcatenationFileSystem.DefaultInternalFileSize;
private const int BufferManagerHeapSize = 1024 * 1024 * 14;
private const int BufferManagerCacheSize = 1024;
@@ -108,8 +110,8 @@ public static class FileSystemServerInitializer
new AccessFailureManagementServiceImpl(in accessFailureManagementServiceConfig);
var speedEmulationRange =
- new InternalProgramIdRangeForSpeedEmulation(SpeedEmulationProgramIdMinimum,
- SpeedEmulationProgramIdMaximum);
+ new InternalProgramIdRangeForSpeedEmulation(SpeedEmulationProgramIdWithoutPlatformIdMinimum,
+ SpeedEmulationProgramIdWithoutPlatformIdMaximum);
var ncaFsServiceConfig = new NcaFileSystemServiceImpl.Configuration();
ncaFsServiceConfig.BaseFsService = baseFsService;
@@ -123,6 +125,8 @@ public static class FileSystemServerInitializer
ncaFsServiceConfig.ProgramRegistryService = programRegistryService;
ncaFsServiceConfig.AccessFailureManagementService = accessFailureManagementService;
ncaFsServiceConfig.SpeedEmulationRange = speedEmulationRange;
+ ncaFsServiceConfig.AddOnContentDivisionSize = ContentDivisionSize;
+ ncaFsServiceConfig.RomDivisionSize = ContentDivisionSize;
ncaFsServiceConfig.FsServer = server;
var ncaFsService = new NcaFileSystemServiceImpl(in ncaFsServiceConfig);
@@ -141,6 +145,12 @@ public static class FileSystemServerInitializer
saveFsServiceConfig.SaveDataFileSystemCacheCount = 1;
saveFsServiceConfig.SaveIndexerManager = saveDataIndexerManager;
saveFsServiceConfig.DebugConfigService = debugConfigurationService;
+ saveFsServiceConfig.JournalIntegritySaveDataVersion = 0x50000;
+ saveFsServiceConfig.JournalIntegritySupportedVersionMin = 0x40000;
+ saveFsServiceConfig.JournalIntegritySupportedVersionMax = 0x50000;
+ saveFsServiceConfig.IntegritySaveDataVersion = 0x10000;
+ saveFsServiceConfig.IntegritySupportedVersionMin = 0x10000;
+ saveFsServiceConfig.IntegritySupportedVersionMax = 0x10000;
saveFsServiceConfig.FsServer = server;
var saveFsService = new SaveDataFileSystemServiceImpl(in saveFsServiceConfig);
diff --git a/src/LibHac/FsSrv/FsCreator/EncryptedFileSystemCreator.cs b/src/LibHac/FsSrv/FsCreator/EncryptedFileSystemCreator.cs
index 15d26e3e..94650694 100644
--- a/src/LibHac/FsSrv/FsCreator/EncryptedFileSystemCreator.cs
+++ b/src/LibHac/FsSrv/FsCreator/EncryptedFileSystemCreator.cs
@@ -16,7 +16,7 @@ public class EncryptedFileSystemCreator : IEncryptedFileSystemCreator
KeySet = keySet;
}
- public Result Create(ref SharedRef outEncryptedFileSystem, ref SharedRef baseFileSystem, KeyId idIndex, in EncryptionSeed encryptionSeed)
+ public Result Create(ref SharedRef outEncryptedFileSystem, ref readonly SharedRef baseFileSystem, KeyId idIndex, in EncryptionSeed encryptionSeed)
{
if (idIndex < KeyId.Save || idIndex > KeyId.CustomStorage)
{
@@ -26,7 +26,7 @@ public class EncryptedFileSystemCreator : IEncryptedFileSystemCreator
// todo: "proper" key generation instead of a lazy hack
KeySet.SetSdSeed(encryptionSeed.Value);
- using var encryptedFileSystem = new SharedRef(new AesXtsFileSystem(ref baseFileSystem,
+ using var encryptedFileSystem = new SharedRef(new AesXtsFileSystem(in baseFileSystem,
KeySet.SdCardEncryptionKeys[(int)idIndex].DataRo.ToArray(), 0x4000));
outEncryptedFileSystem.SetByMove(ref encryptedFileSystem.Ref);
diff --git a/src/LibHac/FsSrv/FsCreator/IEncryptedFileSystemCreator.cs b/src/LibHac/FsSrv/FsCreator/IEncryptedFileSystemCreator.cs
index 10e65124..dcb182eb 100644
--- a/src/LibHac/FsSrv/FsCreator/IEncryptedFileSystemCreator.cs
+++ b/src/LibHac/FsSrv/FsCreator/IEncryptedFileSystemCreator.cs
@@ -13,6 +13,6 @@ public interface IEncryptedFileSystemCreator
CustomStorage = 2
}
- Result Create(ref SharedRef outEncryptedFileSystem, ref SharedRef baseFileSystem,
+ Result Create(ref SharedRef outEncryptedFileSystem, ref readonly SharedRef baseFileSystem,
KeyId idIndex, in EncryptionSeed encryptionSeed);
}
\ No newline at end of file
diff --git a/src/LibHac/FsSrv/FsCreator/ILocalFileSystemCreator.cs b/src/LibHac/FsSrv/FsCreator/ILocalFileSystemCreator.cs
index ef019306..d7b10258 100644
--- a/src/LibHac/FsSrv/FsCreator/ILocalFileSystemCreator.cs
+++ b/src/LibHac/FsSrv/FsCreator/ILocalFileSystemCreator.cs
@@ -7,4 +7,9 @@ namespace LibHac.FsSrv.FsCreator;
public interface ILocalFileSystemCreator
{
Result Create(ref SharedRef outFileSystem, ref readonly Path rootPath, bool openCaseSensitive, bool ensureRootPathExists, Result pathNotFoundResult);
+
+ public Result Create(ref SharedRef outFileSystem, ref readonly Path rootPath, bool openCaseSensitive)
+ {
+ return Create(ref outFileSystem, in rootPath, openCaseSensitive, false, Result.Success).Ret();
+ }
}
\ No newline at end of file
diff --git a/src/LibHac/FsSrv/FsCreator/IPartitionFileSystemCreator.cs b/src/LibHac/FsSrv/FsCreator/IPartitionFileSystemCreator.cs
index ce5ee001..4060a3c1 100644
--- a/src/LibHac/FsSrv/FsCreator/IPartitionFileSystemCreator.cs
+++ b/src/LibHac/FsSrv/FsCreator/IPartitionFileSystemCreator.cs
@@ -6,5 +6,5 @@ namespace LibHac.FsSrv.FsCreator;
public interface IPartitionFileSystemCreator
{
- Result Create(ref SharedRef outFileSystem, ref SharedRef baseStorage);
+ Result Create(ref SharedRef outFileSystem, ref readonly SharedRef baseStorage);
}
\ No newline at end of file
diff --git a/src/LibHac/FsSrv/FsCreator/IRomFileSystemCreator.cs b/src/LibHac/FsSrv/FsCreator/IRomFileSystemCreator.cs
index edd1ead6..ec70c8bd 100644
--- a/src/LibHac/FsSrv/FsCreator/IRomFileSystemCreator.cs
+++ b/src/LibHac/FsSrv/FsCreator/IRomFileSystemCreator.cs
@@ -6,5 +6,5 @@ namespace LibHac.FsSrv.FsCreator;
public interface IRomFileSystemCreator
{
- Result Create(ref SharedRef outFileSystem, ref SharedRef romFsStorage);
+ Result Create(ref SharedRef outFileSystem, ref readonly SharedRef romFsStorage);
}
\ No newline at end of file
diff --git a/src/LibHac/FsSrv/FsCreator/IStorageOnNcaCreator.cs b/src/LibHac/FsSrv/FsCreator/IStorageOnNcaCreator.cs
index f122ce98..d8589fd6 100644
--- a/src/LibHac/FsSrv/FsCreator/IStorageOnNcaCreator.cs
+++ b/src/LibHac/FsSrv/FsCreator/IStorageOnNcaCreator.cs
@@ -7,11 +7,11 @@ namespace LibHac.FsSrv.FsCreator;
public interface IStorageOnNcaCreator
{
Result Create(ref SharedRef outStorage,
- ref SharedRef outStorageAccessSplitter, out NcaFsHeaderReader outHeaderReader,
+ ref SharedRef outStorageAccessSplitter, ref NcaFsHeaderReader outHeaderReader,
ref readonly SharedRef ncaReader, int fsIndex);
Result CreateWithPatch(ref SharedRef outStorage,
- ref SharedRef outStorageAccessSplitter, out NcaFsHeaderReader outHeaderReader,
+ ref SharedRef outStorageAccessSplitter, ref NcaFsHeaderReader outHeaderReader,
ref readonly SharedRef originalNcaReader, ref readonly SharedRef currentNcaReader,
int fsIndex);
diff --git a/src/LibHac/FsSrv/FsCreator/PartitionFileSystemCreator.cs b/src/LibHac/FsSrv/FsCreator/PartitionFileSystemCreator.cs
index 07354dcf..b6550c5e 100644
--- a/src/LibHac/FsSrv/FsCreator/PartitionFileSystemCreator.cs
+++ b/src/LibHac/FsSrv/FsCreator/PartitionFileSystemCreator.cs
@@ -7,7 +7,7 @@ namespace LibHac.FsSrv.FsCreator;
public class PartitionFileSystemCreator : IPartitionFileSystemCreator
{
- public Result Create(ref SharedRef outFileSystem, ref SharedRef baseStorage)
+ public Result Create(ref SharedRef outFileSystem, ref readonly SharedRef baseStorage)
{
using var partitionFs = new SharedRef(new PartitionFileSystem());
diff --git a/src/LibHac/FsSrv/FsCreator/RomFileSystemCreator.cs b/src/LibHac/FsSrv/FsCreator/RomFileSystemCreator.cs
index 0208813a..5bc2d710 100644
--- a/src/LibHac/FsSrv/FsCreator/RomFileSystemCreator.cs
+++ b/src/LibHac/FsSrv/FsCreator/RomFileSystemCreator.cs
@@ -8,9 +8,9 @@ namespace LibHac.FsSrv.FsCreator;
public class RomFileSystemCreator : IRomFileSystemCreator
{
// todo: Implement properly
- public Result Create(ref SharedRef outFileSystem, ref SharedRef romFsStorage)
+ public Result Create(ref SharedRef outFileSystem, ref readonly SharedRef romFsStorage)
{
- outFileSystem.Reset(new RomFsFileSystem(ref romFsStorage));
+ outFileSystem.Reset(new RomFsFileSystem(in romFsStorage));
return Result.Success;
}
}
\ No newline at end of file
diff --git a/src/LibHac/FsSrv/FsCreator/StorageOnNcaCreator.cs b/src/LibHac/FsSrv/FsCreator/StorageOnNcaCreator.cs
index aa21e61c..7c833309 100644
--- a/src/LibHac/FsSrv/FsCreator/StorageOnNcaCreator.cs
+++ b/src/LibHac/FsSrv/FsCreator/StorageOnNcaCreator.cs
@@ -28,7 +28,7 @@ public class StorageOnNcaCreator : IStorageOnNcaCreator
}
public Result Create(ref SharedRef outStorage,
- ref SharedRef outStorageAccessSplitter, out NcaFsHeaderReader outHeaderReader,
+ ref SharedRef outStorageAccessSplitter, ref NcaFsHeaderReader outHeaderReader,
ref readonly SharedRef ncaReader, int fsIndex)
{
var ncaFsDriver = new NcaFileSystemDriver(in ncaReader, _memoryResource, _bufferManager, _hashGeneratorFactorySelector);
@@ -36,7 +36,7 @@ public class StorageOnNcaCreator : IStorageOnNcaCreator
using var storage = new SharedRef();
using var storageAccessSplitter = new SharedRef();
Result res = RomResultConverter.ConvertRomResult(ncaFsDriver.OpenStorage(ref storage.Ref,
- ref storageAccessSplitter.Ref, out outHeaderReader, fsIndex));
+ ref storageAccessSplitter.Ref, ref outHeaderReader, fsIndex));
if (res.IsFailure()) return res.Miss();
using var resultConvertStorage = new SharedRef(new RomResultConvertStorage(in storage));
@@ -48,7 +48,7 @@ public class StorageOnNcaCreator : IStorageOnNcaCreator
}
public Result CreateWithPatch(ref SharedRef outStorage,
- ref SharedRef outStorageAccessSplitter, out NcaFsHeaderReader outHeaderReader,
+ ref SharedRef outStorageAccessSplitter, ref NcaFsHeaderReader outHeaderReader,
ref readonly SharedRef originalNcaReader, ref readonly SharedRef currentNcaReader,
int fsIndex)
{
@@ -58,7 +58,7 @@ public class StorageOnNcaCreator : IStorageOnNcaCreator
using var storage = new SharedRef();
using var storageAccessSplitter = new SharedRef();
Result res = RomResultConverter.ConvertRomResult(ncaFsDriver.OpenStorage(ref storage.Ref,
- ref storageAccessSplitter.Ref, out outHeaderReader, fsIndex));
+ ref storageAccessSplitter.Ref, ref outHeaderReader, fsIndex));
if (res.IsFailure()) return res.Miss();
using var resultConvertStorage = new SharedRef(new RomResultConvertStorage(in storage));
diff --git a/src/LibHac/FsSrv/Impl/ExternalKeyManager.cs b/src/LibHac/FsSrv/Impl/ExternalKeyManager.cs
index 53317c36..a123daca 100644
--- a/src/LibHac/FsSrv/Impl/ExternalKeyManager.cs
+++ b/src/LibHac/FsSrv/Impl/ExternalKeyManager.cs
@@ -34,12 +34,12 @@ public class ExternalKeyManager
throw new NotImplementedException();
}
- public Result Find(out AccessKey outAccessKey, in RightsId rightsId)
+ public Result Find(out AccessKey outAccessKey, RightsId rightsId)
{
throw new NotImplementedException();
}
- private Result FindCore(out AccessKey outAccessKey, in RightsId rightsId)
+ private Result FindCore(out AccessKey outAccessKey, RightsId rightsId)
{
throw new NotImplementedException();
}
diff --git a/src/LibHac/FsSrv/Impl/LocationResolverSet.cs b/src/LibHac/FsSrv/Impl/LocationResolverSet.cs
index 200950e6..972d82a5 100644
--- a/src/LibHac/FsSrv/Impl/LocationResolverSet.cs
+++ b/src/LibHac/FsSrv/Impl/LocationResolverSet.cs
@@ -12,14 +12,14 @@ namespace LibHac.FsSrv.Impl;
public static class LocationResolverSetGlobalMethods
{
- public static void InitializeLocationResolverSet(this FileSystemServer fsSrv)
+ public static void InitializeLocationResolverSet(this FileSystemServer fsServer)
{
- ref LocationResolverSetGlobals globals = ref fsSrv.Globals.LocationResolverSet;
+ ref LocationResolverSetGlobals globals = ref fsServer.Globals.LocationResolverSet;
using ScopedLock scopedLock = ScopedLock.Lock(ref globals.Mutex);
if (!globals.IsLrInitialized)
{
- fsSrv.Hos.Lr.Initialize();
+ fsServer.Hos.Lr.Initialize();
globals.IsLrInitialized = true;
}
}
@@ -39,8 +39,8 @@ internal struct LocationResolverSetGlobals
///
/// Manages resolving the location of NCAs via the lr service.
///
-/// Based on nnSdk 13.4.0 (FS 13.1.0)
-internal class LocationResolverSet : IDisposable
+/// Based on nnSdk 17.5.0 (FS 17.0.0)
+public class LocationResolverSet : IDisposable
{
private Array5> _resolvers;
private Optional _aocResolver;
@@ -76,16 +76,16 @@ internal class LocationResolverSet : IDisposable
private static Result SetUpFsPath(ref Fs.Path outPath, ref readonly Lr.Path lrPath)
{
- var pathFlags = new PathFlags();
- pathFlags.AllowMountName();
+ var flags = new PathFlags();
+ flags.AllowMountName();
if (Utility.IsHostFsMountName(lrPath.Value))
- pathFlags.AllowWindowsPath();
+ flags.AllowWindowsPath();
Result res = outPath.InitializeWithReplaceUnc(lrPath.Value);
if (res.IsFailure()) return res.Miss();
- res = outPath.Normalize(pathFlags);
+ res = outPath.Normalize(flags);
if (res.IsFailure()) return res.Miss();
return Result.Success;
@@ -124,7 +124,7 @@ internal class LocationResolverSet : IDisposable
{
_fsServer.InitializeLocationResolverSet();
- return Hos.Lr.OpenRegisteredLocationResolver(out resolver);
+ return Hos.Lr.OpenRegisteredLocationResolver(out resolver).Ret();
}
private Result GetAddOnContentLocationResolver(out AddOnContentLocationResolver resolver)
@@ -147,75 +147,109 @@ internal class LocationResolverSet : IDisposable
return Result.Success;
}
- public Result ResolveApplicationControlPath(ref Fs.Path outPath, Ncm.ApplicationId applicationId, StorageId storageId)
+ public Result ResolveApplicationControlPath(ref Fs.Path outPath, out ContentAttributes outContentAttributes,
+ Ncm.ApplicationId applicationId, StorageId storageId)
{
+ UnsafeHelpers.SkipParamInit(out outContentAttributes);
+
Result res = GetLocationResolver(out LocationResolver resolver, storageId);
if (res.IsFailure()) return res.Miss();
res = resolver.ResolveApplicationControlPath(out Lr.Path path, applicationId);
if (res.IsFailure()) return res.Miss();
- return SetUpFsPath(ref outPath, in path);
+ outContentAttributes = ContentAttributes.None;
+ return SetUpFsPath(ref outPath, in path).Ret();
}
- public Result ResolveApplicationHtmlDocumentPath(out bool isDirectory, ref Fs.Path outPath, Ncm.ApplicationId applicationId, StorageId storageId)
+ public Result ResolveApplicationHtmlDocumentPath(out bool outIsDirectory, ref Fs.Path outPath,
+ out ContentAttributes outContentAttributes, ulong applicationId, StorageId storageId)
{
- UnsafeHelpers.SkipParamInit(out isDirectory);
+ UnsafeHelpers.SkipParamInit(out outIsDirectory, out outContentAttributes);
Result res = GetLocationResolver(out LocationResolver resolver, storageId);
if (res.IsFailure()) return res.Miss();
- res = resolver.ResolveApplicationHtmlDocumentPath(out Lr.Path path, applicationId);
+ res = resolver.ResolveApplicationHtmlDocumentPath(out Lr.Path path, new ProgramId(applicationId));
if (res.IsFailure()) return res.Miss();
- isDirectory = PathUtility.IsDirectoryPath(path.Value);
+ outContentAttributes = ContentAttributes.None;
+ outIsDirectory = PathUtility.IsDirectoryPath(path.Value);
- return SetUpFsPath(ref outPath, in path);
+ return SetUpFsPath(ref outPath, in path).Ret();
}
- public Result ResolveProgramPath(out bool isDirectory, ref Fs.Path outPath, ProgramId programId, StorageId storageId)
+ public Result ResolveProgramPath(out bool outIsDirectory, ref Fs.Path outPath,
+ out ContentAttributes outContentAttributes, ulong programId, StorageId storageId)
{
- UnsafeHelpers.SkipParamInit(out isDirectory);
+ UnsafeHelpers.SkipParamInit(out outIsDirectory, out outContentAttributes);
Result res = GetLocationResolver(out LocationResolver resolver, storageId);
if (res.IsFailure()) return res.Miss();
- res = resolver.ResolveProgramPath(out Lr.Path path, programId);
+ res = resolver.ResolveProgramPath(out Lr.Path path, new ProgramId(programId));
if (res.IsFailure()) return res.Miss();
- isDirectory = PathUtility.IsDirectoryPath(path.Value);
+ outContentAttributes = ContentAttributes.None;
+ outIsDirectory = PathUtility.IsDirectoryPath(path.Value);
- return SetUpFsPath(ref outPath, in path);
+ return SetUpFsPath(ref outPath, in path).Ret();
}
- public Result ResolveRomPath(out bool isDirectory, ref Fs.Path outPath, ProgramId programId, StorageId storageId)
+ public Result ResolveRomPath(out bool outIsDirectory, ref Fs.Path outPath,
+ out ContentAttributes outContentAttributes, ulong programId, StorageId storageId)
{
- UnsafeHelpers.SkipParamInit(out isDirectory);
+ UnsafeHelpers.SkipParamInit(out outIsDirectory, out outContentAttributes);
Result res = GetLocationResolver(out LocationResolver resolver, storageId);
if (res.IsFailure()) return res.Miss();
- res = resolver.ResolveProgramPathForDebug(out Lr.Path path, programId);
+ res = resolver.ResolveProgramPathForDebug(out Lr.Path path, new ProgramId(programId));
if (res.IsFailure()) return res.Miss();
- isDirectory = PathUtility.IsDirectoryPath(path.Value);
+ outContentAttributes = ContentAttributes.None;
+ outIsDirectory = PathUtility.IsDirectoryPath(path.Value);
- return SetUpFsPath(ref outPath, in path);
+ return SetUpFsPath(ref outPath, in path).Ret();
}
- public Result ResolveAddOnContentPath(ref Fs.Path outPath, DataId dataId)
+ public Result ResolveAddOnContentPath(ref Fs.Path outPath, out ContentAttributes outContentAttributes,
+ ref Fs.Path outPatchPath, out ContentAttributes outPatchContentAttributes, DataId dataId)
{
+ UnsafeHelpers.SkipParamInit(out outContentAttributes, out outPatchContentAttributes);
+
Result res = GetAddOnContentLocationResolver(out AddOnContentLocationResolver resolver);
if (res.IsFailure()) return res.Miss();
- res = resolver.ResolveAddOnContentPath(out Lr.Path path, dataId);
+ res = resolver.GetRegisteredAddOnContentPaths(out Lr.Path path, out Lr.Path patchPath, dataId);
if (res.IsFailure()) return res.Miss();
- return SetUpFsPath(ref outPath, in path);
+ outContentAttributes = ContentAttributes.None;
+
+ if (patchPath.Value[0] != 0)
+ {
+ // Note: FS appears to assign the paths to the wrong outputs here
+ res = SetUpFsPath(ref outPath, in patchPath);
+ if (res.IsFailure()) return res.Miss();
+
+ res = SetUpFsPath(ref outPatchPath, in path);
+ if (res.IsFailure()) return res.Miss();
+
+ outPatchContentAttributes = ContentAttributes.None;
+ }
+ else
+ {
+ res = SetUpFsPath(ref outPath, in path);
+ if (res.IsFailure()) return res.Miss();
+ }
+
+ return Result.Success;
}
- public Result ResolveDataPath(ref Fs.Path outPath, DataId dataId, StorageId storageId)
+ public Result ResolveDataPath(ref Fs.Path outPath, out ContentAttributes outContentAttributes, DataId dataId, StorageId storageId)
{
+ UnsafeHelpers.SkipParamInit(out outContentAttributes);
+
if (storageId == StorageId.None)
return ResultFs.InvalidAlignment.Log();
@@ -225,11 +259,14 @@ internal class LocationResolverSet : IDisposable
res = resolver.ResolveDataPath(out Lr.Path path, dataId);
if (res.IsFailure()) return res.Miss();
- return SetUpFsPath(ref outPath, in path);
+ outContentAttributes = ContentAttributes.None;
+ return SetUpFsPath(ref outPath, in path).Ret();
}
- public Result ResolveRegisteredProgramPath(ref Fs.Path outPath, ulong id)
+ public Result ResolveRegisteredProgramPath(ref Fs.Path outPath, out ContentAttributes outContentAttributes, ulong id)
{
+ UnsafeHelpers.SkipParamInit(out outContentAttributes);
+
RegisteredLocationResolver resolver = null;
try
{
@@ -239,7 +276,8 @@ internal class LocationResolverSet : IDisposable
res = resolver.ResolveProgramPath(out Lr.Path path, new ProgramId(id));
if (res.IsFailure()) return res.Miss();
- return SetUpFsPath(ref outPath, in path);
+ outContentAttributes = ContentAttributes.None;
+ return SetUpFsPath(ref outPath, in path).Ret();
}
finally
{
@@ -247,8 +285,10 @@ internal class LocationResolverSet : IDisposable
}
}
- public Result ResolveRegisteredHtmlDocumentPath(ref Fs.Path outPath, ulong id)
+ public Result ResolveRegisteredHtmlDocumentPath(ref Fs.Path outPath, out ContentAttributes outContentAttributes, ulong id)
{
+ UnsafeHelpers.SkipParamInit(out outContentAttributes);
+
RegisteredLocationResolver resolver = null;
try
{
@@ -258,7 +298,8 @@ internal class LocationResolverSet : IDisposable
res = resolver.ResolveHtmlDocumentPath(out Lr.Path path, new ProgramId(id));
if (res.IsFailure()) return res.Miss();
- return SetUpFsPath(ref outPath, in path);
+ outContentAttributes = ContentAttributes.None;
+ return SetUpFsPath(ref outPath, in path).Ret();
}
finally
{
diff --git a/src/LibHac/FsSrv/Impl/NpdmVerificationFileSystem.cs b/src/LibHac/FsSrv/Impl/NpdmVerificationFileSystem.cs
new file mode 100644
index 00000000..edfc6914
--- /dev/null
+++ b/src/LibHac/FsSrv/Impl/NpdmVerificationFileSystem.cs
@@ -0,0 +1,172 @@
+using System;
+using System.Runtime.CompilerServices;
+using LibHac.Common;
+using LibHac.Fs;
+using LibHac.Fs.Fsa;
+
+namespace LibHac.FsSrv.Impl;
+
+[InlineArray(0x20)]
+public struct NpdmHash
+{
+ private byte _data;
+}
+
+public class NpdmVerificationFile : IFile
+{
+ private NpdmHash _hash;
+ private UniqueRef _baseFile;
+
+ public NpdmVerificationFile(ref UniqueRef baseFile, NpdmHash hash)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override void Dispose()
+ {
+ _baseFile.Destroy();
+ base.Dispose();
+ }
+
+ protected override Result DoRead(out long bytesRead, long offset, Span destination, in ReadOption option)
+ {
+ throw new NotImplementedException();
+ }
+
+ protected override Result DoWrite(long offset, ReadOnlySpan source, in WriteOption option)
+ {
+ throw new NotImplementedException();
+ }
+
+ protected override Result DoFlush()
+ {
+ throw new NotImplementedException();
+ }
+
+ protected override Result DoSetSize(long size)
+ {
+ throw new NotImplementedException();
+ }
+
+ protected override Result DoGetSize(out long size)
+ {
+ throw new NotImplementedException();
+ }
+
+ protected override Result DoOperateRange(Span outBuffer, OperationId operationId, long offset, long size, ReadOnlySpan inBuffer)
+ {
+ throw new NotImplementedException();
+ }
+}
+
+public class NpdmVerificationFileSystem : IFileSystem
+{
+ private NpdmHash _hash;
+ private ReadOnlyFileSystem _baseFileSystem;
+
+ public NpdmVerificationFileSystem(in SharedRef baseFileSystem, NpdmHash hash)
+ {
+
+ }
+
+ public override void Dispose()
+ {
+ _baseFileSystem.Dispose();
+ base.Dispose();
+ }
+
+ protected override Result DoOpenFile(ref UniqueRef outFile, ref readonly Path path, OpenMode mode)
+ {
+ throw new NotImplementedException();
+ }
+
+ protected override Result DoCreateFile(ref readonly Path path, long size, CreateFileOptions option)
+ {
+ throw new NotImplementedException();
+ }
+
+ protected override Result DoDeleteFile(ref readonly Path path)
+ {
+ throw new NotImplementedException();
+ }
+
+ protected override Result DoCreateDirectory(ref readonly Path path)
+ {
+ throw new NotImplementedException();
+ }
+
+ protected override Result DoDeleteDirectory(ref readonly Path path)
+ {
+ throw new NotImplementedException();
+ }
+
+ protected override Result DoDeleteDirectoryRecursively(ref readonly Path path)
+ {
+ throw new NotImplementedException();
+ }
+
+ protected override Result DoCleanDirectoryRecursively(ref readonly Path path)
+ {
+ throw new NotImplementedException();
+ }
+
+ protected override Result DoRenameFile(ref readonly Path currentPath, ref readonly Path newPath)
+ {
+ throw new NotImplementedException();
+ }
+
+ protected override Result DoRenameDirectory(ref readonly Path currentPath, ref readonly Path newPath)
+ {
+ throw new NotImplementedException();
+ }
+
+ protected override Result DoGetEntryType(out DirectoryEntryType entryType, ref readonly Path path)
+ {
+ throw new NotImplementedException();
+ }
+
+ protected override Result DoGetFreeSpaceSize(out long freeSpace, ref readonly Path path)
+ {
+ throw new NotImplementedException();
+ }
+
+ protected override Result DoGetTotalSpaceSize(out long totalSpace, ref readonly Path path)
+ {
+ throw new NotImplementedException();
+ }
+
+ protected override Result DoOpenDirectory(ref UniqueRef outDirectory, ref readonly Path path, OpenDirectoryMode mode)
+ {
+ throw new NotImplementedException();
+ }
+
+ protected override Result DoCommit()
+ {
+ throw new NotImplementedException();
+ }
+
+ protected override Result DoCommitProvisionally(long counter)
+ {
+ throw new NotImplementedException();
+ }
+
+ protected override Result DoRollback()
+ {
+ throw new NotImplementedException();
+ }
+
+ protected override Result DoFlush()
+ {
+ throw new NotImplementedException();
+ }
+
+ protected override Result DoGetFileTimeStampRaw(out FileTimeStampRaw timeStamp, ref readonly Path path)
+ {
+ throw new NotImplementedException();
+ }
+
+ protected override Result DoQueryEntry(Span outBuffer, ReadOnlySpan inBuffer, QueryId queryId, ref readonly Path path)
+ {
+ throw new NotImplementedException();
+ }
+}
\ No newline at end of file
diff --git a/src/LibHac/FsSrv/Impl/ProgramIndexMapInfoManager.cs b/src/LibHac/FsSrv/Impl/ProgramIndexMapInfoManager.cs
index 13c540df..d042c286 100644
--- a/src/LibHac/FsSrv/Impl/ProgramIndexMapInfoManager.cs
+++ b/src/LibHac/FsSrv/Impl/ProgramIndexMapInfoManager.cs
@@ -10,9 +10,10 @@ namespace LibHac.FsSrv.Impl;
///
/// Keeps track of the program IDs and program indexes of each program in a multi-program application.
///
-/// Based on nnSdk 13.4.0 (FS 13.1.0)
+/// Based on nnSdk 17.5.0 (FS 17.0.0)
public class ProgramIndexMapInfoManager : IDisposable
{
+ // Changed: The original uses an intrusive list for the entries
private LinkedList _mapEntries;
private SdkMutexType _mutex;
@@ -38,21 +39,29 @@ public class ProgramIndexMapInfoManager : IDisposable
ClearImpl();
- for (int i = 0; i < programIndexMapInfo.Length; i++)
+ bool isSuccess = false;
+ try
{
- var entry = new ProgramIndexMapInfo
+ for (int i = 0; i < programIndexMapInfo.Length; i++)
{
- ProgramId = programIndexMapInfo[i].ProgramId,
- MainProgramId = programIndexMapInfo[i].MainProgramId,
- ProgramIndex = programIndexMapInfo[i].ProgramIndex
- };
+ var entry = new ProgramIndexMapInfo
+ {
+ ProgramId = programIndexMapInfo[i].ProgramId,
+ MainProgramId = programIndexMapInfo[i].MainProgramId,
+ ProgramIndex = programIndexMapInfo[i].ProgramIndex
+ };
- _mapEntries.AddLast(entry);
+ _mapEntries.AddLast(entry);
+ }
+
+ isSuccess = true;
+ return Result.Success;
+ }
+ finally
+ {
+ if (!isSuccess)
+ ClearImpl();
}
-
- // We skip running ClearImpl() if the allocation failed because we don't need to worry about that in C#
-
- return Result.Success;
}
///
@@ -116,6 +125,19 @@ public class ProgramIndexMapInfoManager : IDisposable
return _mapEntries.Count;
}
+ public ProgramId GetApplicationProgramId(ProgramId programId)
+ {
+ using ScopedLock scopedLock = ScopedLock.Lock(ref _mutex);
+
+ Optional programIndexMapInfo =
+ GetImpl((in ProgramIndexMapInfo x) => x.ProgramId == programId);
+
+ if (!programIndexMapInfo.HasValue)
+ return ProgramId.InvalidId;
+
+ return new ProgramId(programIndexMapInfo.Value.MainProgramId.Value + programIndexMapInfo.Value.ProgramIndex);
+ }
+
private delegate bool EntrySelector(in ProgramIndexMapInfo candidate);
private Optional GetImpl(EntrySelector selector)
diff --git a/src/LibHac/FsSrv/NcaFileSystemServiceImpl.cs b/src/LibHac/FsSrv/NcaFileSystemServiceImpl.cs
index 66d865ab..6645429b 100644
--- a/src/LibHac/FsSrv/NcaFileSystemServiceImpl.cs
+++ b/src/LibHac/FsSrv/NcaFileSystemServiceImpl.cs
@@ -1,17 +1,23 @@
using System;
+using System.Buffers.Text;
+using System.Runtime.CompilerServices;
using LibHac.Common;
+using LibHac.Crypto;
using LibHac.Diag;
using LibHac.Fs;
using LibHac.Fs.Fsa;
using LibHac.FsSrv.FsCreator;
using LibHac.FsSrv.Impl;
using LibHac.FsSystem;
+using LibHac.Lr;
using LibHac.Ncm;
using LibHac.Os;
using LibHac.Spl;
-using NcaFsHeader = LibHac.FsSystem.NcaFsHeader;
+using LibHac.Util;
+using static LibHac.Fs.Impl.CommonMountNames;
+using static LibHac.FsSrv.Anonymous;
+using Path = LibHac.Fs.Path;
using RightsId = LibHac.Fs.RightsId;
-using Utility = LibHac.FsSrv.Impl.Utility;
namespace LibHac.FsSrv;
@@ -19,12 +25,47 @@ file static class Anonymous
{
public static Result GetDeviceHandleByMountName(out GameCardHandle outHandle, U8Span name)
{
- throw new NotImplementedException();
+ const int handleStringLength = 8;
+
+ UnsafeHelpers.SkipParamInit(out outHandle);
+
+ if (StringUtils.GetLength(name, handleStringLength) < handleStringLength)
+ return ResultFs.InvalidPath.Log();
+
+ Span handleString = stackalloc byte[handleStringLength + 1];
+ handleString.Clear();
+ StringUtils.Copy(handleString, name, handleStringLength);
+
+ bool handleParsed = Utf8Parser.TryParse(handleString, out GameCardHandle handle, out int bytesConsumed);
+ if (!handleParsed || bytesConsumed != handleStringLength)
+ return ResultFs.InvalidPath.Log();
+
+ outHandle = handle;
+ return Result.Success;
}
public static Result GetGameCardPartitionByMountName(out GameCardPartition outPartition, U8Span name)
{
- throw new NotImplementedException();
+ if (StringUtils.Compare(name, GameCardFileSystemMountNameSuffixUpdate, 1) == 0)
+ {
+ outPartition = GameCardPartition.Update;
+ return Result.Success;
+ }
+
+ if (StringUtils.Compare(name, GameCardFileSystemMountNameSuffixNormal, 1) == 0)
+ {
+ outPartition = GameCardPartition.Normal;
+ return Result.Success;
+ }
+
+ if (StringUtils.Compare(name, GameCardFileSystemMountNameSuffixSecure, 1) == 0)
+ {
+ outPartition = GameCardPartition.Secure;
+ return Result.Success;
+ }
+
+ outPartition = default;
+ return ResultFs.InvalidPath.Log();
}
public static Result GetPartitionIndex(out int outIndex, FileSystemProxyType type)
@@ -53,15 +94,79 @@ file static class Anonymous
public static void GenerateNcaDigest(out Hash outDigest, NcaReader reader1, NcaReader reader2)
{
- throw new NotImplementedException();
+ UnsafeHelpers.SkipParamInit(out outDigest);
+
+ Assert.SdkAssert(reader1 is not null || reader2 is not null);
+
+ var generator = new Sha256Generator();
+ generator.Initialize();
+
+ if (reader1 is not null)
+ {
+ RuntimeNcaHeader header = reader1.GetHeader();
+ generator.Update(SpanHelpers.AsReadOnlyByteSpan(in header));
+ }
+
+ if (reader2 is not null)
+ {
+ RuntimeNcaHeader header = reader2.GetHeader();
+ generator.Update(SpanHelpers.AsReadOnlyByteSpan(in header));
+ }
+
+ generator.GetHash(SpanHelpers.AsByteSpan(ref outDigest));
}
- public static Result LoadNspdVerificationData(out CodeVerificationData outCodeVerificationData, IFileSystem fileSystem)
+ public static Result LoadNspdVerificationData(ref CodeVerificationData outCodeVerificationData, IFileSystem fileSystem)
{
- throw new NotImplementedException();
+ Assert.SdkRequiresNotNull(ref outCodeVerificationData);
+ Assert.SdkRequiresNotNull(fileSystem);
+
+ const int verificationDataSignatureSize = 0x100;
+ const int verificationDataHashSize = 0x20;
+ ReadOnlySpan verificationDataPath = "/verificationData"u8;
+
+ using var verificationDataFile = new UniqueRef();
+
+ using var pathVerificationData = new Path();
+ Result res = PathFunctions.SetUpFixedPath(ref pathVerificationData.Ref(), verificationDataPath);
+ if (res.IsFailure()) return res.Miss();
+
+ res = fileSystem.OpenFile(ref verificationDataFile.Ref, in pathVerificationData, OpenMode.Read);
+ if (!res.IsSuccess())
+ {
+ if (ResultFs.PathNotFound.Includes(res))
+ {
+ return ResultFs.MissingNspdVerificationData.LogConverted(res);
+ }
+
+ return res.Miss();
+ }
+
+ res = ReadData(in verificationDataFile, ref outCodeVerificationData);
+ if (res.IsFailure())
+ {
+ return ResultFs.InvalidNspdVerificationData.LogConverted(res);
+ }
+
+ return Result.Success;
+
+ static Result ReadData(in UniqueRef file, ref CodeVerificationData outData)
+ {
+ Result res = file.Get.GetSize(out long verificationDataSize);
+ if (res.IsFailure()) return res.Miss();
+
+ if (verificationDataSize != verificationDataSignatureSize + verificationDataHashSize)
+ return ResultFs.InvalidNspdVerificationData.Log();
+
+ return file.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref outData), ReadOption.None).Ret();
+ }
}
}
+///
+/// Handles locating and opening NCA content files.
+///
+/// Based on nnSdk 17.5.0 (FS 17.0.0)
public class NcaFileSystemServiceImpl : IDisposable
{
private Configuration _config;
@@ -87,7 +192,7 @@ public class NcaFileSystemServiceImpl : IDisposable
public ISubDirectoryFileSystemCreator SubDirectoryFsCreator;
public IEncryptedFileSystemCreator EncryptedFsCreator;
public INspRootFileSystemCreator NspRootFileSystemCreator;
- private LocationResolverSet LocationResolverSet;
+ public LocationResolverSet LocationResolverSet;
public ProgramRegistryServiceImpl ProgramRegistryService;
public AccessFailureManagementServiceImpl AccessFailureManagementService;
public InternalProgramIdRangeForSpeedEmulation SpeedEmulationRange;
@@ -146,101 +251,446 @@ public class NcaFileSystemServiceImpl : IDisposable
_updatePartitionPath.Dispose();
}
- public long GetAddOnContentDivisionSize()
- {
- throw new NotImplementedException();
- }
-
- public long GetRomDivisionSize()
- {
- throw new NotImplementedException();
- }
+ public long GetAddOnContentDivisionSize() => _config.AddOnContentDivisionSize;
+ public long GetRomDivisionSize() => _config.RomDivisionSize;
public Result OpenFileSystem(ref SharedRef outFileSystem, ref readonly Path path,
ContentAttributes attributes, FileSystemProxyType type, bool canMountSystemDataPrivate, ulong id,
bool isDirectory)
{
- throw new NotImplementedException();
+ return OpenFileSystem(ref outFileSystem, ref Unsafe.NullRef(), in path, attributes, type,
+ canMountSystemDataPrivate, id, isDirectory).Ret();
}
public Result OpenFileSystem(ref SharedRef outFileSystem, ref readonly Path path,
ContentAttributes attributes, FileSystemProxyType type, ulong id, bool isDirectory)
{
- throw new NotImplementedException();
+ return OpenFileSystem(ref outFileSystem, ref Unsafe.NullRef(), in path, attributes, type,
+ canMountSystemDataPrivate: false, id, isDirectory).Ret();
}
- public Result OpenFileSystem(ref SharedRef outFileSystem, out CodeVerificationData outVerificationData,
+ public Result OpenFileSystem(ref SharedRef outFileSystem, ref CodeVerificationData outVerificationData,
ref readonly Path path, ContentAttributes attributes, FileSystemProxyType type, bool canMountSystemDataPrivate,
ulong id, bool isDirectory)
{
- throw new NotImplementedException();
+ // Get a reference to the path that will be advanced as each part of the path is parsed
+ var currentPath = new U8Span(path.GetString());
+
+ var mountInfo = new MountInfo();
+
+ if (!Unsafe.IsNullRef(ref outVerificationData))
+ outVerificationData.HasData = false;
+
+ // Open the root filesystem based on the path's mount name
+ using var baseFileSystem = new SharedRef();
+ Result res = ParseMountName(ref currentPath, ref baseFileSystem.Ref, ref mountInfo);
+ if (res.IsFailure()) return res.Miss();
+
+ if (mountInfo.IsGameCard() && type == FileSystemProxyType.Logo)
+ {
+ res = _config.BaseFsService.OpenGameCardFileSystem(ref outFileSystem, mountInfo.GcHandle, GameCardPartition.Logo);
+
+ if (res.IsSuccess()) return Result.Success;
+
+ if (!ResultFs.PartitionNotFound.Includes(res))
+ return res.Miss();
+ }
+
+ if (isDirectory)
+ {
+ if (!mountInfo.IsHostOrLocalFs())
+ return ResultFs.PermissionDenied.Log();
+
+ using var directoryPath = new Path();
+ res = directoryPath.InitializeWithNormalization(currentPath.Value);
+ if (res.IsFailure()) return res.Miss();
+
+ if (type == FileSystemProxyType.Manual)
+ {
+ using var hostFileSystem = new SharedRef();
+
+ res = ParseDirWithPathCaseNormalizationOnCaseSensitiveHostOrLocalFs(ref hostFileSystem.Ref, in directoryPath, mountInfo.FsType);
+ if (res.IsFailure()) return res.Miss();
+
+ using var readOnlyFileSystem = new SharedRef(new ReadOnlyFileSystem(ref hostFileSystem.Ref));
+ outFileSystem.SetByMove(ref readOnlyFileSystem.Ref);
+
+ return Result.Success;
+ }
+
+ using var ncdFileSystem = new SharedRef();
+ res = _config.SubDirectoryFsCreator.Create(ref ncdFileSystem.Ref, in baseFileSystem, in directoryPath);
+ if (res.IsFailure()) return res.Miss();
+
+ using var fsPartitionOnNcdFileSystem = new SharedRef();
+ res = ParseContentTypeForDirectory(ref fsPartitionOnNcdFileSystem.Ref, in ncdFileSystem, type);
+ if (res.IsFailure()) return res.Miss();
+
+ if (type == FileSystemProxyType.Code)
+ {
+ if (Unsafe.IsNullRef(ref outVerificationData))
+ return ResultFs.NullptrArgument.Log();
+
+ res = LoadNspdVerificationData(ref outVerificationData, ncdFileSystem.Get);
+ if (!res.IsSuccess())
+ {
+ if (ResultFs.MissingNspdVerificationData.Includes(res))
+ {
+ outVerificationData.HasData = false;
+ outFileSystem.SetByMove(ref fsPartitionOnNcdFileSystem.Ref);
+ return Result.Success;
+ }
+
+ return res.Miss();
+ }
+
+ NpdmHash hash = default;
+ outVerificationData.Hash[..].CopyTo(hash);
+
+ using var codeFileSystem =
+ new SharedRef(new NpdmVerificationFileSystem(in fsPartitionOnNcdFileSystem, hash));
+
+ outFileSystem.SetByMove(ref codeFileSystem.Ref);
+
+ return Result.Success;
+ }
+ }
+
+ res = CheckNcaOrNsp(ref currentPath);
+ if (res.IsFailure()) return res.Miss();
+
+ bool foundNspPath;
+
+ using (SharedRef baseFileSystemCopy = SharedRef.CreateCopy(in baseFileSystem))
+ {
+ res = ParseNsp(out foundNspPath, ref currentPath, ref baseFileSystem.Ref, in baseFileSystemCopy);
+ if (res.IsFailure()) return res.Miss();
+ }
+
+ // Must be the end of the path to open Application Package FS type
+ if (foundNspPath && currentPath.Value.At(0) == 0)
+ {
+ if (type != FileSystemProxyType.Package)
+ return ResultFs.InvalidArgument.Log();
+
+ outFileSystem.SetByMove(ref baseFileSystem.Ref);
+ return Result.Success;
+ }
+
+ if (!mountInfo.CanMountNca)
+ return ResultFs.UnexpectedInNcaFileSystemServiceImplA.Log();
+
+ using var ncaReader = new SharedRef();
+ ulong openProgramId = mountInfo.IsHostOrLocalFs() ? ulong.MaxValue : id;
+ res = ParseNca(ref ncaReader.Ref, ref baseFileSystem.Ref, currentPath, attributes, openProgramId);
+ if (res.IsFailure()) return res.Miss();
+
+ using var storage = new SharedRef();
+ using var storageAccessSplitter = new SharedRef();
+ res = OpenStorageByContentType(ref storage.Ref, ref storageAccessSplitter.Ref, in ncaReader,
+ out NcaFsHeader.FsType fsType, type, mountInfo.IsGameCard(), canMountSystemDataPrivate);
+ if (res.IsFailure()) return res.Miss();
+
+ switch (fsType)
+ {
+ case NcaFsHeader.FsType.RomFs:
+ Assert.SdkAssert(type != FileSystemProxyType.Code);
+
+ return _config.RomFsCreator.Create(ref outFileSystem.Ref, in storage).Ret();
+
+ case NcaFsHeader.FsType.PartitionFs:
+ if (type == FileSystemProxyType.Code)
+ {
+ if (Unsafe.IsNullRef(ref outVerificationData))
+ return ResultFs.NullptrArgument.Log();
+
+ ncaReader.Get.GetHeaderSign2(outVerificationData.Signature);
+ ncaReader.Get.GetHeaderSign2TargetHash(outVerificationData.Hash);
+ outVerificationData.HasData = true;
+ }
+
+ return _config.PartitionFsCreator.Create(ref outFileSystem.Ref, in storage).Ret();
+ default:
+ return ResultFs.InvalidNcaFileSystemType.Log();
+ }
}
public Result OpenDataFileSystem(ref SharedRef outFileSystem, ref readonly Path path,
- ContentAttributes attributes, FileSystemProxyType fsType, ulong programId, bool isDirectory)
+ ContentAttributes attributes, FileSystemProxyType type, ulong programId, bool isDirectory)
{
- throw new NotImplementedException();
- }
+ U8Span currentPath = path.GetString();
+ var mountInfo = new MountInfo();
- public Result OpenDataFileSystem(ref SharedRef outFileSystem)
- {
- throw new NotImplementedException();
+ if (!(type == FileSystemProxyType.Rom || type == FileSystemProxyType.Data && isDirectory))
+ {
+ return ResultFs.PreconditionViolation.Log();
+ }
+
+ using var fileSystem = new SharedRef();
+ Result res = ParseMountName(ref currentPath, ref fileSystem.Ref, ref mountInfo);
+ if (res.IsFailure()) return res.Miss();
+
+ if (isDirectory)
+ {
+ if (!mountInfo.IsHostOrLocalFs())
+ return ResultFs.PreconditionViolation.Log();
+
+ using var hostFileSystem = new SharedRef();
+
+ using var directoryPath = new Path();
+ res = directoryPath.InitializeWithNormalization(currentPath);
+ if (res.IsFailure()) return res.Miss();
+
+ res = ParseDirWithPathCaseNormalizationOnCaseSensitiveHostOrLocalFs(ref hostFileSystem.Ref, in directoryPath, mountInfo.FsType);
+ if (res.IsFailure()) return res.Miss();
+
+ using var readOnlyFileSystem = new SharedRef(new ReadOnlyFileSystem(ref hostFileSystem.Ref));
+
+ outFileSystem.SetByMove(ref readOnlyFileSystem.Ref);
+ return Result.Success;
+ }
+
+ res = CheckNcaOrNsp(ref currentPath);
+ if (res.IsFailure()) return res.Miss();
+
+ bool foundNspPath;
+
+ using (SharedRef fileSystemCopy = SharedRef.CreateCopy(in fileSystem))
+ {
+ res = ParseNsp(out foundNspPath, ref currentPath, ref fileSystem.Ref, in fileSystemCopy);
+ if (res.IsFailure()) return res.Miss();
+ }
+
+ // If we found an .nsp file in the file path, the portion of the path after the .nsp file will be used to open
+ // a file inside the .nsp file.
+ // We're trying to open an .nca file, so there must be something in the path after the .nsp file.
+ if (foundNspPath && (currentPath.Length == 0 || currentPath[0] == 0))
+ return ResultFs.TargetNotFound.Log();
+
+ if (!mountInfo.CanMountNca)
+ return ResultFs.UnexpectedInNcaFileSystemServiceImplA.Log();
+
+ using var ncaReader = new SharedRef();
+ res = ParseNca(ref ncaReader.Ref, in fileSystem, currentPath, attributes, programId);
+ if (res.IsFailure()) return res.Miss();
+
+ using var storage = new SharedRef();
+ using var storageAccessSplitter = new SharedRef();
+ res = OpenStorageByContentType(ref storage.Ref, ref storageAccessSplitter.Ref, in ncaReader,
+ out NcaFsHeader.FsType fsType, type, mountInfo.IsGameCard(), canMountSystemDataPrivate: false);
+ if (res.IsFailure()) return res.Miss();
+
+ if (fsType != NcaFsHeader.FsType.RomFs)
+ return ResultFs.PreconditionViolation.Log();
+
+ return _config.RomFsCreator.Create(ref outFileSystem, in storage).Ret();
}
public Result OpenDataStorage(ref SharedRef outStorage,
- ref SharedRef outStorageAccessSplitter, out Hash outNcaHeaderDigest,
- ref readonly Path path, ContentAttributes attributes, FileSystemProxyType fsType, ulong id)
+ ref SharedRef outStorageAccessSplitter, ref Hash outNcaDigest,
+ ref readonly Path path, ContentAttributes attributes, FileSystemProxyType type, ulong id)
{
- throw new NotImplementedException();
+ return OpenDataStorage(ref outStorage, ref outStorageAccessSplitter, ref outNcaDigest, in path, attributes,
+ type, id, canMountSystemDataPrivate: false).Ret();
}
public Result OpenDataStorage(ref SharedRef outStorage,
- ref SharedRef outStorageAccessSplitter, out Hash outNcaHeaderDigest,
- ref readonly Path path, ContentAttributes attributes, FileSystemProxyType fsType, ulong id,
+ ref SharedRef outStorageAccessSplitter, ref Hash outNcaDigest,
+ ref readonly Path path, ContentAttributes attributes, FileSystemProxyType type, ulong id,
bool canMountSystemDataPrivate)
{
- throw new NotImplementedException();
+ using var ncaReader = new SharedRef();
+ Result res = ParseNca(ref ncaReader.Ref, out bool isGameCard, path.GetString(), attributes, id);
+ if (res.IsFailure()) return res.Miss();
+
+ if (!Unsafe.IsNullRef(in outNcaDigest))
+ {
+ GenerateNcaDigest(out outNcaDigest, ncaReader.Get, null);
+ }
+
+ res = OpenStorageByContentType(ref outStorage, ref outStorageAccessSplitter, in ncaReader,
+ out NcaFsHeader.FsType fsType, type, isGameCard, canMountSystemDataPrivate);
+ if (res.IsFailure()) return res.Miss();
+
+ if (fsType != NcaFsHeader.FsType.RomFs)
+ return ResultFs.PreconditionViolation.Log();
+
+ return Result.Success;
}
public Result OpenStorageWithPatch(ref SharedRef outStorage,
- ref SharedRef outStorageAccessSplitter, out Hash ncaHeaderDigest,
+ ref SharedRef outStorageAccessSplitter, ref Hash outNcaDigest,
ref readonly Path originalNcaPath, ContentAttributes originalAttributes, ref readonly Path currentNcaPath,
- ContentAttributes currentAttributes, FileSystemProxyType fsType, ulong originalId, ulong currentId)
+ ContentAttributes currentAttributes, FileSystemProxyType type, ulong originalId, ulong currentId)
{
- throw new NotImplementedException();
+ return OpenStorageWithPatch(ref outStorage, ref outStorageAccessSplitter, ref outNcaDigest,
+ in originalNcaPath, originalAttributes, in currentNcaPath, currentAttributes, type, originalId, currentId,
+ false).Ret();
}
public Result OpenStorageWithPatch(ref SharedRef outStorage,
- ref SharedRef outStorageAccessSplitter, out Hash ncaHeaderDigest,
+ ref SharedRef outStorageAccessSplitter, ref Hash outNcaDigest,
ref readonly Path originalNcaPath, ContentAttributes originalAttributes, ref readonly Path currentNcaPath,
- ContentAttributes currentAttributes, FileSystemProxyType fsType, ulong originalId, ulong currentId,
+ ContentAttributes currentAttributes, FileSystemProxyType type, ulong originalId, ulong currentId,
bool canMountSystemDataPrivate)
{
- throw new NotImplementedException();
+ Result res;
+ bool isOriginalGameCard = false;
+ using var originalNcaReader = new SharedRef();
+
+ if (!PathExtensions.IsNullRef(in originalNcaPath))
+ {
+ res = ParseNca(ref originalNcaReader.Ref, out isOriginalGameCard, originalNcaPath.GetString(),
+ originalAttributes, originalId);
+ if (res.IsFailure()) return res.Miss();
+
+ if (originalNcaReader.Get.GetDistributionType() == NcaHeader.DistributionType.GameCard && !isOriginalGameCard)
+ return ResultFs.PermissionDenied.Log();
+ }
+
+ if (isOriginalGameCard)
+ originalNcaReader.Get.PrioritizeSwAes();
+
+ using var currentNcaReader = new SharedRef();
+ res = ParseNca(ref currentNcaReader.Ref, out bool isCurrentGameCard, currentNcaPath.GetString(),
+ currentAttributes, currentId);
+ if (res.IsFailure()) return res.Miss();
+
+ if (currentNcaReader.Get.GetDistributionType() == NcaHeader.DistributionType.GameCard && !isCurrentGameCard)
+ return ResultFs.PermissionDenied.Log();
+
+ if (isCurrentGameCard)
+ currentNcaReader.Get.PrioritizeSwAes();
+
+ if (!Unsafe.IsNullRef(in outNcaDigest))
+ {
+ GenerateNcaDigest(out outNcaDigest, originalNcaReader.Get, currentNcaReader.Get);
+ }
+
+ res = OpenStorageWithPatchByContentType(ref outStorage, ref outStorageAccessSplitter, in originalNcaReader,
+ in currentNcaReader, out NcaFsHeader.FsType fsType, type, canMountSystemDataPrivate);
+ if (res.IsFailure()) return res.Miss();
+
+ if (fsType != NcaFsHeader.FsType.RomFs)
+ return ResultFs.PreconditionViolation.Log();
+
+ return Result.Success;
}
public Result OpenFileSystemWithPatch(ref SharedRef outFileSystem, ref readonly Path originalNcaPath,
ContentAttributes originalAttributes, ref readonly Path currentNcaPath, ContentAttributes currentAttributes,
- FileSystemProxyType fsType, ulong originalId, ulong currentId)
+ FileSystemProxyType type, ulong originalId, ulong currentId)
{
- throw new NotImplementedException();
+ using var storage = new SharedRef();
+ using var storageAccessSplitter = new SharedRef();
+ using var fileSystem = new SharedRef();
+
+ Result res = OpenStorageWithPatch(ref storage.Ref, ref storageAccessSplitter.Ref, ref Unsafe.NullRef(),
+ in originalNcaPath, originalAttributes, in currentNcaPath, currentAttributes, type, originalId, currentId,
+ canMountSystemDataPrivate: false);
+ if (res.IsFailure()) return res.Miss();
+
+ res = _config.RomFsCreator.Create(ref fileSystem.Ref, in storage);
+ if (res.IsFailure()) return res.Miss();
+
+ outFileSystem.SetByMove(ref fileSystem.Ref);
+ return Result.Success;
}
public Result OpenContentStorageFileSystem(ref SharedRef outFileSystem,
ContentStorageId contentStorageId)
{
- throw new NotImplementedException();
+ using var fileSystem = new SharedRef();
+ Result res;
+
+ // Open the appropriate base file system for the content storage ID
+ switch (contentStorageId)
+ {
+ case ContentStorageId.System:
+ res = _config.BaseFsService.OpenBisFileSystem(ref fileSystem.Ref, BisPartitionId.System);
+ if (res.IsFailure()) return res.Miss();
+ break;
+ case ContentStorageId.User:
+ res = _config.BaseFsService.OpenBisFileSystem(ref fileSystem.Ref, BisPartitionId.User);
+ if (res.IsFailure()) return res.Miss();
+ break;
+ case ContentStorageId.SdCard:
+ res = _config.BaseFsService.OpenSdCardProxyFileSystem(ref fileSystem.Ref);
+ if (res.IsFailure()) return res.Miss();
+ break;
+ case ContentStorageId.System0:
+ res = _config.BaseFsService.OpenBisFileSystem(ref fileSystem.Ref, BisPartitionId.System0);
+ if (res.IsFailure()) return res.Miss();
+ break;
+ default:
+ return ResultFs.InvalidArgument.Log();
+ }
+
+ Span contentStoragePathBuffer = stackalloc byte[64];
+
+ // Build the appropriate path for the content storage ID
+ if (contentStorageId == ContentStorageId.SdCard)
+ {
+ var sb = new U8StringBuilder(contentStoragePathBuffer);
+ sb.Append(StringTraits.DirectorySeparator).Append(CommonDirNames.SdCardNintendoRootDirectoryName);
+ sb.Append(StringTraits.DirectorySeparator).Append(CommonDirNames.ContentStorageDirectoryName);
+ }
+ else
+ {
+ var sb = new U8StringBuilder(contentStoragePathBuffer);
+ sb.Append(StringTraits.DirectorySeparator).Append(CommonDirNames.ContentStorageDirectoryName);
+ }
+
+ using scoped var contentStoragePath = new Path();
+ res = PathFunctions.SetUpFixedPath(ref contentStoragePath.Ref(), contentStoragePathBuffer);
+ if (res.IsFailure()) return res.Miss();
+
+ // Make sure the content storage path exists
+ res = FsSystem.Utility.EnsureDirectory(fileSystem.Get, in contentStoragePath);
+ if (res.IsFailure()) return res.Miss();
+
+ using var subDirFs = new SharedRef();
+ res = _config.SubDirectoryFsCreator.Create(ref subDirFs.Ref, in fileSystem, in contentStoragePath);
+ if (res.IsFailure()) return res.Miss();
+
+ // Only content on the SD card is encrypted
+ if (contentStorageId == ContentStorageId.SdCard)
+ {
+ using SharedRef tempFileSystem = SharedRef.CreateMove(ref subDirFs.Ref);
+ res = _config.EncryptedFsCreator.Create(ref subDirFs.Ref, in tempFileSystem,
+ IEncryptedFileSystemCreator.KeyId.Content, in _encryptionSeed);
+ if (res.IsFailure()) return res.Miss();
+ }
+
+ outFileSystem.SetByMove(ref subDirFs.Ref);
+ return Result.Success;
}
- public Result GetRightsId(out RightsId rightsId, out byte keyGeneration, ref readonly Path path,
+ public Result GetRightsId(out RightsId outRightsId, out byte outKeyGeneration, ref readonly Path path,
ContentAttributes attributes, ProgramId programId)
{
- throw new NotImplementedException();
+ UnsafeHelpers.SkipParamInit(out outRightsId, out outKeyGeneration);
+
+ using var ncaReader = new SharedRef();
+ Result res = ParseNca(ref ncaReader.Ref, out _, path.GetString(), attributes, programId.Value);
+ if (res.IsFailure()) return res.Miss();
+
+ ncaReader.Get.GetRightsId(SpanHelpers.AsByteSpan(ref outRightsId));
+ outKeyGeneration = ncaReader.Get.GetKeyGeneration();
+
+ return Result.Success;
}
public Result GetProgramId(out ProgramId outProgramId, ref readonly Path path, ContentAttributes attributes)
{
- throw new NotImplementedException();
+ UnsafeHelpers.SkipParamInit(out outProgramId);
+
+ using var ncaReader = new SharedRef();
+ Result res = ParseNca(ref ncaReader.Ref, out _, path.GetString(), attributes, ulong.MaxValue);
+ if (res.IsFailure()) return res.Miss();
+
+ outProgramId = new ProgramId(ncaReader.Get.GetProgramId());
+ return Result.Success;
}
public Result RegisterExternalKey(in RightsId rightsId, in AccessKey accessKey)
@@ -273,64 +723,438 @@ public class NcaFileSystemServiceImpl : IDisposable
updaterProgramId, isDirectory: false).Ret();
}
- private Result ParseMountName(ref U8Span path, ref SharedRef outFileSystem, out MountInfo outMountInfo)
+ private Result ParseMountName(ref U8Span path, ref SharedRef outFileSystem, ref MountInfo outMountInfo)
{
- throw new NotImplementedException();
+ outMountInfo.FsType = MountInfo.FileSystemType.None;
+ outMountInfo.CanMountNca = false;
+
+ if (StringUtils.Compare(path, GameCardFileSystemMountName,
+ GameCardFileSystemMountName.Length) == 0)
+ {
+ path = path.Slice(GameCardFileSystemMountName.Length);
+ Result res = GetGameCardPartitionByMountName(out GameCardPartition partition, path);
+ if (res.IsFailure()) return res.Miss();
+
+ path = path.Slice(1);
+ res = GetDeviceHandleByMountName(out GameCardHandle handle, path);
+ if (res.IsFailure()) return res.Miss();
+
+ path = path.Slice(8);
+ res = _config.BaseFsService.OpenGameCardFileSystem(ref outFileSystem, handle, partition);
+ if (res.IsFailure()) return res.Miss();
+
+ outMountInfo.GcHandle = handle;
+ outMountInfo.FsType = MountInfo.FileSystemType.GameCard;
+ outMountInfo.CanMountNca = true;
+ }
+
+ else if (StringUtils.Compare(path, ContentStorageSystemMountName,
+ ContentStorageSystemMountName.Length) == 0)
+ {
+ path = path.Slice(ContentStorageSystemMountName.Length);
+
+ Result res = OpenContentStorageFileSystem(ref outFileSystem, ContentStorageId.System);
+ if (res.IsFailure()) return res.Miss();
+
+ outMountInfo.CanMountNca = true;
+ }
+
+ else if (StringUtils.Compare(path, ContentStorageUserMountName,
+ ContentStorageUserMountName.Length) == 0)
+ {
+ path = path.Slice(ContentStorageUserMountName.Length);
+
+ Result res = OpenContentStorageFileSystem(ref outFileSystem, ContentStorageId.User);
+ if (res.IsFailure()) return res.Miss();
+
+ outMountInfo.CanMountNca = true;
+ }
+
+ else if (StringUtils.Compare(path, ContentStorageSdCardMountName,
+ ContentStorageSdCardMountName.Length) == 0)
+ {
+ path = path.Slice(ContentStorageSdCardMountName.Length);
+
+ Result res = OpenContentStorageFileSystem(ref outFileSystem, ContentStorageId.SdCard);
+ if (res.IsFailure()) return res.Miss();
+
+ outMountInfo.CanMountNca = true;
+ }
+
+ else if (StringUtils.Compare(path, BisCalibrationFilePartitionMountName,
+ BisCalibrationFilePartitionMountName.Length) == 0)
+ {
+ path = path.Slice(BisCalibrationFilePartitionMountName.Length);
+
+ Result res = _config.BaseFsService.OpenBisFileSystem(ref outFileSystem, BisPartitionId.CalibrationFile);
+ if (res.IsFailure()) return res.Miss();
+ }
+
+ else if (StringUtils.Compare(path, BisSafeModePartitionMountName,
+ BisSafeModePartitionMountName.Length) == 0)
+ {
+ path = path.Slice(BisSafeModePartitionMountName.Length);
+
+ Result res = _config.BaseFsService.OpenBisFileSystem(ref outFileSystem, BisPartitionId.SafeMode);
+ if (res.IsFailure()) return res.Miss();
+ }
+
+ else if (StringUtils.Compare(path, BisUserPartitionMountName,
+ BisUserPartitionMountName.Length) == 0)
+ {
+ path = path.Slice(BisUserPartitionMountName.Length);
+
+ Result res = _config.BaseFsService.OpenBisFileSystem(ref outFileSystem, BisPartitionId.User);
+ if (res.IsFailure()) return res.Miss();
+ }
+
+ else if (StringUtils.Compare(path, BisSystemPartition0MountName,
+ BisSystemPartition0MountName.Length) == 0)
+ {
+ path = path.Slice(BisSystemPartition0MountName.Length);
+
+ Result res = _config.BaseFsService.OpenBisFileSystem(ref outFileSystem, BisPartitionId.System0);
+ if (res.IsFailure()) return res.Miss();
+ }
+
+ else if (StringUtils.Compare(path, BisSystemPartitionMountName,
+ BisSystemPartitionMountName.Length) == 0)
+ {
+ path = path.Slice(BisSystemPartitionMountName.Length);
+
+ Result res = _config.BaseFsService.OpenBisFileSystem(ref outFileSystem, BisPartitionId.System);
+ if (res.IsFailure()) return res.Miss();
+ }
+
+ else if (StringUtils.Compare(path, SdCardFileSystemMountName,
+ SdCardFileSystemMountName.Length) == 0)
+ {
+ path = path.Slice(SdCardFileSystemMountName.Length);
+
+ Result res = _config.BaseFsService.OpenSdCardProxyFileSystem(ref outFileSystem);
+ if (res.IsFailure()) return res.Miss();
+ }
+
+ else if (StringUtils.Compare(path, HostRootFileSystemMountName,
+ HostRootFileSystemMountName.Length) == 0)
+ {
+ path = path.Slice(HostRootFileSystemMountName.Length);
+
+ using var rootPathEmpty = new Path();
+ Result res = rootPathEmpty.InitializeAsEmpty();
+ if (res.IsFailure()) return res.Miss();
+
+ res = OpenHostFileSystem(ref outFileSystem, in rootPathEmpty, openCaseSensitive: false);
+ if (res.IsFailure()) return res.Miss();
+
+ outMountInfo.FsType = MountInfo.FileSystemType.HostFs;
+ outMountInfo.CanMountNca = true;
+ }
+
+ else if (StringUtils.Compare(path, LocalRootFileSystemMountName,
+ LocalRootFileSystemMountName.Length) == 0)
+ {
+ path = path.Slice(LocalRootFileSystemMountName.Length);
+
+ using var rootPathEmpty = new Path();
+ Result res = rootPathEmpty.InitializeAsEmpty();
+ if (res.IsFailure()) return res.Miss();
+
+ res = _config.LocalFsCreator.Create(ref outFileSystem, in rootPathEmpty, openCaseSensitive: false);
+ if (res.IsFailure()) return res.Miss();
+
+ outMountInfo.FsType = MountInfo.FileSystemType.LocalFs;
+ outMountInfo.CanMountNca = true;
+ }
+
+ else if (StringUtils.Compare(path, RegisteredUpdatePartitionMountName,
+ RegisteredUpdatePartitionMountName.Length) == 0)
+ {
+ path = path.Slice(RegisteredUpdatePartitionMountName.Length);
+
+ Result res = OpenRegisteredUpdatePartition(ref outFileSystem);
+ if (res.IsFailure()) return res.Miss();
+
+ outMountInfo.CanMountNca = true;
+ }
+ else
+ {
+ return ResultFs.PathNotFound.Log();
+ }
+
+ if (StringUtils.GetLength(path, PathTool.EntryNameLengthMax) == 0)
+ return ResultFs.PathNotFound.Log();
+
+ if (path[0] == (byte)':')
+ {
+ path = path.Slice(1);
+ }
+
+ return Result.Success;
}
private Result CheckNcaOrNsp(ref U8Span path)
{
- throw new NotImplementedException();
+ ReadOnlySpan ncaExtension = ".nca"u8;
+ ReadOnlySpan nspExtension = ".nsp"u8;
+
+ int pathLen = StringUtils.GetLength(path);
+
+ // Now make sure the path has a content file extension
+ if (pathLen <= 4)
+ return ResultFs.PathNotFound.Log();
+
+ ReadOnlySpan fileExtension = path.Value.Slice(pathLen - 4);
+
+ if (StringUtils.CompareCaseInsensitive(fileExtension, ncaExtension) == 0)
+ return Result.Success;
+
+ if (StringUtils.CompareCaseInsensitive(fileExtension, nspExtension) == 0)
+ return Result.Success;
+
+ return ResultFs.PathNotFound.Log();
}
private Result ParseDir(ref readonly Path path, ref SharedRef outContentFileSystem,
- ref SharedRef baseFileSystem, FileSystemProxyType fsType, bool preserveUnc)
+ ref SharedRef baseFileSystem, FileSystemProxyType type, bool preserveUnc)
{
using var fileSystem = new SharedRef();
Result res = _config.SubDirectoryFsCreator.Create(ref fileSystem.Ref, baseFileSystem, path);
if (res.IsFailure()) return res.Miss();
- return ParseContentTypeForDirectory(ref outContentFileSystem, ref fileSystem.Ref, fsType);
+ return ParseContentTypeForDirectory(ref outContentFileSystem, ref fileSystem.Ref, type);
}
private Result ParseDirWithPathCaseNormalizationOnCaseSensitiveHostOrLocalFs(
ref SharedRef outFileSystem, ref readonly Path path, MountInfo.FileSystemType fsType)
{
- throw new NotImplementedException();
+ using var pathRoot = new Path();
+ using var pathData = new Path();
+
+ Result res = PathFunctions.SetUpFixedPath(ref pathData.Ref(), "/data"u8);
+ if (res.IsFailure()) return res.Miss();
+
+ res = pathRoot.Combine(in path, in pathData);
+ if (res.IsFailure()) return res.Miss();
+
+ switch (fsType)
+ {
+ case MountInfo.FileSystemType.HostFs:
+ res = OpenHostFileSystem(ref outFileSystem, in pathRoot, openCaseSensitive: true);
+ if (res.IsFailure()) return res.Miss();
+ break;
+
+ case MountInfo.FileSystemType.LocalFs:
+ res = _config.LocalFsCreator.Create(ref outFileSystem, in pathRoot, openCaseSensitive: true);
+ if (res.IsFailure()) return res.Miss();
+ break;
+
+ default:
+ Abort.UnexpectedDefault();
+ break;
+ }
+
+ return Result.Success;
}
private Result ParseNsp(out bool outFoundNspPath, ref U8Span path, ref SharedRef outFileSystem,
ref readonly SharedRef baseFileSystem)
{
- throw new NotImplementedException();
+ UnsafeHelpers.SkipParamInit(out outFoundNspPath);
+
+ ReadOnlySpan nspExtension = ".nsp"u8;
+ const int nspExtensionSize = 4;
+
+ // Search for the end of the nsp part of the path
+ int nspPathLen = 0;
+
+ while (true)
+ {
+ U8Span currentSpan = path.Slice(nspPathLen);
+
+ if (StringUtils.CompareCaseInsensitive(currentSpan, nspExtension, nspExtensionSize) == 0)
+ {
+ // The nsp filename must be the end of the entire path or the end of a path segment
+ if (currentSpan.Length <= 4 || currentSpan[4] == 0 || currentSpan[4] == (byte)'/')
+ break;
+
+ nspPathLen += nspExtensionSize;
+ }
+ else if (currentSpan.Length == 0 || currentSpan[0] == 0)
+ {
+ outFoundNspPath = false;
+ return Result.Success;
+ }
+ else
+ {
+ nspPathLen++;
+ }
+ }
+
+ nspPathLen += nspExtensionSize;
+
+ using var nspPath = new Path();
+ Result res = nspPath.InitializeWithNormalization(path, nspPathLen);
+ if (res.IsFailure()) return res.Miss();
+
+ using var fileStorage = new SharedRef(new FileStorageBasedFileSystem());
+ res = fileStorage.Get.Initialize(in baseFileSystem, in nspPath, OpenMode.Read);
+ if (res.IsFailure()) return res.Miss();
+
+ using SharedRef tempStorage = SharedRef.CreateMove(ref fileStorage.Ref);
+ res = _config.NspRootFileSystemCreator.Create(ref outFileSystem, in tempStorage);
+ if (res.IsFailure()) return res.Miss();
+
+ path = path.Slice(nspPathLen);
+ outFoundNspPath = true;
+
+ return Result.Success;
}
private Result ParseNca(ref SharedRef outNcaReader, ref readonly SharedRef baseFileSystem,
U8Span path, ContentAttributes attributes, ulong programId)
{
- throw new NotImplementedException();
+ using var fileStorage = new SharedRef(new FileStorageBasedFileSystem());
+
+ using var pathNca = new Path();
+ Result res = pathNca.InitializeWithNormalization(path);
+ if (res.IsFailure()) return res.Miss();
+
+ res = fileStorage.Get.Initialize(in baseFileSystem, in pathNca, OpenMode.Read);
+ if (res.IsFailure()) return res.Miss();
+
+ using var ncaReader = new SharedRef();
+ using SharedRef tempStorage = SharedRef.CreateMove(ref fileStorage.Ref);
+ res = _config.StorageOnNcaCreator.CreateNcaReader(ref ncaReader.Ref, in tempStorage, attributes);
+ if (res.IsFailure()) return res.Miss();
+
+ if (programId != ulong.MaxValue)
+ {
+ ulong ncaProgramId = ncaReader.Get.GetProgramId();
+
+ if (ncaProgramId != ulong.MaxValue && programId != ncaProgramId)
+ {
+ return ResultFs.InvalidNcaId.Log();
+ }
+ }
+
+ outNcaReader.SetByMove(ref ncaReader.Ref);
+
+ return Result.Success;
}
private Result ParseNca(ref SharedRef outNcaReader, out bool outIsGameCard, U8Span path,
ContentAttributes attributes, ulong programId)
{
- throw new NotImplementedException();
+ UnsafeHelpers.SkipParamInit(out outIsGameCard);
+
+ U8Span currentPath = path;
+
+ var mountInfo = new MountInfo();
+ using var fileSystem = new SharedRef();
+ Result res = ParseMountName(ref currentPath, ref fileSystem.Ref, ref mountInfo);
+ if (res.IsFailure()) return res.Miss();
+
+ outIsGameCard = mountInfo.IsGameCard();
+
+ res = CheckNcaOrNsp(ref currentPath);
+ if (res.IsFailure()) return res.Miss();
+
+ bool foundNspPath;
+
+ using (SharedRef fileSystemCopy = SharedRef.CreateCopy(in fileSystem))
+ {
+ res = ParseNsp(out foundNspPath, ref currentPath, ref fileSystem.Ref, in fileSystemCopy);
+ if (res.IsFailure()) return res.Miss();
+ }
+
+ // If we found an .nsp file in the file path, the portion of the path after the .nsp file will be used to open
+ // a file inside the .nsp file.
+ // We're trying to open an .nca file, so there must be something in the path after the .nsp file.
+ if (foundNspPath && (currentPath.Length == 0 || currentPath[0] == 0))
+ return ResultFs.TargetNotFound.Log();
+
+ if (!mountInfo.CanMountNca)
+ return ResultFs.UnexpectedInNcaFileSystemServiceImplA.Log();
+
+ res = ParseNca(ref outNcaReader, in fileSystem, currentPath, attributes, programId);
+ if (res.IsFailure()) return res.Miss();
+
+ return Result.Success;
}
private Result ParseContentTypeForDirectory(ref SharedRef outFileSystem,
- ref readonly SharedRef baseFileSystem, FileSystemProxyType fsType)
+ ref readonly SharedRef baseFileSystem, FileSystemProxyType type)
{
- throw new NotImplementedException();
+ Span directoryPathBuffer = stackalloc byte[0x10];
+
+ // Get the name of the subdirectory for the filesystem type
+ switch (type)
+ {
+ case FileSystemProxyType.Code:
+ StringUtils.Strlcpy(directoryPathBuffer, "/code"u8, 16);
+ break;
+
+ case FileSystemProxyType.Logo:
+ StringUtils.Strlcpy(directoryPathBuffer, "/logo"u8, 16);
+ break;
+
+ case FileSystemProxyType.Rom:
+ case FileSystemProxyType.Control:
+ case FileSystemProxyType.Manual:
+ case FileSystemProxyType.Meta:
+ case FileSystemProxyType.RegisteredUpdate:
+ StringUtils.Strlcpy(directoryPathBuffer, "/data"u8, 16);
+ break;
+
+ case FileSystemProxyType.Package:
+ outFileSystem.SetByCopy(in baseFileSystem);
+ return Result.Success;
+
+ default:
+ return ResultFs.InvalidArgument.Log();
+ }
+
+ using scoped var directoryPath = new Path();
+ Result res = PathFunctions.SetUpFixedPath(ref directoryPath.Ref(), directoryPathBuffer);
+ if (res.IsFailure()) return res.Miss();
+
+ if (directoryPath.IsEmpty())
+ return ResultFs.InvalidArgument.Log();
+
+ // Open the subdirectory filesystem
+ using var fileSystem = new SharedRef();
+ res = _config.SubDirectoryFsCreator.Create(ref fileSystem.Ref, in baseFileSystem, in directoryPath);
+ if (res.IsFailure()) return res.Miss();
+
+ outFileSystem.SetByMove(ref fileSystem.Ref);
+ return Result.Success;
}
public Result SetExternalKeyForRightsId(NcaReader ncaReader)
{
- throw new NotImplementedException();
+ Span zeroRightsId = stackalloc byte[Unsafe.SizeOf()];
+ Span rightsId = stackalloc byte[Unsafe.SizeOf()];
+
+ zeroRightsId.Clear();
+ ncaReader.GetRightsId(rightsId);
+
+ bool hasRightsId = !Crypto.CryptoUtil.IsSameBytes(rightsId, zeroRightsId, Unsafe.SizeOf());
+
+ if (hasRightsId)
+ {
+ Result res = _externalKeyManager.Find(out AccessKey keySource, SpanHelpers.AsStruct(rightsId));
+ if (res.IsFailure()) return res.Miss();
+
+ ncaReader.SetExternalDecryptionKey(in keySource);
+ }
+
+ return Result.Success;
}
public bool IsAvailableKeySource(ReadOnlySpan keySource)
{
- throw new NotImplementedException();
+ return _externalKeyManager.IsAvailableKeySource(keySource);
}
public Result OpenStorageByContentType(ref SharedRef outNcaStorage,
@@ -338,7 +1162,81 @@ public class NcaFileSystemServiceImpl : IDisposable
ref readonly SharedRef ncaReader, out NcaFsHeader.FsType outFsType, FileSystemProxyType fsProxyType,
bool isGameCard, bool canMountSystemDataPrivate)
{
- throw new NotImplementedException();
+ UnsafeHelpers.SkipParamInit(out outFsType);
+
+ NcaHeader.ContentType contentType = ncaReader.Get.GetContentType();
+
+ switch (fsProxyType)
+ {
+ case FileSystemProxyType.Code:
+ if (contentType != NcaHeader.ContentType.Program)
+ return ResultFs.PreconditionViolation.Log();
+ break;
+
+ case FileSystemProxyType.Rom:
+ if (contentType != NcaHeader.ContentType.Program)
+ return ResultFs.PreconditionViolation.Log();
+ break;
+
+ case FileSystemProxyType.Logo:
+ if (contentType != NcaHeader.ContentType.Program)
+ return ResultFs.PreconditionViolation.Log();
+ break;
+
+ case FileSystemProxyType.Control:
+ if (contentType != NcaHeader.ContentType.Control)
+ return ResultFs.PreconditionViolation.Log();
+ break;
+
+ case FileSystemProxyType.Manual:
+ if (contentType != NcaHeader.ContentType.Manual)
+ return ResultFs.PreconditionViolation.Log();
+ break;
+
+ case FileSystemProxyType.Meta:
+ if (contentType != NcaHeader.ContentType.Meta)
+ return ResultFs.PreconditionViolation.Log();
+ break;
+
+ case FileSystemProxyType.Data:
+ if (contentType != NcaHeader.ContentType.Data && contentType != NcaHeader.ContentType.PublicData)
+ return ResultFs.PreconditionViolation.Log();
+ break;
+
+ case FileSystemProxyType.RegisteredUpdate:
+ if (contentType != NcaHeader.ContentType.Program)
+ return ResultFs.PreconditionViolation.Log();
+ break;
+
+ default:
+ return ResultFs.InvalidArgument.Log();
+ }
+
+ if (contentType == NcaHeader.ContentType.Data && !canMountSystemDataPrivate)
+ return ResultFs.PermissionDenied.Log();
+
+ if (ncaReader.Get.GetDistributionType() == NcaHeader.DistributionType.GameCard && !isGameCard)
+ return ResultFs.PermissionDenied.Log();
+
+ if (isGameCard)
+ {
+ ncaReader.Get.PrioritizeSwAes();
+ }
+
+ Result res = SetExternalKeyForRightsId(ncaReader.Get);
+ if (res.IsFailure()) return res.Miss();
+
+ res = GetPartitionIndex(out int partitionIndex, fsProxyType);
+ if (res.IsFailure()) return res.Miss();
+
+ var ncaFsHeaderReader = new NcaFsHeaderReader();
+
+ res = _config.StorageOnNcaCreator.Create(ref outNcaStorage, ref outStorageAccessSplitter, ref ncaFsHeaderReader,
+ in ncaReader, partitionIndex);
+ if (res.IsFailure()) return res.Miss();
+
+ outFsType = ncaFsHeaderReader.GetFsType();
+ return Result.Success;
}
public Result OpenStorageWithPatchByContentType(ref SharedRef outNcaStorage,
@@ -346,7 +1244,57 @@ public class NcaFileSystemServiceImpl : IDisposable
ref readonly SharedRef originalNcaReader, ref readonly SharedRef currentNcaReader,
out NcaFsHeader.FsType outFsType, FileSystemProxyType fsProxyType, bool canMountSystemDataPrivate)
{
- throw new NotImplementedException();
+ UnsafeHelpers.SkipParamInit(out outFsType);
+
+ NcaHeader.ContentType contentType = currentNcaReader.Get.GetContentType();
+
+ switch (fsProxyType)
+ {
+ case FileSystemProxyType.Rom:
+ if (contentType != NcaHeader.ContentType.Program)
+ return ResultFs.PreconditionViolation.Log();
+ break;
+
+ case FileSystemProxyType.Manual:
+ if (contentType != NcaHeader.ContentType.Manual)
+ return ResultFs.PreconditionViolation.Log();
+ break;
+
+ case FileSystemProxyType.Data:
+ if (contentType != NcaHeader.ContentType.Data && contentType != NcaHeader.ContentType.PublicData)
+ return ResultFs.PreconditionViolation.Log();
+ break;
+
+ default:
+ return ResultFs.InvalidArgument.Log();
+ }
+
+ if (contentType == NcaHeader.ContentType.Data && !canMountSystemDataPrivate)
+ return ResultFs.PermissionDenied.Log();
+
+ Result res = SetExternalKeyForRightsId(currentNcaReader.Get);
+ if (res.IsFailure()) return res.Miss();
+
+ if (originalNcaReader.HasValue)
+ {
+ if (originalNcaReader.Get.GetContentType() != contentType)
+ return ResultFs.PreconditionViolation.Log();
+
+ res = SetExternalKeyForRightsId(originalNcaReader.Get);
+ if (res.IsFailure()) return res.Miss();
+ }
+
+ res = GetPartitionIndex(out int partitionIndex, fsProxyType);
+ if (res.IsFailure()) return res.Miss();
+
+ var ncaFsHeaderReader = new NcaFsHeaderReader();
+
+ res = _config.StorageOnNcaCreator.CreateWithPatch(ref outNcaStorage, ref outStorageAccessSplitter,
+ ref ncaFsHeaderReader, in originalNcaReader, in currentNcaReader, partitionIndex);
+ if (res.IsFailure()) return res.Miss();
+
+ outFsType = ncaFsHeaderReader.GetFsType();
+ return Result.Success;
}
public Result SetSdCardEncryptionSeed(in EncryptionSeed encryptionSeed)
@@ -369,60 +1317,122 @@ public class NcaFileSystemServiceImpl : IDisposable
return Result.Success;
}
- public Result ResolveProgramPath(out bool isDirectory, ref Path outPath, out ContentAttributes outContentAttributes,
+ public Result ResolveProgramPath(out bool outIsDirectory, ref Path outPath, out ContentAttributes outContentAttributes,
ProgramId programId, StorageId storageId)
{
- throw new NotImplementedException();
+ Result res = _config.LocationResolverSet.ResolveProgramPath(out outIsDirectory, ref outPath,
+ out outContentAttributes, programId.Value, storageId);
+ if (res.IsSuccess())
+ return Result.Success;
+
+ outIsDirectory = false;
+
+ res = _config.LocationResolverSet.ResolveDataPath(ref outPath, out outContentAttributes,
+ new DataId(programId.Value), storageId);
+ if (res.IsSuccess())
+ return Result.Success;
+
+ return ResultFs.TargetNotFound.Log();
}
public Result ResolveApplicationControlPath(ref Path outPath, out ContentAttributes outContentAttributes,
- ApplicationId applicationId, StorageId storageId)
+ Ncm.ApplicationId applicationId, StorageId storageId)
{
- throw new NotImplementedException();
+ return _config.LocationResolverSet
+ .ResolveApplicationControlPath(ref outPath, out outContentAttributes, applicationId, storageId).Ret();
}
- public Result ResolveRomPath(out bool isDirectory, ref Path outPath, out ContentAttributes outContentAttributes,
+ public Result ResolveRomPath(out bool outIsDirectory, ref Path outPath, out ContentAttributes outContentAttributes,
out ulong outOriginalProgramId, ulong programId, StorageId storageId)
{
- throw new NotImplementedException();
+ UnsafeHelpers.SkipParamInit(out outOriginalProgramId);
+
+ Result res = _config.LocationResolverSet.ResolveRomPath(out outIsDirectory, ref outPath,
+ out outContentAttributes, programId, storageId);
+
+ if (!res.IsSuccess())
+ {
+ if (ResultLr.DebugProgramNotFound.Includes(res))
+ {
+ ProgramId targetProgramId = _config.ProgramRegistryService.GetApplicationProgramProgramIdByPatchProgramProgramId(new ProgramId(programId));
+ if (targetProgramId == ProgramId.InvalidId)
+ return res.Rethrow();
+
+ res = _config.LocationResolverSet.ResolveRomPath(out outIsDirectory, ref outPath,
+ out outContentAttributes, targetProgramId.Value, storageId);
+ if (res.IsFailure()) return res.Miss();
+
+ outOriginalProgramId = targetProgramId.Value;
+ }
+
+ return res.Miss();
+ }
+
+ outOriginalProgramId = programId;
+ return Result.Success;
}
- public Result ResolveApplicationHtmlDocumentPath(out bool isDirectory, ref Path outPath,
+ public Result ResolveApplicationHtmlDocumentPath(out bool outIsDirectory, ref Path outPath,
out ContentAttributes outContentAttributes, out ulong outOriginalProgramId, ulong programId,
StorageId storageId)
{
- throw new NotImplementedException();
+ UnsafeHelpers.SkipParamInit(out outOriginalProgramId);
+
+ Result res = _config.LocationResolverSet.ResolveApplicationHtmlDocumentPath(out outIsDirectory, ref outPath,
+ out outContentAttributes, programId, storageId);
+
+ if (!res.IsSuccess())
+ {
+ if (ResultLr.HtmlDocumentNotFound.Includes(res))
+ {
+ ProgramId targetProgramId = _config.ProgramRegistryService.GetApplicationHtmlDocumentProgramIdByPatchProgramProgramId(new ProgramId(programId));
+ if (targetProgramId == ProgramId.InvalidId)
+ return res.Rethrow();
+
+ res = _config.LocationResolverSet.ResolveApplicationHtmlDocumentPath(out outIsDirectory, ref outPath,
+ out outContentAttributes, targetProgramId.Value, storageId);
+ if (res.IsFailure()) return res.Miss();
+
+ outOriginalProgramId = targetProgramId.Value;
+ }
+
+ return res.Miss();
+ }
+
+ outOriginalProgramId = programId;
+ return Result.Success;
}
public Result ResolveDataPath(ref Path outPath, out ContentAttributes outContentAttributes, DataId dataId,
StorageId storageId)
{
- throw new NotImplementedException();
+ return _config.LocationResolverSet.ResolveDataPath(ref outPath, out outContentAttributes, dataId, storageId).Ret();
}
public Result ResolveAddOnContentPath(ref Path outPath, out ContentAttributes outContentAttributes,
ref Path outPatchPath, out ContentAttributes outPatchContentAttributes, DataId dataId)
{
- throw new NotImplementedException();
+ return _config.LocationResolverSet.ResolveAddOnContentPath(ref outPath, out outContentAttributes,
+ ref outPatchPath, out outPatchContentAttributes, dataId).Ret();
}
public Result ResolveRegisteredProgramPath(ref Path outPath, out ContentAttributes outContentAttributes,
ulong programId)
{
- throw new NotImplementedException();
+ return _config.LocationResolverSet.ResolveRegisteredProgramPath(ref outPath, out outContentAttributes, programId).Ret();
}
public Result ResolveRegisteredHtmlDocumentPath(ref Path outPath, out ContentAttributes outContentAttributes,
ulong programId)
{
- throw new NotImplementedException();
+ return _config.LocationResolverSet.ResolveRegisteredHtmlDocumentPath(ref outPath, out outContentAttributes, programId).Ret();
}
internal StorageLayoutType GetStorageFlag(ulong programId)
{
Assert.SdkRequiresNotEqual(_config.SpeedEmulationRange.ProgramIdWithoutPlatformIdMax, 0ul);
- ulong programIdWithoutPlatformId = Utility.ClearPlatformIdInProgramId(programId);
+ ulong programIdWithoutPlatformId = Impl.Utility.ClearPlatformIdInProgramId(programId);
if (programIdWithoutPlatformId >= _config.SpeedEmulationRange.ProgramIdWithoutPlatformIdMin &&
programIdWithoutPlatformId <= _config.SpeedEmulationRange.ProgramIdWithoutPlatformIdMax)
@@ -489,12 +1499,12 @@ public class NcaFileSystemServiceImpl : IDisposable
public Result CreateNotifier(ref UniqueRef outNotifier)
{
- throw new NotImplementedException();
+ return _systemDataUpdateEventManager.CreateNotifier(ref outNotifier).Ret();
}
public Result NotifySystemDataUpdateEvent()
{
- throw new NotImplementedException();
+ return _systemDataUpdateEventManager.NotifySystemDataUpdateEvent().Ret();
}
public Result OpenHostFileSystem(ref SharedRef outFileSystem, ref readonly Path rootPath, bool openCaseSensitive)
@@ -502,18 +1512,6 @@ public class NcaFileSystemServiceImpl : IDisposable
return _config.TargetManagerFsCreator.Create(ref outFileSystem, in rootPath, openCaseSensitive, false,
Result.Success).Ret();
}
-
- internal Result GetProgramInfoByProcessId(out ProgramInfo programInfo, ulong processId)
- {
- var registry = new ProgramRegistryImpl(_config.FsServer);
- return registry.GetProgramInfo(out programInfo, processId);
- }
-
- internal Result GetProgramInfoByProgramId(out ProgramInfo programInfo, ulong programId)
- {
- var registry = new ProgramRegistryImpl(_config.FsServer);
- return registry.GetProgramInfoByProgramId(out programInfo, programId);
- }
}
public readonly struct InternalProgramIdRangeForSpeedEmulation
diff --git a/src/LibHac/FsSrv/ProgramRegistryImpl.cs b/src/LibHac/FsSrv/ProgramRegistryImpl.cs
index 92eddbf7..cf7c810c 100644
--- a/src/LibHac/FsSrv/ProgramRegistryImpl.cs
+++ b/src/LibHac/FsSrv/ProgramRegistryImpl.cs
@@ -19,7 +19,7 @@ internal struct ProgramRegistryImplGlobals
/// is stored in a and includes the process' process ID, program ID,
/// storage location and file system permissions. This allows FS to resolve the program ID and
/// verify the permissions of any process calling it.
-/// Based on nnSdk 13.4.0 (FS 13.1.0)
+/// Based on nnSdk 17.5.0 (FS 17.0.0)
public class ProgramRegistryImpl : IProgramRegistry
{
private ulong _processId;
@@ -55,8 +55,9 @@ public class ProgramRegistryImpl : IProgramRegistry
if (accessControlDescriptorSize > accessControlDescriptor.Size)
return ResultFs.InvalidSize.Log();
- return Globals.ServiceImpl.RegisterProgramInfo(processId, programId, storageId, accessControlData.Buffer,
- accessControlDescriptor.Buffer);
+ return Globals.ServiceImpl.RegisterProgramInfo(processId, programId, storageId,
+ accessControlData.Buffer.Slice(0, (int)accessControlDataSize),
+ accessControlDescriptor.Buffer.Slice(0, (int)accessControlDescriptorSize)).Ret();
}
/// : The operation was successful.
@@ -70,7 +71,7 @@ public class ProgramRegistryImpl : IProgramRegistry
if (!_fsServer.IsInitialProgram(_processId))
return ResultFs.PermissionDenied.Log();
- return Globals.ServiceImpl.UnregisterProgramInfo(processId);
+ return Globals.ServiceImpl.UnregisterProgramInfo(processId).Ret();
}
///
@@ -78,7 +79,7 @@ public class ProgramRegistryImpl : IProgramRegistry
{
Assert.SdkRequiresNotNull(Globals.ServiceImpl);
- return Globals.ServiceImpl.GetProgramInfo(out programInfo, processId);
+ return Globals.ServiceImpl.GetProgramInfo(out programInfo, processId).Ret();
}
///
@@ -86,7 +87,7 @@ public class ProgramRegistryImpl : IProgramRegistry
{
Assert.SdkRequiresNotNull(Globals.ServiceImpl);
- return Globals.ServiceImpl.GetProgramInfoByProgramId(out programInfo, programId);
+ return Globals.ServiceImpl.GetProgramInfoByProgramId(out programInfo, programId).Ret();
}
///
diff --git a/src/LibHac/FsSrv/ProgramRegistryService.cs b/src/LibHac/FsSrv/ProgramRegistryService.cs
index 1b601dd1..bc78b82b 100644
--- a/src/LibHac/FsSrv/ProgramRegistryService.cs
+++ b/src/LibHac/FsSrv/ProgramRegistryService.cs
@@ -1,5 +1,4 @@
using System;
-using System.Runtime.InteropServices;
using LibHac.Common;
using LibHac.Fs;
using LibHac.FsSrv.Impl;
@@ -14,7 +13,7 @@ namespace LibHac.FsSrv;
///
/// Appropriate methods calls on IFileSystemProxy are forwarded to this class
/// which then checks the calling process' permissions and performs the requested operation.
-/// Based on nnSdk 13.4.0 (FS 13.1.0)
+/// Based on nnSdk 17.5.0 (FS 17.0.0)
internal readonly struct ProgramIndexRegistryService
{
private readonly ProgramRegistryServiceImpl _serviceImpl;
@@ -53,11 +52,14 @@ internal readonly struct ProgramIndexRegistryService
return ResultFs.PermissionDenied.Log();
}
- // Return early if the program count is 0 so we leave any previously
+ // Return early if the program count is 0, so we leave any previously
// registered entries as they were
if (programCount == 0)
return Result.Success;
+ if (programIndexMapInfo.IsNull)
+ return ResultFs.NullptrArgument.Log();
+
// Verify that the provided buffer is large enough to hold "programCount" entries
ReadOnlySpan mapInfo = programIndexMapInfo.AsSpan();
@@ -65,7 +67,7 @@ internal readonly struct ProgramIndexRegistryService
return ResultFs.InvalidSize.Log();
// Register the map info
- return _serviceImpl.ResetProgramIndexMapInfo(mapInfo.Slice(0, programCount));
+ return _serviceImpl.ResetProgramIndexMapInfo(mapInfo.Slice(0, programCount)).Ret();
}
///
@@ -88,10 +90,10 @@ internal readonly struct ProgramIndexRegistryService
if (res.IsFailure()) return res.Miss();
// Try to get map info for this process
- Optional mapInfo = _serviceImpl.GetProgramIndexMapInfo(programInfo.ProgramId);
+ Optional programMapInfo = _serviceImpl.GetProgramIndexMapInfo(programInfo.ProgramId);
// Set the output program index if map info was found
- programIndex = mapInfo.HasValue ? mapInfo.ValueRo.ProgramIndex : 0;
+ programIndex = programMapInfo.HasValue ? programMapInfo.ValueRo.ProgramIndex : 0;
// Set the number of programs in the current application
programCount = _serviceImpl.GetProgramIndexMapInfoCount();
@@ -110,7 +112,7 @@ internal readonly struct ProgramIndexRegistryService
///
/// Manages the main program registry and the multi-program registry.
///
-/// Based on nnSdk 13.4.0 (FS 13.1.0)
+/// Based on nnSdk 17.5.0 (FS 17.0.0)
public class ProgramRegistryServiceImpl : IDisposable
{
// ReSharper disable once PrivateFieldCanBeConvertedToLocalVariable
@@ -142,37 +144,31 @@ public class ProgramRegistryServiceImpl : IDisposable
ReadOnlySpan accessControlData, ReadOnlySpan accessControlDescriptor)
{
return _registryManager.RegisterProgram(processId, programId, storageId, accessControlData,
- accessControlDescriptor);
+ accessControlDescriptor).Ret();
}
///
public Result UnregisterProgramInfo(ulong processId)
{
- return _registryManager.UnregisterProgram(processId);
+ return _registryManager.UnregisterProgram(processId).Ret();
}
///
public Result GetProgramInfo(out ProgramInfo programInfo, ulong processId)
{
- return _registryManager.GetProgramInfo(out programInfo, processId);
+ return _registryManager.GetProgramInfo(out programInfo, processId).Ret();
}
///
public Result GetProgramInfoByProgramId(out ProgramInfo programInfo, ulong programId)
{
- return _registryManager.GetProgramInfoByProgramId(out programInfo, programId);
+ return _registryManager.GetProgramInfoByProgramId(out programInfo, programId).Ret();
}
///
public Result ResetProgramIndexMapInfo(ReadOnlySpan programIndexMapInfo)
{
- return _programIndexManager.Reset(programIndexMapInfo);
- }
-
- ///
- public ProgramId GetProgramIdByIndex(ProgramId programId, byte programIndex)
- {
- return _programIndexManager.GetProgramId(programId, programIndex);
+ return _programIndexManager.Reset(programIndexMapInfo).Ret();
}
///
@@ -181,6 +177,12 @@ public class ProgramRegistryServiceImpl : IDisposable
return _programIndexManager.Get(programId);
}
+ ///
+ public ProgramId GetProgramIdByIndex(ProgramId programId, byte programIndex)
+ {
+ return _programIndexManager.GetProgramId(programId, programIndex);
+ }
+
///
/// Gets the number of programs in the currently registered application.
///
@@ -189,4 +191,14 @@ public class ProgramRegistryServiceImpl : IDisposable
{
return _programIndexManager.GetProgramCount();
}
+
+ public ProgramId GetApplicationProgramProgramIdByPatchProgramProgramId(ProgramId programId)
+ {
+ return _programIndexManager.GetApplicationProgramId(programId);
+ }
+
+ public ProgramId GetApplicationHtmlDocumentProgramIdByPatchProgramProgramId(ProgramId programId)
+ {
+ return GetApplicationProgramProgramIdByPatchProgramProgramId(programId);
+ }
}
\ No newline at end of file
diff --git a/src/LibHac/FsSrv/SaveDataFileSystemServiceImpl.cs b/src/LibHac/FsSrv/SaveDataFileSystemServiceImpl.cs
index 6a5c7fcc..90ec04e2 100644
--- a/src/LibHac/FsSrv/SaveDataFileSystemServiceImpl.cs
+++ b/src/LibHac/FsSrv/SaveDataFileSystemServiceImpl.cs
@@ -1191,7 +1191,7 @@ public class SaveDataFileSystemServiceImpl : IDisposable
res = Utility.WrapSubDirectory(ref baseFileSystem.Ref, ref tempFileSystem.Ref, in pathSdRoot, createIfMissing);
if (res.IsFailure()) return res.Miss();
- res = _config.EncryptedFsCreator.Create(ref outFileSystem, ref baseFileSystem.Ref,
+ res = _config.EncryptedFsCreator.Create(ref outFileSystem, in baseFileSystem,
IEncryptedFileSystemCreator.KeyId.Save, in _encryptionSeed);
if (res.IsFailure()) return res.Miss();
diff --git a/src/LibHac/FsSystem/ConcatenationFileSystem.cs b/src/LibHac/FsSystem/ConcatenationFileSystem.cs
index 6502271a..b4b6cfa1 100644
--- a/src/LibHac/FsSystem/ConcatenationFileSystem.cs
+++ b/src/LibHac/FsSystem/ConcatenationFileSystem.cs
@@ -490,7 +490,7 @@ public class ConcatenationFileSystem : IFileSystem
}
}
- public static readonly long DefaultInternalFileSize = 0xFFFF0000; // Hard-coded value used by FS
+ public const uint DefaultInternalFileSize = 0xFFFF0000; // Hard-coded value used by FS
private UniqueRef _baseFileSystem;
private long _internalFileSize;
diff --git a/src/LibHac/FsSystem/NcaFileSystemDriver.cs b/src/LibHac/FsSystem/NcaFileSystemDriver.cs
index 877ad27d..5e4eac52 100644
--- a/src/LibHac/FsSystem/NcaFileSystemDriver.cs
+++ b/src/LibHac/FsSystem/NcaFileSystemDriver.cs
@@ -204,13 +204,13 @@ public class NcaFileSystemDriver : IDisposable
}
public Result OpenStorage(ref SharedRef outStorage,
- ref SharedRef outStorageAccessSplitter, out NcaFsHeaderReader outHeaderReader,
+ ref SharedRef outStorageAccessSplitter, ref NcaFsHeaderReader outHeaderReader,
int fsIndex)
{
throw new NotImplementedException();
}
- private Result OpenStorageImpl(ref SharedRef outStorage, out NcaFsHeaderReader outHeaderReader,
+ private Result OpenStorageImpl(ref SharedRef outStorage, ref NcaFsHeaderReader outHeaderReader,
int fsIndex, ref StorageContext storageContext)
{
throw new NotImplementedException();
diff --git a/src/LibHac/Lr/AddOnContentLocationResolver.cs b/src/LibHac/Lr/AddOnContentLocationResolver.cs
index 6758e624..043cb75b 100644
--- a/src/LibHac/Lr/AddOnContentLocationResolver.cs
+++ b/src/LibHac/Lr/AddOnContentLocationResolver.cs
@@ -33,4 +33,13 @@ public class AddOnContentLocationResolver : IDisposable
public Result UnregisterApplicationAddOnContent(Ncm.ApplicationId id) =>
_interface.Get.UnregisterApplicationAddOnContent(id);
+
+ public Result GetRegisteredAddOnContentPaths(out Path outPath, out Path outPatchPath, DataId id) =>
+ _interface.Get.GetRegisteredAddOnContentPaths(out outPath, out outPatchPath, id);
+
+ public Result RegisterAddOnContentPath(DataId id, ApplicationId applicationId, in Path path) =>
+ _interface.Get.RegisterAddOnContentPath(id, applicationId, in path);
+
+ public Result RegisterAddOnContentPaths(DataId id, ApplicationId applicationId, in Path path, in Path patchPath) =>
+ _interface.Get.RegisterAddOnContentPaths(id, applicationId, in path, in patchPath);
}
\ No newline at end of file
diff --git a/src/LibHac/Lr/IAddOnContentLocationResolver.cs b/src/LibHac/Lr/IAddOnContentLocationResolver.cs
index e4d46606..24619132 100644
--- a/src/LibHac/Lr/IAddOnContentLocationResolver.cs
+++ b/src/LibHac/Lr/IAddOnContentLocationResolver.cs
@@ -6,9 +6,12 @@ namespace LibHac.Lr;
public interface IAddOnContentLocationResolver : IDisposable
{
- Result ResolveAddOnContentPath(out Path path, DataId id);
+ Result ResolveAddOnContentPath(out Path outPath, DataId id);
Result RegisterAddOnContentStorage(DataId id, Ncm.ApplicationId applicationId, StorageId storageId);
Result UnregisterAllAddOnContentPath();
Result RefreshApplicationAddOnContent(InArray ids);
Result UnregisterApplicationAddOnContent(Ncm.ApplicationId id);
+ Result GetRegisteredAddOnContentPaths(out Path outPath, out Path outPatchPath, DataId id);
+ Result RegisterAddOnContentPath(DataId id, ApplicationId applicationId, in Path path);
+ Result RegisterAddOnContentPaths(DataId id, ApplicationId applicationId, in Path path, in Path patchPath);
}
\ No newline at end of file
diff --git a/src/LibHac/Tools/FsSystem/AesXtsFileSystem.cs b/src/LibHac/Tools/FsSystem/AesXtsFileSystem.cs
index 5a4ee0ea..97eec75b 100644
--- a/src/LibHac/Tools/FsSystem/AesXtsFileSystem.cs
+++ b/src/LibHac/Tools/FsSystem/AesXtsFileSystem.cs
@@ -17,9 +17,9 @@ public class AesXtsFileSystem : IFileSystem
private byte[] _kekSource;
private byte[] _validationKey;
- public AesXtsFileSystem(ref SharedRef fs, byte[] keys, int blockSize)
+ public AesXtsFileSystem(ref readonly SharedRef fs, byte[] keys, int blockSize)
{
- _sharedBaseFileSystem = SharedRef.CreateMove(ref fs);
+ _sharedBaseFileSystem = SharedRef.CreateCopy(in fs);
_baseFileSystem = _sharedBaseFileSystem.Get;
_kekSource = keys.AsSpan(0, 0x10).ToArray();
_validationKey = keys.AsSpan(0x10, 0x10).ToArray();
diff --git a/src/LibHac/Tools/FsSystem/RomFs/RomFsFileSystem.cs b/src/LibHac/Tools/FsSystem/RomFs/RomFsFileSystem.cs
index f6d8c279..9bb7c429 100644
--- a/src/LibHac/Tools/FsSystem/RomFs/RomFsFileSystem.cs
+++ b/src/LibHac/Tools/FsSystem/RomFs/RomFsFileSystem.cs
@@ -26,9 +26,9 @@ public class RomFsFileSystem : IFileSystem
FileTable = new HierarchicalRomFileTable(dirHashTable, dirEntryTable, fileHashTable, fileEntryTable);
}
- public RomFsFileSystem(ref SharedRef storage) : this(storage.Get)
+ public RomFsFileSystem(ref readonly SharedRef storage) : this(storage.Get)
{
- _baseStorageShared = SharedRef.CreateMove(ref storage);
+ _baseStorageShared = SharedRef.CreateCopy(in storage);
}
public override void Dispose()
diff --git a/tests/LibHac.Tests/LibHac.Tests.csproj.DotSettings b/tests/LibHac.Tests/LibHac.Tests.csproj.DotSettings
index 19c37353..46c92a72 100644
--- a/tests/LibHac.Tests/LibHac.Tests.csproj.DotSettings
+++ b/tests/LibHac.Tests/LibHac.Tests.csproj.DotSettings
@@ -1,2 +1,4 @@
- <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"><ExtraRule Prefix="I" Suffix="" Style="AaBb" /></Policy>
\ No newline at end of file
+ <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"><ExtraRule Prefix="I" Suffix="" Style="AaBb" /></Policy>
+ <Policy><Descriptor Staticness="Any" AccessRightKinds="Any" Description="Types and namespaces"><ElementKinds><Kind Name="NAMESPACE" /><Kind Name="CLASS" /><Kind Name="STRUCT" /><Kind Name="ENUM" /><Kind Name="DELEGATE" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"><ExtraRule Prefix="I" Suffix="" Style="AaBb" /></Policy></Policy>
+ True
\ No newline at end of file