Add content storage and content meta DB interfaces

This commit is contained in:
Alex Barney 2019-07-22 20:38:59 -05:00
parent e9376efba7
commit c3dfaf14e8
13 changed files with 439 additions and 115 deletions

View file

@ -1,6 +1,8 @@
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using LibHac.Fs.NcaUtils; using LibHac.Fs.NcaUtils;
using LibHac.Ncm;
using ContentType = LibHac.Ncm.ContentType;
namespace LibHac namespace LibHac
{ {
@ -8,7 +10,7 @@ namespace LibHac
{ {
public ulong TitleId { get; } public ulong TitleId { get; }
public TitleVersion TitleVersion { get; } public TitleVersion TitleVersion { get; }
public TitleType Type { get; } public ContentMetaType Type { get; }
public byte FieldD { get; } public byte FieldD { get; }
public int TableOffset { get; } public int TableOffset { get; }
public int ContentEntryCount { get; } public int ContentEntryCount { get; }
@ -34,8 +36,8 @@ namespace LibHac
{ {
TitleId = reader.ReadUInt64(); TitleId = reader.ReadUInt64();
uint version = reader.ReadUInt32(); uint version = reader.ReadUInt32();
Type = (TitleType)reader.ReadByte(); Type = (ContentMetaType)reader.ReadByte();
TitleVersion = new TitleVersion(version, Type < TitleType.Application); TitleVersion = new TitleVersion(version, Type < ContentMetaType.Application);
FieldD = reader.ReadByte(); FieldD = reader.ReadByte();
TableOffset = reader.ReadUInt16(); TableOffset = reader.ReadUInt16();
ContentEntryCount = reader.ReadUInt16(); ContentEntryCount = reader.ReadUInt16();
@ -44,16 +46,16 @@ namespace LibHac
switch (Type) switch (Type)
{ {
case TitleType.Application: case ContentMetaType.Application:
ApplicationTitleId = TitleId; ApplicationTitleId = TitleId;
PatchTitleId = reader.ReadUInt64(); PatchTitleId = reader.ReadUInt64();
MinimumSystemVersion = new TitleVersion(reader.ReadUInt32(), true); MinimumSystemVersion = new TitleVersion(reader.ReadUInt32(), true);
break; break;
case TitleType.Patch: case ContentMetaType.Patch:
ApplicationTitleId = reader.ReadUInt64(); ApplicationTitleId = reader.ReadUInt64();
MinimumSystemVersion = new TitleVersion(reader.ReadUInt32(), true); MinimumSystemVersion = new TitleVersion(reader.ReadUInt32(), true);
break; break;
case TitleType.AddOnContent: case ContentMetaType.AddOnContent:
ApplicationTitleId = reader.ReadUInt64(); ApplicationTitleId = reader.ReadUInt64();
MinimumApplicationVersion = new TitleVersion(reader.ReadUInt32()); MinimumApplicationVersion = new TitleVersion(reader.ReadUInt32());
break; break;
@ -74,7 +76,7 @@ namespace LibHac
MetaEntries[i] = new CnmtContentMetaEntry(reader); MetaEntries[i] = new CnmtContentMetaEntry(reader);
} }
if (Type == TitleType.Patch) if (Type == ContentMetaType.Patch)
{ {
ExtendedData = new CnmtExtended(reader); ExtendedData = new CnmtExtended(reader);
} }
@ -89,7 +91,7 @@ namespace LibHac
public byte[] Hash { get; set; } public byte[] Hash { get; set; }
public byte[] NcaId { get; set; } public byte[] NcaId { get; set; }
public long Size { get; set; } public long Size { get; set; }
public CnmtContentType Type { get; set; } public ContentType Type { get; set; }
public CnmtContentEntry() { } public CnmtContentEntry() { }
@ -99,7 +101,7 @@ namespace LibHac
NcaId = reader.ReadBytes(0x10); NcaId = reader.ReadBytes(0x10);
Size = reader.ReadUInt32(); Size = reader.ReadUInt32();
Size |= (long)reader.ReadUInt16() << 32; Size |= (long)reader.ReadUInt16() << 32;
Type = (CnmtContentType)reader.ReadByte(); Type = (ContentType)reader.ReadByte();
reader.BaseStream.Position += 1; reader.BaseStream.Position += 1;
} }
} }
@ -108,7 +110,7 @@ namespace LibHac
{ {
public ulong TitleId { get; } public ulong TitleId { get; }
public TitleVersion Version { get; } public TitleVersion Version { get; }
public CnmtContentType Type { get; } public ContentType Type { get; }
public CnmtContentMetaEntry() { } public CnmtContentMetaEntry() { }
@ -116,7 +118,7 @@ namespace LibHac
{ {
TitleId = reader.ReadUInt64(); TitleId = reader.ReadUInt64();
Version = new TitleVersion(reader.ReadUInt32(), true); Version = new TitleVersion(reader.ReadUInt32(), true);
Type = (CnmtContentType)reader.ReadByte(); Type = (ContentType)reader.ReadByte();
reader.BaseStream.Position += 3; reader.BaseStream.Position += 3;
} }
} }
@ -199,7 +201,7 @@ namespace LibHac
{ {
public ulong TitleId { get; } public ulong TitleId { get; }
public TitleVersion Version { get; } public TitleVersion Version { get; }
public TitleType Type { get; } public ContentMetaType Type { get; }
public byte[] Hash { get; } public byte[] Hash { get; }
public short ContentCount { get; } public short ContentCount { get; }
public short CnmtPrevMetaEntryField32 { get; } public short CnmtPrevMetaEntryField32 { get; }
@ -209,7 +211,7 @@ namespace LibHac
{ {
TitleId = reader.ReadUInt64(); TitleId = reader.ReadUInt64();
Version = new TitleVersion(reader.ReadUInt32()); Version = new TitleVersion(reader.ReadUInt32());
Type = (TitleType)reader.ReadByte(); Type = (ContentMetaType)reader.ReadByte();
reader.BaseStream.Position += 3; reader.BaseStream.Position += 3;
Hash = reader.ReadBytes(0x20); Hash = reader.ReadBytes(0x20);
ContentCount = reader.ReadInt16(); ContentCount = reader.ReadInt16();
@ -265,8 +267,8 @@ namespace LibHac
public long SizeOld { get; } public long SizeOld { get; }
public long SizeNew { get; } public long SizeNew { get; }
public short FragmentCount { get; } public short FragmentCount { get; }
public CnmtContentType Type { get; } public ContentType Type { get; }
public CnmtDeltaType DeltaType { get; } public UpdateType DeltaType { get; }
public int FragmentSetInfoField30 { get; } public int FragmentSetInfoField30 { get; }
@ -281,8 +283,8 @@ namespace LibHac
SizeNew = reader.ReadUInt32(); SizeNew = reader.ReadUInt32();
FragmentCount = reader.ReadInt16(); FragmentCount = reader.ReadInt16();
Type = (CnmtContentType)reader.ReadByte(); Type = (ContentType)reader.ReadByte();
DeltaType = (CnmtDeltaType)reader.ReadByte(); DeltaType = (UpdateType)reader.ReadByte();
FragmentSetInfoField30 = reader.ReadInt32(); FragmentSetInfoField30 = reader.ReadInt32();
} }
} }
@ -291,14 +293,14 @@ namespace LibHac
{ {
public byte[] NcaId { get; } public byte[] NcaId { get; }
public long Size { get; } public long Size { get; }
public CnmtContentType Type { get; } public ContentType Type { get; }
public CnmtPrevContent(BinaryReader reader) public CnmtPrevContent(BinaryReader reader)
{ {
NcaId = reader.ReadBytes(0x10); NcaId = reader.ReadBytes(0x10);
Size = reader.ReadUInt32(); Size = reader.ReadUInt32();
Size |= (long)reader.ReadUInt16() << 32; Size |= (long)reader.ReadUInt16() << 32;
Type = (CnmtContentType)reader.ReadByte(); Type = (ContentType)reader.ReadByte();
reader.BaseStream.Position += 1; reader.BaseStream.Position += 1;
} }
} }
@ -314,35 +316,4 @@ namespace LibHac
FragmentIndex = reader.ReadInt16(); 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
}
} }

