mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Remove deprecated classes
This commit is contained in:
parent
0a8fb8a5c0
commit
2793648d37
6 changed files with 0 additions and 864 deletions
|
@ -10,32 +10,6 @@ namespace LibHac.FsSystem
|
|||
{
|
||||
public static class StorageExtensions
|
||||
{
|
||||
[Obsolete("The standard Span-based IStorage methods should be used instead.")]
|
||||
public static Result Read(this IStorage storage, long offset, byte[] buffer, int count, int bufferOffset)
|
||||
{
|
||||
ValidateStorageParameters(buffer, offset, count, bufferOffset);
|
||||
return storage.Read(offset, buffer.AsSpan(bufferOffset, count));
|
||||
}
|
||||
|
||||
[Obsolete("The standard Span-based IStorage methods should be used instead.")]
|
||||
public static Result Write(this IStorage storage, long offset, byte[] buffer, int count, int bufferOffset)
|
||||
{
|
||||
ValidateStorageParameters(buffer, offset, count, bufferOffset);
|
||||
return storage.Write(offset, buffer.AsSpan(bufferOffset, count));
|
||||
}
|
||||
|
||||
// todo: remove this method when the above Read and Write methods are removed
|
||||
// (Hopefully they're still above, or else someone didn't listen)
|
||||
// ReSharper disable ParameterOnlyUsedForPreconditionCheck.Local
|
||||
private static void ValidateStorageParameters(byte[] buffer, long offset, int count, int bufferOffset)
|
||||
// ReSharper restore ParameterOnlyUsedForPreconditionCheck.Local
|
||||
{
|
||||
if (buffer == null) throw new ArgumentNullException(nameof(buffer));
|
||||
if (offset < 0) throw new ArgumentOutOfRangeException(nameof(offset), "Argument must be non-negative.");
|
||||
if (count < 0) throw new ArgumentOutOfRangeException(nameof(count), "Argument must be non-negative.");
|
||||
if (bufferOffset < 0) throw new ArgumentOutOfRangeException(nameof(bufferOffset), "Argument must be non-negative.");
|
||||
}
|
||||
|
||||
public static IStorage Slice(this IStorage storage, long start)
|
||||
{
|
||||
storage.GetSize(out long length).ThrowIfFailure();
|
||||
|
|
|
@ -1,213 +0,0 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs;
|
||||
using LibHac.FsSystem;
|
||||
using LibHac.Kernel;
|
||||
|
||||
namespace LibHac
|
||||
{
|
||||
[Obsolete("This class has been deprecated. LibHac.Loader.KipReader should be used instead.")]
|
||||
public class Kip
|
||||
{
|
||||
private const int HeaderSize = 0x100;
|
||||
|
||||
public KipHeader Header { get; }
|
||||
|
||||
public int[] SectionOffsets { get; } = new int[6];
|
||||
public int Size { get; }
|
||||
|
||||
private IStorage Storage { get; }
|
||||
|
||||
public Kip(IStorage storage)
|
||||
{
|
||||
Header = new KipHeader(storage);
|
||||
|
||||
Size = HeaderSize;
|
||||
|
||||
for (int index = 0; index < Header.Sections.Length; index++)
|
||||
{
|
||||
int sectionSize = Header.Sections[index].CompressedSize;
|
||||
SectionOffsets[index] = Size;
|
||||
Size += sectionSize;
|
||||
}
|
||||
|
||||
Storage = storage.Slice(0, Size);
|
||||
}
|
||||
|
||||
public IStorage OpenSection(int index)
|
||||
{
|
||||
if (index < 0 || index > 5)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(index), "Section index must be between 0-5");
|
||||
}
|
||||
|
||||
return Storage.Slice(SectionOffsets[index], Header.Sections[index].CompressedSize);
|
||||
}
|
||||
|
||||
public byte[] DecompressSection(int index)
|
||||
{
|
||||
IStorage compStorage = OpenSection(index);
|
||||
compStorage.GetSize(out long compressedSize).ThrowIfFailure();
|
||||
|
||||
byte[] compressed = new byte[compressedSize];
|
||||
compStorage.Read(0, compressed).ThrowIfFailure();
|
||||
|
||||
return DecompressBlz(compressed);
|
||||
}
|
||||
|
||||
public IStorage OpenRawFile() => Storage;
|
||||
|
||||
private static byte[] DecompressBlz(byte[] compressed)
|
||||
{
|
||||
int additionalSize = BitConverter.ToInt32(compressed, compressed.Length - 4);
|
||||
int headerSize = BitConverter.ToInt32(compressed, compressed.Length - 8);
|
||||
int totalCompSize = BitConverter.ToInt32(compressed, compressed.Length - 12);
|
||||
|
||||
byte[] decompressed = new byte[totalCompSize + additionalSize];
|
||||
|
||||
int inOffset = totalCompSize - headerSize;
|
||||
int outOffset = totalCompSize + additionalSize;
|
||||
|
||||
while (outOffset > 0)
|
||||
{
|
||||
byte control = compressed[--inOffset];
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
if ((control & 0x80) != 0)
|
||||
{
|
||||
if (inOffset < 2) throw new InvalidDataException("KIP1 decompression out of bounds!");
|
||||
|
||||
inOffset -= 2;
|
||||
|
||||
ushort segmentValue = BitConverter.ToUInt16(compressed, inOffset);
|
||||
int segmentSize = ((segmentValue >> 12) & 0xF) + 3;
|
||||
int segmentOffset = (segmentValue & 0x0FFF) + 3;
|
||||
|
||||
if (outOffset < segmentSize)
|
||||
{
|
||||
// Kernel restricts segment copy to stay in bounds.
|
||||
segmentSize = outOffset;
|
||||
}
|
||||
outOffset -= segmentSize;
|
||||
|
||||
for (int j = 0; j < segmentSize; j++)
|
||||
{
|
||||
decompressed[outOffset + j] = decompressed[outOffset + j + segmentOffset];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Copy directly.
|
||||
if (inOffset < 1) throw new InvalidDataException("KIP1 decompression out of bounds!");
|
||||
|
||||
decompressed[--outOffset] = compressed[--inOffset];
|
||||
}
|
||||
control <<= 1;
|
||||
if (outOffset == 0) return decompressed;
|
||||
}
|
||||
}
|
||||
|
||||
return decompressed;
|
||||
}
|
||||
}
|
||||
|
||||
public class KipHeader
|
||||
{
|
||||
public string Magic { get; }
|
||||
public string Name { get; }
|
||||
public ulong TitleId { get; }
|
||||
public int ProcessCategory { get; }
|
||||
public byte MainThreadPriority { get; }
|
||||
public byte DefaultCore { get; }
|
||||
public byte Field1E { get; }
|
||||
public byte Flags { get; }
|
||||
public KipSectionHeader[] Sections { get; } = new KipSectionHeader[6];
|
||||
public byte[] Capabilities { get; }
|
||||
|
||||
public KipHeader(IStorage storage)
|
||||
{
|
||||
var reader = new BinaryReader(storage.AsStream());
|
||||
|
||||
Magic = reader.ReadAscii(4);
|
||||
if (Magic != "KIP1")
|
||||
{
|
||||
throw new InvalidDataException("Invalid KIP file!");
|
||||
}
|
||||
|
||||
Name = reader.ReadAsciiZ(0xC);
|
||||
|
||||
reader.BaseStream.Position = 0x10;
|
||||
TitleId = reader.ReadUInt64();
|
||||
ProcessCategory = reader.ReadInt32();
|
||||
MainThreadPriority = reader.ReadByte();
|
||||
DefaultCore = reader.ReadByte();
|
||||
Field1E = reader.ReadByte();
|
||||
Flags = reader.ReadByte();
|
||||
|
||||
for (int i = 0; i < Sections.Length; i++)
|
||||
{
|
||||
Sections[i] = new KipSectionHeader(reader);
|
||||
}
|
||||
|
||||
Capabilities = reader.ReadBytes(0x20);
|
||||
}
|
||||
}
|
||||
|
||||
public class KipSectionHeader
|
||||
{
|
||||
public int OutOffset { get; }
|
||||
public int DecompressedSize { get; }
|
||||
public int CompressedSize { get; }
|
||||
public int Attribute { get; }
|
||||
|
||||
public KipSectionHeader(BinaryReader reader)
|
||||
{
|
||||
OutOffset = reader.ReadInt32();
|
||||
DecompressedSize = reader.ReadInt32();
|
||||
CompressedSize = reader.ReadInt32();
|
||||
Attribute = reader.ReadInt32();
|
||||
}
|
||||
}
|
||||
|
||||
public class Ini1
|
||||
{
|
||||
public KipReader[] Kips { get; }
|
||||
|
||||
public string Magic { get; }
|
||||
public int Size { get; }
|
||||
public int KipCount { get; }
|
||||
|
||||
private IStorage Storage { get; }
|
||||
|
||||
public Ini1(IStorage storage)
|
||||
{
|
||||
Storage = storage;
|
||||
|
||||
var reader = new BinaryReader(Storage.AsStream());
|
||||
|
||||
Magic = reader.ReadAscii(4);
|
||||
if (Magic != "INI1")
|
||||
{
|
||||
throw new InvalidDataException("Invalid INI1 file!");
|
||||
}
|
||||
|
||||
Size = reader.ReadInt32();
|
||||
KipCount = reader.ReadInt32();
|
||||
|
||||
Kips = new KipReader[KipCount];
|
||||
int offset = 0x10;
|
||||
|
||||
for (int i = 0; i < KipCount; i++)
|
||||
{
|
||||
using var sharedStorage = new SharedRef<IStorage>(Storage.Slice(offset));
|
||||
|
||||
Kips[i] = new KipReader();
|
||||
Kips[i].Initialize(in sharedStorage).ThrowIfFailure();
|
||||
|
||||
offset += Kips[i].GetFileSize();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,190 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace LibHac
|
||||
{
|
||||
[Obsolete("This class has been deprecated. LibHac.Ns.ApplicationControlProperty should be used instead.")]
|
||||
public class Nacp
|
||||
{
|
||||
public NacpDescription[] Descriptions { get; } = new NacpDescription[0x10];
|
||||
public string Isbn { get; }
|
||||
public byte StartupUserAccount { get; }
|
||||
public byte UserAccountSwitchLock { get; }
|
||||
public byte AocRegistrationType { get; }
|
||||
public int AttributeFlag { get; }
|
||||
public uint SupportedLanguageFlag { get; }
|
||||
public uint ParentalControlFlag { get; }
|
||||
public byte Screenshot { get; }
|
||||
public byte VideoCapture { get; }
|
||||
public byte DataLossConfirmation { get; }
|
||||
public byte PlayLogPolicy { get; }
|
||||
public ulong PresenceGroupId { get; }
|
||||
public sbyte[] RatingAge { get; } = new sbyte[32];
|
||||
public string DisplayVersion { get; }
|
||||
public ulong AddOnContentBaseId { get; }
|
||||
public ulong SaveDataOwnerId { get; }
|
||||
public long UserAccountSaveDataSize { get; }
|
||||
public long UserAccountSaveDataJournalSize { get; }
|
||||
public long DeviceSaveDataSize { get; }
|
||||
public long DeviceSaveDataJournalSize { get; }
|
||||
public long BcatDeliveryCacheStorageSize { get; }
|
||||
public string ApplicationErrorCodeCategory { get; }
|
||||
public ulong[] LocalCommunicationId { get; } = new ulong[8];
|
||||
public byte LogoType { get; }
|
||||
public byte LogoHandling { get; }
|
||||
public byte RuntimeAddOnContentInstall { get; }
|
||||
public byte[] Reserved00 { get; }
|
||||
public byte CrashReport { get; }
|
||||
public byte Hdcp { get; }
|
||||
public ulong SeedForPseudoDeviceId { get; }
|
||||
public string BcatPassphrase { get; }
|
||||
public byte Reserved01 { get; }
|
||||
public byte[] Reserved02 { get; }
|
||||
public long UserAccountSaveDataSizeMax { get; }
|
||||
public long UserAccountSaveDataJournalSizeMax { get; }
|
||||
public long DeviceSaveDataSizeMax { get; }
|
||||
public long DeviceSaveDataJournalSizeMax { get; }
|
||||
public long TemporaryStorageSize { get; }
|
||||
public long CacheStorageSize { get; }
|
||||
public long CacheStorageJournalSize { get; }
|
||||
public long CacheStorageDataAndJournalSizeMax { get; }
|
||||
public short CacheStorageIndex { get; }
|
||||
public byte[] Reserved03 { get; }
|
||||
public List<ulong> PlayLogQueryableApplicationId { get; } = new List<ulong>();
|
||||
public byte PlayLogQueryCapability { get; }
|
||||
public byte RepairFlag { get; }
|
||||
public byte ProgramIndex { get; }
|
||||
|
||||
public long TotalSaveDataSize { get; }
|
||||
public long UserTotalSaveDataSize { get; }
|
||||
public long DeviceTotalSaveDataSize { get; }
|
||||
|
||||
public Nacp() { }
|
||||
|
||||
public Nacp(Stream file)
|
||||
{
|
||||
long start = file.Position;
|
||||
|
||||
var reader = new BinaryReader(file);
|
||||
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
Descriptions[i] = new NacpDescription(reader, i);
|
||||
}
|
||||
|
||||
Isbn = reader.ReadUtf8Z(37);
|
||||
reader.BaseStream.Position = start + 0x3025;
|
||||
StartupUserAccount = reader.ReadByte();
|
||||
UserAccountSwitchLock = reader.ReadByte();
|
||||
AocRegistrationType = reader.ReadByte();
|
||||
AttributeFlag = reader.ReadInt32();
|
||||
SupportedLanguageFlag = reader.ReadUInt32();
|
||||
ParentalControlFlag = reader.ReadUInt32();
|
||||
Screenshot = reader.ReadByte();
|
||||
VideoCapture = reader.ReadByte();
|
||||
DataLossConfirmation = reader.ReadByte();
|
||||
PlayLogPolicy = reader.ReadByte();
|
||||
PresenceGroupId = reader.ReadUInt64();
|
||||
|
||||
for (int i = 0; i < RatingAge.Length; i++)
|
||||
{
|
||||
RatingAge[i] = reader.ReadSByte();
|
||||
}
|
||||
|
||||
DisplayVersion = reader.ReadUtf8Z(16);
|
||||
reader.BaseStream.Position = start + 0x3070;
|
||||
AddOnContentBaseId = reader.ReadUInt64();
|
||||
SaveDataOwnerId = reader.ReadUInt64();
|
||||
UserAccountSaveDataSize = reader.ReadInt64();
|
||||
UserAccountSaveDataJournalSize = reader.ReadInt64();
|
||||
DeviceSaveDataSize = reader.ReadInt64();
|
||||
DeviceSaveDataJournalSize = reader.ReadInt64();
|
||||
BcatDeliveryCacheStorageSize = reader.ReadInt64();
|
||||
ApplicationErrorCodeCategory = reader.ReadUtf8Z(8);
|
||||
reader.BaseStream.Position = start + 0x30B0;
|
||||
|
||||
for (int i = 0; i < LocalCommunicationId.Length; i++)
|
||||
{
|
||||
LocalCommunicationId[i] = reader.ReadUInt64();
|
||||
}
|
||||
|
||||
LogoType = reader.ReadByte();
|
||||
LogoHandling = reader.ReadByte();
|
||||
RuntimeAddOnContentInstall = reader.ReadByte();
|
||||
Reserved00 = reader.ReadBytes(3);
|
||||
CrashReport = reader.ReadByte();
|
||||
Hdcp = reader.ReadByte();
|
||||
SeedForPseudoDeviceId = reader.ReadUInt64();
|
||||
BcatPassphrase = reader.ReadUtf8Z(65);
|
||||
|
||||
reader.BaseStream.Position = start + 0x3141;
|
||||
Reserved01 = reader.ReadByte();
|
||||
Reserved02 = reader.ReadBytes(6);
|
||||
|
||||
UserAccountSaveDataSizeMax = reader.ReadInt64();
|
||||
UserAccountSaveDataJournalSizeMax = reader.ReadInt64();
|
||||
DeviceSaveDataSizeMax = reader.ReadInt64();
|
||||
DeviceSaveDataJournalSizeMax = reader.ReadInt64();
|
||||
TemporaryStorageSize = reader.ReadInt64();
|
||||
CacheStorageSize = reader.ReadInt64();
|
||||
CacheStorageJournalSize = reader.ReadInt64();
|
||||
CacheStorageDataAndJournalSizeMax = reader.ReadInt64();
|
||||
CacheStorageIndex = reader.ReadInt16();
|
||||
Reserved03 = reader.ReadBytes(6);
|
||||
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
ulong value = reader.ReadUInt64();
|
||||
if (value != 0) PlayLogQueryableApplicationId.Add(value);
|
||||
}
|
||||
|
||||
PlayLogQueryCapability = reader.ReadByte();
|
||||
RepairFlag = reader.ReadByte();
|
||||
ProgramIndex = reader.ReadByte();
|
||||
|
||||
UserTotalSaveDataSize = UserAccountSaveDataSize + UserAccountSaveDataJournalSize;
|
||||
DeviceTotalSaveDataSize = DeviceSaveDataSize + DeviceSaveDataJournalSize;
|
||||
TotalSaveDataSize = UserTotalSaveDataSize + DeviceTotalSaveDataSize;
|
||||
}
|
||||
}
|
||||
|
||||
public class NacpDescription
|
||||
{
|
||||
public string Title { get; }
|
||||
public string Developer { get; }
|
||||
|
||||
public TitleLanguage Language;
|
||||
|
||||
public NacpDescription() { }
|
||||
|
||||
public NacpDescription(BinaryReader reader, int index)
|
||||
{
|
||||
Language = (TitleLanguage)index;
|
||||
long start = reader.BaseStream.Position;
|
||||
Title = reader.ReadUtf8Z();
|
||||
reader.BaseStream.Position = start + 0x200;
|
||||
Developer = reader.ReadUtf8Z();
|
||||
reader.BaseStream.Position = start + 0x300;
|
||||
}
|
||||
}
|
||||
|
||||
public enum TitleLanguage
|
||||
{
|
||||
AmericanEnglish = 0,
|
||||
BritishEnglish,
|
||||
Japanese,
|
||||
French,
|
||||
German,
|
||||
LatinAmericanSpanish,
|
||||
Spanish,
|
||||
Italian,
|
||||
Dutch,
|
||||
CanadianFrench,
|
||||
Portuguese,
|
||||
Russian,
|
||||
Korean,
|
||||
Taiwanese,
|
||||
Chinese
|
||||
}
|
||||
}
|
|
@ -1,116 +0,0 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.IO;
|
||||
using LibHac.Fs;
|
||||
using LibHac.FsSystem;
|
||||
|
||||
namespace LibHac
|
||||
{
|
||||
[Obsolete("This class has been deprecated. LibHac.Loader.NsoReader should be used instead.")]
|
||||
public class Nso
|
||||
{
|
||||
public NsoSection[] Sections { get; }
|
||||
public RodataRelativeExtent[] RodataRelativeExtents { get; }
|
||||
public uint BssSize { get; }
|
||||
public byte[] BuildId { get; } = new byte[0x20];
|
||||
|
||||
private IStorage Storage { get; }
|
||||
|
||||
public Nso(IStorage storage)
|
||||
{
|
||||
Storage = storage;
|
||||
var reader = new BinaryReader(Storage.AsStream());
|
||||
if (reader.ReadAscii(4) != "NSO0")
|
||||
throw new InvalidDataException("NSO magic is incorrect!");
|
||||
reader.ReadUInt32(); // Version
|
||||
reader.ReadUInt32(); // Reserved/Unused
|
||||
var flags = new BitArray(new[] { (int)reader.ReadUInt32() });
|
||||
var textSection = new NsoSection(Storage);
|
||||
var rodataSection = new NsoSection(Storage);
|
||||
var dataSection = new NsoSection(Storage);
|
||||
textSection.IsCompressed = flags[0];
|
||||
rodataSection.IsCompressed = flags[1];
|
||||
dataSection.IsCompressed = flags[2];
|
||||
textSection.CheckHash = flags[3];
|
||||
rodataSection.CheckHash = flags[4];
|
||||
dataSection.CheckHash = flags[5];
|
||||
|
||||
textSection.ReadSegmentHeader(reader);
|
||||
reader.ReadUInt32(); // Module offset (TODO)
|
||||
rodataSection.ReadSegmentHeader(reader);
|
||||
reader.ReadUInt32(); // Module file size
|
||||
dataSection.ReadSegmentHeader(reader);
|
||||
BssSize = reader.ReadUInt32();
|
||||
reader.Read(BuildId, 0, 0x20);
|
||||
textSection.CompressedSize = reader.ReadUInt32();
|
||||
rodataSection.CompressedSize = reader.ReadUInt32();
|
||||
dataSection.CompressedSize = reader.ReadUInt32();
|
||||
reader.ReadBytes(0x1C); // Padding
|
||||
RodataRelativeExtents = new[]
|
||||
{
|
||||
new RodataRelativeExtent(reader), new RodataRelativeExtent(reader), new RodataRelativeExtent(reader)
|
||||
};
|
||||
|
||||
reader.Read(textSection.Hash, 0, 0x20);
|
||||
reader.Read(rodataSection.Hash, 0, 0x20);
|
||||
reader.Read(dataSection.Hash, 0, 0x20);
|
||||
|
||||
Sections = new[] { textSection, rodataSection, dataSection };
|
||||
reader.Close();
|
||||
}
|
||||
|
||||
public class NsoSection
|
||||
{
|
||||
private IStorage Storage { get; }
|
||||
|
||||
public bool IsCompressed { get; set; }
|
||||
public bool CheckHash { get; set; }
|
||||
public uint FileOffset { get; set; }
|
||||
public uint MemoryOffset { get; set; }
|
||||
public uint DecompressedSize { get; set; }
|
||||
public uint CompressedSize { get; set; }
|
||||
|
||||
public byte[] Hash { get; } = new byte[0x20];
|
||||
|
||||
public NsoSection(IStorage storage)
|
||||
{
|
||||
Storage = storage;
|
||||
}
|
||||
|
||||
public IStorage OpenSection()
|
||||
{
|
||||
return Storage.Slice(FileOffset, CompressedSize);
|
||||
}
|
||||
|
||||
public byte[] DecompressSection()
|
||||
{
|
||||
byte[] compressed = new byte[CompressedSize];
|
||||
OpenSection().Read(0, compressed).ThrowIfFailure();
|
||||
|
||||
if (IsCompressed)
|
||||
return Lz4.Decompress(compressed, (int)DecompressedSize);
|
||||
else
|
||||
return compressed;
|
||||
}
|
||||
|
||||
internal void ReadSegmentHeader(BinaryReader reader)
|
||||
{
|
||||
FileOffset = reader.ReadUInt32();
|
||||
MemoryOffset = reader.ReadUInt32();
|
||||
DecompressedSize = reader.ReadUInt32();
|
||||
}
|
||||
}
|
||||
|
||||
public class RodataRelativeExtent
|
||||
{
|
||||
public uint RegionRodataOffset { get; }
|
||||
public uint RegionSize { get; }
|
||||
|
||||
public RodataRelativeExtent(BinaryReader reader)
|
||||
{
|
||||
RegionRodataOffset = reader.ReadUInt32();
|
||||
RegionSize = reader.ReadUInt32();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,122 +0,0 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using LibHac.Common.Keys;
|
||||
using LibHac.Fs;
|
||||
using LibHac.FsSystem;
|
||||
|
||||
namespace LibHac
|
||||
{
|
||||
[Obsolete("This class has been deprecated. LibHac.Boot.Package1 should be used instead.")]
|
||||
public class Package1
|
||||
{
|
||||
private const uint Pk11Magic = 0x31314B50; // PK11
|
||||
|
||||
public byte[] BuildHash { get; }
|
||||
public string BuildDate { get; }
|
||||
public int Field1E { get; }
|
||||
public int Pk11Size { get; }
|
||||
public byte[] Counter { get; }
|
||||
public int KeyRevision { get; }
|
||||
public Pk11 Pk11 { get; }
|
||||
|
||||
private IStorage Storage { get; }
|
||||
|
||||
public Package1(KeySet keySet, IStorage storage)
|
||||
{
|
||||
Storage = storage;
|
||||
var reader = new BinaryReader(storage.AsStream());
|
||||
|
||||
BuildHash = reader.ReadBytes(0x10);
|
||||
BuildDate = reader.ReadAscii(0xE);
|
||||
Field1E = reader.ReadUInt16();
|
||||
|
||||
reader.BaseStream.Position = 0x3FE0;
|
||||
Pk11Size = reader.ReadInt32();
|
||||
|
||||
reader.BaseStream.Position += 0xC;
|
||||
Counter = reader.ReadBytes(0x10);
|
||||
|
||||
// Try decrypting the PK11 blob with all known package1 keys
|
||||
IStorage encStorage = Storage.Slice(0x4000, Pk11Size);
|
||||
byte[] decBuffer = new byte[0x10];
|
||||
|
||||
for (int i = 0; i < 0x20; i++)
|
||||
{
|
||||
var dec = new Aes128CtrStorage(encStorage, keySet.Package1Keys[i].DataRo.ToArray(), Counter, true);
|
||||
dec.Read(0, decBuffer).ThrowIfFailure();
|
||||
|
||||
if (BitConverter.ToUInt32(decBuffer, 0) == Pk11Magic)
|
||||
{
|
||||
KeyRevision = i;
|
||||
|
||||
Pk11 = new Pk11(new CachedStorage(dec, 4, true));
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
throw new InvalidDataException("Failed to decrypt PK11! Is the correct key present?");
|
||||
}
|
||||
|
||||
public IStorage OpenDecryptedPackage()
|
||||
{
|
||||
IStorage[] storages = { OpenPackage1Ldr(), Pk11.OpenDecryptedPk11() };
|
||||
|
||||
return new ConcatenationStorage(storages, true);
|
||||
}
|
||||
|
||||
public IStorage OpenPackage1Ldr() => Storage.Slice(0, 0x4000);
|
||||
}
|
||||
|
||||
public class Pk11
|
||||
{
|
||||
private const int DataOffset = 0x20;
|
||||
|
||||
public string Magic { get; }
|
||||
public int[] SectionSizes { get; } = new int[3];
|
||||
public int[] SectionOffsets { get; } = new int[3];
|
||||
|
||||
private IStorage Storage { get; }
|
||||
|
||||
public Pk11(IStorage storage)
|
||||
{
|
||||
Storage = storage;
|
||||
var reader = new BinaryReader(storage.AsStream());
|
||||
|
||||
Magic = reader.ReadAscii(4);
|
||||
SectionSizes[0] = reader.ReadInt32();
|
||||
SectionOffsets[0] = reader.ReadInt32();
|
||||
|
||||
reader.BaseStream.Position += 4;
|
||||
SectionSizes[1] = reader.ReadInt32();
|
||||
SectionOffsets[1] = reader.ReadInt32();
|
||||
SectionSizes[2] = reader.ReadInt32();
|
||||
SectionOffsets[2] = reader.ReadInt32();
|
||||
|
||||
SectionOffsets[0] = DataOffset;
|
||||
SectionOffsets[1] = SectionOffsets[0] + SectionSizes[0];
|
||||
SectionOffsets[2] = SectionOffsets[1] + SectionSizes[1];
|
||||
}
|
||||
|
||||
public IStorage OpenSection(int index)
|
||||
{
|
||||
if (index < 0 || index > 2)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(index), "Section index must be one of: 0, 1, 2");
|
||||
}
|
||||
|
||||
return Storage.Slice(SectionOffsets[index], SectionSizes[index]);
|
||||
}
|
||||
|
||||
public IStorage OpenDecryptedPk11() => Storage;
|
||||
|
||||
public IStorage OpenWarmboot() => OpenSection(GetWarmbootSection());
|
||||
public IStorage OpenNxBootloader() => OpenSection(GetNxBootloaderSection());
|
||||
public IStorage OpenSecureMonitor() => OpenSection(GetSecureMonitorSection());
|
||||
|
||||
// todo: Handle the old layout from before 2.0.0
|
||||
private int GetWarmbootSection() => 0;
|
||||
private int GetNxBootloaderSection() => 1;
|
||||
private int GetSecureMonitorSection() => 2;
|
||||
}
|
||||
}
|
|
@ -1,197 +0,0 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using LibHac.Common.Keys;
|
||||
using LibHac.Fs;
|
||||
using LibHac.FsSystem;
|
||||
|
||||
namespace LibHac
|
||||
{
|
||||
[Obsolete("This class has been deprecated. LibHac.Boot.Package2StorageReader should be used instead.")]
|
||||
public class Package2
|
||||
{
|
||||
private const uint Pk21Magic = 0x31324B50; // PK21
|
||||
|
||||
public Package2Header Header { get; }
|
||||
public int KeyRevision { get; }
|
||||
public byte[] Key { get; }
|
||||
public int PackageSize { get; }
|
||||
public int HeaderVersion { get; }
|
||||
|
||||
private IStorage Storage { get; }
|
||||
|
||||
public Package2(KeySet keySet, IStorage storage)
|
||||
{
|
||||
Storage = storage;
|
||||
IStorage headerStorage = Storage.Slice(0, 0x200);
|
||||
|
||||
KeyRevision = FindKeyGeneration(keySet, headerStorage);
|
||||
Key = keySet.Package2Keys[KeyRevision].DataRo.ToArray();
|
||||
|
||||
Header = new Package2Header(headerStorage, keySet, KeyRevision);
|
||||
|
||||
PackageSize = BitConverter.ToInt32(Header.Counter, 0) ^ BitConverter.ToInt32(Header.Counter, 8) ^
|
||||
BitConverter.ToInt32(Header.Counter, 12);
|
||||
|
||||
HeaderVersion = Header.Counter[4] ^ Header.Counter[6] ^ Header.Counter[7];
|
||||
|
||||
if (PackageSize != 0x200 + Header.SectionSizes[0] + Header.SectionSizes[1] + Header.SectionSizes[2])
|
||||
{
|
||||
throw new InvalidDataException("Package2 Header is corrupt!");
|
||||
}
|
||||
}
|
||||
|
||||
public IStorage OpenDecryptedPackage()
|
||||
{
|
||||
if (Header.SectionSizes[1] == 0)
|
||||
{
|
||||
IStorage[] storages = { OpenHeaderPart1(), OpenHeaderPart2(), OpenKernel() };
|
||||
|
||||
return new ConcatenationStorage(storages, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
IStorage[] storages = { OpenHeaderPart1(), OpenHeaderPart2(), OpenKernel(), OpenIni1() };
|
||||
|
||||
return new ConcatenationStorage(storages, true);
|
||||
}
|
||||
}
|
||||
|
||||
private IStorage OpenHeaderPart1()
|
||||
{
|
||||
return Storage.Slice(0, 0x110);
|
||||
}
|
||||
|
||||
private IStorage OpenHeaderPart2()
|
||||
{
|
||||
IStorage encStorage = Storage.Slice(0x110, 0xF0);
|
||||
|
||||
// The counter starts counting at 0x100, but the block at 0x100 isn't encrypted.
|
||||
// Increase the counter by one and start decrypting at 0x110.
|
||||
byte[] counter = new byte[0x10];
|
||||
Array.Copy(Header.Counter, counter, 0x10);
|
||||
Utilities.IncrementByteArray(counter);
|
||||
|
||||
return new CachedStorage(new Aes128CtrStorage(encStorage, Key, counter, true), 0x4000, 4, true);
|
||||
}
|
||||
|
||||
public IStorage OpenKernel()
|
||||
{
|
||||
int offset = 0x200;
|
||||
IStorage encStorage = Storage.Slice(offset, Header.SectionSizes[0]);
|
||||
|
||||
return new CachedStorage(new Aes128CtrStorage(encStorage, Key, Header.SectionCounters[0], true), 0x4000, 4, true);
|
||||
}
|
||||
|
||||
public IStorage OpenIni1()
|
||||
{
|
||||
// Handle 8.0.0+ INI1 embedded within Kernel
|
||||
// Todo: Figure out how to better deal with this once newer versions are released
|
||||
if (Header.SectionSizes[1] == 0)
|
||||
{
|
||||
IStorage kernelStorage = OpenKernel();
|
||||
|
||||
var reader = new BinaryReader(kernelStorage.AsStream());
|
||||
reader.BaseStream.Position = 0x168;
|
||||
|
||||
int embeddedIniOffset = (int)reader.ReadInt64();
|
||||
|
||||
reader.BaseStream.Position = embeddedIniOffset + 4;
|
||||
int size = reader.ReadInt32();
|
||||
|
||||
return kernelStorage.Slice(embeddedIniOffset, size);
|
||||
}
|
||||
|
||||
int offset = 0x200 + Header.SectionSizes[0];
|
||||
IStorage encStorage = Storage.Slice(offset, Header.SectionSizes[1]);
|
||||
|
||||
return new CachedStorage(new Aes128CtrStorage(encStorage, Key, Header.SectionCounters[1], true), 0x4000, 4, true);
|
||||
}
|
||||
|
||||
private int FindKeyGeneration(KeySet keySet, IStorage storage)
|
||||
{
|
||||
byte[] counter = new byte[0x10];
|
||||
byte[] decBuffer = new byte[0x10];
|
||||
|
||||
storage.Read(0x100, counter).ThrowIfFailure();
|
||||
|
||||
for (int i = 0; i < 0x20; i++)
|
||||
{
|
||||
var dec = new Aes128CtrStorage(storage.Slice(0x100), keySet.Package2Keys[i].DataRo.ToArray(), counter,
|
||||
false);
|
||||
dec.Read(0x50, decBuffer).ThrowIfFailure();
|
||||
|
||||
if (BitConverter.ToUInt32(decBuffer, 0) == Pk21Magic)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
throw new InvalidDataException("Failed to decrypt package2! Is the correct key present?");
|
||||
}
|
||||
}
|
||||
|
||||
public class Package2Header
|
||||
{
|
||||
public byte[] Signature { get; }
|
||||
public byte[] Counter { get; }
|
||||
|
||||
public byte[][] SectionCounters { get; } = new byte[4][];
|
||||
public int[] SectionSizes { get; } = new int[4];
|
||||
public int[] SectionOffsets { get; } = new int[4];
|
||||
public byte[][] SectionHashes { get; } = new byte[4][];
|
||||
|
||||
public string Magic { get; }
|
||||
public int BaseOffset { get; }
|
||||
public int VersionMax { get; }
|
||||
public int VersionMin { get; }
|
||||
|
||||
public Validity SignatureValidity { get; }
|
||||
|
||||
public Package2Header(IStorage storage, KeySet keySet, int keyGeneration)
|
||||
{
|
||||
var reader = new BinaryReader(storage.AsStream());
|
||||
byte[] key = keySet.Package2Keys[keyGeneration].DataRo.ToArray();
|
||||
|
||||
Signature = reader.ReadBytes(0x100);
|
||||
byte[] sigData = reader.ReadBytes(0x100);
|
||||
SignatureValidity = CryptoOld.Rsa2048PssVerify(sigData, Signature, keySet.Package2SigningKeyParams.Modulus);
|
||||
|
||||
reader.BaseStream.Position -= 0x100;
|
||||
Counter = reader.ReadBytes(0x10);
|
||||
|
||||
Stream headerStream = new CachedStorage(new Aes128CtrStorage(storage.Slice(0x100), key, Counter, true), 0x4000, 4, true).AsStream();
|
||||
|
||||
headerStream.Position = 0x10;
|
||||
reader = new BinaryReader(headerStream);
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
SectionCounters[i] = reader.ReadBytes(0x10);
|
||||
}
|
||||
|
||||
Magic = reader.ReadAscii(4);
|
||||
BaseOffset = reader.ReadInt32();
|
||||
|
||||
reader.BaseStream.Position += 4;
|
||||
VersionMax = reader.ReadByte();
|
||||
VersionMin = reader.ReadByte();
|
||||
|
||||
reader.BaseStream.Position += 2;
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
SectionSizes[i] = reader.ReadInt32();
|
||||
}
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
SectionOffsets[i] = reader.ReadInt32();
|
||||
}
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
SectionHashes[i] = reader.ReadBytes(0x20);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue