Merge pull request #126 from Thealexbarney/nso

Rewrite Nso and Kip classes
This commit is contained in:
Alex Barney 2020-04-01 23:20:53 -07:00 committed by GitHub
commit a5699182b0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 803 additions and 6 deletions

View file

@ -1,4 +1,6 @@
Name,Index
Fs,2
Loader,9
Kvdb,20
Sdmmc,24
Sdmmc,24
LibHac,428
1 Name Index
2 Fs 2
3 Loader 9
4 Kvdb 20
5 Sdmmc 24
6 LibHac 428

View file

@ -1,4 +1,6 @@
Name,Namespace,Path
Fs,LibHac.Fs,LibHac/Fs/ResultFs.cs
Loader,LibHac.Loader,LibHac/Loader/ResultLoader.cs
Kvdb,LibHac.Kvdb,LibHac/Kvdb/ResultKvdb.cs
Sdmmc,LibHac.FsService,LibHac/FsService/ResultSdmmc.cs
Sdmmc,LibHac.FsService,LibHac/FsService/ResultSdmmc.cs
LibHac,LibHac.Common,LibHac/Common/ResultLibHac.cs
1 Name Namespace Path
2 Fs LibHac.Fs LibHac/Fs/ResultFs.cs
3 Loader LibHac.Loader LibHac/Loader/ResultLoader.cs
4 Kvdb LibHac.Kvdb LibHac/Kvdb/ResultKvdb.cs
5 Sdmmc LibHac.FsService LibHac/FsService/ResultSdmmc.cs
6 LibHac LibHac.Common LibHac/Common/ResultLibHac.cs

View file

@ -229,6 +229,45 @@ Module,DescriptionStart,DescriptionEnd,Name,Summary
2,6905,,NotMounted,
2,6906,,SaveDataIsExtending,
9,1,,TooLongArgument,
9,2,,TooManyArguments,
9,3,,TooLargeMeta,
9,4,,InvalidMeta,
9,5,,InvalidNso,
9,6,,InvalidPath,
9,7,,TooManyProcesses,
9,8,,NotPinned,
9,9,,InvalidProgramId,
9,10,,InvalidVersion,
9,51,,InsufficientAddressSpace,
9,52,,InvalidNro,
9,53,,InvalidNrr,
9,54,,InvalidSignature,
9,55,,InsufficientNroRegistrations,
9,56,,InsufficientNrrRegistrations,
9,57,,NroAlreadyLoaded,
9,81,,InvalidAddress,
9,82,,InvalidSize,
9,84,,NotLoaded,
9,85,,NotRegistered,
9,86,,InvalidSession,
9,87,,InvalidProcess,
9,100,,UnknownCapability,
9,103,,InvalidCapabilityKernelFlags,
9,104,,InvalidCapabilitySyscallMask,
9,106,,InvalidCapabilityMapRange,
9,107,,InvalidCapabilityMapPage,
9,111,,InvalidCapabilityInterruptPair,
9,113,,InvalidCapabilityApplicationType,
9,114,,InvalidCapabilityKernelVersion,
9,115,,InvalidCapabilityHandleTable,
9,116,,InvalidCapabilityDebugFlags,
9,200,,InternalError,
20,1,,TooLargeKeyOrDbFull,
20,2,,KeyNotFound,
20,4,,AllocationFailed,
@ -261,4 +300,16 @@ Module,DescriptionStart,DescriptionEnd,Name,Summary
205,110,,IrsensorUnconnected,
205,111,,IrsensorUnsupported,
205,120,,IrsensorNotReady,
205,122,139,IrsensorDeviceError,
205,122,139,IrsensorDeviceError,
428,1,49,InvalidArgument,
428,2,,NullArgument,
428,3,,ArgumentOutOfRange,
428,4,,BufferTooSmall,
428,1000,1999,InvalidData,
428,1001,1019,InvalidKip,
428,1002,,InvalidKipFileSize,The size of the KIP file was smaller than expected.
428,1003,,InvalidKipMagic,The magic value of the KIP file was not KIP1.
428,1004,,InvalidKipSegmentSize,The size of the compressed KIP segment was smaller than expected.
428,1005,,KipSegmentDecompressionFailed,An error occurred while decompressing a KIP segment.

1 Module,DescriptionStart,DescriptionEnd,Name,Summary
229 20,2,,KeyNotFound,
230 20,4,,AllocationFailed,
231 20,5,,InvalidKeyValue,
232 20,6,,BufferInsufficient,
233 24,1,,DeviceNotFound,
234 24,4,,DeviceAsleep,
235 123,0,4999,SslService,
236 124,0,,Cancelled,
237 124,1,,CancelledByUser,
238 124,100,,UserNotExist,
239 124,200,269,NetworkServiceAccountUnavailable,
240 124,430,499,TokenCacheUnavailable,
241 124,3000,8191,NetworkCommunicationError,
242 202,140,149,Invalid,
243 202,601,,DualConnected,
244 202,602,,SameJoyTypeConnected,
245 202,603,,ColorNotAvailable,
246 202,604,,ControllerNotConnected,
247 202,3101,,Canceled,
248 202,3102,,NotSupportedNpadStyle,
249 202,3200,3209,ControllerFirmwareUpdateError,
250 202,3201,,ControllerFirmwareUpdateFailed,
251 205,110,119,IrsensorUnavailable,
252 205,110,,IrsensorUnconnected,
253 205,111,,IrsensorUnsupported,
254 205,120,,IrsensorNotReady,
255 205,122,139,IrsensorDeviceError,
256 428,1,49,InvalidArgument,
257 428,2,,NullArgument,
258 428,3,,ArgumentOutOfRange,
259 428,4,,BufferTooSmall,
260 428,1000,1999,InvalidData,
261 428,1001,1019,InvalidKip,
262 428,1002,,InvalidKipFileSize,The size of the KIP file was smaller than expected.
263 428,1003,,InvalidKipMagic,The magic value of the KIP file was not KIP1.
264 428,1004,,InvalidKipSegmentSize,The size of the compressed KIP segment was smaller than expected.
265 428,1005,,KipSegmentDecompressionFailed,An error occurred while decompressing a KIP segment.
266
267
268
269
270
271
272
273
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315

View file

@ -0,0 +1,42 @@
//-----------------------------------------------------------------------------
// This file was automatically generated.
// Changes to this file will be lost when the file is regenerated.
//
// To change this file, modify /build/CodeGen/results.csv at the root of this
// repo and run the build script.
//
// The script can be run with the "codegen" option to run only the
// code generation portion of the build.
//-----------------------------------------------------------------------------
using System.Runtime.CompilerServices;
namespace LibHac.Common
{
public static class ResultLibHac
{
public const int ModuleLibHac = 428;
/// <summary>Error code: 2428-0001; Range: 1-49; Inner value: 0x3ac</summary>
public static Result.Base InvalidArgument => new Result.Base(ModuleLibHac, 1, 49);
/// <summary>Error code: 2428-0002; Inner value: 0x5ac</summary>
public static Result.Base NullArgument => new Result.Base(ModuleLibHac, 2);
/// <summary>Error code: 2428-0003; Inner value: 0x7ac</summary>
public static Result.Base ArgumentOutOfRange => new Result.Base(ModuleLibHac, 3);
/// <summary>Error code: 2428-0004; Inner value: 0x9ac</summary>
public static Result.Base BufferTooSmall => new Result.Base(ModuleLibHac, 4);
/// <summary>Error code: 2428-1000; Range: 1000-1999; Inner value: 0x7d1ac</summary>
public static Result.Base InvalidData { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleLibHac, 1000, 1999); }
/// <summary>Error code: 2428-1001; Range: 1001-1019; Inner value: 0x7d3ac</summary>
public static Result.Base InvalidKip { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleLibHac, 1001, 1019); }
/// <summary>The size of the KIP file was smaller than expected.<br/>Error code: 2428-1002; Inner value: 0x7d5ac</summary>
public static Result.Base InvalidKipFileSize => new Result.Base(ModuleLibHac, 1002);
/// <summary>The magic value of the KIP file was not KIP1.<br/>Error code: 2428-1003; Inner value: 0x7d7ac</summary>
public static Result.Base InvalidKipMagic => new Result.Base(ModuleLibHac, 1003);
/// <summary>The size of the compressed KIP segment was smaller than expected.<br/>Error code: 2428-1004; Inner value: 0x7d9ac</summary>
public static Result.Base InvalidKipSegmentSize => new Result.Base(ModuleLibHac, 1004);
/// <summary>An error occurred while decompressing a KIP segment.<br/>Error code: 2428-1005; Inner value: 0x7dbac</summary>
public static Result.Base KipSegmentDecompressionFailed => new Result.Base(ModuleLibHac, 1005);
}
}

View file