101
src/LibHac/Common/Id128.cs Normal file
View file

@ -0,0 +1,101 @@
using System;
using System.Runtime.InteropServices;
namespace LibHac.Common
{
/// <summary>
/// A generic 128-bit ID value.
/// </summary>
[StructLayout(LayoutKind.Sequential, Size = 0x10)]
public struct Id128 : IEquatable<Id128>, IComparable<Id128>, 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<byte> uid)
{
ReadOnlySpan<ulong> longs = MemoryMarshal.Cast<byte, ulong>(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<byte> output)
{
Span<ulong> longs = MemoryMarshal.Cast<byte, ulong>(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;
}
}
}

View file

@ -516,7 +516,7 @@ namespace LibHac.Fs.NcaUtils
return Header.VerifySignature1(Keyset.NcaHdrFixedKeyModulus); 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 counterType;
int counterVersion; int counterVersion;
@ -527,14 +527,14 @@ namespace LibHac.Fs.NcaUtils
switch (type) switch (type)
{ {
case CnmtContentType.Program: case Ncm.ContentType.Program:
counterType = sectionIndex + 1; counterType = sectionIndex + 1;
break; break;
case CnmtContentType.HtmlDocument: case Ncm.ContentType.HtmlDocument:
counterType = (int)CnmtContentType.HtmlDocument; counterType = (int)Ncm.ContentType.HtmlDocument;
break; break;
case CnmtContentType.LegalInformation: case Ncm.ContentType.LegalInformation:
counterType = (int)CnmtContentType.LegalInformation; counterType = (int)Ncm.ContentType.LegalInformation;
break; break;
default: default:
counterType = 0; counterType = 0;

47
src/LibHac/Fs/RightsId.cs Normal file
View file

@ -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<RightsId>, IComparable<RightsId>, IComparable
{
public readonly Id128 Id;
public RightsId(ulong high, ulong low)
{
Id = new Id128(high, low);
}
public RightsId(ReadOnlySpan<byte> 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<byte> 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;
}
}

View file

@ -1,66 +1,47 @@
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using LibHac.Common;
namespace LibHac.Fs namespace LibHac.Fs
{ {
[StructLayout(LayoutKind.Sequential, Size = 0x10)]
public struct UserId : IEquatable<UserId>, IComparable<UserId>, IComparable public struct UserId : IEquatable<UserId>, IComparable<UserId>, IComparable
{ {
public readonly ulong High; public readonly Id128 Id;
public readonly ulong Low;
public UserId(ulong high, ulong low) public UserId(ulong high, ulong low)
{ {
High = high; Id = new Id128(high, low);
Low = low;
} }
public UserId(ReadOnlySpan<byte> uid) public UserId(ReadOnlySpan<byte> uid)
{ {
ReadOnlySpan<ulong> longs = MemoryMarshal.Cast<byte, ulong>(uid); Id = new Id128(uid);
High = longs[0];
Low = longs[1];
} }
public bool Equals(UserId other) public bool Equals(UserId other) => Id == other.Id;
{ public override bool Equals(object obj) => obj is UserId other && Equals(other);
return High == other.High && Low == other.Low;
}
public override bool Equals(object obj) public override int GetHashCode() => Id.GetHashCode();
{
return obj is UserId other && Equals(other);
}
public override int GetHashCode() // ReSharper disable once ImpureMethodCallOnReadonlyValueField
{ public int CompareTo(UserId other) => Id.CompareTo(other.Id);
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
}
public int CompareTo(object obj) 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)}"); return obj is UserId other ? CompareTo(other) : throw new ArgumentException($"Object must be of type {nameof(UserId)}");
} }
public void ToBytes(Span<byte> output) // ReSharper disable once ImpureMethodCallOnReadonlyValueField
{ public void ToBytes(Span<byte> output) => Id.ToBytes(output);
Span<ulong> longs = MemoryMarshal.Cast<byte, ulong>(output);
longs[0] = High; public static bool operator ==(UserId left, UserId right) => left.Equals(right);
longs[1] = Low; 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;
} }
} }

