mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Add content storage and content meta DB interfaces
This commit is contained in:
parent
e9376efba7
commit
c3dfaf14e8
13 changed files with 439 additions and 115 deletions
|
@ -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
101
src/LibHac/Common/Id128.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
47
src/LibHac/Fs/RightsId.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
40
src/LibHac/Ncm/ContentEnums.cs
Normal file
40
src/LibHac/Ncm/ContentEnums.cs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
47
src/LibHac/Ncm/ContentId.cs
Normal file
47
src/LibHac/Ncm/ContentId.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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)
|
||||||
|
|
20
src/LibHac/Ncm/ContentMetaStructs.cs
Normal file
20
src/LibHac/Ncm/ContentMetaStructs.cs
Normal 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; }
|
||||||
|
}
|
||||||
|
}
|
32
src/LibHac/Ncm/IContentMetaDatabase.cs
Normal file
32
src/LibHac/Ncm/IContentMetaDatabase.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
37
src/LibHac/Ncm/IContentStorage.cs
Normal file
37
src/LibHac/Ncm/IContentStorage.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
47
src/LibHac/Ncm/PlaceHolderId.cs
Normal file
47
src/LibHac/Ncm/PlaceHolderId.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue