Update package1 and package2 readers to use SharedRef<T>

This commit is contained in:
Alex Barney 2021-11-03 15:40:43 -07:00
parent 2370f76c62
commit 0a8fb8a5c0
6 changed files with 163 additions and 144 deletions

View file

@ -13,33 +13,38 @@ namespace LibHac.Boot
/// <summary> /// <summary>
/// Parses a package2 file and opens the payloads within. /// Parses a package2 file and opens the payloads within.
/// </summary> /// </summary>
public class Package2StorageReader public class Package2StorageReader : IDisposable
{ {
private const int KernelPayloadIndex = 0; private const int KernelPayloadIndex = 0;
private const int IniPayloadIndex = 1; private const int IniPayloadIndex = 1;
private IStorage _storage; private SharedRef<IStorage> _storage;
private Package2Header _header; private Package2Header _header;
private KeySet _keySet; private KeySet _keySet;
private Crypto.AesKey _key; private Crypto.AesKey _key;
public ref readonly Package2Header Header => ref _header; public ref readonly Package2Header Header => ref _header;
public void Dispose()
{
_storage.Destroy();
}
/// <summary> /// <summary>
/// Initializes the <see cref="Package2StorageReader"/>. /// Initializes the <see cref="Package2StorageReader"/>.
/// </summary> /// </summary>
/// <param name="keySet">The keyset to use for decrypting the package.</param> /// <param name="keySet">The keyset to use for decrypting the package.</param>
/// <param name="storage">An <see cref="IStorage"/> of the encrypted package2.</param> /// <param name="storage">An <see cref="IStorage"/> of the encrypted package2.</param>
/// <returns>The <see cref="Result"/> of the operation.</returns> /// <returns>The <see cref="Result"/> of the operation.</returns>
public Result Initialize(KeySet keySet, IStorage storage) public Result Initialize(KeySet keySet, in SharedRef<IStorage> storage)
{ {
Result rc = storage.Read(0, SpanHelpers.AsByteSpan(ref _header)); Result rc = storage.Get.Read(0, SpanHelpers.AsByteSpan(ref _header));
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
_key = keySet.Package2Keys[_header.Meta.KeyGeneration]; _key = keySet.Package2Keys[_header.Meta.KeyGeneration];
DecryptHeader(_key, ref _header.Meta, ref _header.Meta); DecryptHeader(_key, ref _header.Meta, ref _header.Meta);
_storage = storage; _storage.SetByCopy(in storage);
_keySet = keySet; _keySet = keySet;
return Result.Success; return Result.Success;
} }
@ -47,14 +52,12 @@ namespace LibHac.Boot
/// <summary> /// <summary>
/// Opens a decrypted <see cref="IStorage"/> of one of the payloads in the package. /// Opens a decrypted <see cref="IStorage"/> of one of the payloads in the package.
/// </summary> /// </summary>
/// <param name="payloadStorage">If the method returns successfully, contains an <see cref="IStorage"/> /// <param name="outPayloadStorage">If the method returns successfully, contains an <see cref="IStorage"/>
/// of the specified payload.</param> /// of the specified payload.</param>
/// <param name="index">The index of the payload to get. Must me less than <see cref="Package2Header.PayloadCount"/></param> /// <param name="index">The index of the payload to get. Must me less than <see cref="Package2Header.PayloadCount"/></param>
/// <returns>The <see cref="Result"/> of the operation.</returns> /// <returns>The <see cref="Result"/> of the operation.</returns>
public Result OpenPayload(out IStorage payloadStorage, int index) public Result OpenPayload(ref UniqueRef<IStorage> outPayloadStorage, int index)
{ {
UnsafeHelpers.SkipParamInit(out payloadStorage);
if ((uint)index >= Package2Header.PayloadCount) if ((uint)index >= Package2Header.PayloadCount)
return ResultLibHac.ArgumentOutOfRange.Log(); return ResultLibHac.ArgumentOutOfRange.Log();
@ -65,53 +68,52 @@ namespace LibHac.Boot
if (size == 0) if (size == 0)
{ {
payloadStorage = payloadSubStorage; outPayloadStorage.Reset(payloadSubStorage);
return Result.Success; return Result.Success;
} }
byte[] iv = _header.Meta.PayloadIvs[index].Bytes.ToArray(); byte[] iv = _header.Meta.PayloadIvs[index].Bytes.ToArray();
payloadStorage = new CachedStorage(new Aes128CtrStorage(payloadSubStorage, _key.DataRo.ToArray(), iv, true), 0x4000, 1, true); outPayloadStorage.Reset(new CachedStorage(new Aes128CtrStorage(payloadSubStorage, _key.DataRo.ToArray(), iv, true), 0x4000, 1, true));
return Result.Success; return Result.Success;
} }
/// <summary> /// <summary>
/// Opens an <see cref="IStorage"/> of the kernel payload. /// Opens an <see cref="IStorage"/> of the kernel payload.
/// </summary> /// </summary>
/// <param name="kernelStorage">If the method returns successfully, contains an <see cref="IStorage"/> /// <param name="outKernelStorage">If the method returns successfully, contains an <see cref="IStorage"/>
/// of the kernel payload.</param> /// of the kernel payload.</param>
/// <returns>The <see cref="Result"/> of the operation.</returns> /// <returns>The <see cref="Result"/> of the operation.</returns>
public Result OpenKernel(out IStorage kernelStorage) public Result OpenKernel(ref UniqueRef<IStorage> outKernelStorage)
{ {
return OpenPayload(out kernelStorage, KernelPayloadIndex); return OpenPayload(ref outKernelStorage, KernelPayloadIndex);
} }
/// <summary> /// <summary>
/// Opens an <see cref="IStorage"/> of the initial process binary. If the binary is embedded in /// Opens an <see cref="IStorage"/> of the initial process binary. If the binary is embedded in
/// the kernel, this method will attempt to locate and return the embedded binary. /// the kernel, this method will attempt to locate and return the embedded binary.
/// </summary> /// </summary>
/// <param name="iniStorage">If the method returns successfully, contains an <see cref="IStorage"/> /// <param name="outIniStorage">If the method returns successfully, contains an <see cref="IStorage"/>
/// of the initial process binary.</param> /// of the initial process binary.</param>
/// <returns>The <see cref="Result"/> of the operation.</returns> /// <returns>The <see cref="Result"/> of the operation.</returns>
public Result OpenIni(out IStorage iniStorage) public Result OpenIni(ref UniqueRef<IStorage> outIniStorage)
{ {
if (HasIniPayload()) if (HasIniPayload())
{ {
return OpenPayload(out iniStorage, IniPayloadIndex); return OpenPayload(ref outIniStorage, IniPayloadIndex);
} }
// Ini is embedded in the kernel // Ini is embedded in the kernel
UnsafeHelpers.SkipParamInit(out iniStorage); using var kernelStorage = new UniqueRef<IStorage>();
Result rc = OpenKernel(ref kernelStorage.Ref());
Result rc = OpenKernel(out IStorage kernelStorage);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
if (!IniExtract.TryGetIni1Offset(out int offset, out int size, kernelStorage)) if (!IniExtract.TryGetIni1Offset(out int offset, out int size, kernelStorage.Get))
{ {
// Unable to find the ini. Could be a new, unsupported layout. // Unable to find the ini. Could be a new, unsupported layout.
return ResultLibHac.NotImplemented.Log(); return ResultLibHac.NotImplemented.Log();
} }
iniStorage = new SubStorage(kernelStorage, offset, size); outIniStorage.Reset(new SubStorage(kernelStorage.Release(), offset, size));
return Result.Success; return Result.Success;
} }
@ -140,7 +142,7 @@ namespace LibHac.Boot
Unsafe.SkipInit(out Package2Meta meta); Unsafe.SkipInit(out Package2Meta meta);
Span<byte> metaBytes = SpanHelpers.AsByteSpan(ref meta); Span<byte> metaBytes = SpanHelpers.AsByteSpan(ref meta);
Result rc = _storage.Read(Package2Header.SignatureSize, metaBytes); Result rc = _storage.Get.Read(Package2Header.SignatureSize, metaBytes);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
return _header.VerifySignature(_keySet.Package2SigningKeyParams.Modulus, metaBytes); return _header.VerifySignature(_keySet.Package2SigningKeyParams.Modulus, metaBytes);
@ -209,10 +211,10 @@ namespace LibHac.Boot
/// <summary> /// <summary>
/// Opens a decrypted <see cref="IStorage"/> of the entire package. /// Opens a decrypted <see cref="IStorage"/> of the entire package.
/// </summary> /// </summary>
/// <param name="packageStorage">If the method returns successfully, contains a decrypted /// <param name="outPackageStorage">If the method returns successfully, contains a decrypted
/// <see cref="IStorage"/> of the package.</param> /// <see cref="IStorage"/> of the package.</param>
/// <returns>The <see cref="Result"/> of the operation.</returns> /// <returns>The <see cref="Result"/> of the operation.</returns>
public Result OpenDecryptedPackage(out IStorage packageStorage) public Result OpenDecryptedPackage(ref UniqueRef<IStorage> outPackageStorage)
{ {
var storages = new List<IStorage>(4); var storages = new List<IStorage>(4);
@ -239,17 +241,14 @@ namespace LibHac.Boot
if (_header.Meta.PayloadSizes[i] == 0) if (_header.Meta.PayloadSizes[i] == 0)
continue; continue;
Result rc = OpenPayload(out IStorage payloadStorage, i); using var payloadStorage = new UniqueRef<IStorage>();
if (rc.IsFailure()) Result rc = OpenPayload(ref payloadStorage.Ref(), i);
{ if (rc.IsFailure()) return rc.Miss();
UnsafeHelpers.SkipParamInit(out packageStorage);
return rc;
}
storages.Add(payloadStorage); storages.Add(payloadStorage.Release());
} }
packageStorage = new ConcatenationStorage(storages, true); outPackageStorage.Reset(new ConcatenationStorage(storages, true));
return Result.Success; return Result.Success;
} }