View file

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

View file

@ -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<ContentId>, IComparable<ContentId>, IComparable
{
public readonly Id128 Id;
public ContentId(ulong high, ulong low)
{
Id = new Id128(high, low);
}
public ContentId(ReadOnlySpan<byte> 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<byte> 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;
}
}

View file

@ -8,8 +8,8 @@ namespace LibHac.Ncm
{ {
public ulong TitleId { get; private set; } public ulong TitleId { get; private set; }
public uint Version { get; private set; } public uint Version { get; private set; }
public byte Type { get; private set; } public ContentMetaType Type { get; private set; }
public byte Flags { get; private set; } public ContentMetaAttribute Attributes { get; private set; }
public int ExportSize => 0x10; public int ExportSize => 0x10;
private bool _isFrozen; private bool _isFrozen;
@ -20,8 +20,8 @@ namespace LibHac.Ncm
BinaryPrimitives.WriteUInt64LittleEndian(output, TitleId); BinaryPrimitives.WriteUInt64LittleEndian(output, TitleId);
BinaryPrimitives.WriteUInt32LittleEndian(output.Slice(8), Version); BinaryPrimitives.WriteUInt32LittleEndian(output.Slice(8), Version);
output[0xC] = Type; output[0xC] = (byte)Type;
output[0xD] = Flags; output[0xD] = (byte)Attributes;
} }
public void FromBytes(ReadOnlySpan<byte> input) public void FromBytes(ReadOnlySpan<byte> input)
@ -31,8 +31,8 @@ namespace LibHac.Ncm
TitleId = BinaryPrimitives.ReadUInt64LittleEndian(input); TitleId = BinaryPrimitives.ReadUInt64LittleEndian(input);
Version = BinaryPrimitives.ReadUInt32LittleEndian(input.Slice(8)); Version = BinaryPrimitives.ReadUInt32LittleEndian(input.Slice(8));
Type = input[0xC]; Type = (ContentMetaType)input[0xC];
Flags = input[0xD]; Attributes = (ContentMetaAttribute)input[0xD];
} }
public void Freeze() => _isFrozen = true; public void Freeze() => _isFrozen = true;
@ -40,7 +40,7 @@ namespace LibHac.Ncm
public bool Equals(ContentMetaKey other) public bool Equals(ContentMetaKey other)
{ {
return other != null && TitleId == other.TitleId && Version == other.Version && 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) public override bool Equals(object obj)
@ -56,7 +56,7 @@ namespace LibHac.Ncm
int hashCode = TitleId.GetHashCode(); int hashCode = TitleId.GetHashCode();
hashCode = (hashCode * 397) ^ (int)Version; hashCode = (hashCode * 397) ^ (int)Version;
hashCode = (hashCode * 397) ^ Type.GetHashCode(); hashCode = (hashCode * 397) ^ Type.GetHashCode();
hashCode = (hashCode * 397) ^ Flags.GetHashCode(); hashCode = (hashCode * 397) ^ Attributes.GetHashCode();
return hashCode; return hashCode;
// ReSharper restore NonReadonlyMemberInGetHashCode // ReSharper restore NonReadonlyMemberInGetHashCode
} }
@ -72,7 +72,7 @@ namespace LibHac.Ncm
if (versionComparison != 0) return versionComparison; if (versionComparison != 0) return versionComparison;
int typeComparison = Type.CompareTo(other.Type); int typeComparison = Type.CompareTo(other.Type);
if (typeComparison != 0) return typeComparison; if (typeComparison != 0) return typeComparison;
return Flags.CompareTo(other.Flags); return Attributes.CompareTo(other.Attributes);
} }
public int CompareTo(object obj) public int CompareTo(object obj)

