diff --git a/src/LibHac/Common/StringUtils.cs b/src/LibHac/Common/StringUtils.cs index 364d8396..b4501ea7 100644 --- a/src/LibHac/Common/StringUtils.cs +++ b/src/LibHac/Common/StringUtils.cs @@ -65,21 +65,19 @@ namespace LibHac.Common return iDest; } - - public static string FromUtf8Z(this Span value) => FromUtf8Z((ReadOnlySpan)value); - public static string FromUtf8Z(this ReadOnlySpan value) + public static string Utf8ToString(ReadOnlySpan value) { - int i; - for (i = 0; i < value.Length && value[i] != 0; i++) { } - - value = value.Slice(0, i); - #if STRING_SPAN return Encoding.UTF8.GetString(value); #else return Encoding.UTF8.GetString(value.ToArray()); #endif } + + public static string Utf8ZToString(ReadOnlySpan value) + { + return Utf8ToString(value.Slice(0, GetLength(value))); + } } } diff --git a/src/LibHac/Common/U8Span.cs b/src/LibHac/Common/U8Span.cs new file mode 100644 index 00000000..c58aa3e7 --- /dev/null +++ b/src/LibHac/Common/U8Span.cs @@ -0,0 +1,42 @@ +using System; +using System.Diagnostics; +using System.Text; + +namespace LibHac.Common +{ + [DebuggerDisplay("{ToString()}")] + public ref struct U8Span + { + private readonly ReadOnlySpan _buffer; + + public ReadOnlySpan Value => _buffer; + public int Length => _buffer.Length; + + public byte this[int i] + { + get => _buffer[i]; + } + + public U8Span(ReadOnlySpan value) + { + _buffer = value; + } + + public U8Span(string value) + { + _buffer = Encoding.UTF8.GetBytes(value); + } + + public static implicit operator ReadOnlySpan(U8Span value) => value.Value; + + public static explicit operator string(U8Span value) => value.ToString(); + public static explicit operator U8Span(string value) => new U8Span(value); + + public override string ToString() + { + return StringUtils.Utf8ToString(_buffer); + } + + public bool IsNull() => _buffer == default; + } +} diff --git a/src/LibHac/Common/U8SpanMutable.cs b/src/LibHac/Common/U8SpanMutable.cs new file mode 100644 index 00000000..d0edcfa1 --- /dev/null +++ b/src/LibHac/Common/U8SpanMutable.cs @@ -0,0 +1,46 @@ +using System; +using System.Diagnostics; +using System.Text; + +namespace LibHac.Common +{ + [DebuggerDisplay("{ToString()}")] + public ref struct U8SpanMutable + { + private readonly Span _buffer; + + public Span Value => _buffer; + public int Length => _buffer.Length; + + public byte this[int i] + { + get => _buffer[i]; + set => _buffer[i] = value; + } + + public U8SpanMutable(Span value) + { + _buffer = value; + } + + public U8SpanMutable(string value) + { + _buffer = Encoding.UTF8.GetBytes(value); + } + + public static implicit operator U8Span(U8SpanMutable value) => new U8Span(value._buffer); + + public static implicit operator ReadOnlySpan(U8SpanMutable value) => value.Value; + public static implicit operator Span(U8SpanMutable value) => value.Value; + + public static explicit operator string(U8SpanMutable value) => value.ToString(); + public static explicit operator U8SpanMutable(string value) => new U8SpanMutable(value); + + public override string ToString() + { + return StringUtils.Utf8ZToString(_buffer); + } + + public bool IsNull() => _buffer == default; + } +} diff --git a/src/LibHac/Common/U8String.cs b/src/LibHac/Common/U8String.cs new file mode 100644 index 00000000..df952cf5 --- /dev/null +++ b/src/LibHac/Common/U8String.cs @@ -0,0 +1,41 @@ +using System; +using System.Diagnostics; +using System.Text; + +namespace LibHac.Common +{ + [DebuggerDisplay("{ToString()}")] + public struct U8String + { + private readonly byte[] _buffer; + + public ReadOnlySpan Value => _buffer; + public int Length => _buffer.Length; + + public byte this[int i] => _buffer[i]; + + public U8String(byte[] value) + { + _buffer = value; + } + + public U8String(string value) + { + _buffer = Encoding.UTF8.GetBytes(value); + } + + public static implicit operator U8Span(U8String value) => new U8Span(value._buffer); + + public static implicit operator ReadOnlySpan(U8String value) => value.Value; + + public static explicit operator string(U8String value) => value.ToString(); + public static explicit operator U8String(string value) => new U8String(value); + + public override string ToString() + { + return StringUtils.Utf8ToString(_buffer); + } + + public bool IsNull() => _buffer == null; + } +} diff --git a/src/LibHac/Common/U8StringHelpers.cs b/src/LibHac/Common/U8StringHelpers.cs new file mode 100644 index 00000000..003b61fb --- /dev/null +++ b/src/LibHac/Common/U8StringHelpers.cs @@ -0,0 +1,15 @@ +namespace LibHac.Common +{ + public static class U8StringHelpers + { + public static U8String AsU8String(this string value) + { + return new U8String(value); + } + + public static U8Span AsU8Span(this string value) + { + return new U8Span(value); + } + } +} diff --git a/src/LibHac/Common/U8StringMutable.cs b/src/LibHac/Common/U8StringMutable.cs new file mode 100644 index 00000000..2bb7c75b --- /dev/null +++ b/src/LibHac/Common/U8StringMutable.cs @@ -0,0 +1,48 @@ +using System; +using System.Diagnostics; +using System.Text; + +namespace LibHac.Common +{ + [DebuggerDisplay("{ToString()}")] + public struct U8StringMutable + { + private readonly byte[] _buffer; + + public Span Value => _buffer; + public int Length => _buffer.Length; + + public byte this[int i] + { + get => _buffer[i]; + set => _buffer[i] = value; + } + + public U8StringMutable(byte[] value) + { + _buffer = value; + } + + public U8StringMutable(string value) + { + _buffer = Encoding.UTF8.GetBytes(value); + } + + public static implicit operator U8String(U8StringMutable value) => new U8String(value._buffer); + public static implicit operator U8SpanMutable(U8StringMutable value) => new U8SpanMutable(value._buffer); + public static implicit operator U8Span(U8StringMutable value) => new U8Span(value._buffer); + + public static implicit operator ReadOnlySpan(U8StringMutable value) => value.Value; + public static implicit operator Span(U8StringMutable value) => value.Value; + + public static explicit operator string(U8StringMutable value) => value.ToString(); + public static explicit operator U8StringMutable(string value) => new U8StringMutable(value); + + public override string ToString() + { + return StringUtils.Utf8ZToString(_buffer); + } + + public bool IsNull() => _buffer == null; + } +} diff --git a/src/LibHac/Fs/DirectoryUtils.cs b/src/LibHac/Fs/DirectoryUtils.cs index 2ff0839f..1ea54ecc 100644 --- a/src/LibHac/Fs/DirectoryUtils.cs +++ b/src/LibHac/Fs/DirectoryUtils.cs @@ -39,7 +39,7 @@ namespace LibHac.Fs try { - Result rc = sourceFs.OpenFile(out srcFile, sourcePath.FromUtf8Z(), OpenMode.Read); + Result rc = sourceFs.OpenFile(out srcFile, StringUtils.Utf8ZToString(sourcePath), OpenMode.Read); if (rc.IsFailure()) return rc; FsPath dstPath = default; @@ -51,7 +51,7 @@ namespace LibHac.Fs throw new ArgumentException(); } - string dstPathStr = dstPath.Str.FromUtf8Z(); + string dstPathStr = StringUtils.Utf8ZToString(dstPath.Str); rc = destFs.CreateFile(dstPathStr, dirEntry.Size, CreateFileOptions.None); if (rc.IsFailure()) return rc; diff --git a/src/LibHac/Fs/FileSystemExtensions.cs b/src/LibHac/Fs/FileSystemExtensions.cs index 5fca64dd..35495263 100644 --- a/src/LibHac/Fs/FileSystemExtensions.cs +++ b/src/LibHac/Fs/FileSystemExtensions.cs @@ -82,7 +82,7 @@ namespace LibHac.Fs fileSystem.OpenDirectory(out IDirectory directory, path, OpenDirectoryMode.All).ThrowIfFailure(); - while(true) + while (true) { directory.Read(out long entriesRead, SpanHelpers.AsSpan(ref dirEntry)).ThrowIfFailure(); if (entriesRead == 0) break; @@ -109,7 +109,7 @@ namespace LibHac.Fs internal static DirectoryEntryEx GetDirectoryEntryEx(ref DirectoryEntry entry, string parentPath) { - string name = entry.Name.FromUtf8Z(); + string name = StringUtils.Utf8ZToString(entry.Name); string path = PathTools.Combine(parentPath, name); var entryEx = new DirectoryEntryEx(name, path, entry.Type, entry.Size); diff --git a/src/LibHac/Fs/FsEnums.cs b/src/LibHac/Fs/FsEnums.cs index edb8f0b2..210d231d 100644 --- a/src/LibHac/Fs/FsEnums.cs +++ b/src/LibHac/Fs/FsEnums.cs @@ -53,4 +53,10 @@ ProperSystem = 100, Safe = 101 } + + public enum CustomStorageId + { + User = 0, + SdCard = 1 + } } diff --git a/src/LibHac/FsClient/Accessors/FileSystemAccessor.cs b/src/LibHac/FsClient/Accessors/FileSystemAccessor.cs index 8916322b..a52fe3c2 100644 --- a/src/LibHac/FsClient/Accessors/FileSystemAccessor.cs +++ b/src/LibHac/FsClient/Accessors/FileSystemAccessor.cs @@ -11,6 +11,7 @@ namespace LibHac.FsClient.Accessors private IFileSystem FileSystem { get; } internal FileSystemManager FsManager { get; } + private ICommonMountNameGenerator MountNameGenerator { get; } private HashSet OpenFiles { get; } = new HashSet(); private HashSet OpenDirectories { get; } = new HashSet(); @@ -19,11 +20,12 @@ namespace LibHac.FsClient.Accessors internal bool IsAccessLogEnabled { get; set; } - public FileSystemAccessor(string name, IFileSystem baseFileSystem, FileSystemManager fsManager) + public FileSystemAccessor(string name, IFileSystem baseFileSystem, FileSystemManager fsManager, ICommonMountNameGenerator nameGenerator) { Name = name; FileSystem = baseFileSystem; FsManager = fsManager; + MountNameGenerator = nameGenerator; } public Result CreateDirectory(string path) @@ -147,6 +149,13 @@ namespace LibHac.FsClient.Accessors return FileSystem.QueryEntry(outBuffer, inBuffer, queryId, path); } + public Result GetCommonMountName(Span nameBuffer) + { + if (MountNameGenerator == null) return ResultFs.PreconditionViolation; + + return MountNameGenerator.Generate(nameBuffer); + } + internal void NotifyCloseFile(FileAccessor file) { lock (_locker) diff --git a/src/LibHac/FsClient/ContentStorage.cs b/src/LibHac/FsClient/ContentStorage.cs new file mode 100644 index 00000000..a84137aa --- /dev/null +++ b/src/LibHac/FsClient/ContentStorage.cs @@ -0,0 +1,70 @@ +using System; +using LibHac.Common; +using LibHac.Fs; +using LibHac.FsService; + +namespace LibHac.FsClient +{ + public static class ContentStorage + { + private static readonly U8String ContentStorageMountNameSystem = new U8String("@SystemContent"); + private static readonly U8String ContentStorageMountNameUser = new U8String("@UserContent"); + private static readonly U8String ContentStorageMountNameSdCard = new U8String("@SdCardContent"); + + public static Result MountContentStorage(this FileSystemClient fs, ContentStorageId storageId) + { + return MountContentStorage(fs, GetContentStorageMountName(storageId), storageId); + } + + public static Result MountContentStorage(this FileSystemClient fs, U8Span mountName, ContentStorageId storageId) + { + Result rc = MountHelpers.CheckMountNameAcceptingReservedMountName(mountName); + if (rc.IsFailure()) return rc; + + FileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject(); + + rc = fsProxy.OpenContentStorageFileSystem(out IFileSystem contentFs, storageId); + if (rc.IsFailure()) return rc; + + var mountNameGenerator = new ContentStorageCommonMountNameGenerator(storageId); + + return fs.Register(mountName, contentFs, mountNameGenerator); + } + + public static U8String GetContentStorageMountName(ContentStorageId storageId) + { + switch (storageId) + { + case ContentStorageId.System: + return ContentStorageMountNameSystem; + case ContentStorageId.User: + return ContentStorageMountNameUser; + case ContentStorageId.SdCard: + return ContentStorageMountNameSdCard; + default: + throw new ArgumentOutOfRangeException(nameof(storageId), storageId, null); + } + } + + private class ContentStorageCommonMountNameGenerator : ICommonMountNameGenerator + { + private ContentStorageId StorageId { get; } + + public ContentStorageCommonMountNameGenerator(ContentStorageId storageId) + { + StorageId = storageId; + } + + public Result Generate(Span nameBuffer) + { + U8String mountName = GetContentStorageMountName(StorageId); + + int length = StringUtils.Copy(nameBuffer, mountName); + nameBuffer[length] = (byte)':'; + nameBuffer[length + 1] = 0; + + return Result.Success; + } + } + } +} diff --git a/src/LibHac/FsClient/FileSystemClient.Mount.cs b/src/LibHac/FsClient/FileSystemClient.Mount.cs new file mode 100644 index 00000000..e98044d6 --- /dev/null +++ b/src/LibHac/FsClient/FileSystemClient.Mount.cs @@ -0,0 +1,22 @@ +using LibHac.Common; +using LibHac.Fs; +using LibHac.FsService; + +namespace LibHac.FsClient +{ + public partial class FileSystemClient + { + public Result MountCustomStorage(U8Span mountName, CustomStorageId storageId) + { + Result rc = MountHelpers.CheckMountName(mountName); + if (rc.IsFailure()) return rc; + + FileSystemProxy fsProxy = GetFileSystemProxyServiceObject(); + + rc = fsProxy.OpenCustomStorageFileSystem(out IFileSystem customFs, storageId); + if (rc.IsFailure()) return rc; + + return FsManager.Register(mountName, customFs); + } + } +} diff --git a/src/LibHac/FsClient/FileSystemClient.cs b/src/LibHac/FsClient/FileSystemClient.cs index e5cfb5b8..8bc0a42a 100644 --- a/src/LibHac/FsClient/FileSystemClient.cs +++ b/src/LibHac/FsClient/FileSystemClient.cs @@ -1,4 +1,6 @@ -using LibHac.FsService; +using LibHac.Common; +using LibHac.Fs; +using LibHac.FsService; namespace LibHac.FsClient { @@ -16,7 +18,7 @@ namespace LibHac.FsClient FsManager = new FileSystemManager(timer); } - private FileSystemProxy GetFileSystemProxyServiceObject() + public FileSystemProxy GetFileSystemProxyServiceObject() { if (FsProxy != null) return FsProxy; @@ -29,5 +31,15 @@ namespace LibHac.FsClient return FsProxy; } } + + public Result Register(U8Span mountName, IFileSystem fileSystem) + { + return FsManager.Register(mountName, fileSystem); + } + + public Result Register(U8Span mountName, IFileSystem fileSystem, ICommonMountNameGenerator nameGenerator) + { + return FsManager.Register(mountName, fileSystem, nameGenerator); + } } } diff --git a/src/LibHac/FsClient/FileSystemManager.cs b/src/LibHac/FsClient/FileSystemManager.cs index eadb8415..45e2c4fc 100644 --- a/src/LibHac/FsClient/FileSystemManager.cs +++ b/src/LibHac/FsClient/FileSystemManager.cs @@ -1,5 +1,6 @@ using System; using System.Runtime.CompilerServices; +using LibHac.Common; using LibHac.Fs; using LibHac.FsClient.Accessors; @@ -32,13 +33,20 @@ namespace LibHac.FsClient Time = timer; } - public void Register(string mountName, IFileSystem fileSystem) + public Result Register(U8Span mountName, IFileSystem fileSystem) { - var accessor = new FileSystemAccessor(mountName, fileSystem, this); + return Register(mountName, fileSystem, null); + } - MountTable.Mount(accessor).ThrowIfFailure(); + public Result Register(U8Span mountName, IFileSystem fileSystem, ICommonMountNameGenerator nameGenerator) + { + var accessor = new FileSystemAccessor(mountName.ToString(), fileSystem, this, nameGenerator); + + Result rc = MountTable.Mount(accessor); + if (rc.IsFailure()) return rc; accessor.IsAccessLogEnabled = IsEnabledAccessLog(); + return Result.Success; } public void Unmount(string mountName) diff --git a/src/LibHac/FsClient/FileSystemManagerUtils.cs b/src/LibHac/FsClient/FileSystemManagerUtils.cs index e1e23cdc..eb2add2c 100644 --- a/src/LibHac/FsClient/FileSystemManagerUtils.cs +++ b/src/LibHac/FsClient/FileSystemManagerUtils.cs @@ -131,12 +131,10 @@ namespace LibHac.FsClient if (entry.Type != DirectoryEntryType.Directory || !recurse) continue; IEnumerable subEntries = - fs.EnumerateEntries(PathTools.Combine(path, entry.Name), searchPattern, - searchOptions); + fs.EnumerateEntries(PathTools.Combine(path, entry.Name), searchPattern, searchOptions); foreach (DirectoryEntryEx subEntry in subEntries) { - subEntry.FullPath = PathTools.Combine(path, subEntry.Name); yield return subEntry; } } diff --git a/src/LibHac/FsClient/ICommonMountNameGenerator.cs b/src/LibHac/FsClient/ICommonMountNameGenerator.cs new file mode 100644 index 00000000..937c58d2 --- /dev/null +++ b/src/LibHac/FsClient/ICommonMountNameGenerator.cs @@ -0,0 +1,9 @@ +using System; + +namespace LibHac.FsClient +{ + public interface ICommonMountNameGenerator + { + Result Generate(Span nameBuffer); + } +} \ No newline at end of file diff --git a/src/LibHac/FsClient/MountHelpers.cs b/src/LibHac/FsClient/MountHelpers.cs new file mode 100644 index 00000000..18799d41 --- /dev/null +++ b/src/LibHac/FsClient/MountHelpers.cs @@ -0,0 +1,33 @@ +using LibHac.Common; +using LibHac.Fs; + +namespace LibHac.FsClient +{ + internal static class MountHelpers + { + public static Result CheckMountName(U8Span name) + { + if (name.IsNull()) return ResultFs.NullArgument.Log(); + + if (name.Length > 0 && name[0] == '@') return ResultFs.InvalidMountName.Log(); + if (!CheckMountNameImpl(name)) return ResultFs.InvalidMountName.Log(); + + return Result.Success; + } + + public static Result CheckMountNameAcceptingReservedMountName(U8Span name) + { + if (name.IsNull()) return ResultFs.NullArgument.Log(); + + if (!CheckMountNameImpl(name)) return ResultFs.InvalidMountName.Log(); + + return Result.Success; + } + + private static bool CheckMountNameImpl(U8Span name) + { + // Todo + return true; + } + } +} diff --git a/src/LibHac/FsService/FileSystemProxy.cs b/src/LibHac/FsService/FileSystemProxy.cs index 80da9c15..4449c91a 100644 --- a/src/LibHac/FsService/FileSystemProxy.cs +++ b/src/LibHac/FsService/FileSystemProxy.cs @@ -95,6 +95,13 @@ namespace LibHac.FsService return FsProxyCore.OpenContentStorageFileSystem(out fileSystem, storageId); } + public Result OpenCustomStorageFileSystem(out IFileSystem fileSystem, CustomStorageId storageId) + { + // Missing permission check, speed emulation storage type wrapper, and FileSystemInterfaceAdapter + + return FsProxyCore.OpenCustomStorageFileSystem(out fileSystem, storageId); + } + public Result OpenSaveDataFileSystemBySystemSaveDataId(out IFileSystem fileSystem, SaveDataSpaceId spaceId, SaveDataAttribute attribute) { diff --git a/src/LibHac/FsService/FileSystemProxyCore.cs b/src/LibHac/FsService/FileSystemProxyCore.cs index 8c3e0e16..58250891 100644 --- a/src/LibHac/FsService/FileSystemProxyCore.cs +++ b/src/LibHac/FsService/FileSystemProxyCore.cs @@ -75,6 +75,50 @@ namespace LibHac.FsService EncryptedFsKeyId.Content, SdEncryptionSeed); } + public Result OpenCustomStorageFileSystem(out IFileSystem fileSystem, CustomStorageId storageId) + { + fileSystem = default; + + switch (storageId) + { + case CustomStorageId.SdCard: + { + Result rc = FsCreators.SdFileSystemCreator.Create(out IFileSystem sdFs); + if (rc.IsFailure()) return rc; + + string customStorageDir = Util.GetCustomStorageDirectoryName(CustomStorageId.SdCard); + string subDirName = $"/{NintendoDirectoryName}/{customStorageDir}"; + + rc = Util.CreateSubFileSystem(out IFileSystem subFs, sdFs, subDirName, true); + if (rc.IsFailure()) return rc; + + rc = FsCreators.EncryptedFileSystemCreator.Create(out IFileSystem encryptedFs, subFs, + EncryptedFsKeyId.CustomStorage, SdEncryptionSeed); + if (rc.IsFailure()) return rc; + + fileSystem = encryptedFs; + return Result.Success; + } + case CustomStorageId.User: + { + Result rc = FsCreators.BuiltInStorageFileSystemCreator.Create(out IFileSystem userFs, string.Empty, + BisPartitionId.User); + if (rc.IsFailure()) return rc; + + string customStorageDir = Util.GetCustomStorageDirectoryName(CustomStorageId.User); + string subDirName = $"/{customStorageDir}"; + + rc = Util.CreateSubFileSystem(out IFileSystem subFs, userFs, subDirName, true); + if (rc.IsFailure()) return rc; + + fileSystem = subFs; + return Result.Success; + } + default: + return ResultFs.InvalidArgument.Log(); + } + } + public Result SetSdCardEncryptionSeed(ReadOnlySpan seed) { seed.CopyTo(SdEncryptionSeed); diff --git a/src/LibHac/FsService/IFileSystemProxy.cs b/src/LibHac/FsService/IFileSystemProxy.cs index cd3ac661..59d665f3 100644 --- a/src/LibHac/FsService/IFileSystemProxy.cs +++ b/src/LibHac/FsService/IFileSystemProxy.cs @@ -12,6 +12,7 @@ namespace LibHac.FsService Result OpenSaveDataFileSystem(out IFileSystem fileSystem, SaveDataSpaceId spaceId, SaveDataAttribute attribute); Result OpenSaveDataFileSystemBySystemSaveDataId(out IFileSystem fileSystem, SaveDataSpaceId spaceId, SaveDataAttribute attribute); Result OpenContentStorageFileSystem(out IFileSystem fileSystem, ContentStorageId storageId); + Result OpenCustomStorageFileSystem(out IFileSystem fileSystem, CustomStorageId storageId); Result SetSdCardEncryptionSeed(ReadOnlySpan seed); Result SetSaveDataSize(long saveDataSize, long saveDataJournalSize); Result SetSaveDataRootPath(string path); diff --git a/src/LibHac/FsService/Util.cs b/src/LibHac/FsService/Util.cs index 8ac791bd..b6ed2b2a 100644 --- a/src/LibHac/FsService/Util.cs +++ b/src/LibHac/FsService/Util.cs @@ -1,4 +1,5 @@ -using LibHac.Fs; +using System; +using LibHac.Fs; namespace LibHac.FsService { @@ -48,5 +49,17 @@ namespace LibHac.FsService spaceId == SaveDataSpaceId.ProperSystem || spaceId == SaveDataSpaceId.Safe; } + + public static string GetCustomStorageDirectoryName(CustomStorageId storageId) + { + switch (storageId) + { + case CustomStorageId.User: + case CustomStorageId.SdCard: + return "CustomStorage0"; + default: + throw new ArgumentOutOfRangeException(nameof(storageId), storageId, null); + } + } } } diff --git a/src/hactoolnet/ProcessNca.cs b/src/hactoolnet/ProcessNca.cs index 73b94ce9..5cfc9e65 100644 --- a/src/hactoolnet/ProcessNca.cs +++ b/src/hactoolnet/ProcessNca.cs @@ -1,6 +1,7 @@ using System.IO; using System.Text; using LibHac; +using LibHac.Common; using LibHac.Fs; using LibHac.Fs.NcaUtils; using LibHac.FsClient; @@ -47,8 +48,8 @@ namespace hactoolnet string mountName = $"section{i}"; - fs.Register(mountName, OpenFileSystem(i)); - fs.Register("output", new LocalFileSystem(ctx.Options.SectionOutDir[i])); + fs.Register(mountName.AsU8Span(), OpenFileSystem(i)); + fs.Register("output".AsU8Span(), new LocalFileSystem(ctx.Options.SectionOutDir[i])); FsUtils.CopyDirectoryWithProgress(fs, mountName + ":/", "output:/", logger: ctx.Logger); @@ -96,8 +97,8 @@ namespace hactoolnet { FileSystemManager fs = ctx.Horizon.Fs; - fs.Register("rom", OpenFileSystemByType(NcaSectionType.Data)); - fs.Register("output", new LocalFileSystem(ctx.Options.RomfsOutDir)); + fs.Register("rom".AsU8Span(), OpenFileSystemByType(NcaSectionType.Data)); + fs.Register("output".AsU8Span(), new LocalFileSystem(ctx.Options.RomfsOutDir)); FsUtils.CopyDirectoryWithProgress(fs, "rom:/", "output:/", logger: ctx.Logger); @@ -153,8 +154,8 @@ namespace hactoolnet { FileSystemManager fs = ctx.Horizon.Fs; - fs.Register("code", OpenFileSystemByType(NcaSectionType.Code)); - fs.Register("output", new LocalFileSystem(ctx.Options.ExefsOutDir)); + fs.Register("code".AsU8Span(), OpenFileSystemByType(NcaSectionType.Code)); + fs.Register("output".AsU8Span(), new LocalFileSystem(ctx.Options.ExefsOutDir)); FsUtils.CopyDirectoryWithProgress(fs, "code:/", "output:/", logger: ctx.Logger); diff --git a/src/hactoolnet/ProcessSave.cs b/src/hactoolnet/ProcessSave.cs index ae1b7d6b..3bc937c3 100644 --- a/src/hactoolnet/ProcessSave.cs +++ b/src/hactoolnet/ProcessSave.cs @@ -4,6 +4,7 @@ using System.IO; using System.Linq; using System.Text; using LibHac; +using LibHac.Common; using LibHac.Fs; using LibHac.Fs.Save; using LibHac.FsClient; @@ -30,7 +31,7 @@ namespace hactoolnet var save = new SaveDataFileSystem(ctx.Keyset, file, ctx.Options.IntegrityLevel, true); FileSystemManager fs = ctx.Horizon.Fs; - fs.Register("save", save); + fs.Register("save".AsU8Span(), save); if (ctx.Options.Validate) { @@ -39,7 +40,7 @@ namespace hactoolnet if (ctx.Options.OutDir != null) { - fs.Register("output", new LocalFileSystem(ctx.Options.OutDir)); + fs.Register("output".AsU8Span(), new LocalFileSystem(ctx.Options.OutDir)); FsUtils.CopyDirectoryWithProgress(fs, "save:/", "output:/", logger: ctx.Logger); @@ -85,7 +86,7 @@ namespace hactoolnet if (ctx.Options.RepackSource != null) { - fs.Register("input", new LocalFileSystem(ctx.Options.RepackSource)); + fs.Register("input".AsU8Span(), new LocalFileSystem(ctx.Options.RepackSource)); fs.CleanDirectoryRecursively("save:/"); fs.Commit("save");