From c3dfaf14e8a68c967c140d1a8a01a9f7d898693c Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Mon, 22 Jul 2019 20:38:59 -0500 Subject: [PATCH] Add content storage and content meta DB interfaces --- src/LibHac/Cnmt.cs | 71 +++++------------ src/LibHac/Common/Id128.cs | 101 +++++++++++++++++++++++++ src/LibHac/Fs/NcaUtils/Nca.cs | 12 +-- src/LibHac/Fs/RightsId.cs | 47 ++++++++++++ src/LibHac/Fs/UserId.cs | 59 +++++---------- src/LibHac/Ncm/ContentEnums.cs | 40 ++++++++++ src/LibHac/Ncm/ContentId.cs | 47 ++++++++++++ src/LibHac/Ncm/ContentMetaKey.cs | 18 ++--- src/LibHac/Ncm/ContentMetaStructs.cs | 20 +++++ src/LibHac/Ncm/IContentMetaDatabase.cs | 32 ++++++++ src/LibHac/Ncm/IContentStorage.cs | 37 +++++++++ src/LibHac/Ncm/PlaceHolderId.cs | 47 ++++++++++++ src/LibHac/SwitchFs.cs | 23 +++--- 13 files changed, 439 insertions(+), 115 deletions(-) create mode 100644 src/LibHac/Common/Id128.cs create mode 100644 src/LibHac/Fs/RightsId.cs create mode 100644 src/LibHac/Ncm/ContentEnums.cs create mode 100644 src/LibHac/Ncm/ContentId.cs create mode 100644 src/LibHac/Ncm/ContentMetaStructs.cs create mode 100644 src/LibHac/Ncm/IContentMetaDatabase.cs create mode 100644 src/LibHac/Ncm/IContentStorage.cs create mode 100644 src/LibHac/Ncm/PlaceHolderId.cs diff --git a/src/LibHac/Cnmt.cs b/src/LibHac/Cnmt.cs index 9b0d375e..e6869390 100644 --- a/src/LibHac/Cnmt.cs +++ b/src/LibHac/Cnmt.cs @@ -1,6 +1,8 @@ using System.IO; using System.Linq; using LibHac.Fs.NcaUtils; +using LibHac.Ncm; +using ContentType = LibHac.Ncm.ContentType; namespace LibHac { @@ -8,7 +10,7 @@ namespace LibHac { public ulong TitleId { get; } public TitleVersion TitleVersion { get; } - public TitleType Type { get; } + public ContentMetaType Type { get; } public byte FieldD { get; } public int TableOffset { get; } public int ContentEntryCount { get; } @@ -34,8 +36,8 @@ namespace LibHac { TitleId = reader.ReadUInt64(); uint version = reader.ReadUInt32(); - Type = (TitleType)reader.ReadByte(); - TitleVersion = new TitleVersion(version, Type < TitleType.Application); + Type = (ContentMetaType)reader.ReadByte(); + TitleVersion = new TitleVersion(version, Type < ContentMetaType.Application); FieldD = reader.ReadByte(); TableOffset = reader.ReadUInt16(); ContentEntryCount = reader.ReadUInt16(); @@ -44,16 +46,16 @@ namespace LibHac switch (Type) { - case TitleType.Application: + case ContentMetaType.Application: ApplicationTitleId = TitleId; PatchTitleId = reader.ReadUInt64(); MinimumSystemVersion = new TitleVersion(reader.ReadUInt32(), true); break; - case TitleType.Patch: + case ContentMetaType.Patch: ApplicationTitleId = reader.ReadUInt64(); MinimumSystemVersion = new TitleVersion(reader.ReadUInt32(), true); break; - case TitleType.AddOnContent: + case ContentMetaType.AddOnContent: ApplicationTitleId = reader.ReadUInt64(); MinimumApplicationVersion = new TitleVersion(reader.ReadUInt32()); break; @@ -74,7 +76,7 @@ namespace LibHac MetaEntries[i] = new CnmtContentMetaEntry(reader); } - if (Type == TitleType.Patch) + if (Type == ContentMetaType.Patch) { ExtendedData = new CnmtExtended(reader); } @@ -89,7 +91,7 @@ namespace LibHac public byte[] Hash { get; set; } public byte[] NcaId { get; set; } public long Size { get; set; } - public CnmtContentType Type { get; set; } + public ContentType Type { get; set; } public CnmtContentEntry() { } @@ -99,7 +101,7 @@ namespace LibHac NcaId = reader.ReadBytes(0x10); Size = reader.ReadUInt32(); Size |= (long)reader.ReadUInt16() << 32; - Type = (CnmtContentType)reader.ReadByte(); + Type = (ContentType)reader.ReadByte(); reader.BaseStream.Position += 1; } } @@ -108,7 +110,7 @@ namespace LibHac { public ulong TitleId { get; } public TitleVersion Version { get; } - public CnmtContentType Type { get; } + public ContentType Type { get; } public CnmtContentMetaEntry() { } @@ -116,7 +118,7 @@ namespace LibHac { TitleId = reader.ReadUInt64(); Version = new TitleVersion(reader.ReadUInt32(), true); - Type = (CnmtContentType)reader.ReadByte(); + Type = (ContentType)reader.ReadByte(); reader.BaseStream.Position += 3; } } @@ -199,7 +201,7 @@ namespace LibHac { public ulong TitleId { get; } public TitleVersion Version { get; } - public TitleType Type { get; } + public ContentMetaType Type { get; } public byte[] Hash { get; } public short ContentCount { get; } public short CnmtPrevMetaEntryField32 { get; } @@ -209,7 +211,7 @@ namespace LibHac { TitleId = reader.ReadUInt64(); Version = new TitleVersion(reader.ReadUInt32()); - Type = (TitleType)reader.ReadByte(); + Type = (ContentMetaType)reader.ReadByte(); reader.BaseStream.Position += 3; Hash = reader.ReadBytes(0x20); ContentCount = reader.ReadInt16(); @@ -265,8 +267,8 @@ namespace LibHac public long SizeOld { get; } public long SizeNew { get; } public short FragmentCount { get; } - public CnmtContentType Type { get; } - public CnmtDeltaType DeltaType { get; } + public ContentType Type { get; } + public UpdateType DeltaType { get; } public int FragmentSetInfoField30 { get; } @@ -281,8 +283,8 @@ namespace LibHac SizeNew = reader.ReadUInt32(); FragmentCount = reader.ReadInt16(); - Type = (CnmtContentType)reader.ReadByte(); - DeltaType = (CnmtDeltaType)reader.ReadByte(); + Type = (ContentType)reader.ReadByte(); + DeltaType = (UpdateType)reader.ReadByte(); FragmentSetInfoField30 = reader.ReadInt32(); } } @@ -291,14 +293,14 @@ namespace LibHac { public byte[] NcaId { get; } public long Size { get; } - public CnmtContentType Type { get; } + public ContentType Type { get; } public CnmtPrevContent(BinaryReader reader) { NcaId = reader.ReadBytes(0x10); Size = reader.ReadUInt32(); Size |= (long)reader.ReadUInt16() << 32; - Type = (CnmtContentType)reader.ReadByte(); + Type = (ContentType)reader.ReadByte(); reader.BaseStream.Position += 1; } } @@ -314,35 +316,4 @@ namespace LibHac FragmentIndex = reader.ReadInt16(); } } - - public enum CnmtContentType - { - Meta, - Program, - Data, - Control, - HtmlDocument, - LegalInformation, - DeltaFragment - } - - public enum CnmtDeltaType - { - Delta, - Replace, - NewContent - } - - public enum TitleType - { - SystemProgram = 1, - SystemData, - SystemUpdate, - BootImagePackage, - BootImagePackageSafe, - Application = 0x80, - Patch, - AddOnContent, - Delta - } } diff --git a/src/LibHac/Common/Id128.cs b/src/LibHac/Common/Id128.cs new file mode 100644 index 00000000..eab8ab95 --- /dev/null +++ b/src/LibHac/Common/Id128.cs @@ -0,0 +1,101 @@ +using System; +using System.Runtime.InteropServices; + +namespace LibHac.Common +{ + /// + /// A generic 128-bit ID value. + /// + [StructLayout(LayoutKind.Sequential, Size = 0x10)] + public struct Id128 : IEquatable, IComparable, IComparable + { + public readonly ulong High; + public readonly ulong Low; + + public static Id128 InvalidId = new Id128(0, 0); + + public Id128(ulong high, ulong low) + { + High = high; + Low = low; + } + + public Id128(ReadOnlySpan uid) + { + ReadOnlySpan longs = MemoryMarshal.Cast(uid); + + High = longs[0]; + Low = longs[1]; + } + + public bool Equals(Id128 other) + { + return High == other.High && Low == other.Low; + } + + public override bool Equals(object obj) + { + return obj is Fs.UserId other && Equals(other); + } + + public override int GetHashCode() + { + unchecked + { + return (High.GetHashCode() * 397) ^ Low.GetHashCode(); + } + } + + public int CompareTo(Id128 other) + { + // ReSharper disable ImpureMethodCallOnReadonlyValueField + int highComparison = High.CompareTo(other.High); + if (highComparison != 0) return highComparison; + return Low.CompareTo(other.Low); + // ReSharper restore ImpureMethodCallOnReadonlyValueField + } + + public int CompareTo(object obj) + { + if (obj is null) return 1; + return obj is Id128 other ? CompareTo(other) : throw new ArgumentException($"Object must be of type {nameof(Id128)}"); + } + + public void ToBytes(Span output) + { + Span longs = MemoryMarshal.Cast(output); + + longs[0] = High; + longs[1] = Low; + } + + public static bool operator ==(Id128 left, Id128 right) + { + return left.Equals(right); + } + + public static bool operator !=(Id128 left, Id128 right) + { + return !left.Equals(right); + } + public static bool operator <(Id128 left, Id128 right) + { + return left.CompareTo(right) < 0; + } + + public static bool operator >(Id128 left, Id128 right) + { + return left.CompareTo(right) > 0; + } + + public static bool operator <=(Id128 left, Id128 right) + { + return left.CompareTo(right) <= 0; + } + + public static bool operator >=(Id128 left, Id128 right) + { + return left.CompareTo(right) >= 0; + } + } +} diff --git a/src/LibHac/Fs/NcaUtils/Nca.cs b/src/LibHac/Fs/NcaUtils/Nca.cs index bad42048..430ef459 100644 --- a/src/LibHac/Fs/NcaUtils/Nca.cs +++ b/src/LibHac/Fs/NcaUtils/Nca.cs @@ -516,7 +516,7 @@ namespace LibHac.Fs.NcaUtils return Header.VerifySignature1(Keyset.NcaHdrFixedKeyModulus); } - internal void GenerateAesCounter(int sectionIndex, CnmtContentType type, int minorVersion) + internal void GenerateAesCounter(int sectionIndex, Ncm.ContentType type, int minorVersion) { int counterType; int counterVersion; @@ -527,14 +527,14 @@ namespace LibHac.Fs.NcaUtils switch (type) { - case CnmtContentType.Program: + case Ncm.ContentType.Program: counterType = sectionIndex + 1; break; - case CnmtContentType.HtmlDocument: - counterType = (int)CnmtContentType.HtmlDocument; + case Ncm.ContentType.HtmlDocument: + counterType = (int)Ncm.ContentType.HtmlDocument; break; - case CnmtContentType.LegalInformation: - counterType = (int)CnmtContentType.LegalInformation; + case Ncm.ContentType.LegalInformation: + counterType = (int)Ncm.ContentType.LegalInformation; break; default: counterType = 0; diff --git a/src/LibHac/Fs/RightsId.cs b/src/LibHac/Fs/RightsId.cs new file mode 100644 index 00000000..11f0969d --- /dev/null +++ b/src/LibHac/Fs/RightsId.cs @@ -0,0 +1,47 @@ +using System; +using System.Runtime.InteropServices; +using LibHac.Common; + +namespace LibHac.Fs +{ + [StructLayout(LayoutKind.Sequential, Size = 0x10)] + public struct RightsId : IEquatable, IComparable, IComparable + { + public readonly Id128 Id; + + public RightsId(ulong high, ulong low) + { + Id = new Id128(high, low); + } + + public RightsId(ReadOnlySpan uid) + { + Id = new Id128(uid); + } + + public bool Equals(RightsId other) => Id == other.Id; + public override bool Equals(object obj) => obj is RightsId other && Equals(other); + + public override int GetHashCode() => Id.GetHashCode(); + + // ReSharper disable once ImpureMethodCallOnReadonlyValueField + public int CompareTo(RightsId other) => Id.CompareTo(other.Id); + + public int CompareTo(object obj) + { + if (obj is null) return 1; + return obj is RightsId other ? CompareTo(other) : throw new ArgumentException($"Object must be of type {nameof(RightsId)}"); + } + + // ReSharper disable once ImpureMethodCallOnReadonlyValueField + public void ToBytes(Span output) => Id.ToBytes(output); + + public static bool operator ==(RightsId left, RightsId right) => left.Equals(right); + public static bool operator !=(RightsId left, RightsId right) => !left.Equals(right); + + public static bool operator <(RightsId left, RightsId right) => left.CompareTo(right) < 0; + public static bool operator >(RightsId left, RightsId right) => left.CompareTo(right) > 0; + public static bool operator <=(RightsId left, RightsId right) => left.CompareTo(right) <= 0; + public static bool operator >=(RightsId left, RightsId right) => left.CompareTo(right) >= 0; + } +} \ No newline at end of file diff --git a/src/LibHac/Fs/UserId.cs b/src/LibHac/Fs/UserId.cs index 3045249b..4a14fc46 100644 --- a/src/LibHac/Fs/UserId.cs +++ b/src/LibHac/Fs/UserId.cs @@ -1,66 +1,47 @@ using System; using System.Runtime.InteropServices; +using LibHac.Common; namespace LibHac.Fs { + [StructLayout(LayoutKind.Sequential, Size = 0x10)] public struct UserId : IEquatable, IComparable, IComparable { - public readonly ulong High; - public readonly ulong Low; + public readonly Id128 Id; public UserId(ulong high, ulong low) { - High = high; - Low = low; + Id = new Id128(high, low); } public UserId(ReadOnlySpan uid) { - ReadOnlySpan longs = MemoryMarshal.Cast(uid); - - High = longs[0]; - Low = longs[1]; + Id = new Id128(uid); } - public bool Equals(UserId other) - { - return High == other.High && Low == other.Low; - } + public bool Equals(UserId other) => Id == other.Id; + public override bool Equals(object obj) => obj is UserId other && Equals(other); - public override bool Equals(object obj) - { - return obj is UserId other && Equals(other); - } + public override int GetHashCode() => Id.GetHashCode(); - public override int GetHashCode() - { - unchecked - { - return (High.GetHashCode() * 397) ^ Low.GetHashCode(); - } - } - - public int CompareTo(UserId other) - { - // ReSharper disable ImpureMethodCallOnReadonlyValueField - int highComparison = High.CompareTo(other.High); - if (highComparison != 0) return highComparison; - return Low.CompareTo(other.Low); - // ReSharper restore ImpureMethodCallOnReadonlyValueField - } + // ReSharper disable once ImpureMethodCallOnReadonlyValueField + public int CompareTo(UserId other) => Id.CompareTo(other.Id); public int CompareTo(object obj) { - if (ReferenceEquals(null, obj)) return 1; + if (obj is null) return 1; return obj is UserId other ? CompareTo(other) : throw new ArgumentException($"Object must be of type {nameof(UserId)}"); } - public void ToBytes(Span output) - { - Span longs = MemoryMarshal.Cast(output); + // ReSharper disable once ImpureMethodCallOnReadonlyValueField + public void ToBytes(Span output) => Id.ToBytes(output); - longs[0] = High; - longs[1] = Low; - } + public static bool operator ==(UserId left, UserId right) => left.Equals(right); + public static bool operator !=(UserId left, UserId right) => !left.Equals(right); + + public static bool operator <(UserId left, UserId right) => left.CompareTo(right) < 0; + public static bool operator >(UserId left, UserId right) => left.CompareTo(right) > 0; + public static bool operator <=(UserId left, UserId right) => left.CompareTo(right) <= 0; + public static bool operator >=(UserId left, UserId right) => left.CompareTo(right) >= 0; } } diff --git a/src/LibHac/Ncm/ContentEnums.cs b/src/LibHac/Ncm/ContentEnums.cs new file mode 100644 index 00000000..e9197e82 --- /dev/null +++ b/src/LibHac/Ncm/ContentEnums.cs @@ -0,0 +1,40 @@ +namespace LibHac.Ncm +{ + public enum ContentType : byte + { + Meta = 0, + Program = 1, + Data = 2, + Control = 3, + HtmlDocument = 4, + LegalInformation = 5, + DeltaFragment = 6 + } + + public enum ContentMetaType : byte + { + SystemProgram = 1, + SystemData = 2, + SystemUpdate = 3, + BootImagePackage = 4, + BootImagePackageSafe = 5, + Application = 0x80, + Patch = 0x81, + AddOnContent = 0x82, + Delta = 0x83 + } + + public enum ContentMetaAttribute : byte + { + None = 0, + IncludesExFatDriver = 1, + Rebootless = 2 + } + + public enum UpdateType : byte + { + ApplyAsDelta = 0, + Overwrite = 1, + Create = 2 + } +} diff --git a/src/LibHac/Ncm/ContentId.cs b/src/LibHac/Ncm/ContentId.cs new file mode 100644 index 00000000..75c1d120 --- /dev/null +++ b/src/LibHac/Ncm/ContentId.cs @@ -0,0 +1,47 @@ +using System; +using System.Runtime.InteropServices; +using LibHac.Common; + +namespace LibHac.Ncm +{ + [StructLayout(LayoutKind.Sequential, Size = 0x10)] + public struct ContentId : IEquatable, IComparable, IComparable + { + public readonly Id128 Id; + + public ContentId(ulong high, ulong low) + { + Id = new Id128(high, low); + } + + public ContentId(ReadOnlySpan uid) + { + Id = new Id128(uid); + } + + public bool Equals(ContentId other) => Id == other.Id; + public override bool Equals(object obj) => obj is ContentId other && Equals(other); + + public override int GetHashCode() => Id.GetHashCode(); + + // ReSharper disable once ImpureMethodCallOnReadonlyValueField + public int CompareTo(ContentId other) => Id.CompareTo(other.Id); + + public int CompareTo(object obj) + { + if (obj is null) return 1; + return obj is ContentId other ? CompareTo(other) : throw new ArgumentException($"Object must be of type {nameof(ContentId)}"); + } + + // ReSharper disable once ImpureMethodCallOnReadonlyValueField + public void ToBytes(Span output) => Id.ToBytes(output); + + public static bool operator ==(ContentId left, ContentId right) => left.Equals(right); + public static bool operator !=(ContentId left, ContentId right) => !left.Equals(right); + + public static bool operator <(ContentId left, ContentId right) => left.CompareTo(right) < 0; + public static bool operator >(ContentId left, ContentId right) => left.CompareTo(right) > 0; + public static bool operator <=(ContentId left, ContentId right) => left.CompareTo(right) <= 0; + public static bool operator >=(ContentId left, ContentId right) => left.CompareTo(right) >= 0; + } +} \ No newline at end of file diff --git a/src/LibHac/Ncm/ContentMetaKey.cs b/src/LibHac/Ncm/ContentMetaKey.cs index fda438a4..bdbcbce2 100644 --- a/src/LibHac/Ncm/ContentMetaKey.cs +++ b/src/LibHac/Ncm/ContentMetaKey.cs @@ -8,8 +8,8 @@ namespace LibHac.Ncm { public ulong TitleId { get; private set; } public uint Version { get; private set; } - public byte Type { get; private set; } - public byte Flags { get; private set; } + public ContentMetaType Type { get; private set; } + public ContentMetaAttribute Attributes { get; private set; } public int ExportSize => 0x10; private bool _isFrozen; @@ -20,8 +20,8 @@ namespace LibHac.Ncm BinaryPrimitives.WriteUInt64LittleEndian(output, TitleId); BinaryPrimitives.WriteUInt32LittleEndian(output.Slice(8), Version); - output[0xC] = Type; - output[0xD] = Flags; + output[0xC] = (byte)Type; + output[0xD] = (byte)Attributes; } public void FromBytes(ReadOnlySpan input) @@ -31,8 +31,8 @@ namespace LibHac.Ncm TitleId = BinaryPrimitives.ReadUInt64LittleEndian(input); Version = BinaryPrimitives.ReadUInt32LittleEndian(input.Slice(8)); - Type = input[0xC]; - Flags = input[0xD]; + Type = (ContentMetaType)input[0xC]; + Attributes = (ContentMetaAttribute)input[0xD]; } public void Freeze() => _isFrozen = true; @@ -40,7 +40,7 @@ namespace LibHac.Ncm public bool Equals(ContentMetaKey other) { return other != null && TitleId == other.TitleId && Version == other.Version && - Type == other.Type && Flags == other.Flags; + Type == other.Type && Attributes == other.Attributes; } public override bool Equals(object obj) @@ -56,7 +56,7 @@ namespace LibHac.Ncm int hashCode = TitleId.GetHashCode(); hashCode = (hashCode * 397) ^ (int)Version; hashCode = (hashCode * 397) ^ Type.GetHashCode(); - hashCode = (hashCode * 397) ^ Flags.GetHashCode(); + hashCode = (hashCode * 397) ^ Attributes.GetHashCode(); return hashCode; // ReSharper restore NonReadonlyMemberInGetHashCode } @@ -72,7 +72,7 @@ namespace LibHac.Ncm if (versionComparison != 0) return versionComparison; int typeComparison = Type.CompareTo(other.Type); if (typeComparison != 0) return typeComparison; - return Flags.CompareTo(other.Flags); + return Attributes.CompareTo(other.Attributes); } public int CompareTo(object obj) diff --git a/src/LibHac/Ncm/ContentMetaStructs.cs b/src/LibHac/Ncm/ContentMetaStructs.cs new file mode 100644 index 00000000..992b115d --- /dev/null +++ b/src/LibHac/Ncm/ContentMetaStructs.cs @@ -0,0 +1,20 @@ +using System.Runtime.InteropServices; + +namespace LibHac.Ncm +{ + [StructLayout(LayoutKind.Sequential, Size = 0x18, Pack = 1)] + public struct ContentInfo + { + public ContentId contentId; + public uint size1; + public ushort size2; + private ContentType contentType; + private byte IdOffset; + } + + public class ApplicationContentMetaKey + { + public ContentMetaKey Key { get; set; } + public ulong TitleId { get; set; } + } +} diff --git a/src/LibHac/Ncm/IContentMetaDatabase.cs b/src/LibHac/Ncm/IContentMetaDatabase.cs new file mode 100644 index 00000000..90307aee --- /dev/null +++ b/src/LibHac/Ncm/IContentMetaDatabase.cs @@ -0,0 +1,32 @@ +using System; + +namespace LibHac.Ncm +{ + public interface IContentMetaDatabase + { + Result Set(ContentMetaKey key, ReadOnlySpan value); + Result Get(out long valueSize, ContentMetaKey key, Span valueBuffer); + Result Remove(ContentMetaKey key); + Result GetContentIdByType(out ContentId contentId, ContentMetaKey key, ContentType type); + Result ListContentInfo(out int count, Span outInfo, ContentMetaKey key, int startIndex); + + Result List(out int totalEntryCount, out int matchedEntryCount, Span keys, ContentMetaType type, + ulong applicationTitleId, ulong minTitleId, ulong maxTitleId, ContentMetaAttribute attributes); + + Result GetLatestContentMetaKey(out ContentMetaKey key, ulong titleId); + Result ListApplication(out int totalEntryCount, out int matchedEntryCount, Span keys, ContentMetaType type); + Result Has(out bool hasKey, ContentMetaKey key); + Result HasAll(out bool hasAllKeys, ReadOnlySpan key); + Result GetSize(out long size, ContentMetaKey key); + Result GetRequiredSystemVersion(out int version, ContentMetaKey key); + Result GetPatchId(out ulong titleId, ContentMetaKey key); + Result DisableForcibly(); + Result LookupOrphanContent(Span outOrphaned, ReadOnlySpan contentIds); + Result Commit(); + Result HasContent(out bool hasContent, ContentMetaKey key, ContentId contentId); + Result ListContentMetaInfo(out int entryCount, Span outInfo, ContentMetaKey key, int startIndex); + Result GetAttributes(out ContentMetaAttribute attributes, ContentMetaKey key); + Result GetRequiredApplicationVersion(out int version, ContentMetaKey key); + //Result GetContentIdByTypeAndIdOffset(out ContentId contentId, ContentMetaKey key, ContentType type, byte idOffset); + } +} \ No newline at end of file diff --git a/src/LibHac/Ncm/IContentStorage.cs b/src/LibHac/Ncm/IContentStorage.cs new file mode 100644 index 00000000..2cd17ea9 --- /dev/null +++ b/src/LibHac/Ncm/IContentStorage.cs @@ -0,0 +1,37 @@ +using System; +using LibHac.Fs; + +namespace LibHac.Ncm +{ + public interface IContentStorage + { + Result GeneratePlaceHolderId(out PlaceHolderId placeHolderId); + Result CreatePlaceHolder(PlaceHolderId placeHolderId, ContentId contentId, long fileSize); + Result DeletePlaceHolder(PlaceHolderId placeHolderId); + Result HasPlaceHolder(out bool hasPlaceHolder, PlaceHolderId placeHolderId); + Result WritePlaceHolder(PlaceHolderId placeHolderId, long offset, ReadOnlySpan buffer); + Result Register(PlaceHolderId placeHolderId, ContentId contentId); + Result Delete(ContentId contentId); + Result Has(out bool hasContent, ContentId contentId); + Result GetPath(Span outPath, ContentId contentId); + Result GetPlaceHolderPath(Span outPath, PlaceHolderId placeHolderId); + Result CleanupAllPlaceHolder(); + Result ListPlaceHolder(out int count, Span placeHolderIds); + Result GetContentCount(out int count); + Result ListContentId(out int count, Span contentIds, int startOffset); + Result GetSizeFromContentId(out long size, ContentId contentId); + Result DisableForcibly(); + Result RevertToPlaceHolder(PlaceHolderId placeHolderId, ContentId oldContentId, ContentId newContentId); + Result SetPlaceHolderSize(PlaceHolderId placeHolderId, long size); + Result ReadContentIdFile(Span buffer, long size, ContentId contentId, long offset); + Result GetRightsIdFromPlaceHolderId(out RightsId rightsId, out byte keyGeneration, PlaceHolderId placeHolderId); + Result GetRightsIdFromContentId(out RightsId rightsId, out byte keyGeneration, ContentId contentId); + Result WriteContentForDebug(ContentId contentId, long offset, ReadOnlySpan buffer); + Result GetFreeSpaceSize(out long size); + Result GetTotalSpaceSize(out long size); + Result FlushPlaceHolder(); + //Result GetSizeFromPlaceHolderId(out long size, PlaceHolderId placeHolderId); + //Result RepairInvalidFileAttribute(); + //Result GetRightsIdFromPlaceHolderIdWithCache(out RightsId rightsId, out byte keyGeneration, PlaceHolderId placeHolderId, out ContentId cacheContentId); + } +} \ No newline at end of file diff --git a/src/LibHac/Ncm/PlaceHolderId.cs b/src/LibHac/Ncm/PlaceHolderId.cs new file mode 100644 index 00000000..0e31a14d --- /dev/null +++ b/src/LibHac/Ncm/PlaceHolderId.cs @@ -0,0 +1,47 @@ +using System; +using System.Runtime.InteropServices; +using LibHac.Common; + +namespace LibHac.Ncm +{ + [StructLayout(LayoutKind.Sequential, Size = 0x10)] + public struct PlaceHolderId : IEquatable, IComparable, IComparable + { + public readonly Id128 Id; + + public PlaceHolderId(ulong high, ulong low) + { + Id = new Id128(high, low); + } + + public PlaceHolderId(ReadOnlySpan uid) + { + Id = new Id128(uid); + } + + public bool Equals(PlaceHolderId other) => Id == other.Id; + public override bool Equals(object obj) => obj is PlaceHolderId other && Equals(other); + + public override int GetHashCode() => Id.GetHashCode(); + + // ReSharper disable once ImpureMethodCallOnReadonlyValueField + public int CompareTo(PlaceHolderId other) => Id.CompareTo(other.Id); + + public int CompareTo(object obj) + { + if (obj is null) return 1; + return obj is PlaceHolderId other ? CompareTo(other) : throw new ArgumentException($"Object must be of type {nameof(PlaceHolderId)}"); + } + + // ReSharper disable once ImpureMethodCallOnReadonlyValueField + public void ToBytes(Span output) => Id.ToBytes(output); + + public static bool operator ==(PlaceHolderId left, PlaceHolderId right) => left.Equals(right); + public static bool operator !=(PlaceHolderId left, PlaceHolderId right) => !left.Equals(right); + + public static bool operator <(PlaceHolderId left, PlaceHolderId right) => left.CompareTo(right) < 0; + public static bool operator >(PlaceHolderId left, PlaceHolderId right) => left.CompareTo(right) > 0; + public static bool operator <=(PlaceHolderId left, PlaceHolderId right) => left.CompareTo(right) <= 0; + public static bool operator >=(PlaceHolderId left, PlaceHolderId right) => left.CompareTo(right) >= 0; + } +} \ No newline at end of file diff --git a/src/LibHac/SwitchFs.cs b/src/LibHac/SwitchFs.cs index b3070ec7..3e39ae1d 100644 --- a/src/LibHac/SwitchFs.cs +++ b/src/LibHac/SwitchFs.cs @@ -6,6 +6,7 @@ using System.Linq; using LibHac.Fs; using LibHac.Fs.NcaUtils; using LibHac.Fs.Save; +using LibHac.Ncm; namespace LibHac { @@ -81,7 +82,7 @@ namespace LibHac nca = new SwitchFsNca(new Nca(Keyset, storage)); nca.NcaId = GetNcaFilename(fileEntry.Name, nca); - string extension = nca.Nca.Header.ContentType == ContentType.Meta ? ".cnmt.nca" : ".nca"; + string extension = nca.Nca.Header.ContentType == Fs.NcaUtils.ContentType.Meta ? ".cnmt.nca" : ".nca"; nca.Filename = nca.NcaId + extension; } catch (MissingKeyException ex) @@ -131,7 +132,7 @@ namespace LibHac private void ReadTitles() { - foreach (SwitchFsNca nca in Ncas.Values.Where(x => x.Nca.Header.ContentType == ContentType.Meta)) + foreach (SwitchFsNca nca in Ncas.Values.Where(x => x.Nca.Header.ContentType == Fs.NcaUtils.ContentType.Meta)) { try { @@ -160,11 +161,11 @@ namespace LibHac switch (content.Type) { - case CnmtContentType.Program: - case CnmtContentType.Data: + case Ncm.ContentType.Program: + case Ncm.ContentType.Data: title.MainNca = contentNca; break; - case CnmtContentType.Control: + case Ncm.ContentType.Control: title.ControlNca = contentNca; break; } @@ -201,7 +202,7 @@ namespace LibHac private void CreateApplications() { - foreach (Title title in Titles.Values.Where(x => x.Metadata.Type >= TitleType.Application)) + foreach (Title title in Titles.Values.Where(x => x.Metadata.Type >= ContentMetaType.Application)) { Cnmt meta = title.Metadata; ulong appId = meta.ApplicationTitleId; @@ -229,7 +230,7 @@ namespace LibHac private string GetNcaFilename(string name, SwitchFsNca nca) { - if (nca.Nca.Header.ContentType != ContentType.Meta || !name.EndsWith(".cnmt.nca")) + if (nca.Nca.Header.ContentType != Fs.NcaUtils.ContentType.Meta || !name.EndsWith(".cnmt.nca")) { return Path.GetFileNameWithoutExtension(name); } @@ -343,16 +344,16 @@ namespace LibHac switch (title.Metadata.Type) { - case TitleType.Application: + case ContentMetaType.Application: Main = title; break; - case TitleType.Patch: + case ContentMetaType.Patch: Patch = title; break; - case TitleType.AddOnContent: + case ContentMetaType.AddOnContent: AddOnContent.Add(title); break; - case TitleType.Delta: + case ContentMetaType.Delta: break; }