View file

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

View file

@ -0,0 +1,32 @@
using System;
namespace LibHac.Ncm
{
public interface IContentMetaDatabase
{
Result Set(ContentMetaKey key, ReadOnlySpan<byte> value);
Result Get(out long valueSize, ContentMetaKey key, Span<byte> valueBuffer);
Result Remove(ContentMetaKey key);
Result GetContentIdByType(out ContentId contentId, ContentMetaKey key, ContentType type);
Result ListContentInfo(out int count, Span<ContentInfo> outInfo, ContentMetaKey key, int startIndex);
Result List(out int totalEntryCount, out int matchedEntryCount, Span<ContentMetaKey> 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<ApplicationContentMetaKey> keys, ContentMetaType type);
Result Has(out bool hasKey, ContentMetaKey key);
Result HasAll(out bool hasAllKeys, ReadOnlySpan<ContentMetaKey> 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<bool> outOrphaned, ReadOnlySpan<ContentId> contentIds);
Result Commit();
Result HasContent(out bool hasContent, ContentMetaKey key, ContentId contentId);
Result ListContentMetaInfo(out int entryCount, Span<ContentMetaKey> 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);
}
}

View file

@ -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<byte> buffer);
Result Register(PlaceHolderId placeHolderId, ContentId contentId);
Result Delete(ContentId contentId);
Result Has(out bool hasContent, ContentId contentId);
Result GetPath(Span<byte> outPath, ContentId contentId);
Result GetPlaceHolderPath(Span<byte> outPath, PlaceHolderId placeHolderId);
Result CleanupAllPlaceHolder();
Result ListPlaceHolder(out int count, Span<PlaceHolderId> placeHolderIds);
Result GetContentCount(out int count);
Result ListContentId(out int count, Span<ContentId> 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<byte> 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<byte> 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);
}
}