@ -5,6 +5,7 @@ using LibHac.FsSystem;
namespace LibHac
{
[Obsolete("This class has been deprecated. LibHac.Loader.KipReader should be used instead.")]
public class Kip
{
private const int HeaderSize = 0x100;

View file

@ -0,0 +1,76 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using LibHac.Common;
namespace LibHac.Loader
{
[StructLayout(LayoutKind.Explicit, Size = 0x100)]
public struct KipHeader
{
public const uint Kip1Magic = 0x3150494B; // KIP1
public const int NameSize = 12;
public const int SegmentCount = 6;
[FieldOffset(0x00)] public uint Magic;
[FieldOffset(0x04)] private byte _name;
[FieldOffset(0x10)] public ulong ProgramId;
[FieldOffset(0x18)] public int Version;
[FieldOffset(0x1C)] public byte Priority;
[FieldOffset(0x1D)] public byte IdealCoreId;
[FieldOffset(0x1F)] public Flag Flags;
[FieldOffset(0x20)] public int TextMemoryOffset;
[FieldOffset(0x24)] public int TextSize;
[FieldOffset(0x28)] public int TextFileSize;
[FieldOffset(0x2C)] public int AffinityMask;
[FieldOffset(0x30)] public int RoMemoryOffset;
[FieldOffset(0x34)] public int RoSize;
[FieldOffset(0x38)] public int RoFileSize;
[FieldOffset(0x3C)] public int StackSize;
[FieldOffset(0x40)] public int DataMemoryOffset;
[FieldOffset(0x44)] public int DataSize;
[FieldOffset(0x48)] public int DataFileSize;
[FieldOffset(0x50)] public int BssMemoryOffset;
[FieldOffset(0x54)] public int BssSize;
[FieldOffset(0x58)] public int BssFileSize;
[FieldOffset(0x80)] private uint _capabilities;
public Span<byte> Name => SpanHelpers.CreateSpan(ref _name, NameSize);
public Span<SegmentHeader> Segments =>
SpanHelpers.CreateSpan(ref Unsafe.As<int, SegmentHeader>(ref TextMemoryOffset), SegmentCount);
public Span<uint> Capabilities => SpanHelpers.CreateSpan(ref _capabilities, 0x80 / sizeof(uint));
public bool IsValid => Magic == Kip1Magic;
[Flags]
public enum Flag : byte
{
TextCompress = 1 << 0,
RoCompress = 1 << 1,
DataCompress = 1 << 2,
Is64BitInstruction = 1 << 3,
ProcessAddressSpace64Bit = 1 << 4,
UseSecureMemory = 1 << 5
}
[StructLayout(LayoutKind.Sequential, Size = 0x10)]
public struct SegmentHeader
{
public int MemoryOffset;
public int Size;
public int FileSize;
}
}
}

View file

@ -0,0 +1,265 @@
using System;
using System.Buffers.Binary;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using LibHac.Common;
using LibHac.Fs;
namespace LibHac.Loader
{
public class KipReader
{
private IFile KipFile { get; set; }
private KipHeader _header;
public ReadOnlySpan<uint> Capabilities => _header.Capabilities;
public U8Span Name => new U8Span(_header.Name);
public ulong ProgramId => _header.ProgramId;
public int Version => _header.Version;
public byte Priority => _header.Priority;
public byte IdealCoreId => _header.IdealCoreId;
public bool IsTextCompressed => _header.Flags.HasFlag(KipHeader.Flag.TextCompress);
public bool IsRoCompressed => _header.Flags.HasFlag(KipHeader.Flag.RoCompress);
public bool IsDataCompressed => _header.Flags.HasFlag(KipHeader.Flag.DataCompress);
public bool Is64Bit => _header.Flags.HasFlag(KipHeader.Flag.Is64BitInstruction);
public bool Is64BitAddressSpace => _header.Flags.HasFlag(KipHeader.Flag.ProcessAddressSpace64Bit);
public bool UsesSecureMemory => _header.Flags.HasFlag(KipHeader.Flag.UseSecureMemory);
public ReadOnlySpan<KipHeader.SegmentHeader> Segments => _header.Segments;
public int AffinityMask => _header.AffinityMask;
public int StackSize => _header.StackSize;
public Result Initialize(IFile kipFile)
{
if (kipFile is null)
return ResultLibHac.NullArgument.Log();
Result rc = kipFile.Read(out long bytesRead, 0, SpanHelpers.AsByteSpan(ref _header), ReadOption.None);
if (rc.IsFailure()) return rc;
if (bytesRead != Unsafe.SizeOf<KipHeader>())
return ResultLibHac.InvalidKipFileSize.Log();
if (!_header.IsValid)
return ResultLibHac.InvalidKipMagic.Log();
KipFile = kipFile;
return Result.Success;
}
public Result GetSegmentSize(SegmentType segment, out int size)
{
switch (segment)
{
case SegmentType.Text:
case SegmentType.Ro:
case SegmentType.Data:
case SegmentType.Bss:
case SegmentType.Reserved1:
case SegmentType.Reserved2:
size = _header.Segments[(int)segment].Size;
return Result.Success;
default:
size = default;
return ResultLibHac.ArgumentOutOfRange.Log();
}
}
public int GetUncompressedSize()
{
int size = Unsafe.SizeOf<KipHeader>();
for (int i = 0; i < Segments.Length; i++)
{
if (Segments[i].FileSize != 0)
{
size += Segments[i].Size;
}
}
return size;
}
public Result ReadSegment(SegmentType segment, Span<byte> buffer)
{
Result rc = GetSegmentSize(segment, out int segmentSize);
if (rc.IsFailure()) return rc;
if (buffer.Length < segmentSize)
return ResultLibHac.BufferTooSmall.Log();
KipHeader.SegmentHeader segmentHeader = Segments[(int)segment];
// Return early for empty segments.
if (segmentHeader.Size == 0)
return Result.Success;
// The segment is all zeros if it has no data.
if (segmentHeader.FileSize == 0)
{
buffer.Slice(0, segmentHeader.Size).Clear();
return Result.Success;
}
int offset = CalculateSegmentOffset((int)segment);
// Read the segment data.
rc = KipFile.Read(out long bytesRead, offset, buffer.Slice(0, segmentHeader.FileSize), ReadOption.None);
if (rc.IsFailure()) return rc;
if (bytesRead != segmentHeader.FileSize)
return ResultLibHac.InvalidKipFileSize.Log();
// Decompress if necessary.
bool isCompressed = segment switch
{
SegmentType.Text => IsTextCompressed,
SegmentType.Ro => IsRoCompressed,
SegmentType.Data => IsDataCompressed,
_ => false
};
if (isCompressed)
{
rc = DecompressBlz(buffer, segmentHeader.FileSize);
if (rc.IsFailure()) return rc;
}
return Result.Success;
}
public Result ReadUncompressedKip(Span<byte> buffer)
{
if (buffer.Length < GetUncompressedSize())
return ResultLibHac.BufferTooSmall.Log();
Span<byte> segmentBuffer = buffer.Slice(Unsafe.SizeOf<KipHeader>());
// Read each of the segments into the buffer.
for (int i = 0; i < Segments.Length; i++)
{
if (Segments[i].FileSize != 0)
{
Result rc = ReadSegment((SegmentType)i, segmentBuffer);
if (rc.IsFailure()) return rc;
segmentBuffer = segmentBuffer.Slice(Segments[i].Size);
}
}
// Copy the header to the buffer and update the sizes and flags.
ref KipHeader header = ref Unsafe.As<byte, KipHeader>(ref MemoryMarshal.GetReference(buffer));
header = _header;
// Remove any compression flags.
const KipHeader.Flag compressFlagsMask =
~(KipHeader.Flag.TextCompress | KipHeader.Flag.RoCompress | KipHeader.Flag.DataCompress);
header.Flags &= compressFlagsMask;
// Update each segment's uncompressed size.
foreach (ref KipHeader.SegmentHeader segment in header.Segments)
{
if (segment.FileSize != 0)
{
segment.FileSize = segment.Size;
}
}
return Result.Success;
}
private int CalculateSegmentOffset(int index)
{
Debug.Assert((uint)index <= (uint)SegmentType.Reserved2);
int offset = Unsafe.SizeOf<KipHeader>();
ReadOnlySpan<KipHeader.SegmentHeader> segments = Segments;
for (int i = 0; i < index; i++)
{
offset += segments[i].FileSize;
}
return offset;
}
private static Result DecompressBlz(Span<byte> buffer, int compressedDataSize)
{
const int segmentFooterSize = 12;
if (buffer.Length < segmentFooterSize)
return ResultLibHac.InvalidKipSegmentSize.Log();
// Parse the footer, endian agnostic.
Span<byte> footer = buffer.Slice(compressedDataSize - segmentFooterSize);
int totalCompSize = BinaryPrimitives.ReadInt32LittleEndian(footer);
int footerSize = BinaryPrimitives.ReadInt32LittleEndian(footer.Slice(4));
int additionalSize = BinaryPrimitives.ReadInt32LittleEndian(footer.Slice(8));
if (buffer.Length < totalCompSize + additionalSize)
return ResultLibHac.BufferTooSmall.Log();
Span<byte> data = buffer;
int inOffset = totalCompSize - footerSize;
int outOffset = totalCompSize + additionalSize;
while (outOffset != 0)
{
byte control = data[--inOffset];
// Each bit in the control byte is a flag indicating compressed or not compressed.
for (int i = 0; i < 8; i++)
{
if ((control & 0x80) != 0)
{
if (inOffset < 2)
return ResultLibHac.KipSegmentDecompressionFailed.Log();
inOffset -= 2;
ushort segmentValue = BinaryPrimitives.ReadUInt16LittleEndian(data.Slice(inOffset));
int segmentOffset = (segmentValue & 0x0FFF) + 3;
int segmentSize = Math.Min(((segmentValue >> 12) & 0xF) + 3, outOffset);
outOffset -= segmentSize;
for (int j = 0; j < segmentSize; j++)
{
data[outOffset + j] = data[outOffset + segmentOffset + j];
}
}
else
{
if (inOffset < 1)
return ResultLibHac.KipSegmentDecompressionFailed.Log();
// Copy directly.
data[--outOffset] = data[--inOffset];
}
control <<= 1;
if (outOffset == 0)
return Result.Success;
}
}
return Result.Success;
}
public enum SegmentType
{
Text = 0,
Ro = 1,
Data = 2,
Bss = 3,
Reserved1 = 4,
Reserved2 = 5
}
}
}

View file

@ -0,0 +1,84 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using LibHac.Common;
namespace LibHac.Loader
{
[StructLayout(LayoutKind.Explicit, Size = 0x100)]
public struct NsoHeader
{
public const int SegmentCount = 3;
[FieldOffset(0x00)] public uint Magic;
[FieldOffset(0x04)] public uint Version;
[FieldOffset(0x08)] public uint Reserved08;
[FieldOffset(0x0C)] public Flag Flags;
[FieldOffset(0x10)] public uint TextFileOffset;
[FieldOffset(0x14)] public uint TextMemoryOffset;
[FieldOffset(0x18)] public uint TextSize;
[FieldOffset(0x1C)] public uint ModuleNameOffset;
[FieldOffset(0x20)] public uint RoFileOffset;
[FieldOffset(0x24)] public uint RoMemoryOffset;
[FieldOffset(0x28)] public uint RoSize;
[FieldOffset(0x2C)] public uint ModuleNameSize;
[FieldOffset(0x30)] public uint DataFileOffset;
[FieldOffset(0x34)] public uint DataMemoryOffset;
[FieldOffset(0x38)] public uint DataSize;
[FieldOffset(0x3C)] public uint BssSize;
[FieldOffset(0x40)] public Buffer32 ModuleId;
// Size of the sections in the NSO file
[FieldOffset(0x60)] public uint TextFileSize;
[FieldOffset(0x64)] public uint RoFileSize;
[FieldOffset(0x68)] public uint DataFileSize;
[FieldOffset(0x68)] private byte _reserved6C;
[FieldOffset(0x88)] public uint ApiInfoOffset;
[FieldOffset(0x8C)] public uint ApiInfoSize;
[FieldOffset(0x90)] public uint DynStrOffset;
[FieldOffset(0x94)] public uint DynStrSize;
[FieldOffset(0x98)] public uint DynSymOffset;
[FieldOffset(0x9C)] public uint DynSymSize;
[FieldOffset(0xA0)] public Buffer32 TextHash;
[FieldOffset(0xC0)] public Buffer32 RoHash;
[FieldOffset(0xE0)] public Buffer32 DataHash;
public Span<SegmentHeader> Segments =>
SpanHelpers.CreateSpan(ref Unsafe.As<uint, SegmentHeader>(ref TextFileOffset), SegmentCount);
public Span<uint> CompressedSizes => SpanHelpers.CreateSpan(ref TextFileSize, SegmentCount);
public Span<Buffer32> SegmentHashes => SpanHelpers.CreateSpan(ref TextHash, SegmentCount);
public Span<byte> Reserved6C => SpanHelpers.CreateSpan(ref _reserved6C, 0x1C);
[Flags]
public enum Flag
{
TextCompress = 1 << 0,
RoCompress = 1 << 1,
DataCompress = 1 << 2,
TextHash = 1 << 3,
RoHash = 1 << 4,
DataHash = 1 << 5
}
[StructLayout(LayoutKind.Sequential, Size = 0x10)]
public struct SegmentHeader
{
public uint FileOffset;
public uint MemoryOffset;
public uint Size;
}
}
}

View file

@ -0,0 +1,104 @@
using System;
using System.Runtime.CompilerServices;
using LibHac.Common;
using LibHac.Fs;
namespace LibHac.Loader
{
public class NsoReader
{
private IFile NsoFile { get; set; }
public NsoHeader Header;
public Result Initialize(IFile nsoFile)
{
Result rc = nsoFile.Read(out long bytesRead, 0, SpanHelpers.AsByteSpan(ref Header), ReadOption.None);
if (rc.IsFailure()) return rc;
if (bytesRead != Unsafe.SizeOf<NsoHeader>())
return ResultLoader.InvalidNso.Log();
NsoFile = nsoFile;
return Result.Success;
}
public Result GetSegmentSize(SegmentType segment, out uint size)
{
switch (segment)
{
case SegmentType.Text:
case SegmentType.Ro:
case SegmentType.Data:
size = Header.Segments[(int)segment].Size;
return Result.Success;
default:
size = default;
return ResultLibHac.ArgumentOutOfRange.Log();
}
}
public Result ReadSegment(SegmentType segment, Span<byte> buffer)
{
Result rc = GetSegmentSize(segment, out uint segmentSize);
if (rc.IsFailure()) return rc;
if (buffer.Length < segmentSize)
return ResultLibHac.BufferTooSmall.Log();
bool isCompressed = (((int)Header.Flags >> (int)segment) & 1) != 0;
bool checkHash = (((int)Header.Flags >> (int)segment) & 8) != 0;
return ReadSegmentImpl(ref Header.Segments[(int)segment], Header.CompressedSizes[(int)segment],
Header.SegmentHashes[(int)segment], isCompressed, checkHash, buffer);
}
private Result ReadSegmentImpl(ref NsoHeader.SegmentHeader segment, uint fileSize, Buffer32 fileHash,
bool isCompressed, bool checkHash, Span<byte> buffer)
{
// Select read size based on compression.
if (!isCompressed)
{
fileSize = segment.Size;
}
// Validate size.
if (fileSize > segment.Size)
return ResultLoader.InvalidNso.Log();
// Load data from file.
uint loadAddress = isCompressed ? (uint)buffer.Length - fileSize : 0;
Result rc = NsoFile.Read(out long bytesRead, segment.FileOffset, buffer.Slice((int)loadAddress), ReadOption.None);
if (rc.IsFailure()) return rc;
if (bytesRead != fileSize)
return ResultLoader.InvalidNso.Log();
// Uncompress if necessary.
if (isCompressed)
{
Lz4.Decompress(buffer.Slice((int)loadAddress), buffer);
}
// Check hash if necessary.
if (checkHash)
{
Buffer32 hash = default;
Crypto.Sha256.GenerateSha256Hash(buffer.Slice(0, (int)segment.Size), hash.Bytes);
if (hash.Bytes.SequenceCompareTo(fileHash.Bytes) != 0)
return ResultLoader.InvalidNso.Log();
}
return Result.Success;
}
public enum SegmentType
{
Text = 0,
Ro = 1,
Data = 2
}
}
}

View file

@ -0,0 +1,87 @@
//-----------------------------------------------------------------------------
// This file was automatically generated.
// Changes to this file will be lost when the file is regenerated.
//
// To change this file, modify /build/CodeGen/results.csv at the root of this
// repo and run the build script.
//
// The script can be run with the "codegen" option to run only the
// code generation portion of the build.
//-----------------------------------------------------------------------------
namespace LibHac.Loader
{
public static class ResultLoader
{
public const int ModuleLoader = 9;
/// <summary>Error code: 2009-0001; Inner value: 0x209</summary>
public static Result.Base TooLongArgument => new Result.Base(ModuleLoader, 1);
/// <summary>Error code: 2009-0002; Inner value: 0x409</summary>
public static Result.Base TooManyArguments => new Result.Base(ModuleLoader, 2);
/// <summary>Error code: 2009-0003; Inner value: 0x609</summary>
public static Result.Base TooLargeMeta => new Result.Base(ModuleLoader, 3);
/// <summary>Error code: 2009-0004; Inner value: 0x809</summary>
public static Result.Base InvalidMeta => new Result.Base(ModuleLoader, 4);
/// <summary>Error code: 2009-0005; Inner value: 0xa09</summary>
public static Result.Base InvalidNso => new Result.Base(ModuleLoader, 5);
/// <summary>Error code: 2009-0006; Inner value: 0xc09</summary>
public static Result.Base InvalidPath => new Result.Base(ModuleLoader, 6);
/// <summary>Error code: 2009-0007; Inner value: 0xe09</summary>
public static Result.Base TooManyProcesses => new Result.Base(ModuleLoader, 7);
/// <summary>Error code: 2009-0008; Inner value: 0x1009</summary>
public static Result.Base NotPinned => new Result.Base(ModuleLoader, 8);
/// <summary>Error code: 2009-0009; Inner value: 0x1209</summary>
public static Result.Base InvalidProgramId => new Result.Base(ModuleLoader, 9);
/// <summary>Error code: 2009-0010; Inner value: 0x1409</summary>
public static Result.Base InvalidVersion => new Result.Base(ModuleLoader, 10);
/// <summary>Error code: 2009-0051; Inner value: 0x6609</summary>
public static Result.Base InsufficientAddressSpace => new Result.Base(ModuleLoader, 51);
/// <summary>Error code: 2009-0052; Inner value: 0x6809</summary>
public static Result.Base InvalidNro => new Result.Base(ModuleLoader, 52);
/// <summary>Error code: 2009-0053; Inner value: 0x6a09</summary>
public static Result.Base InvalidNrr => new Result.Base(ModuleLoader, 53);
/// <summary>Error code: 2009-0054; Inner value: 0x6c09</summary>
public static Result.Base InvalidSignature => new Result.Base(ModuleLoader, 54);
/// <summary>Error code: 2009-0055; Inner value: 0x6e09</summary>
public static Result.Base InsufficientNroRegistrations => new Result.Base(ModuleLoader, 55);
/// <summary>Error code: 2009-0056; Inner value: 0x7009</summary>
public static Result.Base InsufficientNrrRegistrations => new Result.Base(ModuleLoader, 56);
/// <summary>Error code: 2009-0057; Inner value: 0x7209</summary>
public static Result.Base NroAlreadyLoaded => new Result.Base(ModuleLoader, 57);
/// <summary>Error code: 2009-0081; Inner value: 0xa209</summary>
public static Result.Base InvalidAddress => new Result.Base(ModuleLoader, 81);
/// <summary>Error code: 2009-0082; Inner value: 0xa409</summary>
public static Result.Base InvalidSize => new Result.Base(ModuleLoader, 82);
/// <summary>Error code: 2009-0084; Inner value: 0xa809</summary>
public static Result.Base NotLoaded => new Result.Base(ModuleLoader, 84);
/// <summary>Error code: 2009-0085; Inner value: 0xaa09</summary>
public static Result.Base NotRegistered => new Result.Base(ModuleLoader, 85);
/// <summary>Error code: 2009-0086; Inner value: 0xac09</summary>
public static Result.Base InvalidSession => new Result.Base(ModuleLoader, 86);
/// <summary>Error code: 2009-0087; Inner value: 0xae09</summary>
public static Result.Base InvalidProcess => new Result.Base(ModuleLoader, 87);
/// <summary>Error code: 2009-0100; Inner value: 0xc809</summary>
public static Result.Base UnknownCapability => new Result.Base(ModuleLoader, 100);
/// <summary>Error code: 2009-0103; Inner value: 0xce09</summary>
public static Result.Base InvalidCapabilityKernelFlags => new Result.Base(ModuleLoader, 103);
/// <summary>Error code: 2009-0104; Inner value: 0xd009</summary>
public static Result.Base InvalidCapabilitySyscallMask => new Result.Base(ModuleLoader, 104);
/// <summary>Error code: 2009-0106; Inner value: 0xd409</summary>
public static Result.Base InvalidCapabilityMapRange => new Result.Base(ModuleLoader, 106);
/// <summary>Error code: 2009-0107; Inner value: 0xd609</summary>
public static Result.Base InvalidCapabilityMapPage => new Result.Base(ModuleLoader, 107);
/// <summary>Error code: 2009-0111; Inner value: 0xde09</summary>
public static Result.Base InvalidCapabilityInterruptPair => new Result.Base(ModuleLoader, 111);
/// <summary>Error code: 2009-0113; Inner value: 0xe209</summary>
public static Result.Base InvalidCapabilityApplicationType => new Result.Base(ModuleLoader, 113);
/// <summary>Error code: 2009-0114; Inner value: 0xe409</summary>
public static Result.Base InvalidCapabilityKernelVersion => new Result.Base(ModuleLoader, 114);
/// <summary>Error code: 2009-0115; Inner value: 0xe609</summary>
public static Result.Base InvalidCapabilityHandleTable => new Result.Base(ModuleLoader, 115);
/// <summary>Error code: 2009-0116; Inner value: 0xe809</summary>
public static Result.Base InvalidCapabilityDebugFlags => new Result.Base(ModuleLoader, 116);
/// <summary>Error code: 2009-0200; Inner value: 0x19009</summary>
public static Result.Base InternalError => new Result.Base(ModuleLoader, 200);
}
}

View file

@ -76,5 +76,73 @@ namespace LibHac
return dec;
}
public static void Decompress(ReadOnlySpan<byte> cmp, Span<byte> dec)
{
int cmpPos = 0;
int decPos = 0;
// ReSharper disable once VariableHidesOuterVariable
int GetLength(int length, ReadOnlySpan<byte> cmp)
{
byte sum;
if (length == 0xf)
{
do
{
length += sum = cmp[cmpPos++];
}
while (sum == 0xff);
}
return length;
}
do
{
byte token = cmp[cmpPos++];
int encCount = (token >> 0) & 0xf;
int litCount = (token >> 4) & 0xf;
//Copy literal chunk
litCount = GetLength(litCount, cmp);
cmp.Slice(cmpPos, litCount).CopyTo(dec.Slice(decPos));
cmpPos += litCount;
decPos += litCount;
if (cmpPos >= cmp.Length)
{
break;
}
//Copy compressed chunk
int back = cmp[cmpPos++] << 0 |
cmp[cmpPos++] << 8;
encCount = GetLength(encCount, cmp) + 4;
int encPos = decPos - back;
if (encCount <= back)
{
dec.Slice(encPos, encCount).CopyTo(dec.Slice(decPos));
decPos += encCount;
}
else
{
while (encCount-- > 0)
{
dec[decPos++] = dec[encPos++];
}
}
}
while (cmpPos < cmp.Length &&
decPos < dec.Length);
}
}
}

