Add fscreator interfaces

This commit is contained in:
Alex Barney 2019-08-19 21:24:30 -05:00
parent c3dfaf14e8
commit a052ebb8a1
28 changed files with 309 additions and 64 deletions

View file

@ -0,0 +1,9 @@
using System;
namespace LibHac.Common
{
public interface ITimeStampGenerator
{
DateTimeOffset Generate();
}
}

View file

@ -4,31 +4,31 @@ using System.IO;
namespace LibHac.Fs
{
public class DeltaFragment
public class Delta
{
private const string Ndv0Magic = "NDV0";
private IStorage Original { get; set; }
private IStorage Delta { get; }
public DeltaFragmentHeader Header { get; }
private List<DeltaFragmentSegment> Segments { get; } = new List<DeltaFragmentSegment>();
private IStorage OriginalStorage { get; set; }
private IStorage DeltaStorage { get; }
public DeltaHeader Header { get; }
private List<DeltaSegment> Segments { get; } = new List<DeltaSegment>();
public DeltaFragment(IStorage delta, IStorage originalData) : this(delta)
public Delta(IStorage deltaStorage, IStorage originalData) : this(deltaStorage)
{
SetBaseStorage(originalData);
}
public DeltaFragment(IStorage delta)
public Delta(IStorage deltaStorage)
{
Delta = delta;
DeltaStorage = deltaStorage;
if (Delta.GetSize() < 0x40) throw new InvalidDataException("Delta file is too small.");
if (DeltaStorage.GetSize() < 0x40) throw new InvalidDataException("Delta file is too small.");
Header = new DeltaFragmentHeader(delta.AsFile(OpenMode.Read));
Header = new DeltaHeader(deltaStorage.AsFile(OpenMode.Read));
if (Header.Magic != Ndv0Magic) throw new InvalidDataException("NDV0 magic value is missing.");
long fragmentSize = Header.FragmentHeaderSize + Header.FragmentBodySize;
if (Delta.GetSize() < fragmentSize)
long fragmentSize = Header.HeaderSize + Header.BodySize;
if (DeltaStorage.GetSize() < fragmentSize)
{
throw new InvalidDataException($"Delta file is smaller than the header indicates. (0x{fragmentSize} bytes)");
}
@ -38,9 +38,9 @@ namespace LibHac.Fs
public void SetBaseStorage(IStorage baseStorage)
{
Original = baseStorage;
OriginalStorage = baseStorage;
if (Original.GetSize() != Header.OriginalSize)
if (OriginalStorage.GetSize() != Header.OriginalSize)
{
throw new InvalidDataException($"Original file size does not match the size in the delta header. (0x{Header.OriginalSize} bytes)");
}
@ -48,13 +48,13 @@ namespace LibHac.Fs
public IStorage GetPatchedStorage()
{
if (Original == null) throw new InvalidOperationException("Cannot apply a delta patch without a base file.");
if (OriginalStorage == null) throw new InvalidOperationException("Cannot apply a delta patch without a base file.");
var storages = new List<IStorage>();
foreach (DeltaFragmentSegment segment in Segments)
foreach (DeltaSegment segment in Segments)
{
IStorage source = segment.IsInOriginal ? Original : Delta;
IStorage source = segment.IsInOriginal ? OriginalStorage : DeltaStorage;
// todo Do this without tons of SubStorages
IStorage sub = source.Slice(segment.SourceOffset, segment.Size);
@ -67,9 +67,9 @@ namespace LibHac.Fs
private void ParseDeltaStructure()
{
var reader = new FileReader(Delta.AsFile(OpenMode.Read));
var reader = new FileReader(DeltaStorage.AsFile(OpenMode.Read));
reader.Position = Header.FragmentHeaderSize;
reader.Position = Header.HeaderSize;
long offset = 0;
@ -79,7 +79,7 @@ namespace LibHac.Fs
if (seek > 0)
{
var segment = new DeltaFragmentSegment()
var segment = new DeltaSegment()
{
SourceOffset = offset,
Size = seek,
@ -92,7 +92,7 @@ namespace LibHac.Fs
if (size > 0)
{
var segment = new DeltaFragmentSegment()
var segment = new DeltaSegment()
{
SourceOffset = reader.Position,
Size = size,
@ -131,30 +131,30 @@ namespace LibHac.Fs
}
}
internal class DeltaFragmentSegment
internal class DeltaSegment
{
public long SourceOffset { get; set; }
public int Size { get; set; }
public bool IsInOriginal { get; set; }
}
public class DeltaFragmentHeader
public class DeltaHeader
{
public string Magic { get; }
public long OriginalSize { get; }
public long NewSize { get; }
public long FragmentHeaderSize { get; }
public long FragmentBodySize { get; }
public long HeaderSize { get; }
public long BodySize { get; }
public DeltaFragmentHeader(IFile header)
public DeltaHeader(IFile header)
{
var reader = new FileReader(header);
Magic = reader.ReadAscii(4);
OriginalSize = reader.ReadInt64(8);
NewSize = reader.ReadInt64();
FragmentHeaderSize = reader.ReadInt64();
FragmentBodySize = reader.ReadInt64();
HeaderSize = reader.ReadInt64();
BodySize = reader.ReadInt64();
}
}
}

45
src/LibHac/Fs/FsEnums.cs Normal file
View file

@ -0,0 +1,45 @@
namespace LibHac.Fs
{
public enum BisPartitionId
{
BootPartition1Root = 0,
BootPartition2Root = 10,
UserDataRoot = 20,
BootConfigAndPackage2Part1 = 21,
BootConfigAndPackage2Part2 = 22,
BootConfigAndPackage2Part3 = 23,
BootConfigAndPackage2Part4 = 24,
BootConfigAndPackage2Part5 = 25,
BootConfigAndPackage2Part6 = 26,
CalibrationBinary = 27,
CalibrationFile = 28,
SafeMode = 29,
User = 30,
System = 31,
SystemProperEncryption = 32,
SystemProperPartition = 33,
Invalid = 35
}
public enum ContentStorageId
{
System = 0,
User = 1,
SdCard = 2
}
public enum GameCardPartition
{
Update = 0,
Normal = 1,
Secure = 2,
Logo = 3
}
public enum GameCardPartitionRaw
{
Normal = 0,
Secure = 1,
Writable = 2
}
}

View file

@ -336,7 +336,7 @@ namespace LibHac.Fs.NcaUtils
return GetSectionIndexFromType(type, Header.ContentType);
}
public static int GetSectionIndexFromType(NcaSectionType type, ContentType contentType)
public static int GetSectionIndexFromType(NcaSectionType type, NcaContentType contentType)
{
if (!TryGetSectionIndexFromType(type, contentType, out int index))
{
@ -346,17 +346,17 @@ namespace LibHac.Fs.NcaUtils
return index;
}
public static bool TryGetSectionIndexFromType(NcaSectionType type, ContentType contentType, out int index)
public static bool TryGetSectionIndexFromType(NcaSectionType type, NcaContentType contentType, out int index)
{
switch (type)
{
case NcaSectionType.Code when contentType == ContentType.Program:
case NcaSectionType.Code when contentType == NcaContentType.Program:
index = 0;
return true;
case NcaSectionType.Data when contentType == ContentType.Program:
case NcaSectionType.Data when contentType == NcaContentType.Program:
index = 1;
return true;
case NcaSectionType.Logo when contentType == ContentType.Program:
case NcaSectionType.Logo when contentType == NcaContentType.Program:
index = 2;
return true;
case NcaSectionType.Data:
@ -368,7 +368,7 @@ namespace LibHac.Fs.NcaUtils
}
}
public static NcaSectionType GetSectionTypeFromIndex(int index, ContentType contentType)
public static NcaSectionType GetSectionTypeFromIndex(int index, NcaContentType contentType)
{
if (!TryGetSectionTypeFromIndex(index, contentType, out NcaSectionType type))
{
@ -378,17 +378,17 @@ namespace LibHac.Fs.NcaUtils
return type;
}
public static bool TryGetSectionTypeFromIndex(int index, ContentType contentType, out NcaSectionType type)
public static bool TryGetSectionTypeFromIndex(int index, NcaContentType contentType, out NcaSectionType type)
{
switch (index)
{
case 0 when contentType == ContentType.Program:
case 0 when contentType == NcaContentType.Program:
type = NcaSectionType.Code;
return true;
case 1 when contentType == ContentType.Program:
case 1 when contentType == NcaContentType.Program:
type = NcaSectionType.Data;
return true;
case 2 when contentType == ContentType.Program:
case 2 when contentType == NcaContentType.Program:
type = NcaSectionType.Logo;
return true;
case 0:
@ -545,11 +545,11 @@ namespace LibHac.Fs.NcaUtils
// Haven't checked delta fragment NCAs
switch (Header.ContentType)
{
case ContentType.Program:
case ContentType.Manual:
case NcaContentType.Program:
case NcaContentType.Manual:
counterVersion = Math.Max(minorVersion - 1, 0);
break;
case ContentType.PublicData:
case NcaContentType.PublicData:
counterVersion = minorVersion << 16;
break;
default:

View file

@ -44,9 +44,9 @@ namespace LibHac.Fs.NcaUtils
set => Header.DistributionType = (byte)value;
}
public ContentType ContentType
public NcaContentType ContentType
{
get => (ContentType)Header.ContentType;
get => (NcaContentType)Header.ContentType;
set => Header.ContentType = (byte)value;
}

View file

@ -41,7 +41,7 @@
Logo
};
public enum ContentType
public enum NcaContentType
{
Program,
Meta,

View file

@ -0,0 +1,9 @@
namespace LibHac.Fs.Save
{
public interface ISaveDataExtraDataAccessor
{
Result Write(ExtraData data);
Result Commit();
Result Read(out ExtraData data);
}
}

View file

@ -5,7 +5,7 @@ using LibHac.Kvdb;
namespace LibHac.Fs
{
public class SaveDataStruct : IComparable<SaveDataStruct>, IComparable, IEquatable<SaveDataStruct>, IExportable
public class SaveDataAttribute : IComparable<SaveDataAttribute>, IComparable, IEquatable<SaveDataAttribute>, IExportable
{
public ulong TitleId { get; private set; }
public UserId UserId { get; private set; }
@ -44,7 +44,7 @@ namespace LibHac.Fs
public void Freeze() => _isFrozen = true;
public bool Equals(SaveDataStruct other)
public bool Equals(SaveDataAttribute other)
{
return other != null && TitleId == other.TitleId && UserId.Equals(other.UserId) && SaveId == other.SaveId &&
Type == other.Type && Rank == other.Rank && Index == other.Index;
@ -52,7 +52,7 @@ namespace LibHac.Fs
public override bool Equals(object obj)
{
return obj is SaveDataStruct other && Equals(other);
return obj is SaveDataAttribute other && Equals(other);
}
public override int GetHashCode()
@ -71,7 +71,7 @@ namespace LibHac.Fs
}
}
public int CompareTo(SaveDataStruct other)
public int CompareTo(SaveDataAttribute other)
{
int titleIdComparison = TitleId.CompareTo(other.TitleId);
if (titleIdComparison != 0) return titleIdComparison;
@ -89,7 +89,7 @@ namespace LibHac.Fs
public int CompareTo(object obj)
{
if (obj is null) return 1;
return obj is SaveDataStruct other ? CompareTo(other) : throw new ArgumentException($"Object must be of type {nameof(SaveDataStruct)}");
return obj is SaveDataAttribute other ? CompareTo(other) : throw new ArgumentException($"Object must be of type {nameof(SaveDataAttribute)}");
}
}
}

View file

@ -0,0 +1,20 @@
namespace LibHac.FsService.Creators
{
public class FileSystemCreators
{
public IRomFileSystemCreator RomFileSystemCreator { get; set; }
public IPartitionFileSystemCreator PartitionFileSystemCreator { get; set; }
public IStorageOnNcaCreator StorageOnNcaCreator { get; set; }
public IFatFileSystemCreator FatFileSystemCreator { get; set; }
public ISubDirectoryFileSystemCreator SubDirectoryFileSystemCreator { get; set; }
public IBuiltInStorageCreator BuiltInStorageCreator { get; set; }
public ISdStorageCreator SdStorageCreator { get; set; }
public ISaveDataFileSystemCreator SaveDataFileSystemCreator { get; set; }
public IGameCardStorageCreator GameCardStorageCreator { get; set; }
public IGameCardFileSystemCreator GameCardFileSystemCreator { get; set; }
public IEncryptedFileSystemCreator EncryptedFileSystemCreator { get; set; }
public IMemoryStorageCreator MemoryStorageCreator { get; set; }
public IBuiltInStorageFileSystemCreator BuiltInStorageFileSystemCreator { get; set; }
public ISdFileSystemCreator SdFileSystemCreator { get; set; }
}
}

View file

@ -0,0 +1,9 @@
using LibHac.Fs;
namespace LibHac.FsService.Creators
{
public interface IBuiltInStorageCreator
{
Result Create(out IStorage storage, BisPartitionId partitionId);
}
}

View file

@ -0,0 +1,11 @@
using LibHac.Fs;
namespace LibHac.FsService.Creators
{
public interface IBuiltInStorageFileSystemCreator
{
Result Create(out IFileSystem fileSystem, string rootPath, BisPartitionId partitionId);
Result CreateFatFileSystem(out IFileSystem fileSystem, BisPartitionId partitionId);
Result SetBisRootForHost(BisPartitionId partitionId, string rootPath);
}
}

View file

@ -0,0 +1,18 @@
using System;
using LibHac.Fs;
namespace LibHac.FsService.Creators
{
public interface IEncryptedFileSystemCreator
{
Result Create(out IFileSystem encryptedFileSystem, IFileSystem baseFileSystem, EncryptedFsKeyId keyId,
ReadOnlySpan<byte> encryptionSeed);
}
public enum EncryptedFsKeyId
{
Save = 0,
Content = 1,
CustomStorage = 2
}
}

View file

@ -0,0 +1,9 @@
using LibHac.Fs;
namespace LibHac.FsService.Creators
{
public interface IFatFileSystemCreator
{
Result Create(out IFileSystem fileSystem, IStorage baseStorage);
}
}

View file

@ -0,0 +1,9 @@
using LibHac.Fs;
namespace LibHac.FsService.Creators
{
public interface IGameCardFileSystemCreator
{
Result Create(out IFileSystem fileSystem, GameCardHandle handle, GameCardPartition partitionType);
}
}

View file

@ -0,0 +1,11 @@
using LibHac.Fs;
namespace LibHac.FsService.Creators
{
public interface IGameCardStorageCreator
{
Result CreateNormal(GameCardHandle handle, out IStorage storage);
Result CreateSecure(GameCardHandle handle, out IStorage storage);
Result CreateWritable(GameCardHandle handle, out IStorage storage);
}
}

View file

@ -0,0 +1,11 @@
using System;
using LibHac.Fs;
namespace LibHac.FsService.Creators
{
public interface IMemoryStorageCreator
{
Result Create(out IStorage storage, out Memory<byte> buffer, int storageId);
Result RegisterBuffer(int storageId, Memory<byte> buffer);
}
}

View file

@ -0,0 +1,9 @@
using LibHac.Fs;
namespace LibHac.FsService.Creators
{
public interface IPartitionFileSystemCreator
{
Result Create(out IFileSystem fileSystem, IStorage pFsStorage);
}
}

View file

@ -0,0 +1,9 @@
using LibHac.Fs;
namespace LibHac.FsService.Creators
{
public interface IRomFileSystemCreator
{
Result Create(out IFileSystem fileSystem, IStorage romFsStorage);
}
}

View file

@ -0,0 +1,18 @@
using System;
using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Save;
namespace LibHac.FsService.Creators
{
public interface ISaveDataFileSystemCreator
{
Result CreateFile(out IFile file, IFileSystem sourceFileSystem, ulong saveDataId, OpenMode openMode);
Result Create(out IFileSystem fileSystem, out ISaveDataExtraDataAccessor extraDataAccessor,
IFileSystem sourceFileSystem, ulong saveDataId, bool allowDirectorySaveData, bool useDeviceUniqueMac,
SaveDataType type, ITimeStampGenerator timeStampGenerator);
Result SetSdCardEncryptionSeed(ReadOnlySpan<byte> seed);
}
}

View file

@ -0,0 +1,10 @@
using LibHac.Fs;
namespace LibHac.FsService.Creators
{
public interface ISdFileSystemCreator
{
Result Create(out IFileSystem fileSystem);
Result Format(bool closeOpenEntries);
}
}

View file

@ -0,0 +1,9 @@
using LibHac.Fs;
namespace LibHac.FsService.Creators
{
public interface ISdStorageCreator
{
Result Create(out IStorage storage);
}
}

View file

@ -0,0 +1,13 @@
using LibHac.Fs;
using LibHac.Fs.NcaUtils;
namespace LibHac.FsService.Creators
{
public interface IStorageOnNcaCreator
{
Result Create(out IStorage storage, out NcaFsHeader fsHeader, Nca nca, int fsIndex, bool isCodeFs);
Result CreateWithPatch(out IStorage storage, out NcaFsHeader fsHeader, Nca baseNca, Nca patchNca, int fsIndex, bool isCodeFs);
Result OpenNca(out Nca nca, IStorage ncaStorage);
Result VerifyAcidSignature(IFileSystem codeFileSystem, Nca nca);
}
}

View file

@ -0,0 +1,9 @@
using LibHac.Fs;
namespace LibHac.FsService.Creators
{
public interface ISubDirectoryFileSystemCreator
{
Result Create(out IFileSystem subDirFileSystem, IFileSystem baseFileSystem, string path);
}
}

View file

@ -0,0 +1,7 @@
namespace LibHac.FsService
{
public struct GameCardHandle
{
public int Value;
}
}

View file

@ -82,7 +82,7 @@ namespace LibHac
nca = new SwitchFsNca(new Nca(Keyset, storage));
nca.NcaId = GetNcaFilename(fileEntry.Name, nca);
string extension = nca.Nca.Header.ContentType == Fs.NcaUtils.ContentType.Meta ? ".cnmt.nca" : ".nca";
string extension = nca.Nca.Header.ContentType == NcaContentType.Meta ? ".cnmt.nca" : ".nca";
nca.Filename = nca.NcaId + extension;
}
catch (MissingKeyException ex)
@ -132,7 +132,7 @@ namespace LibHac
private void ReadTitles()
{
foreach (SwitchFsNca nca in Ncas.Values.Where(x => x.Nca.Header.ContentType == Fs.NcaUtils.ContentType.Meta))
foreach (SwitchFsNca nca in Ncas.Values.Where(x => x.Nca.Header.ContentType == NcaContentType.Meta))
{
try
{
@ -161,11 +161,11 @@ namespace LibHac
switch (content.Type)
{
case Ncm.ContentType.Program:
case Ncm.ContentType.Data:
case ContentType.Program:
case ContentType.Data:
title.MainNca = contentNca;
break;
case Ncm.ContentType.Control:
case ContentType.Control:
title.ControlNca = contentNca;
break;
}
@ -230,7 +230,7 @@ namespace LibHac
private string GetNcaFilename(string name, SwitchFsNca nca)
{
if (nca.Nca.Header.ContentType != Fs.NcaUtils.ContentType.Meta || !name.EndsWith(".cnmt.nca"))
if (nca.Nca.Header.ContentType != NcaContentType.Meta || !name.EndsWith(".cnmt.nca"))
{
return Path.GetFileNameWithoutExtension(name);
}

View file

@ -38,7 +38,7 @@ namespace hactoolnet
catch (InvalidDataException) { } // Ignore non-NCA3 files
}
var delta = new DeltaFragment(deltaStorage);
var delta = new Delta(deltaStorage);
if (ctx.Options.BaseFile != null)
{
@ -61,18 +61,18 @@ namespace hactoolnet
}
}
private static string Print(this DeltaFragment delta)
private static string Print(this Delta delta)
{
int colLen = 36;
var sb = new StringBuilder();
sb.AppendLine();
sb.AppendLine("Delta Fragment:");
sb.AppendLine("Delta File:");
PrintItem(sb, colLen, "Magic:", delta.Header.Magic);
PrintItem(sb, colLen, "Base file size:", $"0x{delta.Header.OriginalSize:x12}");
PrintItem(sb, colLen, "New file size:", $"0x{delta.Header.NewSize:x12}");
PrintItem(sb, colLen, "Fragment header size:", $"0x{delta.Header.FragmentHeaderSize:x12}");
PrintItem(sb, colLen, "Fragment body size:", $"0x{delta.Header.FragmentBodySize:x12}");
PrintItem(sb, colLen, "Delta header size:", $"0x{delta.Header.HeaderSize:x12}");
PrintItem(sb, colLen, "Delta body size:", $"0x{delta.Header.BodySize:x12}");
return sb.ToString();
}

View file

@ -128,7 +128,7 @@ namespace hactoolnet
if (ctx.Options.ExefsOutDir != null || ctx.Options.ExefsOut != null)
{
if (nca.Header.ContentType != ContentType.Program)
if (nca.Header.ContentType != NcaContentType.Program)
{
ctx.Logger.LogMessage("NCA's content type is not \"Program\"");
return;
@ -201,7 +201,7 @@ namespace hactoolnet
private static Validity VerifySignature2(this Nca nca)
{
if (nca.Header.ContentType != ContentType.Program) return Validity.Unchecked;
if (nca.Header.ContentType != NcaContentType.Program) return Validity.Unchecked;
IFileSystem pfs = nca.OpenFileSystem(NcaSectionType.Code, IntegrityCheckLevel.ErrorOnInvalid);
if (!pfs.FileExists("main.npdm")) return Validity.Unchecked;
@ -266,7 +266,7 @@ namespace hactoolnet
if (!nca.Header.IsSectionEnabled(i)) continue;
NcaFsHeader sectHeader = nca.Header.GetFsHeader(i);
bool isExefs = nca.Header.ContentType == ContentType.Program && i == 0;
bool isExefs = nca.Header.ContentType == NcaContentType.Program && i == 0;
sb.AppendLine($" Section {i}:");
PrintItem(sb, colLen, " Offset:", $"0x{nca.Header.GetSectionStartOffset(i):x12}");

View file

@ -127,7 +127,7 @@ namespace hactoolnet
IStorage ncaStorage = partition.OpenFile(fileEntry, OpenMode.Read).AsStorage();
var nca = new Nca(ctx.Keyset, ncaStorage);
if (nca.Header.ContentType == ContentType.Program)
if (nca.Header.ContentType == NcaContentType.Program)
{
mainNca = nca;
}