View file

@ -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<PlaceHolderId>, IComparable<PlaceHolderId>, IComparable
{
public readonly Id128 Id;
public PlaceHolderId(ulong high, ulong low)
{
Id = new Id128(high, low);
}
public PlaceHolderId(ReadOnlySpan<byte> 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<byte> 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;
}
}

View file

@ -6,6 +6,7 @@ using System.Linq;
using LibHac.Fs; using LibHac.Fs;
using LibHac.Fs.NcaUtils; using LibHac.Fs.NcaUtils;
using LibHac.Fs.Save; using LibHac.Fs.Save;
using LibHac.Ncm;
namespace LibHac namespace LibHac
{ {
@ -81,7 +82,7 @@ namespace LibHac
nca = new SwitchFsNca(new Nca(Keyset, storage)); nca = new SwitchFsNca(new Nca(Keyset, storage));
nca.NcaId = GetNcaFilename(fileEntry.Name, nca); 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; nca.Filename = nca.NcaId + extension;
} }
catch (MissingKeyException ex) catch (MissingKeyException ex)
@ -131,7 +132,7 @@ namespace LibHac
private void ReadTitles() 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 try
{ {
@ -160,11 +161,11 @@ namespace LibHac
switch (content.Type) switch (content.Type)
{ {
case CnmtContentType.Program: case Ncm.ContentType.Program:
case CnmtContentType.Data: case Ncm.ContentType.Data:
title.MainNca = contentNca; title.MainNca = contentNca;
break; break;
case CnmtContentType.Control: case Ncm.ContentType.Control:
title.ControlNca = contentNca; title.ControlNca = contentNca;
break; break;
} }
@ -201,7 +202,7 @@ namespace LibHac
private void CreateApplications() 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; Cnmt meta = title.Metadata;
ulong appId = meta.ApplicationTitleId; ulong appId = meta.ApplicationTitleId;
@ -229,7 +230,7 @@ namespace LibHac
private string GetNcaFilename(string name, SwitchFsNca nca) 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); return Path.GetFileNameWithoutExtension(name);
} }
@ -343,16 +344,16 @@ namespace LibHac
switch (title.Metadata.Type) switch (title.Metadata.Type)
{ {
case TitleType.Application: case ContentMetaType.Application:
Main = title; Main = title;
break; break;
case TitleType.Patch: case ContentMetaType.Patch:
Patch = title; Patch = title;
break; break;
case TitleType.AddOnContent: case ContentMetaType.AddOnContent:
AddOnContent.Add(title); AddOnContent.Add(title);
break; break;
case TitleType.Delta: case ContentMetaType.Delta:
break; break;
} }