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;
}