View file

@ -1,4 +1,5 @@
using System.Runtime.CompilerServices; using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using LibHac.Common; using LibHac.Common;
using LibHac.Diag; using LibHac.Diag;
@ -6,32 +7,37 @@ using LibHac.Fs;
namespace LibHac.Kernel namespace LibHac.Kernel
{ {
public class InitialProcessBinaryReader public class InitialProcessBinaryReader : IDisposable
{ {
internal const uint ExpectedMagic = 0x31494E49; // INI1 internal const uint ExpectedMagic = 0x31494E49; // INI1
private const int MaxProcessCount = 80; private const int MaxProcessCount = 80;
private IStorage _storage; private SharedRef<IStorage> _storage;
private IniHeader _header; private IniHeader _header;
private (int offset, int size)[] _offsets; private (int offset, int size)[] _offsets;
public ref readonly IniHeader Header => ref _header; public ref readonly IniHeader Header => ref _header;
public int ProcessCount => _header.ProcessCount; public int ProcessCount => _header.ProcessCount;
public Result Initialize(IStorage binaryStorage) public void Dispose()
{ {
if (binaryStorage is null) _storage.Destroy();
}
public Result Initialize(in SharedRef<IStorage> binaryStorage)
{
if (!binaryStorage.HasValue)
return ResultLibHac.NullArgument.Log(); return ResultLibHac.NullArgument.Log();
// Verify there's enough data to read the header // Verify there's enough data to read the header
Result rc = binaryStorage.GetSize(out long iniSize); Result rc = binaryStorage.Get.GetSize(out long iniSize);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
if (iniSize < Unsafe.SizeOf<IniHeader>()) if (iniSize < Unsafe.SizeOf<IniHeader>())
return ResultLibHac.InvalidIniFileSize.Log(); return ResultLibHac.InvalidIniFileSize.Log();
// Read the INI file header and validate some of its values. // Read the INI file header and validate some of its values.
rc = binaryStorage.Read(0, SpanHelpers.AsByteSpan(ref _header)); rc = binaryStorage.Get.Read(0, SpanHelpers.AsByteSpan(ref _header));
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
if (_header.Magic != ExpectedMagic) if (_header.Magic != ExpectedMagic)
@ -45,36 +51,40 @@ namespace LibHac.Kernel
rc = GetKipOffsets(out _offsets, binaryStorage, _header.ProcessCount); rc = GetKipOffsets(out _offsets, binaryStorage, _header.ProcessCount);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
_storage = binaryStorage; _storage.SetByCopy(in binaryStorage);
return Result.Success; return Result.Success;
} }
public Result OpenKipStorage(out IStorage storage, int index) public Result OpenKipStorage(ref UniqueRef<IStorage> outStorage, int index)
{ {
UnsafeHelpers.SkipParamInit(out storage);
if ((uint)index >= _header.ProcessCount) if ((uint)index >= _header.ProcessCount)
return ResultLibHac.ArgumentOutOfRange.Log(); return ResultLibHac.ArgumentOutOfRange.Log();
(int offset, int size) range = _offsets[index]; (int offset, int size) range = _offsets[index];
storage = new SubStorage(_storage, range.offset, range.size); outStorage.Reset(new SubStorage(in _storage, range.offset, range.size));
return Result.Success; return Result.Success;
} }
private static Result GetKipOffsets(out (int offset, int size)[] kipOffsets, IStorage iniStorage, private static Result GetKipOffsets(out (int offset, int size)[] kipOffsets, in SharedRef<IStorage> iniStorage,
int processCount) int processCount)
{ {
Assert.SdkRequiresLessEqual(processCount, MaxProcessCount); Assert.SdkRequiresLessEqual(processCount, MaxProcessCount);
UnsafeHelpers.SkipParamInit(out kipOffsets); UnsafeHelpers.SkipParamInit(out kipOffsets);
Result rc = iniStorage.Get.GetSize(out long iniStorageSize);
if (rc.IsFailure()) return rc.Miss();
var offsets = new (int offset, int size)[processCount]; var offsets = new (int offset, int size)[processCount];
int offset = Unsafe.SizeOf<IniHeader>(); int offset = Unsafe.SizeOf<IniHeader>();
var kipReader = new KipReader(); using var kipReader = new KipReader();
for (int i = 0; i < processCount; i++) for (int i = 0; i < processCount; i++)
{ {
Result rc = kipReader.Initialize(new SubStorage(iniStorage, offset, int.MaxValue)); using var kipStorage =
new SharedRef<IStorage>(new SubStorage(in iniStorage, offset, iniStorageSize - offset));
rc = kipReader.Initialize(in kipStorage);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
int kipSize = kipReader.GetFileSize(); int kipSize = kipReader.GetFileSize();

View file

@ -8,9 +8,9 @@ using LibHac.Fs;
namespace LibHac.Kernel namespace LibHac.Kernel
{ {
public class KipReader public class KipReader : IDisposable
{ {
private IStorage KipStorage { get; set; } private SharedRef<IStorage> _kipStorage;
private KipHeader _header; private KipHeader _header;
@ -34,48 +34,51 @@ namespace LibHac.Kernel
public int AffinityMask => _header.AffinityMask; public int AffinityMask => _header.AffinityMask;
public int StackSize => _header.StackSize; public int StackSize => _header.StackSize;
public Result Initialize(IStorage kipData) public void Dispose()
{ {
if (kipData is null) _kipStorage.Destroy();
}
public Result Initialize(in SharedRef<IStorage> kipData)
{
if (!kipData.HasValue)
return ResultLibHac.NullArgument.Log(); return ResultLibHac.NullArgument.Log();
// Verify there's enough data to read the header // Verify there's enough data to read the header
Result rc = kipData.GetSize(out long kipSize); Result rc = kipData.Get.GetSize(out long kipSize);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
if (kipSize < Unsafe.SizeOf<KipHeader>()) if (kipSize < Unsafe.SizeOf<KipHeader>())
return ResultLibHac.InvalidKipFileSize.Log(); return ResultLibHac.InvalidKipFileSize.Log();
rc = kipData.Read(0, SpanHelpers.AsByteSpan(ref _header)); rc = kipData.Get.Read(0, SpanHelpers.AsByteSpan(ref _header));
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
if (!_header.IsValid) if (!_header.IsValid)
return ResultLibHac.InvalidKipMagic.Log(); return ResultLibHac.InvalidKipMagic.Log();
KipStorage = kipData; _kipStorage.SetByCopy(in kipData);
return Result.Success; return Result.Success;
} }
/// <summary> /// <summary>
/// Gets the raw input KIP file. /// Gets the raw input KIP file.
/// </summary> /// </summary>
/// <param name="kipData">If the operation returns successfully, an <see cref="IStorage"/> /// <param name="outKipData">If the operation returns successfully, an <see cref="IStorage"/>
/// containing the KIP data.</param> /// containing the KIP data.</param>
/// <returns>The <see cref="Result"/> of the operation.</returns> /// <returns>The <see cref="Result"/> of the operation.</returns>
public Result GetRawData(out IStorage kipData) public Result GetRawData(ref UniqueRef<IStorage> outKipData)
{ {
UnsafeHelpers.SkipParamInit(out kipData);
int kipFileSize = GetFileSize(); int kipFileSize = GetFileSize();
Result rc = KipStorage.GetSize(out long inputFileSize); Result rc = _kipStorage.Get.GetSize(out long inputFileSize);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
// Verify the input KIP file isn't truncated // Verify the input KIP file isn't truncated
if (inputFileSize < kipFileSize) if (inputFileSize < kipFileSize)
return ResultLibHac.InvalidKipFileSize.Log(); return ResultLibHac.InvalidKipFileSize.Log();
kipData = new SubStorage(KipStorage, 0, kipFileSize); outKipData.Reset(new SubStorage(in _kipStorage, 0, kipFileSize));
return Result.Success; return Result.Success;
} }
@ -149,14 +152,14 @@ namespace LibHac.Kernel
int offset = CalculateSegmentOffset((int)segment); int offset = CalculateSegmentOffset((int)segment);
// Verify the segment offset is in-range // Verify the segment offset is in-range
rc = KipStorage.GetSize(out long kipSize); rc = _kipStorage.Get.GetSize(out long kipSize);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
if (kipSize < offset + segmentHeader.FileSize) if (kipSize < offset + segmentHeader.FileSize)
return ResultLibHac.InvalidKipFileSize.Log(); return ResultLibHac.InvalidKipFileSize.Log();
// Read the segment data. // Read the segment data.
rc = KipStorage.Read(offset, buffer.Slice(0, segmentHeader.FileSize)); rc = _kipStorage.Get.Read(offset, buffer.Slice(0, segmentHeader.FileSize));
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
// Decompress if necessary. // Decompress if necessary.

View file

@ -1,5 +1,6 @@
using System; using System;
using System.IO; using System.IO;
using LibHac.Common;
using LibHac.Fs; using LibHac.Fs;
using LibHac.FsSystem; using LibHac.FsSystem;
using LibHac.Kernel; using LibHac.Kernel;
@ -200,8 +201,10 @@ namespace LibHac
for (int i = 0; i < KipCount; i++) for (int i = 0; i < KipCount; i++)
{ {
using var sharedStorage = new SharedRef<IStorage>(Storage.Slice(offset));
Kips[i] = new KipReader(); Kips[i] = new KipReader();
Kips[i].Initialize(Storage.Slice(offset)).ThrowIfFailure(); Kips[i].Initialize(in sharedStorage).ThrowIfFailure();
offset += Kips[i].GetFileSize(); offset += Kips[i].GetFileSize();
} }

View file

@ -1,4 +1,5 @@
using System.IO; using System.IO;
using LibHac.Common;
using LibHac.Fs; using LibHac.Fs;
using LibHac.FsSystem; using LibHac.FsSystem;
using LibHac.Kernel; using LibHac.Kernel;
@ -9,50 +10,50 @@ namespace hactoolnet
{ {
public static void ProcessKip1(Context ctx) public static void ProcessKip1(Context ctx)
{ {
using (var file = new LocalStorage(ctx.Options.InFile, FileAccess.Read)) using var file = new SharedRef<IStorage>(new LocalStorage(ctx.Options.InFile, FileAccess.Read));
using var kip = new KipReader();
kip.Initialize(in file).ThrowIfFailure();
if (!string.IsNullOrWhiteSpace(ctx.Options.UncompressedOut))
{ {
var kip = new KipReader(); byte[] uncompressed = new byte[kip.GetUncompressedSize()];
kip.Initialize(file).ThrowIfFailure();
if (!string.IsNullOrWhiteSpace(ctx.Options.UncompressedOut)) kip.ReadUncompressedKip(uncompressed).ThrowIfFailure();
{
byte[] uncompressed = new byte[kip.GetUncompressedSize()];
kip.ReadUncompressedKip(uncompressed).ThrowIfFailure(); File.WriteAllBytes(ctx.Options.UncompressedOut, uncompressed);
File.WriteAllBytes(ctx.Options.UncompressedOut, uncompressed);
}
} }
} }
public static void ProcessIni1(Context ctx) public static void ProcessIni1(Context ctx)
{ {
using (var file = new LocalStorage(ctx.Options.InFile, FileAccess.Read)) using var file = new SharedRef<IStorage>(new LocalStorage(ctx.Options.InFile, FileAccess.Read));
{
string outDir = ctx.Options.OutDir;
if (outDir != null) string outDir = ctx.Options.OutDir;
{
ExtractIni1(file, outDir); if (outDir != null)
} {
ExtractIni1(in file, outDir);
} }
} }
public static void ExtractIni1(IStorage iniStorage, string outDir) public static void ExtractIni1(in SharedRef<IStorage> iniStorage, string outDir)
{ {
var ini1 = new InitialProcessBinaryReader(); using var ini1 = new InitialProcessBinaryReader();
ini1.Initialize(iniStorage).ThrowIfFailure(); ini1.Initialize(iniStorage).ThrowIfFailure();
Directory.CreateDirectory(outDir); Directory.CreateDirectory(outDir);
var kipReader = new KipReader(); using var kipReader = new KipReader();
for (int i = 0; i < ini1.ProcessCount; i++) for (int i = 0; i < ini1.ProcessCount; i++)
{ {
ini1.OpenKipStorage(out IStorage kipStorage, i).ThrowIfFailure(); using var kipStorage = new UniqueRef<IStorage>();
ini1.OpenKipStorage(ref kipStorage.Ref(), i).ThrowIfFailure();
kipReader.Initialize(kipStorage).ThrowIfFailure(); using SharedRef<IStorage> sharedKipStorage = SharedRef<IStorage>.Create(ref kipStorage.Ref());
kipReader.Initialize(in sharedKipStorage).ThrowIfFailure();
kipStorage.WriteAllBytes(System.IO.Path.Combine(outDir, $"{kipReader.Name.ToString()}.kip1")); sharedKipStorage.Get.WriteAllBytes(System.IO.Path.Combine(outDir, $"{kipReader.Name.ToString()}.kip1"));
} }
} }
} }

View file

@ -15,43 +15,42 @@ namespace hactoolnet
{ {
public static void ProcessPk11(Context ctx) public static void ProcessPk11(Context ctx)
{ {
using (var file = new SharedRef<IStorage>(new LocalStorage(ctx.Options.InFile, FileAccess.Read))) using var file = new SharedRef<IStorage>(new LocalStorage(ctx.Options.InFile, FileAccess.Read));
var package1 = new LibHac.Boot.Package1();
package1.Initialize(ctx.KeySet, in file).ThrowIfFailure();
ctx.Logger.LogMessage(package1.Print());
string outDir = ctx.Options.OutDir;
if (package1.IsDecrypted && outDir != null)
{ {
var package1 = new LibHac.Boot.Package1(); Directory.CreateDirectory(outDir);
package1.Initialize(ctx.KeySet, in file).ThrowIfFailure();
ctx.Logger.LogMessage(package1.Print()); IStorage decryptedStorage = package1.OpenDecryptedPackage1Storage();
string outDir = ctx.Options.OutDir; WriteFile(decryptedStorage, "Decrypted.bin");
WriteFile(package1.OpenWarmBootStorage(), "Warmboot.bin");
WriteFile(package1.OpenNxBootloaderStorage(), "NX_Bootloader.bin");
WriteFile(package1.OpenSecureMonitorStorage(), "Secure_Monitor.bin");
if (package1.IsDecrypted && outDir != null) if (package1.IsMariko)
{ {
Directory.CreateDirectory(outDir); WriteFile(package1.OpenDecryptedWarmBootStorage(), "Warmboot_Decrypted.bin");
IStorage decryptedStorage = package1.OpenDecryptedPackage1Storage(); var marikoOemLoader = new SubStorage(decryptedStorage, Unsafe.SizeOf<Package1MarikoOemHeader>(),
package1.MarikoOemHeader.Size);
WriteFile(decryptedStorage, "Decrypted.bin"); WriteFile(marikoOemLoader, "Mariko_OEM_Bootloader.bin");
WriteFile(package1.OpenWarmBootStorage(), "Warmboot.bin");
WriteFile(package1.OpenNxBootloaderStorage(), "NX_Bootloader.bin");
WriteFile(package1.OpenSecureMonitorStorage(), "Secure_Monitor.bin");
if (package1.IsMariko)
{
WriteFile(package1.OpenDecryptedWarmBootStorage(), "Warmboot_Decrypted.bin");
var marikoOemLoader = new SubStorage(decryptedStorage, Unsafe.SizeOf<Package1MarikoOemHeader>(),
package1.MarikoOemHeader.Size);
WriteFile(marikoOemLoader, "Mariko_OEM_Bootloader.bin");
}
} }
}
void WriteFile(IStorage storage, string filename) void WriteFile(IStorage storage, string filename)
{ {
string path = Path.Combine(outDir, filename); string path = Path.Combine(outDir, filename);
ctx.Logger.LogMessage($"Writing {path}..."); ctx.Logger.LogMessage($"Writing {path}...");
storage.WriteAllBytes(path, ctx.Logger); storage.WriteAllBytes(path, ctx.Logger);
}
} }
} }
@ -105,43 +104,47 @@ namespace hactoolnet
public static void ProcessPk21(Context ctx) public static void ProcessPk21(Context ctx)
{ {
using (var file = new CachedStorage(new LocalStorage(ctx.Options.InFile, FileAccess.Read), 0x4000, 4, false)) using var file = new SharedRef<IStorage>(new CachedStorage(new LocalStorage(ctx.Options.InFile, FileAccess.Read), 0x4000, 4, false));
using var package2 = new Package2StorageReader();
package2.Initialize(ctx.KeySet, in file).ThrowIfFailure();
ctx.Logger.LogMessage(package2.Print());
string outDir = ctx.Options.OutDir;
string iniDir = ctx.Options.Ini1OutDir;
if (iniDir == null && ctx.Options.ExtractIni1)
{ {
var package2 = new Package2StorageReader(); iniDir = Path.Combine(outDir, "INI1");
package2.Initialize(ctx.KeySet, file).ThrowIfFailure(); }
ctx.Logger.LogMessage(package2.Print()); if (outDir != null)
{
Directory.CreateDirectory(outDir);
string outDir = ctx.Options.OutDir; using var kernelStorage = new UniqueRef<IStorage>();
string iniDir = ctx.Options.Ini1OutDir; package2.OpenPayload(ref kernelStorage.Ref(), 0).ThrowIfFailure();
kernelStorage.Get.WriteAllBytes(Path.Combine(outDir, "Kernel.bin"), ctx.Logger);
if (iniDir == null && ctx.Options.ExtractIni1) using var ini1Storage = new UniqueRef<IStorage>();
{ package2.OpenIni(ref ini1Storage.Ref()).ThrowIfFailure();
iniDir = Path.Combine(outDir, "INI1"); ini1Storage.Get.WriteAllBytes(Path.Combine(outDir, "INI1.bin"), ctx.Logger);
}
if (outDir != null) using var decPackageStorage = new UniqueRef<IStorage>();
{ package2.OpenDecryptedPackage(ref decPackageStorage.Ref()).ThrowIfFailure();
Directory.CreateDirectory(outDir); decPackageStorage.Get.WriteAllBytes(Path.Combine(outDir, "Decrypted.bin"), ctx.Logger);
}
package2.OpenPayload(out IStorage kernelStorage, 0).ThrowIfFailure(); if (iniDir != null)
kernelStorage.WriteAllBytes(Path.Combine(outDir, "Kernel.bin"), ctx.Logger); {
Directory.CreateDirectory(iniDir);
package2.OpenIni(out IStorage ini1Storage).ThrowIfFailure(); using var ini1Storage = new UniqueRef<IStorage>();
ini1Storage.WriteAllBytes(Path.Combine(outDir, "INI1.bin"), ctx.Logger); package2.OpenIni(ref ini1Storage.Ref()).ThrowIfFailure();
package2.OpenDecryptedPackage(out IStorage decPackageStorage).ThrowIfFailure(); using SharedRef<IStorage> sharedIni1Storage = SharedRef<IStorage>.Create(ref ini1Storage.Ref());
decPackageStorage.WriteAllBytes(Path.Combine(outDir, "Decrypted.bin"), ctx.Logger); ProcessKip.ExtractIni1(in sharedIni1Storage, iniDir);
}
if (iniDir != null)
{
Directory.CreateDirectory(iniDir);
package2.OpenIni(out IStorage ini1Storage).ThrowIfFailure();
ProcessKip.ExtractIni1(ini1Storage, iniDir);
}
} }
} }