View file

@ -38,6 +38,7 @@ namespace hactoolnet
new CliOption("outdir", 1, (o, a) => o.OutDir = a[0]),
new CliOption("outfile", 1, (o, a) => o.OutFile = a[0]),
new CliOption("plaintext", 1, (o, a) => o.PlaintextOut = a[0]),
new CliOption("uncompressed", 1, (o, a) => o.UncompressedOut = a[0]),
new CliOption("nspout", 1, (o, a) => o.NspOut = a[0]),
new CliOption("sdseed", 1, (o, a) => o.SdSeed = a[0]),
new CliOption("sdpath", 1, (o, a) => o.SdPath = a[0]),
@ -221,6 +222,8 @@ namespace hactoolnet
sb.AppendLine(" --romfsdir <dir> Specify RomFS directory path.");
sb.AppendLine(" --listromfs List files in RomFS.");
sb.AppendLine(" --basenca Set Base NCA to use with update partitions.");
sb.AppendLine("KIP1 options:");
sb.AppendLine(" --uncompressed <f> Specify file path for saving uncompressed KIP1.");
sb.AppendLine("RomFS options:");
sb.AppendLine(" --romfsdir <dir> Specify RomFS directory path.");
sb.AppendLine(" --listromfs List files in RomFS.");

View file

@ -29,6 +29,7 @@ namespace hactoolnet
public string OutDir;
public string OutFile;
public string PlaintextOut;
public string UncompressedOut;
public string SdSeed;
public string NspOut;
public string SdPath;

View file

@ -1,6 +1,8 @@
using System.IO;
using LibHac;
using LibHac.Fs;
using LibHac.FsSystem;
using LibHac.Loader;
namespace hactoolnet
{
@ -8,10 +10,19 @@ namespace hactoolnet
{
public static void ProcessKip1(Context ctx)
{
using (var file = new LocalStorage(ctx.Options.InFile, FileAccess.Read))
using (var file = new LocalFile(ctx.Options.InFile, OpenMode.Read))
{
var kip = new Kip(file);
kip.OpenRawFile();
var kip = new KipReader();
kip.Initialize(file).ThrowIfFailure();
if (!string.IsNullOrWhiteSpace(ctx.Options.UncompressedOut))
{
var uncompressed = new byte[kip.GetUncompressedSize()];
kip.ReadUncompressedKip(uncompressed).ThrowIfFailure();
File.WriteAllBytes(ctx.Options.UncompressedOut, uncompressed);
}
}
}